From e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb Mon Sep 17 00:00:00 2001 From: Yang Zhang Date: Fri, 28 Aug 2015 09:58:54 +0800 Subject: Add qemu 2.4.0 Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5 Signed-off-by: Yang Zhang --- qemu/roms/u-boot/drivers/Makefile | 16 + qemu/roms/u-boot/drivers/bios_emulator/Makefile | 12 + qemu/roms/u-boot/drivers/bios_emulator/atibios.c | 334 ++ qemu/roms/u-boot/drivers/bios_emulator/besys.c | 722 +++ qemu/roms/u-boot/drivers/bios_emulator/bios.c | 324 ++ qemu/roms/u-boot/drivers/bios_emulator/biosemu.c | 372 ++ qemu/roms/u-boot/drivers/bios_emulator/biosemui.h | 169 + .../u-boot/drivers/bios_emulator/include/biosemu.h | 392 ++ .../u-boot/drivers/bios_emulator/include/x86emu.h | 201 + .../drivers/bios_emulator/include/x86emu/debug.h | 209 + .../drivers/bios_emulator/include/x86emu/decode.h | 88 + .../drivers/bios_emulator/include/x86emu/ops.h | 45 + .../bios_emulator/include/x86emu/prim_ops.h | 141 + .../drivers/bios_emulator/include/x86emu/regs.h | 340 ++ .../drivers/bios_emulator/include/x86emu/x86emui.h | 101 + .../u-boot/drivers/bios_emulator/x86emu/debug.c | 463 ++ .../u-boot/drivers/bios_emulator/x86emu/decode.c | 1144 +++++ .../roms/u-boot/drivers/bios_emulator/x86emu/ops.c | 5423 ++++++++++++++++++++ .../u-boot/drivers/bios_emulator/x86emu/ops2.c | 1769 +++++++ .../u-boot/drivers/bios_emulator/x86emu/prim_ops.c | 2442 +++++++++ .../roms/u-boot/drivers/bios_emulator/x86emu/sys.c | 323 ++ qemu/roms/u-boot/drivers/block/Makefile | 23 + qemu/roms/u-boot/drivers/block/ahci.c | 994 ++++ qemu/roms/u-boot/drivers/block/ata_piix.c | 712 +++ qemu/roms/u-boot/drivers/block/ata_piix.h | 71 + qemu/roms/u-boot/drivers/block/dwc_ahsata.c | 970 ++++ qemu/roms/u-boot/drivers/block/dwc_ahsata.h | 320 ++ qemu/roms/u-boot/drivers/block/fsl_sata.c | 848 +++ qemu/roms/u-boot/drivers/block/fsl_sata.h | 321 ++ qemu/roms/u-boot/drivers/block/ftide020.c | 347 ++ qemu/roms/u-boot/drivers/block/ftide020.h | 266 + qemu/roms/u-boot/drivers/block/libata.c | 144 + qemu/roms/u-boot/drivers/block/mvsata_ide.c | 158 + qemu/roms/u-boot/drivers/block/mxc_ata.c | 129 + qemu/roms/u-boot/drivers/block/pata_bfin.c | 1204 +++++ qemu/roms/u-boot/drivers/block/pata_bfin.h | 171 + qemu/roms/u-boot/drivers/block/sandbox.c | 124 + qemu/roms/u-boot/drivers/block/sata_dwc.c | 2071 ++++++++ qemu/roms/u-boot/drivers/block/sata_dwc.h | 465 ++ qemu/roms/u-boot/drivers/block/sata_sil.c | 710 +++ qemu/roms/u-boot/drivers/block/sata_sil.h | 214 + qemu/roms/u-boot/drivers/block/sata_sil3114.c | 830 +++ qemu/roms/u-boot/drivers/block/sata_sil3114.h | 134 + qemu/roms/u-boot/drivers/block/sil680.c | 90 + qemu/roms/u-boot/drivers/block/sym53c8xx.c | 852 +++ qemu/roms/u-boot/drivers/block/systemace.c | 257 + qemu/roms/u-boot/drivers/bootcount/Makefile | 12 + qemu/roms/u-boot/drivers/bootcount/bootcount.c | 84 + .../roms/u-boot/drivers/bootcount/bootcount_at91.c | 31 + .../u-boot/drivers/bootcount/bootcount_blackfin.c | 34 + .../u-boot/drivers/bootcount/bootcount_davinci.c | 42 + qemu/roms/u-boot/drivers/bootcount/bootcount_env.c | 29 + qemu/roms/u-boot/drivers/bootcount/bootcount_i2c.c | 44 + qemu/roms/u-boot/drivers/bootcount/bootcount_ram.c | 60 + qemu/roms/u-boot/drivers/core/Makefile | 7 + qemu/roms/u-boot/drivers/core/device.c | 348 ++ qemu/roms/u-boot/drivers/core/lists.c | 155 + qemu/roms/u-boot/drivers/core/root.c | 102 + qemu/roms/u-boot/drivers/core/uclass.c | 285 + qemu/roms/u-boot/drivers/core/util.c | 37 + qemu/roms/u-boot/drivers/crypto/Makefile | 8 + qemu/roms/u-boot/drivers/crypto/ace_sha.c | 184 + qemu/roms/u-boot/drivers/crypto/ace_sha.h | 314 ++ qemu/roms/u-boot/drivers/ddr/fsl/Makefile | 37 + qemu/roms/u-boot/drivers/ddr/fsl/arm_ddr_gen3.c | 214 + qemu/roms/u-boot/drivers/ddr/fsl/ctrl_regs.c | 2308 +++++++++ .../roms/u-boot/drivers/ddr/fsl/ddr1_dimm_params.c | 343 ++ .../roms/u-boot/drivers/ddr/fsl/ddr2_dimm_params.c | 342 ++ .../roms/u-boot/drivers/ddr/fsl/ddr3_dimm_params.c | 341 ++ .../roms/u-boot/drivers/ddr/fsl/ddr4_dimm_params.c | 300 ++ qemu/roms/u-boot/drivers/ddr/fsl/fsl_ddr_gen4.c | 234 + qemu/roms/u-boot/drivers/ddr/fsl/interactive.c | 2240 ++++++++ .../u-boot/drivers/ddr/fsl/lc_common_dimm_params.c | 567 ++ qemu/roms/u-boot/drivers/ddr/fsl/main.c | 766 +++ .../roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen1.c | 91 + .../roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen2.c | 96 + .../roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen3.c | 573 +++ qemu/roms/u-boot/drivers/ddr/fsl/mpc86xx_ddr.c | 85 + qemu/roms/u-boot/drivers/ddr/fsl/options.c | 1164 +++++ qemu/roms/u-boot/drivers/ddr/fsl/util.c | 292 ++ qemu/roms/u-boot/drivers/demo/Makefile | 9 + qemu/roms/u-boot/drivers/demo/demo-pdata.c | 47 + qemu/roms/u-boot/drivers/demo/demo-shape.c | 127 + qemu/roms/u-boot/drivers/demo/demo-simple.c | 47 + qemu/roms/u-boot/drivers/demo/demo-uclass.c | 58 + qemu/roms/u-boot/drivers/dfu/Makefile | 11 + qemu/roms/u-boot/drivers/dfu/dfu.c | 466 ++ qemu/roms/u-boot/drivers/dfu/dfu_mmc.c | 279 + qemu/roms/u-boot/drivers/dfu/dfu_nand.c | 219 + qemu/roms/u-boot/drivers/dfu/dfu_ram.c | 77 + qemu/roms/u-boot/drivers/dma/MCD_dmaApi.c | 1011 ++++ qemu/roms/u-boot/drivers/dma/MCD_tasks.c | 2414 +++++++++ qemu/roms/u-boot/drivers/dma/MCD_tasksInit.c | 226 + qemu/roms/u-boot/drivers/dma/Makefile | 11 + qemu/roms/u-boot/drivers/dma/apbh_dma.c | 616 +++ qemu/roms/u-boot/drivers/dma/fsl_dma.c | 168 + qemu/roms/u-boot/drivers/dma/omap3_dma.c | 167 + qemu/roms/u-boot/drivers/fpga/ACEX1K.c | 249 + qemu/roms/u-boot/drivers/fpga/Makefile | 20 + qemu/roms/u-boot/drivers/fpga/altera.c | 227 + qemu/roms/u-boot/drivers/fpga/cyclon2.c | 205 + qemu/roms/u-boot/drivers/fpga/fpga.c | 287 ++ qemu/roms/u-boot/drivers/fpga/ivm_core.c | 3149 ++++++++++++ qemu/roms/u-boot/drivers/fpga/lattice.c | 380 ++ qemu/roms/u-boot/drivers/fpga/spartan2.c | 455 ++ qemu/roms/u-boot/drivers/fpga/spartan3.c | 473 ++ qemu/roms/u-boot/drivers/fpga/stratixII.c | 190 + qemu/roms/u-boot/drivers/fpga/virtex2.c | 425 ++ qemu/roms/u-boot/drivers/fpga/xilinx.c | 249 + qemu/roms/u-boot/drivers/fpga/zynqpl.c | 416 ++ qemu/roms/u-boot/drivers/gpio/Makefile | 36 + qemu/roms/u-boot/drivers/gpio/adi_gpio2.c | 423 ++ qemu/roms/u-boot/drivers/gpio/altera_pio.c | 286 ++ qemu/roms/u-boot/drivers/gpio/at91_gpio.c | 396 ++ qemu/roms/u-boot/drivers/gpio/bcm2835_gpio.c | 78 + qemu/roms/u-boot/drivers/gpio/da8xx_gpio.c | 396 ++ qemu/roms/u-boot/drivers/gpio/db8500_gpio.c | 221 + qemu/roms/u-boot/drivers/gpio/gpio-uclass.c | 266 + qemu/roms/u-boot/drivers/gpio/intel_ich6_gpio.c | 274 + qemu/roms/u-boot/drivers/gpio/kona_gpio.c | 141 + qemu/roms/u-boot/drivers/gpio/kw_gpio.c | 150 + qemu/roms/u-boot/drivers/gpio/mpc83xx_gpio.c | 183 + qemu/roms/u-boot/drivers/gpio/mvgpio.c | 97 + qemu/roms/u-boot/drivers/gpio/mvgpio.h | 58 + qemu/roms/u-boot/drivers/gpio/mvmfp.c | 66 + qemu/roms/u-boot/drivers/gpio/mxc_gpio.c | 136 + qemu/roms/u-boot/drivers/gpio/mxs_gpio.c | 116 + qemu/roms/u-boot/drivers/gpio/omap_gpio.c | 222 + qemu/roms/u-boot/drivers/gpio/pca953x.c | 312 ++ qemu/roms/u-boot/drivers/gpio/pca9698.c | 127 + qemu/roms/u-boot/drivers/gpio/s3c2440_gpio.c | 79 + qemu/roms/u-boot/drivers/gpio/s5p_gpio.c | 181 + qemu/roms/u-boot/drivers/gpio/sandbox.c | 254 + qemu/roms/u-boot/drivers/gpio/sh_pfc.c | 629 +++ qemu/roms/u-boot/drivers/gpio/spear_gpio.c | 86 + qemu/roms/u-boot/drivers/gpio/sx151x.c | 242 + qemu/roms/u-boot/drivers/gpio/tca642x.c | 333 ++ qemu/roms/u-boot/drivers/gpio/tegra_gpio.c | 246 + qemu/roms/u-boot/drivers/gpio/xilinx_gpio.c | 348 ++ qemu/roms/u-boot/drivers/hwmon/Makefile | 21 + qemu/roms/u-boot/drivers/hwmon/adm1021.c | 164 + qemu/roms/u-boot/drivers/hwmon/adt7460.c | 73 + qemu/roms/u-boot/drivers/hwmon/ds1621.c | 155 + qemu/roms/u-boot/drivers/hwmon/ds1722.c | 137 + qemu/roms/u-boot/drivers/hwmon/ds1775.c | 126 + qemu/roms/u-boot/drivers/hwmon/lm63.c | 160 + qemu/roms/u-boot/drivers/hwmon/lm73.c | 146 + qemu/roms/u-boot/drivers/hwmon/lm75.c | 143 + qemu/roms/u-boot/drivers/hwmon/lm81.c | 111 + qemu/roms/u-boot/drivers/i2c/Makefile | 31 + qemu/roms/u-boot/drivers/i2c/bfin-twi_i2c.c | 379 ++ qemu/roms/u-boot/drivers/i2c/davinci_i2c.c | 384 ++ qemu/roms/u-boot/drivers/i2c/davinci_i2c.h | 78 + qemu/roms/u-boot/drivers/i2c/designware_i2c.c | 436 ++ qemu/roms/u-boot/drivers/i2c/designware_i2c.h | 133 + qemu/roms/u-boot/drivers/i2c/fsl_i2c.c | 541 ++ qemu/roms/u-boot/drivers/i2c/fti2c010.c | 346 ++ qemu/roms/u-boot/drivers/i2c/fti2c010.h | 80 + qemu/roms/u-boot/drivers/i2c/i2c_core.c | 403 ++ qemu/roms/u-boot/drivers/i2c/kona_i2c.c | 730 +++ qemu/roms/u-boot/drivers/i2c/mv_i2c.c | 471 ++ qemu/roms/u-boot/drivers/i2c/mv_i2c.h | 67 + qemu/roms/u-boot/drivers/i2c/mvtwsi.c | 394 ++ qemu/roms/u-boot/drivers/i2c/mxc_i2c.c | 549 ++ qemu/roms/u-boot/drivers/i2c/mxs_i2c.c | 299 ++ qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.c | 680 +++ qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.h | 154 + qemu/roms/u-boot/drivers/i2c/pca9564_i2c.c | 176 + qemu/roms/u-boot/drivers/i2c/ppc4xx_i2c.c | 419 ++ qemu/roms/u-boot/drivers/i2c/rcar_i2c.c | 290 ++ qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.c | 1249 +++++ qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.h | 62 + qemu/roms/u-boot/drivers/i2c/sh_i2c.c | 308 ++ qemu/roms/u-boot/drivers/i2c/sh_sh7734_i2c.c | 374 ++ qemu/roms/u-boot/drivers/i2c/soft_i2c.c | 475 ++ qemu/roms/u-boot/drivers/i2c/tegra_i2c.c | 636 +++ qemu/roms/u-boot/drivers/i2c/tsi108_i2c.c | 273 + qemu/roms/u-boot/drivers/i2c/u8500_i2c.c | 601 +++ qemu/roms/u-boot/drivers/i2c/u8500_i2c.h | 178 + qemu/roms/u-boot/drivers/i2c/zynq_i2c.c | 307 ++ qemu/roms/u-boot/drivers/input/Makefile | 16 + qemu/roms/u-boot/drivers/input/cros_ec_keyb.c | 272 + qemu/roms/u-boot/drivers/input/i8042.c | 706 +++ qemu/roms/u-boot/drivers/input/input.c | 491 ++ qemu/roms/u-boot/drivers/input/key_matrix.c | 191 + qemu/roms/u-boot/drivers/input/keyboard.c | 300 ++ qemu/roms/u-boot/drivers/input/pc_keyb.c | 251 + qemu/roms/u-boot/drivers/input/ps2mult.c | 461 ++ qemu/roms/u-boot/drivers/input/ps2ser.c | 241 + qemu/roms/u-boot/drivers/input/tegra-kbc.c | 376 ++ qemu/roms/u-boot/drivers/misc/Makefile | 25 + qemu/roms/u-boot/drivers/misc/ali512x.c | 402 ++ qemu/roms/u-boot/drivers/misc/cbmem_console.c | 67 + qemu/roms/u-boot/drivers/misc/cros_ec.c | 1796 +++++++ qemu/roms/u-boot/drivers/misc/cros_ec_i2c.c | 176 + qemu/roms/u-boot/drivers/misc/cros_ec_lpc.c | 194 + qemu/roms/u-boot/drivers/misc/cros_ec_sandbox.c | 559 ++ qemu/roms/u-boot/drivers/misc/cros_ec_spi.c | 175 + qemu/roms/u-boot/drivers/misc/ds4510.c | 423 ++ qemu/roms/u-boot/drivers/misc/fsl_ifc.c | 171 + qemu/roms/u-boot/drivers/misc/fsl_iim.c | 281 + qemu/roms/u-boot/drivers/misc/gpio_led.c | 53 + qemu/roms/u-boot/drivers/misc/mc9sdz60.c | 35 + qemu/roms/u-boot/drivers/misc/mxc_ocotp.c | 200 + qemu/roms/u-boot/drivers/misc/mxs_ocotp.c | 311 ++ qemu/roms/u-boot/drivers/misc/ns87308.c | 101 + qemu/roms/u-boot/drivers/misc/pdsp188x.c | 45 + qemu/roms/u-boot/drivers/misc/status_led.c | 111 + qemu/roms/u-boot/drivers/misc/twl4030_led.c | 34 + qemu/roms/u-boot/drivers/mmc/Makefile | 37 + qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.c | 381 ++ qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.h | 195 + qemu/roms/u-boot/drivers/mmc/bcm2835_sdhci.c | 190 + qemu/roms/u-boot/drivers/mmc/bfin_sdh.c | 303 ++ qemu/roms/u-boot/drivers/mmc/davinci_mmc.c | 389 ++ qemu/roms/u-boot/drivers/mmc/dw_mmc.c | 379 ++ qemu/roms/u-boot/drivers/mmc/exynos_dw_mmc.c | 181 + qemu/roms/u-boot/drivers/mmc/fsl_esdhc.c | 655 +++ qemu/roms/u-boot/drivers/mmc/fsl_esdhc_spl.c | 150 + qemu/roms/u-boot/drivers/mmc/ftsdc010_mci.c | 373 ++ qemu/roms/u-boot/drivers/mmc/ftsdc021_sdhci.c | 33 + qemu/roms/u-boot/drivers/mmc/gen_atmel_mci.c | 401 ++ qemu/roms/u-boot/drivers/mmc/kona_sdhci.c | 134 + qemu/roms/u-boot/drivers/mmc/mmc.c | 1528 ++++++ qemu/roms/u-boot/drivers/mmc/mmc_private.h | 45 + qemu/roms/u-boot/drivers/mmc/mmc_spi.c | 291 ++ qemu/roms/u-boot/drivers/mmc/mmc_write.c | 180 + qemu/roms/u-boot/drivers/mmc/mv_sdhci.c | 55 + qemu/roms/u-boot/drivers/mmc/mxcmmc.c | 521 ++ qemu/roms/u-boot/drivers/mmc/mxsmmc.c | 428 ++ qemu/roms/u-boot/drivers/mmc/omap_hsmmc.c | 720 +++ qemu/roms/u-boot/drivers/mmc/pxa_mmc_gen.c | 432 ++ qemu/roms/u-boot/drivers/mmc/s5p_sdhci.c | 217 + qemu/roms/u-boot/drivers/mmc/sdhci.c | 513 ++ qemu/roms/u-boot/drivers/mmc/sh_mmcif.c | 609 +++ qemu/roms/u-boot/drivers/mmc/sh_mmcif.h | 238 + qemu/roms/u-boot/drivers/mmc/socfpga_dw_mmc.c | 68 + qemu/roms/u-boot/drivers/mmc/spear_sdhci.c | 32 + qemu/roms/u-boot/drivers/mmc/tegra_mmc.c | 702 +++ qemu/roms/u-boot/drivers/mmc/zynq_sdhci.c | 63 + qemu/roms/u-boot/drivers/mtd/Makefile | 20 + qemu/roms/u-boot/drivers/mtd/at45.c | 545 ++ qemu/roms/u-boot/drivers/mtd/cfi_flash.c | 2418 +++++++++ qemu/roms/u-boot/drivers/mtd/cfi_mtd.c | 263 + qemu/roms/u-boot/drivers/mtd/dataflash.c | 447 ++ qemu/roms/u-boot/drivers/mtd/ftsmc020.c | 38 + qemu/roms/u-boot/drivers/mtd/jedec_flash.c | 442 ++ qemu/roms/u-boot/drivers/mtd/mtdconcat.c | 773 +++ qemu/roms/u-boot/drivers/mtd/mtdcore.c | 390 ++ qemu/roms/u-boot/drivers/mtd/mtdpart.c | 428 ++ qemu/roms/u-boot/drivers/mtd/mw_eeprom.c | 236 + qemu/roms/u-boot/drivers/mtd/nand/Makefile | 72 + qemu/roms/u-boot/drivers/mtd/nand/am335x_spl_bch.c | 226 + qemu/roms/u-boot/drivers/mtd/nand/atmel_nand.c | 1437 ++++++ qemu/roms/u-boot/drivers/mtd/nand/atmel_nand_ecc.h | 146 + qemu/roms/u-boot/drivers/mtd/nand/bfin_nand.c | 393 ++ qemu/roms/u-boot/drivers/mtd/nand/davinci_nand.c | 653 +++ qemu/roms/u-boot/drivers/mtd/nand/docg4.c | 1028 ++++ qemu/roms/u-boot/drivers/mtd/nand/docg4_spl.c | 219 + qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_nand.c | 829 +++ qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_spl.c | 168 + qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_nand.c | 1039 ++++ qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_spl.c | 252 + qemu/roms/u-boot/drivers/mtd/nand/fsl_upm.c | 199 + qemu/roms/u-boot/drivers/mtd/nand/fsmc_nand.c | 473 ++ qemu/roms/u-boot/drivers/mtd/nand/jz4740_nand.c | 259 + qemu/roms/u-boot/drivers/mtd/nand/kb9202_nand.c | 134 + qemu/roms/u-boot/drivers/mtd/nand/kirkwood_nand.c | 70 + qemu/roms/u-boot/drivers/mtd/nand/kmeter1_nand.c | 123 + qemu/roms/u-boot/drivers/mtd/nand/mpc5121_nfc.c | 681 +++ qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.c | 1342 +++++ qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.h | 209 + qemu/roms/u-boot/drivers/mtd/nand/mxc_nand_spl.c | 351 ++ qemu/roms/u-boot/drivers/mtd/nand/mxs_nand.c | 1179 +++++ qemu/roms/u-boot/drivers/mtd/nand/nand.c | 120 + qemu/roms/u-boot/drivers/mtd/nand/nand_base.c | 3438 +++++++++++++ qemu/roms/u-boot/drivers/mtd/nand/nand_bbt.c | 1397 +++++ qemu/roms/u-boot/drivers/mtd/nand/nand_bch.c | 224 + qemu/roms/u-boot/drivers/mtd/nand/nand_ecc.c | 191 + qemu/roms/u-boot/drivers/mtd/nand/nand_ids.c | 182 + qemu/roms/u-boot/drivers/mtd/nand/nand_plat.c | 64 + qemu/roms/u-boot/drivers/mtd/nand/nand_spl_load.c | 42 + .../roms/u-boot/drivers/mtd/nand/nand_spl_simple.c | 270 + qemu/roms/u-boot/drivers/mtd/nand/nand_util.c | 861 ++++ qemu/roms/u-boot/drivers/mtd/nand/ndfc.c | 214 + qemu/roms/u-boot/drivers/mtd/nand/nomadik.c | 206 + qemu/roms/u-boot/drivers/mtd/nand/omap_elm.c | 196 + qemu/roms/u-boot/drivers/mtd/nand/omap_gpmc.c | 836 +++ qemu/roms/u-boot/drivers/mtd/nand/s3c2410_nand.c | 175 + qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.c | 1041 ++++ qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.h | 241 + qemu/roms/u-boot/drivers/mtd/onenand/Makefile | 13 + .../roms/u-boot/drivers/mtd/onenand/onenand_base.c | 2784 ++++++++++ qemu/roms/u-boot/drivers/mtd/onenand/onenand_bbt.c | 266 + qemu/roms/u-boot/drivers/mtd/onenand/onenand_spl.c | 128 + .../u-boot/drivers/mtd/onenand/onenand_uboot.c | 56 + qemu/roms/u-boot/drivers/mtd/onenand/samsung.c | 577 +++ qemu/roms/u-boot/drivers/mtd/spi/Makefile | 17 + qemu/roms/u-boot/drivers/mtd/spi/eeprom_m95xxx.c | 111 + qemu/roms/u-boot/drivers/mtd/spi/fsl_espi_spl.c | 90 + qemu/roms/u-boot/drivers/mtd/spi/ramtron.c | 403 ++ qemu/roms/u-boot/drivers/mtd/spi/sandbox.c | 483 ++ qemu/roms/u-boot/drivers/mtd/spi/sf.c | 58 + qemu/roms/u-boot/drivers/mtd/spi/sf_internal.h | 159 + qemu/roms/u-boot/drivers/mtd/spi/sf_ops.c | 518 ++ qemu/roms/u-boot/drivers/mtd/spi/sf_params.c | 131 + qemu/roms/u-boot/drivers/mtd/spi/sf_probe.c | 391 ++ qemu/roms/u-boot/drivers/mtd/spi/spi_spl_load.c | 80 + qemu/roms/u-boot/drivers/mtd/st_smi.c | 565 ++ qemu/roms/u-boot/drivers/mtd/ubi/Makefile | 10 + qemu/roms/u-boot/drivers/mtd/ubi/build.c | 1181 +++++ qemu/roms/u-boot/drivers/mtd/ubi/crc32.c | 510 ++ qemu/roms/u-boot/drivers/mtd/ubi/crc32defs.h | 32 + qemu/roms/u-boot/drivers/mtd/ubi/crc32table.h | 136 + qemu/roms/u-boot/drivers/mtd/ubi/debug.c | 180 + qemu/roms/u-boot/drivers/mtd/ubi/debug.h | 140 + qemu/roms/u-boot/drivers/mtd/ubi/eba.c | 1244 +++++ qemu/roms/u-boot/drivers/mtd/ubi/io.c | 1262 +++++ qemu/roms/u-boot/drivers/mtd/ubi/kapi.c | 626 +++ qemu/roms/u-boot/drivers/mtd/ubi/misc.c | 94 + qemu/roms/u-boot/drivers/mtd/ubi/scan.c | 1348 +++++ qemu/roms/u-boot/drivers/mtd/ubi/scan.h | 153 + qemu/roms/u-boot/drivers/mtd/ubi/ubi-media.h | 360 ++ qemu/roms/u-boot/drivers/mtd/ubi/ubi.h | 638 +++ qemu/roms/u-boot/drivers/mtd/ubi/upd.c | 429 ++ qemu/roms/u-boot/drivers/mtd/ubi/vmt.c | 848 +++ qemu/roms/u-boot/drivers/mtd/ubi/vtbl.c | 826 +++ qemu/roms/u-boot/drivers/mtd/ubi/wl.c | 1664 ++++++ qemu/roms/u-boot/drivers/net/4xx_enet.c | 2059 ++++++++ qemu/roms/u-boot/drivers/net/8390.h | 124 + qemu/roms/u-boot/drivers/net/Makefile | 66 + qemu/roms/u-boot/drivers/net/altera_tse.c | 971 ++++ qemu/roms/u-boot/drivers/net/altera_tse.h | 492 ++ qemu/roms/u-boot/drivers/net/armada100_fec.c | 726 +++ qemu/roms/u-boot/drivers/net/armada100_fec.h | 209 + qemu/roms/u-boot/drivers/net/at91_emac.c | 508 ++ qemu/roms/u-boot/drivers/net/ax88180.c | 757 +++ qemu/roms/u-boot/drivers/net/ax88180.h | 396 ++ qemu/roms/u-boot/drivers/net/ax88796.c | 144 + qemu/roms/u-boot/drivers/net/ax88796.h | 67 + qemu/roms/u-boot/drivers/net/bfin_mac.c | 498 ++ qemu/roms/u-boot/drivers/net/bfin_mac.h | 65 + qemu/roms/u-boot/drivers/net/calxedaxgmac.c | 544 ++ qemu/roms/u-boot/drivers/net/cpsw.c | 1020 ++++ qemu/roms/u-boot/drivers/net/cs8900.c | 320 ++ qemu/roms/u-boot/drivers/net/cs8900.h | 249 + qemu/roms/u-boot/drivers/net/davinci_emac.c | 894 ++++ qemu/roms/u-boot/drivers/net/davinci_emac.h | 303 ++ qemu/roms/u-boot/drivers/net/dc2114x.c | 760 +++ qemu/roms/u-boot/drivers/net/designware.c | 449 ++ qemu/roms/u-boot/drivers/net/designware.h | 236 + qemu/roms/u-boot/drivers/net/dm9000x.c | 643 +++ qemu/roms/u-boot/drivers/net/dm9000x.h | 140 + qemu/roms/u-boot/drivers/net/dnet.c | 392 ++ qemu/roms/u-boot/drivers/net/dnet.h | 166 + qemu/roms/u-boot/drivers/net/e1000.c | 5307 +++++++++++++++++++ qemu/roms/u-boot/drivers/net/e1000.h | 2601 ++++++++++ qemu/roms/u-boot/drivers/net/e1000_spi.c | 577 +++ qemu/roms/u-boot/drivers/net/eepro100.c | 931 ++++ qemu/roms/u-boot/drivers/net/enc28j60.c | 963 ++++ qemu/roms/u-boot/drivers/net/enc28j60.h | 238 + qemu/roms/u-boot/drivers/net/ep93xx_eth.c | 639 +++ qemu/roms/u-boot/drivers/net/ep93xx_eth.h | 127 + qemu/roms/u-boot/drivers/net/ethoc.c | 511 ++ qemu/roms/u-boot/drivers/net/fec_mxc.c | 1102 ++++ qemu/roms/u-boot/drivers/net/fec_mxc.h | 323 ++ qemu/roms/u-boot/drivers/net/fm/Makefile | 37 + qemu/roms/u-boot/drivers/net/fm/b4860.c | 75 + qemu/roms/u-boot/drivers/net/fm/dtsec.c | 168 + qemu/roms/u-boot/drivers/net/fm/eth.c | 712 +++ qemu/roms/u-boot/drivers/net/fm/fm.c | 420 ++ qemu/roms/u-boot/drivers/net/fm/fm.h | 148 + qemu/roms/u-boot/drivers/net/fm/init.c | 338 ++ qemu/roms/u-boot/drivers/net/fm/memac.c | 137 + qemu/roms/u-boot/drivers/net/fm/memac_phy.c | 145 + qemu/roms/u-boot/drivers/net/fm/p1023.c | 73 + qemu/roms/u-boot/drivers/net/fm/p4080.c | 99 + qemu/roms/u-boot/drivers/net/fm/p5020.c | 90 + qemu/roms/u-boot/drivers/net/fm/p5040.c | 107 + qemu/roms/u-boot/drivers/net/fm/t1040.c | 72 + qemu/roms/u-boot/drivers/net/fm/t2080.c | 93 + qemu/roms/u-boot/drivers/net/fm/t4240.c | 166 + qemu/roms/u-boot/drivers/net/fm/tgec.c | 106 + qemu/roms/u-boot/drivers/net/fm/tgec_phy.c | 126 + qemu/roms/u-boot/drivers/net/fsl_mcdmafec.c | 571 +++ qemu/roms/u-boot/drivers/net/fsl_mdio.c | 110 + qemu/roms/u-boot/drivers/net/ftgmac100.c | 583 +++ qemu/roms/u-boot/drivers/net/ftgmac100.h | 243 + qemu/roms/u-boot/drivers/net/ftmac100.c | 265 + qemu/roms/u-boot/drivers/net/ftmac100.h | 142 + qemu/roms/u-boot/drivers/net/ftmac110.c | 480 ++ qemu/roms/u-boot/drivers/net/ftmac110.h | 176 + qemu/roms/u-boot/drivers/net/greth.c | 670 +++ qemu/roms/u-boot/drivers/net/greth.h | 81 + qemu/roms/u-boot/drivers/net/keystone_net.c | 716 +++ qemu/roms/u-boot/drivers/net/ks8695eth.c | 229 + qemu/roms/u-boot/drivers/net/ks8851_mll.c | 633 +++ qemu/roms/u-boot/drivers/net/ks8851_mll.h | 357 ++ qemu/roms/u-boot/drivers/net/lan91c96.c | 804 +++ qemu/roms/u-boot/drivers/net/lan91c96.h | 617 +++ qemu/roms/u-boot/drivers/net/macb.c | 702 +++ qemu/roms/u-boot/drivers/net/macb.h | 313 ++ qemu/roms/u-boot/drivers/net/mcffec.c | 609 +++ qemu/roms/u-boot/drivers/net/mcfmii.c | 315 ++ qemu/roms/u-boot/drivers/net/mpc512x_fec.c | 754 +++ qemu/roms/u-boot/drivers/net/mpc512x_fec.h | 98 + qemu/roms/u-boot/drivers/net/mpc5xxx_fec.c | 1027 ++++ qemu/roms/u-boot/drivers/net/mpc5xxx_fec.h | 282 + qemu/roms/u-boot/drivers/net/mvgbe.c | 788 +++ qemu/roms/u-boot/drivers/net/mvgbe.h | 498 ++ qemu/roms/u-boot/drivers/net/natsemi.c | 882 ++++ qemu/roms/u-boot/drivers/net/ne2000.c | 259 + qemu/roms/u-boot/drivers/net/ne2000.h | 94 + qemu/roms/u-boot/drivers/net/ne2000_base.c | 800 +++ qemu/roms/u-boot/drivers/net/ne2000_base.h | 304 ++ qemu/roms/u-boot/drivers/net/netconsole.c | 331 ++ qemu/roms/u-boot/drivers/net/ns8382x.c | 851 +++ qemu/roms/u-boot/drivers/net/pcnet.c | 542 ++ qemu/roms/u-boot/drivers/net/phy/Makefile | 26 + qemu/roms/u-boot/drivers/net/phy/atheros.c | 76 + qemu/roms/u-boot/drivers/net/phy/broadcom.c | 274 + qemu/roms/u-boot/drivers/net/phy/davicom.c | 84 + qemu/roms/u-boot/drivers/net/phy/et1011c.c | 101 + qemu/roms/u-boot/drivers/net/phy/generic_10g.c | 94 + qemu/roms/u-boot/drivers/net/phy/icplus.c | 80 + qemu/roms/u-boot/drivers/net/phy/lxt.c | 73 + qemu/roms/u-boot/drivers/net/phy/marvell.c | 524 ++ qemu/roms/u-boot/drivers/net/phy/micrel.c | 226 + qemu/roms/u-boot/drivers/net/phy/miiphybb.c | 364 ++ qemu/roms/u-boot/drivers/net/phy/mv88e61xx.c | 537 ++ qemu/roms/u-boot/drivers/net/phy/mv88e61xx.h | 61 + qemu/roms/u-boot/drivers/net/phy/mv88e6352.c | 302 ++ qemu/roms/u-boot/drivers/net/phy/natsemi.c | 119 + qemu/roms/u-boot/drivers/net/phy/phy.c | 817 +++ qemu/roms/u-boot/drivers/net/phy/realtek.c | 141 + qemu/roms/u-boot/drivers/net/phy/smsc.c | 79 + qemu/roms/u-boot/drivers/net/phy/teranetics.c | 112 + qemu/roms/u-boot/drivers/net/phy/vitesse.c | 373 ++ qemu/roms/u-boot/drivers/net/plb2800_eth.c | 373 ++ qemu/roms/u-boot/drivers/net/rtl8139.c | 539 ++ qemu/roms/u-boot/drivers/net/rtl8169.c | 967 ++++ qemu/roms/u-boot/drivers/net/sh_eth.c | 655 +++ qemu/roms/u-boot/drivers/net/sh_eth.h | 624 +++ qemu/roms/u-boot/drivers/net/smc91111.c | 1355 +++++ qemu/roms/u-boot/drivers/net/smc91111.h | 789 +++ qemu/roms/u-boot/drivers/net/smc911x.c | 282 + qemu/roms/u-boot/drivers/net/smc911x.h | 500 ++ qemu/roms/u-boot/drivers/net/sunxi_wemac.c | 525 ++ qemu/roms/u-boot/drivers/net/tsec.c | 688 +++ qemu/roms/u-boot/drivers/net/tsi108_eth.c | 1016 ++++ qemu/roms/u-boot/drivers/net/uli526x.c | 996 ++++ qemu/roms/u-boot/drivers/net/vsc7385.c | 97 + qemu/roms/u-boot/drivers/net/xilinx_axi_emac.c | 656 +++ qemu/roms/u-boot/drivers/net/xilinx_emaclite.c | 392 ++ qemu/roms/u-boot/drivers/net/xilinx_ll_temac.c | 402 ++ qemu/roms/u-boot/drivers/net/xilinx_ll_temac.h | 307 ++ .../roms/u-boot/drivers/net/xilinx_ll_temac_fifo.c | 139 + .../roms/u-boot/drivers/net/xilinx_ll_temac_fifo.h | 118 + .../roms/u-boot/drivers/net/xilinx_ll_temac_mdio.c | 177 + .../roms/u-boot/drivers/net/xilinx_ll_temac_mdio.h | 50 + .../roms/u-boot/drivers/net/xilinx_ll_temac_sdma.c | 366 ++ .../roms/u-boot/drivers/net/xilinx_ll_temac_sdma.h | 277 + qemu/roms/u-boot/drivers/net/zynq_gem.c | 580 +++ qemu/roms/u-boot/drivers/pci/Makefile | 19 + qemu/roms/u-boot/drivers/pci/fsl_pci_init.c | 882 ++++ qemu/roms/u-boot/drivers/pci/pci.c | 789 +++ qemu/roms/u-boot/drivers/pci/pci_auto.c | 451 ++ qemu/roms/u-boot/drivers/pci/pci_ftpci100.c | 318 ++ qemu/roms/u-boot/drivers/pci/pci_gt64120.c | 176 + qemu/roms/u-boot/drivers/pci/pci_indirect.c | 125 + qemu/roms/u-boot/drivers/pci/pci_msc01.c | 125 + qemu/roms/u-boot/drivers/pci/pci_sh4.c | 82 + qemu/roms/u-boot/drivers/pci/pci_sh7751.c | 187 + qemu/roms/u-boot/drivers/pci/pci_sh7780.c | 92 + qemu/roms/u-boot/drivers/pci/pcie_imx.c | 617 +++ qemu/roms/u-boot/drivers/pci/tsi108_pci.c | 167 + qemu/roms/u-boot/drivers/pci/w83c553f.c | 206 + qemu/roms/u-boot/drivers/pcmcia/Makefile | 13 + qemu/roms/u-boot/drivers/pcmcia/i82365.c | 989 ++++ qemu/roms/u-boot/drivers/pcmcia/marubun_pcmcia.c | 100 + qemu/roms/u-boot/drivers/pcmcia/mpc8xx_pcmcia.c | 274 + qemu/roms/u-boot/drivers/pcmcia/rpx_pcmcia.c | 73 + qemu/roms/u-boot/drivers/pcmcia/ti_pci1410a.c | 623 +++ qemu/roms/u-boot/drivers/pcmcia/tqm8xx_pcmcia.c | 305 ++ qemu/roms/u-boot/drivers/power/Makefile | 19 + qemu/roms/u-boot/drivers/power/battery/Makefile | 9 + qemu/roms/u-boot/drivers/power/battery/bat_trats.c | 89 + .../roms/u-boot/drivers/power/battery/bat_trats2.c | 65 + qemu/roms/u-boot/drivers/power/exynos-tmu.c | 346 ++ qemu/roms/u-boot/drivers/power/ftpmu010.c | 88 + qemu/roms/u-boot/drivers/power/fuel_gauge/Makefile | 8 + .../u-boot/drivers/power/fuel_gauge/fg_max17042.c | 286 ++ qemu/roms/u-boot/drivers/power/mfd/Makefile | 10 + qemu/roms/u-boot/drivers/power/mfd/fg_max77693.c | 139 + qemu/roms/u-boot/drivers/power/mfd/muic_max77693.c | 77 + qemu/roms/u-boot/drivers/power/mfd/pmic_max77693.c | 96 + qemu/roms/u-boot/drivers/power/palmas.c | 160 + qemu/roms/u-boot/drivers/power/pmic/Makefile | 14 + qemu/roms/u-boot/drivers/power/pmic/muic_max8997.c | 74 + .../roms/u-boot/drivers/power/pmic/pmic_max77686.c | 255 + qemu/roms/u-boot/drivers/power/pmic/pmic_max8997.c | 107 + qemu/roms/u-boot/drivers/power/pmic/pmic_max8998.c | 33 + .../roms/u-boot/drivers/power/pmic/pmic_pfuze100.c | 32 + .../roms/u-boot/drivers/power/pmic/pmic_tps65217.c | 109 + .../roms/u-boot/drivers/power/pmic/pmic_tps65910.c | 83 + qemu/roms/u-boot/drivers/power/power_core.c | 223 + qemu/roms/u-boot/drivers/power/power_dialog.c | 32 + qemu/roms/u-boot/drivers/power/power_fsl.c | 58 + qemu/roms/u-boot/drivers/power/power_i2c.c | 109 + qemu/roms/u-boot/drivers/power/power_spi.c | 92 + qemu/roms/u-boot/drivers/power/tps6586x.c | 264 + qemu/roms/u-boot/drivers/power/twl4030.c | 101 + qemu/roms/u-boot/drivers/power/twl6030.c | 235 + qemu/roms/u-boot/drivers/qe/Makefile | 8 + qemu/roms/u-boot/drivers/qe/fdt.c | 74 + qemu/roms/u-boot/drivers/qe/qe.c | 479 ++ qemu/roms/u-boot/drivers/qe/qe.h | 288 ++ qemu/roms/u-boot/drivers/qe/uccf.c | 389 ++ qemu/roms/u-boot/drivers/qe/uccf.h | 118 + qemu/roms/u-boot/drivers/qe/uec.c | 1421 +++++ qemu/roms/u-boot/drivers/qe/uec.h | 743 +++ qemu/roms/u-boot/drivers/qe/uec_phy.c | 912 ++++ qemu/roms/u-boot/drivers/qe/uec_phy.h | 240 + qemu/roms/u-boot/drivers/rtc/Makefile | 52 + qemu/roms/u-boot/drivers/rtc/at91sam9_rtt.c | 84 + qemu/roms/u-boot/drivers/rtc/bfin_rtc.c | 122 + qemu/roms/u-boot/drivers/rtc/date.c | 140 + qemu/roms/u-boot/drivers/rtc/davinci.c | 83 + qemu/roms/u-boot/drivers/rtc/ds12887.c | 217 + qemu/roms/u-boot/drivers/rtc/ds1302.c | 332 ++ qemu/roms/u-boot/drivers/rtc/ds1306.c | 443 ++ qemu/roms/u-boot/drivers/rtc/ds1307.c | 180 + qemu/roms/u-boot/drivers/rtc/ds1337.c | 195 + qemu/roms/u-boot/drivers/rtc/ds1374.c | 235 + qemu/roms/u-boot/drivers/rtc/ds1556.c | 180 + qemu/roms/u-boot/drivers/rtc/ds164x.c | 176 + qemu/roms/u-boot/drivers/rtc/ds174x.c | 177 + qemu/roms/u-boot/drivers/rtc/ds3231.c | 161 + qemu/roms/u-boot/drivers/rtc/ftrtc010.c | 123 + qemu/roms/u-boot/drivers/rtc/imxdi.c | 228 + qemu/roms/u-boot/drivers/rtc/isl1208.c | 147 + qemu/roms/u-boot/drivers/rtc/m41t11.c | 171 + qemu/roms/u-boot/drivers/rtc/m41t60.c | 241 + qemu/roms/u-boot/drivers/rtc/m41t62.c | 135 + qemu/roms/u-boot/drivers/rtc/m41t94.c | 123 + qemu/roms/u-boot/drivers/rtc/m48t35ax.c | 142 + qemu/roms/u-boot/drivers/rtc/max6900.c | 109 + qemu/roms/u-boot/drivers/rtc/mc13xxx-rtc.c | 67 + qemu/roms/u-boot/drivers/rtc/mc146818.c | 152 + qemu/roms/u-boot/drivers/rtc/mcfrtc.c | 109 + qemu/roms/u-boot/drivers/rtc/mk48t59.c | 180 + qemu/roms/u-boot/drivers/rtc/mpc5xxx.c | 128 + qemu/roms/u-boot/drivers/rtc/mpc8xx.c | 61 + qemu/roms/u-boot/drivers/rtc/mvrtc.c | 109 + qemu/roms/u-boot/drivers/rtc/mvrtc.h | 49 + qemu/roms/u-boot/drivers/rtc/mx27rtc.c | 70 + qemu/roms/u-boot/drivers/rtc/mxsrtc.c | 73 + qemu/roms/u-boot/drivers/rtc/pcf8563.c | 122 + qemu/roms/u-boot/drivers/rtc/pl031.c | 109 + qemu/roms/u-boot/drivers/rtc/pt7c4338.c | 128 + qemu/roms/u-boot/drivers/rtc/rs5c372.c | 281 + qemu/roms/u-boot/drivers/rtc/rtc4543.c | 101 + qemu/roms/u-boot/drivers/rtc/rv3029.c | 190 + qemu/roms/u-boot/drivers/rtc/rx8025.c | 211 + qemu/roms/u-boot/drivers/rtc/s3c24x0_rtc.c | 154 + qemu/roms/u-boot/drivers/rtc/x1205.c | 165 + qemu/roms/u-boot/drivers/serial/Makefile | 40 + qemu/roms/u-boot/drivers/serial/altera_jtag_uart.c | 85 + qemu/roms/u-boot/drivers/serial/altera_uart.c | 100 + qemu/roms/u-boot/drivers/serial/arm_dcc.c | 158 + qemu/roms/u-boot/drivers/serial/atmel_usart.c | 111 + qemu/roms/u-boot/drivers/serial/atmel_usart.h | 294 ++ qemu/roms/u-boot/drivers/serial/lpc32xx_hsuart.c | 89 + qemu/roms/u-boot/drivers/serial/mcfuart.c | 130 + qemu/roms/u-boot/drivers/serial/mxs_auart.c | 151 + qemu/roms/u-boot/drivers/serial/ns16550.c | 147 + qemu/roms/u-boot/drivers/serial/opencores_yanu.c | 157 + qemu/roms/u-boot/drivers/serial/sandbox.c | 111 + qemu/roms/u-boot/drivers/serial/serial.c | 577 +++ qemu/roms/u-boot/drivers/serial/serial_arc.c | 115 + qemu/roms/u-boot/drivers/serial/serial_bfin.c | 411 ++ qemu/roms/u-boot/drivers/serial/serial_imx.c | 223 + qemu/roms/u-boot/drivers/serial/serial_ks8695.c | 121 + qemu/roms/u-boot/drivers/serial/serial_lpuart.c | 119 + qemu/roms/u-boot/drivers/serial/serial_max3100.c | 294 ++ qemu/roms/u-boot/drivers/serial/serial_mxc.c | 224 + qemu/roms/u-boot/drivers/serial/serial_ns16550.c | 301 ++ qemu/roms/u-boot/drivers/serial/serial_pl01x.c | 252 + qemu/roms/u-boot/drivers/serial/serial_pl01x.h | 128 + qemu/roms/u-boot/drivers/serial/serial_pxa.c | 299 ++ qemu/roms/u-boot/drivers/serial/serial_s3c24x0.c | 267 + qemu/roms/u-boot/drivers/serial/serial_s5p.c | 301 ++ qemu/roms/u-boot/drivers/serial/serial_sa1100.c | 162 + qemu/roms/u-boot/drivers/serial/serial_sh.c | 202 + qemu/roms/u-boot/drivers/serial/serial_sh.h | 740 +++ qemu/roms/u-boot/drivers/serial/serial_xuartlite.c | 162 + qemu/roms/u-boot/drivers/serial/serial_zynq.c | 230 + qemu/roms/u-boot/drivers/serial/usbtty.c | 1046 ++++ qemu/roms/u-boot/drivers/serial/usbtty.h | 79 + qemu/roms/u-boot/drivers/sound/Makefile | 13 + qemu/roms/u-boot/drivers/sound/max98095.c | 591 +++ qemu/roms/u-boot/drivers/sound/max98095.h | 317 ++ qemu/roms/u-boot/drivers/sound/samsung-i2s.c | 371 ++ qemu/roms/u-boot/drivers/sound/sandbox.c | 23 + qemu/roms/u-boot/drivers/sound/sound-i2s.c | 208 + qemu/roms/u-boot/drivers/sound/sound.c | 37 + qemu/roms/u-boot/drivers/sound/wm8994.c | 915 ++++ qemu/roms/u-boot/drivers/sound/wm8994.h | 69 + qemu/roms/u-boot/drivers/sound/wm8994_registers.h | 326 ++ qemu/roms/u-boot/drivers/spi/Makefile | 42 + qemu/roms/u-boot/drivers/spi/altera_spi.c | 168 + qemu/roms/u-boot/drivers/spi/andes_spi.c | 284 + qemu/roms/u-boot/drivers/spi/andes_spi.h | 115 + qemu/roms/u-boot/drivers/spi/armada100_spi.c | 203 + qemu/roms/u-boot/drivers/spi/atmel_dataflash_spi.c | 184 + qemu/roms/u-boot/drivers/spi/atmel_spi.c | 211 + qemu/roms/u-boot/drivers/spi/atmel_spi.h | 96 + qemu/roms/u-boot/drivers/spi/bfin_spi.c | 308 ++ qemu/roms/u-boot/drivers/spi/bfin_spi6xx.c | 304 ++ qemu/roms/u-boot/drivers/spi/cf_qspi.c | 355 ++ qemu/roms/u-boot/drivers/spi/cf_spi.c | 347 ++ qemu/roms/u-boot/drivers/spi/davinci_spi.c | 321 ++ qemu/roms/u-boot/drivers/spi/davinci_spi.h | 121 + qemu/roms/u-boot/drivers/spi/exynos_spi.c | 579 +++ qemu/roms/u-boot/drivers/spi/fdt_spi.c | 186 + qemu/roms/u-boot/drivers/spi/fsl_espi.c | 315 ++ qemu/roms/u-boot/drivers/spi/ftssp010_spi.c | 508 ++ qemu/roms/u-boot/drivers/spi/ich.c | 741 +++ qemu/roms/u-boot/drivers/spi/ich.h | 127 + qemu/roms/u-boot/drivers/spi/kirkwood_spi.c | 214 + qemu/roms/u-boot/drivers/spi/mpc52xx_spi.c | 90 + qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c | 166 + qemu/roms/u-boot/drivers/spi/mxc_spi.c | 448 ++ qemu/roms/u-boot/drivers/spi/mxs_spi.c | 362 ++ qemu/roms/u-boot/drivers/spi/oc_tiny_spi.c | 245 + qemu/roms/u-boot/drivers/spi/omap3_spi.c | 453 ++ qemu/roms/u-boot/drivers/spi/omap3_spi.h | 109 + qemu/roms/u-boot/drivers/spi/sandbox_spi.c | 217 + qemu/roms/u-boot/drivers/spi/sh_qspi.c | 278 + qemu/roms/u-boot/drivers/spi/sh_spi.c | 265 + qemu/roms/u-boot/drivers/spi/sh_spi.h | 80 + qemu/roms/u-boot/drivers/spi/soft_spi.c | 175 + qemu/roms/u-boot/drivers/spi/spi.c | 58 + qemu/roms/u-boot/drivers/spi/tegra114_spi.c | 398 ++ qemu/roms/u-boot/drivers/spi/tegra20_sflash.c | 341 ++ qemu/roms/u-boot/drivers/spi/tegra20_slink.c | 359 ++ qemu/roms/u-boot/drivers/spi/ti_qspi.c | 341 ++ qemu/roms/u-boot/drivers/spi/xilinx_spi.c | 215 + qemu/roms/u-boot/drivers/spi/xilinx_spi.h | 138 + qemu/roms/u-boot/drivers/spi/zynq_spi.c | 280 + qemu/roms/u-boot/drivers/tpm/Makefile | 11 + qemu/roms/u-boot/drivers/tpm/tpm.c | 629 +++ qemu/roms/u-boot/drivers/tpm/tpm_atmel_twi.c | 121 + qemu/roms/u-boot/drivers/tpm/tpm_private.h | 137 + qemu/roms/u-boot/drivers/tpm/tpm_tis_i2c.c | 637 +++ qemu/roms/u-boot/drivers/tpm/tpm_tis_lpc.c | 479 ++ qemu/roms/u-boot/drivers/tpm/tpm_tis_sandbox.c | 260 + qemu/roms/u-boot/drivers/twserial/Makefile | 8 + qemu/roms/u-boot/drivers/twserial/soft_tws.c | 94 + qemu/roms/u-boot/drivers/usb/eth/Makefile | 12 + qemu/roms/u-boot/drivers/usb/eth/asix.c | 700 +++ qemu/roms/u-boot/drivers/usb/eth/mcs7830.c | 812 +++ qemu/roms/u-boot/drivers/usb/eth/smsc95xx.c | 897 ++++ qemu/roms/u-boot/drivers/usb/eth/usb_ether.c | 156 + qemu/roms/u-boot/drivers/usb/gadget/Makefile | 38 + .../u-boot/drivers/usb/gadget/atmel_usba_udc.c | 1306 +++++ .../u-boot/drivers/usb/gadget/atmel_usba_udc.h | 326 ++ qemu/roms/u-boot/drivers/usb/gadget/ci_udc.c | 760 +++ qemu/roms/u-boot/drivers/usb/gadget/ci_udc.h | 144 + qemu/roms/u-boot/drivers/usb/gadget/composite.c | 1091 ++++ qemu/roms/u-boot/drivers/usb/gadget/config.c | 108 + qemu/roms/u-boot/drivers/usb/gadget/core.c | 669 +++ .../u-boot/drivers/usb/gadget/designware_udc.c | 1019 ++++ qemu/roms/u-boot/drivers/usb/gadget/ep0.c | 597 +++ qemu/roms/u-boot/drivers/usb/gadget/ep0.h | 26 + qemu/roms/u-boot/drivers/usb/gadget/epautoconf.c | 296 ++ qemu/roms/u-boot/drivers/usb/gadget/ether.c | 2555 +++++++++ qemu/roms/u-boot/drivers/usb/gadget/f_dfu.c | 822 +++ qemu/roms/u-boot/drivers/usb/gadget/f_dfu.h | 87 + .../u-boot/drivers/usb/gadget/f_mass_storage.c | 2783 ++++++++++ qemu/roms/u-boot/drivers/usb/gadget/f_thor.c | 1008 ++++ qemu/roms/u-boot/drivers/usb/gadget/f_thor.h | 124 + qemu/roms/u-boot/drivers/usb/gadget/fotg210.c | 962 ++++ qemu/roms/u-boot/drivers/usb/gadget/g_dnl.c | 264 + qemu/roms/u-boot/drivers/usb/gadget/gadget_chips.h | 227 + qemu/roms/u-boot/drivers/usb/gadget/mpc8xx_udc.c | 1386 +++++ qemu/roms/u-boot/drivers/usb/gadget/ndis.h | 217 + qemu/roms/u-boot/drivers/usb/gadget/omap1510_udc.c | 1555 ++++++ qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c | 2047 ++++++++ qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h | 150 + qemu/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c | 703 +++ qemu/roms/u-boot/drivers/usb/gadget/regs-otg.h | 273 + qemu/roms/u-boot/drivers/usb/gadget/rndis.c | 1316 +++++ qemu/roms/u-boot/drivers/usb/gadget/rndis.h | 260 + qemu/roms/u-boot/drivers/usb/gadget/s3c_udc_otg.c | 890 ++++ .../drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 1480 ++++++ .../u-boot/drivers/usb/gadget/storage_common.c | 624 +++ qemu/roms/u-boot/drivers/usb/gadget/usbstring.c | 139 + qemu/roms/u-boot/drivers/usb/host/Makefile | 44 + qemu/roms/u-boot/drivers/usb/host/ehci-armada100.c | 48 + qemu/roms/u-boot/drivers/usb/host/ehci-atmel.c | 73 + qemu/roms/u-boot/drivers/usb/host/ehci-exynos.c | 231 + qemu/roms/u-boot/drivers/usb/host/ehci-faraday.c | 147 + qemu/roms/u-boot/drivers/usb/host/ehci-fsl.c | 156 + qemu/roms/u-boot/drivers/usb/host/ehci-hcd.c | 1404 +++++ qemu/roms/u-boot/drivers/usb/host/ehci-marvell.c | 100 + qemu/roms/u-boot/drivers/usb/host/ehci-mpc512x.c | 140 + qemu/roms/u-boot/drivers/usb/host/ehci-mx5.c | 258 + qemu/roms/u-boot/drivers/usb/host/ehci-mx6.c | 248 + qemu/roms/u-boot/drivers/usb/host/ehci-mxc.c | 250 + qemu/roms/u-boot/drivers/usb/host/ehci-mxs.c | 158 + qemu/roms/u-boot/drivers/usb/host/ehci-omap.c | 295 ++ qemu/roms/u-boot/drivers/usb/host/ehci-pci.c | 138 + qemu/roms/u-boot/drivers/usb/host/ehci-ppc4xx.c | 34 + qemu/roms/u-boot/drivers/usb/host/ehci-rmobile.c | 130 + qemu/roms/u-boot/drivers/usb/host/ehci-spear.c | 44 + qemu/roms/u-boot/drivers/usb/host/ehci-tegra.c | 827 +++ qemu/roms/u-boot/drivers/usb/host/ehci-vct.c | 45 + qemu/roms/u-boot/drivers/usb/host/ehci.h | 257 + qemu/roms/u-boot/drivers/usb/host/isp116x-hcd.c | 1329 +++++ qemu/roms/u-boot/drivers/usb/host/isp116x.h | 476 ++ qemu/roms/u-boot/drivers/usb/host/ohci-at91.c | 96 + qemu/roms/u-boot/drivers/usb/host/ohci-da8xx.c | 40 + qemu/roms/u-boot/drivers/usb/host/ohci-hcd.c | 1885 +++++++ qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.c | 1691 ++++++ qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.h | 409 ++ qemu/roms/u-boot/drivers/usb/host/ohci.h | 491 ++ qemu/roms/u-boot/drivers/usb/host/r8a66597-hcd.c | 832 +++ qemu/roms/u-boot/drivers/usb/host/r8a66597.h | 659 +++ qemu/roms/u-boot/drivers/usb/host/sl811-hcd.c | 714 +++ qemu/roms/u-boot/drivers/usb/host/sl811.h | 104 + qemu/roms/u-boot/drivers/usb/host/utmi-armada100.c | 80 + qemu/roms/u-boot/drivers/usb/host/xhci-exynos5.c | 328 ++ qemu/roms/u-boot/drivers/usb/host/xhci-mem.c | 720 +++ qemu/roms/u-boot/drivers/usb/host/xhci-omap.c | 158 + qemu/roms/u-boot/drivers/usb/host/xhci-ring.c | 939 ++++ qemu/roms/u-boot/drivers/usb/host/xhci.c | 1030 ++++ qemu/roms/u-boot/drivers/usb/host/xhci.h | 1255 +++++ qemu/roms/u-boot/drivers/usb/musb-new/Makefile | 14 + qemu/roms/u-boot/drivers/usb/musb-new/am35x.c | 709 +++ .../u-boot/drivers/usb/musb-new/linux-compat.h | 98 + qemu/roms/u-boot/drivers/usb/musb-new/musb_core.c | 2500 +++++++++ qemu/roms/u-boot/drivers/usb/musb-new/musb_core.h | 623 +++ qemu/roms/u-boot/drivers/usb/musb-new/musb_debug.h | 58 + qemu/roms/u-boot/drivers/usb/musb-new/musb_dma.h | 186 + qemu/roms/u-boot/drivers/usb/musb-new/musb_dsps.c | 771 +++ .../roms/u-boot/drivers/usb/musb-new/musb_gadget.c | 2333 +++++++++ .../roms/u-boot/drivers/usb/musb-new/musb_gadget.h | 130 + .../u-boot/drivers/usb/musb-new/musb_gadget_ep0.c | 1089 ++++ qemu/roms/u-boot/drivers/usb/musb-new/musb_host.c | 2400 +++++++++ qemu/roms/u-boot/drivers/usb/musb-new/musb_host.h | 114 + qemu/roms/u-boot/drivers/usb/musb-new/musb_io.h | 146 + qemu/roms/u-boot/drivers/usb/musb-new/musb_regs.h | 645 +++ qemu/roms/u-boot/drivers/usb/musb-new/musb_uboot.c | 242 + qemu/roms/u-boot/drivers/usb/musb-new/omap2430.c | 626 +++ qemu/roms/u-boot/drivers/usb/musb-new/omap2430.h | 56 + qemu/roms/u-boot/drivers/usb/musb-new/usb-compat.h | 88 + qemu/roms/u-boot/drivers/usb/musb/Makefile | 14 + qemu/roms/u-boot/drivers/usb/musb/am35x.c | 139 + qemu/roms/u-boot/drivers/usb/musb/am35x.h | 82 + qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.c | 172 + qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.h | 99 + qemu/roms/u-boot/drivers/usb/musb/da8xx.c | 128 + qemu/roms/u-boot/drivers/usb/musb/davinci.c | 124 + qemu/roms/u-boot/drivers/usb/musb/davinci.h | 74 + qemu/roms/u-boot/drivers/usb/musb/musb_core.c | 155 + qemu/roms/u-boot/drivers/usb/musb/musb_core.h | 395 ++ qemu/roms/u-boot/drivers/usb/musb/musb_debug.h | 192 + qemu/roms/u-boot/drivers/usb/musb/musb_hcd.c | 1172 +++++ qemu/roms/u-boot/drivers/usb/musb/musb_hcd.h | 99 + qemu/roms/u-boot/drivers/usb/musb/musb_udc.c | 959 ++++ qemu/roms/u-boot/drivers/usb/musb/omap3.c | 145 + qemu/roms/u-boot/drivers/usb/musb/omap3.h | 39 + qemu/roms/u-boot/drivers/usb/phy/Makefile | 9 + qemu/roms/u-boot/drivers/usb/phy/omap_usb_phy.c | 261 + qemu/roms/u-boot/drivers/usb/phy/twl4030.c | 176 + qemu/roms/u-boot/drivers/usb/ulpi/Makefile | 9 + .../u-boot/drivers/usb/ulpi/omap-ulpi-viewport.c | 81 + qemu/roms/u-boot/drivers/usb/ulpi/ulpi-viewport.c | 120 + qemu/roms/u-boot/drivers/usb/ulpi/ulpi.c | 245 + qemu/roms/u-boot/drivers/video/Makefile | 42 + qemu/roms/u-boot/drivers/video/ati_ids.h | 211 + qemu/roms/u-boot/drivers/video/ati_radeon_fb.c | 764 +++ qemu/roms/u-boot/drivers/video/ati_radeon_fb.h | 282 + qemu/roms/u-boot/drivers/video/atmel_hlcdfb.c | 197 + qemu/roms/u-boot/drivers/video/atmel_lcdfb.c | 138 + qemu/roms/u-boot/drivers/video/bcm2835.c | 116 + qemu/roms/u-boot/drivers/video/bus_vcxk.c | 426 ++ qemu/roms/u-boot/drivers/video/cfb_console.c | 2323 +++++++++ qemu/roms/u-boot/drivers/video/coreboot_fb.c | 85 + qemu/roms/u-boot/drivers/video/ct69000.c | 1267 +++++ qemu/roms/u-boot/drivers/video/da8xx-fb.c | 1054 ++++ qemu/roms/u-boot/drivers/video/da8xx-fb.h | 116 + qemu/roms/u-boot/drivers/video/exynos_dp.c | 982 ++++ .../roms/u-boot/drivers/video/exynos_dp_lowlevel.c | 1257 +++++ .../roms/u-boot/drivers/video/exynos_dp_lowlevel.h | 68 + qemu/roms/u-boot/drivers/video/exynos_fb.c | 322 ++ qemu/roms/u-boot/drivers/video/exynos_fb.h | 41 + qemu/roms/u-boot/drivers/video/exynos_fimd.c | 366 ++ qemu/roms/u-boot/drivers/video/exynos_mipi_dsi.c | 336 ++ .../u-boot/drivers/video/exynos_mipi_dsi_common.c | 620 +++ .../u-boot/drivers/video/exynos_mipi_dsi_common.h | 35 + .../drivers/video/exynos_mipi_dsi_lowlevel.c | 639 +++ .../drivers/video/exynos_mipi_dsi_lowlevel.h | 98 + qemu/roms/u-boot/drivers/video/exynos_pwm_bl.c | 45 + qemu/roms/u-boot/drivers/video/formike.c | 512 ++ qemu/roms/u-boot/drivers/video/fsl_diu_fb.c | 417 ++ qemu/roms/u-boot/drivers/video/ipu.h | 261 + qemu/roms/u-boot/drivers/video/ipu_common.c | 1196 +++++ qemu/roms/u-boot/drivers/video/ipu_disp.c | 1335 +++++ qemu/roms/u-boot/drivers/video/ipu_regs.h | 412 ++ qemu/roms/u-boot/drivers/video/l5f31188.c | 192 + qemu/roms/u-boot/drivers/video/ld9040.c | 128 + qemu/roms/u-boot/drivers/video/mb862xx.c | 484 ++ qemu/roms/u-boot/drivers/video/mb86r0xgdc.c | 168 + qemu/roms/u-boot/drivers/video/mpc8xx_lcd.c | 548 ++ qemu/roms/u-boot/drivers/video/mx3fb.c | 915 ++++ qemu/roms/u-boot/drivers/video/mxc_ipuv3_fb.c | 621 +++ qemu/roms/u-boot/drivers/video/mxcfb.h | 52 + qemu/roms/u-boot/drivers/video/mxsfb.c | 224 + qemu/roms/u-boot/drivers/video/omap3_dss.c | 167 + qemu/roms/u-boot/drivers/video/pxa_lcd.c | 625 +++ qemu/roms/u-boot/drivers/video/s6e63d6.c | 60 + qemu/roms/u-boot/drivers/video/s6e8ax0.c | 265 + qemu/roms/u-boot/drivers/video/sandbox_sdl.c | 79 + qemu/roms/u-boot/drivers/video/scf0403_lcd.c | 296 ++ qemu/roms/u-boot/drivers/video/sed13806.c | 291 ++ qemu/roms/u-boot/drivers/video/sed156x.c | 546 ++ qemu/roms/u-boot/drivers/video/sm501.c | 224 + qemu/roms/u-boot/drivers/video/smiLynxEM.c | 838 +++ qemu/roms/u-boot/drivers/video/tegra.c | 299 ++ qemu/roms/u-boot/drivers/video/videomodes.c | 262 + qemu/roms/u-boot/drivers/video/videomodes.h | 75 + qemu/roms/u-boot/drivers/watchdog/Makefile | 17 + qemu/roms/u-boot/drivers/watchdog/at91sam9_wdt.c | 77 + qemu/roms/u-boot/drivers/watchdog/bfin_wdt.c | 26 + qemu/roms/u-boot/drivers/watchdog/ftwdt010_wdt.c | 92 + qemu/roms/u-boot/drivers/watchdog/imx_watchdog.c | 67 + qemu/roms/u-boot/drivers/watchdog/omap_wdt.c | 121 + qemu/roms/u-boot/drivers/watchdog/s5p_wdt.c | 43 + qemu/roms/u-boot/drivers/watchdog/tnetv107x_wdt.c | 165 + qemu/roms/u-boot/drivers/watchdog/xilinx_tb_wdt.c | 69 + 842 files changed, 332489 insertions(+) create mode 100644 qemu/roms/u-boot/drivers/Makefile create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/Makefile create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/atibios.c create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/besys.c create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/bios.c create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/biosemu.c create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/biosemui.h create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/include/biosemu.h create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/include/x86emu.h create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/debug.h create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/decode.h create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/ops.h create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/prim_ops.h create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/regs.h create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/x86emui.h create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/x86emu/debug.c create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/x86emu/decode.c create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/x86emu/ops.c create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/x86emu/ops2.c create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/x86emu/prim_ops.c create mode 100644 qemu/roms/u-boot/drivers/bios_emulator/x86emu/sys.c create mode 100644 qemu/roms/u-boot/drivers/block/Makefile create mode 100644 qemu/roms/u-boot/drivers/block/ahci.c create mode 100644 qemu/roms/u-boot/drivers/block/ata_piix.c create mode 100644 qemu/roms/u-boot/drivers/block/ata_piix.h create mode 100644 qemu/roms/u-boot/drivers/block/dwc_ahsata.c create mode 100644 qemu/roms/u-boot/drivers/block/dwc_ahsata.h create mode 100644 qemu/roms/u-boot/drivers/block/fsl_sata.c create mode 100644 qemu/roms/u-boot/drivers/block/fsl_sata.h create mode 100644 qemu/roms/u-boot/drivers/block/ftide020.c create mode 100644 qemu/roms/u-boot/drivers/block/ftide020.h create mode 100644 qemu/roms/u-boot/drivers/block/libata.c create mode 100644 qemu/roms/u-boot/drivers/block/mvsata_ide.c create mode 100644 qemu/roms/u-boot/drivers/block/mxc_ata.c create mode 100644 qemu/roms/u-boot/drivers/block/pata_bfin.c create mode 100644 qemu/roms/u-boot/drivers/block/pata_bfin.h create mode 100644 qemu/roms/u-boot/drivers/block/sandbox.c create mode 100644 qemu/roms/u-boot/drivers/block/sata_dwc.c create mode 100644 qemu/roms/u-boot/drivers/block/sata_dwc.h create mode 100644 qemu/roms/u-boot/drivers/block/sata_sil.c create mode 100644 qemu/roms/u-boot/drivers/block/sata_sil.h create mode 100644 qemu/roms/u-boot/drivers/block/sata_sil3114.c create mode 100644 qemu/roms/u-boot/drivers/block/sata_sil3114.h create mode 100644 qemu/roms/u-boot/drivers/block/sil680.c create mode 100644 qemu/roms/u-boot/drivers/block/sym53c8xx.c create mode 100644 qemu/roms/u-boot/drivers/block/systemace.c create mode 100644 qemu/roms/u-boot/drivers/bootcount/Makefile create mode 100644 qemu/roms/u-boot/drivers/bootcount/bootcount.c create mode 100644 qemu/roms/u-boot/drivers/bootcount/bootcount_at91.c create mode 100644 qemu/roms/u-boot/drivers/bootcount/bootcount_blackfin.c create mode 100644 qemu/roms/u-boot/drivers/bootcount/bootcount_davinci.c create mode 100644 qemu/roms/u-boot/drivers/bootcount/bootcount_env.c create mode 100644 qemu/roms/u-boot/drivers/bootcount/bootcount_i2c.c create mode 100644 qemu/roms/u-boot/drivers/bootcount/bootcount_ram.c create mode 100644 qemu/roms/u-boot/drivers/core/Makefile create mode 100644 qemu/roms/u-boot/drivers/core/device.c create mode 100644 qemu/roms/u-boot/drivers/core/lists.c create mode 100644 qemu/roms/u-boot/drivers/core/root.c create mode 100644 qemu/roms/u-boot/drivers/core/uclass.c create mode 100644 qemu/roms/u-boot/drivers/core/util.c create mode 100644 qemu/roms/u-boot/drivers/crypto/Makefile create mode 100644 qemu/roms/u-boot/drivers/crypto/ace_sha.c create mode 100644 qemu/roms/u-boot/drivers/crypto/ace_sha.h create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/Makefile create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/arm_ddr_gen3.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/ctrl_regs.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/ddr1_dimm_params.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/ddr2_dimm_params.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/ddr3_dimm_params.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/ddr4_dimm_params.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/fsl_ddr_gen4.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/interactive.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/lc_common_dimm_params.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/main.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen1.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen2.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen3.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/mpc86xx_ddr.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/options.c create mode 100644 qemu/roms/u-boot/drivers/ddr/fsl/util.c create mode 100644 qemu/roms/u-boot/drivers/demo/Makefile create mode 100644 qemu/roms/u-boot/drivers/demo/demo-pdata.c create mode 100644 qemu/roms/u-boot/drivers/demo/demo-shape.c create mode 100644 qemu/roms/u-boot/drivers/demo/demo-simple.c create mode 100644 qemu/roms/u-boot/drivers/demo/demo-uclass.c create mode 100644 qemu/roms/u-boot/drivers/dfu/Makefile create mode 100644 qemu/roms/u-boot/drivers/dfu/dfu.c create mode 100644 qemu/roms/u-boot/drivers/dfu/dfu_mmc.c create mode 100644 qemu/roms/u-boot/drivers/dfu/dfu_nand.c create mode 100644 qemu/roms/u-boot/drivers/dfu/dfu_ram.c create mode 100644 qemu/roms/u-boot/drivers/dma/MCD_dmaApi.c create mode 100644 qemu/roms/u-boot/drivers/dma/MCD_tasks.c create mode 100644 qemu/roms/u-boot/drivers/dma/MCD_tasksInit.c create mode 100644 qemu/roms/u-boot/drivers/dma/Makefile create mode 100644 qemu/roms/u-boot/drivers/dma/apbh_dma.c create mode 100644 qemu/roms/u-boot/drivers/dma/fsl_dma.c create mode 100644 qemu/roms/u-boot/drivers/dma/omap3_dma.c create mode 100644 qemu/roms/u-boot/drivers/fpga/ACEX1K.c create mode 100644 qemu/roms/u-boot/drivers/fpga/Makefile create mode 100644 qemu/roms/u-boot/drivers/fpga/altera.c create mode 100644 qemu/roms/u-boot/drivers/fpga/cyclon2.c create mode 100644 qemu/roms/u-boot/drivers/fpga/fpga.c create mode 100644 qemu/roms/u-boot/drivers/fpga/ivm_core.c create mode 100644 qemu/roms/u-boot/drivers/fpga/lattice.c create mode 100644 qemu/roms/u-boot/drivers/fpga/spartan2.c create mode 100644 qemu/roms/u-boot/drivers/fpga/spartan3.c create mode 100644 qemu/roms/u-boot/drivers/fpga/stratixII.c create mode 100644 qemu/roms/u-boot/drivers/fpga/virtex2.c create mode 100644 qemu/roms/u-boot/drivers/fpga/xilinx.c create mode 100644 qemu/roms/u-boot/drivers/fpga/zynqpl.c create mode 100644 qemu/roms/u-boot/drivers/gpio/Makefile create mode 100644 qemu/roms/u-boot/drivers/gpio/adi_gpio2.c create mode 100644 qemu/roms/u-boot/drivers/gpio/altera_pio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/at91_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/bcm2835_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/da8xx_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/db8500_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/gpio-uclass.c create mode 100644 qemu/roms/u-boot/drivers/gpio/intel_ich6_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/kona_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/kw_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/mpc83xx_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/mvgpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/mvgpio.h create mode 100644 qemu/roms/u-boot/drivers/gpio/mvmfp.c create mode 100644 qemu/roms/u-boot/drivers/gpio/mxc_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/mxs_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/omap_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/pca953x.c create mode 100644 qemu/roms/u-boot/drivers/gpio/pca9698.c create mode 100644 qemu/roms/u-boot/drivers/gpio/s3c2440_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/s5p_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/sandbox.c create mode 100644 qemu/roms/u-boot/drivers/gpio/sh_pfc.c create mode 100644 qemu/roms/u-boot/drivers/gpio/spear_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/sx151x.c create mode 100644 qemu/roms/u-boot/drivers/gpio/tca642x.c create mode 100644 qemu/roms/u-boot/drivers/gpio/tegra_gpio.c create mode 100644 qemu/roms/u-boot/drivers/gpio/xilinx_gpio.c create mode 100644 qemu/roms/u-boot/drivers/hwmon/Makefile create mode 100644 qemu/roms/u-boot/drivers/hwmon/adm1021.c create mode 100644 qemu/roms/u-boot/drivers/hwmon/adt7460.c create mode 100644 qemu/roms/u-boot/drivers/hwmon/ds1621.c create mode 100644 qemu/roms/u-boot/drivers/hwmon/ds1722.c create mode 100644 qemu/roms/u-boot/drivers/hwmon/ds1775.c create mode 100644 qemu/roms/u-boot/drivers/hwmon/lm63.c create mode 100644 qemu/roms/u-boot/drivers/hwmon/lm73.c create mode 100644 qemu/roms/u-boot/drivers/hwmon/lm75.c create mode 100644 qemu/roms/u-boot/drivers/hwmon/lm81.c create mode 100644 qemu/roms/u-boot/drivers/i2c/Makefile create mode 100644 qemu/roms/u-boot/drivers/i2c/bfin-twi_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/davinci_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/davinci_i2c.h create mode 100644 qemu/roms/u-boot/drivers/i2c/designware_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/designware_i2c.h create mode 100644 qemu/roms/u-boot/drivers/i2c/fsl_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/fti2c010.c create mode 100644 qemu/roms/u-boot/drivers/i2c/fti2c010.h create mode 100644 qemu/roms/u-boot/drivers/i2c/i2c_core.c create mode 100644 qemu/roms/u-boot/drivers/i2c/kona_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/mv_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/mv_i2c.h create mode 100644 qemu/roms/u-boot/drivers/i2c/mvtwsi.c create mode 100644 qemu/roms/u-boot/drivers/i2c/mxc_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/mxs_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.h create mode 100644 qemu/roms/u-boot/drivers/i2c/pca9564_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/ppc4xx_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/rcar_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.h create mode 100644 qemu/roms/u-boot/drivers/i2c/sh_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/sh_sh7734_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/soft_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/tegra_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/tsi108_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/u8500_i2c.c create mode 100644 qemu/roms/u-boot/drivers/i2c/u8500_i2c.h create mode 100644 qemu/roms/u-boot/drivers/i2c/zynq_i2c.c create mode 100644 qemu/roms/u-boot/drivers/input/Makefile create mode 100644 qemu/roms/u-boot/drivers/input/cros_ec_keyb.c create mode 100644 qemu/roms/u-boot/drivers/input/i8042.c create mode 100644 qemu/roms/u-boot/drivers/input/input.c create mode 100644 qemu/roms/u-boot/drivers/input/key_matrix.c create mode 100644 qemu/roms/u-boot/drivers/input/keyboard.c create mode 100644 qemu/roms/u-boot/drivers/input/pc_keyb.c create mode 100644 qemu/roms/u-boot/drivers/input/ps2mult.c create mode 100644 qemu/roms/u-boot/drivers/input/ps2ser.c create mode 100644 qemu/roms/u-boot/drivers/input/tegra-kbc.c create mode 100644 qemu/roms/u-boot/drivers/misc/Makefile create mode 100644 qemu/roms/u-boot/drivers/misc/ali512x.c create mode 100644 qemu/roms/u-boot/drivers/misc/cbmem_console.c create mode 100644 qemu/roms/u-boot/drivers/misc/cros_ec.c create mode 100644 qemu/roms/u-boot/drivers/misc/cros_ec_i2c.c create mode 100644 qemu/roms/u-boot/drivers/misc/cros_ec_lpc.c create mode 100644 qemu/roms/u-boot/drivers/misc/cros_ec_sandbox.c create mode 100644 qemu/roms/u-boot/drivers/misc/cros_ec_spi.c create mode 100644 qemu/roms/u-boot/drivers/misc/ds4510.c create mode 100644 qemu/roms/u-boot/drivers/misc/fsl_ifc.c create mode 100644 qemu/roms/u-boot/drivers/misc/fsl_iim.c create mode 100644 qemu/roms/u-boot/drivers/misc/gpio_led.c create mode 100644 qemu/roms/u-boot/drivers/misc/mc9sdz60.c create mode 100644 qemu/roms/u-boot/drivers/misc/mxc_ocotp.c create mode 100644 qemu/roms/u-boot/drivers/misc/mxs_ocotp.c create mode 100644 qemu/roms/u-boot/drivers/misc/ns87308.c create mode 100644 qemu/roms/u-boot/drivers/misc/pdsp188x.c create mode 100644 qemu/roms/u-boot/drivers/misc/status_led.c create mode 100644 qemu/roms/u-boot/drivers/misc/twl4030_led.c create mode 100644 qemu/roms/u-boot/drivers/mmc/Makefile create mode 100644 qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.h create mode 100644 qemu/roms/u-boot/drivers/mmc/bcm2835_sdhci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/bfin_sdh.c create mode 100644 qemu/roms/u-boot/drivers/mmc/davinci_mmc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/dw_mmc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/exynos_dw_mmc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/fsl_esdhc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/fsl_esdhc_spl.c create mode 100644 qemu/roms/u-boot/drivers/mmc/ftsdc010_mci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/ftsdc021_sdhci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/gen_atmel_mci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/kona_sdhci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/mmc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/mmc_private.h create mode 100644 qemu/roms/u-boot/drivers/mmc/mmc_spi.c create mode 100644 qemu/roms/u-boot/drivers/mmc/mmc_write.c create mode 100644 qemu/roms/u-boot/drivers/mmc/mv_sdhci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/mxcmmc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/mxsmmc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/omap_hsmmc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/pxa_mmc_gen.c create mode 100644 qemu/roms/u-boot/drivers/mmc/s5p_sdhci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/sdhci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/sh_mmcif.c create mode 100644 qemu/roms/u-boot/drivers/mmc/sh_mmcif.h create mode 100644 qemu/roms/u-boot/drivers/mmc/socfpga_dw_mmc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/spear_sdhci.c create mode 100644 qemu/roms/u-boot/drivers/mmc/tegra_mmc.c create mode 100644 qemu/roms/u-boot/drivers/mmc/zynq_sdhci.c create mode 100644 qemu/roms/u-boot/drivers/mtd/Makefile create mode 100644 qemu/roms/u-boot/drivers/mtd/at45.c create mode 100644 qemu/roms/u-boot/drivers/mtd/cfi_flash.c create mode 100644 qemu/roms/u-boot/drivers/mtd/cfi_mtd.c create mode 100644 qemu/roms/u-boot/drivers/mtd/dataflash.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ftsmc020.c create mode 100644 qemu/roms/u-boot/drivers/mtd/jedec_flash.c create mode 100644 qemu/roms/u-boot/drivers/mtd/mtdconcat.c create mode 100644 qemu/roms/u-boot/drivers/mtd/mtdcore.c create mode 100644 qemu/roms/u-boot/drivers/mtd/mtdpart.c create mode 100644 qemu/roms/u-boot/drivers/mtd/mw_eeprom.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/Makefile create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/am335x_spl_bch.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/atmel_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/atmel_nand_ecc.h create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/bfin_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/davinci_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/docg4.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/docg4_spl.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_spl.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_spl.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/fsl_upm.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/fsmc_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/jz4740_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/kb9202_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/kirkwood_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/kmeter1_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/mpc5121_nfc.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.h create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/mxc_nand_spl.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/mxs_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand_base.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand_bbt.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand_bch.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand_ecc.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand_ids.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand_plat.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand_spl_load.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nand_util.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/ndfc.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/nomadik.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/omap_elm.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/omap_gpmc.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/s3c2410_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.c create mode 100644 qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.h create mode 100644 qemu/roms/u-boot/drivers/mtd/onenand/Makefile create mode 100644 qemu/roms/u-boot/drivers/mtd/onenand/onenand_base.c create mode 100644 qemu/roms/u-boot/drivers/mtd/onenand/onenand_bbt.c create mode 100644 qemu/roms/u-boot/drivers/mtd/onenand/onenand_spl.c create mode 100644 qemu/roms/u-boot/drivers/mtd/onenand/onenand_uboot.c create mode 100644 qemu/roms/u-boot/drivers/mtd/onenand/samsung.c create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/Makefile create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/eeprom_m95xxx.c create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/fsl_espi_spl.c create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/ramtron.c create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/sandbox.c create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/sf.c create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/sf_internal.h create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/sf_ops.c create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/sf_params.c create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/sf_probe.c create mode 100644 qemu/roms/u-boot/drivers/mtd/spi/spi_spl_load.c create mode 100644 qemu/roms/u-boot/drivers/mtd/st_smi.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/Makefile create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/build.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/crc32.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/crc32defs.h create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/crc32table.h create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/debug.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/debug.h create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/eba.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/io.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/kapi.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/misc.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/scan.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/scan.h create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/ubi-media.h create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/ubi.h create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/upd.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/vmt.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/vtbl.c create mode 100644 qemu/roms/u-boot/drivers/mtd/ubi/wl.c create mode 100644 qemu/roms/u-boot/drivers/net/4xx_enet.c create mode 100644 qemu/roms/u-boot/drivers/net/8390.h create mode 100644 qemu/roms/u-boot/drivers/net/Makefile create mode 100644 qemu/roms/u-boot/drivers/net/altera_tse.c create mode 100644 qemu/roms/u-boot/drivers/net/altera_tse.h create mode 100644 qemu/roms/u-boot/drivers/net/armada100_fec.c create mode 100644 qemu/roms/u-boot/drivers/net/armada100_fec.h create mode 100644 qemu/roms/u-boot/drivers/net/at91_emac.c create mode 100644 qemu/roms/u-boot/drivers/net/ax88180.c create mode 100644 qemu/roms/u-boot/drivers/net/ax88180.h create mode 100644 qemu/roms/u-boot/drivers/net/ax88796.c create mode 100644 qemu/roms/u-boot/drivers/net/ax88796.h create mode 100644 qemu/roms/u-boot/drivers/net/bfin_mac.c create mode 100644 qemu/roms/u-boot/drivers/net/bfin_mac.h create mode 100644 qemu/roms/u-boot/drivers/net/calxedaxgmac.c create mode 100644 qemu/roms/u-boot/drivers/net/cpsw.c create mode 100644 qemu/roms/u-boot/drivers/net/cs8900.c create mode 100644 qemu/roms/u-boot/drivers/net/cs8900.h create mode 100644 qemu/roms/u-boot/drivers/net/davinci_emac.c create mode 100644 qemu/roms/u-boot/drivers/net/davinci_emac.h create mode 100644 qemu/roms/u-boot/drivers/net/dc2114x.c create mode 100644 qemu/roms/u-boot/drivers/net/designware.c create mode 100644 qemu/roms/u-boot/drivers/net/designware.h create mode 100644 qemu/roms/u-boot/drivers/net/dm9000x.c create mode 100644 qemu/roms/u-boot/drivers/net/dm9000x.h create mode 100644 qemu/roms/u-boot/drivers/net/dnet.c create mode 100644 qemu/roms/u-boot/drivers/net/dnet.h create mode 100644 qemu/roms/u-boot/drivers/net/e1000.c create mode 100644 qemu/roms/u-boot/drivers/net/e1000.h create mode 100644 qemu/roms/u-boot/drivers/net/e1000_spi.c create mode 100644 qemu/roms/u-boot/drivers/net/eepro100.c create mode 100644 qemu/roms/u-boot/drivers/net/enc28j60.c create mode 100644 qemu/roms/u-boot/drivers/net/enc28j60.h create mode 100644 qemu/roms/u-boot/drivers/net/ep93xx_eth.c create mode 100644 qemu/roms/u-boot/drivers/net/ep93xx_eth.h create mode 100644 qemu/roms/u-boot/drivers/net/ethoc.c create mode 100644 qemu/roms/u-boot/drivers/net/fec_mxc.c create mode 100644 qemu/roms/u-boot/drivers/net/fec_mxc.h create mode 100644 qemu/roms/u-boot/drivers/net/fm/Makefile create mode 100644 qemu/roms/u-boot/drivers/net/fm/b4860.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/dtsec.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/eth.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/fm.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/fm.h create mode 100644 qemu/roms/u-boot/drivers/net/fm/init.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/memac.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/memac_phy.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/p1023.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/p4080.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/p5020.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/p5040.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/t1040.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/t2080.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/t4240.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/tgec.c create mode 100644 qemu/roms/u-boot/drivers/net/fm/tgec_phy.c create mode 100644 qemu/roms/u-boot/drivers/net/fsl_mcdmafec.c create mode 100644 qemu/roms/u-boot/drivers/net/fsl_mdio.c create mode 100644 qemu/roms/u-boot/drivers/net/ftgmac100.c create mode 100644 qemu/roms/u-boot/drivers/net/ftgmac100.h create mode 100644 qemu/roms/u-boot/drivers/net/ftmac100.c create mode 100644 qemu/roms/u-boot/drivers/net/ftmac100.h create mode 100644 qemu/roms/u-boot/drivers/net/ftmac110.c create mode 100644 qemu/roms/u-boot/drivers/net/ftmac110.h create mode 100644 qemu/roms/u-boot/drivers/net/greth.c create mode 100644 qemu/roms/u-boot/drivers/net/greth.h create mode 100644 qemu/roms/u-boot/drivers/net/keystone_net.c create mode 100644 qemu/roms/u-boot/drivers/net/ks8695eth.c create mode 100644 qemu/roms/u-boot/drivers/net/ks8851_mll.c create mode 100644 qemu/roms/u-boot/drivers/net/ks8851_mll.h create mode 100644 qemu/roms/u-boot/drivers/net/lan91c96.c create mode 100644 qemu/roms/u-boot/drivers/net/lan91c96.h create mode 100644 qemu/roms/u-boot/drivers/net/macb.c create mode 100644 qemu/roms/u-boot/drivers/net/macb.h create mode 100644 qemu/roms/u-boot/drivers/net/mcffec.c create mode 100644 qemu/roms/u-boot/drivers/net/mcfmii.c create mode 100644 qemu/roms/u-boot/drivers/net/mpc512x_fec.c create mode 100644 qemu/roms/u-boot/drivers/net/mpc512x_fec.h create mode 100644 qemu/roms/u-boot/drivers/net/mpc5xxx_fec.c create mode 100644 qemu/roms/u-boot/drivers/net/mpc5xxx_fec.h create mode 100644 qemu/roms/u-boot/drivers/net/mvgbe.c create mode 100644 qemu/roms/u-boot/drivers/net/mvgbe.h create mode 100644 qemu/roms/u-boot/drivers/net/natsemi.c create mode 100644 qemu/roms/u-boot/drivers/net/ne2000.c create mode 100644 qemu/roms/u-boot/drivers/net/ne2000.h create mode 100644 qemu/roms/u-boot/drivers/net/ne2000_base.c create mode 100644 qemu/roms/u-boot/drivers/net/ne2000_base.h create mode 100644 qemu/roms/u-boot/drivers/net/netconsole.c create mode 100644 qemu/roms/u-boot/drivers/net/ns8382x.c create mode 100644 qemu/roms/u-boot/drivers/net/pcnet.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/Makefile create mode 100644 qemu/roms/u-boot/drivers/net/phy/atheros.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/broadcom.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/davicom.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/et1011c.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/generic_10g.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/icplus.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/lxt.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/marvell.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/micrel.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/miiphybb.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/mv88e61xx.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/mv88e61xx.h create mode 100644 qemu/roms/u-boot/drivers/net/phy/mv88e6352.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/natsemi.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/phy.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/realtek.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/smsc.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/teranetics.c create mode 100644 qemu/roms/u-boot/drivers/net/phy/vitesse.c create mode 100644 qemu/roms/u-boot/drivers/net/plb2800_eth.c create mode 100644 qemu/roms/u-boot/drivers/net/rtl8139.c create mode 100644 qemu/roms/u-boot/drivers/net/rtl8169.c create mode 100644 qemu/roms/u-boot/drivers/net/sh_eth.c create mode 100644 qemu/roms/u-boot/drivers/net/sh_eth.h create mode 100644 qemu/roms/u-boot/drivers/net/smc91111.c create mode 100644 qemu/roms/u-boot/drivers/net/smc91111.h create mode 100644 qemu/roms/u-boot/drivers/net/smc911x.c create mode 100644 qemu/roms/u-boot/drivers/net/smc911x.h create mode 100644 qemu/roms/u-boot/drivers/net/sunxi_wemac.c create mode 100644 qemu/roms/u-boot/drivers/net/tsec.c create mode 100644 qemu/roms/u-boot/drivers/net/tsi108_eth.c create mode 100644 qemu/roms/u-boot/drivers/net/uli526x.c create mode 100644 qemu/roms/u-boot/drivers/net/vsc7385.c create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_axi_emac.c create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_emaclite.c create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_ll_temac.c create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_ll_temac.h create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_ll_temac_fifo.c create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_ll_temac_fifo.h create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_ll_temac_mdio.c create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_ll_temac_mdio.h create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_ll_temac_sdma.c create mode 100644 qemu/roms/u-boot/drivers/net/xilinx_ll_temac_sdma.h create mode 100644 qemu/roms/u-boot/drivers/net/zynq_gem.c create mode 100644 qemu/roms/u-boot/drivers/pci/Makefile create mode 100644 qemu/roms/u-boot/drivers/pci/fsl_pci_init.c create mode 100644 qemu/roms/u-boot/drivers/pci/pci.c create mode 100644 qemu/roms/u-boot/drivers/pci/pci_auto.c create mode 100644 qemu/roms/u-boot/drivers/pci/pci_ftpci100.c create mode 100644 qemu/roms/u-boot/drivers/pci/pci_gt64120.c create mode 100644 qemu/roms/u-boot/drivers/pci/pci_indirect.c create mode 100644 qemu/roms/u-boot/drivers/pci/pci_msc01.c create mode 100644 qemu/roms/u-boot/drivers/pci/pci_sh4.c create mode 100644 qemu/roms/u-boot/drivers/pci/pci_sh7751.c create mode 100644 qemu/roms/u-boot/drivers/pci/pci_sh7780.c create mode 100644 qemu/roms/u-boot/drivers/pci/pcie_imx.c create mode 100644 qemu/roms/u-boot/drivers/pci/tsi108_pci.c create mode 100644 qemu/roms/u-boot/drivers/pci/w83c553f.c create mode 100644 qemu/roms/u-boot/drivers/pcmcia/Makefile create mode 100644 qemu/roms/u-boot/drivers/pcmcia/i82365.c create mode 100644 qemu/roms/u-boot/drivers/pcmcia/marubun_pcmcia.c create mode 100644 qemu/roms/u-boot/drivers/pcmcia/mpc8xx_pcmcia.c create mode 100644 qemu/roms/u-boot/drivers/pcmcia/rpx_pcmcia.c create mode 100644 qemu/roms/u-boot/drivers/pcmcia/ti_pci1410a.c create mode 100644 qemu/roms/u-boot/drivers/pcmcia/tqm8xx_pcmcia.c create mode 100644 qemu/roms/u-boot/drivers/power/Makefile create mode 100644 qemu/roms/u-boot/drivers/power/battery/Makefile create mode 100644 qemu/roms/u-boot/drivers/power/battery/bat_trats.c create mode 100644 qemu/roms/u-boot/drivers/power/battery/bat_trats2.c create mode 100644 qemu/roms/u-boot/drivers/power/exynos-tmu.c create mode 100644 qemu/roms/u-boot/drivers/power/ftpmu010.c create mode 100644 qemu/roms/u-boot/drivers/power/fuel_gauge/Makefile create mode 100644 qemu/roms/u-boot/drivers/power/fuel_gauge/fg_max17042.c create mode 100644 qemu/roms/u-boot/drivers/power/mfd/Makefile create mode 100644 qemu/roms/u-boot/drivers/power/mfd/fg_max77693.c create mode 100644 qemu/roms/u-boot/drivers/power/mfd/muic_max77693.c create mode 100644 qemu/roms/u-boot/drivers/power/mfd/pmic_max77693.c create mode 100644 qemu/roms/u-boot/drivers/power/palmas.c create mode 100644 qemu/roms/u-boot/drivers/power/pmic/Makefile create mode 100644 qemu/roms/u-boot/drivers/power/pmic/muic_max8997.c create mode 100644 qemu/roms/u-boot/drivers/power/pmic/pmic_max77686.c create mode 100644 qemu/roms/u-boot/drivers/power/pmic/pmic_max8997.c create mode 100644 qemu/roms/u-boot/drivers/power/pmic/pmic_max8998.c create mode 100644 qemu/roms/u-boot/drivers/power/pmic/pmic_pfuze100.c create mode 100644 qemu/roms/u-boot/drivers/power/pmic/pmic_tps65217.c create mode 100644 qemu/roms/u-boot/drivers/power/pmic/pmic_tps65910.c create mode 100644 qemu/roms/u-boot/drivers/power/power_core.c create mode 100644 qemu/roms/u-boot/drivers/power/power_dialog.c create mode 100644 qemu/roms/u-boot/drivers/power/power_fsl.c create mode 100644 qemu/roms/u-boot/drivers/power/power_i2c.c create mode 100644 qemu/roms/u-boot/drivers/power/power_spi.c create mode 100644 qemu/roms/u-boot/drivers/power/tps6586x.c create mode 100644 qemu/roms/u-boot/drivers/power/twl4030.c create mode 100644 qemu/roms/u-boot/drivers/power/twl6030.c create mode 100644 qemu/roms/u-boot/drivers/qe/Makefile create mode 100644 qemu/roms/u-boot/drivers/qe/fdt.c create mode 100644 qemu/roms/u-boot/drivers/qe/qe.c create mode 100644 qemu/roms/u-boot/drivers/qe/qe.h create mode 100644 qemu/roms/u-boot/drivers/qe/uccf.c create mode 100644 qemu/roms/u-boot/drivers/qe/uccf.h create mode 100644 qemu/roms/u-boot/drivers/qe/uec.c create mode 100644 qemu/roms/u-boot/drivers/qe/uec.h create mode 100644 qemu/roms/u-boot/drivers/qe/uec_phy.c create mode 100644 qemu/roms/u-boot/drivers/qe/uec_phy.h create mode 100644 qemu/roms/u-boot/drivers/rtc/Makefile create mode 100644 qemu/roms/u-boot/drivers/rtc/at91sam9_rtt.c create mode 100644 qemu/roms/u-boot/drivers/rtc/bfin_rtc.c create mode 100644 qemu/roms/u-boot/drivers/rtc/date.c create mode 100644 qemu/roms/u-boot/drivers/rtc/davinci.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds12887.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds1302.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds1306.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds1307.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds1337.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds1374.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds1556.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds164x.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds174x.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ds3231.c create mode 100644 qemu/roms/u-boot/drivers/rtc/ftrtc010.c create mode 100644 qemu/roms/u-boot/drivers/rtc/imxdi.c create mode 100644 qemu/roms/u-boot/drivers/rtc/isl1208.c create mode 100644 qemu/roms/u-boot/drivers/rtc/m41t11.c create mode 100644 qemu/roms/u-boot/drivers/rtc/m41t60.c create mode 100644 qemu/roms/u-boot/drivers/rtc/m41t62.c create mode 100644 qemu/roms/u-boot/drivers/rtc/m41t94.c create mode 100644 qemu/roms/u-boot/drivers/rtc/m48t35ax.c create mode 100644 qemu/roms/u-boot/drivers/rtc/max6900.c create mode 100644 qemu/roms/u-boot/drivers/rtc/mc13xxx-rtc.c create mode 100644 qemu/roms/u-boot/drivers/rtc/mc146818.c create mode 100644 qemu/roms/u-boot/drivers/rtc/mcfrtc.c create mode 100644 qemu/roms/u-boot/drivers/rtc/mk48t59.c create mode 100644 qemu/roms/u-boot/drivers/rtc/mpc5xxx.c create mode 100644 qemu/roms/u-boot/drivers/rtc/mpc8xx.c create mode 100644 qemu/roms/u-boot/drivers/rtc/mvrtc.c create mode 100644 qemu/roms/u-boot/drivers/rtc/mvrtc.h create mode 100644 qemu/roms/u-boot/drivers/rtc/mx27rtc.c create mode 100644 qemu/roms/u-boot/drivers/rtc/mxsrtc.c create mode 100644 qemu/roms/u-boot/drivers/rtc/pcf8563.c create mode 100644 qemu/roms/u-boot/drivers/rtc/pl031.c create mode 100644 qemu/roms/u-boot/drivers/rtc/pt7c4338.c create mode 100644 qemu/roms/u-boot/drivers/rtc/rs5c372.c create mode 100644 qemu/roms/u-boot/drivers/rtc/rtc4543.c create mode 100644 qemu/roms/u-boot/drivers/rtc/rv3029.c create mode 100644 qemu/roms/u-boot/drivers/rtc/rx8025.c create mode 100644 qemu/roms/u-boot/drivers/rtc/s3c24x0_rtc.c create mode 100644 qemu/roms/u-boot/drivers/rtc/x1205.c create mode 100644 qemu/roms/u-boot/drivers/serial/Makefile create mode 100644 qemu/roms/u-boot/drivers/serial/altera_jtag_uart.c create mode 100644 qemu/roms/u-boot/drivers/serial/altera_uart.c create mode 100644 qemu/roms/u-boot/drivers/serial/arm_dcc.c create mode 100644 qemu/roms/u-boot/drivers/serial/atmel_usart.c create mode 100644 qemu/roms/u-boot/drivers/serial/atmel_usart.h create mode 100644 qemu/roms/u-boot/drivers/serial/lpc32xx_hsuart.c create mode 100644 qemu/roms/u-boot/drivers/serial/mcfuart.c create mode 100644 qemu/roms/u-boot/drivers/serial/mxs_auart.c create mode 100644 qemu/roms/u-boot/drivers/serial/ns16550.c create mode 100644 qemu/roms/u-boot/drivers/serial/opencores_yanu.c create mode 100644 qemu/roms/u-boot/drivers/serial/sandbox.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_arc.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_bfin.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_imx.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_ks8695.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_lpuart.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_max3100.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_mxc.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_ns16550.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_pl01x.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_pl01x.h create mode 100644 qemu/roms/u-boot/drivers/serial/serial_pxa.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_s3c24x0.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_s5p.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_sa1100.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_sh.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_sh.h create mode 100644 qemu/roms/u-boot/drivers/serial/serial_xuartlite.c create mode 100644 qemu/roms/u-boot/drivers/serial/serial_zynq.c create mode 100644 qemu/roms/u-boot/drivers/serial/usbtty.c create mode 100644 qemu/roms/u-boot/drivers/serial/usbtty.h create mode 100644 qemu/roms/u-boot/drivers/sound/Makefile create mode 100644 qemu/roms/u-boot/drivers/sound/max98095.c create mode 100644 qemu/roms/u-boot/drivers/sound/max98095.h create mode 100644 qemu/roms/u-boot/drivers/sound/samsung-i2s.c create mode 100644 qemu/roms/u-boot/drivers/sound/sandbox.c create mode 100644 qemu/roms/u-boot/drivers/sound/sound-i2s.c create mode 100644 qemu/roms/u-boot/drivers/sound/sound.c create mode 100644 qemu/roms/u-boot/drivers/sound/wm8994.c create mode 100644 qemu/roms/u-boot/drivers/sound/wm8994.h create mode 100644 qemu/roms/u-boot/drivers/sound/wm8994_registers.h create mode 100644 qemu/roms/u-boot/drivers/spi/Makefile create mode 100644 qemu/roms/u-boot/drivers/spi/altera_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/andes_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/andes_spi.h create mode 100644 qemu/roms/u-boot/drivers/spi/armada100_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/atmel_dataflash_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/atmel_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/atmel_spi.h create mode 100644 qemu/roms/u-boot/drivers/spi/bfin_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/bfin_spi6xx.c create mode 100644 qemu/roms/u-boot/drivers/spi/cf_qspi.c create mode 100644 qemu/roms/u-boot/drivers/spi/cf_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/davinci_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/davinci_spi.h create mode 100644 qemu/roms/u-boot/drivers/spi/exynos_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/fdt_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/fsl_espi.c create mode 100644 qemu/roms/u-boot/drivers/spi/ftssp010_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/ich.c create mode 100644 qemu/roms/u-boot/drivers/spi/ich.h create mode 100644 qemu/roms/u-boot/drivers/spi/kirkwood_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/mpc52xx_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/mxc_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/mxs_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/oc_tiny_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/omap3_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/omap3_spi.h create mode 100644 qemu/roms/u-boot/drivers/spi/sandbox_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/sh_qspi.c create mode 100644 qemu/roms/u-boot/drivers/spi/sh_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/sh_spi.h create mode 100644 qemu/roms/u-boot/drivers/spi/soft_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/tegra114_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/tegra20_sflash.c create mode 100644 qemu/roms/u-boot/drivers/spi/tegra20_slink.c create mode 100644 qemu/roms/u-boot/drivers/spi/ti_qspi.c create mode 100644 qemu/roms/u-boot/drivers/spi/xilinx_spi.c create mode 100644 qemu/roms/u-boot/drivers/spi/xilinx_spi.h create mode 100644 qemu/roms/u-boot/drivers/spi/zynq_spi.c create mode 100644 qemu/roms/u-boot/drivers/tpm/Makefile create mode 100644 qemu/roms/u-boot/drivers/tpm/tpm.c create mode 100644 qemu/roms/u-boot/drivers/tpm/tpm_atmel_twi.c create mode 100644 qemu/roms/u-boot/drivers/tpm/tpm_private.h create mode 100644 qemu/roms/u-boot/drivers/tpm/tpm_tis_i2c.c create mode 100644 qemu/roms/u-boot/drivers/tpm/tpm_tis_lpc.c create mode 100644 qemu/roms/u-boot/drivers/tpm/tpm_tis_sandbox.c create mode 100644 qemu/roms/u-boot/drivers/twserial/Makefile create mode 100644 qemu/roms/u-boot/drivers/twserial/soft_tws.c create mode 100644 qemu/roms/u-boot/drivers/usb/eth/Makefile create mode 100644 qemu/roms/u-boot/drivers/usb/eth/asix.c create mode 100644 qemu/roms/u-boot/drivers/usb/eth/mcs7830.c create mode 100644 qemu/roms/u-boot/drivers/usb/eth/smsc95xx.c create mode 100644 qemu/roms/u-boot/drivers/usb/eth/usb_ether.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/Makefile create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/ci_udc.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/ci_udc.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/composite.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/config.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/core.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/designware_udc.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/ep0.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/ep0.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/epautoconf.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/ether.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/f_dfu.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/f_dfu.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/f_mass_storage.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/f_thor.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/f_thor.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/fotg210.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/g_dnl.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/gadget_chips.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/mpc8xx_udc.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/ndis.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/omap1510_udc.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/regs-otg.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/rndis.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/rndis.h create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/s3c_udc_otg.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/storage_common.c create mode 100644 qemu/roms/u-boot/drivers/usb/gadget/usbstring.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/Makefile create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-armada100.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-atmel.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-exynos.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-faraday.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-fsl.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-hcd.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-marvell.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-mpc512x.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-mx5.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-mx6.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-mxc.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-mxs.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-omap.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-pci.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-ppc4xx.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-rmobile.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-spear.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-tegra.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci-vct.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ehci.h create mode 100644 qemu/roms/u-boot/drivers/usb/host/isp116x-hcd.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/isp116x.h create mode 100644 qemu/roms/u-boot/drivers/usb/host/ohci-at91.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ohci-da8xx.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ohci-hcd.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.h create mode 100644 qemu/roms/u-boot/drivers/usb/host/ohci.h create mode 100644 qemu/roms/u-boot/drivers/usb/host/r8a66597-hcd.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/r8a66597.h create mode 100644 qemu/roms/u-boot/drivers/usb/host/sl811-hcd.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/sl811.h create mode 100644 qemu/roms/u-boot/drivers/usb/host/utmi-armada100.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/xhci-exynos5.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/xhci-mem.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/xhci-omap.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/xhci-ring.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/xhci.c create mode 100644 qemu/roms/u-boot/drivers/usb/host/xhci.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/Makefile create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/am35x.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/linux-compat.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_core.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_core.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_debug.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_dma.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_dsps.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget_ep0.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_host.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_host.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_io.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_regs.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/musb_uboot.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/omap2430.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/omap2430.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb-new/usb-compat.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb/Makefile create mode 100644 qemu/roms/u-boot/drivers/usb/musb/am35x.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb/am35x.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb/da8xx.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb/davinci.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb/davinci.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb/musb_core.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb/musb_core.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb/musb_debug.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb/musb_hcd.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb/musb_hcd.h create mode 100644 qemu/roms/u-boot/drivers/usb/musb/musb_udc.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb/omap3.c create mode 100644 qemu/roms/u-boot/drivers/usb/musb/omap3.h create mode 100644 qemu/roms/u-boot/drivers/usb/phy/Makefile create mode 100644 qemu/roms/u-boot/drivers/usb/phy/omap_usb_phy.c create mode 100644 qemu/roms/u-boot/drivers/usb/phy/twl4030.c create mode 100644 qemu/roms/u-boot/drivers/usb/ulpi/Makefile create mode 100644 qemu/roms/u-boot/drivers/usb/ulpi/omap-ulpi-viewport.c create mode 100644 qemu/roms/u-boot/drivers/usb/ulpi/ulpi-viewport.c create mode 100644 qemu/roms/u-boot/drivers/usb/ulpi/ulpi.c create mode 100644 qemu/roms/u-boot/drivers/video/Makefile create mode 100644 qemu/roms/u-boot/drivers/video/ati_ids.h create mode 100644 qemu/roms/u-boot/drivers/video/ati_radeon_fb.c create mode 100644 qemu/roms/u-boot/drivers/video/ati_radeon_fb.h create mode 100644 qemu/roms/u-boot/drivers/video/atmel_hlcdfb.c create mode 100644 qemu/roms/u-boot/drivers/video/atmel_lcdfb.c create mode 100644 qemu/roms/u-boot/drivers/video/bcm2835.c create mode 100644 qemu/roms/u-boot/drivers/video/bus_vcxk.c create mode 100644 qemu/roms/u-boot/drivers/video/cfb_console.c create mode 100644 qemu/roms/u-boot/drivers/video/coreboot_fb.c create mode 100644 qemu/roms/u-boot/drivers/video/ct69000.c create mode 100644 qemu/roms/u-boot/drivers/video/da8xx-fb.c create mode 100644 qemu/roms/u-boot/drivers/video/da8xx-fb.h create mode 100644 qemu/roms/u-boot/drivers/video/exynos_dp.c create mode 100644 qemu/roms/u-boot/drivers/video/exynos_dp_lowlevel.c create mode 100644 qemu/roms/u-boot/drivers/video/exynos_dp_lowlevel.h create mode 100644 qemu/roms/u-boot/drivers/video/exynos_fb.c create mode 100644 qemu/roms/u-boot/drivers/video/exynos_fb.h create mode 100644 qemu/roms/u-boot/drivers/video/exynos_fimd.c create mode 100644 qemu/roms/u-boot/drivers/video/exynos_mipi_dsi.c create mode 100644 qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_common.c create mode 100644 qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_common.h create mode 100644 qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_lowlevel.c create mode 100644 qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_lowlevel.h create mode 100644 qemu/roms/u-boot/drivers/video/exynos_pwm_bl.c create mode 100644 qemu/roms/u-boot/drivers/video/formike.c create mode 100644 qemu/roms/u-boot/drivers/video/fsl_diu_fb.c create mode 100644 qemu/roms/u-boot/drivers/video/ipu.h create mode 100644 qemu/roms/u-boot/drivers/video/ipu_common.c create mode 100644 qemu/roms/u-boot/drivers/video/ipu_disp.c create mode 100644 qemu/roms/u-boot/drivers/video/ipu_regs.h create mode 100644 qemu/roms/u-boot/drivers/video/l5f31188.c create mode 100644 qemu/roms/u-boot/drivers/video/ld9040.c create mode 100644 qemu/roms/u-boot/drivers/video/mb862xx.c create mode 100644 qemu/roms/u-boot/drivers/video/mb86r0xgdc.c create mode 100644 qemu/roms/u-boot/drivers/video/mpc8xx_lcd.c create mode 100644 qemu/roms/u-boot/drivers/video/mx3fb.c create mode 100644 qemu/roms/u-boot/drivers/video/mxc_ipuv3_fb.c create mode 100644 qemu/roms/u-boot/drivers/video/mxcfb.h create mode 100644 qemu/roms/u-boot/drivers/video/mxsfb.c create mode 100644 qemu/roms/u-boot/drivers/video/omap3_dss.c create mode 100644 qemu/roms/u-boot/drivers/video/pxa_lcd.c create mode 100644 qemu/roms/u-boot/drivers/video/s6e63d6.c create mode 100644 qemu/roms/u-boot/drivers/video/s6e8ax0.c create mode 100644 qemu/roms/u-boot/drivers/video/sandbox_sdl.c create mode 100644 qemu/roms/u-boot/drivers/video/scf0403_lcd.c create mode 100644 qemu/roms/u-boot/drivers/video/sed13806.c create mode 100644 qemu/roms/u-boot/drivers/video/sed156x.c create mode 100644 qemu/roms/u-boot/drivers/video/sm501.c create mode 100644 qemu/roms/u-boot/drivers/video/smiLynxEM.c create mode 100644 qemu/roms/u-boot/drivers/video/tegra.c create mode 100644 qemu/roms/u-boot/drivers/video/videomodes.c create mode 100644 qemu/roms/u-boot/drivers/video/videomodes.h create mode 100644 qemu/roms/u-boot/drivers/watchdog/Makefile create mode 100644 qemu/roms/u-boot/drivers/watchdog/at91sam9_wdt.c create mode 100644 qemu/roms/u-boot/drivers/watchdog/bfin_wdt.c create mode 100644 qemu/roms/u-boot/drivers/watchdog/ftwdt010_wdt.c create mode 100644 qemu/roms/u-boot/drivers/watchdog/imx_watchdog.c create mode 100644 qemu/roms/u-boot/drivers/watchdog/omap_wdt.c create mode 100644 qemu/roms/u-boot/drivers/watchdog/s5p_wdt.c create mode 100644 qemu/roms/u-boot/drivers/watchdog/tnetv107x_wdt.c create mode 100644 qemu/roms/u-boot/drivers/watchdog/xilinx_tb_wdt.c (limited to 'qemu/roms/u-boot/drivers') diff --git a/qemu/roms/u-boot/drivers/Makefile b/qemu/roms/u-boot/drivers/Makefile new file mode 100644 index 000000000..5d03f37a1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/Makefile @@ -0,0 +1,16 @@ +obj-$(CONFIG_BIOSEMU) += bios_emulator/ +obj-y += block/ +obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/ +obj-y += crypto/ +obj-$(CONFIG_FPGA) += fpga/ +obj-y += hwmon/ +obj-y += misc/ +obj-y += pcmcia/ +obj-y += dfu/ +obj-y += rtc/ +obj-y += sound/ +obj-y += tpm/ +obj-y += twserial/ +obj-y += video/ +obj-y += watchdog/ +obj-$(CONFIG_QE) += qe/ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/Makefile b/qemu/roms/u-boot/drivers/bios_emulator/Makefile new file mode 100644 index 000000000..e56356ee8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/Makefile @@ -0,0 +1,12 @@ +X86DIR = x86emu + +obj-y = atibios.o biosemu.o besys.o bios.o \ + $(X86DIR)/decode.o \ + $(X86DIR)/ops2.o \ + $(X86DIR)/ops.o \ + $(X86DIR)/prim_ops.o \ + $(X86DIR)/sys.o \ + $(X86DIR)/debug.o + +ccflags-y := -I$(srctree)/$(src) -I$(srctree)/$(src)/include \ + -D__PPC__ -D__BIG_ENDIAN__ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/atibios.c b/qemu/roms/u-boot/drivers/bios_emulator/atibios.c new file mode 100644 index 000000000..3b2ed6e10 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/atibios.c @@ -0,0 +1,334 @@ +/**************************************************************************** +* +* Video BOOT Graphics Card POST Module +* +* ======================================================================== +* Copyright (C) 2007 Freescale Semiconductor, Inc. +* Jason Jin +* +* Copyright (C) 1991-2004 SciTech Software, Inc. All rights reserved. +* +* This file may be distributed and/or modified under the terms of the +* GNU General Public License version 2.0 as published by the Free +* Software Foundation and appearing in the file LICENSE.GPL included +* in the packaging of this file. +* +* Licensees holding a valid Commercial License for this product from +* SciTech Software, Inc. may use this file in accordance with the +* Commercial License Agreement provided with the Software. +* +* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE. +* +* See http://www.scitechsoft.com/license/ for information about +* the licensing options available and how to purchase a Commercial +* License Agreement. +* +* Contact license@scitechsoft.com if any conditions of this licensing +* are not clear to you, or you have questions about licensing options. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Linux Kernel +* Developer: Kendall Bennett +* +* Description: Module to implement booting PCI/AGP controllers on the +* bus. We use the x86 real mode emulator to run the BIOS on +* graphics controllers to bring the cards up. +* +* Note that at present this module does *not* support +* multiple controllers. +* +* The orignal name of this file is warmboot.c. +* Jason ported this file to u-boot to run the ATI video card +* BIOS in u-boot. +****************************************************************************/ +#include +#include "biosemui.h" +#include + +/* Length of the BIOS image */ +#define MAX_BIOSLEN (128 * 1024L) + +/* Place to save PCI BAR's that we change and later restore */ +static u32 saveROMBaseAddress; +static u32 saveBaseAddress10; +static u32 saveBaseAddress14; +static u32 saveBaseAddress18; +static u32 saveBaseAddress20; + +/**************************************************************************** +PARAMETERS: +pcidev - PCI device info for the video card on the bus to boot +VGAInfo - BIOS emulator VGA info structure + +REMARKS: +This function executes the BIOS POST code on the controller. We assume that +at this stage the controller has its I/O and memory space enabled and +that all other controllers are in a disabled state. +****************************************************************************/ +static void PCI_doBIOSPOST(pci_dev_t pcidev, BE_VGAInfo * VGAInfo) +{ + RMREGS regs; + RMSREGS sregs; + + /* Determine the value to store in AX for BIOS POST. Per the PCI specs, + AH must contain the bus and AL must contain the devfn, encoded as + (dev << 3) | fn + */ + memset(®s, 0, sizeof(regs)); + memset(&sregs, 0, sizeof(sregs)); + regs.x.ax = ((int)PCI_BUS(pcidev) << 8) | + ((int)PCI_DEV(pcidev) << 3) | (int)PCI_FUNC(pcidev); + + /*Setup the X86 emulator for the VGA BIOS*/ + BE_setVGA(VGAInfo); + + /*Execute the BIOS POST code*/ + BE_callRealMode(0xC000, 0x0003, ®s, &sregs); + + /*Cleanup and exit*/ + BE_getVGA(VGAInfo); +} + +/**************************************************************************** +PARAMETERS: +pcidev - PCI device info for the video card on the bus +bar - Place to return the base address register offset to use + +RETURNS: +The address to use to map the secondary BIOS (AGP devices) + +REMARKS: +Searches all the PCI base address registers for the device looking for a +memory mapping that is large enough to hold our ROM BIOS. We usually end up +finding the framebuffer mapping (usually BAR 0x10), and we use this mapping +to map the BIOS for the device into. We use a mapping that is already +assigned to the device to ensure the memory range will be passed through +by any PCI->PCI or AGP->PCI bridge that may be present. + +NOTE: Usually this function is only used for AGP devices, but it may be + used for PCI devices that have already been POST'ed and the BIOS + ROM base address has been zero'ed out. + +NOTE: This function leaves the original memory aperture disabled by leaving + it programmed to all 1's. It must be restored to the correct value + later. +****************************************************************************/ +static u32 PCI_findBIOSAddr(pci_dev_t pcidev, int *bar) +{ + u32 base, size; + + for (*bar = 0x10; *bar <= 0x14; (*bar) += 4) { + pci_read_config_dword(pcidev, *bar, &base); + if (!(base & 0x1)) { + pci_write_config_dword(pcidev, *bar, 0xFFFFFFFF); + pci_read_config_dword(pcidev, *bar, &size); + size = ~(size & ~0xFF) + 1; + if (size >= MAX_BIOSLEN) + return base & ~0xFF; + } + } + return 0; +} + +/**************************************************************************** +REMARKS: +Some non-x86 Linux kernels map PCI relocateable I/O to values that +are above 64K, which will not work with the BIOS image that requires +the offset for the I/O ports to be a maximum of 16-bits. Ideally +someone should fix the kernel to map the I/O ports for VGA compatible +devices to a different location (or just all I/O ports since it is +unlikely you can have enough devices in the machine to use up all +64K of the I/O space - a total of more than 256 cards would be +necessary). + +Anyway to fix this we change all I/O mapped base registers and +chop off the top bits. +****************************************************************************/ +static void PCI_fixupIObase(pci_dev_t pcidev, int reg, u32 * base) +{ + if ((*base & 0x1) && (*base > 0xFFFE)) { + *base &= 0xFFFF; + pci_write_config_dword(pcidev, reg, *base); + + } +} + +/**************************************************************************** +PARAMETERS: +pcidev - PCI device info for the video card on the bus + +RETURNS: +Pointers to the mapped BIOS image + +REMARKS: +Maps a pointer to the BIOS image on the graphics card on the PCI bus. +****************************************************************************/ +void *PCI_mapBIOSImage(pci_dev_t pcidev) +{ + u32 BIOSImageBus; + int BIOSImageBAR; + u8 *BIOSImage; + + /*Save PCI BAR registers that might get changed*/ + pci_read_config_dword(pcidev, PCI_ROM_ADDRESS, &saveROMBaseAddress); + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &saveBaseAddress10); + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_1, &saveBaseAddress14); + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_2, &saveBaseAddress18); + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_4, &saveBaseAddress20); + + /*Fix up I/O base registers to less than 64K */ + if(saveBaseAddress14 != 0) + PCI_fixupIObase(pcidev, PCI_BASE_ADDRESS_1, &saveBaseAddress14); + else + PCI_fixupIObase(pcidev, PCI_BASE_ADDRESS_4, &saveBaseAddress20); + + /* Some cards have problems that stop us from being able to read the + BIOS image from the ROM BAR. To fix this we have to do some chipset + specific programming for different cards to solve this problem. + */ + + BIOSImageBus = PCI_findBIOSAddr(pcidev, &BIOSImageBAR); + if (BIOSImageBus == 0) { + printf("Find bios addr error\n"); + return NULL; + } + + BIOSImage = pci_bus_to_virt(pcidev, BIOSImageBus, + PCI_REGION_MEM, 0, MAP_NOCACHE); + + /*Change the PCI BAR registers to map it onto the bus.*/ + pci_write_config_dword(pcidev, BIOSImageBAR, 0); + pci_write_config_dword(pcidev, PCI_ROM_ADDRESS, BIOSImageBus | 0x1); + + udelay(1); + + /*Check that the BIOS image is valid. If not fail, or return the + compiled in BIOS image if that option was enabled + */ + if (BIOSImage[0] != 0x55 || BIOSImage[1] != 0xAA || BIOSImage[2] == 0) { + return NULL; + } + + return BIOSImage; +} + +/**************************************************************************** +PARAMETERS: +pcidev - PCI device info for the video card on the bus + +REMARKS: +Unmaps the BIOS image for the device and restores framebuffer mappings +****************************************************************************/ +void PCI_unmapBIOSImage(pci_dev_t pcidev, void *BIOSImage) +{ + pci_write_config_dword(pcidev, PCI_ROM_ADDRESS, saveROMBaseAddress); + pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_0, saveBaseAddress10); + pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_1, saveBaseAddress14); + pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_2, saveBaseAddress18); + pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_4, saveBaseAddress20); +} + +/**************************************************************************** +PARAMETERS: +pcidev - PCI device info for the video card on the bus to boot +VGAInfo - BIOS emulator VGA info structure + +RETURNS: +true if successfully initialised, false if not. + +REMARKS: +Loads and POST's the display controllers BIOS, directly from the BIOS +image we can extract over the PCI bus. +****************************************************************************/ +static int PCI_postController(pci_dev_t pcidev, BE_VGAInfo * VGAInfo) +{ + u32 BIOSImageLen; + uchar *mappedBIOS; + uchar *copyOfBIOS; + + /*Allocate memory to store copy of BIOS from display controller*/ + if ((mappedBIOS = PCI_mapBIOSImage(pcidev)) == NULL) { + printf("videoboot: Video ROM failed to map!\n"); + return false; + } + + BIOSImageLen = mappedBIOS[2] * 512; + + if ((copyOfBIOS = malloc(BIOSImageLen)) == NULL) { + printf("videoboot: Out of memory!\n"); + return false; + } + memcpy(copyOfBIOS, mappedBIOS, BIOSImageLen); + + PCI_unmapBIOSImage(pcidev, mappedBIOS); + + /*Save information in VGAInfo structure*/ + VGAInfo->function = PCI_FUNC(pcidev); + VGAInfo->device = PCI_DEV(pcidev); + VGAInfo->bus = PCI_BUS(pcidev); + VGAInfo->pcidev = pcidev; + VGAInfo->BIOSImage = copyOfBIOS; + VGAInfo->BIOSImageLen = BIOSImageLen; + + /*Now execute the BIOS POST for the device*/ + if (copyOfBIOS[0] != 0x55 || copyOfBIOS[1] != 0xAA) { + printf("videoboot: Video ROM image is invalid!\n"); + return false; + } + + PCI_doBIOSPOST(pcidev, VGAInfo); + + /*Reset the size of the BIOS image to the final size*/ + VGAInfo->BIOSImageLen = copyOfBIOS[2] * 512; + return true; +} + +/**************************************************************************** +PARAMETERS: +pcidev - PCI device info for the video card on the bus to boot +pVGAInfo - Place to return VGA info structure is requested +cleanUp - true to clean up on exit, false to leave emulator active + +REMARKS: +Boots the PCI/AGP video card on the bus using the Video ROM BIOS image +and the X86 BIOS emulator module. +****************************************************************************/ +int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo ** pVGAInfo, int cleanUp) +{ + BE_VGAInfo *VGAInfo; + + printf("videoboot: Booting PCI video card bus %d, function %d, device %d\n", + PCI_BUS(pcidev), PCI_FUNC(pcidev), PCI_DEV(pcidev)); + + /*Initialise the x86 BIOS emulator*/ + if ((VGAInfo = malloc(sizeof(*VGAInfo))) == NULL) { + printf("videoboot: Out of memory!\n"); + return false; + } + memset(VGAInfo, 0, sizeof(*VGAInfo)); + BE_init(0, 65536, VGAInfo, 0); + + /*Post all the display controller BIOS'es*/ + if (!PCI_postController(pcidev, VGAInfo)) + return false; + + /*Cleanup and exit the emulator if requested. If the BIOS emulator + is needed after booting the card, we will not call BE_exit and + leave it enabled for further use (ie: VESA driver etc). + */ + if (cleanUp) { + BE_exit(); + if (VGAInfo->BIOSImage) + free(VGAInfo->BIOSImage); + free(VGAInfo); + VGAInfo = NULL; + } + /*Return VGA info pointer if the caller requested it*/ + if (pVGAInfo) + *pVGAInfo = VGAInfo; + return true; +} diff --git a/qemu/roms/u-boot/drivers/bios_emulator/besys.c b/qemu/roms/u-boot/drivers/bios_emulator/besys.c new file mode 100644 index 000000000..ad88a53f0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/besys.c @@ -0,0 +1,722 @@ +/**************************************************************************** +* +* BIOS emulator and interface +* to Realmode X86 Emulator Library +* +* ======================================================================== +* +* Copyright (C) 2007 Freescale Semiconductor, Inc. +* Jason Jin +* +* Copyright (C) 1991-2004 SciTech Software, Inc. All rights reserved. +* +* This file may be distributed and/or modified under the terms of the +* GNU General Public License version 2.0 as published by the Free +* Software Foundation and appearing in the file LICENSE.GPL included +* in the packaging of this file. +* +* Licensees holding a valid Commercial License for this product from +* SciTech Software, Inc. may use this file in accordance with the +* Commercial License Agreement provided with the Software. +* +* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING +* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE. +* +* See http://www.scitechsoft.com/license/ for information about +* the licensing options available and how to purchase a Commercial +* License Agreement. +* +* Contact license@scitechsoft.com if any conditions of this licensing +* are not clear to you, or you have questions about licensing options. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: This file includes BIOS emulator I/O and memory access +* functions. +* +* Jason ported this file to u-boot to run the ATI video card +* BIOS in u-boot. Removed some emulate functions such as the +* timer port access. Made all the VGA port except reading 0x3c3 +* be emulated. Seems like reading 0x3c3 should return the high +* 16 bit of the io port. +* +****************************************************************************/ + +#define __io +#include +#include +#include "biosemui.h" + +/*------------------------- Global Variables ------------------------------*/ + +#ifndef __i386__ +static char *BE_biosDate = "08/14/99"; +static u8 BE_model = 0xFC; +static u8 BE_submodel = 0x00; +#endif + +/*----------------------------- Implementation ----------------------------*/ + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to convert + +RETURNS: +Actual memory address to read or write the data + +REMARKS: +This function converts an emulator memory address in a 32-bit range to +a real memory address that we wish to access. It handles splitting up the +memory address space appropriately to access the emulator BIOS image, video +memory and system BIOS etc. +****************************************************************************/ +static u8 *BE_memaddr(u32 addr) +{ + if (addr >= 0xC0000 && addr <= _BE_env.biosmem_limit) { + return (u8*)(_BE_env.biosmem_base + addr - 0xC0000); + } else if (addr > _BE_env.biosmem_limit && addr < 0xD0000) { + DB(printf("BE_memaddr: address %#lx may be invalid!\n", addr);) + return M.mem_base; + } else if (addr >= 0xA0000 && addr <= 0xBFFFF) { + return (u8*)(_BE_env.busmem_base + addr - 0xA0000); + } +#ifdef __i386__ + else if (addr >= 0xD0000 && addr <= 0xFFFFF) { + /* We map the real System BIOS directly on real PC's */ + DB(printf("BE_memaddr: System BIOS address %#lx\n", addr);) + return _BE_env.busmem_base + addr - 0xA0000; + } +#else + else if (addr >= 0xFFFF5 && addr < 0xFFFFE) { + /* Return a faked BIOS date string for non-x86 machines */ + DB(printf("BE_memaddr - Returning BIOS date\n");) + return (u8 *)(BE_biosDate + addr - 0xFFFF5); + } else if (addr == 0xFFFFE) { + /* Return system model identifier for non-x86 machines */ + DB(printf("BE_memaddr - Returning model\n");) + return &BE_model; + } else if (addr == 0xFFFFF) { + /* Return system submodel identifier for non-x86 machines */ + DB(printf("BE_memaddr - Returning submodel\n");) + return &BE_submodel; + } +#endif + else if (addr > M.mem_size - 1) { + HALT_SYS(); + return M.mem_base; + } + + return M.mem_base + addr; +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read + +RETURNS: +Byte value read from emulator memory. + +REMARKS: +Reads a byte value from the emulator memory. We have three distinct memory +regions that are handled differently, which this function handles. +****************************************************************************/ +u8 X86API BE_rdb(u32 addr) +{ + if (_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF) + return 0; + else { + u8 val = readb_le(BE_memaddr(addr)); + return val; + } +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read + +RETURNS: +Word value read from emulator memory. + +REMARKS: +Reads a word value from the emulator memory. We have three distinct memory +regions that are handled differently, which this function handles. +****************************************************************************/ +u16 X86API BE_rdw(u32 addr) +{ + if (_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF) + return 0; + else { + u8 *base = BE_memaddr(addr); + u16 val = readw_le(base); + return val; + } +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read + +RETURNS: +Long value read from emulator memory. + +REMARKS: +Reads a 32-bit value from the emulator memory. We have three distinct memory +regions that are handled differently, which this function handles. +****************************************************************************/ +u32 X86API BE_rdl(u32 addr) +{ + if (_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF) + return 0; + else { + u8 *base = BE_memaddr(addr); + u32 val = readl_le(base); + return val; + } +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read +val - Value to store + +REMARKS: +Writes a byte value to emulator memory. We have three distinct memory +regions that are handled differently, which this function handles. +****************************************************************************/ +void X86API BE_wrb(u32 addr, u8 val) +{ + if (!(_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)) { + writeb_le(BE_memaddr(addr), val); + } +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read +val - Value to store + +REMARKS: +Writes a word value to emulator memory. We have three distinct memory +regions that are handled differently, which this function handles. +****************************************************************************/ +void X86API BE_wrw(u32 addr, u16 val) +{ + if (!(_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)) { + u8 *base = BE_memaddr(addr); + writew_le(base, val); + + } +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read +val - Value to store + +REMARKS: +Writes a 32-bit value to emulator memory. We have three distinct memory +regions that are handled differently, which this function handles. +****************************************************************************/ +void X86API BE_wrl(u32 addr, u32 val) +{ + if (!(_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)) { + u8 *base = BE_memaddr(addr); + writel_le(base, val); + } +} + +#if defined(DEBUG) || !defined(__i386__) + +/* For Non-Intel machines we may need to emulate some I/O port accesses that + * the BIOS may try to access, such as the PCI config registers. + */ + +#define IS_TIMER_PORT(port) (0x40 <= port && port <= 0x43) +#define IS_CMOS_PORT(port) (0x70 <= port && port <= 0x71) +/*#define IS_VGA_PORT(port) (_BE_env.emulateVGA && 0x3C0 <= port && port <= 0x3DA)*/ +#define IS_VGA_PORT(port) (0x3C0 <= port && port <= 0x3DA) +#define IS_PCI_PORT(port) (0xCF8 <= port && port <= 0xCFF) +#define IS_SPKR_PORT(port) (port == 0x61) + +/**************************************************************************** +PARAMETERS: +port - Port to read from +type - Type of access to perform + +REMARKS: +Performs an emulated read from the Standard VGA I/O ports. If the target +hardware does not support mapping the VGA I/O and memory (such as some +PowerPC systems), we emulate the VGA so that the BIOS will still be able to +set NonVGA display modes such as on ATI hardware. +****************************************************************************/ +static u8 VGA_inpb (const int port) +{ + u8 val = 0xff; + + switch (port) { + case 0x3C0: + /* 3C0 has funky characteristics because it can act as either + a data register or index register depending on the state + of an internal flip flop in the hardware. Hence we have + to emulate that functionality in here. */ + if (_BE_env.flipFlop3C0 == 0) { + /* Access 3C0 as index register */ + val = _BE_env.emu3C0; + } else { + /* Access 3C0 as data register */ + if (_BE_env.emu3C0 < ATT_C) + val = _BE_env.emu3C1[_BE_env.emu3C0]; + } + _BE_env.flipFlop3C0 ^= 1; + break; + case 0x3C1: + if (_BE_env.emu3C0 < ATT_C) + return _BE_env.emu3C1[_BE_env.emu3C0]; + break; + case 0x3CC: + return _BE_env.emu3C2; + case 0x3C4: + return _BE_env.emu3C4; + case 0x3C5: + if (_BE_env.emu3C4 < ATT_C) + return _BE_env.emu3C5[_BE_env.emu3C4]; + break; + case 0x3C6: + return _BE_env.emu3C6; + case 0x3C7: + return _BE_env.emu3C7; + case 0x3C8: + return _BE_env.emu3C8; + case 0x3C9: + if (_BE_env.emu3C7 < PAL_C) + return _BE_env.emu3C9[_BE_env.emu3C7++]; + break; + case 0x3CE: + return _BE_env.emu3CE; + case 0x3CF: + if (_BE_env.emu3CE < GRA_C) + return _BE_env.emu3CF[_BE_env.emu3CE]; + break; + case 0x3D4: + if (_BE_env.emu3C2 & 0x1) + return _BE_env.emu3D4; + break; + case 0x3D5: + if ((_BE_env.emu3C2 & 0x1) && (_BE_env.emu3D4 < CRT_C)) + return _BE_env.emu3D5[_BE_env.emu3D4]; + break; + case 0x3DA: + _BE_env.flipFlop3C0 = 0; + val = _BE_env.emu3DA; + _BE_env.emu3DA ^= 0x9; + break; + } + return val; +} + +/**************************************************************************** +PARAMETERS: +port - Port to write to +type - Type of access to perform + +REMARKS: +Performs an emulated write to one of the 8253 timer registers. For now +we only emulate timer 0 which is the only timer that the BIOS code appears +to use. +****************************************************************************/ +static void VGA_outpb (int port, u8 val) +{ + switch (port) { + case 0x3C0: + /* 3C0 has funky characteristics because it can act as either + a data register or index register depending on the state + of an internal flip flop in the hardware. Hence we have + to emulate that functionality in here. */ + if (_BE_env.flipFlop3C0 == 0) { + /* Access 3C0 as index register */ + _BE_env.emu3C0 = val; + } else { + /* Access 3C0 as data register */ + if (_BE_env.emu3C0 < ATT_C) + _BE_env.emu3C1[_BE_env.emu3C0] = val; + } + _BE_env.flipFlop3C0 ^= 1; + break; + case 0x3C2: + _BE_env.emu3C2 = val; + break; + case 0x3C4: + _BE_env.emu3C4 = val; + break; + case 0x3C5: + if (_BE_env.emu3C4 < ATT_C) + _BE_env.emu3C5[_BE_env.emu3C4] = val; + break; + case 0x3C6: + _BE_env.emu3C6 = val; + break; + case 0x3C7: + _BE_env.emu3C7 = (int) val *3; + + break; + case 0x3C8: + _BE_env.emu3C8 = (int) val *3; + + break; + case 0x3C9: + if (_BE_env.emu3C8 < PAL_C) + _BE_env.emu3C9[_BE_env.emu3C8++] = val; + break; + case 0x3CE: + _BE_env.emu3CE = val; + break; + case 0x3CF: + if (_BE_env.emu3CE < GRA_C) + _BE_env.emu3CF[_BE_env.emu3CE] = val; + break; + case 0x3D4: + if (_BE_env.emu3C2 & 0x1) + _BE_env.emu3D4 = val; + break; + case 0x3D5: + if ((_BE_env.emu3C2 & 0x1) && (_BE_env.emu3D4 < CRT_C)) + _BE_env.emu3D5[_BE_env.emu3D4] = val; + break; + } +} + +/**************************************************************************** +PARAMETERS: +regOffset - Offset into register space for non-DWORD accesses +value - Value to write to register for PCI_WRITE_* operations +func - Function to perform (PCIAccessRegFlags) + +RETURNS: +Value read from configuration register for PCI_READ_* operations + +REMARKS: +Accesses a PCI configuration space register by decoding the value currently +stored in the _BE_env.configAddress variable and passing it through to the +portable PCI_accessReg function. +****************************************************************************/ +static u32 BE_accessReg(int regOffset, u32 value, int func) +{ +#ifdef __KERNEL__ + int function, device, bus; + u8 val8; + u16 val16; + u32 val32; + + + /* Decode the configuration register values for the register we wish to + * access + */ + regOffset += (_BE_env.configAddress & 0xFF); + function = (_BE_env.configAddress >> 8) & 0x7; + device = (_BE_env.configAddress >> 11) & 0x1F; + bus = (_BE_env.configAddress >> 16) & 0xFF; + + /* Ignore accesses to all devices other than the one we're POSTing */ + if ((function == _BE_env.vgaInfo.function) && + (device == _BE_env.vgaInfo.device) && + (bus == _BE_env.vgaInfo.bus)) { + switch (func) { + case REG_READ_BYTE: + pci_read_config_byte(_BE_env.vgaInfo.pcidev, regOffset, + &val8); + return val8; + case REG_READ_WORD: + pci_read_config_word(_BE_env.vgaInfo.pcidev, regOffset, + &val16); + return val16; + case REG_READ_DWORD: + pci_read_config_dword(_BE_env.vgaInfo.pcidev, regOffset, + &val32); + return val32; + case REG_WRITE_BYTE: + pci_write_config_byte(_BE_env.vgaInfo.pcidev, regOffset, + value); + + return 0; + case REG_WRITE_WORD: + pci_write_config_word(_BE_env.vgaInfo.pcidev, regOffset, + value); + + return 0; + case REG_WRITE_DWORD: + pci_write_config_dword(_BE_env.vgaInfo.pcidev, + regOffset, value); + + return 0; + } + } + return 0; +#else + PCIDeviceInfo pciInfo; + + pciInfo.mech1 = 1; + pciInfo.slot.i = 0; + pciInfo.slot.p.Function = (_BE_env.configAddress >> 8) & 0x7; + pciInfo.slot.p.Device = (_BE_env.configAddress >> 11) & 0x1F; + pciInfo.slot.p.Bus = (_BE_env.configAddress >> 16) & 0xFF; + pciInfo.slot.p.Enable = 1; + + /* Ignore accesses to all devices other than the one we're POSTing */ + if ((pciInfo.slot.p.Function == + _BE_env.vgaInfo.pciInfo->slot.p.Function) + && (pciInfo.slot.p.Device == _BE_env.vgaInfo.pciInfo->slot.p.Device) + && (pciInfo.slot.p.Bus == _BE_env.vgaInfo.pciInfo->slot.p.Bus)) + return PCI_accessReg((_BE_env.configAddress & 0xFF) + regOffset, + value, func, &pciInfo); + return 0; +#endif +} + +/**************************************************************************** +PARAMETERS: +port - Port to read from +type - Type of access to perform + +REMARKS: +Performs an emulated read from one of the PCI configuration space registers. +We emulate this using our PCI_accessReg function which will access the PCI +configuration space registers in a portable fashion. +****************************************************************************/ +static u32 PCI_inp(int port, int type) +{ + switch (type) { + case REG_READ_BYTE: + if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port + && port <= 0xCFF) + return BE_accessReg(port - 0xCFC, 0, REG_READ_BYTE); + break; + case REG_READ_WORD: + if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port + && port <= 0xCFF) + return BE_accessReg(port - 0xCFC, 0, REG_READ_WORD); + break; + case REG_READ_DWORD: + if (port == 0xCF8) + return _BE_env.configAddress; + else if ((_BE_env.configAddress & 0x80000000) && port == 0xCFC) + return BE_accessReg(0, 0, REG_READ_DWORD); + break; + } + return 0; +} + +/**************************************************************************** +PARAMETERS: +port - Port to write to +type - Type of access to perform + +REMARKS: +Performs an emulated write to one of the PCI control registers. +****************************************************************************/ +static void PCI_outp(int port, u32 val, int type) +{ + switch (type) { + case REG_WRITE_BYTE: + if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port + && port <= 0xCFF) + BE_accessReg(port - 0xCFC, val, REG_WRITE_BYTE); + break; + case REG_WRITE_WORD: + if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port + && port <= 0xCFF) + BE_accessReg(port - 0xCFC, val, REG_WRITE_WORD); + break; + case REG_WRITE_DWORD: + if (port == 0xCF8) + { + _BE_env.configAddress = val & 0x80FFFFFC; + } + else if ((_BE_env.configAddress & 0x80000000) && port == 0xCFC) + BE_accessReg(0, val, REG_WRITE_DWORD); + break; + } +} + +#endif + +/**************************************************************************** +PARAMETERS: +port - Port to write to + +RETURNS: +Value read from the I/O port + +REMARKS: +Performs an emulated 8-bit read from an I/O port. We handle special cases +that we need to emulate in here, and fall through to reflecting the write +through to the real hardware if we don't need to special case it. +****************************************************************************/ +u8 X86API BE_inb(X86EMU_pioAddr port) +{ + u8 val = 0; + +#if defined(DEBUG) || !defined(__i386__) + if (IS_VGA_PORT(port)){ + /*seems reading port 0x3c3 return the high 16 bit of io port*/ + if(port == 0x3c3) + val = LOG_inpb(port); + else + val = VGA_inpb(port); + } + else if (IS_TIMER_PORT(port)) + DB(printf("Can not interept TIMER port now!\n");) + else if (IS_SPKR_PORT(port)) + DB(printf("Can not interept SPEAKER port now!\n");) + else if (IS_CMOS_PORT(port)) + DB(printf("Can not interept CMOS port now!\n");) + else if (IS_PCI_PORT(port)) + val = PCI_inp(port, REG_READ_BYTE); + else if (port < 0x100) { + DB(printf("WARN: INVALID inb.%04X -> %02X\n", (u16) port, val);) + val = LOG_inpb(port); + } else +#endif + val = LOG_inpb(port); + return val; +} + +/**************************************************************************** +PARAMETERS: +port - Port to write to + +RETURNS: +Value read from the I/O port + +REMARKS: +Performs an emulated 16-bit read from an I/O port. We handle special cases +that we need to emulate in here, and fall through to reflecting the write +through to the real hardware if we don't need to special case it. +****************************************************************************/ +u16 X86API BE_inw(X86EMU_pioAddr port) +{ + u16 val = 0; + +#if defined(DEBUG) || !defined(__i386__) + if (IS_PCI_PORT(port)) + val = PCI_inp(port, REG_READ_WORD); + else if (port < 0x100) { + DB(printf("WARN: Maybe INVALID inw.%04X -> %04X\n", (u16) port, val);) + val = LOG_inpw(port); + } else +#endif + val = LOG_inpw(port); + return val; +} + +/**************************************************************************** +PARAMETERS: +port - Port to write to + +RETURNS: +Value read from the I/O port + +REMARKS: +Performs an emulated 32-bit read from an I/O port. We handle special cases +that we need to emulate in here, and fall through to reflecting the write +through to the real hardware if we don't need to special case it. +****************************************************************************/ +u32 X86API BE_inl(X86EMU_pioAddr port) +{ + u32 val = 0; + +#if defined(DEBUG) || !defined(__i386__) + if (IS_PCI_PORT(port)) + val = PCI_inp(port, REG_READ_DWORD); + else if (port < 0x100) { + val = LOG_inpd(port); + } else +#endif + val = LOG_inpd(port); + return val; +} + +/**************************************************************************** +PARAMETERS: +port - Port to write to +val - Value to write to port + +REMARKS: +Performs an emulated 8-bit write to an I/O port. We handle special cases +that we need to emulate in here, and fall through to reflecting the write +through to the real hardware if we don't need to special case it. +****************************************************************************/ +void X86API BE_outb(X86EMU_pioAddr port, u8 val) +{ +#if defined(DEBUG) || !defined(__i386__) + if (IS_VGA_PORT(port)) + VGA_outpb(port, val); + else if (IS_TIMER_PORT(port)) + DB(printf("Can not interept TIMER port now!\n");) + else if (IS_SPKR_PORT(port)) + DB(printf("Can not interept SPEAKER port now!\n");) + else if (IS_CMOS_PORT(port)) + DB(printf("Can not interept CMOS port now!\n");) + else if (IS_PCI_PORT(port)) + PCI_outp(port, val, REG_WRITE_BYTE); + else if (port < 0x100) { + DB(printf("WARN:Maybe INVALID outb.%04X <- %02X\n", (u16) port, val);) + LOG_outpb(port, val); + } else +#endif + LOG_outpb(port, val); +} + +/**************************************************************************** +PARAMETERS: +port - Port to write to +val - Value to write to port + +REMARKS: +Performs an emulated 16-bit write to an I/O port. We handle special cases +that we need to emulate in here, and fall through to reflecting the write +through to the real hardware if we don't need to special case it. +****************************************************************************/ +void X86API BE_outw(X86EMU_pioAddr port, u16 val) +{ +#if defined(DEBUG) || !defined(__i386__) + if (IS_VGA_PORT(port)) { + VGA_outpb(port, val); + VGA_outpb(port + 1, val >> 8); + } else if (IS_PCI_PORT(port)) + PCI_outp(port, val, REG_WRITE_WORD); + else if (port < 0x100) { + DB(printf("WARN: MAybe INVALID outw.%04X <- %04X\n", (u16) port, + val);) + LOG_outpw(port, val); + } else +#endif + LOG_outpw(port, val); +} + +/**************************************************************************** +PARAMETERS: +port - Port to write to +val - Value to write to port + +REMARKS: +Performs an emulated 32-bit write to an I/O port. We handle special cases +that we need to emulate in here, and fall through to reflecting the write +through to the real hardware if we don't need to special case it. +****************************************************************************/ +void X86API BE_outl(X86EMU_pioAddr port, u32 val) +{ +#if defined(DEBUG) || !defined(__i386__) + if (IS_PCI_PORT(port)) + PCI_outp(port, val, REG_WRITE_DWORD); + else if (port < 0x100) { + DB(printf("WARN: INVALID outl.%04X <- %08X\n", (u16) port,val);) + LOG_outpd(port, val); + } else +#endif + LOG_outpd(port, val); +} diff --git a/qemu/roms/u-boot/drivers/bios_emulator/bios.c b/qemu/roms/u-boot/drivers/bios_emulator/bios.c new file mode 100644 index 000000000..bcc192fb2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/bios.c @@ -0,0 +1,324 @@ +/**************************************************************************** +* +* BIOS emulator and interface +* to Realmode X86 Emulator Library +* +* Copyright (C) 2007 Freescale Semiconductor, Inc. +* Jason Jin +* +* Copyright (C) 1996-1999 SciTech Software, Inc. +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Module implementing the BIOS specific functions. +* +* Jason ported this file to u-boot to run the ATI video card +* video BIOS. +* +****************************************************************************/ + +#define __io +#include +#include +#include "biosemui.h" + +/*----------------------------- Implementation ----------------------------*/ + +/**************************************************************************** +PARAMETERS: +intno - Interrupt number being serviced + +REMARKS: +Handler for undefined interrupts. +****************************************************************************/ +static void X86API undefined_intr(int intno) +{ + if (BE_rdw(intno * 4 + 2) == BIOS_SEG) { + DB(printf("biosEmu: undefined interrupt %xh called!\n", intno);) + } else + X86EMU_prepareForInt(intno); +} + +/**************************************************************************** +PARAMETERS: +intno - Interrupt number being serviced + +REMARKS: +This function handles the default system BIOS Int 10h (the default is stored +in the Int 42h vector by the system BIOS at bootup). We only need to handle +a small number of special functions used by the BIOS during POST time. +****************************************************************************/ +static void X86API int42(int intno) +{ + if (M.x86.R_AH == 0x12 && M.x86.R_BL == 0x32) { + if (M.x86.R_AL == 0) { + /* Enable CPU accesses to video memory */ + PM_outpb(0x3c2, PM_inpb(0x3cc) | (u8) 0x02); + return; + } else if (M.x86.R_AL == 1) { + /* Disable CPU accesses to video memory */ + PM_outpb(0x3c2, PM_inpb(0x3cc) & (u8) ~ 0x02); + return; + } +#ifdef DEBUG + else { + printf("int42: unknown function AH=0x12, BL=0x32, AL=%#02x\n", + M.x86.R_AL); + } +#endif + } +#ifdef DEBUG + else { + printf("int42: unknown function AH=%#02x, AL=%#02x, BL=%#02x\n", + M.x86.R_AH, M.x86.R_AL, M.x86.R_BL); + } +#endif +} + +/**************************************************************************** +PARAMETERS: +intno - Interrupt number being serviced + +REMARKS: +This function handles the default system BIOS Int 10h. If the POST code +has not yet re-vectored the Int 10h BIOS interrupt vector, we handle this +by simply calling the int42 interrupt handler above. Very early in the +BIOS POST process, the vector gets replaced and we simply let the real +mode interrupt handler process the interrupt. +****************************************************************************/ +static void X86API int10(int intno) +{ + if (BE_rdw(intno * 4 + 2) == BIOS_SEG) + int42(intno); + else + X86EMU_prepareForInt(intno); +} + +/* Result codes returned by the PCI BIOS */ + +#define SUCCESSFUL 0x00 +#define FUNC_NOT_SUPPORT 0x81 +#define BAD_VENDOR_ID 0x83 +#define DEVICE_NOT_FOUND 0x86 +#define BAD_REGISTER_NUMBER 0x87 +#define SET_FAILED 0x88 +#define BUFFER_TOO_SMALL 0x89 + +/**************************************************************************** +PARAMETERS: +intno - Interrupt number being serviced + +REMARKS: +This function handles the default Int 1Ah interrupt handler for the real +mode code, which provides support for the PCI BIOS functions. Since we only +want to allow the real mode BIOS code *only* see the PCI config space for +its own device, we only return information for the specific PCI config +space that we have passed in to the init function. This solves problems +when using the BIOS to warm boot a secondary adapter when there is an +identical adapter before it on the bus (some BIOS'es get confused in this +case). +****************************************************************************/ +static void X86API int1A(int unused) +{ + u16 pciSlot; + +#ifdef __KERNEL__ + u8 interface, subclass, baseclass; + + /* Initialise the PCI slot number */ + pciSlot = ((int)_BE_env.vgaInfo.bus << 8) | + ((int)_BE_env.vgaInfo.device << 3) | (int)_BE_env.vgaInfo.function; +#else +/* Fail if no PCI device information has been registered */ + if (!_BE_env.vgaInfo.pciInfo) + return; + + pciSlot = (u16) (_BE_env.vgaInfo.pciInfo->slot.i >> 8); +#endif + switch (M.x86.R_AX) { + case 0xB101: /* PCI bios present? */ + M.x86.R_AL = 0x00; /* no config space/special cycle generation support */ + M.x86.R_EDX = 0x20494350; /* " ICP" */ + M.x86.R_BX = 0x0210; /* Version 2.10 */ + M.x86.R_CL = 0; /* Max bus number in system */ + CLEAR_FLAG(F_CF); + break; + case 0xB102: /* Find PCI device */ + M.x86.R_AH = DEVICE_NOT_FOUND; +#ifdef __KERNEL__ + if (M.x86.R_DX == _BE_env.vgaInfo.VendorID && + M.x86.R_CX == _BE_env.vgaInfo.DeviceID && M.x86.R_SI == 0) { +#else + if (M.x86.R_DX == _BE_env.vgaInfo.pciInfo->VendorID && + M.x86.R_CX == _BE_env.vgaInfo.pciInfo->DeviceID && + M.x86.R_SI == 0) { +#endif + M.x86.R_AH = SUCCESSFUL; + M.x86.R_BX = pciSlot; + } + CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); + break; + case 0xB103: /* Find PCI class code */ + M.x86.R_AH = DEVICE_NOT_FOUND; +#ifdef __KERNEL__ + pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_PROG, + &interface); + pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_DEVICE, + &subclass); + pci_read_config_byte(_BE_env.vgaInfo.pcidev, + PCI_CLASS_DEVICE + 1, &baseclass); + if (M.x86.R_CL == interface && M.x86.R_CH == subclass + && (u8) (M.x86.R_ECX >> 16) == baseclass) { +#else + if (M.x86.R_CL == _BE_env.vgaInfo.pciInfo->Interface && + M.x86.R_CH == _BE_env.vgaInfo.pciInfo->SubClass && + (u8) (M.x86.R_ECX >> 16) == + _BE_env.vgaInfo.pciInfo->BaseClass) { +#endif + M.x86.R_AH = SUCCESSFUL; + M.x86.R_BX = pciSlot; + } + CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); + break; + case 0xB108: /* Read configuration byte */ + M.x86.R_AH = BAD_REGISTER_NUMBER; + if (M.x86.R_BX == pciSlot) { + M.x86.R_AH = SUCCESSFUL; +#ifdef __KERNEL__ + pci_read_config_byte(_BE_env.vgaInfo.pcidev, M.x86.R_DI, + &M.x86.R_CL); +#else + M.x86.R_CL = + (u8) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_BYTE, + _BE_env.vgaInfo.pciInfo); +#endif + } + CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); + break; + case 0xB109: /* Read configuration word */ + M.x86.R_AH = BAD_REGISTER_NUMBER; + if (M.x86.R_BX == pciSlot) { + M.x86.R_AH = SUCCESSFUL; +#ifdef __KERNEL__ + pci_read_config_word(_BE_env.vgaInfo.pcidev, M.x86.R_DI, + &M.x86.R_CX); +#else + M.x86.R_CX = + (u16) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_WORD, + _BE_env.vgaInfo.pciInfo); +#endif + } + CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); + break; + case 0xB10A: /* Read configuration dword */ + M.x86.R_AH = BAD_REGISTER_NUMBER; + if (M.x86.R_BX == pciSlot) { + M.x86.R_AH = SUCCESSFUL; +#ifdef __KERNEL__ + pci_read_config_dword(_BE_env.vgaInfo.pcidev, + M.x86.R_DI, &M.x86.R_ECX); +#else + M.x86.R_ECX = + (u32) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_DWORD, + _BE_env.vgaInfo.pciInfo); +#endif + } + CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); + break; + case 0xB10B: /* Write configuration byte */ + M.x86.R_AH = BAD_REGISTER_NUMBER; + if (M.x86.R_BX == pciSlot) { + M.x86.R_AH = SUCCESSFUL; +#ifdef __KERNEL__ + pci_write_config_byte(_BE_env.vgaInfo.pcidev, + M.x86.R_DI, M.x86.R_CL); +#else + PCI_accessReg(M.x86.R_DI, M.x86.R_CL, PCI_WRITE_BYTE, + _BE_env.vgaInfo.pciInfo); +#endif + } + CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); + break; + case 0xB10C: /* Write configuration word */ + M.x86.R_AH = BAD_REGISTER_NUMBER; + if (M.x86.R_BX == pciSlot) { + M.x86.R_AH = SUCCESSFUL; +#ifdef __KERNEL__ + pci_write_config_word(_BE_env.vgaInfo.pcidev, + M.x86.R_DI, M.x86.R_CX); +#else + PCI_accessReg(M.x86.R_DI, M.x86.R_CX, PCI_WRITE_WORD, + _BE_env.vgaInfo.pciInfo); +#endif + } + CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); + break; + case 0xB10D: /* Write configuration dword */ + M.x86.R_AH = BAD_REGISTER_NUMBER; + if (M.x86.R_BX == pciSlot) { + M.x86.R_AH = SUCCESSFUL; +#ifdef __KERNEL__ + pci_write_config_dword(_BE_env.vgaInfo.pcidev, + M.x86.R_DI, M.x86.R_ECX); +#else + PCI_accessReg(M.x86.R_DI, M.x86.R_ECX, PCI_WRITE_DWORD, + _BE_env.vgaInfo.pciInfo); +#endif + } + CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF); + break; + default: + printf("biosEmu/bios.int1a: unknown function AX=%#04x\n", + M.x86.R_AX); + } +} + +/**************************************************************************** +REMARKS: +This function initialises the BIOS emulation functions for the specific +PCI display device. We insulate the real mode BIOS from any other devices +on the bus, so that it will work correctly thinking that it is the only +device present on the bus (ie: avoiding any adapters present in from of +the device we are trying to control). +****************************************************************************/ +#define BE_constLE_32(v) ((((((v)&0xff00)>>8)|(((v)&0xff)<<8))<<16)|(((((v)&0xff000000)>>8)|(((v)&0x00ff0000)<<8))>>16)) + +void _BE_bios_init(u32 * intrTab) +{ + int i; + X86EMU_intrFuncs bios_intr_tab[256]; + + for (i = 0; i < 256; ++i) { + intrTab[i] = BE_constLE_32(BIOS_SEG << 16); + bios_intr_tab[i] = undefined_intr; + } + bios_intr_tab[0x10] = int10; + bios_intr_tab[0x1A] = int1A; + bios_intr_tab[0x42] = int42; + bios_intr_tab[0x6D] = int10; + X86EMU_setupIntrFuncs(bios_intr_tab); +} diff --git a/qemu/roms/u-boot/drivers/bios_emulator/biosemu.c b/qemu/roms/u-boot/drivers/bios_emulator/biosemu.c new file mode 100644 index 000000000..9d4f07c07 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/biosemu.c @@ -0,0 +1,372 @@ +/**************************************************************************** +* +* BIOS emulator and interface +* to Realmode X86 Emulator Library +* +* Copyright (C) 2007 Freescale Semiconductor, Inc. +* Jason Jin +* +* Copyright (C) 1996-1999 SciTech Software, Inc. +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Module implementing the system specific functions. This +* module is always compiled and linked in the OS depedent +* libraries, and never in a binary portable driver. +* +* Jason ported this file to u-boot to run the ATI video card BIOS +* in u-boot. Made all the video memory be emulated during the +* BIOS runing process which may affect the VGA function but the +* frambuffer function can work after run the BIOS. +* +****************************************************************************/ + +#include +#include +#include "biosemui.h" + +BE_sysEnv _BE_env = {{0}}; +static X86EMU_memFuncs _BE_mem __attribute__((section(GOT2_TYPE))) = { + BE_rdb, + BE_rdw, + BE_rdl, + BE_wrb, + BE_wrw, + BE_wrl, + }; + +static X86EMU_pioFuncs _BE_pio __attribute__((section(GOT2_TYPE))) = { + BE_inb, + BE_inw, + BE_inl, + BE_outb, + BE_outw, + BE_outl, + }; + +#define OFF(addr) (u16)(((addr) >> 0) & 0xffff) +#define SEG(addr) (u16)(((addr) >> 4) & 0xf000) + +/**************************************************************************** +PARAMETERS: +debugFlags - Flags to enable debugging options (debug builds only) +memSize - Amount of memory to allocate for real mode machine +info - Pointer to default VGA device information + +REMARKS: +This functions initialises the BElib, and uses the passed in +BIOS image as the BIOS that is used and emulated at 0xC0000. +****************************************************************************/ +int X86API BE_init(u32 debugFlags, int memSize, BE_VGAInfo * info, int shared) +{ +#if !defined(__DRIVER__) && !defined(__KERNEL__) + + PM_init(); +#endif + memset(&M, 0, sizeof(M)); + if (memSize < 20480){ + printf("Emulator requires at least 20Kb of memory!\n"); + return 0; + } + + M.mem_base = malloc(memSize); + + if (M.mem_base == NULL){ + printf("Biosemu:Out of memory!"); + return 0; + } + M.mem_size = memSize; + + _BE_env.emulateVGA = 0; + _BE_env.busmem_base = (unsigned long)malloc(128 * 1024); + if ((void *)_BE_env.busmem_base == NULL){ + printf("Biosemu:Out of memory!"); + return 0; + } + M.x86.debug = debugFlags; + _BE_bios_init((u32*)info->LowMem); + X86EMU_setupMemFuncs(&_BE_mem); + X86EMU_setupPioFuncs(&_BE_pio); + BE_setVGA(info); + return 1; +} + +/**************************************************************************** +PARAMETERS: +info - Pointer to VGA device information to make current + +REMARKS: +This function sets the VGA BIOS functions in the emulator to point to the +specific VGA BIOS in use. This includes swapping the BIOS interrupt +vectors, BIOS image and BIOS data area to the new BIOS. This allows the +real mode BIOS to be swapped without resetting the entire emulator. +****************************************************************************/ +void X86API BE_setVGA(BE_VGAInfo * info) +{ + +#ifdef __KERNEL__ + _BE_env.vgaInfo.function = info->function; + _BE_env.vgaInfo.device = info->device; + _BE_env.vgaInfo.bus = info->bus; + _BE_env.vgaInfo.pcidev = info->pcidev; +#else + _BE_env.vgaInfo.pciInfo = info->pciInfo; +#endif + _BE_env.vgaInfo.BIOSImage = info->BIOSImage; + if (info->BIOSImage) { + _BE_env.biosmem_base = (ulong) info->BIOSImage; + _BE_env.biosmem_limit = 0xC0000 + info->BIOSImageLen - 1; + } else { + _BE_env.biosmem_base = _BE_env.busmem_base + 0x20000; + _BE_env.biosmem_limit = 0xC7FFF; + } + if ((info->LowMem[0] == 0) && (info->LowMem[1] == 0) && + (info->LowMem[2] == 0) && (info->LowMem[3] == 0)) + _BE_bios_init((u32 *) info->LowMem); + memcpy((u8 *) M.mem_base, info->LowMem, sizeof(info->LowMem)); +} + +/**************************************************************************** +PARAMETERS: +info - Pointer to VGA device information to retrieve current + +REMARKS: +This function returns the VGA BIOS functions currently active in the +emulator, so they can be restored at a later date. +****************************************************************************/ +void X86API BE_getVGA(BE_VGAInfo * info) +{ +#ifdef __KERNEL__ + info->function = _BE_env.vgaInfo.function; + info->device = _BE_env.vgaInfo.device; + info->bus = _BE_env.vgaInfo.bus; + info->pcidev = _BE_env.vgaInfo.pcidev; +#else + info->pciInfo = _BE_env.vgaInfo.pciInfo; +#endif + info->BIOSImage = _BE_env.vgaInfo.BIOSImage; + memcpy(info->LowMem, (u8 *) M.mem_base, sizeof(info->LowMem)); +} + +/**************************************************************************** +PARAMETERS: +r_seg - Segment for pointer to convert +r_off - Offset for pointer to convert + +REMARKS: +This function maps a real mode pointer in the emulator memory to a protected +mode pointer that can be used to directly access the memory. + +NOTE: The memory is *always* in little endian format, son on non-x86 + systems you will need to do endian translations to access this + memory. +****************************************************************************/ +void *X86API BE_mapRealPointer(uint r_seg, uint r_off) +{ + u32 addr = ((u32) r_seg << 4) + r_off; + + if (addr >= 0xC0000 && addr <= _BE_env.biosmem_limit) { + return (void *)(_BE_env.biosmem_base + addr - 0xC0000); + } else if (addr >= 0xA0000 && addr <= 0xFFFFF) { + return (void *)(_BE_env.busmem_base + addr - 0xA0000); + } + return (void *)(M.mem_base + addr); +} + +/**************************************************************************** +PARAMETERS: +len - Return the length of the VESA buffer +rseg - Place to store VESA buffer segment +roff - Place to store VESA buffer offset + +REMARKS: +This function returns the address of the VESA transfer buffer in real +_BE_piomode emulator memory. The VESA transfer buffer is always 1024 bytes long, +and located at 15Kb into the start of the real mode memory (16Kb is where +we put the real mode code we execute for issuing interrupts). + +NOTE: The memory is *always* in little endian format, son on non-x86 + systems you will need to do endian translations to access this + memory. +****************************************************************************/ +void *X86API BE_getVESABuf(uint * len, uint * rseg, uint * roff) +{ + *len = 1024; + *rseg = SEG(0x03C00); + *roff = OFF(0x03C00); + return (void *)(M.mem_base + ((u32) * rseg << 4) + *roff); +} + +/**************************************************************************** +REMARKS: +Cleans up and exits the emulator. +****************************************************************************/ +void X86API BE_exit(void) +{ + free(M.mem_base); + free((void *)_BE_env.busmem_base); +} + +/**************************************************************************** +PARAMETERS: +seg - Segment of code to call +off - Offset of code to call +regs - Real mode registers to load +sregs - Real mode segment registers to load + +REMARKS: +This functions calls a real mode far function at the specified address, +and loads all the x86 registers from the passed in registers structure. +On exit the registers returned from the call are returned in the same +structures. +****************************************************************************/ +void X86API BE_callRealMode(uint seg, uint off, RMREGS * regs, RMSREGS * sregs) +{ + M.x86.R_EAX = regs->e.eax; + M.x86.R_EBX = regs->e.ebx; + M.x86.R_ECX = regs->e.ecx; + M.x86.R_EDX = regs->e.edx; + M.x86.R_ESI = regs->e.esi; + M.x86.R_EDI = regs->e.edi; + M.x86.R_DS = sregs->ds; + M.x86.R_ES = sregs->es; + M.x86.R_FS = sregs->fs; + M.x86.R_GS = sregs->gs; + + ((u8 *) M.mem_base)[0x4000] = 0x9A; + ((u8 *) M.mem_base)[0x4001] = (u8) off; + ((u8 *) M.mem_base)[0x4002] = (u8) (off >> 8); + ((u8 *) M.mem_base)[0x4003] = (u8) seg; + ((u8 *) M.mem_base)[0x4004] = (u8) (seg >> 8); + ((u8 *) M.mem_base)[0x4005] = 0xF1; /* Illegal op-code */ + M.x86.R_CS = SEG(0x04000); + M.x86.R_IP = OFF(0x04000); + + M.x86.R_SS = SEG(M.mem_size - 2); + M.x86.R_SP = OFF(M.mem_size - 2) + 2; + + X86EMU_exec(); + + regs->e.cflag = M.x86.R_EFLG & F_CF; + regs->e.eax = M.x86.R_EAX; + regs->e.ebx = M.x86.R_EBX; + regs->e.ecx = M.x86.R_ECX; + regs->e.edx = M.x86.R_EDX; + regs->e.esi = M.x86.R_ESI; + regs->e.edi = M.x86.R_EDI; + sregs->ds = M.x86.R_DS; + sregs->es = M.x86.R_ES; + sregs->fs = M.x86.R_FS; + sregs->gs = M.x86.R_GS; +} + +/**************************************************************************** +PARAMETERS: +intno - Interrupt number to execute +in - Real mode registers to load +out - Place to store resulting real mode registers + +REMARKS: +This functions calls a real mode interrupt function at the specified address, +and loads all the x86 registers from the passed in registers structure. +On exit the registers returned from the call are returned in out stucture. +****************************************************************************/ +int X86API BE_int86(int intno, RMREGS * in, RMREGS * out) +{ + M.x86.R_EAX = in->e.eax; + M.x86.R_EBX = in->e.ebx; + M.x86.R_ECX = in->e.ecx; + M.x86.R_EDX = in->e.edx; + M.x86.R_ESI = in->e.esi; + M.x86.R_EDI = in->e.edi; + ((u8 *) M.mem_base)[0x4000] = 0xCD; + ((u8 *) M.mem_base)[0x4001] = (u8) intno; + ((u8 *) M.mem_base)[0x4002] = 0xF1; + M.x86.R_CS = SEG(0x04000); + M.x86.R_IP = OFF(0x04000); + + M.x86.R_SS = SEG(M.mem_size - 1); + M.x86.R_SP = OFF(M.mem_size - 1) - 1; + + X86EMU_exec(); + out->e.cflag = M.x86.R_EFLG & F_CF; + out->e.eax = M.x86.R_EAX; + out->e.ebx = M.x86.R_EBX; + out->e.ecx = M.x86.R_ECX; + out->e.edx = M.x86.R_EDX; + out->e.esi = M.x86.R_ESI; + out->e.edi = M.x86.R_EDI; + return out->x.ax; +} + +/**************************************************************************** +PARAMETERS: +intno - Interrupt number to execute +in - Real mode registers to load +out - Place to store resulting real mode registers +sregs - Real mode segment registers to load + +REMARKS: +This functions calls a real mode interrupt function at the specified address, +and loads all the x86 registers from the passed in registers structure. +On exit the registers returned from the call are returned in out stucture. +****************************************************************************/ +int X86API BE_int86x(int intno, RMREGS * in, RMREGS * out, RMSREGS * sregs) +{ + M.x86.R_EAX = in->e.eax; + M.x86.R_EBX = in->e.ebx; + M.x86.R_ECX = in->e.ecx; + M.x86.R_EDX = in->e.edx; + M.x86.R_ESI = in->e.esi; + M.x86.R_EDI = in->e.edi; + M.x86.R_DS = sregs->ds; + M.x86.R_ES = sregs->es; + M.x86.R_FS = sregs->fs; + M.x86.R_GS = sregs->gs; + ((u8 *) M.mem_base)[0x4000] = 0xCD; + ((u8 *) M.mem_base)[0x4001] = (u8) intno; + ((u8 *) M.mem_base)[0x4002] = 0xF1; + M.x86.R_CS = SEG(0x04000); + M.x86.R_IP = OFF(0x04000); + + M.x86.R_SS = SEG(M.mem_size - 1); + M.x86.R_SP = OFF(M.mem_size - 1) - 1; + + X86EMU_exec(); + out->e.cflag = M.x86.R_EFLG & F_CF; + out->e.eax = M.x86.R_EAX; + out->e.ebx = M.x86.R_EBX; + out->e.ecx = M.x86.R_ECX; + out->e.edx = M.x86.R_EDX; + out->e.esi = M.x86.R_ESI; + out->e.edi = M.x86.R_EDI; + sregs->ds = M.x86.R_DS; + sregs->es = M.x86.R_ES; + sregs->fs = M.x86.R_FS; + sregs->gs = M.x86.R_GS; + return out->x.ax; +} diff --git a/qemu/roms/u-boot/drivers/bios_emulator/biosemui.h b/qemu/roms/u-boot/drivers/bios_emulator/biosemui.h new file mode 100644 index 000000000..8c1f111fc --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/biosemui.h @@ -0,0 +1,169 @@ +/**************************************************************************** +* +* BIOS emulator and interface +* to Realmode X86 Emulator Library +* +* Copyright (C) 2007 Freescale Semiconductor, Inc. +* Jason Jin +* +* Copyright (C) 1996-1999 SciTech Software, Inc. +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Internal header file for the BIOS emulator library. +* +* Jason ported this file to u-boot, Added some architecture +* related Macro. +* +****************************************************************************/ + +#ifndef __BIOSEMUI_H +#define __BIOSEMUI_H + +#include "biosemu.h" +#include +/*---------------------- Macros and type definitions ----------------------*/ + +#ifdef DEBUG +#define DB(x) x +#else +#define DB(x) do{}while(0); +#endif + +#define BIOS_SEG 0xfff0 +extern X86EMU_sysEnv _X86EMU_env; +#define M _X86EMU_env + +/* Macros to read and write values to x86 emulator memory. Memory is always + * considered to be little endian, so we use macros to do endian swapping + * where necessary. + */ + +#ifdef __BIG_ENDIAN__ +#define readb_le(base) *((u8*)(base)) +#define readw_le(base) ((u16)readb_le(base) | ((u16)readb_le((base) + 1) << 8)) +#define readl_le(base) ((u32)readb_le((base) + 0) | ((u32)readb_le((base) + 1) << 8) | \ + ((u32)readb_le((base) + 2) << 16) | ((u32)readb_le((base) + 3) << 24)) +#define writeb_le(base, v) *((u8*)(base)) = (v) +#define writew_le(base, v) writeb_le(base + 0, (v >> 0) & 0xff), \ + writeb_le(base + 1, (v >> 8) & 0xff) +#define writel_le(base, v) writeb_le(base + 0, (v >> 0) & 0xff), \ + writeb_le(base + 1, (v >> 8) & 0xff), \ + writeb_le(base + 2, (v >> 16) & 0xff), \ + writeb_le(base + 3, (v >> 24) & 0xff) +#else +#define readb_le(base) *((u8*)(base)) +#define readw_le(base) *((u16*)(base)) +#define readl_le(base) *((u32*)(base)) +#define writeb_le(base, v) *((u8*)(base)) = (v) +#define writew_le(base, v) *((u16*)(base)) = (v) +#define writel_le(base, v) *((u32*)(base)) = (v) +#endif + +/**************************************************************************** +REMARKS: +Function codes passed to the emulated I/O port functions to determine the +type of operation to perform. +****************************************************************************/ +typedef enum { + REG_READ_BYTE = 0, + REG_READ_WORD = 1, + REG_READ_DWORD = 2, + REG_WRITE_BYTE = 3, + REG_WRITE_WORD = 4, + REG_WRITE_DWORD = 5 +} RegisterFlags; + +/**************************************************************************** +REMARKS: +Function codes passed to the emulated I/O port functions to determine the +type of operation to perform. +****************************************************************************/ +typedef enum { + PORT_BYTE = 1, + PORT_WORD = 2, + PORT_DWORD = 3, +} PortInfoFlags; + +/**************************************************************************** +REMARKS: +Data structure used to describe the details for the BIOS emulator system +environment as used by the X86 emulator library. + +HEADER: +biosemu.h + +MEMBERS: +type - Type of port access (1 = byte, 2 = word, 3 = dword) +defVal - Default power on value +finalVal - Final value +****************************************************************************/ +typedef struct { + u8 type; + u32 defVal; + u32 finalVal; +} BE_portInfo; + +#define PM_inpb(port) inb(port+VIDEO_IO_OFFSET) +#define PM_inpw(port) inw(port+VIDEO_IO_OFFSET) +#define PM_inpd(port) inl(port+VIDEO_IO_OFFSET) +#define PM_outpb(port,val) outb(val,port+VIDEO_IO_OFFSET) +#define PM_outpw(port,val) outw(val,port+VIDEO_IO_OFFSET) +#define PM_outpd(port,val) outl(val,port+VIDEO_IO_OFFSET) + +#define LOG_inpb(port) PM_inpb(port) +#define LOG_inpw(port) PM_inpw(port) +#define LOG_inpd(port) PM_inpd(port) +#define LOG_outpb(port,val) PM_outpb(port,val) +#define LOG_outpw(port,val) PM_outpw(port,val) +#define LOG_outpd(port,val) PM_outpd(port,val) + +/*-------------------------- Function Prototypes --------------------------*/ + +/* bios.c */ + +void _BE_bios_init(u32 * intrTab); +void _BE_setup_funcs(void); + +/* besys.c */ +#define DEBUG_IO() (M.x86.debug & DEBUG_IO_TRACE_F) + +u8 X86API BE_rdb(u32 addr); +u16 X86API BE_rdw(u32 addr); +u32 X86API BE_rdl(u32 addr); +void X86API BE_wrb(u32 addr, u8 val); +void X86API BE_wrw(u32 addr, u16 val); +void X86API BE_wrl(u32 addr, u32 val); + +u8 X86API BE_inb(X86EMU_pioAddr port); +u16 X86API BE_inw(X86EMU_pioAddr port); +u32 X86API BE_inl(X86EMU_pioAddr port); +void X86API BE_outb(X86EMU_pioAddr port, u8 val); +void X86API BE_outw(X86EMU_pioAddr port, u16 val); +void X86API BE_outl(X86EMU_pioAddr port, u32 val); +#endif +/* __BIOSEMUI_H */ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/include/biosemu.h b/qemu/roms/u-boot/drivers/bios_emulator/include/biosemu.h new file mode 100644 index 000000000..e92e96e82 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/include/biosemu.h @@ -0,0 +1,392 @@ +/**************************************************************************** +* +* BIOS emulator and interface +* to Realmode X86 Emulator Library +* +* Copyright (C) 1996-1999 SciTech Software, Inc. +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Header file for the real mode x86 BIOS emulator, which is +* used to warmboot any number of VGA compatible PCI/AGP +* controllers under any OS, on any processor family that +* supports PCI. We also allow the user application to call +* real mode BIOS functions and Int 10h functions (including +* the VESA BIOS). +* +****************************************************************************/ + +#ifndef __BIOSEMU_H +#define __BIOSEMU_H + +#ifdef __KERNEL__ +#include "x86emu.h" +#else +#include "x86emu.h" +#include "pmapi.h" +#include "pcilib.h" +#endif + +/*---------------------- Macros and type definitions ----------------------*/ + +#pragma pack(1) + +#ifndef __KERNEL__ +/**************************************************************************** +REMARKS: +Data structure used to describe the details specific to a particular VGA +controller. This information is used to allow the VGA controller to be +swapped on the fly within the BIOS emulator. + +HEADER: +biosemu.h + +MEMBERS: +pciInfo - PCI device information block for the controller +BIOSImage - Pointer to a read/write copy of the BIOS image +BIOSImageLen - Length of the BIOS image +LowMem - Copy of key low memory areas +****************************************************************************/ +typedef struct { + PCIDeviceInfo *pciInfo; + void *BIOSImage; + ulong BIOSImageLen; + uchar LowMem[1536]; +} BE_VGAInfo; +#else +/**************************************************************************** +REMARKS: +Data structure used to describe the details for the BIOS emulator system +environment as used by the X86 emulator library. + +HEADER: +biosemu.h + +MEMBERS: +vgaInfo - VGA BIOS information structure +biosmem_base - Base of the BIOS image +biosmem_limit - Limit of the BIOS image +busmem_base - Base of the VGA bus memory +****************************************************************************/ +typedef struct { + int function; + int device; + int bus; + u32 VendorID; + u32 DeviceID; + pci_dev_t pcidev; + void *BIOSImage; + u32 BIOSImageLen; + u8 LowMem[1536]; +} BE_VGAInfo; + +#endif /* __KERNEL__ */ + +#define CRT_C 24 /* 24 CRT Controller Registers */ +#define ATT_C 21 /* 21 Attribute Controller Registers */ +#define GRA_C 9 /* 9 Graphics Controller Registers */ +#define SEQ_C 5 /* 5 Sequencer Registers */ +#define PAL_C 768 /* 768 Palette Registers */ + +/**************************************************************************** +REMARKS: +Data structure used to describe the details for the BIOS emulator system +environment as used by the X86 emulator library. + +HEADER: +biosemu.h + +MEMBERS: +vgaInfo - VGA BIOS information structure +biosmem_base - Base of the BIOS image +biosmem_limit - Limit of the BIOS image +busmem_base - Base of the VGA bus memory +timer - Timer used to emulate PC timer ports +timer0 - Latched value for timer 0 +timer0Latched - true if timer 0 value was just latched +timer2 - Current value for timer 2 +emulateVGA - true to emulate VGA I/O and memory accesses +****************************************************************************/ + +typedef struct { + BE_VGAInfo vgaInfo; + ulong biosmem_base; + ulong biosmem_limit; + ulong busmem_base; + + u32 timer0; + int timer0Latched; + u32 timer1; + int timer1Latched; + u32 timer2; + int timer2Latched; + + int emulateVGA; + u8 emu61; + u8 emu70; + int flipFlop3C0; + u32 configAddress; + u8 emu3C0; + u8 emu3C1[ATT_C]; + u8 emu3C2; + u8 emu3C4; + u8 emu3C5[SEQ_C]; + u8 emu3C6; + uint emu3C7; + uint emu3C8; + u8 emu3C9[PAL_C]; + u8 emu3CE; + u8 emu3CF[GRA_C]; + u8 emu3D4; + u8 emu3D5[CRT_C]; + u8 emu3DA; + +} BE_sysEnv; + +#ifdef __KERNEL__ + +/* Define some types when compiling for the Linux kernel that normally + * come from the SciTech PM library. + */ + +/**************************************************************************** +REMARKS: +Structure describing the 32-bit extended x86 CPU registers + +HEADER: +pmapi.h + +MEMBERS: +eax - Value of the EAX register +ebx - Value of the EBX register +ecx - Value of the ECX register +edx - Value of the EDX register +esi - Value of the ESI register +edi - Value of the EDI register +cflag - Value of the carry flag +****************************************************************************/ +typedef struct { + u32 eax; + u32 ebx; + u32 ecx; + u32 edx; + u32 esi; + u32 edi; + u32 cflag; +} RMDWORDREGS; + +/**************************************************************************** +REMARKS: +Structure describing the 16-bit x86 CPU registers + +HEADER: +pmapi.h + +MEMBERS: +ax - Value of the AX register +bx - Value of the BX register +cx - Value of the CX register +dx - Value of the DX register +si - Value of the SI register +di - Value of the DI register +cflag - Value of the carry flag +****************************************************************************/ +#ifdef __BIG_ENDIAN__ +typedef struct { + u16 ax_hi, ax; + u16 bx_hi, bx; + u16 cx_hi, cx; + u16 dx_hi, dx; + u16 si_hi, si; + u16 di_hi, di; + u16 cflag_hi, cflag; +} RMWORDREGS; +#else +typedef struct { + u16 ax, ax_hi; + u16 bx, bx_hi; + u16 cx, cx_hi; + u16 dx, dx_hi; + u16 si, si_hi; + u16 di, di_hi; + u16 cflag, cflag_hi; +} RMWORDREGS; +#endif + +/**************************************************************************** +REMARKS: +Structure describing the 8-bit x86 CPU registers + +HEADER: +pmapi.h + +MEMBERS: +al - Value of the AL register +ah - Value of the AH register +bl - Value of the BL register +bh - Value of the BH register +cl - Value of the CL register +ch - Value of the CH register +dl - Value of the DL register +dh - Value of the DH register +****************************************************************************/ +#ifdef __BIG_ENDIAN__ +typedef struct { + u16 ax_hi; + u8 ah, al; + u16 bx_hi; + u8 bh, bl; + u16 cx_hi; + u8 ch, cl; + u16 dx_hi; + u8 dh, dl; +} RMBYTEREGS; +#else +typedef struct { + u8 al; + u8 ah; + u16 ax_hi; + u8 bl; + u8 bh; + u16 bx_hi; + u8 cl; + u8 ch; + u16 cx_hi; + u8 dl; + u8 dh; + u16 dx_hi; +} RMBYTEREGS; +#endif + +/**************************************************************************** +REMARKS: +Structure describing all the x86 CPU registers + +HEADER: +pmapi.h + +MEMBERS: +e - Member to access registers as 32-bit values +x - Member to access registers as 16-bit values +h - Member to access registers as 8-bit values +****************************************************************************/ +typedef union { + RMDWORDREGS e; + RMWORDREGS x; + RMBYTEREGS h; +} RMREGS; + +/**************************************************************************** +REMARKS: +Structure describing all the x86 segment registers + +HEADER: +pmapi.h + +MEMBERS: +es - ES segment register +cs - CS segment register +ss - SS segment register +ds - DS segment register +fs - FS segment register +gs - GS segment register +****************************************************************************/ +typedef struct { + u16 es; + u16 cs; + u16 ss; + u16 ds; + u16 fs; + u16 gs; +} RMSREGS; + +#endif /* __KERNEL__ */ + +#ifndef __KERNEL__ + +/**************************************************************************** +REMARKS: +Structure defining all the BIOS Emulator API functions as exported from +the Binary Portable DLL. +{secret} +****************************************************************************/ +typedef struct { + ulong dwSize; + ibool(PMAPIP BE_init) (u32 debugFlags, int memSize, BE_VGAInfo * info); + void (PMAPIP BE_setVGA) (BE_VGAInfo * info); + void (PMAPIP BE_getVGA) (BE_VGAInfo * info); + void *(PMAPIP BE_mapRealPointer) (uint r_seg, uint r_off); + void *(PMAPIP BE_getVESABuf) (uint * len, uint * rseg, uint * roff); + void (PMAPIP BE_callRealMode) (uint seg, uint off, RMREGS * regs, + RMSREGS * sregs); + int (PMAPIP BE_int86) (int intno, RMREGS * in, RMREGS * out); + int (PMAPIP BE_int86x) (int intno, RMREGS * in, RMREGS * out, + RMSREGS * sregs); + void *reserved1; + void (PMAPIP BE_exit) (void); +} BE_exports; + +/**************************************************************************** +REMARKS: +Function pointer type for the Binary Portable DLL initialisation entry point. +{secret} +****************************************************************************/ +typedef BE_exports *(PMAPIP BE_initLibrary_t) (PM_imports * PMImp); +#endif + +#pragma pack() + +/*---------------------------- Global variables ---------------------------*/ + +#ifdef __cplusplus +extern "C" { /* Use "C" linkage when in C++ mode */ +#endif + +/* {secret} Global BIOS emulator system environment */ + extern BE_sysEnv _BE_env; + +/*-------------------------- Function Prototypes --------------------------*/ + +/* BIOS emulator library entry points */ + int X86API BE_init(u32 debugFlags, int memSize, BE_VGAInfo * info, + int shared); + void X86API BE_setVGA(BE_VGAInfo * info); + void X86API BE_getVGA(BE_VGAInfo * info); + void X86API BE_setDebugFlags(u32 debugFlags); + void *X86API BE_mapRealPointer(uint r_seg, uint r_off); + void *X86API BE_getVESABuf(uint * len, uint * rseg, uint * roff); + void X86API BE_callRealMode(uint seg, uint off, RMREGS * regs, + RMSREGS * sregs); + int X86API BE_int86(int intno, RMREGS * in, RMREGS * out); + int X86API BE_int86x(int intno, RMREGS * in, RMREGS * out, + RMSREGS * sregs); + void X86API BE_exit(void); + +#ifdef __cplusplus +} /* End of "C" linkage for C++ */ +#endif +#endif /* __BIOSEMU_H */ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu.h b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu.h new file mode 100644 index 000000000..a70a76874 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu.h @@ -0,0 +1,201 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1996-1999 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Header file for public specific functions. +* Any application linking against us should only +* include this header +* +****************************************************************************/ + +#ifndef __X86EMU_X86EMU_H +#define __X86EMU_X86EMU_H + +#include +#include +#include +#include +#define X86API +#define X86APIP * +typedef u16 X86EMU_pioAddr; + +#include "x86emu/regs.h" + +/*---------------------- Macros and type definitions ----------------------*/ + +#if defined (CONFIG_ARM) +#define GAS_LINE_COMMENT "@" +#elif defined(CONFIG_MIPS) || defined(CONFIG_PPC) +#define GAS_LINE_COMMENT "#" +#elif defined (CONFIG_SH) +#define GAS_LINE_COMMENT "!" +#endif + +#define GOT2_TYPE ".got2,\"aw\"\t"GAS_LINE_COMMENT + +#pragma pack(1) + +/**************************************************************************** +REMARKS: +Data structure containing ponters to programmed I/O functions used by the +emulator. This is used so that the user program can hook all programmed +I/O for the emulator to handled as necessary by the user program. By +default the emulator contains simple functions that do not do access the +hardware in any way. To allow the emualtor access the hardware, you will +need to override the programmed I/O functions using the X86EMU_setupPioFuncs +function. + +HEADER: +x86emu.h + +MEMBERS: +inb - Function to read a byte from an I/O port +inw - Function to read a word from an I/O port +inl - Function to read a dword from an I/O port +outb - Function to write a byte to an I/O port +outw - Function to write a word to an I/O port +outl - Function to write a dword to an I/O port +****************************************************************************/ +typedef struct { + u8(X86APIP inb) (X86EMU_pioAddr addr); + u16(X86APIP inw) (X86EMU_pioAddr addr); + u32(X86APIP inl) (X86EMU_pioAddr addr); + void (X86APIP outb) (X86EMU_pioAddr addr, u8 val); + void (X86APIP outw) (X86EMU_pioAddr addr, u16 val); + void (X86APIP outl) (X86EMU_pioAddr addr, u32 val); +} X86EMU_pioFuncs; + +/**************************************************************************** +REMARKS: +Data structure containing ponters to memory access functions used by the +emulator. This is used so that the user program can hook all memory +access functions as necessary for the emulator. By default the emulator +contains simple functions that only access the internal memory of the +emulator. If you need specialised functions to handle access to different +types of memory (ie: hardware framebuffer accesses and BIOS memory access +etc), you will need to override this using the X86EMU_setupMemFuncs +function. + +HEADER: +x86emu.h + +MEMBERS: +rdb - Function to read a byte from an address +rdw - Function to read a word from an address +rdl - Function to read a dword from an address +wrb - Function to write a byte to an address +wrw - Function to write a word to an address +wrl - Function to write a dword to an address +****************************************************************************/ +typedef struct { + u8(X86APIP rdb) (u32 addr); + u16(X86APIP rdw) (u32 addr); + u32(X86APIP rdl) (u32 addr); + void (X86APIP wrb) (u32 addr, u8 val); + void (X86APIP wrw) (u32 addr, u16 val); + void (X86APIP wrl) (u32 addr, u32 val); +} X86EMU_memFuncs; + +/**************************************************************************** + Here are the default memory read and write + function in case they are needed as fallbacks. +***************************************************************************/ +extern u8 X86API rdb(u32 addr); +extern u16 X86API rdw(u32 addr); +extern u32 X86API rdl(u32 addr); +extern void X86API wrb(u32 addr, u8 val); +extern void X86API wrw(u32 addr, u16 val); +extern void X86API wrl(u32 addr, u32 val); + +#pragma pack() + +/*--------------------- type definitions -----------------------------------*/ + +typedef void (X86APIP X86EMU_intrFuncs) (int num); +extern X86EMU_intrFuncs _X86EMU_intrTab[256]; + +/*-------------------------- Function Prototypes --------------------------*/ + +#ifdef __cplusplus +extern "C" { /* Use "C" linkage when in C++ mode */ +#endif + + void X86EMU_setupMemFuncs(X86EMU_memFuncs * funcs); + void X86EMU_setupPioFuncs(X86EMU_pioFuncs * funcs); + void X86EMU_setupIntrFuncs(X86EMU_intrFuncs funcs[]); + void X86EMU_prepareForInt(int num); + +/* decode.c */ + + void X86EMU_exec(void); + void X86EMU_halt_sys(void); + +#ifdef DEBUG +#define HALT_SYS() \ + printf("halt_sys: file %s, line %d\n", __FILE__, __LINE__), \ + X86EMU_halt_sys() +#else +#define HALT_SYS() X86EMU_halt_sys() +#endif + +/* Debug options */ + +#define DEBUG_DECODE_F 0x0001 /* print decoded instruction */ +#define DEBUG_TRACE_F 0x0002 /* dump regs before/after execution */ +#define DEBUG_STEP_F 0x0004 +#define DEBUG_DISASSEMBLE_F 0x0008 +#define DEBUG_BREAK_F 0x0010 +#define DEBUG_SVC_F 0x0020 +#define DEBUG_SAVE_CS_IP 0x0040 +#define DEBUG_FS_F 0x0080 +#define DEBUG_PROC_F 0x0100 +#define DEBUG_SYSINT_F 0x0200 /* bios system interrupts. */ +#define DEBUG_TRACECALL_F 0x0400 +#define DEBUG_INSTRUMENT_F 0x0800 +#define DEBUG_MEM_TRACE_F 0x1000 +#define DEBUG_IO_TRACE_F 0x2000 +#define DEBUG_TRACECALL_REGS_F 0x4000 +#define DEBUG_DECODE_NOPRINT_F 0x8000 +#define DEBUG_EXIT 0x10000 +#define DEBUG_SYS_F (DEBUG_SVC_F|DEBUG_FS_F|DEBUG_PROC_F) + + void X86EMU_trace_regs(void); + void X86EMU_trace_xregs(void); + void X86EMU_dump_memory(u16 seg, u16 off, u32 amt); + int X86EMU_trace_on(void); + int X86EMU_trace_off(void); + +#ifdef __cplusplus +} /* End of "C" linkage for C++ */ +#endif +#endif /* __X86EMU_X86EMU_H */ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/debug.h b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/debug.h new file mode 100644 index 000000000..268c9d391 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/debug.h @@ -0,0 +1,209 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Header file for debug definitions. +* +****************************************************************************/ + +#ifndef __X86EMU_DEBUG_H +#define __X86EMU_DEBUG_H + +/*---------------------- Macros and type definitions ----------------------*/ + +/* checks to be enabled for "runtime" */ + +#define CHECK_IP_FETCH_F 0x1 +#define CHECK_SP_ACCESS_F 0x2 +#define CHECK_MEM_ACCESS_F 0x4 /*using regular linear pointer */ +#define CHECK_DATA_ACCESS_F 0x8 /*using segment:offset */ + +#ifdef DEBUG +# define CHECK_IP_FETCH() (M.x86.check & CHECK_IP_FETCH_F) +# define CHECK_SP_ACCESS() (M.x86.check & CHECK_SP_ACCESS_F) +# define CHECK_MEM_ACCESS() (M.x86.check & CHECK_MEM_ACCESS_F) +# define CHECK_DATA_ACCESS() (M.x86.check & CHECK_DATA_ACCESS_F) +#else +# define CHECK_IP_FETCH() +# define CHECK_SP_ACCESS() +# define CHECK_MEM_ACCESS() +# define CHECK_DATA_ACCESS() +#endif + +#ifdef DEBUG +# define DEBUG_INSTRUMENT() (M.x86.debug & DEBUG_INSTRUMENT_F) +# define DEBUG_DECODE() (M.x86.debug & DEBUG_DECODE_F) +# define DEBUG_TRACE() (M.x86.debug & DEBUG_TRACE_F) +# define DEBUG_STEP() (M.x86.debug & DEBUG_STEP_F) +# define DEBUG_DISASSEMBLE() (M.x86.debug & DEBUG_DISASSEMBLE_F) +# define DEBUG_BREAK() (M.x86.debug & DEBUG_BREAK_F) +# define DEBUG_SVC() (M.x86.debug & DEBUG_SVC_F) +# define DEBUG_SAVE_IP_CS() (M.x86.debug & DEBUG_SAVE_CS_IP) + +# define DEBUG_FS() (M.x86.debug & DEBUG_FS_F) +# define DEBUG_PROC() (M.x86.debug & DEBUG_PROC_F) +# define DEBUG_SYSINT() (M.x86.debug & DEBUG_SYSINT_F) +# define DEBUG_TRACECALL() (M.x86.debug & DEBUG_TRACECALL_F) +# define DEBUG_TRACECALLREGS() (M.x86.debug & DEBUG_TRACECALL_REGS_F) +# define DEBUG_SYS() (M.x86.debug & DEBUG_SYS_F) +# define DEBUG_MEM_TRACE() (M.x86.debug & DEBUG_MEM_TRACE_F) +# define DEBUG_IO_TRACE() (M.x86.debug & DEBUG_IO_TRACE_F) +# define DEBUG_DECODE_NOPRINT() (M.x86.debug & DEBUG_DECODE_NOPRINT_F) +#else +# define DEBUG_INSTRUMENT() 0 +# define DEBUG_DECODE() 0 +# define DEBUG_TRACE() 0 +# define DEBUG_STEP() 0 +# define DEBUG_DISASSEMBLE() 0 +# define DEBUG_BREAK() 0 +# define DEBUG_SVC() 0 +# define DEBUG_SAVE_IP_CS() 0 +# define DEBUG_FS() 0 +# define DEBUG_PROC() 0 +# define DEBUG_SYSINT() 0 +# define DEBUG_TRACECALL() 0 +# define DEBUG_TRACECALLREGS() 0 +# define DEBUG_SYS() 0 +# define DEBUG_MEM_TRACE() 0 +# define DEBUG_IO_TRACE() 0 +# define DEBUG_DECODE_NOPRINT() 0 +#endif + +#ifdef DEBUG + +# define DECODE_PRINTF(x) if (DEBUG_DECODE()) \ + x86emu_decode_printf(x) +# define DECODE_PRINTF2(x,y) if (DEBUG_DECODE()) \ + x86emu_decode_printf2(x,y) + +/* + * The following allow us to look at the bytes of an instruction. The + * first INCR_INSTRN_LEN, is called everytime bytes are consumed in + * the decoding process. The SAVE_IP_CS is called initially when the + * major opcode of the instruction is accessed. + */ +#define INC_DECODED_INST_LEN(x) \ + if (DEBUG_DECODE()) \ + x86emu_inc_decoded_inst_len(x) + +#define SAVE_IP_CS(x,y) \ + if (DEBUG_DECODE() | DEBUG_TRACECALL() | DEBUG_BREAK() \ + | DEBUG_IO_TRACE() | DEBUG_SAVE_IP_CS()) { \ + M.x86.saved_cs = x; \ + M.x86.saved_ip = y; \ + } +#else +# define INC_DECODED_INST_LEN(x) +# define DECODE_PRINTF(x) +# define DECODE_PRINTF2(x,y) +# define SAVE_IP_CS(x,y) +#endif + +#ifdef DEBUG +#define TRACE_REGS() \ + if (DEBUG_DISASSEMBLE()) { \ + x86emu_just_disassemble(); \ + goto EndOfTheInstructionProcedure; \ + } \ + if (DEBUG_TRACE() || DEBUG_DECODE()) X86EMU_trace_regs() +#else +# define TRACE_REGS() +#endif + +#ifdef DEBUG +# define SINGLE_STEP() if (DEBUG_STEP()) x86emu_single_step() +#else +# define SINGLE_STEP() +#endif + +#define TRACE_AND_STEP() \ + TRACE_REGS(); \ + SINGLE_STEP() + +#ifdef DEBUG +# define START_OF_INSTR() +# define END_OF_INSTR() EndOfTheInstructionProcedure: x86emu_end_instr(); +# define END_OF_INSTR_NO_TRACE() x86emu_end_instr(); +#else +# define START_OF_INSTR() +# define END_OF_INSTR() +# define END_OF_INSTR_NO_TRACE() +#endif + +#ifdef DEBUG +# define CALL_TRACE(u,v,w,x,s) \ + if (DEBUG_TRACECALLREGS()) \ + x86emu_dump_regs(); \ + if (DEBUG_TRACECALL()) \ + printk("%04x:%04x: CALL %s%04x:%04x\n", u , v, s, w, x); +# define RETURN_TRACE(n,u,v) \ + if (DEBUG_TRACECALLREGS()) \ + x86emu_dump_regs(); \ + if (DEBUG_TRACECALL()) \ + printk("%04x:%04x: %s\n",u,v,n); +#else +# define CALL_TRACE(u,v,w,x,s) +# define RETURN_TRACE(n,u,v) +#endif + +#ifdef DEBUG +#define DB(x) x +#else +#define DB(x) +#endif + +/*-------------------------- Function Prototypes --------------------------*/ + +#ifdef __cplusplus +extern "C" { /* Use "C" linkage when in C++ mode */ +#endif + + extern void x86emu_inc_decoded_inst_len(int x); + extern void x86emu_decode_printf(char *x); + extern void x86emu_decode_printf2(char *x, int y); + extern void x86emu_just_disassemble(void); + extern void x86emu_single_step(void); + extern void x86emu_end_instr(void); + extern void x86emu_dump_regs(void); + extern void x86emu_dump_xregs(void); + extern void x86emu_print_int_vect(u16 iv); + extern void x86emu_instrument_instruction(void); + extern void x86emu_check_ip_access(void); + extern void x86emu_check_sp_access(void); + extern void x86emu_check_mem_access(u32 p); + extern void x86emu_check_data_access(uint s, uint o); + +#ifdef __cplusplus +} /* End of "C" linkage for C++ */ +#endif +#endif /* __X86EMU_DEBUG_H */ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/decode.h b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/decode.h new file mode 100644 index 000000000..77769f009 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/decode.h @@ -0,0 +1,88 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Header file for instruction decoding logic. +* +****************************************************************************/ + +#ifndef __X86EMU_DECODE_H +#define __X86EMU_DECODE_H + +/*---------------------- Macros and type definitions ----------------------*/ + +/* Instruction Decoding Stuff */ + +#define FETCH_DECODE_MODRM(mod,rh,rl) fetch_decode_modrm(&mod,&rh,&rl) +#define DECODE_RM_BYTE_REGISTER(r) decode_rm_byte_register(r) +#define DECODE_RM_WORD_REGISTER(r) decode_rm_word_register(r) +#define DECODE_RM_LONG_REGISTER(r) decode_rm_long_register(r) +#define DECODE_CLEAR_SEGOVR() M.x86.mode &= ~SYSMODE_CLRMASK + +/*-------------------------- Function Prototypes --------------------------*/ + +#ifdef __cplusplus +extern "C" { /* Use "C" linkage when in C++ mode */ +#endif + +void x86emu_intr_raise (u8 type); +void fetch_decode_modrm (int *mod,int *regh,int *regl); +u8 fetch_byte_imm (void); +u16 fetch_word_imm (void); +u32 fetch_long_imm (void); +u8 fetch_data_byte (uint offset); +u8 fetch_data_byte_abs (uint segment, uint offset); +u16 fetch_data_word (uint offset); +u16 fetch_data_word_abs (uint segment, uint offset); +u32 fetch_data_long (uint offset); +u32 fetch_data_long_abs (uint segment, uint offset); +void store_data_byte (uint offset, u8 val); +void store_data_byte_abs (uint segment, uint offset, u8 val); +void store_data_word (uint offset, u16 val); +void store_data_word_abs (uint segment, uint offset, u16 val); +void store_data_long (uint offset, u32 val); +void store_data_long_abs (uint segment, uint offset, u32 val); +u8* decode_rm_byte_register(int reg); +u16* decode_rm_word_register(int reg); +u32* decode_rm_long_register(int reg); +u16* decode_rm_seg_register(int reg); +unsigned decode_rm00_address(int rm); +unsigned decode_rm01_address(int rm); +unsigned decode_rm10_address(int rm); +unsigned decode_rmXX_address(int mod, int rm); + +#ifdef __cplusplus +} /* End of "C" linkage for C++ */ +#endif + +#endif /* __X86EMU_DECODE_H */ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/ops.h b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/ops.h new file mode 100644 index 000000000..a4f2316ba --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/ops.h @@ -0,0 +1,45 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Header file for operand decoding functions. +* +****************************************************************************/ + +#ifndef __X86EMU_OPS_H +#define __X86EMU_OPS_H + +extern void (*x86emu_optab[0x100])(u8 op1); +extern void (*x86emu_optab2[0x100])(u8 op2); + +#endif /* __X86EMU_OPS_H */ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/prim_ops.h b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/prim_ops.h new file mode 100644 index 000000000..2291e8488 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/prim_ops.h @@ -0,0 +1,141 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Header file for primitive operation functions. +* +****************************************************************************/ + +#ifndef __X86EMU_PRIM_OPS_H +#define __X86EMU_PRIM_OPS_H + +#ifdef __cplusplus +extern "C" { /* Use "C" linkage when in C++ mode */ +#endif + +u16 aaa_word (u16 d); +u16 aas_word (u16 d); +u16 aad_word (u16 d); +u16 aam_word (u8 d); +u8 adc_byte (u8 d, u8 s); +u16 adc_word (u16 d, u16 s); +u32 adc_long (u32 d, u32 s); +u8 add_byte (u8 d, u8 s); +u16 add_word (u16 d, u16 s); +u32 add_long (u32 d, u32 s); +u8 and_byte (u8 d, u8 s); +u16 and_word (u16 d, u16 s); +u32 and_long (u32 d, u32 s); +u8 cmp_byte (u8 d, u8 s); +u16 cmp_word (u16 d, u16 s); +u32 cmp_long (u32 d, u32 s); +u8 daa_byte (u8 d); +u8 das_byte (u8 d); +u8 dec_byte (u8 d); +u16 dec_word (u16 d); +u32 dec_long (u32 d); +u8 inc_byte (u8 d); +u16 inc_word (u16 d); +u32 inc_long (u32 d); +u8 or_byte (u8 d, u8 s); +u16 or_word (u16 d, u16 s); +u32 or_long (u32 d, u32 s); +u8 neg_byte (u8 s); +u16 neg_word (u16 s); +u32 neg_long (u32 s); +u8 not_byte (u8 s); +u16 not_word (u16 s); +u32 not_long (u32 s); +u8 rcl_byte (u8 d, u8 s); +u16 rcl_word (u16 d, u8 s); +u32 rcl_long (u32 d, u8 s); +u8 rcr_byte (u8 d, u8 s); +u16 rcr_word (u16 d, u8 s); +u32 rcr_long (u32 d, u8 s); +u8 rol_byte (u8 d, u8 s); +u16 rol_word (u16 d, u8 s); +u32 rol_long (u32 d, u8 s); +u8 ror_byte (u8 d, u8 s); +u16 ror_word (u16 d, u8 s); +u32 ror_long (u32 d, u8 s); +u8 shl_byte (u8 d, u8 s); +u16 shl_word (u16 d, u8 s); +u32 shl_long (u32 d, u8 s); +u8 shr_byte (u8 d, u8 s); +u16 shr_word (u16 d, u8 s); +u32 shr_long (u32 d, u8 s); +u8 sar_byte (u8 d, u8 s); +u16 sar_word (u16 d, u8 s); +u32 sar_long (u32 d, u8 s); +u16 shld_word (u16 d, u16 fill, u8 s); +u32 shld_long (u32 d, u32 fill, u8 s); +u16 shrd_word (u16 d, u16 fill, u8 s); +u32 shrd_long (u32 d, u32 fill, u8 s); +u8 sbb_byte (u8 d, u8 s); +u16 sbb_word (u16 d, u16 s); +u32 sbb_long (u32 d, u32 s); +u8 sub_byte (u8 d, u8 s); +u16 sub_word (u16 d, u16 s); +u32 sub_long (u32 d, u32 s); +void test_byte (u8 d, u8 s); +void test_word (u16 d, u16 s); +void test_long (u32 d, u32 s); +u8 xor_byte (u8 d, u8 s); +u16 xor_word (u16 d, u16 s); +u32 xor_long (u32 d, u32 s); +void imul_byte (u8 s); +void imul_word (u16 s); +void imul_long (u32 s); +void imul_long_direct(u32 *res_lo, u32* res_hi,u32 d, u32 s); +void mul_byte (u8 s); +void mul_word (u16 s); +void mul_long (u32 s); +void idiv_byte (u8 s); +void idiv_word (u16 s); +void idiv_long (u32 s); +void div_byte (u8 s); +void div_word (u16 s); +void div_long (u32 s); +void ins (int size); +void outs (int size); +u16 mem_access_word (int addr); +void push_word (u16 w); +void push_long (u32 w); +u16 pop_word (void); +u32 pop_long (void); + +#ifdef __cplusplus +} /* End of "C" linkage for C++ */ +#endif + +#endif /* __X86EMU_PRIM_OPS_H */ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/regs.h b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/regs.h new file mode 100644 index 000000000..a7fedd2f6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/regs.h @@ -0,0 +1,340 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Header file for x86 register definitions. +* +****************************************************************************/ + +#ifndef __X86EMU_REGS_H +#define __X86EMU_REGS_H + +/*---------------------- Macros and type definitions ----------------------*/ + +#pragma pack(1) + +/* + * General EAX, EBX, ECX, EDX type registers. Note that for + * portability, and speed, the issue of byte swapping is not addressed + * in the registers. All registers are stored in the default format + * available on the host machine. The only critical issue is that the + * registers should line up EXACTLY in the same manner as they do in + * the 386. That is: + * + * EAX & 0xff === AL + * EAX & 0xffff == AX + * + * etc. The result is that alot of the calculations can then be + * done using the native instruction set fully. + */ + +#ifdef __BIG_ENDIAN__ + +typedef struct { + u32 e_reg; +} I32_reg_t; + +typedef struct { + u16 filler0, x_reg; +} I16_reg_t; + +typedef struct { + u8 filler0, filler1, h_reg, l_reg; +} I8_reg_t; + +#else /* !__BIG_ENDIAN__ */ + +typedef struct { + u32 e_reg; +} I32_reg_t; + +typedef struct { + u16 x_reg; +} I16_reg_t; + +typedef struct { + u8 l_reg, h_reg; +} I8_reg_t; + +#endif /* BIG_ENDIAN */ + +typedef union { + I32_reg_t I32_reg; + I16_reg_t I16_reg; + I8_reg_t I8_reg; +} i386_general_register; + +struct i386_general_regs { + i386_general_register A, B, C, D; +}; + +typedef struct i386_general_regs Gen_reg_t; + +struct i386_special_regs { + i386_general_register SP, BP, SI, DI, IP; + u32 FLAGS; +}; + +/* + * Segment registers here represent the 16 bit quantities + * CS, DS, ES, SS. + */ + +#undef CS +#undef DS +#undef SS +#undef ES +#undef FS +#undef GS + +struct i386_segment_regs { + u16 CS, DS, SS, ES, FS, GS; +}; + +/* 8 bit registers */ +#define R_AH gen.A.I8_reg.h_reg +#define R_AL gen.A.I8_reg.l_reg +#define R_BH gen.B.I8_reg.h_reg +#define R_BL gen.B.I8_reg.l_reg +#define R_CH gen.C.I8_reg.h_reg +#define R_CL gen.C.I8_reg.l_reg +#define R_DH gen.D.I8_reg.h_reg +#define R_DL gen.D.I8_reg.l_reg + +/* 16 bit registers */ +#define R_AX gen.A.I16_reg.x_reg +#define R_BX gen.B.I16_reg.x_reg +#define R_CX gen.C.I16_reg.x_reg +#define R_DX gen.D.I16_reg.x_reg + +/* 32 bit extended registers */ +#define R_EAX gen.A.I32_reg.e_reg +#define R_EBX gen.B.I32_reg.e_reg +#define R_ECX gen.C.I32_reg.e_reg +#define R_EDX gen.D.I32_reg.e_reg + +/* special registers */ +#define R_SP spc.SP.I16_reg.x_reg +#define R_BP spc.BP.I16_reg.x_reg +#define R_SI spc.SI.I16_reg.x_reg +#define R_DI spc.DI.I16_reg.x_reg +#define R_IP spc.IP.I16_reg.x_reg +#define R_FLG spc.FLAGS + +/* special registers */ +#define R_SP spc.SP.I16_reg.x_reg +#define R_BP spc.BP.I16_reg.x_reg +#define R_SI spc.SI.I16_reg.x_reg +#define R_DI spc.DI.I16_reg.x_reg +#define R_IP spc.IP.I16_reg.x_reg +#define R_FLG spc.FLAGS + +/* special registers */ +#define R_ESP spc.SP.I32_reg.e_reg +#define R_EBP spc.BP.I32_reg.e_reg +#define R_ESI spc.SI.I32_reg.e_reg +#define R_EDI spc.DI.I32_reg.e_reg +#define R_EIP spc.IP.I32_reg.e_reg +#define R_EFLG spc.FLAGS + +/* segment registers */ +#define R_CS seg.CS +#define R_DS seg.DS +#define R_SS seg.SS +#define R_ES seg.ES +#define R_FS seg.FS +#define R_GS seg.GS + +/* flag conditions */ +#define FB_CF 0x0001 /* CARRY flag */ +#define FB_PF 0x0004 /* PARITY flag */ +#define FB_AF 0x0010 /* AUX flag */ +#define FB_ZF 0x0040 /* ZERO flag */ +#define FB_SF 0x0080 /* SIGN flag */ +#define FB_TF 0x0100 /* TRAP flag */ +#define FB_IF 0x0200 /* INTERRUPT ENABLE flag */ +#define FB_DF 0x0400 /* DIR flag */ +#define FB_OF 0x0800 /* OVERFLOW flag */ + +/* 80286 and above always have bit#1 set */ +#define F_ALWAYS_ON (0x0002) /* flag bits always on */ + +/* + * Define a mask for only those flag bits we will ever pass back + * (via PUSHF) + */ +#define F_MSK (FB_CF|FB_PF|FB_AF|FB_ZF|FB_SF|FB_TF|FB_IF|FB_DF|FB_OF) + +/* following bits masked in to a 16bit quantity */ + +#define F_CF 0x0001 /* CARRY flag */ +#define F_PF 0x0004 /* PARITY flag */ +#define F_AF 0x0010 /* AUX flag */ +#define F_ZF 0x0040 /* ZERO flag */ +#define F_SF 0x0080 /* SIGN flag */ +#define F_TF 0x0100 /* TRAP flag */ +#define F_IF 0x0200 /* INTERRUPT ENABLE flag */ +#define F_DF 0x0400 /* DIR flag */ +#define F_OF 0x0800 /* OVERFLOW flag */ + +#define TOGGLE_FLAG(flag) (M.x86.R_FLG ^= (flag)) +#define SET_FLAG(flag) (M.x86.R_FLG |= (flag)) +#define CLEAR_FLAG(flag) (M.x86.R_FLG &= ~(flag)) +#define ACCESS_FLAG(flag) (M.x86.R_FLG & (flag)) +#define CLEARALL_FLAG(m) (M.x86.R_FLG = 0) + +#define CONDITIONAL_SET_FLAG(COND,FLAG) \ + if (COND) SET_FLAG(FLAG); else CLEAR_FLAG(FLAG) + +#define F_PF_CALC 0x010000 /* PARITY flag has been calced */ +#define F_ZF_CALC 0x020000 /* ZERO flag has been calced */ +#define F_SF_CALC 0x040000 /* SIGN flag has been calced */ + +#define F_ALL_CALC 0xff0000 /* All have been calced */ + +/* + * Emulator machine state. + * Segment usage control. + */ +#define SYSMODE_SEG_DS_SS 0x00000001 +#define SYSMODE_SEGOVR_CS 0x00000002 +#define SYSMODE_SEGOVR_DS 0x00000004 +#define SYSMODE_SEGOVR_ES 0x00000008 +#define SYSMODE_SEGOVR_FS 0x00000010 +#define SYSMODE_SEGOVR_GS 0x00000020 +#define SYSMODE_SEGOVR_SS 0x00000040 +#define SYSMODE_PREFIX_REPE 0x00000080 +#define SYSMODE_PREFIX_REPNE 0x00000100 +#define SYSMODE_PREFIX_DATA 0x00000200 +#define SYSMODE_PREFIX_ADDR 0x00000400 +#define SYSMODE_INTR_PENDING 0x10000000 +#define SYSMODE_EXTRN_INTR 0x20000000 +#define SYSMODE_HALTED 0x40000000 + +#define SYSMODE_SEGMASK (SYSMODE_SEG_DS_SS | \ + SYSMODE_SEGOVR_CS | \ + SYSMODE_SEGOVR_DS | \ + SYSMODE_SEGOVR_ES | \ + SYSMODE_SEGOVR_FS | \ + SYSMODE_SEGOVR_GS | \ + SYSMODE_SEGOVR_SS) +#define SYSMODE_CLRMASK (SYSMODE_SEG_DS_SS | \ + SYSMODE_SEGOVR_CS | \ + SYSMODE_SEGOVR_DS | \ + SYSMODE_SEGOVR_ES | \ + SYSMODE_SEGOVR_FS | \ + SYSMODE_SEGOVR_GS | \ + SYSMODE_SEGOVR_SS | \ + SYSMODE_PREFIX_DATA | \ + SYSMODE_PREFIX_ADDR) + +#define INTR_SYNCH 0x1 +#define INTR_ASYNCH 0x2 +#define INTR_HALTED 0x4 + +typedef struct { + struct i386_general_regs gen; + struct i386_special_regs spc; + struct i386_segment_regs seg; + /* + * MODE contains information on: + * REPE prefix 2 bits repe,repne + * SEGMENT overrides 5 bits normal,DS,SS,CS,ES + * Delayed flag set 3 bits (zero, signed, parity) + * reserved 6 bits + * interrupt # 8 bits instruction raised interrupt + * BIOS video segregs 4 bits + * Interrupt Pending 1 bits + * Extern interrupt 1 bits + * Halted 1 bits + */ + long mode; + u8 intno; + volatile int intr; /* mask of pending interrupts */ + int debug; +#ifdef DEBUG + int check; + u16 saved_ip; + u16 saved_cs; + int enc_pos; + int enc_str_pos; + char decode_buf[32]; /* encoded byte stream */ + char decoded_buf[256]; /* disassembled strings */ +#endif +} X86EMU_regs; + +/**************************************************************************** +REMARKS: +Structure maintaining the emulator machine state. + +MEMBERS: +x86 - X86 registers +mem_base - Base real mode memory for the emulator +mem_size - Size of the real mode memory block for the emulator +****************************************************************************/ +#undef x86 +typedef struct { + X86EMU_regs x86; + u8 *mem_base; + u32 mem_size; + void *private; +} X86EMU_sysEnv; + +#pragma pack() + +/*----------------------------- Global Variables --------------------------*/ + +#ifdef __cplusplus +extern "C" { /* Use "C" linkage when in C++ mode */ +#endif + +/* Global emulator machine state. + * + * We keep it global to avoid pointer dereferences in the code for speed. + */ + + extern X86EMU_sysEnv _X86EMU_env; +#define M _X86EMU_env + +/*-------------------------- Function Prototypes --------------------------*/ + +/* Function to log information at runtime */ + +#ifndef __KERNEL__ + void printk(const char *fmt, ...); +#endif + +#ifdef __cplusplus +} /* End of "C" linkage for C++ */ +#endif +#endif /* __X86EMU_REGS_H */ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/x86emui.h b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/x86emui.h new file mode 100644 index 000000000..a74957d99 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/include/x86emu/x86emui.h @@ -0,0 +1,101 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: Header file for system specific functions. These functions +* are always compiled and linked in the OS depedent libraries, +* and never in a binary portable driver. +* +****************************************************************************/ + +#ifndef __X86EMU_X86EMUI_H +#define __X86EMU_X86EMUI_H + +/* If we are compiling in C++ mode, we can compile some functions as + * inline to increase performance (however the code size increases quite + * dramatically in this case). + */ + +#if defined(__cplusplus) && !defined(_NO_INLINE) +#define _INLINE inline +#else +#define _INLINE static +#endif + +/* Get rid of unused parameters in C++ compilation mode */ + +#ifdef __cplusplus +#define X86EMU_UNUSED(v) +#else +#define X86EMU_UNUSED(v) v +#endif + +#include "x86emu.h" +#include "x86emu/regs.h" +#include "x86emu/debug.h" +#include "x86emu/decode.h" +#include "x86emu/ops.h" +#include "x86emu/prim_ops.h" +#ifndef __KERNEL__ +#include +#include +#include +#endif + +#define printk printf + + +/*--------------------------- Inline Functions ----------------------------*/ + +#ifdef __cplusplus +extern "C" { /* Use "C" linkage when in C++ mode */ +#endif + + extern u8(X86APIP sys_rdb) (u32 addr); + extern u16(X86APIP sys_rdw) (u32 addr); + extern u32(X86APIP sys_rdl) (u32 addr); + extern void (X86APIP sys_wrb) (u32 addr, u8 val); + extern void (X86APIP sys_wrw) (u32 addr, u16 val); + extern void (X86APIP sys_wrl) (u32 addr, u32 val); + + extern u8(X86APIP sys_inb) (X86EMU_pioAddr addr); + extern u16(X86APIP sys_inw) (X86EMU_pioAddr addr); + extern u32(X86APIP sys_inl) (X86EMU_pioAddr addr); + extern void (X86APIP sys_outb) (X86EMU_pioAddr addr, u8 val); + extern void (X86APIP sys_outw) (X86EMU_pioAddr addr, u16 val); + extern void (X86APIP sys_outl) (X86EMU_pioAddr addr, u32 val); + +#ifdef __cplusplus +} /* End of "C" linkage for C++ */ +#endif +#endif /* __X86EMU_X86EMUI_H */ diff --git a/qemu/roms/u-boot/drivers/bios_emulator/x86emu/debug.c b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/debug.c new file mode 100644 index 000000000..2fa8050f6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/debug.c @@ -0,0 +1,463 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: This file contains the code to handle debugging of the +* emulator. +* +****************************************************************************/ + +#include +#include +#include +#include "x86emu/x86emui.h" + +/*----------------------------- Implementation ----------------------------*/ + +#ifdef DEBUG + +static void print_encoded_bytes(u16 s, u16 o); +static void print_decoded_instruction(void); +static int x86emu_parse_line(char *s, int *ps, int *n); + +/* should look something like debug's output. */ +void X86EMU_trace_regs(void) +{ + if (DEBUG_TRACE()) { + x86emu_dump_regs(); + } + if (DEBUG_DECODE() && !DEBUG_DECODE_NOPRINT()) { + printk("%04x:%04x ", M.x86.saved_cs, M.x86.saved_ip); + print_encoded_bytes(M.x86.saved_cs, M.x86.saved_ip); + print_decoded_instruction(); + } +} + +void X86EMU_trace_xregs(void) +{ + if (DEBUG_TRACE()) { + x86emu_dump_xregs(); + } +} + +void x86emu_just_disassemble(void) +{ + /* + * This routine called if the flag DEBUG_DISASSEMBLE is set kind + * of a hack! + */ + printk("%04x:%04x ", M.x86.saved_cs, M.x86.saved_ip); + print_encoded_bytes(M.x86.saved_cs, M.x86.saved_ip); + print_decoded_instruction(); +} + +static void disassemble_forward(u16 seg, u16 off, int n) +{ + X86EMU_sysEnv tregs; + int i; + u8 op1; + /* + * hack, hack, hack. What we do is use the exact machinery set up + * for execution, except that now there is an additional state + * flag associated with the "execution", and we are using a copy + * of the register struct. All the major opcodes, once fully + * decoded, have the following two steps: TRACE_REGS(r,m); + * SINGLE_STEP(r,m); which disappear if DEBUG is not defined to + * the preprocessor. The TRACE_REGS macro expands to: + * + * if (debug&DEBUG_DISASSEMBLE) + * {just_disassemble(); goto EndOfInstruction;} + * if (debug&DEBUG_TRACE) trace_regs(r,m); + * + * ...... and at the last line of the routine. + * + * EndOfInstruction: end_instr(); + * + * Up to the point where TRACE_REG is expanded, NO modifications + * are done to any register EXCEPT the IP register, for fetch and + * decoding purposes. + * + * This was done for an entirely different reason, but makes a + * nice way to get the system to help debug codes. + */ + tregs = M; + tregs.x86.R_IP = off; + tregs.x86.R_CS = seg; + + /* reset the decoding buffers */ + tregs.x86.enc_str_pos = 0; + tregs.x86.enc_pos = 0; + + /* turn on the "disassemble only, no execute" flag */ + tregs.x86.debug |= DEBUG_DISASSEMBLE_F; + + /* DUMP NEXT n instructions to screen in straight_line fashion */ + /* + * This looks like the regular instruction fetch stream, except + * that when this occurs, each fetched opcode, upon seeing the + * DEBUG_DISASSEMBLE flag set, exits immediately after decoding + * the instruction. XXX --- CHECK THAT MEM IS NOT AFFECTED!!! + * Note the use of a copy of the register structure... + */ + for (i = 0; i < n; i++) { + op1 = (*sys_rdb) (((u32) M.x86.R_CS << 4) + (M.x86.R_IP++)); + (x86emu_optab[op1]) (op1); + } + /* end major hack mode. */ +} + +void x86emu_check_ip_access(void) +{ + /* NULL as of now */ +} + +void x86emu_check_sp_access(void) +{ +} + +void x86emu_check_mem_access(u32 dummy) +{ + /* check bounds, etc */ +} + +void x86emu_check_data_access(uint dummy1, uint dummy2) +{ + /* check bounds, etc */ +} + +void x86emu_inc_decoded_inst_len(int x) +{ + M.x86.enc_pos += x; +} + +void x86emu_decode_printf(char *x) +{ + sprintf(M.x86.decoded_buf + M.x86.enc_str_pos, "%s", x); + M.x86.enc_str_pos += strlen(x); +} + +void x86emu_decode_printf2(char *x, int y) +{ + char temp[100]; + sprintf(temp, x, y); + sprintf(M.x86.decoded_buf + M.x86.enc_str_pos, "%s", temp); + M.x86.enc_str_pos += strlen(temp); +} + +void x86emu_end_instr(void) +{ + M.x86.enc_str_pos = 0; + M.x86.enc_pos = 0; +} + +static void print_encoded_bytes(u16 s, u16 o) +{ + int i; + char buf1[64]; + for (i = 0; i < M.x86.enc_pos; i++) { + sprintf(buf1 + 2 * i, "%02x", fetch_data_byte_abs(s, o + i)); + } + printk("%-20s", buf1); +} + +static void print_decoded_instruction(void) +{ + printk("%s", M.x86.decoded_buf); +} + +void x86emu_print_int_vect(u16 iv) +{ + u16 seg, off; + + if (iv > 256) + return; + seg = fetch_data_word_abs(0, iv * 4); + off = fetch_data_word_abs(0, iv * 4 + 2); + printk("%04x:%04x ", seg, off); +} + +void X86EMU_dump_memory(u16 seg, u16 off, u32 amt) +{ + u32 start = off & 0xfffffff0; + u32 end = (off + 16) & 0xfffffff0; + u32 i; + u32 current; + + current = start; + while (end <= off + amt) { + printk("%04x:%04x ", seg, start); + for (i = start; i < off; i++) + printk(" "); + for (; i < end; i++) + printk("%02x ", fetch_data_byte_abs(seg, i)); + printk("\n"); + start = end; + end = start + 16; + } +} + +void x86emu_single_step(void) +{ + char s[1024]; + int ps[10]; + int ntok; + int cmd; + int done; + int segment; + int offset; + static int breakpoint; + static int noDecode = 1; + + char *p; + + if (DEBUG_BREAK()) { + if (M.x86.saved_ip != breakpoint) { + return; + } else { + M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; + M.x86.debug |= DEBUG_TRACE_F; + M.x86.debug &= ~DEBUG_BREAK_F; + print_decoded_instruction(); + X86EMU_trace_regs(); + } + } + done = 0; + offset = M.x86.saved_ip; + while (!done) { + printk("-"); + cmd = x86emu_parse_line(s, ps, &ntok); + switch (cmd) { + case 'u': + disassemble_forward(M.x86.saved_cs, (u16) offset, 10); + break; + case 'd': + if (ntok == 2) { + segment = M.x86.saved_cs; + offset = ps[1]; + X86EMU_dump_memory(segment, (u16) offset, 16); + offset += 16; + } else if (ntok == 3) { + segment = ps[1]; + offset = ps[2]; + X86EMU_dump_memory(segment, (u16) offset, 16); + offset += 16; + } else { + segment = M.x86.saved_cs; + X86EMU_dump_memory(segment, (u16) offset, 16); + offset += 16; + } + break; + case 'c': + M.x86.debug ^= DEBUG_TRACECALL_F; + break; + case 's': + M.x86.debug ^= + DEBUG_SVC_F | DEBUG_SYS_F | DEBUG_SYSINT_F; + break; + case 'r': + X86EMU_trace_regs(); + break; + case 'x': + X86EMU_trace_xregs(); + break; + case 'g': + if (ntok == 2) { + breakpoint = ps[1]; + if (noDecode) { + M.x86.debug |= DEBUG_DECODE_NOPRINT_F; + } else { + M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; + } + M.x86.debug &= ~DEBUG_TRACE_F; + M.x86.debug |= DEBUG_BREAK_F; + done = 1; + } + break; + case 'q': + M.x86.debug |= DEBUG_EXIT; + return; + case 'P': + noDecode = (noDecode) ? 0 : 1; + printk("Toggled decoding to %s\n", + (noDecode) ? "false" : "true"); + break; + case 't': + case 0: + done = 1; + break; + } + } +} + +int X86EMU_trace_on(void) +{ + return M.x86.debug |= DEBUG_STEP_F | DEBUG_DECODE_F | DEBUG_TRACE_F; +} + +int X86EMU_trace_off(void) +{ + return M.x86.debug &= ~(DEBUG_STEP_F | DEBUG_DECODE_F | DEBUG_TRACE_F); +} + +static int x86emu_parse_line(char *s, int *ps, int *n) +{ + int cmd; + + *n = 0; + while (isblank(*s)) + s++; + ps[*n] = *s; + switch (*s) { + case '\n': + *n += 1; + return 0; + default: + cmd = *s; + *n += 1; + } + + while (1) { + while (!isblank(*s) && *s != '\n') + s++; + + if (*s == '\n') + return cmd; + + while (isblank(*s)) + s++; + + *n += 1; + } +} + +#endif /* DEBUG */ + +void x86emu_dump_regs(void) +{ + printk("\tAX=%04x ", M.x86.R_AX); + printk("BX=%04x ", M.x86.R_BX); + printk("CX=%04x ", M.x86.R_CX); + printk("DX=%04x ", M.x86.R_DX); + printk("SP=%04x ", M.x86.R_SP); + printk("BP=%04x ", M.x86.R_BP); + printk("SI=%04x ", M.x86.R_SI); + printk("DI=%04x\n", M.x86.R_DI); + printk("\tDS=%04x ", M.x86.R_DS); + printk("ES=%04x ", M.x86.R_ES); + printk("SS=%04x ", M.x86.R_SS); + printk("CS=%04x ", M.x86.R_CS); + printk("IP=%04x ", M.x86.R_IP); + if (ACCESS_FLAG(F_OF)) + printk("OV "); /* CHECKED... */ + else + printk("NV "); + if (ACCESS_FLAG(F_DF)) + printk("DN "); + else + printk("UP "); + if (ACCESS_FLAG(F_IF)) + printk("EI "); + else + printk("DI "); + if (ACCESS_FLAG(F_SF)) + printk("NG "); + else + printk("PL "); + if (ACCESS_FLAG(F_ZF)) + printk("ZR "); + else + printk("NZ "); + if (ACCESS_FLAG(F_AF)) + printk("AC "); + else + printk("NA "); + if (ACCESS_FLAG(F_PF)) + printk("PE "); + else + printk("PO "); + if (ACCESS_FLAG(F_CF)) + printk("CY "); + else + printk("NC "); + printk("\n"); +} + +void x86emu_dump_xregs(void) +{ + printk("\tEAX=%08x ", M.x86.R_EAX); + printk("EBX=%08x ", M.x86.R_EBX); + printk("ECX=%08x ", M.x86.R_ECX); + printk("EDX=%08x \n", M.x86.R_EDX); + printk("\tESP=%08x ", M.x86.R_ESP); + printk("EBP=%08x ", M.x86.R_EBP); + printk("ESI=%08x ", M.x86.R_ESI); + printk("EDI=%08x\n", M.x86.R_EDI); + printk("\tDS=%04x ", M.x86.R_DS); + printk("ES=%04x ", M.x86.R_ES); + printk("SS=%04x ", M.x86.R_SS); + printk("CS=%04x ", M.x86.R_CS); + printk("EIP=%08x\n\t", M.x86.R_EIP); + if (ACCESS_FLAG(F_OF)) + printk("OV "); /* CHECKED... */ + else + printk("NV "); + if (ACCESS_FLAG(F_DF)) + printk("DN "); + else + printk("UP "); + if (ACCESS_FLAG(F_IF)) + printk("EI "); + else + printk("DI "); + if (ACCESS_FLAG(F_SF)) + printk("NG "); + else + printk("PL "); + if (ACCESS_FLAG(F_ZF)) + printk("ZR "); + else + printk("NZ "); + if (ACCESS_FLAG(F_AF)) + printk("AC "); + else + printk("NA "); + if (ACCESS_FLAG(F_PF)) + printk("PE "); + else + printk("PO "); + if (ACCESS_FLAG(F_CF)) + printk("CY "); + else + printk("NC "); + printk("\n"); +} diff --git a/qemu/roms/u-boot/drivers/bios_emulator/x86emu/decode.c b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/decode.c new file mode 100644 index 000000000..a782b817b --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/decode.c @@ -0,0 +1,1144 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: This file includes subroutines which are related to +* instruction decoding and accessess of immediate data via IP. etc. +* +****************************************************************************/ +#include +#include "x86emu/x86emui.h" + +/*----------------------------- Implementation ----------------------------*/ + +/**************************************************************************** +REMARKS: +Handles any pending asychronous interrupts. +****************************************************************************/ +static void x86emu_intr_handle(void) +{ + u8 intno; + + if (M.x86.intr & INTR_SYNCH) { + intno = M.x86.intno; + if (_X86EMU_intrTab[intno]) { + (*_X86EMU_intrTab[intno])(intno); + } else { + push_word((u16)M.x86.R_FLG); + CLEAR_FLAG(F_IF); + CLEAR_FLAG(F_TF); + push_word(M.x86.R_CS); + M.x86.R_CS = mem_access_word(intno * 4 + 2); + push_word(M.x86.R_IP); + M.x86.R_IP = mem_access_word(intno * 4); + M.x86.intr = 0; + } + } +} + +/**************************************************************************** +PARAMETERS: +intrnum - Interrupt number to raise + +REMARKS: +Raise the specified interrupt to be handled before the execution of the +next instruction. +****************************************************************************/ +void x86emu_intr_raise( + u8 intrnum) +{ + M.x86.intno = intrnum; + M.x86.intr |= INTR_SYNCH; +} + +/**************************************************************************** +REMARKS: +Main execution loop for the emulator. We return from here when the system +halts, which is normally caused by a stack fault when we return from the +original real mode call. +****************************************************************************/ +void X86EMU_exec(void) +{ + u8 op1; + + M.x86.intr = 0; + DB(x86emu_end_instr();) + + for (;;) { +DB( if (CHECK_IP_FETCH()) + x86emu_check_ip_access();) + /* If debugging, save the IP and CS values. */ + SAVE_IP_CS(M.x86.R_CS, M.x86.R_IP); + INC_DECODED_INST_LEN(1); + if (M.x86.intr) { + if (M.x86.intr & INTR_HALTED) { +DB( if (M.x86.R_SP != 0) { + printk("halted\n"); + X86EMU_trace_regs(); + } + else { + if (M.x86.debug) + printk("Service completed successfully\n"); + }) + return; + } + if (((M.x86.intr & INTR_SYNCH) && (M.x86.intno == 0 || M.x86.intno == 2)) || + !ACCESS_FLAG(F_IF)) { + x86emu_intr_handle(); + } + } + op1 = (*sys_rdb)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP++)); + (*x86emu_optab[op1])(op1); + if (M.x86.debug & DEBUG_EXIT) { + M.x86.debug &= ~DEBUG_EXIT; + return; + } + } +} + +/**************************************************************************** +REMARKS: +Halts the system by setting the halted system flag. +****************************************************************************/ +void X86EMU_halt_sys(void) +{ + M.x86.intr |= INTR_HALTED; +} + +/**************************************************************************** +PARAMETERS: +mod - Mod value from decoded byte +regh - Reg h value from decoded byte +regl - Reg l value from decoded byte + +REMARKS: +Raise the specified interrupt to be handled before the execution of the +next instruction. + +NOTE: Do not inline this function, as (*sys_rdb) is already inline! +****************************************************************************/ +void fetch_decode_modrm( + int *mod, + int *regh, + int *regl) +{ + int fetched; + +DB( if (CHECK_IP_FETCH()) + x86emu_check_ip_access();) + fetched = (*sys_rdb)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP++)); + INC_DECODED_INST_LEN(1); + *mod = (fetched >> 6) & 0x03; + *regh = (fetched >> 3) & 0x07; + *regl = (fetched >> 0) & 0x07; +} + +/**************************************************************************** +RETURNS: +Immediate byte value read from instruction queue + +REMARKS: +This function returns the immediate byte from the instruction queue, and +moves the instruction pointer to the next value. + +NOTE: Do not inline this function, as (*sys_rdb) is already inline! +****************************************************************************/ +u8 fetch_byte_imm(void) +{ + u8 fetched; + +DB( if (CHECK_IP_FETCH()) + x86emu_check_ip_access();) + fetched = (*sys_rdb)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP++)); + INC_DECODED_INST_LEN(1); + return fetched; +} + +/**************************************************************************** +RETURNS: +Immediate word value read from instruction queue + +REMARKS: +This function returns the immediate byte from the instruction queue, and +moves the instruction pointer to the next value. + +NOTE: Do not inline this function, as (*sys_rdw) is already inline! +****************************************************************************/ +u16 fetch_word_imm(void) +{ + u16 fetched; + +DB( if (CHECK_IP_FETCH()) + x86emu_check_ip_access();) + fetched = (*sys_rdw)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP)); + M.x86.R_IP += 2; + INC_DECODED_INST_LEN(2); + return fetched; +} + +/**************************************************************************** +RETURNS: +Immediate lone value read from instruction queue + +REMARKS: +This function returns the immediate byte from the instruction queue, and +moves the instruction pointer to the next value. + +NOTE: Do not inline this function, as (*sys_rdw) is already inline! +****************************************************************************/ +u32 fetch_long_imm(void) +{ + u32 fetched; + +DB( if (CHECK_IP_FETCH()) + x86emu_check_ip_access();) + fetched = (*sys_rdl)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP)); + M.x86.R_IP += 4; + INC_DECODED_INST_LEN(4); + return fetched; +} + +/**************************************************************************** +RETURNS: +Value of the default data segment + +REMARKS: +Inline function that returns the default data segment for the current +instruction. + +On the x86 processor, the default segment is not always DS if there is +no segment override. Address modes such as -3[BP] or 10[BP+SI] all refer to +addresses relative to SS (ie: on the stack). So, at the minimum, all +decodings of addressing modes would have to set/clear a bit describing +whether the access is relative to DS or SS. That is the function of the +cpu-state-varible M.x86.mode. There are several potential states: + + repe prefix seen (handled elsewhere) + repne prefix seen (ditto) + + cs segment override + ds segment override + es segment override + fs segment override + gs segment override + ss segment override + + ds/ss select (in absense of override) + +Each of the above 7 items are handled with a bit in the mode field. +****************************************************************************/ +_INLINE u32 get_data_segment(void) +{ +#define GET_SEGMENT(segment) + switch (M.x86.mode & SYSMODE_SEGMASK) { + case 0: /* default case: use ds register */ + case SYSMODE_SEGOVR_DS: + case SYSMODE_SEGOVR_DS | SYSMODE_SEG_DS_SS: + return M.x86.R_DS; + case SYSMODE_SEG_DS_SS: /* non-overridden, use ss register */ + return M.x86.R_SS; + case SYSMODE_SEGOVR_CS: + case SYSMODE_SEGOVR_CS | SYSMODE_SEG_DS_SS: + return M.x86.R_CS; + case SYSMODE_SEGOVR_ES: + case SYSMODE_SEGOVR_ES | SYSMODE_SEG_DS_SS: + return M.x86.R_ES; + case SYSMODE_SEGOVR_FS: + case SYSMODE_SEGOVR_FS | SYSMODE_SEG_DS_SS: + return M.x86.R_FS; + case SYSMODE_SEGOVR_GS: + case SYSMODE_SEGOVR_GS | SYSMODE_SEG_DS_SS: + return M.x86.R_GS; + case SYSMODE_SEGOVR_SS: + case SYSMODE_SEGOVR_SS | SYSMODE_SEG_DS_SS: + return M.x86.R_SS; + default: +#ifdef DEBUG + printk("error: should not happen: multiple overrides.\n"); +#endif + HALT_SYS(); + return 0; + } +} + +/**************************************************************************** +PARAMETERS: +offset - Offset to load data from + +RETURNS: +Byte value read from the absolute memory location. + +NOTE: Do not inline this function as (*sys_rdX) is already inline! +****************************************************************************/ +u8 fetch_data_byte( + uint offset) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access((u16)get_data_segment(), offset); +#endif + return (*sys_rdb)((get_data_segment() << 4) + offset); +} + +/**************************************************************************** +PARAMETERS: +offset - Offset to load data from + +RETURNS: +Word value read from the absolute memory location. + +NOTE: Do not inline this function as (*sys_rdX) is already inline! +****************************************************************************/ +u16 fetch_data_word( + uint offset) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access((u16)get_data_segment(), offset); +#endif + return (*sys_rdw)((get_data_segment() << 4) + offset); +} + +/**************************************************************************** +PARAMETERS: +offset - Offset to load data from + +RETURNS: +Long value read from the absolute memory location. + +NOTE: Do not inline this function as (*sys_rdX) is already inline! +****************************************************************************/ +u32 fetch_data_long( + uint offset) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access((u16)get_data_segment(), offset); +#endif + return (*sys_rdl)((get_data_segment() << 4) + offset); +} + +/**************************************************************************** +PARAMETERS: +segment - Segment to load data from +offset - Offset to load data from + +RETURNS: +Byte value read from the absolute memory location. + +NOTE: Do not inline this function as (*sys_rdX) is already inline! +****************************************************************************/ +u8 fetch_data_byte_abs( + uint segment, + uint offset) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access(segment, offset); +#endif + return (*sys_rdb)(((u32)segment << 4) + offset); +} + +/**************************************************************************** +PARAMETERS: +segment - Segment to load data from +offset - Offset to load data from + +RETURNS: +Word value read from the absolute memory location. + +NOTE: Do not inline this function as (*sys_rdX) is already inline! +****************************************************************************/ +u16 fetch_data_word_abs( + uint segment, + uint offset) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access(segment, offset); +#endif + return (*sys_rdw)(((u32)segment << 4) + offset); +} + +/**************************************************************************** +PARAMETERS: +segment - Segment to load data from +offset - Offset to load data from + +RETURNS: +Long value read from the absolute memory location. + +NOTE: Do not inline this function as (*sys_rdX) is already inline! +****************************************************************************/ +u32 fetch_data_long_abs( + uint segment, + uint offset) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access(segment, offset); +#endif + return (*sys_rdl)(((u32)segment << 4) + offset); +} + +/**************************************************************************** +PARAMETERS: +offset - Offset to store data at +val - Value to store + +REMARKS: +Writes a word value to an segmented memory location. The segment used is +the current 'default' segment, which may have been overridden. + +NOTE: Do not inline this function as (*sys_wrX) is already inline! +****************************************************************************/ +void store_data_byte( + uint offset, + u8 val) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access((u16)get_data_segment(), offset); +#endif + (*sys_wrb)((get_data_segment() << 4) + offset, val); +} + +/**************************************************************************** +PARAMETERS: +offset - Offset to store data at +val - Value to store + +REMARKS: +Writes a word value to an segmented memory location. The segment used is +the current 'default' segment, which may have been overridden. + +NOTE: Do not inline this function as (*sys_wrX) is already inline! +****************************************************************************/ +void store_data_word( + uint offset, + u16 val) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access((u16)get_data_segment(), offset); +#endif + (*sys_wrw)((get_data_segment() << 4) + offset, val); +} + +/**************************************************************************** +PARAMETERS: +offset - Offset to store data at +val - Value to store + +REMARKS: +Writes a long value to an segmented memory location. The segment used is +the current 'default' segment, which may have been overridden. + +NOTE: Do not inline this function as (*sys_wrX) is already inline! +****************************************************************************/ +void store_data_long( + uint offset, + u32 val) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access((u16)get_data_segment(), offset); +#endif + (*sys_wrl)((get_data_segment() << 4) + offset, val); +} + +/**************************************************************************** +PARAMETERS: +segment - Segment to store data at +offset - Offset to store data at +val - Value to store + +REMARKS: +Writes a byte value to an absolute memory location. + +NOTE: Do not inline this function as (*sys_wrX) is already inline! +****************************************************************************/ +void store_data_byte_abs( + uint segment, + uint offset, + u8 val) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access(segment, offset); +#endif + (*sys_wrb)(((u32)segment << 4) + offset, val); +} + +/**************************************************************************** +PARAMETERS: +segment - Segment to store data at +offset - Offset to store data at +val - Value to store + +REMARKS: +Writes a word value to an absolute memory location. + +NOTE: Do not inline this function as (*sys_wrX) is already inline! +****************************************************************************/ +void store_data_word_abs( + uint segment, + uint offset, + u16 val) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access(segment, offset); +#endif + (*sys_wrw)(((u32)segment << 4) + offset, val); +} + +/**************************************************************************** +PARAMETERS: +segment - Segment to store data at +offset - Offset to store data at +val - Value to store + +REMARKS: +Writes a long value to an absolute memory location. + +NOTE: Do not inline this function as (*sys_wrX) is already inline! +****************************************************************************/ +void store_data_long_abs( + uint segment, + uint offset, + u32 val) +{ +#ifdef DEBUG + if (CHECK_DATA_ACCESS()) + x86emu_check_data_access(segment, offset); +#endif + (*sys_wrl)(((u32)segment << 4) + offset, val); +} + +/**************************************************************************** +PARAMETERS: +reg - Register to decode + +RETURNS: +Pointer to the appropriate register + +REMARKS: +Return a pointer to the register given by the R/RM field of the +modrm byte, for byte operands. Also enables the decoding of instructions. +****************************************************************************/ +u8* decode_rm_byte_register( + int reg) +{ + switch (reg) { + case 0: + DECODE_PRINTF("AL"); + return &M.x86.R_AL; + case 1: + DECODE_PRINTF("CL"); + return &M.x86.R_CL; + case 2: + DECODE_PRINTF("DL"); + return &M.x86.R_DL; + case 3: + DECODE_PRINTF("BL"); + return &M.x86.R_BL; + case 4: + DECODE_PRINTF("AH"); + return &M.x86.R_AH; + case 5: + DECODE_PRINTF("CH"); + return &M.x86.R_CH; + case 6: + DECODE_PRINTF("DH"); + return &M.x86.R_DH; + case 7: + DECODE_PRINTF("BH"); + return &M.x86.R_BH; + } + HALT_SYS(); + return NULL; /* NOT REACHED OR REACHED ON ERROR */ +} + +/**************************************************************************** +PARAMETERS: +reg - Register to decode + +RETURNS: +Pointer to the appropriate register + +REMARKS: +Return a pointer to the register given by the R/RM field of the +modrm byte, for word operands. Also enables the decoding of instructions. +****************************************************************************/ +u16* decode_rm_word_register( + int reg) +{ + switch (reg) { + case 0: + DECODE_PRINTF("AX"); + return &M.x86.R_AX; + case 1: + DECODE_PRINTF("CX"); + return &M.x86.R_CX; + case 2: + DECODE_PRINTF("DX"); + return &M.x86.R_DX; + case 3: + DECODE_PRINTF("BX"); + return &M.x86.R_BX; + case 4: + DECODE_PRINTF("SP"); + return &M.x86.R_SP; + case 5: + DECODE_PRINTF("BP"); + return &M.x86.R_BP; + case 6: + DECODE_PRINTF("SI"); + return &M.x86.R_SI; + case 7: + DECODE_PRINTF("DI"); + return &M.x86.R_DI; + } + HALT_SYS(); + return NULL; /* NOTREACHED OR REACHED ON ERROR */ +} + +/**************************************************************************** +PARAMETERS: +reg - Register to decode + +RETURNS: +Pointer to the appropriate register + +REMARKS: +Return a pointer to the register given by the R/RM field of the +modrm byte, for dword operands. Also enables the decoding of instructions. +****************************************************************************/ +u32* decode_rm_long_register( + int reg) +{ + switch (reg) { + case 0: + DECODE_PRINTF("EAX"); + return &M.x86.R_EAX; + case 1: + DECODE_PRINTF("ECX"); + return &M.x86.R_ECX; + case 2: + DECODE_PRINTF("EDX"); + return &M.x86.R_EDX; + case 3: + DECODE_PRINTF("EBX"); + return &M.x86.R_EBX; + case 4: + DECODE_PRINTF("ESP"); + return &M.x86.R_ESP; + case 5: + DECODE_PRINTF("EBP"); + return &M.x86.R_EBP; + case 6: + DECODE_PRINTF("ESI"); + return &M.x86.R_ESI; + case 7: + DECODE_PRINTF("EDI"); + return &M.x86.R_EDI; + } + HALT_SYS(); + return NULL; /* NOTREACHED OR REACHED ON ERROR */ +} + +/**************************************************************************** +PARAMETERS: +reg - Register to decode + +RETURNS: +Pointer to the appropriate register + +REMARKS: +Return a pointer to the register given by the R/RM field of the +modrm byte, for word operands, modified from above for the weirdo +special case of segreg operands. Also enables the decoding of instructions. +****************************************************************************/ +u16* decode_rm_seg_register( + int reg) +{ + switch (reg) { + case 0: + DECODE_PRINTF("ES"); + return &M.x86.R_ES; + case 1: + DECODE_PRINTF("CS"); + return &M.x86.R_CS; + case 2: + DECODE_PRINTF("SS"); + return &M.x86.R_SS; + case 3: + DECODE_PRINTF("DS"); + return &M.x86.R_DS; + case 4: + DECODE_PRINTF("FS"); + return &M.x86.R_FS; + case 5: + DECODE_PRINTF("GS"); + return &M.x86.R_GS; + case 6: + case 7: + DECODE_PRINTF("ILLEGAL SEGREG"); + break; + } + HALT_SYS(); + return NULL; /* NOT REACHED OR REACHED ON ERROR */ +} + +/**************************************************************************** +PARAMETERS: +scale - scale value of SIB byte +index - index value of SIB byte + +RETURNS: +Value of scale * index + +REMARKS: +Decodes scale/index of SIB byte and returns relevant offset part of +effective address. +****************************************************************************/ +unsigned decode_sib_si( + int scale, + int index) +{ + scale = 1 << scale; + if (scale > 1) { + DECODE_PRINTF2("[%d*", scale); + } else { + DECODE_PRINTF("["); + } + switch (index) { + case 0: + DECODE_PRINTF("EAX]"); + return M.x86.R_EAX * index; + case 1: + DECODE_PRINTF("ECX]"); + return M.x86.R_ECX * index; + case 2: + DECODE_PRINTF("EDX]"); + return M.x86.R_EDX * index; + case 3: + DECODE_PRINTF("EBX]"); + return M.x86.R_EBX * index; + case 4: + DECODE_PRINTF("0]"); + return 0; + case 5: + DECODE_PRINTF("EBP]"); + return M.x86.R_EBP * index; + case 6: + DECODE_PRINTF("ESI]"); + return M.x86.R_ESI * index; + case 7: + DECODE_PRINTF("EDI]"); + return M.x86.R_EDI * index; + } + HALT_SYS(); + return 0; /* NOT REACHED OR REACHED ON ERROR */ +} + +/**************************************************************************** +PARAMETERS: +mod - MOD value of preceding ModR/M byte + +RETURNS: +Offset in memory for the address decoding + +REMARKS: +Decodes SIB addressing byte and returns calculated effective address. +****************************************************************************/ +unsigned decode_sib_address( + int mod) +{ + int sib = fetch_byte_imm(); + int ss = (sib >> 6) & 0x03; + int index = (sib >> 3) & 0x07; + int base = sib & 0x07; + int offset = 0; + int displacement; + + switch (base) { + case 0: + DECODE_PRINTF("[EAX]"); + offset = M.x86.R_EAX; + break; + case 1: + DECODE_PRINTF("[ECX]"); + offset = M.x86.R_ECX; + break; + case 2: + DECODE_PRINTF("[EDX]"); + offset = M.x86.R_EDX; + break; + case 3: + DECODE_PRINTF("[EBX]"); + offset = M.x86.R_EBX; + break; + case 4: + DECODE_PRINTF("[ESP]"); + offset = M.x86.R_ESP; + break; + case 5: + switch (mod) { + case 0: + displacement = (s32)fetch_long_imm(); + DECODE_PRINTF2("[%d]", displacement); + offset = displacement; + break; + case 1: + displacement = (s8)fetch_byte_imm(); + DECODE_PRINTF2("[%d][EBP]", displacement); + offset = M.x86.R_EBP + displacement; + break; + case 2: + displacement = (s32)fetch_long_imm(); + DECODE_PRINTF2("[%d][EBP]", displacement); + offset = M.x86.R_EBP + displacement; + break; + default: + HALT_SYS(); + } + DECODE_PRINTF("[EAX]"); + offset = M.x86.R_EAX; + break; + case 6: + DECODE_PRINTF("[ESI]"); + offset = M.x86.R_ESI; + break; + case 7: + DECODE_PRINTF("[EDI]"); + offset = M.x86.R_EDI; + break; + default: + HALT_SYS(); + } + offset += decode_sib_si(ss, index); + return offset; + +} + +/**************************************************************************** +PARAMETERS: +rm - RM value to decode + +RETURNS: +Offset in memory for the address decoding + +REMARKS: +Return the offset given by mod=00 addressing. Also enables the +decoding of instructions. + +NOTE: The code which specifies the corresponding segment (ds vs ss) + below in the case of [BP+..]. The assumption here is that at the + point that this subroutine is called, the bit corresponding to + SYSMODE_SEG_DS_SS will be zero. After every instruction + except the segment override instructions, this bit (as well + as any bits indicating segment overrides) will be clear. So + if a SS access is needed, set this bit. Otherwise, DS access + occurs (unless any of the segment override bits are set). +****************************************************************************/ +unsigned decode_rm00_address( + int rm) +{ + unsigned offset; + + if (M.x86.mode & SYSMODE_PREFIX_ADDR) { + /* 32-bit addressing */ + switch (rm) { + case 0: + DECODE_PRINTF("[EAX]"); + return M.x86.R_EAX; + case 1: + DECODE_PRINTF("[ECX]"); + return M.x86.R_ECX; + case 2: + DECODE_PRINTF("[EDX]"); + return M.x86.R_EDX; + case 3: + DECODE_PRINTF("[EBX]"); + return M.x86.R_EBX; + case 4: + return decode_sib_address(0); + case 5: + offset = fetch_long_imm(); + DECODE_PRINTF2("[%08x]", offset); + return offset; + case 6: + DECODE_PRINTF("[ESI]"); + return M.x86.R_ESI; + case 7: + DECODE_PRINTF("[EDI]"); + return M.x86.R_EDI; + } + } else { + /* 16-bit addressing */ + switch (rm) { + case 0: + DECODE_PRINTF("[BX+SI]"); + return (M.x86.R_BX + M.x86.R_SI) & 0xffff; + case 1: + DECODE_PRINTF("[BX+DI]"); + return (M.x86.R_BX + M.x86.R_DI) & 0xffff; + case 2: + DECODE_PRINTF("[BP+SI]"); + M.x86.mode |= SYSMODE_SEG_DS_SS; + return (M.x86.R_BP + M.x86.R_SI) & 0xffff; + case 3: + DECODE_PRINTF("[BP+DI]"); + M.x86.mode |= SYSMODE_SEG_DS_SS; + return (M.x86.R_BP + M.x86.R_DI) & 0xffff; + case 4: + DECODE_PRINTF("[SI]"); + return M.x86.R_SI; + case 5: + DECODE_PRINTF("[DI]"); + return M.x86.R_DI; + case 6: + offset = fetch_word_imm(); + DECODE_PRINTF2("[%04x]", offset); + return offset; + case 7: + DECODE_PRINTF("[BX]"); + return M.x86.R_BX; + } + } + HALT_SYS(); + return 0; +} + +/**************************************************************************** +PARAMETERS: +rm - RM value to decode + +RETURNS: +Offset in memory for the address decoding + +REMARKS: +Return the offset given by mod=01 addressing. Also enables the +decoding of instructions. +****************************************************************************/ +unsigned decode_rm01_address( + int rm) +{ + int displacement; + + if (M.x86.mode & SYSMODE_PREFIX_ADDR) { + /* 32-bit addressing */ + if (rm != 4) + displacement = (s8)fetch_byte_imm(); + else + displacement = 0; + + switch (rm) { + case 0: + DECODE_PRINTF2("%d[EAX]", displacement); + return M.x86.R_EAX + displacement; + case 1: + DECODE_PRINTF2("%d[ECX]", displacement); + return M.x86.R_ECX + displacement; + case 2: + DECODE_PRINTF2("%d[EDX]", displacement); + return M.x86.R_EDX + displacement; + case 3: + DECODE_PRINTF2("%d[EBX]", displacement); + return M.x86.R_EBX + displacement; + case 4: { + int offset = decode_sib_address(1); + displacement = (s8)fetch_byte_imm(); + DECODE_PRINTF2("[%d]", displacement); + return offset + displacement; + } + case 5: + DECODE_PRINTF2("%d[EBP]", displacement); + return M.x86.R_EBP + displacement; + case 6: + DECODE_PRINTF2("%d[ESI]", displacement); + return M.x86.R_ESI + displacement; + case 7: + DECODE_PRINTF2("%d[EDI]", displacement); + return M.x86.R_EDI + displacement; + } + } else { + /* 16-bit addressing */ + displacement = (s8)fetch_byte_imm(); + switch (rm) { + case 0: + DECODE_PRINTF2("%d[BX+SI]", displacement); + return (M.x86.R_BX + M.x86.R_SI + displacement) & 0xffff; + case 1: + DECODE_PRINTF2("%d[BX+DI]", displacement); + return (M.x86.R_BX + M.x86.R_DI + displacement) & 0xffff; + case 2: + DECODE_PRINTF2("%d[BP+SI]", displacement); + M.x86.mode |= SYSMODE_SEG_DS_SS; + return (M.x86.R_BP + M.x86.R_SI + displacement) & 0xffff; + case 3: + DECODE_PRINTF2("%d[BP+DI]", displacement); + M.x86.mode |= SYSMODE_SEG_DS_SS; + return (M.x86.R_BP + M.x86.R_DI + displacement) & 0xffff; + case 4: + DECODE_PRINTF2("%d[SI]", displacement); + return (M.x86.R_SI + displacement) & 0xffff; + case 5: + DECODE_PRINTF2("%d[DI]", displacement); + return (M.x86.R_DI + displacement) & 0xffff; + case 6: + DECODE_PRINTF2("%d[BP]", displacement); + M.x86.mode |= SYSMODE_SEG_DS_SS; + return (M.x86.R_BP + displacement) & 0xffff; + case 7: + DECODE_PRINTF2("%d[BX]", displacement); + return (M.x86.R_BX + displacement) & 0xffff; + } + } + HALT_SYS(); + return 0; /* SHOULD NOT HAPPEN */ +} + +/**************************************************************************** +PARAMETERS: +rm - RM value to decode + +RETURNS: +Offset in memory for the address decoding + +REMARKS: +Return the offset given by mod=10 addressing. Also enables the +decoding of instructions. +****************************************************************************/ +unsigned decode_rm10_address( + int rm) +{ + if (M.x86.mode & SYSMODE_PREFIX_ADDR) { + int displacement; + + /* 32-bit addressing */ + if (rm != 4) + displacement = (s32)fetch_long_imm(); + else + displacement = 0; + + switch (rm) { + case 0: + DECODE_PRINTF2("%d[EAX]", displacement); + return M.x86.R_EAX + displacement; + case 1: + DECODE_PRINTF2("%d[ECX]", displacement); + return M.x86.R_ECX + displacement; + case 2: + DECODE_PRINTF2("%d[EDX]", displacement); + return M.x86.R_EDX + displacement; + case 3: + DECODE_PRINTF2("%d[EBX]", displacement); + return M.x86.R_EBX + displacement; + case 4: { + int offset = decode_sib_address(2); + displacement = (s32)fetch_long_imm(); + DECODE_PRINTF2("[%d]", displacement); + return offset + displacement; + } + case 5: + DECODE_PRINTF2("%d[EBP]", displacement); + return M.x86.R_EBP + displacement; + case 6: + DECODE_PRINTF2("%d[ESI]", displacement); + return M.x86.R_ESI + displacement; + case 7: + DECODE_PRINTF2("%d[EDI]", displacement); + return M.x86.R_EDI + displacement; + } + } else { + int displacement = (s16)fetch_word_imm(); + + /* 16-bit addressing */ + switch (rm) { + case 0: + DECODE_PRINTF2("%d[BX+SI]", displacement); + return (M.x86.R_BX + M.x86.R_SI + displacement) & 0xffff; + case 1: + DECODE_PRINTF2("%d[BX+DI]", displacement); + return (M.x86.R_BX + M.x86.R_DI + displacement) & 0xffff; + case 2: + DECODE_PRINTF2("%d[BP+SI]", displacement); + M.x86.mode |= SYSMODE_SEG_DS_SS; + return (M.x86.R_BP + M.x86.R_SI + displacement) & 0xffff; + case 3: + DECODE_PRINTF2("%d[BP+DI]", displacement); + M.x86.mode |= SYSMODE_SEG_DS_SS; + return (M.x86.R_BP + M.x86.R_DI + displacement) & 0xffff; + case 4: + DECODE_PRINTF2("%d[SI]", displacement); + return (M.x86.R_SI + displacement) & 0xffff; + case 5: + DECODE_PRINTF2("%d[DI]", displacement); + return (M.x86.R_DI + displacement) & 0xffff; + case 6: + DECODE_PRINTF2("%d[BP]", displacement); + M.x86.mode |= SYSMODE_SEG_DS_SS; + return (M.x86.R_BP + displacement) & 0xffff; + case 7: + DECODE_PRINTF2("%d[BX]", displacement); + return (M.x86.R_BX + displacement) & 0xffff; + } + } + HALT_SYS(); + return 0; /* SHOULD NOT HAPPEN */ +} + +/**************************************************************************** +PARAMETERS: +mod - modifier +rm - RM value to decode + +RETURNS: +Offset in memory for the address decoding, multiplexing calls to +the decode_rmXX_address functions + +REMARKS: +Return the offset given by "mod" addressing. +****************************************************************************/ + +unsigned decode_rmXX_address(int mod, int rm) +{ + if(mod == 0) + return decode_rm00_address(rm); + if(mod == 1) + return decode_rm01_address(rm); + return decode_rm10_address(rm); +} diff --git a/qemu/roms/u-boot/drivers/bios_emulator/x86emu/ops.c b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/ops.c new file mode 100644 index 000000000..f8e093d75 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/ops.c @@ -0,0 +1,5423 @@ +/**************************************************************************** +* Realmode X86 Emulator Library +* +* Copyright (C) 2007 Freescale Semiconductor, Inc. +* Jason Jin +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: This file includes subroutines to implement the decoding +* and emulation of all the x86 processor instructions. +* +* There are approximately 250 subroutines in here, which correspond +* to the 256 byte-"opcodes" found on the 8086. The table which +* dispatches this is found in the files optab.[ch]. +* +* Each opcode proc has a comment preceeding it which gives it's table +* address. Several opcodes are missing (undefined) in the table. +* +* Each proc includes information for decoding (DECODE_PRINTF and +* DECODE_PRINTF2), debugging (TRACE_REGS, SINGLE_STEP), and misc +* functions (START_OF_INSTR, END_OF_INSTR). +* +* Many of the procedures are *VERY* similar in coding. This has +* allowed for a very large amount of code to be generated in a fairly +* short amount of time (i.e. cut, paste, and modify). The result is +* that much of the code below could have been folded into subroutines +* for a large reduction in size of this file. The downside would be +* that there would be a penalty in execution speed. The file could +* also have been *MUCH* larger by inlining certain functions which +* were called. This could have resulted even faster execution. The +* prime directive I used to decide whether to inline the code or to +* modularize it, was basically: 1) no unnecessary subroutine calls, +* 2) no routines more than about 200 lines in size, and 3) modularize +* any code that I might not get right the first time. The fetch_* +* subroutines fall into the latter category. The The decode_* fall +* into the second category. The coding of the "switch(mod){ .... }" +* in many of the subroutines below falls into the first category. +* Especially, the coding of {add,and,or,sub,...}_{byte,word} +* subroutines are an especially glaring case of the third guideline. +* Since so much of the code is cloned from other modules (compare +* opcode #00 to opcode #01), making the basic operations subroutine +* calls is especially important; otherwise mistakes in coding an +* "add" would represent a nightmare in maintenance. +* +****************************************************************************/ + +#include +#include "x86emu/x86emui.h" + +/*----------------------------- Implementation ----------------------------*/ + +/* constant arrays to do several instructions in just one function */ + +#ifdef DEBUG +static char *x86emu_GenOpName[8] = { + "ADD", "OR", "ADC", "SBB", "AND", "SUB", "XOR", "CMP"}; +#endif + +/* used by several opcodes */ +static u8 (*genop_byte_operation[])(u8 d, u8 s) = +{ + add_byte, /* 00 */ + or_byte, /* 01 */ + adc_byte, /* 02 */ + sbb_byte, /* 03 */ + and_byte, /* 04 */ + sub_byte, /* 05 */ + xor_byte, /* 06 */ + cmp_byte, /* 07 */ +}; + +static u16 (*genop_word_operation[])(u16 d, u16 s) = +{ + add_word, /*00 */ + or_word, /*01 */ + adc_word, /*02 */ + sbb_word, /*03 */ + and_word, /*04 */ + sub_word, /*05 */ + xor_word, /*06 */ + cmp_word, /*07 */ +}; + +static u32 (*genop_long_operation[])(u32 d, u32 s) = +{ + add_long, /*00 */ + or_long, /*01 */ + adc_long, /*02 */ + sbb_long, /*03 */ + and_long, /*04 */ + sub_long, /*05 */ + xor_long, /*06 */ + cmp_long, /*07 */ +}; + +/* used by opcodes 80, c0, d0, and d2. */ +static u8(*opcD0_byte_operation[])(u8 d, u8 s) = +{ + rol_byte, + ror_byte, + rcl_byte, + rcr_byte, + shl_byte, + shr_byte, + shl_byte, /* sal_byte === shl_byte by definition */ + sar_byte, +}; + +/* used by opcodes c1, d1, and d3. */ +static u16(*opcD1_word_operation[])(u16 s, u8 d) = +{ + rol_word, + ror_word, + rcl_word, + rcr_word, + shl_word, + shr_word, + shl_word, /* sal_byte === shl_byte by definition */ + sar_word, +}; + +/* used by opcodes c1, d1, and d3. */ +static u32 (*opcD1_long_operation[])(u32 s, u8 d) = +{ + rol_long, + ror_long, + rcl_long, + rcr_long, + shl_long, + shr_long, + shl_long, /* sal_byte === shl_byte by definition */ + sar_long, +}; + +#ifdef DEBUG + +static char *opF6_names[8] = + { "TEST\t", "", "NOT\t", "NEG\t", "MUL\t", "IMUL\t", "DIV\t", "IDIV\t" }; + +#endif + +/**************************************************************************** +PARAMETERS: +op1 - Instruction op code + +REMARKS: +Handles illegal opcodes. +****************************************************************************/ +void x86emuOp_illegal_op( + u8 op1) +{ + START_OF_INSTR(); + if (M.x86.R_SP != 0) { + DECODE_PRINTF("ILLEGAL X86 OPCODE\n"); + TRACE_REGS(); + DB( printk("%04x:%04x: %02X ILLEGAL X86 OPCODE!\n", + M.x86.R_CS, M.x86.R_IP-1,op1)); + HALT_SYS(); + } + else { + /* If we get here, it means the stack pointer is back to zero + * so we are just returning from an emulator service call + * so therte is no need to display an error message. We trap + * the emulator with an 0xF1 opcode to finish the service + * call. + */ + X86EMU_halt_sys(); + } + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcodes 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38 +****************************************************************************/ +void x86emuOp_genop_byte_RM_R(u8 op1) +{ + int mod, rl, rh; + uint destoffset; + u8 *destreg, *srcreg; + u8 destval; + + op1 = (op1 >> 3) & 0x7; + + START_OF_INSTR(); + DECODE_PRINTF(x86emu_GenOpName[op1]); + DECODE_PRINTF("\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if(mod<3) + { destoffset = decode_rmXX_address(mod,rl); + DECODE_PRINTF(","); + destval = fetch_data_byte(destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = genop_byte_operation[op1](destval, *srcreg); + store_data_byte(destoffset, destval); + } + else + { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = genop_byte_operation[op1](*destreg, *srcreg); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcodes 0x01, 0x09, 0x11, 0x19, 0x21, 0x29, 0x31, 0x39 +****************************************************************************/ +void x86emuOp_genop_word_RM_R(u8 op1) +{ + int mod, rl, rh; + uint destoffset; + + op1 = (op1 >> 3) & 0x7; + + START_OF_INSTR(); + DECODE_PRINTF(x86emu_GenOpName[op1]); + DECODE_PRINTF("\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + + if(mod<3) { + destoffset = decode_rmXX_address(mod,rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + u32 *srcreg; + + DECODE_PRINTF(","); + destval = fetch_data_long(destoffset); + srcreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = genop_long_operation[op1](destval, *srcreg); + store_data_long(destoffset, destval); + } else { + u16 destval; + u16 *srcreg; + + DECODE_PRINTF(","); + destval = fetch_data_word(destoffset); + srcreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = genop_word_operation[op1](destval, *srcreg); + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*srcreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = genop_long_operation[op1](*destreg, *srcreg); + } else { + u16 *destreg,*srcreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = genop_word_operation[op1](*destreg, *srcreg); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcodes 0x02, 0x0a, 0x12, 0x1a, 0x22, 0x2a, 0x32, 0x3a +****************************************************************************/ +void x86emuOp_genop_byte_R_RM(u8 op1) +{ + int mod, rl, rh; + u8 *destreg, *srcreg; + uint srcoffset; + u8 srcval; + + op1 = (op1 >> 3) & 0x7; + + START_OF_INSTR(); + DECODE_PRINTF(x86emu_GenOpName[op1]); + DECODE_PRINTF("\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod,rl); + srcval = fetch_data_byte(srcoffset); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rl); + srcval = *srcreg; + } + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = genop_byte_operation[op1](*destreg, srcval); + + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcodes 0x03, 0x0b, 0x13, 0x1b, 0x23, 0x2b, 0x33, 0x3b +****************************************************************************/ +void x86emuOp_genop_word_R_RM(u8 op1) +{ + int mod, rl, rh; + uint srcoffset; + u32 *destreg32, srcval; + u16 *destreg; + + op1 = (op1 >> 3) & 0x7; + + START_OF_INSTR(); + DECODE_PRINTF(x86emu_GenOpName[op1]); + DECODE_PRINTF("\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcoffset = decode_rmXX_address(mod,rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + destreg32 = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcval = fetch_data_long(srcoffset); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg32 = genop_long_operation[op1](*destreg32, srcval); + } else { + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcval = fetch_data_word(srcoffset); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = genop_word_operation[op1](*destreg, srcval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg; + destreg32 = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg32 = genop_long_operation[op1](*destreg32, *srcreg); + } else { + u16 *srcreg; + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = genop_word_operation[op1](*destreg, *srcreg); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcodes 0x04, 0x0c, 0x14, 0x1c, 0x24, 0x2c, 0x34, 0x3c +****************************************************************************/ +void x86emuOp_genop_byte_AL_IMM(u8 op1) +{ + u8 srcval; + + op1 = (op1 >> 3) & 0x7; + + START_OF_INSTR(); + DECODE_PRINTF(x86emu_GenOpName[op1]); + DECODE_PRINTF("\tAL,"); + srcval = fetch_byte_imm(); + DECODE_PRINTF2("%x\n", srcval); + TRACE_AND_STEP(); + M.x86.R_AL = genop_byte_operation[op1](M.x86.R_AL, srcval); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcodes 0x05, 0x0d, 0x15, 0x1d, 0x25, 0x2d, 0x35, 0x3d +****************************************************************************/ +void x86emuOp_genop_word_AX_IMM(u8 op1) +{ + u32 srcval; + + op1 = (op1 >> 3) & 0x7; + + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF(x86emu_GenOpName[op1]); + DECODE_PRINTF("\tEAX,"); + srcval = fetch_long_imm(); + } else { + DECODE_PRINTF(x86emu_GenOpName[op1]); + DECODE_PRINTF("\tAX,"); + srcval = fetch_word_imm(); + } + DECODE_PRINTF2("%x\n", srcval); + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + M.x86.R_EAX = genop_long_operation[op1](M.x86.R_EAX, srcval); + } else { + M.x86.R_AX = genop_word_operation[op1](M.x86.R_AX, (u16)srcval); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x06 +****************************************************************************/ +void x86emuOp_push_ES(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("PUSH\tES\n"); + TRACE_AND_STEP(); + push_word(M.x86.R_ES); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x07 +****************************************************************************/ +void x86emuOp_pop_ES(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("POP\tES\n"); + TRACE_AND_STEP(); + M.x86.R_ES = pop_word(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0e +****************************************************************************/ +void x86emuOp_push_CS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("PUSH\tCS\n"); + TRACE_AND_STEP(); + push_word(M.x86.R_CS); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f. Escape for two-byte opcode (286 or better) +****************************************************************************/ +void x86emuOp_two_byte(u8 X86EMU_UNUSED(op1)) +{ + u8 op2 = (*sys_rdb)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP++)); + INC_DECODED_INST_LEN(1); + (*x86emu_optab2[op2])(op2); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x16 +****************************************************************************/ +void x86emuOp_push_SS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("PUSH\tSS\n"); + TRACE_AND_STEP(); + push_word(M.x86.R_SS); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x17 +****************************************************************************/ +void x86emuOp_pop_SS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("POP\tSS\n"); + TRACE_AND_STEP(); + M.x86.R_SS = pop_word(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x1e +****************************************************************************/ +void x86emuOp_push_DS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("PUSH\tDS\n"); + TRACE_AND_STEP(); + push_word(M.x86.R_DS); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x1f +****************************************************************************/ +void x86emuOp_pop_DS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("POP\tDS\n"); + TRACE_AND_STEP(); + M.x86.R_DS = pop_word(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x26 +****************************************************************************/ +void x86emuOp_segovr_ES(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("ES:\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_SEGOVR_ES; + /* + * note the lack of DECODE_CLEAR_SEGOVR(r) since, here is one of 4 + * opcode subroutines we do not want to do this. + */ + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x27 +****************************************************************************/ +void x86emuOp_daa(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("DAA\n"); + TRACE_AND_STEP(); + M.x86.R_AL = daa_byte(M.x86.R_AL); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x2e +****************************************************************************/ +void x86emuOp_segovr_CS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("CS:\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_SEGOVR_CS; + /* note no DECODE_CLEAR_SEGOVR here. */ + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x2f +****************************************************************************/ +void x86emuOp_das(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("DAS\n"); + TRACE_AND_STEP(); + M.x86.R_AL = das_byte(M.x86.R_AL); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x36 +****************************************************************************/ +void x86emuOp_segovr_SS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("SS:\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_SEGOVR_SS; + /* no DECODE_CLEAR_SEGOVR ! */ + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x37 +****************************************************************************/ +void x86emuOp_aaa(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("AAA\n"); + TRACE_AND_STEP(); + M.x86.R_AX = aaa_word(M.x86.R_AX); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x3e +****************************************************************************/ +void x86emuOp_segovr_DS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("DS:\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_SEGOVR_DS; + /* NO DECODE_CLEAR_SEGOVR! */ + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x3f +****************************************************************************/ +void x86emuOp_aas(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("AAS\n"); + TRACE_AND_STEP(); + M.x86.R_AX = aas_word(M.x86.R_AX); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x40 - 0x47 +****************************************************************************/ +void x86emuOp_inc_register(u8 op1) +{ + START_OF_INSTR(); + op1 &= 0x7; + DECODE_PRINTF("INC\t"); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *reg; + reg = DECODE_RM_LONG_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *reg = inc_long(*reg); + } else { + u16 *reg; + reg = DECODE_RM_WORD_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *reg = inc_word(*reg); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x48 - 0x4F +****************************************************************************/ +void x86emuOp_dec_register(u8 op1) +{ + START_OF_INSTR(); + op1 &= 0x7; + DECODE_PRINTF("DEC\t"); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *reg; + reg = DECODE_RM_LONG_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *reg = dec_long(*reg); + } else { + u16 *reg; + reg = DECODE_RM_WORD_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *reg = dec_word(*reg); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x50 - 0x57 +****************************************************************************/ +void x86emuOp_push_register(u8 op1) +{ + START_OF_INSTR(); + op1 &= 0x7; + DECODE_PRINTF("PUSH\t"); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *reg; + reg = DECODE_RM_LONG_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + push_long(*reg); + } else { + u16 *reg; + reg = DECODE_RM_WORD_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + push_word(*reg); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x58 - 0x5F +****************************************************************************/ +void x86emuOp_pop_register(u8 op1) +{ + START_OF_INSTR(); + op1 &= 0x7; + DECODE_PRINTF("POP\t"); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *reg; + reg = DECODE_RM_LONG_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *reg = pop_long(); + } else { + u16 *reg; + reg = DECODE_RM_WORD_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *reg = pop_word(); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x60 +****************************************************************************/ +void x86emuOp_push_all(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("PUSHAD\n"); + } else { + DECODE_PRINTF("PUSHA\n"); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 old_sp = M.x86.R_ESP; + + push_long(M.x86.R_EAX); + push_long(M.x86.R_ECX); + push_long(M.x86.R_EDX); + push_long(M.x86.R_EBX); + push_long(old_sp); + push_long(M.x86.R_EBP); + push_long(M.x86.R_ESI); + push_long(M.x86.R_EDI); + } else { + u16 old_sp = M.x86.R_SP; + + push_word(M.x86.R_AX); + push_word(M.x86.R_CX); + push_word(M.x86.R_DX); + push_word(M.x86.R_BX); + push_word(old_sp); + push_word(M.x86.R_BP); + push_word(M.x86.R_SI); + push_word(M.x86.R_DI); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x61 +****************************************************************************/ +void x86emuOp_pop_all(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("POPAD\n"); + } else { + DECODE_PRINTF("POPA\n"); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + M.x86.R_EDI = pop_long(); + M.x86.R_ESI = pop_long(); + M.x86.R_EBP = pop_long(); + M.x86.R_ESP += 4; /* skip ESP */ + M.x86.R_EBX = pop_long(); + M.x86.R_EDX = pop_long(); + M.x86.R_ECX = pop_long(); + M.x86.R_EAX = pop_long(); + } else { + M.x86.R_DI = pop_word(); + M.x86.R_SI = pop_word(); + M.x86.R_BP = pop_word(); + M.x86.R_SP += 2; /* skip SP */ + M.x86.R_BX = pop_word(); + M.x86.R_DX = pop_word(); + M.x86.R_CX = pop_word(); + M.x86.R_AX = pop_word(); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/*opcode 0x62 ILLEGAL OP, calls x86emuOp_illegal_op() */ +/*opcode 0x63 ILLEGAL OP, calls x86emuOp_illegal_op() */ + +/**************************************************************************** +REMARKS: +Handles opcode 0x64 +****************************************************************************/ +void x86emuOp_segovr_FS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("FS:\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_SEGOVR_FS; + /* + * note the lack of DECODE_CLEAR_SEGOVR(r) since, here is one of 4 + * opcode subroutines we do not want to do this. + */ + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x65 +****************************************************************************/ +void x86emuOp_segovr_GS(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("GS:\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_SEGOVR_GS; + /* + * note the lack of DECODE_CLEAR_SEGOVR(r) since, here is one of 4 + * opcode subroutines we do not want to do this. + */ + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x66 - prefix for 32-bit register +****************************************************************************/ +void x86emuOp_prefix_data(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("DATA:\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_PREFIX_DATA; + /* note no DECODE_CLEAR_SEGOVR here. */ + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x67 - prefix for 32-bit address +****************************************************************************/ +void x86emuOp_prefix_addr(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("ADDR:\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_PREFIX_ADDR; + /* note no DECODE_CLEAR_SEGOVR here. */ + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x68 +****************************************************************************/ +void x86emuOp_push_word_IMM(u8 X86EMU_UNUSED(op1)) +{ + u32 imm; + + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + imm = fetch_long_imm(); + } else { + imm = fetch_word_imm(); + } + DECODE_PRINTF2("PUSH\t%x\n", imm); + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + push_long(imm); + } else { + push_word((u16)imm); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x69 +****************************************************************************/ +void x86emuOp_imul_word_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("IMUL\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 srcval; + u32 res_lo,res_hi; + s32 imm; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcval = fetch_data_long(srcoffset); + imm = fetch_long_imm(); + DECODE_PRINTF2(",%d\n", (s32)imm); + TRACE_AND_STEP(); + imul_long_direct(&res_lo,&res_hi,(s32)srcval,(s32)imm); + if ((((res_lo & 0x80000000) == 0) && (res_hi == 0x00000000)) || + (((res_lo & 0x80000000) != 0) && (res_hi == 0xFFFFFFFF))) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } + *destreg = (u32)res_lo; + } else { + u16 *destreg; + u16 srcval; + u32 res; + s16 imm; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcval = fetch_data_word(srcoffset); + imm = fetch_word_imm(); + DECODE_PRINTF2(",%d\n", (s32)imm); + TRACE_AND_STEP(); + res = (s16)srcval * (s16)imm; + if ((((res & 0x8000) == 0) && ((res >> 16) == 0x0000)) || + (((res & 0x8000) != 0) && ((res >> 16) == 0xFFFF))) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } + *destreg = (u16)res; + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*srcreg; + u32 res_lo,res_hi; + s32 imm; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rl); + imm = fetch_long_imm(); + DECODE_PRINTF2(",%d\n", (s32)imm); + TRACE_AND_STEP(); + imul_long_direct(&res_lo,&res_hi,(s32)*srcreg,(s32)imm); + if ((((res_lo & 0x80000000) == 0) && (res_hi == 0x00000000)) || + (((res_lo & 0x80000000) != 0) && (res_hi == 0xFFFFFFFF))) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } + *destreg = (u32)res_lo; + } else { + u16 *destreg,*srcreg; + u32 res; + s16 imm; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rl); + imm = fetch_word_imm(); + DECODE_PRINTF2(",%d\n", (s32)imm); + res = (s16)*srcreg * (s16)imm; + if ((((res & 0x8000) == 0) && ((res >> 16) == 0x0000)) || + (((res & 0x8000) != 0) && ((res >> 16) == 0xFFFF))) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } + *destreg = (u16)res; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x6a +****************************************************************************/ +void x86emuOp_push_byte_IMM(u8 X86EMU_UNUSED(op1)) +{ + s16 imm; + + START_OF_INSTR(); + imm = (s8)fetch_byte_imm(); + DECODE_PRINTF2("PUSH\t%d\n", imm); + TRACE_AND_STEP(); + push_word(imm); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x6b +****************************************************************************/ +void x86emuOp_imul_byte_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint srcoffset; + s8 imm; + + START_OF_INSTR(); + DECODE_PRINTF("IMUL\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 srcval; + u32 res_lo,res_hi; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcval = fetch_data_long(srcoffset); + imm = fetch_byte_imm(); + DECODE_PRINTF2(",%d\n", (s32)imm); + TRACE_AND_STEP(); + imul_long_direct(&res_lo,&res_hi,(s32)srcval,(s32)imm); + if ((((res_lo & 0x80000000) == 0) && (res_hi == 0x00000000)) || + (((res_lo & 0x80000000) != 0) && (res_hi == 0xFFFFFFFF))) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } + *destreg = (u32)res_lo; + } else { + u16 *destreg; + u16 srcval; + u32 res; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcval = fetch_data_word(srcoffset); + imm = fetch_byte_imm(); + DECODE_PRINTF2(",%d\n", (s32)imm); + TRACE_AND_STEP(); + res = (s16)srcval * (s16)imm; + if ((((res & 0x8000) == 0) && ((res >> 16) == 0x0000)) || + (((res & 0x8000) != 0) && ((res >> 16) == 0xFFFF))) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } + *destreg = (u16)res; + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*srcreg; + u32 res_lo,res_hi; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rl); + imm = fetch_byte_imm(); + DECODE_PRINTF2(",%d\n", (s32)imm); + TRACE_AND_STEP(); + imul_long_direct(&res_lo,&res_hi,(s32)*srcreg,(s32)imm); + if ((((res_lo & 0x80000000) == 0) && (res_hi == 0x00000000)) || + (((res_lo & 0x80000000) != 0) && (res_hi == 0xFFFFFFFF))) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } + *destreg = (u32)res_lo; + } else { + u16 *destreg,*srcreg; + u32 res; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rl); + imm = fetch_byte_imm(); + DECODE_PRINTF2(",%d\n", (s32)imm); + TRACE_AND_STEP(); + res = (s16)*srcreg * (s16)imm; + if ((((res & 0x8000) == 0) && ((res >> 16) == 0x0000)) || + (((res & 0x8000) != 0) && ((res >> 16) == 0xFFFF))) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } + *destreg = (u16)res; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x6c +****************************************************************************/ +void x86emuOp_ins_byte(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("INSB\n"); + ins(1); + TRACE_AND_STEP(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x6d +****************************************************************************/ +void x86emuOp_ins_word(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("INSD\n"); + ins(4); + } else { + DECODE_PRINTF("INSW\n"); + ins(2); + } + TRACE_AND_STEP(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x6e +****************************************************************************/ +void x86emuOp_outs_byte(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("OUTSB\n"); + outs(1); + TRACE_AND_STEP(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x6f +****************************************************************************/ +void x86emuOp_outs_word(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("OUTSD\n"); + outs(4); + } else { + DECODE_PRINTF("OUTSW\n"); + outs(2); + } + TRACE_AND_STEP(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x70 - 0x7F +****************************************************************************/ +int x86emu_check_jump_condition(u8 op); + +void x86emuOp_jump_near_cond(u8 op1) +{ + s8 offset; + u16 target; + int cond; + + /* jump to byte offset if overflow flag is set */ + START_OF_INSTR(); + cond = x86emu_check_jump_condition(op1 & 0xF); + offset = (s8)fetch_byte_imm(); + target = (u16)(M.x86.R_IP + (s16)offset); + DECODE_PRINTF2("%x\n", target); + TRACE_AND_STEP(); + if (cond) + M.x86.R_IP = target; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x80 +****************************************************************************/ +void x86emuOp_opc80_byte_RM_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg; + uint destoffset; + u8 imm; + u8 destval; + + /* + * Weirdo special case instruction format. Part of the opcode + * held below in "RH". Doubly nested case would result, except + * that the decoded instruction + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + + switch (rh) { + case 0: + DECODE_PRINTF("ADD\t"); + break; + case 1: + DECODE_PRINTF("OR\t"); + break; + case 2: + DECODE_PRINTF("ADC\t"); + break; + case 3: + DECODE_PRINTF("SBB\t"); + break; + case 4: + DECODE_PRINTF("AND\t"); + break; + case 5: + DECODE_PRINTF("SUB\t"); + break; + case 6: + DECODE_PRINTF("XOR\t"); + break; + case 7: + DECODE_PRINTF("CMP\t"); + break; + } + } +#endif + /* know operation, decode the mod byte to find the addressing + mode. */ + if (mod < 3) { + DECODE_PRINTF("BYTE PTR "); + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + destval = fetch_data_byte(destoffset); + imm = fetch_byte_imm(); + DECODE_PRINTF2("%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_byte_operation[rh]) (destval, imm); + if (rh != 7) + store_data_byte(destoffset, destval); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF(","); + imm = fetch_byte_imm(); + DECODE_PRINTF2("%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_byte_operation[rh]) (*destreg, imm); + if (rh != 7) + *destreg = destval; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x81 +****************************************************************************/ +void x86emuOp_opc81_word_RM_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + + /* + * Weirdo special case instruction format. Part of the opcode + * held below in "RH". Doubly nested case would result, except + * that the decoded instruction + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + + switch (rh) { + case 0: + DECODE_PRINTF("ADD\t"); + break; + case 1: + DECODE_PRINTF("OR\t"); + break; + case 2: + DECODE_PRINTF("ADC\t"); + break; + case 3: + DECODE_PRINTF("SBB\t"); + break; + case 4: + DECODE_PRINTF("AND\t"); + break; + case 5: + DECODE_PRINTF("SUB\t"); + break; + case 6: + DECODE_PRINTF("XOR\t"); + break; + case 7: + DECODE_PRINTF("CMP\t"); + break; + } + } +#endif + /* + * Know operation, decode the mod byte to find the addressing + * mode. + */ + if (mod < 3) { + DECODE_PRINTF("DWORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval,imm; + + DECODE_PRINTF(","); + destval = fetch_data_long(destoffset); + imm = fetch_long_imm(); + DECODE_PRINTF2("%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_long_operation[rh]) (destval, imm); + if (rh != 7) + store_data_long(destoffset, destval); + } else { + u16 destval,imm; + + DECODE_PRINTF(","); + destval = fetch_data_word(destoffset); + imm = fetch_word_imm(); + DECODE_PRINTF2("%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_word_operation[rh]) (destval, imm); + if (rh != 7) + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 destval,imm; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + imm = fetch_long_imm(); + DECODE_PRINTF2("%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_long_operation[rh]) (*destreg, imm); + if (rh != 7) + *destreg = destval; + } else { + u16 *destreg; + u16 destval,imm; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + imm = fetch_word_imm(); + DECODE_PRINTF2("%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_word_operation[rh]) (*destreg, imm); + if (rh != 7) + *destreg = destval; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x82 +****************************************************************************/ +void x86emuOp_opc82_byte_RM_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg; + uint destoffset; + u8 imm; + u8 destval; + + /* + * Weirdo special case instruction format. Part of the opcode + * held below in "RH". Doubly nested case would result, except + * that the decoded instruction Similar to opcode 81, except that + * the immediate byte is sign extended to a word length. + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + switch (rh) { + case 0: + DECODE_PRINTF("ADD\t"); + break; + case 1: + DECODE_PRINTF("OR\t"); + break; + case 2: + DECODE_PRINTF("ADC\t"); + break; + case 3: + DECODE_PRINTF("SBB\t"); + break; + case 4: + DECODE_PRINTF("AND\t"); + break; + case 5: + DECODE_PRINTF("SUB\t"); + break; + case 6: + DECODE_PRINTF("XOR\t"); + break; + case 7: + DECODE_PRINTF("CMP\t"); + break; + } + } +#endif + /* know operation, decode the mod byte to find the addressing + mode. */ + if (mod < 3) { + DECODE_PRINTF("BYTE PTR "); + destoffset = decode_rmXX_address(mod, rl); + destval = fetch_data_byte(destoffset); + imm = fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_byte_operation[rh]) (destval, imm); + if (rh != 7) + store_data_byte(destoffset, destval); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + imm = fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_byte_operation[rh]) (*destreg, imm); + if (rh != 7) + *destreg = destval; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x83 +****************************************************************************/ +void x86emuOp_opc83_word_RM_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + + /* + * Weirdo special case instruction format. Part of the opcode + * held below in "RH". Doubly nested case would result, except + * that the decoded instruction Similar to opcode 81, except that + * the immediate byte is sign extended to a word length. + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + switch (rh) { + case 0: + DECODE_PRINTF("ADD\t"); + break; + case 1: + DECODE_PRINTF("OR\t"); + break; + case 2: + DECODE_PRINTF("ADC\t"); + break; + case 3: + DECODE_PRINTF("SBB\t"); + break; + case 4: + DECODE_PRINTF("AND\t"); + break; + case 5: + DECODE_PRINTF("SUB\t"); + break; + case 6: + DECODE_PRINTF("XOR\t"); + break; + case 7: + DECODE_PRINTF("CMP\t"); + break; + } + } +#endif + /* know operation, decode the mod byte to find the addressing + mode. */ + if (mod < 3) { + DECODE_PRINTF("DWORD PTR "); + destoffset = decode_rmXX_address(mod,rl); + + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval,imm; + + destval = fetch_data_long(destoffset); + imm = (s8) fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_long_operation[rh]) (destval, imm); + if (rh != 7) + store_data_long(destoffset, destval); + } else { + u16 destval,imm; + + destval = fetch_data_word(destoffset); + imm = (s8) fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_word_operation[rh]) (destval, imm); + if (rh != 7) + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 destval,imm; + + destreg = DECODE_RM_LONG_REGISTER(rl); + imm = (s8) fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_long_operation[rh]) (*destreg, imm); + if (rh != 7) + *destreg = destval; + } else { + u16 *destreg; + u16 destval,imm; + + destreg = DECODE_RM_WORD_REGISTER(rl); + imm = (s8) fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + destval = (*genop_word_operation[rh]) (*destreg, imm); + if (rh != 7) + *destreg = destval; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x84 +****************************************************************************/ +void x86emuOp_test_byte_RM_R(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg, *srcreg; + uint destoffset; + u8 destval; + + START_OF_INSTR(); + DECODE_PRINTF("TEST\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + destval = fetch_data_byte(destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + test_byte(destval, *srcreg); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + test_byte(*destreg, *srcreg); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x85 +****************************************************************************/ +void x86emuOp_test_word_RM_R(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + + START_OF_INSTR(); + DECODE_PRINTF("TEST\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + u32 *srcreg; + + DECODE_PRINTF(","); + destval = fetch_data_long(destoffset); + srcreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + test_long(destval, *srcreg); + } else { + u16 destval; + u16 *srcreg; + + DECODE_PRINTF(","); + destval = fetch_data_word(destoffset); + srcreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + test_word(destval, *srcreg); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*srcreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + test_long(*destreg, *srcreg); + } else { + u16 *destreg,*srcreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + test_word(*destreg, *srcreg); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x86 +****************************************************************************/ +void x86emuOp_xchg_byte_RM_R(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg, *srcreg; + uint destoffset; + u8 destval; + u8 tmp; + + START_OF_INSTR(); + DECODE_PRINTF("XCHG\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + destval = fetch_data_byte(destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + tmp = *srcreg; + *srcreg = destval; + destval = tmp; + store_data_byte(destoffset, destval); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + tmp = *srcreg; + *srcreg = *destreg; + *destreg = tmp; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x87 +****************************************************************************/ +void x86emuOp_xchg_word_RM_R(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + + START_OF_INSTR(); + DECODE_PRINTF("XCHG\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg; + u32 destval,tmp; + + destval = fetch_data_long(destoffset); + srcreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + tmp = *srcreg; + *srcreg = destval; + destval = tmp; + store_data_long(destoffset, destval); + } else { + u16 *srcreg; + u16 destval,tmp; + + destval = fetch_data_word(destoffset); + srcreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + tmp = *srcreg; + *srcreg = destval; + destval = tmp; + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*srcreg; + u32 tmp; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + tmp = *srcreg; + *srcreg = *destreg; + *destreg = tmp; + } else { + u16 *destreg,*srcreg; + u16 tmp; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + tmp = *srcreg; + *srcreg = *destreg; + *destreg = tmp; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x88 +****************************************************************************/ +void x86emuOp_mov_byte_RM_R(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg, *srcreg; + uint destoffset; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + store_data_byte(destoffset, *srcreg); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x89 +****************************************************************************/ +void x86emuOp_mov_word_RM_R(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg; + + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + store_data_long(destoffset, *srcreg); + } else { + u16 *srcreg; + + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + store_data_word(destoffset, *srcreg); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*srcreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } else { + u16 *destreg,*srcreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x8a +****************************************************************************/ +void x86emuOp_mov_byte_R_RM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg, *srcreg; + uint srcoffset; + u8 srcval; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = fetch_data_byte(srcoffset); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x8b +****************************************************************************/ +void x86emuOp_mov_word_R_RM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 srcval; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = fetch_data_long(srcoffset); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } else { + u16 *destreg; + u16 srcval; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = fetch_data_word(srcoffset); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg, *srcreg; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } else { + u16 *destreg, *srcreg; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x8c +****************************************************************************/ +void x86emuOp_mov_word_RM_SR(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u16 *destreg, *srcreg; + uint destoffset; + u16 destval; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + srcreg = decode_rm_seg_register(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = *srcreg; + store_data_word(destoffset, destval); + } else { /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + srcreg = decode_rm_seg_register(rh); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x8d +****************************************************************************/ +void x86emuOp_lea_word_R_M(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u16 *srcreg; + uint destoffset; + +/* + * TODO: Need to handle address size prefix! + * + * lea eax,[eax+ebx*2] ?? + */ + + START_OF_INSTR(); + DECODE_PRINTF("LEA\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *srcreg = (u16)destoffset; + } + /* } else { undefined. Do nothing. } */ + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x8e +****************************************************************************/ +void x86emuOp_mov_word_SR_RM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u16 *destreg, *srcreg; + uint srcoffset; + u16 srcval; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destreg = decode_rm_seg_register(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = fetch_data_word(srcoffset); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } else { /* register to register */ + destreg = decode_rm_seg_register(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } + /* + * Clean up, and reset all the R_xSP pointers to the correct + * locations. This is about 3x too much overhead (doing all the + * segreg ptrs when only one is needed, but this instruction + * *cannot* be that common, and this isn't too much work anyway. + */ + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x8f +****************************************************************************/ +void x86emuOp_pop_RM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + + START_OF_INSTR(); + DECODE_PRINTF("POP\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (rh != 0) { + DECODE_PRINTF("ILLEGAL DECODE OF OPCODE 8F\n"); + HALT_SYS(); + } + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = pop_long(); + store_data_long(destoffset, destval); + } else { + u16 destval; + + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = pop_word(); + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = pop_long(); + } else { + u16 *destreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = pop_word(); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x90 +****************************************************************************/ +void x86emuOp_nop(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("NOP\n"); + TRACE_AND_STEP(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x91-0x97 +****************************************************************************/ +void x86emuOp_xchg_word_AX_register(u8 X86EMU_UNUSED(op1)) +{ + u32 tmp; + + op1 &= 0x7; + + START_OF_INSTR(); + + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *reg32; + DECODE_PRINTF("XCHG\tEAX,"); + reg32 = DECODE_RM_LONG_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + tmp = M.x86.R_EAX; + M.x86.R_EAX = *reg32; + *reg32 = tmp; + } else { + u16 *reg16; + DECODE_PRINTF("XCHG\tAX,"); + reg16 = DECODE_RM_WORD_REGISTER(op1); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + tmp = M.x86.R_AX; + M.x86.R_EAX = *reg16; + *reg16 = (u16)tmp; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x98 +****************************************************************************/ +void x86emuOp_cbw(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("CWDE\n"); + } else { + DECODE_PRINTF("CBW\n"); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + if (M.x86.R_AX & 0x8000) { + M.x86.R_EAX |= 0xffff0000; + } else { + M.x86.R_EAX &= 0x0000ffff; + } + } else { + if (M.x86.R_AL & 0x80) { + M.x86.R_AH = 0xff; + } else { + M.x86.R_AH = 0x0; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x99 +****************************************************************************/ +void x86emuOp_cwd(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("CDQ\n"); + } else { + DECODE_PRINTF("CWD\n"); + } + DECODE_PRINTF("CWD\n"); + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + if (M.x86.R_EAX & 0x80000000) { + M.x86.R_EDX = 0xffffffff; + } else { + M.x86.R_EDX = 0x0; + } + } else { + if (M.x86.R_AX & 0x8000) { + M.x86.R_DX = 0xffff; + } else { + M.x86.R_DX = 0x0; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x9a +****************************************************************************/ +void x86emuOp_call_far_IMM(u8 X86EMU_UNUSED(op1)) +{ + u16 farseg, faroff; + + START_OF_INSTR(); + DECODE_PRINTF("CALL\t"); + faroff = fetch_word_imm(); + farseg = fetch_word_imm(); + DECODE_PRINTF2("%04x:", farseg); + DECODE_PRINTF2("%04x\n", faroff); + CALL_TRACE(M.x86.saved_cs, M.x86.saved_ip, farseg, faroff, "FAR "); + + /* XXX + * + * Hooked interrupt vectors calling into our "BIOS" will cause + * problems unless all intersegment stuff is checked for BIOS + * access. Check needed here. For moment, let it alone. + */ + TRACE_AND_STEP(); + push_word(M.x86.R_CS); + M.x86.R_CS = farseg; + push_word(M.x86.R_IP); + M.x86.R_IP = faroff; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x9b +****************************************************************************/ +void x86emuOp_wait(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("WAIT"); + TRACE_AND_STEP(); + /* NADA. */ + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x9c +****************************************************************************/ +void x86emuOp_pushf_word(u8 X86EMU_UNUSED(op1)) +{ + u32 flags; + + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("PUSHFD\n"); + } else { + DECODE_PRINTF("PUSHF\n"); + } + TRACE_AND_STEP(); + + /* clear out *all* bits not representing flags, and turn on real bits */ + flags = (M.x86.R_EFLG & F_MSK) | F_ALWAYS_ON; + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + push_long(flags); + } else { + push_word((u16)flags); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x9d +****************************************************************************/ +void x86emuOp_popf_word(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("POPFD\n"); + } else { + DECODE_PRINTF("POPF\n"); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + M.x86.R_EFLG = pop_long(); + } else { + M.x86.R_FLG = pop_word(); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x9e +****************************************************************************/ +void x86emuOp_sahf(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("SAHF\n"); + TRACE_AND_STEP(); + /* clear the lower bits of the flag register */ + M.x86.R_FLG &= 0xffffff00; + /* or in the AH register into the flags register */ + M.x86.R_FLG |= M.x86.R_AH; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x9f +****************************************************************************/ +void x86emuOp_lahf(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("LAHF\n"); + TRACE_AND_STEP(); + M.x86.R_AH = (u8)(M.x86.R_FLG & 0xff); + /*undocumented TC++ behavior??? Nope. It's documented, but + you have too look real hard to notice it. */ + M.x86.R_AH |= 0x2; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa0 +****************************************************************************/ +void x86emuOp_mov_AL_M_IMM(u8 X86EMU_UNUSED(op1)) +{ + u16 offset; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\tAL,"); + offset = fetch_word_imm(); + DECODE_PRINTF2("[%04x]\n", offset); + TRACE_AND_STEP(); + M.x86.R_AL = fetch_data_byte(offset); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa1 +****************************************************************************/ +void x86emuOp_mov_AX_M_IMM(u8 X86EMU_UNUSED(op1)) +{ + u16 offset; + + START_OF_INSTR(); + offset = fetch_word_imm(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF2("MOV\tEAX,[%04x]\n", offset); + } else { + DECODE_PRINTF2("MOV\tAX,[%04x]\n", offset); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + M.x86.R_EAX = fetch_data_long(offset); + } else { + M.x86.R_AX = fetch_data_word(offset); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa2 +****************************************************************************/ +void x86emuOp_mov_M_AL_IMM(u8 X86EMU_UNUSED(op1)) +{ + u16 offset; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + offset = fetch_word_imm(); + DECODE_PRINTF2("[%04x],AL\n", offset); + TRACE_AND_STEP(); + store_data_byte(offset, M.x86.R_AL); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa3 +****************************************************************************/ +void x86emuOp_mov_M_AX_IMM(u8 X86EMU_UNUSED(op1)) +{ + u16 offset; + + START_OF_INSTR(); + offset = fetch_word_imm(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF2("MOV\t[%04x],EAX\n", offset); + } else { + DECODE_PRINTF2("MOV\t[%04x],AX\n", offset); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + store_data_long(offset, M.x86.R_EAX); + } else { + store_data_word(offset, M.x86.R_AX); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa4 +****************************************************************************/ +void x86emuOp_movs_byte(u8 X86EMU_UNUSED(op1)) +{ + u8 val; + u32 count; + int inc; + + START_OF_INSTR(); + DECODE_PRINTF("MOVS\tBYTE\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -1; + else + inc = 1; + TRACE_AND_STEP(); + count = 1; + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + count = M.x86.R_CX; + M.x86.R_CX = 0; + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + while (count--) { + val = fetch_data_byte(M.x86.R_SI); + store_data_byte_abs(M.x86.R_ES, M.x86.R_DI, val); + M.x86.R_SI += inc; + M.x86.R_DI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa5 +****************************************************************************/ +void x86emuOp_movs_word(u8 X86EMU_UNUSED(op1)) +{ + u32 val; + int inc; + u32 count; + + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("MOVS\tDWORD\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -4; + else + inc = 4; + } else { + DECODE_PRINTF("MOVS\tWORD\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -2; + else + inc = 2; + } + TRACE_AND_STEP(); + count = 1; + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + count = M.x86.R_CX; + M.x86.R_CX = 0; + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + while (count--) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + val = fetch_data_long(M.x86.R_SI); + store_data_long_abs(M.x86.R_ES, M.x86.R_DI, val); + } else { + val = fetch_data_word(M.x86.R_SI); + store_data_word_abs(M.x86.R_ES, M.x86.R_DI, (u16)val); + } + M.x86.R_SI += inc; + M.x86.R_DI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa6 +****************************************************************************/ +void x86emuOp_cmps_byte(u8 X86EMU_UNUSED(op1)) +{ + s8 val1, val2; + int inc; + + START_OF_INSTR(); + DECODE_PRINTF("CMPS\tBYTE\n"); + TRACE_AND_STEP(); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -1; + else + inc = 1; + + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* REPE */ + /* move them until CX is ZERO. */ + while (M.x86.R_CX != 0) { + val1 = fetch_data_byte(M.x86.R_SI); + val2 = fetch_data_byte_abs(M.x86.R_ES, M.x86.R_DI); + cmp_byte(val1, val2); + M.x86.R_CX -= 1; + M.x86.R_SI += inc; + M.x86.R_DI += inc; + if ( (M.x86.mode & SYSMODE_PREFIX_REPE) && (ACCESS_FLAG(F_ZF) == 0) ) break; + if ( (M.x86.mode & SYSMODE_PREFIX_REPNE) && ACCESS_FLAG(F_ZF) ) break; + } + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + val1 = fetch_data_byte(M.x86.R_SI); + val2 = fetch_data_byte_abs(M.x86.R_ES, M.x86.R_DI); + cmp_byte(val1, val2); + M.x86.R_SI += inc; + M.x86.R_DI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa7 +****************************************************************************/ +void x86emuOp_cmps_word(u8 X86EMU_UNUSED(op1)) +{ + u32 val1,val2; + int inc; + + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("CMPS\tDWORD\n"); + inc = 4; + } else { + DECODE_PRINTF("CMPS\tWORD\n"); + inc = 2; + } + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -inc; + + TRACE_AND_STEP(); + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* REPE */ + /* move them until CX is ZERO. */ + while (M.x86.R_CX != 0) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + val1 = fetch_data_long(M.x86.R_SI); + val2 = fetch_data_long_abs(M.x86.R_ES, M.x86.R_DI); + cmp_long(val1, val2); + } else { + val1 = fetch_data_word(M.x86.R_SI); + val2 = fetch_data_word_abs(M.x86.R_ES, M.x86.R_DI); + cmp_word((u16)val1, (u16)val2); + } + M.x86.R_CX -= 1; + M.x86.R_SI += inc; + M.x86.R_DI += inc; + if ( (M.x86.mode & SYSMODE_PREFIX_REPE) && ACCESS_FLAG(F_ZF) == 0 ) break; + if ( (M.x86.mode & SYSMODE_PREFIX_REPNE) && ACCESS_FLAG(F_ZF) ) break; + } + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + val1 = fetch_data_long(M.x86.R_SI); + val2 = fetch_data_long_abs(M.x86.R_ES, M.x86.R_DI); + cmp_long(val1, val2); + } else { + val1 = fetch_data_word(M.x86.R_SI); + val2 = fetch_data_word_abs(M.x86.R_ES, M.x86.R_DI); + cmp_word((u16)val1, (u16)val2); + } + M.x86.R_SI += inc; + M.x86.R_DI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa8 +****************************************************************************/ +void x86emuOp_test_AL_IMM(u8 X86EMU_UNUSED(op1)) +{ + int imm; + + START_OF_INSTR(); + DECODE_PRINTF("TEST\tAL,"); + imm = fetch_byte_imm(); + DECODE_PRINTF2("%04x\n", imm); + TRACE_AND_STEP(); + test_byte(M.x86.R_AL, (u8)imm); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xa9 +****************************************************************************/ +void x86emuOp_test_AX_IMM(u8 X86EMU_UNUSED(op1)) +{ + u32 srcval; + + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("TEST\tEAX,"); + srcval = fetch_long_imm(); + } else { + DECODE_PRINTF("TEST\tAX,"); + srcval = fetch_word_imm(); + } + DECODE_PRINTF2("%x\n", srcval); + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + test_long(M.x86.R_EAX, srcval); + } else { + test_word(M.x86.R_AX, (u16)srcval); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xaa +****************************************************************************/ +void x86emuOp_stos_byte(u8 X86EMU_UNUSED(op1)) +{ + int inc; + + START_OF_INSTR(); + DECODE_PRINTF("STOS\tBYTE\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -1; + else + inc = 1; + TRACE_AND_STEP(); + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + while (M.x86.R_CX != 0) { + store_data_byte_abs(M.x86.R_ES, M.x86.R_DI, M.x86.R_AL); + M.x86.R_CX -= 1; + M.x86.R_DI += inc; + } + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + store_data_byte_abs(M.x86.R_ES, M.x86.R_DI, M.x86.R_AL); + M.x86.R_DI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xab +****************************************************************************/ +void x86emuOp_stos_word(u8 X86EMU_UNUSED(op1)) +{ + int inc; + u32 count; + + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("STOS\tDWORD\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -4; + else + inc = 4; + } else { + DECODE_PRINTF("STOS\tWORD\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -2; + else + inc = 2; + } + TRACE_AND_STEP(); + count = 1; + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + count = M.x86.R_CX; + M.x86.R_CX = 0; + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + while (count--) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + store_data_long_abs(M.x86.R_ES, M.x86.R_DI, M.x86.R_EAX); + } else { + store_data_word_abs(M.x86.R_ES, M.x86.R_DI, M.x86.R_AX); + } + M.x86.R_DI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xac +****************************************************************************/ +void x86emuOp_lods_byte(u8 X86EMU_UNUSED(op1)) +{ + int inc; + + START_OF_INSTR(); + DECODE_PRINTF("LODS\tBYTE\n"); + TRACE_AND_STEP(); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -1; + else + inc = 1; + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + while (M.x86.R_CX != 0) { + M.x86.R_AL = fetch_data_byte(M.x86.R_SI); + M.x86.R_CX -= 1; + M.x86.R_SI += inc; + } + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + M.x86.R_AL = fetch_data_byte(M.x86.R_SI); + M.x86.R_SI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xad +****************************************************************************/ +void x86emuOp_lods_word(u8 X86EMU_UNUSED(op1)) +{ + int inc; + u32 count; + + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("LODS\tDWORD\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -4; + else + inc = 4; + } else { + DECODE_PRINTF("LODS\tWORD\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -2; + else + inc = 2; + } + TRACE_AND_STEP(); + count = 1; + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + count = M.x86.R_CX; + M.x86.R_CX = 0; + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + while (count--) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + M.x86.R_EAX = fetch_data_long(M.x86.R_SI); + } else { + M.x86.R_AX = fetch_data_word(M.x86.R_SI); + } + M.x86.R_SI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xae +****************************************************************************/ +void x86emuOp_scas_byte(u8 X86EMU_UNUSED(op1)) +{ + s8 val2; + int inc; + + START_OF_INSTR(); + DECODE_PRINTF("SCAS\tBYTE\n"); + TRACE_AND_STEP(); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -1; + else + inc = 1; + if (M.x86.mode & SYSMODE_PREFIX_REPE) { + /* REPE */ + /* move them until CX is ZERO. */ + while (M.x86.R_CX != 0) { + val2 = fetch_data_byte_abs(M.x86.R_ES, M.x86.R_DI); + cmp_byte(M.x86.R_AL, val2); + M.x86.R_CX -= 1; + M.x86.R_DI += inc; + if (ACCESS_FLAG(F_ZF) == 0) + break; + } + M.x86.mode &= ~SYSMODE_PREFIX_REPE; + } else if (M.x86.mode & SYSMODE_PREFIX_REPNE) { + /* REPNE */ + /* move them until CX is ZERO. */ + while (M.x86.R_CX != 0) { + val2 = fetch_data_byte_abs(M.x86.R_ES, M.x86.R_DI); + cmp_byte(M.x86.R_AL, val2); + M.x86.R_CX -= 1; + M.x86.R_DI += inc; + if (ACCESS_FLAG(F_ZF)) + break; /* zero flag set means equal */ + } + M.x86.mode &= ~SYSMODE_PREFIX_REPNE; + } else { + val2 = fetch_data_byte_abs(M.x86.R_ES, M.x86.R_DI); + cmp_byte(M.x86.R_AL, val2); + M.x86.R_DI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xaf +****************************************************************************/ +void x86emuOp_scas_word(u8 X86EMU_UNUSED(op1)) +{ + int inc; + u32 val; + + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("SCAS\tDWORD\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -4; + else + inc = 4; + } else { + DECODE_PRINTF("SCAS\tWORD\n"); + if (ACCESS_FLAG(F_DF)) /* down */ + inc = -2; + else + inc = 2; + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_REPE) { + /* REPE */ + /* move them until CX is ZERO. */ + while (M.x86.R_CX != 0) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + val = fetch_data_long_abs(M.x86.R_ES, M.x86.R_DI); + cmp_long(M.x86.R_EAX, val); + } else { + val = fetch_data_word_abs(M.x86.R_ES, M.x86.R_DI); + cmp_word(M.x86.R_AX, (u16)val); + } + M.x86.R_CX -= 1; + M.x86.R_DI += inc; + if (ACCESS_FLAG(F_ZF) == 0) + break; + } + M.x86.mode &= ~SYSMODE_PREFIX_REPE; + } else if (M.x86.mode & SYSMODE_PREFIX_REPNE) { + /* REPNE */ + /* move them until CX is ZERO. */ + while (M.x86.R_CX != 0) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + val = fetch_data_long_abs(M.x86.R_ES, M.x86.R_DI); + cmp_long(M.x86.R_EAX, val); + } else { + val = fetch_data_word_abs(M.x86.R_ES, M.x86.R_DI); + cmp_word(M.x86.R_AX, (u16)val); + } + M.x86.R_CX -= 1; + M.x86.R_DI += inc; + if (ACCESS_FLAG(F_ZF)) + break; /* zero flag set means equal */ + } + M.x86.mode &= ~SYSMODE_PREFIX_REPNE; + } else { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + val = fetch_data_long_abs(M.x86.R_ES, M.x86.R_DI); + cmp_long(M.x86.R_EAX, val); + } else { + val = fetch_data_word_abs(M.x86.R_ES, M.x86.R_DI); + cmp_word(M.x86.R_AX, (u16)val); + } + M.x86.R_DI += inc; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xb0 - 0xb7 +****************************************************************************/ +void x86emuOp_mov_byte_register_IMM(u8 op1) +{ + u8 imm, *ptr; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + ptr = DECODE_RM_BYTE_REGISTER(op1 & 0x7); + DECODE_PRINTF(","); + imm = fetch_byte_imm(); + DECODE_PRINTF2("%x\n", imm); + TRACE_AND_STEP(); + *ptr = imm; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xb8 - 0xbf +****************************************************************************/ +void x86emuOp_mov_word_register_IMM(u8 X86EMU_UNUSED(op1)) +{ + u32 srcval; + + op1 &= 0x7; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *reg32; + reg32 = DECODE_RM_LONG_REGISTER(op1); + srcval = fetch_long_imm(); + DECODE_PRINTF2(",%x\n", srcval); + TRACE_AND_STEP(); + *reg32 = srcval; + } else { + u16 *reg16; + reg16 = DECODE_RM_WORD_REGISTER(op1); + srcval = fetch_word_imm(); + DECODE_PRINTF2(",%x\n", srcval); + TRACE_AND_STEP(); + *reg16 = (u16)srcval; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc0 +****************************************************************************/ +void x86emuOp_opcC0_byte_RM_MEM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg; + uint destoffset; + u8 destval; + u8 amt; + + /* + * Yet another weirdo special case instruction format. Part of + * the opcode held below in "RH". Doubly nested case would + * result, except that the decoded instruction + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + + switch (rh) { + case 0: + DECODE_PRINTF("ROL\t"); + break; + case 1: + DECODE_PRINTF("ROR\t"); + break; + case 2: + DECODE_PRINTF("RCL\t"); + break; + case 3: + DECODE_PRINTF("RCR\t"); + break; + case 4: + DECODE_PRINTF("SHL\t"); + break; + case 5: + DECODE_PRINTF("SHR\t"); + break; + case 6: + DECODE_PRINTF("SAL\t"); + break; + case 7: + DECODE_PRINTF("SAR\t"); + break; + } + } +#endif + /* know operation, decode the mod byte to find the addressing + mode. */ + if (mod < 3) { + DECODE_PRINTF("BYTE PTR "); + destoffset = decode_rmXX_address(mod, rl); + amt = fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", amt); + destval = fetch_data_byte(destoffset); + TRACE_AND_STEP(); + destval = (*opcD0_byte_operation[rh]) (destval, amt); + store_data_byte(destoffset, destval); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + amt = fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", amt); + TRACE_AND_STEP(); + destval = (*opcD0_byte_operation[rh]) (*destreg, amt); + *destreg = destval; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc1 +****************************************************************************/ +void x86emuOp_opcC1_word_RM_MEM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + u8 amt; + + /* + * Yet another weirdo special case instruction format. Part of + * the opcode held below in "RH". Doubly nested case would + * result, except that the decoded instruction + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + + switch (rh) { + case 0: + DECODE_PRINTF("ROL\t"); + break; + case 1: + DECODE_PRINTF("ROR\t"); + break; + case 2: + DECODE_PRINTF("RCL\t"); + break; + case 3: + DECODE_PRINTF("RCR\t"); + break; + case 4: + DECODE_PRINTF("SHL\t"); + break; + case 5: + DECODE_PRINTF("SHR\t"); + break; + case 6: + DECODE_PRINTF("SAL\t"); + break; + case 7: + DECODE_PRINTF("SAR\t"); + break; + } + } +#endif + /* know operation, decode the mod byte to find the addressing + mode. */ + if (mod < 3) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + + DECODE_PRINTF("DWORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + amt = fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", amt); + destval = fetch_data_long(destoffset); + TRACE_AND_STEP(); + destval = (*opcD1_long_operation[rh]) (destval, amt); + store_data_long(destoffset, destval); + } else { + u16 destval; + + DECODE_PRINTF("WORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + amt = fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", amt); + destval = fetch_data_word(destoffset); + TRACE_AND_STEP(); + destval = (*opcD1_word_operation[rh]) (destval, amt); + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + amt = fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", amt); + TRACE_AND_STEP(); + *destreg = (*opcD1_long_operation[rh]) (*destreg, amt); + } else { + u16 *destreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + amt = fetch_byte_imm(); + DECODE_PRINTF2(",%x\n", amt); + TRACE_AND_STEP(); + *destreg = (*opcD1_word_operation[rh]) (*destreg, amt); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc2 +****************************************************************************/ +void x86emuOp_ret_near_IMM(u8 X86EMU_UNUSED(op1)) +{ + u16 imm; + + START_OF_INSTR(); + DECODE_PRINTF("RET\t"); + imm = fetch_word_imm(); + DECODE_PRINTF2("%x\n", imm); + RETURN_TRACE("RET",M.x86.saved_cs,M.x86.saved_ip); + TRACE_AND_STEP(); + M.x86.R_IP = pop_word(); + M.x86.R_SP += imm; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc3 +****************************************************************************/ +void x86emuOp_ret_near(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("RET\n"); + RETURN_TRACE("RET",M.x86.saved_cs,M.x86.saved_ip); + TRACE_AND_STEP(); + M.x86.R_IP = pop_word(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc4 +****************************************************************************/ +void x86emuOp_les_R_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rh, rl; + u16 *dstreg; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("LES\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + dstreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *dstreg = fetch_data_word(srcoffset); + M.x86.R_ES = fetch_data_word(srcoffset + 2); + } + /* else UNDEFINED! register to register */ + + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc5 +****************************************************************************/ +void x86emuOp_lds_R_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rh, rl; + u16 *dstreg; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("LDS\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + dstreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *dstreg = fetch_data_word(srcoffset); + M.x86.R_DS = fetch_data_word(srcoffset + 2); + } + /* else UNDEFINED! */ + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc6 +****************************************************************************/ +void x86emuOp_mov_byte_RM_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg; + uint destoffset; + u8 imm; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (rh != 0) { + DECODE_PRINTF("ILLEGAL DECODE OF OPCODE c6\n"); + HALT_SYS(); + } + if (mod < 3) { + DECODE_PRINTF("BYTE PTR "); + destoffset = decode_rmXX_address(mod, rl); + imm = fetch_byte_imm(); + DECODE_PRINTF2(",%2x\n", imm); + TRACE_AND_STEP(); + store_data_byte(destoffset, imm); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + imm = fetch_byte_imm(); + DECODE_PRINTF2(",%2x\n", imm); + TRACE_AND_STEP(); + *destreg = imm; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc7 +****************************************************************************/ +void x86emuOp_mov_word_RM_IMM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + + START_OF_INSTR(); + DECODE_PRINTF("MOV\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (rh != 0) { + DECODE_PRINTF("ILLEGAL DECODE OF OPCODE 8F\n"); + HALT_SYS(); + } + if (mod < 3) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 imm; + + DECODE_PRINTF("DWORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + imm = fetch_long_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + store_data_long(destoffset, imm); + } else { + u16 imm; + + DECODE_PRINTF("WORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + imm = fetch_word_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + store_data_word(destoffset, imm); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 imm; + + destreg = DECODE_RM_LONG_REGISTER(rl); + imm = fetch_long_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + *destreg = imm; + } else { + u16 *destreg; + u16 imm; + + destreg = DECODE_RM_WORD_REGISTER(rl); + imm = fetch_word_imm(); + DECODE_PRINTF2(",%x\n", imm); + TRACE_AND_STEP(); + *destreg = imm; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc8 +****************************************************************************/ +void x86emuOp_enter(u8 X86EMU_UNUSED(op1)) +{ + u16 local,frame_pointer; + u8 nesting; + int i; + + START_OF_INSTR(); + local = fetch_word_imm(); + nesting = fetch_byte_imm(); + DECODE_PRINTF2("ENTER %x\n", local); + DECODE_PRINTF2(",%x\n", nesting); + TRACE_AND_STEP(); + push_word(M.x86.R_BP); + frame_pointer = M.x86.R_SP; + if (nesting > 0) { + for (i = 1; i < nesting; i++) { + M.x86.R_BP -= 2; + push_word(fetch_data_word_abs(M.x86.R_SS, M.x86.R_BP)); + } + push_word(frame_pointer); + } + M.x86.R_BP = frame_pointer; + M.x86.R_SP = (u16)(M.x86.R_SP - local); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xc9 +****************************************************************************/ +void x86emuOp_leave(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("LEAVE\n"); + TRACE_AND_STEP(); + M.x86.R_SP = M.x86.R_BP; + M.x86.R_BP = pop_word(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xca +****************************************************************************/ +void x86emuOp_ret_far_IMM(u8 X86EMU_UNUSED(op1)) +{ + u16 imm; + + START_OF_INSTR(); + DECODE_PRINTF("RETF\t"); + imm = fetch_word_imm(); + DECODE_PRINTF2("%x\n", imm); + RETURN_TRACE("RETF",M.x86.saved_cs,M.x86.saved_ip); + TRACE_AND_STEP(); + M.x86.R_IP = pop_word(); + M.x86.R_CS = pop_word(); + M.x86.R_SP += imm; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xcb +****************************************************************************/ +void x86emuOp_ret_far(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("RETF\n"); + RETURN_TRACE("RETF",M.x86.saved_cs,M.x86.saved_ip); + TRACE_AND_STEP(); + M.x86.R_IP = pop_word(); + M.x86.R_CS = pop_word(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xcc +****************************************************************************/ +void x86emuOp_int3(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("INT 3\n"); + (void)mem_access_word(3 * 4 + 2); + /* access the segment register */ + TRACE_AND_STEP(); + if (_X86EMU_intrTab[3]) { + (*_X86EMU_intrTab[3])(3); + } else { + push_word((u16)M.x86.R_FLG); + CLEAR_FLAG(F_IF); + CLEAR_FLAG(F_TF); + push_word(M.x86.R_CS); + M.x86.R_CS = mem_access_word(3 * 4 + 2); + push_word(M.x86.R_IP); + M.x86.R_IP = mem_access_word(3 * 4); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xcd +****************************************************************************/ +void x86emuOp_int_IMM(u8 X86EMU_UNUSED(op1)) +{ + u8 intnum; + + START_OF_INSTR(); + DECODE_PRINTF("INT\t"); + intnum = fetch_byte_imm(); + DECODE_PRINTF2("%x\n", intnum); + (void)mem_access_word(intnum * 4 + 2); + TRACE_AND_STEP(); + if (_X86EMU_intrTab[intnum]) { + (*_X86EMU_intrTab[intnum])(intnum); + } else { + push_word((u16)M.x86.R_FLG); + CLEAR_FLAG(F_IF); + CLEAR_FLAG(F_TF); + push_word(M.x86.R_CS); + M.x86.R_CS = mem_access_word(intnum * 4 + 2); + push_word(M.x86.R_IP); + M.x86.R_IP = mem_access_word(intnum * 4); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xce +****************************************************************************/ +void x86emuOp_into(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("INTO\n"); + TRACE_AND_STEP(); + if (ACCESS_FLAG(F_OF)) { + (void)mem_access_word(4 * 4 + 2); + if (_X86EMU_intrTab[4]) { + (*_X86EMU_intrTab[4])(4); + } else { + push_word((u16)M.x86.R_FLG); + CLEAR_FLAG(F_IF); + CLEAR_FLAG(F_TF); + push_word(M.x86.R_CS); + M.x86.R_CS = mem_access_word(4 * 4 + 2); + push_word(M.x86.R_IP); + M.x86.R_IP = mem_access_word(4 * 4); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xcf +****************************************************************************/ +void x86emuOp_iret(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("IRET\n"); + + TRACE_AND_STEP(); + + M.x86.R_IP = pop_word(); + M.x86.R_CS = pop_word(); + M.x86.R_FLG = pop_word(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xd0 +****************************************************************************/ +void x86emuOp_opcD0_byte_RM_1(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg; + uint destoffset; + u8 destval; + + /* + * Yet another weirdo special case instruction format. Part of + * the opcode held below in "RH". Doubly nested case would + * result, except that the decoded instruction + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + switch (rh) { + case 0: + DECODE_PRINTF("ROL\t"); + break; + case 1: + DECODE_PRINTF("ROR\t"); + break; + case 2: + DECODE_PRINTF("RCL\t"); + break; + case 3: + DECODE_PRINTF("RCR\t"); + break; + case 4: + DECODE_PRINTF("SHL\t"); + break; + case 5: + DECODE_PRINTF("SHR\t"); + break; + case 6: + DECODE_PRINTF("SAL\t"); + break; + case 7: + DECODE_PRINTF("SAR\t"); + break; + } + } +#endif + /* know operation, decode the mod byte to find the addressing + mode. */ + if (mod < 3) { + DECODE_PRINTF("BYTE PTR "); + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(",1\n"); + destval = fetch_data_byte(destoffset); + TRACE_AND_STEP(); + destval = (*opcD0_byte_operation[rh]) (destval, 1); + store_data_byte(destoffset, destval); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF(",1\n"); + TRACE_AND_STEP(); + destval = (*opcD0_byte_operation[rh]) (*destreg, 1); + *destreg = destval; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xd1 +****************************************************************************/ +void x86emuOp_opcD1_word_RM_1(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + + /* + * Yet another weirdo special case instruction format. Part of + * the opcode held below in "RH". Doubly nested case would + * result, except that the decoded instruction + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + switch (rh) { + case 0: + DECODE_PRINTF("ROL\t"); + break; + case 1: + DECODE_PRINTF("ROR\t"); + break; + case 2: + DECODE_PRINTF("RCL\t"); + break; + case 3: + DECODE_PRINTF("RCR\t"); + break; + case 4: + DECODE_PRINTF("SHL\t"); + break; + case 5: + DECODE_PRINTF("SHR\t"); + break; + case 6: + DECODE_PRINTF("SAL\t"); + break; + case 7: + DECODE_PRINTF("SAR\t"); + break; + } + } +#endif + /* know operation, decode the mod byte to find the addressing + mode. */ + if (mod < 3) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + + DECODE_PRINTF("DWORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(",1\n"); + destval = fetch_data_long(destoffset); + TRACE_AND_STEP(); + destval = (*opcD1_long_operation[rh]) (destval, 1); + store_data_long(destoffset, destval); + } else { + u16 destval; + + DECODE_PRINTF("WORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(",1\n"); + destval = fetch_data_word(destoffset); + TRACE_AND_STEP(); + destval = (*opcD1_word_operation[rh]) (destval, 1); + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + u32 *destreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(",1\n"); + TRACE_AND_STEP(); + destval = (*opcD1_long_operation[rh]) (*destreg, 1); + *destreg = destval; + } else { + u16 destval; + u16 *destreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(",1\n"); + TRACE_AND_STEP(); + destval = (*opcD1_word_operation[rh]) (*destreg, 1); + *destreg = destval; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xd2 +****************************************************************************/ +void x86emuOp_opcD2_byte_RM_CL(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg; + uint destoffset; + u8 destval; + u8 amt; + + /* + * Yet another weirdo special case instruction format. Part of + * the opcode held below in "RH". Doubly nested case would + * result, except that the decoded instruction + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + switch (rh) { + case 0: + DECODE_PRINTF("ROL\t"); + break; + case 1: + DECODE_PRINTF("ROR\t"); + break; + case 2: + DECODE_PRINTF("RCL\t"); + break; + case 3: + DECODE_PRINTF("RCR\t"); + break; + case 4: + DECODE_PRINTF("SHL\t"); + break; + case 5: + DECODE_PRINTF("SHR\t"); + break; + case 6: + DECODE_PRINTF("SAL\t"); + break; + case 7: + DECODE_PRINTF("SAR\t"); + break; + } + } +#endif + /* know operation, decode the mod byte to find the addressing + mode. */ + amt = M.x86.R_CL; + if (mod < 3) { + DECODE_PRINTF("BYTE PTR "); + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(",CL\n"); + destval = fetch_data_byte(destoffset); + TRACE_AND_STEP(); + destval = (*opcD0_byte_operation[rh]) (destval, amt); + store_data_byte(destoffset, destval); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + destval = (*opcD0_byte_operation[rh]) (*destreg, amt); + *destreg = destval; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xd3 +****************************************************************************/ +void x86emuOp_opcD3_word_RM_CL(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + u8 amt; + + /* + * Yet another weirdo special case instruction format. Part of + * the opcode held below in "RH". Doubly nested case would + * result, except that the decoded instruction + */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + switch (rh) { + case 0: + DECODE_PRINTF("ROL\t"); + break; + case 1: + DECODE_PRINTF("ROR\t"); + break; + case 2: + DECODE_PRINTF("RCL\t"); + break; + case 3: + DECODE_PRINTF("RCR\t"); + break; + case 4: + DECODE_PRINTF("SHL\t"); + break; + case 5: + DECODE_PRINTF("SHR\t"); + break; + case 6: + DECODE_PRINTF("SAL\t"); + break; + case 7: + DECODE_PRINTF("SAR\t"); + break; + } + } +#endif + /* know operation, decode the mod byte to find the addressing + mode. */ + amt = M.x86.R_CL; + if (mod < 3) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + + DECODE_PRINTF("DWORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(",CL\n"); + destval = fetch_data_long(destoffset); + TRACE_AND_STEP(); + destval = (*opcD1_long_operation[rh]) (destval, amt); + store_data_long(destoffset, destval); + } else { + u16 destval; + + DECODE_PRINTF("WORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(",CL\n"); + destval = fetch_data_word(destoffset); + TRACE_AND_STEP(); + destval = (*opcD1_word_operation[rh]) (destval, amt); + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + *destreg = (*opcD1_long_operation[rh]) (*destreg, amt); + } else { + u16 *destreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + *destreg = (*opcD1_word_operation[rh]) (*destreg, amt); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xd4 +****************************************************************************/ +void x86emuOp_aam(u8 X86EMU_UNUSED(op1)) +{ + u8 a; + + START_OF_INSTR(); + DECODE_PRINTF("AAM\n"); + a = fetch_byte_imm(); /* this is a stupid encoding. */ + if (a != 10) { + DECODE_PRINTF("ERROR DECODING AAM\n"); + TRACE_REGS(); + HALT_SYS(); + } + TRACE_AND_STEP(); + /* note the type change here --- returning AL and AH in AX. */ + M.x86.R_AX = aam_word(M.x86.R_AL); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xd5 +****************************************************************************/ +void x86emuOp_aad(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("AAD\n"); + (void)fetch_byte_imm(); + TRACE_AND_STEP(); + M.x86.R_AX = aad_word(M.x86.R_AX); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/* opcode 0xd6 ILLEGAL OPCODE */ + +/**************************************************************************** +REMARKS: +Handles opcode 0xd7 +****************************************************************************/ +void x86emuOp_xlat(u8 X86EMU_UNUSED(op1)) +{ + u16 addr; + + START_OF_INSTR(); + DECODE_PRINTF("XLAT\n"); + TRACE_AND_STEP(); + addr = (u16)(M.x86.R_BX + (u8)M.x86.R_AL); + M.x86.R_AL = fetch_data_byte(addr); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/* instuctions D8 .. DF are in i87_ops.c */ + +/**************************************************************************** +REMARKS: +Handles opcode 0xe0 +****************************************************************************/ +void x86emuOp_loopne(u8 X86EMU_UNUSED(op1)) +{ + s16 ip; + + START_OF_INSTR(); + DECODE_PRINTF("LOOPNE\t"); + ip = (s8) fetch_byte_imm(); + ip += (s16) M.x86.R_IP; + DECODE_PRINTF2("%04x\n", ip); + TRACE_AND_STEP(); + M.x86.R_CX -= 1; + if (M.x86.R_CX != 0 && !ACCESS_FLAG(F_ZF)) /* CX != 0 and !ZF */ + M.x86.R_IP = ip; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xe1 +****************************************************************************/ +void x86emuOp_loope(u8 X86EMU_UNUSED(op1)) +{ + s16 ip; + + START_OF_INSTR(); + DECODE_PRINTF("LOOPE\t"); + ip = (s8) fetch_byte_imm(); + ip += (s16) M.x86.R_IP; + DECODE_PRINTF2("%04x\n", ip); + TRACE_AND_STEP(); + M.x86.R_CX -= 1; + if (M.x86.R_CX != 0 && ACCESS_FLAG(F_ZF)) /* CX != 0 and ZF */ + M.x86.R_IP = ip; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xe2 +****************************************************************************/ +void x86emuOp_loop(u8 X86EMU_UNUSED(op1)) +{ + s16 ip; + + START_OF_INSTR(); + DECODE_PRINTF("LOOP\t"); + ip = (s8) fetch_byte_imm(); + ip += (s16) M.x86.R_IP; + DECODE_PRINTF2("%04x\n", ip); + TRACE_AND_STEP(); + M.x86.R_CX -= 1; + if (M.x86.R_CX != 0) + M.x86.R_IP = ip; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xe3 +****************************************************************************/ +void x86emuOp_jcxz(u8 X86EMU_UNUSED(op1)) +{ + u16 target; + s8 offset; + + /* jump to byte offset if overflow flag is set */ + START_OF_INSTR(); + DECODE_PRINTF("JCXZ\t"); + offset = (s8)fetch_byte_imm(); + target = (u16)(M.x86.R_IP + offset); + DECODE_PRINTF2("%x\n", target); + TRACE_AND_STEP(); + if (M.x86.R_CX == 0) + M.x86.R_IP = target; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xe4 +****************************************************************************/ +void x86emuOp_in_byte_AL_IMM(u8 X86EMU_UNUSED(op1)) +{ + u8 port; + + START_OF_INSTR(); + DECODE_PRINTF("IN\t"); + port = (u8) fetch_byte_imm(); + DECODE_PRINTF2("%x,AL\n", port); + TRACE_AND_STEP(); + M.x86.R_AL = (*sys_inb)(port); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xe5 +****************************************************************************/ +void x86emuOp_in_word_AX_IMM(u8 X86EMU_UNUSED(op1)) +{ + u8 port; + + START_OF_INSTR(); + DECODE_PRINTF("IN\t"); + port = (u8) fetch_byte_imm(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF2("EAX,%x\n", port); + } else { + DECODE_PRINTF2("AX,%x\n", port); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + M.x86.R_EAX = (*sys_inl)(port); + } else { + M.x86.R_AX = (*sys_inw)(port); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xe6 +****************************************************************************/ +void x86emuOp_out_byte_IMM_AL(u8 X86EMU_UNUSED(op1)) +{ + u8 port; + + START_OF_INSTR(); + DECODE_PRINTF("OUT\t"); + port = (u8) fetch_byte_imm(); + DECODE_PRINTF2("%x,AL\n", port); + TRACE_AND_STEP(); + (*sys_outb)(port, M.x86.R_AL); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xe7 +****************************************************************************/ +void x86emuOp_out_word_IMM_AX(u8 X86EMU_UNUSED(op1)) +{ + u8 port; + + START_OF_INSTR(); + DECODE_PRINTF("OUT\t"); + port = (u8) fetch_byte_imm(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF2("%x,EAX\n", port); + } else { + DECODE_PRINTF2("%x,AX\n", port); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + (*sys_outl)(port, M.x86.R_EAX); + } else { + (*sys_outw)(port, M.x86.R_AX); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xe8 +****************************************************************************/ +void x86emuOp_call_near_IMM(u8 X86EMU_UNUSED(op1)) +{ + s16 ip; + + START_OF_INSTR(); + DECODE_PRINTF("CALL\t"); + ip = (s16) fetch_word_imm(); + ip += (s16) M.x86.R_IP; /* CHECK SIGN */ + DECODE_PRINTF2("%04x\n", ip); + CALL_TRACE(M.x86.saved_cs, M.x86.saved_ip, M.x86.R_CS, ip, ""); + TRACE_AND_STEP(); + push_word(M.x86.R_IP); + M.x86.R_IP = ip; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xe9 +****************************************************************************/ +void x86emuOp_jump_near_IMM(u8 X86EMU_UNUSED(op1)) +{ + int ip; + + START_OF_INSTR(); + DECODE_PRINTF("JMP\t"); + ip = (s16)fetch_word_imm(); + ip += (s16)M.x86.R_IP; + DECODE_PRINTF2("%04x\n", ip); + TRACE_AND_STEP(); + M.x86.R_IP = (u16)ip; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xea +****************************************************************************/ +void x86emuOp_jump_far_IMM(u8 X86EMU_UNUSED(op1)) +{ + u16 cs, ip; + + START_OF_INSTR(); + DECODE_PRINTF("JMP\tFAR "); + ip = fetch_word_imm(); + cs = fetch_word_imm(); + DECODE_PRINTF2("%04x:", cs); + DECODE_PRINTF2("%04x\n", ip); + TRACE_AND_STEP(); + M.x86.R_IP = ip; + M.x86.R_CS = cs; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xeb +****************************************************************************/ +void x86emuOp_jump_byte_IMM(u8 X86EMU_UNUSED(op1)) +{ + u16 target; + s8 offset; + + START_OF_INSTR(); + DECODE_PRINTF("JMP\t"); + offset = (s8)fetch_byte_imm(); + target = (u16)(M.x86.R_IP + offset); + DECODE_PRINTF2("%x\n", target); + TRACE_AND_STEP(); + M.x86.R_IP = target; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xec +****************************************************************************/ +void x86emuOp_in_byte_AL_DX(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("IN\tAL,DX\n"); + TRACE_AND_STEP(); + M.x86.R_AL = (*sys_inb)(M.x86.R_DX); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xed +****************************************************************************/ +void x86emuOp_in_word_AX_DX(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("IN\tEAX,DX\n"); + } else { + DECODE_PRINTF("IN\tAX,DX\n"); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + M.x86.R_EAX = (*sys_inl)(M.x86.R_DX); + } else { + M.x86.R_AX = (*sys_inw)(M.x86.R_DX); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xee +****************************************************************************/ +void x86emuOp_out_byte_DX_AL(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("OUT\tDX,AL\n"); + TRACE_AND_STEP(); + (*sys_outb)(M.x86.R_DX, M.x86.R_AL); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xef +****************************************************************************/ +void x86emuOp_out_word_DX_AX(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("OUT\tDX,EAX\n"); + } else { + DECODE_PRINTF("OUT\tDX,AX\n"); + } + TRACE_AND_STEP(); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + (*sys_outl)(M.x86.R_DX, M.x86.R_EAX); + } else { + (*sys_outw)(M.x86.R_DX, M.x86.R_AX); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xf0 +****************************************************************************/ +void x86emuOp_lock(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("LOCK:\n"); + TRACE_AND_STEP(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/*opcode 0xf1 ILLEGAL OPERATION */ + +/**************************************************************************** +REMARKS: +Handles opcode 0xf2 +****************************************************************************/ +void x86emuOp_repne(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("REPNE\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_PREFIX_REPNE; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xf3 +****************************************************************************/ +void x86emuOp_repe(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("REPE\n"); + TRACE_AND_STEP(); + M.x86.mode |= SYSMODE_PREFIX_REPE; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xf4 +****************************************************************************/ +void x86emuOp_halt(u8 X86EMU_UNUSED(op1)) +{ + START_OF_INSTR(); + DECODE_PRINTF("HALT\n"); + TRACE_AND_STEP(); + HALT_SYS(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xf5 +****************************************************************************/ +void x86emuOp_cmc(u8 X86EMU_UNUSED(op1)) +{ + /* complement the carry flag. */ + START_OF_INSTR(); + DECODE_PRINTF("CMC\n"); + TRACE_AND_STEP(); + TOGGLE_FLAG(F_CF); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xf6 +****************************************************************************/ +void x86emuOp_opcF6_byte_RM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + u8 *destreg; + uint destoffset; + u8 destval, srcval; + + /* long, drawn out code follows. Double switch for a total + of 32 cases. */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); + DECODE_PRINTF(opF6_names[rh]); + if (mod < 3) { + DECODE_PRINTF("BYTE PTR "); + destoffset = decode_rmXX_address(mod, rl); + destval = fetch_data_byte(destoffset); + + switch (rh) { + case 0: /* test byte imm */ + DECODE_PRINTF(","); + srcval = fetch_byte_imm(); + DECODE_PRINTF2("%02x\n", srcval); + TRACE_AND_STEP(); + test_byte(destval, srcval); + break; + case 1: + DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); + HALT_SYS(); + break; + case 2: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = not_byte(destval); + store_data_byte(destoffset, destval); + break; + case 3: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = neg_byte(destval); + store_data_byte(destoffset, destval); + break; + case 4: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + mul_byte(destval); + break; + case 5: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + imul_byte(destval); + break; + case 6: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + div_byte(destval); + break; + default: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + idiv_byte(destval); + break; + } + } else { /* mod=11 */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + switch (rh) { + case 0: /* test byte imm */ + DECODE_PRINTF(","); + srcval = fetch_byte_imm(); + DECODE_PRINTF2("%02x\n", srcval); + TRACE_AND_STEP(); + test_byte(*destreg, srcval); + break; + case 1: + DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); + HALT_SYS(); + break; + case 2: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = not_byte(*destreg); + break; + case 3: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = neg_byte(*destreg); + break; + case 4: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + mul_byte(*destreg); /*!!! */ + break; + case 5: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + imul_byte(*destreg); + break; + case 6: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + div_byte(*destreg); + break; + default: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + idiv_byte(*destreg); + break; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xf7 +****************************************************************************/ +void x86emuOp_opcF7_word_RM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rl, rh; + uint destoffset; + + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); + DECODE_PRINTF(opF6_names[rh]); + if (mod < 3) { + + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval, srcval; + + DECODE_PRINTF("DWORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + destval = fetch_data_long(destoffset); + + switch (rh) { + case 0: + DECODE_PRINTF(","); + srcval = fetch_long_imm(); + DECODE_PRINTF2("%x\n", srcval); + TRACE_AND_STEP(); + test_long(destval, srcval); + break; + case 1: + DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F7\n"); + HALT_SYS(); + break; + case 2: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = not_long(destval); + store_data_long(destoffset, destval); + break; + case 3: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = neg_long(destval); + store_data_long(destoffset, destval); + break; + case 4: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + mul_long(destval); + break; + case 5: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + imul_long(destval); + break; + case 6: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + div_long(destval); + break; + case 7: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + idiv_long(destval); + break; + } + } else { + u16 destval, srcval; + + DECODE_PRINTF("WORD PTR "); + destoffset = decode_rmXX_address(mod, rl); + destval = fetch_data_word(destoffset); + + switch (rh) { + case 0: /* test word imm */ + DECODE_PRINTF(","); + srcval = fetch_word_imm(); + DECODE_PRINTF2("%x\n", srcval); + TRACE_AND_STEP(); + test_word(destval, srcval); + break; + case 1: + DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F7\n"); + HALT_SYS(); + break; + case 2: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = not_word(destval); + store_data_word(destoffset, destval); + break; + case 3: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + destval = neg_word(destval); + store_data_word(destoffset, destval); + break; + case 4: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + mul_word(destval); + break; + case 5: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + imul_word(destval); + break; + case 6: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + div_word(destval); + break; + case 7: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + idiv_word(destval); + break; + } + } + + } else { /* mod=11 */ + + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 srcval; + + destreg = DECODE_RM_LONG_REGISTER(rl); + + switch (rh) { + case 0: /* test word imm */ + DECODE_PRINTF(","); + srcval = fetch_long_imm(); + DECODE_PRINTF2("%x\n", srcval); + TRACE_AND_STEP(); + test_long(*destreg, srcval); + break; + case 1: + DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); + HALT_SYS(); + break; + case 2: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = not_long(*destreg); + break; + case 3: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = neg_long(*destreg); + break; + case 4: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + mul_long(*destreg); /*!!! */ + break; + case 5: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + imul_long(*destreg); + break; + case 6: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + div_long(*destreg); + break; + case 7: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + idiv_long(*destreg); + break; + } + } else { + u16 *destreg; + u16 srcval; + + destreg = DECODE_RM_WORD_REGISTER(rl); + + switch (rh) { + case 0: /* test word imm */ + DECODE_PRINTF(","); + srcval = fetch_word_imm(); + DECODE_PRINTF2("%x\n", srcval); + TRACE_AND_STEP(); + test_word(*destreg, srcval); + break; + case 1: + DECODE_PRINTF("ILLEGAL OP MOD=00 RH=01 OP=F6\n"); + HALT_SYS(); + break; + case 2: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = not_word(*destreg); + break; + case 3: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = neg_word(*destreg); + break; + case 4: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + mul_word(*destreg); /*!!! */ + break; + case 5: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + imul_word(*destreg); + break; + case 6: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + div_word(*destreg); + break; + case 7: + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + idiv_word(*destreg); + break; + } + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xf8 +****************************************************************************/ +void x86emuOp_clc(u8 X86EMU_UNUSED(op1)) +{ + /* clear the carry flag. */ + START_OF_INSTR(); + DECODE_PRINTF("CLC\n"); + TRACE_AND_STEP(); + CLEAR_FLAG(F_CF); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xf9 +****************************************************************************/ +void x86emuOp_stc(u8 X86EMU_UNUSED(op1)) +{ + /* set the carry flag. */ + START_OF_INSTR(); + DECODE_PRINTF("STC\n"); + TRACE_AND_STEP(); + SET_FLAG(F_CF); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xfa +****************************************************************************/ +void x86emuOp_cli(u8 X86EMU_UNUSED(op1)) +{ + /* clear interrupts. */ + START_OF_INSTR(); + DECODE_PRINTF("CLI\n"); + TRACE_AND_STEP(); + CLEAR_FLAG(F_IF); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xfb +****************************************************************************/ +void x86emuOp_sti(u8 X86EMU_UNUSED(op1)) +{ + /* enable interrupts. */ + START_OF_INSTR(); + DECODE_PRINTF("STI\n"); + TRACE_AND_STEP(); + SET_FLAG(F_IF); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xfc +****************************************************************************/ +void x86emuOp_cld(u8 X86EMU_UNUSED(op1)) +{ + /* clear interrupts. */ + START_OF_INSTR(); + DECODE_PRINTF("CLD\n"); + TRACE_AND_STEP(); + CLEAR_FLAG(F_DF); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xfd +****************************************************************************/ +void x86emuOp_std(u8 X86EMU_UNUSED(op1)) +{ + /* clear interrupts. */ + START_OF_INSTR(); + DECODE_PRINTF("STD\n"); + TRACE_AND_STEP(); + SET_FLAG(F_DF); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xfe +****************************************************************************/ +void x86emuOp_opcFE_byte_RM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rh, rl; + u8 destval; + uint destoffset; + u8 *destreg; + + /* Yet another special case instruction. */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + + switch (rh) { + case 0: + DECODE_PRINTF("INC\t"); + break; + case 1: + DECODE_PRINTF("DEC\t"); + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + DECODE_PRINTF2("ILLEGAL OP MAJOR OP 0xFE MINOR OP %x \n", mod); + HALT_SYS(); + break; + } + } +#endif + if (mod < 3) { + DECODE_PRINTF("BYTE PTR "); + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF("\n"); + destval = fetch_data_byte(destoffset); + TRACE_AND_STEP(); + if (rh == 0) + destval = inc_byte(destval); + else + destval = dec_byte(destval); + store_data_byte(destoffset, destval); + } else { + destreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + if (rh == 0) + *destreg = inc_byte(*destreg); + else + *destreg = dec_byte(*destreg); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0xff +****************************************************************************/ +void x86emuOp_opcFF_word_RM(u8 X86EMU_UNUSED(op1)) +{ + int mod, rh, rl; + uint destoffset = 0; + u16 *destreg; + u16 destval,destval2; + + /* Yet another special case instruction. */ + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); +#ifdef DEBUG + if (DEBUG_DECODE()) { + /* XXX DECODE_PRINTF may be changed to something more + general, so that it is important to leave the strings + in the same format, even though the result is that the + above test is done twice. */ + + switch (rh) { + case 0: + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("INC\tDWORD PTR "); + } else { + DECODE_PRINTF("INC\tWORD PTR "); + } + break; + case 1: + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + DECODE_PRINTF("DEC\tDWORD PTR "); + } else { + DECODE_PRINTF("DEC\tWORD PTR "); + } + break; + case 2: + DECODE_PRINTF("CALL\t "); + break; + case 3: + DECODE_PRINTF("CALL\tFAR "); + break; + case 4: + DECODE_PRINTF("JMP\t"); + break; + case 5: + DECODE_PRINTF("JMP\tFAR "); + break; + case 6: + DECODE_PRINTF("PUSH\t"); + break; + case 7: + DECODE_PRINTF("ILLEGAL DECODING OF OPCODE FF\t"); + HALT_SYS(); + break; + } + } +#endif + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF("\n"); + switch (rh) { + case 0: /* inc word ptr ... */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + + destval = fetch_data_long(destoffset); + TRACE_AND_STEP(); + destval = inc_long(destval); + store_data_long(destoffset, destval); + } else { + u16 destval; + + destval = fetch_data_word(destoffset); + TRACE_AND_STEP(); + destval = inc_word(destval); + store_data_word(destoffset, destval); + } + break; + case 1: /* dec word ptr ... */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + + destval = fetch_data_long(destoffset); + TRACE_AND_STEP(); + destval = dec_long(destval); + store_data_long(destoffset, destval); + } else { + u16 destval; + + destval = fetch_data_word(destoffset); + TRACE_AND_STEP(); + destval = dec_word(destval); + store_data_word(destoffset, destval); + } + break; + case 2: /* call word ptr ... */ + destval = fetch_data_word(destoffset); + TRACE_AND_STEP(); + push_word(M.x86.R_IP); + M.x86.R_IP = destval; + break; + case 3: /* call far ptr ... */ + destval = fetch_data_word(destoffset); + destval2 = fetch_data_word(destoffset + 2); + TRACE_AND_STEP(); + push_word(M.x86.R_CS); + M.x86.R_CS = destval2; + push_word(M.x86.R_IP); + M.x86.R_IP = destval; + break; + case 4: /* jmp word ptr ... */ + destval = fetch_data_word(destoffset); + TRACE_AND_STEP(); + M.x86.R_IP = destval; + break; + case 5: /* jmp far ptr ... */ + destval = fetch_data_word(destoffset); + destval2 = fetch_data_word(destoffset + 2); + TRACE_AND_STEP(); + M.x86.R_IP = destval; + M.x86.R_CS = destval2; + break; + case 6: /* push word ptr ... */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + + destval = fetch_data_long(destoffset); + TRACE_AND_STEP(); + push_long(destval); + } else { + u16 destval; + + destval = fetch_data_word(destoffset); + TRACE_AND_STEP(); + push_word(destval); + } + break; + } + } else { + switch (rh) { + case 0: + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = inc_long(*destreg); + } else { + u16 *destreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = inc_word(*destreg); + } + break; + case 1: + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = dec_long(*destreg); + } else { + u16 *destreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = dec_word(*destreg); + } + break; + case 2: /* call word ptr ... */ + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + push_word(M.x86.R_IP); + M.x86.R_IP = *destreg; + break; + case 3: /* jmp far ptr ... */ + DECODE_PRINTF("OPERATION UNDEFINED 0XFF \n"); + TRACE_AND_STEP(); + HALT_SYS(); + break; + + case 4: /* jmp ... */ + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + M.x86.R_IP = (u16) (*destreg); + break; + case 5: /* jmp far ptr ... */ + DECODE_PRINTF("OPERATION UNDEFINED 0XFF \n"); + TRACE_AND_STEP(); + HALT_SYS(); + break; + case 6: + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + push_long(*destreg); + } else { + u16 *destreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + push_word(*destreg); + } + break; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/*************************************************************************** + * Single byte operation code table: + **************************************************************************/ +void (*x86emu_optab[256])(u8) = +{ +/* 0x00 */ x86emuOp_genop_byte_RM_R, +/* 0x01 */ x86emuOp_genop_word_RM_R, +/* 0x02 */ x86emuOp_genop_byte_R_RM, +/* 0x03 */ x86emuOp_genop_word_R_RM, +/* 0x04 */ x86emuOp_genop_byte_AL_IMM, +/* 0x05 */ x86emuOp_genop_word_AX_IMM, +/* 0x06 */ x86emuOp_push_ES, +/* 0x07 */ x86emuOp_pop_ES, + +/* 0x08 */ x86emuOp_genop_byte_RM_R, +/* 0x09 */ x86emuOp_genop_word_RM_R, +/* 0x0a */ x86emuOp_genop_byte_R_RM, +/* 0x0b */ x86emuOp_genop_word_R_RM, +/* 0x0c */ x86emuOp_genop_byte_AL_IMM, +/* 0x0d */ x86emuOp_genop_word_AX_IMM, +/* 0x0e */ x86emuOp_push_CS, +/* 0x0f */ x86emuOp_two_byte, + +/* 0x10 */ x86emuOp_genop_byte_RM_R, +/* 0x11 */ x86emuOp_genop_word_RM_R, +/* 0x12 */ x86emuOp_genop_byte_R_RM, +/* 0x13 */ x86emuOp_genop_word_R_RM, +/* 0x14 */ x86emuOp_genop_byte_AL_IMM, +/* 0x15 */ x86emuOp_genop_word_AX_IMM, +/* 0x16 */ x86emuOp_push_SS, +/* 0x17 */ x86emuOp_pop_SS, + +/* 0x18 */ x86emuOp_genop_byte_RM_R, +/* 0x19 */ x86emuOp_genop_word_RM_R, +/* 0x1a */ x86emuOp_genop_byte_R_RM, +/* 0x1b */ x86emuOp_genop_word_R_RM, +/* 0x1c */ x86emuOp_genop_byte_AL_IMM, +/* 0x1d */ x86emuOp_genop_word_AX_IMM, +/* 0x1e */ x86emuOp_push_DS, +/* 0x1f */ x86emuOp_pop_DS, + +/* 0x20 */ x86emuOp_genop_byte_RM_R, +/* 0x21 */ x86emuOp_genop_word_RM_R, +/* 0x22 */ x86emuOp_genop_byte_R_RM, +/* 0x23 */ x86emuOp_genop_word_R_RM, +/* 0x24 */ x86emuOp_genop_byte_AL_IMM, +/* 0x25 */ x86emuOp_genop_word_AX_IMM, +/* 0x26 */ x86emuOp_segovr_ES, +/* 0x27 */ x86emuOp_daa, + +/* 0x28 */ x86emuOp_genop_byte_RM_R, +/* 0x29 */ x86emuOp_genop_word_RM_R, +/* 0x2a */ x86emuOp_genop_byte_R_RM, +/* 0x2b */ x86emuOp_genop_word_R_RM, +/* 0x2c */ x86emuOp_genop_byte_AL_IMM, +/* 0x2d */ x86emuOp_genop_word_AX_IMM, +/* 0x2e */ x86emuOp_segovr_CS, +/* 0x2f */ x86emuOp_das, + +/* 0x30 */ x86emuOp_genop_byte_RM_R, +/* 0x31 */ x86emuOp_genop_word_RM_R, +/* 0x32 */ x86emuOp_genop_byte_R_RM, +/* 0x33 */ x86emuOp_genop_word_R_RM, +/* 0x34 */ x86emuOp_genop_byte_AL_IMM, +/* 0x35 */ x86emuOp_genop_word_AX_IMM, +/* 0x36 */ x86emuOp_segovr_SS, +/* 0x37 */ x86emuOp_aaa, + +/* 0x38 */ x86emuOp_genop_byte_RM_R, +/* 0x39 */ x86emuOp_genop_word_RM_R, +/* 0x3a */ x86emuOp_genop_byte_R_RM, +/* 0x3b */ x86emuOp_genop_word_R_RM, +/* 0x3c */ x86emuOp_genop_byte_AL_IMM, +/* 0x3d */ x86emuOp_genop_word_AX_IMM, +/* 0x3e */ x86emuOp_segovr_DS, +/* 0x3f */ x86emuOp_aas, + +/* 0x40 */ x86emuOp_inc_register, +/* 0x41 */ x86emuOp_inc_register, +/* 0x42 */ x86emuOp_inc_register, +/* 0x43 */ x86emuOp_inc_register, +/* 0x44 */ x86emuOp_inc_register, +/* 0x45 */ x86emuOp_inc_register, +/* 0x46 */ x86emuOp_inc_register, +/* 0x47 */ x86emuOp_inc_register, + +/* 0x48 */ x86emuOp_dec_register, +/* 0x49 */ x86emuOp_dec_register, +/* 0x4a */ x86emuOp_dec_register, +/* 0x4b */ x86emuOp_dec_register, +/* 0x4c */ x86emuOp_dec_register, +/* 0x4d */ x86emuOp_dec_register, +/* 0x4e */ x86emuOp_dec_register, +/* 0x4f */ x86emuOp_dec_register, + +/* 0x50 */ x86emuOp_push_register, +/* 0x51 */ x86emuOp_push_register, +/* 0x52 */ x86emuOp_push_register, +/* 0x53 */ x86emuOp_push_register, +/* 0x54 */ x86emuOp_push_register, +/* 0x55 */ x86emuOp_push_register, +/* 0x56 */ x86emuOp_push_register, +/* 0x57 */ x86emuOp_push_register, + +/* 0x58 */ x86emuOp_pop_register, +/* 0x59 */ x86emuOp_pop_register, +/* 0x5a */ x86emuOp_pop_register, +/* 0x5b */ x86emuOp_pop_register, +/* 0x5c */ x86emuOp_pop_register, +/* 0x5d */ x86emuOp_pop_register, +/* 0x5e */ x86emuOp_pop_register, +/* 0x5f */ x86emuOp_pop_register, + +/* 0x60 */ x86emuOp_push_all, +/* 0x61 */ x86emuOp_pop_all, +/* 0x62 */ x86emuOp_illegal_op, /* bound */ +/* 0x63 */ x86emuOp_illegal_op, /* arpl */ +/* 0x64 */ x86emuOp_segovr_FS, +/* 0x65 */ x86emuOp_segovr_GS, +/* 0x66 */ x86emuOp_prefix_data, +/* 0x67 */ x86emuOp_prefix_addr, + +/* 0x68 */ x86emuOp_push_word_IMM, +/* 0x69 */ x86emuOp_imul_word_IMM, +/* 0x6a */ x86emuOp_push_byte_IMM, +/* 0x6b */ x86emuOp_imul_byte_IMM, +/* 0x6c */ x86emuOp_ins_byte, +/* 0x6d */ x86emuOp_ins_word, +/* 0x6e */ x86emuOp_outs_byte, +/* 0x6f */ x86emuOp_outs_word, + +/* 0x70 */ x86emuOp_jump_near_cond, +/* 0x71 */ x86emuOp_jump_near_cond, +/* 0x72 */ x86emuOp_jump_near_cond, +/* 0x73 */ x86emuOp_jump_near_cond, +/* 0x74 */ x86emuOp_jump_near_cond, +/* 0x75 */ x86emuOp_jump_near_cond, +/* 0x76 */ x86emuOp_jump_near_cond, +/* 0x77 */ x86emuOp_jump_near_cond, + +/* 0x78 */ x86emuOp_jump_near_cond, +/* 0x79 */ x86emuOp_jump_near_cond, +/* 0x7a */ x86emuOp_jump_near_cond, +/* 0x7b */ x86emuOp_jump_near_cond, +/* 0x7c */ x86emuOp_jump_near_cond, +/* 0x7d */ x86emuOp_jump_near_cond, +/* 0x7e */ x86emuOp_jump_near_cond, +/* 0x7f */ x86emuOp_jump_near_cond, + +/* 0x80 */ x86emuOp_opc80_byte_RM_IMM, +/* 0x81 */ x86emuOp_opc81_word_RM_IMM, +/* 0x82 */ x86emuOp_opc82_byte_RM_IMM, +/* 0x83 */ x86emuOp_opc83_word_RM_IMM, +/* 0x84 */ x86emuOp_test_byte_RM_R, +/* 0x85 */ x86emuOp_test_word_RM_R, +/* 0x86 */ x86emuOp_xchg_byte_RM_R, +/* 0x87 */ x86emuOp_xchg_word_RM_R, + +/* 0x88 */ x86emuOp_mov_byte_RM_R, +/* 0x89 */ x86emuOp_mov_word_RM_R, +/* 0x8a */ x86emuOp_mov_byte_R_RM, +/* 0x8b */ x86emuOp_mov_word_R_RM, +/* 0x8c */ x86emuOp_mov_word_RM_SR, +/* 0x8d */ x86emuOp_lea_word_R_M, +/* 0x8e */ x86emuOp_mov_word_SR_RM, +/* 0x8f */ x86emuOp_pop_RM, + +/* 0x90 */ x86emuOp_nop, +/* 0x91 */ x86emuOp_xchg_word_AX_register, +/* 0x92 */ x86emuOp_xchg_word_AX_register, +/* 0x93 */ x86emuOp_xchg_word_AX_register, +/* 0x94 */ x86emuOp_xchg_word_AX_register, +/* 0x95 */ x86emuOp_xchg_word_AX_register, +/* 0x96 */ x86emuOp_xchg_word_AX_register, +/* 0x97 */ x86emuOp_xchg_word_AX_register, + +/* 0x98 */ x86emuOp_cbw, +/* 0x99 */ x86emuOp_cwd, +/* 0x9a */ x86emuOp_call_far_IMM, +/* 0x9b */ x86emuOp_wait, +/* 0x9c */ x86emuOp_pushf_word, +/* 0x9d */ x86emuOp_popf_word, +/* 0x9e */ x86emuOp_sahf, +/* 0x9f */ x86emuOp_lahf, + +/* 0xa0 */ x86emuOp_mov_AL_M_IMM, +/* 0xa1 */ x86emuOp_mov_AX_M_IMM, +/* 0xa2 */ x86emuOp_mov_M_AL_IMM, +/* 0xa3 */ x86emuOp_mov_M_AX_IMM, +/* 0xa4 */ x86emuOp_movs_byte, +/* 0xa5 */ x86emuOp_movs_word, +/* 0xa6 */ x86emuOp_cmps_byte, +/* 0xa7 */ x86emuOp_cmps_word, +/* 0xa8 */ x86emuOp_test_AL_IMM, +/* 0xa9 */ x86emuOp_test_AX_IMM, +/* 0xaa */ x86emuOp_stos_byte, +/* 0xab */ x86emuOp_stos_word, +/* 0xac */ x86emuOp_lods_byte, +/* 0xad */ x86emuOp_lods_word, +/* 0xac */ x86emuOp_scas_byte, +/* 0xad */ x86emuOp_scas_word, + +/* 0xb0 */ x86emuOp_mov_byte_register_IMM, +/* 0xb1 */ x86emuOp_mov_byte_register_IMM, +/* 0xb2 */ x86emuOp_mov_byte_register_IMM, +/* 0xb3 */ x86emuOp_mov_byte_register_IMM, +/* 0xb4 */ x86emuOp_mov_byte_register_IMM, +/* 0xb5 */ x86emuOp_mov_byte_register_IMM, +/* 0xb6 */ x86emuOp_mov_byte_register_IMM, +/* 0xb7 */ x86emuOp_mov_byte_register_IMM, + +/* 0xb8 */ x86emuOp_mov_word_register_IMM, +/* 0xb9 */ x86emuOp_mov_word_register_IMM, +/* 0xba */ x86emuOp_mov_word_register_IMM, +/* 0xbb */ x86emuOp_mov_word_register_IMM, +/* 0xbc */ x86emuOp_mov_word_register_IMM, +/* 0xbd */ x86emuOp_mov_word_register_IMM, +/* 0xbe */ x86emuOp_mov_word_register_IMM, +/* 0xbf */ x86emuOp_mov_word_register_IMM, + +/* 0xc0 */ x86emuOp_opcC0_byte_RM_MEM, +/* 0xc1 */ x86emuOp_opcC1_word_RM_MEM, +/* 0xc2 */ x86emuOp_ret_near_IMM, +/* 0xc3 */ x86emuOp_ret_near, +/* 0xc4 */ x86emuOp_les_R_IMM, +/* 0xc5 */ x86emuOp_lds_R_IMM, +/* 0xc6 */ x86emuOp_mov_byte_RM_IMM, +/* 0xc7 */ x86emuOp_mov_word_RM_IMM, +/* 0xc8 */ x86emuOp_enter, +/* 0xc9 */ x86emuOp_leave, +/* 0xca */ x86emuOp_ret_far_IMM, +/* 0xcb */ x86emuOp_ret_far, +/* 0xcc */ x86emuOp_int3, +/* 0xcd */ x86emuOp_int_IMM, +/* 0xce */ x86emuOp_into, +/* 0xcf */ x86emuOp_iret, + +/* 0xd0 */ x86emuOp_opcD0_byte_RM_1, +/* 0xd1 */ x86emuOp_opcD1_word_RM_1, +/* 0xd2 */ x86emuOp_opcD2_byte_RM_CL, +/* 0xd3 */ x86emuOp_opcD3_word_RM_CL, +/* 0xd4 */ x86emuOp_aam, +/* 0xd5 */ x86emuOp_aad, +/* 0xd6 */ x86emuOp_illegal_op, /* Undocumented SETALC instruction */ +/* 0xd7 */ x86emuOp_xlat, +/* 0xd8 */ NULL, /*x86emuOp_esc_coprocess_d8,*/ +/* 0xd9 */ NULL, /*x86emuOp_esc_coprocess_d9,*/ +/* 0xda */ NULL, /*x86emuOp_esc_coprocess_da,*/ +/* 0xdb */ NULL, /*x86emuOp_esc_coprocess_db,*/ +/* 0xdc */ NULL, /*x86emuOp_esc_coprocess_dc,*/ +/* 0xdd */ NULL, /*x86emuOp_esc_coprocess_dd,*/ +/* 0xde */ NULL, /*x86emuOp_esc_coprocess_de,*/ +/* 0xdf */ NULL, /*x86emuOp_esc_coprocess_df,*/ + +/* 0xe0 */ x86emuOp_loopne, +/* 0xe1 */ x86emuOp_loope, +/* 0xe2 */ x86emuOp_loop, +/* 0xe3 */ x86emuOp_jcxz, +/* 0xe4 */ x86emuOp_in_byte_AL_IMM, +/* 0xe5 */ x86emuOp_in_word_AX_IMM, +/* 0xe6 */ x86emuOp_out_byte_IMM_AL, +/* 0xe7 */ x86emuOp_out_word_IMM_AX, + +/* 0xe8 */ x86emuOp_call_near_IMM, +/* 0xe9 */ x86emuOp_jump_near_IMM, +/* 0xea */ x86emuOp_jump_far_IMM, +/* 0xeb */ x86emuOp_jump_byte_IMM, +/* 0xec */ x86emuOp_in_byte_AL_DX, +/* 0xed */ x86emuOp_in_word_AX_DX, +/* 0xee */ x86emuOp_out_byte_DX_AL, +/* 0xef */ x86emuOp_out_word_DX_AX, + +/* 0xf0 */ x86emuOp_lock, +/* 0xf1 */ x86emuOp_illegal_op, +/* 0xf2 */ x86emuOp_repne, +/* 0xf3 */ x86emuOp_repe, +/* 0xf4 */ x86emuOp_halt, +/* 0xf5 */ x86emuOp_cmc, +/* 0xf6 */ x86emuOp_opcF6_byte_RM, +/* 0xf7 */ x86emuOp_opcF7_word_RM, + +/* 0xf8 */ x86emuOp_clc, +/* 0xf9 */ x86emuOp_stc, +/* 0xfa */ x86emuOp_cli, +/* 0xfb */ x86emuOp_sti, +/* 0xfc */ x86emuOp_cld, +/* 0xfd */ x86emuOp_std, +/* 0xfe */ x86emuOp_opcFE_byte_RM, +/* 0xff */ x86emuOp_opcFF_word_RM, +}; diff --git a/qemu/roms/u-boot/drivers/bios_emulator/x86emu/ops2.c b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/ops2.c new file mode 100644 index 000000000..59dbb422d --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/ops2.c @@ -0,0 +1,1769 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 2007 Freescale Semiconductor, Inc. +* Jason Jin +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: This file includes subroutines to implement the decoding +* and emulation of all the x86 extended two-byte processor +* instructions. +* +****************************************************************************/ + +#include +#include +#include "x86emu/x86emui.h" + +/*----------------------------- Implementation ----------------------------*/ + +/**************************************************************************** +PARAMETERS: +op1 - Instruction op code + +REMARKS: +Handles illegal opcodes. +****************************************************************************/ +void x86emuOp2_illegal_op( + u8 op2) +{ + START_OF_INSTR(); + DECODE_PRINTF("ILLEGAL EXTENDED X86 OPCODE\n"); + TRACE_REGS(); + printk("%04x:%04x: %02X ILLEGAL EXTENDED X86 OPCODE!\n", + M.x86.R_CS, M.x86.R_IP-2,op2); + HALT_SYS(); + END_OF_INSTR(); +} + +#define xorl(a,b) ((a) && !(b)) || (!(a) && (b)) + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0x80-0x8F +****************************************************************************/ +int x86emu_check_jump_condition(u8 op) +{ + switch (op) { + case 0x0: + DECODE_PRINTF("JO\t"); + return ACCESS_FLAG(F_OF); + case 0x1: + DECODE_PRINTF("JNO\t"); + return !ACCESS_FLAG(F_OF); + break; + case 0x2: + DECODE_PRINTF("JB\t"); + return ACCESS_FLAG(F_CF); + break; + case 0x3: + DECODE_PRINTF("JNB\t"); + return !ACCESS_FLAG(F_CF); + break; + case 0x4: + DECODE_PRINTF("JZ\t"); + return ACCESS_FLAG(F_ZF); + break; + case 0x5: + DECODE_PRINTF("JNZ\t"); + return !ACCESS_FLAG(F_ZF); + break; + case 0x6: + DECODE_PRINTF("JBE\t"); + return ACCESS_FLAG(F_CF) || ACCESS_FLAG(F_ZF); + break; + case 0x7: + DECODE_PRINTF("JNBE\t"); + return !(ACCESS_FLAG(F_CF) || ACCESS_FLAG(F_ZF)); + break; + case 0x8: + DECODE_PRINTF("JS\t"); + return ACCESS_FLAG(F_SF); + break; + case 0x9: + DECODE_PRINTF("JNS\t"); + return !ACCESS_FLAG(F_SF); + break; + case 0xa: + DECODE_PRINTF("JP\t"); + return ACCESS_FLAG(F_PF); + break; + case 0xb: + DECODE_PRINTF("JNP\t"); + return !ACCESS_FLAG(F_PF); + break; + case 0xc: + DECODE_PRINTF("JL\t"); + return xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)); + break; + case 0xd: + DECODE_PRINTF("JNL\t"); + return !xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)); + break; + case 0xe: + DECODE_PRINTF("JLE\t"); + return (xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)) || + ACCESS_FLAG(F_ZF)); + break; + default: + DECODE_PRINTF("JNLE\t"); + return !(xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)) || + ACCESS_FLAG(F_ZF)); + } +} + +void x86emuOp2_long_jump(u8 op2) +{ + s32 target; + int cond; + + /* conditional jump to word offset. */ + START_OF_INSTR(); + cond = x86emu_check_jump_condition(op2 & 0xF); + target = (s16) fetch_word_imm(); + target += (s16) M.x86.R_IP; + DECODE_PRINTF2("%04x\n", target); + TRACE_AND_STEP(); + if (cond) + M.x86.R_IP = (u16)target; + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0x90-0x9F +****************************************************************************/ +void x86emuOp2_set_byte(u8 op2) +{ + int mod, rl, rh; + uint destoffset; + u8 *destreg; + __maybe_unused char *name = 0; + int cond = 0; + + START_OF_INSTR(); + switch (op2) { + case 0x90: + name = "SETO\t"; + cond = ACCESS_FLAG(F_OF); + break; + case 0x91: + name = "SETNO\t"; + cond = !ACCESS_FLAG(F_OF); + break; + case 0x92: + name = "SETB\t"; + cond = ACCESS_FLAG(F_CF); + break; + case 0x93: + name = "SETNB\t"; + cond = !ACCESS_FLAG(F_CF); + break; + case 0x94: + name = "SETZ\t"; + cond = ACCESS_FLAG(F_ZF); + break; + case 0x95: + name = "SETNZ\t"; + cond = !ACCESS_FLAG(F_ZF); + break; + case 0x96: + name = "SETBE\t"; + cond = ACCESS_FLAG(F_CF) || ACCESS_FLAG(F_ZF); + break; + case 0x97: + name = "SETNBE\t"; + cond = !(ACCESS_FLAG(F_CF) || ACCESS_FLAG(F_ZF)); + break; + case 0x98: + name = "SETS\t"; + cond = ACCESS_FLAG(F_SF); + break; + case 0x99: + name = "SETNS\t"; + cond = !ACCESS_FLAG(F_SF); + break; + case 0x9a: + name = "SETP\t"; + cond = ACCESS_FLAG(F_PF); + break; + case 0x9b: + name = "SETNP\t"; + cond = !ACCESS_FLAG(F_PF); + break; + case 0x9c: + name = "SETL\t"; + cond = xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)); + break; + case 0x9d: + name = "SETNL\t"; + cond = !xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)); + break; + case 0x9e: + name = "SETLE\t"; + cond = (xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)) || + ACCESS_FLAG(F_ZF)); + break; + case 0x9f: + name = "SETNLE\t"; + cond = !(xorl(ACCESS_FLAG(F_SF), ACCESS_FLAG(F_OF)) || + ACCESS_FLAG(F_ZF)); + break; + } + DECODE_PRINTF(name); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + TRACE_AND_STEP(); + store_data_byte(destoffset, cond ? 0x01 : 0x00); + } else { /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(rl); + TRACE_AND_STEP(); + *destreg = cond ? 0x01 : 0x00; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xa0 +****************************************************************************/ +void x86emuOp2_push_FS(u8 X86EMU_UNUSED(op2)) +{ + START_OF_INSTR(); + DECODE_PRINTF("PUSH\tFS\n"); + TRACE_AND_STEP(); + push_word(M.x86.R_FS); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xa1 +****************************************************************************/ +void x86emuOp2_pop_FS(u8 X86EMU_UNUSED(op2)) +{ + START_OF_INSTR(); + DECODE_PRINTF("POP\tFS\n"); + TRACE_AND_STEP(); + M.x86.R_FS = pop_word(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xa3 +****************************************************************************/ +void x86emuOp2_bt_R(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + int bit,disp; + + START_OF_INSTR(); + DECODE_PRINTF("BT\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 srcval; + u32 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0x1F; + disp = (s16)*shiftreg >> 5; + srcval = fetch_data_long(srcoffset+disp); + CONDITIONAL_SET_FLAG(srcval & (0x1 << bit),F_CF); + } else { + u16 srcval; + u16 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0xF; + disp = (s16)*shiftreg >> 4; + srcval = fetch_data_word(srcoffset+disp); + CONDITIONAL_SET_FLAG(srcval & (0x1 << bit),F_CF); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg,*shiftreg; + + srcreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0x1F; + CONDITIONAL_SET_FLAG(*srcreg & (0x1 << bit),F_CF); + } else { + u16 *srcreg,*shiftreg; + + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0xF; + CONDITIONAL_SET_FLAG(*srcreg & (0x1 << bit),F_CF); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xa4 +****************************************************************************/ +void x86emuOp2_shld_IMM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint destoffset; + u8 shift; + + START_OF_INSTR(); + DECODE_PRINTF("SHLD\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + u32 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + shift = fetch_byte_imm(); + DECODE_PRINTF2("%d\n", shift); + TRACE_AND_STEP(); + destval = fetch_data_long(destoffset); + destval = shld_long(destval,*shiftreg,shift); + store_data_long(destoffset, destval); + } else { + u16 destval; + u16 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + shift = fetch_byte_imm(); + DECODE_PRINTF2("%d\n", shift); + TRACE_AND_STEP(); + destval = fetch_data_word(destoffset); + destval = shld_word(destval,*shiftreg,shift); + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*shiftreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + shift = fetch_byte_imm(); + DECODE_PRINTF2("%d\n", shift); + TRACE_AND_STEP(); + *destreg = shld_long(*destreg,*shiftreg,shift); + } else { + u16 *destreg,*shiftreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + shift = fetch_byte_imm(); + DECODE_PRINTF2("%d\n", shift); + TRACE_AND_STEP(); + *destreg = shld_word(*destreg,*shiftreg,shift); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xa5 +****************************************************************************/ +void x86emuOp2_shld_CL(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint destoffset; + + START_OF_INSTR(); + DECODE_PRINTF("SHLD\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + u32 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + destval = fetch_data_long(destoffset); + destval = shld_long(destval,*shiftreg,M.x86.R_CL); + store_data_long(destoffset, destval); + } else { + u16 destval; + u16 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + destval = fetch_data_word(destoffset); + destval = shld_word(destval,*shiftreg,M.x86.R_CL); + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*shiftreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + *destreg = shld_long(*destreg,*shiftreg,M.x86.R_CL); + } else { + u16 *destreg,*shiftreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + *destreg = shld_word(*destreg,*shiftreg,M.x86.R_CL); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xa8 +****************************************************************************/ +void x86emuOp2_push_GS(u8 X86EMU_UNUSED(op2)) +{ + START_OF_INSTR(); + DECODE_PRINTF("PUSH\tGS\n"); + TRACE_AND_STEP(); + push_word(M.x86.R_GS); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xa9 +****************************************************************************/ +void x86emuOp2_pop_GS(u8 X86EMU_UNUSED(op2)) +{ + START_OF_INSTR(); + DECODE_PRINTF("POP\tGS\n"); + TRACE_AND_STEP(); + M.x86.R_GS = pop_word(); + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xaa +****************************************************************************/ +void x86emuOp2_bts_R(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + int bit,disp; + + START_OF_INSTR(); + DECODE_PRINTF("BTS\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 srcval,mask; + u32 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0x1F; + disp = (s16)*shiftreg >> 5; + srcval = fetch_data_long(srcoffset+disp); + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(srcval & mask,F_CF); + store_data_long(srcoffset+disp, srcval | mask); + } else { + u16 srcval,mask; + u16 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0xF; + disp = (s16)*shiftreg >> 4; + srcval = fetch_data_word(srcoffset+disp); + mask = (u16)(0x1 << bit); + CONDITIONAL_SET_FLAG(srcval & mask,F_CF); + store_data_word(srcoffset+disp, srcval | mask); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg,*shiftreg; + u32 mask; + + srcreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0x1F; + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(*srcreg & mask,F_CF); + *srcreg |= mask; + } else { + u16 *srcreg,*shiftreg; + u16 mask; + + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0xF; + mask = (u16)(0x1 << bit); + CONDITIONAL_SET_FLAG(*srcreg & mask,F_CF); + *srcreg |= mask; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xac +****************************************************************************/ +void x86emuOp2_shrd_IMM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint destoffset; + u8 shift; + + START_OF_INSTR(); + DECODE_PRINTF("SHLD\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + u32 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + shift = fetch_byte_imm(); + DECODE_PRINTF2("%d\n", shift); + TRACE_AND_STEP(); + destval = fetch_data_long(destoffset); + destval = shrd_long(destval,*shiftreg,shift); + store_data_long(destoffset, destval); + } else { + u16 destval; + u16 *shiftreg; + + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + shift = fetch_byte_imm(); + DECODE_PRINTF2("%d\n", shift); + TRACE_AND_STEP(); + destval = fetch_data_word(destoffset); + destval = shrd_word(destval,*shiftreg,shift); + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*shiftreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + shift = fetch_byte_imm(); + DECODE_PRINTF2("%d\n", shift); + TRACE_AND_STEP(); + *destreg = shrd_long(*destreg,*shiftreg,shift); + } else { + u16 *destreg,*shiftreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + shift = fetch_byte_imm(); + DECODE_PRINTF2("%d\n", shift); + TRACE_AND_STEP(); + *destreg = shrd_word(*destreg,*shiftreg,shift); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xad +****************************************************************************/ +void x86emuOp2_shrd_CL(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint destoffset; + + START_OF_INSTR(); + DECODE_PRINTF("SHLD\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 destval; + u32 *shiftreg; + + shiftreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + destval = fetch_data_long(destoffset); + destval = shrd_long(destval,*shiftreg,M.x86.R_CL); + store_data_long(destoffset, destval); + } else { + u16 destval; + u16 *shiftreg; + + shiftreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + destval = fetch_data_word(destoffset); + destval = shrd_word(destval,*shiftreg,M.x86.R_CL); + store_data_word(destoffset, destval); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*shiftreg; + + destreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + *destreg = shrd_long(*destreg,*shiftreg,M.x86.R_CL); + } else { + u16 *destreg,*shiftreg; + + destreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(",CL\n"); + TRACE_AND_STEP(); + *destreg = shrd_word(*destreg,*shiftreg,M.x86.R_CL); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xaf +****************************************************************************/ +void x86emuOp2_imul_R_RM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("IMUL\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 srcval; + u32 res_lo,res_hi; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = fetch_data_long(srcoffset); + TRACE_AND_STEP(); + imul_long_direct(&res_lo,&res_hi,(s32)*destreg,(s32)srcval); + if (res_hi != 0) { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } else { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } + *destreg = (u32)res_lo; + } else { + u16 *destreg; + u16 srcval; + u32 res; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = fetch_data_word(srcoffset); + TRACE_AND_STEP(); + res = (s16)*destreg * (s16)srcval; + if (res > 0xFFFF) { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } else { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } + *destreg = (u16)res; + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg,*srcreg; + u32 res_lo,res_hi; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_LONG_REGISTER(rl); + TRACE_AND_STEP(); + imul_long_direct(&res_lo,&res_hi,(s32)*destreg,(s32)*srcreg); + if (res_hi != 0) { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } else { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } + *destreg = (u32)res_lo; + } else { + u16 *destreg,*srcreg; + u32 res; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rl); + res = (s16)*destreg * (s16)*srcreg; + if (res > 0xFFFF) { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } else { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } + *destreg = (u16)res; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xb2 +****************************************************************************/ +void x86emuOp2_lss_R_IMM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rh, rl; + u16 *dstreg; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("LSS\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + dstreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *dstreg = fetch_data_word(srcoffset); + M.x86.R_SS = fetch_data_word(srcoffset + 2); + } else { /* register to register */ + /* UNDEFINED! */ + TRACE_AND_STEP(); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xb3 +****************************************************************************/ +void x86emuOp2_btr_R(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + int bit,disp; + + START_OF_INSTR(); + DECODE_PRINTF("BTR\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 srcval,mask; + u32 *shiftreg; + + shiftreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0x1F; + disp = (s16)*shiftreg >> 5; + srcval = fetch_data_long(srcoffset+disp); + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(srcval & mask,F_CF); + store_data_long(srcoffset+disp, srcval & ~mask); + } else { + u16 srcval,mask; + u16 *shiftreg; + + shiftreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0xF; + disp = (s16)*shiftreg >> 4; + srcval = fetch_data_word(srcoffset+disp); + mask = (u16)(0x1 << bit); + CONDITIONAL_SET_FLAG(srcval & mask,F_CF); + store_data_word(srcoffset+disp, (u16)(srcval & ~mask)); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg,*shiftreg; + u32 mask; + + srcreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0x1F; + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(*srcreg & mask,F_CF); + *srcreg &= ~mask; + } else { + u16 *srcreg,*shiftreg; + u16 mask; + + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0xF; + mask = (u16)(0x1 << bit); + CONDITIONAL_SET_FLAG(*srcreg & mask,F_CF); + *srcreg &= ~mask; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xb4 +****************************************************************************/ +void x86emuOp2_lfs_R_IMM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rh, rl; + u16 *dstreg; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("LFS\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + dstreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *dstreg = fetch_data_word(srcoffset); + M.x86.R_FS = fetch_data_word(srcoffset + 2); + } else { /* register to register */ + /* UNDEFINED! */ + TRACE_AND_STEP(); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xb5 +****************************************************************************/ +void x86emuOp2_lgs_R_IMM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rh, rl; + u16 *dstreg; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("LGS\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + dstreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *dstreg = fetch_data_word(srcoffset); + M.x86.R_GS = fetch_data_word(srcoffset + 2); + } else { /* register to register */ + /* UNDEFINED! */ + TRACE_AND_STEP(); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xb6 +****************************************************************************/ +void x86emuOp2_movzx_byte_R_RM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("MOVZX\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 srcval; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = fetch_data_byte(srcoffset); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } else { + u16 *destreg; + u16 srcval; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = fetch_data_byte(srcoffset); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u8 *srcreg; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } else { + u16 *destreg; + u8 *srcreg; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xb7 +****************************************************************************/ +void x86emuOp2_movzx_word_R_RM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + u32 *destreg; + u32 srcval; + u16 *srcreg; + + START_OF_INSTR(); + DECODE_PRINTF("MOVZX\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = fetch_data_word(srcoffset); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } else { /* register to register */ + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = *srcreg; + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xba +****************************************************************************/ +void x86emuOp2_btX_I(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + u8 shift; + int bit; + + START_OF_INSTR(); + FETCH_DECODE_MODRM(mod, rh, rl); + switch (rh) { + case 4: + DECODE_PRINTF("BT\t"); + break; + case 5: + DECODE_PRINTF("BTS\t"); + break; + case 6: + DECODE_PRINTF("BTR\t"); + break; + case 7: + DECODE_PRINTF("BTC\t"); + break; + default: + DECODE_PRINTF("ILLEGAL EXTENDED X86 OPCODE\n"); + TRACE_REGS(); + printk("%04x:%04x: %02X%02X ILLEGAL EXTENDED X86 OPCODE EXTENSION!\n", + M.x86.R_CS, M.x86.R_IP-3,op2, (mod<<6)|(rh<<3)|rl); + HALT_SYS(); + } + if (mod < 3) { + + srcoffset = decode_rmXX_address(mod, rl); + shift = fetch_byte_imm(); + DECODE_PRINTF2(",%d\n", shift); + TRACE_AND_STEP(); + + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 srcval, mask; + + bit = shift & 0x1F; + srcval = fetch_data_long(srcoffset); + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(srcval & mask,F_CF); + switch (rh) { + case 5: + store_data_long(srcoffset, srcval | mask); + break; + case 6: + store_data_long(srcoffset, srcval & ~mask); + break; + case 7: + store_data_long(srcoffset, srcval ^ mask); + break; + default: + break; + } + } else { + u16 srcval, mask; + + bit = shift & 0xF; + srcval = fetch_data_word(srcoffset); + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(srcval & mask,F_CF); + switch (rh) { + case 5: + store_data_word(srcoffset, srcval | mask); + break; + case 6: + store_data_word(srcoffset, srcval & ~mask); + break; + case 7: + store_data_word(srcoffset, srcval ^ mask); + break; + default: + break; + } + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg; + u32 mask; + + srcreg = DECODE_RM_LONG_REGISTER(rl); + shift = fetch_byte_imm(); + DECODE_PRINTF2(",%d\n", shift); + TRACE_AND_STEP(); + bit = shift & 0x1F; + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(*srcreg & mask,F_CF); + switch (rh) { + case 5: + *srcreg |= mask; + break; + case 6: + *srcreg &= ~mask; + break; + case 7: + *srcreg ^= mask; + break; + default: + break; + } + } else { + u16 *srcreg; + u16 mask; + + srcreg = DECODE_RM_WORD_REGISTER(rl); + shift = fetch_byte_imm(); + DECODE_PRINTF2(",%d\n", shift); + TRACE_AND_STEP(); + bit = shift & 0xF; + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(*srcreg & mask,F_CF); + switch (rh) { + case 5: + *srcreg |= mask; + break; + case 6: + *srcreg &= ~mask; + break; + case 7: + *srcreg ^= mask; + break; + default: + break; + } + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xbb +****************************************************************************/ +void x86emuOp2_btc_R(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + int bit,disp; + + START_OF_INSTR(); + DECODE_PRINTF("BTC\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 srcval,mask; + u32 *shiftreg; + + shiftreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0x1F; + disp = (s16)*shiftreg >> 5; + srcval = fetch_data_long(srcoffset+disp); + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(srcval & mask,F_CF); + store_data_long(srcoffset+disp, srcval ^ mask); + } else { + u16 srcval,mask; + u16 *shiftreg; + + shiftreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0xF; + disp = (s16)*shiftreg >> 4; + srcval = fetch_data_word(srcoffset+disp); + mask = (u16)(0x1 << bit); + CONDITIONAL_SET_FLAG(srcval & mask,F_CF); + store_data_word(srcoffset+disp, (u16)(srcval ^ mask)); + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg,*shiftreg; + u32 mask; + + srcreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0x1F; + mask = (0x1 << bit); + CONDITIONAL_SET_FLAG(*srcreg & mask,F_CF); + *srcreg ^= mask; + } else { + u16 *srcreg,*shiftreg; + u16 mask; + + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + shiftreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + bit = *shiftreg & 0xF; + mask = (u16)(0x1 << bit); + CONDITIONAL_SET_FLAG(*srcreg & mask,F_CF); + *srcreg ^= mask; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xbc +****************************************************************************/ +void x86emuOp2_bsf(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("BSF\n"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 srcval, *dstreg; + + dstreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + srcval = fetch_data_long(srcoffset); + CONDITIONAL_SET_FLAG(srcval == 0, F_ZF); + for(*dstreg = 0; *dstreg < 32; (*dstreg)++) + if ((srcval >> *dstreg) & 1) break; + } else { + u16 srcval, *dstreg; + + dstreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + srcval = fetch_data_word(srcoffset); + CONDITIONAL_SET_FLAG(srcval == 0, F_ZF); + for(*dstreg = 0; *dstreg < 16; (*dstreg)++) + if ((srcval >> *dstreg) & 1) break; + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg, *dstreg; + + srcreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + dstreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + CONDITIONAL_SET_FLAG(*srcreg == 0, F_ZF); + for(*dstreg = 0; *dstreg < 32; (*dstreg)++) + if ((*srcreg >> *dstreg) & 1) break; + } else { + u16 *srcreg, *dstreg; + + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + dstreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + CONDITIONAL_SET_FLAG(*srcreg == 0, F_ZF); + for(*dstreg = 0; *dstreg < 16; (*dstreg)++) + if ((*srcreg >> *dstreg) & 1) break; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xbd +****************************************************************************/ +void x86emuOp2_bsr(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("BSF\n"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + srcoffset = decode_rmXX_address(mod, rl); + DECODE_PRINTF(","); + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 srcval, *dstreg; + + dstreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + srcval = fetch_data_long(srcoffset); + CONDITIONAL_SET_FLAG(srcval == 0, F_ZF); + for(*dstreg = 31; *dstreg > 0; (*dstreg)--) + if ((srcval >> *dstreg) & 1) break; + } else { + u16 srcval, *dstreg; + + dstreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + srcval = fetch_data_word(srcoffset); + CONDITIONAL_SET_FLAG(srcval == 0, F_ZF); + for(*dstreg = 15; *dstreg > 0; (*dstreg)--) + if ((srcval >> *dstreg) & 1) break; + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *srcreg, *dstreg; + + srcreg = DECODE_RM_LONG_REGISTER(rl); + DECODE_PRINTF(","); + dstreg = DECODE_RM_LONG_REGISTER(rh); + TRACE_AND_STEP(); + CONDITIONAL_SET_FLAG(*srcreg == 0, F_ZF); + for(*dstreg = 31; *dstreg > 0; (*dstreg)--) + if ((*srcreg >> *dstreg) & 1) break; + } else { + u16 *srcreg, *dstreg; + + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF(","); + dstreg = DECODE_RM_WORD_REGISTER(rh); + TRACE_AND_STEP(); + CONDITIONAL_SET_FLAG(*srcreg == 0, F_ZF); + for(*dstreg = 15; *dstreg > 0; (*dstreg)--) + if ((*srcreg >> *dstreg) & 1) break; + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xbe +****************************************************************************/ +void x86emuOp2_movsx_byte_R_RM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + + START_OF_INSTR(); + DECODE_PRINTF("MOVSX\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u32 srcval; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = (s32)((s8)fetch_data_byte(srcoffset)); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } else { + u16 *destreg; + u16 srcval; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = (s16)((s8)fetch_data_byte(srcoffset)); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } + } else { /* register to register */ + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + u32 *destreg; + u8 *srcreg; + + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = (s32)((s8)*srcreg); + } else { + u16 *destreg; + u8 *srcreg; + + destreg = DECODE_RM_WORD_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_BYTE_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = (s16)((s8)*srcreg); + } + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/**************************************************************************** +REMARKS: +Handles opcode 0x0f,0xbf +****************************************************************************/ +void x86emuOp2_movsx_word_R_RM(u8 X86EMU_UNUSED(op2)) +{ + int mod, rl, rh; + uint srcoffset; + u32 *destreg; + u32 srcval; + u16 *srcreg; + + START_OF_INSTR(); + DECODE_PRINTF("MOVSX\t"); + FETCH_DECODE_MODRM(mod, rh, rl); + if (mod < 3) { + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcoffset = decode_rmXX_address(mod, rl); + srcval = (s32)((s16)fetch_data_word(srcoffset)); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = srcval; + } else { /* register to register */ + destreg = DECODE_RM_LONG_REGISTER(rh); + DECODE_PRINTF(","); + srcreg = DECODE_RM_WORD_REGISTER(rl); + DECODE_PRINTF("\n"); + TRACE_AND_STEP(); + *destreg = (s32)((s16)*srcreg); + } + DECODE_CLEAR_SEGOVR(); + END_OF_INSTR(); +} + +/*************************************************************************** + * Double byte operation code table: + **************************************************************************/ +void (*x86emu_optab2[256])(u8) = +{ +/* 0x00 */ x86emuOp2_illegal_op, /* Group F (ring 0 PM) */ +/* 0x01 */ x86emuOp2_illegal_op, /* Group G (ring 0 PM) */ +/* 0x02 */ x86emuOp2_illegal_op, /* lar (ring 0 PM) */ +/* 0x03 */ x86emuOp2_illegal_op, /* lsl (ring 0 PM) */ +/* 0x04 */ x86emuOp2_illegal_op, +/* 0x05 */ x86emuOp2_illegal_op, /* loadall (undocumented) */ +/* 0x06 */ x86emuOp2_illegal_op, /* clts (ring 0 PM) */ +/* 0x07 */ x86emuOp2_illegal_op, /* loadall (undocumented) */ +/* 0x08 */ x86emuOp2_illegal_op, /* invd (ring 0 PM) */ +/* 0x09 */ x86emuOp2_illegal_op, /* wbinvd (ring 0 PM) */ +/* 0x0a */ x86emuOp2_illegal_op, +/* 0x0b */ x86emuOp2_illegal_op, +/* 0x0c */ x86emuOp2_illegal_op, +/* 0x0d */ x86emuOp2_illegal_op, +/* 0x0e */ x86emuOp2_illegal_op, +/* 0x0f */ x86emuOp2_illegal_op, + +/* 0x10 */ x86emuOp2_illegal_op, +/* 0x11 */ x86emuOp2_illegal_op, +/* 0x12 */ x86emuOp2_illegal_op, +/* 0x13 */ x86emuOp2_illegal_op, +/* 0x14 */ x86emuOp2_illegal_op, +/* 0x15 */ x86emuOp2_illegal_op, +/* 0x16 */ x86emuOp2_illegal_op, +/* 0x17 */ x86emuOp2_illegal_op, +/* 0x18 */ x86emuOp2_illegal_op, +/* 0x19 */ x86emuOp2_illegal_op, +/* 0x1a */ x86emuOp2_illegal_op, +/* 0x1b */ x86emuOp2_illegal_op, +/* 0x1c */ x86emuOp2_illegal_op, +/* 0x1d */ x86emuOp2_illegal_op, +/* 0x1e */ x86emuOp2_illegal_op, +/* 0x1f */ x86emuOp2_illegal_op, + +/* 0x20 */ x86emuOp2_illegal_op, /* mov reg32,creg (ring 0 PM) */ +/* 0x21 */ x86emuOp2_illegal_op, /* mov reg32,dreg (ring 0 PM) */ +/* 0x22 */ x86emuOp2_illegal_op, /* mov creg,reg32 (ring 0 PM) */ +/* 0x23 */ x86emuOp2_illegal_op, /* mov dreg,reg32 (ring 0 PM) */ +/* 0x24 */ x86emuOp2_illegal_op, /* mov reg32,treg (ring 0 PM) */ +/* 0x25 */ x86emuOp2_illegal_op, +/* 0x26 */ x86emuOp2_illegal_op, /* mov treg,reg32 (ring 0 PM) */ +/* 0x27 */ x86emuOp2_illegal_op, +/* 0x28 */ x86emuOp2_illegal_op, +/* 0x29 */ x86emuOp2_illegal_op, +/* 0x2a */ x86emuOp2_illegal_op, +/* 0x2b */ x86emuOp2_illegal_op, +/* 0x2c */ x86emuOp2_illegal_op, +/* 0x2d */ x86emuOp2_illegal_op, +/* 0x2e */ x86emuOp2_illegal_op, +/* 0x2f */ x86emuOp2_illegal_op, + +/* 0x30 */ x86emuOp2_illegal_op, +/* 0x31 */ x86emuOp2_illegal_op, +/* 0x32 */ x86emuOp2_illegal_op, +/* 0x33 */ x86emuOp2_illegal_op, +/* 0x34 */ x86emuOp2_illegal_op, +/* 0x35 */ x86emuOp2_illegal_op, +/* 0x36 */ x86emuOp2_illegal_op, +/* 0x37 */ x86emuOp2_illegal_op, +/* 0x38 */ x86emuOp2_illegal_op, +/* 0x39 */ x86emuOp2_illegal_op, +/* 0x3a */ x86emuOp2_illegal_op, +/* 0x3b */ x86emuOp2_illegal_op, +/* 0x3c */ x86emuOp2_illegal_op, +/* 0x3d */ x86emuOp2_illegal_op, +/* 0x3e */ x86emuOp2_illegal_op, +/* 0x3f */ x86emuOp2_illegal_op, + +/* 0x40 */ x86emuOp2_illegal_op, +/* 0x41 */ x86emuOp2_illegal_op, +/* 0x42 */ x86emuOp2_illegal_op, +/* 0x43 */ x86emuOp2_illegal_op, +/* 0x44 */ x86emuOp2_illegal_op, +/* 0x45 */ x86emuOp2_illegal_op, +/* 0x46 */ x86emuOp2_illegal_op, +/* 0x47 */ x86emuOp2_illegal_op, +/* 0x48 */ x86emuOp2_illegal_op, +/* 0x49 */ x86emuOp2_illegal_op, +/* 0x4a */ x86emuOp2_illegal_op, +/* 0x4b */ x86emuOp2_illegal_op, +/* 0x4c */ x86emuOp2_illegal_op, +/* 0x4d */ x86emuOp2_illegal_op, +/* 0x4e */ x86emuOp2_illegal_op, +/* 0x4f */ x86emuOp2_illegal_op, + +/* 0x50 */ x86emuOp2_illegal_op, +/* 0x51 */ x86emuOp2_illegal_op, +/* 0x52 */ x86emuOp2_illegal_op, +/* 0x53 */ x86emuOp2_illegal_op, +/* 0x54 */ x86emuOp2_illegal_op, +/* 0x55 */ x86emuOp2_illegal_op, +/* 0x56 */ x86emuOp2_illegal_op, +/* 0x57 */ x86emuOp2_illegal_op, +/* 0x58 */ x86emuOp2_illegal_op, +/* 0x59 */ x86emuOp2_illegal_op, +/* 0x5a */ x86emuOp2_illegal_op, +/* 0x5b */ x86emuOp2_illegal_op, +/* 0x5c */ x86emuOp2_illegal_op, +/* 0x5d */ x86emuOp2_illegal_op, +/* 0x5e */ x86emuOp2_illegal_op, +/* 0x5f */ x86emuOp2_illegal_op, + +/* 0x60 */ x86emuOp2_illegal_op, +/* 0x61 */ x86emuOp2_illegal_op, +/* 0x62 */ x86emuOp2_illegal_op, +/* 0x63 */ x86emuOp2_illegal_op, +/* 0x64 */ x86emuOp2_illegal_op, +/* 0x65 */ x86emuOp2_illegal_op, +/* 0x66 */ x86emuOp2_illegal_op, +/* 0x67 */ x86emuOp2_illegal_op, +/* 0x68 */ x86emuOp2_illegal_op, +/* 0x69 */ x86emuOp2_illegal_op, +/* 0x6a */ x86emuOp2_illegal_op, +/* 0x6b */ x86emuOp2_illegal_op, +/* 0x6c */ x86emuOp2_illegal_op, +/* 0x6d */ x86emuOp2_illegal_op, +/* 0x6e */ x86emuOp2_illegal_op, +/* 0x6f */ x86emuOp2_illegal_op, + +/* 0x70 */ x86emuOp2_illegal_op, +/* 0x71 */ x86emuOp2_illegal_op, +/* 0x72 */ x86emuOp2_illegal_op, +/* 0x73 */ x86emuOp2_illegal_op, +/* 0x74 */ x86emuOp2_illegal_op, +/* 0x75 */ x86emuOp2_illegal_op, +/* 0x76 */ x86emuOp2_illegal_op, +/* 0x77 */ x86emuOp2_illegal_op, +/* 0x78 */ x86emuOp2_illegal_op, +/* 0x79 */ x86emuOp2_illegal_op, +/* 0x7a */ x86emuOp2_illegal_op, +/* 0x7b */ x86emuOp2_illegal_op, +/* 0x7c */ x86emuOp2_illegal_op, +/* 0x7d */ x86emuOp2_illegal_op, +/* 0x7e */ x86emuOp2_illegal_op, +/* 0x7f */ x86emuOp2_illegal_op, + +/* 0x80 */ x86emuOp2_long_jump, +/* 0x81 */ x86emuOp2_long_jump, +/* 0x82 */ x86emuOp2_long_jump, +/* 0x83 */ x86emuOp2_long_jump, +/* 0x84 */ x86emuOp2_long_jump, +/* 0x85 */ x86emuOp2_long_jump, +/* 0x86 */ x86emuOp2_long_jump, +/* 0x87 */ x86emuOp2_long_jump, +/* 0x88 */ x86emuOp2_long_jump, +/* 0x89 */ x86emuOp2_long_jump, +/* 0x8a */ x86emuOp2_long_jump, +/* 0x8b */ x86emuOp2_long_jump, +/* 0x8c */ x86emuOp2_long_jump, +/* 0x8d */ x86emuOp2_long_jump, +/* 0x8e */ x86emuOp2_long_jump, +/* 0x8f */ x86emuOp2_long_jump, + +/* 0x90 */ x86emuOp2_set_byte, +/* 0x91 */ x86emuOp2_set_byte, +/* 0x92 */ x86emuOp2_set_byte, +/* 0x93 */ x86emuOp2_set_byte, +/* 0x94 */ x86emuOp2_set_byte, +/* 0x95 */ x86emuOp2_set_byte, +/* 0x96 */ x86emuOp2_set_byte, +/* 0x97 */ x86emuOp2_set_byte, +/* 0x98 */ x86emuOp2_set_byte, +/* 0x99 */ x86emuOp2_set_byte, +/* 0x9a */ x86emuOp2_set_byte, +/* 0x9b */ x86emuOp2_set_byte, +/* 0x9c */ x86emuOp2_set_byte, +/* 0x9d */ x86emuOp2_set_byte, +/* 0x9e */ x86emuOp2_set_byte, +/* 0x9f */ x86emuOp2_set_byte, + +/* 0xa0 */ x86emuOp2_push_FS, +/* 0xa1 */ x86emuOp2_pop_FS, +/* 0xa2 */ x86emuOp2_illegal_op, +/* 0xa3 */ x86emuOp2_bt_R, +/* 0xa4 */ x86emuOp2_shld_IMM, +/* 0xa5 */ x86emuOp2_shld_CL, +/* 0xa6 */ x86emuOp2_illegal_op, +/* 0xa7 */ x86emuOp2_illegal_op, +/* 0xa8 */ x86emuOp2_push_GS, +/* 0xa9 */ x86emuOp2_pop_GS, +/* 0xaa */ x86emuOp2_illegal_op, +/* 0xab */ x86emuOp2_bt_R, +/* 0xac */ x86emuOp2_shrd_IMM, +/* 0xad */ x86emuOp2_shrd_CL, +/* 0xae */ x86emuOp2_illegal_op, +/* 0xaf */ x86emuOp2_imul_R_RM, + +/* 0xb0 */ x86emuOp2_illegal_op, /* TODO: cmpxchg */ +/* 0xb1 */ x86emuOp2_illegal_op, /* TODO: cmpxchg */ +/* 0xb2 */ x86emuOp2_lss_R_IMM, +/* 0xb3 */ x86emuOp2_btr_R, +/* 0xb4 */ x86emuOp2_lfs_R_IMM, +/* 0xb5 */ x86emuOp2_lgs_R_IMM, +/* 0xb6 */ x86emuOp2_movzx_byte_R_RM, +/* 0xb7 */ x86emuOp2_movzx_word_R_RM, +/* 0xb8 */ x86emuOp2_illegal_op, +/* 0xb9 */ x86emuOp2_illegal_op, +/* 0xba */ x86emuOp2_btX_I, +/* 0xbb */ x86emuOp2_btc_R, +/* 0xbc */ x86emuOp2_bsf, +/* 0xbd */ x86emuOp2_bsr, +/* 0xbe */ x86emuOp2_movsx_byte_R_RM, +/* 0xbf */ x86emuOp2_movsx_word_R_RM, + +/* 0xc0 */ x86emuOp2_illegal_op, /* TODO: xadd */ +/* 0xc1 */ x86emuOp2_illegal_op, /* TODO: xadd */ +/* 0xc2 */ x86emuOp2_illegal_op, +/* 0xc3 */ x86emuOp2_illegal_op, +/* 0xc4 */ x86emuOp2_illegal_op, +/* 0xc5 */ x86emuOp2_illegal_op, +/* 0xc6 */ x86emuOp2_illegal_op, +/* 0xc7 */ x86emuOp2_illegal_op, +/* 0xc8 */ x86emuOp2_illegal_op, /* TODO: bswap */ +/* 0xc9 */ x86emuOp2_illegal_op, /* TODO: bswap */ +/* 0xca */ x86emuOp2_illegal_op, /* TODO: bswap */ +/* 0xcb */ x86emuOp2_illegal_op, /* TODO: bswap */ +/* 0xcc */ x86emuOp2_illegal_op, /* TODO: bswap */ +/* 0xcd */ x86emuOp2_illegal_op, /* TODO: bswap */ +/* 0xce */ x86emuOp2_illegal_op, /* TODO: bswap */ +/* 0xcf */ x86emuOp2_illegal_op, /* TODO: bswap */ + +/* 0xd0 */ x86emuOp2_illegal_op, +/* 0xd1 */ x86emuOp2_illegal_op, +/* 0xd2 */ x86emuOp2_illegal_op, +/* 0xd3 */ x86emuOp2_illegal_op, +/* 0xd4 */ x86emuOp2_illegal_op, +/* 0xd5 */ x86emuOp2_illegal_op, +/* 0xd6 */ x86emuOp2_illegal_op, +/* 0xd7 */ x86emuOp2_illegal_op, +/* 0xd8 */ x86emuOp2_illegal_op, +/* 0xd9 */ x86emuOp2_illegal_op, +/* 0xda */ x86emuOp2_illegal_op, +/* 0xdb */ x86emuOp2_illegal_op, +/* 0xdc */ x86emuOp2_illegal_op, +/* 0xdd */ x86emuOp2_illegal_op, +/* 0xde */ x86emuOp2_illegal_op, +/* 0xdf */ x86emuOp2_illegal_op, + +/* 0xe0 */ x86emuOp2_illegal_op, +/* 0xe1 */ x86emuOp2_illegal_op, +/* 0xe2 */ x86emuOp2_illegal_op, +/* 0xe3 */ x86emuOp2_illegal_op, +/* 0xe4 */ x86emuOp2_illegal_op, +/* 0xe5 */ x86emuOp2_illegal_op, +/* 0xe6 */ x86emuOp2_illegal_op, +/* 0xe7 */ x86emuOp2_illegal_op, +/* 0xe8 */ x86emuOp2_illegal_op, +/* 0xe9 */ x86emuOp2_illegal_op, +/* 0xea */ x86emuOp2_illegal_op, +/* 0xeb */ x86emuOp2_illegal_op, +/* 0xec */ x86emuOp2_illegal_op, +/* 0xed */ x86emuOp2_illegal_op, +/* 0xee */ x86emuOp2_illegal_op, +/* 0xef */ x86emuOp2_illegal_op, + +/* 0xf0 */ x86emuOp2_illegal_op, +/* 0xf1 */ x86emuOp2_illegal_op, +/* 0xf2 */ x86emuOp2_illegal_op, +/* 0xf3 */ x86emuOp2_illegal_op, +/* 0xf4 */ x86emuOp2_illegal_op, +/* 0xf5 */ x86emuOp2_illegal_op, +/* 0xf6 */ x86emuOp2_illegal_op, +/* 0xf7 */ x86emuOp2_illegal_op, +/* 0xf8 */ x86emuOp2_illegal_op, +/* 0xf9 */ x86emuOp2_illegal_op, +/* 0xfa */ x86emuOp2_illegal_op, +/* 0xfb */ x86emuOp2_illegal_op, +/* 0xfc */ x86emuOp2_illegal_op, +/* 0xfd */ x86emuOp2_illegal_op, +/* 0xfe */ x86emuOp2_illegal_op, +/* 0xff */ x86emuOp2_illegal_op, +}; diff --git a/qemu/roms/u-boot/drivers/bios_emulator/x86emu/prim_ops.c b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/prim_ops.c new file mode 100644 index 000000000..5f6c795fb --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/prim_ops.c @@ -0,0 +1,2442 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: This file contains the code to implement the primitive +* machine operations used by the emulation code in ops.c +* +* Carry Chain Calculation +* +* This represents a somewhat expensive calculation which is +* apparently required to emulate the setting of the OF343364 and AF flag. +* The latter is not so important, but the former is. The overflow +* flag is the XOR of the top two bits of the carry chain for an +* addition (similar for subtraction). Since we do not want to +* simulate the addition in a bitwise manner, we try to calculate the +* carry chain given the two operands and the result. +* +* So, given the following table, which represents the addition of two +* bits, we can derive a formula for the carry chain. +* +* a b cin r cout +* 0 0 0 0 0 +* 0 0 1 1 0 +* 0 1 0 1 0 +* 0 1 1 0 1 +* 1 0 0 1 0 +* 1 0 1 0 1 +* 1 1 0 0 1 +* 1 1 1 1 1 +* +* Construction of table for cout: +* +* ab +* r \ 00 01 11 10 +* |------------------ +* 0 | 0 1 1 1 +* 1 | 0 0 1 0 +* +* By inspection, one gets: cc = ab + r'(a + b) +* +* That represents alot of operations, but NO CHOICE.... +* +* Borrow Chain Calculation. +* +* The following table represents the subtraction of two bits, from +* which we can derive a formula for the borrow chain. +* +* a b bin r bout +* 0 0 0 0 0 +* 0 0 1 1 1 +* 0 1 0 1 1 +* 0 1 1 0 1 +* 1 0 0 1 0 +* 1 0 1 0 0 +* 1 1 0 0 0 +* 1 1 1 1 1 +* +* Construction of table for cout: +* +* ab +* r \ 00 01 11 10 +* |------------------ +* 0 | 0 1 0 0 +* 1 | 1 1 1 0 +* +* By inspection, one gets: bc = a'b + r(a' + b) +* +****************************************************************************/ + +#include + +#define PRIM_OPS_NO_REDEFINE_ASM +#include "x86emu/x86emui.h" + +/*------------------------- Global Variables ------------------------------*/ + +static u32 x86emu_parity_tab[8] = +{ + 0x96696996, + 0x69969669, + 0x69969669, + 0x96696996, + 0x69969669, + 0x96696996, + 0x96696996, + 0x69969669, +}; + +#define PARITY(x) (((x86emu_parity_tab[(x) / 32] >> ((x) % 32)) & 1) == 0) +#define XOR2(x) (((x) ^ ((x)>>1)) & 0x1) + +/*----------------------------- Implementation ----------------------------*/ + + +/*--------- Side effects helper functions -------*/ + +/**************************************************************************** +REMARKS: +implements side efects for byte operations that don't overflow +****************************************************************************/ + +static void set_parity_flag(u32 res) +{ + CONDITIONAL_SET_FLAG(PARITY(res & 0xFF), F_PF); +} + +static void set_szp_flags_8(u8 res) +{ + CONDITIONAL_SET_FLAG(res & 0x80, F_SF); + CONDITIONAL_SET_FLAG(res == 0, F_ZF); + set_parity_flag(res); +} + +static void set_szp_flags_16(u16 res) +{ + CONDITIONAL_SET_FLAG(res & 0x8000, F_SF); + CONDITIONAL_SET_FLAG(res == 0, F_ZF); + set_parity_flag(res); +} + +static void set_szp_flags_32(u32 res) +{ + CONDITIONAL_SET_FLAG(res & 0x80000000, F_SF); + CONDITIONAL_SET_FLAG(res == 0, F_ZF); + set_parity_flag(res); +} + +static void no_carry_byte_side_eff(u8 res) +{ + CLEAR_FLAG(F_OF); + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_AF); + set_szp_flags_8(res); +} + +static void no_carry_word_side_eff(u16 res) +{ + CLEAR_FLAG(F_OF); + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_AF); + set_szp_flags_16(res); +} + +static void no_carry_long_side_eff(u32 res) +{ + CLEAR_FLAG(F_OF); + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_AF); + set_szp_flags_32(res); +} + +static void calc_carry_chain(int bits, u32 d, u32 s, u32 res, int set_carry) +{ + u32 cc; + + cc = (s & d) | ((~res) & (s | d)); + CONDITIONAL_SET_FLAG(XOR2(cc >> (bits - 2)), F_OF); + CONDITIONAL_SET_FLAG(cc & 0x8, F_AF); + if (set_carry) { + CONDITIONAL_SET_FLAG(res & (1 << bits), F_CF); + } +} + +static void calc_borrow_chain(int bits, u32 d, u32 s, u32 res, int set_carry) +{ + u32 bc; + + bc = (res & (~d | s)) | (~d & s); + CONDITIONAL_SET_FLAG(XOR2(bc >> (bits - 2)), F_OF); + CONDITIONAL_SET_FLAG(bc & 0x8, F_AF); + if (set_carry) { + CONDITIONAL_SET_FLAG(bc & (1 << (bits - 1)), F_CF); + } +} + +/**************************************************************************** +REMARKS: +Implements the AAA instruction and side effects. +****************************************************************************/ +u16 aaa_word(u16 d) +{ + u16 res; + if ((d & 0xf) > 0x9 || ACCESS_FLAG(F_AF)) { + d += 0x6; + d += 0x100; + SET_FLAG(F_AF); + SET_FLAG(F_CF); + } else { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_AF); + } + res = (u16)(d & 0xFF0F); + set_szp_flags_16(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the AAA instruction and side effects. +****************************************************************************/ +u16 aas_word(u16 d) +{ + u16 res; + if ((d & 0xf) > 0x9 || ACCESS_FLAG(F_AF)) { + d -= 0x6; + d -= 0x100; + SET_FLAG(F_AF); + SET_FLAG(F_CF); + } else { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_AF); + } + res = (u16)(d & 0xFF0F); + set_szp_flags_16(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the AAD instruction and side effects. +****************************************************************************/ +u16 aad_word(u16 d) +{ + u16 l; + u8 hb, lb; + + hb = (u8)((d >> 8) & 0xff); + lb = (u8)((d & 0xff)); + l = (u16)((lb + 10 * hb) & 0xFF); + + no_carry_byte_side_eff(l & 0xFF); + return l; +} + +/**************************************************************************** +REMARKS: +Implements the AAM instruction and side effects. +****************************************************************************/ +u16 aam_word(u8 d) +{ + u16 h, l; + + h = (u16)(d / 10); + l = (u16)(d % 10); + l |= (u16)(h << 8); + + no_carry_byte_side_eff(l & 0xFF); + return l; +} + +/**************************************************************************** +REMARKS: +Implements the ADC instruction and side effects. +****************************************************************************/ +u8 adc_byte(u8 d, u8 s) +{ + u32 res; /* all operands in native machine order */ + + res = d + s; + if (ACCESS_FLAG(F_CF)) res++; + + set_szp_flags_8(res); + calc_carry_chain(8,s,d,res,1); + + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the ADC instruction and side effects. +****************************************************************************/ +u16 adc_word(u16 d, u16 s) +{ + u32 res; /* all operands in native machine order */ + + res = d + s; + if (ACCESS_FLAG(F_CF)) + res++; + + set_szp_flags_16((u16)res); + calc_carry_chain(16,s,d,res,1); + + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the ADC instruction and side effects. +****************************************************************************/ +u32 adc_long(u32 d, u32 s) +{ + u32 lo; /* all operands in native machine order */ + u32 hi; + u32 res; + + lo = (d & 0xFFFF) + (s & 0xFFFF); + res = d + s; + + if (ACCESS_FLAG(F_CF)) { + lo++; + res++; + } + + hi = (lo >> 16) + (d >> 16) + (s >> 16); + + set_szp_flags_32(res); + calc_carry_chain(32,s,d,res,0); + + CONDITIONAL_SET_FLAG(hi & 0x10000, F_CF); + + return res; +} + +/**************************************************************************** +REMARKS: +Implements the ADD instruction and side effects. +****************************************************************************/ +u8 add_byte(u8 d, u8 s) +{ + u32 res; /* all operands in native machine order */ + + res = d + s; + set_szp_flags_8((u8)res); + calc_carry_chain(8,s,d,res,1); + + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the ADD instruction and side effects. +****************************************************************************/ +u16 add_word(u16 d, u16 s) +{ + u32 res; /* all operands in native machine order */ + + res = d + s; + set_szp_flags_16((u16)res); + calc_carry_chain(16,s,d,res,1); + + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the ADD instruction and side effects. +****************************************************************************/ +u32 add_long(u32 d, u32 s) +{ + u32 res; + + res = d + s; + set_szp_flags_32(res); + calc_carry_chain(32,s,d,res,0); + + CONDITIONAL_SET_FLAG(res < d || res < s, F_CF); + + return res; +} + +/**************************************************************************** +REMARKS: +Implements the AND instruction and side effects. +****************************************************************************/ +u8 and_byte(u8 d, u8 s) +{ + u8 res; /* all operands in native machine order */ + + res = d & s; + + no_carry_byte_side_eff(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the AND instruction and side effects. +****************************************************************************/ +u16 and_word(u16 d, u16 s) +{ + u16 res; /* all operands in native machine order */ + + res = d & s; + + no_carry_word_side_eff(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the AND instruction and side effects. +****************************************************************************/ +u32 and_long(u32 d, u32 s) +{ + u32 res; /* all operands in native machine order */ + + res = d & s; + no_carry_long_side_eff(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the CMP instruction and side effects. +****************************************************************************/ +u8 cmp_byte(u8 d, u8 s) +{ + u32 res; /* all operands in native machine order */ + + res = d - s; + set_szp_flags_8((u8)res); + calc_borrow_chain(8, d, s, res, 1); + + return d; +} + +/**************************************************************************** +REMARKS: +Implements the CMP instruction and side effects. +****************************************************************************/ +u16 cmp_word(u16 d, u16 s) +{ + u32 res; /* all operands in native machine order */ + + res = d - s; + set_szp_flags_16((u16)res); + calc_borrow_chain(16, d, s, res, 1); + + return d; +} + +/**************************************************************************** +REMARKS: +Implements the CMP instruction and side effects. +****************************************************************************/ +u32 cmp_long(u32 d, u32 s) +{ + u32 res; /* all operands in native machine order */ + + res = d - s; + set_szp_flags_32(res); + calc_borrow_chain(32, d, s, res, 1); + + return d; +} + +/**************************************************************************** +REMARKS: +Implements the DAA instruction and side effects. +****************************************************************************/ +u8 daa_byte(u8 d) +{ + u32 res = d; + if ((d & 0xf) > 9 || ACCESS_FLAG(F_AF)) { + res += 6; + SET_FLAG(F_AF); + } + if (res > 0x9F || ACCESS_FLAG(F_CF)) { + res += 0x60; + SET_FLAG(F_CF); + } + set_szp_flags_8((u8)res); + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the DAS instruction and side effects. +****************************************************************************/ +u8 das_byte(u8 d) +{ + if ((d & 0xf) > 9 || ACCESS_FLAG(F_AF)) { + d -= 6; + SET_FLAG(F_AF); + } + if (d > 0x9F || ACCESS_FLAG(F_CF)) { + d -= 0x60; + SET_FLAG(F_CF); + } + set_szp_flags_8(d); + return d; +} + +/**************************************************************************** +REMARKS: +Implements the DEC instruction and side effects. +****************************************************************************/ +u8 dec_byte(u8 d) +{ + u32 res; /* all operands in native machine order */ + + res = d - 1; + set_szp_flags_8((u8)res); + calc_borrow_chain(8, d, 1, res, 0); + + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the DEC instruction and side effects. +****************************************************************************/ +u16 dec_word(u16 d) +{ + u32 res; /* all operands in native machine order */ + + res = d - 1; + set_szp_flags_16((u16)res); + calc_borrow_chain(16, d, 1, res, 0); + + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the DEC instruction and side effects. +****************************************************************************/ +u32 dec_long(u32 d) +{ + u32 res; /* all operands in native machine order */ + + res = d - 1; + + set_szp_flags_32(res); + calc_borrow_chain(32, d, 1, res, 0); + + return res; +} + +/**************************************************************************** +REMARKS: +Implements the INC instruction and side effects. +****************************************************************************/ +u8 inc_byte(u8 d) +{ + u32 res; /* all operands in native machine order */ + + res = d + 1; + set_szp_flags_8((u8)res); + calc_carry_chain(8, d, 1, res, 0); + + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the INC instruction and side effects. +****************************************************************************/ +u16 inc_word(u16 d) +{ + u32 res; /* all operands in native machine order */ + + res = d + 1; + set_szp_flags_16((u16)res); + calc_carry_chain(16, d, 1, res, 0); + + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the INC instruction and side effects. +****************************************************************************/ +u32 inc_long(u32 d) +{ + u32 res; /* all operands in native machine order */ + + res = d + 1; + set_szp_flags_32(res); + calc_carry_chain(32, d, 1, res, 0); + + return res; +} + +/**************************************************************************** +REMARKS: +Implements the OR instruction and side effects. +****************************************************************************/ +u8 or_byte(u8 d, u8 s) +{ + u8 res; /* all operands in native machine order */ + + res = d | s; + no_carry_byte_side_eff(res); + + return res; +} + +/**************************************************************************** +REMARKS: +Implements the OR instruction and side effects. +****************************************************************************/ +u16 or_word(u16 d, u16 s) +{ + u16 res; /* all operands in native machine order */ + + res = d | s; + no_carry_word_side_eff(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the OR instruction and side effects. +****************************************************************************/ +u32 or_long(u32 d, u32 s) +{ + u32 res; /* all operands in native machine order */ + + res = d | s; + no_carry_long_side_eff(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the OR instruction and side effects. +****************************************************************************/ +u8 neg_byte(u8 s) +{ + u8 res; + + CONDITIONAL_SET_FLAG(s != 0, F_CF); + res = (u8)-s; + set_szp_flags_8(res); + calc_borrow_chain(8, 0, s, res, 0); + + return res; +} + +/**************************************************************************** +REMARKS: +Implements the OR instruction and side effects. +****************************************************************************/ +u16 neg_word(u16 s) +{ + u16 res; + + CONDITIONAL_SET_FLAG(s != 0, F_CF); + res = (u16)-s; + set_szp_flags_16((u16)res); + calc_borrow_chain(16, 0, s, res, 0); + + return res; +} + +/**************************************************************************** +REMARKS: +Implements the OR instruction and side effects. +****************************************************************************/ +u32 neg_long(u32 s) +{ + u32 res; + + CONDITIONAL_SET_FLAG(s != 0, F_CF); + res = (u32)-s; + set_szp_flags_32(res); + calc_borrow_chain(32, 0, s, res, 0); + + return res; +} + +/**************************************************************************** +REMARKS: +Implements the NOT instruction and side effects. +****************************************************************************/ +u8 not_byte(u8 s) +{ + return ~s; +} + +/**************************************************************************** +REMARKS: +Implements the NOT instruction and side effects. +****************************************************************************/ +u16 not_word(u16 s) +{ + return ~s; +} + +/**************************************************************************** +REMARKS: +Implements the NOT instruction and side effects. +****************************************************************************/ +u32 not_long(u32 s) +{ + return ~s; +} + +/**************************************************************************** +REMARKS: +Implements the RCL instruction and side effects. +****************************************************************************/ +u8 rcl_byte(u8 d, u8 s) +{ + unsigned int res, cnt, mask, cf; + + /* s is the rotate distance. It varies from 0 - 8. */ + /* have + + CF B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0 + + want to rotate through the carry by "s" bits. We could + loop, but that's inefficient. So the width is 9, + and we split into three parts: + + The new carry flag (was B_n) + the stuff in B_n-1 .. B_0 + the stuff in B_7 .. B_n+1 + + The new rotate is done mod 9, and given this, + for a rotation of n bits (mod 9) the new carry flag is + then located n bits from the MSB. The low part is + then shifted up cnt bits, and the high part is or'd + in. Using CAPS for new values, and lowercase for the + original values, this can be expressed as: + + IF n > 0 + 1) CF <- b_(8-n) + 2) B_(7) .. B_(n) <- b_(8-(n+1)) .. b_0 + 3) B_(n-1) <- cf + 4) B_(n-2) .. B_0 <- b_7 .. b_(8-(n-1)) + */ + res = d; + if ((cnt = s % 9) != 0) { + /* extract the new CARRY FLAG. */ + /* CF <- b_(8-n) */ + cf = (d >> (8 - cnt)) & 0x1; + + /* get the low stuff which rotated + into the range B_7 .. B_cnt */ + /* B_(7) .. B_(n) <- b_(8-(n+1)) .. b_0 */ + /* note that the right hand side done by the mask */ + res = (d << cnt) & 0xff; + + /* now the high stuff which rotated around + into the positions B_cnt-2 .. B_0 */ + /* B_(n-2) .. B_0 <- b_7 .. b_(8-(n-1)) */ + /* shift it downward, 7-(n-2) = 9-n positions. + and mask off the result before or'ing in. + */ + mask = (1 << (cnt - 1)) - 1; + res |= (d >> (9 - cnt)) & mask; + + /* if the carry flag was set, or it in. */ + if (ACCESS_FLAG(F_CF)) { /* carry flag is set */ + /* B_(n-1) <- cf */ + res |= 1 << (cnt - 1); + } + /* set the new carry flag, based on the variable "cf" */ + CONDITIONAL_SET_FLAG(cf, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of CF and the most significant bit. Blecck. */ + /* parenthesized this expression since it appears to + be causing OF to be misset */ + CONDITIONAL_SET_FLAG(cnt == 1 && XOR2(cf + ((res >> 6) & 0x2)), + F_OF); + + } + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the RCL instruction and side effects. +****************************************************************************/ +u16 rcl_word(u16 d, u8 s) +{ + unsigned int res, cnt, mask, cf; + + res = d; + if ((cnt = s % 17) != 0) { + cf = (d >> (16 - cnt)) & 0x1; + res = (d << cnt) & 0xffff; + mask = (1 << (cnt - 1)) - 1; + res |= (d >> (17 - cnt)) & mask; + if (ACCESS_FLAG(F_CF)) { + res |= 1 << (cnt - 1); + } + CONDITIONAL_SET_FLAG(cf, F_CF); + CONDITIONAL_SET_FLAG(cnt == 1 && XOR2(cf + ((res >> 14) & 0x2)), + F_OF); + } + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the RCL instruction and side effects. +****************************************************************************/ +u32 rcl_long(u32 d, u8 s) +{ + u32 res, cnt, mask, cf; + + res = d; + if ((cnt = s % 33) != 0) { + cf = (d >> (32 - cnt)) & 0x1; + res = (d << cnt) & 0xffffffff; + mask = (1 << (cnt - 1)) - 1; + res |= (d >> (33 - cnt)) & mask; + if (ACCESS_FLAG(F_CF)) { /* carry flag is set */ + res |= 1 << (cnt - 1); + } + CONDITIONAL_SET_FLAG(cf, F_CF); + CONDITIONAL_SET_FLAG(cnt == 1 && XOR2(cf + ((res >> 30) & 0x2)), + F_OF); + } + return res; +} + +/**************************************************************************** +REMARKS: +Implements the RCR instruction and side effects. +****************************************************************************/ +u8 rcr_byte(u8 d, u8 s) +{ + u32 res, cnt; + u32 mask, cf, ocf = 0; + + /* rotate right through carry */ + /* + s is the rotate distance. It varies from 0 - 8. + d is the byte object rotated. + + have + + CF B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0 + + The new rotate is done mod 9, and given this, + for a rotation of n bits (mod 9) the new carry flag is + then located n bits from the LSB. The low part is + then shifted up cnt bits, and the high part is or'd + in. Using CAPS for new values, and lowercase for the + original values, this can be expressed as: + + IF n > 0 + 1) CF <- b_(n-1) + 2) B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) + 3) B_(8-n) <- cf + 4) B_(7) .. B_(8-(n-1)) <- b_(n-2) .. b_(0) + */ + res = d; + if ((cnt = s % 9) != 0) { + /* extract the new CARRY FLAG. */ + /* CF <- b_(n-1) */ + if (cnt == 1) { + cf = d & 0x1; + /* note hackery here. Access_flag(..) evaluates to either + 0 if flag not set + non-zero if flag is set. + doing access_flag(..) != 0 casts that into either + 0..1 in any representation of the flags register + (i.e. packed bit array or unpacked.) + */ + ocf = ACCESS_FLAG(F_CF) != 0; + } else + cf = (d >> (cnt - 1)) & 0x1; + + /* B_(8-(n+1)) .. B_(0) <- b_(7) .. b_n */ + /* note that the right hand side done by the mask + This is effectively done by shifting the + object to the right. The result must be masked, + in case the object came in and was treated + as a negative number. Needed??? */ + + mask = (1 << (8 - cnt)) - 1; + res = (d >> cnt) & mask; + + /* now the high stuff which rotated around + into the positions B_cnt-2 .. B_0 */ + /* B_(7) .. B_(8-(n-1)) <- b_(n-2) .. b_(0) */ + /* shift it downward, 7-(n-2) = 9-n positions. + and mask off the result before or'ing in. + */ + res |= (d << (9 - cnt)); + + /* if the carry flag was set, or it in. */ + if (ACCESS_FLAG(F_CF)) { /* carry flag is set */ + /* B_(8-n) <- cf */ + res |= 1 << (8 - cnt); + } + /* set the new carry flag, based on the variable "cf" */ + CONDITIONAL_SET_FLAG(cf, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of CF and the most significant bit. Blecck. */ + /* parenthesized... */ + if (cnt == 1) { + CONDITIONAL_SET_FLAG(XOR2(ocf + ((d >> 6) & 0x2)), + F_OF); + } + } + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the RCR instruction and side effects. +****************************************************************************/ +u16 rcr_word(u16 d, u8 s) +{ + u32 res, cnt; + u32 mask, cf, ocf = 0; + + /* rotate right through carry */ + res = d; + if ((cnt = s % 17) != 0) { + if (cnt == 1) { + cf = d & 0x1; + ocf = ACCESS_FLAG(F_CF) != 0; + } else + cf = (d >> (cnt - 1)) & 0x1; + mask = (1 << (16 - cnt)) - 1; + res = (d >> cnt) & mask; + res |= (d << (17 - cnt)); + if (ACCESS_FLAG(F_CF)) { + res |= 1 << (16 - cnt); + } + CONDITIONAL_SET_FLAG(cf, F_CF); + if (cnt == 1) { + CONDITIONAL_SET_FLAG(XOR2(ocf + ((d >> 14) & 0x2)), + F_OF); + } + } + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the RCR instruction and side effects. +****************************************************************************/ +u32 rcr_long(u32 d, u8 s) +{ + u32 res, cnt; + u32 mask, cf, ocf = 0; + + /* rotate right through carry */ + res = d; + if ((cnt = s % 33) != 0) { + if (cnt == 1) { + cf = d & 0x1; + ocf = ACCESS_FLAG(F_CF) != 0; + } else + cf = (d >> (cnt - 1)) & 0x1; + mask = (1 << (32 - cnt)) - 1; + res = (d >> cnt) & mask; + if (cnt != 1) + res |= (d << (33 - cnt)); + if (ACCESS_FLAG(F_CF)) { /* carry flag is set */ + res |= 1 << (32 - cnt); + } + CONDITIONAL_SET_FLAG(cf, F_CF); + if (cnt == 1) { + CONDITIONAL_SET_FLAG(XOR2(ocf + ((d >> 30) & 0x2)), + F_OF); + } + } + return res; +} + +/**************************************************************************** +REMARKS: +Implements the ROL instruction and side effects. +****************************************************************************/ +u8 rol_byte(u8 d, u8 s) +{ + unsigned int res, cnt, mask; + + /* rotate left */ + /* + s is the rotate distance. It varies from 0 - 8. + d is the byte object rotated. + + have + + CF B_7 ... B_0 + + The new rotate is done mod 8. + Much simpler than the "rcl" or "rcr" operations. + + IF n > 0 + 1) B_(7) .. B_(n) <- b_(8-(n+1)) .. b_(0) + 2) B_(n-1) .. B_(0) <- b_(7) .. b_(8-n) + */ + res = d; + if ((cnt = s % 8) != 0) { + /* B_(7) .. B_(n) <- b_(8-(n+1)) .. b_(0) */ + res = (d << cnt); + + /* B_(n-1) .. B_(0) <- b_(7) .. b_(8-n) */ + mask = (1 << cnt) - 1; + res |= (d >> (8 - cnt)) & mask; + + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res & 0x1, F_CF); + /* OVERFLOW is set *IFF* s==1, then it is the + xor of CF and the most significant bit. Blecck. */ + CONDITIONAL_SET_FLAG(s == 1 && + XOR2((res & 0x1) + ((res >> 6) & 0x2)), + F_OF); + } if (s != 0) { + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res & 0x1, F_CF); + } + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the ROL instruction and side effects. +****************************************************************************/ +u16 rol_word(u16 d, u8 s) +{ + unsigned int res, cnt, mask; + + res = d; + if ((cnt = s % 16) != 0) { + res = (d << cnt); + mask = (1 << cnt) - 1; + res |= (d >> (16 - cnt)) & mask; + CONDITIONAL_SET_FLAG(res & 0x1, F_CF); + CONDITIONAL_SET_FLAG(s == 1 && + XOR2((res & 0x1) + ((res >> 14) & 0x2)), + F_OF); + } if (s != 0) { + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res & 0x1, F_CF); + } + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the ROL instruction and side effects. +****************************************************************************/ +u32 rol_long(u32 d, u8 s) +{ + u32 res, cnt, mask; + + res = d; + if ((cnt = s % 32) != 0) { + res = (d << cnt); + mask = (1 << cnt) - 1; + res |= (d >> (32 - cnt)) & mask; + CONDITIONAL_SET_FLAG(res & 0x1, F_CF); + CONDITIONAL_SET_FLAG(s == 1 && + XOR2((res & 0x1) + ((res >> 30) & 0x2)), + F_OF); + } if (s != 0) { + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res & 0x1, F_CF); + } + return res; +} + +/**************************************************************************** +REMARKS: +Implements the ROR instruction and side effects. +****************************************************************************/ +u8 ror_byte(u8 d, u8 s) +{ + unsigned int res, cnt, mask; + + /* rotate right */ + /* + s is the rotate distance. It varies from 0 - 8. + d is the byte object rotated. + + have + + B_7 ... B_0 + + The rotate is done mod 8. + + IF n > 0 + 1) B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) + 2) B_(7) .. B_(8-n) <- b_(n-1) .. b_(0) + */ + res = d; + if ((cnt = s % 8) != 0) { /* not a typo, do nada if cnt==0 */ + /* B_(7) .. B_(8-n) <- b_(n-1) .. b_(0) */ + res = (d << (8 - cnt)); + + /* B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) */ + mask = (1 << (8 - cnt)) - 1; + res |= (d >> (cnt)) & mask; + + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res & 0x80, F_CF); + /* OVERFLOW is set *IFF* s==1, then it is the + xor of the two most significant bits. Blecck. */ + CONDITIONAL_SET_FLAG(s == 1 && XOR2(res >> 6), F_OF); + } else if (s != 0) { + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res & 0x80, F_CF); + } + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the ROR instruction and side effects. +****************************************************************************/ +u16 ror_word(u16 d, u8 s) +{ + unsigned int res, cnt, mask; + + res = d; + if ((cnt = s % 16) != 0) { + res = (d << (16 - cnt)); + mask = (1 << (16 - cnt)) - 1; + res |= (d >> (cnt)) & mask; + CONDITIONAL_SET_FLAG(res & 0x8000, F_CF); + CONDITIONAL_SET_FLAG(s == 1 && XOR2(res >> 14), F_OF); + } else if (s != 0) { + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res & 0x8000, F_CF); + } + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the ROR instruction and side effects. +****************************************************************************/ +u32 ror_long(u32 d, u8 s) +{ + u32 res, cnt, mask; + + res = d; + if ((cnt = s % 32) != 0) { + res = (d << (32 - cnt)); + mask = (1 << (32 - cnt)) - 1; + res |= (d >> (cnt)) & mask; + CONDITIONAL_SET_FLAG(res & 0x80000000, F_CF); + CONDITIONAL_SET_FLAG(s == 1 && XOR2(res >> 30), F_OF); + } else if (s != 0) { + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res & 0x80000000, F_CF); + } + return res; +} + +/**************************************************************************** +REMARKS: +Implements the SHL instruction and side effects. +****************************************************************************/ +u8 shl_byte(u8 d, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 8) { + cnt = s % 8; + + /* last bit shifted out goes into carry flag */ + if (cnt > 0) { + res = d << cnt; + cf = d & (1 << (8 - cnt)); + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_8((u8)res); + } else { + res = (u8) d; + } + + if (cnt == 1) { + /* Needs simplification. */ + CONDITIONAL_SET_FLAG( + (((res & 0x80) == 0x80) ^ + (ACCESS_FLAG(F_CF) != 0)), + /* was (M.x86.R_FLG&F_CF)==F_CF)), */ + F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CONDITIONAL_SET_FLAG((d << (s-1)) & 0x80, F_CF); + CLEAR_FLAG(F_OF); + CLEAR_FLAG(F_SF); + SET_FLAG(F_PF); + SET_FLAG(F_ZF); + } + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the SHL instruction and side effects. +****************************************************************************/ +u16 shl_word(u16 d, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 16) { + cnt = s % 16; + if (cnt > 0) { + res = d << cnt; + cf = d & (1 << (16 - cnt)); + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_16((u16)res); + } else { + res = (u16) d; + } + + if (cnt == 1) { + CONDITIONAL_SET_FLAG( + (((res & 0x8000) == 0x8000) ^ + (ACCESS_FLAG(F_CF) != 0)), + F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CONDITIONAL_SET_FLAG((d << (s-1)) & 0x8000, F_CF); + CLEAR_FLAG(F_OF); + CLEAR_FLAG(F_SF); + SET_FLAG(F_PF); + SET_FLAG(F_ZF); + } + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the SHL instruction and side effects. +****************************************************************************/ +u32 shl_long(u32 d, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 32) { + cnt = s % 32; + if (cnt > 0) { + res = d << cnt; + cf = d & (1 << (32 - cnt)); + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_32((u32)res); + } else { + res = d; + } + if (cnt == 1) { + CONDITIONAL_SET_FLAG((((res & 0x80000000) == 0x80000000) ^ + (ACCESS_FLAG(F_CF) != 0)), F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CONDITIONAL_SET_FLAG((d << (s-1)) & 0x80000000, F_CF); + CLEAR_FLAG(F_OF); + CLEAR_FLAG(F_SF); + SET_FLAG(F_PF); + SET_FLAG(F_ZF); + } + return res; +} + +/**************************************************************************** +REMARKS: +Implements the SHR instruction and side effects. +****************************************************************************/ +u8 shr_byte(u8 d, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 8) { + cnt = s % 8; + if (cnt > 0) { + cf = d & (1 << (cnt - 1)); + res = d >> cnt; + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_8((u8)res); + } else { + res = (u8) d; + } + + if (cnt == 1) { + CONDITIONAL_SET_FLAG(XOR2(res >> 6), F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CONDITIONAL_SET_FLAG((d >> (s-1)) & 0x1, F_CF); + CLEAR_FLAG(F_OF); + CLEAR_FLAG(F_SF); + SET_FLAG(F_PF); + SET_FLAG(F_ZF); + } + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the SHR instruction and side effects. +****************************************************************************/ +u16 shr_word(u16 d, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 16) { + cnt = s % 16; + if (cnt > 0) { + cf = d & (1 << (cnt - 1)); + res = d >> cnt; + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_16((u16)res); + } else { + res = d; + } + + if (cnt == 1) { + CONDITIONAL_SET_FLAG(XOR2(res >> 14), F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + SET_FLAG(F_ZF); + CLEAR_FLAG(F_SF); + CLEAR_FLAG(F_PF); + } + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the SHR instruction and side effects. +****************************************************************************/ +u32 shr_long(u32 d, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 32) { + cnt = s % 32; + if (cnt > 0) { + cf = d & (1 << (cnt - 1)); + res = d >> cnt; + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_32((u32)res); + } else { + res = d; + } + if (cnt == 1) { + CONDITIONAL_SET_FLAG(XOR2(res >> 30), F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + SET_FLAG(F_ZF); + CLEAR_FLAG(F_SF); + CLEAR_FLAG(F_PF); + } + return res; +} + +/**************************************************************************** +REMARKS: +Implements the SAR instruction and side effects. +****************************************************************************/ +u8 sar_byte(u8 d, u8 s) +{ + unsigned int cnt, res, cf, mask, sf; + + res = d; + sf = d & 0x80; + cnt = s % 8; + if (cnt > 0 && cnt < 8) { + mask = (1 << (8 - cnt)) - 1; + cf = d & (1 << (cnt - 1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, F_CF); + if (sf) { + res |= ~mask; + } + set_szp_flags_8((u8)res); + } else if (cnt >= 8) { + if (sf) { + res = 0xff; + SET_FLAG(F_CF); + CLEAR_FLAG(F_ZF); + SET_FLAG(F_SF); + SET_FLAG(F_PF); + } else { + res = 0; + CLEAR_FLAG(F_CF); + SET_FLAG(F_ZF); + CLEAR_FLAG(F_SF); + CLEAR_FLAG(F_PF); + } + } + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the SAR instruction and side effects. +****************************************************************************/ +u16 sar_word(u16 d, u8 s) +{ + unsigned int cnt, res, cf, mask, sf; + + sf = d & 0x8000; + cnt = s % 16; + res = d; + if (cnt > 0 && cnt < 16) { + mask = (1 << (16 - cnt)) - 1; + cf = d & (1 << (cnt - 1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, F_CF); + if (sf) { + res |= ~mask; + } + set_szp_flags_16((u16)res); + } else if (cnt >= 16) { + if (sf) { + res = 0xffff; + SET_FLAG(F_CF); + CLEAR_FLAG(F_ZF); + SET_FLAG(F_SF); + SET_FLAG(F_PF); + } else { + res = 0; + CLEAR_FLAG(F_CF); + SET_FLAG(F_ZF); + CLEAR_FLAG(F_SF); + CLEAR_FLAG(F_PF); + } + } + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the SAR instruction and side effects. +****************************************************************************/ +u32 sar_long(u32 d, u8 s) +{ + u32 cnt, res, cf, mask, sf; + + sf = d & 0x80000000; + cnt = s % 32; + res = d; + if (cnt > 0 && cnt < 32) { + mask = (1 << (32 - cnt)) - 1; + cf = d & (1 << (cnt - 1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, F_CF); + if (sf) { + res |= ~mask; + } + set_szp_flags_32(res); + } else if (cnt >= 32) { + if (sf) { + res = 0xffffffff; + SET_FLAG(F_CF); + CLEAR_FLAG(F_ZF); + SET_FLAG(F_SF); + SET_FLAG(F_PF); + } else { + res = 0; + CLEAR_FLAG(F_CF); + SET_FLAG(F_ZF); + CLEAR_FLAG(F_SF); + CLEAR_FLAG(F_PF); + } + } + return res; +} + +/**************************************************************************** +REMARKS: +Implements the SHLD instruction and side effects. +****************************************************************************/ +u16 shld_word (u16 d, u16 fill, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 16) { + cnt = s % 16; + if (cnt > 0) { + res = (d << cnt) | (fill >> (16-cnt)); + cf = d & (1 << (16 - cnt)); + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_16((u16)res); + } else { + res = d; + } + if (cnt == 1) { + CONDITIONAL_SET_FLAG((((res & 0x8000) == 0x8000) ^ + (ACCESS_FLAG(F_CF) != 0)), F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CONDITIONAL_SET_FLAG((d << (s-1)) & 0x8000, F_CF); + CLEAR_FLAG(F_OF); + CLEAR_FLAG(F_SF); + SET_FLAG(F_PF); + SET_FLAG(F_ZF); + } + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the SHLD instruction and side effects. +****************************************************************************/ +u32 shld_long (u32 d, u32 fill, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 32) { + cnt = s % 32; + if (cnt > 0) { + res = (d << cnt) | (fill >> (32-cnt)); + cf = d & (1 << (32 - cnt)); + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_32((u32)res); + } else { + res = d; + } + if (cnt == 1) { + CONDITIONAL_SET_FLAG((((res & 0x80000000) == 0x80000000) ^ + (ACCESS_FLAG(F_CF) != 0)), F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CONDITIONAL_SET_FLAG((d << (s-1)) & 0x80000000, F_CF); + CLEAR_FLAG(F_OF); + CLEAR_FLAG(F_SF); + SET_FLAG(F_PF); + SET_FLAG(F_ZF); + } + return res; +} + +/**************************************************************************** +REMARKS: +Implements the SHRD instruction and side effects. +****************************************************************************/ +u16 shrd_word (u16 d, u16 fill, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 16) { + cnt = s % 16; + if (cnt > 0) { + cf = d & (1 << (cnt - 1)); + res = (d >> cnt) | (fill << (16 - cnt)); + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_16((u16)res); + } else { + res = d; + } + + if (cnt == 1) { + CONDITIONAL_SET_FLAG(XOR2(res >> 14), F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + SET_FLAG(F_ZF); + CLEAR_FLAG(F_SF); + CLEAR_FLAG(F_PF); + } + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the SHRD instruction and side effects. +****************************************************************************/ +u32 shrd_long (u32 d, u32 fill, u8 s) +{ + unsigned int cnt, res, cf; + + if (s < 32) { + cnt = s % 32; + if (cnt > 0) { + cf = d & (1 << (cnt - 1)); + res = (d >> cnt) | (fill << (32 - cnt)); + CONDITIONAL_SET_FLAG(cf, F_CF); + set_szp_flags_32((u32)res); + } else { + res = d; + } + if (cnt == 1) { + CONDITIONAL_SET_FLAG(XOR2(res >> 30), F_OF); + } else { + CLEAR_FLAG(F_OF); + } + } else { + res = 0; + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + SET_FLAG(F_ZF); + CLEAR_FLAG(F_SF); + CLEAR_FLAG(F_PF); + } + return res; +} + +/**************************************************************************** +REMARKS: +Implements the SBB instruction and side effects. +****************************************************************************/ +u8 sbb_byte(u8 d, u8 s) +{ + u32 res; /* all operands in native machine order */ + u32 bc; + + if (ACCESS_FLAG(F_CF)) + res = d - s - 1; + else + res = d - s; + set_szp_flags_8((u8)res); + + /* calculate the borrow chain. See note at top */ + bc = (res & (~d | s)) | (~d & s); + CONDITIONAL_SET_FLAG(bc & 0x80, F_CF); + CONDITIONAL_SET_FLAG(XOR2(bc >> 6), F_OF); + CONDITIONAL_SET_FLAG(bc & 0x8, F_AF); + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the SBB instruction and side effects. +****************************************************************************/ +u16 sbb_word(u16 d, u16 s) +{ + u32 res; /* all operands in native machine order */ + u32 bc; + + if (ACCESS_FLAG(F_CF)) + res = d - s - 1; + else + res = d - s; + set_szp_flags_16((u16)res); + + /* calculate the borrow chain. See note at top */ + bc = (res & (~d | s)) | (~d & s); + CONDITIONAL_SET_FLAG(bc & 0x8000, F_CF); + CONDITIONAL_SET_FLAG(XOR2(bc >> 14), F_OF); + CONDITIONAL_SET_FLAG(bc & 0x8, F_AF); + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the SBB instruction and side effects. +****************************************************************************/ +u32 sbb_long(u32 d, u32 s) +{ + u32 res; /* all operands in native machine order */ + u32 bc; + + if (ACCESS_FLAG(F_CF)) + res = d - s - 1; + else + res = d - s; + + set_szp_flags_32(res); + + /* calculate the borrow chain. See note at top */ + bc = (res & (~d | s)) | (~d & s); + CONDITIONAL_SET_FLAG(bc & 0x80000000, F_CF); + CONDITIONAL_SET_FLAG(XOR2(bc >> 30), F_OF); + CONDITIONAL_SET_FLAG(bc & 0x8, F_AF); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the SUB instruction and side effects. +****************************************************************************/ +u8 sub_byte(u8 d, u8 s) +{ + u32 res; /* all operands in native machine order */ + u32 bc; + + res = d - s; + set_szp_flags_8((u8)res); + + /* calculate the borrow chain. See note at top */ + bc = (res & (~d | s)) | (~d & s); + CONDITIONAL_SET_FLAG(bc & 0x80, F_CF); + CONDITIONAL_SET_FLAG(XOR2(bc >> 6), F_OF); + CONDITIONAL_SET_FLAG(bc & 0x8, F_AF); + return (u8)res; +} + +/**************************************************************************** +REMARKS: +Implements the SUB instruction and side effects. +****************************************************************************/ +u16 sub_word(u16 d, u16 s) +{ + u32 res; /* all operands in native machine order */ + u32 bc; + + res = d - s; + set_szp_flags_16((u16)res); + + /* calculate the borrow chain. See note at top */ + bc = (res & (~d | s)) | (~d & s); + CONDITIONAL_SET_FLAG(bc & 0x8000, F_CF); + CONDITIONAL_SET_FLAG(XOR2(bc >> 14), F_OF); + CONDITIONAL_SET_FLAG(bc & 0x8, F_AF); + return (u16)res; +} + +/**************************************************************************** +REMARKS: +Implements the SUB instruction and side effects. +****************************************************************************/ +u32 sub_long(u32 d, u32 s) +{ + u32 res; /* all operands in native machine order */ + u32 bc; + + res = d - s; + set_szp_flags_32(res); + + /* calculate the borrow chain. See note at top */ + bc = (res & (~d | s)) | (~d & s); + CONDITIONAL_SET_FLAG(bc & 0x80000000, F_CF); + CONDITIONAL_SET_FLAG(XOR2(bc >> 30), F_OF); + CONDITIONAL_SET_FLAG(bc & 0x8, F_AF); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the TEST instruction and side effects. +****************************************************************************/ +void test_byte(u8 d, u8 s) +{ + u32 res; /* all operands in native machine order */ + + res = d & s; + + CLEAR_FLAG(F_OF); + set_szp_flags_8((u8)res); + /* AF == dont care */ + CLEAR_FLAG(F_CF); +} + +/**************************************************************************** +REMARKS: +Implements the TEST instruction and side effects. +****************************************************************************/ +void test_word(u16 d, u16 s) +{ + u32 res; /* all operands in native machine order */ + + res = d & s; + + CLEAR_FLAG(F_OF); + set_szp_flags_16((u16)res); + /* AF == dont care */ + CLEAR_FLAG(F_CF); +} + +/**************************************************************************** +REMARKS: +Implements the TEST instruction and side effects. +****************************************************************************/ +void test_long(u32 d, u32 s) +{ + u32 res; /* all operands in native machine order */ + + res = d & s; + + CLEAR_FLAG(F_OF); + set_szp_flags_32(res); + /* AF == dont care */ + CLEAR_FLAG(F_CF); +} + +/**************************************************************************** +REMARKS: +Implements the XOR instruction and side effects. +****************************************************************************/ +u8 xor_byte(u8 d, u8 s) +{ + u8 res; /* all operands in native machine order */ + + res = d ^ s; + no_carry_byte_side_eff(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the XOR instruction and side effects. +****************************************************************************/ +u16 xor_word(u16 d, u16 s) +{ + u16 res; /* all operands in native machine order */ + + res = d ^ s; + no_carry_word_side_eff(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the XOR instruction and side effects. +****************************************************************************/ +u32 xor_long(u32 d, u32 s) +{ + u32 res; /* all operands in native machine order */ + + res = d ^ s; + no_carry_long_side_eff(res); + return res; +} + +/**************************************************************************** +REMARKS: +Implements the IMUL instruction and side effects. +****************************************************************************/ +void imul_byte(u8 s) +{ + s16 res = (s16)((s8)M.x86.R_AL * (s8)s); + + M.x86.R_AX = res; + if (((M.x86.R_AL & 0x80) == 0 && M.x86.R_AH == 0x00) || + ((M.x86.R_AL & 0x80) != 0 && M.x86.R_AH == 0xFF)) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } +} + +/**************************************************************************** +REMARKS: +Implements the IMUL instruction and side effects. +****************************************************************************/ +void imul_word(u16 s) +{ + s32 res = (s16)M.x86.R_AX * (s16)s; + + M.x86.R_AX = (u16)res; + M.x86.R_DX = (u16)(res >> 16); + if (((M.x86.R_AX & 0x8000) == 0 && M.x86.R_DX == 0x0000) || + ((M.x86.R_AX & 0x8000) != 0 && M.x86.R_DX == 0xFFFF)) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } +} + +/**************************************************************************** +REMARKS: +Implements the IMUL instruction and side effects. +****************************************************************************/ +void imul_long_direct(u32 *res_lo, u32* res_hi,u32 d, u32 s) +{ +#ifdef __HAS_LONG_LONG__ + s64 res = (s32)d * (s32)s; + + *res_lo = (u32)res; + *res_hi = (u32)(res >> 32); +#else + u32 d_lo,d_hi,d_sign; + u32 s_lo,s_hi,s_sign; + u32 rlo_lo,rlo_hi,rhi_lo; + + if ((d_sign = d & 0x80000000) != 0) + d = -d; + d_lo = d & 0xFFFF; + d_hi = d >> 16; + if ((s_sign = s & 0x80000000) != 0) + s = -s; + s_lo = s & 0xFFFF; + s_hi = s >> 16; + rlo_lo = d_lo * s_lo; + rlo_hi = (d_hi * s_lo + d_lo * s_hi) + (rlo_lo >> 16); + rhi_lo = d_hi * s_hi + (rlo_hi >> 16); + *res_lo = (rlo_hi << 16) | (rlo_lo & 0xFFFF); + *res_hi = rhi_lo; + if (d_sign != s_sign) { + d = ~*res_lo; + s = (((d & 0xFFFF) + 1) >> 16) + (d >> 16); + *res_lo = ~*res_lo+1; + *res_hi = ~*res_hi+(s >> 16); + } +#endif +} + +/**************************************************************************** +REMARKS: +Implements the IMUL instruction and side effects. +****************************************************************************/ +void imul_long(u32 s) +{ + imul_long_direct(&M.x86.R_EAX,&M.x86.R_EDX,M.x86.R_EAX,s); + if (((M.x86.R_EAX & 0x80000000) == 0 && M.x86.R_EDX == 0x00000000) || + ((M.x86.R_EAX & 0x80000000) != 0 && M.x86.R_EDX == 0xFFFFFFFF)) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } +} + +/**************************************************************************** +REMARKS: +Implements the MUL instruction and side effects. +****************************************************************************/ +void mul_byte(u8 s) +{ + u16 res = (u16)(M.x86.R_AL * s); + + M.x86.R_AX = res; + if (M.x86.R_AH == 0) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } +} + +/**************************************************************************** +REMARKS: +Implements the MUL instruction and side effects. +****************************************************************************/ +void mul_word(u16 s) +{ + u32 res = M.x86.R_AX * s; + + M.x86.R_AX = (u16)res; + M.x86.R_DX = (u16)(res >> 16); + if (M.x86.R_DX == 0) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } +} + +/**************************************************************************** +REMARKS: +Implements the MUL instruction and side effects. +****************************************************************************/ +void mul_long(u32 s) +{ +#ifdef __HAS_LONG_LONG__ + u64 res = (u32)M.x86.R_EAX * (u32)s; + + M.x86.R_EAX = (u32)res; + M.x86.R_EDX = (u32)(res >> 32); +#else + u32 a,a_lo,a_hi; + u32 s_lo,s_hi; + u32 rlo_lo,rlo_hi,rhi_lo; + + a = M.x86.R_EAX; + a_lo = a & 0xFFFF; + a_hi = a >> 16; + s_lo = s & 0xFFFF; + s_hi = s >> 16; + rlo_lo = a_lo * s_lo; + rlo_hi = (a_hi * s_lo + a_lo * s_hi) + (rlo_lo >> 16); + rhi_lo = a_hi * s_hi + (rlo_hi >> 16); + M.x86.R_EAX = (rlo_hi << 16) | (rlo_lo & 0xFFFF); + M.x86.R_EDX = rhi_lo; +#endif + if (M.x86.R_EDX == 0) { + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_OF); + } else { + SET_FLAG(F_CF); + SET_FLAG(F_OF); + } +} + +/**************************************************************************** +REMARKS: +Implements the IDIV instruction and side effects. +****************************************************************************/ +void idiv_byte(u8 s) +{ + s32 dvd, div, mod; + + dvd = (s16)M.x86.R_AX; + if (s == 0) { + x86emu_intr_raise(0); + return; + } + div = dvd / (s8)s; + mod = dvd % (s8)s; + if (abs(div) > 0x7f) { + x86emu_intr_raise(0); + return; + } + M.x86.R_AL = (s8) div; + M.x86.R_AH = (s8) mod; +} + +/**************************************************************************** +REMARKS: +Implements the IDIV instruction and side effects. +****************************************************************************/ +void idiv_word(u16 s) +{ + s32 dvd, div, mod; + + dvd = (((s32)M.x86.R_DX) << 16) | M.x86.R_AX; + if (s == 0) { + x86emu_intr_raise(0); + return; + } + div = dvd / (s16)s; + mod = dvd % (s16)s; + if (abs(div) > 0x7fff) { + x86emu_intr_raise(0); + return; + } + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_SF); + CONDITIONAL_SET_FLAG(div == 0, F_ZF); + set_parity_flag(mod); + + M.x86.R_AX = (u16)div; + M.x86.R_DX = (u16)mod; +} + +/**************************************************************************** +REMARKS: +Implements the IDIV instruction and side effects. +****************************************************************************/ +void idiv_long(u32 s) +{ +#ifdef __HAS_LONG_LONG__ + s64 dvd, div, mod; + + dvd = (((s64)M.x86.R_EDX) << 32) | M.x86.R_EAX; + if (s == 0) { + x86emu_intr_raise(0); + return; + } + div = dvd / (s32)s; + mod = dvd % (s32)s; + if (abs(div) > 0x7fffffff) { + x86emu_intr_raise(0); + return; + } +#else + s32 div = 0, mod; + s32 h_dvd = M.x86.R_EDX; + u32 l_dvd = M.x86.R_EAX; + u32 abs_s = s & 0x7FFFFFFF; + u32 abs_h_dvd = h_dvd & 0x7FFFFFFF; + u32 h_s = abs_s >> 1; + u32 l_s = abs_s << 31; + int counter = 31; + int carry; + + if (s == 0) { + x86emu_intr_raise(0); + return; + } + do { + div <<= 1; + carry = (l_dvd >= l_s) ? 0 : 1; + + if (abs_h_dvd < (h_s + carry)) { + h_s >>= 1; + l_s = abs_s << (--counter); + continue; + } else { + abs_h_dvd -= (h_s + carry); + l_dvd = carry ? ((0xFFFFFFFF - l_s) + l_dvd + 1) + : (l_dvd - l_s); + h_s >>= 1; + l_s = abs_s << (--counter); + div |= 1; + continue; + } + + } while (counter > -1); + /* overflow */ + if (abs_h_dvd || (l_dvd > abs_s)) { + x86emu_intr_raise(0); + return; + } + /* sign */ + div |= ((h_dvd & 0x10000000) ^ (s & 0x10000000)); + mod = l_dvd; + +#endif + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_AF); + CLEAR_FLAG(F_SF); + SET_FLAG(F_ZF); + set_parity_flag(mod); + + M.x86.R_EAX = (u32)div; + M.x86.R_EDX = (u32)mod; +} + +/**************************************************************************** +REMARKS: +Implements the DIV instruction and side effects. +****************************************************************************/ +void div_byte(u8 s) +{ + u32 dvd, div, mod; + + dvd = M.x86.R_AX; + if (s == 0) { + x86emu_intr_raise(0); + return; + } + div = dvd / (u8)s; + mod = dvd % (u8)s; + if (abs(div) > 0xff) { + x86emu_intr_raise(0); + return; + } + M.x86.R_AL = (u8)div; + M.x86.R_AH = (u8)mod; +} + +/**************************************************************************** +REMARKS: +Implements the DIV instruction and side effects. +****************************************************************************/ +void div_word(u16 s) +{ + u32 dvd, div, mod; + + dvd = (((u32)M.x86.R_DX) << 16) | M.x86.R_AX; + if (s == 0) { + x86emu_intr_raise(0); + return; + } + div = dvd / (u16)s; + mod = dvd % (u16)s; + if (abs(div) > 0xffff) { + x86emu_intr_raise(0); + return; + } + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_SF); + CONDITIONAL_SET_FLAG(div == 0, F_ZF); + set_parity_flag(mod); + + M.x86.R_AX = (u16)div; + M.x86.R_DX = (u16)mod; +} + +/**************************************************************************** +REMARKS: +Implements the DIV instruction and side effects. +****************************************************************************/ +void div_long(u32 s) +{ +#ifdef __HAS_LONG_LONG__ + u64 dvd, div, mod; + + dvd = (((u64)M.x86.R_EDX) << 32) | M.x86.R_EAX; + if (s == 0) { + x86emu_intr_raise(0); + return; + } + div = dvd / (u32)s; + mod = dvd % (u32)s; + if (abs(div) > 0xffffffff) { + x86emu_intr_raise(0); + return; + } +#else + s32 div = 0, mod; + s32 h_dvd = M.x86.R_EDX; + u32 l_dvd = M.x86.R_EAX; + + u32 h_s = s; + u32 l_s = 0; + int counter = 32; + int carry; + + if (s == 0) { + x86emu_intr_raise(0); + return; + } + do { + div <<= 1; + carry = (l_dvd >= l_s) ? 0 : 1; + + if (h_dvd < (h_s + carry)) { + h_s >>= 1; + l_s = s << (--counter); + continue; + } else { + h_dvd -= (h_s + carry); + l_dvd = carry ? ((0xFFFFFFFF - l_s) + l_dvd + 1) + : (l_dvd - l_s); + h_s >>= 1; + l_s = s << (--counter); + div |= 1; + continue; + } + + } while (counter > -1); + /* overflow */ + if (h_dvd || (l_dvd > s)) { + x86emu_intr_raise(0); + return; + } + mod = l_dvd; +#endif + CLEAR_FLAG(F_CF); + CLEAR_FLAG(F_AF); + CLEAR_FLAG(F_SF); + SET_FLAG(F_ZF); + set_parity_flag(mod); + + M.x86.R_EAX = (u32)div; + M.x86.R_EDX = (u32)mod; +} + +/**************************************************************************** +REMARKS: +Implements the IN string instruction and side effects. +****************************************************************************/ + +static void single_in(int size) +{ + if(size == 1) + store_data_byte_abs(M.x86.R_ES, M.x86.R_DI,(*sys_inb)(M.x86.R_DX)); + else if (size == 2) + store_data_word_abs(M.x86.R_ES, M.x86.R_DI,(*sys_inw)(M.x86.R_DX)); + else + store_data_long_abs(M.x86.R_ES, M.x86.R_DI,(*sys_inl)(M.x86.R_DX)); +} + +void ins(int size) +{ + int inc = size; + + if (ACCESS_FLAG(F_DF)) { + inc = -size; + } + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* dont care whether REPE or REPNE */ + /* in until CX is ZERO. */ + u32 count = ((M.x86.mode & SYSMODE_PREFIX_DATA) ? + M.x86.R_ECX : M.x86.R_CX); + + while (count--) { + single_in(size); + M.x86.R_DI += inc; + } + M.x86.R_CX = 0; + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + M.x86.R_ECX = 0; + } + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + single_in(size); + M.x86.R_DI += inc; + } +} + +/**************************************************************************** +REMARKS: +Implements the OUT string instruction and side effects. +****************************************************************************/ + +static void single_out(int size) +{ + if(size == 1) + (*sys_outb)(M.x86.R_DX,fetch_data_byte_abs(M.x86.R_ES, M.x86.R_SI)); + else if (size == 2) + (*sys_outw)(M.x86.R_DX,fetch_data_word_abs(M.x86.R_ES, M.x86.R_SI)); + else + (*sys_outl)(M.x86.R_DX,fetch_data_long_abs(M.x86.R_ES, M.x86.R_SI)); +} + +void outs(int size) +{ + int inc = size; + + if (ACCESS_FLAG(F_DF)) { + inc = -size; + } + if (M.x86.mode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + /* dont care whether REPE or REPNE */ + /* out until CX is ZERO. */ + u32 count = ((M.x86.mode & SYSMODE_PREFIX_DATA) ? + M.x86.R_ECX : M.x86.R_CX); + while (count--) { + single_out(size); + M.x86.R_SI += inc; + } + M.x86.R_CX = 0; + if (M.x86.mode & SYSMODE_PREFIX_DATA) { + M.x86.R_ECX = 0; + } + M.x86.mode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + single_out(size); + M.x86.R_SI += inc; + } +} + +/**************************************************************************** +PARAMETERS: +addr - Address to fetch word from + +REMARKS: +Fetches a word from emulator memory using an absolute address. +****************************************************************************/ +u16 mem_access_word(int addr) +{ +DB( if (CHECK_MEM_ACCESS()) + x86emu_check_mem_access(addr);) + return (*sys_rdw)(addr); +} + +/**************************************************************************** +REMARKS: +Pushes a word onto the stack. + +NOTE: Do not inline this, as (*sys_wrX) is already inline! +****************************************************************************/ +void push_word(u16 w) +{ +DB( if (CHECK_SP_ACCESS()) + x86emu_check_sp_access();) + M.x86.R_SP -= 2; + (*sys_wrw)(((u32)M.x86.R_SS << 4) + M.x86.R_SP, w); +} + +/**************************************************************************** +REMARKS: +Pushes a long onto the stack. + +NOTE: Do not inline this, as (*sys_wrX) is already inline! +****************************************************************************/ +void push_long(u32 w) +{ +DB( if (CHECK_SP_ACCESS()) + x86emu_check_sp_access();) + M.x86.R_SP -= 4; + (*sys_wrl)(((u32)M.x86.R_SS << 4) + M.x86.R_SP, w); +} + +/**************************************************************************** +REMARKS: +Pops a word from the stack. + +NOTE: Do not inline this, as (*sys_rdX) is already inline! +****************************************************************************/ +u16 pop_word(void) +{ + u16 res; + +DB( if (CHECK_SP_ACCESS()) + x86emu_check_sp_access();) + res = (*sys_rdw)(((u32)M.x86.R_SS << 4) + M.x86.R_SP); + M.x86.R_SP += 2; + return res; +} + +/**************************************************************************** +REMARKS: +Pops a long from the stack. + +NOTE: Do not inline this, as (*sys_rdX) is already inline! +****************************************************************************/ +u32 pop_long(void) +{ + u32 res; + +DB( if (CHECK_SP_ACCESS()) + x86emu_check_sp_access();) + res = (*sys_rdl)(((u32)M.x86.R_SS << 4) + M.x86.R_SP); + M.x86.R_SP += 4; + return res; +} diff --git a/qemu/roms/u-boot/drivers/bios_emulator/x86emu/sys.c b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/sys.c new file mode 100644 index 000000000..21f9730be --- /dev/null +++ b/qemu/roms/u-boot/drivers/bios_emulator/x86emu/sys.c @@ -0,0 +1,323 @@ +/**************************************************************************** +* +* Realmode X86 Emulator Library +* +* Copyright (C) 1991-2004 SciTech Software, Inc. +* Copyright (C) David Mosberger-Tang +* Copyright (C) 1999 Egbert Eich +* +* ======================================================================== +* +* Permission to use, copy, modify, distribute, and sell this software and +* its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and that +* both that copyright notice and this permission notice appear in +* supporting documentation, and that the name of the authors not be used +* in advertising or publicity pertaining to distribution of the software +* without specific, written prior permission. The authors makes no +* representations about the suitability of this software for any purpose. +* It is provided "as is" without express or implied warranty. +* +* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, 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. +* +* ======================================================================== +* +* Language: ANSI C +* Environment: Any +* Developer: Kendall Bennett +* +* Description: This file includes subroutines which are related to +* programmed I/O and memory access. Included in this module +* are default functions that do nothing. For real uses these +* functions will have to be overriden by the user library. +* +****************************************************************************/ + +#include +#include "x86emu/x86emui.h" + +/*------------------------- Global Variables ------------------------------*/ + +X86EMU_sysEnv _X86EMU_env; /* Global emulator machine state */ +X86EMU_intrFuncs _X86EMU_intrTab[256]; + +int debug_intr; + +/*----------------------------- Implementation ----------------------------*/ + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read + +RETURNS: +Byte value read from emulator memory. + +REMARKS: +Reads a byte value from the emulator memory. +****************************************************************************/ +u8 X86API rdb(u32 addr) +{ + return 0; +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read + +RETURNS: +Word value read from emulator memory. + +REMARKS: +Reads a word value from the emulator memory. +****************************************************************************/ +u16 X86API rdw(u32 addr) +{ + return 0; +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read + +RETURNS: +Long value read from emulator memory. +REMARKS: +Reads a long value from the emulator memory. +****************************************************************************/ +u32 X86API rdl(u32 addr) +{ + return 0; +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read +val - Value to store + +REMARKS: +Writes a byte value to emulator memory. +****************************************************************************/ +void X86API wrb(u32 addr, u8 val) +{ +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read +val - Value to store + +REMARKS: +Writes a word value to emulator memory. +****************************************************************************/ +void X86API wrw(u32 addr, u16 val) +{ +} + +/**************************************************************************** +PARAMETERS: +addr - Emulator memory address to read +val - Value to store + +REMARKS: +Writes a long value to emulator memory. +****************************************************************************/ +void X86API wrl(u32 addr, u32 val) +{ +} + +/**************************************************************************** +PARAMETERS: +addr - PIO address to read +RETURN: +0 +REMARKS: +Default PIO byte read function. Doesn't perform real inb. +****************************************************************************/ +static u8 X86API p_inb(X86EMU_pioAddr addr) +{ + DB(if (DEBUG_IO_TRACE()) + printk("inb %#04x \n", addr);) + return 0; +} + +/**************************************************************************** +PARAMETERS: +addr - PIO address to read +RETURN: +0 +REMARKS: +Default PIO word read function. Doesn't perform real inw. +****************************************************************************/ +static u16 X86API p_inw(X86EMU_pioAddr addr) +{ + DB(if (DEBUG_IO_TRACE()) + printk("inw %#04x \n", addr);) + return 0; +} + +/**************************************************************************** +PARAMETERS: +addr - PIO address to read +RETURN: +0 +REMARKS: +Default PIO long read function. Doesn't perform real inl. +****************************************************************************/ +static u32 X86API p_inl(X86EMU_pioAddr addr) +{ + DB(if (DEBUG_IO_TRACE()) + printk("inl %#04x \n", addr);) + return 0; +} + +/**************************************************************************** +PARAMETERS: +addr - PIO address to write +val - Value to store +REMARKS: +Default PIO byte write function. Doesn't perform real outb. +****************************************************************************/ +static void X86API p_outb(X86EMU_pioAddr addr, u8 val) +{ + DB(if (DEBUG_IO_TRACE()) + printk("outb %#02x -> %#04x \n", val, addr);) + return; +} + +/**************************************************************************** +PARAMETERS: +addr - PIO address to write +val - Value to store +REMARKS: +Default PIO word write function. Doesn't perform real outw. +****************************************************************************/ +static void X86API p_outw(X86EMU_pioAddr addr, u16 val) +{ + DB(if (DEBUG_IO_TRACE()) + printk("outw %#04x -> %#04x \n", val, addr);) + return; +} + +/**************************************************************************** +PARAMETERS: +addr - PIO address to write +val - Value to store +REMARKS: +Default PIO ;ong write function. Doesn't perform real outl. +****************************************************************************/ +static void X86API p_outl(X86EMU_pioAddr addr, u32 val) +{ + DB(if (DEBUG_IO_TRACE()) + printk("outl %#08x -> %#04x \n", val, addr);) + return; +} + +/*------------------------- Global Variables ------------------------------*/ + +u8(X86APIP sys_rdb) (u32 addr) = rdb; +u16(X86APIP sys_rdw) (u32 addr) = rdw; +u32(X86APIP sys_rdl) (u32 addr) = rdl; +void (X86APIP sys_wrb) (u32 addr, u8 val) = wrb; +void (X86APIP sys_wrw) (u32 addr, u16 val) = wrw; +void (X86APIP sys_wrl) (u32 addr, u32 val) = wrl; +u8(X86APIP sys_inb) (X86EMU_pioAddr addr) = p_inb; +u16(X86APIP sys_inw) (X86EMU_pioAddr addr) = p_inw; +u32(X86APIP sys_inl) (X86EMU_pioAddr addr) = p_inl; +void (X86APIP sys_outb) (X86EMU_pioAddr addr, u8 val) = p_outb; +void (X86APIP sys_outw) (X86EMU_pioAddr addr, u16 val) = p_outw; +void (X86APIP sys_outl) (X86EMU_pioAddr addr, u32 val) = p_outl; + +/*----------------------------- Setup -------------------------------------*/ + +/**************************************************************************** +PARAMETERS: +funcs - New memory function pointers to make active + +REMARKS: +This function is used to set the pointers to functions which access +memory space, allowing the user application to override these functions +and hook them out as necessary for their application. +****************************************************************************/ +void X86EMU_setupMemFuncs(X86EMU_memFuncs * funcs) +{ + sys_rdb = funcs->rdb; + sys_rdw = funcs->rdw; + sys_rdl = funcs->rdl; + sys_wrb = funcs->wrb; + sys_wrw = funcs->wrw; + sys_wrl = funcs->wrl; +} + +/**************************************************************************** +PARAMETERS: +funcs - New programmed I/O function pointers to make active + +REMARKS: +This function is used to set the pointers to functions which access +I/O space, allowing the user application to override these functions +and hook them out as necessary for their application. +****************************************************************************/ +void X86EMU_setupPioFuncs(X86EMU_pioFuncs * funcs) +{ + sys_inb = funcs->inb; + sys_inw = funcs->inw; + sys_inl = funcs->inl; + sys_outb = funcs->outb; + sys_outw = funcs->outw; + sys_outl = funcs->outl; +} + +/**************************************************************************** +PARAMETERS: +funcs - New interrupt vector table to make active + +REMARKS: +This function is used to set the pointers to functions which handle +interrupt processing in the emulator, allowing the user application to +hook interrupts as necessary for their application. Any interrupts that +are not hooked by the user application, and reflected and handled internally +in the emulator via the interrupt vector table. This allows the application +to get control when the code being emulated executes specific software +interrupts. +****************************************************************************/ +void X86EMU_setupIntrFuncs(X86EMU_intrFuncs funcs[]) +{ + int i; + + for (i = 0; i < 256; i++) + _X86EMU_intrTab[i] = NULL; + if (funcs) { + for (i = 0; i < 256; i++) + _X86EMU_intrTab[i] = funcs[i]; + } +} + +/**************************************************************************** +PARAMETERS: +int - New software interrupt to prepare for + +REMARKS: +This function is used to set up the emulator state to exceute a software +interrupt. This can be used by the user application code to allow an +interrupt to be hooked, examined and then reflected back to the emulator +so that the code in the emulator will continue processing the software +interrupt as per normal. This essentially allows system code to actively +hook and handle certain software interrupts as necessary. +****************************************************************************/ +void X86EMU_prepareForInt(int num) +{ + push_word((u16) M.x86.R_FLG); + CLEAR_FLAG(F_IF); + CLEAR_FLAG(F_TF); + push_word(M.x86.R_CS); + M.x86.R_CS = mem_access_word(num * 4 + 2); + push_word(M.x86.R_IP); + M.x86.R_IP = mem_access_word(num * 4); + M.x86.intr = 0; +} diff --git a/qemu/roms/u-boot/drivers/block/Makefile b/qemu/roms/u-boot/drivers/block/Makefile new file mode 100644 index 000000000..8697da426 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/Makefile @@ -0,0 +1,23 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_SCSI_AHCI) += ahci.o +obj-$(CONFIG_ATA_PIIX) += ata_piix.o +obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o +obj-$(CONFIG_FSL_SATA) += fsl_sata.o +obj-$(CONFIG_IDE_FTIDE020) += ftide020.o +obj-$(CONFIG_LIBATA) += libata.o +obj-$(CONFIG_MVSATA_IDE) += mvsata_ide.o +obj-$(CONFIG_MX51_PATA) += mxc_ata.o +obj-$(CONFIG_PATA_BFIN) += pata_bfin.o +obj-$(CONFIG_SATA_DWC) += sata_dwc.o +obj-$(CONFIG_SATA_SIL3114) += sata_sil3114.o +obj-$(CONFIG_SATA_SIL) += sata_sil.o +obj-$(CONFIG_IDE_SIL680) += sil680.o +obj-$(CONFIG_SANDBOX) += sandbox.o +obj-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o +obj-$(CONFIG_SYSTEMACE) += systemace.o diff --git a/qemu/roms/u-boot/drivers/block/ahci.c b/qemu/roms/u-boot/drivers/block/ahci.c new file mode 100644 index 000000000..c8f65739e --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/ahci.c @@ -0,0 +1,994 @@ +/* + * Copyright (C) Freescale Semiconductor, Inc. 2006. + * Author: Jason Jin + * Zhang Wei + * + * SPDX-License-Identifier: GPL-2.0+ + * + * with the reference on libata and ahci drvier in kernel + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int ata_io_flush(u8 port); + +struct ahci_probe_ent *probe_ent = NULL; +u16 *ataid[AHCI_MAX_PORTS]; + +#define writel_with_flush(a,b) do { writel(a,b); readl(b); } while (0) + +/* + * Some controllers limit number of blocks they can read/write at once. + * Contemporary SSD devices work much faster if the read/write size is aligned + * to a power of 2. Let's set default to 128 and allowing to be overwritten if + * needed. + */ +#ifndef MAX_SATA_BLOCKS_READ_WRITE +#define MAX_SATA_BLOCKS_READ_WRITE 0x80 +#endif + +/* Maximum timeouts for each event */ +#define WAIT_MS_SPINUP 20000 +#define WAIT_MS_DATAIO 5000 +#define WAIT_MS_FLUSH 5000 +#define WAIT_MS_LINKUP 40 + +static inline u32 ahci_port_base(u32 base, u32 port) +{ + return base + 0x100 + (port * 0x80); +} + + +static void ahci_setup_port(struct ahci_ioports *port, unsigned long base, + unsigned int port_idx) +{ + base = ahci_port_base(base, port_idx); + + port->cmd_addr = base; + port->scr_addr = base + PORT_SCR; +} + + +#define msleep(a) udelay(a * 1000) + +static void ahci_dcache_flush_range(unsigned begin, unsigned len) +{ + const unsigned long start = begin; + const unsigned long end = start + len; + + debug("%s: flush dcache: [%#lx, %#lx)\n", __func__, start, end); + flush_dcache_range(start, end); +} + +/* + * SATA controller DMAs to physical RAM. Ensure data from the + * controller is invalidated from dcache; next access comes from + * physical RAM. + */ +static void ahci_dcache_invalidate_range(unsigned begin, unsigned len) +{ + const unsigned long start = begin; + const unsigned long end = start + len; + + debug("%s: invalidate dcache: [%#lx, %#lx)\n", __func__, start, end); + invalidate_dcache_range(start, end); +} + +/* + * Ensure data for SATA controller is flushed out of dcache and + * written to physical memory. + */ +static void ahci_dcache_flush_sata_cmd(struct ahci_ioports *pp) +{ + ahci_dcache_flush_range((unsigned long)pp->cmd_slot, + AHCI_PORT_PRIV_DMA_SZ); +} + +static int waiting_for_cmd_completed(volatile u8 *offset, + int timeout_msec, + u32 sign) +{ + int i; + u32 status; + + for (i = 0; ((status = readl(offset)) & sign) && i < timeout_msec; i++) + msleep(1); + + return (i < timeout_msec) ? 0 : -1; +} + +int __weak ahci_link_up(struct ahci_probe_ent *probe_ent, u8 port) +{ + u32 tmp; + int j = 0; + u8 *port_mmio = (u8 *)probe_ent->port[port].port_mmio; + + /* + * Bring up SATA link. + * SATA link bringup time is usually less than 1 ms; only very + * rarely has it taken between 1-2 ms. Never seen it above 2 ms. + */ + while (j < WAIT_MS_LINKUP) { + tmp = readl(port_mmio + PORT_SCR_STAT); + tmp &= PORT_SCR_STAT_DET_MASK; + if (tmp == PORT_SCR_STAT_DET_PHYRDY) + return 0; + udelay(1000); + j++; + } + return 1; +} + +static int ahci_host_init(struct ahci_probe_ent *probe_ent) +{ +#ifndef CONFIG_SCSI_AHCI_PLAT + pci_dev_t pdev = probe_ent->dev; + u16 tmp16; + unsigned short vendor; +#endif + volatile u8 *mmio = (volatile u8 *)probe_ent->mmio_base; + u32 tmp, cap_save, cmd; + int i, j, ret; + volatile u8 *port_mmio; + u32 port_map; + + debug("ahci_host_init: start\n"); + + cap_save = readl(mmio + HOST_CAP); + cap_save &= ((1 << 28) | (1 << 17)); + cap_save |= (1 << 27); /* Staggered Spin-up. Not needed. */ + + /* global controller reset */ + tmp = readl(mmio + HOST_CTL); + if ((tmp & HOST_RESET) == 0) + writel_with_flush(tmp | HOST_RESET, mmio + HOST_CTL); + + /* reset must complete within 1 second, or + * the hardware should be considered fried. + */ + i = 1000; + do { + udelay(1000); + tmp = readl(mmio + HOST_CTL); + if (!i--) { + debug("controller reset failed (0x%x)\n", tmp); + return -1; + } + } while (tmp & HOST_RESET); + + writel_with_flush(HOST_AHCI_EN, mmio + HOST_CTL); + writel(cap_save, mmio + HOST_CAP); + writel_with_flush(0xf, mmio + HOST_PORTS_IMPL); + +#ifndef CONFIG_SCSI_AHCI_PLAT + pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor); + + if (vendor == PCI_VENDOR_ID_INTEL) { + u16 tmp16; + pci_read_config_word(pdev, 0x92, &tmp16); + tmp16 |= 0xf; + pci_write_config_word(pdev, 0x92, tmp16); + } +#endif + probe_ent->cap = readl(mmio + HOST_CAP); + probe_ent->port_map = readl(mmio + HOST_PORTS_IMPL); + port_map = probe_ent->port_map; + probe_ent->n_ports = (probe_ent->cap & 0x1f) + 1; + + debug("cap 0x%x port_map 0x%x n_ports %d\n", + probe_ent->cap, probe_ent->port_map, probe_ent->n_ports); + + if (probe_ent->n_ports > CONFIG_SYS_SCSI_MAX_SCSI_ID) + probe_ent->n_ports = CONFIG_SYS_SCSI_MAX_SCSI_ID; + + for (i = 0; i < probe_ent->n_ports; i++) { + if (!(port_map & (1 << i))) + continue; + probe_ent->port[i].port_mmio = ahci_port_base((u32) mmio, i); + port_mmio = (u8 *) probe_ent->port[i].port_mmio; + ahci_setup_port(&probe_ent->port[i], (unsigned long)mmio, i); + + /* make sure port is not active */ + tmp = readl(port_mmio + PORT_CMD); + if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | + PORT_CMD_FIS_RX | PORT_CMD_START)) { + debug("Port %d is active. Deactivating.\n", i); + tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | + PORT_CMD_FIS_RX | PORT_CMD_START); + writel_with_flush(tmp, port_mmio + PORT_CMD); + + /* spec says 500 msecs for each bit, so + * this is slightly incorrect. + */ + msleep(500); + } + + /* Add the spinup command to whatever mode bits may + * already be on in the command register. + */ + cmd = readl(port_mmio + PORT_CMD); + cmd |= PORT_CMD_FIS_RX; + cmd |= PORT_CMD_SPIN_UP; + writel_with_flush(cmd, port_mmio + PORT_CMD); + + /* Bring up SATA link. */ + ret = ahci_link_up(probe_ent, i); + if (ret) { + printf("SATA link %d timeout.\n", i); + continue; + } else { + debug("SATA link ok.\n"); + } + + /* Clear error status */ + tmp = readl(port_mmio + PORT_SCR_ERR); + if (tmp) + writel(tmp, port_mmio + PORT_SCR_ERR); + + debug("Spinning up device on SATA port %d... ", i); + + j = 0; + while (j < WAIT_MS_SPINUP) { + tmp = readl(port_mmio + PORT_TFDATA); + if (!(tmp & (ATA_BUSY | ATA_DRQ))) + break; + udelay(1000); + tmp = readl(port_mmio + PORT_SCR_STAT); + tmp &= PORT_SCR_STAT_DET_MASK; + if (tmp == PORT_SCR_STAT_DET_PHYRDY) + break; + j++; + } + + tmp = readl(port_mmio + PORT_SCR_STAT) & PORT_SCR_STAT_DET_MASK; + if (tmp == PORT_SCR_STAT_DET_COMINIT) { + debug("SATA link %d down (COMINIT received), retrying...\n", i); + i--; + continue; + } + + printf("Target spinup took %d ms.\n", j); + if (j == WAIT_MS_SPINUP) + debug("timeout.\n"); + else + debug("ok.\n"); + + tmp = readl(port_mmio + PORT_SCR_ERR); + debug("PORT_SCR_ERR 0x%x\n", tmp); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* ack any pending irq events for this port */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + debug("PORT_IRQ_STAT 0x%x\n", tmp); + if (tmp) + writel(tmp, port_mmio + PORT_IRQ_STAT); + + writel(1 << i, mmio + HOST_IRQ_STAT); + + /* set irq mask (enables interrupts) */ + writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK); + + /* register linkup ports */ + tmp = readl(port_mmio + PORT_SCR_STAT); + debug("SATA port %d status: 0x%x\n", i, tmp); + if ((tmp & PORT_SCR_STAT_DET_MASK) == PORT_SCR_STAT_DET_PHYRDY) + probe_ent->link_port_map |= (0x01 << i); + } + + tmp = readl(mmio + HOST_CTL); + debug("HOST_CTL 0x%x\n", tmp); + writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); + tmp = readl(mmio + HOST_CTL); + debug("HOST_CTL 0x%x\n", tmp); +#ifndef CONFIG_SCSI_AHCI_PLAT + pci_read_config_word(pdev, PCI_COMMAND, &tmp16); + tmp |= PCI_COMMAND_MASTER; + pci_write_config_word(pdev, PCI_COMMAND, tmp16); +#endif + return 0; +} + + +static void ahci_print_info(struct ahci_probe_ent *probe_ent) +{ +#ifndef CONFIG_SCSI_AHCI_PLAT + pci_dev_t pdev = probe_ent->dev; + u16 cc; +#endif + volatile u8 *mmio = (volatile u8 *)probe_ent->mmio_base; + u32 vers, cap, cap2, impl, speed; + const char *speed_s; + const char *scc_s; + + vers = readl(mmio + HOST_VERSION); + cap = probe_ent->cap; + cap2 = readl(mmio + HOST_CAP2); + impl = probe_ent->port_map; + + speed = (cap >> 20) & 0xf; + if (speed == 1) + speed_s = "1.5"; + else if (speed == 2) + speed_s = "3"; + else if (speed == 3) + speed_s = "6"; + else + speed_s = "?"; + +#ifdef CONFIG_SCSI_AHCI_PLAT + scc_s = "SATA"; +#else + pci_read_config_word(pdev, 0x0a, &cc); + if (cc == 0x0101) + scc_s = "IDE"; + else if (cc == 0x0106) + scc_s = "SATA"; + else if (cc == 0x0104) + scc_s = "RAID"; + else + scc_s = "unknown"; +#endif + printf("AHCI %02x%02x.%02x%02x " + "%u slots %u ports %s Gbps 0x%x impl %s mode\n", + (vers >> 24) & 0xff, + (vers >> 16) & 0xff, + (vers >> 8) & 0xff, + vers & 0xff, + ((cap >> 8) & 0x1f) + 1, (cap & 0x1f) + 1, speed_s, impl, scc_s); + + printf("flags: " + "%s%s%s%s%s%s%s" + "%s%s%s%s%s%s%s" + "%s%s%s%s%s%s\n", + cap & (1 << 31) ? "64bit " : "", + cap & (1 << 30) ? "ncq " : "", + cap & (1 << 28) ? "ilck " : "", + cap & (1 << 27) ? "stag " : "", + cap & (1 << 26) ? "pm " : "", + cap & (1 << 25) ? "led " : "", + cap & (1 << 24) ? "clo " : "", + cap & (1 << 19) ? "nz " : "", + cap & (1 << 18) ? "only " : "", + cap & (1 << 17) ? "pmp " : "", + cap & (1 << 16) ? "fbss " : "", + cap & (1 << 15) ? "pio " : "", + cap & (1 << 14) ? "slum " : "", + cap & (1 << 13) ? "part " : "", + cap & (1 << 7) ? "ccc " : "", + cap & (1 << 6) ? "ems " : "", + cap & (1 << 5) ? "sxs " : "", + cap2 & (1 << 2) ? "apst " : "", + cap2 & (1 << 1) ? "nvmp " : "", + cap2 & (1 << 0) ? "boh " : ""); +} + +#ifndef CONFIG_SCSI_AHCI_PLAT +static int ahci_init_one(pci_dev_t pdev) +{ + u16 vendor; + int rc; + + probe_ent = malloc(sizeof(struct ahci_probe_ent)); + if (!probe_ent) { + printf("%s: No memory for probe_ent\n", __func__); + return -ENOMEM; + } + + memset(probe_ent, 0, sizeof(struct ahci_probe_ent)); + probe_ent->dev = pdev; + + probe_ent->host_flags = ATA_FLAG_SATA + | ATA_FLAG_NO_LEGACY + | ATA_FLAG_MMIO + | ATA_FLAG_PIO_DMA + | ATA_FLAG_NO_ATAPI; + probe_ent->pio_mask = 0x1f; + probe_ent->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */ + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_5, &probe_ent->mmio_base); + debug("ahci mmio_base=0x%08x\n", probe_ent->mmio_base); + + /* Take from kernel: + * JMicron-specific fixup: + * make sure we're in AHCI mode + */ + pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor); + if (vendor == 0x197b) + pci_write_config_byte(pdev, 0x41, 0xa1); + + /* initialize adapter */ + rc = ahci_host_init(probe_ent); + if (rc) + goto err_out; + + ahci_print_info(probe_ent); + + return 0; + + err_out: + return rc; +} +#endif + +#define MAX_DATA_BYTE_COUNT (4*1024*1024) + +static int ahci_fill_sg(u8 port, unsigned char *buf, int buf_len) +{ + struct ahci_ioports *pp = &(probe_ent->port[port]); + struct ahci_sg *ahci_sg = pp->cmd_tbl_sg; + u32 sg_count; + int i; + + sg_count = ((buf_len - 1) / MAX_DATA_BYTE_COUNT) + 1; + if (sg_count > AHCI_MAX_SG) { + printf("Error:Too much sg!\n"); + return -1; + } + + for (i = 0; i < sg_count; i++) { + ahci_sg->addr = + cpu_to_le32((u32) buf + i * MAX_DATA_BYTE_COUNT); + ahci_sg->addr_hi = 0; + ahci_sg->flags_size = cpu_to_le32(0x3fffff & + (buf_len < MAX_DATA_BYTE_COUNT + ? (buf_len - 1) + : (MAX_DATA_BYTE_COUNT - 1))); + ahci_sg++; + buf_len -= MAX_DATA_BYTE_COUNT; + } + + return sg_count; +} + + +static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 opts) +{ + pp->cmd_slot->opts = cpu_to_le32(opts); + pp->cmd_slot->status = 0; + pp->cmd_slot->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff); + pp->cmd_slot->tbl_addr_hi = 0; +} + + +#ifdef CONFIG_AHCI_SETFEATURES_XFER +static void ahci_set_feature(u8 port) +{ + struct ahci_ioports *pp = &(probe_ent->port[port]); + volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio; + u32 cmd_fis_len = 5; /* five dwords */ + u8 fis[20]; + + /* set feature */ + memset(fis, 0, sizeof(fis)); + fis[0] = 0x27; + fis[1] = 1 << 7; + fis[2] = ATA_CMD_SET_FEATURES; + fis[3] = SETFEATURES_XFER; + fis[12] = __ilog2(probe_ent->udma_mask + 1) + 0x40 - 0x01; + + memcpy((unsigned char *)pp->cmd_tbl, fis, sizeof(fis)); + ahci_fill_cmd_slot(pp, cmd_fis_len); + ahci_dcache_flush_sata_cmd(pp); + writel(1, port_mmio + PORT_CMD_ISSUE); + readl(port_mmio + PORT_CMD_ISSUE); + + if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, + WAIT_MS_DATAIO, 0x1)) { + printf("set feature error on port %d!\n", port); + } +} +#endif + + +static int ahci_port_start(u8 port) +{ + struct ahci_ioports *pp = &(probe_ent->port[port]); + volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio; + u32 port_status; + u32 mem; + + debug("Enter start port: %d\n", port); + port_status = readl(port_mmio + PORT_SCR_STAT); + debug("Port %d status: %x\n", port, port_status); + if ((port_status & 0xf) != 0x03) { + printf("No Link on this port!\n"); + return -1; + } + + mem = (u32) malloc(AHCI_PORT_PRIV_DMA_SZ + 2048); + if (!mem) { + free(pp); + printf("%s: No mem for table!\n", __func__); + return -ENOMEM; + } + + mem = (mem + 0x800) & (~0x7ff); /* Aligned to 2048-bytes */ + memset((u8 *) mem, 0, AHCI_PORT_PRIV_DMA_SZ); + + /* + * First item in chunk of DMA memory: 32-slot command table, + * 32 bytes each in size + */ + pp->cmd_slot = + (struct ahci_cmd_hdr *)(uintptr_t)virt_to_phys((void *)mem); + debug("cmd_slot = 0x%x\n", (unsigned)pp->cmd_slot); + mem += (AHCI_CMD_SLOT_SZ + 224); + + /* + * Second item: Received-FIS area + */ + pp->rx_fis = virt_to_phys((void *)mem); + mem += AHCI_RX_FIS_SZ; + + /* + * Third item: data area for storing a single command + * and its scatter-gather table + */ + pp->cmd_tbl = virt_to_phys((void *)mem); + debug("cmd_tbl_dma = 0x%x\n", pp->cmd_tbl); + + mem += AHCI_CMD_TBL_HDR; + pp->cmd_tbl_sg = + (struct ahci_sg *)(uintptr_t)virt_to_phys((void *)mem); + + writel_with_flush((u32) pp->cmd_slot, port_mmio + PORT_LST_ADDR); + + writel_with_flush(pp->rx_fis, port_mmio + PORT_FIS_ADDR); + + writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | + PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | + PORT_CMD_START, port_mmio + PORT_CMD); + + debug("Exit start port %d\n", port); + + return 0; +} + + +static int ahci_device_data_io(u8 port, u8 *fis, int fis_len, u8 *buf, + int buf_len, u8 is_write) +{ + + struct ahci_ioports *pp = &(probe_ent->port[port]); + volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio; + u32 opts; + u32 port_status; + int sg_count; + + debug("Enter %s: for port %d\n", __func__, port); + + if (port > probe_ent->n_ports) { + printf("Invalid port number %d\n", port); + return -1; + } + + port_status = readl(port_mmio + PORT_SCR_STAT); + if ((port_status & 0xf) != 0x03) { + debug("No Link on port %d!\n", port); + return -1; + } + + memcpy((unsigned char *)pp->cmd_tbl, fis, fis_len); + + sg_count = ahci_fill_sg(port, buf, buf_len); + opts = (fis_len >> 2) | (sg_count << 16) | (is_write << 6); + ahci_fill_cmd_slot(pp, opts); + + ahci_dcache_flush_sata_cmd(pp); + ahci_dcache_flush_range((unsigned)buf, (unsigned)buf_len); + + writel_with_flush(1, port_mmio + PORT_CMD_ISSUE); + + if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, + WAIT_MS_DATAIO, 0x1)) { + printf("timeout exit!\n"); + return -1; + } + + ahci_dcache_invalidate_range((unsigned)buf, (unsigned)buf_len); + debug("%s: %d byte transferred.\n", __func__, pp->cmd_slot->status); + + return 0; +} + + +static char *ata_id_strcpy(u16 *target, u16 *src, int len) +{ + int i; + for (i = 0; i < len / 2; i++) + target[i] = swab16(src[i]); + return (char *)target; +} + +/* + * SCSI INQUIRY command operation. + */ +static int ata_scsiop_inquiry(ccb *pccb) +{ + static const u8 hdr[] = { + 0, + 0, + 0x5, /* claim SPC-3 version compatibility */ + 2, + 95 - 4, + }; + u8 fis[20]; + u16 *idbuf; + ALLOC_CACHE_ALIGN_BUFFER(u16, tmpid, ATA_ID_WORDS); + u8 port; + + /* Clean ccb data buffer */ + memset(pccb->pdata, 0, pccb->datalen); + + memcpy(pccb->pdata, hdr, sizeof(hdr)); + + if (pccb->datalen <= 35) + return 0; + + memset(fis, 0, sizeof(fis)); + /* Construct the FIS */ + fis[0] = 0x27; /* Host to device FIS. */ + fis[1] = 1 << 7; /* Command FIS. */ + fis[2] = ATA_CMD_ID_ATA; /* Command byte. */ + + /* Read id from sata */ + port = pccb->target; + + if (ahci_device_data_io(port, (u8 *) &fis, sizeof(fis), (u8 *)tmpid, + ATA_ID_WORDS * 2, 0)) { + debug("scsi_ahci: SCSI inquiry command failure.\n"); + return -EIO; + } + + if (!ataid[port]) { + ataid[port] = malloc(ATA_ID_WORDS * 2); + if (!ataid[port]) { + printf("%s: No memory for ataid[port]\n", __func__); + return -ENOMEM; + } + } + + idbuf = ataid[port]; + + memcpy(idbuf, tmpid, ATA_ID_WORDS * 2); + ata_swap_buf_le16(idbuf, ATA_ID_WORDS); + + memcpy(&pccb->pdata[8], "ATA ", 8); + ata_id_strcpy((u16 *)&pccb->pdata[16], &idbuf[ATA_ID_PROD], 16); + ata_id_strcpy((u16 *)&pccb->pdata[32], &idbuf[ATA_ID_FW_REV], 4); + +#ifdef DEBUG + ata_dump_id(idbuf); +#endif + return 0; +} + + +/* + * SCSI READ10/WRITE10 command operation. + */ +static int ata_scsiop_read_write(ccb *pccb, u8 is_write) +{ + u32 lba = 0; + u16 blocks = 0; + u8 fis[20]; + u8 *user_buffer = pccb->pdata; + u32 user_buffer_size = pccb->datalen; + + /* Retrieve the base LBA number from the ccb structure. */ + memcpy(&lba, pccb->cmd + 2, sizeof(lba)); + lba = be32_to_cpu(lba); + + /* + * And the number of blocks. + * + * For 10-byte and 16-byte SCSI R/W commands, transfer + * length 0 means transfer 0 block of data. + * However, for ATA R/W commands, sector count 0 means + * 256 or 65536 sectors, not 0 sectors as in SCSI. + * + * WARNING: one or two older ATA drives treat 0 as 0... + */ + blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]); + + debug("scsi_ahci: %s %d blocks starting from lba 0x%x\n", + is_write ? "write" : "read", (unsigned)lba, blocks); + + /* Preset the FIS */ + memset(fis, 0, sizeof(fis)); + fis[0] = 0x27; /* Host to device FIS. */ + fis[1] = 1 << 7; /* Command FIS. */ + /* Command byte (read/write). */ + fis[2] = is_write ? ATA_CMD_WRITE_EXT : ATA_CMD_READ_EXT; + + while (blocks) { + u16 now_blocks; /* number of blocks per iteration */ + u32 transfer_size; /* number of bytes per iteration */ + + now_blocks = min(MAX_SATA_BLOCKS_READ_WRITE, blocks); + + transfer_size = ATA_SECT_SIZE * now_blocks; + if (transfer_size > user_buffer_size) { + printf("scsi_ahci: Error: buffer too small.\n"); + return -EIO; + } + + /* LBA48 SATA command but only use 32bit address range within + * that. The next smaller command range (28bit) is too small. + */ + fis[4] = (lba >> 0) & 0xff; + fis[5] = (lba >> 8) & 0xff; + fis[6] = (lba >> 16) & 0xff; + fis[7] = 1 << 6; /* device reg: set LBA mode */ + fis[8] = ((lba >> 24) & 0xff); + fis[3] = 0xe0; /* features */ + + /* Block (sector) count */ + fis[12] = (now_blocks >> 0) & 0xff; + fis[13] = (now_blocks >> 8) & 0xff; + + /* Read/Write from ahci */ + if (ahci_device_data_io(pccb->target, (u8 *) &fis, sizeof(fis), + user_buffer, user_buffer_size, + is_write)) { + debug("scsi_ahci: SCSI %s10 command failure.\n", + is_write ? "WRITE" : "READ"); + return -EIO; + } + + /* If this transaction is a write, do a following flush. + * Writes in u-boot are so rare, and the logic to know when is + * the last write and do a flush only there is sufficiently + * difficult. Just do a flush after every write. This incurs, + * usually, one extra flush when the rare writes do happen. + */ + if (is_write) { + if (-EIO == ata_io_flush(pccb->target)) + return -EIO; + } + user_buffer += transfer_size; + user_buffer_size -= transfer_size; + blocks -= now_blocks; + lba += now_blocks; + } + + return 0; +} + + +/* + * SCSI READ CAPACITY10 command operation. + */ +static int ata_scsiop_read_capacity10(ccb *pccb) +{ + u32 cap; + u64 cap64; + u32 block_size; + + if (!ataid[pccb->target]) { + printf("scsi_ahci: SCSI READ CAPACITY10 command failure. " + "\tNo ATA info!\n" + "\tPlease run SCSI commmand INQUIRY firstly!\n"); + return -EPERM; + } + + cap64 = ata_id_n_sectors(ataid[pccb->target]); + if (cap64 > 0x100000000ULL) + cap64 = 0xffffffff; + + cap = cpu_to_be32(cap64); + memcpy(pccb->pdata, &cap, sizeof(cap)); + + block_size = cpu_to_be32((u32)512); + memcpy(&pccb->pdata[4], &block_size, 4); + + return 0; +} + + +/* + * SCSI READ CAPACITY16 command operation. + */ +static int ata_scsiop_read_capacity16(ccb *pccb) +{ + u64 cap; + u64 block_size; + + if (!ataid[pccb->target]) { + printf("scsi_ahci: SCSI READ CAPACITY16 command failure. " + "\tNo ATA info!\n" + "\tPlease run SCSI commmand INQUIRY firstly!\n"); + return -EPERM; + } + + cap = ata_id_n_sectors(ataid[pccb->target]); + cap = cpu_to_be64(cap); + memcpy(pccb->pdata, &cap, sizeof(cap)); + + block_size = cpu_to_be64((u64)512); + memcpy(&pccb->pdata[8], &block_size, 8); + + return 0; +} + + +/* + * SCSI TEST UNIT READY command operation. + */ +static int ata_scsiop_test_unit_ready(ccb *pccb) +{ + return (ataid[pccb->target]) ? 0 : -EPERM; +} + + +int scsi_exec(ccb *pccb) +{ + int ret; + + switch (pccb->cmd[0]) { + case SCSI_READ10: + ret = ata_scsiop_read_write(pccb, 0); + break; + case SCSI_WRITE10: + ret = ata_scsiop_read_write(pccb, 1); + break; + case SCSI_RD_CAPAC10: + ret = ata_scsiop_read_capacity10(pccb); + break; + case SCSI_RD_CAPAC16: + ret = ata_scsiop_read_capacity16(pccb); + break; + case SCSI_TST_U_RDY: + ret = ata_scsiop_test_unit_ready(pccb); + break; + case SCSI_INQUIRY: + ret = ata_scsiop_inquiry(pccb); + break; + default: + printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]); + return false; + } + + if (ret) { + debug("SCSI command 0x%02x ret errno %d\n", pccb->cmd[0], ret); + return false; + } + return true; + +} + + +void scsi_low_level_init(int busdevfunc) +{ + int i; + u32 linkmap; + +#ifndef CONFIG_SCSI_AHCI_PLAT + ahci_init_one(busdevfunc); +#endif + + linkmap = probe_ent->link_port_map; + + for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) { + if (((linkmap >> i) & 0x01)) { + if (ahci_port_start((u8) i)) { + printf("Can not start port %d\n", i); + continue; + } +#ifdef CONFIG_AHCI_SETFEATURES_XFER + ahci_set_feature((u8) i); +#endif + } + } +} + +#ifdef CONFIG_SCSI_AHCI_PLAT +int ahci_init(u32 base) +{ + int i, rc = 0; + u32 linkmap; + + probe_ent = malloc(sizeof(struct ahci_probe_ent)); + if (!probe_ent) { + printf("%s: No memory for probe_ent\n", __func__); + return -ENOMEM; + } + + memset(probe_ent, 0, sizeof(struct ahci_probe_ent)); + + probe_ent->host_flags = ATA_FLAG_SATA + | ATA_FLAG_NO_LEGACY + | ATA_FLAG_MMIO + | ATA_FLAG_PIO_DMA + | ATA_FLAG_NO_ATAPI; + probe_ent->pio_mask = 0x1f; + probe_ent->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */ + + probe_ent->mmio_base = base; + + /* initialize adapter */ + rc = ahci_host_init(probe_ent); + if (rc) + goto err_out; + + ahci_print_info(probe_ent); + + linkmap = probe_ent->link_port_map; + + for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) { + if (((linkmap >> i) & 0x01)) { + if (ahci_port_start((u8) i)) { + printf("Can not start port %d\n", i); + continue; + } +#ifdef CONFIG_AHCI_SETFEATURES_XFER + ahci_set_feature((u8) i); +#endif + } + } +err_out: + return rc; +} + +void __weak scsi_init(void) +{ +} + +#endif + +/* + * In the general case of generic rotating media it makes sense to have a + * flush capability. It probably even makes sense in the case of SSDs because + * one cannot always know for sure what kind of internal cache/flush mechanism + * is embodied therein. At first it was planned to invoke this after the last + * write to disk and before rebooting. In practice, knowing, a priori, which + * is the last write is difficult. Because writing to the disk in u-boot is + * very rare, this flush command will be invoked after every block write. + */ +static int ata_io_flush(u8 port) +{ + u8 fis[20]; + struct ahci_ioports *pp = &(probe_ent->port[port]); + volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio; + u32 cmd_fis_len = 5; /* five dwords */ + + /* Preset the FIS */ + memset(fis, 0, 20); + fis[0] = 0x27; /* Host to device FIS. */ + fis[1] = 1 << 7; /* Command FIS. */ + fis[2] = ATA_CMD_FLUSH_EXT; + + memcpy((unsigned char *)pp->cmd_tbl, fis, 20); + ahci_fill_cmd_slot(pp, cmd_fis_len); + writel_with_flush(1, port_mmio + PORT_CMD_ISSUE); + + if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, + WAIT_MS_FLUSH, 0x1)) { + debug("scsi_ahci: flush command timeout on port %d.\n", port); + return -EIO; + } + + return 0; +} + + +void scsi_bus_reset(void) +{ + /*Not implement*/ +} + + +void scsi_print_error(ccb * pccb) +{ + /*The ahci error info can be read in the ahci driver*/ +} diff --git a/qemu/roms/u-boot/drivers/block/ata_piix.c b/qemu/roms/u-boot/drivers/block/ata_piix.c new file mode 100644 index 000000000..5cf91ade8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/ata_piix.c @@ -0,0 +1,712 @@ +/* + * Copyright (C) Procsys. All rights reserved. + * Author: Mushtaq Khan + * + * + * SPDX-License-Identifier: GPL-2.0+ + * + * with the reference to ata_piix driver in kernel 2.4.32 + */ + +/* + * This file contains SATA controller and SATA drive initialization functions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SATA 0 /* For debug prints set DEBUG_SATA to 1 */ + +#define SATA_DECL +#define DRV_DECL /* For file specific declarations */ +#include "ata_piix.h" + +/* Macros realted to PCI */ +#define PCI_SATA_BUS 0x00 +#define PCI_SATA_DEV 0x1f +#define PCI_SATA_FUNC 0x02 + +#define PCI_SATA_BASE1 0x10 +#define PCI_SATA_BASE2 0x14 +#define PCI_SATA_BASE3 0x18 +#define PCI_SATA_BASE4 0x1c +#define PCI_SATA_BASE5 0x20 +#define PCI_PMR 0x90 +#define PCI_PI 0x09 +#define PCI_PCS 0x92 +#define PCI_DMA_CTL 0x48 + +#define PORT_PRESENT (1<<0) +#define PORT_ENABLED (1<<4) + +u32 bdf; +u32 iobase1; /* Primary cmd block */ +u32 iobase2; /* Primary ctl block */ +u32 iobase3; /* Sec cmd block */ +u32 iobase4; /* sec ctl block */ +u32 iobase5; /* BMDMA*/ + +int pci_sata_init(void) +{ + u32 bus = PCI_SATA_BUS; + u32 dev = PCI_SATA_DEV; + u32 fun = PCI_SATA_FUNC; + u16 cmd = 0; + u8 lat = 0, pcibios_max_latency = 0xff; + u8 pmr; /* Port mapping reg */ + u8 pi; /* Prgming Interface reg */ + + bdf = PCI_BDF(bus, dev, fun); + pci_read_config_dword(bdf, PCI_SATA_BASE1, &iobase1); + pci_read_config_dword(bdf, PCI_SATA_BASE2, &iobase2); + pci_read_config_dword(bdf, PCI_SATA_BASE3, &iobase3); + pci_read_config_dword(bdf, PCI_SATA_BASE4, &iobase4); + pci_read_config_dword(bdf, PCI_SATA_BASE5, &iobase5); + + if ((iobase1 == 0xFFFFFFFF) || (iobase2 == 0xFFFFFFFF) || + (iobase3 == 0xFFFFFFFF) || (iobase4 == 0xFFFFFFFF) || + (iobase5 == 0xFFFFFFFF)) { + /* ERROR */ + printf("error no base addr for SATA controller\n"); + return 1; + } + + iobase1 &= 0xFFFFFFFE; + iobase2 &= 0xFFFFFFFE; + iobase3 &= 0xFFFFFFFE; + iobase4 &= 0xFFFFFFFE; + iobase5 &= 0xFFFFFFFE; + + /* check for mode */ + pci_read_config_byte(bdf, PCI_PMR, &pmr); + if (pmr > 1) { + puts("combined mode not supported\n"); + return 1; + } + + pci_read_config_byte(bdf, PCI_PI, &pi); + if ((pi & 0x05) != 0x05) { + puts("Sata is in Legacy mode\n"); + return 1; + } else + puts("sata is in Native mode\n"); + + /* MASTER CFG AND IO CFG */ + pci_read_config_word(bdf, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; + pci_write_config_word(bdf, PCI_COMMAND, cmd); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); + + return 0; +} + +int sata_bus_probe(int port_no) +{ + int orig_mask, mask; + u16 pcs; + + mask = (PORT_PRESENT << port_no); + pci_read_config_word(bdf, PCI_PCS, &pcs); + orig_mask = (int) pcs & 0xff; + if ((orig_mask & mask) != mask) + return 0; + else + return 1; +} + +int init_sata(int dev) +{ + static int done; + u8 i, rv = 0; + + if (!done) + done = 1; + else + return 0; + + rv = pci_sata_init(); + if (rv == 1) { + puts("pci initialization failed\n"); + return 1; + } + + port[0].port_no = 0; + port[0].ioaddr.cmd_addr = iobase1; + port[0].ioaddr.altstatus_addr = port[0].ioaddr.ctl_addr = + iobase2 | ATA_PCI_CTL_OFS; + port[0].ioaddr.bmdma_addr = iobase5; + + port[1].port_no = 1; + port[1].ioaddr.cmd_addr = iobase3; + port[1].ioaddr.altstatus_addr = port[1].ioaddr.ctl_addr = + iobase4 | ATA_PCI_CTL_OFS; + port[1].ioaddr.bmdma_addr = iobase5 + 0x8; + + for (i = 0; i < CONFIG_SYS_SATA_MAXBUS; i++) + sata_port(&port[i].ioaddr); + + for (i = 0; i < CONFIG_SYS_SATA_MAXBUS; i++) { + if (!(sata_bus_probe(i))) { + port[i].port_state = 0; + printf("SATA#%d port is not present\n", i); + } else { + printf("SATA#%d port is present\n", i); + if (sata_bus_softreset(i)) + port[i].port_state = 0; + else + port[i].port_state = 1; + } + } + + for (i = 0; i < CONFIG_SYS_SATA_MAXBUS; i++) { + u8 j, devno; + + if (port[i].port_state == 0) + continue; + for (j = 0; j < CONFIG_SYS_SATA_DEVS_PER_BUS; j++) { + sata_identify(i, j); + set_Feature_cmd(i, j); + devno = i * CONFIG_SYS_SATA_DEVS_PER_BUS + j; + if ((sata_dev_desc[devno].lba > 0) && + (sata_dev_desc[devno].blksz > 0)) { + dev_print(&sata_dev_desc[devno]); + /* initialize partition type */ + init_part(&sata_dev_desc[devno]); + } + } + } + return 0; +} + +static inline u8 sata_inb(unsigned long ioaddr) +{ + return inb(ioaddr); +} + +static inline void sata_outb(unsigned char val, unsigned long ioaddr) +{ + outb(val, ioaddr); +} + +static void output_data(struct sata_ioports *ioaddr, ulong * sect_buf, + int words) +{ + outsw(ioaddr->data_addr, sect_buf, words << 1); +} + +static int input_data(struct sata_ioports *ioaddr, ulong * sect_buf, int words) +{ + insw(ioaddr->data_addr, sect_buf, words << 1); + return 0; +} + +static void sata_cpy(unsigned char *dst, unsigned char *src, unsigned int len) +{ + unsigned char *end, *last; + + last = dst; + end = src + len - 1; + + /* reserve space for '\0' */ + if (len < 2) + goto OUT; + + /* skip leading white space */ + while ((*src) && (src < end) && (*src == ' ')) + ++src; + + /* copy string, omitting trailing white space */ + while ((*src) && (src < end)) { + *dst++ = *src; + if (*src++ != ' ') + last = dst; + } +OUT: + *last = '\0'; +} + +int sata_bus_softreset(int num) +{ + u8 dev = 0, status = 0, i; + + port[num].dev_mask = 0; + + for (i = 0; i < CONFIG_SYS_SATA_DEVS_PER_BUS; i++) { + if (!(sata_devchk(&port[num].ioaddr, i))) { + debug("dev_chk failed for dev#%d\n", i); + } else { + port[num].dev_mask |= (1 << i); + debug("dev_chk passed for dev#%d\n", i); + } + } + + if (!(port[num].dev_mask)) { + printf("no devices on port%d\n", num); + return 1; + } + + dev_select(&port[num].ioaddr, dev); + + port[num].ctl_reg = 0x08; /* Default value of control reg */ + sata_outb(port[num].ctl_reg, port[num].ioaddr.ctl_addr); + udelay(10); + sata_outb(port[num].ctl_reg | ATA_SRST, port[num].ioaddr.ctl_addr); + udelay(10); + sata_outb(port[num].ctl_reg, port[num].ioaddr.ctl_addr); + + /* + * spec mandates ">= 2ms" before checking status. + * We wait 150ms, because that was the magic delay used for + * ATAPI devices in Hale Landis's ATADRVR, for the period of time + * between when the ATA command register is written, and then + * status is checked. Because waiting for "a while" before + * checking status is fine, post SRST, we perform this magic + * delay here as well. + */ + mdelay(150); + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 300); + while ((status & ATA_BUSY)) { + mdelay(100); + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 3); + } + + if (status & ATA_BUSY) + printf("ata%u is slow to respond,plz be patient\n", num); + + while ((status & ATA_BUSY)) { + mdelay(100); + status = sata_chk_status(&port[num].ioaddr); + } + + if (status & ATA_BUSY) { + printf("ata%u failed to respond : bus reset failed\n", num); + return 1; + } + return 0; +} + +void sata_identify(int num, int dev) +{ + u8 cmd = 0, status = 0; + u8 devno = num * CONFIG_SYS_SATA_DEVS_PER_BUS + dev; + u16 iobuf[ATA_SECT_SIZE]; + u64 n_sectors = 0; + u8 mask = 0; + + memset(iobuf, 0, sizeof(iobuf)); + hd_driveid_t *iop = (hd_driveid_t *) iobuf; + + if (dev == 0) + mask = 0x01; + else + mask = 0x02; + + if (!(port[num].dev_mask & mask)) { + printf("dev%d is not present on port#%d\n", dev, num); + return; + } + + printf("port=%d dev=%d\n", num, dev); + + dev_select(&port[num].ioaddr, dev); + + status = 0; + cmd = ATA_CMD_IDENT; /* Device Identify Command */ + sata_outb(cmd, port[num].ioaddr.command_addr); + sata_inb(port[num].ioaddr.altstatus_addr); + udelay(10); + + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 1000); + if (status & ATA_ERR) { + puts("\ndevice not responding\n"); + port[num].dev_mask &= ~mask; + return; + } + + input_data(&port[num].ioaddr, (ulong *) iobuf, ATA_SECTORWORDS); + + debug("\nata%u: dev %u cfg 49:%04x 82:%04x 83:%04x 84:%04x85:%04x" + "86:%04x" "87:%04x 88:%04x\n", num, dev, iobuf[49], + iobuf[82], iobuf[83], iobuf[84], iobuf[85], iobuf[86], + iobuf[87], iobuf[88]); + + /* we require LBA and DMA support (bits 8 & 9 of word 49) */ + if (!ata_id_has_dma(iobuf) || !ata_id_has_lba(iobuf)) + debug("ata%u: no dma/lba\n", num); + ata_dump_id(iobuf); + + if (ata_id_has_lba48(iobuf)) + n_sectors = ata_id_u64(iobuf, 100); + else + n_sectors = ata_id_u32(iobuf, 60); + debug("no. of sectors %u\n", ata_id_u64(iobuf, 100)); + debug("no. of sectors %u\n", ata_id_u32(iobuf, 60)); + + if (n_sectors == 0) { + port[num].dev_mask &= ~mask; + return; + } + + sata_cpy((unsigned char *)sata_dev_desc[devno].revision, iop->fw_rev, + sizeof(sata_dev_desc[devno].revision)); + sata_cpy((unsigned char *)sata_dev_desc[devno].vendor, iop->model, + sizeof(sata_dev_desc[devno].vendor)); + sata_cpy((unsigned char *)sata_dev_desc[devno].product, iop->serial_no, + sizeof(sata_dev_desc[devno].product)); + strswab(sata_dev_desc[devno].revision); + strswab(sata_dev_desc[devno].vendor); + + if ((iop->config & 0x0080) == 0x0080) + sata_dev_desc[devno].removable = 1; + else + sata_dev_desc[devno].removable = 0; + + sata_dev_desc[devno].lba = iop->lba_capacity; + debug("lba=0x%x", sata_dev_desc[devno].lba); + +#ifdef CONFIG_LBA48 + if (iop->command_set_2 & 0x0400) { + sata_dev_desc[devno].lba48 = 1; + lba = (unsigned long long) iop->lba48_capacity[0] | + ((unsigned long long) iop->lba48_capacity[1] << 16) | + ((unsigned long long) iop->lba48_capacity[2] << 32) | + ((unsigned long long) iop->lba48_capacity[3] << 48); + } else { + sata_dev_desc[devno].lba48 = 0; + } +#endif + + /* assuming HD */ + sata_dev_desc[devno].type = DEV_TYPE_HARDDISK; + sata_dev_desc[devno].blksz = ATA_BLOCKSIZE; + sata_dev_desc[devno].log2blksz = LOG2(sata_dev_desc[devno].blksz); + sata_dev_desc[devno].lun = 0; /* just to fill something in... */ +} + +void set_Feature_cmd(int num, int dev) +{ + u8 mask = 0x00, status = 0; + + if (dev == 0) + mask = 0x01; + else + mask = 0x02; + + if (!(port[num].dev_mask & mask)) { + debug("dev%d is not present on port#%d\n", dev, num); + return; + } + + dev_select(&port[num].ioaddr, dev); + + sata_outb(SETFEATURES_XFER, port[num].ioaddr.feature_addr); + sata_outb(XFER_PIO_4, port[num].ioaddr.nsect_addr); + sata_outb(0, port[num].ioaddr.lbal_addr); + sata_outb(0, port[num].ioaddr.lbam_addr); + sata_outb(0, port[num].ioaddr.lbah_addr); + + sata_outb(ATA_DEVICE_OBS, port[num].ioaddr.device_addr); + sata_outb(ATA_CMD_SETF, port[num].ioaddr.command_addr); + + udelay(50); + mdelay(150); + + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 5000); + if ((status & (ATA_STAT_BUSY | ATA_STAT_ERR))) { + printf("Error : status 0x%02x\n", status); + port[num].dev_mask &= ~mask; + } +} + +void sata_port(struct sata_ioports *ioport) +{ + ioport->data_addr = ioport->cmd_addr + ATA_REG_DATA; + ioport->error_addr = ioport->cmd_addr + ATA_REG_ERR; + ioport->feature_addr = ioport->cmd_addr + ATA_REG_FEATURE; + ioport->nsect_addr = ioport->cmd_addr + ATA_REG_NSECT; + ioport->lbal_addr = ioport->cmd_addr + ATA_REG_LBAL; + ioport->lbam_addr = ioport->cmd_addr + ATA_REG_LBAM; + ioport->lbah_addr = ioport->cmd_addr + ATA_REG_LBAH; + ioport->device_addr = ioport->cmd_addr + ATA_REG_DEVICE; + ioport->status_addr = ioport->cmd_addr + ATA_REG_STATUS; + ioport->command_addr = ioport->cmd_addr + ATA_REG_CMD; +} + +int sata_devchk(struct sata_ioports *ioaddr, int dev) +{ + u8 nsect, lbal; + + dev_select(ioaddr, dev); + + sata_outb(0x55, ioaddr->nsect_addr); + sata_outb(0xaa, ioaddr->lbal_addr); + + sata_outb(0xaa, ioaddr->nsect_addr); + sata_outb(0x55, ioaddr->lbal_addr); + + sata_outb(0x55, ioaddr->nsect_addr); + sata_outb(0xaa, ioaddr->lbal_addr); + + nsect = sata_inb(ioaddr->nsect_addr); + lbal = sata_inb(ioaddr->lbal_addr); + + if ((nsect == 0x55) && (lbal == 0xaa)) + return 1; /* we found a device */ + else + return 0; /* nothing found */ +} + +void dev_select(struct sata_ioports *ioaddr, int dev) +{ + u8 tmp = 0; + + if (dev == 0) + tmp = ATA_DEVICE_OBS; + else + tmp = ATA_DEVICE_OBS | ATA_DEV1; + + sata_outb(tmp, ioaddr->device_addr); + sata_inb(ioaddr->altstatus_addr); + udelay(5); +} + +u8 sata_busy_wait(struct sata_ioports *ioaddr, int bits, unsigned int max) +{ + u8 status; + + do { + udelay(1000); + status = sata_chk_status(ioaddr); + max--; + } while ((status & bits) && (max > 0)); + + return status; +} + +u8 sata_chk_status(struct sata_ioports *ioaddr) +{ + return sata_inb(ioaddr->status_addr); +} + + +ulong sata_read(int device, ulong blknr, lbaint_t blkcnt, void *buff) +{ + ulong n = 0, *buffer = (ulong *)buff; + u8 dev = 0, num = 0, mask = 0, status = 0; + +#ifdef CONFIG_LBA48 + unsigned char lba48 = 0; + + if (blknr & 0x0000fffff0000000) { + if (!sata_dev_desc[devno].lba48) { + printf("Drive doesn't support 48-bit addressing\n"); + return 0; + } + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + /* Port Number */ + num = device / CONFIG_SYS_SATA_DEVS_PER_BUS; + /* dev on the port */ + if (device >= CONFIG_SYS_SATA_DEVS_PER_BUS) + dev = device - CONFIG_SYS_SATA_DEVS_PER_BUS; + else + dev = device; + + if (dev == 0) + mask = 0x01; + else + mask = 0x02; + + if (!(port[num].dev_mask & mask)) { + printf("dev%d is not present on port#%d\n", dev, num); + return 0; + } + + /* Select device */ + dev_select(&port[num].ioaddr, dev); + + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 500); + if (status & ATA_BUSY) { + printf("ata%u failed to respond\n", port[num].port_no); + return n; + } + while (blkcnt-- > 0) { + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 500); + if (status & ATA_BUSY) { + printf("ata%u failed to respond\n", 0); + return n; + } +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + sata_outb(0, port[num].ioaddr.nsect_addr); + sata_outb((blknr >> 24) & 0xFF, + port[num].ioaddr.lbal_addr); + sata_outb((blknr >> 32) & 0xFF, + port[num].ioaddr.lbam_addr); + sata_outb((blknr >> 40) & 0xFF, + port[num].ioaddr.lbah_addr); + } +#endif + sata_outb(1, port[num].ioaddr.nsect_addr); + sata_outb(((blknr) >> 0) & 0xFF, + port[num].ioaddr.lbal_addr); + sata_outb((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr); + sata_outb((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr); + +#ifdef CONFIG_LBA48 + if (lba48) { + sata_outb(ATA_LBA, port[num].ioaddr.device_addr); + sata_outb(ATA_CMD_READ_EXT, + port[num].ioaddr.command_addr); + } else +#endif + { + sata_outb(ATA_LBA | ((blknr >> 24) & 0xF), + port[num].ioaddr.device_addr); + sata_outb(ATA_CMD_READ, + port[num].ioaddr.command_addr); + } + + mdelay(50); + /* may take up to 4 sec */ + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 4000); + + if ((status & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) + != ATA_STAT_DRQ) { + u8 err = 0; + + printf("Error no DRQ dev %d blk %ld: sts 0x%02x\n", + device, (ulong) blknr, status); + err = sata_inb(port[num].ioaddr.error_addr); + printf("Error reg = 0x%x\n", err); + return n; + } + input_data(&port[num].ioaddr, buffer, ATA_SECTORWORDS); + sata_inb(port[num].ioaddr.altstatus_addr); + udelay(50); + + ++n; + ++blknr; + buffer += ATA_SECTORWORDS; + } + return n; +} + +ulong sata_write(int device, ulong blknr, lbaint_t blkcnt, const void *buff) +{ + ulong n = 0, *buffer = (ulong *)buff; + unsigned char status = 0, num = 0, dev = 0, mask = 0; + +#ifdef CONFIG_LBA48 + unsigned char lba48 = 0; + + if (blknr & 0x0000fffff0000000) { + if (!sata_dev_desc[devno].lba48) { + printf("Drive doesn't support 48-bit addressing\n"); + return 0; + } + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + /* Port Number */ + num = device / CONFIG_SYS_SATA_DEVS_PER_BUS; + /* dev on the Port */ + if (device >= CONFIG_SYS_SATA_DEVS_PER_BUS) + dev = device - CONFIG_SYS_SATA_DEVS_PER_BUS; + else + dev = device; + + if (dev == 0) + mask = 0x01; + else + mask = 0x02; + + /* Select device */ + dev_select(&port[num].ioaddr, dev); + + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 500); + if (status & ATA_BUSY) { + printf("ata%u failed to respond\n", port[num].port_no); + return n; + } + + while (blkcnt-- > 0) { + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 500); + if (status & ATA_BUSY) { + printf("ata%u failed to respond\n", + port[num].port_no); + return n; + } +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + sata_outb(0, port[num].ioaddr.nsect_addr); + sata_outb((blknr >> 24) & 0xFF, + port[num].ioaddr.lbal_addr); + sata_outb((blknr >> 32) & 0xFF, + port[num].ioaddr.lbam_addr); + sata_outb((blknr >> 40) & 0xFF, + port[num].ioaddr.lbah_addr); + } +#endif + sata_outb(1, port[num].ioaddr.nsect_addr); + sata_outb((blknr >> 0) & 0xFF, port[num].ioaddr.lbal_addr); + sata_outb((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr); + sata_outb((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr); +#ifdef CONFIG_LBA48 + if (lba48) { + sata_outb(ATA_LBA, port[num].ioaddr.device_addr); + sata_outb(ATA_CMD_WRITE_EXT, + port[num].ioaddr.command_addr); + } else +#endif + { + sata_outb(ATA_LBA | ((blknr >> 24) & 0xF), + port[num].ioaddr.device_addr); + sata_outb(ATA_CMD_WRITE, + port[num].ioaddr.command_addr); + } + + mdelay(50); + /* may take up to 4 sec */ + status = sata_busy_wait(&port[num].ioaddr, ATA_BUSY, 4000); + if ((status & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) + != ATA_STAT_DRQ) { + printf("Error no DRQ dev %d blk %ld: sts 0x%02x\n", + device, (ulong) blknr, status); + return n; + } + + output_data(&port[num].ioaddr, buffer, ATA_SECTORWORDS); + sata_inb(port[num].ioaddr.altstatus_addr); + udelay(50); + + ++n; + ++blknr; + buffer += ATA_SECTORWORDS; + } + return n; +} + +int scan_sata(int dev) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/block/ata_piix.h b/qemu/roms/u-boot/drivers/block/ata_piix.h new file mode 100644 index 000000000..6c68ea2f0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/ata_piix.h @@ -0,0 +1,71 @@ +#ifndef __ATA_PIIX_H__ +#define __ATA_PIIX_H__ + +struct sata_ioports { + unsigned long cmd_addr; + unsigned long data_addr; + unsigned long error_addr; + unsigned long feature_addr; + unsigned long nsect_addr; + unsigned long lbal_addr; + unsigned long lbam_addr; + unsigned long lbah_addr; + unsigned long device_addr; + unsigned long status_addr; + unsigned long command_addr; + unsigned long altstatus_addr; + unsigned long ctl_addr; + unsigned long bmdma_addr; + unsigned long scr_addr; +}; + +struct sata_port { + unsigned char port_no; /* primary=0, secondary=1 */ + struct sata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */ + unsigned char ctl_reg; + unsigned char last_ctl; + unsigned char port_state; /* 1-port is available and */ + /* 0-port is not available */ + unsigned char dev_mask; +}; + +/***********SATA LIBRARY SPECIFIC DEFINITIONS AND DECLARATIONS**************/ +#ifdef SATA_DECL /* SATA library specific declarations */ +inline void ata_dump_id(u16 *id) +{ + debug("49 = 0x%04x " + "53 = 0x%04x " + "63 = 0x%04x " + "64 = 0x%04x " + "75 = 0x%04x\n", id[49], id[53], id[63], id[64], id[75]); + debug("80 = 0x%04x " + "81 = 0x%04x " + "82 = 0x%04x " + "83 = 0x%04x " + "84 = 0x%04x\n", id[80], id[81], id[82], id[83], id[84]); + debug("88 = 0x%04x " "93 = 0x%04x\n", id[88], id[93]); +} +#endif + +#ifdef SATA_DECL /*SATA library specific declarations */ +int sata_bus_softreset(int num); +void sata_identify(int num, int dev); +void sata_port(struct sata_ioports *ioport); +void set_Feature_cmd(int num, int dev); +int sata_devchk(struct sata_ioports *ioaddr, int dev); +void dev_select(struct sata_ioports *ioaddr, int dev); +u8 sata_busy_wait(struct sata_ioports *ioaddr, int bits, unsigned int max); +u8 sata_chk_status(struct sata_ioports *ioaddr); +#endif + +/************DRIVER SPECIFIC DEFINITIONS AND DECLARATIONS**************/ + +#ifdef DRV_DECL /* Driver specific declaration */ +int init_sata(int dev); +#endif + +#ifdef DRV_DECL /* Defines Driver Specific variables */ +struct sata_port port[CONFIG_SYS_SATA_MAXBUS]; +#endif + +#endif /* __ATA_PIIX_H__ */ diff --git a/qemu/roms/u-boot/drivers/block/dwc_ahsata.c b/qemu/roms/u-boot/drivers/block/dwc_ahsata.c new file mode 100644 index 000000000..356921429 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/dwc_ahsata.c @@ -0,0 +1,970 @@ +/* + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * Terry Lv + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "dwc_ahsata.h" + +struct sata_port_regs { + u32 clb; + u32 clbu; + u32 fb; + u32 fbu; + u32 is; + u32 ie; + u32 cmd; + u32 res1[1]; + u32 tfd; + u32 sig; + u32 ssts; + u32 sctl; + u32 serr; + u32 sact; + u32 ci; + u32 sntf; + u32 res2[1]; + u32 dmacr; + u32 res3[1]; + u32 phycr; + u32 physr; +}; + +struct sata_host_regs { + u32 cap; + u32 ghc; + u32 is; + u32 pi; + u32 vs; + u32 ccc_ctl; + u32 ccc_ports; + u32 res1[2]; + u32 cap2; + u32 res2[30]; + u32 bistafr; + u32 bistcr; + u32 bistfctr; + u32 bistsr; + u32 bistdecr; + u32 res3[2]; + u32 oobr; + u32 res4[8]; + u32 timer1ms; + u32 res5[1]; + u32 gparam1r; + u32 gparam2r; + u32 pparamr; + u32 testr; + u32 versionr; + u32 idr; +}; + +#define MAX_DATA_BYTES_PER_SG (4 * 1024 * 1024) +#define MAX_BYTES_PER_TRANS (AHCI_MAX_SG * MAX_DATA_BYTES_PER_SG) + +#define writel_with_flush(a, b) do { writel(a, b); readl(b); } while (0) + +static int is_ready; + +static inline u32 ahci_port_base(u32 base, u32 port) +{ + return base + 0x100 + (port * 0x80); +} + +static int waiting_for_cmd_completed(u8 *offset, + int timeout_msec, + u32 sign) +{ + int i; + u32 status; + + for (i = 0; + ((status = readl(offset)) & sign) && i < timeout_msec; + ++i) + mdelay(1); + + return (i < timeout_msec) ? 0 : -1; +} + +static int ahci_setup_oobr(struct ahci_probe_ent *probe_ent, + int clk) +{ + struct sata_host_regs *host_mmio = + (struct sata_host_regs *)probe_ent->mmio_base; + + writel(SATA_HOST_OOBR_WE, &(host_mmio->oobr)); + writel(0x02060b14, &(host_mmio->oobr)); + + return 0; +} + +static int ahci_host_init(struct ahci_probe_ent *probe_ent) +{ + u32 tmp, cap_save, num_ports; + int i, j, timeout = 1000; + struct sata_port_regs *port_mmio = NULL; + struct sata_host_regs *host_mmio = + (struct sata_host_regs *)probe_ent->mmio_base; + int clk = mxc_get_clock(MXC_SATA_CLK); + + cap_save = readl(&(host_mmio->cap)); + cap_save |= SATA_HOST_CAP_SSS; + + /* global controller reset */ + tmp = readl(&(host_mmio->ghc)); + if ((tmp & SATA_HOST_GHC_HR) == 0) + writel_with_flush(tmp | SATA_HOST_GHC_HR, &(host_mmio->ghc)); + + while ((readl(&(host_mmio->ghc)) & SATA_HOST_GHC_HR) + && --timeout) + ; + + if (timeout <= 0) { + debug("controller reset failed (0x%x)\n", tmp); + return -1; + } + + /* Set timer 1ms */ + writel(clk / 1000, &(host_mmio->timer1ms)); + + ahci_setup_oobr(probe_ent, 0); + + writel_with_flush(SATA_HOST_GHC_AE, &(host_mmio->ghc)); + writel(cap_save, &(host_mmio->cap)); + num_ports = (cap_save & SATA_HOST_CAP_NP_MASK) + 1; + writel_with_flush((1 << num_ports) - 1, + &(host_mmio->pi)); + + /* + * Determine which Ports are implemented by the DWC_ahsata, + * by reading the PI register. This bit map value aids the + * software to determine how many Ports are available and + * which Port registers need to be initialized. + */ + probe_ent->cap = readl(&(host_mmio->cap)); + probe_ent->port_map = readl(&(host_mmio->pi)); + + /* Determine how many command slots the HBA supports */ + probe_ent->n_ports = + (probe_ent->cap & SATA_HOST_CAP_NP_MASK) + 1; + + debug("cap 0x%x port_map 0x%x n_ports %d\n", + probe_ent->cap, probe_ent->port_map, probe_ent->n_ports); + + for (i = 0; i < probe_ent->n_ports; i++) { + probe_ent->port[i].port_mmio = + ahci_port_base((u32)host_mmio, i); + port_mmio = + (struct sata_port_regs *)probe_ent->port[i].port_mmio; + + /* Ensure that the DWC_ahsata is in idle state */ + tmp = readl(&(port_mmio->cmd)); + + /* + * When P#CMD.ST, P#CMD.CR, P#CMD.FRE and P#CMD.FR + * are all cleared, the Port is in an idle state. + */ + if (tmp & (SATA_PORT_CMD_CR | SATA_PORT_CMD_FR | + SATA_PORT_CMD_FRE | SATA_PORT_CMD_ST)) { + + /* + * System software places a Port into the idle state by + * clearing P#CMD.ST and waiting for P#CMD.CR to return + * 0 when read. + */ + tmp &= ~SATA_PORT_CMD_ST; + writel_with_flush(tmp, &(port_mmio->cmd)); + + /* + * spec says 500 msecs for each bit, so + * this is slightly incorrect. + */ + mdelay(500); + + timeout = 1000; + while ((readl(&(port_mmio->cmd)) & SATA_PORT_CMD_CR) + && --timeout) + ; + + if (timeout <= 0) { + debug("port reset failed (0x%x)\n", tmp); + return -1; + } + } + + /* Spin-up device */ + tmp = readl(&(port_mmio->cmd)); + writel((tmp | SATA_PORT_CMD_SUD), &(port_mmio->cmd)); + + /* Wait for spin-up to finish */ + timeout = 1000; + while (!(readl(&(port_mmio->cmd)) | SATA_PORT_CMD_SUD) + && --timeout) + ; + if (timeout <= 0) { + debug("Spin-Up can't finish!\n"); + return -1; + } + + for (j = 0; j < 100; ++j) { + mdelay(10); + tmp = readl(&(port_mmio->ssts)); + if (((tmp & SATA_PORT_SSTS_DET_MASK) == 0x3) || + ((tmp & SATA_PORT_SSTS_DET_MASK) == 0x1)) + break; + } + + /* Wait for COMINIT bit 26 (DIAG_X) in SERR */ + timeout = 1000; + while (!(readl(&(port_mmio->serr)) | SATA_PORT_SERR_DIAG_X) + && --timeout) + ; + if (timeout <= 0) { + debug("Can't find DIAG_X set!\n"); + return -1; + } + + /* + * For each implemented Port, clear the P#SERR + * register, by writing ones to each implemented\ + * bit location. + */ + tmp = readl(&(port_mmio->serr)); + debug("P#SERR 0x%x\n", + tmp); + writel(tmp, &(port_mmio->serr)); + + /* Ack any pending irq events for this port */ + tmp = readl(&(host_mmio->is)); + debug("IS 0x%x\n", tmp); + if (tmp) + writel(tmp, &(host_mmio->is)); + + writel(1 << i, &(host_mmio->is)); + + /* set irq mask (enables interrupts) */ + writel(DEF_PORT_IRQ, &(port_mmio->ie)); + + /* register linkup ports */ + tmp = readl(&(port_mmio->ssts)); + debug("Port %d status: 0x%x\n", i, tmp); + if ((tmp & SATA_PORT_SSTS_DET_MASK) == 0x03) + probe_ent->link_port_map |= (0x01 << i); + } + + tmp = readl(&(host_mmio->ghc)); + debug("GHC 0x%x\n", tmp); + writel(tmp | SATA_HOST_GHC_IE, &(host_mmio->ghc)); + tmp = readl(&(host_mmio->ghc)); + debug("GHC 0x%x\n", tmp); + + return 0; +} + +static void ahci_print_info(struct ahci_probe_ent *probe_ent) +{ + struct sata_host_regs *host_mmio = + (struct sata_host_regs *)probe_ent->mmio_base; + u32 vers, cap, impl, speed; + const char *speed_s; + const char *scc_s; + + vers = readl(&(host_mmio->vs)); + cap = probe_ent->cap; + impl = probe_ent->port_map; + + speed = (cap & SATA_HOST_CAP_ISS_MASK) + >> SATA_HOST_CAP_ISS_OFFSET; + if (speed == 1) + speed_s = "1.5"; + else if (speed == 2) + speed_s = "3"; + else + speed_s = "?"; + + scc_s = "SATA"; + + printf("AHCI %02x%02x.%02x%02x " + "%u slots %u ports %s Gbps 0x%x impl %s mode\n", + (vers >> 24) & 0xff, + (vers >> 16) & 0xff, + (vers >> 8) & 0xff, + vers & 0xff, + ((cap >> 8) & 0x1f) + 1, + (cap & 0x1f) + 1, + speed_s, + impl, + scc_s); + + printf("flags: " + "%s%s%s%s%s%s" + "%s%s%s%s%s%s%s\n", + cap & (1 << 31) ? "64bit " : "", + cap & (1 << 30) ? "ncq " : "", + cap & (1 << 28) ? "ilck " : "", + cap & (1 << 27) ? "stag " : "", + cap & (1 << 26) ? "pm " : "", + cap & (1 << 25) ? "led " : "", + cap & (1 << 24) ? "clo " : "", + cap & (1 << 19) ? "nz " : "", + cap & (1 << 18) ? "only " : "", + cap & (1 << 17) ? "pmp " : "", + cap & (1 << 15) ? "pio " : "", + cap & (1 << 14) ? "slum " : "", + cap & (1 << 13) ? "part " : ""); +} + +static int ahci_init_one(int pdev) +{ + int rc; + struct ahci_probe_ent *probe_ent = NULL; + + probe_ent = malloc(sizeof(struct ahci_probe_ent)); + memset(probe_ent, 0, sizeof(struct ahci_probe_ent)); + probe_ent->dev = pdev; + + probe_ent->host_flags = ATA_FLAG_SATA + | ATA_FLAG_NO_LEGACY + | ATA_FLAG_MMIO + | ATA_FLAG_PIO_DMA + | ATA_FLAG_NO_ATAPI; + + probe_ent->mmio_base = CONFIG_DWC_AHSATA_BASE_ADDR; + + /* initialize adapter */ + rc = ahci_host_init(probe_ent); + if (rc) + goto err_out; + + ahci_print_info(probe_ent); + + /* Save the private struct to block device struct */ + sata_dev_desc[pdev].priv = (void *)probe_ent; + + return 0; + +err_out: + return rc; +} + +static int ahci_fill_sg(struct ahci_probe_ent *probe_ent, + u8 port, unsigned char *buf, int buf_len) +{ + struct ahci_ioports *pp = &(probe_ent->port[port]); + struct ahci_sg *ahci_sg = pp->cmd_tbl_sg; + u32 sg_count, max_bytes; + int i; + + max_bytes = MAX_DATA_BYTES_PER_SG; + sg_count = ((buf_len - 1) / max_bytes) + 1; + if (sg_count > AHCI_MAX_SG) { + printf("Error:Too much sg!\n"); + return -1; + } + + for (i = 0; i < sg_count; i++) { + ahci_sg->addr = + cpu_to_le32((u32)buf + i * max_bytes); + ahci_sg->addr_hi = 0; + ahci_sg->flags_size = cpu_to_le32(0x3fffff & + (buf_len < max_bytes + ? (buf_len - 1) + : (max_bytes - 1))); + ahci_sg++; + buf_len -= max_bytes; + } + + return sg_count; +} + +static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 cmd_slot, u32 opts) +{ + struct ahci_cmd_hdr *cmd_hdr = (struct ahci_cmd_hdr *)(pp->cmd_slot + + AHCI_CMD_SLOT_SZ * cmd_slot); + + memset(cmd_hdr, 0, AHCI_CMD_SLOT_SZ); + cmd_hdr->opts = cpu_to_le32(opts); + cmd_hdr->status = 0; + cmd_hdr->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff); + cmd_hdr->tbl_addr_hi = 0; +} + +#define AHCI_GET_CMD_SLOT(c) ((c) ? ffs(c) : 0) + +static int ahci_exec_ata_cmd(struct ahci_probe_ent *probe_ent, + u8 port, struct sata_fis_h2d *cfis, + u8 *buf, u32 buf_len, s32 is_write) +{ + struct ahci_ioports *pp = &(probe_ent->port[port]); + struct sata_port_regs *port_mmio = + (struct sata_port_regs *)pp->port_mmio; + u32 opts; + int sg_count = 0, cmd_slot = 0; + + cmd_slot = AHCI_GET_CMD_SLOT(readl(&(port_mmio->ci))); + if (32 == cmd_slot) { + printf("Can't find empty command slot!\n"); + return 0; + } + + /* Check xfer length */ + if (buf_len > MAX_BYTES_PER_TRANS) { + printf("Max transfer length is %dB\n\r", + MAX_BYTES_PER_TRANS); + return 0; + } + + memcpy((u8 *)(pp->cmd_tbl), cfis, sizeof(struct sata_fis_h2d)); + if (buf && buf_len) + sg_count = ahci_fill_sg(probe_ent, port, buf, buf_len); + opts = (sizeof(struct sata_fis_h2d) >> 2) | (sg_count << 16); + if (is_write) { + opts |= 0x40; + flush_cache((ulong)buf, buf_len); + } + ahci_fill_cmd_slot(pp, cmd_slot, opts); + + flush_cache((int)(pp->cmd_slot), AHCI_PORT_PRIV_DMA_SZ); + writel_with_flush(1 << cmd_slot, &(port_mmio->ci)); + + if (waiting_for_cmd_completed((u8 *)&(port_mmio->ci), + 10000, 0x1 << cmd_slot)) { + printf("timeout exit!\n"); + return -1; + } + invalidate_dcache_range((int)(pp->cmd_slot), + (int)(pp->cmd_slot)+AHCI_PORT_PRIV_DMA_SZ); + debug("ahci_exec_ata_cmd: %d byte transferred.\n", + pp->cmd_slot->status); + if (!is_write) + invalidate_dcache_range((ulong)buf, (ulong)buf+buf_len); + + return buf_len; +} + +static void ahci_set_feature(u8 dev, u8 port) +{ + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN); + struct sata_fis_h2d *cfis = &h2d; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 1 << 7; + cfis->command = ATA_CMD_SET_FEATURES; + cfis->features = SETFEATURES_XFER; + cfis->sector_count = ffs(probe_ent->udma_mask + 1) + 0x3e; + + ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, READ_CMD); +} + +static int ahci_port_start(struct ahci_probe_ent *probe_ent, + u8 port) +{ + struct ahci_ioports *pp = &(probe_ent->port[port]); + struct sata_port_regs *port_mmio = + (struct sata_port_regs *)pp->port_mmio; + u32 port_status; + u32 mem; + int timeout = 10000000; + + debug("Enter start port: %d\n", port); + port_status = readl(&(port_mmio->ssts)); + debug("Port %d status: %x\n", port, port_status); + if ((port_status & 0xf) != 0x03) { + printf("No Link on this port!\n"); + return -1; + } + + mem = (u32)malloc(AHCI_PORT_PRIV_DMA_SZ + 1024); + if (!mem) { + free(pp); + printf("No mem for table!\n"); + return -ENOMEM; + } + + mem = (mem + 0x400) & (~0x3ff); /* Aligned to 1024-bytes */ + memset((u8 *)mem, 0, AHCI_PORT_PRIV_DMA_SZ); + + /* + * First item in chunk of DMA memory: 32-slot command table, + * 32 bytes each in size + */ + pp->cmd_slot = (struct ahci_cmd_hdr *)mem; + debug("cmd_slot = 0x%x\n", (unsigned int) pp->cmd_slot); + mem += (AHCI_CMD_SLOT_SZ * DWC_AHSATA_MAX_CMD_SLOTS); + + /* + * Second item: Received-FIS area, 256-Byte aligned + */ + pp->rx_fis = mem; + mem += AHCI_RX_FIS_SZ; + + /* + * Third item: data area for storing a single command + * and its scatter-gather table + */ + pp->cmd_tbl = mem; + debug("cmd_tbl_dma = 0x%x\n", pp->cmd_tbl); + + mem += AHCI_CMD_TBL_HDR; + + writel_with_flush(0x00004444, &(port_mmio->dmacr)); + pp->cmd_tbl_sg = (struct ahci_sg *)mem; + writel_with_flush((u32)pp->cmd_slot, &(port_mmio->clb)); + writel_with_flush(pp->rx_fis, &(port_mmio->fb)); + + /* Enable FRE */ + writel_with_flush((SATA_PORT_CMD_FRE | readl(&(port_mmio->cmd))), + &(port_mmio->cmd)); + + /* Wait device ready */ + while ((readl(&(port_mmio->tfd)) & (SATA_PORT_TFD_STS_ERR | + SATA_PORT_TFD_STS_DRQ | SATA_PORT_TFD_STS_BSY)) + && --timeout) + ; + if (timeout <= 0) { + debug("Device not ready for BSY, DRQ and" + "ERR in TFD!\n"); + return -1; + } + + writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | + PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | + PORT_CMD_START, &(port_mmio->cmd)); + + debug("Exit start port %d\n", port); + + return 0; +} + +int init_sata(int dev) +{ + int i; + u32 linkmap; + struct ahci_probe_ent *probe_ent = NULL; + + if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) { + printf("The sata index %d is out of ranges\n\r", dev); + return -1; + } + + ahci_init_one(dev); + + probe_ent = (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + linkmap = probe_ent->link_port_map; + + if (0 == linkmap) { + printf("No port device detected!\n"); + return 1; + } + + for (i = 0; i < probe_ent->n_ports; i++) { + if ((linkmap >> i) && ((linkmap >> i) & 0x01)) { + if (ahci_port_start(probe_ent, (u8)i)) { + printf("Can not start port %d\n", i); + return 1; + } + probe_ent->hard_port_no = i; + break; + } + } + + return 0; +} + +static void dwc_ahsata_print_info(int dev) +{ + block_dev_desc_t *pdev = &(sata_dev_desc[dev]); + + printf("SATA Device Info:\n\r"); +#ifdef CONFIG_SYS_64BIT_LBA + printf("S/N: %s\n\rProduct model number: %s\n\r" + "Firmware version: %s\n\rCapacity: %lld sectors\n\r", + pdev->product, pdev->vendor, pdev->revision, pdev->lba); +#else + printf("S/N: %s\n\rProduct model number: %s\n\r" + "Firmware version: %s\n\rCapacity: %ld sectors\n\r", + pdev->product, pdev->vendor, pdev->revision, pdev->lba); +#endif +} + +static void dwc_ahsata_identify(int dev, u16 *id) +{ + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN); + struct sata_fis_h2d *cfis = &h2d; + u8 port = probe_ent->hard_port_no; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_ID_ATA; + + ahci_exec_ata_cmd(probe_ent, port, cfis, + (u8 *)id, ATA_ID_WORDS * 2, READ_CMD); + ata_swap_buf_le16(id, ATA_ID_WORDS); +} + +static void dwc_ahsata_xfer_mode(int dev, u16 *id) +{ + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + + probe_ent->pio_mask = id[ATA_ID_PIO_MODES]; + probe_ent->udma_mask = id[ATA_ID_UDMA_MODES]; + debug("pio %04x, udma %04x\n\r", + probe_ent->pio_mask, probe_ent->udma_mask); +} + +static u32 dwc_ahsata_rw_cmd(int dev, u32 start, u32 blkcnt, + u8 *buffer, int is_write) +{ + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN); + struct sata_fis_h2d *cfis = &h2d; + u8 port = probe_ent->hard_port_no; + u32 block; + + block = start; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ; + cfis->device = ATA_LBA; + + cfis->device |= (block >> 24) & 0xf; + cfis->lba_high = (block >> 16) & 0xff; + cfis->lba_mid = (block >> 8) & 0xff; + cfis->lba_low = block & 0xff; + cfis->sector_count = (u8)(blkcnt & 0xff); + + if (ahci_exec_ata_cmd(probe_ent, port, cfis, + buffer, ATA_SECT_SIZE * blkcnt, is_write) > 0) + return blkcnt; + else + return 0; +} + +void dwc_ahsata_flush_cache(int dev) +{ + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN); + struct sata_fis_h2d *cfis = &h2d; + u8 port = probe_ent->hard_port_no; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_FLUSH; + + ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0); +} + +static u32 dwc_ahsata_rw_cmd_ext(int dev, u32 start, lbaint_t blkcnt, + u8 *buffer, int is_write) +{ + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN); + struct sata_fis_h2d *cfis = &h2d; + u8 port = probe_ent->hard_port_no; + u64 block; + + block = (u64)start; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + + cfis->command = (is_write) ? ATA_CMD_WRITE_EXT + : ATA_CMD_READ_EXT; + + cfis->lba_high_exp = (block >> 40) & 0xff; + cfis->lba_mid_exp = (block >> 32) & 0xff; + cfis->lba_low_exp = (block >> 24) & 0xff; + cfis->lba_high = (block >> 16) & 0xff; + cfis->lba_mid = (block >> 8) & 0xff; + cfis->lba_low = block & 0xff; + cfis->device = ATA_LBA; + cfis->sector_count_exp = (blkcnt >> 8) & 0xff; + cfis->sector_count = blkcnt & 0xff; + + if (ahci_exec_ata_cmd(probe_ent, port, cfis, buffer, + ATA_SECT_SIZE * blkcnt, is_write) > 0) + return blkcnt; + else + return 0; +} + +u32 dwc_ahsata_rw_ncq_cmd(int dev, u32 start, lbaint_t blkcnt, + u8 *buffer, int is_write) +{ + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN); + struct sata_fis_h2d *cfis = &h2d; + u8 port = probe_ent->hard_port_no; + u64 block; + + if (sata_dev_desc[dev].lba48 != 1) { + printf("execute FPDMA command on non-LBA48 hard disk\n\r"); + return -1; + } + + block = (u64)start; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + + cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE + : ATA_CMD_FPDMA_READ; + + cfis->lba_high_exp = (block >> 40) & 0xff; + cfis->lba_mid_exp = (block >> 32) & 0xff; + cfis->lba_low_exp = (block >> 24) & 0xff; + cfis->lba_high = (block >> 16) & 0xff; + cfis->lba_mid = (block >> 8) & 0xff; + cfis->lba_low = block & 0xff; + + cfis->device = ATA_LBA; + cfis->features_exp = (blkcnt >> 8) & 0xff; + cfis->features = blkcnt & 0xff; + + /* Use the latest queue */ + ahci_exec_ata_cmd(probe_ent, port, cfis, + buffer, ATA_SECT_SIZE * blkcnt, is_write); + + return blkcnt; +} + +void dwc_ahsata_flush_cache_ext(int dev) +{ + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d __aligned(ARCH_DMA_MINALIGN); + struct sata_fis_h2d *cfis = &h2d; + u8 port = probe_ent->hard_port_no; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_FLUSH_EXT; + + ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0); +} + +static void dwc_ahsata_init_wcache(int dev, u16 *id) +{ + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + + if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id)) + probe_ent->flags |= SATA_FLAG_WCACHE; + if (ata_id_has_flush(id)) + probe_ent->flags |= SATA_FLAG_FLUSH; + if (ata_id_has_flush_ext(id)) + probe_ent->flags |= SATA_FLAG_FLUSH_EXT; +} + +u32 ata_low_level_rw_lba48(int dev, u32 blknr, lbaint_t blkcnt, + const void *buffer, int is_write) +{ + u32 start, blks; + u8 *addr; + int max_blks; + + start = blknr; + blks = blkcnt; + addr = (u8 *)buffer; + + max_blks = ATA_MAX_SECTORS_LBA48; + + do { + if (blks > max_blks) { + if (max_blks != dwc_ahsata_rw_cmd_ext(dev, start, + max_blks, addr, is_write)) + return 0; + start += max_blks; + blks -= max_blks; + addr += ATA_SECT_SIZE * max_blks; + } else { + if (blks != dwc_ahsata_rw_cmd_ext(dev, start, + blks, addr, is_write)) + return 0; + start += blks; + blks = 0; + addr += ATA_SECT_SIZE * blks; + } + } while (blks != 0); + + return blkcnt; +} + +u32 ata_low_level_rw_lba28(int dev, u32 blknr, lbaint_t blkcnt, + const void *buffer, int is_write) +{ + u32 start, blks; + u8 *addr; + int max_blks; + + start = blknr; + blks = blkcnt; + addr = (u8 *)buffer; + + max_blks = ATA_MAX_SECTORS; + do { + if (blks > max_blks) { + if (max_blks != dwc_ahsata_rw_cmd(dev, start, + max_blks, addr, is_write)) + return 0; + start += max_blks; + blks -= max_blks; + addr += ATA_SECT_SIZE * max_blks; + } else { + if (blks != dwc_ahsata_rw_cmd(dev, start, + blks, addr, is_write)) + return 0; + start += blks; + blks = 0; + addr += ATA_SECT_SIZE * blks; + } + } while (blks != 0); + + return blkcnt; +} + +/* + * SATA interface between low level driver and command layer + */ +ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer) +{ + u32 rc; + + if (sata_dev_desc[dev].lba48) + rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, + buffer, READ_CMD); + else + rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, + buffer, READ_CMD); + return rc; +} + +ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer) +{ + u32 rc; + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + u32 flags = probe_ent->flags; + + if (sata_dev_desc[dev].lba48) { + rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, + buffer, WRITE_CMD); + if ((flags & SATA_FLAG_WCACHE) && + (flags & SATA_FLAG_FLUSH_EXT)) + dwc_ahsata_flush_cache_ext(dev); + } else { + rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, + buffer, WRITE_CMD); + if ((flags & SATA_FLAG_WCACHE) && + (flags & SATA_FLAG_FLUSH)) + dwc_ahsata_flush_cache(dev); + } + return rc; +} + +int scan_sata(int dev) +{ + u8 serial[ATA_ID_SERNO_LEN + 1] = { 0 }; + u8 firmware[ATA_ID_FW_REV_LEN + 1] = { 0 }; + u8 product[ATA_ID_PROD_LEN + 1] = { 0 }; + u16 *id; + u64 n_sectors; + struct ahci_probe_ent *probe_ent = + (struct ahci_probe_ent *)sata_dev_desc[dev].priv; + u8 port = probe_ent->hard_port_no; + block_dev_desc_t *pdev = &(sata_dev_desc[dev]); + + id = (u16 *)memalign(ARCH_DMA_MINALIGN, + roundup(ARCH_DMA_MINALIGN, + (ATA_ID_WORDS * 2))); + if (!id) { + printf("id malloc failed\n\r"); + return -1; + } + + /* Identify device to get information */ + dwc_ahsata_identify(dev, id); + + /* Serial number */ + ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial)); + memcpy(pdev->product, serial, sizeof(serial)); + + /* Firmware version */ + ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware)); + memcpy(pdev->revision, firmware, sizeof(firmware)); + + /* Product model */ + ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product)); + memcpy(pdev->vendor, product, sizeof(product)); + + /* Totoal sectors */ + n_sectors = ata_id_n_sectors(id); + pdev->lba = (u32)n_sectors; + + pdev->type = DEV_TYPE_HARDDISK; + pdev->blksz = ATA_SECT_SIZE; + pdev->lun = 0 ; + + /* Check if support LBA48 */ + if (ata_id_has_lba48(id)) { + pdev->lba48 = 1; + debug("Device support LBA48\n\r"); + } + + /* Get the NCQ queue depth from device */ + probe_ent->flags &= (~SATA_FLAG_Q_DEP_MASK); + probe_ent->flags |= ata_id_queue_depth(id); + + /* Get the xfer mode from device */ + dwc_ahsata_xfer_mode(dev, id); + + /* Get the write cache status from device */ + dwc_ahsata_init_wcache(dev, id); + + /* Set the xfer mode to highest speed */ + ahci_set_feature(dev, port); + + free((void *)id); + + dwc_ahsata_print_info(dev); + + is_ready = 1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/block/dwc_ahsata.h b/qemu/roms/u-boot/drivers/block/dwc_ahsata.h new file mode 100644 index 000000000..caa2e501f --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/dwc_ahsata.h @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Terry Lv + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __FSL_SATA_H__ +#define __FSL_SATA_H__ + +#define DWC_AHSATA_MAX_CMD_SLOTS 32 + +/* Max host controller numbers */ +#define SATA_HC_MAX_NUM 4 +/* Max command queue depth per host controller */ +#define DWC_AHSATA_HC_MAX_CMD 32 +/* Max port number per host controller */ +#define SATA_HC_MAX_PORT 16 + +/* Generic Host Register */ + +/* HBA Capabilities Register */ +#define SATA_HOST_CAP_S64A 0x80000000 +#define SATA_HOST_CAP_SNCQ 0x40000000 +#define SATA_HOST_CAP_SSNTF 0x20000000 +#define SATA_HOST_CAP_SMPS 0x10000000 +#define SATA_HOST_CAP_SSS 0x08000000 +#define SATA_HOST_CAP_SALP 0x04000000 +#define SATA_HOST_CAP_SAL 0x02000000 +#define SATA_HOST_CAP_SCLO 0x01000000 +#define SATA_HOST_CAP_ISS_MASK 0x00f00000 +#define SATA_HOST_CAP_ISS_OFFSET 20 +#define SATA_HOST_CAP_SNZO 0x00080000 +#define SATA_HOST_CAP_SAM 0x00040000 +#define SATA_HOST_CAP_SPM 0x00020000 +#define SATA_HOST_CAP_PMD 0x00008000 +#define SATA_HOST_CAP_SSC 0x00004000 +#define SATA_HOST_CAP_PSC 0x00002000 +#define SATA_HOST_CAP_NCS 0x00001f00 +#define SATA_HOST_CAP_CCCS 0x00000080 +#define SATA_HOST_CAP_EMS 0x00000040 +#define SATA_HOST_CAP_SXS 0x00000020 +#define SATA_HOST_CAP_NP_MASK 0x0000001f + +/* Global HBA Control Register */ +#define SATA_HOST_GHC_AE 0x80000000 +#define SATA_HOST_GHC_IE 0x00000002 +#define SATA_HOST_GHC_HR 0x00000001 + +/* Interrupt Status Register */ + +/* Ports Implemented Register */ + +/* AHCI Version Register */ +#define SATA_HOST_VS_MJR_MASK 0xffff0000 +#define SATA_HOST_VS_MJR_OFFSET 16 +#define SATA_HOST_VS_MJR_MNR 0x0000ffff + +/* Command Completion Coalescing Control */ +#define SATA_HOST_CCC_CTL_TV_MASK 0xffff0000 +#define SATA_HOST_CCC_CTL_TV_OFFSET 16 +#define SATA_HOST_CCC_CTL_CC_MASK 0x0000ff00 +#define SATA_HOST_CCC_CTL_CC_OFFSET 8 +#define SATA_HOST_CCC_CTL_INT_MASK 0x000000f8 +#define SATA_HOST_CCC_CTL_INT_OFFSET 3 +#define SATA_HOST_CCC_CTL_EN 0x00000001 + +/* Command Completion Coalescing Ports */ + +/* HBA Capabilities Extended Register */ +#define SATA_HOST_CAP2_APST 0x00000004 + +/* BIST Activate FIS Register */ +#define SATA_HOST_BISTAFR_NCP_MASK 0x0000ff00 +#define SATA_HOST_BISTAFR_NCP_OFFSET 8 +#define SATA_HOST_BISTAFR_PD_MASK 0x000000ff +#define SATA_HOST_BISTAFR_PD_OFFSET 0 + +/* BIST Control Register */ +#define SATA_HOST_BISTCR_FERLB 0x00100000 +#define SATA_HOST_BISTCR_TXO 0x00040000 +#define SATA_HOST_BISTCR_CNTCLR 0x00020000 +#define SATA_HOST_BISTCR_NEALB 0x00010000 +#define SATA_HOST_BISTCR_LLC_MASK 0x00000700 +#define SATA_HOST_BISTCR_LLC_OFFSET 8 +#define SATA_HOST_BISTCR_ERREN 0x00000040 +#define SATA_HOST_BISTCR_FLIP 0x00000020 +#define SATA_HOST_BISTCR_PV 0x00000010 +#define SATA_HOST_BISTCR_PATTERN_MASK 0x0000000f +#define SATA_HOST_BISTCR_PATTERN_OFFSET 0 + +/* BIST FIS Count Register */ + +/* BIST Status Register */ +#define SATA_HOST_BISTSR_FRAMERR_MASK 0x0000ffff +#define SATA_HOST_BISTSR_FRAMERR_OFFSET 0 +#define SATA_HOST_BISTSR_BRSTERR_MASK 0x00ff0000 +#define SATA_HOST_BISTSR_BRSTERR_OFFSET 16 + +/* BIST DWORD Error Count Register */ + +/* OOB Register*/ +#define SATA_HOST_OOBR_WE 0x80000000 +#define SATA_HOST_OOBR_cwMin_MASK 0x7f000000 +#define SATA_HOST_OOBR_cwMAX_MASK 0x00ff0000 +#define SATA_HOST_OOBR_ciMin_MASK 0x0000ff00 +#define SATA_HOST_OOBR_ciMax_MASK 0x000000ff + +/* Timer 1-ms Register */ + +/* Global Parameter 1 Register */ +#define SATA_HOST_GPARAM1R_ALIGN_M 0x80000000 +#define SATA_HOST_GPARAM1R_RX_BUFFER 0x40000000 +#define SATA_HOST_GPARAM1R_PHY_DATA_MASK 0x30000000 +#define SATA_HOST_GPARAM1R_PHY_RST 0x08000000 +#define SATA_HOST_GPARAM1R_PHY_CTRL_MASK 0x07e00000 +#define SATA_HOST_GPARAM1R_PHY_STAT_MASK 0x001f8000 +#define SATA_HOST_GPARAM1R_LATCH_M 0x00004000 +#define SATA_HOST_GPARAM1R_BIST_M 0x00002000 +#define SATA_HOST_GPARAM1R_PHY_TYPE 0x00001000 +#define SATA_HOST_GPARAM1R_RETURN_ERR 0x00000400 +#define SATA_HOST_GPARAM1R_AHB_ENDIAN_MASK 0x00000300 +#define SATA_HOST_GPARAM1R_S_HADDR 0X00000080 +#define SATA_HOST_GPARAM1R_M_HADDR 0X00000040 + +/* Global Parameter 2 Register */ +#define SATA_HOST_GPARAM2R_DEV_CP 0x00004000 +#define SATA_HOST_GPARAM2R_DEV_MP 0x00002000 +#define SATA_HOST_GPARAM2R_DEV_ENCODE_M 0x00001000 +#define SATA_HOST_GPARAM2R_RXOOB_CLK_M 0x00000800 +#define SATA_HOST_GPARAM2R_RXOOB_M 0x00000400 +#define SATA_HOST_GPARAM2R_TX_OOB_M 0x00000200 +#define SATA_HOST_GPARAM2R_RXOOB_CLK_MASK 0x000001ff + +/* Port Parameter Register */ +#define SATA_HOST_PPARAMR_TX_MEM_M 0x00000200 +#define SATA_HOST_PPARAMR_TX_MEM_S 0x00000100 +#define SATA_HOST_PPARAMR_RX_MEM_M 0x00000080 +#define SATA_HOST_PPARAMR_RX_MEM_S 0x00000040 +#define SATA_HOST_PPARAMR_TXFIFO_DEPTH_MASK 0x00000038 +#define SATA_HOST_PPARAMR_RXFIFO_DEPTH_MASK 0x00000007 + +/* Test Register */ +#define SATA_HOST_TESTR_PSEL_MASK 0x00070000 +#define SATA_HOST_TESTR_TEST_IF 0x00000001 + +/* Port Register Descriptions */ +/* Port# Command List Base Address Register */ +#define SATA_PORT_CLB_CLB_MASK 0xfffffc00 + +/* Port# Command List Base Address Upper 32-Bits Register */ + +/* Port# FIS Base Address Register */ +#define SATA_PORT_FB_FB_MASK 0xfffffff0 + +/* Port# FIS Base Address Upper 32-Bits Register */ + +/* Port# Interrupt Status Register */ +#define SATA_PORT_IS_CPDS 0x80000000 +#define SATA_PORT_IS_TFES 0x40000000 +#define SATA_PORT_IS_HBFS 0x20000000 +#define SATA_PORT_IS_HBDS 0x10000000 +#define SATA_PORT_IS_IFS 0x08000000 +#define SATA_PORT_IS_INFS 0x04000000 +#define SATA_PORT_IS_OFS 0x01000000 +#define SATA_PORT_IS_IPMS 0x00800000 +#define SATA_PORT_IS_PRCS 0x00400000 +#define SATA_PORT_IS_DMPS 0x00000080 +#define SATA_PORT_IS_PCS 0x00000040 +#define SATA_PORT_IS_DPS 0x00000020 +#define SATA_PORT_IS_UFS 0x00000010 +#define SATA_PORT_IS_SDBS 0x00000008 +#define SATA_PORT_IS_DSS 0x00000004 +#define SATA_PORT_IS_PSS 0x00000002 +#define SATA_PORT_IS_DHRS 0x00000001 + +/* Port# Interrupt Enable Register */ +#define SATA_PORT_IE_CPDE 0x80000000 +#define SATA_PORT_IE_TFEE 0x40000000 +#define SATA_PORT_IE_HBFE 0x20000000 +#define SATA_PORT_IE_HBDE 0x10000000 +#define SATA_PORT_IE_IFE 0x08000000 +#define SATA_PORT_IE_INFE 0x04000000 +#define SATA_PORT_IE_OFE 0x01000000 +#define SATA_PORT_IE_IPME 0x00800000 +#define SATA_PORT_IE_PRCE 0x00400000 +#define SATA_PORT_IE_DMPE 0x00000080 +#define SATA_PORT_IE_PCE 0x00000040 +#define SATA_PORT_IE_DPE 0x00000020 +#define SATA_PORT_IE_UFE 0x00000010 +#define SATA_PORT_IE_SDBE 0x00000008 +#define SATA_PORT_IE_DSE 0x00000004 +#define SATA_PORT_IE_PSE 0x00000002 +#define SATA_PORT_IE_DHRE 0x00000001 + +/* Port# Command Register */ +#define SATA_PORT_CMD_ICC_MASK 0xf0000000 +#define SATA_PORT_CMD_ASP 0x08000000 +#define SATA_PORT_CMD_ALPE 0x04000000 +#define SATA_PORT_CMD_DLAE 0x02000000 +#define SATA_PORT_CMD_ATAPI 0x01000000 +#define SATA_PORT_CMD_APSTE 0x00800000 +#define SATA_PORT_CMD_ESP 0x00200000 +#define SATA_PORT_CMD_CPD 0x00100000 +#define SATA_PORT_CMD_MPSP 0x00080000 +#define SATA_PORT_CMD_HPCP 0x00040000 +#define SATA_PORT_CMD_PMA 0x00020000 +#define SATA_PORT_CMD_CPS 0x00010000 +#define SATA_PORT_CMD_CR 0x00008000 +#define SATA_PORT_CMD_FR 0x00004000 +#define SATA_PORT_CMD_MPSS 0x00002000 +#define SATA_PORT_CMD_CCS_MASK 0x00001f00 +#define SATA_PORT_CMD_FRE 0x00000010 +#define SATA_PORT_CMD_CLO 0x00000008 +#define SATA_PORT_CMD_POD 0x00000004 +#define SATA_PORT_CMD_SUD 0x00000002 +#define SATA_PORT_CMD_ST 0x00000001 + +/* Port# Task File Data Register */ +#define SATA_PORT_TFD_ERR_MASK 0x0000ff00 +#define SATA_PORT_TFD_STS_MASK 0x000000ff +#define SATA_PORT_TFD_STS_ERR 0x00000001 +#define SATA_PORT_TFD_STS_DRQ 0x00000008 +#define SATA_PORT_TFD_STS_BSY 0x00000080 + +/* Port# Signature Register */ + +/* Port# Serial ATA Status {SStatus} Register */ +#define SATA_PORT_SSTS_IPM_MASK 0x00000f00 +#define SATA_PORT_SSTS_SPD_MASK 0x000000f0 +#define SATA_PORT_SSTS_DET_MASK 0x0000000f + +/* Port# Serial ATA Control {SControl} Register */ +#define SATA_PORT_SCTL_IPM_MASK 0x00000f00 +#define SATA_PORT_SCTL_SPD_MASK 0x000000f0 +#define SATA_PORT_SCTL_DET_MASK 0x0000000f + +/* Port# Serial ATA Error {SError} Register */ +#define SATA_PORT_SERR_DIAG_X 0x04000000 +#define SATA_PORT_SERR_DIAG_F 0x02000000 +#define SATA_PORT_SERR_DIAG_T 0x01000000 +#define SATA_PORT_SERR_DIAG_S 0x00800000 +#define SATA_PORT_SERR_DIAG_H 0x00400000 +#define SATA_PORT_SERR_DIAG_C 0x00200000 +#define SATA_PORT_SERR_DIAG_D 0x00100000 +#define SATA_PORT_SERR_DIAG_B 0x00080000 +#define SATA_PORT_SERR_DIAG_W 0x00040000 +#define SATA_PORT_SERR_DIAG_I 0x00020000 +#define SATA_PORT_SERR_DIAG_N 0x00010000 +#define SATA_PORT_SERR_ERR_E 0x00000800 +#define SATA_PORT_SERR_ERR_P 0x00000400 +#define SATA_PORT_SERR_ERR_C 0x00000200 +#define SATA_PORT_SERR_ERR_T 0x00000100 +#define SATA_PORT_SERR_ERR_M 0x00000002 +#define SATA_PORT_SERR_ERR_I 0x00000001 + +/* Port# Serial ATA Active {SActive} Register */ + +/* Port# Command Issue Register */ + +/* Port# Serial ATA Notification Register */ + +/* Port# DMA Control Register */ +#define SATA_PORT_DMACR_RXABL_MASK 0x0000f000 +#define SATA_PORT_DMACR_TXABL_MASK 0x00000f00 +#define SATA_PORT_DMACR_RXTS_MASK 0x000000f0 +#define SATA_PORT_DMACR_TXTS_MASK 0x0000000f + +/* Port# PHY Control Register */ + +/* Port# PHY Status Register */ + +#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry) + +/* DW0 +*/ +#define CMD_HDR_DI_CFL_MASK 0x0000001f +#define CMD_HDR_DI_CFL_OFFSET 0 +#define CMD_HDR_DI_A 0x00000020 +#define CMD_HDR_DI_W 0x00000040 +#define CMD_HDR_DI_P 0x00000080 +#define CMD_HDR_DI_R 0x00000100 +#define CMD_HDR_DI_B 0x00000200 +#define CMD_HDR_DI_C 0x00000400 +#define CMD_HDR_DI_PMP_MASK 0x0000f000 +#define CMD_HDR_DI_PMP_OFFSET 12 +#define CMD_HDR_DI_PRDTL 0xffff0000 +#define CMD_HDR_DI_PRDTL_OFFSET 16 + +/* prde_fis_len +*/ +#define CMD_HDR_PRD_ENTRY_SHIFT 16 +#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000 +#define CMD_HDR_FIS_LEN_SHIFT 2 + +/* attribute +*/ +#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */ +#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */ +/* Snoop enable for all descriptor */ +#define CMD_HDR_ATTR_SNOOP 0x00000200 +#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */ +#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */ +/* BIST - require the host to enter BIST mode */ +#define CMD_HDR_ATTR_BIST 0x00000040 +#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */ +#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */ + +#define FLAGS_DMA 0x00000000 +#define FLAGS_FPDMA 0x00000001 + +#define SATA_FLAG_Q_DEP_MASK 0x0000000f +#define SATA_FLAG_WCACHE 0x00000100 +#define SATA_FLAG_FLUSH 0x00000200 +#define SATA_FLAG_FLUSH_EXT 0x00000400 + +#define READ_CMD 0 +#define WRITE_CMD 1 + +#endif /* __FSL_SATA_H__ */ diff --git a/qemu/roms/u-boot/drivers/block/fsl_sata.c b/qemu/roms/u-boot/drivers/block/fsl_sata.c new file mode 100644 index 000000000..ebd626178 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/fsl_sata.c @@ -0,0 +1,848 @@ +/* + * Copyright (C) 2008,2010 Freescale Semiconductor, Inc. + * Dave Liu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsl_sata.h" + +#ifndef CONFIG_SYS_SATA1_FLAGS + #define CONFIG_SYS_SATA1_FLAGS FLAGS_DMA +#endif +#ifndef CONFIG_SYS_SATA2_FLAGS + #define CONFIG_SYS_SATA2_FLAGS FLAGS_DMA +#endif + +static struct fsl_sata_info fsl_sata_info[] = { +#ifdef CONFIG_SATA1 + {CONFIG_SYS_SATA1, CONFIG_SYS_SATA1_FLAGS}, +#else + {0, 0}, +#endif +#ifdef CONFIG_SATA2 + {CONFIG_SYS_SATA2, CONFIG_SYS_SATA2_FLAGS}, +#else + {0, 0}, +#endif +}; + +static inline void sdelay(unsigned long sec) +{ + unsigned long i; + for (i = 0; i < sec; i++) + mdelay(1000); +} + +static void fsl_sata_dump_sfis(struct sata_fis_d2h *s) +{ + printf("Status FIS dump:\n\r"); + printf("fis_type: %02x\n\r", s->fis_type); + printf("pm_port_i: %02x\n\r", s->pm_port_i); + printf("status: %02x\n\r", s->status); + printf("error: %02x\n\r", s->error); + printf("lba_low: %02x\n\r", s->lba_low); + printf("lba_mid: %02x\n\r", s->lba_mid); + printf("lba_high: %02x\n\r", s->lba_high); + printf("device: %02x\n\r", s->device); + printf("lba_low_exp: %02x\n\r", s->lba_low_exp); + printf("lba_mid_exp: %02x\n\r", s->lba_mid_exp); + printf("lba_high_exp: %02x\n\r", s->lba_high_exp); + printf("res1: %02x\n\r", s->res1); + printf("sector_count: %02x\n\r", s->sector_count); + printf("sector_count_exp: %02x\n\r", s->sector_count_exp); +} + +static int ata_wait_register(unsigned __iomem *addr, u32 mask, + u32 val, u32 timeout_msec) +{ + int i; + u32 temp; + + for (i = 0; (((temp = in_le32(addr)) & mask) != val) + && i < timeout_msec; i++) + mdelay(1); + return (i < timeout_msec) ? 0 : -1; +} + +int init_sata(int dev) +{ + u32 length, align; + cmd_hdr_tbl_t *cmd_hdr; + u32 cda; + u32 val32; + fsl_sata_reg_t __iomem *reg; + u32 sig; + int i; + fsl_sata_t *sata; + + if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) { + printf("the sata index %d is out of ranges\n\r", dev); + return -1; + } + +#ifdef CONFIG_MPC85xx + if ((dev == 0) && (!is_serdes_configured(SATA1))) { + printf("SATA%d [dev = %d] is not enabled\n", dev+1, dev); + return -1; + } + if ((dev == 1) && (!is_serdes_configured(SATA2))) { + printf("SATA%d [dev = %d] is not enabled\n", dev+1, dev); + return -1; + } +#endif + + /* Allocate SATA device driver struct */ + sata = (fsl_sata_t *)malloc(sizeof(fsl_sata_t)); + if (!sata) { + printf("alloc the sata device struct failed\n\r"); + return -1; + } + /* Zero all of the device driver struct */ + memset((void *)sata, 0, sizeof(fsl_sata_t)); + + /* Save the private struct to block device struct */ + sata_dev_desc[dev].priv = (void *)sata; + + sprintf(sata->name, "SATA%d", dev); + + /* Set the controller register base address to device struct */ + reg = (fsl_sata_reg_t *)(fsl_sata_info[dev].sata_reg_base); + sata->reg_base = reg; + + /* Allocate the command header table, 4 bytes aligned */ + length = sizeof(struct cmd_hdr_tbl); + align = SATA_HC_CMD_HDR_TBL_ALIGN; + sata->cmd_hdr_tbl_offset = (void *)malloc(length + align); + if (!sata) { + printf("alloc the command header failed\n\r"); + return -1; + } + + cmd_hdr = (cmd_hdr_tbl_t *)(((u32)sata->cmd_hdr_tbl_offset + align) + & ~(align - 1)); + sata->cmd_hdr = cmd_hdr; + + /* Zero all of the command header table */ + memset((void *)sata->cmd_hdr_tbl_offset, 0, length + align); + + /* Allocate command descriptor for all command */ + length = sizeof(struct cmd_desc) * SATA_HC_MAX_CMD; + align = SATA_HC_CMD_DESC_ALIGN; + sata->cmd_desc_offset = (void *)malloc(length + align); + if (!sata->cmd_desc_offset) { + printf("alloc the command descriptor failed\n\r"); + return -1; + } + sata->cmd_desc = (cmd_desc_t *)(((u32)sata->cmd_desc_offset + align) + & ~(align - 1)); + /* Zero all of command descriptor */ + memset((void *)sata->cmd_desc_offset, 0, length + align); + + /* Link the command descriptor to command header */ + for (i = 0; i < SATA_HC_MAX_CMD; i++) { + cda = ((u32)sata->cmd_desc + SATA_HC_CMD_DESC_SIZE * i) + & ~(CMD_HDR_CDA_ALIGN - 1); + cmd_hdr->cmd_slot[i].cda = cpu_to_le32(cda); + } + + /* To have safe state, force the controller offline */ + val32 = in_le32(®->hcontrol); + val32 &= ~HCONTROL_ONOFF; + val32 |= HCONTROL_FORCE_OFFLINE; + out_le32(®->hcontrol, val32); + + /* Wait the controller offline */ + ata_wait_register(®->hstatus, HSTATUS_ONOFF, 0, 1000); + + /* Set the command header base address to CHBA register to tell DMA */ + out_le32(®->chba, (u32)cmd_hdr & ~0x3); + + /* Snoop for the command header */ + val32 = in_le32(®->hcontrol); + val32 |= HCONTROL_HDR_SNOOP; + out_le32(®->hcontrol, val32); + + /* Disable all of interrupts */ + val32 = in_le32(®->hcontrol); + val32 &= ~HCONTROL_INT_EN_ALL; + out_le32(®->hcontrol, val32); + + /* Clear all of interrupts */ + val32 = in_le32(®->hstatus); + out_le32(®->hstatus, val32); + + /* Set the ICC, no interrupt coalescing */ + out_le32(®->icc, 0x01000000); + + /* No PM attatched, the SATA device direct connect */ + out_le32(®->cqpmp, 0); + + /* Clear SError register */ + val32 = in_le32(®->serror); + out_le32(®->serror, val32); + + /* Clear CER register */ + val32 = in_le32(®->cer); + out_le32(®->cer, val32); + + /* Clear DER register */ + val32 = in_le32(®->der); + out_le32(®->der, val32); + + /* No device detection or initialization action requested */ + out_le32(®->scontrol, 0x00000300); + + /* Configure the transport layer, default value */ + out_le32(®->transcfg, 0x08000016); + + /* Configure the link layer, default value */ + out_le32(®->linkcfg, 0x0000ff34); + + /* Bring the controller online */ + val32 = in_le32(®->hcontrol); + val32 |= HCONTROL_ONOFF; + out_le32(®->hcontrol, val32); + + mdelay(100); + + /* print sata device name */ + if (!dev) + printf("%s ", sata->name); + else + printf(" %s ", sata->name); + + /* Wait PHY RDY signal changed for 500ms */ + ata_wait_register(®->hstatus, HSTATUS_PHY_RDY, + HSTATUS_PHY_RDY, 500); + + /* Check PHYRDY */ + val32 = in_le32(®->hstatus); + if (val32 & HSTATUS_PHY_RDY) { + sata->link = 1; + } else { + sata->link = 0; + printf("(No RDY)\n\r"); + return -1; + } + + /* Wait for signature updated, which is 1st D2H */ + ata_wait_register(®->hstatus, HSTATUS_SIGNATURE, + HSTATUS_SIGNATURE, 10000); + + if (val32 & HSTATUS_SIGNATURE) { + sig = in_le32(®->sig); + debug("Signature updated, the sig =%08x\n\r", sig); + sata->ata_device_type = ata_dev_classify(sig); + } + + /* Check the speed */ + val32 = in_le32(®->sstatus); + if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN1) + printf("(1.5 Gbps)\n\r"); + else if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN2) + printf("(3 Gbps)\n\r"); + + return 0; +} + +static void fsl_sata_dump_regs(fsl_sata_reg_t __iomem *reg) +{ + printf("\n\rSATA: %08x\n\r", (u32)reg); + printf("CQR: %08x\n\r", in_le32(®->cqr)); + printf("CAR: %08x\n\r", in_le32(®->car)); + printf("CCR: %08x\n\r", in_le32(®->ccr)); + printf("CER: %08x\n\r", in_le32(®->cer)); + printf("CQR: %08x\n\r", in_le32(®->cqr)); + printf("DER: %08x\n\r", in_le32(®->der)); + printf("CHBA: %08x\n\r", in_le32(®->chba)); + printf("HStatus: %08x\n\r", in_le32(®->hstatus)); + printf("HControl: %08x\n\r", in_le32(®->hcontrol)); + printf("CQPMP: %08x\n\r", in_le32(®->cqpmp)); + printf("SIG: %08x\n\r", in_le32(®->sig)); + printf("ICC: %08x\n\r", in_le32(®->icc)); + printf("SStatus: %08x\n\r", in_le32(®->sstatus)); + printf("SError: %08x\n\r", in_le32(®->serror)); + printf("SControl: %08x\n\r", in_le32(®->scontrol)); + printf("SNotification: %08x\n\r", in_le32(®->snotification)); + printf("TransCfg: %08x\n\r", in_le32(®->transcfg)); + printf("TransStatus: %08x\n\r", in_le32(®->transstatus)); + printf("LinkCfg: %08x\n\r", in_le32(®->linkcfg)); + printf("LinkCfg1: %08x\n\r", in_le32(®->linkcfg1)); + printf("LinkCfg2: %08x\n\r", in_le32(®->linkcfg2)); + printf("LinkStatus: %08x\n\r", in_le32(®->linkstatus)); + printf("LinkStatus1: %08x\n\r", in_le32(®->linkstatus1)); + printf("PhyCtrlCfg: %08x\n\r", in_le32(®->phyctrlcfg)); + printf("SYSPR: %08x\n\r", in_be32(®->syspr)); +} + +static int fsl_ata_exec_ata_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis, + int is_ncq, int tag, u8 *buffer, u32 len) +{ + cmd_hdr_entry_t *cmd_hdr; + cmd_desc_t *cmd_desc; + sata_fis_h2d_t *h2d; + prd_entry_t *prde; + u32 ext_c_ddc; + u32 prde_count; + u32 val32; + u32 ttl; + fsl_sata_reg_t __iomem *reg = sata->reg_base; + int i; + + /* Check xfer length */ + if (len > SATA_HC_MAX_XFER_LEN) { + printf("max transfer length is 64MB\n\r"); + return 0; + } + + /* Setup the command descriptor */ + cmd_desc = sata->cmd_desc + tag; + + /* Get the pointer cfis of command descriptor */ + h2d = (sata_fis_h2d_t *)cmd_desc->cfis; + + /* Zero the cfis of command descriptor */ + memset((void *)h2d, 0, SATA_HC_CMD_DESC_CFIS_SIZE); + + /* Copy the cfis from user to command descriptor */ + h2d->fis_type = cfis->fis_type; + h2d->pm_port_c = cfis->pm_port_c; + h2d->command = cfis->command; + + h2d->features = cfis->features; + h2d->features_exp = cfis->features_exp; + + h2d->lba_low = cfis->lba_low; + h2d->lba_mid = cfis->lba_mid; + h2d->lba_high = cfis->lba_high; + h2d->lba_low_exp = cfis->lba_low_exp; + h2d->lba_mid_exp = cfis->lba_mid_exp; + h2d->lba_high_exp = cfis->lba_high_exp; + + if (!is_ncq) { + h2d->sector_count = cfis->sector_count; + h2d->sector_count_exp = cfis->sector_count_exp; + } else { /* NCQ */ + h2d->sector_count = (u8)(tag << 3); + } + + h2d->device = cfis->device; + h2d->control = cfis->control; + + /* Setup the PRD table */ + prde = (prd_entry_t *)cmd_desc->prdt; + memset((void *)prde, 0, sizeof(struct prdt)); + + prde_count = 0; + ttl = len; + for (i = 0; i < SATA_HC_MAX_PRD_DIRECT; i++) { + if (!len) + break; + prde->dba = cpu_to_le32((u32)buffer & ~0x3); + debug("dba = %08x\n\r", (u32)buffer); + + if (len < PRD_ENTRY_MAX_XFER_SZ) { + ext_c_ddc = PRD_ENTRY_DATA_SNOOP | len; + debug("ext_c_ddc1 = %08x, len = %08x\n\r", ext_c_ddc, len); + prde->ext_c_ddc = cpu_to_le32(ext_c_ddc); + prde_count++; + prde++; + break; + } else { + ext_c_ddc = PRD_ENTRY_DATA_SNOOP; /* 4M bytes */ + debug("ext_c_ddc2 = %08x, len = %08x\n\r", ext_c_ddc, len); + prde->ext_c_ddc = cpu_to_le32(ext_c_ddc); + buffer += PRD_ENTRY_MAX_XFER_SZ; + len -= PRD_ENTRY_MAX_XFER_SZ; + prde_count++; + prde++; + } + } + + /* Setup the command slot of cmd hdr */ + cmd_hdr = (cmd_hdr_entry_t *)&sata->cmd_hdr->cmd_slot[tag]; + + cmd_hdr->cda = cpu_to_le32((u32)cmd_desc & ~0x3); + + val32 = prde_count << CMD_HDR_PRD_ENTRY_SHIFT; + val32 |= sizeof(sata_fis_h2d_t); + cmd_hdr->prde_fis_len = cpu_to_le32(val32); + + cmd_hdr->ttl = cpu_to_le32(ttl); + + if (!is_ncq) { + val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP; + } else { + val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP | CMD_HDR_ATTR_FPDMA; + } + + tag &= CMD_HDR_ATTR_TAG; + val32 |= tag; + + debug("attribute = %08x\n\r", val32); + cmd_hdr->attribute = cpu_to_le32(val32); + + /* Make sure cmd desc and cmd slot valid before commmand issue */ + sync(); + + /* PMP*/ + val32 = (u32)(h2d->pm_port_c & 0x0f); + out_le32(®->cqpmp, val32); + + /* Wait no active */ + if (ata_wait_register(®->car, (1 << tag), 0, 10000)) + printf("Wait no active time out\n\r"); + + /* Issue command */ + if (!(in_le32(®->cqr) & (1 << tag))) { + val32 = 1 << tag; + out_le32(®->cqr, val32); + } + + /* Wait command completed for 10s */ + if (ata_wait_register(®->ccr, (1 << tag), (1 << tag), 10000)) { + if (!is_ncq) + printf("Non-NCQ command time out\n\r"); + else + printf("NCQ command time out\n\r"); + } + + val32 = in_le32(®->cer); + + if (val32) { + u32 der; + fsl_sata_dump_sfis((struct sata_fis_d2h *)cmd_desc->sfis); + printf("CE at device\n\r"); + fsl_sata_dump_regs(reg); + der = in_le32(®->der); + out_le32(®->cer, val32); + out_le32(®->der, der); + } + + /* Clear complete flags */ + val32 = in_le32(®->ccr); + out_le32(®->ccr, val32); + + return len; +} + +static int fsl_ata_exec_reset_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis, + int tag, u8 *buffer, u32 len) +{ + return 0; +} + +static int fsl_sata_exec_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis, + enum cmd_type command_type, int tag, u8 *buffer, u32 len) +{ + int rc; + + if (tag > SATA_HC_MAX_CMD || tag < 0) { + printf("tag is out of range, tag=%d\n\r", tag); + return -1; + } + + switch (command_type) { + case CMD_ATA: + rc = fsl_ata_exec_ata_cmd(sata, cfis, 0, tag, buffer, len); + return rc; + case CMD_RESET: + rc = fsl_ata_exec_reset_cmd(sata, cfis, tag, buffer, len); + return rc; + case CMD_NCQ: + rc = fsl_ata_exec_ata_cmd(sata, cfis, 1, tag, buffer, len); + return rc; + case CMD_ATAPI: + case CMD_VENDOR_BIST: + case CMD_BIST: + printf("not support now\n\r"); + return -1; + default: + break; + } + + return -1; +} + +static void fsl_sata_identify(int dev, u16 *id) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d, *cfis = &h2d; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_ID_ATA; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, (u8 *)id, ATA_ID_WORDS * 2); + ata_swap_buf_le16(id, ATA_ID_WORDS); +} + +static void fsl_sata_xfer_mode(int dev, u16 *id) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + + sata->pio = id[ATA_ID_PIO_MODES]; + sata->mwdma = id[ATA_ID_MWDMA_MODES]; + sata->udma = id[ATA_ID_UDMA_MODES]; + debug("pio %04x, mwdma %04x, udma %04x\n\r", sata->pio, sata->mwdma, sata->udma); +} + +static void fsl_sata_set_features(int dev) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d, *cfis = &h2d; + u8 udma_cap; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_SET_FEATURES; + cfis->features = SETFEATURES_XFER; + + /* First check the device capablity */ + udma_cap = (u8)(sata->udma & 0xff); + debug("udma_cap %02x\n\r", udma_cap); + + if (udma_cap == ATA_UDMA6) + cfis->sector_count = XFER_UDMA_6; + if (udma_cap == ATA_UDMA5) + cfis->sector_count = XFER_UDMA_5; + if (udma_cap == ATA_UDMA4) + cfis->sector_count = XFER_UDMA_4; + if (udma_cap == ATA_UDMA3) + cfis->sector_count = XFER_UDMA_3; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0); +} + +static u32 fsl_sata_rw_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d, *cfis = &h2d; + u32 block; + + block = start; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ; + cfis->device = ATA_LBA; + + cfis->device |= (block >> 24) & 0xf; + cfis->lba_high = (block >> 16) & 0xff; + cfis->lba_mid = (block >> 8) & 0xff; + cfis->lba_low = block & 0xff; + cfis->sector_count = (u8)(blkcnt & 0xff); + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, ATA_SECT_SIZE * blkcnt); + return blkcnt; +} + +static void fsl_sata_flush_cache(int dev) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d, *cfis = &h2d; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_FLUSH; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0); +} + +static u32 fsl_sata_rw_cmd_ext(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d, *cfis = &h2d; + u64 block; + + block = (u64)start; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + + cfis->command = (is_write) ? ATA_CMD_WRITE_EXT + : ATA_CMD_READ_EXT; + + cfis->lba_high_exp = (block >> 40) & 0xff; + cfis->lba_mid_exp = (block >> 32) & 0xff; + cfis->lba_low_exp = (block >> 24) & 0xff; + cfis->lba_high = (block >> 16) & 0xff; + cfis->lba_mid = (block >> 8) & 0xff; + cfis->lba_low = block & 0xff; + cfis->device = ATA_LBA; + cfis->sector_count_exp = (blkcnt >> 8) & 0xff; + cfis->sector_count = blkcnt & 0xff; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, ATA_SECT_SIZE * blkcnt); + return blkcnt; +} + +static u32 fsl_sata_rw_ncq_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, + int is_write) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d, *cfis = &h2d; + int ncq_channel; + u64 block; + + if (sata->lba48 != 1) { + printf("execute FPDMA command on non-LBA48 hard disk\n\r"); + return -1; + } + + block = (u64)start; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + + cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE + : ATA_CMD_FPDMA_READ; + + cfis->lba_high_exp = (block >> 40) & 0xff; + cfis->lba_mid_exp = (block >> 32) & 0xff; + cfis->lba_low_exp = (block >> 24) & 0xff; + cfis->lba_high = (block >> 16) & 0xff; + cfis->lba_mid = (block >> 8) & 0xff; + cfis->lba_low = block & 0xff; + + cfis->device = ATA_LBA; + cfis->features_exp = (blkcnt >> 8) & 0xff; + cfis->features = blkcnt & 0xff; + + if (sata->queue_depth >= SATA_HC_MAX_CMD) + ncq_channel = SATA_HC_MAX_CMD - 1; + else + ncq_channel = sata->queue_depth - 1; + + /* Use the latest queue */ + fsl_sata_exec_cmd(sata, cfis, CMD_NCQ, ncq_channel, buffer, ATA_SECT_SIZE * blkcnt); + return blkcnt; +} + +static void fsl_sata_flush_cache_ext(int dev) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + struct sata_fis_h2d h2d, *cfis = &h2d; + + memset(cfis, 0, sizeof(struct sata_fis_h2d)); + + cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D; + cfis->pm_port_c = 0x80; /* is command */ + cfis->command = ATA_CMD_FLUSH_EXT; + + fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0); +} + +static void fsl_sata_init_wcache(int dev, u16 *id) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + + if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id)) + sata->wcache = 1; + if (ata_id_has_flush(id)) + sata->flush = 1; + if (ata_id_has_flush_ext(id)) + sata->flush_ext = 1; +} + +static int fsl_sata_get_wcache(int dev) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + return sata->wcache; +} + +static int fsl_sata_get_flush(int dev) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + return sata->flush; +} + +static int fsl_sata_get_flush_ext(int dev) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + return sata->flush_ext; +} + +static u32 ata_low_level_rw_lba48(int dev, u32 blknr, lbaint_t blkcnt, + const void *buffer, int is_write) +{ + u32 start, blks; + u8 *addr; + int max_blks; + + start = blknr; + blks = blkcnt; + addr = (u8 *)buffer; + + max_blks = ATA_MAX_SECTORS_LBA48; + do { + if (blks > max_blks) { + if (fsl_sata_info[dev].flags != FLAGS_FPDMA) + fsl_sata_rw_cmd_ext(dev, start, max_blks, addr, is_write); + else + fsl_sata_rw_ncq_cmd(dev, start, max_blks, addr, is_write); + start += max_blks; + blks -= max_blks; + addr += ATA_SECT_SIZE * max_blks; + } else { + if (fsl_sata_info[dev].flags != FLAGS_FPDMA) + fsl_sata_rw_cmd_ext(dev, start, blks, addr, is_write); + else + fsl_sata_rw_ncq_cmd(dev, start, blks, addr, is_write); + start += blks; + blks = 0; + addr += ATA_SECT_SIZE * blks; + } + } while (blks != 0); + + return blkcnt; +} + +static u32 ata_low_level_rw_lba28(int dev, u32 blknr, u32 blkcnt, + const void *buffer, int is_write) +{ + u32 start, blks; + u8 *addr; + int max_blks; + + start = blknr; + blks = blkcnt; + addr = (u8 *)buffer; + + max_blks = ATA_MAX_SECTORS; + do { + if (blks > max_blks) { + fsl_sata_rw_cmd(dev, start, max_blks, addr, is_write); + start += max_blks; + blks -= max_blks; + addr += ATA_SECT_SIZE * max_blks; + } else { + fsl_sata_rw_cmd(dev, start, blks, addr, is_write); + start += blks; + blks = 0; + addr += ATA_SECT_SIZE * blks; + } + } while (blks != 0); + + return blkcnt; +} + +/* + * SATA interface between low level driver and command layer + */ +ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer) +{ + u32 rc; + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + + if (sata->lba48) + rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, buffer, READ_CMD); + else + rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, buffer, READ_CMD); + return rc; +} + +ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer) +{ + u32 rc; + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + + if (sata->lba48) { + rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, buffer, WRITE_CMD); + if (fsl_sata_get_wcache(dev) && fsl_sata_get_flush_ext(dev)) + fsl_sata_flush_cache_ext(dev); + } else { + rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, buffer, WRITE_CMD); + if (fsl_sata_get_wcache(dev) && fsl_sata_get_flush(dev)) + fsl_sata_flush_cache(dev); + } + return rc; +} + +int scan_sata(int dev) +{ + fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv; + unsigned char serial[ATA_ID_SERNO_LEN + 1]; + unsigned char firmware[ATA_ID_FW_REV_LEN + 1]; + unsigned char product[ATA_ID_PROD_LEN + 1]; + u16 *id; + u64 n_sectors; + + /* if no detected link */ + if (!sata->link) + return -1; + + id = (u16 *)malloc(ATA_ID_WORDS * 2); + if (!id) { + printf("id malloc failed\n\r"); + return -1; + } + + /* Identify device to get information */ + fsl_sata_identify(dev, id); + + /* Serial number */ + ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial)); + memcpy(sata_dev_desc[dev].product, serial, sizeof(serial)); + + /* Firmware version */ + ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware)); + memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware)); + + /* Product model */ + ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product)); + memcpy(sata_dev_desc[dev].vendor, product, sizeof(product)); + + /* Totoal sectors */ + n_sectors = ata_id_n_sectors(id); + sata_dev_desc[dev].lba = (u32)n_sectors; + +#ifdef CONFIG_LBA48 + /* Check if support LBA48 */ + if (ata_id_has_lba48(id)) { + sata->lba48 = 1; + debug("Device support LBA48\n\r"); + } else + debug("Device supports LBA28\n\r"); +#endif + + /* Get the NCQ queue depth from device */ + sata->queue_depth = ata_id_queue_depth(id); + + /* Get the xfer mode from device */ + fsl_sata_xfer_mode(dev, id); + + /* Get the write cache status from device */ + fsl_sata_init_wcache(dev, id); + + /* Set the xfer mode to highest speed */ + fsl_sata_set_features(dev); +#ifdef DEBUG + fsl_sata_identify(dev, id); + ata_dump_id(id); +#endif + free((void *)id); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/block/fsl_sata.h b/qemu/roms/u-boot/drivers/block/fsl_sata.h new file mode 100644 index 000000000..18d679e78 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/fsl_sata.h @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2007-2008 Freescale Semiconductor, Inc. + * Dave Liu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __FSL_SATA_H__ +#define __FSL_SATA_H__ + +#define SATA_HC_MAX_NUM 4 /* Max host controller numbers */ +#define SATA_HC_MAX_CMD 16 /* Max command queue depth per host controller */ +#define SATA_HC_MAX_PORT 16 /* Max port number per host controller */ + +/* +* SATA Host Controller Registers +*/ +typedef struct fsl_sata_reg { + /* SATA command registers */ + u32 cqr; /* Command queue register */ + u8 res1[0x4]; + u32 car; /* Command active register */ + u8 res2[0x4]; + u32 ccr; /* Command completed register */ + u8 res3[0x4]; + u32 cer; /* Command error register */ + u8 res4[0x4]; + u32 der; /* Device error register */ + u32 chba; /* Command header base address */ + u32 hstatus; /* Host status register */ + u32 hcontrol; /* Host control register */ + u32 cqpmp; /* Port number queue register */ + u32 sig; /* Signature register */ + u32 icc; /* Interrupt coalescing control register */ + u8 res5[0xc4]; + + /* SATA supperset registers */ + u32 sstatus; /* SATA interface status register */ + u32 serror; /* SATA interface error register */ + u32 scontrol; /* SATA interface control register */ + u32 snotification; /* SATA interface notification register */ + u8 res6[0x30]; + + /* SATA control status registers */ + u32 transcfg; /* Transport layer configuration */ + u32 transstatus; /* Transport layer status */ + u32 linkcfg; /* Link layer configuration */ + u32 linkcfg1; /* Link layer configuration1 */ + u32 linkcfg2; /* Link layer configuration2 */ + u32 linkstatus; /* Link layer status */ + u32 linkstatus1; /* Link layer status1 */ + u32 phyctrlcfg; /* PHY control configuration */ + u8 res7[0x2b0]; + + /* SATA system control registers */ + u32 syspr; /* System priority register - big endian */ + u8 res8[0xbec]; +} __attribute__ ((packed)) fsl_sata_reg_t; + +/* HStatus register +*/ +#define HSTATUS_ONOFF 0x80000000 /* Online/offline status */ +#define HSTATUS_FORCE_OFFLINE 0x40000000 /* In process going offline */ +#define HSTATUS_BIST_ERR 0x20000000 + +/* Fatal error */ +#define HSTATUS_MASTER_ERR 0x00004000 +#define HSTATUS_DATA_UNDERRUN 0x00002000 +#define HSTATUS_DATA_OVERRUN 0x00001000 +#define HSTATUS_CRC_ERR_TX 0x00000800 +#define HSTATUS_CRC_ERR_RX 0x00000400 +#define HSTATUS_FIFO_OVERFLOW_TX 0x00000200 +#define HSTATUS_FIFO_OVERFLOW_RX 0x00000100 +#define HSTATUS_FATAL_ERR_ALL (HSTATUS_MASTER_ERR | \ + HSTATUS_DATA_UNDERRUN | \ + HSTATUS_DATA_OVERRUN | \ + HSTATUS_CRC_ERR_TX | \ + HSTATUS_CRC_ERR_RX | \ + HSTATUS_FIFO_OVERFLOW_TX | \ + HSTATUS_FIFO_OVERFLOW_RX) +/* Interrupt status */ +#define HSTATUS_FATAL_ERR 0x00000020 +#define HSTATUS_PHY_RDY 0x00000010 +#define HSTATUS_SIGNATURE 0x00000008 +#define HSTATUS_SNOTIFY 0x00000004 +#define HSTATUS_DEVICE_ERR 0x00000002 +#define HSTATUS_CMD_COMPLETE 0x00000001 + +/* HControl register +*/ +#define HCONTROL_ONOFF 0x80000000 /* Online or offline request */ +#define HCONTROL_FORCE_OFFLINE 0x40000000 /* Force offline request */ +#define HCONTROL_ENTERPRISE_EN 0x10000000 /* Enterprise mode enabled */ +#define HCONTROL_HDR_SNOOP 0x00000400 /* Command header snoop */ +#define HCONTROL_PMP_ATTACHED 0x00000200 /* Port multiplier attached */ + +/* Interrupt enable */ +#define HCONTROL_FATAL_ERR 0x00000020 +#define HCONTROL_PHY_RDY 0x00000010 +#define HCONTROL_SIGNATURE 0x00000008 +#define HCONTROL_SNOTIFY 0x00000004 +#define HCONTROL_DEVICE_ERR 0x00000002 +#define HCONTROL_CMD_COMPLETE 0x00000001 + +#define HCONTROL_INT_EN_ALL (HCONTROL_FATAL_ERR | \ + HCONTROL_PHY_RDY | \ + HCONTROL_SIGNATURE | \ + HCONTROL_SNOTIFY | \ + HCONTROL_DEVICE_ERR | \ + HCONTROL_CMD_COMPLETE) + +/* SStatus register +*/ +#define SSTATUS_IPM_MASK 0x00000780 +#define SSTATUS_IPM_NOPRESENT 0x00000000 +#define SSTATUS_IPM_ACTIVE 0x00000080 +#define SSTATUS_IPM_PATIAL 0x00000100 +#define SSTATUS_IPM_SLUMBER 0x00000300 + +#define SSTATUS_SPD_MASK 0x000000f0 +#define SSTATUS_SPD_GEN1 0x00000010 +#define SSTATUS_SPD_GEN2 0x00000020 + +#define SSTATUS_DET_MASK 0x0000000f +#define SSTATUS_DET_NODEVICE 0x00000000 +#define SSTATUS_DET_DISCONNECT 0x00000001 +#define SSTATUS_DET_CONNECT 0x00000003 +#define SSTATUS_DET_PHY_OFFLINE 0x00000004 + +/* SControl register +*/ +#define SCONTROL_SPM_MASK 0x0000f000 +#define SCONTROL_SPM_GO_PARTIAL 0x00001000 +#define SCONTROL_SPM_GO_SLUMBER 0x00002000 +#define SCONTROL_SPM_GO_ACTIVE 0x00004000 + +#define SCONTROL_IPM_MASK 0x00000f00 +#define SCONTROL_IPM_NO_RESTRICT 0x00000000 +#define SCONTROL_IPM_PARTIAL 0x00000100 +#define SCONTROL_IPM_SLUMBER 0x00000200 +#define SCONTROL_IPM_PART_SLUM 0x00000300 + +#define SCONTROL_SPD_MASK 0x000000f0 +#define SCONTROL_SPD_NO_RESTRICT 0x00000000 +#define SCONTROL_SPD_GEN1 0x00000010 +#define SCONTROL_SPD_GEN2 0x00000020 + +#define SCONTROL_DET_MASK 0x0000000f +#define SCONTROL_DET_HRESET 0x00000001 +#define SCONTROL_DET_DISABLE 0x00000004 + +/* TransCfg register +*/ +#define TRANSCFG_DFIS_SIZE_SHIFT 16 +#define TRANSCFG_RX_WATER_MARK_MASK 0x0000001f + +/* PhyCtrlCfg register +*/ +#define PHYCTRLCFG_FPRFTI_MASK 0x00000018 +#define PHYCTRLCFG_LOOPBACK_MASK 0x0000000e + +/* +* Command Header Entry +*/ +typedef struct cmd_hdr_entry { + __le32 cda; /* Command Descriptor Address, + 4 bytes aligned */ + __le32 prde_fis_len; /* Number of PRD entries and FIS length */ + __le32 ttl; /* Total transfer length */ + __le32 attribute; /* the attribute of command */ +} __attribute__ ((packed)) cmd_hdr_entry_t; + +#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry) + +/* cda +*/ +#define CMD_HDR_CDA_ALIGN 4 + +/* prde_fis_len +*/ +#define CMD_HDR_PRD_ENTRY_SHIFT 16 +#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000 +#define CMD_HDR_FIS_LEN_SHIFT 2 + +/* attribute +*/ +#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */ +#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */ +#define CMD_HDR_ATTR_SNOOP 0x00000200 /* Snoop enable for all descriptor */ +#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */ +#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */ +#define CMD_HDR_ATTR_BIST 0x00000040 /* BIST - require the host to enter BIST mode */ +#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */ +#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */ + +/* command type +*/ +enum cmd_type { + CMD_VENDOR_BIST, + CMD_BIST, + CMD_RESET, /* SRST or device reset */ + CMD_ATAPI, + CMD_NCQ, + CMD_ATA, /* None of all above */ +}; + +/* +* Command Header Table +*/ +typedef struct cmd_hdr_tbl { + cmd_hdr_entry_t cmd_slot[SATA_HC_MAX_CMD]; +} __attribute__ ((packed)) cmd_hdr_tbl_t; + +#define SATA_HC_CMD_HDR_TBL_SIZE sizeof(struct cmd_hdr_tbl) +#define SATA_HC_CMD_HDR_TBL_ALIGN 4 + +/* +* PRD entry - Physical Region Descriptor entry +*/ +typedef struct prd_entry { + __le32 dba; /* Data base address, 4 bytes aligned */ + u32 res1; + u32 res2; + __le32 ext_c_ddc; /* Indirect PRD flags, snoop and data word count */ +} __attribute__ ((packed)) prd_entry_t; + +#define SATA_HC_CMD_DESC_PRD_SIZE sizeof(struct prd_entry) + +/* dba +*/ +#define PRD_ENTRY_DBA_ALIGN 4 + +/* ext_c_ddc +*/ +#define PRD_ENTRY_EXT 0x80000000 /* extension flag */ +#ifdef CONFIG_FSL_SATA_V2 +#define PRD_ENTRY_DATA_SNOOP 0x10000000 /* Data snoop enable */ +#else +#define PRD_ENTRY_DATA_SNOOP 0x00400000 /* Data snoop enable */ +#endif +#define PRD_ENTRY_LEN_MASK 0x003fffff /* Data word count */ + +#define PRD_ENTRY_MAX_XFER_SZ (PRD_ENTRY_LEN_MASK + 1) + +/* + * This SATA host controller supports a max of 16 direct PRD entries, but if use + * chained indirect PRD entries, then the contollers supports upto a max of 63 + * entries including direct and indirect PRD entries. + * The PRDT is an array of 63 PRD entries contigiously, but the PRD entries#15 + * will be setup as an indirect descriptor, pointing to it's next (contigious) + * PRD entries#16. + */ +#define SATA_HC_MAX_PRD 63 /* Max PRD entry numbers per command */ +#define SATA_HC_MAX_PRD_DIRECT 16 /* Direct PRDT entries */ +#define SATA_HC_MAX_PRD_USABLE (SATA_HC_MAX_PRD - 1) +#define SATA_HC_MAX_XFER_LEN 0x4000000 + +/* +* PRDT - Physical Region Descriptor Table +*/ +typedef struct prdt { + prd_entry_t prdt[SATA_HC_MAX_PRD]; +} __attribute__ ((packed)) prdt_t; + +/* +* Command Descriptor +*/ +#define SATA_HC_CMD_DESC_CFIS_SIZE 32 /* bytes */ +#define SATA_HC_CMD_DESC_SFIS_SIZE 32 /* bytes */ +#define SATA_HC_CMD_DESC_ACMD_SIZE 16 /* bytes */ +#define SATA_HC_CMD_DESC_RES 16 /* bytes */ + +typedef struct cmd_desc { + u8 cfis[SATA_HC_CMD_DESC_CFIS_SIZE]; + u8 sfis[SATA_HC_CMD_DESC_SFIS_SIZE]; + u8 acmd[SATA_HC_CMD_DESC_ACMD_SIZE]; + u8 res[SATA_HC_CMD_DESC_RES]; + prd_entry_t prdt[SATA_HC_MAX_PRD]; +} __attribute__ ((packed)) cmd_desc_t; + +#define SATA_HC_CMD_DESC_SIZE sizeof(struct cmd_desc) +#define SATA_HC_CMD_DESC_ALIGN 4 + +/* + * SATA device driver info + */ +typedef struct fsl_sata_info { + u32 sata_reg_base; + u32 flags; +} fsl_sata_info_t; + +#define FLAGS_DMA 0x00000000 +#define FLAGS_FPDMA 0x00000001 + +/* + * SATA device driver struct + */ +typedef struct fsl_sata { + char name[12]; + fsl_sata_reg_t *reg_base; /* the base address of controller register */ + void *cmd_hdr_tbl_offset; /* alloc address of command header table */ + cmd_hdr_tbl_t *cmd_hdr; /* aligned address of command header table */ + void *cmd_desc_offset; /* alloc address of command descriptor */ + cmd_desc_t *cmd_desc; /* aligned address of command descriptor */ + int link; /* PHY link status */ + /* device attribute */ + int ata_device_type; /* device type */ + int lba48; + int queue_depth; /* Max NCQ queue depth */ + u16 pio; + u16 mwdma; + u16 udma; + int wcache; + int flush; + int flush_ext; +} fsl_sata_t; + +#define READ_CMD 0 +#define WRITE_CMD 1 + +#endif /* __FSL_SATA_H__ */ diff --git a/qemu/roms/u-boot/drivers/block/ftide020.c b/qemu/roms/u-boot/drivers/block/ftide020.c new file mode 100644 index 000000000..1f6995ec2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/ftide020.c @@ -0,0 +1,347 @@ +/* + * Faraday FTIDE020 ATA Controller (AHB) + * + * (C) Copyright 2011 Andes Technology + * Greentime Hu + * Macpaul Lin + * Kuo-Wei Chou + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* ftide020.c - ide support functions for the FTIDE020_S controller */ + +#include +#include +#include +#include +#include +#include + +#include "ftide020.h" + +/* base address */ +#define FTIDE_BASE CONFIG_SYS_ATA_BASE_ADDR + +/* + * data address - The CMD and DATA use the same FIFO in FTIDE020_S + * FTIDE_DATA = CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_DATA_OFFSET + * = &ftide020->rw_fifo + */ +#define FTIDE_DATA (&ftide020->rw_fifo) + +/* command and data I/O macros */ +/* 0x0 - DATA FIFO */ +#define WRITE_DATA(x) outl((x), &ftide020->rw_fifo) /* 0x00 */ +#define READ_DATA() inl(&ftide020->rw_fifo) /* 0x00 */ +/* 0x04 - R: Status Reg, W: CMD_FIFO */ +#define WRITE_CMD(x) outl((x), &ftide020->cmd_fifo) /* 0x04 */ +#define READ_STATUS() inl(&ftide020->cmd_fifo) /* 0x04 */ + +void ftide_set_device(int cx8, int dev) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + + WRITE_CMD(SET_DEV_CMD | IDE_SET_CX8(cx8) | dev); +} + +unsigned char ide_read_register(int dev, unsigned int port) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + + ftide_set_device(0, dev); + WRITE_CMD(READ_REG_CMD | IDE_REG_CS_READ(CONFIG_IDE_REG_CS) | + IDE_REG_DA_WRITE(port)); + + return READ_DATA() & 0xff; +} + +void ide_write_register(int dev, unsigned int port, unsigned char val) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + + ftide_set_device(0, dev); + WRITE_CMD(WRITE_REG_CMD | IDE_REG_CS_WRITE(CONFIG_IDE_REG_CS) | + IDE_REG_DA_WRITE(port) | val); +} + +void ide_write_data(int dev, const ulong *sect_buf, int words) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + + ftide_set_device(0, dev); + WRITE_CMD(WRITE_DATA_CMD | ((words << 2) - 1)); + + /* block write */ + outsl(FTIDE_DATA, sect_buf, words); +} + +void ide_read_data(int dev, ulong *sect_buf, int words) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + + ftide_set_device(0, dev); + WRITE_CMD(READ_DATA_CMD | ((words << 2) - 1)); + + /* block read */ + insl(FTIDE_DATA, sect_buf, words); +} + +void ftide_dfifo_ready(ulong *time) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + + while (!(READ_STATUS() & STATUS_RFE)) { + if (*time-- == 0) + break; + + udelay(100); + } +} + +extern ulong ide_bus_offset[CONFIG_SYS_IDE_MAXBUS]; + +/* Reset_IDE_controller */ +static void reset_ide_controller(void) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + unsigned int val; + + val = inl(&ftide020->cr); + + val |= CONTROL_RST; + outl(val, &ftide020->cr); + + /* wait until reset OK, this is poor HW design */ + mdelay(50); + val &= ~(CONTROL_RST); + outl(val, &ftide020->cr); + + mdelay(50); + val |= CONTROL_SRST; + outl(val, &ftide020->cr); + + /* wait until reset OK, this is poor HW design */ + mdelay(50); + val &= ~(CONTROL_SRST); + outl(val, &ftide020->cr); + + /* IORDY enable for PIO, for 2 device */ + val |= (CONTROL_IRE0 | CONTROL_IRE1); + outl(val, &ftide020->cr); +} + +/* IDE clock frequence */ +uint ftide_clock_freq(void) +{ + /* + * todo: To aquire dynamic system frequency is dependend on the power + * management unit which the ftide020 is connected to. In current, + * there are only few PMU supports in u-boot. + * So this function is wait for future enhancement. + */ + return 100; +} + +/* Calculate Timing Registers */ +static unsigned int timing_cal(u16 t0, u16 t1, u16 t2, u16 t4) +{ + unsigned int val, ahb_ns = 8; + u8 TEOC, T1, T2, T4; + + T1 = (u8) (t1 / ahb_ns); + if ((T1 * ahb_ns) == t1) + T1--; + + T2 = (u8) (t2 / ahb_ns); + if ((T2 * ahb_ns) == t2) + T2--; + + T4 = (u8) (t4 / ahb_ns); + if ((T4 * ahb_ns) == t4) + T4--; + + TEOC = (u8) (t0 / ahb_ns); + if ((TEOC * ahb_ns) == t0) + TEOC--; + + TEOC = ((TEOC > (T1 + T2 + T4)) ? (TEOC - (T1 + T2 + T4)) : 0); + + /* + * Here the fields in data timing registers in PIO mode + * is accessed the same way as command timing registers. + */ + val = DT_REG_PIO_T1(T1) | + DT_REG_PIO_T2(T2) | + DT_REG_PIO_T4(T4) | + DT_REG_PIO_TEOC(TEOC); + + return val; +} + +/* Set Timing Register */ +static unsigned int set_mode_timing(u8 dev, u8 id, u8 mode) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + u16 t0, t1, t2, t4; + u8 tcyc, tcvs, tmli, tenv, tack, trp; + unsigned int val, sysclk = 8; + + if (id >= TATOL_TIMING) + return 0; + + sysclk = ftide_clock_freq(); + switch (id) { + case CMD_TIMING: + if (mode < REG_MODE) { + t0 = REG_ACCESS_TIMING[REG_T0][mode]; + t1 = REG_ACCESS_TIMING[REG_T1][mode]; + t2 = REG_ACCESS_TIMING[REG_T2][mode]; + t4 = REG_ACCESS_TIMING[REG_T4][mode]; + + val = timing_cal(t0, t1, t2, t4); + outl(val, (dev ? &ftide020->ctrd1 : &ftide020->ctrd0)); + return 1; + } else + return 0; + case PIO_TIMING: + if (mode < PIO_MODE) { + t0 = PIO_ACCESS_TIMING[PIO_T0][mode]; + t1 = PIO_ACCESS_TIMING[PIO_T1][mode]; + t2 = PIO_ACCESS_TIMING[PIO_T2][mode]; + t4 = PIO_ACCESS_TIMING[PIO_T4][mode]; + + val = timing_cal(t0, t1, t2, t4); + + outl(val, (dev ? &ftide020->dtrd1 : &ftide020->dtrd0)); + return 1; + } else + return 0; + case DMA_TIMING: + if (mode < UDMA_MODE) { + /* + * 0.999 is ceiling + * for tcyc, tcvs, tmli, tenv, trp, tack + */ + tcyc = (u8) (((UDMA_ACCESS_TIMING[UDMA_TCYC][mode] \ + * sysclk) + 9990) / 10000); + tcvs = (u8) (((UDMA_ACCESS_TIMING[UDMA_TCVS][mode] \ + * sysclk) + 9990) / 10000); + tmli = (u8) (((UDMA_ACCESS_TIMING[UDMA_TMLI][mode] \ + * sysclk) + 9990) / 10000); + tenv = (u8) (((UDMA_ACCESS_TIMING[UDMA_TENV][mode] \ + * sysclk) + 9990) / 10000); + trp = (u8) (((UDMA_ACCESS_TIMING[UDMA_TRP][mode] \ + * sysclk) + 9990) / 10000); + tack = (u8) (((UDMA_ACCESS_TIMING[UDMA_TACK][mode] \ + * sysclk) + 9990) / 10000); + + val = DT_REG_UDMA_TENV((tenv > 0) ? (tenv - 1) : 0) | + DT_REG_UDMA_TMLI((tmli > 0) ? (tmli - 1) : 0) | + DT_REG_UDMA_TCYC((tcyc > 0) ? (tcyc - 1) : 0) | + DT_REG_UDMA_TACK((tack > 0) ? (tack - 1) : 0) | + DT_REG_UDMA_TCVS((tcvs > 0) ? (tcvs - 1) : 0) | + DT_REG_UDMA_TRP((trp > 0) ? (trp - 1) : 0); + + outl(val, (dev ? &ftide020->dtrd1 : &ftide020->dtrd0)); + return 1; + } else + return 0; + default: + return 0; + } +} + +static void ftide_read_hwrev(void) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + unsigned int rev; + + rev = inl(&ftide020->revision); +} + +static int ftide_controller_probe(void) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + unsigned int bak; + + bak = inl(&ftide020->ctrd1); + + /* probing by using shorter setup time */ + outl(CONFIG_CTRD1_PROBE_T1, &ftide020->ctrd1); + if ((inl(&ftide020->ctrd1) & 0xff) != CONFIG_CTRD1_PROBE_T1) { + outl(bak, &ftide020->ctrd1); + return 0; + } + + /* probing by using longer setup time */ + outl(CONFIG_CTRD1_PROBE_T2, &ftide020->ctrd1); + if ((inl(&ftide020->ctrd1) & 0xff) != CONFIG_CTRD1_PROBE_T2) { + outl(bak, &ftide020->ctrd1); + return 0; + } + + outl(bak, &ftide020->ctrd1); + + return 1; +} + +/* ide_preinit() was migrated from linux driver ide_probe_for_ftide() */ +int ide_preinit(void) +{ + static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE; + int status; + unsigned int val; + int i; + + status = 1; + for (i = 0; i < CONFIG_SYS_IDE_MAXBUS; i++) + ide_bus_offset[i] = -ATA_STATUS; + + /* auto-detect IDE controller */ + if (ftide_controller_probe()) { + printf("FTIDE020_S\n"); + } else { + printf("FTIDE020_S ATA controller not found.\n"); + return API_ENODEV; + } + + /* check HW IP revision */ + ftide_read_hwrev(); + + /* set FIFO threshold */ + outl(((WRITE_FIFO - RX_THRESH) << 16) | RX_THRESH, &ftide020->dmatirr); + + /* set Device_0 PIO_4 timing */ + set_mode_timing(0, CMD_TIMING, REG_MODE4); + set_mode_timing(0, PIO_TIMING, PIO_MODE4); + + /* set Device_1 PIO_4 timing */ + set_mode_timing(1, CMD_TIMING, REG_MODE4); + set_mode_timing(1, PIO_TIMING, PIO_MODE4); + + /* from E-bios */ + /* little endian */ + outl(0x0, &ftide020->cr); + mdelay(10); + + outl(0x0fff0fff, &ftide020->ahbtr); + mdelay(10); + + /* Enable controller Interrupt */ + val = inl(&ftide020->cr); + + /* Enable: IDE IRQ, IDE Terminate ERROR IRQ, AHB Timeout error IRQ */ + val |= (CONTROL_IIE | CONTROL_TERIE | CONTROL_AERIE); + outl(val, &ftide020->cr); + + status = 0; + + return status; +} + +void ide_set_reset(int flag) +{ + debug("ide_set_reset()\n"); + reset_ide_controller(); + return; +} diff --git a/qemu/roms/u-boot/drivers/block/ftide020.h b/qemu/roms/u-boot/drivers/block/ftide020.h new file mode 100644 index 000000000..2d88c7ce7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/ftide020.h @@ -0,0 +1,266 @@ +/* + * Faraday FTIDE020_s ATA Controller (AHB) + * + * (C) Copyright 2011 Andes Technology + * Greentime Hu + * Macpaul Lin + * Kuo-Wei Chou + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __FTIDE020_H +#define __FTIDE020_H + +/* ftide020.h - ide support functions for the FTIDE020_S controller */ + +/* ATA controller register offset */ +struct ftide020_s { + unsigned int rw_fifo; /* 0x00 - READ/WRITE FIFO */ + unsigned int cmd_fifo; /* 0x04 - R: Status Reg, W: CMD_FIFO */ + unsigned int cr; /* 0x08 - Control Reg */ + unsigned int dmatirr; /* 0x0c - DMA Threshold/Interrupt Reg */ + unsigned int ctrd0; /* 0x10 - Command Timing Reg Device 0 */ + unsigned int dtrd0; /* 0x14 - Data Timing Reg Device 0 */ + unsigned int ctrd1; /* 0x18 - Command Timing Reg Device 1 */ + unsigned int dtrd1; /* 0x1c - Data Timing Reg Device 1 */ + unsigned int ahbtr; /* 0x20 - AHB Timeout Reg */ + unsigned int RESVD0; /* 0x24 */ + unsigned int RESVD1; /* 0x28 */ + unsigned int RESVD2; /* 0x2c */ + unsigned int f_cfifo; /* 0x30 - Feature Info of CMD_FIFO */ + unsigned int f_wfifo; /* 0x34 - Feature Info of WRITE_FIFO */ + unsigned int f_rfifo; /* 0x3c - Feature Info of READ_FIFO */ + unsigned int revision; /* 0x38 - Revision No. of FTIDE020_S */ +}; + +/* reference parameters */ +#define CONFIG_IDE_REG_CS 0x2 /* ref: ATA spec chaper 10, table 42 */ +#define CONFIG_CTRD1_PROBE_T1 0x2 +#define CONFIG_CTRD1_PROBE_T2 0x5 + +/* status register - 0x04 */ +#define STATUS_CSEL (1 << 0) /* CSEL */ +#define STATUS_CS(x) (((x) >> 1) & 0x3) /* CS#[1:0] */ +#define STATUS_DMACK (1 << 3) /* DMACK# */ +#define STATUS_DMARQ (1 << 4) /* DMA req */ +#define STATUS_INTRQ (1 << 5) /* INT req */ +#define STATUS_DIOR (1 << 6) /* DIOR */ +#define STATUS_IORDY (1 << 7) /* I/O ready */ +#define STATUS_DIOW (1 << 8) /* DIOW# */ +#define STATUS_PDIAG (1 << 9) /* PDIAG */ +#define STATUS_DASP (1 << 10) /* DASP# */ +#define STATUS_DEV (1 << 11) /* selected device */ +#define STATUS_PIO (1 << 12) /* PIO in progress */ +#define STATUS_DMA (1 << 13) /* DMA in progress */ +#define STATUS_WFE (1 << 14) /* write fifo full */ +#define STATUS_RFE (1 << 15) /* read fifo empty */ +#define STATUS_COUNTER(x) (((x) >> 16) & 0x3fff) /* data tx counter */ +#define STATUS_ERR (1 << 30) /* trasfer terminated */ +#define STATUS_AER (1 << 31) /* AHB timeout indicate */ + +/* Control register - 0x08 */ +#define CONTROL_TYPE_PIO 0x0 +#define CONTROL_TYPE_UDMA 0x1 + +/* Device 0 */ +#define CONTROL_TYP0(x) (((x) & 0x7) << 0) +#define CONTROL_IRE0 (1 << 3) /* enable IORDY for PIO */ +#define CONTROL_RESVD_DW0 (1 << 4) /* Reserved - DW0 ? */ +#define CONTROL_E0 (1 << 5) /* E0: 1: Big Endian */ +#define CONTROL_RESVD_WP0 (1 << 6) /* Reserved - WP0 ? */ +#define CONTROL_RESVD_SE0 (1 << 7) /* Reserved - SE0 ? */ +#define CONTROL_RESVD_ECC0 (1 << 8) /* Reserved - ECC0 ? */ + +#define CONTROL_RAEIE (1 << 9) /* IRQ - read fifo almost full */ +#define CONTROL_RNEIE (1 << 10) /* IRQ - read fifo not empty */ +#define CONTROL_WAFIE (1 << 11) /* IRQ - write fifo almost empty */ +#define CONTROL_WNFIE (1 << 12) /* IRQ - write fifo not full */ +#define CONTROL_RESVD_FIRQ (1 << 13) /* RESERVED - FIRQ ? */ +#define CONTROL_AERIE (1 << 14) /* IRQ - AHB timeout error */ +#define CONTROL_IIE (1 << 15) /* IDE IRQ enable */ + +/* Device 1 */ +#define CONTROL_TYP1(x) (((x) & 0x7) << 16) +#define CONTROL_IRE1 (1 << 19) /* enable IORDY for PIO */ +#define CONTROL_RESVD_DW1 (1 << 20) /* Reserved - DW1 ? */ +#define CONTROL_E1 (1 << 21) /* E1: 1: Big Endian */ +#define CONTROL_RESVD_WP1 (1 << 22) /* Reserved - WP1 ? */ +#define CONTROL_RESVD_SE1 (1 << 23) /* Reserved - SE1 ? */ +#define CONTROL_RESVD_ECC1 (1 << 24) /* Reserved - ECC1 ? */ + +#define CONTROL_DRE (1 << 25) /* DMA receive enable */ +#define CONTROL_DTE (1 << 26) /* DMA transmit enable */ +#define CONTRIL_RESVD (1 << 27) +#define CONTROL_TERIE (1 << 28) /* transfer terminate error IRQ */ +#define CONTROL_T (1 << 29) /* terminate current operation */ +#define CONTROL_SRST (1 << 30) /* IDE soft reset */ +#define CONTROL_RST (1 << 31) /* IDE hardware reset */ + +/* IRQ register - 0x0c */ +#define IRQ_RXTHRESH(x) (((x) & 0x3ff) << 0) /* Read FIFO threshold */ +#define IRQ_RFAEIRQ (1 << 10) /* Read FIFO almost full intr req */ +#define IRQ_RFNEIRQ (1 << 11) /* Read FIFO not empty intr req */ +#define IRQ_WFAFIRQ (1 << 12) /* Write FIFO almost empty int req */ +#define IRQ_WFNFIRQ (1 << 13) /* Write FIFO not full intr req */ +#define IRQ_RESVD_FIRQ (1 << 14) /* Reserved - FIRQ ? */ +#define IRQ_IIRQ (1 << 15) /* IDE device interrupt request */ +#define IRQ_TXTHRESH(x) (((x) & 0x3ff) << 16) /* Write FIFO thershold */ +#define IRQ_TERMERR (1 << 28) /* Transfer termination indication */ +#define IRQ_AHBERR (1 << 29) /* AHB Timeout indication */ + +/* Command Timing Register 0-1: ctrd (0x10, 0x18) */ +#define CT_REG_T1(x) (((x) & 0xff) << 0) /* setup time of addressed */ +#define CT_REG_T2(x) (((x) & 0xff) << 8) /* pluse width of DIOR/DIOW */ +#define CT_REG_T4(x) (((x) & 0xff) << 16) /* data hold time */ +#define CT_REG_TEOC(x) (((x) & 0xff) << 24) /* time to the end of a cycle */ + +/* Data Timing Register 0-1: dtrd (0x14, 0x1c) */ +/* + * PIO mode: + * b(0:7) DT_REG_PIO_T1: the setup time of addressed + * b(8:15) DT_REG_PIO_T2: the pluse width of DIOR/DIOW + * b(16:23) DT_REG_PIO_T4: data hold time + * b(24:31) DT_REG_PIO_TEOC: the time to the end of a cycle + */ +#define DT_REG_PIO_T1(x) (((x) & 0xff) << 0) +#define DT_REG_PIO_T2(x) (((x) & 0xff) << 8) +#define DT_REG_PIO_T4(x) (((x) & 0xff) << 16) +#define DT_REG_PIO_TEOC(x) (((x) & 0xff) << 24) + +/* + * UDMA mode: + * b(0:3) DT_REG_UDMA_TENV: the envelope time + * b(4:7) DT_REG_UDMA_TMLI: interlock time + * b(8:15) DT_REG_UDMA_TCYC: cycle time - data time + * b(16:19) DT_REG_UDMA_TACK: setup and hold time of DMACK + * b(23:30) DT_REG_UDMA_TCVS: setup time of CRC + * b(24:31) DT_REG_UDMA_TRP: time to ready to pause + */ +#define DT_REG_UDMA_TENV(x) (((x) & 0xf) << 0) +#define DT_REG_UDMA_TMLI(x) (((x) & 0xf) << 4) +#define DT_REG_UDMA_TCYC(x) (((x) & 0xff) << 8) +#define DT_REG_UDMA_TACK(x) (((x) & 0xf) << 16) +#define DT_REG_UDMA_TCVS(x) (((x) & 0xf) << 20) +#define DT_REG_UDMA_TRP(x) (((x) & 0xff) << 24) + +/* ftide020_s command formats */ +/* read: IDE Register (CF1) */ +#define IDE_REG_OPCODE_READ (1 << 13) /* 0x2000 */ +#define IDE_REG_CS_READ(x) (((x) & 0x3) << 11) +#define IDE_REG_DA_READ(x) (((x) & 0x7) << 8) +#define IDE_REG_CMD_READ(x) 0x0 /* fixed value */ + +/* write: IDE Register (CF2) */ +#define IDE_REG_OPCODE_WRITE (0x5 << 13) /* 0xA000 */ +#define IDE_REG_CS_WRITE(x) (((x) & 0x3) << 11) +#define IDE_REG_DA_WRITE(x) (((x) & 0x7) << 8) +/* b(0:7) IDE_REG_CMD_WRITE(x): Actual ATA command or data */ +#define IDE_REG_CMD_WRITE(x) (((x) & 0xff) << 0) + +/* read/write data: PIO/UDMA (CF3) */ +#define IDE_DATA_WRITE (1 << 15) /* read: 0, write: 1 */ +#define IDE_DATA_OPCODE (0x2 << 13) /* device data access opcode */ +/* b(0:12) IDE_DATA_COUNTER(x): Number of transfers minus 1 */ +#define IDE_DATA_COUNTER(x) (((x) & 0x1fff) << 0) + +/* set device: (CF4) */ +#define IDE_SET_OPCODE (0x2740 << 2) /* [15:2], 0x9d00 */ +/* CF3 counter value: 0: Tx in bytes, 1: in blocks (each block is 8 bytes) */ +#define IDE_SET_CX8(x) (((x) & 0x1) << 1) +#define IDE_SET_DEV(x) (((x) & 0x1) << 0) /* 0: Master, 1: Slave */ + +/* + * IDE command bit definition + * This section is designed for minor hardware revision compatibility. + */ +#define READ_REG_CMD IDE_REG_OPCODE_READ /* 0x2000 */ +#define WRITE_REG_CMD IDE_REG_OPCODE_WRITE /* 0xA000 */ +#define READ_DATA_CMD IDE_DATA_OPCODE /* 0x4000 */ +#define WRITE_DATA_CMD (IDE_DATA_OPCODE | IDE_DATA_WRITE) /* 0xC000 */ +#define SET_DEV_CMD IDE_SET_OPCODE /* 0x9D00 */ + +#define TATOL_TIMING 3 +#define CMD_TIMING 0 +#define PIO_TIMING 1 +#define DMA_TIMING 2 + +/* Timing Parameters */ +/* Register Access Timing Parameters */ +#define REG_PARAMETER 4 +#define REG_T0 0 +#define REG_T1 1 +#define REG_T2 2 +#define REG_T4 3 + +#define REG_MODE 5 +#define REG_MODE0 0 +#define REG_MODE1 1 +#define REG_MODE2 2 +#define REG_MODE3 3 +#define REG_MODE4 4 + +/* PIO Access Timing Parameters */ +#define PIO_PARAMETER 4 +#define PIO_T0 0 +#define PIO_T1 1 +#define PIO_T2 2 +#define PIO_T4 3 + +#define PIO_MODE 5 +#define PIO_MODE0 0 +#define PIO_MODE1 1 +#define PIO_MODE2 2 +#define PIO_MODE3 3 +#define PIO_MODE4 4 + +/* UDMA Access Timing Parameters */ +#define UDMA_PARAMETER 6 +#define UDMA_TCYC 0 +#define UDMA_TCVS 1 +#define UDMA_TMLI 2 +#define UDMA_TENV 3 +#define UDMA_TRP 4 +#define UDMA_TACK 5 + +#define UDMA_MODE 7 +#define UDMA_MODE0 0 +#define UDMA_MODE1 1 +#define UDMA_MODE2 2 +#define UDMA_MODE3 3 +#define UDMA_MODE4 4 +#define UDMA_MODE5 5 +#define UDMA_MODE6 6 + +/* + * RX_THRESH: + * hardware limitation: max = 8, should support 1,4,8,16,32,64,128,256 + */ +#define RX_THRESH 8 +#define WRITE_FIFO 32 /* Hardwired value */ + +/* Time Table */ +unsigned int REG_ACCESS_TIMING[REG_PARAMETER][REG_MODE] = { + {600, 383, 330, 180, 120}, + {70, 50, 30, 30, 25}, + {290, 290, 290, 80, 70}, + {30, 20, 15, 10, 10}, +}; + +unsigned int PIO_ACCESS_TIMING[PIO_PARAMETER][PIO_MODE] = { + {600, 383, 240, 180, 120}, + {70, 50, 30, 30, 25}, + {165, 125, 100, 80, 70}, + {30, 20, 15, 10, 10}, +}; + +unsigned int UDMA_ACCESS_TIMING[UDMA_PARAMETER][UDMA_MODE] = { + {1120, 730, 540, 390, 250, 168, 130}, /* 10X */ + {700, 480, 310, 200, 67, 100, 100}, /* 10X */ + {200, 200, 200, 200, 200, 200, 200}, /* 10X */ + {200, 200, 200, 200, 200, 200, 200}, /* 10X */ + {1600, 1250, 1000, 1000, 1000, 850, 850}, /* 10X */ + {200, 200, 200, 200, 200, 200, 200}, /* 10X */ +}; + +#endif /* __FTIDE020_H */ diff --git a/qemu/roms/u-boot/drivers/block/libata.c b/qemu/roms/u-boot/drivers/block/libata.c new file mode 100644 index 000000000..d684270dc --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/libata.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008 Freescale Semiconductor, Inc. + * Dave Liu + * port from the libata of linux kernel + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +u64 ata_id_n_sectors(u16 *id) +{ + if (ata_id_has_lba(id)) { + if (ata_id_has_lba48(id)) + return ata_id_u64(id, ATA_ID_LBA48_SECTORS); + else + return ata_id_u32(id, ATA_ID_LBA_SECTORS); + } else { + return 0; + } +} + +u32 ata_dev_classify(u32 sig) +{ + u8 lbam, lbah; + + lbam = (sig >> 16) & 0xff; + lbah = (sig >> 24) & 0xff; + + if (((lbam == 0) && (lbah == 0)) || + ((lbam == 0x3c) && (lbah == 0xc3))) + return ATA_DEV_ATA; + + if ((lbam == 0x14) && (lbah == 0xeb)) + return ATA_DEV_ATAPI; + + if ((lbam == 0x69) && (lbah == 0x96)) + return ATA_DEV_PMP; + + return ATA_DEV_UNKNOWN; +} + +static void ata_id_string(const u16 *id, unsigned char *s, + unsigned int ofs, unsigned int len) +{ + unsigned int c; + + while (len > 0) { + c = id[ofs] >> 8; + *s = c; + s++; + + c = id[ofs] & 0xff; + *s = c; + s++; + + ofs++; + len -= 2; + } +} + +void ata_id_c_string(const u16 *id, unsigned char *s, + unsigned int ofs, unsigned int len) +{ + unsigned char *p; + + ata_id_string(id, s, ofs, len - 1); + + p = s + strnlen((char *)s, len - 1); + while (p > s && p[-1] == ' ') + p--; + *p = '\0'; +} + +void ata_dump_id(u16 *id) +{ + unsigned char serial[ATA_ID_SERNO_LEN + 1]; + unsigned char firmware[ATA_ID_FW_REV_LEN + 1]; + unsigned char product[ATA_ID_PROD_LEN + 1]; + u64 n_sectors; + + /* Serial number */ + ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial)); + printf("S/N: %s\n\r", serial); + + /* Firmware version */ + ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware)); + printf("Firmware version: %s\n\r", firmware); + + /* Product model */ + ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product)); + printf("Product model number: %s\n\r", product); + + /* Total sectors of device */ + n_sectors = ata_id_n_sectors(id); + printf("Capablity: %lld sectors\n\r", n_sectors); + + printf ("id[49]: capabilities = 0x%04x\n" + "id[53]: field valid = 0x%04x\n" + "id[63]: mwdma = 0x%04x\n" + "id[64]: pio = 0x%04x\n" + "id[75]: queue depth = 0x%04x\n", + id[49], + id[53], + id[63], + id[64], + id[75]); + + printf ("id[76]: sata capablity = 0x%04x\n" + "id[78]: sata features supported = 0x%04x\n" + "id[79]: sata features enable = 0x%04x\n", + id[76], + id[78], + id[79]); + + printf ("id[80]: major version = 0x%04x\n" + "id[81]: minor version = 0x%04x\n" + "id[82]: command set supported 1 = 0x%04x\n" + "id[83]: command set supported 2 = 0x%04x\n" + "id[84]: command set extension = 0x%04x\n", + id[80], + id[81], + id[82], + id[83], + id[84]); + printf ("id[85]: command set enable 1 = 0x%04x\n" + "id[86]: command set enable 2 = 0x%04x\n" + "id[87]: command set default = 0x%04x\n" + "id[88]: udma = 0x%04x\n" + "id[93]: hardware reset result = 0x%04x\n", + id[85], + id[86], + id[87], + id[88], + id[93]); +} + +void ata_swap_buf_le16(u16 *buf, unsigned int buf_words) +{ + unsigned int i; + + for (i = 0; i < buf_words; i++) + buf[i] = le16_to_cpu(buf[i]); +} diff --git a/qemu/roms/u-boot/drivers/block/mvsata_ide.c b/qemu/roms/u-boot/drivers/block/mvsata_ide.c new file mode 100644 index 000000000..574bc40b1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/mvsata_ide.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2010 Albert ARIBAUD + * + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#if defined(CONFIG_ORION5X) +#include +#elif defined(CONFIG_KIRKWOOD) +#include +#endif + +/* SATA port registers */ +struct mvsata_port_registers { + u32 reserved0[10]; + u32 edma_cmd; + u32 reserved1[181]; + /* offset 0x300 : ATA Interface registers */ + u32 sstatus; + u32 serror; + u32 scontrol; + u32 ltmode; + u32 phymode3; + u32 phymode4; + u32 reserved2[5]; + u32 phymode1; + u32 phymode2; + u32 bist_cr; + u32 bist_dw1; + u32 bist_dw2; + u32 serrorintrmask; +}; + +/* + * Sanity checks: + * - to compile at all, we need CONFIG_SYS_ATA_BASE_ADDR. + * - for ide_preinit to make sense, we need at least one of + * CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE1_OFFSET; + * - for ide_preinit to be called, we need CONFIG_IDE_PREINIT. + * Fail with an explanation message if these conditions are not met. + * This is particularly important for CONFIG_IDE_PREINIT, because + * its lack would not cause a build error. + */ + +#if !defined(CONFIG_SYS_ATA_BASE_ADDR) +#error CONFIG_SYS_ATA_BASE_ADDR must be defined +#elif !defined(CONFIG_SYS_ATA_IDE0_OFFSET) \ + && !defined(CONFIG_SYS_ATA_IDE1_OFFSET) +#error CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE1_OFFSET \ + must be defined +#elif !defined(CONFIG_IDE_PREINIT) +#error CONFIG_IDE_PREINIT must be defined +#endif + +/* + * Masks and values for SControl DETection and Interface Power Management, + * and for SStatus DETection. + */ + +#define MVSATA_EDMA_CMD_ATA_RST 0x00000004 +#define MVSATA_SCONTROL_DET_MASK 0x0000000F +#define MVSATA_SCONTROL_DET_NONE 0x00000000 +#define MVSATA_SCONTROL_DET_INIT 0x00000001 +#define MVSATA_SCONTROL_IPM_MASK 0x00000F00 +#define MVSATA_SCONTROL_IPM_NO_LP_ALLOWED 0x00000300 +#define MVSATA_SCONTROL_MASK \ + (MVSATA_SCONTROL_DET_MASK|MVSATA_SCONTROL_IPM_MASK) +#define MVSATA_PORT_INIT \ + (MVSATA_SCONTROL_DET_INIT|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED) +#define MVSATA_PORT_USE \ + (MVSATA_SCONTROL_DET_NONE|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED) +#define MVSATA_SSTATUS_DET_MASK 0x0000000F +#define MVSATA_SSTATUS_DET_DEVCOMM 0x00000003 + +/* + * Status codes to return to client callers. Currently, callers ignore + * exact value and only care for zero or nonzero, so no need to make this + * public, it is only #define'd for clarity. + * If/when standard negative codes are implemented in U-boot, then these + * #defines should be moved to, or replaced by ones from, the common list + * of status codes. + */ + +#define MVSATA_STATUS_OK 0 +#define MVSATA_STATUS_TIMEOUT -1 + +/* + * Initialize one MVSATAHC port: set SControl's IPM to "always active" + * and DET to "reset", then wait for SStatus's DET to become "device and + * comm ok" (or time out after 50 us if no device), then set SControl's + * DET back to "no action". + */ + +static int mvsata_ide_initialize_port(struct mvsata_port_registers *port) +{ + u32 control; + u32 status; + u32 timeleft = 10000; /* wait at most 10 ms for SATA reset to complete */ + + /* Hard reset */ + writel(MVSATA_EDMA_CMD_ATA_RST, &port->edma_cmd); + udelay(25); /* taken from original marvell port */ + writel(0, &port->edma_cmd); + + /* Set control IPM to 3 (no low power) and DET to 1 (initialize) */ + control = readl(&port->scontrol); + control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_INIT; + writel(control, &port->scontrol); + /* Toggle control DET back to 0 (normal operation) */ + control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_USE; + writel(control, &port->scontrol); + /* wait for status DET to become 3 (device and communication OK) */ + while (--timeleft) { + status = readl(&port->sstatus) & MVSATA_SSTATUS_DET_MASK; + if (status == MVSATA_SSTATUS_DET_DEVCOMM) + break; + udelay(1); + } + /* return success or time-out error depending on time left */ + if (!timeleft) + return MVSATA_STATUS_TIMEOUT; + return MVSATA_STATUS_OK; +} + +/* + * ide_preinit() will be called by ide_init in cmd_ide.c and will + * reset the MVSTATHC ports needed by the board. + */ + +int ide_preinit(void) +{ + int ret = MVSATA_STATUS_TIMEOUT; + int status; + + /* Enable ATA port 0 (could be SATA port 0 or 1) if declared */ +#if defined(CONFIG_SYS_ATA_IDE0_OFFSET) + status = mvsata_ide_initialize_port( + (struct mvsata_port_registers *) + (CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE0_OFFSET)); + if (status == MVSATA_STATUS_OK) + ret = MVSATA_STATUS_OK; +#endif + /* Enable ATA port 1 (could be SATA port 0 or 1) if declared */ +#if defined(CONFIG_SYS_ATA_IDE1_OFFSET) + status = mvsata_ide_initialize_port( + (struct mvsata_port_registers *) + (CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE1_OFFSET)); + if (status == MVSATA_STATUS_OK) + ret = MVSATA_STATUS_OK; +#endif + /* Return success if at least one port initialization succeeded */ + return ret; +} diff --git a/qemu/roms/u-boot/drivers/block/mxc_ata.c b/qemu/roms/u-boot/drivers/block/mxc_ata.c new file mode 100644 index 000000000..44bb406f4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/mxc_ata.c @@ -0,0 +1,129 @@ +/* + * Freescale iMX51 ATA driver + * + * Copyright (C) 2010 Marek Vasut + * + * Based on code by: + * Mahesh Mahadevan + * + * Based on code from original FSL ATA driver, which is + * part of eCos, the Embedded Configurable Operating System. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* MXC ATA register offsets */ +struct mxc_ata_config_regs { + u8 time_off; /* 0x00 */ + u8 time_on; + u8 time_1; + u8 time_2w; + u8 time_2r; + u8 time_ax; + u8 time_pio_rdx; + u8 time_4; + u8 time_9; + u8 time_m; + u8 time_jn; + u8 time_d; + u8 time_k; + u8 time_ack; + u8 time_env; + u8 time_udma_rdx; + u8 time_zah; /* 0x10 */ + u8 time_mlix; + u8 time_dvh; + u8 time_dzfs; + u8 time_dvs; + u8 time_cvh; + u8 time_ss; + u8 time_cyc; + u32 fifo_data_32; /* 0x18 */ + u32 fifo_data_16; + u32 fifo_fill; + u32 ata_control; + u32 interrupt_pending; + u32 interrupt_enable; + u32 interrupt_clear; + u32 fifo_alarm; +}; + +struct mxc_data_hdd_regs { + u32 drive_data; /* 0xa0 */ + u32 drive_features; + u32 drive_sector_count; + u32 drive_sector_num; + u32 drive_cyl_low; + u32 drive_cyl_high; + u32 drive_dev_head; + u32 command; + u32 status; + u32 alt_status; +}; + +/* PIO timing table */ +#define NR_PIO_SPECS 5 +static uint16_t pio_t1[NR_PIO_SPECS] = { 70, 50, 30, 30, 25 }; +static uint16_t pio_t2_8[NR_PIO_SPECS] = { 290, 290, 290, 80, 70 }; +static uint16_t pio_t4[NR_PIO_SPECS] = { 30, 20, 15, 10, 10 }; +static uint16_t pio_t9[NR_PIO_SPECS] = { 20, 15, 10, 10, 10 }; +static uint16_t pio_tA[NR_PIO_SPECS] = { 50, 50, 50, 50, 50 }; + +#define REG2OFF(reg) ((((uint32_t)reg) & 0x3) * 8) +static void set_ata_bus_timing(unsigned char mode) +{ + uint32_t T = 1000000000 / mxc_get_clock(MXC_IPG_CLK); + + struct mxc_ata_config_regs *ata_regs; + ata_regs = (struct mxc_ata_config_regs *)CONFIG_SYS_ATA_BASE_ADDR; + + if (mode >= NR_PIO_SPECS) + return; + + /* Write TIME_OFF/ON/1/2W */ + writeb(3, &ata_regs->time_off); + writeb(3, &ata_regs->time_on); + writeb((pio_t1[mode] + T) / T, &ata_regs->time_1); + writeb((pio_t2_8[mode] + T) / T, &ata_regs->time_2w); + + /* Write TIME_2R/AX/RDX/4 */ + writeb((pio_t2_8[mode] + T) / T, &ata_regs->time_2r); + writeb((pio_tA[mode] + T) / T + 2, &ata_regs->time_ax); + writeb(1, &ata_regs->time_pio_rdx); + writeb((pio_t4[mode] + T) / T, &ata_regs->time_4); + + /* Write TIME_9 ; the rest of timing registers is irrelevant for PIO */ + writeb((pio_t9[mode] + T) / T, &ata_regs->time_9); +} + +int ide_preinit(void) +{ + struct mxc_ata_config_regs *ata_regs; + ata_regs = (struct mxc_ata_config_regs *)CONFIG_SYS_ATA_BASE_ADDR; + + /* 46.3.3.4 @ FSL iMX51 manual */ + /* FIFO normal op., drive reset */ + writel(0x80, &ata_regs->ata_control); + /* FIFO normal op., drive not reset */ + writel(0xc0, &ata_regs->ata_control); + + /* Configure the PIO timing */ + set_ata_bus_timing(CONFIG_MXC_ATA_PIO_MODE); + + /* 46.3.3.4 @ FSL iMX51 manual */ + /* Drive not reset, IORDY handshake */ + writel(0x41, &ata_regs->ata_control); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/block/pata_bfin.c b/qemu/roms/u-boot/drivers/block/pata_bfin.c new file mode 100644 index 000000000..b7fd1cd63 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/pata_bfin.c @@ -0,0 +1,1204 @@ +/* + * Driver for Blackfin on-chip ATAPI controller. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (c) 2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pata_bfin.h" + +static struct ata_port port[CONFIG_SYS_SATA_MAX_DEVICE]; + +/** + * PIO Mode - Frequency compatibility + */ +/* mode: 0 1 2 3 4 */ +static const u32 pio_fsclk[] = +{ 33333333, 33333333, 33333333, 33333333, 33333333 }; + +/** + * MDMA Mode - Frequency compatibility + */ +/* mode: 0 1 2 */ +static const u32 mdma_fsclk[] = { 33333333, 33333333, 33333333 }; + +/** + * UDMA Mode - Frequency compatibility + * + * UDMA5 - 100 MB/s - SCLK = 133 MHz + * UDMA4 - 66 MB/s - SCLK >= 80 MHz + * UDMA3 - 44.4 MB/s - SCLK >= 50 MHz + * UDMA2 - 33 MB/s - SCLK >= 40 MHz + */ +/* mode: 0 1 2 3 4 5 */ +static const u32 udma_fsclk[] = +{ 33333333, 33333333, 40000000, 50000000, 80000000, 133333333 }; + +/** + * Register transfer timing table + */ +/* mode: 0 1 2 3 4 */ +/* Cycle Time */ +static const u32 reg_t0min[] = { 600, 383, 330, 180, 120 }; +/* DIOR/DIOW to end cycle */ +static const u32 reg_t2min[] = { 290, 290, 290, 70, 25 }; +/* DIOR/DIOW asserted pulse width */ +static const u32 reg_teocmin[] = { 290, 290, 290, 80, 70 }; + +/** + * PIO timing table + */ +/* mode: 0 1 2 3 4 */ +/* Cycle Time */ +static const u32 pio_t0min[] = { 600, 383, 240, 180, 120 }; +/* Address valid to DIOR/DIORW */ +static const u32 pio_t1min[] = { 70, 50, 30, 30, 25 }; +/* DIOR/DIOW to end cycle */ +static const u32 pio_t2min[] = { 165, 125, 100, 80, 70 }; +/* DIOR/DIOW asserted pulse width */ +static const u32 pio_teocmin[] = { 165, 125, 100, 70, 25 }; +/* DIOW data hold */ +static const u32 pio_t4min[] = { 30, 20, 15, 10, 10 }; + +/* ****************************************************************** + * Multiword DMA timing table + * ****************************************************************** + */ +/* mode: 0 1 2 */ +/* Cycle Time */ +static const u32 mdma_t0min[] = { 480, 150, 120 }; +/* DIOR/DIOW asserted pulse width */ +static const u32 mdma_tdmin[] = { 215, 80, 70 }; +/* DMACK to read data released */ +static const u32 mdma_thmin[] = { 20, 15, 10 }; +/* DIOR/DIOW to DMACK hold */ +static const u32 mdma_tjmin[] = { 20, 5, 5 }; +/* DIOR negated pulse width */ +static const u32 mdma_tkrmin[] = { 50, 50, 25 }; +/* DIOR negated pulse width */ +static const u32 mdma_tkwmin[] = { 215, 50, 25 }; +/* CS[1:0] valid to DIOR/DIOW */ +static const u32 mdma_tmmin[] = { 50, 30, 25 }; +/* DMACK to read data released */ +static const u32 mdma_tzmax[] = { 20, 25, 25 }; + +/** + * Ultra DMA timing table + */ +/* mode: 0 1 2 3 4 5 */ +static const u32 udma_tcycmin[] = { 112, 73, 54, 39, 25, 17 }; +static const u32 udma_tdvsmin[] = { 70, 48, 31, 20, 7, 5 }; +static const u32 udma_tenvmax[] = { 70, 70, 70, 55, 55, 50 }; +static const u32 udma_trpmin[] = { 160, 125, 100, 100, 100, 85 }; +static const u32 udma_tmin[] = { 5, 5, 5, 5, 3, 3 }; + + +static const u32 udma_tmlimin = 20; +static const u32 udma_tzahmin = 20; +static const u32 udma_tenvmin = 20; +static const u32 udma_tackmin = 20; +static const u32 udma_tssmin = 50; + +static void msleep(int count) +{ + int i; + + for (i = 0; i < count; i++) + udelay(1000); +} + +/** + * + * Function: num_clocks_min + * + * Description: + * calculate number of SCLK cycles to meet minimum timing + */ +static unsigned short num_clocks_min(unsigned long tmin, + unsigned long fsclk) +{ + unsigned long tmp ; + unsigned short result; + + tmp = tmin * (fsclk/1000/1000) / 1000; + result = (unsigned short)tmp; + if ((tmp*1000*1000) < (tmin*(fsclk/1000))) + result++; + + return result; +} + +/** + * bfin_set_piomode - Initialize host controller PATA PIO timings + * @ap: Port whose timings we are configuring + * @pio_mode: mode + * + * Set PIO mode for device. + * + * LOCKING: + * None (inherited from caller). + */ + +static void bfin_set_piomode(struct ata_port *ap, int pio_mode) +{ + int mode = pio_mode - XFER_PIO_0; + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + unsigned int fsclk = get_sclk(); + unsigned short teoc_reg, t2_reg, teoc_pio; + unsigned short t4_reg, t2_pio, t1_reg; + unsigned short n0, n6, t6min = 5; + + /* the most restrictive timing value is t6 and tc, the DIOW - data hold + * If one SCLK pulse is longer than this minimum value then register + * transfers cannot be supported at this frequency. + */ + n6 = num_clocks_min(t6min, fsclk); + if (mode >= 0 && mode <= 4 && n6 >= 1) { + debug("set piomode: mode=%d, fsclk=%ud\n", mode, fsclk); + /* calculate the timing values for register transfers. */ + while (mode > 0 && pio_fsclk[mode] > fsclk) + mode--; + + /* DIOR/DIOW to end cycle time */ + t2_reg = num_clocks_min(reg_t2min[mode], fsclk); + /* DIOR/DIOW asserted pulse width */ + teoc_reg = num_clocks_min(reg_teocmin[mode], fsclk); + /* Cycle Time */ + n0 = num_clocks_min(reg_t0min[mode], fsclk); + + /* increase t2 until we meed the minimum cycle length */ + if (t2_reg + teoc_reg < n0) + t2_reg = n0 - teoc_reg; + + /* calculate the timing values for pio transfers. */ + + /* DIOR/DIOW to end cycle time */ + t2_pio = num_clocks_min(pio_t2min[mode], fsclk); + /* DIOR/DIOW asserted pulse width */ + teoc_pio = num_clocks_min(pio_teocmin[mode], fsclk); + /* Cycle Time */ + n0 = num_clocks_min(pio_t0min[mode], fsclk); + + /* increase t2 until we meed the minimum cycle length */ + if (t2_pio + teoc_pio < n0) + t2_pio = n0 - teoc_pio; + + /* Address valid to DIOR/DIORW */ + t1_reg = num_clocks_min(pio_t1min[mode], fsclk); + + /* DIOW data hold */ + t4_reg = num_clocks_min(pio_t4min[mode], fsclk); + + ATAPI_SET_REG_TIM_0(base, (teoc_reg<<8 | t2_reg)); + ATAPI_SET_PIO_TIM_0(base, (t4_reg<<12 | t2_pio<<4 | t1_reg)); + ATAPI_SET_PIO_TIM_1(base, teoc_pio); + if (mode > 2) { + ATAPI_SET_CONTROL(base, + ATAPI_GET_CONTROL(base) | IORDY_EN); + } else { + ATAPI_SET_CONTROL(base, + ATAPI_GET_CONTROL(base) & ~IORDY_EN); + } + + /* Disable host ATAPI PIO interrupts */ + ATAPI_SET_INT_MASK(base, ATAPI_GET_INT_MASK(base) + & ~(PIO_DONE_MASK | HOST_TERM_XFER_MASK)); + SSYNC(); + } +} + +/** + * + * Function: wait_complete + * + * Description: Waits the interrupt from device + * + */ +static inline void wait_complete(void __iomem *base, unsigned short mask) +{ + unsigned short status; + unsigned int i = 0; + + for (i = 0; i < PATA_BFIN_WAIT_TIMEOUT; i++) { + status = ATAPI_GET_INT_STATUS(base) & mask; + if (status) + break; + } + + ATAPI_SET_INT_STATUS(base, mask); +} + +/** + * + * Function: write_atapi_register + * + * Description: Writes to ATA Device Resgister + * + */ + +static void write_atapi_register(void __iomem *base, + unsigned long ata_reg, unsigned short value) +{ + /* Program the ATA_DEV_TXBUF register with write data (to be + * written into the device). + */ + ATAPI_SET_DEV_TXBUF(base, value); + + /* Program the ATA_DEV_ADDR register with address of the + * device register (0x01 to 0x0F). + */ + ATAPI_SET_DEV_ADDR(base, ata_reg); + + /* Program the ATA_CTRL register with dir set to write (1) + */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | XFER_DIR)); + + /* ensure PIO DMA is not set */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA)); + + /* and start the transfer */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START)); + + /* Wait for the interrupt to indicate the end of the transfer. + * (We need to wait on and clear rhe ATA_DEV_INT interrupt status) + */ + wait_complete(base, PIO_DONE_INT); +} + +/** + * + * Function: read_atapi_register + * + *Description: Reads from ATA Device Resgister + * + */ + +static unsigned short read_atapi_register(void __iomem *base, + unsigned long ata_reg) +{ + /* Program the ATA_DEV_ADDR register with address of the + * device register (0x01 to 0x0F). + */ + ATAPI_SET_DEV_ADDR(base, ata_reg); + + /* Program the ATA_CTRL register with dir set to read (0) and + */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~XFER_DIR)); + + /* ensure PIO DMA is not set */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA)); + + /* and start the transfer */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START)); + + /* Wait for the interrupt to indicate the end of the transfer. + * (PIO_DONE interrupt is set and it doesn't seem to matter + * that we don't clear it) + */ + wait_complete(base, PIO_DONE_INT); + + /* Read the ATA_DEV_RXBUF register with write data (to be + * written into the device). + */ + return ATAPI_GET_DEV_RXBUF(base); +} + +/** + * + * Function: write_atapi_register_data + * + * Description: Writes to ATA Device Resgister + * + */ + +static void write_atapi_data(void __iomem *base, + int len, unsigned short *buf) +{ + int i; + + /* Set transfer length to 1 */ + ATAPI_SET_XFER_LEN(base, 1); + + /* Program the ATA_DEV_ADDR register with address of the + * ATA_REG_DATA + */ + ATAPI_SET_DEV_ADDR(base, ATA_REG_DATA); + + /* Program the ATA_CTRL register with dir set to write (1) + */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | XFER_DIR)); + + /* ensure PIO DMA is not set */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA)); + + for (i = 0; i < len; i++) { + /* Program the ATA_DEV_TXBUF register with write data (to be + * written into the device). + */ + ATAPI_SET_DEV_TXBUF(base, buf[i]); + + /* and start the transfer */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START)); + + /* Wait for the interrupt to indicate the end of the transfer. + * (We need to wait on and clear rhe ATA_DEV_INT + * interrupt status) + */ + wait_complete(base, PIO_DONE_INT); + } +} + +/** + * + * Function: read_atapi_register_data + * + * Description: Reads from ATA Device Resgister + * + */ + +static void read_atapi_data(void __iomem *base, + int len, unsigned short *buf) +{ + int i; + + /* Set transfer length to 1 */ + ATAPI_SET_XFER_LEN(base, 1); + + /* Program the ATA_DEV_ADDR register with address of the + * ATA_REG_DATA + */ + ATAPI_SET_DEV_ADDR(base, ATA_REG_DATA); + + /* Program the ATA_CTRL register with dir set to read (0) and + */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~XFER_DIR)); + + /* ensure PIO DMA is not set */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA)); + + for (i = 0; i < len; i++) { + /* and start the transfer */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START)); + + /* Wait for the interrupt to indicate the end of the transfer. + * (PIO_DONE interrupt is set and it doesn't seem to matter + * that we don't clear it) + */ + wait_complete(base, PIO_DONE_INT); + + /* Read the ATA_DEV_RXBUF register with write data (to be + * written into the device). + */ + buf[i] = ATAPI_GET_DEV_RXBUF(base); + } +} + +/** + * bfin_check_status - Read device status reg & clear interrupt + * @ap: port where the device is + * + * Note: Original code is ata_check_status(). + */ + +static u8 bfin_check_status(struct ata_port *ap) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + return read_atapi_register(base, ATA_REG_STATUS); +} + +/** + * bfin_check_altstatus - Read device alternate status reg + * @ap: port where the device is + */ + +static u8 bfin_check_altstatus(struct ata_port *ap) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + return read_atapi_register(base, ATA_REG_ALTSTATUS); +} + +/** + * bfin_ata_busy_wait - Wait for a port status register + * @ap: Port to wait for. + * @bits: bits that must be clear + * @max: number of 10uS waits to perform + * + * Waits up to max*10 microseconds for the selected bits in the port's + * status register to be cleared. + * Returns final value of status register. + * + * LOCKING: + * Inherited from caller. + */ +static inline u8 bfin_ata_busy_wait(struct ata_port *ap, unsigned int bits, + unsigned int max, u8 usealtstatus) +{ + u8 status; + + do { + udelay(10); + if (usealtstatus) + status = bfin_check_altstatus(ap); + else + status = bfin_check_status(ap); + max--; + } while (status != 0xff && (status & bits) && (max > 0)); + + return status; +} + +/** + * bfin_ata_busy_sleep - sleep until BSY clears, or timeout + * @ap: port containing status register to be polled + * @tmout_pat: impatience timeout in msecs + * @tmout: overall timeout in msecs + * + * Sleep until ATA Status register bit BSY clears, + * or a timeout occurs. + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +static int bfin_ata_busy_sleep(struct ata_port *ap, + long tmout_pat, unsigned long tmout) +{ + u8 status; + + status = bfin_ata_busy_wait(ap, ATA_BUSY, 300, 0); + while (status != 0xff && (status & ATA_BUSY) && tmout_pat > 0) { + msleep(50); + tmout_pat -= 50; + status = bfin_ata_busy_wait(ap, ATA_BUSY, 3, 0); + } + + if (status != 0xff && (status & ATA_BUSY)) + printf("port is slow to respond, please be patient " + "(Status 0x%x)\n", status); + + while (status != 0xff && (status & ATA_BUSY) && tmout_pat > 0) { + msleep(50); + tmout_pat -= 50; + status = bfin_check_status(ap); + } + + if (status == 0xff) + return -ENODEV; + + if (status & ATA_BUSY) { + printf("port failed to respond " + "(%lu secs, Status 0x%x)\n", + DIV_ROUND_UP(tmout, 1000), status); + return -EBUSY; + } + + return 0; +} + +/** + * bfin_dev_select - Select device 0/1 on ATA bus + * @ap: ATA channel to manipulate + * @device: ATA device (numbered from zero) to select + * + * Note: Original code is ata_sff_dev_select(). + */ + +static void bfin_dev_select(struct ata_port *ap, unsigned int device) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 tmp; + + + if (device == 0) + tmp = ATA_DEVICE_OBS; + else + tmp = ATA_DEVICE_OBS | ATA_DEV1; + + write_atapi_register(base, ATA_REG_DEVICE, tmp); + udelay(1); +} + +/** + * bfin_devchk - PATA device presence detection + * @ap: ATA channel to examine + * @device: Device to examine (starting at zero) + * + * Note: Original code is ata_devchk(). + */ + +static unsigned int bfin_devchk(struct ata_port *ap, + unsigned int device) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 nsect, lbal; + + bfin_dev_select(ap, device); + + write_atapi_register(base, ATA_REG_NSECT, 0x55); + write_atapi_register(base, ATA_REG_LBAL, 0xaa); + + write_atapi_register(base, ATA_REG_NSECT, 0xaa); + write_atapi_register(base, ATA_REG_LBAL, 0x55); + + write_atapi_register(base, ATA_REG_NSECT, 0x55); + write_atapi_register(base, ATA_REG_LBAL, 0xaa); + + nsect = read_atapi_register(base, ATA_REG_NSECT); + lbal = read_atapi_register(base, ATA_REG_LBAL); + + if ((nsect == 0x55) && (lbal == 0xaa)) + return 1; /* we found a device */ + + return 0; /* nothing found */ +} + +/** + * bfin_bus_post_reset - PATA device post reset + * + * Note: Original code is ata_bus_post_reset(). + */ + +static void bfin_bus_post_reset(struct ata_port *ap, unsigned int devmask) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + unsigned int dev0 = devmask & (1 << 0); + unsigned int dev1 = devmask & (1 << 1); + long deadline; + + /* if device 0 was found in ata_devchk, wait for its + * BSY bit to clear + */ + if (dev0) + bfin_ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + /* if device 1 was found in ata_devchk, wait for + * register access, then wait for BSY to clear + */ + deadline = ATA_TMOUT_BOOT; + while (dev1) { + u8 nsect, lbal; + + bfin_dev_select(ap, 1); + nsect = read_atapi_register(base, ATA_REG_NSECT); + lbal = read_atapi_register(base, ATA_REG_LBAL); + if ((nsect == 1) && (lbal == 1)) + break; + if (deadline <= 0) { + dev1 = 0; + break; + } + msleep(50); /* give drive a breather */ + deadline -= 50; + } + if (dev1) + bfin_ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + /* is all this really necessary? */ + bfin_dev_select(ap, 0); + if (dev1) + bfin_dev_select(ap, 1); + if (dev0) + bfin_dev_select(ap, 0); +} + +/** + * bfin_bus_softreset - PATA device software reset + * + * Note: Original code is ata_bus_softreset(). + */ + +static unsigned int bfin_bus_softreset(struct ata_port *ap, + unsigned int devmask) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + + /* software reset. causes dev0 to be selected */ + write_atapi_register(base, ATA_REG_CTRL, ap->ctl_reg); + udelay(20); + write_atapi_register(base, ATA_REG_CTRL, ap->ctl_reg | ATA_SRST); + udelay(20); + write_atapi_register(base, ATA_REG_CTRL, ap->ctl_reg); + + /* spec mandates ">= 2ms" before checking status. + * We wait 150ms, because that was the magic delay used for + * ATAPI devices in Hale Landis's ATADRVR, for the period of time + * between when the ATA command register is written, and then + * status is checked. Because waiting for "a while" before + * checking status is fine, post SRST, we perform this magic + * delay here as well. + * + * Old drivers/ide uses the 2mS rule and then waits for ready + */ + msleep(150); + + /* Before we perform post reset processing we want to see if + * the bus shows 0xFF because the odd clown forgets the D7 + * pulldown resistor. + */ + if (bfin_check_status(ap) == 0xFF) + return 0; + + bfin_bus_post_reset(ap, devmask); + + return 0; +} + +/** + * bfin_softreset - reset host port via ATA SRST + * @ap: port to reset + * + * Note: Original code is ata_sff_softreset(). + */ + +static int bfin_softreset(struct ata_port *ap) +{ + unsigned int err_mask; + + ap->dev_mask = 0; + + /* determine if device 0/1 are present. + * only one device is supported on one port by now. + */ + if (bfin_devchk(ap, 0)) + ap->dev_mask |= (1 << 0); + else if (bfin_devchk(ap, 1)) + ap->dev_mask |= (1 << 1); + else + return -ENODEV; + + /* select device 0 again */ + bfin_dev_select(ap, 0); + + /* issue bus reset */ + err_mask = bfin_bus_softreset(ap, ap->dev_mask); + if (err_mask) { + printf("SRST failed (err_mask=0x%x)\n", + err_mask); + ap->dev_mask = 0; + return -EIO; + } + + return 0; +} + +/** + * bfin_irq_clear - Clear ATAPI interrupt. + * @ap: Port associated with this ATA transaction. + * + * Note: Original code is ata_sff_irq_clear(). + */ + +static void bfin_irq_clear(struct ata_port *ap) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + + ATAPI_SET_INT_STATUS(base, ATAPI_GET_INT_STATUS(base)|ATAPI_DEV_INT + | MULTI_DONE_INT | UDMAIN_DONE_INT | UDMAOUT_DONE_INT + | MULTI_TERM_INT | UDMAIN_TERM_INT | UDMAOUT_TERM_INT); +} + +static u8 bfin_wait_for_irq(struct ata_port *ap, unsigned int max) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + + do { + if (ATAPI_GET_INT_STATUS(base) & (ATAPI_DEV_INT + | MULTI_DONE_INT | UDMAIN_DONE_INT | UDMAOUT_DONE_INT + | MULTI_TERM_INT | UDMAIN_TERM_INT | UDMAOUT_TERM_INT)) { + break; + } + udelay(1000); + max--; + } while ((max > 0)); + + return max == 0; +} + +/** + * bfin_ata_reset_port - initialize BFIN ATAPI port. + */ + +static int bfin_ata_reset_port(struct ata_port *ap) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + int count; + unsigned short status; + + /* Disable all ATAPI interrupts */ + ATAPI_SET_INT_MASK(base, 0); + SSYNC(); + + /* Assert the RESET signal 25us*/ + ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) | DEV_RST); + udelay(30); + + /* Negate the RESET signal for 2ms*/ + ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) & ~DEV_RST); + msleep(2); + + /* Wait on Busy flag to clear */ + count = 10000000; + do { + status = read_atapi_register(base, ATA_REG_STATUS); + } while (--count && (status & ATA_BUSY)); + + /* Enable only ATAPI Device interrupt */ + ATAPI_SET_INT_MASK(base, 1); + SSYNC(); + + return !count; +} + +/** + * + * Function: bfin_config_atapi_gpio + * + * Description: Configures the ATAPI pins for use + * + */ +static int bfin_config_atapi_gpio(struct ata_port *ap) +{ + const unsigned short pins[] = { + P_ATAPI_RESET, P_ATAPI_DIOR, P_ATAPI_DIOW, P_ATAPI_CS0, + P_ATAPI_CS1, P_ATAPI_DMACK, P_ATAPI_DMARQ, P_ATAPI_INTRQ, + P_ATAPI_IORDY, P_ATAPI_D0A, P_ATAPI_D1A, P_ATAPI_D2A, + P_ATAPI_D3A, P_ATAPI_D4A, P_ATAPI_D5A, P_ATAPI_D6A, + P_ATAPI_D7A, P_ATAPI_D8A, P_ATAPI_D9A, P_ATAPI_D10A, + P_ATAPI_D11A, P_ATAPI_D12A, P_ATAPI_D13A, P_ATAPI_D14A, + P_ATAPI_D15A, P_ATAPI_A0A, P_ATAPI_A1A, P_ATAPI_A2A, 0, + }; + + peripheral_request_list(pins, "pata_bfin"); + + return 0; +} + +/** + * bfin_atapi_probe - attach a bfin atapi interface + * @pdev: platform device + * + * Register a bfin atapi interface. + * + * + * Platform devices are expected to contain 2 resources per port: + * + * - I/O Base (IORESOURCE_IO) + * - IRQ (IORESOURCE_IRQ) + * + */ +static int bfin_ata_probe_port(struct ata_port *ap) +{ + if (bfin_config_atapi_gpio(ap)) { + printf("Requesting Peripherals faild\n"); + return -EFAULT; + } + + if (bfin_ata_reset_port(ap)) { + printf("Fail to reset ATAPI device\n"); + return -EFAULT; + } + + if (ap->ata_mode >= XFER_PIO_0 && ap->ata_mode <= XFER_PIO_4) + bfin_set_piomode(ap, ap->ata_mode); + else { + printf("Given ATA data transfer mode is not supported.\n"); + return -EFAULT; + } + + return 0; +} + +#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2) + +static void bfin_ata_identify(struct ata_port *ap, int dev) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 status = 0; + static u16 iobuf[ATA_SECTOR_WORDS]; + u64 n_sectors = 0; + hd_driveid_t *iop = (hd_driveid_t *)iobuf; + + memset(iobuf, 0, sizeof(iobuf)); + + if (!(ap->dev_mask & (1 << dev))) + return; + + debug("port=%d dev=%d\n", ap->port_no, dev); + + bfin_dev_select(ap, dev); + + status = 0; + /* Device Identify Command */ + write_atapi_register(base, ATA_REG_CMD, ATA_CMD_ID_ATA); + bfin_check_altstatus(ap); + udelay(10); + + status = bfin_ata_busy_wait(ap, ATA_BUSY, 1000, 0); + if (status & ATA_ERR) { + printf("\ndevice not responding\n"); + ap->dev_mask &= ~(1 << dev); + return; + } + + read_atapi_data(base, ATA_SECTOR_WORDS, iobuf); + + ata_swap_buf_le16(iobuf, ATA_SECTOR_WORDS); + + /* we require LBA and DMA support (bits 8 & 9 of word 49) */ + if (!ata_id_has_dma(iobuf) || !ata_id_has_lba(iobuf)) + printf("ata%u: no dma/lba\n", ap->port_no); + +#ifdef DEBUG + ata_dump_id(iobuf); +#endif + + n_sectors = ata_id_n_sectors(iobuf); + + if (n_sectors == 0) { + ap->dev_mask &= ~(1 << dev); + return; + } + + ata_id_c_string(iobuf, (unsigned char *)sata_dev_desc[ap->port_no].revision, + ATA_ID_FW_REV, sizeof(sata_dev_desc[ap->port_no].revision)); + ata_id_c_string(iobuf, (unsigned char *)sata_dev_desc[ap->port_no].vendor, + ATA_ID_PROD, sizeof(sata_dev_desc[ap->port_no].vendor)); + ata_id_c_string(iobuf, (unsigned char *)sata_dev_desc[ap->port_no].product, + ATA_ID_SERNO, sizeof(sata_dev_desc[ap->port_no].product)); + + if ((iop->config & 0x0080) == 0x0080) + sata_dev_desc[ap->port_no].removable = 1; + else + sata_dev_desc[ap->port_no].removable = 0; + + sata_dev_desc[ap->port_no].lba = (u32) n_sectors; + debug("lba=0x%lx\n", sata_dev_desc[ap->port_no].lba); + +#ifdef CONFIG_LBA48 + if (iop->command_set_2 & 0x0400) + sata_dev_desc[ap->port_no].lba48 = 1; + else + sata_dev_desc[ap->port_no].lba48 = 0; +#endif + + /* assuming HD */ + sata_dev_desc[ap->port_no].type = DEV_TYPE_HARDDISK; + sata_dev_desc[ap->port_no].blksz = ATA_SECT_SIZE; + sata_dev_desc[ap->port_no].log2blksz = + LOG2(sata_dev_desc[ap->port_no].blksz); + sata_dev_desc[ap->port_no].lun = 0; /* just to fill something in... */ + + printf("PATA device#%d %s is found on ata port#%d.\n", + ap->port_no%PATA_DEV_NUM_PER_PORT, + sata_dev_desc[ap->port_no].vendor, + ap->port_no/PATA_DEV_NUM_PER_PORT); +} + +static void bfin_ata_set_Feature_cmd(struct ata_port *ap, int dev) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 status = 0; + + if (!(ap->dev_mask & (1 << dev))) + return; + + bfin_dev_select(ap, dev); + + write_atapi_register(base, ATA_REG_FEATURE, SETFEATURES_XFER); + write_atapi_register(base, ATA_REG_NSECT, ap->ata_mode); + write_atapi_register(base, ATA_REG_LBAL, 0); + write_atapi_register(base, ATA_REG_LBAM, 0); + write_atapi_register(base, ATA_REG_LBAH, 0); + + write_atapi_register(base, ATA_REG_DEVICE, ATA_DEVICE_OBS); + write_atapi_register(base, ATA_REG_CMD, ATA_CMD_SET_FEATURES); + + udelay(50); + msleep(150); + + status = bfin_ata_busy_wait(ap, ATA_BUSY, 5000, 0); + if ((status & (ATA_BUSY | ATA_ERR))) { + printf("Error : status 0x%02x\n", status); + ap->dev_mask &= ~(1 << dev); + } +} + +int scan_sata(int dev) +{ + /* dev is the index of each ata device in the system. one PATA port + * contains 2 devices. one element in scan_done array indicates one + * PATA port. device connected to one PATA port is selected by + * bfin_dev_select() before access. + */ + struct ata_port *ap = &port[dev]; + static int scan_done[(CONFIG_SYS_SATA_MAX_DEVICE+1)/PATA_DEV_NUM_PER_PORT]; + + if (scan_done[dev/PATA_DEV_NUM_PER_PORT]) + return 0; + + /* Check for attached device */ + if (!bfin_ata_probe_port(ap)) { + if (bfin_softreset(ap)) { + /* soft reset failed, try a hard one */ + bfin_ata_reset_port(ap); + if (bfin_softreset(ap)) + scan_done[dev/PATA_DEV_NUM_PER_PORT] = 1; + } else { + scan_done[dev/PATA_DEV_NUM_PER_PORT] = 1; + } + } + if (scan_done[dev/PATA_DEV_NUM_PER_PORT]) { + /* Probe device and set xfer mode */ + bfin_ata_identify(ap, dev%PATA_DEV_NUM_PER_PORT); + bfin_ata_set_Feature_cmd(ap, dev%PATA_DEV_NUM_PER_PORT); + init_part(&sata_dev_desc[dev]); + return 0; + } + + printf("PATA device#%d is not present on ATA port#%d.\n", + ap->port_no%PATA_DEV_NUM_PER_PORT, + ap->port_no/PATA_DEV_NUM_PER_PORT); + + return -1; +} + +int init_sata(int dev) +{ + struct ata_port *ap = &port[dev]; + static u8 init_done; + int res = 1; + + if (init_done) + return res; + + init_done = 1; + + switch (dev/PATA_DEV_NUM_PER_PORT) { + case 0: + ap->ioaddr.ctl_addr = ATAPI_CONTROL; + ap->ata_mode = CONFIG_BFIN_ATA_MODE; + break; + default: + printf("Tried to scan unknown port %d.\n", dev); + return res; + } + + if (ap->ata_mode < XFER_PIO_0 || ap->ata_mode > XFER_PIO_4) { + ap->ata_mode = XFER_PIO_4; + printf("DMA mode is not supported. Set to PIO mode 4.\n"); + } + + ap->port_no = dev; + ap->ctl_reg = 0x8; /*Default value of control reg */ + + res = 0; + return res; +} + +/* Read up to 255 sectors + * + * Returns sectors read +*/ +static u8 do_one_read(struct ata_port *ap, u64 blknr, u8 blkcnt, u16 *buffer, + uchar lba48) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 sr = 0; + u8 status; + u16 err = 0; + + if (!(bfin_check_status(ap) & ATA_DRDY)) { + printf("Device ata%d not ready\n", ap->port_no); + return 0; + } + + /* Set up transfer */ +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + write_atapi_register(base, ATA_REG_NSECT, 0); + write_atapi_register(base, ATA_REG_LBAL, (blknr >> 24) & 0xFF); + write_atapi_register(base, ATA_REG_LBAM, (blknr >> 32) & 0xFF); + write_atapi_register(base, ATA_REG_LBAH, (blknr >> 40) & 0xFF); + } +#endif + write_atapi_register(base, ATA_REG_NSECT, blkcnt); + write_atapi_register(base, ATA_REG_LBAL, (blknr >> 0) & 0xFF); + write_atapi_register(base, ATA_REG_LBAM, (blknr >> 8) & 0xFF); + write_atapi_register(base, ATA_REG_LBAH, (blknr >> 16) & 0xFF); + +#ifdef CONFIG_LBA48 + if (lba48) { + write_atapi_register(base, ATA_REG_DEVICE, ATA_LBA); + write_atapi_register(base, ATA_REG_CMD, ATA_CMD_PIO_READ_EXT); + } else +#endif + { + write_atapi_register(base, ATA_REG_DEVICE, ATA_LBA | ((blknr >> 24) & 0xF)); + write_atapi_register(base, ATA_REG_CMD, ATA_CMD_PIO_READ); + } + status = bfin_ata_busy_wait(ap, ATA_BUSY, 500000, 1); + + if (status & (ATA_BUSY | ATA_ERR)) { + printf("Device %d not responding status 0x%x.\n", ap->port_no, status); + err = read_atapi_register(base, ATA_REG_ERR); + printf("Error reg = 0x%x\n", err); + return sr; + } + + while (blkcnt--) { + if (bfin_wait_for_irq(ap, 500)) { + printf("ata%u irq failed\n", ap->port_no); + return sr; + } + + status = bfin_check_status(ap); + if (status & ATA_ERR) { + err = read_atapi_register(base, ATA_REG_ERR); + printf("ata%u error %d\n", ap->port_no, err); + return sr; + } + bfin_irq_clear(ap); + + /* Read one sector */ + read_atapi_data(base, ATA_SECTOR_WORDS, buffer); + buffer += ATA_SECTOR_WORDS; + sr++; + } + + return sr; +} + +ulong sata_read(int dev, ulong block, lbaint_t blkcnt, void *buff) +{ + struct ata_port *ap = &port[dev]; + ulong n = 0, sread; + u16 *buffer = (u16 *) buff; + u8 status = 0; + u64 blknr = (u64) block; + unsigned char lba48 = 0; + +#ifdef CONFIG_LBA48 + if (blknr > 0xfffffff) { + if (!sata_dev_desc[dev].lba48) { + printf("Drive doesn't support 48-bit addressing\n"); + return 0; + } + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + bfin_dev_select(ap, dev%PATA_DEV_NUM_PER_PORT); + + while (blkcnt > 0) { + + if (blkcnt > 255) + sread = 255; + else + sread = blkcnt; + + status = do_one_read(ap, blknr, sread, buffer, lba48); + if (status != sread) { + printf("Read failed\n"); + return n; + } + + blkcnt -= sread; + blknr += sread; + n += sread; + buffer += sread * ATA_SECTOR_WORDS; + } + return n; +} + +ulong sata_write(int dev, ulong block, lbaint_t blkcnt, const void *buff) +{ + struct ata_port *ap = &port[dev]; + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + ulong n = 0; + u16 *buffer = (u16 *) buff; + unsigned char status = 0; + u64 blknr = (u64) block; +#ifdef CONFIG_LBA48 + unsigned char lba48 = 0; + + if (blknr > 0xfffffff) { + if (!sata_dev_desc[dev].lba48) { + printf("Drive doesn't support 48-bit addressing\n"); + return 0; + } + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + + bfin_dev_select(ap, dev%PATA_DEV_NUM_PER_PORT); + + while (blkcnt-- > 0) { + status = bfin_ata_busy_wait(ap, ATA_BUSY, 50000, 0); + if (status & ATA_BUSY) { + printf("ata%u failed to respond\n", ap->port_no); + return n; + } +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + write_atapi_register(base, ATA_REG_NSECT, 0); + write_atapi_register(base, ATA_REG_LBAL, + (blknr >> 24) & 0xFF); + write_atapi_register(base, ATA_REG_LBAM, + (blknr >> 32) & 0xFF); + write_atapi_register(base, ATA_REG_LBAH, + (blknr >> 40) & 0xFF); + } +#endif + write_atapi_register(base, ATA_REG_NSECT, 1); + write_atapi_register(base, ATA_REG_LBAL, (blknr >> 0) & 0xFF); + write_atapi_register(base, ATA_REG_LBAM, (blknr >> 8) & 0xFF); + write_atapi_register(base, ATA_REG_LBAH, (blknr >> 16) & 0xFF); +#ifdef CONFIG_LBA48 + if (lba48) { + write_atapi_register(base, ATA_REG_DEVICE, ATA_LBA); + write_atapi_register(base, ATA_REG_CMD, + ATA_CMD_PIO_WRITE_EXT); + } else +#endif + { + write_atapi_register(base, ATA_REG_DEVICE, + ATA_LBA | ((blknr >> 24) & 0xF)); + write_atapi_register(base, ATA_REG_CMD, + ATA_CMD_PIO_WRITE); + } + + /*may take up to 5 sec */ + status = bfin_ata_busy_wait(ap, ATA_BUSY, 50000, 0); + if ((status & (ATA_DRQ | ATA_BUSY | ATA_ERR)) != ATA_DRQ) { + printf("Error no DRQ dev %d blk %ld: sts 0x%02x\n", + ap->port_no, (ulong) blknr, status); + return n; + } + + write_atapi_data(base, ATA_SECTOR_WORDS, buffer); + bfin_check_altstatus(ap); + udelay(1); + + ++n; + ++blknr; + buffer += ATA_SECTOR_WORDS; + } + return n; +} diff --git a/qemu/roms/u-boot/drivers/block/pata_bfin.h b/qemu/roms/u-boot/drivers/block/pata_bfin.h new file mode 100644 index 000000000..2093cf06b --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/pata_bfin.h @@ -0,0 +1,171 @@ +/* + * Driver for Blackfin on-chip ATAPI controller. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (c) 2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef PATA_BFIN_H +#define PATA_BFIN_H + +#include + +struct ata_ioports { + unsigned long cmd_addr; + unsigned long data_addr; + unsigned long error_addr; + unsigned long feature_addr; + unsigned long nsect_addr; + unsigned long lbal_addr; + unsigned long lbam_addr; + unsigned long lbah_addr; + unsigned long device_addr; + unsigned long status_addr; + unsigned long command_addr; + unsigned long altstatus_addr; + unsigned long ctl_addr; + unsigned long bmdma_addr; + unsigned long scr_addr; +}; + +struct ata_port { + unsigned int port_no; /* primary=0, secondary=1 */ + struct ata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */ + unsigned long flag; + unsigned int ata_mode; + unsigned char ctl_reg; + unsigned char last_ctl; + unsigned char dev_mask; +}; + +#define DRV_NAME "pata-bfin" +#define DRV_VERSION "0.9" +#define __iomem + +#define ATA_REG_CTRL 0x0E +#define ATA_REG_ALTSTATUS ATA_REG_CTRL +#define ATA_TMOUT_BOOT 30000 +#define ATA_TMOUT_BOOT_QUICK 7000 + +#define PATA_BFIN_WAIT_TIMEOUT 10000 +#define PATA_DEV_NUM_PER_PORT 2 + +/* These are the offset of the controller's registers */ +#define ATAPI_OFFSET_CONTROL 0x00 +#define ATAPI_OFFSET_STATUS 0x04 +#define ATAPI_OFFSET_DEV_ADDR 0x08 +#define ATAPI_OFFSET_DEV_TXBUF 0x0c +#define ATAPI_OFFSET_DEV_RXBUF 0x10 +#define ATAPI_OFFSET_INT_MASK 0x14 +#define ATAPI_OFFSET_INT_STATUS 0x18 +#define ATAPI_OFFSET_XFER_LEN 0x1c +#define ATAPI_OFFSET_LINE_STATUS 0x20 +#define ATAPI_OFFSET_SM_STATE 0x24 +#define ATAPI_OFFSET_TERMINATE 0x28 +#define ATAPI_OFFSET_PIO_TFRCNT 0x2c +#define ATAPI_OFFSET_DMA_TFRCNT 0x30 +#define ATAPI_OFFSET_UMAIN_TFRCNT 0x34 +#define ATAPI_OFFSET_UDMAOUT_TFRCNT 0x38 +#define ATAPI_OFFSET_REG_TIM_0 0x40 +#define ATAPI_OFFSET_PIO_TIM_0 0x44 +#define ATAPI_OFFSET_PIO_TIM_1 0x48 +#define ATAPI_OFFSET_MULTI_TIM_0 0x50 +#define ATAPI_OFFSET_MULTI_TIM_1 0x54 +#define ATAPI_OFFSET_MULTI_TIM_2 0x58 +#define ATAPI_OFFSET_ULTRA_TIM_0 0x60 +#define ATAPI_OFFSET_ULTRA_TIM_1 0x64 +#define ATAPI_OFFSET_ULTRA_TIM_2 0x68 +#define ATAPI_OFFSET_ULTRA_TIM_3 0x6c + + +#define ATAPI_GET_CONTROL(base)\ + bfin_read16(base + ATAPI_OFFSET_CONTROL) +#define ATAPI_SET_CONTROL(base, val)\ + bfin_write16(base + ATAPI_OFFSET_CONTROL, val) +#define ATAPI_GET_STATUS(base)\ + bfin_read16(base + ATAPI_OFFSET_STATUS) +#define ATAPI_GET_DEV_ADDR(base)\ + bfin_read16(base + ATAPI_OFFSET_DEV_ADDR) +#define ATAPI_SET_DEV_ADDR(base, val)\ + bfin_write16(base + ATAPI_OFFSET_DEV_ADDR, val) +#define ATAPI_GET_DEV_TXBUF(base)\ + bfin_read16(base + ATAPI_OFFSET_DEV_TXBUF) +#define ATAPI_SET_DEV_TXBUF(base, val)\ + bfin_write16(base + ATAPI_OFFSET_DEV_TXBUF, val) +#define ATAPI_GET_DEV_RXBUF(base)\ + bfin_read16(base + ATAPI_OFFSET_DEV_RXBUF) +#define ATAPI_SET_DEV_RXBUF(base, val)\ + bfin_write16(base + ATAPI_OFFSET_DEV_RXBUF, val) +#define ATAPI_GET_INT_MASK(base)\ + bfin_read16(base + ATAPI_OFFSET_INT_MASK) +#define ATAPI_SET_INT_MASK(base, val)\ + bfin_write16(base + ATAPI_OFFSET_INT_MASK, val) +#define ATAPI_GET_INT_STATUS(base)\ + bfin_read16(base + ATAPI_OFFSET_INT_STATUS) +#define ATAPI_SET_INT_STATUS(base, val)\ + bfin_write16(base + ATAPI_OFFSET_INT_STATUS, val) +#define ATAPI_GET_XFER_LEN(base)\ + bfin_read16(base + ATAPI_OFFSET_XFER_LEN) +#define ATAPI_SET_XFER_LEN(base, val)\ + bfin_write16(base + ATAPI_OFFSET_XFER_LEN, val) +#define ATAPI_GET_LINE_STATUS(base)\ + bfin_read16(base + ATAPI_OFFSET_LINE_STATUS) +#define ATAPI_GET_SM_STATE(base)\ + bfin_read16(base + ATAPI_OFFSET_SM_STATE) +#define ATAPI_GET_TERMINATE(base)\ + bfin_read16(base + ATAPI_OFFSET_TERMINATE) +#define ATAPI_SET_TERMINATE(base, val)\ + bfin_write16(base + ATAPI_OFFSET_TERMINATE, val) +#define ATAPI_GET_PIO_TFRCNT(base)\ + bfin_read16(base + ATAPI_OFFSET_PIO_TFRCNT) +#define ATAPI_GET_DMA_TFRCNT(base)\ + bfin_read16(base + ATAPI_OFFSET_DMA_TFRCNT) +#define ATAPI_GET_UMAIN_TFRCNT(base)\ + bfin_read16(base + ATAPI_OFFSET_UMAIN_TFRCNT) +#define ATAPI_GET_UDMAOUT_TFRCNT(base)\ + bfin_read16(base + ATAPI_OFFSET_UDMAOUT_TFRCNT) +#define ATAPI_GET_REG_TIM_0(base)\ + bfin_read16(base + ATAPI_OFFSET_REG_TIM_0) +#define ATAPI_SET_REG_TIM_0(base, val)\ + bfin_write16(base + ATAPI_OFFSET_REG_TIM_0, val) +#define ATAPI_GET_PIO_TIM_0(base)\ + bfin_read16(base + ATAPI_OFFSET_PIO_TIM_0) +#define ATAPI_SET_PIO_TIM_0(base, val)\ + bfin_write16(base + ATAPI_OFFSET_PIO_TIM_0, val) +#define ATAPI_GET_PIO_TIM_1(base)\ + bfin_read16(base + ATAPI_OFFSET_PIO_TIM_1) +#define ATAPI_SET_PIO_TIM_1(base, val)\ + bfin_write16(base + ATAPI_OFFSET_PIO_TIM_1, val) +#define ATAPI_GET_MULTI_TIM_0(base)\ + bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_0) +#define ATAPI_SET_MULTI_TIM_0(base, val)\ + bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_0, val) +#define ATAPI_GET_MULTI_TIM_1(base)\ + bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_1) +#define ATAPI_SET_MULTI_TIM_1(base, val)\ + bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_1, val) +#define ATAPI_GET_MULTI_TIM_2(base)\ + bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_2) +#define ATAPI_SET_MULTI_TIM_2(base, val)\ + bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_2, val) +#define ATAPI_GET_ULTRA_TIM_0(base)\ + bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_0) +#define ATAPI_SET_ULTRA_TIM_0(base, val)\ + bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_0, val) +#define ATAPI_GET_ULTRA_TIM_1(base)\ + bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_1) +#define ATAPI_SET_ULTRA_TIM_1(base, val)\ + bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_1, val) +#define ATAPI_GET_ULTRA_TIM_2(base)\ + bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_2) +#define ATAPI_SET_ULTRA_TIM_2(base, val)\ + bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_2, val) +#define ATAPI_GET_ULTRA_TIM_3(base)\ + bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_3) +#define ATAPI_SET_ULTRA_TIM_3(base, val)\ + bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_3, val) + +#endif diff --git a/qemu/roms/u-boot/drivers/block/sandbox.c b/qemu/roms/u-boot/drivers/block/sandbox.c new file mode 100644 index 000000000..73f4c4a9e --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/sandbox.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 Henrik Nordstrom + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct host_block_dev host_devices[CONFIG_HOST_MAX_DEVICES]; + +static struct host_block_dev *find_host_device(int dev) +{ + if (dev >= 0 && dev < CONFIG_HOST_MAX_DEVICES) + return &host_devices[dev]; + + return NULL; +} + +static unsigned long host_block_read(int dev, unsigned long start, + lbaint_t blkcnt, void *buffer) +{ + struct host_block_dev *host_dev = find_host_device(dev); + + if (!host_dev) + return -1; + if (os_lseek(host_dev->fd, + start * host_dev->blk_dev.blksz, + OS_SEEK_SET) == -1) { + printf("ERROR: Invalid position\n"); + return -1; + } + ssize_t len = os_read(host_dev->fd, buffer, + blkcnt * host_dev->blk_dev.blksz); + if (len >= 0) + return len / host_dev->blk_dev.blksz; + return -1; +} + +static unsigned long host_block_write(int dev, unsigned long start, + lbaint_t blkcnt, const void *buffer) +{ + struct host_block_dev *host_dev = find_host_device(dev); + if (os_lseek(host_dev->fd, + start * host_dev->blk_dev.blksz, + OS_SEEK_SET) == -1) { + printf("ERROR: Invalid position\n"); + return -1; + } + ssize_t len = os_write(host_dev->fd, buffer, blkcnt * + host_dev->blk_dev.blksz); + if (len >= 0) + return len / host_dev->blk_dev.blksz; + return -1; +} + +int host_dev_bind(int dev, char *filename) +{ + struct host_block_dev *host_dev = find_host_device(dev); + + if (!host_dev) + return -1; + if (host_dev->blk_dev.priv) { + os_close(host_dev->fd); + host_dev->blk_dev.priv = NULL; + } + if (host_dev->filename) + free(host_dev->filename); + if (filename && *filename) { + host_dev->filename = strdup(filename); + } else { + host_dev->filename = NULL; + return 0; + } + + host_dev->fd = os_open(host_dev->filename, OS_O_RDWR); + if (host_dev->fd == -1) { + printf("Failed to access host backing file '%s'\n", + host_dev->filename); + return 1; + } + + block_dev_desc_t *blk_dev = &host_dev->blk_dev; + blk_dev->if_type = IF_TYPE_HOST; + blk_dev->priv = host_dev; + blk_dev->blksz = 512; + blk_dev->lba = os_lseek(host_dev->fd, 0, OS_SEEK_END) / blk_dev->blksz; + blk_dev->block_read = host_block_read; + blk_dev->block_write = host_block_write; + blk_dev->dev = dev; + blk_dev->part_type = PART_TYPE_UNKNOWN; + init_part(blk_dev); + + return 0; +} + +int host_get_dev_err(int dev, block_dev_desc_t **blk_devp) +{ + struct host_block_dev *host_dev = find_host_device(dev); + + if (!host_dev) + return -ENODEV; + + if (!host_dev->blk_dev.priv) + return -ENOENT; + + *blk_devp = &host_dev->blk_dev; + return 0; +} + +block_dev_desc_t *host_get_dev(int dev) +{ + block_dev_desc_t *blk_dev; + + if (host_get_dev_err(dev, &blk_dev)) + return NULL; + + return blk_dev; +} diff --git a/qemu/roms/u-boot/drivers/block/sata_dwc.c b/qemu/roms/u-boot/drivers/block/sata_dwc.c new file mode 100644 index 000000000..efca5eaba --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/sata_dwc.c @@ -0,0 +1,2071 @@ +/* + * sata_dwc.c + * + * Synopsys DesignWare Cores (DWC) SATA host driver + * + * Author: Mark Miesfeld + * + * Ported from 2.6.19.2 to 2.6.25/26 by Stefan Roese + * Copyright 2008 DENX Software Engineering + * + * Based on versions provided by AMCC and Synopsys which are: + * Copyright 2006 Applied Micro Circuits Corporation + * COPYRIGHT (C) 2005 SYNOPSYS, INC. ALL RIGHTS RESERVED + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* + * SATA support based on the chip canyonlands. + * + * 04-17-2009 + * The local version of this driver for the canyonlands board + * does not use interrupts but polls the chip instead. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sata_dwc.h" + +#define DMA_NUM_CHANS 1 +#define DMA_NUM_CHAN_REGS 8 + +#define AHB_DMA_BRST_DFLT 16 + +struct dmareg { + u32 low; + u32 high; +}; + +struct dma_chan_regs { + struct dmareg sar; + struct dmareg dar; + struct dmareg llp; + struct dmareg ctl; + struct dmareg sstat; + struct dmareg dstat; + struct dmareg sstatar; + struct dmareg dstatar; + struct dmareg cfg; + struct dmareg sgr; + struct dmareg dsr; +}; + +struct dma_interrupt_regs { + struct dmareg tfr; + struct dmareg block; + struct dmareg srctran; + struct dmareg dsttran; + struct dmareg error; +}; + +struct ahb_dma_regs { + struct dma_chan_regs chan_regs[DMA_NUM_CHAN_REGS]; + struct dma_interrupt_regs interrupt_raw; + struct dma_interrupt_regs interrupt_status; + struct dma_interrupt_regs interrupt_mask; + struct dma_interrupt_regs interrupt_clear; + struct dmareg statusInt; + struct dmareg rq_srcreg; + struct dmareg rq_dstreg; + struct dmareg rq_sgl_srcreg; + struct dmareg rq_sgl_dstreg; + struct dmareg rq_lst_srcreg; + struct dmareg rq_lst_dstreg; + struct dmareg dma_cfg; + struct dmareg dma_chan_en; + struct dmareg dma_id; + struct dmareg dma_test; + struct dmareg res1; + struct dmareg res2; + /* DMA Comp Params + * Param 6 = dma_param[0], Param 5 = dma_param[1], + * Param 4 = dma_param[2] ... + */ + struct dmareg dma_params[6]; +}; + +#define DMA_EN 0x00000001 +#define DMA_DI 0x00000000 +#define DMA_CHANNEL(ch) (0x00000001 << (ch)) +#define DMA_ENABLE_CHAN(ch) ((0x00000001 << (ch)) | \ + ((0x000000001 << (ch)) << 8)) +#define DMA_DISABLE_CHAN(ch) (0x00000000 | \ + ((0x000000001 << (ch)) << 8)) + +#define SATA_DWC_MAX_PORTS 1 +#define SATA_DWC_SCR_OFFSET 0x24 +#define SATA_DWC_REG_OFFSET 0x64 + +struct sata_dwc_regs { + u32 fptagr; + u32 fpbor; + u32 fptcr; + u32 dmacr; + u32 dbtsr; + u32 intpr; + u32 intmr; + u32 errmr; + u32 llcr; + u32 phycr; + u32 physr; + u32 rxbistpd; + u32 rxbistpd1; + u32 rxbistpd2; + u32 txbistpd; + u32 txbistpd1; + u32 txbistpd2; + u32 bistcr; + u32 bistfctr; + u32 bistsr; + u32 bistdecr; + u32 res[15]; + u32 testr; + u32 versionr; + u32 idr; + u32 unimpl[192]; + u32 dmadr[256]; +}; + +#define SATA_DWC_TXFIFO_DEPTH 0x01FF +#define SATA_DWC_RXFIFO_DEPTH 0x01FF + +#define SATA_DWC_DBTSR_MWR(size) ((size / 4) & SATA_DWC_TXFIFO_DEPTH) +#define SATA_DWC_DBTSR_MRD(size) (((size / 4) & \ + SATA_DWC_RXFIFO_DEPTH) << 16) +#define SATA_DWC_INTPR_DMAT 0x00000001 +#define SATA_DWC_INTPR_NEWFP 0x00000002 +#define SATA_DWC_INTPR_PMABRT 0x00000004 +#define SATA_DWC_INTPR_ERR 0x00000008 +#define SATA_DWC_INTPR_NEWBIST 0x00000010 +#define SATA_DWC_INTPR_IPF 0x10000000 +#define SATA_DWC_INTMR_DMATM 0x00000001 +#define SATA_DWC_INTMR_NEWFPM 0x00000002 +#define SATA_DWC_INTMR_PMABRTM 0x00000004 +#define SATA_DWC_INTMR_ERRM 0x00000008 +#define SATA_DWC_INTMR_NEWBISTM 0x00000010 + +#define SATA_DWC_DMACR_TMOD_TXCHEN 0x00000004 +#define SATA_DWC_DMACR_TXRXCH_CLEAR SATA_DWC_DMACR_TMOD_TXCHEN + +#define SATA_DWC_QCMD_MAX 32 + +#define SATA_DWC_SERROR_ERR_BITS 0x0FFF0F03 + +#define HSDEVP_FROM_AP(ap) (struct sata_dwc_device_port*) \ + (ap)->private_data + +struct sata_dwc_device { + struct device *dev; + struct ata_probe_ent *pe; + struct ata_host *host; + u8 *reg_base; + struct sata_dwc_regs *sata_dwc_regs; + int irq_dma; +}; + +struct sata_dwc_device_port { + struct sata_dwc_device *hsdev; + int cmd_issued[SATA_DWC_QCMD_MAX]; + u32 dma_chan[SATA_DWC_QCMD_MAX]; + int dma_pending[SATA_DWC_QCMD_MAX]; +}; + +enum { + SATA_DWC_CMD_ISSUED_NOT = 0, + SATA_DWC_CMD_ISSUED_PEND = 1, + SATA_DWC_CMD_ISSUED_EXEC = 2, + SATA_DWC_CMD_ISSUED_NODATA = 3, + + SATA_DWC_DMA_PENDING_NONE = 0, + SATA_DWC_DMA_PENDING_TX = 1, + SATA_DWC_DMA_PENDING_RX = 2, +}; + +#define msleep(a) udelay(a * 1000) +#define ssleep(a) msleep(a * 1000) + +static int ata_probe_timeout = (ATA_TMOUT_INTERNAL / 100); + +enum sata_dev_state { + SATA_INIT = 0, + SATA_READY = 1, + SATA_NODEVICE = 2, + SATA_ERROR = 3, +}; +enum sata_dev_state dev_state = SATA_INIT; + +static struct ahb_dma_regs *sata_dma_regs = 0; +static struct ata_host *phost; +static struct ata_port ap; +static struct ata_port *pap = ≈ +static struct ata_device ata_device; +static struct sata_dwc_device_port dwc_devp; + +static void *scr_addr_sstatus; +static u32 temp_n_block = 0; + +static unsigned ata_exec_internal(struct ata_device *dev, + struct ata_taskfile *tf, const u8 *cdb, + int dma_dir, unsigned int buflen, + unsigned long timeout); +static unsigned int ata_dev_set_feature(struct ata_device *dev, + u8 enable,u8 feature); +static unsigned int ata_dev_init_params(struct ata_device *dev, + u16 heads, u16 sectors); +static u8 ata_irq_on(struct ata_port *ap); +static struct ata_queued_cmd *__ata_qc_from_tag(struct ata_port *ap, + unsigned int tag); +static int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, + u8 status, int in_wq); +static void ata_tf_to_host(struct ata_port *ap, + const struct ata_taskfile *tf); +static void ata_exec_command(struct ata_port *ap, + const struct ata_taskfile *tf); +static unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc); +static u8 ata_check_altstatus(struct ata_port *ap); +static u8 ata_check_status(struct ata_port *ap); +static void ata_dev_select(struct ata_port *ap, unsigned int device, + unsigned int wait, unsigned int can_sleep); +static void ata_qc_issue(struct ata_queued_cmd *qc); +static void ata_tf_load(struct ata_port *ap, + const struct ata_taskfile *tf); +static int ata_dev_read_sectors(unsigned char* pdata, + unsigned long datalen, u32 block, u32 n_block); +static int ata_dev_write_sectors(unsigned char* pdata, + unsigned long datalen , u32 block, u32 n_block); +static void ata_std_dev_select(struct ata_port *ap, unsigned int device); +static void ata_qc_complete(struct ata_queued_cmd *qc); +static void __ata_qc_complete(struct ata_queued_cmd *qc); +static void fill_result_tf(struct ata_queued_cmd *qc); +static void ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf); +static void ata_mmio_data_xfer(struct ata_device *dev, + unsigned char *buf, + unsigned int buflen,int do_write); +static void ata_pio_task(struct ata_port *arg_ap); +static void __ata_port_freeze(struct ata_port *ap); +static int ata_port_freeze(struct ata_port *ap); +static void ata_qc_free(struct ata_queued_cmd *qc); +static void ata_pio_sectors(struct ata_queued_cmd *qc); +static void ata_pio_sector(struct ata_queued_cmd *qc); +static void ata_pio_queue_task(struct ata_port *ap, + void *data,unsigned long delay); +static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq); +static int sata_dwc_softreset(struct ata_port *ap); +static int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, + unsigned int flags, u16 *id); +static int check_sata_dev_state(void); + +static const struct ata_port_info sata_dwc_port_info[] = { + { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING | + ATA_FLAG_SRST | ATA_FLAG_NCQ, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, + }, +}; + +int init_sata(int dev) +{ + struct sata_dwc_device hsdev; + struct ata_host host; + struct ata_port_info pi = sata_dwc_port_info[0]; + struct ata_link *link; + struct sata_dwc_device_port hsdevp = dwc_devp; + u8 *base = 0; + u8 *sata_dma_regs_addr = 0; + u8 status; + unsigned long base_addr = 0; + int chan = 0; + int rc; + int i; + + phost = &host; + + base = (u8*)SATA_BASE_ADDR; + + hsdev.sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET); + + host.n_ports = SATA_DWC_MAX_PORTS; + + for (i = 0; i < SATA_DWC_MAX_PORTS; i++) { + ap.pflags |= ATA_PFLAG_INITIALIZING; + ap.flags = ATA_FLAG_DISABLED; + ap.print_id = -1; + ap.ctl = ATA_DEVCTL_OBS; + ap.host = &host; + ap.last_ctl = 0xFF; + + link = &ap.link; + link->ap = ≈ + link->pmp = 0; + link->active_tag = ATA_TAG_POISON; + link->hw_sata_spd_limit = 0; + + ap.port_no = i; + host.ports[i] = ≈ + } + + ap.pio_mask = pi.pio_mask; + ap.mwdma_mask = pi.mwdma_mask; + ap.udma_mask = pi.udma_mask; + ap.flags |= pi.flags; + ap.link.flags |= pi.link_flags; + + host.ports[0]->ioaddr.cmd_addr = base; + host.ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET; + scr_addr_sstatus = base + SATA_DWC_SCR_OFFSET; + + base_addr = (unsigned long)base; + + host.ports[0]->ioaddr.cmd_addr = (void *)base_addr + 0x00; + host.ports[0]->ioaddr.data_addr = (void *)base_addr + 0x00; + + host.ports[0]->ioaddr.error_addr = (void *)base_addr + 0x04; + host.ports[0]->ioaddr.feature_addr = (void *)base_addr + 0x04; + + host.ports[0]->ioaddr.nsect_addr = (void *)base_addr + 0x08; + + host.ports[0]->ioaddr.lbal_addr = (void *)base_addr + 0x0c; + host.ports[0]->ioaddr.lbam_addr = (void *)base_addr + 0x10; + host.ports[0]->ioaddr.lbah_addr = (void *)base_addr + 0x14; + + host.ports[0]->ioaddr.device_addr = (void *)base_addr + 0x18; + host.ports[0]->ioaddr.command_addr = (void *)base_addr + 0x1c; + host.ports[0]->ioaddr.status_addr = (void *)base_addr + 0x1c; + + host.ports[0]->ioaddr.altstatus_addr = (void *)base_addr + 0x20; + host.ports[0]->ioaddr.ctl_addr = (void *)base_addr + 0x20; + + sata_dma_regs_addr = (u8*)SATA_DMA_REG_ADDR; + sata_dma_regs = (void *__iomem)sata_dma_regs_addr; + + status = ata_check_altstatus(&ap); + + if (status == 0x7f) { + printf("Hard Disk not found.\n"); + dev_state = SATA_NODEVICE; + rc = false; + return rc; + } + + printf("Waiting for device..."); + i = 0; + while (1) { + udelay(10000); + + status = ata_check_altstatus(&ap); + + if ((status & ATA_BUSY) == 0) { + printf("\n"); + break; + } + + i++; + if (i > (ATA_RESET_TIME * 100)) { + printf("** TimeOUT **\n"); + + dev_state = SATA_NODEVICE; + rc = false; + return rc; + } + if ((i >= 100) && ((i % 100) == 0)) + printf("."); + } + + rc = sata_dwc_softreset(&ap); + + if (rc) { + printf("sata_dwc : error. soft reset failed\n"); + return rc; + } + + for (chan = 0; chan < DMA_NUM_CHANS; chan++) { + out_le32(&(sata_dma_regs->interrupt_mask.error.low), + DMA_DISABLE_CHAN(chan)); + + out_le32(&(sata_dma_regs->interrupt_mask.tfr.low), + DMA_DISABLE_CHAN(chan)); + } + + out_le32(&(sata_dma_regs->dma_cfg.low), DMA_DI); + + out_le32(&hsdev.sata_dwc_regs->intmr, + SATA_DWC_INTMR_ERRM | + SATA_DWC_INTMR_PMABRTM); + + /* Unmask the error bits that should trigger + * an error interrupt by setting the error mask register. + */ + out_le32(&hsdev.sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS); + + hsdev.host = ap.host; + memset(&hsdevp, 0, sizeof(hsdevp)); + hsdevp.hsdev = &hsdev; + + for (i = 0; i < SATA_DWC_QCMD_MAX; i++) + hsdevp.cmd_issued[i] = SATA_DWC_CMD_ISSUED_NOT; + + out_le32((void __iomem *)scr_addr_sstatus + 4, + in_le32((void __iomem *)scr_addr_sstatus + 4)); + + rc = 0; + return rc; +} + +static u8 ata_check_altstatus(struct ata_port *ap) +{ + u8 val = 0; + val = readb(ap->ioaddr.altstatus_addr); + return val; +} + +static int sata_dwc_softreset(struct ata_port *ap) +{ + u8 nsect,lbal = 0; + u8 tmp = 0; + struct ata_ioports *ioaddr = &ap->ioaddr; + + in_le32((void *)ap->ioaddr.scr_addr + (SCR_ERROR * 4)); + + writeb(0x55, ioaddr->nsect_addr); + writeb(0xaa, ioaddr->lbal_addr); + writeb(0xaa, ioaddr->nsect_addr); + writeb(0x55, ioaddr->lbal_addr); + writeb(0x55, ioaddr->nsect_addr); + writeb(0xaa, ioaddr->lbal_addr); + + nsect = readb(ioaddr->nsect_addr); + lbal = readb(ioaddr->lbal_addr); + + if ((nsect == 0x55) && (lbal == 0xaa)) { + printf("Device found\n"); + } else { + printf("No device found\n"); + dev_state = SATA_NODEVICE; + return false; + } + + tmp = ATA_DEVICE_OBS; + writeb(tmp, ioaddr->device_addr); + writeb(ap->ctl, ioaddr->ctl_addr); + + udelay(200); + + writeb(ap->ctl | ATA_SRST, ioaddr->ctl_addr); + + udelay(200); + writeb(ap->ctl, ioaddr->ctl_addr); + + msleep(150); + ata_check_status(ap); + + msleep(50); + ata_check_status(ap); + + while (1) { + u8 status = ata_check_status(ap); + + if (!(status & ATA_BUSY)) + break; + + printf("Hard Disk status is BUSY.\n"); + msleep(50); + } + + tmp = ATA_DEVICE_OBS; + writeb(tmp, ioaddr->device_addr); + + nsect = readb(ioaddr->nsect_addr); + lbal = readb(ioaddr->lbal_addr); + + return 0; +} + +static u8 ata_check_status(struct ata_port *ap) +{ + u8 val = 0; + val = readb(ap->ioaddr.status_addr); + return val; +} + +static int ata_id_has_hipm(const u16 *id) +{ + u16 val = id[76]; + + if (val == 0 || val == 0xffff) + return -1; + + return val & (1 << 9); +} + +static int ata_id_has_dipm(const u16 *id) +{ + u16 val = id[78]; + + if (val == 0 || val == 0xffff) + return -1; + + return val & (1 << 3); +} + +int scan_sata(int dev) +{ + int i; + int rc; + u8 status; + const u16 *id; + struct ata_device *ata_dev = &ata_device; + unsigned long pio_mask, mwdma_mask; + char revbuf[7]; + u16 iobuf[ATA_SECTOR_WORDS]; + + memset(iobuf, 0, sizeof(iobuf)); + + if (dev_state == SATA_NODEVICE) + return 1; + + printf("Waiting for device..."); + i = 0; + while (1) { + udelay(10000); + + status = ata_check_altstatus(&ap); + + if ((status & ATA_BUSY) == 0) { + printf("\n"); + break; + } + + i++; + if (i > (ATA_RESET_TIME * 100)) { + printf("** TimeOUT **\n"); + + dev_state = SATA_NODEVICE; + return 1; + } + if ((i >= 100) && ((i % 100) == 0)) + printf("."); + } + + udelay(1000); + + rc = ata_dev_read_id(ata_dev, &ata_dev->class, + ATA_READID_POSTRESET,ata_dev->id); + if (rc) { + printf("sata_dwc : error. failed sata scan\n"); + return 1; + } + + /* SATA drives indicate we have a bridge. We don't know which + * end of the link the bridge is which is a problem + */ + if (ata_id_is_sata(ata_dev->id)) + ap.cbl = ATA_CBL_SATA; + + id = ata_dev->id; + + ata_dev->flags &= ~ATA_DFLAG_CFG_MASK; + ata_dev->max_sectors = 0; + ata_dev->cdb_len = 0; + ata_dev->n_sectors = 0; + ata_dev->cylinders = 0; + ata_dev->heads = 0; + ata_dev->sectors = 0; + + if (id[ATA_ID_FIELD_VALID] & (1 << 1)) { + pio_mask = id[ATA_ID_PIO_MODES] & 0x03; + pio_mask <<= 3; + pio_mask |= 0x7; + } else { + /* If word 64 isn't valid then Word 51 high byte holds + * the PIO timing number for the maximum. Turn it into + * a mask. + */ + u8 mode = (id[ATA_ID_OLD_PIO_MODES] >> 8) & 0xFF; + if (mode < 5) { + pio_mask = (2 << mode) - 1; + } else { + pio_mask = 1; + } + } + + mwdma_mask = id[ATA_ID_MWDMA_MODES] & 0x07; + + if (ata_id_is_cfa(id)) { + int pio = id[163] & 0x7; + int dma = (id[163] >> 3) & 7; + + if (pio) + pio_mask |= (1 << 5); + if (pio > 1) + pio_mask |= (1 << 6); + if (dma) + mwdma_mask |= (1 << 3); + if (dma > 1) + mwdma_mask |= (1 << 4); + } + + if (ata_dev->class == ATA_DEV_ATA) { + if (ata_id_is_cfa(id)) { + if (id[162] & 1) + printf("supports DRM functions and may " + "not be fully accessable.\n"); + sprintf(revbuf, "%s", "CFA"); + } else { + if (ata_id_has_tpm(id)) + printf("supports DRM functions and may " + "not be fully accessable.\n"); + } + + ata_dev->n_sectors = ata_id_n_sectors((u16*)id); + + if (ata_dev->id[59] & 0x100) + ata_dev->multi_count = ata_dev->id[59] & 0xff; + + if (ata_id_has_lba(id)) { + char ncq_desc[20]; + + ata_dev->flags |= ATA_DFLAG_LBA; + if (ata_id_has_lba48(id)) { + ata_dev->flags |= ATA_DFLAG_LBA48; + + if (ata_dev->n_sectors >= (1UL << 28) && + ata_id_has_flush_ext(id)) + ata_dev->flags |= ATA_DFLAG_FLUSH_EXT; + } + if (!ata_id_has_ncq(ata_dev->id)) + ncq_desc[0] = '\0'; + + if (ata_dev->horkage & ATA_HORKAGE_NONCQ) + sprintf(ncq_desc, "%s", "NCQ (not used)"); + + if (ap.flags & ATA_FLAG_NCQ) + ata_dev->flags |= ATA_DFLAG_NCQ; + } + ata_dev->cdb_len = 16; + } + ata_dev->max_sectors = ATA_MAX_SECTORS; + if (ata_dev->flags & ATA_DFLAG_LBA48) + ata_dev->max_sectors = ATA_MAX_SECTORS_LBA48; + + if (!(ata_dev->horkage & ATA_HORKAGE_IPM)) { + if (ata_id_has_hipm(ata_dev->id)) + ata_dev->flags |= ATA_DFLAG_HIPM; + if (ata_id_has_dipm(ata_dev->id)) + ata_dev->flags |= ATA_DFLAG_DIPM; + } + + if ((ap.cbl == ATA_CBL_SATA) && (!ata_id_is_sata(ata_dev->id))) { + ata_dev->udma_mask &= ATA_UDMA5; + ata_dev->max_sectors = ATA_MAX_SECTORS; + } + + if (ata_dev->horkage & ATA_HORKAGE_DIAGNOSTIC) { + printf("Drive reports diagnostics failure." + "This may indicate a drive\n"); + printf("fault or invalid emulation." + "Contact drive vendor for information.\n"); + } + + rc = check_sata_dev_state(); + + ata_id_c_string(ata_dev->id, + (unsigned char *)sata_dev_desc[dev].revision, + ATA_ID_FW_REV, sizeof(sata_dev_desc[dev].revision)); + ata_id_c_string(ata_dev->id, + (unsigned char *)sata_dev_desc[dev].vendor, + ATA_ID_PROD, sizeof(sata_dev_desc[dev].vendor)); + ata_id_c_string(ata_dev->id, + (unsigned char *)sata_dev_desc[dev].product, + ATA_ID_SERNO, sizeof(sata_dev_desc[dev].product)); + + sata_dev_desc[dev].lba = (u32) ata_dev->n_sectors; + +#ifdef CONFIG_LBA48 + if (ata_dev->id[83] & (1 << 10)) { + sata_dev_desc[dev].lba48 = 1; + } else { + sata_dev_desc[dev].lba48 = 0; + } +#endif + + return 0; +} + +static u8 ata_busy_wait(struct ata_port *ap, + unsigned int bits,unsigned int max) +{ + u8 status; + + do { + udelay(10); + status = ata_check_status(ap); + max--; + } while (status != 0xff && (status & bits) && (max > 0)); + + return status; +} + +static int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, + unsigned int flags, u16 *id) +{ + struct ata_port *ap = pap; + unsigned int class = *p_class; + struct ata_taskfile tf; + unsigned int err_mask = 0; + const char *reason; + int may_fallback = 1, tried_spinup = 0; + u8 status; + int rc; + + status = ata_busy_wait(ap, ATA_BUSY, 30000); + if (status & ATA_BUSY) { + printf("BSY = 0 check. timeout.\n"); + rc = false; + return rc; + } + + ata_dev_select(ap, dev->devno, 1, 1); + +retry: + memset(&tf, 0, sizeof(tf)); + ap->print_id = 1; + ap->flags &= ~ATA_FLAG_DISABLED; + tf.ctl = ap->ctl; + tf.device = ATA_DEVICE_OBS; + tf.command = ATA_CMD_ID_ATA; + tf.protocol = ATA_PROT_PIO; + + /* Some devices choke if TF registers contain garbage. Make + * sure those are properly initialized. + */ + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + + /* Device presence detection is unreliable on some + * controllers. Always poll IDENTIFY if available. + */ + tf.flags |= ATA_TFLAG_POLLING; + + temp_n_block = 1; + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE, + sizeof(id[0]) * ATA_ID_WORDS, 0); + + if (err_mask) { + if (err_mask & AC_ERR_NODEV_HINT) { + printf("NODEV after polling detection\n"); + return -ENOENT; + } + + if ((err_mask == AC_ERR_DEV) && (tf.feature & ATA_ABORTED)) { + /* Device or controller might have reported + * the wrong device class. Give a shot at the + * other IDENTIFY if the current one is + * aborted by the device. + */ + if (may_fallback) { + may_fallback = 0; + + if (class == ATA_DEV_ATA) { + class = ATA_DEV_ATAPI; + } else { + class = ATA_DEV_ATA; + } + goto retry; + } + /* Control reaches here iff the device aborted + * both flavors of IDENTIFYs which happens + * sometimes with phantom devices. + */ + printf("both IDENTIFYs aborted, assuming NODEV\n"); + return -ENOENT; + } + rc = -EIO; + reason = "I/O error"; + goto err_out; + } + + /* Falling back doesn't make sense if ID data was read + * successfully at least once. + */ + may_fallback = 0; + + unsigned int id_cnt; + + for (id_cnt = 0; id_cnt < ATA_ID_WORDS; id_cnt++) + id[id_cnt] = le16_to_cpu(id[id_cnt]); + + + rc = -EINVAL; + reason = "device reports invalid type"; + + if (class == ATA_DEV_ATA) { + if (!ata_id_is_ata(id) && !ata_id_is_cfa(id)) + goto err_out; + } else { + if (ata_id_is_ata(id)) + goto err_out; + } + if (!tried_spinup && (id[2] == 0x37c8 || id[2] == 0x738c)) { + tried_spinup = 1; + /* + * Drive powered-up in standby mode, and requires a specific + * SET_FEATURES spin-up subcommand before it will accept + * anything other than the original IDENTIFY command. + */ + err_mask = ata_dev_set_feature(dev, SETFEATURES_SPINUP, 0); + if (err_mask && id[2] != 0x738c) { + rc = -EIO; + reason = "SPINUP failed"; + goto err_out; + } + /* + * If the drive initially returned incomplete IDENTIFY info, + * we now must reissue the IDENTIFY command. + */ + if (id[2] == 0x37c8) + goto retry; + } + + if ((flags & ATA_READID_POSTRESET) && class == ATA_DEV_ATA) { + /* + * The exact sequence expected by certain pre-ATA4 drives is: + * SRST RESET + * IDENTIFY (optional in early ATA) + * INITIALIZE DEVICE PARAMETERS (later IDE and ATA) + * anything else.. + * Some drives were very specific about that exact sequence. + * + * Note that ATA4 says lba is mandatory so the second check + * shoud never trigger. + */ + if (ata_id_major_version(id) < 4 || !ata_id_has_lba(id)) { + err_mask = ata_dev_init_params(dev, id[3], id[6]); + if (err_mask) { + rc = -EIO; + reason = "INIT_DEV_PARAMS failed"; + goto err_out; + } + + /* current CHS translation info (id[53-58]) might be + * changed. reread the identify device info. + */ + flags &= ~ATA_READID_POSTRESET; + goto retry; + } + } + + *p_class = class; + return 0; + +err_out: + printf("failed to READ ID (%s, err_mask=0x%x)\n", reason, err_mask); + return rc; +} + +static u8 ata_wait_idle(struct ata_port *ap) +{ + u8 status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + return status; +} + +static void ata_dev_select(struct ata_port *ap, unsigned int device, + unsigned int wait, unsigned int can_sleep) +{ + if (wait) + ata_wait_idle(ap); + + ata_std_dev_select(ap, device); + + if (wait) + ata_wait_idle(ap); +} + +static void ata_std_dev_select(struct ata_port *ap, unsigned int device) +{ + u8 tmp; + + if (device == 0) { + tmp = ATA_DEVICE_OBS; + } else { + tmp = ATA_DEVICE_OBS | ATA_DEV1; + } + + writeb(tmp, ap->ioaddr.device_addr); + + readb(ap->ioaddr.altstatus_addr); + + udelay(1); +} + +static int waiting_for_reg_state(volatile u8 *offset, + int timeout_msec, + u32 sign) +{ + int i; + u32 status; + + for (i = 0; i < timeout_msec; i++) { + status = readl(offset); + if ((status & sign) != 0) + break; + msleep(1); + } + + return (i < timeout_msec) ? 0 : -1; +} + +static void ata_qc_reinit(struct ata_queued_cmd *qc) +{ + qc->dma_dir = DMA_NONE; + qc->flags = 0; + qc->nbytes = qc->extrabytes = qc->curbytes = 0; + qc->n_elem = 0; + qc->err_mask = 0; + qc->sect_size = ATA_SECT_SIZE; + qc->nbytes = ATA_SECT_SIZE * temp_n_block; + + memset(&qc->tf, 0, sizeof(qc->tf)); + qc->tf.ctl = 0; + qc->tf.device = ATA_DEVICE_OBS; + + qc->result_tf.command = ATA_DRDY; + qc->result_tf.feature = 0; +} + +struct ata_queued_cmd *__ata_qc_from_tag(struct ata_port *ap, + unsigned int tag) +{ + if (tag < ATA_MAX_QUEUE) + return &ap->qcmd[tag]; + return NULL; +} + +static void __ata_port_freeze(struct ata_port *ap) +{ + printf("set port freeze.\n"); + ap->pflags |= ATA_PFLAG_FROZEN; +} + +static int ata_port_freeze(struct ata_port *ap) +{ + __ata_port_freeze(ap); + return 0; +} + +unsigned ata_exec_internal(struct ata_device *dev, + struct ata_taskfile *tf, const u8 *cdb, + int dma_dir, unsigned int buflen, + unsigned long timeout) +{ + struct ata_link *link = dev->link; + struct ata_port *ap = pap; + struct ata_queued_cmd *qc; + unsigned int tag, preempted_tag; + u32 preempted_sactive, preempted_qc_active; + int preempted_nr_active_links; + unsigned int err_mask; + int rc = 0; + u8 status; + + status = ata_busy_wait(ap, ATA_BUSY, 300000); + if (status & ATA_BUSY) { + printf("BSY = 0 check. timeout.\n"); + rc = false; + return rc; + } + + if (ap->pflags & ATA_PFLAG_FROZEN) + return AC_ERR_SYSTEM; + + tag = ATA_TAG_INTERNAL; + + if (test_and_set_bit(tag, &ap->qc_allocated)) { + rc = false; + return rc; + } + + qc = __ata_qc_from_tag(ap, tag); + qc->tag = tag; + qc->ap = ap; + qc->dev = dev; + + ata_qc_reinit(qc); + + preempted_tag = link->active_tag; + preempted_sactive = link->sactive; + preempted_qc_active = ap->qc_active; + preempted_nr_active_links = ap->nr_active_links; + link->active_tag = ATA_TAG_POISON; + link->sactive = 0; + ap->qc_active = 0; + ap->nr_active_links = 0; + + qc->tf = *tf; + if (cdb) + memcpy(qc->cdb, cdb, ATAPI_CDB_LEN); + qc->flags |= ATA_QCFLAG_RESULT_TF; + qc->dma_dir = dma_dir; + qc->private_data = 0; + + ata_qc_issue(qc); + + if (!timeout) + timeout = ata_probe_timeout * 1000 / HZ; + + status = ata_busy_wait(ap, ATA_BUSY, 30000); + if (status & ATA_BUSY) { + printf("BSY = 0 check. timeout.\n"); + printf("altstatus = 0x%x.\n", status); + qc->err_mask |= AC_ERR_OTHER; + return qc->err_mask; + } + + if (waiting_for_reg_state(ap->ioaddr.altstatus_addr, 1000, 0x8)) { + u8 status = 0; + u8 errorStatus = 0; + + status = readb(ap->ioaddr.altstatus_addr); + if ((status & 0x01) != 0) { + errorStatus = readb(ap->ioaddr.feature_addr); + if (errorStatus == 0x04 && + qc->tf.command == ATA_CMD_PIO_READ_EXT){ + printf("Hard Disk doesn't support LBA48\n"); + dev_state = SATA_ERROR; + qc->err_mask |= AC_ERR_OTHER; + return qc->err_mask; + } + } + qc->err_mask |= AC_ERR_OTHER; + return qc->err_mask; + } + + status = ata_busy_wait(ap, ATA_BUSY, 10); + if (status & ATA_BUSY) { + printf("BSY = 0 check. timeout.\n"); + qc->err_mask |= AC_ERR_OTHER; + return qc->err_mask; + } + + ata_pio_task(ap); + + if (!rc) { + if (qc->flags & ATA_QCFLAG_ACTIVE) { + qc->err_mask |= AC_ERR_TIMEOUT; + ata_port_freeze(ap); + } + } + + if (qc->flags & ATA_QCFLAG_FAILED) { + if (qc->result_tf.command & (ATA_ERR | ATA_DF)) + qc->err_mask |= AC_ERR_DEV; + + if (!qc->err_mask) + qc->err_mask |= AC_ERR_OTHER; + + if (qc->err_mask & ~AC_ERR_OTHER) + qc->err_mask &= ~AC_ERR_OTHER; + } + + *tf = qc->result_tf; + err_mask = qc->err_mask; + ata_qc_free(qc); + link->active_tag = preempted_tag; + link->sactive = preempted_sactive; + ap->qc_active = preempted_qc_active; + ap->nr_active_links = preempted_nr_active_links; + + if (ap->flags & ATA_FLAG_DISABLED) { + err_mask |= AC_ERR_SYSTEM; + ap->flags &= ~ATA_FLAG_DISABLED; + } + + return err_mask; +} + +static void ata_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_link *link = qc->dev->link; + u8 prot = qc->tf.protocol; + + if (ata_is_ncq(prot)) { + if (!link->sactive) + ap->nr_active_links++; + link->sactive |= 1 << qc->tag; + } else { + ap->nr_active_links++; + link->active_tag = qc->tag; + } + + qc->flags |= ATA_QCFLAG_ACTIVE; + ap->qc_active |= 1 << qc->tag; + + if (qc->dev->flags & ATA_DFLAG_SLEEPING) { + msleep(1); + return; + } + + qc->err_mask |= ata_qc_issue_prot(qc); + if (qc->err_mask) + goto err; + + return; +err: + ata_qc_complete(qc); +} + +static unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + + if (ap->flags & ATA_FLAG_PIO_POLLING) { + switch (qc->tf.protocol) { + case ATA_PROT_PIO: + case ATA_PROT_NODATA: + case ATAPI_PROT_PIO: + case ATAPI_PROT_NODATA: + qc->tf.flags |= ATA_TFLAG_POLLING; + break; + default: + break; + } + } + + ata_dev_select(ap, qc->dev->devno, 1, 0); + + switch (qc->tf.protocol) { + case ATA_PROT_PIO: + if (qc->tf.flags & ATA_TFLAG_POLLING) + qc->tf.ctl |= ATA_NIEN; + + ata_tf_to_host(ap, &qc->tf); + + ap->hsm_task_state = HSM_ST; + + if (qc->tf.flags & ATA_TFLAG_POLLING) + ata_pio_queue_task(ap, qc, 0); + + break; + + default: + return AC_ERR_SYSTEM; + } + + return 0; +} + +static void ata_tf_to_host(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + ata_tf_load(ap, tf); + ata_exec_command(ap, tf); +} + +static void ata_tf_load(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; + + if (tf->ctl != ap->last_ctl) { + if (ioaddr->ctl_addr) + writeb(tf->ctl, ioaddr->ctl_addr); + ap->last_ctl = tf->ctl; + ata_wait_idle(ap); + } + + if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { + writeb(tf->hob_feature, ioaddr->feature_addr); + writeb(tf->hob_nsect, ioaddr->nsect_addr); + writeb(tf->hob_lbal, ioaddr->lbal_addr); + writeb(tf->hob_lbam, ioaddr->lbam_addr); + writeb(tf->hob_lbah, ioaddr->lbah_addr); + } + + if (is_addr) { + writeb(tf->feature, ioaddr->feature_addr); + writeb(tf->nsect, ioaddr->nsect_addr); + writeb(tf->lbal, ioaddr->lbal_addr); + writeb(tf->lbam, ioaddr->lbam_addr); + writeb(tf->lbah, ioaddr->lbah_addr); + } + + if (tf->flags & ATA_TFLAG_DEVICE) + writeb(tf->device, ioaddr->device_addr); + + ata_wait_idle(ap); +} + +static void ata_exec_command(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + writeb(tf->command, ap->ioaddr.command_addr); + + readb(ap->ioaddr.altstatus_addr); + + udelay(1); +} + +static void ata_pio_queue_task(struct ata_port *ap, + void *data,unsigned long delay) +{ + ap->port_task_data = data; +} + +static unsigned int ac_err_mask(u8 status) +{ + if (status & (ATA_BUSY | ATA_DRQ)) + return AC_ERR_HSM; + if (status & (ATA_ERR | ATA_DF)) + return AC_ERR_DEV; + return 0; +} + +static unsigned int __ac_err_mask(u8 status) +{ + unsigned int mask = ac_err_mask(status); + if (mask == 0) + return AC_ERR_OTHER; + return mask; +} + +static void ata_pio_task(struct ata_port *arg_ap) +{ + struct ata_port *ap = arg_ap; + struct ata_queued_cmd *qc = ap->port_task_data; + u8 status; + int poll_next; + +fsm_start: + /* + * This is purely heuristic. This is a fast path. + * Sometimes when we enter, BSY will be cleared in + * a chk-status or two. If not, the drive is probably seeking + * or something. Snooze for a couple msecs, then + * chk-status again. If still busy, queue delayed work. + */ + status = ata_busy_wait(ap, ATA_BUSY, 5); + if (status & ATA_BUSY) { + msleep(2); + status = ata_busy_wait(ap, ATA_BUSY, 10); + if (status & ATA_BUSY) { + ata_pio_queue_task(ap, qc, ATA_SHORT_PAUSE); + return; + } + } + + poll_next = ata_hsm_move(ap, qc, status, 1); + + /* another command or interrupt handler + * may be running at this point. + */ + if (poll_next) + goto fsm_start; +} + +static int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, + u8 status, int in_wq) +{ + int poll_next; + +fsm_start: + switch (ap->hsm_task_state) { + case HSM_ST_FIRST: + poll_next = (qc->tf.flags & ATA_TFLAG_POLLING); + + if ((status & ATA_DRQ) == 0) { + if (status & (ATA_ERR | ATA_DF)) { + qc->err_mask |= AC_ERR_DEV; + } else { + qc->err_mask |= AC_ERR_HSM; + } + ap->hsm_task_state = HSM_ST_ERR; + goto fsm_start; + } + + /* Device should not ask for data transfer (DRQ=1) + * when it finds something wrong. + * We ignore DRQ here and stop the HSM by + * changing hsm_task_state to HSM_ST_ERR and + * let the EH abort the command or reset the device. + */ + if (status & (ATA_ERR | ATA_DF)) { + if (!(qc->dev->horkage & ATA_HORKAGE_STUCK_ERR)) { + printf("DRQ=1 with device error, " + "dev_stat 0x%X\n", status); + qc->err_mask |= AC_ERR_HSM; + ap->hsm_task_state = HSM_ST_ERR; + goto fsm_start; + } + } + + if (qc->tf.protocol == ATA_PROT_PIO) { + /* PIO data out protocol. + * send first data block. + */ + /* ata_pio_sectors() might change the state + * to HSM_ST_LAST. so, the state is changed here + * before ata_pio_sectors(). + */ + ap->hsm_task_state = HSM_ST; + ata_pio_sectors(qc); + } else { + printf("protocol is not ATA_PROT_PIO \n"); + } + break; + + case HSM_ST: + if ((status & ATA_DRQ) == 0) { + if (status & (ATA_ERR | ATA_DF)) { + qc->err_mask |= AC_ERR_DEV; + } else { + /* HSM violation. Let EH handle this. + * Phantom devices also trigger this + * condition. Mark hint. + */ + qc->err_mask |= AC_ERR_HSM | AC_ERR_NODEV_HINT; + } + + ap->hsm_task_state = HSM_ST_ERR; + goto fsm_start; + } + /* For PIO reads, some devices may ask for + * data transfer (DRQ=1) alone with ERR=1. + * We respect DRQ here and transfer one + * block of junk data before changing the + * hsm_task_state to HSM_ST_ERR. + * + * For PIO writes, ERR=1 DRQ=1 doesn't make + * sense since the data block has been + * transferred to the device. + */ + if (status & (ATA_ERR | ATA_DF)) { + qc->err_mask |= AC_ERR_DEV; + + if (!(qc->tf.flags & ATA_TFLAG_WRITE)) { + ata_pio_sectors(qc); + status = ata_wait_idle(ap); + } + + if (status & (ATA_BUSY | ATA_DRQ)) + qc->err_mask |= AC_ERR_HSM; + + /* ata_pio_sectors() might change the + * state to HSM_ST_LAST. so, the state + * is changed after ata_pio_sectors(). + */ + ap->hsm_task_state = HSM_ST_ERR; + goto fsm_start; + } + + ata_pio_sectors(qc); + if (ap->hsm_task_state == HSM_ST_LAST && + (!(qc->tf.flags & ATA_TFLAG_WRITE))) { + status = ata_wait_idle(ap); + goto fsm_start; + } + + poll_next = 1; + break; + + case HSM_ST_LAST: + if (!ata_ok(status)) { + qc->err_mask |= __ac_err_mask(status); + ap->hsm_task_state = HSM_ST_ERR; + goto fsm_start; + } + + ap->hsm_task_state = HSM_ST_IDLE; + + ata_hsm_qc_complete(qc, in_wq); + + poll_next = 0; + break; + + case HSM_ST_ERR: + /* make sure qc->err_mask is available to + * know what's wrong and recover + */ + ap->hsm_task_state = HSM_ST_IDLE; + + ata_hsm_qc_complete(qc, in_wq); + + poll_next = 0; + break; + default: + poll_next = 0; + } + + return poll_next; +} + +static void ata_pio_sectors(struct ata_queued_cmd *qc) +{ + struct ata_port *ap; + ap = pap; + qc->pdata = ap->pdata; + + ata_pio_sector(qc); + + readb(qc->ap->ioaddr.altstatus_addr); + udelay(1); +} + +static void ata_pio_sector(struct ata_queued_cmd *qc) +{ + int do_write = (qc->tf.flags & ATA_TFLAG_WRITE); + struct ata_port *ap = qc->ap; + unsigned int offset; + unsigned char *buf; + char temp_data_buf[512]; + + if (qc->curbytes == qc->nbytes - qc->sect_size) + ap->hsm_task_state = HSM_ST_LAST; + + offset = qc->curbytes; + + switch (qc->tf.command) { + case ATA_CMD_ID_ATA: + buf = (unsigned char *)&ata_device.id[0]; + break; + case ATA_CMD_PIO_READ_EXT: + case ATA_CMD_PIO_READ: + case ATA_CMD_PIO_WRITE_EXT: + case ATA_CMD_PIO_WRITE: + buf = qc->pdata + offset; + break; + default: + buf = (unsigned char *)&temp_data_buf[0]; + } + + ata_mmio_data_xfer(qc->dev, buf, qc->sect_size, do_write); + + qc->curbytes += qc->sect_size; + +} + +static void ata_mmio_data_xfer(struct ata_device *dev, unsigned char *buf, + unsigned int buflen, int do_write) +{ + struct ata_port *ap = pap; + void __iomem *data_addr = ap->ioaddr.data_addr; + unsigned int words = buflen >> 1; + u16 *buf16 = (u16 *)buf; + unsigned int i = 0; + + udelay(100); + if (do_write) { + for (i = 0; i < words; i++) + writew(le16_to_cpu(buf16[i]), data_addr); + } else { + for (i = 0; i < words; i++) + buf16[i] = cpu_to_le16(readw(data_addr)); + } + + if (buflen & 0x01) { + __le16 align_buf[1] = { 0 }; + unsigned char *trailing_buf = buf + buflen - 1; + + if (do_write) { + memcpy(align_buf, trailing_buf, 1); + writew(le16_to_cpu(align_buf[0]), data_addr); + } else { + align_buf[0] = cpu_to_le16(readw(data_addr)); + memcpy(trailing_buf, align_buf, 1); + } + } +} + +static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) +{ + struct ata_port *ap = qc->ap; + + if (in_wq) { + /* EH might have kicked in while host lock is + * released. + */ + qc = &ap->qcmd[qc->tag]; + if (qc) { + if (!(qc->err_mask & AC_ERR_HSM)) { + ata_irq_on(ap); + ata_qc_complete(qc); + } else { + ata_port_freeze(ap); + } + } + } else { + if (!(qc->err_mask & AC_ERR_HSM)) { + ata_qc_complete(qc); + } else { + ata_port_freeze(ap); + } + } +} + +static u8 ata_irq_on(struct ata_port *ap) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + u8 tmp; + + ap->ctl &= ~ATA_NIEN; + ap->last_ctl = ap->ctl; + + if (ioaddr->ctl_addr) + writeb(ap->ctl, ioaddr->ctl_addr); + + tmp = ata_wait_idle(ap); + + return tmp; +} + +static unsigned int ata_tag_internal(unsigned int tag) +{ + return tag == ATA_MAX_QUEUE - 1; +} + +static void ata_qc_complete(struct ata_queued_cmd *qc) +{ + struct ata_device *dev = qc->dev; + if (qc->err_mask) + qc->flags |= ATA_QCFLAG_FAILED; + + if (qc->flags & ATA_QCFLAG_FAILED) { + if (!ata_tag_internal(qc->tag)) { + fill_result_tf(qc); + return; + } + } + if (qc->flags & ATA_QCFLAG_RESULT_TF) + fill_result_tf(qc); + + /* Some commands need post-processing after successful + * completion. + */ + switch (qc->tf.command) { + case ATA_CMD_SET_FEATURES: + if (qc->tf.feature != SETFEATURES_WC_ON && + qc->tf.feature != SETFEATURES_WC_OFF) + break; + case ATA_CMD_INIT_DEV_PARAMS: + case ATA_CMD_SET_MULTI: + break; + + case ATA_CMD_SLEEP: + dev->flags |= ATA_DFLAG_SLEEPING; + break; + } + + __ata_qc_complete(qc); +} + +static void fill_result_tf(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + + qc->result_tf.flags = qc->tf.flags; + ata_tf_read(ap, &qc->result_tf); +} + +static void ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + + tf->command = ata_check_status(ap); + tf->feature = readb(ioaddr->error_addr); + tf->nsect = readb(ioaddr->nsect_addr); + tf->lbal = readb(ioaddr->lbal_addr); + tf->lbam = readb(ioaddr->lbam_addr); + tf->lbah = readb(ioaddr->lbah_addr); + tf->device = readb(ioaddr->device_addr); + + if (tf->flags & ATA_TFLAG_LBA48) { + if (ioaddr->ctl_addr) { + writeb(tf->ctl | ATA_HOB, ioaddr->ctl_addr); + + tf->hob_feature = readb(ioaddr->error_addr); + tf->hob_nsect = readb(ioaddr->nsect_addr); + tf->hob_lbal = readb(ioaddr->lbal_addr); + tf->hob_lbam = readb(ioaddr->lbam_addr); + tf->hob_lbah = readb(ioaddr->lbah_addr); + + writeb(tf->ctl, ioaddr->ctl_addr); + ap->last_ctl = tf->ctl; + } else { + printf("sata_dwc warnning register read.\n"); + } + } +} + +static void __ata_qc_complete(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_link *link = qc->dev->link; + + link->active_tag = ATA_TAG_POISON; + ap->nr_active_links--; + + if (qc->flags & ATA_QCFLAG_CLEAR_EXCL && ap->excl_link == link) + ap->excl_link = NULL; + + qc->flags &= ~ATA_QCFLAG_ACTIVE; + ap->qc_active &= ~(1 << qc->tag); +} + +static void ata_qc_free(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned int tag; + qc->flags = 0; + tag = qc->tag; + if (tag < ATA_MAX_QUEUE) { + qc->tag = ATA_TAG_POISON; + clear_bit(tag, &ap->qc_allocated); + } +} + +static int check_sata_dev_state(void) +{ + unsigned long datalen; + unsigned char *pdata; + int ret = 0; + int i = 0; + char temp_data_buf[512]; + + while (1) { + udelay(10000); + + pdata = (unsigned char*)&temp_data_buf[0]; + datalen = 512; + + ret = ata_dev_read_sectors(pdata, datalen, 0, 1); + + if (ret == true) + break; + + i++; + if (i > (ATA_RESET_TIME * 100)) { + printf("** TimeOUT **\n"); + dev_state = SATA_NODEVICE; + return false; + } + + if ((i >= 100) && ((i % 100) == 0)) + printf("."); + } + + dev_state = SATA_READY; + + return true; +} + +static unsigned int ata_dev_set_feature(struct ata_device *dev, + u8 enable, u8 feature) +{ + struct ata_taskfile tf; + struct ata_port *ap; + ap = pap; + unsigned int err_mask; + + memset(&tf, 0, sizeof(tf)); + tf.ctl = ap->ctl; + + tf.device = ATA_DEVICE_OBS; + tf.command = ATA_CMD_SET_FEATURES; + tf.feature = enable; + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.protocol = ATA_PROT_NODATA; + tf.nsect = feature; + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, 0, 0); + + return err_mask; +} + +static unsigned int ata_dev_init_params(struct ata_device *dev, + u16 heads, u16 sectors) +{ + struct ata_taskfile tf; + struct ata_port *ap; + ap = pap; + unsigned int err_mask; + + if (sectors < 1 || sectors > 255 || heads < 1 || heads > 16) + return AC_ERR_INVALID; + + memset(&tf, 0, sizeof(tf)); + tf.ctl = ap->ctl; + tf.device = ATA_DEVICE_OBS; + tf.command = ATA_CMD_INIT_DEV_PARAMS; + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.protocol = ATA_PROT_NODATA; + tf.nsect = sectors; + tf.device |= (heads - 1) & 0x0f; + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, 0, 0); + + if (err_mask == AC_ERR_DEV && (tf.feature & ATA_ABORTED)) + err_mask = 0; + + return err_mask; +} + +#if defined(CONFIG_SATA_DWC) && !defined(CONFIG_LBA48) +#define SATA_MAX_READ_BLK 0xFF +#else +#define SATA_MAX_READ_BLK 0xFFFF +#endif + +ulong sata_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer) +{ + ulong start,blks, buf_addr; + unsigned short smallblks; + unsigned long datalen; + unsigned char *pdata; + device &= 0xff; + + u32 block = 0; + u32 n_block = 0; + + if (dev_state != SATA_READY) + return 0; + + buf_addr = (unsigned long)buffer; + start = blknr; + blks = blkcnt; + do { + pdata = (unsigned char *)buf_addr; + if (blks > SATA_MAX_READ_BLK) { + datalen = sata_dev_desc[device].blksz * SATA_MAX_READ_BLK; + smallblks = SATA_MAX_READ_BLK; + + block = (u32)start; + n_block = (u32)smallblks; + + start += SATA_MAX_READ_BLK; + blks -= SATA_MAX_READ_BLK; + } else { + datalen = sata_dev_desc[device].blksz * SATA_MAX_READ_BLK; + datalen = sata_dev_desc[device].blksz * blks; + smallblks = (unsigned short)blks; + + block = (u32)start; + n_block = (u32)smallblks; + + start += blks; + blks = 0; + } + + if (ata_dev_read_sectors(pdata, datalen, block, n_block) != true) { + printf("sata_dwc : Hard disk read error.\n"); + blkcnt -= blks; + break; + } + buf_addr += datalen; + } while (blks != 0); + + return (blkcnt); +} + +static int ata_dev_read_sectors(unsigned char *pdata, unsigned long datalen, + u32 block, u32 n_block) +{ + struct ata_port *ap = pap; + struct ata_device *dev = &ata_device; + struct ata_taskfile tf; + unsigned int class = ATA_DEV_ATA; + unsigned int err_mask = 0; + const char *reason; + int may_fallback = 1; + + if (dev_state == SATA_ERROR) + return false; + + ata_dev_select(ap, dev->devno, 1, 1); + +retry: + memset(&tf, 0, sizeof(tf)); + tf.ctl = ap->ctl; + ap->print_id = 1; + ap->flags &= ~ATA_FLAG_DISABLED; + + ap->pdata = pdata; + + tf.device = ATA_DEVICE_OBS; + + temp_n_block = n_block; + +#ifdef CONFIG_LBA48 + tf.command = ATA_CMD_PIO_READ_EXT; + tf.flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48; + + tf.hob_feature = 31; + tf.feature = 31; + tf.hob_nsect = (n_block >> 8) & 0xff; + tf.nsect = n_block & 0xff; + + tf.hob_lbah = 0x0; + tf.hob_lbam = 0x0; + tf.hob_lbal = (block >> 24) & 0xff; + tf.lbah = (block >> 16) & 0xff; + tf.lbam = (block >> 8) & 0xff; + tf.lbal = block & 0xff; + + tf.device = 1 << 6; + if (tf.flags & ATA_TFLAG_FUA) + tf.device |= 1 << 7; +#else + tf.command = ATA_CMD_PIO_READ; + tf.flags |= ATA_TFLAG_LBA ; + + tf.feature = 31; + tf.nsect = n_block & 0xff; + + tf.lbah = (block >> 16) & 0xff; + tf.lbam = (block >> 8) & 0xff; + tf.lbal = block & 0xff; + + tf.device = (block >> 24) & 0xf; + + tf.device |= 1 << 6; + if (tf.flags & ATA_TFLAG_FUA) + tf.device |= 1 << 7; + +#endif + + tf.protocol = ATA_PROT_PIO; + + /* Some devices choke if TF registers contain garbage. Make + * sure those are properly initialized. + */ + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.flags |= ATA_TFLAG_POLLING; + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE, 0, 0); + + if (err_mask) { + if (err_mask & AC_ERR_NODEV_HINT) { + printf("READ_SECTORS NODEV after polling detection\n"); + return -ENOENT; + } + + if ((err_mask == AC_ERR_DEV) && (tf.feature & ATA_ABORTED)) { + /* Device or controller might have reported + * the wrong device class. Give a shot at the + * other IDENTIFY if the current one is + * aborted by the device. + */ + if (may_fallback) { + may_fallback = 0; + + if (class == ATA_DEV_ATA) { + class = ATA_DEV_ATAPI; + } else { + class = ATA_DEV_ATA; + } + goto retry; + } + /* Control reaches here iff the device aborted + * both flavors of IDENTIFYs which happens + * sometimes with phantom devices. + */ + printf("both IDENTIFYs aborted, assuming NODEV\n"); + return -ENOENT; + } + + reason = "I/O error"; + goto err_out; + } + + return true; + +err_out: + printf("failed to READ SECTORS (%s, err_mask=0x%x)\n", reason, err_mask); + return false; +} + +#if defined(CONFIG_SATA_DWC) && !defined(CONFIG_LBA48) +#define SATA_MAX_WRITE_BLK 0xFF +#else +#define SATA_MAX_WRITE_BLK 0xFFFF +#endif + +ulong sata_write(int device, ulong blknr, lbaint_t blkcnt, const void *buffer) +{ + ulong start,blks, buf_addr; + unsigned short smallblks; + unsigned long datalen; + unsigned char *pdata; + device &= 0xff; + + + u32 block = 0; + u32 n_block = 0; + + if (dev_state != SATA_READY) + return 0; + + buf_addr = (unsigned long)buffer; + start = blknr; + blks = blkcnt; + do { + pdata = (unsigned char *)buf_addr; + if (blks > SATA_MAX_WRITE_BLK) { + datalen = sata_dev_desc[device].blksz * SATA_MAX_WRITE_BLK; + smallblks = SATA_MAX_WRITE_BLK; + + block = (u32)start; + n_block = (u32)smallblks; + + start += SATA_MAX_WRITE_BLK; + blks -= SATA_MAX_WRITE_BLK; + } else { + datalen = sata_dev_desc[device].blksz * blks; + smallblks = (unsigned short)blks; + + block = (u32)start; + n_block = (u32)smallblks; + + start += blks; + blks = 0; + } + + if (ata_dev_write_sectors(pdata, datalen, block, n_block) != true) { + printf("sata_dwc : Hard disk read error.\n"); + blkcnt -= blks; + break; + } + buf_addr += datalen; + } while (blks != 0); + + return (blkcnt); +} + +static int ata_dev_write_sectors(unsigned char* pdata, unsigned long datalen, + u32 block, u32 n_block) +{ + struct ata_port *ap = pap; + struct ata_device *dev = &ata_device; + struct ata_taskfile tf; + unsigned int class = ATA_DEV_ATA; + unsigned int err_mask = 0; + const char *reason; + int may_fallback = 1; + + if (dev_state == SATA_ERROR) + return false; + + ata_dev_select(ap, dev->devno, 1, 1); + +retry: + memset(&tf, 0, sizeof(tf)); + tf.ctl = ap->ctl; + ap->print_id = 1; + ap->flags &= ~ATA_FLAG_DISABLED; + + ap->pdata = pdata; + + tf.device = ATA_DEVICE_OBS; + + temp_n_block = n_block; + + +#ifdef CONFIG_LBA48 + tf.command = ATA_CMD_PIO_WRITE_EXT; + tf.flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48 | ATA_TFLAG_WRITE; + + tf.hob_feature = 31; + tf.feature = 31; + tf.hob_nsect = (n_block >> 8) & 0xff; + tf.nsect = n_block & 0xff; + + tf.hob_lbah = 0x0; + tf.hob_lbam = 0x0; + tf.hob_lbal = (block >> 24) & 0xff; + tf.lbah = (block >> 16) & 0xff; + tf.lbam = (block >> 8) & 0xff; + tf.lbal = block & 0xff; + + tf.device = 1 << 6; + if (tf.flags & ATA_TFLAG_FUA) + tf.device |= 1 << 7; +#else + tf.command = ATA_CMD_PIO_WRITE; + tf.flags |= ATA_TFLAG_LBA | ATA_TFLAG_WRITE; + + tf.feature = 31; + tf.nsect = n_block & 0xff; + + tf.lbah = (block >> 16) & 0xff; + tf.lbam = (block >> 8) & 0xff; + tf.lbal = block & 0xff; + + tf.device = (block >> 24) & 0xf; + + tf.device |= 1 << 6; + if (tf.flags & ATA_TFLAG_FUA) + tf.device |= 1 << 7; + +#endif + + tf.protocol = ATA_PROT_PIO; + + /* Some devices choke if TF registers contain garbage. Make + * sure those are properly initialized. + */ + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.flags |= ATA_TFLAG_POLLING; + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE, 0, 0); + + if (err_mask) { + if (err_mask & AC_ERR_NODEV_HINT) { + printf("READ_SECTORS NODEV after polling detection\n"); + return -ENOENT; + } + + if ((err_mask == AC_ERR_DEV) && (tf.feature & ATA_ABORTED)) { + /* Device or controller might have reported + * the wrong device class. Give a shot at the + * other IDENTIFY if the current one is + * aborted by the device. + */ + if (may_fallback) { + may_fallback = 0; + + if (class == ATA_DEV_ATA) { + class = ATA_DEV_ATAPI; + } else { + class = ATA_DEV_ATA; + } + goto retry; + } + /* Control reaches here iff the device aborted + * both flavors of IDENTIFYs which happens + * sometimes with phantom devices. + */ + printf("both IDENTIFYs aborted, assuming NODEV\n"); + return -ENOENT; + } + + reason = "I/O error"; + goto err_out; + } + + return true; + +err_out: + printf("failed to WRITE SECTORS (%s, err_mask=0x%x)\n", reason, err_mask); + return false; +} diff --git a/qemu/roms/u-boot/drivers/block/sata_dwc.h b/qemu/roms/u-boot/drivers/block/sata_dwc.h new file mode 100644 index 000000000..e2d9e0c1f --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/sata_dwc.h @@ -0,0 +1,465 @@ +/* + * sata_dwc.h + * + * Synopsys DesignWare Cores (DWC) SATA host driver + * + * Author: Mark Miesfeld + * + * Ported from 2.6.19.2 to 2.6.25/26 by Stefan Roese + * Copyright 2008 DENX Software Engineering + * + * Based on versions provided by AMCC and Synopsys which are: + * Copyright 2006 Applied Micro Circuits Corporation + * COPYRIGHT (C) 2005 SYNOPSYS, INC. ALL RIGHTS RESERVED + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* + * SATA support based on the chip canyonlands. + * + * 04-17-2009 + * The local version of this driver for the canyonlands board + * does not use interrupts but polls the chip instead. + */ + + +#ifndef _SATA_DWC_H_ +#define _SATA_DWC_H_ + +#define __U_BOOT__ + +#define HZ 100 +#define READ 0 +#define WRITE 1 + +enum { + ATA_READID_POSTRESET = (1 << 0), + + ATA_DNXFER_PIO = 0, + ATA_DNXFER_DMA = 1, + ATA_DNXFER_40C = 2, + ATA_DNXFER_FORCE_PIO = 3, + ATA_DNXFER_FORCE_PIO0 = 4, + + ATA_DNXFER_QUIET = (1 << 31), +}; + +enum hsm_task_states { + HSM_ST_IDLE, + HSM_ST_FIRST, + HSM_ST, + HSM_ST_LAST, + HSM_ST_ERR, +}; + +#define ATA_SHORT_PAUSE ((HZ >> 6) + 1) + +struct ata_queued_cmd { + struct ata_port *ap; + struct ata_device *dev; + + struct ata_taskfile tf; + u8 cdb[ATAPI_CDB_LEN]; + unsigned long flags; + unsigned int tag; + unsigned int n_elem; + + int dma_dir; + unsigned int sect_size; + + unsigned int nbytes; + unsigned int extrabytes; + unsigned int curbytes; + + unsigned int err_mask; + struct ata_taskfile result_tf; + + void *private_data; +#ifndef __U_BOOT__ + void *lldd_task; +#endif + unsigned char *pdata; +}; + +typedef void (*ata_qc_cb_t) (struct ata_queued_cmd *qc); + +#define ATA_TAG_POISON 0xfafbfcfdU + +enum { + LIBATA_MAX_PRD = ATA_MAX_PRD / 2, + LIBATA_DUMB_MAX_PRD = ATA_MAX_PRD / 4, + ATA_MAX_PORTS = 8, + ATA_DEF_QUEUE = 1, + ATA_MAX_QUEUE = 32, + ATA_TAG_INTERNAL = ATA_MAX_QUEUE - 1, + ATA_MAX_BUS = 2, + ATA_DEF_BUSY_WAIT = 10000, + + ATAPI_MAX_DRAIN = 16 << 10, + + ATA_SHT_EMULATED = 1, + ATA_SHT_CMD_PER_LUN = 1, + ATA_SHT_THIS_ID = -1, + ATA_SHT_USE_CLUSTERING = 1, + + ATA_DFLAG_LBA = (1 << 0), + ATA_DFLAG_LBA48 = (1 << 1), + ATA_DFLAG_CDB_INTR = (1 << 2), + ATA_DFLAG_NCQ = (1 << 3), + ATA_DFLAG_FLUSH_EXT = (1 << 4), + ATA_DFLAG_ACPI_PENDING = (1 << 5), + ATA_DFLAG_ACPI_FAILED = (1 << 6), + ATA_DFLAG_AN = (1 << 7), + ATA_DFLAG_HIPM = (1 << 8), + ATA_DFLAG_DIPM = (1 << 9), + ATA_DFLAG_DMADIR = (1 << 10), + ATA_DFLAG_CFG_MASK = (1 << 12) - 1, + + ATA_DFLAG_PIO = (1 << 12), + ATA_DFLAG_NCQ_OFF = (1 << 13), + ATA_DFLAG_SPUNDOWN = (1 << 14), + ATA_DFLAG_SLEEPING = (1 << 15), + ATA_DFLAG_DUBIOUS_XFER = (1 << 16), + ATA_DFLAG_INIT_MASK = (1 << 24) - 1, + + ATA_DFLAG_DETACH = (1 << 24), + ATA_DFLAG_DETACHED = (1 << 25), + + ATA_LFLAG_HRST_TO_RESUME = (1 << 0), + ATA_LFLAG_SKIP_D2H_BSY = (1 << 1), + ATA_LFLAG_NO_SRST = (1 << 2), + ATA_LFLAG_ASSUME_ATA = (1 << 3), + ATA_LFLAG_ASSUME_SEMB = (1 << 4), + ATA_LFLAG_ASSUME_CLASS = ATA_LFLAG_ASSUME_ATA | ATA_LFLAG_ASSUME_SEMB, + ATA_LFLAG_NO_RETRY = (1 << 5), + ATA_LFLAG_DISABLED = (1 << 6), + + ATA_FLAG_SLAVE_POSS = (1 << 0), + ATA_FLAG_SATA = (1 << 1), + ATA_FLAG_NO_LEGACY = (1 << 2), + ATA_FLAG_MMIO = (1 << 3), + ATA_FLAG_SRST = (1 << 4), + ATA_FLAG_SATA_RESET = (1 << 5), + ATA_FLAG_NO_ATAPI = (1 << 6), + ATA_FLAG_PIO_DMA = (1 << 7), + ATA_FLAG_PIO_LBA48 = (1 << 8), + ATA_FLAG_PIO_POLLING = (1 << 9), + ATA_FLAG_NCQ = (1 << 10), + ATA_FLAG_DEBUGMSG = (1 << 13), + ATA_FLAG_IGN_SIMPLEX = (1 << 15), + ATA_FLAG_NO_IORDY = (1 << 16), + ATA_FLAG_ACPI_SATA = (1 << 17), + ATA_FLAG_AN = (1 << 18), + ATA_FLAG_PMP = (1 << 19), + ATA_FLAG_IPM = (1 << 20), + + ATA_FLAG_DISABLED = (1 << 23), + + ATA_PFLAG_EH_PENDING = (1 << 0), + ATA_PFLAG_EH_IN_PROGRESS = (1 << 1), + ATA_PFLAG_FROZEN = (1 << 2), + ATA_PFLAG_RECOVERED = (1 << 3), + ATA_PFLAG_LOADING = (1 << 4), + ATA_PFLAG_UNLOADING = (1 << 5), + ATA_PFLAG_SCSI_HOTPLUG = (1 << 6), + ATA_PFLAG_INITIALIZING = (1 << 7), + ATA_PFLAG_RESETTING = (1 << 8), + ATA_PFLAG_SUSPENDED = (1 << 17), + ATA_PFLAG_PM_PENDING = (1 << 18), + + ATA_QCFLAG_ACTIVE = (1 << 0), + ATA_QCFLAG_DMAMAP = (1 << 1), + ATA_QCFLAG_IO = (1 << 3), + ATA_QCFLAG_RESULT_TF = (1 << 4), + ATA_QCFLAG_CLEAR_EXCL = (1 << 5), + ATA_QCFLAG_QUIET = (1 << 6), + + ATA_QCFLAG_FAILED = (1 << 16), + ATA_QCFLAG_SENSE_VALID = (1 << 17), + ATA_QCFLAG_EH_SCHEDULED = (1 << 18), + + ATA_HOST_SIMPLEX = (1 << 0), + ATA_HOST_STARTED = (1 << 1), + + ATA_TMOUT_BOOT = 30 * 100, + ATA_TMOUT_BOOT_QUICK = 7 * 100, + ATA_TMOUT_INTERNAL = 30 * 100, + ATA_TMOUT_INTERNAL_QUICK = 5 * 100, + + /* FIXME: GoVault needs 2s but we can't afford that without + * parallel probing. 800ms is enough for iVDR disk + * HHD424020F7SV00. Increase to 2secs when parallel probing + * is in place. + */ + ATA_TMOUT_FF_WAIT = 4 * 100 / 5, + + BUS_UNKNOWN = 0, + BUS_DMA = 1, + BUS_IDLE = 2, + BUS_NOINTR = 3, + BUS_NODATA = 4, + BUS_TIMER = 5, + BUS_PIO = 6, + BUS_EDD = 7, + BUS_IDENTIFY = 8, + BUS_PACKET = 9, + + PORT_UNKNOWN = 0, + PORT_ENABLED = 1, + PORT_DISABLED = 2, + + /* encoding various smaller bitmaps into a single + * unsigned long bitmap + */ + ATA_NR_PIO_MODES = 7, + ATA_NR_MWDMA_MODES = 5, + ATA_NR_UDMA_MODES = 8, + + ATA_SHIFT_PIO = 0, + ATA_SHIFT_MWDMA = ATA_SHIFT_PIO + ATA_NR_PIO_MODES, + ATA_SHIFT_UDMA = ATA_SHIFT_MWDMA + ATA_NR_MWDMA_MODES, + + ATA_DMA_PAD_SZ = 4, + + ATA_ERING_SIZE = 32, + + ATA_DEFER_LINK = 1, + ATA_DEFER_PORT = 2, + + ATA_EH_DESC_LEN = 80, + + ATA_EH_REVALIDATE = (1 << 0), + ATA_EH_SOFTRESET = (1 << 1), + ATA_EH_HARDRESET = (1 << 2), + ATA_EH_ENABLE_LINK = (1 << 3), + ATA_EH_LPM = (1 << 4), + + ATA_EH_RESET_MASK = ATA_EH_SOFTRESET | ATA_EH_HARDRESET, + ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE, + + ATA_EHI_HOTPLUGGED = (1 << 0), + ATA_EHI_RESUME_LINK = (1 << 1), + ATA_EHI_NO_AUTOPSY = (1 << 2), + ATA_EHI_QUIET = (1 << 3), + + ATA_EHI_DID_SOFTRESET = (1 << 16), + ATA_EHI_DID_HARDRESET = (1 << 17), + ATA_EHI_PRINTINFO = (1 << 18), + ATA_EHI_SETMODE = (1 << 19), + ATA_EHI_POST_SETMODE = (1 << 20), + + ATA_EHI_DID_RESET = ATA_EHI_DID_SOFTRESET | ATA_EHI_DID_HARDRESET, + ATA_EHI_RESET_MODIFIER_MASK = ATA_EHI_RESUME_LINK, + + ATA_EH_MAX_TRIES = 5, + + ATA_PROBE_MAX_TRIES = 3, + ATA_EH_DEV_TRIES = 3, + ATA_EH_PMP_TRIES = 5, + ATA_EH_PMP_LINK_TRIES = 3, + + SATA_PMP_SCR_TIMEOUT = 250, + + /* Horkage types. May be set by libata or controller on drives + (some horkage may be drive/controller pair dependant */ + + ATA_HORKAGE_DIAGNOSTIC = (1 << 0), + ATA_HORKAGE_NODMA = (1 << 1), + ATA_HORKAGE_NONCQ = (1 << 2), + ATA_HORKAGE_MAX_SEC_128 = (1 << 3), + ATA_HORKAGE_BROKEN_HPA = (1 << 4), + ATA_HORKAGE_SKIP_PM = (1 << 5), + ATA_HORKAGE_HPA_SIZE = (1 << 6), + ATA_HORKAGE_IPM = (1 << 7), + ATA_HORKAGE_IVB = (1 << 8), + ATA_HORKAGE_STUCK_ERR = (1 << 9), + + ATA_DMA_MASK_ATA = (1 << 0), + ATA_DMA_MASK_ATAPI = (1 << 1), + ATA_DMA_MASK_CFA = (1 << 2), + + ATAPI_READ = 0, + ATAPI_WRITE = 1, + ATAPI_READ_CD = 2, + ATAPI_PASS_THRU = 3, + ATAPI_MISC = 4, +}; + +enum ata_completion_errors { + AC_ERR_DEV = (1 << 0), + AC_ERR_HSM = (1 << 1), + AC_ERR_TIMEOUT = (1 << 2), + AC_ERR_MEDIA = (1 << 3), + AC_ERR_ATA_BUS = (1 << 4), + AC_ERR_HOST_BUS = (1 << 5), + AC_ERR_SYSTEM = (1 << 6), + AC_ERR_INVALID = (1 << 7), + AC_ERR_OTHER = (1 << 8), + AC_ERR_NODEV_HINT = (1 << 9), + AC_ERR_NCQ = (1 << 10), +}; + +enum ata_xfer_mask { + ATA_MASK_PIO = ((1LU << ATA_NR_PIO_MODES) - 1) << ATA_SHIFT_PIO, + ATA_MASK_MWDMA = ((1LU << ATA_NR_MWDMA_MODES) - 1) << ATA_SHIFT_MWDMA, + ATA_MASK_UDMA = ((1LU << ATA_NR_UDMA_MODES) - 1) << ATA_SHIFT_UDMA, +}; + +struct ata_port_info { +#ifndef __U_BOOT__ + struct scsi_host_template *sht; +#endif + unsigned long flags; + unsigned long link_flags; + unsigned long pio_mask; + unsigned long mwdma_mask; + unsigned long udma_mask; +#ifndef __U_BOOT__ + const struct ata_port_operations *port_ops; + void *private_data; +#endif +}; + +struct ata_ioports { + void __iomem *cmd_addr; + void __iomem *data_addr; + void __iomem *error_addr; + void __iomem *feature_addr; + void __iomem *nsect_addr; + void __iomem *lbal_addr; + void __iomem *lbam_addr; + void __iomem *lbah_addr; + void __iomem *device_addr; + void __iomem *status_addr; + void __iomem *command_addr; + void __iomem *altstatus_addr; + void __iomem *ctl_addr; +#ifndef __U_BOOT__ + void __iomem *bmdma_addr; +#endif + void __iomem *scr_addr; +}; + +struct ata_host { +#ifndef __U_BOOT__ + void __iomem * const *iomap; + void *private_data; + const struct ata_port_operations *ops; + unsigned long flags; + struct ata_port *simplex_claimed; +#endif + unsigned int n_ports; + struct ata_port *ports[0]; +}; + +#ifndef __U_BOOT__ +struct ata_port_stats { + unsigned long unhandled_irq; + unsigned long idle_irq; + unsigned long rw_reqbuf; +}; +#endif + +struct ata_device { + struct ata_link *link; + unsigned int devno; + unsigned long flags; + unsigned int horkage; +#ifndef __U_BOOT__ + struct scsi_device *sdev; +#ifdef CONFIG_ATA_ACPI + acpi_handle acpi_handle; + union acpi_object *gtf_cache; +#endif +#endif + u64 n_sectors; + unsigned int class; + + union { + u16 id[ATA_ID_WORDS]; + u32 gscr[SATA_PMP_GSCR_DWORDS]; + }; +#ifndef __U_BOOT__ + u8 pio_mode; + u8 dma_mode; + u8 xfer_mode; + unsigned int xfer_shift; +#endif + unsigned int multi_count; + unsigned int max_sectors; + unsigned int cdb_len; +#ifndef __U_BOOT__ + unsigned long pio_mask; + unsigned long mwdma_mask; +#endif + unsigned long udma_mask; + u16 cylinders; + u16 heads; + u16 sectors; +#ifndef __U_BOOT__ + int spdn_cnt; +#endif +}; + +enum dma_data_direction { + DMA_BIDIRECTIONAL = 0, + DMA_TO_DEVICE = 1, + DMA_FROM_DEVICE = 2, + DMA_NONE = 3, +}; + +struct ata_link { + struct ata_port *ap; + int pmp; + unsigned int active_tag; + u32 sactive; + unsigned int flags; + unsigned int hw_sata_spd_limit; +#ifndef __U_BOOT__ + unsigned int sata_spd_limit; + unsigned int sata_spd; + struct ata_device device[2]; +#endif +}; + +struct ata_port { + unsigned long flags; + unsigned int pflags; + unsigned int print_id; + unsigned int port_no; + + struct ata_ioports ioaddr; + + u8 ctl; + u8 last_ctl; + unsigned int pio_mask; + unsigned int mwdma_mask; + unsigned int udma_mask; + unsigned int cbl; + + struct ata_queued_cmd qcmd[ATA_MAX_QUEUE]; + unsigned long qc_allocated; + unsigned int qc_active; + int nr_active_links; + + struct ata_link link; +#ifndef __U_BOOT__ + int nr_pmp_links; + struct ata_link *pmp_link; +#endif + struct ata_link *excl_link; + int nr_pmp_links; +#ifndef __U_BOOT__ + struct ata_port_stats stats; + struct device *dev; + u32 msg_enable; +#endif + struct ata_host *host; + void *port_task_data; + + unsigned int hsm_task_state; + void *private_data; + unsigned char *pdata; +}; + +#endif diff --git a/qemu/roms/u-boot/drivers/block/sata_sil.c b/qemu/roms/u-boot/drivers/block/sata_sil.c new file mode 100644 index 000000000..1f510cd26 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/sata_sil.c @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Author: Tang Yuantian + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sata_sil.h" + +/* Convert sectorsize to wordsize */ +#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2) +#define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v)) + +static struct sata_info sata_info; + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3131}, + {PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3132}, + {PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3124}, + {} +}; + +static void sil_sata_dump_fis(struct sata_fis_d2h *s) +{ + printf("Status FIS dump:\n"); + printf("fis_type: %02x\n", s->fis_type); + printf("pm_port_i: %02x\n", s->pm_port_i); + printf("status: %02x\n", s->status); + printf("error: %02x\n", s->error); + printf("lba_low: %02x\n", s->lba_low); + printf("lba_mid: %02x\n", s->lba_mid); + printf("lba_high: %02x\n", s->lba_high); + printf("device: %02x\n", s->device); + printf("lba_low_exp: %02x\n", s->lba_low_exp); + printf("lba_mid_exp: %02x\n", s->lba_mid_exp); + printf("lba_high_exp: %02x\n", s->lba_high_exp); + printf("res1: %02x\n", s->res1); + printf("sector_count: %02x\n", s->sector_count); + printf("sector_count_exp: %02x\n", s->sector_count_exp); +} + +static const char *sata_spd_string(unsigned int speed) +{ + static const char * const spd_str[] = { + "1.5 Gbps", + "3.0 Gbps", + "6.0 Gbps", + }; + + if ((speed - 1) > 2) + return ""; + + return spd_str[speed - 1]; +} + +static u32 ata_wait_register(void *reg, u32 mask, + u32 val, int timeout_msec) +{ + u32 tmp; + + tmp = readl(reg); + while ((tmp & mask) == val && timeout_msec > 0) { + mdelay(1); + timeout_msec--; + tmp = readl(reg); + } + + return tmp; +} + +static void sil_config_port(void *port) +{ + /* configure IRQ WoC */ + writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); + + /* zero error counters. */ + writew(0x8000, port + PORT_DECODE_ERR_THRESH); + writew(0x8000, port + PORT_CRC_ERR_THRESH); + writew(0x8000, port + PORT_HSHK_ERR_THRESH); + writew(0x0000, port + PORT_DECODE_ERR_CNT); + writew(0x0000, port + PORT_CRC_ERR_CNT); + writew(0x0000, port + PORT_HSHK_ERR_CNT); + + /* always use 64bit activation */ + writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); + + /* clear port multiplier enable and resume bits */ + writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR); +} + +static int sil_init_port(void *port) +{ + u32 tmp; + + writel(PORT_CS_INIT, port + PORT_CTRL_STAT); + ata_wait_register(port + PORT_CTRL_STAT, + PORT_CS_INIT, PORT_CS_INIT, 100); + tmp = ata_wait_register(port + PORT_CTRL_STAT, + PORT_CS_RDY, 0, 100); + + if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY) + return 1; + + return 0; +} + +static void sil_read_fis(int dev, int tag, struct sata_fis_d2h *fis) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + void *port = sata->port; + struct sil_prb *prb; + int i; + u32 *src, *dst; + + prb = port + PORT_LRAM + tag * PORT_LRAM_SLOT_SZ; + src = (u32 *)&prb->fis; + dst = (u32 *)fis; + for (i = 0; i < sizeof(struct sata_fis_h2d); i += 4) + *dst++ = readl(src++); +} + +static int sil_exec_cmd(int dev, struct sil_cmd_block *pcmd, int tag) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + void *port = sata->port; + u64 paddr = virt_to_bus(sata->devno, pcmd); + u32 irq_mask, irq_stat; + int rc; + + writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR, port + PORT_IRQ_ENABLE_CLR); + + /* better to add momery barrior here */ + writel((u32)paddr, port + PORT_CMD_ACTIVATE + tag * 8); + writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + tag * 8 + 4); + + irq_mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT; + irq_stat = ata_wait_register(port + PORT_IRQ_STAT, irq_mask, + 0, 10000); + + /* clear IRQs */ + writel(irq_mask, port + PORT_IRQ_STAT); + irq_stat >>= PORT_IRQ_RAW_SHIFT; + + if (irq_stat & PORT_IRQ_COMPLETE) + rc = 0; + else { + /* force port into known state */ + sil_init_port(port); + if (irq_stat & PORT_IRQ_ERROR) + rc = 1; /* error */ + else + rc = 2; /* busy */ + } + + return rc; +} + +static int sil_cmd_set_feature(int dev) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + struct sil_cmd_block cmdb, *pcmd = &cmdb; + struct sata_fis_d2h fis; + u8 udma_cap; + int ret; + + memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block)); + pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D; + pcmd->prb.fis.pm_port_c = (1 << 7); + pcmd->prb.fis.command = ATA_CMD_SET_FEATURES; + pcmd->prb.fis.features = SETFEATURES_XFER; + + /* First check the device capablity */ + udma_cap = (u8)(sata->udma & 0xff); + debug("udma_cap %02x\n", udma_cap); + + if (udma_cap == ATA_UDMA6) + pcmd->prb.fis.sector_count = XFER_UDMA_6; + if (udma_cap == ATA_UDMA5) + pcmd->prb.fis.sector_count = XFER_UDMA_5; + if (udma_cap == ATA_UDMA4) + pcmd->prb.fis.sector_count = XFER_UDMA_4; + if (udma_cap == ATA_UDMA3) + pcmd->prb.fis.sector_count = XFER_UDMA_3; + + ret = sil_exec_cmd(dev, pcmd, 0); + if (ret) { + sil_read_fis(dev, 0, &fis); + printf("Err: exe cmd(0x%x).\n", + readl(sata->port + PORT_SERROR)); + sil_sata_dump_fis(&fis); + return 1; + } + + return 0; +} + +static int sil_cmd_identify_device(int dev, u16 *id) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + struct sil_cmd_block cmdb, *pcmd = &cmdb; + struct sata_fis_d2h fis; + int ret; + + memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block)); + pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL); + pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ); + pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D; + pcmd->prb.fis.pm_port_c = (1 << 7); + pcmd->prb.fis.command = ATA_CMD_ID_ATA; + pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, id)); + pcmd->sge.cnt = cpu_to_le32(sizeof(id[0]) * ATA_ID_WORDS); + pcmd->sge.flags = cpu_to_le32(SGE_TRM); + + ret = sil_exec_cmd(dev, pcmd, 0); + if (ret) { + sil_read_fis(dev, 0, &fis); + printf("Err: id cmd(0x%x).\n", readl(sata->port + PORT_SERROR)); + sil_sata_dump_fis(&fis); + return 1; + } + ata_swap_buf_le16(id, ATA_ID_WORDS); + + return 0; +} + +static int sil_cmd_soft_reset(int dev) +{ + struct sil_cmd_block cmdb, *pcmd = &cmdb; + struct sil_sata *sata = sata_dev_desc[dev].priv; + struct sata_fis_d2h fis; + void *port = sata->port; + int ret; + + /* put the port into known state */ + if (sil_init_port(port)) { + printf("SRST: port %d not ready\n", dev); + return 1; + } + + memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block)); + + pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_SRST); + pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D; + pcmd->prb.fis.pm_port_c = 0xf; + + ret = sil_exec_cmd(dev, &cmdb, 0); + if (ret) { + sil_read_fis(dev, 0, &fis); + printf("SRST cmd error.\n"); + sil_sata_dump_fis(&fis); + return 1; + } + + return 0; +} + +static ulong sil_sata_rw_cmd(int dev, ulong start, ulong blkcnt, + u8 *buffer, int is_write) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + struct sil_cmd_block cmdb, *pcmd = &cmdb; + struct sata_fis_d2h fis; + u64 block; + int ret; + + block = (u64)start; + memset(pcmd, 0, sizeof(struct sil_cmd_block)); + pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL); + pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D; + pcmd->prb.fis.pm_port_c = (1 << 7); + if (is_write) { + pcmd->prb.fis.command = ATA_CMD_WRITE; + pcmd->prb.prot = cpu_to_le16(PRB_PROT_WRITE); + } else { + pcmd->prb.fis.command = ATA_CMD_READ; + pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ); + } + + pcmd->prb.fis.device = ATA_LBA; + pcmd->prb.fis.device |= (block >> 24) & 0xf; + pcmd->prb.fis.lba_high = (block >> 16) & 0xff; + pcmd->prb.fis.lba_mid = (block >> 8) & 0xff; + pcmd->prb.fis.lba_low = block & 0xff; + pcmd->prb.fis.sector_count = (u8)blkcnt & 0xff; + + pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, buffer)); + pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE); + pcmd->sge.flags = cpu_to_le32(SGE_TRM); + + ret = sil_exec_cmd(dev, pcmd, 0); + if (ret) { + sil_read_fis(dev, 0, &fis); + printf("Err: rw cmd(0x%08x).\n", + readl(sata->port + PORT_SERROR)); + sil_sata_dump_fis(&fis); + return 1; + } + + return blkcnt; +} + +static ulong sil_sata_rw_cmd_ext(int dev, ulong start, ulong blkcnt, + u8 *buffer, int is_write) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + struct sil_cmd_block cmdb, *pcmd = &cmdb; + struct sata_fis_d2h fis; + u64 block; + int ret; + + block = (u64)start; + memset(pcmd, 0, sizeof(struct sil_cmd_block)); + pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL); + pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D; + pcmd->prb.fis.pm_port_c = (1 << 7); + if (is_write) { + pcmd->prb.fis.command = ATA_CMD_WRITE_EXT; + pcmd->prb.prot = cpu_to_le16(PRB_PROT_WRITE); + } else { + pcmd->prb.fis.command = ATA_CMD_READ_EXT; + pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ); + } + + pcmd->prb.fis.lba_high_exp = (block >> 40) & 0xff; + pcmd->prb.fis.lba_mid_exp = (block >> 32) & 0xff; + pcmd->prb.fis.lba_low_exp = (block >> 24) & 0xff; + pcmd->prb.fis.lba_high = (block >> 16) & 0xff; + pcmd->prb.fis.lba_mid = (block >> 8) & 0xff; + pcmd->prb.fis.lba_low = block & 0xff; + pcmd->prb.fis.device = ATA_LBA; + pcmd->prb.fis.sector_count_exp = (blkcnt >> 8) & 0xff; + pcmd->prb.fis.sector_count = blkcnt & 0xff; + + pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, buffer)); + pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE); + pcmd->sge.flags = cpu_to_le32(SGE_TRM); + + ret = sil_exec_cmd(dev, pcmd, 0); + if (ret) { + sil_read_fis(dev, 0, &fis); + printf("Err: rw ext cmd(0x%08x).\n", + readl(sata->port + PORT_SERROR)); + sil_sata_dump_fis(&fis); + return 1; + } + + return blkcnt; +} + +static ulong sil_sata_rw_lba28(int dev, ulong blknr, lbaint_t blkcnt, + const void *buffer, int is_write) +{ + ulong start, blks, max_blks; + u8 *addr; + + start = blknr; + blks = blkcnt; + addr = (u8 *)buffer; + + max_blks = ATA_MAX_SECTORS; + do { + if (blks > max_blks) { + sil_sata_rw_cmd(dev, start, max_blks, addr, is_write); + start += max_blks; + blks -= max_blks; + addr += ATA_SECT_SIZE * max_blks; + } else { + sil_sata_rw_cmd(dev, start, blks, addr, is_write); + start += blks; + blks = 0; + addr += ATA_SECT_SIZE * blks; + } + } while (blks != 0); + + return blkcnt; +} + +static ulong sil_sata_rw_lba48(int dev, ulong blknr, lbaint_t blkcnt, + const void *buffer, int is_write) +{ + ulong start, blks, max_blks; + u8 *addr; + + start = blknr; + blks = blkcnt; + addr = (u8 *)buffer; + + max_blks = ATA_MAX_SECTORS_LBA48; + do { + if (blks > max_blks) { + sil_sata_rw_cmd_ext(dev, start, max_blks, + addr, is_write); + start += max_blks; + blks -= max_blks; + addr += ATA_SECT_SIZE * max_blks; + } else { + sil_sata_rw_cmd_ext(dev, start, blks, + addr, is_write); + start += blks; + blks = 0; + addr += ATA_SECT_SIZE * blks; + } + } while (blks != 0); + + return blkcnt; +} + +static void sil_sata_cmd_flush_cache(int dev) +{ + struct sil_cmd_block cmdb, *pcmd = &cmdb; + + memset((void *)pcmd, 0, sizeof(struct sil_cmd_block)); + pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D; + pcmd->prb.fis.pm_port_c = (1 << 7); + pcmd->prb.fis.command = ATA_CMD_FLUSH; + + sil_exec_cmd(dev, pcmd, 0); +} + +static void sil_sata_cmd_flush_cache_ext(int dev) +{ + struct sil_cmd_block cmdb, *pcmd = &cmdb; + + memset((void *)pcmd, 0, sizeof(struct sil_cmd_block)); + pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D; + pcmd->prb.fis.pm_port_c = (1 << 7); + pcmd->prb.fis.command = ATA_CMD_FLUSH_EXT; + + sil_exec_cmd(dev, pcmd, 0); +} + +static void sil_sata_init_wcache(int dev, u16 *id) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + + if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id)) + sata->wcache = 1; + if (ata_id_has_flush(id)) + sata->flush = 1; + if (ata_id_has_flush_ext(id)) + sata->flush_ext = 1; +} + +static int sil_sata_get_wcache(int dev) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + + return sata->wcache; +} + +static int sil_sata_get_flush(int dev) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + + return sata->flush; +} + +static int sil_sata_get_flush_ext(int dev) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + + return sata->flush_ext; +} + +/* + * SATA interface between low level driver and command layer + */ +ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + ulong rc; + + if (sata->lba48) + rc = sil_sata_rw_lba48(dev, blknr, blkcnt, buffer, READ_CMD); + else + rc = sil_sata_rw_lba28(dev, blknr, blkcnt, buffer, READ_CMD); + + return rc; +} + +/* + * SATA interface between low level driver and command layer + */ +ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer) +{ + struct sil_sata *sata = sata_dev_desc[dev].priv; + ulong rc; + + if (sata->lba48) { + rc = sil_sata_rw_lba48(dev, blknr, blkcnt, buffer, WRITE_CMD); + if (sil_sata_get_wcache(dev) && sil_sata_get_flush_ext(dev)) + sil_sata_cmd_flush_cache_ext(dev); + } else { + rc = sil_sata_rw_lba28(dev, blknr, blkcnt, buffer, WRITE_CMD); + if (sil_sata_get_wcache(dev) && sil_sata_get_flush(dev)) + sil_sata_cmd_flush_cache(dev); + } + + return rc; +} + +/* + * SATA interface between low level driver and command layer + */ +int init_sata(int dev) +{ + static int init_done, idx; + pci_dev_t devno; + u16 word; + + if (init_done == 1 && dev < sata_info.maxport) + return 1; + + init_done = 1; + + /* Find PCI device(s) */ + devno = pci_find_devices(supported, idx++); + if (devno == -1) + return 1; + + pci_read_config_word(devno, PCI_DEVICE_ID, &word); + + /* get the port count */ + word &= 0xf; + + sata_info.portbase = sata_info.maxport; + sata_info.maxport = sata_info.portbase + word; + sata_info.devno = devno; + + /* Read out all BARs */ + sata_info.iobase[0] = (ulong)pci_map_bar(devno, + PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + sata_info.iobase[1] = (ulong)pci_map_bar(devno, + PCI_BASE_ADDRESS_2, PCI_REGION_MEM); + sata_info.iobase[2] = (ulong)pci_map_bar(devno, + PCI_BASE_ADDRESS_4, PCI_REGION_MEM); + + /* mask out the unused bits */ + sata_info.iobase[0] &= 0xffffff80; + sata_info.iobase[1] &= 0xfffffc00; + sata_info.iobase[2] &= 0xffffff80; + + /* Enable Bus Mastering and memory region */ + pci_write_config_word(devno, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Check if mem accesses and Bus Mastering are enabled. */ + pci_read_config_word(devno, PCI_COMMAND, &word); + if (!(word & PCI_COMMAND_MEMORY) || + (!(word & PCI_COMMAND_MASTER))) { + printf("Error: Can not enable MEM access or Bus Mastering.\n"); + debug("PCI command: %04x\n", word); + return 1; + } + + /* GPIO off */ + writel(0, (void *)(sata_info.iobase[0] + HOST_FLASH_CMD)); + /* clear global reset & mask interrupts during initialization */ + writel(0, (void *)(sata_info.iobase[0] + HOST_CTRL)); + + return 0; +} + +/* + * SATA interface between low level driver and command layer + */ +int scan_sata(int dev) +{ + unsigned char serial[ATA_ID_SERNO_LEN + 1]; + unsigned char firmware[ATA_ID_FW_REV_LEN + 1]; + unsigned char product[ATA_ID_PROD_LEN + 1]; + struct sil_sata *sata; + void *port; + int cnt; + u16 *id; + u32 tmp; + + if (dev >= sata_info.maxport) { + printf("SATA#%d is not present\n", dev); + return 1; + } + + printf("SATA#%d\n", dev); + port = (void *)sata_info.iobase[1] + + PORT_REGS_SIZE * (dev - sata_info.portbase); + + /* Initial PHY setting */ + writel(0x20c, port + PORT_PHY_CFG); + + /* clear port RST */ + tmp = readl(port + PORT_CTRL_STAT); + if (tmp & PORT_CS_PORT_RST) { + writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR); + tmp = ata_wait_register(port + PORT_CTRL_STAT, + PORT_CS_PORT_RST, PORT_CS_PORT_RST, 100); + if (tmp & PORT_CS_PORT_RST) + printf("Err: Failed to clear port RST\n"); + } + + /* Check if device is present */ + for (cnt = 0; cnt < 100; cnt++) { + tmp = readl(port + PORT_SSTATUS); + if ((tmp & 0xF) == 0x3) + break; + mdelay(1); + } + + tmp = readl(port + PORT_SSTATUS); + if ((tmp & 0xf) != 0x3) { + printf(" (No RDY)\n"); + return 1; + } + + /* Wait for port ready */ + tmp = ata_wait_register(port + PORT_CTRL_STAT, + PORT_CS_RDY, PORT_CS_RDY, 100); + if ((tmp & PORT_CS_RDY) != PORT_CS_RDY) { + printf("%d port not ready.\n", dev); + return 1; + } + + /* configure port */ + sil_config_port(port); + + /* Reset port */ + writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT); + readl(port + PORT_CTRL_STAT); + tmp = ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_DEV_RST, + PORT_CS_DEV_RST, 100); + if (tmp & PORT_CS_DEV_RST) { + printf("%d port reset failed.\n", dev); + return 1; + } + + sata = (struct sil_sata *)malloc(sizeof(struct sil_sata)); + if (!sata) { + printf("%d no memory.\n", dev); + return 1; + } + memset((void *)sata, 0, sizeof(struct sil_sata)); + + /* turn on port interrupt */ + tmp = readl((void *)(sata_info.iobase[0] + HOST_CTRL)); + tmp |= (1 << (dev - sata_info.portbase)); + writel(tmp, (void *)(sata_info.iobase[0] + HOST_CTRL)); + + /* Save the private struct to block device struct */ + sata_dev_desc[dev].priv = (void *)sata; + sata->port = port; + sata->devno = sata_info.devno; + sprintf(sata->name, "SATA#%d", dev); + sil_cmd_soft_reset(dev); + tmp = readl(port + PORT_SSTATUS); + tmp = (tmp >> 4) & 0xf; + printf(" (%s)\n", sata_spd_string(tmp)); + + id = (u16 *)malloc(ATA_ID_WORDS * 2); + if (!id) { + printf("Id malloc failed\n"); + free((void *)sata); + return 1; + } + sil_cmd_identify_device(dev, id); + +#ifdef CONFIG_LBA48 + /* Check if support LBA48 */ + if (ata_id_has_lba48(id)) { + sata_dev_desc[dev].lba48 = 1; + sata->lba48 = 1; + debug("Device supports LBA48\n"); + } else + debug("Device supports LBA28\n"); +#endif + + /* Serial number */ + ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial)); + memcpy(sata_dev_desc[dev].product, serial, sizeof(serial)); + + /* Firmware version */ + ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware)); + memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware)); + + /* Product model */ + ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product)); + memcpy(sata_dev_desc[dev].vendor, product, sizeof(product)); + + /* Totoal sectors */ + sata_dev_desc[dev].lba = ata_id_n_sectors(id); + + sil_sata_init_wcache(dev, id); + sil_cmd_set_feature(dev); + +#ifdef DEBUG + sil_cmd_identify_device(dev, id); + ata_dump_id(id); +#endif + free((void *)id); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/block/sata_sil.h b/qemu/roms/u-boot/drivers/block/sata_sil.h new file mode 100644 index 000000000..55954efdd --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/sata_sil.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Author: Tang Yuantian + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef SATA_SIL3132_H +#define SATA_SIL3132_H + +#define READ_CMD 0 +#define WRITE_CMD 1 + +/* + * SATA device driver struct for each dev + */ +struct sil_sata { + char name[12]; + void *port; /* the port base address */ + int lba48; + u16 pio; + u16 mwdma; + u16 udma; + pci_dev_t devno; + int wcache; + int flush; + int flush_ext; +}; + +/* sata info for each controller */ +struct sata_info { + ulong iobase[3]; + pci_dev_t devno; + int portbase; + int maxport; +}; + +/* + * Scatter gather entry (SGE),MUST 8 bytes aligned + */ +struct sil_sge { + __le64 addr; + __le32 cnt; + __le32 flags; +} __attribute__ ((aligned(8), packed)); + +/* + * Port request block, MUST 8 bytes aligned + */ +struct sil_prb { + __le16 ctrl; + __le16 prot; + __le32 rx_cnt; + struct sata_fis_h2d fis; +} __attribute__ ((aligned(8), packed)); + +struct sil_cmd_block { + struct sil_prb prb; + struct sil_sge sge; +}; + +enum { + HOST_SLOT_STAT = 0x00, /* 32 bit slot stat * 4 */ + HOST_CTRL = 0x40, + HOST_IRQ_STAT = 0x44, + HOST_PHY_CFG = 0x48, + HOST_BIST_CTRL = 0x50, + HOST_BIST_PTRN = 0x54, + HOST_BIST_STAT = 0x58, + HOST_MEM_BIST_STAT = 0x5c, + HOST_FLASH_CMD = 0x70, + /* 8 bit regs */ + HOST_FLASH_DATA = 0x74, + HOST_TRANSITION_DETECT = 0x75, + HOST_GPIO_CTRL = 0x76, + HOST_I2C_ADDR = 0x78, /* 32 bit */ + HOST_I2C_DATA = 0x7c, + HOST_I2C_XFER_CNT = 0x7e, + HOST_I2C_CTRL = 0x7f, + + /* HOST_SLOT_STAT bits */ + HOST_SSTAT_ATTN = (1 << 31), + + /* HOST_CTRL bits */ + HOST_CTRL_M66EN = (1 << 16), /* M66EN PCI bus signal */ + HOST_CTRL_TRDY = (1 << 17), /* latched PCI TRDY */ + HOST_CTRL_STOP = (1 << 18), /* latched PCI STOP */ + HOST_CTRL_DEVSEL = (1 << 19), /* latched PCI DEVSEL */ + HOST_CTRL_REQ64 = (1 << 20), /* latched PCI REQ64 */ + HOST_CTRL_GLOBAL_RST = (1 << 31), /* global reset */ + + /* + * Port registers + * (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2) + */ + PORT_REGS_SIZE = 0x2000, + + PORT_LRAM = 0x0000, /* 31 LRAM slots and PMP regs */ + PORT_LRAM_SLOT_SZ = 0x0080, /* 32 bytes PRB + 2 SGE, ACT... */ + + PORT_PMP = 0x0f80, /* 8 bytes PMP * 16 (128 bytes) */ + PORT_PMP_STATUS = 0x0000, /* port device status offset */ + PORT_PMP_QACTIVE = 0x0004, /* port device QActive offset */ + PORT_PMP_SIZE = 0x0008, /* 8 bytes per PMP */ + + /* 32 bit regs */ + PORT_CTRL_STAT = 0x1000, /* write: ctrl-set, read: stat */ + PORT_CTRL_CLR = 0x1004, /* write: ctrl-clear */ + PORT_IRQ_STAT = 0x1008, /* high: status, low: interrupt */ + PORT_IRQ_ENABLE_SET = 0x1010, /* write: enable-set */ + PORT_IRQ_ENABLE_CLR = 0x1014, /* write: enable-clear */ + PORT_ACTIVATE_UPPER_ADDR = 0x101c, + PORT_EXEC_FIFO = 0x1020, /* command execution fifo */ + PORT_CMD_ERR = 0x1024, /* command error number */ + PORT_FIS_CFG = 0x1028, + PORT_FIFO_THRES = 0x102c, + + /* 16 bit regs */ + PORT_DECODE_ERR_CNT = 0x1040, + PORT_DECODE_ERR_THRESH = 0x1042, + PORT_CRC_ERR_CNT = 0x1044, + PORT_CRC_ERR_THRESH = 0x1046, + PORT_HSHK_ERR_CNT = 0x1048, + PORT_HSHK_ERR_THRESH = 0x104a, + + /* 32 bit regs */ + PORT_PHY_CFG = 0x1050, + PORT_SLOT_STAT = 0x1800, + PORT_CMD_ACTIVATE = 0x1c00, /* 64 bit cmd activate * 31 */ + PORT_CONTEXT = 0x1e04, + PORT_EXEC_DIAG = 0x1e00, /* 32bit exec diag * 16 */ + PORT_PSD_DIAG = 0x1e40, /* 32bit psd diag * 16 */ + PORT_SCONTROL = 0x1f00, + PORT_SSTATUS = 0x1f04, + PORT_SERROR = 0x1f08, + PORT_SACTIVE = 0x1f0c, + + /* PORT_CTRL_STAT bits */ + PORT_CS_PORT_RST = (1 << 0), /* port reset */ + PORT_CS_DEV_RST = (1 << 1), /* device reset */ + PORT_CS_INIT = (1 << 2), /* port initialize */ + PORT_CS_IRQ_WOC = (1 << 3), /* interrupt write one to clear */ + PORT_CS_CDB16 = (1 << 5), /* 0=12b cdb, 1=16b cdb */ + PORT_CS_PMP_RESUME = (1 << 6), /* PMP resume */ + PORT_CS_32BIT_ACTV = (1 << 10), /* 32-bit activation */ + PORT_CS_PMP_EN = (1 << 13), /* port multiplier enable */ + PORT_CS_RDY = (1 << 31), /* port ready to accept commands */ + + /* PORT_IRQ_STAT/ENABLE_SET/CLR */ + /* bits[11:0] are masked */ + PORT_IRQ_COMPLETE = (1 << 0), /* command(s) completed */ + PORT_IRQ_ERROR = (1 << 1), /* command execution error */ + PORT_IRQ_PORTRDY_CHG = (1 << 2), /* port ready change */ + PORT_IRQ_PWR_CHG = (1 << 3), /* power management change */ + PORT_IRQ_PHYRDY_CHG = (1 << 4), /* PHY ready change */ + PORT_IRQ_COMWAKE = (1 << 5), /* COMWAKE received */ + PORT_IRQ_UNK_FIS = (1 << 6), /* unknown FIS received */ + PORT_IRQ_DEV_XCHG = (1 << 7), /* device exchanged */ + PORT_IRQ_8B10B = (1 << 8), /* 8b/10b decode error threshold */ + PORT_IRQ_CRC = (1 << 9), /* CRC error threshold */ + PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */ + PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */ + + DEF_PORT_IRQ = PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | + PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG | + PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_NOTIFY, + + /* bits[27:16] are unmasked (raw) */ + PORT_IRQ_RAW_SHIFT = 16, + PORT_IRQ_MASKED_MASK = 0x7ff, + PORT_IRQ_RAW_MASK = (0x7ff << PORT_IRQ_RAW_SHIFT), + + /* ENABLE_SET/CLR specific, intr steering - 2 bit field */ + PORT_IRQ_STEER_SHIFT = 30, + PORT_IRQ_STEER_MASK = (3 << PORT_IRQ_STEER_SHIFT), + + /* PORT_CMD_ERR constants */ + PORT_CERR_DEV = 1, /* Error bit in D2H Register FIS */ + PORT_CERR_SDB = 2, /* Error bit in SDB FIS */ + PORT_CERR_DATA = 3, /* Error in data FIS not detected by dev */ + PORT_CERR_SEND = 4, /* Initial cmd FIS transmission failure */ + PORT_CERR_INCONSISTENT = 5, /* Protocol mismatch */ + PORT_CERR_DIRECTION = 6, /* Data direction mismatch */ + PORT_CERR_UNDERRUN = 7, /* Ran out of SGEs while writing */ + PORT_CERR_OVERRUN = 8, /* Ran out of SGEs while reading */ + + /* bits of PRB control field */ + PRB_CTRL_PROTOCOL = (1 << 0), /* override def. ATA protocol */ + PRB_CTRL_PACKET_READ = (1 << 4), /* PACKET cmd read */ + PRB_CTRL_PACKET_WRITE = (1 << 5), /* PACKET cmd write */ + PRB_CTRL_NIEN = (1 << 6), /* Mask completion irq */ + PRB_CTRL_SRST = (1 << 7), /* Soft reset request (ign BSY?) */ + + /* PRB protocol field */ + PRB_PROT_PACKET = (1 << 0), + PRB_PROT_TCQ = (1 << 1), + PRB_PROT_NCQ = (1 << 2), + PRB_PROT_READ = (1 << 3), + PRB_PROT_WRITE = (1 << 4), + PRB_PROT_TRANSPARENT = (1 << 5), + + /* + * Other constants + */ + SGE_TRM = (1 << 31), /* Last SGE in chain */ + SGE_LNK = (1 << 30), /* linked list + Points to SGT, not SGE */ + SGE_DRD = (1 << 29), /* discard data read (/dev/null) + data address ignored */ + + CMD_ERR = 0x21, +}; + +#endif diff --git a/qemu/roms/u-boot/drivers/block/sata_sil3114.c b/qemu/roms/u-boot/drivers/block/sata_sil3114.c new file mode 100644 index 000000000..3aa6fc983 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/sata_sil3114.c @@ -0,0 +1,830 @@ +/* + * Copyright (C) Excito Elektronik i Skåne AB, All rights reserved. + * Author: Tor Krill + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This is a driver for Silicon Image sil3114 sata chip modelled on + * the ata_piix driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sata_sil3114.h" + +/* Convert sectorsize to wordsize */ +#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2) + +/* Forwards */ +u8 sil3114_spin_up (int num); +u8 sil3114_spin_down (int num); +static int sata_bus_softreset (int num); +static void sata_identify (int num, int dev); +static u8 check_power_mode (int num); +static void sata_port (struct sata_ioports *ioport); +static void set_Feature_cmd (int num, int dev); +static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits, + unsigned int max, u8 usealtstatus); +static u8 sata_chk_status (struct sata_ioports *ioaddr, u8 usealtstatus); +static void msleep (int count); + +static u32 iobase[6] = { 0, 0, 0, 0, 0, 0}; /* PCI BAR registers for device */ + +static struct sata_port port[CONFIG_SYS_SATA_MAX_DEVICE]; + +static void output_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words) +{ + while (words--) { + __raw_writew (*sect_buf++, (void *)ioaddr->data_addr); + } +} + +static int input_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words) +{ + while (words--) { + *sect_buf++ = __raw_readw ((void *)ioaddr->data_addr); + } + return 0; +} + +static int sata_bus_softreset (int num) +{ + u8 status = 0; + + port[num].dev_mask = 1; + + port[num].ctl_reg = 0x08; /*Default value of control reg */ + writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr); + udelay (10); + writeb (port[num].ctl_reg | ATA_SRST, port[num].ioaddr.ctl_addr); + udelay (10); + writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr); + + /* spec mandates ">= 2ms" before checking status. + * We wait 150ms, because that was the magic delay used for + * ATAPI devices in Hale Landis's ATADRVR, for the period of time + * between when the ATA command register is written, and then + * status is checked. Because waiting for "a while" before + * checking status is fine, post SRST, we perform this magic + * delay here as well. + */ + msleep (150); + status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 300, 0); + while ((status & ATA_BUSY)) { + msleep (100); + status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 3, 0); + } + + if (status & ATA_BUSY) { + printf ("ata%u is slow to respond,plz be patient\n", num); + } + + while ((status & ATA_BUSY)) { + msleep (100); + status = sata_chk_status (&port[num].ioaddr, 0); + } + + if (status & ATA_BUSY) { + printf ("ata%u failed to respond : ", num); + printf ("bus reset failed\n"); + port[num].dev_mask = 0; + return 1; + } + return 0; +} + +static void sata_identify (int num, int dev) +{ + u8 cmd = 0, status = 0, devno = num; + u16 iobuf[ATA_SECTOR_WORDS]; + u64 n_sectors = 0; + + memset (iobuf, 0, sizeof (iobuf)); + + if (!(port[num].dev_mask & 0x01)) { + printf ("dev%d is not present on port#%d\n", dev, num); + return; + } + + debug ("port=%d dev=%d\n", num, dev); + + status = 0; + cmd = ATA_CMD_ID_ATA; /*Device Identify Command */ + writeb (cmd, port[num].ioaddr.command_addr); + readb (port[num].ioaddr.altstatus_addr); + udelay (10); + + status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 1000, 0); + if (status & ATA_ERR) { + printf ("\ndevice not responding\n"); + port[num].dev_mask &= ~0x01; + return; + } + + input_data (&port[num].ioaddr, iobuf, ATA_SECTOR_WORDS); + + ata_swap_buf_le16 (iobuf, ATA_SECTOR_WORDS); + + debug ("Specific config: %x\n", iobuf[2]); + + /* we require LBA and DMA support (bits 8 & 9 of word 49) */ + if (!ata_id_has_dma (iobuf) || !ata_id_has_lba (iobuf)) { + debug ("ata%u: no dma/lba\n", num); + } +#ifdef DEBUG + ata_dump_id (iobuf); +#endif + n_sectors = ata_id_n_sectors (iobuf); + + if (n_sectors == 0) { + port[num].dev_mask &= ~0x01; + return; + } + ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].revision, + ATA_ID_FW_REV, sizeof (sata_dev_desc[devno].revision)); + ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].vendor, + ATA_ID_PROD, sizeof (sata_dev_desc[devno].vendor)); + ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].product, + ATA_ID_SERNO, sizeof (sata_dev_desc[devno].product)); + + /* TODO - atm we asume harddisk ie not removable */ + sata_dev_desc[devno].removable = 0; + + sata_dev_desc[devno].lba = (u32) n_sectors; + debug("lba=0x%lx\n", sata_dev_desc[devno].lba); + +#ifdef CONFIG_LBA48 + if (iobuf[83] & (1 << 10)) { + sata_dev_desc[devno].lba48 = 1; + } else { + sata_dev_desc[devno].lba48 = 0; + } +#endif + + /* assuming HD */ + sata_dev_desc[devno].type = DEV_TYPE_HARDDISK; + sata_dev_desc[devno].blksz = ATA_SECT_SIZE; + sata_dev_desc[devno].lun = 0; /* just to fill something in... */ +} + +static void set_Feature_cmd (int num, int dev) +{ + u8 status = 0; + + if (!(port[num].dev_mask & 0x01)) { + debug ("dev%d is not present on port#%d\n", dev, num); + return; + } + + writeb (SETFEATURES_XFER, port[num].ioaddr.feature_addr); + writeb (XFER_PIO_4, port[num].ioaddr.nsect_addr); + writeb (0, port[num].ioaddr.lbal_addr); + writeb (0, port[num].ioaddr.lbam_addr); + writeb (0, port[num].ioaddr.lbah_addr); + + writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr); + writeb (ATA_CMD_SET_FEATURES, port[num].ioaddr.command_addr); + + udelay (50); + msleep (150); + + status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0); + if ((status & (ATA_BUSY | ATA_ERR))) { + printf ("Error : status 0x%02x\n", status); + port[num].dev_mask &= ~0x01; + } +} + +u8 sil3114_spin_down (int num) +{ + u8 status = 0; + + debug ("Spin down disk\n"); + + if (!(port[num].dev_mask & 0x01)) { + debug ("Device ata%d is not present\n", num); + return 1; + } + + if ((status = check_power_mode (num)) == 0x00) { + debug ("Already in standby\n"); + return 0; + } + + if (status == 0x01) { + printf ("Failed to check power mode on ata%d\n", num); + return 1; + } + + if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) { + printf ("Device ata%d not ready\n", num); + return 1; + } + + writeb (0x00, port[num].ioaddr.feature_addr); + + writeb (0x00, port[num].ioaddr.nsect_addr); + writeb (0x00, port[num].ioaddr.lbal_addr); + writeb (0x00, port[num].ioaddr.lbam_addr); + writeb (0x00, port[num].ioaddr.lbah_addr); + + writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr); + writeb (ATA_CMD_STANDBY, port[num].ioaddr.command_addr); + + status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0); + if ((status & (ATA_BUSY | ATA_ERR))) { + printf ("Error waiting for disk spin down: status 0x%02x\n", + status); + port[num].dev_mask &= ~0x01; + return 1; + } + return 0; +} + +u8 sil3114_spin_up (int num) +{ + u8 status = 0; + + debug ("Spin up disk\n"); + + if (!(port[num].dev_mask & 0x01)) { + debug ("Device ata%d is not present\n", num); + return 1; + } + + if ((status = check_power_mode (num)) != 0x00) { + if (status == 0x01) { + printf ("Failed to check power mode on ata%d\n", num); + return 1; + } else { + /* should be up and running already */ + return 0; + } + } + + if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) { + printf ("Device ata%d not ready\n", num); + return 1; + } + + debug ("Stautus of device check: %d\n", status); + + writeb (0x00, port[num].ioaddr.feature_addr); + + writeb (0x00, port[num].ioaddr.nsect_addr); + writeb (0x00, port[num].ioaddr.lbal_addr); + writeb (0x00, port[num].ioaddr.lbam_addr); + writeb (0x00, port[num].ioaddr.lbah_addr); + + writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr); + writeb (ATA_CMD_IDLE, port[num].ioaddr.command_addr); + + status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0); + if ((status & (ATA_BUSY | ATA_ERR))) { + printf ("Error waiting for disk spin up: status 0x%02x\n", + status); + port[num].dev_mask &= ~0x01; + return 1; + } + + /* Wait for disk to enter Active state */ + do { + msleep (10); + status = check_power_mode (num); + } while ((status == 0x00) || (status == 0x80)); + + if (status == 0x01) { + printf ("Falied waiting for disk to spin up\n"); + return 1; + } + + return 0; +} + +/* Return value is not the usual here + * 0x00 - Device stand by + * 0x01 - Operation failed + * 0x80 - Device idle + * 0xff - Device active +*/ +static u8 check_power_mode (int num) +{ + u8 status = 0; + u8 res = 0; + if (!(port[num].dev_mask & 0x01)) { + debug ("Device ata%d is not present\n", num); + return 1; + } + + if (!(sata_chk_status (&port[num].ioaddr, 0) & ATA_DRDY)) { + printf ("Device ata%d not ready\n", num); + return 1; + } + + writeb (0, port[num].ioaddr.feature_addr); + writeb (0, port[num].ioaddr.nsect_addr); + writeb (0, port[num].ioaddr.lbal_addr); + writeb (0, port[num].ioaddr.lbam_addr); + writeb (0, port[num].ioaddr.lbah_addr); + + writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr); + writeb (ATA_CMD_CHK_POWER, port[num].ioaddr.command_addr); + + status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0); + if ((status & (ATA_BUSY | ATA_ERR))) { + printf + ("Error waiting for check power mode complete : status 0x%02x\n", + status); + port[num].dev_mask &= ~0x01; + return 1; + } + res = readb (port[num].ioaddr.nsect_addr); + debug ("Check powermode: %d\n", res); + return res; + +} + +static void sata_port (struct sata_ioports *ioport) +{ + ioport->data_addr = ioport->cmd_addr + ATA_REG_DATA; + ioport->error_addr = ioport->cmd_addr + ATA_REG_ERR; + ioport->feature_addr = ioport->cmd_addr + ATA_REG_FEATURE; + ioport->nsect_addr = ioport->cmd_addr + ATA_REG_NSECT; + ioport->lbal_addr = ioport->cmd_addr + ATA_REG_LBAL; + ioport->lbam_addr = ioport->cmd_addr + ATA_REG_LBAM; + ioport->lbah_addr = ioport->cmd_addr + ATA_REG_LBAH; + ioport->device_addr = ioport->cmd_addr + ATA_REG_DEVICE; + ioport->status_addr = ioport->cmd_addr + ATA_REG_STATUS; + ioport->command_addr = ioport->cmd_addr + ATA_REG_CMD; +} + +static u8 wait_for_irq (int num, unsigned int max) +{ + + u32 port = iobase[5]; + switch (num) { + case 0: + port += VND_TF_CNST_CH0; + break; + case 1: + port += VND_TF_CNST_CH1; + break; + case 2: + port += VND_TF_CNST_CH2; + break; + case 3: + port += VND_TF_CNST_CH3; + break; + default: + return 1; + } + + do { + if (readl (port) & VND_TF_CNST_INTST) { + break; + } + udelay (1000); + max--; + } while ((max > 0)); + + return (max == 0); +} + +static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits, + unsigned int max, u8 usealtstatus) +{ + u8 status; + + do { + if (!((status = sata_chk_status (ioaddr, usealtstatus)) & bits)) { + break; + } + udelay (1000); + max--; + } while ((status & bits) && (max > 0)); + + return status; +} + +static u8 sata_chk_status (struct sata_ioports *ioaddr, u8 usealtstatus) +{ + if (!usealtstatus) { + return readb (ioaddr->status_addr); + } else { + return readb (ioaddr->altstatus_addr); + } +} + +static void msleep (int count) +{ + int i; + + for (i = 0; i < count; i++) + udelay (1000); +} + +/* Read up to 255 sectors + * + * Returns sectors read +*/ +static u8 do_one_read (int device, ulong block, u8 blkcnt, u16 * buff, + uchar lba48) +{ + + u8 sr = 0; + u8 status; + u64 blknr = (u64) block; + + if (!(sata_chk_status (&port[device].ioaddr, 0) & ATA_DRDY)) { + printf ("Device ata%d not ready\n", device); + return 0; + } + + /* Set up transfer */ +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + writeb (0, port[device].ioaddr.nsect_addr); + writeb ((blknr >> 24) & 0xFF, port[device].ioaddr.lbal_addr); + writeb ((blknr >> 32) & 0xFF, port[device].ioaddr.lbam_addr); + writeb ((blknr >> 40) & 0xFF, port[device].ioaddr.lbah_addr); + } +#endif + writeb (blkcnt, port[device].ioaddr.nsect_addr); + writeb (((blknr) >> 0) & 0xFF, port[device].ioaddr.lbal_addr); + writeb ((blknr >> 8) & 0xFF, port[device].ioaddr.lbam_addr); + writeb ((blknr >> 16) & 0xFF, port[device].ioaddr.lbah_addr); + +#ifdef CONFIG_LBA48 + if (lba48) { + writeb (ATA_LBA, port[device].ioaddr.device_addr); + writeb (ATA_CMD_PIO_READ_EXT, port[device].ioaddr.command_addr); + } else +#endif + { + writeb (ATA_LBA | ((blknr >> 24) & 0xF), + port[device].ioaddr.device_addr); + writeb (ATA_CMD_PIO_READ, port[device].ioaddr.command_addr); + } + + status = sata_busy_wait (&port[device].ioaddr, ATA_BUSY, 10000, 1); + + if (status & ATA_BUSY) { + u8 err = 0; + + printf ("Device %d not responding status %d\n", device, status); + err = readb (port[device].ioaddr.error_addr); + printf ("Error reg = 0x%x\n", err); + + return (sr); + } + while (blkcnt--) { + + if (wait_for_irq (device, 500)) { + printf ("ata%u irq failed\n", device); + return sr; + } + + status = sata_chk_status (&port[device].ioaddr, 0); + if (status & ATA_ERR) { + printf ("ata%u error %d\n", device, + readb (port[device].ioaddr.error_addr)); + return sr; + } + /* Read one sector */ + input_data (&port[device].ioaddr, buff, ATA_SECTOR_WORDS); + buff += ATA_SECTOR_WORDS; + sr++; + + } + return sr; +} + +ulong sata_read (int device, ulong block, lbaint_t blkcnt, void *buff) +{ + ulong n = 0, sread; + u16 *buffer = (u16 *) buff; + u8 status = 0; + u64 blknr = (u64) block; + unsigned char lba48 = 0; + +#ifdef CONFIG_LBA48 + if (blknr > 0xfffffff) { + if (!sata_dev_desc[device].lba48) { + printf ("Drive doesn't support 48-bit addressing\n"); + return 0; + } + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + + while (blkcnt > 0) { + + if (blkcnt > 255) { + sread = 255; + } else { + sread = blkcnt; + } + + status = do_one_read (device, blknr, sread, buffer, lba48); + if (status != sread) { + printf ("Read failed\n"); + return n; + } + + blkcnt -= sread; + blknr += sread; + n += sread; + buffer += sread * ATA_SECTOR_WORDS; + } + return n; +} + +ulong sata_write (int device, ulong block, lbaint_t blkcnt, const void *buff) +{ + ulong n = 0; + u16 *buffer = (u16 *) buff; + unsigned char status = 0, num = 0; + u64 blknr = (u64) block; +#ifdef CONFIG_LBA48 + unsigned char lba48 = 0; + + if (blknr > 0xfffffff) { + if (!sata_dev_desc[device].lba48) { + printf ("Drive doesn't support 48-bit addressing\n"); + return 0; + } + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + /*Port Number */ + num = device; + + while (blkcnt-- > 0) { + status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 500, 0); + if (status & ATA_BUSY) { + printf ("ata%u failed to respond\n", port[num].port_no); + return n; + } +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + writeb (0, port[num].ioaddr.nsect_addr); + writeb ((blknr >> 24) & 0xFF, + port[num].ioaddr.lbal_addr); + writeb ((blknr >> 32) & 0xFF, + port[num].ioaddr.lbam_addr); + writeb ((blknr >> 40) & 0xFF, + port[num].ioaddr.lbah_addr); + } +#endif + writeb (1, port[num].ioaddr.nsect_addr); + writeb ((blknr >> 0) & 0xFF, port[num].ioaddr.lbal_addr); + writeb ((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr); + writeb ((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr); +#ifdef CONFIG_LBA48 + if (lba48) { + writeb (ATA_LBA, port[num].ioaddr.device_addr); + writeb (ATA_CMD_PIO_WRITE_EXT, port[num].ioaddr.command_addr); + } else +#endif + { + writeb (ATA_LBA | ((blknr >> 24) & 0xF), + port[num].ioaddr.device_addr); + writeb (ATA_CMD_PIO_WRITE, port[num].ioaddr.command_addr); + } + + msleep (50); + /*may take up to 4 sec */ + status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 4000, 0); + if ((status & (ATA_DRQ | ATA_BUSY | ATA_ERR)) != ATA_DRQ) { + printf ("Error no DRQ dev %d blk %ld: sts 0x%02x\n", + device, (ulong) blknr, status); + return (n); + } + + output_data (&port[num].ioaddr, buffer, ATA_SECTOR_WORDS); + readb (port[num].ioaddr.altstatus_addr); + udelay (50); + + ++n; + ++blknr; + buffer += ATA_SECTOR_WORDS; + } + return n; +} + +/* Driver implementation */ +static u8 sil_get_device_cache_line (pci_dev_t pdev) +{ + u8 cache_line = 0; + pci_read_config_byte (pdev, PCI_CACHE_LINE_SIZE, &cache_line); + return cache_line; +} + +int init_sata (int dev) +{ + static u8 init_done = 0; + static int res = 1; + pci_dev_t devno; + u8 cls = 0; + u16 cmd = 0; + u32 sconf = 0; + + if (init_done) { + return res; + } + + init_done = 1; + + if ((devno = pci_find_device (SIL_VEND_ID, SIL3114_DEVICE_ID, 0)) == -1) { + res = 1; + return res; + } + + /* Read out all BARs, even though we only use MMIO from BAR5 */ + pci_read_config_dword (devno, PCI_BASE_ADDRESS_0, &iobase[0]); + pci_read_config_dword (devno, PCI_BASE_ADDRESS_1, &iobase[1]); + pci_read_config_dword (devno, PCI_BASE_ADDRESS_2, &iobase[2]); + pci_read_config_dword (devno, PCI_BASE_ADDRESS_3, &iobase[3]); + pci_read_config_dword (devno, PCI_BASE_ADDRESS_4, &iobase[4]); + pci_read_config_dword (devno, PCI_BASE_ADDRESS_5, &iobase[5]); + + if ((iobase[0] == 0xFFFFFFFF) || (iobase[1] == 0xFFFFFFFF) || + (iobase[2] == 0xFFFFFFFF) || (iobase[3] == 0xFFFFFFFF) || + (iobase[4] == 0xFFFFFFFF) || (iobase[5] == 0xFFFFFFFF)) { + printf ("Error no base addr for SATA controller\n"); + res = 1; + return res; + } + + /* mask off unused bits */ + iobase[0] &= 0xfffffffc; + iobase[1] &= 0xfffffff8; + iobase[2] &= 0xfffffffc; + iobase[3] &= 0xfffffff8; + iobase[4] &= 0xfffffff0; + iobase[5] &= 0xfffffc00; + + /* from sata_sil in Linux kernel */ + cls = sil_get_device_cache_line (devno); + if (cls) { + cls >>= 3; + cls++; /* cls = (line_size/8)+1 */ + writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH0); + writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH1); + writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH2); + writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH3); + } else { + printf ("Cache line not set. Driver may not function\n"); + } + + /* Enable operation */ + pci_read_config_word (devno, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + pci_write_config_word (devno, PCI_COMMAND, cmd); + + /* Disable interrupt usage */ + pci_read_config_dword (devno, VND_SYSCONFSTAT, &sconf); + sconf |= (VND_SYSCONFSTAT_CHN_0_INTBLOCK | VND_SYSCONFSTAT_CHN_1_INTBLOCK); + pci_write_config_dword (devno, VND_SYSCONFSTAT, sconf); + + res = 0; + return res; +} + +/* Check if device is connected to port */ +int sata_bus_probe (int portno) +{ + u32 port = iobase[5]; + u32 val; + switch (portno) { + case 0: + port += VND_SSTATUS_CH0; + break; + case 1: + port += VND_SSTATUS_CH1; + break; + case 2: + port += VND_SSTATUS_CH2; + break; + case 3: + port += VND_SSTATUS_CH3; + break; + default: + return 0; + } + val = readl (port); + if ((val & SATA_DET_PRES) == SATA_DET_PRES) { + return 1; + } else { + return 0; + } +} + +int sata_phy_reset (int portno) +{ + u32 port = iobase[5]; + u32 val; + switch (portno) { + case 0: + port += VND_SCONTROL_CH0; + break; + case 1: + port += VND_SCONTROL_CH1; + break; + case 2: + port += VND_SCONTROL_CH2; + break; + case 3: + port += VND_SCONTROL_CH3; + break; + default: + return 0; + } + val = readl (port); + writel (val | SATA_SC_DET_RST, port); + msleep (150); + writel (val & ~SATA_SC_DET_RST, port); + return 0; +} + +int scan_sata (int dev) +{ + /* A bit brain dead, but the code has a legacy */ + switch (dev) { + case 0: + port[0].port_no = 0; + port[0].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH0; + port[0].ioaddr.altstatus_addr = port[0].ioaddr.ctl_addr = + (iobase[5] + VND_TF2_CH0) | ATA_PCI_CTL_OFS; + port[0].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH0; + break; +#if (CONFIG_SYS_SATA_MAX_DEVICE >= 1) + case 1: + port[1].port_no = 0; + port[1].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH1; + port[1].ioaddr.altstatus_addr = port[1].ioaddr.ctl_addr = + (iobase[5] + VND_TF2_CH1) | ATA_PCI_CTL_OFS; + port[1].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH1; + break; +#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 2) + case 2: + port[2].port_no = 0; + port[2].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH2; + port[2].ioaddr.altstatus_addr = port[2].ioaddr.ctl_addr = + (iobase[5] + VND_TF2_CH2) | ATA_PCI_CTL_OFS; + port[2].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH2; + break; +#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 3) + case 3: + port[3].port_no = 0; + port[3].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH3; + port[3].ioaddr.altstatus_addr = port[3].ioaddr.ctl_addr = + (iobase[5] + VND_TF2_CH3) | ATA_PCI_CTL_OFS; + port[3].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH3; + break; +#endif + default: + printf ("Tried to scan unknown port: ata%d\n", dev); + return 1; + } + + /* Initialize other registers */ + sata_port (&port[dev].ioaddr); + + /* Check for attached device */ + if (!sata_bus_probe (dev)) { + port[dev].port_state = 0; + debug ("SATA#%d port is not present\n", dev); + } else { + debug ("SATA#%d port is present\n", dev); + if (sata_bus_softreset (dev)) { + /* soft reset failed, try a hard one */ + sata_phy_reset (dev); + if (sata_bus_softreset (dev)) { + port[dev].port_state = 0; + } else { + port[dev].port_state = 1; + } + } else { + port[dev].port_state = 1; + } + } + if (port[dev].port_state == 1) { + /* Probe device and set xfer mode */ + sata_identify (dev, 0); + set_Feature_cmd (dev, 0); + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/block/sata_sil3114.h b/qemu/roms/u-boot/drivers/block/sata_sil3114.h new file mode 100644 index 000000000..091fca1d4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/sata_sil3114.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) Excito Elektronik i Skåne AB, All rights reserved. + * Author: Tor Krill + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef SATA_SIL3114_H +#define SATA_SIL3114_H + +struct sata_ioports { + unsigned long cmd_addr; + unsigned long data_addr; + unsigned long error_addr; + unsigned long feature_addr; + unsigned long nsect_addr; + unsigned long lbal_addr; + unsigned long lbam_addr; + unsigned long lbah_addr; + unsigned long device_addr; + unsigned long status_addr; + unsigned long command_addr; + unsigned long altstatus_addr; + unsigned long ctl_addr; + unsigned long bmdma_addr; + unsigned long scr_addr; +}; + +struct sata_port { + unsigned char port_no; /* primary=0, secondary=1 */ + struct sata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */ + unsigned char ctl_reg; + unsigned char last_ctl; + unsigned char port_state; /* 1-port is available and */ + /* 0-port is not available */ + unsigned char dev_mask; +}; + +/* Missing ata defines */ +#define ATA_CMD_STANDBY 0xE2 +#define ATA_CMD_STANDBYNOW1 0xE0 +#define ATA_CMD_IDLE 0xE3 +#define ATA_CMD_IDLEIMMEDIATE 0xE1 + +/* Defines for SIL3114 chip */ + +/* PCI defines */ +#define SIL_VEND_ID 0x1095 +#define SIL3114_DEVICE_ID 0x3114 + +/* some vendor specific registers */ +#define VND_SYSCONFSTAT 0x88 /* System Configuration Status and Command */ +#define VND_SYSCONFSTAT_CHN_0_INTBLOCK (1<<22) +#define VND_SYSCONFSTAT_CHN_1_INTBLOCK (1<<23) +#define VND_SYSCONFSTAT_CHN_2_INTBLOCK (1<<24) +#define VND_SYSCONFSTAT_CHN_3_INTBLOCK (1<<25) + +/* internal registers mapped by BAR5 */ +/* SATA Control*/ +#define VND_SCONTROL_CH0 0x100 +#define VND_SCONTROL_CH1 0x180 +#define VND_SCONTROL_CH2 0x300 +#define VND_SCONTROL_CH3 0x380 + +#define SATA_SC_IPM_T2P (1<<16) +#define SATA_SC_IPM_T2S (2<<16) +#define SATA_SC_SPD_1_5 (1<<4) +#define SATA_SC_SPD_3_0 (2<<4) +#define SATA_SC_DET_RST (1) /* ATA Reset sequence */ +#define SATA_SC_DET_PDIS (4) /* PHY Disable */ + +/* SATA Status */ +#define VND_SSTATUS_CH0 0x104 +#define VND_SSTATUS_CH1 0x184 +#define VND_SSTATUS_CH2 0x304 +#define VND_SSTATUS_CH3 0x384 + +#define SATA_SS_IPM_ACTIVE (1<<8) +#define SATA_SS_IPM_PARTIAL (2<<8) +#define SATA_SS_IPM_SLUMBER (6<<8) +#define SATA_SS_SPD_1_5 (1<<4) +#define SATA_SS_SPD_3_0 (2<<4) +#define SATA_DET_P_NOPHY (1) /* Device presence but no PHY connection established */ +#define SATA_DET_PRES (3) /* Device presence and active PHY */ +#define SATA_DET_OFFLINE (4) /* Device offline or in loopback mode */ + +/* Task file registers in BAR5 mapping */ +#define VND_TF0_CH0 0x80 +#define VND_TF0_CH1 0xc0 +#define VND_TF0_CH2 0x280 +#define VND_TF0_CH3 0x2c0 +#define VND_TF1_CH0 0x88 +#define VND_TF1_CH1 0xc8 +#define VND_TF1_CH2 0x288 +#define VND_TF1_CH3 0x2c8 +#define VND_TF2_CH0 0x88 +#define VND_TF2_CH1 0xc8 +#define VND_TF2_CH2 0x288 +#define VND_TF2_CH3 0x2c8 + +#define VND_BMDMA_CH0 0x00 +#define VND_BMDMA_CH1 0x08 +#define VND_BMDMA_CH2 0x200 +#define VND_BMDMA_CH3 0x208 +#define VND_BMDMA2_CH0 0x10 +#define VND_BMDMA2_CH1 0x18 +#define VND_BMDMA2_CH2 0x210 +#define VND_BMDMA2_CH3 0x218 + +/* FIFO control */ +#define VND_FIFOCFG_CH0 0x40 +#define VND_FIFOCFG_CH1 0x44 +#define VND_FIFOCFG_CH2 0x240 +#define VND_FIFOCFG_CH3 0x244 + +/* Task File configuration and status */ +#define VND_TF_CNST_CH0 0xa0 +#define VND_TF_CNST_CH1 0xe0 +#define VND_TF_CNST_CH2 0x2a0 +#define VND_TF_CNST_CH3 0x2e0 + +#define VND_TF_CNST_BFCMD (1<<1) +#define VND_TF_CNST_CHNRST (1<<2) +#define VND_TF_CNST_VDMA (1<<10) +#define VND_TF_CNST_INTST (1<<11) +#define VND_TF_CNST_WDTO (1<<12) +#define VND_TF_CNST_WDEN (1<<13) +#define VND_TF_CNST_WDIEN (1<<14) + +/* for testing */ +#define VND_SSDR 0x04c /* System Software Data Register */ +#define VND_FMACS 0x050 /* Flash Memory Address control and status */ + +#endif diff --git a/qemu/roms/u-boot/drivers/block/sil680.c b/qemu/roms/u-boot/drivers/block/sil680.c new file mode 100644 index 000000000..3ca64b980 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/sil680.c @@ -0,0 +1,90 @@ +/* + * (C) Copyright 2007 + * Gary Jennejohn, DENX Software Engineering, garyj@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* sil680.c - ide support functions for the Sil0680A controller */ + +/* + * The following parameters must be defined in the configuration file + * of the target board: + * + * #define CONFIG_IDE_SIL680 + * + * #define CONFIG_PCI_PNP + * NOTE it may also be necessary to define this if the default of 8 is + * incorrect for the target board (e.g. the sequoia board requires 0). + * #define CONFIG_SYS_PCI_CACHE_LINE_SIZE 0 + * + * #define CONFIG_CMD_IDE + * #undef CONFIG_IDE_8xx_DIRECT + * #undef CONFIG_IDE_LED + * #undef CONFIG_IDE_RESET + * #define CONFIG_IDE_PREINIT + * #define CONFIG_SYS_IDE_MAXBUS 2 - modify to suit + * #define CONFIG_SYS_IDE_MAXDEVICE (CONFIG_SYS_IDE_MAXBUS*2) - modify to suit + * #define CONFIG_SYS_ATA_BASE_ADDR 0 + * #define CONFIG_SYS_ATA_IDE0_OFFSET 0 + * #define CONFIG_SYS_ATA_IDE1_OFFSET 0 + * #define CONFIG_SYS_ATA_DATA_OFFSET 0 + * #define CONFIG_SYS_ATA_REG_OFFSET 0 + * #define CONFIG_SYS_ATA_ALT_OFFSET 0x0004 + * + * The mapping for PCI IO-space. + * NOTE this is the value for the sequoia board. Modify to suit. + * #define CONFIG_SYS_PCI0_IO_SPACE 0xE8000000 + */ + +#include +#include +#include +#include + +extern ulong ide_bus_offset[CONFIG_SYS_IDE_MAXBUS]; + +int ide_preinit (void) +{ + int status; + pci_dev_t devbusfn; + int l; + + status = 1; + for (l = 0; l < CONFIG_SYS_IDE_MAXBUS; l++) { + ide_bus_offset[l] = -ATA_STATUS; + } + devbusfn = pci_find_device (0x1095, 0x0680, 0); + if (devbusfn != -1) { + status = 0; + + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, + (u32 *) &ide_bus_offset[0]); + ide_bus_offset[0] &= 0xfffffff8; + ide_bus_offset[0] += CONFIG_SYS_PCI0_IO_SPACE; + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_2, + (u32 *) &ide_bus_offset[1]); + ide_bus_offset[1] &= 0xfffffff8; + ide_bus_offset[1] += CONFIG_SYS_PCI0_IO_SPACE; + /* init various things - taken from the Linux driver */ + /* set PIO mode */ + pci_write_config_byte(devbusfn, 0x80, 0x00); + pci_write_config_byte(devbusfn, 0x84, 0x00); + /* IDE0 */ + pci_write_config_byte(devbusfn, 0xA1, 0x02); + pci_write_config_word(devbusfn, 0xA2, 0x328A); + pci_write_config_dword(devbusfn, 0xA4, 0x62DD62DD); + pci_write_config_dword(devbusfn, 0xA8, 0x43924392); + pci_write_config_dword(devbusfn, 0xAC, 0x40094009); + /* IDE1 */ + pci_write_config_byte(devbusfn, 0xB1, 0x02); + pci_write_config_word(devbusfn, 0xB2, 0x328A); + pci_write_config_dword(devbusfn, 0xB4, 0x62DD62DD); + pci_write_config_dword(devbusfn, 0xB8, 0x43924392); + pci_write_config_dword(devbusfn, 0xBC, 0x40094009); + } + return (status); +} + +void ide_set_reset (int flag) { + return; +} diff --git a/qemu/roms/u-boot/drivers/block/sym53c8xx.c b/qemu/roms/u-boot/drivers/block/sym53c8xx.c new file mode 100644 index 000000000..6f1ac8572 --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/sym53c8xx.c @@ -0,0 +1,852 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland, d.peter@mpl.ch. + * + * SPDX-License-Identifier: GPL-2.0+ + * partly derived from + * linux/drivers/scsi/sym53c8xx.c + * + */ + +/* + * SCSI support based on the chip sym53C810. + * + * 09-19-2001 Andreas Heppel, Sysgo RTS GmbH + * The local version of this driver for the BAB750 board does not + * use interrupts but polls the chip instead (see the call of + * 'handle_scsi_int()' in 'scsi_issue()'. + */ + +#include + +#include +#include +#include +#include +#include + +#undef SYM53C8XX_DEBUG + +#ifdef SYM53C8XX_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +#if defined(CONFIG_CMD_SCSI) && defined(CONFIG_SCSI_SYM53C8XX) + +#undef SCSI_SINGLE_STEP +/* + * Single Step is only used for debug purposes + */ +#ifdef SCSI_SINGLE_STEP +static unsigned long start_script_select; +static unsigned long start_script_msgout; +static unsigned long start_script_msgin; +static unsigned long start_script_msg_ext; +static unsigned long start_script_cmd; +static unsigned long start_script_data_in; +static unsigned long start_script_data_out; +static unsigned long start_script_status; +static unsigned long start_script_complete; +static unsigned long start_script_error; +static unsigned long start_script_reselection; +static unsigned int len_script_select; +static unsigned int len_script_msgout; +static unsigned int len_script_msgin; +static unsigned int len_script_msg_ext; +static unsigned int len_script_cmd; +static unsigned int len_script_data_in; +static unsigned int len_script_data_out; +static unsigned int len_script_status; +static unsigned int len_script_complete; +static unsigned int len_script_error; +static unsigned int len_script_reselection; +#endif + + +static unsigned short scsi_int_mask; /* shadow register for SCSI related interrupts */ +static unsigned char script_int_mask; /* shadow register for SCRIPT related interrupts */ +static unsigned long script_select[8]; /* script for selection */ +static unsigned long script_msgout[8]; /* script for message out phase (NOT USED) */ +static unsigned long script_msgin[14]; /* script for message in phase */ +static unsigned long script_msg_ext[32]; /* script for message in phase when more than 1 byte message */ +static unsigned long script_cmd[18]; /* script for command phase */ +static unsigned long script_data_in[8]; /* script for data in phase */ +static unsigned long script_data_out[8]; /* script for data out phase */ +static unsigned long script_status[6]; /* script for status phase */ +static unsigned long script_complete[10]; /* script for complete */ +static unsigned long script_reselection[4]; /* script for reselection (NOT USED) */ +static unsigned long script_error[2]; /* script for error handling */ + +static unsigned long int_stat[3]; /* interrupt status */ +static unsigned long scsi_mem_addr; /* base memory address =SCSI_MEM_ADDRESS; */ + +#define bus_to_phys(a) pci_mem_to_phys(busdevfunc, (unsigned long) (a)) +#define phys_to_bus(a) pci_phys_to_mem(busdevfunc, (unsigned long) (a)) + +#define SCSI_MAX_RETRY 3 /* number of retries in scsi_issue() */ + +#define SCSI_MAX_RETRY_NOT_READY 10 /* number of retries when device is not ready */ +#define SCSI_NOT_READY_TIME_OUT 500 /* timeout per retry when not ready */ + +/********************************************************************************* + * forward declerations + */ + +void scsi_chip_init(void); +void handle_scsi_int(void); + + +/******************************************************************************** + * reports SCSI errors to the user + */ +void scsi_print_error (ccb * pccb) +{ + int i; + + printf ("SCSI Error: Target %d LUN %d Command %02X\n", pccb->target, + pccb->lun, pccb->cmd[0]); + printf (" CCB: "); + for (i = 0; i < pccb->cmdlen; i++) + printf ("%02X ", pccb->cmd[i]); + printf ("(len=%d)\n", pccb->cmdlen); + printf (" Cntrl: "); + switch (pccb->contr_stat) { + case SIR_COMPLETE: + printf ("Complete (no Error)\n"); + break; + case SIR_SEL_ATN_NO_MSG_OUT: + printf ("Selected with ATN no MSG out phase\n"); + break; + case SIR_CMD_OUT_ILL_PH: + printf ("Command out illegal phase\n"); + break; + case SIR_MSG_RECEIVED: + printf ("MSG received Error\n"); + break; + case SIR_DATA_IN_ERR: + printf ("Data in Error\n"); + break; + case SIR_DATA_OUT_ERR: + printf ("Data out Error\n"); + break; + case SIR_SCRIPT_ERROR: + printf ("Script Error\n"); + break; + case SIR_MSG_OUT_NO_CMD: + printf ("MSG out no Command phase\n"); + break; + case SIR_MSG_OVER7: + printf ("MSG in over 7 bytes\n"); + break; + case INT_ON_FY: + printf ("Interrupt on fly\n"); + break; + case SCSI_SEL_TIME_OUT: + printf ("SCSI Selection Timeout\n"); + break; + case SCSI_HNS_TIME_OUT: + printf ("SCSI Handshake Timeout\n"); + break; + case SCSI_MA_TIME_OUT: + printf ("SCSI Phase Error\n"); + break; + case SCSI_UNEXP_DIS: + printf ("SCSI unexpected disconnect\n"); + break; + default: + printf ("unknown status %lx\n", pccb->contr_stat); + break; + } + printf (" Sense: SK %x (", pccb->sense_buf[2] & 0x0f); + switch (pccb->sense_buf[2] & 0xf) { + case SENSE_NO_SENSE: + printf ("No Sense)"); + break; + case SENSE_RECOVERED_ERROR: + printf ("Recovered Error)"); + break; + case SENSE_NOT_READY: + printf ("Not Ready)"); + break; + case SENSE_MEDIUM_ERROR: + printf ("Medium Error)"); + break; + case SENSE_HARDWARE_ERROR: + printf ("Hardware Error)"); + break; + case SENSE_ILLEGAL_REQUEST: + printf ("Illegal request)"); + break; + case SENSE_UNIT_ATTENTION: + printf ("Unit Attention)"); + break; + case SENSE_DATA_PROTECT: + printf ("Data Protect)"); + break; + case SENSE_BLANK_CHECK: + printf ("Blank check)"); + break; + case SENSE_VENDOR_SPECIFIC: + printf ("Vendor specific)"); + break; + case SENSE_COPY_ABORTED: + printf ("Copy aborted)"); + break; + case SENSE_ABORTED_COMMAND: + printf ("Aborted Command)"); + break; + case SENSE_VOLUME_OVERFLOW: + printf ("Volume overflow)"); + break; + case SENSE_MISCOMPARE: + printf ("Misscompare\n"); + break; + default: + printf ("Illegal Sensecode\n"); + break; + } + printf (" ASC %x ASCQ %x\n", pccb->sense_buf[12], + pccb->sense_buf[13]); + printf (" Status: "); + switch (pccb->status) { + case S_GOOD: + printf ("Good\n"); + break; + case S_CHECK_COND: + printf ("Check condition\n"); + break; + case S_COND_MET: + printf ("Condition Met\n"); + break; + case S_BUSY: + printf ("Busy\n"); + break; + case S_INT: + printf ("Intermediate\n"); + break; + case S_INT_COND_MET: + printf ("Intermediate condition met\n"); + break; + case S_CONFLICT: + printf ("Reservation conflict\n"); + break; + case S_TERMINATED: + printf ("Command terminated\n"); + break; + case S_QUEUE_FULL: + printf ("Task set full\n"); + break; + default: + printf ("unknown: %02X\n", pccb->status); + break; + } + +} + + +/****************************************************************************** + * sets-up the SCSI controller + * the base memory address is retrieved via the pci_read_config_dword + */ +void scsi_low_level_init(int busdevfunc) +{ + unsigned int cmd; + unsigned int addr; + unsigned char vec; + + pci_read_config_byte(busdevfunc, PCI_INTERRUPT_LINE, &vec); + pci_read_config_dword(busdevfunc, PCI_BASE_ADDRESS_1, &addr); + + addr = bus_to_phys(addr & ~0xf); + + /* + * Enable bus mastering in case this has not been done, yet. + */ + pci_read_config_dword(busdevfunc, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER; + pci_write_config_dword(busdevfunc, PCI_COMMAND, cmd); + + scsi_mem_addr = addr; + + scsi_chip_init(); + scsi_bus_reset(); +} + + +/************************************************************************************ + * Low level Part of SCSI Driver + */ + +/* + * big-endian -> little endian conversion for the script + */ +unsigned long swap_script(unsigned long val) +{ + unsigned long tmp; + tmp = ((val>>24)&0xff) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | ((val<<24)&0xff000000); + return tmp; +} + + +void scsi_write_byte(ulong offset,unsigned char val) +{ + out8(scsi_mem_addr+offset,val); +} + + +unsigned char scsi_read_byte(ulong offset) +{ + return(in8(scsi_mem_addr+offset)); +} + + +/******************************************************************************** + * interrupt handler + */ +void handle_scsi_int(void) +{ + unsigned char stat,stat1,stat2; + unsigned short sstat; + int i; +#ifdef SCSI_SINGLE_STEP + unsigned long tt; +#endif + stat=scsi_read_byte(ISTAT); + if((stat & DIP)==DIP) { /* DMA Interrupt pending */ + stat1=scsi_read_byte(DSTAT); +#ifdef SCSI_SINGLE_STEP + if((stat1 & SSI)==SSI) { + tt=in32r(scsi_mem_addr+DSP); + if(((tt)>=start_script_select) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_msgout) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_msgin) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_msg_ext) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_cmd) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_data_in) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_data_out) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_status) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_complete) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_error) && ((tt)>2); + goto end_single; + } + if(((tt)>=start_script_reselection) && ((tt)>2); + goto end_single; + } + printf("sc: %lx\n",tt); +end_single: + stat2=scsi_read_byte(DCNTL); + stat2|=STD; + scsi_write_byte(DCNTL,stat2); + } +#endif + if((stat1 & SIR)==SIR) /* script interrupt */ + { + int_stat[0]=in32(scsi_mem_addr+DSPS); + } + if((stat1 & DFE)==0) { /* fifo not epmty */ + scsi_write_byte(CTEST3,CLF); /* Clear DMA FIFO */ + stat2=scsi_read_byte(STEST3); + scsi_write_byte(STEST3,(stat2 | CSF)); /* Clear SCSI FIFO */ + } + } + if((stat & SIP)==SIP) { /* scsi interrupt */ + sstat = (unsigned short)scsi_read_byte(SIST+1); + sstat <<=8; + sstat |= (unsigned short)scsi_read_byte(SIST); + for(i=0;i<3;i++) { + if(int_stat[i]==0) + break; /* found an empty int status */ + } + int_stat[i]=SCSI_INT_STATE | sstat; + stat1=scsi_read_byte(DSTAT); + if((stat1 & DFE)==0) { /* fifo not epmty */ + scsi_write_byte(CTEST3,CLF); /* Clear DMA FIFO */ + stat2=scsi_read_byte(STEST3); + scsi_write_byte(STEST3,(stat2 | CSF)); /* Clear SCSI FIFO */ + } + } + if((stat & INTF)==INTF) { /* interrupt on Fly */ + scsi_write_byte(ISTAT,stat); /* clear it */ + for(i=0;i<3;i++) { + if(int_stat[i]==0) + break; /* found an empty int status */ + } + int_stat[i]=INT_ON_FY; + } +} + +void scsi_bus_reset(void) +{ + unsigned char t; + int i; + int end = CONFIG_SYS_SCSI_SPIN_UP_TIME*1000; + + t=scsi_read_byte(SCNTL1); + scsi_write_byte(SCNTL1,(t | CRST)); + udelay(50); + scsi_write_byte(SCNTL1,t); + + puts("waiting for devices to spin up"); + for(i=0;i>8)); + scsi_write_byte(DIEN,script_int_mask); +} + +void scsi_write_dsp(unsigned long start) +{ +#ifdef SCSI_SINGLE_STEP + unsigned char t; +#endif + out32r(scsi_mem_addr + DSP,start); +#ifdef SCSI_SINGLE_STEP + t=scsi_read_byte(DCNTL); + t|=STD; + scsi_write_byte(DCNTL,t); +#endif +} + +/* only used for debug purposes */ +void scsi_print_script(void) +{ + printf("script_select @ 0x%08lX\n",(unsigned long)&script_select[0]); + printf("script_msgout @ 0x%08lX\n",(unsigned long)&script_msgout[0]); + printf("script_msgin @ 0x%08lX\n",(unsigned long)&script_msgin[0]); + printf("script_msgext @ 0x%08lX\n",(unsigned long)&script_msg_ext[0]); + printf("script_cmd @ 0x%08lX\n",(unsigned long)&script_cmd[0]); + printf("script_data_in @ 0x%08lX\n",(unsigned long)&script_data_in[0]); + printf("script_data_out @ 0x%08lX\n",(unsigned long)&script_data_out[0]); + printf("script_status @ 0x%08lX\n",(unsigned long)&script_status[0]); + printf("script_complete @ 0x%08lX\n",(unsigned long)&script_complete[0]); + printf("script_error @ 0x%08lX\n",(unsigned long)&script_error[0]); +} + + +void scsi_set_script(ccb *pccb) +{ + int busdevfunc = pccb->priv; + int i; + i=0; + script_select[i++]=swap_script(SCR_REG_REG(GPREG, SCR_AND, 0xfe)); + script_select[i++]=0; /* LED ON */ + script_select[i++]=swap_script(SCR_CLR(SCR_TRG)); /* select initiator mode */ + script_select[i++]=0; + /* script_select[i++]=swap_script(SCR_SEL_ABS_ATN | pccb->target << 16); */ + script_select[i++]=swap_script(SCR_SEL_ABS | pccb->target << 16); + script_select[i++]=swap_script(phys_to_bus(&script_cmd[4])); /* error handling */ + script_select[i++]=swap_script(SCR_JUMP); /* next section */ + /* script_select[i++]=swap_script((unsigned long)&script_msgout[0]); */ /* message out */ + script_select[i++]=swap_script(phys_to_bus(&script_cmd[0])); /* command out */ + +#ifdef SCSI_SINGLE_STEP + start_script_select=(unsigned long)&script_select[0]; + len_script_select=i*4; +#endif + + i=0; + script_msgout[i++]=swap_script(SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT))); + script_msgout[i++]=SIR_SEL_ATN_NO_MSG_OUT; + script_msgout[i++]=swap_script( SCR_MOVE_ABS(1) ^ SCR_MSG_OUT); + script_msgout[i++]=swap_script(phys_to_bus(&pccb->msgout[0])); + script_msgout[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_COMMAND))); /* if Command phase */ + script_msgout[i++]=swap_script(phys_to_bus(&script_cmd[0])); /* switch to command */ + script_msgout[i++]=swap_script(SCR_INT); /* interrupt if not */ + script_msgout[i++]=SIR_MSG_OUT_NO_CMD; + +#ifdef SCSI_SINGLE_STEP + start_script_msgout=(unsigned long)&script_msgout[0]; + len_script_msgout=i*4; +#endif + i=0; + script_cmd[i++]=swap_script(SCR_MOVE_ABS(pccb->cmdlen) ^ SCR_COMMAND); + script_cmd[i++]=swap_script(phys_to_bus(&pccb->cmd[0])); + script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN))); /* message in ? */ + script_cmd[i++]=swap_script(phys_to_bus(&script_msgin[0])); + script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_DATA_OUT))); /* data out ? */ + script_cmd[i++]=swap_script(phys_to_bus(&script_data_out[0])); + script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_DATA_IN))); /* data in ? */ + script_cmd[i++]=swap_script(phys_to_bus(&script_data_in[0])); + script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_STATUS))); /* status ? */ + script_cmd[i++]=swap_script(phys_to_bus(&script_status[0])); + script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND))); /* command ? */ + script_cmd[i++]=swap_script(phys_to_bus(&script_cmd[0])); + script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT))); /* message out ? */ + script_cmd[i++]=swap_script(phys_to_bus(&script_msgout[0])); + script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN))); /* just for error handling message in ? */ + script_cmd[i++]=swap_script(phys_to_bus(&script_msgin[0])); + script_cmd[i++]=swap_script(SCR_INT); /* interrupt if not */ + script_cmd[i++]=SIR_CMD_OUT_ILL_PH; +#ifdef SCSI_SINGLE_STEP + start_script_cmd=(unsigned long)&script_cmd[0]; + len_script_cmd=i*4; +#endif + i=0; + script_data_out[i++]=swap_script(SCR_MOVE_ABS(pccb->datalen)^ SCR_DATA_OUT); /* move */ + script_data_out[i++]=swap_script(phys_to_bus(pccb->pdata)); /* pointer to buffer */ + script_data_out[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS))); + script_data_out[i++]=swap_script(phys_to_bus(&script_status[0])); + script_data_out[i++]=swap_script(SCR_INT); + script_data_out[i++]=SIR_DATA_OUT_ERR; + +#ifdef SCSI_SINGLE_STEP + start_script_data_out=(unsigned long)&script_data_out[0]; + len_script_data_out=i*4; +#endif + i=0; + script_data_in[i++]=swap_script(SCR_MOVE_ABS(pccb->datalen)^ SCR_DATA_IN); /* move */ + script_data_in[i++]=swap_script(phys_to_bus(pccb->pdata)); /* pointer to buffer */ + script_data_in[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS))); + script_data_in[i++]=swap_script(phys_to_bus(&script_status[0])); + script_data_in[i++]=swap_script(SCR_INT); + script_data_in[i++]=SIR_DATA_IN_ERR; +#ifdef SCSI_SINGLE_STEP + start_script_data_in=(unsigned long)&script_data_in[0]; + len_script_data_in=i*4; +#endif + i=0; + script_msgin[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); + script_msgin[i++]=swap_script(phys_to_bus(&pccb->msgin[0])); + script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE))); + script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0])); + script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT))); + script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0])); + script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP))); + script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0])); + script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP))); + script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0])); + script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED))); + script_msgin[i++]=swap_script(phys_to_bus(&script_msg_ext[0])); + script_msgin[i++]=swap_script(SCR_INT); + script_msgin[i++]=SIR_MSG_RECEIVED; +#ifdef SCSI_SINGLE_STEP + start_script_msgin=(unsigned long)&script_msgin[0]; + len_script_msgin=i*4; +#endif + i=0; + script_msg_ext[i++]=swap_script(SCR_CLR (SCR_ACK)); /* clear ACK */ + script_msg_ext[i++]=0; + script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* assuming this is the msg length */ + script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[1])); + script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN))); + script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */ + script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */ + script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[2])); + script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN))); + script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */ + script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */ + script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[3])); + script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN))); + script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */ + script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */ + script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[4])); + script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN))); + script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */ + script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */ + script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[5])); + script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN))); + script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */ + script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */ + script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[6])); + script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN))); + script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */ + script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */ + script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[7])); + script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN))); + script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */ + script_msg_ext[i++]=swap_script(SCR_INT); + script_msg_ext[i++]=SIR_MSG_OVER7; +#ifdef SCSI_SINGLE_STEP + start_script_msg_ext=(unsigned long)&script_msg_ext[0]; + len_script_msg_ext=i*4; +#endif + i=0; + script_status[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_STATUS); + script_status[i++]=swap_script(phys_to_bus(&pccb->status)); + script_status[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN))); + script_status[i++]=swap_script(phys_to_bus(&script_msgin[0])); + script_status[i++]=swap_script(SCR_INT); + script_status[i++]=SIR_STATUS_ILL_PH; +#ifdef SCSI_SINGLE_STEP + start_script_status=(unsigned long)&script_status[0]; + len_script_status=i*4; +#endif + i=0; + script_complete[i++]=swap_script(SCR_REG_REG (SCNTL2, SCR_AND, 0x7f)); + script_complete[i++]=0; + script_complete[i++]=swap_script(SCR_CLR (SCR_ACK|SCR_ATN)); + script_complete[i++]=0; + script_complete[i++]=swap_script(SCR_WAIT_DISC); + script_complete[i++]=0; + script_complete[i++]=swap_script(SCR_REG_REG(GPREG, SCR_OR, 0x01)); + script_complete[i++]=0; /* LED OFF */ + script_complete[i++]=swap_script(SCR_INT); + script_complete[i++]=SIR_COMPLETE; +#ifdef SCSI_SINGLE_STEP + start_script_complete=(unsigned long)&script_complete[0]; + len_script_complete=i*4; +#endif + i=0; + script_error[i++]=swap_script(SCR_INT); /* interrupt if error */ + script_error[i++]=SIR_SCRIPT_ERROR; +#ifdef SCSI_SINGLE_STEP + start_script_error=(unsigned long)&script_error[0]; + len_script_error=i*4; +#endif + i=0; + script_reselection[i++]=swap_script(SCR_CLR (SCR_TRG)); /* target status */ + script_reselection[i++]=0; + script_reselection[i++]=swap_script(SCR_WAIT_RESEL); + script_reselection[i++]=swap_script(phys_to_bus(&script_select[0])); /* len = 4 */ +#ifdef SCSI_SINGLE_STEP + start_script_reselection=(unsigned long)&script_reselection[0]; + len_script_reselection=i*4; +#endif +} + + +void scsi_issue(ccb *pccb) +{ + int busdevfunc = pccb->priv; + int i; + unsigned short sstat; + int retrycnt; /* retry counter */ + for(i=0;i<3;i++) + int_stat[i]=0; /* delete all int status */ + /* struct pccb must be set-up correctly */ + retrycnt=0; + PRINTF("ID %d issue cmd %02X\n",pccb->target,pccb->cmd[0]); + pccb->trans_bytes=0; /* no bytes transfered yet */ + scsi_set_script(pccb); /* fill in SCRIPT */ + scsi_int_mask=STO | UDC | MA; /* | CMP; / * Interrupts which are enabled */ + script_int_mask=0xff; /* enable all Ints */ + scsi_int_enable(); + scsi_write_dsp(phys_to_bus(&script_select[0])); /* start script */ + /* now we have to wait for IRQs */ +retry: + /* + * This version of the driver is _not_ interrupt driven, + * but polls the chip's interrupt registers (ISTAT, DSTAT). + */ + while(int_stat[0]==0) + handle_scsi_int(); + + if(int_stat[0]==SIR_COMPLETE) { + if(pccb->msgin[0]==M_DISCONNECT) { + PRINTF("Wait for reselection\n"); + for(i=0;i<3;i++) + int_stat[i]=0; /* delete all int status */ + scsi_write_dsp(phys_to_bus(&script_reselection[0])); /* start reselection script */ + goto retry; + } + pccb->contr_stat=SIR_COMPLETE; + return; + } + if((int_stat[0] & SCSI_INT_STATE)==SCSI_INT_STATE) { /* scsi interrupt */ + sstat=(unsigned short)int_stat[0]; + if((sstat & STO)==STO) { /* selection timeout */ + pccb->contr_stat=SCSI_SEL_TIME_OUT; + scsi_write_byte(GPREG,0x01); + PRINTF("ID: %X Selection Timeout\n",pccb->target); + return; + } + if((sstat & UDC)==UDC) { /* unexpected disconnect */ + pccb->contr_stat=SCSI_UNEXP_DIS; + scsi_write_byte(GPREG,0x01); + PRINTF("ID: %X Unexpected Disconnect\n",pccb->target); + return; + } + if((sstat & RSL)==RSL) { /* reselection */ + pccb->contr_stat=SCSI_UNEXP_DIS; + scsi_write_byte(GPREG,0x01); + PRINTF("ID: %X Unexpected Disconnect\n",pccb->target); + return; + } + if(((sstat & MA)==MA)||((sstat & HTH)==HTH)) { /* phase missmatch */ + if(retrycnttrans_bytes=pccb->datalen - + ((unsigned long)scsi_read_byte(DBC) | + ((unsigned long)scsi_read_byte(DBC+1)<<8) | + ((unsigned long)scsi_read_byte(DBC+2)<<16)); + for(i=0;i<3;i++) + int_stat[i]=0; /* delete all int status */ + retrycnt++; + PRINTF("ID: %X Phase Missmatch Retry %d Phase %02X transfered %lx\n", + pccb->target,retrycnt,scsi_read_byte(SBCL),pccb->trans_bytes); + scsi_write_dsp(phys_to_bus(&script_cmd[4])); /* start retry script */ + goto retry; + } + if((sstat & MA)==MA) + pccb->contr_stat=SCSI_MA_TIME_OUT; + else + pccb->contr_stat=SCSI_HNS_TIME_OUT; + PRINTF("Phase Missmatch stat %lx\n",pccb->contr_stat); + return; + } /* no phase int */ +/* if((sstat & CMP)==CMP) { + pccb->contr_stat=SIR_COMPLETE; + return; + } +*/ + PRINTF("SCSI INT %lX\n",int_stat[0]); + pccb->contr_stat=int_stat[0]; + return; + } /* end scsi int */ + PRINTF("SCRIPT INT %lX phase %02X\n",int_stat[0],scsi_read_byte(SBCL)); + pccb->contr_stat=int_stat[0]; + return; +} + +int scsi_exec(ccb *pccb) +{ + unsigned char tmpcmd[16],tmpstat; + int i,retrycnt,t; + unsigned long transbytes,datalen; + unsigned char *tmpptr; + retrycnt=0; +retry: + scsi_issue(pccb); + if(pccb->contr_stat!=SIR_COMPLETE) + return false; + if(pccb->status==S_GOOD) + return true; + if(pccb->status==S_CHECK_COND) { /* check condition */ + for(i=0;i<16;i++) + tmpcmd[i]=pccb->cmd[i]; + pccb->cmd[0]=SCSI_REQ_SENSE; + pccb->cmd[1]=pccb->lun<<5; + pccb->cmd[2]=0; + pccb->cmd[3]=0; + pccb->cmd[4]=14; + pccb->cmd[5]=0; + pccb->cmdlen=6; + pccb->msgout[0]=SCSI_IDENTIFY; + transbytes=pccb->trans_bytes; + tmpptr=pccb->pdata; + pccb->pdata = &pccb->sense_buf[0]; + datalen=pccb->datalen; + pccb->datalen=14; + tmpstat=pccb->status; + scsi_issue(pccb); + for(i=0;i<16;i++) + pccb->cmd[i]=tmpcmd[i]; + pccb->trans_bytes=transbytes; + pccb->pdata=tmpptr; + pccb->datalen=datalen; + pccb->status=tmpstat; + PRINTF("Request_sense sense key %x ASC %x ASCQ %x\n",pccb->sense_buf[2]&0x0f, + pccb->sense_buf[12],pccb->sense_buf[13]); + switch(pccb->sense_buf[2]&0xf) { + case SENSE_NO_SENSE: + case SENSE_RECOVERED_ERROR: + /* seems to be ok */ + return true; + break; + case SENSE_NOT_READY: + if((pccb->sense_buf[12]!=0x04)||(pccb->sense_buf[13]!=0x01)) { + /* if device is not in process of becoming ready */ + return false; + break; + } /* else fall through */ + case SENSE_UNIT_ATTENTION: + if(retrycnttarget,retrycnt); + for(t=0;ttarget,retrycnt); + return false; + default: + return false; + } + } + PRINTF("Status = %X\n",pccb->status); + return false; +} + + +void scsi_chip_init(void) +{ + /* first we issue a soft reset */ + scsi_write_byte(ISTAT,SRST); + udelay(1000); + scsi_write_byte(ISTAT,0); + /* setup chip */ + scsi_write_byte(SCNTL0,0xC0); /* full arbitration no start, no message, parity disabled, master */ + scsi_write_byte(SCNTL1,0x00); + scsi_write_byte(SCNTL2,0x00); +#ifndef CONFIG_SYS_SCSI_SYM53C8XX_CCF /* config value for none 40 MHz clocks */ + scsi_write_byte(SCNTL3,0x13); /* synchronous clock 40/4=10MHz, asynchronous 40MHz */ +#else + scsi_write_byte(SCNTL3,CONFIG_SYS_SCSI_SYM53C8XX_CCF); /* config value for none 40 MHz clocks */ +#endif + scsi_write_byte(SCID,0x47); /* ID=7, enable reselection */ + scsi_write_byte(SXFER,0x00); /* synchronous transfer period 10MHz, asynchronous */ + scsi_write_byte(SDID,0x00); /* targed SCSI ID = 0 */ + scsi_int_mask=0x0000; /* no Interrupt is enabled */ + script_int_mask=0x00; + scsi_int_enable(); + scsi_write_byte(GPREG,0x01); /* GPIO0 is LED (off) */ + scsi_write_byte(GPCNTL,0x0E); /* GPIO0 is Output */ + scsi_write_byte(STIME0,0x08); /* handshake timer disabled, selection timeout 512msec */ + scsi_write_byte(RESPID,0x80); /* repond only to the own ID (reselection) */ + scsi_write_byte(STEST1,0x00); /* not isolated, SCLK is used */ + scsi_write_byte(STEST2,0x00); /* no Lowlevel Mode? */ + scsi_write_byte(STEST3,0x80); /* enable tolerANT */ + scsi_write_byte(CTEST3,0x04); /* clear FIFO */ + scsi_write_byte(CTEST4,0x00); + scsi_write_byte(CTEST5,0x00); +#ifdef SCSI_SINGLE_STEP +/* scsi_write_byte(DCNTL,IRQM | SSM); */ + scsi_write_byte(DCNTL,IRQD | SSM); + scsi_write_byte(DMODE,MAN); +#else +/* scsi_write_byte(DCNTL,IRQM); */ + scsi_write_byte(DCNTL,IRQD); + scsi_write_byte(DMODE,0x00); +#endif +} +#endif diff --git a/qemu/roms/u-boot/drivers/block/systemace.c b/qemu/roms/u-boot/drivers/block/systemace.c new file mode 100644 index 000000000..fdf75b5ab --- /dev/null +++ b/qemu/roms/u-boot/drivers/block/systemace.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2004 Picture Elements, Inc. + * Stephen Williams (XXXXXXXXXXXXXXXX) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * The Xilinx SystemACE chip support is activated by defining + * CONFIG_SYSTEMACE to turn on support, and CONFIG_SYS_SYSTEMACE_BASE + * to set the base address of the device. This code currently + * assumes that the chip is connected via a byte-wide bus. + * + * The CONFIG_SYSTEMACE also adds to fat support the device class + * "ace" that allows the user to execute "fatls ace 0" and the + * like. This works by making the systemace_get_dev function + * available to cmd_fat.c:get_dev and filling in a block device + * description that has all the bits needed for FAT support to + * read sectors. + * + * According to Xilinx technical support, before accessing the + * SystemACE CF you need to set the following control bits: + * FORCECFGMODE : 1 + * CFGMODE : 0 + * CFGSTART : 0 + */ + +#include +#include +#include +#include +#include + +/* + * The ace_readw and writew functions read/write 16bit words, but the + * offset value is the BYTE offset as most used in the Xilinx + * datasheet for the SystemACE chip. The CONFIG_SYS_SYSTEMACE_BASE is defined + * to be the base address for the chip, usually in the local + * peripheral bus. + */ + +static u32 base = CONFIG_SYS_SYSTEMACE_BASE; +static u32 width = CONFIG_SYS_SYSTEMACE_WIDTH; + +static void ace_writew(u16 val, unsigned off) +{ + if (width == 8) { +#if !defined(__BIG_ENDIAN) + writeb(val >> 8, base + off); + writeb(val, base + off + 1); +#else + writeb(val, base + off); + writeb(val >> 8, base + off + 1); +#endif + } else + out16(base + off, val); +} + +static u16 ace_readw(unsigned off) +{ + if (width == 8) { +#if !defined(__BIG_ENDIAN) + return (readb(base + off) << 8) | readb(base + off + 1); +#else + return readb(base + off) | (readb(base + off + 1) << 8); +#endif + } + + return in16(base + off); +} + +static unsigned long systemace_read(int dev, unsigned long start, + lbaint_t blkcnt, void *buffer); + +static block_dev_desc_t systemace_dev = { 0 }; + +static int get_cf_lock(void) +{ + int retry = 10; + + /* CONTROLREG = LOCKREG */ + unsigned val = ace_readw(0x18); + val |= 0x0002; + ace_writew((val & 0xffff), 0x18); + + /* Wait for MPULOCK in STATUSREG[15:0] */ + while (!(ace_readw(0x04) & 0x0002)) { + + if (retry < 0) + return -1; + + udelay(100000); + retry -= 1; + } + + return 0; +} + +static void release_cf_lock(void) +{ + unsigned val = ace_readw(0x18); + val &= ~(0x0002); + ace_writew((val & 0xffff), 0x18); +} + +#ifdef CONFIG_PARTITIONS +block_dev_desc_t *systemace_get_dev(int dev) +{ + /* The first time through this, the systemace_dev object is + not yet initialized. In that case, fill it in. */ + if (systemace_dev.blksz == 0) { + systemace_dev.if_type = IF_TYPE_UNKNOWN; + systemace_dev.dev = 0; + systemace_dev.part_type = PART_TYPE_UNKNOWN; + systemace_dev.type = DEV_TYPE_HARDDISK; + systemace_dev.blksz = 512; + systemace_dev.log2blksz = LOG2(systemace_dev.blksz); + systemace_dev.removable = 1; + systemace_dev.block_read = systemace_read; + + /* + * Ensure the correct bus mode (8/16 bits) gets enabled + */ + ace_writew(width == 8 ? 0 : 0x0001, 0); + + init_part(&systemace_dev); + + } + + return &systemace_dev; +} +#endif + +/* + * This function is called (by dereferencing the block_read pointer in + * the dev_desc) to read blocks of data. The return value is the + * number of blocks read. A zero return indicates an error. + */ +static unsigned long systemace_read(int dev, unsigned long start, + lbaint_t blkcnt, void *buffer) +{ + int retry; + unsigned blk_countdown; + unsigned char *dp = buffer; + unsigned val; + + if (get_cf_lock() < 0) { + unsigned status = ace_readw(0x04); + + /* If CFDETECT is false, card is missing. */ + if (!(status & 0x0010)) { + printf("** CompactFlash card not present. **\n"); + return 0; + } + + printf("**** ACE locked away from me (STATUSREG=%04x)\n", + status); + return 0; + } +#ifdef DEBUG_SYSTEMACE + printf("... systemace read %lu sectors at %lu\n", blkcnt, start); +#endif + + retry = 2000; + for (;;) { + val = ace_readw(0x04); + + /* If CFDETECT is false, card is missing. */ + if (!(val & 0x0010)) { + printf("**** ACE CompactFlash not found.\n"); + release_cf_lock(); + return 0; + } + + /* If RDYFORCMD, then we are ready to go. */ + if (val & 0x0100) + break; + + if (retry < 0) { + printf("**** SystemACE not ready.\n"); + release_cf_lock(); + return 0; + } + + udelay(1000); + retry -= 1; + } + + /* The SystemACE can only transfer 256 sectors at a time, so + limit the current chunk of sectors. The blk_countdown + variable is the number of sectors left to transfer. */ + + blk_countdown = blkcnt; + while (blk_countdown > 0) { + unsigned trans = blk_countdown; + + if (trans > 256) + trans = 256; + +#ifdef DEBUG_SYSTEMACE + printf("... transfer %lu sector in a chunk\n", trans); +#endif + /* Write LBA block address */ + ace_writew((start >> 0) & 0xffff, 0x10); + ace_writew((start >> 16) & 0x0fff, 0x12); + + /* NOTE: in the Write Sector count below, a count of 0 + causes a transfer of 256, so &0xff gives the right + value for whatever transfer count we want. */ + + /* Write sector count | ReadMemCardData. */ + ace_writew((trans & 0xff) | 0x0300, 0x14); + +/* + * For FPGA configuration via SystemACE is reset unacceptable + * CFGDONE bit in STATUSREG is not set to 1. + */ +#ifndef SYSTEMACE_CONFIG_FPGA + /* Reset the configruation controller */ + val = ace_readw(0x18); + val |= 0x0080; + ace_writew(val, 0x18); +#endif + + retry = trans * 16; + while (retry > 0) { + int idx; + + /* Wait for buffer to become ready. */ + while (!(ace_readw(0x04) & 0x0020)) { + udelay(100); + } + + /* Read 16 words of 2bytes from the sector buffer. */ + for (idx = 0; idx < 16; idx += 1) { + unsigned short val = ace_readw(0x40); + *dp++ = val & 0xff; + *dp++ = (val >> 8) & 0xff; + } + + retry -= 1; + } + + /* Clear the configruation controller reset */ + val = ace_readw(0x18); + val &= ~0x0080; + ace_writew(val, 0x18); + + /* Count the blocks we transfer this time. */ + start += trans; + blk_countdown -= trans; + } + + release_cf_lock(); + + return blkcnt; +} diff --git a/qemu/roms/u-boot/drivers/bootcount/Makefile b/qemu/roms/u-boot/drivers/bootcount/Makefile new file mode 100644 index 000000000..6f1c419c7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bootcount/Makefile @@ -0,0 +1,12 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += bootcount.o +obj-$(CONFIG_AT91SAM9XE) += bootcount_at91.o +obj-$(CONFIG_BLACKFIN) += bootcount_blackfin.o +obj-$(CONFIG_SOC_DA8XX) += bootcount_davinci.o +obj-$(CONFIG_BOOTCOUNT_AM33XX) += bootcount_davinci.o +obj-$(CONFIG_BOOTCOUNT_RAM) += bootcount_ram.o +obj-$(CONFIG_BOOTCOUNT_ENV) += bootcount_env.o +obj-$(CONFIG_BOOTCOUNT_I2C) += bootcount_i2c.o diff --git a/qemu/roms/u-boot/drivers/bootcount/bootcount.c b/qemu/roms/u-boot/drivers/bootcount/bootcount.c new file mode 100644 index 000000000..3ad441393 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bootcount/bootcount.c @@ -0,0 +1,84 @@ +/* + * (C) Copyright 2010-2012 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +/* + * Only override CONFIG_SYS_BOOTCOUNT_ADDR if not already defined. This + * way, some boards can define it directly in their config header. + */ +#if !defined(CONFIG_SYS_BOOTCOUNT_ADDR) + +#if defined(CONFIG_MPC5xxx) +#define CONFIG_SYS_BOOTCOUNT_ADDR (MPC5XXX_CDM_BRDCRMB) +#define CONFIG_SYS_BOOTCOUNT_SINGLEWORD +#endif /* defined(CONFIG_MPC5xxx) */ + +#if defined(CONFIG_MPC512X) +#define CONFIG_SYS_BOOTCOUNT_ADDR (&((immap_t *)CONFIG_SYS_IMMR)->clk.bcr) +#define CONFIG_SYS_BOOTCOUNT_SINGLEWORD +#endif /* defined(CONFIG_MPC512X) */ + +#if defined(CONFIG_8xx) +#define CONFIG_SYS_BOOTCOUNT_ADDR (((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + \ + CPM_BOOTCOUNT_ADDR) +#endif /* defined(CONFIG_8xx) */ + +#if defined(CONFIG_MPC8260) +#include + +#define CONFIG_SYS_BOOTCOUNT_ADDR (CONFIG_SYS_IMMR + CPM_BOOTCOUNT_ADDR) +#endif /* defined(CONFIG_MPC8260) */ + +#if defined(CONFIG_QE) +#include + +#define CONFIG_SYS_BOOTCOUNT_ADDR (CONFIG_SYS_IMMR + 0x110000 + \ + QE_MURAM_SIZE - 2 * sizeof(u32)) +#endif /* defined(CONFIG_MPC8360) */ + +#if defined(CONFIG_4xx) +#define CONFIG_SYS_BOOTCOUNT_ADDR (CONFIG_SYS_OCM_DATA_ADDR + \ + CONFIG_SYS_BOOTCOUNT_ADDR) +#endif /* defined(CONFIG_4xx) */ + +#endif /* !defined(CONFIG_SYS_BOOTCOUNT_ADDR) */ + +/* Now implement the generic default functions */ +#if defined(CONFIG_SYS_BOOTCOUNT_ADDR) +__weak void bootcount_store(ulong a) +{ + void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR; + +#if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD) + raw_bootcount_store(reg, (BOOTCOUNT_MAGIC & 0xffff0000) | a); +#else + raw_bootcount_store(reg, a); + raw_bootcount_store(reg + 4, BOOTCOUNT_MAGIC); +#endif +} + +__weak ulong bootcount_load(void) +{ + void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR; + +#if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD) + u32 tmp = raw_bootcount_load(reg); + + if ((tmp & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000)) + return 0; + else + return (tmp & 0x0000ffff); +#else + if (raw_bootcount_load(reg + 4) != BOOTCOUNT_MAGIC) + return 0; + else + return raw_bootcount_load(reg); +#endif +} +#endif diff --git a/qemu/roms/u-boot/drivers/bootcount/bootcount_at91.c b/qemu/roms/u-boot/drivers/bootcount/bootcount_at91.c new file mode 100644 index 000000000..9c2bbfaf6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bootcount/bootcount_at91.c @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* + * We combine the BOOTCOUNT_MAGIC and bootcount in one 32-bit register. + * This is done so we need to use only one of the four GPBR registers. + */ +void bootcount_store(ulong a) +{ + at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR; + + writel((BOOTCOUNT_MAGIC & 0xffff0000) | (a & 0x0000ffff), + &gpbr->reg[AT91_GPBR_INDEX_BOOTCOUNT]); +} + +ulong bootcount_load(void) +{ + at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR; + + ulong val = readl(&gpbr->reg[AT91_GPBR_INDEX_BOOTCOUNT]); + if ((val & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000)) + return 0; + else + return val & 0x0000ffff; +} diff --git a/qemu/roms/u-boot/drivers/bootcount/bootcount_blackfin.c b/qemu/roms/u-boot/drivers/bootcount/bootcount_blackfin.c new file mode 100644 index 000000000..6cf6dd58b --- /dev/null +++ b/qemu/roms/u-boot/drivers/bootcount/bootcount_blackfin.c @@ -0,0 +1,34 @@ +/* + * functions for handling bootcount support + * + * Copyright (c) 2010 Analog Devices Inc. + * + * Licensed under the 2-clause BSD. + */ + +/* This version uses one 32bit storage and combines the magic/count */ + +#include + +/* We abuse the EVT0 MMR for bootcount storage by default */ +#ifndef CONFIG_SYS_BOOTCOUNT_ADDR +# define CONFIG_SYS_BOOTCOUNT_ADDR EVT0 +#endif + +#define MAGIC_MASK 0xffff0000 +#define COUNT_MASK 0x0000ffff + +void bootcount_store(ulong cnt) +{ + ulong magic = (BOOTCOUNT_MAGIC & MAGIC_MASK) | (cnt & COUNT_MASK); + bfin_write32(CONFIG_SYS_BOOTCOUNT_ADDR, magic); +} + +ulong bootcount_load(void) +{ + ulong magic = bfin_read32(CONFIG_SYS_BOOTCOUNT_ADDR); + if ((magic & MAGIC_MASK) == (BOOTCOUNT_MAGIC & MAGIC_MASK)) + return magic & COUNT_MASK; + else + return 0; +} diff --git a/qemu/roms/u-boot/drivers/bootcount/bootcount_davinci.c b/qemu/roms/u-boot/drivers/bootcount/bootcount_davinci.c new file mode 100644 index 000000000..fa87b5e7b --- /dev/null +++ b/qemu/roms/u-boot/drivers/bootcount/bootcount_davinci.c @@ -0,0 +1,42 @@ +/* + * (C) Copyright 2011 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * A bootcount driver for the RTC IP block found on many TI platforms. + * This requires the RTC clocks, etc, to be enabled prior to use and + * not all boards with this IP block on it will have the RTC in use. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +void bootcount_store(ulong a) +{ + struct davinci_rtc *reg = + (struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR; + + /* + * write RTC kick register to enable write + * for RTC Scratch registers. Scratch0 and 1 are + * used for bootcount values. + */ + writel(RTC_KICK0R_WE, ®->kick0r); + writel(RTC_KICK1R_WE, ®->kick1r); + raw_bootcount_store(®->scratch2, + (BOOTCOUNT_MAGIC & 0xffff0000) | (a & 0x0000ffff)); +} + +ulong bootcount_load(void) +{ + unsigned long val; + struct davinci_rtc *reg = + (struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR; + + val = raw_bootcount_load(®->scratch2); + if ((val & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000)) + return 0; + else + return val & 0x0000ffff; +} diff --git a/qemu/roms/u-boot/drivers/bootcount/bootcount_env.c b/qemu/roms/u-boot/drivers/bootcount/bootcount_env.c new file mode 100644 index 000000000..2d6e8db12 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bootcount/bootcount_env.c @@ -0,0 +1,29 @@ +/* + * (C) Copyright 2013 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +void bootcount_store(ulong a) +{ + int upgrade_available = getenv_ulong("upgrade_available", 10, 0); + + if (upgrade_available) { + setenv_ulong("bootcount", a); + saveenv(); + } +} + +ulong bootcount_load(void) +{ + int upgrade_available = getenv_ulong("upgrade_available", 10, 0); + ulong val = 0; + + if (upgrade_available) + val = getenv_ulong("bootcount", 10, 0); + + return val; +} diff --git a/qemu/roms/u-boot/drivers/bootcount/bootcount_i2c.c b/qemu/roms/u-boot/drivers/bootcount/bootcount_i2c.c new file mode 100644 index 000000000..e27b168c5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bootcount/bootcount_i2c.c @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2013 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define BC_MAGIC 0xbc + +void bootcount_store(ulong a) +{ + unsigned char buf[3]; + int ret; + + buf[0] = BC_MAGIC; + buf[1] = (a & 0xff); + ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR, + CONFIG_BOOTCOUNT_ALEN, buf, 2); + if (ret != 0) + puts("Error writing bootcount\n"); +} + +ulong bootcount_load(void) +{ + unsigned char buf[3]; + int ret; + + ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR, + CONFIG_BOOTCOUNT_ALEN, buf, 2); + if (ret != 0) { + puts("Error loading bootcount\n"); + return 0; + } + if (buf[0] == BC_MAGIC) + return buf[1]; + + bootcount_store(0); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/bootcount/bootcount_ram.c b/qemu/roms/u-boot/drivers/bootcount/bootcount_ram.c new file mode 100644 index 000000000..5bdabcd56 --- /dev/null +++ b/qemu/roms/u-boot/drivers/bootcount/bootcount_ram.c @@ -0,0 +1,60 @@ +/* + * (C) Copyright 2010 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +const ulong patterns[] = { 0x00000000, + 0xFFFFFFFF, + 0xFF00FF00, + 0x0F0F0F0F, + 0xF0F0F0F0}; +const ulong NBR_OF_PATTERNS = sizeof(patterns) / sizeof(*patterns); +const ulong OFFS_PATTERN = 3; +const ulong REPEAT_PATTERN = 1000; + +void bootcount_store(ulong a) +{ + ulong *save_addr; + ulong size = 0; + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) + size += gd->bd->bi_dram[i].size; + save_addr = (ulong *)(size - BOOTCOUNT_ADDR); + writel(a, save_addr); + writel(BOOTCOUNT_MAGIC, &save_addr[1]); + + for (i = 0; i < REPEAT_PATTERN; i++) + writel(patterns[i % NBR_OF_PATTERNS], + &save_addr[i + OFFS_PATTERN]); + +} + +ulong bootcount_load(void) +{ + ulong *save_addr; + ulong size = 0; + ulong counter = 0; + int i, tmp; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) + size += gd->bd->bi_dram[i].size; + save_addr = (ulong *)(size - BOOTCOUNT_ADDR); + + counter = readl(&save_addr[0]); + + /* Is the counter reliable, check in the big pattern for bit errors */ + for (i = 0; (i < REPEAT_PATTERN) && (counter != 0); i++) { + tmp = readl(&save_addr[i + OFFS_PATTERN]); + if (tmp != patterns[i % NBR_OF_PATTERNS]) + counter = 0; + } + return counter; +} diff --git a/qemu/roms/u-boot/drivers/core/Makefile b/qemu/roms/u-boot/drivers/core/Makefile new file mode 100644 index 000000000..90b2a7f06 --- /dev/null +++ b/qemu/roms/u-boot/drivers/core/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (c) 2013 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_DM) := device.o lists.o root.o uclass.o util.o diff --git a/qemu/roms/u-boot/drivers/core/device.c b/qemu/roms/u-boot/drivers/core/device.c new file mode 100644 index 000000000..55ba281be --- /dev/null +++ b/qemu/roms/u-boot/drivers/core/device.c @@ -0,0 +1,348 @@ +/* + * Device manager + * + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * device_chld_unbind() - Unbind all device's children from the device + * + * On error, the function continues to unbind all children, and reports the + * first error. + * + * @dev: The device that is to be stripped of its children + * @return 0 on success, -ve on error + */ +static int device_chld_unbind(struct device *dev) +{ + struct device *pos, *n; + int ret, saved_ret = 0; + + assert(dev); + + list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { + ret = device_unbind(pos); + if (ret && !saved_ret) + saved_ret = ret; + } + + return saved_ret; +} + +/** + * device_chld_remove() - Stop all device's children + * @dev: The device whose children are to be removed + * @return 0 on success, -ve on error + */ +static int device_chld_remove(struct device *dev) +{ + struct device *pos, *n; + int ret; + + assert(dev); + + list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { + ret = device_remove(pos); + if (ret) + return ret; + } + + return 0; +} + +int device_bind(struct device *parent, struct driver *drv, const char *name, + void *platdata, int of_offset, struct device **devp) +{ + struct device *dev; + struct uclass *uc; + int ret = 0; + + *devp = NULL; + if (!name) + return -EINVAL; + + ret = uclass_get(drv->id, &uc); + if (ret) + return ret; + + dev = calloc(1, sizeof(struct device)); + if (!dev) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->sibling_node); + INIT_LIST_HEAD(&dev->child_head); + INIT_LIST_HEAD(&dev->uclass_node); + dev->platdata = platdata; + dev->name = name; + dev->of_offset = of_offset; + dev->parent = parent; + dev->driver = drv; + dev->uclass = uc; + if (!dev->platdata && drv->platdata_auto_alloc_size) + dev->flags |= DM_FLAG_ALLOC_PDATA; + + /* put dev into parent's successor list */ + if (parent) + list_add_tail(&dev->sibling_node, &parent->child_head); + + ret = uclass_bind_device(dev); + if (ret) + goto fail_bind; + + /* if we fail to bind we remove device from successors and free it */ + if (drv->bind) { + ret = drv->bind(dev); + if (ret) { + if (uclass_unbind_device(dev)) { + dm_warn("Failed to unbind dev '%s' on error path\n", + dev->name); + } + goto fail_bind; + } + } + if (parent) + dm_dbg("Bound device %s to %s\n", dev->name, parent->name); + *devp = dev; + + return 0; + +fail_bind: + list_del(&dev->sibling_node); + free(dev); + return ret; +} + +int device_bind_by_name(struct device *parent, const struct driver_info *info, + struct device **devp) +{ + struct driver *drv; + + drv = lists_driver_lookup_name(info->name); + if (!drv) + return -ENOENT; + + return device_bind(parent, drv, info->name, (void *)info->platdata, + -1, devp); +} + +int device_unbind(struct device *dev) +{ + struct driver *drv; + int ret; + + if (!dev) + return -EINVAL; + + if (dev->flags & DM_FLAG_ACTIVATED) + return -EINVAL; + + drv = dev->driver; + assert(drv); + + if (drv->unbind) { + ret = drv->unbind(dev); + if (ret) + return ret; + } + + ret = device_chld_unbind(dev); + if (ret) + return ret; + + ret = uclass_unbind_device(dev); + if (ret) + return ret; + + if (dev->parent) + list_del(&dev->sibling_node); + free(dev); + + return 0; +} + +/** + * device_free() - Free memory buffers allocated by a device + * @dev: Device that is to be started + */ +static void device_free(struct device *dev) +{ + int size; + + if (dev->driver->priv_auto_alloc_size) { + free(dev->priv); + dev->priv = NULL; + } + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + free(dev->platdata); + dev->platdata = NULL; + } + size = dev->uclass->uc_drv->per_device_auto_alloc_size; + if (size) { + free(dev->uclass_priv); + dev->uclass_priv = NULL; + } +} + +int device_probe(struct device *dev) +{ + struct driver *drv; + int size = 0; + int ret; + + if (!dev) + return -EINVAL; + + if (dev->flags & DM_FLAG_ACTIVATED) + return 0; + + drv = dev->driver; + assert(drv); + + /* Allocate private data and platdata if requested */ + if (drv->priv_auto_alloc_size) { + dev->priv = calloc(1, drv->priv_auto_alloc_size); + if (!dev->priv) { + ret = -ENOMEM; + goto fail; + } + } + /* Allocate private data if requested */ + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + dev->platdata = calloc(1, drv->platdata_auto_alloc_size); + if (!dev->platdata) { + ret = -ENOMEM; + goto fail; + } + } + size = dev->uclass->uc_drv->per_device_auto_alloc_size; + if (size) { + dev->uclass_priv = calloc(1, size); + if (!dev->uclass_priv) { + ret = -ENOMEM; + goto fail; + } + } + + /* Ensure all parents are probed */ + if (dev->parent) { + ret = device_probe(dev->parent); + if (ret) + goto fail; + } + + if (drv->ofdata_to_platdata && dev->of_offset >= 0) { + ret = drv->ofdata_to_platdata(dev); + if (ret) + goto fail; + } + + if (drv->probe) { + ret = drv->probe(dev); + if (ret) + goto fail; + } + + dev->flags |= DM_FLAG_ACTIVATED; + + ret = uclass_post_probe_device(dev); + if (ret) { + dev->flags &= ~DM_FLAG_ACTIVATED; + goto fail_uclass; + } + + return 0; +fail_uclass: + if (device_remove(dev)) { + dm_warn("%s: Device '%s' failed to remove on error path\n", + __func__, dev->name); + } +fail: + device_free(dev); + + return ret; +} + +int device_remove(struct device *dev) +{ + struct driver *drv; + int ret; + + if (!dev) + return -EINVAL; + + if (!(dev->flags & DM_FLAG_ACTIVATED)) + return 0; + + drv = dev->driver; + assert(drv); + + ret = uclass_pre_remove_device(dev); + if (ret) + return ret; + + ret = device_chld_remove(dev); + if (ret) + goto err; + + if (drv->remove) { + ret = drv->remove(dev); + if (ret) + goto err_remove; + } + + device_free(dev); + + dev->flags &= ~DM_FLAG_ACTIVATED; + + return 0; + +err_remove: + /* We can't put the children back */ + dm_warn("%s: Device '%s' failed to remove, but children are gone\n", + __func__, dev->name); +err: + ret = uclass_post_probe_device(dev); + if (ret) { + dm_warn("%s: Device '%s' failed to post_probe on error path\n", + __func__, dev->name); + } + + return ret; +} + +void *dev_get_platdata(struct device *dev) +{ + if (!dev) { + dm_warn("%s: null device", __func__); + return NULL; + } + + return dev->platdata; +} + +void *dev_get_priv(struct device *dev) +{ + if (!dev) { + dm_warn("%s: null device", __func__); + return NULL; + } + + return dev->priv; +} diff --git a/qemu/roms/u-boot/drivers/core/lists.c b/qemu/roms/u-boot/drivers/core/lists.c new file mode 100644 index 000000000..4f2c12631 --- /dev/null +++ b/qemu/roms/u-boot/drivers/core/lists.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct driver *lists_driver_lookup_name(const char *name) +{ + struct driver *drv = + ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + struct driver *entry; + int len; + + if (!drv || !n_ents) + return NULL; + + len = strlen(name); + + for (entry = drv; entry != drv + n_ents; entry++) { + if (strncmp(name, entry->name, len)) + continue; + + /* Full match */ + if (len == strlen(entry->name)) + return entry; + } + + /* Not found */ + return NULL; +} + +struct uclass_driver *lists_uclass_lookup(enum uclass_id id) +{ + struct uclass_driver *uclass = + ll_entry_start(struct uclass_driver, uclass); + const int n_ents = ll_entry_count(struct uclass_driver, uclass); + struct uclass_driver *entry; + + if ((id == UCLASS_INVALID) || !uclass) + return NULL; + + for (entry = uclass; entry != uclass + n_ents; entry++) { + if (entry->id == id) + return entry; + } + + return NULL; +} + +int lists_bind_drivers(struct device *parent) +{ + struct driver_info *info = + ll_entry_start(struct driver_info, driver_info); + const int n_ents = ll_entry_count(struct driver_info, driver_info); + struct driver_info *entry; + struct device *dev; + int result = 0; + int ret; + + for (entry = info; entry != info + n_ents; entry++) { + ret = device_bind_by_name(parent, entry, &dev); + if (ret) { + dm_warn("No match for driver '%s'\n", entry->name); + if (!result || ret != -ENOENT) + result = ret; + } + } + + return result; +} + +#ifdef CONFIG_OF_CONTROL +/** + * driver_check_compatible() - Check if a driver is compatible with this node + * + * @param blob: Device tree pointer + * @param offset: Offset of node in device tree + * @param of_matchL List of compatible strings to match + * @return 0 if there is a match, -ENOENT if no match, -ENODEV if the node + * does not have a compatible string, other error <0 if there is a device + * tree error + */ +static int driver_check_compatible(const void *blob, int offset, + const struct device_id *of_match) +{ + int ret; + + if (!of_match) + return -ENOENT; + + while (of_match->compatible) { + ret = fdt_node_check_compatible(blob, offset, + of_match->compatible); + if (!ret) + return 0; + else if (ret == -FDT_ERR_NOTFOUND) + return -ENODEV; + else if (ret < 0) + return -EINVAL; + of_match++; + } + + return -ENOENT; +} + +int lists_bind_fdt(struct device *parent, const void *blob, int offset) +{ + struct driver *driver = ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + struct driver *entry; + struct device *dev; + const char *name; + int result = 0; + int ret; + + dm_dbg("bind node %s\n", fdt_get_name(blob, offset, NULL)); + for (entry = driver; entry != driver + n_ents; entry++) { + ret = driver_check_compatible(blob, offset, entry->of_match); + if (ret == -ENOENT) { + continue; + } else if (ret == -ENODEV) { + break; + } else if (ret) { + dm_warn("Device tree error at offset %d\n", offset); + if (!result || ret != -ENOENT) + result = ret; + break; + } + + name = fdt_get_name(blob, offset, NULL); + dm_dbg(" - found match at '%s'\n", entry->name); + ret = device_bind(parent, entry, name, NULL, offset, &dev); + if (ret) { + dm_warn("No match for driver '%s'\n", entry->name); + if (!result || ret != -ENOENT) + result = ret; + } + } + + return result; +} +#endif diff --git a/qemu/roms/u-boot/drivers/core/root.c b/qemu/roms/u-boot/drivers/core/root.c new file mode 100644 index 000000000..407bc0d04 --- /dev/null +++ b/qemu/roms/u-boot/drivers/core/root.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static const struct driver_info root_info = { + .name = "root_driver", +}; + +struct device *dm_root(void) +{ + if (!gd->dm_root) { + dm_warn("Virtual root driver does not exist!\n"); + return NULL; + } + + return gd->dm_root; +} + +int dm_init(void) +{ + int ret; + + if (gd->dm_root) { + dm_warn("Virtual root driver already exists!\n"); + return -EINVAL; + } + INIT_LIST_HEAD(&gd->uclass_root); + + ret = device_bind_by_name(NULL, &root_info, &gd->dm_root); + if (ret) + return ret; + + return 0; +} + +int dm_scan_platdata(void) +{ + int ret; + + ret = lists_bind_drivers(gd->dm_root); + if (ret == -ENOENT) { + dm_warn("Some drivers were not found\n"); + ret = 0; + } + if (ret) + return ret; + + return 0; +} + +#ifdef CONFIG_OF_CONTROL +int dm_scan_fdt(const void *blob) +{ + int offset = 0; + int ret = 0, err; + int depth = 0; + + do { + offset = fdt_next_node(blob, offset, &depth); + if (offset > 0 && depth == 1) { + err = lists_bind_fdt(gd->dm_root, blob, offset); + if (err && !ret) + ret = err; + } + } while (offset > 0); + + if (ret) + dm_warn("Some drivers failed to bind\n"); + + return ret; +} +#endif + +/* This is the root driver - all drivers are children of this */ +U_BOOT_DRIVER(root_driver) = { + .name = "root_driver", + .id = UCLASS_ROOT, +}; + +/* This is the root uclass */ +UCLASS_DRIVER(root) = { + .name = "root", + .id = UCLASS_ROOT, +}; diff --git a/qemu/roms/u-boot/drivers/core/uclass.c b/qemu/roms/u-boot/drivers/core/uclass.c new file mode 100644 index 000000000..4df5a8bd3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/core/uclass.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct uclass *uclass_find(enum uclass_id key) +{ + struct uclass *uc; + + /* + * TODO(sjg@chromium.org): Optimise this, perhaps moving the found + * node to the start of the list, or creating a linear array mapping + * id to node. + */ + list_for_each_entry(uc, &gd->uclass_root, sibling_node) { + if (uc->uc_drv->id == key) + return uc; + } + + return NULL; +} + +/** + * uclass_add() - Create new uclass in list + * @id: Id number to create + * @ucp: Returns pointer to uclass, or NULL on error + * @return 0 on success, -ve on error + * + * The new uclass is added to the list. There must be only one uclass for + * each id. + */ +static int uclass_add(enum uclass_id id, struct uclass **ucp) +{ + struct uclass_driver *uc_drv; + struct uclass *uc; + int ret; + + *ucp = NULL; + uc_drv = lists_uclass_lookup(id); + if (!uc_drv) { + dm_warn("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", + id); + return -ENOENT; + } + if (uc_drv->ops) { + dm_warn("No ops for uclass id %d\n", id); + return -EINVAL; + } + uc = calloc(1, sizeof(*uc)); + if (!uc) + return -ENOMEM; + if (uc_drv->priv_auto_alloc_size) { + uc->priv = calloc(1, uc_drv->priv_auto_alloc_size); + if (!uc->priv) { + ret = -ENOMEM; + goto fail_mem; + } + } + uc->uc_drv = uc_drv; + INIT_LIST_HEAD(&uc->sibling_node); + INIT_LIST_HEAD(&uc->dev_head); + list_add(&uc->sibling_node, &gd->uclass_root); + + if (uc_drv->init) { + ret = uc_drv->init(uc); + if (ret) + goto fail; + } + + *ucp = uc; + + return 0; +fail: + if (uc_drv->priv_auto_alloc_size) { + free(uc->priv); + uc->priv = NULL; + } + list_del(&uc->sibling_node); +fail_mem: + free(uc); + + return ret; +} + +int uclass_destroy(struct uclass *uc) +{ + struct uclass_driver *uc_drv; + struct device *dev, *tmp; + int ret; + + list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) { + ret = device_remove(dev); + if (ret) + return ret; + ret = device_unbind(dev); + if (ret) + return ret; + } + + uc_drv = uc->uc_drv; + if (uc_drv->destroy) + uc_drv->destroy(uc); + list_del(&uc->sibling_node); + if (uc_drv->priv_auto_alloc_size) + free(uc->priv); + free(uc); + + return 0; +} + +int uclass_get(enum uclass_id id, struct uclass **ucp) +{ + struct uclass *uc; + + *ucp = NULL; + uc = uclass_find(id); + if (!uc) + return uclass_add(id, ucp); + *ucp = uc; + + return 0; +} + +int uclass_find_device(enum uclass_id id, int index, struct device **devp) +{ + struct uclass *uc; + struct device *dev; + int ret; + + *devp = NULL; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (!index--) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int uclass_get_device(enum uclass_id id, int index, struct device **devp) +{ + struct device *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device(id, index, &dev); + if (ret) + return ret; + + ret = device_probe(dev); + if (ret) + return ret; + + *devp = dev; + + return 0; +} + +int uclass_first_device(enum uclass_id id, struct device **devp) +{ + struct uclass *uc; + struct device *dev; + int ret; + + *devp = NULL; + ret = uclass_get(id, &uc); + if (ret) + return ret; + if (list_empty(&uc->dev_head)) + return 0; + + dev = list_first_entry(&uc->dev_head, struct device, uclass_node); + ret = device_probe(dev); + if (ret) + return ret; + *devp = dev; + + return 0; +} + +int uclass_next_device(struct device **devp) +{ + struct device *dev = *devp; + int ret; + + *devp = NULL; + if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head)) + return 0; + + dev = list_entry(dev->uclass_node.next, struct device, uclass_node); + ret = device_probe(dev); + if (ret) + return ret; + *devp = dev; + + return 0; +} + +int uclass_bind_device(struct device *dev) +{ + struct uclass *uc; + int ret; + + uc = dev->uclass; + + list_add_tail(&dev->uclass_node, &uc->dev_head); + + if (uc->uc_drv->post_bind) { + ret = uc->uc_drv->post_bind(dev); + if (ret) { + list_del(&dev->uclass_node); + return ret; + } + } + + return 0; +} + +int uclass_unbind_device(struct device *dev) +{ + struct uclass *uc; + int ret; + + uc = dev->uclass; + if (uc->uc_drv->pre_unbind) { + ret = uc->uc_drv->pre_unbind(dev); + if (ret) + return ret; + } + + list_del(&dev->uclass_node); + return 0; +} + +int uclass_post_probe_device(struct device *dev) +{ + struct uclass_driver *uc_drv = dev->uclass->uc_drv; + + if (uc_drv->post_probe) + return uc_drv->post_probe(dev); + + return 0; +} + +int uclass_pre_remove_device(struct device *dev) +{ + struct uclass_driver *uc_drv; + struct uclass *uc; + int ret; + + uc = dev->uclass; + uc_drv = uc->uc_drv; + if (uc->uc_drv->pre_remove) { + ret = uc->uc_drv->pre_remove(dev); + if (ret) + return ret; + } + if (uc_drv->per_device_auto_alloc_size) { + free(dev->uclass_priv); + dev->uclass_priv = NULL; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/core/util.c b/qemu/roms/u-boot/drivers/core/util.c new file mode 100644 index 000000000..e01dd06d2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/core/util.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +void dm_warn(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +void dm_dbg(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +int list_count_items(struct list_head *head) +{ + struct list_head *node; + int count = 0; + + list_for_each(node, head) + count++; + + return count; +} diff --git a/qemu/roms/u-boot/drivers/crypto/Makefile b/qemu/roms/u-boot/drivers/crypto/Makefile new file mode 100644 index 000000000..b8077953c --- /dev/null +++ b/qemu/roms/u-boot/drivers/crypto/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (c) 2013 Samsung Electronics Co., Ltd. +# http://www.samsung.com +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.o diff --git a/qemu/roms/u-boot/drivers/crypto/ace_sha.c b/qemu/roms/u-boot/drivers/crypto/ace_sha.c new file mode 100644 index 000000000..ed4f54182 --- /dev/null +++ b/qemu/roms/u-boot/drivers/crypto/ace_sha.c @@ -0,0 +1,184 @@ +/* + * Advanced Crypto Engine - SHA Firmware + * Copyright (c) 2012 Samsung Electronics + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include "ace_sha.h" + +#ifdef CONFIG_SHA_HW_ACCEL +#include +#include +#include + +/* SHA1 value for the message of zero length */ +static const unsigned char sha1_digest_emptymsg[SHA1_SUM_LEN] = { + 0xDA, 0x39, 0xA3, 0xEE, 0x5E, 0x6B, 0x4B, 0x0D, + 0x32, 0x55, 0xBF, 0xFF, 0x95, 0x60, 0x18, 0x90, + 0xAF, 0xD8, 0x07, 0x09}; + +/* SHA256 value for the message of zero length */ +static const unsigned char sha256_digest_emptymsg[SHA256_SUM_LEN] = { + 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, + 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, + 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, + 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55}; + +int ace_sha_hash_digest(const unsigned char *pbuf, unsigned int buf_len, + unsigned char *pout, unsigned int hash_type) +{ + unsigned int i, reg, len; + unsigned int *pdigest; + struct exynos_ace_sfr *ace_sha_reg = + (struct exynos_ace_sfr *)samsung_get_base_ace_sfr(); + + if (buf_len == 0) { + /* ACE H/W cannot compute hash value for empty string */ + if (hash_type == ACE_SHA_TYPE_SHA1) + memcpy(pout, sha1_digest_emptymsg, SHA1_SUM_LEN); + else + memcpy(pout, sha256_digest_emptymsg, SHA256_SUM_LEN); + return 0; + } + + /* Flush HRDMA */ + writel(ACE_FC_HRDMACFLUSH_ON, &ace_sha_reg->fc_hrdmac); + writel(ACE_FC_HRDMACFLUSH_OFF, &ace_sha_reg->fc_hrdmac); + + /* Set byte swap of data in */ + writel(ACE_HASH_SWAPDI_ON | ACE_HASH_SWAPDO_ON | ACE_HASH_SWAPIV_ON, + &ace_sha_reg->hash_byteswap); + + /* Select Hash input mux as external source */ + reg = readl(&ace_sha_reg->fc_fifoctrl); + reg = (reg & ~ACE_FC_SELHASH_MASK) | ACE_FC_SELHASH_EXOUT; + writel(reg, &ace_sha_reg->fc_fifoctrl); + + /* Set Hash as SHA1 or SHA256 and start Hash engine */ + reg = (hash_type == ACE_SHA_TYPE_SHA1) ? + ACE_HASH_ENGSEL_SHA1HASH : ACE_HASH_ENGSEL_SHA256HASH; + reg |= ACE_HASH_STARTBIT_ON; + writel(reg, &ace_sha_reg->hash_control); + + /* Enable FIFO mode */ + writel(ACE_HASH_FIFO_ON, &ace_sha_reg->hash_fifo_mode); + + /* Set message length */ + writel(buf_len, &ace_sha_reg->hash_msgsize_low); + writel(0, &ace_sha_reg->hash_msgsize_high); + + /* Set HRDMA */ + writel((unsigned int)pbuf, &ace_sha_reg->fc_hrdmas); + writel(buf_len, &ace_sha_reg->fc_hrdmal); + + while ((readl(&ace_sha_reg->hash_status) & ACE_HASH_MSGDONE_MASK) == + ACE_HASH_MSGDONE_OFF) { + /* + * PRNG error bit goes HIGH if a PRNG request occurs without + * a complete seed setup. We are using this bit to check h/w + * fault because proper setup is not expected in that case. + */ + if ((readl(&ace_sha_reg->hash_status) + & ACE_HASH_PRNGERROR_MASK) == ACE_HASH_PRNGERROR_ON) + return -EBUSY; + } + + /* Clear MSG_DONE bit */ + writel(ACE_HASH_MSGDONE_ON, &ace_sha_reg->hash_status); + + /* Read hash result */ + pdigest = (unsigned int *)pout; + len = (hash_type == ACE_SHA_TYPE_SHA1) ? SHA1_SUM_LEN : SHA256_SUM_LEN; + + for (i = 0; i < len / 4; i++) + pdigest[i] = readl(&ace_sha_reg->hash_result[i]); + + /* Clear HRDMA pending bit */ + writel(ACE_FC_HRDMA, &ace_sha_reg->fc_intpend); + + return 0; +} + +void hw_sha256(const unsigned char *pbuf, unsigned int buf_len, + unsigned char *pout, unsigned int chunk_size) +{ + if (ace_sha_hash_digest(pbuf, buf_len, pout, ACE_SHA_TYPE_SHA256)) + debug("ACE was not setup properly or it is faulty\n"); +} + +void hw_sha1(const unsigned char *pbuf, unsigned int buf_len, + unsigned char *pout, unsigned int chunk_size) +{ + if (ace_sha_hash_digest(pbuf, buf_len, pout, ACE_SHA_TYPE_SHA1)) + debug("ACE was not setup properly or it is faulty\n"); +} +#endif /* CONFIG_SHA_HW_ACCEL */ + +#ifdef CONFIG_LIB_HW_RAND +static unsigned int seed_done; + +void srand(unsigned int seed) +{ + struct exynos_ace_sfr *reg = + (struct exynos_ace_sfr *)samsung_get_base_ace_sfr(); + int i, status; + + /* Seed data */ + for (i = 0; i < ACE_HASH_PRNG_REG_NUM; i++) + writel(seed << i, ®->hash_seed[i]); + + /* Wait for seed setup done */ + while (1) { + status = readl(®->hash_status); + if ((status & ACE_HASH_SEEDSETTING_MASK) || + (status & ACE_HASH_PRNGERROR_MASK)) + break; + } + + seed_done = 1; +} + +unsigned int rand(void) +{ + struct exynos_ace_sfr *reg = + (struct exynos_ace_sfr *)samsung_get_base_ace_sfr(); + int i, status; + unsigned int seed = (unsigned int)&status; + unsigned int ret = 0; + + if (!seed_done) + srand(seed); + + /* Start PRNG */ + writel(ACE_HASH_ENGSEL_PRNG | ACE_HASH_STARTBIT_ON, ®->hash_control); + + /* Wait for PRNG done */ + while (1) { + status = readl(®->hash_status); + if (status & ACE_HASH_PRNGDONE_MASK) + break; + if (status & ACE_HASH_PRNGERROR_MASK) { + seed_done = 0; + return 0; + } + } + + /* Clear Done IRQ */ + writel(ACE_HASH_PRNGDONE_MASK, ®->hash_status); + + /* Read a PRNG result */ + for (i = 0; i < ACE_HASH_PRNG_REG_NUM; i++) + ret += readl(®->hash_prng[i]); + + seed_done = 0; + return ret; +} + +unsigned int rand_r(unsigned int *seedp) +{ + srand(*seedp); + + return rand(); +} +#endif /* CONFIG_LIB_HW_RAND */ diff --git a/qemu/roms/u-boot/drivers/crypto/ace_sha.h b/qemu/roms/u-boot/drivers/crypto/ace_sha.h new file mode 100644 index 000000000..f1097f72d --- /dev/null +++ b/qemu/roms/u-boot/drivers/crypto/ace_sha.h @@ -0,0 +1,314 @@ +/* + * Header file for Advanced Crypto Engine - SFR definitions + * + * Copyright (c) 2012 Samsung Electronics + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ACE_SHA_H +#define __ACE_SHA_H + +struct exynos_ace_sfr { + unsigned int fc_intstat; /* base + 0 */ + unsigned int fc_intenset; + unsigned int fc_intenclr; + unsigned int fc_intpend; + unsigned int fc_fifostat; + unsigned int fc_fifoctrl; + unsigned int fc_global; + unsigned int res1; + unsigned int fc_brdmas; + unsigned int fc_brdmal; + unsigned int fc_brdmac; + unsigned int res2; + unsigned int fc_btdmas; + unsigned int fc_btdmal; + unsigned int fc_btdmac; + unsigned int res3; + unsigned int fc_hrdmas; + unsigned int fc_hrdmal; + unsigned int fc_hrdmac; + unsigned int res4; + unsigned int fc_pkdmas; + unsigned int fc_pkdmal; + unsigned int fc_pkdmac; + unsigned int fc_pkdmao; + unsigned char res5[0x1a0]; + + unsigned int aes_control; /* base + 0x200 */ + unsigned int aes_status; + unsigned char res6[0x8]; + unsigned int aes_in[4]; + unsigned int aes_out[4]; + unsigned int aes_iv[4]; + unsigned int aes_cnt[4]; + unsigned char res7[0x30]; + unsigned int aes_key[8]; + unsigned char res8[0x60]; + + unsigned int tdes_control; /* base + 0x300 */ + unsigned int tdes_status; + unsigned char res9[0x8]; + unsigned int tdes_key[6]; + unsigned int tdes_iv[2]; + unsigned int tdes_in[2]; + unsigned int tdes_out[2]; + unsigned char res10[0xc0]; + + unsigned int hash_control; /* base + 0x400 */ + unsigned int hash_control2; + unsigned int hash_fifo_mode; + unsigned int hash_byteswap; + unsigned int hash_status; + unsigned char res11[0xc]; + unsigned int hash_msgsize_low; + unsigned int hash_msgsize_high; + unsigned int hash_prelen_low; + unsigned int hash_prelen_high; + unsigned int hash_in[16]; + unsigned int hash_key_in[16]; + unsigned int hash_iv[8]; + unsigned char res12[0x30]; + unsigned int hash_result[8]; + unsigned char res13[0x20]; + unsigned int hash_seed[5]; + unsigned char res14[12]; + unsigned int hash_prng[5]; + unsigned char res15[0x18c]; + + unsigned int pka_sfr[5]; /* base + 0x700 */ +}; + +/* ACE_FC_INT */ +#define ACE_FC_PKDMA (1 << 0) +#define ACE_FC_HRDMA (1 << 1) +#define ACE_FC_BTDMA (1 << 2) +#define ACE_FC_BRDMA (1 << 3) +#define ACE_FC_PRNG_ERROR (1 << 4) +#define ACE_FC_MSG_DONE (1 << 5) +#define ACE_FC_PRNG_DONE (1 << 6) +#define ACE_FC_PARTIAL_DONE (1 << 7) + +/* ACE_FC_FIFOSTAT */ +#define ACE_FC_PKFIFO_EMPTY (1 << 0) +#define ACE_FC_PKFIFO_FULL (1 << 1) +#define ACE_FC_HRFIFO_EMPTY (1 << 2) +#define ACE_FC_HRFIFO_FULL (1 << 3) +#define ACE_FC_BTFIFO_EMPTY (1 << 4) +#define ACE_FC_BTFIFO_FULL (1 << 5) +#define ACE_FC_BRFIFO_EMPTY (1 << 6) +#define ACE_FC_BRFIFO_FULL (1 << 7) + +/* ACE_FC_FIFOCTRL */ +#define ACE_FC_SELHASH_MASK (3 << 0) +#define ACE_FC_SELHASH_EXOUT (0 << 0) /* independent source */ +#define ACE_FC_SELHASH_BCIN (1 << 0) /* blk cipher input */ +#define ACE_FC_SELHASH_BCOUT (2 << 0) /* blk cipher output */ +#define ACE_FC_SELBC_MASK (1 << 2) +#define ACE_FC_SELBC_AES (0 << 2) +#define ACE_FC_SELBC_DES (1 << 2) + +/* ACE_FC_GLOBAL */ +#define ACE_FC_SSS_RESET (1 << 0) +#define ACE_FC_DMA_RESET (1 << 1) +#define ACE_FC_AES_RESET (1 << 2) +#define ACE_FC_DES_RESET (1 << 3) +#define ACE_FC_HASH_RESET (1 << 4) +#define ACE_FC_AXI_ENDIAN_MASK (3 << 6) +#define ACE_FC_AXI_ENDIAN_LE (0 << 6) +#define ACE_FC_AXI_ENDIAN_BIBE (1 << 6) +#define ACE_FC_AXI_ENDIAN_WIBE (2 << 6) + +/* Feed control - BRDMA control */ +#define ACE_FC_BRDMACFLUSH_OFF (0 << 0) +#define ACE_FC_BRDMACFLUSH_ON (1 << 0) +#define ACE_FC_BRDMACSWAP_ON (1 << 1) +#define ACE_FC_BRDMACARPROT_MASK (0x7 << 2) +#define ACE_FC_BRDMACARPROT_OFS 2 +#define ACE_FC_BRDMACARCACHE_MASK (0xf << 5) +#define ACE_FC_BRDMACARCACHE_OFS 5 + +/* Feed control - BTDMA control */ +#define ACE_FC_BTDMACFLUSH_OFF (0 << 0) +#define ACE_FC_BTDMACFLUSH_ON (1 << 0) +#define ACE_FC_BTDMACSWAP_ON (1 << 1) +#define ACE_FC_BTDMACAWPROT_MASK (0x7 << 2) +#define ACE_FC_BTDMACAWPROT_OFS 2 +#define ACE_FC_BTDMACAWCACHE_MASK (0xf << 5) +#define ACE_FC_BTDMACAWCACHE_OFS 5 + +/* Feed control - HRDMA control */ +#define ACE_FC_HRDMACFLUSH_OFF (0 << 0) +#define ACE_FC_HRDMACFLUSH_ON (1 << 0) +#define ACE_FC_HRDMACSWAP_ON (1 << 1) +#define ACE_FC_HRDMACARPROT_MASK (0x7 << 2) +#define ACE_FC_HRDMACARPROT_OFS 2 +#define ACE_FC_HRDMACARCACHE_MASK (0xf << 5) +#define ACE_FC_HRDMACARCACHE_OFS 5 + +/* Feed control - PKDMA control */ +#define ACE_FC_PKDMACBYTESWAP_ON (1 << 3) +#define ACE_FC_PKDMACDESEND_ON (1 << 2) +#define ACE_FC_PKDMACTRANSMIT_ON (1 << 1) +#define ACE_FC_PKDMACFLUSH_ON (1 << 0) + +/* Feed control - PKDMA offset */ +#define ACE_FC_SRAMOFFSET_MASK 0xfff + +/* AES control */ +#define ACE_AES_MODE_MASK (1 << 0) +#define ACE_AES_MODE_ENC (0 << 0) +#define ACE_AES_MODE_DEC (1 << 0) +#define ACE_AES_OPERMODE_MASK (3 << 1) +#define ACE_AES_OPERMODE_ECB (0 << 1) +#define ACE_AES_OPERMODE_CBC (1 << 1) +#define ACE_AES_OPERMODE_CTR (2 << 1) +#define ACE_AES_FIFO_MASK (1 << 3) +#define ACE_AES_FIFO_OFF (0 << 3) /* CPU mode */ +#define ACE_AES_FIFO_ON (1 << 3) /* FIFO mode */ +#define ACE_AES_KEYSIZE_MASK (3 << 4) +#define ACE_AES_KEYSIZE_128 (0 << 4) +#define ACE_AES_KEYSIZE_192 (1 << 4) +#define ACE_AES_KEYSIZE_256 (2 << 4) +#define ACE_AES_KEYCNGMODE_MASK (1 << 6) +#define ACE_AES_KEYCNGMODE_OFF (0 << 6) +#define ACE_AES_KEYCNGMODE_ON (1 << 6) +#define ACE_AES_SWAP_MASK (0x1f << 7) +#define ACE_AES_SWAPKEY_OFF (0 << 7) +#define ACE_AES_SWAPKEY_ON (1 << 7) +#define ACE_AES_SWAPCNT_OFF (0 << 8) +#define ACE_AES_SWAPCNT_ON (1 << 8) +#define ACE_AES_SWAPIV_OFF (0 << 9) +#define ACE_AES_SWAPIV_ON (1 << 9) +#define ACE_AES_SWAPDO_OFF (0 << 10) +#define ACE_AES_SWAPDO_ON (1 << 10) +#define ACE_AES_SWAPDI_OFF (0 << 11) +#define ACE_AES_SWAPDI_ON (1 << 11) +#define ACE_AES_COUNTERSIZE_MASK (3 << 12) +#define ACE_AES_COUNTERSIZE_128 (0 << 12) +#define ACE_AES_COUNTERSIZE_64 (1 << 12) +#define ACE_AES_COUNTERSIZE_32 (2 << 12) +#define ACE_AES_COUNTERSIZE_16 (3 << 12) + +/* AES status */ +#define ACE_AES_OUTRDY_MASK (1 << 0) +#define ACE_AES_OUTRDY_OFF (0 << 0) +#define ACE_AES_OUTRDY_ON (1 << 0) +#define ACE_AES_INRDY_MASK (1 << 1) +#define ACE_AES_INRDY_OFF (0 << 1) +#define ACE_AES_INRDY_ON (1 << 1) +#define ACE_AES_BUSY_MASK (1 << 2) +#define ACE_AES_BUSY_OFF (0 << 2) +#define ACE_AES_BUSY_ON (1 << 2) + +/* TDES control */ +#define ACE_TDES_MODE_MASK (1 << 0) +#define ACE_TDES_MODE_ENC (0 << 0) +#define ACE_TDES_MODE_DEC (1 << 0) +#define ACE_TDES_OPERMODE_MASK (1 << 1) +#define ACE_TDES_OPERMODE_ECB (0 << 1) +#define ACE_TDES_OPERMODE_CBC (1 << 1) +#define ACE_TDES_SEL_MASK (3 << 3) +#define ACE_TDES_SEL_DES (0 << 3) +#define ACE_TDES_SEL_TDESEDE (1 << 3) /* TDES EDE mode */ +#define ACE_TDES_SEL_TDESEEE (3 << 3) /* TDES EEE mode */ +#define ACE_TDES_FIFO_MASK (1 << 5) +#define ACE_TDES_FIFO_OFF (0 << 5) /* CPU mode */ +#define ACE_TDES_FIFO_ON (1 << 5) /* FIFO mode */ +#define ACE_TDES_SWAP_MASK (0xf << 6) +#define ACE_TDES_SWAPKEY_OFF (0 << 6) +#define ACE_TDES_SWAPKEY_ON (1 << 6) +#define ACE_TDES_SWAPIV_OFF (0 << 7) +#define ACE_TDES_SWAPIV_ON (1 << 7) +#define ACE_TDES_SWAPDO_OFF (0 << 8) +#define ACE_TDES_SWAPDO_ON (1 << 8) +#define ACE_TDES_SWAPDI_OFF (0 << 9) +#define ACE_TDES_SWAPDI_ON (1 << 9) + +/* TDES status */ +#define ACE_TDES_OUTRDY_MASK (1 << 0) +#define ACE_TDES_OUTRDY_OFF (0 << 0) +#define ACE_TDES_OUTRDY_ON (1 << 0) +#define ACE_TDES_INRDY_MASK (1 << 1) +#define ACE_TDES_INRDY_OFF (0 << 1) +#define ACE_TDES_INRDY_ON (1 << 1) +#define ACE_TDES_BUSY_MASK (1 << 2) +#define ACE_TDES_BUSY_OFF (0 << 2) +#define ACE_TDES_BUSY_ON (1 << 2) + +/* Hash control */ +#define ACE_HASH_ENGSEL_MASK (0xf << 0) +#define ACE_HASH_ENGSEL_SHA1HASH (0x0 << 0) +#define ACE_HASH_ENGSEL_SHA1HMAC (0x1 << 0) +#define ACE_HASH_ENGSEL_SHA1HMACIN (0x1 << 0) +#define ACE_HASH_ENGSEL_SHA1HMACOUT (0x9 << 0) +#define ACE_HASH_ENGSEL_MD5HASH (0x2 << 0) +#define ACE_HASH_ENGSEL_MD5HMAC (0x3 << 0) +#define ACE_HASH_ENGSEL_MD5HMACIN (0x3 << 0) +#define ACE_HASH_ENGSEL_MD5HMACOUT (0xb << 0) +#define ACE_HASH_ENGSEL_SHA256HASH (0x4 << 0) +#define ACE_HASH_ENGSEL_SHA256HMAC (0x5 << 0) +#define ACE_HASH_ENGSEL_PRNG (0x8 << 0) +#define ACE_HASH_STARTBIT_ON (1 << 4) +#define ACE_HASH_USERIV_EN (1 << 5) +#define ACE_HASH_PAUSE_ON (1 << 0) + +/* Hash control - FIFO mode */ +#define ACE_HASH_FIFO_MASK (1 << 0) +#define ACE_HASH_FIFO_OFF (0 << 0) +#define ACE_HASH_FIFO_ON (1 << 0) + +/* Hash control - byte swap */ +#define ACE_HASH_SWAP_MASK (0xf << 0) +#define ACE_HASH_SWAPKEY_OFF (0 << 0) +#define ACE_HASH_SWAPKEY_ON (1 << 0) +#define ACE_HASH_SWAPIV_OFF (0 << 1) +#define ACE_HASH_SWAPIV_ON (1 << 1) +#define ACE_HASH_SWAPDO_OFF (0 << 2) +#define ACE_HASH_SWAPDO_ON (1 << 2) +#define ACE_HASH_SWAPDI_OFF (0 << 3) +#define ACE_HASH_SWAPDI_ON (1 << 3) + +/* Hash status */ +#define ACE_HASH_BUFRDY_MASK (1 << 0) +#define ACE_HASH_BUFRDY_OFF (0 << 0) +#define ACE_HASH_BUFRDY_ON (1 << 0) +#define ACE_HASH_SEEDSETTING_MASK (1 << 1) +#define ACE_HASH_SEEDSETTING_OFF (0 << 1) +#define ACE_HASH_SEEDSETTING_ON (1 << 1) +#define ACE_HASH_PRNGBUSY_MASK (1 << 2) +#define ACE_HASH_PRNGBUSY_OFF (0 << 2) +#define ACE_HASH_PRNGBUSY_ON (1 << 2) +#define ACE_HASH_PARTIALDONE_MASK (1 << 4) +#define ACE_HASH_PARTIALDONE_OFF (0 << 4) +#define ACE_HASH_PARTIALDONE_ON (1 << 4) +#define ACE_HASH_PRNGDONE_MASK (1 << 5) +#define ACE_HASH_PRNGDONE_OFF (0 << 5) +#define ACE_HASH_PRNGDONE_ON (1 << 5) +#define ACE_HASH_MSGDONE_MASK (1 << 6) +#define ACE_HASH_MSGDONE_OFF (0 << 6) +#define ACE_HASH_MSGDONE_ON (1 << 6) +#define ACE_HASH_PRNGERROR_MASK (1 << 7) +#define ACE_HASH_PRNGERROR_OFF (0 << 7) +#define ACE_HASH_PRNGERROR_ON (1 << 7) +#define ACE_HASH_PRNG_REG_NUM 5 + +#define ACE_SHA_TYPE_SHA1 1 +#define ACE_SHA_TYPE_SHA256 2 + +/** + * Computes hash value of input pbuf using ACE + * + * @param in_addr A pointer to the input buffer + * @param bufleni Byte length of input buffer + * @param out_addr A pointer to the output buffer. When complete + * 32 bytes are copied to pout[0]...pout[31]. Thus, a user + * should allocate at least 32 bytes at pOut in advance. + * @param hash_type SHA1 or SHA256 + * + * @return 0 on Success, -1 on Failure (Timeout) + */ +int ace_sha_hash_digest(const uchar * in_addr, uint buflen, + uchar * out_addr, uint hash_type); +#endif diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/Makefile b/qemu/roms/u-boot/drivers/ddr/fsl/Makefile new file mode 100644 index 000000000..df66c0723 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/Makefile @@ -0,0 +1,37 @@ +# +# Copyright 2008-2014 Freescale Semiconductor, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# Version 2 as published by the Free Software Foundation. +# + +obj-$(CONFIG_SYS_FSL_DDR1) += main.o util.o ctrl_regs.o options.o \ + lc_common_dimm_params.o +obj-$(CONFIG_SYS_FSL_DDR2) += main.o util.o ctrl_regs.o options.o \ + lc_common_dimm_params.o +obj-$(CONFIG_SYS_FSL_DDR3) += main.o util.o ctrl_regs.o options.o \ + lc_common_dimm_params.o +obj-$(CONFIG_SYS_FSL_DDR4) += main.o util.o ctrl_regs.o options.o \ + lc_common_dimm_params.o + +ifdef CONFIG_DDR_SPD +SPD := y +endif +ifdef CONFIG_SPD_EEPROM +SPD := y +endif +ifdef SPD +obj-$(CONFIG_SYS_FSL_DDR1) += ddr1_dimm_params.o +obj-$(CONFIG_SYS_FSL_DDR2) += ddr2_dimm_params.o +obj-$(CONFIG_SYS_FSL_DDR3) += ddr3_dimm_params.o +obj-$(CONFIG_SYS_FSL_DDR4) += ddr4_dimm_params.o +endif + +obj-$(CONFIG_FSL_DDR_INTERACTIVE) += interactive.o +obj-$(CONFIG_SYS_FSL_DDRC_GEN1) += mpc85xx_ddr_gen1.o +obj-$(CONFIG_SYS_FSL_DDRC_GEN2) += mpc85xx_ddr_gen2.o +obj-$(CONFIG_SYS_FSL_DDRC_GEN3) += mpc85xx_ddr_gen3.o +obj-$(CONFIG_SYS_FSL_DDR_86XX) += mpc86xx_ddr.o +obj-$(CONFIG_SYS_FSL_DDRC_ARM_GEN3) += arm_ddr_gen3.o +obj-$(CONFIG_SYS_FSL_DDRC_GEN4) += fsl_ddr_gen4.o diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/arm_ddr_gen3.c b/qemu/roms/u-boot/drivers/ddr/fsl/arm_ddr_gen3.c new file mode 100644 index 000000000..d4ed9aec2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/arm_ddr_gen3.c @@ -0,0 +1,214 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Derived from mpc85xx_ddr_gen3.c, removed all workarounds + */ + +#include +#include +#include +#include +#include +#include + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + + +/* + * regs has the to-be-set values for DDR controller registers + * ctrl_num is the DDR controller number + * step: 0 goes through the initialization in one pass + * 1 sets registers and returns before enabling controller + * 2 resumes from step 1 and continues to initialize + * Dividing the initialization to two steps to deassert DDR reset signal + * to comply with JEDEC specs for RDIMMs. + */ +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num, int step) +{ + unsigned int i, bus_width; + struct ccsr_ddr __iomem *ddr; + u32 temp_sdram_cfg; + u32 total_gb_size_per_controller; + int timeout; + + switch (ctrl_num) { + case 0: + ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR; + break; +#if defined(CONFIG_SYS_FSL_DDR2_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 1) + case 1: + ddr = (void *)CONFIG_SYS_FSL_DDR2_ADDR; + break; +#endif +#if defined(CONFIG_SYS_FSL_DDR3_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 2) + case 2: + ddr = (void *)CONFIG_SYS_FSL_DDR3_ADDR; + break; +#endif +#if defined(CONFIG_SYS_FSL_DDR4_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 3) + case 3: + ddr = (void *)CONFIG_SYS_FSL_DDR4_ADDR; + break; +#endif + default: + printf("%s unexpected ctrl_num = %u\n", __func__, ctrl_num); + return; + } + + if (step == 2) + goto step2; + + if (regs->ddr_eor) + ddr_out32(&ddr->eor, regs->ddr_eor); + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + ddr_out32(&ddr->cs0_bnds, regs->cs[i].bnds); + ddr_out32(&ddr->cs0_config, regs->cs[i].config); + ddr_out32(&ddr->cs0_config_2, regs->cs[i].config_2); + + } else if (i == 1) { + ddr_out32(&ddr->cs1_bnds, regs->cs[i].bnds); + ddr_out32(&ddr->cs1_config, regs->cs[i].config); + ddr_out32(&ddr->cs1_config_2, regs->cs[i].config_2); + + } else if (i == 2) { + ddr_out32(&ddr->cs2_bnds, regs->cs[i].bnds); + ddr_out32(&ddr->cs2_config, regs->cs[i].config); + ddr_out32(&ddr->cs2_config_2, regs->cs[i].config_2); + + } else if (i == 3) { + ddr_out32(&ddr->cs3_bnds, regs->cs[i].bnds); + ddr_out32(&ddr->cs3_config, regs->cs[i].config); + ddr_out32(&ddr->cs3_config_2, regs->cs[i].config_2); + } + } + + ddr_out32(&ddr->timing_cfg_3, regs->timing_cfg_3); + ddr_out32(&ddr->timing_cfg_0, regs->timing_cfg_0); + ddr_out32(&ddr->timing_cfg_1, regs->timing_cfg_1); + ddr_out32(&ddr->timing_cfg_2, regs->timing_cfg_2); + ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + ddr_out32(&ddr->sdram_mode, regs->ddr_sdram_mode); + ddr_out32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); + ddr_out32(&ddr->sdram_mode_3, regs->ddr_sdram_mode_3); + ddr_out32(&ddr->sdram_mode_4, regs->ddr_sdram_mode_4); + ddr_out32(&ddr->sdram_mode_5, regs->ddr_sdram_mode_5); + ddr_out32(&ddr->sdram_mode_6, regs->ddr_sdram_mode_6); + ddr_out32(&ddr->sdram_mode_7, regs->ddr_sdram_mode_7); + ddr_out32(&ddr->sdram_mode_8, regs->ddr_sdram_mode_8); + ddr_out32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl); + ddr_out32(&ddr->sdram_interval, regs->ddr_sdram_interval); + ddr_out32(&ddr->sdram_data_init, regs->ddr_data_init); + ddr_out32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); + ddr_out32(&ddr->init_addr, regs->ddr_init_addr); + ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + + ddr_out32(&ddr->timing_cfg_4, regs->timing_cfg_4); + ddr_out32(&ddr->timing_cfg_5, regs->timing_cfg_5); + ddr_out32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl); + ddr_out32(&ddr->ddr_wrlvl_cntl, regs->ddr_wrlvl_cntl); +#ifndef CONFIG_SYS_FSL_DDR_EMU + /* + * Skip these two registers if running on emulator + * because emulator doesn't have skew between bytes. + */ + + if (regs->ddr_wrlvl_cntl_2) + ddr_out32(&ddr->ddr_wrlvl_cntl_2, regs->ddr_wrlvl_cntl_2); + if (regs->ddr_wrlvl_cntl_3) + ddr_out32(&ddr->ddr_wrlvl_cntl_3, regs->ddr_wrlvl_cntl_3); +#endif + + ddr_out32(&ddr->ddr_sr_cntr, regs->ddr_sr_cntr); + ddr_out32(&ddr->ddr_sdram_rcw_1, regs->ddr_sdram_rcw_1); + ddr_out32(&ddr->ddr_sdram_rcw_2, regs->ddr_sdram_rcw_2); + ddr_out32(&ddr->ddr_cdr1, regs->ddr_cdr1); + ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2); + ddr_out32(&ddr->err_disable, regs->err_disable); + ddr_out32(&ddr->err_int_en, regs->err_int_en); + for (i = 0; i < 32; i++) { + if (regs->debug[i]) { + debug("Write to debug_%d as %08x\n", i + 1, + regs->debug[i]); + ddr_out32(&ddr->debug[i], regs->debug[i]); + } + } + + /* + * For RDIMMs, JEDEC spec requires clocks to be stable before reset is + * deasserted. Clocks start when any chip select is enabled and clock + * control register is set. Because all DDR components are connected to + * one reset signal, this needs to be done in two steps. Step 1 is to + * get the clocks started. Step 2 resumes after reset signal is + * deasserted. + */ + if (step == 1) { + udelay(200); + return; + } + +step2: + /* Set, but do not enable the memory */ + temp_sdram_cfg = regs->ddr_sdram_cfg; + temp_sdram_cfg &= ~(SDRAM_CFG_MEM_EN); + ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg); + + /* + * 500 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + * DDR2 need 200 us, and DDR3 need 500 us from spec, + * we choose the max, that is 500 us for all of case. + */ + udelay(500); + asm volatile("dsb sy;isb"); + + /* Let the controller go */ + temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI; + ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN); + asm volatile("dsb sy;isb"); + + total_gb_size_per_controller = 0; + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (!(regs->cs[i].config & 0x80000000)) + continue; + total_gb_size_per_controller += 1 << ( + ((regs->cs[i].config >> 14) & 0x3) + 2 + + ((regs->cs[i].config >> 8) & 0x7) + 12 + + ((regs->cs[i].config >> 0) & 0x7) + 8 + + 3 - ((regs->ddr_sdram_cfg >> 19) & 0x3) - + 26); /* minus 26 (count of 64M) */ + } + if (regs->cs[0].config & 0x20000000) { + /* 2-way interleaving */ + total_gb_size_per_controller <<= 1; + } + /* + * total memory / bus width = transactions needed + * transactions needed / data rate = seconds + * to add plenty of buffer, double the time + * For example, 2GB on 666MT/s 64-bit bus takes about 402ms + * Let's wait for 800ms + */ + bus_width = 3 - ((ddr->sdram_cfg & SDRAM_CFG_DBW_MASK) + >> SDRAM_CFG_DBW_SHIFT); + timeout = ((total_gb_size_per_controller << (6 - bus_width)) * 100 / + (get_ddr_freq(0) >> 20)) << 1; + total_gb_size_per_controller >>= 4; /* shift down to gb size */ + debug("total %d GB\n", total_gb_size_per_controller); + debug("Need to wait up to %d * 10ms\n", timeout); + + /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */ + while ((ddr_in32(&ddr->sdram_cfg_2) & SDRAM_CFG2_D_INIT) && + (timeout >= 0)) { + udelay(10000); /* throttle polling rate */ + timeout--; + } + + if (timeout <= 0) + printf("Waiting for D_INIT timeout. Memory may not work.\n"); +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/ctrl_regs.c b/qemu/roms/u-boot/drivers/ddr/fsl/ctrl_regs.c new file mode 100644 index 000000000..78e82bba3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/ctrl_regs.c @@ -0,0 +1,2308 @@ +/* + * Copyright 2008-2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Generic driver for Freescale DDR/DDR2/DDR3 memory controller. + * Based on code from spd_sdram.c + * Author: James Yang [at freescale.com] + */ + +#include +#include + +#include +#include +#include + +unsigned int picos_to_mclk(unsigned int picos); + +/* + * Determine Rtt value. + * + * This should likely be either board or controller specific. + * + * Rtt(nominal) - DDR2: + * 0 = Rtt disabled + * 1 = 75 ohm + * 2 = 150 ohm + * 3 = 50 ohm + * Rtt(nominal) - DDR3: + * 0 = Rtt disabled + * 1 = 60 ohm + * 2 = 120 ohm + * 3 = 40 ohm + * 4 = 20 ohm + * 5 = 30 ohm + * + * FIXME: Apparently 8641 needs a value of 2 + * FIXME: Old code seys if 667 MHz or higher, use 3 on 8572 + * + * FIXME: There was some effort down this line earlier: + * + * unsigned int i; + * for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL/2; i++) { + * if (popts->dimmslot[i].num_valid_cs + * && (popts->cs_local_opts[2*i].odt_rd_cfg + * || popts->cs_local_opts[2*i].odt_wr_cfg)) { + * rtt = 2; + * break; + * } + * } + */ +static inline int fsl_ddr_get_rtt(void) +{ + int rtt; + +#if defined(CONFIG_SYS_FSL_DDR1) + rtt = 0; +#elif defined(CONFIG_SYS_FSL_DDR2) + rtt = 3; +#else + rtt = 0; +#endif + + return rtt; +} + +#ifdef CONFIG_SYS_FSL_DDR4 +/* + * compute CAS write latency according to DDR4 spec + * CWL = 9 for <= 1600MT/s + * 10 for <= 1866MT/s + * 11 for <= 2133MT/s + * 12 for <= 2400MT/s + * 14 for <= 2667MT/s + * 16 for <= 2933MT/s + * 18 for higher + */ +static inline unsigned int compute_cas_write_latency(void) +{ + unsigned int cwl; + const unsigned int mclk_ps = get_memory_clk_period_ps(); + if (mclk_ps >= 1250) + cwl = 9; + else if (mclk_ps >= 1070) + cwl = 10; + else if (mclk_ps >= 935) + cwl = 11; + else if (mclk_ps >= 833) + cwl = 12; + else if (mclk_ps >= 750) + cwl = 14; + else if (mclk_ps >= 681) + cwl = 16; + else + cwl = 18; + + return cwl; +} +#else +/* + * compute the CAS write latency according to DDR3 spec + * CWL = 5 if tCK >= 2.5ns + * 6 if 2.5ns > tCK >= 1.875ns + * 7 if 1.875ns > tCK >= 1.5ns + * 8 if 1.5ns > tCK >= 1.25ns + * 9 if 1.25ns > tCK >= 1.07ns + * 10 if 1.07ns > tCK >= 0.935ns + * 11 if 0.935ns > tCK >= 0.833ns + * 12 if 0.833ns > tCK >= 0.75ns + */ +static inline unsigned int compute_cas_write_latency(void) +{ + unsigned int cwl; + const unsigned int mclk_ps = get_memory_clk_period_ps(); + + if (mclk_ps >= 2500) + cwl = 5; + else if (mclk_ps >= 1875) + cwl = 6; + else if (mclk_ps >= 1500) + cwl = 7; + else if (mclk_ps >= 1250) + cwl = 8; + else if (mclk_ps >= 1070) + cwl = 9; + else if (mclk_ps >= 935) + cwl = 10; + else if (mclk_ps >= 833) + cwl = 11; + else if (mclk_ps >= 750) + cwl = 12; + else { + cwl = 12; + printf("Warning: CWL is out of range\n"); + } + return cwl; +} +#endif + +/* Chip Select Configuration (CSn_CONFIG) */ +static void set_csn_config(int dimm_number, int i, fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const dimm_params_t *dimm_params) +{ + unsigned int cs_n_en = 0; /* Chip Select enable */ + unsigned int intlv_en = 0; /* Memory controller interleave enable */ + unsigned int intlv_ctl = 0; /* Interleaving control */ + unsigned int ap_n_en = 0; /* Chip select n auto-precharge enable */ + unsigned int odt_rd_cfg = 0; /* ODT for reads configuration */ + unsigned int odt_wr_cfg = 0; /* ODT for writes configuration */ + unsigned int ba_bits_cs_n = 0; /* Num of bank bits for SDRAM on CSn */ + unsigned int row_bits_cs_n = 0; /* Num of row bits for SDRAM on CSn */ + unsigned int col_bits_cs_n = 0; /* Num of ocl bits for SDRAM on CSn */ + int go_config = 0; +#ifdef CONFIG_SYS_FSL_DDR4 + unsigned int bg_bits_cs_n = 0; /* Num of bank group bits */ +#else + unsigned int n_banks_per_sdram_device; +#endif + + /* Compute CS_CONFIG only for existing ranks of each DIMM. */ + switch (i) { + case 0: + if (dimm_params[dimm_number].n_ranks > 0) { + go_config = 1; + /* These fields only available in CS0_CONFIG */ + if (!popts->memctl_interleaving) + break; + switch (popts->memctl_interleaving_mode) { + case FSL_DDR_256B_INTERLEAVING: + case FSL_DDR_CACHE_LINE_INTERLEAVING: + case FSL_DDR_PAGE_INTERLEAVING: + case FSL_DDR_BANK_INTERLEAVING: + case FSL_DDR_SUPERBANK_INTERLEAVING: + intlv_en = popts->memctl_interleaving; + intlv_ctl = popts->memctl_interleaving_mode; + break; + default: + break; + } + } + break; + case 1: + if ((dimm_number == 0 && dimm_params[0].n_ranks > 1) || \ + (dimm_number == 1 && dimm_params[1].n_ranks > 0)) + go_config = 1; + break; + case 2: + if ((dimm_number == 0 && dimm_params[0].n_ranks > 2) || \ + (dimm_number >= 1 && dimm_params[dimm_number].n_ranks > 0)) + go_config = 1; + break; + case 3: + if ((dimm_number == 0 && dimm_params[0].n_ranks > 3) || \ + (dimm_number == 1 && dimm_params[1].n_ranks > 1) || \ + (dimm_number == 3 && dimm_params[3].n_ranks > 0)) + go_config = 1; + break; + default: + break; + } + if (go_config) { + cs_n_en = 1; + ap_n_en = popts->cs_local_opts[i].auto_precharge; + odt_rd_cfg = popts->cs_local_opts[i].odt_rd_cfg; + odt_wr_cfg = popts->cs_local_opts[i].odt_wr_cfg; +#ifdef CONFIG_SYS_FSL_DDR4 + ba_bits_cs_n = dimm_params[dimm_number].bank_addr_bits; + bg_bits_cs_n = dimm_params[dimm_number].bank_group_bits; +#else + n_banks_per_sdram_device + = dimm_params[dimm_number].n_banks_per_sdram_device; + ba_bits_cs_n = __ilog2(n_banks_per_sdram_device) - 2; +#endif + row_bits_cs_n = dimm_params[dimm_number].n_row_addr - 12; + col_bits_cs_n = dimm_params[dimm_number].n_col_addr - 8; + } + ddr->cs[i].config = (0 + | ((cs_n_en & 0x1) << 31) + | ((intlv_en & 0x3) << 29) + | ((intlv_ctl & 0xf) << 24) + | ((ap_n_en & 0x1) << 23) + + /* XXX: some implementation only have 1 bit starting at left */ + | ((odt_rd_cfg & 0x7) << 20) + + /* XXX: Some implementation only have 1 bit starting at left */ + | ((odt_wr_cfg & 0x7) << 16) + + | ((ba_bits_cs_n & 0x3) << 14) + | ((row_bits_cs_n & 0x7) << 8) +#ifdef CONFIG_SYS_FSL_DDR4 + | ((bg_bits_cs_n & 0x3) << 4) +#endif + | ((col_bits_cs_n & 0x7) << 0) + ); + debug("FSLDDR: cs[%d]_config = 0x%08x\n", i,ddr->cs[i].config); +} + +/* Chip Select Configuration 2 (CSn_CONFIG_2) */ +/* FIXME: 8572 */ +static void set_csn_config_2(int i, fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int pasr_cfg = 0; /* Partial array self refresh config */ + + ddr->cs[i].config_2 = ((pasr_cfg & 7) << 24); + debug("FSLDDR: cs[%d]_config_2 = 0x%08x\n", i, ddr->cs[i].config_2); +} + +/* -3E = 667 CL5, -25 = CL6 800, -25E = CL5 800 */ + +#if !defined(CONFIG_SYS_FSL_DDR1) +static inline int avoid_odt_overlap(const dimm_params_t *dimm_params) +{ +#if CONFIG_DIMM_SLOTS_PER_CTLR == 1 + if (dimm_params[0].n_ranks == 4) + return 1; +#endif + +#if CONFIG_DIMM_SLOTS_PER_CTLR == 2 + if ((dimm_params[0].n_ranks == 2) && + (dimm_params[1].n_ranks == 2)) + return 1; + +#ifdef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE + if (dimm_params[0].n_ranks == 4) + return 1; +#endif +#endif + return 0; +} + +/* + * DDR SDRAM Timing Configuration 0 (TIMING_CFG_0) + * + * Avoid writing for DDR I. The new PQ38 DDR controller + * dreams up non-zero default values to be backwards compatible. + */ +static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const dimm_params_t *dimm_params) +{ + unsigned char trwt_mclk = 0; /* Read-to-write turnaround */ + unsigned char twrt_mclk = 0; /* Write-to-read turnaround */ + /* 7.5 ns on -3E; 0 means WL - CL + BL/2 + 1 */ + unsigned char trrt_mclk = 0; /* Read-to-read turnaround */ + unsigned char twwt_mclk = 0; /* Write-to-write turnaround */ + + /* Active powerdown exit timing (tXARD and tXARDS). */ + unsigned char act_pd_exit_mclk; + /* Precharge powerdown exit timing (tXP). */ + unsigned char pre_pd_exit_mclk; + /* ODT powerdown exit timing (tAXPD). */ + unsigned char taxpd_mclk = 0; + /* Mode register set cycle time (tMRD). */ + unsigned char tmrd_mclk; + +#ifdef CONFIG_SYS_FSL_DDR4 + /* tXP=max(4nCK, 6ns) */ + int txp = max((get_memory_clk_period_ps() * 4), 6000); /* unit=ps */ + trwt_mclk = 2; + twrt_mclk = 1; + act_pd_exit_mclk = picos_to_mclk(txp); + pre_pd_exit_mclk = act_pd_exit_mclk; + /* + * MRS_CYC = max(tMRD, tMOD) + * tMRD = 8nCK, tMOD = max(24nCK, 15ns) + */ + tmrd_mclk = max(24, picos_to_mclk(15000)); +#elif defined(CONFIG_SYS_FSL_DDR3) + /* + * (tXARD and tXARDS). Empirical? + * The DDR3 spec has not tXARD, + * we use the tXP instead of it. + * tXP=max(3nCK, 7.5ns) for DDR3. + * spec has not the tAXPD, we use + * tAXPD=1, need design to confirm. + */ + int txp = max((get_memory_clk_period_ps() * 3), 7500); /* unit=ps */ + unsigned int data_rate = get_ddr_freq(0); + tmrd_mclk = 4; + /* set the turnaround time */ + + /* + * for single quad-rank DIMM and two dual-rank DIMMs + * to avoid ODT overlap + */ + if (avoid_odt_overlap(dimm_params)) { + twwt_mclk = 2; + trrt_mclk = 1; + } + /* for faster clock, need more time for data setup */ + trwt_mclk = (data_rate/1000000 > 1800) ? 2 : 1; + + if ((data_rate/1000000 > 1150) || (popts->memctl_interleaving)) + twrt_mclk = 1; + + if (popts->dynamic_power == 0) { /* powerdown is not used */ + act_pd_exit_mclk = 1; + pre_pd_exit_mclk = 1; + taxpd_mclk = 1; + } else { + /* act_pd_exit_mclk = tXARD, see above */ + act_pd_exit_mclk = picos_to_mclk(txp); + /* Mode register MR0[A12] is '1' - fast exit */ + pre_pd_exit_mclk = act_pd_exit_mclk; + taxpd_mclk = 1; + } +#else /* CONFIG_SYS_FSL_DDR2 */ + /* + * (tXARD and tXARDS). Empirical? + * tXARD = 2 for DDR2 + * tXP=2 + * tAXPD=8 + */ + act_pd_exit_mclk = 2; + pre_pd_exit_mclk = 2; + taxpd_mclk = 8; + tmrd_mclk = 2; +#endif + + if (popts->trwt_override) + trwt_mclk = popts->trwt; + + ddr->timing_cfg_0 = (0 + | ((trwt_mclk & 0x3) << 30) /* RWT */ + | ((twrt_mclk & 0x3) << 28) /* WRT */ + | ((trrt_mclk & 0x3) << 26) /* RRT */ + | ((twwt_mclk & 0x3) << 24) /* WWT */ + | ((act_pd_exit_mclk & 0xf) << 20) /* ACT_PD_EXIT */ + | ((pre_pd_exit_mclk & 0xF) << 16) /* PRE_PD_EXIT */ + | ((taxpd_mclk & 0xf) << 8) /* ODT_PD_EXIT */ + | ((tmrd_mclk & 0x1f) << 0) /* MRS_CYC */ + ); + debug("FSLDDR: timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0); +} +#endif /* defined(CONFIG_SYS_FSL_DDR2) */ + +/* DDR SDRAM Timing Configuration 3 (TIMING_CFG_3) */ +static void set_timing_cfg_3(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency, + unsigned int additive_latency) +{ + /* Extended precharge to activate interval (tRP) */ + unsigned int ext_pretoact = 0; + /* Extended Activate to precharge interval (tRAS) */ + unsigned int ext_acttopre = 0; + /* Extended activate to read/write interval (tRCD) */ + unsigned int ext_acttorw = 0; + /* Extended refresh recovery time (tRFC) */ + unsigned int ext_refrec; + /* Extended MCAS latency from READ cmd */ + unsigned int ext_caslat = 0; + /* Extended additive latency */ + unsigned int ext_add_lat = 0; + /* Extended last data to precharge interval (tWR) */ + unsigned int ext_wrrec = 0; + /* Control Adjust */ + unsigned int cntl_adj = 0; + + ext_pretoact = picos_to_mclk(common_dimm->trp_ps) >> 4; + ext_acttopre = picos_to_mclk(common_dimm->tras_ps) >> 4; + ext_acttorw = picos_to_mclk(common_dimm->trcd_ps) >> 4; + ext_caslat = (2 * cas_latency - 1) >> 4; + ext_add_lat = additive_latency >> 4; +#ifdef CONFIG_SYS_FSL_DDR4 + ext_refrec = (picos_to_mclk(common_dimm->trfc1_ps) - 8) >> 4; +#else + ext_refrec = (picos_to_mclk(common_dimm->trfc_ps) - 8) >> 4; + /* ext_wrrec only deals with 16 clock and above, or 14 with OTF */ +#endif + ext_wrrec = (picos_to_mclk(common_dimm->twr_ps) + + (popts->otf_burst_chop_en ? 2 : 0)) >> 4; + + ddr->timing_cfg_3 = (0 + | ((ext_pretoact & 0x1) << 28) + | ((ext_acttopre & 0x3) << 24) + | ((ext_acttorw & 0x1) << 22) + | ((ext_refrec & 0x1F) << 16) + | ((ext_caslat & 0x3) << 12) + | ((ext_add_lat & 0x1) << 10) + | ((ext_wrrec & 0x1) << 8) + | ((cntl_adj & 0x7) << 0) + ); + debug("FSLDDR: timing_cfg_3 = 0x%08x\n", ddr->timing_cfg_3); +} + +/* DDR SDRAM Timing Configuration 1 (TIMING_CFG_1) */ +static void set_timing_cfg_1(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency) +{ + /* Precharge-to-activate interval (tRP) */ + unsigned char pretoact_mclk; + /* Activate to precharge interval (tRAS) */ + unsigned char acttopre_mclk; + /* Activate to read/write interval (tRCD) */ + unsigned char acttorw_mclk; + /* CASLAT */ + unsigned char caslat_ctrl; + /* Refresh recovery time (tRFC) ; trfc_low */ + unsigned char refrec_ctrl; + /* Last data to precharge minimum interval (tWR) */ + unsigned char wrrec_mclk; + /* Activate-to-activate interval (tRRD) */ + unsigned char acttoact_mclk; + /* Last write data pair to read command issue interval (tWTR) */ + unsigned char wrtord_mclk; +#ifdef CONFIG_SYS_FSL_DDR4 + /* DDR4 supports 10, 12, 14, 16, 18, 20, 24 */ + static const u8 wrrec_table[] = { + 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, + 12, 12, 14, 14, 16, + 16, 18, 18, 20, 20, + 24, 24, 24, 24}; +#else + /* DDR_SDRAM_MODE doesn't support 9,11,13,15 */ + static const u8 wrrec_table[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 10, 10, 12, 12, 14, 14, 0, 0}; +#endif + + pretoact_mclk = picos_to_mclk(common_dimm->trp_ps); + acttopre_mclk = picos_to_mclk(common_dimm->tras_ps); + acttorw_mclk = picos_to_mclk(common_dimm->trcd_ps); + + /* + * Translate CAS Latency to a DDR controller field value: + * + * CAS Lat DDR I DDR II Ctrl + * Clocks SPD Bit SPD Bit Value + * ------- ------- ------- ----- + * 1.0 0 0001 + * 1.5 1 0010 + * 2.0 2 2 0011 + * 2.5 3 0100 + * 3.0 4 3 0101 + * 3.5 5 0110 + * 4.0 4 0111 + * 4.5 1000 + * 5.0 5 1001 + */ +#if defined(CONFIG_SYS_FSL_DDR1) + caslat_ctrl = (cas_latency + 1) & 0x07; +#elif defined(CONFIG_SYS_FSL_DDR2) + caslat_ctrl = 2 * cas_latency - 1; +#else + /* + * if the CAS latency more than 8 cycle, + * we need set extend bit for it at + * TIMING_CFG_3[EXT_CASLAT] + */ + if (fsl_ddr_get_version() <= 0x40400) + caslat_ctrl = 2 * cas_latency - 1; + else + caslat_ctrl = (cas_latency - 1) << 1; +#endif + +#ifdef CONFIG_SYS_FSL_DDR4 + refrec_ctrl = picos_to_mclk(common_dimm->trfc1_ps) - 8; + wrrec_mclk = picos_to_mclk(common_dimm->twr_ps); + acttoact_mclk = max(picos_to_mclk(common_dimm->trrds_ps), 4); + wrtord_mclk = max(2, picos_to_mclk(2500)); + if ((wrrec_mclk < 1) || (wrrec_mclk > 24)) + printf("Error: WRREC doesn't support %d clocks\n", wrrec_mclk); + else + wrrec_mclk = wrrec_table[wrrec_mclk - 1]; +#else + refrec_ctrl = picos_to_mclk(common_dimm->trfc_ps) - 8; + wrrec_mclk = picos_to_mclk(common_dimm->twr_ps); + acttoact_mclk = picos_to_mclk(common_dimm->trrd_ps); + wrtord_mclk = picos_to_mclk(common_dimm->twtr_ps); + if ((wrrec_mclk < 1) || (wrrec_mclk > 16)) + printf("Error: WRREC doesn't support %d clocks\n", wrrec_mclk); + else + wrrec_mclk = wrrec_table[wrrec_mclk - 1]; +#endif + if (popts->otf_burst_chop_en) + wrrec_mclk += 2; + + /* + * JEDEC has min requirement for tRRD + */ +#if defined(CONFIG_SYS_FSL_DDR3) + if (acttoact_mclk < 4) + acttoact_mclk = 4; +#endif + /* + * JEDEC has some min requirements for tWTR + */ +#if defined(CONFIG_SYS_FSL_DDR2) + if (wrtord_mclk < 2) + wrtord_mclk = 2; +#elif defined(CONFIG_SYS_FSL_DDR3) + if (wrtord_mclk < 4) + wrtord_mclk = 4; +#endif + if (popts->otf_burst_chop_en) + wrtord_mclk += 2; + + ddr->timing_cfg_1 = (0 + | ((pretoact_mclk & 0x0F) << 28) + | ((acttopre_mclk & 0x0F) << 24) + | ((acttorw_mclk & 0xF) << 20) + | ((caslat_ctrl & 0xF) << 16) + | ((refrec_ctrl & 0xF) << 12) + | ((wrrec_mclk & 0x0F) << 8) + | ((acttoact_mclk & 0x0F) << 4) + | ((wrtord_mclk & 0x0F) << 0) + ); + debug("FSLDDR: timing_cfg_1 = 0x%08x\n", ddr->timing_cfg_1); +} + +/* DDR SDRAM Timing Configuration 2 (TIMING_CFG_2) */ +static void set_timing_cfg_2(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency, + unsigned int additive_latency) +{ + /* Additive latency */ + unsigned char add_lat_mclk; + /* CAS-to-preamble override */ + unsigned short cpo; + /* Write latency */ + unsigned char wr_lat; + /* Read to precharge (tRTP) */ + unsigned char rd_to_pre; + /* Write command to write data strobe timing adjustment */ + unsigned char wr_data_delay; + /* Minimum CKE pulse width (tCKE) */ + unsigned char cke_pls; + /* Window for four activates (tFAW) */ + unsigned short four_act; + + /* FIXME add check that this must be less than acttorw_mclk */ + add_lat_mclk = additive_latency; + cpo = popts->cpo_override; + +#if defined(CONFIG_SYS_FSL_DDR1) + /* + * This is a lie. It should really be 1, but if it is + * set to 1, bits overlap into the old controller's + * otherwise unused ACSM field. If we leave it 0, then + * the HW will magically treat it as 1 for DDR 1. Oh Yea. + */ + wr_lat = 0; +#elif defined(CONFIG_SYS_FSL_DDR2) + wr_lat = cas_latency - 1; +#else + wr_lat = compute_cas_write_latency(); +#endif + +#ifdef CONFIG_SYS_FSL_DDR4 + rd_to_pre = picos_to_mclk(7500); +#else + rd_to_pre = picos_to_mclk(common_dimm->trtp_ps); +#endif + /* + * JEDEC has some min requirements for tRTP + */ +#if defined(CONFIG_SYS_FSL_DDR2) + if (rd_to_pre < 2) + rd_to_pre = 2; +#elif defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + if (rd_to_pre < 4) + rd_to_pre = 4; +#endif + if (popts->otf_burst_chop_en) + rd_to_pre += 2; /* according to UM */ + + wr_data_delay = popts->write_data_delay; +#ifdef CONFIG_SYS_FSL_DDR4 + cpo = 0; + cke_pls = max(3, picos_to_mclk(5000)); +#else + cke_pls = picos_to_mclk(popts->tcke_clock_pulse_width_ps); +#endif + + four_act = picos_to_mclk(popts->tfaw_window_four_activates_ps); + + ddr->timing_cfg_2 = (0 + | ((add_lat_mclk & 0xf) << 28) + | ((cpo & 0x1f) << 23) + | ((wr_lat & 0xf) << 19) + | ((wr_lat & 0x10) << 14) + | ((rd_to_pre & RD_TO_PRE_MASK) << RD_TO_PRE_SHIFT) + | ((wr_data_delay & WR_DATA_DELAY_MASK) << WR_DATA_DELAY_SHIFT) + | ((cke_pls & 0x7) << 6) + | ((four_act & 0x3f) << 0) + ); + debug("FSLDDR: timing_cfg_2 = 0x%08x\n", ddr->timing_cfg_2); +} + +/* DDR SDRAM Register Control Word */ +static void set_ddr_sdram_rcw(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm) +{ + if (common_dimm->all_dimms_registered && + !common_dimm->all_dimms_unbuffered) { + if (popts->rcw_override) { + ddr->ddr_sdram_rcw_1 = popts->rcw_1; + ddr->ddr_sdram_rcw_2 = popts->rcw_2; + } else { + ddr->ddr_sdram_rcw_1 = + common_dimm->rcw[0] << 28 | \ + common_dimm->rcw[1] << 24 | \ + common_dimm->rcw[2] << 20 | \ + common_dimm->rcw[3] << 16 | \ + common_dimm->rcw[4] << 12 | \ + common_dimm->rcw[5] << 8 | \ + common_dimm->rcw[6] << 4 | \ + common_dimm->rcw[7]; + ddr->ddr_sdram_rcw_2 = + common_dimm->rcw[8] << 28 | \ + common_dimm->rcw[9] << 24 | \ + common_dimm->rcw[10] << 20 | \ + common_dimm->rcw[11] << 16 | \ + common_dimm->rcw[12] << 12 | \ + common_dimm->rcw[13] << 8 | \ + common_dimm->rcw[14] << 4 | \ + common_dimm->rcw[15]; + } + debug("FSLDDR: ddr_sdram_rcw_1 = 0x%08x\n", ddr->ddr_sdram_rcw_1); + debug("FSLDDR: ddr_sdram_rcw_2 = 0x%08x\n", ddr->ddr_sdram_rcw_2); + } +} + +/* DDR SDRAM control configuration (DDR_SDRAM_CFG) */ +static void set_ddr_sdram_cfg(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm) +{ + unsigned int mem_en; /* DDR SDRAM interface logic enable */ + unsigned int sren; /* Self refresh enable (during sleep) */ + unsigned int ecc_en; /* ECC enable. */ + unsigned int rd_en; /* Registered DIMM enable */ + unsigned int sdram_type; /* Type of SDRAM */ + unsigned int dyn_pwr; /* Dynamic power management mode */ + unsigned int dbw; /* DRAM dta bus width */ + unsigned int eight_be = 0; /* 8-beat burst enable, DDR2 is zero */ + unsigned int ncap = 0; /* Non-concurrent auto-precharge */ + unsigned int threet_en; /* Enable 3T timing */ + unsigned int twot_en; /* Enable 2T timing */ + unsigned int ba_intlv_ctl; /* Bank (CS) interleaving control */ + unsigned int x32_en = 0; /* x32 enable */ + unsigned int pchb8 = 0; /* precharge bit 8 enable */ + unsigned int hse; /* Global half strength override */ + unsigned int mem_halt = 0; /* memory controller halt */ + unsigned int bi = 0; /* Bypass initialization */ + + mem_en = 1; + sren = popts->self_refresh_in_sleep; + if (common_dimm->all_dimms_ecc_capable) { + /* Allow setting of ECC only if all DIMMs are ECC. */ + ecc_en = popts->ecc_mode; + } else { + ecc_en = 0; + } + + if (common_dimm->all_dimms_registered && + !common_dimm->all_dimms_unbuffered) { + rd_en = 1; + twot_en = 0; + } else { + rd_en = 0; + twot_en = popts->twot_en; + } + + sdram_type = CONFIG_FSL_SDRAM_TYPE; + + dyn_pwr = popts->dynamic_power; + dbw = popts->data_bus_width; + /* 8-beat burst enable DDR-III case + * we must clear it when use the on-the-fly mode, + * must set it when use the 32-bits bus mode. + */ + if ((sdram_type == SDRAM_TYPE_DDR3) || + (sdram_type == SDRAM_TYPE_DDR4)) { + if (popts->burst_length == DDR_BL8) + eight_be = 1; + if (popts->burst_length == DDR_OTF) + eight_be = 0; + if (dbw == 0x1) + eight_be = 1; + } + + threet_en = popts->threet_en; + ba_intlv_ctl = popts->ba_intlv_ctl; + hse = popts->half_strength_driver_enable; + + ddr->ddr_sdram_cfg = (0 + | ((mem_en & 0x1) << 31) + | ((sren & 0x1) << 30) + | ((ecc_en & 0x1) << 29) + | ((rd_en & 0x1) << 28) + | ((sdram_type & 0x7) << 24) + | ((dyn_pwr & 0x1) << 21) + | ((dbw & 0x3) << 19) + | ((eight_be & 0x1) << 18) + | ((ncap & 0x1) << 17) + | ((threet_en & 0x1) << 16) + | ((twot_en & 0x1) << 15) + | ((ba_intlv_ctl & 0x7F) << 8) + | ((x32_en & 0x1) << 5) + | ((pchb8 & 0x1) << 4) + | ((hse & 0x1) << 3) + | ((mem_halt & 0x1) << 1) + | ((bi & 0x1) << 0) + ); + debug("FSLDDR: ddr_sdram_cfg = 0x%08x\n", ddr->ddr_sdram_cfg); +} + +/* DDR SDRAM control configuration 2 (DDR_SDRAM_CFG_2) */ +static void set_ddr_sdram_cfg_2(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const unsigned int unq_mrs_en) +{ + unsigned int frc_sr = 0; /* Force self refresh */ + unsigned int sr_ie = 0; /* Self-refresh interrupt enable */ + unsigned int odt_cfg = 0; /* ODT configuration */ + unsigned int num_pr; /* Number of posted refreshes */ + unsigned int slow = 0; /* DDR will be run less than 1250 */ + unsigned int x4_en = 0; /* x4 DRAM enable */ + unsigned int obc_cfg; /* On-The-Fly Burst Chop Cfg */ + unsigned int ap_en; /* Address Parity Enable */ + unsigned int d_init; /* DRAM data initialization */ + unsigned int rcw_en = 0; /* Register Control Word Enable */ + unsigned int md_en = 0; /* Mirrored DIMM Enable */ + unsigned int qd_en = 0; /* quad-rank DIMM Enable */ + int i; +#ifndef CONFIG_SYS_FSL_DDR4 + unsigned int dll_rst_dis = 1; /* DLL reset disable */ + unsigned int dqs_cfg; /* DQS configuration */ + + dqs_cfg = popts->dqs_config; +#endif + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (popts->cs_local_opts[i].odt_rd_cfg + || popts->cs_local_opts[i].odt_wr_cfg) { + odt_cfg = SDRAM_CFG2_ODT_ONLY_READ; + break; + } + } + + num_pr = 1; /* Make this configurable */ + + /* + * 8572 manual says + * {TIMING_CFG_1[PRETOACT] + * + [DDR_SDRAM_CFG_2[NUM_PR] + * * ({EXT_REFREC || REFREC} + 8 + 2)]} + * << DDR_SDRAM_INTERVAL[REFINT] + */ +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + obc_cfg = popts->otf_burst_chop_en; +#else + obc_cfg = 0; +#endif + +#if (CONFIG_SYS_FSL_DDR_VER >= FSL_DDR_VER_4_7) + slow = get_ddr_freq(0) < 1249000000; +#endif + + if (popts->registered_dimm_en) { + rcw_en = 1; + ap_en = popts->ap_en; + } else { + ap_en = 0; + } + + x4_en = popts->x4_en ? 1 : 0; + +#if defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) + /* Use the DDR controller to auto initialize memory. */ + d_init = popts->ecc_init_using_memctl; + ddr->ddr_data_init = CONFIG_MEM_INIT_VALUE; + debug("DDR: ddr_data_init = 0x%08x\n", ddr->ddr_data_init); +#else + /* Memory will be initialized via DMA, or not at all. */ + d_init = 0; +#endif + +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + md_en = popts->mirrored_dimm; +#endif + qd_en = popts->quad_rank_present ? 1 : 0; + ddr->ddr_sdram_cfg_2 = (0 + | ((frc_sr & 0x1) << 31) + | ((sr_ie & 0x1) << 30) +#ifndef CONFIG_SYS_FSL_DDR4 + | ((dll_rst_dis & 0x1) << 29) + | ((dqs_cfg & 0x3) << 26) +#endif + | ((odt_cfg & 0x3) << 21) + | ((num_pr & 0xf) << 12) + | ((slow & 1) << 11) + | (x4_en << 10) + | (qd_en << 9) + | (unq_mrs_en << 8) + | ((obc_cfg & 0x1) << 6) + | ((ap_en & 0x1) << 5) + | ((d_init & 0x1) << 4) + | ((rcw_en & 0x1) << 2) + | ((md_en & 0x1) << 0) + ); + debug("FSLDDR: ddr_sdram_cfg_2 = 0x%08x\n", ddr->ddr_sdram_cfg_2); +} + +#ifdef CONFIG_SYS_FSL_DDR4 +/* DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) */ +static void set_ddr_sdram_mode_2(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + const unsigned int unq_mrs_en) +{ + unsigned short esdmode2 = 0; /* Extended SDRAM mode 2 */ + unsigned short esdmode3 = 0; /* Extended SDRAM mode 3 */ + int i; + unsigned int wr_crc = 0; /* Disable */ + unsigned int rtt_wr = 0; /* Rtt_WR - dynamic ODT off */ + unsigned int srt = 0; /* self-refresh temerature, normal range */ + unsigned int cwl = compute_cas_write_latency() - 9; + unsigned int mpr = 0; /* serial */ + unsigned int wc_lat; + const unsigned int mclk_ps = get_memory_clk_period_ps(); + + if (popts->rtt_override) + rtt_wr = popts->rtt_wr_override_value; + else + rtt_wr = popts->cs_local_opts[0].odt_rtt_wr; + + if (common_dimm->extended_op_srt) + srt = common_dimm->extended_op_srt; + + esdmode2 = (0 + | ((wr_crc & 0x1) << 12) + | ((rtt_wr & 0x3) << 9) + | ((srt & 0x3) << 6) + | ((cwl & 0x7) << 3)); + + if (mclk_ps >= 1250) + wc_lat = 0; + else if (mclk_ps >= 833) + wc_lat = 1; + else + wc_lat = 2; + + esdmode3 = (0 + | ((mpr & 0x3) << 11) + | ((wc_lat & 0x3) << 9)); + + ddr->ddr_sdram_mode_2 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + debug("FSLDDR: ddr_sdram_mode_2 = 0x%08x\n", ddr->ddr_sdram_mode_2); + + if (unq_mrs_en) { /* unique mode registers are supported */ + for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (popts->rtt_override) + rtt_wr = popts->rtt_wr_override_value; + else + rtt_wr = popts->cs_local_opts[i].odt_rtt_wr; + + esdmode2 &= 0xF9FF; /* clear bit 10, 9 */ + esdmode2 |= (rtt_wr & 0x3) << 9; + switch (i) { + case 1: + ddr->ddr_sdram_mode_4 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + break; + case 2: + ddr->ddr_sdram_mode_6 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + break; + case 3: + ddr->ddr_sdram_mode_8 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + break; + } + } + debug("FSLDDR: ddr_sdram_mode_4 = 0x%08x\n", + ddr->ddr_sdram_mode_4); + debug("FSLDDR: ddr_sdram_mode_6 = 0x%08x\n", + ddr->ddr_sdram_mode_6); + debug("FSLDDR: ddr_sdram_mode_8 = 0x%08x\n", + ddr->ddr_sdram_mode_8); + } +} +#elif defined(CONFIG_SYS_FSL_DDR3) +/* DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) */ +static void set_ddr_sdram_mode_2(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + const unsigned int unq_mrs_en) +{ + unsigned short esdmode2 = 0; /* Extended SDRAM mode 2 */ + unsigned short esdmode3 = 0; /* Extended SDRAM mode 3 */ + int i; + unsigned int rtt_wr = 0; /* Rtt_WR - dynamic ODT off */ + unsigned int srt = 0; /* self-refresh temerature, normal range */ + unsigned int asr = 0; /* auto self-refresh disable */ + unsigned int cwl = compute_cas_write_latency() - 5; + unsigned int pasr = 0; /* partial array self refresh disable */ + + if (popts->rtt_override) + rtt_wr = popts->rtt_wr_override_value; + else + rtt_wr = popts->cs_local_opts[0].odt_rtt_wr; + + if (common_dimm->extended_op_srt) + srt = common_dimm->extended_op_srt; + + esdmode2 = (0 + | ((rtt_wr & 0x3) << 9) + | ((srt & 0x1) << 7) + | ((asr & 0x1) << 6) + | ((cwl & 0x7) << 3) + | ((pasr & 0x7) << 0)); + ddr->ddr_sdram_mode_2 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + debug("FSLDDR: ddr_sdram_mode_2 = 0x%08x\n", ddr->ddr_sdram_mode_2); + + if (unq_mrs_en) { /* unique mode registers are supported */ + for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (popts->rtt_override) + rtt_wr = popts->rtt_wr_override_value; + else + rtt_wr = popts->cs_local_opts[i].odt_rtt_wr; + + esdmode2 &= 0xF9FF; /* clear bit 10, 9 */ + esdmode2 |= (rtt_wr & 0x3) << 9; + switch (i) { + case 1: + ddr->ddr_sdram_mode_4 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + break; + case 2: + ddr->ddr_sdram_mode_6 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + break; + case 3: + ddr->ddr_sdram_mode_8 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + break; + } + } + debug("FSLDDR: ddr_sdram_mode_4 = 0x%08x\n", + ddr->ddr_sdram_mode_4); + debug("FSLDDR: ddr_sdram_mode_6 = 0x%08x\n", + ddr->ddr_sdram_mode_6); + debug("FSLDDR: ddr_sdram_mode_8 = 0x%08x\n", + ddr->ddr_sdram_mode_8); + } +} + +#else /* for DDR2 and DDR1 */ +/* DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) */ +static void set_ddr_sdram_mode_2(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + const unsigned int unq_mrs_en) +{ + unsigned short esdmode2 = 0; /* Extended SDRAM mode 2 */ + unsigned short esdmode3 = 0; /* Extended SDRAM mode 3 */ + + ddr->ddr_sdram_mode_2 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + debug("FSLDDR: ddr_sdram_mode_2 = 0x%08x\n", ddr->ddr_sdram_mode_2); +} +#endif + +#ifdef CONFIG_SYS_FSL_DDR4 +/* DDR SDRAM Mode configuration 9 (DDR_SDRAM_MODE_9) */ +static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + const unsigned int unq_mrs_en) +{ + int i; + unsigned short esdmode4 = 0; /* Extended SDRAM mode 4 */ + unsigned short esdmode5; /* Extended SDRAM mode 5 */ + + esdmode5 = 0x00000400; /* Data mask enabled */ + + ddr->ddr_sdram_mode_9 = (0 + | ((esdmode4 & 0xffff) << 16) + | ((esdmode5 & 0xffff) << 0) + ); + debug("FSLDDR: ddr_sdram_mode_9) = 0x%08x\n", ddr->ddr_sdram_mode_9); + if (unq_mrs_en) { /* unique mode registers are supported */ + for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + switch (i) { + case 1: + ddr->ddr_sdram_mode_11 = (0 + | ((esdmode4 & 0xFFFF) << 16) + | ((esdmode5 & 0xFFFF) << 0) + ); + break; + case 2: + ddr->ddr_sdram_mode_13 = (0 + | ((esdmode4 & 0xFFFF) << 16) + | ((esdmode5 & 0xFFFF) << 0) + ); + break; + case 3: + ddr->ddr_sdram_mode_15 = (0 + | ((esdmode4 & 0xFFFF) << 16) + | ((esdmode5 & 0xFFFF) << 0) + ); + break; + } + } + debug("FSLDDR: ddr_sdram_mode_11 = 0x%08x\n", + ddr->ddr_sdram_mode_11); + debug("FSLDDR: ddr_sdram_mode_13 = 0x%08x\n", + ddr->ddr_sdram_mode_13); + debug("FSLDDR: ddr_sdram_mode_15 = 0x%08x\n", + ddr->ddr_sdram_mode_15); + } +} + +/* DDR SDRAM Mode configuration 10 (DDR_SDRAM_MODE_10) */ +static void set_ddr_sdram_mode_10(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + const unsigned int unq_mrs_en) +{ + int i; + unsigned short esdmode6 = 0; /* Extended SDRAM mode 6 */ + unsigned short esdmode7 = 0; /* Extended SDRAM mode 7 */ + unsigned int tccdl_min = picos_to_mclk(common_dimm->tccdl_ps); + + esdmode6 = ((tccdl_min - 4) & 0x7) << 10; + + ddr->ddr_sdram_mode_10 = (0 + | ((esdmode6 & 0xffff) << 16) + | ((esdmode7 & 0xffff) << 0) + ); + debug("FSLDDR: ddr_sdram_mode_10) = 0x%08x\n", ddr->ddr_sdram_mode_10); + if (unq_mrs_en) { /* unique mode registers are supported */ + for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + switch (i) { + case 1: + ddr->ddr_sdram_mode_12 = (0 + | ((esdmode6 & 0xFFFF) << 16) + | ((esdmode7 & 0xFFFF) << 0) + ); + break; + case 2: + ddr->ddr_sdram_mode_14 = (0 + | ((esdmode6 & 0xFFFF) << 16) + | ((esdmode7 & 0xFFFF) << 0) + ); + break; + case 3: + ddr->ddr_sdram_mode_16 = (0 + | ((esdmode6 & 0xFFFF) << 16) + | ((esdmode7 & 0xFFFF) << 0) + ); + break; + } + } + debug("FSLDDR: ddr_sdram_mode_12 = 0x%08x\n", + ddr->ddr_sdram_mode_12); + debug("FSLDDR: ddr_sdram_mode_14 = 0x%08x\n", + ddr->ddr_sdram_mode_14); + debug("FSLDDR: ddr_sdram_mode_16 = 0x%08x\n", + ddr->ddr_sdram_mode_16); + } +} + +#endif + +/* DDR SDRAM Interval Configuration (DDR_SDRAM_INTERVAL) */ +static void set_ddr_sdram_interval(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm) +{ + unsigned int refint; /* Refresh interval */ + unsigned int bstopre; /* Precharge interval */ + + refint = picos_to_mclk(common_dimm->refresh_rate_ps); + + bstopre = popts->bstopre; + + /* refint field used 0x3FFF in earlier controllers */ + ddr->ddr_sdram_interval = (0 + | ((refint & 0xFFFF) << 16) + | ((bstopre & 0x3FFF) << 0) + ); + debug("FSLDDR: ddr_sdram_interval = 0x%08x\n", ddr->ddr_sdram_interval); +} + +#ifdef CONFIG_SYS_FSL_DDR4 +/* DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) */ +static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency, + unsigned int additive_latency, + const unsigned int unq_mrs_en) +{ + int i; + unsigned short esdmode; /* Extended SDRAM mode */ + unsigned short sdmode; /* SDRAM mode */ + + /* Mode Register - MR1 */ + unsigned int qoff = 0; /* Output buffer enable 0=yes, 1=no */ + unsigned int tdqs_en = 0; /* TDQS Enable: 0=no, 1=yes */ + unsigned int rtt; + unsigned int wrlvl_en = 0; /* Write level enable: 0=no, 1=yes */ + unsigned int al = 0; /* Posted CAS# additive latency (AL) */ + unsigned int dic = 0; /* Output driver impedance, 40ohm */ + unsigned int dll_en = 1; /* DLL Enable 1=Enable (Normal), + 0=Disable (Test/Debug) */ + + /* Mode Register - MR0 */ + unsigned int wr = 0; /* Write Recovery */ + unsigned int dll_rst; /* DLL Reset */ + unsigned int mode; /* Normal=0 or Test=1 */ + unsigned int caslat = 4;/* CAS# latency, default set as 6 cycles */ + /* BT: Burst Type (0=Nibble Sequential, 1=Interleaved) */ + unsigned int bt; + unsigned int bl; /* BL: Burst Length */ + + unsigned int wr_mclk; + /* DDR4 support WR 10, 12, 14, 16, 18, 20, 24 */ + static const u8 wr_table[] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6}; + /* DDR4 support CAS 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24 */ + static const u8 cas_latency_table[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, + 9, 9, 10, 10, 11, 11}; + + if (popts->rtt_override) + rtt = popts->rtt_override_value; + else + rtt = popts->cs_local_opts[0].odt_rtt_norm; + + if (additive_latency == (cas_latency - 1)) + al = 1; + if (additive_latency == (cas_latency - 2)) + al = 2; + + if (popts->quad_rank_present) + dic = 1; /* output driver impedance 240/7 ohm */ + + /* + * The esdmode value will also be used for writing + * MR1 during write leveling for DDR3, although the + * bits specifically related to the write leveling + * scheme will be handled automatically by the DDR + * controller. so we set the wrlvl_en = 0 here. + */ + esdmode = (0 + | ((qoff & 0x1) << 12) + | ((tdqs_en & 0x1) << 11) + | ((rtt & 0x7) << 8) + | ((wrlvl_en & 0x1) << 7) + | ((al & 0x3) << 3) + | ((dic & 0x3) << 1) /* DIC field is split */ + | ((dll_en & 0x1) << 0) + ); + + /* + * DLL control for precharge PD + * 0=slow exit DLL off (tXPDLL) + * 1=fast exit DLL on (tXP) + */ + + wr_mclk = picos_to_mclk(common_dimm->twr_ps); + if (wr_mclk <= 24) { + wr = wr_table[wr_mclk - 10]; + } else { + printf("Error: unsupported write recovery for mode register wr_mclk = %d\n", + wr_mclk); + } + + dll_rst = 0; /* dll no reset */ + mode = 0; /* normal mode */ + + /* look up table to get the cas latency bits */ + if (cas_latency >= 9 && cas_latency <= 24) + caslat = cas_latency_table[cas_latency - 9]; + else + printf("Error: unsupported cas latency for mode register\n"); + + bt = 0; /* Nibble sequential */ + + switch (popts->burst_length) { + case DDR_BL8: + bl = 0; + break; + case DDR_OTF: + bl = 1; + break; + case DDR_BC4: + bl = 2; + break; + default: + printf("Error: invalid burst length of %u specified. ", + popts->burst_length); + puts("Defaulting to on-the-fly BC4 or BL8 beats.\n"); + bl = 1; + break; + } + + sdmode = (0 + | ((wr & 0x7) << 9) + | ((dll_rst & 0x1) << 8) + | ((mode & 0x1) << 7) + | (((caslat >> 1) & 0x7) << 4) + | ((bt & 0x1) << 3) + | ((caslat & 1) << 2) + | ((bl & 0x3) << 0) + ); + + ddr->ddr_sdram_mode = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + + debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode); + + if (unq_mrs_en) { /* unique mode registers are supported */ + for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (popts->rtt_override) + rtt = popts->rtt_override_value; + else + rtt = popts->cs_local_opts[i].odt_rtt_norm; + + esdmode &= 0xF8FF; /* clear bit 10,9,8 for rtt */ + esdmode |= (rtt & 0x7) << 8; + switch (i) { + case 1: + ddr->ddr_sdram_mode_3 = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + break; + case 2: + ddr->ddr_sdram_mode_5 = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + break; + case 3: + ddr->ddr_sdram_mode_7 = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + break; + } + } + debug("FSLDDR: ddr_sdram_mode_3 = 0x%08x\n", + ddr->ddr_sdram_mode_3); + debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n", + ddr->ddr_sdram_mode_5); + debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n", + ddr->ddr_sdram_mode_5); + } +} + +#elif defined(CONFIG_SYS_FSL_DDR3) +/* DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) */ +static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency, + unsigned int additive_latency, + const unsigned int unq_mrs_en) +{ + int i; + unsigned short esdmode; /* Extended SDRAM mode */ + unsigned short sdmode; /* SDRAM mode */ + + /* Mode Register - MR1 */ + unsigned int qoff = 0; /* Output buffer enable 0=yes, 1=no */ + unsigned int tdqs_en = 0; /* TDQS Enable: 0=no, 1=yes */ + unsigned int rtt; + unsigned int wrlvl_en = 0; /* Write level enable: 0=no, 1=yes */ + unsigned int al = 0; /* Posted CAS# additive latency (AL) */ + unsigned int dic = 0; /* Output driver impedance, 40ohm */ + unsigned int dll_en = 0; /* DLL Enable 0=Enable (Normal), + 1=Disable (Test/Debug) */ + + /* Mode Register - MR0 */ + unsigned int dll_on; /* DLL control for precharge PD, 0=off, 1=on */ + unsigned int wr = 0; /* Write Recovery */ + unsigned int dll_rst; /* DLL Reset */ + unsigned int mode; /* Normal=0 or Test=1 */ + unsigned int caslat = 4;/* CAS# latency, default set as 6 cycles */ + /* BT: Burst Type (0=Nibble Sequential, 1=Interleaved) */ + unsigned int bt; + unsigned int bl; /* BL: Burst Length */ + + unsigned int wr_mclk; + /* + * DDR_SDRAM_MODE doesn't support 9,11,13,15 + * Please refer JEDEC Standard No. 79-3E for Mode Register MR0 + * for this table + */ + static const u8 wr_table[] = {1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0}; + + if (popts->rtt_override) + rtt = popts->rtt_override_value; + else + rtt = popts->cs_local_opts[0].odt_rtt_norm; + + if (additive_latency == (cas_latency - 1)) + al = 1; + if (additive_latency == (cas_latency - 2)) + al = 2; + + if (popts->quad_rank_present) + dic = 1; /* output driver impedance 240/7 ohm */ + + /* + * The esdmode value will also be used for writing + * MR1 during write leveling for DDR3, although the + * bits specifically related to the write leveling + * scheme will be handled automatically by the DDR + * controller. so we set the wrlvl_en = 0 here. + */ + esdmode = (0 + | ((qoff & 0x1) << 12) + | ((tdqs_en & 0x1) << 11) + | ((rtt & 0x4) << 7) /* rtt field is split */ + | ((wrlvl_en & 0x1) << 7) + | ((rtt & 0x2) << 5) /* rtt field is split */ + | ((dic & 0x2) << 4) /* DIC field is split */ + | ((al & 0x3) << 3) + | ((rtt & 0x1) << 2) /* rtt field is split */ + | ((dic & 0x1) << 1) /* DIC field is split */ + | ((dll_en & 0x1) << 0) + ); + + /* + * DLL control for precharge PD + * 0=slow exit DLL off (tXPDLL) + * 1=fast exit DLL on (tXP) + */ + dll_on = 1; + + wr_mclk = picos_to_mclk(common_dimm->twr_ps); + if (wr_mclk <= 16) { + wr = wr_table[wr_mclk - 5]; + } else { + printf("Error: unsupported write recovery for mode register " + "wr_mclk = %d\n", wr_mclk); + } + + dll_rst = 0; /* dll no reset */ + mode = 0; /* normal mode */ + + /* look up table to get the cas latency bits */ + if (cas_latency >= 5 && cas_latency <= 16) { + unsigned char cas_latency_table[] = { + 0x2, /* 5 clocks */ + 0x4, /* 6 clocks */ + 0x6, /* 7 clocks */ + 0x8, /* 8 clocks */ + 0xa, /* 9 clocks */ + 0xc, /* 10 clocks */ + 0xe, /* 11 clocks */ + 0x1, /* 12 clocks */ + 0x3, /* 13 clocks */ + 0x5, /* 14 clocks */ + 0x7, /* 15 clocks */ + 0x9, /* 16 clocks */ + }; + caslat = cas_latency_table[cas_latency - 5]; + } else { + printf("Error: unsupported cas latency for mode register\n"); + } + + bt = 0; /* Nibble sequential */ + + switch (popts->burst_length) { + case DDR_BL8: + bl = 0; + break; + case DDR_OTF: + bl = 1; + break; + case DDR_BC4: + bl = 2; + break; + default: + printf("Error: invalid burst length of %u specified. " + " Defaulting to on-the-fly BC4 or BL8 beats.\n", + popts->burst_length); + bl = 1; + break; + } + + sdmode = (0 + | ((dll_on & 0x1) << 12) + | ((wr & 0x7) << 9) + | ((dll_rst & 0x1) << 8) + | ((mode & 0x1) << 7) + | (((caslat >> 1) & 0x7) << 4) + | ((bt & 0x1) << 3) + | ((caslat & 1) << 2) + | ((bl & 0x3) << 0) + ); + + ddr->ddr_sdram_mode = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + + debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode); + + if (unq_mrs_en) { /* unique mode registers are supported */ + for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (popts->rtt_override) + rtt = popts->rtt_override_value; + else + rtt = popts->cs_local_opts[i].odt_rtt_norm; + + esdmode &= 0xFDBB; /* clear bit 9,6,2 */ + esdmode |= (0 + | ((rtt & 0x4) << 7) /* rtt field is split */ + | ((rtt & 0x2) << 5) /* rtt field is split */ + | ((rtt & 0x1) << 2) /* rtt field is split */ + ); + switch (i) { + case 1: + ddr->ddr_sdram_mode_3 = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + break; + case 2: + ddr->ddr_sdram_mode_5 = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + break; + case 3: + ddr->ddr_sdram_mode_7 = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + break; + } + } + debug("FSLDDR: ddr_sdram_mode_3 = 0x%08x\n", + ddr->ddr_sdram_mode_3); + debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n", + ddr->ddr_sdram_mode_5); + debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n", + ddr->ddr_sdram_mode_5); + } +} + +#else /* !CONFIG_SYS_FSL_DDR3 */ + +/* DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) */ +static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency, + unsigned int additive_latency, + const unsigned int unq_mrs_en) +{ + unsigned short esdmode; /* Extended SDRAM mode */ + unsigned short sdmode; /* SDRAM mode */ + + /* + * FIXME: This ought to be pre-calculated in a + * technology-specific routine, + * e.g. compute_DDR2_mode_register(), and then the + * sdmode and esdmode passed in as part of common_dimm. + */ + + /* Extended Mode Register */ + unsigned int mrs = 0; /* Mode Register Set */ + unsigned int outputs = 0; /* 0=Enabled, 1=Disabled */ + unsigned int rdqs_en = 0; /* RDQS Enable: 0=no, 1=yes */ + unsigned int dqs_en = 0; /* DQS# Enable: 0=enable, 1=disable */ + unsigned int ocd = 0; /* 0x0=OCD not supported, + 0x7=OCD default state */ + unsigned int rtt; + unsigned int al; /* Posted CAS# additive latency (AL) */ + unsigned int ods = 0; /* Output Drive Strength: + 0 = Full strength (18ohm) + 1 = Reduced strength (4ohm) */ + unsigned int dll_en = 0; /* DLL Enable 0=Enable (Normal), + 1=Disable (Test/Debug) */ + + /* Mode Register (MR) */ + unsigned int mr; /* Mode Register Definition */ + unsigned int pd; /* Power-Down Mode */ + unsigned int wr; /* Write Recovery */ + unsigned int dll_res; /* DLL Reset */ + unsigned int mode; /* Normal=0 or Test=1 */ + unsigned int caslat = 0;/* CAS# latency */ + /* BT: Burst Type (0=Sequential, 1=Interleaved) */ + unsigned int bt; + unsigned int bl; /* BL: Burst Length */ + + dqs_en = !popts->dqs_config; + rtt = fsl_ddr_get_rtt(); + + al = additive_latency; + + esdmode = (0 + | ((mrs & 0x3) << 14) + | ((outputs & 0x1) << 12) + | ((rdqs_en & 0x1) << 11) + | ((dqs_en & 0x1) << 10) + | ((ocd & 0x7) << 7) + | ((rtt & 0x2) << 5) /* rtt field is split */ + | ((al & 0x7) << 3) + | ((rtt & 0x1) << 2) /* rtt field is split */ + | ((ods & 0x1) << 1) + | ((dll_en & 0x1) << 0) + ); + + mr = 0; /* FIXME: CHECKME */ + + /* + * 0 = Fast Exit (Normal) + * 1 = Slow Exit (Low Power) + */ + pd = 0; + +#if defined(CONFIG_SYS_FSL_DDR1) + wr = 0; /* Historical */ +#elif defined(CONFIG_SYS_FSL_DDR2) + wr = picos_to_mclk(common_dimm->twr_ps); +#endif + dll_res = 0; + mode = 0; + +#if defined(CONFIG_SYS_FSL_DDR1) + if (1 <= cas_latency && cas_latency <= 4) { + unsigned char mode_caslat_table[4] = { + 0x5, /* 1.5 clocks */ + 0x2, /* 2.0 clocks */ + 0x6, /* 2.5 clocks */ + 0x3 /* 3.0 clocks */ + }; + caslat = mode_caslat_table[cas_latency - 1]; + } else { + printf("Warning: unknown cas_latency %d\n", cas_latency); + } +#elif defined(CONFIG_SYS_FSL_DDR2) + caslat = cas_latency; +#endif + bt = 0; + + switch (popts->burst_length) { + case DDR_BL4: + bl = 2; + break; + case DDR_BL8: + bl = 3; + break; + default: + printf("Error: invalid burst length of %u specified. " + " Defaulting to 4 beats.\n", + popts->burst_length); + bl = 2; + break; + } + + sdmode = (0 + | ((mr & 0x3) << 14) + | ((pd & 0x1) << 12) + | ((wr & 0x7) << 9) + | ((dll_res & 0x1) << 8) + | ((mode & 0x1) << 7) + | ((caslat & 0x7) << 4) + | ((bt & 0x1) << 3) + | ((bl & 0x7) << 0) + ); + + ddr->ddr_sdram_mode = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode); +} +#endif + +/* DDR SDRAM Data Initialization (DDR_DATA_INIT) */ +static void set_ddr_data_init(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int init_value; /* Initialization value */ + +#ifdef CONFIG_MEM_INIT_VALUE + init_value = CONFIG_MEM_INIT_VALUE; +#else + init_value = 0xDEADBEEF; +#endif + ddr->ddr_data_init = init_value; +} + +/* + * DDR SDRAM Clock Control (DDR_SDRAM_CLK_CNTL) + * The old controller on the 8540/60 doesn't have this register. + * Hope it's OK to set it (to 0) anyway. + */ +static void set_ddr_sdram_clk_cntl(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts) +{ + unsigned int clk_adjust; /* Clock adjust */ + + clk_adjust = popts->clk_adjust; + ddr->ddr_sdram_clk_cntl = (clk_adjust & 0xF) << 23; + debug("FSLDDR: clk_cntl = 0x%08x\n", ddr->ddr_sdram_clk_cntl); +} + +/* DDR Initialization Address (DDR_INIT_ADDR) */ +static void set_ddr_init_addr(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int init_addr = 0; /* Initialization address */ + + ddr->ddr_init_addr = init_addr; +} + +/* DDR Initialization Address (DDR_INIT_EXT_ADDR) */ +static void set_ddr_init_ext_addr(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int uia = 0; /* Use initialization address */ + unsigned int init_ext_addr = 0; /* Initialization address */ + + ddr->ddr_init_ext_addr = (0 + | ((uia & 0x1) << 31) + | (init_ext_addr & 0xF) + ); +} + +/* DDR SDRAM Timing Configuration 4 (TIMING_CFG_4) */ +static void set_timing_cfg_4(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts) +{ + unsigned int rwt = 0; /* Read-to-write turnaround for same CS */ + unsigned int wrt = 0; /* Write-to-read turnaround for same CS */ + unsigned int rrt = 0; /* Read-to-read turnaround for same CS */ + unsigned int wwt = 0; /* Write-to-write turnaround for same CS */ + unsigned int dll_lock = 0; /* DDR SDRAM DLL Lock Time */ + +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + if (popts->burst_length == DDR_BL8) { + /* We set BL/2 for fixed BL8 */ + rrt = 0; /* BL/2 clocks */ + wwt = 0; /* BL/2 clocks */ + } else { + /* We need to set BL/2 + 2 to BC4 and OTF */ + rrt = 2; /* BL/2 + 2 clocks */ + wwt = 2; /* BL/2 + 2 clocks */ + } +#endif + +#ifdef CONFIG_SYS_FSL_DDR4 + dll_lock = 2; /* tDLLK = 1024 clocks */ +#elif defined(CONFIG_SYS_FSL_DDR3) + dll_lock = 1; /* tDLLK = 512 clocks from spec */ +#endif + ddr->timing_cfg_4 = (0 + | ((rwt & 0xf) << 28) + | ((wrt & 0xf) << 24) + | ((rrt & 0xf) << 20) + | ((wwt & 0xf) << 16) + | (dll_lock & 0x3) + ); + debug("FSLDDR: timing_cfg_4 = 0x%08x\n", ddr->timing_cfg_4); +} + +/* DDR SDRAM Timing Configuration 5 (TIMING_CFG_5) */ +static void set_timing_cfg_5(fsl_ddr_cfg_regs_t *ddr, unsigned int cas_latency) +{ + unsigned int rodt_on = 0; /* Read to ODT on */ + unsigned int rodt_off = 0; /* Read to ODT off */ + unsigned int wodt_on = 0; /* Write to ODT on */ + unsigned int wodt_off = 0; /* Write to ODT off */ + +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + unsigned int wr_lat = ((ddr->timing_cfg_2 & 0x00780000) >> 19) + + ((ddr->timing_cfg_2 & 0x00040000) >> 14); + /* rodt_on = timing_cfg_1[caslat] - timing_cfg_2[wrlat] + 1 */ + if (cas_latency >= wr_lat) + rodt_on = cas_latency - wr_lat + 1; + rodt_off = 4; /* 4 clocks */ + wodt_on = 1; /* 1 clocks */ + wodt_off = 4; /* 4 clocks */ +#endif + + ddr->timing_cfg_5 = (0 + | ((rodt_on & 0x1f) << 24) + | ((rodt_off & 0x7) << 20) + | ((wodt_on & 0x1f) << 12) + | ((wodt_off & 0x7) << 8) + ); + debug("FSLDDR: timing_cfg_5 = 0x%08x\n", ddr->timing_cfg_5); +} + +#ifdef CONFIG_SYS_FSL_DDR4 +static void set_timing_cfg_6(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int hs_caslat = 0; + unsigned int hs_wrlat = 0; + unsigned int hs_wrrec = 0; + unsigned int hs_clkadj = 0; + unsigned int hs_wrlvl_start = 0; + + ddr->timing_cfg_6 = (0 + | ((hs_caslat & 0x1f) << 24) + | ((hs_wrlat & 0x1f) << 19) + | ((hs_wrrec & 0x1f) << 12) + | ((hs_clkadj & 0x1f) << 6) + | ((hs_wrlvl_start & 0x1f) << 0) + ); + debug("FSLDDR: timing_cfg_6 = 0x%08x\n", ddr->timing_cfg_6); +} + +static void set_timing_cfg_7(fsl_ddr_cfg_regs_t *ddr, + const common_timing_params_t *common_dimm) +{ + unsigned int txpr, tcksre, tcksrx; + unsigned int cke_rst, cksre, cksrx, par_lat, cs_to_cmd; + + txpr = max(5, picos_to_mclk(common_dimm->trfc1_ps + 10000)); + tcksre = max(5, picos_to_mclk(10000)); + tcksrx = max(5, picos_to_mclk(10000)); + par_lat = 0; + cs_to_cmd = 0; + + if (txpr <= 200) + cke_rst = 0; + else if (txpr <= 256) + cke_rst = 1; + else if (txpr <= 512) + cke_rst = 2; + else + cke_rst = 3; + + if (tcksre <= 19) + cksre = tcksre - 5; + else + cksre = 15; + + if (tcksrx <= 19) + cksrx = tcksrx - 5; + else + cksrx = 15; + + ddr->timing_cfg_7 = (0 + | ((cke_rst & 0x3) << 28) + | ((cksre & 0xf) << 24) + | ((cksrx & 0xf) << 20) + | ((par_lat & 0xf) << 16) + | ((cs_to_cmd & 0xf) << 4) + ); + debug("FSLDDR: timing_cfg_7 = 0x%08x\n", ddr->timing_cfg_7); +} + +static void set_timing_cfg_8(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency) +{ + unsigned int rwt_bg, wrt_bg, rrt_bg, wwt_bg; + unsigned int acttoact_bg, wrtord_bg, pre_all_rec; + unsigned int tccdl = picos_to_mclk(common_dimm->tccdl_ps); + unsigned int wr_lat = ((ddr->timing_cfg_2 & 0x00780000) >> 19) + + ((ddr->timing_cfg_2 & 0x00040000) >> 14); + + rwt_bg = cas_latency + 2 + 4 - wr_lat; + if (rwt_bg < tccdl) + rwt_bg = tccdl - rwt_bg; + else + rwt_bg = 0; + + wrt_bg = wr_lat + 4 + 1 - cas_latency; + if (wrt_bg < tccdl) + wrt_bg = tccdl - wrt_bg; + else + wrt_bg = 0; + + if (popts->burst_length == DDR_BL8) { + rrt_bg = tccdl - 4; + wwt_bg = tccdl - 4; + } else { + rrt_bg = tccdl - 2; + wwt_bg = tccdl - 4; + } + + acttoact_bg = picos_to_mclk(common_dimm->trrdl_ps); + wrtord_bg = max(4, picos_to_mclk(7500)); + pre_all_rec = 0; + + ddr->timing_cfg_8 = (0 + | ((rwt_bg & 0xf) << 28) + | ((wrt_bg & 0xf) << 24) + | ((rrt_bg & 0xf) << 20) + | ((wwt_bg & 0xf) << 16) + | ((acttoact_bg & 0xf) << 12) + | ((wrtord_bg & 0xf) << 8) + | ((pre_all_rec & 0x1f) << 0) + ); + + debug("FSLDDR: timing_cfg_8 = 0x%08x\n", ddr->timing_cfg_8); +} + +static void set_timing_cfg_9(fsl_ddr_cfg_regs_t *ddr) +{ + ddr->timing_cfg_9 = 0; + debug("FSLDDR: timing_cfg_9 = 0x%08x\n", ddr->timing_cfg_9); +} + +static void set_ddr_dq_mapping(fsl_ddr_cfg_regs_t *ddr, + const dimm_params_t *dimm_params) +{ + ddr->dq_map_0 = ((dimm_params->dq_mapping[0] & 0x3F) << 26) | + ((dimm_params->dq_mapping[1] & 0x3F) << 20) | + ((dimm_params->dq_mapping[2] & 0x3F) << 14) | + ((dimm_params->dq_mapping[3] & 0x3F) << 8) | + ((dimm_params->dq_mapping[4] & 0x3F) << 2); + + ddr->dq_map_1 = ((dimm_params->dq_mapping[5] & 0x3F) << 26) | + ((dimm_params->dq_mapping[6] & 0x3F) << 20) | + ((dimm_params->dq_mapping[7] & 0x3F) << 14) | + ((dimm_params->dq_mapping[10] & 0x3F) << 8) | + ((dimm_params->dq_mapping[11] & 0x3F) << 2); + + ddr->dq_map_2 = ((dimm_params->dq_mapping[12] & 0x3F) << 26) | + ((dimm_params->dq_mapping[13] & 0x3F) << 20) | + ((dimm_params->dq_mapping[14] & 0x3F) << 14) | + ((dimm_params->dq_mapping[15] & 0x3F) << 8) | + ((dimm_params->dq_mapping[16] & 0x3F) << 2); + + ddr->dq_map_3 = ((dimm_params->dq_mapping[17] & 0x3F) << 26) | + ((dimm_params->dq_mapping[8] & 0x3F) << 20) | + ((dimm_params->dq_mapping[9] & 0x3F) << 14) | + dimm_params->dq_mapping_ors; + + debug("FSLDDR: dq_map_0 = 0x%08x\n", ddr->dq_map_0); + debug("FSLDDR: dq_map_1 = 0x%08x\n", ddr->dq_map_1); + debug("FSLDDR: dq_map_2 = 0x%08x\n", ddr->dq_map_2); + debug("FSLDDR: dq_map_3 = 0x%08x\n", ddr->dq_map_3); +} +static void set_ddr_sdram_cfg_3(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts) +{ + int rd_pre; + + rd_pre = popts->quad_rank_present ? 1 : 0; + + ddr->ddr_sdram_cfg_3 = (rd_pre & 0x1) << 16; + + debug("FSLDDR: ddr_sdram_cfg_3 = 0x%08x\n", ddr->ddr_sdram_cfg_3); +} +#endif /* CONFIG_SYS_FSL_DDR4 */ + +/* DDR ZQ Calibration Control (DDR_ZQ_CNTL) */ +static void set_ddr_zq_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int zq_en) +{ + unsigned int zqinit = 0;/* POR ZQ Calibration Time (tZQinit) */ + /* Normal Operation Full Calibration Time (tZQoper) */ + unsigned int zqoper = 0; + /* Normal Operation Short Calibration Time (tZQCS) */ + unsigned int zqcs = 0; +#ifdef CONFIG_SYS_FSL_DDR4 + unsigned int zqcs_init; +#endif + + if (zq_en) { +#ifdef CONFIG_SYS_FSL_DDR4 + zqinit = 10; /* 1024 clocks */ + zqoper = 9; /* 512 clocks */ + zqcs = 7; /* 128 clocks */ + zqcs_init = 5; /* 1024 refresh sequences */ +#else + zqinit = 9; /* 512 clocks */ + zqoper = 8; /* 256 clocks */ + zqcs = 6; /* 64 clocks */ +#endif + } + + ddr->ddr_zq_cntl = (0 + | ((zq_en & 0x1) << 31) + | ((zqinit & 0xF) << 24) + | ((zqoper & 0xF) << 16) + | ((zqcs & 0xF) << 8) +#ifdef CONFIG_SYS_FSL_DDR4 + | ((zqcs_init & 0xF) << 0) +#endif + ); + debug("FSLDDR: zq_cntl = 0x%08x\n", ddr->ddr_zq_cntl); +} + +/* DDR Write Leveling Control (DDR_WRLVL_CNTL) */ +static void set_ddr_wrlvl_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int wrlvl_en, + const memctl_options_t *popts) +{ + /* + * First DQS pulse rising edge after margining mode + * is programmed (tWL_MRD) + */ + unsigned int wrlvl_mrd = 0; + /* ODT delay after margining mode is programmed (tWL_ODTEN) */ + unsigned int wrlvl_odten = 0; + /* DQS/DQS_ delay after margining mode is programmed (tWL_DQSEN) */ + unsigned int wrlvl_dqsen = 0; + /* WRLVL_SMPL: Write leveling sample time */ + unsigned int wrlvl_smpl = 0; + /* WRLVL_WLR: Write leveling repeition time */ + unsigned int wrlvl_wlr = 0; + /* WRLVL_START: Write leveling start time */ + unsigned int wrlvl_start = 0; + + /* suggest enable write leveling for DDR3 due to fly-by topology */ + if (wrlvl_en) { + /* tWL_MRD min = 40 nCK, we set it 64 */ + wrlvl_mrd = 0x6; + /* tWL_ODTEN 128 */ + wrlvl_odten = 0x7; + /* tWL_DQSEN min = 25 nCK, we set it 32 */ + wrlvl_dqsen = 0x5; + /* + * Write leveling sample time at least need 6 clocks + * higher than tWLO to allow enough time for progagation + * delay and sampling the prime data bits. + */ + wrlvl_smpl = 0xf; + /* + * Write leveling repetition time + * at least tWLO + 6 clocks clocks + * we set it 64 + */ + wrlvl_wlr = 0x6; + /* + * Write leveling start time + * The value use for the DQS_ADJUST for the first sample + * when write leveling is enabled. It probably needs to be + * overriden per platform. + */ + wrlvl_start = 0x8; + /* + * Override the write leveling sample and start time + * according to specific board + */ + if (popts->wrlvl_override) { + wrlvl_smpl = popts->wrlvl_sample; + wrlvl_start = popts->wrlvl_start; + } + } + + ddr->ddr_wrlvl_cntl = (0 + | ((wrlvl_en & 0x1) << 31) + | ((wrlvl_mrd & 0x7) << 24) + | ((wrlvl_odten & 0x7) << 20) + | ((wrlvl_dqsen & 0x7) << 16) + | ((wrlvl_smpl & 0xf) << 12) + | ((wrlvl_wlr & 0x7) << 8) + | ((wrlvl_start & 0x1F) << 0) + ); + debug("FSLDDR: wrlvl_cntl = 0x%08x\n", ddr->ddr_wrlvl_cntl); + ddr->ddr_wrlvl_cntl_2 = popts->wrlvl_ctl_2; + debug("FSLDDR: wrlvl_cntl_2 = 0x%08x\n", ddr->ddr_wrlvl_cntl_2); + ddr->ddr_wrlvl_cntl_3 = popts->wrlvl_ctl_3; + debug("FSLDDR: wrlvl_cntl_3 = 0x%08x\n", ddr->ddr_wrlvl_cntl_3); + +} + +/* DDR Self Refresh Counter (DDR_SR_CNTR) */ +static void set_ddr_sr_cntr(fsl_ddr_cfg_regs_t *ddr, unsigned int sr_it) +{ + /* Self Refresh Idle Threshold */ + ddr->ddr_sr_cntr = (sr_it & 0xF) << 16; +} + +static void set_ddr_eor(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts) +{ + if (popts->addr_hash) { + ddr->ddr_eor = 0x40000000; /* address hash enable */ + puts("Address hashing enabled.\n"); + } +} + +static void set_ddr_cdr1(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts) +{ + ddr->ddr_cdr1 = popts->ddr_cdr1; + debug("FSLDDR: ddr_cdr1 = 0x%08x\n", ddr->ddr_cdr1); +} + +static void set_ddr_cdr2(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts) +{ + ddr->ddr_cdr2 = popts->ddr_cdr2; + debug("FSLDDR: ddr_cdr2 = 0x%08x\n", ddr->ddr_cdr2); +} + +unsigned int +check_fsl_memctl_config_regs(const fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int res = 0; + + /* + * Check that DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN] are + * not set at the same time. + */ + if (ddr->ddr_sdram_cfg & 0x10000000 + && ddr->ddr_sdram_cfg & 0x00008000) { + printf("Error: DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN] " + " should not be set at the same time.\n"); + res++; + } + + return res; +} + +unsigned int +compute_fsl_memctl_config_regs(const memctl_options_t *popts, + fsl_ddr_cfg_regs_t *ddr, + const common_timing_params_t *common_dimm, + const dimm_params_t *dimm_params, + unsigned int dbw_cap_adj, + unsigned int size_only) +{ + unsigned int i; + unsigned int cas_latency; + unsigned int additive_latency; + unsigned int sr_it; + unsigned int zq_en; + unsigned int wrlvl_en; + unsigned int ip_rev = 0; + unsigned int unq_mrs_en = 0; + int cs_en = 1; + + memset(ddr, 0, sizeof(fsl_ddr_cfg_regs_t)); + + if (common_dimm == NULL) { + printf("Error: subset DIMM params struct null pointer\n"); + return 1; + } + + /* + * Process overrides first. + * + * FIXME: somehow add dereated caslat to this + */ + cas_latency = (popts->cas_latency_override) + ? popts->cas_latency_override_value + : common_dimm->lowest_common_spd_caslat; + + additive_latency = (popts->additive_latency_override) + ? popts->additive_latency_override_value + : common_dimm->additive_latency; + + sr_it = (popts->auto_self_refresh_en) + ? popts->sr_it + : 0; + /* ZQ calibration */ + zq_en = (popts->zq_en) ? 1 : 0; + /* write leveling */ + wrlvl_en = (popts->wrlvl_en) ? 1 : 0; + + /* Chip Select Memory Bounds (CSn_BNDS) */ + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + unsigned long long ea, sa; + unsigned int cs_per_dimm + = CONFIG_CHIP_SELECTS_PER_CTRL / CONFIG_DIMM_SLOTS_PER_CTLR; + unsigned int dimm_number + = i / cs_per_dimm; + unsigned long long rank_density + = dimm_params[dimm_number].rank_density >> dbw_cap_adj; + + if (dimm_params[dimm_number].n_ranks == 0) { + debug("Skipping setup of CS%u " + "because n_ranks on DIMM %u is 0\n", i, dimm_number); + continue; + } + if (popts->memctl_interleaving) { + switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) { + case FSL_DDR_CS0_CS1_CS2_CS3: + break; + case FSL_DDR_CS0_CS1: + case FSL_DDR_CS0_CS1_AND_CS2_CS3: + if (i > 1) + cs_en = 0; + break; + case FSL_DDR_CS2_CS3: + default: + if (i > 0) + cs_en = 0; + break; + } + sa = common_dimm->base_address; + ea = sa + common_dimm->total_mem - 1; + } else if (!popts->memctl_interleaving) { + /* + * If memory interleaving between controllers is NOT + * enabled, the starting address for each memory + * controller is distinct. However, because rank + * interleaving is enabled, the starting and ending + * addresses of the total memory on that memory + * controller needs to be programmed into its + * respective CS0_BNDS. + */ + switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) { + case FSL_DDR_CS0_CS1_CS2_CS3: + sa = common_dimm->base_address; + ea = sa + common_dimm->total_mem - 1; + break; + case FSL_DDR_CS0_CS1_AND_CS2_CS3: + if ((i >= 2) && (dimm_number == 0)) { + sa = dimm_params[dimm_number].base_address + + 2 * rank_density; + ea = sa + 2 * rank_density - 1; + } else { + sa = dimm_params[dimm_number].base_address; + ea = sa + 2 * rank_density - 1; + } + break; + case FSL_DDR_CS0_CS1: + if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) { + sa = dimm_params[dimm_number].base_address; + ea = sa + rank_density - 1; + if (i != 1) + sa += (i % cs_per_dimm) * rank_density; + ea += (i % cs_per_dimm) * rank_density; + } else { + sa = 0; + ea = 0; + } + if (i == 0) + ea += rank_density; + break; + case FSL_DDR_CS2_CS3: + if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) { + sa = dimm_params[dimm_number].base_address; + ea = sa + rank_density - 1; + if (i != 3) + sa += (i % cs_per_dimm) * rank_density; + ea += (i % cs_per_dimm) * rank_density; + } else { + sa = 0; + ea = 0; + } + if (i == 2) + ea += (rank_density >> dbw_cap_adj); + break; + default: /* No bank(chip-select) interleaving */ + sa = dimm_params[dimm_number].base_address; + ea = sa + rank_density - 1; + if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) { + sa += (i % cs_per_dimm) * rank_density; + ea += (i % cs_per_dimm) * rank_density; + } else { + sa = 0; + ea = 0; + } + break; + } + } + + sa >>= 24; + ea >>= 24; + + if (cs_en) { + ddr->cs[i].bnds = (0 + | ((sa & 0xffff) << 16) /* starting address */ + | ((ea & 0xffff) << 0) /* ending address */ + ); + } else { + /* setting bnds to 0xffffffff for inactive CS */ + ddr->cs[i].bnds = 0xffffffff; + } + + debug("FSLDDR: cs[%d]_bnds = 0x%08x\n", i, ddr->cs[i].bnds); + set_csn_config(dimm_number, i, ddr, popts, dimm_params); + set_csn_config_2(i, ddr); + } + + /* + * In the case we only need to compute the ddr sdram size, we only need + * to set csn registers, so return from here. + */ + if (size_only) + return 0; + + set_ddr_eor(ddr, popts); + +#if !defined(CONFIG_SYS_FSL_DDR1) + set_timing_cfg_0(ddr, popts, dimm_params); +#endif + + set_timing_cfg_3(ddr, popts, common_dimm, cas_latency, + additive_latency); + set_timing_cfg_1(ddr, popts, common_dimm, cas_latency); + set_timing_cfg_2(ddr, popts, common_dimm, + cas_latency, additive_latency); + + set_ddr_cdr1(ddr, popts); + set_ddr_cdr2(ddr, popts); + set_ddr_sdram_cfg(ddr, popts, common_dimm); + ip_rev = fsl_ddr_get_version(); + if (ip_rev > 0x40400) + unq_mrs_en = 1; + + set_ddr_sdram_cfg_2(ddr, popts, unq_mrs_en); + set_ddr_sdram_mode(ddr, popts, common_dimm, + cas_latency, additive_latency, unq_mrs_en); + set_ddr_sdram_mode_2(ddr, popts, common_dimm, unq_mrs_en); +#ifdef CONFIG_SYS_FSL_DDR4 + set_ddr_sdram_mode_9(ddr, popts, common_dimm, unq_mrs_en); + set_ddr_sdram_mode_10(ddr, popts, common_dimm, unq_mrs_en); +#endif + set_ddr_sdram_interval(ddr, popts, common_dimm); + set_ddr_data_init(ddr); + set_ddr_sdram_clk_cntl(ddr, popts); + set_ddr_init_addr(ddr); + set_ddr_init_ext_addr(ddr); + set_timing_cfg_4(ddr, popts); + set_timing_cfg_5(ddr, cas_latency); +#ifdef CONFIG_SYS_FSL_DDR4 + set_ddr_sdram_cfg_3(ddr, popts); + set_timing_cfg_6(ddr); + set_timing_cfg_7(ddr, common_dimm); + set_timing_cfg_8(ddr, popts, common_dimm, cas_latency); + set_timing_cfg_9(ddr); + set_ddr_dq_mapping(ddr, dimm_params); +#endif + + set_ddr_zq_cntl(ddr, zq_en); + set_ddr_wrlvl_cntl(ddr, wrlvl_en, popts); + + set_ddr_sr_cntr(ddr, sr_it); + + set_ddr_sdram_rcw(ddr, popts, common_dimm); + +#ifdef CONFIG_SYS_FSL_DDR_EMU + /* disble DDR training for emulator */ + ddr->debug[2] = 0x00000400; + ddr->debug[4] = 0xff800000; +#endif + return check_fsl_memctl_config_regs(ddr); +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/ddr1_dimm_params.c b/qemu/roms/u-boot/drivers/ddr/fsl/ddr1_dimm_params.c new file mode 100644 index 000000000..7df27b90b --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/ddr1_dimm_params.c @@ -0,0 +1,343 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include +#include + +#include + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Study these table from Byte 31 of JEDEC SPD Spec. + * + * DDR I DDR II + * Bit Size Size + * --- ----- ------ + * 7 high 512MB 512MB + * 6 256MB 256MB + * 5 128MB 128MB + * 4 64MB 16GB + * 3 32MB 8GB + * 2 16MB 4GB + * 1 2GB 2GB + * 0 low 1GB 1GB + * + * Reorder Table to be linear by stripping the bottom + * 2 or 5 bits off and shifting them up to the top. + */ + +static unsigned long long +compute_ranksize(unsigned int mem_type, unsigned char row_dens) +{ + unsigned long long bsize; + + /* Bottom 2 bits up to the top. */ + bsize = ((row_dens >> 2) | ((row_dens & 3) << 6)); + bsize <<= 24ULL; + debug("DDR: DDR I rank density = 0x%16llx\n", bsize); + + return bsize; +} + +/* + * Convert a two-nibble BCD value into a cycle time. + * While the spec calls for nano-seconds, picos are returned. + * + * This implements the tables for bytes 9, 23 and 25 for both + * DDR I and II. No allowance for distinguishing the invalid + * fields absent for DDR I yet present in DDR II is made. + * (That is, cycle times of .25, .33, .66 and .75 ns are + * allowed for both DDR II and I.) + */ +static unsigned int +convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val) +{ + /* Table look up the lower nibble, allow DDR I & II. */ + unsigned int tenths_ps[16] = { + 0, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 250, /* This and the next 3 entries valid ... */ + 330, /* ... only for tCK calculations. */ + 660, + 750, + 0, /* undefined */ + 0 /* undefined */ + }; + + unsigned int whole_ns = (spd_val & 0xF0) >> 4; + unsigned int tenth_ns = spd_val & 0x0F; + unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns]; + + return ps; +} + +static unsigned int +convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val) +{ + unsigned int tenth_ns = (spd_val & 0xF0) >> 4; + unsigned int hundredth_ns = spd_val & 0x0F; + unsigned int ps = tenth_ns * 100 + hundredth_ns * 10; + + return ps; +} + +static unsigned int byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, /* supposed to be RFC, but not sure what that means */ + 0 /* Undefined */ +}; + +static unsigned int +compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc) +{ + unsigned int trfc_ps; + + trfc_ps = (((trctrfc_ext & 0x1) * 256) + trfc) * 1000 + + byte40_table_ps[(trctrfc_ext >> 1) & 0x7]; + + return trfc_ps; +} + +static unsigned int +compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc) +{ + unsigned int trc_ps; + + trc_ps = trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7]; + + return trc_ps; +} + +/* + * tCKmax from DDR I SPD Byte 43 + * + * Bits 7:2 == whole ns + * Bits 1:0 == quarter ns + * 00 == 0.00 ns + * 01 == 0.25 ns + * 10 == 0.50 ns + * 11 == 0.75 ns + * + * Returns picoseconds. + */ +static unsigned int +compute_tckmax_from_spd_ps(unsigned int byte43) +{ + return (byte43 >> 2) * 1000 + (byte43 & 0x3) * 250; +} + +/* + * Determine Refresh Rate. Ignore self refresh bit on DDR I. + * Table from SPD Spec, Byte 12, converted to picoseconds and + * filled in with "default" normal values. + */ +static unsigned int +determine_refresh_rate_ps(const unsigned int spd_refresh) +{ + unsigned int refresh_time_ps[8] = { + 15625000, /* 0 Normal 1.00x */ + 3900000, /* 1 Reduced .25x */ + 7800000, /* 2 Extended .50x */ + 31300000, /* 3 Extended 2.00x */ + 62500000, /* 4 Extended 4.00x */ + 125000000, /* 5 Extended 8.00x */ + 15625000, /* 6 Normal 1.00x filler */ + 15625000, /* 7 Normal 1.00x filler */ + }; + + return refresh_time_ps[spd_refresh & 0x7]; +} + +/* + * The purpose of this function is to compute a suitable + * CAS latency given the DRAM clock period. The SPD only + * defines at most 3 CAS latencies. Typically the slower in + * frequency the DIMM runs at, the shorter its CAS latency can be. + * If the DIMM is operating at a sufficiently low frequency, + * it may be able to run at a CAS latency shorter than the + * shortest SPD-defined CAS latency. + * + * If a CAS latency is not found, 0 is returned. + * + * Do this by finding in the standard speed bin table the longest + * tCKmin that doesn't exceed the value of mclk_ps (tCK). + * + * An assumption made is that the SDRAM device allows the + * CL to be programmed for a value that is lower than those + * advertised by the SPD. This is not always the case, + * as those modes not defined in the SPD are optional. + * + * CAS latency de-rating based upon values JEDEC Standard No. 79-E + * Table 11. + * + * ordinal 2, ddr1_speed_bins[1] contains tCK for CL=2 + */ + /* CL2.0 CL2.5 CL3.0 */ +unsigned short ddr1_speed_bins[] = {0, 7500, 6000, 5000 }; + +unsigned int +compute_derated_DDR1_CAS_latency(unsigned int mclk_ps) +{ + const unsigned int num_speed_bins = ARRAY_SIZE(ddr1_speed_bins); + unsigned int lowest_tCKmin_found = 0; + unsigned int lowest_tCKmin_CL = 0; + unsigned int i; + + debug("mclk_ps = %u\n", mclk_ps); + + for (i = 0; i < num_speed_bins; i++) { + unsigned int x = ddr1_speed_bins[i]; + debug("i=%u, x = %u, lowest_tCKmin_found = %u\n", + i, x, lowest_tCKmin_found); + if (x && lowest_tCKmin_found <= x && x <= mclk_ps) { + lowest_tCKmin_found = x; + lowest_tCKmin_CL = i + 1; + } + } + + debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL); + + return lowest_tCKmin_CL; +} + +/* + * ddr_compute_dimm_parameters for DDR1 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_t structure pointed by pdimm. + * + * FIXME: use #define for the retvals + */ +unsigned int +ddr_compute_dimm_parameters(const ddr1_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + unsigned int retval; + + if (spd->mem_type) { + if (spd->mem_type != SPD_MEMTYPE_DDR) { + printf("DIMM %u: is not a DDR1 SPD.\n", dimm_number); + return 1; + } + } else { + memset(pdimm, 0, sizeof(dimm_params_t)); + return 1; + } + + retval = ddr1_spd_check(spd); + if (retval) { + printf("DIMM %u: failed checksum\n", dimm_number); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = spd->nrows; + pdimm->rank_density = compute_ranksize(spd->mem_type, spd->bank_dens); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = spd->dataw_lsb; + pdimm->primary_sdram_width = spd->primw; + pdimm->ec_sdram_width = spd->ecw; + + /* + * FIXME: Need to determine registered_dimm status. + * 1 == register buffered + * 0 == unbuffered + */ + pdimm->registered_dimm = 0; /* unbuffered */ + + /* SDRAM device parameters */ + pdimm->n_row_addr = spd->nrow_addr; + pdimm->n_col_addr = spd->ncol_addr; + pdimm->n_banks_per_sdram_device = spd->nbanks; + pdimm->edc_config = spd->config; + pdimm->burst_lengths_bitmask = spd->burstl; + pdimm->row_density = spd->bank_dens; + + /* + * Calculate the Maximum Data Rate based on the Minimum Cycle time. + * The SPD clk_cycle field (tCKmin) is measured in tenths of + * nanoseconds and represented as BCD. + */ + pdimm->tckmin_x_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle); + pdimm->tckmin_x_minus_1_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2); + pdimm->tckmin_x_minus_2_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3); + + pdimm->tckmax_ps = compute_tckmax_from_spd_ps(spd->tckmax); + + /* + * Compute CAS latencies defined by SPD + * The SPD caslat_x should have at least 1 and at most 3 bits set. + * + * If cas_lat after masking is 0, the __ilog2 function returns + * 255 into the variable. This behavior is abused once. + */ + pdimm->caslat_x = __ilog2(spd->cas_lat); + pdimm->caslat_x_minus_1 = __ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_x)); + pdimm->caslat_x_minus_2 = __ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_x) + & ~(1 << pdimm->caslat_x_minus_1)); + + /* Compute CAS latencies below that defined by SPD */ + pdimm->caslat_lowest_derated + = compute_derated_DDR1_CAS_latency(get_memory_clk_period_ps()); + + /* Compute timing parameters */ + pdimm->trcd_ps = spd->trcd * 250; + pdimm->trp_ps = spd->trp * 250; + pdimm->tras_ps = spd->tras * 1000; + + pdimm->twr_ps = mclk_to_picos(3); + pdimm->twtr_ps = mclk_to_picos(1); + pdimm->trfc_ps = compute_trfc_ps_from_spd(0, spd->trfc); + + pdimm->trrd_ps = spd->trrd * 250; + pdimm->trc_ps = compute_trc_ps_from_spd(0, spd->trc); + + pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh); + + pdimm->tis_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup); + pdimm->tih_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold); + pdimm->tds_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup); + pdimm->tdh_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold); + + pdimm->trtp_ps = mclk_to_picos(2); /* By the book. */ + pdimm->tdqsq_max_ps = spd->tdqsq * 10; + pdimm->tqhs_ps = spd->tqhs * 10; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/ddr2_dimm_params.c b/qemu/roms/u-boot/drivers/ddr/fsl/ddr2_dimm_params.c new file mode 100644 index 000000000..d865df78a --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/ddr2_dimm_params.c @@ -0,0 +1,342 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include +#include + +#include +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Study these table from Byte 31 of JEDEC SPD Spec. + * + * DDR I DDR II + * Bit Size Size + * --- ----- ------ + * 7 high 512MB 512MB + * 6 256MB 256MB + * 5 128MB 128MB + * 4 64MB 16GB + * 3 32MB 8GB + * 2 16MB 4GB + * 1 2GB 2GB + * 0 low 1GB 1GB + * + * Reorder Table to be linear by stripping the bottom + * 2 or 5 bits off and shifting them up to the top. + * + */ +static unsigned long long +compute_ranksize(unsigned int mem_type, unsigned char row_dens) +{ + unsigned long long bsize; + + /* Bottom 5 bits up to the top. */ + bsize = ((row_dens >> 5) | ((row_dens & 31) << 3)); + bsize <<= 27ULL; + debug("DDR: DDR II rank density = 0x%16llx\n", bsize); + + return bsize; +} + +/* + * Convert a two-nibble BCD value into a cycle time. + * While the spec calls for nano-seconds, picos are returned. + * + * This implements the tables for bytes 9, 23 and 25 for both + * DDR I and II. No allowance for distinguishing the invalid + * fields absent for DDR I yet present in DDR II is made. + * (That is, cycle times of .25, .33, .66 and .75 ns are + * allowed for both DDR II and I.) + */ +static unsigned int +convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val) +{ + /* Table look up the lower nibble, allow DDR I & II. */ + unsigned int tenths_ps[16] = { + 0, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 250, /* This and the next 3 entries valid ... */ + 330, /* ... only for tCK calculations. */ + 660, + 750, + 0, /* undefined */ + 0 /* undefined */ + }; + + unsigned int whole_ns = (spd_val & 0xF0) >> 4; + unsigned int tenth_ns = spd_val & 0x0F; + unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns]; + + return ps; +} + +static unsigned int +convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val) +{ + unsigned int tenth_ns = (spd_val & 0xF0) >> 4; + unsigned int hundredth_ns = spd_val & 0x0F; + unsigned int ps = tenth_ns * 100 + hundredth_ns * 10; + + return ps; +} + +static unsigned int byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, /* supposed to be RFC, but not sure what that means */ + 0 /* Undefined */ +}; + +static unsigned int +compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc) +{ + unsigned int trfc_ps; + + trfc_ps = (((trctrfc_ext & 0x1) * 256) + trfc) * 1000 + + byte40_table_ps[(trctrfc_ext >> 1) & 0x7]; + + return trfc_ps; +} + +static unsigned int +compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc) +{ + unsigned int trc_ps; + + trc_ps = trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7]; + + return trc_ps; +} + +/* + * Determine Refresh Rate. Ignore self refresh bit on DDR I. + * Table from SPD Spec, Byte 12, converted to picoseconds and + * filled in with "default" normal values. + */ +static unsigned int +determine_refresh_rate_ps(const unsigned int spd_refresh) +{ + unsigned int refresh_time_ps[8] = { + 15625000, /* 0 Normal 1.00x */ + 3900000, /* 1 Reduced .25x */ + 7800000, /* 2 Extended .50x */ + 31300000, /* 3 Extended 2.00x */ + 62500000, /* 4 Extended 4.00x */ + 125000000, /* 5 Extended 8.00x */ + 15625000, /* 6 Normal 1.00x filler */ + 15625000, /* 7 Normal 1.00x filler */ + }; + + return refresh_time_ps[spd_refresh & 0x7]; +} + +/* + * The purpose of this function is to compute a suitable + * CAS latency given the DRAM clock period. The SPD only + * defines at most 3 CAS latencies. Typically the slower in + * frequency the DIMM runs at, the shorter its CAS latency can. + * be. If the DIMM is operating at a sufficiently low frequency, + * it may be able to run at a CAS latency shorter than the + * shortest SPD-defined CAS latency. + * + * If a CAS latency is not found, 0 is returned. + * + * Do this by finding in the standard speed bin table the longest + * tCKmin that doesn't exceed the value of mclk_ps (tCK). + * + * An assumption made is that the SDRAM device allows the + * CL to be programmed for a value that is lower than those + * advertised by the SPD. This is not always the case, + * as those modes not defined in the SPD are optional. + * + * CAS latency de-rating based upon values JEDEC Standard No. 79-2C + * Table 40, "DDR2 SDRAM stanadard speed bins and tCK, tRCD, tRP, tRAS, + * and tRC for corresponding bin" + * + * ordinal 2, ddr2_speed_bins[1] contains tCK for CL=3 + * Not certain if any good value exists for CL=2 + */ + /* CL2 CL3 CL4 CL5 CL6 CL7*/ +unsigned short ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500, 1875 }; + +unsigned int +compute_derated_DDR2_CAS_latency(unsigned int mclk_ps) +{ + const unsigned int num_speed_bins = ARRAY_SIZE(ddr2_speed_bins); + unsigned int lowest_tCKmin_found = 0; + unsigned int lowest_tCKmin_CL = 0; + unsigned int i; + + debug("mclk_ps = %u\n", mclk_ps); + + for (i = 0; i < num_speed_bins; i++) { + unsigned int x = ddr2_speed_bins[i]; + debug("i=%u, x = %u, lowest_tCKmin_found = %u\n", + i, x, lowest_tCKmin_found); + if (x && x <= mclk_ps && x >= lowest_tCKmin_found ) { + lowest_tCKmin_found = x; + lowest_tCKmin_CL = i + 2; + } + } + + debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL); + + return lowest_tCKmin_CL; +} + +/* + * ddr_compute_dimm_parameters for DDR2 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_t structure pointed by pdimm. + * + * FIXME: use #define for the retvals + */ +unsigned int +ddr_compute_dimm_parameters(const ddr2_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + unsigned int retval; + + if (spd->mem_type) { + if (spd->mem_type != SPD_MEMTYPE_DDR2) { + printf("DIMM %u: is not a DDR2 SPD.\n", dimm_number); + return 1; + } + } else { + memset(pdimm, 0, sizeof(dimm_params_t)); + return 1; + } + + retval = ddr2_spd_check(spd); + if (retval) { + printf("DIMM %u: failed checksum\n", dimm_number); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = (spd->mod_ranks & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd->mem_type, spd->rank_dens); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = spd->dataw; + pdimm->primary_sdram_width = spd->primw; + pdimm->ec_sdram_width = spd->ecw; + + /* These are all the types defined by the JEDEC DDR2 SPD 1.3 spec */ + switch (spd->dimm_type) { + case DDR2_SPD_DIMMTYPE_RDIMM: + case DDR2_SPD_DIMMTYPE_72B_SO_RDIMM: + case DDR2_SPD_DIMMTYPE_MINI_RDIMM: + /* Registered/buffered DIMMs */ + pdimm->registered_dimm = 1; + break; + + case DDR2_SPD_DIMMTYPE_UDIMM: + case DDR2_SPD_DIMMTYPE_SO_DIMM: + case DDR2_SPD_DIMMTYPE_MICRO_DIMM: + case DDR2_SPD_DIMMTYPE_MINI_UDIMM: + /* Unbuffered DIMMs */ + pdimm->registered_dimm = 0; + break; + + case DDR2_SPD_DIMMTYPE_72B_SO_CDIMM: + default: + printf("unknown dimm_type 0x%02X\n", spd->dimm_type); + return 1; + } + + /* SDRAM device parameters */ + pdimm->n_row_addr = spd->nrow_addr; + pdimm->n_col_addr = spd->ncol_addr; + pdimm->n_banks_per_sdram_device = spd->nbanks; + pdimm->edc_config = spd->config; + pdimm->burst_lengths_bitmask = spd->burstl; + pdimm->row_density = spd->rank_dens; + + /* + * Calculate the Maximum Data Rate based on the Minimum Cycle time. + * The SPD clk_cycle field (tCKmin) is measured in tenths of + * nanoseconds and represented as BCD. + */ + pdimm->tckmin_x_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle); + pdimm->tckmin_x_minus_1_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2); + pdimm->tckmin_x_minus_2_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3); + + pdimm->tckmax_ps = convert_bcd_tenths_to_cycle_time_ps(spd->tckmax); + + /* + * Compute CAS latencies defined by SPD + * The SPD caslat_x should have at least 1 and at most 3 bits set. + * + * If cas_lat after masking is 0, the __ilog2 function returns + * 255 into the variable. This behavior is abused once. + */ + pdimm->caslat_x = __ilog2(spd->cas_lat); + pdimm->caslat_x_minus_1 = __ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_x)); + pdimm->caslat_x_minus_2 = __ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_x) + & ~(1 << pdimm->caslat_x_minus_1)); + + /* Compute CAS latencies below that defined by SPD */ + pdimm->caslat_lowest_derated + = compute_derated_DDR2_CAS_latency(get_memory_clk_period_ps()); + + /* Compute timing parameters */ + pdimm->trcd_ps = spd->trcd * 250; + pdimm->trp_ps = spd->trp * 250; + pdimm->tras_ps = spd->tras * 1000; + + pdimm->twr_ps = spd->twr * 250; + pdimm->twtr_ps = spd->twtr * 250; + pdimm->trfc_ps = compute_trfc_ps_from_spd(spd->trctrfc_ext, spd->trfc); + + pdimm->trrd_ps = spd->trrd * 250; + pdimm->trc_ps = compute_trc_ps_from_spd(spd->trctrfc_ext, spd->trc); + + pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh); + + pdimm->tis_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup); + pdimm->tih_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold); + pdimm->tds_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup); + pdimm->tdh_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold); + + pdimm->trtp_ps = spd->trtp * 250; + pdimm->tdqsq_max_ps = spd->tdqsq * 10; + pdimm->tqhs_ps = spd->tqhs * 10; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/ddr3_dimm_params.c b/qemu/roms/u-boot/drivers/ddr/fsl/ddr3_dimm_params.c new file mode 100644 index 000000000..a4b8c101f --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/ddr3_dimm_params.c @@ -0,0 +1,341 @@ +/* + * Copyright 2008-2012 Freescale Semiconductor, Inc. + * Dave Liu + * + * calculate the organization and timing parameter + * from ddr3 spd, please refer to the spec + * JEDEC standard No.21-C 4_01_02_11R18.pdf + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include +#include + +#include + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * each rank size = + * sdram capacity(bit) / 8 * primary bus width / sdram width + * + * where: sdram capacity = spd byte4[3:0] + * primary bus width = spd byte8[2:0] + * sdram width = spd byte7[2:0] + * + * SPD byte4 - sdram density and banks + * bit[3:0] size(bit) size(byte) + * 0000 256Mb 32MB + * 0001 512Mb 64MB + * 0010 1Gb 128MB + * 0011 2Gb 256MB + * 0100 4Gb 512MB + * 0101 8Gb 1GB + * 0110 16Gb 2GB + * + * SPD byte8 - module memory bus width + * bit[2:0] primary bus width + * 000 8bits + * 001 16bits + * 010 32bits + * 011 64bits + * + * SPD byte7 - module organiztion + * bit[2:0] sdram device width + * 000 4bits + * 001 8bits + * 010 16bits + * 011 32bits + * + */ +static unsigned long long +compute_ranksize(const ddr3_spd_eeprom_t *spd) +{ + unsigned long long bsize; + + int nbit_sdram_cap_bsize = 0; + int nbit_primary_bus_width = 0; + int nbit_sdram_width = 0; + + if ((spd->density_banks & 0xf) < 7) + nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28; + if ((spd->bus_width & 0x7) < 4) + nbit_primary_bus_width = (spd->bus_width & 0x7) + 3; + if ((spd->organization & 0x7) < 4) + nbit_sdram_width = (spd->organization & 0x7) + 2; + + bsize = 1ULL << (nbit_sdram_cap_bsize - 3 + + nbit_primary_bus_width - nbit_sdram_width); + + debug("DDR: DDR III rank density = 0x%16llx\n", bsize); + + return bsize; +} + +/* + * ddr_compute_dimm_parameters for DDR3 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_t structure pointed by pdimm. + * + */ +unsigned int +ddr_compute_dimm_parameters(const ddr3_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + unsigned int retval; + unsigned int mtb_ps; + int ftb_10th_ps; + int i; + + if (spd->mem_type) { + if (spd->mem_type != SPD_MEMTYPE_DDR3) { + printf("DIMM %u: is not a DDR3 SPD.\n", dimm_number); + return 1; + } + } else { + memset(pdimm, 0, sizeof(dimm_params_t)); + return 1; + } + + retval = ddr3_spd_check(spd); + if (retval) { + printf("DIMM %u: failed checksum\n", dimm_number); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + if ((spd->info_size_crc & 0xF) > 1) + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7)); + if ((spd->bus_width >> 3) & 0x3) + pdimm->ec_sdram_width = 8; + else + pdimm->ec_sdram_width = 0; + pdimm->data_width = pdimm->primary_sdram_width + + pdimm->ec_sdram_width; + pdimm->device_width = 1 << ((spd->organization & 0x7) + 2); + + /* These are the types defined by the JEDEC DDR3 SPD spec */ + pdimm->mirrored_dimm = 0; + pdimm->registered_dimm = 0; + switch (spd->module_type & DDR3_SPD_MODULETYPE_MASK) { + case DDR3_SPD_MODULETYPE_RDIMM: + case DDR3_SPD_MODULETYPE_MINI_RDIMM: + case DDR3_SPD_MODULETYPE_72B_SO_RDIMM: + /* Registered/buffered DIMMs */ + pdimm->registered_dimm = 1; + for (i = 0; i < 16; i += 2) { + u8 rcw = spd->mod_section.registered.rcw[i/2]; + pdimm->rcw[i] = (rcw >> 0) & 0x0F; + pdimm->rcw[i+1] = (rcw >> 4) & 0x0F; + } + break; + + case DDR3_SPD_MODULETYPE_UDIMM: + case DDR3_SPD_MODULETYPE_SO_DIMM: + case DDR3_SPD_MODULETYPE_MICRO_DIMM: + case DDR3_SPD_MODULETYPE_MINI_UDIMM: + case DDR3_SPD_MODULETYPE_MINI_CDIMM: + case DDR3_SPD_MODULETYPE_72B_SO_UDIMM: + case DDR3_SPD_MODULETYPE_72B_SO_CDIMM: + case DDR3_SPD_MODULETYPE_LRDIMM: + case DDR3_SPD_MODULETYPE_16B_SO_DIMM: + case DDR3_SPD_MODULETYPE_32B_SO_DIMM: + /* Unbuffered DIMMs */ + if (spd->mod_section.unbuffered.addr_mapping & 0x1) + pdimm->mirrored_dimm = 1; + break; + + default: + printf("unknown module_type 0x%02X\n", spd->module_type); + return 1; + } + + /* SDRAM device parameters */ + pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12; + pdimm->n_col_addr = (spd->addressing & 0x7) + 9; + pdimm->n_banks_per_sdram_device = 8 << ((spd->density_banks >> 4) & 0x7); + + /* + * The SPD spec has not the ECC bit, + * We consider the DIMM as ECC capability + * when the extension bus exist + */ + if (pdimm->ec_sdram_width) + pdimm->edc_config = 0x02; + else + pdimm->edc_config = 0x00; + + /* + * The SPD spec has not the burst length byte + * but DDR3 spec has nature BL8 and BC4, + * BL8 -bit3, BC4 -bit2 + */ + pdimm->burst_lengths_bitmask = 0x0c; + pdimm->row_density = __ilog2(pdimm->rank_density); + + /* MTB - medium timebase + * The unit in the SPD spec is ns, + * We convert it to ps. + * eg: MTB = 0.125ns (125ps) + */ + mtb_ps = (spd->mtb_dividend * 1000) /spd->mtb_divisor; + pdimm->mtb_ps = mtb_ps; + + /* + * FTB - fine timebase + * use 1/10th of ps as our unit to avoid floating point + * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps + */ + ftb_10th_ps = + ((spd->ftb_div & 0xf0) >> 4) * 10 / (spd->ftb_div & 0x0f); + pdimm->ftb_10th_ps = ftb_10th_ps; + /* + * sdram minimum cycle time + * we assume the MTB is 0.125ns + * eg: + * tck_min=15 MTB (1.875ns) ->DDR3-1066 + * =12 MTB (1.5ns) ->DDR3-1333 + * =10 MTB (1.25ns) ->DDR3-1600 + */ + pdimm->tckmin_x_ps = spd->tck_min * mtb_ps + + (spd->fine_tck_min * ftb_10th_ps) / 10; + + /* + * CAS latency supported + * bit4 - CL4 + * bit5 - CL5 + * bit18 - CL18 + */ + pdimm->caslat_x = ((spd->caslat_msb << 8) | spd->caslat_lsb) << 4; + + /* + * min CAS latency time + * eg: taa_min = + * DDR3-800D 100 MTB (12.5ns) + * DDR3-1066F 105 MTB (13.125ns) + * DDR3-1333H 108 MTB (13.5ns) + * DDR3-1600H 90 MTB (11.25ns) + */ + pdimm->taa_ps = spd->taa_min * mtb_ps + + (spd->fine_taa_min * ftb_10th_ps) / 10; + + /* + * min write recovery time + * eg: + * twr_min = 120 MTB (15ns) -> all speed grades. + */ + pdimm->twr_ps = spd->twr_min * mtb_ps; + + /* + * min RAS to CAS delay time + * eg: trcd_min = + * DDR3-800 100 MTB (12.5ns) + * DDR3-1066F 105 MTB (13.125ns) + * DDR3-1333H 108 MTB (13.5ns) + * DDR3-1600H 90 MTB (11.25) + */ + pdimm->trcd_ps = spd->trcd_min * mtb_ps + + (spd->fine_trcd_min * ftb_10th_ps) / 10; + + /* + * min row active to row active delay time + * eg: trrd_min = + * DDR3-800(1KB page) 80 MTB (10ns) + * DDR3-1333(1KB page) 48 MTB (6ns) + */ + pdimm->trrd_ps = spd->trrd_min * mtb_ps; + + /* + * min row precharge delay time + * eg: trp_min = + * DDR3-800D 100 MTB (12.5ns) + * DDR3-1066F 105 MTB (13.125ns) + * DDR3-1333H 108 MTB (13.5ns) + * DDR3-1600H 90 MTB (11.25ns) + */ + pdimm->trp_ps = spd->trp_min * mtb_ps + + (spd->fine_trp_min * ftb_10th_ps) / 10; + + /* min active to precharge delay time + * eg: tRAS_min = + * DDR3-800D 300 MTB (37.5ns) + * DDR3-1066F 300 MTB (37.5ns) + * DDR3-1333H 288 MTB (36ns) + * DDR3-1600H 280 MTB (35ns) + */ + pdimm->tras_ps = (((spd->tras_trc_ext & 0xf) << 8) | spd->tras_min_lsb) + * mtb_ps; + /* + * min active to actice/refresh delay time + * eg: tRC_min = + * DDR3-800D 400 MTB (50ns) + * DDR3-1066F 405 MTB (50.625ns) + * DDR3-1333H 396 MTB (49.5ns) + * DDR3-1600H 370 MTB (46.25ns) + */ + pdimm->trc_ps = (((spd->tras_trc_ext & 0xf0) << 4) | spd->trc_min_lsb) + * mtb_ps + (spd->fine_trc_min * ftb_10th_ps) / 10; + /* + * min refresh recovery delay time + * eg: tRFC_min = + * 512Mb 720 MTB (90ns) + * 1Gb 880 MTB (110ns) + * 2Gb 1280 MTB (160ns) + */ + pdimm->trfc_ps = ((spd->trfc_min_msb << 8) | spd->trfc_min_lsb) + * mtb_ps; + /* + * min internal write to read command delay time + * eg: twtr_min = 40 MTB (7.5ns) - all speed bins. + * tWRT is at least 4 mclk independent of operating freq. + */ + pdimm->twtr_ps = spd->twtr_min * mtb_ps; + + /* + * min internal read to precharge command delay time + * eg: trtp_min = 40 MTB (7.5ns) - all speed bins. + * tRTP is at least 4 mclk independent of operating freq. + */ + pdimm->trtp_ps = spd->trtp_min * mtb_ps; + + /* + * Average periodic refresh interval + * tREFI = 7.8 us at normal temperature range + * = 3.9 us at ext temperature range + */ + pdimm->refresh_rate_ps = 7800000; + if ((spd->therm_ref_opt & 0x1) && !(spd->therm_ref_opt & 0x2)) { + pdimm->refresh_rate_ps = 3900000; + pdimm->extended_op_srt = 1; + } + + /* + * min four active window delay time + * eg: tfaw_min = + * DDR3-800(1KB page) 320 MTB (40ns) + * DDR3-1066(1KB page) 300 MTB (37.5ns) + * DDR3-1333(1KB page) 240 MTB (30ns) + * DDR3-1600(1KB page) 240 MTB (30ns) + */ + pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min) + * mtb_ps; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/ddr4_dimm_params.c b/qemu/roms/u-boot/drivers/ddr/fsl/ddr4_dimm_params.c new file mode 100644 index 000000000..4745b7fb1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/ddr4_dimm_params.c @@ -0,0 +1,300 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * calculate the organization and timing parameter + * from ddr3 spd, please refer to the spec + * JEDEC standard No.21-C 4_01_02_12R23A.pdf + * + * + */ + +#include +#include + +#include + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Total DIMM size = + * sdram capacity(bit) / 8 * primary bus width / sdram width + * * Logical Ranks per DIMM + * + * where: sdram capacity = spd byte4[3:0] + * primary bus width = spd byte13[2:0] + * sdram width = spd byte12[2:0] + * Logical Ranks per DIMM = spd byte12[5:3] for SDP, DDP, QDP + * spd byte12{5:3] * spd byte6[6:4] for 3DS + * + * To simplify each rank size = total DIMM size / Number of Package Ranks + * where Number of Package Ranks = spd byte12[5:3] + * + * SPD byte4 - sdram density and banks + * bit[3:0] size(bit) size(byte) + * 0000 256Mb 32MB + * 0001 512Mb 64MB + * 0010 1Gb 128MB + * 0011 2Gb 256MB + * 0100 4Gb 512MB + * 0101 8Gb 1GB + * 0110 16Gb 2GB + * 0111 32Gb 4GB + * + * SPD byte13 - module memory bus width + * bit[2:0] primary bus width + * 000 8bits + * 001 16bits + * 010 32bits + * 011 64bits + * + * SPD byte12 - module organization + * bit[2:0] sdram device width + * 000 4bits + * 001 8bits + * 010 16bits + * 011 32bits + * + * SPD byte12 - module organization + * bit[5:3] number of package ranks per DIMM + * 000 1 + * 001 2 + * 010 3 + * 011 4 + * + * SPD byte6 - SDRAM package type + * bit[6:4] Die count + * 000 1 + * 001 2 + * 010 3 + * 011 4 + * 100 5 + * 101 6 + * 110 7 + * 111 8 + * + * SPD byte6 - SRAM package type + * bit[1:0] Signal loading + * 00 Not specified + * 01 Multi load stack + * 10 Sigle load stack (3DS) + * 11 Reserved + */ +static unsigned long long +compute_ranksize(const struct ddr4_spd_eeprom_s *spd) +{ + unsigned long long bsize; + + int nbit_sdram_cap_bsize = 0; + int nbit_primary_bus_width = 0; + int nbit_sdram_width = 0; + int die_count = 0; + bool package_3ds; + + if ((spd->density_banks & 0xf) <= 7) + nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28; + if ((spd->bus_width & 0x7) < 4) + nbit_primary_bus_width = (spd->bus_width & 0x7) + 3; + if ((spd->organization & 0x7) < 4) + nbit_sdram_width = (spd->organization & 0x7) + 2; + package_3ds = (spd->package_type & 0x3) == 0x2; + if (package_3ds) + die_count = (spd->package_type >> 4) & 0x7; + + bsize = 1ULL << (nbit_sdram_cap_bsize - 3 + + nbit_primary_bus_width - nbit_sdram_width + + die_count); + + debug("DDR: DDR III rank density = 0x%16llx\n", bsize); + + return bsize; +} + +#define spd_to_ps(mtb, ftb) \ + (mtb * pdimm->mtb_ps + (ftb * pdimm->ftb_10th_ps) / 10) +/* + * ddr_compute_dimm_parameters for DDR3 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_t structure pointed by pdimm. + * + */ +unsigned int +ddr_compute_dimm_parameters(const generic_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + unsigned int retval; + int i; + + if (spd->mem_type) { + if (spd->mem_type != SPD_MEMTYPE_DDR4) { + printf("DIMM %u: is not a DDR4 SPD.\n", dimm_number); + return 1; + } + } else { + memset(pdimm, 0, sizeof(dimm_params_t)); + return 1; + } + + retval = ddr4_spd_check(spd); + if (retval) { + printf("DIMM %u: failed checksum\n", dimm_number); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + if ((spd->info_size_crc & 0xF) > 2) + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7)); + if ((spd->bus_width >> 3) & 0x3) + pdimm->ec_sdram_width = 8; + else + pdimm->ec_sdram_width = 0; + pdimm->data_width = pdimm->primary_sdram_width + + pdimm->ec_sdram_width; + pdimm->device_width = 1 << ((spd->organization & 0x7) + 2); + + /* These are the types defined by the JEDEC DDR3 SPD spec */ + pdimm->mirrored_dimm = 0; + pdimm->registered_dimm = 0; + switch (spd->module_type & DDR3_SPD_MODULETYPE_MASK) { + case DDR3_SPD_MODULETYPE_RDIMM: + /* Registered/buffered DIMMs */ + pdimm->registered_dimm = 1; + break; + + case DDR3_SPD_MODULETYPE_UDIMM: + case DDR3_SPD_MODULETYPE_SO_DIMM: + /* Unbuffered DIMMs */ + if (spd->mod_section.unbuffered.addr_mapping & 0x1) + pdimm->mirrored_dimm = 1; + break; + + default: + printf("unknown module_type 0x%02X\n", spd->module_type); + return 1; + } + + /* SDRAM device parameters */ + pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12; + pdimm->n_col_addr = (spd->addressing & 0x7) + 9; + pdimm->bank_addr_bits = (spd->density_banks >> 4) & 0x3; + pdimm->bank_group_bits = (spd->density_banks >> 6) & 0x3; + + /* + * The SPD spec has not the ECC bit, + * We consider the DIMM as ECC capability + * when the extension bus exist + */ + if (pdimm->ec_sdram_width) + pdimm->edc_config = 0x02; + else + pdimm->edc_config = 0x00; + + /* + * The SPD spec has not the burst length byte + * but DDR4 spec has nature BL8 and BC4, + * BL8 -bit3, BC4 -bit2 + */ + pdimm->burst_lengths_bitmask = 0x0c; + pdimm->row_density = __ilog2(pdimm->rank_density); + + /* MTB - medium timebase + * The MTB in the SPD spec is 125ps, + * + * FTB - fine timebase + * use 1/10th of ps as our unit to avoid floating point + * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps + */ + if ((spd->timebases & 0xf) == 0x0) { + pdimm->mtb_ps = 125; + pdimm->ftb_10th_ps = 10; + + } else { + printf("Unknown Timebases\n"); + } + + /* sdram minimum cycle time */ + pdimm->tckmin_x_ps = spd_to_ps(spd->tck_min, spd->fine_tck_min); + + /* sdram max cycle time */ + pdimm->tckmax_ps = spd_to_ps(spd->tck_max, spd->fine_tck_max); + + /* + * CAS latency supported + * bit0 - CL7 + * bit4 - CL11 + * bit8 - CL15 + * bit12- CL19 + * bit16- CL23 + */ + pdimm->caslat_x = (spd->caslat_b1 << 7) | + (spd->caslat_b2 << 15) | + (spd->caslat_b3 << 23); + + BUG_ON(spd->caslat_b4 != 0); + + /* + * min CAS latency time + */ + pdimm->taa_ps = spd_to_ps(spd->taa_min, spd->fine_taa_min); + + /* + * min RAS to CAS delay time + */ + pdimm->trcd_ps = spd_to_ps(spd->trcd_min, spd->fine_trcd_min); + + /* + * Min Row Precharge Delay Time + */ + pdimm->trp_ps = spd_to_ps(spd->trp_min, spd->fine_trp_min); + + /* min active to precharge delay time */ + pdimm->tras_ps = (((spd->tras_trc_ext & 0xf) << 8) + + spd->tras_min_lsb) * pdimm->mtb_ps; + + /* min active to actice/refresh delay time */ + pdimm->trc_ps = spd_to_ps((((spd->tras_trc_ext & 0xf0) << 4) + + spd->trc_min_lsb), spd->fine_trc_min); + /* Min Refresh Recovery Delay Time */ + pdimm->trfc1_ps = ((spd->trfc1_min_msb << 8) | (spd->trfc1_min_lsb)) * + pdimm->mtb_ps; + pdimm->trfc2_ps = ((spd->trfc2_min_msb << 8) | (spd->trfc2_min_lsb)) * + pdimm->mtb_ps; + pdimm->trfc4_ps = ((spd->trfc4_min_msb << 8) | (spd->trfc4_min_lsb)) * + pdimm->mtb_ps; + /* min four active window delay time */ + pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min) * + pdimm->mtb_ps; + + /* min row active to row active delay time, different bank group */ + pdimm->trrds_ps = spd_to_ps(spd->trrds_min, spd->fine_trrds_min); + /* min row active to row active delay time, same bank group */ + pdimm->trrdl_ps = spd_to_ps(spd->trrdl_min, spd->fine_trrdl_min); + /* min CAS to CAS Delay Time (tCCD_Lmin), same bank group */ + pdimm->tccdl_ps = spd_to_ps(spd->tccdl_min, spd->fine_tccdl_min); + + /* + * Average periodic refresh interval + * tREFI = 7.8 us at normal temperature range + */ + pdimm->refresh_rate_ps = 7800000; + + for (i = 0; i < 18; i++) + pdimm->dq_mapping[i] = spd->mapping[i]; + + pdimm->dq_mapping_ors = ((spd->mapping[0] >> 6) & 0x3) == 0 ? 1 : 0; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/fsl_ddr_gen4.c b/qemu/roms/u-boot/drivers/ddr/fsl/fsl_ddr_gen4.c new file mode 100644 index 000000000..7cd878aee --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/fsl_ddr_gen4.c @@ -0,0 +1,234 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + +/* + * regs has the to-be-set values for DDR controller registers + * ctrl_num is the DDR controller number + * step: 0 goes through the initialization in one pass + * 1 sets registers and returns before enabling controller + * 2 resumes from step 1 and continues to initialize + * Dividing the initialization to two steps to deassert DDR reset signal + * to comply with JEDEC specs for RDIMMs. + */ +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num, int step) +{ + unsigned int i, bus_width; + struct ccsr_ddr __iomem *ddr; + u32 temp_sdram_cfg; + u32 total_gb_size_per_controller; + int timeout; + + switch (ctrl_num) { + case 0: + ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR; + break; +#if defined(CONFIG_SYS_FSL_DDR2_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 1) + case 1: + ddr = (void *)CONFIG_SYS_FSL_DDR2_ADDR; + break; +#endif +#if defined(CONFIG_SYS_FSL_DDR3_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 2) + case 2: + ddr = (void *)CONFIG_SYS_FSL_DDR3_ADDR; + break; +#endif +#if defined(CONFIG_SYS_FSL_DDR4_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 3) + case 3: + ddr = (void *)CONFIG_SYS_FSL_DDR4_ADDR; + break; +#endif + default: + printf("%s unexpected ctrl_num = %u\n", __func__, ctrl_num); + return; + } + + if (step == 2) + goto step2; + + if (regs->ddr_eor) + ddr_out32(&ddr->eor, regs->ddr_eor); + + ddr_out32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); + + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + ddr_out32(&ddr->cs0_bnds, regs->cs[i].bnds); + ddr_out32(&ddr->cs0_config, regs->cs[i].config); + ddr_out32(&ddr->cs0_config_2, regs->cs[i].config_2); + + } else if (i == 1) { + ddr_out32(&ddr->cs1_bnds, regs->cs[i].bnds); + ddr_out32(&ddr->cs1_config, regs->cs[i].config); + ddr_out32(&ddr->cs1_config_2, regs->cs[i].config_2); + + } else if (i == 2) { + ddr_out32(&ddr->cs2_bnds, regs->cs[i].bnds); + ddr_out32(&ddr->cs2_config, regs->cs[i].config); + ddr_out32(&ddr->cs2_config_2, regs->cs[i].config_2); + + } else if (i == 3) { + ddr_out32(&ddr->cs3_bnds, regs->cs[i].bnds); + ddr_out32(&ddr->cs3_config, regs->cs[i].config); + ddr_out32(&ddr->cs3_config_2, regs->cs[i].config_2); + } + } + + ddr_out32(&ddr->timing_cfg_3, regs->timing_cfg_3); + ddr_out32(&ddr->timing_cfg_0, regs->timing_cfg_0); + ddr_out32(&ddr->timing_cfg_1, regs->timing_cfg_1); + ddr_out32(&ddr->timing_cfg_2, regs->timing_cfg_2); + ddr_out32(&ddr->timing_cfg_4, regs->timing_cfg_4); + ddr_out32(&ddr->timing_cfg_5, regs->timing_cfg_5); + ddr_out32(&ddr->timing_cfg_6, regs->timing_cfg_6); + ddr_out32(&ddr->timing_cfg_7, regs->timing_cfg_7); + ddr_out32(&ddr->timing_cfg_8, regs->timing_cfg_8); + ddr_out32(&ddr->timing_cfg_9, regs->timing_cfg_9); + ddr_out32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl); + ddr_out32(&ddr->dq_map_0, regs->dq_map_0); + ddr_out32(&ddr->dq_map_1, regs->dq_map_1); + ddr_out32(&ddr->dq_map_2, regs->dq_map_2); + ddr_out32(&ddr->dq_map_3, regs->dq_map_3); + ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + ddr_out32(&ddr->sdram_cfg_3, regs->ddr_sdram_cfg_3); + ddr_out32(&ddr->sdram_mode, regs->ddr_sdram_mode); + ddr_out32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); + ddr_out32(&ddr->sdram_mode_3, regs->ddr_sdram_mode_3); + ddr_out32(&ddr->sdram_mode_4, regs->ddr_sdram_mode_4); + ddr_out32(&ddr->sdram_mode_5, regs->ddr_sdram_mode_5); + ddr_out32(&ddr->sdram_mode_6, regs->ddr_sdram_mode_6); + ddr_out32(&ddr->sdram_mode_7, regs->ddr_sdram_mode_7); + ddr_out32(&ddr->sdram_mode_8, regs->ddr_sdram_mode_8); + ddr_out32(&ddr->sdram_mode_9, regs->ddr_sdram_mode_9); + ddr_out32(&ddr->sdram_mode_10, regs->ddr_sdram_mode_10); + ddr_out32(&ddr->sdram_mode_11, regs->ddr_sdram_mode_11); + ddr_out32(&ddr->sdram_mode_12, regs->ddr_sdram_mode_12); + ddr_out32(&ddr->sdram_mode_13, regs->ddr_sdram_mode_13); + ddr_out32(&ddr->sdram_mode_14, regs->ddr_sdram_mode_14); + ddr_out32(&ddr->sdram_mode_15, regs->ddr_sdram_mode_15); + ddr_out32(&ddr->sdram_mode_16, regs->ddr_sdram_mode_16); + ddr_out32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl); + ddr_out32(&ddr->sdram_interval, regs->ddr_sdram_interval); + ddr_out32(&ddr->sdram_data_init, regs->ddr_data_init); + ddr_out32(&ddr->init_addr, regs->ddr_init_addr); + ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + ddr_out32(&ddr->ddr_wrlvl_cntl, regs->ddr_wrlvl_cntl); +#ifndef CONFIG_SYS_FSL_DDR_EMU + /* + * Skip these two registers if running on emulator + * because emulator doesn't have skew between bytes. + */ + + if (regs->ddr_wrlvl_cntl_2) + ddr_out32(&ddr->ddr_wrlvl_cntl_2, regs->ddr_wrlvl_cntl_2); + if (regs->ddr_wrlvl_cntl_3) + ddr_out32(&ddr->ddr_wrlvl_cntl_3, regs->ddr_wrlvl_cntl_3); +#endif + + ddr_out32(&ddr->ddr_sr_cntr, regs->ddr_sr_cntr); + ddr_out32(&ddr->ddr_sdram_rcw_1, regs->ddr_sdram_rcw_1); + ddr_out32(&ddr->ddr_sdram_rcw_2, regs->ddr_sdram_rcw_2); + ddr_out32(&ddr->ddr_sdram_rcw_3, regs->ddr_sdram_rcw_3); + ddr_out32(&ddr->ddr_sdram_rcw_4, regs->ddr_sdram_rcw_4); + ddr_out32(&ddr->ddr_sdram_rcw_5, regs->ddr_sdram_rcw_5); + ddr_out32(&ddr->ddr_sdram_rcw_6, regs->ddr_sdram_rcw_6); + ddr_out32(&ddr->ddr_cdr1, regs->ddr_cdr1); + ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2); + ddr_out32(&ddr->err_disable, regs->err_disable); + ddr_out32(&ddr->err_int_en, regs->err_int_en); + for (i = 0; i < 32; i++) { + if (regs->debug[i]) { + debug("Write to debug_%d as %08x\n", + i+1, regs->debug[i]); + ddr_out32(&ddr->debug[i], regs->debug[i]); + } + } + + /* + * For RDIMMs, JEDEC spec requires clocks to be stable before reset is + * deasserted. Clocks start when any chip select is enabled and clock + * control register is set. Because all DDR components are connected to + * one reset signal, this needs to be done in two steps. Step 1 is to + * get the clocks started. Step 2 resumes after reset signal is + * deasserted. + */ + if (step == 1) { + udelay(200); + return; + } + +step2: + /* Set, but do not enable the memory */ + temp_sdram_cfg = regs->ddr_sdram_cfg; + temp_sdram_cfg &= ~(SDRAM_CFG_MEM_EN); + ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg); + + /* + * 500 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + * DDR2 need 200 us, and DDR3 need 500 us from spec, + * we choose the max, that is 500 us for all of case. + */ + udelay(500); + asm volatile("sync;isync"); + + /* Let the controller go */ + temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI; + ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN); + asm volatile("sync;isync"); + + total_gb_size_per_controller = 0; + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (!(regs->cs[i].config & 0x80000000)) + continue; + total_gb_size_per_controller += 1 << ( + ((regs->cs[i].config >> 14) & 0x3) + 2 + + ((regs->cs[i].config >> 8) & 0x7) + 12 + + ((regs->cs[i].config >> 4) & 0x3) + 0 + + ((regs->cs[i].config >> 0) & 0x7) + 8 + + 3 - ((regs->ddr_sdram_cfg >> 19) & 0x3) - + 26); /* minus 26 (count of 64M) */ + } + if (fsl_ddr_get_intl3r() & 0x80000000) /* 3-way interleaving */ + total_gb_size_per_controller *= 3; + else if (regs->cs[0].config & 0x20000000) /* 2-way interleaving */ + total_gb_size_per_controller <<= 1; + /* + * total memory / bus width = transactions needed + * transactions needed / data rate = seconds + * to add plenty of buffer, double the time + * For example, 2GB on 666MT/s 64-bit bus takes about 402ms + * Let's wait for 800ms + */ + bus_width = 3 - ((ddr->sdram_cfg & SDRAM_CFG_DBW_MASK) + >> SDRAM_CFG_DBW_SHIFT); + timeout = ((total_gb_size_per_controller << (6 - bus_width)) * 100 / + (get_ddr_freq(0) >> 20)) << 2; + total_gb_size_per_controller >>= 4; /* shift down to gb size */ + debug("total %d GB\n", total_gb_size_per_controller); + debug("Need to wait up to %d * 10ms\n", timeout); + + /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */ + while ((ddr_in32(&ddr->sdram_cfg_2) & SDRAM_CFG2_D_INIT) && + (timeout >= 0)) { + udelay(10000); /* throttle polling rate */ + timeout--; + } + + if (timeout <= 0) + printf("Waiting for D_INIT timeout. Memory may not work.\n"); + +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/interactive.c b/qemu/roms/u-boot/drivers/ddr/fsl/interactive.c new file mode 100644 index 000000000..cfe1e1f55 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/interactive.c @@ -0,0 +1,2240 @@ +/* + * Copyright 2010-2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Generic driver for Freescale DDR/DDR2/DDR3 memory controller. + * Based on code from spd_sdram.c + * Author: James Yang [at freescale.com] + * York Sun [at freescale.com] + */ + +#include +#include +#include +#include + +#include +#include + +/* Option parameter Structures */ +struct options_string { + const char *option_name; + size_t offset; + unsigned int size; + const char printhex; +}; + +static unsigned int picos_to_mhz(unsigned int picos) +{ + return 1000000 / picos; +} + +static void print_option_table(const struct options_string *table, + int table_size, + const void *base) +{ + unsigned int i; + unsigned int *ptr; + unsigned long long *ptr_l; + + for (i = 0; i < table_size; i++) { + switch (table[i].size) { + case 4: + ptr = (unsigned int *) (base + table[i].offset); + if (table[i].printhex) { + printf("%s = 0x%08X\n", + table[i].option_name, *ptr); + } else { + printf("%s = %u\n", + table[i].option_name, *ptr); + } + break; + case 8: + ptr_l = (unsigned long long *) (base + table[i].offset); + printf("%s = %llu\n", + table[i].option_name, *ptr_l); + break; + default: + printf("Unrecognized size!\n"); + break; + } + } +} + +static int handle_option_table(const struct options_string *table, + int table_size, + void *base, + const char *opt, + const char *val) +{ + unsigned int i; + unsigned int value, *ptr; + unsigned long long value_l, *ptr_l; + + for (i = 0; i < table_size; i++) { + if (strcmp(table[i].option_name, opt) != 0) + continue; + switch (table[i].size) { + case 4: + value = simple_strtoul(val, NULL, 0); + ptr = base + table[i].offset; + *ptr = value; + break; + case 8: + value_l = simple_strtoull(val, NULL, 0); + ptr_l = base + table[i].offset; + *ptr_l = value_l; + break; + default: + printf("Unrecognized size!\n"); + break; + } + return 1; + } + + return 0; +} + +static void fsl_ddr_generic_edit(void *pdata, + void *pend, + unsigned int element_size, + unsigned int element_num, + unsigned int value) +{ + char *pcdata = (char *)pdata; /* BIG ENDIAN ONLY */ + + pcdata += element_num * element_size; + if ((pcdata + element_size) > (char *) pend) { + printf("trying to write past end of data\n"); + return; + } + + switch (element_size) { + case 1: + __raw_writeb(value, pcdata); + break; + case 2: + __raw_writew(value, pcdata); + break; + case 4: + __raw_writel(value, pcdata); + break; + default: + printf("unexpected element size %u\n", element_size); + break; + } +} + +static void fsl_ddr_spd_edit(fsl_ddr_info_t *pinfo, + unsigned int ctrl_num, + unsigned int dimm_num, + unsigned int element_num, + unsigned int value) +{ + generic_spd_eeprom_t *pspd; + + pspd = &(pinfo->spd_installed_dimms[ctrl_num][dimm_num]); + fsl_ddr_generic_edit(pspd, pspd + 1, 1, element_num, value); +} + +#define COMMON_TIMING(x) {#x, offsetof(common_timing_params_t, x), \ + sizeof((common_timing_params_t *)0)->x, 0} + +static void lowest_common_dimm_parameters_edit(fsl_ddr_info_t *pinfo, + unsigned int ctrl_num, + const char *optname_str, + const char *value_str) +{ + common_timing_params_t *p = &pinfo->common_timing_params[ctrl_num]; + + static const struct options_string options[] = { + COMMON_TIMING(tckmin_x_ps), + COMMON_TIMING(tckmax_ps), + COMMON_TIMING(taamin_ps), + COMMON_TIMING(trcd_ps), + COMMON_TIMING(trp_ps), + COMMON_TIMING(tras_ps), + +#ifdef CONFIG_SYS_FSL_DDR4 + COMMON_TIMING(trfc1_ps), + COMMON_TIMING(trfc2_ps), + COMMON_TIMING(trfc4_ps), + COMMON_TIMING(trrds_ps), + COMMON_TIMING(trrdl_ps), + COMMON_TIMING(tccdl_ps), +#else + COMMON_TIMING(twtr_ps), + COMMON_TIMING(trfc_ps), + COMMON_TIMING(trrd_ps), + COMMON_TIMING(trtp_ps), +#endif + COMMON_TIMING(twr_ps), + COMMON_TIMING(trc_ps), + COMMON_TIMING(refresh_rate_ps), + COMMON_TIMING(extended_op_srt), +#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2) + COMMON_TIMING(tis_ps), + COMMON_TIMING(tih_ps), + COMMON_TIMING(tds_ps), + COMMON_TIMING(tdh_ps), + COMMON_TIMING(tdqsq_max_ps), + COMMON_TIMING(tqhs_ps), +#endif + COMMON_TIMING(ndimms_present), + COMMON_TIMING(lowest_common_spd_caslat), + COMMON_TIMING(highest_common_derated_caslat), + COMMON_TIMING(additive_latency), + COMMON_TIMING(all_dimms_burst_lengths_bitmask), + COMMON_TIMING(all_dimms_registered), + COMMON_TIMING(all_dimms_unbuffered), + COMMON_TIMING(all_dimms_ecc_capable), + COMMON_TIMING(total_mem), + COMMON_TIMING(base_address), + }; + static const unsigned int n_opts = ARRAY_SIZE(options); + + if (handle_option_table(options, n_opts, p, optname_str, value_str)) + return; + + printf("Error: couldn't find option string %s\n", optname_str); +} + +#define DIMM_PARM(x) {#x, offsetof(dimm_params_t, x), \ + sizeof((dimm_params_t *)0)->x, 0} + +static void fsl_ddr_dimm_parameters_edit(fsl_ddr_info_t *pinfo, + unsigned int ctrl_num, + unsigned int dimm_num, + const char *optname_str, + const char *value_str) +{ + dimm_params_t *p = &(pinfo->dimm_params[ctrl_num][dimm_num]); + + static const struct options_string options[] = { + DIMM_PARM(n_ranks), + DIMM_PARM(data_width), + DIMM_PARM(primary_sdram_width), + DIMM_PARM(ec_sdram_width), + DIMM_PARM(registered_dimm), + DIMM_PARM(device_width), + + DIMM_PARM(n_row_addr), + DIMM_PARM(n_col_addr), + DIMM_PARM(edc_config), +#ifdef CONFIG_SYS_FSL_DDR4 + DIMM_PARM(bank_addr_bits), + DIMM_PARM(bank_group_bits), +#else + DIMM_PARM(n_banks_per_sdram_device), +#endif + DIMM_PARM(burst_lengths_bitmask), + DIMM_PARM(row_density), + + DIMM_PARM(tckmin_x_ps), + DIMM_PARM(tckmin_x_minus_1_ps), + DIMM_PARM(tckmin_x_minus_2_ps), + DIMM_PARM(tckmax_ps), + + DIMM_PARM(caslat_x), + DIMM_PARM(caslat_x_minus_1), + DIMM_PARM(caslat_x_minus_2), + + DIMM_PARM(caslat_lowest_derated), + + DIMM_PARM(trcd_ps), + DIMM_PARM(trp_ps), + DIMM_PARM(tras_ps), +#ifdef CONFIG_SYS_FSL_DDR4 + DIMM_PARM(trfc1_ps), + DIMM_PARM(trfc2_ps), + DIMM_PARM(trfc4_ps), + DIMM_PARM(trrds_ps), + DIMM_PARM(trrdl_ps), + DIMM_PARM(tccdl_ps), +#else + DIMM_PARM(twr_ps), + DIMM_PARM(twtr_ps), + DIMM_PARM(trfc_ps), + DIMM_PARM(trrd_ps), + DIMM_PARM(trtp_ps), +#endif + DIMM_PARM(trc_ps), + DIMM_PARM(refresh_rate_ps), + DIMM_PARM(extended_op_srt), + +#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2) + DIMM_PARM(tis_ps), + DIMM_PARM(tih_ps), + DIMM_PARM(tds_ps), + DIMM_PARM(tdh_ps), + DIMM_PARM(tdqsq_max_ps), + DIMM_PARM(tqhs_ps), +#endif + + DIMM_PARM(rank_density), + DIMM_PARM(capacity), + DIMM_PARM(base_address), + }; + + static const unsigned int n_opts = ARRAY_SIZE(options); + + if (handle_option_table(options, n_opts, p, optname_str, value_str)) + return; + + printf("couldn't find option string %s\n", optname_str); +} + +static void print_dimm_parameters(const dimm_params_t *pdimm) +{ + static const struct options_string options[] = { + DIMM_PARM(n_ranks), + DIMM_PARM(data_width), + DIMM_PARM(primary_sdram_width), + DIMM_PARM(ec_sdram_width), + DIMM_PARM(registered_dimm), + DIMM_PARM(device_width), + + DIMM_PARM(n_row_addr), + DIMM_PARM(n_col_addr), + DIMM_PARM(edc_config), +#ifdef CONFIG_SYS_FSL_DDR4 + DIMM_PARM(bank_addr_bits), + DIMM_PARM(bank_group_bits), +#else + DIMM_PARM(n_banks_per_sdram_device), +#endif + + DIMM_PARM(tckmin_x_ps), + DIMM_PARM(tckmin_x_minus_1_ps), + DIMM_PARM(tckmin_x_minus_2_ps), + DIMM_PARM(tckmax_ps), + + DIMM_PARM(caslat_x), + DIMM_PARM(taa_ps), + DIMM_PARM(caslat_x_minus_1), + DIMM_PARM(caslat_x_minus_2), + DIMM_PARM(caslat_lowest_derated), + + DIMM_PARM(trcd_ps), + DIMM_PARM(trp_ps), + DIMM_PARM(tras_ps), +#ifdef CONFIG_SYS_FSL_DDR4 + DIMM_PARM(trfc1_ps), + DIMM_PARM(trfc2_ps), + DIMM_PARM(trfc4_ps), + DIMM_PARM(trrds_ps), + DIMM_PARM(trrdl_ps), + DIMM_PARM(tccdl_ps), +#else + DIMM_PARM(twr_ps), + DIMM_PARM(twtr_ps), + DIMM_PARM(trfc_ps), + DIMM_PARM(trrd_ps), + DIMM_PARM(trtp_ps), +#endif + DIMM_PARM(trc_ps), + DIMM_PARM(refresh_rate_ps), + +#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2) + DIMM_PARM(tis_ps), + DIMM_PARM(tih_ps), + DIMM_PARM(tds_ps), + DIMM_PARM(tdh_ps), + DIMM_PARM(tdqsq_max_ps), + DIMM_PARM(tqhs_ps), +#endif + }; + static const unsigned int n_opts = ARRAY_SIZE(options); + + if (pdimm->n_ranks == 0) { + printf("DIMM not present\n"); + return; + } + printf("DIMM organization parameters:\n"); + printf("module part name = %s\n", pdimm->mpart); + printf("rank_density = %llu bytes (%llu megabytes)\n", + pdimm->rank_density, pdimm->rank_density / 0x100000); + printf("capacity = %llu bytes (%llu megabytes)\n", + pdimm->capacity, pdimm->capacity / 0x100000); + printf("burst_lengths_bitmask = %02X\n", + pdimm->burst_lengths_bitmask); + printf("base_addresss = %llu (%08llX %08llX)\n", + pdimm->base_address, + (pdimm->base_address >> 32), + pdimm->base_address & 0xFFFFFFFF); + print_option_table(options, n_opts, pdimm); +} + +static void print_lowest_common_dimm_parameters( + const common_timing_params_t *plcd_dimm_params) +{ + static const struct options_string options[] = { + COMMON_TIMING(taamin_ps), + COMMON_TIMING(trcd_ps), + COMMON_TIMING(trp_ps), + COMMON_TIMING(tras_ps), +#ifdef CONFIG_SYS_FSL_DDR4 + COMMON_TIMING(trfc1_ps), + COMMON_TIMING(trfc2_ps), + COMMON_TIMING(trfc4_ps), + COMMON_TIMING(trrds_ps), + COMMON_TIMING(trrdl_ps), + COMMON_TIMING(tccdl_ps), +#else + COMMON_TIMING(twtr_ps), + COMMON_TIMING(trfc_ps), + COMMON_TIMING(trrd_ps), + COMMON_TIMING(trtp_ps), +#endif + COMMON_TIMING(twr_ps), + COMMON_TIMING(trc_ps), + COMMON_TIMING(refresh_rate_ps), + COMMON_TIMING(extended_op_srt), +#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2) + COMMON_TIMING(tis_ps), + COMMON_TIMING(tih_ps), + COMMON_TIMING(tds_ps), + COMMON_TIMING(tdh_ps), + COMMON_TIMING(tdqsq_max_ps), + COMMON_TIMING(tqhs_ps), +#endif + COMMON_TIMING(lowest_common_spd_caslat), + COMMON_TIMING(highest_common_derated_caslat), + COMMON_TIMING(additive_latency), + COMMON_TIMING(ndimms_present), + COMMON_TIMING(all_dimms_registered), + COMMON_TIMING(all_dimms_unbuffered), + COMMON_TIMING(all_dimms_ecc_capable), + }; + static const unsigned int n_opts = ARRAY_SIZE(options); + + /* Clock frequencies */ + printf("tckmin_x_ps = %u (%u MHz)\n", + plcd_dimm_params->tckmin_x_ps, + picos_to_mhz(plcd_dimm_params->tckmin_x_ps)); + printf("tckmax_ps = %u (%u MHz)\n", + plcd_dimm_params->tckmax_ps, + picos_to_mhz(plcd_dimm_params->tckmax_ps)); + printf("all_dimms_burst_lengths_bitmask = %02X\n", + plcd_dimm_params->all_dimms_burst_lengths_bitmask); + + print_option_table(options, n_opts, plcd_dimm_params); + + printf("total_mem = %llu (%llu megabytes)\n", + plcd_dimm_params->total_mem, + plcd_dimm_params->total_mem / 0x100000); + printf("base_address = %llu (%llu megabytes)\n", + plcd_dimm_params->base_address, + plcd_dimm_params->base_address / 0x100000); +} + +#define CTRL_OPTIONS(x) {#x, offsetof(memctl_options_t, x), \ + sizeof((memctl_options_t *)0)->x, 0} +#define CTRL_OPTIONS_CS(x, y) {"cs" #x "_" #y, \ + offsetof(memctl_options_t, cs_local_opts[x].y), \ + sizeof((memctl_options_t *)0)->cs_local_opts[x].y, 0} + +static void fsl_ddr_options_edit(fsl_ddr_info_t *pinfo, + unsigned int ctl_num, + const char *optname_str, + const char *value_str) +{ + memctl_options_t *p = &(pinfo->memctl_opts[ctl_num]); + /* + * This array all on the stack and *computed* each time this + * function is rung. + */ + static const struct options_string options[] = { + CTRL_OPTIONS_CS(0, odt_rd_cfg), + CTRL_OPTIONS_CS(0, odt_wr_cfg), +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 1) + CTRL_OPTIONS_CS(1, odt_rd_cfg), + CTRL_OPTIONS_CS(1, odt_wr_cfg), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2) + CTRL_OPTIONS_CS(2, odt_rd_cfg), + CTRL_OPTIONS_CS(2, odt_wr_cfg), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2) + CTRL_OPTIONS_CS(3, odt_rd_cfg), + CTRL_OPTIONS_CS(3, odt_wr_cfg), +#endif +#if defined(CONFIG_SYS_FSL_DDR3) + CTRL_OPTIONS_CS(0, odt_rtt_norm), + CTRL_OPTIONS_CS(0, odt_rtt_wr), +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 1) + CTRL_OPTIONS_CS(1, odt_rtt_norm), + CTRL_OPTIONS_CS(1, odt_rtt_wr), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2) + CTRL_OPTIONS_CS(2, odt_rtt_norm), + CTRL_OPTIONS_CS(2, odt_rtt_wr), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2) + CTRL_OPTIONS_CS(3, odt_rtt_norm), + CTRL_OPTIONS_CS(3, odt_rtt_wr), +#endif +#endif + CTRL_OPTIONS(memctl_interleaving), + CTRL_OPTIONS(memctl_interleaving_mode), + CTRL_OPTIONS(ba_intlv_ctl), + CTRL_OPTIONS(ecc_mode), + CTRL_OPTIONS(ecc_init_using_memctl), + CTRL_OPTIONS(dqs_config), + CTRL_OPTIONS(self_refresh_in_sleep), + CTRL_OPTIONS(dynamic_power), + CTRL_OPTIONS(data_bus_width), + CTRL_OPTIONS(burst_length), + CTRL_OPTIONS(cas_latency_override), + CTRL_OPTIONS(cas_latency_override_value), + CTRL_OPTIONS(use_derated_caslat), + CTRL_OPTIONS(additive_latency_override), + CTRL_OPTIONS(additive_latency_override_value), + CTRL_OPTIONS(clk_adjust), + CTRL_OPTIONS(cpo_override), + CTRL_OPTIONS(write_data_delay), + CTRL_OPTIONS(half_strength_driver_enable), + + /* + * These can probably be changed to 2T_EN and 3T_EN + * (using a leading numerical character) without problem + */ + CTRL_OPTIONS(twot_en), + CTRL_OPTIONS(threet_en), + CTRL_OPTIONS(ap_en), + CTRL_OPTIONS(x4_en), + CTRL_OPTIONS(bstopre), + CTRL_OPTIONS(wrlvl_override), + CTRL_OPTIONS(wrlvl_sample), + CTRL_OPTIONS(wrlvl_start), + CTRL_OPTIONS(rcw_override), + CTRL_OPTIONS(rcw_1), + CTRL_OPTIONS(rcw_2), + CTRL_OPTIONS(ddr_cdr1), + CTRL_OPTIONS(ddr_cdr2), + CTRL_OPTIONS(tcke_clock_pulse_width_ps), + CTRL_OPTIONS(tfaw_window_four_activates_ps), + CTRL_OPTIONS(trwt_override), + CTRL_OPTIONS(trwt), + CTRL_OPTIONS(rtt_override), + CTRL_OPTIONS(rtt_override_value), + CTRL_OPTIONS(rtt_wr_override_value), + }; + + static const unsigned int n_opts = ARRAY_SIZE(options); + + if (handle_option_table(options, n_opts, p, + optname_str, value_str)) + return; + + printf("couldn't find option string %s\n", optname_str); +} + +#define CFG_REGS(x) {#x, offsetof(fsl_ddr_cfg_regs_t, x), \ + sizeof((fsl_ddr_cfg_regs_t *)0)->x, 1} +#define CFG_REGS_CS(x, y) {"cs" #x "_" #y, \ + offsetof(fsl_ddr_cfg_regs_t, cs[x].y), \ + sizeof((fsl_ddr_cfg_regs_t *)0)->cs[x].y, 1} + +static void print_fsl_memctl_config_regs(const fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int i; + static const struct options_string options[] = { + CFG_REGS_CS(0, bnds), + CFG_REGS_CS(0, config), + CFG_REGS_CS(0, config_2), +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 1) + CFG_REGS_CS(1, bnds), + CFG_REGS_CS(1, config), + CFG_REGS_CS(1, config_2), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2) + CFG_REGS_CS(2, bnds), + CFG_REGS_CS(2, config), + CFG_REGS_CS(2, config_2), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2) + CFG_REGS_CS(3, bnds), + CFG_REGS_CS(3, config), + CFG_REGS_CS(3, config_2), +#endif + CFG_REGS(timing_cfg_3), + CFG_REGS(timing_cfg_0), + CFG_REGS(timing_cfg_1), + CFG_REGS(timing_cfg_2), + CFG_REGS(ddr_sdram_cfg), + CFG_REGS(ddr_sdram_cfg_2), + CFG_REGS(ddr_sdram_cfg_3), + CFG_REGS(ddr_sdram_mode), + CFG_REGS(ddr_sdram_mode_2), + CFG_REGS(ddr_sdram_mode_3), + CFG_REGS(ddr_sdram_mode_4), + CFG_REGS(ddr_sdram_mode_5), + CFG_REGS(ddr_sdram_mode_6), + CFG_REGS(ddr_sdram_mode_7), + CFG_REGS(ddr_sdram_mode_8), +#ifdef CONFIG_SYS_FSL_DDR4 + CFG_REGS(ddr_sdram_mode_9), + CFG_REGS(ddr_sdram_mode_10), + CFG_REGS(ddr_sdram_mode_11), + CFG_REGS(ddr_sdram_mode_12), + CFG_REGS(ddr_sdram_mode_13), + CFG_REGS(ddr_sdram_mode_14), + CFG_REGS(ddr_sdram_mode_15), + CFG_REGS(ddr_sdram_mode_16), +#endif + CFG_REGS(ddr_sdram_interval), + CFG_REGS(ddr_data_init), + CFG_REGS(ddr_sdram_clk_cntl), + CFG_REGS(ddr_init_addr), + CFG_REGS(ddr_init_ext_addr), + CFG_REGS(timing_cfg_4), + CFG_REGS(timing_cfg_5), +#ifdef CONFIG_SYS_FSL_DDR4 + CFG_REGS(timing_cfg_6), + CFG_REGS(timing_cfg_7), + CFG_REGS(timing_cfg_8), + CFG_REGS(timing_cfg_9), +#endif + CFG_REGS(ddr_zq_cntl), + CFG_REGS(ddr_wrlvl_cntl), + CFG_REGS(ddr_wrlvl_cntl_2), + CFG_REGS(ddr_wrlvl_cntl_3), + CFG_REGS(ddr_sr_cntr), + CFG_REGS(ddr_sdram_rcw_1), + CFG_REGS(ddr_sdram_rcw_2), + CFG_REGS(ddr_cdr1), + CFG_REGS(ddr_cdr2), + CFG_REGS(dq_map_0), + CFG_REGS(dq_map_1), + CFG_REGS(dq_map_2), + CFG_REGS(dq_map_3), + CFG_REGS(err_disable), + CFG_REGS(err_int_en), + CFG_REGS(ddr_eor), + }; + static const unsigned int n_opts = ARRAY_SIZE(options); + + print_option_table(options, n_opts, ddr); + + for (i = 0; i < 32; i++) + printf("debug_%02d = 0x%08X\n", i+1, ddr->debug[i]); +} + +static void fsl_ddr_regs_edit(fsl_ddr_info_t *pinfo, + unsigned int ctrl_num, + const char *regname, + const char *value_str) +{ + unsigned int i; + fsl_ddr_cfg_regs_t *ddr; + char buf[20]; + static const struct options_string options[] = { + CFG_REGS_CS(0, bnds), + CFG_REGS_CS(0, config), + CFG_REGS_CS(0, config_2), +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 1) + CFG_REGS_CS(1, bnds), + CFG_REGS_CS(1, config), + CFG_REGS_CS(1, config_2), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2) + CFG_REGS_CS(2, bnds), + CFG_REGS_CS(2, config), + CFG_REGS_CS(2, config_2), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 3) + CFG_REGS_CS(3, bnds), + CFG_REGS_CS(3, config), + CFG_REGS_CS(3, config_2), +#endif + CFG_REGS(timing_cfg_3), + CFG_REGS(timing_cfg_0), + CFG_REGS(timing_cfg_1), + CFG_REGS(timing_cfg_2), + CFG_REGS(ddr_sdram_cfg), + CFG_REGS(ddr_sdram_cfg_2), + CFG_REGS(ddr_sdram_cfg_3), + CFG_REGS(ddr_sdram_mode), + CFG_REGS(ddr_sdram_mode_2), + CFG_REGS(ddr_sdram_mode_3), + CFG_REGS(ddr_sdram_mode_4), + CFG_REGS(ddr_sdram_mode_5), + CFG_REGS(ddr_sdram_mode_6), + CFG_REGS(ddr_sdram_mode_7), + CFG_REGS(ddr_sdram_mode_8), +#ifdef CONFIG_SYS_FSL_DDR4 + CFG_REGS(ddr_sdram_mode_9), + CFG_REGS(ddr_sdram_mode_10), + CFG_REGS(ddr_sdram_mode_11), + CFG_REGS(ddr_sdram_mode_12), + CFG_REGS(ddr_sdram_mode_13), + CFG_REGS(ddr_sdram_mode_14), + CFG_REGS(ddr_sdram_mode_15), + CFG_REGS(ddr_sdram_mode_16), +#endif + CFG_REGS(ddr_sdram_interval), + CFG_REGS(ddr_data_init), + CFG_REGS(ddr_sdram_clk_cntl), + CFG_REGS(ddr_init_addr), + CFG_REGS(ddr_init_ext_addr), + CFG_REGS(timing_cfg_4), + CFG_REGS(timing_cfg_5), +#ifdef CONFIG_SYS_FSL_DDR4 + CFG_REGS(timing_cfg_6), + CFG_REGS(timing_cfg_7), + CFG_REGS(timing_cfg_8), + CFG_REGS(timing_cfg_9), +#endif + CFG_REGS(ddr_zq_cntl), + CFG_REGS(ddr_wrlvl_cntl), + CFG_REGS(ddr_wrlvl_cntl_2), + CFG_REGS(ddr_wrlvl_cntl_3), + CFG_REGS(ddr_sr_cntr), + CFG_REGS(ddr_sdram_rcw_1), + CFG_REGS(ddr_sdram_rcw_2), + CFG_REGS(ddr_cdr1), + CFG_REGS(ddr_cdr2), + CFG_REGS(dq_map_0), + CFG_REGS(dq_map_1), + CFG_REGS(dq_map_2), + CFG_REGS(dq_map_3), + CFG_REGS(err_disable), + CFG_REGS(err_int_en), + CFG_REGS(ddr_sdram_rcw_2), + CFG_REGS(ddr_sdram_rcw_2), + CFG_REGS(ddr_eor), + }; + static const unsigned int n_opts = ARRAY_SIZE(options); + + debug("fsl_ddr_regs_edit: ctrl_num = %u, " + "regname = %s, value = %s\n", + ctrl_num, regname, value_str); + if (ctrl_num > CONFIG_NUM_DDR_CONTROLLERS) + return; + + ddr = &(pinfo->fsl_ddr_config_reg[ctrl_num]); + + if (handle_option_table(options, n_opts, ddr, regname, value_str)) + return; + + for (i = 0; i < 32; i++) { + unsigned int value = simple_strtoul(value_str, NULL, 0); + sprintf(buf, "debug_%u", i + 1); + if (strcmp(buf, regname) == 0) { + ddr->debug[i] = value; + return; + } + } + printf("Error: couldn't find register string %s\n", regname); +} + +#define CTRL_OPTIONS_HEX(x) {#x, offsetof(memctl_options_t, x), \ + sizeof((memctl_options_t *)0)->x, 1} + +static void print_memctl_options(const memctl_options_t *popts) +{ + static const struct options_string options[] = { + CTRL_OPTIONS_CS(0, odt_rd_cfg), + CTRL_OPTIONS_CS(0, odt_wr_cfg), +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 1) + CTRL_OPTIONS_CS(1, odt_rd_cfg), + CTRL_OPTIONS_CS(1, odt_wr_cfg), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2) + CTRL_OPTIONS_CS(2, odt_rd_cfg), + CTRL_OPTIONS_CS(2, odt_wr_cfg), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 3) + CTRL_OPTIONS_CS(3, odt_rd_cfg), + CTRL_OPTIONS_CS(3, odt_wr_cfg), +#endif +#if defined(CONFIG_SYS_FSL_DDR3) + CTRL_OPTIONS_CS(0, odt_rtt_norm), + CTRL_OPTIONS_CS(0, odt_rtt_wr), +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 1) + CTRL_OPTIONS_CS(1, odt_rtt_norm), + CTRL_OPTIONS_CS(1, odt_rtt_wr), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2) + CTRL_OPTIONS_CS(2, odt_rtt_norm), + CTRL_OPTIONS_CS(2, odt_rtt_wr), +#endif +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 3) + CTRL_OPTIONS_CS(3, odt_rtt_norm), + CTRL_OPTIONS_CS(3, odt_rtt_wr), +#endif +#endif + CTRL_OPTIONS(memctl_interleaving), + CTRL_OPTIONS(memctl_interleaving_mode), + CTRL_OPTIONS_HEX(ba_intlv_ctl), + CTRL_OPTIONS(ecc_mode), + CTRL_OPTIONS(ecc_init_using_memctl), + CTRL_OPTIONS(dqs_config), + CTRL_OPTIONS(self_refresh_in_sleep), + CTRL_OPTIONS(dynamic_power), + CTRL_OPTIONS(data_bus_width), + CTRL_OPTIONS(burst_length), + CTRL_OPTIONS(cas_latency_override), + CTRL_OPTIONS(cas_latency_override_value), + CTRL_OPTIONS(use_derated_caslat), + CTRL_OPTIONS(additive_latency_override), + CTRL_OPTIONS(additive_latency_override_value), + CTRL_OPTIONS(clk_adjust), + CTRL_OPTIONS(cpo_override), + CTRL_OPTIONS(write_data_delay), + CTRL_OPTIONS(half_strength_driver_enable), + /* + * These can probably be changed to 2T_EN and 3T_EN + * (using a leading numerical character) without problem + */ + CTRL_OPTIONS(twot_en), + CTRL_OPTIONS(threet_en), + CTRL_OPTIONS(registered_dimm_en), + CTRL_OPTIONS(ap_en), + CTRL_OPTIONS(x4_en), + CTRL_OPTIONS(bstopre), + CTRL_OPTIONS(wrlvl_override), + CTRL_OPTIONS(wrlvl_sample), + CTRL_OPTIONS(wrlvl_start), + CTRL_OPTIONS(rcw_override), + CTRL_OPTIONS(rcw_1), + CTRL_OPTIONS(rcw_2), + CTRL_OPTIONS_HEX(ddr_cdr1), + CTRL_OPTIONS_HEX(ddr_cdr2), + CTRL_OPTIONS(tcke_clock_pulse_width_ps), + CTRL_OPTIONS(tfaw_window_four_activates_ps), + CTRL_OPTIONS(trwt_override), + CTRL_OPTIONS(trwt), + CTRL_OPTIONS(rtt_override), + CTRL_OPTIONS(rtt_override_value), + CTRL_OPTIONS(rtt_wr_override_value), + }; + static const unsigned int n_opts = ARRAY_SIZE(options); + + print_option_table(options, n_opts, popts); +} + +#ifdef CONFIG_SYS_FSL_DDR1 +void ddr1_spd_dump(const ddr1_spd_eeprom_t *spd) +{ + unsigned int i; + + printf("%-3d : %02x %s\n", 0, spd->info_size, + " spd->info_size, * 0 # bytes written into serial memory *"); + printf("%-3d : %02x %s\n", 1, spd->chip_size, + " spd->chip_size, * 1 Total # bytes of SPD memory device *"); + printf("%-3d : %02x %s\n", 2, spd->mem_type, + " spd->mem_type, * 2 Fundamental memory type *"); + printf("%-3d : %02x %s\n", 3, spd->nrow_addr, + " spd->nrow_addr, * 3 # of Row Addresses on this assembly *"); + printf("%-3d : %02x %s\n", 4, spd->ncol_addr, + " spd->ncol_addr, * 4 # of Column Addrs on this assembly *"); + printf("%-3d : %02x %s\n", 5, spd->nrows, + " spd->nrows * 5 # of DIMM Banks *"); + printf("%-3d : %02x %s\n", 6, spd->dataw_lsb, + " spd->dataw_lsb, * 6 Data Width lsb of this assembly *"); + printf("%-3d : %02x %s\n", 7, spd->dataw_msb, + " spd->dataw_msb, * 7 Data Width msb of this assembly *"); + printf("%-3d : %02x %s\n", 8, spd->voltage, + " spd->voltage, * 8 Voltage intf std of this assembly *"); + printf("%-3d : %02x %s\n", 9, spd->clk_cycle, + " spd->clk_cycle, * 9 SDRAM Cycle time at CL=X *"); + printf("%-3d : %02x %s\n", 10, spd->clk_access, + " spd->clk_access, * 10 SDRAM Access from Clock at CL=X *"); + printf("%-3d : %02x %s\n", 11, spd->config, + " spd->config, * 11 DIMM Configuration type *"); + printf("%-3d : %02x %s\n", 12, spd->refresh, + " spd->refresh, * 12 Refresh Rate/Type *"); + printf("%-3d : %02x %s\n", 13, spd->primw, + " spd->primw, * 13 Primary SDRAM Width *"); + printf("%-3d : %02x %s\n", 14, spd->ecw, + " spd->ecw, * 14 Error Checking SDRAM width *"); + printf("%-3d : %02x %s\n", 15, spd->min_delay, + " spd->min_delay, * 15 Back to Back Random Access *"); + printf("%-3d : %02x %s\n", 16, spd->burstl, + " spd->burstl, * 16 Burst Lengths Supported *"); + printf("%-3d : %02x %s\n", 17, spd->nbanks, + " spd->nbanks, * 17 # of Banks on Each SDRAM Device *"); + printf("%-3d : %02x %s\n", 18, spd->cas_lat, + " spd->cas_lat, * 18 CAS# Latencies Supported *"); + printf("%-3d : %02x %s\n", 19, spd->cs_lat, + " spd->cs_lat, * 19 Chip Select Latency *"); + printf("%-3d : %02x %s\n", 20, spd->write_lat, + " spd->write_lat, * 20 Write Latency/Recovery *"); + printf("%-3d : %02x %s\n", 21, spd->mod_attr, + " spd->mod_attr, * 21 SDRAM Module Attributes *"); + printf("%-3d : %02x %s\n", 22, spd->dev_attr, + " spd->dev_attr, * 22 SDRAM Device Attributes *"); + printf("%-3d : %02x %s\n", 23, spd->clk_cycle2, + " spd->clk_cycle2, * 23 Min SDRAM Cycle time at CL=X-1 *"); + printf("%-3d : %02x %s\n", 24, spd->clk_access2, + " spd->clk_access2, * 24 SDRAM Access from Clock at CL=X-1 *"); + printf("%-3d : %02x %s\n", 25, spd->clk_cycle3, + " spd->clk_cycle3, * 25 Min SDRAM Cycle time at CL=X-2 *"); + printf("%-3d : %02x %s\n", 26, spd->clk_access3, + " spd->clk_access3, * 26 Max Access from Clock at CL=X-2 *"); + printf("%-3d : %02x %s\n", 27, spd->trp, + " spd->trp, * 27 Min Row Precharge Time (tRP)*"); + printf("%-3d : %02x %s\n", 28, spd->trrd, + " spd->trrd, * 28 Min Row Active to Row Active (tRRD) *"); + printf("%-3d : %02x %s\n", 29, spd->trcd, + " spd->trcd, * 29 Min RAS to CAS Delay (tRCD) *"); + printf("%-3d : %02x %s\n", 30, spd->tras, + " spd->tras, * 30 Minimum RAS Pulse Width (tRAS) *"); + printf("%-3d : %02x %s\n", 31, spd->bank_dens, + " spd->bank_dens, * 31 Density of each bank on module *"); + printf("%-3d : %02x %s\n", 32, spd->ca_setup, + " spd->ca_setup, * 32 Cmd + Addr signal input setup time *"); + printf("%-3d : %02x %s\n", 33, spd->ca_hold, + " spd->ca_hold, * 33 Cmd and Addr signal input hold time *"); + printf("%-3d : %02x %s\n", 34, spd->data_setup, + " spd->data_setup, * 34 Data signal input setup time *"); + printf("%-3d : %02x %s\n", 35, spd->data_hold, + " spd->data_hold, * 35 Data signal input hold time *"); + printf("%-3d : %02x %s\n", 36, spd->res_36_40[0], + " spd->res_36_40[0], * 36 Reserved / tWR *"); + printf("%-3d : %02x %s\n", 37, spd->res_36_40[1], + " spd->res_36_40[1], * 37 Reserved / tWTR *"); + printf("%-3d : %02x %s\n", 38, spd->res_36_40[2], + " spd->res_36_40[2], * 38 Reserved / tRTP *"); + printf("%-3d : %02x %s\n", 39, spd->res_36_40[3], + " spd->res_36_40[3], * 39 Reserved / mem_probe *"); + printf("%-3d : %02x %s\n", 40, spd->res_36_40[4], + " spd->res_36_40[4], * 40 Reserved / trc,trfc extensions *"); + printf("%-3d : %02x %s\n", 41, spd->trc, + " spd->trc, * 41 Min Active to Auto refresh time tRC *"); + printf("%-3d : %02x %s\n", 42, spd->trfc, + " spd->trfc, * 42 Min Auto to Active period tRFC *"); + printf("%-3d : %02x %s\n", 43, spd->tckmax, + " spd->tckmax, * 43 Max device cycle time tCKmax *"); + printf("%-3d : %02x %s\n", 44, spd->tdqsq, + " spd->tdqsq, * 44 Max DQS to DQ skew *"); + printf("%-3d : %02x %s\n", 45, spd->tqhs, + " spd->tqhs, * 45 Max Read DataHold skew tQHS *"); + printf("%-3d : %02x %s\n", 46, spd->res_46, + " spd->res_46, * 46 Reserved/ PLL Relock time *"); + printf("%-3d : %02x %s\n", 47, spd->dimm_height, + " spd->dimm_height * 47 SDRAM DIMM Height *"); + + printf("%-3d-%3d: ", 48, 61); + + for (i = 0; i < 14; i++) + printf("%02x", spd->res_48_61[i]); + + printf(" * 48-61 IDD in SPD and Reserved space *\n"); + + printf("%-3d : %02x %s\n", 62, spd->spd_rev, + " spd->spd_rev, * 62 SPD Data Revision Code *"); + printf("%-3d : %02x %s\n", 63, spd->cksum, + " spd->cksum, * 63 Checksum for bytes 0-62 *"); + printf("%-3d-%3d: ", 64, 71); + + for (i = 0; i < 8; i++) + printf("%02x", spd->mid[i]); + + printf("* 64 Mfr's JEDEC ID code per JEP-108E *\n"); + printf("%-3d : %02x %s\n", 72, spd->mloc, + " spd->mloc, * 72 Manufacturing Location *"); + + printf("%-3d-%3d: >>", 73, 90); + + for (i = 0; i < 18; i++) + printf("%c", spd->mpart[i]); + + printf("<<* 73 Manufacturer's Part Number *\n"); + + printf("%-3d-%3d: %02x %02x %s\n", 91, 92, spd->rev[0], spd->rev[1], + "* 91 Revision Code *"); + printf("%-3d-%3d: %02x %02x %s\n", 93, 94, spd->mdate[0], spd->mdate[1], + "* 93 Manufacturing Date *"); + printf("%-3d-%3d: ", 95, 98); + + for (i = 0; i < 4; i++) + printf("%02x", spd->sernum[i]); + + printf("* 95 Assembly Serial Number *\n"); + + printf("%-3d-%3d: ", 99, 127); + + for (i = 0; i < 27; i++) + printf("%02x", spd->mspec[i]); + + printf("* 99 Manufacturer Specific Data *\n"); +} +#endif + +#ifdef CONFIG_SYS_FSL_DDR2 +void ddr2_spd_dump(const ddr2_spd_eeprom_t *spd) +{ + unsigned int i; + + printf("%-3d : %02x %s\n", 0, spd->info_size, + " spd->info_size, * 0 # bytes written into serial memory *"); + printf("%-3d : %02x %s\n", 1, spd->chip_size, + " spd->chip_size, * 1 Total # bytes of SPD memory device *"); + printf("%-3d : %02x %s\n", 2, spd->mem_type, + " spd->mem_type, * 2 Fundamental memory type *"); + printf("%-3d : %02x %s\n", 3, spd->nrow_addr, + " spd->nrow_addr, * 3 # of Row Addresses on this assembly *"); + printf("%-3d : %02x %s\n", 4, spd->ncol_addr, + " spd->ncol_addr, * 4 # of Column Addrs on this assembly *"); + printf("%-3d : %02x %s\n", 5, spd->mod_ranks, + " spd->mod_ranks * 5 # of Module Rows on this assembly *"); + printf("%-3d : %02x %s\n", 6, spd->dataw, + " spd->dataw, * 6 Data Width of this assembly *"); + printf("%-3d : %02x %s\n", 7, spd->res_7, + " spd->res_7, * 7 Reserved *"); + printf("%-3d : %02x %s\n", 8, spd->voltage, + " spd->voltage, * 8 Voltage intf std of this assembly *"); + printf("%-3d : %02x %s\n", 9, spd->clk_cycle, + " spd->clk_cycle, * 9 SDRAM Cycle time at CL=X *"); + printf("%-3d : %02x %s\n", 10, spd->clk_access, + " spd->clk_access, * 10 SDRAM Access from Clock at CL=X *"); + printf("%-3d : %02x %s\n", 11, spd->config, + " spd->config, * 11 DIMM Configuration type *"); + printf("%-3d : %02x %s\n", 12, spd->refresh, + " spd->refresh, * 12 Refresh Rate/Type *"); + printf("%-3d : %02x %s\n", 13, spd->primw, + " spd->primw, * 13 Primary SDRAM Width *"); + printf("%-3d : %02x %s\n", 14, spd->ecw, + " spd->ecw, * 14 Error Checking SDRAM width *"); + printf("%-3d : %02x %s\n", 15, spd->res_15, + " spd->res_15, * 15 Reserved *"); + printf("%-3d : %02x %s\n", 16, spd->burstl, + " spd->burstl, * 16 Burst Lengths Supported *"); + printf("%-3d : %02x %s\n", 17, spd->nbanks, + " spd->nbanks, * 17 # of Banks on Each SDRAM Device *"); + printf("%-3d : %02x %s\n", 18, spd->cas_lat, + " spd->cas_lat, * 18 CAS# Latencies Supported *"); + printf("%-3d : %02x %s\n", 19, spd->mech_char, + " spd->mech_char, * 19 Mechanical Characteristics *"); + printf("%-3d : %02x %s\n", 20, spd->dimm_type, + " spd->dimm_type, * 20 DIMM type *"); + printf("%-3d : %02x %s\n", 21, spd->mod_attr, + " spd->mod_attr, * 21 SDRAM Module Attributes *"); + printf("%-3d : %02x %s\n", 22, spd->dev_attr, + " spd->dev_attr, * 22 SDRAM Device Attributes *"); + printf("%-3d : %02x %s\n", 23, spd->clk_cycle2, + " spd->clk_cycle2, * 23 Min SDRAM Cycle time at CL=X-1 *"); + printf("%-3d : %02x %s\n", 24, spd->clk_access2, + " spd->clk_access2, * 24 SDRAM Access from Clock at CL=X-1 *"); + printf("%-3d : %02x %s\n", 25, spd->clk_cycle3, + " spd->clk_cycle3, * 25 Min SDRAM Cycle time at CL=X-2 *"); + printf("%-3d : %02x %s\n", 26, spd->clk_access3, + " spd->clk_access3, * 26 Max Access from Clock at CL=X-2 *"); + printf("%-3d : %02x %s\n", 27, spd->trp, + " spd->trp, * 27 Min Row Precharge Time (tRP)*"); + printf("%-3d : %02x %s\n", 28, spd->trrd, + " spd->trrd, * 28 Min Row Active to Row Active (tRRD) *"); + printf("%-3d : %02x %s\n", 29, spd->trcd, + " spd->trcd, * 29 Min RAS to CAS Delay (tRCD) *"); + printf("%-3d : %02x %s\n", 30, spd->tras, + " spd->tras, * 30 Minimum RAS Pulse Width (tRAS) *"); + printf("%-3d : %02x %s\n", 31, spd->rank_dens, + " spd->rank_dens, * 31 Density of each rank on module *"); + printf("%-3d : %02x %s\n", 32, spd->ca_setup, + " spd->ca_setup, * 32 Cmd + Addr signal input setup time *"); + printf("%-3d : %02x %s\n", 33, spd->ca_hold, + " spd->ca_hold, * 33 Cmd and Addr signal input hold time *"); + printf("%-3d : %02x %s\n", 34, spd->data_setup, + " spd->data_setup, * 34 Data signal input setup time *"); + printf("%-3d : %02x %s\n", 35, spd->data_hold, + " spd->data_hold, * 35 Data signal input hold time *"); + printf("%-3d : %02x %s\n", 36, spd->twr, + " spd->twr, * 36 Write Recovery time tWR *"); + printf("%-3d : %02x %s\n", 37, spd->twtr, + " spd->twtr, * 37 Int write to read delay tWTR *"); + printf("%-3d : %02x %s\n", 38, spd->trtp, + " spd->trtp, * 38 Int read to precharge delay tRTP *"); + printf("%-3d : %02x %s\n", 39, spd->mem_probe, + " spd->mem_probe, * 39 Mem analysis probe characteristics *"); + printf("%-3d : %02x %s\n", 40, spd->trctrfc_ext, + " spd->trctrfc_ext, * 40 Extensions to trc and trfc *"); + printf("%-3d : %02x %s\n", 41, spd->trc, + " spd->trc, * 41 Min Active to Auto refresh time tRC *"); + printf("%-3d : %02x %s\n", 42, spd->trfc, + " spd->trfc, * 42 Min Auto to Active period tRFC *"); + printf("%-3d : %02x %s\n", 43, spd->tckmax, + " spd->tckmax, * 43 Max device cycle time tCKmax *"); + printf("%-3d : %02x %s\n", 44, spd->tdqsq, + " spd->tdqsq, * 44 Max DQS to DQ skew *"); + printf("%-3d : %02x %s\n", 45, spd->tqhs, + " spd->tqhs, * 45 Max Read DataHold skew tQHS *"); + printf("%-3d : %02x %s\n", 46, spd->pll_relock, + " spd->pll_relock, * 46 PLL Relock time *"); + printf("%-3d : %02x %s\n", 47, spd->t_casemax, + " spd->t_casemax, * 47 t_casemax *"); + printf("%-3d : %02x %s\n", 48, spd->psi_ta_dram, + " spd->psi_ta_dram, * 48 Thermal Resistance of DRAM Package " + "from Top (Case) to Ambient (Psi T-A DRAM) *"); + printf("%-3d : %02x %s\n", 49, spd->dt0_mode, + " spd->dt0_mode, * 49 DRAM Case Temperature Rise from " + "Ambient due to Activate-Precharge/Mode Bits " + "(DT0/Mode Bits) *)"); + printf("%-3d : %02x %s\n", 50, spd->dt2n_dt2q, + " spd->dt2n_dt2q, * 50 DRAM Case Temperature Rise from " + "Ambient due to Precharge/Quiet Standby " + "(DT2N/DT2Q) *"); + printf("%-3d : %02x %s\n", 51, spd->dt2p, + " spd->dt2p, * 51 DRAM Case Temperature Rise from " + "Ambient due to Precharge Power-Down (DT2P) *"); + printf("%-3d : %02x %s\n", 52, spd->dt3n, + " spd->dt3n, * 52 DRAM Case Temperature Rise from " + "Ambient due to Active Standby (DT3N) *"); + printf("%-3d : %02x %s\n", 53, spd->dt3pfast, + " spd->dt3pfast, * 53 DRAM Case Temperature Rise from " + "Ambient due to Active Power-Down with Fast PDN Exit " + "(DT3Pfast) *"); + printf("%-3d : %02x %s\n", 54, spd->dt3pslow, + " spd->dt3pslow, * 54 DRAM Case Temperature Rise from " + "Ambient due to Active Power-Down with Slow PDN Exit " + "(DT3Pslow) *"); + printf("%-3d : %02x %s\n", 55, spd->dt4r_dt4r4w, + " spd->dt4r_dt4r4w, * 55 DRAM Case Temperature Rise from " + "Ambient due to Page Open Burst Read/DT4R4W Mode Bit " + "(DT4R/DT4R4W Mode Bit) *"); + printf("%-3d : %02x %s\n", 56, spd->dt5b, + " spd->dt5b, * 56 DRAM Case Temperature Rise from " + "Ambient due to Burst Refresh (DT5B) *"); + printf("%-3d : %02x %s\n", 57, spd->dt7, + " spd->dt7, * 57 DRAM Case Temperature Rise from " + "Ambient due to Bank Interleave Reads with " + "Auto-Precharge (DT7) *"); + printf("%-3d : %02x %s\n", 58, spd->psi_ta_pll, + " spd->psi_ta_pll, * 58 Thermal Resistance of PLL Package form" + " Top (Case) to Ambient (Psi T-A PLL) *"); + printf("%-3d : %02x %s\n", 59, spd->psi_ta_reg, + " spd->psi_ta_reg, * 59 Thermal Reisitance of Register Package" + " from Top (Case) to Ambient (Psi T-A Register) *"); + printf("%-3d : %02x %s\n", 60, spd->dtpllactive, + " spd->dtpllactive, * 60 PLL Case Temperature Rise from " + "Ambient due to PLL Active (DT PLL Active) *"); + printf("%-3d : %02x %s\n", 61, spd->dtregact, + " spd->dtregact, " + "* 61 Register Case Temperature Rise from Ambient due to " + "Register Active/Mode Bit (DT Register Active/Mode Bit) *"); + printf("%-3d : %02x %s\n", 62, spd->spd_rev, + " spd->spd_rev, * 62 SPD Data Revision Code *"); + printf("%-3d : %02x %s\n", 63, spd->cksum, + " spd->cksum, * 63 Checksum for bytes 0-62 *"); + + printf("%-3d-%3d: ", 64, 71); + + for (i = 0; i < 8; i++) + printf("%02x", spd->mid[i]); + + printf("* 64 Mfr's JEDEC ID code per JEP-108E *\n"); + + printf("%-3d : %02x %s\n", 72, spd->mloc, + " spd->mloc, * 72 Manufacturing Location *"); + + printf("%-3d-%3d: >>", 73, 90); + for (i = 0; i < 18; i++) + printf("%c", spd->mpart[i]); + + + printf("<<* 73 Manufacturer's Part Number *\n"); + + printf("%-3d-%3d: %02x %02x %s\n", 91, 92, spd->rev[0], spd->rev[1], + "* 91 Revision Code *"); + printf("%-3d-%3d: %02x %02x %s\n", 93, 94, spd->mdate[0], spd->mdate[1], + "* 93 Manufacturing Date *"); + printf("%-3d-%3d: ", 95, 98); + + for (i = 0; i < 4; i++) + printf("%02x", spd->sernum[i]); + + printf("* 95 Assembly Serial Number *\n"); + + printf("%-3d-%3d: ", 99, 127); + for (i = 0; i < 27; i++) + printf("%02x", spd->mspec[i]); + + + printf("* 99 Manufacturer Specific Data *\n"); +} +#endif + +#ifdef CONFIG_SYS_FSL_DDR3 +void ddr3_spd_dump(const ddr3_spd_eeprom_t *spd) +{ + unsigned int i; + + /* General Section: Bytes 0-59 */ + +#define PRINT_NXS(x, y, z...) printf("%-3d : %02x " z "\n", x, (u8)y); +#define PRINT_NNXXS(n0, n1, x0, x1, s) \ + printf("%-3d-%3d: %02x %02x " s "\n", n0, n1, x0, x1); + + PRINT_NXS(0, spd->info_size_crc, + "info_size_crc bytes written into serial memory, " + "CRC coverage"); + PRINT_NXS(1, spd->spd_rev, + "spd_rev SPD Revision"); + PRINT_NXS(2, spd->mem_type, + "mem_type Key Byte / DRAM Device Type"); + PRINT_NXS(3, spd->module_type, + "module_type Key Byte / Module Type"); + PRINT_NXS(4, spd->density_banks, + "density_banks SDRAM Density and Banks"); + PRINT_NXS(5, spd->addressing, + "addressing SDRAM Addressing"); + PRINT_NXS(6, spd->module_vdd, + "module_vdd Module Nominal Voltage, VDD"); + PRINT_NXS(7, spd->organization, + "organization Module Organization"); + PRINT_NXS(8, spd->bus_width, + "bus_width Module Memory Bus Width"); + PRINT_NXS(9, spd->ftb_div, + "ftb_div Fine Timebase (FTB) Dividend / Divisor"); + PRINT_NXS(10, spd->mtb_dividend, + "mtb_dividend Medium Timebase (MTB) Dividend"); + PRINT_NXS(11, spd->mtb_divisor, + "mtb_divisor Medium Timebase (MTB) Divisor"); + PRINT_NXS(12, spd->tck_min, + "tck_min SDRAM Minimum Cycle Time"); + PRINT_NXS(13, spd->res_13, + "res_13 Reserved"); + PRINT_NXS(14, spd->caslat_lsb, + "caslat_lsb CAS Latencies Supported, LSB"); + PRINT_NXS(15, spd->caslat_msb, + "caslat_msb CAS Latencies Supported, MSB"); + PRINT_NXS(16, spd->taa_min, + "taa_min Min CAS Latency Time"); + PRINT_NXS(17, spd->twr_min, + "twr_min Min Write REcovery Time"); + PRINT_NXS(18, spd->trcd_min, + "trcd_min Min RAS# to CAS# Delay Time"); + PRINT_NXS(19, spd->trrd_min, + "trrd_min Min Row Active to Row Active Delay Time"); + PRINT_NXS(20, spd->trp_min, + "trp_min Min Row Precharge Delay Time"); + PRINT_NXS(21, spd->tras_trc_ext, + "tras_trc_ext Upper Nibbles for tRAS and tRC"); + PRINT_NXS(22, spd->tras_min_lsb, + "tras_min_lsb Min Active to Precharge Delay Time, LSB"); + PRINT_NXS(23, spd->trc_min_lsb, + "trc_min_lsb Min Active to Active/Refresh Delay Time, LSB"); + PRINT_NXS(24, spd->trfc_min_lsb, + "trfc_min_lsb Min Refresh Recovery Delay Time LSB"); + PRINT_NXS(25, spd->trfc_min_msb, + "trfc_min_msb Min Refresh Recovery Delay Time MSB"); + PRINT_NXS(26, spd->twtr_min, + "twtr_min Min Internal Write to Read Command Delay Time"); + PRINT_NXS(27, spd->trtp_min, + "trtp_min " + "Min Internal Read to Precharge Command Delay Time"); + PRINT_NXS(28, spd->tfaw_msb, + "tfaw_msb Upper Nibble for tFAW"); + PRINT_NXS(29, spd->tfaw_min, + "tfaw_min Min Four Activate Window Delay Time"); + PRINT_NXS(30, spd->opt_features, + "opt_features SDRAM Optional Features"); + PRINT_NXS(31, spd->therm_ref_opt, + "therm_ref_opt SDRAM Thermal and Refresh Opts"); + PRINT_NXS(32, spd->therm_sensor, + "therm_sensor SDRAM Thermal Sensor"); + PRINT_NXS(33, spd->device_type, + "device_type SDRAM Device Type"); + PRINT_NXS(34, spd->fine_tck_min, + "fine_tck_min Fine offset for tCKmin"); + PRINT_NXS(35, spd->fine_taa_min, + "fine_taa_min Fine offset for tAAmin"); + PRINT_NXS(36, spd->fine_trcd_min, + "fine_trcd_min Fine offset for tRCDmin"); + PRINT_NXS(37, spd->fine_trp_min, + "fine_trp_min Fine offset for tRPmin"); + PRINT_NXS(38, spd->fine_trc_min, + "fine_trc_min Fine offset for tRCmin"); + + printf("%-3d-%3d: ", 39, 59); /* Reserved, General Section */ + + for (i = 39; i <= 59; i++) + printf("%02x ", spd->res_39_59[i - 39]); + + puts("\n"); + + switch (spd->module_type) { + case 0x02: /* UDIMM */ + case 0x03: /* SO-DIMM */ + case 0x04: /* Micro-DIMM */ + case 0x06: /* Mini-UDIMM */ + PRINT_NXS(60, spd->mod_section.unbuffered.mod_height, + "mod_height (Unbuffered) Module Nominal Height"); + PRINT_NXS(61, spd->mod_section.unbuffered.mod_thickness, + "mod_thickness (Unbuffered) Module Maximum Thickness"); + PRINT_NXS(62, spd->mod_section.unbuffered.ref_raw_card, + "ref_raw_card (Unbuffered) Reference Raw Card Used"); + PRINT_NXS(63, spd->mod_section.unbuffered.addr_mapping, + "addr_mapping (Unbuffered) Address mapping from " + "Edge Connector to DRAM"); + break; + case 0x01: /* RDIMM */ + case 0x05: /* Mini-RDIMM */ + PRINT_NXS(60, spd->mod_section.registered.mod_height, + "mod_height (Registered) Module Nominal Height"); + PRINT_NXS(61, spd->mod_section.registered.mod_thickness, + "mod_thickness (Registered) Module Maximum Thickness"); + PRINT_NXS(62, spd->mod_section.registered.ref_raw_card, + "ref_raw_card (Registered) Reference Raw Card Used"); + PRINT_NXS(63, spd->mod_section.registered.modu_attr, + "modu_attr (Registered) DIMM Module Attributes"); + PRINT_NXS(64, spd->mod_section.registered.thermal, + "thermal (Registered) Thermal Heat " + "Spreader Solution"); + PRINT_NXS(65, spd->mod_section.registered.reg_id_lo, + "reg_id_lo (Registered) Register Manufacturer ID " + "Code, LSB"); + PRINT_NXS(66, spd->mod_section.registered.reg_id_hi, + "reg_id_hi (Registered) Register Manufacturer ID " + "Code, MSB"); + PRINT_NXS(67, spd->mod_section.registered.reg_rev, + "reg_rev (Registered) Register " + "Revision Number"); + PRINT_NXS(68, spd->mod_section.registered.reg_type, + "reg_type (Registered) Register Type"); + for (i = 69; i <= 76; i++) { + printf("%-3d : %02x rcw[%d]\n", i, + spd->mod_section.registered.rcw[i-69], i-69); + } + break; + default: + /* Module-specific Section, Unsupported Module Type */ + printf("%-3d-%3d: ", 60, 116); + + for (i = 60; i <= 116; i++) + printf("%02x", spd->mod_section.uc[i - 60]); + + break; + } + + /* Unique Module ID: Bytes 117-125 */ + PRINT_NXS(117, spd->mmid_lsb, "Module MfgID Code LSB - JEP-106"); + PRINT_NXS(118, spd->mmid_msb, "Module MfgID Code MSB - JEP-106"); + PRINT_NXS(119, spd->mloc, "Mfg Location"); + PRINT_NNXXS(120, 121, spd->mdate[0], spd->mdate[1], "Mfg Date"); + + printf("%-3d-%3d: ", 122, 125); + + for (i = 122; i <= 125; i++) + printf("%02x ", spd->sernum[i - 122]); + printf(" Module Serial Number\n"); + + /* CRC: Bytes 126-127 */ + PRINT_NNXXS(126, 127, spd->crc[0], spd->crc[1], " SPD CRC"); + + /* Other Manufacturer Fields and User Space: Bytes 128-255 */ + printf("%-3d-%3d: ", 128, 145); + for (i = 128; i <= 145; i++) + printf("%02x ", spd->mpart[i - 128]); + printf(" Mfg's Module Part Number\n"); + + PRINT_NNXXS(146, 147, spd->mrev[0], spd->mrev[1], + "Module Revision code"); + + PRINT_NXS(148, spd->dmid_lsb, "DRAM MfgID Code LSB - JEP-106"); + PRINT_NXS(149, spd->dmid_msb, "DRAM MfgID Code MSB - JEP-106"); + + printf("%-3d-%3d: ", 150, 175); + for (i = 150; i <= 175; i++) + printf("%02x ", spd->msd[i - 150]); + printf(" Mfg's Specific Data\n"); + + printf("%-3d-%3d: ", 176, 255); + for (i = 176; i <= 255; i++) + printf("%02x", spd->cust[i - 176]); + printf(" Mfg's Specific Data\n"); + +} +#endif + +#ifdef CONFIG_SYS_FSL_DDR4 +void ddr4_spd_dump(const struct ddr4_spd_eeprom_s *spd) +{ + unsigned int i; + + /* General Section: Bytes 0-127 */ + +#define PRINT_NXS(x, y, z...) printf("%-3d : %02x " z "\n", x, (u8)y); +#define PRINT_NNXXS(n0, n1, x0, x1, s) \ + printf("%-3d-%3d: %02x %02x " s "\n", n0, n1, x0, x1); + + PRINT_NXS(0, spd->info_size_crc, + "info_size_crc bytes written into serial memory, CRC coverage"); + PRINT_NXS(1, spd->spd_rev, + "spd_rev SPD Revision"); + PRINT_NXS(2, spd->mem_type, + "mem_type Key Byte / DRAM Device Type"); + PRINT_NXS(3, spd->module_type, + "module_type Key Byte / Module Type"); + PRINT_NXS(4, spd->density_banks, + "density_banks SDRAM Density and Banks"); + PRINT_NXS(5, spd->addressing, + "addressing SDRAM Addressing"); + PRINT_NXS(6, spd->package_type, + "package_type Package type"); + PRINT_NXS(7, spd->opt_feature, + "opt_feature Optional features"); + PRINT_NXS(8, spd->thermal_ref, + "thermal_ref Thermal and Refresh options"); + PRINT_NXS(9, spd->oth_opt_features, + "oth_opt_features Other SDRAM optional features"); + PRINT_NXS(10, spd->res_10, + "res_10 Reserved"); + PRINT_NXS(11, spd->module_vdd, + "module_vdd Module Nominal Voltage, VDD"); + PRINT_NXS(12, spd->organization, + "organization Module Organization"); + PRINT_NXS(13, spd->bus_width, + "bus_width Module Memory Bus Width"); + PRINT_NXS(14, spd->therm_sensor, + "therm_sensor Module Thermal Sensor"); + PRINT_NXS(15, spd->ext_type, + "ext_type Extended module type"); + PRINT_NXS(16, spd->res_16, + "res_16 Reserved"); + PRINT_NXS(17, spd->timebases, + "timebases MTb and FTB"); + PRINT_NXS(18, spd->tck_min, + "tck_min tCKAVGmin"); + PRINT_NXS(19, spd->tck_max, + "tck_max TCKAVGmax"); + PRINT_NXS(20, spd->caslat_b1, + "caslat_b1 CAS latencies, 1st byte"); + PRINT_NXS(21, spd->caslat_b2, + "caslat_b2 CAS latencies, 2nd byte"); + PRINT_NXS(22, spd->caslat_b3, + "caslat_b3 CAS latencies, 3rd byte "); + PRINT_NXS(23, spd->caslat_b4, + "caslat_b4 CAS latencies, 4th byte"); + PRINT_NXS(24, spd->taa_min, + "taa_min Min CAS Latency Time"); + PRINT_NXS(25, spd->trcd_min, + "trcd_min Min RAS# to CAS# Delay Time"); + PRINT_NXS(26, spd->trp_min, + "trp_min Min Row Precharge Delay Time"); + PRINT_NXS(27, spd->tras_trc_ext, + "tras_trc_ext Upper Nibbles for tRAS and tRC"); + PRINT_NXS(28, spd->tras_min_lsb, + "tras_min_lsb tRASmin, lsb"); + PRINT_NXS(29, spd->trc_min_lsb, + "trc_min_lsb tRCmin, lsb"); + PRINT_NXS(30, spd->trfc1_min_lsb, + "trfc1_min_lsb Min Refresh Recovery Delay Time, LSB"); + PRINT_NXS(31, spd->trfc1_min_msb, + "trfc1_min_msb Min Refresh Recovery Delay Time, MSB "); + PRINT_NXS(32, spd->trfc2_min_lsb, + "trfc2_min_lsb Min Refresh Recovery Delay Time, LSB"); + PRINT_NXS(33, spd->trfc2_min_msb, + "trfc2_min_msb Min Refresh Recovery Delay Time, MSB"); + PRINT_NXS(34, spd->trfc4_min_lsb, + "trfc4_min_lsb Min Refresh Recovery Delay Time, LSB"); + PRINT_NXS(35, spd->trfc4_min_msb, + "trfc4_min_msb Min Refresh Recovery Delay Time, MSB"); + PRINT_NXS(36, spd->tfaw_msb, + "tfaw_msb Upper Nibble for tFAW"); + PRINT_NXS(37, spd->tfaw_min, + "tfaw_min tFAW, lsb"); + PRINT_NXS(38, spd->trrds_min, + "trrds_min tRRD_Smin, MTB"); + PRINT_NXS(39, spd->trrdl_min, + "trrdl_min tRRD_Lmin, MTB"); + PRINT_NXS(40, spd->tccdl_min, + "tccdl_min tCCS_Lmin, MTB"); + + printf("%-3d-%3d: ", 41, 59); /* Reserved, General Section */ + for (i = 41; i <= 59; i++) + printf("%02x ", spd->res_41[i - 41]); + + puts("\n"); + printf("%-3d-%3d: ", 60, 77); + for (i = 60; i <= 77; i++) + printf("%02x ", spd->mapping[i - 60]); + puts(" mapping[] Connector to SDRAM bit map\n"); + + PRINT_NXS(117, spd->fine_tccdl_min, + "fine_tccdl_min Fine offset for tCCD_Lmin"); + PRINT_NXS(118, spd->fine_trrdl_min, + "fine_trrdl_min Fine offset for tRRD_Lmin"); + PRINT_NXS(119, spd->fine_trrds_min, + "fine_trrds_min Fine offset for tRRD_Smin"); + PRINT_NXS(120, spd->fine_trc_min, + "fine_trc_min Fine offset for tRCmin"); + PRINT_NXS(121, spd->fine_trp_min, + "fine_trp_min Fine offset for tRPmin"); + PRINT_NXS(122, spd->fine_trcd_min, + "fine_trcd_min Fine offset for tRCDmin"); + PRINT_NXS(123, spd->fine_taa_min, + "fine_taa_min Fine offset for tAAmin"); + PRINT_NXS(124, spd->fine_tck_max, + "fine_tck_max Fine offset for tCKAVGmax"); + PRINT_NXS(125, spd->fine_tck_min, + "fine_tck_min Fine offset for tCKAVGmin"); + + /* CRC: Bytes 126-127 */ + PRINT_NNXXS(126, 127, spd->crc[0], spd->crc[1], " SPD CRC"); + + switch (spd->module_type) { + case 0x02: /* UDIMM */ + case 0x03: /* SO-DIMM */ + PRINT_NXS(128, spd->mod_section.unbuffered.mod_height, + "mod_height (Unbuffered) Module Nominal Height"); + PRINT_NXS(129, spd->mod_section.unbuffered.mod_thickness, + "mod_thickness (Unbuffered) Module Maximum Thickness"); + PRINT_NXS(130, spd->mod_section.unbuffered.ref_raw_card, + "ref_raw_card (Unbuffered) Reference Raw Card Used"); + PRINT_NXS(131, spd->mod_section.unbuffered.addr_mapping, + "addr_mapping (Unbuffered) Address mapping from Edge Connector to DRAM"); + PRINT_NNXXS(254, 255, spd->mod_section.unbuffered.crc[0], + spd->mod_section.unbuffered.crc[1], " Module CRC"); + break; + case 0x01: /* RDIMM */ + PRINT_NXS(128, spd->mod_section.registered.mod_height, + "mod_height (Registered) Module Nominal Height"); + PRINT_NXS(129, spd->mod_section.registered.mod_thickness, + "mod_thickness (Registered) Module Maximum Thickness"); + PRINT_NXS(130, spd->mod_section.registered.ref_raw_card, + "ref_raw_card (Registered) Reference Raw Card Used"); + PRINT_NXS(131, spd->mod_section.registered.modu_attr, + "modu_attr (Registered) DIMM Module Attributes"); + PRINT_NXS(132, spd->mod_section.registered.thermal, + "thermal (Registered) Thermal Heat Spreader Solution"); + PRINT_NXS(133, spd->mod_section.registered.reg_id_lo, + "reg_id_lo (Registered) Register Manufacturer ID Code, LSB"); + PRINT_NXS(134, spd->mod_section.registered.reg_id_hi, + "reg_id_hi (Registered) Register Manufacturer ID Code, MSB"); + PRINT_NXS(135, spd->mod_section.registered.reg_rev, + "reg_rev (Registered) Register Revision Number"); + PRINT_NXS(136, spd->mod_section.registered.reg_map, + "reg_map (Registered) Address mapping"); + PRINT_NNXXS(254, 255, spd->mod_section.registered.crc[0], + spd->mod_section.registered.crc[1], " Module CRC"); + break; + case 0x04: /* LRDIMM */ + PRINT_NXS(128, spd->mod_section.loadreduced.mod_height, + "mod_height (Loadreduced) Module Nominal Height"); + PRINT_NXS(129, spd->mod_section.loadreduced.mod_thickness, + "mod_thickness (Loadreduced) Module Maximum Thickness"); + PRINT_NXS(130, spd->mod_section.loadreduced.ref_raw_card, + "ref_raw_card (Loadreduced) Reference Raw Card Used"); + PRINT_NXS(131, spd->mod_section.loadreduced.modu_attr, + "modu_attr (Loadreduced) DIMM Module Attributes"); + PRINT_NXS(132, spd->mod_section.loadreduced.thermal, + "thermal (Loadreduced) Thermal Heat Spreader Solution"); + PRINT_NXS(133, spd->mod_section.loadreduced.reg_id_lo, + "reg_id_lo (Loadreduced) Register Manufacturer ID Code, LSB"); + PRINT_NXS(134, spd->mod_section.loadreduced.reg_id_hi, + "reg_id_hi (Loadreduced) Register Manufacturer ID Code, MSB"); + PRINT_NXS(135, spd->mod_section.loadreduced.reg_rev, + "reg_rev (Loadreduced) Register Revision Number"); + PRINT_NXS(136, spd->mod_section.loadreduced.reg_map, + "reg_map (Loadreduced) Address mapping"); + PRINT_NXS(137, spd->mod_section.loadreduced.reg_drv, + "reg_drv (Loadreduced) Reg output drive strength"); + PRINT_NXS(138, spd->mod_section.loadreduced.reg_drv_ck, + "reg_drv_ck (Loadreduced) Reg output drive strength for CK"); + PRINT_NXS(139, spd->mod_section.loadreduced.data_buf_rev, + "data_buf_rev (Loadreduced) Data Buffer Revision Numbe"); + PRINT_NXS(140, spd->mod_section.loadreduced.vrefqe_r0, + "vrefqe_r0 (Loadreduced) DRAM VrefDQ for Package Rank 0"); + PRINT_NXS(141, spd->mod_section.loadreduced.vrefqe_r1, + "vrefqe_r1 (Loadreduced) DRAM VrefDQ for Package Rank 1"); + PRINT_NXS(142, spd->mod_section.loadreduced.vrefqe_r2, + "vrefqe_r2 (Loadreduced) DRAM VrefDQ for Package Rank 2"); + PRINT_NXS(143, spd->mod_section.loadreduced.vrefqe_r3, + "vrefqe_r3 (Loadreduced) DRAM VrefDQ for Package Rank 3"); + PRINT_NXS(144, spd->mod_section.loadreduced.data_intf, + "data_intf (Loadreduced) Data Buffer VrefDQ for DRAM Interface"); + PRINT_NXS(145, spd->mod_section.loadreduced.data_drv_1866, + "data_drv_1866 (Loadreduced) Data Buffer MDQ Drive Strength and RTT"); + PRINT_NXS(146, spd->mod_section.loadreduced.data_drv_2400, + "data_drv_2400 (Loadreduced) Data Buffer MDQ Drive Strength and RTT"); + PRINT_NXS(147, spd->mod_section.loadreduced.data_drv_3200, + "data_drv_3200 (Loadreduced) Data Buffer MDQ Drive Strength and RTT"); + PRINT_NXS(148, spd->mod_section.loadreduced.dram_drv, + "dram_drv (Loadreduced) DRAM Drive Strength"); + PRINT_NXS(149, spd->mod_section.loadreduced.dram_odt_1866, + "dram_odt_1866 (Loadreduced) DRAM ODT (RTT_WR, RTT_NOM)"); + PRINT_NXS(150, spd->mod_section.loadreduced.dram_odt_2400, + "dram_odt_2400 (Loadreduced) DRAM ODT (RTT_WR, RTT_NOM)"); + PRINT_NXS(151, spd->mod_section.loadreduced.dram_odt_3200, + "dram_odt_3200 (Loadreduced) DRAM ODT (RTT_WR, RTT_NOM)"); + PRINT_NXS(152, spd->mod_section.loadreduced.dram_odt_park_1866, + "dram_odt_park_1866 (Loadreduced) DRAM ODT (RTT_PARK)"); + PRINT_NXS(153, spd->mod_section.loadreduced.dram_odt_park_2400, + "dram_odt_park_2400 (Loadreduced) DRAM ODT (RTT_PARK)"); + PRINT_NXS(154, spd->mod_section.loadreduced.dram_odt_park_3200, + "dram_odt_park_3200 (Loadreduced) DRAM ODT (RTT_PARK)"); + PRINT_NNXXS(254, 255, spd->mod_section.loadreduced.crc[0], + spd->mod_section.loadreduced.crc[1], + " Module CRC"); + break; + default: + /* Module-specific Section, Unsupported Module Type */ + printf("%-3d-%3d: ", 128, 255); + + for (i = 128; i <= 255; i++) + printf("%02x", spd->mod_section.uc[i - 60]); + + break; + } + + /* Unique Module ID: Bytes 320-383 */ + PRINT_NXS(320, spd->mmid_lsb, "Module MfgID Code LSB - JEP-106"); + PRINT_NXS(321, spd->mmid_msb, "Module MfgID Code MSB - JEP-106"); + PRINT_NXS(322, spd->mloc, "Mfg Location"); + PRINT_NNXXS(323, 324, spd->mdate[0], spd->mdate[1], "Mfg Date"); + + printf("%-3d-%3d: ", 325, 328); + + for (i = 325; i <= 328; i++) + printf("%02x ", spd->sernum[i - 325]); + printf(" Module Serial Number\n"); + + printf("%-3d-%3d: ", 329, 348); + for (i = 329; i <= 348; i++) + printf("%02x ", spd->mpart[i - 329]); + printf(" Mfg's Module Part Number\n"); + + PRINT_NXS(349, spd->mrev, "Module Revision code"); + PRINT_NXS(350, spd->dmid_lsb, "DRAM MfgID Code LSB - JEP-106"); + PRINT_NXS(351, spd->dmid_msb, "DRAM MfgID Code MSB - JEP-106"); + PRINT_NXS(352, spd->stepping, "DRAM stepping"); + + printf("%-3d-%3d: ", 353, 381); + for (i = 353; i <= 381; i++) + printf("%02x ", spd->msd[i - 353]); + printf(" Mfg's Specific Data\n"); +} +#endif + +static inline void generic_spd_dump(const generic_spd_eeprom_t *spd) +{ +#if defined(CONFIG_SYS_FSL_DDR1) + ddr1_spd_dump(spd); +#elif defined(CONFIG_SYS_FSL_DDR2) + ddr2_spd_dump(spd); +#elif defined(CONFIG_SYS_FSL_DDR3) + ddr3_spd_dump(spd); +#elif defined(CONFIG_SYS_FSL_DDR4) + ddr4_spd_dump(spd); +#endif +} + +static void fsl_ddr_printinfo(const fsl_ddr_info_t *pinfo, + unsigned int ctrl_mask, + unsigned int dimm_mask, + unsigned int do_mask) +{ + unsigned int i, j, retval; + + /* STEP 1: DIMM SPD data */ + if (do_mask & STEP_GET_SPD) { + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (!(ctrl_mask & (1 << i))) + continue; + + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + if (!(dimm_mask & (1 << j))) + continue; + + printf("SPD info: Controller=%u " + "DIMM=%u\n", i, j); + generic_spd_dump( + &(pinfo->spd_installed_dimms[i][j])); + printf("\n"); + } + printf("\n"); + } + printf("\n"); + } + + /* STEP 2: DIMM Parameters */ + if (do_mask & STEP_COMPUTE_DIMM_PARMS) { + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (!(ctrl_mask & (1 << i))) + continue; + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + if (!(dimm_mask & (1 << j))) + continue; + printf("DIMM parameters: Controller=%u " + "DIMM=%u\n", i, j); + print_dimm_parameters( + &(pinfo->dimm_params[i][j])); + printf("\n"); + } + printf("\n"); + } + printf("\n"); + } + + /* STEP 3: Common Parameters */ + if (do_mask & STEP_COMPUTE_COMMON_PARMS) { + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (!(ctrl_mask & (1 << i))) + continue; + printf("\"lowest common\" DIMM parameters: " + "Controller=%u\n", i); + print_lowest_common_dimm_parameters( + &pinfo->common_timing_params[i]); + printf("\n"); + } + printf("\n"); + } + + /* STEP 4: User Configuration Options */ + if (do_mask & STEP_GATHER_OPTS) { + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (!(ctrl_mask & (1 << i))) + continue; + printf("User Config Options: Controller=%u\n", i); + print_memctl_options(&pinfo->memctl_opts[i]); + printf("\n"); + } + printf("\n"); + } + + /* STEP 5: Address assignment */ + if (do_mask & STEP_ASSIGN_ADDRESSES) { + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (!(ctrl_mask & (1 << i))) + continue; + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + printf("Address Assignment: Controller=%u " + "DIMM=%u\n", i, j); + printf("Don't have this functionality yet\n"); + } + printf("\n"); + } + printf("\n"); + } + + /* STEP 6: computed controller register values */ + if (do_mask & STEP_COMPUTE_REGS) { + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (!(ctrl_mask & (1 << i))) + continue; + printf("Computed Register Values: Controller=%u\n", i); + print_fsl_memctl_config_regs( + &pinfo->fsl_ddr_config_reg[i]); + retval = check_fsl_memctl_config_regs( + &pinfo->fsl_ddr_config_reg[i]); + if (retval) { + printf("check_fsl_memctl_config_regs " + "result = %u\n", retval); + } + printf("\n"); + } + printf("\n"); + } +} + +struct data_strings { + const char *data_name; + unsigned int step_mask; + unsigned int dimm_number_required; +}; + +#define DATA_OPTIONS(name, step, dimm) {#name, step, dimm} + +static unsigned int fsl_ddr_parse_interactive_cmd( + char **argv, + int argc, + unsigned int *pstep_mask, + unsigned int *pctlr_mask, + unsigned int *pdimm_mask, + unsigned int *pdimm_number_required + ) { + + static const struct data_strings options[] = { + DATA_OPTIONS(spd, STEP_GET_SPD, 1), + DATA_OPTIONS(dimmparms, STEP_COMPUTE_DIMM_PARMS, 1), + DATA_OPTIONS(commonparms, STEP_COMPUTE_COMMON_PARMS, 0), + DATA_OPTIONS(opts, STEP_GATHER_OPTS, 0), + DATA_OPTIONS(addresses, STEP_ASSIGN_ADDRESSES, 0), + DATA_OPTIONS(regs, STEP_COMPUTE_REGS, 0), + }; + static const unsigned int n_opts = ARRAY_SIZE(options); + + unsigned int i, j; + unsigned int error = 0; + + for (i = 1; i < argc; i++) { + unsigned int matched = 0; + + for (j = 0; j < n_opts; j++) { + if (strcmp(options[j].data_name, argv[i]) != 0) + continue; + *pstep_mask |= options[j].step_mask; + *pdimm_number_required = + options[j].dimm_number_required; + matched = 1; + break; + } + + if (matched) + continue; + + if (argv[i][0] == 'c') { + char c = argv[i][1]; + if (isdigit(c)) + *pctlr_mask |= 1 << (c - '0'); + continue; + } + + if (argv[i][0] == 'd') { + char c = argv[i][1]; + if (isdigit(c)) + *pdimm_mask |= 1 << (c - '0'); + continue; + } + + printf("unknown arg %s\n", argv[i]); + *pstep_mask = 0; + error = 1; + break; + } + + return error; +} + +int fsl_ddr_interactive_env_var_exists(void) +{ + char buffer[CONFIG_SYS_CBSIZE]; + + if (getenv_f("ddr_interactive", buffer, CONFIG_SYS_CBSIZE) >= 0) + return 1; + + return 0; +} + +unsigned long long fsl_ddr_interactive(fsl_ddr_info_t *pinfo, int var_is_set) +{ + unsigned long long ddrsize; + const char *prompt = "FSL DDR>"; + char buffer[CONFIG_SYS_CBSIZE]; + char buffer2[CONFIG_SYS_CBSIZE]; + char *p = NULL; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + int argc; + unsigned int next_step = STEP_GET_SPD; + const char *usage = { + "commands:\n" + "print print SPD and intermediate computed data\n" + "reset reboot machine\n" + "recompute reload SPD and options to default and recompute regs\n" + "edit modify spd, parameter, or option\n" + "compute recompute registers from current next_step to end\n" + "copy copy parameters\n" + "next_step shows current next_step\n" + "help this message\n" + "go program the memory controller and continue with u-boot\n" + }; + + if (var_is_set) { + if (getenv_f("ddr_interactive", buffer2, CONFIG_SYS_CBSIZE) > 0) { + p = buffer2; + } else { + var_is_set = 0; + } + } + + /* + * The strategy for next_step is that it points to the next + * step in the computation process that needs to be done. + */ + while (1) { + if (var_is_set) { + char *pend = strchr(p, ';'); + if (pend) { + /* found command separator, copy sub-command */ + *pend = '\0'; + strcpy(buffer, p); + p = pend + 1; + } else { + /* separator not found, copy whole string */ + strcpy(buffer, p); + p = NULL; + var_is_set = 0; + } + } else { + /* + * No need to worry for buffer overflow here in + * this function; readline() maxes out at CFG_CBSIZE + */ + readline_into_buffer(prompt, buffer, 0); + } + argc = parse_line(buffer, argv); + if (argc == 0) + continue; + + + if (strcmp(argv[0], "help") == 0) { + puts(usage); + continue; + } + + if (strcmp(argv[0], "next_step") == 0) { + printf("next_step = 0x%02X (%s)\n", + next_step, + step_to_string(next_step)); + continue; + } + + if (strcmp(argv[0], "copy") == 0) { + unsigned int error = 0; + unsigned int step_mask = 0; + unsigned int src_ctlr_mask = 0; + unsigned int src_dimm_mask = 0; + unsigned int dimm_number_required = 0; + unsigned int src_ctlr_num = 0; + unsigned int src_dimm_num = 0; + unsigned int dst_ctlr_num = -1; + unsigned int dst_dimm_num = -1; + unsigned int i, num_dest_parms; + + if (argc == 1) { + printf("copy \n"); + continue; + } + + error = fsl_ddr_parse_interactive_cmd( + argv, argc, + &step_mask, + &src_ctlr_mask, + &src_dimm_mask, + &dimm_number_required + ); + + /* XXX: only dimm_number_required and step_mask will + be used by this function. Parse the controller and + DIMM number separately because it is easier. */ + + if (error) + continue; + + /* parse source destination controller / DIMM */ + + num_dest_parms = dimm_number_required ? 2 : 1; + + for (i = 0; i < argc; i++) { + if (argv[i][0] == 'c') { + char c = argv[i][1]; + if (isdigit(c)) { + src_ctlr_num = (c - '0'); + break; + } + } + } + + for (i = 0; i < argc; i++) { + if (argv[i][0] == 'd') { + char c = argv[i][1]; + if (isdigit(c)) { + src_dimm_num = (c - '0'); + break; + } + } + } + + /* parse destination controller / DIMM */ + + for (i = argc - 1; i >= argc - num_dest_parms; i--) { + if (argv[i][0] == 'c') { + char c = argv[i][1]; + if (isdigit(c)) { + dst_ctlr_num = (c - '0'); + break; + } + } + } + + for (i = argc - 1; i >= argc - num_dest_parms; i--) { + if (argv[i][0] == 'd') { + char c = argv[i][1]; + if (isdigit(c)) { + dst_dimm_num = (c - '0'); + break; + } + } + } + + /* TODO: validate inputs */ + + debug("src_ctlr_num = %u, src_dimm_num = %u, dst_ctlr_num = %u, dst_dimm_num = %u, step_mask = %x\n", + src_ctlr_num, src_dimm_num, dst_ctlr_num, dst_dimm_num, step_mask); + + + switch (step_mask) { + + case STEP_GET_SPD: + memcpy(&(pinfo->spd_installed_dimms[dst_ctlr_num][dst_dimm_num]), + &(pinfo->spd_installed_dimms[src_ctlr_num][src_dimm_num]), + sizeof(pinfo->spd_installed_dimms[0][0])); + break; + + case STEP_COMPUTE_DIMM_PARMS: + memcpy(&(pinfo->dimm_params[dst_ctlr_num][dst_dimm_num]), + &(pinfo->dimm_params[src_ctlr_num][src_dimm_num]), + sizeof(pinfo->dimm_params[0][0])); + break; + + case STEP_COMPUTE_COMMON_PARMS: + memcpy(&(pinfo->common_timing_params[dst_ctlr_num]), + &(pinfo->common_timing_params[src_ctlr_num]), + sizeof(pinfo->common_timing_params[0])); + break; + + case STEP_GATHER_OPTS: + memcpy(&(pinfo->memctl_opts[dst_ctlr_num]), + &(pinfo->memctl_opts[src_ctlr_num]), + sizeof(pinfo->memctl_opts[0])); + break; + + /* someday be able to have addresses to copy addresses... */ + + case STEP_COMPUTE_REGS: + memcpy(&(pinfo->fsl_ddr_config_reg[dst_ctlr_num]), + &(pinfo->fsl_ddr_config_reg[src_ctlr_num]), + sizeof(pinfo->memctl_opts[0])); + break; + + default: + printf("unexpected step_mask value\n"); + } + + continue; + + } + + if (strcmp(argv[0], "edit") == 0) { + unsigned int error = 0; + unsigned int step_mask = 0; + unsigned int ctlr_mask = 0; + unsigned int dimm_mask = 0; + char *p_element = NULL; + char *p_value = NULL; + unsigned int dimm_number_required = 0; + unsigned int ctrl_num; + unsigned int dimm_num; + + if (argc == 1) { + /* Only the element and value must be last */ + printf("edit " + " \n"); + printf("for spd, specify byte number for " + "element\n"); + continue; + } + + error = fsl_ddr_parse_interactive_cmd( + argv, argc - 2, + &step_mask, + &ctlr_mask, + &dimm_mask, + &dimm_number_required + ); + + if (error) + continue; + + + /* Check arguments */ + + /* ERROR: If no steps were found */ + if (step_mask == 0) { + printf("Error: No valid steps were specified " + "in argument.\n"); + continue; + } + + /* ERROR: If multiple steps were found */ + if (step_mask & (step_mask - 1)) { + printf("Error: Multiple steps specified in " + "argument.\n"); + continue; + } + + /* ERROR: Controller not specified */ + if (ctlr_mask == 0) { + printf("Error: controller number not " + "specified or no element and " + "value specified\n"); + continue; + } + + if (ctlr_mask & (ctlr_mask - 1)) { + printf("Error: multiple controllers " + "specified, %X\n", ctlr_mask); + continue; + } + + /* ERROR: DIMM number not specified */ + if (dimm_number_required && dimm_mask == 0) { + printf("Error: DIMM number number not " + "specified or no element and " + "value specified\n"); + continue; + } + + if (dimm_mask & (dimm_mask - 1)) { + printf("Error: multipled DIMMs specified\n"); + continue; + } + + p_element = argv[argc - 2]; + p_value = argv[argc - 1]; + + ctrl_num = __ilog2(ctlr_mask); + dimm_num = __ilog2(dimm_mask); + + switch (step_mask) { + case STEP_GET_SPD: + { + unsigned int element_num; + unsigned int value; + + element_num = simple_strtoul(p_element, + NULL, 0); + value = simple_strtoul(p_value, + NULL, 0); + fsl_ddr_spd_edit(pinfo, + ctrl_num, + dimm_num, + element_num, + value); + next_step = STEP_COMPUTE_DIMM_PARMS; + } + break; + + case STEP_COMPUTE_DIMM_PARMS: + fsl_ddr_dimm_parameters_edit( + pinfo, ctrl_num, dimm_num, + p_element, p_value); + next_step = STEP_COMPUTE_COMMON_PARMS; + break; + + case STEP_COMPUTE_COMMON_PARMS: + lowest_common_dimm_parameters_edit(pinfo, + ctrl_num, p_element, p_value); + next_step = STEP_GATHER_OPTS; + break; + + case STEP_GATHER_OPTS: + fsl_ddr_options_edit(pinfo, ctrl_num, + p_element, p_value); + next_step = STEP_ASSIGN_ADDRESSES; + break; + + case STEP_ASSIGN_ADDRESSES: + printf("editing of address assignment " + "not yet implemented\n"); + break; + + case STEP_COMPUTE_REGS: + { + fsl_ddr_regs_edit(pinfo, + ctrl_num, + p_element, + p_value); + next_step = STEP_PROGRAM_REGS; + } + break; + + default: + printf("programming error\n"); + while (1) + ; + break; + } + continue; + } + + if (strcmp(argv[0], "reset") == 0) { + /* + * Reboot machine. + * Args don't seem to matter because this + * doesn't return + */ + do_reset(NULL, 0, 0, NULL); + printf("Reset didn't work\n"); + } + + if (strcmp(argv[0], "recompute") == 0) { + /* + * Recalculate everything, starting with + * loading SPD EEPROM from DIMMs + */ + next_step = STEP_GET_SPD; + ddrsize = fsl_ddr_compute(pinfo, next_step, 0); + continue; + } + + if (strcmp(argv[0], "compute") == 0) { + /* + * Compute rest of steps starting at + * the current next_step/ + */ + ddrsize = fsl_ddr_compute(pinfo, next_step, 0); + continue; + } + + if (strcmp(argv[0], "print") == 0) { + unsigned int error = 0; + unsigned int step_mask = 0; + unsigned int ctlr_mask = 0; + unsigned int dimm_mask = 0; + unsigned int dimm_number_required = 0; + + if (argc == 1) { + printf("print [c] [d] [spd] [dimmparms] " + "[commonparms] [opts] [addresses] [regs]\n"); + continue; + } + + error = fsl_ddr_parse_interactive_cmd( + argv, argc, + &step_mask, + &ctlr_mask, + &dimm_mask, + &dimm_number_required + ); + + if (error) + continue; + + /* If no particular controller was found, print all */ + if (ctlr_mask == 0) + ctlr_mask = 0xFF; + + /* If no particular dimm was found, print all dimms. */ + if (dimm_mask == 0) + dimm_mask = 0xFF; + + /* If no steps were found, print all steps. */ + if (step_mask == 0) + step_mask = STEP_ALL; + + fsl_ddr_printinfo(pinfo, ctlr_mask, + dimm_mask, step_mask); + continue; + } + + if (strcmp(argv[0], "go") == 0) { + if (next_step) + ddrsize = fsl_ddr_compute(pinfo, next_step, 0); + break; + } + + printf("unknown command %s\n", argv[0]); + } + + debug("end of memory = %llu\n", (u64)ddrsize); + + return ddrsize; +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/lc_common_dimm_params.c b/qemu/roms/u-boot/drivers/ddr/fsl/lc_common_dimm_params.c new file mode 100644 index 000000000..05a24dd6e --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/lc_common_dimm_params.c @@ -0,0 +1,567 @@ +/* + * Copyright 2008-2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include +#include + +#include + +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) +static unsigned int +compute_cas_latency(const dimm_params_t *dimm_params, + common_timing_params_t *outpdimm, + unsigned int number_of_dimms) +{ + unsigned int i; + unsigned int common_caslat; + unsigned int caslat_actual; + unsigned int retry = 16; + unsigned int tmp; + const unsigned int mclk_ps = get_memory_clk_period_ps(); +#ifdef CONFIG_SYS_FSL_DDR3 + const unsigned int taamax = 20000; +#else + const unsigned int taamax = 18000; +#endif + + /* compute the common CAS latency supported between slots */ + tmp = dimm_params[0].caslat_x; + for (i = 1; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) + tmp &= dimm_params[i].caslat_x; + } + common_caslat = tmp; + + /* validate if the memory clk is in the range of dimms */ + if (mclk_ps < outpdimm->tckmin_x_ps) { + printf("DDR clock (MCLK cycle %u ps) is faster than " + "the slowest DIMM(s) (tCKmin %u ps) can support.\n", + mclk_ps, outpdimm->tckmin_x_ps); + } +#ifdef CONFIG_SYS_FSL_DDR4 + if (mclk_ps > outpdimm->tckmax_ps) { + printf("DDR clock (MCLK cycle %u ps) is slower than DIMM(s) (tCKmax %u ps) can support.\n", + mclk_ps, outpdimm->tckmax_ps); + } +#endif + /* determine the acutal cas latency */ + caslat_actual = (outpdimm->taamin_ps + mclk_ps - 1) / mclk_ps; + /* check if the dimms support the CAS latency */ + while (!(common_caslat & (1 << caslat_actual)) && retry > 0) { + caslat_actual++; + retry--; + } + /* once the caculation of caslat_actual is completed + * we must verify that this CAS latency value does not + * exceed tAAmax, which is 20 ns for all DDR3 speed grades, + * 18ns for all DDR4 speed grades. + */ + if (caslat_actual * mclk_ps > taamax) { + printf("The choosen cas latency %d is too large\n", + caslat_actual); + } + outpdimm->lowest_common_spd_caslat = caslat_actual; + debug("lowest_common_spd_caslat is 0x%x\n", caslat_actual); + + return 0; +} +#else /* for DDR1 and DDR2 */ +static unsigned int +compute_cas_latency(const dimm_params_t *dimm_params, + common_timing_params_t *outpdimm, + unsigned int number_of_dimms) +{ + int i; + const unsigned int mclk_ps = get_memory_clk_period_ps(); + unsigned int lowest_good_caslat; + unsigned int not_ok; + unsigned int temp1, temp2; + + debug("using mclk_ps = %u\n", mclk_ps); + if (mclk_ps > outpdimm->tckmax_ps) { + printf("Warning: DDR clock (%u ps) is slower than DIMM(s) (tCKmax %u ps)\n", + mclk_ps, outpdimm->tckmax_ps); + } + + /* + * Compute a CAS latency suitable for all DIMMs + * + * Strategy for SPD-defined latencies: compute only + * CAS latency defined by all DIMMs. + */ + + /* + * Step 1: find CAS latency common to all DIMMs using bitwise + * operation. + */ + temp1 = 0xFF; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) { + temp2 = 0; + temp2 |= 1 << dimm_params[i].caslat_x; + temp2 |= 1 << dimm_params[i].caslat_x_minus_1; + temp2 |= 1 << dimm_params[i].caslat_x_minus_2; + /* + * If there was no entry for X-2 (X-1) in + * the SPD, then caslat_x_minus_2 + * (caslat_x_minus_1) contains either 255 or + * 0xFFFFFFFF because that's what the glorious + * __ilog2 function returns for an input of 0. + * On 32-bit PowerPC, left shift counts with bit + * 26 set (that the value of 255 or 0xFFFFFFFF + * will have), cause the destination register to + * be 0. That is why this works. + */ + temp1 &= temp2; + } + } + + /* + * Step 2: check each common CAS latency against tCK of each + * DIMM's SPD. + */ + lowest_good_caslat = 0; + temp2 = 0; + while (temp1) { + not_ok = 0; + temp2 = __ilog2(temp1); + debug("checking common caslat = %u\n", temp2); + + /* Check if this CAS latency will work on all DIMMs at tCK. */ + for (i = 0; i < number_of_dimms; i++) { + if (!dimm_params[i].n_ranks) + continue; + + if (dimm_params[i].caslat_x == temp2) { + if (mclk_ps >= dimm_params[i].tckmin_x_ps) { + debug("CL = %u ok on DIMM %u at tCK=%u ps with tCKmin_X_ps of %u\n", + temp2, i, mclk_ps, + dimm_params[i].tckmin_x_ps); + continue; + } else { + not_ok++; + } + } + + if (dimm_params[i].caslat_x_minus_1 == temp2) { + unsigned int tckmin_x_minus_1_ps + = dimm_params[i].tckmin_x_minus_1_ps; + if (mclk_ps >= tckmin_x_minus_1_ps) { + debug("CL = %u ok on DIMM %u at tCK=%u ps with tckmin_x_minus_1_ps of %u\n", + temp2, i, mclk_ps, + tckmin_x_minus_1_ps); + continue; + } else { + not_ok++; + } + } + + if (dimm_params[i].caslat_x_minus_2 == temp2) { + unsigned int tckmin_x_minus_2_ps + = dimm_params[i].tckmin_x_minus_2_ps; + if (mclk_ps >= tckmin_x_minus_2_ps) { + debug("CL = %u ok on DIMM %u at tCK=%u ps with tckmin_x_minus_2_ps of %u\n", + temp2, i, mclk_ps, + tckmin_x_minus_2_ps); + continue; + } else { + not_ok++; + } + } + } + + if (!not_ok) + lowest_good_caslat = temp2; + + temp1 &= ~(1 << temp2); + } + + debug("lowest common SPD-defined CAS latency = %u\n", + lowest_good_caslat); + outpdimm->lowest_common_spd_caslat = lowest_good_caslat; + + + /* + * Compute a common 'de-rated' CAS latency. + * + * The strategy here is to find the *highest* dereated cas latency + * with the assumption that all of the DIMMs will support a dereated + * CAS latency higher than or equal to their lowest dereated value. + */ + temp1 = 0; + for (i = 0; i < number_of_dimms; i++) + temp1 = max(temp1, dimm_params[i].caslat_lowest_derated); + + outpdimm->highest_common_derated_caslat = temp1; + debug("highest common dereated CAS latency = %u\n", temp1); + + return 0; +} +#endif + +/* + * compute_lowest_common_dimm_parameters() + * + * Determine the worst-case DIMM timing parameters from the set of DIMMs + * whose parameters have been computed into the array pointed to + * by dimm_params. + */ +unsigned int +compute_lowest_common_dimm_parameters(const dimm_params_t *dimm_params, + common_timing_params_t *outpdimm, + const unsigned int number_of_dimms) +{ + unsigned int i, j; + + unsigned int tckmin_x_ps = 0; + unsigned int tckmax_ps = 0xFFFFFFFF; + unsigned int trcd_ps = 0; + unsigned int trp_ps = 0; + unsigned int tras_ps = 0; +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + unsigned int taamin_ps = 0; +#endif +#ifdef CONFIG_SYS_FSL_DDR4 + unsigned int twr_ps = 15000; + unsigned int trfc1_ps = 0; + unsigned int trfc2_ps = 0; + unsigned int trfc4_ps = 0; + unsigned int trrds_ps = 0; + unsigned int trrdl_ps = 0; + unsigned int tccdl_ps = 0; +#else + unsigned int twr_ps = 0; + unsigned int twtr_ps = 0; + unsigned int trfc_ps = 0; + unsigned int trrd_ps = 0; + unsigned int trtp_ps = 0; +#endif + unsigned int trc_ps = 0; + unsigned int refresh_rate_ps = 0; + unsigned int extended_op_srt = 1; +#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2) + unsigned int tis_ps = 0; + unsigned int tih_ps = 0; + unsigned int tds_ps = 0; + unsigned int tdh_ps = 0; + unsigned int tdqsq_max_ps = 0; + unsigned int tqhs_ps = 0; +#endif + unsigned int temp1, temp2; + unsigned int additive_latency = 0; + + temp1 = 0; + for (i = 0; i < number_of_dimms; i++) { + /* + * If there are no ranks on this DIMM, + * it probably doesn't exist, so skip it. + */ + if (dimm_params[i].n_ranks == 0) { + temp1++; + continue; + } + if (dimm_params[i].n_ranks == 4 && i != 0) { + printf("Found Quad-rank DIMM in wrong bank, ignored." + " Software may not run as expected.\n"); + temp1++; + continue; + } + + /* + * check if quad-rank DIMM is plugged if + * CONFIG_CHIP_SELECT_QUAD_CAPABLE is not defined + * Only the board with proper design is capable + */ +#ifndef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE + if (dimm_params[i].n_ranks == 4 && \ + CONFIG_CHIP_SELECTS_PER_CTRL/CONFIG_DIMM_SLOTS_PER_CTLR < 4) { + printf("Found Quad-rank DIMM, not able to support."); + temp1++; + continue; + } +#endif + /* + * Find minimum tckmax_ps to find fastest slow speed, + * i.e., this is the slowest the whole system can go. + */ + tckmax_ps = min(tckmax_ps, dimm_params[i].tckmax_ps); +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + taamin_ps = max(taamin_ps, dimm_params[i].taa_ps); +#endif + tckmin_x_ps = max(tckmin_x_ps, dimm_params[i].tckmin_x_ps); + trcd_ps = max(trcd_ps, dimm_params[i].trcd_ps); + trp_ps = max(trp_ps, dimm_params[i].trp_ps); + tras_ps = max(tras_ps, dimm_params[i].tras_ps); +#ifdef CONFIG_SYS_FSL_DDR4 + trfc1_ps = max(trfc1_ps, dimm_params[i].trfc1_ps); + trfc2_ps = max(trfc2_ps, dimm_params[i].trfc2_ps); + trfc4_ps = max(trfc4_ps, dimm_params[i].trfc4_ps); + trrds_ps = max(trrds_ps, dimm_params[i].trrds_ps); + trrdl_ps = max(trrdl_ps, dimm_params[i].trrdl_ps); + tccdl_ps = max(tccdl_ps, dimm_params[i].tccdl_ps); +#else + twr_ps = max(twr_ps, dimm_params[i].twr_ps); + twtr_ps = max(twtr_ps, dimm_params[i].twtr_ps); + trfc_ps = max(trfc_ps, dimm_params[i].trfc_ps); + trrd_ps = max(trrd_ps, dimm_params[i].trrd_ps); + trtp_ps = max(trtp_ps, dimm_params[i].trtp_ps); +#endif + trc_ps = max(trc_ps, dimm_params[i].trc_ps); +#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2) + tis_ps = max(tis_ps, dimm_params[i].tis_ps); + tih_ps = max(tih_ps, dimm_params[i].tih_ps); + tds_ps = max(tds_ps, dimm_params[i].tds_ps); + tdh_ps = max(tdh_ps, dimm_params[i].tdh_ps); + tqhs_ps = max(tqhs_ps, dimm_params[i].tqhs_ps); + /* + * Find maximum tdqsq_max_ps to find slowest. + * + * FIXME: is finding the slowest value the correct + * strategy for this parameter? + */ + tdqsq_max_ps = max(tdqsq_max_ps, dimm_params[i].tdqsq_max_ps); +#endif + refresh_rate_ps = max(refresh_rate_ps, + dimm_params[i].refresh_rate_ps); + /* extended_op_srt is either 0 or 1, 0 having priority */ + extended_op_srt = min(extended_op_srt, + dimm_params[i].extended_op_srt); + } + + outpdimm->ndimms_present = number_of_dimms - temp1; + + if (temp1 == number_of_dimms) { + debug("no dimms this memory controller\n"); + return 0; + } + + outpdimm->tckmin_x_ps = tckmin_x_ps; + outpdimm->tckmax_ps = tckmax_ps; +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + outpdimm->taamin_ps = taamin_ps; +#endif + outpdimm->trcd_ps = trcd_ps; + outpdimm->trp_ps = trp_ps; + outpdimm->tras_ps = tras_ps; +#ifdef CONFIG_SYS_FSL_DDR4 + outpdimm->trfc1_ps = trfc1_ps; + outpdimm->trfc2_ps = trfc2_ps; + outpdimm->trfc4_ps = trfc4_ps; + outpdimm->trrds_ps = trrds_ps; + outpdimm->trrdl_ps = trrdl_ps; + outpdimm->tccdl_ps = tccdl_ps; +#else + outpdimm->twtr_ps = twtr_ps; + outpdimm->trfc_ps = trfc_ps; + outpdimm->trrd_ps = trrd_ps; + outpdimm->trtp_ps = trtp_ps; +#endif + outpdimm->twr_ps = twr_ps; + outpdimm->trc_ps = trc_ps; + outpdimm->refresh_rate_ps = refresh_rate_ps; + outpdimm->extended_op_srt = extended_op_srt; +#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2) + outpdimm->tis_ps = tis_ps; + outpdimm->tih_ps = tih_ps; + outpdimm->tds_ps = tds_ps; + outpdimm->tdh_ps = tdh_ps; + outpdimm->tdqsq_max_ps = tdqsq_max_ps; + outpdimm->tqhs_ps = tqhs_ps; +#endif + + /* Determine common burst length for all DIMMs. */ + temp1 = 0xff; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) { + temp1 &= dimm_params[i].burst_lengths_bitmask; + } + } + outpdimm->all_dimms_burst_lengths_bitmask = temp1; + + /* Determine if all DIMMs registered buffered. */ + temp1 = temp2 = 0; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) { + if (dimm_params[i].registered_dimm) { + temp1 = 1; +#ifndef CONFIG_SPL_BUILD + printf("Detected RDIMM %s\n", + dimm_params[i].mpart); +#endif + } else { + temp2 = 1; +#ifndef CONFIG_SPL_BUILD + printf("Detected UDIMM %s\n", + dimm_params[i].mpart); +#endif + } + } + } + + outpdimm->all_dimms_registered = 0; + outpdimm->all_dimms_unbuffered = 0; + if (temp1 && !temp2) { + outpdimm->all_dimms_registered = 1; + } else if (!temp1 && temp2) { + outpdimm->all_dimms_unbuffered = 1; + } else { + printf("ERROR: Mix of registered buffered and unbuffered " + "DIMMs detected!\n"); + } + + temp1 = 0; + if (outpdimm->all_dimms_registered) + for (j = 0; j < 16; j++) { + outpdimm->rcw[j] = dimm_params[0].rcw[j]; + for (i = 1; i < number_of_dimms; i++) { + if (!dimm_params[i].n_ranks) + continue; + if (dimm_params[i].rcw[j] != dimm_params[0].rcw[j]) { + temp1 = 1; + break; + } + } + } + + if (temp1 != 0) + printf("ERROR: Mix different RDIMM detected!\n"); + + /* calculate cas latency for all DDR types */ + if (compute_cas_latency(dimm_params, outpdimm, number_of_dimms)) + return 1; + + /* Determine if all DIMMs ECC capable. */ + temp1 = 1; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks && + !(dimm_params[i].edc_config & EDC_ECC)) { + temp1 = 0; + break; + } + } + if (temp1) { + debug("all DIMMs ECC capable\n"); + } else { + debug("Warning: not all DIMMs ECC capable, cant enable ECC\n"); + } + outpdimm->all_dimms_ecc_capable = temp1; + + /* + * Compute additive latency. + * + * For DDR1, additive latency should be 0. + * + * For DDR2, with ODT enabled, use "a value" less than ACTTORW, + * which comes from Trcd, and also note that: + * add_lat + caslat must be >= 4 + * + * For DDR3, we use the AL=0 + * + * When to use additive latency for DDR2: + * + * I. Because you are using CL=3 and need to do ODT on writes and + * want functionality. + * 1. Are you going to use ODT? (Does your board not have + * additional termination circuitry for DQ, DQS, DQS_, + * DM, RDQS, RDQS_ for x4/x8 configs?) + * 2. If so, is your lowest supported CL going to be 3? + * 3. If so, then you must set AL=1 because + * + * WL >= 3 for ODT on writes + * RL = AL + CL + * WL = RL - 1 + * -> + * WL = AL + CL - 1 + * AL + CL - 1 >= 3 + * AL + CL >= 4 + * QED + * + * RL >= 3 for ODT on reads + * RL = AL + CL + * + * Since CL aren't usually less than 2, AL=0 is a minimum, + * so the WL-derived AL should be the -- FIXME? + * + * II. Because you are using auto-precharge globally and want to + * use additive latency (posted CAS) to get more bandwidth. + * 1. Are you going to use auto-precharge mode globally? + * + * Use addtivie latency and compute AL to be 1 cycle less than + * tRCD, i.e. the READ or WRITE command is in the cycle + * immediately following the ACTIVATE command.. + * + * III. Because you feel like it or want to do some sort of + * degraded-performance experiment. + * 1. Do you just want to use additive latency because you feel + * like it? + * + * Validation: AL is less than tRCD, and within the other + * read-to-precharge constraints. + */ + + additive_latency = 0; + +#if defined(CONFIG_SYS_FSL_DDR2) + if ((outpdimm->lowest_common_spd_caslat < 4) && + (picos_to_mclk(trcd_ps) > outpdimm->lowest_common_spd_caslat)) { + additive_latency = picos_to_mclk(trcd_ps) - + outpdimm->lowest_common_spd_caslat; + if (mclk_to_picos(additive_latency) > trcd_ps) { + additive_latency = picos_to_mclk(trcd_ps); + debug("setting additive_latency to %u because it was " + " greater than tRCD_ps\n", additive_latency); + } + } +#endif + + /* + * Validate additive latency + * + * AL <= tRCD(min) + */ + if (mclk_to_picos(additive_latency) > trcd_ps) { + printf("Error: invalid additive latency exceeds tRCD(min).\n"); + return 1; + } + + /* + * RL = CL + AL; RL >= 3 for ODT_RD_CFG to be enabled + * WL = RL - 1; WL >= 3 for ODT_WL_CFG to be enabled + * ADD_LAT (the register) must be set to a value less + * than ACTTORW if WL = 1, then AL must be set to 1 + * RD_TO_PRE (the register) must be set to a minimum + * tRTP + AL if AL is nonzero + */ + + /* + * Additive latency will be applied only if the memctl option to + * use it. + */ + outpdimm->additive_latency = additive_latency; + + debug("tCKmin_ps = %u\n", outpdimm->tckmin_x_ps); + debug("trcd_ps = %u\n", outpdimm->trcd_ps); + debug("trp_ps = %u\n", outpdimm->trp_ps); + debug("tras_ps = %u\n", outpdimm->tras_ps); +#ifdef CONFIG_SYS_FSL_DDR4 + debug("trfc1_ps = %u\n", trfc1_ps); + debug("trfc2_ps = %u\n", trfc2_ps); + debug("trfc4_ps = %u\n", trfc4_ps); + debug("trrds_ps = %u\n", trrds_ps); + debug("trrdl_ps = %u\n", trrdl_ps); + debug("tccdl_ps = %u\n", tccdl_ps); +#else + debug("twtr_ps = %u\n", outpdimm->twtr_ps); + debug("trfc_ps = %u\n", outpdimm->trfc_ps); + debug("trrd_ps = %u\n", outpdimm->trrd_ps); +#endif + debug("twr_ps = %u\n", outpdimm->twr_ps); + debug("trc_ps = %u\n", outpdimm->trc_ps); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/main.c b/qemu/roms/u-boot/drivers/ddr/fsl/main.c new file mode 100644 index 000000000..5e001fcb9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/main.c @@ -0,0 +1,766 @@ +/* + * Copyright 2008-2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +/* + * Generic driver for Freescale DDR/DDR2/DDR3 memory controller. + * Based on code from spd_sdram.c + * Author: James Yang [at freescale.com] + */ + +#include +#include +#include +#include + +/* + * CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY is the physical address from the view + * of DDR controllers. It is the same as CONFIG_SYS_DDR_SDRAM_BASE for + * all Power SoCs. But it could be different for ARM SoCs. For example, + * fsl_lsch3 has a mapping mechanism to map DDR memory to ranges (in order) of + * 0x00_8000_0000 ~ 0x00_ffff_ffff + * 0x80_8000_0000 ~ 0xff_ffff_ffff + */ +#ifndef CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY +#define CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY CONFIG_SYS_DDR_SDRAM_BASE +#endif + +#ifdef CONFIG_PPC +#include + +void fsl_ddr_set_lawbar( + const common_timing_params_t *memctl_common_params, + unsigned int memctl_interleaved, + unsigned int ctrl_num); +#endif + +void fsl_ddr_set_intl3r(const unsigned int granule_size); +#if defined(SPD_EEPROM_ADDRESS) || \ + defined(SPD_EEPROM_ADDRESS1) || defined(SPD_EEPROM_ADDRESS2) || \ + defined(SPD_EEPROM_ADDRESS3) || defined(SPD_EEPROM_ADDRESS4) +#if (CONFIG_NUM_DDR_CONTROLLERS == 1) && (CONFIG_DIMM_SLOTS_PER_CTLR == 1) +u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = { + [0][0] = SPD_EEPROM_ADDRESS, +}; +#elif (CONFIG_NUM_DDR_CONTROLLERS == 1) && (CONFIG_DIMM_SLOTS_PER_CTLR == 2) +u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = { + [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */ + [0][1] = SPD_EEPROM_ADDRESS2, /* controller 1 */ +}; +#elif (CONFIG_NUM_DDR_CONTROLLERS == 2) && (CONFIG_DIMM_SLOTS_PER_CTLR == 1) +u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = { + [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */ + [1][0] = SPD_EEPROM_ADDRESS2, /* controller 2 */ +}; +#elif (CONFIG_NUM_DDR_CONTROLLERS == 2) && (CONFIG_DIMM_SLOTS_PER_CTLR == 2) +u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = { + [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */ + [0][1] = SPD_EEPROM_ADDRESS2, /* controller 1 */ + [1][0] = SPD_EEPROM_ADDRESS3, /* controller 2 */ + [1][1] = SPD_EEPROM_ADDRESS4, /* controller 2 */ +}; +#elif (CONFIG_NUM_DDR_CONTROLLERS == 3) && (CONFIG_DIMM_SLOTS_PER_CTLR == 1) +u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = { + [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */ + [1][0] = SPD_EEPROM_ADDRESS2, /* controller 2 */ + [2][0] = SPD_EEPROM_ADDRESS3, /* controller 3 */ +}; +#elif (CONFIG_NUM_DDR_CONTROLLERS == 3) && (CONFIG_DIMM_SLOTS_PER_CTLR == 2) +u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = { + [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */ + [0][1] = SPD_EEPROM_ADDRESS2, /* controller 1 */ + [1][0] = SPD_EEPROM_ADDRESS3, /* controller 2 */ + [1][1] = SPD_EEPROM_ADDRESS4, /* controller 2 */ + [2][0] = SPD_EEPROM_ADDRESS5, /* controller 3 */ + [2][1] = SPD_EEPROM_ADDRESS6, /* controller 3 */ +}; + +#endif + +#define SPD_SPA0_ADDRESS 0x36 +#define SPD_SPA1_ADDRESS 0x37 + +static void __get_spd(generic_spd_eeprom_t *spd, u8 i2c_address) +{ + int ret; +#ifdef CONFIG_SYS_FSL_DDR4 + uint8_t dummy = 0; +#endif + + i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM); + +#ifdef CONFIG_SYS_FSL_DDR4 + /* + * DDR4 SPD has 384 to 512 bytes + * To access the lower 256 bytes, we need to set EE page address to 0 + * To access the upper 256 bytes, we need to set EE page address to 1 + * See Jedec standar No. 21-C for detail + */ + i2c_write(SPD_SPA0_ADDRESS, 0, 1, &dummy, 1); + ret = i2c_read(i2c_address, 0, 1, (uchar *)spd, 256); + if (!ret) { + i2c_write(SPD_SPA1_ADDRESS, 0, 1, &dummy, 1); + ret = i2c_read(i2c_address, 0, 1, + (uchar *)((ulong)spd + 256), + min(256, sizeof(generic_spd_eeprom_t) - 256)); + } +#else + ret = i2c_read(i2c_address, 0, 1, (uchar *)spd, + sizeof(generic_spd_eeprom_t)); +#endif + + if (ret) { + if (i2c_address == +#ifdef SPD_EEPROM_ADDRESS + SPD_EEPROM_ADDRESS +#elif defined(SPD_EEPROM_ADDRESS1) + SPD_EEPROM_ADDRESS1 +#endif + ) { + printf("DDR: failed to read SPD from address %u\n", + i2c_address); + } else { + debug("DDR: failed to read SPD from address %u\n", + i2c_address); + } + memset(spd, 0, sizeof(generic_spd_eeprom_t)); + } +} + +__attribute__((weak, alias("__get_spd"))) +void get_spd(generic_spd_eeprom_t *spd, u8 i2c_address); + +void fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd, + unsigned int ctrl_num) +{ + unsigned int i; + unsigned int i2c_address = 0; + + if (ctrl_num >= CONFIG_NUM_DDR_CONTROLLERS) { + printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num); + return; + } + + for (i = 0; i < CONFIG_DIMM_SLOTS_PER_CTLR; i++) { + i2c_address = spd_i2c_addr[ctrl_num][i]; + get_spd(&(ctrl_dimms_spd[i]), i2c_address); + } +} +#else +void fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd, + unsigned int ctrl_num) +{ +} +#endif /* SPD_EEPROM_ADDRESSx */ + +/* + * ASSUMPTIONS: + * - Same number of CONFIG_DIMM_SLOTS_PER_CTLR on each controller + * - Same memory data bus width on all controllers + * + * NOTES: + * + * The memory controller and associated documentation use confusing + * terminology when referring to the orgranization of DRAM. + * + * Here is a terminology translation table: + * + * memory controller/documention |industry |this code |signals + * -------------------------------|-----------|-----------|----------------- + * physical bank/bank |rank |rank |chip select (CS) + * logical bank/sub-bank |bank |bank |bank address (BA) + * page/row |row |page |row address + * ??? |column |column |column address + * + * The naming confusion is further exacerbated by the descriptions of the + * memory controller interleaving feature, where accesses are interleaved + * _BETWEEN_ two seperate memory controllers. This is configured only in + * CS0_CONFIG[INTLV_CTL] of each memory controller. + * + * memory controller documentation | number of chip selects + * | per memory controller supported + * --------------------------------|----------------------------------------- + * cache line interleaving | 1 (CS0 only) + * page interleaving | 1 (CS0 only) + * bank interleaving | 1 (CS0 only) + * superbank interleraving | depends on bank (chip select) + * | interleraving [rank interleaving] + * | mode used on every memory controller + * + * Even further confusing is the existence of the interleaving feature + * _WITHIN_ each memory controller. The feature is referred to in + * documentation as chip select interleaving or bank interleaving, + * although it is configured in the DDR_SDRAM_CFG field. + * + * Name of field | documentation name | this code + * -----------------------------|-----------------------|------------------ + * DDR_SDRAM_CFG[BA_INTLV_CTL] | Bank (chip select) | rank interleaving + * | interleaving + */ + +const char *step_string_tbl[] = { + "STEP_GET_SPD", + "STEP_COMPUTE_DIMM_PARMS", + "STEP_COMPUTE_COMMON_PARMS", + "STEP_GATHER_OPTS", + "STEP_ASSIGN_ADDRESSES", + "STEP_COMPUTE_REGS", + "STEP_PROGRAM_REGS", + "STEP_ALL" +}; + +const char * step_to_string(unsigned int step) { + + unsigned int s = __ilog2(step); + + if ((1 << s) != step) + return step_string_tbl[7]; + + if (s >= ARRAY_SIZE(step_string_tbl)) { + printf("Error for the step in %s\n", __func__); + s = 0; + } + + return step_string_tbl[s]; +} + +static unsigned long long __step_assign_addresses(fsl_ddr_info_t *pinfo, + unsigned int dbw_cap_adj[]) +{ + int i, j; + unsigned long long total_mem, current_mem_base, total_ctlr_mem; + unsigned long long rank_density, ctlr_density = 0; + + /* + * If a reduced data width is requested, but the SPD + * specifies a physically wider device, adjust the + * computed dimm capacities accordingly before + * assigning addresses. + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + unsigned int found = 0; + + switch (pinfo->memctl_opts[i].data_bus_width) { + case 2: + /* 16-bit */ + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + unsigned int dw; + if (!pinfo->dimm_params[i][j].n_ranks) + continue; + dw = pinfo->dimm_params[i][j].primary_sdram_width; + if ((dw == 72 || dw == 64)) { + dbw_cap_adj[i] = 2; + break; + } else if ((dw == 40 || dw == 32)) { + dbw_cap_adj[i] = 1; + break; + } + } + break; + + case 1: + /* 32-bit */ + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + unsigned int dw; + dw = pinfo->dimm_params[i][j].data_width; + if (pinfo->dimm_params[i][j].n_ranks + && (dw == 72 || dw == 64)) { + /* + * FIXME: can't really do it + * like this because this just + * further reduces the memory + */ + found = 1; + break; + } + } + if (found) { + dbw_cap_adj[i] = 1; + } + break; + + case 0: + /* 64-bit */ + break; + + default: + printf("unexpected data bus width " + "specified controller %u\n", i); + return 1; + } + debug("dbw_cap_adj[%d]=%d\n", i, dbw_cap_adj[i]); + } + + current_mem_base = CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY; + total_mem = 0; + if (pinfo->memctl_opts[0].memctl_interleaving) { + rank_density = pinfo->dimm_params[0][0].rank_density >> + dbw_cap_adj[0]; + switch (pinfo->memctl_opts[0].ba_intlv_ctl & + FSL_DDR_CS0_CS1_CS2_CS3) { + case FSL_DDR_CS0_CS1_CS2_CS3: + ctlr_density = 4 * rank_density; + break; + case FSL_DDR_CS0_CS1: + case FSL_DDR_CS0_CS1_AND_CS2_CS3: + ctlr_density = 2 * rank_density; + break; + case FSL_DDR_CS2_CS3: + default: + ctlr_density = rank_density; + break; + } + debug("rank density is 0x%llx, ctlr density is 0x%llx\n", + rank_density, ctlr_density); + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (pinfo->memctl_opts[i].memctl_interleaving) { + switch (pinfo->memctl_opts[i].memctl_interleaving_mode) { + case FSL_DDR_256B_INTERLEAVING: + case FSL_DDR_CACHE_LINE_INTERLEAVING: + case FSL_DDR_PAGE_INTERLEAVING: + case FSL_DDR_BANK_INTERLEAVING: + case FSL_DDR_SUPERBANK_INTERLEAVING: + total_ctlr_mem = 2 * ctlr_density; + break; + case FSL_DDR_3WAY_1KB_INTERLEAVING: + case FSL_DDR_3WAY_4KB_INTERLEAVING: + case FSL_DDR_3WAY_8KB_INTERLEAVING: + total_ctlr_mem = 3 * ctlr_density; + break; + case FSL_DDR_4WAY_1KB_INTERLEAVING: + case FSL_DDR_4WAY_4KB_INTERLEAVING: + case FSL_DDR_4WAY_8KB_INTERLEAVING: + total_ctlr_mem = 4 * ctlr_density; + break; + default: + panic("Unknown interleaving mode"); + } + pinfo->common_timing_params[i].base_address = + current_mem_base; + pinfo->common_timing_params[i].total_mem = + total_ctlr_mem; + total_mem = current_mem_base + total_ctlr_mem; + debug("ctrl %d base 0x%llx\n", i, current_mem_base); + debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem); + } else { + /* when 3rd controller not interleaved */ + current_mem_base = total_mem; + total_ctlr_mem = 0; + pinfo->common_timing_params[i].base_address = + current_mem_base; + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + unsigned long long cap = + pinfo->dimm_params[i][j].capacity >> dbw_cap_adj[i]; + pinfo->dimm_params[i][j].base_address = + current_mem_base; + debug("ctrl %d dimm %d base 0x%llx\n", i, j, current_mem_base); + current_mem_base += cap; + total_ctlr_mem += cap; + } + debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem); + pinfo->common_timing_params[i].total_mem = + total_ctlr_mem; + total_mem += total_ctlr_mem; + } + } + } else { + /* + * Simple linear assignment if memory + * controllers are not interleaved. + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + total_ctlr_mem = 0; + pinfo->common_timing_params[i].base_address = + current_mem_base; + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + /* Compute DIMM base addresses. */ + unsigned long long cap = + pinfo->dimm_params[i][j].capacity >> dbw_cap_adj[i]; + pinfo->dimm_params[i][j].base_address = + current_mem_base; + debug("ctrl %d dimm %d base 0x%llx\n", i, j, current_mem_base); + current_mem_base += cap; + total_ctlr_mem += cap; + } + debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem); + pinfo->common_timing_params[i].total_mem = + total_ctlr_mem; + total_mem += total_ctlr_mem; + } + } + debug("Total mem by %s is 0x%llx\n", __func__, total_mem); + + return total_mem; +} + +/* Use weak function to allow board file to override the address assignment */ +__attribute__((weak, alias("__step_assign_addresses"))) +unsigned long long step_assign_addresses(fsl_ddr_info_t *pinfo, + unsigned int dbw_cap_adj[]); + +unsigned long long +fsl_ddr_compute(fsl_ddr_info_t *pinfo, unsigned int start_step, + unsigned int size_only) +{ + unsigned int i, j; + unsigned long long total_mem = 0; + int assert_reset; + + fsl_ddr_cfg_regs_t *ddr_reg = pinfo->fsl_ddr_config_reg; + common_timing_params_t *timing_params = pinfo->common_timing_params; + assert_reset = board_need_mem_reset(); + + /* data bus width capacity adjust shift amount */ + unsigned int dbw_capacity_adjust[CONFIG_NUM_DDR_CONTROLLERS]; + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + dbw_capacity_adjust[i] = 0; + } + + debug("starting at step %u (%s)\n", + start_step, step_to_string(start_step)); + + switch (start_step) { + case STEP_GET_SPD: +#if defined(CONFIG_DDR_SPD) || defined(CONFIG_SPD_EEPROM) + /* STEP 1: Gather all DIMM SPD data */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + fsl_ddr_get_spd(pinfo->spd_installed_dimms[i], i); + } + + case STEP_COMPUTE_DIMM_PARMS: + /* STEP 2: Compute DIMM parameters from SPD data */ + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + unsigned int retval; + generic_spd_eeprom_t *spd = + &(pinfo->spd_installed_dimms[i][j]); + dimm_params_t *pdimm = + &(pinfo->dimm_params[i][j]); + + retval = compute_dimm_parameters(spd, pdimm, i); +#ifdef CONFIG_SYS_DDR_RAW_TIMING + if (!i && !j && retval) { + printf("SPD error on controller %d! " + "Trying fallback to raw timing " + "calculation\n", i); + fsl_ddr_get_dimm_params(pdimm, i, j); + } +#else + if (retval == 2) { + printf("Error: compute_dimm_parameters" + " non-zero returned FATAL value " + "for memctl=%u dimm=%u\n", i, j); + return 0; + } +#endif + if (retval) { + debug("Warning: compute_dimm_parameters" + " non-zero return value for memctl=%u " + "dimm=%u\n", i, j); + } + } + } + +#elif defined(CONFIG_SYS_DDR_RAW_TIMING) + case STEP_COMPUTE_DIMM_PARMS: + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + dimm_params_t *pdimm = + &(pinfo->dimm_params[i][j]); + fsl_ddr_get_dimm_params(pdimm, i, j); + } + } + debug("Filling dimm parameters from board specific file\n"); +#endif + case STEP_COMPUTE_COMMON_PARMS: + /* + * STEP 3: Compute a common set of timing parameters + * suitable for all of the DIMMs on each memory controller + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + debug("Computing lowest common DIMM" + " parameters for memctl=%u\n", i); + compute_lowest_common_dimm_parameters( + pinfo->dimm_params[i], + &timing_params[i], + CONFIG_DIMM_SLOTS_PER_CTLR); + } + + case STEP_GATHER_OPTS: + /* STEP 4: Gather configuration requirements from user */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + debug("Reloading memory controller " + "configuration options for memctl=%u\n", i); + /* + * This "reloads" the memory controller options + * to defaults. If the user "edits" an option, + * next_step points to the step after this, + * which is currently STEP_ASSIGN_ADDRESSES. + */ + populate_memctl_options( + timing_params[i].all_dimms_registered, + &pinfo->memctl_opts[i], + pinfo->dimm_params[i], i); + /* + * For RDIMMs, JEDEC spec requires clocks to be stable + * before reset signal is deasserted. For the boards + * using fixed parameters, this function should be + * be called from board init file. + */ + if (timing_params[i].all_dimms_registered) + assert_reset = 1; + } + if (assert_reset) { + debug("Asserting mem reset\n"); + board_assert_mem_reset(); + } + + case STEP_ASSIGN_ADDRESSES: + /* STEP 5: Assign addresses to chip selects */ + check_interleaving_options(pinfo); + total_mem = step_assign_addresses(pinfo, dbw_capacity_adjust); + debug("Total mem %llu assigned\n", total_mem); + + case STEP_COMPUTE_REGS: + /* STEP 6: compute controller register values */ + debug("FSL Memory ctrl register computation\n"); + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (timing_params[i].ndimms_present == 0) { + memset(&ddr_reg[i], 0, + sizeof(fsl_ddr_cfg_regs_t)); + continue; + } + + compute_fsl_memctl_config_regs( + &pinfo->memctl_opts[i], + &ddr_reg[i], &timing_params[i], + pinfo->dimm_params[i], + dbw_capacity_adjust[i], + size_only); + } + + default: + break; + } + + { + /* + * Compute the amount of memory available just by + * looking for the highest valid CSn_BNDS value. + * This allows us to also experiment with using + * only CS0 when using dual-rank DIMMs. + */ + unsigned int max_end = 0; + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + for (j = 0; j < CONFIG_CHIP_SELECTS_PER_CTRL; j++) { + fsl_ddr_cfg_regs_t *reg = &ddr_reg[i]; + if (reg->cs[j].config & 0x80000000) { + unsigned int end; + /* + * 0xfffffff is a special value we put + * for unused bnds + */ + if (reg->cs[j].bnds == 0xffffffff) + continue; + end = reg->cs[j].bnds & 0xffff; + if (end > max_end) { + max_end = end; + } + } + } + } + + total_mem = 1 + (((unsigned long long)max_end << 24ULL) | + 0xFFFFFFULL) - CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY; + } + + return total_mem; +} + +/* + * fsl_ddr_sdram() -- this is the main function to be called by + * initdram() in the board file. + * + * It returns amount of memory configured in bytes. + */ +phys_size_t fsl_ddr_sdram(void) +{ + unsigned int i; +#ifdef CONFIG_PPC + unsigned int law_memctl = LAW_TRGT_IF_DDR_1; +#endif + unsigned long long total_memory; + fsl_ddr_info_t info; + int deassert_reset; + + /* Reset info structure. */ + memset(&info, 0, sizeof(fsl_ddr_info_t)); + + /* Compute it once normally. */ +#ifdef CONFIG_FSL_DDR_INTERACTIVE + if (tstc() && (getc() == 'd')) { /* we got a key press of 'd' */ + total_memory = fsl_ddr_interactive(&info, 0); + } else if (fsl_ddr_interactive_env_var_exists()) { + total_memory = fsl_ddr_interactive(&info, 1); + } else +#endif + total_memory = fsl_ddr_compute(&info, STEP_GET_SPD, 0); + + /* setup 3-way interleaving before enabling DDRC */ + if (info.memctl_opts[0].memctl_interleaving) { + switch (info.memctl_opts[0].memctl_interleaving_mode) { + case FSL_DDR_3WAY_1KB_INTERLEAVING: + case FSL_DDR_3WAY_4KB_INTERLEAVING: + case FSL_DDR_3WAY_8KB_INTERLEAVING: + fsl_ddr_set_intl3r( + info.memctl_opts[0].memctl_interleaving_mode); + break; + default: + break; + } + } + + /* + * Program configuration registers. + * JEDEC specs requires clocks to be stable before deasserting reset + * for RDIMMs. Clocks start after chip select is enabled and clock + * control register is set. During step 1, all controllers have their + * registers set but not enabled. Step 2 proceeds after deasserting + * reset through board FPGA or GPIO. + * For non-registered DIMMs, initialization can go through but it is + * also OK to follow the same flow. + */ + deassert_reset = board_need_mem_reset(); + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (info.common_timing_params[i].all_dimms_registered) + deassert_reset = 1; + } + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + debug("Programming controller %u\n", i); + if (info.common_timing_params[i].ndimms_present == 0) { + debug("No dimms present on controller %u; " + "skipping programming\n", i); + continue; + } + /* + * The following call with step = 1 returns before enabling + * the controller. It has to finish with step = 2 later. + */ + fsl_ddr_set_memctl_regs(&(info.fsl_ddr_config_reg[i]), i, + deassert_reset ? 1 : 0); + } + if (deassert_reset) { + /* Use board FPGA or GPIO to deassert reset signal */ + debug("Deasserting mem reset\n"); + board_deassert_mem_reset(); + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + /* Call with step = 2 to continue initialization */ + fsl_ddr_set_memctl_regs(&(info.fsl_ddr_config_reg[i]), + i, 2); + } + } + +#ifdef CONFIG_PPC + /* program LAWs */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (info.memctl_opts[i].memctl_interleaving) { + switch (info.memctl_opts[i].memctl_interleaving_mode) { + case FSL_DDR_CACHE_LINE_INTERLEAVING: + case FSL_DDR_PAGE_INTERLEAVING: + case FSL_DDR_BANK_INTERLEAVING: + case FSL_DDR_SUPERBANK_INTERLEAVING: + if (i == 0) { + law_memctl = LAW_TRGT_IF_DDR_INTRLV; + fsl_ddr_set_lawbar(&info.common_timing_params[i], + law_memctl, i); + } else if (i == 2) { + law_memctl = LAW_TRGT_IF_DDR_INTLV_34; + fsl_ddr_set_lawbar(&info.common_timing_params[i], + law_memctl, i); + } + break; + case FSL_DDR_3WAY_1KB_INTERLEAVING: + case FSL_DDR_3WAY_4KB_INTERLEAVING: + case FSL_DDR_3WAY_8KB_INTERLEAVING: + law_memctl = LAW_TRGT_IF_DDR_INTLV_123; + if (i == 0) { + fsl_ddr_set_lawbar(&info.common_timing_params[i], + law_memctl, i); + } + break; + case FSL_DDR_4WAY_1KB_INTERLEAVING: + case FSL_DDR_4WAY_4KB_INTERLEAVING: + case FSL_DDR_4WAY_8KB_INTERLEAVING: + law_memctl = LAW_TRGT_IF_DDR_INTLV_1234; + if (i == 0) + fsl_ddr_set_lawbar(&info.common_timing_params[i], + law_memctl, i); + /* place holder for future 4-way interleaving */ + break; + default: + break; + } + } else { + switch (i) { + case 0: + law_memctl = LAW_TRGT_IF_DDR_1; + break; + case 1: + law_memctl = LAW_TRGT_IF_DDR_2; + break; + case 2: + law_memctl = LAW_TRGT_IF_DDR_3; + break; + case 3: + law_memctl = LAW_TRGT_IF_DDR_4; + break; + default: + break; + } + fsl_ddr_set_lawbar(&info.common_timing_params[i], + law_memctl, i); + } + } +#endif + + debug("total_memory by %s = %llu\n", __func__, total_memory); + +#if !defined(CONFIG_PHYS_64BIT) + /* Check for 4G or more. Bad. */ + if (total_memory >= (1ull << 32)) { + puts("Detected "); + print_size(total_memory, " of memory\n"); + printf(" This U-Boot only supports < 4G of DDR\n"); + printf(" You could rebuild it with CONFIG_PHYS_64BIT\n"); + printf(" "); /* re-align to match init_func_ram print */ + total_memory = CONFIG_MAX_MEM_MAPPED; + } +#endif + + return total_memory; +} + +/* + * fsl_ddr_sdram_size() - This function only returns the size of the total + * memory without setting ddr control registers. + */ +phys_size_t +fsl_ddr_sdram_size(void) +{ + fsl_ddr_info_t info; + unsigned long long total_memory = 0; + + memset(&info, 0 , sizeof(fsl_ddr_info_t)); + + /* Compute it once normally. */ + total_memory = fsl_ddr_compute(&info, STEP_GET_SPD, 1); + + return total_memory; +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen1.c b/qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen1.c new file mode 100644 index 000000000..8dd4a9136 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen1.c @@ -0,0 +1,91 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num, int step) +{ + unsigned int i; + struct ccsr_ddr __iomem *ddr = + (struct ccsr_ddr __iomem *)CONFIG_SYS_FSL_DDR_ADDR; + + if (ctrl_num != 0) { + printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num); + return; + } + + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + out_be32(&ddr->cs0_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs0_config, regs->cs[i].config); + + } else if (i == 1) { + out_be32(&ddr->cs1_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs1_config, regs->cs[i].config); + + } else if (i == 2) { + out_be32(&ddr->cs2_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs2_config, regs->cs[i].config); + + } else if (i == 3) { + out_be32(&ddr->cs3_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs3_config, regs->cs[i].config); + } + } + + out_be32(&ddr->timing_cfg_1, regs->timing_cfg_1); + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); + out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); +#if defined(CONFIG_MPC8555) || defined(CONFIG_MPC8541) + out_be32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); +#endif + + /* + * 200 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + */ + udelay(200); + asm volatile("sync;isync"); + + out_be32(&ddr->sdram_cfg, regs->ddr_sdram_cfg); + + asm("sync;isync;msync"); + udelay(500); +} + +#if defined(CONFIG_DDR_ECC) && !defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) +/* + * Initialize all of memory for ECC, then enable errors. + */ + +void +ddr_enable_ecc(unsigned int dram_size) +{ + struct ccsr_ddr __iomem *ddr = + (struct ccsr_ddr __iomem *)(CONFIG_SYS_FSL_DDR_ADDR); + + dma_meminit(CONFIG_MEM_INIT_VALUE, dram_size); + + /* + * Enable errors for ECC. + */ + debug("DMA DDR: err_disable = 0x%08x\n", ddr->err_disable); + ddr->err_disable = 0x00000000; + asm("sync;isync;msync"); + debug("DMA DDR: err_disable = 0x%08x\n", ddr->err_disable); +} + +#endif /* CONFIG_DDR_ECC && ! CONFIG_ECC_INIT_VIA_DDRCONTROLLER */ diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen2.c b/qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen2.c new file mode 100644 index 000000000..988b4a494 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen2.c @@ -0,0 +1,96 @@ +/* + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num, int step) +{ + unsigned int i; + struct ccsr_ddr __iomem *ddr = + (struct ccsr_ddr __iomem *)CONFIG_SYS_FSL_DDR_ADDR; + +#if defined(CONFIG_SYS_FSL_ERRATUM_NMG_DDR120) && defined(CONFIG_MPC85xx) + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + uint svr; +#endif + + if (ctrl_num) { + printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num); + return; + } + +#ifdef CONFIG_SYS_FSL_ERRATUM_NMG_DDR120 + /* + * Set the DDR IO receiver to an acceptable bias point. + * Fixed in Rev 2.1. + */ + svr = get_svr(); + if ((SVR_MAJ(svr) == 1) || IS_SVR_REV(svr, 2, 0)) { + if ((regs->ddr_sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) == + SDRAM_CFG_SDRAM_TYPE_DDR2) + out_be32(&gur->ddrioovcr, 0x90000000); + else + out_be32(&gur->ddrioovcr, 0xA8000000); + } +#endif + + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + out_be32(&ddr->cs0_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs0_config, regs->cs[i].config); + + } else if (i == 1) { + out_be32(&ddr->cs1_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs1_config, regs->cs[i].config); + + } else if (i == 2) { + out_be32(&ddr->cs2_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs2_config, regs->cs[i].config); + + } else if (i == 3) { + out_be32(&ddr->cs3_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs3_config, regs->cs[i].config); + } + } + + out_be32(&ddr->timing_cfg_3, regs->timing_cfg_3); + out_be32(&ddr->timing_cfg_0, regs->timing_cfg_0); + out_be32(&ddr->timing_cfg_1, regs->timing_cfg_1); + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); + out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); + out_be32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); + out_be32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); + out_be32(&ddr->sdram_data_init, regs->ddr_data_init); + out_be32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); + out_be32(&ddr->init_addr, regs->ddr_init_addr); + out_be32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + + /* + * 200 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + */ + udelay(200); + asm volatile("sync;isync"); + + out_be32(&ddr->sdram_cfg, regs->ddr_sdram_cfg); + + /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */ + while (in_be32(&ddr->sdram_cfg_2) & 0x10) { + udelay(10000); /* throttle polling rate */ + } +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen3.c b/qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen3.c new file mode 100644 index 000000000..4d5572ef2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/mpc85xx_ddr_gen3.c @@ -0,0 +1,573 @@ +/* + * Copyright 2008-2012 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* + * regs has the to-be-set values for DDR controller registers + * ctrl_num is the DDR controller number + * step: 0 goes through the initialization in one pass + * 1 sets registers and returns before enabling controller + * 2 resumes from step 1 and continues to initialize + * Dividing the initialization to two steps to deassert DDR reset signal + * to comply with JEDEC specs for RDIMMs. + */ +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num, int step) +{ + unsigned int i, bus_width; + struct ccsr_ddr __iomem *ddr; + u32 temp_sdram_cfg; + u32 total_gb_size_per_controller; + int timeout; +#ifdef CONFIG_SYS_FSL_ERRATUM_DDR111_DDR134 + int timeout_save; + volatile ccsr_local_ecm_t *ecm = (void *)CONFIG_SYS_MPC85xx_ECM_ADDR; + unsigned int csn_bnds_backup = 0, cs_sa, cs_ea, *csn_bnds_t; + int csn = -1; +#endif +#ifdef CONFIG_SYS_FSL_ERRATUM_DDR_A003 + u32 save1, save2; +#endif + +#ifdef CONFIG_DEEP_SLEEP + const ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + bool sleep_flag = 0; +#endif + +#ifdef CONFIG_DEEP_SLEEP + if (in_be32(&gur->scrtsr[0]) & (1 << 3)) + sleep_flag = 1; +#endif + + switch (ctrl_num) { + case 0: + ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR; + break; +#if defined(CONFIG_SYS_FSL_DDR2_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 1) + case 1: + ddr = (void *)CONFIG_SYS_FSL_DDR2_ADDR; + break; +#endif +#if defined(CONFIG_SYS_FSL_DDR3_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 2) + case 2: + ddr = (void *)CONFIG_SYS_FSL_DDR3_ADDR; + break; +#endif +#if defined(CONFIG_SYS_FSL_DDR4_ADDR) && (CONFIG_NUM_DDR_CONTROLLERS > 3) + case 3: + ddr = (void *)CONFIG_SYS_FSL_DDR4_ADDR; + break; +#endif + default: + printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num); + return; + } + + if (step == 2) + goto step2; + + if (regs->ddr_eor) + out_be32(&ddr->eor, regs->ddr_eor); +#ifdef CONFIG_SYS_FSL_ERRATUM_DDR111_DDR134 + debug("Workaround for ERRATUM_DDR111_DDR134\n"); + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + cs_sa = (regs->cs[i].bnds >> 16) & 0xfff; + cs_ea = regs->cs[i].bnds & 0xfff; + if ((cs_sa <= 0xff) && (cs_ea >= 0xff)) { + csn = i; + csn_bnds_backup = regs->cs[i].bnds; + csn_bnds_t = (unsigned int *) ®s->cs[i].bnds; + if (cs_ea > 0xeff) + *csn_bnds_t = regs->cs[i].bnds + 0x01000000; + else + *csn_bnds_t = regs->cs[i].bnds + 0x01000100; + debug("Found cs%d_bns (0x%08x) covering 0xff000000, " + "change it to 0x%x\n", + csn, csn_bnds_backup, regs->cs[i].bnds); + break; + } + } +#endif + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + out_be32(&ddr->cs0_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs0_config, regs->cs[i].config); + out_be32(&ddr->cs0_config_2, regs->cs[i].config_2); + + } else if (i == 1) { + out_be32(&ddr->cs1_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs1_config, regs->cs[i].config); + out_be32(&ddr->cs1_config_2, regs->cs[i].config_2); + + } else if (i == 2) { + out_be32(&ddr->cs2_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs2_config, regs->cs[i].config); + out_be32(&ddr->cs2_config_2, regs->cs[i].config_2); + + } else if (i == 3) { + out_be32(&ddr->cs3_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs3_config, regs->cs[i].config); + out_be32(&ddr->cs3_config_2, regs->cs[i].config_2); + } + } + + out_be32(&ddr->timing_cfg_3, regs->timing_cfg_3); + out_be32(&ddr->timing_cfg_0, regs->timing_cfg_0); + out_be32(&ddr->timing_cfg_1, regs->timing_cfg_1); + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); +#ifdef CONFIG_DEEP_SLEEP + if (sleep_flag) + out_be32(&ddr->sdram_cfg_2, + regs->ddr_sdram_cfg_2 & ~SDRAM_CFG2_D_INIT); + else +#endif + out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); + out_be32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); + out_be32(&ddr->sdram_mode_3, regs->ddr_sdram_mode_3); + out_be32(&ddr->sdram_mode_4, regs->ddr_sdram_mode_4); + out_be32(&ddr->sdram_mode_5, regs->ddr_sdram_mode_5); + out_be32(&ddr->sdram_mode_6, regs->ddr_sdram_mode_6); + out_be32(&ddr->sdram_mode_7, regs->ddr_sdram_mode_7); + out_be32(&ddr->sdram_mode_8, regs->ddr_sdram_mode_8); + out_be32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); + out_be32(&ddr->sdram_data_init, regs->ddr_data_init); + out_be32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); +#ifdef CONFIG_DEEP_SLEEP + if (sleep_flag) { + out_be32(&ddr->init_addr, 0); + out_be32(&ddr->init_ext_addr, (1 << 31)); + } else +#endif + { + out_be32(&ddr->init_addr, regs->ddr_init_addr); + out_be32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + } + + out_be32(&ddr->timing_cfg_4, regs->timing_cfg_4); + out_be32(&ddr->timing_cfg_5, regs->timing_cfg_5); + out_be32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl); + out_be32(&ddr->ddr_wrlvl_cntl, regs->ddr_wrlvl_cntl); +#ifndef CONFIG_SYS_FSL_DDR_EMU + /* + * Skip these two registers if running on emulator + * because emulator doesn't have skew between bytes. + */ + + if (regs->ddr_wrlvl_cntl_2) + out_be32(&ddr->ddr_wrlvl_cntl_2, regs->ddr_wrlvl_cntl_2); + if (regs->ddr_wrlvl_cntl_3) + out_be32(&ddr->ddr_wrlvl_cntl_3, regs->ddr_wrlvl_cntl_3); +#endif + + out_be32(&ddr->ddr_sr_cntr, regs->ddr_sr_cntr); + out_be32(&ddr->ddr_sdram_rcw_1, regs->ddr_sdram_rcw_1); + out_be32(&ddr->ddr_sdram_rcw_2, regs->ddr_sdram_rcw_2); + out_be32(&ddr->ddr_cdr1, regs->ddr_cdr1); + out_be32(&ddr->ddr_cdr2, regs->ddr_cdr2); + out_be32(&ddr->err_disable, regs->err_disable); + out_be32(&ddr->err_int_en, regs->err_int_en); + for (i = 0; i < 32; i++) { + if (regs->debug[i]) { + debug("Write to debug_%d as %08x\n", i+1, regs->debug[i]); + out_be32(&ddr->debug[i], regs->debug[i]); + } + } +#ifdef CONFIG_SYS_FSL_ERRATUM_A_004934 + out_be32(&ddr->debug[28], 0x30003000); +#endif + +#ifdef CONFIG_SYS_FSL_ERRATUM_DDR_A003474 + out_be32(&ddr->debug[12], 0x00000015); + out_be32(&ddr->debug[21], 0x24000000); +#endif /* CONFIG_SYS_FSL_ERRATUM_DDR_A003474 */ + + /* + * For RDIMMs, JEDEC spec requires clocks to be stable before reset is + * deasserted. Clocks start when any chip select is enabled and clock + * control register is set. Because all DDR components are connected to + * one reset signal, this needs to be done in two steps. Step 1 is to + * get the clocks started. Step 2 resumes after reset signal is + * deasserted. + */ + if (step == 1) { + udelay(200); + return; + } + +step2: + /* Set, but do not enable the memory */ + temp_sdram_cfg = regs->ddr_sdram_cfg; + temp_sdram_cfg &= ~(SDRAM_CFG_MEM_EN); + out_be32(&ddr->sdram_cfg, temp_sdram_cfg); +#ifdef CONFIG_SYS_FSL_ERRATUM_DDR_A003 + debug("Workaround for ERRATUM_DDR_A003\n"); + if (regs->ddr_sdram_rcw_2 & 0x00f00000) { + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2 & 0xf07fffff); + out_be32(&ddr->debug[2], 0x00000400); + out_be32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl & 0x7fffffff); + out_be32(&ddr->ddr_wrlvl_cntl, regs->ddr_wrlvl_cntl & 0x7fffffff); + out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2 & 0xffffffeb); + out_be32(&ddr->mtcr, 0); + save1 = in_be32(&ddr->debug[12]); + save2 = in_be32(&ddr->debug[21]); + out_be32(&ddr->debug[12], 0x00000015); + out_be32(&ddr->debug[21], 0x24000000); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval & 0xffff); + out_be32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_BI | SDRAM_CFG_MEM_EN); + + asm volatile("sync;isync"); + while (!(in_be32(&ddr->debug[1]) & 0x2)) + ; + + switch (regs->ddr_sdram_rcw_2 & 0x00f00000) { + case 0x00000000: + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS0_CS1 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x02)); +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 2) + if (!(regs->cs[2].config & SDRAM_CS_CONFIG_EN)) + break; + while (in_be32(&ddr->sdram_md_cntl) & MD_CNTL_MD_EN) + ; + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS2_CS3 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x02)); +#endif + break; + case 0x00100000: + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS0_CS1 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x0a)); +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 2) + if (!(regs->cs[2].config & SDRAM_CS_CONFIG_EN)) + break; + while (in_be32(&ddr->sdram_md_cntl) & MD_CNTL_MD_EN) + ; + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS2_CS3 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x0a)); +#endif + break; + case 0x00200000: + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS0_CS1 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x12)); +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 2) + if (!(regs->cs[2].config & SDRAM_CS_CONFIG_EN)) + break; + while (in_be32(&ddr->sdram_md_cntl) & MD_CNTL_MD_EN) + ; + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS2_CS3 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x12)); +#endif + break; + case 0x00300000: + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS0_CS1 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x1a)); +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 2) + if (!(regs->cs[2].config & SDRAM_CS_CONFIG_EN)) + break; + while (in_be32(&ddr->sdram_md_cntl) & MD_CNTL_MD_EN) + ; + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS2_CS3 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x1a)); +#endif + break; + default: + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS0_CS1 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x02)); +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 2) + if (!(regs->cs[2].config & SDRAM_CS_CONFIG_EN)) + break; + while (in_be32(&ddr->sdram_md_cntl) & MD_CNTL_MD_EN) + ; + out_be32(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL_CS2_CS3 | + 0x04000000 | + MD_CNTL_WRCW | + MD_CNTL_MD_VALUE(0x02)); +#endif + printf("Unsupported RC10\n"); + break; + } + + while (in_be32(&ddr->sdram_md_cntl) & 0x80000000) + ; + udelay(6); + out_be32(&ddr->sdram_cfg, temp_sdram_cfg); + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); + out_be32(&ddr->debug[2], 0x0); + out_be32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl); + out_be32(&ddr->ddr_wrlvl_cntl, regs->ddr_wrlvl_cntl); + out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + out_be32(&ddr->debug[12], save1); + out_be32(&ddr->debug[21], save2); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); + + } +#endif + /* + * For 8572 DDR1 erratum - DDR controller may enter illegal state + * when operatiing in 32-bit bus mode with 4-beat bursts, + * This erratum does not affect DDR3 mode, only for DDR2 mode. + */ +#ifdef CONFIG_SYS_FSL_ERRATUM_DDR_115 + debug("Workaround for ERRATUM_DDR_115\n"); + if ((((in_be32(&ddr->sdram_cfg) >> 24) & 0x7) == SDRAM_TYPE_DDR2) + && in_be32(&ddr->sdram_cfg) & 0x80000) { + /* set DEBUG_1[31] */ + setbits_be32(&ddr->debug[0], 1); + } +#endif +#ifdef CONFIG_SYS_FSL_ERRATUM_DDR111_DDR134 + debug("Workaround for ERRATUM_DDR111_DDR134\n"); + /* + * This is the combined workaround for DDR111 and DDR134 + * following the published errata for MPC8572 + */ + + /* 1. Set EEBACR[3] */ + setbits_be32(&ecm->eebacr, 0x10000000); + debug("Setting EEBACR[3] to 0x%08x\n", in_be32(&ecm->eebacr)); + + /* 2. Set DINIT in SDRAM_CFG_2*/ + setbits_be32(&ddr->sdram_cfg_2, SDRAM_CFG2_D_INIT); + debug("Setting sdram_cfg_2[D_INIT] to 0x%08x\n", + in_be32(&ddr->sdram_cfg_2)); + + /* 3. Set DEBUG_3[21] */ + setbits_be32(&ddr->debug[2], 0x400); + debug("Setting DEBUG_3[21] to 0x%08x\n", in_be32(&ddr->debug[2])); + +#endif /* part 1 of the workaound */ + + /* + * 500 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + * DDR2 need 200 us, and DDR3 need 500 us from spec, + * we choose the max, that is 500 us for all of case. + */ + udelay(500); + asm volatile("sync;isync"); + +#ifdef CONFIG_DEEP_SLEEP + if (sleep_flag) { + /* enter self-refresh */ + setbits_be32(&ddr->sdram_cfg_2, (1 << 31)); + /* do board specific memory setup */ + board_mem_sleep_setup(); + } +#endif + + /* Let the controller go */ +#ifdef CONFIG_DEEP_SLEEP + if (sleep_flag) + temp_sdram_cfg = (in_be32(&ddr->sdram_cfg) | SDRAM_CFG_BI); + else +#endif + temp_sdram_cfg = (in_be32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI); + out_be32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN); + asm volatile("sync;isync"); + + total_gb_size_per_controller = 0; + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (!(regs->cs[i].config & 0x80000000)) + continue; + total_gb_size_per_controller += 1 << ( + ((regs->cs[i].config >> 14) & 0x3) + 2 + + ((regs->cs[i].config >> 8) & 0x7) + 12 + + ((regs->cs[i].config >> 0) & 0x7) + 8 + + 3 - ((regs->ddr_sdram_cfg >> 19) & 0x3) - + 26); /* minus 26 (count of 64M) */ + } + if (fsl_ddr_get_intl3r() & 0x80000000) /* 3-way interleaving */ + total_gb_size_per_controller *= 3; + else if (regs->cs[0].config & 0x20000000) /* 2-way interleaving */ + total_gb_size_per_controller <<= 1; + /* + * total memory / bus width = transactions needed + * transactions needed / data rate = seconds + * to add plenty of buffer, double the time + * For example, 2GB on 666MT/s 64-bit bus takes about 402ms + * Let's wait for 800ms + */ + bus_width = 3 - ((ddr->sdram_cfg & SDRAM_CFG_DBW_MASK) + >> SDRAM_CFG_DBW_SHIFT); + timeout = ((total_gb_size_per_controller << (6 - bus_width)) * 100 / + (get_ddr_freq(0) >> 20)) << 1; +#ifdef CONFIG_SYS_FSL_ERRATUM_DDR111_DDR134 + timeout_save = timeout; +#endif + total_gb_size_per_controller >>= 4; /* shift down to gb size */ + debug("total %d GB\n", total_gb_size_per_controller); + debug("Need to wait up to %d * 10ms\n", timeout); + + /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */ + while ((in_be32(&ddr->sdram_cfg_2) & SDRAM_CFG2_D_INIT) && + (timeout >= 0)) { + udelay(10000); /* throttle polling rate */ + timeout--; + } + + if (timeout <= 0) + printf("Waiting for D_INIT timeout. Memory may not work.\n"); + +#ifdef CONFIG_SYS_FSL_ERRATUM_DDR111_DDR134 + /* continue this workaround */ + + /* 4. Clear DEBUG3[21] */ + clrbits_be32(&ddr->debug[2], 0x400); + debug("Clearing D3[21] to 0x%08x\n", in_be32(&ddr->debug[2])); + + /* DDR134 workaround starts */ + /* A: Clear sdram_cfg_2[odt_cfg] */ + clrbits_be32(&ddr->sdram_cfg_2, SDRAM_CFG2_ODT_CFG_MASK); + debug("Clearing SDRAM_CFG2[ODT_CFG] to 0x%08x\n", + in_be32(&ddr->sdram_cfg_2)); + + /* B: Set DEBUG1[15] */ + setbits_be32(&ddr->debug[0], 0x10000); + debug("Setting D1[15] to 0x%08x\n", in_be32(&ddr->debug[0])); + + /* C: Set timing_cfg_2[cpo] to 0b11111 */ + setbits_be32(&ddr->timing_cfg_2, TIMING_CFG_2_CPO_MASK); + debug("Setting TMING_CFG_2[CPO] to 0x%08x\n", + in_be32(&ddr->timing_cfg_2)); + + /* D: Set D6 to 0x9f9f9f9f */ + out_be32(&ddr->debug[5], 0x9f9f9f9f); + debug("Setting D6 to 0x%08x\n", in_be32(&ddr->debug[5])); + + /* E: Set D7 to 0x9f9f9f9f */ + out_be32(&ddr->debug[6], 0x9f9f9f9f); + debug("Setting D7 to 0x%08x\n", in_be32(&ddr->debug[6])); + + /* F: Set D2[20] */ + setbits_be32(&ddr->debug[1], 0x800); + debug("Setting D2[20] to 0x%08x\n", in_be32(&ddr->debug[1])); + + /* G: Poll on D2[20] until cleared */ + while (in_be32(&ddr->debug[1]) & 0x800) + udelay(10000); /* throttle polling rate */ + + /* H: Clear D1[15] */ + clrbits_be32(&ddr->debug[0], 0x10000); + debug("Setting D1[15] to 0x%08x\n", in_be32(&ddr->debug[0])); + + /* I: Set sdram_cfg_2[odt_cfg] */ + setbits_be32(&ddr->sdram_cfg_2, + regs->ddr_sdram_cfg_2 & SDRAM_CFG2_ODT_CFG_MASK); + debug("Setting sdram_cfg_2 to 0x%08x\n", in_be32(&ddr->sdram_cfg_2)); + + /* Continuing with the DDR111 workaround */ + /* 5. Set D2[21] */ + setbits_be32(&ddr->debug[1], 0x400); + debug("Setting D2[21] to 0x%08x\n", in_be32(&ddr->debug[1])); + + /* 6. Poll D2[21] until its cleared */ + while (in_be32(&ddr->debug[1]) & 0x400) + udelay(10000); /* throttle polling rate */ + + /* 7. Wait for state machine 2nd run, roughly 400ms/GB */ + debug("Wait for %d * 10ms\n", timeout_save); + udelay(timeout_save * 10000); + + /* 8. Set sdram_cfg_2[dinit] if options requires */ + setbits_be32(&ddr->sdram_cfg_2, + regs->ddr_sdram_cfg_2 & SDRAM_CFG2_D_INIT); + debug("Setting sdram_cfg_2 to 0x%08x\n", in_be32(&ddr->sdram_cfg_2)); + + /* 9. Poll until dinit is cleared */ + timeout = timeout_save; + debug("Need to wait up to %d * 10ms\n", timeout); + while ((in_be32(&ddr->sdram_cfg_2) & SDRAM_CFG2_D_INIT) && + (timeout >= 0)) { + udelay(10000); /* throttle polling rate */ + timeout--; + } + + if (timeout <= 0) + printf("Waiting for D_INIT timeout. Memory may not work.\n"); + + /* 10. Clear EEBACR[3] */ + clrbits_be32(&ecm->eebacr, 10000000); + debug("Clearing EEBACR[3] to 0x%08x\n", in_be32(&ecm->eebacr)); + + if (csn != -1) { + csn_bnds_t = (unsigned int *) ®s->cs[csn].bnds; + *csn_bnds_t = csn_bnds_backup; + debug("Change cs%d_bnds back to 0x%08x\n", + csn, regs->cs[csn].bnds); + setbits_be32(&ddr->sdram_cfg, 0x2); /* MEM_HALT */ + switch (csn) { + case 0: + out_be32(&ddr->cs0_bnds, regs->cs[csn].bnds); + break; + case 1: + out_be32(&ddr->cs1_bnds, regs->cs[csn].bnds); + break; + case 2: + out_be32(&ddr->cs2_bnds, regs->cs[csn].bnds); + break; + case 3: + out_be32(&ddr->cs3_bnds, regs->cs[csn].bnds); + break; + } + clrbits_be32(&ddr->sdram_cfg, 0x2); + } +#endif /* CONFIG_SYS_FSL_ERRATUM_DDR111_DDR134 */ +#ifdef CONFIG_DEEP_SLEEP + if (sleep_flag) + /* exit self-refresh */ + clrbits_be32(&ddr->sdram_cfg_2, (1 << 31)); +#endif +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/mpc86xx_ddr.c b/qemu/roms/u-boot/drivers/ddr/fsl/mpc86xx_ddr.c new file mode 100644 index 000000000..4551ed87d --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/mpc86xx_ddr.c @@ -0,0 +1,85 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num, int step) +{ + unsigned int i; + struct ccsr_ddr __iomem *ddr; + + switch (ctrl_num) { + case 0: + ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR; + break; + case 1: + ddr = (void *)CONFIG_SYS_FSL_DDR2_ADDR; + break; + default: + printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num); + return; + } + + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + out_be32(&ddr->cs0_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs0_config, regs->cs[i].config); + + } else if (i == 1) { + out_be32(&ddr->cs1_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs1_config, regs->cs[i].config); + + } else if (i == 2) { + out_be32(&ddr->cs2_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs2_config, regs->cs[i].config); + + } else if (i == 3) { + out_be32(&ddr->cs3_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs3_config, regs->cs[i].config); + } + } + + out_be32(&ddr->timing_cfg_3, regs->timing_cfg_3); + out_be32(&ddr->timing_cfg_0, regs->timing_cfg_0); + out_be32(&ddr->timing_cfg_1, regs->timing_cfg_1); + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); + out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); + out_be32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); + out_be32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); + out_be32(&ddr->sdram_data_init, regs->ddr_data_init); + out_be32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); + out_be32(&ddr->init_addr, regs->ddr_init_addr); + out_be32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + + debug("before go\n"); + + /* + * 200 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + */ + udelay(200); + asm volatile("sync;isync"); + + out_be32(&ddr->sdram_cfg, regs->ddr_sdram_cfg); + + /* + * Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done + */ + while (in_be32(&ddr->sdram_cfg_2) & 0x10) { + udelay(10000); /* throttle polling rate */ + } +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/options.c b/qemu/roms/u-boot/drivers/ddr/fsl/options.c new file mode 100644 index 000000000..5986e1a0b --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/options.c @@ -0,0 +1,1164 @@ +/* + * Copyright 2008, 2010-2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include + +/* + * Use our own stack based buffer before relocation to allow accessing longer + * hwconfig strings that might be in the environment before we've relocated. + * This is pretty fragile on both the use of stack and if the buffer is big + * enough. However we will get a warning from getenv_f for the later. + */ + +/* Board-specific functions defined in each board's ddr.c */ +extern void fsl_ddr_board_options(memctl_options_t *popts, + dimm_params_t *pdimm, + unsigned int ctrl_num); + +struct dynamic_odt { + unsigned int odt_rd_cfg; + unsigned int odt_wr_cfg; + unsigned int odt_rtt_norm; + unsigned int odt_rtt_wr; +}; + +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) +static const struct dynamic_odt single_Q[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS_AND_OTHER_DIMM, + DDR3_RTT_20_OHM, + DDR3_RTT_120_OHM + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, /* tied high */ + DDR3_RTT_OFF, + DDR3_RTT_120_OHM + }, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS_AND_OTHER_DIMM, + DDR3_RTT_20_OHM, + DDR3_RTT_120_OHM + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, /* tied high */ + DDR3_RTT_OFF, + DDR3_RTT_120_OHM + } +}; + +static const struct dynamic_odt single_D[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_ALL, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR3_RTT_OFF, + DDR3_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0} +}; + +static const struct dynamic_odt single_S[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_ALL, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, +}; + +static const struct dynamic_odt dual_DD[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR3_RTT_30_OHM, + DDR3_RTT_OFF + }, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR3_RTT_30_OHM, + DDR3_RTT_OFF + } +}; + +static const struct dynamic_odt dual_DS[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR3_RTT_30_OHM, + DDR3_RTT_OFF + }, + { /* cs2 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_ALL, + DDR3_RTT_20_OHM, + DDR3_RTT_120_OHM + }, + {0, 0, 0, 0} +}; +static const struct dynamic_odt dual_SD[4] = { + { /* cs0 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_ALL, + DDR3_RTT_20_OHM, + DDR3_RTT_120_OHM + }, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR3_RTT_20_OHM, + DDR3_RTT_OFF + } +}; + +static const struct dynamic_odt dual_SS[4] = { + { /* cs0 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_ALL, + DDR3_RTT_30_OHM, + DDR3_RTT_120_OHM + }, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_ALL, + DDR3_RTT_30_OHM, + DDR3_RTT_120_OHM + }, + {0, 0, 0, 0} +}; + +static const struct dynamic_odt dual_D0[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR3_RTT_OFF, + DDR3_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0} +}; + +static const struct dynamic_odt dual_0D[4] = { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR3_RTT_OFF, + DDR3_RTT_OFF + } +}; + +static const struct dynamic_odt dual_S0[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + +}; + +static const struct dynamic_odt dual_0S[4] = { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + {0, 0, 0, 0} + +}; + +static const struct dynamic_odt odt_unknown[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + } +}; +#else /* CONFIG_SYS_FSL_DDR3 || CONFIG_SYS_FSL_DDR4 */ +static const struct dynamic_odt single_Q[4] = { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} +}; + +static const struct dynamic_odt single_D[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_ALL, + DDR2_RTT_150_OHM, + DDR2_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR2_RTT_OFF, + DDR2_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0} +}; + +static const struct dynamic_odt single_S[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_ALL, + DDR2_RTT_150_OHM, + DDR2_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, +}; + +static const struct dynamic_odt dual_DD[4] = { + { /* cs0 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR2_RTT_OFF, + DDR2_RTT_OFF + }, + { /* cs2 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR2_RTT_OFF, + DDR2_RTT_OFF + } +}; + +static const struct dynamic_odt dual_DS[4] = { + { /* cs0 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR2_RTT_OFF, + DDR2_RTT_OFF + }, + { /* cs2 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + {0, 0, 0, 0} +}; + +static const struct dynamic_odt dual_SD[4] = { + { /* cs0 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR2_RTT_OFF, + DDR2_RTT_OFF + } +}; + +static const struct dynamic_odt dual_SS[4] = { + { /* cs0 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + {0, 0, 0, 0} +}; + +static const struct dynamic_odt dual_D0[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_ALL, + DDR2_RTT_150_OHM, + DDR2_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR2_RTT_OFF, + DDR2_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0} +}; + +static const struct dynamic_odt dual_0D[4] = { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_ALL, + DDR2_RTT_150_OHM, + DDR2_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR2_RTT_OFF, + DDR2_RTT_OFF + } +}; + +static const struct dynamic_odt dual_S0[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR2_RTT_150_OHM, + DDR2_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + +}; + +static const struct dynamic_odt dual_0S[4] = { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR2_RTT_150_OHM, + DDR2_RTT_OFF + }, + {0, 0, 0, 0} + +}; + +static const struct dynamic_odt odt_unknown[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR2_RTT_OFF, + DDR2_RTT_OFF + }, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR2_RTT_75_OHM, + DDR2_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR2_RTT_OFF, + DDR2_RTT_OFF + } +}; +#endif + +/* + * Automatically seleect bank interleaving mode based on DIMMs + * in this order: cs0_cs1_cs2_cs3, cs0_cs1, null. + * This function only deal with one or two slots per controller. + */ +static inline unsigned int auto_bank_intlv(dimm_params_t *pdimm) +{ +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 1) + if (pdimm[0].n_ranks == 4) + return FSL_DDR_CS0_CS1_CS2_CS3; + else if (pdimm[0].n_ranks == 2) + return FSL_DDR_CS0_CS1; +#elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2) +#ifdef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE + if (pdimm[0].n_ranks == 4) + return FSL_DDR_CS0_CS1_CS2_CS3; +#endif + if (pdimm[0].n_ranks == 2) { + if (pdimm[1].n_ranks == 2) + return FSL_DDR_CS0_CS1_CS2_CS3; + else + return FSL_DDR_CS0_CS1; + } +#endif + return 0; +} + +unsigned int populate_memctl_options(int all_dimms_registered, + memctl_options_t *popts, + dimm_params_t *pdimm, + unsigned int ctrl_num) +{ + unsigned int i; + char buffer[HWCONFIG_BUFFER_SIZE]; + char *buf = NULL; +#if defined(CONFIG_SYS_FSL_DDR3) || \ + defined(CONFIG_SYS_FSL_DDR2) || \ + defined(CONFIG_SYS_FSL_DDR4) + const struct dynamic_odt *pdodt = odt_unknown; +#endif + ulong ddr_freq; + + /* + * Extract hwconfig from environment since we have not properly setup + * the environment but need it for ddr config params + */ + if (getenv_f("hwconfig", buffer, sizeof(buffer)) > 0) + buf = buffer; + +#if defined(CONFIG_SYS_FSL_DDR3) || \ + defined(CONFIG_SYS_FSL_DDR2) || \ + defined(CONFIG_SYS_FSL_DDR4) + /* Chip select options. */ +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 1) + switch (pdimm[0].n_ranks) { + case 1: + pdodt = single_S; + break; + case 2: + pdodt = single_D; + break; + case 4: + pdodt = single_Q; + break; + } +#elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2) + switch (pdimm[0].n_ranks) { +#ifdef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE + case 4: + pdodt = single_Q; + if (pdimm[1].n_ranks) + printf("Error: Quad- and Dual-rank DIMMs cannot be used together\n"); + break; +#endif + case 2: + switch (pdimm[1].n_ranks) { + case 2: + pdodt = dual_DD; + break; + case 1: + pdodt = dual_DS; + break; + case 0: + pdodt = dual_D0; + break; + } + break; + case 1: + switch (pdimm[1].n_ranks) { + case 2: + pdodt = dual_SD; + break; + case 1: + pdodt = dual_SS; + break; + case 0: + pdodt = dual_S0; + break; + } + break; + case 0: + switch (pdimm[1].n_ranks) { + case 2: + pdodt = dual_0D; + break; + case 1: + pdodt = dual_0S; + break; + } + break; + } +#endif /* CONFIG_DIMM_SLOTS_PER_CTLR */ +#endif /* CONFIG_SYS_FSL_DDR2, 3, 4 */ + + /* Pick chip-select local options. */ + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { +#if defined(CONFIG_SYS_FSL_DDR3) || \ + defined(CONFIG_SYS_FSL_DDR2) || \ + defined(CONFIG_SYS_FSL_DDR4) + popts->cs_local_opts[i].odt_rd_cfg = pdodt[i].odt_rd_cfg; + popts->cs_local_opts[i].odt_wr_cfg = pdodt[i].odt_wr_cfg; + popts->cs_local_opts[i].odt_rtt_norm = pdodt[i].odt_rtt_norm; + popts->cs_local_opts[i].odt_rtt_wr = pdodt[i].odt_rtt_wr; +#else + popts->cs_local_opts[i].odt_rd_cfg = FSL_DDR_ODT_NEVER; + popts->cs_local_opts[i].odt_wr_cfg = FSL_DDR_ODT_CS; +#endif + popts->cs_local_opts[i].auto_precharge = 0; + } + + /* Pick interleaving mode. */ + + /* + * 0 = no interleaving + * 1 = interleaving between 2 controllers + */ + popts->memctl_interleaving = 0; + + /* + * 0 = cacheline + * 1 = page + * 2 = (logical) bank + * 3 = superbank (only if CS interleaving is enabled) + */ + popts->memctl_interleaving_mode = 0; + + /* + * 0: cacheline: bit 30 of the 36-bit physical addr selects the memctl + * 1: page: bit to the left of the column bits selects the memctl + * 2: bank: bit to the left of the bank bits selects the memctl + * 3: superbank: bit to the left of the chip select selects the memctl + * + * NOTE: ba_intlv (rank interleaving) is independent of memory + * controller interleaving; it is only within a memory controller. + * Must use superbank interleaving if rank interleaving is used and + * memory controller interleaving is enabled. + */ + + /* + * 0 = no + * 0x40 = CS0,CS1 + * 0x20 = CS2,CS3 + * 0x60 = CS0,CS1 + CS2,CS3 + * 0x04 = CS0,CS1,CS2,CS3 + */ + popts->ba_intlv_ctl = 0; + + /* Memory Organization Parameters */ + popts->registered_dimm_en = all_dimms_registered; + + /* Operational Mode Paramters */ + + /* Pick ECC modes */ + popts->ecc_mode = 0; /* 0 = disabled, 1 = enabled */ +#ifdef CONFIG_DDR_ECC + if (hwconfig_sub_f("fsl_ddr", "ecc", buf)) { + if (hwconfig_subarg_cmp_f("fsl_ddr", "ecc", "on", buf)) + popts->ecc_mode = 1; + } else + popts->ecc_mode = 1; +#endif + popts->ecc_init_using_memctl = 1; /* 0 = use DMA, 1 = use memctl */ + + /* + * Choose DQS config + * 0 for DDR1 + * 1 for DDR2 + */ +#if defined(CONFIG_SYS_FSL_DDR1) + popts->dqs_config = 0; +#elif defined(CONFIG_SYS_FSL_DDR2) || defined(CONFIG_SYS_FSL_DDR3) + popts->dqs_config = 1; +#endif + + /* Choose self-refresh during sleep. */ + popts->self_refresh_in_sleep = 1; + + /* Choose dynamic power management mode. */ + popts->dynamic_power = 0; + + /* + * check first dimm for primary sdram width + * presuming all dimms are similar + * 0 = 64-bit, 1 = 32-bit, 2 = 16-bit + */ +#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2) + if (pdimm[0].n_ranks != 0) { + if ((pdimm[0].data_width >= 64) && \ + (pdimm[0].data_width <= 72)) + popts->data_bus_width = 0; + else if ((pdimm[0].data_width >= 32) || \ + (pdimm[0].data_width <= 40)) + popts->data_bus_width = 1; + else { + panic("Error: data width %u is invalid!\n", + pdimm[0].data_width); + } + } +#else + if (pdimm[0].n_ranks != 0) { + if (pdimm[0].primary_sdram_width == 64) + popts->data_bus_width = 0; + else if (pdimm[0].primary_sdram_width == 32) + popts->data_bus_width = 1; + else if (pdimm[0].primary_sdram_width == 16) + popts->data_bus_width = 2; + else { + panic("Error: primary sdram width %u is invalid!\n", + pdimm[0].primary_sdram_width); + } + } +#endif + + popts->x4_en = (pdimm[0].device_width == 4) ? 1 : 0; + + /* Choose burst length. */ +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) +#if defined(CONFIG_E500MC) + popts->otf_burst_chop_en = 0; /* on-the-fly burst chop disable */ + popts->burst_length = DDR_BL8; /* Fixed 8-beat burst len */ +#else + if ((popts->data_bus_width == 1) || (popts->data_bus_width == 2)) { + /* 32-bit or 16-bit bus */ + popts->otf_burst_chop_en = 0; + popts->burst_length = DDR_BL8; + } else { + popts->otf_burst_chop_en = 1; /* on-the-fly burst chop */ + popts->burst_length = DDR_OTF; /* on-the-fly BC4 and BL8 */ + } +#endif +#else + popts->burst_length = DDR_BL4; /* has to be 4 for DDR2 */ +#endif + + /* Choose ddr controller address mirror mode */ +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + popts->mirrored_dimm = pdimm[0].mirrored_dimm; +#endif + + /* Global Timing Parameters. */ + debug("mclk_ps = %u ps\n", get_memory_clk_period_ps()); + + /* Pick a caslat override. */ + popts->cas_latency_override = 0; + popts->cas_latency_override_value = 3; + if (popts->cas_latency_override) { + debug("using caslat override value = %u\n", + popts->cas_latency_override_value); + } + + /* Decide whether to use the computed derated latency */ + popts->use_derated_caslat = 0; + + /* Choose an additive latency. */ + popts->additive_latency_override = 0; + popts->additive_latency_override_value = 3; + if (popts->additive_latency_override) { + debug("using additive latency override value = %u\n", + popts->additive_latency_override_value); + } + + /* + * 2T_EN setting + * + * Factors to consider for 2T_EN: + * - number of DIMMs installed + * - number of components, number of active ranks + * - how much time you want to spend playing around + */ + popts->twot_en = 0; + popts->threet_en = 0; + + /* for RDIMM, address parity enable */ + popts->ap_en = 1; + + /* + * BSTTOPRE precharge interval + * + * Set this to 0 for global auto precharge + * The value of 0x100 has been used for DDR1, DDR2, DDR3. + * It is not wrong. Any value should be OK. The performance depends on + * applications. There is no one good value for all. + */ + popts->bstopre = 0x100; + + /* Minimum CKE pulse width -- tCKE(MIN) */ + popts->tcke_clock_pulse_width_ps + = mclk_to_picos(FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR); + + /* + * Window for four activates -- tFAW + * + * FIXME: UM: applies only to DDR2/DDR3 with eight logical banks only + * FIXME: varies depending upon number of column addresses or data + * FIXME: width, was considering looking at pdimm->primary_sdram_width + */ +#if defined(CONFIG_SYS_FSL_DDR1) + popts->tfaw_window_four_activates_ps = mclk_to_picos(1); + +#elif defined(CONFIG_SYS_FSL_DDR2) + /* + * x4/x8; some datasheets have 35000 + * x16 wide columns only? Use 50000? + */ + popts->tfaw_window_four_activates_ps = 37500; + +#else + popts->tfaw_window_four_activates_ps = pdimm[0].tfaw_ps; +#endif + popts->zq_en = 0; + popts->wrlvl_en = 0; +#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4) + /* + * due to ddr3 dimm is fly-by topology + * we suggest to enable write leveling to + * meet the tQDSS under different loading. + */ + popts->wrlvl_en = 1; + popts->zq_en = 1; + popts->wrlvl_override = 0; +#endif + + /* + * Check interleaving configuration from environment. + * Please refer to doc/README.fsl-ddr for the detail. + * + * If memory controller interleaving is enabled, then the data + * bus widths must be programmed identically for all memory controllers. + * + * Attempt to set all controllers to the same chip select + * interleaving mode. It will do a best effort to get the + * requested ranks interleaved together such that the result + * should be a subset of the requested configuration. + * + * if CONFIG_SYS_FSL_DDR_INTLV_256B is defined, mandatory interleaving + * with 256 Byte is enabled. + */ +#if (CONFIG_NUM_DDR_CONTROLLERS > 1) + if (!hwconfig_sub_f("fsl_ddr", "ctlr_intlv", buf)) +#ifdef CONFIG_SYS_FSL_DDR_INTLV_256B + ; +#else + goto done; +#endif + if (pdimm[0].n_ranks == 0) { + printf("There is no rank on CS0 for controller %d.\n", ctrl_num); + popts->memctl_interleaving = 0; + goto done; + } + popts->memctl_interleaving = 1; +#ifdef CONFIG_SYS_FSL_DDR_INTLV_256B + popts->memctl_interleaving_mode = FSL_DDR_256B_INTERLEAVING; + popts->memctl_interleaving = 1; + debug("256 Byte interleaving\n"); +#else + /* + * test null first. if CONFIG_HWCONFIG is not defined + * hwconfig_arg_cmp returns non-zero + */ + if (hwconfig_subarg_cmp_f("fsl_ddr", "ctlr_intlv", + "null", buf)) { + popts->memctl_interleaving = 0; + debug("memory controller interleaving disabled.\n"); + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "cacheline", buf)) { + popts->memctl_interleaving_mode = + ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ? + 0 : FSL_DDR_CACHE_LINE_INTERLEAVING; + popts->memctl_interleaving = + ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ? + 0 : 1; + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "page", buf)) { + popts->memctl_interleaving_mode = + ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ? + 0 : FSL_DDR_PAGE_INTERLEAVING; + popts->memctl_interleaving = + ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ? + 0 : 1; + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "bank", buf)) { + popts->memctl_interleaving_mode = + ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ? + 0 : FSL_DDR_BANK_INTERLEAVING; + popts->memctl_interleaving = + ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ? + 0 : 1; + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "superbank", buf)) { + popts->memctl_interleaving_mode = + ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ? + 0 : FSL_DDR_SUPERBANK_INTERLEAVING; + popts->memctl_interleaving = + ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ? + 0 : 1; +#if (CONFIG_NUM_DDR_CONTROLLERS == 3) + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "3way_1KB", buf)) { + popts->memctl_interleaving_mode = + FSL_DDR_3WAY_1KB_INTERLEAVING; + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "3way_4KB", buf)) { + popts->memctl_interleaving_mode = + FSL_DDR_3WAY_4KB_INTERLEAVING; + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "3way_8KB", buf)) { + popts->memctl_interleaving_mode = + FSL_DDR_3WAY_8KB_INTERLEAVING; +#elif (CONFIG_NUM_DDR_CONTROLLERS == 4) + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "4way_1KB", buf)) { + popts->memctl_interleaving_mode = + FSL_DDR_4WAY_1KB_INTERLEAVING; + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "4way_4KB", buf)) { + popts->memctl_interleaving_mode = + FSL_DDR_4WAY_4KB_INTERLEAVING; + } else if (hwconfig_subarg_cmp_f("fsl_ddr", + "ctlr_intlv", + "4way_8KB", buf)) { + popts->memctl_interleaving_mode = + FSL_DDR_4WAY_8KB_INTERLEAVING; +#endif + } else { + popts->memctl_interleaving = 0; + printf("hwconfig has unrecognized parameter for ctlr_intlv.\n"); + } +#endif /* CONFIG_SYS_FSL_DDR_INTLV_256B */ +done: +#endif /* CONFIG_NUM_DDR_CONTROLLERS > 1 */ + if ((hwconfig_sub_f("fsl_ddr", "bank_intlv", buf)) && + (CONFIG_CHIP_SELECTS_PER_CTRL > 1)) { + /* test null first. if CONFIG_HWCONFIG is not defined, + * hwconfig_subarg_cmp_f returns non-zero */ + if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv", + "null", buf)) + debug("bank interleaving disabled.\n"); + else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv", + "cs0_cs1", buf)) + popts->ba_intlv_ctl = FSL_DDR_CS0_CS1; + else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv", + "cs2_cs3", buf)) + popts->ba_intlv_ctl = FSL_DDR_CS2_CS3; + else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv", + "cs0_cs1_and_cs2_cs3", buf)) + popts->ba_intlv_ctl = FSL_DDR_CS0_CS1_AND_CS2_CS3; + else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv", + "cs0_cs1_cs2_cs3", buf)) + popts->ba_intlv_ctl = FSL_DDR_CS0_CS1_CS2_CS3; + else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv", + "auto", buf)) + popts->ba_intlv_ctl = auto_bank_intlv(pdimm); + else + printf("hwconfig has unrecognized parameter for bank_intlv.\n"); + switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) { + case FSL_DDR_CS0_CS1_CS2_CS3: +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 1) + if (pdimm[0].n_ranks < 4) { + popts->ba_intlv_ctl = 0; + printf("Not enough bank(chip-select) for " + "CS0+CS1+CS2+CS3 on controller %d, " + "interleaving disabled!\n", ctrl_num); + } +#elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2) +#ifdef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE + if (pdimm[0].n_ranks == 4) + break; +#endif + if ((pdimm[0].n_ranks < 2) && (pdimm[1].n_ranks < 2)) { + popts->ba_intlv_ctl = 0; + printf("Not enough bank(chip-select) for " + "CS0+CS1+CS2+CS3 on controller %d, " + "interleaving disabled!\n", ctrl_num); + } + if (pdimm[0].capacity != pdimm[1].capacity) { + popts->ba_intlv_ctl = 0; + printf("Not identical DIMM size for " + "CS0+CS1+CS2+CS3 on controller %d, " + "interleaving disabled!\n", ctrl_num); + } +#endif + break; + case FSL_DDR_CS0_CS1: + if (pdimm[0].n_ranks < 2) { + popts->ba_intlv_ctl = 0; + printf("Not enough bank(chip-select) for " + "CS0+CS1 on controller %d, " + "interleaving disabled!\n", ctrl_num); + } + break; + case FSL_DDR_CS2_CS3: +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 1) + if (pdimm[0].n_ranks < 4) { + popts->ba_intlv_ctl = 0; + printf("Not enough bank(chip-select) for CS2+CS3 " + "on controller %d, interleaving disabled!\n", ctrl_num); + } +#elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2) + if (pdimm[1].n_ranks < 2) { + popts->ba_intlv_ctl = 0; + printf("Not enough bank(chip-select) for CS2+CS3 " + "on controller %d, interleaving disabled!\n", ctrl_num); + } +#endif + break; + case FSL_DDR_CS0_CS1_AND_CS2_CS3: +#if (CONFIG_DIMM_SLOTS_PER_CTLR == 1) + if (pdimm[0].n_ranks < 4) { + popts->ba_intlv_ctl = 0; + printf("Not enough bank(CS) for CS0+CS1 and " + "CS2+CS3 on controller %d, " + "interleaving disabled!\n", ctrl_num); + } +#elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2) + if ((pdimm[0].n_ranks < 2) || (pdimm[1].n_ranks < 2)) { + popts->ba_intlv_ctl = 0; + printf("Not enough bank(CS) for CS0+CS1 and " + "CS2+CS3 on controller %d, " + "interleaving disabled!\n", ctrl_num); + } +#endif + break; + default: + popts->ba_intlv_ctl = 0; + break; + } + } + + if (hwconfig_sub_f("fsl_ddr", "addr_hash", buf)) { + if (hwconfig_subarg_cmp_f("fsl_ddr", "addr_hash", "null", buf)) + popts->addr_hash = 0; + else if (hwconfig_subarg_cmp_f("fsl_ddr", "addr_hash", + "true", buf)) + popts->addr_hash = 1; + } + + if (pdimm[0].n_ranks == 4) + popts->quad_rank_present = 1; + + ddr_freq = get_ddr_freq(0) / 1000000; + if (popts->registered_dimm_en) { + popts->rcw_override = 1; + popts->rcw_1 = 0x000a5a00; + if (ddr_freq <= 800) + popts->rcw_2 = 0x00000000; + else if (ddr_freq <= 1066) + popts->rcw_2 = 0x00100000; + else if (ddr_freq <= 1333) + popts->rcw_2 = 0x00200000; + else + popts->rcw_2 = 0x00300000; + } + + fsl_ddr_board_options(popts, pdimm, ctrl_num); + + return 0; +} + +void check_interleaving_options(fsl_ddr_info_t *pinfo) +{ + int i, j, k, check_n_ranks, intlv_invalid = 0; + unsigned int check_intlv, check_n_row_addr, check_n_col_addr; + unsigned long long check_rank_density; + struct dimm_params_s *dimm; + /* + * Check if all controllers are configured for memory + * controller interleaving. Identical dimms are recommended. At least + * the size, row and col address should be checked. + */ + j = 0; + check_n_ranks = pinfo->dimm_params[0][0].n_ranks; + check_rank_density = pinfo->dimm_params[0][0].rank_density; + check_n_row_addr = pinfo->dimm_params[0][0].n_row_addr; + check_n_col_addr = pinfo->dimm_params[0][0].n_col_addr; + check_intlv = pinfo->memctl_opts[0].memctl_interleaving_mode; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + dimm = &pinfo->dimm_params[i][0]; + if (!pinfo->memctl_opts[i].memctl_interleaving) { + continue; + } else if (((check_rank_density != dimm->rank_density) || + (check_n_ranks != dimm->n_ranks) || + (check_n_row_addr != dimm->n_row_addr) || + (check_n_col_addr != dimm->n_col_addr) || + (check_intlv != + pinfo->memctl_opts[i].memctl_interleaving_mode))){ + intlv_invalid = 1; + break; + } else { + j++; + } + + } + if (intlv_invalid) { + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) + pinfo->memctl_opts[i].memctl_interleaving = 0; + printf("Not all DIMMs are identical. " + "Memory controller interleaving disabled.\n"); + } else { + switch (check_intlv) { + case FSL_DDR_256B_INTERLEAVING: + case FSL_DDR_CACHE_LINE_INTERLEAVING: + case FSL_DDR_PAGE_INTERLEAVING: + case FSL_DDR_BANK_INTERLEAVING: + case FSL_DDR_SUPERBANK_INTERLEAVING: +#if (3 == CONFIG_NUM_DDR_CONTROLLERS) + k = 2; +#else + k = CONFIG_NUM_DDR_CONTROLLERS; +#endif + break; + case FSL_DDR_3WAY_1KB_INTERLEAVING: + case FSL_DDR_3WAY_4KB_INTERLEAVING: + case FSL_DDR_3WAY_8KB_INTERLEAVING: + case FSL_DDR_4WAY_1KB_INTERLEAVING: + case FSL_DDR_4WAY_4KB_INTERLEAVING: + case FSL_DDR_4WAY_8KB_INTERLEAVING: + default: + k = CONFIG_NUM_DDR_CONTROLLERS; + break; + } + debug("%d of %d controllers are interleaving.\n", j, k); + if (j && (j != k)) { + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) + pinfo->memctl_opts[i].memctl_interleaving = 0; + printf("Not all controllers have compatible " + "interleaving mode. All disabled.\n"); + } + } + debug("Checking interleaving options completed\n"); +} + +int fsl_use_spd(void) +{ + int use_spd = 0; + +#ifdef CONFIG_DDR_SPD + char buffer[HWCONFIG_BUFFER_SIZE]; + char *buf = NULL; + + /* + * Extract hwconfig from environment since we have not properly setup + * the environment but need it for ddr config params + */ + if (getenv_f("hwconfig", buffer, sizeof(buffer)) > 0) + buf = buffer; + + /* if hwconfig is not enabled, or "sdram" is not defined, use spd */ + if (hwconfig_sub_f("fsl_ddr", "sdram", buf)) { + if (hwconfig_subarg_cmp_f("fsl_ddr", "sdram", "spd", buf)) + use_spd = 1; + else if (hwconfig_subarg_cmp_f("fsl_ddr", "sdram", + "fixed", buf)) + use_spd = 0; + else + use_spd = 1; + } else + use_spd = 1; +#endif + + return use_spd; +} diff --git a/qemu/roms/u-boot/drivers/ddr/fsl/util.c b/qemu/roms/u-boot/drivers/ddr/fsl/util.c new file mode 100644 index 000000000..7a22aa398 --- /dev/null +++ b/qemu/roms/u-boot/drivers/ddr/fsl/util.c @@ -0,0 +1,292 @@ +/* + * Copyright 2008-2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include +#ifdef CONFIG_PPC +#include +#endif +#include + +#include +#include +#include + +/* To avoid 64-bit full-divides, we factor this here */ +#define ULL_2E12 2000000000000ULL +#define UL_5POW12 244140625UL +#define UL_2POW13 (1UL << 13) + +#define ULL_8FS 0xFFFFFFFFULL + +u32 fsl_ddr_get_version(void) +{ + struct ccsr_ddr __iomem *ddr; + u32 ver_major_minor_errata; + + ddr = (void *)_DDR_ADDR; + ver_major_minor_errata = (ddr_in32(&ddr->ip_rev1) & 0xFFFF) << 8; + ver_major_minor_errata |= (ddr_in32(&ddr->ip_rev2) & 0xFF00) >> 8; + + return ver_major_minor_errata; +} + +/* + * Round up mclk_ps to nearest 1 ps in memory controller code + * if the error is 0.5ps or more. + * + * If an imprecise data rate is too high due to rounding error + * propagation, compute a suitably rounded mclk_ps to compute + * a working memory controller configuration. + */ +unsigned int get_memory_clk_period_ps(void) +{ + unsigned int data_rate = get_ddr_freq(0); + unsigned int result; + + /* Round to nearest 10ps, being careful about 64-bit multiply/divide */ + unsigned long long rem, mclk_ps = ULL_2E12; + + /* Now perform the big divide, the result fits in 32-bits */ + rem = do_div(mclk_ps, data_rate); + result = (rem >= (data_rate >> 1)) ? mclk_ps + 1 : mclk_ps; + + return result; +} + +/* Convert picoseconds into DRAM clock cycles (rounding up if needed). */ +unsigned int picos_to_mclk(unsigned int picos) +{ + unsigned long long clks, clks_rem; + unsigned long data_rate = get_ddr_freq(0); + + /* Short circuit for zero picos */ + if (!picos) + return 0; + + /* First multiply the time by the data rate (32x32 => 64) */ + clks = picos * (unsigned long long)data_rate; + /* + * Now divide by 5^12 and track the 32-bit remainder, then divide + * by 2*(2^12) using shifts (and updating the remainder). + */ + clks_rem = do_div(clks, UL_5POW12); + clks_rem += (clks & (UL_2POW13-1)) * UL_5POW12; + clks >>= 13; + + /* If we had a remainder greater than the 1ps error, then round up */ + if (clks_rem > data_rate) + clks++; + + /* Clamp to the maximum representable value */ + if (clks > ULL_8FS) + clks = ULL_8FS; + return (unsigned int) clks; +} + +unsigned int mclk_to_picos(unsigned int mclk) +{ + return get_memory_clk_period_ps() * mclk; +} + +#ifdef CONFIG_PPC +void +__fsl_ddr_set_lawbar(const common_timing_params_t *memctl_common_params, + unsigned int law_memctl, + unsigned int ctrl_num) +{ + unsigned long long base = memctl_common_params->base_address; + unsigned long long size = memctl_common_params->total_mem; + + /* + * If no DIMMs on this controller, do not proceed any further. + */ + if (!memctl_common_params->ndimms_present) { + return; + } + +#if !defined(CONFIG_PHYS_64BIT) + if (base >= CONFIG_MAX_MEM_MAPPED) + return; + if ((base + size) >= CONFIG_MAX_MEM_MAPPED) + size = CONFIG_MAX_MEM_MAPPED - base; +#endif + if (set_ddr_laws(base, size, law_memctl) < 0) { + printf("%s: ERROR (ctrl #%d, TRGT ID=%x)\n", __func__, ctrl_num, + law_memctl); + return ; + } + debug("setup ddr law base = 0x%llx, size 0x%llx, TRGT_ID 0x%x\n", + base, size, law_memctl); +} + +__attribute__((weak, alias("__fsl_ddr_set_lawbar"))) void +fsl_ddr_set_lawbar(const common_timing_params_t *memctl_common_params, + unsigned int memctl_interleaved, + unsigned int ctrl_num); +#endif + +void fsl_ddr_set_intl3r(const unsigned int granule_size) +{ +#ifdef CONFIG_E6500 + u32 *mcintl3r = (void *) (CONFIG_SYS_IMMR + 0x18004); + *mcintl3r = 0x80000000 | (granule_size & 0x1f); + debug("Enable MCINTL3R with granule size 0x%x\n", granule_size); +#endif +} + +u32 fsl_ddr_get_intl3r(void) +{ + u32 val = 0; +#ifdef CONFIG_E6500 + u32 *mcintl3r = (void *) (CONFIG_SYS_IMMR + 0x18004); + val = *mcintl3r; +#endif + return val; +} + +void board_add_ram_info(int use_default) +{ + struct ccsr_ddr __iomem *ddr = + (struct ccsr_ddr __iomem *)(CONFIG_SYS_FSL_DDR_ADDR); + +#if defined(CONFIG_E6500) && (CONFIG_NUM_DDR_CONTROLLERS == 3) + u32 *mcintl3r = (void *) (CONFIG_SYS_IMMR + 0x18004); +#endif +#if (CONFIG_NUM_DDR_CONTROLLERS > 1) + uint32_t cs0_config = ddr_in32(&ddr->cs0_config); +#endif + uint32_t sdram_cfg = ddr_in32(&ddr->sdram_cfg); + int cas_lat; + +#if CONFIG_NUM_DDR_CONTROLLERS >= 2 + if (!(sdram_cfg & SDRAM_CFG_MEM_EN)) { + ddr = (void __iomem *)CONFIG_SYS_FSL_DDR2_ADDR; + sdram_cfg = ddr_in32(&ddr->sdram_cfg); + } +#endif +#if CONFIG_NUM_DDR_CONTROLLERS >= 3 + if (!(sdram_cfg & SDRAM_CFG_MEM_EN)) { + ddr = (void __iomem *)CONFIG_SYS_FSL_DDR3_ADDR; + sdram_cfg = ddr_in32(&ddr->sdram_cfg); + } +#endif + puts(" (DDR"); + switch ((sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) >> + SDRAM_CFG_SDRAM_TYPE_SHIFT) { + case SDRAM_TYPE_DDR1: + puts("1"); + break; + case SDRAM_TYPE_DDR2: + puts("2"); + break; + case SDRAM_TYPE_DDR3: + puts("3"); + break; + case SDRAM_TYPE_DDR4: + puts("4"); + break; + default: + puts("?"); + break; + } + + if (sdram_cfg & SDRAM_CFG_32_BE) + puts(", 32-bit"); + else if (sdram_cfg & SDRAM_CFG_16_BE) + puts(", 16-bit"); + else + puts(", 64-bit"); + + /* Calculate CAS latency based on timing cfg values */ + cas_lat = ((ddr_in32(&ddr->timing_cfg_1) >> 16) & 0xf); + if (fsl_ddr_get_version() <= 0x40400) + cas_lat += 1; + else + cas_lat += 2; + cas_lat += ((ddr_in32(&ddr->timing_cfg_3) >> 12) & 3) << 4; + printf(", CL=%d", cas_lat >> 1); + if (cas_lat & 0x1) + puts(".5"); + + if (sdram_cfg & SDRAM_CFG_ECC_EN) + puts(", ECC on)"); + else + puts(", ECC off)"); + +#if (CONFIG_NUM_DDR_CONTROLLERS == 3) +#ifdef CONFIG_E6500 + if (*mcintl3r & 0x80000000) { + puts("\n"); + puts(" DDR Controller Interleaving Mode: "); + switch (*mcintl3r & 0x1f) { + case FSL_DDR_3WAY_1KB_INTERLEAVING: + puts("3-way 1KB"); + break; + case FSL_DDR_3WAY_4KB_INTERLEAVING: + puts("3-way 4KB"); + break; + case FSL_DDR_3WAY_8KB_INTERLEAVING: + puts("3-way 8KB"); + break; + default: + puts("3-way UNKNOWN"); + break; + } + } +#endif +#endif +#if (CONFIG_NUM_DDR_CONTROLLERS >= 2) + if (cs0_config & 0x20000000) { + puts("\n"); + puts(" DDR Controller Interleaving Mode: "); + + switch ((cs0_config >> 24) & 0xf) { + case FSL_DDR_256B_INTERLEAVING: + puts("256B"); + break; + case FSL_DDR_CACHE_LINE_INTERLEAVING: + puts("cache line"); + break; + case FSL_DDR_PAGE_INTERLEAVING: + puts("page"); + break; + case FSL_DDR_BANK_INTERLEAVING: + puts("bank"); + break; + case FSL_DDR_SUPERBANK_INTERLEAVING: + puts("super-bank"); + break; + default: + puts("invalid"); + break; + } + } +#endif + + if ((sdram_cfg >> 8) & 0x7f) { + puts("\n"); + puts(" DDR Chip-Select Interleaving Mode: "); + switch(sdram_cfg >> 8 & 0x7f) { + case FSL_DDR_CS0_CS1_CS2_CS3: + puts("CS0+CS1+CS2+CS3"); + break; + case FSL_DDR_CS0_CS1: + puts("CS0+CS1"); + break; + case FSL_DDR_CS2_CS3: + puts("CS2+CS3"); + break; + case FSL_DDR_CS0_CS1_AND_CS2_CS3: + puts("CS0+CS1 and CS2+CS3"); + break; + default: + puts("invalid"); + break; + } + } +} diff --git a/qemu/roms/u-boot/drivers/demo/Makefile b/qemu/roms/u-boot/drivers/demo/Makefile new file mode 100644 index 000000000..baaa2baa4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/demo/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (c) 2013 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_DM_DEMO) += demo-uclass.o demo-pdata.o +obj-$(CONFIG_DM_DEMO_SIMPLE) += demo-simple.o +obj-$(CONFIG_DM_DEMO_SHAPE) += demo-shape.o diff --git a/qemu/roms/u-boot/drivers/demo/demo-pdata.c b/qemu/roms/u-boot/drivers/demo/demo-pdata.c new file mode 100644 index 000000000..e92841db6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/demo/demo-pdata.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +static const struct dm_demo_pdata red_square = { + .colour = "red", + .sides = 4. +}; +static const struct dm_demo_pdata green_triangle = { + .colour = "green", + .sides = 3. +}; +static const struct dm_demo_pdata yellow_hexagon = { + .colour = "yellow", + .sides = 6. +}; + +U_BOOT_DEVICE(demo0) = { + .name = "demo_shape_drv", + .platdata = &red_square, +}; + +U_BOOT_DEVICE(demo1) = { + .name = "demo_simple_drv", + .platdata = &red_square, +}; + +U_BOOT_DEVICE(demo2) = { + .name = "demo_shape_drv", + .platdata = &green_triangle, +}; + +U_BOOT_DEVICE(demo3) = { + .name = "demo_simple_drv", + .platdata = &yellow_hexagon, +}; + +U_BOOT_DEVICE(demo4) = { + .name = "demo_shape_drv", + .platdata = &yellow_hexagon, +}; diff --git a/qemu/roms/u-boot/drivers/demo/demo-shape.c b/qemu/roms/u-boot/drivers/demo/demo-shape.c new file mode 100644 index 000000000..2f0eb96bb --- /dev/null +++ b/qemu/roms/u-boot/drivers/demo/demo-shape.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Shape size */ +#define WIDTH 8 +#define HEIGHT 6 + +struct shape_data { + int num_chars; /* Number of non-space characters output so far */ +}; + +/* Crazy little function to draw shapes on the console */ +static int shape_hello(struct device *dev, int ch) +{ + const struct dm_demo_pdata *pdata = dev_get_platdata(dev); + struct shape_data *data = dev_get_priv(dev); + static const struct shape { + int start; + int end; + int dstart; + int dend; + } shapes[3] = { + { 0, 1, 0, 1 }, + { 0, WIDTH, 0, 0 }, + { HEIGHT / 2 - 1, WIDTH - HEIGHT / 2 + 1, -1, 1}, + }; + struct shape shape; + unsigned int index; + int line, pos, inside; + const char *colour = pdata->colour; + int first = 0; + + if (!ch) + ch = pdata->default_char; + if (!ch) + ch = '@'; + + index = (pdata->sides / 2) - 1; + if (index >= ARRAY_SIZE(shapes)) + return -EIO; + shape = shapes[index]; + + for (line = 0; line < HEIGHT; line++) { + first = 1; + for (pos = 0; pos < WIDTH; pos++) { + inside = pos >= shape.start && pos < shape.end; + if (inside) { + putc(first ? *colour++ : ch); + data->num_chars++; + first = 0; + if (!*colour) + colour = pdata->colour; + } else { + putc(' '); + } + } + putc('\n'); + shape.start += shape.dstart; + shape.end += shape.dend; + if (shape.start < 0) { + shape.dstart = -shape.dstart; + shape.dend = -shape.dend; + shape.start += shape.dstart; + shape.end += shape.dend; + } + } + + return 0; +} + +static int shape_status(struct device *dev, int *status) +{ + struct shape_data *data = dev_get_priv(dev); + + *status = data->num_chars; + return 0; +} + +static const struct demo_ops shape_ops = { + .hello = shape_hello, + .status = shape_status, +}; + +static int shape_ofdata_to_platdata(struct device *dev) +{ + struct dm_demo_pdata *pdata = dev_get_platdata(dev); + int ret; + + /* Parse the data that is common with all demo devices */ + ret = demo_parse_dt(dev); + if (ret) + return ret; + + /* Parse the data that only we need */ + pdata->default_char = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "character", '@'); + + return 0; +} + +static const struct device_id demo_shape_id[] = { + { "demo-shape", 0 }, + { }, +}; + +U_BOOT_DRIVER(demo_shape_drv) = { + .name = "demo_shape_drv", + .of_match = demo_shape_id, + .id = UCLASS_DEMO, + .ofdata_to_platdata = shape_ofdata_to_platdata, + .ops = &shape_ops, + .priv_auto_alloc_size = sizeof(struct shape_data), + .platdata_auto_alloc_size = sizeof(struct dm_demo_pdata), +}; diff --git a/qemu/roms/u-boot/drivers/demo/demo-simple.c b/qemu/roms/u-boot/drivers/demo/demo-simple.c new file mode 100644 index 000000000..6ba813172 --- /dev/null +++ b/qemu/roms/u-boot/drivers/demo/demo-simple.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static int simple_hello(struct device *dev, int ch) +{ + const struct dm_demo_pdata *pdata = dev_get_platdata(dev); + + printf("Hello from %08x: %s %d\n", map_to_sysmem(dev), pdata->colour, + pdata->sides); + + return 0; +} + +static const struct demo_ops simple_ops = { + .hello = simple_hello, +}; + +static int demo_shape_ofdata_to_platdata(struct device *dev) +{ + /* Parse the data that is common with all demo devices */ + return demo_parse_dt(dev); +} + +static const struct device_id demo_shape_id[] = { + { "demo-simple", 0 }, + { }, +}; + +U_BOOT_DRIVER(demo_simple_drv) = { + .name = "demo_simple_drv", + .of_match = demo_shape_id, + .id = UCLASS_DEMO, + .ofdata_to_platdata = demo_shape_ofdata_to_platdata, + .ops = &simple_ops, + .platdata_auto_alloc_size = sizeof(struct dm_demo_pdata), +}; diff --git a/qemu/roms/u-boot/drivers/demo/demo-uclass.c b/qemu/roms/u-boot/drivers/demo/demo-uclass.c new file mode 100644 index 000000000..48588be90 --- /dev/null +++ b/qemu/roms/u-boot/drivers/demo/demo-uclass.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(demo) = { + .id = UCLASS_DEMO, +}; + +int demo_hello(struct device *dev, int ch) +{ + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->hello) + return -ENOSYS; + + return ops->hello(dev, ch); +} + +int demo_status(struct device *dev, int *status) +{ + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->status) + return -ENOSYS; + + return ops->status(dev, status); +} + +int demo_parse_dt(struct device *dev) +{ + struct dm_demo_pdata *pdata = dev_get_platdata(dev); + int dn = dev->of_offset; + + pdata->sides = fdtdec_get_int(gd->fdt_blob, dn, "sides", 0); + pdata->colour = fdt_getprop(gd->fdt_blob, dn, "colour", NULL); + if (!pdata->sides || !pdata->colour) { + debug("%s: Invalid device tree data\n", __func__); + return -EINVAL; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/dfu/Makefile b/qemu/roms/u-boot/drivers/dfu/Makefile new file mode 100644 index 000000000..def628dcd --- /dev/null +++ b/qemu/roms/u-boot/drivers/dfu/Makefile @@ -0,0 +1,11 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_DFU_FUNCTION) += dfu.o +obj-$(CONFIG_DFU_MMC) += dfu_mmc.o +obj-$(CONFIG_DFU_NAND) += dfu_nand.o +obj-$(CONFIG_DFU_RAM) += dfu_ram.o diff --git a/qemu/roms/u-boot/drivers/dfu/dfu.c b/qemu/roms/u-boot/drivers/dfu/dfu.c new file mode 100644 index 000000000..51b10263b --- /dev/null +++ b/qemu/roms/u-boot/drivers/dfu/dfu.c @@ -0,0 +1,466 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static bool dfu_reset_request; +static LIST_HEAD(dfu_list); +static int dfu_alt_num; +static int alt_num_cnt; + +bool dfu_reset(void) +{ + return dfu_reset_request; +} + +void dfu_trigger_reset() +{ + dfu_reset_request = true; +} + +static int dfu_find_alt_num(const char *s) +{ + int i = 0; + + for (; *s; s++) + if (*s == ';') + i++; + + return ++i; +} + +int dfu_init_env_entities(char *interface, int dev) +{ + const char *str_env; + char *env_bkp; + int ret; + + str_env = getenv("dfu_alt_info"); + if (!str_env) { + error("\"dfu_alt_info\" env variable not defined!\n"); + return -EINVAL; + } + + env_bkp = strdup(str_env); + ret = dfu_config_entities(env_bkp, interface, dev); + if (ret) { + error("DFU entities configuration failed!\n"); + return ret; + } + + free(env_bkp); + return 0; +} + +static unsigned char *dfu_buf; +static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE; + +unsigned char *dfu_free_buf(void) +{ + free(dfu_buf); + dfu_buf = NULL; + return dfu_buf; +} + +unsigned long dfu_get_buf_size(void) +{ + return dfu_buf_size; +} + +unsigned char *dfu_get_buf(void) +{ + char *s; + + if (dfu_buf != NULL) + return dfu_buf; + + s = getenv("dfu_bufsiz"); + dfu_buf_size = s ? (unsigned long)simple_strtol(s, NULL, 16) : + CONFIG_SYS_DFU_DATA_BUF_SIZE; + + dfu_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, dfu_buf_size); + if (dfu_buf == NULL) + printf("%s: Could not memalign 0x%lx bytes\n", + __func__, dfu_buf_size); + + return dfu_buf; +} + +static int dfu_write_buffer_drain(struct dfu_entity *dfu) +{ + long w_size; + int ret; + + /* flush size? */ + w_size = dfu->i_buf - dfu->i_buf_start; + if (w_size == 0) + return 0; + + /* update CRC32 */ + dfu->crc = crc32(dfu->crc, dfu->i_buf_start, w_size); + + ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start, &w_size); + if (ret) + debug("%s: Write error!\n", __func__); + + /* point back */ + dfu->i_buf = dfu->i_buf_start; + + /* update offset */ + dfu->offset += w_size; + + puts("#"); + + return ret; +} + +int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + int ret = 0; + + if (dfu->flush_medium) + ret = dfu->flush_medium(dfu); + + printf("\nDFU complete CRC32: 0x%08x\n", dfu->crc); + + /* clear everything */ + dfu_free_buf(); + dfu->crc = 0; + dfu->offset = 0; + dfu->i_blk_seq_num = 0; + dfu->i_buf_start = dfu_buf; + dfu->i_buf_end = dfu_buf; + dfu->i_buf = dfu->i_buf_start; + dfu->inited = 0; + + return ret; +} + +int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + int ret = 0; + int tret; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x offset: 0x%llx bufoffset: 0x%x\n", + __func__, dfu->name, buf, size, blk_seq_num, dfu->offset, + dfu->i_buf - dfu->i_buf_start); + + if (!dfu->inited) { + /* initial state */ + dfu->crc = 0; + dfu->offset = 0; + dfu->bad_skip = 0; + dfu->i_blk_seq_num = 0; + dfu->i_buf_start = dfu_get_buf(); + if (dfu->i_buf_start == NULL) + return -ENOMEM; + dfu->i_buf_end = dfu_get_buf() + dfu_buf_size; + dfu->i_buf = dfu->i_buf_start; + + dfu->inited = 1; + } + + if (dfu->i_blk_seq_num != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, dfu->i_blk_seq_num, blk_seq_num); + return -1; + } + + /* DFU 1.1 standard says: + * The wBlockNum field is a block sequence number. It increments each + * time a block is transferred, wrapping to zero from 65,535. It is used + * to provide useful context to the DFU loader in the device." + * + * This means that it's a 16 bit counter that roll-overs at + * 0xffff -> 0x0000. By having a typical 4K transfer block + * we roll-over at exactly 256MB. Not very fun to debug. + * + * Handling rollover, and having an inited variable, + * makes things work. + */ + + /* handle rollover */ + dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff; + + /* flush buffer if overflow */ + if ((dfu->i_buf + size) > dfu->i_buf_end) { + tret = dfu_write_buffer_drain(dfu); + if (ret == 0) + ret = tret; + } + + /* we should be in buffer now (if not then size too large) */ + if ((dfu->i_buf + size) > dfu->i_buf_end) { + error("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf, + size, dfu->i_buf_end); + return -1; + } + + memcpy(dfu->i_buf, buf, size); + dfu->i_buf += size; + + /* if end or if buffer full flush */ + if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) { + tret = dfu_write_buffer_drain(dfu); + if (ret == 0) + ret = tret; + } + + return ret; +} + +static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size) +{ + long chunk; + int ret, readn; + + readn = 0; + while (size > 0) { + /* get chunk that can be read */ + chunk = min(size, dfu->b_left); + /* consume */ + if (chunk > 0) { + memcpy(buf, dfu->i_buf, chunk); + dfu->crc = crc32(dfu->crc, buf, chunk); + dfu->i_buf += chunk; + dfu->b_left -= chunk; + dfu->r_left -= chunk; + size -= chunk; + buf += chunk; + readn += chunk; + } + + /* all done */ + if (size > 0) { + /* no more to read */ + if (dfu->r_left == 0) + break; + + dfu->i_buf = dfu->i_buf_start; + dfu->b_left = dfu->i_buf_end - dfu->i_buf_start; + + /* got to read, but buffer is empty */ + if (dfu->b_left > dfu->r_left) + dfu->b_left = dfu->r_left; + ret = dfu->read_medium(dfu, dfu->offset, dfu->i_buf, + &dfu->b_left); + if (ret != 0) { + debug("%s: Read error!\n", __func__); + return ret; + } + dfu->offset += dfu->b_left; + dfu->r_left -= dfu->b_left; + + puts("#"); + } + } + + return readn; +} + +int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ + int ret = 0; + + debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", + __func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf); + + if (!dfu->inited) { + dfu->i_buf_start = dfu_get_buf(); + if (dfu->i_buf_start == NULL) + return -ENOMEM; + + ret = dfu->read_medium(dfu, 0, dfu->i_buf_start, &dfu->r_left); + if (ret != 0) { + debug("%s: failed to get r_left\n", __func__); + return ret; + } + + debug("%s: %s %ld [B]\n", __func__, dfu->name, dfu->r_left); + + dfu->i_blk_seq_num = 0; + dfu->crc = 0; + dfu->offset = 0; + dfu->i_buf_end = dfu_get_buf() + dfu_buf_size; + dfu->i_buf = dfu->i_buf_start; + dfu->b_left = min(dfu_buf_size, dfu->r_left); + + dfu->bad_skip = 0; + + dfu->inited = 1; + } + + if (dfu->i_blk_seq_num != blk_seq_num) { + printf("%s: Wrong sequence number! [%d] [%d]\n", + __func__, dfu->i_blk_seq_num, blk_seq_num); + return -1; + } + /* handle rollover */ + dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff; + + ret = dfu_read_buffer_fill(dfu, buf, size); + if (ret < 0) { + printf("%s: Failed to fill buffer\n", __func__); + return -1; + } + + if (ret < size) { + debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, dfu->crc); + puts("\nUPLOAD ... done\nCtrl+C to exit ...\n"); + + dfu_free_buf(); + dfu->i_blk_seq_num = 0; + dfu->crc = 0; + dfu->offset = 0; + dfu->i_buf_start = dfu_buf; + dfu->i_buf_end = dfu_buf; + dfu->i_buf = dfu->i_buf_start; + dfu->b_left = 0; + + dfu->bad_skip = 0; + + dfu->inited = 0; + } + + return ret; +} + +static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, + char *interface, int num) +{ + char *st; + + debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num); + st = strsep(&s, " "); + strcpy(dfu->name, st); + + dfu->dev_num = num; + dfu->alt = alt; + + /* Specific for mmc device */ + if (strcmp(interface, "mmc") == 0) { + if (dfu_fill_entity_mmc(dfu, s)) + return -1; + } else if (strcmp(interface, "nand") == 0) { + if (dfu_fill_entity_nand(dfu, s)) + return -1; + } else if (strcmp(interface, "ram") == 0) { + if (dfu_fill_entity_ram(dfu, s)) + return -1; + } else { + printf("%s: Device %s not (yet) supported!\n", + __func__, interface); + return -1; + } + + return 0; +} + +void dfu_free_entities(void) +{ + struct dfu_entity *dfu, *p, *t = NULL; + + list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { + list_del(&dfu->list); + t = dfu; + } + if (t) + free(t); + INIT_LIST_HEAD(&dfu_list); + + alt_num_cnt = 0; +} + +int dfu_config_entities(char *env, char *interface, int num) +{ + struct dfu_entity *dfu; + int i, ret; + char *s; + + dfu_alt_num = dfu_find_alt_num(env); + debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num); + + dfu = calloc(sizeof(*dfu), dfu_alt_num); + if (!dfu) + return -1; + for (i = 0; i < dfu_alt_num; i++) { + + s = strsep(&env, ";"); + ret = dfu_fill_entity(&dfu[i], s, alt_num_cnt, interface, num); + if (ret) + return -1; + + list_add_tail(&dfu[i].list, &dfu_list); + alt_num_cnt++; + } + + return 0; +} + +const char *dfu_get_dev_type(enum dfu_device_type t) +{ + const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM" }; + return dev_t[t]; +} + +const char *dfu_get_layout(enum dfu_layout l) +{ + const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", + "EXT3", "EXT4", "RAM_ADDR" }; + return dfu_layout[l]; +} + +void dfu_show_entities(void) +{ + struct dfu_entity *dfu; + + puts("DFU alt settings list:\n"); + + list_for_each_entry(dfu, &dfu_list, list) { + printf("dev: %s alt: %d name: %s layout: %s\n", + dfu_get_dev_type(dfu->dev_type), dfu->alt, + dfu->name, dfu_get_layout(dfu->layout)); + } +} + +int dfu_get_alt_number(void) +{ + return dfu_alt_num; +} + +struct dfu_entity *dfu_get_entity(int alt) +{ + struct dfu_entity *dfu; + + list_for_each_entry(dfu, &dfu_list, list) { + if (dfu->alt == alt) + return dfu; + } + + return NULL; +} + +int dfu_get_alt(char *name) +{ + struct dfu_entity *dfu; + + list_for_each_entry(dfu, &dfu_list, list) { + if (!strncmp(dfu->name, name, strlen(dfu->name))) + return dfu->alt; + } + + return -ENODEV; +} diff --git a/qemu/roms/u-boot/drivers/dfu/dfu_mmc.c b/qemu/roms/u-boot/drivers/dfu/dfu_mmc.c new file mode 100644 index 000000000..5e10ea7e6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/dfu/dfu_mmc.c @@ -0,0 +1,279 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) + dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; +static long dfu_file_buf_len; + +static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + struct mmc *mmc = find_mmc_device(dfu->dev_num); + u32 blk_start, blk_count, n = 0; + + /* + * We must ensure that we work in lba_blk_size chunks, so ALIGN + * this value. + */ + *len = ALIGN(*len, dfu->data.mmc.lba_blk_size); + + blk_start = dfu->data.mmc.lba_start + + (u32)lldiv(offset, dfu->data.mmc.lba_blk_size); + blk_count = *len / dfu->data.mmc.lba_blk_size; + if (blk_start + blk_count > + dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) { + puts("Request would exceed designated area!\n"); + return -EINVAL; + } + + debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__, + op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num, + blk_start, blk_count, buf); + switch (op) { + case DFU_OP_READ: + n = mmc->block_dev.block_read(dfu->dev_num, blk_start, + blk_count, buf); + break; + case DFU_OP_WRITE: + n = mmc->block_dev.block_write(dfu->dev_num, blk_start, + blk_count, buf); + break; + default: + error("Operation not supported\n"); + } + + if (n != blk_count) { + error("MMC operation failed"); + return -EIO; + } + + return 0; +} + +static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len) +{ + if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) { + dfu_file_buf_len = 0; + return -EINVAL; + } + + /* Add to the current buffer. */ + memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len); + dfu_file_buf_len += *len; + + return 0; +} + +static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu, + void *buf, long *len) +{ + char cmd_buf[DFU_CMD_BUF_SIZE]; + char *str_env; + int ret; + + switch (dfu->layout) { + case DFU_FS_FAT: + sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s", + op == DFU_OP_READ ? "load" : "write", + dfu->data.mmc.dev, dfu->data.mmc.part, + (unsigned int) buf, dfu->name); + break; + case DFU_FS_EXT4: + sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s", + op == DFU_OP_READ ? "load" : "write", + dfu->data.mmc.dev, dfu->data.mmc.part, + (unsigned int) buf, dfu->name); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + return -1; + } + + if (op == DFU_OP_WRITE) + sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len); + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + + ret = run_command(cmd_buf, 0); + if (ret) { + puts("dfu: Read error!\n"); + return ret; + } + + if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) { + str_env = getenv("filesize"); + if (str_env == NULL) { + puts("dfu: Wrong file size!\n"); + return -1; + } + *len = simple_strtoul(str_env, NULL, 16); + } + + return ret; +} + +int dfu_write_medium_mmc(struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len); + break; + case DFU_FS_FAT: + case DFU_FS_EXT4: + ret = mmc_file_buffer(dfu, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +int dfu_flush_medium_mmc(struct dfu_entity *dfu) +{ + int ret = 0; + + if (dfu->layout != DFU_RAW_ADDR) { + /* Do stuff here. */ + ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf, + &dfu_file_buf_len); + + /* Now that we're done */ + dfu_file_buf_len = 0; + } + + return ret; +} + +int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, + long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len); + break; + case DFU_FS_FAT: + case DFU_FS_EXT4: + ret = mmc_file_op(DFU_OP_READ, dfu, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +/* + * @param s Parameter string containing space-separated arguments: + * 1st: + * raw (raw read/write) + * fat (files) + * ext4 (^) + * part (partition image) + * 2nd and 3rd: + * lba_start and lba_size, for raw write + * mmc_dev and mmc_part, for filesystems and part + */ +int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) +{ + const char *entity_type; + size_t second_arg; + size_t third_arg; + + struct mmc *mmc; + + const char *argv[3]; + const char **parg = argv; + + for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) { + *parg = strsep(&s, " "); + if (*parg == NULL) { + error("Invalid number of arguments.\n"); + return -ENODEV; + } + } + + entity_type = argv[0]; + /* + * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8, + * with default 10. + */ + second_arg = simple_strtoul(argv[1], NULL, 0); + third_arg = simple_strtoul(argv[2], NULL, 0); + + mmc = find_mmc_device(dfu->dev_num); + if (mmc == NULL) { + error("Couldn't find MMC device no. %d.\n", dfu->dev_num); + return -ENODEV; + } + + if (mmc_init(mmc)) { + error("Couldn't init MMC device.\n"); + return -ENODEV; + } + + if (!strcmp(entity_type, "raw")) { + dfu->layout = DFU_RAW_ADDR; + dfu->data.mmc.lba_start = second_arg; + dfu->data.mmc.lba_size = third_arg; + dfu->data.mmc.lba_blk_size = mmc->read_bl_len; + } else if (!strcmp(entity_type, "part")) { + disk_partition_t partinfo; + block_dev_desc_t *blk_dev = &mmc->block_dev; + int mmcdev = second_arg; + int mmcpart = third_arg; + + if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) { + error("Couldn't find part #%d on mmc device #%d\n", + mmcpart, mmcdev); + return -ENODEV; + } + + dfu->layout = DFU_RAW_ADDR; + dfu->data.mmc.lba_start = partinfo.start; + dfu->data.mmc.lba_size = partinfo.size; + dfu->data.mmc.lba_blk_size = partinfo.blksz; + } else if (!strcmp(entity_type, "fat")) { + dfu->layout = DFU_FS_FAT; + } else if (!strcmp(entity_type, "ext4")) { + dfu->layout = DFU_FS_EXT4; + } else { + error("Memory layout (%s) not supported!\n", entity_type); + return -ENODEV; + } + + /* if it's NOT a raw write */ + if (strcmp(entity_type, "raw")) { + dfu->data.mmc.dev = second_arg; + dfu->data.mmc.part = third_arg; + } + + dfu->dev_type = DFU_DEV_MMC; + dfu->read_medium = dfu_read_medium_mmc; + dfu->write_medium = dfu_write_medium_mmc; + dfu->flush_medium = dfu_flush_medium_mmc; + dfu->inited = 0; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/dfu/dfu_nand.c b/qemu/roms/u-boot/drivers/dfu/dfu_nand.c new file mode 100644 index 000000000..2d07097e8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/dfu/dfu_nand.c @@ -0,0 +1,219 @@ +/* + * dfu_nand.c -- DFU for NAND routines. + * + * Copyright (C) 2012-2013 Texas Instruments, Inc. + * + * Based on dfu_mmc.c which is: + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + loff_t start, lim; + size_t count, actual; + int ret; + nand_info_t *nand; + + /* if buf == NULL return total size of the area */ + if (buf == NULL) { + *len = dfu->data.nand.size; + return 0; + } + + start = dfu->data.nand.start + offset + dfu->bad_skip; + lim = dfu->data.nand.start + dfu->data.nand.size - start; + count = *len; + + if (nand_curr_device < 0 || + nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + printf("%s: invalid nand device\n", __func__); + return -1; + } + + nand = &nand_info[nand_curr_device]; + + if (op == DFU_OP_READ) { + ret = nand_read_skip_bad(nand, start, &count, &actual, + lim, buf); + } else { + nand_erase_options_t opts; + + memset(&opts, 0, sizeof(opts)); + opts.offset = start; + opts.length = count; + opts.spread = 1; + opts.quiet = 1; + opts.lim = lim; + /* first erase */ + ret = nand_erase_opts(nand, &opts); + if (ret) + return ret; + /* then write */ + ret = nand_write_skip_bad(nand, start, &count, &actual, + lim, buf, 0); + } + + if (ret != 0) { + printf("%s: nand_%s_skip_bad call failed at %llx!\n", + __func__, op == DFU_OP_READ ? "read" : "write", + start); + return ret; + } + + /* + * Find out where we stopped writing data. This can be deeper into + * the NAND than we expected due to having to skip bad blocks. So + * we must take this into account for the next write, if any. + */ + if (actual > count) + dfu->bad_skip += actual - count; + + return ret; +} + +static inline int nand_block_write(struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len); +} + +static inline int nand_block_read(struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + return nand_block_op(DFU_OP_READ, dfu, offset, buf, len); +} + +static int dfu_write_medium_nand(struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = nand_block_write(dfu, offset, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf, + long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + *len = dfu->data.nand.size; + ret = nand_block_read(dfu, offset, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +static int dfu_flush_medium_nand(struct dfu_entity *dfu) +{ + int ret = 0; + + /* in case of ubi partition, erase rest of the partition */ + if (dfu->data.nand.ubi) { + nand_info_t *nand; + nand_erase_options_t opts; + + if (nand_curr_device < 0 || + nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + printf("%s: invalid nand device\n", __func__); + return -1; + } + + nand = &nand_info[nand_curr_device]; + + memset(&opts, 0, sizeof(opts)); + opts.offset = dfu->data.nand.start + dfu->offset + + dfu->bad_skip; + opts.length = dfu->data.nand.start + + dfu->data.nand.size - opts.offset; + ret = nand_erase_opts(nand, &opts); + if (ret != 0) + printf("Failure erase: %d\n", ret); + } + + return ret; +} + +int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) +{ + char *st; + int ret, dev, part; + + dfu->data.nand.ubi = 0; + dfu->dev_type = DFU_DEV_NAND; + st = strsep(&s, " "); + if (!strcmp(st, "raw")) { + dfu->layout = DFU_RAW_ADDR; + dfu->data.nand.start = simple_strtoul(s, &s, 16); + s++; + dfu->data.nand.size = simple_strtoul(s, &s, 16); + } else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) { + char mtd_id[32]; + struct mtd_device *mtd_dev; + u8 part_num; + struct part_info *pi; + + dfu->layout = DFU_RAW_ADDR; + + dev = simple_strtoul(s, &s, 10); + s++; + part = simple_strtoul(s, &s, 10); + + sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1); + printf("using id '%s'\n", mtd_id); + + mtdparts_init(); + + ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); + if (ret != 0) { + printf("Could not locate '%s'\n", mtd_id); + return -1; + } + + dfu->data.nand.start = pi->offset; + dfu->data.nand.size = pi->size; + if (!strcmp(st, "partubi")) + dfu->data.nand.ubi = 1; + } else { + printf("%s: Memory layout (%s) not supported!\n", __func__, st); + return -1; + } + + dfu->read_medium = dfu_read_medium_nand; + dfu->write_medium = dfu_write_medium_nand; + dfu->flush_medium = dfu_flush_medium_nand; + + /* initial state */ + dfu->inited = 0; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/dfu/dfu_ram.c b/qemu/roms/u-boot/drivers/dfu/dfu_ram.c new file mode 100644 index 000000000..335a8e1f2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/dfu/dfu_ram.c @@ -0,0 +1,77 @@ +/* + * (C) Copyright 2013 + * Afzal Mohammed + * + * Reference: dfu_mmc.c + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static int dfu_transfer_medium_ram(enum dfu_op op, struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + if (dfu->layout != DFU_RAM_ADDR) { + error("unsupported layout: %s\n", dfu_get_layout(dfu->layout)); + return -EINVAL; + } + + if (offset > dfu->data.ram.size) { + error("request exceeds allowed area\n"); + return -EINVAL; + } + + if (op == DFU_OP_WRITE) + memcpy(dfu->data.ram.start + offset, buf, *len); + else + memcpy(buf, dfu->data.ram.start + offset, *len); + + return 0; +} + +static int dfu_write_medium_ram(struct dfu_entity *dfu, u64 offset, + void *buf, long *len) +{ + return dfu_transfer_medium_ram(DFU_OP_WRITE, dfu, offset, buf, len); +} + +static int dfu_read_medium_ram(struct dfu_entity *dfu, u64 offset, + void *buf, long *len) +{ + if (!*len) { + *len = dfu->data.ram.size; + return 0; + } + + return dfu_transfer_medium_ram(DFU_OP_READ, dfu, offset, buf, len); +} + +int dfu_fill_entity_ram(struct dfu_entity *dfu, char *s) +{ + char *st; + + dfu->dev_type = DFU_DEV_RAM; + st = strsep(&s, " "); + if (strcmp(st, "ram")) { + error("unsupported device: %s\n", st); + return -ENODEV; + } + + dfu->layout = DFU_RAM_ADDR; + dfu->data.ram.start = (void *)simple_strtoul(s, &s, 16); + s++; + dfu->data.ram.size = simple_strtoul(s, &s, 16); + + dfu->write_medium = dfu_write_medium_ram; + dfu->read_medium = dfu_read_medium_ram; + + dfu->inited = 0; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/dma/MCD_dmaApi.c b/qemu/roms/u-boot/drivers/dma/MCD_dmaApi.c new file mode 100644 index 000000000..74dba1856 --- /dev/null +++ b/qemu/roms/u-boot/drivers/dma/MCD_dmaApi.c @@ -0,0 +1,1011 @@ +/* + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/*Main C file for multi-channel DMA API. */ + +#include + +#include +#include +#include + +/********************************************************************/ +/* This is an API-internal pointer to the DMA's registers */ +dmaRegs *MCD_dmaBar; + +/* + * These are the real and model task tables as generated by the + * build process + */ +extern TaskTableEntry MCD_realTaskTableSrc[NCHANNELS]; +extern TaskTableEntry MCD_modelTaskTableSrc[NUMOFVARIANTS]; + +/* + * However, this (usually) gets relocated to on-chip SRAM, at which + * point we access them as these tables + */ +volatile TaskTableEntry *MCD_taskTable; +TaskTableEntry *MCD_modelTaskTable; + +/* + * MCD_chStatus[] is an array of status indicators for remembering + * whether a DMA has ever been attempted on each channel, pausing + * status, etc. + */ +static int MCD_chStatus[NCHANNELS] = { + MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, + MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, + MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, + MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA +}; + +/* Prototypes for local functions */ +static void MCD_memcpy(int *dest, int *src, u32 size); +static void MCD_resmActions(int channel); + +/* + * Buffer descriptors used for storage of progress info for single Dmas + * Also used as storage for the DMA for CRCs for single DMAs + * Otherwise, the DMA does not parse these buffer descriptors + */ +#ifdef MCD_INCLUDE_EU +extern MCD_bufDesc MCD_singleBufDescs[NCHANNELS]; +#else +MCD_bufDesc MCD_singleBufDescs[NCHANNELS]; +#endif +MCD_bufDesc *MCD_relocBuffDesc; + +/* Defines for the debug control register's functions */ +#define DBG_CTL_COMP1_TASK (0x00002000) +#define DBG_CTL_ENABLE (DBG_CTL_AUTO_ARM | \ + DBG_CTL_BREAK | \ + DBG_CTL_INT_BREAK | \ + DBG_CTL_COMP1_TASK) +#define DBG_CTL_DISABLE (DBG_CTL_AUTO_ARM | \ + DBG_CTL_INT_BREAK | \ + DBG_CTL_COMP1_TASK) +#define DBG_KILL_ALL_STAT (0xFFFFFFFF) + +/* Offset to context save area where progress info is stored */ +#define CSAVE_OFFSET 10 + +/* Defines for Byte Swapping */ +#define MCD_BYTE_SWAP_KILLER 0xFFF8888F +#define MCD_NO_BYTE_SWAP_ATALL 0x00040000 + +/* Execution Unit Identifiers */ +#define MAC 0 /* legacy - not used */ +#define LUAC 1 /* legacy - not used */ +#define CRC 2 /* legacy - not used */ +#define LURC 3 /* Logic Unit with CRC */ + +/* Task Identifiers */ +#define TASK_CHAINNOEU 0 +#define TASK_SINGLENOEU 1 +#ifdef MCD_INCLUDE_EU +#define TASK_CHAINEU 2 +#define TASK_SINGLEEU 3 +#define TASK_FECRX 4 +#define TASK_FECTX 5 +#else +#define TASK_CHAINEU 0 +#define TASK_SINGLEEU 1 +#define TASK_FECRX 2 +#define TASK_FECTX 3 +#endif + +/* + * Structure to remember which variant is on which channel + * TBD- need this? + */ +typedef struct MCD_remVariants_struct MCD_remVariant; +struct MCD_remVariants_struct { + int remDestRsdIncr[NCHANNELS]; /* -1,0,1 */ + int remSrcRsdIncr[NCHANNELS]; /* -1,0,1 */ + s16 remDestIncr[NCHANNELS]; /* DestIncr */ + s16 remSrcIncr[NCHANNELS]; /* srcIncr */ + u32 remXferSize[NCHANNELS]; /* xferSize */ +}; + +/* Structure to remember the startDma parameters for each channel */ +MCD_remVariant MCD_remVariants; +/********************************************************************/ +/* Function: MCD_initDma + * Purpose: Initializes the DMA API by setting up a pointer to the DMA + * registers, relocating and creating the appropriate task + * structures, and setting up some global settings + * Arguments: + * dmaBarAddr - pointer to the multichannel DMA registers + * taskTableDest - location to move DMA task code and structs to + * flags - operational parameters + * Return Value: + * MCD_TABLE_UNALIGNED if taskTableDest is not 512-byte aligned + * MCD_OK otherwise + */ +extern u32 MCD_funcDescTab0[]; + +int MCD_initDma(dmaRegs * dmaBarAddr, void *taskTableDest, u32 flags) +{ + int i; + TaskTableEntry *entryPtr; + + /* setup the local pointer to register set */ + MCD_dmaBar = dmaBarAddr; + + /* do we need to move/create a task table */ + if ((flags & MCD_RELOC_TASKS) != 0) { + int fixedSize; + u32 *fixedPtr; + /*int *tablePtr = taskTableDest;TBD */ + int varTabsOffset, funcDescTabsOffset, contextSavesOffset; + int taskDescTabsOffset; + int taskTableSize, varTabsSize, funcDescTabsSize, + contextSavesSize; + int taskDescTabSize; + + int i; + + /* check if physical address is aligned on 512 byte boundary */ + if (((u32) taskTableDest & 0x000001ff) != 0) + return (MCD_TABLE_UNALIGNED); + + /* set up local pointer to task Table */ + MCD_taskTable = taskTableDest; + + /* + * Create a task table: + * - compute aligned base offsets for variable tables and + * function descriptor tables, then + * - loop through the task table and setup the pointers + * - copy over model task table with the the actual task + * descriptor tables + */ + + taskTableSize = NCHANNELS * sizeof(TaskTableEntry); + /* align variable tables to size */ + varTabsOffset = taskTableSize + (u32) taskTableDest; + if ((varTabsOffset & (VAR_TAB_SIZE - 1)) != 0) + varTabsOffset = + (varTabsOffset + VAR_TAB_SIZE) & (~VAR_TAB_SIZE); + /* align function descriptor tables */ + varTabsSize = NCHANNELS * VAR_TAB_SIZE; + funcDescTabsOffset = varTabsOffset + varTabsSize; + + if ((funcDescTabsOffset & (FUNCDESC_TAB_SIZE - 1)) != 0) + funcDescTabsOffset = + (funcDescTabsOffset + + FUNCDESC_TAB_SIZE) & (~FUNCDESC_TAB_SIZE); + + funcDescTabsSize = FUNCDESC_TAB_NUM * FUNCDESC_TAB_SIZE; + contextSavesOffset = funcDescTabsOffset + funcDescTabsSize; + contextSavesSize = (NCHANNELS * CONTEXT_SAVE_SIZE); + fixedSize = + taskTableSize + varTabsSize + funcDescTabsSize + + contextSavesSize; + + /* zero the thing out */ + fixedPtr = (u32 *) taskTableDest; + for (i = 0; i < (fixedSize / 4); i++) + fixedPtr[i] = 0; + + entryPtr = (TaskTableEntry *) MCD_taskTable; + /* set up fixed pointers */ + for (i = 0; i < NCHANNELS; i++) { + /* update ptr to local value */ + entryPtr[i].varTab = (u32) varTabsOffset; + entryPtr[i].FDTandFlags = + (u32) funcDescTabsOffset | MCD_TT_FLAGS_DEF; + entryPtr[i].contextSaveSpace = (u32) contextSavesOffset; + varTabsOffset += VAR_TAB_SIZE; +#ifdef MCD_INCLUDE_EU + /* if not there is only one, just point to the + same one */ + funcDescTabsOffset += FUNCDESC_TAB_SIZE; +#endif + contextSavesOffset += CONTEXT_SAVE_SIZE; + } + /* copy over the function descriptor table */ + for (i = 0; i < FUNCDESC_TAB_NUM; i++) { + MCD_memcpy((void *)(entryPtr[i]. + FDTandFlags & ~MCD_TT_FLAGS_MASK), + (void *)MCD_funcDescTab0, FUNCDESC_TAB_SIZE); + } + + /* copy model task table to where the context saves stuff + leaves off */ + MCD_modelTaskTable = (TaskTableEntry *) contextSavesOffset; + + MCD_memcpy((void *)MCD_modelTaskTable, + (void *)MCD_modelTaskTableSrc, + NUMOFVARIANTS * sizeof(TaskTableEntry)); + + /* point to local version of model task table */ + entryPtr = MCD_modelTaskTable; + taskDescTabsOffset = (u32) MCD_modelTaskTable + + (NUMOFVARIANTS * sizeof(TaskTableEntry)); + + /* copy actual task code and update TDT ptrs in local + model task table */ + for (i = 0; i < NUMOFVARIANTS; i++) { + taskDescTabSize = + entryPtr[i].TDTend - entryPtr[i].TDTstart + 4; + MCD_memcpy((void *)taskDescTabsOffset, + (void *)entryPtr[i].TDTstart, + taskDescTabSize); + entryPtr[i].TDTstart = (u32) taskDescTabsOffset; + taskDescTabsOffset += taskDescTabSize; + entryPtr[i].TDTend = (u32) taskDescTabsOffset - 4; + } +#ifdef MCD_INCLUDE_EU + /* Tack single DMA BDs onto end of code so API controls + where they are since DMA might write to them */ + MCD_relocBuffDesc = + (MCD_bufDesc *) (entryPtr[NUMOFVARIANTS - 1].TDTend + 4); +#else + /* DMA does not touch them so they can be wherever and we + don't need to waste SRAM on them */ + MCD_relocBuffDesc = MCD_singleBufDescs; +#endif + } else { + /* point the would-be relocated task tables and the + buffer descriptors to the ones the linker generated */ + + if (((u32) MCD_realTaskTableSrc & 0x000001ff) != 0) + return (MCD_TABLE_UNALIGNED); + + /* need to add code to make sure that every thing else is + aligned properly TBD. this is problematic if we init + more than once or after running tasks, need to add + variable to see if we have aleady init'd */ + entryPtr = MCD_realTaskTableSrc; + for (i = 0; i < NCHANNELS; i++) { + if (((entryPtr[i].varTab & (VAR_TAB_SIZE - 1)) != 0) || + ((entryPtr[i]. + FDTandFlags & (FUNCDESC_TAB_SIZE - 1)) != 0)) + return (MCD_TABLE_UNALIGNED); + } + + MCD_taskTable = MCD_realTaskTableSrc; + MCD_modelTaskTable = MCD_modelTaskTableSrc; + MCD_relocBuffDesc = MCD_singleBufDescs; + } + + /* Make all channels as totally inactive, and remember them as such: */ + + MCD_dmaBar->taskbar = (u32) MCD_taskTable; + for (i = 0; i < NCHANNELS; i++) { + MCD_dmaBar->taskControl[i] = 0x0; + MCD_chStatus[i] = MCD_NO_DMA; + } + + /* Set up pausing mechanism to inactive state: */ + /* no particular values yet for either comparator registers */ + MCD_dmaBar->debugComp1 = 0; + MCD_dmaBar->debugComp2 = 0; + MCD_dmaBar->debugControl = DBG_CTL_DISABLE; + MCD_dmaBar->debugStatus = DBG_KILL_ALL_STAT; + + /* enable or disable commbus prefetch, really need an ifdef or + something to keep from trying to set this in the 8220 */ + if ((flags & MCD_COMM_PREFETCH_EN) != 0) + MCD_dmaBar->ptdControl &= ~PTD_CTL_COMM_PREFETCH; + else + MCD_dmaBar->ptdControl |= PTD_CTL_COMM_PREFETCH; + + return (MCD_OK); +} + +/*********************** End of MCD_initDma() ***********************/ + +/********************************************************************/ +/* Function: MCD_dmaStatus + * Purpose: Returns the status of the DMA on the requested channel + * Arguments: channel - channel number + * Returns: Predefined status indicators + */ +int MCD_dmaStatus(int channel) +{ + u16 tcrValue; + + if ((channel < 0) || (channel >= NCHANNELS)) + return (MCD_CHANNEL_INVALID); + + tcrValue = MCD_dmaBar->taskControl[channel]; + if ((tcrValue & TASK_CTL_EN) == 0) { /* nothing running */ + /* if last reported with task enabled */ + if (MCD_chStatus[channel] == MCD_RUNNING + || MCD_chStatus[channel] == MCD_IDLE) + MCD_chStatus[channel] = MCD_DONE; + } else { /* something is running */ + + /* There are three possibilities: paused, running or idle. */ + if (MCD_chStatus[channel] == MCD_RUNNING + || MCD_chStatus[channel] == MCD_IDLE) { + MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT; + /* This register is selected to know which initiator is + actually asserted. */ + if ((MCD_dmaBar->ptdDebug >> channel) & 0x1) + MCD_chStatus[channel] = MCD_RUNNING; + else + MCD_chStatus[channel] = MCD_IDLE; + /* do not change the status if it is already paused. */ + } + } + return MCD_chStatus[channel]; +} + +/******************** End of MCD_dmaStatus() ************************/ + +/********************************************************************/ +/* Function: MCD_startDma + * Ppurpose: Starts a particular kind of DMA + * Arguments: + * srcAddr - the channel on which to run the DMA + * srcIncr - the address to move data from, or buffer-descriptor address + * destAddr - the amount to increment the source address per transfer + * destIncr - the address to move data to + * dmaSize - the amount to increment the destination address per transfer + * xferSize - the number bytes in of each data movement (1, 2, or 4) + * initiator - what device initiates the DMA + * priority - priority of the DMA + * flags - flags describing the DMA + * funcDesc - description of byte swapping, bit swapping, and CRC actions + * srcAddrVirt - virtual buffer descriptor address TBD + * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK + */ + +int MCD_startDma(int channel, s8 * srcAddr, s16 srcIncr, s8 * destAddr, + s16 destIncr, u32 dmaSize, u32 xferSize, u32 initiator, + int priority, u32 flags, u32 funcDesc +#ifdef MCD_NEED_ADDR_TRANS + s8 * srcAddrVirt +#endif + ) +{ + int srcRsdIncr, destRsdIncr; + int *cSave; + short xferSizeIncr; + int tcrCount = 0; +#ifdef MCD_INCLUDE_EU + u32 *realFuncArray; +#endif + + if ((channel < 0) || (channel >= NCHANNELS)) + return (MCD_CHANNEL_INVALID); + + /* tbd - need to determine the proper response to a bad funcDesc when + not including EU functions, for now, assign a benign funcDesc, but + maybe should return an error */ +#ifndef MCD_INCLUDE_EU + funcDesc = MCD_FUNC_NOEU1; +#endif + +#ifdef MCD_DEBUG + printf("startDma:Setting up params\n"); +#endif + /* Set us up for task-wise priority. We don't technically need to do + this on every start, but since the register involved is in the same + longword as other registers that users are in control of, setting + it more than once is probably preferable. That since the + documentation doesn't seem to be completely consistent about the + nature of the PTD control register. */ + MCD_dmaBar->ptdControl |= (u16) 0x8000; + + /* Not sure what we need to keep here rtm TBD */ +#if 1 + /* Calculate additional parameters to the regular DMA calls. */ + srcRsdIncr = srcIncr < 0 ? -1 : (srcIncr > 0 ? 1 : 0); + destRsdIncr = destIncr < 0 ? -1 : (destIncr > 0 ? 1 : 0); + + xferSizeIncr = (xferSize & 0xffff) | 0x20000000; + + /* Remember for each channel which variant is running. */ + MCD_remVariants.remSrcRsdIncr[channel] = srcRsdIncr; + MCD_remVariants.remDestRsdIncr[channel] = destRsdIncr; + MCD_remVariants.remDestIncr[channel] = destIncr; + MCD_remVariants.remSrcIncr[channel] = srcIncr; + MCD_remVariants.remXferSize[channel] = xferSize; +#endif + + cSave = + (int *)(MCD_taskTable[channel].contextSaveSpace) + CSAVE_OFFSET + + CURRBD; + +#ifdef MCD_INCLUDE_EU + /* may move this to EU specific calls */ + realFuncArray = + (u32 *) (MCD_taskTable[channel].FDTandFlags & 0xffffff00); + /* Modify the LURC's normal and byte-residue-loop functions according + to parameter. */ + realFuncArray[(LURC * 16)] = xferSize == 4 ? + funcDesc : xferSize == 2 ? + funcDesc & 0xfffff00f : funcDesc & 0xffff000f; + realFuncArray[(LURC * 16 + 1)] = + (funcDesc & MCD_BYTE_SWAP_KILLER) | MCD_NO_BYTE_SWAP_ATALL; +#endif + /* Write the initiator field in the TCR, and also set the + initiator-hold bit. Note that,due to a hardware quirk, this could + collide with an MDE access to the initiator-register file, so we + have to verify that the write reads back correctly. */ + + MCD_dmaBar->taskControl[channel] = + (initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM; + + while (((MCD_dmaBar->taskControl[channel] & 0x1fff) != + ((initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM)) + && (tcrCount < 1000)) { + tcrCount++; + /*MCD_dmaBar->ptd_tcr[channel] = (initiator << 8) | 0x0020; */ + MCD_dmaBar->taskControl[channel] = + (initiator << 8) | TASK_CTL_HIPRITSKEN | + TASK_CTL_HLDINITNUM; + } + + MCD_dmaBar->priority[channel] = (u8) priority & PRIORITY_PRI_MASK; + /* should be albe to handle this stuff with only one write to ts reg + - tbd */ + if (channel < 8 && channel >= 0) { + MCD_dmaBar->taskSize0 &= ~(0xf << (7 - channel) * 4); + MCD_dmaBar->taskSize0 |= + (xferSize & 3) << (((7 - channel) * 4) + 2); + MCD_dmaBar->taskSize0 |= (xferSize & 3) << ((7 - channel) * 4); + } else { + MCD_dmaBar->taskSize1 &= ~(0xf << (15 - channel) * 4); + MCD_dmaBar->taskSize1 |= + (xferSize & 3) << (((15 - channel) * 4) + 2); + MCD_dmaBar->taskSize1 |= (xferSize & 3) << ((15 - channel) * 4); + } + + /* setup task table flags/options which mostly control the line + buffers */ + MCD_taskTable[channel].FDTandFlags &= ~MCD_TT_FLAGS_MASK; + MCD_taskTable[channel].FDTandFlags |= (MCD_TT_FLAGS_MASK & flags); + + if (flags & MCD_FECTX_DMA) { + /* TDTStart and TDTEnd */ + MCD_taskTable[channel].TDTstart = + MCD_modelTaskTable[TASK_FECTX].TDTstart; + MCD_taskTable[channel].TDTend = + MCD_modelTaskTable[TASK_FECTX].TDTend; + MCD_startDmaENetXmit((char *)srcAddr, (char *)srcAddr, + (char *)destAddr, MCD_taskTable, + channel); + } else if (flags & MCD_FECRX_DMA) { + /* TDTStart and TDTEnd */ + MCD_taskTable[channel].TDTstart = + MCD_modelTaskTable[TASK_FECRX].TDTstart; + MCD_taskTable[channel].TDTend = + MCD_modelTaskTable[TASK_FECRX].TDTend; + MCD_startDmaENetRcv((char *)srcAddr, (char *)srcAddr, + (char *)destAddr, MCD_taskTable, + channel); + } else if (flags & MCD_SINGLE_DMA) { + /* this buffer descriptor is used for storing off initial + parameters for later progress query calculation and for the + DMA to write the resulting checksum. The DMA does not use + this to determine how to operate, that info is passed with + the init routine */ + MCD_relocBuffDesc[channel].srcAddr = srcAddr; + MCD_relocBuffDesc[channel].destAddr = destAddr; + + /* definitely not its final value */ + MCD_relocBuffDesc[channel].lastDestAddr = destAddr; + + MCD_relocBuffDesc[channel].dmaSize = dmaSize; + MCD_relocBuffDesc[channel].flags = 0; /* not used */ + MCD_relocBuffDesc[channel].csumResult = 0; /* not used */ + MCD_relocBuffDesc[channel].next = 0; /* not used */ + + /* Initialize the progress-querying stuff to show no + progress: */ + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[SRCPTR + CSAVE_OFFSET] = (int)srcAddr; + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[DESTPTR + CSAVE_OFFSET] = (int)destAddr; + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0; + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[CURRBD + CSAVE_OFFSET] = +(u32) & (MCD_relocBuffDesc[channel]); + /* tbd - need to keep the user from trying to call the EU + routine when MCD_INCLUDE_EU is not defined */ + if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) { + /* TDTStart and TDTEnd */ + MCD_taskTable[channel].TDTstart = + MCD_modelTaskTable[TASK_SINGLENOEU].TDTstart; + MCD_taskTable[channel].TDTend = + MCD_modelTaskTable[TASK_SINGLENOEU].TDTend; + MCD_startDmaSingleNoEu((char *)srcAddr, srcIncr, + (char *)destAddr, destIncr, + (int)dmaSize, xferSizeIncr, + flags, (int *) + &(MCD_relocBuffDesc[channel]), + cSave, MCD_taskTable, channel); + } else { + /* TDTStart and TDTEnd */ + MCD_taskTable[channel].TDTstart = + MCD_modelTaskTable[TASK_SINGLEEU].TDTstart; + MCD_taskTable[channel].TDTend = + MCD_modelTaskTable[TASK_SINGLEEU].TDTend; + MCD_startDmaSingleEu((char *)srcAddr, srcIncr, + (char *)destAddr, destIncr, + (int)dmaSize, xferSizeIncr, + flags, (int *) + &(MCD_relocBuffDesc[channel]), + cSave, MCD_taskTable, channel); + } + } else { /* chained DMAS */ + /* Initialize the progress-querying stuff to show no + progress: */ +#if 1 + /* (!defined(MCD_NEED_ADDR_TRANS)) */ + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[SRCPTR + CSAVE_OFFSET] + = (int)((MCD_bufDesc *) srcAddr)->srcAddr; + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[DESTPTR + CSAVE_OFFSET] + = (int)((MCD_bufDesc *) srcAddr)->destAddr; +#else + /* if using address translation, need the virtual addr of the + first buffdesc */ + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[SRCPTR + CSAVE_OFFSET] + = (int)((MCD_bufDesc *) srcAddrVirt)->srcAddr; + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[DESTPTR + CSAVE_OFFSET] + = (int)((MCD_bufDesc *) srcAddrVirt)->destAddr; +#endif + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0; + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[CURRBD + CSAVE_OFFSET] = (u32) srcAddr; + + if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) { + /*TDTStart and TDTEnd */ + MCD_taskTable[channel].TDTstart = + MCD_modelTaskTable[TASK_CHAINNOEU].TDTstart; + MCD_taskTable[channel].TDTend = + MCD_modelTaskTable[TASK_CHAINNOEU].TDTend; + MCD_startDmaChainNoEu((int *)srcAddr, srcIncr, + destIncr, xferSize, + xferSizeIncr, cSave, + MCD_taskTable, channel); + } else { + /*TDTStart and TDTEnd */ + MCD_taskTable[channel].TDTstart = + MCD_modelTaskTable[TASK_CHAINEU].TDTstart; + MCD_taskTable[channel].TDTend = + MCD_modelTaskTable[TASK_CHAINEU].TDTend; + MCD_startDmaChainEu((int *)srcAddr, srcIncr, destIncr, + xferSize, xferSizeIncr, cSave, + MCD_taskTable, channel); + } + } + MCD_chStatus[channel] = MCD_IDLE; + return (MCD_OK); +} + +/************************ End of MCD_startDma() *********************/ + +/********************************************************************/ +/* Function: MCD_XferProgrQuery + * Purpose: Returns progress of DMA on requested channel + * Arguments: channel - channel to retrieve progress for + * progRep - pointer to user supplied MCD_XferProg struct + * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK + * + * Notes: + * MCD_XferProgrQuery() upon completing or after aborting a DMA, or + * while the DMA is in progress, this function returns the first + * DMA-destination address not (or not yet) used in the DMA. When + * encountering a non-ready buffer descriptor, the information for + * the last completed descriptor is returned. + * + * MCD_XferProgQuery() has to avoid the possibility of getting + * partially-updated information in the event that we should happen + * to query DMA progress just as the DMA is updating it. It does that + * by taking advantage of the fact context is not saved frequently for + * the most part. We therefore read it at least twice until we get the + * same information twice in a row. + * + * Because a small, but not insignificant, amount of time is required + * to write out the progress-query information, especially upon + * completion of the DMA, it would be wise to guarantee some time lag + * between successive readings of the progress-query information. + */ + +/* How many iterations of the loop below to execute to stabilize values */ +#define STABTIME 0 + +int MCD_XferProgrQuery(int channel, MCD_XferProg * progRep) +{ + MCD_XferProg prevRep; + int again; /* true if we are to try again to ge + consistent results */ + int i; /* used as a time-waste counter */ + int destDiffBytes; /* Total no of bytes that we think actually + got xfered. */ + int numIterations; /* number of iterations */ + int bytesNotXfered; /* bytes that did not get xfered. */ + s8 *LWAlignedInitDestAddr, *LWAlignedCurrDestAddr; + int subModVal, addModVal; /* Mode values to added and subtracted + from the final destAddr */ + + if ((channel < 0) || (channel >= NCHANNELS)) + return (MCD_CHANNEL_INVALID); + + /* Read a trial value for the progress-reporting values */ + prevRep.lastSrcAddr = + (s8 *) ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[SRCPTR + CSAVE_OFFSET]; + prevRep.lastDestAddr = + (s8 *) ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[DESTPTR + CSAVE_OFFSET]; + prevRep.dmaSize = + ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DCOUNT + + CSAVE_OFFSET]; + prevRep.currBufDesc = + (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[CURRBD + CSAVE_OFFSET]; + /* Repeatedly reread those values until they match previous values: */ + do { + /* Waste a little bit of time to ensure stability: */ + for (i = 0; i < STABTIME; i++) { + /* make sure this loop does something so that it + doesn't get optimized out */ + i += i >> 2; + } + /* Check them again: */ + progRep->lastSrcAddr = + (s8 *) ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[SRCPTR + CSAVE_OFFSET]; + progRep->lastDestAddr = + (s8 *) ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[DESTPTR + CSAVE_OFFSET]; + progRep->dmaSize = + ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[DCOUNT + CSAVE_OFFSET]; + progRep->currBufDesc = + (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel]. + contextSaveSpace)[CURRBD + CSAVE_OFFSET]; + /* See if they match: */ + if (prevRep.lastSrcAddr != progRep->lastSrcAddr + || prevRep.lastDestAddr != progRep->lastDestAddr + || prevRep.dmaSize != progRep->dmaSize + || prevRep.currBufDesc != progRep->currBufDesc) { + /* If they don't match, remember previous values and + try again: */ + prevRep.lastSrcAddr = progRep->lastSrcAddr; + prevRep.lastDestAddr = progRep->lastDestAddr; + prevRep.dmaSize = progRep->dmaSize; + prevRep.currBufDesc = progRep->currBufDesc; + again = MCD_TRUE; + } else + again = MCD_FALSE; + } while (again == MCD_TRUE); + + /* Update the dCount, srcAddr and destAddr */ + /* To calculate dmaCount, we consider destination address. C + overs M1,P1,Z for destination */ + switch (MCD_remVariants.remDestRsdIncr[channel]) { + case MINUS1: + subModVal = + ((int)progRep-> + lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) - + 1); + addModVal = + ((int)progRep->currBufDesc-> + destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1); + LWAlignedInitDestAddr = + (progRep->currBufDesc->destAddr) - addModVal; + LWAlignedCurrDestAddr = (progRep->lastDestAddr) - subModVal; + destDiffBytes = LWAlignedInitDestAddr - LWAlignedCurrDestAddr; + bytesNotXfered = + (destDiffBytes / MCD_remVariants.remDestIncr[channel]) * + (MCD_remVariants.remDestIncr[channel] + + MCD_remVariants.remXferSize[channel]); + progRep->dmaSize = + destDiffBytes - bytesNotXfered + addModVal - subModVal; + break; + case ZERO: + progRep->lastDestAddr = progRep->currBufDesc->destAddr; + break; + case PLUS1: + /* This value has to be subtracted from the final + calculated dCount. */ + subModVal = + ((int)progRep->currBufDesc-> + destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1); + /* These bytes are already in lastDestAddr. */ + addModVal = + ((int)progRep-> + lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) - + 1); + LWAlignedInitDestAddr = + (progRep->currBufDesc->destAddr) - subModVal; + LWAlignedCurrDestAddr = (progRep->lastDestAddr) - addModVal; + destDiffBytes = (progRep->lastDestAddr - LWAlignedInitDestAddr); + numIterations = + (LWAlignedCurrDestAddr - + LWAlignedInitDestAddr) / + MCD_remVariants.remDestIncr[channel]; + bytesNotXfered = + numIterations * (MCD_remVariants.remDestIncr[channel] + - MCD_remVariants.remXferSize[channel]); + progRep->dmaSize = destDiffBytes - bytesNotXfered - subModVal; + break; + default: + break; + } + + /* This covers M1,P1,Z for source */ + switch (MCD_remVariants.remSrcRsdIncr[channel]) { + case MINUS1: + progRep->lastSrcAddr = + progRep->currBufDesc->srcAddr + + (MCD_remVariants.remSrcIncr[channel] * + (progRep->dmaSize / MCD_remVariants.remXferSize[channel])); + break; + case ZERO: + progRep->lastSrcAddr = progRep->currBufDesc->srcAddr; + break; + case PLUS1: + progRep->lastSrcAddr = + progRep->currBufDesc->srcAddr + + (MCD_remVariants.remSrcIncr[channel] * + (progRep->dmaSize / MCD_remVariants.remXferSize[channel])); + break; + default: + break; + } + + return (MCD_OK); +} + +/******************* End of MCD_XferProgrQuery() ********************/ + +/********************************************************************/ +/* MCD_resmActions() does the majority of the actions of a DMA resume. + * It is called from MCD_killDma() and MCD_resumeDma(). It has to be + * a separate function because the kill function has to negate the task + * enable before resuming it, but the resume function has to do nothing + * if there is no DMA on that channel (i.e., if the enable bit is 0). + */ +static void MCD_resmActions(int channel) +{ + MCD_dmaBar->debugControl = DBG_CTL_DISABLE; + MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus; + /* This register is selected to know which initiator is + actually asserted. */ + MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT; + + if ((MCD_dmaBar->ptdDebug >> channel) & 0x1) + MCD_chStatus[channel] = MCD_RUNNING; + else + MCD_chStatus[channel] = MCD_IDLE; +} + +/********************* End of MCD_resmActions() *********************/ + +/********************************************************************/ +/* Function: MCD_killDma + * Purpose: Halt the DMA on the requested channel, without any + * intention of resuming the DMA. + * Arguments: channel - requested channel + * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK + * + * Notes: + * A DMA may be killed from any state, including paused state, and it + * always goes to the MCD_HALTED state even if it is killed while in + * the MCD_NO_DMA or MCD_IDLE states. + */ +int MCD_killDma(int channel) +{ + /* MCD_XferProg progRep; */ + + if ((channel < 0) || (channel >= NCHANNELS)) + return (MCD_CHANNEL_INVALID); + + MCD_dmaBar->taskControl[channel] = 0x0; + MCD_resumeDma(channel); + /* + * This must be after the write to the TCR so that the task doesn't + * start up again momentarily, and before the status assignment so + * as to override whatever MCD_resumeDma() may do to the channel + * status. + */ + MCD_chStatus[channel] = MCD_HALTED; + + /* + * Update the current buffer descriptor's lastDestAddr field + * + * MCD_XferProgrQuery (channel, &progRep); + * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr; + */ + return (MCD_OK); +} + +/************************ End of MCD_killDma() **********************/ + +/********************************************************************/ +/* Function: MCD_continDma + * Purpose: Continue a DMA which as stopped due to encountering an + * unready buffer descriptor. + * Arguments: channel - channel to continue the DMA on + * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK + * + * Notes: + * This routine does not check to see if there is a task which can + * be continued. Also this routine should not be used with single DMAs. + */ +int MCD_continDma(int channel) +{ + if ((channel < 0) || (channel >= NCHANNELS)) + return (MCD_CHANNEL_INVALID); + + MCD_dmaBar->taskControl[channel] |= TASK_CTL_EN; + MCD_chStatus[channel] = MCD_RUNNING; + + return (MCD_OK); +} + +/********************** End of MCD_continDma() **********************/ + +/********************************************************************* + * MCD_pauseDma() and MCD_resumeDma() below use the DMA's debug unit + * to freeze a task and resume it. We freeze a task by breakpointing + * on the stated task. That is, not any specific place in the task, + * but any time that task executes. In particular, when that task + * executes, we want to freeze that task and only that task. + * + * The bits of the debug control register influence interrupts vs. + * breakpoints as follows: + * - Bits 14 and 0 enable or disable debug functions. If enabled, you + * will get the interrupt but you may or may not get a breakpoint. + * - Bits 2 and 1 decide whether you also get a breakpoint in addition + * to an interrupt. + * + * The debug unit can do these actions in response to either internally + * detected breakpoint conditions from the comparators, or in response + * to the external breakpoint pin, or both. + * - Bits 14 and 1 perform the above-described functions for + * internally-generated conditions, i.e., the debug comparators. + * - Bits 0 and 2 perform the above-described functions for external + * conditions, i.e., the breakpoint external pin. + * + * Note that, although you "always" get the interrupt when you turn + * the debug functions, the interrupt can nevertheless, if desired, be + * masked by the corresponding bit in the PTD's IMR. Note also that + * this means that bits 14 and 0 must enable debug functions before + * bits 1 and 2, respectively, have any effect. + * + * NOTE: It's extremely important to not pause more than one DMA channel + * at a time. + ********************************************************************/ + +/********************************************************************/ +/* Function: MCD_pauseDma + * Purpose: Pauses the DMA on a given channel (if any DMA is running + * on that channel). + * Arguments: channel + * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK + */ +int MCD_pauseDma(int channel) +{ + /* MCD_XferProg progRep; */ + + if ((channel < 0) || (channel >= NCHANNELS)) + return (MCD_CHANNEL_INVALID); + + if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) { + MCD_dmaBar->debugComp1 = channel; + MCD_dmaBar->debugControl = + DBG_CTL_ENABLE | (1 << (channel + 16)); + MCD_chStatus[channel] = MCD_PAUSED; + + /* + * Update the current buffer descriptor's lastDestAddr field + * + * MCD_XferProgrQuery (channel, &progRep); + * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr; + */ + } + return (MCD_OK); +} + +/************************* End of MCD_pauseDma() ********************/ + +/********************************************************************/ +/* Function: MCD_resumeDma + * Purpose: Resumes the DMA on a given channel (if any DMA is + * running on that channel). + * Arguments: channel - channel on which to resume DMA + * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK + */ +int MCD_resumeDma(int channel) +{ + if ((channel < 0) || (channel >= NCHANNELS)) + return (MCD_CHANNEL_INVALID); + + if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) + MCD_resmActions(channel); + + return (MCD_OK); +} + +/************************ End of MCD_resumeDma() ********************/ + +/********************************************************************/ +/* Function: MCD_csumQuery + * Purpose: Provide the checksum after performing a non-chained DMA + * Arguments: channel - channel to report on + * csum - pointer to where to write the checksum/CRC + * Returns: MCD_ERROR if the channel is invalid, else MCD_OK + * + * Notes: + * + */ +int MCD_csumQuery(int channel, u32 * csum) +{ +#ifdef MCD_INCLUDE_EU + if ((channel < 0) || (channel >= NCHANNELS)) + return (MCD_CHANNEL_INVALID); + + *csum = MCD_relocBuffDesc[channel].csumResult; + return (MCD_OK); +#else + return (MCD_ERROR); +#endif +} + +/*********************** End of MCD_resumeDma() *********************/ + +/********************************************************************/ +/* Function: MCD_getCodeSize + * Purpose: Provide the size requirements of the microcoded tasks + * Returns: Size in bytes + */ +int MCD_getCodeSize(void) +{ +#ifdef MCD_INCLUDE_EU + return (0x2b5c); +#else + return (0x173c); +#endif +} + +/********************** End of MCD_getCodeSize() ********************/ + +/********************************************************************/ +/* Function: MCD_getVersion + * Purpose: Provide the version string and number + * Arguments: longVersion - user supplied pointer to a pointer to a char + * which points to the version string + * Returns: Version number and version string (by reference) + */ +char MCD_versionString[] = "Multi-channel DMA API Alpha v0.3 (2004-04-26)"; +#define MCD_REV_MAJOR 0x00 +#define MCD_REV_MINOR 0x03 + +int MCD_getVersion(char **longVersion) +{ + *longVersion = MCD_versionString; + return ((MCD_REV_MAJOR << 8) | MCD_REV_MINOR); +} + +/********************** End of MCD_getVersion() *********************/ + +/********************************************************************/ +/* Private version of memcpy() + * Note that everything this is used for is longword-aligned. + */ +static void MCD_memcpy(int *dest, int *src, u32 size) +{ + u32 i; + + for (i = 0; i < size; i += sizeof(int), dest++, src++) + *dest = *src; +} diff --git a/qemu/roms/u-boot/drivers/dma/MCD_tasks.c b/qemu/roms/u-boot/drivers/dma/MCD_tasks.c new file mode 100644 index 000000000..f90e4e4e8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/dma/MCD_tasks.c @@ -0,0 +1,2414 @@ +/* + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Contains task code and structures for Multi-channel DMA */ + +#include + +#include + +u32 MCD_varTab0[]; +u32 MCD_varTab1[]; +u32 MCD_varTab2[]; +u32 MCD_varTab3[]; +u32 MCD_varTab4[]; +u32 MCD_varTab5[]; +u32 MCD_varTab6[]; +u32 MCD_varTab7[]; +u32 MCD_varTab8[]; +u32 MCD_varTab9[]; +u32 MCD_varTab10[]; +u32 MCD_varTab11[]; +u32 MCD_varTab12[]; +u32 MCD_varTab13[]; +u32 MCD_varTab14[]; +u32 MCD_varTab15[]; + +u32 MCD_funcDescTab0[]; +#ifdef MCD_INCLUDE_EU +u32 MCD_funcDescTab1[]; +u32 MCD_funcDescTab2[]; +u32 MCD_funcDescTab3[]; +u32 MCD_funcDescTab4[]; +u32 MCD_funcDescTab5[]; +u32 MCD_funcDescTab6[]; +u32 MCD_funcDescTab7[]; +u32 MCD_funcDescTab8[]; +u32 MCD_funcDescTab9[]; +u32 MCD_funcDescTab10[]; +u32 MCD_funcDescTab11[]; +u32 MCD_funcDescTab12[]; +u32 MCD_funcDescTab13[]; +u32 MCD_funcDescTab14[]; +u32 MCD_funcDescTab15[]; +#endif + +u32 MCD_contextSave0[]; +u32 MCD_contextSave1[]; +u32 MCD_contextSave2[]; +u32 MCD_contextSave3[]; +u32 MCD_contextSave4[]; +u32 MCD_contextSave5[]; +u32 MCD_contextSave6[]; +u32 MCD_contextSave7[]; +u32 MCD_contextSave8[]; +u32 MCD_contextSave9[]; +u32 MCD_contextSave10[]; +u32 MCD_contextSave11[]; +u32 MCD_contextSave12[]; +u32 MCD_contextSave13[]; +u32 MCD_contextSave14[]; +u32 MCD_contextSave15[]; + +u32 MCD_realTaskTableSrc[] = { + 0x00000000, + 0x00000000, + (u32) MCD_varTab0, /* Task 0 Variable Table */ + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ + 0x00000000, + 0x00000000, + (u32) MCD_contextSave0, /* Task 0 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab1, /* Task 1 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab1, /* Task 1 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave1, /* Task 1 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab2, /* Task 2 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab2, /* Task 2 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave2, /* Task 2 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab3, /* Task 3 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab3, /* Task 3 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave3, /* Task 3 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab4, /* Task 4 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab4, /* Task 4 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave4, /* Task 4 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab5, /* Task 5 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab5, /* Task 5 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave5, /* Task 5 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab6, /* Task 6 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab6, /* Task 6 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave6, /* Task 6 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab7, /* Task 7 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab7, /* Task 7 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave7, /* Task 7 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab8, /* Task 8 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab8, /* Task 8 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave8, /* Task 8 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab9, /* Task 9 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab9, /* Task 9 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave9, /* Task 9 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab10, /* Task 10 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab10, /* Task 10 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave10, /* Task 10 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab11, /* Task 11 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab11, /* Task 11 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave11, /* Task 11 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab12, /* Task 12 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab12, /* Task 12 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave12, /* Task 12 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab13, /* Task 13 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab13, /* Task 13 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave13, /* Task 13 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab14, /* Task 14 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab14, /* Task 14 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave14, /* Task 14 context save space */ + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_varTab15, /* Task 15 Variable Table */ +#ifdef MCD_INCLUDE_EU + (u32) MCD_funcDescTab15, /* Task 15 Fn Desc. Table & Flags */ +#else + (u32) MCD_funcDescTab0, /* Task 0 Fn Desc. Table & Flags */ +#endif + 0x00000000, + 0x00000000, + (u32) MCD_contextSave15, /* Task 15 context save space */ + 0x00000000, +}; + +u32 MCD_varTab0[] = { /* Task 0 Variable Table */ + 0x00000000, /* var[0] */ + 0x00000000, /* var[1] */ + 0x00000000, /* var[2] */ + 0x00000000, /* var[3] */ + 0x00000000, /* var[4] */ + 0x00000000, /* var[5] */ + 0x00000000, /* var[6] */ + 0x00000000, /* var[7] */ + 0x00000000, /* var[8] */ + 0x00000000, /* var[9] */ + 0x00000000, /* var[10] */ + 0x00000000, /* var[11] */ + 0x00000000, /* var[12] */ + 0x00000000, /* var[13] */ + 0x00000000, /* var[14] */ + 0x00000000, /* var[15] */ + 0x00000000, /* var[16] */ + 0x00000000, /* var[17] */ + 0x00000000, /* var[18] */ + 0x00000000, /* var[19] */ + 0x00000000, /* var[20] */ + 0x00000000, /* var[21] */ + 0x00000000, /* var[22] */ + 0x00000000, /* var[23] */ + 0xe0000000, /* inc[0] */ + 0x20000000, /* inc[1] */ + 0x2000ffff, /* inc[2] */ + 0x00000000, /* inc[3] */ + 0x00000000, /* inc[4] */ + 0x00000000, /* inc[5] */ + 0x00000000, /* inc[6] */ + 0x00000000, /* inc[7] */ +}; + +u32 MCD_varTab1[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab2[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab3[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab4[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab5[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab6[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab7[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab8[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab9[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab10[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab11[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab12[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab13[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab14[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_varTab15[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xe0000000, + 0x20000000, + 0x2000ffff, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_funcDescTab0[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +#ifdef MCD_INCLUDE_EU +u32 MCD_funcDescTab1[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab2[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab3[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab4[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab5[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab6[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab7[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab8[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab9[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab10[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab11[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab12[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab13[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab14[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; + +u32 MCD_funcDescTab15[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xa0045670, + 0xa0000000, + 0xa0000000, + 0x20000000, + 0x21800000, + 0x21e00000, + 0x20400000, + 0x20500000, + 0x205a0000, + 0x20a00000, + 0x202fa000, + 0x202f9000, + 0x202ea000, + 0x202da000, + 0x202e2000, + 0x202f2000, +}; +#endif /*MCD_INCLUDE_EU */ + +u32 MCD_contextSave0[128]; /* Task 0 context save space */ +u32 MCD_contextSave1[128]; /* Task 1 context save space */ +u32 MCD_contextSave2[128]; /* Task 2 context save space */ +u32 MCD_contextSave3[128]; /* Task 3 context save space */ +u32 MCD_contextSave4[128]; /* Task 4 context save space */ +u32 MCD_contextSave5[128]; /* Task 5 context save space */ +u32 MCD_contextSave6[128]; /* Task 6 context save space */ +u32 MCD_contextSave7[128]; /* Task 7 context save space */ +u32 MCD_contextSave8[128]; /* Task 8 context save space */ +u32 MCD_contextSave9[128]; /* Task 9 context save space */ +u32 MCD_contextSave10[128]; /* Task 10 context save space */ +u32 MCD_contextSave11[128]; /* Task 11 context save space */ +u32 MCD_contextSave12[128]; /* Task 12 context save space */ +u32 MCD_contextSave13[128]; /* Task 13 context save space */ +u32 MCD_contextSave14[128]; /* Task 14 context save space */ +u32 MCD_contextSave15[128]; /* Task 15 context save space */ + +u32 MCD_ChainNoEu_TDT[]; +u32 MCD_SingleNoEu_TDT[]; +#ifdef MCD_INCLUDE_EU +u32 MCD_ChainEu_TDT[]; +u32 MCD_SingleEu_TDT[]; +#endif +u32 MCD_ENetRcv_TDT[]; +u32 MCD_ENetXmit_TDT[]; + +u32 MCD_modelTaskTableSrc[] = { + (u32) MCD_ChainNoEu_TDT, + (u32) & ((u8 *) MCD_ChainNoEu_TDT)[0x0000016c], + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_SingleNoEu_TDT, + (u32) & ((u8 *) MCD_SingleNoEu_TDT)[0x000000d4], + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +#ifdef MCD_INCLUDE_EU + (u32) MCD_ChainEu_TDT, + (u32) & ((u8 *) MCD_ChainEu_TDT)[0x000001b4], + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_SingleEu_TDT, + (u32) & ((u8 *) MCD_SingleEu_TDT)[0x00000124], + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +#endif + (u32) MCD_ENetRcv_TDT, + (u32) & ((u8 *) MCD_ENetRcv_TDT)[0x0000009c], + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + (u32) MCD_ENetXmit_TDT, + (u32) & ((u8 *) MCD_ENetXmit_TDT)[0x000000d0], + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +u32 MCD_ChainNoEu_TDT[] = { + 0x80004000, + 0x8118801b, + 0xb8c60018, + 0x10002b10, + 0x7000000d, + 0x018cf89f, + 0x6000000a, + 0x080cf89f, + 0x000001f8, + 0x98180364, + 0x8118801b, + 0xf8c6001a, + 0xb8c6601b, + 0x10002710, + 0x00000f18, + 0xb8c6001d, + 0x10001310, + 0x60000007, + 0x014cf88b, + 0x98c6001c, + 0x00000710, + 0x98c70018, + 0x10001f10, + 0x0000c818, + 0x000001f8, + 0xc1476018, + 0xc003231d, + 0x811a601b, + 0xc1862102, + 0x849be009, + 0x03fed7b8, + 0xda9b001b, + 0x9b9be01b, + 0x1000cb20, + 0x70000006, + 0x088cf88f, + 0x1000cb28, + 0x70000006, + 0x088cf88f, + 0x1000cb30, + 0x70000006, + 0x088cf88f, + 0x1000cb38, + 0x0000c728, + 0x000001f8, + 0xc1476018, + 0xc003241d, + 0x811a601b, + 0xda9b001b, + 0x9b9be01b, + 0x0000d3a0, + 0xc1862102, + 0x849be009, + 0x0bfed7b8, + 0xda9b001b, + 0x9b9be01b, + 0x1000cb20, + 0x70000006, + 0x088cf88f, + 0x1000cb28, + 0x70000006, + 0x088cf88f, + 0x1000cb30, + 0x70000006, + 0x088cf88f, + 0x1000cb38, + 0x0000c728, + 0x000001f8, + 0x8118801b, + 0xd8c60018, + 0x98c6601c, + 0x6000000b, + 0x0c8cfc9f, + 0x000001f8, + 0xa146001e, + 0x10000b08, + 0x10002050, + 0xb8c60018, + 0x10002b10, + 0x7000000a, + 0x080cf89f, + 0x6000000d, + 0x018cf89f, + 0x000001f8, + 0x8618801b, + 0x7000000e, + 0x084cf21f, + 0xd8990336, + 0x8019801b, + 0x040001f8, + 0x000001f8, + 0x000001f8, +}; + +u32 MCD_SingleNoEu_TDT[] = { + 0x8198001b, + 0x7000000d, + 0x080cf81f, + 0x8198801b, + 0x6000000e, + 0x084cf85f, + 0x000001f8, + 0x8298001b, + 0x7000000d, + 0x010cf81f, + 0x6000000e, + 0x018cf81f, + 0xc202601b, + 0xc002221c, + 0x809a601b, + 0xc10420c2, + 0x839be009, + 0x03fed7b8, + 0xda9b001b, + 0x9b9be01b, + 0x70000006, + 0x088cf889, + 0x1000cb28, + 0x70000006, + 0x088cf889, + 0x1000cb30, + 0x70000006, + 0x088cf889, + 0x0000cb38, + 0x000001f8, + 0xc202601b, + 0xc002229c, + 0x809a601b, + 0xda9b001b, + 0x9b9be01b, + 0x0000d3a0, + 0xc10420c2, + 0x839be009, + 0x0bfed7b8, + 0xda9b001b, + 0x9b9be01b, + 0x70000006, + 0x088cf889, + 0x1000cb28, + 0x70000006, + 0x088cf889, + 0x1000cb30, + 0x70000006, + 0x088cf889, + 0x0000cb38, + 0x000001f8, + 0xc318022d, + 0x8018801b, + 0x040001f8, +}; + +#ifdef MCD_INCLUDE_EU +u32 MCD_ChainEu_TDT[] = { + 0x80004000, + 0x8198801b, + 0xb8c68018, + 0x10002f10, + 0x7000000d, + 0x01ccf89f, + 0x6000000a, + 0x080cf89f, + 0x000001f8, + 0x981803a4, + 0x8198801b, + 0xf8c6801a, + 0xb8c6e01b, + 0x10002b10, + 0x00001318, + 0xb8c6801d, + 0x10001710, + 0x60000007, + 0x018cf88c, + 0x98c6801c, + 0x00000b10, + 0x98c78018, + 0x10002310, + 0x0000c820, + 0x000001f8, + 0x8698801b, + 0x7000000f, + 0x084cf2df, + 0xd899042d, + 0x8019801b, + 0x60000003, + 0x2cd7c7df, + 0xd8990364, + 0x8019801b, + 0x60000003, + 0x2c17c7df, + 0x000001f8, + 0xc1c7e018, + 0xc003a35e, + 0x819a601b, + 0xc206a142, + 0x851be009, + 0x63fe0000, + 0x0d4cfddf, + 0xda9b001b, + 0x9b9be01b, + 0x70000002, + 0x004cf81f, + 0x1000cb20, + 0x70000006, + 0x088cf891, + 0x1000cb28, + 0x70000006, + 0x088cf891, + 0x1000cb30, + 0x70000006, + 0x088cf891, + 0x1000cb38, + 0x0000c728, + 0x000001f8, + 0xc1c7e018, + 0xc003a49e, + 0x819a601b, + 0xda9b001b, + 0x9b9be01b, + 0x0000d3a0, + 0xc206a142, + 0x851be009, + 0x6bfe0000, + 0x0d4cfddf, + 0xda9b001b, + 0x9b9be01b, + 0x70000002, + 0x004cf81f, + 0x1000cb20, + 0x70000006, + 0x088cf891, + 0x1000cb28, + 0x70000006, + 0x088cf891, + 0x1000cb30, + 0x70000006, + 0x088cf891, + 0x1000cb38, + 0x0000c728, + 0x000001f8, + 0x8198801b, + 0xd8c68018, + 0x98c6e01c, + 0x6000000b, + 0x0c8cfc9f, + 0x0000cc08, + 0xa1c6801e, + 0x10000f08, + 0x10002458, + 0xb8c68018, + 0x10002f10, + 0x7000000a, + 0x080cf89f, + 0x6000000d, + 0x01ccf89f, + 0x000001f8, + 0x8698801b, + 0x7000000e, + 0x084cf25f, + 0xd899037f, + 0x8019801b, + 0x040001f8, + 0x000001f8, + 0x000001f8, +}; + +u32 MCD_SingleEu_TDT[] = { + 0x8218001b, + 0x7000000d, + 0x080cf81f, + 0x8218801b, + 0x6000000e, + 0x084cf85f, + 0x000001f8, + 0x8318001b, + 0x7000000d, + 0x014cf81f, + 0x6000000e, + 0x01ccf81f, + 0x8498001b, + 0x7000000f, + 0x080cf19f, + 0xd81882a4, + 0x8019001b, + 0x60000003, + 0x2c97c7df, + 0xd818826d, + 0x8019001b, + 0x60000003, + 0x2c17c7df, + 0x000001f8, + 0xc282e01b, + 0xc002a25e, + 0x811a601b, + 0xc184a102, + 0x841be009, + 0x63fe0000, + 0x0d4cfddf, + 0xda9b001b, + 0x9b9be01b, + 0x70000002, + 0x004cf99f, + 0x70000006, + 0x088cf88b, + 0x1000cb28, + 0x70000006, + 0x088cf88b, + 0x1000cb30, + 0x70000006, + 0x088cf88b, + 0x0000cb38, + 0x000001f8, + 0xc282e01b, + 0xc002a31e, + 0x811a601b, + 0xda9b001b, + 0x9b9be01b, + 0x0000d3a0, + 0xc184a102, + 0x841be009, + 0x6bfe0000, + 0x0d4cfddf, + 0xda9b001b, + 0x9b9be01b, + 0x70000002, + 0x004cf99f, + 0x70000006, + 0x088cf88b, + 0x1000cb28, + 0x70000006, + 0x088cf88b, + 0x1000cb30, + 0x70000006, + 0x088cf88b, + 0x0000cb38, + 0x000001f8, + 0x8144801c, + 0x0000c008, + 0xc398027f, + 0x8018801b, + 0x040001f8, +}; +#endif +u32 MCD_ENetRcv_TDT[] = { + 0x80004000, + 0x81988000, + 0x10000788, + 0x6000000a, + 0x080cf05f, + 0x98180209, + 0x81c40004, + 0x7000000e, + 0x010cf05f, + 0x7000000c, + 0x01ccf05f, + 0x70000004, + 0x014cf049, + 0x70000004, + 0x004cf04a, + 0x00000b88, + 0xc4030150, + 0x8119e012, + 0x03e0cf90, + 0x81188000, + 0x000ac788, + 0xc4030000, + 0x8199e000, + 0x70000004, + 0x084cfc8b, + 0x60000005, + 0x0cccf841, + 0x81c60000, + 0xc399021b, + 0x80198000, + 0x00008400, + 0x00000f08, + 0x81988000, + 0x10000788, + 0x6000000a, + 0x080cf05f, + 0xc2188209, + 0x80190000, + 0x040001f8, + 0x000001f8, +}; + +u32 MCD_ENetXmit_TDT[] = { + 0x80004000, + 0x81988000, + 0x10000788, + 0x6000000a, + 0x080cf05f, + 0x98180309, + 0x80004003, + 0x81c60004, + 0x7000000e, + 0x014cf05f, + 0x7000000c, + 0x028cf05f, + 0x7000000d, + 0x018cf05f, + 0x70000004, + 0x01ccf04d, + 0x10000b90, + 0x60000004, + 0x020cf0a1, + 0xc3188312, + 0x83c70000, + 0x00001f10, + 0xc583a3c3, + 0x81042325, + 0x03e0c798, + 0xd8990000, + 0x9999e000, + 0x000acf98, + 0xd8992306, + 0x9999e03f, + 0x03eac798, + 0xd8990000, + 0x9999e000, + 0x000acf98, + 0xd8990000, + 0x99832302, + 0x0beac798, + 0x81988000, + 0x6000000b, + 0x0c4cfc5f, + 0x81c80000, + 0xc5190312, + 0x80198000, + 0x00008400, + 0x00000f08, + 0x81988000, + 0x10000788, + 0x6000000a, + 0x080cf05f, + 0xc2988309, + 0x80190000, + 0x040001f8, + 0x000001f8, +}; + +#ifdef MCD_INCLUDE_EU +MCD_bufDesc MCD_singleBufDescs[NCHANNELS]; +#endif diff --git a/qemu/roms/u-boot/drivers/dma/MCD_tasksInit.c b/qemu/roms/u-boot/drivers/dma/MCD_tasksInit.c new file mode 100644 index 000000000..ce1ef891d --- /dev/null +++ b/qemu/roms/u-boot/drivers/dma/MCD_tasksInit.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +/* Functions for initializing variable tables of different types of tasks. */ + +/* + * Do not edit! + */ + +#include + +extern dmaRegs *MCD_dmaBar; + +/* Task 0 */ + +void MCD_startDmaChainNoEu(int *currBD, short srcIncr, short destIncr, + int xferSize, short xferSizeIncr, int *cSave, + volatile TaskTableEntry * taskTable, int channel) +{ + volatile TaskTableEntry *taskChan = taskTable + channel; + + MCD_SET_VAR(taskChan, 2, (u32) currBD); /* var[2] */ + MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */ + MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */ + MCD_SET_VAR(taskChan, 11, (u32) xferSize); /* var[11] */ + MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */ + MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */ + MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ + MCD_SET_VAR(taskChan, 3, (u32) 0x00000000); /* var[3] */ + MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ + MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ + MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */ + MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ + MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ + MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */ + MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */ + MCD_SET_VAR(taskChan, 12, (u32) 0x00000000); /* var[12] */ + MCD_SET_VAR(taskChan, 13, (u32) 0x80000000); /* var[13] */ + MCD_SET_VAR(taskChan, 14, (u32) 0x00000010); /* var[14] */ + MCD_SET_VAR(taskChan, 15, (u32) 0x00000004); /* var[15] */ + MCD_SET_VAR(taskChan, 16, (u32) 0x08000000); /* var[16] */ + MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */ + MCD_SET_VAR(taskChan, 28, (u32) 0x80000000); /* inc[4] */ + MCD_SET_VAR(taskChan, 29, (u32) 0x80000001); /* inc[5] */ + MCD_SET_VAR(taskChan, 30, (u32) 0x40000000); /* inc[6] */ + + /* Set the task's Enable bit in its Task Control Register */ + MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; +} + +/* Task 1 */ + +void MCD_startDmaSingleNoEu(char *srcAddr, short srcIncr, char *destAddr, + short destIncr, int dmaSize, short xferSizeIncr, + int flags, int *currBD, int *cSave, + volatile TaskTableEntry * taskTable, int channel) +{ + volatile TaskTableEntry *taskChan = taskTable + channel; + + MCD_SET_VAR(taskChan, 7, (u32) srcAddr); /* var[7] */ + MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */ + MCD_SET_VAR(taskChan, 2, (u32) destAddr); /* var[2] */ + MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */ + MCD_SET_VAR(taskChan, 3, (u32) dmaSize); /* var[3] */ + MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */ + MCD_SET_VAR(taskChan, 5, (u32) flags); /* var[5] */ + MCD_SET_VAR(taskChan, 1, (u32) currBD); /* var[1] */ + MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */ + MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ + MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */ + MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ + MCD_SET_VAR(taskChan, 9, (u32) 0x00000004); /* var[9] */ + MCD_SET_VAR(taskChan, 10, (u32) 0x08000000); /* var[10] */ + MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */ + MCD_SET_VAR(taskChan, 28, (u32) 0x80000001); /* inc[4] */ + MCD_SET_VAR(taskChan, 29, (u32) 0x40000000); /* inc[5] */ + + /* Set the task's Enable bit in its Task Control Register */ + MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; +} + +/* Task 2 */ + +void MCD_startDmaChainEu(int *currBD, short srcIncr, short destIncr, + int xferSize, short xferSizeIncr, int *cSave, + volatile TaskTableEntry * taskTable, int channel) +{ + volatile TaskTableEntry *taskChan = taskTable + channel; + + MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */ + MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */ + MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */ + MCD_SET_VAR(taskChan, 12, (u32) xferSize); /* var[12] */ + MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */ + MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */ + MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ + MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */ + MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ + MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ + MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */ + MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ + MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ + MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */ + MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */ + MCD_SET_VAR(taskChan, 11, (u32) 0x00000000); /* var[11] */ + MCD_SET_VAR(taskChan, 13, (u32) 0x00000000); /* var[13] */ + MCD_SET_VAR(taskChan, 14, (u32) 0x80000000); /* var[14] */ + MCD_SET_VAR(taskChan, 15, (u32) 0x00000010); /* var[15] */ + MCD_SET_VAR(taskChan, 16, (u32) 0x00000001); /* var[16] */ + MCD_SET_VAR(taskChan, 17, (u32) 0x00000004); /* var[17] */ + MCD_SET_VAR(taskChan, 18, (u32) 0x08000000); /* var[18] */ + MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */ + MCD_SET_VAR(taskChan, 28, (u32) 0x80000000); /* inc[4] */ + MCD_SET_VAR(taskChan, 29, (u32) 0xc0000000); /* inc[5] */ + MCD_SET_VAR(taskChan, 30, (u32) 0x80000001); /* inc[6] */ + MCD_SET_VAR(taskChan, 31, (u32) 0x40000000); /* inc[7] */ + + /* Set the task's Enable bit in its Task Control Register */ + MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; +} + +/* Task 3 */ + +void MCD_startDmaSingleEu(char *srcAddr, short srcIncr, char *destAddr, + short destIncr, int dmaSize, short xferSizeIncr, + int flags, int *currBD, int *cSave, + volatile TaskTableEntry * taskTable, int channel) +{ + volatile TaskTableEntry *taskChan = taskTable + channel; + + MCD_SET_VAR(taskChan, 8, (u32) srcAddr); /* var[8] */ + MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */ + MCD_SET_VAR(taskChan, 3, (u32) destAddr); /* var[3] */ + MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */ + MCD_SET_VAR(taskChan, 4, (u32) dmaSize); /* var[4] */ + MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */ + MCD_SET_VAR(taskChan, 6, (u32) flags); /* var[6] */ + MCD_SET_VAR(taskChan, 2, (u32) currBD); /* var[2] */ + MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */ + MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ + MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ + MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ + MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */ + MCD_SET_VAR(taskChan, 10, (u32) 0x00000001); /* var[10] */ + MCD_SET_VAR(taskChan, 11, (u32) 0x00000004); /* var[11] */ + MCD_SET_VAR(taskChan, 12, (u32) 0x08000000); /* var[12] */ + MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */ + MCD_SET_VAR(taskChan, 28, (u32) 0xc0000000); /* inc[4] */ + MCD_SET_VAR(taskChan, 29, (u32) 0x80000000); /* inc[5] */ + MCD_SET_VAR(taskChan, 30, (u32) 0x80000001); /* inc[6] */ + MCD_SET_VAR(taskChan, 31, (u32) 0x40000000); /* inc[7] */ + + /* Set the task's Enable bit in its Task Control Register */ + MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; +} + +/* Task 4 */ + +void MCD_startDmaENetRcv(char *bDBase, char *currBD, char *rcvFifoPtr, + volatile TaskTableEntry * taskTable, int channel) +{ + volatile TaskTableEntry *taskChan = taskTable + channel; + + MCD_SET_VAR(taskChan, 0, (u32) bDBase); /* var[0] */ + MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */ + MCD_SET_VAR(taskChan, 6, (u32) rcvFifoPtr); /* var[6] */ + MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ + MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */ + MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ + MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ + MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ + MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ + MCD_SET_VAR(taskChan, 9, (u32) 0x0000ffff); /* var[9] */ + MCD_SET_VAR(taskChan, 10, (u32) 0x30000000); /* var[10] */ + MCD_SET_VAR(taskChan, 11, (u32) 0x0fffffff); /* var[11] */ + MCD_SET_VAR(taskChan, 12, (u32) 0x00000008); /* var[12] */ + MCD_SET_VAR(taskChan, 24, (u32) 0x00000000); /* inc[0] */ + MCD_SET_VAR(taskChan, 25, (u32) 0x60000000); /* inc[1] */ + MCD_SET_VAR(taskChan, 26, (u32) 0x20000004); /* inc[2] */ + MCD_SET_VAR(taskChan, 27, (u32) 0x40000000); /* inc[3] */ + + /* Set the task's Enable bit in its Task Control Register */ + MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; +} + +/* Task 5 */ + +void MCD_startDmaENetXmit(char *bDBase, char *currBD, char *xmitFifoPtr, + volatile TaskTableEntry * taskTable, int channel) +{ + volatile TaskTableEntry *taskChan = taskTable + channel; + + MCD_SET_VAR(taskChan, 0, (u32) bDBase); /* var[0] */ + MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */ + MCD_SET_VAR(taskChan, 11, (u32) xmitFifoPtr); /* var[11] */ + MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */ + MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */ + MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */ + MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */ + MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */ + MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */ + MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */ + MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */ + MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */ + MCD_SET_VAR(taskChan, 12, (u32) 0x00000000); /* var[12] */ + MCD_SET_VAR(taskChan, 13, (u32) 0x0000ffff); /* var[13] */ + MCD_SET_VAR(taskChan, 14, (u32) 0xffffffff); /* var[14] */ + MCD_SET_VAR(taskChan, 15, (u32) 0x00000004); /* var[15] */ + MCD_SET_VAR(taskChan, 16, (u32) 0x00000008); /* var[16] */ + MCD_SET_VAR(taskChan, 24, (u32) 0x00000000); /* inc[0] */ + MCD_SET_VAR(taskChan, 25, (u32) 0x60000000); /* inc[1] */ + MCD_SET_VAR(taskChan, 26, (u32) 0x40000000); /* inc[2] */ + MCD_SET_VAR(taskChan, 27, (u32) 0xc000fffc); /* inc[3] */ + MCD_SET_VAR(taskChan, 28, (u32) 0xe0000004); /* inc[4] */ + MCD_SET_VAR(taskChan, 29, (u32) 0x80000000); /* inc[5] */ + MCD_SET_VAR(taskChan, 30, (u32) 0x4000ffff); /* inc[6] */ + MCD_SET_VAR(taskChan, 31, (u32) 0xe0000001); /* inc[7] */ + + /* Set the task's Enable bit in its Task Control Register */ + MCD_dmaBar->taskControl[channel] |= (u16) 0x8000; +} diff --git a/qemu/roms/u-boot/drivers/dma/Makefile b/qemu/roms/u-boot/drivers/dma/Makefile new file mode 100644 index 000000000..8b2821b76 --- /dev/null +++ b/qemu/roms/u-boot/drivers/dma/Makefile @@ -0,0 +1,11 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o +obj-$(CONFIG_APBH_DMA) += apbh_dma.o +obj-$(CONFIG_FSL_DMA) += fsl_dma.o +obj-$(CONFIG_OMAP3_DMA) += omap3_dma.o diff --git a/qemu/roms/u-boot/drivers/dma/apbh_dma.c b/qemu/roms/u-boot/drivers/dma/apbh_dma.c new file mode 100644 index 000000000..22defcd7d --- /dev/null +++ b/qemu/roms/u-boot/drivers/dma/apbh_dma.c @@ -0,0 +1,616 @@ +/* + * Freescale i.MX28 APBH DMA driver + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS]; + +/* + * Test is the DMA channel is valid channel + */ +int mxs_dma_validate_chan(int channel) +{ + struct mxs_dma_chan *pchan; + + if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS)) + return -EINVAL; + + pchan = mxs_dma_channels + channel; + if (!(pchan->flags & MXS_DMA_FLAGS_ALLOCATED)) + return -EINVAL; + + return 0; +} + +/* + * Return the address of the command within a descriptor. + */ +static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc) +{ + return desc->address + offsetof(struct mxs_dma_desc, cmd); +} + +/* + * Read a DMA channel's hardware semaphore. + * + * As used by the MXS platform's DMA software, the DMA channel's hardware + * semaphore reflects the number of DMA commands the hardware will process, but + * has not yet finished. This is a volatile value read directly from hardware, + * so it must be be viewed as immediately stale. + * + * If the channel is not marked busy, or has finished processing all its + * commands, this value should be zero. + * + * See mxs_dma_append() for details on how DMA command blocks must be configured + * to maintain the expected behavior of the semaphore's value. + */ +static int mxs_dma_read_semaphore(int channel) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + uint32_t tmp; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + tmp = readl(&apbh_regs->ch[channel].hw_apbh_ch_sema); + + tmp &= APBH_CHn_SEMA_PHORE_MASK; + tmp >>= APBH_CHn_SEMA_PHORE_OFFSET; + + return tmp; +} + +#ifndef CONFIG_SYS_DCACHE_OFF +void mxs_dma_flush_desc(struct mxs_dma_desc *desc) +{ + uint32_t addr; + uint32_t size; + + addr = (uint32_t)desc; + size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT); + + flush_dcache_range(addr, addr + size); +} +#else +inline void mxs_dma_flush_desc(struct mxs_dma_desc *desc) {} +#endif + +/* + * Enable a DMA channel. + * + * If the given channel has any DMA descriptors on its active list, this + * function causes the DMA hardware to begin processing them. + * + * This function marks the DMA channel as "busy," whether or not there are any + * descriptors to process. + */ +static int mxs_dma_enable(int channel) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + unsigned int sem; + struct mxs_dma_chan *pchan; + struct mxs_dma_desc *pdesc; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + if (pchan->pending_num == 0) { + pchan->flags |= MXS_DMA_FLAGS_BUSY; + return 0; + } + + pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node); + if (pdesc == NULL) + return -EFAULT; + + if (pchan->flags & MXS_DMA_FLAGS_BUSY) { + if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN)) + return 0; + + sem = mxs_dma_read_semaphore(channel); + if (sem == 0) + return 0; + + if (sem == 1) { + pdesc = list_entry(pdesc->node.next, + struct mxs_dma_desc, node); + writel(mxs_dma_cmd_address(pdesc), + &apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar); + } + writel(pchan->pending_num, + &apbh_regs->ch[channel].hw_apbh_ch_sema); + pchan->active_num += pchan->pending_num; + pchan->pending_num = 0; + } else { + pchan->active_num += pchan->pending_num; + pchan->pending_num = 0; + writel(mxs_dma_cmd_address(pdesc), + &apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar); + writel(pchan->active_num, + &apbh_regs->ch[channel].hw_apbh_ch_sema); + writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), + &apbh_regs->hw_apbh_ctrl0_clr); + } + + pchan->flags |= MXS_DMA_FLAGS_BUSY; + return 0; +} + +/* + * Disable a DMA channel. + * + * This function shuts down a DMA channel and marks it as "not busy." Any + * descriptors on the active list are immediately moved to the head of the + * "done" list, whether or not they have actually been processed by the + * hardware. The "ready" flags of these descriptors are NOT cleared, so they + * still appear to be active. + * + * This function immediately shuts down a DMA channel's hardware, aborting any + * I/O that may be in progress, potentially leaving I/O hardware in an undefined + * state. It is unwise to call this function if there is ANY chance the hardware + * is still processing a command. + */ +static int mxs_dma_disable(int channel) +{ + struct mxs_dma_chan *pchan; + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + if (!(pchan->flags & MXS_DMA_FLAGS_BUSY)) + return -EINVAL; + + writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), + &apbh_regs->hw_apbh_ctrl0_set); + + pchan->flags &= ~MXS_DMA_FLAGS_BUSY; + pchan->active_num = 0; + pchan->pending_num = 0; + list_splice_init(&pchan->active, &pchan->done); + + return 0; +} + +/* + * Resets the DMA channel hardware. + */ +static int mxs_dma_reset(int channel) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + int ret; +#if defined(CONFIG_MX23) + uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_ctrl0_set); + uint32_t offset = APBH_CTRL0_RESET_CHANNEL_OFFSET; +#elif (defined(CONFIG_MX28) || defined(CONFIG_MX6)) + uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_channel_ctrl_set); + uint32_t offset = APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET; +#endif + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + writel(1 << (channel + offset), setreg); + + return 0; +} + +/* + * Enable or disable DMA interrupt. + * + * This function enables the given DMA channel to interrupt the CPU. + */ +static int mxs_dma_enable_irq(int channel, int enable) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + if (enable) + writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET), + &apbh_regs->hw_apbh_ctrl1_set); + else + writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET), + &apbh_regs->hw_apbh_ctrl1_clr); + + return 0; +} + +/* + * Clear DMA interrupt. + * + * The software that is using the DMA channel must register to receive its + * interrupts and, when they arrive, must call this function to clear them. + */ +static int mxs_dma_ack_irq(int channel) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + writel(1 << channel, &apbh_regs->hw_apbh_ctrl1_clr); + writel(1 << channel, &apbh_regs->hw_apbh_ctrl2_clr); + + return 0; +} + +/* + * Request to reserve a DMA channel + */ +static int mxs_dma_request(int channel) +{ + struct mxs_dma_chan *pchan; + + if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS)) + return -EINVAL; + + pchan = mxs_dma_channels + channel; + if ((pchan->flags & MXS_DMA_FLAGS_VALID) != MXS_DMA_FLAGS_VALID) + return -ENODEV; + + if (pchan->flags & MXS_DMA_FLAGS_ALLOCATED) + return -EBUSY; + + pchan->flags |= MXS_DMA_FLAGS_ALLOCATED; + pchan->active_num = 0; + pchan->pending_num = 0; + + INIT_LIST_HEAD(&pchan->active); + INIT_LIST_HEAD(&pchan->done); + + return 0; +} + +/* + * Release a DMA channel. + * + * This function releases a DMA channel from its current owner. + * + * The channel will NOT be released if it's marked "busy" (see + * mxs_dma_enable()). + */ +int mxs_dma_release(int channel) +{ + struct mxs_dma_chan *pchan; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + if (pchan->flags & MXS_DMA_FLAGS_BUSY) + return -EBUSY; + + pchan->dev = 0; + pchan->active_num = 0; + pchan->pending_num = 0; + pchan->flags &= ~MXS_DMA_FLAGS_ALLOCATED; + + return 0; +} + +/* + * Allocate DMA descriptor + */ +struct mxs_dma_desc *mxs_dma_desc_alloc(void) +{ + struct mxs_dma_desc *pdesc; + uint32_t size; + + size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT); + pdesc = memalign(MXS_DMA_ALIGNMENT, size); + + if (pdesc == NULL) + return NULL; + + memset(pdesc, 0, sizeof(*pdesc)); + pdesc->address = (dma_addr_t)pdesc; + + return pdesc; +}; + +/* + * Free DMA descriptor + */ +void mxs_dma_desc_free(struct mxs_dma_desc *pdesc) +{ + if (pdesc == NULL) + return; + + free(pdesc); +} + +/* + * Add a DMA descriptor to a channel. + * + * If the descriptor list for this channel is not empty, this function sets the + * CHAIN bit and the NEXTCMD_ADDR fields in the last descriptor's DMA command so + * it will chain to the new descriptor's command. + * + * Then, this function marks the new descriptor as "ready," adds it to the end + * of the active descriptor list, and increments the count of pending + * descriptors. + * + * The MXS platform DMA software imposes some rules on DMA commands to maintain + * important invariants. These rules are NOT checked, but they must be carefully + * applied by software that uses MXS DMA channels. + * + * Invariant: + * The DMA channel's hardware semaphore must reflect the number of DMA + * commands the hardware will process, but has not yet finished. + * + * Explanation: + * A DMA channel begins processing commands when its hardware semaphore is + * written with a value greater than zero, and it stops processing commands + * when the semaphore returns to zero. + * + * When a channel finishes a DMA command, it will decrement its semaphore if + * the DECREMENT_SEMAPHORE bit is set in that command's flags bits. + * + * In principle, it's not necessary for the DECREMENT_SEMAPHORE to be set, + * unless it suits the purposes of the software. For example, one could + * construct a series of five DMA commands, with the DECREMENT_SEMAPHORE + * bit set only in the last one. Then, setting the DMA channel's hardware + * semaphore to one would cause the entire series of five commands to be + * processed. However, this example would violate the invariant given above. + * + * Rule: + * ALL DMA commands MUST have the DECREMENT_SEMAPHORE bit set so that the DMA + * channel's hardware semaphore will be decremented EVERY time a command is + * processed. + */ +int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc) +{ + struct mxs_dma_chan *pchan; + struct mxs_dma_desc *last; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + pdesc->cmd.next = mxs_dma_cmd_address(pdesc); + pdesc->flags |= MXS_DMA_DESC_FIRST | MXS_DMA_DESC_LAST; + + if (!list_empty(&pchan->active)) { + last = list_entry(pchan->active.prev, struct mxs_dma_desc, + node); + + pdesc->flags &= ~MXS_DMA_DESC_FIRST; + last->flags &= ~MXS_DMA_DESC_LAST; + + last->cmd.next = mxs_dma_cmd_address(pdesc); + last->cmd.data |= MXS_DMA_DESC_CHAIN; + + mxs_dma_flush_desc(last); + } + pdesc->flags |= MXS_DMA_DESC_READY; + if (pdesc->flags & MXS_DMA_DESC_FIRST) + pchan->pending_num++; + list_add_tail(&pdesc->node, &pchan->active); + + mxs_dma_flush_desc(pdesc); + + return ret; +} + +/* + * Clean up processed DMA descriptors. + * + * This function removes processed DMA descriptors from the "active" list. Pass + * in a non-NULL list head to get the descriptors moved to your list. Pass NULL + * to get the descriptors moved to the channel's "done" list. Descriptors on + * the "done" list can be retrieved with mxs_dma_get_finished(). + * + * This function marks the DMA channel as "not busy" if no unprocessed + * descriptors remain on the "active" list. + */ +static int mxs_dma_finish(int channel, struct list_head *head) +{ + int sem; + struct mxs_dma_chan *pchan; + struct list_head *p, *q; + struct mxs_dma_desc *pdesc; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + sem = mxs_dma_read_semaphore(channel); + if (sem < 0) + return sem; + + if (sem == pchan->active_num) + return 0; + + list_for_each_safe(p, q, &pchan->active) { + if ((pchan->active_num) <= sem) + break; + + pdesc = list_entry(p, struct mxs_dma_desc, node); + pdesc->flags &= ~MXS_DMA_DESC_READY; + + if (head) + list_move_tail(p, head); + else + list_move_tail(p, &pchan->done); + + if (pdesc->flags & MXS_DMA_DESC_LAST) + pchan->active_num--; + } + + if (sem == 0) + pchan->flags &= ~MXS_DMA_FLAGS_BUSY; + + return 0; +} + +/* + * Wait for DMA channel to complete + */ +static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(chan); + if (ret) + return ret; + + if (mxs_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg, + 1 << chan, timeout)) { + ret = -ETIMEDOUT; + mxs_dma_reset(chan); + } + + return ret; +} + +/* + * Execute the DMA channel + */ +int mxs_dma_go(int chan) +{ + uint32_t timeout = 10000000; + int ret; + + LIST_HEAD(tmp_desc_list); + + mxs_dma_enable_irq(chan, 1); + mxs_dma_enable(chan); + + /* Wait for DMA to finish. */ + ret = mxs_dma_wait_complete(timeout, chan); + + /* Clear out the descriptors we just ran. */ + mxs_dma_finish(chan, &tmp_desc_list); + + /* Shut the DMA channel down. */ + mxs_dma_ack_irq(chan); + mxs_dma_reset(chan); + mxs_dma_enable_irq(chan, 0); + mxs_dma_disable(chan); + + return ret; +} + +/* + * Execute a continuously running circular DMA descriptor. + * NOTE: This is not intended for general use, but rather + * for the LCD driver in Smart-LCD mode. It allows + * continuous triggering of the RUN bit there. + */ +void mxs_dma_circ_start(int chan, struct mxs_dma_desc *pdesc) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + + mxs_dma_flush_desc(pdesc); + + mxs_dma_enable_irq(chan, 1); + + writel(mxs_dma_cmd_address(pdesc), + &apbh_regs->ch[chan].hw_apbh_ch_nxtcmdar); + writel(1, &apbh_regs->ch[chan].hw_apbh_ch_sema); + writel(1 << (chan + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), + &apbh_regs->hw_apbh_ctrl0_clr); +} + +/* + * Initialize the DMA hardware + */ +void mxs_dma_init(void) +{ + struct mxs_apbh_regs *apbh_regs = + (struct mxs_apbh_regs *)MXS_APBH_BASE; + + mxs_reset_block(&apbh_regs->hw_apbh_ctrl0_reg); + +#ifdef CONFIG_APBH_DMA_BURST8 + writel(APBH_CTRL0_AHB_BURST8_EN, + &apbh_regs->hw_apbh_ctrl0_set); +#else + writel(APBH_CTRL0_AHB_BURST8_EN, + &apbh_regs->hw_apbh_ctrl0_clr); +#endif + +#ifdef CONFIG_APBH_DMA_BURST + writel(APBH_CTRL0_APB_BURST_EN, + &apbh_regs->hw_apbh_ctrl0_set); +#else + writel(APBH_CTRL0_APB_BURST_EN, + &apbh_regs->hw_apbh_ctrl0_clr); +#endif +} + +int mxs_dma_init_channel(int channel) +{ + struct mxs_dma_chan *pchan; + int ret; + + pchan = mxs_dma_channels + channel; + pchan->flags = MXS_DMA_FLAGS_VALID; + + ret = mxs_dma_request(channel); + + if (ret) { + printf("MXS DMA: Can't acquire DMA channel %i\n", + channel); + return ret; + } + + mxs_dma_reset(channel); + mxs_dma_ack_irq(channel); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/dma/fsl_dma.c b/qemu/roms/u-boot/drivers/dma/fsl_dma.c new file mode 100644 index 000000000..45e49c7fa --- /dev/null +++ b/qemu/roms/u-boot/drivers/dma/fsl_dma.c @@ -0,0 +1,168 @@ +/* + * Copyright 2004,2007,2008 Freescale Semiconductor, Inc. + * (C) Copyright 2002, 2003 Motorola Inc. + * Xianghua Xiao (X.Xiao@motorola.com) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* Controller can only transfer 2^26 - 1 bytes at a time */ +#define FSL_DMA_MAX_SIZE (0x3ffffff) + +#if defined(CONFIG_MPC83xx) +#define FSL_DMA_MR_DEFAULT (FSL_DMA_MR_CTM_DIRECT | FSL_DMA_MR_DMSEN) +#else +#define FSL_DMA_MR_DEFAULT (FSL_DMA_MR_BWC_DIS | FSL_DMA_MR_CTM_DIRECT) +#endif + + +#if defined(CONFIG_MPC83xx) +dma83xx_t *dma_base = (void *)(CONFIG_SYS_MPC83xx_DMA_ADDR); +#elif defined(CONFIG_MPC85xx) +ccsr_dma_t *dma_base = (void *)(CONFIG_SYS_MPC85xx_DMA_ADDR); +#elif defined(CONFIG_MPC86xx) +ccsr_dma_t *dma_base = (void *)(CONFIG_SYS_MPC86xx_DMA_ADDR); +#else +#error "Freescale DMA engine not supported on your processor" +#endif + +static void dma_sync(void) +{ +#if defined(CONFIG_MPC85xx) + asm("sync; isync; msync"); +#elif defined(CONFIG_MPC86xx) + asm("sync; isync"); +#endif +} + +static void out_dma32(volatile unsigned *addr, int val) +{ +#if defined(CONFIG_MPC83xx) + out_le32(addr, val); +#else + out_be32(addr, val); +#endif +} + +static uint in_dma32(volatile unsigned *addr) +{ +#if defined(CONFIG_MPC83xx) + return in_le32(addr); +#else + return in_be32(addr); +#endif +} + +static uint dma_check(void) { + volatile fsl_dma_t *dma = &dma_base->dma[0]; + uint status; + + /* While the channel is busy, spin */ + do { + status = in_dma32(&dma->sr); + } while (status & FSL_DMA_SR_CB); + + /* clear MR[CS] channel start bit */ + out_dma32(&dma->mr, in_dma32(&dma->mr) & ~FSL_DMA_MR_CS); + dma_sync(); + + if (status != 0) + printf ("DMA Error: status = %x\n", status); + + return status; +} + +#if !defined(CONFIG_MPC83xx) +void dma_init(void) { + volatile fsl_dma_t *dma = &dma_base->dma[0]; + + out_dma32(&dma->satr, FSL_DMA_SATR_SREAD_SNOOP); + out_dma32(&dma->datr, FSL_DMA_DATR_DWRITE_SNOOP); + out_dma32(&dma->sr, 0xffffffff); /* clear any errors */ + dma_sync(); +} +#endif + +int dmacpy(phys_addr_t dest, phys_addr_t src, phys_size_t count) { + volatile fsl_dma_t *dma = &dma_base->dma[0]; + uint xfer_size; + + while (count) { + xfer_size = MIN(FSL_DMA_MAX_SIZE, count); + + out_dma32(&dma->dar, (u32) (dest & 0xFFFFFFFF)); + out_dma32(&dma->sar, (u32) (src & 0xFFFFFFFF)); +#if !defined(CONFIG_MPC83xx) + out_dma32(&dma->satr, + in_dma32(&dma->satr) | (u32)((u64)src >> 32)); + out_dma32(&dma->datr, + in_dma32(&dma->datr) | (u32)((u64)dest >> 32)); +#endif + out_dma32(&dma->bcr, xfer_size); + dma_sync(); + + /* Prepare mode register */ + out_dma32(&dma->mr, FSL_DMA_MR_DEFAULT); + dma_sync(); + + /* Start the transfer */ + out_dma32(&dma->mr, FSL_DMA_MR_DEFAULT | FSL_DMA_MR_CS); + + count -= xfer_size; + src += xfer_size; + dest += xfer_size; + + dma_sync(); + + if (dma_check()) + return -1; + } + + return 0; +} + +/* + * 85xx/86xx use dma to initialize SDRAM when !CONFIG_ECC_INIT_VIA_DDRCONTROLLER + * while 83xx uses dma to initialize SDRAM when CONFIG_DDR_ECC_INIT_VIA_DMA + */ +#if ((!defined CONFIG_MPC83xx && defined(CONFIG_DDR_ECC) && \ + !defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER)) || \ + (defined(CONFIG_MPC83xx) && defined(CONFIG_DDR_ECC_INIT_VIA_DMA))) +void dma_meminit(uint val, uint size) +{ + uint *p = 0; + uint i = 0; + + for (*p = 0; p < (uint *)(8 * 1024); p++) { + if (((uint)p & 0x1f) == 0) + ppcDcbz((ulong)p); + + *p = (uint)CONFIG_MEM_INIT_VALUE; + + if (((uint)p & 0x1c) == 0x1c) + ppcDcbf((ulong)p); + } + + dmacpy(0x002000, 0, 0x002000); /* 8K */ + dmacpy(0x004000, 0, 0x004000); /* 16K */ + dmacpy(0x008000, 0, 0x008000); /* 32K */ + dmacpy(0x010000, 0, 0x010000); /* 64K */ + dmacpy(0x020000, 0, 0x020000); /* 128K */ + dmacpy(0x040000, 0, 0x040000); /* 256K */ + dmacpy(0x080000, 0, 0x080000); /* 512K */ + dmacpy(0x100000, 0, 0x100000); /* 1M */ + dmacpy(0x200000, 0, 0x200000); /* 2M */ + dmacpy(0x400000, 0, 0x400000); /* 4M */ + + for (i = 1; i < size / 0x800000; i++) + dmacpy((0x800000 * i), 0, 0x800000); +} +#endif diff --git a/qemu/roms/u-boot/drivers/dma/omap3_dma.c b/qemu/roms/u-boot/drivers/dma/omap3_dma.c new file mode 100644 index 000000000..3320b3d08 --- /dev/null +++ b/qemu/roms/u-boot/drivers/dma/omap3_dma.c @@ -0,0 +1,167 @@ +/* Copyright (C) 2011 + * Corscience GmbH & Co. KG - Simon Schwarz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* This is a basic implementation of the SDMA/DMA4 controller of OMAP3 + * Tested on Silicon Revision major:0x4 minor:0x0 + */ + +#include +#include +#include +#include +#include +#include + +static struct dma4 *dma4_cfg = (struct dma4 *)OMAP34XX_DMA4_BASE; +uint32_t dma_active; /* if a transfer is started the respective + bit is set for the logical channel */ + +/* Check if we have the given channel + * PARAMETERS: + * chan: Channel number + * + * RETURN of non-zero means error */ +static inline int check_channel(uint32_t chan) +{ + if (chan < CHAN_NR_MIN || chan > CHAN_NR_MAX) + return -EINVAL; + return 0; +} + +static inline void reset_irq(uint32_t chan) +{ + /* reset IRQ reason */ + writel(0x1DFE, &dma4_cfg->chan[chan].csr); + /* reset IRQ */ + writel((1 << chan), &dma4_cfg->irqstatus_l[0]); + dma_active &= ~(1 << chan); +} + +/* Set Source, Destination and Size of DMA transfer for the + * specified channel. + * PARAMETERS: + * chan: channel to use + * src: source of the transfer + * dst: destination of the transfer + * sze: Size of the transfer + * + * RETURN of non-zero means error */ +int omap3_dma_conf_transfer(uint32_t chan, uint32_t *src, uint32_t *dst, + uint32_t sze) +{ + if (check_channel(chan)) + return -EINVAL; + /* CDSA0 */ + writel((uint32_t)src, &dma4_cfg->chan[chan].cssa); + writel((uint32_t)dst, &dma4_cfg->chan[chan].cdsa); + writel(sze, &dma4_cfg->chan[chan].cen); +return 0; +} + +/* Start the DMA transfer */ +int omap3_dma_start_transfer(uint32_t chan) +{ + uint32_t val; + + if (check_channel(chan)) + return -EINVAL; + + val = readl(&dma4_cfg->chan[chan].ccr); + /* Test for channel already in use */ + if (val & CCR_ENABLE_ENABLE) + return -EBUSY; + + writel((val | CCR_ENABLE_ENABLE), &dma4_cfg->chan[chan].ccr); + dma_active |= (1 << chan); + debug("started transfer...\n"); + return 0; +} + +/* Busy-waiting for a DMA transfer + * This has to be called before another transfer is started + * PARAMETER + * chan: Channel to wait for + * + * RETURN of non-zero means error*/ +int omap3_dma_wait_for_transfer(uint32_t chan) +{ + uint32_t val; + + if (!(dma_active & (1 << chan))) { + val = readl(&dma4_cfg->irqstatus_l[0]); + if (!(val & chan)) { + debug("dma: The channel you are trying to wait for " + "was never activated - ERROR\n"); + return -1; /* channel was never active */ + } + } + + /* all irqs on line 0 */ + while (!(readl(&dma4_cfg->irqstatus_l[0]) & (1 << chan))) + asm("nop"); + + val = readl(&dma4_cfg->chan[chan].csr); + if ((val & CSR_TRANS_ERR) | (val & CSR_SUPERVISOR_ERR) | + (val & CSR_MISALIGNED_ADRS_ERR)) { + debug("err code: %X\n", val); + debug("dma: transfer error detected\n"); + reset_irq(chan); + return -1; + } + reset_irq(chan); + return 0; +} + +/* Get the revision of the DMA module + * PARAMETER + * minor: Address of minor revision to write + * major: Address of major revision to write + * + * RETURN of non-zero means error + */ +int omap3_dma_get_revision(uint32_t *minor, uint32_t *major) +{ + uint32_t val; + + /* debug information */ + val = readl(&dma4_cfg->revision); + *major = (val & 0x000000F0) >> 4; + *minor = (val & 0x0000000F); + debug("DMA Silicon revision (maj/min): 0x%X/0x%X\n", *major, *minor); + return 0; +} + +/* Initial config of omap dma + */ +void omap3_dma_init(void) +{ + dma_active = 0; + /* All interrupts on channel 0 */ + writel(0xFFFFFFFF, &dma4_cfg->irqenable_l[0]); +} + +/* set channel config to config + * + * RETURN of non-zero means error */ +int omap3_dma_conf_chan(uint32_t chan, struct dma4_chan *config) +{ + if (check_channel(chan)) + return -EINVAL; + + dma4_cfg->chan[chan] = *config; + return 0; +} + +/* get channel config to config + * + * RETURN of non-zero means error */ +int omap3_dma_get_conf_chan(uint32_t chan, struct dma4_chan *config) +{ + if (check_channel(chan)) + return -EINVAL; + *config = dma4_cfg->chan[chan]; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/fpga/ACEX1K.c b/qemu/roms/u-boot/drivers/fpga/ACEX1K.c new file mode 100644 index 000000000..06b88372e --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/ACEX1K.c @@ -0,0 +1,249 @@ +/* + * (C) Copyright 2003 + * Steven Scholz, imc Measurement & Control, steven.scholz@imc-berlin.de + * + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include /* core U-Boot definitions */ +#include /* ACEX device family */ + +/* Define FPGA_DEBUG to get debug printf's */ +#ifdef FPGA_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +/* Note: The assumption is that we cannot possibly run fast enough to + * overrun the device (the Slave Parallel mode can free run at 50MHz). + * If there is a need to operate slower, define CONFIG_FPGA_DELAY in + * the board config file to slow things down. + */ +#ifndef CONFIG_FPGA_DELAY +#define CONFIG_FPGA_DELAY() +#endif + +#ifndef CONFIG_SYS_FPGA_WAIT +#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/10 /* 100 ms */ +#endif + +static int ACEX1K_ps_load(Altera_desc *desc, const void *buf, size_t bsize); +static int ACEX1K_ps_dump(Altera_desc *desc, const void *buf, size_t bsize); +/* static int ACEX1K_ps_info(Altera_desc *desc); */ + +/* ------------------------------------------------------------------------- */ +/* ACEX1K Generic Implementation */ +int ACEX1K_load(Altera_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case passive_serial: + PRINTF ("%s: Launching Passive Serial Loader\n", __FUNCTION__); + ret_val = ACEX1K_ps_load (desc, buf, bsize); + break; + + /* Add new interface types here */ + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + + return ret_val; +} + +int ACEX1K_dump(Altera_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case passive_serial: + PRINTF ("%s: Launching Passive Serial Dump\n", __FUNCTION__); + ret_val = ACEX1K_ps_dump (desc, buf, bsize); + break; + + /* Add new interface types here */ + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + + return ret_val; +} + +int ACEX1K_info( Altera_desc *desc ) +{ + return FPGA_SUCCESS; +} + + +/* ------------------------------------------------------------------------- */ +/* ACEX1K Passive Serial Generic Implementation */ + +static int ACEX1K_ps_load(Altera_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume the worst */ + Altera_ACEX1K_Passive_Serial_fns *fn = desc->iface_fns; + int i; + + PRINTF ("%s: start with interface functions @ 0x%p\n", + __FUNCTION__, fn); + + if (fn) { + size_t bytecount = 0; + unsigned char *data = (unsigned char *) buf; + int cookie = desc->cookie; /* make a local copy */ + unsigned long ts; /* timestamp */ + + PRINTF ("%s: Function Table:\n" + "ptr:\t0x%p\n" + "struct: 0x%p\n" + "config:\t0x%p\n" + "status:\t0x%p\n" + "clk:\t0x%p\n" + "data:\t0x%p\n" + "done:\t0x%p\n\n", + __FUNCTION__, &fn, fn, fn->config, fn->status, + fn->clk, fn->data, fn->done); +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + printf ("Loading FPGA Device %d...", cookie); +#endif + + /* + * Run the pre configuration function if there is one. + */ + if (*fn->pre) { + (*fn->pre) (cookie); + } + + /* Establish the initial state */ + (*fn->config) (true, true, cookie); /* Assert nCONFIG */ + + udelay(2); /* T_cfg > 2us */ + + /* nSTATUS should be asserted now */ + (*fn->done) (cookie); + if ( !(*fn->status) (cookie) ) { + puts ("** nSTATUS is not asserted.\n"); + (*fn->abort) (cookie); + return FPGA_FAIL; + } + + (*fn->config) (false, true, cookie); /* Deassert nCONFIG */ + udelay(2); /* T_cf2st1 < 4us */ + + /* Wait for nSTATUS to be released (i.e. deasserted) */ + ts = get_timer (0); /* get current time */ + do { + CONFIG_FPGA_DELAY (); + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for STATUS to go high.\n"); + (*fn->abort) (cookie); + return FPGA_FAIL; + } + (*fn->done) (cookie); + } while ((*fn->status) (cookie)); + + /* Get ready for the burn */ + CONFIG_FPGA_DELAY (); + + /* Load the data */ + while (bytecount < bsize) { + unsigned char val=0; +#ifdef CONFIG_SYS_FPGA_CHECK_CTRLC + if (ctrlc ()) { + (*fn->abort) (cookie); + return FPGA_FAIL; + } +#endif + /* Altera detects an error if INIT goes low (active) + while DONE is low (inactive) */ +#if 0 /* not yet implemented */ + if ((*fn->done) (cookie) == 0 && (*fn->init) (cookie)) { + puts ("** CRC error during FPGA load.\n"); + (*fn->abort) (cookie); + return (FPGA_FAIL); + } +#endif + val = data [bytecount ++ ]; + i = 8; + do { + /* Deassert the clock */ + (*fn->clk) (false, true, cookie); + CONFIG_FPGA_DELAY (); + /* Write data */ + (*fn->data) ((val & 0x01), true, cookie); + CONFIG_FPGA_DELAY (); + /* Assert the clock */ + (*fn->clk) (true, true, cookie); + CONFIG_FPGA_DELAY (); + val >>= 1; + i --; + } while (i > 0); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (bytecount % (bsize / 40) == 0) + putc ('.'); /* let them know we are alive */ +#endif + } + + CONFIG_FPGA_DELAY (); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc (' '); /* terminate the dotted line */ +#endif + + /* + * Checking FPGA's CONF_DONE signal - correctly booted ? + */ + + if ( ! (*fn->done) (cookie) ) { + puts ("** Booting failed! CONF_DONE is still deasserted.\n"); + (*fn->abort) (cookie); + return (FPGA_FAIL); + } + + /* + * "DCLK must be clocked an additional 10 times fpr ACEX 1K..." + */ + + for (i = 0; i < 12; i++) { + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + } + + ret_val = FPGA_SUCCESS; + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (ret_val == FPGA_SUCCESS) { + puts ("Done.\n"); + } + else { + puts ("Fail.\n"); + } +#endif + (*fn->post) (cookie); + + } else { + printf ("%s: NULL Interface function table!\n", __FUNCTION__); + } + + return ret_val; +} + +static int ACEX1K_ps_dump(Altera_desc *desc, const void *buf, size_t bsize) +{ + /* Readback is only available through the Slave Parallel and */ + /* boundary-scan interfaces. */ + printf ("%s: Passive Serial Dumping is unavailable\n", + __FUNCTION__); + return FPGA_FAIL; +} diff --git a/qemu/roms/u-boot/drivers/fpga/Makefile b/qemu/roms/u-boot/drivers/fpga/Makefile new file mode 100644 index 000000000..dfb2e7fc7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/Makefile @@ -0,0 +1,20 @@ +# +# (C) Copyright 2008 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += fpga.o +obj-$(CONFIG_FPGA_SPARTAN2) += spartan2.o +obj-$(CONFIG_FPGA_SPARTAN3) += spartan3.o +obj-$(CONFIG_FPGA_VIRTEX2) += virtex2.o +obj-$(CONFIG_FPGA_ZYNQPL) += zynqpl.o +obj-$(CONFIG_FPGA_XILINX) += xilinx.o +obj-$(CONFIG_FPGA_LATTICE) += ivm_core.o lattice.o +ifdef CONFIG_FPGA_ALTERA +obj-y += altera.o +obj-$(CONFIG_FPGA_ACEX1K) += ACEX1K.o +obj-$(CONFIG_FPGA_CYCLON2) += cyclon2.o +obj-$(CONFIG_FPGA_STRATIX_II) += stratixII.o +endif diff --git a/qemu/roms/u-boot/drivers/fpga/altera.c b/qemu/roms/u-boot/drivers/fpga/altera.c new file mode 100644 index 000000000..af189f4ef --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/altera.c @@ -0,0 +1,227 @@ +/* + * (C) Copyright 2003 + * Steven Scholz, imc Measurement & Control, steven.scholz@imc-berlin.de + * + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Altera FPGA support + */ +#include +#include +#include + +/* Define FPGA_DEBUG to get debug printf's */ +/* #define FPGA_DEBUG */ + +#ifdef FPGA_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +/* Local Static Functions */ +static int altera_validate (Altera_desc * desc, const char *fn); + +/* ------------------------------------------------------------------------- */ +int altera_load(Altera_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume a failure */ + + if (!altera_validate (desc, (char *)__FUNCTION__)) { + printf ("%s: Invalid device descriptor\n", __FUNCTION__); + } else { + switch (desc->family) { + case Altera_ACEX1K: + case Altera_CYC2: +#if defined(CONFIG_FPGA_ACEX1K) + PRINTF ("%s: Launching the ACEX1K Loader...\n", + __FUNCTION__); + ret_val = ACEX1K_load (desc, buf, bsize); +#elif defined(CONFIG_FPGA_CYCLON2) + PRINTF ("%s: Launching the CYCLONE II Loader...\n", + __FUNCTION__); + ret_val = CYC2_load (desc, buf, bsize); +#else + printf ("%s: No support for ACEX1K devices.\n", + __FUNCTION__); +#endif + break; + +#if defined(CONFIG_FPGA_STRATIX_II) + case Altera_StratixII: + PRINTF ("%s: Launching the Stratix II Loader...\n", + __FUNCTION__); + ret_val = StratixII_load (desc, buf, bsize); + break; +#endif + default: + printf ("%s: Unsupported family type, %d\n", + __FUNCTION__, desc->family); + } + } + + return ret_val; +} + +int altera_dump(Altera_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume a failure */ + + if (!altera_validate (desc, (char *)__FUNCTION__)) { + printf ("%s: Invalid device descriptor\n", __FUNCTION__); + } else { + switch (desc->family) { + case Altera_ACEX1K: +#if defined(CONFIG_FPGA_ACEX) + PRINTF ("%s: Launching the ACEX1K Reader...\n", + __FUNCTION__); + ret_val = ACEX1K_dump (desc, buf, bsize); +#else + printf ("%s: No support for ACEX1K devices.\n", + __FUNCTION__); +#endif + break; + +#if defined(CONFIG_FPGA_STRATIX_II) + case Altera_StratixII: + PRINTF ("%s: Launching the Stratix II Reader...\n", + __FUNCTION__); + ret_val = StratixII_dump (desc, buf, bsize); + break; +#endif + default: + printf ("%s: Unsupported family type, %d\n", + __FUNCTION__, desc->family); + } + } + + return ret_val; +} + +int altera_info( Altera_desc *desc ) +{ + int ret_val = FPGA_FAIL; + + if (altera_validate (desc, (char *)__FUNCTION__)) { + printf ("Family: \t"); + switch (desc->family) { + case Altera_ACEX1K: + printf ("ACEX1K\n"); + break; + case Altera_CYC2: + printf ("CYCLON II\n"); + break; + case Altera_StratixII: + printf ("Stratix II\n"); + break; + /* Add new family types here */ + default: + printf ("Unknown family type, %d\n", desc->family); + } + + printf ("Interface type:\t"); + switch (desc->iface) { + case passive_serial: + printf ("Passive Serial (PS)\n"); + break; + case passive_parallel_synchronous: + printf ("Passive Parallel Synchronous (PPS)\n"); + break; + case passive_parallel_asynchronous: + printf ("Passive Parallel Asynchronous (PPA)\n"); + break; + case passive_serial_asynchronous: + printf ("Passive Serial Asynchronous (PSA)\n"); + break; + case altera_jtag_mode: /* Not used */ + printf ("JTAG Mode\n"); + break; + case fast_passive_parallel: + printf ("Fast Passive Parallel (FPP)\n"); + break; + case fast_passive_parallel_security: + printf + ("Fast Passive Parallel with Security (FPPS) \n"); + break; + /* Add new interface types here */ + default: + printf ("Unsupported interface type, %d\n", desc->iface); + } + + printf ("Device Size: \t%d bytes\n" + "Cookie: \t0x%x (%d)\n", + desc->size, desc->cookie, desc->cookie); + + if (desc->iface_fns) { + printf ("Device Function Table @ 0x%p\n", desc->iface_fns); + switch (desc->family) { + case Altera_ACEX1K: + case Altera_CYC2: +#if defined(CONFIG_FPGA_ACEX1K) + ACEX1K_info (desc); +#elif defined(CONFIG_FPGA_CYCLON2) + CYC2_info (desc); +#else + /* just in case */ + printf ("%s: No support for ACEX1K devices.\n", + __FUNCTION__); +#endif + break; +#if defined(CONFIG_FPGA_STRATIX_II) + case Altera_StratixII: + StratixII_info (desc); + break; +#endif + /* Add new family types here */ + default: + /* we don't need a message here - we give one up above */ + break; + } + } else { + printf ("No Device Function Table.\n"); + } + + ret_val = FPGA_SUCCESS; + } else { + printf ("%s: Invalid device descriptor\n", __FUNCTION__); + } + + return ret_val; +} + +/* ------------------------------------------------------------------------- */ + +static int altera_validate (Altera_desc * desc, const char *fn) +{ + int ret_val = false; + + if (desc) { + if ((desc->family > min_altera_type) && + (desc->family < max_altera_type)) { + if ((desc->iface > min_altera_iface_type) && + (desc->iface < max_altera_iface_type)) { + if (desc->size) { + ret_val = true; + } else { + printf ("%s: NULL part size\n", fn); + } + } else { + printf ("%s: Invalid Interface type, %d\n", + fn, desc->iface); + } + } else { + printf ("%s: Invalid family type, %d\n", fn, desc->family); + } + } else { + printf ("%s: NULL descriptor!\n", fn); + } + + return ret_val; +} + +/* ------------------------------------------------------------------------- */ diff --git a/qemu/roms/u-boot/drivers/fpga/cyclon2.c b/qemu/roms/u-boot/drivers/fpga/cyclon2.c new file mode 100644 index 000000000..8ab7679b4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/cyclon2.c @@ -0,0 +1,205 @@ +/* + * (C) Copyright 2006 + * Heiko Schocher, hs@denx.de + * Based on ACE1XK.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include /* core U-Boot definitions */ +#include +#include /* ACEX device family */ + +/* Define FPGA_DEBUG to get debug printf's */ +#ifdef FPGA_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +/* Note: The assumption is that we cannot possibly run fast enough to + * overrun the device (the Slave Parallel mode can free run at 50MHz). + * If there is a need to operate slower, define CONFIG_FPGA_DELAY in + * the board config file to slow things down. + */ +#ifndef CONFIG_FPGA_DELAY +#define CONFIG_FPGA_DELAY() +#endif + +#ifndef CONFIG_SYS_FPGA_WAIT +#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/10 /* 100 ms */ +#endif + +static int CYC2_ps_load(Altera_desc *desc, const void *buf, size_t bsize); +static int CYC2_ps_dump(Altera_desc *desc, const void *buf, size_t bsize); +/* static int CYC2_ps_info( Altera_desc *desc ); */ + +/* ------------------------------------------------------------------------- */ +/* CYCLON2 Generic Implementation */ +int CYC2_load(Altera_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case passive_serial: + PRINTF ("%s: Launching Passive Serial Loader\n", __FUNCTION__); + ret_val = CYC2_ps_load (desc, buf, bsize); + break; + + case fast_passive_parallel: + /* Fast Passive Parallel (FPP) and PS only differ in what is + * done in the write() callback. Use the existing PS load + * function for FPP, too. + */ + PRINTF ("%s: Launching Fast Passive Parallel Loader\n", + __FUNCTION__); + ret_val = CYC2_ps_load(desc, buf, bsize); + break; + + /* Add new interface types here */ + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + + return ret_val; +} + +int CYC2_dump(Altera_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case passive_serial: + PRINTF ("%s: Launching Passive Serial Dump\n", __FUNCTION__); + ret_val = CYC2_ps_dump (desc, buf, bsize); + break; + + /* Add new interface types here */ + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + + return ret_val; +} + +int CYC2_info( Altera_desc *desc ) +{ + return FPGA_SUCCESS; +} + +/* ------------------------------------------------------------------------- */ +/* CYCLON2 Passive Serial Generic Implementation */ +static int CYC2_ps_load(Altera_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume the worst */ + Altera_CYC2_Passive_Serial_fns *fn = desc->iface_fns; + int ret = 0; + + PRINTF ("%s: start with interface functions @ 0x%p\n", + __FUNCTION__, fn); + + if (fn) { + int cookie = desc->cookie; /* make a local copy */ + unsigned long ts; /* timestamp */ + + PRINTF ("%s: Function Table:\n" + "ptr:\t0x%p\n" + "struct: 0x%p\n" + "config:\t0x%p\n" + "status:\t0x%p\n" + "write:\t0x%p\n" + "done:\t0x%p\n\n", + __FUNCTION__, &fn, fn, fn->config, fn->status, + fn->write, fn->done); +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + printf ("Loading FPGA Device %d...", cookie); +#endif + + /* + * Run the pre configuration function if there is one. + */ + if (*fn->pre) { + (*fn->pre) (cookie); + } + + /* Establish the initial state */ + (*fn->config) (false, true, cookie); /* De-assert nCONFIG */ + udelay(100); + (*fn->config) (true, true, cookie); /* Assert nCONFIG */ + + udelay(2); /* T_cfg > 2us */ + + /* Wait for nSTATUS to be asserted */ + ts = get_timer (0); /* get current time */ + do { + CONFIG_FPGA_DELAY (); + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for STATUS to go high.\n"); + (*fn->abort) (cookie); + return FPGA_FAIL; + } + } while (!(*fn->status) (cookie)); + + /* Get ready for the burn */ + CONFIG_FPGA_DELAY (); + + ret = (*fn->write) (buf, bsize, true, cookie); + if (ret) { + puts ("** Write failed.\n"); + (*fn->abort) (cookie); + return FPGA_FAIL; + } +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + puts(" OK? ..."); +#endif + + CONFIG_FPGA_DELAY (); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc (' '); /* terminate the dotted line */ +#endif + + /* + * Checking FPGA's CONF_DONE signal - correctly booted ? + */ + + if ( ! (*fn->done) (cookie) ) { + puts ("** Booting failed! CONF_DONE is still deasserted.\n"); + (*fn->abort) (cookie); + return (FPGA_FAIL); + } +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + puts(" OK\n"); +#endif + + ret_val = FPGA_SUCCESS; + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (ret_val == FPGA_SUCCESS) { + puts ("Done.\n"); + } + else { + puts ("Fail.\n"); + } +#endif + (*fn->post) (cookie); + + } else { + printf ("%s: NULL Interface function table!\n", __FUNCTION__); + } + + return ret_val; +} + +static int CYC2_ps_dump(Altera_desc *desc, const void *buf, size_t bsize) +{ + /* Readback is only available through the Slave Parallel and */ + /* boundary-scan interfaces. */ + printf ("%s: Passive Serial Dumping is unavailable\n", + __FUNCTION__); + return FPGA_FAIL; +} diff --git a/qemu/roms/u-boot/drivers/fpga/fpga.c b/qemu/roms/u-boot/drivers/fpga/fpga.c new file mode 100644 index 000000000..b940d9b31 --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/fpga.c @@ -0,0 +1,287 @@ +/* + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Generic FPGA support */ +#include /* core U-Boot definitions */ +#include /* xilinx specific definitions */ +#include /* altera specific definitions */ +#include + +/* Local definitions */ +#ifndef CONFIG_MAX_FPGA_DEVICES +#define CONFIG_MAX_FPGA_DEVICES 5 +#endif + +/* Local static data */ +static int next_desc = FPGA_INVALID_DEVICE; +static fpga_desc desc_table[CONFIG_MAX_FPGA_DEVICES]; + +/* + * fpga_no_sup + * 'no support' message function + */ +static void fpga_no_sup(char *fn, char *msg) +{ + if (fn && msg) + printf("%s: No support for %s.\n", fn, msg); + else if (msg) + printf("No support for %s.\n", msg); + else + printf("No FPGA suport!\n"); +} + + +/* fpga_get_desc + * map a device number to a descriptor + */ +static const fpga_desc *const fpga_get_desc(int devnum) +{ + fpga_desc *desc = (fpga_desc *)NULL; + + if ((devnum >= 0) && (devnum < next_desc)) { + desc = &desc_table[devnum]; + debug("%s: found fpga descriptor #%d @ 0x%p\n", + __func__, devnum, desc); + } + + return desc; +} + +/* + * fpga_validate + * generic parameter checking code + */ +const fpga_desc *const fpga_validate(int devnum, const void *buf, + size_t bsize, char *fn) +{ + const fpga_desc *desc = fpga_get_desc(devnum); + + if (!desc) + printf("%s: Invalid device number %d\n", fn, devnum); + + if (!buf) { + printf("%s: Null buffer.\n", fn); + return (fpga_desc * const)NULL; + } + return desc; +} + +/* + * fpga_dev_info + * generic multiplexing code + */ +static int fpga_dev_info(int devnum) +{ + int ret_val = FPGA_FAIL; /* assume failure */ + const fpga_desc * const desc = fpga_get_desc(devnum); + + if (desc) { + debug("%s: Device Descriptor @ 0x%p\n", + __func__, desc->devdesc); + + switch (desc->devtype) { + case fpga_xilinx: +#if defined(CONFIG_FPGA_XILINX) + printf("Xilinx Device\nDescriptor @ 0x%p\n", desc); + ret_val = xilinx_info(desc->devdesc); +#else + fpga_no_sup((char *)__func__, "Xilinx devices"); +#endif + break; + case fpga_altera: +#if defined(CONFIG_FPGA_ALTERA) + printf("Altera Device\nDescriptor @ 0x%p\n", desc); + ret_val = altera_info(desc->devdesc); +#else + fpga_no_sup((char *)__func__, "Altera devices"); +#endif + break; + case fpga_lattice: +#if defined(CONFIG_FPGA_LATTICE) + printf("Lattice Device\nDescriptor @ 0x%p\n", desc); + ret_val = lattice_info(desc->devdesc); +#else + fpga_no_sup((char *)__func__, "Lattice devices"); +#endif + break; + default: + printf("%s: Invalid or unsupported device type %d\n", + __func__, desc->devtype); + } + } else { + printf("%s: Invalid device number %d\n", __func__, devnum); + } + + return ret_val; +} + +/* + * fgpa_init is usually called from misc_init_r() and MUST be called + * before any of the other fpga functions are used. + */ +void fpga_init(void) +{ + next_desc = 0; + memset(desc_table, 0, sizeof(desc_table)); + + debug("%s\n", __func__); +} + +/* + * fpga_count + * Basic interface function to get the current number of devices available. + */ +int fpga_count(void) +{ + return next_desc; +} + +/* + * fpga_add + * Add the device descriptor to the device table. + */ +int fpga_add(fpga_type devtype, void *desc) +{ + int devnum = FPGA_INVALID_DEVICE; + + if (next_desc < 0) { + printf("%s: FPGA support not initialized!\n", __func__); + } else if ((devtype > fpga_min_type) && (devtype < fpga_undefined)) { + if (desc) { + if (next_desc < CONFIG_MAX_FPGA_DEVICES) { + devnum = next_desc; + desc_table[next_desc].devtype = devtype; + desc_table[next_desc++].devdesc = desc; + } else { + printf("%s: Exceeded Max FPGA device count\n", + __func__); + } + } else { + printf("%s: NULL device descriptor\n", __func__); + } + } else { + printf("%s: Unsupported FPGA type %d\n", __func__, devtype); + } + + return devnum; +} + +/* + * Convert bitstream data and load into the fpga + */ +int __weak fpga_loadbitstream(int devnum, char *fpgadata, size_t size) +{ + printf("Bitstream support not implemented for this FPGA device\n"); + return FPGA_FAIL; +} + +/* + * Generic multiplexing code + */ +int fpga_load(int devnum, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume failure */ + const fpga_desc *desc = fpga_validate(devnum, buf, bsize, + (char *)__func__); + + if (desc) { + switch (desc->devtype) { + case fpga_xilinx: +#if defined(CONFIG_FPGA_XILINX) + ret_val = xilinx_load(desc->devdesc, buf, bsize); +#else + fpga_no_sup((char *)__func__, "Xilinx devices"); +#endif + break; + case fpga_altera: +#if defined(CONFIG_FPGA_ALTERA) + ret_val = altera_load(desc->devdesc, buf, bsize); +#else + fpga_no_sup((char *)__func__, "Altera devices"); +#endif + break; + case fpga_lattice: +#if defined(CONFIG_FPGA_LATTICE) + ret_val = lattice_load(desc->devdesc, buf, bsize); +#else + fpga_no_sup((char *)__func__, "Lattice devices"); +#endif + break; + default: + printf("%s: Invalid or unsupported device type %d\n", + __func__, desc->devtype); + } + } + + return ret_val; +} + +/* + * fpga_dump + * generic multiplexing code + */ +int fpga_dump(int devnum, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume failure */ + const fpga_desc *desc = fpga_validate(devnum, buf, bsize, + (char *)__func__); + + if (desc) { + switch (desc->devtype) { + case fpga_xilinx: +#if defined(CONFIG_FPGA_XILINX) + ret_val = xilinx_dump(desc->devdesc, buf, bsize); +#else + fpga_no_sup((char *)__func__, "Xilinx devices"); +#endif + break; + case fpga_altera: +#if defined(CONFIG_FPGA_ALTERA) + ret_val = altera_dump(desc->devdesc, buf, bsize); +#else + fpga_no_sup((char *)__func__, "Altera devices"); +#endif + break; + case fpga_lattice: +#if defined(CONFIG_FPGA_LATTICE) + ret_val = lattice_dump(desc->devdesc, buf, bsize); +#else + fpga_no_sup((char *)__func__, "Lattice devices"); +#endif + break; + default: + printf("%s: Invalid or unsupported device type %d\n", + __func__, desc->devtype); + } + } + + return ret_val; +} + +/* + * fpga_info + * front end to fpga_dev_info. If devnum is invalid, report on all + * available devices. + */ +int fpga_info(int devnum) +{ + if (devnum == FPGA_INVALID_DEVICE) { + if (next_desc > 0) { + int dev; + + for (dev = 0; dev < next_desc; dev++) + fpga_dev_info(dev); + + return FPGA_SUCCESS; + } else { + printf("%s: No FPGA devices available.\n", __func__); + return FPGA_FAIL; + } + } + + return fpga_dev_info(devnum); +} diff --git a/qemu/roms/u-boot/drivers/fpga/ivm_core.c b/qemu/roms/u-boot/drivers/fpga/ivm_core.c new file mode 100644 index 000000000..03aea625d --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/ivm_core.c @@ -0,0 +1,3149 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * Lattice ispVME Embedded code to load Lattice's FPGA: + * + * Copyright 2009 Lattice Semiconductor Corp. + * + * ispVME Embedded allows programming of Lattice's suite of FPGA + * devices on embedded systems through the JTAG port. The software + * is distributed in source code form and is open to re - distribution + * and modification where applicable. + * + * Revision History of ivm_core.c module: + * 4/25/06 ht Change some variables from unsigned short or int + * to long int to make the code compiler independent. + * 5/24/06 ht Support using RESET (TRST) pin as a special purpose + * control pin such as triggering the loading of known + * state exit. + * 3/6/07 ht added functions to support output to terminals + * + * 09/11/07 NN Type cast mismatch variables + * Moved the sclock() function to hardware.c + * 08/28/08 NN Added Calculate checksum support. + * 4/1/09 Nguyen replaced the recursive function call codes on + * the ispVMLCOUNT function + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define vme_out_char(c) printf("%c", c) +#define vme_out_hex(c) printf("%x", c) +#define vme_out_string(s) printf("%s", s) + +/* + * + * Global variables used to specify the flow control and data type. + * + * g_usFlowControl: flow control register. Each bit in the + * register can potentially change the + * personality of the embedded engine. + * g_usDataType: holds the data type of the current row. + * + */ + +static unsigned short g_usFlowControl; +unsigned short g_usDataType; + +/* + * + * Global variables used to specify the ENDDR and ENDIR. + * + * g_ucEndDR: the state that the device goes to after SDR. + * g_ucEndIR: the state that the device goes to after SIR. + * + */ + +unsigned char g_ucEndDR = DRPAUSE; +unsigned char g_ucEndIR = IRPAUSE; + +/* + * + * Global variables used to support header/trailer. + * + * g_usHeadDR: the number of lead devices in bypass. + * g_usHeadIR: the sum of IR length of lead devices. + * g_usTailDR: the number of tail devices in bypass. + * g_usTailIR: the sum of IR length of tail devices. + * + */ + +static unsigned short g_usHeadDR; +static unsigned short g_usHeadIR; +static unsigned short g_usTailDR; +static unsigned short g_usTailIR; + +/* + * + * Global variable to store the number of bits of data or instruction + * to be shifted into or out from the device. + * + */ + +static unsigned short g_usiDataSize; + +/* + * + * Stores the frequency. Default to 1 MHz. + * + */ + +static int g_iFrequency = 1000; + +/* + * + * Stores the maximum amount of ram needed to hold a row of data. + * + */ + +static unsigned short g_usMaxSize; + +/* + * + * Stores the LSH or RSH value. + * + */ + +static unsigned short g_usShiftValue; + +/* + * + * Stores the current repeat loop value. + * + */ + +static unsigned short g_usRepeatLoops; + +/* + * + * Stores the current vendor. + * + */ + +static signed char g_cVendor = LATTICE; + +/* + * + * Stores the VME file CRC. + * + */ + +unsigned short g_usCalculatedCRC; + +/* + * + * Stores the Device Checksum. + * + */ +/* 08/28/08 NN Added Calculate checksum support. */ +unsigned long g_usChecksum; +static unsigned int g_uiChecksumIndex; + +/* + * + * Stores the current state of the JTAG state machine. + * + */ + +static signed char g_cCurrentJTAGState; + +/* + * + * Global variables used to support looping. + * + * g_pucHeapMemory: holds the entire repeat loop. + * g_iHeapCounter: points to the current byte in the repeat loop. + * g_iHEAPSize: the current size of the repeat in bytes. + * + */ + +unsigned char *g_pucHeapMemory; +unsigned short g_iHeapCounter; +unsigned short g_iHEAPSize; +static unsigned short previous_size; + +/* + * + * Global variables used to support intelligent programming. + * + * g_usIntelDataIndex: points to the current byte of the + * intelligent buffer. + * g_usIntelBufferSize: holds the size of the intelligent + * buffer. + * + */ + +unsigned short g_usIntelDataIndex; +unsigned short g_usIntelBufferSize; + +/* + * + * Supported VME versions. + * + */ + +const char *const g_szSupportedVersions[] = { + "__VME2.0", "__VME3.0", "____12.0", "____12.1", 0}; + +/* + * + * Holds the maximum size of each respective buffer. These variables are used + * to write the HEX files when converting VME to HEX. + * +*/ + +static unsigned short g_usTDOSize; +static unsigned short g_usMASKSize; +static unsigned short g_usTDISize; +static unsigned short g_usDMASKSize; +static unsigned short g_usLCOUNTSize; +static unsigned short g_usHDRSize; +static unsigned short g_usTDRSize; +static unsigned short g_usHIRSize; +static unsigned short g_usTIRSize; +static unsigned short g_usHeapSize; + +/* + * + * Global variables used to store data. + * + * g_pucOutMaskData: local RAM to hold one row of MASK data. + * g_pucInData: local RAM to hold one row of TDI data. + * g_pucOutData: local RAM to hold one row of TDO data. + * g_pucHIRData: local RAM to hold the current SIR header. + * g_pucTIRData: local RAM to hold the current SIR trailer. + * g_pucHDRData: local RAM to hold the current SDR header. + * g_pucTDRData: local RAM to hold the current SDR trailer. + * g_pucIntelBuffer: local RAM to hold the current intelligent buffer + * g_pucOutDMaskData: local RAM to hold one row of DMASK data. + * + */ + +unsigned char *g_pucOutMaskData = NULL, + *g_pucInData = NULL, + *g_pucOutData = NULL, + *g_pucHIRData = NULL, + *g_pucTIRData = NULL, + *g_pucHDRData = NULL, + *g_pucTDRData = NULL, + *g_pucIntelBuffer = NULL, + *g_pucOutDMaskData = NULL; + +/* + * + * JTAG state machine transition table. + * + */ + +struct { + unsigned char CurState; /* From this state */ + unsigned char NextState; /* Step to this state */ + unsigned char Pattern; /* The tragetory of TMS */ + unsigned char Pulses; /* The number of steps */ +} g_JTAGTransistions[25] = { +{ RESET, RESET, 0xFC, 6 }, /* Transitions from RESET */ +{ RESET, IDLE, 0x00, 1 }, +{ RESET, DRPAUSE, 0x50, 5 }, +{ RESET, IRPAUSE, 0x68, 6 }, +{ IDLE, RESET, 0xE0, 3 }, /* Transitions from IDLE */ +{ IDLE, DRPAUSE, 0xA0, 4 }, +{ IDLE, IRPAUSE, 0xD0, 5 }, +{ DRPAUSE, RESET, 0xF8, 5 }, /* Transitions from DRPAUSE */ +{ DRPAUSE, IDLE, 0xC0, 3 }, +{ DRPAUSE, IRPAUSE, 0xF4, 7 }, +{ DRPAUSE, DRPAUSE, 0xE8, 6 },/* 06/14/06 Support POLL STATUS LOOP*/ +{ IRPAUSE, RESET, 0xF8, 5 }, /* Transitions from IRPAUSE */ +{ IRPAUSE, IDLE, 0xC0, 3 }, +{ IRPAUSE, DRPAUSE, 0xE8, 6 }, +{ DRPAUSE, SHIFTDR, 0x80, 2 }, /* Extra transitions using SHIFTDR */ +{ IRPAUSE, SHIFTDR, 0xE0, 5 }, +{ SHIFTDR, DRPAUSE, 0x80, 2 }, +{ SHIFTDR, IDLE, 0xC0, 3 }, +{ IRPAUSE, SHIFTIR, 0x80, 2 },/* Extra transitions using SHIFTIR */ +{ SHIFTIR, IRPAUSE, 0x80, 2 }, +{ SHIFTIR, IDLE, 0xC0, 3 }, +{ DRPAUSE, DRCAPTURE, 0xE0, 4 }, /* 11/15/05 Support DRCAPTURE*/ +{ DRCAPTURE, DRPAUSE, 0x80, 2 }, +{ IDLE, DRCAPTURE, 0x80, 2 }, +{ IRPAUSE, DRCAPTURE, 0xE0, 4 } +}; + +/* + * + * List to hold all LVDS pairs. + * + */ + +LVDSPair *g_pLVDSList; +unsigned short g_usLVDSPairCount; + +/* + * + * Function prototypes. + * + */ + +static signed char ispVMDataCode(void); +static long int ispVMDataSize(void); +static void ispVMData(unsigned char *Data); +static signed char ispVMShift(signed char Code); +static signed char ispVMAmble(signed char Code); +static signed char ispVMLoop(unsigned short a_usLoopCount); +static signed char ispVMBitShift(signed char mode, unsigned short bits); +static void ispVMComment(unsigned short a_usCommentSize); +static void ispVMHeader(unsigned short a_usHeaderSize); +static signed char ispVMLCOUNT(unsigned short a_usCountSize); +static void ispVMClocks(unsigned short Clocks); +static void ispVMBypass(signed char ScanType, unsigned short Bits); +static void ispVMStateMachine(signed char NextState); +static signed char ispVMSend(unsigned short int); +static signed char ispVMRead(unsigned short int); +static signed char ispVMReadandSave(unsigned short int); +static signed char ispVMProcessLVDS(unsigned short a_usLVDSCount); +static void ispVMMemManager(signed char types, unsigned short size); + +/* + * + * External variables and functions in hardware.c module + * + */ +static signed char g_cCurrentJTAGState; + +#ifdef DEBUG + +/* + * + * GetState + * + * Returns the state as a string based on the opcode. Only used + * for debugging purposes. + * + */ + +const char *GetState(unsigned char a_ucState) +{ + switch (a_ucState) { + case RESET: + return "RESET"; + case IDLE: + return "IDLE"; + case IRPAUSE: + return "IRPAUSE"; + case DRPAUSE: + return "DRPAUSE"; + case SHIFTIR: + return "SHIFTIR"; + case SHIFTDR: + return "SHIFTDR"; + case DRCAPTURE:/* 11/15/05 support DRCAPTURE*/ + return "DRCAPTURE"; + default: + break; + } + + return 0; +} + +/* + * + * PrintData + * + * Prints the data. Only used for debugging purposes. + * + */ + +void PrintData(unsigned short a_iDataSize, unsigned char *a_pucData) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short usByteSize = 0; + unsigned short usBitIndex = 0; + signed short usByteIndex = 0; + unsigned char ucByte = 0; + unsigned char ucFlipByte = 0; + + if (a_iDataSize % 8) { + /* 09/11/07 NN Type cast mismatch variables */ + usByteSize = (unsigned short)(a_iDataSize / 8 + 1); + } else { + /* 09/11/07 NN Type cast mismatch variables */ + usByteSize = (unsigned short)(a_iDataSize / 8); + } + puts("("); + /* 09/11/07 NN Type cast mismatch variables */ + for (usByteIndex = (signed short)(usByteSize - 1); + usByteIndex >= 0; usByteIndex--) { + ucByte = a_pucData[usByteIndex]; + ucFlipByte = 0x00; + + /* + * + * Flip each byte. + * + */ + + for (usBitIndex = 0; usBitIndex < 8; usBitIndex++) { + ucFlipByte <<= 1; + if (ucByte & 0x1) { + ucFlipByte |= 0x1; + } + + ucByte >>= 1; + } + + /* + * + * Print the flipped byte. + * + */ + + printf("%.02X", ucFlipByte); + if ((usByteSize - usByteIndex) % 40 == 39) { + puts("\n\t\t"); + } + if (usByteIndex < 0) + break; + } + puts(")"); +} +#endif /* DEBUG */ + +void ispVMMemManager(signed char cTarget, unsigned short usSize) +{ + switch (cTarget) { + case XTDI: + case TDI: + if (g_pucInData != NULL) { + if (previous_size == usSize) {/*memory exist*/ + break; + } else { + free(g_pucInData); + g_pucInData = NULL; + } + } + g_pucInData = (unsigned char *) malloc(usSize / 8 + 2); + previous_size = usSize; + case XTDO: + case TDO: + if (g_pucOutData != NULL) { + if (previous_size == usSize) { /*already exist*/ + break; + } else { + free(g_pucOutData); + g_pucOutData = NULL; + } + } + g_pucOutData = (unsigned char *) malloc(usSize / 8 + 2); + previous_size = usSize; + break; + case MASK: + if (g_pucOutMaskData != NULL) { + if (previous_size == usSize) {/*already allocated*/ + break; + } else { + free(g_pucOutMaskData); + g_pucOutMaskData = NULL; + } + } + g_pucOutMaskData = (unsigned char *) malloc(usSize / 8 + 2); + previous_size = usSize; + break; + case HIR: + if (g_pucHIRData != NULL) { + free(g_pucHIRData); + g_pucHIRData = NULL; + } + g_pucHIRData = (unsigned char *) malloc(usSize / 8 + 2); + break; + case TIR: + if (g_pucTIRData != NULL) { + free(g_pucTIRData); + g_pucTIRData = NULL; + } + g_pucTIRData = (unsigned char *) malloc(usSize / 8 + 2); + break; + case HDR: + if (g_pucHDRData != NULL) { + free(g_pucHDRData); + g_pucHDRData = NULL; + } + g_pucHDRData = (unsigned char *) malloc(usSize / 8 + 2); + break; + case TDR: + if (g_pucTDRData != NULL) { + free(g_pucTDRData); + g_pucTDRData = NULL; + } + g_pucTDRData = (unsigned char *) malloc(usSize / 8 + 2); + break; + case HEAP: + if (g_pucHeapMemory != NULL) { + free(g_pucHeapMemory); + g_pucHeapMemory = NULL; + } + g_pucHeapMemory = (unsigned char *) malloc(usSize + 2); + break; + case DMASK: + if (g_pucOutDMaskData != NULL) { + if (previous_size == usSize) { /*already allocated*/ + break; + } else { + free(g_pucOutDMaskData); + g_pucOutDMaskData = NULL; + } + } + g_pucOutDMaskData = (unsigned char *) malloc(usSize / 8 + 2); + previous_size = usSize; + break; + case LHEAP: + if (g_pucIntelBuffer != NULL) { + free(g_pucIntelBuffer); + g_pucIntelBuffer = NULL; + } + g_pucIntelBuffer = (unsigned char *) malloc(usSize + 2); + break; + case LVDS: + if (g_pLVDSList != NULL) { + free(g_pLVDSList); + g_pLVDSList = NULL; + } + g_pLVDSList = (LVDSPair *) malloc(usSize * sizeof(LVDSPair)); + if (g_pLVDSList) + memset(g_pLVDSList, 0, usSize * sizeof(LVDSPair)); + break; + default: + return; + } +} + +void ispVMFreeMem(void) +{ + if (g_pucHeapMemory != NULL) { + free(g_pucHeapMemory); + g_pucHeapMemory = NULL; + } + + if (g_pucOutMaskData != NULL) { + free(g_pucOutMaskData); + g_pucOutMaskData = NULL; + } + + if (g_pucInData != NULL) { + free(g_pucInData); + g_pucInData = NULL; + } + + if (g_pucOutData != NULL) { + free(g_pucOutData); + g_pucOutData = NULL; + } + + if (g_pucHIRData != NULL) { + free(g_pucHIRData); + g_pucHIRData = NULL; + } + + if (g_pucTIRData != NULL) { + free(g_pucTIRData); + g_pucTIRData = NULL; + } + + if (g_pucHDRData != NULL) { + free(g_pucHDRData); + g_pucHDRData = NULL; + } + + if (g_pucTDRData != NULL) { + free(g_pucTDRData); + g_pucTDRData = NULL; + } + + if (g_pucOutDMaskData != NULL) { + free(g_pucOutDMaskData); + g_pucOutDMaskData = NULL; + } + + if (g_pucIntelBuffer != NULL) { + free(g_pucIntelBuffer); + g_pucIntelBuffer = NULL; + } + + if (g_pLVDSList != NULL) { + free(g_pLVDSList); + g_pLVDSList = NULL; + } +} + + +/* + * + * ispVMDataSize + * + * Returns a VME-encoded number, usually used to indicate the + * bit length of an SIR/SDR command. + * + */ + +long int ispVMDataSize() +{ + /* 09/11/07 NN added local variables initialization */ + long int iSize = 0; + signed char cCurrentByte = 0; + signed char cIndex = 0; + cIndex = 0; + while ((cCurrentByte = GetByte()) & 0x80) { + iSize |= ((long int) (cCurrentByte & 0x7F)) << cIndex; + cIndex += 7; + } + iSize |= ((long int) (cCurrentByte & 0x7F)) << cIndex; + return iSize; +} + +/* + * + * ispVMCode + * + * This is the heart of the embedded engine. All the high-level opcodes + * are extracted here. Once they have been identified, then it + * will call other functions to handle the processing. + * + */ + +signed char ispVMCode() +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short iRepeatSize = 0; + signed char cOpcode = 0; + signed char cRetCode = 0; + unsigned char ucState = 0; + unsigned short usDelay = 0; + unsigned short usToggle = 0; + unsigned char usByte = 0; + + /* + * + * Check the compression flag only if this is the first time + * this function is entered. Do not check the compression flag if + * it is being called recursively from other functions within + * the embedded engine. + * + */ + + if (!(g_usDataType & LHEAP_IN) && !(g_usDataType & HEAP_IN)) { + usByte = GetByte(); + if (usByte == 0xf1) { + g_usDataType |= COMPRESS; + } else if (usByte == 0xf2) { + g_usDataType &= ~COMPRESS; + } else { + return VME_INVALID_FILE; + } + } + + /* + * + * Begin looping through all the VME opcodes. + * + */ + + while ((cOpcode = GetByte()) >= 0) { + + switch (cOpcode) { + case STATE: + + /* + * Step the JTAG state machine. + */ + + ucState = GetByte(); + + /* + * Step the JTAG state machine to DRCAPTURE + * to support Looping. + */ + + if ((g_usDataType & LHEAP_IN) && + (ucState == DRPAUSE) && + (g_cCurrentJTAGState == ucState)) { + ispVMStateMachine(DRCAPTURE); + } + + ispVMStateMachine(ucState); + +#ifdef DEBUG + if (g_usDataType & LHEAP_IN) { + debug("LDELAY %s ", GetState(ucState)); + } else { + debug("STATE %s;\n", GetState(ucState)); + } +#endif /* DEBUG */ + break; + case SIR: + case SDR: + case XSDR: + +#ifdef DEBUG + switch (cOpcode) { + case SIR: + puts("SIR "); + break; + case SDR: + case XSDR: + if (g_usDataType & LHEAP_IN) { + puts("LSDR "); + } else { + puts("SDR "); + } + break; + } +#endif /* DEBUG */ + /* + * + * Shift in data into the device. + * + */ + + cRetCode = ispVMShift(cOpcode); + if (cRetCode != 0) { + return cRetCode; + } + break; + case WAIT: + + /* + * + * Observe delay. + * + */ + + /* 09/11/07 NN Type cast mismatch variables */ + usDelay = (unsigned short) ispVMDataSize(); + ispVMDelay(usDelay); + +#ifdef DEBUG + if (usDelay & 0x8000) { + + /* + * Since MSB is set, the delay time must be + * decoded to millisecond. The SVF2VME encodes + * the MSB to represent millisecond. + */ + + usDelay &= ~0x8000; + if (g_usDataType & LHEAP_IN) { + printf("%.2E SEC;\n", + (float) usDelay / 1000); + } else { + printf("RUNTEST %.2E SEC;\n", + (float) usDelay / 1000); + } + } else { + /* + * Since MSB is not set, the delay time + * is given as microseconds. + */ + + if (g_usDataType & LHEAP_IN) { + printf("%.2E SEC;\n", + (float) usDelay / 1000000); + } else { + printf("RUNTEST %.2E SEC;\n", + (float) usDelay / 1000000); + } + } +#endif /* DEBUG */ + break; + case TCK: + + /* + * Issue clock toggles. + */ + + /* 09/11/07 NN Type cast mismatch variables */ + usToggle = (unsigned short) ispVMDataSize(); + ispVMClocks(usToggle); + +#ifdef DEBUG + printf("RUNTEST %d TCK;\n", usToggle); +#endif /* DEBUG */ + break; + case ENDDR: + + /* + * + * Set the ENDDR. + * + */ + + g_ucEndDR = GetByte(); + +#ifdef DEBUG + printf("ENDDR %s;\n", GetState(g_ucEndDR)); +#endif /* DEBUG */ + break; + case ENDIR: + + /* + * + * Set the ENDIR. + * + */ + + g_ucEndIR = GetByte(); + +#ifdef DEBUG + printf("ENDIR %s;\n", GetState(g_ucEndIR)); +#endif /* DEBUG */ + break; + case HIR: + case TIR: + case HDR: + case TDR: + +#ifdef DEBUG + switch (cOpcode) { + case HIR: + puts("HIR "); + break; + case TIR: + puts("TIR "); + break; + case HDR: + puts("HDR "); + break; + case TDR: + puts("TDR "); + break; + } +#endif /* DEBUG */ + /* + * Set the header/trailer of the device in order + * to bypass + * successfully. + */ + + cRetCode = ispVMAmble(cOpcode); + if (cRetCode != 0) { + return cRetCode; + } + +#ifdef DEBUG + puts(";\n"); +#endif /* DEBUG */ + break; + case MEM: + + /* + * The maximum RAM required to support + * processing one row of the VME file. + */ + + /* 09/11/07 NN Type cast mismatch variables */ + g_usMaxSize = (unsigned short) ispVMDataSize(); + +#ifdef DEBUG + printf("// MEMSIZE %d\n", g_usMaxSize); +#endif /* DEBUG */ + break; + case VENDOR: + + /* + * + * Set the VENDOR type. + * + */ + + cOpcode = GetByte(); + switch (cOpcode) { + case LATTICE: +#ifdef DEBUG + puts("// VENDOR LATTICE\n"); +#endif /* DEBUG */ + g_cVendor = LATTICE; + break; + case ALTERA: +#ifdef DEBUG + puts("// VENDOR ALTERA\n"); +#endif /* DEBUG */ + g_cVendor = ALTERA; + break; + case XILINX: +#ifdef DEBUG + puts("// VENDOR XILINX\n"); +#endif /* DEBUG */ + g_cVendor = XILINX; + break; + default: + break; + } + break; + case SETFLOW: + + /* + * Set the flow control. Flow control determines + * the personality of the embedded engine. + */ + + /* 09/11/07 NN Type cast mismatch variables */ + g_usFlowControl |= (unsigned short) ispVMDataSize(); + break; + case RESETFLOW: + + /* + * + * Unset the flow control. + * + */ + + /* 09/11/07 NN Type cast mismatch variables */ + g_usFlowControl &= (unsigned short) ~(ispVMDataSize()); + break; + case HEAP: + + /* + * + * Allocate heap size to store loops. + * + */ + + cRetCode = GetByte(); + if (cRetCode != SECUREHEAP) { + return VME_INVALID_FILE; + } + /* 09/11/07 NN Type cast mismatch variables */ + g_iHEAPSize = (unsigned short) ispVMDataSize(); + + /* + * Store the maximum size of the HEAP buffer. + * Used to convert VME to HEX. + */ + + if (g_iHEAPSize > g_usHeapSize) { + g_usHeapSize = g_iHEAPSize; + } + + ispVMMemManager(HEAP, (unsigned short) g_iHEAPSize); + break; + case REPEAT: + + /* + * + * Execute loops. + * + */ + + g_usRepeatLoops = 0; + + /* 09/11/07 NN Type cast mismatch variables */ + iRepeatSize = (unsigned short) ispVMDataSize(); + + cRetCode = ispVMLoop((unsigned short) iRepeatSize); + if (cRetCode != 0) { + return cRetCode; + } + break; + case ENDLOOP: + + /* + * + * Exit point from processing loops. + * + */ + + return cRetCode; + case ENDVME: + + /* + * The only valid exit point that indicates + * end of programming. + */ + + return cRetCode; + case SHR: + + /* + * + * Right-shift address. + * + */ + + g_usFlowControl |= SHIFTRIGHT; + + /* 09/11/07 NN Type cast mismatch variables */ + g_usShiftValue = (unsigned short) (g_usRepeatLoops * + (unsigned short)GetByte()); + break; + case SHL: + + /* + * Left-shift address. + */ + + g_usFlowControl |= SHIFTLEFT; + + /* 09/11/07 NN Type cast mismatch variables */ + g_usShiftValue = (unsigned short) (g_usRepeatLoops * + (unsigned short)GetByte()); + break; + case FREQUENCY: + + /* + * + * Set the frequency. + * + */ + + /* 09/11/07 NN Type cast mismatch variables */ + g_iFrequency = (int) (ispVMDataSize() / 1000); + if (g_iFrequency == 1) + g_iFrequency = 1000; + +#ifdef DEBUG + printf("FREQUENCY %.2E HZ;\n", + (float) g_iFrequency * 1000); +#endif /* DEBUG */ + break; + case LCOUNT: + + /* + * + * Process LCOUNT command. + * + */ + + cRetCode = ispVMLCOUNT((unsigned short)ispVMDataSize()); + if (cRetCode != 0) { + return cRetCode; + } + break; + case VUES: + + /* + * + * Set the flow control to verify USERCODE. + * + */ + + g_usFlowControl |= VERIFYUES; + break; + case COMMENT: + + /* + * + * Display comment. + * + */ + + ispVMComment((unsigned short) ispVMDataSize()); + break; + case LVDS: + + /* + * + * Process LVDS command. + * + */ + + ispVMProcessLVDS((unsigned short) ispVMDataSize()); + break; + case HEADER: + + /* + * + * Discard header. + * + */ + + ispVMHeader((unsigned short) ispVMDataSize()); + break; + /* 03/14/06 Support Toggle ispENABLE signal*/ + case ispEN: + ucState = GetByte(); + if ((ucState == ON) || (ucState == 0x01)) + writePort(g_ucPinENABLE, 0x01); + else + writePort(g_ucPinENABLE, 0x00); + ispVMDelay(1); + break; + /* 05/24/06 support Toggle TRST pin*/ + case TRST: + ucState = GetByte(); + if (ucState == 0x01) + writePort(g_ucPinTRST, 0x01); + else + writePort(g_ucPinTRST, 0x00); + ispVMDelay(1); + break; + default: + + /* + * + * Invalid opcode encountered. + * + */ + +#ifdef DEBUG + printf("\nINVALID OPCODE: 0x%.2X\n", cOpcode); +#endif /* DEBUG */ + + return VME_INVALID_FILE; + } + } + + /* + * + * Invalid exit point. Processing the token 'ENDVME' is the only + * valid way to exit the embedded engine. + * + */ + + return VME_INVALID_FILE; +} + +/* + * + * ispVMDataCode + * + * Processes the TDI/TDO/MASK/DMASK etc of an SIR/SDR command. + * + */ + +signed char ispVMDataCode() +{ + /* 09/11/07 NN added local variables initialization */ + signed char cDataByte = 0; + signed char siDataSource = 0; /*source of data from file by default*/ + + if (g_usDataType & HEAP_IN) { + siDataSource = 1; /*the source of data from memory*/ + } + + /* + * + * Clear the data type register. + * + **/ + + g_usDataType &= ~(MASK_DATA + TDI_DATA + + TDO_DATA + DMASK_DATA + CMASK_DATA); + + /* + * Iterate through SIR/SDR command and look for TDI, + * TDO, MASK, etc. + */ + + while ((cDataByte = GetByte()) >= 0) { + ispVMMemManager(cDataByte, g_usMaxSize); + switch (cDataByte) { + case TDI: + + /* + * Store the maximum size of the TDI buffer. + * Used to convert VME to HEX. + */ + + if (g_usiDataSize > g_usTDISize) { + g_usTDISize = g_usiDataSize; + } + /* + * Updated data type register to indicate that + * TDI data is currently being used. Process the + * data in the VME file into the TDI buffer. + */ + + g_usDataType |= TDI_DATA; + ispVMData(g_pucInData); + break; + case XTDO: + + /* + * Store the maximum size of the TDO buffer. + * Used to convert VME to HEX. + */ + + if (g_usiDataSize > g_usTDOSize) { + g_usTDOSize = g_usiDataSize; + } + + /* + * Updated data type register to indicate that + * TDO data is currently being used. + */ + + g_usDataType |= TDO_DATA; + break; + case TDO: + + /* + * Store the maximum size of the TDO buffer. + * Used to convert VME to HEX. + */ + + if (g_usiDataSize > g_usTDOSize) { + g_usTDOSize = g_usiDataSize; + } + + /* + * Updated data type register to indicate + * that TDO data is currently being used. + * Process the data in the VME file into the + * TDO buffer. + */ + + g_usDataType |= TDO_DATA; + ispVMData(g_pucOutData); + break; + case MASK: + + /* + * Store the maximum size of the MASK buffer. + * Used to convert VME to HEX. + */ + + if (g_usiDataSize > g_usMASKSize) { + g_usMASKSize = g_usiDataSize; + } + + /* + * Updated data type register to indicate that + * MASK data is currently being used. Process + * the data in the VME file into the MASK buffer + */ + + g_usDataType |= MASK_DATA; + ispVMData(g_pucOutMaskData); + break; + case DMASK: + + /* + * Store the maximum size of the DMASK buffer. + * Used to convert VME to HEX. + */ + + if (g_usiDataSize > g_usDMASKSize) { + g_usDMASKSize = g_usiDataSize; + } + + /* + * Updated data type register to indicate that + * DMASK data is currently being used. Process + * the data in the VME file into the DMASK + * buffer. + */ + + g_usDataType |= DMASK_DATA; + ispVMData(g_pucOutDMaskData); + break; + case CMASK: + + /* + * Updated data type register to indicate that + * MASK data is currently being used. Process + * the data in the VME file into the MASK buffer + */ + + g_usDataType |= CMASK_DATA; + ispVMData(g_pucOutMaskData); + break; + case CONTINUE: + return 0; + default: + /* + * Encountered invalid opcode. + */ + return VME_INVALID_FILE; + } + + switch (cDataByte) { + case TDI: + + /* + * Left bit shift. Used when performing + * algorithm looping. + */ + + if (g_usFlowControl & SHIFTLEFT) { + ispVMBitShift(SHL, g_usShiftValue); + g_usFlowControl &= ~SHIFTLEFT; + } + + /* + * Right bit shift. Used when performing + * algorithm looping. + */ + + if (g_usFlowControl & SHIFTRIGHT) { + ispVMBitShift(SHR, g_usShiftValue); + g_usFlowControl &= ~SHIFTRIGHT; + } + default: + break; + } + + if (siDataSource) { + g_usDataType |= HEAP_IN; /*restore from memory*/ + } + } + + if (siDataSource) { /*fetch data from heap memory upon return*/ + g_usDataType |= HEAP_IN; + } + + if (cDataByte < 0) { + + /* + * Encountered invalid opcode. + */ + + return VME_INVALID_FILE; + } else { + return 0; + } +} + +/* + * + * ispVMData + * Extract one row of data operand from the current data type opcode. Perform + * the decompression if necessary. Extra RAM is not required for the + * decompression process. The decompression scheme employed in this module + * is on row by row basis. The format of the data stream: + * [compression code][compressed data stream] + * 0x00 --No compression + * 0x01 --Compress by 0x00. + * Example: + * Original stream: 0x000000000000000000000001 + * Compressed stream: 0x01000901 + * Detail: 0x01 is the code, 0x00 is the key, + * 0x09 is the count of 0x00 bytes, + * 0x01 is the uncompressed byte. + * 0x02 --Compress by 0xFF. + * Example: + * Original stream: 0xFFFFFFFFFFFFFFFFFFFFFF01 + * Compressed stream: 0x02FF0901 + * Detail: 0x02 is the code, 0xFF is the key, + * 0x09 is the count of 0xFF bytes, + * 0x01 is the uncompressed byte. + * 0x03 + * : : + * 0xFE -- Compress by nibble blocks. + * Example: + * Original stream: 0x84210842108421084210 + * Compressed stream: 0x0584210 + * Detail: 0x05 is the code, means 5 nibbles block. + * 0x84210 is the 5 nibble blocks. + * The whole row is 80 bits given by g_usiDataSize. + * The number of times the block repeat itself + * is found by g_usiDataSize/(4*0x05) which is 4. + * 0xFF -- Compress by the most frequently happen byte. + * Example: + * Original stream: 0x04020401030904040404 + * Compressed stream: 0xFF04(0,1,0x02,0,1,0x01,1,0x03,1,0x09,0,0,0) + * or: 0xFF044090181C240 + * Detail: 0xFF is the code, 0x04 is the key. + * a bit of 0 represent the key shall be put into + * the current bit position and a bit of 1 + * represent copying the next of 8 bits of data + * in. + * + */ + +void ispVMData(unsigned char *ByteData) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short size = 0; + unsigned short i, j, m, getData = 0; + unsigned char cDataByte = 0; + unsigned char compress = 0; + unsigned short FFcount = 0; + unsigned char compr_char = 0xFF; + unsigned short index = 0; + signed char compression = 0; + + /*convert number in bits to bytes*/ + if (g_usiDataSize % 8 > 0) { + /* 09/11/07 NN Type cast mismatch variables */ + size = (unsigned short)(g_usiDataSize / 8 + 1); + } else { + /* 09/11/07 NN Type cast mismatch variables */ + size = (unsigned short)(g_usiDataSize / 8); + } + + /* + * If there is compression, then check if compress by key + * of 0x00 or 0xFF or by other keys or by nibble blocks + */ + + if (g_usDataType & COMPRESS) { + compression = 1; + compress = GetByte(); + if ((compress == VAR) && (g_usDataType & HEAP_IN)) { + getData = 1; + g_usDataType &= ~(HEAP_IN); + compress = GetByte(); + } + + switch (compress) { + case 0x00: + /* No compression */ + compression = 0; + break; + case 0x01: + /* Compress by byte 0x00 */ + compr_char = 0x00; + break; + case 0x02: + /* Compress by byte 0xFF */ + compr_char = 0xFF; + break; + case 0xFF: + /* Huffman encoding */ + compr_char = GetByte(); + i = 8; + for (index = 0; index < size; index++) { + ByteData[index] = 0x00; + if (i > 7) { + cDataByte = GetByte(); + i = 0; + } + if ((cDataByte << i++) & 0x80) + m = 8; + else { + ByteData[index] = compr_char; + m = 0; + } + + for (j = 0; j < m; j++) { + if (i > 7) { + cDataByte = GetByte(); + i = 0; + } + ByteData[index] |= + ((cDataByte << i++) & 0x80) >> j; + } + } + size = 0; + break; + default: + for (index = 0; index < size; index++) + ByteData[index] = 0x00; + for (index = 0; index < compress; index++) { + if (index % 2 == 0) + cDataByte = GetByte(); + for (i = 0; i < size * 2 / compress; i++) { + j = (unsigned short)(index + + (i * (unsigned short)compress)); + /*clear the nibble to zero first*/ + if (j%2) { + if (index % 2) + ByteData[j/2] |= + cDataByte & 0xF; + else + ByteData[j/2] |= + cDataByte >> 4; + } else { + if (index % 2) + ByteData[j/2] |= + cDataByte << 4; + else + ByteData[j/2] |= + cDataByte & 0xF0; + } + } + } + size = 0; + break; + } + } + + FFcount = 0; + + /* Decompress by byte 0x00 or 0xFF */ + for (index = 0; index < size; index++) { + if (FFcount <= 0) { + cDataByte = GetByte(); + if ((cDataByte == VAR) && (g_usDataType&HEAP_IN) && + !getData && !(g_usDataType&COMPRESS)) { + getData = 1; + g_usDataType &= ~(HEAP_IN); + cDataByte = GetByte(); + } + ByteData[index] = cDataByte; + if ((compression) && (cDataByte == compr_char)) + /* 09/11/07 NN Type cast mismatch variables */ + FFcount = (unsigned short) ispVMDataSize(); + /*The number of 0xFF or 0x00 bytes*/ + } else { + FFcount--; /*Use up the 0xFF chain first*/ + ByteData[index] = compr_char; + } + } + + if (getData) { + g_usDataType |= HEAP_IN; + getData = 0; + } +} + +/* + * + * ispVMShift + * + * Processes the SDR/XSDR/SIR commands. + * + */ + +signed char ispVMShift(signed char a_cCode) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short iDataIndex = 0; + unsigned short iReadLoop = 0; + signed char cRetCode = 0; + + cRetCode = 0; + /* 09/11/07 NN Type cast mismatch variables */ + g_usiDataSize = (unsigned short) ispVMDataSize(); + + /*clear the flags first*/ + g_usDataType &= ~(SIR_DATA + EXPRESS + SDR_DATA); + switch (a_cCode) { + case SIR: + g_usDataType |= SIR_DATA; + /* + * 1/15/04 If performing cascading, then go directly to SHIFTIR. + * Else, go to IRPAUSE before going to SHIFTIR + */ + if (g_usFlowControl & CASCADE) { + ispVMStateMachine(SHIFTIR); + } else { + ispVMStateMachine(IRPAUSE); + ispVMStateMachine(SHIFTIR); + if (g_usHeadIR > 0) { + ispVMBypass(HIR, g_usHeadIR); + sclock(); + } + } + break; + case XSDR: + g_usDataType |= EXPRESS; /*mark simultaneous in and out*/ + case SDR: + g_usDataType |= SDR_DATA; + /* + * 1/15/04 If already in SHIFTDR, then do not move state or + * shift in header. This would imply that the previously + * shifted frame was a cascaded frame. + */ + if (g_cCurrentJTAGState != SHIFTDR) { + /* + * 1/15/04 If performing cascading, then go directly + * to SHIFTDR. Else, go to DRPAUSE before going + * to SHIFTDR + */ + if (g_usFlowControl & CASCADE) { + if (g_cCurrentJTAGState == DRPAUSE) { + ispVMStateMachine(SHIFTDR); + /* + * 1/15/04 If cascade flag has been seat + * and the current state is DRPAUSE, + * this implies that the first cascaded + * frame is about to be shifted in. The + * header must be shifted prior to + * shifting the first cascaded frame. + */ + if (g_usHeadDR > 0) { + ispVMBypass(HDR, g_usHeadDR); + sclock(); + } + } else { + ispVMStateMachine(SHIFTDR); + } + } else { + ispVMStateMachine(DRPAUSE); + ispVMStateMachine(SHIFTDR); + if (g_usHeadDR > 0) { + ispVMBypass(HDR, g_usHeadDR); + sclock(); + } + } + } + break; + default: + return VME_INVALID_FILE; + } + + cRetCode = ispVMDataCode(); + + if (cRetCode != 0) { + return VME_INVALID_FILE; + } + +#ifdef DEBUG + printf("%d ", g_usiDataSize); + + if (g_usDataType & TDI_DATA) { + puts("TDI "); + PrintData(g_usiDataSize, g_pucInData); + } + + if (g_usDataType & TDO_DATA) { + puts("\n\t\tTDO "); + PrintData(g_usiDataSize, g_pucOutData); + } + + if (g_usDataType & MASK_DATA) { + puts("\n\t\tMASK "); + PrintData(g_usiDataSize, g_pucOutMaskData); + } + + if (g_usDataType & DMASK_DATA) { + puts("\n\t\tDMASK "); + PrintData(g_usiDataSize, g_pucOutDMaskData); + } + + puts(";\n"); +#endif /* DEBUG */ + + if (g_usDataType & TDO_DATA || g_usDataType & DMASK_DATA) { + if (g_usDataType & DMASK_DATA) { + cRetCode = ispVMReadandSave(g_usiDataSize); + if (!cRetCode) { + if (g_usTailDR > 0) { + sclock(); + ispVMBypass(TDR, g_usTailDR); + } + ispVMStateMachine(DRPAUSE); + ispVMStateMachine(SHIFTDR); + if (g_usHeadDR > 0) { + ispVMBypass(HDR, g_usHeadDR); + sclock(); + } + for (iDataIndex = 0; + iDataIndex < g_usiDataSize / 8 + 1; + iDataIndex++) + g_pucInData[iDataIndex] = + g_pucOutData[iDataIndex]; + g_usDataType &= ~(TDO_DATA + DMASK_DATA); + cRetCode = ispVMSend(g_usiDataSize); + } + } else { + cRetCode = ispVMRead(g_usiDataSize); + if (cRetCode == -1 && g_cVendor == XILINX) { + for (iReadLoop = 0; iReadLoop < 30; + iReadLoop++) { + cRetCode = ispVMRead(g_usiDataSize); + if (!cRetCode) { + break; + } else { + /* Always DRPAUSE */ + ispVMStateMachine(DRPAUSE); + /* + * Bypass other devices + * when appropriate + */ + ispVMBypass(TDR, g_usTailDR); + ispVMStateMachine(g_ucEndDR); + ispVMStateMachine(IDLE); + ispVMDelay(1000); + } + } + } + } + } else { /*TDI only*/ + cRetCode = ispVMSend(g_usiDataSize); + } + + /*transfer the input data to the output buffer for the next verify*/ + if ((g_usDataType & EXPRESS) || (a_cCode == SDR)) { + if (g_pucOutData) { + for (iDataIndex = 0; iDataIndex < g_usiDataSize / 8 + 1; + iDataIndex++) + g_pucOutData[iDataIndex] = + g_pucInData[iDataIndex]; + } + } + + switch (a_cCode) { + case SIR: + /* 1/15/04 If not performing cascading, then shift ENDIR */ + if (!(g_usFlowControl & CASCADE)) { + if (g_usTailIR > 0) { + sclock(); + ispVMBypass(TIR, g_usTailIR); + } + ispVMStateMachine(g_ucEndIR); + } + break; + case XSDR: + case SDR: + /* 1/15/04 If not performing cascading, then shift ENDDR */ + if (!(g_usFlowControl & CASCADE)) { + if (g_usTailDR > 0) { + sclock(); + ispVMBypass(TDR, g_usTailDR); + } + ispVMStateMachine(g_ucEndDR); + } + break; + default: + break; + } + + return cRetCode; +} + +/* + * + * ispVMAmble + * + * This routine is to extract Header and Trailer parameter for SIR and + * SDR operations. + * + * The Header and Trailer parameter are the pre-amble and post-amble bit + * stream need to be shifted into TDI or out of TDO of the devices. Mostly + * is for the purpose of bypassing the leading or trailing devices. ispVM + * supports only shifting data into TDI to bypass the devices. + * + * For a single device, the header and trailer parameters are all set to 0 + * as default by ispVM. If it is for multiple devices, the header and trailer + * value will change as specified by the VME file. + * + */ + +signed char ispVMAmble(signed char Code) +{ + signed char compress = 0; + /* 09/11/07 NN Type cast mismatch variables */ + g_usiDataSize = (unsigned short)ispVMDataSize(); + +#ifdef DEBUG + printf("%d", g_usiDataSize); +#endif /* DEBUG */ + + if (g_usiDataSize) { + + /* + * Discard the TDI byte and set the compression bit in the data + * type register to false if compression is set because TDI data + * after HIR/HDR/TIR/TDR is not compressed. + */ + + GetByte(); + if (g_usDataType & COMPRESS) { + g_usDataType &= ~(COMPRESS); + compress = 1; + } + } + + switch (Code) { + case HIR: + + /* + * Store the maximum size of the HIR buffer. + * Used to convert VME to HEX. + */ + + if (g_usiDataSize > g_usHIRSize) { + g_usHIRSize = g_usiDataSize; + } + + /* + * Assign the HIR value and allocate memory. + */ + + g_usHeadIR = g_usiDataSize; + if (g_usHeadIR) { + ispVMMemManager(HIR, g_usHeadIR); + ispVMData(g_pucHIRData); + +#ifdef DEBUG + puts(" TDI "); + PrintData(g_usHeadIR, g_pucHIRData); +#endif /* DEBUG */ + } + break; + case TIR: + + /* + * Store the maximum size of the TIR buffer. + * Used to convert VME to HEX. + */ + + if (g_usiDataSize > g_usTIRSize) { + g_usTIRSize = g_usiDataSize; + } + + /* + * Assign the TIR value and allocate memory. + */ + + g_usTailIR = g_usiDataSize; + if (g_usTailIR) { + ispVMMemManager(TIR, g_usTailIR); + ispVMData(g_pucTIRData); + +#ifdef DEBUG + puts(" TDI "); + PrintData(g_usTailIR, g_pucTIRData); +#endif /* DEBUG */ + } + break; + case HDR: + + /* + * Store the maximum size of the HDR buffer. + * Used to convert VME to HEX. + */ + + if (g_usiDataSize > g_usHDRSize) { + g_usHDRSize = g_usiDataSize; + } + + /* + * Assign the HDR value and allocate memory. + * + */ + + g_usHeadDR = g_usiDataSize; + if (g_usHeadDR) { + ispVMMemManager(HDR, g_usHeadDR); + ispVMData(g_pucHDRData); + +#ifdef DEBUG + puts(" TDI "); + PrintData(g_usHeadDR, g_pucHDRData); +#endif /* DEBUG */ + } + break; + case TDR: + + /* + * Store the maximum size of the TDR buffer. + * Used to convert VME to HEX. + */ + + if (g_usiDataSize > g_usTDRSize) { + g_usTDRSize = g_usiDataSize; + } + + /* + * Assign the TDR value and allocate memory. + * + */ + + g_usTailDR = g_usiDataSize; + if (g_usTailDR) { + ispVMMemManager(TDR, g_usTailDR); + ispVMData(g_pucTDRData); + +#ifdef DEBUG + puts(" TDI "); + PrintData(g_usTailDR, g_pucTDRData); +#endif /* DEBUG */ + } + break; + default: + break; + } + + /* + * + * Re-enable compression if it was previously set. + * + **/ + + if (compress) { + g_usDataType |= COMPRESS; + } + + if (g_usiDataSize) { + Code = GetByte(); + if (Code == CONTINUE) { + return 0; + } else { + + /* + * Encountered invalid opcode. + */ + + return VME_INVALID_FILE; + } + } + + return 0; +} + +/* + * + * ispVMLoop + * + * Perform the function call upon by the REPEAT opcode. + * Memory is to be allocated to store the entire loop from REPEAT to ENDLOOP. + * After the loop is stored then execution begin. The REPEATLOOP flag is set + * on the g_usFlowControl register to indicate the repeat loop is in session + * and therefore fetch opcode from the memory instead of from the file. + * + */ + +signed char ispVMLoop(unsigned short a_usLoopCount) +{ + /* 09/11/07 NN added local variables initialization */ + signed char cRetCode = 0; + unsigned short iHeapIndex = 0; + unsigned short iLoopIndex = 0; + + g_usShiftValue = 0; + for (iHeapIndex = 0; iHeapIndex < g_iHEAPSize; iHeapIndex++) { + g_pucHeapMemory[iHeapIndex] = GetByte(); + } + + if (g_pucHeapMemory[iHeapIndex - 1] != ENDLOOP) { + return VME_INVALID_FILE; + } + + g_usFlowControl |= REPEATLOOP; + g_usDataType |= HEAP_IN; + + for (iLoopIndex = 0; iLoopIndex < a_usLoopCount; iLoopIndex++) { + g_iHeapCounter = 0; + cRetCode = ispVMCode(); + g_usRepeatLoops++; + if (cRetCode < 0) { + break; + } + } + + g_usDataType &= ~(HEAP_IN); + g_usFlowControl &= ~(REPEATLOOP); + return cRetCode; +} + +/* + * + * ispVMBitShift + * + * Shift the TDI stream left or right by the number of bits. The data in + * *g_pucInData is of the VME format, so the actual shifting is the reverse of + * IEEE 1532 or SVF format. + * + */ + +signed char ispVMBitShift(signed char mode, unsigned short bits) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short i = 0; + unsigned short size = 0; + unsigned short tmpbits = 0; + + if (g_usiDataSize % 8 > 0) { + /* 09/11/07 NN Type cast mismatch variables */ + size = (unsigned short)(g_usiDataSize / 8 + 1); + } else { + /* 09/11/07 NN Type cast mismatch variables */ + size = (unsigned short)(g_usiDataSize / 8); + } + + switch (mode) { + case SHR: + for (i = 0; i < size; i++) { + if (g_pucInData[i] != 0) { + tmpbits = bits; + while (tmpbits > 0) { + g_pucInData[i] <<= 1; + if (g_pucInData[i] == 0) { + i--; + g_pucInData[i] = 1; + } + tmpbits--; + } + } + } + break; + case SHL: + for (i = 0; i < size; i++) { + if (g_pucInData[i] != 0) { + tmpbits = bits; + while (tmpbits > 0) { + g_pucInData[i] >>= 1; + if (g_pucInData[i] == 0) { + i--; + g_pucInData[i] = 8; + } + tmpbits--; + } + } + } + break; + default: + return VME_INVALID_FILE; + } + + return 0; +} + +/* + * + * ispVMComment + * + * Displays the SVF comments. + * + */ + +void ispVMComment(unsigned short a_usCommentSize) +{ + char cCurByte = 0; + for (; a_usCommentSize > 0; a_usCommentSize--) { + /* + * + * Print character to the terminal. + * + **/ + cCurByte = GetByte(); + vme_out_char(cCurByte); + } + cCurByte = '\n'; + vme_out_char(cCurByte); +} + +/* + * + * ispVMHeader + * + * Iterate the length of the header and discard it. + * + */ + +void ispVMHeader(unsigned short a_usHeaderSize) +{ + for (; a_usHeaderSize > 0; a_usHeaderSize--) { + GetByte(); + } +} + +/* + * + * ispVMCalculateCRC32 + * + * Calculate the 32-bit CRC. + * + */ + +void ispVMCalculateCRC32(unsigned char a_ucData) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned char ucIndex = 0; + unsigned char ucFlipData = 0; + unsigned short usCRCTableEntry = 0; + unsigned int crc_table[16] = { + 0x0000, 0xCC01, 0xD801, + 0x1400, 0xF001, 0x3C00, + 0x2800, 0xE401, 0xA001, + 0x6C00, 0x7800, 0xB401, + 0x5000, 0x9C01, 0x8801, + 0x4400 + }; + + for (ucIndex = 0; ucIndex < 8; ucIndex++) { + ucFlipData <<= 1; + if (a_ucData & 0x01) { + ucFlipData |= 0x01; + } + a_ucData >>= 1; + } + + /* 09/11/07 NN Type cast mismatch variables */ + usCRCTableEntry = (unsigned short)(crc_table[g_usCalculatedCRC & 0xF]); + g_usCalculatedCRC = (unsigned short)((g_usCalculatedCRC >> 4) & 0x0FFF); + g_usCalculatedCRC = (unsigned short)(g_usCalculatedCRC ^ + usCRCTableEntry ^ crc_table[ucFlipData & 0xF]); + usCRCTableEntry = (unsigned short)(crc_table[g_usCalculatedCRC & 0xF]); + g_usCalculatedCRC = (unsigned short)((g_usCalculatedCRC >> 4) & 0x0FFF); + g_usCalculatedCRC = (unsigned short)(g_usCalculatedCRC ^ + usCRCTableEntry ^ crc_table[(ucFlipData >> 4) & 0xF]); +} + +/* + * + * ispVMLCOUNT + * + * Process the intelligent programming loops. + * + */ + +signed char ispVMLCOUNT(unsigned short a_usCountSize) +{ + unsigned short usContinue = 1; + unsigned short usIntelBufferIndex = 0; + unsigned short usCountIndex = 0; + signed char cRetCode = 0; + signed char cRepeatHeap = 0; + signed char cOpcode = 0; + unsigned char ucState = 0; + unsigned short usDelay = 0; + unsigned short usToggle = 0; + + g_usIntelBufferSize = (unsigned short)ispVMDataSize(); + + /* + * Allocate memory for intel buffer. + * + */ + + ispVMMemManager(LHEAP, g_usIntelBufferSize); + + /* + * Store the maximum size of the intelligent buffer. + * Used to convert VME to HEX. + */ + + if (g_usIntelBufferSize > g_usLCOUNTSize) { + g_usLCOUNTSize = g_usIntelBufferSize; + } + + /* + * Copy intel data to the buffer. + */ + + for (usIntelBufferIndex = 0; usIntelBufferIndex < g_usIntelBufferSize; + usIntelBufferIndex++) { + g_pucIntelBuffer[usIntelBufferIndex] = GetByte(); + } + + /* + * Set the data type register to get data from the intelligent + * data buffer. + */ + + g_usDataType |= LHEAP_IN; + + /* + * + * If the HEAP_IN flag is set, temporarily unset the flag so data will be + * retrieved from the status buffer. + * + **/ + + if (g_usDataType & HEAP_IN) { + g_usDataType &= ~HEAP_IN; + cRepeatHeap = 1; + } + +#ifdef DEBUG + printf("LCOUNT %d;\n", a_usCountSize); +#endif /* DEBUG */ + + /* + * Iterate through the intelligent programming command. + */ + + for (usCountIndex = 0; usCountIndex < a_usCountSize; usCountIndex++) { + + /* + * + * Initialize the intel data index to 0 before each iteration. + * + **/ + + g_usIntelDataIndex = 0; + cOpcode = 0; + ucState = 0; + usDelay = 0; + usToggle = 0; + usContinue = 1; + + /* + * + * Begin looping through all the VME opcodes. + * + */ + /* + * 4/1/09 Nguyen replaced the recursive function call codes on + * the ispVMLCOUNT function + * + */ + while (usContinue) { + cOpcode = GetByte(); + switch (cOpcode) { + case HIR: + case TIR: + case HDR: + case TDR: + /* + * Set the header/trailer of the device in order + * to bypass successfully. + */ + + ispVMAmble(cOpcode); + break; + case STATE: + + /* + * Step the JTAG state machine. + */ + + ucState = GetByte(); + /* + * Step the JTAG state machine to DRCAPTURE + * to support Looping. + */ + + if ((g_usDataType & LHEAP_IN) && + (ucState == DRPAUSE) && + (g_cCurrentJTAGState == ucState)) { + ispVMStateMachine(DRCAPTURE); + } + ispVMStateMachine(ucState); +#ifdef DEBUG + printf("LDELAY %s ", GetState(ucState)); +#endif /* DEBUG */ + break; + case SIR: +#ifdef DEBUG + printf("SIR "); +#endif /* DEBUG */ + /* + * Shift in data into the device. + */ + + cRetCode = ispVMShift(cOpcode); + break; + case SDR: + +#ifdef DEBUG + printf("LSDR "); +#endif /* DEBUG */ + /* + * Shift in data into the device. + */ + + cRetCode = ispVMShift(cOpcode); + break; + case WAIT: + + /* + * + * Observe delay. + * + */ + + usDelay = (unsigned short)ispVMDataSize(); + ispVMDelay(usDelay); + +#ifdef DEBUG + if (usDelay & 0x8000) { + + /* + * Since MSB is set, the delay time must + * be decoded to millisecond. The + * SVF2VME encodes the MSB to represent + * millisecond. + */ + + usDelay &= ~0x8000; + printf("%.2E SEC;\n", + (float) usDelay / 1000); + } else { + /* + * Since MSB is not set, the delay time + * is given as microseconds. + */ + + printf("%.2E SEC;\n", + (float) usDelay / 1000000); + } +#endif /* DEBUG */ + break; + case TCK: + + /* + * Issue clock toggles. + */ + + usToggle = (unsigned short)ispVMDataSize(); + ispVMClocks(usToggle); + +#ifdef DEBUG + printf("RUNTEST %d TCK;\n", usToggle); +#endif /* DEBUG */ + break; + case ENDLOOP: + + /* + * Exit point from processing loops. + */ + usContinue = 0; + break; + + case COMMENT: + + /* + * Display comment. + */ + + ispVMComment((unsigned short) ispVMDataSize()); + break; + case ispEN: + ucState = GetByte(); + if ((ucState == ON) || (ucState == 0x01)) + writePort(g_ucPinENABLE, 0x01); + else + writePort(g_ucPinENABLE, 0x00); + ispVMDelay(1); + break; + case TRST: + if (GetByte() == 0x01) + writePort(g_ucPinTRST, 0x01); + else + writePort(g_ucPinTRST, 0x00); + ispVMDelay(1); + break; + default: + + /* + * Invalid opcode encountered. + */ + + debug("\nINVALID OPCODE: 0x%.2X\n", cOpcode); + + return VME_INVALID_FILE; + } + } + if (cRetCode >= 0) { + /* + * Break if intelligent programming is successful. + */ + + break; + } + + } + /* + * If HEAP_IN flag was temporarily disabled, + * re-enable it before exiting + */ + + if (cRepeatHeap) { + g_usDataType |= HEAP_IN; + } + + /* + * Set the data type register to not get data from the + * intelligent data buffer. + */ + + g_usDataType &= ~LHEAP_IN; + return cRetCode; +} +/* + * + * ispVMClocks + * + * Applies the specified number of pulses to TCK. + * + */ + +void ispVMClocks(unsigned short Clocks) +{ + unsigned short iClockIndex = 0; + for (iClockIndex = 0; iClockIndex < Clocks; iClockIndex++) { + sclock(); + } +} + +/* + * + * ispVMBypass + * + * This procedure takes care of the HIR, HDR, TIR, TDR for the + * purpose of putting the other devices into Bypass mode. The + * current state is checked to find out if it is at DRPAUSE or + * IRPAUSE. If it is at DRPAUSE, perform bypass register scan. + * If it is at IRPAUSE, scan into instruction registers the bypass + * instruction. + * + */ + +void ispVMBypass(signed char ScanType, unsigned short Bits) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short iIndex = 0; + unsigned short iSourceIndex = 0; + unsigned char cBitState = 0; + unsigned char cCurByte = 0; + unsigned char *pcSource = NULL; + + if (Bits <= 0) { + return; + } + + switch (ScanType) { + case HIR: + pcSource = g_pucHIRData; + break; + case TIR: + pcSource = g_pucTIRData; + break; + case HDR: + pcSource = g_pucHDRData; + break; + case TDR: + pcSource = g_pucTDRData; + break; + default: + break; + } + + iSourceIndex = 0; + cBitState = 0; + for (iIndex = 0; iIndex < Bits - 1; iIndex++) { + /* Scan instruction or bypass register */ + if (iIndex % 8 == 0) { + cCurByte = pcSource[iSourceIndex++]; + } + cBitState = (unsigned char) (((cCurByte << iIndex % 8) & 0x80) + ? 0x01 : 0x00); + writePort(g_ucPinTDI, cBitState); + sclock(); + } + + if (iIndex % 8 == 0) { + cCurByte = pcSource[iSourceIndex++]; + } + + cBitState = (unsigned char) (((cCurByte << iIndex % 8) & 0x80) + ? 0x01 : 0x00); + writePort(g_ucPinTDI, cBitState); +} + +/* + * + * ispVMStateMachine + * + * This procedure steps all devices in the daisy chain from a given + * JTAG state to the next desirable state. If the next state is TLR, + * the JTAG state machine is brute forced into TLR by driving TMS + * high and pulse TCK 6 times. + * + */ + +void ispVMStateMachine(signed char cNextJTAGState) +{ + /* 09/11/07 NN added local variables initialization */ + signed char cPathIndex = 0; + signed char cStateIndex = 0; + + if ((g_cCurrentJTAGState == cNextJTAGState) && + (cNextJTAGState != RESET)) { + return; + } + + for (cStateIndex = 0; cStateIndex < 25; cStateIndex++) { + if ((g_cCurrentJTAGState == + g_JTAGTransistions[cStateIndex].CurState) && + (cNextJTAGState == + g_JTAGTransistions[cStateIndex].NextState)) { + break; + } + } + + g_cCurrentJTAGState = cNextJTAGState; + for (cPathIndex = 0; + cPathIndex < g_JTAGTransistions[cStateIndex].Pulses; + cPathIndex++) { + if ((g_JTAGTransistions[cStateIndex].Pattern << cPathIndex) + & 0x80) { + writePort(g_ucPinTMS, (unsigned char) 0x01); + } else { + writePort(g_ucPinTMS, (unsigned char) 0x00); + } + sclock(); + } + + writePort(g_ucPinTDI, 0x00); + writePort(g_ucPinTMS, 0x00); +} + +/* + * + * ispVMStart + * + * Enable the port to the device and set the state to RESET (TLR). + * + */ + +void ispVMStart() +{ +#ifdef DEBUG + printf("// ISPVM EMBEDDED ADDED\n"); + printf("STATE RESET;\n"); +#endif + g_usFlowControl = 0; + g_usDataType = g_uiChecksumIndex = g_cCurrentJTAGState = 0; + g_usHeadDR = g_usHeadIR = g_usTailDR = g_usTailIR = 0; + g_usMaxSize = g_usShiftValue = g_usRepeatLoops = 0; + g_usTDOSize = g_usMASKSize = g_usTDISize = 0; + g_usDMASKSize = g_usLCOUNTSize = g_usHDRSize = 0; + g_usTDRSize = g_usHIRSize = g_usTIRSize = g_usHeapSize = 0; + g_pLVDSList = NULL; + g_usLVDSPairCount = 0; + previous_size = 0; + + ispVMStateMachine(RESET); /*step devices to RESET state*/ +} + +/* + * + * ispVMEnd + * + * Set the state of devices to RESET to enable the devices and disable + * the port. + * + */ + +void ispVMEnd() +{ +#ifdef DEBUG + printf("// ISPVM EMBEDDED ADDED\n"); + printf("STATE RESET;\n"); + printf("RUNTEST 1.00E-001 SEC;\n"); +#endif + + ispVMStateMachine(RESET); /*step devices to RESET state */ + ispVMDelay(1000); /*wake up devices*/ +} + +/* + * + * ispVMSend + * + * Send the TDI data stream to devices. The data stream can be + * instructions or data. + * + */ + +signed char ispVMSend(unsigned short a_usiDataSize) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short iIndex = 0; + unsigned short iInDataIndex = 0; + unsigned char cCurByte = 0; + unsigned char cBitState = 0; + + for (iIndex = 0; iIndex < a_usiDataSize - 1; iIndex++) { + if (iIndex % 8 == 0) { + cCurByte = g_pucInData[iInDataIndex++]; + } + cBitState = (unsigned char)(((cCurByte << iIndex % 8) & 0x80) + ? 0x01 : 0x00); + writePort(g_ucPinTDI, cBitState); + sclock(); + } + + if (iIndex % 8 == 0) { + /* Take care of the last bit */ + cCurByte = g_pucInData[iInDataIndex]; + } + + cBitState = (unsigned char) (((cCurByte << iIndex % 8) & 0x80) + ? 0x01 : 0x00); + + writePort(g_ucPinTDI, cBitState); + if (g_usFlowControl & CASCADE) { + /*1/15/04 Clock in last bit for the first n-1 cascaded frames */ + sclock(); + } + + return 0; +} + +/* + * + * ispVMRead + * + * Read the data stream from devices and verify. + * + */ + +signed char ispVMRead(unsigned short a_usiDataSize) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short usDataSizeIndex = 0; + unsigned short usErrorCount = 0; + unsigned short usLastBitIndex = 0; + unsigned char cDataByte = 0; + unsigned char cMaskByte = 0; + unsigned char cInDataByte = 0; + unsigned char cCurBit = 0; + unsigned char cByteIndex = 0; + unsigned short usBufferIndex = 0; + unsigned char ucDisplayByte = 0x00; + unsigned char ucDisplayFlag = 0x01; + char StrChecksum[256] = {0}; + unsigned char g_usCalculateChecksum = 0x00; + + /* 09/11/07 NN Type cast mismatch variables */ + usLastBitIndex = (unsigned short)(a_usiDataSize - 1); + +#ifndef DEBUG + /* + * If mask is not all zeros, then set the display flag to 0x00, + * otherwise it shall be set to 0x01 to indicate that data read + * from the device shall be displayed. If DEBUG is defined, + * always display data. + */ + + for (usDataSizeIndex = 0; usDataSizeIndex < (a_usiDataSize + 7) / 8; + usDataSizeIndex++) { + if (g_usDataType & MASK_DATA) { + if (g_pucOutMaskData[usDataSizeIndex] != 0x00) { + ucDisplayFlag = 0x00; + break; + } + } else if (g_usDataType & CMASK_DATA) { + g_usCalculateChecksum = 0x01; + ucDisplayFlag = 0x00; + break; + } else { + ucDisplayFlag = 0x00; + break; + } + } +#endif /* DEBUG */ + + /* + * + * Begin shifting data in and out of the device. + * + **/ + + for (usDataSizeIndex = 0; usDataSizeIndex < a_usiDataSize; + usDataSizeIndex++) { + if (cByteIndex == 0) { + + /* + * Grab byte from TDO buffer. + */ + + if (g_usDataType & TDO_DATA) { + cDataByte = g_pucOutData[usBufferIndex]; + } + + /* + * Grab byte from MASK buffer. + */ + + if (g_usDataType & MASK_DATA) { + cMaskByte = g_pucOutMaskData[usBufferIndex]; + } else { + cMaskByte = 0xFF; + } + + /* + * Grab byte from CMASK buffer. + */ + + if (g_usDataType & CMASK_DATA) { + cMaskByte = 0x00; + g_usCalculateChecksum = 0x01; + } + + /* + * Grab byte from TDI buffer. + */ + + if (g_usDataType & TDI_DATA) { + cInDataByte = g_pucInData[usBufferIndex]; + } + + usBufferIndex++; + } + + cCurBit = readPort(); + + if (ucDisplayFlag) { + ucDisplayByte <<= 1; + ucDisplayByte |= cCurBit; + } + + /* + * Check if data read from port matches with expected TDO. + */ + + if (g_usDataType & TDO_DATA) { + /* 08/28/08 NN Added Calculate checksum support. */ + if (g_usCalculateChecksum) { + if (cCurBit == 0x01) + g_usChecksum += + (1 << (g_uiChecksumIndex % 8)); + g_uiChecksumIndex++; + } else { + if ((((cMaskByte << cByteIndex) & 0x80) + ? 0x01 : 0x00)) { + if (cCurBit != (unsigned char) + (((cDataByte << cByteIndex) & 0x80) + ? 0x01 : 0x00)) { + usErrorCount++; + } + } + } + } + + /* + * Write TDI data to the port. + */ + + writePort(g_ucPinTDI, + (unsigned char)(((cInDataByte << cByteIndex) & 0x80) + ? 0x01 : 0x00)); + + if (usDataSizeIndex < usLastBitIndex) { + + /* + * Clock data out from the data shift register. + */ + + sclock(); + } else if (g_usFlowControl & CASCADE) { + + /* + * Clock in last bit for the first N - 1 cascaded frames + */ + + sclock(); + } + + /* + * Increment the byte index. If it exceeds 7, then reset it back + * to zero. + */ + + cByteIndex++; + if (cByteIndex >= 8) { + if (ucDisplayFlag) { + + /* + * Store displayed data in the TDO buffer. By reusing + * the TDO buffer to store displayed data, there is no + * need to allocate a buffer simply to hold display + * data. This will not cause any false verification + * errors because the true TDO byte has already + * been consumed. + */ + + g_pucOutData[usBufferIndex - 1] = ucDisplayByte; + ucDisplayByte = 0; + } + + cByteIndex = 0; + } + /* 09/12/07 Nguyen changed to display the 1 bit expected data */ + else if (a_usiDataSize == 1) { + if (ucDisplayFlag) { + + /* + * Store displayed data in the TDO buffer. + * By reusing the TDO buffer to store displayed + * data, there is no need to allocate + * a buffer simply to hold display data. This + * will not cause any false verification errors + * because the true TDO byte has already + * been consumed. + */ + + /* + * Flip ucDisplayByte and store it in cDataByte. + */ + cDataByte = 0x00; + for (usBufferIndex = 0; usBufferIndex < 8; + usBufferIndex++) { + cDataByte <<= 1; + if (ucDisplayByte & 0x01) { + cDataByte |= 0x01; + } + ucDisplayByte >>= 1; + } + g_pucOutData[0] = cDataByte; + ucDisplayByte = 0; + } + + cByteIndex = 0; + } + } + + if (ucDisplayFlag) { + +#ifdef DEBUG + debug("RECEIVED TDO ("); +#else + vme_out_string("Display Data: 0x"); +#endif /* DEBUG */ + + /* 09/11/07 NN Type cast mismatch variables */ + for (usDataSizeIndex = (unsigned short) + ((a_usiDataSize + 7) / 8); + usDataSizeIndex > 0 ; usDataSizeIndex--) { + cMaskByte = g_pucOutData[usDataSizeIndex - 1]; + cDataByte = 0x00; + + /* + * Flip cMaskByte and store it in cDataByte. + */ + + for (usBufferIndex = 0; usBufferIndex < 8; + usBufferIndex++) { + cDataByte <<= 1; + if (cMaskByte & 0x01) { + cDataByte |= 0x01; + } + cMaskByte >>= 1; + } +#ifdef DEBUG + printf("%.2X", cDataByte); + if ((((a_usiDataSize + 7) / 8) - usDataSizeIndex) + % 40 == 39) { + printf("\n\t\t"); + } +#else + vme_out_hex(cDataByte); +#endif /* DEBUG */ + } + +#ifdef DEBUG + printf(")\n\n"); +#else + vme_out_string("\n\n"); +#endif /* DEBUG */ + /* 09/02/08 Nguyen changed to display the data Checksum */ + if (g_usChecksum != 0) { + g_usChecksum &= 0xFFFF; + sprintf(StrChecksum, "Data Checksum: %.4lX\n\n", + g_usChecksum); + vme_out_string(StrChecksum); + g_usChecksum = 0; + } + } + + if (usErrorCount > 0) { + if (g_usFlowControl & VERIFYUES) { + vme_out_string( + "USERCODE verification failed. " + "Continue programming......\n\n"); + g_usFlowControl &= ~(VERIFYUES); + return 0; + } else { + +#ifdef DEBUG + printf("TOTAL ERRORS: %d\n", usErrorCount); +#endif /* DEBUG */ + + return VME_VERIFICATION_FAILURE; + } + } else { + if (g_usFlowControl & VERIFYUES) { + vme_out_string("USERCODE verification passed. " + "Programming aborted.\n\n"); + g_usFlowControl &= ~(VERIFYUES); + return 1; + } else { + return 0; + } + } +} + +/* + * + * ispVMReadandSave + * + * Support dynamic I/O. + * + */ + +signed char ispVMReadandSave(unsigned short int a_usiDataSize) +{ + /* 09/11/07 NN added local variables initialization */ + unsigned short int usDataSizeIndex = 0; + unsigned short int usLastBitIndex = 0; + unsigned short int usBufferIndex = 0; + unsigned short int usOutBitIndex = 0; + unsigned short int usLVDSIndex = 0; + unsigned char cDataByte = 0; + unsigned char cDMASKByte = 0; + unsigned char cInDataByte = 0; + unsigned char cCurBit = 0; + unsigned char cByteIndex = 0; + signed char cLVDSByteIndex = 0; + + /* 09/11/07 NN Type cast mismatch variables */ + usLastBitIndex = (unsigned short) (a_usiDataSize - 1); + + /* + * + * Iterate through the data bits. + * + */ + + for (usDataSizeIndex = 0; usDataSizeIndex < a_usiDataSize; + usDataSizeIndex++) { + if (cByteIndex == 0) { + + /* + * Grab byte from DMASK buffer. + */ + + if (g_usDataType & DMASK_DATA) { + cDMASKByte = g_pucOutDMaskData[usBufferIndex]; + } else { + cDMASKByte = 0x00; + } + + /* + * Grab byte from TDI buffer. + */ + + if (g_usDataType & TDI_DATA) { + cInDataByte = g_pucInData[usBufferIndex]; + } + + usBufferIndex++; + } + + cCurBit = readPort(); + cDataByte = (unsigned char)(((cInDataByte << cByteIndex) & 0x80) + ? 0x01 : 0x00); + + /* + * Initialize the byte to be zero. + */ + + if (usOutBitIndex % 8 == 0) { + g_pucOutData[usOutBitIndex / 8] = 0x00; + } + + /* + * Use TDI, DMASK, and device TDO to create new TDI (actually + * stored in g_pucOutData). + */ + + if ((((cDMASKByte << cByteIndex) & 0x80) ? 0x01 : 0x00)) { + + if (g_pLVDSList) { + for (usLVDSIndex = 0; + usLVDSIndex < g_usLVDSPairCount; + usLVDSIndex++) { + if (g_pLVDSList[usLVDSIndex]. + usNegativeIndex == + usDataSizeIndex) { + g_pLVDSList[usLVDSIndex]. + ucUpdate = 0x01; + break; + } + } + } + + /* + * DMASK bit is 1, use TDI. + */ + + g_pucOutData[usOutBitIndex / 8] |= (unsigned char) + (((cDataByte & 0x1) ? 0x01 : 0x00) << + (7 - usOutBitIndex % 8)); + } else { + + /* + * DMASK bit is 0, use device TDO. + */ + + g_pucOutData[usOutBitIndex / 8] |= (unsigned char) + (((cCurBit & 0x1) ? 0x01 : 0x00) << + (7 - usOutBitIndex % 8)); + } + + /* + * Shift in TDI in order to get TDO out. + */ + + usOutBitIndex++; + writePort(g_ucPinTDI, cDataByte); + if (usDataSizeIndex < usLastBitIndex) { + sclock(); + } + + /* + * Increment the byte index. If it exceeds 7, then reset it back + * to zero. + */ + + cByteIndex++; + if (cByteIndex >= 8) { + cByteIndex = 0; + } + } + + /* + * If g_pLVDSList exists and pairs need updating, then update + * the negative-pair to receive the flipped positive-pair value. + */ + + if (g_pLVDSList) { + for (usLVDSIndex = 0; usLVDSIndex < g_usLVDSPairCount; + usLVDSIndex++) { + if (g_pLVDSList[usLVDSIndex].ucUpdate) { + + /* + * Read the positive value and flip it. + */ + + cDataByte = (unsigned char) + (((g_pucOutData[g_pLVDSList[usLVDSIndex]. + usPositiveIndex / 8] + << (g_pLVDSList[usLVDSIndex]. + usPositiveIndex % 8)) & 0x80) ? + 0x01 : 0x00); + /* 09/11/07 NN Type cast mismatch variables */ + cDataByte = (unsigned char) (!cDataByte); + + /* + * Get the byte that needs modification. + */ + + cInDataByte = + g_pucOutData[g_pLVDSList[usLVDSIndex]. + usNegativeIndex / 8]; + + if (cDataByte) { + + /* + * Copy over the current byte and + * set the negative bit to 1. + */ + + cDataByte = 0x00; + for (cLVDSByteIndex = 7; + cLVDSByteIndex >= 0; + cLVDSByteIndex--) { + cDataByte <<= 1; + if (7 - + (g_pLVDSList[usLVDSIndex]. + usNegativeIndex % 8) == + cLVDSByteIndex) { + + /* + * Set negative bit to 1 + */ + + cDataByte |= 0x01; + } else if (cInDataByte & 0x80) { + cDataByte |= 0x01; + } + + cInDataByte <<= 1; + } + + /* + * Store the modified byte. + */ + + g_pucOutData[g_pLVDSList[usLVDSIndex]. + usNegativeIndex / 8] = cDataByte; + } else { + + /* + * Copy over the current byte and set + * the negative bit to 0. + */ + + cDataByte = 0x00; + for (cLVDSByteIndex = 7; + cLVDSByteIndex >= 0; + cLVDSByteIndex--) { + cDataByte <<= 1; + if (7 - + (g_pLVDSList[usLVDSIndex]. + usNegativeIndex % 8) == + cLVDSByteIndex) { + + /* + * Set negative bit to 0 + */ + + cDataByte |= 0x00; + } else if (cInDataByte & 0x80) { + cDataByte |= 0x01; + } + + cInDataByte <<= 1; + } + + /* + * Store the modified byte. + */ + + g_pucOutData[g_pLVDSList[usLVDSIndex]. + usNegativeIndex / 8] = cDataByte; + } + + break; + } + } + } + + return 0; +} + +signed char ispVMProcessLVDS(unsigned short a_usLVDSCount) +{ + unsigned short usLVDSIndex = 0; + + /* + * Allocate memory to hold LVDS pairs. + */ + + ispVMMemManager(LVDS, a_usLVDSCount); + g_usLVDSPairCount = a_usLVDSCount; + +#ifdef DEBUG + printf("LVDS %d (", a_usLVDSCount); +#endif /* DEBUG */ + + /* + * Iterate through each given LVDS pair. + */ + + for (usLVDSIndex = 0; usLVDSIndex < g_usLVDSPairCount; usLVDSIndex++) { + + /* + * Assign the positive and negative indices of the LVDS pair. + */ + + /* 09/11/07 NN Type cast mismatch variables */ + g_pLVDSList[usLVDSIndex].usPositiveIndex = + (unsigned short) ispVMDataSize(); + /* 09/11/07 NN Type cast mismatch variables */ + g_pLVDSList[usLVDSIndex].usNegativeIndex = + (unsigned short)ispVMDataSize(); + +#ifdef DEBUG + if (usLVDSIndex < g_usLVDSPairCount - 1) { + printf("%d:%d, ", + g_pLVDSList[usLVDSIndex].usPositiveIndex, + g_pLVDSList[usLVDSIndex].usNegativeIndex); + } else { + printf("%d:%d", + g_pLVDSList[usLVDSIndex].usPositiveIndex, + g_pLVDSList[usLVDSIndex].usNegativeIndex); + } +#endif /* DEBUG */ + + } + +#ifdef DEBUG + printf(");\n", a_usLVDSCount); +#endif /* DEBUG */ + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/fpga/lattice.c b/qemu/roms/u-boot/drivers/fpga/lattice.c new file mode 100644 index 000000000..615a48508 --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/lattice.c @@ -0,0 +1,380 @@ +/* + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * ispVM functions adapted from Lattice's ispmVMEmbedded code: + * Copyright 2009 Lattice Semiconductor Corp. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static lattice_board_specific_func *pfns; +static const char *fpga_image; +static unsigned long read_bytes; +static unsigned long bufsize; +static unsigned short expectedCRC; + +/* + * External variables and functions declared in ivm_core.c module. + */ +extern unsigned short g_usCalculatedCRC; +extern unsigned short g_usDataType; +extern unsigned char *g_pucIntelBuffer; +extern unsigned char *g_pucHeapMemory; +extern unsigned short g_iHeapCounter; +extern unsigned short g_iHEAPSize; +extern unsigned short g_usIntelDataIndex; +extern unsigned short g_usIntelBufferSize; +extern char *const g_szSupportedVersions[]; + + +/* + * ispVMDelay + * + * Users must implement a delay to observe a_usTimeDelay, where + * bit 15 of the a_usTimeDelay defines the unit. + * 1 = milliseconds + * 0 = microseconds + * Example: + * a_usTimeDelay = 0x0001 = 1 microsecond delay. + * a_usTimeDelay = 0x8001 = 1 millisecond delay. + * + * This subroutine is called upon to provide a delay from 1 millisecond to a few + * hundreds milliseconds each time. + * It is understood that due to a_usTimeDelay is defined as unsigned short, a 16 + * bits integer, this function is restricted to produce a delay to 64000 + * micro-seconds or 32000 milli-second maximum. The VME file will never pass on + * to this function a delay time > those maximum number. If it needs more than + * those maximum, the VME file will launch the delay function several times to + * realize a larger delay time cummulatively. + * It is perfectly alright to provide a longer delay than required. It is not + * acceptable if the delay is shorter. + */ +void ispVMDelay(unsigned short delay) +{ + if (delay & 0x8000) + delay = (delay & ~0x8000) * 1000; + udelay(delay); +} + +void writePort(unsigned char a_ucPins, unsigned char a_ucValue) +{ + a_ucValue = a_ucValue ? 1 : 0; + + switch (a_ucPins) { + case g_ucPinTDI: + pfns->jtag_set_tdi(a_ucValue); + break; + case g_ucPinTCK: + pfns->jtag_set_tck(a_ucValue); + break; + case g_ucPinTMS: + pfns->jtag_set_tms(a_ucValue); + break; + default: + printf("%s: requested unknown pin\n", __func__); + } +} + +unsigned char readPort(void) +{ + return pfns->jtag_get_tdo(); +} + +void sclock(void) +{ + writePort(g_ucPinTCK, 0x01); + writePort(g_ucPinTCK, 0x00); +} + +void calibration(void) +{ + /* Apply 2 pulses to TCK. */ + writePort(g_ucPinTCK, 0x00); + writePort(g_ucPinTCK, 0x01); + writePort(g_ucPinTCK, 0x00); + writePort(g_ucPinTCK, 0x01); + writePort(g_ucPinTCK, 0x00); + + ispVMDelay(0x8001); + + /* Apply 2 pulses to TCK. */ + writePort(g_ucPinTCK, 0x01); + writePort(g_ucPinTCK, 0x00); + writePort(g_ucPinTCK, 0x01); + writePort(g_ucPinTCK, 0x00); +} + +/* + * GetByte + * + * Returns a byte to the caller. The returned byte depends on the + * g_usDataType register. If the HEAP_IN bit is set, then the byte + * is returned from the HEAP. If the LHEAP_IN bit is set, then + * the byte is returned from the intelligent buffer. Otherwise, + * the byte is returned directly from the VME file. + */ +unsigned char GetByte(void) +{ + unsigned char ucData; + unsigned int block_size = 4 * 1024; + + if (g_usDataType & HEAP_IN) { + + /* + * Get data from repeat buffer. + */ + + if (g_iHeapCounter > g_iHEAPSize) { + + /* + * Data over-run. + */ + + return 0xFF; + } + + ucData = g_pucHeapMemory[g_iHeapCounter++]; + } else if (g_usDataType & LHEAP_IN) { + + /* + * Get data from intel buffer. + */ + + if (g_usIntelDataIndex >= g_usIntelBufferSize) { + return 0xFF; + } + + ucData = g_pucIntelBuffer[g_usIntelDataIndex++]; + } else { + if (read_bytes == bufsize) { + return 0xFF; + } + ucData = *fpga_image++; + read_bytes++; + + if (!(read_bytes % block_size)) { + printf("Downloading FPGA %ld/%ld completed\r", + read_bytes, + bufsize); + } + + if (expectedCRC != 0) { + ispVMCalculateCRC32(ucData); + } + } + + return ucData; +} + +signed char ispVM(void) +{ + char szFileVersion[9] = { 0 }; + signed char cRetCode = 0; + signed char cIndex = 0; + signed char cVersionIndex = 0; + unsigned char ucReadByte = 0; + unsigned short crc; + + g_pucHeapMemory = NULL; + g_iHeapCounter = 0; + g_iHEAPSize = 0; + g_usIntelDataIndex = 0; + g_usIntelBufferSize = 0; + g_usCalculatedCRC = 0; + expectedCRC = 0; + ucReadByte = GetByte(); + switch (ucReadByte) { + case FILE_CRC: + crc = (unsigned char)GetByte(); + crc <<= 8; + crc |= GetByte(); + expectedCRC = crc; + + for (cIndex = 0; cIndex < 8; cIndex++) + szFileVersion[cIndex] = GetByte(); + + break; + default: + szFileVersion[0] = (signed char) ucReadByte; + for (cIndex = 1; cIndex < 8; cIndex++) + szFileVersion[cIndex] = GetByte(); + + break; + } + + /* + * + * Compare the VME file version against the supported version. + * + */ + + for (cVersionIndex = 0; g_szSupportedVersions[cVersionIndex] != 0; + cVersionIndex++) { + for (cIndex = 0; cIndex < 8; cIndex++) { + if (szFileVersion[cIndex] != + g_szSupportedVersions[cVersionIndex][cIndex]) { + cRetCode = VME_VERSION_FAILURE; + break; + } + cRetCode = 0; + } + + if (cRetCode == 0) { + break; + } + } + + if (cRetCode < 0) { + return VME_VERSION_FAILURE; + } + + printf("VME file checked: starting downloading to FPGA\n"); + + ispVMStart(); + + cRetCode = ispVMCode(); + + ispVMEnd(); + ispVMFreeMem(); + puts("\n"); + + if (cRetCode == 0 && expectedCRC != 0 && + (expectedCRC != g_usCalculatedCRC)) { + printf("Expected CRC: 0x%.4X\n", expectedCRC); + printf("Calculated CRC: 0x%.4X\n", g_usCalculatedCRC); + return VME_CRC_FAILURE; + } + return cRetCode; +} + +static int lattice_validate(Lattice_desc *desc, const char *fn) +{ + int ret_val = false; + + if (desc) { + if ((desc->family > min_lattice_type) && + (desc->family < max_lattice_type)) { + if ((desc->iface > min_lattice_iface_type) && + (desc->iface < max_lattice_iface_type)) { + if (desc->size) { + ret_val = true; + } else { + printf("%s: NULL part size\n", fn); + } + } else { + printf("%s: Invalid Interface type, %d\n", + fn, desc->iface); + } + } else { + printf("%s: Invalid family type, %d\n", + fn, desc->family); + } + } else { + printf("%s: NULL descriptor!\n", fn); + } + + return ret_val; +} + +int lattice_load(Lattice_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + if (!lattice_validate(desc, (char *)__func__)) { + printf("%s: Invalid device descriptor\n", __func__); + } else { + pfns = desc->iface_fns; + + switch (desc->family) { + case Lattice_XP2: + fpga_image = buf; + read_bytes = 0; + bufsize = bsize; + debug("%s: Launching the Lattice ISPVME Loader:" + " addr %p size 0x%lx...\n", + __func__, fpga_image, bufsize); + ret_val = ispVM(); + if (ret_val) + printf("%s: error %d downloading FPGA image\n", + __func__, ret_val); + else + puts("FPGA downloaded successfully\n"); + break; + default: + printf("%s: Unsupported family type, %d\n", + __func__, desc->family); + } + } + + return ret_val; +} + +int lattice_dump(Lattice_desc *desc, const void *buf, size_t bsize) +{ + puts("Dump not supported for Lattice FPGA\n"); + + return FPGA_FAIL; + +} + +int lattice_info(Lattice_desc *desc) +{ + int ret_val = FPGA_FAIL; + + if (lattice_validate(desc, (char *)__func__)) { + printf("Family: \t"); + switch (desc->family) { + case Lattice_XP2: + puts("XP2\n"); + break; + /* Add new family types here */ + default: + printf("Unknown family type, %d\n", desc->family); + } + + puts("Interface type:\t"); + switch (desc->iface) { + case lattice_jtag_mode: + puts("JTAG Mode\n"); + break; + /* Add new interface types here */ + default: + printf("Unsupported interface type, %d\n", desc->iface); + } + + printf("Device Size: \t%d bytes\n", + desc->size); + + if (desc->iface_fns) { + printf("Device Function Table @ 0x%p\n", + desc->iface_fns); + switch (desc->family) { + case Lattice_XP2: + break; + /* Add new family types here */ + default: + break; + } + } else { + puts("No Device Function Table.\n"); + } + + if (desc->desc) + printf("Model: \t%s\n", desc->desc); + + ret_val = FPGA_SUCCESS; + } else { + printf("%s: Invalid device descriptor\n", __func__); + } + + return ret_val; +} diff --git a/qemu/roms/u-boot/drivers/fpga/spartan2.c b/qemu/roms/u-boot/drivers/fpga/spartan2.c new file mode 100644 index 000000000..705405614 --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/spartan2.c @@ -0,0 +1,455 @@ +/* + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include /* core U-Boot definitions */ +#include /* Spartan-II device family */ + +/* Define FPGA_DEBUG to get debug printf's */ +#ifdef FPGA_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +#undef CONFIG_SYS_FPGA_CHECK_BUSY +#undef CONFIG_SYS_FPGA_PROG_FEEDBACK + +/* Note: The assumption is that we cannot possibly run fast enough to + * overrun the device (the Slave Parallel mode can free run at 50MHz). + * If there is a need to operate slower, define CONFIG_FPGA_DELAY in + * the board config file to slow things down. + */ +#ifndef CONFIG_FPGA_DELAY +#define CONFIG_FPGA_DELAY() +#endif + +#ifndef CONFIG_SYS_FPGA_WAIT +#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/100 /* 10 ms */ +#endif + +static int spartan2_sp_load(xilinx_desc *desc, const void *buf, size_t bsize); +static int spartan2_sp_dump(xilinx_desc *desc, const void *buf, size_t bsize); +/* static int spartan2_sp_info(xilinx_desc *desc ); */ + +static int spartan2_ss_load(xilinx_desc *desc, const void *buf, size_t bsize); +static int spartan2_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize); +/* static int spartan2_ss_info(xilinx_desc *desc ); */ + +/* ------------------------------------------------------------------------- */ +/* Spartan-II Generic Implementation */ +static int spartan2_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case slave_serial: + PRINTF ("%s: Launching Slave Serial Load\n", __FUNCTION__); + ret_val = spartan2_ss_load(desc, buf, bsize); + break; + + case slave_parallel: + PRINTF ("%s: Launching Slave Parallel Load\n", __FUNCTION__); + ret_val = spartan2_sp_load(desc, buf, bsize); + break; + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + + return ret_val; +} + +static int spartan2_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case slave_serial: + PRINTF ("%s: Launching Slave Serial Dump\n", __FUNCTION__); + ret_val = spartan2_ss_dump(desc, buf, bsize); + break; + + case slave_parallel: + PRINTF ("%s: Launching Slave Parallel Dump\n", __FUNCTION__); + ret_val = spartan2_sp_dump(desc, buf, bsize); + break; + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + + return ret_val; +} + +static int spartan2_info(xilinx_desc *desc) +{ + return FPGA_SUCCESS; +} + + +/* ------------------------------------------------------------------------- */ +/* Spartan-II Slave Parallel Generic Implementation */ + +static int spartan2_sp_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume the worst */ + xilinx_spartan2_slave_parallel_fns *fn = desc->iface_fns; + + PRINTF ("%s: start with interface functions @ 0x%p\n", + __FUNCTION__, fn); + + if (fn) { + size_t bytecount = 0; + unsigned char *data = (unsigned char *) buf; + int cookie = desc->cookie; /* make a local copy */ + unsigned long ts; /* timestamp */ + + PRINTF ("%s: Function Table:\n" + "ptr:\t0x%p\n" + "struct: 0x%p\n" + "pre: 0x%p\n" + "pgm:\t0x%p\n" + "init:\t0x%p\n" + "err:\t0x%p\n" + "clk:\t0x%p\n" + "cs:\t0x%p\n" + "wr:\t0x%p\n" + "read data:\t0x%p\n" + "write data:\t0x%p\n" + "busy:\t0x%p\n" + "abort:\t0x%p\n", + "post:\t0x%p\n\n", + __FUNCTION__, &fn, fn, fn->pre, fn->pgm, fn->init, fn->err, + fn->clk, fn->cs, fn->wr, fn->rdata, fn->wdata, fn->busy, + fn->abort, fn->post); + + /* + * This code is designed to emulate the "Express Style" + * Continuous Data Loading in Slave Parallel Mode for + * the Spartan-II Family. + */ +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + printf ("Loading FPGA Device %d...\n", cookie); +#endif + /* + * Run the pre configuration function if there is one. + */ + if (*fn->pre) { + (*fn->pre) (cookie); + } + + /* Establish the initial state */ + (*fn->pgm) (true, true, cookie); /* Assert the program, commit */ + + /* Get ready for the burn */ + CONFIG_FPGA_DELAY (); + (*fn->pgm) (false, true, cookie); /* Deassert the program, commit */ + + ts = get_timer (0); /* get current time */ + /* Now wait for INIT and BUSY to go high */ + do { + CONFIG_FPGA_DELAY (); + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for INIT to clear.\n"); + (*fn->abort) (cookie); /* abort the burn */ + return FPGA_FAIL; + } + } while ((*fn->init) (cookie) && (*fn->busy) (cookie)); + + (*fn->wr) (true, true, cookie); /* Assert write, commit */ + (*fn->cs) (true, true, cookie); /* Assert chip select, commit */ + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + /* Load the data */ + while (bytecount < bsize) { + /* XXX - do we check for an Ctrl-C press in here ??? */ + /* XXX - Check the error bit? */ + + (*fn->wdata) (data[bytecount++], true, cookie); /* write the data */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + +#ifdef CONFIG_SYS_FPGA_CHECK_BUSY + ts = get_timer (0); /* get current time */ + while ((*fn->busy) (cookie)) { + /* XXX - we should have a check in here somewhere to + * make sure we aren't busy forever... */ + + CONFIG_FPGA_DELAY (); + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for BUSY to clear.\n"); + (*fn->abort) (cookie); /* abort the burn */ + return FPGA_FAIL; + } + } +#endif + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (bytecount % (bsize / 40) == 0) + putc ('.'); /* let them know we are alive */ +#endif + } + + CONFIG_FPGA_DELAY (); + (*fn->cs) (false, true, cookie); /* Deassert the chip select */ + (*fn->wr) (false, true, cookie); /* Deassert the write pin */ + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc ('\n'); /* terminate the dotted line */ +#endif + + /* now check for done signal */ + ts = get_timer (0); /* get current time */ + ret_val = FPGA_SUCCESS; + while ((*fn->done) (cookie) == FPGA_FAIL) { + + CONFIG_FPGA_DELAY (); + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for DONE to clear.\n"); + (*fn->abort) (cookie); /* abort the burn */ + ret_val = FPGA_FAIL; + break; + } + } + + /* + * Run the post configuration function if there is one. + */ + if (*fn->post) + (*fn->post) (cookie); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (ret_val == FPGA_SUCCESS) + puts ("Done.\n"); + else + puts ("Fail.\n"); +#endif + + } else { + printf ("%s: NULL Interface function table!\n", __FUNCTION__); + } + + return ret_val; +} + +static int spartan2_sp_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume the worst */ + xilinx_spartan2_slave_parallel_fns *fn = desc->iface_fns; + + if (fn) { + unsigned char *data = (unsigned char *) buf; + size_t bytecount = 0; + int cookie = desc->cookie; /* make a local copy */ + + printf ("Starting Dump of FPGA Device %d...\n", cookie); + + (*fn->cs) (true, true, cookie); /* Assert chip select, commit */ + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + /* dump the data */ + while (bytecount < bsize) { + /* XXX - do we check for an Ctrl-C press in here ??? */ + + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + (*fn->rdata) (&(data[bytecount++]), cookie); /* read the data */ +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (bytecount % (bsize / 40) == 0) + putc ('.'); /* let them know we are alive */ +#endif + } + + (*fn->cs) (false, false, cookie); /* Deassert the chip select */ + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc ('\n'); /* terminate the dotted line */ +#endif + puts ("Done.\n"); + + /* XXX - checksum the data? */ + } else { + printf ("%s: NULL Interface function table!\n", __FUNCTION__); + } + + return ret_val; +} + + +/* ------------------------------------------------------------------------- */ + +static int spartan2_ss_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume the worst */ + xilinx_spartan2_slave_serial_fns *fn = desc->iface_fns; + int i; + unsigned char val; + + PRINTF ("%s: start with interface functions @ 0x%p\n", + __FUNCTION__, fn); + + if (fn) { + size_t bytecount = 0; + unsigned char *data = (unsigned char *) buf; + int cookie = desc->cookie; /* make a local copy */ + unsigned long ts; /* timestamp */ + + PRINTF ("%s: Function Table:\n" + "ptr:\t0x%p\n" + "struct: 0x%p\n" + "pgm:\t0x%p\n" + "init:\t0x%p\n" + "clk:\t0x%p\n" + "wr:\t0x%p\n" + "done:\t0x%p\n\n", + __FUNCTION__, &fn, fn, fn->pgm, fn->init, + fn->clk, fn->wr, fn->done); +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + printf ("Loading FPGA Device %d...\n", cookie); +#endif + + /* + * Run the pre configuration function if there is one. + */ + if (*fn->pre) { + (*fn->pre) (cookie); + } + + /* Establish the initial state */ + (*fn->pgm) (true, true, cookie); /* Assert the program, commit */ + + /* Wait for INIT state (init low) */ + ts = get_timer (0); /* get current time */ + do { + CONFIG_FPGA_DELAY (); + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for INIT to start.\n"); + return FPGA_FAIL; + } + } while (!(*fn->init) (cookie)); + + /* Get ready for the burn */ + CONFIG_FPGA_DELAY (); + (*fn->pgm) (false, true, cookie); /* Deassert the program, commit */ + + ts = get_timer (0); /* get current time */ + /* Now wait for INIT to go high */ + do { + CONFIG_FPGA_DELAY (); + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for INIT to clear.\n"); + return FPGA_FAIL; + } + } while ((*fn->init) (cookie)); + + /* Load the data */ + while (bytecount < bsize) { + + /* Xilinx detects an error if INIT goes low (active) + while DONE is low (inactive) */ + if ((*fn->done) (cookie) == 0 && (*fn->init) (cookie)) { + puts ("** CRC error during FPGA load.\n"); + return (FPGA_FAIL); + } + val = data [bytecount ++]; + i = 8; + do { + /* Deassert the clock */ + (*fn->clk) (false, true, cookie); + CONFIG_FPGA_DELAY (); + /* Write data */ + (*fn->wr) ((val & 0x80), true, cookie); + CONFIG_FPGA_DELAY (); + /* Assert the clock */ + (*fn->clk) (true, true, cookie); + CONFIG_FPGA_DELAY (); + val <<= 1; + i --; + } while (i > 0); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (bytecount % (bsize / 40) == 0) + putc ('.'); /* let them know we are alive */ +#endif + } + + CONFIG_FPGA_DELAY (); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc ('\n'); /* terminate the dotted line */ +#endif + + /* now check for done signal */ + ts = get_timer (0); /* get current time */ + ret_val = FPGA_SUCCESS; + (*fn->wr) (true, true, cookie); + + while (! (*fn->done) (cookie)) { + + CONFIG_FPGA_DELAY (); + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + putc ('*'); + + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for DONE to clear.\n"); + ret_val = FPGA_FAIL; + break; + } + } + putc ('\n'); /* terminate the dotted line */ + + /* + * Run the post configuration function if there is one. + */ + if (*fn->post) + (*fn->post) (cookie); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (ret_val == FPGA_SUCCESS) + puts ("Done.\n"); + else + puts ("Fail.\n"); +#endif + + } else { + printf ("%s: NULL Interface function table!\n", __FUNCTION__); + } + + return ret_val; +} + +static int spartan2_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + /* Readback is only available through the Slave Parallel and */ + /* boundary-scan interfaces. */ + printf ("%s: Slave Serial Dumping is unavailable\n", + __FUNCTION__); + return FPGA_FAIL; +} + +struct xilinx_fpga_op spartan2_op = { + .load = spartan2_load, + .dump = spartan2_dump, + .info = spartan2_info, +}; diff --git a/qemu/roms/u-boot/drivers/fpga/spartan3.c b/qemu/roms/u-boot/drivers/fpga/spartan3.c new file mode 100644 index 000000000..5c9412c2f --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/spartan3.c @@ -0,0 +1,473 @@ +/* + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Configuration support for Xilinx Spartan3 devices. Based + * on spartan2.c (Rich Ireland, rireland@enterasys.com). + */ + +#include /* core U-Boot definitions */ +#include /* Spartan-II device family */ + +/* Define FPGA_DEBUG to get debug printf's */ +#ifdef FPGA_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +#undef CONFIG_SYS_FPGA_CHECK_BUSY + +/* Note: The assumption is that we cannot possibly run fast enough to + * overrun the device (the Slave Parallel mode can free run at 50MHz). + * If there is a need to operate slower, define CONFIG_FPGA_DELAY in + * the board config file to slow things down. + */ +#ifndef CONFIG_FPGA_DELAY +#define CONFIG_FPGA_DELAY() +#endif + +#ifndef CONFIG_SYS_FPGA_WAIT +#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/100 /* 10 ms */ +#endif + +static int spartan3_sp_load(xilinx_desc *desc, const void *buf, size_t bsize); +static int spartan3_sp_dump(xilinx_desc *desc, const void *buf, size_t bsize); +/* static int spartan3_sp_info(xilinx_desc *desc ); */ + +static int spartan3_ss_load(xilinx_desc *desc, const void *buf, size_t bsize); +static int spartan3_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize); +/* static int spartan3_ss_info(xilinx_desc *desc); */ + +/* ------------------------------------------------------------------------- */ +/* Spartan-II Generic Implementation */ +static int spartan3_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case slave_serial: + PRINTF ("%s: Launching Slave Serial Load\n", __FUNCTION__); + ret_val = spartan3_ss_load(desc, buf, bsize); + break; + + case slave_parallel: + PRINTF ("%s: Launching Slave Parallel Load\n", __FUNCTION__); + ret_val = spartan3_sp_load(desc, buf, bsize); + break; + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + + return ret_val; +} + +static int spartan3_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case slave_serial: + PRINTF ("%s: Launching Slave Serial Dump\n", __FUNCTION__); + ret_val = spartan3_ss_dump(desc, buf, bsize); + break; + + case slave_parallel: + PRINTF ("%s: Launching Slave Parallel Dump\n", __FUNCTION__); + ret_val = spartan3_sp_dump(desc, buf, bsize); + break; + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + + return ret_val; +} + +static int spartan3_info(xilinx_desc *desc) +{ + return FPGA_SUCCESS; +} + + +/* ------------------------------------------------------------------------- */ +/* Spartan-II Slave Parallel Generic Implementation */ + +static int spartan3_sp_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume the worst */ + xilinx_spartan3_slave_parallel_fns *fn = desc->iface_fns; + + PRINTF ("%s: start with interface functions @ 0x%p\n", + __FUNCTION__, fn); + + if (fn) { + size_t bytecount = 0; + unsigned char *data = (unsigned char *) buf; + int cookie = desc->cookie; /* make a local copy */ + unsigned long ts; /* timestamp */ + + PRINTF ("%s: Function Table:\n" + "ptr:\t0x%p\n" + "struct: 0x%p\n" + "pre: 0x%p\n" + "pgm:\t0x%p\n" + "init:\t0x%p\n" + "err:\t0x%p\n" + "clk:\t0x%p\n" + "cs:\t0x%p\n" + "wr:\t0x%p\n" + "read data:\t0x%p\n" + "write data:\t0x%p\n" + "busy:\t0x%p\n" + "abort:\t0x%p\n", + "post:\t0x%p\n\n", + __FUNCTION__, &fn, fn, fn->pre, fn->pgm, fn->init, fn->err, + fn->clk, fn->cs, fn->wr, fn->rdata, fn->wdata, fn->busy, + fn->abort, fn->post); + + /* + * This code is designed to emulate the "Express Style" + * Continuous Data Loading in Slave Parallel Mode for + * the Spartan-II Family. + */ +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + printf ("Loading FPGA Device %d...\n", cookie); +#endif + /* + * Run the pre configuration function if there is one. + */ + if (*fn->pre) { + (*fn->pre) (cookie); + } + + /* Establish the initial state */ + (*fn->pgm) (true, true, cookie); /* Assert the program, commit */ + + /* Get ready for the burn */ + CONFIG_FPGA_DELAY (); + (*fn->pgm) (false, true, cookie); /* Deassert the program, commit */ + + ts = get_timer (0); /* get current time */ + /* Now wait for INIT and BUSY to go high */ + do { + CONFIG_FPGA_DELAY (); + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for INIT to clear.\n"); + (*fn->abort) (cookie); /* abort the burn */ + return FPGA_FAIL; + } + } while ((*fn->init) (cookie) && (*fn->busy) (cookie)); + + (*fn->wr) (true, true, cookie); /* Assert write, commit */ + (*fn->cs) (true, true, cookie); /* Assert chip select, commit */ + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + /* Load the data */ + while (bytecount < bsize) { + /* XXX - do we check for an Ctrl-C press in here ??? */ + /* XXX - Check the error bit? */ + + (*fn->wdata) (data[bytecount++], true, cookie); /* write the data */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + +#ifdef CONFIG_SYS_FPGA_CHECK_BUSY + ts = get_timer (0); /* get current time */ + while ((*fn->busy) (cookie)) { + /* XXX - we should have a check in here somewhere to + * make sure we aren't busy forever... */ + + CONFIG_FPGA_DELAY (); + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for BUSY to clear.\n"); + (*fn->abort) (cookie); /* abort the burn */ + return FPGA_FAIL; + } + } +#endif + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (bytecount % (bsize / 40) == 0) + putc ('.'); /* let them know we are alive */ +#endif + } + + CONFIG_FPGA_DELAY (); + (*fn->cs) (false, true, cookie); /* Deassert the chip select */ + (*fn->wr) (false, true, cookie); /* Deassert the write pin */ + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc ('\n'); /* terminate the dotted line */ +#endif + + /* now check for done signal */ + ts = get_timer (0); /* get current time */ + ret_val = FPGA_SUCCESS; + while ((*fn->done) (cookie) == FPGA_FAIL) { + /* XXX - we should have a check in here somewhere to + * make sure we aren't busy forever... */ + + CONFIG_FPGA_DELAY (); + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for DONE to clear.\n"); + (*fn->abort) (cookie); /* abort the burn */ + ret_val = FPGA_FAIL; + break; + } + } + + /* + * Run the post configuration function if there is one. + */ + if (*fn->post) + (*fn->post) (cookie); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (ret_val == FPGA_SUCCESS) + puts ("Done.\n"); + else + puts ("Fail.\n"); +#endif + + } else { + printf ("%s: NULL Interface function table!\n", __FUNCTION__); + } + + return ret_val; +} + +static int spartan3_sp_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume the worst */ + xilinx_spartan3_slave_parallel_fns *fn = desc->iface_fns; + + if (fn) { + unsigned char *data = (unsigned char *) buf; + size_t bytecount = 0; + int cookie = desc->cookie; /* make a local copy */ + + printf ("Starting Dump of FPGA Device %d...\n", cookie); + + (*fn->cs) (true, true, cookie); /* Assert chip select, commit */ + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + /* dump the data */ + while (bytecount < bsize) { + /* XXX - do we check for an Ctrl-C press in here ??? */ + + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + (*fn->rdata) (&(data[bytecount++]), cookie); /* read the data */ +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (bytecount % (bsize / 40) == 0) + putc ('.'); /* let them know we are alive */ +#endif + } + + (*fn->cs) (false, false, cookie); /* Deassert the chip select */ + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc ('\n'); /* terminate the dotted line */ +#endif + puts ("Done.\n"); + + /* XXX - checksum the data? */ + } else { + printf ("%s: NULL Interface function table!\n", __FUNCTION__); + } + + return ret_val; +} + + +/* ------------------------------------------------------------------------- */ + +static int spartan3_ss_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; /* assume the worst */ + xilinx_spartan3_slave_serial_fns *fn = desc->iface_fns; + int i; + unsigned char val; + + PRINTF ("%s: start with interface functions @ 0x%p\n", + __FUNCTION__, fn); + + if (fn) { + size_t bytecount = 0; + unsigned char *data = (unsigned char *) buf; + int cookie = desc->cookie; /* make a local copy */ + unsigned long ts; /* timestamp */ + + PRINTF ("%s: Function Table:\n" + "ptr:\t0x%p\n" + "struct: 0x%p\n" + "pgm:\t0x%p\n" + "init:\t0x%p\n" + "clk:\t0x%p\n" + "wr:\t0x%p\n" + "done:\t0x%p\n\n", + __FUNCTION__, &fn, fn, fn->pgm, fn->init, + fn->clk, fn->wr, fn->done); +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + printf ("Loading FPGA Device %d...\n", cookie); +#endif + + /* + * Run the pre configuration function if there is one. + */ + if (*fn->pre) { + (*fn->pre) (cookie); + } + + /* Establish the initial state */ + (*fn->pgm) (true, true, cookie); /* Assert the program, commit */ + + /* Wait for INIT state (init low) */ + ts = get_timer (0); /* get current time */ + do { + CONFIG_FPGA_DELAY (); + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for INIT to start.\n"); + if (*fn->abort) + (*fn->abort) (cookie); + return FPGA_FAIL; + } + } while (!(*fn->init) (cookie)); + + /* Get ready for the burn */ + CONFIG_FPGA_DELAY (); + (*fn->pgm) (false, true, cookie); /* Deassert the program, commit */ + + ts = get_timer (0); /* get current time */ + /* Now wait for INIT to go high */ + do { + CONFIG_FPGA_DELAY (); + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for INIT to clear.\n"); + if (*fn->abort) + (*fn->abort) (cookie); + return FPGA_FAIL; + } + } while ((*fn->init) (cookie)); + + /* Load the data */ + if(*fn->bwr) + (*fn->bwr) (data, bsize, true, cookie); + else { + while (bytecount < bsize) { + + /* Xilinx detects an error if INIT goes low (active) + while DONE is low (inactive) */ + if ((*fn->done) (cookie) == 0 && (*fn->init) (cookie)) { + puts ("** CRC error during FPGA load.\n"); + if (*fn->abort) + (*fn->abort) (cookie); + return (FPGA_FAIL); + } + val = data [bytecount ++]; + i = 8; + do { + /* Deassert the clock */ + (*fn->clk) (false, true, cookie); + CONFIG_FPGA_DELAY (); + /* Write data */ + (*fn->wr) ((val & 0x80), true, cookie); + CONFIG_FPGA_DELAY (); + /* Assert the clock */ + (*fn->clk) (true, true, cookie); + CONFIG_FPGA_DELAY (); + val <<= 1; + i --; + } while (i > 0); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (bytecount % (bsize / 40) == 0) + putc ('.'); /* let them know we are alive */ +#endif + } + } + + CONFIG_FPGA_DELAY (); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc ('\n'); /* terminate the dotted line */ +#endif + + /* now check for done signal */ + ts = get_timer (0); /* get current time */ + ret_val = FPGA_SUCCESS; + (*fn->wr) (true, true, cookie); + + while (! (*fn->done) (cookie)) { + /* XXX - we should have a check in here somewhere to + * make sure we aren't busy forever... */ + + CONFIG_FPGA_DELAY (); + (*fn->clk) (false, true, cookie); /* Deassert the clock pin */ + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); /* Assert the clock pin */ + + putc ('*'); + + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ + puts ("** Timeout waiting for DONE to clear.\n"); + ret_val = FPGA_FAIL; + break; + } + } + putc ('\n'); /* terminate the dotted line */ + + /* + * Run the post configuration function if there is one. + */ + if (*fn->post) + (*fn->post) (cookie); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (ret_val == FPGA_SUCCESS) + puts ("Done.\n"); + else + puts ("Fail.\n"); +#endif + + } else { + printf ("%s: NULL Interface function table!\n", __FUNCTION__); + } + + return ret_val; +} + +static int spartan3_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + /* Readback is only available through the Slave Parallel and */ + /* boundary-scan interfaces. */ + printf ("%s: Slave Serial Dumping is unavailable\n", + __FUNCTION__); + return FPGA_FAIL; +} + +struct xilinx_fpga_op spartan3_op = { + .load = spartan3_load, + .dump = spartan3_dump, + .info = spartan3_info, +}; diff --git a/qemu/roms/u-boot/drivers/fpga/stratixII.c b/qemu/roms/u-boot/drivers/fpga/stratixII.c new file mode 100644 index 000000000..820d016a1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/stratixII.c @@ -0,0 +1,190 @@ +/* + * (C) Copyright 2007 + * Eran Liberty, Extricom , eran.liberty@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include /* core U-Boot definitions */ +#include + +int StratixII_ps_fpp_load (Altera_desc * desc, void *buf, size_t bsize, + int isSerial, int isSecure); +int StratixII_ps_fpp_dump (Altera_desc * desc, void *buf, size_t bsize); + +/****************************************************************/ +/* Stratix II Generic Implementation */ +int StratixII_load (Altera_desc * desc, void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case passive_serial: + ret_val = StratixII_ps_fpp_load (desc, buf, bsize, 1, 0); + break; + case fast_passive_parallel: + ret_val = StratixII_ps_fpp_load (desc, buf, bsize, 0, 0); + break; + case fast_passive_parallel_security: + ret_val = StratixII_ps_fpp_load (desc, buf, bsize, 0, 1); + break; + + /* Add new interface types here */ + default: + printf ("%s: Unsupported interface type, %d\n", __FUNCTION__, + desc->iface); + } + return ret_val; +} + +int StratixII_dump (Altera_desc * desc, void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case passive_serial: + case fast_passive_parallel: + case fast_passive_parallel_security: + ret_val = StratixII_ps_fpp_dump (desc, buf, bsize); + break; + /* Add new interface types here */ + default: + printf ("%s: Unsupported interface type, %d\n", __FUNCTION__, + desc->iface); + } + return ret_val; +} + +int StratixII_info (Altera_desc * desc) +{ + return FPGA_SUCCESS; +} + +int StratixII_ps_fpp_dump (Altera_desc * desc, void *buf, size_t bsize) +{ + printf ("Stratix II Fast Passive Parallel dump is not implemented\n"); + return FPGA_FAIL; +} + +int StratixII_ps_fpp_load (Altera_desc * desc, void *buf, size_t bsize, + int isSerial, int isSecure) +{ + altera_board_specific_func *fns; + int cookie; + int ret_val = FPGA_FAIL; + int bytecount; + char *buff = buf; + int i; + + if (!desc) { + printf ("%s(%d) Altera_desc missing\n", __FUNCTION__, __LINE__); + return FPGA_FAIL; + } + if (!buff) { + printf ("%s(%d) buffer is missing\n", __FUNCTION__, __LINE__); + return FPGA_FAIL; + } + if (!bsize) { + printf ("%s(%d) size is zero\n", __FUNCTION__, __LINE__); + return FPGA_FAIL; + } + if (!desc->iface_fns) { + printf + ("%s(%d) Altera_desc function interface table is missing\n", + __FUNCTION__, __LINE__); + return FPGA_FAIL; + } + fns = (altera_board_specific_func *) (desc->iface_fns); + cookie = desc->cookie; + + if (! + (fns->config && fns->status && fns->done && fns->data + && fns->abort)) { + printf + ("%s(%d) Missing some function in the function interface table\n", + __FUNCTION__, __LINE__); + return FPGA_FAIL; + } + + /* 1. give board specific a chance to do anything before we start */ + if (fns->pre) { + if ((ret_val = fns->pre (cookie)) < 0) { + return ret_val; + } + } + + /* from this point on we must fail gracfully by calling lower layer abort */ + + /* 2. Strat burn cycle by deasserting config for t_CFG and waiting t_CF2CK after reaserted */ + fns->config (0, 1, cookie); + udelay (5); /* nCONFIG low pulse width 2usec */ + fns->config (1, 1, cookie); + udelay (100); /* nCONFIG high to first rising edge on DCLK */ + + /* 3. Start the Data cycle with clk deasserted */ + bytecount = 0; + fns->clk (0, 1, cookie); + + printf ("loading to fpga "); + while (bytecount < bsize) { + /* 3.1 check stratix has not signaled us an error */ + if (fns->status (cookie) != 1) { + printf + ("\n%s(%d) Stratix failed (byte transfered till failure 0x%x)\n", + __FUNCTION__, __LINE__, bytecount); + fns->abort (cookie); + return FPGA_FAIL; + } + if (isSerial) { + int i; + uint8_t data = buff[bytecount++]; + for (i = 0; i < 8; i++) { + /* 3.2(ps) put data on the bus */ + fns->data ((data >> i) & 1, 1, cookie); + + /* 3.3(ps) clock once */ + fns->clk (1, 1, cookie); + fns->clk (0, 1, cookie); + } + } else { + /* 3.2(fpp) put data on the bus */ + fns->data (buff[bytecount++], 1, cookie); + + /* 3.3(fpp) clock once */ + fns->clk (1, 1, cookie); + fns->clk (0, 1, cookie); + + /* 3.4(fpp) for secure cycle push 3 more clocks */ + for (i = 0; isSecure && i < 3; i++) { + fns->clk (1, 1, cookie); + fns->clk (0, 1, cookie); + } + } + + /* 3.5 while clk is deasserted it is safe to print some progress indication */ + if ((bytecount % (bsize / 100)) == 0) { + printf ("\b\b\b%02d\%", bytecount * 100 / bsize); + } + } + + /* 4. Set one last clock and check conf done signal */ + fns->clk (1, 1, cookie); + udelay (100); + if (!fns->done (cookie)) { + printf (" error!.\n"); + fns->abort (cookie); + return FPGA_FAIL; + } else { + printf ("\b\b\b done.\n"); + } + + /* 5. call lower layer post configuration */ + if (fns->post) { + if ((ret_val = fns->post (cookie)) < 0) { + fns->abort (cookie); + return ret_val; + } + } + + return FPGA_SUCCESS; +} diff --git a/qemu/roms/u-boot/drivers/fpga/virtex2.c b/qemu/roms/u-boot/drivers/fpga/virtex2.c new file mode 100644 index 000000000..e092147ed --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/virtex2.c @@ -0,0 +1,425 @@ +/* + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * Keith Outwater, keith_outwater@mvis.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Configuration support for Xilinx Virtex2 devices. Based + * on spartan2.c (Rich Ireland, rireland@enterasys.com). + */ + +#include +#include + +#if 0 +#define FPGA_DEBUG +#endif + +#ifdef FPGA_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +/* + * If the SelectMap interface can be overrun by the processor, define + * CONFIG_SYS_FPGA_CHECK_BUSY and/or CONFIG_FPGA_DELAY in the board configuration + * file and add board-specific support for checking BUSY status. By default, + * assume that the SelectMap interface cannot be overrun. + */ +#ifndef CONFIG_SYS_FPGA_CHECK_BUSY +#undef CONFIG_SYS_FPGA_CHECK_BUSY +#endif + +#ifndef CONFIG_FPGA_DELAY +#define CONFIG_FPGA_DELAY() +#endif + +#ifndef CONFIG_SYS_FPGA_PROG_FEEDBACK +#define CONFIG_SYS_FPGA_PROG_FEEDBACK +#endif + +/* + * Don't allow config cycle to be interrupted + */ +#ifndef CONFIG_SYS_FPGA_CHECK_CTRLC +#undef CONFIG_SYS_FPGA_CHECK_CTRLC +#endif + +/* + * Check for errors during configuration by default + */ +#ifndef CONFIG_SYS_FPGA_CHECK_ERROR +#define CONFIG_SYS_FPGA_CHECK_ERROR +#endif + +/* + * The default timeout in mS for INIT_B to deassert after PROG_B has + * been deasserted. Per the latest Virtex II Handbook (page 347), the + * max time from PORG_B deassertion to INIT_B deassertion is 4uS per + * data frame for the XC2V8000. The XC2V8000 has 2860 data frames + * which yields 11.44 mS. So let's make it bigger in order to handle + * an XC2V1000, if anyone can ever get ahold of one. + */ +#ifndef CONFIG_SYS_FPGA_WAIT_INIT +#define CONFIG_SYS_FPGA_WAIT_INIT CONFIG_SYS_HZ/2 /* 500 ms */ +#endif + +/* + * The default timeout for waiting for BUSY to deassert during configuration. + * This is normally not necessary since for most reasonable configuration + * clock frequencies (i.e. 66 MHz or less), BUSY monitoring is unnecessary. + */ +#ifndef CONFIG_SYS_FPGA_WAIT_BUSY +#define CONFIG_SYS_FPGA_WAIT_BUSY CONFIG_SYS_HZ/200 /* 5 ms*/ +#endif + +/* Default timeout for waiting for FPGA to enter operational mode after + * configuration data has been written. + */ +#ifndef CONFIG_SYS_FPGA_WAIT_CONFIG +#define CONFIG_SYS_FPGA_WAIT_CONFIG CONFIG_SYS_HZ/5 /* 200 ms */ +#endif + +static int virtex2_ssm_load(xilinx_desc *desc, const void *buf, size_t bsize); +static int virtex2_ssm_dump(xilinx_desc *desc, const void *buf, size_t bsize); + +static int virtex2_ss_load(xilinx_desc *desc, const void *buf, size_t bsize); +static int virtex2_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize); + +static int virtex2_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case slave_serial: + PRINTF ("%s: Launching Slave Serial Load\n", __FUNCTION__); + ret_val = virtex2_ss_load(desc, buf, bsize); + break; + + case slave_selectmap: + PRINTF ("%s: Launching Slave Parallel Load\n", __FUNCTION__); + ret_val = virtex2_ssm_load(desc, buf, bsize); + break; + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + return ret_val; +} + +static int virtex2_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + + switch (desc->iface) { + case slave_serial: + PRINTF ("%s: Launching Slave Serial Dump\n", __FUNCTION__); + ret_val = virtex2_ss_dump(desc, buf, bsize); + break; + + case slave_parallel: + PRINTF ("%s: Launching Slave Parallel Dump\n", __FUNCTION__); + ret_val = virtex2_ssm_dump(desc, buf, bsize); + break; + + default: + printf ("%s: Unsupported interface type, %d\n", + __FUNCTION__, desc->iface); + } + return ret_val; +} + +static int virtex2_info(xilinx_desc *desc) +{ + return FPGA_SUCCESS; +} + +/* + * Virtex-II Slave SelectMap configuration loader. Configuration via + * SelectMap is as follows: + * 1. Set the FPGA's PROG_B line low. + * 2. Set the FPGA's PROG_B line high. Wait for INIT_B to go high. + * 3. Write data to the SelectMap port. If INIT_B goes low at any time + * this process, a configuration error (most likely CRC failure) has + * ocurred. At this point a status word may be read from the + * SelectMap interface to determine the source of the problem (You + * could, for instance, put this in your 'abort' function handler). + * 4. After all data has been written, test the state of the FPGA + * INIT_B and DONE lines. If both are high, configuration has + * succeeded. Congratulations! + */ +static int virtex2_ssm_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + xilinx_virtex2_slave_selectmap_fns *fn = desc->iface_fns; + + PRINTF ("%s:%d: Start with interface functions @ 0x%p\n", + __FUNCTION__, __LINE__, fn); + + if (fn) { + size_t bytecount = 0; + unsigned char *data = (unsigned char *) buf; + int cookie = desc->cookie; + unsigned long ts; + + /* Gotta split this one up (so the stack won't blow??) */ + PRINTF ("%s:%d: Function Table:\n" + " base 0x%p\n" + " struct 0x%p\n" + " pre 0x%p\n" + " prog 0x%p\n" + " init 0x%p\n" + " error 0x%p\n", + __FUNCTION__, __LINE__, + &fn, fn, fn->pre, fn->pgm, fn->init, fn->err); + PRINTF (" clock 0x%p\n" + " cs 0x%p\n" + " write 0x%p\n" + " rdata 0x%p\n" + " wdata 0x%p\n" + " busy 0x%p\n" + " abort 0x%p\n" + " post 0x%p\n\n", + fn->clk, fn->cs, fn->wr, fn->rdata, fn->wdata, + fn->busy, fn->abort, fn->post); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + printf ("Initializing FPGA Device %d...\n", cookie); +#endif + /* + * Run the pre configuration function if there is one. + */ + if (*fn->pre) { + (*fn->pre) (cookie); + } + + /* + * Assert the program line. The minimum pulse width for + * Virtex II devices is 300 nS (Tprogram parameter in datasheet). + * There is no maximum value for the pulse width. Check to make + * sure that INIT_B goes low after assertion of PROG_B + */ + (*fn->pgm) (true, true, cookie); + udelay (10); + ts = get_timer (0); + do { + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT_INIT) { + printf ("%s:%d: ** Timeout after %d ticks waiting for INIT" + " to assert.\n", __FUNCTION__, __LINE__, + CONFIG_SYS_FPGA_WAIT_INIT); + (*fn->abort) (cookie); + return FPGA_FAIL; + } + } while (!(*fn->init) (cookie)); + + (*fn->pgm) (false, true, cookie); + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); + + /* + * Start a timer and wait for INIT_B to go high + */ + ts = get_timer (0); + do { + CONFIG_FPGA_DELAY (); + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT_INIT) { + printf ("%s:%d: ** Timeout after %d ticks waiting for INIT" + " to deassert.\n", __FUNCTION__, __LINE__, + CONFIG_SYS_FPGA_WAIT_INIT); + (*fn->abort) (cookie); + return FPGA_FAIL; + } + } while ((*fn->init) (cookie) && (*fn->busy) (cookie)); + + (*fn->wr) (true, true, cookie); + (*fn->cs) (true, true, cookie); + + udelay (10000); + + /* + * Load the data byte by byte + */ + while (bytecount < bsize) { +#ifdef CONFIG_SYS_FPGA_CHECK_CTRLC + if (ctrlc ()) { + (*fn->abort) (cookie); + return FPGA_FAIL; + } +#endif + + if ((*fn->done) (cookie) == FPGA_SUCCESS) { + PRINTF ("%s:%d:done went active early, bytecount = %d\n", + __FUNCTION__, __LINE__, bytecount); + break; + } + +#ifdef CONFIG_SYS_FPGA_CHECK_ERROR + if ((*fn->init) (cookie)) { + printf ("\n%s:%d: ** Error: INIT asserted during" + " configuration\n", __FUNCTION__, __LINE__); + printf ("%d = buffer offset, %d = buffer size\n", + bytecount, bsize); + (*fn->abort) (cookie); + return FPGA_FAIL; + } +#endif + + (*fn->wdata) (data[bytecount++], true, cookie); + CONFIG_FPGA_DELAY (); + + /* + * Cycle the clock pin + */ + (*fn->clk) (false, true, cookie); + CONFIG_FPGA_DELAY (); + (*fn->clk) (true, true, cookie); + +#ifdef CONFIG_SYS_FPGA_CHECK_BUSY + ts = get_timer (0); + while ((*fn->busy) (cookie)) { + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT_BUSY) { + printf ("%s:%d: ** Timeout after %d ticks waiting for" + " BUSY to deassert\n", + __FUNCTION__, __LINE__, CONFIG_SYS_FPGA_WAIT_BUSY); + (*fn->abort) (cookie); + return FPGA_FAIL; + } + } +#endif + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (bytecount % (bsize / 40) == 0) + putc ('.'); +#endif + } + + /* + * Finished writing the data; deassert FPGA CS_B and WRITE_B signals. + */ + CONFIG_FPGA_DELAY (); + (*fn->cs) (false, true, cookie); + (*fn->wr) (false, true, cookie); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc ('\n'); +#endif + + /* + * Check for successful configuration. FPGA INIT_B and DONE should + * both be high upon successful configuration. + */ + ts = get_timer (0); + ret_val = FPGA_SUCCESS; + while (((*fn->done) (cookie) == FPGA_FAIL) || (*fn->init) (cookie)) { + if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT_CONFIG) { + printf ("%s:%d: ** Timeout after %d ticks waiting for DONE to" + "assert and INIT to deassert\n", + __FUNCTION__, __LINE__, CONFIG_SYS_FPGA_WAIT_CONFIG); + (*fn->abort) (cookie); + ret_val = FPGA_FAIL; + break; + } + } + + if (ret_val == FPGA_SUCCESS) { +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + printf ("Initialization of FPGA device %d complete\n", cookie); +#endif + /* + * Run the post configuration function if there is one. + */ + if (*fn->post) { + (*fn->post) (cookie); + } + } else { +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + printf ("** Initialization of FPGA device %d FAILED\n", + cookie); +#endif + } + } else { + printf ("%s:%d: NULL Interface function table!\n", + __FUNCTION__, __LINE__); + } + return ret_val; +} + +/* + * Read the FPGA configuration data + */ +static int virtex2_ssm_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + int ret_val = FPGA_FAIL; + xilinx_virtex2_slave_selectmap_fns *fn = desc->iface_fns; + + if (fn) { + unsigned char *data = (unsigned char *) buf; + size_t bytecount = 0; + int cookie = desc->cookie; + + printf ("Starting Dump of FPGA Device %d...\n", cookie); + + (*fn->cs) (true, true, cookie); + (*fn->clk) (true, true, cookie); + + while (bytecount < bsize) { +#ifdef CONFIG_SYS_FPGA_CHECK_CTRLC + if (ctrlc ()) { + (*fn->abort) (cookie); + return FPGA_FAIL; + } +#endif + /* + * Cycle the clock and read the data + */ + (*fn->clk) (false, true, cookie); + (*fn->clk) (true, true, cookie); + (*fn->rdata) (&(data[bytecount++]), cookie); +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + if (bytecount % (bsize / 40) == 0) + putc ('.'); +#endif + } + + /* + * Deassert CS_B and cycle the clock to deselect the device. + */ + (*fn->cs) (false, false, cookie); + (*fn->clk) (false, true, cookie); + (*fn->clk) (true, true, cookie); + +#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK + putc ('\n'); +#endif + puts ("Done.\n"); + } else { + printf ("%s:%d: NULL Interface function table!\n", + __FUNCTION__, __LINE__); + } + return ret_val; +} + +static int virtex2_ss_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + printf ("%s: Slave Serial Loading is unsupported\n", __FUNCTION__); + return FPGA_FAIL; +} + +static int virtex2_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + printf ("%s: Slave Serial Dumping is unsupported\n", __FUNCTION__); + return FPGA_FAIL; +} + +/* vim: set ts=4 tw=78: */ + +struct xilinx_fpga_op virtex2_op = { + .load = virtex2_load, + .dump = virtex2_dump, + .info = virtex2_info, +}; diff --git a/qemu/roms/u-boot/drivers/fpga/xilinx.c b/qemu/roms/u-boot/drivers/fpga/xilinx.c new file mode 100644 index 000000000..8837f5c12 --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/xilinx.c @@ -0,0 +1,249 @@ +/* + * (C) Copyright 2012-2013, Xilinx, Michal Simek + * + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * Keith Outwater, keith_outwater@mvis.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Xilinx FPGA support + */ + +#include +#include +#include +#include +#include +#include + +/* Local Static Functions */ +static int xilinx_validate(xilinx_desc *desc, char *fn); + +/* ------------------------------------------------------------------------- */ + +int fpga_loadbitstream(int devnum, char *fpgadata, size_t size) +{ + unsigned int length; + unsigned int swapsize; + char buffer[80]; + unsigned char *dataptr; + unsigned int i; + const fpga_desc *desc; + xilinx_desc *xdesc; + + dataptr = (unsigned char *)fpgadata; + /* Find out fpga_description */ + desc = fpga_validate(devnum, dataptr, 0, (char *)__func__); + /* Assign xilinx device description */ + xdesc = desc->devdesc; + + /* skip the first bytes of the bitsteam, their meaning is unknown */ + length = (*dataptr << 8) + *(dataptr + 1); + dataptr += 2; + dataptr += length; + + /* get design name (identifier, length, string) */ + length = (*dataptr << 8) + *(dataptr + 1); + dataptr += 2; + if (*dataptr++ != 0x61) { + debug("%s: Design name id not recognized in bitstream\n", + __func__); + return FPGA_FAIL; + } + + length = (*dataptr << 8) + *(dataptr + 1); + dataptr += 2; + for (i = 0; i < length; i++) + buffer[i] = *dataptr++; + + printf(" design filename = \"%s\"\n", buffer); + + /* get part number (identifier, length, string) */ + if (*dataptr++ != 0x62) { + printf("%s: Part number id not recognized in bitstream\n", + __func__); + return FPGA_FAIL; + } + + length = (*dataptr << 8) + *(dataptr + 1); + dataptr += 2; + for (i = 0; i < length; i++) + buffer[i] = *dataptr++; + + if (xdesc->name) { + i = strncmp(buffer, xdesc->name, strlen(xdesc->name)); + if (i) { + printf("%s: Wrong bitstream ID for this device\n", + __func__); + printf("%s: Bitstream ID %s, current device ID %d/%s\n", + __func__, buffer, devnum, xdesc->name); + return FPGA_FAIL; + } + } else { + printf("%s: Please fill correct device ID to xilinx_desc\n", + __func__); + } + printf(" part number = \"%s\"\n", buffer); + + /* get date (identifier, length, string) */ + if (*dataptr++ != 0x63) { + printf("%s: Date identifier not recognized in bitstream\n", + __func__); + return FPGA_FAIL; + } + + length = (*dataptr << 8) + *(dataptr+1); + dataptr += 2; + for (i = 0; i < length; i++) + buffer[i] = *dataptr++; + printf(" date = \"%s\"\n", buffer); + + /* get time (identifier, length, string) */ + if (*dataptr++ != 0x64) { + printf("%s: Time identifier not recognized in bitstream\n", + __func__); + return FPGA_FAIL; + } + + length = (*dataptr << 8) + *(dataptr+1); + dataptr += 2; + for (i = 0; i < length; i++) + buffer[i] = *dataptr++; + printf(" time = \"%s\"\n", buffer); + + /* get fpga data length (identifier, length) */ + if (*dataptr++ != 0x65) { + printf("%s: Data length id not recognized in bitstream\n", + __func__); + return FPGA_FAIL; + } + swapsize = ((unsigned int) *dataptr << 24) + + ((unsigned int) *(dataptr + 1) << 16) + + ((unsigned int) *(dataptr + 2) << 8) + + ((unsigned int) *(dataptr + 3)); + dataptr += 4; + printf(" bytes in bitstream = %d\n", swapsize); + + return fpga_load(devnum, dataptr, swapsize); +} + +int xilinx_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + if (!xilinx_validate (desc, (char *)__FUNCTION__)) { + printf ("%s: Invalid device descriptor\n", __FUNCTION__); + return FPGA_FAIL; + } + + return desc->operations->load(desc, buf, bsize); +} + +int xilinx_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + if (!xilinx_validate (desc, (char *)__FUNCTION__)) { + printf ("%s: Invalid device descriptor\n", __FUNCTION__); + return FPGA_FAIL; + } + + return desc->operations->dump(desc, buf, bsize); +} + +int xilinx_info(xilinx_desc *desc) +{ + int ret_val = FPGA_FAIL; + + if (xilinx_validate (desc, (char *)__FUNCTION__)) { + printf ("Family: \t"); + switch (desc->family) { + case xilinx_spartan2: + printf ("Spartan-II\n"); + break; + case xilinx_spartan3: + printf ("Spartan-III\n"); + break; + case xilinx_virtex2: + printf ("Virtex-II\n"); + break; + case xilinx_zynq: + printf("Zynq PL\n"); + break; + /* Add new family types here */ + default: + printf ("Unknown family type, %d\n", desc->family); + } + + printf ("Interface type:\t"); + switch (desc->iface) { + case slave_serial: + printf ("Slave Serial\n"); + break; + case master_serial: /* Not used */ + printf ("Master Serial\n"); + break; + case slave_parallel: + printf ("Slave Parallel\n"); + break; + case jtag_mode: /* Not used */ + printf ("JTAG Mode\n"); + break; + case slave_selectmap: + printf ("Slave SelectMap Mode\n"); + break; + case master_selectmap: + printf ("Master SelectMap Mode\n"); + break; + case devcfg: + printf("Device configuration interface (Zynq)\n"); + break; + /* Add new interface types here */ + default: + printf ("Unsupported interface type, %d\n", desc->iface); + } + + printf ("Device Size: \t%d bytes\n" + "Cookie: \t0x%x (%d)\n", + desc->size, desc->cookie, desc->cookie); + if (desc->name) + printf("Device name: \t%s\n", desc->name); + + if (desc->iface_fns) { + printf ("Device Function Table @ 0x%p\n", desc->iface_fns); + desc->operations->info(desc); + } else + printf ("No Device Function Table.\n"); + + ret_val = FPGA_SUCCESS; + } else { + printf ("%s: Invalid device descriptor\n", __FUNCTION__); + } + + return ret_val; +} + +/* ------------------------------------------------------------------------- */ + +static int xilinx_validate(xilinx_desc *desc, char *fn) +{ + int ret_val = false; + + if (desc) { + if ((desc->family > min_xilinx_type) && + (desc->family < max_xilinx_type)) { + if ((desc->iface > min_xilinx_iface_type) && + (desc->iface < max_xilinx_iface_type)) { + if (desc->size) { + ret_val = true; + } else + printf ("%s: NULL part size\n", fn); + } else + printf ("%s: Invalid Interface type, %d\n", + fn, desc->iface); + } else + printf ("%s: Invalid family type, %d\n", fn, desc->family); + } else + printf ("%s: NULL descriptor!\n", fn); + + return ret_val; +} diff --git a/qemu/roms/u-boot/drivers/fpga/zynqpl.c b/qemu/roms/u-boot/drivers/fpga/zynqpl.c new file mode 100644 index 000000000..c066f21d7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/fpga/zynqpl.c @@ -0,0 +1,416 @@ +/* + * (C) Copyright 2012-2013, Xilinx, Michal Simek + * + * (C) Copyright 2012 + * Joe Hershberger + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#define DEVCFG_CTRL_PCFG_PROG_B 0x40000000 +#define DEVCFG_ISR_FATAL_ERROR_MASK 0x00740040 +#define DEVCFG_ISR_ERROR_FLAGS_MASK 0x00340840 +#define DEVCFG_ISR_RX_FIFO_OV 0x00040000 +#define DEVCFG_ISR_DMA_DONE 0x00002000 +#define DEVCFG_ISR_PCFG_DONE 0x00000004 +#define DEVCFG_STATUS_DMA_CMD_Q_F 0x80000000 +#define DEVCFG_STATUS_DMA_CMD_Q_E 0x40000000 +#define DEVCFG_STATUS_DMA_DONE_CNT_MASK 0x30000000 +#define DEVCFG_STATUS_PCFG_INIT 0x00000010 +#define DEVCFG_MCTRL_PCAP_LPBK 0x00000010 +#define DEVCFG_MCTRL_RFIFO_FLUSH 0x00000002 +#define DEVCFG_MCTRL_WFIFO_FLUSH 0x00000001 + +#ifndef CONFIG_SYS_FPGA_WAIT +#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/100 /* 10 ms */ +#endif + +#ifndef CONFIG_SYS_FPGA_PROG_TIME +#define CONFIG_SYS_FPGA_PROG_TIME (CONFIG_SYS_HZ * 4) /* 4 s */ +#endif + +static int zynq_info(xilinx_desc *desc) +{ + return FPGA_SUCCESS; +} + +#define DUMMY_WORD 0xffffffff + +/* Xilinx binary format header */ +static const u32 bin_format[] = { + DUMMY_WORD, /* Dummy words */ + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + 0x000000bb, /* Sync word */ + 0x11220044, /* Sync word */ + DUMMY_WORD, + DUMMY_WORD, + 0xaa995566, /* Sync word */ +}; + +#define SWAP_NO 1 +#define SWAP_DONE 2 + +/* + * Load the whole word from unaligned buffer + * Keep in your mind that it is byte loading on little-endian system + */ +static u32 load_word(const void *buf, u32 swap) +{ + u32 word = 0; + u8 *bitc = (u8 *)buf; + int p; + + if (swap == SWAP_NO) { + for (p = 0; p < 4; p++) { + word <<= 8; + word |= bitc[p]; + } + } else { + for (p = 3; p >= 0; p--) { + word <<= 8; + word |= bitc[p]; + } + } + + return word; +} + +static u32 check_header(const void *buf) +{ + u32 i, pattern; + int swap = SWAP_NO; + u32 *test = (u32 *)buf; + + debug("%s: Let's check bitstream header\n", __func__); + + /* Checking that passing bin is not a bitstream */ + for (i = 0; i < ARRAY_SIZE(bin_format); i++) { + pattern = load_word(&test[i], swap); + + /* + * Bitstreams in binary format are swapped + * compare to regular bistream. + * Do not swap dummy word but if swap is done assume + * that parsing buffer is binary format + */ + if ((__swab32(pattern) != DUMMY_WORD) && + (__swab32(pattern) == bin_format[i])) { + pattern = __swab32(pattern); + swap = SWAP_DONE; + debug("%s: data swapped - let's swap\n", __func__); + } + + debug("%s: %d/%x: pattern %x/%x bin_format\n", __func__, i, + (u32)&test[i], pattern, bin_format[i]); + if (pattern != bin_format[i]) { + debug("%s: Bitstream is not recognized\n", __func__); + return 0; + } + } + debug("%s: Found bitstream header at %x %s swapinng\n", __func__, + (u32)buf, swap == SWAP_NO ? "without" : "with"); + + return swap; +} + +static void *check_data(u8 *buf, size_t bsize, u32 *swap) +{ + u32 word, p = 0; /* possition */ + + /* Because buf doesn't need to be aligned let's read it by chars */ + for (p = 0; p < bsize; p++) { + word = load_word(&buf[p], SWAP_NO); + debug("%s: word %x %x/%x\n", __func__, word, p, (u32)&buf[p]); + + /* Find the first bitstream dummy word */ + if (word == DUMMY_WORD) { + debug("%s: Found dummy word at position %x/%x\n", + __func__, p, (u32)&buf[p]); + *swap = check_header(&buf[p]); + if (*swap) { + /* FIXME add full bitstream checking here */ + return &buf[p]; + } + } + /* Loop can be huge - support CTRL + C */ + if (ctrlc()) + return NULL; + } + return NULL; +} + +static int zynq_dma_transfer(u32 srcbuf, u32 srclen, u32 dstbuf, u32 dstlen) +{ + unsigned long ts; + u32 isr_status; + + /* Set up the transfer */ + writel((u32)srcbuf, &devcfg_base->dma_src_addr); + writel(dstbuf, &devcfg_base->dma_dst_addr); + writel(srclen, &devcfg_base->dma_src_len); + writel(dstlen, &devcfg_base->dma_dst_len); + + isr_status = readl(&devcfg_base->int_sts); + + /* Polling the PCAP_INIT status for Set */ + ts = get_timer(0); + while (!(isr_status & DEVCFG_ISR_DMA_DONE)) { + if (isr_status & DEVCFG_ISR_ERROR_FLAGS_MASK) { + debug("%s: Error: isr = 0x%08X\n", __func__, + isr_status); + debug("%s: Write count = 0x%08X\n", __func__, + readl(&devcfg_base->write_count)); + debug("%s: Read count = 0x%08X\n", __func__, + readl(&devcfg_base->read_count)); + + return FPGA_FAIL; + } + if (get_timer(ts) > CONFIG_SYS_FPGA_PROG_TIME) { + printf("%s: Timeout wait for DMA to complete\n", + __func__); + return FPGA_FAIL; + } + isr_status = readl(&devcfg_base->int_sts); + } + + debug("%s: DMA transfer is done\n", __func__); + + /* Clear out the DMA status */ + writel(DEVCFG_ISR_DMA_DONE, &devcfg_base->int_sts); + + return FPGA_SUCCESS; +} + +static int zynq_dma_xfer_init(u32 partialbit) +{ + u32 status, control, isr_status; + unsigned long ts; + + /* Clear loopback bit */ + clrbits_le32(&devcfg_base->mctrl, DEVCFG_MCTRL_PCAP_LPBK); + + if (!partialbit) { + zynq_slcr_devcfg_disable(); + + /* Setting PCFG_PROG_B signal to high */ + control = readl(&devcfg_base->ctrl); + writel(control | DEVCFG_CTRL_PCFG_PROG_B, &devcfg_base->ctrl); + /* Setting PCFG_PROG_B signal to low */ + writel(control & ~DEVCFG_CTRL_PCFG_PROG_B, &devcfg_base->ctrl); + + /* Polling the PCAP_INIT status for Reset */ + ts = get_timer(0); + while (readl(&devcfg_base->status) & DEVCFG_STATUS_PCFG_INIT) { + if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT) { + printf("%s: Timeout wait for INIT to clear\n", + __func__); + return FPGA_FAIL; + } + } + + /* Setting PCFG_PROG_B signal to high */ + writel(control | DEVCFG_CTRL_PCFG_PROG_B, &devcfg_base->ctrl); + + /* Polling the PCAP_INIT status for Set */ + ts = get_timer(0); + while (!(readl(&devcfg_base->status) & + DEVCFG_STATUS_PCFG_INIT)) { + if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT) { + printf("%s: Timeout wait for INIT to set\n", + __func__); + return FPGA_FAIL; + } + } + } + + isr_status = readl(&devcfg_base->int_sts); + + /* Clear it all, so if Boot ROM comes back, it can proceed */ + writel(0xFFFFFFFF, &devcfg_base->int_sts); + + if (isr_status & DEVCFG_ISR_FATAL_ERROR_MASK) { + debug("%s: Fatal errors in PCAP 0x%X\n", __func__, isr_status); + + /* If RX FIFO overflow, need to flush RX FIFO first */ + if (isr_status & DEVCFG_ISR_RX_FIFO_OV) { + writel(DEVCFG_MCTRL_RFIFO_FLUSH, &devcfg_base->mctrl); + writel(0xFFFFFFFF, &devcfg_base->int_sts); + } + return FPGA_FAIL; + } + + status = readl(&devcfg_base->status); + + debug("%s: Status = 0x%08X\n", __func__, status); + + if (status & DEVCFG_STATUS_DMA_CMD_Q_F) { + debug("%s: Error: device busy\n", __func__); + return FPGA_FAIL; + } + + debug("%s: Device ready\n", __func__); + + if (!(status & DEVCFG_STATUS_DMA_CMD_Q_E)) { + if (!(readl(&devcfg_base->int_sts) & DEVCFG_ISR_DMA_DONE)) { + /* Error state, transfer cannot occur */ + debug("%s: ISR indicates error\n", __func__); + return FPGA_FAIL; + } else { + /* Clear out the status */ + writel(DEVCFG_ISR_DMA_DONE, &devcfg_base->int_sts); + } + } + + if (status & DEVCFG_STATUS_DMA_DONE_CNT_MASK) { + /* Clear the count of completed DMA transfers */ + writel(DEVCFG_STATUS_DMA_DONE_CNT_MASK, &devcfg_base->status); + } + + return FPGA_SUCCESS; +} + +static u32 *zynq_align_dma_buffer(u32 *buf, u32 len, u32 swap) +{ + u32 *new_buf; + u32 i; + + if ((u32)buf != ALIGN((u32)buf, ARCH_DMA_MINALIGN)) { + new_buf = (u32 *)ALIGN((u32)buf, ARCH_DMA_MINALIGN); + + /* + * This might be dangerous but permits to flash if + * ARCH_DMA_MINALIGN is greater than header size + */ + if (new_buf > buf) { + debug("%s: Aligned buffer is after buffer start\n", + __func__); + new_buf -= ARCH_DMA_MINALIGN; + } + printf("%s: Align buffer at %x to %x(swap %d)\n", __func__, + (u32)buf, (u32)new_buf, swap); + + for (i = 0; i < (len/4); i++) + new_buf[i] = load_word(&buf[i], swap); + + buf = new_buf; + } else if (swap != SWAP_DONE) { + /* For bitstream which are aligned */ + u32 *new_buf = (u32 *)buf; + + printf("%s: Bitstream is not swapped(%d) - swap it\n", __func__, + swap); + + for (i = 0; i < (len/4); i++) + new_buf[i] = load_word(&buf[i], swap); + } + + return buf; +} + +static int zynq_validate_bitstream(xilinx_desc *desc, const void *buf, + size_t bsize, u32 blocksize, u32 *swap, + u32 *partialbit) +{ + u32 *buf_start; + u32 diff; + + /* Detect if we are going working with partial or full bitstream */ + if (bsize != desc->size) { + printf("%s: Working with partial bitstream\n", __func__); + *partialbit = 1; + } + buf_start = check_data((u8 *)buf, blocksize, swap); + + if (!buf_start) + return FPGA_FAIL; + + /* Check if data is postpone from start */ + diff = (u32)buf_start - (u32)buf; + if (diff) { + printf("%s: Bitstream is not validated yet (diff %x)\n", + __func__, diff); + return FPGA_FAIL; + } + + if ((u32)buf < SZ_1M) { + printf("%s: Bitstream has to be placed up to 1MB (%x)\n", + __func__, (u32)buf); + return FPGA_FAIL; + } + + if (zynq_dma_xfer_init(*partialbit)) + return FPGA_FAIL; + + return 0; +} + + +static int zynq_load(xilinx_desc *desc, const void *buf, size_t bsize) +{ + unsigned long ts; /* Timestamp */ + u32 partialbit = 0; + u32 isr_status, swap; + + /* + * send bsize inplace of blocksize as it was not a bitstream + * in chunks + */ + if (zynq_validate_bitstream(desc, buf, bsize, bsize, &swap, + &partialbit)) + return FPGA_FAIL; + + buf = zynq_align_dma_buffer((u32 *)buf, bsize, swap); + + debug("%s: Source = 0x%08X\n", __func__, (u32)buf); + debug("%s: Size = %zu\n", __func__, bsize); + + /* flush(clean & invalidate) d-cache range buf */ + flush_dcache_range((u32)buf, (u32)buf + + roundup(bsize, ARCH_DMA_MINALIGN)); + + if (zynq_dma_transfer((u32)buf | 1, bsize >> 2, 0xffffffff, 0)) + return FPGA_FAIL; + + isr_status = readl(&devcfg_base->int_sts); + /* Check FPGA configuration completion */ + ts = get_timer(0); + while (!(isr_status & DEVCFG_ISR_PCFG_DONE)) { + if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT) { + printf("%s: Timeout wait for FPGA to config\n", + __func__); + return FPGA_FAIL; + } + isr_status = readl(&devcfg_base->int_sts); + } + + debug("%s: FPGA config done\n", __func__); + + if (!partialbit) + zynq_slcr_devcfg_enable(); + + return FPGA_SUCCESS; +} + +static int zynq_dump(xilinx_desc *desc, const void *buf, size_t bsize) +{ + return FPGA_FAIL; +} + +struct xilinx_fpga_op zynq_op = { + .load = zynq_load, + .dump = zynq_dump, + .info = zynq_info, +}; diff --git a/qemu/roms/u-boot/drivers/gpio/Makefile b/qemu/roms/u-boot/drivers/gpio/Makefile new file mode 100644 index 000000000..4e001e12b --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/Makefile @@ -0,0 +1,36 @@ +# +# Copyright 2000-2008 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_DM_GPIO) += gpio-uclass.o + +obj-$(CONFIG_AT91_GPIO) += at91_gpio.o +obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o +obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o +obj-$(CONFIG_KONA_GPIO) += kona_gpio.o +obj-$(CONFIG_MARVELL_GPIO) += mvgpio.o +obj-$(CONFIG_MARVELL_MFP) += mvmfp.o +obj-$(CONFIG_MXC_GPIO) += mxc_gpio.o +obj-$(CONFIG_MXS_GPIO) += mxs_gpio.o +obj-$(CONFIG_PCA953X) += pca953x.o +obj-$(CONFIG_PCA9698) += pca9698.o +obj-$(CONFIG_S5P) += s5p_gpio.o +obj-$(CONFIG_SANDBOX_GPIO) += sandbox.o +obj-$(CONFIG_SPEAR_GPIO) += spear_gpio.o +obj-$(CONFIG_TEGRA_GPIO) += tegra_gpio.o +obj-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o +obj-$(CONFIG_DM644X_GPIO) += da8xx_gpio.o +obj-$(CONFIG_ALTERA_PIO) += altera_pio.o +obj-$(CONFIG_MPC83XX_GPIO) += mpc83xx_gpio.o +obj-$(CONFIG_SH_GPIO_PFC) += sh_pfc.o +obj-$(CONFIG_OMAP_GPIO) += omap_gpio.o +obj-$(CONFIG_DB8500_GPIO) += db8500_gpio.o +obj-$(CONFIG_BCM2835_GPIO) += bcm2835_gpio.o +obj-$(CONFIG_S3C2440_GPIO) += s3c2440_gpio.o +obj-$(CONFIG_XILINX_GPIO) += xilinx_gpio.o +obj-$(CONFIG_ADI_GPIO2) += adi_gpio2.o +obj-$(CONFIG_TCA642X) += tca642x.o +oby-$(CONFIG_SX151X) += sx151x.o diff --git a/qemu/roms/u-boot/drivers/gpio/adi_gpio2.c b/qemu/roms/u-boot/drivers/gpio/adi_gpio2.c new file mode 100644 index 000000000..88cd65b87 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/adi_gpio2.c @@ -0,0 +1,423 @@ +/* + * ADI GPIO2 Abstraction Layer + * Support BF54x, BF60x and future processors. + * + * Copyright 2008-2013 Analog Devices Inc. + * + * Licensed under the GPL-2 or later + */ + +#include +#include +#include + +#define RESOURCE_LABEL_SIZE 16 + +static struct str_ident { + char name[RESOURCE_LABEL_SIZE]; +} str_ident[MAX_RESOURCES]; + +static void gpio_error(unsigned gpio) +{ + printf("adi_gpio2: GPIO %d wasn't requested!\n", gpio); +} + +static void set_label(unsigned short ident, const char *label) +{ + if (label) { + strncpy(str_ident[ident].name, label, + RESOURCE_LABEL_SIZE); + str_ident[ident].name[RESOURCE_LABEL_SIZE - 1] = 0; + } +} + +static char *get_label(unsigned short ident) +{ + return *str_ident[ident].name ? str_ident[ident].name : "UNKNOWN"; +} + +static int cmp_label(unsigned short ident, const char *label) +{ + if (label == NULL) + printf("adi_gpio2: please provide none-null label\n"); + + if (label) + return strcmp(str_ident[ident].name, label); + else + return -EINVAL; +} + +#define map_entry(m, i) reserved_##m##_map[gpio_bank(i)] +#define is_reserved(m, i, e) (map_entry(m, i) & gpio_bit(i)) +#define reserve(m, i) (map_entry(m, i) |= gpio_bit(i)) +#define unreserve(m, i) (map_entry(m, i) &= ~gpio_bit(i)) +#define DECLARE_RESERVED_MAP(m, c) unsigned short reserved_##m##_map[c] + +static DECLARE_RESERVED_MAP(gpio, GPIO_BANK_NUM); +static DECLARE_RESERVED_MAP(peri, gpio_bank(MAX_RESOURCES)); + +inline int check_gpio(unsigned gpio) +{ +#if defined(CONFIG_BF54x) + if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15 || + gpio == GPIO_PH14 || gpio == GPIO_PH15 || + gpio == GPIO_PJ14 || gpio == GPIO_PJ15) + return -EINVAL; +#endif + if (gpio >= MAX_GPIOS) + return -EINVAL; + return 0; +} + +static void port_setup(unsigned gpio, unsigned short usage) +{ +#if defined(CONFIG_BF54x) + if (usage == GPIO_USAGE) + gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio); +#else + if (usage == GPIO_USAGE) + gpio_array[gpio_bank(gpio)]->port_fer_clear = gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->port_fer_set = gpio_bit(gpio); +#endif +} + +inline void portmux_setup(unsigned short per) +{ + u32 pmux; + u16 ident = P_IDENT(per); + u16 function = P_FUNCT2MUX(per); + + pmux = gpio_array[gpio_bank(ident)]->port_mux; + + pmux &= ~(0x3 << (2 * gpio_sub_n(ident))); + pmux |= (function & 0x3) << (2 * gpio_sub_n(ident)); + + gpio_array[gpio_bank(ident)]->port_mux = pmux; +} + +inline u16 get_portmux(unsigned short per) +{ + u32 pmux; + u16 ident = P_IDENT(per); + + pmux = gpio_array[gpio_bank(ident)]->port_mux; + + return pmux >> (2 * gpio_sub_n(ident)) & 0x3; +} + +unsigned short get_gpio_dir(unsigned gpio) +{ + return 0x01 & + (gpio_array[gpio_bank(gpio)]->dir_clear >> gpio_sub_n(gpio)); +} + +/*********************************************************** +* +* FUNCTIONS: Peripheral Resource Allocation +* and PortMux Setup +* +* INPUTS/OUTPUTS: +* per Peripheral Identifier +* label String +* +* DESCRIPTION: Peripheral Resource Allocation and Setup API +**************************************************************/ + +int peripheral_request(unsigned short per, const char *label) +{ + unsigned short ident = P_IDENT(per); + + /* + * Don't cares are pins with only one dedicated function + */ + + if (per & P_DONTCARE) + return 0; + + if (!(per & P_DEFINED)) + return -ENODEV; + + BUG_ON(ident >= MAX_RESOURCES); + + /* If a pin can be muxed as either GPIO or peripheral, make + * sure it is not already a GPIO pin when we request it. + */ + if (unlikely(!check_gpio(ident) && is_reserved(gpio, ident, 1))) { + printf("%s: Peripheral %d is already reserved as GPIO by %s!\n", + __func__, ident, get_label(ident)); + return -EBUSY; + } + + if (unlikely(is_reserved(peri, ident, 1))) { + /* + * Pin functions like AMC address strobes my + * be requested and used by several drivers + */ + + if (!((per & P_MAYSHARE) && + get_portmux(per) == P_FUNCT2MUX(per))) { + /* + * Allow that the identical pin function can + * be requested from the same driver twice + */ + + if (cmp_label(ident, label) == 0) + goto anyway; + + printf("%s: Peripheral %d function %d is already " + "reserved by %s!\n", __func__, ident, + P_FUNCT2MUX(per), get_label(ident)); + return -EBUSY; + } + } + + anyway: + reserve(peri, ident); + + portmux_setup(per); + port_setup(ident, PERIPHERAL_USAGE); + + set_label(ident, label); + + return 0; +} + +int peripheral_request_list(const unsigned short per[], const char *label) +{ + u16 cnt; + int ret; + + for (cnt = 0; per[cnt] != 0; cnt++) { + ret = peripheral_request(per[cnt], label); + + if (ret < 0) { + for (; cnt > 0; cnt--) + peripheral_free(per[cnt - 1]); + + return ret; + } + } + + return 0; +} + +void peripheral_free(unsigned short per) +{ + unsigned short ident = P_IDENT(per); + + if (per & P_DONTCARE) + return; + + if (!(per & P_DEFINED)) + return; + + if (unlikely(!is_reserved(peri, ident, 0))) + return; + + if (!(per & P_MAYSHARE)) + port_setup(ident, GPIO_USAGE); + + unreserve(peri, ident); + + set_label(ident, "free"); +} + +void peripheral_free_list(const unsigned short per[]) +{ + u16 cnt; + for (cnt = 0; per[cnt] != 0; cnt++) + peripheral_free(per[cnt]); +} + +/*********************************************************** +* +* FUNCTIONS: GPIO Driver +* +* INPUTS/OUTPUTS: +* gpio PIO Number between 0 and MAX_GPIOS +* label String +* +* DESCRIPTION: GPIO Driver API +**************************************************************/ + +int gpio_request(unsigned gpio, const char *label) +{ + if (check_gpio(gpio) < 0) + return -EINVAL; + + /* + * Allow that the identical GPIO can + * be requested from the same driver twice + * Do nothing and return - + */ + + if (cmp_label(gpio, label) == 0) + return 0; + + if (unlikely(is_reserved(gpio, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved by %s!\n", + gpio, get_label(gpio)); + return -EBUSY; + } + if (unlikely(is_reserved(peri, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved as Peripheral " + "by %s!\n", gpio, get_label(gpio)); + return -EBUSY; + } + + reserve(gpio, gpio); + set_label(gpio, label); + + port_setup(gpio, GPIO_USAGE); + + return 0; +} + +int gpio_free(unsigned gpio) +{ + if (check_gpio(gpio) < 0) + return -1; + + if (unlikely(!is_reserved(gpio, gpio, 0))) { + gpio_error(gpio); + return -1; + } + + unreserve(gpio, gpio); + + set_label(gpio, "free"); + + return 0; +} + +#ifdef ADI_SPECIAL_GPIO_BANKS +static DECLARE_RESERVED_MAP(special_gpio, gpio_bank(MAX_RESOURCES)); + +int special_gpio_request(unsigned gpio, const char *label) +{ + /* + * Allow that the identical GPIO can + * be requested from the same driver twice + * Do nothing and return - + */ + + if (cmp_label(gpio, label) == 0) + return 0; + + if (unlikely(is_reserved(special_gpio, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved by %s!\n", + gpio, get_label(gpio)); + return -EBUSY; + } + if (unlikely(is_reserved(peri, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved as Peripheral " + "by %s!\n", gpio, get_label(gpio)); + + return -EBUSY; + } + + reserve(special_gpio, gpio); + reserve(peri, gpio); + + set_label(gpio, label); + port_setup(gpio, GPIO_USAGE); + + return 0; +} + +void special_gpio_free(unsigned gpio) +{ + if (unlikely(!is_reserved(special_gpio, gpio, 0))) { + gpio_error(gpio); + return; + } + + unreserve(special_gpio, gpio); + unreserve(peri, gpio); + set_label(gpio, "free"); +} +#endif + +static inline void __gpio_direction_input(unsigned gpio) +{ + gpio_array[gpio_bank(gpio)]->dir_clear = gpio_bit(gpio); +#if defined(CONFIG_BF54x) + gpio_array[gpio_bank(gpio)]->inen |= gpio_bit(gpio); +#else + gpio_array[gpio_bank(gpio)]->inen_set = gpio_bit(gpio); +#endif +} + +int gpio_direction_input(unsigned gpio) +{ + unsigned long flags; + + if (!is_reserved(gpio, gpio, 0)) { + gpio_error(gpio); + return -EINVAL; + } + + local_irq_save(flags); + __gpio_direction_input(gpio); + local_irq_restore(flags); + + return 0; +} + +int gpio_set_value(unsigned gpio, int arg) +{ + if (arg) + gpio_array[gpio_bank(gpio)]->data_set = gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->data_clear = gpio_bit(gpio); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned long flags; + + if (!is_reserved(gpio, gpio, 0)) { + gpio_error(gpio); + return -EINVAL; + } + + local_irq_save(flags); + +#if defined(CONFIG_BF54x) + gpio_array[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); +#else + gpio_array[gpio_bank(gpio)]->inen_clear = gpio_bit(gpio); +#endif + gpio_set_value(gpio, value); + gpio_array[gpio_bank(gpio)]->dir_set = gpio_bit(gpio); + + local_irq_restore(flags); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + return 1 & (gpio_array[gpio_bank(gpio)]->data >> gpio_sub_n(gpio)); +} + +void gpio_labels(void) +{ + int c, gpio; + + for (c = 0; c < MAX_RESOURCES; c++) { + gpio = is_reserved(gpio, c, 1); + if (!check_gpio(c) && gpio) + printf("GPIO_%d:\t%s\tGPIO %s\n", c, get_label(c), + get_gpio_dir(c) ? "OUTPUT" : "INPUT"); + else if (is_reserved(peri, c, 1)) + printf("GPIO_%d:\t%s\tPeripheral\n", c, get_label(c)); + else + continue; + } +} diff --git a/qemu/roms/u-boot/drivers/gpio/altera_pio.c b/qemu/roms/u-boot/drivers/gpio/altera_pio.c new file mode 100644 index 000000000..3ca590700 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/altera_pio.c @@ -0,0 +1,286 @@ +/* + * Driver for Altera's PIO ip core + * + * Copyright (C) 2011 Missing Link Electronics + * Joachim Foerster + * + * SPDX-License-Identifier: GPL-2.0+ + * + * To use this driver, in your board's config. header: + * #define CONFIG_ALTERA_PIO + * #define CONFIG_SYS_ALTERA_PIO_NUM + * #define CONFIG_SYS_ALTERA_PIO_GPIO_NUM + * And in your board's early setup routine: + * altera_pio_init(, , 'i'|'o'|'t', + * , , "label"); + * - 'i'|'o'|'t': PIO is input-only/output-only/tri-state + * - : for correct initial status display, output-only + * - is meant to be used to in cases of active-low + * GPIOs, such as LEDs and buttons (on/pressed == 0). Each bit + * which is 1 in inverts the corresponding GPIO's value + * before set/after get. So: gpio_set_value(gpio, 1) => LED on . + * + * Do NOT define CONFIG_SYS_GPIO_BASE ! + * + * Optionally, in your board's config. header: + * - To force a GPIO numbering scheme like in Linux ... + * #define CONFIG_GPIO_DOWNTO_NUMBERING + * ... starting with 255 (default) + * #define CONFIG_GPIO_DOWNTO_MAX 255 + */ +#include +#include +#include + +#ifdef CONFIG_GPIO_DOWNTO_NUMBERING +#ifndef CONFIG_GPIO_DOWNTO_MAX +#define CONFIG_GPIO_DOWNTO_MAX 255 +#endif +#endif + +#define ALTERA_PIO_DATA 0x0 +#define ALTERA_PIO_DIR 0x4 + +#define GPIO_LABEL_SIZE 9 + + +static struct altera_pio { + u32 base; + u8 width; + char iot; + u32 negmask; + u32 sh_data; + u32 sh_dir; + int gidx; + char label[GPIO_LABEL_SIZE]; +} pios[CONFIG_SYS_ALTERA_PIO_NUM]; + +static int pio_num; + +static struct altera_pio_gpio { + unsigned num; + struct altera_pio *pio; + char reqlabel[GPIO_LABEL_SIZE]; +} gpios[CONFIG_SYS_ALTERA_PIO_GPIO_NUM]; + +static int pio_gpio_num; + + +static int altera_pio_gidx(unsigned gpio) +{ + int i; + + for (i = 0; i < pio_gpio_num; ++i) { + if (gpio == gpios[i].num) + break; + } + if (i >= pio_gpio_num) + return -1; + return i; +} + +static struct altera_pio *altera_pio_get_and_mask(unsigned gpio, u32 *mask) +{ + int gidx = altera_pio_gidx(gpio); + if (gidx < 0) + return NULL; + if (mask) + *mask = 1 << (gidx - gpios[gidx].pio->gidx); + return gpios[gidx].pio; +} + +#define altera_pio_use_gidx(_gidx, _reqlabel) \ + { strncpy(gpios[_gidx].reqlabel, _reqlabel, GPIO_LABEL_SIZE); } +#define altera_pio_unuse_gidx(_gidx) { gpios[_gidx].reqlabel[0] = '\0'; } +#define altera_pio_is_gidx_used(_gidx) (gpios[_gidx].reqlabel[0] != '\0') + +static int altera_pio_gpio_init(struct altera_pio *pio, u8 width) +{ + u8 gidx = pio_gpio_num; + int i; + + if (!width) + return -1; + if ((pio_gpio_num + width) > CONFIG_SYS_ALTERA_PIO_GPIO_NUM) + return -1; + + for (i = 0; i < width; ++i) { +#ifdef CONFIG_GPIO_DOWNTO_NUMBERING + gpios[pio_gpio_num + i].num = \ + CONFIG_GPIO_DOWNTO_MAX + 1 - gidx - width + i; +#else + gpios[pio_gpio_num + i].num = pio_gpio_num + i; +#endif + gpios[pio_gpio_num + i].pio = pio; + altera_pio_unuse_gidx(pio_gpio_num + i); + } + pio_gpio_num += width; + return gidx; +} + +int altera_pio_init(u32 base, u8 width, char iot, u32 rstval, u32 negmask, + const char *label) +{ + if (pio_num >= CONFIG_SYS_ALTERA_PIO_NUM) + return -1; + + pios[pio_num].base = base; + pios[pio_num].width = width; + pios[pio_num].iot = iot; + switch (iot) { + case 'i': + /* input only */ + pios[pio_num].sh_dir = 0; + pios[pio_num].sh_data = readl(base + ALTERA_PIO_DATA); + break; + case 'o': + /* output only */ + pios[pio_num].sh_dir = 0xffffffff & ((1 << width) - 1); + pios[pio_num].sh_data = rstval; + break; + case 't': + /* bidir, tri-state */ + pios[pio_num].sh_dir = readl(base + ALTERA_PIO_DIR); + pios[pio_num].sh_data = readl(base + ALTERA_PIO_DATA); + break; + default: + return -1; + } + pios[pio_num].negmask = negmask & ((1 << width) - 1); + pios[pio_num].gidx = altera_pio_gpio_init(&pios[pio_num], width); + if (pios[pio_num].gidx < 0) + return -1; + strncpy(pios[pio_num].label, label, GPIO_LABEL_SIZE); + return pio_num++; +} + +void altera_pio_info(void) +{ + int i; + int j; + int gidx; + u32 mask; + + for (i = 0; i < pio_num; ++i) { + printf("Altera PIO % 2d, @0x%08x, " + "width: %u, label: %s\n", + i, pios[i].base, pios[i].width, pios[i].label); + gidx = pios[i].gidx; + for (j = gidx; j < (gidx + pios[i].width); ++j) { + mask = 1 << (j - gidx); + printf("\tGPIO % 4d: %s %s [%c] %s\n", + gpios[j].num, + gpios[j].pio->sh_dir & mask ? "out" : " in", + gpio_get_value(gpios[j].num) ? "set" : "clr", + altera_pio_is_gidx_used(j) ? 'x' : ' ', + gpios[j].reqlabel); + } + } +} + + +int gpio_request(unsigned gpio, const char *label) +{ + int gidx = altera_pio_gidx(gpio); + if (gidx < 0) + return gidx; + if (altera_pio_is_gidx_used(gidx)) + return -1; + + altera_pio_use_gidx(gidx, label); + return 0; +} + +int gpio_free(unsigned gpio) +{ + int gidx = altera_pio_gidx(gpio); + if (gidx < 0) + return gidx; + if (!altera_pio_is_gidx_used(gidx)) + return -1; + + altera_pio_unuse_gidx(gidx); + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + u32 mask; + struct altera_pio *pio; + + pio = altera_pio_get_and_mask(gpio, &mask); + if (!pio) + return -1; + if (pio->iot == 'o') + return -1; + + writel(pio->sh_dir &= ~mask, pio->base + ALTERA_PIO_DIR); + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + u32 mask; + struct altera_pio *pio; + + pio = altera_pio_get_and_mask(gpio, &mask); + if (!pio) + return -1; + if (pio->iot == 'i') + return -1; + + value = (pio->negmask & mask) ? !value : value; + if (value) + pio->sh_data |= mask; + else + pio->sh_data &= ~mask; + writel(pio->sh_data, pio->base + ALTERA_PIO_DATA); + writel(pio->sh_dir |= mask, pio->base + ALTERA_PIO_DIR); + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + u32 mask; + struct altera_pio *pio; + u32 val; + + pio = altera_pio_get_and_mask(gpio, &mask); + if (!pio) + return -1; + + if ((pio->sh_dir & mask) || (pio->iot == 'o')) + val = pio->sh_data & mask; + else + val = readl(pio->base + ALTERA_PIO_DATA) & mask; + return (pio->negmask & mask) ? !val : val; +} + +void gpio_set_value(unsigned gpio, int value) +{ + u32 mask; + struct altera_pio *pio; + + pio = altera_pio_get_and_mask(gpio, &mask); + if (!pio) + return; + if (pio->iot == 'i') + return; + + value = (pio->negmask & mask) ? !value : value; + if (value) + pio->sh_data |= mask; + else + pio->sh_data &= ~mask; + writel(pio->sh_data, pio->base + ALTERA_PIO_DATA); + return; +} + +int gpio_is_valid(int number) +{ + int gidx = altera_pio_gidx(number); + + if (gidx < 0) + return 1; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/gpio/at91_gpio.c b/qemu/roms/u-boot/drivers/gpio/at91_gpio.c new file mode 100644 index 000000000..0b7007187 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/at91_gpio.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2013 Bo Shen + * + * Copyright (C) 2009 Jens Scharsig (js_at_ng@scharsoft.de) + * + * Copyright (C) 2005 HP Labs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct at91_port *at91_pio_get_port(unsigned port) +{ + switch (port) { + case AT91_PIO_PORTA: + return (struct at91_port *)ATMEL_BASE_PIOA; + case AT91_PIO_PORTB: + return (struct at91_port *)ATMEL_BASE_PIOB; + case AT91_PIO_PORTC: + return (struct at91_port *)ATMEL_BASE_PIOC; +#if (ATMEL_PIO_PORTS > 3) + case AT91_PIO_PORTD: + return (struct at91_port *)ATMEL_BASE_PIOD; +#if (ATMEL_PIO_PORTS > 4) + case AT91_PIO_PORTE: + return (struct at91_port *)ATMEL_BASE_PIOE; +#endif +#endif + default: + return NULL; + } +} + +int at91_set_pio_pullup(unsigned port, unsigned pin, int use_pullup) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + if (use_pullup) + writel(1 << pin, &at91_port->puer); + else + writel(1 << pin, &at91_port->pudr); + writel(mask, &at91_port->per); + } + + return 0; +} + +/* + * mux the pin to the "GPIO" peripheral role. + */ +int at91_set_pio_periph(unsigned port, unsigned pin, int use_pullup) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + writel(mask, &at91_port->idr); + at91_set_pio_pullup(port, pin, use_pullup); + writel(mask, &at91_port->per); + } + + return 0; +} + +/* + * mux the pin to the "A" internal peripheral role. + */ +int at91_set_a_periph(unsigned port, unsigned pin, int use_pullup) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + writel(mask, &at91_port->idr); + at91_set_pio_pullup(port, pin, use_pullup); +#if defined(CPU_HAS_PIO3) + writel(readl(&at91_port->abcdsr1) & ~mask, + &at91_port->abcdsr1); + writel(readl(&at91_port->abcdsr2) & ~mask, + &at91_port->abcdsr2); +#else + writel(mask, &at91_port->asr); +#endif + writel(mask, &at91_port->pdr); + } + + return 0; +} + +/* + * mux the pin to the "B" internal peripheral role. + */ +int at91_set_b_periph(unsigned port, unsigned pin, int use_pullup) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + writel(mask, &at91_port->idr); + at91_set_pio_pullup(port, pin, use_pullup); +#if defined(CPU_HAS_PIO3) + writel(readl(&at91_port->abcdsr1) | mask, + &at91_port->abcdsr1); + writel(readl(&at91_port->abcdsr2) & ~mask, + &at91_port->abcdsr2); +#else + writel(mask, &at91_port->bsr); +#endif + writel(mask, &at91_port->pdr); + } + + return 0; +} + +#if defined(CPU_HAS_PIO3) +/* + * mux the pin to the "C" internal peripheral role. + */ +int at91_set_c_periph(unsigned port, unsigned pin, int use_pullup) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + writel(mask, &at91_port->idr); + at91_set_pio_pullup(port, pin, use_pullup); + writel(readl(&at91_port->abcdsr1) & ~mask, + &at91_port->abcdsr1); + writel(readl(&at91_port->abcdsr2) | mask, + &at91_port->abcdsr2); + writel(mask, &at91_port->pdr); + } + + return 0; +} + +/* + * mux the pin to the "D" internal peripheral role. + */ +int at91_set_d_periph(unsigned port, unsigned pin, int use_pullup) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + writel(mask, &at91_port->idr); + at91_set_pio_pullup(port, pin, use_pullup); + writel(readl(&at91_port->abcdsr1) | mask, + &at91_port->abcdsr1); + writel(readl(&at91_port->abcdsr2) | mask, + &at91_port->abcdsr2); + writel(mask, &at91_port->pdr); + } + + return 0; +} +#endif + +/* + * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and + * configure it for an input. + */ +int at91_set_pio_input(unsigned port, u32 pin, int use_pullup) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + writel(mask, &at91_port->idr); + at91_set_pio_pullup(port, pin, use_pullup); + writel(mask, &at91_port->odr); + writel(mask, &at91_port->per); + } + + return 0; +} + +/* + * mux the pin to the gpio controller (instead of "A" or "B" peripheral), + * and configure it for an output. + */ +int at91_set_pio_output(unsigned port, u32 pin, int value) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if ((port < ATMEL_PIO_PORTS) && (pin < 32)) { + mask = 1 << pin; + writel(mask, &at91_port->idr); + writel(mask, &at91_port->pudr); + if (value) + writel(mask, &at91_port->sodr); + else + writel(mask, &at91_port->codr); + writel(mask, &at91_port->oer); + writel(mask, &at91_port->per); + } + + return 0; +} + +/* + * enable/disable the glitch filter. mostly used with IRQ handling. + */ +int at91_set_pio_deglitch(unsigned port, unsigned pin, int is_on) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + if (is_on) { +#if defined(CPU_HAS_PIO3) + writel(mask, &at91_port->ifscdr); +#endif + writel(mask, &at91_port->ifer); + } else { + writel(mask, &at91_port->ifdr); + } + } + + return 0; +} + +#if defined(CPU_HAS_PIO3) +/* + * enable/disable the debounce filter. + */ +int at91_set_pio_debounce(unsigned port, unsigned pin, int is_on, int div) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + if (is_on) { + writel(mask, &at91_port->ifscer); + writel(div & PIO_SCDR_DIV, &at91_port->scdr); + writel(mask, &at91_port->ifer); + } else { + writel(mask, &at91_port->ifdr); + } + } + + return 0; +} + +/* + * enable/disable the pull-down. + * If pull-up already enabled while calling the function, we disable it. + */ +int at91_set_pio_pulldown(unsigned port, unsigned pin, int is_on) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + writel(mask, &at91_port->pudr); + if (is_on) + writel(mask, &at91_port->ppder); + else + writel(mask, &at91_port->ppddr); + } + + return 0; +} + +/* + * disable Schmitt trigger + */ +int at91_set_pio_disable_schmitt_trig(unsigned port, unsigned pin) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + writel(readl(&at91_port->schmitt) | mask, + &at91_port->schmitt); + } + + return 0; +} +#endif + +/* + * enable/disable the multi-driver. This is only valid for output and + * allows the output pin to run as an open collector output. + */ +int at91_set_pio_multi_drive(unsigned port, unsigned pin, int is_on) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + if (is_on) + writel(mask, &at91_port->mder); + else + writel(mask, &at91_port->mddr); + } + + return 0; +} + +/* + * assuming the pin is muxed as a gpio output, set its value. + */ +int at91_set_pio_value(unsigned port, unsigned pin, int value) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + if (value) + writel(mask, &at91_port->sodr); + else + writel(mask, &at91_port->codr); + } + + return 0; +} + +/* + * read the pin's value (works even if it's not muxed as a gpio). + */ +int at91_get_pio_value(unsigned port, unsigned pin) +{ + struct at91_port *at91_port = at91_pio_get_port(port); + u32 pdsr = 0, mask; + + if (at91_port && (pin < 32)) { + mask = 1 << pin; + pdsr = readl(&at91_port->pdsr) & mask; + } + + return pdsr != 0; +} + +/* Common GPIO API */ + +int gpio_request(unsigned gpio, const char *label) +{ + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + at91_set_pio_input(at91_gpio_to_port(gpio), + at91_gpio_to_pin(gpio), 0); + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + at91_set_pio_output(at91_gpio_to_port(gpio), + at91_gpio_to_pin(gpio), value); + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + return at91_get_pio_value(at91_gpio_to_port(gpio), + at91_gpio_to_pin(gpio)); +} + +int gpio_set_value(unsigned gpio, int value) +{ + at91_set_pio_value(at91_gpio_to_port(gpio), + at91_gpio_to_pin(gpio), value); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/gpio/bcm2835_gpio.c b/qemu/roms/u-boot/drivers/gpio/bcm2835_gpio.c new file mode 100644 index 000000000..97b513711 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/bcm2835_gpio.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 Vikram Narayananan + * + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +inline int gpio_is_valid(unsigned gpio) +{ + return (gpio < BCM2835_GPIO_COUNT); +} + +int gpio_request(unsigned gpio, const char *label) +{ + return !gpio_is_valid(gpio); +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + struct bcm2835_gpio_regs *reg = + (struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE; + unsigned val; + + val = readl(®->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); + val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio)); + val |= (BCM2835_GPIO_INPUT << BCM2835_GPIO_FSEL_SHIFT(gpio)); + writel(val, ®->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + struct bcm2835_gpio_regs *reg = + (struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE; + unsigned val; + + gpio_set_value(gpio, value); + + val = readl(®->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); + val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio)); + val |= (BCM2835_GPIO_OUTPUT << BCM2835_GPIO_FSEL_SHIFT(gpio)); + writel(val, ®->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + struct bcm2835_gpio_regs *reg = + (struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE; + unsigned val; + + val = readl(®->gplev[BCM2835_GPIO_COMMON_BANK(gpio)]); + + return (val >> BCM2835_GPIO_COMMON_SHIFT(gpio)) & 0x1; +} + +int gpio_set_value(unsigned gpio, int value) +{ + struct bcm2835_gpio_regs *reg = + (struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE; + u32 *output_reg = value ? reg->gpset : reg->gpclr; + + writel(1 << BCM2835_GPIO_COMMON_SHIFT(gpio), + &output_reg[BCM2835_GPIO_COMMON_BANK(gpio)]); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/gpio/da8xx_gpio.c b/qemu/roms/u-boot/drivers/gpio/da8xx_gpio.c new file mode 100644 index 000000000..fa3a3946f --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/da8xx_gpio.c @@ -0,0 +1,396 @@ +/* + * GPIO driver for TI DaVinci DA8xx SOCs. + * + * (C) Copyright 2011 Guralp Systems Ltd. + * Laurence Withers + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static struct gpio_registry { + int is_registered; + char name[GPIO_NAME_SIZE]; +} gpio_registry[MAX_NUM_GPIOS]; + +#if defined(CONFIG_SOC_DA8XX) +#define pinmux(x) (&davinci_syscfg_regs->pinmux[x]) + +#if defined(CONFIG_SOC_DA8XX) && !defined(CONFIG_SOC_DA850) +static const struct pinmux_config gpio_pinmux[] = { + { pinmux(13), 8, 6 }, /* GP0[0] */ + { pinmux(13), 8, 7 }, + { pinmux(14), 8, 0 }, + { pinmux(14), 8, 1 }, + { pinmux(14), 8, 2 }, + { pinmux(14), 8, 3 }, + { pinmux(14), 8, 4 }, + { pinmux(14), 8, 5 }, + { pinmux(14), 8, 6 }, + { pinmux(14), 8, 7 }, + { pinmux(15), 8, 0 }, + { pinmux(15), 8, 1 }, + { pinmux(15), 8, 2 }, + { pinmux(15), 8, 3 }, + { pinmux(15), 8, 4 }, + { pinmux(15), 8, 5 }, + { pinmux(15), 8, 6 }, /* GP1[0] */ + { pinmux(15), 8, 7 }, + { pinmux(16), 8, 0 }, + { pinmux(16), 8, 1 }, + { pinmux(16), 8, 2 }, + { pinmux(16), 8, 3 }, + { pinmux(16), 8, 4 }, + { pinmux(16), 8, 5 }, + { pinmux(16), 8, 6 }, + { pinmux(16), 8, 7 }, + { pinmux(17), 8, 0 }, + { pinmux(17), 8, 1 }, + { pinmux(17), 8, 2 }, + { pinmux(17), 8, 3 }, + { pinmux(17), 8, 4 }, + { pinmux(17), 8, 5 }, + { pinmux(17), 8, 6 }, /* GP2[0] */ + { pinmux(17), 8, 7 }, + { pinmux(18), 8, 0 }, + { pinmux(18), 8, 1 }, + { pinmux(18), 8, 2 }, + { pinmux(18), 8, 3 }, + { pinmux(18), 8, 4 }, + { pinmux(18), 8, 5 }, + { pinmux(18), 8, 6 }, + { pinmux(18), 8, 7 }, + { pinmux(19), 8, 0 }, + { pinmux(9), 8, 2 }, + { pinmux(9), 8, 3 }, + { pinmux(9), 8, 4 }, + { pinmux(9), 8, 5 }, + { pinmux(9), 8, 6 }, + { pinmux(10), 8, 1 }, /* GP3[0] */ + { pinmux(10), 8, 2 }, + { pinmux(10), 8, 3 }, + { pinmux(10), 8, 4 }, + { pinmux(10), 8, 5 }, + { pinmux(10), 8, 6 }, + { pinmux(10), 8, 7 }, + { pinmux(11), 8, 0 }, + { pinmux(11), 8, 1 }, + { pinmux(11), 8, 2 }, + { pinmux(11), 8, 3 }, + { pinmux(11), 8, 4 }, + { pinmux(9), 8, 7 }, + { pinmux(2), 8, 6 }, + { pinmux(11), 8, 5 }, + { pinmux(11), 8, 6 }, + { pinmux(12), 8, 4 }, /* GP4[0] */ + { pinmux(12), 8, 5 }, + { pinmux(12), 8, 6 }, + { pinmux(12), 8, 7 }, + { pinmux(13), 8, 0 }, + { pinmux(13), 8, 1 }, + { pinmux(13), 8, 2 }, + { pinmux(13), 8, 3 }, + { pinmux(13), 8, 4 }, + { pinmux(13), 8, 5 }, + { pinmux(11), 8, 7 }, + { pinmux(12), 8, 0 }, + { pinmux(12), 8, 1 }, + { pinmux(12), 8, 2 }, + { pinmux(12), 8, 3 }, + { pinmux(9), 8, 1 }, + { pinmux(7), 8, 3 }, /* GP5[0] */ + { pinmux(7), 8, 4 }, + { pinmux(7), 8, 5 }, + { pinmux(7), 8, 6 }, + { pinmux(7), 8, 7 }, + { pinmux(8), 8, 0 }, + { pinmux(8), 8, 1 }, + { pinmux(8), 8, 2 }, + { pinmux(8), 8, 3 }, + { pinmux(8), 8, 4 }, + { pinmux(8), 8, 5 }, + { pinmux(8), 8, 6 }, + { pinmux(8), 8, 7 }, + { pinmux(9), 8, 0 }, + { pinmux(7), 8, 1 }, + { pinmux(7), 8, 2 }, + { pinmux(5), 8, 1 }, /* GP6[0] */ + { pinmux(5), 8, 2 }, + { pinmux(5), 8, 3 }, + { pinmux(5), 8, 4 }, + { pinmux(5), 8, 5 }, + { pinmux(5), 8, 6 }, + { pinmux(5), 8, 7 }, + { pinmux(6), 8, 0 }, + { pinmux(6), 8, 1 }, + { pinmux(6), 8, 2 }, + { pinmux(6), 8, 3 }, + { pinmux(6), 8, 4 }, + { pinmux(6), 8, 5 }, + { pinmux(6), 8, 6 }, + { pinmux(6), 8, 7 }, + { pinmux(7), 8, 0 }, + { pinmux(1), 8, 0 }, /* GP7[0] */ + { pinmux(1), 8, 1 }, + { pinmux(1), 8, 2 }, + { pinmux(1), 8, 3 }, + { pinmux(1), 8, 4 }, + { pinmux(1), 8, 5 }, + { pinmux(1), 8, 6 }, + { pinmux(1), 8, 7 }, + { pinmux(2), 8, 0 }, + { pinmux(2), 8, 1 }, + { pinmux(2), 8, 2 }, + { pinmux(2), 8, 3 }, + { pinmux(2), 8, 4 }, + { pinmux(2), 8, 5 }, + { pinmux(0), 1, 0 }, + { pinmux(0), 1, 1 }, +}; +#else /* CONFIG_SOC_DA8XX && CONFIG_SOC_DA850 */ +static const struct pinmux_config gpio_pinmux[] = { + { pinmux(1), 8, 7 }, /* GP0[0] */ + { pinmux(1), 8, 6 }, + { pinmux(1), 8, 5 }, + { pinmux(1), 8, 4 }, + { pinmux(1), 8, 3 }, + { pinmux(1), 8, 2 }, + { pinmux(1), 8, 1 }, + { pinmux(1), 8, 0 }, + { pinmux(0), 8, 7 }, + { pinmux(0), 8, 6 }, + { pinmux(0), 8, 5 }, + { pinmux(0), 8, 4 }, + { pinmux(0), 8, 3 }, + { pinmux(0), 8, 2 }, + { pinmux(0), 8, 1 }, + { pinmux(0), 8, 0 }, + { pinmux(4), 8, 7 }, /* GP1[0] */ + { pinmux(4), 8, 6 }, + { pinmux(4), 8, 5 }, + { pinmux(4), 8, 4 }, + { pinmux(4), 8, 3 }, + { pinmux(4), 8, 2 }, + { pinmux(4), 4, 1 }, + { pinmux(4), 4, 0 }, + { pinmux(3), 4, 0 }, + { pinmux(2), 4, 6 }, + { pinmux(2), 4, 5 }, + { pinmux(2), 4, 4 }, + { pinmux(2), 4, 3 }, + { pinmux(2), 4, 2 }, + { pinmux(2), 4, 1 }, + { pinmux(2), 8, 0 }, + { pinmux(6), 8, 7 }, /* GP2[0] */ + { pinmux(6), 8, 6 }, + { pinmux(6), 8, 5 }, + { pinmux(6), 8, 4 }, + { pinmux(6), 8, 3 }, + { pinmux(6), 8, 2 }, + { pinmux(6), 8, 1 }, + { pinmux(6), 8, 0 }, + { pinmux(5), 8, 7 }, + { pinmux(5), 8, 6 }, + { pinmux(5), 8, 5 }, + { pinmux(5), 8, 4 }, + { pinmux(5), 8, 3 }, + { pinmux(5), 8, 2 }, + { pinmux(5), 8, 1 }, + { pinmux(5), 8, 0 }, + { pinmux(8), 8, 7 }, /* GP3[0] */ + { pinmux(8), 8, 6 }, + { pinmux(8), 8, 5 }, + { pinmux(8), 8, 4 }, + { pinmux(8), 8, 3 }, + { pinmux(8), 8, 2 }, + { pinmux(8), 8, 1 }, + { pinmux(8), 8, 0 }, + { pinmux(7), 8, 7 }, + { pinmux(7), 8, 6 }, + { pinmux(7), 8, 5 }, + { pinmux(7), 8, 4 }, + { pinmux(7), 8, 3 }, + { pinmux(7), 8, 2 }, + { pinmux(7), 8, 1 }, + { pinmux(7), 8, 0 }, + { pinmux(10), 8, 7 }, /* GP4[0] */ + { pinmux(10), 8, 6 }, + { pinmux(10), 8, 5 }, + { pinmux(10), 8, 4 }, + { pinmux(10), 8, 3 }, + { pinmux(10), 8, 2 }, + { pinmux(10), 8, 1 }, + { pinmux(10), 8, 0 }, + { pinmux(9), 8, 7 }, + { pinmux(9), 8, 6 }, + { pinmux(9), 8, 5 }, + { pinmux(9), 8, 4 }, + { pinmux(9), 8, 3 }, + { pinmux(9), 8, 2 }, + { pinmux(9), 8, 1 }, + { pinmux(9), 8, 0 }, + { pinmux(12), 8, 7 }, /* GP5[0] */ + { pinmux(12), 8, 6 }, + { pinmux(12), 8, 5 }, + { pinmux(12), 8, 4 }, + { pinmux(12), 8, 3 }, + { pinmux(12), 8, 2 }, + { pinmux(12), 8, 1 }, + { pinmux(12), 8, 0 }, + { pinmux(11), 8, 7 }, + { pinmux(11), 8, 6 }, + { pinmux(11), 8, 5 }, + { pinmux(11), 8, 4 }, + { pinmux(11), 8, 3 }, + { pinmux(11), 8, 2 }, + { pinmux(11), 8, 1 }, + { pinmux(11), 8, 0 }, + { pinmux(19), 8, 6 }, /* GP6[0] */ + { pinmux(19), 8, 5 }, + { pinmux(19), 8, 4 }, + { pinmux(19), 8, 3 }, + { pinmux(19), 8, 2 }, + { pinmux(16), 8, 1 }, + { pinmux(14), 8, 1 }, + { pinmux(14), 8, 0 }, + { pinmux(13), 8, 7 }, + { pinmux(13), 8, 6 }, + { pinmux(13), 8, 5 }, + { pinmux(13), 8, 4 }, + { pinmux(13), 8, 3 }, + { pinmux(13), 8, 2 }, + { pinmux(13), 8, 1 }, + { pinmux(13), 8, 0 }, + { pinmux(18), 8, 1 }, /* GP7[0] */ + { pinmux(18), 8, 0 }, + { pinmux(17), 8, 7 }, + { pinmux(17), 8, 6 }, + { pinmux(17), 8, 5 }, + { pinmux(17), 8, 4 }, + { pinmux(17), 8, 3 }, + { pinmux(17), 8, 2 }, + { pinmux(17), 8, 1 }, + { pinmux(17), 8, 0 }, + { pinmux(16), 8, 7 }, + { pinmux(16), 8, 6 }, + { pinmux(16), 8, 5 }, + { pinmux(16), 8, 4 }, + { pinmux(16), 8, 3 }, + { pinmux(16), 8, 2 }, + { pinmux(19), 8, 0 }, /* GP8[0] */ + { pinmux(3), 4, 7 }, + { pinmux(3), 4, 6 }, + { pinmux(3), 4, 5 }, + { pinmux(3), 4, 4 }, + { pinmux(3), 4, 3 }, + { pinmux(3), 4, 2 }, + { pinmux(2), 4, 7 }, + { pinmux(19), 8, 1 }, + { pinmux(19), 8, 0 }, + { pinmux(18), 8, 7 }, + { pinmux(18), 8, 6 }, + { pinmux(18), 8, 5 }, + { pinmux(18), 8, 4 }, + { pinmux(18), 8, 3 }, + { pinmux(18), 8, 2 }, +}; +#endif /* CONFIG_SOC_DA8XX && !CONFIG_SOC_DA850 */ +#else /* !CONFIG_SOC_DA8XX */ +#define davinci_configure_pin_mux(a, b) +#endif /* CONFIG_SOC_DA8XX */ + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= MAX_NUM_GPIOS) + return -1; + + if (gpio_registry[gpio].is_registered) + return -1; + + gpio_registry[gpio].is_registered = 1; + strncpy(gpio_registry[gpio].name, label, GPIO_NAME_SIZE); + gpio_registry[gpio].name[GPIO_NAME_SIZE - 1] = 0; + + davinci_configure_pin_mux(&gpio_pinmux[gpio], 1); + + return 0; +} + +int gpio_free(unsigned gpio) +{ + if (gpio >= MAX_NUM_GPIOS) + return -1; + + if (!gpio_registry[gpio].is_registered) + return -1; + + gpio_registry[gpio].is_registered = 0; + gpio_registry[gpio].name[0] = '\0'; + /* Do not configure as input or change pin mux here */ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + struct davinci_gpio *bank; + + bank = GPIO_BANK(gpio); + setbits_le32(&bank->dir, 1U << GPIO_BIT(gpio)); + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + struct davinci_gpio *bank; + + bank = GPIO_BANK(gpio); + clrbits_le32(&bank->dir, 1U << GPIO_BIT(gpio)); + gpio_set_value(gpio, value); + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + struct davinci_gpio *bank; + unsigned int ip; + + bank = GPIO_BANK(gpio); + ip = in_le32(&bank->in_data) & (1U << GPIO_BIT(gpio)); + return ip ? 1 : 0; +} + +int gpio_set_value(unsigned gpio, int value) +{ + struct davinci_gpio *bank; + + bank = GPIO_BANK(gpio); + + if (value) + bank->set_data = 1U << GPIO_BIT(gpio); + else + bank->clr_data = 1U << GPIO_BIT(gpio); + + return 0; +} + +void gpio_info(void) +{ + unsigned gpio, dir, val; + struct davinci_gpio *bank; + + for (gpio = 0; gpio < MAX_NUM_GPIOS; ++gpio) { + bank = GPIO_BANK(gpio); + dir = in_le32(&bank->dir) & (1U << GPIO_BIT(gpio)); + val = gpio_get_value(gpio); + + printf("% 4d: %s: %d [%c] %s\n", + gpio, dir ? " in" : "out", val, + gpio_registry[gpio].is_registered ? 'x' : ' ', + gpio_registry[gpio].name); + } +} diff --git a/qemu/roms/u-boot/drivers/gpio/db8500_gpio.c b/qemu/roms/u-boot/drivers/gpio/db8500_gpio.c new file mode 100644 index 000000000..d5cb383e8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/db8500_gpio.c @@ -0,0 +1,221 @@ +/* + * Code ported from Nomadik GPIO driver in ST-Ericsson Linux kernel code. + * The purpose is that GPIO config found in kernel should work by simply + * copy-paste it to U-boot. + * + * Original Linux authors: + * Copyright (C) 2008,2009 STMicroelectronics + * Copyright (C) 2009 Alessandro Rubini + * Rewritten based on work by Prafulla WADASKAR + * + * Ported to U-boot by: + * Copyright (C) 2010 Joakim Axelsson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include +#include +#include + +#define IO_ADDR(x) (void *) (x) + +/* + * The GPIO module in the db8500 Systems-on-Chip is an + * AMBA device, managing 32 pins and alternate functions. The logic block + * is currently only used in the db8500. + */ + +#define GPIO_TOTAL_PINS 268 +#define GPIO_PINS_PER_BLOCK 32 +#define GPIO_BLOCKS_COUNT (GPIO_TOTAL_PINS/GPIO_PINS_PER_BLOCK + 1) +#define GPIO_BLOCK(pin) (((pin + GPIO_PINS_PER_BLOCK) >> 5) - 1) +#define GPIO_PIN_WITHIN_BLOCK(pin) ((pin)%(GPIO_PINS_PER_BLOCK)) + +/* Register in the logic block */ +#define DB8500_GPIO_DAT 0x00 +#define DB8500_GPIO_DATS 0x04 +#define DB8500_GPIO_DATC 0x08 +#define DB8500_GPIO_PDIS 0x0c +#define DB8500_GPIO_DIR 0x10 +#define DB8500_GPIO_DIRS 0x14 +#define DB8500_GPIO_DIRC 0x18 +#define DB8500_GPIO_SLPC 0x1c +#define DB8500_GPIO_AFSLA 0x20 +#define DB8500_GPIO_AFSLB 0x24 + +#define DB8500_GPIO_RIMSC 0x40 +#define DB8500_GPIO_FIMSC 0x44 +#define DB8500_GPIO_IS 0x48 +#define DB8500_GPIO_IC 0x4c +#define DB8500_GPIO_RWIMSC 0x50 +#define DB8500_GPIO_FWIMSC 0x54 +#define DB8500_GPIO_WKS 0x58 + +static void __iomem *get_gpio_addr(unsigned gpio) +{ + /* Our list of GPIO chips */ + static void __iomem *gpio_addrs[GPIO_BLOCKS_COUNT] = { + IO_ADDR(CFG_GPIO_0_BASE), + IO_ADDR(CFG_GPIO_1_BASE), + IO_ADDR(CFG_GPIO_2_BASE), + IO_ADDR(CFG_GPIO_3_BASE), + IO_ADDR(CFG_GPIO_4_BASE), + IO_ADDR(CFG_GPIO_5_BASE), + IO_ADDR(CFG_GPIO_6_BASE), + IO_ADDR(CFG_GPIO_7_BASE), + IO_ADDR(CFG_GPIO_8_BASE) + }; + + return gpio_addrs[GPIO_BLOCK(gpio)]; +} + +static unsigned get_gpio_offset(unsigned gpio) +{ + return GPIO_PIN_WITHIN_BLOCK(gpio); +} + +/* Can only be called from config_pin. Don't configure alt-mode directly */ +static void gpio_set_mode(unsigned gpio, enum db8500_gpio_alt mode) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + u32 bit = 1 << offset; + u32 afunc, bfunc; + + afunc = readl(addr + DB8500_GPIO_AFSLA) & ~bit; + bfunc = readl(addr + DB8500_GPIO_AFSLB) & ~bit; + if (mode & DB8500_GPIO_ALT_A) + afunc |= bit; + if (mode & DB8500_GPIO_ALT_B) + bfunc |= bit; + writel(afunc, addr + DB8500_GPIO_AFSLA); + writel(bfunc, addr + DB8500_GPIO_AFSLB); +} + +/** + * db8500_gpio_set_pull() - enable/disable pull up/down on a gpio + * @gpio: pin number + * @pull: one of DB8500_GPIO_PULL_DOWN, DB8500_GPIO_PULL_UP, + * and DB8500_GPIO_PULL_NONE + * + * Enables/disables pull up/down on a specified pin. This only takes effect if + * the pin is configured as an input (either explicitly or by the alternate + * function). + * + * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is + * configured as an input. Otherwise, due to the way the controller registers + * work, this function will change the value output on the pin. + */ +void db8500_gpio_set_pull(unsigned gpio, enum db8500_gpio_pull pull) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + u32 bit = 1 << offset; + u32 pdis; + + pdis = readl(addr + DB8500_GPIO_PDIS); + if (pull == DB8500_GPIO_PULL_NONE) + pdis |= bit; + else + pdis &= ~bit; + writel(pdis, addr + DB8500_GPIO_PDIS); + + if (pull == DB8500_GPIO_PULL_UP) + writel(bit, addr + DB8500_GPIO_DATS); + else if (pull == DB8500_GPIO_PULL_DOWN) + writel(bit, addr + DB8500_GPIO_DATC); +} + +void db8500_gpio_make_input(unsigned gpio) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + + writel(1 << offset, addr + DB8500_GPIO_DIRC); +} + +int db8500_gpio_get_input(unsigned gpio) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + u32 bit = 1 << offset; + + printf("db8500_gpio_get_input gpio=%u addr=%p offset=%u bit=%#x\n", + gpio, addr, offset, bit); + + return (readl(addr + DB8500_GPIO_DAT) & bit) != 0; +} + +void db8500_gpio_make_output(unsigned gpio, int val) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + + writel(1 << offset, addr + DB8500_GPIO_DIRS); + db8500_gpio_set_output(gpio, val); +} + +void db8500_gpio_set_output(unsigned gpio, int val) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + + if (val) + writel(1 << offset, addr + DB8500_GPIO_DATS); + else + writel(1 << offset, addr + DB8500_GPIO_DATC); +} + +/** + * config_pin - configure a pin's mux attributes + * @cfg: pin confguration + * + * Configures a pin's mode (alternate function or GPIO), its pull up status, + * and its sleep mode based on the specified configuration. The @cfg is + * usually one of the SoC specific macros defined in mach/-pins.h. These + * are constructed using, and can be further enhanced with, the macros in + * plat/pincfg.h. + * + * If a pin's mode is set to GPIO, it is configured as an input to avoid + * side-effects. The gpio can be manipulated later using standard GPIO API + * calls. + */ +static void config_pin(unsigned long cfg) +{ + int pin = PIN_NUM(cfg); + int pull = PIN_PULL(cfg); + int af = PIN_ALT(cfg); + int output = PIN_DIR(cfg); + int val = PIN_VAL(cfg); + + if (output) + db8500_gpio_make_output(pin, val); + else { + db8500_gpio_make_input(pin); + db8500_gpio_set_pull(pin, pull); + } + + gpio_set_mode(pin, af); +} + +/** + * db8500_config_pins - configure several pins at once + * @cfgs: array of pin configurations + * @num: number of elments in the array + * + * Configures several pins using config_pin(). Refer to that function for + * further information. + */ +void db8500_gpio_config_pins(unsigned long *cfgs, size_t num) +{ + size_t i; + + for (i = 0; i < num; i++) + config_pin(cfgs[i]); +} diff --git a/qemu/roms/u-boot/drivers/gpio/gpio-uclass.c b/qemu/roms/u-boot/drivers/gpio/gpio-uclass.c new file mode 100644 index 000000000..56bfd1146 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/gpio-uclass.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/** + * gpio_to_device() - Convert global GPIO number to device, number + * gpio: The numeric representation of the GPIO + * + * Convert the GPIO number to an entry in the list of GPIOs + * or GPIO blocks registered with the GPIO controller. Returns + * entry on success, NULL on error. + */ +static int gpio_to_device(unsigned int gpio, struct device **devp, + unsigned int *offset) +{ + struct gpio_dev_priv *uc_priv; + struct device *dev; + int ret; + + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = uclass_next_device(&dev)) { + uc_priv = dev->uclass_priv; + if (gpio >= uc_priv->gpio_base && + gpio < uc_priv->gpio_base + uc_priv->gpio_count) { + *devp = dev; + *offset = gpio - uc_priv->gpio_base; + return 0; + } + } + + /* No such GPIO */ + return ret ? ret : -EINVAL; +} + +int gpio_lookup_name(const char *name, struct device **devp, + unsigned int *offsetp, unsigned int *gpiop) +{ + struct gpio_dev_priv *uc_priv; + struct device *dev; + int ret; + + if (devp) + *devp = NULL; + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = uclass_next_device(&dev)) { + ulong offset; + int len; + + uc_priv = dev->uclass_priv; + len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0; + + if (!strncmp(name, uc_priv->bank_name, len)) { + if (strict_strtoul(name + len, 10, &offset)) + continue; + if (devp) + *devp = dev; + if (offsetp) + *offsetp = offset; + if (gpiop) + *gpiop = uc_priv->gpio_base + offset; + return 0; + } + } + + return ret ? ret : -EINVAL; +} + +/** + * gpio_request() - [COMPAT] Request GPIO + * gpio: GPIO number + * label: Name for the requested GPIO + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_request(unsigned gpio, const char *label) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + if (!gpio_get_ops(dev)->request) + return 0; + + return gpio_get_ops(dev)->request(dev, offset, label); +} + +/** + * gpio_free() - [COMPAT] Relinquish GPIO + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_free(unsigned gpio) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + if (!gpio_get_ops(dev)->free) + return 0; + return gpio_get_ops(dev)->free(dev, offset); +} + +/** + * gpio_direction_input() - [COMPAT] Set GPIO direction to input + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_input(unsigned gpio) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->direction_input(dev, offset); +} + +/** + * gpio_direction_output() - [COMPAT] Set GPIO direction to output and set value + * gpio: GPIO number + * value: Logical value to be set on the GPIO pin + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->direction_output(dev, offset, value); +} + +/** + * gpio_get_value() - [COMPAT] Sample GPIO pin and return it's value + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns the value of the GPIO pin, or negative value + * on error. + */ +int gpio_get_value(unsigned gpio) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->get_value(dev, offset); +} + +/** + * gpio_set_value() - [COMPAT] Configure logical value on GPIO pin + * gpio: GPIO number + * value: Logical value to be set on the GPIO pin. + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_set_value(unsigned gpio, int value) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->set_value(dev, offset, value); +} + +const char *gpio_get_bank_info(struct device *dev, int *bit_count) +{ + struct gpio_dev_priv *priv; + + /* Must be called on an active device */ + priv = dev->uclass_priv; + assert(priv); + + *bit_count = priv->gpio_count; + return priv->bank_name; +} + +/* We need to renumber the GPIOs when any driver is probed/removed */ +static int gpio_renumber(void) +{ + struct gpio_dev_priv *uc_priv; + struct device *dev; + struct uclass *uc; + unsigned base; + int ret; + + ret = uclass_get(UCLASS_GPIO, &uc); + if (ret) + return ret; + + /* Ensure that we have a base for each bank */ + base = 0; + uclass_foreach_dev(dev, uc) { + if (device_active(dev)) { + uc_priv = dev->uclass_priv; + uc_priv->gpio_base = base; + base += uc_priv->gpio_count; + } + } + + return 0; +} + +static int gpio_post_probe(struct device *dev) +{ + return gpio_renumber(); +} + +static int gpio_pre_remove(struct device *dev) +{ + return gpio_renumber(); +} + +UCLASS_DRIVER(gpio) = { + .id = UCLASS_GPIO, + .name = "gpio", + .post_probe = gpio_post_probe, + .pre_remove = gpio_pre_remove, + .per_device_auto_alloc_size = sizeof(struct gpio_dev_priv), +}; diff --git a/qemu/roms/u-boot/drivers/gpio/intel_ich6_gpio.c b/qemu/roms/u-boot/drivers/gpio/intel_ich6_gpio.c new file mode 100644 index 000000000..7d9fac723 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/intel_ich6_gpio.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This is a GPIO driver for Intel ICH6 and later. The x86 GPIOs are accessed + * through the PCI bus. Each PCI device has 256 bytes of configuration space, + * consisting of a standard header and a device-specific set of registers. PCI + * bus 0, device 31, function 0 gives us access to the chipset GPIOs (among + * other things). Within the PCI configuration space, the GPIOBASE register + * tells us where in the device's I/O region we can find more registers to + * actually access the GPIOs. + * + * PCI bus/device/function 0:1f:0 => PCI config registers + * PCI config register "GPIOBASE" + * PCI I/O space + [GPIOBASE] => start of GPIO registers + * GPIO registers => gpio pin function, direction, value + * + * + * Danger Will Robinson! Bank 0 (GPIOs 0-31) seems to be fairly stable. Most + * ICH versions have more, but the decoding the matrix that describes them is + * absurdly complex and constantly changing. We'll provide Bank 1 and Bank 2, + * but they will ONLY work for certain unspecified chipsets because the offset + * from GPIOBASE changes randomly. Even then, many GPIOs are unimplemented or + * reserved or subject to arcane restrictions. + */ + +#include +#include +#include +#include + +/* Where in config space is the register that points to the GPIO registers? */ +#define PCI_CFG_GPIOBASE 0x48 + +#define NUM_BANKS 3 + +/* Within the I/O space, where are the registers to control the GPIOs? */ +static struct { + u8 use_sel; + u8 io_sel; + u8 lvl; +} gpio_bank[NUM_BANKS] = { + { 0x00, 0x04, 0x0c }, /* Bank 0 */ + { 0x30, 0x34, 0x38 }, /* Bank 1 */ + { 0x40, 0x44, 0x48 } /* Bank 2 */ +}; + +static pci_dev_t dev; /* handle for 0:1f:0 */ +static u32 gpiobase; /* offset into I/O space */ +static int found_it_once; /* valid GPIO device? */ +static u32 lock[NUM_BANKS]; /* "lock" for access to pins */ + +static int bad_arg(int num, int *bank, int *bitnum) +{ + int i = num / 32; + int j = num % 32; + + if (num < 0 || i > NUM_BANKS) { + debug("%s: bogus gpio num: %d\n", __func__, num); + return -1; + } + *bank = i; + *bitnum = j; + return 0; +} + +static int mark_gpio(int bank, int bitnum) +{ + if (lock[bank] & (1UL << bitnum)) { + debug("%s: %d.%d already marked\n", __func__, bank, bitnum); + return -1; + } + lock[bank] |= (1 << bitnum); + return 0; +} + +static void clear_gpio(int bank, int bitnum) +{ + lock[bank] &= ~(1 << bitnum); +} + +static int notmine(int num, int *bank, int *bitnum) +{ + if (bad_arg(num, bank, bitnum)) + return -1; + return !(lock[*bank] & (1UL << *bitnum)); +} + +static int gpio_init(void) +{ + u8 tmpbyte; + u16 tmpword; + u32 tmplong; + + /* Have we already done this? */ + if (found_it_once) + return 0; + + /* Where should it be? */ + dev = PCI_BDF(0, 0x1f, 0); + + /* Is the device present? */ + pci_read_config_word(dev, PCI_VENDOR_ID, &tmpword); + if (tmpword != PCI_VENDOR_ID_INTEL) { + debug("%s: wrong VendorID\n", __func__); + return -1; + } + + pci_read_config_word(dev, PCI_DEVICE_ID, &tmpword); + debug("Found %04x:%04x\n", PCI_VENDOR_ID_INTEL, tmpword); + /* + * We'd like to validate the Device ID too, but pretty much any + * value is either a) correct with slight differences, or b) + * correct but undocumented. We'll have to check a bunch of other + * things instead... + */ + + /* I/O should already be enabled (it's a RO bit). */ + pci_read_config_word(dev, PCI_COMMAND, &tmpword); + if (!(tmpword & PCI_COMMAND_IO)) { + debug("%s: device IO not enabled\n", __func__); + return -1; + } + + /* Header Type must be normal (bits 6-0 only; see spec.) */ + pci_read_config_byte(dev, PCI_HEADER_TYPE, &tmpbyte); + if ((tmpbyte & 0x7f) != PCI_HEADER_TYPE_NORMAL) { + debug("%s: invalid Header type\n", __func__); + return -1; + } + + /* Base Class must be a bridge device */ + pci_read_config_byte(dev, PCI_CLASS_CODE, &tmpbyte); + if (tmpbyte != PCI_CLASS_CODE_BRIDGE) { + debug("%s: invalid class\n", __func__); + return -1; + } + /* Sub Class must be ISA */ + pci_read_config_byte(dev, PCI_CLASS_SUB_CODE, &tmpbyte); + if (tmpbyte != PCI_CLASS_SUB_CODE_BRIDGE_ISA) { + debug("%s: invalid subclass\n", __func__); + return -1; + } + + /* Programming Interface must be 0x00 (no others exist) */ + pci_read_config_byte(dev, PCI_CLASS_PROG, &tmpbyte); + if (tmpbyte != 0x00) { + debug("%s: invalid interface type\n", __func__); + return -1; + } + + /* + * GPIOBASE moved to its current offset with ICH6, but prior to + * that it was unused (or undocumented). Check that it looks + * okay: not all ones or zeros, and mapped to I/O space (bit 0). + */ + pci_read_config_dword(dev, PCI_CFG_GPIOBASE, &tmplong); + if (tmplong == 0x00000000 || tmplong == 0xffffffff || + !(tmplong & 0x00000001)) { + debug("%s: unexpected GPIOBASE value\n", __func__); + return -1; + } + + /* + * Okay, I guess we're looking at the right device. The actual + * GPIO registers are in the PCI device's I/O space, starting + * at the offset that we just read. Bit 0 indicates that it's + * an I/O address, not a memory address, so mask that off. + */ + gpiobase = tmplong & 0xfffffffe; + + /* Finally. These are the droids we're looking for. */ + found_it_once = 1; + return 0; +} + +int gpio_request(unsigned num, const char *label /* UNUSED */) +{ + u32 tmplong; + int i = 0, j = 0; + + /* Is the hardware ready? */ + if (gpio_init()) + return -1; + + if (bad_arg(num, &i, &j)) + return -1; + + /* + * Make sure that the GPIO pin we want isn't already in use for some + * built-in hardware function. We have to check this for every + * requested pin. + */ + tmplong = inl(gpiobase + gpio_bank[i].use_sel); + if (!(tmplong & (1UL << j))) { + debug("%s: gpio %d is reserved for internal use\n", __func__, + num); + return -1; + } + + return mark_gpio(i, j); +} + +int gpio_free(unsigned num) +{ + int i = 0, j = 0; + + if (notmine(num, &i, &j)) + return -1; + + clear_gpio(i, j); + return 0; +} + +int gpio_direction_input(unsigned num) +{ + u32 tmplong; + int i = 0, j = 0; + + if (notmine(num, &i, &j)) + return -1; + + tmplong = inl(gpiobase + gpio_bank[i].io_sel); + tmplong |= (1UL << j); + outl(gpiobase + gpio_bank[i].io_sel, tmplong); + return 0; +} + +int gpio_direction_output(unsigned num, int value) +{ + u32 tmplong; + int i = 0, j = 0; + + if (notmine(num, &i, &j)) + return -1; + + tmplong = inl(gpiobase + gpio_bank[i].io_sel); + tmplong &= ~(1UL << j); + outl(gpiobase + gpio_bank[i].io_sel, tmplong); + return 0; +} + +int gpio_get_value(unsigned num) +{ + u32 tmplong; + int i = 0, j = 0; + int r; + + if (notmine(num, &i, &j)) + return -1; + + tmplong = inl(gpiobase + gpio_bank[i].lvl); + r = (tmplong & (1UL << j)) ? 1 : 0; + return r; +} + +int gpio_set_value(unsigned num, int value) +{ + u32 tmplong; + int i = 0, j = 0; + + if (notmine(num, &i, &j)) + return -1; + + tmplong = inl(gpiobase + gpio_bank[i].lvl); + if (value) + tmplong |= (1UL << j); + else + tmplong &= ~(1UL << j); + outl(gpiobase + gpio_bank[i].lvl, tmplong); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/gpio/kona_gpio.c b/qemu/roms/u-boot/drivers/gpio/kona_gpio.c new file mode 100644 index 000000000..65117438c --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/kona_gpio.c @@ -0,0 +1,141 @@ +/* + * Copyright 2013 Broadcom Corporation. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define GPIO_BASE (void *)GPIO2_BASE_ADDR + +#define GPIO_PASSWD 0x00a5a501 +#define GPIO_PER_BANK 32 +#define GPIO_MAX_BANK_NUM 8 + +#define GPIO_BANK(gpio) ((gpio) >> 5) +#define GPIO_BITMASK(gpio) \ + (1UL << ((gpio) & (GPIO_PER_BANK - 1))) + +#define GPIO_OUT_STATUS(bank) (0x00000000 + ((bank) << 2)) +#define GPIO_IN_STATUS(bank) (0x00000020 + ((bank) << 2)) +#define GPIO_OUT_SET(bank) (0x00000040 + ((bank) << 2)) +#define GPIO_OUT_CLEAR(bank) (0x00000060 + ((bank) << 2)) +#define GPIO_INT_STATUS(bank) (0x00000080 + ((bank) << 2)) +#define GPIO_INT_MASK(bank) (0x000000a0 + ((bank) << 2)) +#define GPIO_INT_MSKCLR(bank) (0x000000c0 + ((bank) << 2)) +#define GPIO_CONTROL(bank) (0x00000100 + ((bank) << 2)) +#define GPIO_PWD_STATUS(bank) (0x00000500 + ((bank) << 2)) + +#define GPIO_GPPWR_OFFSET 0x00000520 + +#define GPIO_GPCTR0_DBR_SHIFT 5 +#define GPIO_GPCTR0_DBR_MASK 0x000001e0 + +#define GPIO_GPCTR0_ITR_SHIFT 3 +#define GPIO_GPCTR0_ITR_MASK 0x00000018 +#define GPIO_GPCTR0_ITR_CMD_RISING_EDGE 0x00000001 +#define GPIO_GPCTR0_ITR_CMD_FALLING_EDGE 0x00000002 +#define GPIO_GPCTR0_ITR_CMD_BOTH_EDGE 0x00000003 + +#define GPIO_GPCTR0_IOTR_MASK 0x00000001 +#define GPIO_GPCTR0_IOTR_CMD_0UTPUT 0x00000000 +#define GPIO_GPCTR0_IOTR_CMD_INPUT 0x00000001 + +int gpio_request(unsigned gpio, const char *label) +{ + unsigned int value, off; + + writel(GPIO_PASSWD, GPIO_BASE + GPIO_GPPWR_OFFSET); + off = GPIO_PWD_STATUS(GPIO_BANK(gpio)); + value = readl(GPIO_BASE + off) & ~GPIO_BITMASK(gpio); + writel(value, GPIO_BASE + off); + + return 0; +} + +int gpio_free(unsigned gpio) +{ + unsigned int value, off; + + writel(GPIO_PASSWD, GPIO_BASE + GPIO_GPPWR_OFFSET); + off = GPIO_PWD_STATUS(GPIO_BANK(gpio)); + value = readl(GPIO_BASE + off) | GPIO_BITMASK(gpio); + writel(value, GPIO_BASE + off); + + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + u32 val; + + val = readl(GPIO_BASE + GPIO_CONTROL(gpio)); + val &= ~GPIO_GPCTR0_IOTR_MASK; + val |= GPIO_GPCTR0_IOTR_CMD_INPUT; + writel(val, GPIO_BASE + GPIO_CONTROL(gpio)); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + int bank_id = GPIO_BANK(gpio); + int bitmask = GPIO_BITMASK(gpio); + u32 val, off; + + val = readl(GPIO_BASE + GPIO_CONTROL(gpio)); + val &= ~GPIO_GPCTR0_IOTR_MASK; + val |= GPIO_GPCTR0_IOTR_CMD_0UTPUT; + writel(val, GPIO_BASE + GPIO_CONTROL(gpio)); + off = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); + + val = readl(GPIO_BASE + off); + val |= bitmask; + writel(val, GPIO_BASE + off); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + int bank_id = GPIO_BANK(gpio); + int bitmask = GPIO_BITMASK(gpio); + u32 val, off; + + /* determine the GPIO pin direction */ + val = readl(GPIO_BASE + GPIO_CONTROL(gpio)); + val &= GPIO_GPCTR0_IOTR_MASK; + + /* read the GPIO bank status */ + off = (GPIO_GPCTR0_IOTR_CMD_INPUT == val) ? + GPIO_IN_STATUS(bank_id) : GPIO_OUT_STATUS(bank_id); + val = readl(GPIO_BASE + off); + + /* return the specified bit status */ + return !!(val & bitmask); +} + +void gpio_set_value(unsigned gpio, int value) +{ + int bank_id = GPIO_BANK(gpio); + int bitmask = GPIO_BITMASK(gpio); + u32 val, off; + + /* determine the GPIO pin direction */ + val = readl(GPIO_BASE + GPIO_CONTROL(gpio)); + val &= GPIO_GPCTR0_IOTR_MASK; + + /* this function only applies to output pin */ + if (GPIO_GPCTR0_IOTR_CMD_INPUT == val) { + printf("%s: Cannot set an input pin %d\n", __func__, gpio); + return; + } + + off = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); + + val = readl(GPIO_BASE + off); + val |= bitmask; + writel(val, GPIO_BASE + off); +} diff --git a/qemu/roms/u-boot/drivers/gpio/kw_gpio.c b/qemu/roms/u-boot/drivers/gpio/kw_gpio.c new file mode 100644 index 000000000..0af75a84e --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/kw_gpio.c @@ -0,0 +1,150 @@ +/* + * arch/arm/plat-orion/gpio.c + * + * Marvell Orion SoC GPIO handling. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Based on (mostly copied from) plat-orion based Linux 2.6 kernel driver. + * Removed orion_gpiochip struct and kernel level irq handling. + * + * Dieter Kiermaier dk-arm-linux@gmx.de + */ + +#include +#include +#include +#include +#include + +static unsigned long gpio_valid_input[BITS_TO_LONGS(GPIO_MAX)]; +static unsigned long gpio_valid_output[BITS_TO_LONGS(GPIO_MAX)]; + +void __set_direction(unsigned pin, int input) +{ + u32 u; + + u = readl(GPIO_IO_CONF(pin)); + if (input) + u |= 1 << (pin & 31); + else + u &= ~(1 << (pin & 31)); + writel(u, GPIO_IO_CONF(pin)); + + u = readl(GPIO_IO_CONF(pin)); +} + +void __set_level(unsigned pin, int high) +{ + u32 u; + + u = readl(GPIO_OUT(pin)); + if (high) + u |= 1 << (pin & 31); + else + u &= ~(1 << (pin & 31)); + writel(u, GPIO_OUT(pin)); +} + +void __set_blinking(unsigned pin, int blink) +{ + u32 u; + + u = readl(GPIO_BLINK_EN(pin)); + if (blink) + u |= 1 << (pin & 31); + else + u &= ~(1 << (pin & 31)); + writel(u, GPIO_BLINK_EN(pin)); +} + +int kw_gpio_is_valid(unsigned pin, int mode) +{ + if (pin < GPIO_MAX) { + if ((mode & GPIO_INPUT_OK) && !test_bit(pin, gpio_valid_input)) + goto err_out; + + if ((mode & GPIO_OUTPUT_OK) && !test_bit(pin, gpio_valid_output)) + goto err_out; + return 0; + } + +err_out: + printf("%s: invalid GPIO %d\n", __func__, pin); + return 1; +} + +void kw_gpio_set_valid(unsigned pin, int mode) +{ + if (mode == 1) + mode = GPIO_INPUT_OK | GPIO_OUTPUT_OK; + if (mode & GPIO_INPUT_OK) + __set_bit(pin, gpio_valid_input); + else + __clear_bit(pin, gpio_valid_input); + if (mode & GPIO_OUTPUT_OK) + __set_bit(pin, gpio_valid_output); + else + __clear_bit(pin, gpio_valid_output); +} +/* + * GENERIC_GPIO primitives. + */ +int kw_gpio_direction_input(unsigned pin) +{ + if (kw_gpio_is_valid(pin, GPIO_INPUT_OK) != 0) + return 1; + + /* Configure GPIO direction. */ + __set_direction(pin, 1); + + return 0; +} + +int kw_gpio_direction_output(unsigned pin, int value) +{ + if (kw_gpio_is_valid(pin, GPIO_OUTPUT_OK) != 0) + { + printf("%s: invalid GPIO %d\n", __func__, pin); + return 1; + } + + __set_blinking(pin, 0); + + /* Configure GPIO output value. */ + __set_level(pin, value); + + /* Configure GPIO direction. */ + __set_direction(pin, 0); + + return 0; +} + +int kw_gpio_get_value(unsigned pin) +{ + int val; + + if (readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31))) + val = readl(GPIO_DATA_IN(pin)) ^ readl(GPIO_IN_POL(pin)); + else + val = readl(GPIO_OUT(pin)); + + return (val >> (pin & 31)) & 1; +} + +void kw_gpio_set_value(unsigned pin, int value) +{ + /* Configure GPIO output value. */ + __set_level(pin, value); +} + +void kw_gpio_set_blink(unsigned pin, int blink) +{ + /* Set output value to zero. */ + __set_level(pin, 0); + + /* Set blinking. */ + __set_blinking(pin, blink); +} diff --git a/qemu/roms/u-boot/drivers/gpio/mpc83xx_gpio.c b/qemu/roms/u-boot/drivers/gpio/mpc83xx_gpio.c new file mode 100644 index 000000000..92b6e0cb7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/mpc83xx_gpio.c @@ -0,0 +1,183 @@ +/* + * Freescale MPC83xx GPIO handling. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#ifndef CONFIG_MPC83XX_GPIO_0_INIT_DIRECTION +#define CONFIG_MPC83XX_GPIO_0_INIT_DIRECTION 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_1_INIT_DIRECTION +#define CONFIG_MPC83XX_GPIO_1_INIT_DIRECTION 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_0_INIT_OPEN_DRAIN +#define CONFIG_MPC83XX_GPIO_0_INIT_OPEN_DRAIN 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_1_INIT_OPEN_DRAIN +#define CONFIG_MPC83XX_GPIO_1_INIT_OPEN_DRAIN 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_0_INIT_VALUE +#define CONFIG_MPC83XX_GPIO_0_INIT_VALUE 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_1_INIT_VALUE +#define CONFIG_MPC83XX_GPIO_1_INIT_VALUE 0 +#endif + +static unsigned int gpio_output_value[MPC83XX_GPIO_CTRLRS]; + +/* + * Generic_GPIO primitives. + */ + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= MAX_NUM_GPIOS) + return -1; + + return 0; +} + +int gpio_free(unsigned gpio) +{ + /* Do not set to input */ + return 0; +} + +/* set GPIO pin 'gpio' as an input */ +int gpio_direction_input(unsigned gpio) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned int ctrlr; + unsigned int line; + unsigned int line_mask; + + /* 32-bits per controller */ + ctrlr = gpio >> 5; + line = gpio & (0x1F); + + /* Big endian */ + line_mask = 1 << (31 - line); + + clrbits_be32(&im->gpio[ctrlr].dir, line_mask); + + return 0; +} + +/* set GPIO pin 'gpio' as an output, with polarity 'value' */ +int gpio_direction_output(unsigned gpio, int value) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned int ctrlr; + unsigned int line; + unsigned int line_mask; + + if (value != 0 && value != 1) { + printf("Error: Value parameter must be 0 or 1.\n"); + return -1; + } + + gpio_set_value(gpio, value); + + /* 32-bits per controller */ + ctrlr = gpio >> 5; + line = gpio & (0x1F); + + /* Big endian */ + line_mask = 1 << (31 - line); + + /* Make the line output */ + setbits_be32(&im->gpio[ctrlr].dir, line_mask); + + return 0; +} + +/* read GPIO IN value of pin 'gpio' */ +int gpio_get_value(unsigned gpio) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned int ctrlr; + unsigned int line; + unsigned int line_mask; + + /* 32-bits per controller */ + ctrlr = gpio >> 5; + line = gpio & (0x1F); + + /* Big endian */ + line_mask = 1 << (31 - line); + + /* Read the value and mask off the bit */ + return (in_be32(&im->gpio[ctrlr].dat) & line_mask) != 0; +} + +/* write GPIO OUT value to pin 'gpio' */ +int gpio_set_value(unsigned gpio, int value) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned int ctrlr; + unsigned int line; + unsigned int line_mask; + + if (value != 0 && value != 1) { + printf("Error: Value parameter must be 0 or 1.\n"); + return -1; + } + + /* 32-bits per controller */ + ctrlr = gpio >> 5; + line = gpio & (0x1F); + + /* Big endian */ + line_mask = 1 << (31 - line); + + /* Update the local output buffer soft copy */ + gpio_output_value[ctrlr] = + (gpio_output_value[ctrlr] & ~line_mask) | \ + (value ? line_mask : 0); + + /* Write the output */ + out_be32(&im->gpio[ctrlr].dat, gpio_output_value[ctrlr]); + + return 0; +} + +/* Configure GPIO registers early */ +void mpc83xx_gpio_init_f(void) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + +#if MPC83XX_GPIO_CTRLRS >= 1 + out_be32(&im->gpio[0].dir, CONFIG_MPC83XX_GPIO_0_INIT_DIRECTION); + out_be32(&im->gpio[0].odr, CONFIG_MPC83XX_GPIO_0_INIT_OPEN_DRAIN); + out_be32(&im->gpio[0].dat, CONFIG_MPC83XX_GPIO_0_INIT_VALUE); + out_be32(&im->gpio[0].ier, 0xFFFFFFFF); /* Clear all events */ + out_be32(&im->gpio[0].imr, 0); + out_be32(&im->gpio[0].icr, 0); +#endif + +#if MPC83XX_GPIO_CTRLRS >= 2 + out_be32(&im->gpio[1].dir, CONFIG_MPC83XX_GPIO_1_INIT_DIRECTION); + out_be32(&im->gpio[1].odr, CONFIG_MPC83XX_GPIO_1_INIT_OPEN_DRAIN); + out_be32(&im->gpio[1].dat, CONFIG_MPC83XX_GPIO_1_INIT_VALUE); + out_be32(&im->gpio[1].ier, 0xFFFFFFFF); /* Clear all events */ + out_be32(&im->gpio[1].imr, 0); + out_be32(&im->gpio[1].icr, 0); +#endif +} + +/* Initialize GPIO soft-copies */ +void mpc83xx_gpio_init_r(void) +{ +#if MPC83XX_GPIO_CTRLRS >= 1 + gpio_output_value[0] = CONFIG_MPC83XX_GPIO_0_INIT_VALUE; +#endif + +#if MPC83XX_GPIO_CTRLRS >= 2 + gpio_output_value[1] = CONFIG_MPC83XX_GPIO_1_INIT_VALUE; +#endif +} diff --git a/qemu/roms/u-boot/drivers/gpio/mvgpio.c b/qemu/roms/u-boot/drivers/gpio/mvgpio.c new file mode 100644 index 000000000..888aa07c4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/mvgpio.c @@ -0,0 +1,97 @@ +/* + * (C) Copyright 2011 + * eInfochips Ltd. + * Written-by: Ajay Bhargav + * + * (C) Copyright 2010 + * Marvell Semiconductor + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "mvgpio.h" +#include + +#ifndef MV_MAX_GPIO +#define MV_MAX_GPIO 128 +#endif + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO requested %d\n", __func__, gpio); + return -1; + } + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + struct gpio_reg *gpio_reg_bank; + + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); + return -1; + } + + gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio)); + writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gcdr); + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + struct gpio_reg *gpio_reg_bank; + + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); + return -1; + } + + gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio)); + writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gsdr); + gpio_set_value(gpio, value); + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + struct gpio_reg *gpio_reg_bank; + u32 gpio_val; + + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); + return -1; + } + + gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio)); + gpio_val = readl(&gpio_reg_bank->gplr); + + return GPIO_VAL(gpio, gpio_val); +} + +int gpio_set_value(unsigned gpio, int value) +{ + struct gpio_reg *gpio_reg_bank; + + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); + return -1; + } + + gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio)); + if (value) + writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gpsr); + else + writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gpcr); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/gpio/mvgpio.h b/qemu/roms/u-boot/drivers/gpio/mvgpio.h new file mode 100644 index 000000000..a3f17a0c3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/mvgpio.h @@ -0,0 +1,58 @@ +/* + * (C) Copyright 2011 + * eInfochips Ltd. + * Written-by: Ajay Bhargav + * + * (C) Copyright 2010 + * Marvell Semiconductor + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MVGPIO_H__ +#define __MVGPIO_H__ + +#include + +#ifdef CONFIG_SHEEVA_88SV331xV5 +/* + * GPIO Register map for SHEEVA 88SV331xV5 + */ +struct gpio_reg { + u32 gplr; /* Pin Level Register - 0x0000 */ + u32 pad0[2]; + u32 gpdr; /* Pin Direction Register - 0x000C */ + u32 pad1[2]; + u32 gpsr; /* Pin Output Set Register - 0x0018 */ + u32 pad2[2]; + u32 gpcr; /* Pin Output Clear Register - 0x0024 */ + u32 pad3[2]; + u32 grer; /* Rising-Edge Detect Enable Register - 0x0030 */ + u32 pad4[2]; + u32 gfer; /* Falling-Edge Detect Enable Register - 0x003C */ + u32 pad5[2]; + u32 gedr; /* Edge Detect Status Register - 0x0048 */ + u32 pad6[2]; + u32 gsdr; /* Bitwise Set of GPIO Direction Register - 0x0054 */ + u32 pad7[2]; + u32 gcdr; /* Bitwise Clear of GPIO Direction Register - 0x0060 */ + u32 pad8[2]; + u32 gsrer; /* Bitwise Set of Rising-Edge Detect Enable + Register - 0x006C */ + u32 pad9[2]; + u32 gcrer; /* Bitwise Clear of Rising-Edge Detect Enable + Register - 0x0078 */ + u32 pad10[2]; + u32 gsfer; /* Bitwise Set of Falling-Edge Detect Enable + Register - 0x0084 */ + u32 pad11[2]; + u32 gcfer; /* Bitwise Clear of Falling-Edge Detect Enable + Register - 0x0090 */ + u32 pad12[2]; + u32 apmask; /* Bitwise Mask of Edge Detect Register - 0x009C */ +}; +#else +#error "CPU core subversion not defined" +#endif + +#endif /* __MVGPIO_H__ */ diff --git a/qemu/roms/u-boot/drivers/gpio/mvmfp.c b/qemu/roms/u-boot/drivers/gpio/mvmfp.c new file mode 100644 index 000000000..97bbe996f --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/mvmfp.c @@ -0,0 +1,66 @@ +/* + * (C) Copyright 2010 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar , + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* + * mfp_config + * + * On most of Marvell SoCs (ex. ARMADA100) there is Multi-Funtion-Pin + * configuration registers to configure each GPIO/Function pin on the + * SoC. + * + * This function reads the array of values for + * MFPR_X registers and programms them into respective + * Multi-Function Pin registers. + * It supports - Alternate Function Selection programming. + * + * Whereas, + * The Configureation value is constructed using MFP() + * array consists of 32bit values as defined in MFP(xx,xx..) macro + */ +void mfp_config(u32 *mfp_cfgs) +{ + u32 *p_mfpr = NULL; + u32 cfg_val, val; + + do { + cfg_val = *mfp_cfgs++; + /* exit if End of configuration table detected */ + if (cfg_val == MFP_EOC) + break; + + p_mfpr = (u32 *)(MV_MFPR_BASE + + MFP_REG_GET_OFFSET(cfg_val)); + + /* Write a mfg register as per configuration */ + val = 0; + if (cfg_val & MFP_AF_FLAG) + /* Abstract and program Afternate-Func Selection */ + val |= cfg_val & MFP_AF_MASK; + if (cfg_val & MFP_EDGE_FLAG) + /* Abstract and program Edge configuration */ + val |= cfg_val & MFP_LPM_EDGE_MASK; + if (cfg_val & MFP_DRIVE_FLAG) + /* Abstract and program Drive configuration */ + val |= cfg_val & MFP_DRIVE_MASK; + if (cfg_val & MFP_PULL_FLAG) + /* Abstract and program Pullup/down configuration */ + val |= cfg_val & MFP_PULL_MASK; + + writel(val, p_mfpr); + } while (1); + /* + * perform a read-back of any MFPR register to make sure the + * previous writings are finished + */ + readl(p_mfpr); +} diff --git a/qemu/roms/u-boot/drivers/gpio/mxc_gpio.c b/qemu/roms/u-boot/drivers/gpio/mxc_gpio.c new file mode 100644 index 000000000..6a572d545 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/mxc_gpio.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2009 + * Guennadi Liakhovetski, DENX Software Engineering, + * + * Copyright (C) 2011 + * Stefano Babic, DENX Software Engineering, + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include + +enum mxc_gpio_direction { + MXC_GPIO_DIRECTION_IN, + MXC_GPIO_DIRECTION_OUT, +}; + +#define GPIO_TO_PORT(n) (n / 32) + +/* GPIO port description */ +static unsigned long gpio_ports[] = { + [0] = GPIO1_BASE_ADDR, + [1] = GPIO2_BASE_ADDR, + [2] = GPIO3_BASE_ADDR, +#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \ + defined(CONFIG_MX53) || defined(CONFIG_MX6) + [3] = GPIO4_BASE_ADDR, +#endif +#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) + [4] = GPIO5_BASE_ADDR, + [5] = GPIO6_BASE_ADDR, +#endif +#if defined(CONFIG_MX53) || defined(CONFIG_MX6) + [6] = GPIO7_BASE_ADDR, +#endif +}; + +static int mxc_gpio_direction(unsigned int gpio, + enum mxc_gpio_direction direction) +{ + unsigned int port = GPIO_TO_PORT(gpio); + struct gpio_regs *regs; + u32 l; + + if (port >= ARRAY_SIZE(gpio_ports)) + return -1; + + gpio &= 0x1f; + + regs = (struct gpio_regs *)gpio_ports[port]; + + l = readl(®s->gpio_dir); + + switch (direction) { + case MXC_GPIO_DIRECTION_OUT: + l |= 1 << gpio; + break; + case MXC_GPIO_DIRECTION_IN: + l &= ~(1 << gpio); + } + writel(l, ®s->gpio_dir); + + return 0; +} + +int gpio_set_value(unsigned gpio, int value) +{ + unsigned int port = GPIO_TO_PORT(gpio); + struct gpio_regs *regs; + u32 l; + + if (port >= ARRAY_SIZE(gpio_ports)) + return -1; + + gpio &= 0x1f; + + regs = (struct gpio_regs *)gpio_ports[port]; + + l = readl(®s->gpio_dr); + if (value) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + writel(l, ®s->gpio_dr); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + unsigned int port = GPIO_TO_PORT(gpio); + struct gpio_regs *regs; + u32 val; + + if (port >= ARRAY_SIZE(gpio_ports)) + return -1; + + gpio &= 0x1f; + + regs = (struct gpio_regs *)gpio_ports[port]; + + val = (readl(®s->gpio_psr) >> gpio) & 0x01; + + return val; +} + +int gpio_request(unsigned gpio, const char *label) +{ + unsigned int port = GPIO_TO_PORT(gpio); + if (port >= ARRAY_SIZE(gpio_ports)) + return -1; + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_IN); +} + +int gpio_direction_output(unsigned gpio, int value) +{ + int ret = gpio_set_value(gpio, value); + + if (ret < 0) + return ret; + + return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT); +} diff --git a/qemu/roms/u-boot/drivers/gpio/mxs_gpio.c b/qemu/roms/u-boot/drivers/gpio/mxs_gpio.c new file mode 100644 index 000000000..da0199b16 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/mxs_gpio.c @@ -0,0 +1,116 @@ +/* + * Freescale i.MX28 GPIO control code + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_MX23) +#define PINCTRL_BANKS 3 +#define PINCTRL_DOUT(n) (0x0500 + ((n) * 0x10)) +#define PINCTRL_DIN(n) (0x0600 + ((n) * 0x10)) +#define PINCTRL_DOE(n) (0x0700 + ((n) * 0x10)) +#define PINCTRL_PIN2IRQ(n) (0x0800 + ((n) * 0x10)) +#define PINCTRL_IRQEN(n) (0x0900 + ((n) * 0x10)) +#define PINCTRL_IRQSTAT(n) (0x0c00 + ((n) * 0x10)) +#elif defined(CONFIG_MX28) +#define PINCTRL_BANKS 5 +#define PINCTRL_DOUT(n) (0x0700 + ((n) * 0x10)) +#define PINCTRL_DIN(n) (0x0900 + ((n) * 0x10)) +#define PINCTRL_DOE(n) (0x0b00 + ((n) * 0x10)) +#define PINCTRL_PIN2IRQ(n) (0x1000 + ((n) * 0x10)) +#define PINCTRL_IRQEN(n) (0x1100 + ((n) * 0x10)) +#define PINCTRL_IRQSTAT(n) (0x1400 + ((n) * 0x10)) +#else +#error "Please select CONFIG_MX23 or CONFIG_MX28" +#endif + +#define GPIO_INT_FALL_EDGE 0x0 +#define GPIO_INT_LOW_LEV 0x1 +#define GPIO_INT_RISE_EDGE 0x2 +#define GPIO_INT_HIGH_LEV 0x3 +#define GPIO_INT_LEV_MASK (1 << 0) +#define GPIO_INT_POL_MASK (1 << 1) + +void mxs_gpio_init(void) +{ + int i; + + for (i = 0; i < PINCTRL_BANKS; i++) { + writel(0, MXS_PINCTRL_BASE + PINCTRL_PIN2IRQ(i)); + writel(0, MXS_PINCTRL_BASE + PINCTRL_IRQEN(i)); + /* Use SCT address here to clear the IRQSTAT bits */ + writel(0xffffffff, MXS_PINCTRL_BASE + PINCTRL_IRQSTAT(i) + 8); + } +} + +int gpio_get_value(unsigned gpio) +{ + uint32_t bank = PAD_BANK(gpio); + uint32_t offset = PINCTRL_DIN(bank); + struct mxs_register_32 *reg = + (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset); + + return (readl(®->reg) >> PAD_PIN(gpio)) & 1; +} + +void gpio_set_value(unsigned gpio, int value) +{ + uint32_t bank = PAD_BANK(gpio); + uint32_t offset = PINCTRL_DOUT(bank); + struct mxs_register_32 *reg = + (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset); + + if (value) + writel(1 << PAD_PIN(gpio), ®->reg_set); + else + writel(1 << PAD_PIN(gpio), ®->reg_clr); +} + +int gpio_direction_input(unsigned gpio) +{ + uint32_t bank = PAD_BANK(gpio); + uint32_t offset = PINCTRL_DOE(bank); + struct mxs_register_32 *reg = + (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset); + + writel(1 << PAD_PIN(gpio), ®->reg_clr); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + uint32_t bank = PAD_BANK(gpio); + uint32_t offset = PINCTRL_DOE(bank); + struct mxs_register_32 *reg = + (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset); + + gpio_set_value(gpio, value); + + writel(1 << PAD_PIN(gpio), ®->reg_set); + + return 0; +} + +int gpio_request(unsigned gpio, const char *label) +{ + if (PAD_BANK(gpio) >= PINCTRL_BANKS) + return -1; + + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/gpio/omap_gpio.c b/qemu/roms/u-boot/drivers/gpio/omap_gpio.c new file mode 100644 index 000000000..13dcf7987 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/omap_gpio.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix + * + * SPDX-License-Identifier: GPL-2.0 + * + * This work is derived from the linux 2.6.27 kernel source + * To fetch, use the kernel repository + * git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git + * Use the v2.6.27 tag. + * + * Below is the original's header including its copyright + * + * linux/arch/arm/plat-omap/gpio.c + * + * Support functions for OMAP GPIO + * + * Copyright (C) 2003-2005 Nokia Corporation + * Written by Juha Yrjölä + */ +#include +#include +#include +#include + +#define OMAP_GPIO_DIR_OUT 0 +#define OMAP_GPIO_DIR_IN 1 + +static inline const struct gpio_bank *get_gpio_bank(int gpio) +{ + return &omap_gpio_bank[gpio >> 5]; +} + +static inline int get_gpio_index(int gpio) +{ + return gpio & 0x1f; +} + +int gpio_is_valid(int gpio) +{ + return (gpio >= 0) && (gpio < OMAP_MAX_GPIO); +} + +static int check_gpio(int gpio) +{ + if (!gpio_is_valid(gpio)) { + printf("ERROR : check_gpio: invalid GPIO %d\n", gpio); + return -1; + } + return 0; +} + +static void _set_gpio_direction(const struct gpio_bank *bank, int gpio, + int is_input) +{ + void *reg = bank->base; + u32 l; + + switch (bank->method) { + case METHOD_GPIO_24XX: + reg += OMAP_GPIO_OE; + break; + default: + return; + } + l = __raw_readl(reg); + if (is_input) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + __raw_writel(l, reg); +} + +/** + * Get the direction of the GPIO by reading the GPIO_OE register + * corresponding to the specified bank. + */ +static int _get_gpio_direction(const struct gpio_bank *bank, int gpio) +{ + void *reg = bank->base; + u32 v; + + switch (bank->method) { + case METHOD_GPIO_24XX: + reg += OMAP_GPIO_OE; + break; + default: + return -1; + } + + v = __raw_readl(reg); + + if (v & (1 << gpio)) + return OMAP_GPIO_DIR_IN; + else + return OMAP_GPIO_DIR_OUT; +} + +static void _set_gpio_dataout(const struct gpio_bank *bank, int gpio, + int enable) +{ + void *reg = bank->base; + u32 l = 0; + + switch (bank->method) { + case METHOD_GPIO_24XX: + if (enable) + reg += OMAP_GPIO_SETDATAOUT; + else + reg += OMAP_GPIO_CLEARDATAOUT; + l = 1 << gpio; + break; + default: + printf("omap3-gpio unknown bank method %s %d\n", + __FILE__, __LINE__); + return; + } + __raw_writel(l, reg); +} + +/** + * Set value of the specified gpio + */ +int gpio_set_value(unsigned gpio, int value) +{ + const struct gpio_bank *bank; + + if (check_gpio(gpio) < 0) + return -1; + bank = get_gpio_bank(gpio); + _set_gpio_dataout(bank, get_gpio_index(gpio), value); + + return 0; +} + +/** + * Get value of the specified gpio + */ +int gpio_get_value(unsigned gpio) +{ + const struct gpio_bank *bank; + void *reg; + int input; + + if (check_gpio(gpio) < 0) + return -1; + bank = get_gpio_bank(gpio); + reg = bank->base; + switch (bank->method) { + case METHOD_GPIO_24XX: + input = _get_gpio_direction(bank, get_gpio_index(gpio)); + switch (input) { + case OMAP_GPIO_DIR_IN: + reg += OMAP_GPIO_DATAIN; + break; + case OMAP_GPIO_DIR_OUT: + reg += OMAP_GPIO_DATAOUT; + break; + default: + return -1; + } + break; + default: + return -1; + } + return (__raw_readl(reg) + & (1 << get_gpio_index(gpio))) != 0; +} + +/** + * Set gpio direction as input + */ +int gpio_direction_input(unsigned gpio) +{ + const struct gpio_bank *bank; + + if (check_gpio(gpio) < 0) + return -1; + + bank = get_gpio_bank(gpio); + _set_gpio_direction(bank, get_gpio_index(gpio), 1); + + return 0; +} + +/** + * Set gpio direction as output + */ +int gpio_direction_output(unsigned gpio, int value) +{ + const struct gpio_bank *bank; + + if (check_gpio(gpio) < 0) + return -1; + + bank = get_gpio_bank(gpio); + _set_gpio_dataout(bank, get_gpio_index(gpio), value); + _set_gpio_direction(bank, get_gpio_index(gpio), 0); + + return 0; +} + +/** + * Request a gpio before using it. + * + * NOTE: Argument 'label' is unused. + */ +int gpio_request(unsigned gpio, const char *label) +{ + if (check_gpio(gpio) < 0) + return -1; + + return 0; +} + +/** + * Reset and free the gpio after using it. + */ +int gpio_free(unsigned gpio) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/gpio/pca953x.c b/qemu/roms/u-boot/drivers/gpio/pca953x.c new file mode 100644 index 000000000..7371cd4a8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/pca953x.c @@ -0,0 +1,312 @@ +/* + * Copyright 2008 Extreme Engineering Solutions, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * Driver for NXP's 4, 8 and 16 bit I2C gpio expanders (eg pca9537, pca9557, + * pca9539, etc) + */ + +#include +#include +#include + +/* Default to an address that hopefully won't corrupt other i2c devices */ +#ifndef CONFIG_SYS_I2C_PCA953X_ADDR +#define CONFIG_SYS_I2C_PCA953X_ADDR (~0) +#endif + +enum { + PCA953X_CMD_INFO, + PCA953X_CMD_DEVICE, + PCA953X_CMD_OUTPUT, + PCA953X_CMD_INPUT, + PCA953X_CMD_INVERT, +}; + +#ifdef CONFIG_SYS_I2C_PCA953X_WIDTH +struct pca953x_chip_ngpio { + uint8_t chip; + uint8_t ngpio; +}; + +static struct pca953x_chip_ngpio pca953x_chip_ngpios[] = + CONFIG_SYS_I2C_PCA953X_WIDTH; + +/* + * Determine the number of GPIO pins supported. If we don't know we assume + * 8 pins. + */ +static int pca953x_ngpio(uint8_t chip) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pca953x_chip_ngpios); i++) + if (pca953x_chip_ngpios[i].chip == chip) + return pca953x_chip_ngpios[i].ngpio; + + return 8; +} +#else +static int pca953x_ngpio(uint8_t chip) +{ + return 8; +} +#endif + +/* + * Modify masked bits in register + */ +static int pca953x_reg_write(uint8_t chip, uint addr, uint mask, uint data) +{ + uint8_t valb; + uint16_t valw; + + if (pca953x_ngpio(chip) <= 8) { + if (i2c_read(chip, addr, 1, &valb, 1)) + return -1; + + valb &= ~mask; + valb |= data; + + return i2c_write(chip, addr, 1, &valb, 1); + } else { + if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2)) + return -1; + + valw &= ~mask; + valw |= data; + + return i2c_write(chip, addr << 1, 1, (u8*)&valw, 2); + } +} + +static int pca953x_reg_read(uint8_t chip, uint addr, uint *data) +{ + uint8_t valb; + uint16_t valw; + + if (pca953x_ngpio(chip) <= 8) { + if (i2c_read(chip, addr, 1, &valb, 1)) + return -1; + *data = (int)valb; + } else { + if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2)) + return -1; + *data = (int)valw; + } + return 0; +} + +/* + * Set output value of IO pins in 'mask' to corresponding value in 'data' + * 0 = low, 1 = high + */ +int pca953x_set_val(uint8_t chip, uint mask, uint data) +{ + return pca953x_reg_write(chip, PCA953X_OUT, mask, data); +} + +/* + * Set read polarity of IO pins in 'mask' to corresponding value in 'data' + * 0 = read pin value, 1 = read inverted pin value + */ +int pca953x_set_pol(uint8_t chip, uint mask, uint data) +{ + return pca953x_reg_write(chip, PCA953X_POL, mask, data); +} + +/* + * Set direction of IO pins in 'mask' to corresponding value in 'data' + * 0 = output, 1 = input + */ +int pca953x_set_dir(uint8_t chip, uint mask, uint data) +{ + return pca953x_reg_write(chip, PCA953X_CONF, mask, data); +} + +/* + * Read current logic level of all IO pins + */ +int pca953x_get_val(uint8_t chip) +{ + uint val; + + if (pca953x_reg_read(chip, PCA953X_IN, &val) < 0) + return -1; + + return (int)val; +} + +#ifdef CONFIG_CMD_PCA953X +#ifdef CONFIG_CMD_PCA953X_INFO +/* + * Display pca953x information + */ +static int pca953x_info(uint8_t chip) +{ + int i; + uint data; + int nr_gpio = pca953x_ngpio(chip); + int msb = nr_gpio - 1; + + printf("pca953x@ 0x%x (%d pins):\n\n", chip, nr_gpio); + printf("gpio pins: "); + for (i = msb; i >= 0; i--) + printf("%x", i); + printf("\n"); + for (i = 11 + nr_gpio; i > 0; i--) + printf("-"); + printf("\n"); + + if (pca953x_reg_read(chip, PCA953X_CONF, &data) < 0) + return -1; + printf("conf: "); + for (i = msb; i >= 0; i--) + printf("%c", data & (1 << i) ? 'i' : 'o'); + printf("\n"); + + if (pca953x_reg_read(chip, PCA953X_POL, &data) < 0) + return -1; + printf("invert: "); + for (i = msb; i >= 0; i--) + printf("%c", data & (1 << i) ? '1' : '0'); + printf("\n"); + + if (pca953x_reg_read(chip, PCA953X_IN, &data) < 0) + return -1; + printf("input: "); + for (i = msb; i >= 0; i--) + printf("%c", data & (1 << i) ? '1' : '0'); + printf("\n"); + + if (pca953x_reg_read(chip, PCA953X_OUT, &data) < 0) + return -1; + printf("output: "); + for (i = msb; i >= 0; i--) + printf("%c", data & (1 << i) ? '1' : '0'); + printf("\n"); + + return 0; +} +#endif /* CONFIG_CMD_PCA953X_INFO */ + +cmd_tbl_t cmd_pca953x[] = { + U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCA953X_CMD_DEVICE, "", ""), + U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCA953X_CMD_OUTPUT, "", ""), + U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCA953X_CMD_INPUT, "", ""), + U_BOOT_CMD_MKENT(invert, 4, 0, (void *)PCA953X_CMD_INVERT, "", ""), +#ifdef CONFIG_CMD_PCA953X_INFO + U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCA953X_CMD_INFO, "", ""), +#endif +}; + +int do_pca953x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + static uint8_t chip = CONFIG_SYS_I2C_PCA953X_ADDR; + int ret = CMD_RET_USAGE, val; + ulong ul_arg2 = 0; + ulong ul_arg3 = 0; + cmd_tbl_t *c; + + c = find_cmd_tbl(argv[1], cmd_pca953x, ARRAY_SIZE(cmd_pca953x)); + + /* All commands but "device" require 'maxargs' arguments */ + if (!c || !((argc == (c->maxargs)) || + (((int)c->cmd == PCA953X_CMD_DEVICE) && + (argc == (c->maxargs - 1))))) { + return CMD_RET_USAGE; + } + + /* arg2 used as chip number or pin number */ + if (argc > 2) + ul_arg2 = simple_strtoul(argv[2], NULL, 16); + + /* arg3 used as pin or invert value */ + if (argc > 3) + ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 0x1; + + switch ((int)c->cmd) { +#ifdef CONFIG_CMD_PCA953X_INFO + case PCA953X_CMD_INFO: + ret = pca953x_info(chip); + if (ret) + ret = CMD_RET_FAILURE; + break; +#endif + + case PCA953X_CMD_DEVICE: + if (argc == 3) + chip = (uint8_t)ul_arg2; + printf("Current device address: 0x%x\n", chip); + ret = CMD_RET_SUCCESS; + break; + + case PCA953X_CMD_INPUT: + ret = pca953x_set_dir(chip, (1 << ul_arg2), + PCA953X_DIR_IN << ul_arg2); + val = (pca953x_get_val(chip) & (1 << ul_arg2)) != 0; + + if (ret) + ret = CMD_RET_FAILURE; + else + printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2, + val); + break; + + case PCA953X_CMD_OUTPUT: + ret = pca953x_set_dir(chip, (1 << ul_arg2), + (PCA953X_DIR_OUT << ul_arg2)); + if (!ret) + ret = pca953x_set_val(chip, (1 << ul_arg2), + (ul_arg3 << ul_arg2)); + if (ret) + ret = CMD_RET_FAILURE; + break; + + case PCA953X_CMD_INVERT: + ret = pca953x_set_pol(chip, (1 << ul_arg2), + (ul_arg3 << ul_arg2)); + if (ret) + ret = CMD_RET_FAILURE; + break; + } + + if (ret == CMD_RET_FAILURE) + eprintf("Error talking to chip at 0x%x\n", chip); + + return ret; +} + +U_BOOT_CMD( + pca953x, 5, 1, do_pca953x, + "pca953x gpio access", + "device [dev]\n" + " - show or set current device address\n" +#ifdef CONFIG_CMD_PCA953X_INFO + "pca953x info\n" + " - display info for current chip\n" +#endif + "pca953x output pin 0|1\n" + " - set pin as output and drive low or high\n" + "pca953x invert pin 0|1\n" + " - disable/enable polarity inversion for reads\n" + "pca953x input pin\n" + " - set pin as input and read value" +); + +#endif /* CONFIG_CMD_PCA953X */ diff --git a/qemu/roms/u-boot/drivers/gpio/pca9698.c b/qemu/roms/u-boot/drivers/gpio/pca9698.c new file mode 100644 index 000000000..3152bf6df --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/pca9698.c @@ -0,0 +1,127 @@ +/* + * (C) Copyright 2011 + * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Driver for NXP's pca9698 40 bit I2C gpio expander + */ + +#include +#include +#include +#include + +/* + * The pca9698 registers + */ + +#define PCA9698_REG_INPUT 0x00 +#define PCA9698_REG_OUTPUT 0x08 +#define PCA9698_REG_POLARITY 0x10 +#define PCA9698_REG_CONFIG 0x18 + +#define PCA9698_BUFFER_SIZE 5 +#define PCA9698_GPIO_COUNT 40 + +static int pca9698_read40(u8 addr, u8 offset, u8 *buffer) +{ + u8 command = offset | 0x80; /* autoincrement */ + + return i2c_read(addr, command, 1, buffer, PCA9698_BUFFER_SIZE); +} + +static int pca9698_write40(u8 addr, u8 offset, u8 *buffer) +{ + u8 command = offset | 0x80; /* autoincrement */ + + return i2c_write(addr, command, 1, buffer, PCA9698_BUFFER_SIZE); +} + +static void pca9698_set_bit(unsigned gpio, u8 *buffer, unsigned value) +{ + unsigned byte = gpio / 8; + unsigned bit = gpio % 8; + + if (value) + buffer[byte] |= (1 << bit); + else + buffer[byte] &= ~(1 << bit); +} + +int pca9698_request(unsigned gpio, const char *label) +{ + if (gpio >= PCA9698_GPIO_COUNT) + return -EINVAL; + + return 0; +} + +void pca9698_free(unsigned gpio) +{ +} + +int pca9698_direction_input(u8 addr, unsigned gpio) +{ + u8 data[PCA9698_BUFFER_SIZE]; + int res; + + res = pca9698_read40(addr, PCA9698_REG_CONFIG, data); + if (res) + return res; + + pca9698_set_bit(gpio, data, 1); + + return pca9698_write40(addr, PCA9698_REG_CONFIG, data); +} + +int pca9698_direction_output(u8 addr, unsigned gpio, int value) +{ + u8 data[PCA9698_BUFFER_SIZE]; + int res; + + res = pca9698_set_value(addr, gpio, value); + if (res) + return res; + + res = pca9698_read40(addr, PCA9698_REG_CONFIG, data); + if (res) + return res; + + pca9698_set_bit(gpio, data, 0); + + return pca9698_write40(addr, PCA9698_REG_CONFIG, data); +} + +int pca9698_get_value(u8 addr, unsigned gpio) +{ + unsigned config_byte = gpio / 8; + unsigned config_bit = gpio % 8; + unsigned value; + u8 data[PCA9698_BUFFER_SIZE]; + int res; + + res = pca9698_read40(addr, PCA9698_REG_INPUT, data); + if (res) + return -1; + + value = data[config_byte] & (1 << config_bit); + + return !!value; +} + +int pca9698_set_value(u8 addr, unsigned gpio, int value) +{ + u8 data[PCA9698_BUFFER_SIZE]; + int res; + + res = pca9698_read40(addr, PCA9698_REG_OUTPUT, data); + if (res) + return res; + + pca9698_set_bit(gpio, data, value); + + return pca9698_write40(addr, PCA9698_REG_OUTPUT, data); +} diff --git a/qemu/roms/u-boot/drivers/gpio/s3c2440_gpio.c b/qemu/roms/u-boot/drivers/gpio/s3c2440_gpio.c new file mode 100644 index 000000000..e1e2d3f80 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/s3c2440_gpio.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2012 + * Gabriel Huau + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include + +#define GPIO_INPUT 0x0 +#define GPIO_OUTPUT 0x1 + +/* 0x4 means that we want DAT and not CON register */ +#define GPIO_PORT(x) ((((x) >> 5) & 0x3) + 0x4) +#define GPIO_BIT(x) ((x) & 0x3f) + +/* + * It's how we calculate the full port address + * We have to get the number of the port + 1 (Port A is at 0x56000001 ...) + * We move it at the second digit, and finally we add 0x4 because we want + * to modify GPIO DAT and not CON + */ +#define GPIO_FULLPORT(x) (S3C24X0_GPIO_BASE | ((GPIO_PORT(gpio) + 1) << 1)) + +int gpio_set_value(unsigned gpio, int value) +{ + unsigned l = readl(GPIO_FULLPORT(gpio)); + unsigned bit; + unsigned port = GPIO_FULLPORT(gpio); + + /* + * All GPIO Port have a configuration on + * 2 bits excepted the first GPIO (A) which + * have only 1 bit of configuration. + */ + if (!GPIO_PORT(gpio)) + bit = (0x1 << GPIO_BIT(gpio)); + else + bit = (0x3 << GPIO_BIT(gpio)); + + if (value) + l |= bit; + else + l &= ~bit; + + return writel(l, port); +} + +int gpio_get_value(unsigned gpio) +{ + unsigned l = readl(GPIO_FULLPORT(gpio)); + + if (GPIO_PORT(gpio) == 0) /* PORT A */ + return (l >> GPIO_BIT(gpio)) & 0x1; + return (l >> GPIO_BIT(gpio)) & 0x3; +} + +int gpio_request(unsigned gpio, const char *label) +{ + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + return writel(GPIO_INPUT << GPIO_BIT(gpio), GPIO_FULLPORT(gpio)); +} + +int gpio_direction_output(unsigned gpio, int value) +{ + writel(GPIO_OUTPUT << GPIO_BIT(gpio), GPIO_FULLPORT(gpio)); + return gpio_set_value(gpio, value); +} diff --git a/qemu/roms/u-boot/drivers/gpio/s5p_gpio.c b/qemu/roms/u-boot/drivers/gpio/s5p_gpio.c new file mode 100644 index 000000000..11a0472c6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/s5p_gpio.c @@ -0,0 +1,181 @@ +/* + * (C) Copyright 2009 Samsung Electronics + * Minkyu Kang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define S5P_GPIO_GET_BANK(x) ((x >> S5P_GPIO_BANK_SHIFT) \ + & S5P_GPIO_BANK_MASK) + +#define S5P_GPIO_GET_PIN(x) (x & S5P_GPIO_PIN_MASK) + +#define CON_MASK(x) (0xf << ((x) << 2)) +#define CON_SFR(x, v) ((v) << ((x) << 2)) + +#define DAT_MASK(x) (0x1 << (x)) +#define DAT_SET(x) (0x1 << (x)) + +#define PULL_MASK(x) (0x3 << ((x) << 1)) +#define PULL_MODE(x, v) ((v) << ((x) << 1)) + +#define DRV_MASK(x) (0x3 << ((x) << 1)) +#define DRV_SET(x, m) ((m) << ((x) << 1)) +#define RATE_MASK(x) (0x1 << (x + 16)) +#define RATE_SET(x) (0x1 << (x + 16)) + +void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) +{ + unsigned int value; + + value = readl(&bank->con); + value &= ~CON_MASK(gpio); + value |= CON_SFR(gpio, cfg); + writel(value, &bank->con); +} + +void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en) +{ + s5p_gpio_cfg_pin(bank, gpio, GPIO_OUTPUT); + s5p_gpio_set_value(bank, gpio, en); +} + +void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio) +{ + s5p_gpio_cfg_pin(bank, gpio, GPIO_INPUT); +} + +void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) +{ + unsigned int value; + + value = readl(&bank->dat); + value &= ~DAT_MASK(gpio); + if (en) + value |= DAT_SET(gpio); + writel(value, &bank->dat); +} + +unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio) +{ + unsigned int value; + + value = readl(&bank->dat); + return !!(value & DAT_MASK(gpio)); +} + +void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) +{ + unsigned int value; + + value = readl(&bank->pull); + value &= ~PULL_MASK(gpio); + + switch (mode) { + case GPIO_PULL_DOWN: + case GPIO_PULL_UP: + value |= PULL_MODE(gpio, mode); + break; + default: + break; + } + + writel(value, &bank->pull); +} + +void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) +{ + unsigned int value; + + value = readl(&bank->drv); + value &= ~DRV_MASK(gpio); + + switch (mode) { + case GPIO_DRV_1X: + case GPIO_DRV_2X: + case GPIO_DRV_3X: + case GPIO_DRV_4X: + value |= DRV_SET(gpio, mode); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) +{ + unsigned int value; + + value = readl(&bank->drv); + value &= ~RATE_MASK(gpio); + + switch (mode) { + case GPIO_DRV_FAST: + case GPIO_DRV_SLOW: + value |= RATE_SET(gpio); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio) +{ + unsigned bank = S5P_GPIO_GET_BANK(gpio); + unsigned base = s5p_gpio_base(gpio); + + return (struct s5p_gpio_bank *)(base + bank); +} + +int s5p_gpio_get_pin(unsigned gpio) +{ + return S5P_GPIO_GET_PIN(gpio); +} + +/* Common GPIO API */ + +int gpio_request(unsigned gpio, const char *label) +{ + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + s5p_gpio_direction_input(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio)); + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + s5p_gpio_direction_output(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), value); + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + return (int) s5p_gpio_get_value(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio)); +} + +int gpio_set_value(unsigned gpio, int value) +{ + s5p_gpio_set_value(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), value); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/gpio/sandbox.c b/qemu/roms/u-boot/drivers/gpio/sandbox.c new file mode 100644 index 000000000..22b6a5f79 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/sandbox.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Flags for each GPIO */ +#define GPIOF_OUTPUT (1 << 0) /* Currently set as an output */ +#define GPIOF_HIGH (1 << 1) /* Currently set high */ +#define GPIOF_RESERVED (1 << 2) /* Is in use / requested */ + +struct gpio_state { + const char *label; /* label given by requester */ + u8 flags; /* flags (GPIOF_...) */ +}; + +/* Access routines for GPIO state */ +static u8 *get_gpio_flags(struct device *dev, unsigned offset) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct gpio_state *state = dev_get_priv(dev); + + if (offset >= uc_priv->gpio_count) { + static u8 invalid_flags; + printf("sandbox_gpio: error: invalid gpio %u\n", offset); + return &invalid_flags; + } + + return &state[offset].flags; +} + +static int get_gpio_flag(struct device *dev, unsigned offset, int flag) +{ + return (*get_gpio_flags(dev, offset) & flag) != 0; +} + +static int set_gpio_flag(struct device *dev, unsigned offset, int flag, + int value) +{ + u8 *gpio = get_gpio_flags(dev, offset); + + if (value) + *gpio |= flag; + else + *gpio &= ~flag; + + return 0; +} + +static int check_reserved(struct device *dev, unsigned offset, + const char *func) +{ + if (!get_gpio_flag(dev, offset, GPIOF_RESERVED)) { + printf("sandbox_gpio: %s: error: offset %u not reserved\n", + func, offset); + return -1; + } + + return 0; +} + +/* + * Back-channel sandbox-internal-only access to GPIO state + */ + +int sandbox_gpio_get_value(struct device *dev, unsigned offset) +{ + if (get_gpio_flag(dev, offset, GPIOF_OUTPUT)) + debug("sandbox_gpio: get_value on output gpio %u\n", offset); + return get_gpio_flag(dev, offset, GPIOF_HIGH); +} + +int sandbox_gpio_set_value(struct device *dev, unsigned offset, int value) +{ + return set_gpio_flag(dev, offset, GPIOF_HIGH, value); +} + +int sandbox_gpio_get_direction(struct device *dev, unsigned offset) +{ + return get_gpio_flag(dev, offset, GPIOF_OUTPUT); +} + +int sandbox_gpio_set_direction(struct device *dev, unsigned offset, int output) +{ + return set_gpio_flag(dev, offset, GPIOF_OUTPUT, output); +} + +/* + * These functions implement the public interface within U-Boot + */ + +/* set GPIO port 'offset' as an input */ +static int sb_gpio_direction_input(struct device *dev, unsigned offset) +{ + debug("%s: offset:%u\n", __func__, offset); + + if (check_reserved(dev, offset, __func__)) + return -1; + + return sandbox_gpio_set_direction(dev, offset, 0); +} + +/* set GPIO port 'offset' as an output, with polarity 'value' */ +static int sb_gpio_direction_output(struct device *dev, unsigned offset, + int value) +{ + debug("%s: offset:%u, value = %d\n", __func__, offset, value); + + if (check_reserved(dev, offset, __func__)) + return -1; + + return sandbox_gpio_set_direction(dev, offset, 1) | + sandbox_gpio_set_value(dev, offset, value); +} + +/* read GPIO IN value of port 'offset' */ +static int sb_gpio_get_value(struct device *dev, unsigned offset) +{ + debug("%s: offset:%u\n", __func__, offset); + + if (check_reserved(dev, offset, __func__)) + return -1; + + return sandbox_gpio_get_value(dev, offset); +} + +/* write GPIO OUT value to port 'offset' */ +static int sb_gpio_set_value(struct device *dev, unsigned offset, int value) +{ + debug("%s: offset:%u, value = %d\n", __func__, offset, value); + + if (check_reserved(dev, offset, __func__)) + return -1; + + if (!sandbox_gpio_get_direction(dev, offset)) { + printf("sandbox_gpio: error: set_value on input gpio %u\n", + offset); + return -1; + } + + return sandbox_gpio_set_value(dev, offset, value); +} + +static int sb_gpio_request(struct device *dev, unsigned offset, + const char *label) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct gpio_state *state = dev_get_priv(dev); + + debug("%s: offset:%u, label:%s\n", __func__, offset, label); + + if (offset >= uc_priv->gpio_count) { + printf("sandbox_gpio: error: invalid gpio %u\n", offset); + return -1; + } + + if (get_gpio_flag(dev, offset, GPIOF_RESERVED)) { + printf("sandbox_gpio: error: gpio %u already reserved\n", + offset); + return -1; + } + + state[offset].label = label; + return set_gpio_flag(dev, offset, GPIOF_RESERVED, 1); +} + +static int sb_gpio_free(struct device *dev, unsigned offset) +{ + struct gpio_state *state = dev_get_priv(dev); + + debug("%s: offset:%u\n", __func__, offset); + + if (check_reserved(dev, offset, __func__)) + return -1; + + state[offset].label = NULL; + return set_gpio_flag(dev, offset, GPIOF_RESERVED, 0); +} + +static int sb_gpio_get_state(struct device *dev, unsigned int offset, + char *buf, int bufsize) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct gpio_state *state = dev_get_priv(dev); + const char *label; + + label = state[offset].label; + snprintf(buf, bufsize, "%s%d: %s: %d [%c]%s%s", + uc_priv->bank_name ? uc_priv->bank_name : "", offset, + sandbox_gpio_get_direction(dev, offset) ? "out" : " in", + sandbox_gpio_get_value(dev, offset), + get_gpio_flag(dev, offset, GPIOF_RESERVED) ? 'x' : ' ', + label ? " " : "", + label ? label : ""); + + return 0; +} + +static const struct dm_gpio_ops gpio_sandbox_ops = { + .request = sb_gpio_request, + .free = sb_gpio_free, + .direction_input = sb_gpio_direction_input, + .direction_output = sb_gpio_direction_output, + .get_value = sb_gpio_get_value, + .set_value = sb_gpio_set_value, + .get_state = sb_gpio_get_state, +}; + +static int sandbox_gpio_ofdata_to_platdata(struct device *dev) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + + uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "num-gpios", 0); + uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, + "gpio-bank-name", NULL); + + return 0; +} + +static int gpio_sandbox_probe(struct device *dev) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + + if (dev->of_offset == -1) { + /* Tell the uclass how many GPIOs we have */ + uc_priv->gpio_count = CONFIG_SANDBOX_GPIO_COUNT; + } + + dev->priv = calloc(sizeof(struct gpio_state), uc_priv->gpio_count); + + return 0; +} + +static const struct device_id sandbox_gpio_ids[] = { + { .compatible = "sandbox,gpio" }, + { } +}; + +U_BOOT_DRIVER(gpio_sandbox) = { + .name = "gpio_sandbox", + .id = UCLASS_GPIO, + .of_match = sandbox_gpio_ids, + .ofdata_to_platdata = sandbox_gpio_ofdata_to_platdata, + .probe = gpio_sandbox_probe, + .ops = &gpio_sandbox_ops, +}; diff --git a/qemu/roms/u-boot/drivers/gpio/sh_pfc.c b/qemu/roms/u-boot/drivers/gpio/sh_pfc.c new file mode 100644 index 000000000..7a5af20a2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/sh_pfc.c @@ -0,0 +1,629 @@ +/* + * Pinmuxed GPIO support for SuperH. + * Copy from linux kernel driver/sh/pfc.c + * + * Copyright (C) 2008 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include + +static struct pinmux_info *gpioc; + +#define pfc_phys_to_virt(p, a) ((void *)a) + +static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) +{ + if (enum_id < r->begin) + return 0; + + if (enum_id > r->end) + return 0; + + return 1; +} + +static unsigned long gpio_read_raw_reg(void *mapped_reg, + unsigned long reg_width) +{ + switch (reg_width) { + + case 8: + return readb(mapped_reg); + case 16: + return readw(mapped_reg); + case 32: + return readl(mapped_reg); + } + + BUG(); + return 0; +} + +static void gpio_write_raw_reg(void *mapped_reg, + unsigned long reg_width, + unsigned long data) +{ + switch (reg_width) { + case 8: + writeb(data, mapped_reg); + return; + case 16: + writew(data, mapped_reg); + return; + case 32: + writel(data, mapped_reg); + return; + } + + BUG(); +} + +static int gpio_read_bit(struct pinmux_data_reg *dr, + unsigned long in_pos) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + + debug("read_bit: addr = %lx, pos = %ld, " + "r_width = %ld\n", dr->reg, pos, dr->reg_width); + + return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; +} + +static void gpio_write_bit(struct pinmux_data_reg *dr, + unsigned long in_pos, unsigned long value) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + + debug("write_bit addr = %lx, value = %d, pos = %ld, " + "r_width = %ld\n", + dr->reg, !!value, pos, dr->reg_width); + + if (value) + __set_bit(pos, &dr->reg_shadow); + else + __clear_bit(pos, &dr->reg_shadow); + + gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); +} + +static void config_reg_helper(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + unsigned long in_pos, +#if 0 + void __iomem **mapped_regp, +#else + void **mapped_regp, +#endif + unsigned long *maskp, + unsigned long *posp) +{ + int k; + + *mapped_regp = pfc_phys_to_virt(gpioc, crp->reg); + + if (crp->field_width) { + *maskp = (1 << crp->field_width) - 1; + *posp = crp->reg_width - ((in_pos + 1) * crp->field_width); + } else { + *maskp = (1 << crp->var_field_width[in_pos]) - 1; + *posp = crp->reg_width; + for (k = 0; k <= in_pos; k++) + *posp -= crp->var_field_width[k]; + } +} + +static int read_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + unsigned long field) +{ + void *mapped_reg; + + unsigned long mask, pos; + + config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); + + debug("read_reg: addr = %lx, field = %ld, " + "r_width = %ld, f_width = %ld\n", + crp->reg, field, crp->reg_width, crp->field_width); + + return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; +} + +static void write_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + unsigned long field, unsigned long value) +{ + void *mapped_reg; + unsigned long mask, pos, data; + + config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); + + debug("write_reg addr = %lx, value = %ld, field = %ld, " + "r_width = %ld, f_width = %ld\n", + crp->reg, value, field, crp->reg_width, crp->field_width); + + mask = ~(mask << pos); + value = value << pos; + + data = gpio_read_raw_reg(mapped_reg, crp->reg_width); + data &= mask; + data |= value; + + if (gpioc->unlock_reg) + gpio_write_raw_reg(pfc_phys_to_virt(gpioc, gpioc->unlock_reg), + 32, ~data); + + gpio_write_raw_reg(mapped_reg, crp->reg_width, data); +} + +static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) +{ + struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_data_reg *data_reg; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + return -1; + + k = 0; + while (1) { + data_reg = gpioc->data_regs + k; + + if (!data_reg->reg_width) + break; + + data_reg->mapped_reg = pfc_phys_to_virt(gpioc, data_reg->reg); + + for (n = 0; n < data_reg->reg_width; n++) { + if (data_reg->enum_ids[n] == gpiop->enum_id) { + gpiop->flags &= ~PINMUX_FLAG_DREG; + gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT); + gpiop->flags &= ~PINMUX_FLAG_DBIT; + gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT); + return 0; + } + } + k++; + } + + BUG(); + + return -1; +} + +static void setup_data_regs(struct pinmux_info *gpioc) +{ + struct pinmux_data_reg *drp; + int k; + + for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) + setup_data_reg(gpioc, k); + + k = 0; + while (1) { + drp = gpioc->data_regs + k; + + if (!drp->reg_width) + break; + + drp->reg_shadow = gpio_read_raw_reg(drp->mapped_reg, + drp->reg_width); + k++; + } +} + +static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, + struct pinmux_data_reg **drp, int *bitp) +{ + struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + return -1; + + k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; + n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; + *drp = gpioc->data_regs + k; + *bitp = n; + return 0; +} + +static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, + struct pinmux_cfg_reg **crp, + int *fieldp, int *valuep, + unsigned long **cntp) +{ + struct pinmux_cfg_reg *config_reg; + unsigned long r_width, f_width, curr_width, ncomb; + int k, m, n, pos, bit_pos; + + k = 0; + while (1) { + config_reg = gpioc->cfg_regs + k; + + r_width = config_reg->reg_width; + f_width = config_reg->field_width; + + if (!r_width) + break; + + pos = 0; + m = 0; + for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { + if (f_width) + curr_width = f_width; + else + curr_width = config_reg->var_field_width[m]; + + ncomb = 1 << curr_width; + for (n = 0; n < ncomb; n++) { + if (config_reg->enum_ids[pos + n] == enum_id) { + *crp = config_reg; + *fieldp = m; + *valuep = n; + *cntp = &config_reg->cnt[m]; + return 0; + } + } + pos += ncomb; + m++; + } + k++; + } + + return -1; +} + +static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, + int pos, pinmux_enum_t *enum_idp) +{ + pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id; + pinmux_enum_t *data = gpioc->gpio_data; + int k; + + if (!enum_in_range(enum_id, &gpioc->data)) { + if (!enum_in_range(enum_id, &gpioc->mark)) { + debug("non data/mark enum_id for gpio %d\n", gpio); + return -1; + } + } + + if (pos) { + *enum_idp = data[pos + 1]; + return pos + 1; + } + + for (k = 0; k < gpioc->gpio_data_size; k++) { + if (data[k] == enum_id) { + *enum_idp = data[k + 1]; + return k + 1; + } + } + + debug("cannot locate data/mark enum_id for gpio %d\n", gpio); + return -1; +} + +enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; + +static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, + int pinmux_type, int cfg_mode) +{ + struct pinmux_cfg_reg *cr = NULL; + pinmux_enum_t enum_id; + struct pinmux_range *range; + int in_range, pos, field, value; + unsigned long *cntp; + + switch (pinmux_type) { + + case PINMUX_TYPE_FUNCTION: + range = NULL; + break; + + case PINMUX_TYPE_OUTPUT: + range = &gpioc->output; + break; + + case PINMUX_TYPE_INPUT: + range = &gpioc->input; + break; + + case PINMUX_TYPE_INPUT_PULLUP: + range = &gpioc->input_pu; + break; + + case PINMUX_TYPE_INPUT_PULLDOWN: + range = &gpioc->input_pd; + break; + + default: + goto out_err; + } + + pos = 0; + enum_id = 0; + field = 0; + value = 0; + while (1) { + pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); + if (pos <= 0) + goto out_err; + + if (!enum_id) + break; + + /* first check if this is a function enum */ + in_range = enum_in_range(enum_id, &gpioc->function); + if (!in_range) { + /* not a function enum */ + if (range) { + /* + * other range exists, so this pin is + * a regular GPIO pin that now is being + * bound to a specific direction. + * + * for this case we only allow function enums + * and the enums that match the other range. + */ + in_range = enum_in_range(enum_id, range); + + /* + * special case pass through for fixed + * input-only or output-only pins without + * function enum register association. + */ + if (in_range && enum_id == range->force) + continue; + } else { + /* + * no other range exists, so this pin + * must then be of the function type. + * + * allow function type pins to select + * any combination of function/in/out + * in their MARK lists. + */ + in_range = 1; + } + } + + if (!in_range) + continue; + + if (get_config_reg(gpioc, enum_id, &cr, + &field, &value, &cntp) != 0) + goto out_err; + + switch (cfg_mode) { + case GPIO_CFG_DRYRUN: + if (!*cntp || + (read_config_reg(gpioc, cr, field) != value)) + continue; + break; + + case GPIO_CFG_REQ: + write_config_reg(gpioc, cr, field, value); + *cntp = *cntp + 1; + break; + + case GPIO_CFG_FREE: + *cntp = *cntp - 1; + break; + } + } + + return 0; + out_err: + return -1; +} + +#if 0 +static DEFINE_SPINLOCK(gpio_lock); +static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip) +{ + return container_of(chip, struct pinmux_info, chip); +} +#endif + +static int sh_gpio_request(unsigned offset) +{ + struct pinmux_data_reg *dummy; + int i, ret, pinmux_type; + + ret = -1; + + if (!gpioc) + goto err_out; + + if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err_out; + + /* setup pin function here if no data is associated with pin */ + + if (get_data_reg(gpioc, offset, &dummy, &i) != 0) + pinmux_type = PINMUX_TYPE_FUNCTION; + else + pinmux_type = PINMUX_TYPE_GPIO; + + if (pinmux_type == PINMUX_TYPE_FUNCTION) { + if (pinmux_config_gpio(gpioc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_out; + + if (pinmux_config_gpio(gpioc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + } + + gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[offset].flags |= pinmux_type; + + ret = 0; +err_out: + return ret; +} + +static void sh_gpio_free(unsigned offset) +{ + int pinmux_type; + + if (!gpioc) + return; + + pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE; + pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE); + gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE; +} + +static int pinmux_direction(struct pinmux_info *gpioc, + unsigned gpio, int new_pinmux_type) +{ + int pinmux_type; + int ret = -1; + + if (!gpioc) + goto err_out; + + pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE; + + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err_out; + } + + if (pinmux_config_gpio(gpioc, gpio, + new_pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_out; + + if (pinmux_config_gpio(gpioc, gpio, + new_pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + + gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; + gpioc->gpios[gpio].flags |= new_pinmux_type; + + ret = 0; + err_out: + return ret; +} + +static int sh_gpio_direction_input(unsigned offset) +{ + return pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT); +} + +static void sh_gpio_set_value(struct pinmux_info *gpioc, + unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) + BUG(); + else + gpio_write_bit(dr, bit, value); +} + +static int sh_gpio_direction_output(unsigned offset, int value) +{ + sh_gpio_set_value(gpioc, offset, value); + return pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT); +} + +static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) + return -1; + + return gpio_read_bit(dr, bit); +} + +static int sh_gpio_get(unsigned offset) +{ + return sh_gpio_get_value(gpioc, offset); +} + +static void sh_gpio_set(unsigned offset, int value) +{ + sh_gpio_set_value(gpioc, offset, value); +} + +int register_pinmux(struct pinmux_info *pip) +{ + if (pip != NULL) { + gpioc = pip; + debug("%s deregistering\n", pip->name); + setup_data_regs(gpioc); + } + return 0; +} + +int unregister_pinmux(struct pinmux_info *pip) +{ + debug("%s deregistering\n", pip->name); + if (gpioc != pip) + return -1; + + gpioc = NULL; + return 0; +} + +int gpio_request(unsigned gpio, const char *label) +{ + sh_gpio_request(gpio); + return 0; +} + +int gpio_free(unsigned gpio) +{ + sh_gpio_free(gpio); + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + return sh_gpio_direction_input(gpio); +} + +int gpio_direction_output(unsigned gpio, int value) +{ + return sh_gpio_direction_output(gpio, value); +} + +void gpio_set_value(unsigned gpio, int value) +{ + sh_gpio_set(gpio, value); +} + +int gpio_get_value(unsigned gpio) +{ + return sh_gpio_get(gpio); +} diff --git a/qemu/roms/u-boot/drivers/gpio/spear_gpio.c b/qemu/roms/u-boot/drivers/gpio/spear_gpio.c new file mode 100644 index 000000000..367b67016 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/spear_gpio.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 Stefan Roese + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Driver for SPEAr600 GPIO controller + */ + +#include +#include +#include +#include +#include + +static int gpio_direction(unsigned gpio, + enum gpio_direction direction) +{ + struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE; + u32 val; + + val = readl(®s->gpiodir); + + if (direction == GPIO_DIRECTION_OUT) + val |= 1 << gpio; + else + val &= ~(1 << gpio); + + writel(val, ®s->gpiodir); + + return 0; +} + +int gpio_set_value(unsigned gpio, int value) +{ + struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE; + + writel(1 << gpio, ®s->gpiodata[DATA_REG_ADDR(gpio)]); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE; + u32 val; + + val = readl(®s->gpiodata[DATA_REG_ADDR(gpio)]); + + return !!val; +} + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= SPEAR_GPIO_COUNT) + return -EINVAL; + + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +void gpio_toggle_value(unsigned gpio) +{ + gpio_set_value(gpio, !gpio_get_value(gpio)); +} + +int gpio_direction_input(unsigned gpio) +{ + return gpio_direction(gpio, GPIO_DIRECTION_IN); +} + +int gpio_direction_output(unsigned gpio, int value) +{ + int ret = gpio_direction(gpio, GPIO_DIRECTION_OUT); + + if (ret < 0) + return ret; + + gpio_set_value(gpio, value); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/gpio/sx151x.c b/qemu/roms/u-boot/drivers/gpio/sx151x.c new file mode 100644 index 000000000..167cf40c7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/sx151x.c @@ -0,0 +1,242 @@ +/* + * (C) Copyright 2013 + * Viktar Palstsiuk, Promwad, viktar.palstsiuk@promwad.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Driver for Semtech SX151x SPI GPIO Expanders + */ + +#include +#include +#include + +#ifndef CONFIG_SX151X_SPI_BUS +#define CONFIG_SX151X_SPI_BUS 0 +#endif + +/* + * The SX151x registers + */ + +#ifdef CONFIG_SX151X_GPIO_COUNT_8 +/* 8bit: SX1511 */ +#define SX151X_REG_DIR 0x07 +#define SX151X_REG_DATA 0x08 +#else +/* 16bit: SX1512 */ +#define SX151X_REG_DIR 0x0F +#define SX151X_REG_DATA 0x11 +#endif +#define SX151X_REG_RESET 0x7D + +static int sx151x_spi_write(int chip, unsigned char reg, unsigned char val) +{ + struct spi_slave *slave; + unsigned char buf[2]; + int ret; + + slave = spi_setup_slave(CONFIG_SX151X_SPI_BUS, chip, 1000000, + SPI_MODE_0); + if (!slave) + return 0; + + spi_claim_bus(slave); + + buf[0] = reg; + buf[1] = val; + + ret = spi_xfer(slave, 16, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END); + if (ret < 0) + printf("spi%d.%d write fail: can't write %02x to %02x: %d\n", + CONFIG_SX151X_SPI_BUS, chip, val, reg, ret); + else + printf("spi%d.%d write 0x%02x to register 0x%02x\n", + CONFIG_SX151X_SPI_BUS, chip, val, reg); + spi_release_bus(slave); + spi_free_slave(slave); + + return ret; +} + +static int sx151x_spi_read(int chip, unsigned char reg) +{ + struct spi_slave *slave; + int ret; + + slave = spi_setup_slave(CONFIG_SX151X_SPI_BUS, chip, 1000000, + SPI_MODE_0); + if (!slave) + return 0; + + spi_claim_bus(slave); + + ret = spi_w8r8(slave, reg | 0x80); + if (ret < 0) + printf("spi%d.%d read fail: can't read %02x: %d\n", + CONFIG_SX151X_SPI_BUS, chip, reg, ret); + else + printf("spi%d.%d read register 0x%02x: 0x%02x\n", + CONFIG_SX151X_SPI_BUS, chip, reg, ret); + + spi_release_bus(slave); + spi_free_slave(slave); + + return ret; +} + +static inline void sx151x_find_cfg(int gpio, unsigned char *reg, unsigned char *mask) +{ + *reg -= gpio / 8; + *mask = 1 << (gpio % 8); +} + +static int sx151x_write_cfg(int chip, unsigned char gpio, unsigned char reg, int val) +{ + unsigned char mask; + unsigned char data; + int ret; + + sx151x_find_cfg(gpio, ®, &mask); + ret = sx151x_spi_read(chip, reg); + if (ret < 0) + return ret; + else + data = ret; + data &= ~mask; + data |= (val << (gpio % 8)) & mask; + return sx151x_spi_write(chip, reg, data); +} + +int sx151x_get_value(int chip, int gpio) +{ + unsigned char reg = SX151X_REG_DATA; + unsigned char mask; + int ret; + + sx151x_find_cfg(gpio, ®, &mask); + ret = sx151x_spi_read(chip, reg); + if (ret >= 0) + ret = (ret & mask) != 0 ? 1 : 0; + + return ret; +} + +int sx151x_set_value(int chip, int gpio, int val) +{ + return sx151x_write_cfg(chip, gpio, SX151X_REG_DATA, (val ? 1 : 0)); +} + +int sx151x_direction_input(int chip, int gpio) +{ + return sx151x_write_cfg(chip, gpio, SX151X_REG_DIR, 1); +} + +int sx151x_direction_output(int chip, int gpio) +{ + return sx151x_write_cfg(chip, gpio, SX151X_REG_DIR, 0); +} + +int sx151x_reset(int chip) +{ + int err; + + err = sx151x_spi_write(chip, SX151X_REG_RESET, 0x12); + if (err < 0) + return err; + + err = sx151x_spi_write(chip, SX151X_REG_RESET, 0x34); + return err; +} + +#ifdef CONFIG_CMD_SX151X + +int do_sx151x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret = CMD_RET_USAGE, chip = 0, gpio = 0, val = 0; + + if (argc < 3) + return CMD_RET_USAGE; + + /* arg2 used as chip number */ + chip = simple_strtoul(argv[2], NULL, 10); + + if (strcmp(argv[1], "reset") == 0) { + ret = sx151x_reset(chip); + if (!ret) { + printf("Device at spi%d.%d was reset\n", + CONFIG_SX151X_SPI_BUS, chip); + } + return ret; + } + + if (argc < 4) + return CMD_RET_USAGE; + + /* arg3 used as gpio number */ + gpio = simple_strtoul(argv[3], NULL, 10); + + if (strcmp(argv[1], "get") == 0) { + ret = sx151x_get_value(chip, gpio); + if (ret < 0) + printf("Failed to get value at spi%d.%d gpio %d\n", + CONFIG_SX151X_SPI_BUS, chip, gpio); + else { + printf("Value at spi%d.%d gpio %d is %d\n", + CONFIG_SX151X_SPI_BUS, chip, gpio, ret); + ret = 0; + } + return ret; + } + + if (argc < 5) + return CMD_RET_USAGE; + + /* arg4 used as value or direction */ + val = simple_strtoul(argv[4], NULL, 10); + + if (strcmp(argv[1], "set") == 0) { + ret = sx151x_set_value(chip, gpio, val); + if (ret < 0) + printf("Failed to set value at spi%d.%d gpio %d\n", + CONFIG_SX151X_SPI_BUS, chip, gpio); + else + printf("New value at spi%d.%d gpio %d is %d\n", + CONFIG_SX151X_SPI_BUS, chip, gpio, val); + return ret; + } else if (strcmp(argv[1], "dir") == 0) { + if (val == 0) + ret = sx151x_direction_output(chip, gpio); + else + ret = sx151x_direction_input(chip, gpio); + + if (ret < 0) + printf("Failed to set direction of spi%d.%d gpio %d\n", + CONFIG_SX151X_SPI_BUS, chip, gpio); + else + printf("New direction of spi%d.%d gpio %d is %d\n", + CONFIG_SX151X_SPI_BUS, chip, gpio, val); + return ret; + } + + printf("Please see usage\n"); + + return ret; +} + +U_BOOT_CMD( + sx151x, 5, 1, do_sx151x, + "sx151x gpio access", + "dir chip gpio 0|1\n" + " - set gpio direction (0 for output, 1 for input)\n" + "sx151x get chip gpio\n" + " - get gpio value\n" + "sx151x set chip gpio 0|1\n" + " - set gpio value\n" + "sx151x reset chip\n" + " - reset chip" +); + +#endif /* CONFIG_CMD_SX151X */ diff --git a/qemu/roms/u-boot/drivers/gpio/tca642x.c b/qemu/roms/u-boot/drivers/gpio/tca642x.c new file mode 100644 index 000000000..6386835d5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/tca642x.c @@ -0,0 +1,333 @@ +/* + * Copyright 2013 Texas Instruments, Inc. + * Author: Dan Murphy + * + * Derived work from the pca953x.c driver + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +/* tca642x register address definitions */ +struct tca642x_bank_info tca642x_regs[] = { + { .input_reg = 0x00, + .output_reg = 0x04, + .polarity_reg = 0x08, + .configuration_reg = 0x0c }, + { .input_reg = 0x01, + .output_reg = 0x05, + .polarity_reg = 0x09, + .configuration_reg = 0x0d }, + { .input_reg = 0x02, + .output_reg = 0x06, + .polarity_reg = 0x0a, + .configuration_reg = 0x0e }, +}; + +/* + * Modify masked bits in register + */ +static int tca642x_reg_write(uchar chip, uint8_t addr, + uint8_t reg_bit, uint8_t data) +{ + uint8_t valw; + int org_bus_num; + int ret; + + org_bus_num = i2c_get_bus_num(); + i2c_set_bus_num(CONFIG_SYS_I2C_TCA642X_BUS_NUM); + + if (i2c_read(chip, addr, 1, (uint8_t *)&valw, 1)) { + printf("Could not read before writing\n"); + ret = -1; + goto error; + } + valw &= ~reg_bit; + valw |= data; + + ret = i2c_write(chip, addr, 1, (u8 *)&valw, 1); + +error: + i2c_set_bus_num(org_bus_num); + return ret; +} + +static int tca642x_reg_read(uchar chip, uint8_t addr, uint8_t *data) +{ + uint8_t valw; + int org_bus_num; + int ret = 0; + + org_bus_num = i2c_get_bus_num(); + i2c_set_bus_num(CONFIG_SYS_I2C_TCA642X_BUS_NUM); + if (i2c_read(chip, addr, 1, (u8 *)&valw, 1)) { + ret = -1; + goto error; + } + + *data = valw; + +error: + i2c_set_bus_num(org_bus_num); + return ret; +} + +/* + * Set output value of IO pins in 'reg_bit' to corresponding value in 'data' + * 0 = low, 1 = high + */ +int tca642x_set_val(uchar chip, uint8_t gpio_bank, + uint8_t reg_bit, uint8_t data) +{ + uint8_t out_reg = tca642x_regs[gpio_bank].output_reg; + + return tca642x_reg_write(chip, out_reg, reg_bit, data); +} + +/* + * Set read polarity of IO pins in 'reg_bit' to corresponding value in 'data' + * 0 = read pin value, 1 = read inverted pin value + */ +int tca642x_set_pol(uchar chip, uint8_t gpio_bank, + uint8_t reg_bit, uint8_t data) +{ + uint8_t pol_reg = tca642x_regs[gpio_bank].polarity_reg; + + return tca642x_reg_write(chip, pol_reg, reg_bit, data); +} + +/* + * Set direction of IO pins in 'reg_bit' to corresponding value in 'data' + * 0 = output, 1 = input + */ +int tca642x_set_dir(uchar chip, uint8_t gpio_bank, + uint8_t reg_bit, uint8_t data) +{ + uint8_t config_reg = tca642x_regs[gpio_bank].configuration_reg; + + return tca642x_reg_write(chip, config_reg, reg_bit, data); +} + +/* + * Read current logic level of all IO pins + */ +int tca642x_get_val(uchar chip, uint8_t gpio_bank) +{ + uint8_t val; + uint8_t in_reg = tca642x_regs[gpio_bank].input_reg; + + if (tca642x_reg_read(chip, in_reg, &val) < 0) + return -1; + + return (int)val; +} + +/* + * Set the inital register states for the tca642x gpio expander + */ +int tca642x_set_inital_state(uchar chip, struct tca642x_bank_info init_data[]) +{ + int i, ret; + uint8_t config_reg; + uint8_t polarity_reg; + uint8_t output_reg; + + for (i = 0; i < 3; i++) { + config_reg = tca642x_regs[i].configuration_reg; + ret = tca642x_reg_write(chip, config_reg, 0xff, + init_data[i].configuration_reg); + polarity_reg = tca642x_regs[i].polarity_reg; + ret = tca642x_reg_write(chip, polarity_reg, 0xff, + init_data[i].polarity_reg); + output_reg = tca642x_regs[i].output_reg; + ret = tca642x_reg_write(chip, output_reg, 0xff, + init_data[i].output_reg); + } + + return ret; +} + +#ifdef CONFIG_CMD_TCA642X +/* + * Display tca642x information + */ +static int tca642x_info(uchar chip) +{ + int i, j; + uint8_t data; + + printf("tca642x@ 0x%x (%d pins):\n", chip, 24); + for (i = 0; i < 3; i++) { + printf("Bank %i\n", i); + if (tca642x_reg_read(chip, + tca642x_regs[i].configuration_reg, + &data) < 0) + return -1; + printf("\tConfiguration: "); + for (j = 7; j >= 0; j--) + printf("%c", data & (1 << j) ? 'i' : 'o'); + printf("\n"); + + if (tca642x_reg_read(chip, + tca642x_regs[i].polarity_reg, &data) < 0) + return -1; + printf("\tPolarity: "); + for (j = 7; j >= 0; j--) + printf("%c", data & (1 << j) ? '1' : '0'); + printf("\n"); + + if (tca642x_reg_read(chip, + tca642x_regs[i].input_reg, &data) < 0) + return -1; + printf("\tInput value: "); + for (j = 7; j >= 0; j--) + printf("%c", data & (1 << j) ? '1' : '0'); + printf("\n"); + + if (tca642x_reg_read(chip, + tca642x_regs[i].output_reg, &data) < 0) + return -1; + printf("\tOutput value: "); + for (j = 7; j >= 0; j--) + printf("%c", data & (1 << j) ? '1' : '0'); + printf("\n"); + } + + return 0; +} + +cmd_tbl_t cmd_tca642x[] = { + U_BOOT_CMD_MKENT(device, 3, 0, (void *)TCA642X_CMD_DEVICE, "", ""), + U_BOOT_CMD_MKENT(output, 4, 0, (void *)TCA642X_CMD_OUTPUT, "", ""), + U_BOOT_CMD_MKENT(input, 3, 0, (void *)TCA642X_CMD_INPUT, "", ""), + U_BOOT_CMD_MKENT(invert, 4, 0, (void *)TCA642X_CMD_INVERT, "", ""), + U_BOOT_CMD_MKENT(info, 2, 0, (void *)TCA642X_CMD_INFO, "", ""), +}; + +int do_tca642x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + static uchar chip = CONFIG_SYS_I2C_TCA642X_ADDR; + int ret = CMD_RET_USAGE, val; + uint8_t gpio_bank = 0; + uint8_t bank_shift; + ulong ul_arg2 = 0; + ulong ul_arg3 = 0; + cmd_tbl_t *c; + + c = find_cmd_tbl(argv[1], cmd_tca642x, ARRAY_SIZE(cmd_tca642x)); + + /* All commands but "device" require 'maxargs' arguments */ + if (!c || + !((argc == (c->maxargs)) || + (((int)c->cmd == TCA642X_CMD_DEVICE) && + (argc == (c->maxargs - 1))))) { + return CMD_RET_USAGE; + } + + /* arg2 used as chip number or pin number */ + if (argc > 2) + ul_arg2 = simple_strtoul(argv[2], NULL, 10); + + /* arg3 used as pin or invert value */ + if (argc > 3) { + ul_arg3 = simple_strtoul(argv[3], NULL, 10) & 0x1; + if (ul_arg2 <= 7) { + gpio_bank = 0; + } else if ((ul_arg2 >= 10) && (ul_arg2 <= 17)) { + gpio_bank = 1; + } else if ((ul_arg2 >= 20) && (ul_arg2 <= 27)) { + gpio_bank = 2; + } else { + printf("Requested pin is not available\n"); + ret = CMD_RET_FAILURE; + goto error; + } + } + + switch ((int)c->cmd) { + case TCA642X_CMD_INFO: + ret = tca642x_info(chip); + if (ret) + ret = CMD_RET_FAILURE; + break; + + case TCA642X_CMD_DEVICE: + if (argc == 3) + chip = (uint8_t)ul_arg2; + printf("Current device address: 0x%x\n", chip); + ret = CMD_RET_SUCCESS; + break; + + case TCA642X_CMD_INPUT: + bank_shift = ul_arg2 - (gpio_bank * 10); + ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift), + TCA642X_DIR_IN << bank_shift); + val = (tca642x_get_val(chip, gpio_bank) & + (1 << bank_shift)) != 0; + + if (ret) + ret = CMD_RET_FAILURE; + else + printf("chip 0x%02x, pin 0x%lx = %d\n", chip, + ul_arg2, val); + break; + + case TCA642X_CMD_OUTPUT: + bank_shift = ul_arg2 - (gpio_bank * 10); + ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift), + (TCA642X_DIR_OUT << bank_shift)); + if (!ret) + ret = tca642x_set_val(chip, + gpio_bank, (1 << bank_shift), + (ul_arg3 << bank_shift)); + if (ret) + ret = CMD_RET_FAILURE; + break; + + case TCA642X_CMD_INVERT: + bank_shift = ul_arg2 - (gpio_bank * 10); + ret = tca642x_set_pol(chip, gpio_bank, (1 << bank_shift), + (ul_arg3 << bank_shift)); + if (ret) + ret = CMD_RET_FAILURE; + break; + } +error: + if (ret == CMD_RET_FAILURE) + eprintf("Error talking to chip at 0x%x\n", chip); + + return ret; +} + +U_BOOT_CMD( + tca642x, 5, 1, do_tca642x, + "tca642x gpio access", + "device [dev]\n" + " - show or set current device address\n" + "tca642x info\n" + " - display info for current chip\n" + "tca642x output pin 0|1\n" + " - set pin as output and drive low or high\n" + "tca642x invert pin 0|1\n" + " - disable/enable polarity inversion for reads\n" + "tca642x input pin\n" + " - set pin as input and read value" +); + +#endif /* CONFIG_CMD_TCA642X */ diff --git a/qemu/roms/u-boot/drivers/gpio/tegra_gpio.c b/qemu/roms/u-boot/drivers/gpio/tegra_gpio.c new file mode 100644 index 000000000..82b30d5ab --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/tegra_gpio.c @@ -0,0 +1,246 @@ +/* + * NVIDIA Tegra20 GPIO handling. + * (C) Copyright 2010-2012 + * NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Based on (mostly copied from) kw_gpio.c based Linux 2.6 kernel driver. + * Tom Warren (twarren@nvidia.com) + */ + +#include +#include +#include +#include +#include + +enum { + TEGRA_CMD_INFO, + TEGRA_CMD_PORT, + TEGRA_CMD_OUTPUT, + TEGRA_CMD_INPUT, +}; + +static struct gpio_names { + char name[GPIO_NAME_SIZE]; +} gpio_names[MAX_NUM_GPIOS]; + +static char *get_name(int i) +{ + return *gpio_names[i].name ? gpio_names[i].name : "UNKNOWN"; +} + +/* Return config of pin 'gpio' as GPIO (1) or SFPIO (0) */ +static int get_config(unsigned gpio) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + int type; + + u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); + type = (u >> GPIO_BIT(gpio)) & 1; + + debug("get_config: port = %d, bit = %d is %s\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); + + return type; +} + +/* Config pin 'gpio' as GPIO or SFPIO, based on 'type' */ +static void set_config(unsigned gpio, int type) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + + debug("set_config: port = %d, bit = %d, %s\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); + + u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); + if (type) /* GPIO */ + u |= 1 << GPIO_BIT(gpio); + else + u &= ~(1 << GPIO_BIT(gpio)); + writel(u, &bank->gpio_config[GPIO_PORT(gpio)]); +} + +/* Return GPIO pin 'gpio' direction - 0 = input or 1 = output */ +static int get_direction(unsigned gpio) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + int dir; + + u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]); + dir = (u >> GPIO_BIT(gpio)) & 1; + + debug("get_direction: port = %d, bit = %d, %s\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), dir ? "OUT" : "IN"); + + return dir; +} + +/* Config GPIO pin 'gpio' as input or output (OE) as per 'output' */ +static void set_direction(unsigned gpio, int output) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + + debug("set_direction: port = %d, bit = %d, %s\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), output ? "OUT" : "IN"); + + u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]); + if (output) + u |= 1 << GPIO_BIT(gpio); + else + u &= ~(1 << GPIO_BIT(gpio)); + writel(u, &bank->gpio_dir_out[GPIO_PORT(gpio)]); +} + +/* set GPIO pin 'gpio' output bit as 0 or 1 as per 'high' */ +static void set_level(unsigned gpio, int high) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + + debug("set_level: port = %d, bit %d == %d\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), high); + + u = readl(&bank->gpio_out[GPIO_PORT(gpio)]); + if (high) + u |= 1 << GPIO_BIT(gpio); + else + u &= ~(1 << GPIO_BIT(gpio)); + writel(u, &bank->gpio_out[GPIO_PORT(gpio)]); +} + +/* + * Generic_GPIO primitives. + */ + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= MAX_NUM_GPIOS) + return -1; + + if (label != NULL) { + strncpy(gpio_names[gpio].name, label, GPIO_NAME_SIZE); + gpio_names[gpio].name[GPIO_NAME_SIZE - 1] = '\0'; + } + + /* Configure as a GPIO */ + set_config(gpio, 1); + + return 0; +} + +int gpio_free(unsigned gpio) +{ + if (gpio >= MAX_NUM_GPIOS) + return -1; + + gpio_names[gpio].name[0] = '\0'; + /* Do not configure as input or change pin mux here */ + return 0; +} + +/* read GPIO OUT value of pin 'gpio' */ +static int gpio_get_output_value(unsigned gpio) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + int val; + + debug("gpio_get_output_value: pin = %d (port %d:bit %d)\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); + + val = readl(&bank->gpio_out[GPIO_PORT(gpio)]); + + return (val >> GPIO_BIT(gpio)) & 1; +} + +/* set GPIO pin 'gpio' as an input */ +int gpio_direction_input(unsigned gpio) +{ + debug("gpio_direction_input: pin = %d (port %d:bit %d)\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); + + /* Configure GPIO direction as input. */ + set_direction(gpio, 0); + + return 0; +} + +/* set GPIO pin 'gpio' as an output, with polarity 'value' */ +int gpio_direction_output(unsigned gpio, int value) +{ + debug("gpio_direction_output: pin = %d (port %d:bit %d) = %s\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), + value ? "HIGH" : "LOW"); + + /* Configure GPIO output value. */ + set_level(gpio, value); + + /* Configure GPIO direction as output. */ + set_direction(gpio, 1); + + return 0; +} + +/* read GPIO IN value of pin 'gpio' */ +int gpio_get_value(unsigned gpio) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + int val; + + debug("gpio_get_value: pin = %d (port %d:bit %d)\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); + + val = readl(&bank->gpio_in[GPIO_PORT(gpio)]); + + return (val >> GPIO_BIT(gpio)) & 1; +} + +/* write GPIO OUT value to pin 'gpio' */ +int gpio_set_value(unsigned gpio, int value) +{ + debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value); + + /* Configure GPIO output value. */ + set_level(gpio, value); + + return 0; +} + +/* + * Display Tegra GPIO information + */ +void gpio_info(void) +{ + unsigned c; + int type; + + for (c = 0; c < MAX_NUM_GPIOS; c++) { + type = get_config(c); /* GPIO, not SFPIO */ + if (type) { + printf("GPIO_%d:\t%s is an %s, ", c, + get_name(c), + get_direction(c) ? "OUTPUT" : "INPUT"); + if (get_direction(c)) + printf("value = %d", gpio_get_output_value(c)); + else + printf("value = %d", gpio_get_value(c)); + printf("\n"); + } else + continue; + } +} diff --git a/qemu/roms/u-boot/drivers/gpio/xilinx_gpio.c b/qemu/roms/u-boot/drivers/gpio/xilinx_gpio.c new file mode 100644 index 000000000..949fd96fe --- /dev/null +++ b/qemu/roms/u-boot/drivers/gpio/xilinx_gpio.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2013 Xilinx, Michal Simek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(gpio_list); + +enum gpio_direction { + GPIO_DIRECTION_OUT = 0, + GPIO_DIRECTION_IN = 1, +}; + +/* Gpio simple map */ +struct gpio_regs { + u32 gpiodata; + u32 gpiodir; +}; + +#define GPIO_NAME_SIZE 10 + +struct gpio_names { + char name[GPIO_NAME_SIZE]; +}; + +/* Initialized, rxbd_current, rx_first_buf must be 0 after init */ +struct xilinx_gpio_priv { + struct gpio_regs *regs; + u32 gpio_min; + u32 gpio_max; + u32 gpiodata_store; + char name[GPIO_NAME_SIZE]; + struct list_head list; + struct gpio_names *gpio_name; +}; + +/* Store number of allocated gpio pins */ +static u32 xilinx_gpio_max; + +/* Get associated gpio controller */ +static struct xilinx_gpio_priv *gpio_get_controller(unsigned gpio) +{ + struct list_head *entry; + struct xilinx_gpio_priv *priv = NULL; + + list_for_each(entry, &gpio_list) { + priv = list_entry(entry, struct xilinx_gpio_priv, list); + if (gpio >= priv->gpio_min && gpio <= priv->gpio_max) { + debug("%s: reg: %x, min-max: %d-%d\n", __func__, + (u32)priv->regs, priv->gpio_min, priv->gpio_max); + return priv; + } + } + puts("!!!Can't get gpio controller!!!\n"); + return NULL; +} + +/* Get gpio pin name if used/setup */ +static char *get_name(unsigned gpio) +{ + u32 gpio_priv; + struct xilinx_gpio_priv *priv; + + debug("%s\n", __func__); + + priv = gpio_get_controller(gpio); + if (priv) { + gpio_priv = gpio - priv->gpio_min; + + return *priv->gpio_name[gpio_priv].name ? + priv->gpio_name[gpio_priv].name : "UNKNOWN"; + } + return "UNKNOWN"; +} + +/* Get output value */ +static int gpio_get_output_value(unsigned gpio) +{ + u32 val, gpio_priv; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + gpio_priv = gpio - priv->gpio_min; + val = !!(priv->gpiodata_store & (1 << gpio_priv)); + debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + + return val; + } + return -1; +} + +/* Get input value */ +static int gpio_get_input_value(unsigned gpio) +{ + u32 val, gpio_priv; + struct gpio_regs *regs; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + regs = priv->regs; + gpio_priv = gpio - priv->gpio_min; + val = readl(®s->gpiodata); + val = !!(val & (1 << gpio_priv)); + debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + + return val; + } + return -1; +} + +/* Set gpio direction */ +static int gpio_set_direction(unsigned gpio, enum gpio_direction direction) +{ + u32 val, gpio_priv; + struct gpio_regs *regs; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + regs = priv->regs; + val = readl(®s->gpiodir); + + gpio_priv = gpio - priv->gpio_min; + if (direction == GPIO_DIRECTION_OUT) + val &= ~(1 << gpio_priv); + else + val |= 1 << gpio_priv; + + writel(val, ®s->gpiodir); + debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + + return 0; + } + + return -1; +} + +/* Get gpio direction */ +static int gpio_get_direction(unsigned gpio) +{ + u32 val, gpio_priv; + struct gpio_regs *regs; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + regs = priv->regs; + gpio_priv = gpio - priv->gpio_min; + val = readl(®s->gpiodir); + val = !!(val & (1 << gpio_priv)); + debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + + return val; + } + + return -1; +} + +/* + * Get input value + * for example gpio setup to output only can't get input value + * which is breaking gpio toggle command + */ +int gpio_get_value(unsigned gpio) +{ + u32 val; + + if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT) + val = gpio_get_output_value(gpio); + else + val = gpio_get_input_value(gpio); + + return val; +} + +/* Set output value */ +static int gpio_set_output_value(unsigned gpio, int value) +{ + u32 val, gpio_priv; + struct gpio_regs *regs; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + regs = priv->regs; + gpio_priv = gpio - priv->gpio_min; + val = priv->gpiodata_store; + if (value) + val |= 1 << gpio_priv; + else + val &= ~(1 << gpio_priv); + + writel(val, ®s->gpiodata); + debug("%s: reg: %x, gpio_no: %d, output_val: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + priv->gpiodata_store = val; + + return 0; + } + + return -1; +} + +int gpio_set_value(unsigned gpio, int value) +{ + if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT) + return gpio_set_output_value(gpio, value); + + return -1; +} + +/* Set GPIO as input */ +int gpio_direction_input(unsigned gpio) +{ + debug("%s\n", __func__); + return gpio_set_direction(gpio, GPIO_DIRECTION_IN); +} + +/* Setup GPIO as output and set output value */ +int gpio_direction_output(unsigned gpio, int value) +{ + int ret = gpio_set_direction(gpio, GPIO_DIRECTION_OUT); + + debug("%s\n", __func__); + + if (ret < 0) + return ret; + + return gpio_set_output_value(gpio, value); +} + +/* Show gpio status */ +void gpio_info(void) +{ + unsigned gpio; + + struct list_head *entry; + struct xilinx_gpio_priv *priv = NULL; + + list_for_each(entry, &gpio_list) { + priv = list_entry(entry, struct xilinx_gpio_priv, list); + printf("\n%s: %s/%x (%d-%d)\n", __func__, priv->name, + (u32)priv->regs, priv->gpio_min, priv->gpio_max); + + for (gpio = priv->gpio_min; gpio <= priv->gpio_max; gpio++) { + printf("GPIO_%d:\t%s is an ", gpio, get_name(gpio)); + if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT) + printf("OUTPUT value = %d\n", + gpio_get_output_value(gpio)); + else + printf("INPUT value = %d\n", + gpio_get_input_value(gpio)); + } + } +} + +int gpio_request(unsigned gpio, const char *label) +{ + u32 gpio_priv; + struct xilinx_gpio_priv *priv; + + if (gpio >= xilinx_gpio_max) + return -EINVAL; + + priv = gpio_get_controller(gpio); + if (priv) { + gpio_priv = gpio - priv->gpio_min; + + if (label != NULL) { + strncpy(priv->gpio_name[gpio_priv].name, label, + GPIO_NAME_SIZE); + priv->gpio_name[gpio_priv].name[GPIO_NAME_SIZE - 1] = + '\0'; + } + return 0; + } + + return -1; +} + +int gpio_free(unsigned gpio) +{ + u32 gpio_priv; + struct xilinx_gpio_priv *priv; + + if (gpio >= xilinx_gpio_max) + return -EINVAL; + + priv = gpio_get_controller(gpio); + if (priv) { + gpio_priv = gpio - priv->gpio_min; + priv->gpio_name[gpio_priv].name[0] = '\0'; + + /* Do nothing here */ + return 0; + } + + return -1; +} + +int gpio_alloc(u32 baseaddr, const char *name, u32 gpio_no) +{ + struct xilinx_gpio_priv *priv; + + priv = calloc(1, sizeof(struct xilinx_gpio_priv)); + + /* Setup gpio name */ + if (name != NULL) { + strncpy(priv->name, name, GPIO_NAME_SIZE); + priv->name[GPIO_NAME_SIZE - 1] = '\0'; + } + priv->regs = (struct gpio_regs *)baseaddr; + + priv->gpio_min = xilinx_gpio_max; + xilinx_gpio_max = priv->gpio_min + gpio_no; + priv->gpio_max = xilinx_gpio_max - 1; + + priv->gpio_name = calloc(gpio_no, sizeof(struct gpio_names)); + + INIT_LIST_HEAD(&priv->list); + list_add_tail(&priv->list, &gpio_list); + + printf("%s: Add %s (%d-%d)\n", __func__, name, + priv->gpio_min, priv->gpio_max); + + /* Return the first gpio allocated for this device */ + return priv->gpio_min; +} + +/* Dual channel gpio is one IP with two independent channels */ +int gpio_alloc_dual(u32 baseaddr, const char *name, u32 gpio_no0, u32 gpio_no1) +{ + int ret; + + ret = gpio_alloc(baseaddr, name, gpio_no0); + gpio_alloc(baseaddr + 8, strcat((char *)name, "_1"), gpio_no1); + + /* Return the first gpio allocated for this device */ + return ret; +} diff --git a/qemu/roms/u-boot/drivers/hwmon/Makefile b/qemu/roms/u-boot/drivers/hwmon/Makefile new file mode 100644 index 000000000..25b8e8a2d --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/Makefile @@ -0,0 +1,21 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2001 +# Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +#ccflags-y += -DDEBUG + +obj-$(CONFIG_DTT_ADM1021) += adm1021.o +obj-$(CONFIG_DTT_ADT7460) += adt7460.o +obj-$(CONFIG_DTT_DS1621) += ds1621.o +obj-$(CONFIG_DTT_DS1722) += ds1722.o +obj-$(CONFIG_DTT_DS1775) += ds1775.o +obj-$(CONFIG_DTT_LM63) += lm63.o +obj-$(CONFIG_DTT_LM73) += lm73.o +obj-$(CONFIG_DTT_LM75) += lm75.o +obj-$(CONFIG_DTT_LM81) += lm81.o diff --git a/qemu/roms/u-boot/drivers/hwmon/adm1021.c b/qemu/roms/u-boot/drivers/hwmon/adm1021.c new file mode 100644 index 000000000..99e942b49 --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/adm1021.c @@ -0,0 +1,164 @@ +/* + * (C) Copyright 2003 + * Murray Jensen, CSIRO-MIT, Murray.Jensen@csiro.au + * + * based on dtt/lm75.c which is ... + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Analog Devices's ADM1021 + * "Low Cost Microprocessor System Temperature Monitor" + */ + +#include + +#include +#include + +#define DTT_READ_LOC_VALUE 0x00 +#define DTT_READ_REM_VALUE 0x01 +#define DTT_READ_STATUS 0x02 +#define DTT_READ_CONFIG 0x03 +#define DTT_READ_CONVRATE 0x04 +#define DTT_READ_LOC_HIGHLIM 0x05 +#define DTT_READ_LOC_LOWLIM 0x06 +#define DTT_READ_REM_HIGHLIM 0x07 +#define DTT_READ_REM_LOWLIM 0x08 +#define DTT_READ_DEVID 0xfe + +#define DTT_WRITE_CONFIG 0x09 +#define DTT_WRITE_CONVRATE 0x0a +#define DTT_WRITE_LOC_HIGHLIM 0x0b +#define DTT_WRITE_LOC_LOWLIM 0x0c +#define DTT_WRITE_REM_HIGHLIM 0x0d +#define DTT_WRITE_REM_LOWLIM 0x0e +#define DTT_WRITE_ONESHOT 0x0f + +#define DTT_STATUS_BUSY 0x80 /* 1=ADC Converting */ +#define DTT_STATUS_LHIGH 0x40 /* 1=Local High Temp Limit Tripped */ +#define DTT_STATUS_LLOW 0x20 /* 1=Local Low Temp Limit Tripped */ +#define DTT_STATUS_RHIGH 0x10 /* 1=Remote High Temp Limit Tripped */ +#define DTT_STATUS_RLOW 0x08 /* 1=Remote Low Temp Limit Tripped */ +#define DTT_STATUS_OPEN 0x04 /* 1=Remote Sensor Open-Circuit */ + +#define DTT_CONFIG_ALERT_MASKED 0x80 /* 0=ALERT Enabled, 1=ALERT Masked */ +#define DTT_CONFIG_STANDBY 0x40 /* 0=Run, 1=Standby */ + +#define DTT_ADM1021_DEVID 0x41 + +typedef + struct { + uint i2c_addr:7; /* 7bit i2c chip address */ + uint conv_rate:3; /* conversion rate */ + uint enable_alert:1; /* enable alert output pin */ + uint enable_local:1; /* enable internal temp sensor */ + uint max_local:8; /* internal temp maximum */ + uint min_local:8; /* internal temp minimum */ + uint enable_remote:1; /* enable remote temp sensor */ + uint max_remote:8; /* remote temp maximum */ + uint min_remote:8; /* remote temp minimum */ + } +dtt_cfg_t; + +dtt_cfg_t dttcfg[] = CONFIG_SYS_DTT_ADM1021; + +int +dtt_read (int sensor, int reg) +{ + dtt_cfg_t *dcp = &dttcfg[sensor >> 1]; + uchar data; + + if (i2c_read(dcp->i2c_addr, reg, 1, &data, 1) != 0) + return -1; + + return (int)data; +} /* dtt_read() */ + +int +dtt_write (int sensor, int reg, int val) +{ + dtt_cfg_t *dcp = &dttcfg[sensor >> 1]; + uchar data; + + data = (uchar)(val & 0xff); + + if (i2c_write(dcp->i2c_addr, reg, 1, &data, 1) != 0) + return 1; + + return 0; +} /* dtt_write() */ + +int +dtt_init_one(int sensor) +{ + dtt_cfg_t *dcp = &dttcfg[sensor >> 1]; + int reg, val; + + if (((sensor & 1) == 0 ? dcp->enable_local : dcp->enable_remote) == 0) + return 1; /* sensor is disabled (or rather ignored) */ + + /* + * Setup High Limit register + */ + if ((sensor & 1) == 0) { + reg = DTT_WRITE_LOC_HIGHLIM; + val = dcp->max_local; + } + else { + reg = DTT_WRITE_REM_HIGHLIM; + val = dcp->max_remote; + } + if (dtt_write (sensor, reg, val) != 0) + return 1; + + /* + * Setup Low Limit register + */ + if ((sensor & 1) == 0) { + reg = DTT_WRITE_LOC_LOWLIM; + val = dcp->min_local; + } + else { + reg = DTT_WRITE_REM_LOWLIM; + val = dcp->min_remote; + } + if (dtt_write (sensor, reg, val) != 0) + return 1; + + /* shouldn't hurt if the rest gets done twice */ + + /* + * Setup Conversion Rate register + */ + if (dtt_write (sensor, DTT_WRITE_CONVRATE, dcp->conv_rate) != 0) + return 1; + + /* + * Setup configuraton register + */ + val = 0; /* running */ + if (dcp->enable_alert == 0) + val |= DTT_CONFIG_ALERT_MASKED; /* mask ALERT pin */ + if (dtt_write (sensor, DTT_WRITE_CONFIG, val) != 0) + return 1; + + return 0; +} /* dtt_init_one() */ + +int +dtt_get_temp (int sensor) +{ + signed char val; + + if ((sensor & 1) == 0) + val = dtt_read(sensor, DTT_READ_LOC_VALUE); + else + val = dtt_read(sensor, DTT_READ_REM_VALUE); + + return (int) val; +} /* dtt_get_temp() */ diff --git a/qemu/roms/u-boot/drivers/hwmon/adt7460.c b/qemu/roms/u-boot/drivers/hwmon/adt7460.c new file mode 100644 index 000000000..fd05c1779 --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/adt7460.c @@ -0,0 +1,73 @@ +/* + * (C) Copyright 2008 + * Ricado Ribalda-Universidad Autonoma de Madrid, ricardo.ribalda@uam.es + * This work has been supported by: QTechnology http://qtec.com/ + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define ADT7460_ADDRESS 0x2c +#define ADT7460_INVALID 128 +#define ADT7460_CONFIG 0x40 +#define ADT7460_REM1_TEMP 0x25 +#define ADT7460_LOCAL_TEMP 0x26 +#define ADT7460_REM2_TEMP 0x27 + +int dtt_read(int sensor, int reg) +{ + u8 dir = reg; + u8 data; + + if (i2c_read(ADT7460_ADDRESS, dir, 1, &data, 1) == -1) + return -1; + if (data == ADT7460_INVALID) + return -1; + + return data; +} + +int dtt_write(int sensor, int reg, int val) +{ + u8 dir = reg; + u8 data = val; + + if (i2c_write(ADT7460_ADDRESS, dir, 1, &data, 1) == -1) + return -1; + + return 0; +} + +int dtt_init_one(int sensor) +{ + printf("ADT7460 at I2C address 0x%2x\n", ADT7460_ADDRESS); + + if (dtt_write(0, ADT7460_CONFIG, 1) == -1) { + puts("Error initialiting ADT7460\n"); + return -1; + } + + return 0; +} + +int dtt_get_temp(int sensor) +{ + int aux; + u8 table[] = + { ADT7460_REM1_TEMP, ADT7460_LOCAL_TEMP, ADT7460_REM2_TEMP }; + + if (sensor > 2) { + puts("DTT sensor does not exist\n"); + return -1; + } + + aux = dtt_read(0, table[sensor]); + if (aux == -1) { + puts("DTT temperature read failed\n"); + return -1; + } + + return aux; +} diff --git a/qemu/roms/u-boot/drivers/hwmon/ds1621.c b/qemu/roms/u-boot/drivers/hwmon/ds1621.c new file mode 100644 index 000000000..66947a664 --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/ds1621.c @@ -0,0 +1,155 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Dallas Semiconductor's DS1621/1631 Digital Thermometer and Thermostat. + */ + +#include +#include +#include + +/* + * Device code + */ +#define DTT_I2C_DEV_CODE 0x48 /* Dallas Semi's DS1621 */ +#define DTT_READ_TEMP 0xAA +#define DTT_READ_COUNTER 0xA8 +#define DTT_READ_SLOPE 0xA9 +#define DTT_WRITE_START_CONV 0xEE +#define DTT_WRITE_STOP_CONV 0x22 +#define DTT_TEMP_HIGH 0xA1 +#define DTT_TEMP_LOW 0xA2 +#define DTT_CONFIG 0xAC + +/* + * Config register bits + */ +#define DTT_CONFIG_1SHOT 0x01 +#define DTT_CONFIG_POLARITY 0x02 +#define DTT_CONFIG_R0 0x04 /* ds1631 only */ +#define DTT_CONFIG_R1 0x08 /* ds1631 only */ +#define DTT_CONFIG_NVB 0x10 +#define DTT_CONFIG_TLF 0x20 +#define DTT_CONFIG_THF 0x40 +#define DTT_CONFIG_DONE 0x80 + + +int dtt_read(int sensor, int reg) +{ + int dlen; + uchar data[2]; + + /* Calculate sensor address and command */ + sensor = DTT_I2C_DEV_CODE + (sensor & 0x07); /* Calculate addr of ds1621*/ + + /* Prepare to handle 2 byte result */ + switch(reg) { + case DTT_READ_TEMP: + case DTT_TEMP_HIGH: + case DTT_TEMP_LOW: + dlen = 2; + break; + default: + dlen = 1; + } + + /* Now try to read the register */ + if (i2c_read(sensor, reg, 1, data, dlen) != 0) + return 1; + + /* Handle 2 byte result */ + if (dlen == 2) + return (short)((data[0] << 8) | data[1]); + + return (int)data[0]; +} + + +int dtt_write(int sensor, int reg, int val) +{ + int dlen; + uchar data[2]; + + /* Calculate sensor address and register */ + sensor = DTT_I2C_DEV_CODE + (sensor & 0x07); + + /* Handle various data sizes. */ + switch(reg) { + case DTT_READ_TEMP: + case DTT_TEMP_HIGH: + case DTT_TEMP_LOW: + dlen = 2; + data[0] = (char)((val >> 8) & 0xff); /* MSB first */ + data[1] = (char)(val & 0xff); + break; + case DTT_WRITE_START_CONV: + case DTT_WRITE_STOP_CONV: + dlen = 0; + data[0] = (char)0; + data[1] = (char)0; + break; + default: + dlen = 1; + data[0] = (char)(val & 0xff); + } + + /* Write value to device */ + if (i2c_write(sensor, reg, 1, data, dlen) != 0) + return 1; + + /* Poll NV memory busy bit in case write was to register stored in EEPROM */ + while(i2c_reg_read(sensor, DTT_CONFIG) & DTT_CONFIG_NVB) + ; + + return 0; +} + + +int dtt_init_one(int sensor) +{ + int val; + + /* Setup High Temp */ + val = ((CONFIG_SYS_DTT_MAX_TEMP * 2) << 7) & 0xff80; + if (dtt_write(sensor, DTT_TEMP_HIGH, val) != 0) + return 1; + + /* Setup Low Temp - hysteresis */ + val = (((CONFIG_SYS_DTT_MAX_TEMP - CONFIG_SYS_DTT_HYSTERESIS) * 2) << 7) & 0xff80; + if (dtt_write(sensor, DTT_TEMP_LOW, val) != 0) + return 1; + + /* + * Setup configuraton register + * + * Clear THF & TLF, Reserved = 1, Polarity = Active Low, One Shot = YES + * + * We run in polled mode, since there isn't any way to know if this + * lousy device is ready to provide temperature readings on power up. + */ + val = 0x9; + if (dtt_write(sensor, DTT_CONFIG, val) != 0) + return 1; + + return 0; +} + +int dtt_get_temp(int sensor) +{ + int i; + + /* Start a conversion, may take up to 1 second. */ + dtt_write(sensor, DTT_WRITE_START_CONV, 0); + for (i = 0; i <= 10; i++) { + udelay(100000); + if (dtt_read(sensor, DTT_CONFIG) & DTT_CONFIG_DONE) + break; + } + + return (dtt_read(sensor, DTT_READ_TEMP) / 256); +} diff --git a/qemu/roms/u-boot/drivers/hwmon/ds1722.c b/qemu/roms/u-boot/drivers/hwmon/ds1722.c new file mode 100644 index 000000000..c46958846 --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/ds1722.c @@ -0,0 +1,137 @@ +#include +#include +#include + +static void ds1722_select(int dev) +{ + ssi_set_interface(4096, 0, 0, 0); + ssi_chip_select(0); + udelay(1); + ssi_chip_select(dev); + udelay(1); +} + + +u8 ds1722_read(int dev, int addr) +{ + u8 res; + + ds1722_select(dev); + + ssi_tx_byte(addr); + res = ssi_rx_byte(); + + ssi_chip_select(0); + + return res; +} + +void ds1722_write(int dev, int addr, u8 data) +{ + ds1722_select(dev); + + ssi_tx_byte(0x80|addr); + ssi_tx_byte(data); + + ssi_chip_select(0); +} + + +u16 ds1722_temp(int dev, int resolution) +{ + static int useconds[] = { + 75000, 150000, 300000, 600000, 1200000 + }; + char temp; + u16 res; + + + /* set up the desired resulotion ... */ + ds1722_write(dev, 0, 0xe0 | (resolution << 1)); + + /* wait while the chip measures the tremperature */ + udelay(useconds[resolution]); + + res = (temp = ds1722_read(dev, 2)) << 8; + + if (temp < 0) { + temp = (16 - (ds1722_read(dev, 1) >> 4)) & 0x0f; + } else { + temp = (ds1722_read(dev, 1) >> 4); + } + + switch (temp) { + case 0: + /* .0000 */ + break; + case 1: + /* .0625 */ + res |=1; + break; + case 2: + /* .1250 */ + res |=1; + break; + case 3: + /* .1875 */ + res |=2; + break; + case 4: + /* .2500 */ + res |=3; + break; + case 5: + /* .3125 */ + res |=3; + break; + case 6: + /* .3750 */ + res |=4; + break; + case 7: + /* .4375 */ + res |=4; + break; + case 8: + /* .5000 */ + res |=5; + break; + case 9: + /* .5625 */ + res |=6; + break; + case 10: + /* .6250 */ + res |=6; + break; + case 11: + /* .6875 */ + res |=7; + break; + case 12: + /* .7500 */ + res |=8; + break; + case 13: + /* .8125 */ + res |=8; + break; + case 14: + /* .8750 */ + res |=9; + break; + case 15: + /* .9375 */ + res |=9; + break; + } + return res; + +} + +int ds1722_probe(int dev) +{ + u16 temp = ds1722_temp(dev, DS1722_RESOLUTION_12BIT); + printf("%d.%d deg C\n\n", (char)(temp >> 8), temp & 0xff); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/hwmon/ds1775.c b/qemu/roms/u-boot/drivers/hwmon/ds1775.c new file mode 100644 index 000000000..b95b130d9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/ds1775.c @@ -0,0 +1,126 @@ +/* + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Dallas Semiconductor's DS1775 Digital Thermometer and Thermostat + */ + +#include + +#include +#include + +#define DTT_I2C_DEV_CODE CONFIG_SYS_I2C_DTT_ADDR /* Dallas Semi's DS1775 device code */ +#define DTT_READ_TEMP 0x0 +#define DTT_CONFIG 0x1 +#define DTT_TEMP_HYST 0x2 +#define DTT_TEMP_OS 0x3 + +int dtt_read(int sensor, int reg) +{ + int dlen; + uchar data[2]; + + /* + * Calculate sensor address and command + */ + sensor = DTT_I2C_DEV_CODE + (sensor & 0x07); /* Calculate addr of ds1775 */ + + /* + * Prepare to handle 2 byte result + */ + if ((reg == DTT_READ_TEMP) || + (reg == DTT_TEMP_OS) || (reg == DTT_TEMP_HYST)) + dlen = 2; + else + dlen = 1; + + /* + * Now try to read the register + */ + if (i2c_read(sensor, reg, 1, data, dlen) != 0) + return 1; + + /* + * Handle 2 byte result + */ + if (dlen == 2) + return ((int)((short)data[1] + (((short)data[0]) << 8))); + + return (int) data[0]; +} + + +int dtt_write(int sensor, int reg, int val) +{ + int dlen; + uchar data[2]; + + /* + * Calculate sensor address and register + */ + sensor = DTT_I2C_DEV_CODE + (sensor & 0x07); + + /* + * Handle various data sizes + */ + if ((reg == DTT_READ_TEMP) || + (reg == DTT_TEMP_OS) || (reg == DTT_TEMP_HYST)) { + dlen = 2; + data[0] = (char)((val >> 8) & 0xff); /* MSB first */ + data[1] = (char)(val & 0xff); + } else { + dlen = 1; + data[0] = (char)(val & 0xff); + } + + /* + * Write value to device + */ + if (i2c_write(sensor, reg, 1, data, dlen) != 0) + return 1; + + return 0; +} + + +int dtt_init_one(int sensor) +{ + int val; + + /* + * Setup High Temp + */ + val = ((CONFIG_SYS_DTT_MAX_TEMP * 2) << 7) & 0xff80; + if (dtt_write(sensor, DTT_TEMP_OS, val) != 0) + return 1; + udelay(50000); /* Max 50ms */ + + /* + * Setup Low Temp - hysteresis + */ + val = (((CONFIG_SYS_DTT_MAX_TEMP - CONFIG_SYS_DTT_HYSTERESIS) * 2) << 7) & 0xff80; + if (dtt_write(sensor, DTT_TEMP_HYST, val) != 0) + return 1; + udelay(50000); /* Max 50ms */ + + /* + * Setup configuraton register + * + * Fault Tolerance limits 4, Thermometer resolution bits is 9, + * Polarity = Active Low,continuous conversion mode, Thermostat + * mode is interrupt mode + */ + val = 0xa; + if (dtt_write(sensor, DTT_CONFIG, val) != 0) + return 1; + udelay(50000); /* Max 50ms */ + + return 0; +} + +int dtt_get_temp(int sensor) +{ + return (dtt_read(sensor, DTT_READ_TEMP) / 256); +} diff --git a/qemu/roms/u-boot/drivers/hwmon/lm63.c b/qemu/roms/u-boot/drivers/hwmon/lm63.c new file mode 100644 index 000000000..053c785fc --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/lm63.c @@ -0,0 +1,160 @@ +/* + * (C) Copyright 2007-2008 + * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de + * based on lm75.c by Bill Hunter + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * National LM63/LM64 Temperature Sensor + * Main difference: LM 64 has -16 Kelvin temperature offset + */ + +#include +#include +#include + +#define DTT_I2C_LM63_ADDR 0x4C /* National LM63 device */ + +#define DTT_READ_TEMP_RMT_MSB 0x01 +#define DTT_CONFIG 0x03 +#define DTT_READ_TEMP_RMT_LSB 0x10 +#define DTT_TACHLIM_LSB 0x48 +#define DTT_TACHLIM_MSB 0x49 +#define DTT_FAN_CONFIG 0x4A +#define DTT_PWM_FREQ 0x4D +#define DTT_PWM_LOOKUP_BASE 0x50 + +struct pwm_lookup_entry { + u8 temp; + u8 pwm; +}; + +/* + * Device code + */ + +int dtt_read(int sensor, int reg) +{ + int dlen; + uchar data[2]; + + /* + * Calculate sensor address and register. + */ + if (!sensor) + sensor = DTT_I2C_LM63_ADDR; /* legacy config */ + + dlen = 1; + + /* + * Now try to read the register. + */ + if (i2c_read(sensor, reg, 1, data, dlen) != 0) + return -1; + + return (int)data[0]; +} /* dtt_read() */ + +int dtt_write(int sensor, int reg, int val) +{ + int dlen; + uchar data[2]; + + /* + * Calculate sensor address and register. + */ + if (!sensor) + sensor = DTT_I2C_LM63_ADDR; /* legacy config */ + + dlen = 1; + data[0] = (char)(val & 0xff); + + /* + * Write value to register. + */ + if (i2c_write(sensor, reg, 1, data, dlen) != 0) + return 1; + + return 0; +} /* dtt_write() */ + +static int is_lm64(int sensor) +{ + return sensor && (sensor != DTT_I2C_LM63_ADDR); +} + +int dtt_init_one(int sensor) +{ + int i; + int val; + + struct pwm_lookup_entry pwm_lookup[] = CONFIG_DTT_PWM_LOOKUPTABLE; + + /* + * Set PWM Frequency to 2.5% resolution + */ + val = 20; + if (dtt_write(sensor, DTT_PWM_FREQ, val) != 0) + return 1; + + /* + * Set Tachometer Limit + */ + val = CONFIG_DTT_TACH_LIMIT; + if (dtt_write(sensor, DTT_TACHLIM_LSB, val & 0xff) != 0) + return 1; + if (dtt_write(sensor, DTT_TACHLIM_MSB, (val >> 8) & 0xff) != 0) + return 1; + + /* + * Make sure PWM Lookup-Table is writeable + */ + if (dtt_write(sensor, DTT_FAN_CONFIG, 0x20) != 0) + return 1; + + /* + * Setup PWM Lookup-Table + */ + for (i = 0; i < ARRAY_SIZE(pwm_lookup); i++) { + int address = DTT_PWM_LOOKUP_BASE + 2 * i; + val = pwm_lookup[i].temp; + if (is_lm64(sensor)) + val -= 16; + if (dtt_write(sensor, address, val) != 0) + return 1; + val = dtt_read(sensor, address); + val = pwm_lookup[i].pwm; + if (dtt_write(sensor, address + 1, val) != 0) + return 1; + } + + /* + * Enable PWM Lookup-Table, PWM Clock 360 kHz, Tachometer Mode 2 + */ + val = 0x02; + if (dtt_write(sensor, DTT_FAN_CONFIG, val) != 0) + return 1; + + /* + * Enable Tach input + */ + val = dtt_read(sensor, DTT_CONFIG) | 0x04; + if (dtt_write(sensor, DTT_CONFIG, val) != 0) + return 1; + + return 0; +} + +int dtt_get_temp(int sensor) +{ + s16 temp = (dtt_read(sensor, DTT_READ_TEMP_RMT_MSB) << 8) + | (dtt_read(sensor, DTT_READ_TEMP_RMT_LSB)); + + if (is_lm64(sensor)) + temp += 16 << 8; + + /* Ignore LSB for now, U-Boot only prints natural numbers */ + return temp >> 8; +} diff --git a/qemu/roms/u-boot/drivers/hwmon/lm73.c b/qemu/roms/u-boot/drivers/hwmon/lm73.c new file mode 100644 index 000000000..c15c7514d --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/lm73.c @@ -0,0 +1,146 @@ +/* + * (C) Copyright 2007-2008 + * Larry Johnson, lrj@acm.org + * + * based on dtt/lm75.c which is ... + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * National Semiconductor LM73 Temperature Sensor + */ + +#include +#include +#include + +/* + * Device code + */ +#define DTT_I2C_DEV_CODE 0x48 /* National Semi's LM73 device */ +#define DTT_READ_TEMP 0x0 +#define DTT_CONFIG 0x1 +#define DTT_TEMP_HIGH 0x2 +#define DTT_TEMP_LOW 0x3 +#define DTT_CONTROL 0x4 +#define DTT_ID 0x7 + +int dtt_read(int const sensor, int const reg) +{ + int dlen; + uint8_t data[2]; + + /* + * Validate 'reg' param and get register size. + */ + switch (reg) { + case DTT_CONFIG: + case DTT_CONTROL: + dlen = 1; + break; + case DTT_READ_TEMP: + case DTT_TEMP_HIGH: + case DTT_TEMP_LOW: + case DTT_ID: + dlen = 2; + break; + default: + return -1; + } + /* + * Try to read the register at the calculated sensor address. + */ + if (0 != + i2c_read(DTT_I2C_DEV_CODE + (sensor & 0x07), reg, 1, data, dlen)) + return -1; + /* + * Handle 2 byte result. + */ + if (2 == dlen) + return (int)((unsigned)data[0] << 8 | (unsigned)data[1]); + + return (int)data[0]; +} /* dtt_read() */ + +int dtt_write(int const sensor, int const reg, int const val) +{ + int dlen; + uint8_t data[2]; + + /* + * Validate 'reg' param and handle register size + */ + switch (reg) { + case DTT_CONFIG: + case DTT_CONTROL: + dlen = 1; + data[0] = (uint8_t) val; + break; + case DTT_TEMP_HIGH: + case DTT_TEMP_LOW: + dlen = 2; + data[0] = (uint8_t) (val >> 8); /* MSB first */ + data[1] = (uint8_t) val; + break; + default: + return -1; + } + /* + * Write value to register at the calculated sensor address. + */ + return 0 != i2c_write(DTT_I2C_DEV_CODE + (sensor & 0x07), reg, 1, data, + dlen); +} /* dtt_write() */ + +int dtt_init_one(int const sensor) +{ + int val; + + /* + * Validate the Identification register + */ + if (0x0190 != dtt_read(sensor, DTT_ID)) + return -1; + /* + * Setup THIGH (upper-limit) and TLOW (lower-limit) registers + */ + val = CONFIG_SYS_DTT_MAX_TEMP << 7; + if (dtt_write(sensor, DTT_TEMP_HIGH, val)) + return -1; + + val = CONFIG_SYS_DTT_MIN_TEMP << 7; + if (dtt_write(sensor, DTT_TEMP_LOW, val)) + return -1; + /* + * Setup configuraton register + */ + /* config = alert active low, disabled, and reset */ + val = 0x64; + if (dtt_write(sensor, DTT_CONFIG, val)) + return -1; + /* + * Setup control/status register + */ + /* control = temp resolution 0.25C */ + val = 0x00; + if (dtt_write(sensor, DTT_CONTROL, val)) + return -1; + + dtt_read(sensor, DTT_CONTROL); /* clear temperature flags */ + return 0; +} /* dtt_init_one() */ + +int dtt_get_temp(int const sensor) +{ + int const ret = dtt_read(sensor, DTT_READ_TEMP); + + if (ret < 0) { + printf("DTT temperature read failed.\n"); + return 0; + } + return (int)((int16_t) ret + 0x0040) >> 7; +} /* dtt_get_temp() */ diff --git a/qemu/roms/u-boot/drivers/hwmon/lm75.c b/qemu/roms/u-boot/drivers/hwmon/lm75.c new file mode 100644 index 000000000..462f902da --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/lm75.c @@ -0,0 +1,143 @@ +/* + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * On Semiconductor's LM75 Temperature Sensor + */ + +#include +#include +#include + +/* + * Device code + */ +#if defined(CONFIG_SYS_I2C_DTT_ADDR) +#define DTT_I2C_DEV_CODE CONFIG_SYS_I2C_DTT_ADDR +#else +#define DTT_I2C_DEV_CODE 0x48 /* ON Semi's LM75 device */ +#endif +#define DTT_READ_TEMP 0x0 +#define DTT_CONFIG 0x1 +#define DTT_TEMP_HYST 0x2 +#define DTT_TEMP_SET 0x3 + +int dtt_read(int sensor, int reg) +{ + int dlen; + uchar data[2]; + +#ifdef CONFIG_DTT_AD7414 + /* + * On AD7414 the first value upon bootup is not read correctly. + * This is most likely because of the 800ms update time of the + * temp register in normal update mode. To get current values + * each time we issue the "dtt" command including upon powerup + * we switch into one-short mode. + * + * Issue one-shot mode command + */ + dtt_write(sensor, DTT_CONFIG, 0x64); +#endif + + /* Validate 'reg' param */ + if((reg < 0) || (reg > 3)) + return -1; + + /* Calculate sensor address and register. */ + sensor = DTT_I2C_DEV_CODE + (sensor & 0x07); + + /* Prepare to handle 2 byte result. */ + if ((reg == DTT_READ_TEMP) || + (reg == DTT_TEMP_HYST) || + (reg == DTT_TEMP_SET)) + dlen = 2; + else + dlen = 1; + + /* Now try to read the register. */ + if (i2c_read(sensor, reg, 1, data, dlen) != 0) + return -1; + + /* Handle 2 byte result. */ + if (dlen == 2) + return ((int)((short)data[1] + (((short)data[0]) << 8))); + + return (int)data[0]; +} /* dtt_read() */ + + +int dtt_write(int sensor, int reg, int val) +{ + int dlen; + uchar data[2]; + + /* Validate 'reg' param */ + if ((reg < 0) || (reg > 3)) + return 1; + + /* Calculate sensor address and register. */ + sensor = DTT_I2C_DEV_CODE + (sensor & 0x07); + + /* Handle 2 byte values. */ + if ((reg == DTT_READ_TEMP) || + (reg == DTT_TEMP_HYST) || + (reg == DTT_TEMP_SET)) { + dlen = 2; + data[0] = (char)((val >> 8) & 0xff); /* MSB first */ + data[1] = (char)(val & 0xff); + } else { + dlen = 1; + data[0] = (char)(val & 0xff); + } + + /* Write value to register. */ + if (i2c_write(sensor, reg, 1, data, dlen) != 0) + return 1; + + return 0; +} /* dtt_write() */ + + +int dtt_init_one(int sensor) +{ + int val; + + /* Setup TSET ( trip point ) register */ + val = ((CONFIG_SYS_DTT_MAX_TEMP * 2) << 7) & 0xff80; /* trip */ + if (dtt_write(sensor, DTT_TEMP_SET, val) != 0) + return 1; + + /* Setup THYST ( untrip point ) register - Hysteresis */ + val = (((CONFIG_SYS_DTT_MAX_TEMP - CONFIG_SYS_DTT_HYSTERESIS) * 2) << 7) & 0xff80; + if (dtt_write(sensor, DTT_TEMP_HYST, val) != 0) + return 1; + + /* Setup configuraton register */ +#ifdef CONFIG_DTT_AD7414 + /* config = alert active low and disabled */ + val = 0x60; +#else + /* config = 6 sample integration, int mode, active low, and enable */ + val = 0x18; +#endif + if (dtt_write(sensor, DTT_CONFIG, val) != 0) + return 1; + + return 0; +} /* dtt_init_one() */ + +int dtt_get_temp(int sensor) +{ + int const ret = dtt_read(sensor, DTT_READ_TEMP); + + if (ret < 0) { + printf("DTT temperature read failed.\n"); + return 0; + } + return (int)((int16_t) ret / 256); +} /* dtt_get_temp() */ diff --git a/qemu/roms/u-boot/drivers/hwmon/lm81.c b/qemu/roms/u-boot/drivers/hwmon/lm81.c new file mode 100644 index 000000000..c1fc42a83 --- /dev/null +++ b/qemu/roms/u-boot/drivers/hwmon/lm81.c @@ -0,0 +1,111 @@ +/* + * (C) Copyright 2006 + * Heiko Schocher, DENX Software Enginnering + * + * based on dtt/lm75.c which is ... + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * On Semiconductor's LM81 Temperature Sensor + */ + +#include +#include +#include + +/* + * Device code + */ +#define DTT_I2C_DEV_CODE 0x2c /* ON Semi's LM81 device */ +#define DTT_READ_TEMP 0x27 +#define DTT_CONFIG_TEMP 0x4b +#define DTT_TEMP_MAX 0x39 +#define DTT_TEMP_HYST 0x3a +#define DTT_CONFIG 0x40 + +int dtt_read(int sensor, int reg) +{ + int dlen = 1; + uchar data[2]; + + /* + * Calculate sensor address and register. + */ + sensor = DTT_I2C_DEV_CODE + (sensor & 0x03); /* calculate address of lm81 */ + + /* + * Now try to read the register. + */ + if (i2c_read(sensor, reg, 1, data, dlen) != 0) + return -1; + + return (int)data[0]; +} /* dtt_read() */ + + +int dtt_write(int sensor, int reg, int val) +{ + uchar data; + + /* + * Calculate sensor address and register. + */ + sensor = DTT_I2C_DEV_CODE + (sensor & 0x03); /* calculate address of lm81 */ + + data = (char)(val & 0xff); + + /* + * Write value to register. + */ + if (i2c_write(sensor, reg, 1, &data, 1) != 0) + return 1; + + return 0; +} /* dtt_write() */ + +#define DTT_MANU 0x3e +#define DTT_REV 0x3f +#define DTT_CONFIG 0x40 +#define DTT_ADR 0x48 + +int dtt_init_one(int sensor) +{ + int man; + int adr; + int rev; + + if (dtt_write (sensor, DTT_CONFIG, 0x01) < 0) + return 1; + /* The LM81 needs 400ms to get the correct values ... */ + udelay (400000); + man = dtt_read (sensor, DTT_MANU); + if (man != 0x01) + return 1; + adr = dtt_read (sensor, DTT_ADR); + if (adr < 0) + return 1; + rev = dtt_read (sensor, DTT_REV); + if (adr < 0) + return 1; + + debug ("DTT: Found LM81@%x Rev: %d\n", adr, rev); + return 0; +} /* dtt_init_one() */ + + +#define TEMP_FROM_REG(temp) \ + ((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \ + ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \ + +int dtt_get_temp(int sensor) +{ + int val = dtt_read (sensor, DTT_READ_TEMP); + int tmpcnf = dtt_read (sensor, DTT_CONFIG_TEMP); + + return (TEMP_FROM_REG((val << 1) + ((tmpcnf & 0x80) >> 7))) / 10; +} /* dtt_get_temp() */ diff --git a/qemu/roms/u-boot/drivers/i2c/Makefile b/qemu/roms/u-boot/drivers/i2c/Makefile new file mode 100644 index 000000000..e33586d8a --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/Makefile @@ -0,0 +1,31 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o +obj-$(CONFIG_DW_I2C) += designware_i2c.o +obj-$(CONFIG_I2C_MVTWSI) += mvtwsi.o +obj-$(CONFIG_I2C_MV) += mv_i2c.o +obj-$(CONFIG_I2C_MXS) += mxs_i2c.o +obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o +obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o +obj-$(CONFIG_U8500_I2C) += u8500_i2c.o +obj-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o +obj-$(CONFIG_SYS_I2C) += i2c_core.o +obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o +obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o +obj-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o +obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o +obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o +obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o +obj-$(CONFIG_SYS_I2C_OMAP34XX) += omap24xx_i2c.o +obj-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o +obj-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o +obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o +obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o +obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o +obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o +obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o diff --git a/qemu/roms/u-boot/drivers/i2c/bfin-twi_i2c.c b/qemu/roms/u-boot/drivers/i2c/bfin-twi_i2c.c new file mode 100644 index 000000000..cfab064df --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/bfin-twi_i2c.c @@ -0,0 +1,379 @@ +/* + * i2c.c - driver for Blackfin on-chip TWI/I2C + * + * Copyright (c) 2006-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include + +#include +#include +#include + +/* Every register is 32bit aligned, but only 16bits in size */ +#define ureg(name) u16 name; u16 __pad_##name; +struct twi_regs { + ureg(clkdiv); + ureg(control); + ureg(slave_ctl); + ureg(slave_stat); + ureg(slave_addr); + ureg(master_ctl); + ureg(master_stat); + ureg(master_addr); + ureg(int_stat); + ureg(int_mask); + ureg(fifo_ctl); + ureg(fifo_stat); + char __pad[0x50]; + ureg(xmt_data8); + ureg(xmt_data16); + ureg(rcv_data8); + ureg(rcv_data16); +}; +#undef ureg + +/* U-Boot I2C framework allows only one active device at a time. */ +#ifdef TWI_CLKDIV +#define TWI0_CLKDIV TWI_CLKDIV +#endif +static volatile struct twi_regs *twi = (void *)TWI0_CLKDIV; + +#ifdef DEBUG +# define dmemset(s, c, n) memset(s, c, n) +#else +# define dmemset(s, c, n) +#endif +#define debugi(fmt, args...) \ + debug( \ + "MSTAT:0x%03x FSTAT:0x%x ISTAT:0x%02x\t%-20s:%-3i: " fmt "\n", \ + twi->master_stat, twi->fifo_stat, twi->int_stat, \ + __func__, __LINE__, ## args) + +#ifdef CONFIG_TWICLK_KHZ +# error do not define CONFIG_TWICLK_KHZ ... use CONFIG_SYS_I2C_SPEED +#endif + +/* + * The way speed is changed into duty often results in integer truncation + * with 50% duty, so we'll force rounding up to the next duty by adding 1 + * to the max. In practice this will get us a speed of something like + * 385 KHz. The other limit is easy to handle as it is only 8 bits. + */ +#define I2C_SPEED_MAX 400000 +#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed)) +#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1) +#define I2C_DUTY_MIN 0xff /* 8 bit limited */ +#define SYS_I2C_DUTY I2C_SPEED_TO_DUTY(CONFIG_SYS_I2C_SPEED) +/* Note: duty is inverse of speed, so the comparisons below are correct */ +#if SYS_I2C_DUTY < I2C_DUTY_MAX || SYS_I2C_DUTY > I2C_DUTY_MIN +# error "The Blackfin I2C hardware can only operate 20KHz - 400KHz" +#endif + +/* All transfers are described by this data structure */ +struct i2c_msg { + u8 flags; +#define I2C_M_COMBO 0x4 +#define I2C_M_STOP 0x2 +#define I2C_M_READ 0x1 + int len; /* msg length */ + u8 *buf; /* pointer to msg data */ + int alen; /* addr length */ + u8 *abuf; /* addr buffer */ +}; + +/* Allow msec timeout per ~byte transfer */ +#define I2C_TIMEOUT 10 + +/** + * wait_for_completion - manage the actual i2c transfer + * @msg: the i2c msg + */ +static int wait_for_completion(struct i2c_msg *msg) +{ + uint16_t int_stat; + ulong timebase = get_timer(0); + + do { + int_stat = twi->int_stat; + + if (int_stat & XMTSERV) { + debugi("processing XMTSERV"); + twi->int_stat = XMTSERV; + SSYNC(); + if (msg->alen) { + twi->xmt_data8 = *(msg->abuf++); + --msg->alen; + } else if (!(msg->flags & I2C_M_COMBO) && msg->len) { + twi->xmt_data8 = *(msg->buf++); + --msg->len; + } else { + twi->master_ctl |= (msg->flags & I2C_M_COMBO) ? RSTART | MDIR : STOP; + SSYNC(); + } + } + if (int_stat & RCVSERV) { + debugi("processing RCVSERV"); + twi->int_stat = RCVSERV; + SSYNC(); + if (msg->len) { + *(msg->buf++) = twi->rcv_data8; + --msg->len; + } else if (msg->flags & I2C_M_STOP) { + twi->master_ctl |= STOP; + SSYNC(); + } + } + if (int_stat & MERR) { + debugi("processing MERR"); + twi->int_stat = MERR; + SSYNC(); + return msg->len; + } + if (int_stat & MCOMP) { + debugi("processing MCOMP"); + twi->int_stat = MCOMP; + SSYNC(); + if (msg->flags & I2C_M_COMBO && msg->len) { + twi->master_ctl = (twi->master_ctl & ~RSTART) | + (min(msg->len, 0xff) << 6) | MEN | MDIR; + SSYNC(); + } else + break; + } + + /* If we were able to do something, reset timeout */ + if (int_stat) + timebase = get_timer(0); + + } while (get_timer(timebase) < I2C_TIMEOUT); + + return msg->len; +} + +/** + * i2c_transfer - setup an i2c transfer + * @return: 0 if things worked, non-0 if things failed + * + * Here we just get the i2c stuff all prepped and ready, and then tail off + * into wait_for_completion() for all the bits to go. + */ +static int i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer, int len, u8 flags) +{ + uchar addr_buffer[] = { + (addr >> 0), + (addr >> 8), + (addr >> 16), + }; + struct i2c_msg msg = { + .flags = flags | (len >= 0xff ? I2C_M_STOP : 0), + .buf = buffer, + .len = len, + .abuf = addr_buffer, + .alen = alen, + }; + int ret; + + dmemset(buffer, 0xff, len); + debugi("chip=0x%x addr=0x%02x alen=%i buf[0]=0x%02x len=%i flags=0x%02x[%s] ", + chip, addr, alen, buffer[0], len, flags, (flags & I2C_M_READ ? "rd" : "wr")); + + /* wait for things to settle */ + while (twi->master_stat & BUSBUSY) + if (ctrlc()) + return 1; + + /* Set Transmit device address */ + twi->master_addr = chip; + + /* Clear the FIFO before starting things */ + twi->fifo_ctl = XMTFLUSH | RCVFLUSH; + SSYNC(); + twi->fifo_ctl = 0; + SSYNC(); + + /* prime the pump */ + if (msg.alen) { + len = (msg.flags & I2C_M_COMBO) ? msg.alen : msg.alen + len; + debugi("first byte=0x%02x", *msg.abuf); + twi->xmt_data8 = *(msg.abuf++); + --msg.alen; + } else if (!(msg.flags & I2C_M_READ) && msg.len) { + debugi("first byte=0x%02x", *msg.buf); + twi->xmt_data8 = *(msg.buf++); + --msg.len; + } + + /* clear int stat */ + twi->master_stat = -1; + twi->int_stat = -1; + twi->int_mask = 0; + SSYNC(); + + /* Master enable */ + twi->master_ctl = + (twi->master_ctl & FAST) | + (min(len, 0xff) << 6) | MEN | + ((msg.flags & I2C_M_READ) ? MDIR : 0); + SSYNC(); + debugi("CTL=0x%04x", twi->master_ctl); + + /* process the rest */ + ret = wait_for_completion(&msg); + debugi("ret=%d", ret); + + if (ret) { + twi->master_ctl &= ~MEN; + twi->control &= ~TWI_ENA; + SSYNC(); + twi->control |= TWI_ENA; + SSYNC(); + } + + return ret; +} + +/** + * i2c_set_bus_speed - set i2c bus speed + * @speed: bus speed (in HZ) + */ +int i2c_set_bus_speed(unsigned int speed) +{ + u16 clkdiv = I2C_SPEED_TO_DUTY(speed); + + /* Set TWI interface clock */ + if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN) + return -1; + twi->clkdiv = (clkdiv << 8) | (clkdiv & 0xff); + + /* Don't turn it on */ + twi->master_ctl = (speed > 100000 ? FAST : 0); + + return 0; +} + +/** + * i2c_get_bus_speed - get i2c bus speed + * @speed: bus speed (in HZ) + */ +unsigned int i2c_get_bus_speed(void) +{ + /* 10 MHz / (2 * CLKDIV) -> 5 MHz / CLKDIV */ + return 5000000 / (twi->clkdiv & 0xff); +} + +/** + * i2c_init - initialize the i2c bus + * @speed: bus speed (in HZ) + * @slaveaddr: address of device in slave mode (0 - not slave) + * + * Slave mode isn't actually implemented. It'll stay that way until + * we get a real request for it. + */ +void i2c_init(int speed, int slaveaddr) +{ + uint8_t prescale = ((get_i2c_clk() / 1000 / 1000 + 5) / 10) & 0x7F; + + /* Set TWI internal clock as 10MHz */ + twi->control = prescale; + + /* Set TWI interface clock as specified */ + i2c_set_bus_speed(speed); + + /* Enable it */ + twi->control = TWI_ENA | prescale; + SSYNC(); + + debugi("CONTROL:0x%04x CLKDIV:0x%04x", twi->control, twi->clkdiv); + +#if CONFIG_SYS_I2C_SLAVE +# error I2C slave support not tested/supported + /* If they want us as a slave, do it */ + if (slaveaddr) { + twi->slave_addr = slaveaddr; + twi->slave_ctl = SEN; + } +#endif +} + +/** + * i2c_probe - test if a chip exists at a given i2c address + * @chip: i2c chip addr to search for + * @return: 0 if found, non-0 if not found + */ +int i2c_probe(uchar chip) +{ + u8 byte; + return i2c_read(chip, 0, 0, &byte, 1); +} + +/** + * i2c_read - read data from an i2c device + * @chip: i2c chip addr + * @addr: memory (register) address in the chip + * @alen: byte size of address + * @buffer: buffer to store data read from chip + * @len: how many bytes to read + * @return: 0 on success, non-0 on failure + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + return i2c_transfer(chip, addr, alen, buffer, len, (alen ? I2C_M_COMBO : I2C_M_READ)); +} + +/** + * i2c_write - write data to an i2c device + * @chip: i2c chip addr + * @addr: memory (register) address in the chip + * @alen: byte size of address + * @buffer: buffer holding data to write to chip + * @len: how many bytes to write + * @return: 0 on success, non-0 on failure + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + return i2c_transfer(chip, addr, alen, buffer, len, 0); +} + +/** + * i2c_set_bus_num - change active I2C bus + * @bus: bus index, zero based + * @returns: 0 on success, non-0 on failure + */ +int i2c_set_bus_num(unsigned int bus) +{ + switch (bus) { +#if CONFIG_SYS_MAX_I2C_BUS > 0 + case 0: twi = (void *)TWI0_CLKDIV; return 0; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 1 + case 1: twi = (void *)TWI1_CLKDIV; return 0; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 2 + case 2: twi = (void *)TWI2_CLKDIV; return 0; +#endif + default: return -1; + } +} + +/** + * i2c_get_bus_num - returns index of active I2C bus + */ +unsigned int i2c_get_bus_num(void) +{ + switch ((unsigned long)twi) { +#if CONFIG_SYS_MAX_I2C_BUS > 0 + case TWI0_CLKDIV: return 0; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 1 + case TWI1_CLKDIV: return 1; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 2 + case TWI2_CLKDIV: return 2; +#endif + default: return -1; + } +} diff --git a/qemu/roms/u-boot/drivers/i2c/davinci_i2c.c b/qemu/roms/u-boot/drivers/i2c/davinci_i2c.c new file mode 100644 index 000000000..9ca99c4ab --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/davinci_i2c.c @@ -0,0 +1,384 @@ +/* + * TI DaVinci (TMS320DM644x) I2C driver. + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, + * (C) Copyright 2007 Sergey Kubushyn + * -------------------------------------------------------- + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include "davinci_i2c.h" + +#define CHECK_NACK() \ + do {\ + if (tmp & (I2C_TIMEOUT | I2C_STAT_NACK)) {\ + REG(&(i2c_base->i2c_con)) = 0;\ + return 1;\ + } \ + } while (0) + +static struct i2c_regs *davinci_get_base(struct i2c_adapter *adap); + +static int wait_for_bus(struct i2c_adapter *adap) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + int stat, timeout; + + REG(&(i2c_base->i2c_stat)) = 0xffff; + + for (timeout = 0; timeout < 10; timeout++) { + stat = REG(&(i2c_base->i2c_stat)); + if (!((stat) & I2C_STAT_BB)) { + REG(&(i2c_base->i2c_stat)) = 0xffff; + return 0; + } + + REG(&(i2c_base->i2c_stat)) = stat; + udelay(50000); + } + + REG(&(i2c_base->i2c_stat)) = 0xffff; + return 1; +} + + +static int poll_i2c_irq(struct i2c_adapter *adap, int mask) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + int stat, timeout; + + for (timeout = 0; timeout < 10; timeout++) { + udelay(1000); + stat = REG(&(i2c_base->i2c_stat)); + if (stat & mask) + return stat; + } + + REG(&(i2c_base->i2c_stat)) = 0xffff; + return stat | I2C_TIMEOUT; +} + +static void flush_rx(struct i2c_adapter *adap) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + + while (1) { + if (!(REG(&(i2c_base->i2c_stat)) & I2C_STAT_RRDY)) + break; + + REG(&(i2c_base->i2c_drr)); + REG(&(i2c_base->i2c_stat)) = I2C_STAT_RRDY; + udelay(1000); + } +} + +static uint davinci_i2c_setspeed(struct i2c_adapter *adap, uint speed) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + uint32_t div, psc; + + psc = 2; + /* SCLL + SCLH */ + div = (CONFIG_SYS_HZ_CLOCK / ((psc + 1) * speed)) - 10; + REG(&(i2c_base->i2c_psc)) = psc; /* 27MHz / (2 + 1) = 9MHz */ + REG(&(i2c_base->i2c_scll)) = (div * 50) / 100; /* 50% Duty */ + REG(&(i2c_base->i2c_sclh)) = div - REG(&(i2c_base->i2c_scll)); + + adap->speed = speed; + return 0; +} + +static void davinci_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + + if (REG(&(i2c_base->i2c_con)) & I2C_CON_EN) { + REG(&(i2c_base->i2c_con)) = 0; + udelay(50000); + } + + davinci_i2c_setspeed(adap, speed); + + REG(&(i2c_base->i2c_oa)) = slaveadd; + REG(&(i2c_base->i2c_cnt)) = 0; + + /* Interrupts must be enabled or I2C module won't work */ + REG(&(i2c_base->i2c_ie)) = I2C_IE_SCD_IE | I2C_IE_XRDY_IE | + I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | I2C_IE_NACK_IE; + + /* Now enable I2C controller (get it out of reset) */ + REG(&(i2c_base->i2c_con)) = I2C_CON_EN; + + udelay(1000); +} + +static int davinci_i2c_probe(struct i2c_adapter *adap, uint8_t chip) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + int rc = 1; + + if (chip == REG(&(i2c_base->i2c_oa))) + return rc; + + REG(&(i2c_base->i2c_con)) = 0; + if (wait_for_bus(adap)) + return 1; + + /* try to read one byte from current (or only) address */ + REG(&(i2c_base->i2c_cnt)) = 1; + REG(&(i2c_base->i2c_sa)) = chip; + REG(&(i2c_base->i2c_con)) = (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | + I2C_CON_STP); + udelay(50000); + + if (!(REG(&(i2c_base->i2c_stat)) & I2C_STAT_NACK)) { + rc = 0; + flush_rx(adap); + REG(&(i2c_base->i2c_stat)) = 0xffff; + } else { + REG(&(i2c_base->i2c_stat)) = 0xffff; + REG(&(i2c_base->i2c_con)) |= I2C_CON_STP; + udelay(20000); + if (wait_for_bus(adap)) + return 1; + } + + flush_rx(adap); + REG(&(i2c_base->i2c_stat)) = 0xffff; + REG(&(i2c_base->i2c_cnt)) = 0; + return rc; +} + +static int davinci_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint32_t addr, int alen, uint8_t *buf, int len) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + uint32_t tmp; + int i; + + if ((alen < 0) || (alen > 2)) { + printf("%s(): bogus address length %x\n", __func__, alen); + return 1; + } + + if (wait_for_bus(adap)) + return 1; + + if (alen != 0) { + /* Start address phase */ + tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX; + REG(&(i2c_base->i2c_cnt)) = alen; + REG(&(i2c_base->i2c_sa)) = chip; + REG(&(i2c_base->i2c_con)) = tmp; + + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + switch (alen) { + case 2: + /* Send address MSByte */ + if (tmp & I2C_STAT_XRDY) { + REG(&(i2c_base->i2c_dxr)) = (addr >> 8) & 0xff; + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + /* No break, fall through */ + case 1: + /* Send address LSByte */ + if (tmp & I2C_STAT_XRDY) { + REG(&(i2c_base->i2c_dxr)) = addr & 0xff; + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | + I2C_STAT_NACK | I2C_STAT_ARDY); + + CHECK_NACK(); + + if (!(tmp & I2C_STAT_ARDY)) { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + } + } + + /* Address phase is over, now read 'len' bytes and stop */ + tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP; + REG(&(i2c_base->i2c_cnt)) = len & 0xffff; + REG(&(i2c_base->i2c_sa)) = chip; + REG(&(i2c_base->i2c_con)) = tmp; + + for (i = 0; i < len; i++) { + tmp = poll_i2c_irq(adap, I2C_STAT_RRDY | I2C_STAT_NACK | + I2C_STAT_ROVR); + + CHECK_NACK(); + + if (tmp & I2C_STAT_RRDY) { + buf[i] = REG(&(i2c_base->i2c_drr)); + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + } + + tmp = poll_i2c_irq(adap, I2C_STAT_SCD | I2C_STAT_NACK); + + CHECK_NACK(); + + if (!(tmp & I2C_STAT_SCD)) { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + + flush_rx(adap); + REG(&(i2c_base->i2c_stat)) = 0xffff; + REG(&(i2c_base->i2c_cnt)) = 0; + REG(&(i2c_base->i2c_con)) = 0; + + return 0; +} + +static int davinci_i2c_write(struct i2c_adapter *adap, uint8_t chip, + uint32_t addr, int alen, uint8_t *buf, int len) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + uint32_t tmp; + int i; + + if ((alen < 0) || (alen > 2)) { + printf("%s(): bogus address length %x\n", __func__, alen); + return 1; + } + if (len < 0) { + printf("%s(): bogus length %x\n", __func__, len); + return 1; + } + + if (wait_for_bus(adap)) + return 1; + + /* Start address phase */ + tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | + I2C_CON_TRX | I2C_CON_STP; + REG(&(i2c_base->i2c_cnt)) = (alen == 0) ? + len & 0xffff : (len & 0xffff) + alen; + REG(&(i2c_base->i2c_sa)) = chip; + REG(&(i2c_base->i2c_con)) = tmp; + + switch (alen) { + case 2: + /* Send address MSByte */ + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + if (tmp & I2C_STAT_XRDY) { + REG(&(i2c_base->i2c_dxr)) = (addr >> 8) & 0xff; + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + /* No break, fall through */ + case 1: + /* Send address LSByte */ + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + if (tmp & I2C_STAT_XRDY) { + REG(&(i2c_base->i2c_dxr)) = addr & 0xff; + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + } + + for (i = 0; i < len; i++) { + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + if (tmp & I2C_STAT_XRDY) + REG(&(i2c_base->i2c_dxr)) = buf[i]; + else + return 1; + } + + tmp = poll_i2c_irq(adap, I2C_STAT_SCD | I2C_STAT_NACK); + + CHECK_NACK(); + + if (!(tmp & I2C_STAT_SCD)) { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + + flush_rx(adap); + REG(&(i2c_base->i2c_stat)) = 0xffff; + REG(&(i2c_base->i2c_cnt)) = 0; + REG(&(i2c_base->i2c_con)) = 0; + + return 0; +} + +static struct i2c_regs *davinci_get_base(struct i2c_adapter *adap) +{ + switch (adap->hwadapnr) { +#if I2C_BUS_MAX >= 3 + case 2: + return (struct i2c_regs *)I2C2_BASE; +#endif +#if I2C_BUS_MAX >= 2 + case 1: + return (struct i2c_regs *)I2C1_BASE; +#endif + case 0: + return (struct i2c_regs *)I2C_BASE; + + default: + printf("wrong hwadapnr: %d\n", adap->hwadapnr); + } + + return NULL; +} + +U_BOOT_I2C_ADAP_COMPLETE(davinci_0, davinci_i2c_init, davinci_i2c_probe, + davinci_i2c_read, davinci_i2c_write, + davinci_i2c_setspeed, + CONFIG_SYS_DAVINCI_I2C_SPEED, + CONFIG_SYS_DAVINCI_I2C_SLAVE, + 0) + +#if I2C_BUS_MAX >= 2 +U_BOOT_I2C_ADAP_COMPLETE(davinci_1, davinci_i2c_init, davinci_i2c_probe, + davinci_i2c_read, davinci_i2c_write, + davinci_i2c_setspeed, + CONFIG_SYS_DAVINCI_I2C_SPEED1, + CONFIG_SYS_DAVINCI_I2C_SLAVE1, + 1) +#endif + +#if I2C_BUS_MAX >= 3 +U_BOOT_I2C_ADAP_COMPLETE(davinci_2, davinci_i2c_init, davinci_i2c_probe, + davinci_i2c_read, davinci_i2c_write, + davinci_i2c_setspeed, + CONFIG_SYS_DAVINCI_I2C_SPEED2, + CONFIG_SYS_DAVINCI_I2C_SLAVE2, + 2) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/davinci_i2c.h b/qemu/roms/u-boot/drivers/i2c/davinci_i2c.h new file mode 100644 index 000000000..20d43424b --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/davinci_i2c.h @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2004-2014 + * Texas Instruments, + * + * Some changes copyright (C) 2007 Sergey Kubushyn + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _DAVINCI_I2C_H_ +#define _DAVINCI_I2C_H_ + +#define I2C_WRITE 0 +#define I2C_READ 1 + +struct i2c_regs { + u32 i2c_oa; + u32 i2c_ie; + u32 i2c_stat; + u32 i2c_scll; + u32 i2c_sclh; + u32 i2c_cnt; + u32 i2c_drr; + u32 i2c_sa; + u32 i2c_dxr; + u32 i2c_con; + u32 i2c_iv; + u32 res_2c; + u32 i2c_psc; +}; + +/* I2C masks */ + +/* I2C Interrupt Enable Register (I2C_IE): */ +#define I2C_IE_SCD_IE (1 << 5) /* Stop condition detect interrupt enable */ +#define I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt enable */ +#define I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt enable */ +#define I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt enable */ +#define I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Status Register (I2C_STAT): */ + +#define I2C_STAT_BB (1 << 12) /* Bus busy */ +#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */ +#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ +#define I2C_STAT_AAS (1 << 9) /* Address as slave */ +#define I2C_STAT_SCD (1 << 5) /* Stop condition detect */ +#define I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ +#define I2C_STAT_RRDY (1 << 3) /* Receive data ready */ +#define I2C_STAT_ARDY (1 << 2) /* Register access ready */ +#define I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Interrupt Code Register (I2C_INTCODE): */ + +#define I2C_INTCODE_MASK 7 +#define I2C_INTCODE_NONE 0 +#define I2C_INTCODE_AL 1 /* Arbitration lost */ +#define I2C_INTCODE_NAK 2 /* No acknowledgement/general call */ +#define I2C_INTCODE_ARDY 3 /* Register access ready */ +#define I2C_INTCODE_RRDY 4 /* Rcv data ready */ +#define I2C_INTCODE_XRDY 5 /* Xmit data ready */ +#define I2C_INTCODE_SCD 6 /* Stop condition detect */ + +/* I2C Configuration Register (I2C_CON): */ + +#define I2C_CON_EN (1 << 5) /* I2C module enable */ +#define I2C_CON_STB (1 << 4) /* Start byte mode (master mode only) */ +#define I2C_CON_MST (1 << 10) /* Master/slave mode */ +#define I2C_CON_TRX (1 << 9) /* Tx/Rx mode (master mode only) */ +#define I2C_CON_XA (1 << 8) /* Expand address */ +#define I2C_CON_STP (1 << 11) /* Stop condition (master mode only) */ +#define I2C_CON_STT (1 << 13) /* Start condition (master mode only) */ +#define I2C_CON_FREE (1 << 14) /* Free run on emulation */ + +#define I2C_TIMEOUT 0xffff0000 /* Timeout mask for poll_i2c_irq() */ + +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/designware_i2c.c b/qemu/roms/u-boot/drivers/i2c/designware_i2c.c new file mode 100644 index 000000000..c891ebd39 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/designware_i2c.c @@ -0,0 +1,436 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include "designware_i2c.h" + +#ifdef CONFIG_I2C_MULTI_BUS +static unsigned int bus_initialized[CONFIG_SYS_I2C_BUS_MAX]; +static unsigned int current_bus = 0; +#endif + +static struct i2c_regs *i2c_regs_p = + (struct i2c_regs *)CONFIG_SYS_I2C_BASE; + +/* + * set_speed - Set the i2c speed mode (standard, high, fast) + * @i2c_spd: required i2c speed mode + * + * Set the i2c speed mode (standard, high, fast) + */ +static void set_speed(int i2c_spd) +{ + unsigned int cntl; + unsigned int hcnt, lcnt; + unsigned int enbl; + + /* to set speed cltr must be disabled */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl &= ~IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + + cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK)); + + switch (i2c_spd) { + case IC_SPEED_MODE_MAX: + cntl |= IC_CON_SPD_HS; + hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_hs_scl_hcnt); + lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_hs_scl_lcnt); + break; + + case IC_SPEED_MODE_STANDARD: + cntl |= IC_CON_SPD_SS; + hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_ss_scl_hcnt); + lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_ss_scl_lcnt); + break; + + case IC_SPEED_MODE_FAST: + default: + cntl |= IC_CON_SPD_FS; + hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt); + lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt); + break; + } + + writel(cntl, &i2c_regs_p->ic_con); + + /* Enable back i2c now speed set */ + enbl |= IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); +} + +/* + * i2c_set_bus_speed - Set the i2c speed + * @speed: required i2c speed + * + * Set the i2c speed. + */ +int i2c_set_bus_speed(int speed) +{ + if (speed >= I2C_MAX_SPEED) + set_speed(IC_SPEED_MODE_MAX); + else if (speed >= I2C_FAST_SPEED) + set_speed(IC_SPEED_MODE_FAST); + else + set_speed(IC_SPEED_MODE_STANDARD); + + return 0; +} + +/* + * i2c_get_bus_speed - Gets the i2c speed + * + * Gets the i2c speed. + */ +int i2c_get_bus_speed(void) +{ + u32 cntl; + + cntl = (readl(&i2c_regs_p->ic_con) & IC_CON_SPD_MSK); + + if (cntl == IC_CON_SPD_HS) + return I2C_MAX_SPEED; + else if (cntl == IC_CON_SPD_FS) + return I2C_FAST_SPEED; + else if (cntl == IC_CON_SPD_SS) + return I2C_STANDARD_SPEED; + + return 0; +} + +/* + * i2c_init - Init function + * @speed: required i2c speed + * @slaveadd: slave address for the device + * + * Initialization function. + */ +void i2c_init(int speed, int slaveadd) +{ + unsigned int enbl; + + /* Disable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl &= ~IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + + writel((IC_CON_SD | IC_CON_SPD_FS | IC_CON_MM), &i2c_regs_p->ic_con); + writel(IC_RX_TL, &i2c_regs_p->ic_rx_tl); + writel(IC_TX_TL, &i2c_regs_p->ic_tx_tl); + i2c_set_bus_speed(speed); + writel(IC_STOP_DET, &i2c_regs_p->ic_intr_mask); + writel(slaveadd, &i2c_regs_p->ic_sar); + + /* Enable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl |= IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + +#ifdef CONFIG_I2C_MULTI_BUS + bus_initialized[current_bus] = 1; +#endif +} + +/* + * i2c_setaddress - Sets the target slave address + * @i2c_addr: target i2c address + * + * Sets the target slave address. + */ +static void i2c_setaddress(unsigned int i2c_addr) +{ + unsigned int enbl; + + /* Disable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl &= ~IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + + writel(i2c_addr, &i2c_regs_p->ic_tar); + + /* Enable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl |= IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); +} + +/* + * i2c_flush_rxfifo - Flushes the i2c RX FIFO + * + * Flushes the i2c RX FIFO + */ +static void i2c_flush_rxfifo(void) +{ + while (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) + readl(&i2c_regs_p->ic_cmd_data); +} + +/* + * i2c_wait_for_bb - Waits for bus busy + * + * Waits for bus busy + */ +static int i2c_wait_for_bb(void) +{ + unsigned long start_time_bb = get_timer(0); + + while ((readl(&i2c_regs_p->ic_status) & IC_STATUS_MA) || + !(readl(&i2c_regs_p->ic_status) & IC_STATUS_TFE)) { + + /* Evaluate timeout */ + if (get_timer(start_time_bb) > (unsigned long)(I2C_BYTE_TO_BB)) + return 1; + } + + return 0; +} + +static int i2c_xfer_init(uchar chip, uint addr, int alen) +{ + if (i2c_wait_for_bb()) + return 1; + + i2c_setaddress(chip); + while (alen) { + alen--; + /* high byte address going out first */ + writel((addr >> (alen * 8)) & 0xff, + &i2c_regs_p->ic_cmd_data); + } + return 0; +} + +static int i2c_xfer_finish(void) +{ + ulong start_stop_det = get_timer(0); + + while (1) { + if ((readl(&i2c_regs_p->ic_raw_intr_stat) & IC_STOP_DET)) { + readl(&i2c_regs_p->ic_clr_stop_det); + break; + } else if (get_timer(start_stop_det) > I2C_STOPDET_TO) { + break; + } + } + + if (i2c_wait_for_bb()) { + printf("Timed out waiting for bus\n"); + return 1; + } + + i2c_flush_rxfifo(); + + return 0; +} + +/* + * i2c_read - Read from i2c memory + * @chip: target i2c address + * @addr: address to read from + * @alen: + * @buffer: buffer for read data + * @len: no of bytes to be read + * + * Read from i2c memory. + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + unsigned long start_time_rx; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); + addr &= ~(CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW << (alen * 8)); + + debug("%s: fix addr_overflow: chip %02x addr %02x\n", __func__, chip, + addr); +#endif + + if (i2c_xfer_init(chip, addr, alen)) + return 1; + + start_time_rx = get_timer(0); + while (len) { + if (len == 1) + writel(IC_CMD | IC_STOP, &i2c_regs_p->ic_cmd_data); + else + writel(IC_CMD, &i2c_regs_p->ic_cmd_data); + + if (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) { + *buffer++ = (uchar)readl(&i2c_regs_p->ic_cmd_data); + len--; + start_time_rx = get_timer(0); + + } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { + return 1; + } + } + + return i2c_xfer_finish(); +} + +/* + * i2c_write - Write to i2c memory + * @chip: target i2c address + * @addr: address to read from + * @alen: + * @buffer: buffer for read data + * @len: no of bytes to be read + * + * Write to i2c memory. + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int nb = len; + unsigned long start_time_tx; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); + addr &= ~(CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW << (alen * 8)); + + debug("%s: fix addr_overflow: chip %02x addr %02x\n", __func__, chip, + addr); +#endif + + if (i2c_xfer_init(chip, addr, alen)) + return 1; + + start_time_tx = get_timer(0); + while (len) { + if (readl(&i2c_regs_p->ic_status) & IC_STATUS_TFNF) { + if (--len == 0) + writel(*buffer | IC_STOP, &i2c_regs_p->ic_cmd_data); + else + writel(*buffer, &i2c_regs_p->ic_cmd_data); + buffer++; + start_time_tx = get_timer(0); + + } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { + printf("Timed out. i2c write Failed\n"); + return 1; + } + } + + return i2c_xfer_finish(); +} + +/* + * i2c_probe - Probe the i2c chip + */ +int i2c_probe(uchar chip) +{ + u32 tmp; + int ret; + + /* + * Try to read the first location of the chip. + */ + ret = i2c_read(chip, 0, 1, (uchar *)&tmp, 1); + if (ret) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return ret; +} + +#ifdef CONFIG_I2C_MULTI_BUS +int i2c_set_bus_num(unsigned int bus) +{ + switch (bus) { + case 0: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE; + break; +#ifdef CONFIG_SYS_I2C_BASE1 + case 1: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE1; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE2 + case 2: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE2; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE3 + case 3: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE3; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE4 + case 4: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE4; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE5 + case 5: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE5; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE6 + case 6: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE6; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE7 + case 7: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE7; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE8 + case 8: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE8; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE9 + case 9: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE9; + break; +#endif + default: + printf("Bad bus: %d\n", bus); + return -1; + } + + current_bus = bus; + + if (!bus_initialized[current_bus]) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return 0; +} + +int i2c_get_bus_num(void) +{ + return current_bus; +} +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/designware_i2c.h b/qemu/roms/u-boot/drivers/i2c/designware_i2c.h new file mode 100644 index 000000000..19b09dfa5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/designware_i2c.h @@ -0,0 +1,133 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DW_I2C_H_ +#define __DW_I2C_H_ + +struct i2c_regs { + u32 ic_con; + u32 ic_tar; + u32 ic_sar; + u32 ic_hs_maddr; + u32 ic_cmd_data; + u32 ic_ss_scl_hcnt; + u32 ic_ss_scl_lcnt; + u32 ic_fs_scl_hcnt; + u32 ic_fs_scl_lcnt; + u32 ic_hs_scl_hcnt; + u32 ic_hs_scl_lcnt; + u32 ic_intr_stat; + u32 ic_intr_mask; + u32 ic_raw_intr_stat; + u32 ic_rx_tl; + u32 ic_tx_tl; + u32 ic_clr_intr; + u32 ic_clr_rx_under; + u32 ic_clr_rx_over; + u32 ic_clr_tx_over; + u32 ic_clr_rd_req; + u32 ic_clr_tx_abrt; + u32 ic_clr_rx_done; + u32 ic_clr_activity; + u32 ic_clr_stop_det; + u32 ic_clr_start_det; + u32 ic_clr_gen_call; + u32 ic_enable; + u32 ic_status; + u32 ic_txflr; + u32 ix_rxflr; + u32 reserved_1; + u32 ic_tx_abrt_source; +}; + +#if !defined(IC_CLK) +#define IC_CLK 166 +#endif +#define NANO_TO_MICRO 1000 + +/* High and low times in different speed modes (in ns) */ +#define MIN_SS_SCL_HIGHTIME 4000 +#define MIN_SS_SCL_LOWTIME 4700 +#define MIN_FS_SCL_HIGHTIME 600 +#define MIN_FS_SCL_LOWTIME 1300 +#define MIN_HS_SCL_HIGHTIME 60 +#define MIN_HS_SCL_LOWTIME 160 + +/* Worst case timeout for 1 byte is kept as 2ms */ +#define I2C_BYTE_TO (CONFIG_SYS_HZ/500) +#define I2C_STOPDET_TO (CONFIG_SYS_HZ/500) +#define I2C_BYTE_TO_BB (I2C_BYTE_TO * 16) + +/* i2c control register definitions */ +#define IC_CON_SD 0x0040 +#define IC_CON_RE 0x0020 +#define IC_CON_10BITADDRMASTER 0x0010 +#define IC_CON_10BITADDR_SLAVE 0x0008 +#define IC_CON_SPD_MSK 0x0006 +#define IC_CON_SPD_SS 0x0002 +#define IC_CON_SPD_FS 0x0004 +#define IC_CON_SPD_HS 0x0006 +#define IC_CON_MM 0x0001 + +/* i2c target address register definitions */ +#define TAR_ADDR 0x0050 + +/* i2c slave address register definitions */ +#define IC_SLAVE_ADDR 0x0002 + +/* i2c data buffer and command register definitions */ +#define IC_CMD 0x0100 +#define IC_STOP 0x0200 + +/* i2c interrupt status register definitions */ +#define IC_GEN_CALL 0x0800 +#define IC_START_DET 0x0400 +#define IC_STOP_DET 0x0200 +#define IC_ACTIVITY 0x0100 +#define IC_RX_DONE 0x0080 +#define IC_TX_ABRT 0x0040 +#define IC_RD_REQ 0x0020 +#define IC_TX_EMPTY 0x0010 +#define IC_TX_OVER 0x0008 +#define IC_RX_FULL 0x0004 +#define IC_RX_OVER 0x0002 +#define IC_RX_UNDER 0x0001 + +/* fifo threshold register definitions */ +#define IC_TL0 0x00 +#define IC_TL1 0x01 +#define IC_TL2 0x02 +#define IC_TL3 0x03 +#define IC_TL4 0x04 +#define IC_TL5 0x05 +#define IC_TL6 0x06 +#define IC_TL7 0x07 +#define IC_RX_TL IC_TL0 +#define IC_TX_TL IC_TL0 + +/* i2c enable register definitions */ +#define IC_ENABLE_0B 0x0001 + +/* i2c status register definitions */ +#define IC_STATUS_SA 0x0040 +#define IC_STATUS_MA 0x0020 +#define IC_STATUS_RFF 0x0010 +#define IC_STATUS_RFNE 0x0008 +#define IC_STATUS_TFE 0x0004 +#define IC_STATUS_TFNF 0x0002 +#define IC_STATUS_ACT 0x0001 + +/* Speed Selection */ +#define IC_SPEED_MODE_STANDARD 1 +#define IC_SPEED_MODE_FAST 2 +#define IC_SPEED_MODE_MAX 3 + +#define I2C_MAX_SPEED 3400000 +#define I2C_FAST_SPEED 400000 +#define I2C_STANDARD_SPEED 100000 + +#endif /* __DW_I2C_H_ */ diff --git a/qemu/roms/u-boot/drivers/i2c/fsl_i2c.c b/qemu/roms/u-boot/drivers/i2c/fsl_i2c.c new file mode 100644 index 000000000..aa159f8d4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/fsl_i2c.c @@ -0,0 +1,541 @@ +/* + * Copyright 2006,2009 Freescale Semiconductor, Inc. + * + * 2012, Heiko Schocher, DENX Software Engineering, hs@denx.de. + * Changes for multibus/multiadapter I2C support. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include /* Functional interface */ +#include +#include /* HW definitions */ + +/* The maximum number of microseconds we will wait until another master has + * released the bus. If not defined in the board header file, then use a + * generic value. + */ +#ifndef CONFIG_I2C_MBB_TIMEOUT +#define CONFIG_I2C_MBB_TIMEOUT 100000 +#endif + +/* The maximum number of microseconds we will wait for a read or write + * operation to complete. If not defined in the board header file, then use a + * generic value. + */ +#ifndef CONFIG_I2C_TIMEOUT +#define CONFIG_I2C_TIMEOUT 10000 +#endif + +#define I2C_READ_BIT 1 +#define I2C_WRITE_BIT 0 + +DECLARE_GLOBAL_DATA_PTR; + +static const struct fsl_i2c *i2c_dev[2] = { + (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C_OFFSET), +#ifdef CONFIG_SYS_FSL_I2C2_OFFSET + (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C2_OFFSET) +#endif +}; + +/* I2C speed map for a DFSR value of 1 */ + +/* + * Map I2C frequency dividers to FDR and DFSR values + * + * This structure is used to define the elements of a table that maps I2C + * frequency divider (I2C clock rate divided by I2C bus speed) to a value to be + * programmed into the Frequency Divider Ratio (FDR) and Digital Filter + * Sampling Rate (DFSR) registers. + * + * The actual table should be defined in the board file, and it must be called + * fsl_i2c_speed_map[]. + * + * The last entry of the table must have a value of {-1, X}, where X is same + * FDR/DFSR values as the second-to-last entry. This guarantees that any + * search through the array will always find a match. + * + * The values of the divider must be in increasing numerical order, i.e. + * fsl_i2c_speed_map[x+1].divider > fsl_i2c_speed_map[x].divider. + * + * For this table, the values are based on a value of 1 for the DFSR + * register. See the application note AN2919 "Determining the I2C Frequency + * Divider Ratio for SCL" + * + * ColdFire I2C frequency dividers for FDR values are different from + * PowerPC. The protocol to use the I2C module is still the same. + * A different table is defined and are based on MCF5xxx user manual. + * + */ +static const struct { + unsigned short divider; + u8 fdr; +} fsl_i2c_speed_map[] = { +#ifdef __M68K__ + {20, 32}, {22, 33}, {24, 34}, {26, 35}, + {28, 0}, {28, 36}, {30, 1}, {32, 37}, + {34, 2}, {36, 38}, {40, 3}, {40, 39}, + {44, 4}, {48, 5}, {48, 40}, {56, 6}, + {56, 41}, {64, 42}, {68, 7}, {72, 43}, + {80, 8}, {80, 44}, {88, 9}, {96, 41}, + {104, 10}, {112, 42}, {128, 11}, {128, 43}, + {144, 12}, {160, 13}, {160, 48}, {192, 14}, + {192, 49}, {224, 50}, {240, 15}, {256, 51}, + {288, 16}, {320, 17}, {320, 52}, {384, 18}, + {384, 53}, {448, 54}, {480, 19}, {512, 55}, + {576, 20}, {640, 21}, {640, 56}, {768, 22}, + {768, 57}, {960, 23}, {896, 58}, {1024, 59}, + {1152, 24}, {1280, 25}, {1280, 60}, {1536, 26}, + {1536, 61}, {1792, 62}, {1920, 27}, {2048, 63}, + {2304, 28}, {2560, 29}, {3072, 30}, {3840, 31}, + {-1, 31} +#endif +}; + +/** + * Set the I2C bus speed for a given I2C device + * + * @param dev: the I2C device + * @i2c_clk: I2C bus clock frequency + * @speed: the desired speed of the bus + * + * The I2C device must be stopped before calling this function. + * + * The return value is the actual bus speed that is set. + */ +static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev, + unsigned int i2c_clk, unsigned int speed) +{ + unsigned short divider = min(i2c_clk / speed, (unsigned short) -1); + + /* + * We want to choose an FDR/DFSR that generates an I2C bus speed that + * is equal to or lower than the requested speed. That means that we + * want the first divider that is equal to or greater than the + * calculated divider. + */ +#ifdef __PPC__ + u8 dfsr, fdr = 0x31; /* Default if no FDR found */ + /* a, b and dfsr matches identifiers A,B and C respectively in AN2919 */ + unsigned short a, b, ga, gb; + unsigned long c_div, est_div; + +#ifdef CONFIG_FSL_I2C_CUSTOM_DFSR + dfsr = CONFIG_FSL_I2C_CUSTOM_DFSR; +#else + /* Condition 1: dfsr <= 50/T */ + dfsr = (5 * (i2c_clk / 1000)) / 100000; +#endif +#ifdef CONFIG_FSL_I2C_CUSTOM_FDR + fdr = CONFIG_FSL_I2C_CUSTOM_FDR; + speed = i2c_clk / divider; /* Fake something */ +#else + debug("Requested speed:%d, i2c_clk:%d\n", speed, i2c_clk); + if (!dfsr) + dfsr = 1; + + est_div = ~0; + for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) { + for (gb = 0; gb < 8; gb++) { + b = 16 << gb; + c_div = b * (a + ((3*dfsr)/b)*2); + if ((c_div > divider) && (c_div < est_div)) { + unsigned short bin_gb, bin_ga; + + est_div = c_div; + bin_gb = gb << 2; + bin_ga = (ga & 0x3) | ((ga & 0x4) << 3); + fdr = bin_gb | bin_ga; + speed = i2c_clk / est_div; + debug("FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x, " + "a:%d, b:%d, speed:%d\n", + fdr, est_div, ga, gb, a, b, speed); + /* Condition 2 not accounted for */ + debug("Tr <= %d ns\n", + (b - 3 * dfsr) * 1000000 / + (i2c_clk / 1000)); + } + } + if (a == 20) + a += 2; + if (a == 24) + a += 4; + } + debug("divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr); + debug("FDR:0x%.2x, speed:%d\n", fdr, speed); +#endif + writeb(dfsr, &dev->dfsrr); /* set default filter */ + writeb(fdr, &dev->fdr); /* set bus speed */ +#else + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fsl_i2c_speed_map); i++) + if (fsl_i2c_speed_map[i].divider >= divider) { + u8 fdr; + + fdr = fsl_i2c_speed_map[i].fdr; + speed = i2c_clk / fsl_i2c_speed_map[i].divider; + writeb(fdr, &dev->fdr); /* set bus speed */ + + break; + } +#endif + return speed; +} + +static unsigned int get_i2c_clock(int bus) +{ + if (bus) + return gd->arch.i2c2_clk; /* I2C2 clock */ + else + return gd->arch.i2c1_clk; /* I2C1 clock */ +} + +static int fsl_i2c_fixup(const struct fsl_i2c *dev) +{ + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + unsigned long long timeval = 0; + int ret = -1; + unsigned int flags = 0; + +#ifdef CONFIG_SYS_FSL_ERRATUM_I2C_A004447 + unsigned int svr = get_svr(); + if ((SVR_SOC_VER(svr) == SVR_8548 && IS_SVR_REV(svr, 3, 1)) || + (SVR_REV(svr) <= CONFIG_SYS_FSL_A004447_SVR_REV)) + flags = I2C_CR_BIT6; +#endif + + writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr); + + timeval = get_ticks(); + while (!(readb(&dev->sr) & I2C_SR_MBB)) { + if ((get_ticks() - timeval) > timeout) + goto err; + } + + if (readb(&dev->sr) & I2C_SR_MAL) { + /* SDA is stuck low */ + writeb(0, &dev->cr); + udelay(100); + writeb(I2C_CR_MSTA | flags, &dev->cr); + writeb(I2C_CR_MEN | I2C_CR_MSTA | flags, &dev->cr); + } + + readb(&dev->dr); + + timeval = get_ticks(); + while (!(readb(&dev->sr) & I2C_SR_MIF)) { + if ((get_ticks() - timeval) > timeout) + goto err; + } + ret = 0; + +err: + writeb(I2C_CR_MEN | flags, &dev->cr); + writeb(0, &dev->sr); + udelay(100); + + return ret; +} + +static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + const struct fsl_i2c *dev; + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + unsigned long long timeval; + +#ifdef CONFIG_SYS_I2C_INIT_BOARD + /* Call board specific i2c bus reset routine before accessing the + * environment, which might be in a chip on that bus. For details + * about this problem see doc/I2C_Edge_Conditions. + */ + i2c_init_board(); +#endif + dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + + writeb(0, &dev->cr); /* stop I2C controller */ + udelay(5); /* let it shutdown in peace */ + set_i2c_bus_speed(dev, get_i2c_clock(adap->hwadapnr), speed); + writeb(slaveadd << 1, &dev->adr);/* write slave address */ + writeb(0x0, &dev->sr); /* clear status register */ + writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ + + timeval = get_ticks(); + while (readb(&dev->sr) & I2C_SR_MBB) { + if ((get_ticks() - timeval) < timeout) + continue; + + if (fsl_i2c_fixup(dev)) + debug("i2c_init: BUS#%d failed to init\n", + adap->hwadapnr); + + break; + } + +#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT + /* Call board specific i2c bus reset routine AFTER the bus has been + * initialized. Use either this callpoint or i2c_init_board; + * which is called before i2c_init operations. + * For details about this problem see doc/I2C_Edge_Conditions. + */ + i2c_board_late_init(); +#endif +} + +static int +i2c_wait4bus(struct i2c_adapter *adap) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + unsigned long long timeval = get_ticks(); + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + + while (readb(&dev->sr) & I2C_SR_MBB) { + if ((get_ticks() - timeval) > timeout) + return -1; + } + + return 0; +} + +static __inline__ int +i2c_wait(struct i2c_adapter *adap, int write) +{ + u32 csr; + unsigned long long timeval = get_ticks(); + const unsigned long long timeout = usec2ticks(CONFIG_I2C_TIMEOUT); + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + + do { + csr = readb(&dev->sr); + if (!(csr & I2C_SR_MIF)) + continue; + /* Read again to allow register to stabilise */ + csr = readb(&dev->sr); + + writeb(0x0, &dev->sr); + + if (csr & I2C_SR_MAL) { + debug("i2c_wait: MAL\n"); + return -1; + } + + if (!(csr & I2C_SR_MCF)) { + debug("i2c_wait: unfinished\n"); + return -1; + } + + if (write == I2C_WRITE_BIT && (csr & I2C_SR_RXAK)) { + debug("i2c_wait: No RXACK\n"); + return -1; + } + + return 0; + } while ((get_ticks() - timeval) < timeout); + + debug("i2c_wait: timed out\n"); + return -1; +} + +static __inline__ int +i2c_write_addr(struct i2c_adapter *adap, u8 dev, u8 dir, int rsta) +{ + struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX + | (rsta ? I2C_CR_RSTA : 0), + &device->cr); + + writeb((dev << 1) | dir, &device->dr); + + if (i2c_wait(adap, I2C_WRITE_BIT) < 0) + return 0; + + return 1; +} + +static __inline__ int +__i2c_write(struct i2c_adapter *adap, u8 *data, int length) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + int i; + + for (i = 0; i < length; i++) { + writeb(data[i], &dev->dr); + + if (i2c_wait(adap, I2C_WRITE_BIT) < 0) + break; + } + + return i; +} + +static __inline__ int +__i2c_read(struct i2c_adapter *adap, u8 *data, int length) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + int i; + + writeb(I2C_CR_MEN | I2C_CR_MSTA | ((length == 1) ? I2C_CR_TXAK : 0), + &dev->cr); + + /* dummy read */ + readb(&dev->dr); + + for (i = 0; i < length; i++) { + if (i2c_wait(adap, I2C_READ_BIT) < 0) + break; + + /* Generate ack on last next to last byte */ + if (i == length - 2) + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_TXAK, + &dev->cr); + + /* Do not generate stop on last byte */ + if (i == length - 1) + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX, + &dev->cr); + + data[i] = readb(&dev->dr); + } + + return i; +} + +static int +fsl_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr, int alen, u8 *data, + int length) +{ + struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + int i = -1; /* signal error */ + u8 *a = (u8*)&addr; + int len = alen * -1; + + if (i2c_wait4bus(adap) < 0) + return -1; + + /* To handle the need of I2C devices that require to write few bytes + * (more than 4 bytes of address as in the case of else part) + * of data before reading, Negative equivalent of length(bytes to write) + * is passed, but used the +ve part of len for writing data + */ + if (alen < 0) { + /* Generate a START and send the Address and + * the Tx Bytes to the slave. + * "START: Address: Write bytes data[len]" + * IF part supports writing any number of bytes in contrast + * to the else part, which supports writing address offset + * of upto 4 bytes only. + * bytes that need to be written are passed in + * "data", which will eventually keep the data READ, + * after writing the len bytes out of it + */ + if (i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0) + i = __i2c_write(adap, data, len); + + if (i != len) + return -1; + + if (length && i2c_write_addr(adap, dev, I2C_READ_BIT, 1) != 0) + i = __i2c_read(adap, data, length); + } else { + if ((!length || alen > 0) && + i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 && + __i2c_write(adap, &a[4 - alen], alen) == alen) + i = 0; /* No error so far */ + + if (length && + i2c_write_addr(adap, dev, I2C_READ_BIT, alen ? 1 : 0) != 0) + i = __i2c_read(adap, data, length); + } + + writeb(I2C_CR_MEN, &device->cr); + + if (i2c_wait4bus(adap)) /* Wait until STOP */ + debug("i2c_read: wait4bus timed out\n"); + + if (i == length) + return 0; + + return -1; +} + +static int +fsl_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr, int alen, + u8 *data, int length) +{ + struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + int i = -1; /* signal error */ + u8 *a = (u8*)&addr; + + if (i2c_wait4bus(adap) < 0) + return -1; + + if (i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 && + __i2c_write(adap, &a[4 - alen], alen) == alen) { + i = __i2c_write(adap, data, length); + } + + writeb(I2C_CR_MEN, &device->cr); + if (i2c_wait4bus(adap)) /* Wait until STOP */ + debug("i2c_write: wait4bus timed out\n"); + + if (i == length) + return 0; + + return -1; +} + +static int +fsl_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + /* For unknow reason the controller will ACK when + * probing for a slave with the same address, so skip + * it. + */ + if (chip == (readb(&dev->adr) >> 1)) + return -1; + + return fsl_i2c_read(adap, chip, 0, 0, NULL, 0); +} + +static unsigned int fsl_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + + writeb(0, &dev->cr); /* stop controller */ + set_i2c_bus_speed(dev, get_i2c_clock(adap->hwadapnr), speed); + writeb(I2C_CR_MEN, &dev->cr); /* start controller */ + + return 0; +} + +/* + * Register fsl i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(fsl_0, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read, + fsl_i2c_write, fsl_i2c_set_bus_speed, + CONFIG_SYS_FSL_I2C_SPEED, CONFIG_SYS_FSL_I2C_SLAVE, + 0) +#ifdef CONFIG_SYS_FSL_I2C2_OFFSET +U_BOOT_I2C_ADAP_COMPLETE(fsl_1, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read, + fsl_i2c_write, fsl_i2c_set_bus_speed, + CONFIG_SYS_FSL_I2C2_SPEED, CONFIG_SYS_FSL_I2C2_SLAVE, + 1) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/fti2c010.c b/qemu/roms/u-boot/drivers/i2c/fti2c010.c new file mode 100644 index 000000000..68d9a4291 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/fti2c010.c @@ -0,0 +1,346 @@ +/* + * Faraday I2C Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include "fti2c010.h" + +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 5000 +#endif + +#ifndef CONFIG_SYS_I2C_SLAVE +#define CONFIG_SYS_I2C_SLAVE 0 +#endif + +#ifndef CONFIG_FTI2C010_CLOCK +#define CONFIG_FTI2C010_CLOCK clk_get_rate("I2C") +#endif + +#ifndef CONFIG_FTI2C010_TIMEOUT +#define CONFIG_FTI2C010_TIMEOUT 10 /* ms */ +#endif + +/* 7-bit dev address + 1-bit read/write */ +#define I2C_RD(dev) ((((dev) << 1) & 0xfe) | 1) +#define I2C_WR(dev) (((dev) << 1) & 0xfe) + +struct fti2c010_chip { + struct fti2c010_regs *regs; +}; + +static struct fti2c010_chip chip_list[] = { + { + .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE, + }, +#ifdef CONFIG_FTI2C010_BASE1 + { + .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE1, + }, +#endif +#ifdef CONFIG_FTI2C010_BASE2 + { + .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE2, + }, +#endif +#ifdef CONFIG_FTI2C010_BASE3 + { + .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE3, + }, +#endif +}; + +static int fti2c010_reset(struct fti2c010_chip *chip) +{ + ulong ts; + int ret = -1; + struct fti2c010_regs *regs = chip->regs; + + writel(CR_I2CRST, ®s->cr); + for (ts = get_timer(0); get_timer(ts) < CONFIG_FTI2C010_TIMEOUT; ) { + if (!(readl(®s->cr) & CR_I2CRST)) { + ret = 0; + break; + } + } + + if (ret) + printf("fti2c010: reset timeout\n"); + + return ret; +} + +static int fti2c010_wait(struct fti2c010_chip *chip, uint32_t mask) +{ + int ret = -1; + uint32_t stat, ts; + struct fti2c010_regs *regs = chip->regs; + + for (ts = get_timer(0); get_timer(ts) < CONFIG_FTI2C010_TIMEOUT; ) { + stat = readl(®s->sr); + if ((stat & mask) == mask) { + ret = 0; + break; + } + } + + return ret; +} + +static unsigned int set_i2c_bus_speed(struct fti2c010_chip *chip, + unsigned int speed) +{ + struct fti2c010_regs *regs = chip->regs; + unsigned int clk = CONFIG_FTI2C010_CLOCK; + unsigned int gsr = 0; + unsigned int tsr = 32; + unsigned int div, rate; + + for (div = 0; div < 0x3ffff; ++div) { + /* SCLout = PCLK/(2*(COUNT + 2) + GSR) */ + rate = clk / (2 * (div + 2) + gsr); + if (rate <= speed) + break; + } + + writel(TGSR_GSR(gsr) | TGSR_TSR(tsr), ®s->tgsr); + writel(CDR_DIV(div), ®s->cdr); + + return rate; +} + +/* + * Initialization, must be called once on start up, may be called + * repeatedly to change the speed and slave addresses. + */ +static void fti2c010_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + + if (adap->init_done) + return; + +#ifdef CONFIG_SYS_I2C_INIT_BOARD + /* Call board specific i2c bus reset routine before accessing the + * environment, which might be in a chip on that bus. For details + * about this problem see doc/I2C_Edge_Conditions. + */ + i2c_init_board(); +#endif + + /* master init */ + + fti2c010_reset(chip); + + set_i2c_bus_speed(chip, speed); + + /* slave init, don't care */ + +#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT + /* Call board specific i2c bus reset routine AFTER the bus has been + * initialized. Use either this callpoint or i2c_init_board; + * which is called before fti2c010_init operations. + * For details about this problem see doc/I2C_Edge_Conditions. + */ + i2c_board_late_init(); +#endif +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +static int fti2c010_probe(struct i2c_adapter *adap, u8 dev) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + struct fti2c010_regs *regs = chip->regs; + int ret; + + /* 1. Select slave device (7bits Address + 1bit R/W) */ + writel(I2C_WR(dev), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + + /* 2. Select device register */ + writel(0, ®s->dr); + writel(CR_ENABLE | CR_TBEN, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + + return ret; +} + +static void to_i2c_addr(u8 *buf, uint32_t addr, int alen) +{ + int i, shift; + + if (!buf || alen <= 0) + return; + + /* MSB first */ + i = 0; + shift = (alen - 1) * 8; + while (alen-- > 0) { + buf[i] = (u8)(addr >> shift); + shift -= 8; + } +} + +static int fti2c010_read(struct i2c_adapter *adap, + u8 dev, uint addr, int alen, uchar *buf, int len) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + struct fti2c010_regs *regs = chip->regs; + int ret, pos; + uchar paddr[4] = { 0 }; + + to_i2c_addr(paddr, addr, alen); + + /* + * Phase A. Set register address + */ + + /* A.1 Select slave device (7bits Address + 1bit R/W) */ + writel(I2C_WR(dev), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + + /* A.2 Select device register */ + for (pos = 0; pos < alen; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + + writel(paddr[pos], ®s->dr); + writel(ctrl, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + } + + /* + * Phase B. Get register data + */ + + /* B.1 Select slave device (7bits Address + 1bit R/W) */ + writel(I2C_RD(dev), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + + /* B.2 Get register data */ + for (pos = 0; pos < len; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + uint32_t stat = SR_DR; + + if (pos == len - 1) { + ctrl |= CR_NAK | CR_STOP; + stat |= SR_ACK; + } + writel(ctrl, ®s->cr); + ret = fti2c010_wait(chip, stat); + if (ret) + break; + buf[pos] = (uchar)(readl(®s->dr) & 0xFF); + } + + return ret; +} + +static int fti2c010_write(struct i2c_adapter *adap, + u8 dev, uint addr, int alen, u8 *buf, int len) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + struct fti2c010_regs *regs = chip->regs; + int ret, pos; + uchar paddr[4] = { 0 }; + + to_i2c_addr(paddr, addr, alen); + + /* + * Phase A. Set register address + * + * A.1 Select slave device (7bits Address + 1bit R/W) + */ + writel(I2C_WR(dev), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + + /* A.2 Select device register */ + for (pos = 0; pos < alen; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + + writel(paddr[pos], ®s->dr); + writel(ctrl, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + } + + /* + * Phase B. Set register data + */ + for (pos = 0; pos < len; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + + if (pos == len - 1) + ctrl |= CR_STOP; + writel(buf[pos], ®s->dr); + writel(ctrl, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + break; + } + + return ret; +} + +static unsigned int fti2c010_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + int ret; + + fti2c010_reset(chip); + ret = set_i2c_bus_speed(chip, speed); + + return ret; +} + +/* + * Register i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(i2c_0, fti2c010_init, fti2c010_probe, fti2c010_read, + fti2c010_write, fti2c010_set_bus_speed, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + 0) +#ifdef CONFIG_FTI2C010_BASE1 +U_BOOT_I2C_ADAP_COMPLETE(i2c_1, fti2c010_init, fti2c010_probe, fti2c010_read, + fti2c010_write, fti2c010_set_bus_speed, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + 1) +#endif +#ifdef CONFIG_FTI2C010_BASE2 +U_BOOT_I2C_ADAP_COMPLETE(i2c_2, fti2c010_init, fti2c010_probe, fti2c010_read, + fti2c010_write, fti2c010_set_bus_speed, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + 2) +#endif +#ifdef CONFIG_FTI2C010_BASE3 +U_BOOT_I2C_ADAP_COMPLETE(i2c_3, fti2c010_init, fti2c010_probe, fti2c010_read, + fti2c010_write, fti2c010_set_bus_speed, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + 3) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/fti2c010.h b/qemu/roms/u-boot/drivers/i2c/fti2c010.h new file mode 100644 index 000000000..b9d0eb74a --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/fti2c010.h @@ -0,0 +1,80 @@ +/* + * Faraday I2C Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __FTI2C010_H +#define __FTI2C010_H + +/* + * FTI2C010 registers + */ +struct fti2c010_regs { + uint32_t cr; /* 0x00: control register */ + uint32_t sr; /* 0x04: status register */ + uint32_t cdr; /* 0x08: clock division register */ + uint32_t dr; /* 0x0c: data register */ + uint32_t sar; /* 0x10: slave address register */ + uint32_t tgsr;/* 0x14: time & glitch suppression register */ + uint32_t bmr; /* 0x18: bus monitor register */ + uint32_t rsvd[5]; + uint32_t revr;/* 0x30: revision register */ +}; + +/* + * control register + */ +#define CR_ALIRQ 0x2000 /* arbitration lost interrupt (master) */ +#define CR_SAMIRQ 0x1000 /* slave address match interrupt (slave) */ +#define CR_STOPIRQ 0x800 /* stop condition interrupt (slave) */ +#define CR_NAKRIRQ 0x400 /* NACK response interrupt (master) */ +#define CR_DRIRQ 0x200 /* rx interrupt (both) */ +#define CR_DTIRQ 0x100 /* tx interrupt (both) */ +#define CR_TBEN 0x80 /* tx enable (both) */ +#define CR_NAK 0x40 /* NACK (both) */ +#define CR_STOP 0x20 /* stop (master) */ +#define CR_START 0x10 /* start (master) */ +#define CR_GCEN 0x8 /* general call support (slave) */ +#define CR_SCLEN 0x4 /* enable clock out (master) */ +#define CR_I2CEN 0x2 /* enable I2C (both) */ +#define CR_I2CRST 0x1 /* reset I2C (both) */ +#define CR_ENABLE \ + (CR_ALIRQ | CR_NAKRIRQ | CR_DRIRQ | CR_DTIRQ | CR_SCLEN | CR_I2CEN) + +/* + * status register + */ +#define SR_CLRAL 0x400 /* clear arbitration lost */ +#define SR_CLRGC 0x200 /* clear general call */ +#define SR_CLRSAM 0x100 /* clear slave address match */ +#define SR_CLRSTOP 0x80 /* clear stop */ +#define SR_CLRNAKR 0x40 /* clear NACK respond */ +#define SR_DR 0x20 /* rx ready */ +#define SR_DT 0x10 /* tx done */ +#define SR_BB 0x8 /* bus busy */ +#define SR_BUSY 0x4 /* chip busy */ +#define SR_ACK 0x2 /* ACK/NACK received */ +#define SR_RW 0x1 /* set when master-rx or slave-tx mode */ + +/* + * clock division register + */ +#define CDR_DIV(n) ((n) & 0x3ffff) + +/* + * time & glitch suppression register + */ +#define TGSR_GSR(n) (((n) & 0x7) << 10) +#define TGSR_TSR(n) ((n) & 0x3ff) + +/* + * bus monitor register + */ +#define BMR_SCL 0x2 /* SCL is pull-up */ +#define BMR_SDA 0x1 /* SDA is pull-up */ + +#endif /* __FTI2C010_H */ diff --git a/qemu/roms/u-boot/drivers/i2c/i2c_core.c b/qemu/roms/u-boot/drivers/i2c/i2c_core.c new file mode 100644 index 000000000..18d673660 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/i2c_core.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2009 Sergey Kubushyn + * + * (C) Copyright 2012 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Multibus/multiadapter I2C core functions (wrappers) + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include + +struct i2c_adapter *i2c_get_adapter(int index) +{ + struct i2c_adapter *i2c_adap_p = ll_entry_start(struct i2c_adapter, + i2c); + int max = ll_entry_count(struct i2c_adapter, i2c); + int i; + + if (index >= max) { + printf("Error, wrong i2c adapter %d max %d possible\n", + index, max); + return i2c_adap_p; + } + if (index == 0) + return i2c_adap_p; + + for (i = 0; i < index; i++) + i2c_adap_p++; + + return i2c_adap_p; +} + +#if !defined(CONFIG_SYS_I2C_DIRECT_BUS) +struct i2c_bus_hose i2c_bus[CONFIG_SYS_NUM_I2C_BUSES] = + CONFIG_SYS_I2C_BUSES; +#endif + +DECLARE_GLOBAL_DATA_PTR; + +void i2c_reloc_fixup(void) +{ +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + struct i2c_adapter *i2c_adap_p = ll_entry_start(struct i2c_adapter, + i2c); + struct i2c_adapter *tmp = i2c_adap_p; + int max = ll_entry_count(struct i2c_adapter, i2c); + int i; + unsigned long addr; + + if (gd->reloc_off == 0) + return; + + for (i = 0; i < max; i++) { + /* i2c_init() */ + addr = (unsigned long)i2c_adap_p->init; + addr += gd->reloc_off; + i2c_adap_p->init = (void *)addr; + /* i2c_probe() */ + addr = (unsigned long)i2c_adap_p->probe; + addr += gd->reloc_off; + i2c_adap_p->probe = (void *)addr; + /* i2c_read() */ + addr = (unsigned long)i2c_adap_p->read; + addr += gd->reloc_off; + i2c_adap_p->read = (void *)addr; + /* i2c_write() */ + addr = (unsigned long)i2c_adap_p->write; + addr += gd->reloc_off; + i2c_adap_p->write = (void *)addr; + /* i2c_set_bus_speed() */ + addr = (unsigned long)i2c_adap_p->set_bus_speed; + addr += gd->reloc_off; + i2c_adap_p->set_bus_speed = (void *)addr; + /* name */ + addr = (unsigned long)i2c_adap_p->name; + addr += gd->reloc_off; + i2c_adap_p->name = (char *)addr; + tmp++; + i2c_adap_p = tmp; + } +#endif +} + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS +/* + * i2c_mux_set() + * ------------- + * + * This turns on the given channel on I2C multiplexer chip connected to + * a given I2C adapter directly or via other multiplexers. In the latter + * case the entire multiplexer chain must be initialized first starting + * with the one connected directly to the adapter. When disabling a chain + * muxes must be programmed in reverse order, starting with the one + * farthest from the adapter. + * + * mux_id is the multiplexer chip type from defined in i2c.h. So far only + * NXP (Philips) PCA954x multiplexers are supported. Switches are NOT + * supported (anybody uses them?) + */ + +static int i2c_mux_set(struct i2c_adapter *adap, int mux_id, int chip, + int channel) +{ + uint8_t buf; + int ret; + + /* channel < 0 - turn off the mux */ + if (channel < 0) { + buf = 0; + ret = adap->write(adap, chip, 0, 0, &buf, 1); + if (ret) + printf("%s: Could not turn off the mux.\n", __func__); + return ret; + } + + switch (mux_id) { + case I2C_MUX_PCA9540_ID: + case I2C_MUX_PCA9542_ID: + if (channel > 1) + return -1; + buf = (uint8_t)((channel & 0x01) | (1 << 2)); + break; + case I2C_MUX_PCA9544_ID: + if (channel > 3) + return -1; + buf = (uint8_t)((channel & 0x03) | (1 << 2)); + break; + case I2C_MUX_PCA9547_ID: + if (channel > 7) + return -1; + buf = (uint8_t)((channel & 0x07) | (1 << 3)); + break; + case I2C_MUX_PCA9548_ID: + if (channel > 7) + return -1; + buf = (uint8_t)(0x01 << channel); + break; + default: + printf("%s: wrong mux id: %d\n", __func__, mux_id); + return -1; + } + + ret = adap->write(adap, chip, 0, 0, &buf, 1); + if (ret) + printf("%s: could not set mux: id: %d chip: %x channel: %d\n", + __func__, mux_id, chip, channel); + return ret; +} + +static int i2c_mux_set_all(void) +{ + struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS]; + int i; + + /* Connect requested bus if behind muxes */ + if (i2c_bus_tmp->next_hop[0].chip != 0) { + /* Set all muxes along the path to that bus */ + for (i = 0; i < CONFIG_SYS_I2C_MAX_HOPS; i++) { + int ret; + + if (i2c_bus_tmp->next_hop[i].chip == 0) + break; + + ret = i2c_mux_set(I2C_ADAP, + i2c_bus_tmp->next_hop[i].mux.id, + i2c_bus_tmp->next_hop[i].chip, + i2c_bus_tmp->next_hop[i].channel); + if (ret != 0) + return ret; + } + } + return 0; +} + +static int i2c_mux_disconnet_all(void) +{ + struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS]; + int i; + uint8_t buf; + + if (I2C_ADAP->init_done == 0) + return 0; + + /* Disconnect current bus (turn off muxes if any) */ + if ((i2c_bus_tmp->next_hop[0].chip != 0) && + (I2C_ADAP->init_done != 0)) { + i = CONFIG_SYS_I2C_MAX_HOPS; + do { + uint8_t chip; + int ret; + + chip = i2c_bus_tmp->next_hop[--i].chip; + if (chip == 0) + continue; + + ret = I2C_ADAP->write(I2C_ADAP, chip, 0, 0, &buf, 1); + if (ret != 0) { + printf("i2c: mux diconnect error\n"); + return ret; + } + } while (i > 0); + } + + return 0; +} +#endif + +/* + * i2c_init_bus(): + * --------------- + * + * Initializes one bus. Will initialize the parent adapter. No current bus + * changes, no mux (if any) setup. + */ +static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr) +{ + if (bus_no >= CONFIG_SYS_NUM_I2C_BUSES) + return; + + I2C_ADAP->init(I2C_ADAP, speed, slaveaddr); + + if (gd->flags & GD_FLG_RELOC) { + I2C_ADAP->init_done = 1; + I2C_ADAP->speed = speed; + I2C_ADAP->slaveaddr = slaveaddr; + } +} + +/* implement possible board specific board init */ +static void __def_i2c_init_board(void) +{ +} +void i2c_init_board(void) + __attribute__((weak, alias("__def_i2c_init_board"))); + +/* + * i2c_init_all(): + * + * not longer needed, will deleted. Actual init the SPD_BUS + * for compatibility. + * i2c_adap[] must be initialized beforehead with function pointers and + * data, including speed and slaveaddr. + */ +void i2c_init_all(void) +{ + i2c_init_board(); + i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM); + return; +} + +/* + * i2c_get_bus_num(): + * ------------------ + * + * Returns index of currently active I2C bus. Zero-based. + */ +unsigned int i2c_get_bus_num(void) +{ + return gd->cur_i2c_bus; +} + +/* + * i2c_set_bus_num(): + * ------------------ + * + * Change the active I2C bus. Subsequent read/write calls will + * go to this one. Sets all of the muxes in a proper condition + * if that bus is behind muxes. + * If previously selected bus is behind the muxes turns off all the + * muxes along the path to that bus. + * + * bus - bus index, zero based + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_set_bus_num(unsigned int bus) +{ + int max; + + if ((bus == I2C_BUS) && (I2C_ADAP->init_done > 0)) + return 0; + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + if (bus >= CONFIG_SYS_NUM_I2C_BUSES) + return -1; +#endif + + max = ll_entry_count(struct i2c_adapter, i2c); + if (I2C_ADAPTER(bus) >= max) { + printf("Error, wrong i2c adapter %d max %d possible\n", + I2C_ADAPTER(bus), max); + return -2; + } + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + i2c_mux_disconnet_all(); +#endif + + gd->cur_i2c_bus = bus; + if (I2C_ADAP->init_done == 0) + i2c_init_bus(bus, I2C_ADAP->speed, I2C_ADAP->slaveaddr); + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + i2c_mux_set_all(); +#endif + return 0; +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(uint8_t chip) +{ + return I2C_ADAP->probe(I2C_ADAP, chip); +} + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(uint8_t chip, unsigned int addr, int alen, + uint8_t *buffer, int len) +{ + return I2C_ADAP->read(I2C_ADAP, chip, addr, alen, buffer, len); +} + +int i2c_write(uint8_t chip, unsigned int addr, int alen, + uint8_t *buffer, int len) +{ + return I2C_ADAP->write(I2C_ADAP, chip, addr, alen, buffer, len); +} + +unsigned int i2c_set_bus_speed(unsigned int speed) +{ + unsigned int ret; + + if (I2C_ADAP->set_bus_speed == NULL) + return 0; + ret = I2C_ADAP->set_bus_speed(I2C_ADAP, speed); + if (gd->flags & GD_FLG_RELOC) + I2C_ADAP->speed = (ret == 0) ? speed : 0; + + return ret; +} + +unsigned int i2c_get_bus_speed(void) +{ + struct i2c_adapter *cur = I2C_ADAP; + return cur->speed; +} + +uint8_t i2c_reg_read(uint8_t addr, uint8_t reg) +{ + uint8_t buf; + +#ifdef CONFIG_8xx + /* MPC8xx needs this. Maybe one day we can get rid of it. */ + /* maybe it is now the time for it ... */ + i2c_set_bus_num(i2c_get_bus_num()); +#endif + i2c_read(addr, reg, 1, &buf, 1); + +#ifdef DEBUG + printf("%s: bus=%d addr=0x%02x, reg=0x%02x, val=0x%02x\n", + __func__, i2c_get_bus_num(), addr, reg, buf); +#endif + + return buf; +} + +void i2c_reg_write(uint8_t addr, uint8_t reg, uint8_t val) +{ +#ifdef CONFIG_8xx + /* MPC8xx needs this. Maybe one day we can get rid of it. */ + /* maybe it is now the time for it ... */ + i2c_set_bus_num(i2c_get_bus_num()); +#endif + +#ifdef DEBUG + printf("%s: bus=%d addr=0x%02x, reg=0x%02x, val=0x%02x\n", + __func__, i2c_get_bus_num(), addr, reg, val); +#endif + + i2c_write(addr, reg, 1, &val, 1); +} + +void __i2c_init(int speed, int slaveaddr) +{ + i2c_init_bus(i2c_get_bus_num(), speed, slaveaddr); +} +void i2c_init(int speed, int slaveaddr) + __attribute__((weak, alias("__i2c_init"))); diff --git a/qemu/roms/u-boot/drivers/i2c/kona_i2c.c b/qemu/roms/u-boot/drivers/i2c/kona_i2c.c new file mode 100644 index 000000000..0b1715abf --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/kona_i2c.c @@ -0,0 +1,730 @@ +/* + * Copyright 2013 Broadcom Corporation. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +/* Hardware register offsets and field defintions */ +#define CS_OFFSET 0x00000020 +#define CS_ACK_SHIFT 3 +#define CS_ACK_MASK 0x00000008 +#define CS_ACK_CMD_GEN_START 0x00000000 +#define CS_ACK_CMD_GEN_RESTART 0x00000001 +#define CS_CMD_SHIFT 1 +#define CS_CMD_CMD_NO_ACTION 0x00000000 +#define CS_CMD_CMD_START_RESTART 0x00000001 +#define CS_CMD_CMD_STOP 0x00000002 +#define CS_EN_SHIFT 0 +#define CS_EN_CMD_ENABLE_BSC 0x00000001 + +#define TIM_OFFSET 0x00000024 +#define TIM_PRESCALE_SHIFT 6 +#define TIM_P_SHIFT 3 +#define TIM_NO_DIV_SHIFT 2 +#define TIM_DIV_SHIFT 0 + +#define DAT_OFFSET 0x00000028 + +#define TOUT_OFFSET 0x0000002c + +#define TXFCR_OFFSET 0x0000003c +#define TXFCR_FIFO_FLUSH_MASK 0x00000080 +#define TXFCR_FIFO_EN_MASK 0x00000040 + +#define IER_OFFSET 0x00000044 +#define IER_READ_COMPLETE_INT_MASK 0x00000010 +#define IER_I2C_INT_EN_MASK 0x00000008 +#define IER_FIFO_INT_EN_MASK 0x00000002 +#define IER_NOACK_EN_MASK 0x00000001 + +#define ISR_OFFSET 0x00000048 +#define ISR_RESERVED_MASK 0xffffff60 +#define ISR_CMDBUSY_MASK 0x00000080 +#define ISR_READ_COMPLETE_MASK 0x00000010 +#define ISR_SES_DONE_MASK 0x00000008 +#define ISR_ERR_MASK 0x00000004 +#define ISR_TXFIFOEMPTY_MASK 0x00000002 +#define ISR_NOACK_MASK 0x00000001 + +#define CLKEN_OFFSET 0x0000004c +#define CLKEN_AUTOSENSE_OFF_MASK 0x00000080 +#define CLKEN_M_SHIFT 4 +#define CLKEN_N_SHIFT 1 +#define CLKEN_CLKEN_MASK 0x00000001 + +#define FIFO_STATUS_OFFSET 0x00000054 +#define FIFO_STATUS_RXFIFO_EMPTY_MASK 0x00000004 +#define FIFO_STATUS_TXFIFO_EMPTY_MASK 0x00000010 + +#define HSTIM_OFFSET 0x00000058 +#define HSTIM_HS_MODE_MASK 0x00008000 +#define HSTIM_HS_HOLD_SHIFT 10 +#define HSTIM_HS_HIGH_PHASE_SHIFT 5 +#define HSTIM_HS_SETUP_SHIFT 0 + +#define PADCTL_OFFSET 0x0000005c +#define PADCTL_PAD_OUT_EN_MASK 0x00000004 + +#define RXFCR_OFFSET 0x00000068 +#define RXFCR_NACK_EN_SHIFT 7 +#define RXFCR_READ_COUNT_SHIFT 0 +#define RXFIFORDOUT_OFFSET 0x0000006c + +/* Locally used constants */ +#define MAX_RX_FIFO_SIZE 64U /* bytes */ +#define MAX_TX_FIFO_SIZE 64U /* bytes */ + +#define I2C_TIMEOUT 100000 /* usecs */ + +#define WAIT_INT_CHK 100 /* usecs */ +#if I2C_TIMEOUT % WAIT_INT_CHK +#error I2C_TIMEOUT must be a multiple of WAIT_INT_CHK +#endif + +/* Operations that can be commanded to the controller */ +enum bcm_kona_cmd_t { + BCM_CMD_NOACTION = 0, + BCM_CMD_START, + BCM_CMD_RESTART, + BCM_CMD_STOP, +}; + +enum bus_speed_index { + BCM_SPD_100K = 0, + BCM_SPD_400K, + BCM_SPD_1MHZ, +}; + +/* Internal divider settings for standard mode, fast mode and fast mode plus */ +struct bus_speed_cfg { + uint8_t time_m; /* Number of cycles for setup time */ + uint8_t time_n; /* Number of cycles for hold time */ + uint8_t prescale; /* Prescale divider */ + uint8_t time_p; /* Timing coefficient */ + uint8_t no_div; /* Disable clock divider */ + uint8_t time_div; /* Post-prescale divider */ +}; + +static const struct bus_speed_cfg std_cfg_table[] = { + [BCM_SPD_100K] = {0x01, 0x01, 0x03, 0x06, 0x00, 0x02}, + [BCM_SPD_400K] = {0x05, 0x01, 0x03, 0x05, 0x01, 0x02}, + [BCM_SPD_1MHZ] = {0x01, 0x01, 0x03, 0x01, 0x01, 0x03}, +}; + +struct bcm_kona_i2c_dev { + void *base; + uint speed; + const struct bus_speed_cfg *std_cfg; +}; + +/* Keep these two defines in sync */ +#define DEF_SPD 100000 +#define DEF_SPD_ENUM BCM_SPD_100K + +#define DEF_DEVICE(num) \ +{(void *)CONFIG_SYS_I2C_BASE##num, DEF_SPD, &std_cfg_table[DEF_SPD_ENUM]} + +static struct bcm_kona_i2c_dev g_i2c_devs[CONFIG_SYS_MAX_I2C_BUS] = { +#ifdef CONFIG_SYS_I2C_BASE0 + DEF_DEVICE(0), +#endif +#ifdef CONFIG_SYS_I2C_BASE1 + DEF_DEVICE(1), +#endif +#ifdef CONFIG_SYS_I2C_BASE2 + DEF_DEVICE(2), +#endif +#ifdef CONFIG_SYS_I2C_BASE3 + DEF_DEVICE(3), +#endif +#ifdef CONFIG_SYS_I2C_BASE4 + DEF_DEVICE(4), +#endif +#ifdef CONFIG_SYS_I2C_BASE5 + DEF_DEVICE(5), +#endif +}; + +#define I2C_M_TEN 0x0010 /* ten bit address */ +#define I2C_M_RD 0x0001 /* read data */ +#define I2C_M_NOSTART 0x4000 /* no restart between msgs */ + +struct i2c_msg { + uint16_t addr; + uint16_t flags; + uint16_t len; + uint8_t *buf; +}; + +static void bcm_kona_i2c_send_cmd_to_ctrl(struct bcm_kona_i2c_dev *dev, + enum bcm_kona_cmd_t cmd) +{ + debug("%s, %d\n", __func__, cmd); + + switch (cmd) { + case BCM_CMD_NOACTION: + writel((CS_CMD_CMD_NO_ACTION << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + case BCM_CMD_START: + writel((CS_ACK_CMD_GEN_START << CS_ACK_SHIFT) | + (CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + case BCM_CMD_RESTART: + writel((CS_ACK_CMD_GEN_RESTART << CS_ACK_SHIFT) | + (CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + case BCM_CMD_STOP: + writel((CS_CMD_CMD_STOP << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + default: + printf("Unknown command %d\n", cmd); + } +} + +static void bcm_kona_i2c_enable_clock(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + CLKEN_OFFSET) | CLKEN_CLKEN_MASK, + dev->base + CLKEN_OFFSET); +} + +static void bcm_kona_i2c_disable_clock(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_CLKEN_MASK, + dev->base + CLKEN_OFFSET); +} + +/* Wait until at least one of the mask bit(s) are set */ +static unsigned long wait_for_int_timeout(struct bcm_kona_i2c_dev *dev, + unsigned long time_left, + uint32_t mask) +{ + uint32_t status; + + while (time_left) { + status = readl(dev->base + ISR_OFFSET); + + if ((status & ~ISR_RESERVED_MASK) == 0) { + debug("Bogus I2C interrupt 0x%x\n", status); + continue; + } + + /* Must flush the TX FIFO when NAK detected */ + if (status & ISR_NOACK_MASK) + writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK, + dev->base + TXFCR_OFFSET); + + writel(status & ~ISR_RESERVED_MASK, dev->base + ISR_OFFSET); + + if (status & mask) { + /* We are done since one of the mask bits are set */ + return time_left; + } + udelay(WAIT_INT_CHK); + time_left -= WAIT_INT_CHK; + } + return 0; +} + +/* Send command to I2C bus */ +static int bcm_kona_send_i2c_cmd(struct bcm_kona_i2c_dev *dev, + enum bcm_kona_cmd_t cmd) +{ + int rc = 0; + unsigned long time_left = I2C_TIMEOUT; + + /* Send the command */ + bcm_kona_i2c_send_cmd_to_ctrl(dev, cmd); + + /* Wait for transaction to finish or timeout */ + time_left = wait_for_int_timeout(dev, time_left, IER_I2C_INT_EN_MASK); + + if (!time_left) { + printf("controller timed out\n"); + rc = -ETIMEDOUT; + } + + /* Clear command */ + bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION); + + return rc; +} + +/* Read a single RX FIFO worth of data from the i2c bus */ +static int bcm_kona_i2c_read_fifo_single(struct bcm_kona_i2c_dev *dev, + uint8_t *buf, unsigned int len, + unsigned int last_byte_nak) +{ + unsigned long time_left = I2C_TIMEOUT; + + /* Start the RX FIFO */ + writel((last_byte_nak << RXFCR_NACK_EN_SHIFT) | + (len << RXFCR_READ_COUNT_SHIFT), dev->base + RXFCR_OFFSET); + + /* Wait for FIFO read to complete */ + time_left = + wait_for_int_timeout(dev, time_left, IER_READ_COMPLETE_INT_MASK); + + if (!time_left) { + printf("RX FIFO time out\n"); + return -EREMOTEIO; + } + + /* Read data from FIFO */ + for (; len > 0; len--, buf++) + *buf = readl(dev->base + RXFIFORDOUT_OFFSET); + + return 0; +} + +/* Read any amount of data using the RX FIFO from the i2c bus */ +static int bcm_kona_i2c_read_fifo(struct bcm_kona_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned int bytes_to_read = MAX_RX_FIFO_SIZE; + unsigned int last_byte_nak = 0; + unsigned int bytes_read = 0; + int rc; + + uint8_t *tmp_buf = msg->buf; + + while (bytes_read < msg->len) { + if (msg->len - bytes_read <= MAX_RX_FIFO_SIZE) { + last_byte_nak = 1; /* NAK last byte of transfer */ + bytes_to_read = msg->len - bytes_read; + } + + rc = bcm_kona_i2c_read_fifo_single(dev, tmp_buf, bytes_to_read, + last_byte_nak); + if (rc < 0) + return -EREMOTEIO; + + bytes_read += bytes_to_read; + tmp_buf += bytes_to_read; + } + + return 0; +} + +/* Write a single byte of data to the i2c bus */ +static int bcm_kona_i2c_write_byte(struct bcm_kona_i2c_dev *dev, uint8_t data, + unsigned int nak_expected) +{ + unsigned long time_left = I2C_TIMEOUT; + unsigned int nak_received; + + /* Clear pending session done interrupt */ + writel(ISR_SES_DONE_MASK, dev->base + ISR_OFFSET); + + /* Send one byte of data */ + writel(data, dev->base + DAT_OFFSET); + + time_left = wait_for_int_timeout(dev, time_left, IER_I2C_INT_EN_MASK); + + if (!time_left) { + debug("controller timed out\n"); + return -ETIMEDOUT; + } + + nak_received = readl(dev->base + CS_OFFSET) & CS_ACK_MASK ? 1 : 0; + + if (nak_received ^ nak_expected) { + debug("unexpected NAK/ACK\n"); + return -EREMOTEIO; + } + + return 0; +} + +/* Write a single TX FIFO worth of data to the i2c bus */ +static int bcm_kona_i2c_write_fifo_single(struct bcm_kona_i2c_dev *dev, + uint8_t *buf, unsigned int len) +{ + int k; + unsigned long time_left = I2C_TIMEOUT; + unsigned int fifo_status; + + /* Write data into FIFO */ + for (k = 0; k < len; k++) + writel(buf[k], (dev->base + DAT_OFFSET)); + + /* Wait for FIFO to empty */ + do { + time_left = + wait_for_int_timeout(dev, time_left, + (IER_FIFO_INT_EN_MASK | + IER_NOACK_EN_MASK)); + fifo_status = readl(dev->base + FIFO_STATUS_OFFSET); + } while (time_left && !(fifo_status & FIFO_STATUS_TXFIFO_EMPTY_MASK)); + + /* Check if there was a NAK */ + if (readl(dev->base + CS_OFFSET) & CS_ACK_MASK) { + printf("unexpected NAK\n"); + return -EREMOTEIO; + } + + /* Check if a timeout occured */ + if (!time_left) { + printf("completion timed out\n"); + return -EREMOTEIO; + } + + return 0; +} + +/* Write any amount of data using TX FIFO to the i2c bus */ +static int bcm_kona_i2c_write_fifo(struct bcm_kona_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned int bytes_to_write = MAX_TX_FIFO_SIZE; + unsigned int bytes_written = 0; + int rc; + + uint8_t *tmp_buf = msg->buf; + + while (bytes_written < msg->len) { + if (msg->len - bytes_written <= MAX_TX_FIFO_SIZE) + bytes_to_write = msg->len - bytes_written; + + rc = bcm_kona_i2c_write_fifo_single(dev, tmp_buf, + bytes_to_write); + if (rc < 0) + return -EREMOTEIO; + + bytes_written += bytes_to_write; + tmp_buf += bytes_to_write; + } + + return 0; +} + +/* Send i2c address */ +static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned char addr; + + if (msg->flags & I2C_M_TEN) { + /* First byte is 11110XX0 where XX is upper 2 bits */ + addr = 0xf0 | ((msg->addr & 0x300) >> 7); + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + + /* Second byte is the remaining 8 bits */ + addr = msg->addr & 0xff; + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + + if (msg->flags & I2C_M_RD) { + /* For read, send restart command */ + if (bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART) < 0) + return -EREMOTEIO; + + /* Then re-send the first byte with the read bit set */ + addr = 0xf0 | ((msg->addr & 0x300) >> 7) | 0x01; + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + } + } else { + addr = msg->addr << 1; + + if (msg->flags & I2C_M_RD) + addr |= 1; + + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + } + + return 0; +} + +static void bcm_kona_i2c_enable_autosense(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_AUTOSENSE_OFF_MASK, + dev->base + CLKEN_OFFSET); +} + +static void bcm_kona_i2c_config_timing(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + HSTIM_OFFSET) & ~HSTIM_HS_MODE_MASK, + dev->base + HSTIM_OFFSET); + + writel((dev->std_cfg->prescale << TIM_PRESCALE_SHIFT) | + (dev->std_cfg->time_p << TIM_P_SHIFT) | + (dev->std_cfg->no_div << TIM_NO_DIV_SHIFT) | + (dev->std_cfg->time_div << TIM_DIV_SHIFT), + dev->base + TIM_OFFSET); + + writel((dev->std_cfg->time_m << CLKEN_M_SHIFT) | + (dev->std_cfg->time_n << CLKEN_N_SHIFT) | + CLKEN_CLKEN_MASK, dev->base + CLKEN_OFFSET); +} + +/* Master transfer function */ +static int bcm_kona_i2c_xfer(struct bcm_kona_i2c_dev *dev, + struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + int rc = 0; + int i; + + /* Enable pad output */ + writel(0, dev->base + PADCTL_OFFSET); + + /* Enable internal clocks */ + bcm_kona_i2c_enable_clock(dev); + + /* Send start command */ + rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_START); + if (rc < 0) { + printf("Start command failed rc = %d\n", rc); + goto xfer_disable_pad; + } + + /* Loop through all messages */ + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + + /* Send restart for subsequent messages */ + if ((i != 0) && ((pmsg->flags & I2C_M_NOSTART) == 0)) { + rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART); + if (rc < 0) { + printf("restart cmd failed rc = %d\n", rc); + goto xfer_send_stop; + } + } + + /* Send slave address */ + if (!(pmsg->flags & I2C_M_NOSTART)) { + rc = bcm_kona_i2c_do_addr(dev, pmsg); + if (rc < 0) { + debug("NAK from addr %2.2x msg#%d rc = %d\n", + pmsg->addr, i, rc); + goto xfer_send_stop; + } + } + + /* Perform data transfer */ + if (pmsg->flags & I2C_M_RD) { + rc = bcm_kona_i2c_read_fifo(dev, pmsg); + if (rc < 0) { + printf("read failure\n"); + goto xfer_send_stop; + } + } else { + rc = bcm_kona_i2c_write_fifo(dev, pmsg); + if (rc < 0) { + printf("write failure"); + goto xfer_send_stop; + } + } + } + + rc = num; + +xfer_send_stop: + /* Send a STOP command */ + bcm_kona_send_i2c_cmd(dev, BCM_CMD_STOP); + +xfer_disable_pad: + /* Disable pad output */ + writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET); + + /* Stop internal clock */ + bcm_kona_i2c_disable_clock(dev); + + return rc; +} + +static uint bcm_kona_i2c_assign_bus_speed(struct bcm_kona_i2c_dev *dev, + uint speed) +{ + switch (speed) { + case 100000: + dev->std_cfg = &std_cfg_table[BCM_SPD_100K]; + break; + case 400000: + dev->std_cfg = &std_cfg_table[BCM_SPD_400K]; + break; + case 1000000: + dev->std_cfg = &std_cfg_table[BCM_SPD_1MHZ]; + break; + default: + printf("%d hz bus speed not supported\n", speed); + return -EINVAL; + } + dev->speed = speed; + return 0; +} + +static void bcm_kona_i2c_init(struct bcm_kona_i2c_dev *dev) +{ + /* Parse bus speed */ + bcm_kona_i2c_assign_bus_speed(dev, dev->speed); + + /* Enable internal clocks */ + bcm_kona_i2c_enable_clock(dev); + + /* Configure internal dividers */ + bcm_kona_i2c_config_timing(dev); + + /* Disable timeout */ + writel(0, dev->base + TOUT_OFFSET); + + /* Enable autosense */ + bcm_kona_i2c_enable_autosense(dev); + + /* Enable TX FIFO */ + writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK, + dev->base + TXFCR_OFFSET); + + /* Mask all interrupts */ + writel(0, dev->base + IER_OFFSET); + + /* Clear all pending interrupts */ + writel(ISR_CMDBUSY_MASK | + ISR_READ_COMPLETE_MASK | + ISR_SES_DONE_MASK | + ISR_ERR_MASK | + ISR_TXFIFOEMPTY_MASK | ISR_NOACK_MASK, dev->base + ISR_OFFSET); + + /* Enable the controller but leave it idle */ + bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION); + + /* Disable pad output */ + writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET); +} + +/* + * uboot layer + */ +struct bcm_kona_i2c_dev *kona_get_dev(struct i2c_adapter *adap) +{ + return &g_i2c_devs[adap->hwadapnr]; +} + +static void kona_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + struct bcm_kona_i2c_dev *dev = kona_get_dev(adap); + + if (clk_bsc_enable(dev->base)) + return; + + bcm_kona_i2c_init(dev); +} + +static int kona_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + /* msg[0] writes the addr, msg[1] reads the data */ + struct i2c_msg msg[2]; + unsigned char msgbuf0[64]; + struct bcm_kona_i2c_dev *dev = kona_get_dev(adap); + + msg[0].addr = chip; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = msgbuf0; /* msgbuf0 contains incrementing reg addr */ + + msg[1].addr = chip; + msg[1].flags = I2C_M_RD; + /* msg[1].buf dest ptr increments each read */ + + msgbuf0[0] = (unsigned char)addr; + msg[1].buf = buffer; + msg[1].len = len; + if (bcm_kona_i2c_xfer(dev, msg, 2) < 0) { + /* Sending 2 i2c messages */ + kona_i2c_init(adap, adap->speed, adap->slaveaddr); + debug("I2C read: I/O error\n"); + return -EIO; + } + return 0; +} + +static int kona_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c_msg msg[0]; + unsigned char msgbuf0[64]; + unsigned int i; + struct bcm_kona_i2c_dev *dev = kona_get_dev(adap); + + msg[0].addr = chip; + msg[0].flags = 0; + msg[0].len = 2; /* addr byte plus data */ + msg[0].buf = msgbuf0; + + for (i = 0; i < len; i++) { + msgbuf0[0] = addr++; + msgbuf0[1] = buffer[i]; + if (bcm_kona_i2c_xfer(dev, msg, 1) < 0) { + kona_i2c_init(adap, adap->speed, adap->slaveaddr); + debug("I2C write: I/O error\n"); + return -EIO; + } + } + return 0; +} + +static int kona_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + uchar tmp; + + /* + * read addr 0x0 of the given chip. + */ + return kona_i2c_read(adap, chip, 0x0, 1, &tmp, 1); +} + +static uint kona_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed) +{ + struct bcm_kona_i2c_dev *dev = kona_get_dev(adap); + return bcm_kona_i2c_assign_bus_speed(dev, speed); +} + +/* + * Register kona i2c adapters. Keep the order below so + * that the bus number matches the adapter number. + */ +#define DEF_ADAPTER(num) \ +U_BOOT_I2C_ADAP_COMPLETE(kona##num, kona_i2c_init, kona_i2c_probe, \ + kona_i2c_read, kona_i2c_write, \ + kona_i2c_set_bus_speed, DEF_SPD, 0x00, num) + +#ifdef CONFIG_SYS_I2C_BASE0 + DEF_ADAPTER(0) +#endif +#ifdef CONFIG_SYS_I2C_BASE1 + DEF_ADAPTER(1) +#endif +#ifdef CONFIG_SYS_I2C_BASE2 + DEF_ADAPTER(2) +#endif +#ifdef CONFIG_SYS_I2C_BASE3 + DEF_ADAPTER(3) +#endif +#ifdef CONFIG_SYS_I2C_BASE4 + DEF_ADAPTER(4) +#endif +#ifdef CONFIG_SYS_I2C_BASE5 + DEF_ADAPTER(5) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/mv_i2c.c b/qemu/roms/u-boot/drivers/i2c/mv_i2c.c new file mode 100644 index 000000000..dac346334 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mv_i2c.c @@ -0,0 +1,471 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * (C) Copyright 2003 Pengutronix e.K. + * Robert Schwebel + * + * (C) Copyright 2011 Marvell Inc. + * Lei Wen + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Back ported to the 8xx platform (from the 8260 platform) by + * Murray.Jensen@cmst.csiro.au, 27-Jan-01. + */ + +#include +#include + +#ifdef CONFIG_HARD_I2C +#include +#include "mv_i2c.h" + +#ifdef DEBUG_I2C +#define PRINTD(x) printf x +#else +#define PRINTD(x) +#endif + +/* All transfers are described by this data structure */ +struct i2c_msg { + u8 condition; + u8 acknack; + u8 direction; + u8 data; +}; + +struct mv_i2c { + u32 ibmr; + u32 pad0; + u32 idbr; + u32 pad1; + u32 icr; + u32 pad2; + u32 isr; + u32 pad3; + u32 isar; +}; + +static struct mv_i2c *base; +static void i2c_board_init(struct mv_i2c *base) +{ +#ifdef CONFIG_SYS_I2C_INIT_BOARD + u32 icr; + /* + * call board specific i2c bus reset routine before accessing the + * environment, which might be in a chip on that bus. For details + * about this problem see doc/I2C_Edge_Conditions. + * + * disable I2C controller first, otherwhise it thinks we want to + * talk to the slave port... + */ + icr = readl(&base->icr); + writel(readl(&base->icr) & ~(ICR_SCLE | ICR_IUE), &base->icr); + + i2c_init_board(); + + writel(icr, &base->icr); +#endif +} + +#ifdef CONFIG_I2C_MULTI_BUS +static u32 i2c_regs[CONFIG_MV_I2C_NUM] = CONFIG_MV_I2C_REG; +static unsigned int bus_initialized[CONFIG_MV_I2C_NUM]; +static unsigned int current_bus; + +int i2c_set_bus_num(unsigned int bus) +{ + if ((bus < 0) || (bus >= CONFIG_MV_I2C_NUM)) { + printf("Bad bus: %d\n", bus); + return -1; + } + + base = (struct mv_i2c *)i2c_regs[bus]; + current_bus = bus; + + if (!bus_initialized[current_bus]) { + i2c_board_init(base); + bus_initialized[current_bus] = 1; + } + + return 0; +} + +unsigned int i2c_get_bus_num(void) +{ + return current_bus; +} +#endif + +/* + * i2c_reset: - reset the host controller + * + */ +static void i2c_reset(void) +{ + writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */ + writel(readl(&base->icr) | ICR_UR, &base->icr); /* reset the unit */ + udelay(100); + writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */ + + i2c_clk_enable(); + + writel(CONFIG_SYS_I2C_SLAVE, &base->isar); /* set our slave address */ + writel(I2C_ICR_INIT, &base->icr); /* set control reg values */ + writel(I2C_ISR_INIT, &base->isr); /* set clear interrupt bits */ + writel(readl(&base->icr) | ICR_IUE, &base->icr); /* enable unit */ + udelay(100); +} + +/* + * i2c_isr_set_cleared: - wait until certain bits of the I2C status register + * are set and cleared + * + * @return: 1 in case of success, 0 means timeout (no match within 10 ms). + */ +static int i2c_isr_set_cleared(unsigned long set_mask, + unsigned long cleared_mask) +{ + int timeout = 1000, isr; + + do { + isr = readl(&base->isr); + udelay(10); + if (timeout-- < 0) + return 0; + } while (((isr & set_mask) != set_mask) + || ((isr & cleared_mask) != 0)); + + return 1; +} + +/* + * i2c_transfer: - Transfer one byte over the i2c bus + * + * This function can tranfer a byte over the i2c bus in both directions. + * It is used by the public API functions. + * + * @return: 0: transfer successful + * -1: message is empty + * -2: transmit timeout + * -3: ACK missing + * -4: receive timeout + * -5: illegal parameters + * -6: bus is busy and couldn't be aquired + */ +int i2c_transfer(struct i2c_msg *msg) +{ + int ret; + + if (!msg) + goto transfer_error_msg_empty; + + switch (msg->direction) { + case I2C_WRITE: + /* check if bus is not busy */ + if (!i2c_isr_set_cleared(0, ISR_IBB)) + goto transfer_error_bus_busy; + + /* start transmission */ + writel(readl(&base->icr) & ~ICR_START, &base->icr); + writel(readl(&base->icr) & ~ICR_STOP, &base->icr); + writel(msg->data, &base->idbr); + if (msg->condition == I2C_COND_START) + writel(readl(&base->icr) | ICR_START, &base->icr); + if (msg->condition == I2C_COND_STOP) + writel(readl(&base->icr) | ICR_STOP, &base->icr); + if (msg->acknack == I2C_ACKNAK_SENDNAK) + writel(readl(&base->icr) | ICR_ACKNAK, &base->icr); + if (msg->acknack == I2C_ACKNAK_SENDACK) + writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr); + writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr); + writel(readl(&base->icr) | ICR_TB, &base->icr); + + /* transmit register empty? */ + if (!i2c_isr_set_cleared(ISR_ITE, 0)) + goto transfer_error_transmit_timeout; + + /* clear 'transmit empty' state */ + writel(readl(&base->isr) | ISR_ITE, &base->isr); + + /* wait for ACK from slave */ + if (msg->acknack == I2C_ACKNAK_WAITACK) + if (!i2c_isr_set_cleared(0, ISR_ACKNAK)) + goto transfer_error_ack_missing; + break; + + case I2C_READ: + + /* check if bus is not busy */ + if (!i2c_isr_set_cleared(0, ISR_IBB)) + goto transfer_error_bus_busy; + + /* start receive */ + writel(readl(&base->icr) & ~ICR_START, &base->icr); + writel(readl(&base->icr) & ~ICR_STOP, &base->icr); + if (msg->condition == I2C_COND_START) + writel(readl(&base->icr) | ICR_START, &base->icr); + if (msg->condition == I2C_COND_STOP) + writel(readl(&base->icr) | ICR_STOP, &base->icr); + if (msg->acknack == I2C_ACKNAK_SENDNAK) + writel(readl(&base->icr) | ICR_ACKNAK, &base->icr); + if (msg->acknack == I2C_ACKNAK_SENDACK) + writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr); + writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr); + writel(readl(&base->icr) | ICR_TB, &base->icr); + + /* receive register full? */ + if (!i2c_isr_set_cleared(ISR_IRF, 0)) + goto transfer_error_receive_timeout; + + msg->data = readl(&base->idbr); + + /* clear 'receive empty' state */ + writel(readl(&base->isr) | ISR_IRF, &base->isr); + break; + default: + goto transfer_error_illegal_param; + } + + return 0; + +transfer_error_msg_empty: + PRINTD(("i2c_transfer: error: 'msg' is empty\n")); + ret = -1; goto i2c_transfer_finish; + +transfer_error_transmit_timeout: + PRINTD(("i2c_transfer: error: transmit timeout\n")); + ret = -2; goto i2c_transfer_finish; + +transfer_error_ack_missing: + PRINTD(("i2c_transfer: error: ACK missing\n")); + ret = -3; goto i2c_transfer_finish; + +transfer_error_receive_timeout: + PRINTD(("i2c_transfer: error: receive timeout\n")); + ret = -4; goto i2c_transfer_finish; + +transfer_error_illegal_param: + PRINTD(("i2c_transfer: error: illegal parameters\n")); + ret = -5; goto i2c_transfer_finish; + +transfer_error_bus_busy: + PRINTD(("i2c_transfer: error: bus is busy\n")); + ret = -6; goto i2c_transfer_finish; + +i2c_transfer_finish: + PRINTD(("i2c_transfer: ISR: 0x%04x\n", readl(&base->isr))); + i2c_reset(); + return ret; +} + +/* ------------------------------------------------------------------------ */ +/* API Functions */ +/* ------------------------------------------------------------------------ */ +void i2c_init(int speed, int slaveaddr) +{ +#ifdef CONFIG_I2C_MULTI_BUS + current_bus = 0; + base = (struct mv_i2c *)i2c_regs[current_bus]; +#else + base = (struct mv_i2c *)CONFIG_MV_I2C_REG; +#endif + + i2c_board_init(base); +} + +/* + * i2c_probe: - Test if a chip answers for a given i2c address + * + * @chip: address of the chip which is searched for + * @return: 0 if a chip was found, -1 otherwhise + */ +int i2c_probe(uchar chip) +{ + struct i2c_msg msg; + + i2c_reset(); + + msg.condition = I2C_COND_START; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = (chip << 1) + 1; + if (i2c_transfer(&msg)) + return -1; + + msg.condition = I2C_COND_STOP; + msg.acknack = I2C_ACKNAK_SENDNAK; + msg.direction = I2C_READ; + msg.data = 0x00; + if (i2c_transfer(&msg)) + return -1; + + return 0; +} + +/* + * i2c_read: - Read multiple bytes from an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be read + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to write the data + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + struct i2c_msg msg; + u8 addr_bytes[3]; /* lowest...highest byte of data address */ + + PRINTD(("i2c_read(chip=0x%02x, addr=0x%02x, alen=0x%02x, " + "len=0x%02x)\n", chip, addr, alen, len)); + + i2c_reset(); + + /* dummy chip address write */ + PRINTD(("i2c_read: dummy chip address write\n")); + msg.condition = I2C_COND_START; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = (chip << 1); + msg.data &= 0xFE; + if (i2c_transfer(&msg)) + return -1; + + /* + * send memory address bytes; + * alen defines how much bytes we have to send. + */ + /*addr &= ((1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS)-1); */ + addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF); + addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF); + addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF); + + while (--alen >= 0) { + PRINTD(("i2c_read: send memory word address byte %1d\n", alen)); + msg.condition = I2C_COND_NORMAL; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = addr_bytes[alen]; + if (i2c_transfer(&msg)) + return -1; + } + + /* start read sequence */ + PRINTD(("i2c_read: start read sequence\n")); + msg.condition = I2C_COND_START; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = (chip << 1); + msg.data |= 0x01; + if (i2c_transfer(&msg)) + return -1; + + /* read bytes; send NACK at last byte */ + while (len--) { + if (len == 0) { + msg.condition = I2C_COND_STOP; + msg.acknack = I2C_ACKNAK_SENDNAK; + } else { + msg.condition = I2C_COND_NORMAL; + msg.acknack = I2C_ACKNAK_SENDACK; + } + + msg.direction = I2C_READ; + msg.data = 0x00; + if (i2c_transfer(&msg)) + return -1; + + *buffer = msg.data; + PRINTD(("i2c_read: reading byte (0x%08x)=0x%02x\n", + (unsigned int)buffer, *buffer)); + buffer++; + } + + i2c_reset(); + + return 0; +} + +/* + * i2c_write: - Write multiple bytes to an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be written + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to find the data to be written + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + struct i2c_msg msg; + u8 addr_bytes[3]; /* lowest...highest byte of data address */ + + PRINTD(("i2c_write(chip=0x%02x, addr=0x%02x, alen=0x%02x, " + "len=0x%02x)\n", chip, addr, alen, len)); + + i2c_reset(); + + /* chip address write */ + PRINTD(("i2c_write: chip address write\n")); + msg.condition = I2C_COND_START; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = (chip << 1); + msg.data &= 0xFE; + if (i2c_transfer(&msg)) + return -1; + + /* + * send memory address bytes; + * alen defines how much bytes we have to send. + */ + addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF); + addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF); + addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF); + + while (--alen >= 0) { + PRINTD(("i2c_write: send memory word address\n")); + msg.condition = I2C_COND_NORMAL; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = addr_bytes[alen]; + if (i2c_transfer(&msg)) + return -1; + } + + /* write bytes; send NACK at last byte */ + while (len--) { + PRINTD(("i2c_write: writing byte (0x%08x)=0x%02x\n", + (unsigned int)buffer, *buffer)); + + if (len == 0) + msg.condition = I2C_COND_STOP; + else + msg.condition = I2C_COND_NORMAL; + + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = *(buffer++); + + if (i2c_transfer(&msg)) + return -1; + } + + i2c_reset(); + + return 0; +} +#endif /* CONFIG_HARD_I2C */ diff --git a/qemu/roms/u-boot/drivers/i2c/mv_i2c.h b/qemu/roms/u-boot/drivers/i2c/mv_i2c.h new file mode 100644 index 000000000..ae27c447b --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mv_i2c.h @@ -0,0 +1,67 @@ +/* + * (C) Copyright 2011 + * Marvell Inc, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _MV_I2C_H_ +#define _MV_I2C_H_ +extern void i2c_clk_enable(void); + +/* Shall the current transfer have a start/stop condition? */ +#define I2C_COND_NORMAL 0 +#define I2C_COND_START 1 +#define I2C_COND_STOP 2 + +/* Shall the current transfer be ack/nacked or being waited for it? */ +#define I2C_ACKNAK_WAITACK 1 +#define I2C_ACKNAK_SENDACK 2 +#define I2C_ACKNAK_SENDNAK 4 + +/* Specify who shall transfer the data (master or slave) */ +#define I2C_READ 0 +#define I2C_WRITE 1 + +#if (CONFIG_SYS_I2C_SPEED == 400000) +#define I2C_ICR_INIT (ICR_FM | ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD \ + | ICR_SCLE) +#else +#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE) +#endif + +#define I2C_ISR_INIT 0x7FF +/* ----- Control register bits ---------------------------------------- */ + +#define ICR_START 0x1 /* start bit */ +#define ICR_STOP 0x2 /* stop bit */ +#define ICR_ACKNAK 0x4 /* send ACK(0) or NAK(1) */ +#define ICR_TB 0x8 /* transfer byte bit */ +#define ICR_MA 0x10 /* master abort */ +#define ICR_SCLE 0x20 /* master clock enable, mona SCLEA */ +#define ICR_IUE 0x40 /* unit enable */ +#define ICR_GCD 0x80 /* general call disable */ +#define ICR_ITEIE 0x100 /* enable tx interrupts */ +#define ICR_IRFIE 0x200 /* enable rx interrupts, mona: DRFIE */ +#define ICR_BEIE 0x400 /* enable bus error ints */ +#define ICR_SSDIE 0x800 /* slave STOP detected int enable */ +#define ICR_ALDIE 0x1000 /* enable arbitration interrupt */ +#define ICR_SADIE 0x2000 /* slave address detected int enable */ +#define ICR_UR 0x4000 /* unit reset */ +#define ICR_FM 0x8000 /* Fast Mode */ + +/* ----- Status register bits ----------------------------------------- */ + +#define ISR_RWM 0x1 /* read/write mode */ +#define ISR_ACKNAK 0x2 /* ack/nak status */ +#define ISR_UB 0x4 /* unit busy */ +#define ISR_IBB 0x8 /* bus busy */ +#define ISR_SSD 0x10 /* slave stop detected */ +#define ISR_ALD 0x20 /* arbitration loss detected */ +#define ISR_ITE 0x40 /* tx buffer empty */ +#define ISR_IRF 0x80 /* rx buffer full */ +#define ISR_GCAD 0x100 /* general call address detected */ +#define ISR_SAD 0x200 /* slave address detected */ +#define ISR_BED 0x400 /* bus error no ACK/NAK */ + +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/mvtwsi.c b/qemu/roms/u-boot/drivers/i2c/mvtwsi.c new file mode 100644 index 000000000..5ba0e0386 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mvtwsi.c @@ -0,0 +1,394 @@ +/* + * Driver for the TWSI (i2c) controller found on the Marvell + * orion5x and kirkwood SoC families. + * + * Author: Albert Aribaud + * Copyright (c) 2010 Albert Aribaud. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* + * include a file that will provide CONFIG_I2C_MVTWSI_BASE + * and possibly other settings + */ + +#if defined(CONFIG_ORION5X) +#include +#elif defined(CONFIG_KIRKWOOD) +#include +#else +#error Driver mvtwsi not supported by SoC or board +#endif + +/* + * TWSI register structure + */ + +struct mvtwsi_registers { + u32 slave_address; + u32 data; + u32 control; + union { + u32 status; /* when reading */ + u32 baudrate; /* when writing */ + }; + u32 xtnd_slave_addr; + u32 reserved[2]; + u32 soft_reset; +}; + +/* + * Control register fields + */ + +#define MVTWSI_CONTROL_ACK 0x00000004 +#define MVTWSI_CONTROL_IFLG 0x00000008 +#define MVTWSI_CONTROL_STOP 0x00000010 +#define MVTWSI_CONTROL_START 0x00000020 +#define MVTWSI_CONTROL_TWSIEN 0x00000040 +#define MVTWSI_CONTROL_INTEN 0x00000080 + +/* + * Status register values -- only those expected in normal master + * operation on non-10-bit-address devices; whatever status we don't + * expect in nominal conditions (bus errors, arbitration losses, + * missing ACKs...) we just pass back to the caller as an error + * code. + */ + +#define MVTWSI_STATUS_START 0x08 +#define MVTWSI_STATUS_REPEATED_START 0x10 +#define MVTWSI_STATUS_ADDR_W_ACK 0x18 +#define MVTWSI_STATUS_DATA_W_ACK 0x28 +#define MVTWSI_STATUS_ADDR_R_ACK 0x40 +#define MVTWSI_STATUS_ADDR_R_NAK 0x48 +#define MVTWSI_STATUS_DATA_R_ACK 0x50 +#define MVTWSI_STATUS_DATA_R_NAK 0x58 +#define MVTWSI_STATUS_IDLE 0xF8 + +/* + * The single instance of the controller we'll be dealing with + */ + +static struct mvtwsi_registers *twsi = + (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE; + +/* + * Returned statuses are 0 for success and nonzero otherwise. + * Currently, cmd_i2c and cmd_eeprom do not interpret an error status. + * Thus to ease debugging, the return status contains some debug info: + * - bits 31..24 are error class: 1 is timeout, 2 is 'status mismatch'. + * - bits 23..16 are the last value of the control register. + * - bits 15..8 are the last value of the status register. + * - bits 7..0 are the expected value of the status register. + */ + +#define MVTWSI_ERROR_WRONG_STATUS 0x01 +#define MVTWSI_ERROR_TIMEOUT 0x02 + +#define MVTWSI_ERROR(ec, lc, ls, es) (((ec << 24) & 0xFF000000) | \ + ((lc << 16) & 0x00FF0000) | ((ls<<8) & 0x0000FF00) | (es & 0xFF)) + +/* + * Wait for IFLG to raise, or return 'timeout'; then if status is as expected, + * return 0 (ok) or return 'wrong status'. + */ +static int twsi_wait(int expected_status) +{ + int control, status; + int timeout = 1000; + + do { + control = readl(&twsi->control); + if (control & MVTWSI_CONTROL_IFLG) { + status = readl(&twsi->status); + if (status == expected_status) + return 0; + else + return MVTWSI_ERROR( + MVTWSI_ERROR_WRONG_STATUS, + control, status, expected_status); + } + udelay(10); /* one clock cycle at 100 kHz */ + } while (timeout--); + status = readl(&twsi->status); + return MVTWSI_ERROR( + MVTWSI_ERROR_TIMEOUT, control, status, expected_status); +} + +/* + * These flags are ORed to any write to the control register + * They allow global setting of TWSIEN and ACK. + * By default none are set. + * twsi_start() sets TWSIEN (in case the controller was disabled) + * twsi_recv() sets ACK or resets it depending on expected status. + */ +static u8 twsi_control_flags = MVTWSI_CONTROL_TWSIEN; + +/* + * Assert the START condition, either in a single I2C transaction + * or inside back-to-back ones (repeated starts). + */ +static int twsi_start(int expected_status) +{ + /* globally set TWSIEN in case it was not */ + twsi_control_flags |= MVTWSI_CONTROL_TWSIEN; + /* assert START */ + writel(twsi_control_flags | MVTWSI_CONTROL_START, &twsi->control); + /* wait for controller to process START */ + return twsi_wait(expected_status); +} + +/* + * Send a byte (i2c address or data). + */ +static int twsi_send(u8 byte, int expected_status) +{ + /* put byte in data register for sending */ + writel(byte, &twsi->data); + /* clear any pending interrupt -- that'll cause sending */ + writel(twsi_control_flags, &twsi->control); + /* wait for controller to receive byte and check ACK */ + return twsi_wait(expected_status); +} + +/* + * Receive a byte. + * Global mvtwsi_control_flags variable says if we should ack or nak. + */ +static int twsi_recv(u8 *byte) +{ + int expected_status, status; + + /* compute expected status based on ACK bit in global control flags */ + if (twsi_control_flags & MVTWSI_CONTROL_ACK) + expected_status = MVTWSI_STATUS_DATA_R_ACK; + else + expected_status = MVTWSI_STATUS_DATA_R_NAK; + /* acknowledge *previous state* and launch receive */ + writel(twsi_control_flags, &twsi->control); + /* wait for controller to receive byte and assert ACK or NAK */ + status = twsi_wait(expected_status); + /* if we did receive expected byte then store it */ + if (status == 0) + *byte = readl(&twsi->data); + /* return status */ + return status; +} + +/* + * Assert the STOP condition. + * This is also used to force the bus back in idle (SDA=SCL=1). + */ +static int twsi_stop(int status) +{ + int control, stop_status; + int timeout = 1000; + + /* assert STOP */ + control = MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_STOP; + writel(control, &twsi->control); + /* wait for IDLE; IFLG won't rise so twsi_wait() is no use. */ + do { + stop_status = readl(&twsi->status); + if (stop_status == MVTWSI_STATUS_IDLE) + break; + udelay(10); /* one clock cycle at 100 kHz */ + } while (timeout--); + control = readl(&twsi->control); + if (stop_status != MVTWSI_STATUS_IDLE) + if (status == 0) + status = MVTWSI_ERROR( + MVTWSI_ERROR_TIMEOUT, + control, status, MVTWSI_STATUS_IDLE); + return status; +} + +/* + * Ugly formula to convert m and n values to a frequency comes from + * TWSI specifications + */ + +#define TWSI_FREQUENCY(m, n) \ + (CONFIG_SYS_TCLK / (10 * (m + 1) * (1 << n))) + +/* + * Reset controller. + * Called at end of i2c_init unsuccessful i2c transactions. + * Controller reset also resets the baud rate and slave address, so + * re-establish them. + */ +static void twsi_reset(u8 baud_rate, u8 slave_address) +{ + /* ensure controller will be enabled by any twsi*() function */ + twsi_control_flags = MVTWSI_CONTROL_TWSIEN; + /* reset controller */ + writel(0, &twsi->soft_reset); + /* wait 2 ms -- this is what the Marvell LSP does */ + udelay(20000); + /* set baud rate */ + writel(baud_rate, &twsi->baudrate); + /* set slave address even though we don't use it */ + writel(slave_address, &twsi->slave_address); + writel(0, &twsi->xtnd_slave_addr); + /* assert STOP but don't care for the result */ + (void) twsi_stop(0); +} + +/* + * I2C init called by cmd_i2c when doing 'i2c reset'. + * Sets baud to the highest possible value not exceeding requested one. + */ +void i2c_init(int requested_speed, int slaveadd) +{ + int tmp_speed, highest_speed, n, m; + int baud = 0x44; /* baudrate at controller reset */ + + /* use actual speed to collect progressively higher values */ + highest_speed = 0; + /* compute m, n setting for highest speed not above requested speed */ + for (n = 0; n < 8; n++) { + for (m = 0; m < 16; m++) { + tmp_speed = TWSI_FREQUENCY(m, n); + if ((tmp_speed <= requested_speed) + && (tmp_speed > highest_speed)) { + highest_speed = tmp_speed; + baud = (m << 3) | n; + } + } + } + /* reset controller */ + twsi_reset(baud, slaveadd); +} + +/* + * Begin I2C transaction with expected start status, at given address. + * Common to i2c_probe, i2c_read and i2c_write. + * Expected address status will derive from direction bit (bit 0) in addr. + */ +static int i2c_begin(int expected_start_status, u8 addr) +{ + int status, expected_addr_status; + + /* compute expected address status from direction bit in addr */ + if (addr & 1) /* reading */ + expected_addr_status = MVTWSI_STATUS_ADDR_R_ACK; + else /* writing */ + expected_addr_status = MVTWSI_STATUS_ADDR_W_ACK; + /* assert START */ + status = twsi_start(expected_start_status); + /* send out the address if the start went well */ + if (status == 0) + status = twsi_send(addr, expected_addr_status); + /* return ok or status of first failure to caller */ + return status; +} + +/* + * I2C probe called by cmd_i2c when doing 'i2c probe'. + * Begin read, nak data byte, end. + */ +int i2c_probe(uchar chip) +{ + u8 dummy_byte; + int status; + + /* begin i2c read */ + status = i2c_begin(MVTWSI_STATUS_START, (chip << 1) | 1); + /* dummy read was accepted: receive byte but NAK it. */ + if (status == 0) + status = twsi_recv(&dummy_byte); + /* Stop transaction */ + twsi_stop(0); + /* return 0 or status of first failure */ + return status; +} + +/* + * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c + * Begin write, send address byte(s), begin read, receive data bytes, end. + * + * NOTE: some EEPROMS want a stop right before the second start, while + * some will choke if it is there. Deciding which we should do is eeprom + * stuff, not i2c, but at the moment the APIs won't let us put it in + * cmd_eeprom, so we have to choose here, and for the moment that'll be + * a repeated start without a preceding stop. + */ +int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) +{ + int status; + + /* begin i2c write to send the address bytes */ + status = i2c_begin(MVTWSI_STATUS_START, (dev << 1)); + /* send addr bytes */ + while ((status == 0) && alen--) + status = twsi_send(addr >> (8*alen), + MVTWSI_STATUS_DATA_W_ACK); + /* begin i2c read to receive eeprom data bytes */ + if (status == 0) + status = i2c_begin( + MVTWSI_STATUS_REPEATED_START, (dev << 1) | 1); + /* prepare ACK if at least one byte must be received */ + if (length > 0) + twsi_control_flags |= MVTWSI_CONTROL_ACK; + /* now receive actual bytes */ + while ((status == 0) && length--) { + /* reset NAK if we if no more to read now */ + if (length == 0) + twsi_control_flags &= ~MVTWSI_CONTROL_ACK; + /* read current byte */ + status = twsi_recv(data++); + } + /* Stop transaction */ + status = twsi_stop(status); + /* return 0 or status of first failure */ + return status; +} + +/* + * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c + * Begin write, send address byte(s), send data bytes, end. + */ +int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) +{ + int status; + + /* begin i2c write to send the eeprom adress bytes then data bytes */ + status = i2c_begin(MVTWSI_STATUS_START, (dev << 1)); + /* send addr bytes */ + while ((status == 0) && alen--) + status = twsi_send(addr >> (8*alen), + MVTWSI_STATUS_DATA_W_ACK); + /* send data bytes */ + while ((status == 0) && (length-- > 0)) + status = twsi_send(*(data++), MVTWSI_STATUS_DATA_W_ACK); + /* Stop transaction */ + status = twsi_stop(status); + /* return 0 or status of first failure */ + return status; +} + +/* + * Bus set routine: we only support bus 0. + */ +int i2c_set_bus_num(unsigned int bus) +{ + if (bus > 0) { + return -1; + } + return 0; +} + +/* + * Bus get routine: hard-return bus 0. + */ +unsigned int i2c_get_bus_num(void) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/i2c/mxc_i2c.c b/qemu/roms/u-boot/drivers/i2c/mxc_i2c.c new file mode 100644 index 000000000..48468d74b --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mxc_i2c.c @@ -0,0 +1,549 @@ +/* + * i2c driver for Freescale i.MX series + * + * (c) 2007 Pengutronix, Sascha Hauer + * (c) 2011 Marek Vasut + * + * Based on i2c-imx.c from linux kernel: + * Copyright (C) 2005 Torsten Koschorrek + * Copyright (C) 2005 Matthias Blaschke + * Copyright (C) 2007 RightHand Technologies, Inc. + * Copyright (C) 2008 Darius Augulis + * + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef I2C_QUIRK_REG +struct mxc_i2c_regs { + uint8_t iadr; + uint8_t ifdr; + uint8_t i2cr; + uint8_t i2sr; + uint8_t i2dr; +}; +#else +struct mxc_i2c_regs { + uint32_t iadr; + uint32_t ifdr; + uint32_t i2cr; + uint32_t i2sr; + uint32_t i2dr; +}; +#endif + +#define I2CR_IIEN (1 << 6) +#define I2CR_MSTA (1 << 5) +#define I2CR_MTX (1 << 4) +#define I2CR_TX_NO_AK (1 << 3) +#define I2CR_RSTA (1 << 2) + +#define I2SR_ICF (1 << 7) +#define I2SR_IBB (1 << 5) +#define I2SR_IAL (1 << 4) +#define I2SR_IIF (1 << 1) +#define I2SR_RX_NO_AK (1 << 0) + +#ifdef I2C_QUIRK_REG +#define I2CR_IEN (0 << 7) +#define I2CR_IDIS (1 << 7) +#define I2SR_IIF_CLEAR (1 << 1) +#else +#define I2CR_IEN (1 << 7) +#define I2CR_IDIS (0 << 7) +#define I2SR_IIF_CLEAR (0 << 1) +#endif + +#if defined(CONFIG_HARD_I2C) && !defined(CONFIG_SYS_I2C_BASE) +#error "define CONFIG_SYS_I2C_BASE to use the mxc_i2c driver" +#endif + +#ifdef I2C_QUIRK_REG +static u16 i2c_clk_div[60][2] = { + { 20, 0x00 }, { 22, 0x01 }, { 24, 0x02 }, { 26, 0x03 }, + { 28, 0x04 }, { 30, 0x05 }, { 32, 0x09 }, { 34, 0x06 }, + { 36, 0x0A }, { 40, 0x07 }, { 44, 0x0C }, { 48, 0x0D }, + { 52, 0x43 }, { 56, 0x0E }, { 60, 0x45 }, { 64, 0x12 }, + { 68, 0x0F }, { 72, 0x13 }, { 80, 0x14 }, { 88, 0x15 }, + { 96, 0x19 }, { 104, 0x16 }, { 112, 0x1A }, { 128, 0x17 }, + { 136, 0x4F }, { 144, 0x1C }, { 160, 0x1D }, { 176, 0x55 }, + { 192, 0x1E }, { 208, 0x56 }, { 224, 0x22 }, { 228, 0x24 }, + { 240, 0x1F }, { 256, 0x23 }, { 288, 0x5C }, { 320, 0x25 }, + { 384, 0x26 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B }, + { 576, 0x2C }, { 640, 0x2D }, { 768, 0x31 }, { 896, 0x32 }, + { 960, 0x2F }, { 1024, 0x33 }, { 1152, 0x34 }, { 1280, 0x35 }, + { 1536, 0x36 }, { 1792, 0x3A }, { 1920, 0x37 }, { 2048, 0x3B }, + { 2304, 0x3C }, { 2560, 0x3D }, { 3072, 0x3E }, { 3584, 0x7A }, + { 3840, 0x3F }, { 4096, 0x7B }, { 5120, 0x7D }, { 6144, 0x7E }, +}; +#else +static u16 i2c_clk_div[50][2] = { + { 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 } +}; +#endif + + +#ifndef CONFIG_SYS_MXC_I2C1_SPEED +#define CONFIG_SYS_MXC_I2C1_SPEED 100000 +#endif +#ifndef CONFIG_SYS_MXC_I2C2_SPEED +#define CONFIG_SYS_MXC_I2C2_SPEED 100000 +#endif +#ifndef CONFIG_SYS_MXC_I2C3_SPEED +#define CONFIG_SYS_MXC_I2C3_SPEED 100000 +#endif + +#ifndef CONFIG_SYS_MXC_I2C1_SLAVE +#define CONFIG_SYS_MXC_I2C1_SLAVE 0 +#endif +#ifndef CONFIG_SYS_MXC_I2C2_SLAVE +#define CONFIG_SYS_MXC_I2C2_SLAVE 0 +#endif +#ifndef CONFIG_SYS_MXC_I2C3_SLAVE +#define CONFIG_SYS_MXC_I2C3_SLAVE 0 +#endif + + +/* + * Calculate and set proper clock divider + */ +static uint8_t i2c_imx_get_clk(unsigned int rate) +{ + unsigned int i2c_clk_rate; + unsigned int div; + u8 clk_div; + +#if defined(CONFIG_MX31) + struct clock_control_regs *sc_regs = + (struct clock_control_regs *)CCM_BASE; + + /* start the required I2C clock */ + writel(readl(&sc_regs->cgr0) | (3 << CONFIG_SYS_I2C_CLK_OFFSET), + &sc_regs->cgr0); +#endif + + /* Divider value calculation */ + i2c_clk_rate = mxc_get_clock(MXC_I2C_CLK); + div = (i2c_clk_rate + rate - 1) / rate; + if (div < i2c_clk_div[0][0]) + clk_div = 0; + else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) + clk_div = ARRAY_SIZE(i2c_clk_div) - 1; + else + for (clk_div = 0; i2c_clk_div[clk_div][0] < div; clk_div++) + ; + + /* Store divider value */ + return clk_div; +} + +/* + * Set I2C Bus speed + */ +static int bus_i2c_set_bus_speed(void *base, int speed) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; + u8 clk_idx = i2c_imx_get_clk(speed); + u8 idx = i2c_clk_div[clk_idx][1]; + + /* Store divider value */ + writeb(idx, &i2c_regs->ifdr); + + /* Reset module */ + writeb(I2CR_IDIS, &i2c_regs->i2cr); + writeb(0, &i2c_regs->i2sr); + return 0; +} + +#define ST_BUS_IDLE (0 | (I2SR_IBB << 8)) +#define ST_BUS_BUSY (I2SR_IBB | (I2SR_IBB << 8)) +#define ST_IIF (I2SR_IIF | (I2SR_IIF << 8)) + +static int wait_for_sr_state(struct mxc_i2c_regs *i2c_regs, unsigned state) +{ + unsigned sr; + ulong elapsed; + ulong start_time = get_timer(0); + for (;;) { + sr = readb(&i2c_regs->i2sr); + if (sr & I2SR_IAL) { +#ifdef I2C_QUIRK_REG + writeb(sr | I2SR_IAL, &i2c_regs->i2sr); +#else + writeb(sr & ~I2SR_IAL, &i2c_regs->i2sr); +#endif + printf("%s: Arbitration lost sr=%x cr=%x state=%x\n", + __func__, sr, readb(&i2c_regs->i2cr), state); + return -ERESTART; + } + if ((sr & (state >> 8)) == (unsigned char)state) + return sr; + WATCHDOG_RESET(); + elapsed = get_timer(start_time); + if (elapsed > (CONFIG_SYS_HZ / 10)) /* .1 seconds */ + break; + } + printf("%s: failed sr=%x cr=%x state=%x\n", __func__, + sr, readb(&i2c_regs->i2cr), state); + return -ETIMEDOUT; +} + +static int tx_byte(struct mxc_i2c_regs *i2c_regs, u8 byte) +{ + int ret; + + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); + writeb(byte, &i2c_regs->i2dr); + ret = wait_for_sr_state(i2c_regs, ST_IIF); + if (ret < 0) + return ret; + if (ret & I2SR_RX_NO_AK) + return -ENODEV; + return 0; +} + +/* + * Stop I2C transaction + */ +static void i2c_imx_stop(struct mxc_i2c_regs *i2c_regs) +{ + int ret; + unsigned int temp = readb(&i2c_regs->i2cr); + + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr); + ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE); + if (ret < 0) + printf("%s:trigger stop failed\n", __func__); +} + +/* + * Send start signal, chip address and + * write register address + */ +static int i2c_init_transfer_(struct mxc_i2c_regs *i2c_regs, + uchar chip, uint addr, int alen) +{ + unsigned int temp; + int ret; + + /* Enable I2C controller */ +#ifdef I2C_QUIRK_REG + if (readb(&i2c_regs->i2cr) & I2CR_IDIS) { +#else + if (!(readb(&i2c_regs->i2cr) & I2CR_IEN)) { +#endif + writeb(I2CR_IEN, &i2c_regs->i2cr); + /* Wait for controller to be stable */ + udelay(50); + } + if (readb(&i2c_regs->iadr) == (chip << 1)) + writeb((chip << 1) ^ 2, &i2c_regs->iadr); + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); + ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE); + if (ret < 0) + return ret; + + /* Start I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_MSTA; + writeb(temp, &i2c_regs->i2cr); + + ret = wait_for_sr_state(i2c_regs, ST_BUS_BUSY); + if (ret < 0) + return ret; + + temp |= I2CR_MTX | I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + + /* write slave address */ + ret = tx_byte(i2c_regs, chip << 1); + if (ret < 0) + return ret; + + while (alen--) { + ret = tx_byte(i2c_regs, (addr >> (alen * 8)) & 0xff); + if (ret < 0) + return ret; + } + return 0; +} + +static int i2c_idle_bus(void *base); + +static int i2c_init_transfer(struct mxc_i2c_regs *i2c_regs, + uchar chip, uint addr, int alen) +{ + int retry; + int ret; + for (retry = 0; retry < 3; retry++) { + ret = i2c_init_transfer_(i2c_regs, chip, addr, alen); + if (ret >= 0) + return 0; + i2c_imx_stop(i2c_regs); + if (ret == -ENODEV) + return ret; + + printf("%s: failed for chip 0x%x retry=%d\n", __func__, chip, + retry); + if (ret != -ERESTART) + /* Disable controller */ + writeb(I2CR_IDIS, &i2c_regs->i2cr); + udelay(100); + if (i2c_idle_bus(i2c_regs) < 0) + break; + } + printf("%s: give up i2c_regs=%p\n", __func__, i2c_regs); + return ret; +} + +/* + * Read data from I2C device + */ +int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf, + int len) +{ + int ret; + unsigned int temp; + int i; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; + + ret = i2c_init_transfer(i2c_regs, chip, addr, alen); + if (ret < 0) + return ret; + + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_RSTA; + writeb(temp, &i2c_regs->i2cr); + + ret = tx_byte(i2c_regs, (chip << 1) | 1); + if (ret < 0) { + i2c_imx_stop(i2c_regs); + return ret; + } + + /* setup bus to read data */ + temp = readb(&i2c_regs->i2cr); + temp &= ~(I2CR_MTX | I2CR_TX_NO_AK); + if (len == 1) + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); + readb(&i2c_regs->i2dr); /* dummy read to clear ICF */ + + /* read data */ + for (i = 0; i < len; i++) { + ret = wait_for_sr_state(i2c_regs, ST_IIF); + if (ret < 0) { + i2c_imx_stop(i2c_regs); + return ret; + } + + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + if (i == (len - 1)) { + i2c_imx_stop(i2c_regs); + } else if (i == (len - 2)) { + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + } + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); + buf[i] = readb(&i2c_regs->i2dr); + } + i2c_imx_stop(i2c_regs); + return 0; +} + +/* + * Write data to I2C device + */ +int bus_i2c_write(void *base, uchar chip, uint addr, int alen, + const uchar *buf, int len) +{ + int ret; + int i; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; + + ret = i2c_init_transfer(i2c_regs, chip, addr, alen); + if (ret < 0) + return ret; + + for (i = 0; i < len; i++) { + ret = tx_byte(i2c_regs, buf[i]); + if (ret < 0) + break; + } + i2c_imx_stop(i2c_regs); + return ret; +} + +struct i2c_parms { + void *base; + void *idle_bus_data; + int (*idle_bus_fn)(void *p); +}; + +struct sram_data { + unsigned curr_i2c_bus; + struct i2c_parms i2c_data[3]; +}; + +static void * const i2c_bases[] = { +#if defined(CONFIG_MX25) + (void *)IMX_I2C_BASE, + (void *)IMX_I2C2_BASE, + (void *)IMX_I2C3_BASE +#elif defined(CONFIG_MX27) + (void *)IMX_I2C1_BASE, + (void *)IMX_I2C2_BASE +#elif defined(CONFIG_MX31) || defined(CONFIG_MX35) || \ + defined(CONFIG_MX51) || defined(CONFIG_MX53) || \ + defined(CONFIG_MX6) + (void *)I2C1_BASE_ADDR, + (void *)I2C2_BASE_ADDR, + (void *)I2C3_BASE_ADDR +#elif defined(CONFIG_VF610) + (void *)I2C0_BASE_ADDR +#else +#error "architecture not supported" +#endif +}; + +void *i2c_get_base(struct i2c_adapter *adap) +{ + return i2c_bases[adap->hwadapnr]; +} + +static struct i2c_parms *i2c_get_parms(void *base) +{ + struct sram_data *srdata = (void *)gd->srdata; + int i = 0; + struct i2c_parms *p = srdata->i2c_data; + while (i < ARRAY_SIZE(srdata->i2c_data)) { + if (p->base == base) + return p; + p++; + i++; + } + printf("Invalid I2C base: %p\n", base); + return NULL; +} + +static int i2c_idle_bus(void *base) +{ + struct i2c_parms *p = i2c_get_parms(base); + if (p && p->idle_bus_fn) + return p->idle_bus_fn(p->idle_bus_data); + return 0; +} + +static int mxc_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, + int len) +{ + return bus_i2c_read(i2c_get_base(adap), chip, addr, alen, buffer, len); +} + +static int mxc_i2c_write(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, + int len) +{ + return bus_i2c_write(i2c_get_base(adap), chip, addr, alen, buffer, len); +} + +/* + * Test if a chip at a given address responds (probe the chip) + */ +static int mxc_i2c_probe(struct i2c_adapter *adap, uint8_t chip) +{ + return bus_i2c_write(i2c_get_base(adap), chip, 0, 0, NULL, 0); +} + +void bus_i2c_init(void *base, int speed, int unused, + int (*idle_bus_fn)(void *p), void *idle_bus_data) +{ + struct sram_data *srdata = (void *)gd->srdata; + int i = 0; + struct i2c_parms *p = srdata->i2c_data; + if (!base) + return; + for (;;) { + if (!p->base || (p->base == base)) { + p->base = base; + if (idle_bus_fn) { + p->idle_bus_fn = idle_bus_fn; + p->idle_bus_data = idle_bus_data; + } + break; + } + p++; + i++; + if (i >= ARRAY_SIZE(srdata->i2c_data)) + return; + } + bus_i2c_set_bus_speed(base, speed); +} + +/* + * Init I2C Bus + */ +static void mxc_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + bus_i2c_init(i2c_get_base(adap), speed, slaveaddr, NULL, NULL); +} + +/* + * Set I2C Speed + */ +static uint mxc_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed) +{ + return bus_i2c_set_bus_speed(i2c_get_base(adap), speed); +} + +/* + * Register mxc i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(mxc0, mxc_i2c_init, mxc_i2c_probe, + mxc_i2c_read, mxc_i2c_write, + mxc_i2c_set_bus_speed, + CONFIG_SYS_MXC_I2C1_SPEED, + CONFIG_SYS_MXC_I2C1_SLAVE, 0) +U_BOOT_I2C_ADAP_COMPLETE(mxc1, mxc_i2c_init, mxc_i2c_probe, + mxc_i2c_read, mxc_i2c_write, + mxc_i2c_set_bus_speed, + CONFIG_SYS_MXC_I2C2_SPEED, + CONFIG_SYS_MXC_I2C2_SLAVE, 1) +#if defined(CONFIG_MX31) || defined(CONFIG_MX35) ||\ + defined(CONFIG_MX51) || defined(CONFIG_MX53) ||\ + defined(CONFIG_MX6) +U_BOOT_I2C_ADAP_COMPLETE(mxc2, mxc_i2c_init, mxc_i2c_probe, + mxc_i2c_read, mxc_i2c_write, + mxc_i2c_set_bus_speed, + CONFIG_SYS_MXC_I2C3_SPEED, + CONFIG_SYS_MXC_I2C3_SLAVE, 2) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/mxs_i2c.c b/qemu/roms/u-boot/drivers/i2c/mxs_i2c.c new file mode 100644 index 000000000..de3b19402 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mxs_i2c.c @@ -0,0 +1,299 @@ +/* + * Freescale i.MX28 I2C Driver + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * Partly based on Linux kernel i2c-mxs.c driver: + * Copyright (C) 2011 Wolfram Sang, Pengutronix e.K. + * + * Which was based on a (non-working) driver which was: + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MXS_I2C_MAX_TIMEOUT 1000000 + +static void mxs_i2c_reset(void) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + int ret; + int speed = i2c_get_bus_speed(); + + ret = mxs_reset_block(&i2c_regs->hw_i2c_ctrl0_reg); + if (ret) { + debug("MXS I2C: Block reset timeout\n"); + return; + } + + writel(I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | I2C_CTRL1_NO_SLAVE_ACK_IRQ | + I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ | + I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ, + &i2c_regs->hw_i2c_ctrl1_clr); + + writel(I2C_QUEUECTRL_PIO_QUEUE_MODE, &i2c_regs->hw_i2c_queuectrl_set); + + i2c_set_bus_speed(speed); +} + +static void mxs_i2c_setup_read(uint8_t chip, int len) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + + writel(I2C_QUEUECMD_RETAIN_CLOCK | I2C_QUEUECMD_PRE_SEND_START | + I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION | + (1 << I2C_QUEUECMD_XFER_COUNT_OFFSET), + &i2c_regs->hw_i2c_queuecmd); + + writel((chip << 1) | 1, &i2c_regs->hw_i2c_data); + + writel(I2C_QUEUECMD_SEND_NAK_ON_LAST | I2C_QUEUECMD_MASTER_MODE | + (len << I2C_QUEUECMD_XFER_COUNT_OFFSET) | + I2C_QUEUECMD_POST_SEND_STOP, &i2c_regs->hw_i2c_queuecmd); + + writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set); +} + +static int mxs_i2c_write(uchar chip, uint addr, int alen, + uchar *buf, int blen, int stop) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t data, tmp; + int i, remain, off; + int timeout = MXS_I2C_MAX_TIMEOUT; + + if ((alen > 4) || (alen == 0)) { + debug("MXS I2C: Invalid address length\n"); + return -EINVAL; + } + + if (stop) + stop = I2C_QUEUECMD_POST_SEND_STOP; + + writel(I2C_QUEUECMD_PRE_SEND_START | + I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION | + ((blen + alen + 1) << I2C_QUEUECMD_XFER_COUNT_OFFSET) | stop, + &i2c_regs->hw_i2c_queuecmd); + + data = (chip << 1) << 24; + + for (i = 0; i < alen; i++) { + data >>= 8; + data |= ((char *)&addr)[alen - i - 1] << 24; + if ((i & 3) == 2) + writel(data, &i2c_regs->hw_i2c_data); + } + + off = i; + for (; i < off + blen; i++) { + data >>= 8; + data |= buf[i - off] << 24; + if ((i & 3) == 2) + writel(data, &i2c_regs->hw_i2c_data); + } + + remain = 24 - ((i & 3) * 8); + if (remain) + writel(data >> remain, &i2c_regs->hw_i2c_data); + + writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set); + + while (--timeout) { + tmp = readl(&i2c_regs->hw_i2c_queuestat); + if (tmp & I2C_QUEUESTAT_WR_QUEUE_EMPTY) + break; + } + + if (!timeout) { + debug("MXS I2C: Failed transmitting data!\n"); + return -EINVAL; + } + + return 0; +} + +static int mxs_i2c_wait_for_ack(void) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t tmp; + int timeout = MXS_I2C_MAX_TIMEOUT; + + for (;;) { + tmp = readl(&i2c_regs->hw_i2c_ctrl1); + if (tmp & I2C_CTRL1_NO_SLAVE_ACK_IRQ) { + debug("MXS I2C: No slave ACK\n"); + goto err; + } + + if (tmp & ( + I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ | + I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ)) { + debug("MXS I2C: Error (CTRL1 = %08x)\n", tmp); + goto err; + } + + if (tmp & I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ) + break; + + if (!timeout--) { + debug("MXS I2C: Operation timed out\n"); + goto err; + } + + udelay(1); + } + + return 0; + +err: + mxs_i2c_reset(); + return 1; +} + +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t tmp = 0; + int timeout = MXS_I2C_MAX_TIMEOUT; + int ret; + int i; + + ret = mxs_i2c_write(chip, addr, alen, NULL, 0, 0); + if (ret) { + debug("MXS I2C: Failed writing address\n"); + return ret; + } + + ret = mxs_i2c_wait_for_ack(); + if (ret) { + debug("MXS I2C: Failed writing address\n"); + return ret; + } + + mxs_i2c_setup_read(chip, len); + ret = mxs_i2c_wait_for_ack(); + if (ret) { + debug("MXS I2C: Failed reading address\n"); + return ret; + } + + for (i = 0; i < len; i++) { + if (!(i & 3)) { + while (--timeout) { + tmp = readl(&i2c_regs->hw_i2c_queuestat); + if (!(tmp & I2C_QUEUESTAT_RD_QUEUE_EMPTY)) + break; + } + + if (!timeout) { + debug("MXS I2C: Failed receiving data!\n"); + return -ETIMEDOUT; + } + + tmp = readl(&i2c_regs->hw_i2c_queuedata); + } + buffer[i] = tmp & 0xff; + tmp >>= 8; + } + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int ret; + ret = mxs_i2c_write(chip, addr, alen, buffer, len, 1); + if (ret) { + debug("MXS I2C: Failed writing address\n"); + return ret; + } + + ret = mxs_i2c_wait_for_ack(); + if (ret) + debug("MXS I2C: Failed writing address\n"); + + return ret; +} + +int i2c_probe(uchar chip) +{ + int ret; + ret = mxs_i2c_write(chip, 0, 1, NULL, 0, 1); + if (!ret) + ret = mxs_i2c_wait_for_ack(); + mxs_i2c_reset(); + return ret; +} + +int i2c_set_bus_speed(unsigned int speed) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + /* + * The timing derivation algorithm. There is no documentation for this + * algorithm available, it was derived by using the scope and fiddling + * with constants until the result observed on the scope was good enough + * for 20kHz, 50kHz, 100kHz, 200kHz, 300kHz and 400kHz. It should be + * possible to assume the algorithm works for other frequencies as well. + * + * Note it was necessary to cap the frequency on both ends as it's not + * possible to configure completely arbitrary frequency for the I2C bus + * clock. + */ + uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); + uint32_t base = ((clk / speed) - 38) / 2; + uint16_t high_count = base + 3; + uint16_t low_count = base - 3; + uint16_t rcv_count = (high_count * 3) / 4; + uint16_t xmit_count = low_count / 4; + + if (speed > 540000) { + printf("MXS I2C: Speed too high (%d Hz)\n", speed); + return -EINVAL; + } + + if (speed < 12000) { + printf("MXS I2C: Speed too low (%d Hz)\n", speed); + return -EINVAL; + } + + writel((high_count << 16) | rcv_count, &i2c_regs->hw_i2c_timing0); + writel((low_count << 16) | xmit_count, &i2c_regs->hw_i2c_timing1); + + writel((0x0030 << I2C_TIMING2_BUS_FREE_OFFSET) | + (0x0030 << I2C_TIMING2_LEADIN_COUNT_OFFSET), + &i2c_regs->hw_i2c_timing2); + + return 0; +} + +unsigned int i2c_get_bus_speed(void) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); + uint32_t timing0; + + timing0 = readl(&i2c_regs->hw_i2c_timing0); + /* + * This is a reverse version of the algorithm presented in + * i2c_set_bus_speed(). Please refer there for details. + */ + return clk / ((((timing0 >> 16) - 3) * 2) + 38); +} + +void i2c_init(int speed, int slaveadd) +{ + mxs_i2c_reset(); + i2c_set_bus_speed(speed); + + return; +} diff --git a/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.c b/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.c new file mode 100644 index 000000000..a39b5917e --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.c @@ -0,0 +1,680 @@ +/* + * Basic I2C functions + * + * Copyright (c) 2004 Texas Instruments + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the license found in the file + * named COPYING that should have accompanied this file. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Jian Zhang jzhang@ti.com, Texas Instruments + * + * Copyright (c) 2003 Wolfgang Denk, wd@denx.de + * Rewritten to fit into the current U-Boot framework + * + * Adapted for OMAP2420 I2C, r-woodruff2@ti.com + * + * Copyright (c) 2013 Lubomir Popov , MM Solutions + * New i2c_read, i2c_write and i2c_probe functions, tested on OMAP4 + * (4430/60/70), OMAP5 (5430) and AM335X (3359); should work on older + * OMAPs and derivatives as well. The only anticipated exception would + * be the OMAP2420, which shall require driver modification. + * - Rewritten i2c_read to operate correctly with all types of chips + * (old function could not read consistent data from some I2C slaves). + * - Optimized i2c_write. + * - New i2c_probe, performs write access vs read. The old probe could + * hang the system under certain conditions (e.g. unconfigured pads). + * - The read/write/probe functions try to identify unconfigured bus. + * - Status functions now read irqstatus_raw as per TRM guidelines + * (except for OMAP243X and OMAP34XX). + * - Driver now supports up to I2C5 (OMAP5). + * + * Copyright (c) 2014 Hannes Petermaier , B&R + * - Added support for set_speed + * + */ + +#include +#include + +#include +#include + +#include "omap24xx_i2c.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define I2C_TIMEOUT 1000 + +/* Absolutely safe for status update at 100 kHz I2C: */ +#define I2C_WAIT 200 + +static int wait_for_bb(struct i2c_adapter *adap); +static struct i2c *omap24_get_base(struct i2c_adapter *adap); +static u16 wait_for_event(struct i2c_adapter *adap); +static void flush_fifo(struct i2c_adapter *adap); +static int omap24_i2c_findpsc(u32 *pscl, u32 *psch, uint speed) +{ + unsigned int sampleclk, prescaler; + int fsscll, fssclh; + + speed <<= 1; + prescaler = 0; + /* + * some divisors may cause a precission loss, but shouldn't + * be a big thing, because i2c_clk is then allready very slow. + */ + while (prescaler <= 0xFF) { + sampleclk = I2C_IP_CLK / (prescaler+1); + + fsscll = sampleclk / speed; + fssclh = fsscll; + fsscll -= I2C_FASTSPEED_SCLL_TRIM; + fssclh -= I2C_FASTSPEED_SCLH_TRIM; + + if (((fsscll > 0) && (fssclh > 0)) && + ((fsscll <= (255-I2C_FASTSPEED_SCLL_TRIM)) && + (fssclh <= (255-I2C_FASTSPEED_SCLH_TRIM)))) { + if (pscl) + *pscl = fsscll; + if (psch) + *psch = fssclh; + + return prescaler; + } + prescaler++; + } + return -1; +} +static uint omap24_i2c_setspeed(struct i2c_adapter *adap, uint speed) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int psc, fsscll = 0, fssclh = 0; + int hsscll = 0, hssclh = 0; + u32 scll = 0, sclh = 0; + + if (speed >= OMAP_I2C_HIGH_SPEED) { + /* High speed */ + psc = I2C_IP_CLK / I2C_INTERNAL_SAMPLING_CLK; + psc -= 1; + if (psc < I2C_PSC_MIN) { + printf("Error : I2C unsupported prescaler %d\n", psc); + return -1; + } + + /* For first phase of HS mode */ + fsscll = I2C_INTERNAL_SAMPLING_CLK / (2 * speed); + + fssclh = fsscll; + + fsscll -= I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM; + fssclh -= I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM; + if (((fsscll < 0) || (fssclh < 0)) || + ((fsscll > 255) || (fssclh > 255))) { + puts("Error : I2C initializing first phase clock\n"); + return -1; + } + + /* For second phase of HS mode */ + hsscll = hssclh = I2C_INTERNAL_SAMPLING_CLK / (2 * speed); + + hsscll -= I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM; + hssclh -= I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM; + if (((fsscll < 0) || (fssclh < 0)) || + ((fsscll > 255) || (fssclh > 255))) { + puts("Error : I2C initializing second phase clock\n"); + return -1; + } + + scll = (unsigned int)hsscll << 8 | (unsigned int)fsscll; + sclh = (unsigned int)hssclh << 8 | (unsigned int)fssclh; + + } else { + /* Standard and fast speed */ + psc = omap24_i2c_findpsc(&scll, &sclh, speed); + if (0 > psc) { + puts("Error : I2C initializing clock\n"); + return -1; + } + } + + adap->speed = speed; + adap->waitdelay = (10000000 / speed) * 2; /* wait for 20 clkperiods */ + writew(0, &i2c_base->con); + writew(psc, &i2c_base->psc); + writew(scll, &i2c_base->scll); + writew(sclh, &i2c_base->sclh); + writew(I2C_CON_EN, &i2c_base->con); + writew(0xFFFF, &i2c_base->stat); /* clear all pending status */ + + return 0; +} +static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int timeout = I2C_TIMEOUT; + + if (readw(&i2c_base->con) & I2C_CON_EN) { + writew(0, &i2c_base->con); + udelay(50000); + } + + writew(0x2, &i2c_base->sysc); /* for ES2 after soft reset */ + udelay(1000); + + writew(I2C_CON_EN, &i2c_base->con); + while (!(readw(&i2c_base->syss) & I2C_SYSS_RDONE) && timeout--) { + if (timeout <= 0) { + puts("ERROR: Timeout in soft-reset\n"); + return; + } + udelay(1000); + } + + if (0 != omap24_i2c_setspeed(adap, speed)) { + printf("ERROR: failed to setup I2C bus-speed!\n"); + return; + } + + /* own address */ + writew(slaveadd, &i2c_base->oa); + +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) + /* + * Have to enable interrupts for OMAP2/3, these IPs don't have + * an 'irqstatus_raw' register and we shall have to poll 'stat' + */ + writew(I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | + I2C_IE_NACK_IE | I2C_IE_AL_IE, &i2c_base->ie); +#endif + udelay(1000); + flush_fifo(adap); + writew(0xFFFF, &i2c_base->stat); +} + +static void flush_fifo(struct i2c_adapter *adap) +{ + struct i2c *i2c_base = omap24_get_base(adap); + u16 stat; + + /* + * note: if you try and read data when its not there or ready + * you get a bus error + */ + while (1) { + stat = readw(&i2c_base->stat); + if (stat == I2C_STAT_RRDY) { + readb(&i2c_base->data); + writew(I2C_STAT_RRDY, &i2c_base->stat); + udelay(1000); + } else + break; + } +} + +/* + * i2c_probe: Use write access. Allows to identify addresses that are + * write-only (like the config register of dual-port EEPROMs) + */ +static int omap24_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + struct i2c *i2c_base = omap24_get_base(adap); + u16 status; + int res = 1; /* default = fail */ + + if (chip == readw(&i2c_base->oa)) + return res; + + /* Wait until bus is free */ + if (wait_for_bb(adap)) + return res; + + /* No data transfer, slave addr only */ + writew(chip, &i2c_base->sa); + /* Stop bit needed here */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); + + status = wait_for_event(adap); + + if ((status & ~I2C_STAT_XRDY) == 0 || (status & I2C_STAT_AL)) { + /* + * With current high-level command implementation, notifying + * the user shall flood the console with 127 messages. If + * silent exit is desired upon unconfigured bus, remove the + * following 'if' section: + */ + if (status == I2C_STAT_XRDY) + printf("i2c_probe: pads on bus %d probably not configured (status=0x%x)\n", + adap->hwadapnr, status); + + goto pr_exit; + } + + /* Check for ACK (!NAK) */ + if (!(status & I2C_STAT_NACK)) { + res = 0; /* Device found */ + udelay(adap->waitdelay);/* Required by AM335X in SPL */ + /* Abort transfer (force idle state) */ + writew(I2C_CON_MST | I2C_CON_TRX, &i2c_base->con); /* Reset */ + udelay(1000); + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); /* STP */ + } +pr_exit: + flush_fifo(adap); + writew(0xFFFF, &i2c_base->stat); + return res; +} + +/* + * i2c_read: Function now uses a single I2C read transaction with bulk transfer + * of the requested number of bytes (note that the 'i2c md' command + * limits this to 16 bytes anyway). If CONFIG_I2C_REPEATED_START is + * defined in the board config header, this transaction shall be with + * Repeated Start (Sr) between the address and data phases; otherwise + * Stop-Start (P-S) shall be used (some I2C chips do require a P-S). + * The address (reg offset) may be 0, 1 or 2 bytes long. + * Function now reads correctly from chips that return more than one + * byte of data per addressed register (like TI temperature sensors), + * or that do not need a register address at all (such as some clock + * distributors). + */ +static int omap24_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int i2c_error = 0; + u16 status; + + if (alen < 0) { + puts("I2C read: addr len < 0\n"); + return 1; + } + if (len < 0) { + puts("I2C read: data len < 0\n"); + return 1; + } + if (buffer == NULL) { + puts("I2C read: NULL pointer passed\n"); + return 1; + } + + if (alen > 2) { + printf("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > (1 << 16)) { + puts("I2C read: address out of range\n"); + return 1; + } + + /* Wait until bus not busy */ + if (wait_for_bb(adap)) + return 1; + + /* Zero, one or two bytes reg address (offset) */ + writew(alen, &i2c_base->cnt); + /* Set slave address */ + writew(chip, &i2c_base->sa); + + if (alen) { + /* Must write reg offset first */ +#ifdef CONFIG_I2C_REPEATED_START + /* No stop bit, use Repeated Start (Sr) */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | + I2C_CON_TRX, &i2c_base->con); +#else + /* Stop - Start (P-S) */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP | + I2C_CON_TRX, &i2c_base->con); +#endif + /* Send register offset */ + while (1) { + status = wait_for_event(adap); + /* Try to identify bus that is not padconf'd for I2C */ + if (status == I2C_STAT_XRDY) { + i2c_error = 2; + printf("i2c_read (addr phase): pads on bus %d probably not configured (status=0x%x)\n", + adap->hwadapnr, status); + goto rd_exit; + } + if (status == 0 || (status & I2C_STAT_NACK)) { + i2c_error = 1; + printf("i2c_read: error waiting for addr ACK (status=0x%x)\n", + status); + goto rd_exit; + } + if (alen) { + if (status & I2C_STAT_XRDY) { + alen--; + /* Do we have to use byte access? */ + writeb((addr >> (8 * alen)) & 0xff, + &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + } + /* Set slave address */ + writew(chip, &i2c_base->sa); + /* Read len bytes from slave */ + writew(len, &i2c_base->cnt); + /* Need stop bit here */ + writew(I2C_CON_EN | I2C_CON_MST | + I2C_CON_STT | I2C_CON_STP, + &i2c_base->con); + + /* Receive data */ + while (1) { + status = wait_for_event(adap); + /* + * Try to identify bus that is not padconf'd for I2C. This + * state could be left over from previous transactions if + * the address phase is skipped due to alen=0. + */ + if (status == I2C_STAT_XRDY) { + i2c_error = 2; + printf("i2c_read (data phase): pads on bus %d probably not configured (status=0x%x)\n", + adap->hwadapnr, status); + goto rd_exit; + } + if (status == 0 || (status & I2C_STAT_NACK)) { + i2c_error = 1; + goto rd_exit; + } + if (status & I2C_STAT_RRDY) { + *buffer++ = readb(&i2c_base->data); + writew(I2C_STAT_RRDY, &i2c_base->stat); + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + +rd_exit: + flush_fifo(adap); + writew(0xFFFF, &i2c_base->stat); + return i2c_error; +} + +/* i2c_write: Address (reg offset) may be 0, 1 or 2 bytes long. */ +static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int i; + u16 status; + int i2c_error = 0; + int timeout = I2C_TIMEOUT; + + if (alen < 0) { + puts("I2C write: addr len < 0\n"); + return 1; + } + + if (len < 0) { + puts("I2C write: data len < 0\n"); + return 1; + } + + if (buffer == NULL) { + puts("I2C write: NULL pointer passed\n"); + return 1; + } + + if (alen > 2) { + printf("I2C write: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > (1 << 16)) { + printf("I2C write: address 0x%x + 0x%x out of range\n", + addr, len); + return 1; + } + + /* Wait until bus not busy */ + if (wait_for_bb(adap)) + return 1; + + /* Start address phase - will write regoffset + len bytes data */ + writew(alen + len, &i2c_base->cnt); + /* Set slave address */ + writew(chip, &i2c_base->sa); + /* Stop bit needed here */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); + + while (alen) { + /* Must write reg offset (one or two bytes) */ + status = wait_for_event(adap); + /* Try to identify bus that is not padconf'd for I2C */ + if (status == I2C_STAT_XRDY) { + i2c_error = 2; + printf("i2c_write: pads on bus %d probably not configured (status=0x%x)\n", + adap->hwadapnr, status); + goto wr_exit; + } + if (status == 0 || (status & I2C_STAT_NACK)) { + i2c_error = 1; + printf("i2c_write: error waiting for addr ACK (status=0x%x)\n", + status); + goto wr_exit; + } + if (status & I2C_STAT_XRDY) { + alen--; + writeb((addr >> (8 * alen)) & 0xff, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } else { + i2c_error = 1; + printf("i2c_write: bus not ready for addr Tx (status=0x%x)\n", + status); + goto wr_exit; + } + } + /* Address phase is over, now write data */ + for (i = 0; i < len; i++) { + status = wait_for_event(adap); + if (status == 0 || (status & I2C_STAT_NACK)) { + i2c_error = 1; + printf("i2c_write: error waiting for data ACK (status=0x%x)\n", + status); + goto wr_exit; + } + if (status & I2C_STAT_XRDY) { + writeb(buffer[i], &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } else { + i2c_error = 1; + printf("i2c_write: bus not ready for data Tx (i=%d)\n", + i); + goto wr_exit; + } + } + /* + * poll ARDY bit for making sure that last byte really has been + * transferred on the bus. + */ + do { + status = wait_for_event(adap); + } while (!(status & I2C_STAT_ARDY) && timeout--); + if (timeout <= 0) + printf("i2c_write: timed out writig last byte!\n"); + +wr_exit: + flush_fifo(adap); + writew(0xFFFF, &i2c_base->stat); + return i2c_error; +} + +/* + * Wait for the bus to be free by checking the Bus Busy (BB) + * bit to become clear + */ +static int wait_for_bb(struct i2c_adapter *adap) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int timeout = I2C_TIMEOUT; + u16 stat; + + writew(0xFFFF, &i2c_base->stat); /* clear current interrupts...*/ +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) + while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) { +#else + /* Read RAW status */ + while ((stat = readw(&i2c_base->irqstatus_raw) & + I2C_STAT_BB) && timeout--) { +#endif + writew(stat, &i2c_base->stat); + udelay(adap->waitdelay); + } + + if (timeout <= 0) { + printf("Timed out in wait_for_bb: status=%04x\n", + stat); + return 1; + } + writew(0xFFFF, &i2c_base->stat); /* clear delayed stuff*/ + return 0; +} + +/* + * Wait for the I2C controller to complete current action + * and update status + */ +static u16 wait_for_event(struct i2c_adapter *adap) +{ + struct i2c *i2c_base = omap24_get_base(adap); + u16 status; + int timeout = I2C_TIMEOUT; + + do { + udelay(adap->waitdelay); +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) + status = readw(&i2c_base->stat); +#else + /* Read RAW status */ + status = readw(&i2c_base->irqstatus_raw); +#endif + } while (!(status & + (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY | + I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK | + I2C_STAT_AL)) && timeout--); + + if (timeout <= 0) { + printf("Timed out in wait_for_event: status=%04x\n", + status); + /* + * If status is still 0 here, probably the bus pads have + * not been configured for I2C, and/or pull-ups are missing. + */ + printf("Check if pads/pull-ups of bus %d are properly configured\n", + adap->hwadapnr); + writew(0xFFFF, &i2c_base->stat); + status = 0; + } + + return status; +} + +static struct i2c *omap24_get_base(struct i2c_adapter *adap) +{ + switch (adap->hwadapnr) { + case 0: + return (struct i2c *)I2C_BASE1; + break; + case 1: + return (struct i2c *)I2C_BASE2; + break; +#if (I2C_BUS_MAX > 2) + case 2: + return (struct i2c *)I2C_BASE3; + break; +#if (I2C_BUS_MAX > 3) + case 3: + return (struct i2c *)I2C_BASE4; + break; +#if (I2C_BUS_MAX > 4) + case 4: + return (struct i2c *)I2C_BASE5; + break; +#endif +#endif +#endif + default: + printf("wrong hwadapnr: %d\n", adap->hwadapnr); + break; + } + return NULL; +} + +#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED1) +#define CONFIG_SYS_OMAP24_I2C_SPEED1 CONFIG_SYS_OMAP24_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE1) +#define CONFIG_SYS_OMAP24_I2C_SLAVE1 CONFIG_SYS_OMAP24_I2C_SLAVE +#endif + +U_BOOT_I2C_ADAP_COMPLETE(omap24_0, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, omap24_i2c_setspeed, + CONFIG_SYS_OMAP24_I2C_SPEED, + CONFIG_SYS_OMAP24_I2C_SLAVE, + 0) +U_BOOT_I2C_ADAP_COMPLETE(omap24_1, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, omap24_i2c_setspeed, + CONFIG_SYS_OMAP24_I2C_SPEED1, + CONFIG_SYS_OMAP24_I2C_SLAVE1, + 1) +#if (I2C_BUS_MAX > 2) +#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED2) +#define CONFIG_SYS_OMAP24_I2C_SPEED2 CONFIG_SYS_OMAP24_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE2) +#define CONFIG_SYS_OMAP24_I2C_SLAVE2 CONFIG_SYS_OMAP24_I2C_SLAVE +#endif + +U_BOOT_I2C_ADAP_COMPLETE(omap24_2, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, NULL, + CONFIG_SYS_OMAP24_I2C_SPEED2, + CONFIG_SYS_OMAP24_I2C_SLAVE2, + 2) +#if (I2C_BUS_MAX > 3) +#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED3) +#define CONFIG_SYS_OMAP24_I2C_SPEED3 CONFIG_SYS_OMAP24_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE3) +#define CONFIG_SYS_OMAP24_I2C_SLAVE3 CONFIG_SYS_OMAP24_I2C_SLAVE +#endif + +U_BOOT_I2C_ADAP_COMPLETE(omap24_3, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, NULL, + CONFIG_SYS_OMAP24_I2C_SPEED3, + CONFIG_SYS_OMAP24_I2C_SLAVE3, + 3) +#if (I2C_BUS_MAX > 4) +#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED4) +#define CONFIG_SYS_OMAP24_I2C_SPEED4 CONFIG_SYS_OMAP24_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE4) +#define CONFIG_SYS_OMAP24_I2C_SLAVE4 CONFIG_SYS_OMAP24_I2C_SLAVE +#endif + +U_BOOT_I2C_ADAP_COMPLETE(omap24_4, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, NULL, + CONFIG_SYS_OMAP24_I2C_SPEED4, + CONFIG_SYS_OMAP24_I2C_SLAVE4, + 4) +#endif +#endif +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.h b/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.h new file mode 100644 index 000000000..3dae295e5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.h @@ -0,0 +1,154 @@ +/* + * (C) Copyright 2004-2010 + * Texas Instruments, + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _OMAP2PLUS_I2C_H_ +#define _OMAP2PLUS_I2C_H_ + +/* I2C masks */ + +/* I2C Interrupt Enable Register (I2C_IE): */ +#define I2C_IE_GC_IE (1 << 5) +#define I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt enable */ +#define I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt enable */ +#define I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt enable */ +#define I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Status Register (I2C_STAT): */ + +#define I2C_STAT_SBD (1 << 15) /* Single byte data */ +#define I2C_STAT_BB (1 << 12) /* Bus busy */ +#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */ +#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ +#define I2C_STAT_AAS (1 << 9) /* Address as slave */ +#define I2C_STAT_GC (1 << 5) +#define I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ +#define I2C_STAT_RRDY (1 << 3) /* Receive data ready */ +#define I2C_STAT_ARDY (1 << 2) /* Register access ready */ +#define I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Interrupt Code Register (I2C_INTCODE): */ + +#define I2C_INTCODE_MASK 7 +#define I2C_INTCODE_NONE 0 +#define I2C_INTCODE_AL 1 /* Arbitration lost */ +#define I2C_INTCODE_NAK 2 /* No acknowledgement/general call */ +#define I2C_INTCODE_ARDY 3 /* Register access ready */ +#define I2C_INTCODE_RRDY 4 /* Rcv data ready */ +#define I2C_INTCODE_XRDY 5 /* Xmit data ready */ + +/* I2C Buffer Configuration Register (I2C_BUF): */ + +#define I2C_BUF_RDMA_EN (1 << 15) /* Receive DMA channel enable */ +#define I2C_BUF_XDMA_EN (1 << 7) /* Transmit DMA channel enable */ + +/* I2C Configuration Register (I2C_CON): */ + +#define I2C_CON_EN (1 << 15) /* I2C module enable */ +#define I2C_CON_BE (1 << 14) /* Big endian mode */ +#define I2C_CON_STB (1 << 11) /* Start byte mode (master mode only) */ +#define I2C_CON_MST (1 << 10) /* Master/slave mode */ +#define I2C_CON_TRX (1 << 9) /* Transmitter/receiver mode */ + /* (master mode only) */ +#define I2C_CON_XA (1 << 8) /* Expand address */ +#define I2C_CON_STP (1 << 1) /* Stop condition (master mode only) */ +#define I2C_CON_STT (1 << 0) /* Start condition (master mode only) */ + +/* I2C System Test Register (I2C_SYSTEST): */ + +#define I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ +#define I2C_SYSTEST_FREE (1 << 14) /* Free running mode, on brkpoint) */ +#define I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */ +#define I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */ +#define I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense input value */ +#define I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive output value */ +#define I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense input value */ +#define I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive output value */ + +/* I2C System Status Register (I2C_SYSS): */ + +#define I2C_SYSS_RDONE (1 << 0) /* Internel reset monitoring */ + +#define I2C_SCLL_SCLL 0 +#define I2C_SCLL_SCLL_M 0xFF +#define I2C_SCLL_HSSCLL 8 +#define I2C_SCLH_HSSCLL_M 0xFF +#define I2C_SCLH_SCLH 0 +#define I2C_SCLH_SCLH_M 0xFF +#define I2C_SCLH_HSSCLH 8 +#define I2C_SCLH_HSSCLH_M 0xFF + +#define OMAP_I2C_STANDARD 100000 +#define OMAP_I2C_FAST_MODE 400000 +#define OMAP_I2C_HIGH_SPEED 3400000 + +#define SYSTEM_CLOCK_12 12000000 +#define SYSTEM_CLOCK_13 13000000 +#define SYSTEM_CLOCK_192 19200000 +#define SYSTEM_CLOCK_96 96000000 + +/* Use the reference value of 96MHz if not explicitly set by the board */ +#ifndef I2C_IP_CLK +#define I2C_IP_CLK SYSTEM_CLOCK_96 +#endif + +/* + * The reference minimum clock for high speed is 19.2MHz. + * The linux 2.6.30 kernel uses this value. + * The reference minimum clock for fast mode is 9.6MHz + * The reference minimum clock for standard mode is 4MHz + * In TRM, the value of 12MHz is used. + */ +#ifndef I2C_INTERNAL_SAMPLING_CLK +#define I2C_INTERNAL_SAMPLING_CLK 19200000 +#endif + +/* + * The equation for the low and high time is + * tlow = scll + scll_trim = (sampling clock * tlow_duty) / speed + * thigh = sclh + sclh_trim = (sampling clock * (1 - tlow_duty)) / speed + * + * If the duty cycle is 50% + * + * tlow = scll + scll_trim = sampling clock / (2 * speed) + * thigh = sclh + sclh_trim = sampling clock / (2 * speed) + * + * In TRM + * scll_trim = 7 + * sclh_trim = 5 + * + * The linux 2.6.30 kernel uses + * scll_trim = 6 + * sclh_trim = 6 + * + * These are the trim values for standard and fast speed + */ +#ifndef I2C_FASTSPEED_SCLL_TRIM +#define I2C_FASTSPEED_SCLL_TRIM 6 +#endif +#ifndef I2C_FASTSPEED_SCLH_TRIM +#define I2C_FASTSPEED_SCLH_TRIM 6 +#endif + +/* These are the trim values for high speed */ +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif + +#define I2C_PSC_MAX 0x0f +#define I2C_PSC_MIN 0x00 + +#endif /* _OMAP24XX_I2C_H_ */ diff --git a/qemu/roms/u-boot/drivers/i2c/pca9564_i2c.c b/qemu/roms/u-boot/drivers/i2c/pca9564_i2c.c new file mode 100644 index 000000000..313288dd1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/pca9564_i2c.c @@ -0,0 +1,176 @@ +/* + * File: drivers/i2c/pca9564.c + * Based on: drivers/i2c/s3c44b0_i2c.c + * Author: + * + * Created: 2009-06-23 + * Description: PCA9564 i2c bridge driver + * + * Modified: + * Copyright 2009 CJSC "NII STT", http://www.niistt.ru/ + * + * Bugs: + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define PCA_STA (CONFIG_PCA9564_BASE + 0) +#define PCA_TO (CONFIG_PCA9564_BASE + 0) +#define PCA_DAT (CONFIG_PCA9564_BASE + (1 << 2)) +#define PCA_ADR (CONFIG_PCA9564_BASE + (2 << 2)) +#define PCA_CON (CONFIG_PCA9564_BASE + (3 << 2)) + +static unsigned char pca_read_reg(unsigned int reg) +{ + return readb((void *)reg); +} + +static void pca_write_reg(unsigned int reg, unsigned char value) +{ + writeb(value, (void *)reg); +} + +static int pca_wait_busy(void) +{ + unsigned int timeout = 10000; + + while (!(pca_read_reg(PCA_CON) & PCA_CON_SI) && --timeout) + udelay(1); + + if (timeout == 0) + debug("I2C timeout!\n"); + + debug("CON = 0x%02x, STA = 0x%02x\n", pca_read_reg(PCA_CON), + pca_read_reg(PCA_STA)); + + return timeout ? 0 : 1; +} + +/*=====================================================================*/ +/* Public Functions */ +/*=====================================================================*/ + +/*----------------------------------------------------------------------- + * Initialization + */ +void i2c_init(int speed, int slaveaddr) +{ + pca_write_reg(PCA_CON, PCA_CON_ENSIO | speed); +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ + +int i2c_probe(uchar chip) +{ + unsigned char res; + + pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO); + pca_wait_busy(); + + pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, (chip << 1) | 1); + res = pca_wait_busy(); + + if ((res == 0) && (pca_read_reg(PCA_STA) == 0x48)) + res = 1; + + pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO); + + return res; +} + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int i; + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA); + pca_wait_busy(); + + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, (chip << 1)); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + if (alen > 0) { + pca_write_reg(PCA_DAT, addr); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + } + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO); + + udelay(500); + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, (chip << 1) | 1); + pca_wait_busy(); + + for (i = 0; i < len; ++i) { + if (i == len - 1) + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + else + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_AA); + + pca_wait_busy(); + buffer[i] = pca_read_reg(PCA_DAT); + + } + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO); + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int i; + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, chip << 1); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + if (alen > 0) { + pca_write_reg(PCA_DAT, addr); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + } + + for (i = 0; i < len; ++i) { + pca_write_reg(PCA_DAT, buffer[i]); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + } + + pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/i2c/ppc4xx_i2c.c b/qemu/roms/u-boot/drivers/i2c/ppc4xx_i2c.c new file mode 100644 index 000000000..e7a15ba64 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/ppc4xx_i2c.c @@ -0,0 +1,419 @@ +/* + * (C) Copyright 2007-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * based on work by Anne Sophie Harnois + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static inline struct ppc4xx_i2c *ppc4xx_get_i2c(int hwadapnr) +{ + unsigned long base; + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) + base = CONFIG_SYS_PERIPHERAL_BASE + 0x00000700 + (hwadapnr * 0x100); +#elif defined(CONFIG_440) || defined(CONFIG_405EX) +/* all remaining 440 variants */ + base = CONFIG_SYS_PERIPHERAL_BASE + 0x00000400 + (hwadapnr * 0x100); +#else +/* all 405 variants */ + base = 0xEF600500 + (hwadapnr * 0x100); +#endif + return (struct ppc4xx_i2c *)base; +} + +static void _i2c_bus_reset(struct i2c_adapter *adap) +{ + struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr); + int i; + u8 dc; + + /* Reset status register */ + /* write 1 in SCMP and IRQA to clear these fields */ + out_8(&i2c->sts, 0x0A); + + /* write 1 in IRQP IRQD LA ICT XFRA to clear these fields */ + out_8(&i2c->extsts, 0x8F); + + /* Place chip in the reset state */ + out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST); + + /* Check if bus is free */ + dc = in_8(&i2c->directcntl); + if (!DIRCTNL_FREE(dc)){ + /* Try to set bus free state */ + out_8(&i2c->directcntl, IIC_DIRCNTL_SDAC | IIC_DIRCNTL_SCC); + + /* Wait until we regain bus control */ + for (i = 0; i < 100; ++i) { + dc = in_8(&i2c->directcntl); + if (DIRCTNL_FREE(dc)) + break; + + /* Toggle SCL line */ + dc ^= IIC_DIRCNTL_SCC; + out_8(&i2c->directcntl, dc); + udelay(10); + dc ^= IIC_DIRCNTL_SCC; + out_8(&i2c->directcntl, dc); + } + } + + /* Remove reset */ + out_8(&i2c->xtcntlss, 0); +} + +static void ppc4xx_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr); + int val, divisor; + +#ifdef CONFIG_SYS_I2C_INIT_BOARD + /* + * Call board specific i2c bus reset routine before accessing the + * environment, which might be in a chip on that bus. For details + * about this problem see doc/I2C_Edge_Conditions. + */ + i2c_init_board(); +#endif + + /* Handle possible failed I2C state */ + /* FIXME: put this into i2c_init_board()? */ + _i2c_bus_reset(adap); + + /* clear lo master address */ + out_8(&i2c->lmadr, 0); + + /* clear hi master address */ + out_8(&i2c->hmadr, 0); + + /* clear lo slave address */ + out_8(&i2c->lsadr, 0); + + /* clear hi slave address */ + out_8(&i2c->hsadr, 0); + + /* Clock divide Register */ + /* set divisor according to freq_opb */ + divisor = (get_OPB_freq() - 1) / 10000000; + if (divisor == 0) + divisor = 1; + out_8(&i2c->clkdiv, divisor); + + /* no interrupts */ + out_8(&i2c->intrmsk, 0); + + /* clear transfer count */ + out_8(&i2c->xfrcnt, 0); + + /* clear extended control & stat */ + /* write 1 in SRC SRS SWC SWS to clear these fields */ + out_8(&i2c->xtcntlss, 0xF0); + + /* Mode Control Register + Flush Slave/Master data buffer */ + out_8(&i2c->mdcntl, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB); + + val = in_8(&i2c->mdcntl); + + /* Ignore General Call, slave transfers are ignored, + * disable interrupts, exit unknown bus state, enable hold + * SCL 100kHz normaly or FastMode for 400kHz and above + */ + + val |= IIC_MDCNTL_EUBS | IIC_MDCNTL_HSCL; + if (speed >= 400000) + val |= IIC_MDCNTL_FSM; + out_8(&i2c->mdcntl, val); + + /* clear control reg */ + out_8(&i2c->cntl, 0x00); +} + +/* + * This code tries to use the features of the 405GP i2c + * controller. It will transfer up to 4 bytes in one pass + * on the loop. It only does out_8((u8 *)lbz) to the buffer when it + * is possible to do out16(lhz) transfers. + * + * cmd_type is 0 for write 1 for read. + * + * addr_len can take any value from 0-255, it is only limited + * by the char, we could make it larger if needed. If it is + * 0 we skip the address write cycle. + * + * Typical case is a Write of an addr followd by a Read. The + * IBM FAQ does not cover this. On the last byte of the write + * we don't set the creg CHT bit, and on the first bytes of the + * read we set the RPST bit. + * + * It does not support address only transfers, there must be + * a data part. If you want to write the address yourself, put + * it in the data pointer. + * + * It does not support transfer to/from address 0. + * + * It does not check XFRCNT. + */ +static int _i2c_transfer(struct i2c_adapter *adap, + unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) +{ + struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr); + u8 *ptr; + int reading; + int tran, cnt; + int result; + int status; + int i; + u8 creg; + + if (data == 0 || data_len == 0) { + /* Don't support data transfer of no length or to address 0 */ + printf( "i2c_transfer: bad call\n" ); + return IIC_NOK; + } + if (addr && addr_len) { + ptr = addr; + cnt = addr_len; + reading = 0; + } else { + ptr = data; + cnt = data_len; + reading = cmd_type; + } + + /* Clear Stop Complete Bit */ + out_8(&i2c->sts, IIC_STS_SCMP); + + /* Check init */ + i = 10; + do { + /* Get status */ + status = in_8(&i2c->sts); + i--; + } while ((status & IIC_STS_PT) && (i > 0)); + + if (status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + return(result); + } + + /* flush the Master/Slave Databuffers */ + out_8(&i2c->mdcntl, in_8(&i2c->mdcntl) | + IIC_MDCNTL_FMDB | IIC_MDCNTL_FSDB); + + /* need to wait 4 OPB clocks? code below should take that long */ + + /* 7-bit adressing */ + out_8(&i2c->hmadr, 0); + out_8(&i2c->lmadr, chip); + + tran = 0; + result = IIC_OK; + creg = 0; + + while (tran != cnt && (result == IIC_OK)) { + int bc,j; + + /* + * Control register = + * Normal transfer, 7-bits adressing, Transfer up to + * bc bytes, Normal start, Transfer is a sequence of transfers + */ + creg |= IIC_CNTL_PT; + + bc = (cnt - tran) > 4 ? 4 : cnt - tran; + creg |= (bc - 1) << 4; + /* if the real cmd type is write continue trans */ + if ((!cmd_type && (ptr == addr)) || ((tran + bc) != cnt)) + creg |= IIC_CNTL_CHT; + + if (reading) { + creg |= IIC_CNTL_READ; + } else { + for(j = 0; j < bc; j++) { + /* Set buffer */ + out_8(&i2c->mdbuf, ptr[tran + j]); + } + } + out_8(&i2c->cntl, creg); + + /* + * Transfer is in progress + * we have to wait for upto 5 bytes of data + * 1 byte chip address+r/w bit then bc bytes + * of data. + * udelay(10) is 1 bit time at 100khz + * Doubled for slop. 20 is too small. + */ + i = 2 * 5 * 8; + do { + /* Get status */ + status = in_8(&i2c->sts); + udelay(10); + i--; + } while ((status & IIC_STS_PT) && !(status & IIC_STS_ERR) && + (i > 0)); + + if (status & IIC_STS_ERR) { + result = IIC_NOK; + status = in_8(&i2c->extsts); + /* Lost arbitration? */ + if (status & IIC_EXTSTS_LA) + result = IIC_NOK_LA; + /* Incomplete transfer? */ + if (status & IIC_EXTSTS_ICT) + result = IIC_NOK_ICT; + /* Transfer aborted? */ + if (status & IIC_EXTSTS_XFRA) + result = IIC_NOK_XFRA; + } else if ( status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + } + + /* Command is reading => get buffer */ + if ((reading) && (result == IIC_OK)) { + /* Are there data in buffer */ + if (status & IIC_STS_MDBS) { + /* + * even if we have data we have to wait 4OPB + * clocks for it to hit the front of the FIFO, + * after that we can just read. We should check + * XFCNT here and if the FIFO is full there is + * no need to wait. + */ + udelay(1); + for (j = 0; j < bc; j++) + ptr[tran + j] = in_8(&i2c->mdbuf); + } else + result = IIC_NOK_DATA; + } + creg = 0; + tran += bc; + if (ptr == addr && tran == cnt) { + ptr = data; + cnt = data_len; + tran = 0; + reading = cmd_type; + if (reading) + creg = IIC_CNTL_RPST; + } + } + return result; +} + +static int ppc4xx_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + uchar buf[1]; + + buf[0] = 0; + + /* + * What is needed is to send the chip address and verify that the + * address was ed (i.e. there was a chip at that address which + * drove the data line low). + */ + return (_i2c_transfer(adap, 1, chip << 1, 0, 0, buf, 1) != 0); +} + +static int ppc4xx_i2c_transfer(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len, int read) +{ + uchar xaddr[4]; + int ret; + + if (alen > 4) { + printf("I2C: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + ret = _i2c_transfer(adap, read, chip << 1, &xaddr[4 - alen], alen, + buffer, len); + if (ret) { + printf("I2C %s: failed %d\n", read ? "read" : "write", ret); + return 1; + } + + return 0; +} + +static int ppc4xx_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + return ppc4xx_i2c_transfer(adap, chip, addr, alen, buffer, len, 1); +} + +static int ppc4xx_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + return ppc4xx_i2c_transfer(adap, chip, addr, alen, buffer, len, 0); +} + +static unsigned int ppc4xx_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + if (speed != adap->speed) + return -1; + return speed; +} + +/* + * Register ppc4xx i2c adapters + */ +#ifdef CONFIG_SYS_I2C_PPC4XX_CH0 +U_BOOT_I2C_ADAP_COMPLETE(ppc4xx_0, ppc4xx_i2c_init, ppc4xx_i2c_probe, + ppc4xx_i2c_read, ppc4xx_i2c_write, + ppc4xx_i2c_set_bus_speed, + CONFIG_SYS_I2C_PPC4XX_SPEED_0, + CONFIG_SYS_I2C_PPC4XX_SLAVE_0, 0) +#endif +#ifdef CONFIG_SYS_I2C_PPC4XX_CH1 +U_BOOT_I2C_ADAP_COMPLETE(ppc4xx_1, ppc4xx_i2c_init, ppc4xx_i2c_probe, + ppc4xx_i2c_read, ppc4xx_i2c_write, + ppc4xx_i2c_set_bus_speed, + CONFIG_SYS_I2C_PPC4XX_SPEED_1, + CONFIG_SYS_I2C_PPC4XX_SLAVE_1, 1) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/rcar_i2c.c b/qemu/roms/u-boot/drivers/i2c/rcar_i2c.c new file mode 100644 index 000000000..50cebd622 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/rcar_i2c.c @@ -0,0 +1,290 @@ +/* + * drivers/i2c/rcar_i2c.c + * + * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2013 Nobuhiro Iwamatsu + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct rcar_i2c { + u32 icscr; + u32 icmcr; + u32 icssr; + u32 icmsr; + u32 icsier; + u32 icmier; + u32 icccr; + u32 icsar; + u32 icmar; + u32 icrxdtxd; + u32 icccr2; + u32 icmpr; + u32 ichpr; + u32 iclpr; +}; + +#define MCR_MDBS 0x80 /* non-fifo mode switch */ +#define MCR_FSCL 0x40 /* override SCL pin */ +#define MCR_FSDA 0x20 /* override SDA pin */ +#define MCR_OBPC 0x10 /* override pins */ +#define MCR_MIE 0x08 /* master if enable */ +#define MCR_TSBE 0x04 +#define MCR_FSB 0x02 /* force stop bit */ +#define MCR_ESG 0x01 /* en startbit gen. */ + +#define MSR_MASK 0x7f +#define MSR_MNR 0x40 /* nack received */ +#define MSR_MAL 0x20 /* arbitration lost */ +#define MSR_MST 0x10 /* sent a stop */ +#define MSR_MDE 0x08 +#define MSR_MDT 0x04 +#define MSR_MDR 0x02 +#define MSR_MAT 0x01 /* slave addr xfer done */ + +static const struct rcar_i2c *i2c_dev[CONFIF_SYS_RCAR_I2C_NUM_CONTROLLERS] = { + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C0_BASE, + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C1_BASE, + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C2_BASE, + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C3_BASE, +}; + +static void rcar_i2c_raw_rw_common(struct rcar_i2c *dev, u8 chip, uint addr) +{ + /* set slave address */ + writel(chip << 1, &dev->icmar); + /* set register address */ + writel(addr, &dev->icrxdtxd); + /* clear status */ + writel(0, &dev->icmsr); + /* start master send */ + writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr); + + while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDE)) + != (MSR_MAT | MSR_MDE)) + udelay(10); + + /* clear ESG */ + writel(MCR_MDBS | MCR_MIE, &dev->icmcr); + /* start SCLclk */ + writel(~(MSR_MAT | MSR_MDE), &dev->icmsr); + + while (!(readl(&dev->icmsr) & MSR_MDE)) + udelay(10); +} + +static void rcar_i2c_raw_rw_finish(struct rcar_i2c *dev) +{ + while (!(readl(&dev->icmsr) & MSR_MST)) + udelay(10); + + writel(0, &dev->icmcr); +} + +static int +rcar_i2c_raw_write(struct rcar_i2c *dev, u8 chip, uint addr, u8 *val, int size) +{ + rcar_i2c_raw_rw_common(dev, chip, addr); + + /* set send date */ + writel(*val, &dev->icrxdtxd); + /* start SCLclk */ + writel(~MSR_MDE, &dev->icmsr); + + while (!(readl(&dev->icmsr) & MSR_MDE)) + udelay(10); + + /* set stop condition */ + writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr); + /* start SCLclk */ + writel(~MSR_MDE, &dev->icmsr); + + rcar_i2c_raw_rw_finish(dev); + + return 0; +} + +static u8 +rcar_i2c_raw_read(struct rcar_i2c *dev, u8 chip, uint addr) +{ + u8 ret; + + rcar_i2c_raw_rw_common(dev, chip, addr); + + /* set slave address, receive */ + writel((chip << 1) | 1, &dev->icmar); + /* clear status */ + writel(0, &dev->icmsr); + /* start master receive */ + writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr); + + while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDR)) + != (MSR_MAT | MSR_MDR)) + udelay(10); + + /* clear ESG */ + writel(MCR_MDBS | MCR_MIE, &dev->icmcr); + /* prepare stop condition */ + writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr); + /* start SCLclk */ + writel(~(MSR_MAT | MSR_MDR), &dev->icmsr); + + while (!(readl(&dev->icmsr) & MSR_MDR)) + udelay(10); + + /* get receive data */ + ret = (u8)readl(&dev->icrxdtxd); + /* start SCLclk */ + writel(~MSR_MDR, &dev->icmsr); + + rcar_i2c_raw_rw_finish(dev); + + return ret; +} + +/* + * SCL = iicck / (20 + SCGD * 8 + F[(ticf + tr + intd) * iicck]) + * iicck : I2C internal clock < 20 MHz + * ticf : I2C SCL falling time: 35 ns + * tr : I2C SCL rising time: 200 ns + * intd : LSI internal delay: I2C0: 50 ns I2C1-3: 5 + * F[n] : n rounded up to an integer + */ +static u32 rcar_clock_gen(int i2c_no, u32 bus_speed) +{ + u32 iicck, f, scl, scgd; + u32 intd = 5; + + int bit = 0, cdf_width = 3; + for (bit = 0; bit < (1 << cdf_width); bit++) { + iicck = CONFIG_HP_CLK_FREQ / (1 + bit); + if (iicck < 20000000) + break; + } + + if (bit > (1 << cdf_width)) { + puts("rcar-i2c: Can not get CDF\n"); + return 0; + } + + if (i2c_no == 0) + intd = 50; + + f = (35 + 200 + intd) * (iicck / 1000000000); + + for (scgd = 0; scgd < 0x40; scgd++) { + scl = iicck / (20 + (scgd * 8) + f); + if (scl <= bus_speed) + break; + } + + if (scgd > 0x40) { + puts("rcar-i2c: Can not get SDGB\n"); + return 0; + } + + debug("%s: scl: %d\n", __func__, scl); + debug("%s: bit %x\n", __func__, bit); + debug("%s: scgd %x\n", __func__, scgd); + debug("%s: iccr %x\n", __func__, (scgd << (cdf_width) | bit)); + + return scgd << (cdf_width) | bit; +} + +static void +rcar_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + u32 icccr = 0; + + /* No i2c support prior to relocation */ + if (!(gd->flags & GD_FLG_RELOC)) + return; + + /* + * reset slave mode. + * slave mode is not used on this driver + */ + writel(0, &dev->icsier); + writel(0, &dev->icsar); + writel(0, &dev->icscr); + writel(0, &dev->icssr); + + /* reset master mode */ + writel(0, &dev->icmier); + writel(0, &dev->icmcr); + writel(0, &dev->icmsr); + writel(0, &dev->icmar); + + icccr = rcar_clock_gen(adap->hwadapnr, adap->speed); + if (icccr == 0) + puts("I2C: Init failed\n"); + else + writel(icccr, &dev->icccr); +} + +static int rcar_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, u8 *data, int len) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + int i; + + for (i = 0; i < len; i++) + data[i] = rcar_i2c_raw_read(dev, chip, addr + i); + + return 0; +} + +static int rcar_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr, + int alen, u8 *data, int len) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + return rcar_i2c_raw_write(dev, chip, addr, data, len); +} + +static int +rcar_i2c_probe(struct i2c_adapter *adap, u8 dev) +{ + return rcar_i2c_read(adap, dev, 0, 0, NULL, 0); +} + +static unsigned int rcar_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + u32 icccr; + int ret = 0; + + rcar_i2c_raw_rw_finish(dev); + + icccr = rcar_clock_gen(adap->hwadapnr, speed); + if (icccr == 0) { + puts("I2C: Init failed\n"); + ret = -1; + } else { + writel(icccr, &dev->icccr); + } + return ret; +} + +/* + * Register RCAR i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(rcar_0, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C0_SPEED, 0, 0) +U_BOOT_I2C_ADAP_COMPLETE(rcar_1, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C1_SPEED, 0, 1) +U_BOOT_I2C_ADAP_COMPLETE(rcar_2, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C2_SPEED, 0, 2) +U_BOOT_I2C_ADAP_COMPLETE(rcar_3, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C3_SPEED, 0, 3) diff --git a/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.c b/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.c new file mode 100644 index 000000000..fd328f054 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.c @@ -0,0 +1,1249 @@ +/* + * (C) Copyright 2002 + * David Mueller, ELSOFT AG, d.mueller@elsoft.ch + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* This code should work for both the S3C2400 and the S3C2410 + * as they seem to have the same I2C controller inside. + * The different address mapping is handled by the s3c24xx.h files below. + */ + +#include +#include +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) +#include +#include +#include +#else +#include +#endif +#include +#include +#include "s3c24x0_i2c.h" + +#define I2C_WRITE 0 +#define I2C_READ 1 + +#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */ + +/* HSI2C specific register description */ + +/* I2C_CTL Register bits */ +#define HSI2C_FUNC_MODE_I2C (1u << 0) +#define HSI2C_MASTER (1u << 3) +#define HSI2C_RXCHON (1u << 6) /* Write/Send */ +#define HSI2C_TXCHON (1u << 7) /* Read/Receive */ +#define HSI2C_SW_RST (1u << 31) + +/* I2C_FIFO_CTL Register bits */ +#define HSI2C_RXFIFO_EN (1u << 0) +#define HSI2C_TXFIFO_EN (1u << 1) +#define HSI2C_TXFIFO_TRIGGER_LEVEL (0x20 << 16) +#define HSI2C_RXFIFO_TRIGGER_LEVEL (0x20 << 4) + +/* I2C_TRAILING_CTL Register bits */ +#define HSI2C_TRAILING_COUNT (0xff) + +/* I2C_INT_EN Register bits */ +#define HSI2C_TX_UNDERRUN_EN (1u << 2) +#define HSI2C_TX_OVERRUN_EN (1u << 3) +#define HSI2C_RX_UNDERRUN_EN (1u << 4) +#define HSI2C_RX_OVERRUN_EN (1u << 5) +#define HSI2C_INT_TRAILING_EN (1u << 6) +#define HSI2C_INT_I2C_EN (1u << 9) + +#define HSI2C_INT_ERROR_MASK (HSI2C_TX_UNDERRUN_EN |\ + HSI2C_TX_OVERRUN_EN |\ + HSI2C_RX_UNDERRUN_EN |\ + HSI2C_RX_OVERRUN_EN |\ + HSI2C_INT_TRAILING_EN) + +/* I2C_CONF Register bits */ +#define HSI2C_AUTO_MODE (1u << 31) +#define HSI2C_10BIT_ADDR_MODE (1u << 30) +#define HSI2C_HS_MODE (1u << 29) + +/* I2C_AUTO_CONF Register bits */ +#define HSI2C_READ_WRITE (1u << 16) +#define HSI2C_STOP_AFTER_TRANS (1u << 17) +#define HSI2C_MASTER_RUN (1u << 31) + +/* I2C_TIMEOUT Register bits */ +#define HSI2C_TIMEOUT_EN (1u << 31) + +/* I2C_TRANS_STATUS register bits */ +#define HSI2C_MASTER_BUSY (1u << 17) +#define HSI2C_SLAVE_BUSY (1u << 16) +#define HSI2C_TIMEOUT_AUTO (1u << 4) +#define HSI2C_NO_DEV (1u << 3) +#define HSI2C_NO_DEV_ACK (1u << 2) +#define HSI2C_TRANS_ABORT (1u << 1) +#define HSI2C_TRANS_SUCCESS (1u << 0) +#define HSI2C_TRANS_ERROR_MASK (HSI2C_TIMEOUT_AUTO |\ + HSI2C_NO_DEV | HSI2C_NO_DEV_ACK |\ + HSI2C_TRANS_ABORT) +#define HSI2C_TRANS_FINISHED_MASK (HSI2C_TRANS_ERROR_MASK | HSI2C_TRANS_SUCCESS) + + +/* I2C_FIFO_STAT Register bits */ +#define HSI2C_RX_FIFO_EMPTY (1u << 24) +#define HSI2C_RX_FIFO_FULL (1u << 23) +#define HSI2C_TX_FIFO_EMPTY (1u << 8) +#define HSI2C_TX_FIFO_FULL (1u << 7) +#define HSI2C_RX_FIFO_LEVEL(x) (((x) >> 16) & 0x7f) +#define HSI2C_TX_FIFO_LEVEL(x) ((x) & 0x7f) + +#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10) + +/* S3C I2C Controller bits */ +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ + +#define I2C_TIMEOUT_MS 1000 /* 1 second */ + +#define HSI2C_TIMEOUT_US 100000 /* 100 ms, finer granularity */ + + +/* To support VCMA9 boards and other who dont define max_i2c_num */ +#ifndef CONFIG_MAX_I2C_NUM +#define CONFIG_MAX_I2C_NUM 1 +#endif + +/* + * For SPL boot some boards need i2c before SDRAM is initialised so force + * variables to live in SRAM + */ +static struct s3c24x0_i2c_bus i2c_bus[CONFIG_MAX_I2C_NUM] + __attribute__((section(".data"))); + +/** + * Get a pointer to the given bus index + * + * @bus_idx: Bus index to look up + * @return pointer to bus, or NULL if invalid or not available + */ +static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx) +{ + if (bus_idx < ARRAY_SIZE(i2c_bus)) { + struct s3c24x0_i2c_bus *bus; + + bus = &i2c_bus[bus_idx]; + if (bus->active) + return bus; + } + + debug("Undefined bus: %d\n", bus_idx); + return NULL; +} + +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) +static int GetI2CSDA(void) +{ + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + +#ifdef CONFIG_S3C2410 + return (readl(&gpio->gpedat) & 0x8000) >> 15; +#endif +#ifdef CONFIG_S3C2400 + return (readl(&gpio->pgdat) & 0x0020) >> 5; +#endif +} + +static void SetI2CSCL(int x) +{ + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + +#ifdef CONFIG_S3C2410 + writel((readl(&gpio->gpedat) & ~0x4000) | + (x & 1) << 14, &gpio->gpedat); +#endif +#ifdef CONFIG_S3C2400 + writel((readl(&gpio->pgdat) & ~0x0040) | (x & 1) << 6, &gpio->pgdat); +#endif +} +#endif + +/* + * Wait til the byte transfer is completed. + * + * @param i2c- pointer to the appropriate i2c register bank. + * @return I2C_OK, if transmission was ACKED + * I2C_NACK, if transmission was NACKED + * I2C_NOK_TIMEOUT, if transaction did not complete in I2C_TIMEOUT_MS + */ + +static int WaitForXfer(struct s3c24x0_i2c *i2c) +{ + ulong start_time = get_timer(0); + + do { + if (readl(&i2c->iiccon) & I2CCON_IRPND) + return (readl(&i2c->iicstat) & I2CSTAT_NACK) ? + I2C_NACK : I2C_OK; + } while (get_timer(start_time) < I2C_TIMEOUT_MS); + + return I2C_NOK_TOUT; +} + +/* + * Wait for transfer completion. + * + * This function reads the interrupt status register waiting for the INT_I2C + * bit to be set, which indicates copletion of a transaction. + * + * @param i2c: pointer to the appropriate register bank + * + * @return: I2C_OK in case of successful completion, I2C_NOK_TIMEOUT in case + * the status bits do not get set in time, or an approrpiate error + * value in case of transfer errors. + */ +static int hsi2c_wait_for_trx(struct exynos5_hsi2c *i2c) +{ + int i = HSI2C_TIMEOUT_US; + + while (i-- > 0) { + u32 int_status = readl(&i2c->usi_int_stat); + + if (int_status & HSI2C_INT_I2C_EN) { + u32 trans_status = readl(&i2c->usi_trans_status); + + /* Deassert pending interrupt. */ + writel(int_status, &i2c->usi_int_stat); + + if (trans_status & HSI2C_NO_DEV_ACK) { + debug("%s: no ACK from device\n", __func__); + return I2C_NACK; + } + if (trans_status & HSI2C_NO_DEV) { + debug("%s: no device\n", __func__); + return I2C_NOK; + } + if (trans_status & HSI2C_TRANS_ABORT) { + debug("%s: arbitration lost\n", __func__); + return I2C_NOK_LA; + } + if (trans_status & HSI2C_TIMEOUT_AUTO) { + debug("%s: device timed out\n", __func__); + return I2C_NOK_TOUT; + } + return I2C_OK; + } + udelay(1); + } + debug("%s: transaction timeout!\n", __func__); + return I2C_NOK_TOUT; +} + +static void ReadWriteByte(struct s3c24x0_i2c *i2c) +{ + writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); +} + +static struct s3c24x0_i2c *get_base_i2c(int bus) +{ +#ifdef CONFIG_EXYNOS4 + struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c() + + (EXYNOS4_I2C_SPACING + * bus)); + return i2c; +#elif defined CONFIG_EXYNOS5 + struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c() + + (EXYNOS5_I2C_SPACING + * bus)); + return i2c; +#else + return s3c24x0_get_base_i2c(); +#endif +} + +static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) +{ + ulong freq, pres = 16, div; +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) + freq = get_i2c_clk(); +#else + freq = get_PCLK(); +#endif + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + div = 0; + while ((freq / pres / (div + 1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon); + + /* init to SLAVE REVEIVE and set slaveaddr */ + writel(0, &i2c->iicstat); + writel(slaveadd, &i2c->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); +} + +static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; + ulong clkin; + unsigned int op_clk = i2c_bus->clock_frequency; + unsigned int i = 0, utemp0 = 0, utemp1 = 0; + unsigned int t_ftl_cycle; + +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) + clkin = get_i2c_clk(); +#else + clkin = get_PCLK(); +#endif + /* FPCLK / FI2C = + * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE + * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + * uTemp1 = (TSCLK_L + TSCLK_H + 2) + * uTemp2 = TSCLK_L + TSCLK_H + */ + t_ftl_cycle = (readl(&hsregs->usi_conf) >> 16) & 0x7; + utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle; + + /* CLK_DIV max is 256 */ + for (i = 0; i < 256; i++) { + utemp1 = utemp0 / (i + 1); + if ((utemp1 < 512) && (utemp1 > 4)) { + i2c_bus->clk_cycle = utemp1 - 2; + i2c_bus->clk_div = i; + return 0; + } + } + return -1; +} + +static void hsi2c_ch_init(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; + unsigned int t_sr_release; + unsigned int n_clkdiv; + unsigned int t_start_su, t_start_hd; + unsigned int t_stop_su; + unsigned int t_data_su, t_data_hd; + unsigned int t_scl_l, t_scl_h; + u32 i2c_timing_s1; + u32 i2c_timing_s2; + u32 i2c_timing_s3; + u32 i2c_timing_sla; + + n_clkdiv = i2c_bus->clk_div; + t_scl_l = i2c_bus->clk_cycle / 2; + t_scl_h = i2c_bus->clk_cycle / 2; + t_start_su = t_scl_l; + t_start_hd = t_scl_l; + t_stop_su = t_scl_l; + t_data_su = t_scl_l / 2; + t_data_hd = t_scl_l / 2; + t_sr_release = i2c_bus->clk_cycle; + + i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8; + i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0; + i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0; + i2c_timing_sla = t_data_hd << 0; + + writel(HSI2C_TRAILING_COUNT, &hsregs->usi_trailing_ctl); + + /* Clear to enable Timeout */ + clrsetbits_le32(&hsregs->usi_timeout, HSI2C_TIMEOUT_EN, 0); + + /* set AUTO mode */ + writel(readl(&hsregs->usi_conf) | HSI2C_AUTO_MODE, &hsregs->usi_conf); + + /* Enable completion conditions' reporting. */ + writel(HSI2C_INT_I2C_EN, &hsregs->usi_int_en); + + /* Enable FIFOs */ + writel(HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN, &hsregs->usi_fifo_ctl); + + /* Currently operating in Fast speed mode. */ + writel(i2c_timing_s1, &hsregs->usi_timing_fs1); + writel(i2c_timing_s2, &hsregs->usi_timing_fs2); + writel(i2c_timing_s3, &hsregs->usi_timing_fs3); + writel(i2c_timing_sla, &hsregs->usi_timing_sla); +} + +/* SW reset for the high speed bus */ +static void exynos5_i2c_reset(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *i2c = i2c_bus->hsregs; + u32 i2c_ctl; + + /* Set and clear the bit for reset */ + i2c_ctl = readl(&i2c->usi_ctl); + i2c_ctl |= HSI2C_SW_RST; + writel(i2c_ctl, &i2c->usi_ctl); + + i2c_ctl = readl(&i2c->usi_ctl); + i2c_ctl &= ~HSI2C_SW_RST; + writel(i2c_ctl, &i2c->usi_ctl); + + /* Initialize the configure registers */ + hsi2c_ch_init(i2c_bus); +} + +static void s3c24x0_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + struct s3c24x0_i2c *i2c; + struct s3c24x0_i2c_bus *bus; + +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); +#endif + ulong start_time = get_timer(0); + + /* By default i2c channel 0 is the current bus */ + i2c = get_base_i2c(adap->hwadapnr); + + /* + * In case the previous transfer is still going, wait to give it a + * chance to finish. + */ + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) { + printf("%s: I2C bus busy for %p\n", __func__, + &i2c->iicstat); + return; + } + } + +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) + int i; + + if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) { +#ifdef CONFIG_S3C2410 + ulong old_gpecon = readl(&gpio->gpecon); +#endif +#ifdef CONFIG_S3C2400 + ulong old_gpecon = readl(&gpio->pgcon); +#endif + /* bus still busy probably by (most) previously interrupted + transfer */ + +#ifdef CONFIG_S3C2410 + /* set I2CSDA and I2CSCL (GPE15, GPE14) to GPIO */ + writel((readl(&gpio->gpecon) & ~0xF0000000) | 0x10000000, + &gpio->gpecon); +#endif +#ifdef CONFIG_S3C2400 + /* set I2CSDA and I2CSCL (PG5, PG6) to GPIO */ + writel((readl(&gpio->pgcon) & ~0x00003c00) | 0x00001000, + &gpio->pgcon); +#endif + + /* toggle I2CSCL until bus idle */ + SetI2CSCL(0); + udelay(1000); + i = 10; + while ((i > 0) && (GetI2CSDA() != 1)) { + SetI2CSCL(1); + udelay(1000); + SetI2CSCL(0); + udelay(1000); + i--; + } + SetI2CSCL(1); + udelay(1000); + + /* restore pin functions */ +#ifdef CONFIG_S3C2410 + writel(old_gpecon, &gpio->gpecon); +#endif +#ifdef CONFIG_S3C2400 + writel(old_gpecon, &gpio->pgcon); +#endif + } +#endif /* #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) */ + i2c_ch_init(i2c, speed, slaveadd); + + bus = &i2c_bus[adap->hwadapnr]; + bus->active = true; + bus->regs = i2c; +} + +/* + * Poll the appropriate bit of the fifo status register until the interface is + * ready to process the next byte or timeout expires. + * + * In addition to the FIFO status register this function also polls the + * interrupt status register to be able to detect unexpected transaction + * completion. + * + * When FIFO is ready to process the next byte, this function returns I2C_OK. + * If in course of polling the INT_I2C assertion is detected, the function + * returns I2C_NOK. If timeout happens before any of the above conditions is + * met - the function returns I2C_NOK_TOUT; + + * @param i2c: pointer to the appropriate i2c register bank. + * @param rx_transfer: set to True if the receive transaction is in progress. + * @return: as described above. + */ +static unsigned hsi2c_poll_fifo(struct exynos5_hsi2c *i2c, bool rx_transfer) +{ + u32 fifo_bit = rx_transfer ? HSI2C_RX_FIFO_EMPTY : HSI2C_TX_FIFO_FULL; + int i = HSI2C_TIMEOUT_US; + + while (readl(&i2c->usi_fifo_stat) & fifo_bit) { + if (readl(&i2c->usi_int_stat) & HSI2C_INT_I2C_EN) { + /* + * There is a chance that assertion of + * HSI2C_INT_I2C_EN and deassertion of + * HSI2C_RX_FIFO_EMPTY happen simultaneously. Let's + * give FIFO status priority and check it one more + * time before reporting interrupt. The interrupt will + * be reported next time this function is called. + */ + if (rx_transfer && + !(readl(&i2c->usi_fifo_stat) & fifo_bit)) + break; + return I2C_NOK; + } + if (!i--) { + debug("%s: FIFO polling timeout!\n", __func__); + return I2C_NOK_TOUT; + } + udelay(1); + } + return I2C_OK; +} + +/* + * Preapre hsi2c transaction, either read or write. + * + * Set up transfer as described in section 27.5.1.2 'I2C Channel Auto Mode' of + * the 5420 UM. + * + * @param i2c: pointer to the appropriate i2c register bank. + * @param chip: slave address on the i2c bus (with read/write bit exlcuded) + * @param len: number of bytes expected to be sent or received + * @param rx_transfer: set to true for receive transactions + * @param: issue_stop: set to true if i2c stop condition should be generated + * after this transaction. + * @return: I2C_NOK_TOUT in case the bus remained busy for HSI2C_TIMEOUT_US, + * I2C_OK otherwise. + */ +static int hsi2c_prepare_transaction(struct exynos5_hsi2c *i2c, + u8 chip, + u16 len, + bool rx_transfer, + bool issue_stop) +{ + u32 conf; + + conf = len | HSI2C_MASTER_RUN; + + if (issue_stop) + conf |= HSI2C_STOP_AFTER_TRANS; + + /* Clear to enable Timeout */ + writel(readl(&i2c->usi_timeout) & ~HSI2C_TIMEOUT_EN, &i2c->usi_timeout); + + /* Set slave address */ + writel(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr); + + if (rx_transfer) { + /* i2c master, read transaction */ + writel((HSI2C_RXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + &i2c->usi_ctl); + + /* read up to len bytes, stop after transaction is finished */ + writel(conf | HSI2C_READ_WRITE, &i2c->usi_auto_conf); + } else { + /* i2c master, write transaction */ + writel((HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + &i2c->usi_ctl); + + /* write up to len bytes, stop after transaction is finished */ + writel(conf, &i2c->usi_auto_conf); + } + + /* Reset all pending interrupt status bits we care about, if any */ + writel(HSI2C_INT_I2C_EN, &i2c->usi_int_stat); + + return I2C_OK; +} + +/* + * Wait while i2c bus is settling down (mostly stop gets completed). + */ +static int hsi2c_wait_while_busy(struct exynos5_hsi2c *i2c) +{ + int i = HSI2C_TIMEOUT_US; + + while (readl(&i2c->usi_trans_status) & HSI2C_MASTER_BUSY) { + if (!i--) { + debug("%s: bus busy\n", __func__); + return I2C_NOK_TOUT; + } + udelay(1); + } + return I2C_OK; +} + +static int hsi2c_write(struct exynos5_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + unsigned char data[], + unsigned short len, + bool issue_stop) +{ + int i, rv = 0; + + if (!(len + alen)) { + /* Writes of zero length not supported in auto mode. */ + debug("%s: zero length writes not supported\n", __func__); + return I2C_NOK; + } + + rv = hsi2c_prepare_transaction + (i2c, chip, len + alen, false, issue_stop); + if (rv != I2C_OK) + return rv; + + /* Move address, if any, and the data, if any, into the FIFO. */ + for (i = 0; i < alen; i++) { + rv = hsi2c_poll_fifo(i2c, false); + if (rv != I2C_OK) { + debug("%s: address write failed\n", __func__); + goto write_error; + } + writel(addr[i], &i2c->usi_txdata); + } + + for (i = 0; i < len; i++) { + rv = hsi2c_poll_fifo(i2c, false); + if (rv != I2C_OK) { + debug("%s: data write failed\n", __func__); + goto write_error; + } + writel(data[i], &i2c->usi_txdata); + } + + rv = hsi2c_wait_for_trx(i2c); + + write_error: + if (issue_stop) { + int tmp_ret = hsi2c_wait_while_busy(i2c); + if (rv == I2C_OK) + rv = tmp_ret; + } + + writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); /* done */ + return rv; +} + +static int hsi2c_read(struct exynos5_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + unsigned char data[], + unsigned short len) +{ + int i, rv, tmp_ret; + bool drop_data = false; + + if (!len) { + /* Reads of zero length not supported in auto mode. */ + debug("%s: zero length read adjusted\n", __func__); + drop_data = true; + len = 1; + } + + if (alen) { + /* Internal register adress needs to be written first. */ + rv = hsi2c_write(i2c, chip, addr, alen, NULL, 0, false); + if (rv != I2C_OK) + return rv; + } + + rv = hsi2c_prepare_transaction(i2c, chip, len, true, true); + + if (rv != I2C_OK) + return rv; + + for (i = 0; i < len; i++) { + rv = hsi2c_poll_fifo(i2c, true); + if (rv != I2C_OK) + goto read_err; + if (drop_data) + continue; + data[i] = readl(&i2c->usi_rxdata); + } + + rv = hsi2c_wait_for_trx(i2c); + + read_err: + tmp_ret = hsi2c_wait_while_busy(i2c); + if (rv == I2C_OK) + rv = tmp_ret; + + writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); /* done */ + return rv; +} + +static unsigned int s3c24x0_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct s3c24x0_i2c_bus *i2c_bus; + + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -1; + + i2c_bus->clock_frequency = speed; + + if (i2c_bus->is_highspeed) { + if (hsi2c_get_clk_details(i2c_bus)) + return -1; + hsi2c_ch_init(i2c_bus); + } else { + i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, + CONFIG_SYS_I2C_S3C24X0_SLAVE); + } + + return 0; +} + +#ifdef CONFIG_EXYNOS5 +static void exynos_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + /* This will override the speed selected in the fdt for that port */ + debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); + if (i2c_set_bus_speed(speed)) + printf("i2c_init: failed to init bus %d for speed = %d\n", + adap->hwadapnr, speed); +} +#endif + +/* + * cmd_type is 0 for write, 1 for read. + * + * addr_len can take any value from 0-255, it is only limited + * by the char, we could make it larger if needed. If it is + * 0 we skip the address write cycle. + */ +static int i2c_transfer(struct s3c24x0_i2c *i2c, + unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) +{ + int i = 0, result; + ulong start_time = get_timer(0); + + if (data == 0 || data_len == 0) { + /*Don't support data transfer of no length or to address 0 */ + debug("i2c_transfer: bad call\n"); + return I2C_NOK; + } + + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) + return I2C_NOK_TOUT; + } + + writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon); + + /* Get the slave chip address going */ + writel(chip, &i2c->iicds); + if ((cmd_type == I2C_WRITE) || (addr && addr_len)) + writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + else + writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + + /* Wait for chip address to transmit. */ + result = WaitForXfer(i2c); + if (result != I2C_OK) + goto bailout; + + /* If register address needs to be transmitted - do it now. */ + if (addr && addr_len) { + while ((i < addr_len) && (result == I2C_OK)) { + writel(addr[i++], &i2c->iicds); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + } + i = 0; + if (result != I2C_OK) + goto bailout; + } + + switch (cmd_type) { + case I2C_WRITE: + while ((i < data_len) && (result == I2C_OK)) { + writel(data[i++], &i2c->iicds); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + } + break; + + case I2C_READ: + if (addr && addr_len) { + /* + * Register address has been sent, now send slave chip + * address again to start the actual read transaction. + */ + writel(chip, &i2c->iicds); + + /* Generate a re-START. */ + writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + + if (result != I2C_OK) + goto bailout; + } + + while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + writel(readl(&i2c->iiccon) + & ~I2CCON_ACKGEN, + &i2c->iiccon); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + data[i++] = readl(&i2c->iicds); + } + if (result == I2C_NACK) + result = I2C_OK; /* Normal terminated read. */ + break; + + default: + debug("i2c_transfer: bad call\n"); + result = I2C_NOK; + break; + } + +bailout: + /* Send STOP. */ + writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); + ReadWriteByte(i2c); + + return result; +} + +static int s3c24x0_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + struct s3c24x0_i2c_bus *i2c_bus; + uchar buf[1]; + int ret; + + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -1; + buf[0] = 0; + + /* + * What is needed is to send the chip address and verify that the + * address was ed (i.e. there was a chip at that address which + * drove the data line low). + */ + if (i2c_bus->is_highspeed) { + ret = hsi2c_read(i2c_bus->hsregs, + chip, 0, 0, buf, 1); + } else { + ret = i2c_transfer(i2c_bus->regs, + I2C_READ, chip << 1, 0, 0, buf, 1); + } + + return ret != I2C_OK; +} + +static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct s3c24x0_i2c_bus *i2c_bus; + uchar xaddr[4]; + int ret; + + if (alen > 4) { + debug("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -1; + + if (i2c_bus->is_highspeed) + ret = hsi2c_read(i2c_bus->hsregs, chip, &xaddr[4 - alen], + alen, buffer, len); + else + ret = i2c_transfer(i2c_bus->regs, I2C_READ, chip << 1, + &xaddr[4 - alen], alen, buffer, len); + + if (ret) { + if (i2c_bus->is_highspeed) + exynos5_i2c_reset(i2c_bus); + debug("I2c read failed %d\n", ret); + return 1; + } + return 0; +} + +static int s3c24x0_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct s3c24x0_i2c_bus *i2c_bus; + uchar xaddr[4]; + int ret; + + if (alen > 4) { + debug("I2C write: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -1; + + if (i2c_bus->is_highspeed) + ret = hsi2c_write(i2c_bus->hsregs, chip, &xaddr[4 - alen], + alen, buffer, len, true); + else + ret = i2c_transfer(i2c_bus->regs, I2C_WRITE, chip << 1, + &xaddr[4 - alen], alen, buffer, len); + + if (ret != 0) { + if (i2c_bus->is_highspeed) + exynos5_i2c_reset(i2c_bus); + return 1; + } else { + return 0; + } +} + +#ifdef CONFIG_OF_CONTROL +static void process_nodes(const void *blob, int node_list[], int count, + int is_highspeed) +{ + struct s3c24x0_i2c_bus *bus; + int i; + + for (i = 0; i < count; i++) { + int node = node_list[i]; + + if (node <= 0) + continue; + + bus = &i2c_bus[i]; + bus->active = true; + bus->is_highspeed = is_highspeed; + + if (is_highspeed) + bus->hsregs = (struct exynos5_hsi2c *) + fdtdec_get_addr(blob, node, "reg"); + else + bus->regs = (struct s3c24x0_i2c *) + fdtdec_get_addr(blob, node, "reg"); + + bus->id = pinmux_decode_periph_id(blob, node); + bus->clock_frequency = fdtdec_get_int(blob, node, + "clock-frequency", + CONFIG_SYS_I2C_S3C24X0_SPEED); + bus->node = node; + bus->bus_num = i; + exynos_pinmux_config(bus->id, 0); + + /* Mark position as used */ + node_list[i] = -1; + } +} + +void board_i2c_init(const void *blob) +{ + int node_list[CONFIG_MAX_I2C_NUM]; + int count; + + /* First get the normal i2c ports */ + count = fdtdec_find_aliases_for_id(blob, "i2c", + COMPAT_SAMSUNG_S3C2440_I2C, node_list, + CONFIG_MAX_I2C_NUM); + process_nodes(blob, node_list, count, 0); + + /* Now look for high speed i2c ports */ + count = fdtdec_find_aliases_for_id(blob, "i2c", + COMPAT_SAMSUNG_EXYNOS5_I2C, node_list, + CONFIG_MAX_I2C_NUM); + process_nodes(blob, node_list, count, 1); + +} + +int i2c_get_bus_num_fdt(int node) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(i2c_bus); i++) { + if (node == i2c_bus[i].node) + return i; + } + + debug("%s: Can't find any matched I2C bus\n", __func__); + return -1; +} + +int i2c_reset_port_fdt(const void *blob, int node) +{ + struct s3c24x0_i2c_bus *i2c_bus; + int bus; + + bus = i2c_get_bus_num_fdt(node); + if (bus < 0) { + debug("could not get bus for node %d\n", node); + return -1; + } + + i2c_bus = get_bus(bus); + if (!i2c_bus) { + debug("get_bus() failed for node node %d\n", node); + return -1; + } + + if (i2c_bus->is_highspeed) { + if (hsi2c_get_clk_details(i2c_bus)) + return -1; + hsi2c_ch_init(i2c_bus); + } else { + i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, + CONFIG_SYS_I2C_S3C24X0_SLAVE); + } + + return 0; +} +#endif + +/* + * Register s3c24x0 i2c adapters + */ +#if defined(CONFIG_EXYNOS5420) +U_BOOT_I2C_ADAP_COMPLETE(i2c00, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) +U_BOOT_I2C_ADAP_COMPLETE(i2c01, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 1) +U_BOOT_I2C_ADAP_COMPLETE(i2c02, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 2) +U_BOOT_I2C_ADAP_COMPLETE(i2c03, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 3) +U_BOOT_I2C_ADAP_COMPLETE(i2c04, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 4) +U_BOOT_I2C_ADAP_COMPLETE(i2c05, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 5) +U_BOOT_I2C_ADAP_COMPLETE(i2c06, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 6) +U_BOOT_I2C_ADAP_COMPLETE(i2c07, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 7) +U_BOOT_I2C_ADAP_COMPLETE(i2c08, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 8) +U_BOOT_I2C_ADAP_COMPLETE(i2c09, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 9) +U_BOOT_I2C_ADAP_COMPLETE(i2c10, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 10) +#elif defined(CONFIG_EXYNOS5250) +U_BOOT_I2C_ADAP_COMPLETE(i2c00, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) +U_BOOT_I2C_ADAP_COMPLETE(i2c01, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 1) +U_BOOT_I2C_ADAP_COMPLETE(i2c02, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 2) +U_BOOT_I2C_ADAP_COMPLETE(i2c03, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 3) +U_BOOT_I2C_ADAP_COMPLETE(i2c04, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 4) +U_BOOT_I2C_ADAP_COMPLETE(i2c05, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 5) +U_BOOT_I2C_ADAP_COMPLETE(i2c06, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 6) +U_BOOT_I2C_ADAP_COMPLETE(i2c07, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 7) +U_BOOT_I2C_ADAP_COMPLETE(i2c08, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 8) +U_BOOT_I2C_ADAP_COMPLETE(i2c09, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 9) +U_BOOT_I2C_ADAP_COMPLETE(s3c10, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 10) +#elif defined(CONFIG_EXYNOS4) +U_BOOT_I2C_ADAP_COMPLETE(i2c00, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) +U_BOOT_I2C_ADAP_COMPLETE(i2c01, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 1) +U_BOOT_I2C_ADAP_COMPLETE(i2c02, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 2) +U_BOOT_I2C_ADAP_COMPLETE(i2c03, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 3) +U_BOOT_I2C_ADAP_COMPLETE(i2c04, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 4) +U_BOOT_I2C_ADAP_COMPLETE(i2c05, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 5) +U_BOOT_I2C_ADAP_COMPLETE(i2c06, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 6) +U_BOOT_I2C_ADAP_COMPLETE(i2c07, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 7) +U_BOOT_I2C_ADAP_COMPLETE(i2c08, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 8) +#else +U_BOOT_I2C_ADAP_COMPLETE(s3c0, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.h b/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.h new file mode 100644 index 000000000..1ae73d277 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _S3C24X0_I2C_H +#define _S3C24X0_I2C_H + +struct s3c24x0_i2c { + u32 iiccon; + u32 iicstat; + u32 iicadd; + u32 iicds; + u32 iiclc; +}; + +struct exynos5_hsi2c { + u32 usi_ctl; + u32 usi_fifo_ctl; + u32 usi_trailing_ctl; + u32 usi_clk_ctl; + u32 usi_clk_slot; + u32 spi_ctl; + u32 uart_ctl; + u32 res1; + u32 usi_int_en; + u32 usi_int_stat; + u32 usi_modem_stat; + u32 usi_error_stat; + u32 usi_fifo_stat; + u32 usi_txdata; + u32 usi_rxdata; + u32 res2; + u32 usi_conf; + u32 usi_auto_conf; + u32 usi_timeout; + u32 usi_manual_cmd; + u32 usi_trans_status; + u32 usi_timing_hs1; + u32 usi_timing_hs2; + u32 usi_timing_hs3; + u32 usi_timing_fs1; + u32 usi_timing_fs2; + u32 usi_timing_fs3; + u32 usi_timing_sla; + u32 i2c_addr; +}; + +struct s3c24x0_i2c_bus { + bool active; /* port is active and available */ + int node; /* device tree node */ + int bus_num; /* i2c bus number */ + struct s3c24x0_i2c *regs; + struct exynos5_hsi2c *hsregs; + int is_highspeed; /* High speed type, rather than I2C */ + unsigned clock_frequency; + int id; + unsigned clk_cycle; + unsigned clk_div; +}; +#endif /* _S3C24X0_I2C_H */ diff --git a/qemu/roms/u-boot/drivers/i2c/sh_i2c.c b/qemu/roms/u-boot/drivers/i2c/sh_i2c.c new file mode 100644 index 000000000..e7e96921d --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/sh_i2c.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2011, 2013 Renesas Solutions Corp. + * Copyright (C) 2011, 2013 Nobuhiro Iwamatsu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Every register is 32bit aligned, but only 8bits in size */ +#define ureg(name) u8 name; u8 __pad_##name##0; u16 __pad_##name##1; +struct sh_i2c { + ureg(icdr); + ureg(iccr); + ureg(icsr); + ureg(icic); + ureg(iccl); + ureg(icch); +}; +#undef ureg + +/* ICCR */ +#define SH_I2C_ICCR_ICE (1 << 7) +#define SH_I2C_ICCR_RACK (1 << 6) +#define SH_I2C_ICCR_RTS (1 << 4) +#define SH_I2C_ICCR_BUSY (1 << 2) +#define SH_I2C_ICCR_SCP (1 << 0) + +/* ICSR / ICIC */ +#define SH_IC_BUSY (1 << 4) +#define SH_IC_TACK (1 << 2) +#define SH_IC_WAIT (1 << 1) +#define SH_IC_DTE (1 << 0) + +#ifdef CONFIG_SH_I2C_8BIT +/* store 8th bit of iccl and icch in ICIC register */ +#define SH_I2C_ICIC_ICCLB8 (1 << 7) +#define SH_I2C_ICIC_ICCHB8 (1 << 6) +#endif + +static const struct sh_i2c *i2c_dev[CONFIG_SYS_I2C_SH_NUM_CONTROLLERS] = { + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE0, +#ifdef CONFIG_SYS_I2C_SH_BASE1 + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE1, +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE2 + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE2, +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE3 + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE3, +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE4 + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE4, +#endif +}; + +static u16 iccl, icch; + +#define IRQ_WAIT 1000 + +static void sh_irq_dte(struct sh_i2c *dev) +{ + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (SH_IC_DTE & readb(&dev->icsr)) + break; + udelay(10); + } +} + +static int sh_irq_dte_with_tack(struct sh_i2c *dev) +{ + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (SH_IC_DTE & readb(&dev->icsr)) + break; + if (SH_IC_TACK & readb(&dev->icsr)) + return -1; + udelay(10); + } + return 0; +} + +static void sh_irq_busy(struct sh_i2c *dev) +{ + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (!(SH_IC_BUSY & readb(&dev->icsr))) + break; + udelay(10); + } +} + +static int sh_i2c_set_addr(struct sh_i2c *dev, u8 chip, u8 addr, int stop) +{ + u8 icic = SH_IC_TACK; + + debug("%s: chip: %x, addr: %x iccl: %x, icch %x\n", + __func__, chip, addr, iccl, icch); + clrbits_8(&dev->iccr, SH_I2C_ICCR_ICE); + setbits_8(&dev->iccr, SH_I2C_ICCR_ICE); + + writeb(iccl & 0xff, &dev->iccl); + writeb(icch & 0xff, &dev->icch); +#ifdef CONFIG_SH_I2C_8BIT + if (iccl > 0xff) + icic |= SH_I2C_ICIC_ICCLB8; + if (icch > 0xff) + icic |= SH_I2C_ICIC_ICCHB8; +#endif + writeb(icic, &dev->icic); + + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS|SH_I2C_ICCR_BUSY), &dev->iccr); + sh_irq_dte(dev); + + clrbits_8(&dev->icsr, SH_IC_TACK); + writeb(chip << 1, &dev->icdr); + if (sh_irq_dte_with_tack(dev) != 0) + return -1; + + writeb(addr, &dev->icdr); + if (stop) + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS), &dev->iccr); + + if (sh_irq_dte_with_tack(dev) != 0) + return -1; + return 0; +} + +static void sh_i2c_finish(struct sh_i2c *dev) +{ + writeb(0, &dev->icsr); + clrbits_8(&dev->iccr, SH_I2C_ICCR_ICE); +} + +static int +sh_i2c_raw_write(struct sh_i2c *dev, u8 chip, uint addr, u8 val) +{ + int ret = -1; + if (sh_i2c_set_addr(dev, chip, addr, 0) != 0) + goto exit0; + udelay(10); + + writeb(val, &dev->icdr); + if (sh_irq_dte_with_tack(dev) != 0) + goto exit0; + + writeb((SH_I2C_ICCR_ICE | SH_I2C_ICCR_RTS), &dev->iccr); + if (sh_irq_dte_with_tack(dev) != 0) + goto exit0; + sh_irq_busy(dev); + ret = 0; + +exit0: + sh_i2c_finish(dev); + return ret; +} + +static int sh_i2c_raw_read(struct sh_i2c *dev, u8 chip, u8 addr) +{ + int ret = -1; + +#if defined(CONFIG_SH73A0) + if (sh_i2c_set_addr(dev, chip, addr, 0) != 0) + goto exit0; +#else + if (sh_i2c_set_addr(dev, chip, addr, 1) != 0) + goto exit0; + udelay(100); +#endif + + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS|SH_I2C_ICCR_BUSY), &dev->iccr); + sh_irq_dte(dev); + + writeb(chip << 1 | 0x01, &dev->icdr); + if (sh_irq_dte_with_tack(dev) != 0) + goto exit0; + + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_SCP), &dev->iccr); + if (sh_irq_dte_with_tack(dev) != 0) + goto exit0; + + ret = readb(&dev->icdr) & 0xff; + + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RACK), &dev->iccr); + readb(&dev->icdr); /* Dummy read */ + sh_irq_busy(dev); + +exit0: + sh_i2c_finish(dev); + + return ret; +} + +static void +sh_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + int num, denom, tmp; + + /* No i2c support prior to relocation */ + if (!(gd->flags & GD_FLG_RELOC)) + return; + + /* + * Calculate the value for iccl. From the data sheet: + * iccl = (p-clock / transfer-rate) * (L / (L + H)) + * where L and H are the SCL low and high ratio. + */ + num = CONFIG_SH_I2C_CLOCK * CONFIG_SH_I2C_DATA_LOW; + denom = speed * (CONFIG_SH_I2C_DATA_HIGH + CONFIG_SH_I2C_DATA_LOW); + tmp = num * 10 / denom; + if (tmp % 10 >= 5) + iccl = (u16)((num/denom) + 1); + else + iccl = (u16)(num/denom); + + /* Calculate the value for icch. From the data sheet: + icch = (p clock / transfer rate) * (H / (L + H)) */ + num = CONFIG_SH_I2C_CLOCK * CONFIG_SH_I2C_DATA_HIGH; + tmp = num * 10 / denom; + if (tmp % 10 >= 5) + icch = (u16)((num/denom) + 1); + else + icch = (u16)(num/denom); + + debug("clock: %d, speed %d, iccl: %x, icch: %x\n", + CONFIG_SH_I2C_CLOCK, speed, iccl, icch); +} + +static int sh_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, u8 *data, int len) +{ + int ret, i; + struct sh_i2c *dev = (struct sh_i2c *)i2c_dev[adap->hwadapnr]; + + for (i = 0; i < len; i++) { + ret = sh_i2c_raw_read(dev, chip, addr + i); + if (ret < 0) + return -1; + + data[i] = ret & 0xff; + debug("%s: data[%d]: %02x\n", __func__, i, data[i]); + } + + return 0; +} + +static int sh_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr, + int alen, u8 *data, int len) +{ + struct sh_i2c *dev = (struct sh_i2c *)i2c_dev[adap->hwadapnr]; + int i; + + for (i = 0; i < len; i++) { + debug("%s: data[%d]: %02x\n", __func__, i, data[i]); + if (sh_i2c_raw_write(dev, chip, addr + i, data[i]) != 0) + return -1; + } + return 0; +} + +static int +sh_i2c_probe(struct i2c_adapter *adap, u8 dev) +{ + u8 dummy[1]; + + return sh_i2c_read(adap, dev, 0, 0, dummy, sizeof dummy); +} + +static unsigned int sh_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct sh_i2c *dev = (struct sh_i2c *)i2c_dev[adap->hwadapnr]; + + sh_i2c_finish(dev); + sh_i2c_init(adap, speed, 0); + + return 0; +} + +/* + * Register RCAR i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(sh_0, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED0, 0, 0) +#ifdef CONFIG_SYS_I2C_SH_BASE1 +U_BOOT_I2C_ADAP_COMPLETE(sh_1, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED1, 0, 1) +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE2 +U_BOOT_I2C_ADAP_COMPLETE(sh_2, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED2, 0, 2) +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE3 +U_BOOT_I2C_ADAP_COMPLETE(sh_3, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED3, 0, 3) +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE4 +U_BOOT_I2C_ADAP_COMPLETE(sh_4, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED4, 0, 4) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/sh_sh7734_i2c.c b/qemu/roms/u-boot/drivers/i2c/sh_sh7734_i2c.c new file mode 100644 index 000000000..6c2f221fe --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/sh_sh7734_i2c.c @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2012 Nobuhiro Iwamatsu + * Copyright (C) 2012 Renesas Solutions Corp. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +struct sh_i2c { + u8 iccr1; + u8 iccr2; + u8 icmr; + u8 icier; + u8 icsr; + u8 sar; + u8 icdrt; + u8 icdrr; + u8 nf2cyc; + u8 __pad0; + u8 __pad1; +}; + +static struct sh_i2c *base; +static u8 iccr1_cks, nf2cyc; + +/* ICCR1 */ +#define SH_I2C_ICCR1_ICE (1 << 7) +#define SH_I2C_ICCR1_RCVD (1 << 6) +#define SH_I2C_ICCR1_MST (1 << 5) +#define SH_I2C_ICCR1_TRS (1 << 4) +#define SH_I2C_ICCR1_MTRS \ + (SH_I2C_ICCR1_MST | SH_I2C_ICCR1_TRS) + +/* ICCR1 */ +#define SH_I2C_ICCR2_BBSY (1 << 7) +#define SH_I2C_ICCR2_SCP (1 << 6) +#define SH_I2C_ICCR2_SDAO (1 << 5) +#define SH_I2C_ICCR2_SDAOP (1 << 4) +#define SH_I2C_ICCR2_SCLO (1 << 3) +#define SH_I2C_ICCR2_IICRST (1 << 1) + +#define SH_I2C_ICIER_TIE (1 << 7) +#define SH_I2C_ICIER_TEIE (1 << 6) +#define SH_I2C_ICIER_RIE (1 << 5) +#define SH_I2C_ICIER_NAKIE (1 << 4) +#define SH_I2C_ICIER_STIE (1 << 3) +#define SH_I2C_ICIER_ACKE (1 << 2) +#define SH_I2C_ICIER_ACKBR (1 << 1) +#define SH_I2C_ICIER_ACKBT (1 << 0) + +#define SH_I2C_ICSR_TDRE (1 << 7) +#define SH_I2C_ICSR_TEND (1 << 6) +#define SH_I2C_ICSR_RDRF (1 << 5) +#define SH_I2C_ICSR_NACKF (1 << 4) +#define SH_I2C_ICSR_STOP (1 << 3) +#define SH_I2C_ICSR_ALOVE (1 << 2) +#define SH_I2C_ICSR_AAS (1 << 1) +#define SH_I2C_ICSR_ADZ (1 << 0) + +#define IRQ_WAIT 1000 + +static void sh_i2c_send_stop(struct sh_i2c *base) +{ + clrbits_8(&base->iccr2, SH_I2C_ICCR2_BBSY | SH_I2C_ICCR2_SCP); +} + +static int check_icsr_bits(struct sh_i2c *base, u8 bits) +{ + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (bits & readb(&base->icsr)) + return 0; + udelay(10); + } + + return 1; +} + +static int check_stop(struct sh_i2c *base) +{ + int ret = check_icsr_bits(base, SH_I2C_ICSR_STOP); + clrbits_8(&base->icsr, SH_I2C_ICSR_STOP); + + return ret; +} + +static int check_tend(struct sh_i2c *base, int stop) +{ + int ret = check_icsr_bits(base, SH_I2C_ICSR_TEND); + + if (stop) { + clrbits_8(&base->icsr, SH_I2C_ICSR_STOP); + sh_i2c_send_stop(base); + } + + clrbits_8(&base->icsr, SH_I2C_ICSR_TEND); + return ret; +} + +static int check_tdre(struct sh_i2c *base) +{ + return check_icsr_bits(base, SH_I2C_ICSR_TDRE); +} + +static int check_rdrf(struct sh_i2c *base) +{ + return check_icsr_bits(base, SH_I2C_ICSR_RDRF); +} + +static int check_bbsy(struct sh_i2c *base) +{ + int i; + + for (i = 0 ; i < IRQ_WAIT ; i++) { + if (!(SH_I2C_ICCR2_BBSY & readb(&base->iccr2))) + return 0; + udelay(10); + } + return 1; +} + +static int check_ackbr(struct sh_i2c *base) +{ + int i; + + for (i = 0 ; i < IRQ_WAIT ; i++) { + if (!(SH_I2C_ICIER_ACKBR & readb(&base->icier))) + return 0; + udelay(10); + } + + return 1; +} + +static void sh_i2c_reset(struct sh_i2c *base) +{ + setbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST); + + udelay(100); + + clrbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST); +} + +static int i2c_set_addr(struct sh_i2c *base, u8 id, u8 reg) +{ + if (check_bbsy(base)) { + puts("i2c bus busy\n"); + goto fail; + } + + setbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS); + clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY); + + writeb((id << 1), &base->icdrt); + + if (check_tend(base, 0)) { + puts("TEND check fail...\n"); + goto fail; + } + + if (check_ackbr(base)) { + check_tend(base, 0); + sh_i2c_send_stop(base); + goto fail; + } + + writeb(reg, &base->icdrt); + + if (check_tdre(base)) { + puts("TDRE check fail...\n"); + goto fail; + } + + if (check_tend(base, 0)) { + puts("TEND check fail...\n"); + goto fail; + } + + return 0; +fail: + + return 1; +} + +static int +i2c_raw_write(struct sh_i2c *base, u8 id, u8 reg, u8 *val, int size) +{ + int i; + + if (i2c_set_addr(base, id, reg)) { + puts("Fail set slave address\n"); + return 1; + } + + for (i = 0; i < size; i++) { + writeb(val[i], &base->icdrt); + check_tdre(base); + } + + check_tend(base, 1); + check_stop(base); + + udelay(100); + + clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS); + clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE); + sh_i2c_reset(base); + + return 0; +} + +static u8 i2c_raw_read(struct sh_i2c *base, u8 id, u8 reg) +{ + u8 ret = 0; + + if (i2c_set_addr(base, id, reg)) { + puts("Fail set slave address\n"); + goto fail; + } + + clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY); + writeb((id << 1) | 1, &base->icdrt); + + if (check_tend(base, 0)) + puts("TDRE check fail...\n"); + + clrsetbits_8(&base->iccr1, SH_I2C_ICCR1_TRS, SH_I2C_ICCR1_MST); + clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE); + setbits_8(&base->icier, SH_I2C_ICIER_ACKBT); + setbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD); + + /* read data (dummy) */ + ret = readb(&base->icdrr); + + if (check_rdrf(base)) { + puts("check RDRF error\n"); + goto fail; + } + + clrbits_8(&base->icsr, SH_I2C_ICSR_STOP); + udelay(1000); + + sh_i2c_send_stop(base); + + if (check_stop(base)) { + puts("check STOP error\n"); + goto fail; + } + + clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS); + clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE); + + /* data read */ + ret = readb(&base->icdrr); + +fail: + clrbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD); + + return ret; +} + +#ifdef CONFIG_I2C_MULTI_BUS +static unsigned int current_bus; + +/** + * i2c_set_bus_num - change active I2C bus + * @bus: bus index, zero based + * @returns: 0 on success, non-0 on failure + */ +int i2c_set_bus_num(unsigned int bus) +{ + switch (bus) { + case 0: + base = (void *)CONFIG_SH_I2C_BASE0; + break; + case 1: + base = (void *)CONFIG_SH_I2C_BASE1; + break; + default: + printf("Bad bus: %d\n", bus); + return -1; + } + + current_bus = bus; + + return 0; +} + +/** + * i2c_get_bus_num - returns index of active I2C bus + */ +unsigned int i2c_get_bus_num(void) +{ + return current_bus; +} +#endif + +void i2c_init(int speed, int slaveaddr) +{ +#ifdef CONFIG_I2C_MULTI_BUS + current_bus = 0; +#endif + base = (struct sh_i2c *)CONFIG_SH_I2C_BASE0; + + if (speed == 400000) + iccr1_cks = 0x07; + else + iccr1_cks = 0x0F; + + nf2cyc = 1; + + /* Reset */ + sh_i2c_reset(base); + + /* ICE enable and set clock */ + writeb(SH_I2C_ICCR1_ICE | iccr1_cks, &base->iccr1); + writeb(nf2cyc, &base->nf2cyc); +} + +/* + * i2c_read: - Read multiple bytes from an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be read + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to write the data + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_read(u8 chip, u32 addr, int alen, u8 *buffer, int len) +{ + int i = 0; + for (i = 0; i < len; i++) + buffer[i] = i2c_raw_read(base, chip, addr + i); + + return 0; +} + +/* + * i2c_write: - Write multiple bytes to an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be written + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to find the data to be written + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_write(u8 chip, u32 addr, int alen, u8 *buffer, int len) +{ + return i2c_raw_write(base, chip, addr, buffer, len); +} + +/* + * i2c_probe: - Test if a chip answers for a given i2c address + * + * @chip: address of the chip which is searched for + * @return: 0 if a chip was found, -1 otherwhise + */ +int i2c_probe(u8 chip) +{ + u8 byte; + return i2c_read(chip, 0, 0, &byte, 1); +} diff --git a/qemu/roms/u-boot/drivers/i2c/soft_i2c.c b/qemu/roms/u-boot/drivers/i2c/soft_i2c.c new file mode 100644 index 000000000..db9b4026b --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/soft_i2c.c @@ -0,0 +1,475 @@ +/* + * (C) Copyright 2009 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * Changes for multibus/multiadapter I2C support. + * + * (C) Copyright 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This has been changed substantially by Gerald Van Baren, Custom IDEAS, + * vanbaren@cideas.com. It was heavily influenced by LiMon, written by + * Neil Russell. + */ + +#include +#ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ +#include +#include +#endif +#if defined(CONFIG_AVR32) +#include +#endif +#if defined(CONFIG_AT91FAMILY) +#include +#include +#include +#ifdef CONFIG_ATMEL_LEGACY +#include +#endif +#endif +#if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) +#include +#endif +#include + +#if defined(CONFIG_SOFT_I2C_GPIO_SCL) +# include + +# ifndef I2C_GPIO_SYNC +# define I2C_GPIO_SYNC +# endif + +# ifndef I2C_INIT +# define I2C_INIT \ + do { \ + gpio_request(CONFIG_SOFT_I2C_GPIO_SCL, "soft_i2c"); \ + gpio_request(CONFIG_SOFT_I2C_GPIO_SDA, "soft_i2c"); \ + } while (0) +# endif + +# ifndef I2C_ACTIVE +# define I2C_ACTIVE do { } while (0) +# endif + +# ifndef I2C_TRISTATE +# define I2C_TRISTATE do { } while (0) +# endif + +# ifndef I2C_READ +# define I2C_READ gpio_get_value(CONFIG_SOFT_I2C_GPIO_SDA) +# endif + +# ifndef I2C_SDA +# define I2C_SDA(bit) \ + do { \ + if (bit) \ + gpio_direction_input(CONFIG_SOFT_I2C_GPIO_SDA); \ + else \ + gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SDA, 0); \ + I2C_GPIO_SYNC; \ + } while (0) +# endif + +# ifndef I2C_SCL +# define I2C_SCL(bit) \ + do { \ + gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SCL, bit); \ + I2C_GPIO_SYNC; \ + } while (0) +# endif + +# ifndef I2C_DELAY +# define I2C_DELAY udelay(5) /* 1/4 I2C clock duration */ +# endif + +#endif + +/* #define DEBUG_I2C */ + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef I2C_SOFT_DECLARATIONS +# if defined(CONFIG_MPC8260) +# define I2C_SOFT_DECLARATIONS volatile ioport_t *iop = \ + ioport_addr((immap_t *)CONFIG_SYS_IMMR, I2C_PORT); +# elif defined(CONFIG_8xx) +# define I2C_SOFT_DECLARATIONS volatile immap_t *immr = \ + (immap_t *)CONFIG_SYS_IMMR; +# else +# define I2C_SOFT_DECLARATIONS +# endif +#endif + +#if !defined(CONFIG_SYS_I2C_SOFT_SPEED) +#define CONFIG_SYS_I2C_SOFT_SPEED CONFIG_SYS_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_I2C_SOFT_SLAVE) +#define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE +#endif + +/*----------------------------------------------------------------------- + * Definitions + */ +#define RETRIES 0 + +#define I2C_ACK 0 /* PD_SDA level to ack a byte */ +#define I2C_NOACK 1 /* PD_SDA level to noack a byte */ + + +#ifdef DEBUG_I2C +#define PRINTD(fmt,args...) do { \ + printf (fmt ,##args); \ + } while (0) +#else +#define PRINTD(fmt,args...) +#endif + +/*----------------------------------------------------------------------- + * Local functions + */ +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +static void send_reset (void); +#endif +static void send_start (void); +static void send_stop (void); +static void send_ack (int); +static int write_byte (uchar byte); +static uchar read_byte (int); + +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +/*----------------------------------------------------------------------- + * Send a reset sequence consisting of 9 clocks with the data signal high + * to clock any confused device back into an idle state. Also send a + * at the end of the sequence for belts & suspenders. + */ +static void send_reset(void) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + int j; + + I2C_SCL(1); + I2C_SDA(1); +#ifdef I2C_INIT + I2C_INIT; +#endif + I2C_TRISTATE; + for(j = 0; j < 9; j++) { + I2C_SCL(0); + I2C_DELAY; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + } + send_stop(); + I2C_TRISTATE; +} +#endif + +/*----------------------------------------------------------------------- + * START: High -> Low on SDA while SCL is High + */ +static void send_start(void) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + + I2C_DELAY; + I2C_SDA(1); + I2C_ACTIVE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_SDA(0); + I2C_DELAY; +} + +/*----------------------------------------------------------------------- + * STOP: Low -> High on SDA while SCL is High + */ +static void send_stop(void) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + + I2C_SCL(0); + I2C_DELAY; + I2C_SDA(0); + I2C_ACTIVE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_SDA(1); + I2C_DELAY; + I2C_TRISTATE; +} + +/*----------------------------------------------------------------------- + * ack should be I2C_ACK or I2C_NOACK + */ +static void send_ack(int ack) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + + I2C_SCL(0); + I2C_DELAY; + I2C_ACTIVE; + I2C_SDA(ack); + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + I2C_SCL(0); + I2C_DELAY; +} + +/*----------------------------------------------------------------------- + * Send 8 bits and look for an acknowledgement. + */ +static int write_byte(uchar data) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + int j; + int nack; + + I2C_ACTIVE; + for(j = 0; j < 8; j++) { + I2C_SCL(0); + I2C_DELAY; + I2C_SDA(data & 0x80); + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + + data <<= 1; + } + + /* + * Look for an (negative logic) and return it. + */ + I2C_SCL(0); + I2C_DELAY; + I2C_SDA(1); + I2C_TRISTATE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + nack = I2C_READ; + I2C_SCL(0); + I2C_DELAY; + I2C_ACTIVE; + + return(nack); /* not a nack is an ack */ +} + +/*----------------------------------------------------------------------- + * if ack == I2C_ACK, ACK the byte so can continue reading, else + * send I2C_NOACK to end the read. + */ +static uchar read_byte(int ack) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + int data; + int j; + + /* + * Read 8 bits, MSB first. + */ + I2C_TRISTATE; + I2C_SDA(1); + data = 0; + for(j = 0; j < 8; j++) { + I2C_SCL(0); + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + data <<= 1; + data |= I2C_READ; + I2C_DELAY; + } + send_ack(ack); + + return(data); +} + +/*----------------------------------------------------------------------- + * Initialization + */ +static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ +#if defined(CONFIG_SYS_I2C_INIT_BOARD) + /* call board specific i2c bus reset routine before accessing the */ + /* environment, which might be in a chip on that bus. For details */ + /* about this problem see doc/I2C_Edge_Conditions. */ + i2c_init_board(); +#else + /* + * WARNING: Do NOT save speed in a static variable: if the + * I2C routines are called before RAM is initialized (to read + * the DIMM SPD, for instance), RAM won't be usable and your + * system will crash. + */ + send_reset (); +#endif +} + +/*----------------------------------------------------------------------- + * Probe to see if a chip is present. Also good for checking for the + * completion of EEPROM writes since the chip stops responding until + * the write completes (typically 10mSec). + */ +static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr) +{ + int rc; + + /* + * perform 1 byte write transaction with just address byte + * (fake write) + */ + send_start(); + rc = write_byte ((addr << 1) | 0); + send_stop(); + + return (rc ? 1 : 0); +} + +/*----------------------------------------------------------------------- + * Read bytes + */ +static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + int shift; + PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n", + chip, addr, alen, buffer, len); + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); + + PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n", + chip, addr); +#endif + + /* + * Do the addressing portion of a write cycle to set the + * chip's address pointer. If the address length is zero, + * don't do the normal write cycle to set the address pointer, + * there is no address pointer in this chip. + */ + send_start(); + if(alen > 0) { + if(write_byte(chip << 1)) { /* write cycle */ + send_stop(); + PRINTD("i2c_read, no chip responded %02X\n", chip); + return(1); + } + shift = (alen-1) * 8; + while(alen-- > 0) { + if(write_byte(addr >> shift)) { + PRINTD("i2c_read, address not ed\n"); + return(1); + } + shift -= 8; + } + + /* Some I2C chips need a stop/start sequence here, + * other chips don't work with a full stop and need + * only a start. Default behaviour is to send the + * stop/start sequence. + */ +#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START + send_start(); +#else + send_stop(); + send_start(); +#endif + } + /* + * Send the chip address again, this time for a read cycle. + * Then read the data. On the last byte, we do a NACK instead + * of an ACK(len == 0) to terminate the read. + */ + write_byte((chip << 1) | 1); /* read cycle */ + while(len-- > 0) { + *buffer++ = read_byte(len == 0); + } + send_stop(); + return(0); +} + +/*----------------------------------------------------------------------- + * Write bytes + */ +static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + int shift, failures = 0; + + PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n", + chip, addr, alen, buffer, len); + + send_start(); + if(write_byte(chip << 1)) { /* write cycle */ + send_stop(); + PRINTD("i2c_write, no chip responded %02X\n", chip); + return(1); + } + shift = (alen-1) * 8; + while(alen-- > 0) { + if(write_byte(addr >> shift)) { + PRINTD("i2c_write, address not ed\n"); + return(1); + } + shift -= 8; + } + + while(len-- > 0) { + if(write_byte(*buffer++)) { + failures++; + } + } + send_stop(); + return(failures); +} + +/* + * Register soft i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE, + 0) +#if defined(I2C_SOFT_DECLARATIONS2) +U_BOOT_I2C_ADAP_COMPLETE(soft1, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED_2, + CONFIG_SYS_I2C_SOFT_SLAVE_2, + 1) +#endif +#if defined(I2C_SOFT_DECLARATIONS3) +U_BOOT_I2C_ADAP_COMPLETE(soft2, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED_3, + CONFIG_SYS_I2C_SOFT_SLAVE_3, + 2) +#endif +#if defined(I2C_SOFT_DECLARATIONS4) +U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED_4, + CONFIG_SYS_I2C_SOFT_SLAVE_4, + 3) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/tegra_i2c.c b/qemu/roms/u-boot/drivers/i2c/tegra_i2c.c new file mode 100644 index 000000000..594e5ddeb --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/tegra_i2c.c @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Copyright (c) 2010-2011 NVIDIA Corporation + * NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Information about i2c controller */ +struct i2c_bus { + int id; + enum periph_id periph_id; + int speed; + int pinmux_config; + struct i2c_control *control; + struct i2c_ctlr *regs; + int is_dvc; /* DVC type, rather than I2C */ + int is_scs; /* single clock source (T114+) */ + int inited; /* bus is inited */ +}; + +static struct i2c_bus i2c_controllers[TEGRA_I2C_NUM_CONTROLLERS]; + +static void set_packet_mode(struct i2c_bus *i2c_bus) +{ + u32 config; + + config = I2C_CNFG_NEW_MASTER_FSM_MASK | I2C_CNFG_PACKET_MODE_MASK; + + if (i2c_bus->is_dvc) { + struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs; + + writel(config, &dvc->cnfg); + } else { + writel(config, &i2c_bus->regs->cnfg); + /* + * program I2C_SL_CNFG.NEWSL to ENABLE. This fixes probe + * issues, i.e., some slaves may be wrongly detected. + */ + setbits_le32(&i2c_bus->regs->sl_cnfg, I2C_SL_CNFG_NEWSL_MASK); + } +} + +static void i2c_reset_controller(struct i2c_bus *i2c_bus) +{ + /* Reset I2C controller. */ + reset_periph(i2c_bus->periph_id, 1); + + /* re-program config register to packet mode */ + set_packet_mode(i2c_bus); +} + +static void i2c_init_controller(struct i2c_bus *i2c_bus) +{ + /* + * Use PLLP - DP-04508-001_v06 datasheet indicates a divisor of 8 + * here, in section 23.3.1, but in fact we seem to need a factor of + * 16 to get the right frequency. + */ + clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, + i2c_bus->speed * 2 * 8); + + if (i2c_bus->is_scs) { + /* + * T114 I2C went to a single clock source for standard/fast and + * HS clock speeds. The new clock rate setting calculation is: + * SCL = CLK_SOURCE.I2C / + * (CLK_MULT_STD_FAST_MODE * (I2C_CLK_DIV_STD_FAST_MODE+1) * + * I2C FREQUENCY DIVISOR) as per the T114 TRM (sec 30.3.1). + * + * NOTE: We do this here, after the initial clock/pll start, + * because if we read the clk_div reg before the controller + * is running, we hang, and we need it for the new calc. + */ + int clk_div_stdfst_mode = readl(&i2c_bus->regs->clk_div) >> 16; + debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__, + clk_div_stdfst_mode); + + clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, + CLK_MULT_STD_FAST_MODE * (clk_div_stdfst_mode + 1) * + i2c_bus->speed * 2); + } + + /* Reset I2C controller. */ + i2c_reset_controller(i2c_bus); + + /* Configure I2C controller. */ + if (i2c_bus->is_dvc) { /* only for DVC I2C */ + struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs; + + setbits_le32(&dvc->ctrl3, DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK); + } + + funcmux_select(i2c_bus->periph_id, i2c_bus->pinmux_config); +} + +static void send_packet_headers( + struct i2c_bus *i2c_bus, + struct i2c_trans_info *trans, + u32 packet_id) +{ + u32 data; + + /* prepare header1: Header size = 0 Protocol = I2C, pktType = 0 */ + data = PROTOCOL_TYPE_I2C << PKT_HDR1_PROTOCOL_SHIFT; + data |= packet_id << PKT_HDR1_PKT_ID_SHIFT; + data |= i2c_bus->id << PKT_HDR1_CTLR_ID_SHIFT; + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 1 sent (0x%x)\n", data); + + /* prepare header2 */ + data = (trans->num_bytes - 1) << PKT_HDR2_PAYLOAD_SIZE_SHIFT; + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 2 sent (0x%x)\n", data); + + /* prepare IO specific header: configure the slave address */ + data = trans->address << PKT_HDR3_SLAVE_ADDR_SHIFT; + + /* Enable Read if it is not a write transaction */ + if (!(trans->flags & I2C_IS_WRITE)) + data |= PKT_HDR3_READ_MODE_MASK; + + /* Write I2C specific header */ + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 3 sent (0x%x)\n", data); +} + +static int wait_for_tx_fifo_empty(struct i2c_control *control) +{ + u32 count; + int timeout_us = I2C_TIMEOUT_USEC; + + while (timeout_us >= 0) { + count = (readl(&control->fifo_status) & TX_FIFO_EMPTY_CNT_MASK) + >> TX_FIFO_EMPTY_CNT_SHIFT; + if (count == I2C_FIFO_DEPTH) + return 1; + udelay(10); + timeout_us -= 10; + } + + return 0; +} + +static int wait_for_rx_fifo_notempty(struct i2c_control *control) +{ + u32 count; + int timeout_us = I2C_TIMEOUT_USEC; + + while (timeout_us >= 0) { + count = (readl(&control->fifo_status) & TX_FIFO_FULL_CNT_MASK) + >> TX_FIFO_FULL_CNT_SHIFT; + if (count) + return 1; + udelay(10); + timeout_us -= 10; + } + + return 0; +} + +static int wait_for_transfer_complete(struct i2c_control *control) +{ + int int_status; + int timeout_us = I2C_TIMEOUT_USEC; + + while (timeout_us >= 0) { + int_status = readl(&control->int_status); + if (int_status & I2C_INT_NO_ACK_MASK) + return -int_status; + if (int_status & I2C_INT_ARBITRATION_LOST_MASK) + return -int_status; + if (int_status & I2C_INT_XFER_COMPLETE_MASK) + return 0; + + udelay(10); + timeout_us -= 10; + } + + return -1; +} + +static int send_recv_packets(struct i2c_bus *i2c_bus, + struct i2c_trans_info *trans) +{ + struct i2c_control *control = i2c_bus->control; + u32 int_status; + u32 words; + u8 *dptr; + u32 local; + uchar last_bytes; + int error = 0; + int is_write = trans->flags & I2C_IS_WRITE; + + /* clear status from previous transaction, XFER_COMPLETE, NOACK, etc. */ + int_status = readl(&control->int_status); + writel(int_status, &control->int_status); + + send_packet_headers(i2c_bus, trans, 1); + + words = DIV_ROUND_UP(trans->num_bytes, 4); + last_bytes = trans->num_bytes & 3; + dptr = trans->buf; + + while (words) { + u32 *wptr = (u32 *)dptr; + + if (is_write) { + /* deal with word alignment */ + if ((unsigned)dptr & 3) { + memcpy(&local, dptr, sizeof(u32)); + writel(local, &control->tx_fifo); + debug("pkt data sent (0x%x)\n", local); + } else { + writel(*wptr, &control->tx_fifo); + debug("pkt data sent (0x%x)\n", *wptr); + } + if (!wait_for_tx_fifo_empty(control)) { + error = -1; + goto exit; + } + } else { + if (!wait_for_rx_fifo_notempty(control)) { + error = -1; + goto exit; + } + /* + * for the last word, we read into our local buffer, + * in case that caller did not provide enough buffer. + */ + local = readl(&control->rx_fifo); + if ((words == 1) && last_bytes) + memcpy(dptr, (char *)&local, last_bytes); + else if ((unsigned)dptr & 3) + memcpy(dptr, &local, sizeof(u32)); + else + *wptr = local; + debug("pkt data received (0x%x)\n", local); + } + words--; + dptr += sizeof(u32); + } + + if (wait_for_transfer_complete(control)) { + error = -1; + goto exit; + } + return 0; +exit: + /* error, reset the controller. */ + i2c_reset_controller(i2c_bus); + + return error; +} + +static int tegra_i2c_write_data(struct i2c_bus *bus, u32 addr, u8 *data, + u32 len) +{ + int error; + struct i2c_trans_info trans_info; + + trans_info.address = addr; + trans_info.buf = data; + trans_info.flags = I2C_IS_WRITE; + trans_info.num_bytes = len; + trans_info.is_10bit_address = 0; + + error = send_recv_packets(bus, &trans_info); + if (error) + debug("tegra_i2c_write_data: Error (%d) !!!\n", error); + + return error; +} + +static int tegra_i2c_read_data(struct i2c_bus *bus, u32 addr, u8 *data, + u32 len) +{ + int error; + struct i2c_trans_info trans_info; + + trans_info.address = addr | 1; + trans_info.buf = data; + trans_info.flags = 0; + trans_info.num_bytes = len; + trans_info.is_10bit_address = 0; + + error = send_recv_packets(bus, &trans_info); + if (error) + debug("tegra_i2c_read_data: Error (%d) !!!\n", error); + + return error; +} + +#ifndef CONFIG_OF_CONTROL +#error "Please enable device tree support to use this driver" +#endif + +/** + * Check that a bus number is valid and return a pointer to it + * + * @param bus_num Bus number to check / return + * @return pointer to bus, if valid, else NULL + */ +static struct i2c_bus *tegra_i2c_get_bus(struct i2c_adapter *adap) +{ + struct i2c_bus *bus; + + bus = &i2c_controllers[adap->hwadapnr]; + if (!bus->inited) { + debug("%s: Bus %u not available\n", __func__, adap->hwadapnr); + return NULL; + } + + return bus; +} + +static unsigned int tegra_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct i2c_bus *bus; + + bus = tegra_i2c_get_bus(adap); + if (!bus) + return 0; + bus->speed = speed; + i2c_init_controller(bus); + + return 0; +} + +static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) +{ + i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg"); + + /* + * We don't have a binding for pinmux yet. Leave it out for now. So + * far no one needs anything other than the default. + */ + i2c_bus->pinmux_config = FUNCMUX_DEFAULT; + i2c_bus->speed = fdtdec_get_int(blob, node, "clock-frequency", 0); + i2c_bus->periph_id = clock_decode_periph_id(blob, node); + + /* + * We can't specify the pinmux config in the fdt, so I2C2 will not + * work on Seaboard. It normally has no devices on it anyway. + * You could add in this little hack if you need to use it. + * The correct solution is a pinmux binding in the fdt. + * + * if (i2c_bus->periph_id == PERIPH_ID_I2C2) + * i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA; + */ + if (i2c_bus->periph_id == -1) + return -FDT_ERR_NOTFOUND; + + return 0; +} + +/* + * Process a list of nodes, adding them to our list of I2C ports. + * + * @param blob fdt blob + * @param node_list list of nodes to process (any <=0 are ignored) + * @param count number of nodes to process + * @param is_dvc 1 if these are DVC ports, 0 if standard I2C + * @param is_scs 1 if this HW uses a single clock source (T114+) + * @return 0 if ok, -1 on error + */ +static int process_nodes(const void *blob, int node_list[], int count, + int is_dvc, int is_scs) +{ + struct i2c_bus *i2c_bus; + int i; + + /* build the i2c_controllers[] for each controller */ + for (i = 0; i < count; i++) { + int node = node_list[i]; + + if (node <= 0) + continue; + + i2c_bus = &i2c_controllers[i]; + i2c_bus->id = i; + + if (i2c_get_config(blob, node, i2c_bus)) { + printf("i2c_init_board: failed to decode bus %d\n", i); + return -1; + } + + i2c_bus->is_scs = is_scs; + + i2c_bus->is_dvc = is_dvc; + if (is_dvc) { + i2c_bus->control = + &((struct dvc_ctlr *)i2c_bus->regs)->control; + } else { + i2c_bus->control = &i2c_bus->regs->control; + } + debug("%s: controller bus %d at %p, periph_id %d, speed %d: ", + is_dvc ? "dvc" : "i2c", i, i2c_bus->regs, + i2c_bus->periph_id, i2c_bus->speed); + i2c_init_controller(i2c_bus); + debug("ok\n"); + i2c_bus->inited = 1; + + /* Mark position as used */ + node_list[i] = -1; + } + + return 0; +} + +/* Sadly there is no error return from this function */ +void i2c_init_board(void) +{ + int node_list[TEGRA_I2C_NUM_CONTROLLERS]; + const void *blob = gd->fdt_blob; + int count; + + /* First check for newer (T114+) I2C ports */ + count = fdtdec_find_aliases_for_id(blob, "i2c", + COMPAT_NVIDIA_TEGRA114_I2C, node_list, + TEGRA_I2C_NUM_CONTROLLERS); + if (process_nodes(blob, node_list, count, 0, 1)) + return; + + /* Now get the older (T20/T30) normal I2C ports */ + count = fdtdec_find_aliases_for_id(blob, "i2c", + COMPAT_NVIDIA_TEGRA20_I2C, node_list, + TEGRA_I2C_NUM_CONTROLLERS); + if (process_nodes(blob, node_list, count, 0, 0)) + return; + + /* Now look for dvc ports */ + count = fdtdec_add_aliases_for_id(blob, "i2c", + COMPAT_NVIDIA_TEGRA20_DVC, node_list, + TEGRA_I2C_NUM_CONTROLLERS); + if (process_nodes(blob, node_list, count, 1, 0)) + return; +} + +static void tegra_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + /* No i2c support prior to relocation */ + if (!(gd->flags & GD_FLG_RELOC)) + return; + + /* This will override the speed selected in the fdt for that port */ + debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); + i2c_set_bus_speed(speed); +} + +/* i2c write version without the register address */ +int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, int len) +{ + int rc; + + debug("i2c_write_data: chip=0x%x, len=0x%x\n", chip, len); + debug("write_data: "); + /* use rc for counter */ + for (rc = 0; rc < len; ++rc) + debug(" 0x%02x", buffer[rc]); + debug("\n"); + + /* Shift 7-bit address over for lower-level i2c functions */ + rc = tegra_i2c_write_data(bus, chip << 1, buffer, len); + if (rc) + debug("i2c_write_data(): rc=%d\n", rc); + + return rc; +} + +/* i2c read version without the register address */ +int i2c_read_data(struct i2c_bus *bus, uchar chip, uchar *buffer, int len) +{ + int rc; + + debug("inside i2c_read_data():\n"); + /* Shift 7-bit address over for lower-level i2c functions */ + rc = tegra_i2c_read_data(bus, chip << 1, buffer, len); + if (rc) { + debug("i2c_read_data(): rc=%d\n", rc); + return rc; + } + + debug("i2c_read_data: "); + /* reuse rc for counter*/ + for (rc = 0; rc < len; ++rc) + debug(" 0x%02x", buffer[rc]); + debug("\n"); + + return 0; +} + +/* Probe to see if a chip is present. */ +static int tegra_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + struct i2c_bus *bus; + int rc; + uchar reg; + + debug("i2c_probe: addr=0x%x\n", chip); + bus = tegra_i2c_get_bus(adap); + if (!bus) + return 1; + reg = 0; + rc = i2c_write_data(bus, chip, ®, 1); + if (rc) { + debug("Error probing 0x%x.\n", chip); + return 1; + } + return 0; +} + +static int i2c_addr_ok(const uint addr, const int alen) +{ + /* We support 7 or 10 bit addresses, so one or two bytes each */ + return alen == 1 || alen == 2; +} + +/* Read bytes */ +static int tegra_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c_bus *bus; + uint offset; + int i; + + debug("i2c_read: chip=0x%x, addr=0x%x, len=0x%x\n", + chip, addr, len); + bus = tegra_i2c_get_bus(adap); + if (!bus) + return 1; + if (!i2c_addr_ok(addr, alen)) { + debug("i2c_read: Bad address %x.%d.\n", addr, alen); + return 1; + } + for (offset = 0; offset < len; offset++) { + if (alen) { + uchar data[alen]; + for (i = 0; i < alen; i++) { + data[alen - i - 1] = + (addr + offset) >> (8 * i); + } + if (i2c_write_data(bus, chip, data, alen)) { + debug("i2c_read: error sending (0x%x)\n", + addr); + return 1; + } + } + if (i2c_read_data(bus, chip, buffer + offset, 1)) { + debug("i2c_read: error reading (0x%x)\n", addr); + return 1; + } + } + + return 0; +} + +/* Write bytes */ +static int tegra_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c_bus *bus; + uint offset; + int i; + + debug("i2c_write: chip=0x%x, addr=0x%x, len=0x%x\n", + chip, addr, len); + bus = tegra_i2c_get_bus(adap); + if (!bus) + return 1; + if (!i2c_addr_ok(addr, alen)) { + debug("i2c_write: Bad address %x.%d.\n", addr, alen); + return 1; + } + for (offset = 0; offset < len; offset++) { + uchar data[alen + 1]; + for (i = 0; i < alen; i++) + data[alen - i - 1] = (addr + offset) >> (8 * i); + data[alen] = buffer[offset]; + if (i2c_write_data(bus, chip, data, alen + 1)) { + debug("i2c_write: error sending (0x%x)\n", addr); + return 1; + } + } + + return 0; +} + +int tegra_i2c_get_dvc_bus_num(void) +{ + int i; + + for (i = 0; i < TEGRA_I2C_NUM_CONTROLLERS; i++) { + struct i2c_bus *bus = &i2c_controllers[i]; + + if (bus->inited && bus->is_dvc) + return i; + } + + return -1; +} + +/* + * Register soft i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(tegra0, tegra_i2c_init, tegra_i2c_probe, + tegra_i2c_read, tegra_i2c_write, + tegra_i2c_set_bus_speed, 100000, 0, 0) +U_BOOT_I2C_ADAP_COMPLETE(tegra1, tegra_i2c_init, tegra_i2c_probe, + tegra_i2c_read, tegra_i2c_write, + tegra_i2c_set_bus_speed, 100000, 0, 1) +U_BOOT_I2C_ADAP_COMPLETE(tegra2, tegra_i2c_init, tegra_i2c_probe, + tegra_i2c_read, tegra_i2c_write, + tegra_i2c_set_bus_speed, 100000, 0, 2) +U_BOOT_I2C_ADAP_COMPLETE(tegra3, tegra_i2c_init, tegra_i2c_probe, + tegra_i2c_read, tegra_i2c_write, + tegra_i2c_set_bus_speed, 100000, 0, 3) +#if TEGRA_I2C_NUM_CONTROLLERS > 4 +U_BOOT_I2C_ADAP_COMPLETE(tegra4, tegra_i2c_init, tegra_i2c_probe, + tegra_i2c_read, tegra_i2c_write, + tegra_i2c_set_bus_speed, 100000, 0, 4) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/tsi108_i2c.c b/qemu/roms/u-boot/drivers/i2c/tsi108_i2c.c new file mode 100644 index 000000000..c0779079a --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/tsi108_i2c.c @@ -0,0 +1,273 @@ +/* + * (C) Copyright 2004 Tundra Semiconductor Corp. + * Author: Alex Bounine + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include + +#if defined(CONFIG_CMD_I2C) + +#define I2C_DELAY 100000 +#undef DEBUG_I2C + +#ifdef DEBUG_I2C +#define DPRINT(x) printf (x) +#else +#define DPRINT(x) +#endif + +/* All functions assume that Tsi108 I2C block is the only master on the bus */ +/* I2C read helper function */ + +void i2c_init(int speed, int slaveaddr) +{ + /* + * The TSI108 has a fixed I2C clock rate and doesn't support slave + * operation. This function only exists as a stub to fit into the + * U-Boot I2C API. + */ +} + +static int i2c_read_byte ( + uint i2c_chan, /* I2C channel number: 0 - main, 1 - SDC SPD */ + uchar chip_addr,/* I2C device address on the bus */ + uint byte_addr, /* Byte address within I2C device */ + uchar * buffer /* pointer to data buffer */ + ) +{ + u32 temp; + u32 to_count = I2C_DELAY; + u32 op_status = TSI108_I2C_TIMEOUT_ERR; + u32 chan_offset = TSI108_I2C_OFFSET; + + DPRINT (("I2C read_byte() %d 0x%02x 0x%02x\n", + i2c_chan, chip_addr, byte_addr)); + + if (0 != i2c_chan) + chan_offset = TSI108_I2C_SDRAM_OFFSET; + + /* Check if I2C operation is in progress */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | + I2C_CNTRL2_START))) { + /* Set device address and operation (read = 0) */ + temp = (byte_addr << 16) | ((chip_addr & 0x07) << 8) | + ((chip_addr >> 3) & 0x0F); + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL1) = + temp; + + /* Issue the read command + * (at this moment all other parameters are 0 + * (size = 1 byte, lane = 0) + */ + + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2) = + (I2C_CNTRL2_START); + + /* Wait until operation completed */ + do { + /* Read I2C operation status */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_START))) { + if (0 == (temp & + (I2C_CNTRL2_I2C_CFGERR | + I2C_CNTRL2_I2C_TO_ERR)) + ) { + op_status = TSI108_I2C_SUCCESS; + + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + + chan_offset + + I2C_RD_DATA); + + *buffer = (u8) (temp & 0xFF); + } else { + /* report HW error */ + op_status = TSI108_I2C_IF_ERROR; + + DPRINT (("I2C HW error reported: 0x%02x\n", temp)); + } + + break; + } + } while (to_count--); + } else { + op_status = TSI108_I2C_IF_BUSY; + + DPRINT (("I2C Transaction start failed: 0x%02x\n", temp)); + } + + DPRINT (("I2C read_byte() status: 0x%02x\n", op_status)); + return op_status; +} + +/* + * I2C Read interface as defined in "include/i2c.h" : + * chip_addr: I2C chip address, range 0..127 + * (to read from SPD channel EEPROM use (0xD0 ... 0xD7) + * NOTE: The bit 7 in the chip_addr serves as a channel select. + * This hack is for enabling "i2c sdram" command on Tsi108 boards + * without changes to common code. Used for I2C reads only. + * byte_addr: Memory or register address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Pointer to destination buffer for data to be read + * len: How many bytes to read + * + * Returns: 0 on success, not 0 on failure + */ + +int i2c_read (uchar chip_addr, uint byte_addr, int alen, + uchar * buffer, int len) +{ + u32 op_status = TSI108_I2C_PARAM_ERR; + u32 i2c_if = 0; + + /* Hack to support second (SPD) I2C controller (SPD EEPROM read only).*/ + if (0xD0 == (chip_addr & ~0x07)) { + i2c_if = 1; + chip_addr &= 0x7F; + } + /* Check for valid I2C address */ + if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { + while (len--) { + op_status = i2c_read_byte(i2c_if, chip_addr, byte_addr++, buffer++); + + if (TSI108_I2C_SUCCESS != op_status) { + DPRINT (("I2C read_byte() failed: 0x%02x (%d left)\n", op_status, len)); + + break; + } + } + } + + DPRINT (("I2C read() status: 0x%02x\n", op_status)); + return op_status; +} + +/* I2C write helper function */ + +static int i2c_write_byte (uchar chip_addr,/* I2C device address on the bus */ + uint byte_addr, /* Byte address within I2C device */ + uchar * buffer /* pointer to data buffer */ + ) +{ + u32 temp; + u32 to_count = I2C_DELAY; + u32 op_status = TSI108_I2C_TIMEOUT_ERR; + + /* Check if I2C operation is in progress */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) { + /* Place data into the I2C Tx Register */ + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + + I2C_TX_DATA) = (u32) * buffer; + + /* Set device address and operation */ + temp = + I2C_CNTRL1_I2CWRITE | (byte_addr << 16) | + ((chip_addr & 0x07) << 8) | ((chip_addr >> 3) & 0x0F); + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + + I2C_CNTRL1) = temp; + + /* Issue the write command (at this moment all other parameters + * are 0 (size = 1 byte, lane = 0) + */ + + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + + I2C_CNTRL2) = (I2C_CNTRL2_START); + + op_status = TSI108_I2C_TIMEOUT_ERR; + + /* Wait until operation completed */ + do { + /* Read I2C operation status */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) { + if (0 == (temp & + (I2C_CNTRL2_I2C_CFGERR | + I2C_CNTRL2_I2C_TO_ERR))) { + op_status = TSI108_I2C_SUCCESS; + } else { + /* report detected HW error */ + op_status = TSI108_I2C_IF_ERROR; + + DPRINT (("I2C HW error reported: 0x%02x\n", temp)); + } + + break; + } + + } while (to_count--); + } else { + op_status = TSI108_I2C_IF_BUSY; + + DPRINT (("I2C Transaction start failed: 0x%02x\n", temp)); + } + + return op_status; +} + +/* + * I2C Write interface as defined in "include/i2c.h" : + * chip_addr: I2C chip address, range 0..127 + * byte_addr: Memory or register address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Pointer to data to be written + * len: How many bytes to write + * + * Returns: 0 on success, not 0 on failure + */ + +int i2c_write (uchar chip_addr, uint byte_addr, int alen, uchar * buffer, + int len) +{ + u32 op_status = TSI108_I2C_PARAM_ERR; + + /* Check for valid I2C address */ + if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { + while (len--) { + op_status = + i2c_write_byte (chip_addr, byte_addr++, buffer++); + + if (TSI108_I2C_SUCCESS != op_status) { + DPRINT (("I2C write_byte() failed: 0x%02x (%d left)\n", op_status, len)); + + break; + } + } + } + + return op_status; +} + +/* + * I2C interface function as defined in "include/i2c.h". + * Probe the given I2C chip address by reading single byte from offset 0. + * Returns 0 if a chip responded, not 0 on failure. + */ + +int i2c_probe (uchar chip) +{ + u32 tmp; + + /* + * Try to read the first location of the chip. + * The Tsi108 HW doesn't support sending just the chip address + * and checkong for an back. + */ + return i2c_read (chip, 0, 1, (uchar *)&tmp, 1); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/u8500_i2c.c b/qemu/roms/u-boot/drivers/i2c/u8500_i2c.c new file mode 100644 index 000000000..81ffb8ed7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/u8500_i2c.c @@ -0,0 +1,601 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Basic U-Boot I2C interface for STn8500/DB8500 + * Author: Michael Brandt for ST-Ericsson + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Only 7-bit I2C device addresses are supported. + */ + +#include +#include + +#include "u8500_i2c.h" +#include +#include + +#define U8500_I2C_ENDAD_COUNTER (CONFIG_SYS_HZ/100) /* I2C bus timeout */ +#define U8500_I2C_FIFO_FLUSH_COUNTER 500000 /* flush "timeout" */ +#define U8500_I2C_SCL_FREQ 100000 /* I2C bus clock freq */ +#define U8500_I2C_INPUT_FREQ 48000000 /* Input clock freq */ +#define TX_FIFO_THRESHOLD 0x4 +#define RX_FIFO_THRESHOLD 0x4 +#define SLAVE_SETUP_TIME 14 /* Slave data setup time, 250ns for 48MHz i2c_clk */ + +#define WRITE_FIELD(var, mask, shift, value) \ + (var = ((var & ~(mask)) | ((value) << (shift)))) + +static unsigned int bus_initialized[CONFIG_SYS_U8500_I2C_BUS_MAX]; +static unsigned int i2c_bus_num; +static unsigned int i2c_bus_speed[] = { + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED +}; +static struct u8500_i2c_regs *i2c_dev[] = { + (struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C0_BASE, + (struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C1_BASE, + (struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C2_BASE, + (struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C3_BASE, +}; + +static struct { + int periph; + int pcken; + int kcken; +} i2c_clock_bits[] = { + {3, 3, 3}, /* I2C0 */ + {1, 2, 2}, /* I2C1 */ + {1, 6, 6}, /* I2C2 */ + {2, 0, 0}, /* I2C3 */ +}; + +static void i2c_set_bit(void *reg, u32 mask) +{ + writel(readl(reg) | mask, reg); +} + +static void i2c_clr_bit(void *reg, u32 mask) +{ + writel(readl(reg) & ~mask, reg); +} + +static void i2c_write_field(void *reg, u32 mask, uint shift, u32 value) +{ + writel((readl(reg) & ~mask) | (value << shift), reg); +} + +static int __i2c_set_bus_speed(unsigned int speed) +{ + u32 value; + struct u8500_i2c_regs *i2c_regs; + + i2c_regs = i2c_dev[i2c_bus_num]; + + /* Select standard (100 kbps) speed mode */ + i2c_write_field(&i2c_regs->cr, U8500_I2C_CR_SM, + U8500_I2C_CR_SHIFT_SM, 0x0); + + /* + * Set the Baud Rate Counter 2 value + * Baud rate (standard) = fi2cclk / ( (BRCNT2 x 2) + Foncycle ) + * Foncycle = 0 (no digital filtering) + */ + value = (u32) (U8500_I2C_INPUT_FREQ / (speed * 2)); + i2c_write_field(&i2c_regs->brcr, U8500_I2C_BRCR_BRCNT2, + U8500_I2C_BRCR_SHIFT_BRCNT2, value); + + /* ensure that BRCNT value is zero */ + i2c_write_field(&i2c_regs->brcr, U8500_I2C_BRCR_BRCNT1, + U8500_I2C_BRCR_SHIFT_BRCNT1, 0); + + return U8500_I2C_INPUT_FREQ/(value * 2); +} + +/* + * i2c_init - initialize the i2c bus + * + * speed: bus speed (in HZ) + * slaveaddr: address of device in slave mode + * + * Slave mode is not implemented. + */ +void i2c_init(int speed, int slaveaddr) +{ + struct u8500_i2c_regs *i2c_regs; + + debug("i2c_init bus %d, speed %d\n", i2c_bus_num, speed); + + u8500_clock_enable(i2c_clock_bits[i2c_bus_num].periph, + i2c_clock_bits[i2c_bus_num].pcken, + i2c_clock_bits[i2c_bus_num].kcken); + + i2c_regs = i2c_dev[i2c_bus_num]; + + /* Disable the controller */ + i2c_clr_bit(&i2c_regs->cr, U8500_I2C_CR_PE); + + /* Clear registers */ + writel(0, &i2c_regs->cr); + writel(0, &i2c_regs->scr); + writel(0, &i2c_regs->hsmcr); + writel(0, &i2c_regs->tftr); + writel(0, &i2c_regs->rftr); + writel(0, &i2c_regs->dmar); + + i2c_bus_speed[i2c_bus_num] = __i2c_set_bus_speed(speed); + + /* + * Set our own address. + * Set slave address mode to 7 bit addressing mode + */ + i2c_clr_bit(&i2c_regs->cr, U8500_I2C_CR_SAM); + i2c_write_field(&i2c_regs->scr, U8500_I2C_SCR_ADDR, + U8500_I2C_SCR_SHIFT_ADDR, slaveaddr); + /* Slave Data Set up Time */ + i2c_write_field(&i2c_regs->scr, U8500_I2C_SCR_DATA_SETUP_TIME, + U8500_I2C_SCR_SHIFT_DATA_SETUP_TIME, SLAVE_SETUP_TIME); + + /* Disable the DMA sync logic */ + i2c_write_field(&i2c_regs->cr, U8500_I2C_CR_DMA_SLE, + U8500_I2C_CR_SHIFT_DMA_SLE, 0); + + /* Disable interrupts */ + writel(0, &i2c_regs->imscr); + + /* Configure bus master mode */ + i2c_write_field(&i2c_regs->cr, U8500_I2C_CR_OM, U8500_I2C_CR_SHIFT_OM, + U8500_I2C_BUS_MASTER_MODE); + /* Set FIFO threshold values */ + writel(TX_FIFO_THRESHOLD, &i2c_regs->tftr); + writel(RX_FIFO_THRESHOLD, &i2c_regs->rftr); + + /* Enable the I2C Controller */ + i2c_set_bit(&i2c_regs->cr, U8500_I2C_CR_PE); + + bus_initialized[i2c_bus_num] = 1; +} + + +/* + * loop_till_bit_clear - polls on a bit till it clears + * ioreg: register where you want to check status + * mask: bit mask for the bit you wish to check + * timeout: timeout in ticks/s + */ +static int loop_till_bit_clear(void *io_reg, u32 mask, unsigned long timeout) +{ + unsigned long timebase = get_timer(0); + + do { + if ((readl(io_reg) & mask) == 0x0UL) + return 0; + } while (get_timer(timebase) < timeout); + + debug("loop_till_bit_clear timed out\n"); + return -1; +} + +/* + * loop_till_bit_set - polls on a bit till it is set. + * ioreg: register where you want to check status + * mask: bit mask for the bit you wish to check + * timeout: timeout in ticks/s + */ +static int loop_till_bit_set(void *io_reg, u32 mask, unsigned long timeout) +{ + unsigned long timebase = get_timer(0); + + do { + if ((readl(io_reg) & mask) != 0x0UL) + return 0; + } while (get_timer(timebase) < timeout); + + debug("loop_till_bit_set timed out\n"); + return -1; +} + +/* + * flush_fifo - flush the I2C TX and RX FIFOs + */ +static void flush_fifo(struct u8500_i2c_regs *i2c_regs) +{ + int counter = U8500_I2C_FIFO_FLUSH_COUNTER; + + /* Flush Tx FIFO */ + i2c_set_bit(&i2c_regs->cr, U8500_I2C_CR_FTX); + /* Flush Rx FIFO */ + i2c_set_bit(&i2c_regs->cr, U8500_I2C_CR_FRX); + while (counter--) { + if (!(readl(&i2c_regs->cr) & + (U8500_I2C_CR_FTX | U8500_I2C_CR_FRX))) + break; + } + return; +} + +#ifdef DEBUG +static void print_abort_reason(struct u8500_i2c_regs *i2c_regs) +{ + int cause; + + printf("abort: risr %08x, sr %08x\n", i2c_regs->risr, i2c_regs->sr); + cause = (readl(&i2c_regs->sr) & U8500_I2C_SR_CAUSE) >> + U8500_I2C_SR_SHIFT_CAUSE; + switch (cause) { + case U8500_I2C_NACK_ADDR: + printf("No Ack received after Slave Address xmission\n"); + break; + case U8500_I2C_NACK_DATA: + printf("Valid for MASTER_WRITE: No Ack received " + "during data phase\n"); + break; + case U8500_I2C_ACK_MCODE: + printf("Master recv ack after xmission of master code" + "in hs mode\n"); + break; + case U8500_I2C_ARB_LOST: + printf("Master Lost arbitration\n"); + break; + case U8500_I2C_BERR_START: + printf("Slave restarts\n"); + break; + case U8500_I2C_BERR_STOP: + printf("Slave reset\n"); + break; + case U8500_I2C_OVFL: + printf("Overflow\n"); + break; + default: + printf("Unknown error type\n"); + } +} +#endif + +/* + * i2c_abort - called when a I2C transaction failed + */ +static void i2c_abort(struct u8500_i2c_regs *i2c_regs) +{ +#ifdef DEBUG + print_abort_reason(i2c_regs); +#endif + /* flush RX and TX fifos */ + flush_fifo(i2c_regs); + + /* Acknowledge the Master Transaction Done */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD); + + /* Acknowledge the Master Transaction Done Without Stop */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + + i2c_init(i2c_bus_speed[i2c_bus_num], CONFIG_SYS_I2C_SLAVE); +} + +/* + * write addr, alias index, to I2C bus. + */ +static int i2c_write_addr(struct u8500_i2c_regs *i2c_regs, uint addr, int alen) +{ + while (alen--) { + /* Wait until the Tx Fifo is not full */ + if (loop_till_bit_clear((void *)&i2c_regs->risr, + U8500_I2C_INT_TXFF, + U8500_I2C_ENDAD_COUNTER)) { + i2c_abort(i2c_regs); + return -1; + } + + /* MSB first */ + writeb((addr >> (alen * 8)) & 0xff, &i2c_regs->tfr); + } + + return 0; +} + +/* + * Internal simplified read function: + * i2c_regs: Pointer to I2C registers for current bus + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one register) + * value: Where to put the data + * + * Returns: 0 on success, not 0 on failure + */ +static int i2c_read_byte(struct u8500_i2c_regs *i2c_regs, uchar chip, + uint addr, int alen, uchar *value) +{ + u32 mcr = 0; + + /* Set the address mode to 7 bit */ + WRITE_FIELD(mcr, U8500_I2C_MCR_AM, U8500_I2C_MCR_SHIFT_AM, 1); + + /* Store the slave address in the master control register */ + WRITE_FIELD(mcr, U8500_I2C_MCR_A7, U8500_I2C_MCR_SHIFT_A7, chip); + + if (alen != 0) { + /* Master write operation */ + mcr &= ~(U8500_I2C_MCR_OP); + + /* Configure the Frame length to one byte */ + WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, + U8500_I2C_MCR_SHIFT_LENGTH, 1); + + /* Repeated start, no stop */ + mcr &= ~(U8500_I2C_MCR_STOP); + + /* Write Master Control Register */ + writel(mcr, &i2c_regs->mcr); + + /* send addr/index */ + if (i2c_write_addr(i2c_regs, addr, alen) != 0) + return -1; + + /* Check for the Master Transaction Done Without Stop */ + if (loop_till_bit_set((void *)&i2c_regs->risr, + U8500_I2C_INT_MTDWS, + U8500_I2C_ENDAD_COUNTER)) { + return -1; + } + + /* Acknowledge the Master Transaction Done Without Stop */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + } + + /* Master control configuration for read operation */ + mcr |= U8500_I2C_MCR_OP; + + /* Configure the STOP condition, we read only one byte */ + mcr |= U8500_I2C_MCR_STOP; + + /* Set the frame length to one byte, we support only 1 byte reads */ + WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, U8500_I2C_MCR_SHIFT_LENGTH, 1); + + i2c_write_field(&i2c_regs->mcr, U8500_I2C_MCR_LENGTH_STOP_OP, + U8500_I2C_MCR_SHIFT_LENGTH_STOP_OP, mcr); + + /* + * receive_data_polling + */ + + /* Wait until the Rx FIFO is not empty */ + if (loop_till_bit_clear((void *)&i2c_regs->risr, + U8500_I2C_INT_RXFE, + U8500_I2C_ENDAD_COUNTER)) + return -1; + + /* Read the data byte from Rx FIFO */ + *value = readb(&i2c_regs->rfr); + + /* Wait until the work is done */ + if (loop_till_bit_set((void *)&i2c_regs->risr, U8500_I2C_INT_MTD, + U8500_I2C_ENDAD_COUNTER)) + return -1; + + /* Acknowledge the Master Transaction Done */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD); + + /* If MTD is set, Master Transaction Done Without Stop is set too */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + + return 0; +} + +/* + * Internal simplified write function: + * i2c_regs: Pointer to I2C registers for current bus + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one register) + * data: Where to read the data + * len: How many bytes to write + * + * Returns: 0 on success, not 0 on failure + */ +static int __i2c_write(struct u8500_i2c_regs *i2c_regs, u8 chip, uint addr, + int alen, u8 *data, int len) +{ + int i; + u32 mcr = 0; + + /* Set the address mode to 7 bit */ + WRITE_FIELD(mcr, U8500_I2C_MCR_AM, U8500_I2C_MCR_SHIFT_AM, 1); + + /* Store the slave address in the master control register */ + WRITE_FIELD(mcr, U8500_I2C_MCR_A7, U8500_I2C_MCR_SHIFT_A7, chip); + + /* Write operation */ + mcr &= ~(U8500_I2C_MCR_OP); + + /* Current transaction is terminated by STOP condition */ + mcr |= U8500_I2C_MCR_STOP; + + /* Frame length: addr byte + len */ + WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, U8500_I2C_MCR_SHIFT_LENGTH, + (alen + len)); + + /* Write MCR register */ + writel(mcr, &i2c_regs->mcr); + + if (i2c_write_addr(i2c_regs, addr, alen) != 0) + return -1; + + for (i = 0; i < len; i++) { + /* Wait until the Tx FIFO is not full */ + if (loop_till_bit_clear((void *)&i2c_regs->risr, + U8500_I2C_INT_TXFF, + U8500_I2C_ENDAD_COUNTER)) + return -1; + + /* it is a 32 bit register with upper 24 reserved R/O */ + writeb(data[i], &i2c_regs->tfr); + } + + /* Check for Master Transaction Done */ + if (loop_till_bit_set((void *)&i2c_regs->risr, + U8500_I2C_INT_MTD, + U8500_I2C_ENDAD_COUNTER)) { + printf("i2c_write_byte error2: risr %08x\n", + i2c_regs->risr); + return -1; + } + + /* Acknowledge Master Transaction Done */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD); + + /* Acknowledge Master Transaction Done Without Stop */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + + return 0; +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(uchar chip) +{ + u32 mcr = 0; + struct u8500_i2c_regs *i2c_regs; + + if (chip == CONFIG_SYS_I2C_SLAVE) + return 1; + + i2c_regs = i2c_dev[i2c_bus_num]; + + /* Set the address mode to 7 bit */ + WRITE_FIELD(mcr, U8500_I2C_MCR_AM, U8500_I2C_MCR_SHIFT_AM, 1); + + /* Store the slave address in the master control register */ + WRITE_FIELD(mcr, U8500_I2C_MCR_A10, U8500_I2C_MCR_SHIFT_A7, chip); + + /* Read operation */ + mcr |= U8500_I2C_MCR_OP; + + /* Set the frame length to one byte */ + WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, U8500_I2C_MCR_SHIFT_LENGTH, 1); + + /* Current transaction is terminated by STOP condition */ + mcr |= U8500_I2C_MCR_STOP; + + /* Write MCR register */ + writel(mcr, &i2c_regs->mcr); + + /* Wait until the Rx Fifo is not empty */ + if (loop_till_bit_clear((void *)&i2c_regs->risr, + U8500_I2C_INT_RXFE, + U8500_I2C_ENDAD_COUNTER)) { + i2c_abort(i2c_regs); + return -1; + } + + flush_fifo(i2c_regs); + + /* Acknowledge the Master Transaction Done */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD); + + /* Acknowledge the Master Transaction Done Without Stop */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + + return 0; +} + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int i; + int rc; + struct u8500_i2c_regs *i2c_regs; + + if (alen > 2) { + debug("I2C read: addr len %d not supported\n", alen); + return 1; + } + + i2c_regs = i2c_dev[i2c_bus_num]; + + for (i = 0; i < len; i++) { + rc = i2c_read_byte(i2c_regs, chip, addr + i, alen, &buffer[i]); + if (rc != 0) { + debug("I2C read: I/O error: %d\n", rc); + i2c_abort(i2c_regs); + return rc; + } + } + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int rc; + struct u8500_i2c_regs *i2c_regs; + i2c_regs = i2c_dev[i2c_bus_num]; + + rc = __i2c_write(i2c_regs, chip, addr, alen, buffer, + len); + if (rc != 0) { + debug("I2C write: I/O error\n"); + i2c_abort(i2c_regs); + return rc; + } + return 0; +} + +int i2c_set_bus_num(unsigned int bus) +{ + if (bus > ARRAY_SIZE(i2c_dev) - 1) { + debug("i2c_set_bus_num: only up to bus %d supported\n", + ARRAY_SIZE(i2c_dev)-1); + return -1; + } + + i2c_bus_num = bus; + + if (!bus_initialized[i2c_bus_num]) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return 0; +} + +int i2c_set_bus_speed(unsigned int speed) +{ + + if (speed > U8500_I2C_MAX_STANDARD_SCL) { + debug("i2c_set_bus_speed: only up to %d supported\n", + U8500_I2C_MAX_STANDARD_SCL); + return -1; + } + + /* sets as side effect i2c_bus_speed[i2c_bus_num] */ + i2c_init(speed, CONFIG_SYS_I2C_SLAVE); + + return 0; +} + +unsigned int i2c_get_bus_num(void) +{ + return i2c_bus_num; +} + +unsigned int i2c_get_bus_speed(void) +{ + return i2c_bus_speed[i2c_bus_num]; +} diff --git a/qemu/roms/u-boot/drivers/i2c/u8500_i2c.h b/qemu/roms/u-boot/drivers/i2c/u8500_i2c.h new file mode 100644 index 000000000..ceecdeede --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/u8500_i2c.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) ST-Ericsson SA 2009 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _U8500_I2C_H_ +#define _U8500_I2C_H_ + +#include +#include +#include +#include + +struct u8500_i2c_regs { + u32 cr; /* Control Register 0x00 */ + u32 scr; /* Slave Address Register 0x04 */ + u32 hsmcr; /* HS Master code Register 0x08 */ + u32 mcr; /* Master Control Register 0x0C */ + u32 tfr; /* Transmit Fifo Register 0x10 */ + u32 sr; /* Status Register 0x14 */ + u32 rfr; /* Receiver Fifo Register 0x18 */ + u32 tftr; /* Transmit Fifo Threshold Register 0x1C */ + u32 rftr; /* Receiver Fifo Threshold Register 0x20 */ + u32 dmar; /* DMA register 0x24 */ + u32 brcr; /* Baud Rate Counter Register 0x28 */ + u32 imscr; /* Interrupt Mask Set and Clear Register 0x2C */ + u32 risr; /* Raw interrupt status register 0x30 */ + u32 misr; /* Masked interrupt status register 0x34 */ + u32 icr; /* Interrupt Set and Clear Register 0x38 */ + u32 reserved_1[(0xFE0 - 0x3c) >> 2]; /* Reserved 0x03C to 0xFE0 */ + u32 periph_id_0; /* peripheral ID 0 0xFE0 */ + u32 periph_id_1; /* peripheral ID 1 0xFE4 */ + u32 periph_id_2; /* peripheral ID 2 0xFE8 */ + u32 periph_id_3; /* peripheral ID 3 0xFEC */ + u32 cell_id_0; /* I2C cell ID 0 0xFF0 */ + u32 cell_id_1; /* I2C cell ID 1 0xFF4 */ + u32 cell_id_2; /* I2C cell ID 2 0xFF8 */ + u32 cell_id_3; /* I2C cell ID 3 0xFFC */ +}; + + +/* Control Register */ + +/* Mask values for control register mask */ +#define U8500_I2C_CR_PE 0x0001 /* Peripheral enable */ +#define U8500_I2C_CR_OM 0x0006 /* Operation mode */ +#define U8500_I2C_CR_SAM 0x0008 /* Slave Addressing mode */ +#define U8500_I2C_CR_SM 0x0030 /* Speed mode */ +#define U8500_I2C_CR_SGCM 0x0040 /* Slave General call mode */ +#define U8500_I2C_CR_FTX 0x0080 /* Flush Transmit */ +#define U8500_I2C_CR_FRX 0x0100 /* Flush Receive */ +#define U8500_I2C_CR_DMA_TX_EN 0x0200 /* DMA TX Enable */ +#define U8500_I2C_CR_DMA_RX_EN 0x0400 /* DMA Rx Enable */ +#define U8500_I2C_CR_DMA_SLE 0x0800 /* DMA Synchronization Logic enable */ +#define U8500_I2C_CR_LM 0x1000 /* Loop back mode */ +#define U8500_I2C_CR_FON 0x6000 /* Filtering On */ + +/* shift valus for control register bit fields */ +#define U8500_I2C_CR_SHIFT_PE 0 /* Peripheral enable */ +#define U8500_I2C_CR_SHIFT_OM 1 /* Operation mode */ +#define U8500_I2C_CR_SHIFT_SAM 3 /* Slave Addressing mode */ +#define U8500_I2C_CR_SHIFT_SM 4 /* Speed mode */ +#define U8500_I2C_CR_SHIFT_SGCM 6 /* Slave General call mode */ +#define U8500_I2C_CR_SHIFT_FTX 7 /* Flush Transmit */ +#define U8500_I2C_CR_SHIFT_FRX 8 /* Flush Receive */ +#define U8500_I2C_CR_SHIFT_DMA_TX_EN 9 /* DMA TX Enable */ +#define U8500_I2C_CR_SHIFT_DMA_RX_EN 10 /* DMA Rx Enable */ +#define U8500_I2C_CR_SHIFT_DMA_SLE 11 /* DMA Synch Logic enable */ +#define U8500_I2C_CR_SHIFT_LM 12 /* Loop back mode */ +#define U8500_I2C_CR_SHIFT_FON 13 /* Filtering On */ + +/* bus operation modes */ +#define U8500_I2C_BUS_SLAVE_MODE 0 +#define U8500_I2C_BUS_MASTER_MODE 1 +#define U8500_I2C_BUS_MASTER_SLAVE_MODE 2 + + +/* Slave control register*/ + +/* Mask values slave control register */ +#define U8500_I2C_SCR_ADDR 0x3FF +#define U8500_I2C_SCR_DATA_SETUP_TIME 0xFFFF0000 + +/* Shift values for Slave control register */ +#define U8500_I2C_SCR_SHIFT_ADDR 0 +#define U8500_I2C_SCR_SHIFT_DATA_SETUP_TIME 16 + + +/* Master Control Register */ + +/* Mask values for Master control register */ +#define U8500_I2C_MCR_OP 0x00000001 /* Operation */ +#define U8500_I2C_MCR_A7 0x000000FE /* LSB bits of Address */ +#define U8500_I2C_MCR_EA10 0x00000700 /* Extended Address */ +#define U8500_I2C_MCR_SB 0x00000800 /* Start byte procedure */ +#define U8500_I2C_MCR_AM 0x00003000 /* Address type */ +#define U8500_I2C_MCR_STOP 0x00004000 /* stop condition */ +#define U8500_I2C_MCR_LENGTH 0x03FF8000 /* Frame length */ +#define U8500_I2C_MCR_A10 0x000007FE /* Enable 10 bit address */ +/* mask for length field,stop and operation */ +#define U8500_I2C_MCR_LENGTH_STOP_OP 0x3FFC001 + +/* Shift values for Master control values */ +#define U8500_I2C_MCR_SHIFT_OP 0 /* Operation */ +#define U8500_I2C_MCR_SHIFT_A7 1 /* LSB bits of Address */ +#define U8500_I2C_MCR_SHIFT_EA10 8 /* Extended Address */ +#define U8500_I2C_MCR_SHIFT_SB 11 /* Start byte procedure */ +#define U8500_I2C_MCR_SHIFT_AM 12 /* Address type */ +#define U8500_I2C_MCR_SHIFT_STOP 14 /* stop condition */ +#define U8500_I2C_MCR_SHIFT_LENGTH 15 /* Frame length */ +#define U8500_I2C_MCR_SHIFT_A10 1 /* Enable 10 bit address */ + +#define U8500_I2C_MCR_SHIFT_LENGTH_STOP_OP 0 + + +/* Status Register */ + +/* Mask values for Status register */ +#define U8500_I2C_SR_OP 0x00000003 /* Operation */ +#define U8500_I2C_SR_STATUS 0x0000000C /* Controller Status */ +#define U8500_I2C_SR_CAUSE 0x00000070 /* Abort Cause */ +#define U8500_I2C_SR_TYPE 0x00000180 /* Receive Type */ +#define U8500_I2C_SR_LENGTH 0x000FF700 /* Transfer length */ + +/* Shift values for Status register */ +#define U8500_I2C_SR_SHIFT_OP 0 /* Operation */ +#define U8500_I2C_SR_SHIFT_STATUS 2 /* Controller Status */ +#define U8500_I2C_SR_SHIFT_CAUSE 4 /* Abort Cause */ +#define U8500_I2C_SR_SHIFT_TYPE 7 /* Receive Type */ +#define U8500_I2C_SR_SHIFT_LENGTH 9 /* Transfer length */ + +/* abort cause */ +#define U8500_I2C_NACK_ADDR 0 +#define U8500_I2C_NACK_DATA 1 +#define U8500_I2C_ACK_MCODE 2 +#define U8500_I2C_ARB_LOST 3 +#define U8500_I2C_BERR_START 4 +#define U8500_I2C_BERR_STOP 5 +#define U8500_I2C_OVFL 6 + + +/* Baud rate counter registers */ + +/* Mask values for Baud rate counter register */ +#define U8500_I2C_BRCR_BRCNT2 0xFFFF /* Baud Rate Cntr BRCR for HS */ +#define U8500_I2C_BRCR_BRCNT1 0xFFFF0000 /* BRCR for Standard and Fast */ + +/* Shift values for the Baud rate counter register */ +#define U8500_I2C_BRCR_SHIFT_BRCNT2 0 +#define U8500_I2C_BRCR_SHIFT_BRCNT1 16 + + +/* Interrupt Register */ + +/* Mask values for Interrupt registers */ +#define U8500_I2C_INT_TXFE 0x00000001 /* Tx fifo empty */ +#define U8500_I2C_INT_TXFNE 0x00000002 /* Tx Fifo nearly empty */ +#define U8500_I2C_INT_TXFF 0x00000004 /* Tx Fifo Full */ +#define U8500_I2C_INT_TXFOVR 0x00000008 /* Tx Fifo over run */ +#define U8500_I2C_INT_RXFE 0x00000010 /* Rx Fifo Empty */ +#define U8500_I2C_INT_RXFNF 0x00000020 /* Rx Fifo nearly empty */ +#define U8500_I2C_INT_RXFF 0x00000040 /* Rx Fifo Full */ +#define U8500_I2C_INT_RFSR 0x00010000 /* Read From slave request */ +#define U8500_I2C_INT_RFSE 0x00020000 /* Read from slave empty */ +#define U8500_I2C_INT_WTSR 0x00040000 /* Write to Slave request */ +#define U8500_I2C_INT_MTD 0x00080000 /* Master Transcation Done*/ +#define U8500_I2C_INT_STD 0x00100000 /* Slave Transaction Done */ +#define U8500_I2C_INT_MAL 0x01000000 /* Master Arbitation Lost */ +#define U8500_I2C_INT_BERR 0x02000000 /* Bus Error */ +#define U8500_I2C_INT_MTDWS 0x10000000 /* Master Tran Done wo/ Stop */ + +/* Max clocks (Hz) */ +#define U8500_I2C_MAX_STANDARD_SCL 100000 +#define U8500_I2C_MAX_FAST_SCL 400000 +#define U8500_I2C_MAX_HIGH_SPEED_SCL 3400000 + +#endif /* _U8500_I2C_H_ */ diff --git a/qemu/roms/u-boot/drivers/i2c/zynq_i2c.c b/qemu/roms/u-boot/drivers/i2c/zynq_i2c.c new file mode 100644 index 000000000..b3264af45 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/zynq_i2c.c @@ -0,0 +1,307 @@ +/* + * Driver for the Zynq-7000 PS I2C controller + * IP from Cadence (ID T-CS-PE-0007-100, Version R1p10f2) + * + * Author: Joe Hershberger + * Copyright (c) 2012 Joe Hershberger. + * + * Copyright (c) 2012-2013 Xilinx, Michal Simek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/* i2c register set */ +struct zynq_i2c_registers { + u32 control; + u32 status; + u32 address; + u32 data; + u32 interrupt_status; + u32 transfer_size; + u32 slave_mon_pause; + u32 time_out; + u32 interrupt_mask; + u32 interrupt_enable; + u32 interrupt_disable; +}; + +/* Control register fields */ +#define ZYNQ_I2C_CONTROL_RW 0x00000001 +#define ZYNQ_I2C_CONTROL_MS 0x00000002 +#define ZYNQ_I2C_CONTROL_NEA 0x00000004 +#define ZYNQ_I2C_CONTROL_ACKEN 0x00000008 +#define ZYNQ_I2C_CONTROL_HOLD 0x00000010 +#define ZYNQ_I2C_CONTROL_SLVMON 0x00000020 +#define ZYNQ_I2C_CONTROL_CLR_FIFO 0x00000040 +#define ZYNQ_I2C_CONTROL_DIV_B_SHIFT 8 +#define ZYNQ_I2C_CONTROL_DIV_B_MASK 0x00003F00 +#define ZYNQ_I2C_CONTROL_DIV_A_SHIFT 14 +#define ZYNQ_I2C_CONTROL_DIV_A_MASK 0x0000C000 + +/* Status register values */ +#define ZYNQ_I2C_STATUS_RXDV 0x00000020 +#define ZYNQ_I2C_STATUS_TXDV 0x00000040 +#define ZYNQ_I2C_STATUS_RXOVF 0x00000080 +#define ZYNQ_I2C_STATUS_BA 0x00000100 + +/* Interrupt register fields */ +#define ZYNQ_I2C_INTERRUPT_COMP 0x00000001 +#define ZYNQ_I2C_INTERRUPT_DATA 0x00000002 +#define ZYNQ_I2C_INTERRUPT_NACK 0x00000004 +#define ZYNQ_I2C_INTERRUPT_TO 0x00000008 +#define ZYNQ_I2C_INTERRUPT_SLVRDY 0x00000010 +#define ZYNQ_I2C_INTERRUPT_RXOVF 0x00000020 +#define ZYNQ_I2C_INTERRUPT_TXOVF 0x00000040 +#define ZYNQ_I2C_INTERRUPT_RXUNF 0x00000080 +#define ZYNQ_I2C_INTERRUPT_ARBLOST 0x00000200 + +#define ZYNQ_I2C_FIFO_DEPTH 16 +#define ZYNQ_I2C_TRANSFERT_SIZE_MAX 255 /* Controller transfer limit */ + +static struct zynq_i2c_registers *i2c_select(struct i2c_adapter *adap) +{ + return adap->hwadapnr ? + /* Zynq PS I2C1 */ + (struct zynq_i2c_registers *)ZYNQ_I2C_BASEADDR1 : + /* Zynq PS I2C0 */ + (struct zynq_i2c_registers *)ZYNQ_I2C_BASEADDR0; +} + +/* I2C init called by cmd_i2c when doing 'i2c reset'. */ +static void zynq_i2c_init(struct i2c_adapter *adap, int requested_speed, + int slaveadd) +{ + struct zynq_i2c_registers *zynq_i2c = i2c_select(adap); + + /* 111MHz / ( (3 * 17) * 22 ) = ~100KHz */ + writel((16 << ZYNQ_I2C_CONTROL_DIV_B_SHIFT) | + (2 << ZYNQ_I2C_CONTROL_DIV_A_SHIFT), &zynq_i2c->control); + + /* Enable master mode, ack, and 7-bit addressing */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_MS | + ZYNQ_I2C_CONTROL_ACKEN | ZYNQ_I2C_CONTROL_NEA); +} + +#ifdef DEBUG +static void zynq_i2c_debug_status(struct zynq_i2c_registers *zynq_i2c) +{ + int int_status; + int status; + int_status = readl(&zynq_i2c->interrupt_status); + + status = readl(&zynq_i2c->status); + if (int_status || status) { + debug("Status: "); + if (int_status & ZYNQ_I2C_INTERRUPT_COMP) + debug("COMP "); + if (int_status & ZYNQ_I2C_INTERRUPT_DATA) + debug("DATA "); + if (int_status & ZYNQ_I2C_INTERRUPT_NACK) + debug("NACK "); + if (int_status & ZYNQ_I2C_INTERRUPT_TO) + debug("TO "); + if (int_status & ZYNQ_I2C_INTERRUPT_SLVRDY) + debug("SLVRDY "); + if (int_status & ZYNQ_I2C_INTERRUPT_RXOVF) + debug("RXOVF "); + if (int_status & ZYNQ_I2C_INTERRUPT_TXOVF) + debug("TXOVF "); + if (int_status & ZYNQ_I2C_INTERRUPT_RXUNF) + debug("RXUNF "); + if (int_status & ZYNQ_I2C_INTERRUPT_ARBLOST) + debug("ARBLOST "); + if (status & ZYNQ_I2C_STATUS_RXDV) + debug("RXDV "); + if (status & ZYNQ_I2C_STATUS_TXDV) + debug("TXDV "); + if (status & ZYNQ_I2C_STATUS_RXOVF) + debug("RXOVF "); + if (status & ZYNQ_I2C_STATUS_BA) + debug("BA "); + debug("TS%d ", readl(&zynq_i2c->transfer_size)); + debug("\n"); + } +} +#endif + +/* Wait for an interrupt */ +static u32 zynq_i2c_wait(struct zynq_i2c_registers *zynq_i2c, u32 mask) +{ + int timeout, int_status; + + for (timeout = 0; timeout < 100; timeout++) { + udelay(100); + int_status = readl(&zynq_i2c->interrupt_status); + if (int_status & mask) + break; + } +#ifdef DEBUG + zynq_i2c_debug_status(zynq_i2c); +#endif + /* Clear interrupt status flags */ + writel(int_status & mask, &zynq_i2c->interrupt_status); + + return int_status & mask; +} + +/* + * I2C probe called by cmd_i2c when doing 'i2c probe'. + * Begin read, nak data byte, end. + */ +static int zynq_i2c_probe(struct i2c_adapter *adap, u8 dev) +{ + struct zynq_i2c_registers *zynq_i2c = i2c_select(adap); + + /* Attempt to read a byte */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_RW); + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + writel(0xFF, &zynq_i2c->interrupt_status); + writel(dev, &zynq_i2c->address); + writel(1, &zynq_i2c->transfer_size); + + return (zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP | + ZYNQ_I2C_INTERRUPT_NACK) & + ZYNQ_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT; +} + +/* + * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c + * Begin write, send address byte(s), begin read, receive data bytes, end. + */ +static int zynq_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr, + int alen, u8 *data, int length) +{ + u32 status; + u32 i = 0; + u8 *cur_data = data; + struct zynq_i2c_registers *zynq_i2c = i2c_select(adap); + + /* Check the hardware can handle the requested bytes */ + if ((length < 0) || (length > ZYNQ_I2C_TRANSFERT_SIZE_MAX)) + return -EINVAL; + + /* Write the register address */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_HOLD); + /* + * Temporarily disable restart (by clearing hold) + * It doesn't seem to work. + */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + writel(0xFF, &zynq_i2c->interrupt_status); + if (alen) { + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW); + writel(dev, &zynq_i2c->address); + while (alen--) + writel(addr >> (8 * alen), &zynq_i2c->data); + + /* Wait for the address to be sent */ + if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + debug("Device acked address\n"); + } + + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_RW); + /* Start reading data */ + writel(dev, &zynq_i2c->address); + writel(length, &zynq_i2c->transfer_size); + + /* Wait for data */ + do { + status = zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP | + ZYNQ_I2C_INTERRUPT_DATA); + if (!status) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + debug("Read %d bytes\n", + length - readl(&zynq_i2c->transfer_size)); + for (; i < length - readl(&zynq_i2c->transfer_size); i++) + *(cur_data++) = readl(&zynq_i2c->data); + } while (readl(&zynq_i2c->transfer_size) != 0); + /* All done... release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + +#ifdef DEBUG + zynq_i2c_debug_status(zynq_i2c); +#endif + return 0; +} + +/* + * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c + * Begin write, send address byte(s), send data bytes, end. + */ +static int zynq_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr, + int alen, u8 *data, int length) +{ + u8 *cur_data = data; + struct zynq_i2c_registers *zynq_i2c = i2c_select(adap); + + /* Write the register address */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_HOLD); + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW); + writel(0xFF, &zynq_i2c->interrupt_status); + writel(dev, &zynq_i2c->address); + if (alen) { + while (alen--) + writel(addr >> (8 * alen), &zynq_i2c->data); + /* Start the tranfer */ + if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + debug("Device acked address\n"); + } + + while (length--) { + writel(*(cur_data++), &zynq_i2c->data); + if (readl(&zynq_i2c->transfer_size) == ZYNQ_I2C_FIFO_DEPTH) { + if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, + ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + } + } + + /* All done... release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + /* Wait for the address and data to be sent */ + if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) + return -ETIMEDOUT; + return 0; +} + +static unsigned int zynq_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + if (speed != 1000000) + return -EINVAL; + + return 0; +} + +U_BOOT_I2C_ADAP_COMPLETE(zynq_0, zynq_i2c_init, zynq_i2c_probe, zynq_i2c_read, + zynq_i2c_write, zynq_i2c_set_bus_speed, + CONFIG_SYS_I2C_ZYNQ_SPEED, CONFIG_SYS_I2C_ZYNQ_SLAVE, + 0) +U_BOOT_I2C_ADAP_COMPLETE(zynq_1, zynq_i2c_init, zynq_i2c_probe, zynq_i2c_read, + zynq_i2c_write, zynq_i2c_set_bus_speed, + CONFIG_SYS_I2C_ZYNQ_SPEED, CONFIG_SYS_I2C_ZYNQ_SLAVE, + 1) diff --git a/qemu/roms/u-boot/drivers/input/Makefile b/qemu/roms/u-boot/drivers/input/Makefile new file mode 100644 index 000000000..a8e9be2f7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/Makefile @@ -0,0 +1,16 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_I8042_KBD) += i8042.o +obj-$(CONFIG_TEGRA_KEYBOARD) += tegra-kbc.o +obj-$(CONFIG_CROS_EC_KEYB) += cros_ec_keyb.o +ifdef CONFIG_PS2KBD +obj-y += keyboard.o pc_keyb.o +obj-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o +endif +obj-y += input.o +obj-$(CONFIG_OF_CONTROL) += key_matrix.o diff --git a/qemu/roms/u-boot/drivers/input/cros_ec_keyb.c b/qemu/roms/u-boot/drivers/input/cros_ec_keyb.c new file mode 100644 index 000000000..a2501e020 --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/cros_ec_keyb.c @@ -0,0 +1,272 @@ +/* + * Chromium OS Matrix Keyboard + * + * Copyright (c) 2012 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +enum { + KBC_MAX_KEYS = 8, /* Maximum keys held down at once */ +}; + +static struct keyb { + struct cros_ec_dev *dev; /* The CROS_EC device */ + struct input_config input; /* The input layer */ + struct key_matrix matrix; /* The key matrix layer */ + int key_rows; /* Number of keyboard rows */ + int key_cols; /* Number of keyboard columns */ + unsigned int repeat_delay_ms; /* Time before autorepeat starts */ + unsigned int repeat_rate_ms; /* Autorepeat rate in ms */ + int ghost_filter; /* 1 to enable ghost filter, else 0 */ + int inited; /* 1 if keyboard is ready */ +} config; + + +/** + * Check the keyboard controller and return a list of key matrix positions + * for which a key is pressed + * + * @param config Keyboard config + * @param keys List of keys that we have detected + * @param max_count Maximum number of keys to return + * @param samep Set to true if this scan repeats the last, else false + * @return number of pressed keys, 0 for none, -EIO on error + */ +static int check_for_keys(struct keyb *config, + struct key_matrix_key *keys, int max_count, + bool *samep) +{ + struct key_matrix_key *key; + static struct mbkp_keyscan last_scan; + static bool last_scan_valid; + struct mbkp_keyscan scan; + unsigned int row, col, bit, data; + int num_keys; + + if (cros_ec_scan_keyboard(config->dev, &scan)) { + debug("%s: keyboard scan failed\n", __func__); + return -EIO; + } + *samep = last_scan_valid && !memcmp(&last_scan, &scan, sizeof(scan)); + + /* + * This is a bit odd. The EC has no way to tell us that it has run + * out of key scans. It just returns the same scan over and over + * again. So the only way to detect that we have run out is to detect + * that this scan is the same as the last. + */ + last_scan_valid = true; + memcpy(&last_scan, &scan, sizeof(last_scan)); + + for (col = num_keys = bit = 0; col < config->matrix.num_cols; + col++) { + for (row = 0; row < config->matrix.num_rows; row++) { + unsigned int mask = 1 << (bit & 7); + + data = scan.data[bit / 8]; + if ((data & mask) && num_keys < max_count) { + key = keys + num_keys++; + key->row = row; + key->col = col; + key->valid = 1; + } + bit++; + } + } + + return num_keys; +} + +/** + * Test if keys are available to be read + * + * @return 0 if no keys available, 1 if keys are available + */ +static int kbd_tstc(void) +{ + /* Just get input to do this for us */ + return config.inited ? input_tstc(&config.input) : 0; +} + +/** + * Read a key + * + * @return ASCII key code, or 0 if no key, or -1 if error + */ +static int kbd_getc(void) +{ + /* Just get input to do this for us */ + return config.inited ? input_getc(&config.input) : 0; +} + +/** + * Check the keyboard, and send any keys that are pressed. + * + * This is called by input_tstc() and input_getc() when they need more + * characters + * + * @param input Input configuration + * @return 1, to indicate that we have something to look at + */ +int cros_ec_kbc_check(struct input_config *input) +{ + static struct key_matrix_key last_keys[KBC_MAX_KEYS]; + static int last_num_keys; + struct key_matrix_key keys[KBC_MAX_KEYS]; + int keycodes[KBC_MAX_KEYS]; + int num_keys, num_keycodes; + int irq_pending, sent; + bool same = false; + + /* + * Loop until the EC has no more keyscan records, or we have + * received at least one character. This means we know that tstc() + * will always return non-zero if keys have been pressed. + * + * Without this loop, a key release (which generates no new ascii + * characters) will cause us to exit this function, and just tstc() + * may return 0 before all keys have been read from the EC. + */ + do { + irq_pending = cros_ec_interrupt_pending(config.dev); + if (irq_pending) { + num_keys = check_for_keys(&config, keys, KBC_MAX_KEYS, + &same); + if (num_keys < 0) + return 0; + last_num_keys = num_keys; + memcpy(last_keys, keys, sizeof(keys)); + } else { + /* + * EC doesn't want to be asked, so use keys from last + * time. + */ + num_keys = last_num_keys; + memcpy(keys, last_keys, sizeof(keys)); + } + + if (num_keys < 0) + return -1; + num_keycodes = key_matrix_decode(&config.matrix, keys, + num_keys, keycodes, KBC_MAX_KEYS); + sent = input_send_keycodes(input, keycodes, num_keycodes); + + /* + * For those ECs without an interrupt, stop scanning when we + * see that the scan is the same as last time. + */ + if ((irq_pending < 0) && same) + break; + } while (irq_pending && !sent); + + return 1; +} + +/** + * Decode MBKP keyboard details from the device tree + * + * @param blob Device tree blob + * @param node Node to decode from + * @param config Configuration data read from fdt + * @return 0 if ok, -1 on error + */ +static int cros_ec_keyb_decode_fdt(const void *blob, int node, + struct keyb *config) +{ + /* + * Get keyboard rows and columns - at present we are limited to + * 8 columns by the protocol (one byte per row scan) + */ + config->key_rows = fdtdec_get_int(blob, node, "google,key-rows", 0); + config->key_cols = fdtdec_get_int(blob, node, "google,key-columns", 0); + if (!config->key_rows || !config->key_cols || + config->key_rows * config->key_cols / 8 + > CROS_EC_KEYSCAN_COLS) { + debug("%s: Invalid key matrix size %d x %d\n", __func__, + config->key_rows, config->key_cols); + return -1; + } + config->repeat_delay_ms = fdtdec_get_int(blob, node, + "google,repeat-delay-ms", 0); + config->repeat_rate_ms = fdtdec_get_int(blob, node, + "google,repeat-rate-ms", 0); + config->ghost_filter = fdtdec_get_bool(blob, node, + "google,ghost-filter"); + return 0; +} + +/** + * Set up the keyboard. This is called by the stdio device handler. + * + * We want to do this init when the keyboard is actually used rather than + * at start-up, since keyboard input may not currently be selected. + * + * @return 0 if ok, -1 on error + */ +static int cros_ec_init_keyboard(void) +{ + const void *blob = gd->fdt_blob; + int node; + + config.dev = board_get_cros_ec_dev(); + if (!config.dev) { + debug("%s: no cros_ec device: cannot init keyboard\n", + __func__); + return -1; + } + node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_CROS_EC_KEYB); + if (node < 0) { + debug("%s: Node not found\n", __func__); + return -1; + } + if (cros_ec_keyb_decode_fdt(blob, node, &config)) + return -1; + input_set_delays(&config.input, config.repeat_delay_ms, + config.repeat_rate_ms); + if (key_matrix_init(&config.matrix, config.key_rows, + config.key_cols, config.ghost_filter)) { + debug("%s: cannot init key matrix\n", __func__); + return -1; + } + if (key_matrix_decode_fdt(&config.matrix, gd->fdt_blob, node)) { + debug("%s: Could not decode key matrix from fdt\n", __func__); + return -1; + } + config.inited = 1; + debug("%s: Matrix keyboard %dx%d ready\n", __func__, config.key_rows, + config.key_cols); + + return 0; +} + +int drv_keyboard_init(void) +{ + struct stdio_dev dev; + + if (input_init(&config.input, 0)) { + debug("%s: Cannot set up input\n", __func__); + return -1; + } + config.input.read_keys = cros_ec_kbc_check; + + memset(&dev, '\0', sizeof(dev)); + strcpy(dev.name, "cros-ec-keyb"); + dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + dev.getc = kbd_getc; + dev.tstc = kbd_tstc; + dev.start = cros_ec_init_keyboard; + + /* Register the device. cros_ec_init_keyboard() will be called soon */ + return input_stdio_register(&dev); +} diff --git a/qemu/roms/u-boot/drivers/input/i8042.c b/qemu/roms/u-boot/drivers/input/i8042.c new file mode 100644 index 000000000..35fa0bb50 --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/i8042.c @@ -0,0 +1,706 @@ +/* + * (C) Copyright 2002 ELTEC Elektronik AG + * Frank Gottschling + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* i8042.c - Intel 8042 keyboard driver routines */ + +/* includes */ + +#include +#include + +#ifdef CONFIG_USE_CPCIDVI +extern u8 gt_cpcidvi_in8(u32 offset); +extern void gt_cpcidvi_out8(u32 offset, u8 data); + +#define in8(a) gt_cpcidvi_in8(a) +#define out8(a, b) gt_cpcidvi_out8(a, b) +#endif + +#include + +/* defines */ + +#ifdef CONFIG_CONSOLE_CURSOR +extern void console_cursor(int state); +static int blinkCount = CONFIG_SYS_CONSOLE_BLINK_COUNT; +static int cursor_state; +#endif + +/* locals */ + +static int kbd_input = -1; /* no input yet */ +static int kbd_mapping = KBD_US; /* default US keyboard */ +static int kbd_flags = NORMAL; /* after reset */ +static int kbd_state; /* unshift code */ + +static void kbd_conv_char(unsigned char scan_code); +static void kbd_led_set(void); +static void kbd_normal(unsigned char scan_code); +static void kbd_shift(unsigned char scan_code); +static void kbd_ctrl(unsigned char scan_code); +static void kbd_num(unsigned char scan_code); +static void kbd_caps(unsigned char scan_code); +static void kbd_scroll(unsigned char scan_code); +static void kbd_alt(unsigned char scan_code); +static int kbd_input_empty(void); +static int kbd_reset(void); + +static unsigned char kbd_fct_map[144] = { + /* kbd_fct_map table for scan code */ + 0, AS, AS, AS, AS, AS, AS, AS, /* scan 0- 7 */ + AS, AS, AS, AS, AS, AS, AS, AS, /* scan 8- F */ + AS, AS, AS, AS, AS, AS, AS, AS, /* scan 10-17 */ + AS, AS, AS, AS, AS, CN, AS, AS, /* scan 18-1F */ + AS, AS, AS, AS, AS, AS, AS, AS, /* scan 20-27 */ + AS, AS, SH, AS, AS, AS, AS, AS, /* scan 28-2F */ + AS, AS, AS, AS, AS, AS, SH, AS, /* scan 30-37 */ + AS, AS, CP, 0, 0, 0, 0, 0, /* scan 38-3F */ + 0, 0, 0, 0, 0, NM, ST, ES, /* scan 40-47 */ + ES, ES, ES, ES, ES, ES, ES, ES, /* scan 48-4F */ + ES, ES, ES, ES, 0, 0, AS, 0, /* scan 50-57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */ + AS, 0, 0, AS, 0, 0, AS, 0, /* scan 70-77 */ + 0, AS, 0, 0, 0, AS, 0, 0, /* scan 78-7F */ + AS, CN, AS, AS, AK, ST, EX, EX, /* enhanced */ + AS, EX, EX, AS, EX, AS, EX, EX /* enhanced */ + }; + +static unsigned char kbd_key_map[2][5][144] = { + { /* US keyboard */ + { /* unshift code */ + 0, 0x1b, '1', '2', '3', '4', '5', '6', /* scan 0- 7 */ + '7', '8', '9', '0', '-', '=', 0x08, '\t', /* scan 8- F */ + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', /* scan 10-17 */ + 'o', 'p', '[', ']', '\r', CN, 'a', 's', /* scan 18-1F */ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* scan 20-27 */ + '\'', '`', SH, '\\', 'z', 'x', 'c', 'v', /* scan 28-2F */ + 'b', 'n', 'm', ',', '.', '/', SH, '*', /* scan 30-37 */ + ' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */ + 0, 0, 0, 0, 0, NM, ST, '7', /* scan 40-47 */ + '8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */ + '2', '3', '0', '.', 0, 0, 0, 0, /* scan 50-57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */ + '\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */ + 0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */ + }, + { /* shift code */ + 0, 0x1b, '!', '@', '#', '$', '%', '^', /* scan 0- 7 */ + '&', '*', '(', ')', '_', '+', 0x08, '\t', /* scan 8- F */ + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', /* scan 10-17 */ + 'O', 'P', '{', '}', '\r', CN, 'A', 'S', /* scan 18-1F */ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', /* scan 20-27 */ + '"', '~', SH, '|', 'Z', 'X', 'C', 'V', /* scan 28-2F */ + 'B', 'N', 'M', '<', '>', '?', SH, '*', /* scan 30-37 */ + ' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */ + 0, 0, 0, 0, 0, NM, ST, '7', /* scan 40-47 */ + '8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */ + '2', '3', '0', '.', 0, 0, 0, 0, /* scan 50-57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */ + '\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */ + 0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */ + }, + { /* control code */ + 0xff, 0x1b, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, /* scan 0- 7 */ + 0x1e, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, '\t', /* scan 8- F */ + 0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, /* scan 10-17 */ + 0x0f, 0x10, 0x1b, 0x1d, '\r', CN, 0x01, 0x13, /* scan 18-1F */ + 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0xff, /* scan 20-27 */ + 0xff, 0x1c, SH, 0xff, 0x1a, 0x18, 0x03, 0x16, /* scan 28-2F */ + 0x02, 0x0e, 0x0d, 0xff, 0xff, 0xff, SH, 0xff, /* scan 30-37 */ + 0xff, 0xff, CP, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38-3F */ + 0xff, 0xff, 0xff, 0xff, 0xff, NM, ST, 0xff, /* scan 40-47 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 48-4F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 50-57 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58-5F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60-67 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68-6F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70-77 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78-7F */ + '\r', CN, '/', '*', ' ', ST, 0xff, 0xff, /* extended */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* extended */ + }, + { /* non numeric code */ + 0, 0x1b, '1', '2', '3', '4', '5', '6', /* scan 0- 7 */ + '7', '8', '9', '0', '-', '=', 0x08, '\t', /* scan 8- F */ + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', /* scan 10-17 */ + 'o', 'p', '[', ']', '\r', CN, 'a', 's', /* scan 18-1F */ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* scan 20-27 */ + '\'', '`', SH, '\\', 'z', 'x', 'c', 'v', /* scan 28-2F */ + 'b', 'n', 'm', ',', '.', '/', SH, '*', /* scan 30-37 */ + ' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */ + 0, 0, 0, 0, 0, NM, ST, 'w', /* scan 40-47 */ + 'x', 'y', 'l', 't', 'u', 'v', 'm', 'q', /* scan 48-4F */ + 'r', 's', 'p', 'n', 0, 0, 0, 0, /* scan 50-57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */ + '\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */ + 0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */ + }, + { /* right alt mode - not used in US keyboard */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 0 - 7 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 8 - F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 10 -17 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 18 -1F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 20 -27 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 28 -2F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 30 -37 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38 -3F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 40 -47 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 48 -4F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 50 -57 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58 -5F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60 -67 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68 -6F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70 -77 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78 -7F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* extended */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* extended */ + } + }, + { /* german keyboard */ + { /* unshift code */ + 0, 0x1b, '1', '2', '3', '4', '5', '6', /* scan 0- 7 */ + '7', '8', '9', '0', 0xe1, '\'', 0x08, '\t', /* scan 8- F */ + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', /* scan 10-17 */ + 'o', 'p', 0x81, '+', '\r', CN, 'a', 's', /* scan 18-1F */ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0x94, /* scan 20-27 */ + 0x84, '^', SH, '#', 'y', 'x', 'c', 'v', /* scan 28-2F */ + 'b', 'n', 'm', ',', '.', '-', SH, '*', /* scan 30-37 */ + ' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */ + 0, 0, 0, 0, 0, NM, ST, '7', /* scan 40-47 */ + '8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */ + '2', '3', '0', ',', 0, 0, '<', 0, /* scan 50-57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */ + '\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */ + 0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */ + }, + { /* shift code */ + 0, 0x1b, '!', '"', 0x15, '$', '%', '&', /* scan 0- 7 */ + '/', '(', ')', '=', '?', '`', 0x08, '\t', /* scan 8- F */ + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', /* scan 10-17 */ + 'O', 'P', 0x9a, '*', '\r', CN, 'A', 'S', /* scan 18-1F */ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0x99, /* scan 20-27 */ + 0x8e, 0xf8, SH, '\'', 'Y', 'X', 'C', 'V', /* scan 28-2F */ + 'B', 'N', 'M', ';', ':', '_', SH, '*', /* scan 30-37 */ + ' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */ + 0, 0, 0, 0, 0, NM, ST, '7', /* scan 40-47 */ + '8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */ + '2', '3', '0', ',', 0, 0, '>', 0, /* scan 50-57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */ + '\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */ + 0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */ + }, + { /* control code */ + 0xff, 0x1b, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, /* scan 0- 7 */ + 0x1e, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, '\t', /* scan 8- F */ + 0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, /* scan 10-17 */ + 0x0f, 0x10, 0x1b, 0x1d, '\r', CN, 0x01, 0x13, /* scan 18-1F */ + 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0xff, /* scan 20-27 */ + 0xff, 0x1c, SH, 0xff, 0x1a, 0x18, 0x03, 0x16, /* scan 28-2F */ + 0x02, 0x0e, 0x0d, 0xff, 0xff, 0xff, SH, 0xff, /* scan 30-37 */ + 0xff, 0xff, CP, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38-3F */ + 0xff, 0xff, 0xff, 0xff, 0xff, NM, ST, 0xff, /* scan 40-47 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 48-4F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 50-57 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58-5F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60-67 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68-6F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70-77 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78-7F */ + '\r', CN, '/', '*', ' ', ST, 0xff, 0xff, /* extended */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* extended */ + }, + { /* non numeric code */ + 0, 0x1b, '1', '2', '3', '4', '5', '6', /* scan 0- 7 */ + '7', '8', '9', '0', 0xe1, '\'', 0x08, '\t', /* scan 8- F */ + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', /* scan 10-17 */ + 'o', 'p', 0x81, '+', '\r', CN, 'a', 's', /* scan 18-1F */ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0x94, /* scan 20-27 */ + 0x84, '^', SH, 0, 'y', 'x', 'c', 'v', /* scan 28-2F */ + 'b', 'n', 'm', ',', '.', '-', SH, '*', /* scan 30-37 */ + ' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */ + 0, 0, 0, 0, 0, NM, ST, 'w', /* scan 40-47 */ + 'x', 'y', 'l', 't', 'u', 'v', 'm', 'q', /* scan 48-4F */ + 'r', 's', 'p', 'n', 0, 0, '<', 0, /* scan 50-57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */ + '\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */ + 0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */ + }, + { /* Right alt mode - is used in German keyboard */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 0 - 7 */ + '{', '[', ']', '}', '\\', 0xff, 0xff, 0xff, /* scan 8 - F */ + '@', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 10 -17 */ + 0xff, 0xff, 0xff, '~', 0xff, 0xff, 0xff, 0xff, /* scan 18 -1F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 20 -27 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 28 -2F */ + 0xff, 0xff, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 30 -37 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38 -3F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 40 -47 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 48 -4F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '|', 0xff, /* scan 50 -57 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58 -5F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60 -67 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68 -6F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70 -77 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78 -7F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* extended */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* extended */ + } + } + }; + +static unsigned char ext_key_map[] = { + 0x1c, /* keypad enter */ + 0x1d, /* right control */ + 0x35, /* keypad slash */ + 0x37, /* print screen */ + 0x38, /* right alt */ + 0x46, /* break */ + 0x47, /* editpad home */ + 0x48, /* editpad up */ + 0x49, /* editpad pgup */ + 0x4b, /* editpad left */ + 0x4d, /* editpad right */ + 0x4f, /* editpad end */ + 0x50, /* editpad dn */ + 0x51, /* editpad pgdn */ + 0x52, /* editpad ins */ + 0x53, /* editpad del */ + 0x00 /* map end */ + }; + +/******************************************************************************/ + +static int kbd_controller_present(void) +{ + return in8(I8042_STATUS_REG) != 0xff; +} + +/* + * Implement a weak default function for boards that optionally + * need to skip the i8042 initialization. + */ +int __weak board_i8042_skip(void) +{ + /* As default, don't skip */ + return 0; +} + +void i8042_flush(void) +{ + int timeout; + + /* + * The delay is to give the keyboard controller some time to fill the + * next byte. + */ + while (1) { + timeout = 100; /* wait for no longer than 100us */ + while (timeout > 0 && !(in8(I8042_STATUS_REG) & 0x01)) { + udelay(1); + timeout--; + } + + /* Try to pull next byte if not timeout. */ + if (in8(I8042_STATUS_REG) & 0x01) + in8(I8042_DATA_REG); + else + break; + } +} + +int i8042_disable(void) +{ + if (kbd_input_empty() == 0) + return -1; + + /* Disable keyboard */ + out8(I8042_COMMAND_REG, 0xad); + + if (kbd_input_empty() == 0) + return -1; + + return 0; +} + + +/******************************************************************************* + * + * i8042_kbd_init - reset keyboard and init state flags + */ +int i8042_kbd_init(void) +{ + int keymap, try; + char *penv; + + if (!kbd_controller_present() || board_i8042_skip()) + return -1; + +#ifdef CONFIG_USE_CPCIDVI + penv = getenv("console"); + if (penv != NULL) { + if (strncmp(penv, "serial", 7) == 0) + return -1; + } +#endif + /* Init keyboard device (default US layout) */ + keymap = KBD_US; + penv = getenv("keymap"); + if (penv != NULL) { + if (strncmp(penv, "de", 3) == 0) + keymap = KBD_GER; + } + + for (try = 0; try < KBD_RESET_TRIES; try++) { + if (kbd_reset() == 0) { + kbd_mapping = keymap; + kbd_flags = NORMAL; + kbd_state = 0; + kbd_led_set(); + return 0; + } + } + return -1; +} + + +/******************************************************************************* + * + * i8042_tstc - test if keyboard input is available + * option: cursor blinking if called in a loop + */ +int i8042_tstc(void) +{ + unsigned char scan_code = 0; + +#ifdef CONFIG_CONSOLE_CURSOR + if (--blinkCount == 0) { + cursor_state ^= 1; + console_cursor(cursor_state); + blinkCount = CONFIG_SYS_CONSOLE_BLINK_COUNT; + udelay(10); + } +#endif + + if ((in8(I8042_STATUS_REG) & 0x01) == 0) { + return 0; + } else { + scan_code = in8(I8042_DATA_REG); + if (scan_code == 0xfa) + return 0; + + kbd_conv_char(scan_code); + + if (kbd_input != -1) + return 1; + } + return 0; +} + + +/******************************************************************************* + * + * i8042_getc - wait till keyboard input is available + * option: turn on/off cursor while waiting + */ +int i8042_getc(void) +{ + int ret_chr; + unsigned char scan_code; + + while (kbd_input == -1) { + while ((in8(I8042_STATUS_REG) & 0x01) == 0) { +#ifdef CONFIG_CONSOLE_CURSOR + if (--blinkCount == 0) { + cursor_state ^= 1; + console_cursor(cursor_state); + blinkCount = CONFIG_SYS_CONSOLE_BLINK_COUNT; + } + udelay(10); +#endif + } + scan_code = in8(I8042_DATA_REG); + if (scan_code != 0xfa) + kbd_conv_char (scan_code); + } + ret_chr = kbd_input; + kbd_input = -1; + return ret_chr; +} + + +/******************************************************************************/ + +static void kbd_conv_char(unsigned char scan_code) +{ + if (scan_code == 0xe0) { + kbd_flags |= EXT; + return; + } + + /* if high bit of scan_code, set break flag */ + if (scan_code & 0x80) + kbd_flags |= BRK; + else + kbd_flags &= ~BRK; + + if ((scan_code == 0xe1) || (kbd_flags & E1)) { + if (scan_code == 0xe1) { + kbd_flags ^= BRK; /* reset the break flag */ + kbd_flags ^= E1; /* bitwise EXOR with E1 flag */ + } + return; + } + + scan_code &= 0x7f; + + if (kbd_flags & EXT) { + int i; + + kbd_flags ^= EXT; + for (i = 0; ext_key_map[i]; i++) { + if (ext_key_map[i] == scan_code) { + scan_code = 0x80 + i; + break; + } + } + /* not found ? */ + if (!ext_key_map[i]) + return; + } + + switch (kbd_fct_map[scan_code]) { + case AS: + kbd_normal(scan_code); + break; + case SH: + kbd_shift(scan_code); + break; + case CN: + kbd_ctrl(scan_code); + break; + case NM: + kbd_num(scan_code); + break; + case CP: + kbd_caps(scan_code); + break; + case ST: + kbd_scroll(scan_code); + break; + case AK: + kbd_alt(scan_code); + break; + } + return; +} + + +/******************************************************************************/ + +static void kbd_normal(unsigned char scan_code) +{ + unsigned char chr; + + if ((kbd_flags & BRK) == NORMAL) { + chr = kbd_key_map[kbd_mapping][kbd_state][scan_code]; + if ((chr == 0xff) || (chr == 0x00)) + return; + + /* if caps lock convert upper to lower */ + if (((kbd_flags & CAPS) == CAPS) && + (chr >= 'a' && chr <= 'z')) { + chr -= 'a' - 'A'; + } + kbd_input = chr; + } +} + + +/******************************************************************************/ + +static void kbd_shift(unsigned char scan_code) +{ + if ((kbd_flags & BRK) == BRK) { + kbd_state = AS; + kbd_flags &= (~SHIFT); + } else { + kbd_state = SH; + kbd_flags |= SHIFT; + } +} + + +/******************************************************************************/ + +static void kbd_ctrl(unsigned char scan_code) +{ + if ((kbd_flags & BRK) == BRK) { + kbd_state = AS; + kbd_flags &= (~CTRL); + } else { + kbd_state = CN; + kbd_flags |= CTRL; + } +} + + +/******************************************************************************/ + +static void kbd_caps(unsigned char scan_code) +{ + if ((kbd_flags & BRK) == NORMAL) { + kbd_flags ^= CAPS; + kbd_led_set(); /* update keyboard LED */ + } +} + + +/******************************************************************************/ + +static void kbd_num(unsigned char scan_code) +{ + if ((kbd_flags & BRK) == NORMAL) { + kbd_flags ^= NUM; + kbd_state = (kbd_flags & NUM) ? AS : NM; + kbd_led_set(); /* update keyboard LED */ + } +} + + +/******************************************************************************/ + +static void kbd_scroll(unsigned char scan_code) +{ + if ((kbd_flags & BRK) == NORMAL) { + kbd_flags ^= STP; + kbd_led_set(); /* update keyboard LED */ + if (kbd_flags & STP) + kbd_input = 0x13; + else + kbd_input = 0x11; + } +} + +/******************************************************************************/ + +static void kbd_alt(unsigned char scan_code) +{ + if ((kbd_flags & BRK) == BRK) { + kbd_state = AS; + kbd_flags &= (~ALT); + } else { + kbd_state = AK; + kbd_flags &= ALT; + } +} + + +/******************************************************************************/ + +static void kbd_led_set(void) +{ + kbd_input_empty(); + out8(I8042_DATA_REG, 0xed); /* SET LED command */ + kbd_input_empty(); + out8(I8042_DATA_REG, (kbd_flags & 0x7)); /* LED bits only */ +} + + +/******************************************************************************/ + +static int kbd_input_empty(void) +{ + int kbdTimeout = KBD_TIMEOUT * 1000; + + while ((in8(I8042_STATUS_REG) & I8042_STATUS_IN_DATA) && kbdTimeout--) + udelay(1); + + return kbdTimeout != -1; +} + +/******************************************************************************/ + +static int wait_until_kbd_output_full(void) +{ + int kbdTimeout = KBD_TIMEOUT * 1000; + + while (((in8(I8042_STATUS_REG) & 0x01) == 0) && kbdTimeout--) + udelay(1); + + return kbdTimeout != -1; +} + +/******************************************************************************/ + +static int kbd_reset(void) +{ + /* KB Reset */ + if (kbd_input_empty() == 0) + return -1; + + out8(I8042_DATA_REG, 0xff); + + if (wait_until_kbd_output_full() == 0) + return -1; + + if (in8(I8042_DATA_REG) != 0xfa) /* ACK */ + return -1; + + if (wait_until_kbd_output_full() == 0) + return -1; + + if (in8(I8042_DATA_REG) != 0xaa) /* Test Pass*/ + return -1; + + if (kbd_input_empty() == 0) + return -1; + + /* Set KBC mode */ + out8(I8042_COMMAND_REG, 0x60); + + if (kbd_input_empty() == 0) + return -1; + + out8(I8042_DATA_REG, 0x45); + + if (kbd_input_empty() == 0) + return -1; + + /* Enable Keyboard */ + out8(I8042_COMMAND_REG, 0xae); + + if (kbd_input_empty() == 0) + return -1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/input/input.c b/qemu/roms/u-boot/drivers/input/input.c new file mode 100644 index 000000000..007b85524 --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/input.c @@ -0,0 +1,491 @@ +/* + * Translate key codes into ASCII + * + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +enum { + /* These correspond to the lights on the keyboard */ + FLAG_NUM_LOCK = 1 << 0, + FLAG_CAPS_LOCK = 1 << 1, + FLAG_SCROLL_LOCK = 1 << 2, + + /* Special flag ORed with key code to indicate release */ + KEY_RELEASE = 1 << 15, + KEY_MASK = 0xfff, +}; + +/* + * These takes map key codes to ASCII. 0xff means no key, or special key. + * Three tables are provided - one for plain keys, one for when the shift + * 'modifier' key is pressed and one for when the ctrl modifier key is + * pressed. + */ +static const uchar kbd_plain_xlate[] = { + 0xff, 0x1b, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '\b', '\t', /* 0x00 - 0x0f */ + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', '\r', 0xff, 'a', 's', /* 0x10 - 0x1f */ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', 0xff, '\\', 'z', 'x', 'c', 'v', /* 0x20 - 0x2f */ + 'b', 'n', 'm', ',' , '.', '/', 0xff, 0xff, 0xff, + ' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', + '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */ + '\r', 0xff, 0xff +}; + +static unsigned char kbd_shift_xlate[] = { + 0xff, 0x1b, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', '\b', '\t', /* 0x00 - 0x0f */ + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', '\r', 0xff, 'A', 'S', /* 0x10 - 0x1f */ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '"', '~', 0xff, '|', 'Z', 'X', 'C', 'V', /* 0x20 - 0x2f */ + 'B', 'N', 'M', '<', '>', '?', 0xff, 0xff, 0xff, + ' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', + '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */ + '\r', 0xff, 0xff +}; + +static unsigned char kbd_ctrl_xlate[] = { + 0xff, 0x1b, '1', 0x00, '3', '4', '5', 0x1E, + '7', '8', '9', '0', 0x1F, '=', '\b', '\t', /* 0x00 - 0x0f */ + 0x11, 0x17, 0x05, 0x12, 0x14, 0x18, 0x15, 0x09, + 0x0f, 0x10, 0x1b, 0x1d, '\n', 0xff, 0x01, 0x13, /* 0x10 - 0x1f */ + 0x04, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, ';', + '\'', '~', 0x00, 0x1c, 0x1a, 0x18, 0x03, 0x16, /* 0x20 - 0x2f */ + 0x02, 0x0e, 0x0d, '<', '>', '?', 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', + '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */ + '\r', 0xff, 0xff +}; + +/* + * Scan key code to ANSI 3.64 escape sequence table. This table is + * incomplete in that it does not include all possible extra keys. + */ +static struct { + int kbd_scan_code; + char *escape; +} kbd_to_ansi364[] = { + { KEY_UP, "\033[A"}, + { KEY_DOWN, "\033[B"}, + { KEY_RIGHT, "\033[C"}, + { KEY_LEFT, "\033[D"}, +}; + +/* Maximum number of output characters that an ANSI sequence expands to */ +#define ANSI_CHAR_MAX 3 + +static int input_queue_ascii(struct input_config *config, int ch) +{ + if (config->fifo_in + 1 == INPUT_BUFFER_LEN) { + if (!config->fifo_out) + return -1; /* buffer full */ + else + config->fifo_in = 0; + } else { + if (config->fifo_in + 1 == config->fifo_out) + return -1; /* buffer full */ + config->fifo_in++; + } + config->fifo[config->fifo_in] = (uchar)ch; + + return 0; +} + +int input_tstc(struct input_config *config) +{ + if (config->fifo_in == config->fifo_out && config->read_keys) { + if (!(*config->read_keys)(config)) + return 0; + } + return config->fifo_in != config->fifo_out; +} + +int input_getc(struct input_config *config) +{ + int err = 0; + + while (config->fifo_in == config->fifo_out) { + if (config->read_keys) + err = (*config->read_keys)(config); + if (err) + return -1; + } + + if (++config->fifo_out == INPUT_BUFFER_LEN) + config->fifo_out = 0; + + return config->fifo[config->fifo_out]; +} + +/** + * Process a modifier/special key press or release and decide which key + * translation array should be used as a result. + * + * TODO: Should keep track of modifier press/release + * + * @param config Input state + * @param key Key code to process + * @param release 0 if a press, 1 if a release + * @return pointer to keycode->ascii translation table that should be used + */ +static struct input_key_xlate *process_modifier(struct input_config *config, + int key, int release) +{ + struct input_key_xlate *table; + int flip = -1; + int i; + + /* Start with the main table, and see what modifiers change it */ + assert(config->num_tables > 0); + table = &config->table[0]; + for (i = 1; i < config->num_tables; i++) { + struct input_key_xlate *tab = &config->table[i]; + + if (key == tab->left_keycode || key == tab->right_keycode) + table = tab; + } + + /* Handle the lighted keys */ + if (!release) { + switch (key) { + case KEY_SCROLLLOCK: + flip = FLAG_SCROLL_LOCK; + break; + case KEY_NUMLOCK: + flip = FLAG_NUM_LOCK; + break; + case KEY_CAPSLOCK: + flip = FLAG_CAPS_LOCK; + break; + } + } + + if (flip != -1) { + int leds = 0; + + config->leds ^= flip; + if (config->flags & FLAG_NUM_LOCK) + leds |= INPUT_LED_NUM; + if (config->flags & FLAG_CAPS_LOCK) + leds |= INPUT_LED_CAPS; + if (config->flags & FLAG_SCROLL_LOCK) + leds |= INPUT_LED_SCROLL; + config->leds = leds; + } + + return table; +} + +/** + * Search an int array for a key value + * + * @param array Array to search + * @param count Number of elements in array + * @param key Key value to find + * @return element where value was first found, -1 if none + */ +static int array_search(int *array, int count, int key) +{ + int i; + + for (i = 0; i < count; i++) { + if (array[i] == key) + return i; + } + + return -1; +} + +/** + * Sort an array so that those elements that exist in the ordering are + * first in the array, and in the same order as the ordering. The algorithm + * is O(count * ocount) and designed for small arrays. + * + * TODO: Move this to common / lib? + * + * @param dest Array with elements to sort, also destination array + * @param count Number of elements to sort + * @param order Array containing ordering elements + * @param ocount Number of ordering elements + * @return number of elements in dest that are in order (these will be at the + * start of dest). + */ +static int sort_array_by_ordering(int *dest, int count, int *order, + int ocount) +{ + int temp[count]; + int dest_count; + int same; /* number of elements which are the same */ + int i; + + /* setup output items, copy items to be sorted into our temp area */ + memcpy(temp, dest, count * sizeof(*dest)); + dest_count = 0; + + /* work through the ordering, move over the elements we agree on */ + for (i = 0; i < ocount; i++) { + if (array_search(temp, count, order[i]) != -1) + dest[dest_count++] = order[i]; + } + same = dest_count; + + /* now move over the elements that are not in the ordering */ + for (i = 0; i < count; i++) { + if (array_search(order, ocount, temp[i]) == -1) + dest[dest_count++] = temp[i]; + } + assert(dest_count == count); + return same; +} + +/** + * Check a list of key codes against the previous key scan + * + * Given a list of new key codes, we check how many of these are the same + * as last time. + * + * @param config Input state + * @param keycode List of key codes to examine + * @param num_keycodes Number of key codes + * @param same Returns number of key codes which are the same + */ +static int input_check_keycodes(struct input_config *config, + int keycode[], int num_keycodes, int *same) +{ + /* Select the 'plain' xlate table to start with */ + if (!config->num_tables) { + debug("%s: No xlate tables: cannot decode keys\n", __func__); + return -1; + } + + /* sort the keycodes into the same order as the previous ones */ + *same = sort_array_by_ordering(keycode, num_keycodes, + config->prev_keycodes, config->num_prev_keycodes); + + memcpy(config->prev_keycodes, keycode, num_keycodes * sizeof(int)); + config->num_prev_keycodes = num_keycodes; + + return *same != num_keycodes; +} + +/** + * Checks and converts a special key code into ANSI 3.64 escape sequence. + * + * @param config Input state + * @param keycode Key code to examine + * @param output_ch Buffer to place output characters into. It should + * be at least ANSI_CHAR_MAX bytes long, to allow for + * an ANSI sequence. + * @param max_chars Maximum number of characters to add to output_ch + * @return number of characters output, if the key was converted, otherwise 0. + * This may be larger than max_chars, in which case the overflow + * characters are not output. + */ +static int input_keycode_to_ansi364(struct input_config *config, + int keycode, char output_ch[], int max_chars) +{ + const char *escape; + int ch_count; + int i; + + for (i = ch_count = 0; i < ARRAY_SIZE(kbd_to_ansi364); i++) { + if (keycode != kbd_to_ansi364[i].kbd_scan_code) + continue; + for (escape = kbd_to_ansi364[i].escape; *escape; escape++) { + if (ch_count < max_chars) + output_ch[ch_count] = *escape; + ch_count++; + } + return ch_count; + } + + return 0; +} + +/** + * Converts and queues a list of key codes in escaped ASCII string form + * Convert a list of key codes into ASCII + * + * You must call input_check_keycodes() before this. It turns the keycode + * list into a list of ASCII characters and sends them to the input layer. + * + * Characters which were seen last time do not generate fresh ASCII output. + * The output (calls to queue_ascii) may be longer than num_keycodes, if the + * keycode contains special keys that was encoded to longer escaped sequence. + * + * @param config Input state + * @param keycode List of key codes to examine + * @param num_keycodes Number of key codes + * @param output_ch Buffer to place output characters into. It should + * be at last ANSI_CHAR_MAX * num_keycodes, to allow for + * ANSI sequences. + * @param max_chars Maximum number of characters to add to output_ch + * @param same Number of key codes which are the same + * @return number of characters written into output_ch, or -1 if we would + * exceed max_chars chars. + */ +static int input_keycodes_to_ascii(struct input_config *config, + int keycode[], int num_keycodes, char output_ch[], + int max_chars, int same) +{ + struct input_key_xlate *table; + int ch_count = 0; + int i; + + table = &config->table[0]; + + /* deal with modifiers first */ + for (i = 0; i < num_keycodes; i++) { + int key = keycode[i] & KEY_MASK; + + if (key >= table->num_entries || table->xlate[key] == 0xff) { + table = process_modifier(config, key, + keycode[i] & KEY_RELEASE); + } + } + + /* Start conversion by looking for the first new keycode (by same). */ + for (i = same; i < num_keycodes; i++) { + int key = keycode[i]; + int ch = (key < table->num_entries) ? table->xlate[key] : 0xff; + + /* + * For a normal key (with an ASCII value), add it; otherwise + * translate special key to escape sequence if possible. + */ + if (ch != 0xff) { + if (ch_count < max_chars) + output_ch[ch_count] = (uchar)ch; + ch_count++; + } else { + ch_count += input_keycode_to_ansi364(config, key, + output_ch, max_chars); + } + } + + if (ch_count > max_chars) { + debug("%s: Output char buffer overflow size=%d, need=%d\n", + __func__, max_chars, ch_count); + return -1; + } + + /* ok, so return keys */ + return ch_count; +} + +int input_send_keycodes(struct input_config *config, + int keycode[], int num_keycodes) +{ + char ch[num_keycodes * ANSI_CHAR_MAX]; + int count, i, same = 0; + int is_repeat = 0; + unsigned delay_ms; + + config->modifiers = 0; + if (!input_check_keycodes(config, keycode, num_keycodes, &same)) { + /* + * Same as last time - is it time for another repeat? + * TODO(sjg@chromium.org) We drop repeats here and since + * the caller may not call in again for a while, our + * auto-repeat speed is not quite correct. We should + * insert another character if we later realise that we + * have missed a repeat slot. + */ + is_repeat = config->repeat_rate_ms && + (int)get_timer(config->next_repeat_ms) >= 0; + if (!is_repeat) + return 0; + } + + count = input_keycodes_to_ascii(config, keycode, num_keycodes, + ch, sizeof(ch), is_repeat ? 0 : same); + for (i = 0; i < count; i++) + input_queue_ascii(config, ch[i]); + delay_ms = is_repeat ? + config->repeat_rate_ms : + config->repeat_delay_ms; + + config->next_repeat_ms = get_timer(0) + delay_ms; + + return count; +} + +int input_add_table(struct input_config *config, int left_keycode, + int right_keycode, const uchar *xlate, int num_entries) +{ + struct input_key_xlate *table; + + if (config->num_tables == INPUT_MAX_MODIFIERS) { + debug("%s: Too many modifier tables\n", __func__); + return -1; + } + + table = &config->table[config->num_tables++]; + table->left_keycode = left_keycode; + table->right_keycode = right_keycode; + table->xlate = xlate; + table->num_entries = num_entries; + + return 0; +} + +void input_set_delays(struct input_config *config, int repeat_delay_ms, + int repeat_rate_ms) +{ + config->repeat_delay_ms = repeat_delay_ms; + config->repeat_rate_ms = repeat_rate_ms; +} + +int input_init(struct input_config *config, int leds) +{ + memset(config, '\0', sizeof(*config)); + config->leds = leds; + if (input_add_table(config, -1, -1, + kbd_plain_xlate, ARRAY_SIZE(kbd_plain_xlate)) || + input_add_table(config, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, + kbd_shift_xlate, ARRAY_SIZE(kbd_shift_xlate)) || + input_add_table(config, KEY_LEFTCTRL, KEY_RIGHTCTRL, + kbd_ctrl_xlate, ARRAY_SIZE(kbd_ctrl_xlate))) { + debug("%s: Could not add modifier tables\n", __func__); + return -1; + } + + return 0; +} + +int input_stdio_register(struct stdio_dev *dev) +{ + int error; + + error = stdio_register(dev); + + /* check if this is the standard input device */ + if (!error && strcmp(getenv("stdin"), dev->name) == 0) { + /* reassign the console */ + if (OVERWRITE_CONSOLE || + console_assign(stdin, dev->name)) + return -1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/input/key_matrix.c b/qemu/roms/u-boot/drivers/input/key_matrix.c new file mode 100644 index 000000000..8867e4964 --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/key_matrix.c @@ -0,0 +1,191 @@ +/* + * Manage Keyboard Matrices + * + * Copyright (c) 2012 The Chromium OS Authors. + * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/** + * Determine if the current keypress configuration can cause key ghosting + * + * We figure this out by seeing if we have two or more keys in the same + * column, as well as two or more keys in the same row. + * + * @param config Keyboard matrix config + * @param keys List of keys to check + * @param valid Number of valid keypresses to check + * @return 0 if no ghosting is possible, 1 if it is + */ +static int has_ghosting(struct key_matrix *config, struct key_matrix_key *keys, + int valid) +{ + int key_in_same_col = 0, key_in_same_row = 0; + int i, j; + + if (!config->ghost_filter || valid < 3) + return 0; + + for (i = 0; i < valid; i++) { + /* + * Find 2 keys such that one key is in the same row + * and the other is in the same column as the i-th key. + */ + for (j = i + 1; j < valid; j++) { + if (keys[j].col == keys[i].col) + key_in_same_col = 1; + if (keys[j].row == keys[i].row) + key_in_same_row = 1; + } + } + + if (key_in_same_col && key_in_same_row) + return 1; + else + return 0; +} + +int key_matrix_decode(struct key_matrix *config, struct key_matrix_key keys[], + int num_keys, int keycode[], int max_keycodes) +{ + const u8 *keymap; + int valid, upto; + int pos; + + debug("%s: num_keys = %d\n", __func__, num_keys); + keymap = config->plain_keycode; + for (valid = upto = 0; upto < num_keys; upto++) { + struct key_matrix_key *key = &keys[upto]; + + debug(" valid=%d, row=%d, col=%d\n", key->valid, key->row, + key->col); + if (!key->valid) + continue; + pos = key->row * config->num_cols + key->col; + if (config->fn_keycode && pos == config->fn_pos) + keymap = config->fn_keycode; + + /* Convert the (row, col) values into a keycode */ + if (valid < max_keycodes) + keycode[valid++] = keymap[pos]; + debug(" keycode=%d\n", keymap[pos]); + } + + /* For a ghost key config, ignore the keypresses for this iteration. */ + if (has_ghosting(config, keys, valid)) { + valid = 0; + debug(" ghosting detected!\n"); + } + debug(" %d valid keycodes found\n", valid); + + return valid; +} + +/** + * Create a new keycode map from some provided data + * + * This decodes a keycode map in the format used by the fdt, which is one + * word per entry, with the row, col and keycode encoded in that word. + * + * We create a (row x col) size byte array with each entry containing the + * keycode for that (row, col). We also search for map_keycode and return + * its position if found (this is used for finding the Fn key). + * + * @param config Key matrix dimensions structure + * @param data Keycode data + * @param len Number of entries in keycode table + * @param map_keycode Key code to find in the map + * @param pos Returns position of map_keycode, if found, else -1 + * @return map Pointer to allocated map + */ +static uchar *create_keymap(struct key_matrix *config, u32 *data, int len, + int map_keycode, int *pos) +{ + uchar *map; + + if (pos) + *pos = -1; + map = (uchar *)calloc(1, config->key_count); + if (!map) { + debug("%s: failed to malloc %d bytes\n", __func__, + config->key_count); + return NULL; + } + + for (; len >= sizeof(u32); data++, len -= 4) { + u32 tmp = fdt32_to_cpu(*data); + int key_code, row, col; + int entry; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + entry = row * config->num_cols + col; + map[entry] = key_code; + debug(" map %d, %d: pos=%d, keycode=%d\n", row, col, + entry, key_code); + if (pos && map_keycode == key_code) + *pos = entry; + } + + return map; +} + +int key_matrix_decode_fdt(struct key_matrix *config, const void *blob, int node) +{ + const struct fdt_property *prop; + int proplen; + uchar *plain_keycode; + + prop = fdt_get_property(blob, node, "linux,keymap", &proplen); + /* Basic keymap is required */ + if (!prop) { + debug("%s: cannot find keycode-plain map\n", __func__); + return -1; + } + + plain_keycode = create_keymap(config, (u32 *)prop->data, + proplen, KEY_FN, &config->fn_pos); + config->plain_keycode = plain_keycode; + /* Conversion error -> fail */ + if (!config->plain_keycode) + return -1; + + prop = fdt_get_property(blob, node, "linux,fn-keymap", &proplen); + /* fn keymap is optional */ + if (!prop) + goto done; + + config->fn_keycode = create_keymap(config, (u32 *)prop->data, + proplen, -1, NULL); + /* Conversion error -> fail */ + if (!config->fn_keycode) { + free(plain_keycode); + return -1; + } + +done: + debug("%s: Decoded key maps %p, %p from fdt\n", __func__, + config->plain_keycode, config->fn_keycode); + return 0; +} + +int key_matrix_init(struct key_matrix *config, int rows, int cols, + int ghost_filter) +{ + memset(config, '\0', sizeof(*config)); + config->num_rows = rows; + config->num_cols = cols; + config->key_count = rows * cols; + config->ghost_filter = ghost_filter; + assert(config->key_count > 0); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/input/keyboard.c b/qemu/roms/u-boot/drivers/input/keyboard.c new file mode 100644 index 000000000..614592ef3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/keyboard.c @@ -0,0 +1,300 @@ +/*********************************************************************** + * + * (C) Copyright 2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * + * Keyboard driver + * + ***********************************************************************/ + +#include + +#include +#include + +#undef KBG_DEBUG + +#ifdef KBG_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + + +#define DEVNAME "kbd" + +#define LED_SCR 0x01 /* scroll lock led */ +#define LED_CAP 0x04 /* caps lock led */ +#define LED_NUM 0x02 /* num lock led */ + +#define KBD_BUFFER_LEN 0x20 /* size of the keyboardbuffer */ + +#if defined(CONFIG_MPC5xxx) || defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) +int ps2ser_check(void); +#endif + +static volatile char kbd_buffer[KBD_BUFFER_LEN]; +static volatile int in_pointer = 0; +static volatile int out_pointer = 0; + +static unsigned char leds = 0; +static unsigned char num_lock = 0; +static unsigned char caps_lock = 0; +static unsigned char scroll_lock = 0; +static unsigned char shift = 0; +static unsigned char ctrl = 0; +static unsigned char alt = 0; +static unsigned char e0 = 0; + +/****************************************************************** + * Queue handling + ******************************************************************/ + +/* puts character in the queue and sets up the in and out pointer */ +static void kbd_put_queue(char data) +{ + if((in_pointer+1)==KBD_BUFFER_LEN) { + if(out_pointer==0) { + return; /* buffer full */ + } else{ + in_pointer=0; + } + } else { + if((in_pointer+1)==out_pointer) + return; /* buffer full */ + in_pointer++; + } + kbd_buffer[in_pointer]=data; + return; +} + +/* test if a character is in the queue */ +static int kbd_testc(void) +{ +#if defined(CONFIG_MPC5xxx) || defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) + /* no ISR is used, so received chars must be polled */ + ps2ser_check(); +#endif + if(in_pointer==out_pointer) + return(0); /* no data */ + else + return(1); +} + +/* gets the character from the queue */ +static int kbd_getc(void) +{ + char c; + while(in_pointer==out_pointer) { +#if defined(CONFIG_MPC5xxx) || defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) + /* no ISR is used, so received chars must be polled */ + ps2ser_check(); +#endif + ;} + if((out_pointer+1)==KBD_BUFFER_LEN) + out_pointer=0; + else + out_pointer++; + c=kbd_buffer[out_pointer]; + return (int)c; + +} + +/* Simple translation table for the keys */ + +static unsigned char kbd_plain_xlate[] = { + 0xff,0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=','\b','\t', /* 0x00 - 0x0f */ + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']','\r',0xff, 'a', 's', /* 0x10 - 0x1f */ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`',0xff,'\\', 'z', 'x', 'c', 'v', /* 0x20 - 0x2f */ + 'b', 'n', 'm', ',', '.', '/',0xff,0xff,0xff, ' ',0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */ + '\r',0xff,0xff + }; + +static unsigned char kbd_shift_xlate[] = { + 0xff,0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+','\b','\t', /* 0x00 - 0x0f */ + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}','\r',0xff, 'A', 'S', /* 0x10 - 0x1f */ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',0xff, '|', 'Z', 'X', 'C', 'V', /* 0x20 - 0x2f */ + 'B', 'N', 'M', '<', '>', '?',0xff,0xff,0xff, ' ',0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */ + '\r',0xff,0xff + }; + +static unsigned char kbd_ctrl_xlate[] = { + 0xff,0x1b, '1',0x00, '3', '4', '5',0x1E, '7', '8', '9', '0',0x1F, '=','\b','\t', /* 0x00 - 0x0f */ + 0x11,0x17,0x05,0x12,0x14,0x18,0x15,0x09,0x0f,0x10,0x1b,0x1d,'\n',0xff,0x01,0x13, /* 0x10 - 0x1f */ + 0x04,0x06,0x08,0x09,0x0a,0x0b,0x0c, ';','\'', '~',0x00,0x1c,0x1a,0x18,0x03,0x16, /* 0x20 - 0x2f */ + 0x02,0x0e,0x0d, '<', '>', '?',0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */ + '\r',0xff,0xff + }; + + +void handle_scancode(unsigned char scancode) +{ + unsigned char keycode; + + /* Convert scancode to keycode */ + PRINTF("scancode %x\n",scancode); + if(scancode==0xe0) { + e0=1; /* special charakters */ + return; + } + if(e0==1) { + e0=0; /* delete flag */ + if(!( ((scancode&0x7F)==0x38)|| /* the right ctrl key */ + ((scancode&0x7F)==0x1D)|| /* the right alt key */ + ((scancode&0x7F)==0x35)|| /* the right '/' key */ + ((scancode&0x7F)==0x1C) )) /* the right enter key */ + /* we swallow unknown e0 codes */ + return; + } + /* special cntrl keys */ + switch(scancode) { + case 0x2A: + case 0x36: /* shift pressed */ + shift=1; + return; /* do nothing else */ + case 0xAA: + case 0xB6: /* shift released */ + shift=0; + return; /* do nothing else */ + case 0x38: /* alt pressed */ + alt=1; + return; /* do nothing else */ + case 0xB8: /* alt released */ + alt=0; + return; /* do nothing else */ + case 0x1d: /* ctrl pressed */ + ctrl=1; + return; /* do nothing else */ + case 0x9d: /* ctrl released */ + ctrl=0; + return; /* do nothing else */ + case 0x46: /* scrollock pressed */ + scroll_lock=~scroll_lock; + if(scroll_lock==0) + leds&=~LED_SCR; /* switch LED off */ + else + leds|=LED_SCR; /* switch on LED */ + pckbd_leds(leds); + return; /* do nothing else */ + case 0x3A: /* capslock pressed */ + caps_lock=~caps_lock; + if(caps_lock==0) + leds&=~LED_CAP; /* switch caps_lock off */ + else + leds|=LED_CAP; /* switch on LED */ + pckbd_leds(leds); + return; + case 0x45: /* numlock pressed */ + num_lock=~num_lock; + if(num_lock==0) + leds&=~LED_NUM; /* switch LED off */ + else + leds|=LED_NUM; /* switch on LED */ + pckbd_leds(leds); + return; + case 0xC6: /* scroll lock released */ + case 0xC5: /* num lock released */ + case 0xBA: /* caps lock released */ + return; /* just swallow */ + } +#if 1 + if((scancode&0x80)==0x80) /* key released */ + return; +#else + if((scancode&0x80)==0x00) /* key pressed */ + return; + scancode &= ~0x80; +#endif + /* now, decide which table we need */ + if(scancode > (sizeof(kbd_plain_xlate)/sizeof(kbd_plain_xlate[0]))) { /* scancode not in list */ + PRINTF("unkown scancode %X\n",scancode); + return; /* swallow it */ + } + /* setup plain code first */ + keycode=kbd_plain_xlate[scancode]; + if(caps_lock==1) { /* caps_lock is pressed, overwrite plain code */ + if(scancode > (sizeof(kbd_shift_xlate)/sizeof(kbd_shift_xlate[0]))) { /* scancode not in list */ + PRINTF("unkown caps-locked scancode %X\n",scancode); + return; /* swallow it */ + } + keycode=kbd_shift_xlate[scancode]; + if(keycode<'A') { /* we only want the alphas capital */ + keycode=kbd_plain_xlate[scancode]; + } + } + if(shift==1) { /* shift overwrites caps_lock */ + if(scancode > (sizeof(kbd_shift_xlate)/sizeof(kbd_shift_xlate[0]))) { /* scancode not in list */ + PRINTF("unkown shifted scancode %X\n",scancode); + return; /* swallow it */ + } + keycode=kbd_shift_xlate[scancode]; + } + if(ctrl==1) { /* ctrl overwrites caps_lock and shift */ + if(scancode > (sizeof(kbd_ctrl_xlate)/sizeof(kbd_ctrl_xlate[0]))) { /* scancode not in list */ + PRINTF("unkown ctrl scancode %X\n",scancode); + return; /* swallow it */ + } + keycode=kbd_ctrl_xlate[scancode]; + } + /* check if valid keycode */ + if(keycode==0xff) { + PRINTF("unkown scancode %X\n",scancode); + return; /* swallow unknown codes */ + } + + kbd_put_queue(keycode); + PRINTF("%x\n",keycode); +} + +/****************************************************************** + * Init + ******************************************************************/ + +#ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE +extern int overwrite_console (void); +#define OVERWRITE_CONSOLE overwrite_console () +#else +#define OVERWRITE_CONSOLE 0 +#endif /* CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE */ + +int kbd_init (void) +{ + int error; + struct stdio_dev kbddev ; + char *stdinname = getenv ("stdin"); + + if(kbd_init_hw()==-1) + return -1; + memset (&kbddev, 0, sizeof(kbddev)); + strcpy(kbddev.name, DEVNAME); + kbddev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + kbddev.putc = NULL ; + kbddev.puts = NULL ; + kbddev.getc = kbd_getc ; + kbddev.tstc = kbd_testc ; + + error = stdio_register (&kbddev); + if(error==0) { + /* check if this is the standard input device */ + if(strcmp(stdinname,DEVNAME)==0) { + /* reassign the console */ + if(OVERWRITE_CONSOLE) { + return 1; + } + error=console_assign(stdin,DEVNAME); + if(error==0) + return 1; + else + return error; + } + return 1; + } + return error; +} diff --git a/qemu/roms/u-boot/drivers/input/pc_keyb.c b/qemu/roms/u-boot/drivers/input/pc_keyb.c new file mode 100644 index 000000000..1606ab33f --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/pc_keyb.c @@ -0,0 +1,251 @@ +/*********************************************************************** + * + * (C) Copyright 2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * + * PS/2 keyboard driver + * + * Originally from linux source (drivers/char/pc_keyb.c) + * + ***********************************************************************/ + +#include + +#include +#include + +#undef KBG_DEBUG + +#ifdef KBG_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + + +/* + * This reads the keyboard status port, and does the + * appropriate action. + * + */ +static unsigned char handle_kbd_event(void) +{ + unsigned char status = kbd_read_status(); + unsigned int work = 10000; + + while ((--work > 0) && (status & KBD_STAT_OBF)) { + unsigned char scancode; + + scancode = kbd_read_input(); + + /* Error bytes must be ignored to make the + Synaptics touchpads compaq use work */ + /* Ignore error bytes */ + if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR))) { + if (status & KBD_STAT_MOUSE_OBF) + ; /* not supported: handle_mouse_event(scancode); */ + else + handle_scancode(scancode); + } + status = kbd_read_status(); + } + if (!work) + PRINTF("pc_keyb: controller jammed (0x%02X).\n", status); + return status; +} + + +static int kbd_read_data(void) +{ + int val; + unsigned char status; + + val = -1; + status = kbd_read_status(); + if (status & KBD_STAT_OBF) { + val = kbd_read_input(); + if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) + val = -2; + } + return val; +} + +static int kbd_wait_for_input(void) +{ + unsigned long timeout; + int val; + + timeout = KBD_TIMEOUT; + val=kbd_read_data(); + while(val < 0) { + if(timeout--==0) + return -1; + udelay(1000); + val=kbd_read_data(); + } + return val; +} + + +static int kb_wait(void) +{ + unsigned long timeout = KBC_TIMEOUT * 10; + + do { + unsigned char status = handle_kbd_event(); + if (!(status & KBD_STAT_IBF)) + return 0; /* ok */ + udelay(1000); + timeout--; + } while (timeout); + return 1; +} + +static void kbd_write_command_w(int data) +{ + if(kb_wait()) + PRINTF("timeout in kbd_write_command_w\n"); + kbd_write_command(data); +} + +static void kbd_write_output_w(int data) +{ + if(kb_wait()) + PRINTF("timeout in kbd_write_output_w\n"); + kbd_write_output(data); +} + +static void kbd_send_data(unsigned char data) +{ + kbd_write_output_w(data); + kbd_wait_for_input(); +} + + +static char * kbd_initialize(void) +{ + int status; + + /* + * Test the keyboard interface. + * This seems to be the only way to get it going. + * If the test is successful a x55 is placed in the input buffer. + */ + kbd_write_command_w(KBD_CCMD_SELF_TEST); + if (kbd_wait_for_input() != 0x55) + return "Kbd: failed self test"; + /* + * Perform a keyboard interface test. This causes the controller + * to test the keyboard clock and data lines. The results of the + * test are placed in the input buffer. + */ + kbd_write_command_w(KBD_CCMD_KBD_TEST); + if (kbd_wait_for_input() != 0x00) + return "Kbd: interface failed self test"; + /* + * Enable the keyboard by allowing the keyboard clock to run. + */ + kbd_write_command_w(KBD_CCMD_KBD_ENABLE); + + /* + * Reset keyboard. If the read times out + * then the assumption is that no keyboard is + * plugged into the machine. + * This defaults the keyboard to scan-code set 2. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + kbd_write_output_w(KBD_CMD_RESET); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) { + PRINTF("status: %X\n",status); + return "Kbd: reset failed, no ACK"; + } + } while (1); + if (kbd_wait_for_input() != KBD_REPLY_POR) + return "Kbd: reset failed, no POR"; + + /* + * Set keyboard controller mode. During this, the keyboard should be + * in the disabled state. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + kbd_write_output_w(KBD_CMD_DISABLE); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) + return "Kbd: disable keyboard: no ACK"; + } while (1); + + kbd_write_command_w(KBD_CCMD_WRITE_MODE); + kbd_write_output_w(KBD_MODE_KBD_INT + | KBD_MODE_SYS + | KBD_MODE_DISABLE_MOUSE + | KBD_MODE_KCC); + + /* AMCC powerpc portables need this to use scan-code set 1 -- Cort */ + kbd_write_command_w(KBD_CCMD_READ_MODE); + if (!(kbd_wait_for_input() & KBD_MODE_KCC)) { + /* + * If the controller does not support conversion, + * Set the keyboard to scan-code set 1. + */ + kbd_write_output_w(0xF0); + kbd_wait_for_input(); + kbd_write_output_w(0x01); + kbd_wait_for_input(); + } + kbd_write_output_w(KBD_CMD_ENABLE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Kbd: enable keyboard: no ACK"; + + /* + * Finally, set the typematic rate to maximum. + */ + kbd_write_output_w(KBD_CMD_SET_RATE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Kbd: Set rate: no ACK"; + kbd_write_output_w(0x00); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Kbd: Set rate: no ACK"; + return NULL; +} + +static void kbd_interrupt(void *dev_id) +{ + handle_kbd_event(); +} + +/****************************************************************** + * Init + ******************************************************************/ + +int kbd_init_hw(void) +{ + char* result; + + kbd_request_region(); + + result=kbd_initialize(); + if (result==NULL) { + PRINTF("AT Keyboard initialized\n"); + kbd_request_irq(kbd_interrupt); + return (1); + } else { + printf("%s\n",result); + return (-1); + } +} + +void pckbd_leds(unsigned char leds) +{ + kbd_send_data(KBD_CMD_SET_LEDS); + kbd_send_data(leds); +} diff --git a/qemu/roms/u-boot/drivers/input/ps2mult.c b/qemu/roms/u-boot/drivers/input/ps2mult.c new file mode 100644 index 000000000..ab749336b --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/ps2mult.c @@ -0,0 +1,461 @@ +/*********************************************************************** + * + * (C) Copyright 2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * + * PS/2 multiplexer driver + * + * Originally from linux source (drivers/char/ps2mult.c) + * + * Uses simple serial driver (ps2ser.c) to access the multiplexer + * Used by PS/2 keyboard driver (pc_keyb.c) + * + ***********************************************************************/ + +#include + +#include +#include +#include + +/* #define DEBUG_MULT */ +/* #define DEBUG_KEYB */ + +#define KBD_STAT_DEFAULT (KBD_STAT_SELFTEST | KBD_STAT_UNLOCKED) + +#define PRINTF(format, args...) printf("ps2mult.c: " format, ## args) + +#ifdef DEBUG_MULT +#define PRINTF_MULT(format, args...) printf("PS2MULT: " format, ## args) +#else +#define PRINTF_MULT(format, args...) +#endif + +#ifdef DEBUG_KEYB +#define PRINTF_KEYB(format, args...) printf("KEYB: " format, ## args) +#else +#define PRINTF_KEYB(format, args...) +#endif + + +static ulong start_time; +static int init_done = 0; + +static int received_escape = 0; +static int received_bsync = 0; +static int received_selector = 0; + +static int kbd_command_active = 0; +static int mouse_command_active = 0; +static int ctl_command_active = 0; + +static u_char command_byte = 0; + +static void (*keyb_handler)(void *dev_id); + +static u_char ps2mult_buf [PS2BUF_SIZE]; +static atomic_t ps2mult_buf_cnt; +static int ps2mult_buf_in_idx; +static int ps2mult_buf_out_idx; + +static u_char ps2mult_buf_status [PS2BUF_SIZE]; + +#ifndef CONFIG_BOARD_EARLY_INIT_R +#error #define CONFIG_BOARD_EARLY_INIT_R and call ps2mult_early_init() in board_early_init_r() +#endif +void ps2mult_early_init (void) +{ + start_time = get_timer(0); +} + +static void ps2mult_send_byte(u_char byte, u_char sel) +{ + ps2ser_putc(sel); + + if (sel == PS2MULT_KB_SELECTOR) { + PRINTF_MULT("0x%02x send KEYBOARD\n", byte); + kbd_command_active = 1; + } else { + PRINTF_MULT("0x%02x send MOUSE\n", byte); + mouse_command_active = 1; + } + + switch (byte) { + case PS2MULT_ESCAPE: + case PS2MULT_BSYNC: + case PS2MULT_KB_SELECTOR: + case PS2MULT_MS_SELECTOR: + case PS2MULT_SESSION_START: + case PS2MULT_SESSION_END: + ps2ser_putc(PS2MULT_ESCAPE); + break; + default: + break; + } + + ps2ser_putc(byte); +} + +static void ps2mult_receive_byte(u_char byte, u_char sel) +{ + u_char status = KBD_STAT_DEFAULT; + +#if 1 /* Ignore mouse in U-Boot */ + if (sel == PS2MULT_MS_SELECTOR) return; +#endif + + if (sel == PS2MULT_KB_SELECTOR) { + if (kbd_command_active) { + if (!received_bsync) { + PRINTF_MULT("0x%02x lost KEYBOARD !!!\n", byte); + return; + } else { + kbd_command_active = 0; + received_bsync = 0; + } + } + PRINTF_MULT("0x%02x receive KEYBOARD\n", byte); + status |= KBD_STAT_IBF | KBD_STAT_OBF; + } else { + if (mouse_command_active) { + if (!received_bsync) { + PRINTF_MULT("0x%02x lost MOUSE !!!\n", byte); + return; + } else { + mouse_command_active = 0; + received_bsync = 0; + } + } + PRINTF_MULT("0x%02x receive MOUSE\n", byte); + status |= KBD_STAT_IBF | KBD_STAT_OBF | KBD_STAT_MOUSE_OBF; + } + + if (atomic_read(&ps2mult_buf_cnt) < PS2BUF_SIZE) { + ps2mult_buf_status[ps2mult_buf_in_idx] = status; + ps2mult_buf[ps2mult_buf_in_idx++] = byte; + ps2mult_buf_in_idx &= (PS2BUF_SIZE - 1); + atomic_inc(&ps2mult_buf_cnt); + } else { + PRINTF("buffer overflow\n"); + } + + if (received_bsync) { + PRINTF("unexpected BSYNC\n"); + received_bsync = 0; + } +} + +void ps2mult_callback (int in_cnt) +{ + int i; + u_char byte; + static int keyb_handler_active = 0; + + if (!init_done) { + return; + } + + for (i = 0; i < in_cnt; i ++) { + byte = ps2ser_getc(); + + if (received_escape) { + ps2mult_receive_byte(byte, received_selector); + received_escape = 0; + } else switch (byte) { + case PS2MULT_ESCAPE: + PRINTF_MULT("ESCAPE receive\n"); + received_escape = 1; + break; + + case PS2MULT_BSYNC: + PRINTF_MULT("BSYNC receive\n"); + received_bsync = 1; + break; + + case PS2MULT_KB_SELECTOR: + case PS2MULT_MS_SELECTOR: + PRINTF_MULT("%s receive\n", + byte == PS2MULT_KB_SELECTOR ? "KB_SEL" : "MS_SEL"); + received_selector = byte; + break; + + case PS2MULT_SESSION_START: + case PS2MULT_SESSION_END: + PRINTF_MULT("%s receive\n", + byte == PS2MULT_SESSION_START ? + "SESSION_START" : "SESSION_END"); + break; + + default: + ps2mult_receive_byte(byte, received_selector); + } + } + + if (keyb_handler && !keyb_handler_active && + atomic_read(&ps2mult_buf_cnt)) { + keyb_handler_active = 1; + keyb_handler(NULL); + keyb_handler_active = 0; + } +} + +u_char ps2mult_read_status(void) +{ + u_char byte; + + if (atomic_read(&ps2mult_buf_cnt) == 0) { + ps2ser_check(); + } + + if (atomic_read(&ps2mult_buf_cnt)) { + byte = ps2mult_buf_status[ps2mult_buf_out_idx]; + } else { + byte = KBD_STAT_DEFAULT; + } + PRINTF_KEYB("read_status()=0x%02x\n", byte); + return byte; +} + +u_char ps2mult_read_input(void) +{ + u_char byte = 0; + + if (atomic_read(&ps2mult_buf_cnt) == 0) { + ps2ser_check(); + } + + if (atomic_read(&ps2mult_buf_cnt)) { + byte = ps2mult_buf[ps2mult_buf_out_idx++]; + ps2mult_buf_out_idx &= (PS2BUF_SIZE - 1); + atomic_dec(&ps2mult_buf_cnt); + } + PRINTF_KEYB("read_input()=0x%02x\n", byte); + return byte; +} + +void ps2mult_write_output(u_char val) +{ + int i; + + PRINTF_KEYB("write_output(0x%02x)\n", val); + + for (i = 0; i < KBD_TIMEOUT; i++) { + if (!kbd_command_active && !mouse_command_active) { + break; + } + udelay(1000); + ps2ser_check(); + } + + if (kbd_command_active) { + PRINTF("keyboard command not acknoledged\n"); + kbd_command_active = 0; + } + + if (mouse_command_active) { + PRINTF("mouse command not acknoledged\n"); + mouse_command_active = 0; + } + + if (ctl_command_active) { + switch (ctl_command_active) { + case KBD_CCMD_WRITE_MODE: + /* Scan code conversion not supported */ + command_byte = val & ~KBD_MODE_KCC; + break; + + case KBD_CCMD_WRITE_AUX_OBUF: + ps2mult_receive_byte(val, PS2MULT_MS_SELECTOR); + break; + + case KBD_CCMD_WRITE_MOUSE: + ps2mult_send_byte(val, PS2MULT_MS_SELECTOR); + break; + + default: + PRINTF("invalid controller command\n"); + break; + } + + ctl_command_active = 0; + return; + } + + ps2mult_send_byte(val, PS2MULT_KB_SELECTOR); +} + +void ps2mult_write_command(u_char val) +{ + ctl_command_active = 0; + + PRINTF_KEYB("write_command(0x%02x)\n", val); + + switch (val) { + case KBD_CCMD_READ_MODE: + ps2mult_receive_byte(command_byte, PS2MULT_KB_SELECTOR); + break; + + case KBD_CCMD_WRITE_MODE: + ctl_command_active = val; + break; + + case KBD_CCMD_MOUSE_DISABLE: + break; + + case KBD_CCMD_MOUSE_ENABLE: + break; + + case KBD_CCMD_SELF_TEST: + ps2mult_receive_byte(0x55, PS2MULT_KB_SELECTOR); + break; + + case KBD_CCMD_KBD_TEST: + ps2mult_receive_byte(0x00, PS2MULT_KB_SELECTOR); + break; + + case KBD_CCMD_KBD_DISABLE: + break; + + case KBD_CCMD_KBD_ENABLE: + break; + + case KBD_CCMD_WRITE_AUX_OBUF: + ctl_command_active = val; + break; + + case KBD_CCMD_WRITE_MOUSE: + ctl_command_active = val; + break; + + default: + PRINTF("invalid controller command\n"); + break; + } +} + +static int ps2mult_getc_w (void) +{ + int res = -1; + int i; + + for (i = 0; i < KBD_TIMEOUT; i++) { + if (ps2ser_check()) { + res = ps2ser_getc(); + break; + } + udelay(1000); + } + + switch (res) { + case PS2MULT_KB_SELECTOR: + case PS2MULT_MS_SELECTOR: + received_selector = res; + break; + default: + break; + } + + return res; +} + +int ps2mult_init (void) +{ + int byte; + int kbd_found = 0; + int mouse_found = 0; + + while (get_timer(start_time) < CONFIG_PS2MULT_DELAY); + + ps2ser_init(); + + ps2ser_putc(PS2MULT_SESSION_START); + + ps2ser_putc(PS2MULT_KB_SELECTOR); + ps2ser_putc(KBD_CMD_RESET); + + do { + byte = ps2mult_getc_w(); + } while (byte >= 0 && byte != KBD_REPLY_ACK); + + if (byte == KBD_REPLY_ACK) { + byte = ps2mult_getc_w(); + if (byte == 0xaa) { + kbd_found = 1; + puts("keyboard"); + } + } + + if (!kbd_found) { + while (byte >= 0) { + byte = ps2mult_getc_w(); + } + } + +#if 1 /* detect mouse */ + ps2ser_putc(PS2MULT_MS_SELECTOR); + ps2ser_putc(AUX_RESET); + + do { + byte = ps2mult_getc_w(); + } while (byte >= 0 && byte != AUX_ACK); + + if (byte == AUX_ACK) { + byte = ps2mult_getc_w(); + if (byte == 0xaa) { + byte = ps2mult_getc_w(); + if (byte == 0x00) { + mouse_found = 1; + puts(", mouse"); + } + } + } + + if (!mouse_found) { + while (byte >= 0) { + byte = ps2mult_getc_w(); + } + } +#endif + + if (mouse_found || kbd_found) { + if (!received_selector) { + if (mouse_found) { + received_selector = PS2MULT_MS_SELECTOR; + } else { + received_selector = PS2MULT_KB_SELECTOR; + } + } + + init_done = 1; + } else { + puts("No device found"); + } + + puts("\n"); + +#if 0 /* for testing */ + { + int i; + u_char key[] = { + 0x1f, 0x12, 0x14, 0x12, 0x31, 0x2f, 0x39, /* setenv */ + 0x1f, 0x14, 0x20, 0x17, 0x31, 0x39, /* stdin */ + 0x1f, 0x12, 0x13, 0x17, 0x1e, 0x26, 0x1c, /* serial */ + }; + + for (i = 0; i < sizeof (key); i++) { + ps2mult_receive_byte (key[i], PS2MULT_KB_SELECTOR); + ps2mult_receive_byte (key[i] | 0x80, PS2MULT_KB_SELECTOR); + } + } +#endif + + return init_done ? 0 : -1; +} + +int ps2mult_request_irq(void (*handler)(void *)) +{ + keyb_handler = handler; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/input/ps2ser.c b/qemu/roms/u-boot/drivers/input/ps2ser.c new file mode 100644 index 000000000..bcbe52af1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/ps2ser.c @@ -0,0 +1,241 @@ +/*********************************************************************** + * + * (C) Copyright 2004-2009 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * + * Simple 16550A serial driver + * + * Originally from linux source (drivers/char/ps2ser.c) + * + * Used by the PS/2 multiplexer driver (ps2mult.c) + * + ***********************************************************************/ + +#include + +#include +#include +#include +/* This is needed for ns16550.h */ +#ifndef CONFIG_SYS_NS16550_REG_SIZE +#define CONFIG_SYS_NS16550_REG_SIZE 1 +#endif +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* #define DEBUG */ + +#define PS2SER_BAUD 57600 + +#ifdef CONFIG_MPC5xxx +#if CONFIG_PS2SERIAL == 1 +#define PSC_BASE MPC5XXX_PSC1 +#elif CONFIG_PS2SERIAL == 2 +#define PSC_BASE MPC5XXX_PSC2 +#elif CONFIG_PS2SERIAL == 3 +#define PSC_BASE MPC5XXX_PSC3 +#elif CONFIG_PS2SERIAL == 4 +#define PSC_BASE MPC5XXX_PSC4 +#elif CONFIG_PS2SERIAL == 5 +#define PSC_BASE MPC5XXX_PSC5 +#elif CONFIG_PS2SERIAL == 6 +#define PSC_BASE MPC5XXX_PSC6 +#else +#error CONFIG_PS2SERIAL must be in 1 ... 6 +#endif + +#else + +#if CONFIG_PS2SERIAL == 1 +#define COM_BASE (CONFIG_SYS_CCSRBAR+0x4500) +#elif CONFIG_PS2SERIAL == 2 +#define COM_BASE (CONFIG_SYS_CCSRBAR+0x4600) +#else +#error CONFIG_PS2SERIAL must be in 1 ... 2 +#endif + +#endif /* CONFIG_MPC5xxx / other */ + +static int ps2ser_getc_hw(void); +static void ps2ser_interrupt(void *dev_id); + +extern struct serial_state rs_table[]; /* in serial.c */ + +static u_char ps2buf[PS2BUF_SIZE]; +static atomic_t ps2buf_cnt; +static int ps2buf_in_idx; +static int ps2buf_out_idx; + +#ifdef CONFIG_MPC5xxx +int ps2ser_init(void) +{ + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; + unsigned long baseclk; + int div; + + /* reset PSC */ + psc->command = PSC_SEL_MODE_REG_1; + + /* select clock sources */ + psc->psc_clock_select = 0; + baseclk = (gd->arch.ipb_clk + 16) / 32; + + /* switch to UART mode */ + psc->sicr = 0; + + /* configure parity, bit length and so on */ + psc->mode = PSC_MODE_8_BITS | PSC_MODE_PARNONE; + psc->mode = PSC_MODE_ONE_STOP; + + /* set up UART divisor */ + div = (baseclk + (PS2SER_BAUD/2)) / PS2SER_BAUD; + psc->ctur = (div >> 8) & 0xff; + psc->ctlr = div & 0xff; + + /* disable all interrupts */ + psc->psc_imr = 0; + + /* reset and enable Rx/Tx */ + psc->command = PSC_RST_RX; + psc->command = PSC_RST_TX; + psc->command = PSC_RX_ENABLE | PSC_TX_ENABLE; + + return (0); +} + +#else + +int ps2ser_init(void) +{ + NS16550_t com_port = (NS16550_t)COM_BASE; + + com_port->ier = 0x00; + com_port->lcr = UART_LCR_BKSE | UART_LCR_8N1; + com_port->dll = (CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) & 0xff; + com_port->dlm = ((CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) >> 8) & 0xff; + com_port->lcr = UART_LCR_8N1; + com_port->mcr = (UART_MCR_DTR | UART_MCR_RTS); + com_port->fcr = (UART_FCR_FIFO_EN | UART_FCR_RXSR | UART_FCR_TXSR); + + return (0); +} + +#endif /* CONFIG_MPC5xxx / other */ + +void ps2ser_putc(int chr) +{ +#ifdef CONFIG_MPC5xxx + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#else + NS16550_t com_port = (NS16550_t)COM_BASE; +#endif + debug(">>>> 0x%02x\n", chr); + +#ifdef CONFIG_MPC5xxx + while (!(psc->psc_status & PSC_SR_TXRDY)); + + psc->psc_buffer_8 = chr; +#else + while ((com_port->lsr & UART_LSR_THRE) == 0); + com_port->thr = chr; +#endif +} + +static int ps2ser_getc_hw(void) +{ +#ifdef CONFIG_MPC5xxx + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#else + NS16550_t com_port = (NS16550_t)COM_BASE; +#endif + int res = -1; + +#ifdef CONFIG_MPC5xxx + if (psc->psc_status & PSC_SR_RXRDY) { + res = (psc->psc_buffer_8); + } +#else + if (com_port->lsr & UART_LSR_DR) { + res = com_port->rbr; + } +#endif + + return res; +} + +int ps2ser_getc(void) +{ + volatile int chr; + int flags; + + debug("<< "); + + flags = disable_interrupts(); + + do { + if (atomic_read(&ps2buf_cnt) != 0) { + chr = ps2buf[ps2buf_out_idx++]; + ps2buf_out_idx &= (PS2BUF_SIZE - 1); + atomic_dec(&ps2buf_cnt); + } else { + chr = ps2ser_getc_hw(); + } + } + while (chr < 0); + + if (flags) + enable_interrupts(); + + debug("0x%02x\n", chr); + + return chr; +} + +int ps2ser_check(void) +{ + int flags; + + flags = disable_interrupts(); + ps2ser_interrupt(NULL); + if (flags) enable_interrupts(); + + return atomic_read(&ps2buf_cnt); +} + +static void ps2ser_interrupt(void *dev_id) +{ +#ifdef CONFIG_MPC5xxx + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#else + NS16550_t com_port = (NS16550_t)COM_BASE; +#endif + int chr; + int status; + + do { + chr = ps2ser_getc_hw(); +#ifdef CONFIG_MPC5xxx + status = psc->psc_status; +#else + status = com_port->lsr; +#endif + if (chr < 0) continue; + + if (atomic_read(&ps2buf_cnt) < PS2BUF_SIZE) { + ps2buf[ps2buf_in_idx++] = chr; + ps2buf_in_idx &= (PS2BUF_SIZE - 1); + atomic_inc(&ps2buf_cnt); + } else { + printf ("ps2ser.c: buffer overflow\n"); + } +#ifdef CONFIG_MPC5xxx + } while (status & PSC_SR_RXRDY); +#else + } while (status & UART_LSR_DR); +#endif + if (atomic_read(&ps2buf_cnt)) { + ps2mult_callback(atomic_read(&ps2buf_cnt)); + } +} diff --git a/qemu/roms/u-boot/drivers/input/tegra-kbc.c b/qemu/roms/u-boot/drivers/input/tegra-kbc.c new file mode 100644 index 000000000..f137f930a --- /dev/null +++ b/qemu/roms/u-boot/drivers/input/tegra-kbc.c @@ -0,0 +1,376 @@ +/* + * (C) Copyright 2011 + * NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +enum { + KBC_MAX_GPIO = 24, + KBC_MAX_KPENT = 8, /* size of keypress entry queue */ +}; + +#define KBC_FIFO_TH_CNT_SHIFT 14 +#define KBC_DEBOUNCE_CNT_SHIFT 4 +#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) +#define KBC_CONTROL_KBC_EN (1 << 0) +#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2) +#define KBC_KPENT_VALID (1 << 7) +#define KBC_ST_STATUS (1 << 3) + +enum { + KBC_DEBOUNCE_COUNT = 2, + KBC_REPEAT_RATE_MS = 30, + KBC_REPEAT_DELAY_MS = 240, + KBC_CLOCK_KHZ = 32, /* Keyboard uses a 32KHz clock */ +}; + +/* keyboard controller config and state */ +static struct keyb { + struct input_config input; /* The input layer */ + struct key_matrix matrix; /* The key matrix layer */ + + struct kbc_tegra *kbc; /* tegra keyboard controller */ + unsigned char inited; /* 1 if keyboard has been inited */ + unsigned char first_scan; /* 1 if this is our first key scan */ + unsigned char created; /* 1 if driver has been created */ + + /* + * After init we must wait a short time before polling the keyboard. + * This gives the tegra keyboard controller time to react after reset + * and lets us grab keys pressed during reset. + */ + unsigned int init_dly_ms; /* Delay before we can read keyboard */ + unsigned int start_time_ms; /* Time that we inited (in ms) */ + unsigned int last_poll_ms; /* Time we should last polled */ + unsigned int next_repeat_ms; /* Next time we repeat a key */ +} config; + +/** + * reads the keyboard fifo for current keypresses + * + * @param config Keyboard config + * @param fifo Place to put fifo results + * @param max_keycodes Maximum number of key codes to put in the fifo + * @return number of items put into fifo + */ +static int tegra_kbc_find_keys(struct keyb *config, int *fifo, + int max_keycodes) +{ + struct key_matrix_key keys[KBC_MAX_KPENT], *key; + u32 kp_ent = 0; + int i; + + for (key = keys, i = 0; i < KBC_MAX_KPENT; i++, key++) { + /* Get next word */ + if (!(i & 3)) + kp_ent = readl(&config->kbc->kp_ent[i / 4]); + + key->valid = (kp_ent & KBC_KPENT_VALID) != 0; + key->row = (kp_ent >> 3) & 0xf; + key->col = kp_ent & 0x7; + + /* Shift to get next entry */ + kp_ent >>= 8; + } + return key_matrix_decode(&config->matrix, keys, KBC_MAX_KPENT, fifo, + max_keycodes); +} + +/** + * Process all the keypress sequences in fifo and send key codes + * + * The fifo contains zero or more keypress sets. Each set + * consists of from 1-8 keycodes, representing the keycodes which + * were simultaneously pressed during that scan. + * + * This function works through each set and generates ASCII characters + * for each. Not that one set may produce more than one ASCII characters - + * for example holding down 'd' and 'f' at the same time will generate + * two ASCII characters. + * + * Note: if fifo_cnt is 0, we will tell the input layer that no keys are + * pressed. + * + * @param config Keyboard config + * @param fifo_cnt Number of entries in the keyboard fifo + */ +static void process_fifo(struct keyb *config, int fifo_cnt) +{ + int fifo[KBC_MAX_KPENT]; + int cnt = 0; + + /* Always call input_send_keycodes() at least once */ + do { + if (fifo_cnt) + cnt = tegra_kbc_find_keys(config, fifo, KBC_MAX_KPENT); + + input_send_keycodes(&config->input, fifo, cnt); + } while (--fifo_cnt > 0); +} + +/** + * Check the keyboard controller and emit ASCII characters for any keys that + * are pressed. + * + * @param config Keyboard config + */ +static void check_for_keys(struct keyb *config) +{ + int fifo_cnt; + + if (!config->first_scan && + get_timer(config->last_poll_ms) < KBC_REPEAT_RATE_MS) + return; + config->last_poll_ms = get_timer(0); + config->first_scan = 0; + + /* + * Once we get here we know the keyboard has been scanned. So if there + * scan waiting for us, we know that nothing is held down. + */ + fifo_cnt = (readl(&config->kbc->interrupt) >> 4) & 0xf; + process_fifo(config, fifo_cnt); +} + +/** + * In order to detect keys pressed on boot, wait for the hardware to + * complete scanning the keys. This includes time to transition from + * Wkup mode to Continous polling mode and the repoll time. We can + * deduct the time that's already elapsed. + * + * @param config Keyboard config + */ +static void kbd_wait_for_fifo_init(struct keyb *config) +{ + if (!config->inited) { + unsigned long elapsed_time; + long delay_ms; + + elapsed_time = get_timer(config->start_time_ms); + delay_ms = config->init_dly_ms - elapsed_time; + if (delay_ms > 0) { + udelay(delay_ms * 1000); + debug("%s: delay %ldms\n", __func__, delay_ms); + } + + config->inited = 1; + } +} + +/** + * Check the tegra keyboard, and send any keys that are pressed. + * + * This is called by input_tstc() and input_getc() when they need more + * characters + * + * @param input Input configuration + * @return 1, to indicate that we have something to look at + */ +int tegra_kbc_check(struct input_config *input) +{ + kbd_wait_for_fifo_init(&config); + check_for_keys(&config); + + return 1; +} + +/** + * Test if keys are available to be read + * + * @return 0 if no keys available, 1 if keys are available + */ +static int kbd_tstc(void) +{ + /* Just get input to do this for us */ + return input_tstc(&config.input); +} + +/** + * Read a key + * + * TODO: U-Boot wants 0 for no key, but Ctrl-@ is a valid key... + * + * @return ASCII key code, or 0 if no key, or -1 if error + */ +static int kbd_getc(void) +{ + /* Just get input to do this for us */ + return input_getc(&config.input); +} + +/* configures keyboard GPIO registers to use the rows and columns */ +static void config_kbc_gpio(struct kbc_tegra *kbc) +{ + int i; + + for (i = 0; i < KBC_MAX_GPIO; i++) { + u32 row_cfg, col_cfg; + u32 r_shift = 5 * (i % 6); + u32 c_shift = 4 * (i % 8); + u32 r_mask = 0x1f << r_shift; + u32 c_mask = 0xf << c_shift; + u32 r_offs = i / 6; + u32 c_offs = i / 8; + + row_cfg = readl(&kbc->row_cfg[r_offs]); + col_cfg = readl(&kbc->col_cfg[c_offs]); + + row_cfg &= ~r_mask; + col_cfg &= ~c_mask; + + if (i < config.matrix.num_rows) { + row_cfg |= ((i << 1) | 1) << r_shift; + } else { + col_cfg |= (((i - config.matrix.num_rows) << 1) | 1) + << c_shift; + } + + writel(row_cfg, &kbc->row_cfg[r_offs]); + writel(col_cfg, &kbc->col_cfg[c_offs]); + } +} + +/** + * Start up the keyboard device + */ +static void tegra_kbc_open(void) +{ + struct kbc_tegra *kbc = config.kbc; + unsigned int scan_period; + u32 val; + + /* + * We will scan at twice the keyboard repeat rate, so that there is + * always a scan ready when we check it in check_for_keys(). + */ + scan_period = KBC_REPEAT_RATE_MS / 2; + writel(scan_period * KBC_CLOCK_KHZ, &kbc->rpt_dly); + writel(scan_period * KBC_CLOCK_KHZ, &kbc->init_dly); + /* + * Before reading from the keyboard we must wait for the init_dly + * plus the rpt_delay, plus 2ms for the row scan time. + */ + config.init_dly_ms = scan_period * 2 + 2; + + val = KBC_DEBOUNCE_COUNT << KBC_DEBOUNCE_CNT_SHIFT; + val |= 1 << KBC_FIFO_TH_CNT_SHIFT; /* fifo interrupt threshold */ + val |= KBC_CONTROL_KBC_EN; /* enable */ + writel(val, &kbc->control); + + config.start_time_ms = get_timer(0); + config.last_poll_ms = config.next_repeat_ms = get_timer(0); + config.first_scan = 1; +} + +/** + * Set up the tegra keyboard. This is called by the stdio device handler + * + * We want to do this init when the keyboard is actually used rather than + * at start-up, since keyboard input may not currently be selected. + * + * Once the keyboard starts there will be a period during which we must + * wait for the keyboard to init. We do this only when a key is first + * read - see kbd_wait_for_fifo_init(). + * + * @return 0 if ok, -ve on error + */ +static int init_tegra_keyboard(void) +{ + /* check if already created */ + if (config.created) + return 0; + +#ifdef CONFIG_OF_CONTROL + int node; + + node = fdtdec_next_compatible(gd->fdt_blob, 0, + COMPAT_NVIDIA_TEGRA20_KBC); + if (node < 0) { + debug("%s: cannot locate keyboard node\n", __func__); + return node; + } + config.kbc = (struct kbc_tegra *)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); + if ((fdt_addr_t)config.kbc == FDT_ADDR_T_NONE) { + debug("%s: No keyboard register found\n", __func__); + return -1; + } + input_set_delays(&config.input, KBC_REPEAT_DELAY_MS, + KBC_REPEAT_RATE_MS); + + /* Decode the keyboard matrix information (16 rows, 8 columns) */ + if (key_matrix_init(&config.matrix, 16, 8, 1)) { + debug("%s: Could not init key matrix\n", __func__); + return -1; + } + if (key_matrix_decode_fdt(&config.matrix, gd->fdt_blob, node)) { + debug("%s: Could not decode key matrix from fdt\n", __func__); + return -1; + } + if (config.matrix.fn_keycode) { + if (input_add_table(&config.input, KEY_FN, -1, + config.matrix.fn_keycode, + config.matrix.key_count)) + return -1; + } +#else +#error "Tegra keyboard driver requires FDT definitions" +#endif + + /* Set up pin mux and enable the clock */ + funcmux_select(PERIPH_ID_KBC, FUNCMUX_DEFAULT); + clock_enable(PERIPH_ID_KBC); + config_kbc_gpio(config.kbc); + + tegra_kbc_open(); + config.created = 1; + debug("%s: Tegra keyboard ready\n", __func__); + + return 0; +} + +int drv_keyboard_init(void) +{ + struct stdio_dev dev; + char *stdinname = getenv("stdin"); + int error; + + if (input_init(&config.input, 0)) { + debug("%s: Cannot set up input\n", __func__); + return -1; + } + config.input.read_keys = tegra_kbc_check; + + memset(&dev, '\0', sizeof(dev)); + strcpy(dev.name, "tegra-kbc"); + dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + dev.getc = kbd_getc; + dev.tstc = kbd_tstc; + dev.start = init_tegra_keyboard; + + /* Register the device. init_tegra_keyboard() will be called soon */ + error = input_stdio_register(&dev); + if (error) + return error; +#ifdef CONFIG_CONSOLE_MUX + error = iomux_doenv(stdin, stdinname); + if (error) + return error; +#endif + return 0; +} diff --git a/qemu/roms/u-boot/drivers/misc/Makefile b/qemu/roms/u-boot/drivers/misc/Makefile new file mode 100644 index 000000000..2f2e48f97 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/Makefile @@ -0,0 +1,25 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_ALI152X) += ali512x.o +obj-$(CONFIG_DS4510) += ds4510.o +obj-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o +obj-$(CONFIG_CROS_EC) += cros_ec.o +obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o +obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o +obj-$(CONFIG_CROS_EC_SANDBOX) += cros_ec_sandbox.o +obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o +obj-$(CONFIG_FSL_IIM) += fsl_iim.o +obj-$(CONFIG_GPIO_LED) += gpio_led.o +obj-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o +obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o +obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o +obj-$(CONFIG_NS87308) += ns87308.o +obj-$(CONFIG_PDSP188x) += pdsp188x.o +obj-$(CONFIG_STATUS_LED) += status_led.o +obj-$(CONFIG_TWL4030_LED) += twl4030_led.o +obj-$(CONFIG_FSL_IFC) += fsl_ifc.o diff --git a/qemu/roms/u-boot/drivers/misc/ali512x.c b/qemu/roms/u-boot/drivers/misc/ali512x.c new file mode 100644 index 000000000..f271f8a68 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/ali512x.c @@ -0,0 +1,402 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB . + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Based on sc520cdp.c from rolo 1.6: + *---------------------------------------------------------------------- + * (C) Copyright 2000 + * Sysgo Real-Time Solutions GmbH + * Klein-Winternheim, Germany + *---------------------------------------------------------------------- + */ + +#include + +#include +#include +#include + + +/* ALI M5123 Logical device numbers: + * 0 FDC + * 1 unused? + * 2 unused? + * 3 lpt + * 4 UART1 + * 5 UART2 + * 6 RTC + * 7 mouse/kbd + * 8 CIO + */ + +/* + ************************************************************ + * Some access primitives for the ALi chip: * + ************************************************************ + */ + +static void ali_write(u8 index, u8 value) +{ + /* write an arbirary register */ + outb(index, ALI_INDEX); + outb(value, ALI_DATA); +} + +#if 0 +static int ali_read(u8 index) +{ + outb(index, ALI_INDEX); + return inb(ALI_DATA); +} +#endif + +#define ALI_OPEN() \ + outb(0x51, ALI_INDEX); \ + outb(0x23, ALI_INDEX) + + +#define ALI_CLOSE() \ + outb(0xbb, ALI_INDEX) + +/* Select a logical device */ +#define ALI_SELDEV(dev) \ + ali_write(0x07, dev) + + +void ali512x_init(void) +{ + ALI_OPEN(); + + ali_write(0x02, 0x01); /* soft reset */ + ali_write(0x03, 0x03); /* disable access to CIOs */ + ali_write(0x22, 0x00); /* disable direct powerdown */ + ali_write(0x23, 0x00); /* disable auto powerdown */ + ali_write(0x24, 0x00); /* IR 8 is active hi, pin26 is PDIR */ + + ALI_CLOSE(); +} + +void ali512x_set_fdc(int enabled, u16 io, u8 irq, u8 dma_channel) +{ + ALI_OPEN(); + ALI_SELDEV(0); + + ali_write(0x30, enabled?1:0); + if (enabled) { + ali_write(0x60, io >> 8); + ali_write(0x61, io & 0xff); + ali_write(0x70, irq); + ali_write(0x74, dma_channel); + + /* AT mode, no drive swap */ + ali_write(0xf0, 0x08); + ali_write(0xf1, 0x00); + ali_write(0xf2, 0xff); + ali_write(0xf4, 0x00); + } + ALI_CLOSE(); +} + + +void ali512x_set_pp(int enabled, u16 io, u8 irq, u8 dma_channel) +{ + ALI_OPEN(); + ALI_SELDEV(3); + + ali_write(0x30, enabled?1:0); + if (enabled) { + ali_write(0x60, io >> 8); + ali_write(0x61, io & 0xff); + ali_write(0x70, irq); + ali_write(0x74, dma_channel); + + /* mode: EPP 1.9, ECP FIFO threshold = 7, IRQ active low */ + ali_write(0xf0, 0xbc); + /* 12 MHz, Burst DMA in ECP */ + ali_write(0xf1, 0x05); + } + ALI_CLOSE(); + +} + +void ali512x_set_uart(int enabled, int index, u16 io, u8 irq) +{ + ALI_OPEN(); + ALI_SELDEV(index?5:4); + + ali_write(0x30, enabled?1:0); + if (enabled) { + ali_write(0x60, io >> 8); + ali_write(0x61, io & 0xff); + ali_write(0x70, irq); + + ali_write(0xf0, 0x00); + ali_write(0xf1, 0x00); + + /* huh? write 0xf2 twice - a typo in rolo + * or some secret ali errata? Who knows? + */ + if (index) { + ali_write(0xf2, 0x00); + } + ali_write(0xf2, 0x0c); + } + ALI_CLOSE(); + +} + +void ali512x_set_uart2_irda(int enabled) +{ + ALI_OPEN(); + ALI_SELDEV(5); + + ali_write(0xf1, enabled?0x48:0x00); /* fullduplex IrDa */ + ALI_CLOSE(); + +} + +void ali512x_set_rtc(int enabled, u16 io, u8 irq) +{ + ALI_OPEN(); + ALI_SELDEV(6); + + ali_write(0x30, enabled?1:0); + if (enabled) { + ali_write(0x60, io >> 8); + ali_write(0x61, io & 0xff); + ali_write(0x70, irq); + + ali_write(0xf0, 0x00); + } + ALI_CLOSE(); +} + +void ali512x_set_kbc(int enabled, u8 kbc_irq, u8 mouse_irq) +{ + ALI_OPEN(); + ALI_SELDEV(7); + + ali_write(0x30, enabled?1:0); + if (enabled) { + ali_write(0x70, kbc_irq); + ali_write(0x72, mouse_irq); + + ali_write(0xf0, 0x00); + } + ALI_CLOSE(); +} + + +/* Common I/O + * + * (This descripotsion is base on several incompete sources + * since I have not been able to obtain any datasheet for the device + * there may be some mis-understandings burried in here. + * -- Daniel daniel@omicron.se) + * + * There are 22 CIO pins numbered + * 10-17 + * 20-25 + * 30-37 + * + * 20-24 are dedicated CIO pins, the other 17 are muliplexed with + * other functions. + * + * Secondary + * CIO Pin Function Decription + * ======================================================= + * CIO10 IRQIN1 Interrupt input 1? + * CIO11 IRQIN2 Interrupt input 2? + * CIO12 IRRX IrDa Receive + * CIO13 IRTX IrDa Transmit + * CIO14 P21 KBC P21 fucntion + * CIO15 P20 KBC P21 fucntion + * CIO16 I2C_CLK I2C Clock + * CIO17 I2C_DAT I2C Data + * + * CIO20 - + * CIO21 - + * CIO22 - + * CIO23 - + * CIO24 - + * CIO25 LOCK Keylock + * + * CIO30 KBC_CLK Keybaord Clock + * CIO31 CS0J General Chip Select decoder CS0J + * CIO32 CS1J General Chip Select decoder CS1J + * CIO33 ALT_KCLK Alternative Keyboard Clock + * CIO34 ALT_KDAT Alternative Keyboard Data + * CIO35 ALT_MCLK Alternative Mouse Clock + * CIO36 ALT_MDAT Alternative Mouse Data + * CIO37 ALT_KBC Alternative KBC select + * + * The CIO use an indirect address scheme. + * + * Reigster 3 in the SIO is used to select the index and data + * port addresses where the CIO I/O registers show up. + * The function selection registers are accessible under + * function SIO 8. + * + * SIO reigster 3 (CIO Address Selection) bit definitions: + * bit 7 CIO index and data registers enabled + * bit 1-0 CIO indirect registers port address select + * 0 index = 0xE0 data = 0xE1 + * 1 index = 0xE2 data = 0xE3 + * 2 index = 0xE4 data = 0xE5 + * 3 index = 0xEA data = 0xEB + * + * There are three CIO I/O register accessed via CIO index port and CIO data port + * 0x01 CIO 10-17 data + * 0x02 CIO 20-25 data (bits 7-6 unused) + * 0x03 CIO 30-37 data + * + * + * The pin function is accessed through normal + * SIO registers, each register have the same format: + * + * Bit Function Value + * 0 Input/output 1=input + * 1 Polarity of signal 1=inverted + * 2 Unused ?? + * 3 Function (normal or special) 1=special + * 7-4 Unused + * + * SIO REG + * 0xe0 CIO 10 Config + * 0xe1 CIO 11 Config + * 0xe2 CIO 12 Config + * 0xe3 CIO 13 Config + * 0xe4 CIO 14 Config + * 0xe5 CIO 15 Config + * 0xe6 CIO 16 Config + * 0xe7 CIO 16 Config + * + * 0xe8 CIO 20 Config + * 0xe9 CIO 21 Config + * 0xea CIO 22 Config + * 0xeb CIO 23 Config + * 0xec CIO 24 Config + * 0xed CIO 25 Config + * + * 0xf5 CIO 30 Config + * 0xf6 CIO 31 Config + * 0xf7 CIO 32 Config + * 0xf8 CIO 33 Config + * 0xf9 CIO 34 Config + * 0xfa CIO 35 Config + * 0xfb CIO 36 Config + * 0xfc CIO 37 Config + * + */ + +#define ALI_CIO_PORT_SEL 0x83 +#define ALI_CIO_INDEX 0xea +#define ALI_CIO_DATA 0xeb + +void ali512x_set_cio(int enabled) +{ + int i; + + ALI_OPEN(); + + if (enabled) { + ali_write(0x3, ALI_CIO_PORT_SEL); /* Enable CIO data register */ + } else { + ali_write(0x3, ALI_CIO_PORT_SEL & ~0x80); + } + + ALI_SELDEV(8); + + ali_write(0x30, enabled?1:0); + + /* set all pins to input to start with */ + for (i=0xe0;i<0xee;i++) { + ali_write(i, 1); + } + + for (i=0xf5;i<0xfe;i++) { + ali_write(i, 1); + } + + ALI_CLOSE(); +} + + +void ali512x_cio_function(int pin, int special, int inv, int input) +{ + u8 data; + u8 addr; + + /* valid pins are 10-17, 20-25 and 30-37 */ + if (pin >= 10 && pin <= 17) { + addr = 0xe0+(pin&7); + } else if (pin >= 20 && pin <= 25) { + addr = 0xe8+(pin&7); + } else if (pin >= 30 && pin <= 37) { + addr = 0xf5+(pin&7); + } else { + return; + } + + ALI_OPEN(); + + ALI_SELDEV(8); + + + data=0xf4; + if (special) { + data |= 0x08; + } else { + if (inv) { + data |= 0x02; + } + if (input) { + data |= 0x01; + } + } + + ali_write(addr, data); + + ALI_CLOSE(); +} + +void ali512x_cio_out(int pin, int value) +{ + u8 reg; + u8 data; + u8 bit; + + reg = pin/10; + bit = 1 << (pin%10); + + + outb(reg, ALI_CIO_INDEX); /* select I/O register */ + data = inb(ALI_CIO_DATA); + if (value) { + data |= bit; + } else { + data &= ~bit; + } + outb(data, ALI_CIO_DATA); +} + +int ali512x_cio_in(int pin) +{ + u8 reg; + u8 data; + u8 bit; + + /* valid pins are 10-17, 20-25 and 30-37 */ + reg = pin/10; + bit = 1 << (pin%10); + + + outb(reg, ALI_CIO_INDEX); /* select I/O register */ + data = inb(ALI_CIO_DATA); + + return data & bit; +} diff --git a/qemu/roms/u-boot/drivers/misc/cbmem_console.c b/qemu/roms/u-boot/drivers/misc/cbmem_console.c new file mode 100644 index 000000000..80a84fdf8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/cbmem_console.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include + +#ifndef CONFIG_SYS_COREBOOT +#error This driver requires coreboot +#endif + +#include + +struct cbmem_console { + u32 buffer_size; + u32 buffer_cursor; + u8 buffer_body[0]; +} __attribute__ ((__packed__)); + +static struct cbmem_console *cbmem_console_p; + +void cbmemc_putc(char data) +{ + int cursor; + + cursor = cbmem_console_p->buffer_cursor++; + if (cursor < cbmem_console_p->buffer_size) + cbmem_console_p->buffer_body[cursor] = data; +} + +void cbmemc_puts(const char *str) +{ + char c; + + while ((c = *str++) != 0) + cbmemc_putc(c); +} + +int cbmemc_init(void) +{ + int rc; + struct stdio_dev cons_dev; + cbmem_console_p = lib_sysinfo.cbmem_cons; + + memset(&cons_dev, 0, sizeof(cons_dev)); + + strcpy(cons_dev.name, "cbmem"); + cons_dev.flags = DEV_FLAGS_OUTPUT; /* Output only */ + cons_dev.putc = cbmemc_putc; + cons_dev.puts = cbmemc_puts; + + rc = stdio_register(&cons_dev); + + return (rc == 0) ? 1 : rc; +} diff --git a/qemu/roms/u-boot/drivers/misc/cros_ec.c b/qemu/roms/u-boot/drivers/misc/cros_ec.c new file mode 100644 index 000000000..068373b94 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/cros_ec.c @@ -0,0 +1,1796 @@ +/* + * Chromium OS cros_ec driver + * + * Copyright (c) 2012 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This is the interface to the Chrome OS EC. It provides keyboard functions, + * power control and battery management. Quite a few other functions are + * provided to enable the EC software to be updated, talk to the EC's I2C bus + * and store a small amount of data in a memory which persists while the EC + * is not reset. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_TRACE +#define debug_trace(fmt, b...) debug(fmt, #b) +#else +#define debug_trace(fmt, b...) +#endif + +enum { + /* Timeout waiting for a flash erase command to complete */ + CROS_EC_CMD_TIMEOUT_MS = 5000, + /* Timeout waiting for a synchronous hash to be recomputed */ + CROS_EC_CMD_HASH_TIMEOUT_MS = 2000, +}; + +static struct cros_ec_dev static_dev, *last_dev; + +DECLARE_GLOBAL_DATA_PTR; + +/* Note: depends on enum ec_current_image */ +static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"}; + +void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data, int len) +{ +#ifdef DEBUG + int i; + + printf("%s: ", name); + if (cmd != -1) + printf("cmd=%#x: ", cmd); + for (i = 0; i < len; i++) + printf("%02x ", data[i]); + printf("\n"); +#endif +} + +/* + * Calculate a simple 8-bit checksum of a data block + * + * @param data Data block to checksum + * @param size Size of data block in bytes + * @return checksum value (0 to 255) + */ +int cros_ec_calc_checksum(const uint8_t *data, int size) +{ + int csum, i; + + for (i = csum = 0; i < size; i++) + csum += data[i]; + return csum & 0xff; +} + +/** + * Create a request packet for protocol version 3. + * + * The packet is stored in the device's internal output buffer. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @return packet size in bytes, or <0 if error. + */ +static int create_proto3_request(struct cros_ec_dev *dev, + int cmd, int cmd_version, + const void *dout, int dout_len) +{ + struct ec_host_request *rq = (struct ec_host_request *)dev->dout; + int out_bytes = dout_len + sizeof(*rq); + + /* Fail if output size is too big */ + if (out_bytes > (int)sizeof(dev->dout)) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -EC_RES_REQUEST_TRUNCATED; + } + + /* Fill in request packet */ + rq->struct_version = EC_HOST_REQUEST_VERSION; + rq->checksum = 0; + rq->command = cmd; + rq->command_version = cmd_version; + rq->reserved = 0; + rq->data_len = dout_len; + + /* Copy data after header */ + memcpy(rq + 1, dout, dout_len); + + /* Write checksum field so the entire packet sums to 0 */ + rq->checksum = (uint8_t)(-cros_ec_calc_checksum(dev->dout, out_bytes)); + + cros_ec_dump_data("out", cmd, dev->dout, out_bytes); + + /* Return size of request packet */ + return out_bytes; +} + +/** + * Prepare the device to receive a protocol version 3 response. + * + * @param dev CROS-EC device + * @param din_len Maximum size of response in bytes + * @return maximum expected number of bytes in response, or <0 if error. + */ +static int prepare_proto3_response_buffer(struct cros_ec_dev *dev, int din_len) +{ + int in_bytes = din_len + sizeof(struct ec_host_response); + + /* Fail if input size is too big */ + if (in_bytes > (int)sizeof(dev->din)) { + debug("%s: Cannot receive %d bytes\n", __func__, din_len); + return -EC_RES_RESPONSE_TOO_BIG; + } + + /* Return expected size of response packet */ + return in_bytes; +} + +/** + * Handle a protocol version 3 response packet. + * + * The packet must already be stored in the device's internal input buffer. + * + * @param dev CROS-EC device + * @param dinp Returns pointer to response data + * @param din_len Maximum size of response in bytes + * @return number of bytes of response data, or <0 if error + */ +static int handle_proto3_response(struct cros_ec_dev *dev, + uint8_t **dinp, int din_len) +{ + struct ec_host_response *rs = (struct ec_host_response *)dev->din; + int in_bytes; + int csum; + + cros_ec_dump_data("in-header", -1, dev->din, sizeof(*rs)); + + /* Check input data */ + if (rs->struct_version != EC_HOST_RESPONSE_VERSION) { + debug("%s: EC response version mismatch\n", __func__); + return -EC_RES_INVALID_RESPONSE; + } + + if (rs->reserved) { + debug("%s: EC response reserved != 0\n", __func__); + return -EC_RES_INVALID_RESPONSE; + } + + if (rs->data_len > din_len) { + debug("%s: EC returned too much data\n", __func__); + return -EC_RES_RESPONSE_TOO_BIG; + } + + cros_ec_dump_data("in-data", -1, dev->din + sizeof(*rs), rs->data_len); + + /* Update in_bytes to actual data size */ + in_bytes = sizeof(*rs) + rs->data_len; + + /* Verify checksum */ + csum = cros_ec_calc_checksum(dev->din, in_bytes); + if (csum) { + debug("%s: EC response checksum invalid: 0x%02x\n", __func__, + csum); + return -EC_RES_INVALID_CHECKSUM; + } + + /* Return error result, if any */ + if (rs->result) + return -(int)rs->result; + + /* If we're still here, set response data pointer and return length */ + *dinp = (uint8_t *)(rs + 1); + + return rs->data_len; +} + +static int send_command_proto3(struct cros_ec_dev *dev, + int cmd, int cmd_version, + const void *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int out_bytes, in_bytes; + int rv; + + /* Create request packet */ + out_bytes = create_proto3_request(dev, cmd, cmd_version, + dout, dout_len); + if (out_bytes < 0) + return out_bytes; + + /* Prepare response buffer */ + in_bytes = prepare_proto3_response_buffer(dev, din_len); + if (in_bytes < 0) + return in_bytes; + + switch (dev->interface) { +#ifdef CONFIG_CROS_EC_SPI + case CROS_EC_IF_SPI: + rv = cros_ec_spi_packet(dev, out_bytes, in_bytes); + break; +#endif +#ifdef CONFIG_CROS_EC_SANDBOX + case CROS_EC_IF_SANDBOX: + rv = cros_ec_sandbox_packet(dev, out_bytes, in_bytes); + break; +#endif + case CROS_EC_IF_NONE: + /* TODO: support protocol 3 for LPC, I2C; for now fall through */ + default: + debug("%s: Unsupported interface\n", __func__); + rv = -1; + } + if (rv < 0) + return rv; + + /* Process the response */ + return handle_proto3_response(dev, dinp, din_len); +} + +static int send_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const void *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int ret = -1; + + /* Handle protocol version 3 support */ + if (dev->protocol_version == 3) { + return send_command_proto3(dev, cmd, cmd_version, + dout, dout_len, dinp, din_len); + } + + switch (dev->interface) { +#ifdef CONFIG_CROS_EC_SPI + case CROS_EC_IF_SPI: + ret = cros_ec_spi_command(dev, cmd, cmd_version, + (const uint8_t *)dout, dout_len, + dinp, din_len); + break; +#endif +#ifdef CONFIG_CROS_EC_I2C + case CROS_EC_IF_I2C: + ret = cros_ec_i2c_command(dev, cmd, cmd_version, + (const uint8_t *)dout, dout_len, + dinp, din_len); + break; +#endif +#ifdef CONFIG_CROS_EC_LPC + case CROS_EC_IF_LPC: + ret = cros_ec_lpc_command(dev, cmd, cmd_version, + (const uint8_t *)dout, dout_len, + dinp, din_len); + break; +#endif + case CROS_EC_IF_NONE: + default: + ret = -1; + } + + return ret; +} + +/** + * Send a command to the CROS-EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Response data (may be NULL If din_len=0). + * If not NULL, it will be updated to point to the data + * and will always be double word aligned (64-bits) + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +static int ec_command_inptr(struct cros_ec_dev *dev, uint8_t cmd, + int cmd_version, const void *dout, int dout_len, uint8_t **dinp, + int din_len) +{ + uint8_t *din = NULL; + int len; + + len = send_command(dev, cmd, cmd_version, dout, dout_len, + &din, din_len); + + /* If the command doesn't complete, wait a while */ + if (len == -EC_RES_IN_PROGRESS) { + struct ec_response_get_comms_status *resp = NULL; + ulong start; + + /* Wait for command to complete */ + start = get_timer(0); + do { + int ret; + + mdelay(50); /* Insert some reasonable delay */ + ret = send_command(dev, EC_CMD_GET_COMMS_STATUS, 0, + NULL, 0, + (uint8_t **)&resp, sizeof(*resp)); + if (ret < 0) + return ret; + + if (get_timer(start) > CROS_EC_CMD_TIMEOUT_MS) { + debug("%s: Command %#02x timeout\n", + __func__, cmd); + return -EC_RES_TIMEOUT; + } + } while (resp->flags & EC_COMMS_STATUS_PROCESSING); + + /* OK it completed, so read the status response */ + /* not sure why it was 0 for the last argument */ + len = send_command(dev, EC_CMD_RESEND_RESPONSE, 0, + NULL, 0, &din, din_len); + } + + debug("%s: len=%d, dinp=%p, *dinp=%p\n", __func__, len, dinp, + dinp ? *dinp : NULL); + if (dinp) { + /* If we have any data to return, it must be 64bit-aligned */ + assert(len <= 0 || !((uintptr_t)din & 7)); + *dinp = din; + } + + return len; +} + +/** + * Send a command to the CROS-EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param din Response data (may be NULL If din_len=0). + * It not NULL, it is a place for ec_command() to copy the + * data to. + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +static int ec_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const void *dout, int dout_len, + void *din, int din_len) +{ + uint8_t *in_buffer; + int len; + + assert((din_len == 0) || din); + len = ec_command_inptr(dev, cmd, cmd_version, dout, dout_len, + &in_buffer, din_len); + if (len > 0) { + /* + * If we were asked to put it somewhere, do so, otherwise just + * disregard the result. + */ + if (din && in_buffer) { + assert(len <= din_len); + memmove(din, in_buffer, len); + } + } + return len; +} + +int cros_ec_scan_keyboard(struct cros_ec_dev *dev, struct mbkp_keyscan *scan) +{ + if (ec_command(dev, EC_CMD_MKBP_STATE, 0, NULL, 0, scan, + sizeof(scan->data)) != sizeof(scan->data)) + return -1; + + return 0; +} + +int cros_ec_read_id(struct cros_ec_dev *dev, char *id, int maxlen) +{ + struct ec_response_get_version *r; + + if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, + (uint8_t **)&r, sizeof(*r)) != sizeof(*r)) + return -1; + + if (maxlen > (int)sizeof(r->version_string_ro)) + maxlen = sizeof(r->version_string_ro); + + switch (r->current_image) { + case EC_IMAGE_RO: + memcpy(id, r->version_string_ro, maxlen); + break; + case EC_IMAGE_RW: + memcpy(id, r->version_string_rw, maxlen); + break; + default: + return -1; + } + + id[maxlen - 1] = '\0'; + return 0; +} + +int cros_ec_read_version(struct cros_ec_dev *dev, + struct ec_response_get_version **versionp) +{ + if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, + (uint8_t **)versionp, sizeof(**versionp)) + != sizeof(**versionp)) + return -1; + + return 0; +} + +int cros_ec_read_build_info(struct cros_ec_dev *dev, char **strp) +{ + if (ec_command_inptr(dev, EC_CMD_GET_BUILD_INFO, 0, NULL, 0, + (uint8_t **)strp, EC_PROTO2_MAX_PARAM_SIZE) < 0) + return -1; + + return 0; +} + +int cros_ec_read_current_image(struct cros_ec_dev *dev, + enum ec_current_image *image) +{ + struct ec_response_get_version *r; + + if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, + (uint8_t **)&r, sizeof(*r)) != sizeof(*r)) + return -1; + + *image = r->current_image; + return 0; +} + +static int cros_ec_wait_on_hash_done(struct cros_ec_dev *dev, + struct ec_response_vboot_hash *hash) +{ + struct ec_params_vboot_hash p; + ulong start; + + start = get_timer(0); + while (hash->status == EC_VBOOT_HASH_STATUS_BUSY) { + mdelay(50); /* Insert some reasonable delay */ + + p.cmd = EC_VBOOT_HASH_GET; + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + hash, sizeof(*hash)) < 0) + return -1; + + if (get_timer(start) > CROS_EC_CMD_HASH_TIMEOUT_MS) { + debug("%s: EC_VBOOT_HASH_GET timeout\n", __func__); + return -EC_RES_TIMEOUT; + } + } + return 0; +} + + +int cros_ec_read_hash(struct cros_ec_dev *dev, + struct ec_response_vboot_hash *hash) +{ + struct ec_params_vboot_hash p; + int rv; + + p.cmd = EC_VBOOT_HASH_GET; + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + hash, sizeof(*hash)) < 0) + return -1; + + /* If the EC is busy calculating the hash, fidget until it's done. */ + rv = cros_ec_wait_on_hash_done(dev, hash); + if (rv) + return rv; + + /* If the hash is valid, we're done. Otherwise, we have to kick it off + * again and wait for it to complete. Note that we explicitly assume + * that hashing zero bytes is always wrong, even though that would + * produce a valid hash value. */ + if (hash->status == EC_VBOOT_HASH_STATUS_DONE && hash->size) + return 0; + + debug("%s: No valid hash (status=%d size=%d). Compute one...\n", + __func__, hash->status, hash->size); + + p.cmd = EC_VBOOT_HASH_START; + p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; + p.nonce_size = 0; + p.offset = EC_VBOOT_HASH_OFFSET_RW; + + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + hash, sizeof(*hash)) < 0) + return -1; + + rv = cros_ec_wait_on_hash_done(dev, hash); + if (rv) + return rv; + + debug("%s: hash done\n", __func__); + + return 0; +} + +static int cros_ec_invalidate_hash(struct cros_ec_dev *dev) +{ + struct ec_params_vboot_hash p; + struct ec_response_vboot_hash *hash; + + /* We don't have an explict command for the EC to discard its current + * hash value, so we'll just tell it to calculate one that we know is + * wrong (we claim that hashing zero bytes is always invalid). + */ + p.cmd = EC_VBOOT_HASH_RECALC; + p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; + p.nonce_size = 0; + p.offset = 0; + p.size = 0; + + debug("%s:\n", __func__); + + if (ec_command_inptr(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + (uint8_t **)&hash, sizeof(*hash)) < 0) + return -1; + + /* No need to wait for it to finish */ + return 0; +} + +int cros_ec_reboot(struct cros_ec_dev *dev, enum ec_reboot_cmd cmd, + uint8_t flags) +{ + struct ec_params_reboot_ec p; + + p.cmd = cmd; + p.flags = flags; + + if (ec_command_inptr(dev, EC_CMD_REBOOT_EC, 0, &p, sizeof(p), NULL, 0) + < 0) + return -1; + + if (!(flags & EC_REBOOT_FLAG_ON_AP_SHUTDOWN)) { + /* + * EC reboot will take place immediately so delay to allow it + * to complete. Note that some reboot types (EC_REBOOT_COLD) + * will reboot the AP as well, in which case we won't actually + * get to this point. + */ + /* + * TODO(rspangler@chromium.org): Would be nice if we had a + * better way to determine when the reboot is complete. Could + * we poll a memory-mapped LPC value? + */ + udelay(50000); + } + + return 0; +} + +int cros_ec_interrupt_pending(struct cros_ec_dev *dev) +{ + /* no interrupt support : always poll */ + if (!fdt_gpio_isvalid(&dev->ec_int)) + return -ENOENT; + + return !gpio_get_value(dev->ec_int.gpio); +} + +int cros_ec_info(struct cros_ec_dev *dev, struct ec_response_mkbp_info *info) +{ + if (ec_command(dev, EC_CMD_MKBP_INFO, 0, NULL, 0, info, + sizeof(*info)) != sizeof(*info)) + return -1; + + return 0; +} + +int cros_ec_get_host_events(struct cros_ec_dev *dev, uint32_t *events_ptr) +{ + struct ec_response_host_event_mask *resp; + + /* + * Use the B copy of the event flags, because the main copy is already + * used by ACPI/SMI. + */ + if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_GET_B, 0, NULL, 0, + (uint8_t **)&resp, sizeof(*resp)) < (int)sizeof(*resp)) + return -1; + + if (resp->mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INVALID)) + return -1; + + *events_ptr = resp->mask; + return 0; +} + +int cros_ec_clear_host_events(struct cros_ec_dev *dev, uint32_t events) +{ + struct ec_params_host_event_mask params; + + params.mask = events; + + /* + * Use the B copy of the event flags, so it affects the data returned + * by cros_ec_get_host_events(). + */ + if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_CLEAR_B, 0, + ¶ms, sizeof(params), NULL, 0) < 0) + return -1; + + return 0; +} + +int cros_ec_flash_protect(struct cros_ec_dev *dev, + uint32_t set_mask, uint32_t set_flags, + struct ec_response_flash_protect *resp) +{ + struct ec_params_flash_protect params; + + params.mask = set_mask; + params.flags = set_flags; + + if (ec_command(dev, EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT, + ¶ms, sizeof(params), + resp, sizeof(*resp)) != sizeof(*resp)) + return -1; + + return 0; +} + +static int cros_ec_check_version(struct cros_ec_dev *dev) +{ + struct ec_params_hello req; + struct ec_response_hello *resp; + +#ifdef CONFIG_CROS_EC_LPC + /* LPC has its own way of doing this */ + if (dev->interface == CROS_EC_IF_LPC) + return cros_ec_lpc_check_version(dev); +#endif + + /* + * TODO(sjg@chromium.org). + * There is a strange oddity here with the EC. We could just ignore + * the response, i.e. pass the last two parameters as NULL and 0. + * In this case we won't read back very many bytes from the EC. + * On the I2C bus the EC gets upset about this and will try to send + * the bytes anyway. This means that we will have to wait for that + * to complete before continuing with a new EC command. + * + * This problem is probably unique to the I2C bus. + * + * So for now, just read all the data anyway. + */ + + /* Try sending a version 3 packet */ + dev->protocol_version = 3; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) > 0) { + return 0; + } + + /* Try sending a version 2 packet */ + dev->protocol_version = 2; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) > 0) { + return 0; + } + + /* + * Fail if we're still here, since the EC doesn't understand any + * protcol version we speak. Version 1 interface without command + * version is no longer supported, and we don't know about any new + * protocol versions. + */ + dev->protocol_version = 0; + printf("%s: ERROR: old EC interface not supported\n", __func__); + return -1; +} + +int cros_ec_test(struct cros_ec_dev *dev) +{ + struct ec_params_hello req; + struct ec_response_hello *resp; + + req.in_data = 0x12345678; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) { + printf("ec_command_inptr() returned error\n"); + return -1; + } + if (resp->out_data != req.in_data + 0x01020304) { + printf("Received invalid handshake %x\n", resp->out_data); + return -1; + } + + return 0; +} + +int cros_ec_flash_offset(struct cros_ec_dev *dev, enum ec_flash_region region, + uint32_t *offset, uint32_t *size) +{ + struct ec_params_flash_region_info p; + struct ec_response_flash_region_info *r; + int ret; + + p.region = region; + ret = ec_command_inptr(dev, EC_CMD_FLASH_REGION_INFO, + EC_VER_FLASH_REGION_INFO, + &p, sizeof(p), (uint8_t **)&r, sizeof(*r)); + if (ret != sizeof(*r)) + return -1; + + if (offset) + *offset = r->offset; + if (size) + *size = r->size; + + return 0; +} + +int cros_ec_flash_erase(struct cros_ec_dev *dev, uint32_t offset, uint32_t size) +{ + struct ec_params_flash_erase p; + + p.offset = offset; + p.size = size; + return ec_command_inptr(dev, EC_CMD_FLASH_ERASE, 0, &p, sizeof(p), + NULL, 0); +} + +/** + * Write a single block to the flash + * + * Write a block of data to the EC flash. The size must not exceed the flash + * write block size which you can obtain from cros_ec_flash_write_burst_size(). + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to write for a particular region. + * + * Attempting to write to the region where the EC is currently running from + * will result in an error. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to write + * @param offset Offset within flash to write to. + * @param size Number of bytes to write + * @return 0 if ok, -1 on error + */ +static int cros_ec_flash_write_block(struct cros_ec_dev *dev, + const uint8_t *data, uint32_t offset, uint32_t size) +{ + struct ec_params_flash_write p; + + p.offset = offset; + p.size = size; + assert(data && p.size <= EC_FLASH_WRITE_VER0_SIZE); + memcpy(&p + 1, data, p.size); + + return ec_command_inptr(dev, EC_CMD_FLASH_WRITE, 0, + &p, sizeof(p), NULL, 0) >= 0 ? 0 : -1; +} + +/** + * Return optimal flash write burst size + */ +static int cros_ec_flash_write_burst_size(struct cros_ec_dev *dev) +{ + return EC_FLASH_WRITE_VER0_SIZE; +} + +/** + * Check if a block of data is erased (all 0xff) + * + * This function is useful when dealing with flash, for checking whether a + * data block is erased and thus does not need to be programmed. + * + * @param data Pointer to data to check (must be word-aligned) + * @param size Number of bytes to check (must be word-aligned) + * @return 0 if erased, non-zero if any word is not erased + */ +static int cros_ec_data_is_erased(const uint32_t *data, int size) +{ + assert(!(size & 3)); + size /= sizeof(uint32_t); + for (; size > 0; size -= 4, data++) + if (*data != -1U) + return 0; + + return 1; +} + +int cros_ec_flash_write(struct cros_ec_dev *dev, const uint8_t *data, + uint32_t offset, uint32_t size) +{ + uint32_t burst = cros_ec_flash_write_burst_size(dev); + uint32_t end, off; + int ret; + + /* + * TODO: round up to the nearest multiple of write size. Can get away + * without that on link right now because its write size is 4 bytes. + */ + end = offset + size; + for (off = offset; off < end; off += burst, data += burst) { + uint32_t todo; + + /* If the data is empty, there is no point in programming it */ + todo = min(end - off, burst); + if (dev->optimise_flash_write && + cros_ec_data_is_erased((uint32_t *)data, todo)) + continue; + + ret = cros_ec_flash_write_block(dev, data, off, todo); + if (ret) + return ret; + } + + return 0; +} + +/** + * Read a single block from the flash + * + * Read a block of data from the EC flash. The size must not exceed the flash + * write block size which you can obtain from cros_ec_flash_write_burst_size(). + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to read for a particular region. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to read into + * @param offset Offset within flash to read from + * @param size Number of bytes to read + * @return 0 if ok, -1 on error + */ +static int cros_ec_flash_read_block(struct cros_ec_dev *dev, uint8_t *data, + uint32_t offset, uint32_t size) +{ + struct ec_params_flash_read p; + + p.offset = offset; + p.size = size; + + return ec_command(dev, EC_CMD_FLASH_READ, 0, + &p, sizeof(p), data, size) >= 0 ? 0 : -1; +} + +int cros_ec_flash_read(struct cros_ec_dev *dev, uint8_t *data, uint32_t offset, + uint32_t size) +{ + uint32_t burst = cros_ec_flash_write_burst_size(dev); + uint32_t end, off; + int ret; + + end = offset + size; + for (off = offset; off < end; off += burst, data += burst) { + ret = cros_ec_flash_read_block(dev, data, off, + min(end - off, burst)); + if (ret) + return ret; + } + + return 0; +} + +int cros_ec_flash_update_rw(struct cros_ec_dev *dev, + const uint8_t *image, int image_size) +{ + uint32_t rw_offset, rw_size; + int ret; + + if (cros_ec_flash_offset(dev, EC_FLASH_REGION_RW, &rw_offset, &rw_size)) + return -1; + if (image_size > (int)rw_size) + return -1; + + /* Invalidate the existing hash, just in case the AP reboots + * unexpectedly during the update. If that happened, the EC RW firmware + * would be invalid, but the EC would still have the original hash. + */ + ret = cros_ec_invalidate_hash(dev); + if (ret) + return ret; + + /* + * Erase the entire RW section, so that the EC doesn't see any garbage + * past the new image if it's smaller than the current image. + * + * TODO: could optimize this to erase just the current image, since + * presumably everything past that is 0xff's. But would still need to + * round up to the nearest multiple of erase size. + */ + ret = cros_ec_flash_erase(dev, rw_offset, rw_size); + if (ret) + return ret; + + /* Write the image */ + ret = cros_ec_flash_write(dev, image, rw_offset, image_size); + if (ret) + return ret; + + return 0; +} + +int cros_ec_read_vbnvcontext(struct cros_ec_dev *dev, uint8_t *block) +{ + struct ec_params_vbnvcontext p; + int len; + + p.op = EC_VBNV_CONTEXT_OP_READ; + + len = ec_command(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, + &p, sizeof(p), block, EC_VBNV_BLOCK_SIZE); + if (len < EC_VBNV_BLOCK_SIZE) + return -1; + + return 0; +} + +int cros_ec_write_vbnvcontext(struct cros_ec_dev *dev, const uint8_t *block) +{ + struct ec_params_vbnvcontext p; + int len; + + p.op = EC_VBNV_CONTEXT_OP_WRITE; + memcpy(p.block, block, sizeof(p.block)); + + len = ec_command_inptr(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, + &p, sizeof(p), NULL, 0); + if (len < 0) + return -1; + + return 0; +} + +int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state) +{ + struct ec_params_ldo_set params; + + params.index = index; + params.state = state; + + if (ec_command_inptr(dev, EC_CMD_LDO_SET, 0, + ¶ms, sizeof(params), + NULL, 0)) + return -1; + + return 0; +} + +int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state) +{ + struct ec_params_ldo_get params; + struct ec_response_ldo_get *resp; + + params.index = index; + + if (ec_command_inptr(dev, EC_CMD_LDO_GET, 0, + ¶ms, sizeof(params), + (uint8_t **)&resp, sizeof(*resp)) != sizeof(*resp)) + return -1; + + *state = resp->state; + + return 0; +} + +/** + * Decode EC interface details from the device tree and allocate a suitable + * device. + * + * @param blob Device tree blob + * @param node Node to decode from + * @param devp Returns a pointer to the new allocated device + * @return 0 if ok, -1 on error + */ +static int cros_ec_decode_fdt(const void *blob, int node, + struct cros_ec_dev **devp) +{ + enum fdt_compat_id compat; + struct cros_ec_dev *dev; + int parent; + + /* See what type of parent we are inside (this is expensive) */ + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + dev = &static_dev; + dev->node = node; + dev->parent_node = parent; + + compat = fdtdec_lookup(blob, parent); + switch (compat) { +#ifdef CONFIG_CROS_EC_SPI + case COMPAT_SAMSUNG_EXYNOS_SPI: + dev->interface = CROS_EC_IF_SPI; + if (cros_ec_spi_decode_fdt(dev, blob)) + return -1; + break; +#endif +#ifdef CONFIG_CROS_EC_I2C + case COMPAT_SAMSUNG_S3C2440_I2C: + dev->interface = CROS_EC_IF_I2C; + if (cros_ec_i2c_decode_fdt(dev, blob)) + return -1; + break; +#endif +#ifdef CONFIG_CROS_EC_LPC + case COMPAT_INTEL_LPC: + dev->interface = CROS_EC_IF_LPC; + break; +#endif +#ifdef CONFIG_CROS_EC_SANDBOX + case COMPAT_SANDBOX_HOST_EMULATION: + dev->interface = CROS_EC_IF_SANDBOX; + break; +#endif + default: + debug("%s: Unknown compat id %d\n", __func__, compat); + return -1; + } + + fdtdec_decode_gpio(blob, node, "ec-interrupt", &dev->ec_int); + dev->optimise_flash_write = fdtdec_get_bool(blob, node, + "optimise-flash-write"); + *devp = dev; + + return 0; +} + +int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp) +{ + char id[MSG_BYTES]; + struct cros_ec_dev *dev; + int node = 0; + + *cros_ecp = NULL; + do { + node = fdtdec_next_compatible(blob, node, + COMPAT_GOOGLE_CROS_EC); + if (node < 0) { + debug("%s: Node not found\n", __func__); + return 0; + } + } while (!fdtdec_get_is_enabled(blob, node)); + + if (cros_ec_decode_fdt(blob, node, &dev)) { + debug("%s: Failed to decode device.\n", __func__); + return -CROS_EC_ERR_FDT_DECODE; + } + + switch (dev->interface) { +#ifdef CONFIG_CROS_EC_SPI + case CROS_EC_IF_SPI: + if (cros_ec_spi_init(dev, blob)) { + debug("%s: Could not setup SPI interface\n", __func__); + return -CROS_EC_ERR_DEV_INIT; + } + break; +#endif +#ifdef CONFIG_CROS_EC_I2C + case CROS_EC_IF_I2C: + if (cros_ec_i2c_init(dev, blob)) + return -CROS_EC_ERR_DEV_INIT; + break; +#endif +#ifdef CONFIG_CROS_EC_LPC + case CROS_EC_IF_LPC: + if (cros_ec_lpc_init(dev, blob)) + return -CROS_EC_ERR_DEV_INIT; + break; +#endif +#ifdef CONFIG_CROS_EC_SANDBOX + case CROS_EC_IF_SANDBOX: + if (cros_ec_sandbox_init(dev, blob)) + return -CROS_EC_ERR_DEV_INIT; + break; +#endif + case CROS_EC_IF_NONE: + default: + return 0; + } + + /* we will poll the EC interrupt line */ + fdtdec_setup_gpio(&dev->ec_int); + if (fdt_gpio_isvalid(&dev->ec_int)) + gpio_direction_input(dev->ec_int.gpio); + + if (cros_ec_check_version(dev)) { + debug("%s: Could not detect CROS-EC version\n", __func__); + return -CROS_EC_ERR_CHECK_VERSION; + } + + if (cros_ec_read_id(dev, id, sizeof(id))) { + debug("%s: Could not read KBC ID\n", __func__); + return -CROS_EC_ERR_READ_ID; + } + + /* Remember this device for use by the cros_ec command */ + last_dev = *cros_ecp = dev; + debug("Google Chrome EC CROS-EC driver ready, id '%s'\n", id); + + return 0; +} + +int cros_ec_decode_region(int argc, char * const argv[]) +{ + if (argc > 0) { + if (0 == strcmp(*argv, "rw")) + return EC_FLASH_REGION_RW; + else if (0 == strcmp(*argv, "ro")) + return EC_FLASH_REGION_RO; + + debug("%s: Invalid region '%s'\n", __func__, *argv); + } else { + debug("%s: Missing region parameter\n", __func__); + } + + return -1; +} + +int cros_ec_decode_ec_flash(const void *blob, struct fdt_cros_ec *config) +{ + int flash_node, node; + + node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_CROS_EC); + if (node < 0) { + debug("Failed to find chrome-ec node'\n"); + return -1; + } + + flash_node = fdt_subnode_offset(blob, node, "flash"); + if (flash_node < 0) { + debug("Failed to find flash node\n"); + return -1; + } + + if (fdtdec_read_fmap_entry(blob, flash_node, "flash", + &config->flash)) { + debug("Failed to decode flash node in chrome-ec'\n"); + return -1; + } + + config->flash_erase_value = fdtdec_get_int(blob, flash_node, + "erase-value", -1); + for (node = fdt_first_subnode(blob, flash_node); node >= 0; + node = fdt_next_subnode(blob, node)) { + const char *name = fdt_get_name(blob, node, NULL); + enum ec_flash_region region; + + if (0 == strcmp(name, "ro")) { + region = EC_FLASH_REGION_RO; + } else if (0 == strcmp(name, "rw")) { + region = EC_FLASH_REGION_RW; + } else if (0 == strcmp(name, "wp-ro")) { + region = EC_FLASH_REGION_WP_RO; + } else { + debug("Unknown EC flash region name '%s'\n", name); + return -1; + } + + if (fdtdec_read_fmap_entry(blob, node, "reg", + &config->region[region])) { + debug("Failed to decode flash region in chrome-ec'\n"); + return -1; + } + } + + return 0; +} + +int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr, + int alen, uchar *buffer, int len, int is_read) +{ + union { + struct ec_params_i2c_passthru p; + uint8_t outbuf[EC_PROTO2_MAX_PARAM_SIZE]; + } params; + union { + struct ec_response_i2c_passthru r; + uint8_t inbuf[EC_PROTO2_MAX_PARAM_SIZE]; + } response; + struct ec_params_i2c_passthru *p = ¶ms.p; + struct ec_response_i2c_passthru *r = &response.r; + struct ec_params_i2c_passthru_msg *msg = p->msg; + uint8_t *pdata; + int read_len, write_len; + int size; + int rv; + + p->port = 0; + + if (alen != 1) { + printf("Unsupported address length %d\n", alen); + return -1; + } + if (is_read) { + read_len = len; + write_len = alen; + p->num_msgs = 2; + } else { + read_len = 0; + write_len = alen + len; + p->num_msgs = 1; + } + + size = sizeof(*p) + p->num_msgs * sizeof(*msg); + if (size + write_len > sizeof(params)) { + puts("Params too large for buffer\n"); + return -1; + } + if (sizeof(*r) + read_len > sizeof(response)) { + puts("Read length too big for buffer\n"); + return -1; + } + + /* Create a message to write the register address and optional data */ + pdata = (uint8_t *)p + size; + msg->addr_flags = chip; + msg->len = write_len; + pdata[0] = addr; + if (!is_read) + memcpy(pdata + 1, buffer, len); + msg++; + + if (read_len) { + msg->addr_flags = chip | EC_I2C_FLAG_READ; + msg->len = read_len; + } + + rv = ec_command(dev, EC_CMD_I2C_PASSTHRU, 0, p, size + write_len, + r, sizeof(*r) + read_len); + if (rv < 0) + return rv; + + /* Parse response */ + if (r->i2c_status & EC_I2C_STATUS_ERROR) { + printf("Transfer failed with status=0x%x\n", r->i2c_status); + return -1; + } + + if (rv < sizeof(*r) + read_len) { + puts("Truncated read response\n"); + return -1; + } + + if (read_len) + memcpy(buffer, r->data, read_len); + + return 0; +} + +#ifdef CONFIG_CMD_CROS_EC + +/** + * Perform a flash read or write command + * + * @param dev CROS-EC device to read/write + * @param is_write 1 do to a write, 0 to do a read + * @param argc Number of arguments + * @param argv Arguments (2 is region, 3 is address) + * @return 0 for ok, 1 for a usage error or -ve for ec command error + * (negative EC_RES_...) + */ +static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc, + char * const argv[]) +{ + uint32_t offset, size = -1U, region_size; + unsigned long addr; + char *endp; + int region; + int ret; + + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == -1) + return 1; + if (argc < 4) + return 1; + addr = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + return 1; + if (argc > 4) { + size = simple_strtoul(argv[4], &endp, 16); + if (*argv[4] == 0 || *endp != 0) + return 1; + } + + ret = cros_ec_flash_offset(dev, region, &offset, ®ion_size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + return ret; + } + if (size == -1U) + size = region_size; + + ret = is_write ? + cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) : + cros_ec_flash_read(dev, (uint8_t *)addr, offset, size); + if (ret) { + debug("%s: Could not %s region\n", __func__, + is_write ? "write" : "read"); + return ret; + } + + return 0; +} + +/** + * get_alen() - Small parser helper function to get address length + * + * Returns the address length. + */ +static uint get_alen(char *arg) +{ + int j; + int alen; + + alen = 1; + for (j = 0; j < 8; j++) { + if (arg[j] == '.') { + alen = arg[j+1] - '0'; + break; + } else if (arg[j] == '\0') { + break; + } + } + return alen; +} + +#define DISP_LINE_LEN 16 + +/* + * TODO(sjg@chromium.org): This code copied almost verbatim from cmd_i2c.c + * so we can remove it later. + */ +static int cros_ec_i2c_md(struct cros_ec_dev *dev, int flag, int argc, + char * const argv[]) +{ + u_char chip; + uint addr, alen, length = 0x10; + int j, nbytes, linebytes; + + if (argc < 2) + return CMD_RET_USAGE; + + if (1 || (flag & CMD_FLAG_REPEAT) == 0) { + /* + * New command specified. + */ + + /* + * I2C chip address + */ + chip = simple_strtoul(argv[0], NULL, 16); + + /* + * I2C data address within the chip. This can be 1 or + * 2 bytes long. Some day it might be 3 bytes long :-). + */ + addr = simple_strtoul(argv[1], NULL, 16); + alen = get_alen(argv[1]); + if (alen > 3) + return CMD_RET_USAGE; + + /* + * If another parameter, it is the length to display. + * Length is the number of objects, not number of bytes. + */ + if (argc > 2) + length = simple_strtoul(argv[2], NULL, 16); + } + + /* + * Print the lines. + * + * We buffer all read data, so we can make sure data is read only + * once. + */ + nbytes = length; + do { + unsigned char linebuf[DISP_LINE_LEN]; + unsigned char *cp; + + linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; + + if (cros_ec_i2c_xfer(dev, chip, addr, alen, linebuf, linebytes, + 1)) + puts("Error reading the chip.\n"); + else { + printf("%04x:", addr); + cp = linebuf; + for (j = 0; j < linebytes; j++) { + printf(" %02x", *cp++); + addr++; + } + puts(" "); + cp = linebuf; + for (j = 0; j < linebytes; j++) { + if ((*cp < 0x20) || (*cp > 0x7e)) + puts("."); + else + printf("%c", *cp); + cp++; + } + putc('\n'); + } + nbytes -= linebytes; + } while (nbytes > 0); + + return 0; +} + +static int cros_ec_i2c_mw(struct cros_ec_dev *dev, int flag, int argc, + char * const argv[]) +{ + uchar chip; + ulong addr; + uint alen; + uchar byte; + int count; + + if ((argc < 3) || (argc > 4)) + return CMD_RET_USAGE; + + /* + * Chip is always specified. + */ + chip = simple_strtoul(argv[0], NULL, 16); + + /* + * Address is always specified. + */ + addr = simple_strtoul(argv[1], NULL, 16); + alen = get_alen(argv[1]); + if (alen > 3) + return CMD_RET_USAGE; + + /* + * Value to write is always specified. + */ + byte = simple_strtoul(argv[2], NULL, 16); + + /* + * Optional count + */ + if (argc == 4) + count = simple_strtoul(argv[3], NULL, 16); + else + count = 1; + + while (count-- > 0) { + if (cros_ec_i2c_xfer(dev, chip, addr++, alen, &byte, 1, 0)) + puts("Error writing the chip.\n"); + /* + * Wait for the write to complete. The write can take + * up to 10mSec (we allow a little more time). + */ +/* + * No write delay with FRAM devices. + */ +#if !defined(CONFIG_SYS_I2C_FRAM) + udelay(11000); +#endif + } + + return 0; +} + +/* Temporary code until we have driver model and can use the i2c command */ +static int cros_ec_i2c_passthrough(struct cros_ec_dev *dev, int flag, + int argc, char * const argv[]) +{ + const char *cmd; + + if (argc < 1) + return CMD_RET_USAGE; + cmd = *argv++; + argc--; + if (0 == strcmp("md", cmd)) + cros_ec_i2c_md(dev, flag, argc, argv); + else if (0 == strcmp("mw", cmd)) + cros_ec_i2c_mw(dev, flag, argc, argv); + else + return CMD_RET_USAGE; + + return 0; +} + +static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct cros_ec_dev *dev = last_dev; + const char *cmd; + int ret = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + cmd = argv[1]; + if (0 == strcmp("init", cmd)) { + ret = cros_ec_init(gd->fdt_blob, &dev); + if (ret) { + printf("Could not init cros_ec device (err %d)\n", ret); + return 1; + } + return 0; + } + + /* Just use the last allocated device; there should be only one */ + if (!last_dev) { + printf("No CROS-EC device available\n"); + return 1; + } + if (0 == strcmp("id", cmd)) { + char id[MSG_BYTES]; + + if (cros_ec_read_id(dev, id, sizeof(id))) { + debug("%s: Could not read KBC ID\n", __func__); + return 1; + } + printf("%s\n", id); + } else if (0 == strcmp("info", cmd)) { + struct ec_response_mkbp_info info; + + if (cros_ec_info(dev, &info)) { + debug("%s: Could not read KBC info\n", __func__); + return 1; + } + printf("rows = %u\n", info.rows); + printf("cols = %u\n", info.cols); + printf("switches = %#x\n", info.switches); + } else if (0 == strcmp("curimage", cmd)) { + enum ec_current_image image; + + if (cros_ec_read_current_image(dev, &image)) { + debug("%s: Could not read KBC image\n", __func__); + return 1; + } + printf("%d\n", image); + } else if (0 == strcmp("hash", cmd)) { + struct ec_response_vboot_hash hash; + int i; + + if (cros_ec_read_hash(dev, &hash)) { + debug("%s: Could not read KBC hash\n", __func__); + return 1; + } + + if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256) + printf("type: SHA-256\n"); + else + printf("type: %d\n", hash.hash_type); + + printf("offset: 0x%08x\n", hash.offset); + printf("size: 0x%08x\n", hash.size); + + printf("digest: "); + for (i = 0; i < hash.digest_size; i++) + printf("%02x", hash.hash_digest[i]); + printf("\n"); + } else if (0 == strcmp("reboot", cmd)) { + int region; + enum ec_reboot_cmd cmd; + + if (argc >= 3 && !strcmp(argv[2], "cold")) + cmd = EC_REBOOT_COLD; + else { + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == EC_FLASH_REGION_RO) + cmd = EC_REBOOT_JUMP_RO; + else if (region == EC_FLASH_REGION_RW) + cmd = EC_REBOOT_JUMP_RW; + else + return CMD_RET_USAGE; + } + + if (cros_ec_reboot(dev, cmd, 0)) { + debug("%s: Could not reboot KBC\n", __func__); + return 1; + } + } else if (0 == strcmp("events", cmd)) { + uint32_t events; + + if (cros_ec_get_host_events(dev, &events)) { + debug("%s: Could not read host events\n", __func__); + return 1; + } + printf("0x%08x\n", events); + } else if (0 == strcmp("clrevents", cmd)) { + uint32_t events = 0x7fffffff; + + if (argc >= 3) + events = simple_strtol(argv[2], NULL, 0); + + if (cros_ec_clear_host_events(dev, events)) { + debug("%s: Could not clear host events\n", __func__); + return 1; + } + } else if (0 == strcmp("read", cmd)) { + ret = do_read_write(dev, 0, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("write", cmd)) { + ret = do_read_write(dev, 1, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("erase", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + if (cros_ec_flash_offset(dev, region, &offset, &size)) { + debug("%s: Could not read region info\n", __func__); + ret = -1; + } else { + ret = cros_ec_flash_erase(dev, offset, size); + if (ret) { + debug("%s: Could not erase region\n", + __func__); + } + } + } else if (0 == strcmp("regioninfo", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + ret = cros_ec_flash_offset(dev, region, &offset, &size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + } else { + printf("Region: %s\n", region == EC_FLASH_REGION_RO ? + "RO" : "RW"); + printf("Offset: %x\n", offset); + printf("Size: %x\n", size); + } + } else if (0 == strcmp("vbnvcontext", cmd)) { + uint8_t block[EC_VBNV_BLOCK_SIZE]; + char buf[3]; + int i, len; + unsigned long result; + + if (argc <= 2) { + ret = cros_ec_read_vbnvcontext(dev, block); + if (!ret) { + printf("vbnv_block: "); + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) + printf("%02x", block[i]); + putc('\n'); + } + } else { + /* + * TODO(clchiou): Move this to a utility function as + * cmd_spi might want to call it. + */ + memset(block, 0, EC_VBNV_BLOCK_SIZE); + len = strlen(argv[2]); + buf[2] = '\0'; + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) { + if (i * 2 >= len) + break; + buf[0] = argv[2][i * 2]; + if (i * 2 + 1 >= len) + buf[1] = '0'; + else + buf[1] = argv[2][i * 2 + 1]; + strict_strtoul(buf, 16, &result); + block[i] = result; + } + ret = cros_ec_write_vbnvcontext(dev, block); + } + if (ret) { + debug("%s: Could not %s VbNvContext\n", __func__, + argc <= 2 ? "read" : "write"); + } + } else if (0 == strcmp("test", cmd)) { + int result = cros_ec_test(dev); + + if (result) + printf("Test failed with error %d\n", result); + else + puts("Test passed\n"); + } else if (0 == strcmp("version", cmd)) { + struct ec_response_get_version *p; + char *build_string; + + ret = cros_ec_read_version(dev, &p); + if (!ret) { + /* Print versions */ + printf("RO version: %1.*s\n", + (int)sizeof(p->version_string_ro), + p->version_string_ro); + printf("RW version: %1.*s\n", + (int)sizeof(p->version_string_rw), + p->version_string_rw); + printf("Firmware copy: %s\n", + (p->current_image < + ARRAY_SIZE(ec_current_image_name) ? + ec_current_image_name[p->current_image] : + "?")); + ret = cros_ec_read_build_info(dev, &build_string); + if (!ret) + printf("Build info: %s\n", build_string); + } + } else if (0 == strcmp("ldo", cmd)) { + uint8_t index, state; + char *endp; + + if (argc < 3) + return CMD_RET_USAGE; + index = simple_strtoul(argv[2], &endp, 10); + if (*argv[2] == 0 || *endp != 0) + return CMD_RET_USAGE; + if (argc > 3) { + state = simple_strtoul(argv[3], &endp, 10); + if (*argv[3] == 0 || *endp != 0) + return CMD_RET_USAGE; + ret = cros_ec_set_ldo(dev, index, state); + } else { + ret = cros_ec_get_ldo(dev, index, &state); + if (!ret) { + printf("LDO%d: %s\n", index, + state == EC_LDO_STATE_ON ? + "on" : "off"); + } + } + + if (ret) { + debug("%s: Could not access LDO%d\n", __func__, index); + return ret; + } + } else if (0 == strcmp("i2c", cmd)) { + ret = cros_ec_i2c_passthrough(dev, flag, argc - 2, argv + 2); + } else { + return CMD_RET_USAGE; + } + + if (ret < 0) { + printf("Error: CROS-EC command failed (error %d)\n", ret); + ret = 1; + } + + return ret; +} + +U_BOOT_CMD( + crosec, 6, 1, do_cros_ec, + "CROS-EC utility command", + "init Re-init CROS-EC (done on startup automatically)\n" + "crosec id Read CROS-EC ID\n" + "crosec info Read CROS-EC info\n" + "crosec curimage Read CROS-EC current image\n" + "crosec hash Read CROS-EC hash\n" + "crosec reboot [rw | ro | cold] Reboot CROS-EC\n" + "crosec events Read CROS-EC host events\n" + "crosec clrevents [mask] Clear CROS-EC host events\n" + "crosec regioninfo Read image info\n" + "crosec erase Erase EC image\n" + "crosec read [] Read EC image\n" + "crosec write [] Write EC image\n" + "crosec vbnvcontext [hexstring] Read [write] VbNvContext from EC\n" + "crosec ldo [] Switch/Read LDO state\n" + "crosec test run tests on cros_ec\n" + "crosec version Read CROS-EC version\n" + "crosec i2c md chip address[.0, .1, .2] [# of objects] - read from I2C passthru\n" + "crosec i2c mw chip address[.0, .1, .2] value [count] - write to I2C passthru (fill)" +); +#endif diff --git a/qemu/roms/u-boot/drivers/misc/cros_ec_i2c.c b/qemu/roms/u-boot/drivers/misc/cros_ec_i2c.c new file mode 100644 index 000000000..513cdb1cb --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/cros_ec_i2c.c @@ -0,0 +1,176 @@ +/* + * Chromium OS cros_ec driver - I2C interface + * + * Copyright (c) 2012 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * The Matrix Keyboard Protocol driver handles talking to the keyboard + * controller chip. Mostly this is for keyboard functions, but some other + * things have slipped in, so we provide generic services to talk to the + * KBC. + */ + +#include +#include +#include + +#ifdef DEBUG_TRACE +#define debug_trace(fmt, b...) debug(fmt, #b) +#else +#define debug_trace(fmt, b...) +#endif + +int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int old_bus = 0; + /* version8, cmd8, arglen8, out8[dout_len], csum8 */ + int out_bytes = dout_len + 4; + /* response8, arglen8, in8[din_len], checksum8 */ + int in_bytes = din_len + 3; + uint8_t *ptr; + /* Receive input data, so that args will be dword aligned */ + uint8_t *in_ptr; + int len, csum, ret; + + old_bus = i2c_get_bus_num(); + + /* + * Sanity-check I/O sizes given transaction overhead in internal + * buffers. + */ + if (out_bytes > sizeof(dev->dout)) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -1; + } + if (in_bytes > sizeof(dev->din)) { + debug("%s: Cannot receive %d bytes\n", __func__, din_len); + return -1; + } + assert(dout_len >= 0); + assert(dinp); + + /* + * Copy command and data into output buffer so we can do a single I2C + * burst transaction. + */ + ptr = dev->dout; + + /* + * in_ptr starts of pointing to a dword-aligned input data buffer. + * We decrement it back by the number of header bytes we expect to + * receive, so that the first parameter of the resulting input data + * will be dword aligned. + */ + in_ptr = dev->din + sizeof(int64_t); + + if (dev->protocol_version != 2) { + /* Something we don't support */ + debug("%s: Protocol version %d unsupported\n", + __func__, dev->protocol_version); + return -1; + } + + *ptr++ = EC_CMD_VERSION0 + cmd_version; + *ptr++ = cmd; + *ptr++ = dout_len; + in_ptr -= 2; /* Expect status, length bytes */ + + memcpy(ptr, dout, dout_len); + ptr += dout_len; + + *ptr++ = (uint8_t) + cros_ec_calc_checksum(dev->dout, dout_len + 3); + + /* Set to the proper i2c bus */ + if (i2c_set_bus_num(dev->bus_num)) { + debug("%s: Cannot change to I2C bus %d\n", __func__, + dev->bus_num); + return -1; + } + + /* Send output data */ + cros_ec_dump_data("out", -1, dev->dout, out_bytes); + ret = i2c_write(dev->addr, 0, 0, dev->dout, out_bytes); + if (ret) { + debug("%s: Cannot complete I2C write to 0x%x\n", + __func__, dev->addr); + ret = -1; + } + + if (!ret) { + ret = i2c_read(dev->addr, 0, 0, in_ptr, in_bytes); + if (ret) { + debug("%s: Cannot complete I2C read from 0x%x\n", + __func__, dev->addr); + ret = -1; + } + } + + /* Return to original bus number */ + i2c_set_bus_num(old_bus); + if (ret) + return ret; + + if (*in_ptr != EC_RES_SUCCESS) { + debug("%s: Received bad result code %d\n", __func__, *in_ptr); + return -(int)*in_ptr; + } + + len = in_ptr[1]; + if (len + 3 > sizeof(dev->din)) { + debug("%s: Received length %#02x too large\n", + __func__, len); + return -1; + } + csum = cros_ec_calc_checksum(in_ptr, 2 + len); + if (csum != in_ptr[2 + len]) { + debug("%s: Invalid checksum rx %#02x, calced %#02x\n", + __func__, in_ptr[2 + din_len], csum); + return -1; + } + din_len = min(din_len, len); + cros_ec_dump_data("in", -1, in_ptr, din_len + 3); + + /* Return pointer to dword-aligned input data, if any */ + *dinp = dev->din + sizeof(int64_t); + + return din_len; +} + +int cros_ec_i2c_decode_fdt(struct cros_ec_dev *dev, const void *blob) +{ + /* Decode interface-specific FDT params */ + dev->max_frequency = fdtdec_get_int(blob, dev->node, + "i2c-max-frequency", 100000); + dev->bus_num = i2c_get_bus_num_fdt(dev->parent_node); + if (dev->bus_num == -1) { + debug("%s: Failed to read bus number\n", __func__); + return -1; + } + dev->addr = fdtdec_get_int(blob, dev->node, "reg", -1); + if (dev->addr == -1) { + debug("%s: Failed to read device address\n", __func__); + return -1; + } + + return 0; +} + +/** + * Initialize I2C protocol. + * + * @param dev CROS_EC device + * @param blob Device tree blob + * @return 0 if ok, -1 on error + */ +int cros_ec_i2c_init(struct cros_ec_dev *dev, const void *blob) +{ + i2c_init(dev->max_frequency, dev->addr); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/misc/cros_ec_lpc.c b/qemu/roms/u-boot/drivers/misc/cros_ec_lpc.c new file mode 100644 index 000000000..0e02671c9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/cros_ec_lpc.c @@ -0,0 +1,194 @@ +/* + * Chromium OS cros_ec driver - LPC interface + * + * Copyright (c) 2012 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * The Matrix Keyboard Protocol driver handles talking to the keyboard + * controller chip. Mostly this is for keyboard functions, but some other + * things have slipped in, so we provide generic services to talk to the + * KBC. + */ + +#include +#include +#include +#include + +#ifdef DEBUG_TRACE +#define debug_trace(fmt, b...) debug(fmt, ##b) +#else +#define debug_trace(fmt, b...) +#endif + +static int wait_for_sync(struct cros_ec_dev *dev) +{ + unsigned long start; + + start = get_timer(0); + while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) { + if (get_timer(start) > 1000) { + debug("%s: Timeout waiting for CROS_EC sync\n", + __func__); + return -1; + } + } + + return 0; +} + +int cros_ec_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + const int cmd_addr = EC_LPC_ADDR_HOST_CMD; + const int data_addr = EC_LPC_ADDR_HOST_DATA; + const int args_addr = EC_LPC_ADDR_HOST_ARGS; + const int param_addr = EC_LPC_ADDR_HOST_PARAM; + + struct ec_lpc_host_args args; + uint8_t *d; + int csum; + int i; + + if (dout_len > EC_HOST_PARAM_SIZE) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -1; + } + + /* Fill in args */ + args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; + args.command_version = cmd_version; + args.data_size = dout_len; + + /* Calculate checksum */ + csum = cmd + args.flags + args.command_version + args.data_size; + for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) + csum += *d; + + args.checksum = (uint8_t)csum; + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting ready\n", __func__); + return -1; + } + + /* Write args */ + for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) + outb(*d, args_addr + i); + + /* Write data, if any */ + debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version); + for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) { + outb(*d, param_addr + i); + debug_trace("%02x ", *d); + } + + outb(cmd, cmd_addr); + debug_trace("\n"); + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting for response\n", __func__); + return -1; + } + + /* Check result */ + i = inb(data_addr); + if (i) { + debug("%s: CROS_EC result code %d\n", __func__, i); + return -i; + } + + /* Read back args */ + for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) + *d = inb(args_addr + i); + + /* + * If EC didn't modify args flags, then somehow we sent a new-style + * command to an old EC, which means it would have read its params + * from the wrong place. + */ + if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) { + debug("%s: CROS_EC protocol mismatch\n", __func__); + return -EC_RES_INVALID_RESPONSE; + } + + if (args.data_size > din_len) { + debug("%s: CROS_EC returned too much data %d > %d\n", + __func__, args.data_size, din_len); + return -EC_RES_INVALID_RESPONSE; + } + + /* Read data, if any */ + for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) { + *d = inb(param_addr + i); + debug_trace("%02x ", *d); + } + debug_trace("\n"); + + /* Verify checksum */ + csum = cmd + args.flags + args.command_version + args.data_size; + for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) + csum += *d; + + if (args.checksum != (uint8_t)csum) { + debug("%s: CROS_EC response has invalid checksum\n", __func__); + return -EC_RES_INVALID_CHECKSUM; + } + *dinp = dev->din; + + /* Return actual amount of data received */ + return args.data_size; +} + +/** + * Initialize LPC protocol. + * + * @param dev CROS_EC device + * @param blob Device tree blob + * @return 0 if ok, -1 on error + */ +int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob) +{ + int byte, i; + + /* See if we can find an EC at the other end */ + byte = 0xff; + byte &= inb(EC_LPC_ADDR_HOST_CMD); + byte &= inb(EC_LPC_ADDR_HOST_DATA); + for (i = 0; i < EC_HOST_PARAM_SIZE && (byte == 0xff); i++) + byte &= inb(EC_LPC_ADDR_HOST_PARAM + i); + if (byte == 0xff) { + debug("%s: CROS_EC device not found on LPC bus\n", + __func__); + return -1; + } + + return 0; +} + +/* + * Test if LPC command args are supported. + * + * The cheapest way to do this is by looking for the memory-mapped + * flag. This is faster than sending a new-style 'hello' command and + * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag + * in args when it responds. + */ +int cros_ec_lpc_check_version(struct cros_ec_dev *dev) +{ + if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' && + inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) + == 'C' && + (inb(EC_LPC_ADDR_MEMMAP + + EC_MEMMAP_HOST_CMD_FLAGS) & + EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) { + return 0; + } + + printf("%s: ERROR: old EC interface not supported\n", __func__); + return -1; +} diff --git a/qemu/roms/u-boot/drivers/misc/cros_ec_sandbox.c b/qemu/roms/u-boot/drivers/misc/cros_ec_sandbox.c new file mode 100644 index 000000000..4bb1d60e5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/cros_ec_sandbox.c @@ -0,0 +1,559 @@ +/* + * Chromium OS cros_ec driver - sandbox emulation + * + * Copyright (c) 2013 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Ultimately it shold be possible to connect an Chrome OS EC emulation + * to U-Boot and remove all of this code. But this provides a test + * environment for bringing up chromeos_sandbox and demonstrating its + * utility. + * + * This emulation includes the following: + * + * 1. Emulation of the keyboard, by converting keypresses received from SDL + * into key scan data, passed back from the EC as key scan messages. The + * key layout is read from the device tree. + * + * 2. Emulation of vboot context - so this can be read/written as required. + * + * 3. Save/restore of EC state, so that the vboot context, flash memory + * contents and current image can be preserved across boots. This is important + * since the EC is supposed to continue running even if the AP resets. + * + * 4. Some event support, in particular allowing Escape to be pressed on boot + * to enter recovery mode. The EC passes this to U-Boot through the normal + * event message. + * + * 5. Flash read/write/erase support, so that software sync works. The + * protect messages are supported but no protection is implemented. + * + * 6. Hashing of the EC image, again to support software sync. + * + * Other features can be added, although a better path is probably to link + * the EC image in with U-Boot (Vic has demonstrated a prototype for this). + */ + +DECLARE_GLOBAL_DATA_PTR; + +#define KEYBOARD_ROWS 8 +#define KEYBOARD_COLS 13 + +/* A single entry of the key matrix */ +struct ec_keymatrix_entry { + int row; /* key matrix row */ + int col; /* key matrix column */ + int keycode; /* corresponding linux key code */ +}; + +/** + * struct ec_state - Information about the EC state + * + * @vbnv_context: Vboot context data stored by EC + * @ec_config: FDT config information about the EC (e.g. flashmap) + * @flash_data: Contents of flash memory + * @flash_data_len: Size of flash memory + * @current_image: Current image the EC is running + * @matrix_count: Number of keys to decode in matrix + * @matrix: Information about keyboard matrix + * @keyscan: Current keyscan information (bit set for each row/column pressed) + * @recovery_req: Keyboard recovery requested + */ +struct ec_state { + uint8_t vbnv_context[EC_VBNV_BLOCK_SIZE]; + struct fdt_cros_ec ec_config; + uint8_t *flash_data; + int flash_data_len; + enum ec_current_image current_image; + int matrix_count; + struct ec_keymatrix_entry *matrix; /* the key matrix info */ + uint8_t keyscan[KEYBOARD_COLS]; + bool recovery_req; +} s_state, *state; + +/** + * cros_ec_read_state() - read the sandbox EC state from the state file + * + * If data is available, then blob and node will provide access to it. If + * not this function sets up an empty EC. + * + * @param blob: Pointer to device tree blob, or NULL if no data to read + * @param node: Node offset to read from + */ +static int cros_ec_read_state(const void *blob, int node) +{ + struct ec_state *ec = &s_state; + const char *prop; + int len; + + /* Set everything to defaults */ + ec->current_image = EC_IMAGE_RO; + if (!blob) + return 0; + + /* Read the data if available */ + ec->current_image = fdtdec_get_int(blob, node, "current-image", + EC_IMAGE_RO); + prop = fdt_getprop(blob, node, "vbnv-context", &len); + if (prop && len == sizeof(ec->vbnv_context)) + memcpy(ec->vbnv_context, prop, len); + + prop = fdt_getprop(blob, node, "flash-data", &len); + if (prop) { + ec->flash_data_len = len; + ec->flash_data = os_malloc(len); + if (!ec->flash_data) + return -ENOMEM; + memcpy(ec->flash_data, prop, len); + debug("%s: Loaded EC flash data size %#x\n", __func__, len); + } + + return 0; +} + +/** + * cros_ec_write_state() - Write out our state to the state file + * + * The caller will ensure that there is a node ready for the state. The node + * may already contain the old state, in which case it is overridden. + * + * @param blob: Device tree blob holding state + * @param node: Node to write our state into + */ +static int cros_ec_write_state(void *blob, int node) +{ + struct ec_state *ec = &s_state; + + /* We are guaranteed enough space to write basic properties */ + fdt_setprop_u32(blob, node, "current-image", ec->current_image); + fdt_setprop(blob, node, "vbnv-context", ec->vbnv_context, + sizeof(ec->vbnv_context)); + return state_setprop(node, "flash-data", ec->flash_data, + ec->ec_config.flash.length); +} + +SANDBOX_STATE_IO(cros_ec, "google,cros-ec", cros_ec_read_state, + cros_ec_write_state); + +/** + * Return the number of bytes used in the specified image. + * + * This is the actual size of code+data in the image, as opposed to the + * amount of space reserved in flash for that image. This code is similar to + * that used by the real EC code base. + * + * @param ec Current emulated EC state + * @param entry Flash map entry containing the image to check + * @return actual image size in bytes, 0 if the image contains no content or + * error. + */ +static int get_image_used(struct ec_state *ec, struct fmap_entry *entry) +{ + int size; + + /* + * Scan backwards looking for 0xea byte, which is by definition the + * last byte of the image. See ec.lds.S for how this is inserted at + * the end of the image. + */ + for (size = entry->length - 1; + size > 0 && ec->flash_data[entry->offset + size] != 0xea; + size--) + ; + + return size ? size + 1 : 0; /* 0xea byte IS part of the image */ +} + +/** + * Read the key matrix from the device tree + * + * Keymap entries in the fdt take the form of 0xRRCCKKKK where + * RR=Row CC=Column KKKK=Key Code + * + * @param ec Current emulated EC state + * @param blob Device tree blob containing keyscan information + * @param node Keyboard node of device tree containing keyscan information + * @return 0 if ok, -1 on error + */ +static int keyscan_read_fdt_matrix(struct ec_state *ec, const void *blob, + int node) +{ + const u32 *cell; + int upto; + int len; + + cell = fdt_getprop(blob, node, "linux,keymap", &len); + ec->matrix_count = len / 4; + ec->matrix = calloc(ec->matrix_count, sizeof(*ec->matrix)); + if (!ec->matrix) { + debug("%s: Out of memory for key matrix\n", __func__); + return -1; + } + + /* Now read the data */ + for (upto = 0; upto < ec->matrix_count; upto++) { + struct ec_keymatrix_entry *matrix = &ec->matrix[upto]; + u32 word; + + word = fdt32_to_cpu(*cell++); + matrix->row = word >> 24; + matrix->col = (word >> 16) & 0xff; + matrix->keycode = word & 0xffff; + + /* Hard-code some sanity limits for now */ + if (matrix->row >= KEYBOARD_ROWS || + matrix->col >= KEYBOARD_COLS) { + debug("%s: Matrix pos out of range (%d,%d)\n", + __func__, matrix->row, matrix->col); + return -1; + } + } + + if (upto != ec->matrix_count) { + debug("%s: Read mismatch from key matrix\n", __func__); + return -1; + } + + return 0; +} + +/** + * Return the next keyscan message contents + * + * @param ec Current emulated EC state + * @param scan Place to put keyscan bytes for the keyscan message (must hold + * enough space for a full keyscan) + * @return number of bytes of valid scan data + */ +static int cros_ec_keyscan(struct ec_state *ec, uint8_t *scan) +{ + const struct ec_keymatrix_entry *matrix; + int bytes = KEYBOARD_COLS; + int key[8]; /* allow up to 8 keys to be pressed at once */ + int count; + int i; + + memset(ec->keyscan, '\0', bytes); + count = sandbox_sdl_scan_keys(key, ARRAY_SIZE(key)); + + /* Look up keycode in matrix */ + for (i = 0, matrix = ec->matrix; i < ec->matrix_count; i++, matrix++) { + bool found; + int j; + + for (found = false, j = 0; j < count; j++) { + if (matrix->keycode == key[j]) + found = true; + } + + if (found) { + debug("%d: %d,%d\n", matrix->keycode, matrix->row, + matrix->col); + ec->keyscan[matrix->col] |= 1 << matrix->row; + } + } + + memcpy(scan, ec->keyscan, bytes); + return bytes; +} + +/** + * Process an emulated EC command + * + * @param ec Current emulated EC state + * @param req_hdr Pointer to request header + * @param req_data Pointer to body of request + * @param resp_hdr Pointer to place to put response header + * @param resp_data Pointer to place to put response data, if any + * @return length of response data, or 0 for no response data, or -1 on error + */ +static int process_cmd(struct ec_state *ec, + struct ec_host_request *req_hdr, const void *req_data, + struct ec_host_response *resp_hdr, void *resp_data) +{ + int len; + + /* TODO(sjg@chromium.org): Check checksums */ + debug("EC command %#0x\n", req_hdr->command); + + switch (req_hdr->command) { + case EC_CMD_HELLO: { + const struct ec_params_hello *req = req_data; + struct ec_response_hello *resp = resp_data; + + resp->out_data = req->in_data + 0x01020304; + len = sizeof(*resp); + break; + } + case EC_CMD_GET_VERSION: { + struct ec_response_get_version *resp = resp_data; + + strcpy(resp->version_string_ro, "sandbox_ro"); + strcpy(resp->version_string_rw, "sandbox_rw"); + resp->current_image = ec->current_image; + debug("Current image %d\n", resp->current_image); + len = sizeof(*resp); + break; + } + case EC_CMD_VBNV_CONTEXT: { + const struct ec_params_vbnvcontext *req = req_data; + struct ec_response_vbnvcontext *resp = resp_data; + + switch (req->op) { + case EC_VBNV_CONTEXT_OP_READ: + memcpy(resp->block, ec->vbnv_context, + sizeof(resp->block)); + len = sizeof(*resp); + break; + case EC_VBNV_CONTEXT_OP_WRITE: + memcpy(ec->vbnv_context, resp->block, + sizeof(resp->block)); + len = 0; + break; + default: + printf(" ** Unknown vbnv_context command %#02x\n", + req->op); + return -1; + } + break; + } + case EC_CMD_REBOOT_EC: { + const struct ec_params_reboot_ec *req = req_data; + + printf("Request reboot type %d\n", req->cmd); + switch (req->cmd) { + case EC_REBOOT_DISABLE_JUMP: + len = 0; + break; + case EC_REBOOT_JUMP_RW: + ec->current_image = EC_IMAGE_RW; + len = 0; + break; + default: + puts(" ** Unknown type"); + return -1; + } + break; + } + case EC_CMD_HOST_EVENT_GET_B: { + struct ec_response_host_event_mask *resp = resp_data; + + resp->mask = 0; + if (ec->recovery_req) { + resp->mask |= EC_HOST_EVENT_MASK( + EC_HOST_EVENT_KEYBOARD_RECOVERY); + } + + len = sizeof(*resp); + break; + } + case EC_CMD_VBOOT_HASH: { + const struct ec_params_vboot_hash *req = req_data; + struct ec_response_vboot_hash *resp = resp_data; + struct fmap_entry *entry; + int ret, size; + + entry = &state->ec_config.region[EC_FLASH_REGION_RW]; + + switch (req->cmd) { + case EC_VBOOT_HASH_RECALC: + case EC_VBOOT_HASH_GET: + size = SHA256_SUM_LEN; + len = get_image_used(ec, entry); + ret = hash_block("sha256", + ec->flash_data + entry->offset, + len, resp->hash_digest, &size); + if (ret) { + printf(" ** hash_block() failed\n"); + return -1; + } + resp->status = EC_VBOOT_HASH_STATUS_DONE; + resp->hash_type = EC_VBOOT_HASH_TYPE_SHA256; + resp->digest_size = size; + resp->reserved0 = 0; + resp->offset = entry->offset; + resp->size = len; + len = sizeof(*resp); + break; + default: + printf(" ** EC_CMD_VBOOT_HASH: Unknown command %d\n", + req->cmd); + return -1; + } + break; + } + case EC_CMD_FLASH_PROTECT: { + const struct ec_params_flash_protect *req = req_data; + struct ec_response_flash_protect *resp = resp_data; + uint32_t expect = EC_FLASH_PROTECT_ALL_NOW | + EC_FLASH_PROTECT_ALL_AT_BOOT; + + printf("mask=%#x, flags=%#x\n", req->mask, req->flags); + if (req->flags == expect || req->flags == 0) { + resp->flags = req->flags ? EC_FLASH_PROTECT_ALL_NOW : + 0; + resp->valid_flags = EC_FLASH_PROTECT_ALL_NOW; + resp->writable_flags = 0; + len = sizeof(*resp); + } else { + puts(" ** unexpected flash protect request\n"); + return -1; + } + break; + } + case EC_CMD_FLASH_REGION_INFO: { + const struct ec_params_flash_region_info *req = req_data; + struct ec_response_flash_region_info *resp = resp_data; + struct fmap_entry *entry; + + switch (req->region) { + case EC_FLASH_REGION_RO: + case EC_FLASH_REGION_RW: + case EC_FLASH_REGION_WP_RO: + entry = &state->ec_config.region[req->region]; + resp->offset = entry->offset; + resp->size = entry->length; + len = sizeof(*resp); + printf("EC flash region %d: offset=%#x, size=%#x\n", + req->region, resp->offset, resp->size); + break; + default: + printf("** Unknown flash region %d\n", req->region); + return -1; + } + break; + } + case EC_CMD_FLASH_ERASE: { + const struct ec_params_flash_erase *req = req_data; + + memset(ec->flash_data + req->offset, + ec->ec_config.flash_erase_value, + req->size); + len = 0; + break; + } + case EC_CMD_FLASH_WRITE: { + const struct ec_params_flash_write *req = req_data; + + memcpy(ec->flash_data + req->offset, req + 1, req->size); + len = 0; + break; + } + case EC_CMD_MKBP_STATE: + len = cros_ec_keyscan(ec, resp_data); + break; + default: + printf(" ** Unknown EC command %#02x\n", req_hdr->command); + return -1; + } + + return len; +} + +int cros_ec_sandbox_packet(struct cros_ec_dev *dev, int out_bytes, + int in_bytes) +{ + struct ec_host_request *req_hdr = (struct ec_host_request *)dev->dout; + const void *req_data = req_hdr + 1; + struct ec_host_response *resp_hdr = (struct ec_host_response *)dev->din; + void *resp_data = resp_hdr + 1; + int len; + + len = process_cmd(&s_state, req_hdr, req_data, resp_hdr, resp_data); + if (len < 0) + return len; + + resp_hdr->struct_version = 3; + resp_hdr->result = EC_RES_SUCCESS; + resp_hdr->data_len = len; + resp_hdr->reserved = 0; + len += sizeof(*resp_hdr); + resp_hdr->checksum = 0; + resp_hdr->checksum = (uint8_t) + -cros_ec_calc_checksum((const uint8_t *)resp_hdr, len); + + return in_bytes; +} + +int cros_ec_sandbox_decode_fdt(struct cros_ec_dev *dev, const void *blob) +{ + return 0; +} + +void cros_ec_check_keyboard(struct cros_ec_dev *dev) +{ + struct ec_state *ec = &s_state; + ulong start; + + printf("Press keys for EC to detect on reset (ESC=recovery)..."); + start = get_timer(0); + while (get_timer(start) < 1000) + ; + putc('\n'); + if (!sandbox_sdl_key_pressed(KEY_ESC)) { + ec->recovery_req = true; + printf(" - EC requests recovery\n"); + } +} + +/** + * Initialize sandbox EC emulation. + * + * @param dev CROS_EC device + * @param blob Device tree blob + * @return 0 if ok, -1 on error + */ +int cros_ec_sandbox_init(struct cros_ec_dev *dev, const void *blob) +{ + struct ec_state *ec = &s_state; + int node; + int err; + + state = &s_state; + err = cros_ec_decode_ec_flash(blob, &ec->ec_config); + if (err) + return err; + + node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_CROS_EC_KEYB); + if (node < 0) { + debug("%s: No cros_ec keyboard found\n", __func__); + } else if (keyscan_read_fdt_matrix(ec, blob, node)) { + debug("%s: Could not read key matrix\n", __func__); + return -1; + } + + /* If we loaded EC data, check that the length matches */ + if (ec->flash_data && + ec->flash_data_len != ec->ec_config.flash.length) { + printf("EC data length is %x, expected %x, discarding data\n", + ec->flash_data_len, ec->ec_config.flash.length); + os_free(ec->flash_data); + ec->flash_data = NULL; + } + + /* Otherwise allocate the memory */ + if (!ec->flash_data) { + ec->flash_data_len = ec->ec_config.flash.length; + ec->flash_data = os_malloc(ec->flash_data_len); + if (!ec->flash_data) + return -ENOMEM; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/misc/cros_ec_spi.c b/qemu/roms/u-boot/drivers/misc/cros_ec_spi.c new file mode 100644 index 000000000..7df709cc7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/cros_ec_spi.c @@ -0,0 +1,175 @@ +/* + * Chromium OS cros_ec driver - SPI interface + * + * Copyright (c) 2012 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * The Matrix Keyboard Protocol driver handles talking to the keyboard + * controller chip. Mostly this is for keyboard functions, but some other + * things have slipped in, so we provide generic services to talk to the + * KBC. + */ + +#include +#include +#include + +int cros_ec_spi_packet(struct cros_ec_dev *dev, int out_bytes, int in_bytes) +{ + int rv; + + /* Do the transfer */ + if (spi_claim_bus(dev->spi)) { + debug("%s: Cannot claim SPI bus\n", __func__); + return -1; + } + + rv = spi_xfer(dev->spi, max(out_bytes, in_bytes) * 8, + dev->dout, dev->din, + SPI_XFER_BEGIN | SPI_XFER_END); + + spi_release_bus(dev->spi); + + if (rv) { + debug("%s: Cannot complete SPI transfer\n", __func__); + return -1; + } + + return in_bytes; +} + +/** + * Send a command to a LPC CROS_EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS_EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Returns pointer to response data. This will be + * untouched unless we return a value > 0. + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int in_bytes = din_len + 4; /* status, length, checksum, trailer */ + uint8_t *out; + uint8_t *p; + int csum, len; + int rv; + + if (dev->protocol_version != 2) { + debug("%s: Unsupported EC protcol version %d\n", + __func__, dev->protocol_version); + return -1; + } + + /* + * Sanity-check input size to make sure it plus transaction overhead + * fits in the internal device buffer. + */ + if (in_bytes > sizeof(dev->din)) { + debug("%s: Cannot receive %d bytes\n", __func__, din_len); + return -1; + } + + /* We represent message length as a byte */ + if (dout_len > 0xff) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -1; + } + + /* + * Clear input buffer so we don't get false hits for MSG_HEADER + */ + memset(dev->din, '\0', in_bytes); + + if (spi_claim_bus(dev->spi)) { + debug("%s: Cannot claim SPI bus\n", __func__); + return -1; + } + + out = dev->dout; + out[0] = cmd_version; + out[1] = cmd; + out[2] = (uint8_t)dout_len; + memcpy(out + 3, dout, dout_len); + csum = cros_ec_calc_checksum(out, 3) + + cros_ec_calc_checksum(dout, dout_len); + out[3 + dout_len] = (uint8_t)csum; + + /* + * Send output data and receive input data starting such that the + * message body will be dword aligned. + */ + p = dev->din + sizeof(int64_t) - 2; + len = dout_len + 4; + cros_ec_dump_data("out", cmd, out, len); + rv = spi_xfer(dev->spi, max(len, in_bytes) * 8, out, p, + SPI_XFER_BEGIN | SPI_XFER_END); + + spi_release_bus(dev->spi); + + if (rv) { + debug("%s: Cannot complete SPI transfer\n", __func__); + return -1; + } + + len = min(p[1], din_len); + cros_ec_dump_data("in", -1, p, len + 3); + + /* Response code is first byte of message */ + if (p[0] != EC_RES_SUCCESS) { + printf("%s: Returned status %d\n", __func__, p[0]); + return -(int)(p[0]); + } + + /* Check checksum */ + csum = cros_ec_calc_checksum(p, len + 2); + if (csum != p[len + 2]) { + debug("%s: Invalid checksum rx %#02x, calced %#02x\n", __func__, + p[2 + len], csum); + return -1; + } + + /* Anything else is the response data */ + *dinp = p + 2; + + return len; +} + +int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob) +{ + /* Decode interface-specific FDT params */ + dev->max_frequency = fdtdec_get_int(blob, dev->node, + "spi-max-frequency", 500000); + dev->cs = fdtdec_get_int(blob, dev->node, "reg", 0); + + return 0; +} + +/** + * Initialize SPI protocol. + * + * @param dev CROS_EC device + * @param blob Device tree blob + * @return 0 if ok, -1 on error + */ +int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob) +{ + dev->spi = spi_setup_slave_fdt(blob, dev->parent_node, dev->node); + if (!dev->spi) { + debug("%s: Could not setup SPI slave\n", __func__); + return -1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/misc/ds4510.c b/qemu/roms/u-boot/drivers/misc/ds4510.c new file mode 100644 index 000000000..aa893c35f --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/ds4510.c @@ -0,0 +1,423 @@ +/* + * Copyright 2008 Extreme Engineering Solutions, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * Driver for DS4510, a CPU supervisor with integrated EEPROM, SRAM, + * and 4 programmable non-volatile GPIO pins. + */ + +#include +#include +#include +#include + +/* Default to an address that hopefully won't corrupt other i2c devices */ +#ifndef CONFIG_SYS_I2C_DS4510_ADDR +#define CONFIG_SYS_I2C_DS4510_ADDR (~0) +#endif + +enum { + DS4510_CMD_INFO, + DS4510_CMD_DEVICE, + DS4510_CMD_NV, + DS4510_CMD_RSTDELAY, + DS4510_CMD_OUTPUT, + DS4510_CMD_INPUT, + DS4510_CMD_PULLUP, + DS4510_CMD_EEPROM, + DS4510_CMD_SEEPROM, + DS4510_CMD_SRAM, +}; + +/* + * Write to DS4510, taking page boundaries into account + */ +int ds4510_mem_write(uint8_t chip, int offset, uint8_t *buf, int count) +{ + int wrlen; + int i = 0; + + do { + wrlen = DS4510_EEPROM_PAGE_SIZE - + DS4510_EEPROM_PAGE_OFFSET(offset); + if (count < wrlen) + wrlen = count; + if (i2c_write(chip, offset, 1, &buf[i], wrlen)) + return -1; + + /* + * This delay isn't needed for SRAM writes but shouldn't delay + * things too much, so do it unconditionally for simplicity + */ + udelay(DS4510_EEPROM_PAGE_WRITE_DELAY_MS * 1000); + count -= wrlen; + offset += wrlen; + i += wrlen; + } while (count > 0); + + return 0; +} + +/* + * General read from DS4510 + */ +int ds4510_mem_read(uint8_t chip, int offset, uint8_t *buf, int count) +{ + return i2c_read(chip, offset, 1, buf, count); +} + +/* + * Write SEE bit in config register. + * nv = 0 - Writes to SEEPROM registers behave like EEPROM + * nv = 1 - Writes to SEEPROM registers behave like SRAM + */ +int ds4510_see_write(uint8_t chip, uint8_t nv) +{ + uint8_t data; + + if (i2c_read(chip, DS4510_CFG, 1, &data, 1)) + return -1; + + if (nv) /* Treat SEEPROM bits as EEPROM */ + data &= ~DS4510_CFG_SEE; + else /* Treat SEEPROM bits as SRAM */ + data |= DS4510_CFG_SEE; + + return ds4510_mem_write(chip, DS4510_CFG, &data, 1); +} + +/* + * Write de-assertion of reset signal delay + */ +int ds4510_rstdelay_write(uint8_t chip, uint8_t delay) +{ + uint8_t data; + + if (i2c_read(chip, DS4510_RSTDELAY, 1, &data, 1)) + return -1; + + data &= ~DS4510_RSTDELAY_MASK; + data |= delay & DS4510_RSTDELAY_MASK; + + return ds4510_mem_write(chip, DS4510_RSTDELAY, &data, 1); +} + +/* + * Write pullup characteristics of IO pins + */ +int ds4510_pullup_write(uint8_t chip, uint8_t val) +{ + val &= DS4510_IO_MASK; + + return ds4510_mem_write(chip, DS4510_PULLUP, (uint8_t *)&val, 1); +} + +/* + * Read pullup characteristics of IO pins + */ +int ds4510_pullup_read(uint8_t chip) +{ + uint8_t val; + + if (i2c_read(chip, DS4510_PULLUP, 1, &val, 1)) + return -1; + + return val & DS4510_IO_MASK; +} + +/* + * Write drive level of IO pins + */ +int ds4510_gpio_write(uint8_t chip, uint8_t val) +{ + uint8_t data; + int i; + + for (i = 0; i < DS4510_NUM_IO; i++) { + if (i2c_read(chip, DS4510_IO0 - i, 1, &data, 1)) + return -1; + + if (val & (0x1 << i)) + data |= 0x1; + else + data &= ~0x1; + + if (ds4510_mem_write(chip, DS4510_IO0 - i, &data, 1)) + return -1; + } + + return 0; +} + +/* + * Read drive level of IO pins + */ +int ds4510_gpio_read(uint8_t chip) +{ + uint8_t data; + int val = 0; + int i; + + for (i = 0; i < DS4510_NUM_IO; i++) { + if (i2c_read(chip, DS4510_IO0 - i, 1, &data, 1)) + return -1; + + if (data & 1) + val |= (1 << i); + } + + return val; +} + +/* + * Read physical level of IO pins + */ +int ds4510_gpio_read_val(uint8_t chip) +{ + uint8_t val; + + if (i2c_read(chip, DS4510_IO_STATUS, 1, &val, 1)) + return -1; + + return val & DS4510_IO_MASK; +} + +#ifdef CONFIG_CMD_DS4510 +#ifdef CONFIG_CMD_DS4510_INFO +/* + * Display DS4510 information + */ +static int ds4510_info(uint8_t chip) +{ + int i; + int tmp; + uint8_t data; + + printf("DS4510 @ 0x%x:\n\n", chip); + + if (i2c_read(chip, DS4510_RSTDELAY, 1, &data, 1)) + return -1; + printf("rstdelay = 0x%x\n\n", data & DS4510_RSTDELAY_MASK); + + if (i2c_read(chip, DS4510_CFG, 1, &data, 1)) + return -1; + printf("config = 0x%x\n", data); + printf(" /ready = %d\n", data & DS4510_CFG_READY ? 1 : 0); + printf(" trip pt = %d\n", data & DS4510_CFG_TRIP_POINT ? 1 : 0); + printf(" rst sts = %d\n", data & DS4510_CFG_RESET ? 1 : 0); + printf(" /see = %d\n", data & DS4510_CFG_SEE ? 1 : 0); + printf(" swrst = %d\n\n", data & DS4510_CFG_SWRST ? 1 : 0); + + printf("gpio pins: 3210\n"); + printf("---------------\n"); + printf("pullup "); + + tmp = ds4510_pullup_read(chip); + if (tmp == -1) + return tmp; + for (i = DS4510_NUM_IO - 1; i >= 0; i--) + printf("%d", (tmp & (1 << i)) ? 1 : 0); + printf("\n"); + + printf("driven "); + tmp = ds4510_gpio_read(chip); + if (tmp == -1) + return -1; + for (i = DS4510_NUM_IO - 1; i >= 0; i--) + printf("%d", (tmp & (1 << i)) ? 1 : 0); + printf("\n"); + + printf("read "); + tmp = ds4510_gpio_read_val(chip); + if (tmp == -1) + return -1; + for (i = DS4510_NUM_IO - 1; i >= 0; i--) + printf("%d", (tmp & (1 << i)) ? 1 : 0); + printf("\n"); + + return 0; +} +#endif /* CONFIG_CMD_DS4510_INFO */ + +cmd_tbl_t cmd_ds4510[] = { + U_BOOT_CMD_MKENT(device, 3, 0, (void *)DS4510_CMD_DEVICE, "", ""), + U_BOOT_CMD_MKENT(nv, 3, 0, (void *)DS4510_CMD_NV, "", ""), + U_BOOT_CMD_MKENT(output, 4, 0, (void *)DS4510_CMD_OUTPUT, "", ""), + U_BOOT_CMD_MKENT(input, 3, 0, (void *)DS4510_CMD_INPUT, "", ""), + U_BOOT_CMD_MKENT(pullup, 4, 0, (void *)DS4510_CMD_PULLUP, "", ""), +#ifdef CONFIG_CMD_DS4510_INFO + U_BOOT_CMD_MKENT(info, 2, 0, (void *)DS4510_CMD_INFO, "", ""), +#endif +#ifdef CONFIG_CMD_DS4510_RST + U_BOOT_CMD_MKENT(rstdelay, 3, 0, (void *)DS4510_CMD_RSTDELAY, "", ""), +#endif +#ifdef CONFIG_CMD_DS4510_MEM + U_BOOT_CMD_MKENT(eeprom, 6, 0, (void *)DS4510_CMD_EEPROM, "", ""), + U_BOOT_CMD_MKENT(seeprom, 6, 0, (void *)DS4510_CMD_SEEPROM, "", ""), + U_BOOT_CMD_MKENT(sram, 6, 0, (void *)DS4510_CMD_SRAM, "", ""), +#endif +}; + +int do_ds4510(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + static uint8_t chip = CONFIG_SYS_I2C_DS4510_ADDR; + cmd_tbl_t *c; + ulong ul_arg2 = 0; + ulong ul_arg3 = 0; + int tmp; +#ifdef CONFIG_CMD_DS4510_MEM + ulong addr; + ulong off; + ulong cnt; + int end; + int (*rw_func)(uint8_t, int, uint8_t *, int); +#endif + + c = find_cmd_tbl(argv[1], cmd_ds4510, ARRAY_SIZE(cmd_ds4510)); + + /* All commands but "device" require 'maxargs' arguments */ + if (!c || !((argc == (c->maxargs)) || + (((int)c->cmd == DS4510_CMD_DEVICE) && + (argc == (c->maxargs - 1))))) { + return cmd_usage(cmdtp); + } + + /* arg2 used as chip addr and pin number */ + if (argc > 2) + ul_arg2 = simple_strtoul(argv[2], NULL, 16); + + /* arg3 used as output/pullup value */ + if (argc > 3) + ul_arg3 = simple_strtoul(argv[3], NULL, 16); + + switch ((int)c->cmd) { + case DS4510_CMD_DEVICE: + if (argc == 3) + chip = ul_arg2; + printf("Current device address: 0x%x\n", chip); + return 0; + case DS4510_CMD_NV: + return ds4510_see_write(chip, ul_arg2); + case DS4510_CMD_OUTPUT: + tmp = ds4510_gpio_read(chip); + if (tmp == -1) + return -1; + if (ul_arg3) + tmp |= (1 << ul_arg2); + else + tmp &= ~(1 << ul_arg2); + return ds4510_gpio_write(chip, tmp); + case DS4510_CMD_INPUT: + tmp = ds4510_gpio_read_val(chip); + if (tmp == -1) + return -1; + return (tmp & (1 << ul_arg2)) != 0; + case DS4510_CMD_PULLUP: + tmp = ds4510_pullup_read(chip); + if (tmp == -1) + return -1; + if (ul_arg3) + tmp |= (1 << ul_arg2); + else + tmp &= ~(1 << ul_arg2); + return ds4510_pullup_write(chip, tmp); +#ifdef CONFIG_CMD_DS4510_INFO + case DS4510_CMD_INFO: + return ds4510_info(chip); +#endif +#ifdef CONFIG_CMD_DS4510_RST + case DS4510_CMD_RSTDELAY: + return ds4510_rstdelay_write(chip, ul_arg2); +#endif +#ifdef CONFIG_CMD_DS4510_MEM + case DS4510_CMD_EEPROM: + end = DS4510_EEPROM + DS4510_EEPROM_SIZE; + off = DS4510_EEPROM; + break; + case DS4510_CMD_SEEPROM: + end = DS4510_SEEPROM + DS4510_SEEPROM_SIZE; + off = DS4510_SEEPROM; + break; + case DS4510_CMD_SRAM: + end = DS4510_SRAM + DS4510_SRAM_SIZE; + off = DS4510_SRAM; + break; +#endif + default: + /* We should never get here... */ + return 1; + } + +#ifdef CONFIG_CMD_DS4510_MEM + /* Only eeprom, seeprom, and sram commands should make it here */ + if (strcmp(argv[2], "read") == 0) + rw_func = ds4510_mem_read; + else if (strcmp(argv[2], "write") == 0) + rw_func = ds4510_mem_write; + else + return cmd_usage(cmdtp); + + addr = simple_strtoul(argv[3], NULL, 16); + off += simple_strtoul(argv[4], NULL, 16); + cnt = simple_strtoul(argv[5], NULL, 16); + + if ((off + cnt) > end) { + printf("ERROR: invalid len\n"); + return -1; + } + + return rw_func(chip, off, (uint8_t *)addr, cnt); +#endif +} + +U_BOOT_CMD( + ds4510, 6, 1, do_ds4510, + "ds4510 eeprom/seeprom/sram/gpio access", + "device [dev]\n" + " - show or set current device address\n" +#ifdef CONFIG_CMD_DS4510_INFO + "ds4510 info\n" + " - display ds4510 info\n" +#endif + "ds4510 output pin 0|1\n" + " - set pin low or high-Z\n" + "ds4510 input pin\n" + " - read value of pin\n" + "ds4510 pullup pin 0|1\n" + " - disable/enable pullup on specified pin\n" + "ds4510 nv 0|1\n" + " - make gpio and seeprom writes volatile/non-volatile" +#ifdef CONFIG_CMD_DS4510_RST + "\n" + "ds4510 rstdelay 0-3\n" + " - set reset output delay" +#endif +#ifdef CONFIG_CMD_DS4510_MEM + "\n" + "ds4510 eeprom read addr off cnt\n" + "ds4510 eeprom write addr off cnt\n" + " - read/write 'cnt' bytes at EEPROM offset 'off'\n" + "ds4510 seeprom read addr off cnt\n" + "ds4510 seeprom write addr off cnt\n" + " - read/write 'cnt' bytes at SRAM-shadowed EEPROM offset 'off'\n" + "ds4510 sram read addr off cnt\n" + "ds4510 sram write addr off cnt\n" + " - read/write 'cnt' bytes at SRAM offset 'off'" +#endif +); +#endif /* CONFIG_CMD_DS4510 */ diff --git a/qemu/roms/u-boot/drivers/misc/fsl_ifc.c b/qemu/roms/u-boot/drivers/misc/fsl_ifc.c new file mode 100644 index 000000000..3902e9ff5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/fsl_ifc.c @@ -0,0 +1,171 @@ +/* + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * Author: Dipen Dudhat + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +void print_ifc_regs(void) +{ + int i, j; + + printf("IFC Controller Registers\n"); + for (i = 0; i < CONFIG_SYS_FSL_IFC_BANK_COUNT; i++) { + printf("CSPR%d:0x%08X\tAMASK%d:0x%08X\tCSOR%d:0x%08X\n", + i, get_ifc_cspr(i), i, get_ifc_amask(i), + i, get_ifc_csor(i)); + for (j = 0; j < 4; j++) + printf("IFC_FTIM%d:0x%08X\n", j, get_ifc_ftim(i, j)); + } +} + +void init_early_memctl_regs(void) +{ +#if defined(CONFIG_SYS_CSPR0) && defined(CONFIG_SYS_CSOR0) + set_ifc_ftim(IFC_CS0, IFC_FTIM0, CONFIG_SYS_CS0_FTIM0); + set_ifc_ftim(IFC_CS0, IFC_FTIM1, CONFIG_SYS_CS0_FTIM1); + set_ifc_ftim(IFC_CS0, IFC_FTIM2, CONFIG_SYS_CS0_FTIM2); + set_ifc_ftim(IFC_CS0, IFC_FTIM3, CONFIG_SYS_CS0_FTIM3); + +#ifndef CONFIG_A003399_NOR_WORKAROUND +#ifdef CONFIG_SYS_CSPR0_EXT + set_ifc_cspr_ext(IFC_CS0, CONFIG_SYS_CSPR0_EXT); +#endif +#ifdef CONFIG_SYS_CSOR0_EXT + set_ifc_csor_ext(IFC_CS0, CONFIG_SYS_CSOR0_EXT); +#endif + set_ifc_cspr(IFC_CS0, CONFIG_SYS_CSPR0); + set_ifc_amask(IFC_CS0, CONFIG_SYS_AMASK0); + set_ifc_csor(IFC_CS0, CONFIG_SYS_CSOR0); +#endif +#endif + +#ifdef CONFIG_SYS_CSPR1_EXT + set_ifc_cspr_ext(IFC_CS1, CONFIG_SYS_CSPR1_EXT); +#endif +#ifdef CONFIG_SYS_CSOR1_EXT + set_ifc_csor_ext(IFC_CS1, CONFIG_SYS_CSOR1_EXT); +#endif +#if defined(CONFIG_SYS_CSPR1) && defined(CONFIG_SYS_CSOR1) + set_ifc_ftim(IFC_CS1, IFC_FTIM0, CONFIG_SYS_CS1_FTIM0); + set_ifc_ftim(IFC_CS1, IFC_FTIM1, CONFIG_SYS_CS1_FTIM1); + set_ifc_ftim(IFC_CS1, IFC_FTIM2, CONFIG_SYS_CS1_FTIM2); + set_ifc_ftim(IFC_CS1, IFC_FTIM3, CONFIG_SYS_CS1_FTIM3); + + set_ifc_csor(IFC_CS1, CONFIG_SYS_CSOR1); + set_ifc_amask(IFC_CS1, CONFIG_SYS_AMASK1); + set_ifc_cspr(IFC_CS1, CONFIG_SYS_CSPR1); +#endif + +#ifdef CONFIG_SYS_CSPR2_EXT + set_ifc_cspr_ext(IFC_CS2, CONFIG_SYS_CSPR2_EXT); +#endif +#ifdef CONFIG_SYS_CSOR2_EXT + set_ifc_csor_ext(IFC_CS2, CONFIG_SYS_CSOR2_EXT); +#endif +#if defined(CONFIG_SYS_CSPR2) && defined(CONFIG_SYS_CSOR2) + set_ifc_ftim(IFC_CS2, IFC_FTIM0, CONFIG_SYS_CS2_FTIM0); + set_ifc_ftim(IFC_CS2, IFC_FTIM1, CONFIG_SYS_CS2_FTIM1); + set_ifc_ftim(IFC_CS2, IFC_FTIM2, CONFIG_SYS_CS2_FTIM2); + set_ifc_ftim(IFC_CS2, IFC_FTIM3, CONFIG_SYS_CS2_FTIM3); + + set_ifc_csor(IFC_CS2, CONFIG_SYS_CSOR2); + set_ifc_amask(IFC_CS2, CONFIG_SYS_AMASK2); + set_ifc_cspr(IFC_CS2, CONFIG_SYS_CSPR2); +#endif + +#ifdef CONFIG_SYS_CSPR3_EXT + set_ifc_cspr_ext(IFC_CS3, CONFIG_SYS_CSPR3_EXT); +#endif +#ifdef CONFIG_SYS_CSOR3_EXT + set_ifc_csor_ext(IFC_CS3, CONFIG_SYS_CSOR3_EXT); +#endif +#if defined(CONFIG_SYS_CSPR3) && defined(CONFIG_SYS_CSOR3) + set_ifc_ftim(IFC_CS3, IFC_FTIM0, CONFIG_SYS_CS3_FTIM0); + set_ifc_ftim(IFC_CS3, IFC_FTIM1, CONFIG_SYS_CS3_FTIM1); + set_ifc_ftim(IFC_CS3, IFC_FTIM2, CONFIG_SYS_CS3_FTIM2); + set_ifc_ftim(IFC_CS3, IFC_FTIM3, CONFIG_SYS_CS3_FTIM3); + + set_ifc_cspr(IFC_CS3, CONFIG_SYS_CSPR3); + set_ifc_amask(IFC_CS3, CONFIG_SYS_AMASK3); + set_ifc_csor(IFC_CS3, CONFIG_SYS_CSOR3); +#endif + +#ifdef CONFIG_SYS_CSPR4_EXT + set_ifc_cspr_ext(IFC_CS4, CONFIG_SYS_CSPR4_EXT); +#endif +#ifdef CONFIG_SYS_CSOR4_EXT + set_ifc_csor_ext(IFC_CS4, CONFIG_SYS_CSOR4_EXT); +#endif +#if defined(CONFIG_SYS_CSPR4) && defined(CONFIG_SYS_CSOR4) + set_ifc_ftim(IFC_CS4, IFC_FTIM0, CONFIG_SYS_CS4_FTIM0); + set_ifc_ftim(IFC_CS4, IFC_FTIM1, CONFIG_SYS_CS4_FTIM1); + set_ifc_ftim(IFC_CS4, IFC_FTIM2, CONFIG_SYS_CS4_FTIM2); + set_ifc_ftim(IFC_CS4, IFC_FTIM3, CONFIG_SYS_CS4_FTIM3); + + set_ifc_cspr(IFC_CS4, CONFIG_SYS_CSPR4); + set_ifc_amask(IFC_CS4, CONFIG_SYS_AMASK4); + set_ifc_csor(IFC_CS4, CONFIG_SYS_CSOR4); +#endif + +#ifdef CONFIG_SYS_CSPR5_EXT + set_ifc_cspr_ext(IFC_CS5, CONFIG_SYS_CSPR5_EXT); +#endif +#ifdef CONFIG_SYS_CSOR5_EXT + set_ifc_csor_ext(IFC_CS5, CONFIG_SYS_CSOR5_EXT); +#endif +#if defined(CONFIG_SYS_CSPR5) && defined(CONFIG_SYS_CSOR5) + set_ifc_ftim(IFC_CS5, IFC_FTIM0, CONFIG_SYS_CS5_FTIM0); + set_ifc_ftim(IFC_CS5, IFC_FTIM1, CONFIG_SYS_CS5_FTIM1); + set_ifc_ftim(IFC_CS5, IFC_FTIM2, CONFIG_SYS_CS5_FTIM2); + set_ifc_ftim(IFC_CS5, IFC_FTIM3, CONFIG_SYS_CS5_FTIM3); + + set_ifc_cspr(IFC_CS5, CONFIG_SYS_CSPR5); + set_ifc_amask(IFC_CS5, CONFIG_SYS_AMASK5); + set_ifc_csor(IFC_CS5, CONFIG_SYS_CSOR5); +#endif + +#ifdef CONFIG_SYS_CSPR6_EXT + set_ifc_cspr_ext(IFC_CS6, CONFIG_SYS_CSPR6_EXT); +#endif +#ifdef CONFIG_SYS_CSOR6_EXT + set_ifc_csor_ext(IFC_CS6, CONFIG_SYS_CSOR6_EXT); +#endif +#if defined(CONFIG_SYS_CSPR6) && defined(CONFIG_SYS_CSOR6) + set_ifc_ftim(IFC_CS6, IFC_FTIM0, CONFIG_SYS_CS6_FTIM0); + set_ifc_ftim(IFC_CS6, IFC_FTIM1, CONFIG_SYS_CS6_FTIM1); + set_ifc_ftim(IFC_CS6, IFC_FTIM2, CONFIG_SYS_CS6_FTIM2); + set_ifc_ftim(IFC_CS6, IFC_FTIM3, CONFIG_SYS_CS6_FTIM3); + + set_ifc_cspr(IFC_CS6, CONFIG_SYS_CSPR6); + set_ifc_amask(IFC_CS6, CONFIG_SYS_AMASK6); + set_ifc_csor(IFC_CS6, CONFIG_SYS_CSOR6); +#endif + +#ifdef CONFIG_SYS_CSPR7_EXT + set_ifc_cspr_ext(IFC_CS7, CONFIG_SYS_CSPR7_EXT); +#endif +#ifdef CONFIG_SYS_CSOR7_EXT + set_ifc_csor_ext(IFC_CS7, CONFIG_SYS_CSOR7_EXT); +#endif +#if defined(CONFIG_SYS_CSPR7) && defined(CONFIG_SYS_CSOR7) + set_ifc_ftim(IFC_CS7, IFC_FTIM0, CONFIG_SYS_CS7_FTIM0); + set_ifc_ftim(IFC_CS7, IFC_FTIM1, CONFIG_SYS_CS7_FTIM1); + set_ifc_ftim(IFC_CS7, IFC_FTIM2, CONFIG_SYS_CS7_FTIM2); + set_ifc_ftim(IFC_CS7, IFC_FTIM3, CONFIG_SYS_CS7_FTIM3); + + set_ifc_cspr(IFC_CS7, CONFIG_SYS_CSPR7); + set_ifc_amask(IFC_CS7, CONFIG_SYS_AMASK7); + set_ifc_csor(IFC_CS7, CONFIG_SYS_CSOR7); +#endif +} + +void init_final_memctl_regs(void) +{ +#ifdef CONFIG_SYS_CSPR0_FINAL + set_ifc_cspr(IFC_CS0, CONFIG_SYS_CSPR0_FINAL); +#endif +} diff --git a/qemu/roms/u-boot/drivers/misc/fsl_iim.c b/qemu/roms/u-boot/drivers/misc/fsl_iim.c new file mode 100644 index 000000000..36433a74f --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/fsl_iim.c @@ -0,0 +1,281 @@ +/* + * (C) Copyright 2009-2013 ADVANSEE + * Benoît Thébaudeau + * + * Based on the mpc512x iim code: + * Copyright 2008 Silicon Turnkey Express, Inc. + * Martha Marx + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#ifndef CONFIG_MPC512X +#include +#endif +#if defined(CONFIG_MX51) || defined(CONFIG_MX53) +#include +#endif + +/* FSL IIM-specific constants */ +#define STAT_BUSY 0x80 +#define STAT_PRGD 0x02 +#define STAT_SNSD 0x01 + +#define STATM_PRGD_M 0x02 +#define STATM_SNSD_M 0x01 + +#define ERR_PRGE 0x80 +#define ERR_WPE 0x40 +#define ERR_OPE 0x20 +#define ERR_RPE 0x10 +#define ERR_WLRE 0x08 +#define ERR_SNSE 0x04 +#define ERR_PARITYE 0x02 + +#define EMASK_PRGE_M 0x80 +#define EMASK_WPE_M 0x40 +#define EMASK_OPE_M 0x20 +#define EMASK_RPE_M 0x10 +#define EMASK_WLRE_M 0x08 +#define EMASK_SNSE_M 0x04 +#define EMASK_PARITYE_M 0x02 + +#define FCTL_DPC 0x80 +#define FCTL_PRG_LENGTH_MASK 0x70 +#define FCTL_ESNS_N 0x08 +#define FCTL_ESNS_0 0x04 +#define FCTL_ESNS_1 0x02 +#define FCTL_PRG 0x01 + +#define UA_A_BANK_MASK 0x38 +#define UA_A_ROWH_MASK 0x07 + +#define LA_A_ROWL_MASK 0xf8 +#define LA_A_BIT_MASK 0x07 + +#define PREV_PROD_REV_MASK 0xf8 +#define PREV_PROD_VT_MASK 0x07 + +/* Select the correct accessors depending on endianness */ +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define iim_read32 in_le32 +#define iim_write32 out_le32 +#define iim_clrsetbits32 clrsetbits_le32 +#define iim_clrbits32 clrbits_le32 +#define iim_setbits32 setbits_le32 +#elif __BYTE_ORDER == __BIG_ENDIAN +#define iim_read32 in_be32 +#define iim_write32 out_be32 +#define iim_clrsetbits32 clrsetbits_be32 +#define iim_clrbits32 clrbits_be32 +#define iim_setbits32 setbits_be32 +#else +#error Endianess is not defined: please fix to continue +#endif + +/* IIM control registers */ +struct fsl_iim { + u32 stat; + u32 statm; + u32 err; + u32 emask; + u32 fctl; + u32 ua; + u32 la; + u32 sdat; + u32 prev; + u32 srev; + u32 prg_p; + u32 scs[0x1f5]; + struct { + u32 word[0x100]; + } bank[8]; +}; + +#if !defined(CONFIG_MX51) && !defined(CONFIG_MX53) +#define enable_efuse_prog_supply(enable) +#endif + +static int prepare_access(struct fsl_iim **regs, u32 bank, u32 word, int assert, + const char *caller) +{ + *regs = (struct fsl_iim *)IIM_BASE_ADDR; + + if (bank >= ARRAY_SIZE((*regs)->bank) || + word >= ARRAY_SIZE((*regs)->bank[0].word) || + !assert) { + printf("fsl_iim %s(): Invalid argument\n", caller); + return -EINVAL; + } + + return 0; +} + +static void clear_status(struct fsl_iim *regs) +{ + iim_setbits32(®s->stat, 0); + iim_setbits32(®s->err, 0); +} + +static void finish_access(struct fsl_iim *regs, u32 *stat, u32 *err) +{ + *stat = iim_read32(®s->stat); + *err = iim_read32(®s->err); + clear_status(regs); +} + +static int prepare_read(struct fsl_iim **regs, u32 bank, u32 word, u32 *val, + const char *caller) +{ + int ret; + + ret = prepare_access(regs, bank, word, val != NULL, caller); + if (ret) + return ret; + + clear_status(*regs); + + return 0; +} + +int fuse_read(u32 bank, u32 word, u32 *val) +{ + struct fsl_iim *regs; + u32 stat, err; + int ret; + + ret = prepare_read(®s, bank, word, val, __func__); + if (ret) + return ret; + + *val = iim_read32(®s->bank[bank].word[word]); + finish_access(regs, &stat, &err); + + if (err & ERR_RPE) { + puts("fsl_iim fuse_read(): Read protect error\n"); + return -EIO; + } + + return 0; +} + +static void direct_access(struct fsl_iim *regs, u32 bank, u32 word, u32 bit, + u32 fctl, u32 *stat, u32 *err) +{ + iim_write32(®s->ua, bank << 3 | word >> 5); + iim_write32(®s->la, (word << 3 | bit) & 0xff); + if (fctl == FCTL_PRG) + iim_write32(®s->prg_p, 0xaa); + iim_setbits32(®s->fctl, fctl); + while (iim_read32(®s->stat) & STAT_BUSY) + udelay(20); + finish_access(regs, stat, err); +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ + struct fsl_iim *regs; + u32 stat, err; + int ret; + + ret = prepare_read(®s, bank, word, val, __func__); + if (ret) + return ret; + + direct_access(regs, bank, word, 0, FCTL_ESNS_N, &stat, &err); + + if (err & ERR_SNSE) { + puts("fsl_iim fuse_sense(): Explicit sense cycle error\n"); + return -EIO; + } + + if (!(stat & STAT_SNSD)) { + puts("fsl_iim fuse_sense(): Explicit sense cycle did not complete\n"); + return -EIO; + } + + *val = iim_read32(®s->sdat); + return 0; +} + +static int prog_bit(struct fsl_iim *regs, u32 bank, u32 word, u32 bit) +{ + u32 stat, err; + + clear_status(regs); + direct_access(regs, bank, word, bit, FCTL_PRG, &stat, &err); + iim_write32(®s->prg_p, 0x00); + + if (err & ERR_PRGE) { + puts("fsl_iim fuse_prog(): Program error\n"); + return -EIO; + } + + if (err & ERR_WPE) { + puts("fsl_iim fuse_prog(): Write protect error\n"); + return -EIO; + } + + if (!(stat & STAT_PRGD)) { + puts("fsl_iim fuse_prog(): Program did not complete\n"); + return -EIO; + } + + return 0; +} + +static int prepare_write(struct fsl_iim **regs, u32 bank, u32 word, u32 val, + const char *caller) +{ + return prepare_access(regs, bank, word, !(val & ~0xff), caller); +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ + struct fsl_iim *regs; + u32 bit; + int ret; + + ret = prepare_write(®s, bank, word, val, __func__); + if (ret) + return ret; + + enable_efuse_prog_supply(1); + for (bit = 0; val; bit++, val >>= 1) + if (val & 0x01) { + ret = prog_bit(regs, bank, word, bit); + if (ret) { + enable_efuse_prog_supply(0); + return ret; + } + } + enable_efuse_prog_supply(0); + + return 0; +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ + struct fsl_iim *regs; + u32 stat, err; + int ret; + + ret = prepare_write(®s, bank, word, val, __func__); + if (ret) + return ret; + + clear_status(regs); + iim_write32(®s->bank[bank].word[word], val); + finish_access(regs, &stat, &err); + + if (err & ERR_OPE) { + puts("fsl_iim fuse_override(): Override protect error\n"); + return -EIO; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/misc/gpio_led.c b/qemu/roms/u-boot/drivers/misc/gpio_led.c new file mode 100644 index 000000000..3e95727d7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/gpio_led.c @@ -0,0 +1,53 @@ +/* + * Status LED driver based on GPIO access conventions of Linux + * + * Copyright (C) 2010 Thomas Chou + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include + +#ifndef CONFIG_GPIO_LED_INVERTED_TABLE +#define CONFIG_GPIO_LED_INVERTED_TABLE {} +#endif + +static led_id_t gpio_led_inv[] = CONFIG_GPIO_LED_INVERTED_TABLE; + +static int gpio_led_gpio_value(led_id_t mask, int state) +{ + int i, gpio_value = (state == STATUS_LED_ON); + + for (i = 0; i < ARRAY_SIZE(gpio_led_inv); i++) { + if (gpio_led_inv[i] == mask) + gpio_value = !gpio_value; + } + + return gpio_value; +} + +void __led_init(led_id_t mask, int state) +{ + int gpio_value; + + if (gpio_request(mask, "gpio_led") != 0) { + printf("%s: failed requesting GPIO%lu!\n", __func__, mask); + return; + } + + gpio_value = gpio_led_gpio_value(mask, state); + gpio_direction_output(mask, gpio_value); +} + +void __led_set(led_id_t mask, int state) +{ + int gpio_value = gpio_led_gpio_value(mask, state); + + gpio_set_value(mask, gpio_value); +} + +void __led_toggle(led_id_t mask) +{ + gpio_set_value(mask, !gpio_get_value(mask)); +} diff --git a/qemu/roms/u-boot/drivers/misc/mc9sdz60.c b/qemu/roms/u-boot/drivers/misc/mc9sdz60.c new file mode 100644 index 000000000..cd56b58c2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/mc9sdz60.c @@ -0,0 +1,35 @@ +/* + * (C) Copyright 2010 Stefano Babic + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_SYS_FSL_MC9SDZ60_I2C_ADDR +#error "You have to configure I2C address for MC9SDZ60" +#endif + + +u8 mc9sdz60_reg_read(enum mc9sdz60_reg reg) +{ + u8 val; + + if (i2c_read(CONFIG_SYS_FSL_MC9SDZ60_I2C_ADDR, reg, 1, &val, 1)) { + puts("Error reading MC9SDZ60 register\n"); + return -1; + } + + return val; +} + +void mc9sdz60_reg_write(enum mc9sdz60_reg reg, u8 val) +{ + i2c_write(CONFIG_SYS_FSL_MC9SDZ60_I2C_ADDR, reg, 1, &val, 1); +} diff --git a/qemu/roms/u-boot/drivers/misc/mxc_ocotp.c b/qemu/roms/u-boot/drivers/misc/mxc_ocotp.c new file mode 100644 index 000000000..3de124569 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/mxc_ocotp.c @@ -0,0 +1,200 @@ +/* + * (C) Copyright 2013 ADVANSEE + * Benoît Thébaudeau + * + * Based on Dirk Behme's + * https://github.com/dirkbehme/u-boot-imx6/blob/28b17e9/drivers/misc/imx_otp.c, + * which is based on Freescale's + * http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tree/drivers/misc/imx_otp.c?h=imx_v2009.08_1.1.0&id=9aa74e6, + * which is: + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#define BO_CTRL_WR_UNLOCK 16 +#define BM_CTRL_WR_UNLOCK 0xffff0000 +#define BV_CTRL_WR_UNLOCK_KEY 0x3e77 +#define BM_CTRL_ERROR 0x00000200 +#define BM_CTRL_BUSY 0x00000100 +#define BO_CTRL_ADDR 0 +#define BM_CTRL_ADDR 0x0000007f + +#define BO_TIMING_STROBE_READ 16 +#define BM_TIMING_STROBE_READ 0x003f0000 +#define BV_TIMING_STROBE_READ_NS 37 +#define BO_TIMING_RELAX 12 +#define BM_TIMING_RELAX 0x0000f000 +#define BV_TIMING_RELAX_NS 17 +#define BO_TIMING_STROBE_PROG 0 +#define BM_TIMING_STROBE_PROG 0x00000fff +#define BV_TIMING_STROBE_PROG_US 10 + +#define BM_READ_CTRL_READ_FUSE 0x00000001 + +#define BF(value, field) (((value) << BO_##field) & BM_##field) + +#define WRITE_POSTAMBLE_US 2 + +static void wait_busy(struct ocotp_regs *regs, unsigned int delay_us) +{ + while (readl(®s->ctrl) & BM_CTRL_BUSY) + udelay(delay_us); +} + +static void clear_error(struct ocotp_regs *regs) +{ + writel(BM_CTRL_ERROR, ®s->ctrl_clr); +} + +static int prepare_access(struct ocotp_regs **regs, u32 bank, u32 word, + int assert, const char *caller) +{ + *regs = (struct ocotp_regs *)OCOTP_BASE_ADDR; + + if (bank >= ARRAY_SIZE((*regs)->bank) || + word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 2 || + !assert) { + printf("mxc_ocotp %s(): Invalid argument\n", caller); + return -EINVAL; + } + + enable_ocotp_clk(1); + + wait_busy(*regs, 1); + clear_error(*regs); + + return 0; +} + +static int finish_access(struct ocotp_regs *regs, const char *caller) +{ + u32 err; + + err = !!(readl(®s->ctrl) & BM_CTRL_ERROR); + clear_error(regs); + + enable_ocotp_clk(0); + + if (err) { + printf("mxc_ocotp %s(): Access protect error\n", caller); + return -EIO; + } + + return 0; +} + +static int prepare_read(struct ocotp_regs **regs, u32 bank, u32 word, u32 *val, + const char *caller) +{ + return prepare_access(regs, bank, word, val != NULL, caller); +} + +int fuse_read(u32 bank, u32 word, u32 *val) +{ + struct ocotp_regs *regs; + int ret; + + ret = prepare_read(®s, bank, word, val, __func__); + if (ret) + return ret; + + *val = readl(®s->bank[bank].fuse_regs[word << 2]); + + return finish_access(regs, __func__); +} + +static void set_timing(struct ocotp_regs *regs) +{ + u32 ipg_clk; + u32 relax, strobe_read, strobe_prog; + u32 timing; + + ipg_clk = mxc_get_clock(MXC_IPG_CLK); + + relax = DIV_ROUND_UP(ipg_clk * BV_TIMING_RELAX_NS, 1000000000) - 1; + strobe_read = DIV_ROUND_UP(ipg_clk * BV_TIMING_STROBE_READ_NS, + 1000000000) + 2 * (relax + 1) - 1; + strobe_prog = DIV_ROUND(ipg_clk * BV_TIMING_STROBE_PROG_US, 1000000) + + 2 * (relax + 1) - 1; + + timing = BF(strobe_read, TIMING_STROBE_READ) | + BF(relax, TIMING_RELAX) | + BF(strobe_prog, TIMING_STROBE_PROG); + + clrsetbits_le32(®s->timing, BM_TIMING_STROBE_READ | BM_TIMING_RELAX | + BM_TIMING_STROBE_PROG, timing); +} + +static void setup_direct_access(struct ocotp_regs *regs, u32 bank, u32 word, + int write) +{ + u32 wr_unlock = write ? BV_CTRL_WR_UNLOCK_KEY : 0; + u32 addr = bank << 3 | word; + + set_timing(regs); + clrsetbits_le32(®s->ctrl, BM_CTRL_WR_UNLOCK | BM_CTRL_ADDR, + BF(wr_unlock, CTRL_WR_UNLOCK) | + BF(addr, CTRL_ADDR)); +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ + struct ocotp_regs *regs; + int ret; + + ret = prepare_read(®s, bank, word, val, __func__); + if (ret) + return ret; + + setup_direct_access(regs, bank, word, false); + writel(BM_READ_CTRL_READ_FUSE, ®s->read_ctrl); + wait_busy(regs, 1); + *val = readl(®s->read_fuse_data); + + return finish_access(regs, __func__); +} + +static int prepare_write(struct ocotp_regs **regs, u32 bank, u32 word, + const char *caller) +{ + return prepare_access(regs, bank, word, true, caller); +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ + struct ocotp_regs *regs; + int ret; + + ret = prepare_write(®s, bank, word, __func__); + if (ret) + return ret; + + setup_direct_access(regs, bank, word, true); + writel(val, ®s->data); + wait_busy(regs, BV_TIMING_STROBE_PROG_US); + udelay(WRITE_POSTAMBLE_US); + + return finish_access(regs, __func__); +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ + struct ocotp_regs *regs; + int ret; + + ret = prepare_write(®s, bank, word, __func__); + if (ret) + return ret; + + writel(val, ®s->bank[bank].fuse_regs[word << 2]); + + return finish_access(regs, __func__); +} diff --git a/qemu/roms/u-boot/drivers/misc/mxs_ocotp.c b/qemu/roms/u-boot/drivers/misc/mxs_ocotp.c new file mode 100644 index 000000000..545d3ebf5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/mxs_ocotp.c @@ -0,0 +1,311 @@ +/* + * Freescale i.MX28 OCOTP Driver + * + * Copyright (C) 2014 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Note: The i.MX23/i.MX28 OCOTP block is a predecessor to the OCOTP block + * used in i.MX6 . While these blocks are very similar at the first + * glance, by digging deeper, one will notice differences (like the + * tight dependence on MXS power block, some completely new registers + * etc.) which would make common driver an ifdef nightmare :-( + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MXS_OCOTP_TIMEOUT 100000 + +static struct mxs_ocotp_regs *ocotp_regs = + (struct mxs_ocotp_regs *)MXS_OCOTP_BASE; +static struct mxs_power_regs *power_regs = + (struct mxs_power_regs *)MXS_POWER_BASE; +static struct mxs_clkctrl_regs *clkctrl_regs = + (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; + +static int mxs_ocotp_wait_busy_clear(void) +{ + uint32_t reg; + int timeout = MXS_OCOTP_TIMEOUT; + + while (--timeout) { + reg = readl(&ocotp_regs->hw_ocotp_ctrl); + if (!(reg & OCOTP_CTRL_BUSY)) + break; + udelay(10); + } + + if (!timeout) + return -EINVAL; + + /* Wait a little as per FSL datasheet's 'write postamble' section. */ + udelay(10); + + return 0; +} + +static void mxs_ocotp_clear_error(void) +{ + writel(OCOTP_CTRL_ERROR, &ocotp_regs->hw_ocotp_ctrl_clr); +} + +static int mxs_ocotp_read_bank_open(bool open) +{ + int ret = 0; + + if (open) { + writel(OCOTP_CTRL_RD_BANK_OPEN, + &ocotp_regs->hw_ocotp_ctrl_set); + + /* + * Wait before polling the BUSY bit, since the BUSY bit might + * be asserted only after a few HCLK cycles and if we were to + * poll immediatelly, we could miss the busy bit. + */ + udelay(10); + ret = mxs_ocotp_wait_busy_clear(); + } else { + writel(OCOTP_CTRL_RD_BANK_OPEN, + &ocotp_regs->hw_ocotp_ctrl_clr); + } + + return ret; +} + +static void mxs_ocotp_scale_vddio(bool enter, uint32_t *val) +{ + uint32_t scale_val; + + if (enter) { + /* + * Enter the fuse programming VDDIO voltage setup. We start + * scaling the voltage from it's current value down to 2.8V + * which is the one and only correct voltage for programming + * the OCOTP fuses (according to datasheet). + */ + scale_val = readl(&power_regs->hw_power_vddioctrl); + scale_val &= POWER_VDDIOCTRL_TRG_MASK; + + /* Return the original voltage. */ + *val = scale_val; + + /* + * Start scaling VDDIO down to 0x2, which is 2.8V . Actually, + * the value 0x0 should be 2.8V, but that's not the case on + * most designs due to load etc., so we play safe. Undervolt + * can actually cause incorrect programming of the fuses and + * or reboots of the board. + */ + while (scale_val > 2) { + clrsetbits_le32(&power_regs->hw_power_vddioctrl, + POWER_VDDIOCTRL_TRG_MASK, --scale_val); + udelay(500); + } + } else { + /* Start scaling VDDIO up to original value . */ + for (scale_val = 2; scale_val <= *val; scale_val++) { + clrsetbits_le32(&power_regs->hw_power_vddioctrl, + POWER_VDDIOCTRL_TRG_MASK, scale_val); + udelay(500); + } + } + + mdelay(10); +} + +static int mxs_ocotp_wait_hclk_ready(void) +{ + uint32_t reg, timeout = MXS_OCOTP_TIMEOUT; + + while (--timeout) { + reg = readl(&clkctrl_regs->hw_clkctrl_hbus); + if (!(reg & CLKCTRL_HBUS_ASM_BUSY)) + break; + } + + if (!timeout) + return -EINVAL; + + return 0; +} + +static int mxs_ocotp_scale_hclk(bool enter, uint32_t *val) +{ + uint32_t scale_val; + int ret; + + ret = mxs_ocotp_wait_hclk_ready(); + if (ret) + return ret; + + /* Set CPU bypass */ + writel(CLKCTRL_CLKSEQ_BYPASS_CPU, + &clkctrl_regs->hw_clkctrl_clkseq_set); + + if (enter) { + /* Return the original HCLK clock speed. */ + *val = readl(&clkctrl_regs->hw_clkctrl_hbus); + *val &= CLKCTRL_HBUS_DIV_MASK; + + /* Scale the HCLK to 454/19 = 23.9 MHz . */ + scale_val = (~19) << CLKCTRL_HBUS_DIV_OFFSET; + scale_val &= CLKCTRL_HBUS_DIV_MASK; + } else { + /* Scale the HCLK back to original frequency. */ + scale_val = (~(*val)) << CLKCTRL_HBUS_DIV_OFFSET; + scale_val &= CLKCTRL_HBUS_DIV_MASK; + } + + writel(CLKCTRL_HBUS_DIV_MASK, + &clkctrl_regs->hw_clkctrl_hbus_set); + writel(scale_val, + &clkctrl_regs->hw_clkctrl_hbus_clr); + + mdelay(10); + + ret = mxs_ocotp_wait_hclk_ready(); + if (ret) + return ret; + + /* Disable CPU bypass */ + writel(CLKCTRL_CLKSEQ_BYPASS_CPU, + &clkctrl_regs->hw_clkctrl_clkseq_clr); + + mdelay(10); + + return 0; +} + +static int mxs_ocotp_write_fuse(uint32_t addr, uint32_t mask) +{ + uint32_t hclk_val, vddio_val; + int ret; + + /* Make sure the banks are closed for reading. */ + ret = mxs_ocotp_read_bank_open(0); + if (ret) { + puts("Failed closing banks for reading!\n"); + return ret; + } + + ret = mxs_ocotp_scale_hclk(1, &hclk_val); + if (ret) { + puts("Failed scaling down the HCLK!\n"); + return ret; + } + mxs_ocotp_scale_vddio(1, &vddio_val); + + ret = mxs_ocotp_wait_busy_clear(); + if (ret) { + puts("Failed waiting for ready state!\n"); + goto fail; + } + + /* Program the fuse address */ + writel(addr | OCOTP_CTRL_WR_UNLOCK_KEY, &ocotp_regs->hw_ocotp_ctrl); + + /* Program the data. */ + writel(mask, &ocotp_regs->hw_ocotp_data); + + udelay(10); + + ret = mxs_ocotp_wait_busy_clear(); + if (ret) { + puts("Failed waiting for ready state!\n"); + goto fail; + } + +fail: + mxs_ocotp_scale_vddio(0, &vddio_val); + ret = mxs_ocotp_scale_hclk(0, &hclk_val); + if (ret) { + puts("Failed scaling up the HCLK!\n"); + return ret; + } + + return ret; +} + +static int mxs_ocotp_read_fuse(uint32_t reg, uint32_t *val) +{ + int ret; + + /* Register offset from CUST0 */ + reg = ((uint32_t)&ocotp_regs->hw_ocotp_cust0) + (reg << 4); + + ret = mxs_ocotp_wait_busy_clear(); + if (ret) { + puts("Failed waiting for ready state!\n"); + return ret; + } + + mxs_ocotp_clear_error(); + + ret = mxs_ocotp_read_bank_open(1); + if (ret) { + puts("Failed opening banks for reading!\n"); + return ret; + } + + *val = readl(reg); + + ret = mxs_ocotp_read_bank_open(0); + if (ret) { + puts("Failed closing banks for reading!\n"); + return ret; + } + + return ret; +} + +static int mxs_ocotp_valid(u32 bank, u32 word) +{ + if (bank > 4) + return -EINVAL; + if (word > 7) + return -EINVAL; + return 0; +} + +/* + * The 'fuse' command API + */ +int fuse_read(u32 bank, u32 word, u32 *val) +{ + int ret; + + ret = mxs_ocotp_valid(bank, word); + if (ret) + return ret; + + return mxs_ocotp_read_fuse((bank << 3) | word, val); +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ + int ret; + + ret = mxs_ocotp_valid(bank, word); + if (ret) + return ret; + + return mxs_ocotp_write_fuse((bank << 3) | word, val); +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ + /* We do not support sensing :-( */ + return -EINVAL; +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ + /* We do not support overriding :-( */ + return -EINVAL; +} diff --git a/qemu/roms/u-boot/drivers/misc/ns87308.c b/qemu/roms/u-boot/drivers/misc/ns87308.c new file mode 100644 index 000000000..8a92ccb88 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/ns87308.c @@ -0,0 +1,101 @@ +/* + * (C) Copyright 2000 + * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include + +void initialise_ns87308 (void) +{ +#ifdef CONFIG_SYS_NS87308_PS2MOD + unsigned char data; + + /* + * Switch floppy drive to PS/2 mode. + */ + read_pnp_config(SUPOERIO_CONF1, &data); + data &= 0xFB; + write_pnp_config(SUPOERIO_CONF1, data); +#endif + +#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_KBC1) + PNP_SET_DEVICE_BASE(LDEV_KBC1, CONFIG_SYS_NS87308_KBC1_BASE); + write_pnp_config(LUN_CONFIG_REG, 0); + write_pnp_config(CBASE_HIGH, 0x00); + write_pnp_config(CBASE_LOW, 0x64); +#endif + +#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_MOUSE) + PNP_ACTIVATE_DEVICE(LDEV_MOUSE); +#endif + +#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_RTC_APC) + PNP_SET_DEVICE_BASE(LDEV_RTC_APC, CONFIG_SYS_NS87308_RTC_BASE); +#endif + +#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_FDC) + PNP_SET_DEVICE_BASE(LDEV_FDC, CONFIG_SYS_NS87308_FDC_BASE); + write_pnp_config(LUN_CONFIG_REG, 0x40); +#endif + +#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_RARP) + PNP_SET_DEVICE_BASE(LDEV_PARP, CONFIG_SYS_NS87308_LPT_BASE); +#endif + +#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_UART1) + PNP_SET_DEVICE_BASE(LDEV_UART1, CONFIG_SYS_NS87308_UART1_BASE); +#endif + +#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_UART2) + PNP_SET_DEVICE_BASE(LDEV_UART2, CONFIG_SYS_NS87308_UART2_BASE); +#endif + +#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_GPIO) + PNP_SET_DEVICE_BASE(LDEV_GPIO, CONFIG_SYS_NS87308_GPIO_BASE); +#endif + +#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_POWRMAN) +#ifndef CONFIG_SYS_NS87308_PWMAN_BASE + PNP_ACTIVATE_DEVICE(LDEV_POWRMAN); +#else + PNP_SET_DEVICE_BASE(LDEV_POWRMAN, CONFIG_SYS_NS87308_PWMAN_BASE); + + /* + * Enable all units + */ + write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_FER1, 0x7d); + write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_FER2, 0x87); + +#ifdef CONFIG_SYS_NS87308_PMC1 + write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_PMC1, CONFIG_SYS_NS87308_PMC1); +#endif + +#ifdef CONFIG_SYS_NS87308_PMC2 + write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_PMC2, CONFIG_SYS_NS87308_PMC2); +#endif + +#ifdef CONFIG_SYS_NS87308_PMC3 + write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_PMC3, CONFIG_SYS_NS87308_PMC3); +#endif +#endif +#endif + +#ifdef CONFIG_SYS_NS87308_CS0_BASE + PNP_PGCS_CSLINE_BASE(0, CONFIG_SYS_NS87308_CS0_BASE); + PNP_PGCS_CSLINE_CONF(0, CONFIG_SYS_NS87308_CS0_CONF); +#endif + +#ifdef CONFIG_SYS_NS87308_CS1_BASE + PNP_PGCS_CSLINE_BASE(1, CONFIG_SYS_NS87308_CS1_BASE); + PNP_PGCS_CSLINE_CONF(1, CONFIG_SYS_NS87308_CS1_CONF); +#endif + +#ifdef CONFIG_SYS_NS87308_CS2_BASE + PNP_PGCS_CSLINE_BASE(2, CONFIG_SYS_NS87308_CS2_BASE); + PNP_PGCS_CSLINE_CONF(2, CONFIG_SYS_NS87308_CS2_CONF); +#endif +} diff --git a/qemu/roms/u-boot/drivers/misc/pdsp188x.c b/qemu/roms/u-boot/drivers/misc/pdsp188x.c new file mode 100644 index 000000000..aa4351a0a --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/pdsp188x.c @@ -0,0 +1,45 @@ +/* + * Copyright 2010 Sergey Poselenov, Emcraft Systems, + * Copyright 2010 Ilya Yanok, Emcraft Systems, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#ifdef CONFIG_CMD_DISPLAY +#define CWORD_CLEAR 0x80 +#define CLEAR_DELAY (110 * 2) +#define DISPLAY_SIZE 8 + +static int pos; /* Current display position */ + +/* Handle different display commands */ +void display_set(int cmd) +{ + if (cmd & DISPLAY_CLEAR) { + out_8((unsigned char *)CONFIG_SYS_DISP_CWORD, CWORD_CLEAR); + udelay(1000 * CLEAR_DELAY); + } + + if (cmd & DISPLAY_HOME) { + pos = 0; + } +} + +/* + * Display a character at the current display position. + * Characters beyond the display size are ignored. + */ +int display_putc(char c) +{ + if (pos >= DISPLAY_SIZE) + return -1; + + out_8((unsigned char *)CONFIG_SYS_DISP_CHR_RAM + pos++, c); + + return c; +} +#endif diff --git a/qemu/roms/u-boot/drivers/misc/status_led.c b/qemu/roms/u-boot/drivers/misc/status_led.c new file mode 100644 index 000000000..ed9adb21d --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/status_led.c @@ -0,0 +1,111 @@ +/* + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +/* + * The purpose of this code is to signal the operational status of a + * target which usually boots over the network; while running in + * U-Boot, a status LED is blinking. As soon as a valid BOOTP reply + * message has been received, the LED is turned off. The Linux + * kernel, once it is running, will start blinking the LED again, + * with another frequency. + */ + +/* ------------------------------------------------------------------------- */ + +typedef struct { + led_id_t mask; + int state; + int period; + int cnt; +} led_dev_t; + +led_dev_t led_dev[] = { + { STATUS_LED_BIT, + STATUS_LED_STATE, + STATUS_LED_PERIOD, + 0, + }, +#if defined(STATUS_LED_BIT1) + { STATUS_LED_BIT1, + STATUS_LED_STATE1, + STATUS_LED_PERIOD1, + 0, + }, +#endif +#if defined(STATUS_LED_BIT2) + { STATUS_LED_BIT2, + STATUS_LED_STATE2, + STATUS_LED_PERIOD2, + 0, + }, +#endif +#if defined(STATUS_LED_BIT3) + { STATUS_LED_BIT3, + STATUS_LED_STATE3, + STATUS_LED_PERIOD3, + 0, + }, +#endif +}; + +#define MAX_LED_DEV (sizeof(led_dev)/sizeof(led_dev_t)) + +static int status_led_init_done = 0; + +static void status_led_init (void) +{ + led_dev_t *ld; + int i; + + for (i = 0, ld = led_dev; i < MAX_LED_DEV; i++, ld++) + __led_init (ld->mask, ld->state); + status_led_init_done = 1; +} + +void status_led_tick (ulong timestamp) +{ + led_dev_t *ld; + int i; + + if (!status_led_init_done) + status_led_init (); + + for (i = 0, ld = led_dev; i < MAX_LED_DEV; i++, ld++) { + + if (ld->state != STATUS_LED_BLINKING) + continue; + + if (++ld->cnt >= ld->period) { + __led_toggle (ld->mask); + ld->cnt -= ld->period; + } + + } +} + +void status_led_set (int led, int state) +{ + led_dev_t *ld; + + if (led < 0 || led >= MAX_LED_DEV) + return; + + if (!status_led_init_done) + status_led_init (); + + ld = &led_dev[led]; + + ld->state = state; + if (state == STATUS_LED_BLINKING) { + ld->cnt = 0; /* always start with full period */ + state = STATUS_LED_ON; /* always start with LED _ON_ */ + } + __led_set (ld->mask, state); +} diff --git a/qemu/roms/u-boot/drivers/misc/twl4030_led.c b/qemu/roms/u-boot/drivers/misc/twl4030_led.c new file mode 100644 index 000000000..432e74125 --- /dev/null +++ b/qemu/roms/u-boot/drivers/misc/twl4030_led.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix + * + * SPDX-License-Identifier: GPL-2.0+ + * + * twl4030_led_init is from cpu/omap3/common.c, power_init_r + * + * (C) Copyright 2004-2008 + * Texas Instruments, + * + * Author : + * Sunil Kumar + * Shashi Ranjan + * + * Derived from Beagle Board and 3430 SDP code by + * Richard Woodruff + * Syed Mohammed Khasim + */ + +#include + +void twl4030_led_init(unsigned char ledon_mask) +{ + /* LEDs need to have corresponding PWMs enabled */ + if (ledon_mask & TWL4030_LED_LEDEN_LEDAON) + ledon_mask |= TWL4030_LED_LEDEN_LEDAPWM; + if (ledon_mask & TWL4030_LED_LEDEN_LEDBON) + ledon_mask |= TWL4030_LED_LEDEN_LEDBPWM; + + twl4030_i2c_write_u8(TWL4030_CHIP_LED, TWL4030_LED_LEDEN, + ledon_mask); + +} diff --git a/qemu/roms/u-boot/drivers/mmc/Makefile b/qemu/roms/u-boot/drivers/mmc/Makefile new file mode 100644 index 000000000..931922bc4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/Makefile @@ -0,0 +1,37 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_BFIN_SDH) += bfin_sdh.o +obj-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o +obj-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o +obj-$(CONFIG_FTSDC010) += ftsdc010_mci.o +obj-$(CONFIG_FTSDC021) += ftsdc021_sdhci.o +obj-$(CONFIG_GENERIC_MMC) += mmc.o +obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o +obj-$(CONFIG_MMC_SPI) += mmc_spi.o +obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o +obj-$(CONFIG_MV_SDHCI) += mv_sdhci.o +obj-$(CONFIG_MXC_MMC) += mxcmmc.o +obj-$(CONFIG_MXS_MMC) += mxsmmc.o +obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o +obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o +obj-$(CONFIG_SDHCI) += sdhci.o +obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o +obj-$(CONFIG_KONA_SDHCI) += kona_sdhci.o +obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o +obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o +obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o +obj-$(CONFIG_TEGRA_MMC) += tegra_mmc.o +obj-$(CONFIG_DWMMC) += dw_mmc.o +obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o +obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o +obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o +else +obj-$(CONFIG_GENERIC_MMC) += mmc_write.o +endif diff --git a/qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.c b/qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.c new file mode 100644 index 000000000..5ef7ff7ff --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.c @@ -0,0 +1,381 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson + * Author: Martin Lundholm + * Ported to drivers/mmc/ by: Matt Waddel + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ + +#include +#include "common.h" +#include +#include +#include "arm_pl180_mmci.h" +#include + +static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) +{ + u32 hoststatus, statusmask; + struct pl180_mmc_host *host = dev->priv; + + statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; + if ((cmd->resp_type & MMC_RSP_PRESENT)) + statusmask |= SDI_STA_CMDREND; + else + statusmask |= SDI_STA_CMDSENT; + + do + hoststatus = readl(&host->base->status) & statusmask; + while (!hoststatus); + + writel(statusmask, &host->base->status_clear); + if (hoststatus & SDI_STA_CTIMEOUT) { + debug("CMD%d time out\n", cmd->cmdidx); + return TIMEOUT; + } else if ((hoststatus & SDI_STA_CCRCFAIL) && + (cmd->resp_type & MMC_RSP_CRC)) { + printf("CMD%d CRC error\n", cmd->cmdidx); + return -EILSEQ; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + cmd->response[0] = readl(&host->base->response0); + cmd->response[1] = readl(&host->base->response1); + cmd->response[2] = readl(&host->base->response2); + cmd->response[3] = readl(&host->base->response3); + debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, " + "response[2]:0x%08X, response[3]:0x%08X\n", + cmd->cmdidx, cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + } + + return 0; +} + +/* send command to the mmc card and wait for results */ +static int do_command(struct mmc *dev, struct mmc_cmd *cmd) +{ + int result; + u32 sdi_cmd = 0; + struct pl180_mmc_host *host = dev->priv; + + sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); + + if (cmd->resp_type) { + sdi_cmd |= SDI_CMD_WAITRESP; + if (cmd->resp_type & MMC_RSP_136) + sdi_cmd |= SDI_CMD_LONGRESP; + } + + writel((u32)cmd->cmdarg, &host->base->argument); + udelay(COMMAND_REG_DELAY); + writel(sdi_cmd, &host->base->command); + result = wait_for_command_end(dev, cmd); + + /* After CMD2 set RCA to a none zero value. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)) + dev->rca = 10; + + /* After CMD3 open drain is switched off and push pull is used. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) { + u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD; + writel(sdi_pwr, &host->base->power); + } + + return result; +} + +static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = dest; + u64 xfercount = blkcount * blksize; + struct pl180_mmc_host *host = dev->priv; + u32 status, status_err; + + debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + while ((!status_err) && (xfercount >= sizeof(u32))) { + if (status & SDI_STA_RXDAVL) { + *(tempbuff) = readl(&host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Read data bytes CRC error: 0x%x\n", status); + return -EILSEQ; + } else if (status & SDI_STA_RXOVERR) { + printf("Read data RX overflow error\n"); + return -EIO; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Read data error, xfercount: %llu\n", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = src; + int i; + u64 xfercount = blkcount * blksize; + struct pl180_mmc_host *host = dev->priv; + u32 status, status_err; + + debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + while (!status_err && xfercount) { + if (status & SDI_STA_TXFIFOBW) { + if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) { + for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) + writel(*(tempbuff + i), + &host->base->fifo); + tempbuff += SDI_FIFO_BURST_SIZE; + xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); + } else { + while (xfercount >= sizeof(u32)) { + writel(*(tempbuff), &host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + } + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Write data timed out, xfercount:%llu,status:0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Write data CRC error\n"); + return -EILSEQ; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Write data error, xfercount:%llu", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int do_data_transfer(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int error = -ETIMEDOUT; + struct pl180_mmc_host *host = dev->priv; + u32 blksz = 0; + u32 data_ctrl = 0; + u32 data_len = (u32) (data->blocks * data->blocksize); + + if (!host->version2) { + blksz = (ffs(data->blocksize) - 1); + data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); + } else { + blksz = data->blocksize; + data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT); + } + data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE; + + writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); + writel(data_len, &host->base->datalength); + udelay(DATA_REG_DELAY); + + if (data->flags & MMC_DATA_READ) { + data_ctrl |= SDI_DCTRL_DTDIR_IN; + writel(data_ctrl, &host->base->datactrl); + + error = do_command(dev, cmd); + if (error) + return error; + + error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks, + (u32)data->blocksize); + } else if (data->flags & MMC_DATA_WRITE) { + error = do_command(dev, cmd); + if (error) + return error; + + writel(data_ctrl, &host->base->datactrl); + error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, + (u32)data->blocksize); + } + + return error; +} + +static int host_request(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int result; + + if (data) + result = do_data_transfer(dev, cmd, data); + else + result = do_command(dev, cmd); + + return result; +} + +/* MMC uses open drain drivers in the enumeration phase */ +static int mmc_host_reset(struct mmc *dev) +{ + struct pl180_mmc_host *host = dev->priv; + + writel(host->pwr_init, &host->base->power); + + return 0; +} + +static void host_set_ios(struct mmc *dev) +{ + struct pl180_mmc_host *host = dev->priv; + u32 sdi_clkcr; + + sdi_clkcr = readl(&host->base->clock); + + /* Ramp up the clock rate */ + if (dev->clock) { + u32 clkdiv = 0; + u32 tmp_clock; + + if (dev->clock >= dev->cfg->f_max) { + clkdiv = 0; + dev->clock = dev->cfg->f_max; + } else { + clkdiv = (host->clock_in / dev->clock) - 2; + } + + tmp_clock = host->clock_in / (clkdiv + 2); + while (tmp_clock > dev->clock) { + clkdiv++; + tmp_clock = host->clock_in / (clkdiv + 2); + } + + if (clkdiv > SDI_CLKCR_CLKDIV_MASK) + clkdiv = SDI_CLKCR_CLKDIV_MASK; + + tmp_clock = host->clock_in / (clkdiv + 2); + dev->clock = tmp_clock; + sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); + sdi_clkcr |= clkdiv; + } + + /* Set the bus width */ + if (dev->bus_width) { + u32 buswidth = 0; + + switch (dev->bus_width) { + case 1: + buswidth |= SDI_CLKCR_WIDBUS_1; + break; + case 4: + buswidth |= SDI_CLKCR_WIDBUS_4; + break; + case 8: + buswidth |= SDI_CLKCR_WIDBUS_8; + break; + default: + printf("Invalid bus width: %d\n", dev->bus_width); + break; + } + sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); + sdi_clkcr |= buswidth; + } + + writel(sdi_clkcr, &host->base->clock); + udelay(CLK_CHANGE_DELAY); +} + +static const struct mmc_ops arm_pl180_mmci_ops = { + .send_cmd = host_request, + .set_ios = host_set_ios, + .init = mmc_host_reset, +}; + +/* + * mmc_host_init - initialize the mmc controller. + * Set initial clock and power for mmc slot. + * Initialize mmc struct and register with mmc framework. + */ +int arm_pl180_mmci_init(struct pl180_mmc_host *host) +{ + struct mmc *mmc; + u32 sdi_u32; + + writel(host->pwr_init, &host->base->power); + writel(host->clkdiv_init, &host->base->clock); + udelay(CLK_CHANGE_DELAY); + + /* Disable mmc interrupts */ + sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; + writel(sdi_u32, &host->base->mask0); + + host->cfg.name = host->name; + host->cfg.ops = &arm_pl180_mmci_ops; + /* TODO remove the duplicates */ + host->cfg.host_caps = host->caps; + host->cfg.voltages = host->voltages; + host->cfg.f_min = host->clock_min; + host->cfg.f_max = host->clock_max; + if (host->b_max != 0) + host->cfg.b_max = host->b_max; + else + host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + + mmc = mmc_create(&host->cfg, host); + if (mmc == NULL) + return -1; + + debug("registered mmc interface number is:%d\n", mmc->block_dev.dev); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.h b/qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.h new file mode 100644 index 000000000..f23bd391e --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/arm_pl180_mmci.h @@ -0,0 +1,195 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson + * Author: Martin Lundholm + * Ported to drivers/mmc/ by: Matt Waddel + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ARM_PL180_MMCI_H__ +#define __ARM_PL180_MMCI_H__ + +/* need definition of struct mmc_config */ +#include + +#define COMMAND_REG_DELAY 300 +#define DATA_REG_DELAY 1000 +#define CLK_CHANGE_DELAY 2000 + +#define INIT_PWR 0xBF /* Power on, full power, not open drain */ +#define ARM_MCLK (100*1000*1000) + +/* SDI Power Control register bits */ +#define SDI_PWR_PWRCTRL_MASK 0x00000003 +#define SDI_PWR_PWRCTRL_ON 0x00000003 +#define SDI_PWR_PWRCTRL_OFF 0x00000000 +#define SDI_PWR_DAT2DIREN 0x00000004 +#define SDI_PWR_CMDDIREN 0x00000008 +#define SDI_PWR_DAT0DIREN 0x00000010 +#define SDI_PWR_DAT31DIREN 0x00000020 +#define SDI_PWR_OPD 0x00000040 +#define SDI_PWR_FBCLKEN 0x00000080 +#define SDI_PWR_DAT74DIREN 0x00000100 +#define SDI_PWR_RSTEN 0x00000200 + +#define VOLTAGE_WINDOW_MMC 0x00FF8080 +#define VOLTAGE_WINDOW_SD 0x80010000 + +/* SDI clock control register bits */ +#define SDI_CLKCR_CLKDIV_MASK 0x000000FF +#define SDI_CLKCR_CLKEN 0x00000100 +#define SDI_CLKCR_PWRSAV 0x00000200 +#define SDI_CLKCR_BYPASS 0x00000400 +#define SDI_CLKCR_WIDBUS_MASK 0x00001800 +#define SDI_CLKCR_WIDBUS_1 0x00000000 +#define SDI_CLKCR_WIDBUS_4 0x00000800 +/* V2 only */ +#define SDI_CLKCR_WIDBUS_8 0x00001000 +#define SDI_CLKCR_NEDGE 0x00002000 +#define SDI_CLKCR_HWFC_EN 0x00004000 + +#define SDI_CLKCR_CLKDIV_INIT_V1 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */ +#define SDI_CLKCR_CLKDIV_INIT_V2 0x000000FD + +/* SDI command register bits */ +#define SDI_CMD_CMDINDEX_MASK 0x000000FF +#define SDI_CMD_WAITRESP 0x00000040 +#define SDI_CMD_LONGRESP 0x00000080 +#define SDI_CMD_WAITINT 0x00000100 +#define SDI_CMD_WAITPEND 0x00000200 +#define SDI_CMD_CPSMEN 0x00000400 +#define SDI_CMD_SDIOSUSPEND 0x00000800 +#define SDI_CMD_ENDCMDCOMPL 0x00001000 +#define SDI_CMD_NIEN 0x00002000 +#define SDI_CMD_CE_ATACMD 0x00004000 +#define SDI_CMD_CBOOTMODEEN 0x00008000 + +#define SDI_DTIMER_DEFAULT 0xFFFF0000 + +/* SDI Status register bits */ +#define SDI_STA_CCRCFAIL 0x00000001 +#define SDI_STA_DCRCFAIL 0x00000002 +#define SDI_STA_CTIMEOUT 0x00000004 +#define SDI_STA_DTIMEOUT 0x00000008 +#define SDI_STA_TXUNDERR 0x00000010 +#define SDI_STA_RXOVERR 0x00000020 +#define SDI_STA_CMDREND 0x00000040 +#define SDI_STA_CMDSENT 0x00000080 +#define SDI_STA_DATAEND 0x00000100 +#define SDI_STA_STBITERR 0x00000200 +#define SDI_STA_DBCKEND 0x00000400 +#define SDI_STA_CMDACT 0x00000800 +#define SDI_STA_TXACT 0x00001000 +#define SDI_STA_RXACT 0x00002000 +#define SDI_STA_TXFIFOBW 0x00004000 +#define SDI_STA_RXFIFOBR 0x00008000 +#define SDI_STA_TXFIFOF 0x00010000 +#define SDI_STA_RXFIFOF 0x00020000 +#define SDI_STA_TXFIFOE 0x00040000 +#define SDI_STA_RXFIFOE 0x00080000 +#define SDI_STA_TXDAVL 0x00100000 +#define SDI_STA_RXDAVL 0x00200000 +#define SDI_STA_SDIOIT 0x00400000 +#define SDI_STA_CEATAEND 0x00800000 +#define SDI_STA_CARDBUSY 0x01000000 +#define SDI_STA_BOOTMODE 0x02000000 +#define SDI_STA_BOOTACKERR 0x04000000 +#define SDI_STA_BOOTACKTIMEOUT 0x08000000 +#define SDI_STA_RSTNEND 0x10000000 + +/* SDI Interrupt Clear register bits */ +#define SDI_ICR_MASK 0x1DC007FF +#define SDI_ICR_CCRCFAILC 0x00000001 +#define SDI_ICR_DCRCFAILC 0x00000002 +#define SDI_ICR_CTIMEOUTC 0x00000004 +#define SDI_ICR_DTIMEOUTC 0x00000008 +#define SDI_ICR_TXUNDERRC 0x00000010 +#define SDI_ICR_RXOVERRC 0x00000020 +#define SDI_ICR_CMDRENDC 0x00000040 +#define SDI_ICR_CMDSENTC 0x00000080 +#define SDI_ICR_DATAENDC 0x00000100 +#define SDI_ICR_STBITERRC 0x00000200 +#define SDI_ICR_DBCKENDC 0x00000400 +#define SDI_ICR_SDIOITC 0x00400000 +#define SDI_ICR_CEATAENDC 0x00800000 +#define SDI_ICR_BUSYENDC 0x01000000 +#define SDI_ICR_BOOTACKERRC 0x04000000 +#define SDI_ICR_BOOTACKTIMEOUTC 0x08000000 +#define SDI_ICR_RSTNENDC 0x10000000 + +#define SDI_MASK0_MASK 0x1FFFFFFF + +/* SDI Data control register bits */ +#define SDI_DCTRL_DTEN 0x00000001 +#define SDI_DCTRL_DTDIR_IN 0x00000002 +#define SDI_DCTRL_DTMODE_STREAM 0x00000004 +#define SDI_DCTRL_DMAEN 0x00000008 +#define SDI_DCTRL_DBLKSIZE_MASK 0x000000F0 +#define SDI_DCTRL_RWSTART 0x00000100 +#define SDI_DCTRL_RWSTOP 0x00000200 +#define SDI_DCTRL_RWMOD 0x00000200 +#define SDI_DCTRL_SDIOEN 0x00000800 +#define SDI_DCTRL_DMAREQCTL 0x00001000 +#define SDI_DCTRL_DBOOTMODEEN 0x00002000 +#define SDI_DCTRL_BUSYMODE 0x00004000 +#define SDI_DCTRL_DDR_MODE 0x00008000 +#define SDI_DCTRL_DBLOCKSIZE_V2_MASK 0x7fff0000 +#define SDI_DCTRL_DBLOCKSIZE_V2_SHIFT 16 + +#define SDI_FIFO_BURST_SIZE 8 + +struct sdi_registers { + u32 power; /* 0x00*/ + u32 clock; /* 0x04*/ + u32 argument; /* 0x08*/ + u32 command; /* 0x0c*/ + u32 respcommand; /* 0x10*/ + u32 response0; /* 0x14*/ + u32 response1; /* 0x18*/ + u32 response2; /* 0x1c*/ + u32 response3; /* 0x20*/ + u32 datatimer; /* 0x24*/ + u32 datalength; /* 0x28*/ + u32 datactrl; /* 0x2c*/ + u32 datacount; /* 0x30*/ + u32 status; /* 0x34*/ + u32 status_clear; /* 0x38*/ + u32 mask0; /* 0x3c*/ + u32 mask1; /* 0x40*/ + u32 card_select; /* 0x44*/ + u32 fifo_count; /* 0x48*/ + u32 padding1[(0x80-0x4C)>>2]; + u32 fifo; /* 0x80*/ + u32 padding2[(0xFE0-0x84)>>2]; + u32 periph_id0; /* 0xFE0 mmc Peripheral Identifcation Register*/ + u32 periph_id1; /* 0xFE4*/ + u32 periph_id2; /* 0xFE8*/ + u32 periph_id3; /* 0xFEC*/ + u32 pcell_id0; /* 0xFF0*/ + u32 pcell_id1; /* 0xFF4*/ + u32 pcell_id2; /* 0xFF8*/ + u32 pcell_id3; /* 0xFFC*/ +}; + +struct pl180_mmc_host { + struct sdi_registers *base; + char name[32]; + unsigned int b_max; + unsigned int voltages; + unsigned int caps; + unsigned int clock_in; + unsigned int clock_min; + unsigned int clock_max; + unsigned int clkdiv_init; + unsigned int pwr_init; + int version2; + struct mmc_config cfg; +}; + +int arm_pl180_mmci_init(struct pl180_mmc_host *); + +#endif diff --git a/qemu/roms/u-boot/drivers/mmc/bcm2835_sdhci.c b/qemu/roms/u-boot/drivers/mmc/bcm2835_sdhci.c new file mode 100644 index 000000000..54cfabfb9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/bcm2835_sdhci.c @@ -0,0 +1,190 @@ +/* + * This code was extracted from: + * git://github.com/gonzoua/u-boot-pi.git master + * and hence presumably (C) 2012 Oleksandr Tymoshenko + * + * Tweaks for U-Boot upstreaming + * (C) 2012 Stephen Warren + * + * Portions (e.g. read/write macros, concepts for back-to-back register write + * timing workarounds) obviously extracted from the Linux kernel at: + * https://github.com/raspberrypi/linux.git rpi-3.6.y + * + * The Linux kernel code has the following (c) and license, which is hence + * propagated to Oleksandr's tree and here: + * + * Support for SDHCI device on 2835 + * Based on sdhci-bcm2708.c (c) 2010 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * SDHCI platform device - Arasan SD controller in BCM2708 + * + * Inspired by sdhci-pci.c, by Pierre Ossman + */ + +#include +#include +#include +#include + +/* 400KHz is max freq for card ID etc. Use that as min */ +#define MIN_FREQ 400000 + +struct bcm2835_sdhci_host { + struct sdhci_host host; + uint twoticks_delay; + ulong last_write; +}; + +static inline struct bcm2835_sdhci_host *to_bcm(struct sdhci_host *host) +{ + return (struct bcm2835_sdhci_host *)host; +} + +static inline void bcm2835_sdhci_raw_writel(struct sdhci_host *host, u32 val, + int reg) +{ + struct bcm2835_sdhci_host *bcm_host = to_bcm(host); + + /* + * The Arasan has a bugette whereby it may lose the content of + * successive writes to registers that are within two SD-card clock + * cycles of each other (a clock domain crossing problem). + * It seems, however, that the data register does not have this problem. + * (Which is just as well - otherwise we'd have to nobble the DMA engine + * too) + */ + while (get_timer_us(bcm_host->last_write) < bcm_host->twoticks_delay) + ; + + writel(val, host->ioaddr + reg); + bcm_host->last_write = get_timer_us(0); +} + +static inline u32 bcm2835_sdhci_raw_readl(struct sdhci_host *host, int reg) +{ + return readl(host->ioaddr + reg); +} + +static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ + bcm2835_sdhci_raw_writel(host, val, reg); +} + +static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + static u32 shadow; + u32 oldval = (reg == SDHCI_COMMAND) ? shadow : + bcm2835_sdhci_raw_readl(host, reg & ~3); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 mask = 0xffff << word_shift; + u32 newval = (oldval & ~mask) | (val << word_shift); + + if (reg == SDHCI_TRANSFER_MODE) + shadow = newval; + else + bcm2835_sdhci_raw_writel(host, newval, reg & ~3); +} + +static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + u32 oldval = bcm2835_sdhci_raw_readl(host, reg & ~3); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 mask = 0xff << byte_shift; + u32 newval = (oldval & ~mask) | (val << byte_shift); + + bcm2835_sdhci_raw_writel(host, newval, reg & ~3); +} + +static u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_raw_readl(host, reg); + + return val; +} + +static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3)); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 word = (val >> word_shift) & 0xffff; + + return word; +} + +static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3)); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 byte = (val >> byte_shift) & 0xff; + + return byte; +} + +static const struct sdhci_ops bcm2835_ops = { + .write_l = bcm2835_sdhci_writel, + .write_w = bcm2835_sdhci_writew, + .write_b = bcm2835_sdhci_writeb, + .read_l = bcm2835_sdhci_readl, + .read_w = bcm2835_sdhci_readw, + .read_b = bcm2835_sdhci_readb, +}; + +int bcm2835_sdhci_init(u32 regbase, u32 emmc_freq) +{ + struct bcm2835_sdhci_host *bcm_host; + struct sdhci_host *host; + + bcm_host = malloc(sizeof(*bcm_host)); + if (!bcm_host) { + printf("sdhci_host malloc fail!\n"); + return 1; + } + + /* + * See the comments in bcm2835_sdhci_raw_writel(). + * + * This should probably be dynamically calculated based on the actual + * frequency. However, this is the longest we'll have to wait, and + * doesn't seem to slow access down too much, so the added complexity + * doesn't seem worth it for now. + * + * 1/MIN_FREQ is (max) time per tick of eMMC clock. + * 2/MIN_FREQ is time for two ticks. + * Multiply by 1000000 to get uS per two ticks. + * +1 for hack rounding. + */ + bcm_host->twoticks_delay = ((2 * 1000000) / MIN_FREQ) + 1; + bcm_host->last_write = 0; + + host = &bcm_host->host; + host->name = "bcm2835_sdhci"; + host->ioaddr = (void *)regbase; + host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B | + SDHCI_QUIRK_WAIT_SEND_CMD; + host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + host->ops = &bcm2835_ops; + + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + add_sdhci(host, emmc_freq, MIN_FREQ); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/bfin_sdh.c b/qemu/roms/u-boot/drivers/mmc/bfin_sdh.c new file mode 100644 index 000000000..bcd6a3e52 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/bfin_sdh.c @@ -0,0 +1,303 @@ +/* + * Driver for Blackfin on-chip SDH controller + * + * Copyright (c) 2008-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__ADSPBF50x__) || defined(__ADSPBF51x__) || defined(__ADSPBF60x__) +# define bfin_read_SDH_CLK_CTL bfin_read_RSI_CLK_CONTROL +# define bfin_write_SDH_CLK_CTL bfin_write_RSI_CLK_CONTROL +# define bfin_write_SDH_ARGUMENT bfin_write_RSI_ARGUMENT +# define bfin_write_SDH_COMMAND bfin_write_RSI_COMMAND +# define bfin_read_SDH_RESPONSE0 bfin_read_RSI_RESPONSE0 +# define bfin_read_SDH_RESPONSE1 bfin_read_RSI_RESPONSE1 +# define bfin_read_SDH_RESPONSE2 bfin_read_RSI_RESPONSE2 +# define bfin_read_SDH_RESPONSE3 bfin_read_RSI_RESPONSE3 +# define bfin_write_SDH_DATA_TIMER bfin_write_RSI_DATA_TIMER +# define bfin_write_SDH_DATA_LGTH bfin_write_RSI_DATA_LGTH +# define bfin_read_SDH_DATA_CTL bfin_read_RSI_DATA_CONTROL +# define bfin_write_SDH_DATA_CTL bfin_write_RSI_DATA_CONTROL +# define bfin_read_SDH_STATUS bfin_read_RSI_STATUS +# define bfin_write_SDH_STATUS_CLR bfin_write_RSI_STATUSCL +# define bfin_read_SDH_CFG bfin_read_RSI_CONFIG +# define bfin_write_SDH_CFG bfin_write_RSI_CONFIG +# if defined(__ADSPBF60x__) +# define bfin_read_SDH_BLK_SIZE bfin_read_RSI_BLKSZ +# define bfin_write_SDH_BLK_SIZE bfin_write_RSI_BLKSZ +# define bfin_write_DMA_START_ADDR bfin_write_DMA10_START_ADDR +# define bfin_write_DMA_X_COUNT bfin_write_DMA10_X_COUNT +# define bfin_write_DMA_X_MODIFY bfin_write_DMA10_X_MODIFY +# define bfin_write_DMA_CONFIG bfin_write_DMA10_CONFIG +# else +# define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CONTROL +# define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CONTROL +# define bfin_write_DMA_START_ADDR bfin_write_DMA4_START_ADDR +# define bfin_write_DMA_X_COUNT bfin_write_DMA4_X_COUNT +# define bfin_write_DMA_X_MODIFY bfin_write_DMA4_X_MODIFY +# define bfin_write_DMA_CONFIG bfin_write_DMA4_CONFIG +# endif +# define PORTMUX_PINS \ + { P_RSI_DATA0, P_RSI_DATA1, P_RSI_DATA2, P_RSI_DATA3, P_RSI_CMD, P_RSI_CLK, 0 } +#elif defined(__ADSPBF54x__) +# define bfin_write_DMA_START_ADDR bfin_write_DMA22_START_ADDR +# define bfin_write_DMA_X_COUNT bfin_write_DMA22_X_COUNT +# define bfin_write_DMA_X_MODIFY bfin_write_DMA22_X_MODIFY +# define bfin_write_DMA_CONFIG bfin_write_DMA22_CONFIG +# define PORTMUX_PINS \ + { P_SD_D0, P_SD_D1, P_SD_D2, P_SD_D3, P_SD_CLK, P_SD_CMD, 0 } +#else +# error no support for this proc yet +#endif + +static int +sdh_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd) +{ + unsigned int status, timeout; + int cmd = mmc_cmd->cmdidx; + int flags = mmc_cmd->resp_type; + int arg = mmc_cmd->cmdarg; + int ret; + u16 sdh_cmd; + + sdh_cmd = cmd | CMD_E; + if (flags & MMC_RSP_PRESENT) + sdh_cmd |= CMD_RSP; + if (flags & MMC_RSP_136) + sdh_cmd |= CMD_L_RSP; +#ifdef RSI_BLKSZ + sdh_cmd |= CMD_DATA0_BUSY; +#endif + + bfin_write_SDH_ARGUMENT(arg); + bfin_write_SDH_COMMAND(sdh_cmd); + + /* wait for a while */ + timeout = 0; + do { + if (++timeout > 1000000) { + status = CMD_TIME_OUT; + break; + } + udelay(1); + status = bfin_read_SDH_STATUS(); + } while (!(status & (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | + CMD_CRC_FAIL))); + + if (flags & MMC_RSP_PRESENT) { + mmc_cmd->response[0] = bfin_read_SDH_RESPONSE0(); + if (flags & MMC_RSP_136) { + mmc_cmd->response[1] = bfin_read_SDH_RESPONSE1(); + mmc_cmd->response[2] = bfin_read_SDH_RESPONSE2(); + mmc_cmd->response[3] = bfin_read_SDH_RESPONSE3(); + } + } + + if (status & CMD_TIME_OUT) + ret = TIMEOUT; + else if (status & CMD_CRC_FAIL && flags & MMC_RSP_CRC) + ret = COMM_ERR; + else + ret = 0; + + bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT | + CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT); +#ifdef RSI_BLKSZ + /* wait till card ready */ + while (!(bfin_read_RSI_ESTAT() & SD_CARD_READY)) + continue; + bfin_write_RSI_ESTAT(SD_CARD_READY); +#endif + + return ret; +} + +/* set data for single block transfer */ +static int sdh_setup_data(struct mmc *mmc, struct mmc_data *data) +{ + u16 data_ctl = 0; + u16 dma_cfg = 0; + unsigned long data_size = data->blocksize * data->blocks; + + /* Don't support write yet. */ + if (data->flags & MMC_DATA_WRITE) + return UNUSABLE_ERR; +#ifndef RSI_BLKSZ + data_ctl |= ((ffs(data_size) - 1) << 4); +#else + bfin_write_SDH_BLK_SIZE(data_size); +#endif + data_ctl |= DTX_DIR; + bfin_write_SDH_DATA_CTL(data_ctl); + dma_cfg = WDSIZE_32 | PSIZE_32 | RESTART | WNR | DMAEN; + + bfin_write_SDH_DATA_TIMER(-1); + + blackfin_dcache_flush_invalidate_range(data->dest, + data->dest + data_size); + /* configure DMA */ + bfin_write_DMA_START_ADDR(data->dest); + bfin_write_DMA_X_COUNT(data_size / 4); + bfin_write_DMA_X_MODIFY(4); + bfin_write_DMA_CONFIG(dma_cfg); + bfin_write_SDH_DATA_LGTH(data_size); + /* kick off transfer */ + bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E); + + return 0; +} + + +static int bfin_sdh_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + u32 status; + int ret = 0; + + if (data) { + ret = sdh_setup_data(mmc, data); + if (ret) + return ret; + } + + ret = sdh_send_cmd(mmc, cmd); + if (ret) { + bfin_write_SDH_COMMAND(0); + bfin_write_DMA_CONFIG(0); + bfin_write_SDH_DATA_CTL(0); + SSYNC(); + printf("sending CMD%d failed\n", cmd->cmdidx); + return ret; + } + + if (data) { + do { + udelay(1); + status = bfin_read_SDH_STATUS(); + } while (!(status & (DAT_BLK_END | DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN))); + + if (status & DAT_TIME_OUT) { + bfin_write_SDH_STATUS_CLR(DAT_TIMEOUT_STAT); + ret |= TIMEOUT; + } else if (status & (DAT_CRC_FAIL | RX_OVERRUN)) { + bfin_write_SDH_STATUS_CLR(DAT_CRC_FAIL_STAT | RX_OVERRUN_STAT); + ret |= COMM_ERR; + } else + bfin_write_SDH_STATUS_CLR(DAT_BLK_END_STAT | DAT_END_STAT); + + if (ret) { + printf("tranfering data failed\n"); + return ret; + } + } + return 0; +} + +static void sdh_set_clk(unsigned long clk) +{ + unsigned long sys_clk; + unsigned long clk_div; + u16 clk_ctl = 0; + + clk_ctl = bfin_read_SDH_CLK_CTL(); + if (clk) { + /* setting SD_CLK */ + sys_clk = get_sclk(); + bfin_write_SDH_CLK_CTL(clk_ctl & ~CLK_E); + if (sys_clk % (2 * clk) == 0) + clk_div = sys_clk / (2 * clk) - 1; + else + clk_div = sys_clk / (2 * clk); + + if (clk_div > 0xff) + clk_div = 0xff; + clk_ctl |= (clk_div & 0xff); + clk_ctl |= CLK_E; + bfin_write_SDH_CLK_CTL(clk_ctl); + } else + bfin_write_SDH_CLK_CTL(clk_ctl & ~CLK_E); +} + +static void bfin_sdh_set_ios(struct mmc *mmc) +{ + u16 cfg = 0; + u16 clk_ctl = 0; + + if (mmc->bus_width == 4) { + cfg = bfin_read_SDH_CFG(); +#ifndef RSI_BLKSZ + cfg &= ~PD_SDDAT3; +#endif + cfg |= PUP_SDDAT3; + bfin_write_SDH_CFG(cfg); + clk_ctl |= WIDE_BUS_4; + } + bfin_write_SDH_CLK_CTL(clk_ctl); + sdh_set_clk(mmc->clock); +} + +static int bfin_sdh_init(struct mmc *mmc) +{ + const unsigned short pins[] = PORTMUX_PINS; + int ret; + + /* Initialize sdh controller */ + ret = peripheral_request_list(pins, "bfin_sdh"); + if (ret < 0) + return ret; +#if defined(__ADSPBF54x__) + bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1); +#endif + bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN); + /* Disable card detect pin */ + bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | 0x60); +#ifndef RSI_BLKSZ + bfin_write_SDH_PWR_CTL(PWR_ON | ROD_CTL); +#else + bfin_write_SDH_CFG(bfin_read_SDH_CFG() | PWR_ON); +#endif + return 0; +} + +static const struct mmc_ops bfin_mmc_ops = { + .send_cmd = bfin_sdh_request, + .set_ios = bfin_sdh_set_ios, + .init = bfin_sdh_init, +}; + +static struct mmc_config bfin_mmc_cfg = { + .name = "Blackfin SDH", + .ops = &bfin_mmc_ops, + .host_caps = MMC_MODE_4BIT, + .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, + .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, +}; + +int bfin_mmc_init(bd_t *bis) +{ + struct mmc *mmc; + + bfin_mmc_cfg.f_max = get_sclk(); + bfin_mmc_cfg.f_min = bfin_mmc_cfg.f_max >> 9; + + mmc = mmc_create(&bfin_mmc_cfg, NULL); + if (mmc == NULL) + return -1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/davinci_mmc.c b/qemu/roms/u-boot/drivers/mmc/davinci_mmc.c new file mode 100644 index 000000000..aae00e9da --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/davinci_mmc.c @@ -0,0 +1,389 @@ +/* + * Davinci MMC Controller Driver + * + * Copyright (C) 2010 Texas Instruments Incorporated + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DAVINCI_MAX_BLOCKS (32) +#define WATCHDOG_COUNT (100000) + +#define get_val(addr) REG(addr) +#define set_val(addr, val) REG(addr) = (val) +#define set_bit(addr, val) set_val((addr), (get_val(addr) | (val))) +#define clear_bit(addr, val) set_val((addr), (get_val(addr) & ~(val))) + +/* Set davinci clock prescalar value based on the required clock in HZ */ +static void dmmc_set_clock(struct mmc *mmc, uint clock) +{ + struct davinci_mmc *host = mmc->priv; + struct davinci_mmc_regs *regs = host->reg_base; + uint clkrt, sysclk2, act_clock; + + if (clock < mmc->cfg->f_min) + clock = mmc->cfg->f_min; + if (clock > mmc->cfg->f_max) + clock = mmc->cfg->f_max; + + set_val(®s->mmcclk, 0); + sysclk2 = host->input_clk; + clkrt = (sysclk2 / (2 * clock)) - 1; + + /* Calculate the actual clock for the divider used */ + act_clock = (sysclk2 / (2 * (clkrt + 1))); + + /* Adjust divider if actual clock exceeds the required clock */ + if (act_clock > clock) + clkrt++; + + /* check clock divider boundary and correct it */ + if (clkrt > 0xFF) + clkrt = 0xFF; + + set_val(®s->mmcclk, (clkrt | MMCCLK_CLKEN)); +} + +/* Status bit wait loop for MMCST1 */ +static int +dmmc_wait_fifo_status(volatile struct davinci_mmc_regs *regs, uint status) +{ + uint wdog = WATCHDOG_COUNT; + + while (--wdog && ((get_val(®s->mmcst1) & status) != status)) + udelay(10); + + if (!(get_val(®s->mmcctl) & MMCCTL_WIDTH_4_BIT)) + udelay(100); + + if (wdog == 0) + return COMM_ERR; + + return 0; +} + +/* Busy bit wait loop for MMCST1 */ +static int dmmc_busy_wait(volatile struct davinci_mmc_regs *regs) +{ + uint wdog = WATCHDOG_COUNT; + + while (--wdog && (get_val(®s->mmcst1) & MMCST1_BUSY)) + udelay(10); + + if (wdog == 0) + return COMM_ERR; + + return 0; +} + +/* Status bit wait loop for MMCST0 - Checks for error bits as well */ +static int dmmc_check_status(volatile struct davinci_mmc_regs *regs, + uint *cur_st, uint st_ready, uint st_error) +{ + uint wdog = WATCHDOG_COUNT; + uint mmcstatus = *cur_st; + + while (wdog--) { + if (mmcstatus & st_ready) { + *cur_st = mmcstatus; + mmcstatus = get_val(®s->mmcst1); + return 0; + } else if (mmcstatus & st_error) { + if (mmcstatus & MMCST0_TOUTRS) + return TIMEOUT; + printf("[ ST0 ERROR %x]\n", mmcstatus); + /* + * Ignore CRC errors as some MMC cards fail to + * initialize on DM365-EVM on the SD1 slot + */ + if (mmcstatus & MMCST0_CRCRS) + return 0; + return COMM_ERR; + } + udelay(10); + + mmcstatus = get_val(®s->mmcst0); + } + + printf("Status %x Timeout ST0:%x ST1:%x\n", st_ready, mmcstatus, + get_val(®s->mmcst1)); + return COMM_ERR; +} + +/* + * Sends a command out on the bus. Takes the mmc pointer, + * a command pointer, and an optional data pointer. + */ +static int +dmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + struct davinci_mmc *host = mmc->priv; + volatile struct davinci_mmc_regs *regs = host->reg_base; + uint mmcstatus, status_rdy, status_err; + uint i, cmddata, bytes_left = 0; + int fifo_words, fifo_bytes, err; + char *data_buf = NULL; + + /* Clear status registers */ + mmcstatus = get_val(®s->mmcst0); + fifo_words = (host->version == MMC_CTLR_VERSION_2) ? 16 : 8; + fifo_bytes = fifo_words << 2; + + /* Wait for any previous busy signal to be cleared */ + dmmc_busy_wait(regs); + + cmddata = cmd->cmdidx; + cmddata |= MMCCMD_PPLEN; + + /* Send init clock for CMD0 */ + if (cmd->cmdidx == MMC_CMD_GO_IDLE_STATE) + cmddata |= MMCCMD_INITCK; + + switch (cmd->resp_type) { + case MMC_RSP_R1b: + cmddata |= MMCCMD_BSYEXP; + /* Fall-through */ + case MMC_RSP_R1: /* R1, R1b, R5, R6, R7 */ + cmddata |= MMCCMD_RSPFMT_R1567; + break; + case MMC_RSP_R2: + cmddata |= MMCCMD_RSPFMT_R2; + break; + case MMC_RSP_R3: /* R3, R4 */ + cmddata |= MMCCMD_RSPFMT_R3; + break; + } + + set_val(®s->mmcim, 0); + + if (data) { + /* clear previous data transfer if any and set new one */ + bytes_left = (data->blocksize * data->blocks); + + /* Reset FIFO - Always use 32 byte fifo threshold */ + set_val(®s->mmcfifoctl, + (MMCFIFOCTL_FIFOLEV | MMCFIFOCTL_FIFORST)); + + if (host->version == MMC_CTLR_VERSION_2) + cmddata |= MMCCMD_DMATRIG; + + cmddata |= MMCCMD_WDATX; + if (data->flags == MMC_DATA_READ) { + set_val(®s->mmcfifoctl, MMCFIFOCTL_FIFOLEV); + } else if (data->flags == MMC_DATA_WRITE) { + set_val(®s->mmcfifoctl, + (MMCFIFOCTL_FIFOLEV | + MMCFIFOCTL_FIFODIR)); + cmddata |= MMCCMD_DTRW; + } + + set_val(®s->mmctod, 0xFFFF); + set_val(®s->mmcnblk, (data->blocks & MMCNBLK_NBLK_MASK)); + set_val(®s->mmcblen, (data->blocksize & MMCBLEN_BLEN_MASK)); + + if (data->flags == MMC_DATA_WRITE) { + uint val; + data_buf = (char *)data->src; + /* For write, fill FIFO with data before issue of CMD */ + for (i = 0; (i < fifo_words) && bytes_left; i++) { + memcpy((char *)&val, data_buf, 4); + set_val(®s->mmcdxr, val); + data_buf += 4; + bytes_left -= 4; + } + } + } else { + set_val(®s->mmcblen, 0); + set_val(®s->mmcnblk, 0); + } + + set_val(®s->mmctor, 0x1FFF); + + /* Send the command */ + set_val(®s->mmcarghl, cmd->cmdarg); + set_val(®s->mmccmd, cmddata); + + status_rdy = MMCST0_RSPDNE; + status_err = (MMCST0_TOUTRS | MMCST0_TOUTRD | + MMCST0_CRCWR | MMCST0_CRCRD); + if (cmd->resp_type & MMC_RSP_CRC) + status_err |= MMCST0_CRCRS; + + mmcstatus = get_val(®s->mmcst0); + err = dmmc_check_status(regs, &mmcstatus, status_rdy, status_err); + if (err) + return err; + + /* For R1b wait for busy done */ + if (cmd->resp_type == MMC_RSP_R1b) + dmmc_busy_wait(regs); + + /* Collect response from controller for specific commands */ + if (mmcstatus & MMCST0_RSPDNE) { + /* Copy the response to the response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[0] = get_val(®s->mmcrsp67); + cmd->response[1] = get_val(®s->mmcrsp45); + cmd->response[2] = get_val(®s->mmcrsp23); + cmd->response[3] = get_val(®s->mmcrsp01); + } else if (cmd->resp_type & MMC_RSP_PRESENT) { + cmd->response[0] = get_val(®s->mmcrsp67); + } + } + + if (data == NULL) + return 0; + + if (data->flags == MMC_DATA_READ) { + /* check for DATDNE along with DRRDY as the controller might + * set the DATDNE without DRRDY for smaller transfers with + * less than FIFO threshold bytes + */ + status_rdy = MMCST0_DRRDY | MMCST0_DATDNE; + status_err = MMCST0_TOUTRD | MMCST0_CRCRD; + data_buf = data->dest; + } else { + status_rdy = MMCST0_DXRDY | MMCST0_DATDNE; + status_err = MMCST0_CRCWR; + } + + /* Wait until all of the blocks are transferred */ + while (bytes_left) { + err = dmmc_check_status(regs, &mmcstatus, status_rdy, + status_err); + if (err) + return err; + + if (data->flags == MMC_DATA_READ) { + /* + * MMC controller sets the Data receive ready bit + * (DRRDY) in MMCST0 even before the entire FIFO is + * full. This results in erratic behavior if we start + * reading the FIFO soon after DRRDY. Wait for the + * FIFO full bit in MMCST1 for proper FIFO clearing. + */ + if (bytes_left > fifo_bytes) + dmmc_wait_fifo_status(regs, 0x4a); + else if (bytes_left == fifo_bytes) { + dmmc_wait_fifo_status(regs, 0x40); + if (cmd->cmdidx == MMC_CMD_SEND_EXT_CSD) + udelay(600); + } + + for (i = 0; bytes_left && (i < fifo_words); i++) { + cmddata = get_val(®s->mmcdrr); + memcpy(data_buf, (char *)&cmddata, 4); + data_buf += 4; + bytes_left -= 4; + } + } else { + /* + * MMC controller sets the Data transmit ready bit + * (DXRDY) in MMCST0 even before the entire FIFO is + * empty. This results in erratic behavior if we start + * writing the FIFO soon after DXRDY. Wait for the + * FIFO empty bit in MMCST1 for proper FIFO clearing. + */ + dmmc_wait_fifo_status(regs, MMCST1_FIFOEMP); + for (i = 0; bytes_left && (i < fifo_words); i++) { + memcpy((char *)&cmddata, data_buf, 4); + set_val(®s->mmcdxr, cmddata); + data_buf += 4; + bytes_left -= 4; + } + dmmc_busy_wait(regs); + } + } + + err = dmmc_check_status(regs, &mmcstatus, MMCST0_DATDNE, status_err); + if (err) + return err; + + return 0; +} + +/* Initialize Davinci MMC controller */ +static int dmmc_init(struct mmc *mmc) +{ + struct davinci_mmc *host = mmc->priv; + struct davinci_mmc_regs *regs = host->reg_base; + + /* Clear status registers explicitly - soft reset doesn't clear it + * If Uboot is invoked from UBL with SDMMC Support, the status + * registers can have uncleared bits + */ + get_val(®s->mmcst0); + get_val(®s->mmcst1); + + /* Hold software reset */ + set_bit(®s->mmcctl, MMCCTL_DATRST); + set_bit(®s->mmcctl, MMCCTL_CMDRST); + udelay(10); + + set_val(®s->mmcclk, 0x0); + set_val(®s->mmctor, 0x1FFF); + set_val(®s->mmctod, 0xFFFF); + + /* Clear software reset */ + clear_bit(®s->mmcctl, MMCCTL_DATRST); + clear_bit(®s->mmcctl, MMCCTL_CMDRST); + + udelay(10); + + /* Reset FIFO - Always use the maximum fifo threshold */ + set_val(®s->mmcfifoctl, (MMCFIFOCTL_FIFOLEV | MMCFIFOCTL_FIFORST)); + set_val(®s->mmcfifoctl, MMCFIFOCTL_FIFOLEV); + + return 0; +} + +/* Set buswidth or clock as indicated by the GENERIC_MMC framework */ +static void dmmc_set_ios(struct mmc *mmc) +{ + struct davinci_mmc *host = mmc->priv; + struct davinci_mmc_regs *regs = host->reg_base; + + /* Set the bus width */ + if (mmc->bus_width == 4) + set_bit(®s->mmcctl, MMCCTL_WIDTH_4_BIT); + else + clear_bit(®s->mmcctl, MMCCTL_WIDTH_4_BIT); + + /* Set clock speed */ + if (mmc->clock) + dmmc_set_clock(mmc, mmc->clock); +} + +static const struct mmc_ops dmmc_ops = { + .send_cmd = dmmc_send_cmd, + .set_ios = dmmc_set_ios, + .init = dmmc_init, +}; + +/* Called from board_mmc_init during startup. Can be called multiple times + * depending on the number of slots available on board and controller + */ +int davinci_mmc_init(bd_t *bis, struct davinci_mmc *host) +{ + host->cfg.name = "davinci"; + host->cfg.ops = &dmmc_ops; + host->cfg.f_min = 200000; + host->cfg.f_max = 25000000; + host->cfg.voltages = host->voltages; + host->cfg.host_caps = host->host_caps; + + host->cfg.b_max = DAVINCI_MAX_BLOCKS; + + mmc_create(&host->cfg, host); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/dw_mmc.c b/qemu/roms/u-boot/drivers/mmc/dw_mmc.c new file mode 100644 index 000000000..eb4e2be51 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/dw_mmc.c @@ -0,0 +1,379 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Jaehoon Chung + * Rajeshawari Shinde + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#define PAGE_SIZE 4096 + +static int dwmci_wait_reset(struct dwmci_host *host, u32 value) +{ + unsigned long timeout = 1000; + u32 ctrl; + + dwmci_writel(host, DWMCI_CTRL, value); + + while (timeout--) { + ctrl = dwmci_readl(host, DWMCI_CTRL); + if (!(ctrl & DWMCI_RESET_ALL)) + return 1; + } + return 0; +} + +static void dwmci_set_idma_desc(struct dwmci_idmac *idmac, + u32 desc0, u32 desc1, u32 desc2) +{ + struct dwmci_idmac *desc = idmac; + + desc->flags = desc0; + desc->cnt = desc1; + desc->addr = desc2; + desc->next_addr = (unsigned int)desc + sizeof(struct dwmci_idmac); +} + +static void dwmci_prepare_data(struct dwmci_host *host, + struct mmc_data *data, + struct dwmci_idmac *cur_idmac, + void *bounce_buffer) +{ + unsigned long ctrl; + unsigned int i = 0, flags, cnt, blk_cnt; + ulong data_start, data_end; + + + blk_cnt = data->blocks; + + dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); + + data_start = (ulong)cur_idmac; + dwmci_writel(host, DWMCI_DBADDR, (unsigned int)cur_idmac); + + do { + flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH ; + flags |= (i == 0) ? DWMCI_IDMAC_FS : 0; + if (blk_cnt <= 8) { + flags |= DWMCI_IDMAC_LD; + cnt = data->blocksize * blk_cnt; + } else + cnt = data->blocksize * 8; + + dwmci_set_idma_desc(cur_idmac, flags, cnt, + (u32)bounce_buffer + (i * PAGE_SIZE)); + + if (blk_cnt <= 8) + break; + blk_cnt -= 8; + cur_idmac++; + i++; + } while(1); + + data_end = (ulong)cur_idmac; + flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN); + + ctrl = dwmci_readl(host, DWMCI_CTRL); + ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN; + dwmci_writel(host, DWMCI_CTRL, ctrl); + + ctrl = dwmci_readl(host, DWMCI_BMOD); + ctrl |= DWMCI_BMOD_IDMAC_FB | DWMCI_BMOD_IDMAC_EN; + dwmci_writel(host, DWMCI_BMOD, ctrl); + + dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); + dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks); +} + +static int dwmci_set_transfer_mode(struct dwmci_host *host, + struct mmc_data *data) +{ + unsigned long mode; + + mode = DWMCI_CMD_DATA_EXP; + if (data->flags & MMC_DATA_WRITE) + mode |= DWMCI_CMD_RW; + + return mode; +} + +static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct dwmci_host *host = mmc->priv; + ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, + data ? DIV_ROUND_UP(data->blocks, 8) : 0); + int flags = 0, i; + unsigned int timeout = 100000; + u32 retry = 10000; + u32 mask, ctrl; + ulong start = get_timer(0); + struct bounce_buffer bbstate; + + while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { + if (get_timer(start) > timeout) { + printf("Timeout on data busy\n"); + return TIMEOUT; + } + } + + dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); + + if (data) { + if (data->flags == MMC_DATA_READ) { + bounce_buffer_start(&bbstate, (void*)data->dest, + data->blocksize * + data->blocks, GEN_BB_WRITE); + } else { + bounce_buffer_start(&bbstate, (void*)data->src, + data->blocksize * + data->blocks, GEN_BB_READ); + } + dwmci_prepare_data(host, data, cur_idmac, + bbstate.bounce_buffer); + } + + dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); + + if (data) + flags = dwmci_set_transfer_mode(host, data); + + if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) + return -1; + + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + flags |= DWMCI_CMD_ABORT_STOP; + else + flags |= DWMCI_CMD_PRV_DAT_WAIT; + + if (cmd->resp_type & MMC_RSP_PRESENT) { + flags |= DWMCI_CMD_RESP_EXP; + if (cmd->resp_type & MMC_RSP_136) + flags |= DWMCI_CMD_RESP_LENGTH; + } + + if (cmd->resp_type & MMC_RSP_CRC) + flags |= DWMCI_CMD_CHECK_CRC; + + flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); + + debug("Sending CMD%d\n",cmd->cmdidx); + + dwmci_writel(host, DWMCI_CMD, flags); + + for (i = 0; i < retry; i++) { + mask = dwmci_readl(host, DWMCI_RINTSTS); + if (mask & DWMCI_INTMSK_CDONE) { + if (!data) + dwmci_writel(host, DWMCI_RINTSTS, mask); + break; + } + } + + if (i == retry) + return TIMEOUT; + + if (mask & DWMCI_INTMSK_RTO) { + debug("Response Timeout..\n"); + return TIMEOUT; + } else if (mask & DWMCI_INTMSK_RE) { + debug("Response Error..\n"); + return -1; + } + + + if (cmd->resp_type & MMC_RSP_PRESENT) { + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); + cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); + cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); + cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); + } else { + cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); + } + } + + if (data) { + do { + mask = dwmci_readl(host, DWMCI_RINTSTS); + if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) { + debug("DATA ERROR!\n"); + return -1; + } + } while (!(mask & DWMCI_INTMSK_DTO)); + + dwmci_writel(host, DWMCI_RINTSTS, mask); + + ctrl = dwmci_readl(host, DWMCI_CTRL); + ctrl &= ~(DWMCI_DMA_EN); + dwmci_writel(host, DWMCI_CTRL, ctrl); + + bounce_buffer_stop(&bbstate); + } + + udelay(100); + + return 0; +} + +static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) +{ + u32 div, status; + int timeout = 10000; + unsigned long sclk; + + if ((freq == host->clock) || (freq == 0)) + return 0; + /* + * If host->get_mmc_clk didn't define, + * then assume that host->bus_hz is source clock value. + * host->bus_hz should be set from user. + */ + if (host->get_mmc_clk) + sclk = host->get_mmc_clk(host); + else if (host->bus_hz) + sclk = host->bus_hz; + else { + printf("Didn't get source clock value..\n"); + return -EINVAL; + } + + div = DIV_ROUND_UP(sclk, 2 * freq); + + dwmci_writel(host, DWMCI_CLKENA, 0); + dwmci_writel(host, DWMCI_CLKSRC, 0); + + dwmci_writel(host, DWMCI_CLKDIV, div); + dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | + DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); + + do { + status = dwmci_readl(host, DWMCI_CMD); + if (timeout-- < 0) { + printf("TIMEOUT error!!\n"); + return -ETIMEDOUT; + } + } while (status & DWMCI_CMD_START); + + dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE | + DWMCI_CLKEN_LOW_PWR); + + dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | + DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); + + timeout = 10000; + do { + status = dwmci_readl(host, DWMCI_CMD); + if (timeout-- < 0) { + printf("TIMEOUT error!!\n"); + return -ETIMEDOUT; + } + } while (status & DWMCI_CMD_START); + + host->clock = freq; + + return 0; +} + +static void dwmci_set_ios(struct mmc *mmc) +{ + struct dwmci_host *host = mmc->priv; + u32 ctype; + + debug("Buswidth = %d, clock: %d\n",mmc->bus_width, mmc->clock); + + dwmci_setup_bus(host, mmc->clock); + switch (mmc->bus_width) { + case 8: + ctype = DWMCI_CTYPE_8BIT; + break; + case 4: + ctype = DWMCI_CTYPE_4BIT; + break; + default: + ctype = DWMCI_CTYPE_1BIT; + break; + } + + dwmci_writel(host, DWMCI_CTYPE, ctype); + + if (host->clksel) + host->clksel(host); +} + +static int dwmci_init(struct mmc *mmc) +{ + struct dwmci_host *host = mmc->priv; + + if (host->board_init) + host->board_init(host); + + dwmci_writel(host, DWMCI_PWREN, 1); + + if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) { + debug("%s[%d] Fail-reset!!\n",__func__,__LINE__); + return -1; + } + + /* Enumerate at 400KHz */ + dwmci_setup_bus(host, mmc->cfg->f_min); + + dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF); + dwmci_writel(host, DWMCI_INTMASK, 0); + + dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF); + + dwmci_writel(host, DWMCI_IDINTEN, 0); + dwmci_writel(host, DWMCI_BMOD, 1); + + if (host->fifoth_val) { + dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); + } + + dwmci_writel(host, DWMCI_CLKENA, 0); + dwmci_writel(host, DWMCI_CLKSRC, 0); + + return 0; +} + +static const struct mmc_ops dwmci_ops = { + .send_cmd = dwmci_send_cmd, + .set_ios = dwmci_set_ios, + .init = dwmci_init, +}; + +int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk) +{ + host->cfg.name = host->name; + host->cfg.ops = &dwmci_ops; + host->cfg.f_min = min_clk; + host->cfg.f_max = max_clk; + + host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + + host->cfg.host_caps = host->caps; + + if (host->buswidth == 8) { + host->cfg.host_caps |= MMC_MODE_8BIT; + host->cfg.host_caps &= ~MMC_MODE_4BIT; + } else { + host->cfg.host_caps |= MMC_MODE_4BIT; + host->cfg.host_caps &= ~MMC_MODE_8BIT; + } + host->cfg.host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_HC; + + host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + + host->mmc = mmc_create(&host->cfg, host); + if (host->mmc == NULL) + return -1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/exynos_dw_mmc.c b/qemu/roms/u-boot/drivers/mmc/exynos_dw_mmc.c new file mode 100644 index 000000000..de8cdcc42 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/exynos_dw_mmc.c @@ -0,0 +1,181 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Jaehoon Chung + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DWMMC_MAX_CH_NUM 4 +#define DWMMC_MAX_FREQ 52000000 +#define DWMMC_MIN_FREQ 400000 +#define DWMMC_MMC0_CLKSEL_VAL 0x03030001 +#define DWMMC_MMC2_CLKSEL_VAL 0x03020001 + +/* + * Function used as callback function to initialise the + * CLKSEL register for every mmc channel. + */ +static void exynos_dwmci_clksel(struct dwmci_host *host) +{ + dwmci_writel(host, DWMCI_CLKSEL, host->clksel_val); +} + +unsigned int exynos_dwmci_get_clk(struct dwmci_host *host) +{ + unsigned long sclk; + int8_t clk_div; + + /* + * Since SDCLKIN is divided inside controller by the DIVRATIO + * value set in the CLKSEL register, we need to use the same output + * clock value to calculate the CLKDIV value. + * as per user manual:cclk_in = SDCLKIN / (DIVRATIO + 1) + */ + clk_div = ((dwmci_readl(host, DWMCI_CLKSEL) >> DWMCI_DIVRATIO_BIT) + & DWMCI_DIVRATIO_MASK) + 1; + sclk = get_mmc_clk(host->dev_index); + + return sclk / clk_div; +} + +static void exynos_dwmci_board_init(struct dwmci_host *host) +{ + if (host->quirks & DWMCI_QUIRK_DISABLE_SMU) { + dwmci_writel(host, EMMCP_MPSBEGIN0, 0); + dwmci_writel(host, EMMCP_SEND0, 0); + dwmci_writel(host, EMMCP_CTRL0, + MPSCTRL_SECURE_READ_BIT | + MPSCTRL_SECURE_WRITE_BIT | + MPSCTRL_NON_SECURE_READ_BIT | + MPSCTRL_NON_SECURE_WRITE_BIT | MPSCTRL_VALID); + } +} + +/* + * This function adds the mmc channel to be registered with mmc core. + * index - mmc channel number. + * regbase - register base address of mmc channel specified in 'index'. + * bus_width - operating bus width of mmc channel specified in 'index'. + * clksel - value to be written into CLKSEL register in case of FDT. + * NULL in case od non-FDT. + */ +int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel) +{ + struct dwmci_host *host = NULL; + unsigned int div; + unsigned long freq, sclk; + host = malloc(sizeof(struct dwmci_host)); + if (!host) { + printf("dwmci_host malloc fail!\n"); + return 1; + } + /* request mmc clock vlaue of 52MHz. */ + freq = 52000000; + sclk = get_mmc_clk(index); + div = DIV_ROUND_UP(sclk, freq); + /* set the clock divisor for mmc */ + set_mmc_clk(index, div); + + host->name = "EXYNOS DWMMC"; + host->ioaddr = (void *)regbase; + host->buswidth = bus_width; +#ifdef CONFIG_EXYNOS5420 + host->quirks = DWMCI_QUIRK_DISABLE_SMU; +#endif + host->board_init = exynos_dwmci_board_init; + + if (clksel) { + host->clksel_val = clksel; + } else { + if (0 == index) + host->clksel_val = DWMMC_MMC0_CLKSEL_VAL; + if (2 == index) + host->clksel_val = DWMMC_MMC2_CLKSEL_VAL; + } + + host->clksel = exynos_dwmci_clksel; + host->dev_index = index; + host->get_mmc_clk = exynos_dwmci_get_clk; + /* Add the mmc channel to be registered with mmc core */ + if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) { + debug("dwmmc%d registration failed\n", index); + return -1; + } + return 0; +} + +#ifdef CONFIG_OF_CONTROL +int exynos_dwmmc_init(const void *blob) +{ + int index, bus_width; + int node_list[DWMMC_MAX_CH_NUM]; + int err = 0, dev_id, flag, count, i; + u32 clksel_val, base, timing[3]; + + count = fdtdec_find_aliases_for_id(blob, "mmc", + COMPAT_SAMSUNG_EXYNOS5_DWMMC, node_list, + DWMMC_MAX_CH_NUM); + + for (i = 0; i < count; i++) { + int node = node_list[i]; + + if (node <= 0) + continue; + + /* Extract device id for each mmc channel */ + dev_id = pinmux_decode_periph_id(blob, node); + + /* Get the bus width from the device node */ + bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); + if (bus_width <= 0) { + debug("DWMMC: Can't get bus-width\n"); + return -1; + } + if (8 == bus_width) + flag = PINMUX_FLAG_8BIT_MODE; + else + flag = PINMUX_FLAG_NONE; + + /* config pinmux for each mmc channel */ + err = exynos_pinmux_config(dev_id, flag); + if (err) { + debug("DWMMC not configured\n"); + return err; + } + + index = dev_id - PERIPH_ID_SDMMC0; + + /* Get the base address from the device node */ + base = fdtdec_get_addr(blob, node, "reg"); + if (!base) { + debug("DWMMC: Can't get base address\n"); + return -1; + } + /* Extract the timing info from the node */ + err = fdtdec_get_int_array(blob, node, "samsung,timing", + timing, 3); + if (err) { + debug("Can't get sdr-timings for divider\n"); + return -1; + } + + clksel_val = (DWMCI_SET_SAMPLE_CLK(timing[0]) | + DWMCI_SET_DRV_CLK(timing[1]) | + DWMCI_SET_DIV_RATIO(timing[2])); + /* Initialise each mmc channel */ + err = exynos_dwmci_add_port(index, base, bus_width, clksel_val); + if (err) + debug("dwmmc Channel-%d init failed\n", index); + } + return 0; +} +#endif diff --git a/qemu/roms/u-boot/drivers/mmc/fsl_esdhc.c b/qemu/roms/u-boot/drivers/mmc/fsl_esdhc.c new file mode 100644 index 000000000..50cba64d9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/fsl_esdhc.c @@ -0,0 +1,655 @@ +/* + * Copyright 2007, 2010-2011 Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the pxa mmc code: + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct fsl_esdhc { + uint dsaddr; /* SDMA system address register */ + uint blkattr; /* Block attributes register */ + uint cmdarg; /* Command argument register */ + uint xfertyp; /* Transfer type register */ + uint cmdrsp0; /* Command response 0 register */ + uint cmdrsp1; /* Command response 1 register */ + uint cmdrsp2; /* Command response 2 register */ + uint cmdrsp3; /* Command response 3 register */ + uint datport; /* Buffer data port register */ + uint prsstat; /* Present state register */ + uint proctl; /* Protocol control register */ + uint sysctl; /* System Control Register */ + uint irqstat; /* Interrupt status register */ + uint irqstaten; /* Interrupt status enable register */ + uint irqsigen; /* Interrupt signal enable register */ + uint autoc12err; /* Auto CMD error status register */ + uint hostcapblt; /* Host controller capabilities register */ + uint wml; /* Watermark level register */ + uint mixctrl; /* For USDHC */ + char reserved1[4]; /* reserved */ + uint fevt; /* Force event register */ + uint admaes; /* ADMA error status register */ + uint adsaddr; /* ADMA system address register */ + char reserved2[160]; /* reserved */ + uint hostver; /* Host controller version register */ + char reserved3[4]; /* reserved */ + uint dmaerraddr; /* DMA error address register */ + char reserved4[4]; /* reserved */ + uint dmaerrattr; /* DMA error attribute register */ + char reserved5[4]; /* reserved */ + uint hostcapblt2; /* Host controller capabilities register 2 */ + char reserved6[8]; /* reserved */ + uint tcr; /* Tuning control register */ + char reserved7[28]; /* reserved */ + uint sddirctl; /* SD direction control register */ + char reserved8[712]; /* reserved */ + uint scr; /* eSDHC control register */ +}; + +/* Return the XFERTYP flags for a given command and data packet */ +static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) +{ + uint xfertyp = 0; + + if (data) { + xfertyp |= XFERTYP_DPSEL; +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO + xfertyp |= XFERTYP_DMAEN; +#endif + if (data->blocks > 1) { + xfertyp |= XFERTYP_MSBSEL; + xfertyp |= XFERTYP_BCEN; +#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111 + xfertyp |= XFERTYP_AC12EN; +#endif + } + + if (data->flags & MMC_DATA_READ) + xfertyp |= XFERTYP_DTDSEL; + } + + if (cmd->resp_type & MMC_RSP_CRC) + xfertyp |= XFERTYP_CCCEN; + if (cmd->resp_type & MMC_RSP_OPCODE) + xfertyp |= XFERTYP_CICEN; + if (cmd->resp_type & MMC_RSP_136) + xfertyp |= XFERTYP_RSPTYP_136; + else if (cmd->resp_type & MMC_RSP_BUSY) + xfertyp |= XFERTYP_RSPTYP_48_BUSY; + else if (cmd->resp_type & MMC_RSP_PRESENT) + xfertyp |= XFERTYP_RSPTYP_48; + +#if defined(CONFIG_MX53) || defined(CONFIG_PPC_T4240) + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + xfertyp |= XFERTYP_CMDTYP_ABORT; +#endif + return XFERTYP_CMD(cmd->cmdidx) | xfertyp; +} + +#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO +/* + * PIO Read/Write Mode reduce the performace as DMA is not used in this mode. + */ +static void +esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data) +{ + struct fsl_esdhc_cfg *cfg = mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + uint blocks; + char *buffer; + uint databuf; + uint size; + uint irqstat; + uint timeout; + + if (data->flags & MMC_DATA_READ) { + blocks = data->blocks; + buffer = data->dest; + while (blocks) { + timeout = PIO_TIMEOUT; + size = data->blocksize; + irqstat = esdhc_read32(®s->irqstat); + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BREN) + && --timeout); + if (timeout <= 0) { + printf("\nData Read Failed in PIO Mode."); + return; + } + while (size && (!(irqstat & IRQSTAT_TC))) { + udelay(100); /* Wait before last byte transfer complete */ + irqstat = esdhc_read32(®s->irqstat); + databuf = in_le32(®s->datport); + *((uint *)buffer) = databuf; + buffer += 4; + size -= 4; + } + blocks--; + } + } else { + blocks = data->blocks; + buffer = (char *)data->src; + while (blocks) { + timeout = PIO_TIMEOUT; + size = data->blocksize; + irqstat = esdhc_read32(®s->irqstat); + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BWEN) + && --timeout); + if (timeout <= 0) { + printf("\nData Write Failed in PIO Mode."); + return; + } + while (size && (!(irqstat & IRQSTAT_TC))) { + udelay(100); /* Wait before last byte transfer complete */ + databuf = *((uint *)buffer); + buffer += 4; + size -= 4; + irqstat = esdhc_read32(®s->irqstat); + out_le32(®s->datport, databuf); + } + blocks--; + } + } +} +#endif + +static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) +{ + int timeout; + struct fsl_esdhc_cfg *cfg = mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO + uint wml_value; + + wml_value = data->blocksize/4; + + if (data->flags & MMC_DATA_READ) { + if (wml_value > WML_RD_WML_MAX) + wml_value = WML_RD_WML_MAX_VAL; + + esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); + esdhc_write32(®s->dsaddr, (u32)data->dest); + } else { + flush_dcache_range((ulong)data->src, + (ulong)data->src+data->blocks + *data->blocksize); + + if (wml_value > WML_WR_WML_MAX) + wml_value = WML_WR_WML_MAX_VAL; + if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { + printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); + return TIMEOUT; + } + + esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, + wml_value << 16); + esdhc_write32(®s->dsaddr, (u32)data->src); + } +#else /* CONFIG_SYS_FSL_ESDHC_USE_PIO */ + if (!(data->flags & MMC_DATA_READ)) { + if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { + printf("\nThe SD card is locked. " + "Can not write to a locked card.\n\n"); + return TIMEOUT; + } + esdhc_write32(®s->dsaddr, (u32)data->src); + } else + esdhc_write32(®s->dsaddr, (u32)data->dest); +#endif /* CONFIG_SYS_FSL_ESDHC_USE_PIO */ + + esdhc_write32(®s->blkattr, data->blocks << 16 | data->blocksize); + + /* Calculate the timeout period for data transactions */ + /* + * 1)Timeout period = (2^(timeout+13)) SD Clock cycles + * 2)Timeout period should be minimum 0.250sec as per SD Card spec + * So, Number of SD Clock cycles for 0.25sec should be minimum + * (SD Clock/sec * 0.25 sec) SD Clock cycles + * = (mmc->clock * 1/4) SD Clock cycles + * As 1) >= 2) + * => (2^(timeout+13)) >= mmc->clock * 1/4 + * Taking log2 both the sides + * => timeout + 13 >= log2(mmc->clock/4) + * Rounding up to next power of 2 + * => timeout + 13 = log2(mmc->clock/4) + 1 + * => timeout + 13 = fls(mmc->clock/4) + */ + timeout = fls(mmc->clock/4); + timeout -= 13; + + if (timeout > 14) + timeout = 14; + + if (timeout < 0) + timeout = 0; + +#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC_A001 + if ((timeout == 4) || (timeout == 8) || (timeout == 12)) + timeout++; +#endif + +#ifdef ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE + timeout = 0xE; +#endif + esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); + + return 0; +} + +static void check_and_invalidate_dcache_range + (struct mmc_cmd *cmd, + struct mmc_data *data) { + unsigned start = (unsigned)data->dest ; + unsigned size = roundup(ARCH_DMA_MINALIGN, + data->blocks*data->blocksize); + unsigned end = start+size ; + invalidate_dcache_range(start, end); +} +/* + * Sends a command out on the bus. Takes the mmc pointer, + * a command pointer, and an optional data pointer. + */ +static int +esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + int err = 0; + uint xfertyp; + uint irqstat; + struct fsl_esdhc_cfg *cfg = mmc->priv; + volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + +#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111 + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + return 0; +#endif + + esdhc_write32(®s->irqstat, -1); + + sync(); + + /* Wait for the bus to be idle */ + while ((esdhc_read32(®s->prsstat) & PRSSTAT_CICHB) || + (esdhc_read32(®s->prsstat) & PRSSTAT_CIDHB)) + ; + + while (esdhc_read32(®s->prsstat) & PRSSTAT_DLA) + ; + + /* Wait at least 8 SD clock cycles before the next command */ + /* + * Note: This is way more than 8 cycles, but 1ms seems to + * resolve timing issues with some cards + */ + udelay(1000); + + /* Set up for a data transfer if we have one */ + if (data) { + err = esdhc_setup_data(mmc, data); + if(err) + return err; + } + + /* Figure out the transfer arguments */ + xfertyp = esdhc_xfertyp(cmd, data); + + /* Mask all irqs */ + esdhc_write32(®s->irqsigen, 0); + + /* Send the command */ + esdhc_write32(®s->cmdarg, cmd->cmdarg); +#if defined(CONFIG_FSL_USDHC) + esdhc_write32(®s->mixctrl, + (esdhc_read32(®s->mixctrl) & 0xFFFFFF80) | (xfertyp & 0x7F)); + esdhc_write32(®s->xfertyp, xfertyp & 0xFFFF0000); +#else + esdhc_write32(®s->xfertyp, xfertyp); +#endif + + /* Wait for the command to complete */ + while (!(esdhc_read32(®s->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE))) + ; + + irqstat = esdhc_read32(®s->irqstat); + + if (irqstat & CMD_ERR) { + err = COMM_ERR; + goto out; + } + + if (irqstat & IRQSTAT_CTOE) { + err = TIMEOUT; + goto out; + } + + /* Workaround for ESDHC errata ENGcm03648 */ + if (!data && (cmd->resp_type & MMC_RSP_BUSY)) { + int timeout = 2500; + + /* Poll on DATA0 line for cmd with busy signal for 250 ms */ + while (timeout > 0 && !(esdhc_read32(®s->prsstat) & + PRSSTAT_DAT0)) { + udelay(100); + timeout--; + } + + if (timeout <= 0) { + printf("Timeout waiting for DAT0 to go high!\n"); + err = TIMEOUT; + goto out; + } + } + + /* Copy the response to the response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; + + cmdrsp3 = esdhc_read32(®s->cmdrsp3); + cmdrsp2 = esdhc_read32(®s->cmdrsp2); + cmdrsp1 = esdhc_read32(®s->cmdrsp1); + cmdrsp0 = esdhc_read32(®s->cmdrsp0); + cmd->response[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24); + cmd->response[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24); + cmd->response[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24); + cmd->response[3] = (cmdrsp0 << 8); + } else + cmd->response[0] = esdhc_read32(®s->cmdrsp0); + + /* Wait until all of the blocks are transferred */ + if (data) { +#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO + esdhc_pio_read_write(mmc, data); +#else + do { + irqstat = esdhc_read32(®s->irqstat); + + if (irqstat & IRQSTAT_DTOE) { + err = TIMEOUT; + goto out; + } + + if (irqstat & DATA_ERR) { + err = COMM_ERR; + goto out; + } + } while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE); +#endif + if (data->flags & MMC_DATA_READ) + check_and_invalidate_dcache_range(cmd, data); + } + +out: + /* Reset CMD and DATA portions on error */ + if (err) { + esdhc_write32(®s->sysctl, esdhc_read32(®s->sysctl) | + SYSCTL_RSTC); + while (esdhc_read32(®s->sysctl) & SYSCTL_RSTC) + ; + + if (data) { + esdhc_write32(®s->sysctl, + esdhc_read32(®s->sysctl) | + SYSCTL_RSTD); + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTD)) + ; + } + } + + esdhc_write32(®s->irqstat, -1); + + return err; +} + +static void set_sysctl(struct mmc *mmc, uint clock) +{ + int div, pre_div; + struct fsl_esdhc_cfg *cfg = mmc->priv; + volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + int sdhc_clk = cfg->sdhc_clk; + uint clk; + + if (clock < mmc->cfg->f_min) + clock = mmc->cfg->f_min; + + if (sdhc_clk / 16 > clock) { + for (pre_div = 2; pre_div < 256; pre_div *= 2) + if ((sdhc_clk / pre_div) <= (clock * 16)) + break; + } else + pre_div = 2; + + for (div = 1; div <= 16; div++) + if ((sdhc_clk / (div * pre_div)) <= clock) + break; + + pre_div >>= 1; + div -= 1; + + clk = (pre_div << 8) | (div << 4); + + esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN); + + esdhc_clrsetbits32(®s->sysctl, SYSCTL_CLOCK_MASK, clk); + + udelay(10000); + + clk = SYSCTL_PEREN | SYSCTL_CKEN; + + esdhc_setbits32(®s->sysctl, clk); +} + +static void esdhc_set_ios(struct mmc *mmc) +{ + struct fsl_esdhc_cfg *cfg = mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + + /* Set the clock speed */ + set_sysctl(mmc, mmc->clock); + + /* Set the bus width */ + esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); + + if (mmc->bus_width == 4) + esdhc_setbits32(®s->proctl, PROCTL_DTW_4); + else if (mmc->bus_width == 8) + esdhc_setbits32(®s->proctl, PROCTL_DTW_8); + +} + +static int esdhc_init(struct mmc *mmc) +{ + struct fsl_esdhc_cfg *cfg = mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + int timeout = 1000; + + /* Reset the entire host controller */ + esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); + + /* Wait until the controller is available */ + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) + udelay(1000); + +#ifndef ARCH_MXC + /* Enable cache snooping */ + esdhc_write32(®s->scr, 0x00000040); +#endif + + esdhc_setbits32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); + + /* Set the initial clock speed */ + mmc_set_clock(mmc, 400000); + + /* Disable the BRR and BWR bits in IRQSTAT */ + esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); + + /* Put the PROCTL reg back to the default */ + esdhc_write32(®s->proctl, PROCTL_INIT); + + /* Set timout to the maximum value */ + esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16); + + return 0; +} + +static int esdhc_getcd(struct mmc *mmc) +{ + struct fsl_esdhc_cfg *cfg = mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + int timeout = 1000; + +#ifdef CONFIG_ESDHC_DETECT_QUIRK + if (CONFIG_ESDHC_DETECT_QUIRK) + return 1; +#endif + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_CINS) && --timeout) + udelay(1000); + + return timeout > 0; +} + +static void esdhc_reset(struct fsl_esdhc *regs) +{ + unsigned long timeout = 100; /* wait max 100 ms */ + + /* reset the controller */ + esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); + + /* hardware clears the bit when it is done */ + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) + udelay(1000); + if (!timeout) + printf("MMC/SD: Reset never completed.\n"); +} + +static const struct mmc_ops esdhc_ops = { + .send_cmd = esdhc_send_cmd, + .set_ios = esdhc_set_ios, + .init = esdhc_init, + .getcd = esdhc_getcd, +}; + +int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) +{ + struct fsl_esdhc *regs; + struct mmc *mmc; + u32 caps, voltage_caps; + + if (!cfg) + return -1; + + regs = (struct fsl_esdhc *)cfg->esdhc_base; + + /* First reset the eSDHC controller */ + esdhc_reset(regs); + + esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN + | SYSCTL_IPGEN | SYSCTL_CKEN); + + memset(&cfg->cfg, 0, sizeof(cfg->cfg)); + + voltage_caps = 0; + caps = regs->hostcapblt; + +#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC135 + caps = caps & ~(ESDHC_HOSTCAPBLT_SRS | + ESDHC_HOSTCAPBLT_VS18 | ESDHC_HOSTCAPBLT_VS30); +#endif + +/* T4240 host controller capabilities register should have VS33 bit */ +#ifdef CONFIG_SYS_FSL_MMC_HAS_CAPBLT_VS33 + caps = caps | ESDHC_HOSTCAPBLT_VS33; +#endif + + if (caps & ESDHC_HOSTCAPBLT_VS18) + voltage_caps |= MMC_VDD_165_195; + if (caps & ESDHC_HOSTCAPBLT_VS30) + voltage_caps |= MMC_VDD_29_30 | MMC_VDD_30_31; + if (caps & ESDHC_HOSTCAPBLT_VS33) + voltage_caps |= MMC_VDD_32_33 | MMC_VDD_33_34; + + cfg->cfg.name = "FSL_SDHC"; + cfg->cfg.ops = &esdhc_ops; +#ifdef CONFIG_SYS_SD_VOLTAGE + cfg->cfg.voltages = CONFIG_SYS_SD_VOLTAGE; +#else + cfg->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; +#endif + if ((cfg->cfg.voltages & voltage_caps) == 0) { + printf("voltage not supported by controller\n"); + return -1; + } + + cfg->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HC; + + if (cfg->max_bus_width > 0) { + if (cfg->max_bus_width < 8) + cfg->cfg.host_caps &= ~MMC_MODE_8BIT; + if (cfg->max_bus_width < 4) + cfg->cfg.host_caps &= ~MMC_MODE_4BIT; + } + + if (caps & ESDHC_HOSTCAPBLT_HSS) + cfg->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + +#ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK + if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK) + cfg->cfg.host_caps &= ~MMC_MODE_8BIT; +#endif + + cfg->cfg.f_min = 400000; + cfg->cfg.f_max = MIN(gd->arch.sdhc_clk, 52000000); + + cfg->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + + mmc = mmc_create(&cfg->cfg, cfg); + if (mmc == NULL) + return -1; + + return 0; +} + +int fsl_esdhc_mmc_init(bd_t *bis) +{ + struct fsl_esdhc_cfg *cfg; + + cfg = calloc(sizeof(struct fsl_esdhc_cfg), 1); + cfg->esdhc_base = CONFIG_SYS_FSL_ESDHC_ADDR; + cfg->sdhc_clk = gd->arch.sdhc_clk; + return fsl_esdhc_initialize(bis, cfg); +} + +#ifdef CONFIG_OF_LIBFDT +void fdt_fixup_esdhc(void *blob, bd_t *bd) +{ + const char *compat = "fsl,esdhc"; + +#ifdef CONFIG_FSL_ESDHC_PIN_MUX + if (!hwconfig("esdhc")) { + do_fixup_by_compat(blob, compat, "status", "disabled", + 8 + 1, 1); + return; + } +#endif + + do_fixup_by_compat_u32(blob, compat, "clock-frequency", + gd->arch.sdhc_clk, 1); + + do_fixup_by_compat(blob, compat, "status", "okay", + 4 + 1, 1); +} +#endif diff --git a/qemu/roms/u-boot/drivers/mmc/fsl_esdhc_spl.c b/qemu/roms/u-boot/drivers/mmc/fsl_esdhc_spl.c new file mode 100644 index 000000000..b1cb4b353 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/fsl_esdhc_spl.c @@ -0,0 +1,150 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +/* + * The environment variables are written to just after the u-boot image + * on SDCard, so we must read the MBR to get the start address and code + * length of the u-boot image, then calculate the address of the env. + */ +#define ESDHC_BOOT_IMAGE_SIZE 0x48 +#define ESDHC_BOOT_IMAGE_ADDR 0x50 +#define MBRDBR_BOOT_SIG_55 0x1fe +#define MBRDBR_BOOT_SIG_AA 0x1ff +#define CONFIG_CFG_DATA_SECTOR 0 + + +void mmc_spl_load_image(uint32_t offs, unsigned int size, void *vdst) +{ + uint blk_start, blk_cnt, err; + + struct mmc *mmc = find_mmc_device(0); + if (!mmc) { + puts("spl: mmc device not found!!\n"); + hang(); + } + + if (mmc_init(mmc)) { + puts("MMC init failed\n"); + return; + } + + blk_start = ALIGN(offs, mmc->read_bl_len) / mmc->read_bl_len; + blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len; + + err = mmc->block_dev.block_read(0, blk_start, blk_cnt, vdst); + if (err != blk_cnt) { + puts("spl: mmc read failed!!\n"); + hang(); + } +} + +/* + * The main entry for mmc booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from mmc into SDRAM and starts it from there. + */ + +void __noreturn mmc_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + uint blk_start, blk_cnt, err; +#ifndef CONFIG_FSL_CORENET + uchar *tmp_buf; + u32 blklen; + uchar val; + uint i, byte_num; +#endif + u32 offset, code_len; + struct mmc *mmc; + + mmc = find_mmc_device(0); + if (!mmc) { + puts("spl: mmc device not found!!\n"); + hang(); + } + +#ifdef CONFIG_FSL_CORENET + offset = CONFIG_SYS_MMC_U_BOOT_OFFS; + code_len = CONFIG_SYS_MMC_U_BOOT_SIZE; +#else + blklen = mmc->read_bl_len; + tmp_buf = malloc(blklen); + if (!tmp_buf) { + puts("spl: malloc memory failed!!\n"); + hang(); + } + memset(tmp_buf, 0, blklen); + + /* + * Read source addr from sd card + */ + err = mmc->block_dev.block_read(0, CONFIG_CFG_DATA_SECTOR, 1, tmp_buf); + if (err != 1) { + puts("spl: mmc read failed!!\n"); + free(tmp_buf); + hang(); + } + + val = *(tmp_buf + MBRDBR_BOOT_SIG_55); + if (0x55 != val) { + puts("spl: mmc signature is not valid!!\n"); + free(tmp_buf); + hang(); + } + val = *(tmp_buf + MBRDBR_BOOT_SIG_AA); + if (0xAA != val) { + puts("spl: mmc signature is not valid!!\n"); + free(tmp_buf); + hang(); + } + + byte_num = 4; + offset = 0; + for (i = 0; i < byte_num; i++) { + val = *(tmp_buf + ESDHC_BOOT_IMAGE_ADDR + i); + offset = (offset << 8) + val; + } + offset += CONFIG_SYS_MMC_U_BOOT_OFFS; + /* Get the code size from offset 0x48 */ + byte_num = 4; + code_len = 0; + for (i = 0; i < byte_num; i++) { + val = *(tmp_buf + ESDHC_BOOT_IMAGE_SIZE + i); + code_len = (code_len << 8) + val; + } + code_len -= CONFIG_SYS_MMC_U_BOOT_OFFS; + /* + * Load U-Boot image from mmc into RAM + */ +#endif + blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len; + blk_cnt = ALIGN(code_len, mmc->read_bl_len) / mmc->read_bl_len; + err = mmc->block_dev.block_read(0, blk_start, blk_cnt, + (uchar *)CONFIG_SYS_MMC_U_BOOT_DST); + if (err != blk_cnt) { + puts("spl: mmc read failed!!\n"); +#ifndef CONFIG_FSL_CORENET + free(tmp_buf); +#endif + hang(); + } + + /* + * Clean d-cache and invalidate i-cache, to + * make sure that no stale data is executed. + */ + flush_cache(CONFIG_SYS_MMC_U_BOOT_DST, CONFIG_SYS_MMC_U_BOOT_SIZE); + + /* + * Jump to U-Boot image + */ + uboot = (void *)CONFIG_SYS_MMC_U_BOOT_START; + (*uboot)(); +} diff --git a/qemu/roms/u-boot/drivers/mmc/ftsdc010_mci.c b/qemu/roms/u-boot/drivers/mmc/ftsdc010_mci.c new file mode 100644 index 000000000..a620678e5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/ftsdc010_mci.c @@ -0,0 +1,373 @@ +/* + * Faraday MMC/SD Host Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 4) /* 250 ms */ +#define CFG_RST_TIMEOUT CONFIG_SYS_HZ /* 1 sec reset timeout */ + +struct ftsdc010_chip { + void __iomem *regs; + uint32_t wprot; /* write protected (locked) */ + uint32_t rate; /* actual SD clock in Hz */ + uint32_t sclk; /* FTSDC010 source clock in Hz */ + uint32_t fifo; /* fifo depth in bytes */ + uint32_t acmd; + struct mmc_config cfg; /* mmc configuration */ +}; + +static inline int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + int ret = TIMEOUT; + uint32_t ts, st; + uint32_t cmd = FTSDC010_CMD_IDX(mmc_cmd->cmdidx); + uint32_t arg = mmc_cmd->cmdarg; + uint32_t flags = mmc_cmd->resp_type; + + cmd |= FTSDC010_CMD_CMD_EN; + + if (chip->acmd) { + cmd |= FTSDC010_CMD_APP_CMD; + chip->acmd = 0; + } + + if (flags & MMC_RSP_PRESENT) + cmd |= FTSDC010_CMD_NEED_RSP; + + if (flags & MMC_RSP_136) + cmd |= FTSDC010_CMD_LONG_RSP; + + writel(FTSDC010_STATUS_RSP_MASK | FTSDC010_STATUS_CMD_SEND, + ®s->clr); + writel(arg, ®s->argu); + writel(cmd, ®s->cmd); + + if (!(flags & (MMC_RSP_PRESENT | MMC_RSP_136))) { + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + if (readl(®s->status) & FTSDC010_STATUS_CMD_SEND) { + writel(FTSDC010_STATUS_CMD_SEND, ®s->clr); + ret = 0; + break; + } + } + } else { + st = 0; + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + st = readl(®s->status); + writel(st & FTSDC010_STATUS_RSP_MASK, ®s->clr); + if (st & FTSDC010_STATUS_RSP_MASK) + break; + } + if (st & FTSDC010_STATUS_RSP_CRC_OK) { + if (flags & MMC_RSP_136) { + mmc_cmd->response[0] = readl(®s->rsp3); + mmc_cmd->response[1] = readl(®s->rsp2); + mmc_cmd->response[2] = readl(®s->rsp1); + mmc_cmd->response[3] = readl(®s->rsp0); + } else { + mmc_cmd->response[0] = readl(®s->rsp0); + } + ret = 0; + } else { + debug("ftsdc010: rsp err (cmd=%d, st=0x%x)\n", + mmc_cmd->cmdidx, st); + } + } + + if (ret) { + debug("ftsdc010: cmd timeout (op code=%d)\n", + mmc_cmd->cmdidx); + } else if (mmc_cmd->cmdidx == MMC_CMD_APP_CMD) { + chip->acmd = 1; + } + + return ret; +} + +static void ftsdc010_clkset(struct mmc *mmc, uint32_t rate) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + uint32_t div; + + for (div = 0; div < 0x7f; ++div) { + if (rate >= chip->sclk / (2 * (div + 1))) + break; + } + chip->rate = chip->sclk / (2 * (div + 1)); + + writel(FTSDC010_CCR_CLK_DIV(div), ®s->ccr); + + if (IS_SD(mmc)) { + setbits_le32(®s->ccr, FTSDC010_CCR_CLK_SD); + + if (chip->rate > 25000000) + setbits_le32(®s->ccr, FTSDC010_CCR_CLK_HISPD); + else + clrbits_le32(®s->ccr, FTSDC010_CCR_CLK_HISPD); + } +} + +static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask) +{ + int ret = TIMEOUT; + uint32_t st, ts; + + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + st = readl(®s->status); + if (!(st & mask)) + continue; + writel(st & mask, ®s->clr); + ret = 0; + break; + } + + if (ret) + debug("ftsdc010: wait st(0x%x) timeout\n", mask); + + return ret; +} + +/* + * u-boot mmc api + */ + +static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int ret = UNUSABLE_ERR; + uint32_t len = 0; + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + + if (data && (data->flags & MMC_DATA_WRITE) && chip->wprot) { + printf("ftsdc010: the card is write protected!\n"); + return ret; + } + + if (data) { + uint32_t dcr; + + len = data->blocksize * data->blocks; + + /* 1. data disable + fifo reset */ + dcr = 0; +#ifdef CONFIG_FTSDC010_SDIO + dcr |= FTSDC010_DCR_FIFO_RST; +#endif + writel(dcr, ®s->dcr); + + /* 2. clear status register */ + writel(FTSDC010_STATUS_DATA_MASK | FTSDC010_STATUS_FIFO_URUN + | FTSDC010_STATUS_FIFO_ORUN, ®s->clr); + + /* 3. data timeout (1 sec) */ + writel(chip->rate, ®s->dtr); + + /* 4. data length (bytes) */ + writel(len, ®s->dlr); + + /* 5. data enable */ + dcr = (ffs(data->blocksize) - 1) | FTSDC010_DCR_DATA_EN; + if (data->flags & MMC_DATA_WRITE) + dcr |= FTSDC010_DCR_DATA_WRITE; + writel(dcr, ®s->dcr); + } + + ret = ftsdc010_send_cmd(mmc, cmd); + if (ret) { + printf("ftsdc010: CMD%d failed\n", cmd->cmdidx); + return ret; + } + + if (!data) + return ret; + + if (data->flags & MMC_DATA_WRITE) { + const uint8_t *buf = (const uint8_t *)data->src; + + while (len > 0) { + int wlen; + + /* wait for tx ready */ + ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_URUN); + if (ret) + break; + + /* write bytes to ftsdc010 */ + for (wlen = 0; wlen < len && wlen < chip->fifo; ) { + writel(*(uint32_t *)buf, ®s->dwr); + buf += 4; + wlen += 4; + } + + len -= wlen; + } + + } else { + uint8_t *buf = (uint8_t *)data->dest; + + while (len > 0) { + int rlen; + + /* wait for rx ready */ + ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_ORUN); + if (ret) + break; + + /* fetch bytes from ftsdc010 */ + for (rlen = 0; rlen < len && rlen < chip->fifo; ) { + *(uint32_t *)buf = readl(®s->dwr); + buf += 4; + rlen += 4; + } + + len -= rlen; + } + + } + + if (!ret) { + ret = ftsdc010_wait(regs, + FTSDC010_STATUS_DATA_END | FTSDC010_STATUS_DATA_ERROR); + } + + return ret; +} + +static void ftsdc010_set_ios(struct mmc *mmc) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + + ftsdc010_clkset(mmc, mmc->clock); + + clrbits_le32(®s->bwr, FTSDC010_BWR_MODE_MASK); + switch (mmc->bus_width) { + case 4: + setbits_le32(®s->bwr, FTSDC010_BWR_MODE_4BIT); + break; + case 8: + setbits_le32(®s->bwr, FTSDC010_BWR_MODE_8BIT); + break; + default: + setbits_le32(®s->bwr, FTSDC010_BWR_MODE_1BIT); + break; + } +} + +static int ftsdc010_init(struct mmc *mmc) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + uint32_t ts; + + if (readl(®s->status) & FTSDC010_STATUS_CARD_DETECT) + return NO_CARD_ERR; + + if (readl(®s->status) & FTSDC010_STATUS_WRITE_PROT) { + printf("ftsdc010: write protected\n"); + chip->wprot = 1; + } + + chip->fifo = (readl(®s->feature) & 0xff) << 2; + + /* 1. chip reset */ + writel(FTSDC010_CMD_SDC_RST, ®s->cmd); + for (ts = get_timer(0); get_timer(ts) < CFG_RST_TIMEOUT; ) { + if (readl(®s->cmd) & FTSDC010_CMD_SDC_RST) + continue; + break; + } + if (readl(®s->cmd) & FTSDC010_CMD_SDC_RST) { + printf("ftsdc010: reset failed\n"); + return UNUSABLE_ERR; + } + + /* 2. enter low speed mode (400k card detection) */ + ftsdc010_clkset(mmc, 400000); + + /* 3. interrupt disabled */ + writel(0, ®s->int_mask); + + return 0; +} + +static const struct mmc_ops ftsdc010_ops = { + .send_cmd = ftsdc010_request, + .set_ios = ftsdc010_set_ios, + .init = ftsdc010_init, +}; + +int ftsdc010_mmc_init(int devid) +{ + struct mmc *mmc; + struct ftsdc010_chip *chip; + struct ftsdc010_mmc __iomem *regs; +#ifdef CONFIG_FTSDC010_BASE_LIST + uint32_t base_list[] = CONFIG_FTSDC010_BASE_LIST; + + if (devid < 0 || devid >= ARRAY_SIZE(base_list)) + return -1; + regs = (void __iomem *)base_list[devid]; +#else + regs = (void __iomem *)(CONFIG_FTSDC010_BASE + (devid << 20)); +#endif + + chip = malloc(sizeof(struct ftsdc010_chip)); + if (!chip) + return -ENOMEM; + memset(chip, 0, sizeof(struct ftsdc010_chip)); + + chip->regs = regs; +#ifdef CONFIG_SYS_CLK_FREQ + chip->sclk = CONFIG_SYS_CLK_FREQ; +#else + chip->sclk = clk_get_rate("SDC"); +#endif + + chip->cfg.name = "ftsdc010"; + chip->cfg.ops = &ftsdc010_ops; + chip->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz; + switch (readl(®s->bwr) & FTSDC010_BWR_CAPS_MASK) { + case FTSDC010_BWR_CAPS_4BIT: + chip->cfg.host_caps |= MMC_MODE_4BIT; + break; + case FTSDC010_BWR_CAPS_8BIT: + chip->cfg.host_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; + break; + default: + break; + } + + chip->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + chip->cfg.f_max = chip->sclk / 2; + chip->cfg.f_min = chip->sclk / 0x100; + + chip->cfg.part_type = PART_TYPE_DOS; + chip->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + + mmc = mmc_create(&chip->cfg, chip); + if (mmc == NULL) { + free(chip); + return -ENOMEM; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/ftsdc021_sdhci.c b/qemu/roms/u-boot/drivers/mmc/ftsdc021_sdhci.c new file mode 100644 index 000000000..1f6cdba17 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/ftsdc021_sdhci.c @@ -0,0 +1,33 @@ +/* + * (C) Copyright 2013 Faraday Technology + * Kuo-Jung Su + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#ifndef CONFIG_FTSDC021_CLOCK +#define CONFIG_FTSDC021_CLOCK clk_get_rate("MMC") +#endif + +int ftsdc021_sdhci_init(u32 regbase) +{ + struct sdhci_host *host = NULL; + uint32_t freq = CONFIG_FTSDC021_CLOCK; + + host = calloc(1, sizeof(struct sdhci_host)); + if (!host) { + puts("sdh_host malloc fail!\n"); + return 1; + } + + host->name = "FTSDC021"; + host->ioaddr = (void __iomem *)regbase; + host->quirks = 0; + add_sdhci(host, freq, 0); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/gen_atmel_mci.c b/qemu/roms/u-boot/drivers/mmc/gen_atmel_mci.c new file mode 100644 index 000000000..acca0269e --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/gen_atmel_mci.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2010 + * Rob Emanuele + * Reinhard Meyer, EMK Elektronik + * + * Original Driver: + * Copyright (C) 2004-2006 Atmel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "atmel_mci.h" + +#ifndef CONFIG_SYS_MMC_CLK_OD +# define CONFIG_SYS_MMC_CLK_OD 150000 +#endif + +#define MMC_DEFAULT_BLKLEN 512 + +#if defined(CONFIG_ATMEL_MCI_PORTB) +# define MCI_BUS 1 +#else +# define MCI_BUS 0 +#endif + +static int initialized = 0; + +/* Read Atmel MCI IP version */ +static unsigned int atmel_mci_get_version(struct atmel_mci *mci) +{ + return readl(&mci->version) & 0x00000fff; +} + +/* + * Print command and status: + * + * - always when DEBUG is defined + * - on command errors + */ +static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg) +{ + printf("gen_atmel_mci: CMDR %08x (%2u) ARGR %08x (SR: %08x) %s\n", + cmdr, cmdr&0x3F, arg, status, msg); +} + +/* Setup for MCI Clock and Block Size */ +static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) +{ + atmel_mci_t *mci = mmc->priv; + u32 bus_hz = get_mci_clk_rate(); + u32 clkdiv = 255; + + debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n", + bus_hz, hz, blklen); + if (hz > 0) { + /* find lowest clkdiv yielding a rate <= than requested */ + for (clkdiv=0; clkdiv<255; clkdiv++) { + if ((bus_hz / (clkdiv+1) / 2) <= hz) + break; + } + } + printf("mci: setting clock %u Hz, block size %u\n", + (bus_hz / (clkdiv+1)) / 2, blklen); + + blklen &= 0xfffc; + /* On some platforms RDPROOF and WRPROOF are ignored */ + writel((MMCI_BF(CLKDIV, clkdiv) + | MMCI_BF(BLKLEN, blklen) + | MMCI_BIT(RDPROOF) + | MMCI_BIT(WRPROOF)), &mci->mr); + /* + * On some new platforms BLKLEN in mci->mr is ignored. + * Should use the BLKLEN in the block register. + */ + writel(MMCI_BF(BLKLEN, blklen), &mci->blkr); + initialized = 1; +} + +/* Return the CMDR with flags for a given command and data packet */ +static u32 mci_encode_cmd( + struct mmc_cmd *cmd, struct mmc_data *data, u32* error_flags) +{ + u32 cmdr = 0; + + /* Default Flags for Errors */ + *error_flags |= (MMCI_BIT(DTOE) | MMCI_BIT(RDIRE) | MMCI_BIT(RENDE) | + MMCI_BIT(RINDE) | MMCI_BIT(RTOE)); + + /* Default Flags for the Command */ + cmdr |= MMCI_BIT(MAXLAT); + + if (data) { + cmdr |= MMCI_BF(TRCMD, 1); + if (data->blocks > 1) + cmdr |= MMCI_BF(TRTYP, 1); + if (data->flags & MMC_DATA_READ) + cmdr |= MMCI_BIT(TRDIR); + } + + if (cmd->resp_type & MMC_RSP_CRC) + *error_flags |= MMCI_BIT(RCRCE); + if (cmd->resp_type & MMC_RSP_136) + cmdr |= MMCI_BF(RSPTYP, 2); + else if (cmd->resp_type & MMC_RSP_BUSY) + cmdr |= MMCI_BF(RSPTYP, 3); + else if (cmd->resp_type & MMC_RSP_PRESENT) + cmdr |= MMCI_BF(RSPTYP, 1); + + return cmdr | MMCI_BF(CMDNB, cmd->cmdidx); +} + +/* Entered into function pointer in mci_send_cmd */ +static u32 mci_data_read(atmel_mci_t *mci, u32* data, u32 error_flags) +{ + u32 status; + + do { + status = readl(&mci->sr); + if (status & (error_flags | MMCI_BIT(OVRE))) + goto io_fail; + } while (!(status & MMCI_BIT(RXRDY))); + + if (status & MMCI_BIT(RXRDY)) { + *data = readl(&mci->rdr); + status = 0; + } +io_fail: + return status; +} + +/* Entered into function pointer in mci_send_cmd */ +static u32 mci_data_write(atmel_mci_t *mci, u32* data, u32 error_flags) +{ + u32 status; + + do { + status = readl(&mci->sr); + if (status & (error_flags | MMCI_BIT(UNRE))) + goto io_fail; + } while (!(status & MMCI_BIT(TXRDY))); + + if (status & MMCI_BIT(TXRDY)) { + writel(*data, &mci->tdr); + status = 0; + } +io_fail: + return status; +} + +/* + * Entered into mmc structure during driver init + * + * Sends a command out on the bus and deals with the block data. + * Takes the mmc pointer, a command pointer, and an optional data pointer. + */ +static int +mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + atmel_mci_t *mci = mmc->priv; + u32 cmdr; + u32 error_flags = 0; + u32 status; + + if (!initialized) { + puts ("MCI not initialized!\n"); + return COMM_ERR; + } + + /* Figure out the transfer arguments */ + cmdr = mci_encode_cmd(cmd, data, &error_flags); + + /* For multi blocks read/write, set the block register */ + if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) + || (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) + writel(data->blocks | MMCI_BF(BLKLEN, mmc->read_bl_len), + &mci->blkr); + + /* Send the command */ + writel(cmd->cmdarg, &mci->argr); + writel(cmdr, &mci->cmdr); + +#ifdef DEBUG + dump_cmd(cmdr, cmd->cmdarg, 0, "DEBUG"); +#endif + + /* Wait for the command to complete */ + while (!((status = readl(&mci->sr)) & MMCI_BIT(CMDRDY))); + + if ((status & error_flags) & MMCI_BIT(RTOE)) { + dump_cmd(cmdr, cmd->cmdarg, status, "Command Time Out"); + return TIMEOUT; + } else if (status & error_flags) { + dump_cmd(cmdr, cmd->cmdarg, status, "Command Failed"); + return COMM_ERR; + } + + /* Copy the response to the response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[0] = readl(&mci->rspr); + cmd->response[1] = readl(&mci->rspr1); + cmd->response[2] = readl(&mci->rspr2); + cmd->response[3] = readl(&mci->rspr3); + } else + cmd->response[0] = readl(&mci->rspr); + + /* transfer all of the blocks */ + if (data) { + u32 word_count, block_count; + u32* ioptr; + u32 sys_blocksize, dummy, i; + u32 (*mci_data_op) + (atmel_mci_t *mci, u32* data, u32 error_flags); + + if (data->flags & MMC_DATA_READ) { + mci_data_op = mci_data_read; + sys_blocksize = mmc->read_bl_len; + ioptr = (u32*)data->dest; + } else { + mci_data_op = mci_data_write; + sys_blocksize = mmc->write_bl_len; + ioptr = (u32*)data->src; + } + + status = 0; + for (block_count = 0; + block_count < data->blocks && !status; + block_count++) { + word_count = 0; + do { + status = mci_data_op(mci, ioptr, error_flags); + word_count++; + ioptr++; + } while (!status && word_count < (data->blocksize/4)); +#ifdef DEBUG + if (data->flags & MMC_DATA_READ) + { + printf("Read Data:\n"); + print_buffer(0, data->dest, 1, + word_count*4, 0); + } +#endif +#ifdef DEBUG + if (!status && word_count < (sys_blocksize / 4)) + printf("filling rest of block...\n"); +#endif + /* fill the rest of a full block */ + while (!status && word_count < (sys_blocksize / 4)) { + status = mci_data_op(mci, &dummy, + error_flags); + word_count++; + } + if (status) { + dump_cmd(cmdr, cmd->cmdarg, status, + "Data Transfer Failed"); + return COMM_ERR; + } + } + + /* Wait for Transfer End */ + i = 0; + do { + status = readl(&mci->sr); + + if (status & error_flags) { + dump_cmd(cmdr, cmd->cmdarg, status, + "DTIP Wait Failed"); + return COMM_ERR; + } + i++; + } while ((status & MMCI_BIT(DTIP)) && i < 10000); + if (status & MMCI_BIT(DTIP)) { + dump_cmd(cmdr, cmd->cmdarg, status, + "XFER DTIP never unset, ignoring"); + } + } + + return 0; +} + +/* Entered into mmc structure during driver init */ +static void mci_set_ios(struct mmc *mmc) +{ + atmel_mci_t *mci = mmc->priv; + int bus_width = mmc->bus_width; + unsigned int version = atmel_mci_get_version(mci); + int busw; + + /* Set the clock speed */ + mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN); + + /* + * set the bus width and select slot for this interface + * there is no capability for multiple slots on the same interface yet + */ + if ((version & 0xf00) >= 0x300) { + switch (bus_width) { + case 8: + busw = 3; + break; + case 4: + busw = 2; + break; + default: + busw = 0; + break; + } + + writel(busw << 6 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); + } else { + busw = (bus_width == 4) ? 1 : 0; + + writel(busw << 7 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); + } +} + +/* Entered into mmc structure during driver init */ +static int mci_init(struct mmc *mmc) +{ + atmel_mci_t *mci = mmc->priv; + + /* Initialize controller */ + writel(MMCI_BIT(SWRST), &mci->cr); /* soft reset */ + writel(MMCI_BIT(PWSDIS), &mci->cr); /* disable power save */ + writel(MMCI_BIT(MCIEN), &mci->cr); /* enable mci */ + writel(MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); /* select port */ + + /* This delay can be optimized, but stick with max value */ + writel(0x7f, &mci->dtor); + /* Disable Interrupts */ + writel(~0UL, &mci->idr); + + /* Set default clocks and blocklen */ + mci_set_mode(mmc, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN); + + return 0; +} + +static const struct mmc_ops atmel_mci_ops = { + .send_cmd = mci_send_cmd, + .set_ios = mci_set_ios, + .init = mci_init, +}; + +/* + * This is the only exported function + * + * Call it with the MCI register base address + */ +int atmel_mci_init(void *regs) +{ + struct mmc *mmc; + struct mmc_config *cfg; + struct atmel_mci *mci; + unsigned int version; + + cfg = malloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + memset(cfg, 0, sizeof(*cfg)); + + mci = (struct atmel_mci *)regs; + + cfg->name = "mci"; + cfg->ops = &atmel_mci_ops; + + /* need to be able to pass these in on a board by board basis */ + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + version = atmel_mci_get_version(mci); + if ((version & 0xf00) >= 0x300) + cfg->host_caps = MMC_MODE_8BIT; + + cfg->host_caps |= MMC_MODE_4BIT; + + /* + * min and max frequencies determined by + * max and min of clock divider + */ + cfg->f_min = get_mci_clk_rate() / (2*256); + cfg->f_max = get_mci_clk_rate() / (2*1); + + cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + + mmc = mmc_create(cfg, regs); + + if (mmc == NULL) { + free(cfg); + return -1; + } + /* NOTE: possibly leaking the cfg structure */ + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/kona_sdhci.c b/qemu/roms/u-boot/drivers/mmc/kona_sdhci.c new file mode 100644 index 000000000..77e42c8af --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/kona_sdhci.c @@ -0,0 +1,134 @@ +/* + * Copyright 2013 Broadcom Corporation. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define SDHCI_CORECTRL_OFFSET 0x00008000 +#define SDHCI_CORECTRL_EN 0x01 +#define SDHCI_CORECTRL_RESET 0x02 + +#define SDHCI_CORESTAT_OFFSET 0x00008004 +#define SDHCI_CORESTAT_CD_SW 0x01 + +#define SDHCI_COREIMR_OFFSET 0x00008008 +#define SDHCI_COREIMR_IP 0x01 + +static int init_kona_mmc_core(struct sdhci_host *host) +{ + unsigned int mask; + unsigned int timeout; + + if (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL) { + printf("%s: sd host controller reset error\n", __func__); + return 1; + } + + /* For kona a hardware reset before anything else. */ + mask = sdhci_readl(host, SDHCI_CORECTRL_OFFSET) | SDHCI_CORECTRL_RESET; + sdhci_writel(host, mask, SDHCI_CORECTRL_OFFSET); + + /* Wait max 100 ms */ + timeout = 1000; + do { + if (timeout == 0) { + printf("%s: reset timeout error\n", __func__); + return 1; + } + timeout--; + udelay(100); + } while (0 == + (sdhci_readl(host, SDHCI_CORECTRL_OFFSET) & + SDHCI_CORECTRL_RESET)); + + /* Clear the reset bit. */ + mask = mask & ~SDHCI_CORECTRL_RESET; + sdhci_writel(host, mask, SDHCI_CORECTRL_OFFSET); + + /* Enable AHB clock */ + mask = sdhci_readl(host, SDHCI_CORECTRL_OFFSET); + sdhci_writel(host, mask | SDHCI_CORECTRL_EN, SDHCI_CORECTRL_OFFSET); + + /* Enable interrupts */ + sdhci_writel(host, SDHCI_COREIMR_IP, SDHCI_COREIMR_OFFSET); + + /* Make sure Card is detected in controller */ + mask = sdhci_readl(host, SDHCI_CORESTAT_OFFSET); + sdhci_writel(host, mask | SDHCI_CORESTAT_CD_SW, SDHCI_CORESTAT_OFFSET); + + /* Wait max 100 ms */ + timeout = 1000; + while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { + if (timeout == 0) { + printf("%s: CARD DETECT timeout error\n", __func__); + return 1; + } + timeout--; + udelay(100); + } + return 0; +} + +int kona_sdhci_init(int dev_index, u32 min_clk, u32 quirks) +{ + int ret = 0; + u32 max_clk; + void *reg_base; + struct sdhci_host *host = NULL; + + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("%s: sdhci host malloc fail!\n", __func__); + return -ENOMEM; + } + switch (dev_index) { + case 0: + reg_base = (void *)CONFIG_SYS_SDIO_BASE0; + ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO0_MAX_CLK, + &max_clk); + break; + case 1: + reg_base = (void *)CONFIG_SYS_SDIO_BASE1; + ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO1_MAX_CLK, + &max_clk); + break; + case 2: + reg_base = (void *)CONFIG_SYS_SDIO_BASE2; + ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO2_MAX_CLK, + &max_clk); + break; + case 3: + reg_base = (void *)CONFIG_SYS_SDIO_BASE3; + ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO3_MAX_CLK, + &max_clk); + break; + default: + printf("%s: sdio dev index %d not supported\n", + __func__, dev_index); + ret = -EINVAL; + } + if (ret) + return ret; + + host->name = "kona-sdhci"; + host->ioaddr = reg_base; + host->quirks = quirks; + host->host_caps = MMC_MODE_HC; + + if (init_kona_mmc_core(host)) + return -EINVAL; + + if (quirks & SDHCI_QUIRK_REG32_RW) + host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; + else + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + add_sdhci(host, max_clk, min_clk); + return ret; +} diff --git a/qemu/roms/u-boot/drivers/mmc/mmc.c b/qemu/roms/u-boot/drivers/mmc/mmc.c new file mode 100644 index 000000000..16051e52f --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/mmc.c @@ -0,0 +1,1528 @@ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmc_private.h" + +static struct list_head mmc_devices; +static int cur_dev_num = -1; + +int __weak board_mmc_getwp(struct mmc *mmc) +{ + return -1; +} + +int mmc_getwp(struct mmc *mmc) +{ + int wp; + + wp = board_mmc_getwp(mmc); + + if (wp < 0) { + if (mmc->cfg->ops->getwp) + wp = mmc->cfg->ops->getwp(mmc); + else + wp = 0; + } + + return wp; +} + +int __board_mmc_getcd(struct mmc *mmc) { + return -1; +} + +int board_mmc_getcd(struct mmc *mmc)__attribute__((weak, + alias("__board_mmc_getcd"))); + +int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + int ret; + +#ifdef CONFIG_MMC_TRACE + int i; + u8 *ptr; + + printf("CMD_SEND:%d\n", cmd->cmdidx); + printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg); + ret = mmc->cfg->ops->send_cmd(mmc, cmd, data); + switch (cmd->resp_type) { + case MMC_RSP_NONE: + printf("\t\tMMC_RSP_NONE\n"); + break; + case MMC_RSP_R1: + printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08X \n", + cmd->response[0]); + break; + case MMC_RSP_R1b: + printf("\t\tMMC_RSP_R1b\t\t 0x%08X \n", + cmd->response[0]); + break; + case MMC_RSP_R2: + printf("\t\tMMC_RSP_R2\t\t 0x%08X \n", + cmd->response[0]); + printf("\t\t \t\t 0x%08X \n", + cmd->response[1]); + printf("\t\t \t\t 0x%08X \n", + cmd->response[2]); + printf("\t\t \t\t 0x%08X \n", + cmd->response[3]); + printf("\n"); + printf("\t\t\t\t\tDUMPING DATA\n"); + for (i = 0; i < 4; i++) { + int j; + printf("\t\t\t\t\t%03d - ", i*4); + ptr = (u8 *)&cmd->response[i]; + ptr += 3; + for (j = 0; j < 4; j++) + printf("%02X ", *ptr--); + printf("\n"); + } + break; + case MMC_RSP_R3: + printf("\t\tMMC_RSP_R3,4\t\t 0x%08X \n", + cmd->response[0]); + break; + default: + printf("\t\tERROR MMC rsp not supported\n"); + break; + } +#else + ret = mmc->cfg->ops->send_cmd(mmc, cmd, data); +#endif + return ret; +} + +int mmc_send_status(struct mmc *mmc, int timeout) +{ + struct mmc_cmd cmd; + int err, retries = 5; +#ifdef CONFIG_MMC_TRACE + int status; +#endif + + cmd.cmdidx = MMC_CMD_SEND_STATUS; + cmd.resp_type = MMC_RSP_R1; + if (!mmc_host_is_spi(mmc)) + cmd.cmdarg = mmc->rca << 16; + + do { + err = mmc_send_cmd(mmc, &cmd, NULL); + if (!err) { + if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) && + (cmd.response[0] & MMC_STATUS_CURR_STATE) != + MMC_STATE_PRG) + break; + else if (cmd.response[0] & MMC_STATUS_MASK) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("Status Error: 0x%08X\n", + cmd.response[0]); +#endif + return COMM_ERR; + } + } else if (--retries < 0) + return err; + + udelay(1000); + + } while (timeout--); + +#ifdef CONFIG_MMC_TRACE + status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9; + printf("CURR STATE:%d\n", status); +#endif + if (timeout <= 0) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("Timeout waiting card ready\n"); +#endif + return TIMEOUT; + } + + return 0; +} + +int mmc_set_blocklen(struct mmc *mmc, int len) +{ + struct mmc_cmd cmd; + + cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = len; + + return mmc_send_cmd(mmc, &cmd, NULL); +} + +struct mmc *find_mmc_device(int dev_num) +{ + struct mmc *m; + struct list_head *entry; + + list_for_each(entry, &mmc_devices) { + m = list_entry(entry, struct mmc, link); + + if (m->block_dev.dev == dev_num) + return m; + } + +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("MMC Device %d not found\n", dev_num); +#endif + + return NULL; +} + +static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, + lbaint_t blkcnt) +{ + struct mmc_cmd cmd; + struct mmc_data data; + + if (blkcnt > 1) + cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * mmc->read_bl_len; + + cmd.resp_type = MMC_RSP_R1; + + data.dest = dst; + data.blocks = blkcnt; + data.blocksize = mmc->read_bl_len; + data.flags = MMC_DATA_READ; + + if (mmc_send_cmd(mmc, &cmd, &data)) + return 0; + + if (blkcnt > 1) { + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + if (mmc_send_cmd(mmc, &cmd, NULL)) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("mmc fail to send stop cmd\n"); +#endif + return 0; + } + } + + return blkcnt; +} + +static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst) +{ + lbaint_t cur, blocks_todo = blkcnt; + + if (blkcnt == 0) + return 0; + + struct mmc *mmc = find_mmc_device(dev_num); + if (!mmc) + return 0; + + if ((start + blkcnt) > mmc->block_dev.lba) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", + start + blkcnt, mmc->block_dev.lba); +#endif + return 0; + } + + if (mmc_set_blocklen(mmc, mmc->read_bl_len)) + return 0; + + do { + cur = (blocks_todo > mmc->cfg->b_max) ? + mmc->cfg->b_max : blocks_todo; + if(mmc_read_blocks(mmc, dst, start, cur) != cur) + return 0; + blocks_todo -= cur; + start += cur; + dst += cur * mmc->read_bl_len; + } while (blocks_todo > 0); + + return blkcnt; +} + +static int mmc_go_idle(struct mmc *mmc) +{ + struct mmc_cmd cmd; + int err; + + udelay(1000); + + cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_NONE; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(2000); + + return 0; +} + +static int sd_send_op_cond(struct mmc *mmc) +{ + int timeout = 1000; + int err; + struct mmc_cmd cmd; + + do { + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SEND_OP_COND; + cmd.resp_type = MMC_RSP_R3; + + /* + * Most cards do not answer if some reserved bits + * in the ocr are set. However, Some controller + * can set bit 7 (reserved for low voltages), but + * how to manage low voltages SD card is not yet + * specified. + */ + cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 : + (mmc->cfg->voltages & 0xff8000); + + if (mmc->version == SD_VERSION_2) + cmd.cmdarg |= OCR_HCS; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(1000); + } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--); + + if (timeout <= 0) + return UNUSABLE_ERR; + + if (mmc->version != SD_VERSION_2) + mmc->version = SD_VERSION_1_0; + + if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ + cmd.cmdidx = MMC_CMD_SPI_READ_OCR; + cmd.resp_type = MMC_RSP_R3; + cmd.cmdarg = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } + + mmc->ocr = cmd.response[0]; + + mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); + mmc->rca = 0; + + return 0; +} + +/* We pass in the cmd since otherwise the init seems to fail */ +static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd, + int use_arg) +{ + int err; + + cmd->cmdidx = MMC_CMD_SEND_OP_COND; + cmd->resp_type = MMC_RSP_R3; + cmd->cmdarg = 0; + if (use_arg && !mmc_host_is_spi(mmc)) { + cmd->cmdarg = + (mmc->cfg->voltages & + (mmc->op_cond_response & OCR_VOLTAGE_MASK)) | + (mmc->op_cond_response & OCR_ACCESS_MODE); + + if (mmc->cfg->host_caps & MMC_MODE_HC) + cmd->cmdarg |= OCR_HCS; + } + err = mmc_send_cmd(mmc, cmd, NULL); + if (err) + return err; + mmc->op_cond_response = cmd->response[0]; + return 0; +} + +int mmc_send_op_cond(struct mmc *mmc) +{ + struct mmc_cmd cmd; + int err, i; + + /* Some cards seem to need this */ + mmc_go_idle(mmc); + + /* Asking to the card its capabilities */ + mmc->op_cond_pending = 1; + for (i = 0; i < 2; i++) { + err = mmc_send_op_cond_iter(mmc, &cmd, i != 0); + if (err) + return err; + + /* exit if not busy (flag seems to be inverted) */ + if (mmc->op_cond_response & OCR_BUSY) + return 0; + } + return IN_PROGRESS; +} + +int mmc_complete_op_cond(struct mmc *mmc) +{ + struct mmc_cmd cmd; + int timeout = 1000; + uint start; + int err; + + mmc->op_cond_pending = 0; + start = get_timer(0); + do { + err = mmc_send_op_cond_iter(mmc, &cmd, 1); + if (err) + return err; + if (get_timer(start) > timeout) + return UNUSABLE_ERR; + udelay(100); + } while (!(mmc->op_cond_response & OCR_BUSY)); + + if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ + cmd.cmdidx = MMC_CMD_SPI_READ_OCR; + cmd.resp_type = MMC_RSP_R3; + cmd.cmdarg = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } + + mmc->version = MMC_VERSION_UNKNOWN; + mmc->ocr = cmd.response[0]; + + mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); + mmc->rca = 1; + + return 0; +} + + +static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int err; + + /* Get the Card Status Register */ + cmd.cmdidx = MMC_CMD_SEND_EXT_CSD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + + data.dest = (char *)ext_csd; + data.blocks = 1; + data.blocksize = MMC_MAX_BLOCK_LEN; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + + return err; +} + + +static int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) +{ + struct mmc_cmd cmd; + int timeout = 1000; + int ret; + + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (index << 16) | + (value << 8); + + ret = mmc_send_cmd(mmc, &cmd, NULL); + + /* Waiting for the ready status */ + if (!ret) + ret = mmc_send_status(mmc, timeout); + + return ret; + +} + +static int mmc_change_freq(struct mmc *mmc) +{ + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); + char cardtype; + int err; + + mmc->card_caps = 0; + + if (mmc_host_is_spi(mmc)) + return 0; + + /* Only version 4 supports high-speed */ + if (mmc->version < MMC_VERSION_4) + return 0; + + err = mmc_send_ext_csd(mmc, ext_csd); + + if (err) + return err; + + cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); + + if (err) + return err; + + /* Now check to see that it worked */ + err = mmc_send_ext_csd(mmc, ext_csd); + + if (err) + return err; + + /* No high-speed support */ + if (!ext_csd[EXT_CSD_HS_TIMING]) + return 0; + + /* High Speed is set, there are two types: 52MHz and 26MHz */ + if (cardtype & MMC_HS_52MHZ) + mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + else + mmc->card_caps |= MMC_MODE_HS; + + return 0; +} + +static int mmc_set_capacity(struct mmc *mmc, int part_num) +{ + switch (part_num) { + case 0: + mmc->capacity = mmc->capacity_user; + break; + case 1: + case 2: + mmc->capacity = mmc->capacity_boot; + break; + case 3: + mmc->capacity = mmc->capacity_rpmb; + break; + case 4: + case 5: + case 6: + case 7: + mmc->capacity = mmc->capacity_gp[part_num - 4]; + break; + default: + return -1; + } + + mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); + + return 0; +} + +int mmc_switch_part(int dev_num, unsigned int part_num) +{ + struct mmc *mmc = find_mmc_device(dev_num); + int ret; + + if (!mmc) + return -1; + + ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, + (mmc->part_config & ~PART_ACCESS_MASK) + | (part_num & PART_ACCESS_MASK)); + if (ret) + return ret; + + return mmc_set_capacity(mmc, part_num); +} + +int mmc_getcd(struct mmc *mmc) +{ + int cd; + + cd = board_mmc_getcd(mmc); + + if (cd < 0) { + if (mmc->cfg->ops->getcd) + cd = mmc->cfg->ops->getcd(mmc); + else + cd = 1; + } + + return cd; +} + +static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) +{ + struct mmc_cmd cmd; + struct mmc_data data; + + /* Switch the frequency */ + cmd.cmdidx = SD_CMD_SWITCH_FUNC; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = (mode << 31) | 0xffffff; + cmd.cmdarg &= ~(0xf << (group * 4)); + cmd.cmdarg |= value << (group * 4); + + data.dest = (char *)resp; + data.blocksize = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + return mmc_send_cmd(mmc, &cmd, &data); +} + + +static int sd_change_freq(struct mmc *mmc) +{ + int err; + struct mmc_cmd cmd; + ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2); + ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); + struct mmc_data data; + int timeout; + + mmc->card_caps = 0; + + if (mmc_host_is_spi(mmc)) + return 0; + + /* Read the SCR to find out if this card supports higher speeds */ + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SEND_SCR; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + + timeout = 3; + +retry_scr: + data.dest = (char *)scr; + data.blocksize = 8; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + + if (err) { + if (timeout--) + goto retry_scr; + + return err; + } + + mmc->scr[0] = __be32_to_cpu(scr[0]); + mmc->scr[1] = __be32_to_cpu(scr[1]); + + switch ((mmc->scr[0] >> 24) & 0xf) { + case 0: + mmc->version = SD_VERSION_1_0; + break; + case 1: + mmc->version = SD_VERSION_1_10; + break; + case 2: + mmc->version = SD_VERSION_2; + if ((mmc->scr[0] >> 15) & 0x1) + mmc->version = SD_VERSION_3; + break; + default: + mmc->version = SD_VERSION_1_0; + break; + } + + if (mmc->scr[0] & SD_DATA_4BIT) + mmc->card_caps |= MMC_MODE_4BIT; + + /* Version 1.0 doesn't support switching */ + if (mmc->version == SD_VERSION_1_0) + return 0; + + timeout = 4; + while (timeout--) { + err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1, + (u8 *)switch_status); + + if (err) + return err; + + /* The high-speed function is busy. Try again */ + if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY)) + break; + } + + /* If high-speed isn't supported, we return */ + if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) + return 0; + + /* + * If the host doesn't support SD_HIGHSPEED, do not switch card to + * HIGHSPEED mode even if the card support SD_HIGHSPPED. + * This can avoid furthur problem when the card runs in different + * mode between the host. + */ + if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) && + (mmc->cfg->host_caps & MMC_MODE_HS))) + return 0; + + err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); + + if (err) + return err; + + if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000) + mmc->card_caps |= MMC_MODE_HS; + + return 0; +} + +/* frequency bases */ +/* divided by 10 to be nice to platforms without floating point */ +static const int fbase[] = { + 10000, + 100000, + 1000000, + 10000000, +}; + +/* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice + * to platforms without floating point. + */ +static const int multipliers[] = { + 0, /* reserved */ + 10, + 12, + 13, + 15, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 55, + 60, + 70, + 80, +}; + +static void mmc_set_ios(struct mmc *mmc) +{ + if (mmc->cfg->ops->set_ios) + mmc->cfg->ops->set_ios(mmc); +} + +void mmc_set_clock(struct mmc *mmc, uint clock) +{ + if (clock > mmc->cfg->f_max) + clock = mmc->cfg->f_max; + + if (clock < mmc->cfg->f_min) + clock = mmc->cfg->f_min; + + mmc->clock = clock; + + mmc_set_ios(mmc); +} + +static void mmc_set_bus_width(struct mmc *mmc, uint width) +{ + mmc->bus_width = width; + + mmc_set_ios(mmc); +} + +static int mmc_startup(struct mmc *mmc) +{ + int err, i; + uint mult, freq; + u64 cmult, csize, capacity; + struct mmc_cmd cmd; + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); + ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); + int timeout = 1000; + +#ifdef CONFIG_MMC_SPI_CRC_ON + if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */ + cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 1; + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } +#endif + + /* Put the Card in Identify Mode */ + cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID : + MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */ + cmd.resp_type = MMC_RSP_R2; + cmd.cmdarg = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + memcpy(mmc->cid, cmd.response, 16); + + /* + * For MMC cards, set the Relative Address. + * For SD cards, get the Relatvie Address. + * This also puts the cards into Standby State + */ + if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ + cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; + cmd.cmdarg = mmc->rca << 16; + cmd.resp_type = MMC_RSP_R6; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + if (IS_SD(mmc)) + mmc->rca = (cmd.response[0] >> 16) & 0xffff; + } + + /* Get the Card-Specific Data */ + cmd.cmdidx = MMC_CMD_SEND_CSD; + cmd.resp_type = MMC_RSP_R2; + cmd.cmdarg = mmc->rca << 16; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); + + if (err) + return err; + + mmc->csd[0] = cmd.response[0]; + mmc->csd[1] = cmd.response[1]; + mmc->csd[2] = cmd.response[2]; + mmc->csd[3] = cmd.response[3]; + + if (mmc->version == MMC_VERSION_UNKNOWN) { + int version = (cmd.response[0] >> 26) & 0xf; + + switch (version) { + case 0: + mmc->version = MMC_VERSION_1_2; + break; + case 1: + mmc->version = MMC_VERSION_1_4; + break; + case 2: + mmc->version = MMC_VERSION_2_2; + break; + case 3: + mmc->version = MMC_VERSION_3; + break; + case 4: + mmc->version = MMC_VERSION_4; + break; + default: + mmc->version = MMC_VERSION_1_2; + break; + } + } + + /* divide frequency by 10, since the mults are 10x bigger */ + freq = fbase[(cmd.response[0] & 0x7)]; + mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; + + mmc->tran_speed = freq * mult; + + mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); + mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); + + if (IS_SD(mmc)) + mmc->write_bl_len = mmc->read_bl_len; + else + mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf); + + if (mmc->high_capacity) { + csize = (mmc->csd[1] & 0x3f) << 16 + | (mmc->csd[2] & 0xffff0000) >> 16; + cmult = 8; + } else { + csize = (mmc->csd[1] & 0x3ff) << 2 + | (mmc->csd[2] & 0xc0000000) >> 30; + cmult = (mmc->csd[2] & 0x00038000) >> 15; + } + + mmc->capacity_user = (csize + 1) << (cmult + 2); + mmc->capacity_user *= mmc->read_bl_len; + mmc->capacity_boot = 0; + mmc->capacity_rpmb = 0; + for (i = 0; i < 4; i++) + mmc->capacity_gp[i] = 0; + + if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN) + mmc->read_bl_len = MMC_MAX_BLOCK_LEN; + + if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN) + mmc->write_bl_len = MMC_MAX_BLOCK_LEN; + + if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) { + cmd.cmdidx = MMC_CMD_SET_DSR; + cmd.cmdarg = (mmc->dsr & 0xffff) << 16; + cmd.resp_type = MMC_RSP_NONE; + if (mmc_send_cmd(mmc, &cmd, NULL)) + printf("MMC: SET_DSR failed\n"); + } + + /* Select the card, and put it into Transfer Mode */ + if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ + cmd.cmdidx = MMC_CMD_SELECT_CARD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } + + /* + * For SD, its erase group is always one sector + */ + mmc->erase_grp_size = 1; + mmc->part_config = MMCPART_NOAVAILABLE; + if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { + /* check ext_csd version and capacity */ + err = mmc_send_ext_csd(mmc, ext_csd); + if (!err && (ext_csd[EXT_CSD_REV] >= 2)) { + /* + * According to the JEDEC Standard, the value of + * ext_csd's capacity is valid if the value is more + * than 2GB + */ + capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 + | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 + | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 + | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; + capacity *= MMC_MAX_BLOCK_LEN; + if ((capacity >> 20) > 2 * 1024) + mmc->capacity_user = capacity; + } + + switch (ext_csd[EXT_CSD_REV]) { + case 1: + mmc->version = MMC_VERSION_4_1; + break; + case 2: + mmc->version = MMC_VERSION_4_2; + break; + case 3: + mmc->version = MMC_VERSION_4_3; + break; + case 5: + mmc->version = MMC_VERSION_4_41; + break; + case 6: + mmc->version = MMC_VERSION_4_5; + break; + } + + /* + * Host needs to enable ERASE_GRP_DEF bit if device is + * partitioned. This bit will be lost every time after a reset + * or power off. This will affect erase size. + */ + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && + (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GROUP_DEF, 1); + + if (err) + return err; + + /* Read out group size from ext_csd */ + mmc->erase_grp_size = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * + MMC_MAX_BLOCK_LEN * 1024; + } else { + /* Calculate the group size from the csd value. */ + int erase_gsz, erase_gmul; + erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; + erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; + mmc->erase_grp_size = (erase_gsz + 1) + * (erase_gmul + 1); + } + + /* store the partition info of emmc */ + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || + ext_csd[EXT_CSD_BOOT_MULT]) + mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; + + mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; + + mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; + + for (i = 0; i < 4; i++) { + int idx = EXT_CSD_GP_SIZE_MULT + i * 3; + mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) + + (ext_csd[idx + 1] << 8) + ext_csd[idx]; + mmc->capacity_gp[i] *= + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + } + } + + err = mmc_set_capacity(mmc, mmc->part_num); + if (err) + return err; + + if (IS_SD(mmc)) + err = sd_change_freq(mmc); + else + err = mmc_change_freq(mmc); + + if (err) + return err; + + /* Restrict card's capabilities by what the host can do */ + mmc->card_caps &= mmc->cfg->host_caps; + + if (IS_SD(mmc)) { + if (mmc->card_caps & MMC_MODE_4BIT) { + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 2; + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + mmc_set_bus_width(mmc, 4); + } + + if (mmc->card_caps & MMC_MODE_HS) + mmc->tran_speed = 50000000; + else + mmc->tran_speed = 25000000; + } else { + int idx; + + /* An array of possible bus widths in order of preference */ + static unsigned ext_csd_bits[] = { + EXT_CSD_BUS_WIDTH_8, + EXT_CSD_BUS_WIDTH_4, + EXT_CSD_BUS_WIDTH_1, + }; + + /* An array to map CSD bus widths to host cap bits */ + static unsigned ext_to_hostcaps[] = { + [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, + [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, + }; + + /* An array to map chosen bus width to an integer */ + static unsigned widths[] = { + 8, 4, 1, + }; + + for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { + unsigned int extw = ext_csd_bits[idx]; + + /* + * Check to make sure the controller supports + * this bus width, if it's more than 1 + */ + if (extw != EXT_CSD_BUS_WIDTH_1 && + !(mmc->cfg->host_caps & ext_to_hostcaps[extw])) + continue; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, extw); + + if (err) + continue; + + mmc_set_bus_width(mmc, widths[idx]); + + err = mmc_send_ext_csd(mmc, test_csd); + if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \ + == test_csd[EXT_CSD_PARTITIONING_SUPPORT] + && ext_csd[EXT_CSD_ERASE_GROUP_DEF] \ + == test_csd[EXT_CSD_ERASE_GROUP_DEF] \ + && ext_csd[EXT_CSD_REV] \ + == test_csd[EXT_CSD_REV] + && ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] \ + == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \ + &test_csd[EXT_CSD_SEC_CNT], 4) == 0) { + + mmc->card_caps |= ext_to_hostcaps[extw]; + break; + } + } + + if (mmc->card_caps & MMC_MODE_HS) { + if (mmc->card_caps & MMC_MODE_HS_52MHz) + mmc->tran_speed = 52000000; + else + mmc->tran_speed = 26000000; + } + } + + mmc_set_clock(mmc, mmc->tran_speed); + + /* fill in device description */ + mmc->block_dev.lun = 0; + mmc->block_dev.type = 0; + mmc->block_dev.blksz = mmc->read_bl_len; + mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz); + mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x", + mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff), + (mmc->cid[3] >> 16) & 0xffff); + sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff, + (mmc->cid[2] >> 24) & 0xff); + sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf, + (mmc->cid[2] >> 16) & 0xf); +#else + mmc->block_dev.vendor[0] = 0; + mmc->block_dev.product[0] = 0; + mmc->block_dev.revision[0] = 0; +#endif +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT) + init_part(&mmc->block_dev); +#endif + + return 0; +} + +static int mmc_send_if_cond(struct mmc *mmc) +{ + struct mmc_cmd cmd; + int err; + + cmd.cmdidx = SD_CMD_SEND_IF_COND; + /* We set the bit if the host supports voltages between 2.7 and 3.6 V */ + cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa; + cmd.resp_type = MMC_RSP_R7; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + if ((cmd.response[0] & 0xff) != 0xaa) + return UNUSABLE_ERR; + else + mmc->version = SD_VERSION_2; + + return 0; +} + +/* not used any more */ +int __deprecated mmc_register(struct mmc *mmc) +{ +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("%s is deprecated! use mmc_create() instead.\n", __func__); +#endif + return -1; +} + +struct mmc *mmc_create(const struct mmc_config *cfg, void *priv) +{ + struct mmc *mmc; + + /* quick validation */ + if (cfg == NULL || cfg->ops == NULL || cfg->ops->send_cmd == NULL || + cfg->f_min == 0 || cfg->f_max == 0 || cfg->b_max == 0) + return NULL; + + mmc = calloc(1, sizeof(*mmc)); + if (mmc == NULL) + return NULL; + + mmc->cfg = cfg; + mmc->priv = priv; + + /* the following chunk was mmc_register() */ + + /* Setup dsr related values */ + mmc->dsr_imp = 0; + mmc->dsr = 0xffffffff; + /* Setup the universal parts of the block interface just once */ + mmc->block_dev.if_type = IF_TYPE_MMC; + mmc->block_dev.dev = cur_dev_num++; + mmc->block_dev.removable = 1; + mmc->block_dev.block_read = mmc_bread; + mmc->block_dev.block_write = mmc_bwrite; + mmc->block_dev.block_erase = mmc_berase; + + /* setup initial part type */ + mmc->block_dev.part_type = mmc->cfg->part_type; + + INIT_LIST_HEAD(&mmc->link); + + list_add_tail(&mmc->link, &mmc_devices); + + return mmc; +} + +void mmc_destroy(struct mmc *mmc) +{ + /* only freeing memory for now */ + free(mmc); +} + +#ifdef CONFIG_PARTITIONS +block_dev_desc_t *mmc_get_dev(int dev) +{ + struct mmc *mmc = find_mmc_device(dev); + if (!mmc || mmc_init(mmc)) + return NULL; + + return &mmc->block_dev; +} +#endif + +int mmc_start_init(struct mmc *mmc) +{ + int err; + + /* we pretend there's no card when init is NULL */ + if (mmc_getcd(mmc) == 0 || mmc->cfg->ops->init == NULL) { + mmc->has_init = 0; +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("MMC: no card present\n"); +#endif + return NO_CARD_ERR; + } + + if (mmc->has_init) + return 0; + + /* made sure it's not NULL earlier */ + err = mmc->cfg->ops->init(mmc); + + if (err) + return err; + + mmc_set_bus_width(mmc, 1); + mmc_set_clock(mmc, 1); + + /* Reset the Card */ + err = mmc_go_idle(mmc); + + if (err) + return err; + + /* The internal partition reset to user partition(0) at every CMD0*/ + mmc->part_num = 0; + + /* Test for SD version 2 */ + err = mmc_send_if_cond(mmc); + + /* Now try to get the SD card's operating condition */ + err = sd_send_op_cond(mmc); + + /* If the command timed out, we check for an MMC card */ + if (err == TIMEOUT) { + err = mmc_send_op_cond(mmc); + + if (err && err != IN_PROGRESS) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("Card did not respond to voltage select!\n"); +#endif + return UNUSABLE_ERR; + } + } + + if (err == IN_PROGRESS) + mmc->init_in_progress = 1; + + return err; +} + +static int mmc_complete_init(struct mmc *mmc) +{ + int err = 0; + + if (mmc->op_cond_pending) + err = mmc_complete_op_cond(mmc); + + if (!err) + err = mmc_startup(mmc); + if (err) + mmc->has_init = 0; + else + mmc->has_init = 1; + mmc->init_in_progress = 0; + return err; +} + +int mmc_init(struct mmc *mmc) +{ + int err = IN_PROGRESS; + unsigned start = get_timer(0); + + if (mmc->has_init) + return 0; + if (!mmc->init_in_progress) + err = mmc_start_init(mmc); + + if (!err || err == IN_PROGRESS) + err = mmc_complete_init(mmc); + debug("%s: %d, time %lu\n", __func__, err, get_timer(start)); + return err; +} + +int mmc_set_dsr(struct mmc *mmc, u16 val) +{ + mmc->dsr = val; + return 0; +} + +/* + * CPU and board-specific MMC initializations. Aliased function + * signals caller to move on + */ +static int __def_mmc_init(bd_t *bis) +{ + return -1; +} + +int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); +int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); + +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + +void print_mmc_devices(char separator) +{ + struct mmc *m; + struct list_head *entry; + + list_for_each(entry, &mmc_devices) { + m = list_entry(entry, struct mmc, link); + + printf("%s: %d", m->cfg->name, m->block_dev.dev); + + if (entry->next != &mmc_devices) + printf("%c ", separator); + } + + printf("\n"); +} + +#else +void print_mmc_devices(char separator) { } +#endif + +int get_mmc_num(void) +{ + return cur_dev_num; +} + +void mmc_set_preinit(struct mmc *mmc, int preinit) +{ + mmc->preinit = preinit; +} + +static void do_preinit(void) +{ + struct mmc *m; + struct list_head *entry; + + list_for_each(entry, &mmc_devices) { + m = list_entry(entry, struct mmc, link); + + if (m->preinit) + mmc_start_init(m); + } +} + + +int mmc_initialize(bd_t *bis) +{ + INIT_LIST_HEAD (&mmc_devices); + cur_dev_num = 0; + + if (board_mmc_init(bis) < 0) + cpu_mmc_init(bis); + +#ifndef CONFIG_SPL_BUILD + print_mmc_devices(','); +#endif + + do_preinit(); + return 0; +} + +#ifdef CONFIG_SUPPORT_EMMC_BOOT +/* + * This function changes the size of boot partition and the size of rpmb + * partition present on EMMC devices. + * + * Input Parameters: + * struct *mmc: pointer for the mmc device strcuture + * bootsize: size of boot partition + * rpmbsize: size of rpmb partition + * + * Returns 0 on success. + */ + +int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + int err; + struct mmc_cmd cmd; + + /* Only use this command for raw EMMC moviNAND. Enter backdoor mode */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error1 = %d\n", err); + return err; + } + + /* Boot partition changing mode */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG2; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error2 = %d\n", err); + return err; + } + /* boot partition size is multiple of 128KB */ + bootsize = (bootsize * 1024) / 128; + + /* Arg: boot partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = bootsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error3 = %d\n", err); + return err; + } + /* RPMB partition size is multiple of 128KB */ + rpmbsize = (rpmbsize * 1024) / 128; + /* Arg: RPMB partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = rpmbsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error4 = %d\n", err); + return err; + } + return 0; +} + +/* + * Modify EXT_CSD[177] which is BOOT_BUS_WIDTH + * based on the passed in values for BOOT_BUS_WIDTH, RESET_BOOT_BUS_WIDTH + * and BOOT_MODE. + * + * Returns 0 on success. + */ +int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode) +{ + int err; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_BUS_WIDTH, + EXT_CSD_BOOT_BUS_WIDTH_MODE(mode) | + EXT_CSD_BOOT_BUS_WIDTH_RESET(reset) | + EXT_CSD_BOOT_BUS_WIDTH_WIDTH(width)); + + if (err) + return err; + return 0; +} + +/* + * Modify EXT_CSD[179] which is PARTITION_CONFIG (formerly BOOT_CONFIG) + * based on the passed in values for BOOT_ACK, BOOT_PARTITION_ENABLE and + * PARTITION_ACCESS. + * + * Returns 0 on success. + */ +int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access) +{ + int err; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, + EXT_CSD_BOOT_ACK(ack) | + EXT_CSD_BOOT_PART_NUM(part_num) | + EXT_CSD_PARTITION_ACCESS(access)); + + if (err) + return err; + return 0; +} + +/* + * Modify EXT_CSD[162] which is RST_n_FUNCTION based on the given value + * for enable. Note that this is a write-once field for non-zero values. + * + * Returns 0 on success. + */ +int mmc_set_rst_n_function(struct mmc *mmc, u8 enable) +{ + return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_RST_N_FUNCTION, + enable); +} +#endif diff --git a/qemu/roms/u-boot/drivers/mmc/mmc_private.h b/qemu/roms/u-boot/drivers/mmc/mmc_private.h new file mode 100644 index 000000000..16dcf9ff6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/mmc_private.h @@ -0,0 +1,45 @@ +/* + * Copyright 2008,2010 Freescale Semiconductor, Inc + * Andy Fleming + * + * Based (loosely) on the Linux code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _MMC_PRIVATE_H_ +#define _MMC_PRIVATE_H_ + +#include + +extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data); +extern int mmc_send_status(struct mmc *mmc, int timeout); +extern int mmc_set_blocklen(struct mmc *mmc, int len); + +#ifndef CONFIG_SPL_BUILD + +extern unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt); + +extern ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, + const void *src); + +#else /* CONFIG_SPL_BUILD */ + +/* SPL will never write or erase, declare dummies to reduce code size. */ + +static inline unsigned long mmc_berase(int dev_num, lbaint_t start, + lbaint_t blkcnt) +{ + return 0; +} + +static inline ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, + const void *src) +{ + return 0; +} + +#endif /* CONFIG_SPL_BUILD */ + +#endif /* _MMC_PRIVATE_H_ */ diff --git a/qemu/roms/u-boot/drivers/mmc/mmc_spi.c b/qemu/roms/u-boot/drivers/mmc/mmc_spi.c new file mode 100644 index 000000000..5b5b33a4b --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/mmc_spi.c @@ -0,0 +1,291 @@ +/* + * generic mmc spi driver + * + * Copyright (C) 2010 Thomas Chou + * Licensed under the GPL-2 or later. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* MMC/SD in SPI mode reports R1 status always */ +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +/* R1 bit 7 is always zero, reuse this bit for error */ +#define R1_SPI_ERROR (1 << 7) + +/* Response tokens used to ack each block written: */ +#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f) +#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1) +#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1) +#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1) + +/* Read and write blocks start with these tokens and end with crc; + * on error, read tokens act like a subset of R2_SPI_* values. + */ +#define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */ +#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */ +#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */ + +/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */ +#define MMC_SPI_CMD(x) (0x40 | (x & 0x3f)) + +/* bus capability */ +#define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34) +#define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */ + +/* timeout value */ +#define CTOUT 8 +#define RTOUT 3000000 /* 1 sec */ +#define WTOUT 3000000 /* 1 sec */ + +static uint mmc_spi_sendcmd(struct mmc *mmc, ushort cmdidx, u32 cmdarg) +{ + struct spi_slave *spi = mmc->priv; + u8 cmdo[7]; + u8 r1; + int i; + cmdo[0] = 0xff; + cmdo[1] = MMC_SPI_CMD(cmdidx); + cmdo[2] = cmdarg >> 24; + cmdo[3] = cmdarg >> 16; + cmdo[4] = cmdarg >> 8; + cmdo[5] = cmdarg; + cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01; + spi_xfer(spi, sizeof(cmdo) * 8, cmdo, NULL, 0); + for (i = 0; i < CTOUT; i++) { + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (i && (r1 & 0x80) == 0) /* r1 response */ + break; + } + debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1); + return r1; +} + +static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf, + u32 bcnt, u32 bsize) +{ + struct spi_slave *spi = mmc->priv; + u8 *buf = xbuf; + u8 r1; + u16 crc; + int i; + while (bcnt--) { + for (i = 0; i < RTOUT; i++) { + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (r1 != 0xff) /* data token */ + break; + } + debug("%s:tok%d %x\n", __func__, i, r1); + if (r1 == SPI_TOKEN_SINGLE) { + spi_xfer(spi, bsize * 8, NULL, buf, 0); + spi_xfer(spi, 2 * 8, NULL, &crc, 0); +#ifdef CONFIG_MMC_SPI_CRC_ON + if (swab16(cyg_crc16(buf, bsize)) != crc) { + debug("%s: CRC error\n", mmc->cfg->name); + r1 = R1_SPI_COM_CRC; + break; + } +#endif + r1 = 0; + } else { + r1 = R1_SPI_ERROR; + break; + } + buf += bsize; + } + return r1; +} + +static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf, + u32 bcnt, u32 bsize, int multi) +{ + struct spi_slave *spi = mmc->priv; + const u8 *buf = xbuf; + u8 r1; + u16 crc; + u8 tok[2]; + int i; + tok[0] = 0xff; + tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE; + while (bcnt--) { +#ifdef CONFIG_MMC_SPI_CRC_ON + crc = swab16(cyg_crc16((u8 *)buf, bsize)); +#endif + spi_xfer(spi, 2 * 8, tok, NULL, 0); + spi_xfer(spi, bsize * 8, buf, NULL, 0); + spi_xfer(spi, 2 * 8, &crc, NULL, 0); + for (i = 0; i < CTOUT; i++) { + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if ((r1 & 0x10) == 0) /* response token */ + break; + } + debug("%s:tok%d %x\n", __func__, i, r1); + if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) { + for (i = 0; i < WTOUT; i++) { /* wait busy */ + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (i && r1 == 0xff) { + r1 = 0; + break; + } + } + if (i == WTOUT) { + debug("%s:wtout %x\n", __func__, r1); + r1 = R1_SPI_ERROR; + break; + } + } else { + debug("%s: err %x\n", __func__, r1); + r1 = R1_SPI_COM_CRC; + break; + } + buf += bsize; + } + if (multi && bcnt == -1) { /* stop multi write */ + tok[1] = SPI_TOKEN_STOP_TRAN; + spi_xfer(spi, 2 * 8, tok, NULL, 0); + for (i = 0; i < WTOUT; i++) { /* wait busy */ + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (i && r1 == 0xff) { + r1 = 0; + break; + } + } + if (i == WTOUT) { + debug("%s:wstop %x\n", __func__, r1); + r1 = R1_SPI_ERROR; + } + } + return r1; +} + +static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct spi_slave *spi = mmc->priv; + u8 r1; + int i; + int ret = 0; + debug("%s:cmd%d %x %x\n", __func__, + cmd->cmdidx, cmd->resp_type, cmd->cmdarg); + spi_claim_bus(spi); + spi_cs_activate(spi); + r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg); + if (r1 == 0xff) { /* no response */ + ret = NO_CARD_ERR; + goto done; + } else if (r1 & R1_SPI_COM_CRC) { + ret = COMM_ERR; + goto done; + } else if (r1 & ~R1_SPI_IDLE) { /* other errors */ + ret = TIMEOUT; + goto done; + } else if (cmd->resp_type == MMC_RSP_R2) { + r1 = mmc_spi_readdata(mmc, cmd->response, 1, 16); + for (i = 0; i < 4; i++) + cmd->response[i] = swab32(cmd->response[i]); + debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + } else if (!data) { + switch (cmd->cmdidx) { + case SD_CMD_APP_SEND_OP_COND: + case MMC_CMD_SEND_OP_COND: + cmd->response[0] = (r1 & R1_SPI_IDLE) ? 0 : OCR_BUSY; + break; + case SD_CMD_SEND_IF_COND: + case MMC_CMD_SPI_READ_OCR: + spi_xfer(spi, 4 * 8, NULL, cmd->response, 0); + cmd->response[0] = swab32(cmd->response[0]); + debug("r32 %x\n", cmd->response[0]); + break; + case MMC_CMD_SEND_STATUS: + spi_xfer(spi, 1 * 8, NULL, cmd->response, 0); + cmd->response[0] = (cmd->response[0] & 0xff) ? + MMC_STATUS_ERROR : MMC_STATUS_RDY_FOR_DATA; + break; + } + } else { + debug("%s:data %x %x %x\n", __func__, + data->flags, data->blocks, data->blocksize); + if (data->flags == MMC_DATA_READ) + r1 = mmc_spi_readdata(mmc, data->dest, + data->blocks, data->blocksize); + else if (data->flags == MMC_DATA_WRITE) + r1 = mmc_spi_writedata(mmc, data->src, + data->blocks, data->blocksize, + (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)); + if (r1 & R1_SPI_COM_CRC) + ret = COMM_ERR; + else if (r1) /* other errors */ + ret = TIMEOUT; + } +done: + spi_cs_deactivate(spi); + spi_release_bus(spi); + return ret; +} + +static void mmc_spi_set_ios(struct mmc *mmc) +{ + struct spi_slave *spi = mmc->priv; + + debug("%s: clock %u\n", __func__, mmc->clock); + if (mmc->clock) + spi_set_speed(spi, mmc->clock); +} + +static int mmc_spi_init_p(struct mmc *mmc) +{ + struct spi_slave *spi = mmc->priv; + spi_set_speed(spi, MMC_SPI_MIN_CLOCK); + spi_claim_bus(spi); + /* cs deactivated for 100+ clock */ + spi_xfer(spi, 18 * 8, NULL, NULL, 0); + spi_release_bus(spi); + return 0; +} + +static const struct mmc_ops mmc_spi_ops = { + .send_cmd = mmc_spi_request, + .set_ios = mmc_spi_set_ios, + .init = mmc_spi_init_p, +}; + +static struct mmc_config mmc_spi_cfg = { + .name = "MMC_SPI", + .ops = &mmc_spi_ops, + .host_caps = MMC_MODE_SPI, + .voltages = MMC_SPI_VOLTAGE, + .f_min = MMC_SPI_MIN_CLOCK, + .part_type = PART_TYPE_DOS, + .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, +}; + +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode) +{ + struct mmc *mmc; + struct spi_slave *spi; + + spi = spi_setup_slave(bus, cs, speed, mode); + if (spi == NULL) + return NULL; + + mmc_spi_cfg.f_max = speed; + + mmc = mmc_create(&mmc_spi_cfg, spi); + if (mmc == NULL) { + spi_free_slave(spi); + return NULL; + } + return mmc; +} diff --git a/qemu/roms/u-boot/drivers/mmc/mmc_write.c b/qemu/roms/u-boot/drivers/mmc/mmc_write.c new file mode 100644 index 000000000..3db9669c8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/mmc_write.c @@ -0,0 +1,180 @@ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "mmc_private.h" + +static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) +{ + struct mmc_cmd cmd; + ulong end; + int err, start_cmd, end_cmd; + + if (mmc->high_capacity) { + end = start + blkcnt - 1; + } else { + end = (start + blkcnt - 1) * mmc->write_bl_len; + start *= mmc->write_bl_len; + } + + if (IS_SD(mmc)) { + start_cmd = SD_CMD_ERASE_WR_BLK_START; + end_cmd = SD_CMD_ERASE_WR_BLK_END; + } else { + start_cmd = MMC_CMD_ERASE_GROUP_START; + end_cmd = MMC_CMD_ERASE_GROUP_END; + } + + cmd.cmdidx = start_cmd; + cmd.cmdarg = start; + cmd.resp_type = MMC_RSP_R1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + cmd.cmdidx = end_cmd; + cmd.cmdarg = end; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + cmd.cmdidx = MMC_CMD_ERASE; + cmd.cmdarg = SECURE_ERASE; + cmd.resp_type = MMC_RSP_R1b; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + return 0; + +err_out: + puts("mmc erase failed\n"); + return err; +} + +unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) +{ + int err = 0; + struct mmc *mmc = find_mmc_device(dev_num); + lbaint_t blk = 0, blk_r = 0; + int timeout = 1000; + + if (!mmc) + return -1; + + if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) + printf("\n\nCaution! Your devices Erase group is 0x%x\n" + "The erase range would be change to " + "0x" LBAF "~0x" LBAF "\n\n", + mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), + ((start + blkcnt + mmc->erase_grp_size) + & ~(mmc->erase_grp_size - 1)) - 1); + + while (blk < blkcnt) { + blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? + mmc->erase_grp_size : (blkcnt - blk); + err = mmc_erase_t(mmc, start + blk, blk_r); + if (err) + break; + + blk += blk_r; + + /* Waiting for the ready status */ + if (mmc_send_status(mmc, timeout)) + return 0; + } + + return blk; +} + +static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, + lbaint_t blkcnt, const void *src) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int timeout = 1000; + + if ((start + blkcnt) > mmc->block_dev.lba) { + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", + start + blkcnt, mmc->block_dev.lba); + return 0; + } + + if (blkcnt == 0) + return 0; + else if (blkcnt == 1) + cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * mmc->write_bl_len; + + cmd.resp_type = MMC_RSP_R1; + + data.src = src; + data.blocks = blkcnt; + data.blocksize = mmc->write_bl_len; + data.flags = MMC_DATA_WRITE; + + if (mmc_send_cmd(mmc, &cmd, &data)) { + printf("mmc write failed\n"); + return 0; + } + + /* SPI multiblock writes terminate using a special + * token, not a STOP_TRANSMISSION request. + */ + if (!mmc_host_is_spi(mmc) && blkcnt > 1) { + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + if (mmc_send_cmd(mmc, &cmd, NULL)) { + printf("mmc fail to send stop cmd\n"); + return 0; + } + } + + /* Waiting for the ready status */ + if (mmc_send_status(mmc, timeout)) + return 0; + + return blkcnt; +} + +ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void *src) +{ + lbaint_t cur, blocks_todo = blkcnt; + + struct mmc *mmc = find_mmc_device(dev_num); + if (!mmc) + return 0; + + if (mmc_set_blocklen(mmc, mmc->write_bl_len)) + return 0; + + do { + cur = (blocks_todo > mmc->cfg->b_max) ? + mmc->cfg->b_max : blocks_todo; + if (mmc_write_blocks(mmc, start, cur, src) != cur) + return 0; + blocks_todo -= cur; + start += cur; + src += cur * mmc->write_bl_len; + } while (blocks_todo > 0); + + return blkcnt; +} diff --git a/qemu/roms/u-boot/drivers/mmc/mv_sdhci.c b/qemu/roms/u-boot/drivers/mmc/mv_sdhci.c new file mode 100644 index 000000000..63e1f9062 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/mv_sdhci.c @@ -0,0 +1,55 @@ +#include +#include +#include + +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS +static struct sdhci_ops mv_ops; + +#if defined(CONFIG_SHEEVA_88SV331xV5) +#define SD_CE_ATA_2 0xEA +#define MMC_CARD 0x1000 +#define MMC_WIDTH 0x0100 +static inline void mv_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + struct mmc *mmc = host->mmc; + u32 ata = (u32)host->ioaddr + SD_CE_ATA_2; + + if (!IS_SD(mmc) && reg == SDHCI_HOST_CONTROL) { + if (mmc->bus_width == 8) + writew(readw(ata) | (MMC_CARD | MMC_WIDTH), ata); + else + writew(readw(ata) & ~(MMC_CARD | MMC_WIDTH), ata); + } + + writeb(val, host->ioaddr + reg); +} + +#else +#define mv_sdhci_writeb NULL +#endif /* CONFIG_SHEEVA_88SV331xV5 */ +#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */ + +static char *MVSDH_NAME = "mv_sdh"; +int mv_sdh_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) +{ + struct sdhci_host *host = NULL; + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("sdh_host malloc fail!\n"); + return 1; + } + + host->name = MVSDH_NAME; + host->ioaddr = (void *)regbase; + host->quirks = quirks; +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + memset(&mv_ops, 0, sizeof(struct sdhci_ops)); + mv_ops.write_b = mv_sdhci_writeb; + host->ops = &mv_ops; +#endif + if (quirks & SDHCI_QUIRK_REG32_RW) + host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; + else + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + return add_sdhci(host, max_clk, min_clk); +} diff --git a/qemu/roms/u-boot/drivers/mmc/mxcmmc.c b/qemu/roms/u-boot/drivers/mmc/mxcmmc.c new file mode 100644 index 000000000..561b20459 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/mxcmmc.c @@ -0,0 +1,521 @@ +/* + * This is a driver for the SDHC controller found in Freescale MX2/MX3 + * SoCs. It is basically the same hardware as found on MX1 (imxmmc.c). + * Unlike the hardware found on MX1, this hardware just works and does + * not need all the quirks found in imxmmc.c, hence the seperate driver. + * + * Copyright (C) 2009 Ilya Yanok, + * Copyright (C) 2008 Sascha Hauer, Pengutronix + * Copyright (C) 2006 Pavel Pisa, PiKRON + * + * derived from pxamci.c by Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mxc-mmc" + +struct mxcmci_regs { + u32 str_stp_clk; + u32 status; + u32 clk_rate; + u32 cmd_dat_cont; + u32 res_to; + u32 read_to; + u32 blk_len; + u32 nob; + u32 rev_no; + u32 int_cntr; + u32 cmd; + u32 arg; + u32 pad; + u32 res_fifo; + u32 buffer_access; +}; + +#define STR_STP_CLK_RESET (1 << 3) +#define STR_STP_CLK_START_CLK (1 << 1) +#define STR_STP_CLK_STOP_CLK (1 << 0) + +#define STATUS_CARD_INSERTION (1 << 31) +#define STATUS_CARD_REMOVAL (1 << 30) +#define STATUS_YBUF_EMPTY (1 << 29) +#define STATUS_XBUF_EMPTY (1 << 28) +#define STATUS_YBUF_FULL (1 << 27) +#define STATUS_XBUF_FULL (1 << 26) +#define STATUS_BUF_UND_RUN (1 << 25) +#define STATUS_BUF_OVFL (1 << 24) +#define STATUS_SDIO_INT_ACTIVE (1 << 14) +#define STATUS_END_CMD_RESP (1 << 13) +#define STATUS_WRITE_OP_DONE (1 << 12) +#define STATUS_DATA_TRANS_DONE (1 << 11) +#define STATUS_READ_OP_DONE (1 << 11) +#define STATUS_WR_CRC_ERROR_CODE_MASK (3 << 10) +#define STATUS_CARD_BUS_CLK_RUN (1 << 8) +#define STATUS_BUF_READ_RDY (1 << 7) +#define STATUS_BUF_WRITE_RDY (1 << 6) +#define STATUS_RESP_CRC_ERR (1 << 5) +#define STATUS_CRC_READ_ERR (1 << 3) +#define STATUS_CRC_WRITE_ERR (1 << 2) +#define STATUS_TIME_OUT_RESP (1 << 1) +#define STATUS_TIME_OUT_READ (1 << 0) +#define STATUS_ERR_MASK 0x2f + +#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1 << 12) +#define CMD_DAT_CONT_STOP_READWAIT (1 << 11) +#define CMD_DAT_CONT_START_READWAIT (1 << 10) +#define CMD_DAT_CONT_BUS_WIDTH_4 (2 << 8) +#define CMD_DAT_CONT_INIT (1 << 7) +#define CMD_DAT_CONT_WRITE (1 << 4) +#define CMD_DAT_CONT_DATA_ENABLE (1 << 3) +#define CMD_DAT_CONT_RESPONSE_48BIT_CRC (1 << 0) +#define CMD_DAT_CONT_RESPONSE_136BIT (2 << 0) +#define CMD_DAT_CONT_RESPONSE_48BIT (3 << 0) + +#define INT_SDIO_INT_WKP_EN (1 << 18) +#define INT_CARD_INSERTION_WKP_EN (1 << 17) +#define INT_CARD_REMOVAL_WKP_EN (1 << 16) +#define INT_CARD_INSERTION_EN (1 << 15) +#define INT_CARD_REMOVAL_EN (1 << 14) +#define INT_SDIO_IRQ_EN (1 << 13) +#define INT_DAT0_EN (1 << 12) +#define INT_BUF_READ_EN (1 << 4) +#define INT_BUF_WRITE_EN (1 << 3) +#define INT_END_CMD_RES_EN (1 << 2) +#define INT_WRITE_OP_DONE_EN (1 << 1) +#define INT_READ_OP_EN (1 << 0) + +struct mxcmci_host { + struct mmc *mmc; + struct mxcmci_regs *base; + int irq; + int detect_irq; + int dma; + int do_dma; + unsigned int power_mode; + + struct mmc_cmd *cmd; + struct mmc_data *data; + + unsigned int dma_nents; + unsigned int datasize; + unsigned int dma_dir; + + u16 rev_no; + unsigned int cmdat; + + int clock; +}; + +static struct mxcmci_host mxcmci_host; + +/* maintainer note: do we really want to have a global host pointer? */ +static struct mxcmci_host *host = &mxcmci_host; + +static inline int mxcmci_use_dma(struct mxcmci_host *host) +{ + return host->do_dma; +} + +static void mxcmci_softreset(struct mxcmci_host *host) +{ + int i; + + /* reset sequence */ + writel(STR_STP_CLK_RESET, &host->base->str_stp_clk); + writel(STR_STP_CLK_RESET | STR_STP_CLK_START_CLK, + &host->base->str_stp_clk); + + for (i = 0; i < 8; i++) + writel(STR_STP_CLK_START_CLK, &host->base->str_stp_clk); + + writel(0xff, &host->base->res_to); +} + +static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) +{ + unsigned int nob = data->blocks; + unsigned int blksz = data->blocksize; + unsigned int datasize = nob * blksz; + + host->data = data; + + writel(nob, &host->base->nob); + writel(blksz, &host->base->blk_len); + host->datasize = datasize; +} + +static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_cmd *cmd, + unsigned int cmdat) +{ + if (host->cmd != NULL) + printf("mxcmci: error!\n"); + host->cmd = cmd; + + switch (cmd->resp_type) { + case MMC_RSP_R1: /* short CRC, OPCODE */ + case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */ + cmdat |= CMD_DAT_CONT_RESPONSE_48BIT_CRC; + break; + case MMC_RSP_R2: /* long 136 bit + CRC */ + cmdat |= CMD_DAT_CONT_RESPONSE_136BIT; + break; + case MMC_RSP_R3: /* short */ + cmdat |= CMD_DAT_CONT_RESPONSE_48BIT; + break; + case MMC_RSP_NONE: + break; + default: + printf("mxcmci: unhandled response type 0x%x\n", + cmd->resp_type); + return -EINVAL; + } + + writel(cmd->cmdidx, &host->base->cmd); + writel(cmd->cmdarg, &host->base->arg); + writel(cmdat, &host->base->cmd_dat_cont); + + return 0; +} + +static void mxcmci_finish_request(struct mxcmci_host *host, + struct mmc_cmd *cmd, struct mmc_data *data) +{ + host->cmd = NULL; + host->data = NULL; +} + +static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat) +{ + int data_error = 0; + + if (stat & STATUS_ERR_MASK) { + printf("request failed. status: 0x%08x\n", + stat); + if (stat & STATUS_CRC_READ_ERR) { + data_error = -EILSEQ; + } else if (stat & STATUS_CRC_WRITE_ERR) { + u32 err_code = (stat >> 9) & 0x3; + if (err_code == 2) /* No CRC response */ + data_error = TIMEOUT; + else + data_error = -EILSEQ; + } else if (stat & STATUS_TIME_OUT_READ) { + data_error = TIMEOUT; + } else { + data_error = -EIO; + } + } + + host->data = NULL; + + return data_error; +} + +static int mxcmci_read_response(struct mxcmci_host *host, unsigned int stat) +{ + struct mmc_cmd *cmd = host->cmd; + int i; + u32 a, b, c; + u32 *resp = (u32 *)cmd->response; + + if (!cmd) + return 0; + + if (stat & STATUS_TIME_OUT_RESP) { + printf("CMD TIMEOUT\n"); + return TIMEOUT; + } else if (stat & STATUS_RESP_CRC_ERR && cmd->resp_type & MMC_RSP_CRC) { + printf("cmd crc error\n"); + return -EILSEQ; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + if (cmd->resp_type & MMC_RSP_136) { + for (i = 0; i < 4; i++) { + a = readl(&host->base->res_fifo) & 0xFFFF; + b = readl(&host->base->res_fifo) & 0xFFFF; + resp[i] = a << 16 | b; + } + } else { + a = readl(&host->base->res_fifo) & 0xFFFF; + b = readl(&host->base->res_fifo) & 0xFFFF; + c = readl(&host->base->res_fifo) & 0xFFFF; + resp[0] = a << 24 | b << 8 | c >> 8; + } + } + return 0; +} + +static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask) +{ + u32 stat; + unsigned long timeout = get_ticks() + CONFIG_SYS_HZ; + + do { + stat = readl(&host->base->status); + if (stat & STATUS_ERR_MASK) + return stat; + if (timeout < get_ticks()) + return STATUS_TIME_OUT_READ; + if (stat & mask) + return 0; + } while (1); +} + +static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes) +{ + unsigned int stat; + u32 *buf = _buf; + + while (bytes > 3) { + stat = mxcmci_poll_status(host, + STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE); + if (stat) + return stat; + *buf++ = readl(&host->base->buffer_access); + bytes -= 4; + } + + if (bytes) { + u8 *b = (u8 *)buf; + u32 tmp; + + stat = mxcmci_poll_status(host, + STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE); + if (stat) + return stat; + tmp = readl(&host->base->buffer_access); + memcpy(b, &tmp, bytes); + } + + return 0; +} + +static int mxcmci_push(struct mxcmci_host *host, const void *_buf, int bytes) +{ + unsigned int stat; + const u32 *buf = _buf; + + while (bytes > 3) { + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); + if (stat) + return stat; + writel(*buf++, &host->base->buffer_access); + bytes -= 4; + } + + if (bytes) { + const u8 *b = (u8 *)buf; + u32 tmp; + + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); + if (stat) + return stat; + + memcpy(&tmp, b, bytes); + writel(tmp, &host->base->buffer_access); + } + + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); + if (stat) + return stat; + + return 0; +} + +static int mxcmci_transfer_data(struct mxcmci_host *host) +{ + struct mmc_data *data = host->data; + int stat; + unsigned long length; + + length = data->blocks * data->blocksize; + host->datasize = 0; + + if (data->flags & MMC_DATA_READ) { + stat = mxcmci_pull(host, data->dest, length); + if (stat) + return stat; + host->datasize += length; + } else { + stat = mxcmci_push(host, (const void *)(data->src), length); + if (stat) + return stat; + host->datasize += length; + stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE); + if (stat) + return stat; + } + return 0; +} + +static int mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat) +{ + int datastat; + int ret; + + ret = mxcmci_read_response(host, stat); + + if (ret) { + mxcmci_finish_request(host, host->cmd, host->data); + return ret; + } + + if (!host->data) { + mxcmci_finish_request(host, host->cmd, host->data); + return 0; + } + + datastat = mxcmci_transfer_data(host); + ret = mxcmci_finish_data(host, datastat); + mxcmci_finish_request(host, host->cmd, host->data); + return ret; +} + +static int mxcmci_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct mxcmci_host *host = mmc->priv; + unsigned int cmdat = host->cmdat; + u32 stat; + int ret; + + host->cmdat &= ~CMD_DAT_CONT_INIT; + if (data) { + mxcmci_setup_data(host, data); + + cmdat |= CMD_DAT_CONT_DATA_ENABLE; + + if (data->flags & MMC_DATA_WRITE) + cmdat |= CMD_DAT_CONT_WRITE; + } + + if ((ret = mxcmci_start_cmd(host, cmd, cmdat))) { + mxcmci_finish_request(host, cmd, data); + return ret; + } + + do { + stat = readl(&host->base->status); + writel(stat, &host->base->status); + } while (!(stat & STATUS_END_CMD_RESP)); + + return mxcmci_cmd_done(host, stat); +} + +static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios) +{ + unsigned int divider; + int prescaler = 0; + unsigned long clk_in = mxc_get_clock(MXC_ESDHC_CLK); + + while (prescaler <= 0x800) { + for (divider = 1; divider <= 0xF; divider++) { + int x; + + x = (clk_in / (divider + 1)); + + if (prescaler) + x /= (prescaler * 2); + + if (x <= clk_ios) + break; + } + if (divider < 0x10) + break; + + if (prescaler == 0) + prescaler = 1; + else + prescaler <<= 1; + } + + writel((prescaler << 4) | divider, &host->base->clk_rate); +} + +static void mxcmci_set_ios(struct mmc *mmc) +{ + struct mxcmci_host *host = mmc->priv; + if (mmc->bus_width == 4) + host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4; + else + host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4; + + if (mmc->clock) { + mxcmci_set_clk_rate(host, mmc->clock); + writel(STR_STP_CLK_START_CLK, &host->base->str_stp_clk); + } else { + writel(STR_STP_CLK_STOP_CLK, &host->base->str_stp_clk); + } + + host->clock = mmc->clock; +} + +static int mxcmci_init(struct mmc *mmc) +{ + struct mxcmci_host *host = mmc->priv; + + mxcmci_softreset(host); + + host->rev_no = readl(&host->base->rev_no); + if (host->rev_no != 0x400) { + printf("wrong rev.no. 0x%08x. aborting.\n", + host->rev_no); + return -ENODEV; + } + + /* recommended in data sheet */ + writel(0x2db4, &host->base->read_to); + + writel(0, &host->base->int_cntr); + + return 0; +} + +static const struct mmc_ops mxcmci_ops = { + .send_cmd = mxcmci_request, + .set_ios = mxcmci_set_ios, + .init = mxcmci_init, +}; + +static struct mmc_config mxcmci_cfg = { + .name = "MXC MCI", + .ops = &mxcmci_ops, + .host_caps = MMC_MODE_4BIT, + .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, + .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, +}; + +static int mxcmci_initialize(bd_t *bis) +{ + host->base = (struct mxcmci_regs *)CONFIG_MXC_MCI_REGS_BASE; + + mxcmci_cfg.f_min = mxc_get_clock(MXC_ESDHC_CLK) >> 7; + mxcmci_cfg.f_max = mxc_get_clock(MXC_ESDHC_CLK) >> 1; + + host->mmc = mmc_create(&mxcmci_cfg, host); + if (host->mmc == NULL) + return -1; + + return 0; +} + +int mxc_mmc_init(bd_t *bis) +{ + return mxcmci_initialize(bis); +} diff --git a/qemu/roms/u-boot/drivers/mmc/mxsmmc.c b/qemu/roms/u-boot/drivers/mmc/mxsmmc.c new file mode 100644 index 000000000..2fa4eeef4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/mxsmmc.c @@ -0,0 +1,428 @@ +/* + * Freescale i.MX28 SSP MMC driver + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * (C) Copyright 2008-2010 Freescale Semiconductor, Inc. + * Terry Lv + * + * Copyright 2007, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the pxa mmc code: + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mxsmmc_priv { + int id; + struct mxs_ssp_regs *regs; + uint32_t buswidth; + int (*mmc_is_wp)(int); + int (*mmc_cd)(int); + struct mxs_dma_desc *desc; + struct mmc_config cfg; /* mmc configuration */ +}; + +#define MXSMMC_MAX_TIMEOUT 10000 +#define MXSMMC_SMALL_TRANSFER 512 + +static int mxsmmc_cd(struct mxsmmc_priv *priv) +{ + struct mxs_ssp_regs *ssp_regs = priv->regs; + + if (priv->mmc_cd) + return priv->mmc_cd(priv->id); + + return !(readl(&ssp_regs->hw_ssp_status) & SSP_STATUS_CARD_DETECT); +} + +static int mxsmmc_send_cmd_pio(struct mxsmmc_priv *priv, struct mmc_data *data) +{ + struct mxs_ssp_regs *ssp_regs = priv->regs; + uint32_t *data_ptr; + int timeout = MXSMMC_MAX_TIMEOUT; + uint32_t reg; + uint32_t data_count = data->blocksize * data->blocks; + + if (data->flags & MMC_DATA_READ) { + data_ptr = (uint32_t *)data->dest; + while (data_count && --timeout) { + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & SSP_STATUS_FIFO_EMPTY)) { + *data_ptr++ = readl(&ssp_regs->hw_ssp_data); + data_count -= 4; + timeout = MXSMMC_MAX_TIMEOUT; + } else + udelay(1000); + } + } else { + data_ptr = (uint32_t *)data->src; + timeout *= 100; + while (data_count && --timeout) { + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & SSP_STATUS_FIFO_FULL)) { + writel(*data_ptr++, &ssp_regs->hw_ssp_data); + data_count -= 4; + timeout = MXSMMC_MAX_TIMEOUT; + } else + udelay(1000); + } + } + + return timeout ? 0 : COMM_ERR; +} + +static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data) +{ + uint32_t data_count = data->blocksize * data->blocks; + int dmach; + struct mxs_dma_desc *desc = priv->desc; + void *addr; + unsigned int flags; + struct bounce_buffer bbstate; + + memset(desc, 0, sizeof(struct mxs_dma_desc)); + desc->address = (dma_addr_t)desc; + + if (data->flags & MMC_DATA_READ) { + priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; + addr = data->dest; + flags = GEN_BB_WRITE; + } else { + priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; + addr = (void *)data->src; + flags = GEN_BB_READ; + } + + bounce_buffer_start(&bbstate, addr, data_count, flags); + + priv->desc->cmd.address = (dma_addr_t)bbstate.bounce_buffer; + + priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | + (data_count << MXS_DMA_DESC_BYTES_OFFSET); + + dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id; + mxs_dma_desc_append(dmach, priv->desc); + if (mxs_dma_go(dmach)) { + bounce_buffer_stop(&bbstate); + return COMM_ERR; + } + + bounce_buffer_stop(&bbstate); + + return 0; +} + +/* + * Sends a command out on the bus. Takes the mmc pointer, + * a command pointer, and an optional data pointer. + */ +static int +mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + struct mxsmmc_priv *priv = mmc->priv; + struct mxs_ssp_regs *ssp_regs = priv->regs; + uint32_t reg; + int timeout; + uint32_t ctrl0; + int ret; + + debug("MMC%d: CMD%d\n", mmc->block_dev.dev, cmd->cmdidx); + + /* Check bus busy */ + timeout = MXSMMC_MAX_TIMEOUT; + while (--timeout) { + udelay(1000); + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & + (SSP_STATUS_BUSY | SSP_STATUS_DATA_BUSY | + SSP_STATUS_CMD_BUSY))) { + break; + } + } + + if (!timeout) { + printf("MMC%d: Bus busy timeout!\n", mmc->block_dev.dev); + return TIMEOUT; + } + + /* See if card is present */ + if (!mxsmmc_cd(priv)) { + printf("MMC%d: No card detected!\n", mmc->block_dev.dev); + return NO_CARD_ERR; + } + + /* Start building CTRL0 contents */ + ctrl0 = priv->buswidth; + + /* Set up command */ + if (!(cmd->resp_type & MMC_RSP_CRC)) + ctrl0 |= SSP_CTRL0_IGNORE_CRC; + if (cmd->resp_type & MMC_RSP_PRESENT) /* Need to get response */ + ctrl0 |= SSP_CTRL0_GET_RESP; + if (cmd->resp_type & MMC_RSP_136) /* It's a 136 bits response */ + ctrl0 |= SSP_CTRL0_LONG_RESP; + + if (data && (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER)) + writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr); + else + writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set); + + /* Command index */ + reg = readl(&ssp_regs->hw_ssp_cmd0); + reg &= ~(SSP_CMD0_CMD_MASK | SSP_CMD0_APPEND_8CYC); + reg |= cmd->cmdidx << SSP_CMD0_CMD_OFFSET; + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + reg |= SSP_CMD0_APPEND_8CYC; + writel(reg, &ssp_regs->hw_ssp_cmd0); + + /* Command argument */ + writel(cmd->cmdarg, &ssp_regs->hw_ssp_cmd1); + + /* Set up data */ + if (data) { + /* READ or WRITE */ + if (data->flags & MMC_DATA_READ) { + ctrl0 |= SSP_CTRL0_READ; + } else if (priv->mmc_is_wp && + priv->mmc_is_wp(mmc->block_dev.dev)) { + printf("MMC%d: Can not write a locked card!\n", + mmc->block_dev.dev); + return UNUSABLE_ERR; + } + + ctrl0 |= SSP_CTRL0_DATA_XFER; + + reg = data->blocksize * data->blocks; +#if defined(CONFIG_MX23) + ctrl0 |= reg & SSP_CTRL0_XFER_COUNT_MASK; + + clrsetbits_le32(&ssp_regs->hw_ssp_cmd0, + SSP_CMD0_BLOCK_SIZE_MASK | SSP_CMD0_BLOCK_COUNT_MASK, + ((data->blocks - 1) << SSP_CMD0_BLOCK_COUNT_OFFSET) | + ((ffs(data->blocksize) - 1) << + SSP_CMD0_BLOCK_SIZE_OFFSET)); +#elif defined(CONFIG_MX28) + writel(reg, &ssp_regs->hw_ssp_xfer_size); + + reg = ((data->blocks - 1) << + SSP_BLOCK_SIZE_BLOCK_COUNT_OFFSET) | + ((ffs(data->blocksize) - 1) << + SSP_BLOCK_SIZE_BLOCK_SIZE_OFFSET); + writel(reg, &ssp_regs->hw_ssp_block_size); +#endif + } + + /* Kick off the command */ + ctrl0 |= SSP_CTRL0_WAIT_FOR_IRQ | SSP_CTRL0_ENABLE | SSP_CTRL0_RUN; + writel(ctrl0, &ssp_regs->hw_ssp_ctrl0); + + /* Wait for the command to complete */ + timeout = MXSMMC_MAX_TIMEOUT; + while (--timeout) { + udelay(1000); + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & SSP_STATUS_CMD_BUSY)) + break; + } + + if (!timeout) { + printf("MMC%d: Command %d busy\n", + mmc->block_dev.dev, cmd->cmdidx); + return TIMEOUT; + } + + /* Check command timeout */ + if (reg & SSP_STATUS_RESP_TIMEOUT) { + printf("MMC%d: Command %d timeout (status 0x%08x)\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return TIMEOUT; + } + + /* Check command errors */ + if (reg & (SSP_STATUS_RESP_CRC_ERR | SSP_STATUS_RESP_ERR)) { + printf("MMC%d: Command %d error (status 0x%08x)!\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return COMM_ERR; + } + + /* Copy response to response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[3] = readl(&ssp_regs->hw_ssp_sdresp0); + cmd->response[2] = readl(&ssp_regs->hw_ssp_sdresp1); + cmd->response[1] = readl(&ssp_regs->hw_ssp_sdresp2); + cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp3); + } else + cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp0); + + /* Return if no data to process */ + if (!data) + return 0; + + if (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER) { + ret = mxsmmc_send_cmd_pio(priv, data); + if (ret) { + printf("MMC%d: Data timeout with command %d " + "(status 0x%08x)!\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return ret; + } + } else { + ret = mxsmmc_send_cmd_dma(priv, data); + if (ret) { + printf("MMC%d: DMA transfer failed\n", + mmc->block_dev.dev); + return ret; + } + } + + /* Check data errors */ + reg = readl(&ssp_regs->hw_ssp_status); + if (reg & + (SSP_STATUS_TIMEOUT | SSP_STATUS_DATA_CRC_ERR | + SSP_STATUS_FIFO_OVRFLW | SSP_STATUS_FIFO_UNDRFLW)) { + printf("MMC%d: Data error with command %d (status 0x%08x)!\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return COMM_ERR; + } + + return 0; +} + +static void mxsmmc_set_ios(struct mmc *mmc) +{ + struct mxsmmc_priv *priv = mmc->priv; + struct mxs_ssp_regs *ssp_regs = priv->regs; + + /* Set the clock speed */ + if (mmc->clock) + mxs_set_ssp_busclock(priv->id, mmc->clock / 1000); + + switch (mmc->bus_width) { + case 1: + priv->buswidth = SSP_CTRL0_BUS_WIDTH_ONE_BIT; + break; + case 4: + priv->buswidth = SSP_CTRL0_BUS_WIDTH_FOUR_BIT; + break; + case 8: + priv->buswidth = SSP_CTRL0_BUS_WIDTH_EIGHT_BIT; + break; + } + + /* Set the bus width */ + clrsetbits_le32(&ssp_regs->hw_ssp_ctrl0, + SSP_CTRL0_BUS_WIDTH_MASK, priv->buswidth); + + debug("MMC%d: Set %d bits bus width\n", + mmc->block_dev.dev, mmc->bus_width); +} + +static int mxsmmc_init(struct mmc *mmc) +{ + struct mxsmmc_priv *priv = mmc->priv; + struct mxs_ssp_regs *ssp_regs = priv->regs; + + /* Reset SSP */ + mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); + + /* Reconfigure the SSP block for MMC operation */ + writel(SSP_CTRL1_SSP_MODE_SD_MMC | + SSP_CTRL1_WORD_LENGTH_EIGHT_BITS | + SSP_CTRL1_DMA_ENABLE | + SSP_CTRL1_POLARITY | + SSP_CTRL1_RECV_TIMEOUT_IRQ_EN | + SSP_CTRL1_DATA_CRC_IRQ_EN | + SSP_CTRL1_DATA_TIMEOUT_IRQ_EN | + SSP_CTRL1_RESP_TIMEOUT_IRQ_EN | + SSP_CTRL1_RESP_ERR_IRQ_EN, + &ssp_regs->hw_ssp_ctrl1_set); + + /* Set initial bit clock 400 KHz */ + mxs_set_ssp_busclock(priv->id, 400); + + /* Send initial 74 clock cycles (185 us @ 400 KHz)*/ + writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_set); + udelay(200); + writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_clr); + + return 0; +} + +static const struct mmc_ops mxsmmc_ops = { + .send_cmd = mxsmmc_send_cmd, + .set_ios = mxsmmc_set_ios, + .init = mxsmmc_init, +}; + +int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int), int (*cd)(int)) +{ + struct mmc *mmc = NULL; + struct mxsmmc_priv *priv = NULL; + int ret; + const unsigned int mxsmmc_clk_id = mxs_ssp_clock_by_bus(id); + + if (!mxs_ssp_bus_id_valid(id)) + return -ENODEV; + + priv = malloc(sizeof(struct mxsmmc_priv)); + if (!priv) + return -ENOMEM; + + priv->desc = mxs_dma_desc_alloc(); + if (!priv->desc) { + free(priv); + return -ENOMEM; + } + + ret = mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + id); + if (ret) + return ret; + + priv->mmc_is_wp = wp; + priv->mmc_cd = cd; + priv->id = id; + priv->regs = mxs_ssp_regs_by_bus(id); + + priv->cfg.name = "MXS MMC"; + priv->cfg.ops = &mxsmmc_ops; + + priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + + priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | + MMC_MODE_HS_52MHz | MMC_MODE_HS | + MMC_MODE_HC; + + /* + * SSPCLK = 480 * 18 / 29 / 1 = 297.731 MHz + * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), + * CLOCK_DIVIDE has to be an even value from 2 to 254, and + * CLOCK_RATE could be any integer from 0 to 255. + */ + priv->cfg.f_min = 400000; + priv->cfg.f_max = mxc_get_clock(MXC_SSP0_CLK + mxsmmc_clk_id) * 1000 / 2; + priv->cfg.b_max = 0x20; + + mmc = mmc_create(&priv->cfg, priv); + if (mmc == NULL) { + mxs_dma_desc_free(priv->desc); + free(priv); + return -ENOMEM; + } + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/omap_hsmmc.c b/qemu/roms/u-boot/drivers/mmc/omap_hsmmc.c new file mode 100644 index 000000000..17cbb0983 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/omap_hsmmc.c @@ -0,0 +1,720 @@ +/* + * (C) Copyright 2008 + * Texas Instruments, + * Sukumar Ghorai + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation's version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* simplify defines to OMAP_HSMMC_USE_GPIO */ +#if (defined(CONFIG_OMAP_GPIO) && !defined(CONFIG_SPL_BUILD)) || \ + (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_GPIO_SUPPORT)) +#define OMAP_HSMMC_USE_GPIO +#else +#undef OMAP_HSMMC_USE_GPIO +#endif + +/* common definitions for all OMAPs */ +#define SYSCTL_SRC (1 << 25) +#define SYSCTL_SRD (1 << 26) + +struct omap_hsmmc_data { + struct hsmmc *base_addr; + struct mmc_config cfg; +#ifdef OMAP_HSMMC_USE_GPIO + int cd_gpio; + int wp_gpio; +#endif +}; + +/* If we fail after 1 second wait, something is really bad */ +#define MAX_RETRY_MS 1000 + +static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); +static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, + unsigned int siz); + +#ifdef OMAP_HSMMC_USE_GPIO +static int omap_mmc_setup_gpio_in(int gpio, const char *label) +{ + if (!gpio_is_valid(gpio)) + return -1; + + if (gpio_request(gpio, label) < 0) + return -1; + + if (gpio_direction_input(gpio) < 0) + return -1; + + return gpio; +} +#endif + +#if defined(CONFIG_OMAP44XX) && defined(CONFIG_TWL6030_POWER) +static void omap4_vmmc_pbias_config(struct mmc *mmc) +{ + u32 value = 0; + + value = readl((*ctrl)->control_pbiaslite); + value &= ~(MMC1_PBIASLITE_PWRDNZ | MMC1_PWRDNZ); + writel(value, (*ctrl)->control_pbiaslite); + /* set VMMC to 3V */ + twl6030_power_mmc_init(); + value = readl((*ctrl)->control_pbiaslite); + value |= MMC1_PBIASLITE_VMODE | MMC1_PBIASLITE_PWRDNZ | MMC1_PWRDNZ; + writel(value, (*ctrl)->control_pbiaslite); +} +#endif + +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_PALMAS_POWER) +static void omap5_pbias_config(struct mmc *mmc) +{ + u32 value = 0; + + value = readl((*ctrl)->control_pbias); + value &= ~SDCARD_PWRDNZ; + writel(value, (*ctrl)->control_pbias); + udelay(10); /* wait 10 us */ + value &= ~SDCARD_BIAS_PWRDNZ; + writel(value, (*ctrl)->control_pbias); + + palmas_mmc1_poweron_ldo(); + + value = readl((*ctrl)->control_pbias); + value |= SDCARD_BIAS_PWRDNZ; + writel(value, (*ctrl)->control_pbias); + udelay(150); /* wait 150 us */ + value |= SDCARD_PWRDNZ; + writel(value, (*ctrl)->control_pbias); + udelay(150); /* wait 150 us */ +} +#endif + +unsigned char mmc_board_init(struct mmc *mmc) +{ +#if defined(CONFIG_OMAP34XX) + t2_t *t2_base = (t2_t *)T2_BASE; + struct prcm *prcm_base = (struct prcm *)PRCM_BASE; + u32 pbias_lite; + + pbias_lite = readl(&t2_base->pbias_lite); + pbias_lite &= ~(PBIASLITEPWRDNZ1 | PBIASLITEPWRDNZ0); + writel(pbias_lite, &t2_base->pbias_lite); +#endif +#if defined(CONFIG_TWL4030_POWER) + twl4030_power_mmc_init(); + mdelay(100); /* ramp-up delay from Linux code */ +#endif +#if defined(CONFIG_OMAP34XX) + writel(pbias_lite | PBIASLITEPWRDNZ1 | + PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0, + &t2_base->pbias_lite); + + writel(readl(&t2_base->devconf0) | MMCSDIO1ADPCLKISEL, + &t2_base->devconf0); + + writel(readl(&t2_base->devconf1) | MMCSDIO2ADPCLKISEL, + &t2_base->devconf1); + + /* Change from default of 52MHz to 26MHz if necessary */ + if (!(mmc->cfg->host_caps & MMC_MODE_HS_52MHz)) + writel(readl(&t2_base->ctl_prog_io1) & ~CTLPROGIO1SPEEDCTRL, + &t2_base->ctl_prog_io1); + + writel(readl(&prcm_base->fclken1_core) | + EN_MMC1 | EN_MMC2 | EN_MMC3, + &prcm_base->fclken1_core); + + writel(readl(&prcm_base->iclken1_core) | + EN_MMC1 | EN_MMC2 | EN_MMC3, + &prcm_base->iclken1_core); +#endif + +#if defined(CONFIG_OMAP44XX) && defined(CONFIG_TWL6030_POWER) + /* PBIAS config needed for MMC1 only */ + if (mmc->block_dev.dev == 0) + omap4_vmmc_pbias_config(mmc); +#endif +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_PALMAS_POWER) + if (mmc->block_dev.dev == 0) + omap5_pbias_config(mmc); +#endif + + return 0; +} + +void mmc_init_stream(struct hsmmc *mmc_base) +{ + ulong start; + + writel(readl(&mmc_base->con) | INIT_INITSTREAM, &mmc_base->con); + + writel(MMC_CMD0, &mmc_base->cmd); + start = get_timer(0); + while (!(readl(&mmc_base->stat) & CC_MASK)) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for cc!\n", __func__); + return; + } + } + writel(CC_MASK, &mmc_base->stat) + ; + writel(MMC_CMD0, &mmc_base->cmd) + ; + start = get_timer(0); + while (!(readl(&mmc_base->stat) & CC_MASK)) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for cc2!\n", __func__); + return; + } + } + writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con); +} + + +static int omap_hsmmc_init_setup(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + unsigned int reg_val; + unsigned int dsor; + ulong start; + + mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr; + mmc_board_init(mmc); + + writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET, + &mmc_base->sysconfig); + start = get_timer(0); + while ((readl(&mmc_base->sysstatus) & RESETDONE) == 0) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for cc2!\n", __func__); + return TIMEOUT; + } + } + writel(readl(&mmc_base->sysctl) | SOFTRESETALL, &mmc_base->sysctl); + start = get_timer(0); + while ((readl(&mmc_base->sysctl) & SOFTRESETALL) != 0x0) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for softresetall!\n", + __func__); + return TIMEOUT; + } + } + writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl); + writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP, + &mmc_base->capa); + + reg_val = readl(&mmc_base->con) & RESERVED_MASK; + + writel(CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH | CDP_ACTIVEHIGH | + MIT_CTO | DW8_1_4BITMODE | MODE_FUNC | STR_BLOCK | + HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN, &mmc_base->con); + + dsor = 240; + mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK), + (ICE_STOP | DTO_15THDTO | CEN_DISABLE)); + mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, + (dsor << CLKD_OFFSET) | ICE_OSCILLATE); + start = get_timer(0); + while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for ics!\n", __func__); + return TIMEOUT; + } + } + writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); + + writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl); + + writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE | + IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC, + &mmc_base->ie); + + mmc_init_stream(mmc_base); + + return 0; +} + +/* + * MMC controller internal finite state machine reset + * + * Used to reset command or data internal state machines, using respectively + * SRC or SRD bit of SYSCTL register + */ +static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) +{ + ulong start; + + mmc_reg_out(&mmc_base->sysctl, bit, bit); + + /* + * CMD(DAT) lines reset procedures are slightly different + * for OMAP3 and OMAP4(AM335x,OMAP5,DRA7xx). + * According to OMAP3 TRM: + * Set SRC(SRD) bit in MMCHS_SYSCTL register to 0x1 and wait until it + * returns to 0x0. + * According to OMAP4(AM335x,OMAP5,DRA7xx) TRMs, CMD(DATA) lines reset + * procedure steps must be as follows: + * 1. Initiate CMD(DAT) line reset by writing 0x1 to SRC(SRD) bit in + * MMCHS_SYSCTL register (SD_SYSCTL for AM335x). + * 2. Poll the SRC(SRD) bit until it is set to 0x1. + * 3. Wait until the SRC (SRD) bit returns to 0x0 + * (reset procedure is completed). + */ +#if defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ + defined(CONFIG_AM33XX) + if (!(readl(&mmc_base->sysctl) & bit)) { + start = get_timer(0); + while (!(readl(&mmc_base->sysctl) & bit)) { + if (get_timer(0) - start > MAX_RETRY_MS) + return; + } + } +#endif + start = get_timer(0); + while ((readl(&mmc_base->sysctl) & bit) != 0) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for sysctl %x to clear\n", + __func__, bit); + return; + } + } +} + +static int omap_hsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct hsmmc *mmc_base; + unsigned int flags, mmc_stat; + ulong start; + + mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr; + start = get_timer(0); + while ((readl(&mmc_base->pstate) & (DATI_MASK | CMDI_MASK)) != 0) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting on cmd inhibit to clear\n", + __func__); + return TIMEOUT; + } + } + writel(0xFFFFFFFF, &mmc_base->stat); + start = get_timer(0); + while (readl(&mmc_base->stat)) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for STAT (%x) to clear\n", + __func__, readl(&mmc_base->stat)); + return TIMEOUT; + } + } + /* + * CMDREG + * CMDIDX[13:8] : Command index + * DATAPRNT[5] : Data Present Select + * ENCMDIDX[4] : Command Index Check Enable + * ENCMDCRC[3] : Command CRC Check Enable + * RSPTYP[1:0] + * 00 = No Response + * 01 = Length 136 + * 10 = Length 48 + * 11 = Length 48 Check busy after response + */ + /* Delay added before checking the status of frq change + * retry not supported by mmc.c(core file) + */ + if (cmd->cmdidx == SD_CMD_APP_SEND_SCR) + udelay(50000); /* wait 50 ms */ + + if (!(cmd->resp_type & MMC_RSP_PRESENT)) + flags = 0; + else if (cmd->resp_type & MMC_RSP_136) + flags = RSP_TYPE_LGHT136 | CICE_NOCHECK; + else if (cmd->resp_type & MMC_RSP_BUSY) + flags = RSP_TYPE_LGHT48B; + else + flags = RSP_TYPE_LGHT48; + + /* enable default flags */ + flags = flags | (CMD_TYPE_NORMAL | CICE_NOCHECK | CCCE_NOCHECK | + MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE | DE_DISABLE); + + if (cmd->resp_type & MMC_RSP_CRC) + flags |= CCCE_CHECK; + if (cmd->resp_type & MMC_RSP_OPCODE) + flags |= CICE_CHECK; + + if (data) { + if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) || + (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) { + flags |= (MSBS_MULTIBLK | BCE_ENABLE); + data->blocksize = 512; + writel(data->blocksize | (data->blocks << 16), + &mmc_base->blk); + } else + writel(data->blocksize | NBLK_STPCNT, &mmc_base->blk); + + if (data->flags & MMC_DATA_READ) + flags |= (DP_DATA | DDIR_READ); + else + flags |= (DP_DATA | DDIR_WRITE); + } + + writel(cmd->cmdarg, &mmc_base->arg); + udelay(20); /* To fix "No status update" error on eMMC */ + writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd); + + start = get_timer(0); + do { + mmc_stat = readl(&mmc_base->stat); + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s : timeout: No status update\n", __func__); + return TIMEOUT; + } + } while (!mmc_stat); + + if ((mmc_stat & IE_CTO) != 0) { + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); + return TIMEOUT; + } else if ((mmc_stat & ERRI_MASK) != 0) + return -1; + + if (mmc_stat & CC_MASK) { + writel(CC_MASK, &mmc_base->stat); + if (cmd->resp_type & MMC_RSP_PRESENT) { + if (cmd->resp_type & MMC_RSP_136) { + /* response type 2 */ + cmd->response[3] = readl(&mmc_base->rsp10); + cmd->response[2] = readl(&mmc_base->rsp32); + cmd->response[1] = readl(&mmc_base->rsp54); + cmd->response[0] = readl(&mmc_base->rsp76); + } else + /* response types 1, 1b, 3, 4, 5, 6 */ + cmd->response[0] = readl(&mmc_base->rsp10); + } + } + + if (data && (data->flags & MMC_DATA_READ)) { + mmc_read_data(mmc_base, data->dest, + data->blocksize * data->blocks); + } else if (data && (data->flags & MMC_DATA_WRITE)) { + mmc_write_data(mmc_base, data->src, + data->blocksize * data->blocks); + } + return 0; +} + +static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size) +{ + unsigned int *output_buf = (unsigned int *)buf; + unsigned int mmc_stat; + unsigned int count; + + /* + * Start Polled Read + */ + count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size; + count /= 4; + + while (size) { + ulong start = get_timer(0); + do { + mmc_stat = readl(&mmc_base->stat); + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for status!\n", + __func__); + return TIMEOUT; + } + } while (mmc_stat == 0); + + if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + + if ((mmc_stat & ERRI_MASK) != 0) + return 1; + + if (mmc_stat & BRR_MASK) { + unsigned int k; + + writel(readl(&mmc_base->stat) | BRR_MASK, + &mmc_base->stat); + for (k = 0; k < count; k++) { + *output_buf = readl(&mmc_base->data); + output_buf++; + } + size -= (count*4); + } + + if (mmc_stat & BWR_MASK) + writel(readl(&mmc_base->stat) | BWR_MASK, + &mmc_base->stat); + + if (mmc_stat & TC_MASK) { + writel(readl(&mmc_base->stat) | TC_MASK, + &mmc_base->stat); + break; + } + } + return 0; +} + +static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, + unsigned int size) +{ + unsigned int *input_buf = (unsigned int *)buf; + unsigned int mmc_stat; + unsigned int count; + + /* + * Start Polled Write + */ + count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size; + count /= 4; + + while (size) { + ulong start = get_timer(0); + do { + mmc_stat = readl(&mmc_base->stat); + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for status!\n", + __func__); + return TIMEOUT; + } + } while (mmc_stat == 0); + + if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + + if ((mmc_stat & ERRI_MASK) != 0) + return 1; + + if (mmc_stat & BWR_MASK) { + unsigned int k; + + writel(readl(&mmc_base->stat) | BWR_MASK, + &mmc_base->stat); + for (k = 0; k < count; k++) { + writel(*input_buf, &mmc_base->data); + input_buf++; + } + size -= (count*4); + } + + if (mmc_stat & BRR_MASK) + writel(readl(&mmc_base->stat) | BRR_MASK, + &mmc_base->stat); + + if (mmc_stat & TC_MASK) { + writel(readl(&mmc_base->stat) | TC_MASK, + &mmc_base->stat); + break; + } + } + return 0; +} + +static void omap_hsmmc_set_ios(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + unsigned int dsor = 0; + ulong start; + + mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr; + /* configue bus width */ + switch (mmc->bus_width) { + case 8: + writel(readl(&mmc_base->con) | DTW_8_BITMODE, + &mmc_base->con); + break; + + case 4: + writel(readl(&mmc_base->con) & ~DTW_8_BITMODE, + &mmc_base->con); + writel(readl(&mmc_base->hctl) | DTW_4_BITMODE, + &mmc_base->hctl); + break; + + case 1: + default: + writel(readl(&mmc_base->con) & ~DTW_8_BITMODE, + &mmc_base->con); + writel(readl(&mmc_base->hctl) & ~DTW_4_BITMODE, + &mmc_base->hctl); + break; + } + + /* configure clock with 96Mhz system clock. + */ + if (mmc->clock != 0) { + dsor = (MMC_CLOCK_REFERENCE * 1000000 / mmc->clock); + if ((MMC_CLOCK_REFERENCE * 1000000) / dsor > mmc->clock) + dsor++; + } + + mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK), + (ICE_STOP | DTO_15THDTO | CEN_DISABLE)); + + mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, + (dsor << CLKD_OFFSET) | ICE_OSCILLATE); + + start = get_timer(0); + while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for ics!\n", __func__); + return; + } + } + writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); +} + +#ifdef OMAP_HSMMC_USE_GPIO +static int omap_hsmmc_getcd(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv_data = mmc->priv; + int cd_gpio; + + /* if no CD return as 1 */ + cd_gpio = priv_data->cd_gpio; + if (cd_gpio < 0) + return 1; + + return gpio_get_value(cd_gpio); +} + +static int omap_hsmmc_getwp(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv_data = mmc->priv; + int wp_gpio; + + /* if no WP return as 0 */ + wp_gpio = priv_data->wp_gpio; + if (wp_gpio < 0) + return 0; + + return gpio_get_value(wp_gpio); +} +#endif + +static const struct mmc_ops omap_hsmmc_ops = { + .send_cmd = omap_hsmmc_send_cmd, + .set_ios = omap_hsmmc_set_ios, + .init = omap_hsmmc_init_setup, +#ifdef OMAP_HSMMC_USE_GPIO + .getcd = omap_hsmmc_getcd, + .getwp = omap_hsmmc_getwp, +#endif +}; + +int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, + int wp_gpio) +{ + struct mmc *mmc; + struct omap_hsmmc_data *priv_data; + struct mmc_config *cfg; + uint host_caps_val; + + priv_data = malloc(sizeof(*priv_data)); + if (priv_data == NULL) + return -1; + + host_caps_val = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | + MMC_MODE_HC; + + switch (dev_index) { + case 0: + priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE; + break; +#ifdef OMAP_HSMMC2_BASE + case 1: + priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE; +#if (defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ + defined(CONFIG_DRA7XX)) && defined(CONFIG_HSMMC2_8BIT) + /* Enable 8-bit interface for eMMC on OMAP4/5 or DRA7XX */ + host_caps_val |= MMC_MODE_8BIT; +#endif + break; +#endif +#ifdef OMAP_HSMMC3_BASE + case 2: + priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE; +#if defined(CONFIG_DRA7XX) && defined(CONFIG_HSMMC3_8BIT) + /* Enable 8-bit interface for eMMC on DRA7XX */ + host_caps_val |= MMC_MODE_8BIT; +#endif + break; +#endif + default: + priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE; + return 1; + } +#ifdef OMAP_HSMMC_USE_GPIO + /* on error gpio values are set to -1, which is what we want */ + priv_data->cd_gpio = omap_mmc_setup_gpio_in(cd_gpio, "mmc_cd"); + priv_data->wp_gpio = omap_mmc_setup_gpio_in(wp_gpio, "mmc_wp"); +#endif + + cfg = &priv_data->cfg; + + cfg->name = "OMAP SD/MMC"; + cfg->ops = &omap_hsmmc_ops; + + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + cfg->host_caps = host_caps_val & ~host_caps_mask; + + cfg->f_min = 400000; + + if (f_max != 0) + cfg->f_max = f_max; + else { + if (cfg->host_caps & MMC_MODE_HS) { + if (cfg->host_caps & MMC_MODE_HS_52MHz) + cfg->f_max = 52000000; + else + cfg->f_max = 26000000; + } else + cfg->f_max = 20000000; + } + + cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + +#if defined(CONFIG_OMAP34XX) + /* + * Silicon revs 2.1 and older do not support multiblock transfers. + */ + if ((get_cpu_family() == CPU_OMAP34XX) && (get_cpu_rev() <= CPU_3XX_ES21)) + cfg->b_max = 1; +#endif + mmc = mmc_create(cfg, priv_data); + if (mmc == NULL) + return -1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/pxa_mmc_gen.c b/qemu/roms/u-boot/drivers/mmc/pxa_mmc_gen.c new file mode 100644 index 000000000..1f297571e --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/pxa_mmc_gen.c @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2010 Marek Vasut + * + * Loosely based on the old code and Linux's PXA MMC driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* PXAMMC Generic default config for various CPUs */ +#if defined(CONFIG_CPU_PXA25X) +#define PXAMMC_FIFO_SIZE 1 +#define PXAMMC_MIN_SPEED 312500 +#define PXAMMC_MAX_SPEED 20000000 +#define PXAMMC_HOST_CAPS (0) +#elif defined(CONFIG_CPU_PXA27X) +#define PXAMMC_CRC_SKIP +#define PXAMMC_FIFO_SIZE 32 +#define PXAMMC_MIN_SPEED 304000 +#define PXAMMC_MAX_SPEED 19500000 +#define PXAMMC_HOST_CAPS (MMC_MODE_4BIT) +#elif defined(CONFIG_CPU_MONAHANS) +#define PXAMMC_FIFO_SIZE 32 +#define PXAMMC_MIN_SPEED 304000 +#define PXAMMC_MAX_SPEED 26000000 +#define PXAMMC_HOST_CAPS (MMC_MODE_4BIT | MMC_MODE_HS) +#else +#error "This CPU isn't supported by PXA MMC!" +#endif + +#define MMC_STAT_ERRORS \ + (MMC_STAT_RES_CRC_ERROR | MMC_STAT_SPI_READ_ERROR_TOKEN | \ + MMC_STAT_CRC_READ_ERROR | MMC_STAT_TIME_OUT_RESPONSE | \ + MMC_STAT_READ_TIME_OUT | MMC_STAT_CRC_WRITE_ERROR) + +/* 1 millisecond (in wait cycles below it's 100 x 10uS waits) */ +#define PXA_MMC_TIMEOUT 100 + +struct pxa_mmc_priv { + struct pxa_mmc_regs *regs; +}; + +/* Wait for bit to be set */ +static int pxa_mmc_wait(struct mmc *mmc, uint32_t mask) +{ + struct pxa_mmc_priv *priv = mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + unsigned int timeout = PXA_MMC_TIMEOUT; + + /* Wait for bit to be set */ + while (--timeout) { + if (readl(®s->stat) & mask) + break; + udelay(10); + } + + if (!timeout) + return -ETIMEDOUT; + + return 0; +} + +static int pxa_mmc_stop_clock(struct mmc *mmc) +{ + struct pxa_mmc_priv *priv = mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + unsigned int timeout = PXA_MMC_TIMEOUT; + + /* If the clock aren't running, exit */ + if (!(readl(®s->stat) & MMC_STAT_CLK_EN)) + return 0; + + /* Tell the controller to turn off the clock */ + writel(MMC_STRPCL_STOP_CLK, ®s->strpcl); + + /* Wait until the clock are off */ + while (--timeout) { + if (!(readl(®s->stat) & MMC_STAT_CLK_EN)) + break; + udelay(10); + } + + /* The clock refused to stop, scream and die a painful death */ + if (!timeout) + return -ETIMEDOUT; + + /* The clock stopped correctly */ + return 0; +} + +static int pxa_mmc_start_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + uint32_t cmdat) +{ + struct pxa_mmc_priv *priv = mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + int ret; + + /* The card can send a "busy" response */ + if (cmd->resp_type & MMC_RSP_BUSY) + cmdat |= MMC_CMDAT_BUSY; + + /* Inform the controller about response type */ + switch (cmd->resp_type) { + case MMC_RSP_R1: + case MMC_RSP_R1b: + cmdat |= MMC_CMDAT_R1; + break; + case MMC_RSP_R2: + cmdat |= MMC_CMDAT_R2; + break; + case MMC_RSP_R3: + cmdat |= MMC_CMDAT_R3; + break; + default: + break; + } + + /* Load command and it's arguments into the controller */ + writel(cmd->cmdidx, ®s->cmd); + writel(cmd->cmdarg >> 16, ®s->argh); + writel(cmd->cmdarg & 0xffff, ®s->argl); + writel(cmdat, ®s->cmdat); + + /* Start the controller clock and wait until they are started */ + writel(MMC_STRPCL_START_CLK, ®s->strpcl); + + ret = pxa_mmc_wait(mmc, MMC_STAT_CLK_EN); + if (ret) + return ret; + + /* Correct and happy end */ + return 0; +} + +static int pxa_mmc_cmd_done(struct mmc *mmc, struct mmc_cmd *cmd) +{ + struct pxa_mmc_priv *priv = mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t a, b, c; + int i; + int stat; + + /* Read the controller status */ + stat = readl(®s->stat); + + /* + * Linux says: + * Did I mention this is Sick. We always need to + * discard the upper 8 bits of the first 16-bit word. + */ + a = readl(®s->res) & 0xffff; + for (i = 0; i < 4; i++) { + b = readl(®s->res) & 0xffff; + c = readl(®s->res) & 0xffff; + cmd->response[i] = (a << 24) | (b << 8) | (c >> 8); + a = c; + } + + /* The command response didn't arrive */ + if (stat & MMC_STAT_TIME_OUT_RESPONSE) + return -ETIMEDOUT; + else if (stat & MMC_STAT_RES_CRC_ERROR + && cmd->resp_type & MMC_RSP_CRC) { +#ifdef PXAMMC_CRC_SKIP + if (cmd->resp_type & MMC_RSP_136 + && cmd->response[0] & (1 << 31)) + printf("Ignoring CRC, this may be dangerous!\n"); + else +#endif + return -EILSEQ; + } + + /* The command response was successfully read */ + return 0; +} + +static int pxa_mmc_do_read_xfer(struct mmc *mmc, struct mmc_data *data) +{ + struct pxa_mmc_priv *priv = mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t len; + uint32_t *buf = (uint32_t *)data->dest; + int size; + int ret; + + len = data->blocks * data->blocksize; + + while (len) { + /* The controller has data ready */ + if (readl(®s->i_reg) & MMC_I_REG_RXFIFO_RD_REQ) { + size = min(len, PXAMMC_FIFO_SIZE); + len -= size; + size /= 4; + + /* Read data into the buffer */ + while (size--) + *buf++ = readl(®s->rxfifo); + + } + + if (readl(®s->stat) & MMC_STAT_ERRORS) + return -EIO; + } + + /* Wait for the transmission-done interrupt */ + ret = pxa_mmc_wait(mmc, MMC_STAT_DATA_TRAN_DONE); + if (ret) + return ret; + + return 0; +} + +static int pxa_mmc_do_write_xfer(struct mmc *mmc, struct mmc_data *data) +{ + struct pxa_mmc_priv *priv = mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t len; + uint32_t *buf = (uint32_t *)data->src; + int size; + int ret; + + len = data->blocks * data->blocksize; + + while (len) { + /* The controller is ready to receive data */ + if (readl(®s->i_reg) & MMC_I_REG_TXFIFO_WR_REQ) { + size = min(len, PXAMMC_FIFO_SIZE); + len -= size; + size /= 4; + + while (size--) + writel(*buf++, ®s->txfifo); + + if (min(len, PXAMMC_FIFO_SIZE) < 32) + writel(MMC_PRTBUF_BUF_PART_FULL, ®s->prtbuf); + } + + if (readl(®s->stat) & MMC_STAT_ERRORS) + return -EIO; + } + + /* Wait for the transmission-done interrupt */ + ret = pxa_mmc_wait(mmc, MMC_STAT_DATA_TRAN_DONE); + if (ret) + return ret; + + /* Wait until the data are really written to the card */ + ret = pxa_mmc_wait(mmc, MMC_STAT_PRG_DONE); + if (ret) + return ret; + + return 0; +} + +static int pxa_mmc_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct pxa_mmc_priv *priv = mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t cmdat = 0; + int ret; + + /* Stop the controller */ + ret = pxa_mmc_stop_clock(mmc); + if (ret) + return ret; + + /* If we're doing data transfer, configure the controller accordingly */ + if (data) { + writel(data->blocks, ®s->nob); + writel(data->blocksize, ®s->blklen); + /* This delay can be optimized, but stick with max value */ + writel(0xffff, ®s->rdto); + cmdat |= MMC_CMDAT_DATA_EN; + if (data->flags & MMC_DATA_WRITE) + cmdat |= MMC_CMDAT_WRITE; + } + + /* Run in 4bit mode if the card can do it */ + if (mmc->bus_width == 4) + cmdat |= MMC_CMDAT_SD_4DAT; + + /* Execute the command */ + ret = pxa_mmc_start_cmd(mmc, cmd, cmdat); + if (ret) + return ret; + + /* Wait until the command completes */ + ret = pxa_mmc_wait(mmc, MMC_STAT_END_CMD_RES); + if (ret) + return ret; + + /* Read back the result */ + ret = pxa_mmc_cmd_done(mmc, cmd); + if (ret) + return ret; + + /* In case there was a data transfer scheduled, do it */ + if (data) { + if (data->flags & MMC_DATA_WRITE) + pxa_mmc_do_write_xfer(mmc, data); + else + pxa_mmc_do_read_xfer(mmc, data); + } + + return 0; +} + +static void pxa_mmc_set_ios(struct mmc *mmc) +{ + struct pxa_mmc_priv *priv = mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t tmp; + uint32_t pxa_mmc_clock; + + if (!mmc->clock) { + pxa_mmc_stop_clock(mmc); + return; + } + + /* PXA3xx can do 26MHz with special settings. */ + if (mmc->clock == 26000000) { + writel(0x7, ®s->clkrt); + return; + } + + /* Set clock to the card the usual way. */ + pxa_mmc_clock = 0; + tmp = mmc->cfg->f_max / mmc->clock; + tmp += tmp % 2; + + while (tmp > 1) { + pxa_mmc_clock++; + tmp >>= 1; + } + + writel(pxa_mmc_clock, ®s->clkrt); +} + +static int pxa_mmc_init(struct mmc *mmc) +{ + struct pxa_mmc_priv *priv = mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + + /* Make sure the clock are stopped */ + pxa_mmc_stop_clock(mmc); + + /* Turn off SPI mode */ + writel(0, ®s->spi); + + /* Set up maximum timeout to wait for command response */ + writel(MMC_RES_TO_MAX_MASK, ®s->resto); + + /* Mask all interrupts */ + writel(~(MMC_I_MASK_TXFIFO_WR_REQ | MMC_I_MASK_RXFIFO_RD_REQ), + ®s->i_mask); + return 0; +} + +static const struct mmc_ops pxa_mmc_ops = { + .send_cmd = pxa_mmc_request, + .set_ios = pxa_mmc_set_ios, + .init = pxa_mmc_init, +}; + +static struct mmc_config pxa_mmc_cfg = { + .name = "PXA MMC", + .ops = &pxa_mmc_ops, + .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, + .f_max = PXAMMC_MAX_SPEED, + .f_min = PXAMMC_MIN_SPEED, + .host_caps = PXAMMC_HOST_CAPS, + .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, +}; + +int pxa_mmc_register(int card_index) +{ + struct mmc *mmc; + struct pxa_mmc_priv *priv; + uint32_t reg; + int ret = -ENOMEM; + + priv = malloc(sizeof(struct pxa_mmc_priv)); + if (!priv) + goto err0; + + memset(priv, 0, sizeof(*priv)); + + switch (card_index) { + case 0: + priv->regs = (struct pxa_mmc_regs *)MMC0_BASE; + break; + case 1: + priv->regs = (struct pxa_mmc_regs *)MMC1_BASE; + break; + default: + ret = -EINVAL; + printf("PXA MMC: Invalid MMC controller ID (card_index = %d)\n", + card_index); + goto err1; + } + +#ifndef CONFIG_CPU_MONAHANS /* PXA2xx */ + reg = readl(CKEN); + reg |= CKEN12_MMC; + writel(reg, CKEN); +#else /* PXA3xx */ + reg = readl(CKENA); + reg |= CKENA_12_MMC0 | CKENA_13_MMC1; + writel(reg, CKENA); +#endif + + mmc = mmc_create(&pxa_mmc_cfg, priv); + if (mmc == NULL) + goto err1; + + return 0; + +err1: + free(priv); +err0: + return ret; +} diff --git a/qemu/roms/u-boot/drivers/mmc/s5p_sdhci.c b/qemu/roms/u-boot/drivers/mmc/s5p_sdhci.c new file mode 100644 index 000000000..ccae4ccae --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/s5p_sdhci.c @@ -0,0 +1,217 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Jaehoon Chung + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF_CONTROL +#include +#endif + +static char *S5P_NAME = "SAMSUNG SDHCI"; +static void s5p_sdhci_set_control_reg(struct sdhci_host *host) +{ + unsigned long val, ctrl; + /* + * SELCLKPADDS[17:16] + * 00 = 2mA + * 01 = 4mA + * 10 = 7mA + * 11 = 9mA + */ + sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4); + + val = sdhci_readl(host, SDHCI_CONTROL2); + val &= SDHCI_CTRL2_SELBASECLK_SHIFT; + + val |= SDHCI_CTRL2_ENSTAASYNCCLR | + SDHCI_CTRL2_ENCMDCNFMSK | + SDHCI_CTRL2_ENFBCLKRX | + SDHCI_CTRL2_ENCLKOUTHOLD; + + sdhci_writel(host, val, SDHCI_CONTROL2); + + /* + * FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7] + * FCSel[1:0] : Rx Feedback Clock Delay Control + * Inverter delay means10ns delay if SDCLK 50MHz setting + * 01 = Delay1 (basic delay) + * 11 = Delay2 (basic delay + 2ns) + * 00 = Delay3 (inverter delay) + * 10 = Delay4 (inverter delay + 2ns) + */ + val = SDHCI_CTRL3_FCSEL0 | SDHCI_CTRL3_FCSEL1; + sdhci_writel(host, val, SDHCI_CONTROL3); + + /* + * SELBASECLK[5:4] + * 00/01 = HCLK + * 10 = EPLL + * 11 = XTI or XEXTCLK + */ + ctrl = sdhci_readl(host, SDHCI_CONTROL2); + ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3); + ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2); + sdhci_writel(host, ctrl, SDHCI_CONTROL2); +} + +int s5p_sdhci_init(u32 regbase, int index, int bus_width) +{ + struct sdhci_host *host = NULL; + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("sdhci__host malloc fail!\n"); + return 1; + } + + host->name = S5P_NAME; + host->ioaddr = (void *)regbase; + + host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | + SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8; + host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + host->set_control_reg = &s5p_sdhci_set_control_reg; + host->set_clock = set_mmc_clk; + host->index = index; + + host->host_caps = MMC_MODE_HC; + if (bus_width == 8) + host->host_caps |= MMC_MODE_8BIT; + + return add_sdhci(host, 52000000, 400000); +} + +#ifdef CONFIG_OF_CONTROL +struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; + +static int do_sdhci_init(struct sdhci_host *host) +{ + int dev_id, flag; + int err = 0; + + flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; + dev_id = host->index + PERIPH_ID_SDMMC0; + + if (fdt_gpio_isvalid(&host->pwr_gpio)) { + gpio_direction_output(host->pwr_gpio.gpio, 1); + err = exynos_pinmux_config(dev_id, flag); + if (err) { + debug("MMC not configured\n"); + return err; + } + } + + if (fdt_gpio_isvalid(&host->cd_gpio)) { + gpio_direction_output(host->cd_gpio.gpio, 0xf); + if (gpio_get_value(host->cd_gpio.gpio)) + return -ENODEV; + + err = exynos_pinmux_config(dev_id, flag); + if (err) { + printf("external SD not configured\n"); + return err; + } + } + + host->name = S5P_NAME; + + host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | + SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_WAIT_SEND_CMD; + host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + host->set_control_reg = &s5p_sdhci_set_control_reg; + host->set_clock = set_mmc_clk; + + host->host_caps = MMC_MODE_HC; + + return add_sdhci(host, 52000000, 400000); +} + +static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) +{ + int bus_width, dev_id; + unsigned int base; + + /* Get device id */ + dev_id = pinmux_decode_periph_id(blob, node); + if (dev_id < PERIPH_ID_SDMMC0 && dev_id > PERIPH_ID_SDMMC3) { + debug("MMC: Can't get device id\n"); + return -1; + } + host->index = dev_id - PERIPH_ID_SDMMC0; + + /* Get bus width */ + bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); + if (bus_width <= 0) { + debug("MMC: Can't get bus-width\n"); + return -1; + } + host->bus_width = bus_width; + + /* Get the base address from the device node */ + base = fdtdec_get_addr(blob, node, "reg"); + if (!base) { + debug("MMC: Can't get base address\n"); + return -1; + } + host->ioaddr = (void *)base; + + fdtdec_decode_gpio(blob, node, "pwr-gpios", &host->pwr_gpio); + fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); + + return 0; +} + +static int process_nodes(const void *blob, int node_list[], int count) +{ + struct sdhci_host *host; + int i, node; + + debug("%s: count = %d\n", __func__, count); + + /* build sdhci_host[] for each controller */ + for (i = 0; i < count; i++) { + node = node_list[i]; + if (node <= 0) + continue; + + host = &sdhci_host[i]; + + if (sdhci_get_config(blob, node, host)) { + printf("%s: failed to decode dev %d\n", __func__, i); + return -1; + } + do_sdhci_init(host); + } + return 0; +} + +int exynos_mmc_init(const void *blob) +{ + int count; + int node_list[SDHCI_MAX_HOSTS]; + + count = fdtdec_find_aliases_for_id(blob, "mmc", + COMPAT_SAMSUNG_EXYNOS_MMC, node_list, + SDHCI_MAX_HOSTS); + + process_nodes(blob, node_list, count); + + return 1; +} +#endif diff --git a/qemu/roms/u-boot/drivers/mmc/sdhci.c b/qemu/roms/u-boot/drivers/mmc/sdhci.c new file mode 100644 index 000000000..3125d13ba --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/sdhci.c @@ -0,0 +1,513 @@ +/* + * Copyright 2011, Marvell Semiconductor Inc. + * Lei Wen + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Back ported to the 8xx platform (from the 8260 platform) by + * Murray.Jensen@cmst.csiro.au, 27-Jan-01. + */ + +#include +#include +#include +#include + +void *aligned_buffer; + +static void sdhci_reset(struct sdhci_host *host, u8 mask) +{ + unsigned long timeout; + + /* Wait max 100 ms */ + timeout = 100; + sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); + while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) { + if (timeout == 0) { + printf("%s: Reset 0x%x never completed.\n", + __func__, (int)mask); + return; + } + timeout--; + udelay(1000); + } +} + +static void sdhci_cmd_done(struct sdhci_host *host, struct mmc_cmd *cmd) +{ + int i; + if (cmd->resp_type & MMC_RSP_136) { + /* CRC is stripped so we need to do some shifting. */ + for (i = 0; i < 4; i++) { + cmd->response[i] = sdhci_readl(host, + SDHCI_RESPONSE + (3-i)*4) << 8; + if (i != 3) + cmd->response[i] |= sdhci_readb(host, + SDHCI_RESPONSE + (3-i)*4-1); + } + } else { + cmd->response[0] = sdhci_readl(host, SDHCI_RESPONSE); + } +} + +static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data) +{ + int i; + char *offs; + for (i = 0; i < data->blocksize; i += 4) { + offs = data->dest + i; + if (data->flags == MMC_DATA_READ) + *(u32 *)offs = sdhci_readl(host, SDHCI_BUFFER); + else + sdhci_writel(host, *(u32 *)offs, SDHCI_BUFFER); + } +} + +static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, + unsigned int start_addr) +{ + unsigned int stat, rdy, mask, timeout, block = 0; +#ifdef CONFIG_MMC_SDMA + unsigned char ctrl; + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_DMA_MASK; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +#endif + + timeout = 1000000; + rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL; + mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE; + do { + stat = sdhci_readl(host, SDHCI_INT_STATUS); + if (stat & SDHCI_INT_ERROR) { + printf("%s: Error detected in status(0x%X)!\n", + __func__, stat); + return -1; + } + if (stat & rdy) { + if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)) + continue; + sdhci_writel(host, rdy, SDHCI_INT_STATUS); + sdhci_transfer_pio(host, data); + data->dest += data->blocksize; + if (++block >= data->blocks) + break; + } +#ifdef CONFIG_MMC_SDMA + if (stat & SDHCI_INT_DMA_END) { + sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS); + start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1); + start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE; + sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); + } +#endif + if (timeout-- > 0) + udelay(10); + else { + printf("%s: Transfer data timeout\n", __func__); + return -1; + } + } while (!(stat & SDHCI_INT_DATA_END)); + return 0; +} + +/* + * No command will be sent by driver if card is busy, so driver must wait + * for card ready state. + * Every time when card is busy after timeout then (last) timeout value will be + * increased twice but only if it doesn't exceed global defined maximum. + * Each function call will use last timeout value. Max timeout can be redefined + * in board config file. + */ +#ifndef CONFIG_SDHCI_CMD_MAX_TIMEOUT +#define CONFIG_SDHCI_CMD_MAX_TIMEOUT 3200 +#endif +#define CONFIG_SDHCI_CMD_DEFAULT_TIMEOUT 100 + +int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct sdhci_host *host = mmc->priv; + unsigned int stat = 0; + int ret = 0; + int trans_bytes = 0, is_aligned = 1; + u32 mask, flags, mode; + unsigned int time = 0, start_addr = 0; + unsigned int retry = 10000; + int mmc_dev = mmc->block_dev.dev; + + /* Timeout unit - ms */ + static unsigned int cmd_timeout = CONFIG_SDHCI_CMD_DEFAULT_TIMEOUT; + + sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); + mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; + + /* We shouldn't wait for data inihibit for stop commands, even + though they might use busy signaling */ + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + mask &= ~SDHCI_DATA_INHIBIT; + + while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { + if (time >= cmd_timeout) { + printf("%s: MMC: %d busy ", __func__, mmc_dev); + if (2 * cmd_timeout <= CONFIG_SDHCI_CMD_MAX_TIMEOUT) { + cmd_timeout += cmd_timeout; + printf("timeout increasing to: %u ms.\n", + cmd_timeout); + } else { + puts("timeout.\n"); + return COMM_ERR; + } + } + time++; + udelay(1000); + } + + mask = SDHCI_INT_RESPONSE; + if (!(cmd->resp_type & MMC_RSP_PRESENT)) + flags = SDHCI_CMD_RESP_NONE; + else if (cmd->resp_type & MMC_RSP_136) + flags = SDHCI_CMD_RESP_LONG; + else if (cmd->resp_type & MMC_RSP_BUSY) { + flags = SDHCI_CMD_RESP_SHORT_BUSY; + mask |= SDHCI_INT_DATA_END; + } else + flags = SDHCI_CMD_RESP_SHORT; + + if (cmd->resp_type & MMC_RSP_CRC) + flags |= SDHCI_CMD_CRC; + if (cmd->resp_type & MMC_RSP_OPCODE) + flags |= SDHCI_CMD_INDEX; + if (data) + flags |= SDHCI_CMD_DATA; + + /* Set Transfer mode regarding to data flag */ + if (data != 0) { + sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); + mode = SDHCI_TRNS_BLK_CNT_EN; + trans_bytes = data->blocks * data->blocksize; + if (data->blocks > 1) + mode |= SDHCI_TRNS_MULTI; + + if (data->flags == MMC_DATA_READ) + mode |= SDHCI_TRNS_READ; + +#ifdef CONFIG_MMC_SDMA + if (data->flags == MMC_DATA_READ) + start_addr = (unsigned int)data->dest; + else + start_addr = (unsigned int)data->src; + if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && + (start_addr & 0x7) != 0x0) { + is_aligned = 0; + start_addr = (unsigned int)aligned_buffer; + if (data->flags != MMC_DATA_READ) + memcpy(aligned_buffer, data->src, trans_bytes); + } + + sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); + mode |= SDHCI_TRNS_DMA; +#endif + sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, + data->blocksize), + SDHCI_BLOCK_SIZE); + sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); + sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); + } + + sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); +#ifdef CONFIG_MMC_SDMA + flush_cache(start_addr, trans_bytes); +#endif + sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); + do { + stat = sdhci_readl(host, SDHCI_INT_STATUS); + if (stat & SDHCI_INT_ERROR) + break; + if (--retry == 0) + break; + } while ((stat & mask) != mask); + + if (retry == 0) { + if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) + return 0; + else { + printf("%s: Timeout for status update!\n", __func__); + return TIMEOUT; + } + } + + if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { + sdhci_cmd_done(host, cmd); + sdhci_writel(host, mask, SDHCI_INT_STATUS); + } else + ret = -1; + + if (!ret && data) + ret = sdhci_transfer_data(host, data, start_addr); + + if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) + udelay(1000); + + stat = sdhci_readl(host, SDHCI_INT_STATUS); + sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); + if (!ret) { + if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && + !is_aligned && (data->flags == MMC_DATA_READ)) + memcpy(data->dest, aligned_buffer, trans_bytes); + return 0; + } + + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + if (stat & SDHCI_INT_TIMEOUT) + return TIMEOUT; + else + return COMM_ERR; +} + +static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) +{ + struct sdhci_host *host = mmc->priv; + unsigned int div, clk, timeout; + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) + return 0; + + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { + /* Version 3.00 divisors must be a multiple of 2. */ + if (mmc->cfg->f_max <= clock) + div = 1; + else { + for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { + if ((mmc->cfg->f_max / div) <= clock) + break; + } + } + } else { + /* Version 2.00 divisors must be a power of 2. */ + for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) { + if ((mmc->cfg->f_max / div) <= clock) + break; + } + } + div >>= 1; + + if (host->set_clock) + host->set_clock(host->index, div); + + clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; + clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) + << SDHCI_DIVIDER_HI_SHIFT; + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + /* Wait max 20 ms */ + timeout = 20; + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { + printf("%s: Internal clock never stabilised.\n", + __func__); + return -1; + } + timeout--; + udelay(1000); + } + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + return 0; +} + +static void sdhci_set_power(struct sdhci_host *host, unsigned short power) +{ + u8 pwr = 0; + + if (power != (unsigned short)-1) { + switch (1 << power) { + case MMC_VDD_165_195: + pwr = SDHCI_POWER_180; + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + pwr = SDHCI_POWER_300; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + pwr = SDHCI_POWER_330; + break; + } + } + + if (pwr == 0) { + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + return; + } + + if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + + pwr |= SDHCI_POWER_ON; + + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); +} + +void sdhci_set_ios(struct mmc *mmc) +{ + u32 ctrl; + struct sdhci_host *host = mmc->priv; + + if (host->set_control_reg) + host->set_control_reg(host); + + if (mmc->clock != host->clock) + sdhci_set_clock(mmc, mmc->clock); + + /* Set bus width */ + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + if (mmc->bus_width == 8) { + ctrl &= ~SDHCI_CTRL_4BITBUS; + if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) || + (host->quirks & SDHCI_QUIRK_USE_WIDE8)) + ctrl |= SDHCI_CTRL_8BITBUS; + } else { + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) + ctrl &= ~SDHCI_CTRL_8BITBUS; + if (mmc->bus_width == 4) + ctrl |= SDHCI_CTRL_4BITBUS; + else + ctrl &= ~SDHCI_CTRL_4BITBUS; + } + + if (mmc->clock > 26000000) + ctrl |= SDHCI_CTRL_HISPD; + else + ctrl &= ~SDHCI_CTRL_HISPD; + + if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT) + ctrl &= ~SDHCI_CTRL_HISPD; + + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +} + +int sdhci_init(struct mmc *mmc) +{ + struct sdhci_host *host = mmc->priv; + + if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !aligned_buffer) { + aligned_buffer = memalign(8, 512*1024); + if (!aligned_buffer) { + printf("%s: Aligned buffer alloc failed!!!\n", + __func__); + return -1; + } + } + + sdhci_set_power(host, fls(mmc->cfg->voltages) - 1); + + if (host->quirks & SDHCI_QUIRK_NO_CD) { + unsigned int status; + + sdhci_writel(host, SDHCI_CTRL_CD_TEST_INS | SDHCI_CTRL_CD_TEST, + SDHCI_HOST_CONTROL); + + status = sdhci_readl(host, SDHCI_PRESENT_STATE); + while ((!(status & SDHCI_CARD_PRESENT)) || + (!(status & SDHCI_CARD_STATE_STABLE)) || + (!(status & SDHCI_CARD_DETECT_PIN_LEVEL))) + status = sdhci_readl(host, SDHCI_PRESENT_STATE); + } + + /* Enable only interrupts served by the SD controller */ + sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK, + SDHCI_INT_ENABLE); + /* Mask all sdhci interrupt sources */ + sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE); + + return 0; +} + + +static const struct mmc_ops sdhci_ops = { + .send_cmd = sdhci_send_command, + .set_ios = sdhci_set_ios, + .init = sdhci_init, +}; + +int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) +{ + unsigned int caps; + + host->cfg.name = host->name; + host->cfg.ops = &sdhci_ops; + + caps = sdhci_readl(host, SDHCI_CAPABILITIES); +#ifdef CONFIG_MMC_SDMA + if (!(caps & SDHCI_CAN_DO_SDMA)) { + printf("%s: Your controller doesn't support SDMA!!\n", + __func__); + return -1; + } +#endif + + if (max_clk) + host->cfg.f_max = max_clk; + else { + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) + host->cfg.f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK) + >> SDHCI_CLOCK_BASE_SHIFT; + else + host->cfg.f_max = (caps & SDHCI_CLOCK_BASE_MASK) + >> SDHCI_CLOCK_BASE_SHIFT; + host->cfg.f_max *= 1000000; + } + if (host->cfg.f_max == 0) { + printf("%s: Hardware doesn't specify base clock frequency\n", + __func__); + return -1; + } + if (min_clk) + host->cfg.f_min = min_clk; + else { + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) + host->cfg.f_min = host->cfg.f_max / + SDHCI_MAX_DIV_SPEC_300; + else + host->cfg.f_min = host->cfg.f_max / + SDHCI_MAX_DIV_SPEC_200; + } + + host->cfg.voltages = 0; + if (caps & SDHCI_CAN_VDD_330) + host->cfg.voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; + if (caps & SDHCI_CAN_VDD_300) + host->cfg.voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; + if (caps & SDHCI_CAN_VDD_180) + host->cfg.voltages |= MMC_VDD_165_195; + + if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE) + host->cfg.voltages |= host->voltages; + + host->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { + if (caps & SDHCI_CAN_DO_8BIT) + host->cfg.host_caps |= MMC_MODE_8BIT; + } + if (host->host_caps) + host->cfg.host_caps |= host->host_caps; + + host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + + sdhci_reset(host, SDHCI_RESET_ALL); + + host->mmc = mmc_create(&host->cfg, host); + if (host->mmc == NULL) { + printf("%s: mmc create fail!\n", __func__); + return -1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/sh_mmcif.c b/qemu/roms/u-boot/drivers/mmc/sh_mmcif.c new file mode 100644 index 000000000..ed83a14c2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/sh_mmcif.c @@ -0,0 +1,609 @@ +/* + * MMCIF driver. + * + * Copyright (C) 2011 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sh_mmcif.h" + +#define DRIVER_NAME "sh_mmcif" + +static int sh_mmcif_intr(void *dev_id) +{ + struct sh_mmcif_host *host = dev_id; + u32 state = 0; + + state = sh_mmcif_read(&host->regs->ce_int); + state &= sh_mmcif_read(&host->regs->ce_int_mask); + + if (state & INT_RBSYE) { + sh_mmcif_write(~(INT_RBSYE | INT_CRSPE), &host->regs->ce_int); + sh_mmcif_bitclr(MASK_MRBSYE, &host->regs->ce_int_mask); + goto end; + } else if (state & INT_CRSPE) { + sh_mmcif_write(~INT_CRSPE, &host->regs->ce_int); + sh_mmcif_bitclr(MASK_MCRSPE, &host->regs->ce_int_mask); + /* one more interrupt (INT_RBSYE) */ + if (sh_mmcif_read(&host->regs->ce_cmd_set) & CMD_SET_RBSY) + return -EAGAIN; + goto end; + } else if (state & INT_BUFREN) { + sh_mmcif_write(~INT_BUFREN, &host->regs->ce_int); + sh_mmcif_bitclr(MASK_MBUFREN, &host->regs->ce_int_mask); + goto end; + } else if (state & INT_BUFWEN) { + sh_mmcif_write(~INT_BUFWEN, &host->regs->ce_int); + sh_mmcif_bitclr(MASK_MBUFWEN, &host->regs->ce_int_mask); + goto end; + } else if (state & INT_CMD12DRE) { + sh_mmcif_write(~(INT_CMD12DRE | INT_CMD12RBE | INT_CMD12CRE | + INT_BUFRE), &host->regs->ce_int); + sh_mmcif_bitclr(MASK_MCMD12DRE, &host->regs->ce_int_mask); + goto end; + } else if (state & INT_BUFRE) { + sh_mmcif_write(~INT_BUFRE, &host->regs->ce_int); + sh_mmcif_bitclr(MASK_MBUFRE, &host->regs->ce_int_mask); + goto end; + } else if (state & INT_DTRANE) { + sh_mmcif_write(~INT_DTRANE, &host->regs->ce_int); + sh_mmcif_bitclr(MASK_MDTRANE, &host->regs->ce_int_mask); + goto end; + } else if (state & INT_CMD12RBE) { + sh_mmcif_write(~(INT_CMD12RBE | INT_CMD12CRE), + &host->regs->ce_int); + sh_mmcif_bitclr(MASK_MCMD12RBE, &host->regs->ce_int_mask); + goto end; + } else if (state & INT_ERR_STS) { + /* err interrupts */ + sh_mmcif_write(~state, &host->regs->ce_int); + sh_mmcif_bitclr(state, &host->regs->ce_int_mask); + goto err; + } else + return -EAGAIN; + +err: + host->sd_error = 1; + debug("%s: int err state = %08x\n", DRIVER_NAME, state); +end: + host->wait_int = 1; + return 0; +} + +static int mmcif_wait_interrupt_flag(struct sh_mmcif_host *host) +{ + int timeout = 10000000; + + while (1) { + timeout--; + if (timeout < 0) { + printf("timeout\n"); + return 0; + } + + if (!sh_mmcif_intr(host)) + break; + + udelay(1); /* 1 usec */ + } + + return 1; /* Return value: NOT 0 = complete waiting */ +} + +static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) +{ + int i; + + sh_mmcif_bitclr(CLK_ENABLE, &host->regs->ce_clk_ctrl); + sh_mmcif_bitclr(CLK_CLEAR, &host->regs->ce_clk_ctrl); + + if (!clk) + return; + if (clk == CLKDEV_EMMC_DATA) { + sh_mmcif_bitset(CLK_PCLK, &host->regs->ce_clk_ctrl); + } else { + for (i = 1; (unsigned int)host->clk / (1 << i) >= clk; i++) + ; + sh_mmcif_bitset((i - 1) << 16, &host->regs->ce_clk_ctrl); + } + sh_mmcif_bitset(CLK_ENABLE, &host->regs->ce_clk_ctrl); +} + +static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) +{ + u32 tmp; + + tmp = sh_mmcif_read(&host->regs->ce_clk_ctrl) & (CLK_ENABLE | + CLK_CLEAR); + + sh_mmcif_write(SOFT_RST_ON, &host->regs->ce_version); + sh_mmcif_write(SOFT_RST_OFF, &host->regs->ce_version); + sh_mmcif_bitset(tmp | SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29, + &host->regs->ce_clk_ctrl); + /* byte swap on */ + sh_mmcif_bitset(BUF_ACC_ATYP, &host->regs->ce_buf_acc); +} + +static int sh_mmcif_error_manage(struct sh_mmcif_host *host) +{ + u32 state1, state2; + int ret, timeout = 10000000; + + host->sd_error = 0; + host->wait_int = 0; + + state1 = sh_mmcif_read(&host->regs->ce_host_sts1); + state2 = sh_mmcif_read(&host->regs->ce_host_sts2); + debug("%s: ERR HOST_STS1 = %08x\n", \ + DRIVER_NAME, sh_mmcif_read(&host->regs->ce_host_sts1)); + debug("%s: ERR HOST_STS2 = %08x\n", \ + DRIVER_NAME, sh_mmcif_read(&host->regs->ce_host_sts2)); + + if (state1 & STS1_CMDSEQ) { + debug("%s: Forced end of command sequence\n", DRIVER_NAME); + sh_mmcif_bitset(CMD_CTRL_BREAK, &host->regs->ce_cmd_ctrl); + sh_mmcif_bitset(~CMD_CTRL_BREAK, &host->regs->ce_cmd_ctrl); + while (1) { + timeout--; + if (timeout < 0) { + printf(DRIVER_NAME": Forceed end of " \ + "command sequence timeout err\n"); + return -EILSEQ; + } + if (!(sh_mmcif_read(&host->regs->ce_host_sts1) + & STS1_CMDSEQ)) + break; + } + sh_mmcif_sync_reset(host); + return -EILSEQ; + } + + if (state2 & STS2_CRC_ERR) + ret = -EILSEQ; + else if (state2 & STS2_TIMEOUT_ERR) + ret = TIMEOUT; + else + ret = -EILSEQ; + return ret; +} + +static int sh_mmcif_single_read(struct sh_mmcif_host *host, + struct mmc_data *data) +{ + long time; + u32 blocksize, i; + unsigned long *p = (unsigned long *)data->dest; + + if ((unsigned long)p & 0x00000001) { + printf("%s: The data pointer is unaligned.", __func__); + return -EIO; + } + + host->wait_int = 0; + + /* buf read enable */ + sh_mmcif_bitset(MASK_MBUFREN, &host->regs->ce_int_mask); + time = mmcif_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_mmcif_error_manage(host); + + host->wait_int = 0; + blocksize = (BLOCK_SIZE_MASK & + sh_mmcif_read(&host->regs->ce_block_set)) + 3; + for (i = 0; i < blocksize / 4; i++) + *p++ = sh_mmcif_read(&host->regs->ce_data); + + /* buffer read end */ + sh_mmcif_bitset(MASK_MBUFRE, &host->regs->ce_int_mask); + time = mmcif_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_mmcif_error_manage(host); + + host->wait_int = 0; + return 0; +} + +static int sh_mmcif_multi_read(struct sh_mmcif_host *host, + struct mmc_data *data) +{ + long time; + u32 blocksize, i, j; + unsigned long *p = (unsigned long *)data->dest; + + if ((unsigned long)p & 0x00000001) { + printf("%s: The data pointer is unaligned.", __func__); + return -EIO; + } + + host->wait_int = 0; + blocksize = BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set); + for (j = 0; j < data->blocks; j++) { + sh_mmcif_bitset(MASK_MBUFREN, &host->regs->ce_int_mask); + time = mmcif_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_mmcif_error_manage(host); + + host->wait_int = 0; + for (i = 0; i < blocksize / 4; i++) + *p++ = sh_mmcif_read(&host->regs->ce_data); + + WATCHDOG_RESET(); + } + return 0; +} + +static int sh_mmcif_single_write(struct sh_mmcif_host *host, + struct mmc_data *data) +{ + long time; + u32 blocksize, i; + const unsigned long *p = (unsigned long *)data->dest; + + if ((unsigned long)p & 0x00000001) { + printf("%s: The data pointer is unaligned.", __func__); + return -EIO; + } + + host->wait_int = 0; + sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask); + + time = mmcif_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_mmcif_error_manage(host); + + host->wait_int = 0; + blocksize = (BLOCK_SIZE_MASK & + sh_mmcif_read(&host->regs->ce_block_set)) + 3; + for (i = 0; i < blocksize / 4; i++) + sh_mmcif_write(*p++, &host->regs->ce_data); + + /* buffer write end */ + sh_mmcif_bitset(MASK_MDTRANE, &host->regs->ce_int_mask); + + time = mmcif_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_mmcif_error_manage(host); + + host->wait_int = 0; + return 0; +} + +static int sh_mmcif_multi_write(struct sh_mmcif_host *host, + struct mmc_data *data) +{ + long time; + u32 i, j, blocksize; + const unsigned long *p = (unsigned long *)data->dest; + + if ((unsigned long)p & 0x00000001) { + printf("%s: The data pointer is unaligned.", __func__); + return -EIO; + } + + host->wait_int = 0; + blocksize = BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set); + for (j = 0; j < data->blocks; j++) { + sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask); + + time = mmcif_wait_interrupt_flag(host); + + if (time == 0 || host->sd_error != 0) + return sh_mmcif_error_manage(host); + + host->wait_int = 0; + for (i = 0; i < blocksize / 4; i++) + sh_mmcif_write(*p++, &host->regs->ce_data); + + WATCHDOG_RESET(); + } + return 0; +} + +static void sh_mmcif_get_response(struct sh_mmcif_host *host, + struct mmc_cmd *cmd) +{ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp3); + cmd->response[1] = sh_mmcif_read(&host->regs->ce_resp2); + cmd->response[2] = sh_mmcif_read(&host->regs->ce_resp1); + cmd->response[3] = sh_mmcif_read(&host->regs->ce_resp0); + debug(" RESP %08x, %08x, %08x, %08x\n", cmd->response[0], + cmd->response[1], cmd->response[2], cmd->response[3]); + } else { + cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp0); + } +} + +static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host, + struct mmc_cmd *cmd) +{ + cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp_cmd12); +} + +static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, + struct mmc_data *data, struct mmc_cmd *cmd) +{ + u32 tmp = 0; + u32 opc = cmd->cmdidx; + + /* Response Type check */ + switch (cmd->resp_type) { + case MMC_RSP_NONE: + tmp |= CMD_SET_RTYP_NO; + break; + case MMC_RSP_R1: + case MMC_RSP_R1b: + case MMC_RSP_R3: + tmp |= CMD_SET_RTYP_6B; + break; + case MMC_RSP_R2: + tmp |= CMD_SET_RTYP_17B; + break; + default: + printf(DRIVER_NAME": Not support type response.\n"); + break; + } + + /* RBSY */ + if (opc == MMC_CMD_SWITCH) + tmp |= CMD_SET_RBSY; + + /* WDAT / DATW */ + if (host->data) { + tmp |= CMD_SET_WDAT; + switch (host->bus_width) { + case MMC_BUS_WIDTH_1: + tmp |= CMD_SET_DATW_1; + break; + case MMC_BUS_WIDTH_4: + tmp |= CMD_SET_DATW_4; + break; + case MMC_BUS_WIDTH_8: + tmp |= CMD_SET_DATW_8; + break; + default: + printf(DRIVER_NAME": Not support bus width.\n"); + break; + } + } + /* DWEN */ + if (opc == MMC_CMD_WRITE_SINGLE_BLOCK || + opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) + tmp |= CMD_SET_DWEN; + /* CMLTE/CMD12EN */ + if (opc == MMC_CMD_READ_MULTIPLE_BLOCK || + opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) { + tmp |= CMD_SET_CMLTE | CMD_SET_CMD12EN; + sh_mmcif_bitset(data->blocks << 16, &host->regs->ce_block_set); + } + /* RIDXC[1:0] check bits */ + if (opc == MMC_CMD_SEND_OP_COND || opc == MMC_CMD_ALL_SEND_CID || + opc == MMC_CMD_SEND_CSD || opc == MMC_CMD_SEND_CID) + tmp |= CMD_SET_RIDXC_BITS; + /* RCRC7C[1:0] check bits */ + if (opc == MMC_CMD_SEND_OP_COND) + tmp |= CMD_SET_CRC7C_BITS; + /* RCRC7C[1:0] internal CRC7 */ + if (opc == MMC_CMD_ALL_SEND_CID || + opc == MMC_CMD_SEND_CSD || opc == MMC_CMD_SEND_CID) + tmp |= CMD_SET_CRC7C_INTERNAL; + + return opc = ((opc << 24) | tmp); +} + +static u32 sh_mmcif_data_trans(struct sh_mmcif_host *host, + struct mmc_data *data, u16 opc) +{ + u32 ret; + + switch (opc) { + case MMC_CMD_READ_MULTIPLE_BLOCK: + ret = sh_mmcif_multi_read(host, data); + break; + case MMC_CMD_WRITE_MULTIPLE_BLOCK: + ret = sh_mmcif_multi_write(host, data); + break; + case MMC_CMD_WRITE_SINGLE_BLOCK: + ret = sh_mmcif_single_write(host, data); + break; + case MMC_CMD_READ_SINGLE_BLOCK: + case MMC_CMD_SEND_EXT_CSD: + ret = sh_mmcif_single_read(host, data); + break; + default: + printf(DRIVER_NAME": NOT SUPPORT CMD = d'%08d\n", opc); + ret = -EINVAL; + break; + } + return ret; +} + +static int sh_mmcif_start_cmd(struct sh_mmcif_host *host, + struct mmc_data *data, struct mmc_cmd *cmd) +{ + long time; + int ret = 0, mask = 0; + u32 opc = cmd->cmdidx; + + if (opc == MMC_CMD_STOP_TRANSMISSION) { + /* MMCIF sends the STOP command automatically */ + if (host->last_cmd == MMC_CMD_READ_MULTIPLE_BLOCK) + sh_mmcif_bitset(MASK_MCMD12DRE, + &host->regs->ce_int_mask); + else + sh_mmcif_bitset(MASK_MCMD12RBE, + &host->regs->ce_int_mask); + + time = mmcif_wait_interrupt_flag(host); + if (time == 0 || host->sd_error != 0) + return sh_mmcif_error_manage(host); + + sh_mmcif_get_cmd12response(host, cmd); + return 0; + } + if (opc == MMC_CMD_SWITCH) + mask = MASK_MRBSYE; + else + mask = MASK_MCRSPE; + + mask |= MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | + MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | + MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | + MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO; + + if (host->data) { + sh_mmcif_write(0, &host->regs->ce_block_set); + sh_mmcif_write(data->blocksize, &host->regs->ce_block_set); + } + opc = sh_mmcif_set_cmd(host, data, cmd); + + sh_mmcif_write(INT_START_MAGIC, &host->regs->ce_int); + sh_mmcif_write(mask, &host->regs->ce_int_mask); + + debug("CMD%d ARG:%08x\n", cmd->cmdidx, cmd->cmdarg); + /* set arg */ + sh_mmcif_write(cmd->cmdarg, &host->regs->ce_arg); + host->wait_int = 0; + /* set cmd */ + sh_mmcif_write(opc, &host->regs->ce_cmd_set); + + time = mmcif_wait_interrupt_flag(host); + if (time == 0) + return sh_mmcif_error_manage(host); + + if (host->sd_error) { + switch (cmd->cmdidx) { + case MMC_CMD_ALL_SEND_CID: + case MMC_CMD_SELECT_CARD: + case MMC_CMD_APP_CMD: + ret = TIMEOUT; + break; + default: + printf(DRIVER_NAME": Cmd(d'%d) err\n", cmd->cmdidx); + ret = sh_mmcif_error_manage(host); + break; + } + host->sd_error = 0; + host->wait_int = 0; + return ret; + } + + /* if no response */ + if (!(opc & 0x00C00000)) + return 0; + + if (host->wait_int == 1) { + sh_mmcif_get_response(host, cmd); + host->wait_int = 0; + } + if (host->data) + ret = sh_mmcif_data_trans(host, data, cmd->cmdidx); + host->last_cmd = cmd->cmdidx; + + return ret; +} + +static int sh_mmcif_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct sh_mmcif_host *host = mmc->priv; + int ret; + + WATCHDOG_RESET(); + + switch (cmd->cmdidx) { + case MMC_CMD_APP_CMD: + return TIMEOUT; + case MMC_CMD_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ + if (data) + /* ext_csd */ + break; + else + /* send_if_cond cmd (not support) */ + return TIMEOUT; + default: + break; + } + host->sd_error = 0; + host->data = data; + ret = sh_mmcif_start_cmd(host, data, cmd); + host->data = NULL; + + return ret; +} + +static void sh_mmcif_set_ios(struct mmc *mmc) +{ + struct sh_mmcif_host *host = mmc->priv; + + if (mmc->clock) + sh_mmcif_clock_control(host, mmc->clock); + + if (mmc->bus_width == 8) + host->bus_width = MMC_BUS_WIDTH_8; + else if (mmc->bus_width == 4) + host->bus_width = MMC_BUS_WIDTH_4; + else + host->bus_width = MMC_BUS_WIDTH_1; + + debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width); +} + +static int sh_mmcif_init(struct mmc *mmc) +{ + struct sh_mmcif_host *host = mmc->priv; + + sh_mmcif_sync_reset(host); + sh_mmcif_write(MASK_ALL, &host->regs->ce_int_mask); + return 0; +} + +static const struct mmc_ops sh_mmcif_ops = { + .send_cmd = sh_mmcif_request, + .set_ios = sh_mmcif_set_ios, + .init = sh_mmcif_init, +}; + +static struct mmc_config sh_mmcif_cfg = { + .name = DRIVER_NAME, + .ops = &sh_mmcif_ops, + .host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | + MMC_MODE_8BIT | MMC_MODE_HC, + .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, + .f_min = CLKDEV_MMC_INIT, + .f_max = CLKDEV_EMMC_DATA, + .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, +}; + +int mmcif_mmc_init(void) +{ + struct mmc *mmc; + struct sh_mmcif_host *host = NULL; + + host = malloc(sizeof(struct sh_mmcif_host)); + if (!host) + return -ENOMEM; + memset(host, 0, sizeof(*host)); + + host->regs = (struct sh_mmcif_regs *)CONFIG_SH_MMCIF_ADDR; + host->clk = CONFIG_SH_MMCIF_CLK; + + mmc = mmc_create(&sh_mmcif_cfg, host); + if (mmc == NULL) { + free(host); + return -ENOMEM; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/sh_mmcif.h b/qemu/roms/u-boot/drivers/mmc/sh_mmcif.h new file mode 100644 index 000000000..bd6fbf7c6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/sh_mmcif.h @@ -0,0 +1,238 @@ +/* + * MMCIF driver. + * + * Copyright (C) 2011 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + */ + +#ifndef _SH_MMCIF_H_ +#define _SH_MMCIF_H_ + +struct sh_mmcif_regs { + unsigned long ce_cmd_set; + unsigned long reserved; + unsigned long ce_arg; + unsigned long ce_arg_cmd12; + unsigned long ce_cmd_ctrl; + unsigned long ce_block_set; + unsigned long ce_clk_ctrl; + unsigned long ce_buf_acc; + unsigned long ce_resp3; + unsigned long ce_resp2; + unsigned long ce_resp1; + unsigned long ce_resp0; + unsigned long ce_resp_cmd12; + unsigned long ce_data; + unsigned long reserved2[2]; + unsigned long ce_int; + unsigned long ce_int_mask; + unsigned long ce_host_sts1; + unsigned long ce_host_sts2; + unsigned long reserved3[11]; + unsigned long ce_version; +}; + +/* CE_CMD_SET */ +#define CMD_MASK 0x3f000000 +#define CMD_SET_RTYP_NO ((0 << 23) | (0 << 22)) +/* R1/R1b/R3/R4/R5 */ +#define CMD_SET_RTYP_6B ((0 << 23) | (1 << 22)) +/* R2 */ +#define CMD_SET_RTYP_17B ((1 << 23) | (0 << 22)) +/* R1b */ +#define CMD_SET_RBSY (1 << 21) +#define CMD_SET_CCSEN (1 << 20) +/* 1: on data, 0: no data */ +#define CMD_SET_WDAT (1 << 19) +/* 1: write to card, 0: read from card */ +#define CMD_SET_DWEN (1 << 18) +/* 1: multi block trans, 0: single */ +#define CMD_SET_CMLTE (1 << 17) +/* 1: CMD12 auto issue */ +#define CMD_SET_CMD12EN (1 << 16) +/* index check */ +#define CMD_SET_RIDXC_INDEX ((0 << 15) | (0 << 14)) +/* check bits check */ +#define CMD_SET_RIDXC_BITS ((0 << 15) | (1 << 14)) +/* no check */ +#define CMD_SET_RIDXC_NO ((1 << 15) | (0 << 14)) +/* 1: CRC7 check*/ +#define CMD_SET_CRC7C ((0 << 13) | (0 << 12)) +/* 1: check bits check*/ +#define CMD_SET_CRC7C_BITS ((0 << 13) | (1 << 12)) +/* 1: internal CRC7 check*/ +#define CMD_SET_CRC7C_INTERNAL ((1 << 13) | (0 << 12)) +/* 1: CRC16 check*/ +#define CMD_SET_CRC16C (1 << 10) +/* 1: not receive CRC status */ +#define CMD_SET_CRCSTE (1 << 8) +/* 1: tran mission bit "Low" */ +#define CMD_SET_TBIT (1 << 7) +/* 1: open/drain */ +#define CMD_SET_OPDM (1 << 6) +#define CMD_SET_CCSH (1 << 5) +/* 1bit */ +#define CMD_SET_DATW_1 ((0 << 1) | (0 << 0)) +/* 4bit */ +#define CMD_SET_DATW_4 ((0 << 1) | (1 << 0)) +/* 8bit */ +#define CMD_SET_DATW_8 ((1 << 1) | (0 << 0)) + +/* CE_CMD_CTRL */ +#define CMD_CTRL_BREAK (1 << 0) + +/* CE_BLOCK_SET */ +#define BLOCK_SIZE_MASK 0x0000ffff + +/* CE_CLK_CTRL */ +#define CLK_ENABLE (1 << 24) +#define CLK_CLEAR ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16)) +#define CLK_PCLK ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16)) +/* respons timeout */ +#define SRSPTO_256 ((1 << 13) | (0 << 12)) +/* respons busy timeout */ +#define SRBSYTO_29 ((1 << 11) | (1 << 10) | (1 << 9) | (1 << 8)) +/* read/write timeout */ +#define SRWDTO_29 ((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4)) +/* ccs timeout */ +#define SCCSTO_29 ((1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)) + +/* CE_BUF_ACC */ +#define BUF_ACC_DMAWEN (1 << 25) +#define BUF_ACC_DMAREN (1 << 24) +#define BUF_ACC_BUSW_32 (0 << 17) +#define BUF_ACC_BUSW_16 (1 << 17) +#define BUF_ACC_ATYP (1 << 16) + +/* CE_INT */ +#define INT_CCSDE (1 << 29) +#define INT_CMD12DRE (1 << 26) +#define INT_CMD12RBE (1 << 25) +#define INT_CMD12CRE (1 << 24) +#define INT_DTRANE (1 << 23) +#define INT_BUFRE (1 << 22) +#define INT_BUFWEN (1 << 21) +#define INT_BUFREN (1 << 20) +#define INT_CCSRCV (1 << 19) +#define INT_RBSYE (1 << 17) +#define INT_CRSPE (1 << 16) +#define INT_CMDVIO (1 << 15) +#define INT_BUFVIO (1 << 14) +#define INT_WDATERR (1 << 11) +#define INT_RDATERR (1 << 10) +#define INT_RIDXERR (1 << 9) +#define INT_RSPERR (1 << 8) +#define INT_CCSTO (1 << 5) +#define INT_CRCSTO (1 << 4) +#define INT_WDATTO (1 << 3) +#define INT_RDATTO (1 << 2) +#define INT_RBSYTO (1 << 1) +#define INT_RSPTO (1 << 0) +#define INT_ERR_STS (INT_CMDVIO | INT_BUFVIO | INT_WDATERR | \ + INT_RDATERR | INT_RIDXERR | INT_RSPERR | \ + INT_CCSTO | INT_CRCSTO | INT_WDATTO | \ + INT_RDATTO | INT_RBSYTO | INT_RSPTO) +#define INT_START_MAGIC 0xD80430C0 + +/* CE_INT_MASK */ +#define MASK_ALL 0x00000000 +#define MASK_MCCSDE (1 << 29) +#define MASK_MCMD12DRE (1 << 26) +#define MASK_MCMD12RBE (1 << 25) +#define MASK_MCMD12CRE (1 << 24) +#define MASK_MDTRANE (1 << 23) +#define MASK_MBUFRE (1 << 22) +#define MASK_MBUFWEN (1 << 21) +#define MASK_MBUFREN (1 << 20) +#define MASK_MCCSRCV (1 << 19) +#define MASK_MRBSYE (1 << 17) +#define MASK_MCRSPE (1 << 16) +#define MASK_MCMDVIO (1 << 15) +#define MASK_MBUFVIO (1 << 14) +#define MASK_MWDATERR (1 << 11) +#define MASK_MRDATERR (1 << 10) +#define MASK_MRIDXERR (1 << 9) +#define MASK_MRSPERR (1 << 8) +#define MASK_MCCSTO (1 << 5) +#define MASK_MCRCSTO (1 << 4) +#define MASK_MWDATTO (1 << 3) +#define MASK_MRDATTO (1 << 2) +#define MASK_MRBSYTO (1 << 1) +#define MASK_MRSPTO (1 << 0) + +/* CE_HOST_STS1 */ +#define STS1_CMDSEQ (1 << 31) + +/* CE_HOST_STS2 */ +#define STS2_CRCSTE (1 << 31) +#define STS2_CRC16E (1 << 30) +#define STS2_AC12CRCE (1 << 29) +#define STS2_RSPCRC7E (1 << 28) +#define STS2_CRCSTEBE (1 << 27) +#define STS2_RDATEBE (1 << 26) +#define STS2_AC12REBE (1 << 25) +#define STS2_RSPEBE (1 << 24) +#define STS2_AC12IDXE (1 << 23) +#define STS2_RSPIDXE (1 << 22) +#define STS2_CCSTO (1 << 15) +#define STS2_RDATTO (1 << 14) +#define STS2_DATBSYTO (1 << 13) +#define STS2_CRCSTTO (1 << 12) +#define STS2_AC12BSYTO (1 << 11) +#define STS2_RSPBSYTO (1 << 10) +#define STS2_AC12RSPTO (1 << 9) +#define STS2_RSPTO (1 << 8) + +#define STS2_CRC_ERR (STS2_CRCSTE | STS2_CRC16E | \ + STS2_AC12CRCE | STS2_RSPCRC7E | STS2_CRCSTEBE) +#define STS2_TIMEOUT_ERR (STS2_CCSTO | STS2_RDATTO | \ + STS2_DATBSYTO | STS2_CRCSTTO | \ + STS2_AC12BSYTO | STS2_RSPBSYTO | \ + STS2_AC12RSPTO | STS2_RSPTO) + +/* CE_VERSION */ +#define SOFT_RST_ON (1 << 31) +#define SOFT_RST_OFF (0 << 31) + +#define CLKDEV_EMMC_DATA 52000000 /* 52MHz */ +#define CLKDEV_MMC_INIT 400000 /* 100 - 400 KHz */ + +#define MMC_BUS_WIDTH_1 0 +#define MMC_BUS_WIDTH_4 2 +#define MMC_BUS_WIDTH_8 3 + +struct sh_mmcif_host { + struct mmc_data *data; + struct sh_mmcif_regs *regs; + unsigned int clk; + int bus_width; + u16 wait_int; + u16 sd_error; + u8 last_cmd; +}; + +static inline u32 sh_mmcif_read(unsigned long *reg) +{ + return readl(reg); +} + +static inline void sh_mmcif_write(u32 val, unsigned long *reg) +{ + writel(val, reg); +} + +static inline void sh_mmcif_bitset(u32 val, unsigned long *reg) +{ + sh_mmcif_write(val | sh_mmcif_read(reg), reg); +} + +static inline void sh_mmcif_bitclr(u32 val, unsigned long *reg) +{ + sh_mmcif_write(~val & sh_mmcif_read(reg), reg); +} + +#endif /* _SH_MMCIF_H_ */ diff --git a/qemu/roms/u-boot/drivers/mmc/socfpga_dw_mmc.c b/qemu/roms/u-boot/drivers/mmc/socfpga_dw_mmc.c new file mode 100644 index 000000000..bc53a5da2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/socfpga_dw_mmc.c @@ -0,0 +1,68 @@ +/* + * (C) Copyright 2013 Altera Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +static const struct socfpga_clock_manager *clock_manager_base = + (void *)SOCFPGA_CLKMGR_ADDRESS; +static const struct socfpga_system_manager *system_manager_base = + (void *)SOCFPGA_SYSMGR_ADDRESS; + +static char *SOCFPGA_NAME = "SOCFPGA DWMMC"; + +static void socfpga_dwmci_clksel(struct dwmci_host *host) +{ + unsigned int drvsel; + unsigned int smplsel; + + /* Disable SDMMC clock. */ + clrbits_le32(&clock_manager_base->per_pll_en, + CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); + + /* Configures drv_sel and smpl_sel */ + drvsel = CONFIG_SOCFPGA_DWMMC_DRVSEL; + smplsel = CONFIG_SOCFPGA_DWMMC_SMPSEL; + + debug("%s: drvsel %d smplsel %d\n", __func__, drvsel, smplsel); + writel(SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel), + &system_manager_base->sdmmcgrp_ctrl); + + debug("%s: SYSMGR_SDMMCGRP_CTRL_REG = 0x%x\n", __func__, + readl(&system_manager_base->sdmmcgrp_ctrl)); + + /* Enable SDMMC clock */ + setbits_le32(&clock_manager_base->per_pll_en, + CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); +} + +int socfpga_dwmmc_init(u32 regbase, int bus_width, int index) +{ + struct dwmci_host *host = NULL; + host = calloc(sizeof(struct dwmci_host), 1); + if (!host) { + printf("dwmci_host calloc fail!\n"); + return -1; + } + + host->name = SOCFPGA_NAME; + host->ioaddr = (void *)regbase; + host->buswidth = bus_width; + host->clksel = socfpga_dwmci_clksel; + host->dev_index = index; + /* fixed clock divide by 4 which due to the SDMMC wrapper */ + host->bus_hz = CONFIG_SOCFPGA_DWMMC_BUS_HZ; + host->fifoth_val = MSIZE(0x2) | + RX_WMARK(CONFIG_SOCFPGA_DWMMC_FIFO_DEPTH / 2 - 1) | + TX_WMARK(CONFIG_SOCFPGA_DWMMC_FIFO_DEPTH / 2); + + return add_dwmci(host, host->bus_hz, 400000); +} + diff --git a/qemu/roms/u-boot/drivers/mmc/spear_sdhci.c b/qemu/roms/u-boot/drivers/mmc/spear_sdhci.c new file mode 100644 index 000000000..6ca96a2d0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/spear_sdhci.c @@ -0,0 +1,32 @@ +/* + * (C) Copyright 2012 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +int spear_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) +{ + struct sdhci_host *host = NULL; + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("sdhci host malloc fail!\n"); + return 1; + } + + host->name = "sdhci"; + host->ioaddr = (void *)regbase; + host->quirks = quirks; + + if (quirks & SDHCI_QUIRK_REG32_RW) + host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; + else + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + add_sdhci(host, max_clk, min_clk); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mmc/tegra_mmc.c b/qemu/roms/u-boot/drivers/mmc/tegra_mmc.c new file mode 100644 index 000000000..ed67eec25 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/tegra_mmc.c @@ -0,0 +1,702 @@ +/* + * (C) Copyright 2009 SAMSUNG Electronics + * Minkyu Kang + * Jaehoon Chung + * Portions Copyright 2011-2013 NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct mmc_host mmc_host[MAX_HOSTS]; + +#ifndef CONFIG_OF_CONTROL +#error "Please enable device tree support to use this driver" +#endif + +static void mmc_set_power(struct mmc_host *host, unsigned short power) +{ + u8 pwr = 0; + debug("%s: power = %x\n", __func__, power); + + if (power != (unsigned short)-1) { + switch (1 << power) { + case MMC_VDD_165_195: + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; + break; + } + } + debug("%s: pwr = %X\n", __func__, pwr); + + /* Set the bus voltage first (if any) */ + writeb(pwr, &host->reg->pwrcon); + if (pwr == 0) + return; + + /* Now enable bus power */ + pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; + writeb(pwr, &host->reg->pwrcon); +} + +static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, + struct bounce_buffer *bbstate) +{ + unsigned char ctrl; + + + debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n", + bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, + data->blocksize); + + writel((u32)bbstate->bounce_buffer, &host->reg->sysad); + /* + * DMASEL[4:3] + * 00 = Selects SDMA + * 01 = Reserved + * 10 = Selects 32-bit Address ADMA2 + * 11 = Selects 64-bit Address ADMA2 + */ + ctrl = readb(&host->reg->hostctl); + ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; + ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; + writeb(ctrl, &host->reg->hostctl); + + /* We do not handle DMA boundaries, so set it to max (512 KiB) */ + writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); + writew(data->blocks, &host->reg->blkcnt); +} + +static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) +{ + unsigned short mode; + debug(" mmc_set_transfer_mode called\n"); + /* + * TRNMOD + * MUL1SIN0[5] : Multi/Single Block Select + * RD1WT0[4] : Data Transfer Direction Select + * 1 = read + * 0 = write + * ENACMD12[2] : Auto CMD12 Enable + * ENBLKCNT[1] : Block Count Enable + * ENDMA[0] : DMA Enable + */ + mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | + TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); + + if (data->blocks > 1) + mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; + + if (data->flags & MMC_DATA_READ) + mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + + writew(mode, &host->reg->trnmod); +} + +static int mmc_wait_inhibit(struct mmc_host *host, + struct mmc_cmd *cmd, + struct mmc_data *data, + unsigned int timeout) +{ + /* + * PRNSTS + * CMDINHDAT[1] : Command Inhibit (DAT) + * CMDINHCMD[0] : Command Inhibit (CMD) + */ + unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; + + /* + * We shouldn't wait for data inhibit for stop commands, even + * though they might use busy signaling + */ + if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) + mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; + + while (readl(&host->reg->prnsts) & mask) { + if (timeout == 0) { + printf("%s: timeout error\n", __func__); + return -1; + } + timeout--; + udelay(1000); + } + + return 0; +} + +static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data, struct bounce_buffer *bbstate) +{ + struct mmc_host *host = mmc->priv; + int flags, i; + int result; + unsigned int mask = 0; + unsigned int retry = 0x100000; + debug(" mmc_send_cmd called\n"); + + result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); + + if (result < 0) + return result; + + if (data) + mmc_prepare_data(host, data, bbstate); + + debug("cmd->arg: %08x\n", cmd->cmdarg); + writel(cmd->cmdarg, &host->reg->argument); + + if (data) + mmc_set_transfer_mode(host, data); + + if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) + return -1; + + /* + * CMDREG + * CMDIDX[13:8] : Command index + * DATAPRNT[5] : Data Present Select + * ENCMDIDX[4] : Command Index Check Enable + * ENCMDCRC[3] : Command CRC Check Enable + * RSPTYP[1:0] + * 00 = No Response + * 01 = Length 136 + * 10 = Length 48 + * 11 = Length 48 Check busy after response + */ + if (!(cmd->resp_type & MMC_RSP_PRESENT)) + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; + else if (cmd->resp_type & MMC_RSP_136) + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; + else if (cmd->resp_type & MMC_RSP_BUSY) + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; + else + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; + + if (cmd->resp_type & MMC_RSP_CRC) + flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + if (cmd->resp_type & MMC_RSP_OPCODE) + flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; + if (data) + flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; + + debug("cmd: %d\n", cmd->cmdidx); + + writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); + + for (i = 0; i < retry; i++) { + mask = readl(&host->reg->norintsts); + /* Command Complete */ + if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { + if (!data) + writel(mask, &host->reg->norintsts); + break; + } + } + + if (i == retry) { + printf("%s: waiting for status update\n", __func__); + writel(mask, &host->reg->norintsts); + return TIMEOUT; + } + + if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { + /* Timeout Error */ + debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); + writel(mask, &host->reg->norintsts); + return TIMEOUT; + } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { + /* Error Interrupt */ + debug("error: %08x cmd %d\n", mask, cmd->cmdidx); + writel(mask, &host->reg->norintsts); + return -1; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + if (cmd->resp_type & MMC_RSP_136) { + /* CRC is stripped so we need to do some shifting. */ + for (i = 0; i < 4; i++) { + unsigned int offset = + (unsigned int)(&host->reg->rspreg3 - i); + cmd->response[i] = readl(offset) << 8; + + if (i != 3) { + cmd->response[i] |= + readb(offset - 1); + } + debug("cmd->resp[%d]: %08x\n", + i, cmd->response[i]); + } + } else if (cmd->resp_type & MMC_RSP_BUSY) { + for (i = 0; i < retry; i++) { + /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ + if (readl(&host->reg->prnsts) + & (1 << 20)) /* DAT[0] */ + break; + } + + if (i == retry) { + printf("%s: card is still busy\n", __func__); + writel(mask, &host->reg->norintsts); + return TIMEOUT; + } + + cmd->response[0] = readl(&host->reg->rspreg0); + debug("cmd->resp[0]: %08x\n", cmd->response[0]); + } else { + cmd->response[0] = readl(&host->reg->rspreg0); + debug("cmd->resp[0]: %08x\n", cmd->response[0]); + } + } + + if (data) { + unsigned long start = get_timer(0); + + while (1) { + mask = readl(&host->reg->norintsts); + + if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { + /* Error Interrupt */ + writel(mask, &host->reg->norintsts); + printf("%s: error during transfer: 0x%08x\n", + __func__, mask); + return -1; + } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { + /* + * DMA Interrupt, restart the transfer where + * it was interrupted. + */ + unsigned int address = readl(&host->reg->sysad); + + debug("DMA end\n"); + writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, + &host->reg->norintsts); + writel(address, &host->reg->sysad); + } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { + /* Transfer Complete */ + debug("r/w is done\n"); + break; + } else if (get_timer(start) > 2000UL) { + writel(mask, &host->reg->norintsts); + printf("%s: MMC Timeout\n" + " Interrupt status 0x%08x\n" + " Interrupt status enable 0x%08x\n" + " Interrupt signal enable 0x%08x\n" + " Present status 0x%08x\n", + __func__, mask, + readl(&host->reg->norintstsen), + readl(&host->reg->norintsigen), + readl(&host->reg->prnsts)); + return -1; + } + } + writel(mask, &host->reg->norintsts); + } + + udelay(1000); + return 0; +} + +static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + void *buf; + unsigned int bbflags; + size_t len; + struct bounce_buffer bbstate; + int ret; + + if (data) { + if (data->flags & MMC_DATA_READ) { + buf = data->dest; + bbflags = GEN_BB_WRITE; + } else { + buf = (void *)data->src; + bbflags = GEN_BB_READ; + } + len = data->blocks * data->blocksize; + + bounce_buffer_start(&bbstate, buf, len, bbflags); + } + + ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); + + if (data) + bounce_buffer_stop(&bbstate); + + return ret; +} + +static void mmc_change_clock(struct mmc_host *host, uint clock) +{ + int div; + unsigned short clk; + unsigned long timeout; + + debug(" mmc_change_clock called\n"); + + /* + * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0 + */ + if (clock == 0) + goto out; + clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, + &div); + debug("div = %d\n", div); + + writew(0, &host->reg->clkcon); + + /* + * CLKCON + * SELFREQ[15:8] : base clock divided by value + * ENSDCLK[2] : SD Clock Enable + * STBLINTCLK[1] : Internal Clock Stable + * ENINTCLK[0] : Internal Clock Enable + */ + div >>= 1; + clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | + TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); + writew(clk, &host->reg->clkcon); + + /* Wait max 10 ms */ + timeout = 10; + while (!(readw(&host->reg->clkcon) & + TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { + if (timeout == 0) { + printf("%s: timeout error\n", __func__); + return; + } + timeout--; + udelay(1000); + } + + clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + writew(clk, &host->reg->clkcon); + + debug("mmc_change_clock: clkcon = %08X\n", clk); + +out: + host->clock = clock; +} + +static void tegra_mmc_set_ios(struct mmc *mmc) +{ + struct mmc_host *host = mmc->priv; + unsigned char ctrl; + debug(" mmc_set_ios called\n"); + + debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); + + /* Change clock first */ + mmc_change_clock(host, mmc->clock); + + ctrl = readb(&host->reg->hostctl); + + /* + * WIDE8[5] + * 0 = Depend on WIDE4 + * 1 = 8-bit mode + * WIDE4[1] + * 1 = 4-bit mode + * 0 = 1-bit mode + */ + if (mmc->bus_width == 8) + ctrl |= (1 << 5); + else if (mmc->bus_width == 4) + ctrl |= (1 << 1); + else + ctrl &= ~(1 << 1); + + writeb(ctrl, &host->reg->hostctl); + debug("mmc_set_ios: hostctl = %08X\n", ctrl); +} + +static void mmc_reset(struct mmc_host *host, struct mmc *mmc) +{ + unsigned int timeout; + debug(" mmc_reset called\n"); + + /* + * RSTALL[0] : Software reset for all + * 1 = reset + * 0 = work + */ + writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); + + host->clock = 0; + + /* Wait max 100 ms */ + timeout = 100; + + /* hw clears the bit when it's done */ + while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { + if (timeout == 0) { + printf("%s: timeout error\n", __func__); + return; + } + timeout--; + udelay(1000); + } + + /* Set SD bus voltage & enable bus power */ + mmc_set_power(host, fls(mmc->cfg->voltages) - 1); + debug("%s: power control = %02X, host control = %02X\n", __func__, + readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); + + /* Make sure SDIO pads are set up */ + pad_init_mmc(host); +} + +static int tegra_mmc_core_init(struct mmc *mmc) +{ + struct mmc_host *host = mmc->priv; + unsigned int mask; + debug(" mmc_core_init called\n"); + + mmc_reset(host, mmc); + + host->version = readw(&host->reg->hcver); + debug("host version = %x\n", host->version); + + /* mask all */ + writel(0xffffffff, &host->reg->norintstsen); + writel(0xffffffff, &host->reg->norintsigen); + + writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ + /* + * NORMAL Interrupt Status Enable Register init + * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable + * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable + * [3] ENSTADMAINT : DMA boundary interrupt + * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable + * [0] ENSTACMDCMPLT : Command Complete Status Enable + */ + mask = readl(&host->reg->norintstsen); + mask &= ~(0xffff); + mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | + TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | + TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | + TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | + TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); + writel(mask, &host->reg->norintstsen); + + /* + * NORMAL Interrupt Signal Enable Register init + * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable + */ + mask = readl(&host->reg->norintsigen); + mask &= ~(0xffff); + mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; + writel(mask, &host->reg->norintsigen); + + return 0; +} + +int tegra_mmc_getcd(struct mmc *mmc) +{ + struct mmc_host *host = mmc->priv; + + debug("tegra_mmc_getcd called\n"); + + if (fdt_gpio_isvalid(&host->cd_gpio)) + return fdtdec_get_gpio(&host->cd_gpio); + + return 1; +} + +static const struct mmc_ops tegra_mmc_ops = { + .send_cmd = tegra_mmc_send_cmd, + .set_ios = tegra_mmc_set_ios, + .init = tegra_mmc_core_init, + .getcd = tegra_mmc_getcd, +}; + +static int do_mmc_init(int dev_index) +{ + struct mmc_host *host; + char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */ + struct mmc *mmc; + + /* DT should have been read & host config filled in */ + host = &mmc_host[dev_index]; + if (!host->enabled) + return -1; + + debug(" do_mmc_init: index %d, bus width %d " + "pwr_gpio %d cd_gpio %d\n", + dev_index, host->width, + host->pwr_gpio.gpio, host->cd_gpio.gpio); + + host->clock = 0; + clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); + + if (fdt_gpio_isvalid(&host->pwr_gpio)) { + sprintf(gpusage, "SD/MMC%d PWR", dev_index); + gpio_request(host->pwr_gpio.gpio, gpusage); + gpio_direction_output(host->pwr_gpio.gpio, 1); + debug(" Power GPIO name = %s\n", host->pwr_gpio.name); + } + + if (fdt_gpio_isvalid(&host->cd_gpio)) { + sprintf(gpusage, "SD/MMC%d CD", dev_index); + gpio_request(host->cd_gpio.gpio, gpusage); + gpio_direction_input(host->cd_gpio.gpio); + debug(" CD GPIO name = %s\n", host->cd_gpio.name); + } + + memset(&host->cfg, 0, sizeof(host->cfg)); + + host->cfg.name = "Tegra SD/MMC"; + host->cfg.ops = &tegra_mmc_ops; + + host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + host->cfg.host_caps = 0; + if (host->width == 8) + host->cfg.host_caps |= MMC_MODE_8BIT; + if (host->width >= 4) + host->cfg.host_caps |= MMC_MODE_4BIT; + host->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; + + /* + * min freq is for card identification, and is the highest + * low-speed SDIO card frequency (actually 400KHz) + * max freq is highest HS eMMC clock as per the SD/MMC spec + * (actually 52MHz) + */ + host->cfg.f_min = 375000; + host->cfg.f_max = 48000000; + + host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + + mmc = mmc_create(&host->cfg, host); + if (mmc == NULL) + return -1; + + return 0; +} + +/** + * Get the host address and peripheral ID for a node. + * + * @param blob fdt blob + * @param node Device index (0-3) + * @param host Structure to fill in (reg, width, mmc_id) + */ +static int mmc_get_config(const void *blob, int node, struct mmc_host *host) +{ + debug("%s: node = %d\n", __func__, node); + + host->enabled = fdtdec_get_is_enabled(blob, node); + + host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg"); + if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) { + debug("%s: no sdmmc base reg info found\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + host->mmc_id = clock_decode_periph_id(blob, node); + if (host->mmc_id == PERIPH_ID_NONE) { + debug("%s: could not decode periph id\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + /* + * NOTE: mmc->bus_width is determined by mmc.c dynamically. + * TBD: Override it with this value? + */ + host->width = fdtdec_get_int(blob, node, "bus-width", 0); + if (!host->width) + debug("%s: no sdmmc width found\n", __func__); + + /* These GPIOs are optional */ + fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); + fdtdec_decode_gpio(blob, node, "wp-gpios", &host->wp_gpio); + fdtdec_decode_gpio(blob, node, "power-gpios", &host->pwr_gpio); + + debug("%s: found controller at %p, width = %d, periph_id = %d\n", + __func__, host->reg, host->width, host->mmc_id); + return 0; +} + +/* + * Process a list of nodes, adding them to our list of SDMMC ports. + * + * @param blob fdt blob + * @param node_list list of nodes to process (any <=0 are ignored) + * @param count number of nodes to process + * @return 0 if ok, -1 on error + */ +static int process_nodes(const void *blob, int node_list[], int count) +{ + struct mmc_host *host; + int i, node; + + debug("%s: count = %d\n", __func__, count); + + /* build mmc_host[] for each controller */ + for (i = 0; i < count; i++) { + node = node_list[i]; + if (node <= 0) + continue; + + host = &mmc_host[i]; + host->id = i; + + if (mmc_get_config(blob, node, host)) { + printf("%s: failed to decode dev %d\n", __func__, i); + return -1; + } + do_mmc_init(i); + } + return 0; +} + +void tegra_mmc_init(void) +{ + int node_list[MAX_HOSTS], count; + const void *blob = gd->fdt_blob; + debug("%s entry\n", __func__); + + /* See if any Tegra124 MMC controllers are present */ + count = fdtdec_find_aliases_for_id(blob, "sdhci", + COMPAT_NVIDIA_TEGRA124_SDMMC, node_list, MAX_HOSTS); + debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count); + if (process_nodes(blob, node_list, count)) { + printf("%s: Error processing T30 mmc node(s)!\n", __func__); + return; + } + + /* See if any Tegra30 MMC controllers are present */ + count = fdtdec_find_aliases_for_id(blob, "sdhci", + COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, MAX_HOSTS); + debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); + if (process_nodes(blob, node_list, count)) { + printf("%s: Error processing T30 mmc node(s)!\n", __func__); + return; + } + + /* Now look for any Tegra20 MMC controllers */ + count = fdtdec_find_aliases_for_id(blob, "sdhci", + COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, MAX_HOSTS); + debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); + if (process_nodes(blob, node_list, count)) { + printf("%s: Error processing T20 mmc node(s)!\n", __func__); + return; + } +} diff --git a/qemu/roms/u-boot/drivers/mmc/zynq_sdhci.c b/qemu/roms/u-boot/drivers/mmc/zynq_sdhci.c new file mode 100644 index 000000000..fdce2c2c1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mmc/zynq_sdhci.c @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2013 Inc. + * + * Xilinx Zynq SD Host Controller Interface + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +int zynq_sdhci_init(u32 regbase) +{ + struct sdhci_host *host = NULL; + + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("zynq_sdhci_init: sdhci_host malloc fail\n"); + return 1; + } + + host->name = "zynq_sdhci"; + host->ioaddr = (void *)regbase; + host->quirks = SDHCI_QUIRK_NO_CD | SDHCI_QUIRK_WAIT_SEND_CMD | + SDHCI_QUIRK_BROKEN_R1B; + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + host->host_caps = MMC_MODE_HC; + + add_sdhci(host, 52000000, 52000000 >> 9); + return 0; +} + +#ifdef CONFIG_OF_CONTROL +int zynq_sdhci_of_init(const void *blob) +{ + int offset = 0; + u32 ret = 0; + u32 reg; + + debug("ZYNQ SDHCI: Initialization\n"); + + do { + offset = fdt_node_offset_by_compatible(blob, offset, + "arasan,sdhci-8.9a"); + if (offset != -1) { + reg = fdtdec_get_addr(blob, offset, "reg"); + if (reg != FDT_ADDR_T_NONE) { + ret |= zynq_sdhci_init(reg); + } else { + debug("ZYNQ SDHCI: Can't get base address\n"); + return -1; + } + } + } while (offset != -1); + + return ret; +} +#endif diff --git a/qemu/roms/u-boot/drivers/mtd/Makefile b/qemu/roms/u-boot/drivers/mtd/Makefile new file mode 100644 index 000000000..5467a951b --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/Makefile @@ -0,0 +1,20 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND))) +obj-y += mtdcore.o +endif +obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o +obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o +obj-$(CONFIG_HAS_DATAFLASH) += at45.o +obj-$(CONFIG_FLASH_CFI_DRIVER) += cfi_flash.o +obj-$(CONFIG_FLASH_CFI_MTD) += cfi_mtd.o +obj-$(CONFIG_HAS_DATAFLASH) += dataflash.o +obj-$(CONFIG_FTSMC020) += ftsmc020.o +obj-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o +obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o +obj-$(CONFIG_ST_SMI) += st_smi.o diff --git a/qemu/roms/u-boot/drivers/mtd/at45.c b/qemu/roms/u-boot/drivers/mtd/at45.c new file mode 100644 index 000000000..2f49be38b --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/at45.c @@ -0,0 +1,545 @@ +/* Driver for ATMEL DataFlash support + * Author : Hamid Ikdoumi (Atmel) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +/* + * spi.c API + */ +extern unsigned int AT91F_SpiWrite(AT91PS_DataflashDesc pDesc); +extern void AT91F_SpiEnable(int cs); + +#define AT91C_TIMEOUT_WRDY 200000 + +/*----------------------------------------------------------------------*/ +/* \fn AT91F_DataFlashSendCommand */ +/* \brief Generic function to send a command to the dataflash */ +/*----------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashSendCommand(AT91PS_DataFlash pDataFlash, + unsigned char OpCode, + unsigned int CmdSize, + unsigned int DataflashAddress) +{ + unsigned int adr; + + if ((pDataFlash->pDataFlashDesc->state) != IDLE) + return DATAFLASH_BUSY; + + /* process the address to obtain page address and byte address */ + adr = ((DataflashAddress / (pDataFlash->pDevice->pages_size)) << + pDataFlash->pDevice->page_offset) + + (DataflashAddress % (pDataFlash->pDevice->pages_size)); + + /* fill the command buffer */ + pDataFlash->pDataFlashDesc->command[0] = OpCode; + if (pDataFlash->pDevice->pages_number >= 16384) { + pDataFlash->pDataFlashDesc->command[1] = + (unsigned char)((adr & 0x0F000000) >> 24); + pDataFlash->pDataFlashDesc->command[2] = + (unsigned char)((adr & 0x00FF0000) >> 16); + pDataFlash->pDataFlashDesc->command[3] = + (unsigned char)((adr & 0x0000FF00) >> 8); + pDataFlash->pDataFlashDesc->command[4] = + (unsigned char)(adr & 0x000000FF); + } else { + pDataFlash->pDataFlashDesc->command[1] = + (unsigned char)((adr & 0x00FF0000) >> 16); + pDataFlash->pDataFlashDesc->command[2] = + (unsigned char)((adr & 0x0000FF00) >> 8); + pDataFlash->pDataFlashDesc->command[3] = + (unsigned char)(adr & 0x000000FF); + pDataFlash->pDataFlashDesc->command[4] = 0; + } + pDataFlash->pDataFlashDesc->command[5] = 0; + pDataFlash->pDataFlashDesc->command[6] = 0; + pDataFlash->pDataFlashDesc->command[7] = 0; + + /* Initialize the SpiData structure for the spi write fuction */ + pDataFlash->pDataFlashDesc->tx_cmd_pt = + pDataFlash->pDataFlashDesc->command; + pDataFlash->pDataFlashDesc->tx_cmd_size = CmdSize; + pDataFlash->pDataFlashDesc->rx_cmd_pt = + pDataFlash->pDataFlashDesc->command; + pDataFlash->pDataFlashDesc->rx_cmd_size = CmdSize; + + /* send the command and read the data */ + return AT91F_SpiWrite(pDataFlash->pDataFlashDesc); +} + +/*----------------------------------------------------------------------*/ +/* \fn AT91F_DataFlashGetStatus */ +/* \brief Read the status register of the dataflash */ +/*----------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashGetStatus(AT91PS_DataflashDesc pDesc) +{ + AT91S_DataFlashStatus status; + + /* if a transfert is in progress ==> return 0 */ + if ((pDesc->state) != IDLE) + return DATAFLASH_BUSY; + + /* first send the read status command (D7H) */ + pDesc->command[0] = DB_STATUS; + pDesc->command[1] = 0; + + pDesc->DataFlash_state = GET_STATUS; + pDesc->tx_data_size = 0; /* Transmit the command */ + /* and receive response */ + pDesc->tx_cmd_pt = pDesc->command; + pDesc->rx_cmd_pt = pDesc->command; + pDesc->rx_cmd_size = 2; + pDesc->tx_cmd_size = 2; + status = AT91F_SpiWrite(pDesc); + + pDesc->DataFlash_state = *((unsigned char *)(pDesc->rx_cmd_pt) + 1); + + return status; +} + +/*----------------------------------------------------------------------*/ +/* \fn AT91F_DataFlashWaitReady */ +/* \brief wait for dataflash ready (bit7 of the status register == 1) */ +/*----------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashWaitReady(AT91PS_DataflashDesc + pDataFlashDesc, + unsigned int timeout) +{ + pDataFlashDesc->DataFlash_state = IDLE; + + do { + AT91F_DataFlashGetStatus(pDataFlashDesc); + timeout--; + } while (((pDataFlashDesc->DataFlash_state & 0x80) != 0x80) && + (timeout > 0)); + + if ((pDataFlashDesc->DataFlash_state & 0x80) != 0x80) + return DATAFLASH_ERROR; + + return DATAFLASH_OK; +} + +/*--------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataFlashContinuousRead */ +/* Object : Continuous stream Read */ +/* Input Parameters : DataFlash Service */ +/* : = dataflash address */ +/* : <*dataBuffer> = data buffer pointer */ +/* : = data buffer size */ +/* Return value : State of the dataflash */ +/*--------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashContinuousRead( + AT91PS_DataFlash pDataFlash, + int src, + unsigned char *dataBuffer, + int sizeToRead) +{ + AT91S_DataFlashStatus status; + /* Test the size to read in the device */ + if ((src + sizeToRead) > + (pDataFlash->pDevice->pages_size * + (pDataFlash->pDevice->pages_number))) + return DATAFLASH_MEMORY_OVERFLOW; + + pDataFlash->pDataFlashDesc->rx_data_pt = dataBuffer; + pDataFlash->pDataFlashDesc->rx_data_size = sizeToRead; + pDataFlash->pDataFlashDesc->tx_data_pt = dataBuffer; + pDataFlash->pDataFlashDesc->tx_data_size = sizeToRead; + + status = AT91F_DataFlashSendCommand( + pDataFlash, DB_CONTINUOUS_ARRAY_READ, 8, src); + /* Send the command to the dataflash */ + return (status); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataFlashPagePgmBuf */ +/* Object : Main memory page program thru buffer 1 or buffer 2 */ +/* Input Parameters : DataFlash Service */ +/* : <*src> = Source buffer */ +/* : = dataflash destination address */ +/* : = data buffer size */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashPagePgmBuf(AT91PS_DataFlash pDataFlash, + unsigned char *src, + unsigned int dest, + unsigned int SizeToWrite) +{ + int cmdsize; + pDataFlash->pDataFlashDesc->tx_data_pt = src; + pDataFlash->pDataFlashDesc->tx_data_size = SizeToWrite; + pDataFlash->pDataFlashDesc->rx_data_pt = src; + pDataFlash->pDataFlashDesc->rx_data_size = SizeToWrite; + + cmdsize = 4; + /* Send the command to the dataflash */ + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + return (AT91F_DataFlashSendCommand( + pDataFlash, DB_PAGE_PGM_BUF1, cmdsize, dest)); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_MainMemoryToBufferTransfert */ +/* Object : Read a page in the SRAM Buffer 1 or 2 */ +/* Input Parameters : DataFlash Service */ +/* : Page concerned */ +/* : */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_MainMemoryToBufferTransfert( + AT91PS_DataFlash + pDataFlash, + unsigned char + BufferCommand, + unsigned int page) +{ + int cmdsize; + /* Test if the buffer command is legal */ + if ((BufferCommand != DB_PAGE_2_BUF1_TRF) && + (BufferCommand != DB_PAGE_2_BUF2_TRF)) { + return DATAFLASH_BAD_COMMAND; + } + + /* no data to transmit or receive */ + pDataFlash->pDataFlashDesc->tx_data_size = 0; + cmdsize = 4; + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + return (AT91F_DataFlashSendCommand( + pDataFlash, BufferCommand, cmdsize, + page * pDataFlash->pDevice->pages_size)); +} + +/*-------------------------------------------------------------------------- */ +/* Function Name : AT91F_DataFlashWriteBuffer */ +/* Object : Write data to the internal sram buffer 1 or 2 */ +/* Input Parameters : DataFlash Service */ +/* : = command to write buffer1 or 2 */ +/* : <*dataBuffer> = data buffer to write */ +/* : = address in the internal buffer */ +/* : = data buffer size */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashWriteBuffer( + AT91PS_DataFlash pDataFlash, + unsigned char BufferCommand, + unsigned char *dataBuffer, + unsigned int bufferAddress, + int SizeToWrite) +{ + int cmdsize; + /* Test if the buffer command is legal */ + if ((BufferCommand != DB_BUF1_WRITE) && + (BufferCommand != DB_BUF2_WRITE)) { + return DATAFLASH_BAD_COMMAND; + } + + /* buffer address must be lower than page size */ + if (bufferAddress > pDataFlash->pDevice->pages_size) + return DATAFLASH_BAD_ADDRESS; + + if ((pDataFlash->pDataFlashDesc->state) != IDLE) + return DATAFLASH_BUSY; + + /* Send first Write Command */ + pDataFlash->pDataFlashDesc->command[0] = BufferCommand; + pDataFlash->pDataFlashDesc->command[1] = 0; + if (pDataFlash->pDevice->pages_number >= 16384) { + pDataFlash->pDataFlashDesc->command[2] = 0; + pDataFlash->pDataFlashDesc->command[3] = + (unsigned char)(((unsigned int)(bufferAddress & + pDataFlash->pDevice-> + byte_mask)) >> 8); + pDataFlash->pDataFlashDesc->command[4] = + (unsigned char)((unsigned int)bufferAddress & 0x00FF); + cmdsize = 5; + } else { + pDataFlash->pDataFlashDesc->command[2] = + (unsigned char)(((unsigned int)(bufferAddress & + pDataFlash->pDevice-> + byte_mask)) >> 8); + pDataFlash->pDataFlashDesc->command[3] = + (unsigned char)((unsigned int)bufferAddress & 0x00FF); + pDataFlash->pDataFlashDesc->command[4] = 0; + cmdsize = 4; + } + + pDataFlash->pDataFlashDesc->tx_cmd_pt = + pDataFlash->pDataFlashDesc->command; + pDataFlash->pDataFlashDesc->tx_cmd_size = cmdsize; + pDataFlash->pDataFlashDesc->rx_cmd_pt = + pDataFlash->pDataFlashDesc->command; + pDataFlash->pDataFlashDesc->rx_cmd_size = cmdsize; + + pDataFlash->pDataFlashDesc->rx_data_pt = dataBuffer; + pDataFlash->pDataFlashDesc->tx_data_pt = dataBuffer; + pDataFlash->pDataFlashDesc->rx_data_size = SizeToWrite; + pDataFlash->pDataFlashDesc->tx_data_size = SizeToWrite; + + return AT91F_SpiWrite(pDataFlash->pDataFlashDesc); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_PageErase */ +/* Object : Erase a page */ +/* Input Parameters : DataFlash Service */ +/* : Page concerned */ +/* : */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_PageErase( + AT91PS_DataFlash pDataFlash, + unsigned int page) +{ + int cmdsize; + /* Test if the buffer command is legal */ + /* no data to transmit or receive */ + pDataFlash->pDataFlashDesc->tx_data_size = 0; + + cmdsize = 4; + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + return (AT91F_DataFlashSendCommand(pDataFlash, + DB_PAGE_ERASE, cmdsize, + page * pDataFlash->pDevice->pages_size)); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_BlockErase */ +/* Object : Erase a Block */ +/* Input Parameters : DataFlash Service */ +/* : Page concerned */ +/* : */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_BlockErase( + AT91PS_DataFlash pDataFlash, + unsigned int block) +{ + int cmdsize; + /* Test if the buffer command is legal */ + /* no data to transmit or receive */ + pDataFlash->pDataFlashDesc->tx_data_size = 0; + cmdsize = 4; + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + return (AT91F_DataFlashSendCommand(pDataFlash, DB_BLOCK_ERASE, cmdsize, + block * 8 * + pDataFlash->pDevice->pages_size)); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_WriteBufferToMain */ +/* Object : Write buffer to the main memory */ +/* Input Parameters : DataFlash Service */ +/* : = command to send to buffer1 or buffer2 */ +/* : = main memory address */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_WriteBufferToMain(AT91PS_DataFlash pDataFlash, + unsigned char BufferCommand, + unsigned int dest) +{ + int cmdsize; + /* Test if the buffer command is correct */ + if ((BufferCommand != DB_BUF1_PAGE_PGM) && + (BufferCommand != DB_BUF1_PAGE_ERASE_PGM) && + (BufferCommand != DB_BUF2_PAGE_PGM) && + (BufferCommand != DB_BUF2_PAGE_ERASE_PGM)) + return DATAFLASH_BAD_COMMAND; + + /* no data to transmit or receive */ + pDataFlash->pDataFlashDesc->tx_data_size = 0; + + cmdsize = 4; + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + /* Send the command to the dataflash */ + return (AT91F_DataFlashSendCommand(pDataFlash, BufferCommand, + cmdsize, dest)); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_PartialPageWrite */ +/* Object : Erase partielly a page */ +/* Input Parameters : = page number */ +/* : = adr to begin the fading */ +/* : = Number of bytes to erase */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_PartialPageWrite(AT91PS_DataFlash pDataFlash, + unsigned char *src, + unsigned int dest, + unsigned int size) +{ + unsigned int page; + unsigned int AdrInPage; + + page = dest / (pDataFlash->pDevice->pages_size); + AdrInPage = dest % (pDataFlash->pDevice->pages_size); + + /* Read the contents of the page in the Sram Buffer */ + AT91F_MainMemoryToBufferTransfert(pDataFlash, DB_PAGE_2_BUF1_TRF, page); + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + /*Update the SRAM buffer */ + AT91F_DataFlashWriteBuffer(pDataFlash, DB_BUF1_WRITE, src, + AdrInPage, size); + + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + + /* Erase page if a 128 Mbits device */ + if (pDataFlash->pDevice->pages_number >= 16384) { + AT91F_PageErase(pDataFlash, page); + /* Rewrite the modified Sram Buffer in the main memory */ + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + } + + /* Rewrite the modified Sram Buffer in the main memory */ + return (AT91F_WriteBufferToMain(pDataFlash, DB_BUF1_PAGE_ERASE_PGM, + (page * + pDataFlash->pDevice->pages_size))); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataFlashWrite */ +/* Object : */ +/* Input Parameters : <*src> = Source buffer */ +/* : = dataflash adress */ +/* : = data buffer size */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashWrite(AT91PS_DataFlash pDataFlash, + unsigned char *src, + int dest, int size) +{ + unsigned int length; + unsigned int page; + unsigned int status; + + AT91F_SpiEnable(pDataFlash->pDevice->cs); + + if ((dest + size) > (pDataFlash->pDevice->pages_size * + (pDataFlash->pDevice->pages_number))) + return DATAFLASH_MEMORY_OVERFLOW; + + /* If destination does not fit a page start address */ + if ((dest % ((unsigned int)(pDataFlash->pDevice->pages_size))) != 0) { + length = + pDataFlash->pDevice->pages_size - + (dest % ((unsigned int)(pDataFlash->pDevice->pages_size))); + + if (size < length) + length = size; + + if (!AT91F_PartialPageWrite(pDataFlash, src, dest, length)) + return DATAFLASH_ERROR; + + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + + /* Update size, source and destination pointers */ + size -= length; + dest += length; + src += length; + } + + while ((size - pDataFlash->pDevice->pages_size) >= 0) { + /* program dataflash page */ + page = (unsigned int)dest / (pDataFlash->pDevice->pages_size); + + status = AT91F_DataFlashWriteBuffer(pDataFlash, + DB_BUF1_WRITE, src, 0, + pDataFlash->pDevice-> + pages_size); + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + + status = AT91F_PageErase(pDataFlash, page); + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + if (!status) + return DATAFLASH_ERROR; + + status = AT91F_WriteBufferToMain(pDataFlash, + DB_BUF1_PAGE_PGM, dest); + if (!status) + return DATAFLASH_ERROR; + + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + + /* Update size, source and destination pointers */ + size -= pDataFlash->pDevice->pages_size; + dest += pDataFlash->pDevice->pages_size; + src += pDataFlash->pDevice->pages_size; + } + + /* If still some bytes to read */ + if (size > 0) { + /* program dataflash page */ + if (!AT91F_PartialPageWrite(pDataFlash, src, dest, size)) + return DATAFLASH_ERROR; + + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + } + return DATAFLASH_OK; +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataFlashRead */ +/* Object : Read a block in dataflash */ +/* Input Parameters : */ +/* Return value : */ +/*---------------------------------------------------------------------------*/ +int AT91F_DataFlashRead(AT91PS_DataFlash pDataFlash, + unsigned long addr, unsigned long size, char *buffer) +{ + unsigned long SizeToRead; + + AT91F_SpiEnable(pDataFlash->pDevice->cs); + + if (AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY) != DATAFLASH_OK) + return -1; + + while (size) { + SizeToRead = (size < 0x8000) ? size : 0x8000; + + if (AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY) != + DATAFLASH_OK) + return -1; + + if (AT91F_DataFlashContinuousRead(pDataFlash, addr, + (uchar *) buffer, + SizeToRead) != DATAFLASH_OK) + return -1; + + size -= SizeToRead; + addr += SizeToRead; + buffer += SizeToRead; + } + + return DATAFLASH_OK; +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataflashProbe */ +/* Object : */ +/* Input Parameters : */ +/* Return value : Dataflash status register */ +/*---------------------------------------------------------------------------*/ +int AT91F_DataflashProbe(int cs, AT91PS_DataflashDesc pDesc) +{ + AT91F_SpiEnable(cs); + AT91F_DataFlashGetStatus(pDesc); + return ((pDesc->command[1] == 0xFF) ? 0 : pDesc->command[1] & 0x3C); +} diff --git a/qemu/roms/u-boot/drivers/mtd/cfi_flash.c b/qemu/roms/u-boot/drivers/mtd/cfi_flash.c new file mode 100644 index 000000000..a389cd101 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/cfi_flash.c @@ -0,0 +1,2418 @@ +/* + * (C) Copyright 2002-2004 + * Brad Kemp, Seranoa Networks, Brad.Kemp@seranoa.com + * + * Copyright (C) 2003 Arabella Software Ltd. + * Yuli Barcohen + * + * Copyright (C) 2004 + * Ed Okerson + * + * Copyright (C) 2006 + * Tolunay Orkun + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* The DEBUG define must be before common to enable debugging */ +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This file implements a Common Flash Interface (CFI) driver for + * U-Boot. + * + * The width of the port and the width of the chips are determined at + * initialization. These widths are used to calculate the address for + * access CFI data structures. + * + * References + * JEDEC Standard JESD68 - Common Flash Interface (CFI) + * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes + * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets + * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet + * AMD CFI Specification, Release 2.0 December 1, 2001 + * AMD/Spansion Application Note: Migration from Single-byte to Three-byte + * Device IDs, Publication Number 25538 Revision A, November 8, 2001 + * + * Define CONFIG_SYS_WRITE_SWAPPED_DATA, if you have to swap the Bytes between + * reading and writing ... (yes there is such a Hardware). + */ + +static uint flash_offset_cfi[2] = { FLASH_OFFSET_CFI, FLASH_OFFSET_CFI_ALT }; +#ifdef CONFIG_FLASH_CFI_MTD +static uint flash_verbose = 1; +#else +#define flash_verbose 1 +#endif + +flash_info_t flash_info[CFI_MAX_FLASH_BANKS]; /* FLASH chips info */ + +/* + * Check if chip width is defined. If not, start detecting with 8bit. + */ +#ifndef CONFIG_SYS_FLASH_CFI_WIDTH +#define CONFIG_SYS_FLASH_CFI_WIDTH FLASH_CFI_8BIT +#endif + +/* + * 0xffff is an undefined value for the configuration register. When + * this value is returned, the configuration register shall not be + * written at all (default mode). + */ +static u16 cfi_flash_config_reg(int i) +{ +#ifdef CONFIG_SYS_CFI_FLASH_CONFIG_REGS + return ((u16 [])CONFIG_SYS_CFI_FLASH_CONFIG_REGS)[i]; +#else + return 0xffff; +#endif +} + +#if defined(CONFIG_SYS_MAX_FLASH_BANKS_DETECT) +int cfi_flash_num_flash_banks = CONFIG_SYS_MAX_FLASH_BANKS_DETECT; +#endif + +static phys_addr_t __cfi_flash_bank_addr(int i) +{ + return ((phys_addr_t [])CONFIG_SYS_FLASH_BANKS_LIST)[i]; +} +phys_addr_t cfi_flash_bank_addr(int i) + __attribute__((weak, alias("__cfi_flash_bank_addr"))); + +static unsigned long __cfi_flash_bank_size(int i) +{ +#ifdef CONFIG_SYS_FLASH_BANKS_SIZES + return ((unsigned long [])CONFIG_SYS_FLASH_BANKS_SIZES)[i]; +#else + return 0; +#endif +} +unsigned long cfi_flash_bank_size(int i) + __attribute__((weak, alias("__cfi_flash_bank_size"))); + +static void __flash_write8(u8 value, void *addr) +{ + __raw_writeb(value, addr); +} + +static void __flash_write16(u16 value, void *addr) +{ + __raw_writew(value, addr); +} + +static void __flash_write32(u32 value, void *addr) +{ + __raw_writel(value, addr); +} + +static void __flash_write64(u64 value, void *addr) +{ + /* No architectures currently implement __raw_writeq() */ + *(volatile u64 *)addr = value; +} + +static u8 __flash_read8(void *addr) +{ + return __raw_readb(addr); +} + +static u16 __flash_read16(void *addr) +{ + return __raw_readw(addr); +} + +static u32 __flash_read32(void *addr) +{ + return __raw_readl(addr); +} + +static u64 __flash_read64(void *addr) +{ + /* No architectures currently implement __raw_readq() */ + return *(volatile u64 *)addr; +} + +#ifdef CONFIG_CFI_FLASH_USE_WEAK_ACCESSORS +void flash_write8(u8 value, void *addr)__attribute__((weak, alias("__flash_write8"))); +void flash_write16(u16 value, void *addr)__attribute__((weak, alias("__flash_write16"))); +void flash_write32(u32 value, void *addr)__attribute__((weak, alias("__flash_write32"))); +void flash_write64(u64 value, void *addr)__attribute__((weak, alias("__flash_write64"))); +u8 flash_read8(void *addr)__attribute__((weak, alias("__flash_read8"))); +u16 flash_read16(void *addr)__attribute__((weak, alias("__flash_read16"))); +u32 flash_read32(void *addr)__attribute__((weak, alias("__flash_read32"))); +u64 flash_read64(void *addr)__attribute__((weak, alias("__flash_read64"))); +#else +#define flash_write8 __flash_write8 +#define flash_write16 __flash_write16 +#define flash_write32 __flash_write32 +#define flash_write64 __flash_write64 +#define flash_read8 __flash_read8 +#define flash_read16 __flash_read16 +#define flash_read32 __flash_read32 +#define flash_read64 __flash_read64 +#endif + +/*----------------------------------------------------------------------- + */ +#if defined(CONFIG_ENV_IS_IN_FLASH) || defined(CONFIG_ENV_ADDR_REDUND) || (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE) +flash_info_t *flash_get_info(ulong base) +{ + int i; + flash_info_t *info; + + for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { + info = &flash_info[i]; + if (info->size && info->start[0] <= base && + base <= info->start[0] + info->size - 1) + return info; + } + + return NULL; +} +#endif + +unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect) +{ + if (sect != (info->sector_count - 1)) + return info->start[sect + 1] - info->start[sect]; + else + return info->start[0] + info->size - info->start[sect]; +} + +/*----------------------------------------------------------------------- + * create an address based on the offset and the port width + */ +static inline void * +flash_map (flash_info_t * info, flash_sect_t sect, uint offset) +{ + unsigned int byte_offset = offset * info->portwidth; + + return (void *)(info->start[sect] + byte_offset); +} + +static inline void flash_unmap(flash_info_t *info, flash_sect_t sect, + unsigned int offset, void *addr) +{ +} + +/*----------------------------------------------------------------------- + * make a proper sized command based on the port and chip widths + */ +static void flash_make_cmd(flash_info_t *info, u32 cmd, void *cmdbuf) +{ + int i; + int cword_offset; + int cp_offset; +#if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA) + u32 cmd_le = cpu_to_le32(cmd); +#endif + uchar val; + uchar *cp = (uchar *) cmdbuf; + + for (i = info->portwidth; i > 0; i--){ + cword_offset = (info->portwidth-i)%info->chipwidth; +#if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA) + cp_offset = info->portwidth - i; + val = *((uchar*)&cmd_le + cword_offset); +#else + cp_offset = i - 1; + val = *((uchar*)&cmd + sizeof(u32) - cword_offset - 1); +#endif + cp[cp_offset] = (cword_offset >= sizeof(u32)) ? 0x00 : val; + } +} + +#ifdef DEBUG +/*----------------------------------------------------------------------- + * Debug support + */ +static void print_longlong (char *str, unsigned long long data) +{ + int i; + char *cp; + + cp = (char *) &data; + for (i = 0; i < 8; i++) + sprintf (&str[i * 2], "%2.2x", *cp++); +} + +static void flash_printqry (struct cfi_qry *qry) +{ + u8 *p = (u8 *)qry; + int x, y; + + for (x = 0; x < sizeof(struct cfi_qry); x += 16) { + debug("%02x : ", x); + for (y = 0; y < 16; y++) + debug("%2.2x ", p[x + y]); + debug(" "); + for (y = 0; y < 16; y++) { + unsigned char c = p[x + y]; + if (c >= 0x20 && c <= 0x7e) + debug("%c", c); + else + debug("."); + } + debug("\n"); + } +} +#endif + + +/*----------------------------------------------------------------------- + * read a character at a port width address + */ +static inline uchar flash_read_uchar (flash_info_t * info, uint offset) +{ + uchar *cp; + uchar retval; + + cp = flash_map (info, 0, offset); +#if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA) + retval = flash_read8(cp); +#else + retval = flash_read8(cp + info->portwidth - 1); +#endif + flash_unmap (info, 0, offset, cp); + return retval; +} + +/*----------------------------------------------------------------------- + * read a word at a port width address, assume 16bit bus + */ +static inline ushort flash_read_word (flash_info_t * info, uint offset) +{ + ushort *addr, retval; + + addr = flash_map (info, 0, offset); + retval = flash_read16 (addr); + flash_unmap (info, 0, offset, addr); + return retval; +} + + +/*----------------------------------------------------------------------- + * read a long word by picking the least significant byte of each maximum + * port size word. Swap for ppc format. + */ +static ulong flash_read_long (flash_info_t * info, flash_sect_t sect, + uint offset) +{ + uchar *addr; + ulong retval; + +#ifdef DEBUG + int x; +#endif + addr = flash_map (info, sect, offset); + +#ifdef DEBUG + debug ("long addr is at %p info->portwidth = %d\n", addr, + info->portwidth); + for (x = 0; x < 4 * info->portwidth; x++) { + debug ("addr[%x] = 0x%x\n", x, flash_read8(addr + x)); + } +#endif +#if defined(__LITTLE_ENDIAN) || defined(CONFIG_SYS_WRITE_SWAPPED_DATA) + retval = ((flash_read8(addr) << 16) | + (flash_read8(addr + info->portwidth) << 24) | + (flash_read8(addr + 2 * info->portwidth)) | + (flash_read8(addr + 3 * info->portwidth) << 8)); +#else + retval = ((flash_read8(addr + 2 * info->portwidth - 1) << 24) | + (flash_read8(addr + info->portwidth - 1) << 16) | + (flash_read8(addr + 4 * info->portwidth - 1) << 8) | + (flash_read8(addr + 3 * info->portwidth - 1))); +#endif + flash_unmap(info, sect, offset, addr); + + return retval; +} + +/* + * Write a proper sized command to the correct address + */ +void flash_write_cmd (flash_info_t * info, flash_sect_t sect, + uint offset, u32 cmd) +{ + + void *addr; + cfiword_t cword; + + addr = flash_map (info, sect, offset); + flash_make_cmd (info, cmd, &cword); + switch (info->portwidth) { + case FLASH_CFI_8BIT: + debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr, cmd, + cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + flash_write8(cword.c, addr); + break; + case FLASH_CFI_16BIT: + debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr, + cmd, cword.w, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + flash_write16(cword.w, addr); + break; + case FLASH_CFI_32BIT: + debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit\n", addr, + cmd, cword.l, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + flash_write32(cword.l, addr); + break; + case FLASH_CFI_64BIT: +#ifdef DEBUG + { + char str[20]; + + print_longlong (str, cword.ll); + + debug ("fwrite addr %p cmd %x %s 64 bit x %d bit\n", + addr, cmd, str, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + } +#endif + flash_write64(cword.ll, addr); + break; + } + + /* Ensure all the instructions are fully finished */ + sync(); + + flash_unmap(info, sect, offset, addr); +} + +static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect) +{ + flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_UNLOCK_START); + flash_write_cmd (info, sect, info->addr_unlock2, AMD_CMD_UNLOCK_ACK); +} + +/*----------------------------------------------------------------------- + */ +static int flash_isequal (flash_info_t * info, flash_sect_t sect, + uint offset, uchar cmd) +{ + void *addr; + cfiword_t cword; + int retval; + + addr = flash_map (info, sect, offset); + flash_make_cmd (info, cmd, &cword); + + debug ("is= cmd %x(%c) addr %p ", cmd, cmd, addr); + switch (info->portwidth) { + case FLASH_CFI_8BIT: + debug ("is= %x %x\n", flash_read8(addr), cword.c); + retval = (flash_read8(addr) == cword.c); + break; + case FLASH_CFI_16BIT: + debug ("is= %4.4x %4.4x\n", flash_read16(addr), cword.w); + retval = (flash_read16(addr) == cword.w); + break; + case FLASH_CFI_32BIT: + debug ("is= %8.8x %8.8lx\n", flash_read32(addr), cword.l); + retval = (flash_read32(addr) == cword.l); + break; + case FLASH_CFI_64BIT: +#ifdef DEBUG + { + char str1[20]; + char str2[20]; + + print_longlong (str1, flash_read64(addr)); + print_longlong (str2, cword.ll); + debug ("is= %s %s\n", str1, str2); + } +#endif + retval = (flash_read64(addr) == cword.ll); + break; + default: + retval = 0; + break; + } + flash_unmap(info, sect, offset, addr); + + return retval; +} + +/*----------------------------------------------------------------------- + */ +static int flash_isset (flash_info_t * info, flash_sect_t sect, + uint offset, uchar cmd) +{ + void *addr; + cfiword_t cword; + int retval; + + addr = flash_map (info, sect, offset); + flash_make_cmd (info, cmd, &cword); + switch (info->portwidth) { + case FLASH_CFI_8BIT: + retval = ((flash_read8(addr) & cword.c) == cword.c); + break; + case FLASH_CFI_16BIT: + retval = ((flash_read16(addr) & cword.w) == cword.w); + break; + case FLASH_CFI_32BIT: + retval = ((flash_read32(addr) & cword.l) == cword.l); + break; + case FLASH_CFI_64BIT: + retval = ((flash_read64(addr) & cword.ll) == cword.ll); + break; + default: + retval = 0; + break; + } + flash_unmap(info, sect, offset, addr); + + return retval; +} + +/*----------------------------------------------------------------------- + */ +static int flash_toggle (flash_info_t * info, flash_sect_t sect, + uint offset, uchar cmd) +{ + void *addr; + cfiword_t cword; + int retval; + + addr = flash_map (info, sect, offset); + flash_make_cmd (info, cmd, &cword); + switch (info->portwidth) { + case FLASH_CFI_8BIT: + retval = flash_read8(addr) != flash_read8(addr); + break; + case FLASH_CFI_16BIT: + retval = flash_read16(addr) != flash_read16(addr); + break; + case FLASH_CFI_32BIT: + retval = flash_read32(addr) != flash_read32(addr); + break; + case FLASH_CFI_64BIT: + retval = ( (flash_read32( addr ) != flash_read32( addr )) || + (flash_read32(addr+4) != flash_read32(addr+4)) ); + break; + default: + retval = 0; + break; + } + flash_unmap(info, sect, offset, addr); + + return retval; +} + +/* + * flash_is_busy - check to see if the flash is busy + * + * This routine checks the status of the chip and returns true if the + * chip is busy. + */ +static int flash_is_busy (flash_info_t * info, flash_sect_t sect) +{ + int retval; + + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE); + break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: +#ifdef CONFIG_FLASH_CFI_LEGACY + case CFI_CMDSET_AMD_LEGACY: +#endif + retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE); + break; + default: + retval = 0; + } + debug ("flash_is_busy: %d\n", retval); + return retval; +} + +/*----------------------------------------------------------------------- + * wait for XSR.7 to be set. Time out with an error if it does not. + * This routine does not set the flash to read-array mode. + */ +static int flash_status_check (flash_info_t * info, flash_sect_t sector, + ulong tout, char *prompt) +{ + ulong start; + +#if CONFIG_SYS_HZ != 1000 + if ((ulong)CONFIG_SYS_HZ > 100000) + tout *= (ulong)CONFIG_SYS_HZ / 1000; /* for a big HZ, avoid overflow */ + else + tout = DIV_ROUND_UP(tout * (ulong)CONFIG_SYS_HZ, 1000); +#endif + + /* Wait for command completion */ +#ifdef CONFIG_SYS_LOW_RES_TIMER + reset_timer(); +#endif + start = get_timer (0); + WATCHDOG_RESET(); + while (flash_is_busy (info, sector)) { + if (get_timer (start) > tout) { + printf ("Flash %s timeout at address %lx data %lx\n", + prompt, info->start[sector], + flash_read_long (info, sector, 0)); + flash_write_cmd (info, sector, 0, info->cmd_reset); + udelay(1); + return ERR_TIMOUT; + } + udelay (1); /* also triggers watchdog */ + } + return ERR_OK; +} + +/*----------------------------------------------------------------------- + * Wait for XSR.7 to be set, if it times out print an error, otherwise + * do a full status check. + * + * This routine sets the flash to read-array mode. + */ +static int flash_full_status_check (flash_info_t * info, flash_sect_t sector, + ulong tout, char *prompt) +{ + int retcode; + + retcode = flash_status_check (info, sector, tout, prompt); + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_EXTENDED: + case CFI_CMDSET_INTEL_STANDARD: + if ((retcode != ERR_OK) + && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) { + retcode = ERR_INVAL; + printf ("Flash %s error at address %lx\n", prompt, + info->start[sector]); + if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS | + FLASH_STATUS_PSLBS)) { + puts ("Command Sequence Error.\n"); + } else if (flash_isset (info, sector, 0, + FLASH_STATUS_ECLBS)) { + puts ("Block Erase Error.\n"); + retcode = ERR_NOT_ERASED; + } else if (flash_isset (info, sector, 0, + FLASH_STATUS_PSLBS)) { + puts ("Locking Error\n"); + } + if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) { + puts ("Block locked.\n"); + retcode = ERR_PROTECTED; + } + if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS)) + puts ("Vpp Low Error.\n"); + } + flash_write_cmd (info, sector, 0, info->cmd_reset); + udelay(1); + break; + default: + break; + } + return retcode; +} + +static int use_flash_status_poll(flash_info_t *info) +{ +#ifdef CONFIG_SYS_CFI_FLASH_STATUS_POLL + if (info->vendor == CFI_CMDSET_AMD_EXTENDED || + info->vendor == CFI_CMDSET_AMD_STANDARD) + return 1; +#endif + return 0; +} + +static int flash_status_poll(flash_info_t *info, void *src, void *dst, + ulong tout, char *prompt) +{ +#ifdef CONFIG_SYS_CFI_FLASH_STATUS_POLL + ulong start; + int ready; + +#if CONFIG_SYS_HZ != 1000 + if ((ulong)CONFIG_SYS_HZ > 100000) + tout *= (ulong)CONFIG_SYS_HZ / 1000; /* for a big HZ, avoid overflow */ + else + tout = DIV_ROUND_UP(tout * (ulong)CONFIG_SYS_HZ, 1000); +#endif + + /* Wait for command completion */ +#ifdef CONFIG_SYS_LOW_RES_TIMER + reset_timer(); +#endif + start = get_timer(0); + WATCHDOG_RESET(); + while (1) { + switch (info->portwidth) { + case FLASH_CFI_8BIT: + ready = flash_read8(dst) == flash_read8(src); + break; + case FLASH_CFI_16BIT: + ready = flash_read16(dst) == flash_read16(src); + break; + case FLASH_CFI_32BIT: + ready = flash_read32(dst) == flash_read32(src); + break; + case FLASH_CFI_64BIT: + ready = flash_read64(dst) == flash_read64(src); + break; + default: + ready = 0; + break; + } + if (ready) + break; + if (get_timer(start) > tout) { + printf("Flash %s timeout at address %lx data %lx\n", + prompt, (ulong)dst, (ulong)flash_read8(dst)); + return ERR_TIMOUT; + } + udelay(1); /* also triggers watchdog */ + } +#endif /* CONFIG_SYS_CFI_FLASH_STATUS_POLL */ + return ERR_OK; +} + +/*----------------------------------------------------------------------- + */ +static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c) +{ +#if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA) + unsigned short w; + unsigned int l; + unsigned long long ll; +#endif + + switch (info->portwidth) { + case FLASH_CFI_8BIT: + cword->c = c; + break; + case FLASH_CFI_16BIT: +#if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA) + w = c; + w <<= 8; + cword->w = (cword->w >> 8) | w; +#else + cword->w = (cword->w << 8) | c; +#endif + break; + case FLASH_CFI_32BIT: +#if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA) + l = c; + l <<= 24; + cword->l = (cword->l >> 8) | l; +#else + cword->l = (cword->l << 8) | c; +#endif + break; + case FLASH_CFI_64BIT: +#if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_WRITE_SWAPPED_DATA) + ll = c; + ll <<= 56; + cword->ll = (cword->ll >> 8) | ll; +#else + cword->ll = (cword->ll << 8) | c; +#endif + break; + } +} + +/* + * Loop through the sector table starting from the previously found sector. + * Searches forwards or backwards, dependent on the passed address. + */ +static flash_sect_t find_sector (flash_info_t * info, ulong addr) +{ + static flash_sect_t saved_sector; /* previously found sector */ + static flash_info_t *saved_info; /* previously used flash bank */ + flash_sect_t sector = saved_sector; + + if ((info != saved_info) || (sector >= info->sector_count)) + sector = 0; + + while ((info->start[sector] < addr) + && (sector < info->sector_count - 1)) + sector++; + while ((info->start[sector] > addr) && (sector > 0)) + /* + * also decrements the sector in case of an overshot + * in the first loop + */ + sector--; + + saved_sector = sector; + saved_info = info; + return sector; +} + +/*----------------------------------------------------------------------- + */ +static int flash_write_cfiword (flash_info_t * info, ulong dest, + cfiword_t cword) +{ + void *dstaddr = (void *)dest; + int flag; + flash_sect_t sect = 0; + char sect_found = 0; + + /* Check if Flash is (sufficiently) erased */ + switch (info->portwidth) { + case FLASH_CFI_8BIT: + flag = ((flash_read8(dstaddr) & cword.c) == cword.c); + break; + case FLASH_CFI_16BIT: + flag = ((flash_read16(dstaddr) & cword.w) == cword.w); + break; + case FLASH_CFI_32BIT: + flag = ((flash_read32(dstaddr) & cword.l) == cword.l); + break; + case FLASH_CFI_64BIT: + flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll); + break; + default: + flag = 0; + break; + } + if (!flag) + return ERR_NOT_ERASED; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts (); + + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_EXTENDED: + case CFI_CMDSET_INTEL_STANDARD: + flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE); + break; + case CFI_CMDSET_AMD_EXTENDED: + case CFI_CMDSET_AMD_STANDARD: + sect = find_sector(info, dest); + flash_unlock_seq (info, sect); + flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_WRITE); + sect_found = 1; + break; +#ifdef CONFIG_FLASH_CFI_LEGACY + case CFI_CMDSET_AMD_LEGACY: + sect = find_sector(info, dest); + flash_unlock_seq (info, 0); + flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_WRITE); + sect_found = 1; + break; +#endif + } + + switch (info->portwidth) { + case FLASH_CFI_8BIT: + flash_write8(cword.c, dstaddr); + break; + case FLASH_CFI_16BIT: + flash_write16(cword.w, dstaddr); + break; + case FLASH_CFI_32BIT: + flash_write32(cword.l, dstaddr); + break; + case FLASH_CFI_64BIT: + flash_write64(cword.ll, dstaddr); + break; + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts (); + + if (!sect_found) + sect = find_sector (info, dest); + + if (use_flash_status_poll(info)) + return flash_status_poll(info, &cword, dstaddr, + info->write_tout, "write"); + else + return flash_full_status_check(info, sect, + info->write_tout, "write"); +} + +#ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE + +static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, + int len) +{ + flash_sect_t sector; + int cnt; + int retcode; + void *src = cp; + void *dst = (void *)dest; + void *dst2 = dst; + int flag = 1; + uint offset = 0; + unsigned int shift; + uchar write_cmd; + + switch (info->portwidth) { + case FLASH_CFI_8BIT: + shift = 0; + break; + case FLASH_CFI_16BIT: + shift = 1; + break; + case FLASH_CFI_32BIT: + shift = 2; + break; + case FLASH_CFI_64BIT: + shift = 3; + break; + default: + retcode = ERR_INVAL; + goto out_unmap; + } + + cnt = len >> shift; + + while ((cnt-- > 0) && (flag == 1)) { + switch (info->portwidth) { + case FLASH_CFI_8BIT: + flag = ((flash_read8(dst2) & flash_read8(src)) == + flash_read8(src)); + src += 1, dst2 += 1; + break; + case FLASH_CFI_16BIT: + flag = ((flash_read16(dst2) & flash_read16(src)) == + flash_read16(src)); + src += 2, dst2 += 2; + break; + case FLASH_CFI_32BIT: + flag = ((flash_read32(dst2) & flash_read32(src)) == + flash_read32(src)); + src += 4, dst2 += 4; + break; + case FLASH_CFI_64BIT: + flag = ((flash_read64(dst2) & flash_read64(src)) == + flash_read64(src)); + src += 8, dst2 += 8; + break; + } + } + if (!flag) { + retcode = ERR_NOT_ERASED; + goto out_unmap; + } + + src = cp; + sector = find_sector (info, dest); + + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + write_cmd = (info->vendor == CFI_CMDSET_INTEL_PROG_REGIONS) ? + FLASH_CMD_WRITE_BUFFER_PROG : FLASH_CMD_WRITE_TO_BUFFER; + flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd (info, sector, 0, FLASH_CMD_READ_STATUS); + flash_write_cmd (info, sector, 0, write_cmd); + retcode = flash_status_check (info, sector, + info->buffer_write_tout, + "write to buffer"); + if (retcode == ERR_OK) { + /* reduce the number of loops by the width of + * the port */ + cnt = len >> shift; + flash_write_cmd (info, sector, 0, cnt - 1); + while (cnt-- > 0) { + switch (info->portwidth) { + case FLASH_CFI_8BIT: + flash_write8(flash_read8(src), dst); + src += 1, dst += 1; + break; + case FLASH_CFI_16BIT: + flash_write16(flash_read16(src), dst); + src += 2, dst += 2; + break; + case FLASH_CFI_32BIT: + flash_write32(flash_read32(src), dst); + src += 4, dst += 4; + break; + case FLASH_CFI_64BIT: + flash_write64(flash_read64(src), dst); + src += 8, dst += 8; + break; + default: + retcode = ERR_INVAL; + goto out_unmap; + } + } + flash_write_cmd (info, sector, 0, + FLASH_CMD_WRITE_BUFFER_CONFIRM); + retcode = flash_full_status_check ( + info, sector, info->buffer_write_tout, + "buffer write"); + } + + break; + + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + flash_unlock_seq(info,0); + +#ifdef CONFIG_FLASH_SPANSION_S29WS_N + offset = ((unsigned long)dst - info->start[sector]) >> shift; +#endif + flash_write_cmd(info, sector, offset, AMD_CMD_WRITE_TO_BUFFER); + cnt = len >> shift; + flash_write_cmd(info, sector, offset, cnt - 1); + + switch (info->portwidth) { + case FLASH_CFI_8BIT: + while (cnt-- > 0) { + flash_write8(flash_read8(src), dst); + src += 1, dst += 1; + } + break; + case FLASH_CFI_16BIT: + while (cnt-- > 0) { + flash_write16(flash_read16(src), dst); + src += 2, dst += 2; + } + break; + case FLASH_CFI_32BIT: + while (cnt-- > 0) { + flash_write32(flash_read32(src), dst); + src += 4, dst += 4; + } + break; + case FLASH_CFI_64BIT: + while (cnt-- > 0) { + flash_write64(flash_read64(src), dst); + src += 8, dst += 8; + } + break; + default: + retcode = ERR_INVAL; + goto out_unmap; + } + + flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM); + if (use_flash_status_poll(info)) + retcode = flash_status_poll(info, src - (1 << shift), + dst - (1 << shift), + info->buffer_write_tout, + "buffer write"); + else + retcode = flash_full_status_check(info, sector, + info->buffer_write_tout, + "buffer write"); + break; + + default: + debug ("Unknown Command Set\n"); + retcode = ERR_INVAL; + break; + } + +out_unmap: + return retcode; +} +#endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */ + + +/*----------------------------------------------------------------------- + */ +int flash_erase (flash_info_t * info, int s_first, int s_last) +{ + int rcode = 0; + int prot; + flash_sect_t sect; + int st; + + if (info->flash_id != FLASH_MAN_CFI) { + puts ("Can't erase unknown flash type - aborted\n"); + return 1; + } + if ((s_first < 0) || (s_first > s_last)) { + puts ("- no sectors to erase\n"); + return 1; + } + + prot = 0; + for (sect = s_first; sect <= s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else if (flash_verbose) { + putc ('\n'); + } + + + for (sect = s_first; sect <= s_last; sect++) { + if (ctrlc()) { + printf("\n"); + return 1; + } + + if (info->protect[sect] == 0) { /* not protected */ +#ifdef CONFIG_SYS_FLASH_CHECK_BLANK_BEFORE_ERASE + int k; + int size; + int erased; + u32 *flash; + + /* + * Check if whole sector is erased + */ + size = flash_sector_size(info, sect); + erased = 1; + flash = (u32 *)info->start[sect]; + /* divide by 4 for longword access */ + size = size >> 2; + for (k = 0; k < size; k++) { + if (flash_read32(flash++) != 0xffffffff) { + erased = 0; + break; + } + } + if (erased) { + if (flash_verbose) + putc(','); + continue; + } +#endif + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + flash_write_cmd (info, sect, 0, + FLASH_CMD_CLEAR_STATUS); + flash_write_cmd (info, sect, 0, + FLASH_CMD_BLOCK_ERASE); + flash_write_cmd (info, sect, 0, + FLASH_CMD_ERASE_CONFIRM); + break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + flash_unlock_seq (info, sect); + flash_write_cmd (info, sect, + info->addr_unlock1, + AMD_CMD_ERASE_START); + flash_unlock_seq (info, sect); + flash_write_cmd (info, sect, 0, + info->cmd_erase_sector); + break; +#ifdef CONFIG_FLASH_CFI_LEGACY + case CFI_CMDSET_AMD_LEGACY: + flash_unlock_seq (info, 0); + flash_write_cmd (info, 0, info->addr_unlock1, + AMD_CMD_ERASE_START); + flash_unlock_seq (info, 0); + flash_write_cmd (info, sect, 0, + AMD_CMD_ERASE_SECTOR); + break; +#endif + default: + debug ("Unkown flash vendor %d\n", + info->vendor); + break; + } + + if (use_flash_status_poll(info)) { + cfiword_t cword; + void *dest; + cword.ll = 0xffffffffffffffffULL; + dest = flash_map(info, sect, 0); + st = flash_status_poll(info, &cword, dest, + info->erase_blk_tout, "erase"); + flash_unmap(info, sect, 0, dest); + } else + st = flash_full_status_check(info, sect, + info->erase_blk_tout, + "erase"); + if (st) + rcode = 1; + else if (flash_verbose) + putc ('.'); + } + } + + if (flash_verbose) + puts (" done\n"); + + return rcode; +} + +#ifdef CONFIG_SYS_FLASH_EMPTY_INFO +static int sector_erased(flash_info_t *info, int i) +{ + int k; + int size; + u32 *flash; + + /* + * Check if whole sector is erased + */ + size = flash_sector_size(info, i); + flash = (u32 *)info->start[i]; + /* divide by 4 for longword access */ + size = size >> 2; + + for (k = 0; k < size; k++) { + if (flash_read32(flash++) != 0xffffffff) + return 0; /* not erased */ + } + + return 1; /* erased */ +} +#endif /* CONFIG_SYS_FLASH_EMPTY_INFO */ + +void flash_print_info (flash_info_t * info) +{ + int i; + + if (info->flash_id != FLASH_MAN_CFI) { + puts ("missing or unknown FLASH type\n"); + return; + } + + printf ("%s flash (%d x %d)", + info->name, + (info->portwidth << 3), (info->chipwidth << 3)); + if (info->size < 1024*1024) + printf (" Size: %ld kB in %d Sectors\n", + info->size >> 10, info->sector_count); + else + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + printf (" "); + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + printf ("Intel Prog Regions"); + break; + case CFI_CMDSET_INTEL_STANDARD: + printf ("Intel Standard"); + break; + case CFI_CMDSET_INTEL_EXTENDED: + printf ("Intel Extended"); + break; + case CFI_CMDSET_AMD_STANDARD: + printf ("AMD Standard"); + break; + case CFI_CMDSET_AMD_EXTENDED: + printf ("AMD Extended"); + break; +#ifdef CONFIG_FLASH_CFI_LEGACY + case CFI_CMDSET_AMD_LEGACY: + printf ("AMD Legacy"); + break; +#endif + default: + printf ("Unknown (%d)", info->vendor); + break; + } + printf (" command set, Manufacturer ID: 0x%02X, Device ID: 0x", + info->manufacturer_id); + printf (info->chipwidth == FLASH_CFI_16BIT ? "%04X" : "%02X", + info->device_id); + if ((info->device_id & 0xff) == 0x7E) { + printf(info->chipwidth == FLASH_CFI_16BIT ? "%04X" : "%02X", + info->device_id2); + } + if ((info->vendor == CFI_CMDSET_AMD_STANDARD) && (info->legacy_unlock)) + printf("\n Advanced Sector Protection (PPB) enabled"); + printf ("\n Erase timeout: %ld ms, write timeout: %ld ms\n", + info->erase_blk_tout, + info->write_tout); + if (info->buffer_size > 1) { + printf (" Buffer write timeout: %ld ms, " + "buffer size: %d bytes\n", + info->buffer_write_tout, + info->buffer_size); + } + + puts ("\n Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { + if (ctrlc()) + break; + if ((i % 5) == 0) + putc('\n'); +#ifdef CONFIG_SYS_FLASH_EMPTY_INFO + /* print empty and read-only info */ + printf (" %08lX %c %s ", + info->start[i], + sector_erased(info, i) ? 'E' : ' ', + info->protect[i] ? "RO" : " "); +#else /* ! CONFIG_SYS_FLASH_EMPTY_INFO */ + printf (" %08lX %s ", + info->start[i], + info->protect[i] ? "RO" : " "); +#endif + } + putc ('\n'); + return; +} + +/*----------------------------------------------------------------------- + * This is used in a few places in write_buf() to show programming + * progress. Making it a function is nasty because it needs to do side + * effect updates to digit and dots. Repeated code is nasty too, so + * we define it once here. + */ +#ifdef CONFIG_FLASH_SHOW_PROGRESS +#define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub) \ + if (flash_verbose) { \ + dots -= dots_sub; \ + if ((scale > 0) && (dots <= 0)) { \ + if ((digit % 5) == 0) \ + printf ("%d", digit / 5); \ + else \ + putc ('.'); \ + digit--; \ + dots += scale; \ + } \ + } +#else +#define FLASH_SHOW_PROGRESS(scale, dots, digit, dots_sub) +#endif + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) +{ + ulong wp; + uchar *p; + int aln; + cfiword_t cword; + int i, rc; +#ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE + int buffered_size; +#endif +#ifdef CONFIG_FLASH_SHOW_PROGRESS + int digit = CONFIG_FLASH_SHOW_PROGRESS; + int scale = 0; + int dots = 0; + + /* + * Suppress if there are fewer than CONFIG_FLASH_SHOW_PROGRESS writes. + */ + if (cnt >= CONFIG_FLASH_SHOW_PROGRESS) { + scale = (int)((cnt + CONFIG_FLASH_SHOW_PROGRESS - 1) / + CONFIG_FLASH_SHOW_PROGRESS); + } +#endif + + /* get lower aligned address */ + wp = (addr & ~(info->portwidth - 1)); + + /* handle unaligned start */ + if ((aln = addr - wp) != 0) { + cword.l = 0; + p = (uchar *)wp; + for (i = 0; i < aln; ++i) + flash_add_byte (info, &cword, flash_read8(p + i)); + + for (; (i < info->portwidth) && (cnt > 0); i++) { + flash_add_byte (info, &cword, *src++); + cnt--; + } + for (; (cnt == 0) && (i < info->portwidth); ++i) + flash_add_byte (info, &cword, flash_read8(p + i)); + + rc = flash_write_cfiword (info, wp, cword); + if (rc != 0) + return rc; + + wp += i; + FLASH_SHOW_PROGRESS(scale, dots, digit, i); + } + + /* handle the aligned part */ +#ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE + buffered_size = (info->portwidth / info->chipwidth); + buffered_size *= info->buffer_size; + while (cnt >= info->portwidth) { + /* prohibit buffer write when buffer_size is 1 */ + if (info->buffer_size == 1) { + cword.l = 0; + for (i = 0; i < info->portwidth; i++) + flash_add_byte (info, &cword, *src++); + if ((rc = flash_write_cfiword (info, wp, cword)) != 0) + return rc; + wp += info->portwidth; + cnt -= info->portwidth; + continue; + } + + /* write buffer until next buffered_size aligned boundary */ + i = buffered_size - (wp % buffered_size); + if (i > cnt) + i = cnt; + if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK) + return rc; + i -= i & (info->portwidth - 1); + wp += i; + src += i; + cnt -= i; + FLASH_SHOW_PROGRESS(scale, dots, digit, i); + /* Only check every once in a while */ + if ((cnt & 0xFFFF) < buffered_size && ctrlc()) + return ERR_ABORTED; + } +#else + while (cnt >= info->portwidth) { + cword.l = 0; + for (i = 0; i < info->portwidth; i++) { + flash_add_byte (info, &cword, *src++); + } + if ((rc = flash_write_cfiword (info, wp, cword)) != 0) + return rc; + wp += info->portwidth; + cnt -= info->portwidth; + FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth); + /* Only check every once in a while */ + if ((cnt & 0xFFFF) < info->portwidth && ctrlc()) + return ERR_ABORTED; + } +#endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */ + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + cword.l = 0; + p = (uchar *)wp; + for (i = 0; (i < info->portwidth) && (cnt > 0); ++i) { + flash_add_byte (info, &cword, *src++); + --cnt; + } + for (; i < info->portwidth; ++i) + flash_add_byte (info, &cword, flash_read8(p + i)); + + return flash_write_cfiword (info, wp, cword); +} + +static inline int manufact_match(flash_info_t *info, u32 manu) +{ + return info->manufacturer_id == ((manu & FLASH_VENDMASK) >> 16); +} + +/*----------------------------------------------------------------------- + */ +#ifdef CONFIG_SYS_FLASH_PROTECTION + +static int cfi_protect_bugfix(flash_info_t *info, long sector, int prot) +{ + if (manufact_match(info, INTEL_MANUFACT) + && info->device_id == NUMONYX_256MBIT) { + /* + * see errata called + * "Numonyx Axcell P33/P30 Specification Update" :) + */ + flash_write_cmd(info, sector, 0, FLASH_CMD_READ_ID); + if (!flash_isequal(info, sector, FLASH_OFFSET_PROTECT, + prot)) { + /* + * cmd must come before FLASH_CMD_PROTECT + 20us + * Disable interrupts which might cause a timeout here. + */ + int flag = disable_interrupts(); + unsigned short cmd; + + if (prot) + cmd = FLASH_CMD_PROTECT_SET; + else + cmd = FLASH_CMD_PROTECT_CLEAR; + flash_write_cmd(info, sector, 0, + FLASH_CMD_PROTECT); + flash_write_cmd(info, sector, 0, cmd); + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + } + return 1; + } + return 0; +} + +int flash_real_protect (flash_info_t * info, long sector, int prot) +{ + int retcode = 0; + + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + if (!cfi_protect_bugfix(info, sector, prot)) { + flash_write_cmd(info, sector, 0, + FLASH_CMD_CLEAR_STATUS); + flash_write_cmd(info, sector, 0, + FLASH_CMD_PROTECT); + if (prot) + flash_write_cmd(info, sector, 0, + FLASH_CMD_PROTECT_SET); + else + flash_write_cmd(info, sector, 0, + FLASH_CMD_PROTECT_CLEAR); + + } + break; + case CFI_CMDSET_AMD_EXTENDED: + case CFI_CMDSET_AMD_STANDARD: + /* U-Boot only checks the first byte */ + if (manufact_match(info, ATM_MANUFACT)) { + if (prot) { + flash_unlock_seq (info, 0); + flash_write_cmd (info, 0, + info->addr_unlock1, + ATM_CMD_SOFTLOCK_START); + flash_unlock_seq (info, 0); + flash_write_cmd (info, sector, 0, + ATM_CMD_LOCK_SECT); + } else { + flash_write_cmd (info, 0, + info->addr_unlock1, + AMD_CMD_UNLOCK_START); + if (info->device_id == ATM_ID_BV6416) + flash_write_cmd (info, sector, + 0, ATM_CMD_UNLOCK_SECT); + } + } + if (info->legacy_unlock) { + int flag = disable_interrupts(); + int lock_flag; + + flash_unlock_seq(info, 0); + flash_write_cmd(info, 0, info->addr_unlock1, + AMD_CMD_SET_PPB_ENTRY); + lock_flag = flash_isset(info, sector, 0, 0x01); + if (prot) { + if (lock_flag) { + flash_write_cmd(info, sector, 0, + AMD_CMD_PPB_LOCK_BC1); + flash_write_cmd(info, sector, 0, + AMD_CMD_PPB_LOCK_BC2); + } + debug("sector %ld %slocked\n", sector, + lock_flag ? "" : "already "); + } else { + if (!lock_flag) { + debug("unlock %ld\n", sector); + flash_write_cmd(info, 0, 0, + AMD_CMD_PPB_UNLOCK_BC1); + flash_write_cmd(info, 0, 0, + AMD_CMD_PPB_UNLOCK_BC2); + } + debug("sector %ld %sunlocked\n", sector, + !lock_flag ? "" : "already "); + } + if (flag) + enable_interrupts(); + + if (flash_status_check(info, sector, + info->erase_blk_tout, + prot ? "protect" : "unprotect")) + printf("status check error\n"); + + flash_write_cmd(info, 0, 0, + AMD_CMD_SET_PPB_EXIT_BC1); + flash_write_cmd(info, 0, 0, + AMD_CMD_SET_PPB_EXIT_BC2); + } + break; +#ifdef CONFIG_FLASH_CFI_LEGACY + case CFI_CMDSET_AMD_LEGACY: + flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT); + if (prot) + flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_SET); + else + flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_CLEAR); +#endif + }; + + /* + * Flash needs to be in status register read mode for + * flash_full_status_check() to work correctly + */ + flash_write_cmd(info, sector, 0, FLASH_CMD_READ_STATUS); + if ((retcode = + flash_full_status_check (info, sector, info->erase_blk_tout, + prot ? "protect" : "unprotect")) == 0) { + + info->protect[sector] = prot; + + /* + * On some of Intel's flash chips (marked via legacy_unlock) + * unprotect unprotects all locking. + */ + if ((prot == 0) && (info->legacy_unlock)) { + flash_sect_t i; + + for (i = 0; i < info->sector_count; i++) { + if (info->protect[i]) + flash_real_protect (info, i, 1); + } + } + } + return retcode; +} + +/*----------------------------------------------------------------------- + * flash_read_user_serial - read the OneTimeProgramming cells + */ +void flash_read_user_serial (flash_info_t * info, void *buffer, int offset, + int len) +{ + uchar *src; + uchar *dst; + + dst = buffer; + src = flash_map (info, 0, FLASH_OFFSET_USER_PROTECTION); + flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); + memcpy (dst, src + offset, len); + flash_write_cmd (info, 0, 0, info->cmd_reset); + udelay(1); + flash_unmap(info, 0, FLASH_OFFSET_USER_PROTECTION, src); +} + +/* + * flash_read_factory_serial - read the device Id from the protection area + */ +void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset, + int len) +{ + uchar *src; + + src = flash_map (info, 0, FLASH_OFFSET_INTEL_PROTECTION); + flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); + memcpy (buffer, src + offset, len); + flash_write_cmd (info, 0, 0, info->cmd_reset); + udelay(1); + flash_unmap(info, 0, FLASH_OFFSET_INTEL_PROTECTION, src); +} + +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + +/*----------------------------------------------------------------------- + * Reverse the order of the erase regions in the CFI QRY structure. + * This is needed for chips that are either a) correctly detected as + * top-boot, or b) buggy. + */ +static void cfi_reverse_geometry(struct cfi_qry *qry) +{ + unsigned int i, j; + u32 tmp; + + for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) { + tmp = get_unaligned(&(qry->erase_region_info[i])); + put_unaligned(get_unaligned(&(qry->erase_region_info[j])), + &(qry->erase_region_info[i])); + put_unaligned(tmp, &(qry->erase_region_info[j])); + } +} + +/*----------------------------------------------------------------------- + * read jedec ids from device and set corresponding fields in info struct + * + * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct + * + */ +static void cmdset_intel_read_jedec_ids(flash_info_t *info) +{ + flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); + udelay(1); + flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID); + udelay(1000); /* some flash are slow to respond */ + info->manufacturer_id = flash_read_uchar (info, + FLASH_OFFSET_MANUFACTURER_ID); + info->device_id = (info->chipwidth == FLASH_CFI_16BIT) ? + flash_read_word (info, FLASH_OFFSET_DEVICE_ID) : + flash_read_uchar (info, FLASH_OFFSET_DEVICE_ID); + flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); +} + +static int cmdset_intel_init(flash_info_t *info, struct cfi_qry *qry) +{ + info->cmd_reset = FLASH_CMD_RESET; + + cmdset_intel_read_jedec_ids(info); + flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI); + +#ifdef CONFIG_SYS_FLASH_PROTECTION + /* read legacy lock/unlock bit from intel flash */ + if (info->ext_addr) { + info->legacy_unlock = flash_read_uchar (info, + info->ext_addr + 5) & 0x08; + } +#endif + + return 0; +} + +static void cmdset_amd_read_jedec_ids(flash_info_t *info) +{ + ushort bankId = 0; + uchar manuId; + + flash_write_cmd(info, 0, 0, AMD_CMD_RESET); + flash_unlock_seq(info, 0); + flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID); + udelay(1000); /* some flash are slow to respond */ + + manuId = flash_read_uchar (info, FLASH_OFFSET_MANUFACTURER_ID); + /* JEDEC JEP106Z specifies ID codes up to bank 7 */ + while (manuId == FLASH_CONTINUATION_CODE && bankId < 0x800) { + bankId += 0x100; + manuId = flash_read_uchar (info, + bankId | FLASH_OFFSET_MANUFACTURER_ID); + } + info->manufacturer_id = manuId; + + switch (info->chipwidth){ + case FLASH_CFI_8BIT: + info->device_id = flash_read_uchar (info, + FLASH_OFFSET_DEVICE_ID); + if (info->device_id == 0x7E) { + /* AMD 3-byte (expanded) device ids */ + info->device_id2 = flash_read_uchar (info, + FLASH_OFFSET_DEVICE_ID2); + info->device_id2 <<= 8; + info->device_id2 |= flash_read_uchar (info, + FLASH_OFFSET_DEVICE_ID3); + } + break; + case FLASH_CFI_16BIT: + info->device_id = flash_read_word (info, + FLASH_OFFSET_DEVICE_ID); + if ((info->device_id & 0xff) == 0x7E) { + /* AMD 3-byte (expanded) device ids */ + info->device_id2 = flash_read_uchar (info, + FLASH_OFFSET_DEVICE_ID2); + info->device_id2 <<= 8; + info->device_id2 |= flash_read_uchar (info, + FLASH_OFFSET_DEVICE_ID3); + } + break; + default: + break; + } + flash_write_cmd(info, 0, 0, AMD_CMD_RESET); + udelay(1); +} + +static int cmdset_amd_init(flash_info_t *info, struct cfi_qry *qry) +{ + info->cmd_reset = AMD_CMD_RESET; + info->cmd_erase_sector = AMD_CMD_ERASE_SECTOR; + + cmdset_amd_read_jedec_ids(info); + flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI); + +#ifdef CONFIG_SYS_FLASH_PROTECTION + if (info->ext_addr) { + /* read sector protect/unprotect scheme (at 0x49) */ + if (flash_read_uchar(info, info->ext_addr + 9) == 0x8) + info->legacy_unlock = 1; + } +#endif + + return 0; +} + +#ifdef CONFIG_FLASH_CFI_LEGACY +static void flash_read_jedec_ids (flash_info_t * info) +{ + info->manufacturer_id = 0; + info->device_id = 0; + info->device_id2 = 0; + + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + cmdset_intel_read_jedec_ids(info); + break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + cmdset_amd_read_jedec_ids(info); + break; + default: + break; + } +} + +/*----------------------------------------------------------------------- + * Call board code to request info about non-CFI flash. + * board_flash_get_legacy needs to fill in at least: + * info->portwidth, info->chipwidth and info->interface for Jedec probing. + */ +static int flash_detect_legacy(phys_addr_t base, int banknum) +{ + flash_info_t *info = &flash_info[banknum]; + + if (board_flash_get_legacy(base, banknum, info)) { + /* board code may have filled info completely. If not, we + use JEDEC ID probing. */ + if (!info->vendor) { + int modes[] = { + CFI_CMDSET_AMD_STANDARD, + CFI_CMDSET_INTEL_STANDARD + }; + int i; + + for (i = 0; i < ARRAY_SIZE(modes); i++) { + info->vendor = modes[i]; + info->start[0] = + (ulong)map_physmem(base, + info->portwidth, + MAP_NOCACHE); + if (info->portwidth == FLASH_CFI_8BIT + && info->interface == FLASH_CFI_X8X16) { + info->addr_unlock1 = 0x2AAA; + info->addr_unlock2 = 0x5555; + } else { + info->addr_unlock1 = 0x5555; + info->addr_unlock2 = 0x2AAA; + } + flash_read_jedec_ids(info); + debug("JEDEC PROBE: ID %x %x %x\n", + info->manufacturer_id, + info->device_id, + info->device_id2); + if (jedec_flash_match(info, info->start[0])) + break; + else + unmap_physmem((void *)info->start[0], + info->portwidth); + } + } + + switch(info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + info->cmd_reset = FLASH_CMD_RESET; + break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + case CFI_CMDSET_AMD_LEGACY: + info->cmd_reset = AMD_CMD_RESET; + break; + } + info->flash_id = FLASH_MAN_CFI; + return 1; + } + return 0; /* use CFI */ +} +#else +static inline int flash_detect_legacy(phys_addr_t base, int banknum) +{ + return 0; /* use CFI */ +} +#endif + +/*----------------------------------------------------------------------- + * detect if flash is compatible with the Common Flash Interface (CFI) + * http://www.jedec.org/download/search/jesd68.pdf + */ +static void flash_read_cfi (flash_info_t *info, void *buf, + unsigned int start, size_t len) +{ + u8 *p = buf; + unsigned int i; + + for (i = 0; i < len; i++) + p[i] = flash_read_uchar(info, start + i); +} + +static void __flash_cmd_reset(flash_info_t *info) +{ + /* + * We do not yet know what kind of commandset to use, so we issue + * the reset command in both Intel and AMD variants, in the hope + * that AMD flash roms ignore the Intel command. + */ + flash_write_cmd(info, 0, 0, AMD_CMD_RESET); + udelay(1); + flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); +} +void flash_cmd_reset(flash_info_t *info) + __attribute__((weak,alias("__flash_cmd_reset"))); + +static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry) +{ + int cfi_offset; + + /* Issue FLASH reset command */ + flash_cmd_reset(info); + + for (cfi_offset = 0; cfi_offset < ARRAY_SIZE(flash_offset_cfi); + cfi_offset++) { + flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset], + FLASH_CMD_CFI); + if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q') + && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R') + && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) { + flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP, + sizeof(struct cfi_qry)); + info->interface = le16_to_cpu(qry->interface_desc); + + info->cfi_offset = flash_offset_cfi[cfi_offset]; + debug ("device interface is %d\n", + info->interface); + debug ("found port %d chip %d ", + info->portwidth, info->chipwidth); + debug ("port %d bits chip %d bits\n", + info->portwidth << CFI_FLASH_SHIFT_WIDTH, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + + /* calculate command offsets as in the Linux driver */ + info->addr_unlock1 = 0x555; + info->addr_unlock2 = 0x2aa; + + /* + * modify the unlock address if we are + * in compatibility mode + */ + if ( /* x8/x16 in x8 mode */ + ((info->chipwidth == FLASH_CFI_BY8) && + (info->interface == FLASH_CFI_X8X16)) || + /* x16/x32 in x16 mode */ + ((info->chipwidth == FLASH_CFI_BY16) && + (info->interface == FLASH_CFI_X16X32))) + { + info->addr_unlock1 = 0xaaa; + info->addr_unlock2 = 0x555; + } + + info->name = "CFI conformant"; + return 1; + } + } + + return 0; +} + +static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry) +{ + debug ("flash detect cfi\n"); + + for (info->portwidth = CONFIG_SYS_FLASH_CFI_WIDTH; + info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) { + for (info->chipwidth = FLASH_CFI_BY8; + info->chipwidth <= info->portwidth; + info->chipwidth <<= 1) + if (__flash_detect_cfi(info, qry)) + return 1; + } + debug ("not found\n"); + return 0; +} + +/* + * Manufacturer-specific quirks. Add workarounds for geometry + * reversal, etc. here. + */ +static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry) +{ + /* check if flash geometry needs reversal */ + if (qry->num_erase_regions > 1) { + /* reverse geometry if top boot part */ + if (info->cfi_version < 0x3131) { + /* CFI < 1.1, try to guess from device id */ + if ((info->device_id & 0x80) != 0) + cfi_reverse_geometry(qry); + } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) { + /* CFI >= 1.1, deduct from top/bottom flag */ + /* note: ext_addr is valid since cfi_version > 0 */ + cfi_reverse_geometry(qry); + } + } +} + +static void flash_fixup_atmel(flash_info_t *info, struct cfi_qry *qry) +{ + int reverse_geometry = 0; + + /* Check the "top boot" bit in the PRI */ + if (info->ext_addr && !(flash_read_uchar(info, info->ext_addr + 6) & 1)) + reverse_geometry = 1; + + /* AT49BV6416(T) list the erase regions in the wrong order. + * However, the device ID is identical with the non-broken + * AT49BV642D they differ in the high byte. + */ + if (info->device_id == 0xd6 || info->device_id == 0xd2) + reverse_geometry = !reverse_geometry; + + if (reverse_geometry) + cfi_reverse_geometry(qry); +} + +static void flash_fixup_stm(flash_info_t *info, struct cfi_qry *qry) +{ + /* check if flash geometry needs reversal */ + if (qry->num_erase_regions > 1) { + /* reverse geometry if top boot part */ + if (info->cfi_version < 0x3131) { + /* CFI < 1.1, guess by device id */ + if (info->device_id == 0x22CA || /* M29W320DT */ + info->device_id == 0x2256 || /* M29W320ET */ + info->device_id == 0x22D7) { /* M29W800DT */ + cfi_reverse_geometry(qry); + } + } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) { + /* CFI >= 1.1, deduct from top/bottom flag */ + /* note: ext_addr is valid since cfi_version > 0 */ + cfi_reverse_geometry(qry); + } + } +} + +static void flash_fixup_sst(flash_info_t *info, struct cfi_qry *qry) +{ + /* + * SST, for many recent nor parallel flashes, says they are + * CFI-conformant. This is not true, since qry struct. + * reports a std. AMD command set (0x0002), while SST allows to + * erase two different sector sizes for the same memory. + * 64KB sector (SST call it block) needs 0x30 to be erased. + * 4KB sector (SST call it sector) needs 0x50 to be erased. + * Since CFI query detect the 4KB number of sectors, users expects + * a sector granularity of 4KB, and it is here set. + */ + if (info->device_id == 0x5D23 || /* SST39VF3201B */ + info->device_id == 0x5C23) { /* SST39VF3202B */ + /* set sector granularity to 4KB */ + info->cmd_erase_sector=0x50; + } +} + +static void flash_fixup_num(flash_info_t *info, struct cfi_qry *qry) +{ + /* + * The M29EW devices seem to report the CFI information wrong + * when it's in 8 bit mode. + * There's an app note from Numonyx on this issue. + * So adjust the buffer size for M29EW while operating in 8-bit mode + */ + if (((qry->max_buf_write_size) > 0x8) && + (info->device_id == 0x7E) && + (info->device_id2 == 0x2201 || + info->device_id2 == 0x2301 || + info->device_id2 == 0x2801 || + info->device_id2 == 0x4801)) { + debug("Adjusted buffer size on Numonyx flash" + " M29EW family in 8 bit mode\n"); + qry->max_buf_write_size = 0x8; + } +} + +/* + * The following code cannot be run from FLASH! + * + */ +ulong flash_get_size (phys_addr_t base, int banknum) +{ + flash_info_t *info = &flash_info[banknum]; + int i, j; + flash_sect_t sect_cnt; + phys_addr_t sector; + unsigned long tmp; + int size_ratio; + uchar num_erase_regions; + int erase_region_size; + int erase_region_count; + struct cfi_qry qry; + unsigned long max_size; + + memset(&qry, 0, sizeof(qry)); + + info->ext_addr = 0; + info->cfi_version = 0; +#ifdef CONFIG_SYS_FLASH_PROTECTION + info->legacy_unlock = 0; +#endif + + info->start[0] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE); + + if (flash_detect_cfi (info, &qry)) { + info->vendor = le16_to_cpu(get_unaligned(&(qry.p_id))); + info->ext_addr = le16_to_cpu(get_unaligned(&(qry.p_adr))); + num_erase_regions = qry.num_erase_regions; + + if (info->ext_addr) { + info->cfi_version = (ushort) flash_read_uchar (info, + info->ext_addr + 3) << 8; + info->cfi_version |= (ushort) flash_read_uchar (info, + info->ext_addr + 4); + } + +#ifdef DEBUG + flash_printqry (&qry); +#endif + + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + cmdset_intel_init(info, &qry); + break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + cmdset_amd_init(info, &qry); + break; + default: + printf("CFI: Unknown command set 0x%x\n", + info->vendor); + /* + * Unfortunately, this means we don't know how + * to get the chip back to Read mode. Might + * as well try an Intel-style reset... + */ + flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); + return 0; + } + + /* Do manufacturer-specific fixups */ + switch (info->manufacturer_id) { + case 0x0001: /* AMD */ + case 0x0037: /* AMIC */ + flash_fixup_amd(info, &qry); + break; + case 0x001f: + flash_fixup_atmel(info, &qry); + break; + case 0x0020: + flash_fixup_stm(info, &qry); + break; + case 0x00bf: /* SST */ + flash_fixup_sst(info, &qry); + break; + case 0x0089: /* Numonyx */ + flash_fixup_num(info, &qry); + break; + } + + debug ("manufacturer is %d\n", info->vendor); + debug ("manufacturer id is 0x%x\n", info->manufacturer_id); + debug ("device id is 0x%x\n", info->device_id); + debug ("device id2 is 0x%x\n", info->device_id2); + debug ("cfi version is 0x%04x\n", info->cfi_version); + + size_ratio = info->portwidth / info->chipwidth; + /* if the chip is x8/x16 reduce the ratio by half */ + if ((info->interface == FLASH_CFI_X8X16) + && (info->chipwidth == FLASH_CFI_BY8)) { + size_ratio >>= 1; + } + debug ("size_ratio %d port %d bits chip %d bits\n", + size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + info->size = 1 << qry.dev_size; + /* multiply the size by the number of chips */ + info->size *= size_ratio; + max_size = cfi_flash_bank_size(banknum); + if (max_size && (info->size > max_size)) { + debug("[truncated from %ldMiB]", info->size >> 20); + info->size = max_size; + } + debug ("found %d erase regions\n", num_erase_regions); + sect_cnt = 0; + sector = base; + for (i = 0; i < num_erase_regions; i++) { + if (i > NUM_ERASE_REGIONS) { + printf ("%d erase regions found, only %d used\n", + num_erase_regions, NUM_ERASE_REGIONS); + break; + } + + tmp = le32_to_cpu(get_unaligned( + &(qry.erase_region_info[i]))); + debug("erase region %u: 0x%08lx\n", i, tmp); + + erase_region_count = (tmp & 0xffff) + 1; + tmp >>= 16; + erase_region_size = + (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128; + debug ("erase_region_count = %d erase_region_size = %d\n", + erase_region_count, erase_region_size); + for (j = 0; j < erase_region_count; j++) { + if (sector - base >= info->size) + break; + if (sect_cnt >= CONFIG_SYS_MAX_FLASH_SECT) { + printf("ERROR: too many flash sectors\n"); + break; + } + info->start[sect_cnt] = + (ulong)map_physmem(sector, + info->portwidth, + MAP_NOCACHE); + sector += (erase_region_size * size_ratio); + + /* + * Only read protection status from + * supported devices (intel...) + */ + switch (info->vendor) { + case CFI_CMDSET_INTEL_PROG_REGIONS: + case CFI_CMDSET_INTEL_EXTENDED: + case CFI_CMDSET_INTEL_STANDARD: + /* + * Set flash to read-id mode. Otherwise + * reading protected status is not + * guaranteed. + */ + flash_write_cmd(info, sect_cnt, 0, + FLASH_CMD_READ_ID); + info->protect[sect_cnt] = + flash_isset (info, sect_cnt, + FLASH_OFFSET_PROTECT, + FLASH_STATUS_PROTECT); + break; + case CFI_CMDSET_AMD_EXTENDED: + case CFI_CMDSET_AMD_STANDARD: + if (!info->legacy_unlock) { + /* default: not protected */ + info->protect[sect_cnt] = 0; + break; + } + + /* Read protection (PPB) from sector */ + flash_write_cmd(info, 0, 0, + info->cmd_reset); + flash_unlock_seq(info, 0); + flash_write_cmd(info, 0, + info->addr_unlock1, + FLASH_CMD_READ_ID); + info->protect[sect_cnt] = + flash_isset( + info, sect_cnt, + FLASH_OFFSET_PROTECT, + FLASH_STATUS_PROTECT); + break; + default: + /* default: not protected */ + info->protect[sect_cnt] = 0; + } + + sect_cnt++; + } + } + + info->sector_count = sect_cnt; + info->buffer_size = 1 << le16_to_cpu(qry.max_buf_write_size); + tmp = 1 << qry.block_erase_timeout_typ; + info->erase_blk_tout = tmp * + (1 << qry.block_erase_timeout_max); + tmp = (1 << qry.buf_write_timeout_typ) * + (1 << qry.buf_write_timeout_max); + + /* round up when converting to ms */ + info->buffer_write_tout = (tmp + 999) / 1000; + tmp = (1 << qry.word_write_timeout_typ) * + (1 << qry.word_write_timeout_max); + /* round up when converting to ms */ + info->write_tout = (tmp + 999) / 1000; + info->flash_id = FLASH_MAN_CFI; + if ((info->interface == FLASH_CFI_X8X16) && + (info->chipwidth == FLASH_CFI_BY8)) { + /* XXX - Need to test on x8/x16 in parallel. */ + info->portwidth >>= 1; + } + + flash_write_cmd (info, 0, 0, info->cmd_reset); + } + + return (info->size); +} + +#ifdef CONFIG_FLASH_CFI_MTD +void flash_set_verbose(uint v) +{ + flash_verbose = v; +} +#endif + +static void cfi_flash_set_config_reg(u32 base, u16 val) +{ +#ifdef CONFIG_SYS_CFI_FLASH_CONFIG_REGS + /* + * Only set this config register if really defined + * to a valid value (0xffff is invalid) + */ + if (val == 0xffff) + return; + + /* + * Set configuration register. Data is "encrypted" in the 16 lower + * address bits. + */ + flash_write16(FLASH_CMD_SETUP, (void *)(base + (val << 1))); + flash_write16(FLASH_CMD_SET_CR_CONFIRM, (void *)(base + (val << 1))); + + /* + * Finally issue reset-command to bring device back to + * read-array mode + */ + flash_write16(FLASH_CMD_RESET, (void *)base); +#endif +} + +/*----------------------------------------------------------------------- + */ + +void flash_protect_default(void) +{ +#if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST) + int i; + struct apl_s { + ulong start; + ulong size; + } apl[] = CONFIG_SYS_FLASH_AUTOPROTECT_LIST; +#endif + + /* Monitor protection ON by default */ +#if (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE) && \ + (!defined(CONFIG_MONITOR_IS_IN_RAM)) + flash_protect(FLAG_PROTECT_SET, + CONFIG_SYS_MONITOR_BASE, + CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, + flash_get_info(CONFIG_SYS_MONITOR_BASE)); +#endif + + /* Environment protection ON by default */ +#ifdef CONFIG_ENV_IS_IN_FLASH + flash_protect(FLAG_PROTECT_SET, + CONFIG_ENV_ADDR, + CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1, + flash_get_info(CONFIG_ENV_ADDR)); +#endif + + /* Redundant environment protection ON by default */ +#ifdef CONFIG_ENV_ADDR_REDUND + flash_protect(FLAG_PROTECT_SET, + CONFIG_ENV_ADDR_REDUND, + CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SECT_SIZE - 1, + flash_get_info(CONFIG_ENV_ADDR_REDUND)); +#endif + +#if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST) + for (i = 0; i < ARRAY_SIZE(apl); i++) { + debug("autoprotecting from %08lx to %08lx\n", + apl[i].start, apl[i].start + apl[i].size - 1); + flash_protect(FLAG_PROTECT_SET, + apl[i].start, + apl[i].start + apl[i].size - 1, + flash_get_info(apl[i].start)); + } +#endif +} + +unsigned long flash_init (void) +{ + unsigned long size = 0; + int i; + +#ifdef CONFIG_SYS_FLASH_PROTECTION + /* read environment from EEPROM */ + char s[64]; + getenv_f("unlock", s, sizeof(s)); +#endif + + /* Init: no FLASHes known */ + for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + + /* Optionally write flash configuration register */ + cfi_flash_set_config_reg(cfi_flash_bank_addr(i), + cfi_flash_config_reg(i)); + + if (!flash_detect_legacy(cfi_flash_bank_addr(i), i)) + flash_get_size(cfi_flash_bank_addr(i), i); + size += flash_info[i].size; + if (flash_info[i].flash_id == FLASH_UNKNOWN) { +#ifndef CONFIG_SYS_FLASH_QUIET_TEST + printf ("## Unknown flash on Bank %d " + "- Size = 0x%08lx = %ld MB\n", + i+1, flash_info[i].size, + flash_info[i].size >> 20); +#endif /* CONFIG_SYS_FLASH_QUIET_TEST */ + } +#ifdef CONFIG_SYS_FLASH_PROTECTION + else if ((s != NULL) && (strcmp(s, "yes") == 0)) { + /* + * Only the U-Boot image and it's environment + * is protected, all other sectors are + * unprotected (unlocked) if flash hardware + * protection is used (CONFIG_SYS_FLASH_PROTECTION) + * and the environment variable "unlock" is + * set to "yes". + */ + if (flash_info[i].legacy_unlock) { + int k; + + /* + * Disable legacy_unlock temporarily, + * since flash_real_protect would + * relock all other sectors again + * otherwise. + */ + flash_info[i].legacy_unlock = 0; + + /* + * Legacy unlocking (e.g. Intel J3) -> + * unlock only one sector. This will + * unlock all sectors. + */ + flash_real_protect (&flash_info[i], 0, 0); + + flash_info[i].legacy_unlock = 1; + + /* + * Manually mark other sectors as + * unlocked (unprotected) + */ + for (k = 1; k < flash_info[i].sector_count; k++) + flash_info[i].protect[k] = 0; + } else { + /* + * No legancy unlocking -> unlock all sectors + */ + flash_protect (FLAG_PROTECT_CLEAR, + flash_info[i].start[0], + flash_info[i].start[0] + + flash_info[i].size - 1, + &flash_info[i]); + } + } +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } + + flash_protect_default(); +#ifdef CONFIG_FLASH_CFI_MTD + cfi_mtd_init(); +#endif + + return (size); +} diff --git a/qemu/roms/u-boot/drivers/mtd/cfi_mtd.c b/qemu/roms/u-boot/drivers/mtd/cfi_mtd.c new file mode 100644 index 000000000..ac805ff1e --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/cfi_mtd.c @@ -0,0 +1,263 @@ +/* + * (C) Copyright 2008 Semihalf + * + * Written by: Piotr Ziecik + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include +#include +#include + +static struct mtd_info cfi_mtd_info[CFI_MAX_FLASH_BANKS]; +static char cfi_mtd_names[CFI_MAX_FLASH_BANKS][16]; +#ifdef CONFIG_MTD_CONCAT +static char c_mtd_name[16]; +#endif + +static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + flash_info_t *fi = mtd->priv; + size_t a_start = fi->start[0] + instr->addr; + size_t a_end = a_start + instr->len; + int s_first = -1; + int s_last = -1; + int error, sect; + + for (sect = 0; sect < fi->sector_count; sect++) { + if (a_start == fi->start[sect]) + s_first = sect; + + if (sect < fi->sector_count - 1) { + if (a_end == fi->start[sect + 1]) { + s_last = sect; + break; + } + } else { + s_last = sect; + break; + } + } + + if (s_first >= 0 && s_first <= s_last) { + instr->state = MTD_ERASING; + + flash_set_verbose(0); + error = flash_erase(fi, s_first, s_last); + flash_set_verbose(1); + + if (error) { + instr->state = MTD_ERASE_FAILED; + return -EIO; + } + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + return 0; + } + + return -EINVAL; +} + +static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + flash_info_t *fi = mtd->priv; + u_char *f = (u_char*)(fi->start[0]) + from; + + memcpy(buf, f, len); + *retlen = len; + + return 0; +} + +static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + flash_info_t *fi = mtd->priv; + u_long t = fi->start[0] + to; + int error; + + flash_set_verbose(0); + error = write_buff(fi, (u_char*)buf, t, len); + flash_set_verbose(1); + + if (!error) { + *retlen = len; + return 0; + } + + return -EIO; +} + +static void cfi_mtd_sync(struct mtd_info *mtd) +{ + /* + * This function should wait until all pending operations + * finish. However this driver is fully synchronous, so + * this function returns immediately + */ +} + +static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + flash_info_t *fi = mtd->priv; + + flash_set_verbose(0); + flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs, + fi->start[0] + ofs + len - 1, fi); + flash_set_verbose(1); + + return 0; +} + +static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + flash_info_t *fi = mtd->priv; + + flash_set_verbose(0); + flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs, + fi->start[0] + ofs + len - 1, fi); + flash_set_verbose(1); + + return 0; +} + +static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi) +{ + int sect_size = 0; + int sect_size_old = 0; + int sect; + int regions = 0; + int numblocks = 0; + ulong offset; + ulong base_addr; + + /* + * First detect the number of eraseregions so that we can allocate + * the array of eraseregions correctly + */ + for (sect = 0; sect < fi->sector_count; sect++) { + if (sect_size_old != flash_sector_size(fi, sect)) + regions++; + sect_size_old = flash_sector_size(fi, sect); + } + + switch (regions) { + case 0: + return 1; + case 1: /* flash has uniform erase size */ + mtd->numeraseregions = 0; + mtd->erasesize = sect_size_old; + return 0; + } + + mtd->numeraseregions = regions; + mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) * regions); + + /* + * Now detect the largest sector and fill the eraseregions + */ + regions = 0; + base_addr = offset = fi->start[0]; + sect_size_old = flash_sector_size(fi, 0); + for (sect = 0; sect < fi->sector_count; sect++) { + if (sect_size_old != flash_sector_size(fi, sect)) { + mtd->eraseregions[regions].offset = offset - base_addr; + mtd->eraseregions[regions].erasesize = sect_size_old; + mtd->eraseregions[regions].numblocks = numblocks; + /* Now start counting the next eraseregions */ + numblocks = 0; + regions++; + offset = fi->start[sect]; + } + numblocks++; + + /* + * Select the largest sector size as erasesize (e.g. for UBI) + */ + if (flash_sector_size(fi, sect) > sect_size) + sect_size = flash_sector_size(fi, sect); + + sect_size_old = flash_sector_size(fi, sect); + } + + /* + * Set the last region + */ + mtd->eraseregions[regions].offset = offset - base_addr; + mtd->eraseregions[regions].erasesize = sect_size_old; + mtd->eraseregions[regions].numblocks = numblocks; + + mtd->erasesize = sect_size; + + return 0; +} + +int cfi_mtd_init(void) +{ + struct mtd_info *mtd; + flash_info_t *fi; + int error, i; +#ifdef CONFIG_MTD_CONCAT + int devices_found = 0; + struct mtd_info *mtd_list[CONFIG_SYS_MAX_FLASH_BANKS]; +#endif + + for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { + fi = &flash_info[i]; + mtd = &cfi_mtd_info[i]; + + memset(mtd, 0, sizeof(struct mtd_info)); + + error = cfi_mtd_set_erasesize(mtd, fi); + if (error) + continue; + + sprintf(cfi_mtd_names[i], "nor%d", i); + mtd->name = cfi_mtd_names[i]; + mtd->type = MTD_NORFLASH; + mtd->flags = MTD_CAP_NORFLASH; + mtd->size = fi->size; + mtd->writesize = 1; + + mtd->_erase = cfi_mtd_erase; + mtd->_read = cfi_mtd_read; + mtd->_write = cfi_mtd_write; + mtd->_sync = cfi_mtd_sync; + mtd->_lock = cfi_mtd_lock; + mtd->_unlock = cfi_mtd_unlock; + mtd->priv = fi; + + if (add_mtd_device(mtd)) + return -ENOMEM; + +#ifdef CONFIG_MTD_CONCAT + mtd_list[devices_found++] = mtd; +#endif + } + +#ifdef CONFIG_MTD_CONCAT + if (devices_found > 1) { + /* + * We detected multiple devices. Concatenate them together. + */ + sprintf(c_mtd_name, "nor%d", devices_found); + mtd = mtd_concat_create(mtd_list, devices_found, c_mtd_name); + + if (mtd == NULL) + return -ENXIO; + + if (add_mtd_device(mtd)) + return -ENOMEM; + } +#endif /* CONFIG_MTD_CONCAT */ + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/dataflash.c b/qemu/roms/u-boot/drivers/mtd/dataflash.c new file mode 100644 index 000000000..3fb6ed6df --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/dataflash.c @@ -0,0 +1,447 @@ +/* + * LowLevel function for ATMEL DataFlash support + * Author : Hamid Ikdoumi (Atmel) + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include + +static AT91S_DataFlash DataFlashInst; + +extern void AT91F_SpiInit (void); +extern int AT91F_DataflashProbe (int i, AT91PS_DataflashDesc pDesc); +extern int AT91F_DataFlashRead (AT91PS_DataFlash pDataFlash, + unsigned long addr, + unsigned long size, char *buffer); +extern int AT91F_DataFlashWrite( AT91PS_DataFlash pDataFlash, + unsigned char *src, + int dest, + int size ); + +int AT91F_DataflashInit (void) +{ + int i, j; + int dfcode; + int part; + int found[CONFIG_SYS_MAX_DATAFLASH_BANKS]; + unsigned char protected; + + AT91F_SpiInit (); + + for (i = 0; i < CONFIG_SYS_MAX_DATAFLASH_BANKS; i++) { + found[i] = 0; + dataflash_info[i].Desc.state = IDLE; + dataflash_info[i].id = 0; + dataflash_info[i].Device.pages_number = 0; + dfcode = AT91F_DataflashProbe (cs[i].cs, + &dataflash_info[i].Desc); + + switch (dfcode) { + case AT45DB021: + dataflash_info[i].Device.pages_number = 1024; + dataflash_info[i].Device.pages_size = 264; + dataflash_info[i].Device.page_offset = 9; + dataflash_info[i].Device.byte_mask = 0x300; + dataflash_info[i].Device.cs = cs[i].cs; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i].addr; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + case AT45DB081: + dataflash_info[i].Device.pages_number = 4096; + dataflash_info[i].Device.pages_size = 264; + dataflash_info[i].Device.page_offset = 9; + dataflash_info[i].Device.byte_mask = 0x300; + dataflash_info[i].Device.cs = cs[i].cs; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i].addr; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + case AT45DB161: + dataflash_info[i].Device.pages_number = 4096; + dataflash_info[i].Device.pages_size = 528; + dataflash_info[i].Device.page_offset = 10; + dataflash_info[i].Device.byte_mask = 0x300; + dataflash_info[i].Device.cs = cs[i].cs; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i].addr; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + case AT45DB321: + dataflash_info[i].Device.pages_number = 8192; + dataflash_info[i].Device.pages_size = 528; + dataflash_info[i].Device.page_offset = 10; + dataflash_info[i].Device.byte_mask = 0x300; + dataflash_info[i].Device.cs = cs[i].cs; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i].addr; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + case AT45DB642: + dataflash_info[i].Device.pages_number = 8192; + dataflash_info[i].Device.pages_size = 1056; + dataflash_info[i].Device.page_offset = 11; + dataflash_info[i].Device.byte_mask = 0x700; + dataflash_info[i].Device.cs = cs[i].cs; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i].addr; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + case AT45DB128: + dataflash_info[i].Device.pages_number = 16384; + dataflash_info[i].Device.pages_size = 1056; + dataflash_info[i].Device.page_offset = 11; + dataflash_info[i].Device.byte_mask = 0x700; + dataflash_info[i].Device.cs = cs[i].cs; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i].addr; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + default: + dfcode = 0; + break; + } + /* set the last area end to the dataflash size*/ + dataflash_info[i].end_address = + (dataflash_info[i].Device.pages_number * + dataflash_info[i].Device.pages_size) - 1; + + part = 0; + /* set the area addresses */ + for(j = 0; j < NB_DATAFLASH_AREA; j++) { + if(found[i]!=0) { + dataflash_info[i].Device.area_list[j].start = + area_list[part].start + + dataflash_info[i].logical_address; + if(area_list[part].end == 0xffffffff) { + dataflash_info[i].Device.area_list[j].end = + dataflash_info[i].end_address + + dataflash_info[i].logical_address; + } else { + dataflash_info[i].Device.area_list[j].end = + area_list[part].end + + dataflash_info[i].logical_address; + } + protected = area_list[part].protected; + /* Set the environment according to the label...*/ + if(protected == FLAG_PROTECT_INVALID) { + dataflash_info[i].Device.area_list[j].protected = + FLAG_PROTECT_INVALID; + } else { + dataflash_info[i].Device.area_list[j].protected = + protected; + } + strcpy((char*)(dataflash_info[i].Device.area_list[j].label), + (const char *)area_list[part].label); + } + part++; + } + } + return found[0]; +} + +void AT91F_DataflashSetEnv (void) +{ + int i, j; + int part; + unsigned char env; + unsigned char s[32]; /* Will fit a long int in hex */ + unsigned long start; + + for (i = 0, part= 0; i < CONFIG_SYS_MAX_DATAFLASH_BANKS; i++) { + for(j = 0; j < NB_DATAFLASH_AREA; j++) { + env = area_list[part].setenv; + /* Set the environment according to the label...*/ + if((env & FLAG_SETENV) == FLAG_SETENV) { + start = dataflash_info[i].Device.area_list[j].start; + sprintf((char*) s,"%lX",start); + setenv((char*) area_list[part].label,(char*) s); + } + part++; + } + } +} + +void dataflash_print_info (void) +{ + int i, j; + + for (i = 0; i < CONFIG_SYS_MAX_DATAFLASH_BANKS; i++) { + if (dataflash_info[i].id != 0) { + printf("DataFlash:"); + switch (dataflash_info[i].id) { + case AT45DB021: + printf("AT45DB021\n"); + break; + case AT45DB161: + printf("AT45DB161\n"); + break; + + case AT45DB321: + printf("AT45DB321\n"); + break; + + case AT45DB642: + printf("AT45DB642\n"); + break; + case AT45DB128: + printf("AT45DB128\n"); + break; + } + + printf("Nb pages: %6d\n" + "Page Size: %6d\n" + "Size=%8d bytes\n" + "Logical address: 0x%08X\n", + (unsigned int) dataflash_info[i].Device.pages_number, + (unsigned int) dataflash_info[i].Device.pages_size, + (unsigned int) dataflash_info[i].Device.pages_number * + dataflash_info[i].Device.pages_size, + (unsigned int) dataflash_info[i].logical_address); + for (j = 0; j < NB_DATAFLASH_AREA; j++) { + switch(dataflash_info[i].Device.area_list[j].protected) { + case FLAG_PROTECT_SET: + case FLAG_PROTECT_CLEAR: + printf("Area %i:\t%08lX to %08lX %s", j, + dataflash_info[i].Device.area_list[j].start, + dataflash_info[i].Device.area_list[j].end, + (dataflash_info[i].Device.area_list[j].protected==FLAG_PROTECT_SET) ? "(RO)" : " "); + printf(" %s\n", dataflash_info[i].Device.area_list[j].label); + break; + case FLAG_PROTECT_INVALID: + break; + } + } + } + } +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataflashSelect */ +/* Object : Select the correct device */ +/*---------------------------------------------------------------------------*/ +AT91PS_DataFlash AT91F_DataflashSelect (AT91PS_DataFlash pFlash, + unsigned long *addr) +{ + char addr_valid = 0; + int i; + + for (i = 0; i < CONFIG_SYS_MAX_DATAFLASH_BANKS; i++) + if ( dataflash_info[i].id + && ((((int) *addr) & 0xFF000000) == + dataflash_info[i].logical_address)) { + addr_valid = 1; + break; + } + if (!addr_valid) { + pFlash = (AT91PS_DataFlash) 0; + return pFlash; + } + pFlash->pDataFlashDesc = &(dataflash_info[i].Desc); + pFlash->pDevice = &(dataflash_info[i].Device); + *addr -= dataflash_info[i].logical_address; + return (pFlash); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : addr_dataflash */ +/* Object : Test if address is valid */ +/*---------------------------------------------------------------------------*/ +int addr_dataflash (unsigned long addr) +{ + int addr_valid = 0; + int i; + + for (i = 0; i < CONFIG_SYS_MAX_DATAFLASH_BANKS; i++) { + if ((((int) addr) & 0xFF000000) == + dataflash_info[i].logical_address) { + addr_valid = 1; + break; + } + } + + return addr_valid; +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : size_dataflash */ +/* Object : Test if address is valid regarding the size */ +/*---------------------------------------------------------------------------*/ +int size_dataflash (AT91PS_DataFlash pdataFlash, unsigned long addr, + unsigned long size) +{ + /* is outside the dataflash */ + if (((int)addr & 0x0FFFFFFF) > (pdataFlash->pDevice->pages_size * + pdataFlash->pDevice->pages_number)) return 0; + /* is too large for the dataflash */ + if (size > ((pdataFlash->pDevice->pages_size * + pdataFlash->pDevice->pages_number) - + ((int)addr & 0x0FFFFFFF))) return 0; + + return 1; +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : prot_dataflash */ +/* Object : Test if destination area is protected */ +/*---------------------------------------------------------------------------*/ +int prot_dataflash (AT91PS_DataFlash pdataFlash, unsigned long addr) +{ + int area; + + /* find area */ + for (area = 0; area < NB_DATAFLASH_AREA; area++) { + if ((addr >= pdataFlash->pDevice->area_list[area].start) && + (addr < pdataFlash->pDevice->area_list[area].end)) + break; + } + if (area == NB_DATAFLASH_AREA) + return -1; + + /*test protection value*/ + if (pdataFlash->pDevice->area_list[area].protected == FLAG_PROTECT_SET) + return 0; + if (pdataFlash->pDevice->area_list[area].protected == FLAG_PROTECT_INVALID) + return 0; + + return 1; +} + +/*--------------------------------------------------------------------------*/ +/* Function Name : dataflash_real_protect */ +/* Object : protect/unprotect area */ +/*--------------------------------------------------------------------------*/ +int dataflash_real_protect (int flag, unsigned long start_addr, + unsigned long end_addr) +{ + int i,j, area1, area2, addr_valid = 0; + + /* find dataflash */ + for (i = 0; i < CONFIG_SYS_MAX_DATAFLASH_BANKS; i++) { + if ((((int) start_addr) & 0xF0000000) == + dataflash_info[i].logical_address) { + addr_valid = 1; + break; + } + } + if (!addr_valid) { + return -1; + } + /* find start area */ + for (area1 = 0; area1 < NB_DATAFLASH_AREA; area1++) { + if (start_addr == dataflash_info[i].Device.area_list[area1].start) + break; + } + if (area1 == NB_DATAFLASH_AREA) return -1; + /* find end area */ + for (area2 = 0; area2 < NB_DATAFLASH_AREA; area2++) { + if (end_addr == dataflash_info[i].Device.area_list[area2].end) + break; + } + if (area2 == NB_DATAFLASH_AREA) + return -1; + + /*set protection value*/ + for(j = area1; j < area2 + 1 ; j++) + if(dataflash_info[i].Device.area_list[j].protected + != FLAG_PROTECT_INVALID) { + if (flag == 0) { + dataflash_info[i].Device.area_list[j].protected + = FLAG_PROTECT_CLEAR; + } else { + dataflash_info[i].Device.area_list[j].protected + = FLAG_PROTECT_SET; + } + } + + return (area2 - area1 + 1); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : read_dataflash */ +/* Object : dataflash memory read */ +/*---------------------------------------------------------------------------*/ +int read_dataflash (unsigned long addr, unsigned long size, char *result) +{ + unsigned long AddrToRead = addr; + AT91PS_DataFlash pFlash = &DataFlashInst; + + pFlash = AT91F_DataflashSelect (pFlash, &AddrToRead); + + if (pFlash == 0) + return ERR_UNKNOWN_FLASH_TYPE; + + if (size_dataflash(pFlash,addr,size) == 0) + return ERR_INVAL; + + return (AT91F_DataFlashRead (pFlash, AddrToRead, size, result)); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : write_dataflash */ +/* Object : write a block in dataflash */ +/*---------------------------------------------------------------------------*/ +int write_dataflash (unsigned long addr_dest, unsigned long addr_src, + unsigned long size) +{ + unsigned long AddrToWrite = addr_dest; + AT91PS_DataFlash pFlash = &DataFlashInst; + + pFlash = AT91F_DataflashSelect (pFlash, &AddrToWrite); + + if (pFlash == 0) + return ERR_UNKNOWN_FLASH_TYPE; + + if (size_dataflash(pFlash,addr_dest,size) == 0) + return ERR_INVAL; + + if (prot_dataflash(pFlash,addr_dest) == 0) + return ERR_PROTECTED; + + if (AddrToWrite == -1) + return -1; + + return AT91F_DataFlashWrite (pFlash, (uchar *)addr_src, + AddrToWrite, size); +} + +void dataflash_perror (int err) +{ + switch (err) { + case ERR_OK: + break; + case ERR_TIMOUT: + printf("Timeout writing to DataFlash\n"); + break; + case ERR_PROTECTED: + printf("Can't write to protected/invalid DataFlash sectors\n"); + break; + case ERR_INVAL: + printf("Outside available DataFlash\n"); + break; + case ERR_UNKNOWN_FLASH_TYPE: + printf("Unknown Type of DataFlash\n"); + break; + case ERR_PROG_ERROR: + printf("General DataFlash Programming Error\n"); + break; + default: + printf("%s[%d] FIXME: rc=%d\n", __FILE__, __LINE__, err); + break; + } +} diff --git a/qemu/roms/u-boot/drivers/mtd/ftsmc020.c b/qemu/roms/u-boot/drivers/mtd/ftsmc020.c new file mode 100644 index 000000000..e2e808227 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ftsmc020.c @@ -0,0 +1,38 @@ +/* + * (C) Copyright 2009 Faraday Technology + * Po-Yu Chuang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +struct ftsmc020_config { + unsigned int config; + unsigned int timing; +}; + +static void ftsmc020_setup_bank(unsigned int bank, struct ftsmc020_config *cfg) +{ + struct ftsmc020 *smc = (struct ftsmc020 *)CONFIG_FTSMC020_BASE; + + if (bank > 3) { + printf("bank # %u invalid\n", bank); + return; + } + + writel(cfg->config, &smc->bank[bank].cr); + writel(cfg->timing, &smc->bank[bank].tpr); +} + +void ftsmc020_init(void) +{ + struct ftsmc020_config config[] = CONFIG_SYS_FTSMC020_CONFIGS; + int i; + + for (i = 0; i < ARRAY_SIZE(config); i++) + ftsmc020_setup_bank(i, &config[i]); +} diff --git a/qemu/roms/u-boot/drivers/mtd/jedec_flash.c b/qemu/roms/u-boot/drivers/mtd/jedec_flash.c new file mode 100644 index 000000000..593b9b843 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/jedec_flash.c @@ -0,0 +1,442 @@ +/* + * (C) Copyright 2007 + * Michael Schwingen, + * + * based in great part on jedec_probe.c from linux kernel: + * (C) 2000 Red Hat. GPL'd. + * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* The DEBUG define must be before common to enable debugging */ +/*#define DEBUG*/ + +#include +#include +#include +#include +#include + +#define P_ID_AMD_STD CFI_CMDSET_AMD_LEGACY + +/* AMD */ +#define AM29DL800BB 0x22CB +#define AM29DL800BT 0x224A + +#define AM29F400BB 0x22AB +#define AM29F800BB 0x2258 +#define AM29F800BT 0x22D6 +#define AM29LV400BB 0x22BA +#define AM29LV400BT 0x22B9 +#define AM29LV800BB 0x225B +#define AM29LV800BT 0x22DA +#define AM29LV160DT 0x22C4 +#define AM29LV160DB 0x2249 +#define AM29F017D 0x003D +#define AM29F016D 0x00AD +#define AM29F080 0x00D5 +#define AM29F040 0x00A4 +#define AM29LV040B 0x004F +#define AM29F032B 0x0041 +#define AM29F002T 0x00B0 + +/* SST */ +#define SST39LF800 0x2781 +#define SST39LF160 0x2782 +#define SST39VF1601 0x234b +#define SST39LF512 0x00D4 +#define SST39LF010 0x00D5 +#define SST39LF020 0x00D6 +#define SST39LF040 0x00D7 +#define SST39SF010A 0x00B5 +#define SST39SF020A 0x00B6 + +/* STM */ +#define STM29F400BB 0x00D6 + +/* MXIC */ +#define MX29LV040 0x004F + +/* WINBOND */ +#define W39L040A 0x00D6 + +/* AMIC */ +#define A29L040 0x0092 + +/* EON */ +#define EN29LV040A 0x004F + +/* + * Unlock address sets for AMD command sets. + * Intel command sets use the MTD_UADDR_UNNECESSARY. + * Each identifier, except MTD_UADDR_UNNECESSARY, and + * MTD_UADDR_NO_SUPPORT must be defined below in unlock_addrs[]. + * MTD_UADDR_NOT_SUPPORTED must be 0 so that structure + * initialization need not require initializing all of the + * unlock addresses for all bit widths. + */ +enum uaddr { + MTD_UADDR_NOT_SUPPORTED = 0, /* data width not supported */ + MTD_UADDR_0x0555_0x02AA, + MTD_UADDR_0x0555_0x0AAA, + MTD_UADDR_0x5555_0x2AAA, + MTD_UADDR_0x0AAA_0x0555, + MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ + MTD_UADDR_UNNECESSARY, /* Does not require any address */ +}; + + +struct unlock_addr { + u32 addr1; + u32 addr2; +}; + + +/* + * I don't like the fact that the first entry in unlock_addrs[] + * exists, but is for MTD_UADDR_NOT_SUPPORTED - and, therefore, + * should not be used. The problem is that structures with + * initializers have extra fields initialized to 0. It is _very_ + * desireable to have the unlock address entries for unsupported + * data widths automatically initialized - that means that + * MTD_UADDR_NOT_SUPPORTED must be 0 and the first entry here + * must go unused. + */ +static const struct unlock_addr unlock_addrs[] = { + [MTD_UADDR_NOT_SUPPORTED] = { + .addr1 = 0xffff, + .addr2 = 0xffff + }, + + [MTD_UADDR_0x0555_0x02AA] = { + .addr1 = 0x0555, + .addr2 = 0x02aa + }, + + [MTD_UADDR_0x0555_0x0AAA] = { + .addr1 = 0x0555, + .addr2 = 0x0aaa + }, + + [MTD_UADDR_0x5555_0x2AAA] = { + .addr1 = 0x5555, + .addr2 = 0x2aaa + }, + + [MTD_UADDR_0x0AAA_0x0555] = { + .addr1 = 0x0AAA, + .addr2 = 0x0555 + }, + + [MTD_UADDR_DONT_CARE] = { + .addr1 = 0x0000, /* Doesn't matter which address */ + .addr2 = 0x0000 /* is used - must be last entry */ + }, + + [MTD_UADDR_UNNECESSARY] = { + .addr1 = 0x0000, + .addr2 = 0x0000 + } +}; + + +struct amd_flash_info { + const __u16 mfr_id; + const __u16 dev_id; + const char *name; + const int DevSize; + const int NumEraseRegions; + const int CmdSet; + const __u8 uaddr[4]; /* unlock addrs for 8, 16, 32, 64 */ + const ulong regions[6]; +}; + +#define ERASEINFO(size,blocks) (size<<8)|(blocks-1) + +#define SIZE_64KiB 16 +#define SIZE_128KiB 17 +#define SIZE_256KiB 18 +#define SIZE_512KiB 19 +#define SIZE_1MiB 20 +#define SIZE_2MiB 21 +#define SIZE_4MiB 22 +#define SIZE_8MiB 23 + +static const struct amd_flash_info jedec_table[] = { +#ifdef CONFIG_SYS_FLASH_LEGACY_256Kx8 + { + .mfr_id = (u16)SST_MANUFACT, + .dev_id = SST39LF020, + .name = "SST 39LF020", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,64), + } + }, +#endif +#ifdef CONFIG_SYS_FLASH_LEGACY_512Kx8 + { + .mfr_id = (u16)AMD_MANUFACT, + .dev_id = AM29LV040B, + .name = "AMD AM29LV040B", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8), + } + }, + { + .mfr_id = (u16)SST_MANUFACT, + .dev_id = SST39LF040, + .name = "SST 39LF040", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,128), + } + }, + { + .mfr_id = (u16)STM_MANUFACT, + .dev_id = STM_ID_M29W040B, + .name = "ST Micro M29W040B", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8), + } + }, + { + .mfr_id = (u16)MX_MANUFACT, + .dev_id = MX29LV040, + .name = "MXIC MX29LV040", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000, 8), + } + }, + { + .mfr_id = (u16)WINB_MANUFACT, + .dev_id = W39L040A, + .name = "WINBOND W39L040A", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000, 8), + } + }, + { + .mfr_id = (u16)AMIC_MANUFACT, + .dev_id = A29L040, + .name = "AMIC A29L040", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000, 8), + } + }, + { + .mfr_id = (u16)EON_MANUFACT, + .dev_id = EN29LV040A, + .name = "EON EN29LV040A", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000, 8), + } + }, +#endif +#ifdef CONFIG_SYS_FLASH_LEGACY_512Kx16 + { + .mfr_id = (u16)AMD_MANUFACT, + .dev_id = AM29F400BB, + .name = "AMD AM29F400BB", + .uaddr = { + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = CFI_CMDSET_AMD_LEGACY, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000, 1), + ERASEINFO(0x02000, 2), + ERASEINFO(0x08000, 1), + ERASEINFO(0x10000, 7), + } + }, + { + .mfr_id = (u16)AMD_MANUFACT, + .dev_id = AM29LV400BB, + .name = "AMD AM29LV400BB", + .uaddr = { + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = CFI_CMDSET_AMD_LEGACY, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,7), + } + }, + { + .mfr_id = (u16)AMD_MANUFACT, + .dev_id = AM29LV800BB, + .name = "AMD AM29LV800BB", + .uaddr = { + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = CFI_CMDSET_AMD_LEGACY, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000, 1), + ERASEINFO(0x02000, 2), + ERASEINFO(0x08000, 1), + ERASEINFO(0x10000, 15), + } + }, + { + .mfr_id = (u16)STM_MANUFACT, + .dev_id = STM29F400BB, + .name = "ST Micro M29F400BB", + .uaddr = { + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = CFI_CMDSET_AMD_LEGACY, + .NumEraseRegions = 4, + .regions = { + ERASEINFO(0x04000, 1), + ERASEINFO(0x02000, 2), + ERASEINFO(0x08000, 1), + ERASEINFO(0x10000, 7), + } + }, +#endif +}; + +static inline void fill_info(flash_info_t *info, const struct amd_flash_info *jedec_entry, ulong base) +{ + int i,j; + int sect_cnt; + int size_ratio; + int total_size; + enum uaddr uaddr_idx; + + size_ratio = info->portwidth / info->chipwidth; + + debug("Found JEDEC Flash: %s\n", jedec_entry->name); + info->vendor = jedec_entry->CmdSet; + /* Todo: do we need device-specific timeouts? */ + info->erase_blk_tout = 30000; + info->buffer_write_tout = 1000; + info->write_tout = 100; + info->name = jedec_entry->name; + + /* copy unlock addresses from device table to CFI info struct. This + is just here because the addresses are in the table anyway - if + the flash is not detected due to wrong unlock addresses, + flash_detect_legacy would have to try all of them before we even + get here. */ + switch(info->chipwidth) { + case FLASH_CFI_8BIT: + uaddr_idx = jedec_entry->uaddr[0]; + break; + case FLASH_CFI_16BIT: + uaddr_idx = jedec_entry->uaddr[1]; + break; + case FLASH_CFI_32BIT: + uaddr_idx = jedec_entry->uaddr[2]; + break; + default: + uaddr_idx = MTD_UADDR_NOT_SUPPORTED; + break; + } + + debug("unlock address index %d\n", uaddr_idx); + info->addr_unlock1 = unlock_addrs[uaddr_idx].addr1; + info->addr_unlock2 = unlock_addrs[uaddr_idx].addr2; + debug("unlock addresses are 0x%lx/0x%lx\n", + info->addr_unlock1, info->addr_unlock2); + + sect_cnt = 0; + total_size = 0; + for (i = 0; i < jedec_entry->NumEraseRegions; i++) { + ulong erase_region_size = jedec_entry->regions[i] >> 8; + ulong erase_region_count = (jedec_entry->regions[i] & 0xff) + 1; + + total_size += erase_region_size * erase_region_count; + debug("erase_region_count = %ld erase_region_size = %ld\n", + erase_region_count, erase_region_size); + for (j = 0; j < erase_region_count; j++) { + if (sect_cnt >= CONFIG_SYS_MAX_FLASH_SECT) { + printf("ERROR: too many flash sectors\n"); + break; + } + info->start[sect_cnt] = base; + base += (erase_region_size * size_ratio); + sect_cnt++; + } + } + info->sector_count = sect_cnt; + info->size = total_size * size_ratio; +} + +/*----------------------------------------------------------------------- + * match jedec ids against table. If a match is found, fill flash_info entry + */ +int jedec_flash_match(flash_info_t *info, ulong base) +{ + int ret = 0; + int i; + ulong mask = 0xFFFF; + if (info->chipwidth == 1) + mask = 0xFF; + + for (i = 0; i < ARRAY_SIZE(jedec_table); i++) { + if ((jedec_table[i].mfr_id & mask) == (info->manufacturer_id & mask) && + (jedec_table[i].dev_id & mask) == (info->device_id & mask)) { + fill_info(info, &jedec_table[i], base); + ret = 1; + break; + } + } + return ret; +} diff --git a/qemu/roms/u-boot/drivers/mtd/mtdconcat.c b/qemu/roms/u-boot/drivers/mtd/mtdconcat.c new file mode 100644 index 000000000..31e4289b1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/mtdconcat.c @@ -0,0 +1,773 @@ +/* + * MTD device concatenation layer + * + * (C) 2002 Robert Kaiser + * + * NAND support by Christian Gan + * + * This code is GPL + */ + +#include +#include +#include +#include + +/* + * Our storage structure: + * Subdev points to an array of pointers to struct mtd_info objects + * which is allocated along with this structure + * + */ +struct mtd_concat { + struct mtd_info mtd; + int num_subdev; + struct mtd_info **subdev; +}; + +/* + * how to calculate the size required for the above structure, + * including the pointer array subdev points to: + */ +#define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ + ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) + +/* + * Given a pointer to the MTD object in the mtd_concat structure, + * we can retrieve the pointer to that structure with this macro. + */ +#define CONCAT(x) ((struct mtd_concat *)(x)) + +/* + * MTD methods which look up the relevant subdevice, translate the + * effective address and pass through to the subdevice. + */ + +static int +concat_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int ret = 0, err; + int i; + + *retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (from >= subdev->size) { + /* Not destined for this subdev */ + size = 0; + from -= subdev->size; + continue; + } + if (from + len > subdev->size) + /* First part goes into this subdev */ + size = subdev->size - from; + else + /* Entire transaction goes into this subdev */ + size = len; + + err = mtd_read(subdev, from, size, &retsize, buf); + + /* Save information about bitflips! */ + if (unlikely(err)) { + if (mtd_is_eccerr(err)) { + mtd->ecc_stats.failed++; + ret = err; + } else if (mtd_is_bitflip(err)) { + mtd->ecc_stats.corrected++; + /* Do not overwrite -EBADMSG !! */ + if (!ret) + ret = err; + } else + return err; + } + + *retlen += retsize; + len -= size; + if (len == 0) + return ret; + + buf += size; + from = 0; + } + return -EINVAL; +} + +static int +concat_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + + *retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (to >= subdev->size) { + size = 0; + to -= subdev->size; + continue; + } + if (to + len > subdev->size) + size = subdev->size - to; + else + size = len; + + err = mtd_write(subdev, to, size, &retsize, buf); + if (err) + break; + + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } + return err; +} + +static int +concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_oob_ops devops = *ops; + int i, err, ret = 0; + + ops->retlen = ops->oobretlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (from >= subdev->size) { + from -= subdev->size; + continue; + } + + /* partial read ? */ + if (from + devops.len > subdev->size) + devops.len = subdev->size - from; + + err = mtd_read_oob(subdev, from, &devops); + ops->retlen += devops.retlen; + ops->oobretlen += devops.oobretlen; + + /* Save information about bitflips! */ + if (unlikely(err)) { + if (mtd_is_eccerr(err)) { + mtd->ecc_stats.failed++; + ret = err; + } else if (mtd_is_bitflip(err)) { + mtd->ecc_stats.corrected++; + /* Do not overwrite -EBADMSG !! */ + if (!ret) + ret = err; + } else + return err; + } + + if (devops.datbuf) { + devops.len = ops->len - ops->retlen; + if (!devops.len) + return ret; + devops.datbuf += devops.retlen; + } + if (devops.oobbuf) { + devops.ooblen = ops->ooblen - ops->oobretlen; + if (!devops.ooblen) + return ret; + devops.oobbuf += ops->oobretlen; + } + + from = 0; + } + return -EINVAL; +} + +static int +concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_oob_ops devops = *ops; + int i, err; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + ops->retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (to >= subdev->size) { + to -= subdev->size; + continue; + } + + /* partial write ? */ + if (to + devops.len > subdev->size) + devops.len = subdev->size - to; + + err = mtd_write_oob(subdev, to, &devops); + ops->retlen += devops.retlen; + if (err) + return err; + + if (devops.datbuf) { + devops.len = ops->len - ops->retlen; + if (!devops.len) + return 0; + devops.datbuf += devops.retlen; + } + if (devops.oobbuf) { + devops.ooblen = ops->ooblen - ops->oobretlen; + if (!devops.ooblen) + return 0; + devops.oobbuf += devops.oobretlen; + } + to = 0; + } + return -EINVAL; +} + +static void concat_erase_callback(struct erase_info *instr) +{ + /* Nothing to do here in U-Boot */ +} + +static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + int err; + wait_queue_head_t waitq; + DECLARE_WAITQUEUE(wait, current); + + /* + * This code was stol^H^H^H^Hinspired by mtdchar.c + */ + init_waitqueue_head(&waitq); + + erase->mtd = mtd; + erase->callback = concat_erase_callback; + erase->priv = (unsigned long) &waitq; + + /* + * FIXME: Allow INTERRUPTIBLE. Which means + * not having the wait_queue head on the stack. + */ + err = mtd_erase(mtd, erase); + if (!err) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&waitq, &wait); + if (erase->state != MTD_ERASE_DONE + && erase->state != MTD_ERASE_FAILED) + schedule(); + remove_wait_queue(&waitq, &wait); + set_current_state(TASK_RUNNING); + + err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0; + } + return err; +} + +static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_info *subdev; + int i, err; + uint64_t length, offset = 0; + struct erase_info *erase; + + /* + * Check for proper erase block alignment of the to-be-erased area. + * It is easier to do this based on the super device's erase + * region info rather than looking at each particular sub-device + * in turn. + */ + if (!concat->mtd.numeraseregions) { + /* the easy case: device has uniform erase block size */ + if (instr->addr & (concat->mtd.erasesize - 1)) + return -EINVAL; + if (instr->len & (concat->mtd.erasesize - 1)) + return -EINVAL; + } else { + /* device has variable erase size */ + struct mtd_erase_region_info *erase_regions = + concat->mtd.eraseregions; + + /* + * Find the erase region where the to-be-erased area begins: + */ + for (i = 0; i < concat->mtd.numeraseregions && + instr->addr >= erase_regions[i].offset; i++) ; + --i; + + /* + * Now erase_regions[i] is the region in which the + * to-be-erased area begins. Verify that the starting + * offset is aligned to this region's erase size: + */ + if (instr->addr & (erase_regions[i].erasesize - 1)) + return -EINVAL; + + /* + * now find the erase region where the to-be-erased area ends: + */ + for (; i < concat->mtd.numeraseregions && + (instr->addr + instr->len) >= erase_regions[i].offset; + ++i) ; + --i; + /* + * check if the ending offset is aligned to this region's erase size + */ + if ((instr->addr + instr->len) & (erase_regions[i].erasesize - + 1)) + return -EINVAL; + } + + /* make a local copy of instr to avoid modifying the caller's struct */ + erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); + + if (!erase) + return -ENOMEM; + + *erase = *instr; + length = instr->len; + + /* + * find the subdevice where the to-be-erased area begins, adjust + * starting offset to be relative to the subdevice start + */ + for (i = 0; i < concat->num_subdev; i++) { + subdev = concat->subdev[i]; + if (subdev->size <= erase->addr) { + erase->addr -= subdev->size; + offset += subdev->size; + } else { + break; + } + } + + /* must never happen since size limit has been verified above */ + BUG_ON(i >= concat->num_subdev); + + /* now do the erase: */ + err = 0; + for (; length > 0; i++) { + /* loop for all subdevices affected by this request */ + subdev = concat->subdev[i]; /* get current subdevice */ + + /* limit length to subdevice's size: */ + if (erase->addr + length > subdev->size) + erase->len = subdev->size - erase->addr; + else + erase->len = length; + + length -= erase->len; + if ((err = concat_dev_erase(subdev, erase))) { + /* sanity check: should never happen since + * block alignment has been checked above */ + BUG_ON(err == -EINVAL); + if (erase->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr = erase->fail_addr + offset; + break; + } + /* + * erase->addr specifies the offset of the area to be + * erased *within the current subdevice*. It can be + * non-zero only the first time through this loop, i.e. + * for the first subdevice where blocks need to be erased. + * All the following erases must begin at the start of the + * current subdevice, i.e. at offset zero. + */ + erase->addr = 0; + offset += subdev->size; + } + instr->state = erase->state; + kfree(erase); + if (err) + return err; + + if (instr->callback) + instr->callback(instr); + return 0; +} + +static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + uint64_t size; + + if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; + continue; + } + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = mtd_lock(subdev, ofs, size); + + if (err) + break; + + len -= size; + if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + + return err; +} + +static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + uint64_t size; + + if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; + continue; + } + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = mtd_unlock(subdev, ofs, size); + + if (err) + break; + + len -= size; + if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + + return err; +} + +static void concat_sync(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + mtd_sync(subdev); + } +} + +static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, res = 0; + + if (!mtd_can_have_bb(concat->subdev[0])) + return res; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (ofs >= subdev->size) { + ofs -= subdev->size; + continue; + } + + res = mtd_block_isbad(subdev, ofs); + break; + } + + return res; +} + +static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; + + if (!mtd_can_have_bb(concat->subdev[0])) + return 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (ofs >= subdev->size) { + ofs -= subdev->size; + continue; + } + + err = mtd_block_markbad(subdev, ofs); + if (!err) + mtd->ecc_stats.badblocks++; + break; + } + + return err; +} + +/* + * This function constructs a virtual MTD device by concatenating + * num_devs MTD devices. A pointer to the new device object is + * stored to *new_dev upon success. This function does _not_ + * register any devices: this is the caller's responsibility. + */ +struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ + const char *name) +{ /* name for the new device */ + int i; + size_t size; + struct mtd_concat *concat; + uint32_t max_erasesize, curr_erasesize; + int num_erase_region; + + debug("Concatenating MTD devices:\n"); + for (i = 0; i < num_devs; i++) + debug("(%d): \"%s\"\n", i, subdev[i]->name); + debug("into device \"%s\"\n", name); + + /* allocate the device structure */ + size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); + concat = kzalloc(size, GFP_KERNEL); + if (!concat) { + printk + ("memory allocation error while creating concatenated device \"%s\"\n", + name); + return NULL; + } + concat->subdev = (struct mtd_info **) (concat + 1); + + /* + * Set up the new "super" device's MTD object structure, check for + * incompatibilites between the subdevices. + */ + concat->mtd.type = subdev[0]->type; + concat->mtd.flags = subdev[0]->flags; + concat->mtd.size = subdev[0]->size; + concat->mtd.erasesize = subdev[0]->erasesize; + concat->mtd.writesize = subdev[0]->writesize; + concat->mtd.subpage_sft = subdev[0]->subpage_sft; + concat->mtd.oobsize = subdev[0]->oobsize; + concat->mtd.oobavail = subdev[0]->oobavail; + if (subdev[0]->_read_oob) + concat->mtd._read_oob = concat_read_oob; + if (subdev[0]->_write_oob) + concat->mtd._write_oob = concat_write_oob; + if (subdev[0]->_block_isbad) + concat->mtd._block_isbad = concat_block_isbad; + if (subdev[0]->_block_markbad) + concat->mtd._block_markbad = concat_block_markbad; + + concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; + + concat->subdev[0] = subdev[0]; + + for (i = 1; i < num_devs; i++) { + if (concat->mtd.type != subdev[i]->type) { + kfree(concat); + printk("Incompatible device type on \"%s\"\n", + subdev[i]->name); + return NULL; + } + if (concat->mtd.flags != subdev[i]->flags) { + /* + * Expect all flags except MTD_WRITEABLE to be + * equal on all subdevices. + */ + if ((concat->mtd.flags ^ subdev[i]-> + flags) & ~MTD_WRITEABLE) { + kfree(concat); + printk("Incompatible device flags on \"%s\"\n", + subdev[i]->name); + return NULL; + } else + /* if writeable attribute differs, + make super device writeable */ + concat->mtd.flags |= + subdev[i]->flags & MTD_WRITEABLE; + } + + concat->mtd.size += subdev[i]->size; + concat->mtd.ecc_stats.badblocks += + subdev[i]->ecc_stats.badblocks; + if (concat->mtd.writesize != subdev[i]->writesize || + concat->mtd.subpage_sft != subdev[i]->subpage_sft || + concat->mtd.oobsize != subdev[i]->oobsize || + !concat->mtd._read_oob != !subdev[i]->_read_oob || + !concat->mtd._write_oob != !subdev[i]->_write_oob) { + kfree(concat); + printk("Incompatible OOB or ECC data on \"%s\"\n", + subdev[i]->name); + return NULL; + } + concat->subdev[i] = subdev[i]; + + } + + concat->mtd.ecclayout = subdev[0]->ecclayout; + + concat->num_subdev = num_devs; + concat->mtd.name = name; + + concat->mtd._erase = concat_erase; + concat->mtd._read = concat_read; + concat->mtd._write = concat_write; + concat->mtd._sync = concat_sync; + concat->mtd._lock = concat_lock; + concat->mtd._unlock = concat_unlock; + + /* + * Combine the erase block size info of the subdevices: + * + * first, walk the map of the new device and see how + * many changes in erase size we have + */ + max_erasesize = curr_erasesize = subdev[0]->erasesize; + num_erase_region = 1; + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* if it differs from the last subdevice's erase size, count it */ + ++num_erase_region; + curr_erasesize = subdev[i]->erasesize; + if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } else { + /* current subdevice has variable erase size */ + int j; + for (j = 0; j < subdev[i]->numeraseregions; j++) { + + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j].erasesize != + curr_erasesize) { + ++num_erase_region; + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; + if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + } + } + + if (num_erase_region == 1) { + /* + * All subdevices have the same uniform erase size. + * This is easy: + */ + concat->mtd.erasesize = curr_erasesize; + concat->mtd.numeraseregions = 0; + } else { + uint64_t tmp64; + + /* + * erase block size varies across the subdevices: allocate + * space to store the data describing the variable erase regions + */ + struct mtd_erase_region_info *erase_region_p; + uint64_t begin, position; + + concat->mtd.erasesize = max_erasesize; + concat->mtd.numeraseregions = num_erase_region; + concat->mtd.eraseregions = erase_region_p = + kmalloc(num_erase_region * + sizeof (struct mtd_erase_region_info), GFP_KERNEL); + if (!erase_region_p) { + kfree(concat); + printk + ("memory allocation error while creating erase region list" + " for device \"%s\"\n", name); + return NULL; + } + + /* + * walk the map of the new device once more and fill in + * in erase region info: + */ + curr_erasesize = subdev[0]->erasesize; + begin = position = 0; + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* + * fill in an mtd_erase_region_info structure for the area + * we have walked so far: + */ + erase_region_p->offset = begin; + erase_region_p->erasesize = + curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; + begin = position; + + curr_erasesize = subdev[i]->erasesize; + ++erase_region_p; + } + position += subdev[i]->size; + } else { + /* current subdevice has variable erase size */ + int j; + for (j = 0; j < subdev[i]->numeraseregions; j++) { + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j]. + erasesize != curr_erasesize) { + erase_region_p->offset = begin; + erase_region_p->erasesize = + curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; + begin = position; + + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; + ++erase_region_p; + } + position += + subdev[i]->eraseregions[j]. + numblocks * (uint64_t)curr_erasesize; + } + } + } + /* Now write the final entry */ + erase_region_p->offset = begin; + erase_region_p->erasesize = curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; + } + + return &concat->mtd; +} diff --git a/qemu/roms/u-boot/drivers/mtd/mtdcore.c b/qemu/roms/u-boot/drivers/mtd/mtdcore.c new file mode 100644 index 000000000..0a38fbef1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/mtdcore.c @@ -0,0 +1,390 @@ +/* + * Core registration and callback routines for MTD + * drivers and users. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +struct mtd_info *mtd_table[MAX_MTD_DEVICES]; + +int add_mtd_device(struct mtd_info *mtd) +{ + int i; + + BUG_ON(mtd->writesize == 0); + + for (i = 0; i < MAX_MTD_DEVICES; i++) + if (!mtd_table[i]) { + mtd_table[i] = mtd; + mtd->index = i; + mtd->usecount = 0; + + /* default value if not set by driver */ + if (mtd->bitflip_threshold == 0) + mtd->bitflip_threshold = mtd->ecc_strength; + + + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + + /* We _know_ we aren't being removed, because + our caller is still holding us here. So none + of this try_ nonsense, and no bitching about it + either. :) */ + return 0; + } + + return 1; +} + +/** + * del_mtd_device - unregister an MTD device + * @mtd: pointer to MTD device info structure + * + * Remove a device from the list of MTD devices present in the system, + * and notify each currently active MTD 'user' of its departure. + * Returns zero on success or 1 on failure, which currently will happen + * if the requested device does not appear to be present in the list. + */ +int del_mtd_device(struct mtd_info *mtd) +{ + int ret; + + if (mtd_table[mtd->index] != mtd) { + ret = -ENODEV; + } else if (mtd->usecount) { + printk(KERN_NOTICE "Removing MTD device #%d (%s)" + " with use count %d\n", + mtd->index, mtd->name, mtd->usecount); + ret = -EBUSY; + } else { + /* No need to get a refcount on the module containing + * the notifier, since we hold the mtd_table_mutex */ + mtd_table[mtd->index] = NULL; + + ret = 0; + } + + return ret; +} + +/** + * get_mtd_device - obtain a validated handle for an MTD device + * @mtd: last known address of the required MTD device + * @num: internal device number of the required MTD device + * + * Given a number and NULL address, return the num'th entry in the device + * table, if any. Given an address and num == -1, search the device table + * for a device with that address and return if it's still present. Given + * both, return the num'th driver only if its address matches. Return + * error code if not. + */ +struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) +{ + struct mtd_info *ret = NULL; + int i, err = -ENODEV; + + if (num == -1) { + for (i = 0; i < MAX_MTD_DEVICES; i++) + if (mtd_table[i] == mtd) + ret = mtd_table[i]; + } else if (num < MAX_MTD_DEVICES) { + ret = mtd_table[num]; + if (mtd && mtd != ret) + ret = NULL; + } + + if (!ret) + goto out_unlock; + + ret->usecount++; + return ret; + +out_unlock: + return ERR_PTR(err); +} + +/** + * get_mtd_device_nm - obtain a validated handle for an MTD device by + * device name + * @name: MTD device name to open + * + * This function returns MTD device description structure in case of + * success and an error code in case of failure. + */ +struct mtd_info *get_mtd_device_nm(const char *name) +{ + int i, err = -ENODEV; + struct mtd_info *mtd = NULL; + + for (i = 0; i < MAX_MTD_DEVICES; i++) { + if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { + mtd = mtd_table[i]; + break; + } + } + + if (!mtd) + goto out_unlock; + + mtd->usecount++; + return mtd; + +out_unlock: + return ERR_PTR(err); +} + +void put_mtd_device(struct mtd_info *mtd) +{ + int c; + + c = --mtd->usecount; + BUG_ON(c < 0); +} + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** + * mtd_get_len_incl_bad + * + * Check if length including bad blocks fits into device. + * + * @param mtd an MTD device + * @param offset offset in flash + * @param length image length + * @return image length including bad blocks in *len_incl_bad and whether or not + * the length returned was truncated in *truncated + */ +void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, + const uint64_t length, uint64_t *len_incl_bad, + int *truncated) +{ + *truncated = 0; + *len_incl_bad = 0; + + if (!mtd->block_isbad) { + *len_incl_bad = length; + return; + } + + uint64_t len_excl_bad = 0; + uint64_t block_len; + + while (len_excl_bad < length) { + if (offset >= mtd->size) { + *truncated = 1; + return; + } + + block_len = mtd->erasesize - (offset & (mtd->erasesize - 1)); + + if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1))) + len_excl_bad += block_len; + + *len_incl_bad += block_len; + offset += block_len; + } +} +#endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */ + + /* + * Erase is an asynchronous operation. Device drivers are supposed + * to call instr->callback() whenever the operation completes, even + * if it completes with a failure. + * Callers are supposed to pass a callback function and wait for it + * to be called before writing to the block. + */ +int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr) + return -EINVAL; + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + if (!instr->len) { + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + return 0; + } + return mtd->_erase(mtd, instr); +} + +int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, + u_char *buf) +{ + int ret_code; + if (from < 0 || from > mtd->size || len > mtd->size - from) + return -EINVAL; + if (!len) + return 0; + + /* + * In the absence of an error, drivers return a non-negative integer + * representing the maximum number of bitflips that were corrected on + * any one ecc region (if applicable; zero otherwise). + */ + ret_code = mtd->_read(mtd, from, len, retlen, buf); + if (unlikely(ret_code < 0)) + return ret_code; + if (mtd->ecc_strength == 0) + return 0; /* device lacks ecc */ + return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; +} + +int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, + const u_char *buf) +{ + *retlen = 0; + if (to < 0 || to > mtd->size || len > mtd->size - to) + return -EINVAL; + if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (!len) + return 0; + return mtd->_write(mtd, to, len, retlen, buf); +} + +/* + * In blackbox flight recorder like scenarios we want to make successful writes + * in interrupt context. panic_write() is only intended to be called when its + * known the kernel is about to panic and we need the write to succeed. Since + * the kernel is not going to be running for much longer, this function can + * break locks and delay to ensure the write succeeds (but not sleep). + */ +int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, + const u_char *buf) +{ + *retlen = 0; + if (!mtd->_panic_write) + return -EOPNOTSUPP; + if (to < 0 || to > mtd->size || len > mtd->size - to) + return -EINVAL; + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (!len) + return 0; + return mtd->_panic_write(mtd, to, len, retlen, buf); +} + +int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) +{ + ops->retlen = ops->oobretlen = 0; + if (!mtd->_read_oob) + return -EOPNOTSUPP; + return mtd->_read_oob(mtd, from, ops); +} + +/* + * Method to access the protection register area, present in some flash + * devices. The user data is one time programmable but the factory data is read + * only. + */ +int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, + size_t len) +{ + if (!mtd->_get_fact_prot_info) + return -EOPNOTSUPP; + if (!len) + return 0; + return mtd->_get_fact_prot_info(mtd, buf, len); +} + +int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + *retlen = 0; + if (!mtd->_read_fact_prot_reg) + return -EOPNOTSUPP; + if (!len) + return 0; + return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf); +} + +int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf, + size_t len) +{ + if (!mtd->_get_user_prot_info) + return -EOPNOTSUPP; + if (!len) + return 0; + return mtd->_get_user_prot_info(mtd, buf, len); +} + +int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + *retlen = 0; + if (!mtd->_read_user_prot_reg) + return -EOPNOTSUPP; + if (!len) + return 0; + return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf); +} + +int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, u_char *buf) +{ + *retlen = 0; + if (!mtd->_write_user_prot_reg) + return -EOPNOTSUPP; + if (!len) + return 0; + return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); +} + +int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) +{ + if (!mtd->_lock_user_prot_reg) + return -EOPNOTSUPP; + if (!len) + return 0; + return mtd->_lock_user_prot_reg(mtd, from, len); +} + +/* Chip-supported device locking */ +int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + if (!mtd->_lock) + return -EOPNOTSUPP; + if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) + return -EINVAL; + if (!len) + return 0; + return mtd->_lock(mtd, ofs, len); +} + +int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + if (!mtd->_unlock) + return -EOPNOTSUPP; + if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) + return -EINVAL; + if (!len) + return 0; + return mtd->_unlock(mtd, ofs, len); +} + +int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + if (!mtd->_block_isbad) + return 0; + if (ofs < 0 || ofs > mtd->size) + return -EINVAL; + return mtd->_block_isbad(mtd, ofs); +} + +int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + if (!mtd->_block_markbad) + return -EOPNOTSUPP; + if (ofs < 0 || ofs > mtd->size) + return -EINVAL; + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + return mtd->_block_markbad(mtd, ofs); +} diff --git a/qemu/roms/u-boot/drivers/mtd/mtdpart.c b/qemu/roms/u-boot/drivers/mtd/mtdpart.c new file mode 100644 index 000000000..146ce11eb --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/mtdpart.c @@ -0,0 +1,428 @@ +/* + * Simple MTD partitioning layer + * + * (C) 2000 Nicolas Pitre + * + * This code is GPL + * + * 02-21-2002 Thomas Gleixner + * added support for read_oob, write_oob + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Our partition linked list */ +struct list_head mtd_partitions; + +/* Our partition node structure */ +struct mtd_part { + struct mtd_info mtd; + struct mtd_info *master; + uint64_t offset; + int index; + struct list_head list; + int registered; +}; + +/* + * Given a pointer to the MTD object in the mtd_part structure, we can retrieve + * the pointer to that structure with this macro. + */ +#define PART(x) ((struct mtd_part *)(x)) + + +/* + * MTD methods which simply translate the effective address and pass through + * to the _real_ device. + */ + +static int part_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + struct mtd_ecc_stats stats; + int res; + + stats = part->master->ecc_stats; + res = mtd_read(part->master, from + part->offset, len, retlen, buf); + if (unlikely(mtd_is_eccerr(res))) + mtd->ecc_stats.failed += + part->master->ecc_stats.failed - stats.failed; + else + mtd->ecc_stats.corrected += + part->master->ecc_stats.corrected - stats.corrected; + return res; +} + +static int part_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct mtd_part *part = PART(mtd); + int res; + + if (from >= mtd->size) + return -EINVAL; + if (ops->datbuf && from + ops->len > mtd->size) + return -EINVAL; + res = mtd_read_oob(part->master, from + part->offset, ops); + + if (unlikely(res)) { + if (mtd_is_bitflip(res)) + mtd->ecc_stats.corrected++; + if (mtd_is_eccerr(res)) + mtd->ecc_stats.failed++; + } + return res; +} + +static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + return mtd_read_user_prot_reg(part->master, from, len, retlen, buf); +} + +static int part_get_user_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + struct mtd_part *part = PART(mtd); + return mtd_get_user_prot_info(part->master, buf, len); +} + +static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + return mtd_read_fact_prot_reg(part->master, from, len, retlen, buf); +} + +static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, + size_t len) +{ + struct mtd_part *part = PART(mtd); + return mtd_get_fact_prot_info(part->master, buf, len); +} + +static int part_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_part *part = PART(mtd); + return mtd_write(part->master, to + part->offset, len, retlen, buf); +} + +static int part_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct mtd_part *part = PART(mtd); + + if (to >= mtd->size) + return -EINVAL; + if (ops->datbuf && to + ops->len > mtd->size) + return -EINVAL; + return mtd_write_oob(part->master, to + part->offset, ops); +} + +static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + return mtd_write_user_prot_reg(part->master, from, len, retlen, buf); +} + +static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len) +{ + struct mtd_part *part = PART(mtd); + return mtd_lock_user_prot_reg(part->master, from, len); +} + +static int part_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct mtd_part *part = PART(mtd); + int ret; + + instr->addr += part->offset; + ret = mtd_erase(part->master, instr); + if (ret) { + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr -= part->offset; + instr->addr -= part->offset; + } + return ret; +} + +void mtd_erase_callback(struct erase_info *instr) +{ + if (instr->mtd->_erase == part_erase) { + struct mtd_part *part = PART(instr->mtd); + + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr -= part->offset; + instr->addr -= part->offset; + } + if (instr->callback) + instr->callback(instr); +} + +static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct mtd_part *part = PART(mtd); + return mtd_lock(part->master, ofs + part->offset, len); +} + +static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct mtd_part *part = PART(mtd); + return mtd_unlock(part->master, ofs + part->offset, len); +} + +static void part_sync(struct mtd_info *mtd) +{ + struct mtd_part *part = PART(mtd); + mtd_sync(part->master); +} + +static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + ofs += part->offset; + return mtd_block_isbad(part->master, ofs); +} + +static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + int res; + + ofs += part->offset; + res = mtd_block_markbad(part->master, ofs); + if (!res) + mtd->ecc_stats.badblocks++; + return res; +} + +/* + * This function unregisters and destroy all slave MTD objects which are + * attached to the given master MTD object. + */ + +int del_mtd_partitions(struct mtd_info *master) +{ + struct mtd_part *slave, *next; + + list_for_each_entry_safe(slave, next, &mtd_partitions, list) + if (slave->master == master) { + list_del(&slave->list); + if (slave->registered) + del_mtd_device(&slave->mtd); + kfree(slave); + } + + return 0; +} + +static struct mtd_part *add_one_partition(struct mtd_info *master, + const struct mtd_partition *part, int partno, + uint64_t cur_offset) +{ + struct mtd_part *slave; + + /* allocate the partition structure */ + slave = kzalloc(sizeof(*slave), GFP_KERNEL); + if (!slave) { + printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", + master->name); + del_mtd_partitions(master); + return NULL; + } + list_add(&slave->list, &mtd_partitions); + + /* set up the MTD object for this partition */ + slave->mtd.type = master->type; + slave->mtd.flags = master->flags & ~part->mask_flags; + slave->mtd.size = part->size; + slave->mtd.writesize = master->writesize; + slave->mtd.oobsize = master->oobsize; + slave->mtd.oobavail = master->oobavail; + slave->mtd.subpage_sft = master->subpage_sft; + + slave->mtd.name = part->name; + slave->mtd.owner = master->owner; + + slave->mtd._read = part_read; + slave->mtd._write = part_write; + + if (master->_read_oob) + slave->mtd._read_oob = part_read_oob; + if (master->_write_oob) + slave->mtd._write_oob = part_write_oob; + if (master->_read_user_prot_reg) + slave->mtd._read_user_prot_reg = part_read_user_prot_reg; + if (master->_read_fact_prot_reg) + slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg; + if (master->_write_user_prot_reg) + slave->mtd._write_user_prot_reg = part_write_user_prot_reg; + if (master->_lock_user_prot_reg) + slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg; + if (master->_get_user_prot_info) + slave->mtd._get_user_prot_info = part_get_user_prot_info; + if (master->_get_fact_prot_info) + slave->mtd._get_fact_prot_info = part_get_fact_prot_info; + if (master->_sync) + slave->mtd._sync = part_sync; + if (master->_lock) + slave->mtd._lock = part_lock; + if (master->_unlock) + slave->mtd._unlock = part_unlock; + if (master->_block_isbad) + slave->mtd._block_isbad = part_block_isbad; + if (master->_block_markbad) + slave->mtd._block_markbad = part_block_markbad; + slave->mtd._erase = part_erase; + slave->master = master; + slave->offset = part->offset; + slave->index = partno; + + if (slave->offset == MTDPART_OFS_APPEND) + slave->offset = cur_offset; + if (slave->offset == MTDPART_OFS_NXTBLK) { + slave->offset = cur_offset; + if (mtd_mod_by_eb(cur_offset, master) != 0) { + /* Round up to next erasesize */ + slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize; + debug("Moving partition %d: 0x%012llx -> 0x%012llx\n", + partno, (unsigned long long)cur_offset, + (unsigned long long)slave->offset); + } + } + if (slave->mtd.size == MTDPART_SIZ_FULL) + slave->mtd.size = master->size - slave->offset; + + debug("0x%012llx-0x%012llx : \"%s\"\n", + (unsigned long long)slave->offset, + (unsigned long long)(slave->offset + slave->mtd.size), + slave->mtd.name); + + /* let's do some sanity checks */ + if (slave->offset >= master->size) { + /* let's register it anyway to preserve ordering */ + slave->offset = 0; + slave->mtd.size = 0; + printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", + part->name); + goto out_register; + } + if (slave->offset + slave->mtd.size > master->size) { + slave->mtd.size = master->size - slave->offset; + printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", + part->name, master->name, (unsigned long long)slave->mtd.size); + } + if (master->numeraseregions > 1) { + /* Deal with variable erase size stuff */ + int i, max = master->numeraseregions; + u64 end = slave->offset + slave->mtd.size; + struct mtd_erase_region_info *regions = master->eraseregions; + + /* Find the first erase regions which is part of this + * partition. */ + for (i = 0; i < max && regions[i].offset <= slave->offset; i++) + ; + /* The loop searched for the region _behind_ the first one */ + i--; + + /* Pick biggest erasesize */ + for (; i < max && regions[i].offset < end; i++) { + if (slave->mtd.erasesize < regions[i].erasesize) { + slave->mtd.erasesize = regions[i].erasesize; + } + } + BUG_ON(slave->mtd.erasesize == 0); + } else { + /* Single erase size */ + slave->mtd.erasesize = master->erasesize; + } + + if ((slave->mtd.flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->offset, &slave->mtd)) { + /* Doesn't start on a boundary of major erase size */ + /* FIXME: Let it be writable if it is on a boundary of + * _minor_ erase size though */ + slave->mtd.flags &= ~MTD_WRITEABLE; + printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", + part->name); + } + if ((slave->mtd.flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { + slave->mtd.flags &= ~MTD_WRITEABLE; + printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", + part->name); + } + + slave->mtd.ecclayout = master->ecclayout; + if (master->_block_isbad) { + uint64_t offs = 0; + + while (offs < slave->mtd.size) { + if (mtd_block_isbad(master, offs + slave->offset)) + slave->mtd.ecc_stats.badblocks++; + offs += slave->mtd.erasesize; + } + } + +out_register: + if (part->mtdp) { + /* store the object pointer (caller may or may not register it*/ + *part->mtdp = &slave->mtd; + slave->registered = 0; + } else { + /* register our partition */ + add_mtd_device(&slave->mtd); + slave->registered = 1; + } + return slave; +} + +/* + * This function, given a master MTD object and a partition table, creates + * and registers slave MTD objects which are bound to the master according to + * the partition definitions. + * + * We don't register the master, or expect the caller to have done so, + * for reasons of data integrity. + */ + +int add_mtd_partitions(struct mtd_info *master, + const struct mtd_partition *parts, + int nbparts) +{ + struct mtd_part *slave; + uint64_t cur_offset = 0; + int i; + + /* + * Need to init the list here, since LIST_INIT() does not + * work on platforms where relocation has problems (like MIPS + * & PPC). + */ + if (mtd_partitions.next == NULL) + INIT_LIST_HEAD(&mtd_partitions); + + debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + + for (i = 0; i < nbparts; i++) { + slave = add_one_partition(master, parts + i, i, cur_offset); + if (!slave) + return -ENOMEM; + cur_offset = slave->offset + slave->mtd.size; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/mw_eeprom.c b/qemu/roms/u-boot/drivers/mtd/mw_eeprom.c new file mode 100644 index 000000000..f7791b51a --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/mw_eeprom.c @@ -0,0 +1,236 @@ +/* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */ + +#include +#include + +/* + * Serial EEPROM opcodes, including start bit + */ +#define EEP_OPC_ERASE 0x7 /* 3-bit opcode */ +#define EEP_OPC_WRITE 0x5 /* 3-bit opcode */ +#define EEP_OPC_READ 0x6 /* 3-bit opcode */ + +#define EEP_OPC_ERASE_ALL 0x12 /* 5-bit opcode */ +#define EEP_OPC_ERASE_EN 0x13 /* 5-bit opcode */ +#define EEP_OPC_WRITE_ALL 0x11 /* 5-bit opcode */ +#define EEP_OPC_ERASE_DIS 0x10 /* 5-bit opcode */ + +static int addrlen; + +static void mw_eeprom_select(int dev) +{ + ssi_set_interface(2048, 0, 0, 0); + ssi_chip_select(0); + udelay(1); + ssi_chip_select(dev); + udelay(1); +} + +static int mw_eeprom_size(int dev) +{ + int x; + u16 res; + + mw_eeprom_select(dev); + ssi_tx_byte(EEP_OPC_READ); + + res = ssi_txrx_byte(0) << 8; + res |= ssi_rx_byte(); + for (x = 0; x < 16; x++) { + if (! (res & 0x8000)) { + break; + } + res <<= 1; + } + ssi_chip_select(0); + + return x; +} + +int mw_eeprom_erase_enable(int dev) +{ + mw_eeprom_select(dev); + ssi_tx_byte(EEP_OPC_ERASE_EN); + ssi_tx_byte(0); + udelay(1); + ssi_chip_select(0); + + return 0; +} + +int mw_eeprom_erase_disable(int dev) +{ + mw_eeprom_select(dev); + ssi_tx_byte(EEP_OPC_ERASE_DIS); + ssi_tx_byte(0); + udelay(1); + ssi_chip_select(0); + + return 0; +} + + +u32 mw_eeprom_read_word(int dev, int addr) +{ + u16 rcv; + u16 res; + int bits; + + mw_eeprom_select(dev); + ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f)); + rcv = ssi_txrx_byte(addr << (13 - addrlen)); + res = rcv << (16 - addrlen); + bits = 4 + addrlen; + + while (bits>0) { + rcv = ssi_rx_byte(); + if (bits > 7) { + res |= rcv << (bits - 8); + } else { + res |= rcv >> (8 - bits); + } + bits -= 8; + } + + ssi_chip_select(0); + + return res; +} + +int mw_eeprom_write_word(int dev, int addr, u16 data) +{ + u8 byte1=0; + u8 byte2=0; + + mw_eeprom_erase_enable(dev); + mw_eeprom_select(dev); + + switch (addrlen) { + case 6: + byte1 = EEP_OPC_WRITE >> 2; + byte2 = (EEP_OPC_WRITE << 6)&0xc0; + byte2 |= addr; + break; + case 7: + byte1 = EEP_OPC_WRITE >> 1; + byte2 = (EEP_OPC_WRITE << 7)&0x80; + byte2 |= addr; + break; + case 8: + byte1 = EEP_OPC_WRITE; + byte2 = addr; + break; + case 9: + byte1 = EEP_OPC_WRITE << 1; + byte1 |= addr >> 8; + byte2 = addr & 0xff; + break; + case 10: + byte1 = EEP_OPC_WRITE << 2; + byte1 |= addr >> 8; + byte2 = addr & 0xff; + break; + default: + printf("Unsupported number of address bits: %d\n", addrlen); + return -1; + + } + + ssi_tx_byte(byte1); + ssi_tx_byte(byte2); + ssi_tx_byte(data >> 8); + ssi_tx_byte(data & 0xff); + ssi_chip_select(0); + udelay(10000); /* Worst case */ + mw_eeprom_erase_disable(dev); + + return 0; +} + + +int mw_eeprom_write(int dev, int addr, u8 *buffer, int len) +{ + int done; + + done = 0; + if (addr & 1) { + u16 temp = mw_eeprom_read_word(dev, addr >> 1); + temp &= 0xff00; + temp |= buffer[0]; + + mw_eeprom_write_word(dev, addr >> 1, temp); + len--; + addr++; + buffer++; + done++; + } + + while (len <= 2) { + mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer); + len-=2; + addr+=2; + buffer+=2; + done+=2; + } + + if (len) { + u16 temp = mw_eeprom_read_word(dev, addr >> 1); + temp &= 0x00ff; + temp |= buffer[0] << 8; + + mw_eeprom_write_word(dev, addr >> 1, temp); + len--; + addr++; + buffer++; + done++; + } + + return done; +} + + +int mw_eeprom_read(int dev, int addr, u8 *buffer, int len) +{ + int done; + + done = 0; + if (addr & 1) { + u16 temp = mw_eeprom_read_word(dev, addr >> 1); + buffer[0]= temp & 0xff; + + len--; + addr++; + buffer++; + done++; + } + + while (len <= 2) { + *(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1); + len-=2; + addr+=2; + buffer+=2; + done+=2; + } + + if (len) { + u16 temp = mw_eeprom_read_word(dev, addr >> 1); + buffer[0] = temp >> 8; + + len--; + addr++; + buffer++; + done++; + } + + return done; +} + +int mw_eeprom_probe(int dev) +{ + addrlen = mw_eeprom_size(dev); + + if (addrlen < 6 || addrlen > 10) { + return -1; + } + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/Makefile b/qemu/roms/u-boot/drivers/mtd/nand/Makefile new file mode 100644 index 000000000..4eb354da9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/Makefile @@ -0,0 +1,72 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +ifdef CONFIG_SPL_BUILD + +ifdef CONFIG_SPL_NAND_DRIVERS +NORMAL_DRIVERS=y +endif + +obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o +obj-$(CONFIG_SPL_NAND_DOCG4) += docg4_spl.o +obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o +obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o +obj-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o +obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o +obj-$(CONFIG_SPL_NAND_INIT) += nand.o +ifeq ($(CONFIG_SPL_ENV_SUPPORT),y) +obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o +endif + +else # not spl + +NORMAL_DRIVERS=y + +obj-y += nand.o +obj-y += nand_bbt.o +obj-y += nand_ids.o +obj-y += nand_util.o +obj-y += nand_ecc.o +obj-y += nand_base.o + +endif # not spl + +ifdef NORMAL_DRIVERS + +obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o + +obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o +obj-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o +obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o +obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o +obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o +obj-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +obj-$(CONFIG_NAND_FSMC) += fsmc_nand.o +obj-$(CONFIG_NAND_JZ4740) += jz4740_nand.o +obj-$(CONFIG_NAND_KB9202) += kb9202_nand.o +obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o +obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o +obj-$(CONFIG_NAND_MPC5121_NFC) += mpc5121_nfc.o +obj-$(CONFIG_NAND_MXC) += mxc_nand.o +obj-$(CONFIG_NAND_MXS) += mxs_nand.o +obj-$(CONFIG_NAND_NDFC) += ndfc.o +obj-$(CONFIG_NAND_NOMADIK) += nomadik.o +obj-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o +obj-$(CONFIG_NAND_SPEAR) += spr_nand.o +obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o +obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o +obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o +obj-$(CONFIG_NAND_PLAT) += nand_plat.o +obj-$(CONFIG_NAND_DOCG4) += docg4.o + +else # minimal SPL drivers + +obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o +obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o +obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o + +endif # drivers diff --git a/qemu/roms/u-boot/drivers/mtd/nand/am335x_spl_bch.c b/qemu/roms/u-boot/drivers/mtd/nand/am335x_spl_bch.c new file mode 100644 index 000000000..bd89b067d --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/am335x_spl_bch.c @@ -0,0 +1,226 @@ +/* + * (C) Copyright 2012 + * Konstantin Kozhevnikov, Cogent Embedded + * + * based on nand_spl_simple code + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +nand_info_t nand_info[1]; +static struct nand_chip nand_chip; + +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + + +/* + * NAND command for large page NAND devices (2k) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = nand_info[0].priv; + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + + while (!this->dev_ready(&nand_info[0])) + ; + + /* Emulate NAND_CMD_READOOB */ + if (cmd == NAND_CMD_READOOB) { + offs += CONFIG_SYS_NAND_PAGE_SIZE; + cmd = NAND_CMD_READ0; + } + + /* Begin command latch cycle */ + hwctrl(&nand_info[0], cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + + if (cmd == NAND_CMD_RESET) { + hwctrl(&nand_info[0], NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + while (!this->dev_ready(&nand_info[0])) + ; + return 0; + } + + /* Shift the offset from byte addressing to word addressing. */ + if (this->options & NAND_BUSWIDTH_16) + offs >>= 1; + + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + hwctrl(&nand_info[0], offs & 0xff, + NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ + hwctrl(&nand_info[0], (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ + /* Row address */ + hwctrl(&nand_info[0], (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */ + hwctrl(&nand_info[0], ((page_addr >> 8) & 0xff), + NAND_CTRL_ALE); /* A[27:20] */ +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + /* One more address cycle for devices > 128MiB */ + hwctrl(&nand_info[0], (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[31:28] */ +#endif + hwctrl(&nand_info[0], NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + if (cmd == NAND_CMD_READ0) { + /* Latch in address */ + hwctrl(&nand_info[0], NAND_CMD_READSTART, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + hwctrl(&nand_info[0], NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + while (!this->dev_ready(&nand_info[0])) + ; + } else if (cmd == NAND_CMD_RNDOUT) { + hwctrl(&nand_info[0], NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE | + NAND_CTRL_CHANGE); + hwctrl(&nand_info[0], NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + } + + return 0; +} + +static int nand_is_bad_block(int block) +{ + struct nand_chip *this = nand_info[0].priv; + + nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, + NAND_CMD_READOOB); + + /* + * Read one byte (or two if it's a 16 bit chip). + */ + if (this->options & NAND_BUSWIDTH_16) { + if (readw(this->IO_ADDR_R) != 0xffff) + return 1; + } else { + if (readb(this->IO_ADDR_R) != 0xff) + return 1; + } + + return 0; +} + +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = nand_info[0].priv; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + uint32_t data_pos = 0; + uint8_t *oob = &oob_data[0] + nand_ecc_pos[0]; + uint32_t oob_pos = eccsize * eccsteps + nand_ecc_pos[0]; + + nand_command(block, page, 0, NAND_CMD_READ0); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + this->ecc.hwctl(&nand_info[0], NAND_ECC_READ); + nand_command(block, page, data_pos, NAND_CMD_RNDOUT); + + this->read_buf(&nand_info[0], p, eccsize); + + nand_command(block, page, oob_pos, NAND_CMD_RNDOUT); + + this->read_buf(&nand_info[0], oob, eccbytes); + this->ecc.calculate(&nand_info[0], p, &ecc_calc[i]); + + data_pos += eccsize; + oob_pos += eccbytes; + oob += eccbytes; + } + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + eccsteps = ECCSTEPS; + p = dst; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + /* No chance to do something with the possible error message + * from correct_data(). We just hope that all possible errors + * are corrected by this routine. + */ + this->ecc.correct(&nand_info[0], p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + unsigned int block, lastblock; + unsigned int page; + + /* + * offs has to be aligned to a page address! + */ + block = offs / CONFIG_SYS_NAND_BLOCK_SIZE; + lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE; + page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE; + + while (block <= lastblock) { + if (!nand_is_bad_block(block)) { + /* + * Skip bad blocks + */ + while (page < CONFIG_SYS_NAND_PAGE_COUNT) { + nand_read_page(block, page, dst); + dst += CONFIG_SYS_NAND_PAGE_SIZE; + page++; + } + + page = 0; + } else { + lastblock++; + } + + block++; + } + + return 0; +} + +/* nand_init() - initialize data to make nand usable by SPL */ +void nand_init(void) +{ + /* + * Init board specific nand support + */ + nand_info[0].priv = &nand_chip; + nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + board_nand_init(&nand_chip); + + if (nand_chip.select_chip) + nand_chip.select_chip(&nand_info[0], 0); + + /* NAND chip may require reset after power-on */ + nand_command(0, 0, 0, NAND_CMD_RESET); +} + +/* Unselect after operation */ +void nand_deselect(void) +{ + if (nand_chip.select_chip) + nand_chip.select_chip(&nand_info[0], -1); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/atmel_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/atmel_nand.c new file mode 100644 index 000000000..e1fc48fca --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/atmel_nand.c @@ -0,0 +1,1437 @@ +/* + * (C) Copyright 2007-2008 + * Stelian Pop + * Lead Tech Design + * + * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * Add Programmable Multibit ECC support for various AT91 SoC + * (C) Copyright 2012 ATMEL, Hong Xu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_ATMEL_NAND_HWECC + +/* Register access macros */ +#define ecc_readl(add, reg) \ + readl(AT91_BASE_SYS + add + ATMEL_ECC_##reg) +#define ecc_writel(add, reg, value) \ + writel((value), AT91_BASE_SYS + add + ATMEL_ECC_##reg) + +#include "atmel_nand_ecc.h" /* Hardware ECC registers */ + +#ifdef CONFIG_ATMEL_NAND_HW_PMECC + +#ifdef CONFIG_SPL_BUILD +#undef CONFIG_SYS_NAND_ONFI_DETECTION +#endif + +struct atmel_nand_host { + struct pmecc_regs __iomem *pmecc; + struct pmecc_errloc_regs __iomem *pmerrloc; + void __iomem *pmecc_rom_base; + + u8 pmecc_corr_cap; + u16 pmecc_sector_size; + u32 pmecc_index_table_offset; + + int pmecc_bytes_per_sector; + int pmecc_sector_number; + int pmecc_degree; /* Degree of remainders */ + int pmecc_cw_len; /* Length of codeword */ + + /* lookup table for alpha_to and index_of */ + void __iomem *pmecc_alpha_to; + void __iomem *pmecc_index_of; + + /* data for pmecc computation */ + int16_t *pmecc_smu; + int16_t *pmecc_partial_syn; + int16_t *pmecc_si; + int16_t *pmecc_lmu; /* polynomal order */ + int *pmecc_mu; + int *pmecc_dmu; + int *pmecc_delta; +}; + +static struct atmel_nand_host pmecc_host; +static struct nand_ecclayout atmel_pmecc_oobinfo; + +/* + * Return number of ecc bytes per sector according to sector size and + * correction capability + * + * Following table shows what at91 PMECC supported: + * Correction Capability Sector_512_bytes Sector_1024_bytes + * ===================== ================ ================= + * 2-bits 4-bytes 4-bytes + * 4-bits 7-bytes 7-bytes + * 8-bits 13-bytes 14-bytes + * 12-bits 20-bytes 21-bytes + * 24-bits 39-bytes 42-bytes + */ +static int pmecc_get_ecc_bytes(int cap, int sector_size) +{ + int m = 12 + sector_size / 512; + return (m * cap + 7) / 8; +} + +static void pmecc_config_ecc_layout(struct nand_ecclayout *layout, + int oobsize, int ecc_len) +{ + int i; + + layout->eccbytes = ecc_len; + + /* ECC will occupy the last ecc_len bytes continuously */ + for (i = 0; i < ecc_len; i++) + layout->eccpos[i] = oobsize - ecc_len + i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = + oobsize - ecc_len - layout->oobfree[0].offset; +} + +static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) +{ + int table_size; + + table_size = host->pmecc_sector_size == 512 ? + PMECC_INDEX_TABLE_SIZE_512 : PMECC_INDEX_TABLE_SIZE_1024; + + /* the ALPHA lookup table is right behind the INDEX lookup table. */ + return host->pmecc_rom_base + host->pmecc_index_table_offset + + table_size * sizeof(int16_t); +} + +static void pmecc_data_free(struct atmel_nand_host *host) +{ + free(host->pmecc_partial_syn); + free(host->pmecc_si); + free(host->pmecc_lmu); + free(host->pmecc_smu); + free(host->pmecc_mu); + free(host->pmecc_dmu); + free(host->pmecc_delta); +} + +static int pmecc_data_alloc(struct atmel_nand_host *host) +{ + const int cap = host->pmecc_corr_cap; + int size; + + size = (2 * cap + 1) * sizeof(int16_t); + host->pmecc_partial_syn = malloc(size); + host->pmecc_si = malloc(size); + host->pmecc_lmu = malloc((cap + 1) * sizeof(int16_t)); + host->pmecc_smu = malloc((cap + 2) * size); + + size = (cap + 1) * sizeof(int); + host->pmecc_mu = malloc(size); + host->pmecc_dmu = malloc(size); + host->pmecc_delta = malloc(size); + + if (host->pmecc_partial_syn && + host->pmecc_si && + host->pmecc_lmu && + host->pmecc_smu && + host->pmecc_mu && + host->pmecc_dmu && + host->pmecc_delta) + return 0; + + /* error happened */ + pmecc_data_free(host); + return -ENOMEM; + +} + +static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + int i; + uint32_t value; + + /* Fill odd syndromes */ + for (i = 0; i < host->pmecc_corr_cap; i++) { + value = readl(&host->pmecc->rem_port[sector].rem[i / 2]); + if (i & 1) + value >>= 16; + value &= 0xffff; + host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value; + } +} + +static void pmecc_substitute(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + int16_t __iomem *alpha_to = host->pmecc_alpha_to; + int16_t __iomem *index_of = host->pmecc_index_of; + int16_t *partial_syn = host->pmecc_partial_syn; + const int cap = host->pmecc_corr_cap; + int16_t *si; + int i, j; + + /* si[] is a table that holds the current syndrome value, + * an element of that table belongs to the field + */ + si = host->pmecc_si; + + memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1)); + + /* Computation 2t syndromes based on S(x) */ + /* Odd syndromes */ + for (i = 1; i < 2 * cap; i += 2) { + for (j = 0; j < host->pmecc_degree; j++) { + if (partial_syn[i] & (0x1 << j)) + si[i] = readw(alpha_to + i * j) ^ si[i]; + } + } + /* Even syndrome = (Odd syndrome) ** 2 */ + for (i = 2, j = 1; j <= cap; i = ++j << 1) { + if (si[j] == 0) { + si[i] = 0; + } else { + int16_t tmp; + + tmp = readw(index_of + si[j]); + tmp = (tmp * 2) % host->pmecc_cw_len; + si[i] = readw(alpha_to + tmp); + } + } +} + +/* + * This function defines a Berlekamp iterative procedure for + * finding the value of the error location polynomial. + * The input is si[], initialize by pmecc_substitute(). + * The output is smu[][]. + * + * This function is written according to chip datasheet Chapter: + * Find the Error Location Polynomial Sigma(x) of Section: + * Programmable Multibit ECC Control (PMECC). + */ +static void pmecc_get_sigma(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + + int16_t *lmu = host->pmecc_lmu; + int16_t *si = host->pmecc_si; + int *mu = host->pmecc_mu; + int *dmu = host->pmecc_dmu; /* Discrepancy */ + int *delta = host->pmecc_delta; /* Delta order */ + int cw_len = host->pmecc_cw_len; + const int16_t cap = host->pmecc_corr_cap; + const int num = 2 * cap + 1; + int16_t __iomem *index_of = host->pmecc_index_of; + int16_t __iomem *alpha_to = host->pmecc_alpha_to; + int i, j, k; + uint32_t dmu_0_count, tmp; + int16_t *smu = host->pmecc_smu; + + /* index of largest delta */ + int ro; + int largest; + int diff; + + /* Init the Sigma(x) */ + memset(smu, 0, sizeof(int16_t) * ARRAY_SIZE(smu)); + + dmu_0_count = 0; + + /* First Row */ + + /* Mu */ + mu[0] = -1; + + smu[0] = 1; + + /* discrepancy set to 1 */ + dmu[0] = 1; + /* polynom order set to 0 */ + lmu[0] = 0; + /* delta[0] = (mu[0] * 2 - lmu[0]) >> 1; */ + delta[0] = -1; + + /* Second Row */ + + /* Mu */ + mu[1] = 0; + /* Sigma(x) set to 1 */ + smu[num] = 1; + + /* discrepancy set to S1 */ + dmu[1] = si[1]; + + /* polynom order set to 0 */ + lmu[1] = 0; + + /* delta[1] = (mu[1] * 2 - lmu[1]) >> 1; */ + delta[1] = 0; + + for (i = 1; i <= cap; i++) { + mu[i + 1] = i << 1; + /* Begin Computing Sigma (Mu+1) and L(mu) */ + /* check if discrepancy is set to 0 */ + if (dmu[i] == 0) { + dmu_0_count++; + + tmp = ((cap - (lmu[i] >> 1) - 1) / 2); + if ((cap - (lmu[i] >> 1) - 1) & 0x1) + tmp += 2; + else + tmp += 1; + + if (dmu_0_count == tmp) { + for (j = 0; j <= (lmu[i] >> 1) + 1; j++) + smu[(cap + 1) * num + j] = + smu[i * num + j]; + + lmu[cap + 1] = lmu[i]; + return; + } + + /* copy polynom */ + for (j = 0; j <= lmu[i] >> 1; j++) + smu[(i + 1) * num + j] = smu[i * num + j]; + + /* copy previous polynom order to the next */ + lmu[i + 1] = lmu[i]; + } else { + ro = 0; + largest = -1; + /* find largest delta with dmu != 0 */ + for (j = 0; j < i; j++) { + if ((dmu[j]) && (delta[j] > largest)) { + largest = delta[j]; + ro = j; + } + } + + /* compute difference */ + diff = (mu[i] - mu[ro]); + + /* Compute degree of the new smu polynomial */ + if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) + lmu[i + 1] = lmu[i]; + else + lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; + + /* Init smu[i+1] with 0 */ + for (k = 0; k < num; k++) + smu[(i + 1) * num + k] = 0; + + /* Compute smu[i+1] */ + for (k = 0; k <= lmu[ro] >> 1; k++) { + int16_t a, b, c; + + if (!(smu[ro * num + k] && dmu[i])) + continue; + a = readw(index_of + dmu[i]); + b = readw(index_of + dmu[ro]); + c = readw(index_of + smu[ro * num + k]); + tmp = a + (cw_len - b) + c; + a = readw(alpha_to + tmp % cw_len); + smu[(i + 1) * num + (k + diff)] = a; + } + + for (k = 0; k <= lmu[i] >> 1; k++) + smu[(i + 1) * num + k] ^= smu[i * num + k]; + } + + /* End Computing Sigma (Mu+1) and L(mu) */ + /* In either case compute delta */ + delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; + + /* Do not compute discrepancy for the last iteration */ + if (i >= cap) + continue; + + for (k = 0; k <= (lmu[i + 1] >> 1); k++) { + tmp = 2 * (i - 1); + if (k == 0) { + dmu[i + 1] = si[tmp + 3]; + } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { + int16_t a, b, c; + a = readw(index_of + + smu[(i + 1) * num + k]); + b = si[2 * (i - 1) + 3 - k]; + c = readw(index_of + b); + tmp = a + c; + tmp %= cw_len; + dmu[i + 1] = readw(alpha_to + tmp) ^ + dmu[i + 1]; + } + } + } +} + +static int pmecc_err_location(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + const int cap = host->pmecc_corr_cap; + const int num = 2 * cap + 1; + int sector_size = host->pmecc_sector_size; + int err_nbr = 0; /* number of error */ + int roots_nbr; /* number of roots */ + int i; + uint32_t val; + int16_t *smu = host->pmecc_smu; + int timeout = PMECC_MAX_TIMEOUT_US; + + writel(PMERRLOC_DISABLE, &host->pmerrloc->eldis); + + for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) { + writel(smu[(cap + 1) * num + i], &host->pmerrloc->sigma[i]); + err_nbr++; + } + + val = PMERRLOC_ELCFG_NUM_ERRORS(err_nbr - 1); + if (sector_size == 1024) + val |= PMERRLOC_ELCFG_SECTOR_1024; + + writel(val, &host->pmerrloc->elcfg); + writel(sector_size * 8 + host->pmecc_degree * cap, + &host->pmerrloc->elen); + + while (--timeout) { + if (readl(&host->pmerrloc->elisr) & PMERRLOC_CALC_DONE) + break; + WATCHDOG_RESET(); + udelay(1); + } + + if (!timeout) { + dev_err(host->dev, "atmel_nand : Timeout to calculate PMECC error location\n"); + return -1; + } + + roots_nbr = (readl(&host->pmerrloc->elisr) & PMERRLOC_ERR_NUM_MASK) + >> 8; + /* Number of roots == degree of smu hence <= cap */ + if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1) + return err_nbr - 1; + + /* Number of roots does not match the degree of smu + * unable to correct error */ + return -1; +} + +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc, + int sector_num, int extra_bytes, int err_nbr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + int i = 0; + int byte_pos, bit_pos, sector_size, pos; + uint32_t tmp; + uint8_t err_byte; + + sector_size = host->pmecc_sector_size; + + while (err_nbr) { + tmp = readl(&host->pmerrloc->el[i]) - 1; + byte_pos = tmp / 8; + bit_pos = tmp % 8; + + if (byte_pos >= (sector_size + extra_bytes)) + BUG(); /* should never happen */ + + if (byte_pos < sector_size) { + err_byte = *(buf + byte_pos); + *(buf + byte_pos) ^= (1 << bit_pos); + + pos = sector_num * host->pmecc_sector_size + byte_pos; + dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", + pos, bit_pos, err_byte, *(buf + byte_pos)); + } else { + /* Bit flip in OOB area */ + tmp = sector_num * host->pmecc_bytes_per_sector + + (byte_pos - sector_size); + err_byte = ecc[tmp]; + ecc[tmp] ^= (1 << bit_pos); + + pos = tmp + nand_chip->ecc.layout->eccpos[0]; + dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", + pos, bit_pos, err_byte, ecc[tmp]); + } + + i++; + err_nbr--; + } + + return; +} + +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, + u8 *ecc) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + int i, err_nbr, eccbytes; + uint8_t *buf_pos; + + eccbytes = nand_chip->ecc.bytes; + for (i = 0; i < eccbytes; i++) + if (ecc[i] != 0xff) + goto normal_check; + /* Erased page, return OK */ + return 0; + +normal_check: + for (i = 0; i < host->pmecc_sector_number; i++) { + err_nbr = 0; + if (pmecc_stat & 0x1) { + buf_pos = buf + i * host->pmecc_sector_size; + + pmecc_gen_syndrome(mtd, i); + pmecc_substitute(mtd); + pmecc_get_sigma(mtd); + + err_nbr = pmecc_err_location(mtd); + if (err_nbr == -1) { + dev_err(host->dev, "PMECC: Too many errors\n"); + mtd->ecc_stats.failed++; + return -EIO; + } else { + pmecc_correct_data(mtd, buf_pos, ecc, i, + host->pmecc_bytes_per_sector, err_nbr); + mtd->ecc_stats.corrected += err_nbr; + } + } + pmecc_stat >>= 1; + } + + return 0; +} + +static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + struct atmel_nand_host *host = chip->priv; + int eccsize = chip->ecc.size; + uint8_t *oob = chip->oob_poi; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t stat; + int timeout = PMECC_MAX_TIMEOUT_US; + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + pmecc_writel(host->pmecc, cfg, ((pmecc_readl(host->pmecc, cfg)) + & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE); + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); + + chip->read_buf(mtd, buf, eccsize); + chip->read_buf(mtd, oob, mtd->oobsize); + + while (--timeout) { + if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) + break; + WATCHDOG_RESET(); + udelay(1); + } + + if (!timeout) { + dev_err(host->dev, "atmel_nand : Timeout to read PMECC page\n"); + return -1; + } + + stat = pmecc_readl(host->pmecc, isr); + if (stat != 0) + if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0) + return -EIO; + + return 0; +} + +static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required) +{ + struct atmel_nand_host *host = chip->priv; + uint32_t *eccpos = chip->ecc.layout->eccpos; + int i, j; + int timeout = PMECC_MAX_TIMEOUT_US; + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + + pmecc_writel(host->pmecc, cfg, (pmecc_readl(host->pmecc, cfg) | + PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE); + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); + + chip->write_buf(mtd, (u8 *)buf, mtd->writesize); + + while (--timeout) { + if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) + break; + WATCHDOG_RESET(); + udelay(1); + } + + if (!timeout) { + dev_err(host->dev, "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n"); + goto out; + } + + for (i = 0; i < host->pmecc_sector_number; i++) { + for (j = 0; j < host->pmecc_bytes_per_sector; j++) { + int pos; + + pos = i * host->pmecc_bytes_per_sector + j; + chip->oob_poi[eccpos[pos]] = + readb(&host->pmecc->ecc_port[i].ecc[j]); + } + } + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +out: + return 0; +} + +static void atmel_pmecc_core_init(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + uint32_t val = 0; + struct nand_ecclayout *ecc_layout; + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + + switch (host->pmecc_corr_cap) { + case 2: + val = PMECC_CFG_BCH_ERR2; + break; + case 4: + val = PMECC_CFG_BCH_ERR4; + break; + case 8: + val = PMECC_CFG_BCH_ERR8; + break; + case 12: + val = PMECC_CFG_BCH_ERR12; + break; + case 24: + val = PMECC_CFG_BCH_ERR24; + break; + } + + if (host->pmecc_sector_size == 512) + val |= PMECC_CFG_SECTOR512; + else if (host->pmecc_sector_size == 1024) + val |= PMECC_CFG_SECTOR1024; + + switch (host->pmecc_sector_number) { + case 1: + val |= PMECC_CFG_PAGE_1SECTOR; + break; + case 2: + val |= PMECC_CFG_PAGE_2SECTORS; + break; + case 4: + val |= PMECC_CFG_PAGE_4SECTORS; + break; + case 8: + val |= PMECC_CFG_PAGE_8SECTORS; + break; + } + + val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE + | PMECC_CFG_AUTO_DISABLE); + pmecc_writel(host->pmecc, cfg, val); + + ecc_layout = nand_chip->ecc.layout; + pmecc_writel(host->pmecc, sarea, mtd->oobsize - 1); + pmecc_writel(host->pmecc, saddr, ecc_layout->eccpos[0]); + pmecc_writel(host->pmecc, eaddr, + ecc_layout->eccpos[ecc_layout->eccbytes - 1]); + /* See datasheet about PMECC Clock Control Register */ + pmecc_writel(host->pmecc, clk, PMECC_CLK_133MHZ); + pmecc_writel(host->pmecc, idr, 0xff); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); +} + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION +/* + * get_onfi_ecc_param - Get ECC requirement from ONFI parameters + * @ecc_bits: store the ONFI ECC correct bits capbility + * @sector_size: in how many bytes that ONFI require to correct @ecc_bits + * + * Returns -1 if ONFI parameters is not supported. In this case @ecc_bits, + * @sector_size are initialize to 0. + * Return 0 if success to get the ECC requirement. + */ +static int get_onfi_ecc_param(struct nand_chip *chip, + int *ecc_bits, int *sector_size) +{ + *ecc_bits = *sector_size = 0; + + if (chip->onfi_params.ecc_bits == 0xff) + /* TODO: the sector_size and ecc_bits need to be find in + * extended ecc parameter, currently we don't support it. + */ + return -1; + + *ecc_bits = chip->onfi_params.ecc_bits; + + /* The default sector size (ecc codeword size) is 512 */ + *sector_size = 512; + + return 0; +} + +/* + * pmecc_choose_ecc - Get ecc requirement from ONFI parameters. If + * pmecc_corr_cap or pmecc_sector_size is 0, then set it as + * ONFI ECC parameters. + * @host: point to an atmel_nand_host structure. + * if host->pmecc_corr_cap is 0 then set it as the ONFI ecc_bits. + * if host->pmecc_sector_size is 0 then set it as the ONFI sector_size. + * @chip: point to an nand_chip structure. + * @cap: store the ONFI ECC correct bits capbility + * @sector_size: in how many bytes that ONFI require to correct @ecc_bits + * + * Return 0 if success. otherwise return the error code. + */ +static int pmecc_choose_ecc(struct atmel_nand_host *host, + struct nand_chip *chip, + int *cap, int *sector_size) +{ + /* Get ECC requirement from ONFI parameters */ + *cap = *sector_size = 0; + if (chip->onfi_version) { + if (!get_onfi_ecc_param(chip, cap, sector_size)) { + MTDDEBUG(MTD_DEBUG_LEVEL1, "ONFI params, minimum required ECC: %d bits in %d bytes\n", + *cap, *sector_size); + } else { + dev_info(host->dev, "NAND chip ECC reqirement is in Extended ONFI parameter, we don't support yet.\n"); + } + } else { + dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes"); + } + if (*cap == 0 && *sector_size == 0) { + /* Non-ONFI compliant or use extended ONFI parameters */ + *cap = 2; + *sector_size = 512; + } + + /* If head file doesn't specify then use the one in ONFI parameters */ + if (host->pmecc_corr_cap == 0) { + /* use the most fitable ecc bits (the near bigger one ) */ + if (*cap <= 2) + host->pmecc_corr_cap = 2; + else if (*cap <= 4) + host->pmecc_corr_cap = 4; + else if (*cap <= 8) + host->pmecc_corr_cap = 8; + else if (*cap <= 12) + host->pmecc_corr_cap = 12; + else if (*cap <= 24) + host->pmecc_corr_cap = 24; + else + return -EINVAL; + } + if (host->pmecc_sector_size == 0) { + /* use the most fitable sector size (the near smaller one ) */ + if (*sector_size >= 1024) + host->pmecc_sector_size = 1024; + else if (*sector_size >= 512) + host->pmecc_sector_size = 512; + else + return -EINVAL; + } + return 0; +} +#endif + +static int atmel_pmecc_nand_init_params(struct nand_chip *nand, + struct mtd_info *mtd) +{ + struct atmel_nand_host *host; + int cap, sector_size; + + host = nand->priv = &pmecc_host; + + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.calculate = NULL; + nand->ecc.correct = NULL; + nand->ecc.hwctl = NULL; + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + host->pmecc_corr_cap = host->pmecc_sector_size = 0; + +#ifdef CONFIG_PMECC_CAP + host->pmecc_corr_cap = CONFIG_PMECC_CAP; +#endif +#ifdef CONFIG_PMECC_SECTOR_SIZE + host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; +#endif + /* Get ECC requirement of ONFI parameters. And if CONFIG_PMECC_CAP or + * CONFIG_PMECC_SECTOR_SIZE not defined, then use ecc_bits, sector_size + * from ONFI. + */ + if (pmecc_choose_ecc(host, nand, &cap, §or_size)) { + dev_err(host->dev, "The NAND flash's ECC requirement(ecc_bits: %d, sector_size: %d) are not support!", + cap, sector_size); + return -EINVAL; + } + + if (cap > host->pmecc_corr_cap) + dev_info(host->dev, "WARNING: Using different ecc correct bits(%d bit) from Nand ONFI ECC reqirement (%d bit).\n", + host->pmecc_corr_cap, cap); + if (sector_size < host->pmecc_sector_size) + dev_info(host->dev, "WARNING: Using different ecc correct sector size (%d bytes) from Nand ONFI ECC reqirement (%d bytes).\n", + host->pmecc_sector_size, sector_size); +#else /* CONFIG_SYS_NAND_ONFI_DETECTION */ + host->pmecc_corr_cap = CONFIG_PMECC_CAP; + host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; +#endif + + cap = host->pmecc_corr_cap; + sector_size = host->pmecc_sector_size; + + /* TODO: need check whether cap & sector_size is validate */ + + if (host->pmecc_sector_size == 512) + host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512; + else + host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_1024; + + MTDDEBUG(MTD_DEBUG_LEVEL1, + "Initialize PMECC params, cap: %d, sector: %d\n", + cap, sector_size); + + host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC; + host->pmerrloc = (struct pmecc_errloc_regs __iomem *) + ATMEL_BASE_PMERRLOC; + host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM; + + /* ECC is calculated for the whole page (1 step) */ + nand->ecc.size = mtd->writesize; + + /* set ECC page size and oob layout */ + switch (mtd->writesize) { + case 2048: + case 4096: + case 8192: + host->pmecc_degree = (sector_size == 512) ? + PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14; + host->pmecc_cw_len = (1 << host->pmecc_degree) - 1; + host->pmecc_sector_number = mtd->writesize / sector_size; + host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes( + cap, sector_size); + host->pmecc_alpha_to = pmecc_get_alpha_to(host); + host->pmecc_index_of = host->pmecc_rom_base + + host->pmecc_index_table_offset; + + nand->ecc.steps = 1; + nand->ecc.bytes = host->pmecc_bytes_per_sector * + host->pmecc_sector_number; + + if (nand->ecc.bytes > MTD_MAX_ECCPOS_ENTRIES_LARGE) { + dev_err(host->dev, "too large eccpos entries. max support ecc.bytes is %d\n", + MTD_MAX_ECCPOS_ENTRIES_LARGE); + return -EINVAL; + } + + if (nand->ecc.bytes > mtd->oobsize - 2) { + dev_err(host->dev, "No room for ECC bytes\n"); + return -EINVAL; + } + pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, + mtd->oobsize, + nand->ecc.bytes); + nand->ecc.layout = &atmel_pmecc_oobinfo; + break; + case 512: + case 1024: + /* TODO */ + dev_err(host->dev, "Unsupported page size for PMECC, use Software ECC\n"); + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.read_page = NULL; + nand->ecc.postpad = 0; + nand->ecc.prepad = 0; + nand->ecc.bytes = 0; + return 0; + } + + /* Allocate data for PMECC computation */ + if (pmecc_data_alloc(host)) { + dev_err(host->dev, "Cannot allocate memory for PMECC computation!\n"); + return -ENOMEM; + } + + nand->ecc.read_page = atmel_nand_pmecc_read_page; + nand->ecc.write_page = atmel_nand_pmecc_write_page; + nand->ecc.strength = cap; + + atmel_pmecc_core_init(mtd); + + return 0; +} + +#else + +/* oob layout for large page size + * bad block info is on bytes 0 and 1 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout atmel_oobinfo_large = { + .eccbytes = 4, + .eccpos = {60, 61, 62, 63}, + .oobfree = { + {2, 58} + }, +}; + +/* oob layout for small page size + * bad block info is on bytes 4 and 5 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout atmel_oobinfo_small = { + .eccbytes = 4, + .eccpos = {0, 1, 2, 3}, + .oobfree = { + {6, 10} + }, +}; + +/* + * Calculate HW ECC + * + * function called after a write + * + * mtd: MTD block structure + * dat: raw data (unused) + * ecc_code: buffer for ECC + */ +static int atmel_nand_calculate(struct mtd_info *mtd, + const u_char *dat, unsigned char *ecc_code) +{ + unsigned int ecc_value; + + /* get the first 2 ECC bytes */ + ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR); + + ecc_code[0] = ecc_value & 0xFF; + ecc_code[1] = (ecc_value >> 8) & 0xFF; + + /* get the last 2 ECC bytes */ + ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, NPR) & ATMEL_ECC_NPARITY; + + ecc_code[2] = ecc_value & 0xFF; + ecc_code[3] = (ecc_value >> 8) & 0xFF; + + return 0; +} + +/* + * HW ECC read page function + * + * mtd: mtd info structure + * chip: nand chip info structure + * buf: buffer to store read data + * oob_required: caller expects OOB data read to chip->oob_poi + */ +static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + uint8_t *ecc_pos; + int stat; + + /* read the page */ + chip->read_buf(mtd, p, eccsize); + + /* move to ECC position if needed */ + if (eccpos[0] != 0) { + /* This only works on large pages + * because the ECC controller waits for + * NAND_CMD_RNDOUTSTART after the + * NAND_CMD_RNDOUT. + * anyway, for small pages, the eccpos[0] == 0 + */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + eccpos[0], -1); + } + + /* the ECC controller needs to read the ECC just after the data */ + ecc_pos = oob + eccpos[0]; + chip->read_buf(mtd, ecc_pos, eccbytes); + + /* check if there's an error */ + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + /* get back to oob start (end of page) */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + + /* read the oob */ + chip->read_buf(mtd, oob, mtd->oobsize); + + return 0; +} + +/* + * HW ECC Correction + * + * function called after a read + * + * mtd: MTD block structure + * dat: raw data read from the chip + * read_ecc: ECC from the chip (unused) + * isnull: unused + * + * Detect and correct a 1 bit error for a page + */ +static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) +{ + struct nand_chip *nand_chip = mtd->priv; + unsigned int ecc_status; + unsigned int ecc_word, ecc_bit; + + /* get the status from the Status Register */ + ecc_status = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, SR); + + /* if there's no error */ + if (likely(!(ecc_status & ATMEL_ECC_RECERR))) + return 0; + + /* get error bit offset (4 bits) */ + ecc_bit = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_BITADDR; + /* get word address (12 bits) */ + ecc_word = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_WORDADDR; + ecc_word >>= 4; + + /* if there are multiple errors */ + if (ecc_status & ATMEL_ECC_MULERR) { + /* check if it is a freshly erased block + * (filled with 0xff) */ + if ((ecc_bit == ATMEL_ECC_BITADDR) + && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) { + /* the block has just been erased, return OK */ + return 0; + } + /* it doesn't seems to be a freshly + * erased block. + * We can't correct so many errors */ + dev_warn(host->dev, "atmel_nand : multiple errors detected." + " Unable to correct.\n"); + return -EIO; + } + + /* if there's a single bit error : we can correct it */ + if (ecc_status & ATMEL_ECC_ECCERR) { + /* there's nothing much to do here. + * the bit error is on the ECC itself. + */ + dev_warn(host->dev, "atmel_nand : one bit error on ECC code." + " Nothing to correct\n"); + return 0; + } + + dev_warn(host->dev, "atmel_nand : one bit error on data." + " (word offset in the page :" + " 0x%x bit offset : 0x%x)\n", + ecc_word, ecc_bit); + /* correct the error */ + if (nand_chip->options & NAND_BUSWIDTH_16) { + /* 16 bits words */ + ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit); + } else { + /* 8 bits words */ + dat[ecc_word] ^= (1 << ecc_bit); + } + dev_warn(host->dev, "atmel_nand : error corrected\n"); + return 1; +} + +/* + * Enable HW ECC : unused on most chips + */ +static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) +{ +} + +int atmel_hwecc_nand_init_param(struct nand_chip *nand, struct mtd_info *mtd) +{ + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.calculate = atmel_nand_calculate; + nand->ecc.correct = atmel_nand_correct; + nand->ecc.hwctl = atmel_nand_hwctl; + nand->ecc.read_page = atmel_nand_read_page; + nand->ecc.bytes = 4; + + if (nand->ecc.mode == NAND_ECC_HW) { + /* ECC is calculated for the whole page (1 step) */ + nand->ecc.size = mtd->writesize; + + /* set ECC page size and oob layout */ + switch (mtd->writesize) { + case 512: + nand->ecc.layout = &atmel_oobinfo_small; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, + ATMEL_ECC_PAGESIZE_528); + break; + case 1024: + nand->ecc.layout = &atmel_oobinfo_large; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, + ATMEL_ECC_PAGESIZE_1056); + break; + case 2048: + nand->ecc.layout = &atmel_oobinfo_large; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, + ATMEL_ECC_PAGESIZE_2112); + break; + case 4096: + nand->ecc.layout = &atmel_oobinfo_large; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, + ATMEL_ECC_PAGESIZE_4224); + break; + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.calculate = NULL; + nand->ecc.correct = NULL; + nand->ecc.hwctl = NULL; + nand->ecc.read_page = NULL; + nand->ecc.postpad = 0; + nand->ecc.prepad = 0; + nand->ecc.bytes = 0; + break; + } + } + + return 0; +} + +#endif /* CONFIG_ATMEL_NAND_HW_PMECC */ + +#endif /* CONFIG_ATMEL_NAND_HWECC */ + +static void at91_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + IO_ADDR_W &= ~(CONFIG_SYS_NAND_MASK_ALE + | CONFIG_SYS_NAND_MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= CONFIG_SYS_NAND_MASK_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= CONFIG_SYS_NAND_MASK_ALE; + +#ifdef CONFIG_SYS_NAND_ENABLE_PIN + gpio_set_value(CONFIG_SYS_NAND_ENABLE_PIN, !(ctrl & NAND_NCE)); +#endif + this->IO_ADDR_W = (void *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +#ifdef CONFIG_SYS_NAND_READY_PIN +static int at91_nand_ready(struct mtd_info *mtd) +{ + return gpio_get_value(CONFIG_SYS_NAND_READY_PIN); +} +#endif + +#ifdef CONFIG_SPL_BUILD +/* The following code is for SPL */ +static nand_info_t mtd; +static struct nand_chip nand_chip; + +static int nand_command(int block, int page, uint32_t offs, u8 cmd) +{ + struct nand_chip *this = mtd.priv; + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + + while (this->dev_ready(&mtd)) + ; + + if (cmd == NAND_CMD_READOOB) { + offs += CONFIG_SYS_NAND_PAGE_SIZE; + cmd = NAND_CMD_READ0; + } + + hwctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + + if (this->options & NAND_BUSWIDTH_16) + offs >>= 1; + + hwctrl(&mtd, offs & 0xff, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + hwctrl(&mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); + hwctrl(&mtd, (page_addr & 0xff), NAND_CTRL_ALE); + hwctrl(&mtd, ((page_addr >> 8) & 0xff), NAND_CTRL_ALE); +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + hwctrl(&mtd, (page_addr >> 16) & 0x0f, NAND_CTRL_ALE); +#endif + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + hwctrl(&mtd, NAND_CMD_READSTART, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + while (this->dev_ready(&mtd)) + ; + + return 0; +} + +static int nand_is_bad_block(int block) +{ + struct nand_chip *this = mtd.priv; + + nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, NAND_CMD_READOOB); + + if (this->options & NAND_BUSWIDTH_16) { + if (readw(this->IO_ADDR_R) != 0xffff) + return 1; + } else { + if (readb(this->IO_ADDR_R) != 0xff) + return 1; + } + + return 0; +} + +#ifdef CONFIG_SPL_NAND_ECC +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd.priv; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + int i; + uint8_t *p = dst; + nand_command(block, page, 0, NAND_CMD_READ0); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + if (this->ecc.mode != NAND_ECC_SOFT) + this->ecc.hwctl(&mtd, NAND_ECC_READ); + this->read_buf(&mtd, p, eccsize); + this->ecc.calculate(&mtd, p, &ecc_calc[i]); + } + this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + eccsteps = ECCSTEPS; + p = dst; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]); + + return 0; +} +#else +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd.priv; + + nand_command(block, page, 0, NAND_CMD_READ0); + atmel_nand_pmecc_read_page(&mtd, this, dst, 0, page); + + return 0; +} +#endif /* CONFIG_SPL_NAND_ECC */ + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + unsigned int block, lastblock; + unsigned int page; + + block = offs / CONFIG_SYS_NAND_BLOCK_SIZE; + lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE; + page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE; + + while (block <= lastblock) { + if (!nand_is_bad_block(block)) { + while (page < CONFIG_SYS_NAND_PAGE_COUNT) { + nand_read_page(block, page, dst); + dst += CONFIG_SYS_NAND_PAGE_SIZE; + page++; + } + + page = 0; + } else { + lastblock++; + } + + block++; + } + + return 0; +} + +int at91_nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + udelay(this->chip_delay); + + return 0; +} + +int board_nand_init(struct nand_chip *nand) +{ + int ret = 0; + + nand->ecc.mode = NAND_ECC_SOFT; +#ifdef CONFIG_SYS_NAND_DBW_16 + nand->options = NAND_BUSWIDTH_16; + nand->read_buf = nand_read_buf16; +#else + nand->read_buf = nand_read_buf; +#endif + nand->cmd_ctrl = at91_nand_hwcontrol; +#ifdef CONFIG_SYS_NAND_READY_PIN + nand->dev_ready = at91_nand_ready; +#else + nand->dev_ready = at91_nand_wait_ready; +#endif + nand->chip_delay = 20; + +#ifdef CONFIG_ATMEL_NAND_HWECC +#ifdef CONFIG_ATMEL_NAND_HW_PMECC + ret = atmel_pmecc_nand_init_params(nand, &mtd); +#endif +#endif + + return ret; +} + +void nand_init(void) +{ + mtd.writesize = CONFIG_SYS_NAND_PAGE_SIZE; + mtd.oobsize = CONFIG_SYS_NAND_OOBSIZE; + mtd.priv = &nand_chip; + nand_chip.IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE; + nand_chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; + board_nand_init(&nand_chip); + +#ifdef CONFIG_SPL_NAND_ECC + if (nand_chip.ecc.mode == NAND_ECC_SOFT) { + nand_chip.ecc.calculate = nand_calculate_ecc; + nand_chip.ecc.correct = nand_correct_data; + } +#endif + + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, 0); +} + +void nand_deselect(void) +{ + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, -1); +} + +#else + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; +static ulong base_addr[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST; + +int atmel_nand_chip_init(int devnum, ulong base_addr) +{ + int ret; + struct mtd_info *mtd = &nand_info[devnum]; + struct nand_chip *nand = &nand_chip[devnum]; + + mtd->priv = nand; + nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; + +#ifdef CONFIG_NAND_ECC_BCH + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif +#ifdef CONFIG_SYS_NAND_DBW_16 + nand->options = NAND_BUSWIDTH_16; +#endif + nand->cmd_ctrl = at91_nand_hwcontrol; +#ifdef CONFIG_SYS_NAND_READY_PIN + nand->dev_ready = at91_nand_ready; +#endif + nand->chip_delay = 75; + + ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL); + if (ret) + return ret; + +#ifdef CONFIG_ATMEL_NAND_HWECC +#ifdef CONFIG_ATMEL_NAND_HW_PMECC + ret = atmel_pmecc_nand_init_params(nand, mtd); +#else + ret = atmel_hwecc_nand_init_param(nand, mtd); +#endif + if (ret) + return ret; +#endif + + ret = nand_scan_tail(mtd); + if (!ret) + nand_register(devnum); + + return ret; +} + +void board_nand_init(void) +{ + int i; + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + if (atmel_nand_chip_init(i, base_addr[i])) + dev_err(host->dev, "atmel_nand: Fail to initialize #%d chip", + i); +} +#endif /* CONFIG_SPL_BUILD */ diff --git a/qemu/roms/u-boot/drivers/mtd/nand/atmel_nand_ecc.h b/qemu/roms/u-boot/drivers/mtd/nand/atmel_nand_ecc.h new file mode 100644 index 000000000..55d7711c8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/atmel_nand_ecc.h @@ -0,0 +1,146 @@ +/* + * Error Corrected Code Controller (ECC) - System peripherals regsters. + * Based on AT91SAM9260 datasheet revision B. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef ATMEL_NAND_ECC_H +#define ATMEL_NAND_ECC_H + +#define ATMEL_ECC_CR 0x00 /* Control register */ +#define ATMEL_ECC_RST (1 << 0) /* Reset parity */ + +#define ATMEL_ECC_MR 0x04 /* Mode register */ +#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */ +#define ATMEL_ECC_PAGESIZE_528 (0) +#define ATMEL_ECC_PAGESIZE_1056 (1) +#define ATMEL_ECC_PAGESIZE_2112 (2) +#define ATMEL_ECC_PAGESIZE_4224 (3) + +#define ATMEL_ECC_SR 0x08 /* Status register */ +#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */ +#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */ +#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */ + +#define ATMEL_ECC_PR 0x0c /* Parity register */ +#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */ +#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */ + +#define ATMEL_ECC_NPR 0x10 /* NParity register */ +#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */ + +/* Register access macros for PMECC */ +#define pmecc_readl(addr, reg) \ + readl(&addr->reg) + +#define pmecc_writel(addr, reg, value) \ + writel((value), &addr->reg) + +/* PMECC Register Definitions */ +#define PMECC_MAX_SECTOR_NUM 8 +struct pmecc_regs { + u32 cfg; /* 0x00 PMECC Configuration Register */ + u32 sarea; /* 0x04 PMECC Spare Area Size Register */ + u32 saddr; /* 0x08 PMECC Start Address Register */ + u32 eaddr; /* 0x0C PMECC End Address Register */ + u32 clk; /* 0x10 PMECC Clock Control Register */ + u32 ctrl; /* 0x14 PMECC Control Register */ + u32 sr; /* 0x18 PMECC Status Register */ + u32 ier; /* 0x1C PMECC Interrupt Enable Register */ + u32 idr; /* 0x20 PMECC Interrupt Disable Register */ + u32 imr; /* 0x24 PMECC Interrupt Mask Register */ + u32 isr; /* 0x28 PMECC Interrupt Status Register */ + u32 reserved0[5]; /* 0x2C-0x3C Reserved */ + + /* 0x40 + sector_num * (0x40), Redundancy Registers */ + struct { + u8 ecc[44]; /* PMECC Generated Redundancy Byte Per Sector */ + u32 reserved1[5]; + } ecc_port[PMECC_MAX_SECTOR_NUM]; + + /* 0x240 + sector_num * (0x40) Remainder Registers */ + struct { + u32 rem[12]; + u32 reserved2[4]; + } rem_port[PMECC_MAX_SECTOR_NUM]; + u32 reserved3[16]; /* 0x440-0x47C Reserved */ +}; + +/* For PMECC Configuration Register */ +#define PMECC_CFG_BCH_ERR2 (0 << 0) +#define PMECC_CFG_BCH_ERR4 (1 << 0) +#define PMECC_CFG_BCH_ERR8 (2 << 0) +#define PMECC_CFG_BCH_ERR12 (3 << 0) +#define PMECC_CFG_BCH_ERR24 (4 << 0) + +#define PMECC_CFG_SECTOR512 (0 << 4) +#define PMECC_CFG_SECTOR1024 (1 << 4) + +#define PMECC_CFG_PAGE_1SECTOR (0 << 8) +#define PMECC_CFG_PAGE_2SECTORS (1 << 8) +#define PMECC_CFG_PAGE_4SECTORS (2 << 8) +#define PMECC_CFG_PAGE_8SECTORS (3 << 8) + +#define PMECC_CFG_READ_OP (0 << 12) +#define PMECC_CFG_WRITE_OP (1 << 12) + +#define PMECC_CFG_SPARE_ENABLE (1 << 16) +#define PMECC_CFG_SPARE_DISABLE (0 << 16) + +#define PMECC_CFG_AUTO_ENABLE (1 << 20) +#define PMECC_CFG_AUTO_DISABLE (0 << 20) + +/* For PMECC Clock Control Register */ +#define PMECC_CLK_133MHZ (2 << 0) + +/* For PMECC Control Register */ +#define PMECC_CTRL_RST (1 << 0) +#define PMECC_CTRL_DATA (1 << 1) +#define PMECC_CTRL_USER (1 << 2) +#define PMECC_CTRL_ENABLE (1 << 4) +#define PMECC_CTRL_DISABLE (1 << 5) + +/* For PMECC Status Register */ +#define PMECC_SR_BUSY (1 << 0) +#define PMECC_SR_ENABLE (1 << 4) + +/* PMERRLOC Register Definitions */ +struct pmecc_errloc_regs { + u32 elcfg; /* 0x00 Error Location Configuration Register */ + u32 elprim; /* 0x04 Error Location Primitive Register */ + u32 elen; /* 0x08 Error Location Enable Register */ + u32 eldis; /* 0x0C Error Location Disable Register */ + u32 elsr; /* 0x10 Error Location Status Register */ + u32 elier; /* 0x14 Error Location Interrupt Enable Register */ + u32 elidr; /* 0x08 Error Location Interrupt Disable Register */ + u32 elimr; /* 0x0C Error Location Interrupt Mask Register */ + u32 elisr; /* 0x20 Error Location Interrupt Status Register */ + u32 reserved0; /* 0x24 Reserved */ + u32 sigma[25]; /* 0x28-0x88 Error Location Sigma Registers */ + u32 el[24]; /* 0x8C-0xE8 Error Location Registers */ + u32 reserved1[5]; /* 0xEC-0xFC Reserved */ +}; + +/* For Error Location Configuration Register */ +#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0) +#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0) +#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16) + +/* For Error Location Disable Register */ +#define PMERRLOC_DISABLE (1 << 0) + +/* For Error Location Interrupt Status Register */ +#define PMERRLOC_ERR_NUM_MASK (0x1f << 8) +#define PMERRLOC_CALC_DONE (1 << 0) + +/* Galois field dimension */ +#define PMECC_GF_DIMENSION_13 13 +#define PMECC_GF_DIMENSION_14 14 + +#define PMECC_INDEX_TABLE_SIZE_512 0x2000 +#define PMECC_INDEX_TABLE_SIZE_1024 0x4000 + +#define PMECC_MAX_TIMEOUT_US (100 * 1000) + +#endif diff --git a/qemu/roms/u-boot/drivers/mtd/nand/bfin_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/bfin_nand.c new file mode 100644 index 000000000..7e755e896 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/bfin_nand.c @@ -0,0 +1,393 @@ +/* + * Driver for Blackfin on-chip NAND controller. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (c) 2007-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +/* TODO: + * - move bit defines into mach-common/bits/nand.h + * - try and replace all IRQSTAT usage with STAT polling + * - have software ecc mode use same algo as hw ecc ? + */ + +#include +#include + +#ifdef DEBUG +# define pr_stamp() printf("%s:%s:%i: here i am\n", __FILE__, __func__, __LINE__) +#else +# define pr_stamp() +#endif + +#include + +#include +#include + +/* Bit masks for NFC_CTL */ + +#define WR_DLY 0xf /* Write Strobe Delay */ +#define RD_DLY 0xf0 /* Read Strobe Delay */ +#define NWIDTH 0x100 /* NAND Data Width */ +#define PG_SIZE 0x200 /* Page Size */ + +/* Bit masks for NFC_STAT */ + +#define NBUSY 0x1 /* Not Busy */ +#define WB_FULL 0x2 /* Write Buffer Full */ +#define PG_WR_STAT 0x4 /* Page Write Pending */ +#define PG_RD_STAT 0x8 /* Page Read Pending */ +#define WB_EMPTY 0x10 /* Write Buffer Empty */ + +/* Bit masks for NFC_IRQSTAT */ + +#define NBUSYIRQ 0x1 /* Not Busy IRQ */ +#define WB_OVF 0x2 /* Write Buffer Overflow */ +#define WB_EDGE 0x4 /* Write Buffer Edge Detect */ +#define RD_RDY 0x8 /* Read Data Ready */ +#define WR_DONE 0x10 /* Page Write Done */ + +#define NAND_IS_512() (CONFIG_BFIN_NFC_CTL_VAL & 0x200) + +/* + * hardware specific access to control-lines + */ +static void bfin_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + pr_stamp(); + + if (cmd == NAND_CMD_NONE) + return; + + while (bfin_read_NFC_STAT() & WB_FULL) + continue; + + if (ctrl & NAND_CLE) + bfin_write_NFC_CMD(cmd); + else + bfin_write_NFC_ADDR(cmd); + SSYNC(); +} + +static int bfin_nfc_devready(struct mtd_info *mtd) +{ + pr_stamp(); + return (bfin_read_NFC_STAT() & NBUSY) ? 1 : 0; +} + +/* + * PIO mode for buffer writing and reading + */ +static void bfin_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + pr_stamp(); + + int i; + + /* + * Data reads are requested by first writing to NFC_DATA_RD + * and then reading back from NFC_READ. + */ + for (i = 0; i < len; ++i) { + while (bfin_read_NFC_STAT() & WB_FULL) + if (ctrlc()) + return; + + /* Contents do not matter */ + bfin_write_NFC_DATA_RD(0x0000); + SSYNC(); + + while (!(bfin_read_NFC_IRQSTAT() & RD_RDY)) + if (ctrlc()) + return; + + buf[i] = bfin_read_NFC_READ(); + + bfin_write_NFC_IRQSTAT(RD_RDY); + } +} + +static uint8_t bfin_nfc_read_byte(struct mtd_info *mtd) +{ + pr_stamp(); + + uint8_t val; + bfin_nfc_read_buf(mtd, &val, 1); + return val; +} + +static void bfin_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + pr_stamp(); + + int i; + + for (i = 0; i < len; ++i) { + while (bfin_read_NFC_STAT() & WB_FULL) + if (ctrlc()) + return; + + bfin_write_NFC_DATA_WR(buf[i]); + } + + /* Wait for the buffer to drain before we return */ + while (!(bfin_read_NFC_STAT() & WB_EMPTY)) + if (ctrlc()) + return; +} + +/* + * ECC functions + * These allow the bfin to use the controller's ECC + * generator block to ECC the data as it passes through + */ + +/* + * ECC error correction function + */ +static int bfin_nfc_correct_data_256(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + u32 syndrome[5]; + u32 calced, stored; + unsigned short failing_bit, failing_byte; + u_char data; + + pr_stamp(); + + calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16); + stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16); + + syndrome[0] = (calced ^ stored); + + /* + * syndrome 0: all zero + * No error in data + * No action + */ + if (!syndrome[0] || !calced || !stored) + return 0; + + /* + * sysdrome 0: only one bit is one + * ECC data was incorrect + * No action + */ + if (hweight32(syndrome[0]) == 1) + return 1; + + syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF); + syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF); + syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF); + syndrome[4] = syndrome[2] ^ syndrome[3]; + + /* + * sysdrome 0: exactly 11 bits are one, each parity + * and parity' pair is 1 & 0 or 0 & 1. + * 1-bit correctable error + * Correct the error + */ + if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) { + failing_bit = syndrome[1] & 0x7; + failing_byte = syndrome[1] >> 0x3; + data = *(dat + failing_byte); + data = data ^ (0x1 << failing_bit); + *(dat + failing_byte) = data; + + return 0; + } + + /* + * sysdrome 0: random data + * More than 1-bit error, non-correctable error + * Discard data, mark bad block + */ + + return 1; +} + +static int bfin_nfc_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + int ret; + + pr_stamp(); + + ret = bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc); + + /* If page size is 512, correct second 256 bytes */ + if (NAND_IS_512()) { + dat += 256; + read_ecc += 8; + calc_ecc += 8; + ret |= bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc); + } + + return ret; +} + +static void reset_ecc(void) +{ + bfin_write_NFC_RST(0x1); + while (bfin_read_NFC_RST() & 1) + continue; +} + +static void bfin_nfc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + reset_ecc(); +} + +static int bfin_nfc_calculate_ecc(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + u16 ecc0, ecc1; + u32 code[2]; + u8 *p; + + pr_stamp(); + + /* first 4 bytes ECC code for 256 page size */ + ecc0 = bfin_read_NFC_ECC0(); + ecc1 = bfin_read_NFC_ECC1(); + + code[0] = (ecc0 & 0x7FF) | ((ecc1 & 0x7FF) << 11); + + /* first 3 bytes in ecc_code for 256 page size */ + p = (u8 *) code; + memcpy(ecc_code, p, 3); + + /* second 4 bytes ECC code for 512 page size */ + if (NAND_IS_512()) { + ecc0 = bfin_read_NFC_ECC2(); + ecc1 = bfin_read_NFC_ECC3(); + code[1] = (ecc0 & 0x7FF) | ((ecc1 & 0x7FF) << 11); + + /* second 3 bytes in ecc_code for second 256 + * bytes of 512 page size + */ + p = (u8 *) (code + 1); + memcpy((ecc_code + 3), p, 3); + } + + reset_ecc(); + + return 0; +} + +#ifdef CONFIG_BFIN_NFC_BOOTROM_ECC +# define BOOTROM_ECC 1 +#else +# define BOOTROM_ECC 0 +#endif + +static uint8_t bbt_pattern[] = { 0xff }; + +static struct nand_bbt_descr bootrom_bbt = { + .options = 0, + .offs = 63, + .len = 1, + .pattern = bbt_pattern, +}; + +static struct nand_ecclayout bootrom_ecclayout = { + .eccbytes = 24, + .eccpos = { + 0x8 * 0, 0x8 * 0 + 1, 0x8 * 0 + 2, + 0x8 * 1, 0x8 * 1 + 1, 0x8 * 1 + 2, + 0x8 * 2, 0x8 * 2 + 1, 0x8 * 2 + 2, + 0x8 * 3, 0x8 * 3 + 1, 0x8 * 3 + 2, + 0x8 * 4, 0x8 * 4 + 1, 0x8 * 4 + 2, + 0x8 * 5, 0x8 * 5 + 1, 0x8 * 5 + 2, + 0x8 * 6, 0x8 * 6 + 1, 0x8 * 6 + 2, + 0x8 * 7, 0x8 * 7 + 1, 0x8 * 7 + 2 + }, + .oobfree = { + { 0x8 * 0 + 3, 5 }, + { 0x8 * 1 + 3, 5 }, + { 0x8 * 2 + 3, 5 }, + { 0x8 * 3 + 3, 5 }, + { 0x8 * 4 + 3, 5 }, + { 0x8 * 5 + 3, 5 }, + { 0x8 * 6 + 3, 5 }, + { 0x8 * 7 + 3, 5 }, + } +}; + +/* + * Board-specific NAND initialization. The following members of the + * argument are board-specific (per include/linux/mtd/nand.h): + * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device + * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device + * - cmd_ctrl: hardwarespecific function for accesing control-lines + * - dev_ready: hardwarespecific function for accesing device ready/busy line + * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must + * only be provided if a hardware ECC is available + * - ecc.mode: mode of ecc, see defines + * - chip_delay: chip dependent delay for transfering data from array to + * read regs (tR) + * - options: various chip options. They can partly be set to inform + * nand_scan about special functionality. See the defines for further + * explanation + * Members with a "?" were not set in the merged testing-NAND branch, + * so they are not set here either. + */ +int board_nand_init(struct nand_chip *chip) +{ + const unsigned short pins[] = { + P_NAND_CE, P_NAND_RB, P_NAND_D0, P_NAND_D1, P_NAND_D2, + P_NAND_D3, P_NAND_D4, P_NAND_D5, P_NAND_D6, P_NAND_D7, + P_NAND_WE, P_NAND_RE, P_NAND_CLE, P_NAND_ALE, 0, + }; + + pr_stamp(); + + /* set width/ecc/timings/etc... */ + bfin_write_NFC_CTL(CONFIG_BFIN_NFC_CTL_VAL); + + /* clear interrupt status */ + bfin_write_NFC_IRQMASK(0x0); + bfin_write_NFC_IRQSTAT(0xffff); + + /* enable GPIO function enable register */ + peripheral_request_list(pins, "bfin_nand"); + + chip->cmd_ctrl = bfin_nfc_cmd_ctrl; + chip->read_buf = bfin_nfc_read_buf; + chip->write_buf = bfin_nfc_write_buf; + chip->read_byte = bfin_nfc_read_byte; + +#ifdef CONFIG_BFIN_NFC_NO_HW_ECC +# define ECC_HW 0 +#else +# define ECC_HW 1 +#endif + if (ECC_HW) { + if (BOOTROM_ECC) { + chip->badblock_pattern = &bootrom_bbt; + chip->ecc.layout = &bootrom_ecclayout; + } + if (!NAND_IS_512()) { + chip->ecc.bytes = 3; + chip->ecc.size = 256; + chip->ecc.strength = 1; + } else { + chip->ecc.bytes = 6; + chip->ecc.size = 512; + chip->ecc.strength = 2; + } + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.calculate = bfin_nfc_calculate_ecc; + chip->ecc.correct = bfin_nfc_correct_data; + chip->ecc.hwctl = bfin_nfc_enable_hwecc; + } else + chip->ecc.mode = NAND_ECC_SOFT; + chip->dev_ready = bfin_nfc_devready; + chip->chip_delay = 0; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/davinci_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/davinci_nand.c new file mode 100644 index 000000000..75b03a74b --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/davinci_nand.c @@ -0,0 +1,653 @@ +/* + * NAND driver for TI DaVinci based boards. + * + * Copyright (C) 2007 Sergey Kubushyn + * + * Based on Linux DaVinci NAND driver by TI. Original copyright follows: + */ + +/* + * + * linux/drivers/mtd/nand/nand_davinci.c + * + * NAND Flash Driver + * + * Copyright (C) 2006 Texas Instruments. + * + * ---------------------------------------------------------------------------- + * + * SPDX-License-Identifier: GPL-2.0+ + * + * ---------------------------------------------------------------------------- + * + * Overview: + * This is a device driver for the NAND flash device found on the + * DaVinci board which utilizes the Samsung k9k2g08 part. + * + Modifications: + ver. 1.0: Feb 2005, Vinod/Sudhakar + - + */ + +#include +#include +#include +#include +#include + +/* Definitions for 4-bit hardware ECC */ +#define NAND_TIMEOUT 10240 +#define NAND_ECC_BUSY 0xC +#define NAND_4BITECC_MASK 0x03FF03FF +#define EMIF_NANDFSR_ECC_STATE_MASK 0x00000F00 +#define ECC_STATE_NO_ERR 0x0 +#define ECC_STATE_TOO_MANY_ERRS 0x1 +#define ECC_STATE_ERR_CORR_COMP_P 0x2 +#define ECC_STATE_ERR_CORR_COMP_N 0x3 + +/* + * Exploit the little endianness of the ARM to do multi-byte transfers + * per device read. This can perform over twice as quickly as individual + * byte transfers when buffer alignment is conducive. + * + * NOTE: This only works if the NAND is not connected to the 2 LSBs of + * the address bus. On Davinci EVM platforms this has always been true. + */ +static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + const u32 *nand = chip->IO_ADDR_R; + + /* Make sure that buf is 32 bit aligned */ + if (((int)buf & 0x3) != 0) { + if (((int)buf & 0x1) != 0) { + if (len) { + *buf = readb(nand); + buf += 1; + len--; + } + } + + if (((int)buf & 0x3) != 0) { + if (len >= 2) { + *(u16 *)buf = readw(nand); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + *(u32 *)buf = __raw_readl(nand); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + *(u16 *)buf = readw(nand); + buf += 2; + len -= 2; + } + + if (len) + *buf = readb(nand); + } +} + +static void nand_davinci_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd->priv; + const u32 *nand = chip->IO_ADDR_W; + + /* Make sure that buf is 32 bit aligned */ + if (((int)buf & 0x3) != 0) { + if (((int)buf & 0x1) != 0) { + if (len) { + writeb(*buf, nand); + buf += 1; + len--; + } + } + + if (((int)buf & 0x3) != 0) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + __raw_writel(*(u32 *)buf, nand); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + + if (len) + writeb(*buf, nand); + } +} + +static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + u_int32_t IO_ADDR_W = (u_int32_t)this->IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + IO_ADDR_W &= ~(MASK_ALE|MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= MASK_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= MASK_ALE; + this->IO_ADDR_W = (void __iomem *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, IO_ADDR_W); +} + +#ifdef CONFIG_SYS_NAND_HW_ECC + +static u_int32_t nand_davinci_readecc(struct mtd_info *mtd) +{ + u_int32_t ecc = 0; + + ecc = __raw_readl(&(davinci_emif_regs->nandfecc[ + CONFIG_SYS_NAND_CS - 2])); + + return ecc; +} + +static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode) +{ + u_int32_t val; + + /* reading the ECC result register resets the ECC calculation */ + nand_davinci_readecc(mtd); + + val = __raw_readl(&davinci_emif_regs->nandfcr); + val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_1BIT_ECC_START(CONFIG_SYS_NAND_CS); + __raw_writel(val, &davinci_emif_regs->nandfcr); +} + +static int nand_davinci_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + u_int32_t tmp; + + tmp = nand_davinci_readecc(mtd); + + /* Squeeze 4 bytes ECC into 3 bytes by removing RESERVED bits + * and shifting. RESERVED bits are 31 to 28 and 15 to 12. */ + tmp = (tmp & 0x00000fff) | ((tmp & 0x0fff0000) >> 4); + + /* Invert so that erased block ECC is correct */ + tmp = ~tmp; + + *ecc_code++ = tmp; + *ecc_code++ = tmp >> 8; + *ecc_code++ = tmp >> 16; + + /* NOTE: the above code matches mainline Linux: + * .PQR.stu ==> ~PQRstu + * + * MontaVista/TI kernels encode those bytes differently, use + * complicated (and allegedly sometimes-wrong) correction code, + * and usually shipped with U-Boot that uses software ECC: + * .PQR.stu ==> PsQRtu + * + * If you need MV/TI compatible NAND I/O in U-Boot, it should + * be possible to (a) change the mangling above, (b) reverse + * that mangling in nand_davinci_correct_data() below. + */ + + return 0; +} + +static int nand_davinci_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *this = mtd->priv; + u_int32_t ecc_nand = read_ecc[0] | (read_ecc[1] << 8) | + (read_ecc[2] << 16); + u_int32_t ecc_calc = calc_ecc[0] | (calc_ecc[1] << 8) | + (calc_ecc[2] << 16); + u_int32_t diff = ecc_calc ^ ecc_nand; + + if (diff) { + if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { + /* Correctable error */ + if ((diff >> (12 + 3)) < this->ecc.size) { + uint8_t find_bit = 1 << ((diff >> 12) & 7); + uint32_t find_byte = diff >> (12 + 3); + + dat[find_byte] ^= find_bit; + MTDDEBUG(MTD_DEBUG_LEVEL0, "Correcting single " + "bit ECC error at offset: %d, bit: " + "%d\n", find_byte, find_bit); + return 1; + } else { + return -1; + } + } else if (!(diff & (diff - 1))) { + /* Single bit ECC error in the ECC itself, + nothing to fix */ + MTDDEBUG(MTD_DEBUG_LEVEL0, "Single bit ECC error in " + "ECC.\n"); + return 1; + } else { + /* Uncorrectable error */ + MTDDEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n"); + return -1; + } + } + return 0; +} +#endif /* CONFIG_SYS_NAND_HW_ECC */ + +#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST +static struct nand_ecclayout nand_davinci_4bit_layout_oobfirst = { +#if defined(CONFIG_SYS_NAND_PAGE_2K) + .eccbytes = 40, +#ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + }, + .oobfree = { + {2, 4}, {16, 6}, {32, 6}, {48, 6}, + }, +#else + .eccpos = { + 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, + }, + .oobfree = { + {.offset = 2, .length = 22, }, + }, +#endif /* #ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC */ +#elif defined(CONFIG_SYS_NAND_PAGE_4K) + .eccbytes = 80, + .eccpos = { + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + }, + .oobfree = { + {.offset = 2, .length = 46, }, + }, +#endif +}; + +static void nand_davinci_4bit_enable_hwecc(struct mtd_info *mtd, int mode) +{ + u32 val; + + switch (mode) { + case NAND_ECC_WRITE: + case NAND_ECC_READ: + /* + * Start a new ECC calculation for reading or writing 512 bytes + * of data. + */ + val = __raw_readl(&davinci_emif_regs->nandfcr); + val &= ~DAVINCI_NANDFCR_4BIT_ECC_SEL_MASK; + val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_4BIT_ECC_SEL(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_4BIT_ECC_START; + __raw_writel(val, &davinci_emif_regs->nandfcr); + break; + case NAND_ECC_READSYN: + val = __raw_readl(&davinci_emif_regs->nand4bitecc[0]); + break; + default: + break; + } +} + +static u32 nand_davinci_4bit_readecc(struct mtd_info *mtd, unsigned int ecc[4]) +{ + int i; + + for (i = 0; i < 4; i++) { + ecc[i] = __raw_readl(&davinci_emif_regs->nand4bitecc[i]) & + NAND_4BITECC_MASK; + } + + return 0; +} + +static int nand_davinci_4bit_calculate_ecc(struct mtd_info *mtd, + const uint8_t *dat, + uint8_t *ecc_code) +{ + unsigned int hw_4ecc[4]; + unsigned int i; + + nand_davinci_4bit_readecc(mtd, hw_4ecc); + + /*Convert 10 bit ecc value to 8 bit */ + for (i = 0; i < 2; i++) { + unsigned int hw_ecc_low = hw_4ecc[i * 2]; + unsigned int hw_ecc_hi = hw_4ecc[(i * 2) + 1]; + + /* Take first 8 bits from val1 (count1=0) or val5 (count1=1) */ + *ecc_code++ = hw_ecc_low & 0xFF; + + /* + * Take 2 bits as LSB bits from val1 (count1=0) or val5 + * (count1=1) and 6 bits from val2 (count1=0) or + * val5 (count1=1) + */ + *ecc_code++ = + ((hw_ecc_low >> 8) & 0x3) | ((hw_ecc_low >> 14) & 0xFC); + + /* + * Take 4 bits from val2 (count1=0) or val5 (count1=1) and + * 4 bits from val3 (count1=0) or val6 (count1=1) + */ + *ecc_code++ = + ((hw_ecc_low >> 22) & 0xF) | ((hw_ecc_hi << 4) & 0xF0); + + /* + * Take 6 bits from val3(count1=0) or val6 (count1=1) and + * 2 bits from val4 (count1=0) or val7 (count1=1) + */ + *ecc_code++ = + ((hw_ecc_hi >> 4) & 0x3F) | ((hw_ecc_hi >> 10) & 0xC0); + + /* Take 8 bits from val4 (count1=0) or val7 (count1=1) */ + *ecc_code++ = (hw_ecc_hi >> 18) & 0xFF; + } + + return 0; +} + +static int nand_davinci_4bit_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + int i; + unsigned int hw_4ecc[4]; + unsigned int iserror; + unsigned short *ecc16; + unsigned int numerrors, erroraddress, errorvalue; + u32 val; + + /* + * Check for an ECC where all bytes are 0xFF. If this is the case, we + * will assume we are looking at an erased page and we should ignore + * the ECC. + */ + for (i = 0; i < 10; i++) { + if (read_ecc[i] != 0xFF) + break; + } + if (i == 10) + return 0; + + /* Convert 8 bit in to 10 bit */ + ecc16 = (unsigned short *)&read_ecc[0]; + + /* + * Write the parity values in the NAND Flash 4-bit ECC Load register. + * Write each parity value one at a time starting from 4bit_ecc_val8 + * to 4bit_ecc_val1. + */ + + /*Take 2 bits from 8th byte and 8 bits from 9th byte */ + __raw_writel(((ecc16[4]) >> 6) & 0x3FF, + &davinci_emif_regs->nand4biteccload); + + /* Take 4 bits from 7th byte and 6 bits from 8th byte */ + __raw_writel((((ecc16[3]) >> 12) & 0xF) | ((((ecc16[4])) << 4) & 0x3F0), + &davinci_emif_regs->nand4biteccload); + + /* Take 6 bits from 6th byte and 4 bits from 7th byte */ + __raw_writel((ecc16[3] >> 2) & 0x3FF, + &davinci_emif_regs->nand4biteccload); + + /* Take 8 bits from 5th byte and 2 bits from 6th byte */ + __raw_writel(((ecc16[2]) >> 8) | ((((ecc16[3])) << 8) & 0x300), + &davinci_emif_regs->nand4biteccload); + + /*Take 2 bits from 3rd byte and 8 bits from 4th byte */ + __raw_writel((((ecc16[1]) >> 14) & 0x3) | ((((ecc16[2])) << 2) & 0x3FC), + &davinci_emif_regs->nand4biteccload); + + /* Take 4 bits form 2nd bytes and 6 bits from 3rd bytes */ + __raw_writel(((ecc16[1]) >> 4) & 0x3FF, + &davinci_emif_regs->nand4biteccload); + + /* Take 6 bits from 1st byte and 4 bits from 2nd byte */ + __raw_writel((((ecc16[0]) >> 10) & 0x3F) | (((ecc16[1]) << 6) & 0x3C0), + &davinci_emif_regs->nand4biteccload); + + /* Take 10 bits from 0th and 1st bytes */ + __raw_writel((ecc16[0]) & 0x3FF, + &davinci_emif_regs->nand4biteccload); + + /* + * Perform a dummy read to the EMIF Revision Code and Status register. + * This is required to ensure time for syndrome calculation after + * writing the ECC values in previous step. + */ + + val = __raw_readl(&davinci_emif_regs->nandfsr); + + /* + * Read the syndrome from the NAND Flash 4-Bit ECC 1-4 registers. + * A syndrome value of 0 means no bit errors. If the syndrome is + * non-zero then go further otherwise return. + */ + nand_davinci_4bit_readecc(mtd, hw_4ecc); + + if (!(hw_4ecc[0] | hw_4ecc[1] | hw_4ecc[2] | hw_4ecc[3])) + return 0; + + /* + * Clear any previous address calculation by doing a dummy read of an + * error address register. + */ + val = __raw_readl(&davinci_emif_regs->nanderradd1); + + /* + * Set the addr_calc_st bit(bit no 13) in the NAND Flash Control + * register to 1. + */ + __raw_writel(DAVINCI_NANDFCR_4BIT_CALC_START, + &davinci_emif_regs->nandfcr); + + /* + * Wait for the corr_state field (bits 8 to 11) in the + * NAND Flash Status register to be not equal to 0x0, 0x1, 0x2, or 0x3. + * Otherwise ECC calculation has not even begun and the next loop might + * fail because of a false positive! + */ + i = NAND_TIMEOUT; + do { + val = __raw_readl(&davinci_emif_regs->nandfsr); + val &= 0xc00; + i--; + } while ((i > 0) && !val); + + /* + * Wait for the corr_state field (bits 8 to 11) in the + * NAND Flash Status register to be equal to 0x0, 0x1, 0x2, or 0x3. + */ + i = NAND_TIMEOUT; + do { + val = __raw_readl(&davinci_emif_regs->nandfsr); + val &= 0xc00; + i--; + } while ((i > 0) && val); + + iserror = __raw_readl(&davinci_emif_regs->nandfsr); + iserror &= EMIF_NANDFSR_ECC_STATE_MASK; + iserror = iserror >> 8; + + /* + * ECC_STATE_TOO_MANY_ERRS (0x1) means errors cannot be + * corrected (five or more errors). The number of errors + * calculated (err_num field) differs from the number of errors + * searched. ECC_STATE_ERR_CORR_COMP_P (0x2) means error + * correction complete (errors on bit 8 or 9). + * ECC_STATE_ERR_CORR_COMP_N (0x3) means error correction + * complete (error exists). + */ + + if (iserror == ECC_STATE_NO_ERR) { + val = __raw_readl(&davinci_emif_regs->nanderrval1); + return 0; + } else if (iserror == ECC_STATE_TOO_MANY_ERRS) { + val = __raw_readl(&davinci_emif_regs->nanderrval1); + return -1; + } + + numerrors = ((__raw_readl(&davinci_emif_regs->nandfsr) >> 16) + & 0x3) + 1; + + /* Read the error address, error value and correct */ + for (i = 0; i < numerrors; i++) { + if (i > 1) { + erroraddress = + ((__raw_readl(&davinci_emif_regs->nanderradd2) >> + (16 * (i & 1))) & 0x3FF); + erroraddress = ((512 + 7) - erroraddress); + errorvalue = + ((__raw_readl(&davinci_emif_regs->nanderrval2) >> + (16 * (i & 1))) & 0xFF); + } else { + erroraddress = + ((__raw_readl(&davinci_emif_regs->nanderradd1) >> + (16 * (i & 1))) & 0x3FF); + erroraddress = ((512 + 7) - erroraddress); + errorvalue = + ((__raw_readl(&davinci_emif_regs->nanderrval1) >> + (16 * (i & 1))) & 0xFF); + } + /* xor the corrupt data with error value */ + if (erroraddress < 512) + dat[erroraddress] ^= errorvalue; + } + + return numerrors; +} +#endif /* CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST */ + +static int nand_davinci_dev_ready(struct mtd_info *mtd) +{ + return __raw_readl(&davinci_emif_regs->nandfsr) & 0x1; +} + +static void nand_flash_init(void) +{ + /* This is for DM6446 EVM and *very* similar. DO NOT GROW THIS! + * Instead, have your board_init() set EMIF timings, based on its + * knowledge of the clocks and what devices are hooked up ... and + * don't even do that unless no UBL handled it. + */ +#ifdef CONFIG_SOC_DM644X + u_int32_t acfg1 = 0x3ffffffc; + + /*------------------------------------------------------------------* + * NAND FLASH CHIP TIMEOUT @ 459 MHz * + * * + * AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz * + * AEMIF.CLK period = 1/76.5 MHz = 13.1 ns * + * * + *------------------------------------------------------------------*/ + acfg1 = 0 + | (0 << 31) /* selectStrobe */ + | (0 << 30) /* extWait */ + | (1 << 26) /* writeSetup 10 ns */ + | (3 << 20) /* writeStrobe 40 ns */ + | (1 << 17) /* writeHold 10 ns */ + | (1 << 13) /* readSetup 10 ns */ + | (5 << 7) /* readStrobe 60 ns */ + | (1 << 4) /* readHold 10 ns */ + | (3 << 2) /* turnAround ?? ns */ + | (0 << 0) /* asyncSize 8-bit bus */ + ; + + __raw_writel(acfg1, &davinci_emif_regs->ab1cr); /* CS2 */ + + /* NAND flash on CS2 */ + __raw_writel(0x00000101, &davinci_emif_regs->nandfcr); +#endif +} + +void davinci_nand_init(struct nand_chip *nand) +{ + nand->chip_delay = 0; +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + nand->bbt_options |= NAND_BBT_USE_FLASH; +#endif +#ifdef CONFIG_SYS_NAND_NO_SUBPAGE_WRITE + nand->options |= NAND_NO_SUBPAGE_WRITE; +#endif +#ifdef CONFIG_SYS_NAND_HW_ECC + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.strength = 1; + nand->ecc.calculate = nand_davinci_calculate_ecc; + nand->ecc.correct = nand_davinci_correct_data; + nand->ecc.hwctl = nand_davinci_enable_hwecc; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif /* CONFIG_SYS_NAND_HW_ECC */ +#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST + nand->ecc.mode = NAND_ECC_HW_OOB_FIRST; + nand->ecc.size = 512; + nand->ecc.bytes = 10; + nand->ecc.strength = 4; + nand->ecc.calculate = nand_davinci_4bit_calculate_ecc; + nand->ecc.correct = nand_davinci_4bit_correct_data; + nand->ecc.hwctl = nand_davinci_4bit_enable_hwecc; + nand->ecc.layout = &nand_davinci_4bit_layout_oobfirst; +#endif + /* Set address of hardware control function */ + nand->cmd_ctrl = nand_davinci_hwcontrol; + + nand->read_buf = nand_davinci_read_buf; + nand->write_buf = nand_davinci_write_buf; + + nand->dev_ready = nand_davinci_dev_ready; + + nand_flash_init(); +} + +int board_nand_init(struct nand_chip *chip) __attribute__((weak)); + +int board_nand_init(struct nand_chip *chip) +{ + davinci_nand_init(chip); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/docg4.c b/qemu/roms/u-boot/drivers/mtd/nand/docg4.c new file mode 100644 index 000000000..b9121c397 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/docg4.c @@ -0,0 +1,1028 @@ +/* + * drivers/mtd/nand/docg4.c + * + * Copyright (C) 2013 Mike Dunn + * + * SPDX-License-Identifier: GPL-2.0+ + * + * mtd nand driver for M-Systems DiskOnChip G4 + * + * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus + * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others. + * Should work on these as well. Let me know! + * + * TODO: + * + * Mechanism for management of password-protected areas + * + * Hamming ecc when reading oob only + * + * According to the M-Sys documentation, this device is also available in a + * "dual-die" configuration having a 256MB capacity, but no mechanism for + * detecting this variant is documented. Currently this driver assumes 128MB + * capacity. + * + * Support for multiple cascaded devices ("floors"). Not sure which gadgets + * contain multiple G4s in a cascaded configuration, if any. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The device has a nop register which M-Sys claims is for the purpose of + * inserting precise delays. But beware; at least some operations fail if the + * nop writes are replaced with a generic delay! + */ +static inline void write_nop(void __iomem *docptr) +{ + writew(0, docptr + DOC_NOP); +} + + +static int poll_status(void __iomem *docptr) +{ + /* + * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL + * register. Operations known to take a long time (e.g., block erase) + * should sleep for a while before calling this. + */ + + uint8_t flash_status; + + /* hardware quirk requires reading twice initially */ + flash_status = readb(docptr + DOC_FLASHCONTROL); + + do { + flash_status = readb(docptr + DOC_FLASHCONTROL); + } while (!(flash_status & DOC_CTRL_FLASHREADY)); + + return 0; +} + +static void write_addr(void __iomem *docptr, uint32_t docg4_addr) +{ + /* write the four address bytes packed in docg4_addr to the device */ + + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +} + +/* + * This is a module parameter in the linux kernel version of this driver. It is + * hard-coded to 'off' for u-boot. This driver uses oob to mark bad blocks. + * This can be problematic when dealing with data not intended for the mtd/nand + * subsystem. For example, on boards that boot from the docg4 and use the IPL + * to load an spl + u-boot image, the blocks containing the image will be + * reported as "bad" because the oob of the first page of each block contains a + * magic number that the IPL looks for, which causes the badblock scan to + * erroneously add them to the bad block table. To erase such a block, use + * u-boot's 'nand scrub'. scrub is safe for the docg4. The device does have a + * factory bad block table, but it is read-only, and is used in conjunction with + * oob bad block markers that are written by mtd/nand when a block is deemed to + * be bad. To read data from "bad" blocks, use 'read.raw'. Unfortunately, + * read.raw does not use ecc, which would still work fine on such misidentified + * bad blocks. TODO: u-boot nand utilities need the ability to ignore bad + * blocks. + */ +static const int ignore_badblocks; /* remains false */ + +struct docg4_priv { + int status; + struct { + unsigned int command; + int column; + int page; + } last_command; + uint8_t oob_buf[16]; + uint8_t ecc_buf[7]; + int oob_page; + struct bch_control *bch; +}; +/* + * Oob bytes 0 - 6 are available to the user. + * Byte 7 is hamming ecc for first 7 bytes. Bytes 8 - 14 are hw-generated ecc. + * Byte 15 (the last) is used by the driver as a "page written" flag. + */ +static struct nand_ecclayout docg4_oobinfo = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 7, + .oobfree = { {0, 7} } +}; + +static void reset(void __iomem *docptr) +{ + /* full device reset */ + + writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN, docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + write_nop(docptr); + + writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + + writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1); + + poll_status(docptr); +} + +static void docg4_select_chip(struct mtd_info *mtd, int chip) +{ + /* + * Select among multiple cascaded chips ("floors"). Multiple floors are + * not yet supported, so the only valid non-negative value is 0. + */ + void __iomem *docptr = CONFIG_SYS_NAND_BASE; + + if (chip < 0) + return; /* deselected */ + + if (chip > 0) + printf("multiple floors currently unsupported\n"); + + writew(0, docptr + DOC_DEVICESELECT); +} + +static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf) +{ + /* read the 7 hw-generated ecc bytes */ + + int i; + for (i = 0; i < 7; i++) { /* hw quirk; read twice */ + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + } +} + +static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + /* + * Called after a page read when hardware reports bitflips. + * Up to four bitflips can be corrected. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = CONFIG_SYS_NAND_BASE; + int i, numerrs; + unsigned int errpos[4]; + const uint8_t blank_read_hwecc[8] = { + 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 }; + + read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */ + + /* check if read error is due to a blank page */ + if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7)) + return 0; /* yes */ + + /* skip additional check of "written flag" if ignore_badblocks */ + if (!ignore_badblocks) { + /* + * If the hw ecc bytes are not those of a blank page, there's + * still a chance that the page is blank, but was read with + * errors. Check the "written flag" in last oob byte, which + * is set to zero when a page is written. If more than half + * the bits are set, assume a blank page. Unfortunately, the + * bit flips(s) are not reported in stats. + */ + + if (doc->oob_buf[15]) { + int bit, numsetbits = 0; + unsigned long written_flag = doc->oob_buf[15]; + + for (bit = 0; bit < 8; bit++) { + if (written_flag & 0x01) + numsetbits++; + written_flag >>= 1; + } + if (numsetbits > 4) { /* assume blank */ + printf("errors in blank page at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return 0; + } + } + } + + /* + * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch + * algorithm is used to decode this. However the hw operates on page + * data in a bit order that is the reverse of that of the bch alg, + * requiring that the bits be reversed on the result. Thanks to Ivan + * Djelic for his analysis! + */ + for (i = 0; i < 7; i++) + doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]); + + numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL, + doc->ecc_buf, NULL, errpos); + + if (numerrs == -EBADMSG) { + printf("uncorrectable errors at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return -EBADMSG; + } + + BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */ + + /* undo last step in BCH alg (modulo mirroring not needed) */ + for (i = 0; i < numerrs; i++) + errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7)); + + /* fix the errors */ + for (i = 0; i < numerrs; i++) { + /* ignore if error within oob ecc bytes */ + if (errpos[i] > DOCG4_USERDATA_LEN * 8) + continue; + + /* if error within oob area preceeding ecc bytes... */ + if (errpos[i] > DOCG4_PAGE_SIZE * 8) + __change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8, + (unsigned long *)doc->oob_buf); + + else /* error in page data */ + __change_bit(errpos[i], (unsigned long *)buf); + } + + printf("%d error(s) corrected at offset %08x\n", + numerrs, page * DOCG4_PAGE_SIZE); + + return numerrs; +} + +static int read_progstatus(struct docg4_priv *doc, void __iomem *docptr) +{ + /* + * This apparently checks the status of programming. Done after an + * erasure, and after page data is written. On error, the status is + * saved, to be later retrieved by the nand infrastructure code. + */ + + /* status is read from the I/O reg */ + uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG); + + MTDDEBUG(MTD_DEBUG_LEVEL3, "docg4: %s: %02x %02x %02x\n", + __func__, status1, status2, status3); + + if (status1 != DOCG4_PROGSTATUS_GOOD || + status2 != DOCG4_PROGSTATUS_GOOD_2 || + status3 != DOCG4_PROGSTATUS_GOOD_2) { + doc->status = NAND_STATUS_FAIL; + printf("read_progstatus failed: %02x, %02x, %02x\n", + status1, status2, status3); + return -EIO; + } + return 0; +} + +static int pageprog(struct mtd_info *mtd) +{ + /* + * Final step in writing a page. Writes the contents of its + * internal buffer out to the flash array, or some such. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = CONFIG_SYS_NAND_BASE; + int retval = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "docg4: %s\n", __func__); + + writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + /* Just busy-wait; usleep_range() slows things down noticeably. */ + poll_status(docptr); + + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + retval = read_progstatus(doc, docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(docptr); + write_nop(docptr); + + return retval; +} + +static void sequence_reset(void __iomem *docptr) +{ + /* common starting sequence for all operations */ + + writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL); + writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + poll_status(docptr); + write_nop(docptr); +} + +static void read_page_prologue(void __iomem *docptr, uint32_t docg4_addr) +{ + /* first step in reading a page */ + + sequence_reset(docptr); + + writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + write_addr(docptr, docg4_addr); + + write_nop(docptr); + writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + poll_status(docptr); +} + +static void write_page_prologue(void __iomem *docptr, uint32_t docg4_addr) +{ + /* first step in writing a page */ + + sequence_reset(docptr); + writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_addr(docptr, docg4_addr); + write_nop(docptr); + write_nop(docptr); + poll_status(docptr); +} + +static uint32_t mtd_to_docg4_address(int page, int column) +{ + /* + * Convert mtd address to format used by the device, 32 bit packed. + * + * Some notes on G4 addressing... The M-Sys documentation on this device + * claims that pages are 2K in length, and indeed, the format of the + * address used by the device reflects that. But within each page are + * four 512 byte "sub-pages", each with its own oob data that is + * read/written immediately after the 512 bytes of page data. This oob + * data contains the ecc bytes for the preceeding 512 bytes. + * + * Rather than tell the mtd nand infrastructure that page size is 2k, + * with four sub-pages each, we engage in a little subterfuge and tell + * the infrastructure code that pages are 512 bytes in size. This is + * done because during the course of reverse-engineering the device, I + * never observed an instance where an entire 2K "page" was read or + * written as a unit. Each "sub-page" is always addressed individually, + * its data read/written, and ecc handled before the next "sub-page" is + * addressed. + * + * This requires us to convert addresses passed by the mtd nand + * infrastructure code to those used by the device. + * + * The address that is written to the device consists of four bytes: the + * first two are the 2k page number, and the second is the index into + * the page. The index is in terms of 16-bit half-words and includes + * the preceeding oob data, so e.g., the index into the second + * "sub-page" is 0x108, and the full device address of the start of mtd + * page 0x201 is 0x00800108. + */ + int g4_page = page / 4; /* device's 2K page */ + int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */ + return (g4_page << 16) | g4_index; /* pack */ +} + +static void docg4_command(struct mtd_info *mtd, unsigned command, int column, + int page_addr) +{ + /* handle standard nand commands */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + uint32_t g4_addr = mtd_to_docg4_address(page_addr, column); + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s %x, page_addr=%x, column=%x\n", + __func__, command, page_addr, column); + + /* + * Save the command and its arguments. This enables emulation of + * standard flash devices, and also some optimizations. + */ + doc->last_command.command = command; + doc->last_command.column = column; + doc->last_command.page = page_addr; + + switch (command) { + case NAND_CMD_RESET: + reset(CONFIG_SYS_NAND_BASE); + break; + + case NAND_CMD_READ0: + read_page_prologue(CONFIG_SYS_NAND_BASE, g4_addr); + break; + + case NAND_CMD_STATUS: + /* next call to read_byte() will expect a status */ + break; + + case NAND_CMD_SEQIN: + write_page_prologue(CONFIG_SYS_NAND_BASE, g4_addr); + + /* hack for deferred write of oob bytes */ + if (doc->oob_page == page_addr) + memcpy(nand->oob_poi, doc->oob_buf, 16); + break; + + case NAND_CMD_PAGEPROG: + pageprog(mtd); + break; + + /* we don't expect these, based on review of nand_base.c */ + case NAND_CMD_READOOB: + case NAND_CMD_READID: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + printf("docg4_command: unexpected nand command 0x%x\n", + command); + break; + } +} + +static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd->priv; + uint16_t *p = (uint16_t *)buf; + len >>= 1; + + for (i = 0; i < len; i++) + p[i] = readw(nand->IO_ADDR_R); +} + +static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = CONFIG_SYS_NAND_BASE; + uint16_t status; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: page %x\n", __func__, page); + + /* + * Oob bytes are read as part of a normal page read. If the previous + * nand command was a read of the page whose oob is now being read, just + * copy the oob bytes that we saved in a local buffer and avoid a + * separate oob read. + */ + if (doc->last_command.command == NAND_CMD_READ0 && + doc->last_command.page == page) { + memcpy(nand->oob_poi, doc->oob_buf, 16); + return 0; + } + + /* + * Separate read of oob data only. + */ + docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page); + + writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is oob data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + printf("docg4_read_oob failed: status = 0x%02x\n", status); + return -EIO; + } + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: status = 0x%x\n", __func__, status); + + docg4_read_buf(mtd, nand->oob_poi, 16); + + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + + return 0; +} + +static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + /* + * Writing oob-only is not really supported, because MLC nand must write + * oob bytes at the same time as page data. Nonetheless, we save the + * oob buffer contents here, and then write it along with the page data + * if the same page is subsequently written. This allows user space + * utilities that write the oob data prior to the page data to work + * (e.g., nandwrite). The disdvantage is that, if the intention was to + * write oob only, the operation is quietly ignored. Also, oob can get + * corrupted if two concurrent processes are running nandwrite. + */ + + /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */ + struct docg4_priv *doc = nand->priv; + doc->oob_page = page; + memcpy(doc->oob_buf, nand->oob_poi, 16); + return 0; +} + +static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + /* only called when module_param ignore_badblocks is set */ + return 0; +} + +static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd->priv; + uint16_t *p = (uint16_t *)buf; + len >>= 1; + + for (i = 0; i < len; i++) + writew(p[i], nand->IO_ADDR_W); +} + +static int write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, int use_ecc) +{ + void __iomem *docptr = CONFIG_SYS_NAND_BASE; + uint8_t ecc_buf[8]; + + writew(DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + + /* write the page data */ + docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE); + + /* oob bytes 0 through 5 are written to I/O reg */ + docg4_write_buf16(mtd, nand->oob_poi, 6); + + /* oob byte 6 written to a separate reg */ + writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7); + + write_nop(docptr); + write_nop(docptr); + + /* write hw-generated ecc bytes to oob */ + if (likely(use_ecc)) { + /* oob byte 7 is hamming code */ + uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY); + hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */ + writew(hamming, docptr + DOCG4_OOB_6_7); + write_nop(docptr); + + /* read the 7 bch bytes from ecc regs */ + read_hw_ecc(docptr, ecc_buf); + ecc_buf[7] = 0; /* clear the "page written" flag */ + } + + /* write user-supplied bytes to oob */ + else { + writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7); + write_nop(docptr); + memcpy(ecc_buf, &nand->oob_poi[8], 8); + } + + docg4_write_buf16(mtd, ecc_buf, 8); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + + return 0; +} + +static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, int oob_required) +{ + return write_page(mtd, nand, buf, 0); +} + +static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, int oob_required) +{ + return write_page(mtd, nand, buf, 1); +} + +static int read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page, int use_ecc) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = CONFIG_SYS_NAND_BASE; + uint16_t status, edc_err, *buf16; + + writew(DOC_ECCCONF0_READ_MODE | + DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is page data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + printf("docg4_read_page: bad status: 0x%02x\n", status); + writew(0, docptr + DOC_DATAEND); + return -EIO; + } + + docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */ + + /* first 14 oob bytes read from I/O reg */ + docg4_read_buf(mtd, nand->oob_poi, 14); + + /* last 2 read from another reg */ + buf16 = (uint16_t *)(nand->oob_poi + 14); + *buf16 = readw(docptr + DOCG4_MYSTERY_REG); + + /* + * Diskonchips read oob immediately after a page read. Mtd + * infrastructure issues a separate command for reading oob after the + * page is read. So we save the oob bytes in a local buffer and just + * copy it if the next command reads oob from the same page. + */ + memcpy(doc->oob_buf, nand->oob_poi, 16); + + write_nop(docptr); + + if (likely(use_ecc)) { + /* read the register that tells us if bitflip(s) detected */ + edc_err = readw(docptr + DOC_ECCCONF1); + edc_err = readw(docptr + DOC_ECCCONF1); + + /* If bitflips are reported, attempt to correct with ecc */ + if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { + int bits_corrected = correct_data(mtd, buf, page); + if (bits_corrected == -EBADMSG) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += bits_corrected; + } + } + + writew(0, docptr + DOC_DATAEND); + return 0; +} + + +static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, int page) +{ + return read_page(mtd, nand, buf, page, 0); +} + +static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, int page) +{ + return read_page(mtd, nand, buf, page, 1); +} + +static void docg4_erase_block(struct mtd_info *mtd, int page) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = CONFIG_SYS_NAND_BASE; + uint16_t g4_page; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: page %04x\n", __func__, page); + + sequence_reset(docptr); + + writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + /* only 2 bytes of address are written to specify erase block */ + g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */ + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + g4_page >>= 8; + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + write_nop(docptr); + + /* start the erasure */ + writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + poll_status(docptr); + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + read_progstatus(doc, docptr); + + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(docptr); + write_nop(docptr); +} + +static int read_factory_bbt(struct mtd_info *mtd) +{ + /* + * The device contains a read-only factory bad block table. Read it and + * update the memory-based bbt accordingly. + */ + + struct nand_chip *nand = mtd->priv; + uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); + uint8_t *buf; + int i, block, status; + + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + read_page_prologue(CONFIG_SYS_NAND_BASE, g4_addr); + status = docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); + if (status) + goto exit; + + /* + * If no memory-based bbt was created, exit. This will happen if module + * parameter ignore_badblocks is set. Then why even call this function? + * For an unknown reason, block erase always fails if it's the first + * operation after device power-up. The above read ensures it never is. + * Ugly, I know. + */ + if (nand->bbt == NULL) /* no memory-based bbt */ + goto exit; + + /* + * Parse factory bbt and update memory-based bbt. Factory bbt format is + * simple: one bit per block, block numbers increase left to right (msb + * to lsb). Bit clear means bad block. + */ + for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) { + int bitnum; + uint8_t mask; + for (bitnum = 0, mask = 0x80; + bitnum < 8; bitnum++, mask >>= 1) { + if (!(buf[i] & mask)) { + int badblock = block + bitnum; + nand->bbt[badblock / 4] |= + 0x03 << ((badblock % 4) * 2); + mtd->ecc_stats.badblocks++; + printf("factory-marked bad block: %d\n", + badblock); + } + } + } + exit: + kfree(buf); + return status; +} + +static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + /* + * Mark a block as bad. Bad blocks are marked in the oob area of the + * first page of the block. The default scan_bbt() in the nand + * infrastructure code works fine for building the memory-based bbt + * during initialization, as does the nand infrastructure function that + * checks if a block is bad by reading the bbt. This function replaces + * the nand default because writes to oob-only are not supported. + */ + + int ret, i; + uint8_t *buf; + struct nand_chip *nand = mtd->priv; + struct nand_bbt_descr *bbtd = nand->badblock_pattern; + int block = (int)(ofs >> nand->bbt_erase_shift); + int page = (int)(ofs >> nand->page_shift); + uint32_t g4_addr = mtd_to_docg4_address(page, 0); + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: %08llx\n", __func__, ofs); + + if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1))) + printf("%s: ofs %llx not start of block!\n", + __func__, ofs); + + /* allocate blank buffer for page data */ + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + /* update bbt in memory */ + nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2); + + /* write bit-wise negation of pattern to oob buffer */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + for (i = 0; i < bbtd->len; i++) + nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i]; + + /* write first page of block */ + write_page_prologue(CONFIG_SYS_NAND_BASE, g4_addr); + docg4_write_page(mtd, nand, buf, 1); + ret = pageprog(mtd); + if (!ret) + mtd->ecc_stats.badblocks++; + + kfree(buf); + + return ret; +} + +static uint8_t docg4_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s\n", __func__); + + if (doc->last_command.command == NAND_CMD_STATUS) { + int status; + + /* + * Previous nand command was status request, so nand + * infrastructure code expects to read the status here. If an + * error occurred in a previous operation, report it. + */ + doc->last_command.command = 0; + + if (doc->status) { + status = doc->status; + doc->status = 0; + } + + /* why is NAND_STATUS_WP inverse logic?? */ + else + status = NAND_STATUS_WP | NAND_STATUS_READY; + + return status; + } + + printf("unexpectd call to read_byte()\n"); + + return 0; +} + +static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand) +{ + struct docg4_priv *doc = nand->priv; + int status = NAND_STATUS_WP; /* inverse logic?? */ + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s...\n", __func__); + + /* report any previously unreported error */ + if (doc->status) { + status |= doc->status; + doc->status = 0; + return status; + } + + status |= poll_status(CONFIG_SYS_NAND_BASE); + return status; +} + +int docg4_nand_init(struct mtd_info *mtd, struct nand_chip *nand, int devnum) +{ + uint16_t id1, id2; + struct docg4_priv *docg4; + int retval; + + docg4 = kzalloc(sizeof(*docg4), GFP_KERNEL); + if (!docg4) + return -1; + + mtd->priv = nand; + nand->priv = docg4; + + /* These must be initialized here because the docg4 is non-standard + * and doesn't produce an id that the nand code can use to look up + * these values (nand_scan_ident() not called). + */ + mtd->size = DOCG4_CHIP_SIZE; + mtd->name = "Msys_Diskonchip_G4"; + mtd->writesize = DOCG4_PAGE_SIZE; + mtd->erasesize = DOCG4_BLOCK_SIZE; + mtd->oobsize = DOCG4_OOB_SIZE; + + nand->IO_ADDR_R = + (void __iomem *)CONFIG_SYS_NAND_BASE + DOC_IOSPACE_DATA; + nand->IO_ADDR_W = nand->IO_ADDR_R; + nand->chipsize = DOCG4_CHIP_SIZE; + nand->chip_shift = DOCG4_CHIP_SHIFT; + nand->bbt_erase_shift = DOCG4_ERASE_SHIFT; + nand->phys_erase_shift = DOCG4_ERASE_SHIFT; + nand->chip_delay = 20; + nand->page_shift = DOCG4_PAGE_SHIFT; + nand->pagemask = 0x3ffff; + nand->badblockpos = NAND_LARGE_BADBLOCK_POS; + nand->badblockbits = 8; + nand->ecc.layout = &docg4_oobinfo; + nand->ecc.mode = NAND_ECC_HW_SYNDROME; + nand->ecc.size = DOCG4_PAGE_SIZE; + nand->ecc.prepad = 8; + nand->ecc.bytes = 8; + nand->ecc.strength = DOCG4_T; + nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; + nand->controller = &nand->hwcontrol; + + /* methods */ + nand->cmdfunc = docg4_command; + nand->waitfunc = docg4_wait; + nand->select_chip = docg4_select_chip; + nand->read_byte = docg4_read_byte; + nand->block_markbad = docg4_block_markbad; + nand->read_buf = docg4_read_buf; + nand->write_buf = docg4_write_buf16; + nand->scan_bbt = nand_default_bbt; + nand->erase_cmd = docg4_erase_block; + nand->ecc.read_page = docg4_read_page; + nand->ecc.write_page = docg4_write_page; + nand->ecc.read_page_raw = docg4_read_page_raw; + nand->ecc.write_page_raw = docg4_write_page_raw; + nand->ecc.read_oob = docg4_read_oob; + nand->ecc.write_oob = docg4_write_oob; + + /* + * The way the nand infrastructure code is written, a memory-based bbt + * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt, + * nand->block_bad() is used. So when ignoring bad blocks, we skip the + * scan and define a dummy block_bad() which always returns 0. + */ + if (ignore_badblocks) { + nand->options |= NAND_SKIP_BBTSCAN; + nand->block_bad = docg4_block_neverbad; + } + + reset(CONFIG_SYS_NAND_BASE); + + /* check for presence of g4 chip by reading id registers */ + id1 = readw(CONFIG_SYS_NAND_BASE + DOC_CHIPID); + id1 = readw(CONFIG_SYS_NAND_BASE + DOCG4_MYSTERY_REG); + id2 = readw(CONFIG_SYS_NAND_BASE + DOC_CHIPID_INV); + id2 = readw(CONFIG_SYS_NAND_BASE + DOCG4_MYSTERY_REG); + if (id1 != DOCG4_IDREG1_VALUE || id2 != DOCG4_IDREG2_VALUE) + return -1; + + /* initialize bch algorithm */ + docg4->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); + if (docg4->bch == NULL) + return -1; + + retval = nand_scan_tail(mtd); + if (retval) + return -1; + + /* + * Scan for bad blocks and create bbt here, then add the factory-marked + * bad blocks to the bbt. + */ + nand->scan_bbt(mtd); + nand->options |= NAND_BBT_SCANNED; + retval = read_factory_bbt(mtd); + if (retval) + return -1; + + retval = nand_register(devnum); + if (retval) + return -1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/docg4_spl.c b/qemu/roms/u-boot/drivers/mtd/nand/docg4_spl.c new file mode 100644 index 000000000..351b75a09 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/docg4_spl.c @@ -0,0 +1,219 @@ +/* + * SPL driver for Diskonchip G4 nand flash + * + * Copyright (C) 2013 Mike Dunn + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This driver basically mimics the load functionality of a typical IPL (initial + * program loader) resident in the 2k NOR-like region of the docg4 that is + * mapped to the reset vector. It allows the u-boot SPL to continue loading if + * the IPL loads a fixed number of flash blocks that is insufficient to contain + * the entire u-boot image. In this case, a concatenated spl + u-boot image is + * written at the flash offset from which the IPL loads an image, and when the + * IPL jumps to the SPL, the SPL resumes loading where the IPL left off. See + * the palmtreo680 for an example. + * + * This driver assumes that the data was written to the flash using the device's + * "reliable" mode, and also assumes that each 512 byte page is stored + * redundantly in the subsequent page. This storage format is likely to be used + * by all boards that boot from the docg4. The format compensates for the lack + * of ecc in the IPL. + * + * Reliable mode reduces the capacity of a block by half, and the redundant + * pages reduce it by half again. As a result, the normal 256k capacity of a + * block is reduced to 64k for the purposes of the IPL/SPL. + */ + +#include +#include + +/* forward declarations */ +static inline void write_nop(void __iomem *docptr); +static int poll_status(void __iomem *docptr); +static void write_addr(void __iomem *docptr, uint32_t docg4_addr); +static void address_sequence(unsigned int g4_page, unsigned int g4_index, + void __iomem *docptr); +static int docg4_load_block_reliable(uint32_t flash_offset, void *dest_addr); + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + void *load_addr = dst; + uint32_t flash_offset = offs; + const unsigned int block_count = + (size + DOCG4_BLOCK_CAPACITY_SPL - 1) + / DOCG4_BLOCK_CAPACITY_SPL; + int i; + + for (i = 0; i < block_count; i++) { + int ret = docg4_load_block_reliable(flash_offset, load_addr); + if (ret) + return ret; + load_addr += DOCG4_BLOCK_CAPACITY_SPL; + flash_offset += DOCG4_BLOCK_SIZE; + } + return 0; +} + +static inline void write_nop(void __iomem *docptr) +{ + writew(0, docptr + DOC_NOP); +} + +static int poll_status(void __iomem *docptr) +{ + /* + * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL + * register. Operations known to take a long time (e.g., block erase) + * should sleep for a while before calling this. + */ + + uint8_t flash_status; + + /* hardware quirk requires reading twice initially */ + flash_status = readb(docptr + DOC_FLASHCONTROL); + + do { + flash_status = readb(docptr + DOC_FLASHCONTROL); + } while (!(flash_status & DOC_CTRL_FLASHREADY)); + + return 0; +} + +static void write_addr(void __iomem *docptr, uint32_t docg4_addr) +{ + /* write the four address bytes packed in docg4_addr to the device */ + + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +} + +static void address_sequence(unsigned int g4_page, unsigned int g4_index, + void __iomem *docptr) +{ + writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_addr(docptr, ((uint32_t)g4_page << 16) | g4_index); + write_nop(docptr); +} + +static int docg4_load_block_reliable(uint32_t flash_offset, void *dest_addr) +{ + void __iomem *docptr = (void *)CONFIG_SYS_NAND_BASE; + unsigned int g4_page = flash_offset >> 11; /* 2k page */ + const unsigned int last_g4_page = g4_page + 0x80; /* last in block */ + int g4_index = 0; + uint16_t flash_status; + uint16_t *buf; + + /* flash_offset must be aligned to the start of a block */ + if (flash_offset & 0x3ffff) + return -1; + + writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + poll_status(docptr); + write_nop(docptr); + writew(0x45, docptr + DOC_FLASHSEQUENCE); + writew(0xa3, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + writew(0x22, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + /* read 1st 4 oob bytes of first subpage of block */ + address_sequence(g4_page, 0x0100, docptr); /* index at oob */ + write_nop(docptr); + flash_status = readw(docptr + DOC_FLASHCONTROL); + flash_status = readw(docptr + DOC_FLASHCONTROL); + if (flash_status & 0x06) /* sequence or protection errors */ + return -1; + writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + poll_status(docptr); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* + * Here we read the first four oob bytes of the first page of the block. + * The IPL on the palmtreo680 requires that this contain a 32 bit magic + * number, or the load aborts. We'll ignore it. + */ + readw(docptr + 0x103c); /* hw quirk; 1st read discarded */ + readw(docptr + 0x103c); /* lower 16 bits of magic number */ + readw(docptr + DOCG4_MYSTERY_REG); /* upper 16 bits of magic number */ + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + write_nop(docptr); + + /* load contents of block to memory */ + buf = (uint16_t *)dest_addr; + do { + int i; + + address_sequence(g4_page, g4_index, docptr); + writew(DOCG4_CMD_READ2, + docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + poll_status(docptr); + writew(DOC_ECCCONF0_READ_MODE | + DOC_ECCCONF0_ECC_ENABLE | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* read the 512 bytes of page data, 2 bytes at a time */ + readw(docptr + 0x103c); /* hw quirk */ + for (i = 0; i < 256; i++) + *buf++ = readw(docptr + 0x103c); + + /* read oob, but discard it */ + for (i = 0; i < 7; i++) + readw(docptr + 0x103c); + readw(docptr + DOCG4_OOB_6_7); + readw(docptr + DOCG4_OOB_6_7); + + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + write_nop(docptr); + + if (!(g4_index & 0x100)) { + /* not redundant subpage read; check for ecc error */ + write_nop(docptr); + flash_status = readw(docptr + DOC_ECCCONF1); + flash_status = readw(docptr + DOC_ECCCONF1); + if (flash_status & 0x80) { /* ecc error */ + g4_index += 0x108; /* read redundant subpage */ + buf -= 256; /* back up ram ptr */ + continue; + } else /* no ecc error */ + g4_index += 0x210; /* skip redundant subpage */ + } else /* redundant page was just read; skip ecc error check */ + g4_index += 0x108; + + if (g4_index == 0x420) { /* finished with 2k page */ + g4_index = 0; + g4_page += 2; /* odd-numbered 2k pages skipped */ + } + + } while (g4_page != last_g4_page); /* while still on same block */ + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_nand.c new file mode 100644 index 000000000..2f31fc96a --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_nand.c @@ -0,0 +1,829 @@ +/* Freescale Enhanced Local Bus Controller FCM NAND driver + * + * Copyright (c) 2006-2008 Freescale Semiconductor + * + * Authors: Nick Spence , + * Scott Wood + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef VERBOSE_DEBUG +#define DEBUG_ELBC +#define vdbg(format, arg...) printf("DEBUG: " format, ##arg) +#else +#define vdbg(format, arg...) do {} while (0) +#endif + +/* Can't use plain old DEBUG because the linux mtd + * headers define it as a macro. + */ +#ifdef DEBUG_ELBC +#define dbg(format, arg...) printf("DEBUG: " format, ##arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif + +#define MAX_BANKS 8 +#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ +#define FCM_TIMEOUT_MSECS 10 /* Maximum number of mSecs to wait for FCM */ + +#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) + +struct fsl_elbc_ctrl; + +/* mtd information per set */ + +struct fsl_elbc_mtd { + struct nand_chip chip; + struct fsl_elbc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + u8 __iomem *vbase; /* Chip select base virtual address */ + int page_size; /* NAND page size (0=512, 1=2048) */ + unsigned int fmr; /* FCM Flash Mode Register value */ +}; + +/* overview of the fsl elbc controller */ + +struct fsl_elbc_ctrl { + struct nand_hw_control controller; + struct fsl_elbc_mtd *chips[MAX_BANKS]; + + /* device info */ + fsl_lbc_t *regs; + u8 __iomem *addr; /* Address of assigned FCM buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes; /* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int status; /* status read from LTESR after last op */ + unsigned int mdr; /* UPM/FCM Data Register value */ + unsigned int use_mdr; /* Non zero if the MDR is to be set */ + unsigned int oob; /* Non zero if operating on OOB data */ +}; + +/* These map to the positions used by the FCM hardware ECC generator */ + +/* Small Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { + .eccbytes = 3, + .eccpos = {6, 7, 8}, + .oobfree = { {0, 5}, {9, 7} }, +}; + +/* Small Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { + .eccbytes = 3, + .eccpos = {8, 9, 10}, + .oobfree = { {0, 5}, {6, 2}, {11, 5} }, +}; + +/* Large Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { + .eccbytes = 12, + .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, + .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }, +}; + +/* Large Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { + .eccbytes = 12, + .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, + .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }, +}; + +/* + * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset + * 1, so we have to adjust bad block pattern. This pattern should be used for + * x8 chips only. So far hardware does not support x16 chips anyway. + */ +static u8 scan_ff_pattern[] = { 0xff, }; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern, +}; + +/* + * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, + * interfere with ECC positions, that's why we implement our own descriptors. + * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/*=================================*/ + +/* + * Set up the FCM hardware block and page address fields, and the fcm + * structure addr field to point to the correct FCM buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + int buf_num; + + ctrl->page = page_addr; + + if (priv->page_size) { + out_be32(&lbc->fbar, page_addr >> 6); + out_be32(&lbc->fpar, + ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | + (oob ? FPAR_LP_MS : 0) | column); + buf_num = (page_addr & 1) << 2; + } else { + out_be32(&lbc->fbar, page_addr >> 5); + out_be32(&lbc->fpar, + ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | + (oob ? FPAR_SP_MS : 0) | column); + buf_num = page_addr & 7; + } + + ctrl->addr = priv->vbase + buf_num * 1024; + ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ctrl->index += priv->page_size ? 2048 : 512; + + vdbg("set_addr: bank=%d, ctrl->addr=0x%p (0x%p), " + "index %x, pes %d ps %d\n", + buf_num, ctrl->addr, priv->vbase, ctrl->index, + chip->phys_erase_shift, chip->page_shift); +} + +/* + * execute FCM command and wait for it to complete + */ +static int fsl_elbc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + long long end_tick; + u32 ltesr; + + /* Setup the FMR[OP] to execute without write protection */ + out_be32(&lbc->fmr, priv->fmr | 3); + if (ctrl->use_mdr) + out_be32(&lbc->mdr, ctrl->mdr); + + vdbg("fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", + in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); + vdbg("fsl_elbc_run_command: fbar=%08x fpar=%08x " + "fbcr=%08x bank=%d\n", + in_be32(&lbc->fbar), in_be32(&lbc->fpar), + in_be32(&lbc->fbcr), priv->bank); + + /* execute special operation */ + out_be32(&lbc->lsor, priv->bank); + + /* wait for FCM complete flag or timeout */ + end_tick = usec2ticks(FCM_TIMEOUT_MSECS * 1000) + get_ticks(); + + ltesr = 0; + while (end_tick > get_ticks()) { + ltesr = in_be32(&lbc->ltesr); + if (ltesr & LTESR_CC) + break; + } + + ctrl->status = ltesr & LTESR_NAND_MASK; + out_be32(&lbc->ltesr, ctrl->status); + out_be32(&lbc->lteatr, 0); + + /* store mdr value in case it was needed */ + if (ctrl->use_mdr) + ctrl->mdr = in_be32(&lbc->mdr); + + ctrl->use_mdr = 0; + + vdbg("fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n", + ctrl->status, ctrl->mdr, in_be32(&lbc->fmr)); + + /* returns 0 on success otherwise non-zero) */ + return ctrl->status == LTESR_CC ? 0 : -EIO; +} + +static void fsl_elbc_do_read(struct nand_chip *chip, int oob) +{ + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + + if (priv->page_size) { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RBW << FIR_OP4_SHIFT)); + + out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); + } else { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_RBW << FIR_OP3_SHIFT)); + + if (oob) + out_be32(&lbc->fcr, + NAND_CMD_READOOB << FCR_CMD0_SHIFT); + else + out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); + } +} + +/* cmdfunc send commands to the FCM */ +static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + + ctrl->use_mdr = 0; + + /* clear the read buffer */ + ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ctrl->index = 0; + + switch (command) { + /* READ0 and READ1 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ1: + column += 256; + + /* fall-through */ + case NAND_CMD_READ0: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ + set_addr(mtd, 0, page_addr, 0); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ctrl->index += column; + + fsl_elbc_do_read(chip, 0); + fsl_elbc_run_command(mtd); + return; + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + out_be32(&lbc->fbcr, mtd->oobsize - column); + set_addr(mtd, column, page_addr, 1); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_elbc_do_read(chip, 1); + fsl_elbc_run_command(mtd); + + return; + + /* READID must read all 5 possible bytes while CEB is active */ + case NAND_CMD_READID: + case NAND_CMD_PARAM: + vdbg("fsl_elbc_cmdfunc: NAND_CMD 0x%x.\n", command); + + out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_UA << FIR_OP1_SHIFT) | + (FIR_OP_RBW << FIR_OP2_SHIFT)); + out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + out_be32(&lbc->fbcr, 256); + ctrl->read_bytes = 256; + ctrl->use_mdr = 1; + ctrl->mdr = column; + set_addr(mtd, 0, 0, 0); + fsl_elbc_run_command(mtd); + return; + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " + "page_addr: 0x%x.\n", page_addr); + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_PA << FIR_OP1_SHIFT) | + (FIR_OP_CM1 << FIR_OP2_SHIFT)); + + out_be32(&lbc->fcr, + (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT)); + + out_be32(&lbc->fbcr, 0); + ctrl->read_bytes = 0; + + fsl_elbc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + u32 fcr; + vdbg("fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " + "page_addr: 0x%x, column: 0x%x.\n", + page_addr, column); + + ctrl->column = column; + ctrl->oob = 0; + + if (priv->page_size) { + fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | + (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_WB << FIR_OP3_SHIFT) | + (FIR_OP_CW1 << FIR_OP4_SHIFT)); + } else { + fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | + (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM2 << FIR_OP1_SHIFT) | + (FIR_OP_CA << FIR_OP2_SHIFT) | + (FIR_OP_PA << FIR_OP3_SHIFT) | + (FIR_OP_WB << FIR_OP4_SHIFT) | + (FIR_OP_CW1 << FIR_OP5_SHIFT)); + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; + ctrl->oob = 1; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; + } else { + /* Second 256 bytes --> READ1 */ + fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT; + } + } + + out_be32(&lbc->fcr, fcr); + set_addr(mtd, column, page_addr, ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: { + vdbg("fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " + "writing %d bytes.\n", ctrl->index); + + /* if the write did not start at 0 or is not a full page + * then set the exact length, otherwise use a full page + * write so the HW generates the ECC. + */ + if (ctrl->oob || ctrl->column != 0 || + ctrl->index != mtd->writesize + mtd->oobsize) + out_be32(&lbc->fbcr, ctrl->index); + else + out_be32(&lbc->fbcr, 0); + + fsl_elbc_run_command(mtd); + + return; + } + + /* CMD_STATUS must read the status byte while CEB is active */ + /* Note - it does not wait for the ready line */ + case NAND_CMD_STATUS: + out_be32(&lbc->fir, + (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); + out_be32(&lbc->fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_elbc_run_command(mtd); + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); + return; + + /* RESET without waiting for the ready line */ + case NAND_CMD_RESET: + dbg("fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); + out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); + out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); + fsl_elbc_run_command(mtd); + return; + + default: + printf("fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", + command); + } +} + +static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip) +{ + /* The hardware does not seem to support multiple + * chips per bank. + */ +} + +/* + * Write buf to the FCM Controller Data Buffer + */ +static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len <= 0) { + printf("write_buf of %d bytes", len); + ctrl->status = 0; + return; + } + + if ((unsigned int)len > bufsize - ctrl->index) { + printf("write_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, bufsize - ctrl->index); + len = bufsize - ctrl->index; + } + + memcpy_toio(&ctrl->addr[ctrl->index], buf, len); + /* + * This is workaround for the weird elbc hangs during nand write, + * Scott Wood says: "...perhaps difference in how long it takes a + * write to make it through the localbus compared to a write to IMMR + * is causing problems, and sync isn't helping for some reason." + * Reading back the last byte helps though. + */ + in_8(&ctrl->addr[ctrl->index] + len - 1); + + ctrl->index += len; +} + +/* + * read a byte from either the FCM hardware buffer if it has any data left + * otherwise issue a command to read a single byte. + */ +static u8 fsl_elbc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + + /* If there are still bytes in the FCM, then use the next byte. */ + if (ctrl->index < ctrl->read_bytes) + return in_8(&ctrl->addr[ctrl->index++]); + + printf("read_byte beyond end of buffer\n"); + return ERR_BYTE; +} + +/* + * Read from the FCM Controller Data Buffer + */ +static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + int avail; + + if (len < 0) + return; + + avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); + memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail); + ctrl->index += avail; + + if (len > avail) + printf("read_buf beyond end of buffer " + "(%d requested, %d available)\n", + len, avail); +} + +/* + * Verify buffer against the FCM Controller Data Buffer + */ +static int fsl_elbc_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + int i; + + if (len < 0) { + printf("write_buf of %d bytes", len); + return -EINVAL; + } + + if ((unsigned int)len > ctrl->read_bytes - ctrl->index) { + printf("verify_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, ctrl->read_bytes - ctrl->index); + + ctrl->index = ctrl->read_bytes; + return -EINVAL; + } + + for (i = 0; i < len; i++) + if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i]) + break; + + ctrl->index += len; + return i == len && ctrl->status == LTESR_CC ? 0 : -EIO; +} + +/* This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + + if (ctrl->status != LTESR_CC) + return NAND_STATUS_FAIL; + + /* Use READ_STATUS command, but wait for the device to be ready */ + ctrl->use_mdr = 0; + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); + out_be32(&lbc->fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_elbc_run_command(mtd); + + if (ctrl->status != LTESR_CC) + return NAND_STATUS_FAIL; + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); + return fsl_elbc_read_byte(mtd); +} + +static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + fsl_elbc_read_buf(mtd, buf, mtd->writesize); + fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) + mtd->ecc_stats.failed++; + + return 0; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + fsl_elbc_write_buf(mtd, buf, mtd->writesize); + fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static struct fsl_elbc_ctrl *elbc_ctrl; + +static void fsl_elbc_ctrl_init(void) +{ + elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL); + if (!elbc_ctrl) + return; + + elbc_ctrl->regs = LBC_BASE_ADDR; + + /* clear event registers */ + out_be32(&elbc_ctrl->regs->ltesr, LTESR_NAND_MASK); + out_be32(&elbc_ctrl->regs->lteatr, 0); + + /* Enable interrupts for any detected events */ + out_be32(&elbc_ctrl->regs->lteir, LTESR_NAND_MASK); + + elbc_ctrl->read_bytes = 0; + elbc_ctrl->index = 0; + elbc_ctrl->addr = NULL; +} + +static int fsl_elbc_chip_init(int devnum, u8 *addr) +{ + struct mtd_info *mtd = &nand_info[devnum]; + struct nand_chip *nand; + struct fsl_elbc_mtd *priv; + uint32_t br = 0, or = 0; + int ret; + + if (!elbc_ctrl) { + fsl_elbc_ctrl_init(); + if (!elbc_ctrl) + return -1; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ctrl = elbc_ctrl; + priv->vbase = addr; + + /* Find which chip select it is connected to. It'd be nice + * if we could pass more than one datum to the NAND driver... + */ + for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { + phys_addr_t phys_addr = virt_to_phys(addr); + + br = in_be32(&elbc_ctrl->regs->bank[priv->bank].br); + or = in_be32(&elbc_ctrl->regs->bank[priv->bank].or); + + if ((br & BR_V) && (br & BR_MSEL) == BR_MS_FCM && + (br & or & BR_BA) == BR_PHYS_ADDR(phys_addr)) + break; + } + + if (priv->bank >= MAX_BANKS) { + printf("fsl_elbc_nand: address did not match any " + "chip selects\n"); + return -ENODEV; + } + + nand = &priv->chip; + mtd->priv = nand; + + elbc_ctrl->chips[priv->bank] = priv; + + /* fill in nand_chip structure */ + /* set up function call table */ + nand->read_byte = fsl_elbc_read_byte; + nand->write_buf = fsl_elbc_write_buf; + nand->read_buf = fsl_elbc_read_buf; + nand->verify_buf = fsl_elbc_verify_buf; + nand->select_chip = fsl_elbc_select_chip; + nand->cmdfunc = fsl_elbc_cmdfunc; + nand->waitfunc = fsl_elbc_wait; + + /* set up nand options */ + nand->bbt_td = &bbt_main_descr; + nand->bbt_md = &bbt_mirror_descr; + + /* set up nand options */ + nand->options = NAND_NO_SUBPAGE_WRITE; + nand->bbt_options = NAND_BBT_USE_FLASH; + + nand->controller = &elbc_ctrl->controller; + nand->priv = priv; + + nand->ecc.read_page = fsl_elbc_read_page; + nand->ecc.write_page = fsl_elbc_write_page; + + priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT); + + /* If CS Base Register selects full hardware ECC then use it */ + if ((br & BR_DECC) == BR_DECC_CHK_GEN) { + nand->ecc.mode = NAND_ECC_HW; + + nand->ecc.layout = (priv->fmr & FMR_ECCM) ? + &fsl_elbc_oob_sp_eccm1 : + &fsl_elbc_oob_sp_eccm0; + + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.steps = 1; + nand->ecc.strength = 1; + } else { + /* otherwise fall back to software ECC */ +#if defined(CONFIG_NAND_ECC_BCH) + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif + } + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + /* Large-page-specific setup */ + if (mtd->writesize == 2048) { + setbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, + OR_FCM_PGS); + in_be32(&elbc_ctrl->regs->bank[priv->bank].or); + + priv->page_size = 1; + nand->badblock_pattern = &largepage_memorybased; + + /* + * Hardware expects small page has ECCM0, large page has + * ECCM1 when booting from NAND, and we follow that even + * when not booting from NAND. + */ + priv->fmr |= FMR_ECCM; + + /* adjust ecc setup if needed */ + if ((br & BR_DECC) == BR_DECC_CHK_GEN) { + nand->ecc.steps = 4; + nand->ecc.layout = (priv->fmr & FMR_ECCM) ? + &fsl_elbc_oob_lp_eccm1 : + &fsl_elbc_oob_lp_eccm0; + } + } else if (mtd->writesize == 512) { + clrbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, + OR_FCM_PGS); + in_be32(&elbc_ctrl->regs->bank[priv->bank].or); + } else { + return -ENODEV; + } + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = nand_register(devnum); + if (ret) + return ret; + + return 0; +} + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] = + CONFIG_SYS_NAND_BASE_LIST; + +void board_nand_init(void) +{ + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + fsl_elbc_chip_init(i, (u8 *)base_address[i]); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_spl.c b/qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_spl.c new file mode 100644 index 000000000..29521359a --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/fsl_elbc_spl.c @@ -0,0 +1,168 @@ +/* + * NAND boot for Freescale Enhanced Local Bus Controller, Flash Control Machine + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * Copyright (c) 2008 Freescale Semiconductor, Inc. + * Author: Scott Wood + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define WINDOW_SIZE 8192 + +static void nand_wait(void) +{ + fsl_lbc_t *regs = LBC_BASE_ADDR; + + for (;;) { + uint32_t status = in_be32(®s->ltesr); + + if (status == 1) + return; + + if (status & 1) { + puts("read failed (ltesr)\n"); + for (;;); + } + } +} + +#ifdef CONFIG_TPL_BUILD +int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) +#else +static int nand_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) +#endif +{ + fsl_lbc_t *regs = LBC_BASE_ADDR; + uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; + const int large = CONFIG_SYS_NAND_OR_PRELIM & OR_FCM_PGS; + const int block_shift = large ? 17 : 14; + const int block_size = 1 << block_shift; + const int page_size = large ? 2048 : 512; + const int bad_marker = large ? page_size + 0 : page_size + 5; + int fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT) | 2; + int pos = 0; + char *dst = vdst; + + if (offs & (block_size - 1)) { + puts("bad offset\n"); + for (;;); + } + + if (large) { + fmr |= FMR_ECCM; + out_be32(®s->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); + out_be32(®s->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RBW << FIR_OP4_SHIFT)); + } else { + out_be32(®s->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); + out_be32(®s->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_RBW << FIR_OP3_SHIFT)); + } + + out_be32(®s->fbcr, 0); + clrsetbits_be32(®s->bank[0].br, BR_DECC, BR_DECC_CHK_GEN); + + while (pos < uboot_size) { + int i = 0; + out_be32(®s->fbar, offs >> block_shift); + + do { + int j; + unsigned int page_offs = (offs & (block_size - 1)) << 1; + + out_be32(®s->ltesr, ~0); + out_be32(®s->lteatr, 0); + out_be32(®s->fpar, page_offs); + out_be32(®s->fmr, fmr); + out_be32(®s->lsor, 0); + nand_wait(); + + page_offs %= WINDOW_SIZE; + + /* + * If either of the first two pages are marked bad, + * continue to the next block. + */ + if (i++ < 2 && buf[page_offs + bad_marker] != 0xff) { + puts("skipping\n"); + offs = (offs + block_size) & ~(block_size - 1); + pos &= ~(block_size - 1); + break; + } + + for (j = 0; j < page_size; j++) + dst[pos + j] = buf[page_offs + j]; + + pos += page_size; + offs += page_size; + } while ((offs & (block_size - 1)) && (pos < uboot_size)); + } + + return 0; +} + +/* + * Defines a static function nand_load_image() here, because non-static makes + * the code too large for certain SPLs(minimal SPL, maximum size <= 4Kbytes) + */ +#ifndef CONFIG_TPL_BUILD +#define nand_spl_load_image(offs, uboot_size, vdst) \ + nand_load_image(offs, uboot_size, vdst) +#endif + +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + /* + * Load U-Boot image from NAND into RAM + */ + nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_U_BOOT_SIZE, + (void *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST + nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND + nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif + +#ifdef CONFIG_SPL_FLUSH_IMAGE + /* + * Clean d-cache and invalidate i-cache, to + * make sure that no stale data is executed. + */ + flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); +#endif + + puts("transfering control\n"); + /* + * Jump to U-Boot image + */ + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + (*uboot)(); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_nand.c new file mode 100644 index 000000000..be5a16a1b --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_nand.c @@ -0,0 +1,1039 @@ +/* Integrated Flash Controller NAND Machine Driver + * + * Copyright (c) 2012 Freescale Semiconductor, Inc + * + * Authors: Dipen Dudhat + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define FSL_IFC_V1_1_0 0x01010000 +#define MAX_BANKS 4 +#define ERR_BYTE 0xFF /* Value returned for read bytes + when read failed */ +#define IFC_TIMEOUT_MSECS 10 /* Maximum number of mSecs to wait for IFC + NAND Machine */ + +struct fsl_ifc_ctrl; + +/* mtd information per set */ +struct fsl_ifc_mtd { + struct nand_chip chip; + struct fsl_ifc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ + u8 __iomem *vbase; /* Chip select base virtual address */ +}; + +/* overview of the fsl ifc controller */ +struct fsl_ifc_ctrl { + struct nand_hw_control controller; + struct fsl_ifc_mtd *chips[MAX_BANKS]; + + /* device info */ + struct fsl_ifc *regs; + uint8_t __iomem *addr; /* Address of assigned IFC buffer */ + unsigned int cs_nand; /* On which chipsel NAND is connected */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes; /* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int status; /* status read from NEESR after last op */ + unsigned int oob; /* Non zero if operating on OOB data */ + unsigned int eccread; /* Non zero for a full-page ECC read */ +}; + +static struct fsl_ifc_ctrl *ifc_ctrl; + +/* 512-byte page with 4-bit ECC, 8-bit */ +static struct nand_ecclayout oob_512_8bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {0, 5}, {6, 2} }, +}; + +/* 512-byte page with 4-bit ECC, 16-bit */ +static struct nand_ecclayout oob_512_16bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {2, 6}, }, +}; + +/* 2048-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_2048_ecc4 = { + .eccbytes = 32, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + }, + .oobfree = { {2, 6}, {40, 24} }, +}; + +/* 4096-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_4096_ecc4 = { + .eccbytes = 64, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + }, + .oobfree = { {2, 6}, {72, 56} }, +}; + +/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_4096_ecc8 = { + .eccbytes = 128, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobfree = { {2, 6}, {136, 82} }, +}; + +/* 8192-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_8192_ecc4 = { + .eccbytes = 128, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobfree = { {2, 6}, {136, 208} }, +}; + +/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_8192_ecc8 = { + .eccbytes = 256, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, + }, + .oobfree = { {2, 6}, {264, 80} }, +}; + +/* + * Generic flash bbt descriptors + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* + * Set up the IFC hardware block and page address fields, and the ifc nand + * structure addr field to point to the correct IFC buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc *ifc = ctrl->regs; + int buf_num; + + ctrl->page = page_addr; + + /* Program ROW0/COL0 */ + ifc_out32(&ifc->ifc_nand.row0, page_addr); + ifc_out32(&ifc->ifc_nand.col0, (oob ? IFC_NAND_COL_MS : 0) | column); + + buf_num = page_addr & priv->bufnum_mask; + + ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); + ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ctrl->index += mtd->writesize; +} + +static int is_blank(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, + unsigned int bufnum) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2); + u32 __iomem *main = (u32 *)addr; + u8 __iomem *oob = addr + mtd->writesize; + int i; + + for (i = 0; i < mtd->writesize / 4; i++) { + if (__raw_readl(&main[i]) != 0xffffffff) + return 0; + } + + for (i = 0; i < chip->ecc.layout->eccbytes; i++) { + int pos = chip->ecc.layout->eccpos[i]; + + if (__raw_readb(&oob[pos]) != 0xff) + return 0; + } + + return 1; +} + +/* returns nonzero if entire page is blank */ +static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, + u32 *eccstat, unsigned int bufnum) +{ + u32 reg = eccstat[bufnum / 4]; + int errors; + + errors = (reg >> ((3 - bufnum % 4) * 8)) & 15; + + return errors; +} + +/* + * execute IFC NAND command and wait for it to complete + */ +static int fsl_ifc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc *ifc = ctrl->regs; + long long end_tick; + u32 eccstat[4]; + int i; + + /* set the chip select for NAND Transaction */ + ifc_out32(&ifc->ifc_nand.nand_csel, ifc_ctrl->cs_nand); + + /* start read/write seq */ + ifc_out32(&ifc->ifc_nand.nandseq_strt, + IFC_NAND_SEQ_STRT_FIR_STRT); + + /* wait for NAND Machine complete flag or timeout */ + end_tick = usec2ticks(IFC_TIMEOUT_MSECS * 1000) + get_ticks(); + + while (end_tick > get_ticks()) { + ctrl->status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); + + if (ctrl->status & IFC_NAND_EVTER_STAT_OPC) + break; + } + + ifc_out32(&ifc->ifc_nand.nand_evter_stat, ctrl->status); + + if (ctrl->status & IFC_NAND_EVTER_STAT_FTOER) + printf("%s: Flash Time Out Error\n", __func__); + if (ctrl->status & IFC_NAND_EVTER_STAT_WPER) + printf("%s: Write Protect Error\n", __func__); + + if (ctrl->eccread) { + int errors; + int bufnum = ctrl->page & priv->bufnum_mask; + int sector = bufnum * chip->ecc.steps; + int sector_end = sector + chip->ecc.steps - 1; + + for (i = sector / 4; i <= sector_end / 4; i++) + eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]); + + for (i = sector; i <= sector_end; i++) { + errors = check_read_ecc(mtd, ctrl, eccstat, i); + + if (errors == 15) { + /* + * Uncorrectable error. + * OK only if the whole page is blank. + * + * We disable ECCER reporting due to erratum + * IFC-A002770 -- so report it now if we + * see an uncorrectable error in ECCSTAT. + */ + if (!is_blank(mtd, ctrl, bufnum)) + ctrl->status |= + IFC_NAND_EVTER_STAT_ECCER; + break; + } + + mtd->ecc_stats.corrected += errors; + } + + ctrl->eccread = 0; + } + + /* returns 0 on success otherwise non-zero) */ + return ctrl->status == IFC_NAND_EVTER_STAT_OPC ? 0 : -EIO; +} + +static void fsl_ifc_do_read(struct nand_chip *chip, + int oob, + struct mtd_info *mtd) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc *ifc = ctrl->regs; + + /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ + if (mtd->writesize > 512) { + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); + + ifc_out32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); + } else { + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT)); + + if (oob) + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT); + else + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); + } +} + +/* cmdfunc send commands to the IFC NAND Machine */ +static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc *ifc = ctrl->regs; + + /* clear the read buffer */ + ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ctrl->index = 0; + + switch (command) { + /* READ0 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ0: { + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); + set_addr(mtd, 0, page_addr, 0); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ctrl->index += column; + + if (chip->ecc.mode == NAND_ECC_HW) + ctrl->eccread = 1; + + fsl_ifc_do_read(chip, 0, mtd); + fsl_ifc_run_command(mtd); + return; + } + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + ifc_out32(&ifc->ifc_nand.nand_fbcr, mtd->oobsize - column); + set_addr(mtd, column, page_addr, 1); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_ifc_do_read(chip, 1, mtd); + fsl_ifc_run_command(mtd); + + return; + + /* READID must read all possible bytes while CEB is active */ + case NAND_CMD_READID: + case NAND_CMD_PARAM: { + int timing = IFC_FIR_OP_RB; + if (command == NAND_CMD_PARAM) + timing = IFC_FIR_OP_RBCD; + + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (timing << IFC_NAND_FIR0_OP2_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fcr0, + command << IFC_NAND_FCR0_CMD0_SHIFT); + ifc_out32(&ifc->ifc_nand.row3, column); + + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + ifc_out32(&ifc->ifc_nand.nand_fbcr, 256); + ctrl->read_bytes = 256; + + set_addr(mtd, 0, 0, 0); + fsl_ifc_run_command(mtd); + return; + } + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT)); + + ifc_out32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT)); + + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); + ctrl->read_bytes = 0; + fsl_ifc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + u32 nand_fcr0; + ctrl->column = column; + ctrl->oob = 0; + + if (mtd->writesize > 512) { + nand_fcr0 = + (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); + + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_WBCD << + IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, + (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_RDSTAT << + IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT)); + } else { + nand_fcr0 = ((NAND_CMD_PAGEPROG << + IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_SEQIN << + IFC_NAND_FCR0_CMD2_SHIFT) | + (NAND_CMD_STATUS << + IFC_NAND_FCR0_CMD3_SHIFT)); + + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_RDSTAT << + IFC_NAND_FIR1_OP7_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT)); + + if (column >= mtd->writesize) + nand_fcr0 |= + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; + else + nand_fcr0 |= + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; + } + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + ctrl->oob = 1; + } + ifc_out32(&ifc->ifc_nand.nand_fcr0, nand_fcr0); + set_addr(mtd, column, page_addr, ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: + if (ctrl->oob) + ifc_out32(&ifc->ifc_nand.nand_fbcr, + ctrl->index - ctrl->column); + else + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); + + fsl_ifc_run_command(mtd); + return; + + case NAND_CMD_STATUS: + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT); + ifc_out32(&ifc->ifc_nand.nand_fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + /* Chip sometimes reporting write protect even when it's not */ + out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); + return; + + case NAND_CMD_RESET: + ifc_out32(&ifc->ifc_nand.nand_fir0, + IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT); + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT); + fsl_ifc_run_command(mtd); + return; + + default: + printf("%s: error, unsupported command 0x%x.\n", + __func__, command); + } +} + +/* + * Write buf to the IFC NAND Controller Data Buffer + */ +static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len <= 0) { + printf("%s of %d bytes", __func__, len); + ctrl->status = 0; + return; + } + + if ((unsigned int)len > bufsize - ctrl->index) { + printf("%s beyond end of buffer " + "(%d requested, %u available)\n", + __func__, len, bufsize - ctrl->index); + len = bufsize - ctrl->index; + } + + memcpy_toio(&ctrl->addr[ctrl->index], buf, len); + ctrl->index += len; +} + +/* + * read a byte from either the IFC hardware buffer if it has any data left + * otherwise issue a command to read a single byte. + */ +static u8 fsl_ifc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + + /* If there are still bytes in the IFC buffer, then use the + * next byte. */ + if (ctrl->index < ctrl->read_bytes) + return in_8(&ctrl->addr[ctrl->index++]); + + printf("%s beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read two bytes from the IFC hardware buffer + * read function for 16-bit buswith + */ +static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + uint16_t data; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ctrl->index < ctrl->read_bytes) { + data = ifc_in16((uint16_t *)&ctrl-> + addr[ctrl->index]); + ctrl->index += 2; + return (uint8_t)data; + } + + printf("%s beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read from the IFC Controller Data Buffer + */ +static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + int avail; + + if (len < 0) + return; + + avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); + memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail); + ctrl->index += avail; + + if (len > avail) + printf("%s beyond end of buffer " + "(%d requested, %d available)\n", + __func__, len, avail); +} + +/* + * Verify buffer against the IFC Controller Data Buffer + */ +static int fsl_ifc_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + int i; + + if (len < 0) { + printf("%s of %d bytes", __func__, len); + return -EINVAL; + } + + if ((unsigned int)len > ctrl->read_bytes - ctrl->index) { + printf("%s beyond end of buffer " + "(%d requested, %u available)\n", + __func__, len, ctrl->read_bytes - ctrl->index); + + ctrl->index = ctrl->read_bytes; + return -EINVAL; + } + + for (i = 0; i < len; i++) + if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i]) + break; + + ctrl->index += len; + return i == len && ctrl->status == IFC_NAND_EVTER_STAT_OPC ? 0 : -EIO; +} + +/* This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc *ifc = ctrl->regs; + u32 nand_fsr; + + if (ctrl->status != IFC_NAND_EVTER_STAT_OPC) + return NAND_STATUS_FAIL; + + /* Use READ_STATUS command, but wait for the device to be ready */ + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fcr0, NAND_CMD_STATUS << + IFC_NAND_FCR0_CMD0_SHIFT); + ifc_out32(&ifc->ifc_nand.nand_fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + if (ctrl->status != IFC_NAND_EVTER_STAT_OPC) + return NAND_STATUS_FAIL; + + nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); + + /* Chip sometimes reporting write protect even when it's not */ + nand_fsr = nand_fsr | NAND_STATUS_WP; + return nand_fsr; +} + +static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + + fsl_ifc_read_buf(mtd, buf, mtd->writesize); + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (ctrl->status != IFC_NAND_EVTER_STAT_OPC) + mtd->ecc_stats.failed++; + + return 0; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + fsl_ifc_write_buf(mtd, buf, mtd->writesize); + fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static void fsl_ifc_ctrl_init(void) +{ + ifc_ctrl = kzalloc(sizeof(*ifc_ctrl), GFP_KERNEL); + if (!ifc_ctrl) + return; + + ifc_ctrl->regs = IFC_BASE_ADDR; + + /* clear event registers */ + ifc_out32(&ifc_ctrl->regs->ifc_nand.nand_evter_stat, ~0U); + ifc_out32(&ifc_ctrl->regs->ifc_nand.pgrdcmpl_evt_stat, ~0U); + + /* Enable error and event for any detected errors */ + ifc_out32(&ifc_ctrl->regs->ifc_nand.nand_evter_en, + IFC_NAND_EVTER_EN_OPC_EN | + IFC_NAND_EVTER_EN_PGRDCMPL_EN | + IFC_NAND_EVTER_EN_FTOER_EN | + IFC_NAND_EVTER_EN_WPER_EN); + + ifc_out32(&ifc_ctrl->regs->ifc_nand.ncfgr, 0x0); +} + +static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) +{ +} + +static void fsl_ifc_sram_init(void) +{ + struct fsl_ifc *ifc = ifc_ctrl->regs; + uint32_t cs = 0, csor = 0, csor_8k = 0, csor_ext = 0; + long long end_tick; + + cs = ifc_ctrl->cs_nand >> IFC_NAND_CSEL_SHIFT; + + /* Save CSOR and CSOR_ext */ + csor = ifc_in32(&ifc_ctrl->regs->csor_cs[cs].csor); + csor_ext = ifc_in32(&ifc_ctrl->regs->csor_cs[cs].csor_ext); + + /* chage PageSize 8K and SpareSize 1K*/ + csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; + ifc_out32(&ifc_ctrl->regs->csor_cs[cs].csor, csor_8k); + ifc_out32(&ifc_ctrl->regs->csor_cs[cs].csor_ext, 0x0000400); + + /* READID */ + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT); + ifc_out32(&ifc->ifc_nand.row3, 0x0); + + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0x0); + + /* Program ROW0/COL0 */ + ifc_out32(&ifc->ifc_nand.row0, 0x0); + ifc_out32(&ifc->ifc_nand.col0, 0x0); + + /* set the chip select for NAND Transaction */ + ifc_out32(&ifc->ifc_nand.nand_csel, ifc_ctrl->cs_nand); + + /* start read seq */ + ifc_out32(&ifc->ifc_nand.nandseq_strt, IFC_NAND_SEQ_STRT_FIR_STRT); + + /* wait for NAND Machine complete flag or timeout */ + end_tick = usec2ticks(IFC_TIMEOUT_MSECS * 1000) + get_ticks(); + + while (end_tick > get_ticks()) { + ifc_ctrl->status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); + + if (ifc_ctrl->status & IFC_NAND_EVTER_STAT_OPC) + break; + } + + ifc_out32(&ifc->ifc_nand.nand_evter_stat, ifc_ctrl->status); + + /* Restore CSOR and CSOR_ext */ + ifc_out32(&ifc_ctrl->regs->csor_cs[cs].csor, csor); + ifc_out32(&ifc_ctrl->regs->csor_cs[cs].csor_ext, csor_ext); +} + +static int fsl_ifc_chip_init(int devnum, u8 *addr) +{ + struct mtd_info *mtd = &nand_info[devnum]; + struct nand_chip *nand; + struct fsl_ifc_mtd *priv; + struct nand_ecclayout *layout; + uint32_t cspr = 0, csor = 0, ver = 0; + int ret; + + if (!ifc_ctrl) { + fsl_ifc_ctrl_init(); + if (!ifc_ctrl) + return -1; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ctrl = ifc_ctrl; + priv->vbase = addr; + + /* Find which chip select it is connected to. + */ + for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { + phys_addr_t phys_addr = virt_to_phys(addr); + + cspr = ifc_in32(&ifc_ctrl->regs->cspr_cs[priv->bank].cspr); + csor = ifc_in32(&ifc_ctrl->regs->csor_cs[priv->bank].csor); + + if ((cspr & CSPR_V) && (cspr & CSPR_MSEL) == CSPR_MSEL_NAND && + (cspr & CSPR_BA) == CSPR_PHYS_ADDR(phys_addr)) { + ifc_ctrl->cs_nand = priv->bank << IFC_NAND_CSEL_SHIFT; + break; + } + } + + if (priv->bank >= MAX_BANKS) { + printf("%s: address did not match any " + "chip selects\n", __func__); + kfree(priv); + return -ENODEV; + } + + nand = &priv->chip; + mtd->priv = nand; + + ifc_ctrl->chips[priv->bank] = priv; + + /* fill in nand_chip structure */ + /* set up function call table */ + + nand->write_buf = fsl_ifc_write_buf; + nand->read_buf = fsl_ifc_read_buf; + nand->verify_buf = fsl_ifc_verify_buf; + nand->select_chip = fsl_ifc_select_chip; + nand->cmdfunc = fsl_ifc_cmdfunc; + nand->waitfunc = fsl_ifc_wait; + + /* set up nand options */ + nand->bbt_td = &bbt_main_descr; + nand->bbt_md = &bbt_mirror_descr; + + /* set up nand options */ + nand->options = NAND_NO_SUBPAGE_WRITE; + nand->bbt_options = NAND_BBT_USE_FLASH; + + if (cspr & CSPR_PORT_SIZE_16) { + nand->read_byte = fsl_ifc_read_byte16; + nand->options |= NAND_BUSWIDTH_16; + } else { + nand->read_byte = fsl_ifc_read_byte; + } + + nand->controller = &ifc_ctrl->controller; + nand->priv = priv; + + nand->ecc.read_page = fsl_ifc_read_page; + nand->ecc.write_page = fsl_ifc_write_page; + + /* Hardware generates ECC per 512 Bytes */ + nand->ecc.size = 512; + nand->ecc.bytes = 8; + + switch (csor & CSOR_NAND_PGS_MASK) { + case CSOR_NAND_PGS_512: + if (nand->options & NAND_BUSWIDTH_16) { + layout = &oob_512_16bit_ecc4; + } else { + layout = &oob_512_8bit_ecc4; + + /* Avoid conflict with bad block marker */ + bbt_main_descr.offs = 0; + bbt_mirror_descr.offs = 0; + } + + nand->ecc.strength = 4; + priv->bufnum_mask = 15; + break; + + case CSOR_NAND_PGS_2K: + layout = &oob_2048_ecc4; + nand->ecc.strength = 4; + priv->bufnum_mask = 3; + break; + + case CSOR_NAND_PGS_4K: + if ((csor & CSOR_NAND_ECC_MODE_MASK) == + CSOR_NAND_ECC_MODE_4) { + layout = &oob_4096_ecc4; + nand->ecc.strength = 4; + } else { + layout = &oob_4096_ecc8; + nand->ecc.strength = 8; + nand->ecc.bytes = 16; + } + + priv->bufnum_mask = 1; + break; + + case CSOR_NAND_PGS_8K: + if ((csor & CSOR_NAND_ECC_MODE_MASK) == + CSOR_NAND_ECC_MODE_4) { + layout = &oob_8192_ecc4; + nand->ecc.strength = 4; + } else { + layout = &oob_8192_ecc8; + nand->ecc.strength = 8; + nand->ecc.bytes = 16; + } + + priv->bufnum_mask = 0; + break; + + + default: + printf("ifc nand: bad csor %#x: bad page size\n", csor); + return -ENODEV; + } + + /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ + if (csor & CSOR_NAND_ECC_DEC_EN) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = layout; + } else { + nand->ecc.mode = NAND_ECC_SOFT; + } + + ver = ifc_in32(&ifc_ctrl->regs->ifc_rev); + if (ver == FSL_IFC_V1_1_0) + fsl_ifc_sram_init(); + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = nand_register(devnum); + if (ret) + return ret; + return 0; +} + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] = + CONFIG_SYS_NAND_BASE_LIST; + +void board_nand_init(void) +{ + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + fsl_ifc_chip_init(i, (u8 *)base_address[i]); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_spl.c b/qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_spl.c new file mode 100644 index 000000000..510077282 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/fsl_ifc_spl.c @@ -0,0 +1,252 @@ +/* + * NAND boot for Freescale Integrated Flash Controller, NAND FCM + * + * Copyright 2011 Freescale Semiconductor, Inc. + * Author: Dipen Dudhat + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static inline int is_blank(uchar *addr, int page_size) +{ + int i; + + for (i = 0; i < page_size; i++) { + if (__raw_readb(&addr[i]) != 0xff) + return 0; + } + + /* + * For the SPL, don't worry about uncorrectable errors + * where the main area is all FFs but shouldn't be. + */ + return 1; +} + +/* returns nonzero if entire page is blank */ +static inline int check_read_ecc(uchar *buf, u32 *eccstat, + unsigned int bufnum, int page_size) +{ + u32 reg = eccstat[bufnum / 4]; + int errors = (reg >> ((3 - bufnum % 4) * 8)) & 0xf; + + if (errors == 0xf) { /* uncorrectable */ + /* Blank pages fail hw ECC checks */ + if (is_blank(buf, page_size)) + return 1; + + puts("ecc error\n"); + for (;;) + ; + } + + return 0; +} + +static inline void nand_wait(uchar *buf, int bufnum, int page_size) +{ + struct fsl_ifc *ifc = IFC_BASE_ADDR; + u32 status; + u32 eccstat[4]; + int bufperpage = page_size / 512; + int bufnum_end, i; + + bufnum *= bufperpage; + bufnum_end = bufnum + bufperpage - 1; + + do { + status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); + } while (!(status & IFC_NAND_EVTER_STAT_OPC)); + + if (status & IFC_NAND_EVTER_STAT_FTOER) { + puts("flash time out error\n"); + for (;;) + ; + } + + for (i = bufnum / 4; i <= bufnum_end / 4; i++) + eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]); + + for (i = bufnum; i <= bufnum_end; i++) { + if (check_read_ecc(buf, eccstat, i, page_size)) + break; + } + + ifc_out32(&ifc->ifc_nand.nand_evter_stat, status); +} + +static inline int bad_block(uchar *marker, int port_size) +{ + if (port_size == 8) + return __raw_readb(marker) != 0xff; + else + return __raw_readw((u16 *)marker) != 0xffff; +} + +int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) +{ + struct fsl_ifc *ifc = IFC_BASE_ADDR; + uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; + int page_size; + int port_size; + int pages_per_blk; + int blk_size; + int bad_marker = 0; + int bufnum_mask, bufnum; + + int csor, cspr; + int pos = 0; + int j = 0; + + int sram_addr; + int pg_no; + uchar *dst = vdst; + + /* Get NAND Flash configuration */ + csor = CONFIG_SYS_NAND_CSOR; + cspr = CONFIG_SYS_NAND_CSPR; + + port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8; + + if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_8K) { + page_size = 8192; + bufnum_mask = 0x0; + } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_4K) { + page_size = 4096; + bufnum_mask = 0x1; + } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_2K) { + page_size = 2048; + bufnum_mask = 0x3; + } else { + page_size = 512; + bufnum_mask = 0xf; + + if (port_size == 8) + bad_marker = 5; + } + + pages_per_blk = + 32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT); + + blk_size = pages_per_blk * page_size; + + /* Open Full SRAM mapping for spare are access */ + ifc_out32(&ifc->ifc_nand.ncfgr, 0x0); + + /* Clear Boot events */ + ifc_out32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff); + + /* Program FIR/FCR for Large/Small page */ + if (page_size > 512) { + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); + + ifc_out32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); + } else { + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); + + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); + } + + /* Program FBCR = 0 for full page read */ + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); + + /* Read and copy u-boot on SDRAM from NAND device, In parallel + * check for Bad block if found skip it and read continue to + * next Block + */ + while (pos < uboot_size) { + int i = 0; + do { + pg_no = offs / page_size; + bufnum = pg_no & bufnum_mask; + sram_addr = bufnum * page_size * 2; + + ifc_out32(&ifc->ifc_nand.row0, pg_no); + ifc_out32(&ifc->ifc_nand.col0, 0); + /* start read */ + ifc_out32(&ifc->ifc_nand.nandseq_strt, + IFC_NAND_SEQ_STRT_FIR_STRT); + + /* wait for read to complete */ + nand_wait(&buf[sram_addr], bufnum, page_size); + + /* + * If either of the first two pages are marked bad, + * continue to the next block. + */ + if (i++ < 2 && + bad_block(&buf[sram_addr + page_size + bad_marker], + port_size)) { + puts("skipping\n"); + offs = (offs + blk_size) & ~(blk_size - 1); + pos &= ~(blk_size - 1); + break; + } + + for (j = 0; j < page_size; j++) + dst[pos + j] = __raw_readb(&buf[sram_addr + j]); + + pos += page_size; + offs += page_size; + } while ((offs & (blk_size - 1)) && (pos < uboot_size)); + } + + return 0; +} + +/* + * Main entrypoint for NAND Boot. It's necessary that SDRAM is already + * configured and available since this code loads the main U-boot image + * from NAND into SDRAM and starts from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + /* + * Load U-Boot image from NAND into RAM + */ + nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_U_BOOT_SIZE, + (uchar *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST + nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, + (uchar *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND + nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, + (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif + /* + * Jump to U-Boot image + */ +#ifdef CONFIG_SPL_FLUSH_IMAGE + /* + * Clean d-cache and invalidate i-cache, to + * make sure that no stale data is executed. + */ + flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); +#endif + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + uboot(); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/fsl_upm.c b/qemu/roms/u-boot/drivers/mtd/nand/fsl_upm.c new file mode 100644 index 000000000..3ae0044f2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/fsl_upm.c @@ -0,0 +1,199 @@ +/* + * FSL UPM NAND driver + * + * Copyright (C) 2007 MontaVista Software, Inc. + * Anton Vorontsov + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset) +{ + clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset); + (void)in_be32(upm->mxmr); +} + +static void fsl_upm_end_pattern(struct fsl_upm *upm) +{ + clrbits_be32(upm->mxmr, MxMR_OP_RUNP); + + while (in_be32(upm->mxmr) & MxMR_OP_RUNP) + eieio(); +} + +static void fsl_upm_run_pattern(struct fsl_upm *upm, int width, + void __iomem *io_addr, u32 mar) +{ + out_be32(upm->mar, mar); + (void)in_be32(upm->mar); + switch (width) { + case 8: + out_8(io_addr, 0x0); + break; + case 16: + out_be16(io_addr, 0x0); + break; + case 32: + out_be32(io_addr, 0x0); + break; + } +} + +static void fun_wait(struct fsl_upm_nand *fun) +{ + if (fun->dev_ready) { + while (!fun->dev_ready(fun->chip_nr)) + debug("unexpected busy state\n"); + } else { + /* + * If the R/B pin is not connected, + * a short delay is necessary. + */ + udelay(1); + } +} + +#if CONFIG_SYS_NAND_MAX_CHIPS > 1 +static void fun_select_chip(struct mtd_info *mtd, int chip_nr) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_upm_nand *fun = chip->priv; + + if (chip_nr >= 0) { + fun->chip_nr = chip_nr; + chip->IO_ADDR_R = chip->IO_ADDR_W = + fun->upm.io_addr + fun->chip_offset * chip_nr; + } else if (chip_nr == -1) { + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + } +} +#endif + +static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_upm_nand *fun = chip->priv; + void __iomem *io_addr; + u32 mar; + + if (!(ctrl & fun->last_ctrl)) { + fsl_upm_end_pattern(&fun->upm); + + if (cmd == NAND_CMD_NONE) + return; + + fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); + } + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_ALE) + fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); + else if (ctrl & NAND_CLE) + fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); + } + + mar = cmd << (32 - fun->width); + io_addr = fun->upm.io_addr; +#if CONFIG_SYS_NAND_MAX_CHIPS > 1 + if (fun->chip_nr > 0) { + io_addr += fun->chip_offset * fun->chip_nr; + if (fun->upm_mar_chip_offset) + mar |= fun->upm_mar_chip_offset * fun->chip_nr; + } +#endif + fsl_upm_run_pattern(&fun->upm, fun->width, io_addr, mar); + + /* + * Some boards/chips needs this. At least the MPC8360E-RDK + * needs it. Probably weird chip, because I don't see any + * need for this on MPC8555E + Samsung K9F1G08U0A. Usually + * here are 0-2 unexpected busy states per block read. + */ + if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) + fun_wait(fun); +} + +static u8 upm_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + return in_8(chip->IO_ADDR_R); +} + +static void upm_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + struct fsl_upm_nand *fun = chip->priv; + + for (i = 0; i < len; i++) { + out_8(chip->IO_ADDR_W, buf[i]); + if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) + fun_wait(fun); + } + + if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) + fun_wait(fun); +} + +static void upm_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = in_8(chip->IO_ADDR_R); +} + +static int upm_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) { + if (buf[i] != in_8(chip->IO_ADDR_R)) + return -EFAULT; + } + + return 0; +} + +static int nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_upm_nand *fun = chip->priv; + + return fun->dev_ready(fun->chip_nr); +} + +int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun) +{ + if (fun->width != 8 && fun->width != 16 && fun->width != 32) + return -ENOSYS; + + fun->last_ctrl = NAND_CLE; + + chip->priv = fun; + chip->chip_delay = fun->chip_delay; + chip->ecc.mode = NAND_ECC_SOFT; + chip->cmd_ctrl = fun_cmd_ctrl; +#if CONFIG_SYS_NAND_MAX_CHIPS > 1 + chip->select_chip = fun_select_chip; +#endif + chip->read_byte = upm_nand_read_byte; + chip->read_buf = upm_nand_read_buf; + chip->write_buf = upm_nand_write_buf; + chip->verify_buf = upm_nand_verify_buf; + if (fun->dev_ready) + chip->dev_ready = nand_dev_ready; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/fsmc_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/fsmc_nand.c new file mode 100644 index 000000000..567eff091 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/fsmc_nand.c @@ -0,0 +1,473 @@ +/* + * (C) Copyright 2010 + * Vipin Kumar, ST Microelectronics, vipin.kumar@st.com. + * + * (C) Copyright 2012 + * Amit Virdi, ST Microelectronics, amit.virdi@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static u32 fsmc_version; +static struct fsmc_regs *const fsmc_regs_p = (struct fsmc_regs *) + CONFIG_SYS_FSMC_BASE; + +/* + * ECC4 and ECC1 have 13 bytes and 3 bytes of ecc respectively for 512 bytes of + * data. ECC4 can correct up to 8 bits in 512 bytes of data while ECC1 can + * correct 1 bit in 512 bytes + */ + +static struct nand_ecclayout fsmc_ecc4_lp_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 1} + } +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_224_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 97} + } +}; + +/* + * ECC placement definitions in oobfree type format + * There are 13 bytes of ecc for every 512 byte block and it has to be read + * consecutively and immediately after the 512 byte data block for hardware to + * generate the error bit offsets in 512 byte data + * Managing the ecc bytes in the following way makes it easier for software to + * read ecc bytes consecutive to data bytes. This way is similar to + * oobfree structure maintained already in u-boot nand driver + */ +static struct fsmc_eccplace fsmc_eccpl_lp = { + .eccplace = { + {.offset = 2, .length = 13}, + {.offset = 18, .length = 13}, + {.offset = 34, .length = 13}, + {.offset = 50, .length = 13}, + {.offset = 66, .length = 13}, + {.offset = 82, .length = 13}, + {.offset = 98, .length = 13}, + {.offset = 114, .length = 13} + } +}; + +static struct nand_ecclayout fsmc_ecc4_sp_layout = { + .eccbytes = 13, + .eccpos = { 0, 1, 2, 3, 6, 7, 8, + 9, 10, 11, 12, 13, 14 + }, + .oobfree = { + {.offset = 15, .length = 1}, + } +}; + +static struct fsmc_eccplace fsmc_eccpl_sp = { + .eccplace = { + {.offset = 0, .length = 4}, + {.offset = 6, .length = 9} + } +}; + +static struct nand_ecclayout fsmc_ecc1_layout = { + .eccbytes = 24, + .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, + 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, + .oobfree = { + {.offset = 8, .length = 8}, + {.offset = 24, .length = 8}, + {.offset = 40, .length = 8}, + {.offset = 56, .length = 8}, + {.offset = 72, .length = 8}, + {.offset = 88, .length = 8}, + {.offset = 104, .length = 8}, + {.offset = 120, .length = 8} + } +}; + +/* Count the number of 0's in buff upto a max of max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static void fsmc_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) +{ + struct nand_chip *this = mtd->priv; + ulong IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + IO_ADDR_W = (ulong)this->IO_ADDR_W; + + IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE); + if (ctrl & NAND_CLE) + IO_ADDR_W |= CONFIG_SYS_NAND_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= CONFIG_SYS_NAND_ALE; + + if (ctrl & NAND_NCE) { + writel(readl(&fsmc_regs_p->pc) | + FSMC_ENABLE, &fsmc_regs_p->pc); + } else { + writel(readl(&fsmc_regs_p->pc) & + ~FSMC_ENABLE, &fsmc_regs_p->pc); + } + this->IO_ADDR_W = (void *)IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +static int fsmc_bch8_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + /* The calculated ecc is actually the correction index in data */ + u32 err_idx[8]; + u32 num_err, i; + u32 ecc1, ecc2, ecc3, ecc4; + + num_err = (readl(&fsmc_regs_p->sts) >> 10) & 0xF; + + if (likely(num_err == 0)) + return 0; + + if (unlikely(num_err > 8)) { + /* + * This is a temporary erase check. A newly erased page read + * would result in an ecc error because the oob data is also + * erased to FF and the calculated ecc for an FF data is not + * FF..FF. + * This is a workaround to skip performing correction in case + * data is FF..FF + * + * Logic: + * For every page, each bit written as 0 is counted until these + * number of bits are greater than 8 (the maximum correction + * capability of FSMC for each 512 + 13 bytes) + */ + + int bits_ecc = count_written_bits(read_ecc, 13, 8); + int bits_data = count_written_bits(dat, 512, 8); + + if ((bits_ecc + bits_data) <= 8) { + if (bits_data) + memset(dat, 0xff, 512); + return bits_data + bits_ecc; + } + + return -EBADMSG; + } + + ecc1 = readl(&fsmc_regs_p->ecc1); + ecc2 = readl(&fsmc_regs_p->ecc2); + ecc3 = readl(&fsmc_regs_p->ecc3); + ecc4 = readl(&fsmc_regs_p->sts); + + err_idx[0] = (ecc1 >> 0) & 0x1FFF; + err_idx[1] = (ecc1 >> 13) & 0x1FFF; + err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); + err_idx[3] = (ecc2 >> 7) & 0x1FFF; + err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); + err_idx[5] = (ecc3 >> 1) & 0x1FFF; + err_idx[6] = (ecc3 >> 14) & 0x1FFF; + err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); + + i = 0; + while (i < num_err) { + err_idx[i] ^= 3; + + if (err_idx[i] < 512 * 8) + __change_bit(err_idx[i], dat); + + i++; + } + + return num_err; +} + +static int fsmc_read_hwecc(struct mtd_info *mtd, + const u_char *data, u_char *ecc) +{ + u_int ecc_tmp; + int timeout = CONFIG_SYS_HZ; + ulong start; + + switch (fsmc_version) { + case FSMC_VER8: + start = get_timer(0); + while (get_timer(start) < timeout) { + /* + * Busy waiting for ecc computation + * to finish for 512 bytes + */ + if (readl(&fsmc_regs_p->sts) & FSMC_CODE_RDY) + break; + } + + ecc_tmp = readl(&fsmc_regs_p->ecc1); + ecc[0] = (u_char) (ecc_tmp >> 0); + ecc[1] = (u_char) (ecc_tmp >> 8); + ecc[2] = (u_char) (ecc_tmp >> 16); + ecc[3] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->ecc2); + ecc[4] = (u_char) (ecc_tmp >> 0); + ecc[5] = (u_char) (ecc_tmp >> 8); + ecc[6] = (u_char) (ecc_tmp >> 16); + ecc[7] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->ecc3); + ecc[8] = (u_char) (ecc_tmp >> 0); + ecc[9] = (u_char) (ecc_tmp >> 8); + ecc[10] = (u_char) (ecc_tmp >> 16); + ecc[11] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->sts); + ecc[12] = (u_char) (ecc_tmp >> 16); + break; + + default: + ecc_tmp = readl(&fsmc_regs_p->ecc1); + ecc[0] = (u_char) (ecc_tmp >> 0); + ecc[1] = (u_char) (ecc_tmp >> 8); + ecc[2] = (u_char) (ecc_tmp >> 16); + break; + } + + return 0; +} + +void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCPLEN_256, + &fsmc_regs_p->pc); + writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCEN, + &fsmc_regs_p->pc); + writel(readl(&fsmc_regs_p->pc) | FSMC_ECCEN, + &fsmc_regs_p->pc); +} + +/* + * fsmc_read_page_hwecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi + * @page: page number to read + * + * This routine is needed for fsmc verison 8 as reading from NAND chip has to be + * performed in a strict sequence as follows: + * data(512 byte) -> ecc(13 byte) + * After this read, fsmc hardware generates and reports error data bits(upto a + * max of 8 bits) + */ +static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct fsmc_eccplace *fsmc_eccpl; + int i, j, s, stat, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + int off, len, group = 0; + uint8_t oob[13] __attribute__ ((aligned (2))); + + /* Differentiate between small and large page ecc place definitions */ + if (mtd->writesize == 512) + fsmc_eccpl = &fsmc_eccpl_sp; + else + fsmc_eccpl = &fsmc_eccpl_lp; + + for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { + + chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + for (j = 0; j < eccbytes;) { + off = fsmc_eccpl->eccplace[group].offset; + len = fsmc_eccpl->eccplace[group].length; + group++; + + /* + * length is intentionally kept a higher multiple of 2 + * to read at least 13 bytes even in case of 16 bit NAND + * devices + */ + if (chip->options & NAND_BUSWIDTH_16) + len = roundup(len, 2); + chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); + chip->read_buf(mtd, oob + j, len); + j += len; + } + + memcpy(&ecc_code[i], oob, 13); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], + &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + + return 0; +} + +int fsmc_nand_init(struct nand_chip *nand) +{ + static int chip_nr; + struct mtd_info *mtd; + int i; + u32 peripid2 = readl(&fsmc_regs_p->peripid2); + + fsmc_version = (peripid2 >> FSMC_REVISION_SHFT) & + FSMC_REVISION_MSK; + + writel(readl(&fsmc_regs_p->ctrl) | FSMC_WP, &fsmc_regs_p->ctrl); + +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) + writel(FSMC_DEVWID_16 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->pc); +#elif defined(CONFIG_SYS_FSMC_NAND_8BIT) + writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->pc); +#else +#error Please define CONFIG_SYS_FSMC_NAND_16BIT or CONFIG_SYS_FSMC_NAND_8BIT +#endif + writel(readl(&fsmc_regs_p->pc) | FSMC_TCLR_1 | FSMC_TAR_1, + &fsmc_regs_p->pc); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->comm); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->attrib); + + nand->options = 0; +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) + nand->options |= NAND_BUSWIDTH_16; +#endif + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 512; + nand->ecc.calculate = fsmc_read_hwecc; + nand->ecc.hwctl = fsmc_enable_hwecc; + nand->cmd_ctrl = fsmc_nand_hwcontrol; + nand->IO_ADDR_R = nand->IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + nand->badblockbits = 7; + + mtd = &nand_info[chip_nr++]; + mtd->priv = nand; + + switch (fsmc_version) { + case FSMC_VER8: + nand->ecc.bytes = 13; + nand->ecc.strength = 8; + nand->ecc.correct = fsmc_bch8_correct_data; + nand->ecc.read_page = fsmc_read_page_hwecc; + if (mtd->writesize == 512) + nand->ecc.layout = &fsmc_ecc4_sp_layout; + else { + if (mtd->oobsize == 224) + nand->ecc.layout = &fsmc_ecc4_224_layout; + else + nand->ecc.layout = &fsmc_ecc4_lp_layout; + } + + break; + default: + nand->ecc.bytes = 3; + nand->ecc.strength = 1; + nand->ecc.layout = &fsmc_ecc1_layout; + nand->ecc.correct = nand_correct_data; + break; + } + + /* Detect NAND chips */ + if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) + return -ENXIO; + + if (nand_scan_tail(mtd)) + return -ENXIO; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + if (nand_register(i)) + return -ENXIO; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/jz4740_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/jz4740_nand.c new file mode 100644 index 000000000..7a62cc336 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/jz4740_nand.c @@ -0,0 +1,259 @@ +/* + * Platform independend driver for JZ4740. + * + * Copyright (c) 2007 Ingenic Semiconductor Inc. + * Author: + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include + +#include +#include +#include + +#define JZ_NAND_DATA_ADDR ((void __iomem *)0xB8000000) +#define JZ_NAND_CMD_ADDR (JZ_NAND_DATA_ADDR + 0x8000) +#define JZ_NAND_ADDR_ADDR (JZ_NAND_DATA_ADDR + 0x10000) + +#define BIT(x) (1 << (x)) +#define JZ_NAND_ECC_CTRL_ENCODING BIT(3) +#define JZ_NAND_ECC_CTRL_RS BIT(2) +#define JZ_NAND_ECC_CTRL_RESET BIT(1) +#define JZ_NAND_ECC_CTRL_ENABLE BIT(0) + +#define EMC_SMCR1_OPT_NAND 0x094c4400 +/* Optimize the timing of nand */ + +static struct jz4740_emc * emc = (struct jz4740_emc *)JZ4740_EMC_BASE; + +static struct nand_ecclayout qi_lb60_ecclayout_2gb = { + .eccbytes = 72, + .eccpos = { + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83 }, + .oobfree = { + {.offset = 2, + .length = 10 }, + {.offset = 84, + .length = 44 } } +}; + +static int is_reading; + +static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + uint32_t reg; + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_ALE) + this->IO_ADDR_W = JZ_NAND_ADDR_ADDR; + else if (ctrl & NAND_CLE) + this->IO_ADDR_W = JZ_NAND_CMD_ADDR; + else + this->IO_ADDR_W = JZ_NAND_DATA_ADDR; + + reg = readl(&emc->nfcsr); + if (ctrl & NAND_NCE) + reg |= EMC_NFCSR_NFCE1; + else + reg &= ~EMC_NFCSR_NFCE1; + writel(reg, &emc->nfcsr); + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +static int jz_nand_device_ready(struct mtd_info *mtd) +{ + return (readl(GPIO_PXPIN(2)) & 0x40000000) ? 1 : 0; +} + +void board_nand_select_device(struct nand_chip *nand, int chip) +{ + /* + * Don't use "chip" to address the NAND device, + * generate the cs from the address where it is encoded. + */ +} + +static int jz_nand_rs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + uint32_t status; + int i; + + if (is_reading) + return 0; + + do { + status = readl(&emc->nfints); + } while (!(status & EMC_NFINTS_ENCF)); + + /* disable ecc */ + writel(readl(&emc->nfecr) & ~EMC_NFECR_ECCE, &emc->nfecr); + + for (i = 0; i < 9; i++) + ecc_code[i] = readb(&emc->nfpar[i]); + + return 0; +} + +static void jz_nand_hwctl(struct mtd_info *mtd, int mode) +{ + uint32_t reg; + + writel(0, &emc->nfints); + reg = readl(&emc->nfecr); + reg |= JZ_NAND_ECC_CTRL_RESET; + reg |= JZ_NAND_ECC_CTRL_ENABLE; + reg |= JZ_NAND_ECC_CTRL_RS; + + switch (mode) { + case NAND_ECC_READ: + reg &= ~JZ_NAND_ECC_CTRL_ENCODING; + is_reading = 1; + break; + case NAND_ECC_WRITE: + reg |= JZ_NAND_ECC_CTRL_ENCODING; + is_reading = 0; + break; + default: + break; + } + + writel(reg, &emc->nfecr); +} + +/* Correct 1~9-bit errors in 512-bytes data */ +static void jz_rs_correct(unsigned char *dat, int idx, int mask) +{ + int i; + + idx--; + + i = idx + (idx >> 3); + if (i >= 512) + return; + + mask <<= (idx & 0x7); + + dat[i] ^= mask & 0xff; + if (i < 511) + dat[i + 1] ^= (mask >> 8) & 0xff; +} + +static int jz_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + int k; + uint32_t errcnt, index, mask, status; + + /* Set PAR values */ + const uint8_t all_ff_ecc[] = { + 0xcd, 0x9d, 0x90, 0x58, 0xf4, 0x8b, 0xff, 0xb7, 0x6f }; + + if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && + read_ecc[2] == 0xff && read_ecc[3] == 0xff && + read_ecc[4] == 0xff && read_ecc[5] == 0xff && + read_ecc[6] == 0xff && read_ecc[7] == 0xff && + read_ecc[8] == 0xff) { + for (k = 0; k < 9; k++) + writeb(all_ff_ecc[k], &emc->nfpar[k]); + } else { + for (k = 0; k < 9; k++) + writeb(read_ecc[k], &emc->nfpar[k]); + } + /* Set PRDY */ + writel(readl(&emc->nfecr) | EMC_NFECR_PRDY, &emc->nfecr); + + /* Wait for completion */ + do { + status = readl(&emc->nfints); + } while (!(status & EMC_NFINTS_DECF)); + + /* disable ecc */ + writel(readl(&emc->nfecr) & ~EMC_NFECR_ECCE, &emc->nfecr); + + /* Check decoding */ + if (!(status & EMC_NFINTS_ERR)) + return 0; + + if (status & EMC_NFINTS_UNCOR) { + printf("uncorrectable ecc\n"); + return -1; + } + + errcnt = (status & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT; + + switch (errcnt) { + case 4: + index = (readl(&emc->nferr[3]) & EMC_NFERR_INDEX_MASK) >> + EMC_NFERR_INDEX_BIT; + mask = (readl(&emc->nferr[3]) & EMC_NFERR_MASK_MASK) >> + EMC_NFERR_MASK_BIT; + jz_rs_correct(dat, index, mask); + case 3: + index = (readl(&emc->nferr[2]) & EMC_NFERR_INDEX_MASK) >> + EMC_NFERR_INDEX_BIT; + mask = (readl(&emc->nferr[2]) & EMC_NFERR_MASK_MASK) >> + EMC_NFERR_MASK_BIT; + jz_rs_correct(dat, index, mask); + case 2: + index = (readl(&emc->nferr[1]) & EMC_NFERR_INDEX_MASK) >> + EMC_NFERR_INDEX_BIT; + mask = (readl(&emc->nferr[1]) & EMC_NFERR_MASK_MASK) >> + EMC_NFERR_MASK_BIT; + jz_rs_correct(dat, index, mask); + case 1: + index = (readl(&emc->nferr[0]) & EMC_NFERR_INDEX_MASK) >> + EMC_NFERR_INDEX_BIT; + mask = (readl(&emc->nferr[0]) & EMC_NFERR_MASK_MASK) >> + EMC_NFERR_MASK_BIT; + jz_rs_correct(dat, index, mask); + default: + break; + } + + return errcnt; +} + +/* + * Main initialization routine + */ +int board_nand_init(struct nand_chip *nand) +{ + uint32_t reg; + + reg = readl(&emc->nfcsr); + reg |= EMC_NFCSR_NFE1; /* EMC setup, Set NFE bit */ + writel(reg, &emc->nfcsr); + + writel(EMC_SMCR1_OPT_NAND, &emc->smcr[1]); + + nand->IO_ADDR_R = JZ_NAND_DATA_ADDR; + nand->IO_ADDR_W = JZ_NAND_DATA_ADDR; + nand->cmd_ctrl = jz_nand_cmd_ctrl; + nand->dev_ready = jz_nand_device_ready; + nand->ecc.hwctl = jz_nand_hwctl; + nand->ecc.correct = jz_nand_rs_correct_data; + nand->ecc.calculate = jz_nand_rs_calculate_ecc; + nand->ecc.mode = NAND_ECC_HW_OOB_FIRST; + nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; + nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; + nand->ecc.strength = 4; + nand->ecc.layout = &qi_lb60_ecclayout_2gb; + nand->chip_delay = 50; + nand->bbt_options |= NAND_BBT_USE_FLASH; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/kb9202_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/kb9202_nand.c new file mode 100644 index 000000000..22c562540 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/kb9202_nand.c @@ -0,0 +1,134 @@ +/* + * (C) Copyright 2006 + * KwikByte + * + * (C) Copyright 2009 + * Matthias Kaehlcke + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#include + +/* + * hardware specific access to control-lines + */ + +#define MASK_ALE (1 << 22) /* our ALE is A22 */ +#define MASK_CLE (1 << 21) /* our CLE is A21 */ + +#define KB9202_NAND_NCE (1 << 28) /* EN* on D28 */ +#define KB9202_NAND_BUSY (1 << 29) /* RB* on D29 */ + +#define KB9202_SMC2_NWS (1 << 2) +#define KB9202_SMC2_TDF (1 << 8) +#define KB9202_SMC2_RWSETUP (1 << 24) +#define KB9202_SMC2_RWHOLD (1 << 29) + +/* + * Board-specific function to access device control signals + */ +static void kb9202_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + + /* clear ALE and CLE bits */ + IO_ADDR_W &= ~(MASK_ALE | MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= MASK_CLE; + + if (ctrl & NAND_ALE) + IO_ADDR_W |= MASK_ALE; + + this->IO_ADDR_W = (void *) IO_ADDR_W; + + if (ctrl & NAND_NCE) + writel(KB9202_NAND_NCE, AT91C_PIOC_CODR); + else + writel(KB9202_NAND_NCE, AT91C_PIOC_SODR); + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + + +/* + * Board-specific function to access the device ready signal. + */ +static int kb9202_nand_ready(struct mtd_info *mtd) +{ + return readl(AT91C_PIOC_PDSR) & KB9202_NAND_BUSY; +} + + +/* + * Board-specific NAND init. Copied from include/linux/mtd/nand.h for reference. + * + * struct nand_chip - NAND Private Flash Chip Data + * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device + * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device + * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines + * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line + * If set to NULL no access to ready/busy is available and the ready/busy information + * is read from the chip status register + * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only + * be provided if a hardware ECC is available + * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines + * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) + * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about + * special functionality. See the defines for further explanation +*/ +/* + * This routine initializes controller and GPIOs. + */ +int board_nand_init(struct nand_chip *nand) +{ + unsigned int value; + + nand->ecc.mode = NAND_ECC_SOFT; + nand->cmd_ctrl = kb9202_nand_hwcontrol; + nand->dev_ready = kb9202_nand_ready; + + /* in case running outside of bootloader */ + writel(1 << AT91C_ID_PIOC, AT91C_PMC_PCER); + + /* setup nand flash access (allow ample margin) */ + /* 4 wait states, 1 setup, 1 hold, 1 float for 8-bit device */ + writel(AT91C_SMC2_WSEN | KB9202_SMC2_NWS | KB9202_SMC2_TDF | + AT91C_SMC2_DBW_8 | KB9202_SMC2_RWSETUP | KB9202_SMC2_RWHOLD, + AT91C_SMC_CSR3); + + /* enable internal NAND controller */ + value = readl(AT91C_EBI_CSA); + value |= AT91C_EBI_CS3A_SMC_SmartMedia; + writel(value, AT91C_EBI_CSA); + + /* enable SMOE/SMWE */ + writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_ASR); + writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_PDR); + writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_OER); + + /* set NCE to high */ + writel(KB9202_NAND_NCE, AT91C_PIOC_SODR); + + /* disable output on pin connected to the busy line of the NAND */ + writel(KB9202_NAND_BUSY, AT91C_PIOC_ODR); + + /* enable the PIO to control NCE and BUSY */ + writel(KB9202_NAND_NCE | KB9202_NAND_BUSY, AT91C_PIOC_PER); + + /* enable output for NCE */ + writel(KB9202_NAND_NCE, AT91C_PIOC_OER); + + return (0); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/kirkwood_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/kirkwood_nand.c new file mode 100644 index 000000000..72687a1da --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/kirkwood_nand.c @@ -0,0 +1,70 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* NAND Flash Soc registers */ +struct kwnandf_registers { + u32 rd_params; /* 0x10418 */ + u32 wr_param; /* 0x1041c */ + u8 pad[0x10470 - 0x1041c - 4]; + u32 ctrl; /* 0x10470 */ +}; + +static struct kwnandf_registers *nf_reg = + (struct kwnandf_registers *)KW_NANDF_BASE; + +/* + * hardware specific access to control-lines/bits + */ +#define NAND_ACTCEBOOT_BIT 0x02 + +static void kw_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *nc = mtd->priv; + u32 offs; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + offs = (1 << 0); /* Commands with A[1:0] == 01 */ + else if (ctrl & NAND_ALE) + offs = (1 << 1); /* Addresses with A[1:0] == 10 */ + else + return; + + writeb(cmd, nc->IO_ADDR_W + offs); +} + +void kw_nand_select_chip(struct mtd_info *mtd, int chip) +{ + u32 data; + + data = readl(&nf_reg->ctrl); + data |= NAND_ACTCEBOOT_BIT; + writel(data, &nf_reg->ctrl); +} + +int board_nand_init(struct nand_chip *nand) +{ + nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING; +#if defined(CONFIG_NAND_ECC_BCH) + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif + nand->cmd_ctrl = kw_nand_hwcontrol; + nand->chip_delay = 40; + nand->select_chip = kw_nand_select_chip; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/kmeter1_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/kmeter1_nand.c new file mode 100644 index 000000000..df0bde579 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/kmeter1_nand.c @@ -0,0 +1,123 @@ +/* + * (C) Copyright 2009 + * Heiko Schocher, DENX Software Engineering, hs@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define CONFIG_NAND_MODE_REG (void *)(CONFIG_SYS_NAND_BASE + 0x20000) +#define CONFIG_NAND_DATA_REG (void *)(CONFIG_SYS_NAND_BASE + 0x30000) + +#define read_mode() in_8(CONFIG_NAND_MODE_REG) +#define write_mode(val) out_8(CONFIG_NAND_MODE_REG, val) +#define read_data() in_8(CONFIG_NAND_DATA_REG) +#define write_data(val) out_8(CONFIG_NAND_DATA_REG, val) + +#define KPN_RDY2 (1 << 7) +#define KPN_RDY1 (1 << 6) +#define KPN_WPN (1 << 4) +#define KPN_CE2N (1 << 3) +#define KPN_CE1N (1 << 2) +#define KPN_ALE (1 << 1) +#define KPN_CLE (1 << 0) + +#define KPN_DEFAULT_CHIP_DELAY 50 + +static int kpn_chip_ready(void) +{ + if (read_mode() & KPN_RDY1) + return 1; + + return 0; +} + +static void kpn_wait_rdy(void) +{ + int cnt = 1000000; + + while (--cnt && !kpn_chip_ready()) + udelay(1); + + if (!cnt) + printf ("timeout while waiting for RDY\n"); +} + +static void kpn_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + u8 reg_val = read_mode(); + + if (ctrl & NAND_CTRL_CHANGE) { + reg_val = reg_val & ~(KPN_ALE + KPN_CLE); + + if (ctrl & NAND_CLE) + reg_val = reg_val | KPN_CLE; + if (ctrl & NAND_ALE) + reg_val = reg_val | KPN_ALE; + if (ctrl & NAND_NCE) + reg_val = reg_val & ~KPN_CE1N; + else + reg_val = reg_val | KPN_CE1N; + + write_mode(reg_val); + } + if (cmd != NAND_CMD_NONE) + write_data(cmd); + + /* wait until flash is ready */ + kpn_wait_rdy(); +} + +static u_char kpn_nand_read_byte(struct mtd_info *mtd) +{ + return read_data(); +} + +static void kpn_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + write_data(buf[i]); + kpn_wait_rdy(); + } +} + +static void kpn_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + buf[i] = read_data(); +} + +static int kpn_nand_dev_ready(struct mtd_info *mtd) +{ + kpn_wait_rdy(); + + return 1; +} + +int board_nand_init(struct nand_chip *nand) +{ +#if defined(CONFIG_NAND_ECC_BCH) + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif + + /* Reference hardware control function */ + nand->cmd_ctrl = kpn_nand_hwcontrol; + nand->read_byte = kpn_nand_read_byte; + nand->write_buf = kpn_nand_write_buf; + nand->read_buf = kpn_nand_read_buf; + nand->dev_ready = kpn_nand_dev_ready; + nand->chip_delay = KPN_DEFAULT_CHIP_DELAY; + + /* reset mode register */ + write_mode(KPN_CE1N + KPN_CE2N + KPN_WPN); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/mpc5121_nfc.c b/qemu/roms/u-boot/drivers/mtd/nand/mpc5121_nfc.c new file mode 100644 index 000000000..d0f3a3532 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/mpc5121_nfc.c @@ -0,0 +1,681 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. + * Copyright 2009 Semihalf. + * (C) Copyright 2009 Stefan Roese + * + * Based on original driver from Freescale Semiconductor + * written by John Rigby on basis + * of drivers/mtd/nand/mxc_nand.c. Reworked and extended + * Piotr Ziecik . + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DRV_NAME "mpc5121_nfc" + +/* Timeouts */ +#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ +#define NFC_TIMEOUT 2000 /* 2000 us */ + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x200) + +/* Addresses for NFC SPARE BUFFER areas */ +#define NFC_SPARE_BUFFERS 8 +#define NFC_SPARE_LEN 0x40 +#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN)) + +/* MPC5121 NFC registers */ +#define NFC_BUF_ADDR 0x1E04 +#define NFC_FLASH_ADDR 0x1E06 +#define NFC_FLASH_CMD 0x1E08 +#define NFC_CONFIG 0x1E0A +#define NFC_ECC_STATUS1 0x1E0C +#define NFC_ECC_STATUS2 0x1E0E +#define NFC_SPAS 0x1E10 +#define NFC_WRPROT 0x1E12 +#define NFC_NF_WRPRST 0x1E18 +#define NFC_CONFIG1 0x1E1A +#define NFC_CONFIG2 0x1E1C +#define NFC_UNLOCKSTART_BLK0 0x1E20 +#define NFC_UNLOCKEND_BLK0 0x1E22 +#define NFC_UNLOCKSTART_BLK1 0x1E24 +#define NFC_UNLOCKEND_BLK1 0x1E26 +#define NFC_UNLOCKSTART_BLK2 0x1E28 +#define NFC_UNLOCKEND_BLK2 0x1E2A +#define NFC_UNLOCKSTART_BLK3 0x1E2C +#define NFC_UNLOCKEND_BLK3 0x1E2E + +/* Bit Definitions: NFC_BUF_ADDR */ +#define NFC_RBA_MASK (7 << 0) +#define NFC_ACTIVE_CS_SHIFT 5 +#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT) + +/* Bit Definitions: NFC_CONFIG */ +#define NFC_BLS_UNLOCKED (1 << 1) + +/* Bit Definitions: NFC_CONFIG1 */ +#define NFC_ECC_4BIT (1 << 0) +#define NFC_FULL_PAGE_DMA (1 << 1) +#define NFC_SPARE_ONLY (1 << 2) +#define NFC_ECC_ENABLE (1 << 3) +#define NFC_INT_MASK (1 << 4) +#define NFC_BIG_ENDIAN (1 << 5) +#define NFC_RESET (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_PPB_32 (0 << 9) +#define NFC_PPB_64 (1 << 9) +#define NFC_PPB_128 (2 << 9) +#define NFC_PPB_256 (3 << 9) +#define NFC_PPB_MASK (3 << 9) +#define NFC_FULL_PAGE_INT (1 << 11) + +/* Bit Definitions: NFC_CONFIG2 */ +#define NFC_COMMAND (1 << 0) +#define NFC_ADDRESS (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) +#define NFC_CMD_FAIL (1 << 15) +#define NFC_INT (1 << 15) + +/* Bit Definitions: NFC_WRPROT */ +#define NFC_WPC_LOCK_TIGHT (1 << 0) +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) + +struct mpc5121_nfc_prv { + struct mtd_info mtd; + struct nand_chip chip; + int irq; + void __iomem *regs; + struct clk *clk; + uint column; + int spareonly; + int chipsel; +}; + +int mpc5121_nfc_chip = 0; + +static void mpc5121_nfc_done(struct mtd_info *mtd); + +/* Read NFC register */ +static inline u16 nfc_read(struct mtd_info *mtd, uint reg) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + return in_be16(prv->regs + reg); +} + +/* Write NFC register */ +static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + out_be16(prv->regs + reg, val); +} + +/* Set bits in NFC register */ +static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); +} + +/* Clear bits in NFC register */ +static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); +} + +/* Invoke address cycle */ +static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr) +{ + nfc_write(mtd, NFC_FLASH_ADDR, addr); + nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS); + mpc5121_nfc_done(mtd); +} + +/* Invoke command cycle */ +static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd) +{ + nfc_write(mtd, NFC_FLASH_CMD, cmd); + nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND); + mpc5121_nfc_done(mtd); +} + +/* Send data from NFC buffers to NAND flash */ +static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_INPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive data from NAND flash */ +static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive ID from NAND flash */ +static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_ID); + mpc5121_nfc_done(mtd); +} + +/* Receive status from NAND flash */ +static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_STATUS); + mpc5121_nfc_done(mtd); +} + +static void mpc5121_nfc_done(struct mtd_info *mtd) +{ + int max_retries = NFC_TIMEOUT; + + while (1) { + max_retries--; + if (nfc_read(mtd, NFC_CONFIG2) & NFC_INT) + break; + udelay(1); + } + + if (max_retries <= 0) + printk(KERN_WARNING DRV_NAME + ": Timeout while waiting for completion.\n"); +} + +/* Do address cycle(s) */ +static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) +{ + struct nand_chip *chip = mtd->priv; + u32 pagemask = chip->pagemask; + + if (column != -1) { + mpc5121_nfc_send_addr(mtd, column); + if (mtd->writesize > 512) + mpc5121_nfc_send_addr(mtd, column >> 8); + } + + if (page != -1) { + do { + mpc5121_nfc_send_addr(mtd, page & 0xFF); + page >>= 8; + pagemask >>= 8; + } while (pagemask); + } +} + +/* Control chip select signals */ + +/* + * Selecting the active device: + * + * This is different than the linux version. Switching between chips + * is done via board_nand_select_device(). The Linux select_chip + * function used here in U-Boot has only 2 valid chip numbers: + * 0 select + * -1 deselect + */ + +/* + * Implement it as a weak default, so that boards with a specific + * chip-select routine can use their own function. + */ +void __mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + if (chip < 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_CE); + return; + } + + nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) & + NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_CONFIG1, NFC_CE); +} +void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) + __attribute__((weak, alias("__mpc5121_nfc_select_chip"))); + +void board_nand_select_device(struct nand_chip *nand, int chip) +{ + /* + * Only save this chip number in global variable here. This + * will be used later in mpc5121_nfc_select_chip(). + */ + mpc5121_nfc_chip = chip; +} + +/* Read NAND Ready/Busy signal */ +static int mpc5121_nfc_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles ready/busy signal internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +/* Write command to NAND flash */ +static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + prv->column = (column >= 0) ? column : 0; + prv->spareonly = 0; + + switch (command) { + case NAND_CMD_PAGEPROG: + mpc5121_nfc_send_prog_page(mtd); + break; + /* + * NFC does not support sub-page reads and writes, + * so emulate them using full page transfers. + */ + case NAND_CMD_READ0: + column = 0; + break; + + case NAND_CMD_READ1: + prv->column += 256; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_READOOB: + prv->spareonly = 1; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_SEQIN: + mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page); + column = 0; + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_READID: + case NAND_CMD_STATUS: + case NAND_CMD_RESET: + break; + + default: + return; + } + + mpc5121_nfc_send_cmd(mtd, command); + mpc5121_nfc_addr_cycle(mtd, column, page); + + switch (command) { + case NAND_CMD_READ0: + if (mtd->writesize > 512) + mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART); + mpc5121_nfc_send_read_page(mtd); + break; + + case NAND_CMD_READID: + mpc5121_nfc_send_read_id(mtd); + break; + + case NAND_CMD_STATUS: + mpc5121_nfc_send_read_status(mtd); + if (chip->options & NAND_BUSWIDTH_16) + prv->column = 1; + else + prv->column = 0; + break; + } +} + +/* Copy data from/to NFC spare buffers. */ +static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset, + u8 * buffer, uint size, int wr) +{ + struct nand_chip *nand = mtd->priv; + struct mpc5121_nfc_prv *prv = nand->priv; + uint o, s, sbsize, blksize; + + /* + * NAND spare area is available through NFC spare buffers. + * The NFC divides spare area into (page_size / 512) chunks. + * Each chunk is placed into separate spare memory area, using + * first (spare_size / num_of_chunks) bytes of the buffer. + * + * For NAND device in which the spare area is not divided fully + * by the number of chunks, number of used bytes in each spare + * buffer is rounded down to the nearest even number of bytes, + * and all remaining bytes are added to the last used spare area. + * + * For more information read section 26.6.10 of MPC5121e + * Microcontroller Reference Manual, Rev. 3. + */ + + /* Calculate number of valid bytes in each spare buffer */ + sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1; + + while (size) { + /* Calculate spare buffer number */ + s = offset / sbsize; + if (s > NFC_SPARE_BUFFERS - 1) + s = NFC_SPARE_BUFFERS - 1; + + /* + * Calculate offset to requested data block in selected spare + * buffer and its size. + */ + o = offset - (s * sbsize); + blksize = min(sbsize - o, size); + + if (wr) + memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, + buffer, blksize); + else + memcpy_fromio(buffer, + prv->regs + NFC_SPARE_AREA(s) + o, + blksize); + + buffer += blksize; + offset += blksize; + size -= blksize; + }; +} + +/* Copy data from/to NFC main and spare buffers */ +static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char * buf, int len, + int wr) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + uint c = prv->column; + uint l; + + /* Handle spare area access */ + if (prv->spareonly || c >= mtd->writesize) { + /* Calculate offset from beginning of spare area */ + if (c >= mtd->writesize) + c -= mtd->writesize; + + prv->column += len; + mpc5121_nfc_copy_spare(mtd, c, buf, len, wr); + return; + } + + /* + * Handle main area access - limit copy length to prevent + * crossing main/spare boundary. + */ + l = min((uint) len, mtd->writesize - c); + prv->column += l; + + if (wr) + memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); + else + memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); + + /* Handle crossing main/spare boundary */ + if (l != len) { + buf += l; + len -= l; + mpc5121_nfc_buf_copy(mtd, buf, len, wr); + } +} + +/* Read data from NFC buffers */ +static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, buf, len, 0); +} + +/* Write data to NFC buffers */ +static void mpc5121_nfc_write_buf(struct mtd_info *mtd, + const u_char * buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, (u_char *) buf, len, 1); +} + +/* Compare buffer with NAND flash */ +static int mpc5121_nfc_verify_buf(struct mtd_info *mtd, + const u_char * buf, int len) +{ + u_char tmp[256]; + uint bsize; + + while (len) { + bsize = min(len, 256); + mpc5121_nfc_read_buf(mtd, tmp, bsize); + + if (memcmp(buf, tmp, bsize)) + return 1; + + buf += bsize; + len -= bsize; + } + + return 0; +} + +/* Read byte from NFC buffers */ +static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) +{ + u8 tmp; + + mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp)); + + return tmp; +} + +/* Read word from NFC buffers */ +static u16 mpc5121_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + mpc5121_nfc_read_buf(mtd, (u_char *) & tmp, sizeof(tmp)); + + return tmp; +} + +/* + * Read NFC configuration from Reset Config Word + * + * NFC is configured during reset in basis of information stored + * in Reset Config Word. There is no other way to set NAND block + * size, spare size and bus width. + */ +static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + struct nand_chip *chip = mtd->priv; + uint rcw_pagesize = 0; + uint rcw_sparesize = 0; + uint rcw_width; + uint rcwh; + uint romloc, ps; + + rcwh = in_be32(&(im->reset.rcwh)); + + /* Bit 6: NFC bus width */ + rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1; + + /* Bit 7: NFC Page/Spare size */ + ps = (rcwh >> 7) & 0x1; + + /* Bits [22:21]: ROM Location */ + romloc = (rcwh >> 21) & 0x3; + + /* Decode RCW bits */ + switch ((ps << 2) | romloc) { + case 0x00: + case 0x01: + rcw_pagesize = 512; + rcw_sparesize = 16; + break; + case 0x02: + case 0x03: + rcw_pagesize = 4096; + rcw_sparesize = 128; + break; + case 0x04: + case 0x05: + rcw_pagesize = 2048; + rcw_sparesize = 64; + break; + case 0x06: + case 0x07: + rcw_pagesize = 4096; + rcw_sparesize = 218; + break; + } + + mtd->writesize = rcw_pagesize; + mtd->oobsize = rcw_sparesize; + if (rcw_width == 2) + chip->options |= NAND_BUSWIDTH_16; + + debug(KERN_NOTICE DRV_NAME ": Configured for " + "%u-bit NAND, page size %u with %u spare.\n", + rcw_width * 8, rcw_pagesize, rcw_sparesize); + return 0; +} + +int board_nand_init(struct nand_chip *chip) +{ + struct mpc5121_nfc_prv *prv; + struct mtd_info *mtd; + int resettime = 0; + int retval = 0; + int rev; + static int chip_nr = 0; + + /* + * Check SoC revision. This driver supports only NFC + * in MPC5121 revision 2. + */ + rev = (mfspr(SPRN_SVR) >> 4) & 0xF; + if (rev != 2) { + printk(KERN_ERR DRV_NAME + ": SoC revision %u is not supported!\n", rev); + return -ENXIO; + } + + prv = malloc(sizeof(*prv)); + if (!prv) { + printk(KERN_ERR DRV_NAME ": Memory exhausted!\n"); + return -ENOMEM; + } + + mtd = &nand_info[chip_nr++]; + mtd->priv = chip; + chip->priv = prv; + + /* Read NFC configuration from Reset Config Word */ + retval = mpc5121_nfc_read_hw_config(mtd); + if (retval) { + printk(KERN_ERR DRV_NAME ": Unable to read NFC config!\n"); + return retval; + } + + prv->regs = (void __iomem *)CONFIG_SYS_NAND_BASE; + chip->dev_ready = mpc5121_nfc_dev_ready; + chip->cmdfunc = mpc5121_nfc_command; + chip->read_byte = mpc5121_nfc_read_byte; + chip->read_word = mpc5121_nfc_read_word; + chip->read_buf = mpc5121_nfc_read_buf; + chip->write_buf = mpc5121_nfc_write_buf; + chip->verify_buf = mpc5121_nfc_verify_buf; + chip->select_chip = mpc5121_nfc_select_chip; + chip->bbt_options = NAND_BBT_USE_FLASH; + chip->ecc.mode = NAND_ECC_SOFT; + + /* Reset NAND Flash controller */ + nfc_set(mtd, NFC_CONFIG1, NFC_RESET); + while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) { + if (resettime++ >= NFC_RESET_TIMEOUT) { + printk(KERN_ERR DRV_NAME + ": Timeout while resetting NFC!\n"); + retval = -EINVAL; + goto error; + } + + udelay(1); + } + + /* Enable write to NFC memory */ + nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED); + + /* Enable write to all NAND pages */ + nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000); + nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF); + nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK); + + /* + * Setup NFC: + * - Big Endian transfers, + * - Interrupt after full page read/write. + */ + nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK | + NFC_FULL_PAGE_INT); + + /* Set spare area size */ + nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1); + + /* Detect NAND chips */ + if (nand_scan(mtd, 1)) { + printk(KERN_ERR DRV_NAME ": NAND Flash not found !\n"); + retval = -ENXIO; + goto error; + } + + /* Set erase block size */ + switch (mtd->erasesize / mtd->writesize) { + case 32: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32); + break; + + case 64: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64); + break; + + case 128: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128); + break; + + case 256: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256); + break; + + default: + printk(KERN_ERR DRV_NAME ": Unsupported NAND flash!\n"); + retval = -ENXIO; + goto error; + } + + return 0; +error: + return retval; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.c new file mode 100644 index 000000000..ed0ca3aca --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.c @@ -0,0 +1,1342 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. + * Copyright 2008 Sascha Hauer, kernel@pengutronix.de + * Copyright 2009 Ilya Yanok, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35) || \ + defined(CONFIG_MX51) || defined(CONFIG_MX53) +#include +#endif +#include "mxc_nand.h" + +#define DRIVER_NAME "mxc_nand" + +struct mxc_nand_host { + struct mtd_info mtd; + struct nand_chip *nand; + + struct mxc_nand_regs __iomem *regs; +#ifdef MXC_NFC_V3_2 + struct mxc_nand_ip_regs __iomem *ip_regs; +#endif + int spare_only; + int status_request; + int pagesize_2k; + int clk_act; + uint16_t col_addr; + unsigned int page_addr; +}; + +static struct mxc_nand_host mxc_host; +static struct mxc_nand_host *host = &mxc_host; + +/* Define delays in microsec for NAND device operations */ +#define TROP_US_DELAY 2000 +/* Macros to get byte and bit positions of ECC */ +#define COLPOS(x) ((x) >> 3) +#define BITPOS(x) ((x) & 0xf) + +/* Define single bit Error positions in Main & Spare area */ +#define MAIN_SINGLEBIT_ERROR 0x4 +#define SPARE_SINGLEBIT_ERROR 0x1 + +/* OOB placement block for use with hardware ecc generation */ +#if defined(MXC_NFC_V1) +#ifndef CONFIG_SYS_NAND_LARGEPAGE +static struct nand_ecclayout nand_hw_eccoob = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = { {0, 5}, {11, 5}, } +}; +#else +static struct nand_ecclayout nand_hw_eccoob2k = { + .eccbytes = 20, + .eccpos = { + 6, 7, 8, 9, 10, + 22, 23, 24, 25, 26, + 38, 39, 40, 41, 42, + 54, 55, 56, 57, 58, + }, + .oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} }, +}; +#endif +#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) +#ifndef CONFIG_SYS_NAND_LARGEPAGE +static struct nand_ecclayout nand_hw_eccoob = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {2, 5} } +}; +#else +static struct nand_ecclayout nand_hw_eccoob2k = { + .eccbytes = 36, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15, + 23, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 40, 41, 42, 43, 44, 45, 46, 47, + 55, 56, 57, 58, 59, 60, 61, 62, 63, + }, + .oobfree = { {2, 5}, {16, 7}, {32, 7}, {48, 7} }, +}; +#endif +#endif + +static int is_16bit_nand(void) +{ +#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT) + return 1; +#else + return 0; +#endif +} + +static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size) +{ + uint32_t *d = dest; + + size >>= 2; + while (size--) + __raw_writel(__raw_readl(source++), d++); + return dest; +} + +/* + * This function polls the NANDFC to wait for the basic operation to + * complete by checking the INT bit. + */ +static void wait_op_done(struct mxc_nand_host *host, int max_retries, + uint16_t param) +{ + uint32_t tmp; + + while (max_retries-- > 0) { +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + tmp = readnfc(&host->regs->config2); + if (tmp & NFC_V1_V2_CONFIG2_INT) { + tmp &= ~NFC_V1_V2_CONFIG2_INT; + writenfc(tmp, &host->regs->config2); +#elif defined(MXC_NFC_V3_2) + tmp = readnfc(&host->ip_regs->ipc); + if (tmp & NFC_V3_IPC_INT) { + tmp &= ~NFC_V3_IPC_INT; + writenfc(tmp, &host->ip_regs->ipc); +#endif + break; + } + udelay(1); + } + if (max_retries < 0) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", + __func__, param); + } +} + +/* + * This function issues the specified command to the NAND device and + * waits for completion. + */ +static void send_cmd(struct mxc_nand_host *host, uint16_t cmd) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x)\n", cmd); + + writenfc(cmd, &host->regs->flash_cmd); + writenfc(NFC_CMD, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, cmd); +} + +/* + * This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. + */ +static void send_addr(struct mxc_nand_host *host, uint16_t addr) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x)\n", addr); + + writenfc(addr, &host->regs->flash_addr); + writenfc(NFC_ADDR, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, addr); +} + +/* + * This function requests the NANDFC to initiate the transfer + * of data currently in the NANDFC RAM buffer to the NAND device. + */ +static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only) +{ + if (spare_only) + MTDDEBUG(MTD_DEBUG_LEVEL1, "send_prog_page (%d)\n", spare_only); + + if (is_mxc_nfc_21() || is_mxc_nfc_32()) { + int i; + /* + * The controller copies the 64 bytes of spare data from + * the first 16 bytes of each of the 4 64 byte spare buffers. + * Copy the contiguous data starting in spare_area[0] to + * the four spare area buffers. + */ + for (i = 1; i < 4; i++) { + void __iomem *src = &host->regs->spare_area[0][i * 16]; + void __iomem *dst = &host->regs->spare_area[i][0]; + + mxc_nand_memcpy32(dst, src, 16); + } + } + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(buf_id, &host->regs->buf_addr); +#elif defined(MXC_NFC_V3_2) + uint32_t tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_V3_CONFIG1_RBA_MASK; + tmp |= NFC_V3_CONFIG1_RBA(buf_id); + writenfc(tmp, &host->regs->config1); +#endif + + /* Configure spare or page+spare access */ + if (!host->pagesize_2k) { + uint32_t config1 = readnfc(&host->regs->config1); + if (spare_only) + config1 |= NFC_CONFIG1_SP_EN; + else + config1 &= ~NFC_CONFIG1_SP_EN; + writenfc(config1, &host->regs->config1); + } + + writenfc(NFC_INPUT, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, spare_only); +} + +/* + * Requests NANDFC to initiate the transfer of data from the + * NAND device into in the NANDFC ram buffer. + */ +static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(buf_id, &host->regs->buf_addr); +#elif defined(MXC_NFC_V3_2) + uint32_t tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_V3_CONFIG1_RBA_MASK; + tmp |= NFC_V3_CONFIG1_RBA(buf_id); + writenfc(tmp, &host->regs->config1); +#endif + + /* Configure spare or page+spare access */ + if (!host->pagesize_2k) { + uint32_t config1 = readnfc(&host->regs->config1); + if (spare_only) + config1 |= NFC_CONFIG1_SP_EN; + else + config1 &= ~NFC_CONFIG1_SP_EN; + writenfc(config1, &host->regs->config1); + } + + writenfc(NFC_OUTPUT, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, spare_only); + + if (is_mxc_nfc_21() || is_mxc_nfc_32()) { + int i; + + /* + * The controller copies the 64 bytes of spare data to + * the first 16 bytes of each of the 4 spare buffers. + * Make the data contiguous starting in spare_area[0]. + */ + for (i = 1; i < 4; i++) { + void __iomem *src = &host->regs->spare_area[i][0]; + void __iomem *dst = &host->regs->spare_area[0][i * 16]; + + mxc_nand_memcpy32(dst, src, 16); + } + } +} + +/* Request the NANDFC to perform a read of the NAND device ID. */ +static void send_read_id(struct mxc_nand_host *host) +{ + uint32_t tmp; + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + /* NANDFC buffer 0 is used for device ID output */ + writenfc(0x0, &host->regs->buf_addr); +#elif defined(MXC_NFC_V3_2) + tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_V3_CONFIG1_RBA_MASK; + writenfc(tmp, &host->regs->config1); +#endif + + /* Read ID into main buffer */ + tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_CONFIG1_SP_EN; + writenfc(tmp, &host->regs->config1); + + writenfc(NFC_ID, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, 0); +} + +/* + * This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. + */ +static uint16_t get_dev_status(struct mxc_nand_host *host) +{ +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + void __iomem *main_buf = host->regs->main_area[1]; + uint32_t store; +#endif + uint32_t ret, tmp; + /* Issue status request to NAND device */ + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + /* store the main area1 first word, later do recovery */ + store = readl(main_buf); + /* NANDFC buffer 1 is used for device status */ + writenfc(1, &host->regs->buf_addr); +#endif + + /* Read status into main buffer */ + tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_CONFIG1_SP_EN; + writenfc(tmp, &host->regs->config1); + + writenfc(NFC_STATUS, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, 0); + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + /* + * Status is placed in first word of main buffer + * get status, then recovery area 1 data + */ + ret = readw(main_buf); + writel(store, main_buf); +#elif defined(MXC_NFC_V3_2) + ret = readnfc(&host->regs->config1) >> 16; +#endif + + return ret; +} + +/* This function is used by upper layer to checks if device is ready */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles R/B internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + uint16_t tmp = readnfc(&host->regs->config1); + + if (on) + tmp |= NFC_V1_V2_CONFIG1_ECC_EN; + else + tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN; + writenfc(tmp, &host->regs->config1); +#elif defined(MXC_NFC_V3_2) + uint32_t tmp = readnfc(&host->ip_regs->config2); + + if (on) + tmp |= NFC_V3_CONFIG2_ECC_EN; + else + tmp &= ~NFC_V3_CONFIG2_ECC_EN; + writenfc(tmp, &host->ip_regs->config2); +#endif +} + +#ifdef CONFIG_MXC_NAND_HWECC +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + /* + * If HW ECC is enabled, we turn it on during init. There is + * no need to enable again here. + */ +} + +#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) +static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + int page) +{ + struct mxc_nand_host *host = chip->priv; + uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *bufpoi = buf; + int i, toread; + + MTDDEBUG(MTD_DEBUG_LEVEL0, + "%s: Reading OOB area of page %u to oob %p\n", + __func__, page, buf); + + chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); + for (i = 0; i < chip->ecc.steps; i++) { + toread = min_t(int, length, chip->ecc.prepad); + if (toread) { + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + bufpoi += chip->ecc.bytes; + host->col_addr += chip->ecc.bytes; + length -= chip->ecc.bytes; + + toread = min_t(int, length, chip->ecc.postpad); + if (toread) { + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + } + if (length > 0) + chip->read_buf(mtd, bufpoi, length); + + _mxc_nand_enable_hwecc(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READOOB, + mtd->writesize + chip->ecc.prepad, page); + bufpoi = buf + chip->ecc.prepad; + length = mtd->oobsize - chip->ecc.prepad; + for (i = 0; i < chip->ecc.steps; i++) { + toread = min_t(int, length, chip->ecc.bytes); + chip->read_buf(mtd, bufpoi, toread); + bufpoi += eccpitch; + length -= eccpitch; + host->col_addr += chip->ecc.postpad + chip->ecc.prepad; + } + _mxc_nand_enable_hwecc(mtd, 1); + return 1; +} + +static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, + int oob_required, + int page) +{ + struct mxc_nand_host *host = chip->priv; + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *oob = chip->oob_poi; + int steps, size; + int n; + + _mxc_nand_enable_hwecc(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + + for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { + host->col_addr = n * eccsize; + chip->read_buf(mtd, buf, eccsize); + buf += eccsize; + + host->col_addr = mtd->writesize + n * eccpitch; + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->read_buf(mtd, oob, size); + _mxc_nand_enable_hwecc(mtd, 1); + + return 0; +} + +static int mxc_nand_read_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, + int oob_required, + int page) +{ + struct mxc_nand_host *host = chip->priv; + int n, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + MTDDEBUG(MTD_DEBUG_LEVEL1, "Reading page %u to buf %p oob %p\n", + page, buf, oob); + + /* first read the data area and the available portion of OOB */ + for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { + int stat; + + host->col_addr = n * eccsize; + + chip->read_buf(mtd, p, eccsize); + + host->col_addr = mtd->writesize + n * eccpitch; + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + n = mtd->oobsize - (oob - chip->oob_poi); + if (n) + chip->read_buf(mtd, oob, n); + + /* Then switch ECC off and read the OOB area to get the ECC code */ + _mxc_nand_enable_hwecc(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); + eccsteps = chip->ecc.steps; + oob = chip->oob_poi + chip->ecc.prepad; + for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { + host->col_addr = mtd->writesize + + n * eccpitch + + chip->ecc.prepad; + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes + chip->ecc.postpad; + } + _mxc_nand_enable_hwecc(mtd, 1); + return 0; +} + +static int mxc_nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + struct mxc_nand_host *host = chip->priv; + int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int length = mtd->oobsize; + int i, len, status, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + for (i = 0; i < steps; i++) { + len = min_t(int, length, eccpitch); + + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + host->col_addr += chip->ecc.prepad + chip->ecc.postpad; + } + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +static int mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required) +{ + struct mxc_nand_host *host = chip->priv; + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *oob = chip->oob_poi; + int steps, size; + int n; + + for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { + host->col_addr = n * eccsize; + chip->write_buf(mtd, buf, eccsize); + buf += eccsize; + + host->col_addr = mtd->writesize + n * eccpitch; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + host->col_addr += eccbytes; + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->write_buf(mtd, oob, size); + return 0; +} + +static int mxc_nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required) +{ + struct mxc_nand_host *host = chip->priv; + int i, n, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + for (i = n = 0; + eccsteps; + n++, eccsteps--, i += eccbytes, p += eccsize) { + host->col_addr = n * eccsize; + + chip->write_buf(mtd, p, eccsize); + + host->col_addr = mtd->writesize + n * eccpitch; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->write_buf(mtd, oob, i); + return 0; +} + +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint32_t ecc_status = readl(&host->regs->ecc_status_result); + int subpages = mtd->writesize / nand_chip->subpagesize; + int pg2blk_shift = nand_chip->phys_erase_shift - + nand_chip->page_shift; + + do { + if ((ecc_status & 0xf) > 4) { + static int last_bad = -1; + + if (last_bad != host->page_addr >> pg2blk_shift) { + last_bad = host->page_addr >> pg2blk_shift; + printk(KERN_DEBUG + "MXC_NAND: HWECC uncorrectable ECC error" + " in block %u page %u subpage %d\n", + last_bad, host->page_addr, + mtd->writesize / nand_chip->subpagesize + - subpages); + } + return -1; + } + ecc_status >>= 4; + subpages--; + } while (subpages > 0); + + return 0; +} +#else +#define mxc_nand_read_page_syndrome NULL +#define mxc_nand_read_page_raw_syndrome NULL +#define mxc_nand_read_oob_syndrome NULL +#define mxc_nand_write_page_syndrome NULL +#define mxc_nand_write_page_raw_syndrome NULL +#define mxc_nand_write_oob_syndrome NULL + +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + /* + * 1-Bit errors are automatically corrected in HW. No need for + * additional correction. 2-Bit errors cannot be corrected by + * HW ECC, so we need to return failure + */ + uint16_t ecc_status = readnfc(&host->regs->ecc_status_result); + + if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, + "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + return -1; + } + + return 0; +} +#endif + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} +#endif + +static u_char mxc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint8_t ret = 0; + uint16_t col; + uint16_t __iomem *main_buf = + (uint16_t __iomem *)host->regs->main_area[0]; + uint16_t __iomem *spare_buf = + (uint16_t __iomem *)host->regs->spare_area[0]; + union { + uint16_t word; + uint8_t bytes[2]; + } nfc_word; + + /* Check for status request */ + if (host->status_request) + return get_dev_status(host) & 0xFF; + + /* Get column for 16-bit access */ + col = host->col_addr >> 1; + + /* If we are accessing the spare region */ + if (host->spare_only) + nfc_word.word = readw(&spare_buf[col]); + else + nfc_word.word = readw(&main_buf[col]); + + /* Pick upper/lower byte of word from RAM buffer */ + ret = nfc_word.bytes[host->col_addr & 0x1]; + + /* Update saved column address */ + if (nand_chip->options & NAND_BUSWIDTH_16) + host->col_addr += 2; + else + host->col_addr++; + + return ret; +} + +static uint16_t mxc_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t col, ret; + uint16_t __iomem *p; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_word(col = %d)\n", host->col_addr); + + col = host->col_addr; + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + if (col < mtd->writesize) { + p = (uint16_t __iomem *)(host->regs->main_area[0] + + (col >> 1)); + } else { + p = (uint16_t __iomem *)(host->regs->spare_area[0] + + ((col - mtd->writesize) >> 1)); + } + + if (col & 1) { + union { + uint16_t word; + uint8_t bytes[2]; + } nfc_word[3]; + + nfc_word[0].word = readw(p); + nfc_word[1].word = readw(p + 1); + + nfc_word[2].bytes[0] = nfc_word[0].bytes[1]; + nfc_word[2].bytes[1] = nfc_word[1].bytes[0]; + + ret = nfc_word[2].word; + } else { + ret = readw(p); + } + + /* Update saved column address */ + host->col_addr = col + 2; + + return ret; +} + +/* + * Write data of length len to buffer buf. The data to be + * written on NAND Flash is first copied to RAMbuffer. After the Data Input + * Operation by the NFC, the data is written to NAND Flash + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + int n, col, i = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, + len); + + col = host->col_addr; + + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); + + while (n > 0) { + void __iomem *p; + + if (col < mtd->writesize) { + p = host->regs->main_area[0] + (col & ~3); + } else { + p = host->regs->spare_area[0] - + mtd->writesize + (col & ~3); + } + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__, + __LINE__, p); + + if (((col | (unsigned long)&buf[i]) & 3) || n < 4) { + union { + uint32_t word; + uint8_t bytes[4]; + } nfc_word; + + nfc_word.word = readl(p); + nfc_word.bytes[col & 3] = buf[i++]; + n--; + col++; + + writel(nfc_word.word, p); + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: n = %d, m = %d, i = %d, col = %d\n", + __func__, __LINE__, n, m, i, col); + + mxc_nand_memcpy32(p, (uint32_t *)&buf[i], m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + host->col_addr = col; +} + +/* + * Read the data buffer from the NAND Flash. To read the data from NAND + * Flash first the data output cycle is initiated by the NFC, which copies + * the data to RAMbuffer. This data of length len is then copied to buffer buf. + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + int n, col, i = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, len); + + col = host->col_addr; + + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + while (n > 0) { + void __iomem *p; + + if (col < mtd->writesize) { + p = host->regs->main_area[0] + (col & ~3); + } else { + p = host->regs->spare_area[0] - + mtd->writesize + (col & ~3); + } + + if (((col | (int)&buf[i]) & 3) || n < 4) { + union { + uint32_t word; + uint8_t bytes[4]; + } nfc_word; + + nfc_word.word = readl(p); + buf[i++] = nfc_word.bytes[col & 3]; + n--; + col++; + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + mxc_nand_memcpy32((uint32_t *)&buf[i], p, m); + + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + host->col_addr = col; +} + +/* + * Used by the upper layer to verify the data in NAND Flash + * with the data in the buf. + */ +static int mxc_nand_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + u_char tmp[256]; + uint bsize; + + while (len) { + bsize = min(len, 256); + mxc_nand_read_buf(mtd, tmp, bsize); + + if (memcmp(buf, tmp, bsize)) + return 1; + + buf += bsize; + len -= bsize; + } + + return 0; +} + +/* + * This function is used by upper layer for select and + * deselect of the NAND chip + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + switch (chip) { + case -1: + /* TODO: Disable the NFC clock */ + if (host->clk_act) + host->clk_act = 0; + break; + case 0: + /* TODO: Enable the NFC clock */ + if (!host->clk_act) + host->clk_act = 1; + break; + + default: + break; + } +} + +/* + * Used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash + */ +void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* Reset command state information */ + host->status_request = false; + + /* Command pre-processing step */ + switch (command) { + + case NAND_CMD_STATUS: + host->col_addr = 0; + host->status_request = true; + break; + + case NAND_CMD_READ0: + host->page_addr = page_addr; + host->col_addr = column; + host->spare_only = false; + break; + + case NAND_CMD_READOOB: + host->col_addr = column; + host->spare_only = true; + if (host->pagesize_2k) + command = NAND_CMD_READ0; /* only READ0 is valid */ + break; + + case NAND_CMD_SEQIN: + if (column >= mtd->writesize) { + /* + * before sending SEQIN command for partial write, + * we need read one page out. FSL NFC does not support + * partial write. It always sends out 512+ecc+512+ecc + * for large page nand flash. But for small page nand + * flash, it does support SPARE ONLY operation. + */ + if (host->pagesize_2k) { + /* call ourself to read a page */ + mxc_nand_command(mtd, NAND_CMD_READ0, 0, + page_addr); + } + + host->col_addr = column - mtd->writesize; + host->spare_only = true; + + /* Set program pointer to spare region */ + if (!host->pagesize_2k) + send_cmd(host, NAND_CMD_READOOB); + } else { + host->spare_only = false; + host->col_addr = column; + + /* Set program pointer to page start */ + if (!host->pagesize_2k) + send_cmd(host, NAND_CMD_READ0); + } + break; + + case NAND_CMD_PAGEPROG: + send_prog_page(host, 0, host->spare_only); + + if (host->pagesize_2k && is_mxc_nfc_1()) { + /* data in 4 areas */ + send_prog_page(host, 1, host->spare_only); + send_prog_page(host, 2, host->spare_only); + send_prog_page(host, 3, host->spare_only); + } + + break; + } + + /* Write out the command to the device. */ + send_cmd(host, command); + + /* Write out column address, if necessary */ + if (column != -1) { + /* + * MXC NANDFC can only perform full page+spare or + * spare-only read/write. When the upper layers perform + * a read/write buffer operation, we will use the saved + * column address to index into the full page. + */ + send_addr(host, 0); + if (host->pagesize_2k) + /* another col addr cycle for 2k page */ + send_addr(host, 0); + } + + /* Write out page address, if necessary */ + if (page_addr != -1) { + u32 page_mask = nand_chip->pagemask; + do { + send_addr(host, page_addr & 0xFF); + page_addr >>= 8; + page_mask >>= 8; + } while (page_mask); + } + + /* Command post-processing step */ + switch (command) { + + case NAND_CMD_RESET: + break; + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (host->pagesize_2k) { + /* send read confirm command */ + send_cmd(host, NAND_CMD_READSTART); + /* read for each AREA */ + send_read_page(host, 0, host->spare_only); + if (is_mxc_nfc_1()) { + send_read_page(host, 1, host->spare_only); + send_read_page(host, 2, host->spare_only); + send_read_page(host, 3, host->spare_only); + } + } else { + send_read_page(host, 0, host->spare_only); + } + break; + + case NAND_CMD_READID: + host->col_addr = 0; + send_read_id(host); + break; + + case NAND_CMD_PAGEPROG: + break; + + case NAND_CMD_STATUS: + break; + + case NAND_CMD_ERASE2: + break; + } +} + +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +#endif + +int board_nand_init(struct nand_chip *this) +{ + struct mtd_info *mtd; +#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) + uint32_t tmp; +#endif + +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + this->bbt_options |= NAND_BBT_USE_FLASH; + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; +#endif + + /* structures must be linked */ + mtd = &host->mtd; + mtd->priv = this; + host->nand = this; + + /* 5 us command delay time */ + this->chip_delay = 5; + + this->priv = host; + this->dev_ready = mxc_nand_dev_ready; + this->cmdfunc = mxc_nand_command; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->verify_buf = mxc_nand_verify_buf; + + host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; +#ifdef MXC_NFC_V3_2 + host->ip_regs = + (struct mxc_nand_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE; +#endif + host->clk_act = 1; + +#ifdef CONFIG_MXC_NAND_HWECC + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.correct = mxc_nand_correct_data; + if (is_mxc_nfc_21() || is_mxc_nfc_32()) { + this->ecc.mode = NAND_ECC_HW_SYNDROME; + this->ecc.read_page = mxc_nand_read_page_syndrome; + this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome; + this->ecc.read_oob = mxc_nand_read_oob_syndrome; + this->ecc.write_page = mxc_nand_write_page_syndrome; + this->ecc.write_page_raw = mxc_nand_write_page_raw_syndrome; + this->ecc.write_oob = mxc_nand_write_oob_syndrome; + this->ecc.bytes = 9; + this->ecc.prepad = 7; + } else { + this->ecc.mode = NAND_ECC_HW; + } + + if (is_mxc_nfc_1()) + this->ecc.strength = 1; + else + this->ecc.strength = 4; + + host->pagesize_2k = 0; + + this->ecc.size = 512; + _mxc_nand_enable_hwecc(mtd, 1); +#else + this->ecc.layout = &nand_soft_eccoob; + this->ecc.mode = NAND_ECC_SOFT; + _mxc_nand_enable_hwecc(mtd, 0); +#endif + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* NAND bus width determines access functions used by upper layer */ + if (is_16bit_nand()) + this->options |= NAND_BUSWIDTH_16; + +#ifdef CONFIG_SYS_NAND_LARGEPAGE + host->pagesize_2k = 1; + this->ecc.layout = &nand_hw_eccoob2k; +#else + host->pagesize_2k = 0; + this->ecc.layout = &nand_hw_eccoob; +#endif + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +#ifdef MXC_NFC_V2_1 + tmp = readnfc(&host->regs->config1); + tmp |= NFC_V2_CONFIG1_ONE_CYCLE; + tmp |= NFC_V2_CONFIG1_ECC_MODE_4; + writenfc(tmp, &host->regs->config1); + if (host->pagesize_2k) + writenfc(64/2, &host->regs->spare_area_size); + else + writenfc(16/2, &host->regs->spare_area_size); +#endif + + /* + * preset operation + * Unlock the internal RAM Buffer + */ + writenfc(0x2, &host->regs->config); + + /* Blocks to be unlocked */ + writenfc(0x0, &host->regs->unlockstart_blkaddr); + /* Originally (Freescale LTIB 2.6.21) 0x4000 was written to the + * unlockend_blkaddr, but the magic 0x4000 does not always work + * when writing more than some 32 megabytes (on 2k page nands) + * However 0xFFFF doesn't seem to have this kind + * of limitation (tried it back and forth several times). + * The linux kernel driver sets this to 0xFFFF for the v2 controller + * only, but probably this was not tested there for v1. + * The very same limitation seems to apply to this kernel driver. + * This might be NAND chip specific and the i.MX31 datasheet is + * extremely vague about the semantics of this register. + */ + writenfc(0xFFFF, &host->regs->unlockend_blkaddr); + + /* Unlock Block Command for given address range */ + writenfc(0x4, &host->regs->wrprot); +#elif defined(MXC_NFC_V3_2) + writenfc(NFC_V3_CONFIG1_RBA(0), &host->regs->config1); + writenfc(NFC_V3_IPC_CREQ, &host->ip_regs->ipc); + + /* Unlock the internal RAM Buffer */ + writenfc(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, + &host->ip_regs->wrprot); + + /* Blocks to be unlocked */ + for (tmp = 0; tmp < CONFIG_SYS_NAND_MAX_CHIPS; tmp++) + writenfc(0x0 | 0xFFFF << 16, + &host->ip_regs->wrprot_unlock_blkaddr[tmp]); + + writenfc(0, &host->ip_regs->ipc); + + tmp = readnfc(&host->ip_regs->config2); + tmp &= ~(NFC_V3_CONFIG2_SPAS_MASK | NFC_V3_CONFIG2_EDC_MASK | + NFC_V3_CONFIG2_ECC_MODE_8 | NFC_V3_CONFIG2_PS_MASK); + tmp |= NFC_V3_CONFIG2_ONE_CYCLE; + + if (host->pagesize_2k) { + tmp |= NFC_V3_CONFIG2_SPAS(64/2); + tmp |= NFC_V3_CONFIG2_PS_2048; + } else { + tmp |= NFC_V3_CONFIG2_SPAS(16/2); + tmp |= NFC_V3_CONFIG2_PS_512; + } + + writenfc(tmp, &host->ip_regs->config2); + + tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); + + if (!(this->options & NAND_BUSWIDTH_16)) + tmp |= NFC_V3_CONFIG3_FW8; + + writenfc(tmp, &host->ip_regs->config3); + + writenfc(0, &host->ip_regs->delay_line); +#endif + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.h b/qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.h new file mode 100644 index 000000000..a02d6e0a5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/mxc_nand.h @@ -0,0 +1,209 @@ +/* + * (c) 2009 Magnus Lilja + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MXC_NAND_H +#define __MXC_NAND_H + +/* + * Register map and bit definitions for the Freescale NAND Flash Controller + * present in various i.MX devices. + * + * MX31 and MX27 have version 1, which has: + * 4 512-byte main buffers and + * 4 16-byte spare buffers + * to support up to 2K byte pagesize nand. + * Reading or writing a 2K page requires 4 FDI/FDO cycles. + * + * MX25 and MX35 have version 2.1, and MX51 and MX53 have version 3.2, which + * have: + * 8 512-byte main buffers and + * 8 64-byte spare buffers + * to support up to 4K byte pagesize nand. + * Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle. + * Also some of registers are moved and/or changed meaning as seen below. + */ +#if defined(CONFIG_MX27) || defined(CONFIG_MX31) +#define MXC_NFC_V1 +#define is_mxc_nfc_1() 1 +#define is_mxc_nfc_21() 0 +#define is_mxc_nfc_32() 0 +#elif defined(CONFIG_MX25) || defined(CONFIG_MX35) +#define MXC_NFC_V2_1 +#define is_mxc_nfc_1() 0 +#define is_mxc_nfc_21() 1 +#define is_mxc_nfc_32() 0 +#elif defined(CONFIG_MX51) || defined(CONFIG_MX53) +#define MXC_NFC_V3 +#define MXC_NFC_V3_2 +#define is_mxc_nfc_1() 0 +#define is_mxc_nfc_21() 0 +#define is_mxc_nfc_32() 1 +#else +#error "MXC NFC implementation not supported" +#endif +#define is_mxc_nfc_3() is_mxc_nfc_32() + +#if defined(MXC_NFC_V1) +#define NAND_MXC_NR_BUFS 4 +#define NAND_MXC_SPARE_BUF_SIZE 16 +#define NAND_MXC_REG_OFFSET 0xe00 +#define NAND_MXC_2K_MULTI_CYCLE +#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) +#define NAND_MXC_NR_BUFS 8 +#define NAND_MXC_SPARE_BUF_SIZE 64 +#define NAND_MXC_REG_OFFSET 0x1e00 +#endif + +struct mxc_nand_regs { + u8 main_area[NAND_MXC_NR_BUFS][0x200]; + u8 spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE]; + /* + * reserved size is offset of nfc registers + * minus total main and spare sizes + */ + u8 reserved1[NAND_MXC_REG_OFFSET + - NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)]; +#if defined(MXC_NFC_V1) + u16 buf_size; + u16 reserved2; + u16 buf_addr; + u16 flash_addr; + u16 flash_cmd; + u16 config; + u16 ecc_status_result; + u16 rsltmain_area; + u16 rsltspare_area; + u16 wrprot; + u16 unlockstart_blkaddr; + u16 unlockend_blkaddr; + u16 nf_wrprst; + u16 config1; + u16 config2; +#elif defined(MXC_NFC_V2_1) + u16 reserved2[2]; + u16 buf_addr; + u16 flash_addr; + u16 flash_cmd; + u16 config; + u32 ecc_status_result; + u16 spare_area_size; + u16 wrprot; + u16 reserved3[2]; + u16 nf_wrprst; + u16 config1; + u16 config2; + u16 reserved4; + u16 unlockstart_blkaddr; + u16 unlockend_blkaddr; + u16 unlockstart_blkaddr1; + u16 unlockend_blkaddr1; + u16 unlockstart_blkaddr2; + u16 unlockend_blkaddr2; + u16 unlockstart_blkaddr3; + u16 unlockend_blkaddr3; +#elif defined(MXC_NFC_V3_2) + u32 flash_cmd; + u32 flash_addr[12]; + u32 config1; + u32 ecc_status_result; + u32 status_sum; + u32 launch; +#endif +}; + +#ifdef MXC_NFC_V3_2 +struct mxc_nand_ip_regs { + u32 wrprot; + u32 wrprot_unlock_blkaddr[8]; + u32 config2; + u32 config3; + u32 ipc; + u32 err_addr; + u32 delay_line; +}; +#endif + +/* Set FCMD to 1, rest to 0 for Command operation */ +#define NFC_CMD 0x1 + +/* Set FADD to 1, rest to 0 for Address operation */ +#define NFC_ADDR 0x2 + +/* Set FDI to 1, rest to 0 for Input operation */ +#define NFC_INPUT 0x4 + +/* Set FDO to 001, rest to 0 for Data Output operation */ +#define NFC_OUTPUT 0x8 + +/* Set FDO to 010, rest to 0 for Read ID operation */ +#define NFC_ID 0x10 + +/* Set FDO to 100, rest to 0 for Read Status operation */ +#define NFC_STATUS 0x20 + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +#define NFC_CONFIG1_SP_EN (1 << 2) +#define NFC_CONFIG1_RST (1 << 6) +#define NFC_CONFIG1_CE (1 << 7) +#elif defined(MXC_NFC_V3_2) +#define NFC_CONFIG1_SP_EN (1 << 0) +#define NFC_CONFIG1_CE (1 << 1) +#define NFC_CONFIG1_RST (1 << 2) +#endif +#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) +#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) +#define NFC_V1_V2_CONFIG1_BIG (1 << 5) +#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) +#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) +#define NFC_V2_CONFIG1_FP_INT (1 << 11) +#define NFC_V3_CONFIG1_RBA_MASK (0x7 << 4) +#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7) << 4) + +#define NFC_V1_V2_CONFIG2_INT (1 << 15) +#define NFC_V3_CONFIG2_PS_MASK (0x3 << 0) +#define NFC_V3_CONFIG2_PS_512 (0 << 0) +#define NFC_V3_CONFIG2_PS_2048 (1 << 0) +#define NFC_V3_CONFIG2_PS_4096 (2 << 0) +#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) +#define NFC_V3_CONFIG2_ECC_EN (1 << 3) +#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) +#define NFC_V3_CONFIG2_NUM_ADDR_PH0 (1 << 5) +#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) +#define NFC_V3_CONFIG2_PPB_MASK (0x3 << 7) +#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7) +#define NFC_V3_CONFIG2_EDC_MASK (0x7 << 9) +#define NFC_V3_CONFIG2_EDC(x) (((x) & 0x7) << 9) +#define NFC_V3_CONFIG2_NUM_ADDR_PH1(x) (((x) & 0x3) << 12) +#define NFC_V3_CONFIG2_INT_MSK (1 << 15) +#define NFC_V3_CONFIG2_SPAS_MASK (0xff << 16) +#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) +#define NFC_V3_CONFIG2_ST_CMD_MASK (0xff << 24) +#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) + +#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) +#define NFC_V3_CONFIG3_FW8 (1 << 3) +#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) +#define NFC_V3_CONFIG3_NUM_OF_DEVS(x) (((x) & 0x7) << 12) +#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) +#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) + +#define NFC_V3_WRPROT_UNLOCK (1 << 2) +#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) + +#define NFC_V3_IPC_CREQ (1 << 0) +#define NFC_V3_IPC_INT (1 << 31) + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +#define operation config2 +#define readnfc readw +#define writenfc writew +#elif defined(MXC_NFC_V3_2) +#define operation launch +#define readnfc readl +#define writenfc writel +#endif + +#endif /* __MXC_NAND_H */ diff --git a/qemu/roms/u-boot/drivers/mtd/nand/mxc_nand_spl.c b/qemu/roms/u-boot/drivers/mtd/nand/mxc_nand_spl.c new file mode 100644 index 000000000..69b736a84 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/mxc_nand_spl.c @@ -0,0 +1,351 @@ +/* + * (C) Copyright 2009 + * Magnus Lilja + * + * (C) Copyright 2008 + * Maxim Artamonov, + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr at denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "mxc_nand.h" + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR; +#elif defined(MXC_NFC_V3_2) +static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR_AXI; +static struct mxc_nand_ip_regs *const nfc_ip = (void *)NFC_BASE_ADDR; +#endif + +static void nfc_wait_ready(void) +{ + uint32_t tmp; + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + while (!(readnfc(&nfc->config2) & NFC_V1_V2_CONFIG2_INT)) + ; + + /* Reset interrupt flag */ + tmp = readnfc(&nfc->config2); + tmp &= ~NFC_V1_V2_CONFIG2_INT; + writenfc(tmp, &nfc->config2); +#elif defined(MXC_NFC_V3_2) + while (!(readnfc(&nfc_ip->ipc) & NFC_V3_IPC_INT)) + ; + + /* Reset interrupt flag */ + tmp = readnfc(&nfc_ip->ipc); + tmp &= ~NFC_V3_IPC_INT; + writenfc(tmp, &nfc_ip->ipc); +#endif +} + +static void nfc_nand_init(void) +{ +#if defined(MXC_NFC_V3_2) + int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; + int tmp; + + tmp = (readnfc(&nfc_ip->config2) & ~(NFC_V3_CONFIG2_SPAS_MASK | + NFC_V3_CONFIG2_EDC_MASK | NFC_V3_CONFIG2_PS_MASK)) | + NFC_V3_CONFIG2_SPAS(CONFIG_SYS_NAND_OOBSIZE / 2) | + NFC_V3_CONFIG2_INT_MSK | NFC_V3_CONFIG2_ECC_EN | + NFC_V3_CONFIG2_ONE_CYCLE; + if (CONFIG_SYS_NAND_PAGE_SIZE == 4096) + tmp |= NFC_V3_CONFIG2_PS_4096; + else if (CONFIG_SYS_NAND_PAGE_SIZE == 2048) + tmp |= NFC_V3_CONFIG2_PS_2048; + else if (CONFIG_SYS_NAND_PAGE_SIZE == 512) + tmp |= NFC_V3_CONFIG2_PS_512; + /* + * if spare size is larger that 16 bytes per 512 byte hunk + * then use 8 symbol correction instead of 4 + */ + if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16) + tmp |= NFC_V3_CONFIG2_ECC_MODE_8; + else + tmp &= ~NFC_V3_CONFIG2_ECC_MODE_8; + writenfc(tmp, &nfc_ip->config2); + + tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); +#ifndef CONFIG_SYS_NAND_BUSWIDTH_16 + tmp |= NFC_V3_CONFIG3_FW8; +#endif + writenfc(tmp, &nfc_ip->config3); + + writenfc(0, &nfc_ip->delay_line); +#elif defined(MXC_NFC_V2_1) + int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; + int config1; + + writenfc(CONFIG_SYS_NAND_OOBSIZE / 2, &nfc->spare_area_size); + + /* unlocking RAM Buff */ + writenfc(0x2, &nfc->config); + + /* hardware ECC checking and correct */ + config1 = readnfc(&nfc->config1) | NFC_V1_V2_CONFIG1_ECC_EN | + NFC_V1_V2_CONFIG1_INT_MSK | NFC_V2_CONFIG1_ONE_CYCLE | + NFC_V2_CONFIG1_FP_INT; + /* + * if spare size is larger that 16 bytes per 512 byte hunk + * then use 8 symbol correction instead of 4 + */ + if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16) + config1 &= ~NFC_V2_CONFIG1_ECC_MODE_4; + else + config1 |= NFC_V2_CONFIG1_ECC_MODE_4; + writenfc(config1, &nfc->config1); +#elif defined(MXC_NFC_V1) + /* unlocking RAM Buff */ + writenfc(0x2, &nfc->config); + + /* hardware ECC checking and correct */ + writenfc(NFC_V1_V2_CONFIG1_ECC_EN | NFC_V1_V2_CONFIG1_INT_MSK, + &nfc->config1); +#endif +} + +static void nfc_nand_command(unsigned short command) +{ + writenfc(command, &nfc->flash_cmd); + writenfc(NFC_CMD, &nfc->operation); + nfc_wait_ready(); +} + +static void nfc_nand_address(unsigned short address) +{ + writenfc(address, &nfc->flash_addr); + writenfc(NFC_ADDR, &nfc->operation); + nfc_wait_ready(); +} + +static void nfc_nand_page_address(unsigned int page_address) +{ + unsigned int page_count; + + nfc_nand_address(0x00); + + /* code only for large page flash */ + if (CONFIG_SYS_NAND_PAGE_SIZE > 512) + nfc_nand_address(0x00); + + page_count = CONFIG_SYS_NAND_SIZE / CONFIG_SYS_NAND_PAGE_SIZE; + + if (page_address <= page_count) { + page_count--; /* transform 0x01000000 to 0x00ffffff */ + do { + nfc_nand_address(page_address & 0xff); + page_address = page_address >> 8; + page_count = page_count >> 8; + } while (page_count); + } + + nfc_nand_address(0x00); +} + +static void nfc_nand_data_output(void) +{ +#ifdef NAND_MXC_2K_MULTI_CYCLE + int i; +#endif + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(0, &nfc->buf_addr); +#elif defined(MXC_NFC_V3_2) + int config1 = readnfc(&nfc->config1); + config1 &= ~NFC_V3_CONFIG1_RBA_MASK; + writenfc(config1, &nfc->config1); +#endif + writenfc(NFC_OUTPUT, &nfc->operation); + nfc_wait_ready(); +#ifdef NAND_MXC_2K_MULTI_CYCLE + /* + * This NAND controller requires multiple input commands + * for pages larger than 512 bytes. + */ + for (i = 1; i < CONFIG_SYS_NAND_PAGE_SIZE / 512; i++) { + writenfc(i, &nfc->buf_addr); + writenfc(NFC_OUTPUT, &nfc->operation); + nfc_wait_ready(); + } +#endif +} + +static int nfc_nand_check_ecc(void) +{ +#if defined(MXC_NFC_V1) + u16 ecc_status = readw(&nfc->ecc_status_result); + return (ecc_status & 0x3) == 2 || (ecc_status >> 2) == 2; +#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) + u32 ecc_status = readl(&nfc->ecc_status_result); + int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; + int err_limit = CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16 ? 8 : 4; + int subpages = CONFIG_SYS_NAND_PAGE_SIZE / 512; + + do { + if ((ecc_status & 0xf) > err_limit) + return 1; + ecc_status >>= 4; + } while (--subpages); + + return 0; +#endif +} + +static void nfc_nand_read_page(unsigned int page_address) +{ + /* read in first 0 buffer */ +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(0, &nfc->buf_addr); +#elif defined(MXC_NFC_V3_2) + int config1 = readnfc(&nfc->config1); + config1 &= ~NFC_V3_CONFIG1_RBA_MASK; + writenfc(config1, &nfc->config1); +#endif + nfc_nand_command(NAND_CMD_READ0); + nfc_nand_page_address(page_address); + + if (CONFIG_SYS_NAND_PAGE_SIZE > 512) + nfc_nand_command(NAND_CMD_READSTART); + + nfc_nand_data_output(); /* fill the main buffer 0 */ +} + +static int nfc_read_page(unsigned int page_address, unsigned char *buf) +{ + int i; + u32 *src; + u32 *dst; + + nfc_nand_read_page(page_address); + + if (nfc_nand_check_ecc()) + return -1; + + src = (u32 *)&nfc->main_area[0][0]; + dst = (u32 *)buf; + + /* main copy loop from NAND-buffer to SDRAM memory */ + for (i = 0; i < CONFIG_SYS_NAND_PAGE_SIZE / 4; i++) { + writel(readl(src), dst); + src++; + dst++; + } + + return 0; +} + +static int is_badblock(int pagenumber) +{ + int page = pagenumber; + u32 badblock; + u32 *src; + + /* Check the first two pages for bad block markers */ + for (page = pagenumber; page < pagenumber + 2; page++) { + nfc_nand_read_page(page); + + src = (u32 *)&nfc->spare_area[0][0]; + + /* + * IMPORTANT NOTE: The nand flash controller uses a non- + * standard layout for large page devices. This can + * affect the position of the bad block marker. + */ + /* Get the bad block marker */ + badblock = readl(&src[CONFIG_SYS_NAND_BAD_BLOCK_POS / 4]); + badblock >>= 8 * (CONFIG_SYS_NAND_BAD_BLOCK_POS % 4); + badblock &= 0xff; + + /* bad block marker verify */ + if (badblock != 0xff) + return 1; /* potential bad block */ + } + + return 0; +} + +int nand_spl_load_image(uint32_t from, unsigned int size, void *buf) +{ + int i; + unsigned int page; + unsigned int maxpages = CONFIG_SYS_NAND_SIZE / + CONFIG_SYS_NAND_PAGE_SIZE; + + nfc_nand_init(); + + /* Convert to page number */ + page = from / CONFIG_SYS_NAND_PAGE_SIZE; + i = 0; + + size = roundup(size, CONFIG_SYS_NAND_PAGE_SIZE); + while (i < size / CONFIG_SYS_NAND_PAGE_SIZE) { + if (nfc_read_page(page, buf) < 0) + return -1; + + page++; + i++; + buf = buf + CONFIG_SYS_NAND_PAGE_SIZE; + + /* + * Check if we have crossed a block boundary, and if so + * check for bad block. + */ + if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) { + /* + * Yes, new block. See if this block is good. If not, + * loop until we find a good block. + */ + while (is_badblock(page)) { + page = page + CONFIG_SYS_NAND_PAGE_COUNT; + /* Check i we've reached the end of flash. */ + if (page >= maxpages) + return -1; + } + } + } + + return 0; +} + +#ifndef CONFIG_SPL_FRAMEWORK +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + + /* + * CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must + * be aligned to full pages + */ + if (!nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_U_BOOT_SIZE, + (uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) { + /* Copy from NAND successful, start U-boot */ + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + uboot(); + } else { + /* Unrecoverable error when copying from NAND */ + hang(); + } +} +#endif + +void nand_init(void) {} +void nand_deselect(void) {} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/mxs_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/mxs_nand.c new file mode 100644 index 000000000..036c113ad --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/mxs_nand.c @@ -0,0 +1,1179 @@ +/* + * Freescale i.MX28 NAND flash driver + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 + +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 +#if defined(CONFIG_MX6) +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 2 +#else +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0 +#endif +#define MXS_NAND_METADATA_SIZE 10 + +#define MXS_NAND_COMMAND_BUFFER_SIZE 32 + +#define MXS_NAND_BCH_TIMEOUT 10000 + +struct mxs_nand_info { + int cur_chip; + + uint32_t cmd_queue_len; + uint32_t data_buf_size; + + uint8_t *cmd_buf; + uint8_t *data_buf; + uint8_t *oob_buf; + + uint8_t marking_block_bad; + uint8_t raw_oob_mode; + + /* Functions with altered behaviour */ + int (*hooked_read_oob)(struct mtd_info *mtd, + loff_t from, struct mtd_oob_ops *ops); + int (*hooked_write_oob)(struct mtd_info *mtd, + loff_t to, struct mtd_oob_ops *ops); + int (*hooked_block_markbad)(struct mtd_info *mtd, + loff_t ofs); + + /* DMA descriptors */ + struct mxs_dma_desc **desc; + uint32_t desc_index; +}; + +struct nand_ecclayout fake_ecc_layout; + +/* + * Cache management functions + */ +#ifndef CONFIG_SYS_DCACHE_OFF +static void mxs_nand_flush_data_buf(struct mxs_nand_info *info) +{ + uint32_t addr = (uint32_t)info->data_buf; + + flush_dcache_range(addr, addr + info->data_buf_size); +} + +static void mxs_nand_inval_data_buf(struct mxs_nand_info *info) +{ + uint32_t addr = (uint32_t)info->data_buf; + + invalidate_dcache_range(addr, addr + info->data_buf_size); +} + +static void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info) +{ + uint32_t addr = (uint32_t)info->cmd_buf; + + flush_dcache_range(addr, addr + MXS_NAND_COMMAND_BUFFER_SIZE); +} +#else +static inline void mxs_nand_flush_data_buf(struct mxs_nand_info *info) {} +static inline void mxs_nand_inval_data_buf(struct mxs_nand_info *info) {} +static inline void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info) {} +#endif + +static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info) +{ + struct mxs_dma_desc *desc; + + if (info->desc_index >= MXS_NAND_DMA_DESCRIPTOR_COUNT) { + printf("MXS NAND: Too many DMA descriptors requested\n"); + return NULL; + } + + desc = info->desc[info->desc_index]; + info->desc_index++; + + return desc; +} + +static void mxs_nand_return_dma_descs(struct mxs_nand_info *info) +{ + int i; + struct mxs_dma_desc *desc; + + for (i = 0; i < info->desc_index; i++) { + desc = info->desc[i]; + memset(desc, 0, sizeof(struct mxs_dma_desc)); + desc->address = (dma_addr_t)desc; + } + + info->desc_index = 0; +} + +static uint32_t mxs_nand_ecc_chunk_cnt(uint32_t page_data_size) +{ + return page_data_size / MXS_NAND_CHUNK_DATA_CHUNK_SIZE; +} + +static uint32_t mxs_nand_ecc_size_in_bits(uint32_t ecc_strength) +{ + return ecc_strength * 13; +} + +static uint32_t mxs_nand_aux_status_offset(void) +{ + return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3; +} + +static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size, + uint32_t page_oob_size) +{ + if (page_data_size == 2048) + return 8; + + if (page_data_size == 4096) { + if (page_oob_size == 128) + return 8; + + if (page_oob_size == 218) + return 16; + + if (page_oob_size == 224) + return 16; + } + + return 0; +} + +static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, + uint32_t ecc_strength) +{ + uint32_t chunk_data_size_in_bits; + uint32_t chunk_ecc_size_in_bits; + uint32_t chunk_total_size_in_bits; + uint32_t block_mark_chunk_number; + uint32_t block_mark_chunk_bit_offset; + uint32_t block_mark_bit_offset; + + chunk_data_size_in_bits = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 8; + chunk_ecc_size_in_bits = mxs_nand_ecc_size_in_bits(ecc_strength); + + chunk_total_size_in_bits = + chunk_data_size_in_bits + chunk_ecc_size_in_bits; + + /* Compute the bit offset of the block mark within the physical page. */ + block_mark_bit_offset = page_data_size * 8; + + /* Subtract the metadata bits. */ + block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; + + /* + * Compute the chunk number (starting at zero) in which the block mark + * appears. + */ + block_mark_chunk_number = + block_mark_bit_offset / chunk_total_size_in_bits; + + /* + * Compute the bit offset of the block mark within its chunk, and + * validate it. + */ + block_mark_chunk_bit_offset = block_mark_bit_offset - + (block_mark_chunk_number * chunk_total_size_in_bits); + + if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) + return 1; + + /* + * Now that we know the chunk number in which the block mark appears, + * we can subtract all the ECC bits that appear before it. + */ + block_mark_bit_offset -= + block_mark_chunk_number * chunk_ecc_size_in_bits; + + return block_mark_bit_offset; +} + +static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd) +{ + uint32_t ecc_strength; + ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); + return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3; +} + +static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd) +{ + uint32_t ecc_strength; + ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); + return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) & 0x7; +} + +/* + * Wait for BCH complete IRQ and clear the IRQ + */ +static int mxs_nand_wait_for_bch_complete(void) +{ + struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; + int timeout = MXS_NAND_BCH_TIMEOUT; + int ret; + + ret = mxs_wait_mask_set(&bch_regs->hw_bch_ctrl_reg, + BCH_CTRL_COMPLETE_IRQ, timeout); + + writel(BCH_CTRL_COMPLETE_IRQ, &bch_regs->hw_bch_ctrl_clr); + + return ret; +} + +/* + * This is the function that we install in the cmd_ctrl function pointer of the + * owning struct nand_chip. The only functions in the reference implementation + * that use these functions pointers are cmdfunc and select_chip. + * + * In this driver, we implement our own select_chip, so this function will only + * be called by the reference implementation's cmdfunc. For this reason, we can + * ignore the chip enable bit and concentrate only on sending bytes to the NAND + * Flash. + */ +static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + /* + * If this condition is true, something is _VERY_ wrong in MTD + * subsystem! + */ + if (nand_info->cmd_queue_len == MXS_NAND_COMMAND_BUFFER_SIZE) { + printf("MXS NAND: Command queue too long\n"); + return; + } + + /* + * Every operation begins with a command byte and a series of zero or + * more address bytes. These are distinguished by either the Address + * Latch Enable (ALE) or Command Latch Enable (CLE) signals being + * asserted. When MTD is ready to execute the command, it will + * deasert both latch enables. + * + * Rather than run a separate DMA operation for every single byte, we + * queue them up and run a single DMA operation for the entire series + * of command and data bytes. + */ + if (ctrl & (NAND_ALE | NAND_CLE)) { + if (data != NAND_CMD_NONE) + nand_info->cmd_buf[nand_info->cmd_queue_len++] = data; + return; + } + + /* + * If control arrives here, MTD has deasserted both the ALE and CLE, + * which means it's ready to run an operation. Check if we have any + * bytes to send. + */ + if (nand_info->cmd_queue_len == 0) + return; + + /* Compile the DMA descriptor -- a descriptor that sends command. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_DEC_SEM | + MXS_DMA_DESC_WAIT4END | (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (nand_info->cmd_queue_len << MXS_DMA_DESC_BYTES_OFFSET); + + d->cmd.address = (dma_addr_t)nand_info->cmd_buf; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + nand_info->cmd_queue_len; + + mxs_dma_desc_append(channel, d); + + /* Flush caches */ + mxs_nand_flush_cmd_buf(nand_info); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) + printf("MXS NAND: Error sending command\n"); + + mxs_nand_return_dma_descs(nand_info); + + /* Reset the command queue. */ + nand_info->cmd_queue_len = 0; +} + +/* + * Test if the NAND flash is ready. + */ +static int mxs_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mxs_nand_info *nand_info = chip->priv; + struct mxs_gpmi_regs *gpmi_regs = + (struct mxs_gpmi_regs *)MXS_GPMI_BASE; + uint32_t tmp; + + tmp = readl(&gpmi_regs->hw_gpmi_stat); + tmp >>= (GPMI_STAT_READY_BUSY_OFFSET + nand_info->cur_chip); + + return tmp & 1; +} + +/* + * Select the NAND chip. + */ +static void mxs_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + + nand_info->cur_chip = chip; +} + +/* + * Handle block mark swapping. + * + * Note that, when this function is called, it doesn't know whether it's + * swapping the block mark, or swapping it *back* -- but it doesn't matter + * because the the operation is the same. + */ +static void mxs_nand_swap_block_mark(struct mtd_info *mtd, + uint8_t *data_buf, uint8_t *oob_buf) +{ + uint32_t bit_offset; + uint32_t buf_offset; + + uint32_t src; + uint32_t dst; + + bit_offset = mxs_nand_mark_bit_offset(mtd); + buf_offset = mxs_nand_mark_byte_offset(mtd); + + /* + * Get the byte from the data area that overlays the block mark. Since + * the ECC engine applies its own view to the bits in the page, the + * physical block mark won't (in general) appear on a byte boundary in + * the data. + */ + src = data_buf[buf_offset] >> bit_offset; + src |= data_buf[buf_offset + 1] << (8 - bit_offset); + + dst = oob_buf[0]; + + oob_buf[0] = src; + + data_buf[buf_offset] &= ~(0xff << bit_offset); + data_buf[buf_offset + 1] &= 0xff << bit_offset; + + data_buf[buf_offset] |= dst << bit_offset; + data_buf[buf_offset + 1] |= dst >> (8 - bit_offset); +} + +/* + * Read data from NAND. + */ +static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + if (length > NAND_MAX_PAGESIZE) { + printf("MXS NAND: DMA buffer too big\n"); + return; + } + + if (!buf) { + printf("MXS NAND: DMA buffer is NULL\n"); + return; + } + + /* Compile the DMA descriptor - a descriptor that reads data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_DMA_WRITE | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (length << MXS_DMA_DESC_BYTES_OFFSET); + + d->cmd.address = (dma_addr_t)nand_info->data_buf; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + length; + + mxs_dma_desc_append(channel, d); + + /* + * A DMA descriptor that waits for the command to end and the chip to + * become ready. + * + * I think we actually should *not* be waiting for the chip to become + * ready because, after all, we don't care. I think the original code + * did that and no one has re-thought it yet. + */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_DEC_SEM | + MXS_DMA_DESC_WAIT4END | (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + + mxs_dma_desc_append(channel, d); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) { + printf("MXS NAND: DMA read error\n"); + goto rtn; + } + + /* Invalidate caches */ + mxs_nand_inval_data_buf(nand_info); + + memcpy(buf, nand_info->data_buf, length); + +rtn: + mxs_nand_return_dma_descs(nand_info); +} + +/* + * Write data to NAND. + */ +static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int length) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + if (length > NAND_MAX_PAGESIZE) { + printf("MXS NAND: DMA buffer too big\n"); + return; + } + + if (!buf) { + printf("MXS NAND: DMA buffer is NULL\n"); + return; + } + + memcpy(nand_info->data_buf, buf, length); + + /* Compile the DMA descriptor - a descriptor that writes data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (length << MXS_DMA_DESC_BYTES_OFFSET); + + d->cmd.address = (dma_addr_t)nand_info->data_buf; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + length; + + mxs_dma_desc_append(channel, d); + + /* Flush caches */ + mxs_nand_flush_data_buf(nand_info); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) + printf("MXS NAND: DMA write error\n"); + + mxs_nand_return_dma_descs(nand_info); +} + +/* + * Read a single byte from NAND. + */ +static uint8_t mxs_nand_read_byte(struct mtd_info *mtd) +{ + uint8_t buf; + mxs_nand_read_buf(mtd, &buf, 1); + return buf; +} + +/* + * Read a page from NAND. + */ +static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, + int page) +{ + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + uint32_t corrected = 0, failed = 0; + uint8_t *status; + int i, ret; + + /* Compile the DMA descriptor - wait for ready. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + + mxs_dma_desc_append(channel, d); + + /* Compile the DMA descriptor - enable the BCH block and read. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_WAIT4END | (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[1] = 0; + d->cmd.pio_words[2] = + GPMI_ECCCTRL_ENABLE_ECC | + GPMI_ECCCTRL_ECC_CMD_DECODE | + GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; + d->cmd.pio_words[3] = mtd->writesize + mtd->oobsize; + d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; + d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; + + mxs_dma_desc_append(channel, d); + + /* Compile the DMA descriptor - disable the BCH block. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | + (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[1] = 0; + d->cmd.pio_words[2] = 0; + + mxs_dma_desc_append(channel, d); + + /* Compile the DMA descriptor - deassert the NAND lock and interrupt. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM; + + d->cmd.address = 0; + + mxs_dma_desc_append(channel, d); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) { + printf("MXS NAND: DMA read error\n"); + goto rtn; + } + + ret = mxs_nand_wait_for_bch_complete(); + if (ret) { + printf("MXS NAND: BCH read timeout\n"); + goto rtn; + } + + /* Invalidate caches */ + mxs_nand_inval_data_buf(nand_info); + + /* Read DMA completed, now do the mark swapping. */ + mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf); + + /* Loop over status bytes, accumulating ECC status. */ + status = nand_info->oob_buf + mxs_nand_aux_status_offset(); + for (i = 0; i < mxs_nand_ecc_chunk_cnt(mtd->writesize); i++) { + if (status[i] == 0x00) + continue; + + if (status[i] == 0xff) + continue; + + if (status[i] == 0xfe) { + failed++; + continue; + } + + corrected += status[i]; + } + + /* Propagate ECC status to the owning MTD. */ + mtd->ecc_stats.failed += failed; + mtd->ecc_stats.corrected += corrected; + + /* + * It's time to deliver the OOB bytes. See mxs_nand_ecc_read_oob() for + * details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the block + * mark to the caller's buffer. Note that, if block mark swapping was + * necessary, it has already been done, so we can rely on the first + * byte of the auxiliary buffer to contain the block mark. + */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + + nand->oob_poi[0] = nand_info->oob_buf[0]; + + memcpy(buf, nand_info->data_buf, mtd->writesize); + +rtn: + mxs_nand_return_dma_descs(nand_info); + + return ret; +} + +/* + * Write a page to NAND. + */ +static int mxs_nand_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *nand, const uint8_t *buf, + int oob_required) +{ + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + memcpy(nand_info->data_buf, buf, mtd->writesize); + memcpy(nand_info->oob_buf, nand->oob_poi, mtd->oobsize); + + /* Handle block mark swapping. */ + mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf); + + /* Compile the DMA descriptor - write data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + d->cmd.pio_words[1] = 0; + d->cmd.pio_words[2] = + GPMI_ECCCTRL_ENABLE_ECC | + GPMI_ECCCTRL_ECC_CMD_ENCODE | + GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; + d->cmd.pio_words[3] = (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; + d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; + + mxs_dma_desc_append(channel, d); + + /* Flush caches */ + mxs_nand_flush_data_buf(nand_info); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) { + printf("MXS NAND: DMA write error\n"); + goto rtn; + } + + ret = mxs_nand_wait_for_bch_complete(); + if (ret) { + printf("MXS NAND: BCH write timeout\n"); + goto rtn; + } + +rtn: + mxs_nand_return_dma_descs(nand_info); + return 0; +} + +/* + * Read OOB from NAND. + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + */ +static int mxs_nand_hook_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct mxs_nand_info *nand_info = chip->priv; + int ret; + + if (ops->mode == MTD_OPS_RAW) + nand_info->raw_oob_mode = 1; + else + nand_info->raw_oob_mode = 0; + + ret = nand_info->hooked_read_oob(mtd, from, ops); + + nand_info->raw_oob_mode = 0; + + return ret; +} + +/* + * Write OOB to NAND. + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + */ +static int mxs_nand_hook_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct mxs_nand_info *nand_info = chip->priv; + int ret; + + if (ops->mode == MTD_OPS_RAW) + nand_info->raw_oob_mode = 1; + else + nand_info->raw_oob_mode = 0; + + ret = nand_info->hooked_write_oob(mtd, to, ops); + + nand_info->raw_oob_mode = 0; + + return ret; +} + +/* + * Mark a block bad in NAND. + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + */ +static int mxs_nand_hook_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + struct mxs_nand_info *nand_info = chip->priv; + int ret; + + nand_info->marking_block_bad = 1; + + ret = nand_info->hooked_block_markbad(mtd, ofs); + + nand_info->marking_block_bad = 0; + + return ret; +} + +/* + * There are several places in this driver where we have to handle the OOB and + * block marks. This is the function where things are the most complicated, so + * this is where we try to explain it all. All the other places refer back to + * here. + * + * These are the rules, in order of decreasing importance: + * + * 1) Nothing the caller does can be allowed to imperil the block mark, so all + * write operations take measures to protect it. + * + * 2) In read operations, the first byte of the OOB we return must reflect the + * true state of the block mark, no matter where that block mark appears in + * the physical page. + * + * 3) ECC-based read operations return an OOB full of set bits (since we never + * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads + * return). + * + * 4) "Raw" read operations return a direct view of the physical bytes in the + * page, using the conventional definition of which bytes are data and which + * are OOB. This gives the caller a way to see the actual, physical bytes + * in the page, without the distortions applied by our ECC engine. + * + * What we do for this specific read operation depends on whether we're doing + * "raw" read, or an ECC-based read. + * + * It turns out that knowing whether we want an "ECC-based" or "raw" read is not + * easy. When reading a page, for example, the NAND Flash MTD code calls our + * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an + * ECC-based or raw view of the page is implicit in which function it calls + * (there is a similar pair of ECC-based/raw functions for writing). + * + * Since MTD assumes the OOB is not covered by ECC, there is no pair of + * ECC-based/raw functions for reading or or writing the OOB. The fact that the + * caller wants an ECC-based or raw view of the page is not propagated down to + * this driver. + * + * Since our OOB *is* covered by ECC, we need this information. So, we hook the + * ecc.read_oob and ecc.write_oob function pointers in the owning + * struct mtd_info with our own functions. These hook functions set the + * raw_oob_mode field so that, when control finally arrives here, we'll know + * what to do. + */ +static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + struct mxs_nand_info *nand_info = nand->priv; + + /* + * First, fill in the OOB buffer. If we're doing a raw read, we need to + * get the bytes from the physical page. If we're not doing a raw read, + * we need to fill the buffer with set bits. + */ + if (nand_info->raw_oob_mode) { + /* + * If control arrives here, we're doing a "raw" read. Send the + * command to read the conventional OOB and read it. + */ + nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + nand->read_buf(mtd, nand->oob_poi, mtd->oobsize); + } else { + /* + * If control arrives here, we're not doing a "raw" read. Fill + * the OOB buffer with set bits and correct the block mark. + */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + + nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + mxs_nand_read_buf(mtd, nand->oob_poi, 1); + } + + return 0; + +} + +/* + * Write OOB data to NAND. + */ +static int mxs_nand_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + struct mxs_nand_info *nand_info = nand->priv; + uint8_t block_mark = 0; + + /* + * There are fundamental incompatibilities between the i.MX GPMI NFC and + * the NAND Flash MTD model that make it essentially impossible to write + * the out-of-band bytes. + * + * We permit *ONE* exception. If the *intent* of writing the OOB is to + * mark a block bad, we can do that. + */ + + if (!nand_info->marking_block_bad) { + printf("NXS NAND: Writing OOB isn't supported\n"); + return -EIO; + } + + /* Write the block mark. */ + nand->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + nand->write_buf(mtd, &block_mark, 1); + nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + /* Check if it worked. */ + if (nand->waitfunc(mtd, nand) & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +/* + * Claims all blocks are good. + * + * In principle, this function is *only* called when the NAND Flash MTD system + * isn't allowed to keep an in-memory bad block table, so it is forced to ask + * the driver for bad block information. + * + * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so + * this function is *only* called when we take it away. + * + * Thus, this function is only called when we want *all* blocks to look good, + * so it *always* return success. + */ +static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} + +/* + * Nominally, the purpose of this function is to look for or create the bad + * block table. In fact, since the we call this function at the very end of + * the initialization process started by nand_scan(), and we doesn't have a + * more formal mechanism, we "hook" this function to continue init process. + * + * At this point, the physical NAND Flash chips have been identified and + * counted, so we know the physical geometry. This enables us to make some + * important configuration decisions. + * + * The return value of this function propogates directly back to this driver's + * call to nand_scan(). Anything other than zero will cause this driver to + * tear everything down and declare failure. + */ +static int mxs_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; + uint32_t tmp; + + /* Configure BCH and set NFC geometry */ + mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); + + /* Configure layout 0 */ + tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) + << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; + tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; + tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) + << BCH_FLASHLAYOUT0_ECC0_OFFSET; + tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE + >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + writel(tmp, &bch_regs->hw_bch_flash0layout0); + + tmp = (mtd->writesize + mtd->oobsize) + << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; + tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) + << BCH_FLASHLAYOUT1_ECCN_OFFSET; + tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE + >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + writel(tmp, &bch_regs->hw_bch_flash0layout1); + + /* Set *all* chip selects to use layout 0 */ + writel(0, &bch_regs->hw_bch_layoutselect); + + /* Enable BCH complete interrupt */ + writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set); + + /* Hook some operations at the MTD level. */ + if (mtd->_read_oob != mxs_nand_hook_read_oob) { + nand_info->hooked_read_oob = mtd->_read_oob; + mtd->_read_oob = mxs_nand_hook_read_oob; + } + + if (mtd->_write_oob != mxs_nand_hook_write_oob) { + nand_info->hooked_write_oob = mtd->_write_oob; + mtd->_write_oob = mxs_nand_hook_write_oob; + } + + if (mtd->_block_markbad != mxs_nand_hook_block_markbad) { + nand_info->hooked_block_markbad = mtd->_block_markbad; + mtd->_block_markbad = mxs_nand_hook_block_markbad; + } + + /* We use the reference implementation for bad block management. */ + return nand_default_bbt(mtd); +} + +/* + * Allocate DMA buffers + */ +int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info) +{ + uint8_t *buf; + const int size = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE; + + nand_info->data_buf_size = roundup(size, MXS_DMA_ALIGNMENT); + + /* DMA buffers */ + buf = memalign(MXS_DMA_ALIGNMENT, nand_info->data_buf_size); + if (!buf) { + printf("MXS NAND: Error allocating DMA buffers\n"); + return -ENOMEM; + } + + memset(buf, 0, nand_info->data_buf_size); + + nand_info->data_buf = buf; + nand_info->oob_buf = buf + NAND_MAX_PAGESIZE; + /* Command buffers */ + nand_info->cmd_buf = memalign(MXS_DMA_ALIGNMENT, + MXS_NAND_COMMAND_BUFFER_SIZE); + if (!nand_info->cmd_buf) { + free(buf); + printf("MXS NAND: Error allocating command buffers\n"); + return -ENOMEM; + } + memset(nand_info->cmd_buf, 0, MXS_NAND_COMMAND_BUFFER_SIZE); + nand_info->cmd_queue_len = 0; + + return 0; +} + +/* + * Initializes the NFC hardware. + */ +int mxs_nand_init(struct mxs_nand_info *info) +{ + struct mxs_gpmi_regs *gpmi_regs = + (struct mxs_gpmi_regs *)MXS_GPMI_BASE; + struct mxs_bch_regs *bch_regs = + (struct mxs_bch_regs *)MXS_BCH_BASE; + int i = 0, j; + + info->desc = malloc(sizeof(struct mxs_dma_desc *) * + MXS_NAND_DMA_DESCRIPTOR_COUNT); + if (!info->desc) + goto err1; + + /* Allocate the DMA descriptors. */ + for (i = 0; i < MXS_NAND_DMA_DESCRIPTOR_COUNT; i++) { + info->desc[i] = mxs_dma_desc_alloc(); + if (!info->desc[i]) + goto err2; + } + + /* Init the DMA controller. */ + for (j = MXS_DMA_CHANNEL_AHB_APBH_GPMI0; + j <= MXS_DMA_CHANNEL_AHB_APBH_GPMI7; j++) { + if (mxs_dma_init_channel(j)) + goto err3; + } + + /* Reset the GPMI block. */ + mxs_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg); + mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); + + /* + * Choose NAND mode, set IRQ polarity, disable write protection and + * select BCH ECC. + */ + clrsetbits_le32(&gpmi_regs->hw_gpmi_ctrl1, + GPMI_CTRL1_GPMI_MODE, + GPMI_CTRL1_ATA_IRQRDY_POLARITY | GPMI_CTRL1_DEV_RESET | + GPMI_CTRL1_BCH_MODE); + + return 0; + +err3: + for (--j; j >= 0; j--) + mxs_dma_release(j); +err2: + free(info->desc); +err1: + for (--i; i >= 0; i--) + mxs_dma_desc_free(info->desc[i]); + printf("MXS NAND: Unable to allocate DMA descriptors\n"); + return -ENOMEM; +} + +/*! + * This function is called during the driver binding process. + * + * @param pdev the device structure used to store device specific + * information that is used by the suspend, resume and + * remove functions + * + * @return The function always returns 0. + */ +int board_nand_init(struct nand_chip *nand) +{ + struct mxs_nand_info *nand_info; + int err; + + nand_info = malloc(sizeof(struct mxs_nand_info)); + if (!nand_info) { + printf("MXS NAND: Failed to allocate private data\n"); + return -ENOMEM; + } + memset(nand_info, 0, sizeof(struct mxs_nand_info)); + + err = mxs_nand_alloc_buffers(nand_info); + if (err) + goto err1; + + err = mxs_nand_init(nand_info); + if (err) + goto err2; + + memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout)); + + nand->priv = nand_info; + nand->options |= NAND_NO_SUBPAGE_WRITE; + + nand->cmd_ctrl = mxs_nand_cmd_ctrl; + + nand->dev_ready = mxs_nand_device_ready; + nand->select_chip = mxs_nand_select_chip; + nand->block_bad = mxs_nand_block_bad; + nand->scan_bbt = mxs_nand_scan_bbt; + + nand->read_byte = mxs_nand_read_byte; + + nand->read_buf = mxs_nand_read_buf; + nand->write_buf = mxs_nand_write_buf; + + nand->ecc.read_page = mxs_nand_ecc_read_page; + nand->ecc.write_page = mxs_nand_ecc_write_page; + nand->ecc.read_oob = mxs_nand_ecc_read_oob; + nand->ecc.write_oob = mxs_nand_ecc_write_oob; + + nand->ecc.layout = &fake_ecc_layout; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.bytes = 9; + nand->ecc.size = 512; + nand->ecc.strength = 8; + + return 0; + +err2: + free(nand_info->data_buf); + free(nand_info->cmd_buf); +err1: + free(nand_info); + return err; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand.c b/qemu/roms/u-boot/drivers/mtd/nand/nand.c new file mode 100644 index 000000000..4cf4c1c70 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand.c @@ -0,0 +1,120 @@ +/* + * (C) Copyright 2005 + * 2N Telekomunikace, a.s. + * Ladislav Michl + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +DECLARE_GLOBAL_DATA_PTR; + +int nand_curr_device = -1; + + +nand_info_t nand_info[CONFIG_SYS_MAX_NAND_DEVICE]; + +#ifndef CONFIG_SYS_NAND_SELF_INIT +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; +static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST; +#endif + +static char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8]; + +static unsigned long total_nand_size; /* in kiB */ + +/* Register an initialized NAND mtd device with the U-Boot NAND command. */ +int nand_register(int devnum) +{ + struct mtd_info *mtd; + + if (devnum >= CONFIG_SYS_MAX_NAND_DEVICE) + return -EINVAL; + + mtd = &nand_info[devnum]; + + sprintf(dev_name[devnum], "nand%d", devnum); + mtd->name = dev_name[devnum]; + +#ifdef CONFIG_MTD_DEVICE + /* + * Add MTD device so that we can reference it later + * via the mtdcore infrastructure (e.g. ubi). + */ + add_mtd_device(mtd); +#endif + + total_nand_size += mtd->size / 1024; + + if (nand_curr_device == -1) + nand_curr_device = devnum; + + return 0; +} + +#ifndef CONFIG_SYS_NAND_SELF_INIT +static void nand_init_chip(int i) +{ + struct mtd_info *mtd = &nand_info[i]; + struct nand_chip *nand = &nand_chip[i]; + ulong base_addr = base_address[i]; + int maxchips = CONFIG_SYS_NAND_MAX_CHIPS; + + if (maxchips < 1) + maxchips = 1; + + mtd->priv = nand; + nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; + + if (board_nand_init(nand)) + return; + + if (nand_scan(mtd, maxchips)) + return; + + nand_register(i); +} +#endif + +void nand_init(void) +{ +#ifdef CONFIG_SYS_NAND_SELF_INIT + board_nand_init(); +#else + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + nand_init_chip(i); +#endif + + printf("%lu MiB\n", total_nand_size / 1024); + +#ifdef CONFIG_SYS_NAND_SELECT_DEVICE + /* + * Select the chip in the board/cpu specific driver + */ + board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device); +#endif +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_base.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_base.c new file mode 100644 index 000000000..1ce55fde8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_base.c @@ -0,0 +1,3438 @@ +/* + * drivers/mtd/nand.c + * + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + * Basic support for AG-AND chips is provided. + * + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/doc/nand.html + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002-2006 Thomas Gleixner (tglx@linutronix.de) + * + * Credits: + * David Woodhouse for adding multichip support + * + * Aleph One Ltd. and Toby Churchill Ltd. for supporting the + * rework for 2K page size chips + * + * TODO: + * Enable cached programming for 2k page size chips + * Check, if mtd->ecctype should be set to MTD_ECC_HW + * if we have HW ECC support. + * The AG-AND chips have nice features for speed improvement, + * which are not supported yet. Read / program 4 pages in one go. + * BBT table is not serialized, has to be fixed + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include + +#define ENOTSUPP 524 /* Operation is not supported */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif + +#include +#include + +/* + * CONFIG_SYS_NAND_RESET_CNT is used as a timeout mechanism when resetting + * a flash. NAND flash is initialized prior to interrupts so standard timers + * can't be used. CONFIG_SYS_NAND_RESET_CNT should be set to a value + * which is greater than (max NAND reset time / NAND status read time). + * A conservative default of 200000 (500 us / 25 ns) is used as a default. + */ +#ifndef CONFIG_SYS_NAND_RESET_CNT +#define CONFIG_SYS_NAND_RESET_CNT 200000 +#endif + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_ecclayout nand_oob_8 = { + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { + {.offset = 3, + .length = 2}, + {.offset = 6, + .length = 2} } +}; + +static struct nand_ecclayout nand_oob_16 = { + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 6, 7}, + .oobfree = { + {.offset = 8, + . length = 8} } +}; + +static struct nand_ecclayout nand_oob_64 = { + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { + {.offset = 2, + .length = 38} } +}; + +static struct nand_ecclayout nand_oob_128 = { + .eccbytes = 48, + .eccpos = { + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + .oobfree = { + {.offset = 2, + .length = 78} } +}; + +static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, + int new_state); + +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this); + +static int check_offs_len(struct mtd_info *mtd, + loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd->priv; + int ret = 0; + + /* Start address must align on block boundary */ + if (ofs & ((1 << chip->phys_erase_shift) - 1)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + ret = -EINVAL; + } + + /* Length must align on block boundary */ + if (len & ((1 << chip->phys_erase_shift) - 1)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +/** + * nand_release_device - [GENERIC] release chip + * @mtd: MTD device structure + * + * Deselect, release chip lock and wake up anyone waiting on the device. + */ +static void nand_release_device(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + /* De-select the NAND device */ + chip->select_chip(mtd, -1); +} + +/** + * nand_read_byte - [DEFAULT] read one byte from the chip + * @mtd: MTD device structure + * + * Default read function for 8bit buswidth. + */ +uint8_t nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + return readb(chip->IO_ADDR_R); +} + +/** + * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip + * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswidth with endianness conversion. + * + */ +static uint8_t nand_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); +} + +/** + * nand_read_word - [DEFAULT] read one word from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswidth without endianness conversion. + */ +static u16 nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + return readw(chip->IO_ADDR_R); +} + +/** + * nand_select_chip - [DEFAULT] control CE line + * @mtd: MTD device structure + * @chipnr: chipnumber to select, -1 for deselect + * + * Default select function for 1 chip devices. + */ +static void nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct nand_chip *chip = mtd->priv; + + switch (chipnr) { + case -1: + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + break; + case 0: + break; + + default: + BUG(); + } +} + +/** + * nand_write_buf - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 8bit buswidth. + */ +void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) + writeb(buf[i], chip->IO_ADDR_W); +} + +/** + * nand_read_buf - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 8bit buswidth. + */ +void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = readb(chip->IO_ADDR_R); +} + +/** + * nand_verify_buf - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 8bit buswidth. + */ +static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) + if (buf[i] != readb(chip->IO_ADDR_R)) + return -EFAULT; + return 0; +} + +/** + * nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 16bit buswidth. + */ +void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + writew(p[i], chip->IO_ADDR_W); + +} + +/** + * nand_read_buf16 - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 16bit buswidth. + */ +void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + p[i] = readw(chip->IO_ADDR_R); +} + +/** + * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 16bit buswidth. + */ +static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + if (p[i] != readw(chip->IO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/** + * nand_block_bad - [DEFAULT] Read bad block marker from the chip + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * + * Check, if the block is bad. + */ +static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + int page, chipnr, res = 0, i = 0; + struct nand_chip *chip = mtd->priv; + u16 bad; + + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; + + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + + if (getchip) { + chipnr = (int)(ofs >> chip->chip_shift); + + nand_get_device(chip, mtd, FL_READING); + + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); + } + + do { + if (chip->options & NAND_BUSWIDTH_16) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, + chip->badblockpos & 0xFE, page); + bad = cpu_to_le16(chip->read_word(mtd)); + if (chip->badblockpos & 0x1) + bad >>= 8; + else + bad &= 0xFF; + } else { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, + page); + bad = chip->read_byte(mtd); + } + + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + ofs += mtd->writesize; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + i++; + } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); + + if (getchip) + nand_release_device(mtd); + + return res; +} + +/** + * nand_default_block_markbad - [DEFAULT] mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This is the default implementation, which can be overridden by a hardware + * specific driver. We try operations in the following order, according to our + * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + * (1) erase the affected block, to allow OOB marker to be written cleanly + * (2) update in-memory BBT + * (3) write bad block marker to OOB area of affected block + * (4) update flash-based BBT + * Note that we retain the first error encountered in (3) or (4), finish the + * procedures, and dump the error in the end. +*/ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + uint8_t buf[2] = { 0, 0 }; + int block, res, ret = 0, i = 0; + int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); + + if (write_oob) { + struct erase_info einfo; + + /* Attempt erase before marking OOB */ + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = ofs; + einfo.len = 1 << chip->phys_erase_shift; + nand_erase_nand(mtd, &einfo, 0); + } + + /* Get block number */ + block = (int)(ofs >> chip->bbt_erase_shift); + /* Mark block bad in memory-based BBT */ + if (chip->bbt) + chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + + /* Write bad block marker to OOB */ + if (write_oob) { + struct mtd_oob_ops ops; + loff_t wr_ofs = ofs; + + nand_get_device(chip, mtd, FL_WRITING); + + ops.datbuf = NULL; + ops.oobbuf = buf; + ops.ooboffs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + ops.ooboffs &= ~0x01; + ops.len = ops.ooblen = 2; + } else { + ops.len = ops.ooblen = 1; + } + ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + wr_ofs += mtd->erasesize - mtd->writesize; + do { + res = nand_do_write_oob(mtd, wr_ofs, &ops); + if (!ret) + ret = res; + + i++; + wr_ofs += mtd->writesize; + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); + + nand_release_device(mtd); + } + + /* Update flash-based bad block table */ + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + res = nand_update_bbt(mtd, ofs); + if (!ret) + ret = res; + } + + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} + +/** + * nand_check_wp - [GENERIC] check if the chip is write protected + * @mtd: MTD device structure + * + * Check, if the device is write protected. The function expects, that the + * device is already selected. + */ +static int nand_check_wp(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + /* Broken xD cards report WP despite being writable */ + if (chip->options & NAND_BROKEN_XD) + return 0; + + /* Check the WP bit */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; +} + +/** + * nand_block_checkbad - [GENERIC] Check if a block is marked bad + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * @allowbbt: 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, + int allowbbt) +{ + struct nand_chip *chip = mtd->priv; + + if (!(chip->options & NAND_BBT_SCANNED)) { + chip->options |= NAND_BBT_SCANNED; + chip->scan_bbt(mtd); + } + + if (!chip->bbt) + return chip->block_bad(mtd, ofs, getchip); + + /* Return info from the table */ + return nand_isbad_bbt(mtd, ofs, allowbbt); +} + +/* Wait for the ready pin, after a command. The timeout is caught later. */ +void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + u32 timeo = (CONFIG_SYS_HZ * 20) / 1000; + u32 time_start; + + time_start = get_timer(0); + + /* Wait until command is processed or timeout occurs */ + while (get_timer(time_start) < timeo) { + if (chip->dev_ready) + if (chip->dev_ready(mtd)) + break; + } +} + +/** + * nand_command - [DEFAULT] Send command to NAND device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This function is used for small page devices + * (256/512 Bytes per page). + */ +static void nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; + uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT; + + /* Write out the command to the device */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + chip->cmd_ctrl(mtd, readcmd, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + chip->cmd_ctrl(mtd, command, ctrl); + + /* Address cycle, when necessary */ + ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); + /* One more address cycle for devices > 32MiB */ + if (chip->chipsize > (32 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Program and erase have their own busy handlers status and sequential + * in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, + NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY) && + (rst_sts_cnt--)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + /* + * Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. + */ + ndelay(100); + + nand_wait_ready(mtd); +} + +/** + * nand_command_lp - [DEFAULT] Send command to NAND large page device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This is the version for the new large page + * devices. We don't have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. + */ +static void nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT; + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Command latch cycle */ + chip->cmd_ctrl(mtd, command & 0xff, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + + if (column != -1 || page_addr != -1) { + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, column >> 8, ctrl); + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + chip->cmd_ctrl(mtd, page_addr >> 8, + NAND_NCE | NAND_ALE); + /* One more address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); + } + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Program and erase have their own busy handlers status, sequential + * in, and deplete1 need no delay. + */ + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + return; + + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + /* Read error status commands require only a short delay */ + udelay(chip->chip_delay); + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY) && + (rst_sts_cnt--)); + return; + + case NAND_CMD_RNDOUT: + /* No ready / busy check necessary */ + chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + return; + + case NAND_CMD_READ0: + chip->cmd_ctrl(mtd, NAND_CMD_READSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay. + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + + /* + * Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. + */ + ndelay(100); + + nand_wait_ready(mtd); +} + +/** + * nand_get_device - [GENERIC] Get chip for selected access + * @chip: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Get the device and lock it for exclusive access + */ +static int +nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) +{ + chip->state = new_state; + return 0; +} + +/** + * nand_wait - [DEFAULT] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * + * Wait for command done. This applies to erase and program only. Erase can + * take up to 400ms and program up to 20ms according to general NAND and + * SmartMedia specs. + */ +static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + unsigned long timeo; + int state = chip->state; + u32 time_start; + + if (state == FL_ERASING) + timeo = (CONFIG_SYS_HZ * 400) / 1000; + else + timeo = (CONFIG_SYS_HZ * 20) / 1000; + + if ((state == FL_ERASING) && (chip->options & NAND_IS_AND)) + chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); + else + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + + time_start = get_timer(0); + + while (1) { + if (get_timer(time_start) > timeo) { + printf("Timeout!"); + return 0x01; + } + + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + } +#ifdef PPCHAMELON_NAND_TIMER_HACK + time_start = get_timer(0); + while (get_timer(time_start) < 10) + ; +#endif /* PPCHAMELON_NAND_TIMER_HACK */ + + return (int)chip->read_byte(mtd); +} + +/** + * nand_read_page_raw - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Not for syndrome calculating ECC controllers, which use a special oob layout. + */ +static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + chip->read_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +/** + * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * We need a special oob layout and handling even when OOB isn't used. + */ +static int nand_read_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->read_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->read_buf(mtd, oob, size); + + return 0; +} + +/** + * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + */ +static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * nand_read_subpage - [REPLACEABLE] software ECC based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @data_offs: offset of requested data within the page + * @readlen: data length + * @bufpoi: buffer to store read data + */ +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) +{ + int start_step, end_step, num_steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p; + int data_col_addr, i, gaps = 0; + int datafrag_len, eccfrag_len, aligned_len, aligned_pos; + int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + int index = 0; + + /* Column address within the page aligned to ECC size (256bytes) */ + start_step = data_offs / chip->ecc.size; + end_step = (data_offs + readlen - 1) / chip->ecc.size; + num_steps = end_step - start_step + 1; + + /* Data size aligned to ECC ecc.size */ + datafrag_len = num_steps * chip->ecc.size; + eccfrag_len = num_steps * chip->ecc.bytes; + + data_col_addr = start_step * chip->ecc.size; + /* If we read not a page aligned data */ + if (data_col_addr != 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); + + p = bufpoi + data_col_addr; + chip->read_buf(mtd, p, datafrag_len); + + /* Calculate ECC */ + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) + chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + + /* + * The performance is faster if we position offsets according to + * ecc.pos. Let's make sure that there are no gaps in ECC positions. + */ + for (i = 0; i < eccfrag_len - 1; i++) { + if (eccpos[i + start_step * chip->ecc.bytes] + 1 != + eccpos[i + start_step * chip->ecc.bytes + 1]) { + gaps = 1; + break; + } + } + if (gaps) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + } else { + /* + * Send the command to read the particular ECC bytes take care + * about buswidth alignment in read_buf. + */ + index = start_step * chip->ecc.bytes; + + aligned_pos = eccpos[index] & ~(busw - 1); + aligned_len = eccfrag_len; + if (eccpos[index] & (busw - 1)) + aligned_len++; + if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1)) + aligned_len++; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + aligned_pos, -1); + chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + } + + for (i = 0; i < eccfrag_len; i++) + chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]]; + + p = bufpoi + data_col_addr; + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + int stat; + + stat = chip->ecc.correct(mtd, p, + &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Not for syndrome calculating ECC controllers which need a special oob layout. + */ +static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Hardware ECC for large page chips, require OOB to be read first. For this + * ECC mode, the write_page method is re-used from ECC_HW. These methods + * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with + * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from + * the data area, by overwriting the NAND manufacturer bad block markings. + */ +static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *ecc_calc = chip->buffers->ecccalc; + + /* Read the OOB area first */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling. + */ +static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.hwctl(mtd, NAND_ECC_READSYN); + chip->read_buf(mtd, oob, eccbytes); + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->read_buf(mtd, oob, i); + + return 0; +} + +/** + * nand_transfer_oob - [INTERN] Transfer oob to client buffer + * @chip: nand chip structure + * @oob: oob destination address + * @ops: oob ops structure + * @len: size of oob to transfer + */ +static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops, size_t len) +{ + switch (ops->mode) { + + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + memcpy(oob, chip->oob_poi + ops->ooboffs, len); + return oob + len; + + case MTD_OPS_AUTO_OOB: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, roffs = ops->ooboffs; + size_t bytes = 0; + + for (; free->length && len; free++, len -= bytes) { + /* Read request not from offset 0? */ + if (unlikely(roffs)) { + if (roffs >= free->length) { + roffs -= free->length; + continue; + } + boffs = free->offset + roffs; + bytes = min_t(size_t, len, + (free->length - roffs)); + roffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(oob, chip->oob_poi + boffs, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +/** + * nand_do_read_ops - [INTERN] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob ops structure + * + * Internal function. Called with chip held. + */ +static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int chipnr, page, realpage, col, bytes, aligned, oob_required; + struct nand_chip *chip = mtd->priv; + struct mtd_ecc_stats stats; + int ret = 0; + uint32_t readlen = ops->len; + uint32_t oobreadlen = ops->ooblen; + uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ? + mtd->oobavail : mtd->oobsize; + + uint8_t *bufpoi, *oob, *buf; + unsigned int max_bitflips = 0; + + stats = mtd->ecc_stats; + + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; + + col = (int)(from & (mtd->writesize - 1)); + + buf = ops->datbuf; + oob = ops->oobbuf; + oob_required = oob ? 1 : 0; + + while (1) { + WATCHDOG_RESET(); + + bytes = min(mtd->writesize - col, readlen); + aligned = (bytes == mtd->writesize); + + /* Is the current page in the buffer? */ + if (realpage != chip->pagebuf || oob) { + bufpoi = aligned ? buf : chip->buffers->databuf; + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + + /* + * Now read the page into the buffer. Absent an error, + * the read methods return max bitflips per ecc step. + */ + if (unlikely(ops->mode == MTD_OPS_RAW)) + ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, + oob_required, + page); + else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && + !oob) + ret = chip->ecc.read_subpage(mtd, chip, + col, bytes, bufpoi); + else + ret = chip->ecc.read_page(mtd, chip, bufpoi, + oob_required, page); + if (ret < 0) { + if (!aligned) + /* Invalidate page cache */ + chip->pagebuf = -1; + break; + } + + max_bitflips = max_t(unsigned int, max_bitflips, ret); + + /* Transfer not aligned data */ + if (!aligned) { + if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && + !(mtd->ecc_stats.failed - stats.failed) && + (ops->mode != MTD_OPS_RAW)) { + chip->pagebuf = realpage; + chip->pagebuf_bitflips = ret; + } else { + /* Invalidate page cache */ + chip->pagebuf = -1; + } + memcpy(buf, chip->buffers->databuf + col, bytes); + } + + buf += bytes; + + if (unlikely(oob)) { + int toread = min(oobreadlen, max_oobsize); + + if (toread) { + oob = nand_transfer_oob(chip, + oob, ops, toread); + oobreadlen -= toread; + } + } + } else { + memcpy(buf, chip->buffers->databuf + col, bytes); + buf += bytes; + max_bitflips = max_t(unsigned int, max_bitflips, + chip->pagebuf_bitflips); + } + + readlen -= bytes; + + if (!readlen) + break; + + /* For subsequent reads align to page boundary */ + col = 0; + /* Increment page address */ + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + + ops->retlen = ops->len - (size_t) readlen; + if (oob) + ops->oobretlen = ops->ooblen - oobreadlen; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return max_bitflips; +} + +/** + * nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * Get hold of the chip and call nand_do_read. + */ +static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; + int ret; + + nand_get_device(chip, mtd, FL_READING); + ops.len = len; + ops.datbuf = buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_read_ops(mtd, from, &ops); + *retlen = ops.retlen; + nand_release_device(mtd); + return ret; +} + +/** + * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + */ +static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +/** + * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC + * with syndromes + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + */ +static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size; + uint8_t *bufpoi = buf; + int i, toread, sndrnd = 0, pos; + + chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); + for (i = 0; i < chip->ecc.steps; i++) { + if (sndrnd) { + pos = eccsize + i * (eccsize + chunk); + if (mtd->writesize > 512) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); + else + chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); + } else + sndrnd = 1; + toread = min_t(int, length, chunk); + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + if (length > 0) + chip->read_buf(mtd, bufpoi, length); + + return 0; +} + +/** + * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0; + const uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, buf, length); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC + * with syndrome - only for large page flash + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size, length = mtd->oobsize; + int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; + + /* + * data-ecc-data-ecc ... ecc-oob + * or + * data-pad-ecc-pad-data-pad .... ecc-pad-oob + */ + if (!chip->ecc.prepad && !chip->ecc.postpad) { + pos = steps * (eccsize + chunk); + steps = 0; + } else + pos = eccsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + for (i = 0; i < steps; i++) { + if (sndcmd) { + if (mtd->writesize <= 512) { + uint32_t fill = 0xFFFFFFFF; + + len = eccsize; + while (len > 0) { + int num = min_t(int, len, 4); + chip->write_buf(mtd, (uint8_t *)&fill, + num); + len -= num; + } + } else { + pos = eccsize + i * (eccsize + chunk); + chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); + } + } else + sndcmd = 1; + len = min_t(int, length, chunk); + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + } + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * nand_do_read_oob - [INTERN] NAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operations description structure + * + * NAND read out-of-band data from the spare area. + */ +static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int page, realpage, chipnr; + struct nand_chip *chip = mtd->priv; + struct mtd_ecc_stats stats; + int readlen = ops->ooblen; + int len; + uint8_t *buf = ops->oobbuf; + int ret = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", + __func__, (unsigned long long)from, readlen); + + stats = mtd->ecc_stats; + + if (ops->mode == MTD_OPS_AUTO_OOB) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + + if (unlikely(ops->ooboffs >= len)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read " + "outside oob\n", __func__); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(from >= mtd->size || + ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - + (from >> chip->page_shift)) * len)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end " + "of device\n", __func__); + return -EINVAL; + } + + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Shift to get page */ + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; + + while (1) { + WATCHDOG_RESET(); + if (ops->mode == MTD_OPS_RAW) + ret = chip->ecc.read_oob_raw(mtd, chip, page); + else + ret = chip->ecc.read_oob(mtd, chip, page); + + if (ret < 0) + break; + + len = min(len, readlen); + buf = nand_transfer_oob(chip, buf, ops, len); + + readlen -= len; + if (!readlen) + break; + + /* Increment page address */ + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + + ops->oobretlen = ops->ooblen - readlen; + + if (ret < 0) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +} + +/** + * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + * + * NAND read data and/or out-of-band data. + */ +static int nand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow reads past end of device */ + if (ops->datbuf && (from + ops->len) > mtd->size) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read " + "beyond end of device\n", __func__); + return -EINVAL; + } + + nand_get_device(chip, mtd, FL_READING); + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_read_oob(mtd, from, ops); + else + ret = nand_do_read_ops(mtd, from, ops); + +out: + nand_release_device(mtd); + return ret; +} + + +/** + * nand_write_page_raw - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * + * Not for syndrome calculating ECC controllers, which use a special oob layout. + */ +static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + chip->write_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +/** + * nand_write_page_raw_syndrome - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * + * We need a special oob layout and handling even when ECC isn't checked. + */ +static int nand_write_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->write_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->write_buf(mtd, oob, size); + + return 0; +} +/** + * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + */ +static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + /* Software ECC calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + return chip->ecc.write_page_raw(mtd, chip, buf, 1); +} + +/** + * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + */ +static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +/** + * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling. + */ +static int nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.calculate(mtd, p, oob); + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->write_buf(mtd, oob, i); + + return 0; +} + +/** + * nand_write_page - [REPLACEABLE] write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * @cached: cached programming + * @raw: use _raw version of write_page + */ +static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page, + int cached, int raw) +{ + int status; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required); + else + status = chip->ecc.write_page(mtd, chip, buf, oob_required); + + if (status < 0) + return status; + + /* + * Cached progamming disabled for now. Not sure if it's worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s). + */ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + /* + * See if operation failed and additional status checks are + * available. + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, + page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + } else { + chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) + return -EIO; + + /* Make sure the next page prog is preceded by a status read */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); +#endif + return 0; +} + +/** + * nand_fill_oob - [INTERN] Transfer client buffer to oob + * @mtd: MTD device structure + * @oob: oob data buffer + * @len: oob data write length + * @ops: oob ops structure + */ +static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + + /* + * Initialise to all 0xFF, to avoid the possibility of left over OOB + * data from a previous OOB read. + */ + memset(chip->oob_poi, 0xff, mtd->oobsize); + + switch (ops->mode) { + + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OPS_AUTO_OOB: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, woffs = ops->ooboffs; + size_t bytes = 0; + + for (; free->length && len; free++, len -= bytes) { + /* Write request not from offset 0? */ + if (unlikely(woffs)) { + if (woffs >= free->length) { + woffs -= free->length; + continue; + } + boffs = free->offset + woffs; + bytes = min_t(size_t, len, + (free->length - woffs)); + woffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + boffs, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0) + +/** + * nand_do_write_ops - [INTERN] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operations description structure + * + * NAND write with ECC. + */ +static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, realpage, page, blockmask, column; + struct nand_chip *chip = mtd->priv; + uint32_t writelen = ops->len; + + uint32_t oobwritelen = ops->ooblen; + uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ? + mtd->oobavail : mtd->oobsize; + + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; + int ret, subpage; + int oob_required = oob ? 1 : 0; + + ops->retlen = 0; + if (!writelen) + return 0; + + column = to & (mtd->writesize - 1); + subpage = column || (writelen & (mtd->writesize - 1)); + + if (subpage && oob) + return -EINVAL; + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n"); + return -EIO; + } + + realpage = (int)(to >> chip->page_shift); + page = realpage & chip->pagemask; + blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + + /* Invalidate the page cache, when we write to the cached page */ + if (to <= (chip->pagebuf << chip->page_shift) && + (chip->pagebuf << chip->page_shift) < (to + ops->len)) + chip->pagebuf = -1; + + /* Don't allow multipage oob writes with offset */ + if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) + return -EINVAL; + + while (1) { + WATCHDOG_RESET(); + + int bytes = mtd->writesize; + int cached = writelen > bytes && page != blockmask; + uint8_t *wbuf = buf; + + /* Partial page write? */ + if (unlikely(column || writelen < mtd->writesize)) { + cached = 0; + bytes = min_t(int, bytes - column, (int) writelen); + chip->pagebuf = -1; + memset(chip->buffers->databuf, 0xff, mtd->writesize); + memcpy(&chip->buffers->databuf[column], buf, bytes); + wbuf = chip->buffers->databuf; + } + + if (unlikely(oob)) { + size_t len = min(oobwritelen, oobmaxlen); + oob = nand_fill_oob(mtd, oob, len, ops); + oobwritelen -= len; + } else { + /* We still need to erase leftover OOB data */ + memset(chip->oob_poi, 0xff, mtd->oobsize); + } + + ret = chip->write_page(mtd, chip, wbuf, oob_required, page, + cached, (ops->mode == MTD_OPS_RAW)); + if (ret) + break; + + writelen -= bytes; + if (!writelen) + break; + + column = 0; + buf += bytes; + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + + ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; + return ret; +} + +/** + * nand_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. + */ +static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; + int ret; + + nand_get_device(chip, mtd, FL_WRITING); + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_write_ops(mtd, to, &ops); + *retlen = ops.retlen; + nand_release_device(mtd); + return ret; +} + +/** + * nand_do_write_oob - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + * + * NAND write out-of-band. + */ +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, page, status, len; + struct nand_chip *chip = mtd->priv; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", + __func__, (unsigned int)to, (int)ops->ooblen); + + if (ops->mode == MTD_OPS_AUTO_OOB) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + + /* Do not allow write past end of page */ + if ((ops->ooboffs + ops->ooblen) > len) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write " + "past end of page\n", __func__); + return -EINVAL; + } + + if (unlikely(ops->ooboffs >= len)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start " + "write outside oob\n", __func__); + return -EINVAL; + } + + /* Do not allow write past end of device */ + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > + ((mtd->size >> chip->page_shift) - + (to >> chip->page_shift)) * len)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " + "end of device\n", __func__); + return -EINVAL; + } + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Shift to get page */ + page = (int)(to >> chip->page_shift); + + /* + * Reset the chip. Some chips (like the Toshiba TC5832DC found in one + * of my DiskOnChip 2000 test units) will clear the whole data page too + * if we don't do this. I have no clue why, but I seem to have 'fixed' + * it in the doc2000 driver in August 1999. dwmw2. + */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + return -EROFS; + + /* Invalidate the page cache, if we write to the cached page */ + if (page == chip->pagebuf) + chip->pagebuf = -1; + + nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); + + if (ops->mode == MTD_OPS_RAW) + status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); + else + status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + + if (status) + return status; + + ops->oobretlen = ops->ooblen; + + return 0; +} + +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + */ +static int nand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " + "end of device\n", __func__); + return -EINVAL; + } + + nand_get_device(chip, mtd, FL_WRITING); + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(mtd, to, ops); + else + ret = nand_do_write_ops(mtd, to, ops); + +out: + nand_release_device(mtd); + return ret; +} + +/** + * single_erase_cmd - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * Standard erase command for NAND chips. + */ +static void single_erase_cmd(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + /* Send commands to erase a block */ + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * multi_erase_cmd - [GENERIC] AND specific block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * AND multi block erase command function. Erase 4 consecutive blocks. + */ +static void multi_erase_cmd(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + /* Send commands to erase a block */ + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * nand_erase - [MTD Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks. + */ +static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + return nand_erase_nand(mtd, instr, 0); +} + +#define BBT_PAGE_MASK 0xffffff3f +/** + * nand_erase_nand - [INTERN] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area + * + * Erase one ore more blocks. + */ +int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + int allowbbt) +{ + int page, status, pages_per_block, ret, chipnr; + struct nand_chip *chip = mtd->priv; + loff_t rewrite_bbt[CONFIG_SYS_NAND_MAX_CHIPS] = {0}; + unsigned int bbt_masked_page = 0xffffffff; + loff_t len; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)instr->addr, + (unsigned long long)instr->len); + + if (check_offs_len(mtd, instr->addr, instr->len)) + return -EINVAL; + + /* Grab the lock and see if the device is available */ + nand_get_device(chip, mtd, FL_ERASING); + + /* Shift to get first page */ + page = (int)(instr->addr >> chip->page_shift); + chipnr = (int)(instr->addr >> chip->chip_shift); + + /* Calculate pages in each block */ + pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); + + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* + * If BBT requires refresh, set the BBT page mask to see if the BBT + * should be rewritten. Otherwise the mask is set to 0xffffffff which + * can not be matched. This is also done when the bbt is actually + * erased to avoid recursive updates. + */ + if (chip->options & BBT_AUTO_REFRESH && !allowbbt) + bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + + /* Loop through the pages */ + len = instr->len; + + instr->state = MTD_ERASING; + + while (len) { + WATCHDOG_RESET(); + /* Check if we have a bad block, we do not erase bad blocks! */ + if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) << + chip->page_shift, 0, allowbbt)) { + pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", + __func__, page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* + * Invalidate the page cache, if we erase the block which + * contains the current cached page. + */ + if (page <= chip->pagebuf && chip->pagebuf < + (page + pages_per_block)) + chip->pagebuf = -1; + + chip->erase_cmd(mtd, page & chip->pagemask); + + status = chip->waitfunc(mtd, chip); + + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_ERASING, + status, page); + + /* See if block erase succeeded */ + if (status & NAND_STATUS_FAIL) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, " + "page 0x%08x\n", __func__, page); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = + ((loff_t)page << chip->page_shift); + goto erase_exit; + } + + /* + * If BBT requires refresh, set the BBT rewrite flag to the + * page being erased. + */ + if (bbt_masked_page != 0xffffffff && + (page & BBT_PAGE_MASK) == bbt_masked_page) + rewrite_bbt[chipnr] = + ((loff_t)page << chip->page_shift); + + /* Increment page address and decrement length */ + len -= (1 << chip->phys_erase_shift); + page += pages_per_block; + + /* Check, if we cross a chip boundary */ + if (len && !(page & chip->pagemask)) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + + /* + * If BBT requires refresh and BBT-PERCHIP, set the BBT + * page mask to see if this BBT should be rewritten. + */ + if (bbt_masked_page != 0xffffffff && + (chip->bbt_td->options & NAND_BBT_PERCHIP)) + bbt_masked_page = chip->bbt_td->pages[chipnr] & + BBT_PAGE_MASK; + } + } + instr->state = MTD_ERASE_DONE; + +erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); + + /* + * If BBT requires refresh and erase was successful, rewrite any + * selected bad block tables. + */ + if (bbt_masked_page == 0xffffffff || ret) + return ret; + + for (chipnr = 0; chipnr < chip->numchips; chipnr++) { + if (!rewrite_bbt[chipnr]) + continue; + /* Update the BBT for chip */ + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt " + "(%d:0x%0llx 0x%0x)\n", __func__, chipnr, + rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); + nand_update_bbt(mtd, rewrite_bbt[chipnr]); + } + + /* Return more or less happy */ + return ret; +} + +/** + * nand_sync - [MTD Interface] sync + * @mtd: MTD device structure + * + * Sync is actually a wait for chip ready function. + */ +static void nand_sync(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__); + + /* Grab the lock and see if the device is available */ + nand_get_device(chip, mtd, FL_SYNCING); + /* Release it and go back */ + nand_release_device(mtd); +} + +/** + * nand_block_isbad - [MTD Interface] Check if block at offset is bad + * @mtd: MTD device structure + * @offs: offset relative to mtd start + */ +static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + return nand_block_checkbad(mtd, offs, 1, 0); +} + +/** + * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + int ret; + + ret = nand_block_isbad(mtd, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + return chip->block_markbad(mtd, ofs); +} + + /** + * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand + * @mtd: MTD device structure + * @chip: nand chip info structure + * @addr: feature address. + * @subfeature_param: the subfeature parameters, a four bytes array. + */ +static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, uint8_t *subfeature_param) +{ + int status; + + if (!chip->onfi_version) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); + chip->write_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN); + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + return 0; +} + +/** + * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand + * @mtd: MTD device structure + * @chip: nand chip info structure + * @addr: feature address. + * @subfeature_param: the subfeature parameters, a four bytes array. + */ +static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, uint8_t *subfeature_param) +{ + if (!chip->onfi_version) + return -EINVAL; + + /* clear the sub feature parameters */ + memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN); + + chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); + chip->read_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN); + return 0; +} + +/* Set default functions */ +static void nand_set_defaults(struct nand_chip *chip, int busw) +{ + /* check for proper chip_delay setup, set 20us if not */ + if (!chip->chip_delay) + chip->chip_delay = 20; + + /* check, if a user supplied command function given */ + if (chip->cmdfunc == NULL) + chip->cmdfunc = nand_command; + + /* check, if a user supplied wait function given */ + if (chip->waitfunc == NULL) + chip->waitfunc = nand_wait; + + if (!chip->select_chip) + chip->select_chip = nand_select_chip; + if (!chip->read_byte) + chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; + if (!chip->read_word) + chip->read_word = nand_read_word; + if (!chip->block_bad) + chip->block_bad = nand_block_bad; + if (!chip->block_markbad) + chip->block_markbad = nand_default_block_markbad; + if (!chip->write_buf) + chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; + if (!chip->read_buf) + chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; + if (!chip->verify_buf) + chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; + if (!chip->scan_bbt) + chip->scan_bbt = nand_default_bbt; + if (!chip->controller) + chip->controller = &chip->hwcontrol; +} + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION +/* Sanitize ONFI strings so we can safely print them */ +static void sanitize_string(char *s, size_t len) +{ + ssize_t i; + + /* Null terminate */ + s[len - 1] = 0; + + /* Remove non printable chars */ + for (i = 0; i < len - 1; i++) { + if (s[i] < ' ' || s[i] > 127) + s[i] = '?'; + } + + /* Remove trailing spaces */ + strim(s); +} + +static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++ << 8; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); + } + + return crc; +} + +/* + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. + */ +static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, + int *busw) +{ + struct nand_onfi_params *p = &chip->onfi_params; + int i; + int val; + + /* Try ONFI for unknown chip or LP */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); + if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || + chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') + return 0; + + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + for (i = 0; i < 3; i++) { + chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == + le16_to_cpu(p->crc)) { + pr_info("ONFI param page %d valid\n", i); + break; + } + } + + if (i == 3) + return 0; + + /* Check version */ + val = le16_to_cpu(p->revision); + if (val & (1 << 5)) + chip->onfi_version = 23; + else if (val & (1 << 4)) + chip->onfi_version = 22; + else if (val & (1 << 3)) + chip->onfi_version = 21; + else if (val & (1 << 2)) + chip->onfi_version = 20; + else if (val & (1 << 1)) + chip->onfi_version = 10; + else + chip->onfi_version = 0; + + if (!chip->onfi_version) { + pr_info("%s: unsupported ONFI version: %d\n", __func__, val); + return 0; + } + + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); + if (!mtd->name) + mtd->name = p->model; + mtd->writesize = le32_to_cpu(p->byte_per_page); + mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + chip->chipsize = le32_to_cpu(p->blocks_per_lun); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; + *busw = 0; + if (le16_to_cpu(p->features) & 1) + *busw = NAND_BUSWIDTH_16; + + pr_info("ONFI flash detected\n"); + return 1; +} +#else +static inline int nand_flash_detect_onfi(struct mtd_info *mtd, + struct nand_chip *chip, + int *busw) +{ + return 0; +} +#endif + +/* + * nand_id_has_period - Check if an ID string has a given wraparound period + * @id_data: the ID string + * @arrlen: the length of the @id_data array + * @period: the period of repitition + * + * Check if an ID string is repeated within a given sequence of bytes at + * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a + * period of 2). This is a helper function for nand_id_len(). Returns non-zero + * if the repetition has a period of @period; otherwise, returns zero. + */ +static int nand_id_has_period(u8 *id_data, int arrlen, int period) +{ + int i, j; + for (i = 0; i < period; i++) + for (j = i + period; j < arrlen; j += period) + if (id_data[i] != id_data[j]) + return 0; + return 1; +} + +/* + * nand_id_len - Get the length of an ID string returned by CMD_READID + * @id_data: the ID string + * @arrlen: the length of the @id_data array + + * Returns the length of the ID string, according to known wraparound/trailing + * zero patterns. If no pattern exists, returns the length of the array. + */ +static int nand_id_len(u8 *id_data, int arrlen) +{ + int last_nonzero, period; + + /* Find last non-zero byte */ + for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--) + if (id_data[last_nonzero]) + break; + + /* All zeros */ + if (last_nonzero < 0) + return 0; + + /* Calculate wraparound period */ + for (period = 1; period < arrlen; period++) + if (nand_id_has_period(id_data, arrlen, period)) + break; + + /* There's a repeated pattern */ + if (period < arrlen) + return period; + + /* There are trailing zeros */ + if (last_nonzero < arrlen - 1) + return last_nonzero + 1; + + /* No pattern detected */ + return arrlen; +} + +/* + * Many new NAND share similar device ID codes, which represent the size of the + * chip. The rest of the parameters must be decoded according to generic or + * manufacturer-specific "extended ID" decoding patterns. + */ +static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, + u8 id_data[8], int *busw) +{ + int extid, id_len; + /* The 3rd id byte holds MLC / multichip data */ + chip->cellinfo = id_data[2]; + /* The 4th id byte is the important one */ + extid = id_data[3]; + + id_len = nand_id_len(id_data, 8); + + /* + * Field definitions are in the following datasheets: + * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) + * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) + * Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22) + * + * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung + * ID to decide what to do. + */ + if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG && + (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + id_data[5] != 0x00) { + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + switch (((extid >> 2) & 0x04) | (extid & 0x03)) { + case 1: + mtd->oobsize = 128; + break; + case 2: + mtd->oobsize = 218; + break; + case 3: + mtd->oobsize = 400; + break; + case 4: + mtd->oobsize = 436; + break; + case 5: + mtd->oobsize = 512; + break; + case 6: + default: /* Other cases are "reserved" (unknown) */ + mtd->oobsize = 640; + break; + } + extid >>= 2; + /* Calc blocksize */ + mtd->erasesize = (128 * 1024) << + (((extid >> 1) & 0x04) | (extid & 0x03)); + *busw = 0; + } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX && + (chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { + unsigned int tmp; + + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + switch (((extid >> 2) & 0x04) | (extid & 0x03)) { + case 0: + mtd->oobsize = 128; + break; + case 1: + mtd->oobsize = 224; + break; + case 2: + mtd->oobsize = 448; + break; + case 3: + mtd->oobsize = 64; + break; + case 4: + mtd->oobsize = 32; + break; + case 5: + mtd->oobsize = 16; + break; + default: + mtd->oobsize = 640; + break; + } + extid >>= 2; + /* Calc blocksize */ + tmp = ((extid >> 1) & 0x04) | (extid & 0x03); + if (tmp < 0x03) + mtd->erasesize = (128 * 1024) << tmp; + else if (tmp == 0x03) + mtd->erasesize = 768 * 1024; + else + mtd->erasesize = (64 * 1024) << tmp; + *busw = 0; + } else { + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * + (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + } +} + + /* + * Old devices have chip data hardcoded in the device ID table. nand_decode_id + * decodes a matching ID table entry and assigns the MTD size parameters for + * the chip. + */ +static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, + const struct nand_flash_dev *type, u8 id_data[8], + int *busw) +{ + int maf_id = id_data[0]; + + mtd->erasesize = type->erasesize; + mtd->writesize = type->pagesize; + mtd->oobsize = mtd->writesize / 32; + *busw = type->options & NAND_BUSWIDTH_16; + + /* + * Check for Spansion/AMD ID + repeating 5th, 6th byte since + * some Spansion chips have erasesize that conflicts with size + * listed in nand_ids table. + * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) + */ + if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00 + && id_data[6] == 0x00 && id_data[7] == 0x00 + && mtd->writesize == 512) { + mtd->erasesize = 128 * 1024; + mtd->erasesize <<= ((id_data[3] & 0x03) << 1); + } +} + + /* + * Set the bad block marker/indicator (BBM/BBI) patterns according to some + * heuristic patterns using various detected parameters (e.g., manufacturer, + * page size, cell-type information). + */ +static void nand_decode_bbm_options(struct mtd_info *mtd, + struct nand_chip *chip, u8 id_data[8]) +{ + int maf_id = id_data[0]; + + /* Set the bad block position */ + if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16)) + chip->badblockpos = NAND_LARGE_BADBLOCK_POS; + else + chip->badblockpos = NAND_SMALL_BADBLOCK_POS; + + /* + * Bad block marker is stored in the last page of each block on Samsung + * and Hynix MLC devices; stored in first two pages of each block on + * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba, + * AMD/Spansion, and Macronix. All others scan only the first page. + */ + if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + (maf_id == NAND_MFR_SAMSUNG || + maf_id == NAND_MFR_HYNIX)) + chip->bbt_options |= NAND_BBT_SCANLASTPAGE; + else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + (maf_id == NAND_MFR_SAMSUNG || + maf_id == NAND_MFR_HYNIX || + maf_id == NAND_MFR_TOSHIBA || + maf_id == NAND_MFR_AMD || + maf_id == NAND_MFR_MACRONIX)) || + (mtd->writesize == 2048 && + maf_id == NAND_MFR_MICRON)) + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; +} + +/* + * Get the flash and manufacturer id and lookup if the type is supported. + */ +static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + struct nand_chip *chip, + int busw, + int *maf_id, int *dev_id, + const struct nand_flash_dev *type) +{ + const char *name; + int i, maf_idx; + u8 id_data[8]; + + /* Select the device */ + chip->select_chip(mtd, 0); + + /* + * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) + * after power-up. + */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Send the command for reading device ID */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + *maf_id = chip->read_byte(mtd); + *dev_id = chip->read_byte(mtd); + + /* + * Try again to make sure, as some systems the bus-hold or other + * interface concerns can cause random data which looks like a + * possibly credible NAND flash to appear. If the two results do + * not match, ignore the device completely. + */ + + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read entire ID string */ + for (i = 0; i < 8; i++) + id_data[i] = chip->read_byte(mtd); + + if (id_data[0] != *maf_id || id_data[1] != *dev_id) { + pr_info("%s: second ID read did not match " + "%02x,%02x against %02x,%02x\n", __func__, + *maf_id, *dev_id, id_data[0], id_data[1]); + return ERR_PTR(-ENODEV); + } + + if (!type) + type = nand_flash_ids; + + for (; type->name != NULL; type++) + if (*dev_id == type->id) + break; + + chip->onfi_version = 0; + if (!type->name || !type->pagesize) { + /* Check is chip is ONFI compliant */ + if (nand_flash_detect_onfi(mtd, chip, &busw)) + goto ident_done; + } + + if (!type->name) + return ERR_PTR(-ENODEV); + + if (!mtd->name) + mtd->name = type->name; + + chip->chipsize = (uint64_t)type->chipsize << 20; + + if (!type->pagesize && chip->init_size) { + /* Set the pagesize, oobsize, erasesize by the driver */ + busw = chip->init_size(mtd, chip, id_data); + } else if (!type->pagesize) { + /* Decode parameters from extended ID */ + nand_decode_ext_id(mtd, chip, id_data, &busw); + } else { + nand_decode_id(mtd, chip, type, id_data, &busw); + } + /* Get chip options, preserve non chip based options */ + chip->options |= type->options; + + /* + * Check if chip is not a Samsung device. Do not clear the + * options for chips which do not have an extended id. + */ + if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) + chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; +ident_done: + + /* Try to identify manufacturer */ + for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { + if (nand_manuf_ids[maf_idx].id == *maf_id) + break; + } + + /* + * Check, if buswidth is correct. Hardware drivers should set + * chip correct! + */ + if (busw != (chip->options & NAND_BUSWIDTH_16)) { + pr_info("NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, + *dev_id, nand_manuf_ids[maf_idx].name, mtd->name); + pr_warn("NAND bus width %d instead %d bit\n", + (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, + busw ? 16 : 8); + return ERR_PTR(-EINVAL); + } + + nand_decode_bbm_options(mtd, chip, id_data); + + /* Calculate the address shift from the page size */ + chip->page_shift = ffs(mtd->writesize) - 1; + /* Convert chipsize to number of pages per chip -1 */ + chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; + + chip->bbt_erase_shift = chip->phys_erase_shift = + ffs(mtd->erasesize) - 1; + if (chip->chipsize & 0xffffffff) + chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; + else { + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); + chip->chip_shift += 32 - 1; + } + + chip->badblockbits = 8; + + /* Check for AND chips with 4 page planes */ + if (chip->options & NAND_4PAGE_ARRAY) + chip->erase_cmd = multi_erase_cmd; + else + chip->erase_cmd = single_erase_cmd; + + /* Do not replace user supplied command function! */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + name = type->name; +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + if (chip->onfi_version) + name = chip->onfi_params.model; +#endif + pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)," + " page size: %d, OOB size: %d\n", + *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, + name, + mtd->writesize, mtd->oobsize); + + return type; +} + +/** + * nand_scan_ident - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: number of chips to scan for + * @table: alternative NAND ID table + * + * This is the first phase of the normal nand_scan() function. It reads the + * flash ID and sets up MTD fields accordingly. + * + * The mtd->owner field must be set to the module of the caller. + */ +int nand_scan_ident(struct mtd_info *mtd, int maxchips, + const struct nand_flash_dev *table) +{ + int i, busw, nand_maf_id, nand_dev_id; + struct nand_chip *chip = mtd->priv; + const struct nand_flash_dev *type; + + /* Get buswidth to select the correct functions */ + busw = chip->options & NAND_BUSWIDTH_16; + /* Set the default functions */ + nand_set_defaults(chip, busw); + + /* Read the flash type */ + type = nand_get_flash_type(mtd, chip, busw, + &nand_maf_id, &nand_dev_id, table); + + if (IS_ERR(type)) { +#ifndef CONFIG_SYS_NAND_QUIET_TEST + pr_warn("No NAND device found\n"); +#endif + chip->select_chip(mtd, -1); + return PTR_ERR(type); + } + + /* Check for a chip array */ + for (i = 1; i < maxchips; i++) { + chip->select_chip(mtd, i); + /* See comment in nand_get_flash_type for reset */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + /* Send the command for reading device ID */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + /* Read manufacturer and device IDs */ + if (nand_maf_id != chip->read_byte(mtd) || + nand_dev_id != chip->read_byte(mtd)) + break; + } +#ifdef DEBUG + if (i > 1) + pr_info("%d NAND chips detected\n", i); +#endif + + /* Store the number of chips and calc total size for mtd */ + chip->numchips = i; + mtd->size = i * chip->chipsize; + + return 0; +} + + +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * + * This is the second phase of the normal nand_scan() function. It fills out + * all the uninitialized function pointers with the defaults and scans for a + * bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ + int i; + struct nand_chip *chip = mtd->priv; + + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ + BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH)); + + if (!(chip->options & NAND_OWN_BUFFERS)) + chip->buffers = memalign(ARCH_DMA_MINALIGN, + sizeof(*chip->buffers)); + if (!chip->buffers) + return -ENOMEM; + + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->buffers->databuf + mtd->writesize; + + /* + * If no default placement scheme is given, select an appropriate one. + */ + if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) { + switch (mtd->oobsize) { + case 8: + chip->ecc.layout = &nand_oob_8; + break; + case 16: + chip->ecc.layout = &nand_oob_16; + break; + case 64: + chip->ecc.layout = &nand_oob_64; + break; + case 128: + chip->ecc.layout = &nand_oob_128; + break; + default: + pr_warn("No oob scheme defined for oobsize %d\n", + mtd->oobsize); + } + } + + if (!chip->write_page) + chip->write_page = nand_write_page; + + /* set for ONFI nand */ + if (!chip->onfi_set_features) + chip->onfi_set_features = nand_onfi_set_features; + if (!chip->onfi_get_features) + chip->onfi_get_features = nand_onfi_get_features; + + /* + * Check ECC mode, default to software if 3byte/512byte hardware ECC is + * selected and we have 256 byte pagesize fallback to software ECC + */ + + switch (chip->ecc.mode) { + case NAND_ECC_HW_OOB_FIRST: + /* Similar to NAND_ECC_HW, but a separate read_page handle */ + if (!chip->ecc.calculate || !chip->ecc.correct || + !chip->ecc.hwctl) { + pr_warn("No ECC functions supplied; " + "hardware ECC not possible\n"); + BUG(); + } + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_hwecc_oob_first; + + case NAND_ECC_HW: + /* Use standard hwecc read page function? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_hwecc; + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_hwecc; + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw; + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_std; + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_std; + + case NAND_ECC_HW_SYNDROME: + if ((!chip->ecc.calculate || !chip->ecc.correct || + !chip->ecc.hwctl) && + (!chip->ecc.read_page || + chip->ecc.read_page == nand_read_page_hwecc || + !chip->ecc.write_page || + chip->ecc.write_page == nand_write_page_hwecc)) { + pr_warn("No ECC functions supplied; " + "hardware ECC not possible\n"); + BUG(); + } + /* Use standard syndrome read/write page function? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_syndrome; + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_syndrome; + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw_syndrome; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw_syndrome; + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_syndrome; + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_syndrome; + + if (mtd->writesize >= chip->ecc.size) { + if (!chip->ecc.strength) { + pr_warn("Driver must set ecc.strength when using hardware ECC\n"); + BUG(); + } + break; + } + pr_warn("%d byte HW ECC not possible on " + "%d byte page size, fallback to SW ECC\n", + chip->ecc.size, mtd->writesize); + chip->ecc.mode = NAND_ECC_SOFT; + + case NAND_ECC_SOFT: + chip->ecc.calculate = nand_calculate_ecc; + chip->ecc.correct = nand_correct_data; + chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.read_subpage = nand_read_subpage; + chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + if (!chip->ecc.size) + chip->ecc.size = 256; + chip->ecc.bytes = 3; + chip->ecc.strength = 1; + break; + + case NAND_ECC_SOFT_BCH: + if (!mtd_nand_has_bch()) { + pr_warn("CONFIG_MTD_ECC_BCH not enabled\n"); + return -EINVAL; + } + chip->ecc.calculate = nand_bch_calculate_ecc; + chip->ecc.correct = nand_bch_correct_data; + chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.read_subpage = nand_read_subpage; + chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + /* + * Board driver should supply ecc.size and ecc.bytes values to + * select how many bits are correctable; see nand_bch_init() + * for details. Otherwise, default to 4 bits for large page + * devices. + */ + if (!chip->ecc.size && (mtd->oobsize >= 64)) { + chip->ecc.size = 512; + chip->ecc.bytes = 7; + } + chip->ecc.priv = nand_bch_init(mtd, + chip->ecc.size, + chip->ecc.bytes, + &chip->ecc.layout); + if (!chip->ecc.priv) + pr_warn("BCH ECC initialization failed!\n"); + chip->ecc.strength = + chip->ecc.bytes * 8 / fls(8 * chip->ecc.size); + break; + + case NAND_ECC_NONE: + pr_warn("NAND_ECC_NONE selected by board driver. " + "This is not recommended !!\n"); + chip->ecc.read_page = nand_read_page_raw; + chip->ecc.write_page = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + chip->ecc.write_oob = nand_write_oob_std; + chip->ecc.size = mtd->writesize; + chip->ecc.bytes = 0; + break; + + default: + pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode); + BUG(); + } + + /* For many systems, the standard OOB write also works for raw */ + if (!chip->ecc.read_oob_raw) + chip->ecc.read_oob_raw = chip->ecc.read_oob; + if (!chip->ecc.write_oob_raw) + chip->ecc.write_oob_raw = chip->ecc.write_oob; + + /* + * The number of bytes available for a client to place data into + * the out of band area. + */ + chip->ecc.layout->oobavail = 0; + for (i = 0; chip->ecc.layout->oobfree[i].length + && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) + chip->ecc.layout->oobavail += + chip->ecc.layout->oobfree[i].length; + mtd->oobavail = chip->ecc.layout->oobavail; + + /* + * Set the number of read / write steps for one page depending on ECC + * mode. + */ + chip->ecc.steps = mtd->writesize / chip->ecc.size; + if (chip->ecc.steps * chip->ecc.size != mtd->writesize) { + pr_warn("Invalid ECC parameters\n"); + BUG(); + } + chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; + + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && + !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { + switch (chip->ecc.steps) { + case 2: + mtd->subpage_sft = 1; + break; + case 4: + case 8: + case 16: + mtd->subpage_sft = 2; + break; + } + } + chip->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* Initialize state */ + chip->state = FL_READY; + + /* De-select the device */ + chip->select_chip(mtd, -1); + + /* Invalidate the pagebuffer reference */ + chip->pagebuf = -1; + + /* Large page NAND with SOFT_ECC should support subpage reads */ + if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9)) + chip->options |= NAND_SUBPAGE_READ; + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : + MTD_CAP_NANDFLASH; + mtd->_erase = nand_erase; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_read = nand_read; + mtd->_write = nand_write; + mtd->_read_oob = nand_read_oob; + mtd->_write_oob = nand_write_oob; + mtd->_sync = nand_sync; + mtd->_lock = NULL; + mtd->_unlock = NULL; + mtd->_block_isbad = nand_block_isbad; + mtd->_block_markbad = nand_block_markbad; + + /* propagate ecc info to mtd_info */ + mtd->ecclayout = chip->ecc.layout; + mtd->ecc_strength = chip->ecc.strength; + /* + * Initialize bitflip_threshold to its default prior scan_bbt() call. + * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be + * properly set. + */ + if (!mtd->bitflip_threshold) + mtd->bitflip_threshold = mtd->ecc_strength; + + /* Check, if we should skip the bad block table scan */ + if (chip->options & NAND_SKIP_BBTSCAN) + chip->options |= NAND_BBT_SCANNED; + + return 0; +} + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: number of chips to scan for + * + * This fills out all the uninitialized function pointers with the defaults. + * The flash ID is read and the mtd/chip structures are filled with the + * appropriate values. The mtd->owner field must be set to the module of the + * caller. + */ +int nand_scan(struct mtd_info *mtd, int maxchips) +{ + int ret; + + ret = nand_scan_ident(mtd, maxchips, NULL); + if (!ret) + ret = nand_scan_tail(mtd); + return ret; +} + +/** + * nand_release - [NAND Interface] Free resources held by the NAND device + * @mtd: MTD device structure + */ +void nand_release(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->ecc.mode == NAND_ECC_SOFT_BCH) + nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + +#ifdef CONFIG_MTD_PARTITIONS + /* Deregister partitions */ + del_mtd_partitions(mtd); +#endif + + /* Free bad block table memory */ + kfree(chip->bbt); + if (!(chip->options & NAND_OWN_BUFFERS)) + kfree(chip->buffers); + + /* Free bad block descriptor memory */ + if (chip->badblock_pattern && chip->badblock_pattern->options + & NAND_BBT_DYNAMICSTRUCT) + kfree(chip->badblock_pattern); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_bbt.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_bbt.c new file mode 100644 index 000000000..8ef58451d --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_bbt.c @@ -0,0 +1,1397 @@ +/* + * drivers/mtd/nand_bbt.c + * + * Overview: + * Bad block table support for the NAND driver + * + * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Description: + * + * When nand_scan_bbt is called, then it tries to find the bad block table + * depending on the options in the BBT descriptor(s). If no flash based BBT + * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory + * marked good / bad blocks. This information is used to create a memory BBT. + * Once a new bad block is discovered then the "factory" information is updated + * on the device. + * If a flash based BBT is specified then the function first tries to find the + * BBT on flash. If a BBT is found then the contents are read and the memory + * based BBT is created. If a mirrored BBT is selected then the mirror is + * searched too and the versions are compared. If the mirror has a greater + * version number, then the mirror BBT is used to build the memory based BBT. + * If the tables are not versioned, then we "or" the bad block information. + * If one of the BBTs is out of date or does not exist it is (re)created. + * If no BBT exists at all then the device is scanned for factory marked + * good / bad blocks and the bad block tables are created. + * + * For manufacturer created BBTs like the one found on M-SYS DOC devices + * the BBT is searched and read but never created + * + * The auto generated bad block table is located in the last good blocks + * of the device. The table is mirrored, so it can be updated eventually. + * The table is marked in the OOB area with an ident pattern and a version + * number which indicates which of both tables is more up to date. If the NAND + * controller needs the complete OOB area for the ECC information then the + * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of + * course): it moves the ident pattern and the version byte into the data area + * and the OOB area will remain untouched. + * + * The table uses 2 bits per block + * 11b: block is good + * 00b: block is factory marked bad + * 01b, 10b: block is marked bad due to wear + * + * The memory bad block table uses the following scheme: + * 00b: block is good + * 01b: block is marked bad due to wear + * 10b: block is reserved (to protect the bbt area) + * 11b: block is factory marked bad + * + * Multichip devices like DOC store the bad block info per floor. + * + * Following assumptions are made: + * - bbts start at a page boundary, if autolocated on a block boundary + * - the space necessary for a bbt in FLASH does not exceed a block boundary + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) +{ + if (memcmp(buf, td->pattern, td->len)) + return -1; + return 0; +} + +/** + * check_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block tables and + * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if + * all bytes except the pattern area contain 0xff. + */ +static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + int end = 0; + uint8_t *p = buf; + + if (td->options & NAND_BBT_NO_OOB) + return check_pattern_no_oob(buf, td); + + end = paglen + td->offs; + if (td->options & NAND_BBT_SCANEMPTY) + if (memchr_inv(p, 0xff, end)) + return -1; + p += end; + + /* Compare the pattern */ + if (memcmp(p, td->pattern, td->len)) + return -1; + + if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += td->len; + if (memchr_inv(p, 0xff, len - end)) + return -1; + } + return 0; +} + +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block tables and + * good / bad block identifiers. Same as check_pattern, but no optional empty + * check. + */ +static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) +{ + /* Compare the pattern */ + if (memcmp(buf + td->offs, td->pattern, td->len)) + return -1; + return 0; +} + +/** + * add_marker_len - compute the length of the marker in data area + * @td: BBT descriptor used for computation + * + * The length will be 0 if the marker is located in OOB area. + */ +static u32 add_marker_len(struct nand_bbt_descr *td) +{ + u32 len; + + if (!(td->options & NAND_BBT_NO_OOB)) + return 0; + + len = td->len; + if (td->options & NAND_BBT_VERSION) + len++; + return len; +} + +/** + * read_bbt - [GENERIC] Read the bad block table starting from page + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @td: the bbt describtion table + * @offs: offset in the memory table + * + * Read the bad block table starting from page. + */ +static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, + struct nand_bbt_descr *td, int offs) +{ + int res, ret = 0, i, j, act = 0; + struct nand_chip *this = mtd->priv; + size_t retlen, len, totlen; + loff_t from; + int bits = td->options & NAND_BBT_NRBITS_MSK; + uint8_t msk = (uint8_t)((1 << bits) - 1); + u32 marker_len; + int reserved_block_code = td->reserved_block_code; + + totlen = (num * bits) >> 3; + marker_len = add_marker_len(td); + from = ((loff_t)page) << this->page_shift; + + while (totlen) { + len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); + if (marker_len) { + /* + * In case the BBT marker is not in the OOB area it + * will be just in the first page. + */ + len -= marker_len; + from += marker_len; + marker_len = 0; + } + res = mtd_read(mtd, from, len, &retlen, buf); + if (res < 0) { + if (mtd_is_eccerr(res)) { + pr_info("nand_bbt: ECC error in BBT at " + "0x%012llx\n", from & ~mtd->writesize); + return res; + } else if (mtd_is_bitflip(res)) { + pr_info("nand_bbt: corrected error in BBT at " + "0x%012llx\n", from & ~mtd->writesize); + ret = res; + } else { + pr_info("nand_bbt: error reading BBT\n"); + return res; + } + } + + /* Analyse data */ + for (i = 0; i < len; i++) { + uint8_t dat = buf[i]; + for (j = 0; j < 8; j += bits, act += 2) { + uint8_t tmp = (dat >> j) & msk; + if (tmp == msk) + continue; + if (reserved_block_code && (tmp == reserved_block_code)) { + pr_info("nand_read_bbt: reserved block at 0x%012llx\n", + (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + mtd->ecc_stats.bbtblocks++; + continue; + } + pr_info("nand_read_bbt: Bad block at 0x%012llx\n", + (loff_t)((offs << 2) + (act >> 1)) + << this->bbt_erase_shift); + /* Factory marked bad or worn out? */ + if (tmp == 0) + this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + else + this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + mtd->ecc_stats.badblocks++; + } + } + totlen -= len; + from += len; + } + return ret; +} + +/** + * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips; applies only if + * NAND_BBT_PERCHIP option is set + * + * Read the bad block table for all chips starting at a given page. We assume + * that the bbt bits are in consecutive order. + */ +static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +{ + struct nand_chip *this = mtd->priv; + int res = 0, i; + + if (td->options & NAND_BBT_PERCHIP) { + int offs = 0; + for (i = 0; i < this->numchips; i++) { + if (chip == -1 || chip == i) + res = read_bbt(mtd, buf, td->pages[i], + this->chipsize >> this->bbt_erase_shift, + td, offs); + if (res) + return res; + offs += this->chipsize >> (this->bbt_erase_shift + 2); + } + } else { + res = read_bbt(mtd, buf, td->pages[0], + mtd->size >> this->bbt_erase_shift, td, 0); + if (res) + return res; + } + return 0; +} + +/* BBT marker is in the first page, no OOB */ +static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + struct nand_bbt_descr *td) +{ + size_t retlen; + size_t len; + + len = td->len; + if (td->options & NAND_BBT_VERSION) + len++; + + return mtd_read(mtd, offs, len, &retlen, buf); +} + +/** + * scan_read_oob - [GENERIC] Scan data+OOB region to buffer + * @mtd: MTD device structure + * @buf: temporary buffer + * @offs: offset at which to scan + * @len: length of data region to read + * + * Scan read data from data+OOB. May traverse multiple pages, interleaving + * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" + * ECC condition (error or bitflip). May quit on the first (non-ECC) error. + */ +static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len) +{ + struct mtd_oob_ops ops; + int res, ret = 0; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + + while (len > 0) { + ops.datbuf = buf; + ops.len = min(len, (size_t)mtd->writesize); + ops.oobbuf = buf + ops.len; + + res = mtd_read_oob(mtd, offs, &ops); + if (res) { + if (!mtd_is_bitflip_or_eccerr(res)) + return res; + else if (mtd_is_eccerr(res) || !ret) + ret = res; + } + + buf += mtd->oobsize + mtd->writesize; + len -= mtd->writesize; + offs += mtd->writesize; + } + return ret; +} + +static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len, struct nand_bbt_descr *td) +{ + if (td->options & NAND_BBT_NO_OOB) + return scan_read_data(mtd, buf, offs, td); + else + return scan_read_oob(mtd, buf, offs, len); +} + +/* Scan write data with oob to flash */ +static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, + uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + + return mtd_write_oob(mtd, offs, &ops); +} + +static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + u32 ver_offs = td->veroffs; + + if (!(td->options & NAND_BBT_NO_OOB)) + ver_offs += mtd->writesize; + return ver_offs; +} + +/** + * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Read the bad block table(s) for all chips starting at a given page. We + * assume that the bbt bits are in consecutive order. + */ +static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + struct nand_chip *this = mtd->priv; + + /* Read the primary version, if available */ + if (td->options & NAND_BBT_VERSION) { + scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift, + mtd->writesize, td); + td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; + pr_info("Bad block table at page %d, version 0x%02X\n", + td->pages[0], td->version[0]); + } + + /* Read the mirror version, if available */ + if (md && (md->options & NAND_BBT_VERSION)) { + scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift, + mtd->writesize, md); + md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; + pr_info("Bad block table at page %d, version 0x%02X\n", + md->pages[0], md->version[0]); + } +} + +/* Scan a given block full */ +static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, size_t readlen, + int scanlen, int numpages) +{ + int ret, j; + + ret = scan_read_oob(mtd, buf, offs, readlen); + /* Ignore ECC errors when checking for BBM */ + if (ret && !mtd_is_bitflip_or_eccerr(ret)) + return ret; + + for (j = 0; j < numpages; j++, buf += scanlen) { + if (check_pattern(buf, scanlen, mtd->writesize, bd)) + return 1; + } + return 0; +} + +/* Scan a given block partially */ +static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, int numpages) +{ + struct mtd_oob_ops ops; + int j, ret; + + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + for (j = 0; j < numpages; j++) { + /* + * Read the full oob until read_oob is fixed to handle single + * byte reads for 16 bit buswidth. + */ + ret = mtd_read_oob(mtd, offs, &ops); + /* Ignore ECC errors when checking for BBM */ + if (ret && !mtd_is_bitflip_or_eccerr(ret)) + return ret; + + if (check_short_pattern(buf, bd)) + return 1; + + offs += mtd->writesize; + } + return 0; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @chip: create the table for a specific chip, -1 read all chips; applies only + * if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device for the given good/bad block + * identify pattern. + */ +static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *bd, int chip) +{ + struct nand_chip *this = mtd->priv; + int i, numblocks, numpages, scanlen; + int startblock; + loff_t from; + size_t readlen; + + pr_info("Scanning device for bad blocks\n"); + + if (bd->options & NAND_BBT_SCANALLPAGES) + numpages = 1 << (this->bbt_erase_shift - this->page_shift); + else if (bd->options & NAND_BBT_SCAN2NDPAGE) + numpages = 2; + else + numpages = 1; + + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + /* We need only read few bytes from the OOB area */ + scanlen = 0; + readlen = bd->len; + } else { + /* Full page content should be read */ + scanlen = mtd->writesize + mtd->oobsize; + readlen = numpages * mtd->writesize; + } + + if (chip == -1) { + /* + * Note that numblocks is 2 * (real numblocks) here, see i+=2 + * below as it makes shifting and masking less painful + */ + numblocks = mtd->size >> (this->bbt_erase_shift - 1); + startblock = 0; + from = 0; + } else { + if (chip >= this->numchips) { + pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", + chip + 1, this->numchips); + return -EINVAL; + } + numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + startblock = chip * numblocks; + numblocks += startblock; + from = (loff_t)startblock << (this->bbt_erase_shift - 1); + } + + if (this->bbt_options & NAND_BBT_SCANLASTPAGE) + from += mtd->erasesize - (mtd->writesize * numpages); + + for (i = startblock; i < numblocks;) { + int ret; + + BUG_ON(bd->options & NAND_BBT_NO_OOB); + + if (bd->options & NAND_BBT_SCANALLPAGES) + ret = scan_block_full(mtd, bd, from, buf, readlen, + scanlen, numpages); + else + ret = scan_block_fast(mtd, bd, from, buf, numpages); + + if (ret < 0) + return ret; + + if (ret) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + pr_warn("Bad eraseblock %d at 0x%012llx\n", + i >> 1, (unsigned long long)from); + mtd->ecc_stats.badblocks++; + } + + i += 2; + from += (1 << this->bbt_erase_shift); + } + return 0; +} + +/** + * search_bbt - [GENERIC] scan the device for a specific bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * + * Read the bad block table by searching for a given ident pattern. Search is + * preformed either from the beginning up or from the end of the device + * downwards. The search starts always at the start of a block. If the option + * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains + * the bad block information of this chip. This is necessary to provide support + * for certain DOC devices. + * + * The bbt ident pattern resides in the oob area of the first page in a block. + */ +static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, chips; + int startblock, block, dir; + int scanlen = mtd->writesize + mtd->oobsize; + int bbtblocks; + int blocktopage = this->bbt_erase_shift - this->page_shift; + + /* Search direction top -> down? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = (mtd->size >> this->bbt_erase_shift) - 1; + dir = -1; + } else { + startblock = 0; + dir = 1; + } + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + bbtblocks = this->chipsize >> this->bbt_erase_shift; + startblock &= bbtblocks - 1; + } else { + chips = 1; + bbtblocks = mtd->size >> this->bbt_erase_shift; + } + + for (i = 0; i < chips; i++) { + /* Reset version information */ + td->version[i] = 0; + td->pages[i] = -1; + /* Scan the maximum number of blocks */ + for (block = 0; block < td->maxblocks; block++) { + + int actblock = startblock + dir * block; + loff_t offs = (loff_t)actblock << this->bbt_erase_shift; + + /* Read first page */ + scan_read(mtd, buf, offs, mtd->writesize, td); + if (!check_pattern(buf, scanlen, mtd->writesize, td)) { + td->pages[i] = actblock << blocktopage; + if (td->options & NAND_BBT_VERSION) { + offs = bbt_get_ver_offs(mtd, td); + td->version[i] = buf[offs]; + } + break; + } + } + startblock += this->chipsize >> this->bbt_erase_shift; + } + /* Check, if we found a bbt for each requested chip */ + for (i = 0; i < chips; i++) { + if (td->pages[i] == -1) + pr_warn("Bad block table not found for chip %d\n", i); + else + pr_info("Bad block table found at page %d, version 0x%02X\n", td->pages[i], + td->version[i]); + } + return 0; +} + +/** + * search_read_bbts - [GENERIC] scan the device for bad block table(s) + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Search and read the bad block table(s). + */ +static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, + struct nand_bbt_descr *md) +{ + /* Search the primary table */ + search_bbt(mtd, buf, td); + + /* Search the mirror table */ + if (md) + search_bbt(mtd, buf, md); +} + +/** + * write_bbt - [GENERIC] (Re)write the bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all + * + * (Re)write the bad block table. + */ +static int write_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, + int chipsel) +{ + struct nand_chip *this = mtd->priv; + struct erase_info einfo; + int i, j, res, chip = 0; + int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; + int nrchips, bbtoffs, pageoffs, ooboffs; + uint8_t msk[4]; + uint8_t rcode = td->reserved_block_code; + size_t retlen, len = 0; + loff_t to; + struct mtd_oob_ops ops; + + ops.ooblen = mtd->oobsize; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + if (!rcode) + rcode = 0xff; + /* Write bad block table per chip rather than per device? */ + if (td->options & NAND_BBT_PERCHIP) { + numblocks = (int)(this->chipsize >> this->bbt_erase_shift); + /* Full device write or specific chip? */ + if (chipsel == -1) { + nrchips = this->numchips; + } else { + nrchips = chipsel + 1; + chip = chipsel; + } + } else { + numblocks = (int)(mtd->size >> this->bbt_erase_shift); + nrchips = 1; + } + + /* Loop through the chips */ + for (; chip < nrchips; chip++) { + /* + * There was already a version of the table, reuse the page + * This applies for absolute placement too, as we have the + * page nr. in td->pages. + */ + if (td->pages[chip] != -1) { + page = td->pages[chip]; + goto write; + } + + /* + * Automatic placement of the bad block table. Search direction + * top -> down? + */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + + for (i = 0; i < td->maxblocks; i++) { + int block = startblock + dir * i; + /* Check, if the block is bad */ + switch ((this->bbt[block >> 2] >> + (2 * (block & 0x03))) & 0x03) { + case 0x01: + case 0x03: + continue; + } + page = block << + (this->bbt_erase_shift - this->page_shift); + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + goto write; + } + pr_err("No space left to write bad block table\n"); + return -ENOSPC; + write: + + /* Set up shift count and masks for the flash table */ + bits = td->options & NAND_BBT_NRBITS_MSK; + msk[2] = ~rcode; + switch (bits) { + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x01; + break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x03; + break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; + msk[3] = 0x0f; + break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; + msk[3] = 0xff; + break; + default: return -EINVAL; + } + + bbtoffs = chip * (numblocks >> 2); + + to = ((loff_t)page) << this->page_shift; + + /* Must we save the block contents? */ + if (td->options & NAND_BBT_SAVECONTENT) { + /* Make it block aligned */ + to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1)); + len = 1 << this->bbt_erase_shift; + res = mtd_read(mtd, to, len, &retlen, buf); + if (res < 0) { + if (retlen != len) { + pr_info("nand_bbt: error reading block " + "for writing the bad block table\n"); + return res; + } + pr_warn("nand_bbt: ECC error while reading " + "block for writing bad block table\n"); + } + /* Read oob data */ + ops.ooblen = (len >> this->page_shift) * mtd->oobsize; + ops.oobbuf = &buf[len]; + res = mtd_read_oob(mtd, to + mtd->writesize, &ops); + if (res < 0 || ops.oobretlen != ops.ooblen) + goto outerr; + + /* Calc the byte offset in the buffer */ + pageoffs = page - (int)(to >> this->page_shift); + offs = pageoffs << this->page_shift; + /* Preset the bbt area with 0xff */ + memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); + ooboffs = len + (pageoffs * mtd->oobsize); + + } else if (td->options & NAND_BBT_NO_OOB) { + ooboffs = 0; + offs = td->len; + /* The version byte */ + if (td->options & NAND_BBT_VERSION) + offs++; + /* Calc length */ + len = (size_t)(numblocks >> sft); + len += offs; + /* Make it page aligned! */ + len = ALIGN(len, mtd->writesize); + /* Preset the buffer with 0xff */ + memset(buf, 0xff, len); + /* Pattern is located at the begin of first page */ + memcpy(buf, td->pattern, td->len); + } else { + /* Calc length */ + len = (size_t)(numblocks >> sft); + /* Make it page aligned! */ + len = ALIGN(len, mtd->writesize); + /* Preset the buffer with 0xff */ + memset(buf, 0xff, len + + (len >> this->page_shift)* mtd->oobsize); + offs = 0; + ooboffs = len; + /* Pattern is located in oob area of first page */ + memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); + } + + if (td->options & NAND_BBT_VERSION) + buf[ooboffs + td->veroffs] = td->version[chip]; + + /* Walk through the memory table */ + for (i = 0; i < numblocks;) { + uint8_t dat; + dat = this->bbt[bbtoffs + (i >> 2)]; + for (j = 0; j < 4; j++, i++) { + int sftcnt = (i << (3 - sft)) & sftmsk; + /* Do not store the reserved bbt blocks! */ + buf[offs + (i >> sft)] &= + ~(msk[dat & 0x03] << sftcnt); + dat >>= 2; + } + } + + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = to; + einfo.len = 1 << this->bbt_erase_shift; + res = nand_erase_nand(mtd, &einfo, 1); + if (res < 0) + goto outerr; + + res = scan_write_bbt(mtd, to, len, buf, + td->options & NAND_BBT_NO_OOB ? NULL : + &buf[len]); + if (res < 0) + goto outerr; + + pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", + (unsigned long long)to, td->version[chip]); + + /* Mark it as used */ + td->pages[chip] = page; + } + return 0; + + outerr: + pr_warn("nand_bbt: error while writing bad block table %d\n", res); + return res; +} + +/** + * nand_memory_bbt - [GENERIC] create a memory based bad block table + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device for + * manufacturer / software marked good / bad blocks. + */ +static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt(mtd, this->buffers->databuf, bd, -1); +} + +/** + * check_create - [GENERIC] create and write bbt(s) if necessary + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * + * The function checks the results of the previous call to read_bbt and creates + * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found + * for the chip/device. Update is necessary if one of the tables is missing or + * the version nr. of one table is less than the other. + */ +static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +{ + int i, chips, writeops, create, chipsel, res, res2; + struct nand_chip *this = mtd->priv; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + struct nand_bbt_descr *rd, *rd2; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) + chips = this->numchips; + else + chips = 1; + + for (i = 0; i < chips; i++) { + writeops = 0; + create = 0; + rd = NULL; + rd2 = NULL; + res = res2 = 0; + /* Per chip or per device? */ + chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; + /* Mirrored table available? */ + if (md) { + if (td->pages[i] == -1 && md->pages[i] == -1) { + create = 1; + writeops = 0x03; + } else if (td->pages[i] == -1) { + rd = md; + writeops = 0x01; + } else if (md->pages[i] == -1) { + rd = td; + writeops = 0x02; + } else if (td->version[i] == md->version[i]) { + rd = td; + if (!(td->options & NAND_BBT_VERSION)) + rd2 = md; + } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { + rd = td; + writeops = 0x02; + } else { + rd = md; + writeops = 0x01; + } + } else { + if (td->pages[i] == -1) { + create = 1; + writeops = 0x01; + } else { + rd = td; + } + } + + if (create) { + /* Create the bad block table by scanning the device? */ + if (!(td->options & NAND_BBT_CREATE)) + continue; + + /* Create the table in memory by scanning the chip(s) */ + if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) + create_bbt(mtd, buf, bd, chipsel); + + td->version[i] = 1; + if (md) + md->version[i] = 1; + } + + /* Read back first? */ + if (rd) { + res = read_abs_bbt(mtd, buf, rd, chipsel); + if (mtd_is_eccerr(res)) { + /* Mark table as invalid */ + rd->pages[i] = -1; + rd->version[i] = 0; + i--; + continue; + } + } + /* If they weren't versioned, read both */ + if (rd2) { + res2 = read_abs_bbt(mtd, buf, rd2, chipsel); + if (mtd_is_eccerr(res2)) { + /* Mark table as invalid */ + rd2->pages[i] = -1; + rd2->version[i] = 0; + i--; + continue; + } + } + + /* Scrub the flash table(s)? */ + if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) + writeops = 0x03; + + /* Update version numbers before writing */ + if (md) { + td->version[i] = max(td->version[i], md->version[i]); + md->version[i] = td->version[i]; + } + + /* Write the bad block table to the device? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + return res; + } + + /* Write the mirror bad block table to the device? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, md, td, chipsel); + if (res < 0) + return res; + } + } + return 0; +} + +/** + * mark_bbt_regions - [GENERIC] mark the bad block table regions + * @mtd: MTD device structure + * @td: bad block table descriptor + * + * The bad block table regions are marked as "bad" to prevent accidental + * erasures / writes. The regions are identified by the mark 0x02. + */ +static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, j, chips, block, nrblocks, update; + uint8_t oldval, newval; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); + } else { + chips = 1; + nrblocks = (int)(mtd->size >> this->bbt_erase_shift); + } + + for (i = 0; i < chips; i++) { + if ((td->options & NAND_BBT_ABSPAGE) || + !(td->options & NAND_BBT_WRITE)) { + if (td->pages[i] == -1) + continue; + block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); + block <<= 1; + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if ((oldval != newval) && td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); + continue; + } + update = 0; + if (td->options & NAND_BBT_LASTBLOCK) + block = ((i + 1) * nrblocks) - td->maxblocks; + else + block = i * nrblocks; + block <<= 1; + for (j = 0; j < td->maxblocks; j++) { + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if (oldval != newval) + update = 1; + block += 2; + } + /* + * If we want reserved blocks to be recorded to flash, and some + * new ones have been marked, then we need to update the stored + * bbts. This should only happen once. + */ + if (update && td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); + } +} + +/** + * verify_bbt_descr - verify the bad block description + * @mtd: MTD device structure + * @bd: the table to verify + * + * This functions performs a few sanity checks on the bad block description + * table. + */ +static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + u32 pattern_len; + u32 bits; + u32 table_size; + + if (!bd) + return; + + pattern_len = bd->len; + bits = bd->options & NAND_BBT_NRBITS_MSK; + + BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && + !(this->bbt_options & NAND_BBT_USE_FLASH)); + BUG_ON(!bits); + + if (bd->options & NAND_BBT_VERSION) + pattern_len++; + + if (bd->options & NAND_BBT_NO_OOB) { + BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); + BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); + BUG_ON(bd->offs); + if (bd->options & NAND_BBT_VERSION) + BUG_ON(bd->veroffs != bd->len); + BUG_ON(bd->options & NAND_BBT_SAVECONTENT); + } + + if (bd->options & NAND_BBT_PERCHIP) + table_size = this->chipsize >> this->bbt_erase_shift; + else + table_size = mtd->size >> this->bbt_erase_shift; + table_size >>= 3; + table_size *= bits; + if (bd->options & NAND_BBT_NO_OOB) + table_size += pattern_len; + BUG_ON(table_size > (1 << this->bbt_erase_shift)); +} + +/** + * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already available. If + * not it scans the device for manufacturer marked good / bad blocks and writes + * the bad block table(s) to the selected place. + * + * The bad block table memory is allocated here. It must be freed by calling + * the nand_free_bbt function. + */ +int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + len = mtd->size >> (this->bbt_erase_shift + 2); + /* + * Allocate memory (2bit per block) and clear the memory bad block + * table. + */ + this->bbt = kzalloc(len, GFP_KERNEL); + if (!this->bbt) + return -ENOMEM; + + /* + * If no primary table decriptor is given, scan the device to build a + * memory based bad block table. + */ + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); + kfree(this->bbt); + this->bbt = NULL; + } + return res; + } + verify_bbt_descr(mtd, td); + verify_bbt_descr(mtd, md); + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = vmalloc(len); + if (!buf) { + kfree(this->bbt); + this->bbt = NULL; + return -ENOMEM; + } + + /* Is the bbt at a given page? */ + if (td->options & NAND_BBT_ABSPAGE) { + read_abs_bbts(mtd, buf, td, md); + } else { + /* Search the bad block table using a pattern in oob */ + search_read_bbts(mtd, buf, td, md); + } + + res = check_create(mtd, buf, bd); + + /* Prevent the bbt regions from erasing / writing */ + mark_bbt_region(mtd, td); + if (md) + mark_bbt_region(mtd, md); + + vfree(buf); + return res; +} + +/** + * nand_update_bbt - [NAND Interface] update bad block table(s) + * @mtd: MTD device structure + * @offs: the offset of the newly marked block + * + * The function updates the bad block table(s). + */ +int nand_update_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0; + int chip, chipsel; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + if (!this->bbt || !td) + return -EINVAL; + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chip = (int)(offs >> this->chip_shift); + chipsel = chip; + } else { + chip = 0; + chipsel = -1; + } + + td->version[chip]++; + if (md) + md->version[chip]++; + + /* Write the bad block table to the device? */ + if (td->options & NAND_BBT_WRITE) { + res = write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + goto out; + } + /* Write the mirror bad block table to the device? */ + if (md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, md, td, chipsel); + } + + out: + kfree(buf); + return res; +} + +/* + * Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. + */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; + +static struct nand_bbt_descr agand_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0x20, + .len = 6, + .pattern = scan_agand_pattern +}; + +/* Generic flash bbt descriptors */ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = mirror_pattern +}; + +static struct nand_bbt_descr bbt_main_no_oob_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP + | NAND_BBT_NO_OOB, + .len = 4, + .veroffs = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_no_oob_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP + | NAND_BBT_NO_OOB, + .len = 4, + .veroffs = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = mirror_pattern +}; + +#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) +/** + * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure + * @this: NAND chip to create descriptor for + * + * This function allocates and initializes a nand_bbt_descr for BBM detection + * based on the properties of @this. The new descriptor is stored in + * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when + * passed to this function. + */ +static int nand_create_badblock_pattern(struct nand_chip *this) +{ + struct nand_bbt_descr *bd; + if (this->badblock_pattern) { + pr_warn("Bad block pattern already allocated; not replacing\n"); + return -EINVAL; + } + bd = kzalloc(sizeof(*bd), GFP_KERNEL); + if (!bd) + return -ENOMEM; + bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; + bd->offs = this->badblockpos; + bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; + bd->pattern = scan_ff_pattern; + bd->options |= NAND_BBT_DYNAMICSTRUCT; + this->badblock_pattern = bd; + return 0; +} + +/** + * nand_default_bbt - [NAND Interface] Select a default bad block table for the device + * @mtd: MTD device structure + * + * This function selects the default bad block table support for the device and + * calls the nand_scan_bbt function. + */ +int nand_default_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* + * Default for AG-AND. We must use a flash based bad block table as the + * devices have factory marked _good_ blocks. Erasing those blocks + * leads to loss of the good / bad information, so we _must_ store this + * information in a good / bad table during startup. + */ + if (this->options & NAND_IS_AND) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + this->bbt_options |= NAND_BBT_USE_FLASH; + return nand_scan_bbt(mtd, &agand_flashbased); + } + + /* Is a flash based bad block table requested? */ + if (this->bbt_options & NAND_BBT_USE_FLASH) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + if (this->bbt_options & NAND_BBT_NO_OOB) { + this->bbt_td = &bbt_main_no_oob_descr; + this->bbt_md = &bbt_mirror_no_oob_descr; + } else { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + } + } else { + this->bbt_td = NULL; + this->bbt_md = NULL; + } + + if (!this->badblock_pattern) + nand_create_badblock_pattern(this); + + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +/** + * nand_isbad_bbt - [NAND Interface] Check if a block is bad + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + */ +int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct nand_chip *this = mtd->priv; + int block; + uint8_t res; + + /* Get block number * 2 */ + block = (int)(offs >> (this->bbt_erase_shift - 1)); + res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + + MTDDEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block >> 1, res); + + switch ((int)res) { + case 0x00: + return 0; + case 0x01: + return 1; + case 0x02: + return allowbbt ? 0 : 1; + } + return 1; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_bch.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_bch.c new file mode 100644 index 000000000..35d2140da --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_bch.c @@ -0,0 +1,224 @@ +/* + * This file provides ECC correction for more than 1 bit per block of data, + * using binary BCH codes. It relies on the generic BCH library lib/bch.c. + * + * Copyright © 2011 Ivan Djelic + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +/*#include */ +#include + +#include +#include +#include +#include +#include +#include + +/** + * struct nand_bch_control - private NAND BCH control structure + * @bch: BCH control structure + * @ecclayout: private ecc layout for this BCH configuration + * @errloc: error location array + * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid + */ +struct nand_bch_control { + struct bch_control *bch; + struct nand_ecclayout ecclayout; + unsigned int *errloc; + unsigned char *eccmask; +}; + +/** + * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int i; + + memset(code, 0, chip->ecc.bytes); + encode_bch(nbc->bch, buf, chip->ecc.size, code); + + /* apply mask so that an erased page is a valid codeword */ + for (i = 0; i < chip->ecc.bytes; i++) + code[i] ^= nbc->eccmask[i]; + + return 0; +} + +/** + * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct bit errors for a data byte block + */ +int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int *errloc = nbc->errloc; + int i, count; + + count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, + NULL, errloc); + if (count > 0) { + for (i = 0; i < count; i++) { + if (errloc[i] < (chip->ecc.size*8)) + /* error is located in data, correct it */ + buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); + /* else error in ecc, no action needed */ + + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: corrected bitflip %u\n", + __func__, errloc[i]); + } + } else if (count < 0) { + printk(KERN_ERR "ecc unrecoverable error\n"); + count = -1; + } + return count; +} + +/** + * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction + * @mtd: MTD block structure + * @eccsize: ecc block size in bytes + * @eccbytes: ecc length in bytes + * @ecclayout: output default layout + * + * Returns: + * a pointer to a new NAND BCH control structure, or NULL upon failure + * + * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes + * are used to compute BCH parameters m (Galois field order) and t (error + * correction capability). @eccbytes should be equal to the number of bytes + * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. + * + * Example: to configure 4 bit correction per 512 bytes, you should pass + * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) + * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) + */ +struct nand_bch_control * +nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, + struct nand_ecclayout **ecclayout) +{ + unsigned int m, t, eccsteps, i; + struct nand_ecclayout *layout; + struct nand_bch_control *nbc = NULL; + unsigned char *erased_page; + + if (!eccsize || !eccbytes) { + printk(KERN_WARNING "ecc parameters not supplied\n"); + goto fail; + } + + m = fls(1+8*eccsize); + t = (eccbytes*8)/m; + + nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); + if (!nbc) + goto fail; + + nbc->bch = init_bch(m, t, 0); + if (!nbc->bch) + goto fail; + + /* verify that eccbytes has the expected value */ + if (nbc->bch->ecc_bytes != eccbytes) { + printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", + eccbytes, nbc->bch->ecc_bytes); + goto fail; + } + + eccsteps = mtd->writesize/eccsize; + + /* if no ecc placement scheme was provided, build one */ + if (!*ecclayout) { + + /* handle large page devices only */ + if (mtd->oobsize < 64) { + printk(KERN_WARNING "must provide an oob scheme for " + "oobsize %d\n", mtd->oobsize); + goto fail; + } + + layout = &nbc->ecclayout; + layout->eccbytes = eccsteps*eccbytes; + + /* reserve 2 bytes for bad block marker */ + if (layout->eccbytes+2 > mtd->oobsize) { + printk(KERN_WARNING "no suitable oob scheme available " + "for oobsize %d eccbytes %u\n", mtd->oobsize, + eccbytes); + goto fail; + } + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; + + *ecclayout = layout; + } + + /* sanity checks */ + if (8*(eccsize+eccbytes) >= (1 << m)) { + printk(KERN_WARNING "eccsize %u is too large\n", eccsize); + goto fail; + } + if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) { + printk(KERN_WARNING "invalid ecc layout\n"); + goto fail; + } + + nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); + nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); + if (!nbc->eccmask || !nbc->errloc) + goto fail; + /* + * compute and store the inverted ecc of an erased ecc block + */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) + goto fail; + + memset(erased_page, 0xff, eccsize); + memset(nbc->eccmask, 0, eccbytes); + encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + nbc->eccmask[i] ^= 0xff; + + return nbc; +fail: + nand_bch_free(nbc); + return NULL; +} + +/** + * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources + * @nbc: NAND BCH control structure + */ +void nand_bch_free(struct nand_bch_control *nbc) +{ + if (nbc) { + free_bch(nbc->bch); + kfree(nbc->errloc); + kfree(nbc->eccmask); + kfree(nbc); + } +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_ecc.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_ecc.c new file mode 100644 index 000000000..083e0e99e --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_ecc.c @@ -0,0 +1,191 @@ +/* + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. + * + * drivers/mtd/nand/nand_ecc.c + * + * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. + * + * Copyright (C) 2006 Thomas Gleixner + * + * SPDX-License-Identifier: GPL-2.0+ + * + * As a special exception, if other files instantiate templates or use + * macros or inline functions from these files, or you compile these + * files and link them with other works to produce a work based on these + * files, these files do not by themselves cause the resulting work to be + * covered by the GNU General Public License. However the source code for + * these files must still be made available in accordance with section (3) + * of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + */ + +#include + +#include +#include +#include + +/* The PPC4xx NDFC uses Smart Media (SMC) bytes order */ +#ifdef CONFIG_NAND_NDFC +#define CONFIG_MTD_NAND_ECC_SMC +#endif + +/* + * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(), + * only nand_correct_data() is needed + */ + +#if !defined(CONFIG_NAND_SPL) || defined(CONFIG_SPL_NAND_SOFTECC) +/* + * Pre-calculated 256-way 1 byte column parity + */ +static const u_char nand_ecc_precalc_table[] = { + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 +}; + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block + * @mtd: MTD block structure + * @dat: raw data + * @ecc_code: buffer for ECC + */ +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; + int i; + + /* Initialize variables */ + reg1 = reg2 = reg3 = 0; + + /* Build up column parity */ + for(i = 0; i < 256; i++) { + /* Get CP0 - CP5 from table */ + idx = nand_ecc_precalc_table[*dat++]; + reg1 ^= (idx & 0x3f); + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (uint8_t) i; + reg2 ^= ~((uint8_t) i); + } + } + + /* Create non-inverted ECC code from line parity */ + tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ + tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ + tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ + tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ + tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ + tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ + tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ + tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ + + tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ + tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ + tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ + tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ + tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ + tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ + tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ + tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ + + /* Calculate final ECC code */ +#ifdef CONFIG_MTD_NAND_ECC_SMC + ecc_code[0] = ~tmp2; + ecc_code[1] = ~tmp1; +#else + ecc_code[0] = ~tmp1; + ecc_code[1] = ~tmp2; +#endif + ecc_code[2] = ((~reg1) << 2) | 0x03; + + return 0; +} +#endif /* CONFIG_NAND_SPL */ + +static inline int countbits(uint32_t byte) +{ + int res = 0; + + for (;byte; byte >>= 1) + res += byte & 0x01; + return res; +} + +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @dat: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256 byte block + */ +int nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + uint8_t s0, s1, s2; + +#ifdef CONFIG_MTD_NAND_ECC_SMC + s0 = calc_ecc[0] ^ read_ecc[0]; + s1 = calc_ecc[1] ^ read_ecc[1]; + s2 = calc_ecc[2] ^ read_ecc[2]; +#else + s1 = calc_ecc[0] ^ read_ecc[0]; + s0 = calc_ecc[1] ^ read_ecc[1]; + s2 = calc_ecc[2] ^ read_ecc[2]; +#endif + if ((s0 | s1 | s2) == 0) + return 0; + + /* Check for a single bit error */ + if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && + ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && + ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { + + uint32_t byteoffs, bitnum; + + byteoffs = (s1 << 0) & 0x80; + byteoffs |= (s1 << 1) & 0x40; + byteoffs |= (s1 << 2) & 0x20; + byteoffs |= (s1 << 3) & 0x10; + + byteoffs |= (s0 >> 4) & 0x08; + byteoffs |= (s0 >> 3) & 0x04; + byteoffs |= (s0 >> 2) & 0x02; + byteoffs |= (s0 >> 1) & 0x01; + + bitnum = (s2 >> 5) & 0x04; + bitnum |= (s2 >> 4) & 0x02; + bitnum |= (s2 >> 3) & 0x01; + + dat[byteoffs] ^= (1 << bitnum); + + return 1; + } + + if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) + return 1; + + return -EBADMSG; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_ids.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_ids.c new file mode 100644 index 000000000..f3f0cb676 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_ids.c @@ -0,0 +1,182 @@ +/* + * drivers/mtd/nandids.c + * + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +/* +* Chip ID list +* +* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, +* options +* +* Pagesize; 0, 256, 512 +* 0 get this information from the extended chip ID ++ 256 256 Byte page size +* 512 512 Byte page size +*/ +const struct nand_flash_dev nand_flash_ids[] = { + +#ifdef CONFIG_MTD_NAND_MUSEUM_IDS + {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, + {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, + {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, + {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, + + {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, + {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, +#endif + + {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, + {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, + {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, + {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, + {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, + {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, + {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, + {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + + /* + * These are the new chips with large page size. The pagesize and the + * erasesize is determined from the extended id bytes + */ +#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS +#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) + + /* 512 Megabit */ + {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 3,3V 8-bit", 0xF0, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, + {"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16}, + {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, + {"NAND 64MiB 3,3V 16-bit", 0xC0, 0, 64, 0, LP_OPTIONS16}, + + /* 1 Gigabit */ + {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, + {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, + {"NAND 128MiB 1,8V 16-bit", 0xAD, 0, 128, 0, LP_OPTIONS16}, + + /* 2 Gigabit */ + {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, + {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS}, + {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16}, + {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16}, + + /* 4 Gigabit */ + {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS}, + {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS}, + {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16}, + {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16}, + + /* 8 Gigabit */ + {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS}, + {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS}, + {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16}, + {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16}, + + /* 16 Gigabit */ + {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS}, + {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}, + {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, + {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + + /* 32 Gigabit */ + {"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS}, + {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS}, + {"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16}, + {"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16}, + + /* 64 Gigabit */ + {"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS}, + {"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS}, + {"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16}, + {"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16}, + + /* 128 Gigabit */ + {"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS}, + {"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS}, + {"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16}, + {"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16}, + + /* 256 Gigabit */ + {"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS}, + {"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS}, + {"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16}, + {"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16}, + + /* 512 Gigabit */ + {"NAND 64GiB 1,8V 8-bit", 0x1E, 0, 65536, 0, LP_OPTIONS}, + {"NAND 64GiB 3,3V 8-bit", 0x3E, 0, 65536, 0, LP_OPTIONS}, + {"NAND 64GiB 1,8V 16-bit", 0x2E, 0, 65536, 0, LP_OPTIONS16}, + {"NAND 64GiB 3,3V 16-bit", 0x4E, 0, 65536, 0, LP_OPTIONS16}, + + /* + * Renesas AND 1 Gigabit. Those chips do not support extended id and + * have a strange page/block layout ! The chosen minimum erasesize is + * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page + * planes 1 block = 2 pages, but due to plane arrangement the blocks + * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would + * increase the eraseblock size so we chose a combined one which can be + * erased in one go There are more speed improvements for reads and + * writes possible, but not implemented now + */ + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, + NAND_IS_AND | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, + + {NULL,} +}; + +/* +* Manufacturer ID list +*/ +const struct nand_manufacturers nand_manuf_ids[] = { + {NAND_MFR_TOSHIBA, "Toshiba"}, + {NAND_MFR_SAMSUNG, "Samsung"}, + {NAND_MFR_FUJITSU, "Fujitsu"}, + {NAND_MFR_NATIONAL, "National"}, + {NAND_MFR_RENESAS, "Renesas"}, + {NAND_MFR_STMICRO, "ST Micro"}, + {NAND_MFR_HYNIX, "Hynix"}, + {NAND_MFR_MICRON, "Micron"}, + {NAND_MFR_AMD, "AMD/Spansion"}, + {NAND_MFR_MACRONIX, "Macronix"}, + {NAND_MFR_EON, "Eon"}, + {0x0, "Unknown"} +}; diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_plat.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_plat.c new file mode 100644 index 000000000..37a0206ad --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_plat.c @@ -0,0 +1,64 @@ +/* + * Genericish driver for memory mapped NAND devices + * + * Copyright (c) 2006-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +/* Your board must implement the following macros: + * NAND_PLAT_WRITE_CMD(chip, cmd) + * NAND_PLAT_WRITE_ADR(chip, cmd) + * NAND_PLAT_INIT() + * + * It may also implement the following: + * NAND_PLAT_DEV_READY(chip) + */ + +#include +#include +#ifdef NAND_PLAT_GPIO_DEV_READY +# include +# define NAND_PLAT_DEV_READY(chip) gpio_get_value(NAND_PLAT_GPIO_DEV_READY) +#endif + +#include + +static void plat_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + NAND_PLAT_WRITE_CMD(this, cmd); + else + NAND_PLAT_WRITE_ADR(this, cmd); +} + +#ifdef NAND_PLAT_DEV_READY +static int plat_dev_ready(struct mtd_info *mtd) +{ + return NAND_PLAT_DEV_READY((struct nand_chip *)mtd->priv); +} +#else +# define plat_dev_ready NULL +#endif + +int board_nand_init(struct nand_chip *nand) +{ +#ifdef NAND_PLAT_GPIO_DEV_READY + gpio_request(NAND_PLAT_GPIO_DEV_READY, "nand-plat"); + gpio_direction_input(NAND_PLAT_GPIO_DEV_READY); +#endif + +#ifdef NAND_PLAT_INIT + NAND_PLAT_INIT(); +#endif + + nand->cmd_ctrl = plat_cmd_ctrl; + nand->dev_ready = plat_dev_ready; + nand->ecc.mode = NAND_ECC_SOFT; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_spl_load.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_spl_load.c new file mode 100644 index 000000000..5a2564464 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_spl_load.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + + /* + * Load U-Boot image from NAND into RAM + */ + nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_U_BOOT_SIZE, + (void *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST + nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND + nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif + + /* + * Jump to U-Boot image + */ + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + (*uboot)(); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c new file mode 100644 index 000000000..cead4b506 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c @@ -0,0 +1,270 @@ +/* + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +static nand_info_t mtd; +static struct nand_chip nand_chip; + +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + + +#if (CONFIG_SYS_NAND_PAGE_SIZE <= 512) +/* + * NAND command for small page NAND devices (512) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = mtd.priv; + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + + while (!this->dev_ready(&mtd)) + ; + + /* Begin command latch cycle */ + this->cmd_ctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + this->cmd_ctrl(&mtd, offs, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + this->cmd_ctrl(&mtd, page_addr & 0xff, NAND_CTRL_ALE); /* A[16:9] */ + this->cmd_ctrl(&mtd, (page_addr >> 8) & 0xff, + NAND_CTRL_ALE); /* A[24:17] */ +#ifdef CONFIG_SYS_NAND_4_ADDR_CYCLE + /* One more address cycle for devices > 32MiB */ + this->cmd_ctrl(&mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[28:25] */ +#endif + /* Latch in address */ + this->cmd_ctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + while (!this->dev_ready(&mtd)) + ; + + return 0; +} +#else +/* + * NAND command for large page NAND devices (2k) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = mtd.priv; + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + + while (!this->dev_ready(&mtd)) + ; + + /* Emulate NAND_CMD_READOOB */ + if (cmd == NAND_CMD_READOOB) { + offs += CONFIG_SYS_NAND_PAGE_SIZE; + cmd = NAND_CMD_READ0; + } + + /* Shift the offset from byte addressing to word addressing. */ + if (this->options & NAND_BUSWIDTH_16) + offs >>= 1; + + /* Begin command latch cycle */ + hwctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + hwctrl(&mtd, offs & 0xff, + NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ + hwctrl(&mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ + /* Row address */ + hwctrl(&mtd, (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */ + hwctrl(&mtd, ((page_addr >> 8) & 0xff), + NAND_CTRL_ALE); /* A[27:20] */ +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + /* One more address cycle for devices > 128MiB */ + hwctrl(&mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[31:28] */ +#endif + /* Latch in address */ + hwctrl(&mtd, NAND_CMD_READSTART, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + while (!this->dev_ready(&mtd)) + ; + + return 0; +} +#endif + +static int nand_is_bad_block(int block) +{ + struct nand_chip *this = mtd.priv; + + nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, + NAND_CMD_READOOB); + + /* + * Read one byte (or two if it's a 16 bit chip). + */ + if (this->options & NAND_BUSWIDTH_16) { + if (readw(this->IO_ADDR_R) != 0xffff) + return 1; + } else { + if (readb(this->IO_ADDR_R) != 0xff) + return 1; + } + + return 0; +} + +#if defined(CONFIG_SYS_NAND_HW_ECC_OOBFIRST) +static int nand_read_page(int block, int page, uchar *dst) +{ + struct nand_chip *this = mtd.priv; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + + nand_command(block, page, 0, NAND_CMD_READOOB); + this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + nand_command(block, page, 0, NAND_CMD_READ0); + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + this->ecc.hwctl(&mtd, NAND_ECC_READ); + this->read_buf(&mtd, p, eccsize); + this->ecc.calculate(&mtd, p, &ecc_calc[i]); + this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} +#else +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd.priv; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + + nand_command(block, page, 0, NAND_CMD_READ0); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + if (this->ecc.mode != NAND_ECC_SOFT) + this->ecc.hwctl(&mtd, NAND_ECC_READ); + this->read_buf(&mtd, p, eccsize); + this->ecc.calculate(&mtd, p, &ecc_calc[i]); + } + this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + eccsteps = ECCSTEPS; + p = dst; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + /* No chance to do something with the possible error message + * from correct_data(). We just hope that all possible errors + * are corrected by this routine. + */ + this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} +#endif + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + unsigned int block, lastblock; + unsigned int page; + + /* + * offs has to be aligned to a page address! + */ + block = offs / CONFIG_SYS_NAND_BLOCK_SIZE; + lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE; + page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE; + + while (block <= lastblock) { + if (!nand_is_bad_block(block)) { + /* + * Skip bad blocks + */ + while (page < CONFIG_SYS_NAND_PAGE_COUNT) { + nand_read_page(block, page, dst); + dst += CONFIG_SYS_NAND_PAGE_SIZE; + page++; + } + + page = 0; + } else { + lastblock++; + } + + block++; + } + + return 0; +} + +/* nand_init() - initialize data to make nand usable by SPL */ +void nand_init(void) +{ + /* + * Init board specific nand support + */ + mtd.priv = &nand_chip; + nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + board_nand_init(&nand_chip); + +#ifdef CONFIG_SPL_NAND_SOFTECC + if (nand_chip.ecc.mode == NAND_ECC_SOFT) { + nand_chip.ecc.calculate = nand_calculate_ecc; + nand_chip.ecc.correct = nand_correct_data; + } +#endif + + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, 0); +} + +/* Unselect after operation */ +void nand_deselect(void) +{ + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, -1); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_util.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_util.c new file mode 100644 index 000000000..b29282603 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_util.c @@ -0,0 +1,861 @@ +/* + * drivers/mtd/nand/nand_util.c + * + * Copyright (C) 2006 by Weiss-Electronic GmbH. + * All rights reserved. + * + * @author: Guido Classen + * @descr: NAND Flash support + * @references: borrowed heavily from Linux mtd-utils code: + * flash_eraseall.c by Arcom Control System Ltd + * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) + * and Thomas Gleixner (tglx@linutronix.de) + * + * Copyright (C) 2008 Nokia Corporation: drop_ffs() function by + * Artem Bityutskiy from mtd-utils + * + * Copyright 2010 Freescale Semiconductor + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef struct erase_info erase_info_t; +typedef struct mtd_info mtd_info_t; + +/* support only for native endian JFFS2 */ +#define cpu_to_je16(x) (x) +#define cpu_to_je32(x) (x) + +/** + * nand_erase_opts: - erase NAND flash with support for various options + * (jffs2 formatting) + * + * @param meminfo NAND device to erase + * @param opts options, @see struct nand_erase_options + * @return 0 in case of success + * + * This code is ported from flash_eraseall.c from Linux mtd utils by + * Arcom Control System Ltd. + */ +int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) +{ + struct jffs2_unknown_node cleanmarker; + erase_info_t erase; + unsigned long erase_length, erased_length; /* in blocks */ + int result; + int percent_complete = -1; + const char *mtd_device = meminfo->name; + struct mtd_oob_ops oob_opts; + struct nand_chip *chip = meminfo->priv; + + if ((opts->offset & (meminfo->erasesize - 1)) != 0) { + printf("Attempt to erase non block-aligned data\n"); + return -1; + } + + memset(&erase, 0, sizeof(erase)); + memset(&oob_opts, 0, sizeof(oob_opts)); + + erase.mtd = meminfo; + erase.len = meminfo->erasesize; + erase.addr = opts->offset; + erase_length = lldiv(opts->length + meminfo->erasesize - 1, + meminfo->erasesize); + + cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); + cleanmarker.totlen = cpu_to_je32(8); + + /* scrub option allows to erase badblock. To prevent internal + * check from erase() method, set block check method to dummy + * and disable bad block table while erasing. + */ + if (opts->scrub) { + erase.scrub = opts->scrub; + /* + * We don't need the bad block table anymore... + * after scrub, there are no bad blocks left! + */ + if (chip->bbt) { + kfree(chip->bbt); + } + chip->bbt = NULL; + } + + for (erased_length = 0; + erased_length < erase_length; + erase.addr += meminfo->erasesize) { + + WATCHDOG_RESET(); + + if (opts->lim && (erase.addr >= (opts->offset + opts->lim))) { + puts("Size of erase exceeds limit\n"); + return -EFBIG; + } + if (!opts->scrub) { + int ret = mtd_block_isbad(meminfo, erase.addr); + if (ret > 0) { + if (!opts->quiet) + printf("\rSkipping bad block at " + "0x%08llx " + " \n", + erase.addr); + + if (!opts->spread) + erased_length++; + + continue; + + } else if (ret < 0) { + printf("\n%s: MTD get bad block failed: %d\n", + mtd_device, + ret); + return -1; + } + } + + erased_length++; + + result = mtd_erase(meminfo, &erase); + if (result != 0) { + printf("\n%s: MTD Erase failure: %d\n", + mtd_device, result); + continue; + } + + /* format for JFFS2 ? */ + if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) { + struct mtd_oob_ops ops; + ops.ooblen = 8; + ops.datbuf = NULL; + ops.oobbuf = (uint8_t *)&cleanmarker; + ops.ooboffs = 0; + ops.mode = MTD_OPS_AUTO_OOB; + + result = mtd_write_oob(meminfo, + erase.addr, + &ops); + if (result != 0) { + printf("\n%s: MTD writeoob failure: %d\n", + mtd_device, result); + continue; + } + } + + if (!opts->quiet) { + unsigned long long n = erased_length * 100ULL; + int percent; + + do_div(n, erase_length); + percent = (int)n; + + /* output progress message only at whole percent + * steps to reduce the number of messages printed + * on (slow) serial consoles + */ + if (percent != percent_complete) { + percent_complete = percent; + + printf("\rErasing at 0x%llx -- %3d%% complete.", + erase.addr, percent); + + if (opts->jffs2 && result == 0) + printf(" Cleanmarker written at 0x%llx.", + erase.addr); + } + } + } + if (!opts->quiet) + printf("\n"); + + if (opts->scrub) + chip->scan_bbt(meminfo); + + return 0; +} + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + +/****************************************************************************** + * Support for locking / unlocking operations of some NAND devices + *****************************************************************************/ + +/** + * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT + * state + * + * @param mtd nand mtd instance + * @param tight bring device in lock tight mode + * + * @return 0 on success, -1 in case of error + * + * The lock / lock-tight command only applies to the whole chip. To get some + * parts of the chip lock and others unlocked use the following sequence: + * + * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) + * - Call nand_unlock() once for each consecutive area to be unlocked + * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) + * + * If the device is in lock-tight state software can't change the + * current active lock/unlock state of all pages. nand_lock() / nand_unlock() + * calls will fail. It is only posible to leave lock-tight state by + * an hardware signal (low pulse on _WP pin) or by power down. + */ +int nand_lock(struct mtd_info *mtd, int tight) +{ + int ret = 0; + int status; + struct nand_chip *chip = mtd->priv; + + /* select the NAND device */ + chip->select_chip(mtd, 0); + + /* check the Lock Tight Status */ + chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, 0); + if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { + printf("nand_lock: Device is locked tight!\n"); + ret = -1; + goto out; + } + + chip->cmdfunc(mtd, + (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), + -1, -1); + + /* call wait ready function */ + status = chip->waitfunc(mtd, chip); + + /* see if device thinks it succeeded */ + if (status & 0x01) { + ret = -1; + } + + out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + return ret; +} + +/** + * nand_get_lock_status: - query current lock state from one page of NAND + * flash + * + * @param mtd nand mtd instance + * @param offset page address to query (must be page-aligned!) + * + * @return -1 in case of error + * >0 lock status: + * bitfield with the following combinations: + * NAND_LOCK_STATUS_TIGHT: page in tight state + * NAND_LOCK_STATUS_UNLOCK: page unlocked + * + */ +int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) +{ + int ret = 0; + int chipnr; + int page; + struct nand_chip *chip = mtd->priv; + + /* select the NAND device */ + chipnr = (int)(offset >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + + if ((offset & (mtd->writesize - 1)) != 0) { + printf("nand_get_lock_status: " + "Start address must be beginning of " + "nand page!\n"); + ret = -1; + goto out; + } + + /* check the Lock Status */ + page = (int)(offset >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); + + ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT + | NAND_LOCK_STATUS_UNLOCK); + + out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + return ret; +} + +/** + * nand_unlock: - Unlock area of NAND pages + * only one consecutive area can be unlocked at one time! + * + * @param mtd nand mtd instance + * @param start start byte address + * @param length number of bytes to unlock (must be a multiple of + * page size nand->writesize) + * @param allexcept if set, unlock everything not selected + * + * @return 0 on success, -1 in case of error + */ +int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length, + int allexcept) +{ + int ret = 0; + int chipnr; + int status; + int page; + struct nand_chip *chip = mtd->priv; + + debug("nand_unlock%s: start: %08llx, length: %zd!\n", + allexcept ? " (allexcept)" : "", start, length); + + /* select the NAND device */ + chipnr = (int)(start >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* check the WP bit */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) { + printf("nand_unlock: Device is write protected!\n"); + ret = -1; + goto out; + } + + /* check the Lock Tight Status */ + page = (int)(start >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); + if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { + printf("nand_unlock: Device is locked tight!\n"); + ret = -1; + goto out; + } + + if ((start & (mtd->erasesize - 1)) != 0) { + printf("nand_unlock: Start address must be beginning of " + "nand block!\n"); + ret = -1; + goto out; + } + + if (length == 0 || (length & (mtd->erasesize - 1)) != 0) { + printf("nand_unlock: Length must be a multiple of nand block " + "size %08x!\n", mtd->erasesize); + ret = -1; + goto out; + } + + /* + * Set length so that the last address is set to the + * starting address of the last block + */ + length -= mtd->erasesize; + + /* submit address of first page to unlock */ + chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); + + /* submit ADDRESS of LAST page to unlock */ + page += (int)(length >> chip->page_shift); + + /* + * Page addresses for unlocking are supposed to be block-aligned. + * At least some NAND chips use the low bit to indicate that the + * page range should be inverted. + */ + if (allexcept) + page |= 1; + + chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask); + + /* call wait ready function */ + status = chip->waitfunc(mtd, chip); + /* see if device thinks it succeeded */ + if (status & 0x01) { + /* there was an error */ + ret = -1; + goto out; + } + + out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + return ret; +} +#endif + +/** + * check_skip_len + * + * Check if there are any bad blocks, and whether length including bad + * blocks fits into device + * + * @param nand NAND device + * @param offset offset in flash + * @param length image length + * @param used length of flash needed for the requested length + * @return 0 if the image fits and there are no bad blocks + * 1 if the image fits, but there are bad blocks + * -1 if the image does not fit + */ +static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length, + size_t *used) +{ + size_t len_excl_bad = 0; + int ret = 0; + + while (len_excl_bad < length) { + size_t block_len, block_off; + loff_t block_start; + + if (offset >= nand->size) + return -1; + + block_start = offset & ~(loff_t)(nand->erasesize - 1); + block_off = offset & (nand->erasesize - 1); + block_len = nand->erasesize - block_off; + + if (!nand_block_isbad(nand, block_start)) + len_excl_bad += block_len; + else + ret = 1; + + offset += block_len; + *used += block_len; + } + + /* If the length is not a multiple of block_len, adjust. */ + if (len_excl_bad > length) + *used -= (len_excl_bad - length); + + return ret; +} + +#ifdef CONFIG_CMD_NAND_TRIMFFS +static size_t drop_ffs(const nand_info_t *nand, const u_char *buf, + const size_t *len) +{ + size_t l = *len; + ssize_t i; + + for (i = l - 1; i >= 0; i--) + if (buf[i] != 0xFF) + break; + + /* The resulting length must be aligned to the minimum flash I/O size */ + l = i + 1; + l = (l + nand->writesize - 1) / nand->writesize; + l *= nand->writesize; + + /* + * since the input length may be unaligned, prevent access past the end + * of the buffer + */ + return min(l, *len); +} +#endif + +/** + * nand_write_skip_bad: + * + * Write image to NAND flash. + * Blocks that are marked bad are skipped and the is written to the next + * block instead as long as the image is short enough to fit even after + * skipping the bad blocks. Due to bad blocks we may not be able to + * perform the requested write. In the case where the write would + * extend beyond the end of the NAND device, both length and actual (if + * not NULL) are set to 0. In the case where the write would extend + * beyond the limit we are passed, length is set to 0 and actual is set + * to the required length. + * + * @param nand NAND device + * @param offset offset in flash + * @param length buffer length + * @param actual set to size required to write length worth of + * buffer or 0 on error, if not NULL + * @param lim maximum size that actual may be in order to not + * exceed the buffer + * @param buffer buffer to read from + * @param flags flags modifying the behaviour of the write to NAND + * @return 0 in case of success + */ +int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, + size_t *actual, loff_t lim, u_char *buffer, int flags) +{ + int rval = 0, blocksize; + size_t left_to_write = *length; + size_t used_for_write = 0; + u_char *p_buffer = buffer; + int need_skip; + + if (actual) + *actual = 0; + +#ifdef CONFIG_CMD_NAND_YAFFS + if (flags & WITH_YAFFS_OOB) { + if (flags & ~WITH_YAFFS_OOB) + return -EINVAL; + + int pages; + pages = nand->erasesize / nand->writesize; + blocksize = (pages * nand->oobsize) + nand->erasesize; + if (*length % (nand->writesize + nand->oobsize)) { + printf("Attempt to write incomplete page" + " in yaffs mode\n"); + return -EINVAL; + } + } else +#endif + { + blocksize = nand->erasesize; + } + + /* + * nand_write() handles unaligned, partial page writes. + * + * We allow length to be unaligned, for convenience in + * using the $filesize variable. + * + * However, starting at an unaligned offset makes the + * semantics of bad block skipping ambiguous (really, + * you should only start a block skipping access at a + * partition boundary). So don't try to handle that. + */ + if ((offset & (nand->writesize - 1)) != 0) { + printf("Attempt to write non page-aligned data\n"); + *length = 0; + return -EINVAL; + } + + need_skip = check_skip_len(nand, offset, *length, &used_for_write); + + if (actual) + *actual = used_for_write; + + if (need_skip < 0) { + printf("Attempt to write outside the flash area\n"); + *length = 0; + return -EINVAL; + } + + if (used_for_write > lim) { + puts("Size of write exceeds partition or device limit\n"); + *length = 0; + return -EFBIG; + } + + if (!need_skip && !(flags & WITH_DROP_FFS)) { + rval = nand_write(nand, offset, length, buffer); + if (rval == 0) + return 0; + + *length = 0; + printf("NAND write to offset %llx failed %d\n", + offset, rval); + return rval; + } + + while (left_to_write > 0) { + size_t block_offset = offset & (nand->erasesize - 1); + size_t write_size, truncated_write_size; + + WATCHDOG_RESET(); + + if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) { + printf("Skip bad block 0x%08llx\n", + offset & ~(nand->erasesize - 1)); + offset += nand->erasesize - block_offset; + continue; + } + + if (left_to_write < (blocksize - block_offset)) + write_size = left_to_write; + else + write_size = blocksize - block_offset; + +#ifdef CONFIG_CMD_NAND_YAFFS + if (flags & WITH_YAFFS_OOB) { + int page, pages; + size_t pagesize = nand->writesize; + size_t pagesize_oob = pagesize + nand->oobsize; + struct mtd_oob_ops ops; + + ops.len = pagesize; + ops.ooblen = nand->oobsize; + ops.mode = MTD_OPS_AUTO_OOB; + ops.ooboffs = 0; + + pages = write_size / pagesize_oob; + for (page = 0; page < pages; page++) { + WATCHDOG_RESET(); + + ops.datbuf = p_buffer; + ops.oobbuf = ops.datbuf + pagesize; + + rval = mtd_write_oob(nand, offset, &ops); + if (rval != 0) + break; + + offset += pagesize; + p_buffer += pagesize_oob; + } + } + else +#endif + { + truncated_write_size = write_size; +#ifdef CONFIG_CMD_NAND_TRIMFFS + if (flags & WITH_DROP_FFS) + truncated_write_size = drop_ffs(nand, p_buffer, + &write_size); +#endif + + rval = nand_write(nand, offset, &truncated_write_size, + p_buffer); + offset += write_size; + p_buffer += write_size; + } + + if (rval != 0) { + printf("NAND write to offset %llx failed %d\n", + offset, rval); + *length -= left_to_write; + return rval; + } + + left_to_write -= write_size; + } + + return 0; +} + +/** + * nand_read_skip_bad: + * + * Read image from NAND flash. + * Blocks that are marked bad are skipped and the next block is read + * instead as long as the image is short enough to fit even after + * skipping the bad blocks. Due to bad blocks we may not be able to + * perform the requested read. In the case where the read would extend + * beyond the end of the NAND device, both length and actual (if not + * NULL) are set to 0. In the case where the read would extend beyond + * the limit we are passed, length is set to 0 and actual is set to the + * required length. + * + * @param nand NAND device + * @param offset offset in flash + * @param length buffer length, on return holds number of read bytes + * @param actual set to size required to read length worth of buffer or 0 + * on error, if not NULL + * @param lim maximum size that actual may be in order to not exceed the + * buffer + * @param buffer buffer to write to + * @return 0 in case of success + */ +int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, + size_t *actual, loff_t lim, u_char *buffer) +{ + int rval; + size_t left_to_read = *length; + size_t used_for_read = 0; + u_char *p_buffer = buffer; + int need_skip; + + if ((offset & (nand->writesize - 1)) != 0) { + printf("Attempt to read non page-aligned data\n"); + *length = 0; + if (actual) + *actual = 0; + return -EINVAL; + } + + need_skip = check_skip_len(nand, offset, *length, &used_for_read); + + if (actual) + *actual = used_for_read; + + if (need_skip < 0) { + printf("Attempt to read outside the flash area\n"); + *length = 0; + return -EINVAL; + } + + if (used_for_read > lim) { + puts("Size of read exceeds partition or device limit\n"); + *length = 0; + return -EFBIG; + } + + if (!need_skip) { + rval = nand_read(nand, offset, length, buffer); + if (!rval || rval == -EUCLEAN) + return 0; + + *length = 0; + printf("NAND read from offset %llx failed %d\n", + offset, rval); + return rval; + } + + while (left_to_read > 0) { + size_t block_offset = offset & (nand->erasesize - 1); + size_t read_length; + + WATCHDOG_RESET(); + + if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) { + printf("Skipping bad block 0x%08llx\n", + offset & ~(nand->erasesize - 1)); + offset += nand->erasesize - block_offset; + continue; + } + + if (left_to_read < (nand->erasesize - block_offset)) + read_length = left_to_read; + else + read_length = nand->erasesize - block_offset; + + rval = nand_read(nand, offset, &read_length, p_buffer); + if (rval && rval != -EUCLEAN) { + printf("NAND read from offset %llx failed %d\n", + offset, rval); + *length -= left_to_read; + return rval; + } + + left_to_read -= read_length; + offset += read_length; + p_buffer += read_length; + } + + return 0; +} + +#ifdef CONFIG_CMD_NAND_TORTURE + +/** + * check_pattern: + * + * Check if buffer contains only a certain byte pattern. + * + * @param buf buffer to check + * @param patt the pattern to check + * @param size buffer size in bytes + * @return 1 if there are only patt bytes in buf + * 0 if something else was found + */ +static int check_pattern(const u_char *buf, u_char patt, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (buf[i] != patt) + return 0; + return 1; +} + +/** + * nand_torture: + * + * Torture a block of NAND flash. + * This is useful to determine if a block that caused a write error is still + * good or should be marked as bad. + * + * @param nand NAND device + * @param offset offset in flash + * @return 0 if the block is still good + */ +int nand_torture(nand_info_t *nand, loff_t offset) +{ + u_char patterns[] = {0xa5, 0x5a, 0x00}; + struct erase_info instr = { + .mtd = nand, + .addr = offset, + .len = nand->erasesize, + }; + size_t retlen; + int err, ret = -1, i, patt_count; + u_char *buf; + + if ((offset & (nand->erasesize - 1)) != 0) { + puts("Attempt to torture a block at a non block-aligned offset\n"); + return -EINVAL; + } + + if (offset + nand->erasesize > nand->size) { + puts("Attempt to torture a block outside the flash area\n"); + return -EINVAL; + } + + patt_count = ARRAY_SIZE(patterns); + + buf = malloc(nand->erasesize); + if (buf == NULL) { + puts("Out of memory for erase block buffer\n"); + return -ENOMEM; + } + + for (i = 0; i < patt_count; i++) { + err = nand->erase(nand, &instr); + if (err) { + printf("%s: erase() failed for block at 0x%llx: %d\n", + nand->name, instr.addr, err); + goto out; + } + + /* Make sure the block contains only 0xff bytes */ + err = nand->read(nand, offset, nand->erasesize, &retlen, buf); + if ((err && err != -EUCLEAN) || retlen != nand->erasesize) { + printf("%s: read() failed for block at 0x%llx: %d\n", + nand->name, instr.addr, err); + goto out; + } + + err = check_pattern(buf, 0xff, nand->erasesize); + if (!err) { + printf("Erased block at 0x%llx, but a non-0xff byte was found\n", + offset); + ret = -EIO; + goto out; + } + + /* Write a pattern and check it */ + memset(buf, patterns[i], nand->erasesize); + err = nand->write(nand, offset, nand->erasesize, &retlen, buf); + if (err || retlen != nand->erasesize) { + printf("%s: write() failed for block at 0x%llx: %d\n", + nand->name, instr.addr, err); + goto out; + } + + err = nand->read(nand, offset, nand->erasesize, &retlen, buf); + if ((err && err != -EUCLEAN) || retlen != nand->erasesize) { + printf("%s: read() failed for block at 0x%llx: %d\n", + nand->name, instr.addr, err); + goto out; + } + + err = check_pattern(buf, patterns[i], nand->erasesize); + if (!err) { + printf("Pattern 0x%.2x checking failed for block at " + "0x%llx\n", patterns[i], offset); + ret = -EIO; + goto out; + } + } + + ret = 0; + +out: + free(buf); + return ret; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/mtd/nand/ndfc.c b/qemu/roms/u-boot/drivers/mtd/nand/ndfc.c new file mode 100644 index 000000000..5510b13c0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/ndfc.c @@ -0,0 +1,214 @@ +/* + * Overview: + * Platform independend driver for NDFC (NanD Flash Controller) + * integrated into IBM/AMCC PPC4xx cores + * + * (C) Copyright 2006-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * Based on original work by + * Thomas Gleixner + * Copyright 2006 IBM + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_SYS_NAND_BCR +#define CONFIG_SYS_NAND_BCR 0x80002222 +#endif +#ifndef CONFIG_SYS_NDFC_EBC0_CFG +#define CONFIG_SYS_NDFC_EBC0_CFG 0xb8400000 +#endif + +/* + * We need to store the info, which chip-select (CS) is used for the + * chip number. For example on Sequoia NAND chip #0 uses + * CS #3. + */ +static int ndfc_cs[NDFC_MAX_BANKS]; + +static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + out_8((u8 *)(base + NDFC_CMD), cmd & 0xFF); + else + out_8((u8 *)(base + NDFC_ALE), cmd & 0xFF); +} + +static int ndfc_dev_ready(struct mtd_info *mtdinfo) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + + return (in_be32((u32 *)(base + NDFC_STAT)) & NDFC_STAT_IS_READY); +} + +static void ndfc_enable_hwecc(struct mtd_info *mtdinfo, int mode) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + u32 ccr; + + ccr = in_be32((u32 *)(base + NDFC_CCR)); + ccr |= NDFC_CCR_RESET_ECC; + out_be32((u32 *)(base + NDFC_CCR), ccr); +} + +static int ndfc_calculate_ecc(struct mtd_info *mtdinfo, + const u_char *dat, u_char *ecc_code) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + u32 ecc; + u8 *p = (u8 *)&ecc; + + ecc = in_be32((u32 *)(base + NDFC_ECC)); + + /* The NDFC uses Smart Media (SMC) bytes order + */ + ecc_code[0] = p[1]; + ecc_code[1] = p[2]; + ecc_code[2] = p[3]; + + return 0; +} + +/* + * Speedups for buffer read/write/verify + * + * NDFC allows 32bit read/write of data. So we can speed up the buffer + * functions. No further checking, as nand_base will always read/write + * page aligned. + */ +static void ndfc_read_buf(struct mtd_info *mtdinfo, uint8_t *buf, int len) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + uint32_t *p = (uint32_t *) buf; + + for (;len > 0; len -= 4) + *p++ = in_be32((u32 *)(base + NDFC_DATA)); +} + +/* + * Don't use these speedup functions in NAND boot image, since the image + * has to fit into 4kByte. + */ +static void ndfc_write_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + uint32_t *p = (uint32_t *) buf; + + for (; len > 0; len -= 4) + out_be32((u32 *)(base + NDFC_DATA), *p++); +} + +static int ndfc_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + uint32_t *p = (uint32_t *) buf; + + for (; len > 0; len -= 4) + if (*p++ != in_be32((u32 *)(base + NDFC_DATA))) + return -1; + + return 0; +} + +/* + * Read a byte from the NDFC. + */ +static uint8_t ndfc_read_byte(struct mtd_info *mtd) +{ + + struct nand_chip *chip = mtd->priv; + +#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT + return (uint8_t) readw(chip->IO_ADDR_R); +#else + return readb(chip->IO_ADDR_R); +#endif + +} + +void board_nand_select_device(struct nand_chip *nand, int chip) +{ + /* + * Don't use "chip" to address the NAND device, + * generate the cs from the address where it is encoded. + */ + ulong base = (ulong)nand->IO_ADDR_W & 0xffffff00; + int cs = ndfc_cs[chip]; + + /* Set NandFlash Core Configuration Register */ + /* 1 col x 2 rows */ + out_be32((u32 *)(base + NDFC_CCR), 0x00000000 | (cs << 24)); + out_be32((u32 *)(base + NDFC_BCFG0 + (cs << 2)), CONFIG_SYS_NAND_BCR); +} + +static void ndfc_select_chip(struct mtd_info *mtd, int chip) +{ + /* + * Nothing to do here! + */ +} + +int board_nand_init(struct nand_chip *nand) +{ + int cs = (ulong)nand->IO_ADDR_W & 0x00000003; + ulong base = (ulong)nand->IO_ADDR_W & 0xffffff00; + static int chip = 0; + + /* + * Save chip-select for this chip # + */ + ndfc_cs[chip] = cs; + + /* + * Select required NAND chip in NDFC + */ + board_nand_select_device(nand, chip); + + nand->IO_ADDR_R = (void __iomem *)(base + NDFC_DATA); + nand->IO_ADDR_W = (void __iomem *)(base + NDFC_DATA); + nand->cmd_ctrl = ndfc_hwcontrol; + nand->chip_delay = 50; + nand->read_buf = ndfc_read_buf; + nand->dev_ready = ndfc_dev_ready; + nand->ecc.correct = nand_correct_data; + nand->ecc.hwctl = ndfc_enable_hwecc; + nand->ecc.calculate = ndfc_calculate_ecc; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 256; + nand->ecc.bytes = 3; + nand->ecc.strength = 1; + nand->select_chip = ndfc_select_chip; + +#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT + nand->options |= NAND_BUSWIDTH_16; +#endif + + nand->write_buf = ndfc_write_buf; + nand->verify_buf = ndfc_verify_buf; + nand->read_byte = ndfc_read_byte; + + chip++; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nomadik.c b/qemu/roms/u-boot/drivers/mtd/nand/nomadik.c new file mode 100644 index 000000000..a7cee5138 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nomadik.c @@ -0,0 +1,206 @@ +/* + * (C) Copyright 2007 STMicroelectronics, + * (C) Copyright 2009 Alessandro Rubini + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */ +{ + __asm__ __volatile__( + "eor %0, %0, %0, lsr #4\n\t" + "eor %0, %0, %0, lsr #2\n\t" + "eor %0, %0, %0, lsr #1\n\t" + "ands %0, %0, #1\n\t" + "subne %0, %0, #2\t" + : "=r" (b) : "0" (b)); + return b; +} + +/* + * This is the ECC routine used in hardware, according to the manual. + * HW claims to make the calculation but not the correction; so we must + * recalculate the bytes for a comparison. + */ +static int ecc512(const unsigned char *data, unsigned char *ecc) +{ + int gpar = 0; + int i, val, par; + int pbits = 0; /* P8, P16, ... P2048 */ + int pprime = 0; /* P8', P16', ... P2048' */ + int lowbits; /* P1, P2, P4 and primes */ + + for (i = 0; i < 512; i++) { + par = parity((val = data[i])); + gpar ^= val; + pbits ^= (i & par); + } + /* + * Ok, now gpar is global parity (xor of all bytes) + * pbits are all the parity bits (non-prime ones) + */ + par = parity(gpar); + pprime = pbits ^ par; + /* Put low bits in the right position for ecc[2] (bits 7..2) */ + lowbits = 0 + | (parity(gpar & 0xf0) & 0x80) /* P4 */ + | (parity(gpar & 0x0f) & 0x40) /* P4' */ + | (parity(gpar & 0xcc) & 0x20) /* P2 */ + | (parity(gpar & 0x33) & 0x10) /* P2' */ + | (parity(gpar & 0xaa) & 0x08) /* P1 */ + | (parity(gpar & 0x55) & 0x04); /* P1' */ + + ecc[2] = ~(lowbits | ((pbits & 0x100) >> 7) | ((pprime & 0x100) >> 8)); + /* now intermix bits for ecc[1] (P1024..P128') and ecc[0] (P64..P8') */ + ecc[1] = ~( (pbits & 0x80) >> 0 | ((pprime & 0x80) >> 1) + | ((pbits & 0x40) >> 1) | ((pprime & 0x40) >> 2) + | ((pbits & 0x20) >> 2) | ((pprime & 0x20) >> 3) + | ((pbits & 0x10) >> 3) | ((pprime & 0x10) >> 4)); + + ecc[0] = ~( (pbits & 0x8) << 4 | ((pprime & 0x8) << 3) + | ((pbits & 0x4) << 3) | ((pprime & 0x4) << 2) + | ((pbits & 0x2) << 2) | ((pprime & 0x2) << 1) + | ((pbits & 0x1) << 1) | ((pprime & 0x1) << 0)); + return 0; +} + +/* This is the method in the chip->ecc field */ +static int nomadik_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + return ecc512(dat, ecc_code); +} + +static int nomadik_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *r_ecc, uint8_t *c_ecc) +{ + struct nand_chip *chip = mtd->priv; + uint32_t r, c, d, diff; /*read, calculated, xor of them */ + + if (!memcmp(r_ecc, c_ecc, chip->ecc.bytes)) + return 0; + + /* Reorder the bytes into ascending-order 24 bits -- see manual */ + r = r_ecc[2] << 22 | r_ecc[1] << 14 | r_ecc[0] << 6 | r_ecc[2] >> 2; + c = c_ecc[2] << 22 | c_ecc[1] << 14 | c_ecc[0] << 6 | c_ecc[2] >> 2; + diff = (r ^ c) & ((1<<24)-1); /* use 24 bits only */ + + /* If 12 bits are different, one per pair, it's correctable */ + if (((diff | (diff>>1)) & 0x555555) == 0x555555) { + int bit = ((diff & 2) >> 1) + | ((diff & 0x8) >> 2) | ((diff & 0x20) >> 3); + int byte; + + d = diff >> 6; /* remove bit-order info */ + byte = ((d & 2) >> 1) + | ((d & 0x8) >> 2) | ((d & 0x20) >> 3) + | ((d & 0x80) >> 4) | ((d & 0x200) >> 5) + | ((d & 0x800) >> 6) | ((d & 0x2000) >> 7) + | ((d & 0x8000) >> 8) | ((d & 0x20000) >> 9); + /* correct the single bit */ + dat[byte] ^= 1<priv; + u32 pcr0 = readl(REG_FSMC_PCR0); + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + IO_ADDR_W &= ~(MASK_ALE | MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= MASK_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= MASK_ALE; + + if (ctrl & NAND_NCE) + writel(pcr0 | 0x4, REG_FSMC_PCR0); + else + writel(pcr0 & ~0x4, REG_FSMC_PCR0); + + this->IO_ADDR_W = (void *) IO_ADDR_W; + this->IO_ADDR_R = (void *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* Returns 1 when ready; upper layers timeout at 20ms with timer routines */ +static int nomadik_nand_ready(struct mtd_info *mtd) +{ + return 1; /* The ready bit is handled in hardware */ +} + +/* Copy a buffer 32bits at a time: faster than defualt method which is 8bit */ +static void nomadik_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u32 *p = (u32 *) buf; + + len >>= 2; + writel(0, REG_FSMC_ECCR0); + for (i = 0; i < len; i++) + p[i] = readl(chip->IO_ADDR_R); +} + +int board_nand_init(struct nand_chip *chip) +{ + /* Set up the FSMC_PCR0 for nand access*/ + writel(0x0000004a, REG_FSMC_PCR0); + /* Set up FSMC_PMEM0, FSMC_PATT0 with timing data for access */ + writel(0x00020401, REG_FSMC_PMEM0); + writel(0x00020404, REG_FSMC_PATT0); + + chip->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING; + chip->cmd_ctrl = nomadik_nand_hwcontrol; + chip->dev_ready = nomadik_nand_ready; + /* The chip allows 32bit reads, so avoid the default 8bit copy */ + chip->read_buf = nomadik_nand_read_buf; + + /* ECC: follow the hardware-defined rulse, but do it in sw */ + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.bytes = 3; + chip->ecc.size = 512; + chip->ecc.strength = 1; + chip->ecc.layout = &nomadik_ecc_layout; + chip->ecc.calculate = nomadik_ecc_calculate; + chip->ecc.hwctl = nomadik_ecc_hwctl; + chip->ecc.correct = nomadik_ecc_correct; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/omap_elm.c b/qemu/roms/u-boot/drivers/mtd/nand/omap_elm.c new file mode 100644 index 000000000..47b1f1bfe --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/omap_elm.c @@ -0,0 +1,196 @@ +/* + * (C) Copyright 2010-2011 Texas Instruments, + * Mansoor Ahamed + * + * BCH Error Location Module (ELM) support. + * + * NOTE: + * 1. Supports only continuous mode. Dont see need for page mode in uboot + * 2. Supports only syndrome polynomial 0. i.e. poly local variable is + * always set to ELM_DEFAULT_POLY. Dont see need for other polynomial + * sets in uboot + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#define ELM_DEFAULT_POLY (0) + +struct elm *elm_cfg; + +/** + * elm_load_syndromes - Load BCH syndromes based on nibble selection + * @syndrome: BCH syndrome + * @nibbles: + * @poly: Syndrome Polynomial set to use + * + * Load BCH syndromes based on nibble selection + */ +static void elm_load_syndromes(u8 *syndrome, u32 nibbles, u8 poly) +{ + u32 *ptr; + u32 val; + + /* reg 0 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0]; + val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) | + (syndrome[3] << 24); + writel(val, ptr); + /* reg 1 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1]; + val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) | + (syndrome[7] << 24); + writel(val, ptr); + + /* BCH 8-bit with 26 nibbles (4*8=32) */ + if (nibbles > 13) { + /* reg 2 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2]; + val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) | + (syndrome[11] << 24); + writel(val, ptr); + /* reg 3 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[3]; + val = syndrome[12] | (syndrome[13] << 8) | + (syndrome[14] << 16) | (syndrome[15] << 24); + writel(val, ptr); + } + + /* BCH 16-bit with 52 nibbles (7*8=56) */ + if (nibbles > 26) { + /* reg 4 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[4]; + val = syndrome[16] | (syndrome[17] << 8) | + (syndrome[18] << 16) | (syndrome[19] << 24); + writel(val, ptr); + + /* reg 5 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[5]; + val = syndrome[20] | (syndrome[21] << 8) | + (syndrome[22] << 16) | (syndrome[23] << 24); + writel(val, ptr); + + /* reg 6 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]; + val = syndrome[24] | (syndrome[25] << 8) | + (syndrome[26] << 16) | (syndrome[27] << 24); + writel(val, ptr); + } +} + +/** + * elm_check_errors - Check for BCH errors and return error locations + * @syndrome: BCH syndrome + * @nibbles: + * @error_count: Returns number of errrors in the syndrome + * @error_locations: Returns error locations (in decimal) in this array + * + * Check the provided syndrome for BCH errors and return error count + * and locations in the array passed. Returns -1 if error is not correctable, + * else returns 0 + */ +int elm_check_error(u8 *syndrome, u32 nibbles, u32 *error_count, + u32 *error_locations) +{ + u8 poly = ELM_DEFAULT_POLY; + s8 i; + u32 location_status; + + elm_load_syndromes(syndrome, nibbles, poly); + + /* start processing */ + writel((readl(&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]) + | ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID), + &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]); + + /* wait for processing to complete */ + while ((readl(&elm_cfg->irqstatus) & (0x1 << poly)) != 0x1) + ; + /* clear status */ + writel((readl(&elm_cfg->irqstatus) | (0x1 << poly)), + &elm_cfg->irqstatus); + + /* check if correctable */ + location_status = readl(&elm_cfg->error_location[poly].location_status); + if (!(location_status & ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK)) + return -1; + + /* get error count */ + *error_count = readl(&elm_cfg->error_location[poly].location_status) & + ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK; + + for (i = 0; i < *error_count; i++) { + error_locations[i] = + readl(&elm_cfg->error_location[poly].error_location_x[i]); + } + + return 0; +} + + +/** + * elm_config - Configure ELM module + * @level: 4 / 8 / 16 bit BCH + * + * Configure ELM module based on BCH level. + * Set mode as continuous mode. + * Currently we are using only syndrome 0 and syndromes 1 to 6 are not used. + * Also, the mode is set only for syndrome 0 + */ +int elm_config(enum bch_level level) +{ + u32 val; + u8 poly = ELM_DEFAULT_POLY; + u32 buffer_size = 0x7FF; + + /* config size and level */ + val = (u32)(level) & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK; + val |= ((buffer_size << ELM_LOCATION_CONFIG_ECC_SIZE_POS) & + ELM_LOCATION_CONFIG_ECC_SIZE_MASK); + writel(val, &elm_cfg->location_config); + + /* config continous mode */ + /* enable interrupt generation for syndrome polynomial set */ + writel((readl(&elm_cfg->irqenable) | (0x1 << poly)), + &elm_cfg->irqenable); + /* set continuous mode for the syndrome polynomial set */ + writel((readl(&elm_cfg->page_ctrl) & ~(0x1 << poly)), + &elm_cfg->page_ctrl); + + return 0; +} + +/** + * elm_reset - Do a soft reset of ELM + * + * Perform a soft reset of ELM and return after reset is done. + */ +void elm_reset(void) +{ + /* initiate reset */ + writel((readl(&elm_cfg->sysconfig) | ELM_SYSCONFIG_SOFTRESET), + &elm_cfg->sysconfig); + + /* wait for reset complete and normal operation */ + while ((readl(&elm_cfg->sysstatus) & ELM_SYSSTATUS_RESETDONE) != + ELM_SYSSTATUS_RESETDONE) + ; +} + +/** + * elm_init - Initialize ELM module + * + * Initialize ELM support. Currently it does only base address init + * and ELM reset. + */ +void elm_init(void) +{ + elm_cfg = (struct elm *)ELM_BASE; + elm_reset(); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/omap_gpmc.c b/qemu/roms/u-boot/drivers/mtd/nand/omap_gpmc.c new file mode 100644 index 000000000..881a63618 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/omap_gpmc.c @@ -0,0 +1,836 @@ +/* + * (C) Copyright 2004-2008 Texas Instruments, + * Rohit Choraria + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BADBLOCK_MARKER_LENGTH 2 +#define SECTOR_BYTES 512 +#define ECCCLEAR (0x1 << 8) +#define ECCRESULTREG1 (0x1 << 0) +/* 4 bit padding to make byte aligned, 56 = 52 + 4 */ +#define BCH4_BIT_PAD 4 + +#ifdef CONFIG_BCH +static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, + 0x97, 0x79, 0xe5, 0x24, 0xb5}; +#endif +static uint8_t cs; +static __maybe_unused struct nand_ecclayout omap_ecclayout; + +/* + * omap_nand_hwcontrol - Set the address pointers corretly for the + * following address/data/command operation + */ +static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd, + uint32_t ctrl) +{ + register struct nand_chip *this = mtd->priv; + + /* + * Point the IO_ADDR to DATA and ADDRESS registers instead + * of chip address + */ + switch (ctrl) { + case NAND_CTRL_CHANGE | NAND_CTRL_CLE: + this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; + break; + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_adr; + break; + case NAND_CTRL_CHANGE | NAND_NCE: + this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; + break; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +#ifdef CONFIG_SPL_BUILD +/* Check wait pin as dev ready indicator */ +int omap_spl_dev_ready(struct mtd_info *mtd) +{ + return gpmc_cfg->status & (1 << 8); +} +#endif + + +/* + * gen_true_ecc - This function will generate true ECC value, which + * can be used when correcting data read from NAND flash memory core + * + * @ecc_buf: buffer to store ecc code + * + * @return: re-formatted ECC value + */ +static uint32_t gen_true_ecc(uint8_t *ecc_buf) +{ + return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | + ((ecc_buf[2] & 0x0F) << 8); +} + +/* + * omap_correct_data - Compares the ecc read from nand spare area with ECC + * registers values and corrects one bit error if it has occured + * Further details can be had from OMAP TRM and the following selected links: + * http://en.wikipedia.org/wiki/Hamming_code + * http://www.cs.utexas.edu/users/plaxton/c/337/05f/slides/ErrorCorrection-4.pdf + * + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from ECC registers + * + * @return 0 if data is OK or corrected, else returns -1 + */ +static int __maybe_unused omap_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + uint32_t orig_ecc, new_ecc, res, hm; + uint16_t parity_bits, byte; + uint8_t bit; + + /* Regenerate the orginal ECC */ + orig_ecc = gen_true_ecc(read_ecc); + new_ecc = gen_true_ecc(calc_ecc); + /* Get the XOR of real ecc */ + res = orig_ecc ^ new_ecc; + if (res) { + /* Get the hamming width */ + hm = hweight32(res); + /* Single bit errors can be corrected! */ + if (hm == 12) { + /* Correctable data! */ + parity_bits = res >> 16; + bit = (parity_bits & 0x7); + byte = (parity_bits >> 3) & 0x1FF; + /* Flip the bit to correct */ + dat[byte] ^= (0x1 << bit); + } else if (hm == 1) { + printf("Error: Ecc is wrong\n"); + /* ECC itself is corrupted */ + return 2; + } else { + /* + * hm distance != parity pairs OR one, could mean 2 bit + * error OR potentially be on a blank page.. + * orig_ecc: contains spare area data from nand flash. + * new_ecc: generated ecc while reading data area. + * Note: if the ecc = 0, all data bits from which it was + * generated are 0xFF. + * The 3 byte(24 bits) ecc is generated per 512byte + * chunk of a page. If orig_ecc(from spare area) + * is 0xFF && new_ecc(computed now from data area)=0x0, + * this means that data area is 0xFF and spare area is + * 0xFF. A sure sign of a erased page! + */ + if ((orig_ecc == 0x0FFF0FFF) && (new_ecc == 0x00000000)) + return 0; + printf("Error: Bad compare! failed\n"); + /* detected 2 bit error */ + return -1; + } + } + return 0; +} + +/* + * Generic BCH interface + */ +struct nand_bch_priv { + uint8_t mode; + uint8_t type; + uint8_t nibbles; + struct bch_control *control; + enum omap_ecc ecc_scheme; +}; + +/* bch types */ +#define ECC_BCH4 0 +#define ECC_BCH8 1 +#define ECC_BCH16 2 + +/* BCH nibbles for diff bch levels */ +#define ECC_BCH4_NIBBLES 13 +#define ECC_BCH8_NIBBLES 26 +#define ECC_BCH16_NIBBLES 52 + +/* + * This can be a single instance cause all current users have only one NAND + * with nearly the same setup (BCH8, some with ELM and others with sw BCH + * library). + * When some users with other BCH strength will exists this have to change! + */ +static __maybe_unused struct nand_bch_priv bch_priv = { + .type = ECC_BCH8, + .nibbles = ECC_BCH8_NIBBLES, + .control = NULL +}; + +/* + * omap_reverse_list - re-orders list elements in reverse order [internal] + * @list: pointer to start of list + * @length: length of list +*/ +void omap_reverse_list(u8 *list, unsigned int length) +{ + unsigned int i, j; + unsigned int half_length = length / 2; + u8 tmp; + for (i = 0, j = length - 1; i < half_length; i++, j--) { + tmp = list[i]; + list[i] = list[j]; + list[j] = tmp; + } +} + +/* + * omap_enable_hwecc - configures GPMC as per ECC scheme before read/write + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +__maybe_unused +static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) +{ + struct nand_chip *nand = mtd->priv; + struct nand_bch_priv *bch = nand->priv; + unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0; + unsigned int ecc_algo = 0; + unsigned int bch_type = 0; + unsigned int eccsize1 = 0x00, eccsize0 = 0x00, bch_wrapmode = 0x00; + u32 ecc_size_config_val = 0; + u32 ecc_config_val = 0; + + /* configure GPMC for specific ecc-scheme */ + switch (bch->ecc_scheme) { + case OMAP_ECC_HAM1_CODE_SW: + return; + case OMAP_ECC_HAM1_CODE_HW: + ecc_algo = 0x0; + bch_type = 0x0; + bch_wrapmode = 0x00; + eccsize0 = 0xFF; + eccsize1 = 0xFF; + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH8_CODE_HW: + ecc_algo = 0x1; + bch_type = 0x1; + if (mode == NAND_ECC_WRITE) { + bch_wrapmode = 0x01; + eccsize0 = 0; /* extra bits in nibbles per sector */ + eccsize1 = 28; /* OOB bits in nibbles per sector */ + } else { + bch_wrapmode = 0x01; + eccsize0 = 26; /* ECC bits in nibbles per sector */ + eccsize1 = 2; /* non-ECC bits in nibbles per sector */ + } + break; + default: + return; + } + /* Clear ecc and enable bits */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + /* Configure ecc size for BCH */ + ecc_size_config_val = (eccsize1 << 22) | (eccsize0 << 12); + writel(ecc_size_config_val, &gpmc_cfg->ecc_size_config); + + /* Configure device details for BCH engine */ + ecc_config_val = ((ecc_algo << 16) | /* HAM1 | BCHx */ + (bch_type << 12) | /* BCH4/BCH8/BCH16 */ + (bch_wrapmode << 8) | /* wrap mode */ + (dev_width << 7) | /* bus width */ + (0x0 << 4) | /* number of sectors */ + (cs << 1) | /* ECC CS */ + (0x1)); /* enable ECC */ + writel(ecc_config_val, &gpmc_cfg->ecc_config); +} + +/* + * omap_calculate_ecc - Read ECC result + * @mtd: MTD structure + * @dat: unused + * @ecc_code: ecc_code buffer + * Using noninverted ECC can be considered ugly since writing a blank + * page ie. padding will clear the ECC bytes. This is no problem as + * long nobody is trying to write data on the seemingly unused page. + * Reading an erased page will produce an ECC mismatch between + * generated and read ECC bytes that has to be dealt with separately. + * E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC + * is used, the result of read will be 0x0 while the ECC offsets of the + * spare area will be 0xFF which will result in an ECC mismatch. + */ +static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *bch = chip->priv; + uint32_t *ptr, val = 0; + int8_t i = 0, j; + + switch (bch->ecc_scheme) { + case OMAP_ECC_HAM1_CODE_HW: + val = readl(&gpmc_cfg->ecc1_result); + ecc_code[0] = val & 0xFF; + ecc_code[1] = (val >> 16) & 0xFF; + ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0); + break; +#ifdef CONFIG_BCH + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: +#endif + case OMAP_ECC_BCH8_CODE_HW: + ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3]; + val = readl(ptr); + ecc_code[i++] = (val >> 0) & 0xFF; + ptr--; + for (j = 0; j < 3; j++) { + val = readl(ptr); + ecc_code[i++] = (val >> 24) & 0xFF; + ecc_code[i++] = (val >> 16) & 0xFF; + ecc_code[i++] = (val >> 8) & 0xFF; + ecc_code[i++] = (val >> 0) & 0xFF; + ptr--; + } + break; + default: + return -EINVAL; + } + /* ECC scheme specific syndrome customizations */ + switch (bch->ecc_scheme) { + case OMAP_ECC_HAM1_CODE_HW: + break; +#ifdef CONFIG_BCH + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + + for (i = 0; i < chip->ecc.bytes; i++) + *(ecc_code + i) = *(ecc_code + i) ^ + bch8_polynomial[i]; + break; +#endif + case OMAP_ECC_BCH8_CODE_HW: + ecc_code[chip->ecc.bytes - 1] = 0x00; + break; + default: + return -EINVAL; + } + return 0; +} + +#ifdef CONFIG_NAND_OMAP_ELM +/* + * omap_correct_data_bch - Compares the ecc read from nand spare area + * with ECC registers values and corrects one bit error if it has occured + * + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash (ignored) + * @calc_ecc: ecc read from ECC registers + * + * @return 0 if data is OK or corrected, else returns -1 + */ +static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *bch = chip->priv; + uint32_t eccbytes = chip->ecc.bytes; + uint32_t error_count = 0, error_max; + uint32_t error_loc[8]; + uint32_t i, ecc_flag = 0; + uint8_t count, err = 0; + uint32_t byte_pos, bit_pos; + + /* check calculated ecc */ + for (i = 0; i < chip->ecc.bytes && !ecc_flag; i++) { + if (calc_ecc[i] != 0x00) + ecc_flag = 1; + } + if (!ecc_flag) + return 0; + + /* check for whether its a erased-page */ + ecc_flag = 0; + for (i = 0; i < chip->ecc.bytes && !ecc_flag; i++) { + if (read_ecc[i] != 0xff) + ecc_flag = 1; + } + if (!ecc_flag) + return 0; + + /* + * while reading ECC result we read it in big endian. + * Hence while loading to ELM we have rotate to get the right endian. + */ + switch (bch->ecc_scheme) { + case OMAP_ECC_BCH8_CODE_HW: + omap_reverse_list(calc_ecc, eccbytes - 1); + break; + default: + return -EINVAL; + } + /* use elm module to check for errors */ + elm_config((enum bch_level)(bch->type)); + if (elm_check_error(calc_ecc, bch->nibbles, &error_count, error_loc)) { + printf("nand: error: uncorrectable ECC errors\n"); + return -EINVAL; + } + /* correct bch error */ + for (count = 0; count < error_count; count++) { + switch (bch->type) { + case ECC_BCH8: + /* 14th byte in ECC is reserved to match ROM layout */ + error_max = SECTOR_BYTES + (eccbytes - 1); + break; + default: + return -EINVAL; + } + byte_pos = error_max - (error_loc[count] / 8) - 1; + bit_pos = error_loc[count] % 8; + if (byte_pos < SECTOR_BYTES) { + dat[byte_pos] ^= 1 << bit_pos; + printf("nand: bit-flip corrected @data=%d\n", byte_pos); + } else if (byte_pos < error_max) { + read_ecc[byte_pos - SECTOR_BYTES] = 1 << bit_pos; + printf("nand: bit-flip corrected @oob=%d\n", byte_pos - + SECTOR_BYTES); + } else { + err = -EBADMSG; + printf("nand: error: invalid bit-flip location\n"); + } + } + return (err) ? err : error_count; +} + +/** + * omap_read_page_bch - hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi + * @page: page number to read + * + */ +static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *oob = chip->oob_poi; + uint32_t data_pos; + uint32_t oob_pos; + + data_pos = 0; + /* oob area start */ + oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0]; + oob += chip->ecc.layout->eccpos[0]; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize, + oob += eccbytes) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + /* read data */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, page); + chip->read_buf(mtd, p, eccsize); + + /* read respective ecc from oob area */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, page); + chip->read_buf(mtd, oob, eccbytes); + /* read syndrome */ + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + data_pos += eccsize; + oob_pos += eccbytes; + } + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} +#endif /* CONFIG_NAND_OMAP_ELM */ + +/* + * OMAP3 BCH8 support (with BCH library) + */ +#ifdef CONFIG_BCH +/** + * omap_correct_data_bch_sw - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = chip_priv->control; + + count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < 8*512) + data[errloc[i]/8] ^= 1 << (errloc[i] & 7); + printf("corrected bitflip %u\n", errloc[i]); +#ifdef DEBUG + puts("read_ecc: "); + /* + * BCH8 have 13 bytes of ECC; BCH4 needs adoption + * here! + */ + for (i = 0; i < 13; i++) + printf("%02x ", read_ecc[i]); + puts("\n"); + puts("calc_ecc: "); + for (i = 0; i < 13; i++) + printf("%02x ", calc_ecc[i]); + puts("\n"); +#endif + } + } else if (count < 0) { + puts("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void __maybe_unused omap_free_bch(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = NULL; + + if (chip_priv) + bch = chip_priv->control; + + if (bch) { + free_bch(bch); + chip_priv->control = NULL; + } +} +#endif /* CONFIG_BCH */ + +/** + * omap_select_ecc_scheme - configures driver for particular ecc-scheme + * @nand: NAND chip device structure + * @ecc_scheme: ecc scheme to configure + * @pagesize: number of main-area bytes per page of NAND device + * @oobsize: number of OOB/spare bytes per page of NAND device + */ +static int omap_select_ecc_scheme(struct nand_chip *nand, + enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) { + struct nand_bch_priv *bch = nand->priv; + struct nand_ecclayout *ecclayout = &omap_ecclayout; + int eccsteps = pagesize / SECTOR_BYTES; + int i; + + switch (ecc_scheme) { + case OMAP_ECC_HAM1_CODE_SW: + debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n"); + /* For this ecc-scheme, ecc.bytes, ecc.layout, ... are + * initialized in nand_scan_tail(), so just set ecc.mode */ + bch_priv.control = NULL; + bch_priv.type = 0; + nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.layout = NULL; + nand->ecc.size = 0; + bch->ecc_scheme = OMAP_ECC_HAM1_CODE_SW; + break; + + case OMAP_ECC_HAM1_CODE_HW: + debug("nand: selected OMAP_ECC_HAM1_CODE_HW\n"); + /* check ecc-scheme requirements before updating ecc info */ + if ((3 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { + printf("nand: error: insufficient OOB: require=%d\n", ( + (3 * eccsteps) + BADBLOCK_MARKER_LENGTH)); + return -EINVAL; + } + bch_priv.control = NULL; + bch_priv.type = 0; + /* populate ecc specific fields */ + memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.strength = 1; + nand->ecc.size = SECTOR_BYTES; + nand->ecc.bytes = 3; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.calculate = omap_calculate_ecc; + /* define ecc-layout */ + ecclayout->eccbytes = nand->ecc.bytes * eccsteps; + for (i = 0; i < ecclayout->eccbytes; i++) { + if (nand->options & NAND_BUSWIDTH_16) + ecclayout->eccpos[i] = i + 2; + else + ecclayout->eccpos[i] = i + 1; + } + ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - + BADBLOCK_MARKER_LENGTH; + bch->ecc_scheme = OMAP_ECC_HAM1_CODE_HW; + break; + + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: +#ifdef CONFIG_BCH + debug("nand: selected OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); + /* check ecc-scheme requirements before updating ecc info */ + if ((13 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { + printf("nand: error: insufficient OOB: require=%d\n", ( + (13 * eccsteps) + BADBLOCK_MARKER_LENGTH)); + return -EINVAL; + } + /* check if BCH S/W library can be used for error detection */ + bch_priv.control = init_bch(13, 8, 0x201b); + if (!bch_priv.control) { + printf("nand: error: could not init_bch()\n"); + return -ENODEV; + } + bch_priv.type = ECC_BCH8; + /* populate ecc specific fields */ + memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.strength = 8; + nand->ecc.size = SECTOR_BYTES; + nand->ecc.bytes = 13; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data_bch_sw; + nand->ecc.calculate = omap_calculate_ecc; + /* define ecc-layout */ + ecclayout->eccbytes = nand->ecc.bytes * eccsteps; + ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH; + for (i = 1; i < ecclayout->eccbytes; i++) { + if (i % nand->ecc.bytes) + ecclayout->eccpos[i] = + ecclayout->eccpos[i - 1] + 1; + else + ecclayout->eccpos[i] = + ecclayout->eccpos[i - 1] + 2; + } + ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - + BADBLOCK_MARKER_LENGTH; + bch->ecc_scheme = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; + break; +#else + printf("nand: error: CONFIG_BCH required for ECC\n"); + return -EINVAL; +#endif + + case OMAP_ECC_BCH8_CODE_HW: +#ifdef CONFIG_NAND_OMAP_ELM + debug("nand: selected OMAP_ECC_BCH8_CODE_HW\n"); + /* check ecc-scheme requirements before updating ecc info */ + if ((14 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { + printf("nand: error: insufficient OOB: require=%d\n", ( + (14 * eccsteps) + BADBLOCK_MARKER_LENGTH)); + return -EINVAL; + } + /* intialize ELM for ECC error detection */ + elm_init(); + bch_priv.type = ECC_BCH8; + /* populate ecc specific fields */ + memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.strength = 8; + nand->ecc.size = SECTOR_BYTES; + nand->ecc.bytes = 14; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc; + nand->ecc.read_page = omap_read_page_bch; + /* define ecc-layout */ + ecclayout->eccbytes = nand->ecc.bytes * eccsteps; + for (i = 0; i < ecclayout->eccbytes; i++) + ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - + BADBLOCK_MARKER_LENGTH; + bch->ecc_scheme = OMAP_ECC_BCH8_CODE_HW; + break; +#else + printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n"); + return -EINVAL; +#endif + + default: + debug("nand: error: ecc scheme not enabled or supported\n"); + return -EINVAL; + } + + /* nand_scan_tail() sets ham1 sw ecc; hw ecc layout is set by driver */ + if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW) + nand->ecc.layout = ecclayout; + + return 0; +} + +#ifndef CONFIG_SPL_BUILD +/* + * omap_nand_switch_ecc - switch the ECC operation between different engines + * (h/w and s/w) and different algorithms (hamming and BCHx) + * + * @hardware - true if one of the HW engines should be used + * @eccstrength - the number of bits that could be corrected + * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16) + */ +int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) +{ + struct nand_chip *nand; + struct mtd_info *mtd; + int err = 0; + + if (nand_curr_device < 0 || + nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + printf("nand: error: no NAND devices found\n"); + return -ENODEV; + } + + mtd = &nand_info[nand_curr_device]; + nand = mtd->priv; + nand->options |= NAND_OWN_BUFFERS; + nand->options &= ~NAND_SUBPAGE_READ; + /* Setup the ecc configurations again */ + if (hardware) { + if (eccstrength == 1) { + err = omap_select_ecc_scheme(nand, + OMAP_ECC_HAM1_CODE_HW, + mtd->writesize, mtd->oobsize); + } else if (eccstrength == 8) { + err = omap_select_ecc_scheme(nand, + OMAP_ECC_BCH8_CODE_HW, + mtd->writesize, mtd->oobsize); + } else { + printf("nand: error: unsupported ECC scheme\n"); + return -EINVAL; + } + } else { + err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW, + mtd->writesize, mtd->oobsize); + } + + /* Update NAND handling after ECC mode switch */ + if (!err) + err = nand_scan_tail(mtd); + return err; +} +#endif /* CONFIG_SPL_BUILD */ + +/* + * Board-specific NAND initialization. The following members of the + * argument are board-specific: + * - IO_ADDR_R: address to read the 8 I/O lines of the flash device + * - IO_ADDR_W: address to write the 8 I/O lines of the flash device + * - cmd_ctrl: hardwarespecific function for accesing control-lines + * - waitfunc: hardwarespecific function for accesing device ready/busy line + * - ecc.hwctl: function to enable (reset) hardware ecc generator + * - ecc.mode: mode of ecc, see defines + * - chip_delay: chip dependent delay for transfering data from array to + * read regs (tR) + * - options: various chip options. They can partly be set to inform + * nand_scan about special functionality. See the defines for further + * explanation + */ +int board_nand_init(struct nand_chip *nand) +{ + int32_t gpmc_config = 0; + cs = 0; + int err = 0; + /* + * xloader/Uboot's gpmc configuration would have configured GPMC for + * nand type of memory. The following logic scans and latches on to the + * first CS with NAND type memory. + * TBD: need to make this logic generic to handle multiple CS NAND + * devices. + */ + while (cs < GPMC_MAX_CS) { + /* Check if NAND type is set */ + if ((readl(&gpmc_cfg->cs[cs].config1) & 0xC00) == 0x800) { + /* Found it!! */ + break; + } + cs++; + } + if (cs >= GPMC_MAX_CS) { + printf("nand: error: Unable to find NAND settings in " + "GPMC Configuration - quitting\n"); + return -ENODEV; + } + + gpmc_config = readl(&gpmc_cfg->config); + /* Disable Write protect */ + gpmc_config |= 0x10; + writel(gpmc_config, &gpmc_cfg->config); + + nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; + nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; + nand->priv = &bch_priv; + nand->cmd_ctrl = omap_nand_hwcontrol; + nand->options |= NAND_NO_PADDING | NAND_CACHEPRG; + /* If we are 16 bit dev, our gpmc config tells us that */ + if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000) + nand->options |= NAND_BUSWIDTH_16; + + nand->chip_delay = 100; + nand->ecc.layout = &omap_ecclayout; + + /* select ECC scheme */ +#if defined(CONFIG_NAND_OMAP_ECCSCHEME) + err = omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME, + CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE); +#else + /* pagesize and oobsize are not required to configure sw ecc-scheme */ + err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW, + 0, 0); +#endif + if (err) + return err; + +#ifdef CONFIG_SPL_BUILD + if (nand->options & NAND_BUSWIDTH_16) + nand->read_buf = nand_read_buf16; + else + nand->read_buf = nand_read_buf; + nand->dev_ready = omap_spl_dev_ready; +#endif + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/s3c2410_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/s3c2410_nand.c new file mode 100644 index 000000000..db87d0726 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/s3c2410_nand.c @@ -0,0 +1,175 @@ +/* + * (C) Copyright 2006 OpenMoko, Inc. + * Author: Harald Welte + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include +#include +#include + +#define S3C2410_NFCONF_EN (1<<15) +#define S3C2410_NFCONF_512BYTE (1<<14) +#define S3C2410_NFCONF_4STEP (1<<13) +#define S3C2410_NFCONF_INITECC (1<<12) +#define S3C2410_NFCONF_nFCE (1<<11) +#define S3C2410_NFCONF_TACLS(x) ((x)<<8) +#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4) +#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0) + +#define S3C2410_ADDR_NALE 4 +#define S3C2410_ADDR_NCLE 8 + +#ifdef CONFIG_NAND_SPL + +/* in the early stage of NAND flash booting, printf() is not available */ +#define printf(fmt, args...) + +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = readb(this->IO_ADDR_R); +} +#endif + +static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + struct s3c2410_nand *nand = s3c2410_get_base_nand(); + + debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl); + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong)nand; + + if (!(ctrl & NAND_CLE)) + IO_ADDR_W |= S3C2410_ADDR_NCLE; + if (!(ctrl & NAND_ALE)) + IO_ADDR_W |= S3C2410_ADDR_NALE; + + chip->IO_ADDR_W = (void *)IO_ADDR_W; + + if (ctrl & NAND_NCE) + writel(readl(&nand->nfconf) & ~S3C2410_NFCONF_nFCE, + &nand->nfconf); + else + writel(readl(&nand->nfconf) | S3C2410_NFCONF_nFCE, + &nand->nfconf); + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); +} + +static int s3c2410_dev_ready(struct mtd_info *mtd) +{ + struct s3c2410_nand *nand = s3c2410_get_base_nand(); + debug("dev_ready\n"); + return readl(&nand->nfstat) & 0x01; +} + +#ifdef CONFIG_S3C2410_NAND_HWECC +void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand *nand = s3c2410_get_base_nand(); + debug("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode); + writel(readl(&nand->nfconf) | S3C2410_NFCONF_INITECC, &nand->nfconf); +} + +static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct s3c2410_nand *nand = s3c2410_get_base_nand(); + ecc_code[0] = readb(&nand->nfecc); + ecc_code[1] = readb(&nand->nfecc + 1); + ecc_code[2] = readb(&nand->nfecc + 2); + debug("s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n", + mtd , ecc_code[0], ecc_code[1], ecc_code[2]); + + return 0; +} + +static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + if (read_ecc[0] == calc_ecc[0] && + read_ecc[1] == calc_ecc[1] && + read_ecc[2] == calc_ecc[2]) + return 0; + + printf("s3c2410_nand_correct_data: not implemented\n"); + return -1; +} +#endif + +int board_nand_init(struct nand_chip *nand) +{ + u_int32_t cfg; + u_int8_t tacls, twrph0, twrph1; + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); + struct s3c2410_nand *nand_reg = s3c2410_get_base_nand(); + + debug("board_nand_init()\n"); + + writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon); + + /* initialize hardware */ +#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING) + tacls = CONFIG_S3C24XX_TACLS; + twrph0 = CONFIG_S3C24XX_TWRPH0; + twrph1 = CONFIG_S3C24XX_TWRPH1; +#else + tacls = 4; + twrph0 = 8; + twrph1 = 8; +#endif + + cfg = S3C2410_NFCONF_EN; + cfg |= S3C2410_NFCONF_TACLS(tacls - 1); + cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); + cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); + writel(cfg, &nand_reg->nfconf); + + /* initialize nand_chip data structure */ + nand->IO_ADDR_R = (void *)&nand_reg->nfdata; + nand->IO_ADDR_W = (void *)&nand_reg->nfdata; + + nand->select_chip = NULL; + + /* read_buf and write_buf are default */ + /* read_byte and write_byte are default */ +#ifdef CONFIG_NAND_SPL + nand->read_buf = nand_read_buf; +#endif + + /* hwcontrol always must be implemented */ + nand->cmd_ctrl = s3c2410_hwcontrol; + + nand->dev_ready = s3c2410_dev_ready; + +#ifdef CONFIG_S3C2410_NAND_HWECC + nand->ecc.hwctl = s3c2410_nand_enable_hwecc; + nand->ecc.calculate = s3c2410_nand_calculate_ecc; + nand->ecc.correct = s3c2410_nand_correct_data; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; + nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; + nand->ecc.strength = 1; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif + +#ifdef CONFIG_S3C2410_NAND_BBT + nand->bbt_options |= NAND_BBT_USE_FLASH; +#endif + + debug("end of nand_init\n"); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.c b/qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.c new file mode 100644 index 000000000..163cf29a3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.c @@ -0,0 +1,1041 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2011 NVIDIA Corporation + * (C) Copyright 2006 Detlev Zundel, dzu@denx.de + * (C) Copyright 2006 DENX Software Engineering + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tegra_nand.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define NAND_CMD_TIMEOUT_MS 10 + +#define SKIPPED_SPARE_BYTES 4 + +/* ECC bytes to be generated for tag data */ +#define TAG_ECC_BYTES 4 + +/* 64 byte oob block info for large page (== 2KB) device + * + * OOB flash layout for Tegra with Reed-Solomon 4 symbol correct ECC: + * Skipped bytes(4) + * Main area Ecc(36) + * Tag data(20) + * Tag data Ecc(4) + * + * Yaffs2 will use 16 tag bytes. + */ +static struct nand_ecclayout eccoob = { + .eccbytes = 36, + .eccpos = { + 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, + }, + .oobavail = 20, + .oobfree = { + { + .offset = 40, + .length = 20, + }, + } +}; + +enum { + ECC_OK, + ECC_TAG_ERROR = 1 << 0, + ECC_DATA_ERROR = 1 << 1 +}; + +/* Timing parameters */ +enum { + FDT_NAND_MAX_TRP_TREA, + FDT_NAND_TWB, + FDT_NAND_MAX_TCR_TAR_TRR, + FDT_NAND_TWHR, + FDT_NAND_MAX_TCS_TCH_TALS_TALH, + FDT_NAND_TWH, + FDT_NAND_TWP, + FDT_NAND_TRH, + FDT_NAND_TADL, + + FDT_NAND_TIMING_COUNT +}; + +/* Information about an attached NAND chip */ +struct fdt_nand { + struct nand_ctlr *reg; + int enabled; /* 1 to enable, 0 to disable */ + struct fdt_gpio_state wp_gpio; /* write-protect GPIO */ + s32 width; /* bit width, normally 8 */ + u32 timing[FDT_NAND_TIMING_COUNT]; +}; + +struct nand_drv { + struct nand_ctlr *reg; + + /* + * When running in PIO mode to get READ ID bytes from register + * RESP_0, we need this variable as an index to know which byte in + * register RESP_0 should be read. + * Because common code in nand_base.c invokes read_byte function two + * times for NAND_CMD_READID. + * And our controller returns 4 bytes at once in register RESP_0. + */ + int pio_byte_index; + struct fdt_nand config; +}; + +static struct nand_drv nand_ctrl; +static struct mtd_info *our_mtd; +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; + +#ifdef CONFIG_SYS_DCACHE_OFF +static inline void dma_prepare(void *start, unsigned long length, + int is_writing) +{ +} +#else +/** + * Prepare for a DMA transaction + * + * For a write we flush out our data. For a read we invalidate, since we + * need to do this before we read from the buffer after the DMA has + * completed, so may as well do it now. + * + * @param start Start address for DMA buffer (should be cache-aligned) + * @param length Length of DMA buffer in bytes + * @param is_writing 0 if reading, non-zero if writing + */ +static void dma_prepare(void *start, unsigned long length, int is_writing) +{ + unsigned long addr = (unsigned long)start; + + length = ALIGN(length, ARCH_DMA_MINALIGN); + if (is_writing) + flush_dcache_range(addr, addr + length); + else + invalidate_dcache_range(addr, addr + length); +} +#endif + +/** + * Wait for command completion + * + * @param reg nand_ctlr structure + * @return + * 1 - Command completed + * 0 - Timeout + */ +static int nand_waitfor_cmd_completion(struct nand_ctlr *reg) +{ + u32 reg_val; + int running; + int i; + + for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) { + if ((readl(®->command) & CMD_GO) || + !(readl(®->status) & STATUS_RBSY0) || + !(readl(®->isr) & ISR_IS_CMD_DONE)) { + udelay(1); + continue; + } + reg_val = readl(®->dma_mst_ctrl); + /* + * If DMA_MST_CTRL_EN_A_ENABLE or DMA_MST_CTRL_EN_B_ENABLE + * is set, that means DMA engine is running. + * + * Then we have to wait until DMA_MST_CTRL_IS_DMA_DONE + * is cleared, indicating DMA transfer completion. + */ + running = reg_val & (DMA_MST_CTRL_EN_A_ENABLE | + DMA_MST_CTRL_EN_B_ENABLE); + if (!running || (reg_val & DMA_MST_CTRL_IS_DMA_DONE)) + return 1; + udelay(1); + } + return 0; +} + +/** + * Read one byte from the chip + * + * @param mtd MTD device structure + * @return data byte + * + * Read function for 8bit bus-width + */ +static uint8_t read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + u32 dword_read; + struct nand_drv *info; + + info = (struct nand_drv *)chip->priv; + + /* In PIO mode, only 4 bytes can be transferred with single CMD_GO. */ + if (info->pio_byte_index > 3) { + info->pio_byte_index = 0; + writel(CMD_GO | CMD_PIO + | CMD_RX | CMD_CE0, + &info->reg->command); + if (!nand_waitfor_cmd_completion(info->reg)) + printf("Command timeout\n"); + } + + dword_read = readl(&info->reg->resp); + dword_read = dword_read >> (8 * info->pio_byte_index); + info->pio_byte_index++; + return (uint8_t)dword_read; +} + +/** + * Read len bytes from the chip into a buffer + * + * @param mtd MTD device structure + * @param buf buffer to store data to + * @param len number of bytes to read + * + * Read function for 8bit bus-width + */ +static void read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i, s; + unsigned int reg; + struct nand_chip *chip = mtd->priv; + struct nand_drv *info = (struct nand_drv *)chip->priv; + + for (i = 0; i < len; i += 4) { + s = (len - i) > 4 ? 4 : len - i; + writel(CMD_PIO | CMD_RX | CMD_A_VALID | CMD_CE0 | + ((s - 1) << CMD_TRANS_SIZE_SHIFT) | CMD_GO, + &info->reg->command); + if (!nand_waitfor_cmd_completion(info->reg)) + puts("Command timeout during read_buf\n"); + reg = readl(&info->reg->resp); + memcpy(buf + i, ®, s); + } +} + +/** + * Check NAND status to see if it is ready or not + * + * @param mtd MTD device structure + * @return + * 1 - ready + * 0 - not ready + */ +static int nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + int reg_val; + struct nand_drv *info; + + info = (struct nand_drv *)chip->priv; + + reg_val = readl(&info->reg->status); + if (reg_val & STATUS_RBSY0) + return 1; + else + return 0; +} + +/* Dummy implementation: we don't support multiple chips */ +static void nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + switch (chipnr) { + case -1: + case 0: + break; + + default: + BUG(); + } +} + +/** + * Clear all interrupt status bits + * + * @param reg nand_ctlr structure + */ +static void nand_clear_interrupt_status(struct nand_ctlr *reg) +{ + u32 reg_val; + + /* Clear interrupt status */ + reg_val = readl(®->isr); + writel(reg_val, ®->isr); +} + +/** + * Send command to NAND device + * + * @param mtd MTD device structure + * @param command the command to be sent + * @param column the column address for this command, -1 if none + * @param page_addr the page address for this command, -1 if none + */ +static void nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct nand_drv *info; + + info = (struct nand_drv *)chip->priv; + + /* + * Write out the command to the device. + * + * Only command NAND_CMD_RESET or NAND_CMD_READID will come + * here before mtd->writesize is initialized. + */ + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + assert(mtd->writesize != 0); + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Adjust columns for 16 bit bus-width */ + if (column != -1 && (chip->options & NAND_BUSWIDTH_16)) + column >>= 1; + + nand_clear_interrupt_status(info->reg); + + /* Stop DMA engine, clear DMA completion status */ + writel(DMA_MST_CTRL_EN_A_DISABLE + | DMA_MST_CTRL_EN_B_DISABLE + | DMA_MST_CTRL_IS_DMA_DONE, + &info->reg->dma_mst_ctrl); + + /* + * Program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + case NAND_CMD_READID: + writel(NAND_CMD_READID, &info->reg->cmd_reg1); + writel(column & 0xFF, &info->reg->addr_reg1); + writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_PIO + | CMD_RX | + ((4 - 1) << CMD_TRANS_SIZE_SHIFT) + | CMD_CE0, + &info->reg->command); + info->pio_byte_index = 0; + break; + case NAND_CMD_PARAM: + writel(NAND_CMD_PARAM, &info->reg->cmd_reg1); + writel(column & 0xFF, &info->reg->addr_reg1); + writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_CE0, + &info->reg->command); + break; + case NAND_CMD_READ0: + writel(NAND_CMD_READ0, &info->reg->cmd_reg1); + writel(NAND_CMD_READSTART, &info->reg->cmd_reg2); + writel((page_addr << 16) | (column & 0xFFFF), + &info->reg->addr_reg1); + writel(page_addr >> 16, &info->reg->addr_reg2); + return; + case NAND_CMD_SEQIN: + writel(NAND_CMD_SEQIN, &info->reg->cmd_reg1); + writel(NAND_CMD_PAGEPROG, &info->reg->cmd_reg2); + writel((page_addr << 16) | (column & 0xFFFF), + &info->reg->addr_reg1); + writel(page_addr >> 16, + &info->reg->addr_reg2); + return; + case NAND_CMD_PAGEPROG: + return; + case NAND_CMD_ERASE1: + writel(NAND_CMD_ERASE1, &info->reg->cmd_reg1); + writel(NAND_CMD_ERASE2, &info->reg->cmd_reg2); + writel(page_addr, &info->reg->addr_reg1); + writel(CMD_GO | CMD_CLE | CMD_ALE | + CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3, + &info->reg->command); + break; + case NAND_CMD_ERASE2: + return; + case NAND_CMD_STATUS: + writel(NAND_CMD_STATUS, &info->reg->cmd_reg1); + writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX + | ((1 - 0) << CMD_TRANS_SIZE_SHIFT) + | CMD_CE0, + &info->reg->command); + info->pio_byte_index = 0; + break; + case NAND_CMD_RESET: + writel(NAND_CMD_RESET, &info->reg->cmd_reg1); + writel(CMD_GO | CMD_CLE | CMD_CE0, + &info->reg->command); + break; + case NAND_CMD_RNDOUT: + default: + printf("%s: Unsupported command %d\n", __func__, command); + return; + } + if (!nand_waitfor_cmd_completion(info->reg)) + printf("Command 0x%02X timeout\n", command); +} + +/** + * Check whether the pointed buffer are all 0xff (blank). + * + * @param buf data buffer for blank check + * @param len length of the buffer in byte + * @return + * 1 - blank + * 0 - non-blank + */ +static int blank_check(u8 *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + if (buf[i] != 0xFF) + return 0; + return 1; +} + +/** + * After a DMA transfer for read, we call this function to see whether there + * is any uncorrectable error on the pointed data buffer or oob buffer. + * + * @param reg nand_ctlr structure + * @param databuf data buffer + * @param a_len data buffer length + * @param oobbuf oob buffer + * @param b_len oob buffer length + * @return + * ECC_OK - no ECC error or correctable ECC error + * ECC_TAG_ERROR - uncorrectable tag ECC error + * ECC_DATA_ERROR - uncorrectable data ECC error + * ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error + */ +static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf, + int a_len, u8 *oobbuf, int b_len) +{ + int return_val = ECC_OK; + u32 reg_val; + + if (!(readl(®->isr) & ISR_IS_ECC_ERR)) + return ECC_OK; + + /* + * Area A is used for the data block (databuf). Area B is used for + * the spare block (oobbuf) + */ + reg_val = readl(®->dec_status); + if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) { + reg_val = readl(®->bch_dec_status_buf); + /* + * If uncorrectable error occurs on data area, then see whether + * they are all FF. If all are FF, it's a blank page. + * Not error. + */ + if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) && + !blank_check(databuf, a_len)) + return_val |= ECC_DATA_ERROR; + } + + if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) { + reg_val = readl(®->bch_dec_status_buf); + /* + * If uncorrectable error occurs on tag area, then see whether + * they are all FF. If all are FF, it's a blank page. + * Not error. + */ + if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) && + !blank_check(oobbuf, b_len)) + return_val |= ECC_TAG_ERROR; + } + + return return_val; +} + +/** + * Set GO bit to send command to device + * + * @param reg nand_ctlr structure + */ +static void start_command(struct nand_ctlr *reg) +{ + u32 reg_val; + + reg_val = readl(®->command); + reg_val |= CMD_GO; + writel(reg_val, ®->command); +} + +/** + * Clear command GO bit, DMA GO bit, and DMA completion status + * + * @param reg nand_ctlr structure + */ +static void stop_command(struct nand_ctlr *reg) +{ + /* Stop command */ + writel(0, ®->command); + + /* Stop DMA engine and clear DMA completion status */ + writel(DMA_MST_CTRL_GO_DISABLE + | DMA_MST_CTRL_IS_DMA_DONE, + ®->dma_mst_ctrl); +} + +/** + * Set up NAND bus width and page size + * + * @param info nand_info structure + * @param *reg_val address of reg_val + * @return 0 if ok, -1 on error + */ +static int set_bus_width_page_size(struct fdt_nand *config, + u32 *reg_val) +{ + if (config->width == 8) + *reg_val = CFG_BUS_WIDTH_8BIT; + else if (config->width == 16) + *reg_val = CFG_BUS_WIDTH_16BIT; + else { + debug("%s: Unsupported bus width %d\n", __func__, + config->width); + return -1; + } + + if (our_mtd->writesize == 512) + *reg_val |= CFG_PAGE_SIZE_512; + else if (our_mtd->writesize == 2048) + *reg_val |= CFG_PAGE_SIZE_2048; + else if (our_mtd->writesize == 4096) + *reg_val |= CFG_PAGE_SIZE_4096; + else { + debug("%s: Unsupported page size %d\n", __func__, + our_mtd->writesize); + return -1; + } + + return 0; +} + +/** + * Page read/write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf data buffer + * @param page page number + * @param with_ecc 1 to enable ECC, 0 to disable ECC + * @param is_writing 0 for read, 1 for write + * @return 0 when successfully completed + * -EIO when command timeout + */ +static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page, int with_ecc, int is_writing) +{ + u32 reg_val; + int tag_size; + struct nand_oobfree *free = chip->ecc.layout->oobfree; + /* 4*128=512 (byte) is the value that our HW can support. */ + ALLOC_CACHE_ALIGN_BUFFER(u32, tag_buf, 128); + char *tag_ptr; + struct nand_drv *info; + struct fdt_nand *config; + + if ((uintptr_t)buf & 0x03) { + printf("buf %p has to be 4-byte aligned\n", buf); + return -EINVAL; + } + + info = (struct nand_drv *)chip->priv; + config = &info->config; + if (set_bus_width_page_size(config, ®_val)) + return -EINVAL; + + /* Need to be 4-byte aligned */ + tag_ptr = (char *)tag_buf; + + stop_command(info->reg); + + writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a); + writel(virt_to_phys(buf), &info->reg->data_block_ptr); + + if (with_ecc) { + writel(virt_to_phys(tag_ptr), &info->reg->tag_ptr); + if (is_writing) + memcpy(tag_ptr, chip->oob_poi + free->offset, + chip->ecc.layout->oobavail + + TAG_ECC_BYTES); + } else { + writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr); + } + + /* Set ECC selection, configure ECC settings */ + if (with_ecc) { + tag_size = chip->ecc.layout->oobavail + TAG_ECC_BYTES; + reg_val |= (CFG_SKIP_SPARE_SEL_4 + | CFG_SKIP_SPARE_ENABLE + | CFG_HW_ECC_CORRECTION_ENABLE + | CFG_ECC_EN_TAG_DISABLE + | CFG_HW_ECC_SEL_RS + | CFG_HW_ECC_ENABLE + | CFG_TVAL4 + | (tag_size - 1)); + + if (!is_writing) + tag_size += SKIPPED_SPARE_BYTES; + dma_prepare(tag_ptr, tag_size, is_writing); + } else { + tag_size = mtd->oobsize; + reg_val |= (CFG_SKIP_SPARE_DISABLE + | CFG_HW_ECC_CORRECTION_DISABLE + | CFG_ECC_EN_TAG_DISABLE + | CFG_HW_ECC_DISABLE + | (tag_size - 1)); + dma_prepare(chip->oob_poi, tag_size, is_writing); + } + writel(reg_val, &info->reg->config); + + dma_prepare(buf, 1 << chip->page_shift, is_writing); + + writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config); + + writel(tag_size - 1, &info->reg->dma_cfg_b); + + nand_clear_interrupt_status(info->reg); + + reg_val = CMD_CLE | CMD_ALE + | CMD_SEC_CMD + | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) + | CMD_A_VALID + | CMD_B_VALID + | (CMD_TRANS_SIZE_PAGE << CMD_TRANS_SIZE_SHIFT) + | CMD_CE0; + if (!is_writing) + reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); + else + reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); + writel(reg_val, &info->reg->command); + + /* Setup DMA engine */ + reg_val = DMA_MST_CTRL_GO_ENABLE + | DMA_MST_CTRL_BURST_8WORDS + | DMA_MST_CTRL_EN_A_ENABLE + | DMA_MST_CTRL_EN_B_ENABLE; + + if (!is_writing) + reg_val |= DMA_MST_CTRL_DIR_READ; + else + reg_val |= DMA_MST_CTRL_DIR_WRITE; + + writel(reg_val, &info->reg->dma_mst_ctrl); + + start_command(info->reg); + + if (!nand_waitfor_cmd_completion(info->reg)) { + if (!is_writing) + printf("Read Page 0x%X timeout ", page); + else + printf("Write Page 0x%X timeout ", page); + if (with_ecc) + printf("with ECC"); + else + printf("without ECC"); + printf("\n"); + return -EIO; + } + + if (with_ecc && !is_writing) { + memcpy(chip->oob_poi, tag_ptr, + SKIPPED_SPARE_BYTES); + memcpy(chip->oob_poi + free->offset, + tag_ptr + SKIPPED_SPARE_BYTES, + chip->ecc.layout->oobavail); + reg_val = (u32)check_ecc_error(info->reg, (u8 *)buf, + 1 << chip->page_shift, + (u8 *)(tag_ptr + SKIPPED_SPARE_BYTES), + chip->ecc.layout->oobavail); + if (reg_val & ECC_TAG_ERROR) + printf("Read Page 0x%X tag ECC error\n", page); + if (reg_val & ECC_DATA_ERROR) + printf("Read Page 0x%X data ECC error\n", + page); + if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR)) + return -EIO; + } + return 0; +} + +/** + * Hardware ecc based page read function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf buffer to store read data + * @param page page number to read + * @return 0 when successfully completed + * -EIO when command timeout + */ +static int nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + return nand_rw_page(mtd, chip, buf, page, 1, 0); +} + +/** + * Hardware ecc based page write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf data buffer + */ +static int nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required) +{ + int page; + struct nand_drv *info; + + info = (struct nand_drv *)chip->priv; + + page = (readl(&info->reg->addr_reg1) >> 16) | + (readl(&info->reg->addr_reg2) << 16); + + nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1); + return 0; +} + + +/** + * Read raw page data without ecc + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf buffer to store read data + * @param page page number to read + * @return 0 when successfully completed + * -EINVAL when chip->oob_poi is not double-word aligned + * -EIO when command timeout + */ +static int nand_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + return nand_rw_page(mtd, chip, buf, page, 0, 0); +} + +/** + * Raw page write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf data buffer + */ +static int nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required) +{ + int page; + struct nand_drv *info; + + info = (struct nand_drv *)chip->priv; + page = (readl(&info->reg->addr_reg1) >> 16) | + (readl(&info->reg->addr_reg2) << 16); + + nand_rw_page(mtd, chip, (uint8_t *)buf, page, 0, 1); + return 0; +} + +/** + * OOB data read/write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param page page number to read + * @param with_ecc 1 to enable ECC, 0 to disable ECC + * @param is_writing 0 for read, 1 for write + * @return 0 when successfully completed + * -EINVAL when chip->oob_poi is not double-word aligned + * -EIO when command timeout + */ +static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int with_ecc, int is_writing) +{ + u32 reg_val; + int tag_size; + struct nand_oobfree *free = chip->ecc.layout->oobfree; + struct nand_drv *info; + + if (((int)chip->oob_poi) & 0x03) + return -EINVAL; + info = (struct nand_drv *)chip->priv; + if (set_bus_width_page_size(&info->config, ®_val)) + return -EINVAL; + + stop_command(info->reg); + + writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr); + + /* Set ECC selection */ + tag_size = mtd->oobsize; + if (with_ecc) + reg_val |= CFG_ECC_EN_TAG_ENABLE; + else + reg_val |= (CFG_ECC_EN_TAG_DISABLE); + + reg_val |= ((tag_size - 1) | + CFG_SKIP_SPARE_DISABLE | + CFG_HW_ECC_CORRECTION_DISABLE | + CFG_HW_ECC_DISABLE); + writel(reg_val, &info->reg->config); + + dma_prepare(chip->oob_poi, tag_size, is_writing); + + writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config); + + if (is_writing && with_ecc) + tag_size -= TAG_ECC_BYTES; + + writel(tag_size - 1, &info->reg->dma_cfg_b); + + nand_clear_interrupt_status(info->reg); + + reg_val = CMD_CLE | CMD_ALE + | CMD_SEC_CMD + | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) + | CMD_B_VALID + | CMD_CE0; + if (!is_writing) + reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); + else + reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); + writel(reg_val, &info->reg->command); + + /* Setup DMA engine */ + reg_val = DMA_MST_CTRL_GO_ENABLE + | DMA_MST_CTRL_BURST_8WORDS + | DMA_MST_CTRL_EN_B_ENABLE; + if (!is_writing) + reg_val |= DMA_MST_CTRL_DIR_READ; + else + reg_val |= DMA_MST_CTRL_DIR_WRITE; + + writel(reg_val, &info->reg->dma_mst_ctrl); + + start_command(info->reg); + + if (!nand_waitfor_cmd_completion(info->reg)) { + if (!is_writing) + printf("Read OOB of Page 0x%X timeout\n", page); + else + printf("Write OOB of Page 0x%X timeout\n", page); + return -EIO; + } + + if (with_ecc && !is_writing) { + reg_val = (u32)check_ecc_error(info->reg, 0, 0, + (u8 *)(chip->oob_poi + free->offset), + chip->ecc.layout->oobavail); + if (reg_val & ECC_TAG_ERROR) + printf("Read OOB of Page 0x%X tag ECC error\n", page); + } + return 0; +} + +/** + * OOB data read function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param page page number to read + */ +static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + nand_rw_oob(mtd, chip, page, 0, 0); + return 0; +} + +/** + * OOB data write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param page page number to write + * @return 0 when successfully completed + * -EINVAL when chip->oob_poi is not double-word aligned + * -EIO when command timeout + */ +static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + + return nand_rw_oob(mtd, chip, page, 0, 1); +} + +/** + * Set up NAND memory timings according to the provided parameters + * + * @param timing Timing parameters + * @param reg NAND controller register address + */ +static void setup_timing(unsigned timing[FDT_NAND_TIMING_COUNT], + struct nand_ctlr *reg) +{ + u32 reg_val, clk_rate, clk_period, time_val; + + clk_rate = (u32)clock_get_periph_rate(PERIPH_ID_NDFLASH, + CLOCK_ID_PERIPH) / 1000000; + clk_period = 1000 / clk_rate; + reg_val = ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) << + TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TWB] / clk_period) << + TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK; + time_val = timing[FDT_NAND_MAX_TCR_TAR_TRR] / clk_period; + if (time_val > 2) + reg_val |= ((time_val - 2) << TIMING_TCR_TAR_TRR_CNT_SHIFT) & + TIMING_TCR_TAR_TRR_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TWHR] / clk_period) << + TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK; + time_val = timing[FDT_NAND_MAX_TCS_TCH_TALS_TALH] / clk_period; + if (time_val > 1) + reg_val |= ((time_val - 1) << TIMING_TCS_CNT_SHIFT) & + TIMING_TCS_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TWH] / clk_period) << + TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TWP] / clk_period) << + TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TRH] / clk_period) << + TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK; + reg_val |= ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) << + TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK; + writel(reg_val, ®->timing); + + reg_val = 0; + time_val = timing[FDT_NAND_TADL] / clk_period; + if (time_val > 2) + reg_val = (time_val - 2) & TIMING2_TADL_CNT_MASK; + writel(reg_val, ®->timing2); +} + +/** + * Decode NAND parameters from the device tree + * + * @param blob Device tree blob + * @param node Node containing "nand-flash" compatble node + * @return 0 if ok, -ve on error (FDT_ERR_...) + */ +static int fdt_decode_nand(const void *blob, int node, struct fdt_nand *config) +{ + int err; + + config->reg = (struct nand_ctlr *)fdtdec_get_addr(blob, node, "reg"); + config->enabled = fdtdec_get_is_enabled(blob, node); + config->width = fdtdec_get_int(blob, node, "nvidia,nand-width", 8); + err = fdtdec_decode_gpio(blob, node, "nvidia,wp-gpios", + &config->wp_gpio); + if (err) + return err; + err = fdtdec_get_int_array(blob, node, "nvidia,timing", + config->timing, FDT_NAND_TIMING_COUNT); + if (err < 0) + return err; + + /* Now look up the controller and decode that */ + node = fdt_next_node(blob, node, NULL); + if (node < 0) + return node; + + return 0; +} + +/** + * Board-specific NAND initialization + * + * @param nand nand chip info structure + * @return 0, after initialized, -1 on error + */ +int tegra_nand_init(struct nand_chip *nand, int devnum) +{ + struct nand_drv *info = &nand_ctrl; + struct fdt_nand *config = &info->config; + int node, ret; + + node = fdtdec_next_compatible(gd->fdt_blob, 0, + COMPAT_NVIDIA_TEGRA20_NAND); + if (node < 0) + return -1; + if (fdt_decode_nand(gd->fdt_blob, node, config)) { + printf("Could not decode nand-flash in device tree\n"); + return -1; + } + if (!config->enabled) + return -1; + info->reg = config->reg; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &eccoob; + + nand->options = LP_OPTIONS; + nand->cmdfunc = nand_command; + nand->read_byte = read_byte; + nand->read_buf = read_buf; + nand->ecc.read_page = nand_read_page_hwecc; + nand->ecc.write_page = nand_write_page_hwecc; + nand->ecc.read_page_raw = nand_read_page_raw; + nand->ecc.write_page_raw = nand_write_page_raw; + nand->ecc.read_oob = nand_read_oob; + nand->ecc.write_oob = nand_write_oob; + nand->ecc.strength = 1; + nand->select_chip = nand_select_chip; + nand->dev_ready = nand_dev_ready; + nand->priv = &nand_ctrl; + + /* Adjust controller clock rate */ + clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, 52000000); + + /* Adjust timing for NAND device */ + setup_timing(config->timing, info->reg); + + fdtdec_setup_gpio(&config->wp_gpio); + gpio_direction_output(config->wp_gpio.gpio, 1); + + our_mtd = &nand_info[devnum]; + our_mtd->priv = nand; + ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL); + if (ret) + return ret; + + nand->ecc.size = our_mtd->writesize; + nand->ecc.bytes = our_mtd->oobsize; + + ret = nand_scan_tail(our_mtd); + if (ret) + return ret; + + ret = nand_register(devnum); + if (ret) + return ret; + + return 0; +} + +void board_nand_init(void) +{ + struct nand_chip *nand = &nand_chip[0]; + + if (tegra_nand_init(nand, 0)) + puts("Tegra NAND init failed\n"); +} diff --git a/qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.h b/qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.h new file mode 100644 index 000000000..ded9d7104 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/tegra_nand.h @@ -0,0 +1,241 @@ +/* + * (C) Copyright 2011 NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* register offset */ +#define COMMAND_0 0x00 +#define CMD_GO (1 << 31) +#define CMD_CLE (1 << 30) +#define CMD_ALE (1 << 29) +#define CMD_PIO (1 << 28) +#define CMD_TX (1 << 27) +#define CMD_RX (1 << 26) +#define CMD_SEC_CMD (1 << 25) +#define CMD_AFT_DAT_MASK (1 << 24) +#define CMD_AFT_DAT_DISABLE 0 +#define CMD_AFT_DAT_ENABLE (1 << 24) +#define CMD_TRANS_SIZE_SHIFT 20 +#define CMD_TRANS_SIZE_PAGE 8 +#define CMD_A_VALID (1 << 19) +#define CMD_B_VALID (1 << 18) +#define CMD_RD_STATUS_CHK (1 << 17) +#define CMD_R_BSY_CHK (1 << 16) +#define CMD_CE7 (1 << 15) +#define CMD_CE6 (1 << 14) +#define CMD_CE5 (1 << 13) +#define CMD_CE4 (1 << 12) +#define CMD_CE3 (1 << 11) +#define CMD_CE2 (1 << 10) +#define CMD_CE1 (1 << 9) +#define CMD_CE0 (1 << 8) +#define CMD_CLE_BYTE_SIZE_SHIFT 4 +enum { + CMD_CLE_BYTES1 = 0, + CMD_CLE_BYTES2, + CMD_CLE_BYTES3, + CMD_CLE_BYTES4, +}; +#define CMD_ALE_BYTE_SIZE_SHIFT 0 +enum { + CMD_ALE_BYTES1 = 0, + CMD_ALE_BYTES2, + CMD_ALE_BYTES3, + CMD_ALE_BYTES4, + CMD_ALE_BYTES5, + CMD_ALE_BYTES6, + CMD_ALE_BYTES7, + CMD_ALE_BYTES8 +}; + +#define STATUS_0 0x04 +#define STATUS_RBSY0 (1 << 8) + +#define ISR_0 0x08 +#define ISR_IS_CMD_DONE (1 << 5) +#define ISR_IS_ECC_ERR (1 << 4) + +#define IER_0 0x0C + +#define CFG_0 0x10 +#define CFG_HW_ECC_MASK (1 << 31) +#define CFG_HW_ECC_DISABLE 0 +#define CFG_HW_ECC_ENABLE (1 << 31) +#define CFG_HW_ECC_SEL_MASK (1 << 30) +#define CFG_HW_ECC_SEL_HAMMING 0 +#define CFG_HW_ECC_SEL_RS (1 << 30) +#define CFG_HW_ECC_CORRECTION_MASK (1 << 29) +#define CFG_HW_ECC_CORRECTION_DISABLE 0 +#define CFG_HW_ECC_CORRECTION_ENABLE (1 << 29) +#define CFG_PIPELINE_EN_MASK (1 << 28) +#define CFG_PIPELINE_EN_DISABLE 0 +#define CFG_PIPELINE_EN_ENABLE (1 << 28) +#define CFG_ECC_EN_TAG_MASK (1 << 27) +#define CFG_ECC_EN_TAG_DISABLE 0 +#define CFG_ECC_EN_TAG_ENABLE (1 << 27) +#define CFG_TVALUE_MASK (3 << 24) +enum { + CFG_TVAL4 = 0 << 24, + CFG_TVAL6 = 1 << 24, + CFG_TVAL8 = 2 << 24 +}; +#define CFG_SKIP_SPARE_MASK (1 << 23) +#define CFG_SKIP_SPARE_DISABLE 0 +#define CFG_SKIP_SPARE_ENABLE (1 << 23) +#define CFG_COM_BSY_MASK (1 << 22) +#define CFG_COM_BSY_DISABLE 0 +#define CFG_COM_BSY_ENABLE (1 << 22) +#define CFG_BUS_WIDTH_MASK (1 << 21) +#define CFG_BUS_WIDTH_8BIT 0 +#define CFG_BUS_WIDTH_16BIT (1 << 21) +#define CFG_LPDDR1_MODE_MASK (1 << 20) +#define CFG_LPDDR1_MODE_DISABLE 0 +#define CFG_LPDDR1_MODE_ENABLE (1 << 20) +#define CFG_EDO_MODE_MASK (1 << 19) +#define CFG_EDO_MODE_DISABLE 0 +#define CFG_EDO_MODE_ENABLE (1 << 19) +#define CFG_PAGE_SIZE_SEL_MASK (7 << 16) +enum { + CFG_PAGE_SIZE_256 = 0 << 16, + CFG_PAGE_SIZE_512 = 1 << 16, + CFG_PAGE_SIZE_1024 = 2 << 16, + CFG_PAGE_SIZE_2048 = 3 << 16, + CFG_PAGE_SIZE_4096 = 4 << 16 +}; +#define CFG_SKIP_SPARE_SEL_MASK (3 << 14) +enum { + CFG_SKIP_SPARE_SEL_4 = 0 << 14, + CFG_SKIP_SPARE_SEL_8 = 1 << 14, + CFG_SKIP_SPARE_SEL_12 = 2 << 14, + CFG_SKIP_SPARE_SEL_16 = 3 << 14 +}; +#define CFG_TAG_BYTE_SIZE_MASK 0x1FF + +#define TIMING_0 0x14 +#define TIMING_TRP_RESP_CNT_SHIFT 28 +#define TIMING_TRP_RESP_CNT_MASK (0xf << TIMING_TRP_RESP_CNT_SHIFT) +#define TIMING_TWB_CNT_SHIFT 24 +#define TIMING_TWB_CNT_MASK (0xf << TIMING_TWB_CNT_SHIFT) +#define TIMING_TCR_TAR_TRR_CNT_SHIFT 20 +#define TIMING_TCR_TAR_TRR_CNT_MASK (0xf << TIMING_TCR_TAR_TRR_CNT_SHIFT) +#define TIMING_TWHR_CNT_SHIFT 16 +#define TIMING_TWHR_CNT_MASK (0xf << TIMING_TWHR_CNT_SHIFT) +#define TIMING_TCS_CNT_SHIFT 14 +#define TIMING_TCS_CNT_MASK (3 << TIMING_TCS_CNT_SHIFT) +#define TIMING_TWH_CNT_SHIFT 12 +#define TIMING_TWH_CNT_MASK (3 << TIMING_TWH_CNT_SHIFT) +#define TIMING_TWP_CNT_SHIFT 8 +#define TIMING_TWP_CNT_MASK (0xf << TIMING_TWP_CNT_SHIFT) +#define TIMING_TRH_CNT_SHIFT 4 +#define TIMING_TRH_CNT_MASK (3 << TIMING_TRH_CNT_SHIFT) +#define TIMING_TRP_CNT_SHIFT 0 +#define TIMING_TRP_CNT_MASK (0xf << TIMING_TRP_CNT_SHIFT) + +#define RESP_0 0x18 + +#define TIMING2_0 0x1C +#define TIMING2_TADL_CNT_SHIFT 0 +#define TIMING2_TADL_CNT_MASK (0xf << TIMING2_TADL_CNT_SHIFT) + +#define CMD_REG1_0 0x20 +#define CMD_REG2_0 0x24 +#define ADDR_REG1_0 0x28 +#define ADDR_REG2_0 0x2C + +#define DMA_MST_CTRL_0 0x30 +#define DMA_MST_CTRL_GO_MASK (1 << 31) +#define DMA_MST_CTRL_GO_DISABLE 0 +#define DMA_MST_CTRL_GO_ENABLE (1 << 31) +#define DMA_MST_CTRL_DIR_MASK (1 << 30) +#define DMA_MST_CTRL_DIR_READ 0 +#define DMA_MST_CTRL_DIR_WRITE (1 << 30) +#define DMA_MST_CTRL_PERF_EN_MASK (1 << 29) +#define DMA_MST_CTRL_PERF_EN_DISABLE 0 +#define DMA_MST_CTRL_PERF_EN_ENABLE (1 << 29) +#define DMA_MST_CTRL_REUSE_BUFFER_MASK (1 << 27) +#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE 0 +#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE (1 << 27) +#define DMA_MST_CTRL_BURST_SIZE_SHIFT 24 +#define DMA_MST_CTRL_BURST_SIZE_MASK (7 << DMA_MST_CTRL_BURST_SIZE_SHIFT) +enum { + DMA_MST_CTRL_BURST_1WORDS = 2 << DMA_MST_CTRL_BURST_SIZE_SHIFT, + DMA_MST_CTRL_BURST_4WORDS = 3 << DMA_MST_CTRL_BURST_SIZE_SHIFT, + DMA_MST_CTRL_BURST_8WORDS = 4 << DMA_MST_CTRL_BURST_SIZE_SHIFT, + DMA_MST_CTRL_BURST_16WORDS = 5 << DMA_MST_CTRL_BURST_SIZE_SHIFT +}; +#define DMA_MST_CTRL_IS_DMA_DONE (1 << 20) +#define DMA_MST_CTRL_EN_A_MASK (1 << 2) +#define DMA_MST_CTRL_EN_A_DISABLE 0 +#define DMA_MST_CTRL_EN_A_ENABLE (1 << 2) +#define DMA_MST_CTRL_EN_B_MASK (1 << 1) +#define DMA_MST_CTRL_EN_B_DISABLE 0 +#define DMA_MST_CTRL_EN_B_ENABLE (1 << 1) + +#define DMA_CFG_A_0 0x34 +#define DMA_CFG_B_0 0x38 +#define FIFO_CTRL_0 0x3C +#define DATA_BLOCK_PTR_0 0x40 +#define TAG_PTR_0 0x44 +#define ECC_PTR_0 0x48 + +#define DEC_STATUS_0 0x4C +#define DEC_STATUS_A_ECC_FAIL (1 << 1) +#define DEC_STATUS_B_ECC_FAIL (1 << 0) + +#define BCH_CONFIG_0 0xCC +#define BCH_CONFIG_BCH_TVALUE_SHIFT 4 +#define BCH_CONFIG_BCH_TVALUE_MASK (3 << BCH_CONFIG_BCH_TVALUE_SHIFT) +enum { + BCH_CONFIG_BCH_TVAL4 = 0 << BCH_CONFIG_BCH_TVALUE_SHIFT, + BCH_CONFIG_BCH_TVAL8 = 1 << BCH_CONFIG_BCH_TVALUE_SHIFT, + BCH_CONFIG_BCH_TVAL14 = 2 << BCH_CONFIG_BCH_TVALUE_SHIFT, + BCH_CONFIG_BCH_TVAL16 = 3 << BCH_CONFIG_BCH_TVALUE_SHIFT +}; +#define BCH_CONFIG_BCH_ECC_MASK (1 << 0) +#define BCH_CONFIG_BCH_ECC_DISABLE 0 +#define BCH_CONFIG_BCH_ECC_ENABLE (1 << 0) + +#define BCH_DEC_RESULT_0 0xD0 +#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK (1 << 8) +#define BCH_DEC_RESULT_PAGE_COUNT_MASK 0xFF + +#define BCH_DEC_STATUS_BUF_0 0xD4 +#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK 0xFF000000 +#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK 0x00FF0000 +#define BCH_DEC_STATUS_FAIL_TAG_MASK (1 << 14) +#define BCH_DEC_STATUS_CORR_TAG_MASK (1 << 13) +#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (0x1f << 8) +#define BCH_DEC_STATUS_PAGE_NUMBER_MASK 0xFF + +#define LP_OPTIONS 0 + +struct nand_ctlr { + u32 command; /* offset 00h */ + u32 status; /* offset 04h */ + u32 isr; /* offset 08h */ + u32 ier; /* offset 0Ch */ + u32 config; /* offset 10h */ + u32 timing; /* offset 14h */ + u32 resp; /* offset 18h */ + u32 timing2; /* offset 1Ch */ + u32 cmd_reg1; /* offset 20h */ + u32 cmd_reg2; /* offset 24h */ + u32 addr_reg1; /* offset 28h */ + u32 addr_reg2; /* offset 2Ch */ + u32 dma_mst_ctrl; /* offset 30h */ + u32 dma_cfg_a; /* offset 34h */ + u32 dma_cfg_b; /* offset 38h */ + u32 fifo_ctrl; /* offset 3Ch */ + u32 data_block_ptr; /* offset 40h */ + u32 tag_ptr; /* offset 44h */ + u32 resv1; /* offset 48h */ + u32 dec_status; /* offset 4Ch */ + u32 hwstatus_cmd; /* offset 50h */ + u32 hwstatus_mask; /* offset 54h */ + u32 resv2[29]; + u32 bch_config; /* offset CCh */ + u32 bch_dec_result; /* offset D0h */ + u32 bch_dec_status_buf; + /* offset D4h */ +}; diff --git a/qemu/roms/u-boot/drivers/mtd/onenand/Makefile b/qemu/roms/u-boot/drivers/mtd/onenand/Makefile new file mode 100644 index 000000000..b24934881 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/onenand/Makefile @@ -0,0 +1,13 @@ +# +# Copyright (C) 2005-2007 Samsung Electronics. +# Kyungmin Park +# +# SPDX-License-Identifier: GPL-2.0+ +# + +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_CMD_ONENAND) := onenand_uboot.o onenand_base.o onenand_bbt.o +obj-$(CONFIG_SAMSUNG_ONENAND) += samsung.o +else +obj-y := onenand_spl.o +endif diff --git a/qemu/roms/u-boot/drivers/mtd/onenand/onenand_base.c b/qemu/roms/u-boot/drivers/mtd/onenand/onenand_base.c new file mode 100644 index 000000000..e33e8d38e --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/onenand/onenand_base.c @@ -0,0 +1,2784 @@ +/* + * linux/drivers/mtd/onenand/onenand_base.c + * + * Copyright (C) 2005-2007 Samsung Electronics + * Kyungmin Park + * + * Credits: + * Adrian Hunter : + * auto-placement support, read-while load support, various fixes + * Copyright (C) Nokia Corporation, 2007 + * + * Rohit Hagargundgi , + * Amul Kumar Saha : + * Flex-OneNAND support + * Copyright (C) Samsung Electronics, 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include + +/* It should access 16-bit instead of 8-bit */ +static void *memcpy_16(void *dst, const void *src, unsigned int len) +{ + void *ret = dst; + short *d = dst; + const short *s = src; + + len >>= 1; + while (len-- > 0) + *d++ = *s++; + return ret; +} + +/** + * onenand_oob_128 - oob info for Flex-Onenand with 4KB page + * For now, we expose only 64 out of 80 ecc bytes + */ +static struct nand_ecclayout onenand_oob_128 = { + .eccbytes = 64, + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 102, 103, 104, 105 + }, + .oobfree = { + {2, 4}, {18, 4}, {34, 4}, {50, 4}, + {66, 4}, {82, 4}, {98, 4}, {114, 4} + } +}; + +/** + * onenand_oob_64 - oob info for large (2KB) page + */ +static struct nand_ecclayout onenand_oob_64 = { + .eccbytes = 20, + .eccpos = { + 8, 9, 10, 11, 12, + 24, 25, 26, 27, 28, + 40, 41, 42, 43, 44, + 56, 57, 58, 59, 60, + }, + .oobfree = { + {2, 3}, {14, 2}, {18, 3}, {30, 2}, + {34, 3}, {46, 2}, {50, 3}, {62, 2} + } +}; + +/** + * onenand_oob_32 - oob info for middle (1KB) page + */ +static struct nand_ecclayout onenand_oob_32 = { + .eccbytes = 10, + .eccpos = { + 8, 9, 10, 11, 12, + 24, 25, 26, 27, 28, + }, + .oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} } +}; + +/* + * Warning! This array is used with the memcpy_16() function, thus + * it must be aligned to 2 bytes. GCC can make this array unaligned + * as the array is made of unsigned char, which memcpy16() doesn't + * like and will cause unaligned access. + */ +static const unsigned char __aligned(2) ffchars[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ +}; + +/** + * onenand_readw - [OneNAND Interface] Read OneNAND register + * @param addr address to read + * + * Read OneNAND register + */ +static unsigned short onenand_readw(void __iomem * addr) +{ + return readw(addr); +} + +/** + * onenand_writew - [OneNAND Interface] Write OneNAND register with value + * @param value value to write + * @param addr address to write + * + * Write OneNAND register with value + */ +static void onenand_writew(unsigned short value, void __iomem * addr) +{ + writew(value, addr); +} + +/** + * onenand_block_address - [DEFAULT] Get block address + * @param device the device id + * @param block the block + * @return translated block address if DDP, otherwise same + * + * Setup Start Address 1 Register (F100h) + */ +static int onenand_block_address(struct onenand_chip *this, int block) +{ + /* Device Flash Core select, NAND Flash Block Address */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); + + return block; +} + +/** + * onenand_bufferram_address - [DEFAULT] Get bufferram address + * @param device the device id + * @param block the block + * @return set DBS value if DDP, otherwise 0 + * + * Setup Start Address 2 Register (F101h) for DDP + */ +static int onenand_bufferram_address(struct onenand_chip *this, int block) +{ + /* Device BufferRAM Select */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1; + + return ONENAND_DDP_CHIP0; +} + +/** + * onenand_page_address - [DEFAULT] Get page address + * @param page the page address + * @param sector the sector address + * @return combined page and sector address + * + * Setup Start Address 8 Register (F107h) + */ +static int onenand_page_address(int page, int sector) +{ + /* Flash Page Address, Flash Sector Address */ + int fpa, fsa; + + fpa = page & ONENAND_FPA_MASK; + fsa = sector & ONENAND_FSA_MASK; + + return ((fpa << ONENAND_FPA_SHIFT) | fsa); +} + +/** + * onenand_buffer_address - [DEFAULT] Get buffer address + * @param dataram1 DataRAM index + * @param sectors the sector address + * @param count the number of sectors + * @return the start buffer value + * + * Setup Start Buffer Register (F200h) + */ +static int onenand_buffer_address(int dataram1, int sectors, int count) +{ + int bsa, bsc; + + /* BufferRAM Sector Address */ + bsa = sectors & ONENAND_BSA_MASK; + + if (dataram1) + bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ + else + bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ + + /* BufferRAM Sector Count */ + bsc = count & ONENAND_BSC_MASK; + + return ((bsa << ONENAND_BSA_SHIFT) | bsc); +} + +/** + * flexonenand_block - Return block number for flash address + * @param this - OneNAND device structure + * @param addr - Address for which block number is needed + */ +static unsigned int flexonenand_block(struct onenand_chip *this, loff_t addr) +{ + unsigned int boundary, blk, die = 0; + + if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) { + die = 1; + addr -= this->diesize[0]; + } + + boundary = this->boundary[die]; + + blk = addr >> (this->erase_shift - 1); + if (blk > boundary) + blk = (blk + boundary + 1) >> 1; + + blk += die ? this->density_mask : 0; + return blk; +} + +unsigned int onenand_block(struct onenand_chip *this, loff_t addr) +{ + if (!FLEXONENAND(this)) + return addr >> this->erase_shift; + return flexonenand_block(this, addr); +} + +/** + * flexonenand_addr - Return address of the block + * @this: OneNAND device structure + * @block: Block number on Flex-OneNAND + * + * Return address of the block + */ +static loff_t flexonenand_addr(struct onenand_chip *this, int block) +{ + loff_t ofs = 0; + int die = 0, boundary; + + if (ONENAND_IS_DDP(this) && block >= this->density_mask) { + block -= this->density_mask; + die = 1; + ofs = this->diesize[0]; + } + + boundary = this->boundary[die]; + ofs += (loff_t) block << (this->erase_shift - 1); + if (block > (boundary + 1)) + ofs += (loff_t) (block - boundary - 1) + << (this->erase_shift - 1); + return ofs; +} + +loff_t onenand_addr(struct onenand_chip *this, int block) +{ + if (!FLEXONENAND(this)) + return (loff_t) block << this->erase_shift; + return flexonenand_addr(this, block); +} + +/** + * flexonenand_region - [Flex-OneNAND] Return erase region of addr + * @param mtd MTD device structure + * @param addr address whose erase region needs to be identified + */ +int flexonenand_region(struct mtd_info *mtd, loff_t addr) +{ + int i; + + for (i = 0; i < mtd->numeraseregions; i++) + if (addr < mtd->eraseregions[i].offset) + break; + return i - 1; +} + +/** + * onenand_get_density - [DEFAULT] Get OneNAND density + * @param dev_id OneNAND device ID + * + * Get OneNAND density from device ID + */ +static inline int onenand_get_density(int dev_id) +{ + int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; + return (density & ONENAND_DEVICE_DENSITY_MASK); +} + +/** + * onenand_command - [DEFAULT] Send command to OneNAND device + * @param mtd MTD device structure + * @param cmd the command to be sent + * @param addr offset to read from or write to + * @param len number of bytes to read or write + * + * Send command to OneNAND device. This function is used for middle/large page + * devices (1KB/2KB Bytes per page) + */ +static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + int value; + int block, page; + + /* Now we use page size operation */ + int sectors = 0, count = 0; + + /* Address translation */ + switch (cmd) { + case ONENAND_CMD_UNLOCK: + case ONENAND_CMD_LOCK: + case ONENAND_CMD_LOCK_TIGHT: + case ONENAND_CMD_UNLOCK_ALL: + block = -1; + page = -1; + break; + + case FLEXONENAND_CMD_PI_ACCESS: + /* addr contains die index */ + block = addr * this->density_mask; + page = -1; + break; + + case ONENAND_CMD_ERASE: + case ONENAND_CMD_BUFFERRAM: + block = onenand_block(this, addr); + page = -1; + break; + + case FLEXONENAND_CMD_READ_PI: + cmd = ONENAND_CMD_READ; + block = addr * this->density_mask; + page = 0; + break; + + default: + block = onenand_block(this, addr); + page = (int) (addr + - onenand_addr(this, block)) >> this->page_shift; + page &= this->page_mask; + break; + } + + /* NOTE: The setting order of the registers is very important! */ + if (cmd == ONENAND_CMD_BUFFERRAM) { + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS2); + + if (ONENAND_IS_4KB_PAGE(this)) + ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + ONENAND_SET_NEXT_BUFFERRAM(this); + + return 0; + } + + if (block != -1) { + /* Write 'DFS, FBA' of Flash */ + value = onenand_block_address(this, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS1); + + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS2); + } + + if (page != -1) { + int dataram; + + switch (cmd) { + case FLEXONENAND_CMD_RECOVER_LSB: + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + if (ONENAND_IS_4KB_PAGE(this)) + dataram = ONENAND_SET_BUFFERRAM0(this); + else + dataram = ONENAND_SET_NEXT_BUFFERRAM(this); + + break; + + default: + dataram = ONENAND_CURRENT_BUFFERRAM(this); + break; + } + + /* Write 'FPA, FSA' of Flash */ + value = onenand_page_address(page, sectors); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS8); + + /* Write 'BSA, BSC' of DataRAM */ + value = onenand_buffer_address(dataram, sectors, count); + this->write_word(value, this->base + ONENAND_REG_START_BUFFER); + } + + /* Interrupt clear */ + this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); + /* Write command */ + this->write_word(cmd, this->base + ONENAND_REG_COMMAND); + + return 0; +} + +/** + * onenand_read_ecc - return ecc status + * @param this onenand chip structure + */ +static int onenand_read_ecc(struct onenand_chip *this) +{ + int ecc, i; + + if (!FLEXONENAND(this)) + return this->read_word(this->base + ONENAND_REG_ECC_STATUS); + + for (i = 0; i < 4; i++) { + ecc = this->read_word(this->base + + ((ONENAND_REG_ECC_STATUS + i) << 1)); + if (likely(!ecc)) + continue; + if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) + return ONENAND_ECC_2BIT_ALL; + } + + return 0; +} + +/** + * onenand_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. This applies to all OneNAND command + * Read can take up to 30us, erase up to 2ms and program up to 350us + * according to general OneNAND specs + */ +static int onenand_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + unsigned int flags = ONENAND_INT_MASTER; + unsigned int interrupt = 0; + unsigned int ctrl; + + while (1) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + if (interrupt & flags) + break; + } + + ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + + if (interrupt & ONENAND_INT_READ) { + int ecc = onenand_read_ecc(this); + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk("onenand_wait: ECC error = 0x%04x\n", ecc); + return -EBADMSG; + } + } + + if (ctrl & ONENAND_CTRL_ERROR) { + printk("onenand_wait: controller error = 0x%04x\n", ctrl); + if (ctrl & ONENAND_CTRL_LOCK) + printk("onenand_wait: it's locked error = 0x%04x\n", + ctrl); + + return -EIO; + } + + + return 0; +} + +/** + * onenand_bufferram_offset - [DEFAULT] BufferRAM offset + * @param mtd MTD data structure + * @param area BufferRAM area + * @return offset given area + * + * Return BufferRAM offset given area + */ +static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) +{ + struct onenand_chip *this = mtd->priv; + + if (ONENAND_CURRENT_BUFFERRAM(this)) { + if (area == ONENAND_DATARAM) + return mtd->writesize; + if (area == ONENAND_SPARERAM) + return mtd->oobsize; + } + + return 0; +} + +/** + * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area + * @param mtd MTD data structure + * @param area BufferRAM area + * @param buffer the databuffer to put/get data + * @param offset offset to read from or write to + * @param count number of bytes to read/write + * + * Read the BufferRAM area + */ +static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area, + unsigned char *buffer, int offset, + size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + + bufferram = this->base + area; + bufferram += onenand_bufferram_offset(mtd, area); + + memcpy_16(buffer, bufferram + offset, count); + + return 0; +} + +/** + * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode + * @param mtd MTD data structure + * @param area BufferRAM area + * @param buffer the databuffer to put/get data + * @param offset offset to read from or write to + * @param count number of bytes to read/write + * + * Read the BufferRAM area with Sync. Burst Mode + */ +static int onenand_sync_read_bufferram(struct mtd_info *mtd, loff_t addr, int area, + unsigned char *buffer, int offset, + size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + + bufferram = this->base + area; + bufferram += onenand_bufferram_offset(mtd, area); + + this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); + + memcpy_16(buffer, bufferram + offset, count); + + this->mmcontrol(mtd, 0); + + return 0; +} + +/** + * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area + * @param mtd MTD data structure + * @param area BufferRAM area + * @param buffer the databuffer to put/get data + * @param offset offset to read from or write to + * @param count number of bytes to read/write + * + * Write the BufferRAM area + */ +static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area, + const unsigned char *buffer, int offset, + size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + + bufferram = this->base + area; + bufferram += onenand_bufferram_offset(mtd, area); + + memcpy_16(bufferram + offset, buffer, count); + + return 0; +} + +/** + * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode + * @param mtd MTD data structure + * @param addr address to check + * @return blockpage address + * + * Get blockpage address at 2x program mode + */ +static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) +{ + struct onenand_chip *this = mtd->priv; + int blockpage, block, page; + + /* Calculate the even block number */ + block = (int) (addr >> this->erase_shift) & ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; + blockpage = (block << 7) | page; + + return blockpage; +} + +/** + * onenand_check_bufferram - [GENERIC] Check BufferRAM information + * @param mtd MTD data structure + * @param addr address to check + * @return 1 if there are valid data, otherwise 0 + * + * Check bufferram if there is data we required + */ +static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) +{ + struct onenand_chip *this = mtd->priv; + int blockpage, found = 0; + unsigned int i; + + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int) (addr >> this->page_shift); + + /* Is there valid data? */ + i = ONENAND_CURRENT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) + found = 1; + else { + /* Check another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) { + ONENAND_SET_NEXT_BUFFERRAM(this); + found = 1; + } + } + + if (found && ONENAND_IS_DDP(this)) { + /* Select DataRAM for DDP */ + int block = onenand_block(this, addr); + int value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + } + + return found; +} + +/** + * onenand_update_bufferram - [GENERIC] Update BufferRAM information + * @param mtd MTD data structure + * @param addr address to update + * @param valid valid flag + * + * Update BufferRAM information + */ +static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, + int valid) +{ + struct onenand_chip *this = mtd->priv; + int blockpage; + unsigned int i; + + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int)(addr >> this->page_shift); + + /* Invalidate another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) + this->bufferram[i].blockpage = -1; + + /* Update BufferRAM */ + i = ONENAND_CURRENT_BUFFERRAM(this); + if (valid) + this->bufferram[i].blockpage = blockpage; + else + this->bufferram[i].blockpage = -1; + + return 0; +} + +/** + * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information + * @param mtd MTD data structure + * @param addr start address to invalidate + * @param len length to invalidate + * + * Invalidate BufferRAM information + */ +static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, + unsigned int len) +{ + struct onenand_chip *this = mtd->priv; + int i; + loff_t end_addr = addr + len; + + /* Invalidate BufferRAM */ + for (i = 0; i < MAX_BUFFERRAM; i++) { + loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; + + if (buf_addr >= addr && buf_addr < end_addr) + this->bufferram[i].blockpage = -1; + } +} + +/** + * onenand_get_device - [GENERIC] Get chip for selected access + * @param mtd MTD device structure + * @param new_state the state which is requested + * + * Get the device and lock it for exclusive access + */ +static void onenand_get_device(struct mtd_info *mtd, int new_state) +{ + /* Do nothing */ +} + +/** + * onenand_release_device - [GENERIC] release chip + * @param mtd MTD device structure + * + * Deselect, release chip lock and wake up anyone waiting on the device + */ +static void onenand_release_device(struct mtd_info *mtd) +{ + /* Do nothing */ +} + +/** + * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer + * @param mtd MTD device structure + * @param buf destination address + * @param column oob offset to read from + * @param thislen oob length to read + */ +static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, + int column, int thislen) +{ + struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int readcol = column; + int readend = column + thislen; + int lastgap = 0; + unsigned int i; + uint8_t *oob_buf = this->oob_buf; + + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length; + i++, free++) { + if (readcol >= lastgap) + readcol += free->offset - lastgap; + if (readend >= lastgap) + readend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length; + i++, free++) { + int free_end = free->offset + free->length; + if (free->offset < readend && free_end > readcol) { + int st = max_t(int,free->offset,readcol); + int ed = min_t(int,free_end,readend); + int n = ed - st; + memcpy(buf, oob_buf + st, n); + buf += n; + } else if (column == 0) + break; + } + return 0; +} + +/** + * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data + * @param mtd MTD device structure + * @param addr address to recover + * @param status return value from onenand_wait + * + * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has + * lower page address and MSB page has higher page address in paired pages. + * If power off occurs during MSB page program, the paired LSB page data can + * become corrupt. LSB page recovery read is a way to read LSB page though page + * data are corrupted. When uncorrectable error occurs as a result of LSB page + * read after power up, issue LSB page recovery read. + */ +static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) +{ + struct onenand_chip *this = mtd->priv; + int i; + + /* Recovery is only for Flex-OneNAND */ + if (!FLEXONENAND(this)) + return status; + + /* check if we failed due to uncorrectable error */ + if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR) + return status; + + /* check if address lies in MLC region */ + i = flexonenand_region(mtd, addr); + if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift)) + return status; + + printk("onenand_recover_lsb:" + "Attempting to recover from uncorrectable read\n"); + + /* Issue the LSB page recovery command */ + this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); + return this->wait(mtd, FL_READING); +} + +/** + * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param ops oob operation description structure + * + * OneNAND read main and/or out-of-band data + */ +static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + struct mtd_ecc_stats stats; + size_t len = ops->len; + size_t ooblen = ops->ooblen; + u_char *buf = ops->datbuf; + u_char *oobbuf = ops->oobbuf; + int read = 0, column, thislen; + int oobread = 0, oobcolumn, thisooblen, oobsize; + int ret = 0, boundary = 0; + int writesize = this->writesize; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + if (ops->mode == MTD_OPS_AUTO_OOB) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + oobcolumn = from & (mtd->oobsize - 1); + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n"); + ops->retlen = 0; + ops->oobretlen = 0; + return -EINVAL; + } + + stats = mtd->ecc_stats; + + /* Read-while-load method */ + /* Note: We can't use this feature in MLC */ + + /* Do first load to bufferRAM */ + if (read < len) { + if (!onenand_check_bufferram(mtd, from)) { + this->main_buf = buf; + this->command(mtd, ONENAND_CMD_READ, from, writesize); + ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; + } + } + + thislen = min_t(int, writesize, len - read); + column = from & (writesize - 1); + if (column + thislen > writesize) + thislen = writesize - column; + + while (!ret) { + /* If there is more to load then start next load */ + from += thislen; + if (!ONENAND_IS_4KB_PAGE(this) && read + thislen < len) { + this->main_buf = buf + thislen; + this->command(mtd, ONENAND_CMD_READ, from, writesize); + /* + * Chip boundary handling in DDP + * Now we issued chip 1 read and pointed chip 1 + * bufferam so we have to point chip 0 bufferam. + */ + if (ONENAND_IS_DDP(this) && + unlikely(from == (this->chipsize >> 1))) { + this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); + boundary = 1; + } else + boundary = 0; + ONENAND_SET_PREV_BUFFERRAM(this); + } + + /* While load is going, read from last bufferRAM */ + this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen); + + /* Read oob area if needed */ + if (oobbuf) { + thisooblen = oobsize - oobcolumn; + thisooblen = min_t(int, thisooblen, ooblen - oobread); + + if (ops->mode == MTD_OPS_AUTO_OOB) + onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); + else + this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); + oobread += thisooblen; + oobbuf += thisooblen; + oobcolumn = 0; + } + + if (ONENAND_IS_4KB_PAGE(this) && (read + thislen < len)) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + onenand_update_bufferram(mtd, from, !ret); + if (mtd_is_eccerr(ret)) + ret = 0; + } + + /* See if we are done */ + read += thislen; + if (read == len) + break; + /* Set up for next read from bufferRAM */ + if (unlikely(boundary)) + this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); + if (!ONENAND_IS_4KB_PAGE(this)) + ONENAND_SET_NEXT_BUFFERRAM(this); + buf += thislen; + thislen = min_t(int, writesize, len - read); + column = 0; + + if (!ONENAND_IS_4KB_PAGE(this)) { + /* Now wait for load */ + ret = this->wait(mtd, FL_READING); + onenand_update_bufferram(mtd, from, !ret); + if (mtd_is_eccerr(ret)) + ret = 0; + } + } + + /* + * Return success, if no ECC failures, else -EBADMSG + * fs driver will take care of that, because + * retlen == desired len and result == -EBADMSG + */ + ops->retlen = read; + ops->oobretlen = oobread; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; +} + +/** + * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param ops oob operation description structure + * + * OneNAND read out-of-band data from the spare area + */ +static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + struct mtd_ecc_stats stats; + int read = 0, thislen, column, oobsize; + size_t len = ops->ooblen; + unsigned int mode = ops->mode; + u_char *buf = ops->oobbuf; + int ret = 0, readcmd; + + from += ops->ooboffs; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + /* Initialize return length value */ + ops->oobretlen = 0; + + if (mode == MTD_OPS_AUTO_OOB) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + column = from & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(from >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (from >> this->page_shift)) * oobsize)) { + printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n"); + return -EINVAL; + } + + stats = mtd->ecc_stats; + + readcmd = ONENAND_IS_4KB_PAGE(this) ? + ONENAND_CMD_READ : ONENAND_CMD_READOOB; + + while (read < len) { + thislen = oobsize - column; + thislen = min_t(int, thislen, len); + + this->spare_buf = buf; + this->command(mtd, readcmd, from, mtd->oobsize); + + onenand_update_bufferram(mtd, from, 0); + + ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + + if (ret && ret != -EBADMSG) { + printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); + break; + } + + if (mode == MTD_OPS_AUTO_OOB) + onenand_transfer_auto_oob(mtd, buf, column, thislen); + else + this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen); + + read += thislen; + + if (read == len) + break; + + buf += thislen; + + /* Read more? */ + if (read < len) { + /* Page size */ + from += mtd->writesize; + column = 0; + } + } + + ops->oobretlen = read; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return 0; +} + +/** + * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc + * @param mtd MTD device structure + * @param from offset to read from + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put data + * + * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL +*/ +int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) +{ + struct mtd_oob_ops ops = { + .len = len, + .ooblen = 0, + .datbuf = buf, + .oobbuf = NULL, + }; + int ret; + + onenand_get_device(mtd, FL_READING); + ret = onenand_read_ops_nolock(mtd, from, &ops); + onenand_release_device(mtd); + + *retlen = ops.retlen; + return ret; +} + +/** + * onenand_read_oob - [MTD Interface] OneNAND read out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param ops oob operations description structure + * + * OneNAND main and/or out-of-band + */ +int onenand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int ret; + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + break; + case MTD_OPS_RAW: + /* Not implemented yet */ + default: + return -EINVAL; + } + + onenand_get_device(mtd, FL_READING); + if (ops->datbuf) + ret = onenand_read_ops_nolock(mtd, from, ops); + else + ret = onenand_read_oob_nolock(mtd, from, ops); + onenand_release_device(mtd); + + return ret; +} + +/** + * onenand_bbt_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. + */ +static int onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + unsigned int flags = ONENAND_INT_MASTER; + unsigned int interrupt; + unsigned int ctrl; + + while (1) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + if (interrupt & flags) + break; + } + + /* To get correct interrupt status in timeout case */ + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + + if (interrupt & ONENAND_INT_READ) { + int ecc = onenand_read_ecc(this); + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" + ", controller = 0x%04x\n", ecc, ctrl); + return ONENAND_BBT_READ_ERROR; + } + } else { + printk(KERN_ERR "onenand_bbt_wait: read timeout!" + "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + /* Initial bad block case: 0x2400 or 0x0400 */ + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); + return ONENAND_BBT_READ_ERROR; + } + + return 0; +} + +/** + * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan + * @param mtd MTD device structure + * @param from offset to read from + * @param ops oob operation description structure + * + * OneNAND read out-of-band data from the spare area for bbt scan + */ +int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int read = 0, thislen, column; + int ret = 0, readcmd; + size_t len = ops->ooblen; + u_char *buf = ops->oobbuf; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); + + readcmd = ONENAND_IS_4KB_PAGE(this) ? + ONENAND_CMD_READ : ONENAND_CMD_READOOB; + + /* Initialize return value */ + ops->oobretlen = 0; + + /* Do not allow reads past end of device */ + if (unlikely((from + len) > mtd->size)) { + printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_READING); + + column = from & (mtd->oobsize - 1); + + while (read < len) { + + thislen = mtd->oobsize - column; + thislen = min_t(int, thislen, len); + + this->spare_buf = buf; + this->command(mtd, readcmd, from, mtd->oobsize); + + onenand_update_bufferram(mtd, from, 0); + + ret = this->bbt_wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + + if (ret) + break; + + this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen); + read += thislen; + if (read == len) + break; + + buf += thislen; + + /* Read more? */ + if (read < len) { + /* Update Page size */ + from += this->writesize; + column = 0; + } + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + ops->oobretlen = read; + return ret; +} + + +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE +/** + * onenand_verify_oob - [GENERIC] verify the oob contents after a write + * @param mtd MTD device structure + * @param buf the databuffer to verify + * @param to offset to read from + */ +static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) +{ + struct onenand_chip *this = mtd->priv; + u_char *oob_buf = this->oob_buf; + int status, i, readcmd; + + readcmd = ONENAND_IS_4KB_PAGE(this) ? + ONENAND_CMD_READ : ONENAND_CMD_READOOB; + + this->command(mtd, readcmd, to, mtd->oobsize); + onenand_update_bufferram(mtd, to, 0); + status = this->wait(mtd, FL_READING); + if (status) + return status; + + this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); + for (i = 0; i < mtd->oobsize; i++) + if (buf[i] != 0xFF && buf[i] != oob_buf[i]) + return -EBADMSG; + + return 0; +} + +/** + * onenand_verify - [GENERIC] verify the chip contents after a write + * @param mtd MTD device structure + * @param buf the databuffer to verify + * @param addr offset to read from + * @param len number of bytes to read and compare + */ +static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *dataram; + int ret = 0; + int thislen, column; + + while (len != 0) { + thislen = min_t(int, this->writesize, len); + column = addr & (this->writesize - 1); + if (column + thislen > this->writesize) + thislen = this->writesize - column; + + this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); + + onenand_update_bufferram(mtd, addr, 0); + + ret = this->wait(mtd, FL_READING); + if (ret) + return ret; + + onenand_update_bufferram(mtd, addr, 1); + + dataram = this->base + ONENAND_DATARAM; + dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); + + if (memcmp(buf, dataram + column, thislen)) + return -EBADMSG; + + len -= thislen; + buf += thislen; + addr += thislen; + } + + return 0; +} +#else +#define onenand_verify(...) (0) +#define onenand_verify_oob(...) (0) +#endif + +#define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) + +/** + * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer + * @param mtd MTD device structure + * @param oob_buf oob buffer + * @param buf source address + * @param column oob offset to write to + * @param thislen oob length to write + */ +static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, + const u_char *buf, int column, int thislen) +{ + struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int writecol = column; + int writeend = column + thislen; + int lastgap = 0; + unsigned int i; + + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length; + i++, free++) { + if (writecol >= lastgap) + writecol += free->offset - lastgap; + if (writeend >= lastgap) + writeend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length; + i++, free++) { + int free_end = free->offset + free->length; + if (free->offset < writeend && free_end > writecol) { + int st = max_t(int,free->offset,writecol); + int ed = min_t(int,free_end,writeend); + int n = ed - st; + memcpy(oob_buf + st, buf, n); + buf += n; + } else if (column == 0) + break; + } + return 0; +} + +/** + * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band + * @param mtd MTD device structure + * @param to offset to write to + * @param ops oob operation description structure + * + * Write main and/or oob with ECC + */ +static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int written = 0, column, thislen, subpage; + int oobwritten = 0, oobcolumn, thisooblen, oobsize; + size_t len = ops->len; + size_t ooblen = ops->ooblen; + const u_char *buf = ops->datbuf; + const u_char *oob = ops->oobbuf; + u_char *oobbuf; + int ret = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Initialize retlen, in case of early exit */ + ops->retlen = 0; + ops->oobretlen = 0; + + /* Reject writes, which are not page aligned */ + if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { + printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); + return -EINVAL; + } + + if (ops->mode == MTD_OPS_AUTO_OOB) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + oobcolumn = to & (mtd->oobsize - 1); + + column = to & (mtd->writesize - 1); + + /* Loop until all data write */ + while (written < len) { + u_char *wbuf = (u_char *) buf; + + thislen = min_t(int, mtd->writesize - column, len - written); + thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); + + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); + + /* Partial page write */ + subpage = thislen < mtd->writesize; + if (subpage) { + memset(this->page_buf, 0xff, mtd->writesize); + memcpy(this->page_buf + column, buf, thislen); + wbuf = this->page_buf; + } + + this->write_bufferram(mtd, to, ONENAND_DATARAM, wbuf, 0, mtd->writesize); + + if (oob) { + oobbuf = this->oob_buf; + + /* We send data to spare ram with oobsize + * * to prevent byte access */ + memset(oobbuf, 0xff, mtd->oobsize); + if (ops->mode == MTD_OPS_AUTO_OOB) + onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); + else + memcpy(oobbuf + oobcolumn, oob, thisooblen); + + oobwritten += thisooblen; + oob += thisooblen; + oobcolumn = 0; + } else + oobbuf = (u_char *) ffchars; + + this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); + + this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); + + ret = this->wait(mtd, FL_WRITING); + + /* In partial page write we don't update bufferram */ + onenand_update_bufferram(mtd, to, !ret && !subpage); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); + } + + if (ret) { + printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); + break; + } + + /* Only check verify write turn on */ + ret = onenand_verify(mtd, buf, to, thislen); + if (ret) { + printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); + break; + } + + written += thislen; + + if (written == len) + break; + + column = 0; + to += thislen; + buf += thislen; + } + + ops->retlen = written; + + return ret; +} + +/** + * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * @param mode operation mode + * + * OneNAND write out-of-band + */ +static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int column, ret = 0, oobsize; + int written = 0, oobcmd; + u_char *oobbuf; + size_t len = ops->ooblen; + const u_char *buf = ops->oobbuf; + unsigned int mode = ops->mode; + + to += ops->ooboffs; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Initialize retlen, in case of early exit */ + ops->oobretlen = 0; + + if (mode == MTD_OPS_AUTO_OOB) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n"); + return -EINVAL; + } + + /* For compatibility with NAND: Do not allow write past end of page */ + if (unlikely(column + len > oobsize)) { + printk(KERN_ERR "onenand_write_oob_nolock: " + "Attempt to write past end of page\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (to >> this->page_shift)) * oobsize)) { + printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n"); + return -EINVAL; + } + + oobbuf = this->oob_buf; + + oobcmd = ONENAND_IS_4KB_PAGE(this) ? + ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; + + /* Loop until all data write */ + while (written < len) { + int thislen = min_t(int, oobsize, len - written); + + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); + + /* We send data to spare ram with oobsize + * to prevent byte access */ + memset(oobbuf, 0xff, mtd->oobsize); + if (mode == MTD_OPS_AUTO_OOB) + onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen); + else + memcpy(oobbuf + column, buf, thislen); + this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); + + if (ONENAND_IS_4KB_PAGE(this)) { + /* Set main area of DataRAM to 0xff*/ + memset(this->page_buf, 0xff, mtd->writesize); + this->write_bufferram(mtd, 0, ONENAND_DATARAM, + this->page_buf, 0, mtd->writesize); + } + + this->command(mtd, oobcmd, to, mtd->oobsize); + + onenand_update_bufferram(mtd, to, 0); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, 0); + } + + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret); + break; + } + + ret = onenand_verify_oob(mtd, oobbuf, to); + if (ret) { + printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret); + break; + } + + written += thislen; + if (written == len) + break; + + to += mtd->writesize; + buf += thislen; + column = 0; + } + + ops->oobretlen = written; + + return ret; +} + +/** + * onenand_write - [MTD Interface] compability function for onenand_write_ecc + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * + * Write with ECC + */ +int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct mtd_oob_ops ops = { + .len = len, + .ooblen = 0, + .datbuf = (u_char *) buf, + .oobbuf = NULL, + }; + int ret; + + onenand_get_device(mtd, FL_WRITING); + ret = onenand_write_ops_nolock(mtd, to, &ops); + onenand_release_device(mtd); + + *retlen = ops.retlen; + return ret; +} + +/** + * onenand_write_oob - [MTD Interface] OneNAND write out-of-band + * @param mtd MTD device structure + * @param to offset to write to + * @param ops oob operation description structure + * + * OneNAND write main and/or out-of-band + */ +int onenand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int ret; + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + break; + case MTD_OPS_RAW: + /* Not implemented yet */ + default: + return -EINVAL; + } + + onenand_get_device(mtd, FL_WRITING); + if (ops->datbuf) + ret = onenand_write_ops_nolock(mtd, to, ops); + else + ret = onenand_write_oob_nolock(mtd, to, ops); + onenand_release_device(mtd); + + return ret; + +} + +/** + * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad + * @param mtd MTD device structure + * @param ofs offset from device start + * @param allowbbt 1, if its allowed to access the bbt area + * + * Check, if the block is bad, Either by reading the bad block table or + * calling of the scan function. + */ +static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + + /* Return info from the table */ + return bbm->isbad_bbt(mtd, ofs, allowbbt); +} + + +/** + * onenand_erase - [MTD Interface] erase block(s) + * @param mtd MTD device structure + * @param instr erase instruction + * + * Erase one ore more blocks + */ +int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct onenand_chip *this = mtd->priv; + unsigned int block_size; + loff_t addr = instr->addr; + unsigned int len = instr->len; + int ret = 0, i; + struct mtd_erase_region_info *region = NULL; + unsigned int region_end = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", + (unsigned int) addr, len); + + if (FLEXONENAND(this)) { + /* Find the eraseregion of this address */ + i = flexonenand_region(mtd, addr); + region = &mtd->eraseregions[i]; + + block_size = region->erasesize; + region_end = region->offset + + region->erasesize * region->numblocks; + + /* Start address within region must align on block boundary. + * Erase region's start offset is always block start address. + */ + if (unlikely((addr - region->offset) & (block_size - 1))) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" + " Unaligned address\n"); + return -EINVAL; + } + } else { + block_size = 1 << this->erase_shift; + + /* Start address must align on block boundary */ + if (unlikely(addr & (block_size - 1))) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" + "Unaligned address\n"); + return -EINVAL; + } + } + + /* Length must align on block boundary */ + if (unlikely(len & (block_size - 1))) { + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_erase: Length not block aligned\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_ERASING); + + /* Loop throught the pages */ + instr->state = MTD_ERASING; + + while (len) { + + /* Check if we have a bad block, we do not erase bad blocks */ + if (instr->priv == 0 && onenand_block_isbad_nolock(mtd, addr, 0)) { + printk(KERN_WARNING "onenand_erase: attempt to erase" + " a bad block at addr 0x%08x\n", + (unsigned int) addr); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); + + onenand_invalidate_bufferram(mtd, addr, block_size); + + ret = this->wait(mtd, FL_ERASING); + /* Check, if it is write protected */ + if (ret) { + if (ret == -EPERM) + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " + "Device is write protected!!!\n"); + else + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " + "Failed erase, block %d\n", + onenand_block(this, addr)); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = addr; + + goto erase_exit; + } + + len -= block_size; + addr += block_size; + + if (addr == region_end) { + if (!len) + break; + region++; + + block_size = region->erasesize; + region_end = region->offset + + region->erasesize * region->numblocks; + + if (len & (block_size - 1)) { + /* This has been checked at MTD + * partitioning level. */ + printk("onenand_erase: Unaligned address\n"); + goto erase_exit; + } + } + } + + instr->state = MTD_ERASE_DONE; + +erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + return ret; +} + +/** + * onenand_sync - [MTD Interface] sync + * @param mtd MTD device structure + * + * Sync is actually a wait for chip ready function + */ +void onenand_sync(struct mtd_info *mtd) +{ + MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_SYNCING); + + /* Release it and go back */ + onenand_release_device(mtd); +} + +/** + * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * + * Check whether the block is bad + */ +int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + int ret; + + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + + onenand_get_device(mtd, FL_READING); + ret = onenand_block_isbad_nolock(mtd,ofs, 0); + onenand_release_device(mtd); + return ret; +} + +/** + * onenand_default_block_markbad - [DEFAULT] mark a block bad + * @param mtd MTD device structure + * @param ofs offset from device start + * + * This is the default implementation, which can be overridden by + * a hardware specific driver. + */ +static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + u_char buf[2] = {0, 0}; + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .ooblen = 2, + .oobbuf = buf, + .ooboffs = 0, + }; + int block; + + /* Get block number */ + block = onenand_block(this, ofs); + if (bbm->bbt) + bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + + /* We write two bytes, so we dont have to mess with 16 bit access */ + ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); + return onenand_write_oob_nolock(mtd, ofs, &ops); +} + +/** + * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * + * Mark the block as bad + */ +int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + int ret; + + ret = onenand_block_isbad(mtd, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + ret = mtd_block_markbad(mtd, ofs); + return ret; +} + +/** + * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to lock or unlock + * @param cmd lock or unlock command + * + * Lock or unlock one or more blocks + */ +static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd) +{ + struct onenand_chip *this = mtd->priv; + int start, end, block, value, status; + + start = onenand_block(this, ofs); + end = onenand_block(this, ofs + len); + + /* Continuous lock scheme */ + if (this->options & ONENAND_HAS_CONT_LOCK) { + /* Set start block address */ + this->write_word(start, + this->base + ONENAND_REG_START_BLOCK_ADDRESS); + /* Set end block address */ + this->write_word(end - 1, + this->base + ONENAND_REG_END_BLOCK_ADDRESS); + /* Write unlock command */ + this->command(mtd, cmd, 0, 0); + + /* There's no return value */ + this->wait(mtd, FL_UNLOCKING); + + /* Sanity check */ + while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) + & ONENAND_CTRL_ONGO) + continue; + + /* Check lock status */ + status = this->read_word(this->base + ONENAND_REG_WP_STATUS); + if (!(status & ONENAND_WP_US)) + printk(KERN_ERR "wp status = 0x%x\n", status); + + return 0; + } + + /* Block lock scheme */ + for (block = start; block < end; block++) { + /* Set block address */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + + /* Set start block address */ + this->write_word(block, + this->base + ONENAND_REG_START_BLOCK_ADDRESS); + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); + + /* There's no return value */ + this->wait(mtd, FL_UNLOCKING); + + /* Sanity check */ + while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) + & ONENAND_CTRL_ONGO) + continue; + + /* Check lock status */ + status = this->read_word(this->base + ONENAND_REG_WP_STATUS); + if (!(status & ONENAND_WP_US)) + printk(KERN_ERR "block = %d, wp status = 0x%x\n", + block, status); + } + + return 0; +} + +#ifdef ONENAND_LINUX +/** + * onenand_lock - [MTD Interface] Lock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to unlock + * + * Lock one or more blocks + */ +static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + int ret; + + onenand_get_device(mtd, FL_LOCKING); + ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); + onenand_release_device(mtd); + return ret; +} + +/** + * onenand_unlock - [MTD Interface] Unlock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to unlock + * + * Unlock one or more blocks + */ +static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + int ret; + + onenand_get_device(mtd, FL_LOCKING); + ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); + onenand_release_device(mtd); + return ret; +} +#endif + +/** + * onenand_check_lock_status - [OneNAND Interface] Check lock status + * @param this onenand chip data structure + * + * Check lock status + */ +static int onenand_check_lock_status(struct onenand_chip *this) +{ + unsigned int value, block, status; + unsigned int end; + + end = this->chipsize >> this->erase_shift; + for (block = 0; block < end; block++) { + /* Set block address */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + /* Set start block address */ + this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); + + /* Check lock status */ + status = this->read_word(this->base + ONENAND_REG_WP_STATUS); + if (!(status & ONENAND_WP_US)) { + printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); + return 0; + } + } + + return 1; +} + +/** + * onenand_unlock_all - [OneNAND Interface] unlock all blocks + * @param mtd MTD device structure + * + * Unlock all blocks + */ +static void onenand_unlock_all(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + loff_t ofs = 0; + size_t len = mtd->size; + + if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Set start block address */ + this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + + /* There's no return value */ + this->wait(mtd, FL_LOCKING); + + /* Sanity check */ + while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) + & ONENAND_CTRL_ONGO) + continue; + + /* Check lock status */ + if (onenand_check_lock_status(this)) + return; + + /* Workaround for all block unlock in DDP */ + if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) { + /* All blocks on another chip */ + ofs = this->chipsize >> 1; + len = this->chipsize >> 1; + } + } + + onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); +} + + +/** + * onenand_check_features - Check and set OneNAND features + * @param mtd MTD data structure + * + * Check and set OneNAND features + * - lock scheme + * - two plane + */ +static void onenand_check_features(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned int density, process; + + /* Lock scheme depends on density and process */ + density = onenand_get_density(this->device_id); + process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; + + /* Lock scheme */ + switch (density) { + case ONENAND_DEVICE_DENSITY_4Gb: + if (ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + else + this->options |= ONENAND_HAS_4KB_PAGE; + + case ONENAND_DEVICE_DENSITY_2Gb: + /* 2Gb DDP don't have 2 plane */ + if (!ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + this->options |= ONENAND_HAS_UNLOCK_ALL; + + case ONENAND_DEVICE_DENSITY_1Gb: + /* A-Die has all block unlock */ + if (process) + this->options |= ONENAND_HAS_UNLOCK_ALL; + break; + + default: + /* Some OneNAND has continuous lock scheme */ + if (!process) + this->options |= ONENAND_HAS_CONT_LOCK; + break; + } + + if (ONENAND_IS_MLC(this)) + this->options |= ONENAND_HAS_4KB_PAGE; + + if (ONENAND_IS_4KB_PAGE(this)) + this->options &= ~ONENAND_HAS_2PLANE; + + if (FLEXONENAND(this)) { + this->options &= ~ONENAND_HAS_CONT_LOCK; + this->options |= ONENAND_HAS_UNLOCK_ALL; + } + + if (this->options & ONENAND_HAS_CONT_LOCK) + printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); + if (this->options & ONENAND_HAS_UNLOCK_ALL) + printk(KERN_DEBUG "Chip support all block unlock\n"); + if (this->options & ONENAND_HAS_2PLANE) + printk(KERN_DEBUG "Chip has 2 plane\n"); + if (this->options & ONENAND_HAS_4KB_PAGE) + printk(KERN_DEBUG "Chip has 4KiB pagesize\n"); + +} + +/** + * onenand_print_device_info - Print device ID + * @param device device ID + * + * Print device ID + */ +char *onenand_print_device_info(int device, int version) +{ + int vcc, demuxed, ddp, density, flexonenand; + char *dev_info = malloc(80); + char *p = dev_info; + + vcc = device & ONENAND_DEVICE_VCC_MASK; + demuxed = device & ONENAND_DEVICE_IS_DEMUX; + ddp = device & ONENAND_DEVICE_IS_DDP; + density = onenand_get_density(device); + flexonenand = device & DEVICE_IS_FLEXONENAND; + p += sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)", + demuxed ? "" : "Muxed ", + flexonenand ? "Flex-" : "", + ddp ? "(DDP)" : "", + (16 << density), vcc ? "2.65/3.3" : "1.8", device); + + sprintf(p, "\nOneNAND version = 0x%04x", version); + printk("%s\n", dev_info); + + return dev_info; +} + +static const struct onenand_manufacturers onenand_manuf_ids[] = { + {ONENAND_MFR_NUMONYX, "Numonyx"}, + {ONENAND_MFR_SAMSUNG, "Samsung"}, +}; + +/** + * onenand_check_maf - Check manufacturer ID + * @param manuf manufacturer ID + * + * Check manufacturer ID + */ +static int onenand_check_maf(int manuf) +{ + int size = ARRAY_SIZE(onenand_manuf_ids); + int i; +#ifdef ONENAND_DEBUG + char *name; +#endif + + for (i = 0; i < size; i++) + if (manuf == onenand_manuf_ids[i].id) + break; + +#ifdef ONENAND_DEBUG + if (i < size) + name = onenand_manuf_ids[i].name; + else + name = "Unknown"; + + printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf); +#endif + + return i == size; +} + +/** +* flexonenand_get_boundary - Reads the SLC boundary +* @param onenand_info - onenand info structure +* +* Fill up boundary[] field in onenand_chip +**/ +static int flexonenand_get_boundary(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned int die, bdry; + int syscfg, locked; + + /* Disable ECC */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); + + for (die = 0; die < this->dies; die++) { + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + this->wait(mtd, FL_READING); + + bdry = this->read_word(this->base + ONENAND_DATARAM); + if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) + locked = 0; + else + locked = 1; + this->boundary[die] = bdry & FLEXONENAND_PI_MASK; + + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + printk(KERN_INFO "Die %d boundary: %d%s\n", die, + this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); + } + + /* Enable ECC */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + return 0; +} + +/** + * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info + * boundary[], diesize[], mtd->size, mtd->erasesize, + * mtd->eraseregions + * @param mtd - MTD device structure + */ +static void flexonenand_get_size(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int die, i, eraseshift, density; + int blksperdie, maxbdry; + loff_t ofs; + + density = onenand_get_density(this->device_id); + blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift); + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + maxbdry = blksperdie - 1; + eraseshift = this->erase_shift - 1; + + mtd->numeraseregions = this->dies << 1; + + /* This fills up the device boundary */ + flexonenand_get_boundary(mtd); + die = 0; + ofs = 0; + i = -1; + for (; die < this->dies; die++) { + if (!die || this->boundary[die-1] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = + this->boundary[die] + 1; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift++; + } else { + mtd->numeraseregions -= 1; + mtd->eraseregions[i].numblocks += + this->boundary[die] + 1; + ofs += (this->boundary[die] + 1) << (eraseshift - 1); + } + if (this->boundary[die] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = maxbdry ^ + this->boundary[die]; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift--; + } else + mtd->numeraseregions -= 1; + } + + /* Expose MLC erase size except when all blocks are SLC */ + mtd->erasesize = 1 << this->erase_shift; + if (mtd->numeraseregions == 1) + mtd->erasesize >>= 1; + + printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); + for (i = 0; i < mtd->numeraseregions; i++) + printk(KERN_INFO "[offset: 0x%08llx, erasesize: 0x%05x," + " numblocks: %04u]\n", mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + + for (die = 0, mtd->size = 0; die < this->dies; die++) { + this->diesize[die] = (loff_t) (blksperdie << this->erase_shift); + this->diesize[die] -= (loff_t) (this->boundary[die] + 1) + << (this->erase_shift - 1); + mtd->size += this->diesize[die]; + } +} + +/** + * flexonenand_check_blocks_erased - Check if blocks are erased + * @param mtd_info - mtd info structure + * @param start - first erase block to check + * @param end - last erase block to check + * + * Converting an unerased block from MLC to SLC + * causes byte values to change. Since both data and its ECC + * have changed, reads on the block give uncorrectable error. + * This might lead to the block being detected as bad. + * + * Avoid this by ensuring that the block to be converted is + * erased. + */ +static int flexonenand_check_blocks_erased(struct mtd_info *mtd, + int start, int end) +{ + struct onenand_chip *this = mtd->priv; + int i, ret; + int block; + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .ooboffs = 0, + .ooblen = mtd->oobsize, + .datbuf = NULL, + .oobbuf = this->oob_buf, + }; + loff_t addr; + + printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end); + + for (block = start; block <= end; block++) { + addr = flexonenand_addr(this, block); + if (onenand_block_isbad_nolock(mtd, addr, 0)) + continue; + + /* + * Since main area write results in ECC write to spare, + * it is sufficient to check only ECC bytes for change. + */ + ret = onenand_read_oob_nolock(mtd, addr, &ops); + if (ret) + return ret; + + for (i = 0; i < mtd->oobsize; i++) + if (this->oob_buf[i] != 0xff) + break; + + if (i != mtd->oobsize) { + printk(KERN_WARNING "Block %d not erased.\n", block); + return 1; + } + } + + return 0; +} + +/** + * flexonenand_set_boundary - Writes the SLC boundary + * @param mtd - mtd info structure + */ +int flexonenand_set_boundary(struct mtd_info *mtd, int die, + int boundary, int lock) +{ + struct onenand_chip *this = mtd->priv; + int ret, density, blksperdie, old, new, thisboundary; + loff_t addr; + + if (die >= this->dies) + return -EINVAL; + + if (boundary == this->boundary[die]) + return 0; + + density = onenand_get_density(this->device_id); + blksperdie = ((16 << density) << 20) >> this->erase_shift; + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + + if (boundary >= blksperdie) { + printk("flexonenand_set_boundary:" + "Invalid boundary value. " + "Boundary not changed.\n"); + return -EINVAL; + } + + /* Check if converting blocks are erased */ + old = this->boundary[die] + (die * this->density_mask); + new = boundary + (die * this->density_mask); + ret = flexonenand_check_blocks_erased(mtd, min(old, new) + + 1, max(old, new)); + if (ret) { + printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n"); + return ret; + } + + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + /* Check is boundary is locked */ + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + ret = this->wait(mtd, FL_READING); + + thisboundary = this->read_word(this->base + ONENAND_DATARAM); + if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { + printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n"); + goto out; + } + + printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n", + die, boundary, lock ? "(Locked)" : "(Unlocked)"); + + boundary &= FLEXONENAND_PI_MASK; + boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); + + addr = die ? this->diesize[0] : 0; + this->command(mtd, ONENAND_CMD_ERASE, addr, 0); + ret = this->wait(mtd, FL_ERASING); + if (ret) { + printk("flexonenand_set_boundary:" + "Failed PI erase for Die %d\n", die); + goto out; + } + + this->write_word(boundary, this->base + ONENAND_DATARAM); + this->command(mtd, ONENAND_CMD_PROG, addr, 0); + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk("flexonenand_set_boundary:" + "Failed PI write for Die %d\n", die); + goto out; + } + + this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0); + ret = this->wait(mtd, FL_WRITING); +out: + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); + this->wait(mtd, FL_RESETING); + if (!ret) + /* Recalculate device size on boundary change*/ + flexonenand_get_size(mtd); + + return ret; +} + +/** + * onenand_chip_probe - [OneNAND Interface] Probe the OneNAND chip + * @param mtd MTD device structure + * + * OneNAND detection method: + * Compare the the values from command with ones from register + */ +static int onenand_chip_probe(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int bram_maf_id, bram_dev_id, maf_id, dev_id; + int syscfg; + + /* Save system configuration 1 */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + + /* Clear Sync. Burst Read mode to read BootRAM */ + this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), + this->base + ONENAND_REG_SYS_CFG1); + + /* Send the command for reading device ID from BootRAM */ + this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); + + /* Read manufacturer and device IDs from BootRAM */ + bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); + bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); + + /* Reset OneNAND to read default register values */ + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); + + /* Wait reset */ + this->wait(mtd, FL_RESETING); + + /* Restore system configuration 1 */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + + /* Check manufacturer ID */ + if (onenand_check_maf(bram_maf_id)) + return -ENXIO; + + /* Read manufacturer and device IDs from Register */ + maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); + dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + + /* Check OneNAND device */ + if (maf_id != bram_maf_id || dev_id != bram_dev_id) + return -ENXIO; + + return 0; +} + +/** + * onenand_probe - [OneNAND Interface] Probe the OneNAND device + * @param mtd MTD device structure + * + * OneNAND detection method: + * Compare the the values from command with ones from register + */ +int onenand_probe(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int dev_id, ver_id; + int density; + int ret; + + ret = this->chip_probe(mtd); + if (ret) + return ret; + + /* Read device IDs from Register */ + dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); + this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); + + /* Flash device information */ + mtd->name = onenand_print_device_info(dev_id, ver_id); + this->device_id = dev_id; + this->version_id = ver_id; + + /* Check OneNAND features */ + onenand_check_features(mtd); + + density = onenand_get_density(dev_id); + if (FLEXONENAND(this)) { + this->dies = ONENAND_IS_DDP(this) ? 2 : 1; + /* Maximum possible erase regions */ + mtd->numeraseregions = this->dies << 1; + mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) + * (this->dies << 1)); + if (!mtd->eraseregions) + return -ENOMEM; + } + + /* + * For Flex-OneNAND, chipsize represents maximum possible device size. + * mtd->size represents the actual device size. + */ + this->chipsize = (16 << density) << 20; + + /* OneNAND page size & block size */ + /* The data buffer size is equal to page size */ + mtd->writesize = + this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); + /* We use the full BufferRAM */ + if (ONENAND_IS_4KB_PAGE(this)) + mtd->writesize <<= 1; + + mtd->oobsize = mtd->writesize >> 5; + /* Pagers per block is always 64 in OneNAND */ + mtd->erasesize = mtd->writesize << 6; + /* + * Flex-OneNAND SLC area has 64 pages per block. + * Flex-OneNAND MLC area has 128 pages per block. + * Expose MLC erase size to find erase_shift and page_mask. + */ + if (FLEXONENAND(this)) + mtd->erasesize <<= 1; + + this->erase_shift = ffs(mtd->erasesize) - 1; + this->page_shift = ffs(mtd->writesize) - 1; + this->ppb_shift = (this->erase_shift - this->page_shift); + this->page_mask = (mtd->erasesize / mtd->writesize) - 1; + /* Set density mask. it is used for DDP */ + if (ONENAND_IS_DDP(this)) + this->density_mask = this->chipsize >> (this->erase_shift + 1); + /* It's real page size */ + this->writesize = mtd->writesize; + + /* REVIST: Multichip handling */ + + if (FLEXONENAND(this)) + flexonenand_get_size(mtd); + else + mtd->size = this->chipsize; + + mtd->flags = MTD_CAP_NANDFLASH; + mtd->_erase = onenand_erase; + mtd->_read = onenand_read; + mtd->_write = onenand_write; + mtd->_read_oob = onenand_read_oob; + mtd->_write_oob = onenand_write_oob; + mtd->_sync = onenand_sync; + mtd->_block_isbad = onenand_block_isbad; + mtd->_block_markbad = onenand_block_markbad; + + return 0; +} + +/** + * onenand_scan - [OneNAND Interface] Scan for the OneNAND device + * @param mtd MTD device structure + * @param maxchips Number of chips to scan for + * + * This fills out all the not initialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + */ +int onenand_scan(struct mtd_info *mtd, int maxchips) +{ + int i; + struct onenand_chip *this = mtd->priv; + + if (!this->read_word) + this->read_word = onenand_readw; + if (!this->write_word) + this->write_word = onenand_writew; + + if (!this->command) + this->command = onenand_command; + if (!this->wait) + this->wait = onenand_wait; + if (!this->bbt_wait) + this->bbt_wait = onenand_bbt_wait; + + if (!this->read_bufferram) + this->read_bufferram = onenand_read_bufferram; + if (!this->write_bufferram) + this->write_bufferram = onenand_write_bufferram; + + if (!this->chip_probe) + this->chip_probe = onenand_chip_probe; + + if (!this->block_markbad) + this->block_markbad = onenand_default_block_markbad; + if (!this->scan_bbt) + this->scan_bbt = onenand_default_bbt; + + if (onenand_probe(mtd)) + return -ENXIO; + + /* Set Sync. Burst Read after probing */ + if (this->mmcontrol) { + printk(KERN_INFO "OneNAND Sync. Burst Read support\n"); + this->read_bufferram = onenand_sync_read_bufferram; + } + + /* Allocate buffers, if necessary */ + if (!this->page_buf) { + this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL); + if (!this->page_buf) { + printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); + return -ENOMEM; + } + this->options |= ONENAND_PAGEBUF_ALLOC; + } + if (!this->oob_buf) { + this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + if (!this->oob_buf) { + printk(KERN_ERR "onenand_scan: Can't allocate oob_buf\n"); + if (this->options & ONENAND_PAGEBUF_ALLOC) { + this->options &= ~ONENAND_PAGEBUF_ALLOC; + kfree(this->page_buf); + } + return -ENOMEM; + } + this->options |= ONENAND_OOBBUF_ALLOC; + } + + this->state = FL_READY; + + /* + * Allow subpage writes up to oobsize. + */ + switch (mtd->oobsize) { + case 128: + this->ecclayout = &onenand_oob_128; + mtd->subpage_sft = 0; + break; + + case 64: + this->ecclayout = &onenand_oob_64; + mtd->subpage_sft = 2; + break; + + case 32: + this->ecclayout = &onenand_oob_32; + mtd->subpage_sft = 1; + break; + + default: + printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n", + mtd->oobsize); + mtd->subpage_sft = 0; + /* To prevent kernel oops */ + this->ecclayout = &onenand_oob_32; + break; + } + + this->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + this->ecclayout->oobavail = 0; + + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && + this->ecclayout->oobfree[i].length; i++) + this->ecclayout->oobavail += + this->ecclayout->oobfree[i].length; + mtd->oobavail = this->ecclayout->oobavail; + + mtd->ecclayout = this->ecclayout; + + /* Unlock whole block */ + onenand_unlock_all(mtd); + + return this->scan_bbt(mtd); +} + +/** + * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device + * @param mtd MTD device structure + */ +void onenand_release(struct mtd_info *mtd) +{ +} diff --git a/qemu/roms/u-boot/drivers/mtd/onenand/onenand_bbt.c b/qemu/roms/u-boot/drivers/mtd/onenand/onenand_bbt.c new file mode 100644 index 000000000..0267c2c5c --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/onenand/onenand_bbt.c @@ -0,0 +1,266 @@ +/* + * linux/drivers/mtd/onenand/onenand_bbt.c + * + * Bad Block Table support for the OneNAND driver + * + * Copyright(c) 2005-2008 Samsung Electronics + * Kyungmin Park + * + * TODO: + * Split BBT core and chip specific BBT. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include + +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @param buf the buffer to search + * @param len the length of buffer to search + * @param paglen the pagelength + * @param td search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check and the pattern is expected to start + * at offset 0. + */ +static int check_short_pattern(uint8_t * buf, int len, int paglen, + struct nand_bbt_descr *td) +{ + int i; + uint8_t *p = buf; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + return 0; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @param mtd MTD device structure + * @param buf temporary buffer + * @param bd descriptor for the good/bad block search pattern + * @param chip create the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device + * for the given good/bad block identify pattern + */ +static int create_bbt(struct mtd_info *mtd, uint8_t * buf, + struct nand_bbt_descr *bd, int chip) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + int i, j, numblocks, len, scanlen; + int startblock; + loff_t from; + size_t readlen, ooblen; + struct mtd_oob_ops ops; + int rgn; + + printk(KERN_INFO "Scanning device for bad blocks\n"); + + len = 1; + + /* We need only read few bytes from the OOB area */ + scanlen = ooblen = 0; + readlen = bd->len; + + /* chip == -1 case only */ + /* Note that numblocks is 2 * (real numblocks) here; + * see i += 2 below as it makses shifting and masking less painful + */ + numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); + startblock = 0; + from = 0; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooblen = readlen; + ops.oobbuf = buf; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + + for (i = startblock; i < numblocks;) { + int ret; + + for (j = 0; j < len; j++) { + /* No need to read pages fully, + * just read required OOB bytes */ + ret = onenand_bbt_read_oob(mtd, + from + j * mtd->writesize + + bd->offs, &ops); + + /* If it is a initial bad block, just ignore it */ + if (ret == ONENAND_BBT_READ_FATAL_ERROR) + return -EIO; + + if (ret || check_short_pattern + (&buf[j * scanlen], scanlen, mtd->writesize, bd)) { + bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk(KERN_WARNING + "Bad eraseblock %d at 0x%08x\n", i >> 1, + (unsigned int)from); + break; + } + } + i += 2; + + if (FLEXONENAND(this)) { + rgn = flexonenand_region(mtd, from); + from += mtd->eraseregions[rgn].erasesize; + } else + from += (1 << bbm->bbt_erase_shift); + } + + return 0; +} + +/** + * onenand_memory_bbt - [GENERIC] create a memory based bad block table + * @param mtd MTD device structure + * @param bd descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device + * for manufacturer / software marked good / bad blocks + */ +static inline int onenand_memory_bbt(struct mtd_info *mtd, + struct nand_bbt_descr *bd) +{ + unsigned char data_buf[MAX_ONENAND_PAGESIZE]; + + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt(mtd, data_buf, bd, -1); +} + +/** + * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad + * @param mtd MTD device structure + * @param offs offset in the device + * @param allowbbt allow access to bad block table region + */ +static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + int block; + uint8_t res; + + /* Get block number * 2 */ + block = (int) (onenand_block(this, offs) << 1); + res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; + + MTDDEBUG (MTD_DEBUG_LEVEL2, + "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block >> 1, res); + + switch ((int)res) { + case 0x00: + return 0; + case 0x01: + return 1; + case 0x02: + return allowbbt ? 0 : 1; + } + + return 1; +} + +/** + * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s) + * @param mtd MTD device structure + * @param bd descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already + * available. If not it scans the device for manufacturer + * marked good / bad blocks and writes the bad block table(s) to + * the selected place. + * + * The bad block table memory is allocated here. It must be freed + * by calling the onenand_free_bbt function. + * + */ +int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + int len, ret = 0; + + len = this->chipsize >> (this->erase_shift + 2); + /* Allocate memory (2bit per block) */ + bbm->bbt = malloc(len); + if (!bbm->bbt) + return -ENOMEM; + /* Clear the memory bad block table */ + memset(bbm->bbt, 0x00, len); + + /* Set the bad block position */ + bbm->badblockpos = ONENAND_BADBLOCK_POS; + + /* Set erase shift */ + bbm->bbt_erase_shift = this->erase_shift; + + if (!bbm->isbad_bbt) + bbm->isbad_bbt = onenand_isbad_bbt; + + /* Scan the device to build a memory based bad block table */ + if ((ret = onenand_memory_bbt(mtd, bd))) { + printk(KERN_ERR + "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n"); + free(bbm->bbt); + bbm->bbt = NULL; + } + + return ret; +} + +/* + * Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. + */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern, +}; + +/** + * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device + * @param mtd MTD device structure + * + * This function selects the default bad block table + * support for the device and calls the onenand_scan_bbt function + */ +int onenand_default_bbt(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm; + + this->bbm = malloc(sizeof(struct bbm_info)); + if (!this->bbm) + return -ENOMEM; + + bbm = this->bbm; + + memset(bbm, 0, sizeof(struct bbm_info)); + + /* 1KB page has same configuration as 2KB page */ + if (!bbm->badblock_pattern) + bbm->badblock_pattern = &largepage_memorybased; + + return onenand_scan_bbt(mtd, bbm->badblock_pattern); +} diff --git a/qemu/roms/u-boot/drivers/mtd/onenand/onenand_spl.c b/qemu/roms/u-boot/drivers/mtd/onenand/onenand_spl.c new file mode 100644 index 000000000..fe6b7d923 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/onenand/onenand_spl.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2011 Marek Vasut + * + * Based on code: + * Copyright (C) 2005-2009 Samsung Electronics + * Kyungmin Park + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* + * Device geometry: + * - 2048b page, 128k erase block. + * - 4096b page, 256k erase block. + */ +enum onenand_spl_pagesize { + PAGE_2K = 2048, + PAGE_4K = 4096, +}; + +#define ONENAND_PAGES_PER_BLOCK 64 +#define onenand_block_address(block) (block) +#define onenand_sector_address(page) (page << 2) +#define onenand_buffer_address() ((1 << 3) << 8) +#define onenand_bufferram_address(block) (0) + +static inline uint16_t onenand_readw(uint32_t addr) +{ + return readw(CONFIG_SYS_ONENAND_BASE + addr); +} + +static inline void onenand_writew(uint16_t value, uint32_t addr) +{ + writew(value, CONFIG_SYS_ONENAND_BASE + addr); +} + +static enum onenand_spl_pagesize onenand_spl_get_geometry(void) +{ + uint32_t dev_id, density; + + if (!onenand_readw(ONENAND_REG_TECHNOLOGY)) { + dev_id = onenand_readw(ONENAND_REG_DEVICE_ID); + density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density &= ONENAND_DEVICE_DENSITY_MASK; + + if (density < ONENAND_DEVICE_DENSITY_4Gb) + return PAGE_2K; + + if (dev_id & ONENAND_DEVICE_IS_DDP) + return PAGE_2K; + } + + return PAGE_4K; +} + +static int onenand_spl_read_page(uint32_t block, uint32_t page, uint32_t *buf, + enum onenand_spl_pagesize pagesize) +{ + const uint32_t addr = CONFIG_SYS_ONENAND_BASE + ONENAND_DATARAM; + uint32_t offset; + + onenand_writew(onenand_block_address(block), + ONENAND_REG_START_ADDRESS1); + + onenand_writew(onenand_bufferram_address(block), + ONENAND_REG_START_ADDRESS2); + + onenand_writew(onenand_sector_address(page), + ONENAND_REG_START_ADDRESS8); + + onenand_writew(onenand_buffer_address(), + ONENAND_REG_START_BUFFER); + + onenand_writew(ONENAND_INT_CLEAR, ONENAND_REG_INTERRUPT); + + onenand_writew(ONENAND_CMD_READ, ONENAND_REG_COMMAND); + + while (!(onenand_readw(ONENAND_REG_INTERRUPT) & ONENAND_INT_READ)) + continue; + + /* Check for invalid block mark */ + if (page < 2 && (onenand_readw(ONENAND_SPARERAM) != 0xffff)) + return 1; + + for (offset = 0; offset < pagesize; offset += 4) + buf[offset / 4] = readl(addr + offset); + + return 0; +} + +void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst) +{ + uint32_t *addr = (uint32_t *)dst; + uint32_t to_page; + uint32_t block; + uint32_t page, rpage; + enum onenand_spl_pagesize pagesize; + int ret; + + pagesize = onenand_spl_get_geometry(); + + /* + * The page can be either 2k or 4k, avoid using DIV_ROUND_UP to avoid + * pulling further unwanted functions into the SPL. + */ + if (pagesize == 2048) { + page = offs / 2048; + to_page = page + DIV_ROUND_UP(size, 2048); + } else { + page = offs / 4096; + to_page = page + DIV_ROUND_UP(size, 4096); + } + + for (; page <= to_page; page++) { + block = page / ONENAND_PAGES_PER_BLOCK; + rpage = page & (ONENAND_PAGES_PER_BLOCK - 1); + ret = onenand_spl_read_page(block, rpage, addr, pagesize); + if (ret) + page += ONENAND_PAGES_PER_BLOCK - 1; + else + addr += pagesize / 4; + } +} diff --git a/qemu/roms/u-boot/drivers/mtd/onenand/onenand_uboot.c b/qemu/roms/u-boot/drivers/mtd/onenand/onenand_uboot.c new file mode 100644 index 000000000..ae60c3bb7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/onenand/onenand_uboot.c @@ -0,0 +1,56 @@ +/* + * drivers/mtd/onenand/onenand_uboot.c + * + * Copyright (C) 2005-2008 Samsung Electronics + * Kyungmin Park + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * OneNAND initialization at U-Boot + */ + +#include +#include +#include +#include + +struct mtd_info onenand_mtd; +struct onenand_chip onenand_chip; +static __attribute__((unused)) char dev_name[] = "onenand0"; + +void onenand_init(void) +{ + memset(&onenand_mtd, 0, sizeof(struct mtd_info)); + memset(&onenand_chip, 0, sizeof(struct onenand_chip)); + + onenand_mtd.priv = &onenand_chip; + +#ifdef CONFIG_USE_ONENAND_BOARD_INIT + /* + * It's used for some board init required + */ + onenand_board_init(&onenand_mtd); +#else + onenand_chip.base = (void *) CONFIG_SYS_ONENAND_BASE; +#endif + + onenand_scan(&onenand_mtd, 1); + + if (onenand_chip.device_id & DEVICE_IS_FLEXONENAND) + puts("Flex-"); + puts("OneNAND: "); + print_size(onenand_chip.chipsize, "\n"); + +#ifdef CONFIG_MTD_DEVICE + /* + * Add MTD device so that we can reference it later + * via the mtdcore infrastructure (e.g. ubi). + */ + onenand_mtd.name = dev_name; + add_mtd_device(&onenand_mtd); +#endif +} diff --git a/qemu/roms/u-boot/drivers/mtd/onenand/samsung.c b/qemu/roms/u-boot/drivers/mtd/onenand/samsung.c new file mode 100644 index 000000000..df04c2bb4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/onenand/samsung.c @@ -0,0 +1,577 @@ +/* + * S5PC100 OneNAND driver at U-Boot + * + * Copyright (C) 2008-2009 Samsung Electronics + * Kyungmin Park + * + * Implementation: + * Emulate the pseudo BufferRAM + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef ONENAND_DEBUG +#define DPRINTK(format, args...) \ +do { \ + printf("%s[%d]: " format "\n", __func__, __LINE__, ##args); \ +} while (0) +#else +#define DPRINTK(...) do { } while (0) +#endif + +#define ONENAND_ERASE_STATUS 0x00 +#define ONENAND_MULTI_ERASE_SET 0x01 +#define ONENAND_ERASE_START 0x03 +#define ONENAND_UNLOCK_START 0x08 +#define ONENAND_UNLOCK_END 0x09 +#define ONENAND_LOCK_START 0x0A +#define ONENAND_LOCK_END 0x0B +#define ONENAND_LOCK_TIGHT_START 0x0C +#define ONENAND_LOCK_TIGHT_END 0x0D +#define ONENAND_UNLOCK_ALL 0x0E +#define ONENAND_OTP_ACCESS 0x12 +#define ONENAND_SPARE_ACCESS_ONLY 0x13 +#define ONENAND_MAIN_ACCESS_ONLY 0x14 +#define ONENAND_ERASE_VERIFY 0x15 +#define ONENAND_MAIN_SPARE_ACCESS 0x16 +#define ONENAND_PIPELINE_READ 0x4000 + +#if defined(CONFIG_S5P) +#define MAP_00 (0x0 << 26) +#define MAP_01 (0x1 << 26) +#define MAP_10 (0x2 << 26) +#define MAP_11 (0x3 << 26) +#endif + +/* read/write of XIP buffer */ +#define CMD_MAP_00(mem_addr) (MAP_00 | ((mem_addr) << 1)) +/* read/write to the memory device */ +#define CMD_MAP_01(mem_addr) (MAP_01 | (mem_addr)) +/* control special functions of the memory device */ +#define CMD_MAP_10(mem_addr) (MAP_10 | (mem_addr)) +/* direct interface(direct access) with the memory device */ +#define CMD_MAP_11(mem_addr) (MAP_11 | ((mem_addr) << 2)) + +struct s3c_onenand { + struct mtd_info *mtd; + void __iomem *base; + void __iomem *ahb_addr; + int bootram_command; + void __iomem *page_buf; + void __iomem *oob_buf; + unsigned int (*mem_addr)(int fba, int fpa, int fsa); + struct samsung_onenand *reg; +}; + +static struct s3c_onenand *onenand; + +static int s3c_read_cmd(unsigned int cmd) +{ + return readl(onenand->ahb_addr + cmd); +} + +static void s3c_write_cmd(int value, unsigned int cmd) +{ + writel(value, onenand->ahb_addr + cmd); +} + +/* + * MEM_ADDR + * + * fba: flash block address + * fpa: flash page address + * fsa: flash sector address + * + * return the buffer address on the memory device + * It will be combined with CMD_MAP_XX + */ +#if defined(CONFIG_S5P) +static unsigned int s3c_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << 13) | (fpa << 7) | (fsa << 5); +} +#endif + +static void s3c_onenand_reset(void) +{ + unsigned long timeout = 0x10000; + int stat; + + writel(ONENAND_MEM_RESET_COLD, &onenand->reg->mem_reset); + while (timeout--) { + stat = readl(&onenand->reg->int_err_stat); + if (stat & RST_CMP) + break; + } + stat = readl(&onenand->reg->int_err_stat); + writel(stat, &onenand->reg->int_err_ack); + + /* Clear interrupt */ + writel(0x0, &onenand->reg->int_err_ack); + /* Clear the ECC status */ + writel(0x0, &onenand->reg->ecc_err_stat); +} + +static unsigned short s3c_onenand_readw(void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + int reg = addr - this->base; + int word_addr = reg >> 1; + int value; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_MANUFACTURER_ID: + return readl(&onenand->reg->manufact_id); + case ONENAND_REG_DEVICE_ID: + return readl(&onenand->reg->device_id); + case ONENAND_REG_VERSION_ID: + return readl(&onenand->reg->flash_ver_id); + case ONENAND_REG_DATA_BUFFER_SIZE: + return readl(&onenand->reg->data_buf_size); + case ONENAND_REG_TECHNOLOGY: + return readl(&onenand->reg->tech); + case ONENAND_REG_SYS_CFG1: + return readl(&onenand->reg->mem_cfg); + + /* Used at unlock all status */ + case ONENAND_REG_CTRL_STATUS: + return 0; + + case ONENAND_REG_WP_STATUS: + return ONENAND_WP_US; + + default: + break; + } + + /* BootRAM access control */ + if (reg < ONENAND_DATARAM && onenand->bootram_command) { + if (word_addr == 0) + return readl(&onenand->reg->manufact_id); + if (word_addr == 1) + return readl(&onenand->reg->device_id); + if (word_addr == 2) + return readl(&onenand->reg->flash_ver_id); + } + + value = s3c_read_cmd(CMD_MAP_11(word_addr)) & 0xffff; + printk(KERN_INFO "s3c_onenand_readw: Illegal access" + " at reg 0x%x, value 0x%x\n", word_addr, value); + return value; +} + +static void s3c_onenand_writew(unsigned short value, void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + int reg = addr - this->base; + int word_addr = reg >> 1; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_SYS_CFG1: + writel(value, &onenand->reg->mem_cfg); + return; + + case ONENAND_REG_START_ADDRESS1: + case ONENAND_REG_START_ADDRESS2: + return; + + /* Lock/lock-tight/unlock/unlock_all */ + case ONENAND_REG_START_BLOCK_ADDRESS: + return; + + default: + break; + } + + /* BootRAM access control */ + if (reg < ONENAND_DATARAM) { + if (value == ONENAND_CMD_READID) { + onenand->bootram_command = 1; + return; + } + if (value == ONENAND_CMD_RESET) { + writel(ONENAND_MEM_RESET_COLD, + &onenand->reg->mem_reset); + onenand->bootram_command = 0; + return; + } + } + + printk(KERN_INFO "s3c_onenand_writew: Illegal access" + " at reg 0x%x, value 0x%x\n", word_addr, value); + + s3c_write_cmd(value, CMD_MAP_11(word_addr)); +} + +static int s3c_onenand_wait(struct mtd_info *mtd, int state) +{ + unsigned int flags = INT_ACT; + unsigned int stat, ecc; + unsigned long timeout = 0x100000; + + switch (state) { + case FL_READING: + flags |= BLK_RW_CMP | LOAD_CMP; + break; + case FL_WRITING: + flags |= BLK_RW_CMP | PGM_CMP; + break; + case FL_ERASING: + flags |= BLK_RW_CMP | ERS_CMP; + break; + case FL_LOCKING: + flags |= BLK_RW_CMP; + break; + default: + break; + } + + while (timeout--) { + stat = readl(&onenand->reg->int_err_stat); + if (stat & flags) + break; + } + + /* To get correct interrupt status in timeout case */ + stat = readl(&onenand->reg->int_err_stat); + writel(stat, &onenand->reg->int_err_ack); + + /* + * In the Spec. it checks the controller status first + * However if you get the correct information in case of + * power off recovery (POR) test, it should read ECC status first + */ + if (stat & LOAD_CMP) { + ecc = readl(&onenand->reg->ecc_err_stat); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + printk(KERN_INFO "%s: ECC error = 0x%04x\n", + __func__, ecc); + mtd->ecc_stats.failed++; + return -EBADMSG; + } + } + + if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) { + printk(KERN_INFO "%s: controller error = 0x%04x\n", + __func__, stat); + if (stat & LOCKED_BLK) + printk(KERN_INFO "%s: it's locked error = 0x%04x\n", + __func__, stat); + + return -EIO; + } + + return 0; +} + +static int s3c_onenand_command(struct mtd_info *mtd, int cmd, + loff_t addr, size_t len) +{ + struct onenand_chip *this = mtd->priv; + unsigned int *m, *s; + int fba, fpa, fsa = 0; + unsigned int mem_addr; + int i, mcount, scount; + int index; + + fba = (int) (addr >> this->erase_shift); + fpa = (int) (addr >> this->page_shift); + fpa &= this->page_mask; + + mem_addr = onenand->mem_addr(fba, fpa, fsa); + + switch (cmd) { + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + case ONENAND_CMD_BUFFERRAM: + ONENAND_SET_NEXT_BUFFERRAM(this); + default: + break; + } + + index = ONENAND_CURRENT_BUFFERRAM(this); + + /* + * Emulate Two BufferRAMs and access with 4 bytes pointer + */ + m = (unsigned int *) onenand->page_buf; + s = (unsigned int *) onenand->oob_buf; + + if (index) { + m += (this->writesize >> 2); + s += (mtd->oobsize >> 2); + } + + mcount = mtd->writesize >> 2; + scount = mtd->oobsize >> 2; + + switch (cmd) { + case ONENAND_CMD_READ: + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(CMD_MAP_01(mem_addr)); + return 0; + + case ONENAND_CMD_READOOB: + writel(TSRF, &onenand->reg->trans_spare); + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(CMD_MAP_01(mem_addr)); + + /* Spare */ + for (i = 0; i < scount; i++) + *s++ = s3c_read_cmd(CMD_MAP_01(mem_addr)); + + writel(0, &onenand->reg->trans_spare); + return 0; + + case ONENAND_CMD_PROG: + /* Main */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(*m++, CMD_MAP_01(mem_addr)); + return 0; + + case ONENAND_CMD_PROGOOB: + writel(TSRF, &onenand->reg->trans_spare); + + /* Main - dummy write */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(0xffffffff, CMD_MAP_01(mem_addr)); + + /* Spare */ + for (i = 0; i < scount; i++) + s3c_write_cmd(*s++, CMD_MAP_01(mem_addr)); + + writel(0, &onenand->reg->trans_spare); + return 0; + + case ONENAND_CMD_UNLOCK_ALL: + s3c_write_cmd(ONENAND_UNLOCK_ALL, CMD_MAP_10(mem_addr)); + return 0; + + case ONENAND_CMD_ERASE: + s3c_write_cmd(ONENAND_ERASE_START, CMD_MAP_10(mem_addr)); + return 0; + + case ONENAND_CMD_MULTIBLOCK_ERASE: + s3c_write_cmd(ONENAND_MULTI_ERASE_SET, CMD_MAP_10(mem_addr)); + return 0; + + case ONENAND_CMD_ERASE_VERIFY: + s3c_write_cmd(ONENAND_ERASE_VERIFY, CMD_MAP_10(mem_addr)); + return 0; + + default: + break; + } + + return 0; +} + +static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area) +{ + struct onenand_chip *this = mtd->priv; + int index = ONENAND_CURRENT_BUFFERRAM(this); + unsigned char *p; + + if (area == ONENAND_DATARAM) { + p = (unsigned char *) onenand->page_buf; + if (index == 1) + p += this->writesize; + } else { + p = (unsigned char *) onenand->oob_buf; + if (index == 1) + p += mtd->oobsize; + } + + return p; +} + +static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area, + unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(buffer, p + offset, count); + return 0; +} + +static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area, + const unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(p + offset, buffer, count); + return 0; +} + +static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + struct samsung_onenand *reg = (struct samsung_onenand *)onenand->base; + unsigned int flags = INT_ACT | LOAD_CMP; + unsigned int stat; + unsigned long timeout = 0x10000; + + while (timeout--) { + stat = readl(®->int_err_stat); + if (stat & flags) + break; + } + /* To get correct interrupt status in timeout case */ + stat = readl(&onenand->reg->int_err_stat); + writel(stat, &onenand->reg->int_err_ack); + + if (stat & LD_FAIL_ECC_ERR) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + + if (stat & LOAD_CMP) { + int ecc = readl(&onenand->reg->ecc_err_stat); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + } + + return 0; +} + +static void s3c_onenand_check_lock_status(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned int block, end; + + end = this->chipsize >> this->erase_shift; + + for (block = 0; block < end; block++) { + s3c_read_cmd(CMD_MAP_01(onenand->mem_addr(block, 0, 0))); + + if (readl(&onenand->reg->int_err_stat) & LOCKED_BLK) { + printf("block %d is write-protected!\n", block); + writel(LOCKED_BLK, &onenand->reg->int_err_ack); + } + } +} + +static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, + size_t len, int cmd) +{ + struct onenand_chip *this = mtd->priv; + int start, end, start_mem_addr, end_mem_addr; + + start = ofs >> this->erase_shift; + start_mem_addr = onenand->mem_addr(start, 0, 0); + end = start + (len >> this->erase_shift) - 1; + end_mem_addr = onenand->mem_addr(end, 0, 0); + + if (cmd == ONENAND_CMD_LOCK) { + s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(start_mem_addr)); + s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(end_mem_addr)); + } else { + s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(start_mem_addr)); + s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(end_mem_addr)); + } + + this->wait(mtd, FL_LOCKING); +} + +static void s3c_onenand_unlock_all(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + loff_t ofs = 0; + size_t len = this->chipsize; + + /* FIXME workaround */ + this->subpagesize = mtd->writesize; + mtd->subpage_sft = 0; + + if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + + /* No need to check return value */ + this->wait(mtd, FL_LOCKING); + + /* Workaround for all block unlock in DDP */ + if (!ONENAND_IS_DDP(this)) { + s3c_onenand_check_lock_status(mtd); + return; + } + + /* All blocks on another chip */ + ofs = this->chipsize >> 1; + len = this->chipsize >> 1; + } + + s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); + s3c_onenand_check_lock_status(mtd); +} + +int s5pc110_chip_probe(struct mtd_info *mtd) +{ + return 0; +} + +int s5pc210_chip_probe(struct mtd_info *mtd) +{ + return 0; +} + +void s3c_onenand_init(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + u32 size = (4 << 10); /* 4 KiB */ + + onenand = malloc(sizeof(struct s3c_onenand)); + if (!onenand) + return; + + onenand->page_buf = malloc(size * sizeof(char)); + if (!onenand->page_buf) + return; + memset(onenand->page_buf, 0xff, size); + + onenand->oob_buf = malloc(128 * sizeof(char)); + if (!onenand->oob_buf) + return; + memset(onenand->oob_buf, 0xff, 128); + + onenand->mtd = mtd; + +#if defined(CONFIG_S5P) + onenand->base = (void *)0xE7100000; + onenand->ahb_addr = (void *)0xB0000000; +#endif + onenand->mem_addr = s3c_mem_addr; + onenand->reg = (struct samsung_onenand *)onenand->base; + + this->read_word = s3c_onenand_readw; + this->write_word = s3c_onenand_writew; + + this->wait = s3c_onenand_wait; + this->bbt_wait = s3c_onenand_bbt_wait; + this->unlock_all = s3c_onenand_unlock_all; + this->command = s3c_onenand_command; + + this->read_bufferram = onenand_read_bufferram; + this->write_bufferram = onenand_write_bufferram; + + this->options |= ONENAND_RUNTIME_BADBLOCK_CHECK; +} diff --git a/qemu/roms/u-boot/drivers/mtd/spi/Makefile b/qemu/roms/u-boot/drivers/mtd/spi/Makefile new file mode 100644 index 000000000..9e18fb41d --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/Makefile @@ -0,0 +1,17 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o +obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o +endif + +obj-$(CONFIG_CMD_SF) += sf.o +obj-$(CONFIG_SPI_FLASH) += sf_params.o sf_probe.o sf_ops.o +obj-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o +obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o +obj-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o diff --git a/qemu/roms/u-boot/drivers/mtd/spi/eeprom_m95xxx.c b/qemu/roms/u-boot/drivers/mtd/spi/eeprom_m95xxx.c new file mode 100644 index 000000000..a019939b8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/eeprom_m95xxx.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 + * Albin Tonnerre, Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#define SPI_EEPROM_WREN 0x06 +#define SPI_EEPROM_RDSR 0x05 +#define SPI_EEPROM_READ 0x03 +#define SPI_EEPROM_WRITE 0x02 + +#ifndef CONFIG_DEFAULT_SPI_BUS +#define CONFIG_DEFAULT_SPI_BUS 0 +#endif + +#ifndef CONFIG_DEFAULT_SPI_MODE +#define CONFIG_DEFAULT_SPI_MODE SPI_MODE_0 +#endif + +#ifndef CONFIG_SYS_SPI_WRITE_TOUT +#define CONFIG_SYS_SPI_WRITE_TOUT (5 * CONFIG_SYS_HZ) +#endif + +ssize_t spi_read(uchar *addr, int alen, uchar *buffer, int len) +{ + struct spi_slave *slave; + u8 cmd = SPI_EEPROM_READ; + + slave = spi_setup_slave(CONFIG_DEFAULT_SPI_BUS, 1, 1000000, + CONFIG_DEFAULT_SPI_MODE); + if (!slave) + return 0; + + spi_claim_bus(slave); + + /* command */ + if (spi_xfer(slave, 8, &cmd, NULL, SPI_XFER_BEGIN)) + return -1; + + /* + * if alen == 3, addr[0] is the block number, we never use it here. + * All we need are the lower 16 bits. + */ + if (alen == 3) + addr++; + + /* address, and data */ + if (spi_xfer(slave, 16, addr, NULL, 0)) + return -1; + if (spi_xfer(slave, 8 * len, NULL, buffer, SPI_XFER_END)) + return -1; + + spi_release_bus(slave); + spi_free_slave(slave); + return len; +} + +ssize_t spi_write(uchar *addr, int alen, uchar *buffer, int len) +{ + struct spi_slave *slave; + char buf[3]; + ulong start; + + slave = spi_setup_slave(CONFIG_DEFAULT_SPI_BUS, 1, 1000000, + CONFIG_DEFAULT_SPI_MODE); + if (!slave) + return 0; + + spi_claim_bus(slave); + + buf[0] = SPI_EEPROM_WREN; + if (spi_xfer(slave, 8, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END)) + return -1; + + buf[0] = SPI_EEPROM_WRITE; + + /* As for reading, drop addr[0] if alen is 3 */ + if (alen == 3) { + alen--; + addr++; + } + + memcpy(buf + 1, addr, alen); + /* command + addr, then data */ + if (spi_xfer(slave, 24, buf, NULL, SPI_XFER_BEGIN)) + return -1; + if (spi_xfer(slave, len * 8, buffer, NULL, SPI_XFER_END)) + return -1; + + start = get_timer(0); + do { + buf[0] = SPI_EEPROM_RDSR; + buf[1] = 0; + spi_xfer(slave, 16, buf, buf, SPI_XFER_BEGIN | SPI_XFER_END); + + if (!(buf[1] & 1)) + break; + + } while (get_timer(start) < CONFIG_SYS_SPI_WRITE_TOUT); + + if (buf[1] & 1) + printf("*** spi_write: Timeout while writing!\n"); + + spi_release_bus(slave); + spi_free_slave(slave); + return len; +} diff --git a/qemu/roms/u-boot/drivers/mtd/spi/fsl_espi_spl.c b/qemu/roms/u-boot/drivers/mtd/spi/fsl_espi_spl.c new file mode 100644 index 000000000..b915469b4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/fsl_espi_spl.c @@ -0,0 +1,90 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define ESPI_BOOT_IMAGE_SIZE 0x48 +#define ESPI_BOOT_IMAGE_ADDR 0x50 +#define CONFIG_CFG_DATA_SECTOR 0 + +void spi_spl_load_image(uint32_t offs, unsigned int size, void *vdst) +{ + struct spi_flash *flash; + + flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (flash == NULL) { + puts("\nspi_flash_probe failed"); + hang(); + } + + spi_flash_read(flash, offs, size, vdst); +} + +/* + * The main entry for SPI booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from SPI into SDRAM and starts it from there. + */ +void spi_boot(void) +{ + void (*uboot)(void) __noreturn; + u32 offset, code_len, copy_len = 0; +#ifndef CONFIG_FSL_CORENET + unsigned char *buf = NULL; +#endif + struct spi_flash *flash; + + flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (flash == NULL) { + puts("\nspi_flash_probe failed"); + hang(); + } + +#ifdef CONFIG_FSL_CORENET + offset = CONFIG_SYS_SPI_FLASH_U_BOOT_OFFS; + code_len = CONFIG_SYS_SPI_FLASH_U_BOOT_SIZE; +#else + /* + * Load U-Boot image from SPI flash into RAM + */ + buf = malloc(flash->page_size); + if (buf == NULL) { + puts("\nmalloc failed"); + hang(); + } + memset(buf, 0, flash->page_size); + + spi_flash_read(flash, CONFIG_CFG_DATA_SECTOR, + flash->page_size, (void *)buf); + offset = *(u32 *)(buf + ESPI_BOOT_IMAGE_ADDR); + /* Skip spl code */ + offset += CONFIG_SYS_SPI_FLASH_U_BOOT_OFFS; + /* Get the code size from offset 0x48 */ + code_len = *(u32 *)(buf + ESPI_BOOT_IMAGE_SIZE); + /* Skip spl code */ + code_len = code_len - CONFIG_SPL_MAX_SIZE; +#endif + /* copy code to DDR */ + printf("Loading second stage boot loader "); + while (copy_len <= code_len) { + spi_flash_read(flash, offset + copy_len, 0x2000, + (void *)(CONFIG_SYS_SPI_FLASH_U_BOOT_DST + + copy_len)); + copy_len = copy_len + 0x2000; + putc('.'); + } + + /* + * Jump to U-Boot image + */ + flush_cache(CONFIG_SYS_SPI_FLASH_U_BOOT_DST, code_len); + uboot = (void *)CONFIG_SYS_SPI_FLASH_U_BOOT_START; + (*uboot)(); +} diff --git a/qemu/roms/u-boot/drivers/mtd/spi/ramtron.c b/qemu/roms/u-boot/drivers/mtd/spi/ramtron.c new file mode 100644 index 000000000..d50da37c8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/ramtron.c @@ -0,0 +1,403 @@ +/* + * (C) Copyright 2010 + * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Note: RAMTRON SPI FRAMs are ferroelectric, nonvolatile RAMs + * with an interface identical to SPI flash devices. + * However since they behave like RAM there are no delays or + * busy polls required. They can sustain read or write at the + * allowed SPI bus speed, which can be 40 MHz for some devices. + * + * Unfortunately some RAMTRON devices do not have a means of + * identifying them. They will leave the SO line undriven when + * the READ-ID command is issued. It is therefore mandatory + * that the MISO line has a proper pull-up, so that READ-ID + * will return a row of 0xff. This 0xff pseudo-id will cause + * probes by all vendor specific functions that are designed + * to handle it. If the MISO line is not pulled up, READ-ID + * could return any random noise, even mimicking another + * device. + * + * We use CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC + * to define which device will be assumed after a simple status + * register verify. This method is prone to false positive + * detection and should therefore be the last to be tried. + * Enter it in the last position in the table in spi_flash.c! + * + * The define CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC both activates + * compilation of the special handler and defines the device + * to assume. + */ + +#include +#include +#include +#include "sf_internal.h" + +/* + * Properties of supported FRAMs + * Note: speed is currently not used because we have no method to deliver that + * value to the upper layers + */ +struct ramtron_spi_fram_params { + u32 size; /* size in bytes */ + u8 addr_len; /* number of address bytes */ + u8 merge_cmd; /* some address bits are in the command byte */ + u8 id1; /* device ID 1 (family, density) */ + u8 id2; /* device ID 2 (sub, rev, rsvd) */ + u32 speed; /* max. SPI clock in Hz */ + const char *name; /* name for display and/or matching */ +}; + +struct ramtron_spi_fram { + struct spi_flash flash; + const struct ramtron_spi_fram_params *params; +}; + +static inline struct ramtron_spi_fram *to_ramtron_spi_fram(struct spi_flash + *flash) +{ + return container_of(flash, struct ramtron_spi_fram, flash); +} + +/* + * table describing supported FRAM chips: + * chips without RDID command must have the values 0xff for id1 and id2 + */ +static const struct ramtron_spi_fram_params ramtron_spi_fram_table[] = { + { + .size = 32*1024, + .addr_len = 2, + .merge_cmd = 0, + .id1 = 0x22, + .id2 = 0x00, + .speed = 40000000, + .name = "FM25V02", + }, + { + .size = 32*1024, + .addr_len = 2, + .merge_cmd = 0, + .id1 = 0x22, + .id2 = 0x01, + .speed = 40000000, + .name = "FM25VN02", + }, + { + .size = 64*1024, + .addr_len = 2, + .merge_cmd = 0, + .id1 = 0x23, + .id2 = 0x00, + .speed = 40000000, + .name = "FM25V05", + }, + { + .size = 64*1024, + .addr_len = 2, + .merge_cmd = 0, + .id1 = 0x23, + .id2 = 0x01, + .speed = 40000000, + .name = "FM25VN05", + }, + { + .size = 128*1024, + .addr_len = 3, + .merge_cmd = 0, + .id1 = 0x24, + .id2 = 0x00, + .speed = 40000000, + .name = "FM25V10", + }, + { + .size = 128*1024, + .addr_len = 3, + .merge_cmd = 0, + .id1 = 0x24, + .id2 = 0x01, + .speed = 40000000, + .name = "FM25VN10", + }, +#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC + { + .size = 256*1024, + .addr_len = 3, + .merge_cmd = 0, + .id1 = 0xff, + .id2 = 0xff, + .speed = 40000000, + .name = "FM25H20", + }, +#endif +}; + +static int ramtron_common(struct spi_flash *flash, + u32 offset, size_t len, void *buf, u8 command) +{ + struct ramtron_spi_fram *sn = to_ramtron_spi_fram(flash); + u8 cmd[4]; + int cmd_len; + int ret; + + if (sn->params->addr_len == 3 && sn->params->merge_cmd == 0) { + cmd[0] = command; + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + cmd[3] = offset; + cmd_len = 4; + } else if (sn->params->addr_len == 2 && sn->params->merge_cmd == 0) { + cmd[0] = command; + cmd[1] = offset >> 8; + cmd[2] = offset; + cmd_len = 3; + } else { + printf("SF: unsupported addr_len or merge_cmd\n"); + return -1; + } + + /* claim the bus */ + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + if (command == CMD_PAGE_PROGRAM) { + /* send WREN */ + ret = spi_flash_cmd_write_enable(flash); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + goto releasebus; + } + } + + /* do the transaction */ + if (command == CMD_PAGE_PROGRAM) + ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, buf, len); + else + ret = spi_flash_cmd_read(flash->spi, cmd, cmd_len, buf, len); + if (ret < 0) + debug("SF: Transaction failed\n"); + +releasebus: + /* release the bus */ + spi_release_bus(flash->spi); + return ret; +} + +static int ramtron_read(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + return ramtron_common(flash, offset, len, buf, + CMD_READ_ARRAY_SLOW); +} + +static int ramtron_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + return ramtron_common(flash, offset, len, (void *)buf, + CMD_PAGE_PROGRAM); +} + +static int ramtron_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + debug("SF: Erase of RAMTRON FRAMs is pointless\n"); + return -1; +} + +/* + * nore: we are called here with idcode pointing to the first non-0x7f byte + * already! + */ +static struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, + u8 *idcode) +{ + const struct ramtron_spi_fram_params *params; + struct ramtron_spi_fram *sn; + unsigned int i; +#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC + int ret; + u8 sr; +#endif + + /* NOTE: the bus has been claimed before this function is called! */ + switch (idcode[0]) { + case 0xc2: + /* JEDEC conformant RAMTRON id */ + for (i = 0; i < ARRAY_SIZE(ramtron_spi_fram_table); i++) { + params = &ramtron_spi_fram_table[i]; + if (idcode[1] == params->id1 && + idcode[2] == params->id2) + goto found; + } + break; +#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC + case 0xff: + /* + * probably open MISO line, pulled up. + * We COULD have a non JEDEC conformant FRAM here, + * read the status register to verify + */ + ret = spi_flash_cmd(spi, CMD_READ_STATUS, &sr, 1); + if (ret) + return NULL; + + /* Bits 5,4,0 are fixed 0 for all devices */ + if ((sr & 0x31) != 0x00) + return NULL; + /* now find the device */ + for (i = 0; i < ARRAY_SIZE(ramtron_spi_fram_table); i++) { + params = &ramtron_spi_fram_table[i]; + if (!strcmp(params->name, + CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC)) + goto found; + } + debug("SF: Unsupported non-JEDEC RAMTRON device " + CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC "\n"); + break; +#endif + default: + break; + } + + /* arriving here means no method has found a device we can handle */ + debug("SF/ramtron: unsupported device id0=%02x id1=%02x id2=%02x\n", + idcode[0], idcode[1], idcode[2]); + return NULL; + +found: + sn = malloc(sizeof(*sn)); + if (!sn) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + sn->params = params; + + sn->flash.write = ramtron_write; + sn->flash.read = ramtron_read; + sn->flash.erase = ramtron_erase; + sn->flash.size = params->size; + + return &sn->flash; +} + +/* + * The following table holds all device probe functions + * (All flashes are removed and implemented a common probe at + * spi_flash_probe.c) + * + * shift: number of continuation bytes before the ID + * idcode: the expected IDCODE or 0xff for non JEDEC devices + * probe: the function to call + * + * Non JEDEC devices should be ordered in the table such that + * the probe functions with best detection algorithms come first. + * + * Several matching entries are permitted, they will be tried + * in sequence until a probe function returns non NULL. + * + * IDCODE_CONT_LEN may be redefined if a device needs to declare a + * larger "shift" value. IDCODE_PART_LEN generally shouldn't be + * changed. This is the max number of bytes probe functions may + * examine when looking up part-specific identification info. + * + * Probe functions will be given the idcode buffer starting at their + * manu id byte (the "idcode" in the table below). In other words, + * all of the continuation bytes will be skipped (the "shift" below). + */ +#define IDCODE_CONT_LEN 0 +#define IDCODE_PART_LEN 5 +static const struct { + const u8 shift; + const u8 idcode; + struct spi_flash *(*probe) (struct spi_slave *spi, u8 *idcode); +} flashes[] = { + /* Keep it sorted by define name */ +#ifdef CONFIG_SPI_FRAM_RAMTRON + { 6, 0xc2, spi_fram_probe_ramtron, }, +# undef IDCODE_CONT_LEN +# define IDCODE_CONT_LEN 6 +#endif +#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC + { 0, 0xff, spi_fram_probe_ramtron, }, +#endif +}; +#define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN) + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *spi; + struct spi_flash *flash = NULL; + int ret, i, shift; + u8 idcode[IDCODE_LEN], *idp; + + spi = spi_setup_slave(bus, cs, max_hz, spi_mode); + if (!spi) { + printf("SF: Failed to set up slave\n"); + return NULL; + } + + ret = spi_claim_bus(spi); + if (ret) { + debug("SF: Failed to claim SPI bus: %d\n", ret); + goto err_claim_bus; + } + + /* Read the ID codes */ + ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); + if (ret) + goto err_read_id; + +#ifdef DEBUG + printf("SF: Got idcodes\n"); + print_buffer(0, idcode, 1, sizeof(idcode), 0); +#endif + + /* count the number of continuation bytes */ + for (shift = 0, idp = idcode; + shift < IDCODE_CONT_LEN && *idp == 0x7f; + ++shift, ++idp) + continue; + + /* search the table for matches in shift and id */ + for (i = 0; i < ARRAY_SIZE(flashes); ++i) + if (flashes[i].shift == shift && flashes[i].idcode == *idp) { + /* we have a match, call probe */ + flash = flashes[i].probe(spi, idp); + if (flash) + break; + } + + if (!flash) { + printf("SF: Unsupported manufacturer %02x\n", *idp); + goto err_manufacturer_probe; + } + + printf("SF: Detected %s with total size ", flash->name); + print_size(flash->size, ""); + puts("\n"); + + spi_release_bus(spi); + + return flash; + +err_manufacturer_probe: +err_read_id: + spi_release_bus(spi); +err_claim_bus: + spi_free_slave(spi); + return NULL; +} + +void spi_flash_free(struct spi_flash *flash) +{ + spi_free_slave(flash->spi); + free(flash); +} diff --git a/qemu/roms/u-boot/drivers/mtd/spi/sandbox.c b/qemu/roms/u-boot/drivers/mtd/spi/sandbox.c new file mode 100644 index 000000000..a62ef4cbb --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/sandbox.c @@ -0,0 +1,483 @@ +/* + * Simulate a SPI flash + * + * Copyright (c) 2011-2013 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include + +#include +#include "sf_internal.h" + +#include +#include +#include + +/* + * The different states that our SPI flash transitions between. + * We need to keep track of this across multiple xfer calls since + * the SPI bus could possibly call down into us multiple times. + */ +enum sandbox_sf_state { + SF_CMD, /* default state -- we're awaiting a command */ + SF_ID, /* read the flash's (jedec) ID code */ + SF_ADDR, /* processing the offset in the flash to read/etc... */ + SF_READ, /* reading data from the flash */ + SF_WRITE, /* writing data to the flash, i.e. page programming */ + SF_ERASE, /* erase the flash */ + SF_READ_STATUS, /* read the flash's status register */ + SF_READ_STATUS1, /* read the flash's status register upper 8 bits*/ +}; + +static const char *sandbox_sf_state_name(enum sandbox_sf_state state) +{ + static const char * const states[] = { + "CMD", "ID", "ADDR", "READ", "WRITE", "ERASE", "READ_STATUS", + }; + return states[state]; +} + +/* Bits for the status register */ +#define STAT_WIP (1 << 0) +#define STAT_WEL (1 << 1) + +/* Assume all SPI flashes have 3 byte addresses since they do atm */ +#define SF_ADDR_LEN 3 + +struct sandbox_spi_flash_erase_commands { + u8 cmd; + u32 size; +}; +#define IDCODE_LEN 5 +#define MAX_ERASE_CMDS 3 +struct sandbox_spi_flash_data { + const char *name; + u8 idcode[IDCODE_LEN]; + u32 size; + const struct sandbox_spi_flash_erase_commands + erase_cmds[MAX_ERASE_CMDS]; +}; + +/* Structure describing all the flashes we know how to emulate */ +static const struct sandbox_spi_flash_data sandbox_sf_flashes[] = { + { + "M25P16", { 0x20, 0x20, 0x15 }, (2 << 20), + { /* erase commands */ + { 0xd8, (64 << 10), }, /* sector */ + { 0xc7, (2 << 20), }, /* bulk */ + }, + }, + { + "W25Q32", { 0xef, 0x40, 0x16 }, (4 << 20), + { /* erase commands */ + { 0x20, (4 << 10), }, /* 4KB */ + { 0xd8, (64 << 10), }, /* sector */ + { 0xc7, (4 << 20), }, /* bulk */ + }, + }, + { + "W25Q128", { 0xef, 0x40, 0x18 }, (16 << 20), + { /* erase commands */ + { 0x20, (4 << 10), }, /* 4KB */ + { 0xd8, (64 << 10), }, /* sector */ + { 0xc7, (16 << 20), }, /* bulk */ + }, + }, +}; + +/* Used to quickly bulk erase backing store */ +static u8 sandbox_sf_0xff[0x1000]; + +/* Internal state data for each SPI flash */ +struct sandbox_spi_flash { + /* + * As we receive data over the SPI bus, our flash transitions + * between states. For example, we start off in the SF_CMD + * state where the first byte tells us what operation to perform + * (such as read or write the flash). But the operation itself + * can go through a few states such as first reading in the + * offset in the flash to perform the requested operation. + * Thus "state" stores the exact state that our machine is in + * while "cmd" stores the overall command we're processing. + */ + enum sandbox_sf_state state; + uint cmd; + const void *cmd_data; + /* Current position in the flash; used when reading/writing/etc... */ + uint off; + /* How many address bytes we've consumed */ + uint addr_bytes, pad_addr_bytes; + /* The current flash status (see STAT_XXX defines above) */ + u16 status; + /* Data describing the flash we're emulating */ + const struct sandbox_spi_flash_data *data; + /* The file on disk to serv up data from */ + int fd; +}; + +static int sandbox_sf_setup(void **priv, const char *spec) +{ + /* spec = idcode:file */ + struct sandbox_spi_flash *sbsf; + const char *file; + size_t i, len, idname_len; + const struct sandbox_spi_flash_data *data; + + file = strchr(spec, ':'); + if (!file) { + printf("sandbox_sf: unable to parse file\n"); + goto error; + } + idname_len = file - spec; + ++file; + + for (i = 0; i < ARRAY_SIZE(sandbox_sf_flashes); ++i) { + data = &sandbox_sf_flashes[i]; + len = strlen(data->name); + if (idname_len != len) + continue; + if (!memcmp(spec, data->name, len)) + break; + } + if (i == ARRAY_SIZE(sandbox_sf_flashes)) { + printf("sandbox_sf: unknown flash '%*s'\n", (int)idname_len, + spec); + goto error; + } + + if (sandbox_sf_0xff[0] == 0x00) + memset(sandbox_sf_0xff, 0xff, sizeof(sandbox_sf_0xff)); + + sbsf = calloc(sizeof(*sbsf), 1); + if (!sbsf) { + printf("sandbox_sf: out of memory\n"); + goto error; + } + + sbsf->fd = os_open(file, 02); + if (sbsf->fd == -1) { + free(sbsf); + printf("sandbox_sf: unable to open file '%s'\n", file); + goto error; + } + + sbsf->data = data; + + *priv = sbsf; + return 0; + + error: + return 1; +} + +static void sandbox_sf_free(void *priv) +{ + struct sandbox_spi_flash *sbsf = priv; + + os_close(sbsf->fd); + free(sbsf); +} + +static void sandbox_sf_cs_activate(void *priv) +{ + struct sandbox_spi_flash *sbsf = priv; + + debug("sandbox_sf: CS activated; state is fresh!\n"); + + /* CS is asserted, so reset state */ + sbsf->off = 0; + sbsf->addr_bytes = 0; + sbsf->pad_addr_bytes = 0; + sbsf->state = SF_CMD; + sbsf->cmd = SF_CMD; +} + +static void sandbox_sf_cs_deactivate(void *priv) +{ + debug("sandbox_sf: CS deactivated; cmd done processing!\n"); +} + +/* Figure out what command this stream is telling us to do */ +static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, + u8 *tx) +{ + enum sandbox_sf_state oldstate = sbsf->state; + + /* We need to output a byte for the cmd byte we just ate */ + sandbox_spi_tristate(tx, 1); + + sbsf->cmd = rx[0]; + switch (sbsf->cmd) { + case CMD_READ_ID: + sbsf->state = SF_ID; + sbsf->cmd = SF_ID; + break; + case CMD_READ_ARRAY_FAST: + sbsf->pad_addr_bytes = 1; + case CMD_READ_ARRAY_SLOW: + case CMD_PAGE_PROGRAM: + state_addr: + sbsf->state = SF_ADDR; + break; + case CMD_WRITE_DISABLE: + debug(" write disabled\n"); + sbsf->status &= ~STAT_WEL; + break; + case CMD_READ_STATUS: + sbsf->state = SF_READ_STATUS; + break; + case CMD_READ_STATUS1: + sbsf->state = SF_READ_STATUS1; + break; + case CMD_WRITE_ENABLE: + debug(" write enabled\n"); + sbsf->status |= STAT_WEL; + break; + default: { + size_t i; + + /* handle erase commands first */ + for (i = 0; i < MAX_ERASE_CMDS; ++i) { + const struct sandbox_spi_flash_erase_commands * + erase_cmd = &sbsf->data->erase_cmds[i]; + + if (erase_cmd->cmd == 0x00) + continue; + if (sbsf->cmd != erase_cmd->cmd) + continue; + + sbsf->cmd_data = erase_cmd; + goto state_addr; + } + + debug(" cmd unknown: %#x\n", sbsf->cmd); + return 1; + } + } + + if (oldstate != sbsf->state) + debug(" cmd: transition to %s state\n", + sandbox_sf_state_name(sbsf->state)); + + return 0; +} + +int sandbox_erase_part(struct sandbox_spi_flash *sbsf, int size) +{ + int todo; + int ret; + + while (size > 0) { + todo = min(size, sizeof(sandbox_sf_0xff)); + ret = os_write(sbsf->fd, sandbox_sf_0xff, todo); + if (ret != todo) + return ret; + size -= todo; + } + + return 0; +} + +static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, + uint bytes) +{ + struct sandbox_spi_flash *sbsf = priv; + uint cnt, pos = 0; + int ret; + + debug("sandbox_sf: state:%x(%s) bytes:%u\n", sbsf->state, + sandbox_sf_state_name(sbsf->state), bytes); + + if (sbsf->state == SF_CMD) { + /* Figure out the initial state */ + if (sandbox_sf_process_cmd(sbsf, rx, tx)) + return 1; + ++pos; + } + + /* Process the remaining data */ + while (pos < bytes) { + switch (sbsf->state) { + case SF_ID: { + u8 id; + + debug(" id: off:%u tx:", sbsf->off); + if (sbsf->off < IDCODE_LEN) + id = sbsf->data->idcode[sbsf->off]; + else + id = 0; + debug("%02x\n", id); + tx[pos++] = id; + ++sbsf->off; + break; + } + case SF_ADDR: + debug(" addr: bytes:%u rx:%02x ", sbsf->addr_bytes, + rx[pos]); + + if (sbsf->addr_bytes++ < SF_ADDR_LEN) + sbsf->off = (sbsf->off << 8) | rx[pos]; + debug("addr:%06x\n", sbsf->off); + + sandbox_spi_tristate(&tx[pos++], 1); + + /* See if we're done processing */ + if (sbsf->addr_bytes < + SF_ADDR_LEN + sbsf->pad_addr_bytes) + break; + + /* Next state! */ + if (os_lseek(sbsf->fd, sbsf->off, OS_SEEK_SET) < 0) { + puts("sandbox_sf: os_lseek() failed"); + return 1; + } + switch (sbsf->cmd) { + case CMD_READ_ARRAY_FAST: + case CMD_READ_ARRAY_SLOW: + sbsf->state = SF_READ; + break; + case CMD_PAGE_PROGRAM: + sbsf->state = SF_WRITE; + break; + default: + /* assume erase state ... */ + sbsf->state = SF_ERASE; + goto case_sf_erase; + } + debug(" cmd: transition to %s state\n", + sandbox_sf_state_name(sbsf->state)); + break; + case SF_READ: + /* + * XXX: need to handle exotic behavior: + * - reading past end of device + */ + + cnt = bytes - pos; + debug(" tx: read(%u)\n", cnt); + ret = os_read(sbsf->fd, tx + pos, cnt); + if (ret < 0) { + puts("sandbox_spi: os_read() failed\n"); + return 1; + } + pos += ret; + break; + case SF_READ_STATUS: + debug(" read status: %#x\n", sbsf->status); + cnt = bytes - pos; + memset(tx + pos, sbsf->status, cnt); + pos += cnt; + break; + case SF_READ_STATUS1: + debug(" read status: %#x\n", sbsf->status); + cnt = bytes - pos; + memset(tx + pos, sbsf->status >> 8, cnt); + pos += cnt; + break; + case SF_WRITE: + /* + * XXX: need to handle exotic behavior: + * - unaligned addresses + * - more than a page (256) worth of data + * - reading past end of device + */ + if (!(sbsf->status & STAT_WEL)) { + puts("sandbox_sf: write enable not set before write\n"); + goto done; + } + + cnt = bytes - pos; + debug(" rx: write(%u)\n", cnt); + sandbox_spi_tristate(&tx[pos], cnt); + ret = os_write(sbsf->fd, rx + pos, cnt); + if (ret < 0) { + puts("sandbox_spi: os_write() failed\n"); + return 1; + } + pos += ret; + sbsf->status &= ~STAT_WEL; + break; + case SF_ERASE: + case_sf_erase: { + const struct sandbox_spi_flash_erase_commands * + erase_cmd = sbsf->cmd_data; + + if (!(sbsf->status & STAT_WEL)) { + puts("sandbox_sf: write enable not set before erase\n"); + goto done; + } + + /* verify address is aligned */ + if (sbsf->off & (erase_cmd->size - 1)) { + debug(" sector erase: cmd:%#x needs align:%#x, but we got %#x\n", + erase_cmd->cmd, erase_cmd->size, + sbsf->off); + sbsf->status &= ~STAT_WEL; + goto done; + } + + debug(" sector erase addr: %u\n", sbsf->off); + + cnt = bytes - pos; + sandbox_spi_tristate(&tx[pos], cnt); + pos += cnt; + + /* + * TODO(vapier@gentoo.org): latch WIP in status, and + * delay before clearing it ? + */ + ret = sandbox_erase_part(sbsf, erase_cmd->size); + sbsf->status &= ~STAT_WEL; + if (ret) { + debug("sandbox_sf: Erase failed\n"); + goto done; + } + goto done; + } + default: + debug(" ??? no idea what to do ???\n"); + goto done; + } + } + + done: + return pos == bytes ? 0 : 1; +} + +static const struct sandbox_spi_emu_ops sandbox_sf_ops = { + .setup = sandbox_sf_setup, + .free = sandbox_sf_free, + .cs_activate = sandbox_sf_cs_activate, + .cs_deactivate = sandbox_sf_cs_deactivate, + .xfer = sandbox_sf_xfer, +}; + +static int sandbox_cmdline_cb_spi_sf(struct sandbox_state *state, + const char *arg) +{ + unsigned long bus, cs; + const char *spec = sandbox_spi_parse_spec(arg, &bus, &cs); + + if (!spec) + return 1; + + /* + * It is safe to not make a copy of 'spec' because it comes from the + * command line. + * + * TODO(sjg@chromium.org): It would be nice if we could parse the + * spec here, but the problem is that no U-Boot init has been done + * yet. Perhaps we can figure something out. + */ + state->spi[bus][cs].ops = &sandbox_sf_ops; + state->spi[bus][cs].spec = spec; + return 0; +} +SANDBOX_CMDLINE_OPT(spi_sf, 1, "connect a SPI flash: :::"); diff --git a/qemu/roms/u-boot/drivers/mtd/spi/sf.c b/qemu/roms/u-boot/drivers/mtd/spi/sf.c new file mode 100644 index 000000000..664e86082 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/sf.c @@ -0,0 +1,58 @@ +/* + * SPI flash interface + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +static int spi_flash_read_write(struct spi_slave *spi, + const u8 *cmd, size_t cmd_len, + const u8 *data_out, u8 *data_in, + size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + +#ifdef CONFIG_SF_DUAL_FLASH + if (spi->flags & SPI_XFER_U_PAGE) + flags |= SPI_XFER_U_PAGE; +#endif + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, data_out, data_in, + SPI_XFER_END); + if (ret) + debug("SF: Failed to transfer %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len); +} + +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) +{ + return spi_flash_cmd_read(spi, &cmd, 1, response, len); +} + +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len) +{ + return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len); +} diff --git a/qemu/roms/u-boot/drivers/mtd/spi/sf_internal.h b/qemu/roms/u-boot/drivers/mtd/spi/sf_internal.h new file mode 100644 index 000000000..6bcd52204 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/sf_internal.h @@ -0,0 +1,159 @@ +/* + * SPI flash internal definitions + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SF_INTERNAL_H_ +#define _SF_INTERNAL_H_ + +#define SPI_FLASH_3B_ADDR_LEN 3 +#define SPI_FLASH_CMD_LEN (1 + SPI_FLASH_3B_ADDR_LEN) +#define SPI_FLASH_16MB_BOUN 0x1000000 + +/* CFI Manufacture ID's */ +#define SPI_FLASH_CFI_MFR_SPANSION 0x01 +#define SPI_FLASH_CFI_MFR_STMICRO 0x20 +#define SPI_FLASH_CFI_MFR_MACRONIX 0xc2 +#define SPI_FLASH_CFI_MFR_WINBOND 0xef + +/* Erase commands */ +#define CMD_ERASE_4K 0x20 +#define CMD_ERASE_32K 0x52 +#define CMD_ERASE_CHIP 0xc7 +#define CMD_ERASE_64K 0xd8 + +/* Write commands */ +#define CMD_WRITE_STATUS 0x01 +#define CMD_PAGE_PROGRAM 0x02 +#define CMD_WRITE_DISABLE 0x04 +#define CMD_READ_STATUS 0x05 +#define CMD_QUAD_PAGE_PROGRAM 0x32 +#define CMD_READ_STATUS1 0x35 +#define CMD_WRITE_ENABLE 0x06 +#define CMD_READ_CONFIG 0x35 +#define CMD_FLAG_STATUS 0x70 + +/* Read commands */ +#define CMD_READ_ARRAY_SLOW 0x03 +#define CMD_READ_ARRAY_FAST 0x0b +#define CMD_READ_DUAL_OUTPUT_FAST 0x3b +#define CMD_READ_DUAL_IO_FAST 0xbb +#define CMD_READ_QUAD_OUTPUT_FAST 0x6b +#define CMD_READ_QUAD_IO_FAST 0xeb +#define CMD_READ_ID 0x9f + +/* Bank addr access commands */ +#ifdef CONFIG_SPI_FLASH_BAR +# define CMD_BANKADDR_BRWR 0x17 +# define CMD_BANKADDR_BRRD 0x16 +# define CMD_EXTNADDR_WREAR 0xC5 +# define CMD_EXTNADDR_RDEAR 0xC8 +#endif + +/* Common status */ +#define STATUS_WIP (1 << 0) +#define STATUS_QEB_WINSPAN (1 << 1) +#define STATUS_QEB_MXIC (1 << 6) +#define STATUS_PEC (1 << 7) + +/* Flash timeout values */ +#define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ) +#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) +#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ) + +/* SST specific */ +#ifdef CONFIG_SPI_FLASH_SST +# define SST_WP 0x01 /* Supports AAI word program */ +# define CMD_SST_BP 0x02 /* Byte Program */ +# define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ + +int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf); +#endif + +/* Send a single-byte command to the device and read the response */ +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); + +/* + * Send a multi-byte command to the device and read the response. Used + * for flash array reads, etc. + */ +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len); + +/* + * Send a multi-byte command to the device followed by (optional) + * data. Used for programming the flash array, etc. + */ +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len); + + +/* Flash erase(sectors) operation, support all possible erase commands */ +int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len); + +/* Read the status register */ +int spi_flash_cmd_read_status(struct spi_flash *flash, u8 *rs); + +/* Program the status register */ +int spi_flash_cmd_write_status(struct spi_flash *flash, u8 ws); + +/* Read the config register */ +int spi_flash_cmd_read_config(struct spi_flash *flash, u8 *rc); + +/* Program the config register */ +int spi_flash_cmd_write_config(struct spi_flash *flash, u8 wc); + +/* Enable writing on the SPI flash */ +static inline int spi_flash_cmd_write_enable(struct spi_flash *flash) +{ + return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0); +} + +/* Disable writing on the SPI flash */ +static inline int spi_flash_cmd_write_disable(struct spi_flash *flash) +{ + return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0); +} + +/* + * Send the read status command to the device and wait for the wip + * (write-in-progress) bit to clear itself. + */ +int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout); + +/* + * Used for spi_flash write operation + * - SPI claim + * - spi_flash_cmd_write_enable + * - spi_flash_cmd_write + * - spi_flash_cmd_wait_ready + * - SPI release + */ +int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, const void *buf, size_t buf_len); + +/* + * Flash write operation, support all possible write commands. + * Write the requested data out breaking it up into multiple write + * commands as needed per the write size. + */ +int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, + size_t len, const void *buf); + +/* + * Same as spi_flash_cmd_read() except it also claims/releases the SPI + * bus. Used as common part of the ->read() operation. + */ +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len); + +/* Flash read operation, support all possible read commands */ +int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, + size_t len, void *data); + +#endif /* _SF_INTERNAL_H_ */ diff --git a/qemu/roms/u-boot/drivers/mtd/spi/sf_ops.c b/qemu/roms/u-boot/drivers/mtd/spi/sf_ops.c new file mode 100644 index 000000000..ef91b924d --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/sf_ops.c @@ -0,0 +1,518 @@ +/* + * SPI flash operations + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include "sf_internal.h" + +static void spi_flash_addr(u32 addr, u8 *cmd) +{ + /* cmd[0] is actual command */ + cmd[1] = addr >> 16; + cmd[2] = addr >> 8; + cmd[3] = addr >> 0; +} + +int spi_flash_cmd_read_status(struct spi_flash *flash, u8 *rs) +{ + int ret; + u8 cmd; + + cmd = CMD_READ_STATUS; + ret = spi_flash_read_common(flash, &cmd, 1, rs, 1); + if (ret < 0) { + debug("SF: fail to read status register\n"); + return ret; + } + + return 0; +} + +int spi_flash_cmd_write_status(struct spi_flash *flash, u8 ws) +{ + u8 cmd; + int ret; + + cmd = CMD_WRITE_STATUS; + ret = spi_flash_write_common(flash, &cmd, 1, &ws, 1); + if (ret < 0) { + debug("SF: fail to write status register\n"); + return ret; + } + + return 0; +} + +#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) +int spi_flash_cmd_read_config(struct spi_flash *flash, u8 *rc) +{ + int ret; + u8 cmd; + + cmd = CMD_READ_CONFIG; + ret = spi_flash_read_common(flash, &cmd, 1, rc, 1); + if (ret < 0) { + debug("SF: fail to read config register\n"); + return ret; + } + + return 0; +} + +int spi_flash_cmd_write_config(struct spi_flash *flash, u8 wc) +{ + u8 data[2]; + u8 cmd; + int ret; + + ret = spi_flash_cmd_read_status(flash, &data[0]); + if (ret < 0) + return ret; + + cmd = CMD_WRITE_STATUS; + data[1] = wc; + ret = spi_flash_write_common(flash, &cmd, 1, &data, 2); + if (ret) { + debug("SF: fail to write config register\n"); + return ret; + } + + return 0; +} +#endif + +#ifdef CONFIG_SPI_FLASH_BAR +static int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel) +{ + u8 cmd; + int ret; + + if (flash->bank_curr == bank_sel) { + debug("SF: not require to enable bank%d\n", bank_sel); + return 0; + } + + cmd = flash->bank_write_cmd; + ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); + if (ret < 0) { + debug("SF: fail to write bank register\n"); + return ret; + } + flash->bank_curr = bank_sel; + + return 0; +} + +static int spi_flash_bank(struct spi_flash *flash, u32 offset) +{ + u8 bank_sel; + int ret; + + bank_sel = offset / (SPI_FLASH_16MB_BOUN << flash->shift); + + ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); + if (ret) { + debug("SF: fail to set bank%d\n", bank_sel); + return ret; + } + + return bank_sel; +} +#endif + +#ifdef CONFIG_SF_DUAL_FLASH +static void spi_flash_dual_flash(struct spi_flash *flash, u32 *addr) +{ + switch (flash->dual_flash) { + case SF_DUAL_STACKED_FLASH: + if (*addr >= (flash->size >> 1)) { + *addr -= flash->size >> 1; + flash->spi->flags |= SPI_XFER_U_PAGE; + } else { + flash->spi->flags &= ~SPI_XFER_U_PAGE; + } + break; + case SF_DUAL_PARALLEL_FLASH: + *addr >>= flash->shift; + break; + default: + debug("SF: Unsupported dual_flash=%d\n", flash->dual_flash); + break; + } +} +#endif + +int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + unsigned long flags = SPI_XFER_BEGIN; + int ret; + u8 status; + u8 check_status = 0x0; + u8 poll_bit = STATUS_WIP; + u8 cmd = flash->poll_cmd; + + if (cmd == CMD_FLAG_STATUS) { + poll_bit = STATUS_PEC; + check_status = poll_bit; + } + +#ifdef CONFIG_SF_DUAL_FLASH + if (spi->flags & SPI_XFER_U_PAGE) + flags |= SPI_XFER_U_PAGE; +#endif + ret = spi_xfer(spi, 8, &cmd, NULL, flags); + if (ret) { + debug("SF: fail to read %s status register\n", + cmd == CMD_READ_STATUS ? "read" : "flag"); + return ret; + } + + timebase = get_timer(0); + do { + WATCHDOG_RESET(); + + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if ((status & poll_bit) == check_status) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & poll_bit) == check_status) + return 0; + + /* Timed out */ + debug("SF: time out!\n"); + return -1; +} + +int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, const void *buf, size_t buf_len) +{ + struct spi_slave *spi = flash->spi; + unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; + int ret; + + if (buf == NULL) + timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + + ret = spi_flash_cmd_write_enable(flash); + if (ret < 0) { + debug("SF: enabling write failed\n"); + return ret; + } + + ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); + if (ret < 0) { + debug("SF: write cmd failed\n"); + return ret; + } + + ret = spi_flash_cmd_wait_ready(flash, timeout); + if (ret < 0) { + debug("SF: write %s timed out\n", + timeout == SPI_FLASH_PROG_TIMEOUT ? + "program" : "page erase"); + return ret; + } + + spi_release_bus(spi); + + return ret; +} + +int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) +{ + u32 erase_size, erase_addr; + u8 cmd[SPI_FLASH_CMD_LEN]; + int ret = -1; + + erase_size = flash->erase_size; + if (offset % erase_size || len % erase_size) { + debug("SF: Erase offset/length not multiple of erase size\n"); + return -1; + } + + cmd[0] = flash->erase_cmd; + while (len) { + erase_addr = offset; + +#ifdef CONFIG_SF_DUAL_FLASH + if (flash->dual_flash > SF_SINGLE_FLASH) + spi_flash_dual_flash(flash, &erase_addr); +#endif +#ifdef CONFIG_SPI_FLASH_BAR + ret = spi_flash_bank(flash, erase_addr); + if (ret < 0) + return ret; +#endif + spi_flash_addr(erase_addr, cmd); + + debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], + cmd[2], cmd[3], erase_addr); + + ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); + if (ret < 0) { + debug("SF: erase failed\n"); + break; + } + + offset += erase_size; + len -= erase_size; + } + + return ret; +} + +int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, + size_t len, const void *buf) +{ + unsigned long byte_addr, page_size; + u32 write_addr; + size_t chunk_len, actual; + u8 cmd[SPI_FLASH_CMD_LEN]; + int ret = -1; + + page_size = flash->page_size; + + cmd[0] = flash->write_cmd; + for (actual = 0; actual < len; actual += chunk_len) { + write_addr = offset; + +#ifdef CONFIG_SF_DUAL_FLASH + if (flash->dual_flash > SF_SINGLE_FLASH) + spi_flash_dual_flash(flash, &write_addr); +#endif +#ifdef CONFIG_SPI_FLASH_BAR + ret = spi_flash_bank(flash, write_addr); + if (ret < 0) + return ret; +#endif + byte_addr = offset % page_size; + chunk_len = min(len - actual, page_size - byte_addr); + + if (flash->spi->max_write_size) + chunk_len = min(chunk_len, flash->spi->max_write_size); + + spi_flash_addr(write_addr, cmd); + + debug("SF: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", + buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_write_common(flash, cmd, sizeof(cmd), + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: write failed\n"); + break; + } + + offset += chunk_len; + } + + return ret; +} + +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + struct spi_slave *spi = flash->spi; + int ret; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + + ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); + if (ret < 0) { + debug("SF: read cmd failed\n"); + return ret; + } + + spi_release_bus(spi); + + return ret; +} + +int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, + size_t len, void *data) +{ + u8 *cmd, cmdsz; + u32 remain_len, read_len, read_addr; + int bank_sel = 0; + int ret = -1; + + /* Handle memory-mapped SPI */ + if (flash->memory_map) { + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP); + memcpy(data, flash->memory_map + offset, len); + spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP_END); + spi_release_bus(flash->spi); + return 0; + } + + cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte; + cmd = calloc(1, cmdsz); + if (!cmd) { + debug("SF: Failed to allocate cmd\n"); + return -ENOMEM; + } + + cmd[0] = flash->read_cmd; + while (len) { + read_addr = offset; + +#ifdef CONFIG_SF_DUAL_FLASH + if (flash->dual_flash > SF_SINGLE_FLASH) + spi_flash_dual_flash(flash, &read_addr); +#endif +#ifdef CONFIG_SPI_FLASH_BAR + bank_sel = spi_flash_bank(flash, read_addr); + if (bank_sel < 0) + return ret; +#endif + remain_len = ((SPI_FLASH_16MB_BOUN << flash->shift) * + (bank_sel + 1)) - offset; + if (len < remain_len) + read_len = len; + else + read_len = remain_len; + + spi_flash_addr(read_addr, cmd); + + ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); + if (ret < 0) { + debug("SF: read failed\n"); + break; + } + + offset += read_len; + len -= read_len; + data += read_len; + } + + return ret; +} + +#ifdef CONFIG_SPI_FLASH_SST +static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) +{ + int ret; + u8 cmd[4] = { + CMD_SST_BP, + offset >> 16, + offset >> 8, + offset, + }; + + debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", + spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset); + + ret = spi_flash_cmd_write_enable(flash); + if (ret) + return ret; + + ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); + if (ret) + return ret; + + return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); +} + +int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf) +{ + size_t actual, cmd_len; + int ret; + u8 cmd[4]; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + /* If the data is not word aligned, write out leading single byte */ + actual = offset % 2; + if (actual) { + ret = sst_byte_write(flash, offset, buf); + if (ret) + goto done; + } + offset += actual; + + ret = spi_flash_cmd_write_enable(flash); + if (ret) + goto done; + + cmd_len = 4; + cmd[0] = CMD_SST_AAI_WP; + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + cmd[3] = offset; + + for (; actual < len - 1; actual += 2) { + debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", + spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, + cmd[0], offset); + + ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, + buf + actual, 2); + if (ret) { + debug("SF: sst word program failed\n"); + break; + } + + ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret) + break; + + cmd_len = 1; + offset += 2; + } + + if (!ret) + ret = spi_flash_cmd_write_disable(flash); + + /* If there is a single trailing byte, write it out */ + if (!ret && actual != len) + ret = sst_byte_write(flash, offset, buf + actual); + + done: + debug("SF: sst: program %s %zu bytes @ 0x%zx\n", + ret ? "failure" : "success", len, offset - actual); + + spi_release_bus(flash->spi); + return ret; +} +#endif diff --git a/qemu/roms/u-boot/drivers/mtd/spi/sf_params.c b/qemu/roms/u-boot/drivers/mtd/spi/sf_params.c new file mode 100644 index 000000000..eb372b757 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/sf_params.c @@ -0,0 +1,131 @@ +/* + * SPI flash Params table + * + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include "sf_internal.h" + +/* SPI/QSPI flash device params structure */ +const struct spi_flash_params spi_flash_params_table[] = { +#ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */ + {"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, 0, SECT_4K}, + {"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, 0, SECT_4K}, + {"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, 0, SECT_4K}, + {"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, 0, SECT_4K}, + {"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, 0, SECT_4K}, + {"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, 0, SECT_4K}, + {"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, 0, SECT_4K}, + {"AT25DF321", 0x1f4701, 0x0, 64 * 1024, 64, 0, SECT_4K}, +#endif +#ifdef CONFIG_SPI_FLASH_EON /* EON */ + {"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, 0, 0}, + {"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, 0, SECT_4K}, + {"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, 0, 0}, + {"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, 0, 0}, +#endif +#ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */ + {"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, 0, SECT_4K}, + {"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, 0, SECT_4K}, +#endif +#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ + {"MX25L2006E", 0xc22012, 0x0, 64 * 1024, 4, 0, 0}, + {"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, 0, 0}, + {"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, 0, 0}, + {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, 0, 0}, + {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, 0, 0}, + {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, 0, 0}, + {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, + {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP}, + {"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP}, + {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, +#endif +#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ + {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, 0, 0}, + {"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, 0, 0}, + {"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, 0, 0}, + {"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, 0, 0}, + {"S25FL128P_256K", 0x012018, 0x0300, 256 * 1024, 64, RD_FULL, WR_QPP}, + {"S25FL128P_64K", 0x012018, 0x0301, 64 * 1024, 256, RD_FULL, WR_QPP}, + {"S25FL032P", 0x010215, 0x4d00, 64 * 1024, 64, RD_FULL, WR_QPP}, + {"S25FL064P", 0x010216, 0x4d00, 64 * 1024, 128, RD_FULL, WR_QPP}, + {"S25FL128S_256K", 0x012018, 0x4d00, 256 * 1024, 64, RD_FULL, WR_QPP}, + {"S25FL128S_64K", 0x012018, 0x4d01, 64 * 1024, 256, RD_FULL, WR_QPP}, + {"S25FL256S_256K", 0x010219, 0x4d00, 256 * 1024, 128, RD_FULL, WR_QPP}, + {"S25FL256S_64K", 0x010219, 0x4d01, 64 * 1024, 512, RD_FULL, WR_QPP}, + {"S25FL512S_256K", 0x010220, 0x4d00, 256 * 1024, 256, RD_FULL, WR_QPP}, + {"S25FL512S_64K", 0x010220, 0x4d01, 64 * 1024, 1024, RD_FULL, WR_QPP}, +#endif +#ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ + {"M25P10", 0x202011, 0x0, 32 * 1024, 4, 0, 0}, + {"M25P20", 0x202012, 0x0, 64 * 1024, 4, 0, 0}, + {"M25P40", 0x202013, 0x0, 64 * 1024, 8, 0, 0}, + {"M25P80", 0x202014, 0x0, 64 * 1024, 16, 0, 0}, + {"M25P16", 0x202015, 0x0, 64 * 1024, 32, 0, 0}, + {"M25P32", 0x202016, 0x0, 64 * 1024, 64, 0, 0}, + {"M25P64", 0x202017, 0x0, 64 * 1024, 128, 0, 0}, + {"M25P128", 0x202018, 0x0, 256 * 1024, 64, 0, 0}, + {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K}, + {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K}, + {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K}, + {"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K}, + {"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, + {"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, + {"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K}, + {"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K}, + {"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP | E_FSR | SECT_4K}, + {"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP | E_FSR | SECT_4K}, + {"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL, WR_QPP | E_FSR | SECT_4K}, + {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL, WR_QPP | E_FSR | SECT_4K}, +#endif +#ifdef CONFIG_SPI_FLASH_SST /* SST */ + {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, 0, SECT_4K | SST_WP}, + {"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, 0, SECT_4K | SST_WP}, + {"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, 0, SECT_4K | SST_WP}, + {"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, 0, SECT_4K | SST_WP}, + {"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, 0, SECT_4K}, + {"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, 0, SECT_4K | SST_WP}, + {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, 0, SECT_4K | SST_WP}, + {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, 0, SECT_4K | SST_WP}, + {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, 0, SECT_4K | SST_WP}, + {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, 0, SECT_4K | SST_WP}, +#endif +#ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */ + {"W25P80", 0xef2014, 0x0, 64 * 1024, 16, 0, 0}, + {"W25P16", 0xef2015, 0x0, 64 * 1024, 32, 0, 0}, + {"W25P32", 0xef2016, 0x0, 64 * 1024, 64, 0, 0}, + {"W25X40", 0xef3013, 0x0, 64 * 1024, 8, 0, SECT_4K}, + {"W25X16", 0xef3015, 0x0, 64 * 1024, 32, 0, SECT_4K}, + {"W25X32", 0xef3016, 0x0, 64 * 1024, 64, 0, SECT_4K}, + {"W25X64", 0xef3017, 0x0, 64 * 1024, 128, 0, SECT_4K}, + {"W25Q80BL", 0xef4014, 0x0, 64 * 1024, 16, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q16CL", 0xef4015, 0x0, 64 * 1024, 32, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q32BV", 0xef4016, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q64CV", 0xef4017, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q128BV", 0xef4018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q256", 0xef4019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q80BW", 0xef5014, 0x0, 64 * 1024, 16, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q16DW", 0xef6015, 0x0, 64 * 1024, 32, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q32DW", 0xef6016, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q64DW", 0xef6017, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K}, + {"W25Q128FW", 0xef6018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP | SECT_4K}, +#endif + /* + * Note: + * Below paired flash devices has similar spi_flash params. + * (S25FL129P_64K, S25FL128S_64K) + * (W25Q80BL, W25Q80BV) + * (W25Q16CL, W25Q16DV) + * (W25Q32BV, W25Q32FV_SPI) + * (W25Q64CV, W25Q64FV_SPI) + * (W25Q128BV, W25Q128FV_SPI) + * (W25Q32DW, W25Q32FV_QPI) + * (W25Q64DW, W25Q64FV_QPI) + * (W25Q128FW, W25Q128FV_QPI) + */ +}; diff --git a/qemu/roms/u-boot/drivers/mtd/spi/sf_probe.c b/qemu/roms/u-boot/drivers/mtd/spi/sf_probe.c new file mode 100644 index 000000000..0a46fe38d --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/sf_probe.c @@ -0,0 +1,391 @@ +/* + * SPI flash probing + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include "sf_internal.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* Read commands array */ +static u8 spi_read_cmds_array[] = { + CMD_READ_ARRAY_SLOW, + CMD_READ_DUAL_OUTPUT_FAST, + CMD_READ_DUAL_IO_FAST, + CMD_READ_QUAD_OUTPUT_FAST, + CMD_READ_QUAD_IO_FAST, +}; + +#ifdef CONFIG_SPI_FLASH_MACRONIX +static int spi_flash_set_qeb_mxic(struct spi_flash *flash) +{ + u8 qeb_status; + int ret; + + ret = spi_flash_cmd_read_status(flash, &qeb_status); + if (ret < 0) + return ret; + + if (qeb_status & STATUS_QEB_MXIC) { + debug("SF: mxic: QEB is already set\n"); + } else { + ret = spi_flash_cmd_write_status(flash, STATUS_QEB_MXIC); + if (ret < 0) + return ret; + } + + return ret; +} +#endif + +#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) +static int spi_flash_set_qeb_winspan(struct spi_flash *flash) +{ + u8 qeb_status; + int ret; + + ret = spi_flash_cmd_read_config(flash, &qeb_status); + if (ret < 0) + return ret; + + if (qeb_status & STATUS_QEB_WINSPAN) { + debug("SF: winspan: QEB is already set\n"); + } else { + ret = spi_flash_cmd_write_config(flash, STATUS_QEB_WINSPAN); + if (ret < 0) + return ret; + } + + return ret; +} +#endif + +static int spi_flash_set_qeb(struct spi_flash *flash, u8 idcode0) +{ + switch (idcode0) { +#ifdef CONFIG_SPI_FLASH_MACRONIX + case SPI_FLASH_CFI_MFR_MACRONIX: + return spi_flash_set_qeb_mxic(flash); +#endif +#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) + case SPI_FLASH_CFI_MFR_SPANSION: + case SPI_FLASH_CFI_MFR_WINBOND: + return spi_flash_set_qeb_winspan(flash); +#endif +#ifdef CONFIG_SPI_FLASH_STMICRO + case SPI_FLASH_CFI_MFR_STMICRO: + debug("SF: QEB is volatile for %02x flash\n", idcode0); + return 0; +#endif + default: + printf("SF: Need set QEB func for %02x flash\n", idcode0); + return -1; + } +} + +static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi, + u8 *idcode) +{ + const struct spi_flash_params *params; + struct spi_flash *flash; + u8 cmd; + u16 jedec = idcode[1] << 8 | idcode[2]; + u16 ext_jedec = idcode[3] << 8 | idcode[4]; + + params = spi_flash_params_table; + for (; params->name != NULL; params++) { + if ((params->jedec >> 16) == idcode[0]) { + if ((params->jedec & 0xFFFF) == jedec) { + if (params->ext_jedec == 0) + break; + else if (params->ext_jedec == ext_jedec) + break; + } + } + } + + if (!params->name) { + printf("SF: Unsupported flash IDs: "); + printf("manuf %02x, jedec %04x, ext_jedec %04x\n", + idcode[0], jedec, ext_jedec); + return NULL; + } + + flash = calloc(1, sizeof(*flash)); + if (!flash) { + debug("SF: Failed to allocate spi_flash\n"); + return NULL; + } + + /* Assign spi data */ + flash->spi = spi; + flash->name = params->name; + flash->memory_map = spi->memory_map; + flash->dual_flash = flash->spi->option; + + /* Assign spi_flash ops */ + flash->write = spi_flash_cmd_write_ops; +#ifdef CONFIG_SPI_FLASH_SST + if (params->flags & SST_WP) + flash->write = sst_write_wp; +#endif + flash->erase = spi_flash_cmd_erase_ops; + flash->read = spi_flash_cmd_read_ops; + + /* Compute the flash size */ + flash->shift = (flash->dual_flash & SF_DUAL_PARALLEL_FLASH) ? 1 : 0; + /* + * The Spansion S25FL032P and S25FL064P have 256b pages, yet use the + * 0x4d00 Extended JEDEC code. The rest of the Spansion flashes with + * the 0x4d00 Extended JEDEC code have 512b pages. All of the others + * have 256b pages. + */ + if (ext_jedec == 0x4d00) { + if ((jedec == 0x0215) || (jedec == 0x216)) + flash->page_size = 256; + else + flash->page_size = 512; + } else { + flash->page_size = 256; + } + flash->page_size <<= flash->shift; + flash->sector_size = params->sector_size << flash->shift; + flash->size = flash->sector_size * params->nr_sectors << flash->shift; +#ifdef CONFIG_SF_DUAL_FLASH + if (flash->dual_flash & SF_DUAL_STACKED_FLASH) + flash->size <<= 1; +#endif + + /* Compute erase sector and command */ + if (params->flags & SECT_4K) { + flash->erase_cmd = CMD_ERASE_4K; + flash->erase_size = 4096 << flash->shift; + } else if (params->flags & SECT_32K) { + flash->erase_cmd = CMD_ERASE_32K; + flash->erase_size = 32768 << flash->shift; + } else { + flash->erase_cmd = CMD_ERASE_64K; + flash->erase_size = flash->sector_size; + } + + /* Look for the fastest read cmd */ + cmd = fls(params->e_rd_cmd & flash->spi->op_mode_rx); + if (cmd) { + cmd = spi_read_cmds_array[cmd - 1]; + flash->read_cmd = cmd; + } else { + /* Go for default supported read cmd */ + flash->read_cmd = CMD_READ_ARRAY_FAST; + } + + /* Not require to look for fastest only two write cmds yet */ + if (params->flags & WR_QPP && flash->spi->op_mode_tx & SPI_OPM_TX_QPP) + flash->write_cmd = CMD_QUAD_PAGE_PROGRAM; + else + /* Go for default supported write cmd */ + flash->write_cmd = CMD_PAGE_PROGRAM; + + /* Set the quad enable bit - only for quad commands */ + if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) || + (flash->read_cmd == CMD_READ_QUAD_IO_FAST) || + (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) { + if (spi_flash_set_qeb(flash, idcode[0])) { + debug("SF: Fail to set QEB for %02x\n", idcode[0]); + return NULL; + } + } + + /* Read dummy_byte: dummy byte is determined based on the + * dummy cycles of a particular command. + * Fast commands - dummy_byte = dummy_cycles/8 + * I/O commands- dummy_byte = (dummy_cycles * no.of lines)/8 + * For I/O commands except cmd[0] everything goes on no.of lines + * based on particular command but incase of fast commands except + * data all go on single line irrespective of command. + */ + switch (flash->read_cmd) { + case CMD_READ_QUAD_IO_FAST: + flash->dummy_byte = 2; + break; + case CMD_READ_ARRAY_SLOW: + flash->dummy_byte = 0; + break; + default: + flash->dummy_byte = 1; + } + + /* Poll cmd selection */ + flash->poll_cmd = CMD_READ_STATUS; +#ifdef CONFIG_SPI_FLASH_STMICRO + if (params->flags & E_FSR) + flash->poll_cmd = CMD_FLAG_STATUS; +#endif + + /* Configure the BAR - discover bank cmds and read current bank */ +#ifdef CONFIG_SPI_FLASH_BAR + u8 curr_bank = 0; + if (flash->size > SPI_FLASH_16MB_BOUN) { + flash->bank_read_cmd = (idcode[0] == 0x01) ? + CMD_BANKADDR_BRRD : CMD_EXTNADDR_RDEAR; + flash->bank_write_cmd = (idcode[0] == 0x01) ? + CMD_BANKADDR_BRWR : CMD_EXTNADDR_WREAR; + + if (spi_flash_read_common(flash, &flash->bank_read_cmd, 1, + &curr_bank, 1)) { + debug("SF: fail to read bank addr register\n"); + return NULL; + } + flash->bank_curr = curr_bank; + } else { + flash->bank_curr = curr_bank; + } +#endif + + /* Flash powers up read-only, so clear BP# bits */ +#if defined(CONFIG_SPI_FLASH_ATMEL) || \ + defined(CONFIG_SPI_FLASH_MACRONIX) || \ + defined(CONFIG_SPI_FLASH_SST) + spi_flash_cmd_write_status(flash, 0); +#endif + + return flash; +} + +#ifdef CONFIG_OF_CONTROL +int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) +{ + fdt_addr_t addr; + fdt_size_t size; + int node; + + /* If there is no node, do nothing */ + node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH); + if (node < 0) + return 0; + + addr = fdtdec_get_addr_size(blob, node, "memory-map", &size); + if (addr == FDT_ADDR_T_NONE) { + debug("%s: Cannot decode address\n", __func__); + return 0; + } + + if (flash->size != size) { + debug("%s: Memory map must cover entire device\n", __func__); + return -1; + } + flash->memory_map = map_sysmem(addr, size); + + return 0; +} +#endif /* CONFIG_OF_CONTROL */ + +static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi) +{ + struct spi_flash *flash = NULL; + u8 idcode[5]; + int ret; + + /* Setup spi_slave */ + if (!spi) { + printf("SF: Failed to set up slave\n"); + return NULL; + } + + /* Claim spi bus */ + ret = spi_claim_bus(spi); + if (ret) { + debug("SF: Failed to claim SPI bus: %d\n", ret); + goto err_claim_bus; + } + + /* Read the ID codes */ + ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); + if (ret) { + printf("SF: Failed to get idcodes\n"); + goto err_read_id; + } + +#ifdef DEBUG + printf("SF: Got idcodes\n"); + print_buffer(0, idcode, 1, sizeof(idcode), 0); +#endif + + /* Validate params from spi_flash_params table */ + flash = spi_flash_validate_params(spi, idcode); + if (!flash) + goto err_read_id; + +#ifdef CONFIG_OF_CONTROL + if (spi_flash_decode_fdt(gd->fdt_blob, flash)) { + debug("SF: FDT decode error\n"); + goto err_read_id; + } +#endif +#ifndef CONFIG_SPL_BUILD + printf("SF: Detected %s with page size ", flash->name); + print_size(flash->page_size, ", erase size "); + print_size(flash->erase_size, ", total "); + print_size(flash->size, ""); + if (flash->memory_map) + printf(", mapped at %p", flash->memory_map); + puts("\n"); +#endif +#ifndef CONFIG_SPI_FLASH_BAR + if (((flash->dual_flash == SF_SINGLE_FLASH) && + (flash->size > SPI_FLASH_16MB_BOUN)) || + ((flash->dual_flash > SF_SINGLE_FLASH) && + (flash->size > SPI_FLASH_16MB_BOUN << 1))) { + puts("SF: Warning - Only lower 16MiB accessible,"); + puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); + } +#endif + + /* Release spi bus */ + spi_release_bus(spi); + + return flash; + +err_read_id: + spi_release_bus(spi); +err_claim_bus: + spi_free_slave(spi); + return NULL; +} + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *spi; + + spi = spi_setup_slave(bus, cs, max_hz, spi_mode); + return spi_flash_probe_slave(spi); +} + +#ifdef CONFIG_OF_SPI_FLASH +struct spi_flash *spi_flash_probe_fdt(const void *blob, int slave_node, + int spi_node) +{ + struct spi_slave *spi; + + spi = spi_setup_slave_fdt(blob, slave_node, spi_node); + return spi_flash_probe_slave(spi); +} +#endif + +void spi_flash_free(struct spi_flash *flash) +{ + spi_free_slave(flash->spi); + free(flash); +} diff --git a/qemu/roms/u-boot/drivers/mtd/spi/spi_spl_load.c b/qemu/roms/u-boot/drivers/mtd/spi/spi_spl_load.c new file mode 100644 index 000000000..1954b7e88 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/spi/spi_spl_load.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 OMICRON electronics GmbH + * + * based on drivers/mtd/nand/nand_spl_load.c + * + * Copyright (C) 2011 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#ifdef CONFIG_SPL_OS_BOOT +/* + * Load the kernel, check for a valid header we can parse, and if found load + * the kernel and then device tree. + */ +static int spi_load_image_os(struct spi_flash *flash, + struct image_header *header) +{ + /* Read for a header, parse or error out. */ + spi_flash_read(flash, CONFIG_SYS_SPI_KERNEL_OFFS, 0x40, + (void *)header); + + if (image_get_magic(header) != IH_MAGIC) + return -1; + + spl_parse_image_header(header); + + spi_flash_read(flash, CONFIG_SYS_SPI_KERNEL_OFFS, + spl_image.size, (void *)spl_image.load_addr); + + /* Read device tree. */ + spi_flash_read(flash, CONFIG_SYS_SPI_ARGS_OFFS, + CONFIG_SYS_SPI_ARGS_SIZE, + (void *)CONFIG_SYS_SPL_ARGS_ADDR); + + return 0; +} +#endif + +/* + * The main entry for SPI booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from SPI into SDRAM and starts it from there. + */ +void spl_spi_load_image(void) +{ + struct spi_flash *flash; + struct image_header *header; + + /* + * Load U-Boot image from SPI flash into RAM + */ + + flash = spi_flash_probe(CONFIG_SPL_SPI_BUS, CONFIG_SPL_SPI_CS, + CONFIG_SF_DEFAULT_SPEED, SPI_MODE_3); + if (!flash) { + puts("SPI probe failed.\n"); + hang(); + } + + /* use CONFIG_SYS_TEXT_BASE as temporary storage area */ + header = (struct image_header *)(CONFIG_SYS_TEXT_BASE); + +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || spi_load_image_os(flash, header)) +#endif + { + /* Load u-boot, mkimage header is 64 bytes. */ + spi_flash_read(flash, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40, + (void *)header); + spl_parse_image_header(header); + spi_flash_read(flash, CONFIG_SYS_SPI_U_BOOT_OFFS, + spl_image.size, (void *)spl_image.load_addr); + } +} diff --git a/qemu/roms/u-boot/drivers/mtd/st_smi.c b/qemu/roms/u-boot/drivers/mtd/st_smi.c new file mode 100644 index 000000000..208119c5f --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/st_smi.c @@ -0,0 +1,565 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Microelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#include +#include + +#if !defined(CONFIG_SYS_NO_FLASH) + +static struct smi_regs *const smicntl = + (struct smi_regs * const)CONFIG_SYS_SMI_BASE; +static ulong bank_base[CONFIG_SYS_MAX_FLASH_BANKS] = + CONFIG_SYS_FLASH_ADDR_BASE; +flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; + +/* data structure to maintain flash ids from different vendors */ +struct flash_device { + char *name; + u8 erase_cmd; + u32 device_id; + u32 pagesize; + unsigned long sectorsize; + unsigned long size_in_bytes; +}; + +#define FLASH_ID(n, es, id, psize, ssize, size) \ +{ \ + .name = n, \ + .erase_cmd = es, \ + .device_id = id, \ + .pagesize = psize, \ + .sectorsize = ssize, \ + .size_in_bytes = size \ +} + +/* + * List of supported flash devices. + * Currently the erase_cmd field is not used in this driver. + */ +static struct flash_device flash_devices[] = { + FLASH_ID("st m25p16" , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000), + FLASH_ID("st m25p32" , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000), + FLASH_ID("st m25p64" , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000), + FLASH_ID("st m25p128" , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000), + FLASH_ID("st m25p05" , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000), + FLASH_ID("st m25p10" , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000), + FLASH_ID("st m25p20" , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000), + FLASH_ID("st m25p40" , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000), + FLASH_ID("st m25p80" , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000), + FLASH_ID("st m45pe10" , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000), + FLASH_ID("st m45pe20" , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000), + FLASH_ID("st m45pe40" , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000), + FLASH_ID("st m45pe80" , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000), + FLASH_ID("sp s25fl004" , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000), + FLASH_ID("sp s25fl008" , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000), + FLASH_ID("sp s25fl016" , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000), + FLASH_ID("sp s25fl032" , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000), + FLASH_ID("sp s25fl064" , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000), + FLASH_ID("mac 25l512" , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000), + FLASH_ID("mac 25l1005" , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000), + FLASH_ID("mac 25l2005" , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000), + FLASH_ID("mac 25l4005" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), + FLASH_ID("mac 25l4005a" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), + FLASH_ID("mac 25l8005" , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000), + FLASH_ID("mac 25l1605" , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000), + FLASH_ID("mac 25l1605a" , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000), + FLASH_ID("mac 25l3205" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), + FLASH_ID("mac 25l3205a" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), + FLASH_ID("mac 25l6405" , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000), + FLASH_ID("wbd w25q128" , 0xd8, 0x001840EF, 0x100, 0x10000, 0x1000000), +}; + +/* + * smi_wait_xfer_finish - Wait until TFF is set in status register + * @timeout: timeout in milliseconds + * + * Wait until TFF is set in status register + */ +static int smi_wait_xfer_finish(int timeout) +{ + ulong start = get_timer(0); + + while (get_timer(start) < timeout) { + if (readl(&smicntl->smi_sr) & TFF) + return 0; + + /* Try after 10 ms */ + udelay(10); + }; + + return -1; +} + +/* + * smi_read_id - Read flash id + * @info: flash_info structure pointer + * @banknum: bank number + * + * Read the flash id present at bank #banknum + */ +static unsigned int smi_read_id(flash_info_t *info, int banknum) +{ + unsigned int value; + + writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); + writel(READ_ID, &smicntl->smi_tr); + writel((banknum << BANKSEL_SHIFT) | SEND | TX_LEN_1 | RX_LEN_3, + &smicntl->smi_cr2); + + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -EIO; + + value = (readl(&smicntl->smi_rr) & 0x00FFFFFF); + + writel(readl(&smicntl->smi_sr) & ~TFF, &smicntl->smi_sr); + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); + + return value; +} + +/* + * flash_get_size - Detect the SMI flash by reading the ID. + * @base: Base address of the flash area bank #banknum + * @banknum: Bank number + * + * Detect the SMI flash by reading the ID. Initializes the flash_info structure + * with size, sector count etc. + */ +static ulong flash_get_size(ulong base, int banknum) +{ + flash_info_t *info = &flash_info[banknum]; + int value; + int i; + + value = smi_read_id(info, banknum); + + if (value < 0) { + printf("Flash id could not be read\n"); + return 0; + } + + /* Matches chip-id to entire list of 'serial-nor flash' ids */ + for (i = 0; i < ARRAY_SIZE(flash_devices); i++) { + if (flash_devices[i].device_id == value) { + info->size = flash_devices[i].size_in_bytes; + info->flash_id = value; + info->start[0] = base; + info->sector_count = + info->size/flash_devices[i].sectorsize; + + return info->size; + } + } + + return 0; +} + +/* + * smi_read_sr - Read status register of SMI + * @bank: bank number + * + * This routine will get the status register of the flash chip present at the + * given bank + */ +static int smi_read_sr(int bank) +{ + u32 ctrlreg1, val; + + /* store the CTRL REG1 state */ + ctrlreg1 = readl(&smicntl->smi_cr1); + + /* Program SMI in HW Mode */ + writel(readl(&smicntl->smi_cr1) & ~(SW_MODE | WB_MODE), + &smicntl->smi_cr1); + + /* Performing a RSR instruction in HW mode */ + writel((bank << BANKSEL_SHIFT) | RD_STATUS_REG, &smicntl->smi_cr2); + + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -1; + + val = readl(&smicntl->smi_sr); + + /* Restore the CTRL REG1 state */ + writel(ctrlreg1, &smicntl->smi_cr1); + + return val; +} + +/* + * smi_wait_till_ready - Wait till last operation is over. + * @bank: bank number shifted. + * @timeout: timeout in milliseconds. + * + * This routine checks for WIP(write in progress)bit in Status register(SMSR-b0) + * The routine checks for #timeout loops, each at interval of 1 milli-second. + * If successful the routine returns 0. + */ +static int smi_wait_till_ready(int bank, int timeout) +{ + int sr; + ulong start = get_timer(0); + + /* One chip guarantees max 5 msec wait here after page writes, + but potentially three seconds (!) after page erase. */ + while (get_timer(start) < timeout) { + sr = smi_read_sr(bank); + if ((sr >= 0) && (!(sr & WIP_BIT))) + return 0; + + /* Try again after 10 usec */ + udelay(10); + } while (timeout--); + + printf("SMI controller is still in wait, timeout=%d\n", timeout); + return -EIO; +} + +/* + * smi_write_enable - Enable the flash to do write operation + * @bank: bank number + * + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static int smi_write_enable(int bank) +{ + u32 ctrlreg1; + u32 start; + int timeout = WMODE_TOUT; + int sr; + + /* Store the CTRL REG1 state */ + ctrlreg1 = readl(&smicntl->smi_cr1); + + /* Program SMI in H/W Mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); + + /* Give the Flash, Write Enable command */ + writel((bank << BANKSEL_SHIFT) | WE, &smicntl->smi_cr2); + + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -1; + + /* Restore the CTRL REG1 state */ + writel(ctrlreg1, &smicntl->smi_cr1); + + start = get_timer(0); + while (get_timer(start) < timeout) { + sr = smi_read_sr(bank); + if ((sr >= 0) && (sr & (1 << (bank + WM_SHIFT)))) + return 0; + + /* Try again after 10 usec */ + udelay(10); + }; + + return -1; +} + +/* + * smi_init - SMI initialization routine + * + * SMI initialization routine. Sets SMI control register1. + */ +void smi_init(void) +{ + /* Setting the fast mode values. SMI working at 166/4 = 41.5 MHz */ + writel(HOLD1 | FAST_MODE | BANK_EN | DSEL_TIME | PRESCAL4, + &smicntl->smi_cr1); +} + +/* + * smi_sector_erase - Erase flash sector + * @info: flash_info structure pointer + * @sector: sector number + * + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static int smi_sector_erase(flash_info_t *info, unsigned int sector) +{ + int bank; + unsigned int sect_add; + unsigned int instruction; + + switch (info->start[0]) { + case SMIBANK0_BASE: + bank = BANK0; + break; + case SMIBANK1_BASE: + bank = BANK1; + break; + case SMIBANK2_BASE: + bank = BANK2; + break; + case SMIBANK3_BASE: + bank = BANK3; + break; + default: + return -1; + } + + sect_add = sector * (info->size / info->sector_count); + instruction = ((sect_add >> 8) & 0x0000FF00) | SECTOR_ERASE; + + writel(readl(&smicntl->smi_sr) & ~(ERF1 | ERF2), &smicntl->smi_sr); + + /* Wait until finished previous write command. */ + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; + + /* Send write enable, before erase commands. */ + if (smi_write_enable(bank)) + return -EIO; + + /* Put SMI in SW mode */ + writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); + + /* Send Sector Erase command in SW Mode */ + writel(instruction, &smicntl->smi_tr); + writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, + &smicntl->smi_cr2); + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -EIO; + + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; + + /* Put SMI in HW mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, + &smicntl->smi_cr1); + + return 0; +} + +/* + * smi_write - Write to SMI flash + * @src_addr: source buffer + * @dst_addr: destination buffer + * @length: length to write in bytes + * @bank: bank base address + * + * Write to SMI flash + */ +static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, + unsigned int length, ulong bank_addr) +{ + u8 *src_addr8 = (u8 *)src_addr; + u8 *dst_addr8 = (u8 *)dst_addr; + int banknum; + int i; + + switch (bank_addr) { + case SMIBANK0_BASE: + banknum = BANK0; + break; + case SMIBANK1_BASE: + banknum = BANK1; + break; + case SMIBANK2_BASE: + banknum = BANK2; + break; + case SMIBANK3_BASE: + banknum = BANK3; + break; + default: + return -1; + } + + if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) + return -EBUSY; + + /* Set SMI in Hardware Mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); + + if (smi_write_enable(banknum)) + return -EIO; + + /* Perform the write command */ + for (i = 0; i < length; i += 4) { + if (((ulong) (dst_addr) % SFLASH_PAGE_SIZE) == 0) { + if (smi_wait_till_ready(banknum, + CONFIG_SYS_FLASH_WRITE_TOUT)) + return -EBUSY; + + if (smi_write_enable(banknum)) + return -EIO; + } + + if (length < 4) { + int k; + + /* + * Handle special case, where length < 4 (redundant env) + */ + for (k = 0; k < length; k++) + *dst_addr8++ = *src_addr8++; + } else { + /* Normal 32bit write */ + *dst_addr++ = *src_addr++; + } + + if ((readl(&smicntl->smi_sr) & (ERF1 | ERF2))) + return -EIO; + } + + if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) + return -EBUSY; + + writel(readl(&smicntl->smi_sr) & ~(WCF), &smicntl->smi_sr); + + return 0; +} + +/* + * write_buff - Write to SMI flash + * @info: flash info structure + * @src: source buffer + * @dest_addr: destination buffer + * @length: length to write in words + * + * Write to SMI flash + */ +int write_buff(flash_info_t *info, uchar *src, ulong dest_addr, ulong length) +{ + return smi_write((unsigned int *)src, (unsigned int *)dest_addr, + length, info->start[0]); +} + +/* + * flash_init - SMI flash initialization + * + * SMI flash initialization + */ +unsigned long flash_init(void) +{ + unsigned long size = 0; + int i, j; + + smi_init(); + + for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { + flash_info[i].flash_id = FLASH_UNKNOWN; + size += flash_info[i].size = flash_get_size(bank_base[i], i); + } + + for (j = 0; j < CONFIG_SYS_MAX_FLASH_BANKS; j++) { + for (i = 1; i < flash_info[j].sector_count; i++) + flash_info[j].start[i] = + flash_info[j].start[i - 1] + + flash_info->size / flash_info->sector_count; + + } + + return size; +} + +/* + * flash_print_info - Print SMI flash information + * + * Print SMI flash information + */ +void flash_print_info(flash_info_t *info) +{ + int i; + if (info->flash_id == FLASH_UNKNOWN) { + puts("missing or unknown FLASH type\n"); + return; + } + + if (info->size >= 0x100000) + printf(" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + else + printf(" Size: %ld KB in %d Sectors\n", + info->size >> 10, info->sector_count); + + puts(" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { +#ifdef CONFIG_SYS_FLASH_EMPTY_INFO + int size; + int erased; + u32 *flash; + + /* + * Check if whole sector is erased + */ + size = (info->size) / (info->sector_count); + flash = (u32 *) info->start[i]; + size = size / sizeof(int); + + while ((size--) && (*flash++ == ~0)) + ; + + size++; + if (size) + erased = 0; + else + erased = 1; + + if ((i % 5) == 0) + printf("\n"); + + printf(" %08lX%s%s", + info->start[i], + erased ? " E" : " ", info->protect[i] ? "RO " : " "); +#else + if ((i % 5) == 0) + printf("\n "); + printf(" %08lX%s", + info->start[i], info->protect[i] ? " (RO) " : " "); +#endif + } + putc('\n'); + return; +} + +/* + * flash_erase - Erase SMI flash + * + * Erase SMI flash + */ +int flash_erase(flash_info_t *info, int s_first, int s_last) +{ + int rcode = 0; + int prot = 0; + flash_sect_t sect; + + if ((s_first < 0) || (s_first > s_last)) { + puts("- no sectors to erase\n"); + return 1; + } + + for (sect = s_first; sect <= s_last; ++sect) { + if (info->protect[sect]) + prot++; + } + if (prot) { + printf("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + putc('\n'); + } + + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { + if (smi_sector_erase(info, sect)) + rcode = 1; + else + putc('.'); + } + } + puts(" done\n"); + return rcode; +} +#endif diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/Makefile b/qemu/roms/u-boot/drivers/mtd/ubi/Makefile new file mode 100644 index 000000000..56c282347 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/Makefile @@ -0,0 +1,10 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o scan.o crc32.o +obj-y += misc.o +obj-y += debug.o diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/build.c b/qemu/roms/u-boot/drivers/mtd/ubi/build.c new file mode 100644 index 000000000..6d86c0b6b --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/build.c @@ -0,0 +1,1181 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2007 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём), + * Frank Haverkamp + */ + +/* + * This file includes UBI initialization and building of UBI devices. + * + * When UBI is initialized, it attaches all the MTD devices specified as the + * module load parameters or the kernel boot parameters. If MTD devices were + * specified, UBI does not attach any MTD device, but it is possible to do + * later using the "UBI control device". + * + * At the moment we only attach UBI devices by scanning, which will become a + * bottleneck when flashes reach certain large size. Then one may improve UBI + * and add other methods, although it does not seem to be easy to do. + */ + +#ifdef UBI_LINUX +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#include +#include "ubi.h" + +#if (CONFIG_SYS_MALLOC_LEN < (512 << 10)) +#error Malloc area too small for UBI, increase CONFIG_SYS_MALLOC_LEN to >= 512k +#endif + +/* Maximum length of the 'mtd=' parameter */ +#define MTD_PARAM_LEN_MAX 64 + +/** + * struct mtd_dev_param - MTD device parameter description data structure. + * @name: MTD device name or number string + * @vid_hdr_offs: VID header offset + */ +struct mtd_dev_param +{ + char name[MTD_PARAM_LEN_MAX]; + int vid_hdr_offs; +}; + +/* Numbers of elements set in the @mtd_dev_param array */ +static int mtd_devs = 0; + +/* MTD devices specification parameters */ +static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; + +/* Root UBI "class" object (corresponds to '//class/ubi/') */ +struct class *ubi_class; + +#ifdef UBI_LINUX +/* Slab cache for wear-leveling entries */ +struct kmem_cache *ubi_wl_entry_slab; + +/* UBI control character device */ +static struct miscdevice ubi_ctrl_cdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ubi_ctrl", + .fops = &ubi_ctrl_cdev_operations, +}; +#endif + +/* All UBI devices in system */ +struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; + +#ifdef UBI_LINUX +/* Serializes UBI devices creations and removals */ +DEFINE_MUTEX(ubi_devices_mutex); + +/* Protects @ubi_devices and @ubi->ref_count */ +static DEFINE_SPINLOCK(ubi_devices_lock); + +/* "Show" method for files in '//class/ubi/' */ +static ssize_t ubi_version_show(struct class *class, char *buf) +{ + return sprintf(buf, "%d\n", UBI_VERSION); +} + +/* UBI version attribute ('//class/ubi/version') */ +static struct class_attribute ubi_version = + __ATTR(version, S_IRUGO, ubi_version_show, NULL); + +static ssize_t dev_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf); + +/* UBI device attributes (correspond to files in '//class/ubi/ubiX') */ +static struct device_attribute dev_eraseblock_size = + __ATTR(eraseblock_size, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_avail_eraseblocks = + __ATTR(avail_eraseblocks, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_total_eraseblocks = + __ATTR(total_eraseblocks, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_volumes_count = + __ATTR(volumes_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_max_ec = + __ATTR(max_ec, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_reserved_for_bad = + __ATTR(reserved_for_bad, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_bad_peb_count = + __ATTR(bad_peb_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_max_vol_count = + __ATTR(max_vol_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_min_io_size = + __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_bgt_enabled = + __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_mtd_num = + __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); +#endif + +/** + * ubi_get_device - get UBI device. + * @ubi_num: UBI device number + * + * This function returns UBI device description object for UBI device number + * @ubi_num, or %NULL if the device does not exist. This function increases the + * device reference count to prevent removal of the device. In other words, the + * device cannot be removed if its reference count is not zero. + */ +struct ubi_device *ubi_get_device(int ubi_num) +{ + struct ubi_device *ubi; + + spin_lock(&ubi_devices_lock); + ubi = ubi_devices[ubi_num]; + if (ubi) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); + } + spin_unlock(&ubi_devices_lock); + + return ubi; +} + +/** + * ubi_put_device - drop an UBI device reference. + * @ubi: UBI device description object + */ +void ubi_put_device(struct ubi_device *ubi) +{ + spin_lock(&ubi_devices_lock); + ubi->ref_count -= 1; + put_device(&ubi->dev); + spin_unlock(&ubi_devices_lock); +} + +/** + * ubi_get_by_major - get UBI device description object by character device + * major number. + * @major: major number + * + * This function is similar to 'ubi_get_device()', but it searches the device + * by its major number. + */ +struct ubi_device *ubi_get_by_major(int major) +{ + int i; + struct ubi_device *ubi; + + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; + if (ubi && MAJOR(ubi->cdev.dev) == major) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); + spin_unlock(&ubi_devices_lock); + return ubi; + } + } + spin_unlock(&ubi_devices_lock); + + return NULL; +} + +/** + * ubi_major2num - get UBI device number by character device major number. + * @major: major number + * + * This function searches UBI device number object by its major number. If UBI + * device was not found, this function returns -ENODEV, otherwise the UBI device + * number is returned. + */ +int ubi_major2num(int major) +{ + int i, ubi_num = -ENODEV; + + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + struct ubi_device *ubi = ubi_devices[i]; + + if (ubi && MAJOR(ubi->cdev.dev) == major) { + ubi_num = ubi->ubi_num; + break; + } + } + spin_unlock(&ubi_devices_lock); + + return ubi_num; +} + +#ifdef UBI_LINUX +/* "Show" method for files in '//class/ubi/ubiX/' */ +static ssize_t dev_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct ubi_device *ubi; + + /* + * The below code looks weird, but it actually makes sense. We get the + * UBI device reference from the contained 'struct ubi_device'. But it + * is unclear if the device was removed or not yet. Indeed, if the + * device was removed before we increased its reference count, + * 'ubi_get_device()' will return -ENODEV and we fail. + * + * Remember, 'struct ubi_device' is freed in the release function, so + * we still can use 'ubi->ubi_num'. + */ + ubi = container_of(dev, struct ubi_device, dev); + ubi = ubi_get_device(ubi->ubi_num); + if (!ubi) + return -ENODEV; + + if (attr == &dev_eraseblock_size) + ret = sprintf(buf, "%d\n", ubi->leb_size); + else if (attr == &dev_avail_eraseblocks) + ret = sprintf(buf, "%d\n", ubi->avail_pebs); + else if (attr == &dev_total_eraseblocks) + ret = sprintf(buf, "%d\n", ubi->good_peb_count); + else if (attr == &dev_volumes_count) + ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT); + else if (attr == &dev_max_ec) + ret = sprintf(buf, "%d\n", ubi->max_ec); + else if (attr == &dev_reserved_for_bad) + ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); + else if (attr == &dev_bad_peb_count) + ret = sprintf(buf, "%d\n", ubi->bad_peb_count); + else if (attr == &dev_max_vol_count) + ret = sprintf(buf, "%d\n", ubi->vtbl_slots); + else if (attr == &dev_min_io_size) + ret = sprintf(buf, "%d\n", ubi->min_io_size); + else if (attr == &dev_bgt_enabled) + ret = sprintf(buf, "%d\n", ubi->thread_enabled); + else if (attr == &dev_mtd_num) + ret = sprintf(buf, "%d\n", ubi->mtd->index); + else + ret = -EINVAL; + + ubi_put_device(ubi); + return ret; +} + +/* Fake "release" method for UBI devices */ +static void dev_release(struct device *dev) { } + +/** + * ubi_sysfs_init - initialize sysfs for an UBI device. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int ubi_sysfs_init(struct ubi_device *ubi) +{ + int err; + + ubi->dev.release = dev_release; + ubi->dev.devt = ubi->cdev.dev; + ubi->dev.class = ubi_class; + sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); + err = device_register(&ubi->dev); + if (err) + return err; + + err = device_create_file(&ubi->dev, &dev_eraseblock_size); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_avail_eraseblocks); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_total_eraseblocks); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_volumes_count); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_max_ec); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_reserved_for_bad); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_bad_peb_count); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_max_vol_count); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_min_io_size); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_bgt_enabled); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_mtd_num); + return err; +} + +/** + * ubi_sysfs_close - close sysfs for an UBI device. + * @ubi: UBI device description object + */ +static void ubi_sysfs_close(struct ubi_device *ubi) +{ + device_remove_file(&ubi->dev, &dev_mtd_num); + device_remove_file(&ubi->dev, &dev_bgt_enabled); + device_remove_file(&ubi->dev, &dev_min_io_size); + device_remove_file(&ubi->dev, &dev_max_vol_count); + device_remove_file(&ubi->dev, &dev_bad_peb_count); + device_remove_file(&ubi->dev, &dev_reserved_for_bad); + device_remove_file(&ubi->dev, &dev_max_ec); + device_remove_file(&ubi->dev, &dev_volumes_count); + device_remove_file(&ubi->dev, &dev_total_eraseblocks); + device_remove_file(&ubi->dev, &dev_avail_eraseblocks); + device_remove_file(&ubi->dev, &dev_eraseblock_size); + device_unregister(&ubi->dev); +} +#endif + +/** + * kill_volumes - destroy all volumes. + * @ubi: UBI device description object + */ +static void kill_volumes(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) + ubi_free_volume(ubi, ubi->volumes[i]); +} + +/** + * uif_init - initialize user interfaces for an UBI device. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int uif_init(struct ubi_device *ubi) +{ + int i, err; +#ifdef UBI_LINUX + dev_t dev; +#endif + + sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); + + /* + * Major numbers for the UBI character devices are allocated + * dynamically. Major numbers of volume character devices are + * equivalent to ones of the corresponding UBI character device. Minor + * numbers of UBI character devices are 0, while minor numbers of + * volume character devices start from 1. Thus, we allocate one major + * number and ubi->vtbl_slots + 1 minor numbers. + */ + err = alloc_chrdev_region(&dev, 0, ubi->vtbl_slots + 1, ubi->ubi_name); + if (err) { + ubi_err("cannot register UBI character devices"); + return err; + } + + ubi_assert(MINOR(dev) == 0); + cdev_init(&ubi->cdev, &ubi_cdev_operations); + dbg_msg("%s major is %u", ubi->ubi_name, MAJOR(dev)); + ubi->cdev.owner = THIS_MODULE; + + err = cdev_add(&ubi->cdev, dev, 1); + if (err) { + ubi_err("cannot add character device"); + goto out_unreg; + } + + err = ubi_sysfs_init(ubi); + if (err) + goto out_sysfs; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) { + err = ubi_add_volume(ubi, ubi->volumes[i]); + if (err) { + ubi_err("cannot add volume %d", i); + goto out_volumes; + } + } + + return 0; + +out_volumes: + kill_volumes(ubi); +out_sysfs: + ubi_sysfs_close(ubi); + cdev_del(&ubi->cdev); +out_unreg: + unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); + ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err); + return err; +} + +/** + * uif_close - close user interfaces for an UBI device. + * @ubi: UBI device description object + */ +static void uif_close(struct ubi_device *ubi) +{ + kill_volumes(ubi); + ubi_sysfs_close(ubi); + cdev_del(&ubi->cdev); + unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); +} + +/** + * attach_by_scanning - attach an MTD device using scanning method. + * @ubi: UBI device descriptor + * + * This function returns zero in case of success and a negative error code in + * case of failure. + * + * Note, currently this is the only method to attach UBI devices. Hopefully in + * the future we'll have more scalable attaching methods and avoid full media + * scanning. But even in this case scanning will be needed as a fall-back + * attaching method if there are some on-flash table corruptions. + */ +static int attach_by_scanning(struct ubi_device *ubi) +{ + int err; + struct ubi_scan_info *si; + + si = ubi_scan(ubi); + if (IS_ERR(si)) + return PTR_ERR(si); + + ubi->bad_peb_count = si->bad_peb_count; + ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count; + ubi->max_ec = si->max_ec; + ubi->mean_ec = si->mean_ec; + + err = ubi_read_volume_table(ubi, si); + if (err) + goto out_si; + + err = ubi_eba_init_scan(ubi, si); + if (err) + goto out_vtbl; + + err = ubi_wl_init_scan(ubi, si); + if (err) + goto out_eba; + + ubi_scan_destroy_si(si); + return 0; + +out_eba: + ubi_eba_close(ubi); +out_vtbl: + vfree(ubi->vtbl); +out_si: + ubi_scan_destroy_si(si); + return err; +} + +/** + * io_init - initialize I/O unit for a given UBI device. + * @ubi: UBI device description object + * + * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are + * assumed: + * o EC header is always at offset zero - this cannot be changed; + * o VID header starts just after the EC header at the closest address + * aligned to @io->hdrs_min_io_size; + * o data starts just after the VID header at the closest address aligned to + * @io->min_io_size + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int io_init(struct ubi_device *ubi) +{ + if (ubi->mtd->numeraseregions != 0) { + /* + * Some flashes have several erase regions. Different regions + * may have different eraseblock size and other + * characteristics. It looks like mostly multi-region flashes + * have one "main" region and one or more small regions to + * store boot loader code or boot parameters or whatever. I + * guess we should just pick the largest region. But this is + * not implemented. + */ + ubi_err("multiple regions, not implemented"); + return -EINVAL; + } + + if (ubi->vid_hdr_offset < 0) + return -EINVAL; + + /* + * Note, in this implementation we support MTD devices with 0x7FFFFFFF + * physical eraseblocks maximum. + */ + + ubi->peb_size = ubi->mtd->erasesize; + ubi->peb_count = mtd_div_by_eb(ubi->mtd->size, ubi->mtd); + ubi->flash_size = ubi->mtd->size; + + if (mtd_can_have_bb(ubi->mtd)) + ubi->bad_allowed = 1; + + ubi->min_io_size = ubi->mtd->writesize; + ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; + + /* + * Make sure minimal I/O unit is power of 2. Note, there is no + * fundamental reason for this assumption. It is just an optimization + * which allows us to avoid costly division operations. + */ + if (!is_power_of_2(ubi->min_io_size)) { + ubi_err("min. I/O unit (%d) is not power of 2", + ubi->min_io_size); + return -EINVAL; + } + + ubi_assert(ubi->hdrs_min_io_size > 0); + ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size); + ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0); + + /* Calculate default aligned sizes of EC and VID headers */ + ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size); + ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size); + + dbg_msg("min_io_size %d", ubi->min_io_size); + dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size); + dbg_msg("ec_hdr_alsize %d", ubi->ec_hdr_alsize); + dbg_msg("vid_hdr_alsize %d", ubi->vid_hdr_alsize); + + if (ubi->vid_hdr_offset == 0) + /* Default offset */ + ubi->vid_hdr_offset = ubi->vid_hdr_aloffset = + ubi->ec_hdr_alsize; + else { + ubi->vid_hdr_aloffset = ubi->vid_hdr_offset & + ~(ubi->hdrs_min_io_size - 1); + ubi->vid_hdr_shift = ubi->vid_hdr_offset - + ubi->vid_hdr_aloffset; + } + + /* Similar for the data offset */ + ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE; + ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); + + dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset); + dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset); + dbg_msg("vid_hdr_shift %d", ubi->vid_hdr_shift); + dbg_msg("leb_start %d", ubi->leb_start); + + /* The shift must be aligned to 32-bit boundary */ + if (ubi->vid_hdr_shift % 4) { + ubi_err("unaligned VID header shift %d", + ubi->vid_hdr_shift); + return -EINVAL; + } + + /* Check sanity */ + if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE || + ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE || + ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE || + ubi->leb_start & (ubi->min_io_size - 1)) { + ubi_err("bad VID header (%d) or data offsets (%d)", + ubi->vid_hdr_offset, ubi->leb_start); + return -EINVAL; + } + + /* + * It may happen that EC and VID headers are situated in one minimal + * I/O unit. In this case we can only accept this UBI image in + * read-only mode. + */ + if (ubi->vid_hdr_offset + UBI_VID_HDR_SIZE <= ubi->hdrs_min_io_size) { + ubi_warn("EC and VID headers are in the same minimal I/O unit, " + "switch to read-only mode"); + ubi->ro_mode = 1; + } + + ubi->leb_size = ubi->peb_size - ubi->leb_start; + + if (!(ubi->mtd->flags & MTD_WRITEABLE)) { + ubi_msg("MTD device %d is write-protected, attach in " + "read-only mode", ubi->mtd->index); + ubi->ro_mode = 1; + } + + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + ubi->peb_size, ubi->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); + if (ubi->hdrs_min_io_size != ubi->min_io_size) + ubi_msg("sub-page size: %d", + ubi->hdrs_min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); + ubi_msg("data offset: %d", ubi->leb_start); + + /* + * Note, ideally, we have to initialize ubi->bad_peb_count here. But + * unfortunately, MTD does not provide this information. We should loop + * over all physical eraseblocks and invoke mtd->block_is_bad() for + * each physical eraseblock. So, we skip ubi->bad_peb_count + * uninitialized and initialize it after scanning. + */ + + return 0; +} + +/** + * autoresize - re-size the volume which has the "auto-resize" flag set. + * @ubi: UBI device description object + * @vol_id: ID of the volume to re-size + * + * This function re-sizes the volume marked by the @UBI_VTBL_AUTORESIZE_FLG in + * the volume table to the largest possible size. See comments in ubi-header.h + * for more description of the flag. Returns zero in case of success and a + * negative error code in case of failure. + */ +static int autoresize(struct ubi_device *ubi, int vol_id) +{ + struct ubi_volume_desc desc; + struct ubi_volume *vol = ubi->volumes[vol_id]; + int err, old_reserved_pebs = vol->reserved_pebs; + + /* + * Clear the auto-resize flag in the volume in-memory copy of the + * volume table, and 'ubi_resize_volume()' will propogate this change + * to the flash. + */ + ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG; + + if (ubi->avail_pebs == 0) { + struct ubi_vtbl_record vtbl_rec; + + /* + * No avalilable PEBs to re-size the volume, clear the flag on + * flash and exit. + */ + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], + sizeof(struct ubi_vtbl_record)); + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + ubi_err("cannot clean auto-resize flag for volume %d", + vol_id); + } else { + desc.vol = vol; + err = ubi_resize_volume(&desc, + old_reserved_pebs + ubi->avail_pebs); + if (err) + ubi_err("cannot auto-resize volume %d", vol_id); + } + + if (err) + return err; + + ubi_msg("volume %d (\"%s\") re-sized from %d to %d LEBs", vol_id, + vol->name, old_reserved_pebs, vol->reserved_pebs); + return 0; +} + +/** + * ubi_attach_mtd_dev - attach an MTD device. + * @mtd_dev: MTD device description object + * @ubi_num: number to assign to the new UBI device + * @vid_hdr_offset: VID header offset + * + * This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number + * to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in + * which case this function finds a vacant device nubert and assings it + * automatically. Returns the new UBI device number in case of success and a + * negative error code in case of failure. + * + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. + */ +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) +{ + struct ubi_device *ubi; + int i, err; + + /* + * Check if we already have the same MTD device attached. + * + * Note, this function assumes that UBI devices creations and deletions + * are serialized, so it does not take the &ubi_devices_lock. + */ + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; + if (ubi && mtd->index == ubi->mtd->index) { + dbg_err("mtd%d is already attached to ubi%d", + mtd->index, i); + return -EEXIST; + } + } + + /* + * Make sure this MTD device is not emulated on top of an UBI volume + * already. Well, generally this recursion works fine, but there are + * different problems like the UBI module takes a reference to itself + * by attaching (and thus, opening) the emulated MTD device. This + * results in inability to unload the module. And in general it makes + * no sense to attach emulated MTD devices, so we prohibit this. + */ + if (mtd->type == MTD_UBIVOLUME) { + ubi_err("refuse attaching mtd%d - it is already emulated on " + "top of UBI", mtd->index); + return -EINVAL; + } + + if (ubi_num == UBI_DEV_NUM_AUTO) { + /* Search for an empty slot in the @ubi_devices array */ + for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) + if (!ubi_devices[ubi_num]) + break; + if (ubi_num == UBI_MAX_DEVICES) { + dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES); + return -ENFILE; + } + } else { + if (ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; + + /* Make sure ubi_num is not busy */ + if (ubi_devices[ubi_num]) { + dbg_err("ubi%d already exists", ubi_num); + return -EEXIST; + } + } + + ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL); + if (!ubi) + return -ENOMEM; + + ubi->mtd = mtd; + ubi->ubi_num = ubi_num; + ubi->vid_hdr_offset = vid_hdr_offset; + ubi->autoresize_vol_id = -1; + + mutex_init(&ubi->buf_mutex); + mutex_init(&ubi->ckvol_mutex); + mutex_init(&ubi->volumes_mutex); + spin_lock_init(&ubi->volumes_lock); + + ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num); + + err = io_init(ubi); + if (err) + goto out_free; + + err = -ENOMEM; + ubi->peb_buf1 = vmalloc(ubi->peb_size); + if (!ubi->peb_buf1) + goto out_free; + + ubi->peb_buf2 = vmalloc(ubi->peb_size); + if (!ubi->peb_buf2) + goto out_free; + +#ifdef CONFIG_MTD_UBI_DEBUG + mutex_init(&ubi->dbg_buf_mutex); + ubi->dbg_peb_buf = vmalloc(ubi->peb_size); + if (!ubi->dbg_peb_buf) + goto out_free; +#endif + + err = attach_by_scanning(ubi); + if (err) { + dbg_err("failed to attach by scanning, error %d", err); + goto out_free; + } + + if (ubi->autoresize_vol_id != -1) { + err = autoresize(ubi, ubi->autoresize_vol_id); + if (err) + goto out_detach; + } + + err = uif_init(ubi); + if (err) + goto out_detach; + + ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); + if (IS_ERR(ubi->bgt_thread)) { + err = PTR_ERR(ubi->bgt_thread); + ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, + err); + goto out_uif; + } + + ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num); + ubi_msg("MTD device name: \"%s\"", mtd->name); + ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); + ubi_msg("number of good PEBs: %d", ubi->good_peb_count); + ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); + ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); + ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); + ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); + ubi_msg("number of user volumes: %d", + ubi->vol_count - UBI_INT_VOL_COUNT); + ubi_msg("available PEBs: %d", ubi->avail_pebs); + ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs); + ubi_msg("number of PEBs reserved for bad PEB handling: %d", + ubi->beb_rsvd_pebs); + ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec); + + /* Enable the background thread */ + if (!DBG_DISABLE_BGT) { + ubi->thread_enabled = 1; + wake_up_process(ubi->bgt_thread); + } + + ubi_devices[ubi_num] = ubi; + return ubi_num; + +out_uif: + uif_close(ubi); +out_detach: + ubi_eba_close(ubi); + ubi_wl_close(ubi); + vfree(ubi->vtbl); +out_free: + vfree(ubi->peb_buf1); + vfree(ubi->peb_buf2); +#ifdef CONFIG_MTD_UBI_DEBUG + vfree(ubi->dbg_peb_buf); +#endif + kfree(ubi); + return err; +} + +/** + * ubi_detach_mtd_dev - detach an MTD device. + * @ubi_num: UBI device number to detach from + * @anyway: detach MTD even if device reference count is not zero + * + * This function destroys an UBI device number @ubi_num and detaches the + * underlying MTD device. Returns zero in case of success and %-EBUSY if the + * UBI device is busy and cannot be destroyed, and %-EINVAL if it does not + * exist. + * + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. + */ +int ubi_detach_mtd_dev(int ubi_num, int anyway) +{ + struct ubi_device *ubi; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; + + spin_lock(&ubi_devices_lock); + ubi = ubi_devices[ubi_num]; + if (!ubi) { + spin_unlock(&ubi_devices_lock); + return -EINVAL; + } + + if (ubi->ref_count) { + if (!anyway) { + spin_unlock(&ubi_devices_lock); + return -EBUSY; + } + /* This may only happen if there is a bug */ + ubi_err("%s reference count %d, destroy anyway", + ubi->ubi_name, ubi->ref_count); + } + ubi_devices[ubi_num] = NULL; + spin_unlock(&ubi_devices_lock); + + ubi_assert(ubi_num == ubi->ubi_num); + dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); + + /* + * Before freeing anything, we have to stop the background thread to + * prevent it from doing anything on this device while we are freeing. + */ + if (ubi->bgt_thread) + kthread_stop(ubi->bgt_thread); + + uif_close(ubi); + ubi_eba_close(ubi); + ubi_wl_close(ubi); + vfree(ubi->vtbl); + put_mtd_device(ubi->mtd); + vfree(ubi->peb_buf1); + vfree(ubi->peb_buf2); +#ifdef CONFIG_MTD_UBI_DEBUG + vfree(ubi->dbg_peb_buf); +#endif + ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); + kfree(ubi); + return 0; +} + +/** + * find_mtd_device - open an MTD device by its name or number. + * @mtd_dev: name or number of the device + * + * This function tries to open and MTD device described by @mtd_dev string, + * which is first treated as an ASCII number, and if it is not true, it is + * treated as MTD device name. Returns MTD device description object in case of + * success and a negative error code in case of failure. + */ +static struct mtd_info * __init open_mtd_device(const char *mtd_dev) +{ + struct mtd_info *mtd; + int mtd_num; + char *endp; + + mtd_num = simple_strtoul(mtd_dev, &endp, 0); + if (*endp != '\0' || mtd_dev == endp) { + /* + * This does not look like an ASCII integer, probably this is + * MTD device name. + */ + mtd = get_mtd_device_nm(mtd_dev); + } else + mtd = get_mtd_device(NULL, mtd_num); + + return mtd; +} + +int __init ubi_init(void) +{ + int err, i, k; + + /* Ensure that EC and VID headers have correct size */ + BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64); + BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); + + if (mtd_devs > UBI_MAX_DEVICES) { + ubi_err("too many MTD devices, maximum is %d", UBI_MAX_DEVICES); + return -EINVAL; + } + + /* Create base sysfs directory and sysfs files */ + ubi_class = class_create(THIS_MODULE, UBI_NAME_STR); + if (IS_ERR(ubi_class)) { + err = PTR_ERR(ubi_class); + ubi_err("cannot create UBI class"); + goto out; + } + + err = class_create_file(ubi_class, &ubi_version); + if (err) { + ubi_err("cannot create sysfs file"); + goto out_class; + } + + err = misc_register(&ubi_ctrl_cdev); + if (err) { + ubi_err("cannot register device"); + goto out_version; + } + +#ifdef UBI_LINUX + ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", + sizeof(struct ubi_wl_entry), + 0, 0, NULL); + if (!ubi_wl_entry_slab) + goto out_dev_unreg; +#endif + + /* Attach MTD devices */ + for (i = 0; i < mtd_devs; i++) { + struct mtd_dev_param *p = &mtd_dev_param[i]; + struct mtd_info *mtd; + + cond_resched(); + + mtd = open_mtd_device(p->name); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + goto out_detach; + } + + mutex_lock(&ubi_devices_mutex); + err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, + p->vid_hdr_offs); + mutex_unlock(&ubi_devices_mutex); + if (err < 0) { + put_mtd_device(mtd); + ubi_err("cannot attach mtd%d", mtd->index); + goto out_detach; + } + } + + return 0; + +out_detach: + for (k = 0; k < i; k++) + if (ubi_devices[k]) { + mutex_lock(&ubi_devices_mutex); + ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1); + mutex_unlock(&ubi_devices_mutex); + } +#ifdef UBI_LINUX + kmem_cache_destroy(ubi_wl_entry_slab); +out_dev_unreg: +#endif + misc_deregister(&ubi_ctrl_cdev); +out_version: + class_remove_file(ubi_class, &ubi_version); +out_class: + class_destroy(ubi_class); +out: + mtd_devs = 0; + ubi_err("UBI error: cannot initialize UBI, error %d", err); + return err; +} +module_init(ubi_init); + +void __exit ubi_exit(void) +{ + int i; + + for (i = 0; i < UBI_MAX_DEVICES; i++) + if (ubi_devices[i]) { + mutex_lock(&ubi_devices_mutex); + ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1); + mutex_unlock(&ubi_devices_mutex); + } + kmem_cache_destroy(ubi_wl_entry_slab); + misc_deregister(&ubi_ctrl_cdev); + class_remove_file(ubi_class, &ubi_version); + class_destroy(ubi_class); + mtd_devs = 0; +} +module_exit(ubi_exit); + +/** + * bytes_str_to_int - convert a string representing number of bytes to an + * integer. + * @str: the string to convert + * + * This function returns positive resulting integer in case of success and a + * negative error code in case of failure. + */ +static int __init bytes_str_to_int(const char *str) +{ + char *endp; + unsigned long result; + + result = simple_strtoul(str, &endp, 0); + if (str == endp || result < 0) { + printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", + str); + return -EINVAL; + } + + switch (*endp) { + case 'G': + result *= 1024; + case 'M': + result *= 1024; + case 'K': + result *= 1024; + if (endp[1] == 'i' && endp[2] == 'B') + endp += 2; + case '\0': + break; + default: + printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", + str); + return -EINVAL; + } + + return result; +} + +/** + * ubi_mtd_param_parse - parse the 'mtd=' UBI parameter. + * @val: the parameter value to parse + * @kp: not used + * + * This function returns zero in case of success and a negative error code in + * case of error. + */ +int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) +{ + int i, len; + struct mtd_dev_param *p; + char buf[MTD_PARAM_LEN_MAX]; + char *pbuf = &buf[0]; + char *tokens[2] = {NULL, NULL}; + + if (!val) + return -EINVAL; + + if (mtd_devs == UBI_MAX_DEVICES) { + printk(KERN_ERR "UBI error: too many parameters, max. is %d\n", + UBI_MAX_DEVICES); + return -EINVAL; + } + + len = strnlen(val, MTD_PARAM_LEN_MAX); + if (len == MTD_PARAM_LEN_MAX) { + printk(KERN_ERR "UBI error: parameter \"%s\" is too long, " + "max. is %d\n", val, MTD_PARAM_LEN_MAX); + return -EINVAL; + } + + if (len == 0) { + printk(KERN_WARNING "UBI warning: empty 'mtd=' parameter - " + "ignored\n"); + return 0; + } + + strcpy(buf, val); + + /* Get rid of the final newline */ + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + for (i = 0; i < 2; i++) + tokens[i] = strsep(&pbuf, ","); + + if (pbuf) { + printk(KERN_ERR "UBI error: too many arguments at \"%s\"\n", + val); + return -EINVAL; + } + + p = &mtd_dev_param[mtd_devs]; + strcpy(&p->name[0], tokens[0]); + + if (tokens[1]) + p->vid_hdr_offs = bytes_str_to_int(tokens[1]); + + if (p->vid_hdr_offs < 0) + return p->vid_hdr_offs; + + mtd_devs += 1; + return 0; +} + +module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); +MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: " + "mtd=[,].\n" + "Multiple \"mtd\" parameters may be specified.\n" + "MTD devices may be specified by their number or name.\n" + "Optional \"vid_hdr_offs\" parameter specifies UBI VID " + "header position and data starting position to be used " + "by UBI.\n" + "Example: mtd=content,1984 mtd=4 - attach MTD device" + "with name \"content\" using VID header offset 1984, and " + "MTD device number 4 with default VID header offset."); + +MODULE_VERSION(__stringify(UBI_VERSION)); +MODULE_DESCRIPTION("UBI - Unsorted Block Images"); +MODULE_AUTHOR("Artem Bityutskiy"); +MODULE_LICENSE("GPL"); diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/crc32.c b/qemu/roms/u-boot/drivers/mtd/ubi/crc32.c new file mode 100644 index 000000000..f1bebf58c --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/crc32.c @@ -0,0 +1,510 @@ +/* + * Oct 15, 2000 Matt Domsch + * Nicer crc32 functions/docs submitted by linux@horizon.com. Thanks! + * Code was from the public domain, copyright abandoned. Code was + * subsequently included in the kernel, thus was re-licensed under the + * GNU GPL v2. + * + * Oct 12, 2000 Matt Domsch + * Same crc32 function was used in 5 other places in the kernel. + * I made one version, and deleted the others. + * There are various incantations of crc32(). Some use a seed of 0 or ~0. + * Some xor at the end with ~0. The generic crc32() function takes + * seed as an argument, and doesn't xor at the end. Then individual + * users can do whatever they need. + * drivers/net/smc9194.c uses seed ~0, doesn't xor with ~0. + * fs/jffs2 uses seed 0, doesn't xor with ~0. + * fs/partitions/efi.c uses seed ~0, xor's with ~0. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#ifdef UBI_LINUX +#include +#include +#include +#include +#endif +#include + +#include + +#ifdef UBI_LINUX +#include +#include +#include +#endif +#include "crc32defs.h" +#define CRC_LE_BITS 8 + +#if CRC_LE_BITS == 8 +#define tole(x) cpu_to_le32(x) +#define tobe(x) cpu_to_be32(x) +#else +#define tole(x) (x) +#define tobe(x) (x) +#endif +#include "crc32table.h" +#ifdef UBI_LINUX +MODULE_AUTHOR("Matt Domsch "); +MODULE_DESCRIPTION("Ethernet CRC32 calculations"); +MODULE_LICENSE("GPL"); +#endif +/** + * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 + * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for + * other uses, or the previous crc32 value if computing incrementally. + * @p: pointer to buffer over which CRC is run + * @len: length of buffer @p + */ +u32 crc32_le(u32 crc, unsigned char const *p, size_t len); + +#if CRC_LE_BITS == 1 +/* + * In fact, the table-based code will work in this case, but it can be + * simplified by inlining the table in ?: form. + */ + +u32 crc32_le(u32 crc, unsigned char const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + return crc; +} +#else /* Table-based approach */ + +u32 crc32_le(u32 crc, unsigned char const *p, size_t len) +{ +# if CRC_LE_BITS == 8 + const u32 *b =(u32 *)p; + const u32 *tab = crc32table_le; + +# ifdef __LITTLE_ENDIAN +# define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8) +# else +# define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8) +# endif + /* printf("Crc32_le crc=%x\n",crc); */ + crc = __cpu_to_le32(crc); + /* Align it */ + if((((long)b)&3 && len)){ + do { + u8 *p = (u8 *)b; + DO_CRC(*p++); + b = (void *)p; + } while ((--len) && ((long)b)&3 ); + } + if((len >= 4)){ + /* load data 32 bits wide, xor data 32 bits wide. */ + size_t save_len = len & 3; + len = len >> 2; + --b; /* use pre increment below(*++b) for speed */ + do { + crc ^= *++b; + DO_CRC(0); + DO_CRC(0); + DO_CRC(0); + DO_CRC(0); + } while (--len); + b++; /* point to next byte(s) */ + len = save_len; + } + /* And the last few bytes */ + if(len){ + do { + u8 *p = (u8 *)b; + DO_CRC(*p++); + b = (void *)p; + } while (--len); + } + + return __le32_to_cpu(crc); +#undef ENDIAN_SHIFT +#undef DO_CRC + +# elif CRC_LE_BITS == 4 + while (len--) { + crc ^= *p++; + crc = (crc >> 4) ^ crc32table_le[crc & 15]; + crc = (crc >> 4) ^ crc32table_le[crc & 15]; + } + return crc; +# elif CRC_LE_BITS == 2 + while (len--) { + crc ^= *p++; + crc = (crc >> 2) ^ crc32table_le[crc & 3]; + crc = (crc >> 2) ^ crc32table_le[crc & 3]; + crc = (crc >> 2) ^ crc32table_le[crc & 3]; + crc = (crc >> 2) ^ crc32table_le[crc & 3]; + } + return crc; +# endif +} +#endif +#ifdef UBI_LINUX +/** + * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 + * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for + * other uses, or the previous crc32 value if computing incrementally. + * @p: pointer to buffer over which CRC is run + * @len: length of buffer @p + */ +u32 __attribute_pure__ crc32_be(u32 crc, unsigned char const *p, size_t len); + +#if CRC_BE_BITS == 1 +/* + * In fact, the table-based code will work in this case, but it can be + * simplified by inlining the table in ?: form. + */ + +u32 __attribute_pure__ crc32_be(u32 crc, unsigned char const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++ << 24; + for (i = 0; i < 8; i++) + crc = + (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : + 0); + } + return crc; +} + +#else /* Table-based approach */ +u32 __attribute_pure__ crc32_be(u32 crc, unsigned char const *p, size_t len) +{ +# if CRC_BE_BITS == 8 + const u32 *b =(u32 *)p; + const u32 *tab = crc32table_be; + +# ifdef __LITTLE_ENDIAN +# define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8) +# else +# define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8) +# endif + + crc = __cpu_to_be32(crc); + /* Align it */ + if(unlikely(((long)b)&3 && len)){ + do { + u8 *p = (u8 *)b; + DO_CRC(*p++); + b = (u32 *)p; + } while ((--len) && ((long)b)&3 ); + } + if(likely(len >= 4)){ + /* load data 32 bits wide, xor data 32 bits wide. */ + size_t save_len = len & 3; + len = len >> 2; + --b; /* use pre increment below(*++b) for speed */ + do { + crc ^= *++b; + DO_CRC(0); + DO_CRC(0); + DO_CRC(0); + DO_CRC(0); + } while (--len); + b++; /* point to next byte(s) */ + len = save_len; + } + /* And the last few bytes */ + if(len){ + do { + u8 *p = (u8 *)b; + DO_CRC(*p++); + b = (void *)p; + } while (--len); + } + return __be32_to_cpu(crc); +#undef ENDIAN_SHIFT +#undef DO_CRC + +# elif CRC_BE_BITS == 4 + while (len--) { + crc ^= *p++ << 24; + crc = (crc << 4) ^ crc32table_be[crc >> 28]; + crc = (crc << 4) ^ crc32table_be[crc >> 28]; + } + return crc; +# elif CRC_BE_BITS == 2 + while (len--) { + crc ^= *p++ << 24; + crc = (crc << 2) ^ crc32table_be[crc >> 30]; + crc = (crc << 2) ^ crc32table_be[crc >> 30]; + crc = (crc << 2) ^ crc32table_be[crc >> 30]; + crc = (crc << 2) ^ crc32table_be[crc >> 30]; + } + return crc; +# endif +} +#endif + +EXPORT_SYMBOL(crc32_le); +EXPORT_SYMBOL(crc32_be); +#endif +/* + * A brief CRC tutorial. + * + * A CRC is a long-division remainder. You add the CRC to the message, + * and the whole thing (message+CRC) is a multiple of the given + * CRC polynomial. To check the CRC, you can either check that the + * CRC matches the recomputed value, *or* you can check that the + * remainder computed on the message+CRC is 0. This latter approach + * is used by a lot of hardware implementations, and is why so many + * protocols put the end-of-frame flag after the CRC. + * + * It's actually the same long division you learned in school, except that + * - We're working in binary, so the digits are only 0 and 1, and + * - When dividing polynomials, there are no carries. Rather than add and + * subtract, we just xor. Thus, we tend to get a bit sloppy about + * the difference between adding and subtracting. + * + * A 32-bit CRC polynomial is actually 33 bits long. But since it's + * 33 bits long, bit 32 is always going to be set, so usually the CRC + * is written in hex with the most significant bit omitted. (If you're + * familiar with the IEEE 754 floating-point format, it's the same idea.) + * + * Note that a CRC is computed over a string of *bits*, so you have + * to decide on the endianness of the bits within each byte. To get + * the best error-detecting properties, this should correspond to the + * order they're actually sent. For example, standard RS-232 serial is + * little-endian; the most significant bit (sometimes used for parity) + * is sent last. And when appending a CRC word to a message, you should + * do it in the right order, matching the endianness. + * + * Just like with ordinary division, the remainder is always smaller than + * the divisor (the CRC polynomial) you're dividing by. Each step of the + * division, you take one more digit (bit) of the dividend and append it + * to the current remainder. Then you figure out the appropriate multiple + * of the divisor to subtract to being the remainder back into range. + * In binary, it's easy - it has to be either 0 or 1, and to make the + * XOR cancel, it's just a copy of bit 32 of the remainder. + * + * When computing a CRC, we don't care about the quotient, so we can + * throw the quotient bit away, but subtract the appropriate multiple of + * the polynomial from the remainder and we're back to where we started, + * ready to process the next bit. + * + * A big-endian CRC written this way would be coded like: + * for (i = 0; i < input_bits; i++) { + * multiple = remainder & 0x80000000 ? CRCPOLY : 0; + * remainder = (remainder << 1 | next_input_bit()) ^ multiple; + * } + * Notice how, to get at bit 32 of the shifted remainder, we look + * at bit 31 of the remainder *before* shifting it. + * + * But also notice how the next_input_bit() bits we're shifting into + * the remainder don't actually affect any decision-making until + * 32 bits later. Thus, the first 32 cycles of this are pretty boring. + * Also, to add the CRC to a message, we need a 32-bit-long hole for it at + * the end, so we have to add 32 extra cycles shifting in zeros at the + * end of every message, + * + * So the standard trick is to rearrage merging in the next_input_bit() + * until the moment it's needed. Then the first 32 cycles can be precomputed, + * and merging in the final 32 zero bits to make room for the CRC can be + * skipped entirely. + * This changes the code to: + * for (i = 0; i < input_bits; i++) { + * remainder ^= next_input_bit() << 31; + * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; + * remainder = (remainder << 1) ^ multiple; + * } + * With this optimization, the little-endian code is simpler: + * for (i = 0; i < input_bits; i++) { + * remainder ^= next_input_bit(); + * multiple = (remainder & 1) ? CRCPOLY : 0; + * remainder = (remainder >> 1) ^ multiple; + * } + * + * Note that the other details of endianness have been hidden in CRCPOLY + * (which must be bit-reversed) and next_input_bit(). + * + * However, as long as next_input_bit is returning the bits in a sensible + * order, we can actually do the merging 8 or more bits at a time rather + * than one bit at a time: + * for (i = 0; i < input_bytes; i++) { + * remainder ^= next_input_byte() << 24; + * for (j = 0; j < 8; j++) { + * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; + * remainder = (remainder << 1) ^ multiple; + * } + * } + * Or in little-endian: + * for (i = 0; i < input_bytes; i++) { + * remainder ^= next_input_byte(); + * for (j = 0; j < 8; j++) { + * multiple = (remainder & 1) ? CRCPOLY : 0; + * remainder = (remainder << 1) ^ multiple; + * } + * } + * If the input is a multiple of 32 bits, you can even XOR in a 32-bit + * word at a time and increase the inner loop count to 32. + * + * You can also mix and match the two loop styles, for example doing the + * bulk of a message byte-at-a-time and adding bit-at-a-time processing + * for any fractional bytes at the end. + * + * The only remaining optimization is to the byte-at-a-time table method. + * Here, rather than just shifting one bit of the remainder to decide + * in the correct multiple to subtract, we can shift a byte at a time. + * This produces a 40-bit (rather than a 33-bit) intermediate remainder, + * but again the multiple of the polynomial to subtract depends only on + * the high bits, the high 8 bits in this case. + * + * The multile we need in that case is the low 32 bits of a 40-bit + * value whose high 8 bits are given, and which is a multiple of the + * generator polynomial. This is simply the CRC-32 of the given + * one-byte message. + * + * Two more details: normally, appending zero bits to a message which + * is already a multiple of a polynomial produces a larger multiple of that + * polynomial. To enable a CRC to detect this condition, it's common to + * invert the CRC before appending it. This makes the remainder of the + * message+crc come out not as zero, but some fixed non-zero value. + * + * The same problem applies to zero bits prepended to the message, and + * a similar solution is used. Instead of starting with a remainder of + * 0, an initial remainder of all ones is used. As long as you start + * the same way on decoding, it doesn't make a difference. + */ + +#ifdef UNITTEST + +#include +#include + +#ifdef UBI_LINUX /*Not used at present */ +static void +buf_dump(char const *prefix, unsigned char const *buf, size_t len) +{ + fputs(prefix, stdout); + while (len--) + printf(" %02x", *buf++); + putchar('\n'); + +} +#endif + +static void bytereverse(unsigned char *buf, size_t len) +{ + while (len--) { + unsigned char x = bitrev8(*buf); + *buf++ = x; + } +} + +static void random_garbage(unsigned char *buf, size_t len) +{ + while (len--) + *buf++ = (unsigned char) random(); +} + +#ifdef UBI_LINUX /* Not used at present */ +static void store_le(u32 x, unsigned char *buf) +{ + buf[0] = (unsigned char) x; + buf[1] = (unsigned char) (x >> 8); + buf[2] = (unsigned char) (x >> 16); + buf[3] = (unsigned char) (x >> 24); +} +#endif + +static void store_be(u32 x, unsigned char *buf) +{ + buf[0] = (unsigned char) (x >> 24); + buf[1] = (unsigned char) (x >> 16); + buf[2] = (unsigned char) (x >> 8); + buf[3] = (unsigned char) x; +} + +/* + * This checks that CRC(buf + CRC(buf)) = 0, and that + * CRC commutes with bit-reversal. This has the side effect + * of bytewise bit-reversing the input buffer, and returns + * the CRC of the reversed buffer. + */ +static u32 test_step(u32 init, unsigned char *buf, size_t len) +{ + u32 crc1, crc2; + size_t i; + + crc1 = crc32_be(init, buf, len); + store_be(crc1, buf + len); + crc2 = crc32_be(init, buf, len + 4); + if (crc2) + printf("\nCRC cancellation fail: 0x%08x should be 0\n", + crc2); + + for (i = 0; i <= len + 4; i++) { + crc2 = crc32_be(init, buf, i); + crc2 = crc32_be(crc2, buf + i, len + 4 - i); + if (crc2) + printf("\nCRC split fail: 0x%08x\n", crc2); + } + + /* Now swap it around for the other test */ + + bytereverse(buf, len + 4); + init = bitrev32(init); + crc2 = bitrev32(crc1); + if (crc1 != bitrev32(crc2)) + printf("\nBit reversal fail: 0x%08x -> 0x%08x -> 0x%08x\n", + crc1, crc2, bitrev32(crc2)); + crc1 = crc32_le(init, buf, len); + if (crc1 != crc2) + printf("\nCRC endianness fail: 0x%08x != 0x%08x\n", crc1, + crc2); + crc2 = crc32_le(init, buf, len + 4); + if (crc2) + printf("\nCRC cancellation fail: 0x%08x should be 0\n", + crc2); + + for (i = 0; i <= len + 4; i++) { + crc2 = crc32_le(init, buf, i); + crc2 = crc32_le(crc2, buf + i, len + 4 - i); + if (crc2) + printf("\nCRC split fail: 0x%08x\n", crc2); + } + + return crc1; +} + +#define SIZE 64 +#define INIT1 0 +#define INIT2 0 + +int main(void) +{ + unsigned char buf1[SIZE + 4]; + unsigned char buf2[SIZE + 4]; + unsigned char buf3[SIZE + 4]; + int i, j; + u32 crc1, crc2, crc3; + + for (i = 0; i <= SIZE; i++) { + printf("\rTesting length %d...", i); + fflush(stdout); + random_garbage(buf1, i); + random_garbage(buf2, i); + for (j = 0; j < i; j++) + buf3[j] = buf1[j] ^ buf2[j]; + + crc1 = test_step(INIT1, buf1, i); + crc2 = test_step(INIT2, buf2, i); + /* Now check that CRC(buf1 ^ buf2) = CRC(buf1) ^ CRC(buf2) */ + crc3 = test_step(INIT1 ^ INIT2, buf3, i); + if (crc3 != (crc1 ^ crc2)) + printf("CRC XOR fail: 0x%08x != 0x%08x ^ 0x%08x\n", + crc3, crc1, crc2); + } + printf("\nAll test complete. No failures expected.\n"); + return 0; +} + +#endif /* UNITTEST */ diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/crc32defs.h b/qemu/roms/u-boot/drivers/mtd/ubi/crc32defs.h new file mode 100644 index 000000000..f5a540176 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/crc32defs.h @@ -0,0 +1,32 @@ +/* + * There are multiple 16-bit CRC polynomials in common use, but this is + * *the* standard CRC-32 polynomial, first popularized by Ethernet. + * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 + */ +#define CRCPOLY_LE 0xedb88320 +#define CRCPOLY_BE 0x04c11db7 + +/* How many bits at a time to use. Requires a table of 4< 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1 +# error CRC_LE_BITS must be a power of 2 between 1 and 8 +#endif + +/* + * Big-endian CRC computation. Used with serial bit streams sent + * msbit-first. Be sure to use cpu_to_be32() to append the computed CRC. + */ +#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1 +# error CRC_BE_BITS must be a power of 2 between 1 and 8 +#endif diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/crc32table.h b/qemu/roms/u-boot/drivers/mtd/ubi/crc32table.h new file mode 100644 index 000000000..0438af435 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/crc32table.h @@ -0,0 +1,136 @@ +/* this file is generated - do not edit */ + +static const u32 crc32table_le[] = { +tole(0x00000000L), tole(0x77073096L), tole(0xee0e612cL), tole(0x990951baL), +tole(0x076dc419L), tole(0x706af48fL), tole(0xe963a535L), tole(0x9e6495a3L), +tole(0x0edb8832L), tole(0x79dcb8a4L), tole(0xe0d5e91eL), tole(0x97d2d988L), +tole(0x09b64c2bL), tole(0x7eb17cbdL), tole(0xe7b82d07L), tole(0x90bf1d91L), +tole(0x1db71064L), tole(0x6ab020f2L), tole(0xf3b97148L), tole(0x84be41deL), +tole(0x1adad47dL), tole(0x6ddde4ebL), tole(0xf4d4b551L), tole(0x83d385c7L), +tole(0x136c9856L), tole(0x646ba8c0L), tole(0xfd62f97aL), tole(0x8a65c9ecL), +tole(0x14015c4fL), tole(0x63066cd9L), tole(0xfa0f3d63L), tole(0x8d080df5L), +tole(0x3b6e20c8L), tole(0x4c69105eL), tole(0xd56041e4L), tole(0xa2677172L), +tole(0x3c03e4d1L), tole(0x4b04d447L), tole(0xd20d85fdL), tole(0xa50ab56bL), +tole(0x35b5a8faL), tole(0x42b2986cL), tole(0xdbbbc9d6L), tole(0xacbcf940L), +tole(0x32d86ce3L), tole(0x45df5c75L), tole(0xdcd60dcfL), tole(0xabd13d59L), +tole(0x26d930acL), tole(0x51de003aL), tole(0xc8d75180L), tole(0xbfd06116L), +tole(0x21b4f4b5L), tole(0x56b3c423L), tole(0xcfba9599L), tole(0xb8bda50fL), +tole(0x2802b89eL), tole(0x5f058808L), tole(0xc60cd9b2L), tole(0xb10be924L), +tole(0x2f6f7c87L), tole(0x58684c11L), tole(0xc1611dabL), tole(0xb6662d3dL), +tole(0x76dc4190L), tole(0x01db7106L), tole(0x98d220bcL), tole(0xefd5102aL), +tole(0x71b18589L), tole(0x06b6b51fL), tole(0x9fbfe4a5L), tole(0xe8b8d433L), +tole(0x7807c9a2L), tole(0x0f00f934L), tole(0x9609a88eL), tole(0xe10e9818L), +tole(0x7f6a0dbbL), tole(0x086d3d2dL), tole(0x91646c97L), tole(0xe6635c01L), +tole(0x6b6b51f4L), tole(0x1c6c6162L), tole(0x856530d8L), tole(0xf262004eL), +tole(0x6c0695edL), tole(0x1b01a57bL), tole(0x8208f4c1L), tole(0xf50fc457L), +tole(0x65b0d9c6L), tole(0x12b7e950L), tole(0x8bbeb8eaL), tole(0xfcb9887cL), +tole(0x62dd1ddfL), tole(0x15da2d49L), tole(0x8cd37cf3L), tole(0xfbd44c65L), +tole(0x4db26158L), tole(0x3ab551ceL), tole(0xa3bc0074L), tole(0xd4bb30e2L), +tole(0x4adfa541L), tole(0x3dd895d7L), tole(0xa4d1c46dL), tole(0xd3d6f4fbL), +tole(0x4369e96aL), tole(0x346ed9fcL), tole(0xad678846L), tole(0xda60b8d0L), +tole(0x44042d73L), tole(0x33031de5L), tole(0xaa0a4c5fL), tole(0xdd0d7cc9L), +tole(0x5005713cL), tole(0x270241aaL), tole(0xbe0b1010L), tole(0xc90c2086L), +tole(0x5768b525L), tole(0x206f85b3L), tole(0xb966d409L), tole(0xce61e49fL), +tole(0x5edef90eL), tole(0x29d9c998L), tole(0xb0d09822L), tole(0xc7d7a8b4L), +tole(0x59b33d17L), tole(0x2eb40d81L), tole(0xb7bd5c3bL), tole(0xc0ba6cadL), +tole(0xedb88320L), tole(0x9abfb3b6L), tole(0x03b6e20cL), tole(0x74b1d29aL), +tole(0xead54739L), tole(0x9dd277afL), tole(0x04db2615L), tole(0x73dc1683L), +tole(0xe3630b12L), tole(0x94643b84L), tole(0x0d6d6a3eL), tole(0x7a6a5aa8L), +tole(0xe40ecf0bL), tole(0x9309ff9dL), tole(0x0a00ae27L), tole(0x7d079eb1L), +tole(0xf00f9344L), tole(0x8708a3d2L), tole(0x1e01f268L), tole(0x6906c2feL), +tole(0xf762575dL), tole(0x806567cbL), tole(0x196c3671L), tole(0x6e6b06e7L), +tole(0xfed41b76L), tole(0x89d32be0L), tole(0x10da7a5aL), tole(0x67dd4accL), +tole(0xf9b9df6fL), tole(0x8ebeeff9L), tole(0x17b7be43L), tole(0x60b08ed5L), +tole(0xd6d6a3e8L), tole(0xa1d1937eL), tole(0x38d8c2c4L), tole(0x4fdff252L), +tole(0xd1bb67f1L), tole(0xa6bc5767L), tole(0x3fb506ddL), tole(0x48b2364bL), +tole(0xd80d2bdaL), tole(0xaf0a1b4cL), tole(0x36034af6L), tole(0x41047a60L), +tole(0xdf60efc3L), tole(0xa867df55L), tole(0x316e8eefL), tole(0x4669be79L), +tole(0xcb61b38cL), tole(0xbc66831aL), tole(0x256fd2a0L), tole(0x5268e236L), +tole(0xcc0c7795L), tole(0xbb0b4703L), tole(0x220216b9L), tole(0x5505262fL), +tole(0xc5ba3bbeL), tole(0xb2bd0b28L), tole(0x2bb45a92L), tole(0x5cb36a04L), +tole(0xc2d7ffa7L), tole(0xb5d0cf31L), tole(0x2cd99e8bL), tole(0x5bdeae1dL), +tole(0x9b64c2b0L), tole(0xec63f226L), tole(0x756aa39cL), tole(0x026d930aL), +tole(0x9c0906a9L), tole(0xeb0e363fL), tole(0x72076785L), tole(0x05005713L), +tole(0x95bf4a82L), tole(0xe2b87a14L), tole(0x7bb12baeL), tole(0x0cb61b38L), +tole(0x92d28e9bL), tole(0xe5d5be0dL), tole(0x7cdcefb7L), tole(0x0bdbdf21L), +tole(0x86d3d2d4L), tole(0xf1d4e242L), tole(0x68ddb3f8L), tole(0x1fda836eL), +tole(0x81be16cdL), tole(0xf6b9265bL), tole(0x6fb077e1L), tole(0x18b74777L), +tole(0x88085ae6L), tole(0xff0f6a70L), tole(0x66063bcaL), tole(0x11010b5cL), +tole(0x8f659effL), tole(0xf862ae69L), tole(0x616bffd3L), tole(0x166ccf45L), +tole(0xa00ae278L), tole(0xd70dd2eeL), tole(0x4e048354L), tole(0x3903b3c2L), +tole(0xa7672661L), tole(0xd06016f7L), tole(0x4969474dL), tole(0x3e6e77dbL), +tole(0xaed16a4aL), tole(0xd9d65adcL), tole(0x40df0b66L), tole(0x37d83bf0L), +tole(0xa9bcae53L), tole(0xdebb9ec5L), tole(0x47b2cf7fL), tole(0x30b5ffe9L), +tole(0xbdbdf21cL), tole(0xcabac28aL), tole(0x53b39330L), tole(0x24b4a3a6L), +tole(0xbad03605L), tole(0xcdd70693L), tole(0x54de5729L), tole(0x23d967bfL), +tole(0xb3667a2eL), tole(0xc4614ab8L), tole(0x5d681b02L), tole(0x2a6f2b94L), +tole(0xb40bbe37L), tole(0xc30c8ea1L), tole(0x5a05df1bL), tole(0x2d02ef8dL) +}; +#ifdef UBI_LINUX +static const u32 crc32table_be[] = { +tobe(0x00000000L), tobe(0x04c11db7L), tobe(0x09823b6eL), tobe(0x0d4326d9L), +tobe(0x130476dcL), tobe(0x17c56b6bL), tobe(0x1a864db2L), tobe(0x1e475005L), +tobe(0x2608edb8L), tobe(0x22c9f00fL), tobe(0x2f8ad6d6L), tobe(0x2b4bcb61L), +tobe(0x350c9b64L), tobe(0x31cd86d3L), tobe(0x3c8ea00aL), tobe(0x384fbdbdL), +tobe(0x4c11db70L), tobe(0x48d0c6c7L), tobe(0x4593e01eL), tobe(0x4152fda9L), +tobe(0x5f15adacL), tobe(0x5bd4b01bL), tobe(0x569796c2L), tobe(0x52568b75L), +tobe(0x6a1936c8L), tobe(0x6ed82b7fL), tobe(0x639b0da6L), tobe(0x675a1011L), +tobe(0x791d4014L), tobe(0x7ddc5da3L), tobe(0x709f7b7aL), tobe(0x745e66cdL), +tobe(0x9823b6e0L), tobe(0x9ce2ab57L), tobe(0x91a18d8eL), tobe(0x95609039L), +tobe(0x8b27c03cL), tobe(0x8fe6dd8bL), tobe(0x82a5fb52L), tobe(0x8664e6e5L), +tobe(0xbe2b5b58L), tobe(0xbaea46efL), tobe(0xb7a96036L), tobe(0xb3687d81L), +tobe(0xad2f2d84L), tobe(0xa9ee3033L), tobe(0xa4ad16eaL), tobe(0xa06c0b5dL), +tobe(0xd4326d90L), tobe(0xd0f37027L), tobe(0xddb056feL), tobe(0xd9714b49L), +tobe(0xc7361b4cL), tobe(0xc3f706fbL), tobe(0xceb42022L), tobe(0xca753d95L), +tobe(0xf23a8028L), tobe(0xf6fb9d9fL), tobe(0xfbb8bb46L), tobe(0xff79a6f1L), +tobe(0xe13ef6f4L), tobe(0xe5ffeb43L), tobe(0xe8bccd9aL), tobe(0xec7dd02dL), +tobe(0x34867077L), tobe(0x30476dc0L), tobe(0x3d044b19L), tobe(0x39c556aeL), +tobe(0x278206abL), tobe(0x23431b1cL), tobe(0x2e003dc5L), tobe(0x2ac12072L), +tobe(0x128e9dcfL), tobe(0x164f8078L), tobe(0x1b0ca6a1L), tobe(0x1fcdbb16L), +tobe(0x018aeb13L), tobe(0x054bf6a4L), tobe(0x0808d07dL), tobe(0x0cc9cdcaL), +tobe(0x7897ab07L), tobe(0x7c56b6b0L), tobe(0x71159069L), tobe(0x75d48ddeL), +tobe(0x6b93dddbL), tobe(0x6f52c06cL), tobe(0x6211e6b5L), tobe(0x66d0fb02L), +tobe(0x5e9f46bfL), tobe(0x5a5e5b08L), tobe(0x571d7dd1L), tobe(0x53dc6066L), +tobe(0x4d9b3063L), tobe(0x495a2dd4L), tobe(0x44190b0dL), tobe(0x40d816baL), +tobe(0xaca5c697L), tobe(0xa864db20L), tobe(0xa527fdf9L), tobe(0xa1e6e04eL), +tobe(0xbfa1b04bL), tobe(0xbb60adfcL), tobe(0xb6238b25L), tobe(0xb2e29692L), +tobe(0x8aad2b2fL), tobe(0x8e6c3698L), tobe(0x832f1041L), tobe(0x87ee0df6L), +tobe(0x99a95df3L), tobe(0x9d684044L), tobe(0x902b669dL), tobe(0x94ea7b2aL), +tobe(0xe0b41de7L), tobe(0xe4750050L), tobe(0xe9362689L), tobe(0xedf73b3eL), +tobe(0xf3b06b3bL), tobe(0xf771768cL), tobe(0xfa325055L), tobe(0xfef34de2L), +tobe(0xc6bcf05fL), tobe(0xc27dede8L), tobe(0xcf3ecb31L), tobe(0xcbffd686L), +tobe(0xd5b88683L), tobe(0xd1799b34L), tobe(0xdc3abdedL), tobe(0xd8fba05aL), +tobe(0x690ce0eeL), tobe(0x6dcdfd59L), tobe(0x608edb80L), tobe(0x644fc637L), +tobe(0x7a089632L), tobe(0x7ec98b85L), tobe(0x738aad5cL), tobe(0x774bb0ebL), +tobe(0x4f040d56L), tobe(0x4bc510e1L), tobe(0x46863638L), tobe(0x42472b8fL), +tobe(0x5c007b8aL), tobe(0x58c1663dL), tobe(0x558240e4L), tobe(0x51435d53L), +tobe(0x251d3b9eL), tobe(0x21dc2629L), tobe(0x2c9f00f0L), tobe(0x285e1d47L), +tobe(0x36194d42L), tobe(0x32d850f5L), tobe(0x3f9b762cL), tobe(0x3b5a6b9bL), +tobe(0x0315d626L), tobe(0x07d4cb91L), tobe(0x0a97ed48L), tobe(0x0e56f0ffL), +tobe(0x1011a0faL), tobe(0x14d0bd4dL), tobe(0x19939b94L), tobe(0x1d528623L), +tobe(0xf12f560eL), tobe(0xf5ee4bb9L), tobe(0xf8ad6d60L), tobe(0xfc6c70d7L), +tobe(0xe22b20d2L), tobe(0xe6ea3d65L), tobe(0xeba91bbcL), tobe(0xef68060bL), +tobe(0xd727bbb6L), tobe(0xd3e6a601L), tobe(0xdea580d8L), tobe(0xda649d6fL), +tobe(0xc423cd6aL), tobe(0xc0e2d0ddL), tobe(0xcda1f604L), tobe(0xc960ebb3L), +tobe(0xbd3e8d7eL), tobe(0xb9ff90c9L), tobe(0xb4bcb610L), tobe(0xb07daba7L), +tobe(0xae3afba2L), tobe(0xaafbe615L), tobe(0xa7b8c0ccL), tobe(0xa379dd7bL), +tobe(0x9b3660c6L), tobe(0x9ff77d71L), tobe(0x92b45ba8L), tobe(0x9675461fL), +tobe(0x8832161aL), tobe(0x8cf30badL), tobe(0x81b02d74L), tobe(0x857130c3L), +tobe(0x5d8a9099L), tobe(0x594b8d2eL), tobe(0x5408abf7L), tobe(0x50c9b640L), +tobe(0x4e8ee645L), tobe(0x4a4ffbf2L), tobe(0x470cdd2bL), tobe(0x43cdc09cL), +tobe(0x7b827d21L), tobe(0x7f436096L), tobe(0x7200464fL), tobe(0x76c15bf8L), +tobe(0x68860bfdL), tobe(0x6c47164aL), tobe(0x61043093L), tobe(0x65c52d24L), +tobe(0x119b4be9L), tobe(0x155a565eL), tobe(0x18197087L), tobe(0x1cd86d30L), +tobe(0x029f3d35L), tobe(0x065e2082L), tobe(0x0b1d065bL), tobe(0x0fdc1becL), +tobe(0x3793a651L), tobe(0x3352bbe6L), tobe(0x3e119d3fL), tobe(0x3ad08088L), +tobe(0x2497d08dL), tobe(0x2056cd3aL), tobe(0x2d15ebe3L), tobe(0x29d4f654L), +tobe(0xc5a92679L), tobe(0xc1683bceL), tobe(0xcc2b1d17L), tobe(0xc8ea00a0L), +tobe(0xd6ad50a5L), tobe(0xd26c4d12L), tobe(0xdf2f6bcbL), tobe(0xdbee767cL), +tobe(0xe3a1cbc1L), tobe(0xe760d676L), tobe(0xea23f0afL), tobe(0xeee2ed18L), +tobe(0xf0a5bd1dL), tobe(0xf464a0aaL), tobe(0xf9278673L), tobe(0xfde69bc4L), +tobe(0x89b8fd09L), tobe(0x8d79e0beL), tobe(0x803ac667L), tobe(0x84fbdbd0L), +tobe(0x9abc8bd5L), tobe(0x9e7d9662L), tobe(0x933eb0bbL), tobe(0x97ffad0cL), +tobe(0xafb010b1L), tobe(0xab710d06L), tobe(0xa6322bdfL), tobe(0xa2f33668L), +tobe(0xbcb4666dL), tobe(0xb8757bdaL), tobe(0xb5365d03L), tobe(0xb1f740b4L) +}; +#endif diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/debug.c b/qemu/roms/u-boot/drivers/mtd/ubi/debug.c new file mode 100644 index 000000000..6c22301d9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/debug.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * Here we keep all the UBI debugging stuff which should normally be disabled + * and compiled-out, but it is extremely helpful when hunting bugs or doing big + * changes. + */ +#include + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG + +#include "ubi.h" + +/** + * ubi_dbg_dump_ec_hdr - dump an erase counter header. + * @ec_hdr: the erase counter header to dump + */ +void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) +{ + dbg_msg("erase counter header dump:"); + dbg_msg("magic %#08x", be32_to_cpu(ec_hdr->magic)); + dbg_msg("version %d", (int)ec_hdr->version); + dbg_msg("ec %llu", (long long)be64_to_cpu(ec_hdr->ec)); + dbg_msg("vid_hdr_offset %d", be32_to_cpu(ec_hdr->vid_hdr_offset)); + dbg_msg("data_offset %d", be32_to_cpu(ec_hdr->data_offset)); + dbg_msg("hdr_crc %#08x", be32_to_cpu(ec_hdr->hdr_crc)); + dbg_msg("erase counter header hexdump:"); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + ec_hdr, UBI_EC_HDR_SIZE, 1); +} + +/** + * ubi_dbg_dump_vid_hdr - dump a volume identifier header. + * @vid_hdr: the volume identifier header to dump + */ +void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) +{ + dbg_msg("volume identifier header dump:"); + dbg_msg("magic %08x", be32_to_cpu(vid_hdr->magic)); + dbg_msg("version %d", (int)vid_hdr->version); + dbg_msg("vol_type %d", (int)vid_hdr->vol_type); + dbg_msg("copy_flag %d", (int)vid_hdr->copy_flag); + dbg_msg("compat %d", (int)vid_hdr->compat); + dbg_msg("vol_id %d", be32_to_cpu(vid_hdr->vol_id)); + dbg_msg("lnum %d", be32_to_cpu(vid_hdr->lnum)); + dbg_msg("leb_ver %u", be32_to_cpu(vid_hdr->leb_ver)); + dbg_msg("data_size %d", be32_to_cpu(vid_hdr->data_size)); + dbg_msg("used_ebs %d", be32_to_cpu(vid_hdr->used_ebs)); + dbg_msg("data_pad %d", be32_to_cpu(vid_hdr->data_pad)); + dbg_msg("sqnum %llu", + (unsigned long long)be64_to_cpu(vid_hdr->sqnum)); + dbg_msg("hdr_crc %08x", be32_to_cpu(vid_hdr->hdr_crc)); + dbg_msg("volume identifier header hexdump:"); +} + +/** + * ubi_dbg_dump_vol_info- dump volume information. + * @vol: UBI volume description object + */ +void ubi_dbg_dump_vol_info(const struct ubi_volume *vol) +{ + dbg_msg("volume information dump:"); + dbg_msg("vol_id %d", vol->vol_id); + dbg_msg("reserved_pebs %d", vol->reserved_pebs); + dbg_msg("alignment %d", vol->alignment); + dbg_msg("data_pad %d", vol->data_pad); + dbg_msg("vol_type %d", vol->vol_type); + dbg_msg("name_len %d", vol->name_len); + dbg_msg("usable_leb_size %d", vol->usable_leb_size); + dbg_msg("used_ebs %d", vol->used_ebs); + dbg_msg("used_bytes %lld", vol->used_bytes); + dbg_msg("last_eb_bytes %d", vol->last_eb_bytes); + dbg_msg("corrupted %d", vol->corrupted); + dbg_msg("upd_marker %d", vol->upd_marker); + + if (vol->name_len <= UBI_VOL_NAME_MAX && + strnlen(vol->name, vol->name_len + 1) == vol->name_len) { + dbg_msg("name %s", vol->name); + } else { + dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c", + vol->name[0], vol->name[1], vol->name[2], + vol->name[3], vol->name[4]); + } +} + +/** + * ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object. + * @r: the object to dump + * @idx: volume table index + */ +void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) +{ + int name_len = be16_to_cpu(r->name_len); + + dbg_msg("volume table record %d dump:", idx); + dbg_msg("reserved_pebs %d", be32_to_cpu(r->reserved_pebs)); + dbg_msg("alignment %d", be32_to_cpu(r->alignment)); + dbg_msg("data_pad %d", be32_to_cpu(r->data_pad)); + dbg_msg("vol_type %d", (int)r->vol_type); + dbg_msg("upd_marker %d", (int)r->upd_marker); + dbg_msg("name_len %d", name_len); + + if (r->name[0] == '\0') { + dbg_msg("name NULL"); + return; + } + + if (name_len <= UBI_VOL_NAME_MAX && + strnlen(&r->name[0], name_len + 1) == name_len) { + dbg_msg("name %s", &r->name[0]); + } else { + dbg_msg("1st 5 characters of the name: %c%c%c%c%c", + r->name[0], r->name[1], r->name[2], r->name[3], + r->name[4]); + } + dbg_msg("crc %#08x", be32_to_cpu(r->crc)); +} + +/** + * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object. + * @sv: the object to dump + */ +void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv) +{ + dbg_msg("volume scanning information dump:"); + dbg_msg("vol_id %d", sv->vol_id); + dbg_msg("highest_lnum %d", sv->highest_lnum); + dbg_msg("leb_count %d", sv->leb_count); + dbg_msg("compat %d", sv->compat); + dbg_msg("vol_type %d", sv->vol_type); + dbg_msg("used_ebs %d", sv->used_ebs); + dbg_msg("last_data_size %d", sv->last_data_size); + dbg_msg("data_pad %d", sv->data_pad); +} + +/** + * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object. + * @seb: the object to dump + * @type: object type: 0 - not corrupted, 1 - corrupted + */ +void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type) +{ + dbg_msg("eraseblock scanning information dump:"); + dbg_msg("ec %d", seb->ec); + dbg_msg("pnum %d", seb->pnum); + if (type == 0) { + dbg_msg("lnum %d", seb->lnum); + dbg_msg("scrub %d", seb->scrub); + dbg_msg("sqnum %llu", seb->sqnum); + dbg_msg("leb_ver %u", seb->leb_ver); + } +} + +/** + * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object. + * @req: the object to dump + */ +void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req) +{ + char nm[17]; + + dbg_msg("volume creation request dump:"); + dbg_msg("vol_id %d", req->vol_id); + dbg_msg("alignment %d", req->alignment); + dbg_msg("bytes %lld", (long long)req->bytes); + dbg_msg("vol_type %d", req->vol_type); + dbg_msg("name_len %d", req->name_len); + + memcpy(nm, req->name, 16); + nm[16] = 0; + dbg_msg("the 1st 16 characters of the name: %s", nm); +} + +#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/debug.h b/qemu/roms/u-boot/drivers/mtd/ubi/debug.h new file mode 100644 index 000000000..222b2b8ae --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/debug.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_DEBUG_H__ +#define __UBI_DEBUG_H__ + +#ifdef CONFIG_MTD_UBI_DEBUG +#ifdef UBI_LINUX +#include +#endif + +#define ubi_assert(expr) BUG_ON(!(expr)) +#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__) +#else +#define ubi_assert(expr) ({}) +#define dbg_err(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT +#define DBG_DISABLE_BGT 1 +#else +#define DBG_DISABLE_BGT 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG +/* Generic debugging message */ +#define dbg_msg(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", \ + __FUNCTION__, ##__VA_ARGS__) + +#define ubi_dbg_dump_stack() dump_stack() + +struct ubi_ec_hdr; +struct ubi_vid_hdr; +struct ubi_volume; +struct ubi_vtbl_record; +struct ubi_scan_volume; +struct ubi_scan_leb; +struct ubi_mkvol_req; + +void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr); +void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr); +void ubi_dbg_dump_vol_info(const struct ubi_volume *vol); +void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx); +void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv); +void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type); +void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); + +#else + +#define dbg_msg(fmt, ...) ({}) +#define ubi_dbg_dump_stack() ({}) +#define ubi_dbg_dump_ec_hdr(ec_hdr) ({}) +#define ubi_dbg_dump_vid_hdr(vid_hdr) ({}) +#define ubi_dbg_dump_vol_info(vol) ({}) +#define ubi_dbg_dump_vtbl_record(r, idx) ({}) +#define ubi_dbg_dump_sv(sv) ({}) +#define ubi_dbg_dump_seb(seb, type) ({}) +#define ubi_dbg_dump_mkvol_req(req) ({}) + +#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA +/* Messages from the eraseblock association unit */ +#define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) +#else +#define dbg_eba(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL +/* Messages from the wear-leveling unit */ +#define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) +#else +#define dbg_wl(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO +/* Messages from the input/output unit */ +#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) +#else +#define dbg_io(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD +/* Initialization and build messages */ +#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) +#else +#define dbg_bld(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS +/** + * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip. + * + * Returns non-zero if a bit-flip should be emulated, otherwise returns zero. + */ +static inline int ubi_dbg_is_bitflip(void) +{ + return !(random32() % 200); +} +#else +#define ubi_dbg_is_bitflip() 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES +/** + * ubi_dbg_is_write_failure - if it is time to emulate a write failure. + * + * Returns non-zero if a write failure should be emulated, otherwise returns + * zero. + */ +static inline int ubi_dbg_is_write_failure(void) +{ + return !(random32() % 500); +} +#else +#define ubi_dbg_is_write_failure() 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES +/** + * ubi_dbg_is_erase_failure - if its time to emulate an erase failure. + * + * Returns non-zero if an erase failure should be emulated, otherwise returns + * zero. + */ +static inline int ubi_dbg_is_erase_failure(void) +{ + return !(random32() % 400); +} +#else +#define ubi_dbg_is_erase_failure() 0 +#endif + +#endif /* !__UBI_DEBUG_H__ */ diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/eba.c b/qemu/roms/u-boot/drivers/mtd/ubi/eba.c new file mode 100644 index 000000000..7d27edaee --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/eba.c @@ -0,0 +1,1244 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * The UBI Eraseblock Association (EBA) unit. + * + * This unit is responsible for I/O to/from logical eraseblock. + * + * Although in this implementation the EBA table is fully kept and managed in + * RAM, which assumes poor scalability, it might be (partially) maintained on + * flash in future implementations. + * + * The EBA unit implements per-logical eraseblock locking. Before accessing a + * logical eraseblock it is locked for reading or writing. The per-logical + * eraseblock locking is implemented by means of the lock tree. The lock tree + * is an RB-tree which refers all the currently locked logical eraseblocks. The + * lock tree elements are &struct ubi_ltree_entry objects. They are indexed by + * (@vol_id, @lnum) pairs. + * + * EBA also maintains the global sequence counter which is incremented each + * time a logical eraseblock is mapped to a physical eraseblock and it is + * stored in the volume identifier header. This means that each VID header has + * a unique sequence number. The sequence number is only increased an we assume + * 64 bits is enough to never overflow. + */ + +#ifdef UBI_LINUX +#include +#include +#include +#endif + +#include +#include "ubi.h" + +/* Number of physical eraseblocks reserved for atomic LEB change operation */ +#define EBA_RESERVED_PEBS 1 + +/** + * next_sqnum - get next sequence number. + * @ubi: UBI device description object + * + * This function returns next sequence number to use, which is just the current + * global sequence counter value. It also increases the global sequence + * counter. + */ +static unsigned long long next_sqnum(struct ubi_device *ubi) +{ + unsigned long long sqnum; + + spin_lock(&ubi->ltree_lock); + sqnum = ubi->global_sqnum++; + spin_unlock(&ubi->ltree_lock); + + return sqnum; +} + +/** + * ubi_get_compat - get compatibility flags of a volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function returns compatibility flags for an internal volume. User + * volumes have no compatibility flags, so %0 is returned. + */ +static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) +{ + if (vol_id == UBI_LAYOUT_VOLUME_ID) + return UBI_LAYOUT_VOLUME_COMPAT; + return 0; +} + +/** + * ltree_lookup - look up the lock tree. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function returns a pointer to the corresponding &struct ubi_ltree_entry + * object if the logical eraseblock is locked and %NULL if it is not. + * @ubi->ltree_lock has to be locked. + */ +static struct ubi_ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, + int lnum) +{ + struct rb_node *p; + + p = ubi->ltree.rb_node; + while (p) { + struct ubi_ltree_entry *le; + + le = rb_entry(p, struct ubi_ltree_entry, rb); + + if (vol_id < le->vol_id) + p = p->rb_left; + else if (vol_id > le->vol_id) + p = p->rb_right; + else { + if (lnum < le->lnum) + p = p->rb_left; + else if (lnum > le->lnum) + p = p->rb_right; + else + return le; + } + } + + return NULL; +} + +/** + * ltree_add_entry - add new entry to the lock tree. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function adds new entry for logical eraseblock (@vol_id, @lnum) to the + * lock tree. If such entry is already there, its usage counter is increased. + * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation + * failed. + */ +static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi, + int vol_id, int lnum) +{ + struct ubi_ltree_entry *le, *le1, *le_free; + + le = kmalloc(sizeof(struct ubi_ltree_entry), GFP_NOFS); + if (!le) + return ERR_PTR(-ENOMEM); + + le->users = 0; + init_rwsem(&le->mutex); + le->vol_id = vol_id; + le->lnum = lnum; + + spin_lock(&ubi->ltree_lock); + le1 = ltree_lookup(ubi, vol_id, lnum); + + if (le1) { + /* + * This logical eraseblock is already locked. The newly + * allocated lock entry is not needed. + */ + le_free = le; + le = le1; + } else { + struct rb_node **p, *parent = NULL; + + /* + * No lock entry, add the newly allocated one to the + * @ubi->ltree RB-tree. + */ + le_free = NULL; + + p = &ubi->ltree.rb_node; + while (*p) { + parent = *p; + le1 = rb_entry(parent, struct ubi_ltree_entry, rb); + + if (vol_id < le1->vol_id) + p = &(*p)->rb_left; + else if (vol_id > le1->vol_id) + p = &(*p)->rb_right; + else { + ubi_assert(lnum != le1->lnum); + if (lnum < le1->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&le->rb, parent, p); + rb_insert_color(&le->rb, &ubi->ltree); + } + le->users += 1; + spin_unlock(&ubi->ltree_lock); + + if (le_free) + kfree(le_free); + + return le; +} + +/** + * leb_read_lock - lock logical eraseblock for reading. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for reading. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) +{ + struct ubi_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + down_read(&le->mutex); + return 0; +} + +/** + * leb_read_unlock - unlock logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + */ +static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) +{ + int _free = 0; + struct ubi_ltree_entry *le; + + spin_lock(&ubi->ltree_lock); + le = ltree_lookup(ubi, vol_id, lnum); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + _free = 1; + } + spin_unlock(&ubi->ltree_lock); + + up_read(&le->mutex); + if (_free) + kfree(le); +} + +/** + * leb_write_lock - lock logical eraseblock for writing. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for writing. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) +{ + struct ubi_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + down_write(&le->mutex); + return 0; +} + +/** + * leb_write_lock - lock logical eraseblock for writing. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for writing if there is no + * contention and does nothing if there is contention. Returns %0 in case of + * success, %1 in case of contention, and and a negative error code in case of + * failure. + */ +static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) +{ + int _free; + struct ubi_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + if (down_write_trylock(&le->mutex)) + return 0; + + /* Contention, cancel */ + spin_lock(&ubi->ltree_lock); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + _free = 1; + } else + _free = 0; + spin_unlock(&ubi->ltree_lock); + if (_free) + kfree(le); + + return 1; +} + +/** + * leb_write_unlock - unlock logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + */ +static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) +{ + int _free; + struct ubi_ltree_entry *le; + + spin_lock(&ubi->ltree_lock); + le = ltree_lookup(ubi, vol_id, lnum); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + _free = 1; + } else + _free = 0; + spin_unlock(&ubi->ltree_lock); + + up_write(&le->mutex); + if (_free) + kfree(le); +} + +/** + * ubi_eba_unmap_leb - un-map logical eraseblock. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and schedules corresponding + * physical eraseblock for erasure. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum) +{ + int err, pnum, vol_id = vol->vol_id; + + if (ubi->ro_mode) + return -EROFS; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum < 0) + /* This logical eraseblock is already unmapped */ + goto out_unlock; + + dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum); + + vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED; + err = ubi_wl_put_peb(ubi, pnum, 0); + +out_unlock: + leb_write_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * ubi_eba_read_leb - read data. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: buffer to store the read data + * @offset: offset from where to read + * @len: how many bytes to read + * @check: data CRC check flag + * + * If the logical eraseblock @lnum is unmapped, @buf is filled with 0xFF + * bytes. The @check flag only makes sense for static volumes and forces + * eraseblock data CRC checking. + * + * In case of success this function returns zero. In case of a static volume, + * if data CRC mismatches - %-EBADMSG is returned. %-EBADMSG may also be + * returned for any volume type if an ECC error was detected by the MTD device + * driver. Other negative error cored may be returned in case of other errors. + */ +int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int offset, int len, int check) +{ + int err, pnum, scrub = 0, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + uint32_t uninitialized_var(crc); + + err = leb_read_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum < 0) { + /* + * The logical eraseblock is not mapped, fill the whole buffer + * with 0xFF bytes. The exception is static volumes for which + * it is an error to read unmapped logical eraseblocks. + */ + dbg_eba("read %d bytes from offset %d of LEB %d:%d (unmapped)", + len, offset, vol_id, lnum); + leb_read_unlock(ubi, vol_id, lnum); + ubi_assert(vol->vol_type != UBI_STATIC_VOLUME); + memset(buf, 0xFF, len); + return 0; + } + + dbg_eba("read %d bytes from offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + check = 0; + +retry: + if (check) { + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) { + err = -ENOMEM; + goto out_unlock; + } + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (err && err != UBI_IO_BITFLIPS) { + if (err > 0) { + /* + * The header is either absent or corrupted. + * The former case means there is a bug - + * switch to read-only mode just in case. + * The latter case means a real corruption - we + * may try to recover data. FIXME: but this is + * not implemented. + */ + if (err == UBI_IO_BAD_VID_HDR) { + ubi_warn("bad VID header at PEB %d, LEB" + "%d:%d", pnum, vol_id, lnum); + err = -EBADMSG; + } else + ubi_ro_mode(ubi); + } + goto out_free; + } else if (err == UBI_IO_BITFLIPS) + scrub = 1; + + ubi_assert(lnum < be32_to_cpu(vid_hdr->used_ebs)); + ubi_assert(len == be32_to_cpu(vid_hdr->data_size)); + + crc = be32_to_cpu(vid_hdr->data_crc); + ubi_free_vid_hdr(ubi, vid_hdr); + } + + err = ubi_io_read_data(ubi, buf, pnum, offset, len); + if (err) { + if (err == UBI_IO_BITFLIPS) { + scrub = 1; + err = 0; + } else if (mtd_is_eccerr(err)) { + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + goto out_unlock; + scrub = 1; + if (!check) { + ubi_msg("force data checking"); + check = 1; + goto retry; + } + } else + goto out_unlock; + } + + if (check) { + uint32_t crc1 = crc32(UBI_CRC32_INIT, buf, len); + if (crc1 != crc) { + ubi_warn("CRC error: calculated %#08x, must be %#08x", + crc1, crc); + err = -EBADMSG; + goto out_unlock; + } + } + + if (scrub) + err = ubi_wl_scrub_peb(ubi, pnum); + + leb_read_unlock(ubi, vol_id, lnum); + return err; + +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); +out_unlock: + leb_read_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * recover_peb - recover from write failure. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to recover + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data which was not written because of the write failure + * @offset: offset of the failed write + * @len: how many bytes should have been written + * + * This function is called in case of a write failure and moves all good data + * from the potentially bad physical eraseblock to a good physical eraseblock. + * This function also writes the data which was not written due to the failure. + * Returns new physical eraseblock number in case of success, and a negative + * error code in case of failure. + */ +static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, + const void *buf, int offset, int len) +{ + int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0; + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) { + return -ENOMEM; + } + + mutex_lock(&ubi->buf_mutex); + +retry: + new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN); + if (new_pnum < 0) { + mutex_unlock(&ubi->buf_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return new_pnum; + } + + ubi_msg("recover PEB %d, move data to PEB %d", pnum, new_pnum); + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (err && err != UBI_IO_BITFLIPS) { + if (err > 0) + err = -EIO; + goto out_put; + } + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); + if (err) + goto write_error; + + data_size = offset + len; + memset(ubi->peb_buf1 + offset, 0xFF, len); + + /* Read everything before the area where the write failure happened */ + if (offset > 0) { + err = ubi_io_read_data(ubi, ubi->peb_buf1, pnum, 0, offset); + if (err && err != UBI_IO_BITFLIPS) + goto out_put; + } + + memcpy(ubi->peb_buf1 + offset, buf, len); + + err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size); + if (err) + goto write_error; + + mutex_unlock(&ubi->buf_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + + vol->eba_tbl[lnum] = new_pnum; + ubi_wl_put_peb(ubi, pnum, 1); + + ubi_msg("data was successfully recovered"); + return 0; + +out_put: + mutex_unlock(&ubi->buf_mutex); + ubi_wl_put_peb(ubi, new_pnum, 1); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + /* + * Bad luck? This physical eraseblock is bad too? Crud. Let's try to + * get another one. + */ + ubi_warn("failed to write to PEB %d", new_pnum); + ubi_wl_put_peb(ubi, new_pnum, 1); + if (++tries > UBI_IO_RETRIES) { + mutex_unlock(&ubi->buf_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + ubi_msg("try again"); + goto retry; +} + +/** + * ubi_eba_write_leb - write data to dynamic volume. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: the data to write + * @offset: offset within the logical eraseblock where to write + * @len: how many bytes to write + * @dtype: data type + * + * This function writes data to logical eraseblock @lnum of a dynamic volume + * @vol. Returns zero in case of success and a negative error code in case + * of failure. In case of error, it is possible that something was still + * written to the flash media, but may be some garbage. + */ +int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + const void *buf, int offset, int len, int dtype) +{ + int err, pnum, tries = 0, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + + if (ubi->ro_mode) + return -EROFS; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum >= 0) { + dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn("failed to write data to PEB %d", pnum); + if (err == -EIO && ubi->bad_allowed) + err = recover_peb(ubi, pnum, vol_id, lnum, buf, + offset, len); + if (err) + ubi_ro_mode(ubi); + } + leb_write_unlock(ubi, vol_id, lnum); + return err; + } + + /* + * The logical eraseblock is not mapped. We have to get a free physical + * eraseblock and write the volume identifier header there first. + */ + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) { + leb_write_unlock(ubi, vol_id, lnum); + return -ENOMEM; + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_be32(vol_id); + vid_hdr->lnum = cpu_to_be32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_be32(vol->data_pad); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + if (len) { + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn("failed to write %d bytes at offset %d of " + "LEB %d:%d, PEB %d", len, offset, vol_id, + lnum, pnum); + goto write_error; + } + } + + vol->eba_tbl[lnum] = pnum; + + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + /* + * Fortunately, this is the first write operation to this physical + * eraseblock, so just put it and request a new one. We assume that if + * this physical eraseblock went bad, the erase code will handle that. + */ + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/** + * ubi_eba_write_leb_st - write data to static volume. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: data to write + * @len: how many bytes to write + * @dtype: data type + * @used_ebs: how many logical eraseblocks will this volume contain + * + * This function writes data to logical eraseblock @lnum of static volume + * @vol. The @used_ebs argument should contain total number of logical + * eraseblock in this static volume. + * + * When writing to the last logical eraseblock, the @len argument doesn't have + * to be aligned to the minimal I/O unit size. Instead, it has to be equivalent + * to the real data size, although the @buf buffer has to contain the + * alignment. In all other cases, @len has to be aligned. + * + * It is prohibited to write more then once to logical eraseblocks of static + * volumes. This function returns zero in case of success and a negative error + * code in case of failure. + */ +int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype, + int used_ebs) +{ + int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + + if (lnum == used_ebs - 1) + /* If this is the last LEB @len may be unaligned */ + len = ALIGN(data_size, ubi->min_io_size); + else + ubi_assert(!(len & (ubi->min_io_size - 1))); + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) { + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_be32(vol_id); + vid_hdr->lnum = cpu_to_be32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_be32(vol->data_pad); + + crc = crc32(UBI_CRC32_INIT, buf, data_size); + vid_hdr->vol_type = UBI_VID_STATIC; + vid_hdr->data_size = cpu_to_be32(data_size); + vid_hdr->used_ebs = cpu_to_be32(used_ebs); + vid_hdr->data_crc = cpu_to_be32(crc); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d", + len, vol_id, lnum, pnum, used_ebs); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, 0, len); + if (err) { + ubi_warn("failed to write %d bytes of data to PEB %d", + len, pnum); + goto write_error; + } + + ubi_assert(vol->eba_tbl[lnum] < 0); + vol->eba_tbl[lnum] = pnum; + + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/* + * ubi_eba_atomic_leb_change - change logical eraseblock atomically. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: data to write + * @len: how many bytes to write + * @dtype: data type + * + * This function changes the contents of a logical eraseblock atomically. @buf + * has to contain new logical eraseblock data, and @len - the length of the + * data, which has to be aligned. This function guarantees that in case of an + * unclean reboot the old contents is preserved. Returns zero in case of + * success and a negative error code in case of failure. + * + * UBI reserves one LEB for the "atomic LEB change" operation, so only one + * LEB change may be done at a time. This is ensured by @ubi->alc_mutex. + */ +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype) +{ + int err, pnum, tries = 0, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + + if (len == 0) { + /* + * Special case when data length is zero. In this case the LEB + * has to be unmapped and mapped somewhere else. + */ + err = ubi_eba_unmap_leb(ubi, vol, lnum); + if (err) + return err; + return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); + } + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; + + mutex_lock(&ubi->alc_mutex); + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + goto out_mutex; + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_be32(vol_id); + vid_hdr->lnum = cpu_to_be32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_be32(vol->data_pad); + + crc = crc32(UBI_CRC32_INIT, buf, len); + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->data_size = cpu_to_be32(len); + vid_hdr->copy_flag = 1; + vid_hdr->data_crc = cpu_to_be32(crc); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + err = pnum; + goto out_leb_unlock; + } + + dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d", + vol_id, lnum, vol->eba_tbl[lnum], pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, 0, len); + if (err) { + ubi_warn("failed to write %d bytes of data to PEB %d", + len, pnum); + goto write_error; + } + + if (vol->eba_tbl[lnum] >= 0) { + err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1); + if (err) + goto out_leb_unlock; + } + + vol->eba_tbl[lnum] = pnum; + +out_leb_unlock: + leb_write_unlock(ubi, vol_id, lnum); +out_mutex: + mutex_unlock(&ubi->alc_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + ubi_ro_mode(ubi); + goto out_leb_unlock; + } + + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + goto out_leb_unlock; + } + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/** + * ubi_eba_copy_leb - copy logical eraseblock. + * @ubi: UBI device description object + * @from: physical eraseblock number from where to copy + * @to: physical eraseblock number where to copy + * @vid_hdr: VID header of the @from physical eraseblock + * + * This function copies logical eraseblock from physical eraseblock @from to + * physical eraseblock @to. The @vid_hdr buffer may be changed by this + * function. Returns: + * o %0 in case of success; + * o %1 if the operation was canceled and should be tried later (e.g., + * because a bit-flip was detected at the target PEB); + * o %2 if the volume is being deleted and this LEB should not be moved. + */ +int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + struct ubi_vid_hdr *vid_hdr) +{ + int err, vol_id, lnum, data_size, aldata_size, idx; + struct ubi_volume *vol; + uint32_t crc; + + vol_id = be32_to_cpu(vid_hdr->vol_id); + lnum = be32_to_cpu(vid_hdr->lnum); + + dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to); + + if (vid_hdr->vol_type == UBI_VID_STATIC) { + data_size = be32_to_cpu(vid_hdr->data_size); + aldata_size = ALIGN(data_size, ubi->min_io_size); + } else + data_size = aldata_size = + ubi->leb_size - be32_to_cpu(vid_hdr->data_pad); + + idx = vol_id2idx(ubi, vol_id); + spin_lock(&ubi->volumes_lock); + /* + * Note, we may race with volume deletion, which means that the volume + * this logical eraseblock belongs to might be being deleted. Since the + * volume deletion unmaps all the volume's logical eraseblocks, it will + * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish. + */ + vol = ubi->volumes[idx]; + if (!vol) { + /* No need to do further work, cancel */ + dbg_eba("volume %d is being removed, cancel", vol_id); + spin_unlock(&ubi->volumes_lock); + return 2; + } + spin_unlock(&ubi->volumes_lock); + + /* + * We do not want anybody to write to this logical eraseblock while we + * are moving it, so lock it. + * + * Note, we are using non-waiting locking here, because we cannot sleep + * on the LEB, since it may cause deadlocks. Indeed, imagine a task is + * unmapping the LEB which is mapped to the PEB we are going to move + * (@from). This task locks the LEB and goes sleep in the + * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are + * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the + * LEB is already locked, we just do not move it and return %1. + */ + err = leb_write_trylock(ubi, vol_id, lnum); + if (err) { + dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum); + return err; + } + + /* + * The LEB might have been put meanwhile, and the task which put it is + * probably waiting on @ubi->move_mutex. No need to continue the work, + * cancel it. + */ + if (vol->eba_tbl[lnum] != from) { + dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " + "PEB %d, cancel", vol_id, lnum, from, + vol->eba_tbl[lnum]); + err = 1; + goto out_unlock_leb; + } + + /* + * OK, now the LEB is locked and we can safely start moving iy. Since + * this function utilizes thie @ubi->peb1_buf buffer which is shared + * with some other functions, so lock the buffer by taking the + * @ubi->buf_mutex. + */ + mutex_lock(&ubi->buf_mutex); + dbg_eba("read %d bytes of data", aldata_size); + err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size); + if (err && err != UBI_IO_BITFLIPS) { + ubi_warn("error %d while reading data from PEB %d", + err, from); + goto out_unlock_buf; + } + + /* + * Now we have got to calculate how much data we have to to copy. In + * case of a static volume it is fairly easy - the VID header contains + * the data size. In case of a dynamic volume it is more difficult - we + * have to read the contents, cut 0xFF bytes from the end and copy only + * the first part. We must do this to avoid writing 0xFF bytes as it + * may have some side-effects. And not only this. It is important not + * to include those 0xFFs to CRC because later the they may be filled + * by data. + */ + if (vid_hdr->vol_type == UBI_VID_DYNAMIC) + aldata_size = data_size = + ubi_calc_data_len(ubi, ubi->peb_buf1, data_size); + + cond_resched(); + crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size); + cond_resched(); + + /* + * It may turn out to me that the whole @from physical eraseblock + * contains only 0xFF bytes. Then we have to only write the VID header + * and do not write any data. This also means we should not set + * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc. + */ + if (data_size > 0) { + vid_hdr->copy_flag = 1; + vid_hdr->data_size = cpu_to_be32(data_size); + vid_hdr->data_crc = cpu_to_be32(crc); + } + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + + err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); + if (err) + goto out_unlock_buf; + + cond_resched(); + + /* Read the VID header back and check if it was written correctly */ + err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1); + if (err) { + if (err != UBI_IO_BITFLIPS) + ubi_warn("cannot read VID header back from PEB %d", to); + else + err = 1; + goto out_unlock_buf; + } + + if (data_size > 0) { + err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size); + if (err) + goto out_unlock_buf; + + cond_resched(); + + /* + * We've written the data and are going to read it back to make + * sure it was written correctly. + */ + + err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size); + if (err) { + if (err != UBI_IO_BITFLIPS) + ubi_warn("cannot read data back from PEB %d", + to); + else + err = 1; + goto out_unlock_buf; + } + + cond_resched(); + + if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) { + ubi_warn("read data back from PEB %d - it is different", + to); + goto out_unlock_buf; + } + } + + ubi_assert(vol->eba_tbl[lnum] == from); + vol->eba_tbl[lnum] = to; + +out_unlock_buf: + mutex_unlock(&ubi->buf_mutex); +out_unlock_leb: + leb_write_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * ubi_eba_init_scan - initialize the EBA unit using scanning information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int i, j, err, num_volumes; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + struct ubi_scan_leb *seb; + struct rb_node *rb; + + dbg_eba("initialize EBA unit"); + + spin_lock_init(&ubi->ltree_lock); + mutex_init(&ubi->alc_mutex); + ubi->ltree = RB_ROOT; + + ubi->global_sqnum = si->max_sqnum + 1; + num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; + + for (i = 0; i < num_volumes; i++) { + vol = ubi->volumes[i]; + if (!vol) + continue; + + cond_resched(); + + vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), + GFP_KERNEL); + if (!vol->eba_tbl) { + err = -ENOMEM; + goto out_free; + } + + for (j = 0; j < vol->reserved_pebs; j++) + vol->eba_tbl[j] = UBI_LEB_UNMAPPED; + + sv = ubi_scan_find_sv(si, idx2vol_id(ubi, i)); + if (!sv) + continue; + + ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { + if (seb->lnum >= vol->reserved_pebs) + /* + * This may happen in case of an unclean reboot + * during re-size. + */ + ubi_scan_move_to_list(sv, seb, &si->erase); + vol->eba_tbl[seb->lnum] = seb->pnum; + } + } + + if (ubi->avail_pebs < EBA_RESERVED_PEBS) { + ubi_err("no enough physical eraseblocks (%d, need %d)", + ubi->avail_pebs, EBA_RESERVED_PEBS); + err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= EBA_RESERVED_PEBS; + ubi->rsvd_pebs += EBA_RESERVED_PEBS; + + if (ubi->bad_allowed) { + ubi_calculate_reserved(ubi); + + if (ubi->avail_pebs < ubi->beb_rsvd_level) { + /* No enough free physical eraseblocks */ + ubi->beb_rsvd_pebs = ubi->avail_pebs; + ubi_warn("cannot reserve enough PEBs for bad PEB " + "handling, reserved %d, need %d", + ubi->beb_rsvd_pebs, ubi->beb_rsvd_level); + } else + ubi->beb_rsvd_pebs = ubi->beb_rsvd_level; + + ubi->avail_pebs -= ubi->beb_rsvd_pebs; + ubi->rsvd_pebs += ubi->beb_rsvd_pebs; + } + + dbg_eba("EBA unit is initialized"); + return 0; + +out_free: + for (i = 0; i < num_volumes; i++) { + if (!ubi->volumes[i]) + continue; + kfree(ubi->volumes[i]->eba_tbl); + } + return err; +} + +/** + * ubi_eba_close - close EBA unit. + * @ubi: UBI device description object + */ +void ubi_eba_close(const struct ubi_device *ubi) +{ + int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; + + dbg_eba("close EBA unit"); + + for (i = 0; i < num_volumes; i++) { + if (!ubi->volumes[i]) + continue; + kfree(ubi->volumes[i]->eba_tbl); + } +} diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/io.c b/qemu/roms/u-boot/drivers/mtd/ubi/io.c new file mode 100644 index 000000000..960befc6d --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/io.c @@ -0,0 +1,1262 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * UBI input/output unit. + * + * This unit provides a uniform way to work with all kinds of the underlying + * MTD devices. It also implements handy functions for reading and writing UBI + * headers. + * + * We are trying to have a paranoid mindset and not to trust to what we read + * from the flash media in order to be more secure and robust. So this unit + * validates every single header it reads from the flash media. + * + * Some words about how the eraseblock headers are stored. + * + * The erase counter header is always stored at offset zero. By default, the + * VID header is stored after the EC header at the closest aligned offset + * (i.e. aligned to the minimum I/O unit size). Data starts next to the VID + * header at the closest aligned offset. But this default layout may be + * changed. For example, for different reasons (e.g., optimization) UBI may be + * asked to put the VID header at further offset, and even at an unaligned + * offset. Of course, if the offset of the VID header is unaligned, UBI adds + * proper padding in front of it. Data offset may also be changed but it has to + * be aligned. + * + * About minimal I/O units. In general, UBI assumes flash device model where + * there is only one minimal I/O unit size. E.g., in case of NOR flash it is 1, + * in case of NAND flash it is a NAND page, etc. This is reported by MTD in the + * @ubi->mtd->writesize field. But as an exception, UBI admits of using another + * (smaller) minimal I/O unit size for EC and VID headers to make it possible + * to do different optimizations. + * + * This is extremely useful in case of NAND flashes which admit of several + * write operations to one NAND page. In this case UBI can fit EC and VID + * headers at one NAND page. Thus, UBI may use "sub-page" size as the minimal + * I/O unit for the headers (the @ubi->hdrs_min_io_size field). But it still + * reports NAND page size (@ubi->min_io_size) as a minimal I/O unit for the UBI + * users. + * + * Example: some Samsung NANDs with 2KiB pages allow 4x 512-byte writes, so + * although the minimal I/O unit is 2K, UBI uses 512 bytes for EC and VID + * headers. + * + * Q: why not just to treat sub-page as a minimal I/O unit of this flash + * device, e.g., make @ubi->min_io_size = 512 in the example above? + * + * A: because when writing a sub-page, MTD still writes a full 2K page but the + * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing + * 4x512 sub-pages is 4 times slower then writing one 2KiB NAND page. Thus, we + * prefer to use sub-pages only for EV and VID headers. + * + * As it was noted above, the VID header may start at a non-aligned offset. + * For example, in case of a 2KiB page NAND flash with a 512 bytes sub-page, + * the VID header may reside at offset 1984 which is the last 64 bytes of the + * last sub-page (EC header is always at offset zero). This causes some + * difficulties when reading and writing VID headers. + * + * Suppose we have a 64-byte buffer and we read a VID header at it. We change + * the data and want to write this VID header out. As we can only write in + * 512-byte chunks, we have to allocate one more buffer and copy our VID header + * to offset 448 of this buffer. + * + * The I/O unit does the following trick in order to avoid this extra copy. + * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header + * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the + * VID header is being written out, it shifts the VID header pointer back and + * writes the whole sub-page. + */ + +#ifdef UBI_LINUX +#include +#include +#endif + +#include +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum); +static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum); +static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr); +static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum); +static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr); +static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, + int len); +#else +#define paranoid_check_not_bad(ubi, pnum) 0 +#define paranoid_check_peb_ec_hdr(ubi, pnum) 0 +#define paranoid_check_ec_hdr(ubi, pnum, ec_hdr) 0 +#define paranoid_check_peb_vid_hdr(ubi, pnum) 0 +#define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0 +#define paranoid_check_all_ff(ubi, pnum, offset, len) 0 +#endif + +/** + * ubi_io_read - read data from a physical eraseblock. + * @ubi: UBI device description object + * @buf: buffer where to store the read data + * @pnum: physical eraseblock number to read from + * @offset: offset within the physical eraseblock from where to read + * @len: how many bytes to read + * + * This function reads data from offset @offset of physical eraseblock @pnum + * and stores the read data in the @buf buffer. The following return codes are + * possible: + * + * o %0 if all the requested data were successfully read; + * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but + * correctable bit-flips were detected; this is harmless but may indicate + * that this eraseblock may become bad soon (but do not have to); + * o %-EBADMSG if the MTD subsystem reported about data integrity problems, for + * example it can be an ECC error in case of NAND; this most probably means + * that the data is corrupted; + * o %-EIO if some I/O error occurred; + * o other negative error codes in case of other errors. + */ +int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + int len) +{ + int err, retries = 0; + size_t read; + loff_t addr; + + dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset); + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); + ubi_assert(len > 0); + + err = paranoid_check_not_bad(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + + addr = (loff_t)pnum * ubi->peb_size + offset; +retry: + err = mtd_read(ubi->mtd, addr, len, &read, buf); + if (err) { + if (err == -EUCLEAN) { + /* + * -EUCLEAN is reported if there was a bit-flip which + * was corrected, so this is harmless. + */ + ubi_msg("fixable bit-flip detected at PEB %d", pnum); + ubi_assert(len == read); + return UBI_IO_BITFLIPS; + } + + if (read != len && retries++ < UBI_IO_RETRIES) { + dbg_io("error %d while reading %d bytes from PEB %d:%d, " + "read only %zd bytes, retry", + err, len, pnum, offset, read); + yield(); + goto retry; + } + + ubi_err("error %d while reading %d bytes from PEB %d:%d, " + "read %zd bytes", err, len, pnum, offset, read); + ubi_dbg_dump_stack(); + + /* + * The driver should never return -EBADMSG if it failed to read + * all the requested data. But some buggy drivers might do + * this, so we change it to -EIO. + */ + if (read != len && err == -EBADMSG) { + ubi_assert(0); + printk("%s[%d] not here\n", __func__, __LINE__); +/* err = -EIO; */ + } + } else { + ubi_assert(len == read); + + if (ubi_dbg_is_bitflip()) { + dbg_msg("bit-flip (emulated)"); + err = UBI_IO_BITFLIPS; + } + } + + return err; +} + +/** + * ubi_io_write - write data to a physical eraseblock. + * @ubi: UBI device description object + * @buf: buffer with the data to write + * @pnum: physical eraseblock number to write to + * @offset: offset within the physical eraseblock where to write + * @len: how many bytes to write + * + * This function writes @len bytes of data from buffer @buf to offset @offset + * of physical eraseblock @pnum. If all the data were successfully written, + * zero is returned. If an error occurred, this function returns a negative + * error code. If %-EIO is returned, the physical eraseblock most probably went + * bad. + * + * Note, in case of an error, it is possible that something was still written + * to the flash media, but may be some garbage. + */ +int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, + int len) +{ + int err; + size_t written; + loff_t addr; + + dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset); + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); + ubi_assert(offset % ubi->hdrs_min_io_size == 0); + ubi_assert(len > 0 && len % ubi->hdrs_min_io_size == 0); + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + /* The below has to be compiled out if paranoid checks are disabled */ + + err = paranoid_check_not_bad(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + + /* The area we are writing to has to contain all 0xFF bytes */ + err = paranoid_check_all_ff(ubi, pnum, offset, len); + if (err) + return err > 0 ? -EINVAL : err; + + if (offset >= ubi->leb_start) { + /* + * We write to the data area of the physical eraseblock. Make + * sure it has valid EC and VID headers. + */ + err = paranoid_check_peb_ec_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + err = paranoid_check_peb_vid_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + } + + if (ubi_dbg_is_write_failure()) { + dbg_err("cannot write %d bytes to PEB %d:%d " + "(emulated)", len, pnum, offset); + ubi_dbg_dump_stack(); + return -EIO; + } + + addr = (loff_t)pnum * ubi->peb_size + offset; + err = mtd_write(ubi->mtd, addr, len, &written, buf); + if (err) { + ubi_err("error %d while writing %d bytes to PEB %d:%d, written" + " %zd bytes", err, len, pnum, offset, written); + ubi_dbg_dump_stack(); + } else + ubi_assert(written == len); + + return err; +} + +/** + * erase_callback - MTD erasure call-back. + * @ei: MTD erase information object. + * + * Note, even though MTD erase interface is asynchronous, all the current + * implementations are synchronous anyway. + */ +static void erase_callback(struct erase_info *ei) +{ + wake_up_interruptible((wait_queue_head_t *)ei->priv); +} + +/** + * do_sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to erase + * + * This function synchronously erases physical eraseblock @pnum and returns + * zero in case of success and a negative error code in case of failure. If + * %-EIO is returned, the physical eraseblock most probably went bad. + */ +static int do_sync_erase(struct ubi_device *ubi, int pnum) +{ + int err, retries = 0; + struct erase_info ei; + wait_queue_head_t wq; + + dbg_io("erase PEB %d", pnum); + +retry: + init_waitqueue_head(&wq); + memset(&ei, 0, sizeof(struct erase_info)); + + ei.mtd = ubi->mtd; + ei.addr = (loff_t)pnum * ubi->peb_size; + ei.len = ubi->peb_size; + ei.callback = erase_callback; + ei.priv = (unsigned long)&wq; + + err = mtd_erase(ubi->mtd, &ei); + if (err) { + if (retries++ < UBI_IO_RETRIES) { + dbg_io("error %d while erasing PEB %d, retry", + err, pnum); + yield(); + goto retry; + } + ubi_err("cannot erase PEB %d, error %d", pnum, err); + ubi_dbg_dump_stack(); + return err; + } + + err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE || + ei.state == MTD_ERASE_FAILED); + if (err) { + ubi_err("interrupted PEB %d erasure", pnum); + return -EINTR; + } + + if (ei.state == MTD_ERASE_FAILED) { + if (retries++ < UBI_IO_RETRIES) { + dbg_io("error while erasing PEB %d, retry", pnum); + yield(); + goto retry; + } + ubi_err("cannot erase PEB %d", pnum); + ubi_dbg_dump_stack(); + return -EIO; + } + + err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size); + if (err) + return err > 0 ? -EINVAL : err; + + if (ubi_dbg_is_erase_failure() && !err) { + dbg_err("cannot erase PEB %d (emulated)", pnum); + return -EIO; + } + + return 0; +} + +/** + * check_pattern - check if buffer contains only a certain byte pattern. + * @buf: buffer to check + * @patt: the pattern to check + * @size: buffer size in bytes + * + * This function returns %1 in there are only @patt bytes in @buf, and %0 if + * something else was also found. + */ +static int check_pattern(const void *buf, uint8_t patt, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (((const uint8_t *)buf)[i] != patt) + return 0; + return 1; +} + +/* Patterns to write to a physical eraseblock when torturing it */ +static uint8_t patterns[] = {0xa5, 0x5a, 0x0}; + +/** + * torture_peb - test a supposedly bad physical eraseblock. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to test + * + * This function returns %-EIO if the physical eraseblock did not pass the + * test, a positive number of erase operations done if the test was + * successfully passed, and other negative error codes in case of other errors. + */ +static int torture_peb(struct ubi_device *ubi, int pnum) +{ + int err, i, patt_count; + + patt_count = ARRAY_SIZE(patterns); + ubi_assert(patt_count > 0); + + mutex_lock(&ubi->buf_mutex); + for (i = 0; i < patt_count; i++) { + err = do_sync_erase(ubi, pnum); + if (err) + goto out; + + /* Make sure the PEB contains only 0xFF bytes */ + err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + if (err) + goto out; + + err = check_pattern(ubi->peb_buf1, 0xFF, ubi->peb_size); + if (err == 0) { + ubi_err("erased PEB %d, but a non-0xFF byte found", + pnum); + err = -EIO; + goto out; + } + + /* Write a pattern and check it */ + memset(ubi->peb_buf1, patterns[i], ubi->peb_size); + err = ubi_io_write(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + if (err) + goto out; + + memset(ubi->peb_buf1, ~patterns[i], ubi->peb_size); + err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + if (err) + goto out; + + err = check_pattern(ubi->peb_buf1, patterns[i], ubi->peb_size); + if (err == 0) { + ubi_err("pattern %x checking failed for PEB %d", + patterns[i], pnum); + err = -EIO; + goto out; + } + } + + err = patt_count; + +out: + mutex_unlock(&ubi->buf_mutex); + if (err == UBI_IO_BITFLIPS || err == -EBADMSG) { + /* + * If a bit-flip or data integrity error was detected, the test + * has not passed because it happened on a freshly erased + * physical eraseblock which means something is wrong with it. + */ + ubi_err("read problems on freshly erased PEB %d, must be bad", + pnum); + err = -EIO; + } + return err; +} + +/** + * ubi_io_sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to erase + * @torture: if this physical eraseblock has to be tortured + * + * This function synchronously erases physical eraseblock @pnum. If @torture + * flag is not zero, the physical eraseblock is checked by means of writing + * different patterns to it and reading them back. If the torturing is enabled, + * the physical eraseblock is erased more then once. + * + * This function returns the number of erasures made in case of success, %-EIO + * if the erasure failed or the torturing test failed, and other negative error + * codes in case of other errors. Note, %-EIO means that the physical + * eraseblock is bad. + */ +int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture) +{ + int err, ret = 0; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + err = paranoid_check_not_bad(ubi, pnum); + if (err != 0) + return err > 0 ? -EINVAL : err; + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (torture) { + ret = torture_peb(ubi, pnum); + if (ret < 0) + return ret; + } + + err = do_sync_erase(ubi, pnum); + if (err) + return err; + + return ret + 1; +} + +/** + * ubi_io_is_bad - check if a physical eraseblock is bad. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns a positive number if the physical eraseblock is bad, + * zero if not, and a negative error code if an error occurred. + */ +int ubi_io_is_bad(const struct ubi_device *ubi, int pnum) +{ + struct mtd_info *mtd = ubi->mtd; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + if (ubi->bad_allowed) { + int ret; + + ret = mtd_block_isbad(mtd, (loff_t)pnum * ubi->peb_size); + if (ret < 0) + ubi_err("error %d while checking if PEB %d is bad", + ret, pnum); + else if (ret) + dbg_io("PEB %d is bad", pnum); + return ret; + } + + return 0; +} + +/** + * ubi_io_mark_bad - mark a physical eraseblock as bad. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to mark + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum) +{ + int err; + struct mtd_info *mtd = ubi->mtd; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (!ubi->bad_allowed) + return 0; + + err = mtd_block_markbad(mtd, (loff_t)pnum * ubi->peb_size); + if (err) + ubi_err("cannot mark PEB %d bad, error %d", pnum, err); + return err; +} + +/** + * validate_ec_hdr - validate an erase counter header. + * @ubi: UBI device description object + * @ec_hdr: the erase counter header to check + * + * This function returns zero if the erase counter header is OK, and %1 if + * not. + */ +static int validate_ec_hdr(const struct ubi_device *ubi, + const struct ubi_ec_hdr *ec_hdr) +{ + long long ec; + int vid_hdr_offset, leb_start; + + ec = be64_to_cpu(ec_hdr->ec); + vid_hdr_offset = be32_to_cpu(ec_hdr->vid_hdr_offset); + leb_start = be32_to_cpu(ec_hdr->data_offset); + + if (ec_hdr->version != UBI_VERSION) { + ubi_err("node with incompatible UBI version found: " + "this UBI version is %d, image version is %d", + UBI_VERSION, (int)ec_hdr->version); + goto bad; + } + + if (vid_hdr_offset != ubi->vid_hdr_offset) { + ubi_err("bad VID header offset %d, expected %d", + vid_hdr_offset, ubi->vid_hdr_offset); + goto bad; + } + + if (leb_start != ubi->leb_start) { + ubi_err("bad data offset %d, expected %d", + leb_start, ubi->leb_start); + goto bad; + } + + if (ec < 0 || ec > UBI_MAX_ERASECOUNTER) { + ubi_err("bad erase counter %lld", ec); + goto bad; + } + + return 0; + +bad: + ubi_err("bad EC header"); + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * ubi_io_read_ec_hdr - read and check an erase counter header. + * @ubi: UBI device description object + * @pnum: physical eraseblock to read from + * @ec_hdr: a &struct ubi_ec_hdr object where to store the read erase counter + * header + * @verbose: be verbose if the header is corrupted or was not found + * + * This function reads erase counter header from physical eraseblock @pnum and + * stores it in @ec_hdr. This function also checks CRC checksum of the read + * erase counter header. The following codes may be returned: + * + * o %0 if the CRC checksum is correct and the header was successfully read; + * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected + * and corrected by the flash driver; this is harmless but may indicate that + * this eraseblock may become bad soon (but may be not); + * o %UBI_IO_BAD_EC_HDR if the erase counter header is corrupted (a CRC error); + * o %UBI_IO_PEB_EMPTY if the physical eraseblock is empty; + * o a negative error code in case of failure. + */ +int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr, int verbose) +{ + int err, read_err = 0; + uint32_t crc, magic, hdr_crc; + + dbg_io("read EC header from PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + if (UBI_IO_DEBUG) + verbose = 1; + + err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); + if (err) { + if (err != UBI_IO_BITFLIPS && err != -EBADMSG) + return err; + + /* + * We read all the data, but either a correctable bit-flip + * occurred, or MTD reported about some data integrity error, + * like an ECC error in case of NAND. The former is harmless, + * the later may mean that the read data is corrupted. But we + * have a CRC check-sum and we will detect this. If the EC + * header is still OK, we just report this as there was a + * bit-flip. + */ + read_err = err; + } + + magic = be32_to_cpu(ec_hdr->magic); + if (magic != UBI_EC_HDR_MAGIC) { + /* + * The magic field is wrong. Let's check if we have read all + * 0xFF. If yes, this physical eraseblock is assumed to be + * empty. + * + * But if there was a read error, we do not test it for all + * 0xFFs. Even if it does contain all 0xFFs, this error + * indicates that something is still wrong with this physical + * eraseblock and we anyway cannot treat it as empty. + */ + if (read_err != -EBADMSG && + check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) { + /* The physical eraseblock is supposedly empty */ + + /* + * The below is just a paranoid check, it has to be + * compiled out if paranoid checks are disabled. + */ + err = paranoid_check_all_ff(ubi, pnum, 0, + ubi->peb_size); + if (err) + return err > 0 ? UBI_IO_BAD_EC_HDR : err; + + if (verbose) + ubi_warn("no EC header found at PEB %d, " + "only 0xFF bytes", pnum); + return UBI_IO_PEB_EMPTY; + } + + /* + * This is not a valid erase counter header, and these are not + * 0xFF bytes. Report that the header is corrupted. + */ + if (verbose) { + ubi_warn("bad magic number at PEB %d: %08x instead of " + "%08x", pnum, magic, UBI_EC_HDR_MAGIC); + ubi_dbg_dump_ec_hdr(ec_hdr); + } + return UBI_IO_BAD_EC_HDR; + } + + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = be32_to_cpu(ec_hdr->hdr_crc); + + if (hdr_crc != crc) { + if (verbose) { + ubi_warn("bad EC header CRC at PEB %d, calculated %#08x," + " read %#08x", pnum, crc, hdr_crc); + ubi_dbg_dump_ec_hdr(ec_hdr); + } + return UBI_IO_BAD_EC_HDR; + } + + /* And of course validate what has just been read from the media */ + err = validate_ec_hdr(ubi, ec_hdr); + if (err) { + ubi_err("validation failed for PEB %d", pnum); + return -EINVAL; + } + + return read_err ? UBI_IO_BITFLIPS : 0; +} + +/** + * ubi_io_write_ec_hdr - write an erase counter header. + * @ubi: UBI device description object + * @pnum: physical eraseblock to write to + * @ec_hdr: the erase counter header to write + * + * This function writes erase counter header described by @ec_hdr to physical + * eraseblock @pnum. It also fills most fields of @ec_hdr before writing, so + * the caller do not have to fill them. Callers must only fill the @ec_hdr->ec + * field. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock most probably + * went bad. + */ +int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr) +{ + int err; + uint32_t crc; + + dbg_io("write EC header to PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + ec_hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); + ec_hdr->version = UBI_VERSION; + ec_hdr->vid_hdr_offset = cpu_to_be32(ubi->vid_hdr_offset); + ec_hdr->data_offset = cpu_to_be32(ubi->leb_start); + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + ec_hdr->hdr_crc = cpu_to_be32(crc); + + err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + if (err) + return -EINVAL; + + err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); + return err; +} + +/** + * validate_vid_hdr - validate a volume identifier header. + * @ubi: UBI device description object + * @vid_hdr: the volume identifier header to check + * + * This function checks that data stored in the volume identifier header + * @vid_hdr. Returns zero if the VID header is OK and %1 if not. + */ +static int validate_vid_hdr(const struct ubi_device *ubi, + const struct ubi_vid_hdr *vid_hdr) +{ + int vol_type = vid_hdr->vol_type; + int copy_flag = vid_hdr->copy_flag; + int vol_id = be32_to_cpu(vid_hdr->vol_id); + int lnum = be32_to_cpu(vid_hdr->lnum); + int compat = vid_hdr->compat; + int data_size = be32_to_cpu(vid_hdr->data_size); + int used_ebs = be32_to_cpu(vid_hdr->used_ebs); + int data_pad = be32_to_cpu(vid_hdr->data_pad); + int data_crc = be32_to_cpu(vid_hdr->data_crc); + int usable_leb_size = ubi->leb_size - data_pad; + + if (copy_flag != 0 && copy_flag != 1) { + dbg_err("bad copy_flag"); + goto bad; + } + + if (vol_id < 0 || lnum < 0 || data_size < 0 || used_ebs < 0 || + data_pad < 0) { + dbg_err("negative values"); + goto bad; + } + + if (vol_id >= UBI_MAX_VOLUMES && vol_id < UBI_INTERNAL_VOL_START) { + dbg_err("bad vol_id"); + goto bad; + } + + if (vol_id < UBI_INTERNAL_VOL_START && compat != 0) { + dbg_err("bad compat"); + goto bad; + } + + if (vol_id >= UBI_INTERNAL_VOL_START && compat != UBI_COMPAT_DELETE && + compat != UBI_COMPAT_RO && compat != UBI_COMPAT_PRESERVE && + compat != UBI_COMPAT_REJECT) { + dbg_err("bad compat"); + goto bad; + } + + if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { + dbg_err("bad vol_type"); + goto bad; + } + + if (data_pad >= ubi->leb_size / 2) { + dbg_err("bad data_pad"); + goto bad; + } + + if (vol_type == UBI_VID_STATIC) { + /* + * Although from high-level point of view static volumes may + * contain zero bytes of data, but no VID headers can contain + * zero at these fields, because they empty volumes do not have + * mapped logical eraseblocks. + */ + if (used_ebs == 0) { + dbg_err("zero used_ebs"); + goto bad; + } + if (data_size == 0) { + dbg_err("zero data_size"); + goto bad; + } + if (lnum < used_ebs - 1) { + if (data_size != usable_leb_size) { + dbg_err("bad data_size"); + goto bad; + } + } else if (lnum == used_ebs - 1) { + if (data_size == 0) { + dbg_err("bad data_size at last LEB"); + goto bad; + } + } else { + dbg_err("too high lnum"); + goto bad; + } + } else { + if (copy_flag == 0) { + if (data_crc != 0) { + dbg_err("non-zero data CRC"); + goto bad; + } + if (data_size != 0) { + dbg_err("non-zero data_size"); + goto bad; + } + } else { + if (data_size == 0) { + dbg_err("zero data_size of copy"); + goto bad; + } + } + if (used_ebs != 0) { + dbg_err("bad used_ebs"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("bad VID header"); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * ubi_io_read_vid_hdr - read and check a volume identifier header. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to read from + * @vid_hdr: &struct ubi_vid_hdr object where to store the read volume + * identifier header + * @verbose: be verbose if the header is corrupted or wasn't found + * + * This function reads the volume identifier header from physical eraseblock + * @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read + * volume identifier header. The following codes may be returned: + * + * o %0 if the CRC checksum is correct and the header was successfully read; + * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected + * and corrected by the flash driver; this is harmless but may indicate that + * this eraseblock may become bad soon; + * o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted (a CRC + * error detected); + * o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID + * header there); + * o a negative error code in case of failure. + */ +int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose) +{ + int err, read_err = 0; + uint32_t crc, magic, hdr_crc; + void *p; + + dbg_io("read VID header from PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + if (UBI_IO_DEBUG) + verbose = 1; + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + if (err) { + if (err != UBI_IO_BITFLIPS && err != -EBADMSG) + return err; + + /* + * We read all the data, but either a correctable bit-flip + * occurred, or MTD reported about some data integrity error, + * like an ECC error in case of NAND. The former is harmless, + * the later may mean the read data is corrupted. But we have a + * CRC check-sum and we will identify this. If the VID header is + * still OK, we just report this as there was a bit-flip. + */ + read_err = err; + } + + magic = be32_to_cpu(vid_hdr->magic); + if (magic != UBI_VID_HDR_MAGIC) { + /* + * If we have read all 0xFF bytes, the VID header probably does + * not exist and the physical eraseblock is assumed to be free. + * + * But if there was a read error, we do not test the data for + * 0xFFs. Even if it does contain all 0xFFs, this error + * indicates that something is still wrong with this physical + * eraseblock and it cannot be regarded as free. + */ + if (read_err != -EBADMSG && + check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) { + /* The physical eraseblock is supposedly free */ + + /* + * The below is just a paranoid check, it has to be + * compiled out if paranoid checks are disabled. + */ + err = paranoid_check_all_ff(ubi, pnum, ubi->leb_start, + ubi->leb_size); + if (err) + return err > 0 ? UBI_IO_BAD_VID_HDR : err; + + if (verbose) + ubi_warn("no VID header found at PEB %d, " + "only 0xFF bytes", pnum); + return UBI_IO_PEB_FREE; + } + + /* + * This is not a valid VID header, and these are not 0xFF + * bytes. Report that the header is corrupted. + */ + if (verbose) { + ubi_warn("bad magic number at PEB %d: %08x instead of " + "%08x", pnum, magic, UBI_VID_HDR_MAGIC); + ubi_dbg_dump_vid_hdr(vid_hdr); + } + return UBI_IO_BAD_VID_HDR; + } + + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); + hdr_crc = be32_to_cpu(vid_hdr->hdr_crc); + + if (hdr_crc != crc) { + if (verbose) { + ubi_warn("bad CRC at PEB %d, calculated %#08x, " + "read %#08x", pnum, crc, hdr_crc); + ubi_dbg_dump_vid_hdr(vid_hdr); + } + return UBI_IO_BAD_VID_HDR; + } + + /* Validate the VID header that we have just read */ + err = validate_vid_hdr(ubi, vid_hdr); + if (err) { + ubi_err("validation failed for PEB %d", pnum); + return -EINVAL; + } + + return read_err ? UBI_IO_BITFLIPS : 0; +} + +/** + * ubi_io_write_vid_hdr - write a volume identifier header. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to write to + * @vid_hdr: the volume identifier header to write + * + * This function writes the volume identifier header described by @vid_hdr to + * physical eraseblock @pnum. This function automatically fills the + * @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates + * header CRC checksum and stores it at vid_hdr->hdr_crc. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock probably went + * bad. + */ +int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr) +{ + int err; + uint32_t crc; + void *p; + + dbg_io("write VID header to PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + err = paranoid_check_peb_ec_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL: err; + + vid_hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); + vid_hdr->version = UBI_VERSION; + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); + vid_hdr->hdr_crc = cpu_to_be32(crc); + + err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + if (err) + return -EINVAL; + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + return err; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_not_bad - ensure that a physical eraseblock is not bad. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to check + * + * This function returns zero if the physical eraseblock is good, a positive + * number if it is bad and a negative error code if an error occurred. + */ +static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum) +{ + int err; + + err = ubi_io_is_bad(ubi, pnum); + if (!err) + return err; + + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_stack(); + return err; +} + +/** + * paranoid_check_ec_hdr - check if an erase counter header is all right. + * @ubi: UBI device description object + * @pnum: physical eraseblock number the erase counter header belongs to + * @ec_hdr: the erase counter header to check + * + * This function returns zero if the erase counter header contains valid + * values, and %1 if not. + */ +static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr) +{ + int err; + uint32_t magic; + + magic = be32_to_cpu(ec_hdr->magic); + if (magic != UBI_EC_HDR_MAGIC) { + ubi_err("bad magic %#08x, must be %#08x", + magic, UBI_EC_HDR_MAGIC); + goto fail; + } + + err = validate_ec_hdr(ubi, ec_hdr); + if (err) { + ubi_err("paranoid check failed for PEB %d", pnum); + goto fail; + } + + return 0; + +fail: + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * paranoid_check_peb_ec_hdr - check that the erase counter header of a + * physical eraseblock is in-place and is all right. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns zero if the erase counter header is all right, %1 if + * not, and a negative error code if an error occurred. + */ +static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum) +{ + int err; + uint32_t crc, hdr_crc; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); + if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) + goto exit; + + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = be32_to_cpu(ec_hdr->hdr_crc); + if (hdr_crc != crc) { + ubi_err("bad CRC, calculated %#08x, read %#08x", crc, hdr_crc); + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + err = 1; + goto exit; + } + + err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + +exit: + kfree(ec_hdr); + return err; +} + +/** + * paranoid_check_vid_hdr - check that a volume identifier header is all right. + * @ubi: UBI device description object + * @pnum: physical eraseblock number the volume identifier header belongs to + * @vid_hdr: the volume identifier header to check + * + * This function returns zero if the volume identifier header is all right, and + * %1 if not. + */ +static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + int err; + uint32_t magic; + + magic = be32_to_cpu(vid_hdr->magic); + if (magic != UBI_VID_HDR_MAGIC) { + ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x", + magic, pnum, UBI_VID_HDR_MAGIC); + goto fail; + } + + err = validate_vid_hdr(ubi, vid_hdr); + if (err) { + ubi_err("paranoid check failed for PEB %d", pnum); + goto fail; + } + + return err; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + return 1; + +} + +/** + * paranoid_check_peb_vid_hdr - check that the volume identifier header of a + * physical eraseblock is in-place and is all right. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns zero if the volume identifier header is all right, + * %1 if not, and a negative error code if an error occurred. + */ +static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) +{ + int err; + uint32_t crc, hdr_crc; + struct ubi_vid_hdr *vid_hdr; + void *p; + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) + goto exit; + + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = be32_to_cpu(vid_hdr->hdr_crc); + if (hdr_crc != crc) { + ubi_err("bad VID header CRC at PEB %d, calculated %#08x, " + "read %#08x", pnum, crc, hdr_crc); + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + err = 1; + goto exit; + } + + err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + +exit: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; +} + +/** + * paranoid_check_all_ff - check that a region of flash is empty. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * @offset: the starting offset within the physical eraseblock to check + * @len: the length of the region to check + * + * This function returns zero if only 0xFF bytes are present at offset + * @offset of the physical eraseblock @pnum, %1 if not, and a negative error + * code if an error occurred. + */ +static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, + int len) +{ + size_t read; + int err; + loff_t addr = (loff_t)pnum * ubi->peb_size + offset; + + mutex_lock(&ubi->dbg_buf_mutex); + err = mtd_read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf); + if (err && err != -EUCLEAN) { + ubi_err("error %d while reading %d bytes from PEB %d:%d, " + "read %zd bytes", err, len, pnum, offset, read); + goto error; + } + + err = check_pattern(ubi->dbg_peb_buf, 0xFF, len); + if (err == 0) { + ubi_err("flash region at PEB %d:%d, length %d does not " + "contain all 0xFF bytes", pnum, offset, len); + goto fail; + } + mutex_unlock(&ubi->dbg_buf_mutex); + + return 0; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + dbg_msg("hex dump of the %d-%d region", offset, offset + len); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + ubi->dbg_peb_buf, len, 1); + err = 1; +error: + ubi_dbg_dump_stack(); + mutex_unlock(&ubi->dbg_buf_mutex); + return err; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/kapi.c b/qemu/roms/u-boot/drivers/mtd/ubi/kapi.c new file mode 100644 index 000000000..63c56c998 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/kapi.c @@ -0,0 +1,626 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* This file mostly implements UBI kernel API functions */ + +#ifdef UBI_LINUX +#include +#include +#include +#endif + +#include +#include "ubi.h" + +/** + * ubi_get_device_info - get information about UBI device. + * @ubi_num: UBI device number + * @di: the information is stored here + * + * This function returns %0 in case of success, %-EINVAL if the UBI device + * number is invalid, and %-ENODEV if there is no such UBI device. + */ +int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) +{ + struct ubi_device *ubi; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return -ENODEV; + + di->ubi_num = ubi->ubi_num; + di->leb_size = ubi->leb_size; + di->min_io_size = ubi->min_io_size; + di->ro_mode = ubi->ro_mode; + di->cdev = ubi->cdev.dev; + + ubi_put_device(ubi); + return 0; +} +EXPORT_SYMBOL_GPL(ubi_get_device_info); + +/** + * ubi_get_volume_info - get information about UBI volume. + * @desc: volume descriptor + * @vi: the information is stored here + */ +void ubi_get_volume_info(struct ubi_volume_desc *desc, + struct ubi_volume_info *vi) +{ + const struct ubi_volume *vol = desc->vol; + const struct ubi_device *ubi = vol->ubi; + + vi->vol_id = vol->vol_id; + vi->ubi_num = ubi->ubi_num; + vi->size = vol->reserved_pebs; + vi->used_bytes = vol->used_bytes; + vi->vol_type = vol->vol_type; + vi->corrupted = vol->corrupted; + vi->upd_marker = vol->upd_marker; + vi->alignment = vol->alignment; + vi->usable_leb_size = vol->usable_leb_size; + vi->name_len = vol->name_len; + vi->name = vol->name; + vi->cdev = vol->cdev.dev; +} +EXPORT_SYMBOL_GPL(ubi_get_volume_info); + +/** + * ubi_open_volume - open UBI volume. + * @ubi_num: UBI device number + * @vol_id: volume ID + * @mode: open mode + * + * The @mode parameter specifies if the volume should be opened in read-only + * mode, read-write mode, or exclusive mode. The exclusive mode guarantees that + * nobody else will be able to open this volume. UBI allows to have many volume + * readers and one writer at a time. + * + * If a static volume is being opened for the first time since boot, it will be + * checked by this function, which means it will be fully read and the CRC + * checksum of each logical eraseblock will be checked. + * + * This function returns volume descriptor in case of success and a negative + * error code in case of failure. + */ +struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) +{ + int err; + struct ubi_volume_desc *desc; + struct ubi_device *ubi; + struct ubi_volume *vol; + + dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return ERR_PTR(-EINVAL); + + if (mode != UBI_READONLY && mode != UBI_READWRITE && + mode != UBI_EXCLUSIVE) + return ERR_PTR(-EINVAL); + + /* + * First of all, we have to get the UBI device to prevent its removal. + */ + ubi = ubi_get_device(ubi_num); + if (!ubi) + return ERR_PTR(-ENODEV); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) { + err = -EINVAL; + goto out_put_ubi; + } + + desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); + if (!desc) { + err = -ENOMEM; + goto out_put_ubi; + } + + err = -ENODEV; + if (!try_module_get(THIS_MODULE)) + goto out_free; + + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[vol_id]; + if (!vol) + goto out_unlock; + + err = -EBUSY; + switch (mode) { + case UBI_READONLY: + if (vol->exclusive) + goto out_unlock; + vol->readers += 1; + break; + + case UBI_READWRITE: + if (vol->exclusive || vol->writers > 0) + goto out_unlock; + vol->writers += 1; + break; + + case UBI_EXCLUSIVE: + if (vol->exclusive || vol->writers || vol->readers) + goto out_unlock; + vol->exclusive = 1; + break; + } + get_device(&vol->dev); + vol->ref_count += 1; + spin_unlock(&ubi->volumes_lock); + + desc->vol = vol; + desc->mode = mode; + + mutex_lock(&ubi->ckvol_mutex); + if (!vol->checked) { + /* This is the first open - check the volume */ + err = ubi_check_volume(ubi, vol_id); + if (err < 0) { + mutex_unlock(&ubi->ckvol_mutex); + ubi_close_volume(desc); + return ERR_PTR(err); + } + if (err == 1) { + ubi_warn("volume %d on UBI device %d is corrupted", + vol_id, ubi->ubi_num); + vol->corrupted = 1; + } + vol->checked = 1; + } + mutex_unlock(&ubi->ckvol_mutex); + + return desc; + +out_unlock: + spin_unlock(&ubi->volumes_lock); + module_put(THIS_MODULE); +out_free: + kfree(desc); +out_put_ubi: + ubi_put_device(ubi); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(ubi_open_volume); + +/** + * ubi_open_volume_nm - open UBI volume by name. + * @ubi_num: UBI device number + * @name: volume name + * @mode: open mode + * + * This function is similar to 'ubi_open_volume()', but opens a volume by name. + */ +struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, + int mode) +{ + int i, vol_id = -1, len; + struct ubi_device *ubi; + struct ubi_volume_desc *ret; + + dbg_msg("open volume %s, mode %d", name, mode); + + if (!name) + return ERR_PTR(-EINVAL); + + len = strnlen(name, UBI_VOL_NAME_MAX + 1); + if (len > UBI_VOL_NAME_MAX) + return ERR_PTR(-EINVAL); + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return ERR_PTR(-EINVAL); + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return ERR_PTR(-ENODEV); + + spin_lock(&ubi->volumes_lock); + /* Walk all volumes of this UBI device */ + for (i = 0; i < ubi->vtbl_slots; i++) { + struct ubi_volume *vol = ubi->volumes[i]; + + if (vol && len == vol->name_len && !strcmp(name, vol->name)) { + vol_id = i; + break; + } + } + spin_unlock(&ubi->volumes_lock); + + if (vol_id >= 0) + ret = ubi_open_volume(ubi_num, vol_id, mode); + else + ret = ERR_PTR(-ENODEV); + + /* + * We should put the UBI device even in case of success, because + * 'ubi_open_volume()' took a reference as well. + */ + ubi_put_device(ubi); + return ret; +} +EXPORT_SYMBOL_GPL(ubi_open_volume_nm); + +/** + * ubi_close_volume - close UBI volume. + * @desc: volume descriptor + */ +void ubi_close_volume(struct ubi_volume_desc *desc) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + + dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); + + spin_lock(&ubi->volumes_lock); + switch (desc->mode) { + case UBI_READONLY: + vol->readers -= 1; + break; + case UBI_READWRITE: + vol->writers -= 1; + break; + case UBI_EXCLUSIVE: + vol->exclusive = 0; + } + vol->ref_count -= 1; + spin_unlock(&ubi->volumes_lock); + + kfree(desc); + put_device(&vol->dev); + ubi_put_device(ubi); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL_GPL(ubi_close_volume); + +/** + * ubi_leb_read - read data. + * @desc: volume descriptor + * @lnum: logical eraseblock number to read from + * @buf: buffer where to store the read data + * @offset: offset within the logical eraseblock to read from + * @len: how many bytes to read + * @check: whether UBI has to check the read data's CRC or not. + * + * This function reads data from offset @offset of logical eraseblock @lnum and + * stores the data at @buf. When reading from static volumes, @check specifies + * whether the data has to be checked or not. If yes, the whole logical + * eraseblock will be read and its CRC checksum will be checked (i.e., the CRC + * checksum is per-eraseblock). So checking may substantially slow down the + * read speed. The @check argument is ignored for dynamic volumes. + * + * In case of success, this function returns zero. In case of failure, this + * function returns a negative error code. + * + * %-EBADMSG error code is returned: + * o for both static and dynamic volumes if MTD driver has detected a data + * integrity problem (unrecoverable ECC checksum mismatch in case of NAND); + * o for static volumes in case of data CRC mismatch. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF error code. + */ +int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, + int len, int check) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err, vol_id = vol->vol_id; + + dbg_msg("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 || + lnum >= vol->used_ebs || offset < 0 || len < 0 || + offset + len > vol->usable_leb_size) + return -EINVAL; + + if (vol->vol_type == UBI_STATIC_VOLUME) { + if (vol->used_ebs == 0) + /* Empty static UBI volume */ + return 0; + if (lnum == vol->used_ebs - 1 && + offset + len > vol->last_eb_bytes) + return -EINVAL; + } + + if (vol->upd_marker) + return -EBADF; + if (len == 0) + return 0; + + err = ubi_eba_read_leb(ubi, vol, lnum, buf, offset, len, check); + if (err && mtd_is_eccerr(err) && vol->vol_type == UBI_STATIC_VOLUME) { + ubi_warn("mark volume %d as corrupted", vol_id); + vol->corrupted = 1; + } + + return err; +} +EXPORT_SYMBOL_GPL(ubi_leb_read); + +/** + * ubi_leb_write - write data. + * @desc: volume descriptor + * @lnum: logical eraseblock number to write to + * @buf: data to write + * @offset: offset within the logical eraseblock where to write + * @len: how many bytes to write + * @dtype: expected data type + * + * This function writes @len bytes of data from @buf to offset @offset of + * logical eraseblock @lnum. The @dtype argument describes expected lifetime of + * the data. + * + * This function takes care of physical eraseblock write failures. If write to + * the physical eraseblock write operation fails, the logical eraseblock is + * re-mapped to another physical eraseblock, the data is recovered, and the + * write finishes. UBI has a pool of reserved physical eraseblocks for this. + * + * If all the data were successfully written, zero is returned. If an error + * occurred and UBI has not been able to recover from it, this function returns + * a negative error code. Note, in case of an error, it is possible that + * something was still written to the flash media, but that may be some + * garbage. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF code. + */ +int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, + int offset, int len, int dtype) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + dbg_msg("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + return -EINVAL; + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 || + offset + len > vol->usable_leb_size || + offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1)) + return -EINVAL; + + if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && + dtype != UBI_UNKNOWN) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (len == 0) + return 0; + + return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len, dtype); +} +EXPORT_SYMBOL_GPL(ubi_leb_write); + +/* + * ubi_leb_change - change logical eraseblock atomically. + * @desc: volume descriptor + * @lnum: logical eraseblock number to change + * @buf: data to write + * @len: how many bytes to write + * @dtype: expected data type + * + * This function changes the contents of a logical eraseblock atomically. @buf + * has to contain new logical eraseblock data, and @len - the length of the + * data, which has to be aligned. The length may be shorter then the logical + * eraseblock size, ant the logical eraseblock may be appended to more times + * later on. This function guarantees that in case of an unclean reboot the old + * contents is preserved. Returns zero in case of success and a negative error + * code in case of failure. + */ +int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, + int len, int dtype) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + return -EINVAL; + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 || + len > vol->usable_leb_size || len & (ubi->min_io_size - 1)) + return -EINVAL; + + if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && + dtype != UBI_UNKNOWN) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (len == 0) + return 0; + + return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len, dtype); +} +EXPORT_SYMBOL_GPL(ubi_leb_change); + +/** + * ubi_leb_erase - erase logical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and synchronously erases the + * correspondent physical eraseblock. Returns zero in case of success and a + * negative error code in case of failure. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF code. + */ +int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err; + + dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + err = ubi_eba_unmap_leb(ubi, vol, lnum); + if (err) + return err; + + return ubi_wl_flush(ubi); +} +EXPORT_SYMBOL_GPL(ubi_leb_erase); + +/** + * ubi_leb_unmap - un-map logical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and schedules the + * corresponding physical eraseblock for erasure, so that it will eventually be + * physically erased in background. This operation is much faster then the + * erase operation. + * + * Unlike erase, the un-map operation does not guarantee that the logical + * eraseblock will contain all 0xFF bytes when UBI is initialized again. For + * example, if several logical eraseblocks are un-mapped, and an unclean reboot + * happens after this, the logical eraseblocks will not necessarily be + * un-mapped again when this MTD device is attached. They may actually be + * mapped to the same physical eraseblocks again. So, this function has to be + * used with care. + * + * In other words, when un-mapping a logical eraseblock, UBI does not store + * any information about this on the flash media, it just marks the logical + * eraseblock as "un-mapped" in RAM. If UBI is detached before the physical + * eraseblock is physically erased, it will be mapped again to the same logical + * eraseblock when the MTD device is attached again. + * + * The main and obvious use-case of this function is when the contents of a + * logical eraseblock has to be re-written. Then it is much more efficient to + * first un-map it, then write new data, rather then first erase it, then write + * new data. Note, once new data has been written to the logical eraseblock, + * UBI guarantees that the old contents has gone forever. In other words, if an + * unclean reboot happens after the logical eraseblock has been un-mapped and + * then written to, it will contain the last written data. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If the volume is damaged because of an interrupted update + * this function just returns immediately with %-EBADF code. + */ +int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + + dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + return ubi_eba_unmap_leb(ubi, vol, lnum); +} +EXPORT_SYMBOL_GPL(ubi_leb_unmap); + +/** + * ubi_leb_map - map logical erasblock to a physical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * @dtype: expected data type + * + * This function maps an un-mapped logical eraseblock @lnum to a physical + * eraseblock. This means, that after a successfull invocation of this + * function the logical eraseblock @lnum will be empty (contain only %0xFF + * bytes) and be mapped to a physical eraseblock, even if an unclean reboot + * happens. + * + * This function returns zero in case of success, %-EBADF if the volume is + * damaged because of an interrupted update, %-EBADMSG if the logical + * eraseblock is already mapped, and other negative error codes in case of + * other failures. + */ +int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + + dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && + dtype != UBI_UNKNOWN) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (vol->eba_tbl[lnum] >= 0) + return -EBADMSG; + + return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); +} +EXPORT_SYMBOL_GPL(ubi_leb_map); + +/** + * ubi_is_mapped - check if logical eraseblock is mapped. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function checks if logical eraseblock @lnum is mapped to a physical + * eraseblock. If a logical eraseblock is un-mapped, this does not necessarily + * mean it will still be un-mapped after the UBI device is re-attached. The + * logical eraseblock may become mapped to the physical eraseblock it was last + * mapped to. + * + * This function returns %1 if the LEB is mapped, %0 if not, and a negative + * error code in case of failure. If the volume is damaged because of an + * interrupted update this function just returns immediately with %-EBADF error + * code. + */ +int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + + dbg_msg("test LEB %d:%d", vol->vol_id, lnum); + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + return vol->eba_tbl[lnum] >= 0; +} +EXPORT_SYMBOL_GPL(ubi_is_mapped); diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/misc.c b/qemu/roms/u-boot/drivers/mtd/ubi/misc.c new file mode 100644 index 000000000..5ff55b4f7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/misc.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* Here we keep miscellaneous functions which are used all over the UBI code */ + +#include +#include "ubi.h" + +/** + * calc_data_len - calculate how much real data is stored in a buffer. + * @ubi: UBI device description object + * @buf: a buffer with the contents of the physical eraseblock + * @length: the buffer length + * + * This function calculates how much "real data" is stored in @buf and returnes + * the length. Continuous 0xFF bytes at the end of the buffer are not + * considered as "real data". + */ +int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, + int length) +{ + int i; + + ubi_assert(!(length & (ubi->min_io_size - 1))); + + for (i = length - 1; i >= 0; i--) + if (((const uint8_t *)buf)[i] != 0xFF) + break; + + /* The resulting length must be aligned to the minimum flash I/O size */ + length = ALIGN(i + 1, ubi->min_io_size); + return length; +} + +/** + * ubi_check_volume - check the contents of a static volume. + * @ubi: UBI device description object + * @vol_id: ID of the volume to check + * + * This function checks if static volume @vol_id is corrupted by fully reading + * it and checking data CRC. This function returns %0 if the volume is not + * corrupted, %1 if it is corrupted and a negative error code in case of + * failure. Dynamic volumes are not checked and zero is returned immediately. + */ +int ubi_check_volume(struct ubi_device *ubi, int vol_id) +{ + void *buf; + int err = 0, i; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + if (vol->vol_type != UBI_STATIC_VOLUME) + return 0; + + buf = vmalloc(vol->usable_leb_size); + if (!buf) + return -ENOMEM; + + for (i = 0; i < vol->used_ebs; i++) { + int size; + + if (i == vol->used_ebs - 1) + size = vol->last_eb_bytes; + else + size = vol->usable_leb_size; + + err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1); + if (err) { + if (mtd_is_eccerr(err)) + err = 1; + break; + } + } + + vfree(buf); + return err; +} + +/** + * ubi_calculate_rsvd_pool - calculate how many PEBs must be reserved for bad + * eraseblock handling. + * @ubi: UBI device description object + */ +void ubi_calculate_reserved(struct ubi_device *ubi) +{ + ubi->beb_rsvd_level = ubi->good_peb_count/100; + ubi->beb_rsvd_level *= CONFIG_MTD_UBI_BEB_RESERVE; + if (ubi->beb_rsvd_level < MIN_RESEVED_PEBS) + ubi->beb_rsvd_level = MIN_RESEVED_PEBS; +} diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/scan.c b/qemu/roms/u-boot/drivers/mtd/ubi/scan.c new file mode 100644 index 000000000..a6d0fbcbe --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/scan.c @@ -0,0 +1,1348 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * UBI scanning unit. + * + * This unit is responsible for scanning the flash media, checking UBI + * headers and providing complete information about the UBI flash image. + * + * The scanning information is represented by a &struct ubi_scan_info' object. + * Information about found volumes is represented by &struct ubi_scan_volume + * objects which are kept in volume RB-tree with root at the @volumes field. + * The RB-tree is indexed by the volume ID. + * + * Found logical eraseblocks are represented by &struct ubi_scan_leb objects. + * These objects are kept in per-volume RB-trees with the root at the + * corresponding &struct ubi_scan_volume object. To put it differently, we keep + * an RB-tree of per-volume objects and each of these objects is the root of + * RB-tree of per-eraseblock objects. + * + * Corrupted physical eraseblocks are put to the @corr list, free physical + * eraseblocks are put to the @free list and the physical eraseblock to be + * erased are put to the @erase list. + */ + +#ifdef UBI_LINUX +#include +#include +#include +#endif + +#include +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si); +#else +#define paranoid_check_si(ubi, si) 0 +#endif + +/* Temporary variables used during scanning */ +static struct ubi_ec_hdr *ech; +static struct ubi_vid_hdr *vidh; + +/** + * add_to_list - add physical eraseblock to a list. + * @si: scanning information + * @pnum: physical eraseblock number to add + * @ec: erase counter of the physical eraseblock + * @list: the list to add to + * + * This function adds physical eraseblock @pnum to free, erase, corrupted or + * alien lists. Returns zero in case of success and a negative error code in + * case of failure. + */ +static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, + struct list_head *list) +{ + struct ubi_scan_leb *seb; + + if (list == &si->free) + dbg_bld("add to free: PEB %d, EC %d", pnum, ec); + else if (list == &si->erase) + dbg_bld("add to erase: PEB %d, EC %d", pnum, ec); + else if (list == &si->corr) + dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec); + else if (list == &si->alien) + dbg_bld("add to alien: PEB %d, EC %d", pnum, ec); + else + BUG(); + + seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL); + if (!seb) + return -ENOMEM; + + seb->pnum = pnum; + seb->ec = ec; + list_add_tail(&seb->u.list, list); + return 0; +} + +/** + * validate_vid_hdr - check that volume identifier header is correct and + * consistent. + * @vid_hdr: the volume identifier header to check + * @sv: information about the volume this logical eraseblock belongs to + * @pnum: physical eraseblock number the VID header came from + * + * This function checks that data stored in @vid_hdr is consistent. Returns + * non-zero if an inconsistency was found and zero if not. + * + * Note, UBI does sanity check of everything it reads from the flash media. + * Most of the checks are done in the I/O unit. Here we check that the + * information in the VID header is consistent to the information in other VID + * headers of the same volume. + */ +static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr, + const struct ubi_scan_volume *sv, int pnum) +{ + int vol_type = vid_hdr->vol_type; + int vol_id = be32_to_cpu(vid_hdr->vol_id); + int used_ebs = be32_to_cpu(vid_hdr->used_ebs); + int data_pad = be32_to_cpu(vid_hdr->data_pad); + + if (sv->leb_count != 0) { + int sv_vol_type; + + /* + * This is not the first logical eraseblock belonging to this + * volume. Ensure that the data in its VID header is consistent + * to the data in previous logical eraseblock headers. + */ + + if (vol_id != sv->vol_id) { + dbg_err("inconsistent vol_id"); + goto bad; + } + + if (sv->vol_type == UBI_STATIC_VOLUME) + sv_vol_type = UBI_VID_STATIC; + else + sv_vol_type = UBI_VID_DYNAMIC; + + if (vol_type != sv_vol_type) { + dbg_err("inconsistent vol_type"); + goto bad; + } + + if (used_ebs != sv->used_ebs) { + dbg_err("inconsistent used_ebs"); + goto bad; + } + + if (data_pad != sv->data_pad) { + dbg_err("inconsistent data_pad"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("inconsistent VID header at PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_sv(sv); + return -EINVAL; +} + +/** + * add_volume - add volume to the scanning information. + * @si: scanning information + * @vol_id: ID of the volume to add + * @pnum: physical eraseblock number + * @vid_hdr: volume identifier header + * + * If the volume corresponding to the @vid_hdr logical eraseblock is already + * present in the scanning information, this function does nothing. Otherwise + * it adds corresponding volume to the scanning information. Returns a pointer + * to the scanning volume object in case of success and a negative error code + * in case of failure. + */ +static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id, + int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + struct ubi_scan_volume *sv; + struct rb_node **p = &si->volumes.rb_node, *parent = NULL; + + ubi_assert(vol_id == be32_to_cpu(vid_hdr->vol_id)); + + /* Walk the volume RB-tree to look if this volume is already present */ + while (*p) { + parent = *p; + sv = rb_entry(parent, struct ubi_scan_volume, rb); + + if (vol_id == sv->vol_id) + return sv; + + if (vol_id > sv->vol_id) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + /* The volume is absent - add it */ + sv = kmalloc(sizeof(struct ubi_scan_volume), GFP_KERNEL); + if (!sv) + return ERR_PTR(-ENOMEM); + + sv->highest_lnum = sv->leb_count = 0; + sv->vol_id = vol_id; + sv->root = RB_ROOT; + sv->used_ebs = be32_to_cpu(vid_hdr->used_ebs); + sv->data_pad = be32_to_cpu(vid_hdr->data_pad); + sv->compat = vid_hdr->compat; + sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME + : UBI_STATIC_VOLUME; + if (vol_id > si->highest_vol_id) + si->highest_vol_id = vol_id; + + rb_link_node(&sv->rb, parent, p); + rb_insert_color(&sv->rb, &si->volumes); + si->vols_found += 1; + dbg_bld("added volume %d", vol_id); + return sv; +} + +/** + * compare_lebs - find out which logical eraseblock is newer. + * @ubi: UBI device description object + * @seb: first logical eraseblock to compare + * @pnum: physical eraseblock number of the second logical eraseblock to + * compare + * @vid_hdr: volume identifier header of the second logical eraseblock + * + * This function compares 2 copies of a LEB and informs which one is newer. In + * case of success this function returns a positive value, in case of failure, a + * negative error code is returned. The success return codes use the following + * bits: + * o bit 0 is cleared: the first PEB (described by @seb) is newer then the + * second PEB (described by @pnum and @vid_hdr); + * o bit 0 is set: the second PEB is newer; + * o bit 1 is cleared: no bit-flips were detected in the newer LEB; + * o bit 1 is set: bit-flips were detected in the newer LEB; + * o bit 2 is cleared: the older LEB is not corrupted; + * o bit 2 is set: the older LEB is corrupted. + */ +static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, + int pnum, const struct ubi_vid_hdr *vid_hdr) +{ + void *buf; + int len, err, second_is_newer, bitflips = 0, corrupted = 0; + uint32_t data_crc, crc; + struct ubi_vid_hdr *vh = NULL; + unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum); + + if (seb->sqnum == 0 && sqnum2 == 0) { + long long abs, v1 = seb->leb_ver, v2 = be32_to_cpu(vid_hdr->leb_ver); + + /* + * UBI constantly increases the logical eraseblock version + * number and it can overflow. Thus, we have to bear in mind + * that versions that are close to %0xFFFFFFFF are less then + * versions that are close to %0. + * + * The UBI WL unit guarantees that the number of pending tasks + * is not greater then %0x7FFFFFFF. So, if the difference + * between any two versions is greater or equivalent to + * %0x7FFFFFFF, there was an overflow and the logical + * eraseblock with lower version is actually newer then the one + * with higher version. + * + * FIXME: but this is anyway obsolete and will be removed at + * some point. + */ + dbg_bld("using old crappy leb_ver stuff"); + + if (v1 == v2) { + ubi_err("PEB %d and PEB %d have the same version %lld", + seb->pnum, pnum, v1); + return -EINVAL; + } + + abs = v1 - v2; + if (abs < 0) + abs = -abs; + + if (abs < 0x7FFFFFFF) + /* Non-overflow situation */ + second_is_newer = (v2 > v1); + else + second_is_newer = (v2 < v1); + } else + /* Obviously the LEB with lower sequence counter is older */ + second_is_newer = sqnum2 > seb->sqnum; + + /* + * Now we know which copy is newer. If the copy flag of the PEB with + * newer version is not set, then we just return, otherwise we have to + * check data CRC. For the second PEB we already have the VID header, + * for the first one - we'll need to re-read it from flash. + * + * FIXME: this may be optimized so that we wouldn't read twice. + */ + + if (second_is_newer) { + if (!vid_hdr->copy_flag) { + /* It is not a copy, so it is newer */ + dbg_bld("second PEB %d is newer, copy_flag is unset", + pnum); + return 1; + } + } else { + pnum = seb->pnum; + + vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + if (!vh) + return -ENOMEM; + + err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); + if (err) { + if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else { + dbg_err("VID of PEB %d header is bad, but it " + "was OK earlier", pnum); + if (err > 0) + err = -EIO; + + goto out_free_vidh; + } + } + + if (!vh->copy_flag) { + /* It is not a copy, so it is newer */ + dbg_bld("first PEB %d is newer, copy_flag is unset", + pnum); + err = bitflips << 1; + goto out_free_vidh; + } + + vid_hdr = vh; + } + + /* Read the data of the copy and check the CRC */ + + len = be32_to_cpu(vid_hdr->data_size); + buf = vmalloc(len); + if (!buf) { + err = -ENOMEM; + goto out_free_vidh; + } + + err = ubi_io_read_data(ubi, buf, pnum, 0, len); + if (err && err != UBI_IO_BITFLIPS) + goto out_free_buf; + + data_crc = be32_to_cpu(vid_hdr->data_crc); + crc = crc32(UBI_CRC32_INIT, buf, len); + if (crc != data_crc) { + dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x", + pnum, crc, data_crc); + corrupted = 1; + bitflips = 0; + second_is_newer = !second_is_newer; + } else { + dbg_bld("PEB %d CRC is OK", pnum); + bitflips = !!err; + } + + vfree(buf); + ubi_free_vid_hdr(ubi, vh); + + if (second_is_newer) + dbg_bld("second PEB %d is newer, copy_flag is set", pnum); + else + dbg_bld("first PEB %d is newer, copy_flag is set", pnum); + + return second_is_newer | (bitflips << 1) | (corrupted << 2); + +out_free_buf: + vfree(buf); +out_free_vidh: + ubi_free_vid_hdr(ubi, vh); + return err; +} + +/** + * ubi_scan_add_used - add information about a physical eraseblock to the + * scanning information. + * @ubi: UBI device description object + * @si: scanning information + * @pnum: the physical eraseblock number + * @ec: erase counter + * @vid_hdr: the volume identifier header + * @bitflips: if bit-flips were detected when this physical eraseblock was read + * + * This function adds information about a used physical eraseblock to the + * 'used' tree of the corresponding volume. The function is rather complex + * because it has to handle cases when this is not the first physical + * eraseblock belonging to the same logical eraseblock, and the newer one has + * to be picked, while the older one has to be dropped. This function returns + * zero in case of success and a negative error code in case of failure. + */ +int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, + int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, + int bitflips) +{ + int err, vol_id, lnum; + uint32_t leb_ver; + unsigned long long sqnum; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb; + struct rb_node **p, *parent = NULL; + + vol_id = be32_to_cpu(vid_hdr->vol_id); + lnum = be32_to_cpu(vid_hdr->lnum); + sqnum = be64_to_cpu(vid_hdr->sqnum); + leb_ver = be32_to_cpu(vid_hdr->leb_ver); + + dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d", + pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips); + + sv = add_volume(si, vol_id, pnum, vid_hdr); + if (IS_ERR(sv) < 0) + return PTR_ERR(sv); + + if (si->max_sqnum < sqnum) + si->max_sqnum = sqnum; + + /* + * Walk the RB-tree of logical eraseblocks of volume @vol_id to look + * if this is the first instance of this logical eraseblock or not. + */ + p = &sv->root.rb_node; + while (*p) { + int cmp_res; + + parent = *p; + seb = rb_entry(parent, struct ubi_scan_leb, u.rb); + if (lnum != seb->lnum) { + if (lnum < seb->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + continue; + } + + /* + * There is already a physical eraseblock describing the same + * logical eraseblock present. + */ + + dbg_bld("this LEB already exists: PEB %d, sqnum %llu, " + "LEB ver %u, EC %d", seb->pnum, seb->sqnum, + seb->leb_ver, seb->ec); + + /* + * Make sure that the logical eraseblocks have different + * versions. Otherwise the image is bad. + */ + if (seb->leb_ver == leb_ver && leb_ver != 0) { + ubi_err("two LEBs with same version %u", leb_ver); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_vid_hdr(vid_hdr); + return -EINVAL; + } + + /* + * Make sure that the logical eraseblocks have different + * sequence numbers. Otherwise the image is bad. + * + * FIXME: remove 'sqnum != 0' check when leb_ver is removed. + */ + if (seb->sqnum == sqnum && sqnum != 0) { + ubi_err("two LEBs with same sequence number %llu", + sqnum); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_vid_hdr(vid_hdr); + return -EINVAL; + } + + /* + * Now we have to drop the older one and preserve the newer + * one. + */ + cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr); + if (cmp_res < 0) + return cmp_res; + + if (cmp_res & 1) { + /* + * This logical eraseblock is newer then the one + * found earlier. + */ + err = validate_vid_hdr(vid_hdr, sv, pnum); + if (err) + return err; + + if (cmp_res & 4) + err = add_to_list(si, seb->pnum, seb->ec, + &si->corr); + else + err = add_to_list(si, seb->pnum, seb->ec, + &si->erase); + if (err) + return err; + + seb->ec = ec; + seb->pnum = pnum; + seb->scrub = ((cmp_res & 2) || bitflips); + seb->sqnum = sqnum; + seb->leb_ver = leb_ver; + + if (sv->highest_lnum == lnum) + sv->last_data_size = + be32_to_cpu(vid_hdr->data_size); + + return 0; + } else { + /* + * This logical eraseblock is older then the one found + * previously. + */ + if (cmp_res & 4) + return add_to_list(si, pnum, ec, &si->corr); + else + return add_to_list(si, pnum, ec, &si->erase); + } + } + + /* + * We've met this logical eraseblock for the first time, add it to the + * scanning information. + */ + + err = validate_vid_hdr(vid_hdr, sv, pnum); + if (err) + return err; + + seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL); + if (!seb) + return -ENOMEM; + + seb->ec = ec; + seb->pnum = pnum; + seb->lnum = lnum; + seb->sqnum = sqnum; + seb->scrub = bitflips; + seb->leb_ver = leb_ver; + + if (sv->highest_lnum <= lnum) { + sv->highest_lnum = lnum; + sv->last_data_size = be32_to_cpu(vid_hdr->data_size); + } + + sv->leb_count += 1; + rb_link_node(&seb->u.rb, parent, p); + rb_insert_color(&seb->u.rb, &sv->root); + return 0; +} + +/** + * ubi_scan_find_sv - find information about a particular volume in the + * scanning information. + * @si: scanning information + * @vol_id: the requested volume ID + * + * This function returns a pointer to the volume description or %NULL if there + * are no data about this volume in the scanning information. + */ +struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si, + int vol_id) +{ + struct ubi_scan_volume *sv; + struct rb_node *p = si->volumes.rb_node; + + while (p) { + sv = rb_entry(p, struct ubi_scan_volume, rb); + + if (vol_id == sv->vol_id) + return sv; + + if (vol_id > sv->vol_id) + p = p->rb_left; + else + p = p->rb_right; + } + + return NULL; +} + +/** + * ubi_scan_find_seb - find information about a particular logical + * eraseblock in the volume scanning information. + * @sv: a pointer to the volume scanning information + * @lnum: the requested logical eraseblock + * + * This function returns a pointer to the scanning logical eraseblock or %NULL + * if there are no data about it in the scanning volume information. + */ +struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv, + int lnum) +{ + struct ubi_scan_leb *seb; + struct rb_node *p = sv->root.rb_node; + + while (p) { + seb = rb_entry(p, struct ubi_scan_leb, u.rb); + + if (lnum == seb->lnum) + return seb; + + if (lnum > seb->lnum) + p = p->rb_left; + else + p = p->rb_right; + } + + return NULL; +} + +/** + * ubi_scan_rm_volume - delete scanning information about a volume. + * @si: scanning information + * @sv: the volume scanning information to delete + */ +void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv) +{ + struct rb_node *rb; + struct ubi_scan_leb *seb; + + dbg_bld("remove scanning information about volume %d", sv->vol_id); + + while ((rb = rb_first(&sv->root))) { + seb = rb_entry(rb, struct ubi_scan_leb, u.rb); + rb_erase(&seb->u.rb, &sv->root); + list_add_tail(&seb->u.list, &si->erase); + } + + rb_erase(&sv->rb, &si->volumes); + kfree(sv); + si->vols_found -= 1; +} + +/** + * ubi_scan_erase_peb - erase a physical eraseblock. + * @ubi: UBI device description object + * @si: scanning information + * @pnum: physical eraseblock number to erase; + * @ec: erase counter value to write (%UBI_SCAN_UNKNOWN_EC if it is unknown) + * + * This function erases physical eraseblock 'pnum', and writes the erase + * counter header to it. This function should only be used on UBI device + * initialization stages, when the EBA unit had not been yet initialized. This + * function returns zero in case of success and a negative error code in case + * of failure. + */ +int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si, + int pnum, int ec) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + + if ((long long)ec >= UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. Upgrade UBI and use 64-bit + * erase counters internally. + */ + ubi_err("erase counter overflow at PEB %d, EC %d", pnum, ec); + return -EINVAL; + } + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + ec_hdr->ec = cpu_to_be64(ec); + + err = ubi_io_sync_erase(ubi, pnum, 0); + if (err < 0) + goto out_free; + + err = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr); + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * ubi_scan_get_free_peb - get a free physical eraseblock. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns a free physical eraseblock. It is supposed to be + * called on the UBI initialization stages when the wear-leveling unit is not + * initialized yet. This function picks a physical eraseblocks from one of the + * lists, writes the EC header if it is needed, and removes it from the list. + * + * This function returns scanning physical eraseblock information in case of + * success and an error code in case of failure. + */ +struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int err = 0, i; + struct ubi_scan_leb *seb; + + if (!list_empty(&si->free)) { + seb = list_entry(si->free.next, struct ubi_scan_leb, u.list); + list_del(&seb->u.list); + dbg_bld("return free PEB %d, EC %d", seb->pnum, seb->ec); + return seb; + } + + for (i = 0; i < 2; i++) { + struct list_head *head; + struct ubi_scan_leb *tmp_seb; + + if (i == 0) + head = &si->erase; + else + head = &si->corr; + + /* + * We try to erase the first physical eraseblock from the @head + * list and pick it if we succeed, or try to erase the + * next one if not. And so forth. We don't want to take care + * about bad eraseblocks here - they'll be handled later. + */ + list_for_each_entry_safe(seb, tmp_seb, head, u.list) { + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + + err = ubi_scan_erase_peb(ubi, si, seb->pnum, seb->ec+1); + if (err) + continue; + + seb->ec += 1; + list_del(&seb->u.list); + dbg_bld("return PEB %d, EC %d", seb->pnum, seb->ec); + return seb; + } + } + + ubi_err("no eraseblocks found"); + return ERR_PTR(-ENOSPC); +} + +/** + * process_eb - read UBI headers, check them and add corresponding data + * to the scanning information. + * @ubi: UBI device description object + * @si: scanning information + * @pnum: the physical eraseblock number + * + * This function returns a zero if the physical eraseblock was successfully + * handled and a negative error code in case of failure. + */ +static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) +{ + long long uninitialized_var(ec); + int err, bitflips = 0, vol_id, ec_corr = 0; + + dbg_bld("scan PEB %d", pnum); + + /* Skip bad physical eraseblocks */ + err = ubi_io_is_bad(ubi, pnum); + if (err < 0) + return err; + else if (err) { + /* + * FIXME: this is actually duty of the I/O unit to initialize + * this, but MTD does not provide enough information. + */ + si->bad_peb_count += 1; + return 0; + } + + err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); + if (err < 0) + return err; + else if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else if (err == UBI_IO_PEB_EMPTY) + return add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, &si->erase); + else if (err == UBI_IO_BAD_EC_HDR) { + /* + * We have to also look at the VID header, possibly it is not + * corrupted. Set %bitflips flag in order to make this PEB be + * moved and EC be re-created. + */ + ec_corr = 1; + ec = UBI_SCAN_UNKNOWN_EC; + bitflips = 1; + } + + si->is_empty = 0; + + if (!ec_corr) { + /* Make sure UBI version is OK */ + if (ech->version != UBI_VERSION) { + ubi_err("this UBI version is %d, image version is %d", + UBI_VERSION, (int)ech->version); + return -EINVAL; + } + + ec = be64_to_cpu(ech->ec); + if (ec > UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. The EC headers have 64 bits + * reserved, but we anyway make use of only 31 bit + * values, as this seems to be enough for any existing + * flash. Upgrade UBI and use 64-bit erase counters + * internally. + */ + ubi_err("erase counter overflow, max is %d", + UBI_MAX_ERASECOUNTER); + ubi_dbg_dump_ec_hdr(ech); + return -EINVAL; + } + } + + /* OK, we've done with the EC header, let's look at the VID header */ + + err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0); + if (err < 0) + return err; + else if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else if (err == UBI_IO_BAD_VID_HDR || + (err == UBI_IO_PEB_FREE && ec_corr)) { + /* VID header is corrupted */ + err = add_to_list(si, pnum, ec, &si->corr); + if (err) + return err; + goto adjust_mean_ec; + } else if (err == UBI_IO_PEB_FREE) { + /* No VID header - the physical eraseblock is free */ + err = add_to_list(si, pnum, ec, &si->free); + if (err) + return err; + goto adjust_mean_ec; + } + + vol_id = be32_to_cpu(vidh->vol_id); + if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) { + int lnum = be32_to_cpu(vidh->lnum); + + /* Unsupported internal volume */ + switch (vidh->compat) { + case UBI_COMPAT_DELETE: + ubi_msg("\"delete\" compatible internal volume %d:%d" + " found, remove it", vol_id, lnum); + err = add_to_list(si, pnum, ec, &si->corr); + if (err) + return err; + break; + + case UBI_COMPAT_RO: + ubi_msg("read-only compatible internal volume %d:%d" + " found, switch to read-only mode", + vol_id, lnum); + ubi->ro_mode = 1; + break; + + case UBI_COMPAT_PRESERVE: + ubi_msg("\"preserve\" compatible internal volume %d:%d" + " found", vol_id, lnum); + err = add_to_list(si, pnum, ec, &si->alien); + if (err) + return err; + si->alien_peb_count += 1; + return 0; + + case UBI_COMPAT_REJECT: + ubi_err("incompatible internal volume %d:%d found", + vol_id, lnum); + return -EINVAL; + } + } + + /* Both UBI headers seem to be fine */ + err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips); + if (err) + return err; + +adjust_mean_ec: + if (!ec_corr) { + si->ec_sum += ec; + si->ec_count += 1; + if (ec > si->max_ec) + si->max_ec = ec; + if (ec < si->min_ec) + si->min_ec = ec; + } + + return 0; +} + +/** + * ubi_scan - scan an MTD device. + * @ubi: UBI device description object + * + * This function does full scanning of an MTD device and returns complete + * information about it. In case of failure, an error code is returned. + */ +struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) +{ + int err, pnum; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb; + struct ubi_scan_info *si; + + si = kzalloc(sizeof(struct ubi_scan_info), GFP_KERNEL); + if (!si) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&si->corr); + INIT_LIST_HEAD(&si->free); + INIT_LIST_HEAD(&si->erase); + INIT_LIST_HEAD(&si->alien); + si->volumes = RB_ROOT; + si->is_empty = 1; + + err = -ENOMEM; + ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ech) + goto out_si; + + vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + if (!vidh) + goto out_ech; + + for (pnum = 0; pnum < ubi->peb_count; pnum++) { + cond_resched(); + + dbg_msg("process PEB %d", pnum); + err = process_eb(ubi, si, pnum); + if (err < 0) + goto out_vidh; + } + + dbg_msg("scanning is finished"); + + /* Calculate mean erase counter */ + if (si->ec_count) { + do_div(si->ec_sum, si->ec_count); + si->mean_ec = si->ec_sum; + } + + if (si->is_empty) + ubi_msg("empty MTD device detected"); + + /* + * In case of unknown erase counter we use the mean erase counter + * value. + */ + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + } + + list_for_each_entry(seb, &si->free, u.list) { + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + } + + list_for_each_entry(seb, &si->corr, u.list) + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + + list_for_each_entry(seb, &si->erase, u.list) + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + + err = paranoid_check_si(ubi, si); + if (err) { + if (err > 0) + err = -EINVAL; + goto out_vidh; + } + + ubi_free_vid_hdr(ubi, vidh); + kfree(ech); + + return si; + +out_vidh: + ubi_free_vid_hdr(ubi, vidh); +out_ech: + kfree(ech); +out_si: + ubi_scan_destroy_si(si); + return ERR_PTR(err); +} + +/** + * destroy_sv - free the scanning volume information + * @sv: scanning volume information + * + * This function destroys the volume RB-tree (@sv->root) and the scanning + * volume information. + */ +static void destroy_sv(struct ubi_scan_volume *sv) +{ + struct ubi_scan_leb *seb; + struct rb_node *this = sv->root.rb_node; + + while (this) { + if (this->rb_left) + this = this->rb_left; + else if (this->rb_right) + this = this->rb_right; + else { + seb = rb_entry(this, struct ubi_scan_leb, u.rb); + this = rb_parent(this); + if (this) { + if (this->rb_left == &seb->u.rb) + this->rb_left = NULL; + else + this->rb_right = NULL; + } + + kfree(seb); + } + } + kfree(sv); +} + +/** + * ubi_scan_destroy_si - destroy scanning information. + * @si: scanning information + */ +void ubi_scan_destroy_si(struct ubi_scan_info *si) +{ + struct ubi_scan_leb *seb, *seb_tmp; + struct ubi_scan_volume *sv; + struct rb_node *rb; + + list_for_each_entry_safe(seb, seb_tmp, &si->alien, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->erase, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->corr, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->free, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + + /* Destroy the volume RB-tree */ + rb = si->volumes.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + sv = rb_entry(rb, struct ubi_scan_volume, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &sv->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + destroy_sv(sv); + } + } + + kfree(si); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_si - check if the scanning information is correct and + * consistent. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero if the scanning information is all right, %1 if + * not and a negative error code if an error occurred. + */ +static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int pnum, err, vols_found = 0; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb, *last_seb; + uint8_t *buf; + + /* + * At first, check that scanning information is OK. + */ + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + int leb_count = 0; + + cond_resched(); + + vols_found += 1; + + if (si->is_empty) { + ubi_err("bad is_empty flag"); + goto bad_sv; + } + + if (sv->vol_id < 0 || sv->highest_lnum < 0 || + sv->leb_count < 0 || sv->vol_type < 0 || sv->used_ebs < 0 || + sv->data_pad < 0 || sv->last_data_size < 0) { + ubi_err("negative values"); + goto bad_sv; + } + + if (sv->vol_id >= UBI_MAX_VOLUMES && + sv->vol_id < UBI_INTERNAL_VOL_START) { + ubi_err("bad vol_id"); + goto bad_sv; + } + + if (sv->vol_id > si->highest_vol_id) { + ubi_err("highest_vol_id is %d, but vol_id %d is there", + si->highest_vol_id, sv->vol_id); + goto out; + } + + if (sv->vol_type != UBI_DYNAMIC_VOLUME && + sv->vol_type != UBI_STATIC_VOLUME) { + ubi_err("bad vol_type"); + goto bad_sv; + } + + if (sv->data_pad > ubi->leb_size / 2) { + ubi_err("bad data_pad"); + goto bad_sv; + } + + last_seb = NULL; + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + cond_resched(); + + last_seb = seb; + leb_count += 1; + + if (seb->pnum < 0 || seb->ec < 0) { + ubi_err("negative values"); + goto bad_seb; + } + + if (seb->ec < si->min_ec) { + ubi_err("bad si->min_ec (%d), %d found", + si->min_ec, seb->ec); + goto bad_seb; + } + + if (seb->ec > si->max_ec) { + ubi_err("bad si->max_ec (%d), %d found", + si->max_ec, seb->ec); + goto bad_seb; + } + + if (seb->pnum >= ubi->peb_count) { + ubi_err("too high PEB number %d, total PEBs %d", + seb->pnum, ubi->peb_count); + goto bad_seb; + } + + if (sv->vol_type == UBI_STATIC_VOLUME) { + if (seb->lnum >= sv->used_ebs) { + ubi_err("bad lnum or used_ebs"); + goto bad_seb; + } + } else { + if (sv->used_ebs != 0) { + ubi_err("non-zero used_ebs"); + goto bad_seb; + } + } + + if (seb->lnum > sv->highest_lnum) { + ubi_err("incorrect highest_lnum or lnum"); + goto bad_seb; + } + } + + if (sv->leb_count != leb_count) { + ubi_err("bad leb_count, %d objects in the tree", + leb_count); + goto bad_sv; + } + + if (!last_seb) + continue; + + seb = last_seb; + + if (seb->lnum != sv->highest_lnum) { + ubi_err("bad highest_lnum"); + goto bad_seb; + } + } + + if (vols_found != si->vols_found) { + ubi_err("bad si->vols_found %d, should be %d", + si->vols_found, vols_found); + goto out; + } + + /* Check that scanning information is correct */ + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + last_seb = NULL; + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + int vol_type; + + cond_resched(); + + last_seb = seb; + + err = ubi_io_read_vid_hdr(ubi, seb->pnum, vidh, 1); + if (err && err != UBI_IO_BITFLIPS) { + ubi_err("VID header is not OK (%d)", err); + if (err > 0) + err = -EIO; + return err; + } + + vol_type = vidh->vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + if (sv->vol_type != vol_type) { + ubi_err("bad vol_type"); + goto bad_vid_hdr; + } + + if (seb->sqnum != be64_to_cpu(vidh->sqnum)) { + ubi_err("bad sqnum %llu", seb->sqnum); + goto bad_vid_hdr; + } + + if (sv->vol_id != be32_to_cpu(vidh->vol_id)) { + ubi_err("bad vol_id %d", sv->vol_id); + goto bad_vid_hdr; + } + + if (sv->compat != vidh->compat) { + ubi_err("bad compat %d", vidh->compat); + goto bad_vid_hdr; + } + + if (seb->lnum != be32_to_cpu(vidh->lnum)) { + ubi_err("bad lnum %d", seb->lnum); + goto bad_vid_hdr; + } + + if (sv->used_ebs != be32_to_cpu(vidh->used_ebs)) { + ubi_err("bad used_ebs %d", sv->used_ebs); + goto bad_vid_hdr; + } + + if (sv->data_pad != be32_to_cpu(vidh->data_pad)) { + ubi_err("bad data_pad %d", sv->data_pad); + goto bad_vid_hdr; + } + + if (seb->leb_ver != be32_to_cpu(vidh->leb_ver)) { + ubi_err("bad leb_ver %u", seb->leb_ver); + goto bad_vid_hdr; + } + } + + if (!last_seb) + continue; + + if (sv->highest_lnum != be32_to_cpu(vidh->lnum)) { + ubi_err("bad highest_lnum %d", sv->highest_lnum); + goto bad_vid_hdr; + } + + if (sv->last_data_size != be32_to_cpu(vidh->data_size)) { + ubi_err("bad last_data_size %d", sv->last_data_size); + goto bad_vid_hdr; + } + } + + /* + * Make sure that all the physical eraseblocks are in one of the lists + * or trees. + */ + buf = kzalloc(ubi->peb_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (pnum = 0; pnum < ubi->peb_count; pnum++) { + err = ubi_io_is_bad(ubi, pnum); + if (err < 0) { + kfree(buf); + return err; + } + else if (err) + buf[pnum] = 1; + } + + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) + buf[seb->pnum] = 1; + + list_for_each_entry(seb, &si->free, u.list) + buf[seb->pnum] = 1; + + list_for_each_entry(seb, &si->corr, u.list) + buf[seb->pnum] = 1; + + list_for_each_entry(seb, &si->erase, u.list) + buf[seb->pnum] = 1; + + list_for_each_entry(seb, &si->alien, u.list) + buf[seb->pnum] = 1; + + err = 0; + for (pnum = 0; pnum < ubi->peb_count; pnum++) + if (!buf[pnum]) { + ubi_err("PEB %d is not referred", pnum); + err = 1; + } + + kfree(buf); + if (err) + goto out; + return 0; + +bad_seb: + ubi_err("bad scanning information about LEB %d", seb->lnum); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_sv(sv); + goto out; + +bad_sv: + ubi_err("bad scanning information about volume %d", sv->vol_id); + ubi_dbg_dump_sv(sv); + goto out; + +bad_vid_hdr: + ubi_err("bad scanning information about volume %d", sv->vol_id); + ubi_dbg_dump_sv(sv); + ubi_dbg_dump_vid_hdr(vidh); + +out: + ubi_dbg_dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/scan.h b/qemu/roms/u-boot/drivers/mtd/ubi/scan.h new file mode 100644 index 000000000..252b1f1e8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/scan.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_SCAN_H__ +#define __UBI_SCAN_H__ + +/* The erase counter value for this physical eraseblock is unknown */ +#define UBI_SCAN_UNKNOWN_EC (-1) + +/** + * struct ubi_scan_leb - scanning information about a physical eraseblock. + * @ec: erase counter (%UBI_SCAN_UNKNOWN_EC if it is unknown) + * @pnum: physical eraseblock number + * @lnum: logical eraseblock number + * @scrub: if this physical eraseblock needs scrubbing + * @sqnum: sequence number + * @u: unions RB-tree or @list links + * @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects + * @u.list: link in one of the eraseblock lists + * @leb_ver: logical eraseblock version (obsolete) + * + * One object of this type is allocated for each physical eraseblock during + * scanning. + */ +struct ubi_scan_leb { + int ec; + int pnum; + int lnum; + int scrub; + unsigned long long sqnum; + union { + struct rb_node rb; + struct list_head list; + } u; + uint32_t leb_ver; +}; + +/** + * struct ubi_scan_volume - scanning information about a volume. + * @vol_id: volume ID + * @highest_lnum: highest logical eraseblock number in this volume + * @leb_count: number of logical eraseblocks in this volume + * @vol_type: volume type + * @used_ebs: number of used logical eraseblocks in this volume (only for + * static volumes) + * @last_data_size: amount of data in the last logical eraseblock of this + * volume (always equivalent to the usable logical eraseblock size in case of + * dynamic volumes) + * @data_pad: how many bytes at the end of logical eraseblocks of this volume + * are not used (due to volume alignment) + * @compat: compatibility flags of this volume + * @rb: link in the volume RB-tree + * @root: root of the RB-tree containing all the eraseblock belonging to this + * volume (&struct ubi_scan_leb objects) + * + * One object of this type is allocated for each volume during scanning. + */ +struct ubi_scan_volume { + int vol_id; + int highest_lnum; + int leb_count; + int vol_type; + int used_ebs; + int last_data_size; + int data_pad; + int compat; + struct rb_node rb; + struct rb_root root; +}; + +/** + * struct ubi_scan_info - UBI scanning information. + * @volumes: root of the volume RB-tree + * @corr: list of corrupted physical eraseblocks + * @free: list of free physical eraseblocks + * @erase: list of physical eraseblocks which have to be erased + * @alien: list of physical eraseblocks which should not be used by UBI (e.g., + * @bad_peb_count: count of bad physical eraseblocks + * those belonging to "preserve"-compatible internal volumes) + * @vols_found: number of volumes found during scanning + * @highest_vol_id: highest volume ID + * @alien_peb_count: count of physical eraseblocks in the @alien list + * @is_empty: flag indicating whether the MTD device is empty or not + * @min_ec: lowest erase counter value + * @max_ec: highest erase counter value + * @max_sqnum: highest sequence number value + * @mean_ec: mean erase counter value + * @ec_sum: a temporary variable used when calculating @mean_ec + * @ec_count: a temporary variable used when calculating @mean_ec + * + * This data structure contains the result of scanning and may be used by other + * UBI units to build final UBI data structures, further error-recovery and so + * on. + */ +struct ubi_scan_info { + struct rb_root volumes; + struct list_head corr; + struct list_head free; + struct list_head erase; + struct list_head alien; + int bad_peb_count; + int vols_found; + int highest_vol_id; + int alien_peb_count; + int is_empty; + int min_ec; + int max_ec; + unsigned long long max_sqnum; + int mean_ec; + uint64_t ec_sum; + int ec_count; +}; + +struct ubi_device; +struct ubi_vid_hdr; + +/* + * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a + * list. + * + * @sv: volume scanning information + * @seb: scanning eraseblock infprmation + * @list: the list to move to + */ +static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv, + struct ubi_scan_leb *seb, + struct list_head *list) +{ + rb_erase(&seb->u.rb, &sv->root); + list_add_tail(&seb->u.list, list); +} + +int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, + int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, + int bitflips); +struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si, + int vol_id); +struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv, + int lnum); +void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv); +struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, + struct ubi_scan_info *si); +int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si, + int pnum, int ec); +struct ubi_scan_info *ubi_scan(struct ubi_device *ubi); +void ubi_scan_destroy_si(struct ubi_scan_info *si); + +#endif /* !__UBI_SCAN_H__ */ diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/ubi-media.h b/qemu/roms/u-boot/drivers/mtd/ubi/ubi-media.h new file mode 100644 index 000000000..9012326d6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/ubi-media.h @@ -0,0 +1,360 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Thomas Gleixner + * Frank Haverkamp + * Oliver Lohmann + * Andreas Arnez + */ + +/* + * This file defines the layout of UBI headers and all the other UBI on-flash + * data structures. + */ + +#ifndef __UBI_MEDIA_H__ +#define __UBI_MEDIA_H__ + +#include + +/* The version of UBI images supported by this implementation */ +#define UBI_VERSION 1 + +/* The highest erase counter value supported by this implementation */ +#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF + +/* The initial CRC32 value used when calculating CRC checksums */ +#define UBI_CRC32_INIT 0xFFFFFFFFU + +/* Erase counter header magic number (ASCII "UBI#") */ +#define UBI_EC_HDR_MAGIC 0x55424923 +/* Volume identifier header magic number (ASCII "UBI!") */ +#define UBI_VID_HDR_MAGIC 0x55424921 + +/* + * Volume type constants used in the volume identifier header. + * + * @UBI_VID_DYNAMIC: dynamic volume + * @UBI_VID_STATIC: static volume + */ +enum { + UBI_VID_DYNAMIC = 1, + UBI_VID_STATIC = 2 +}; + +/* + * Volume flags used in the volume table record. + * + * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume + * + * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume + * table. UBI automatically re-sizes the volume which has this flag and makes + * the volume to be of largest possible size. This means that if after the + * initialization UBI finds out that there are available physical eraseblocks + * present on the device, it automatically appends all of them to the volume + * (the physical eraseblocks reserved for bad eraseblocks handling and other + * reserved physical eraseblocks are not taken). So, if there is a volume with + * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical + * eraseblocks will be zero after UBI is loaded, because all of them will be + * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared + * after the volume had been initialized. + * + * The auto-resize feature is useful for device production purposes. For + * example, different NAND flash chips may have different amount of initial bad + * eraseblocks, depending of particular chip instance. Manufacturers of NAND + * chips usually guarantee that the amount of initial bad eraseblocks does not + * exceed certain percent, e.g. 2%. When one creates an UBI image which will be + * flashed to the end devices in production, he does not know the exact amount + * of good physical eraseblocks the NAND chip on the device will have, but this + * number is required to calculate the volume sized and put them to the volume + * table of the UBI image. In this case, one of the volumes (e.g., the one + * which will store the root file system) is marked as "auto-resizable", and + * UBI will adjust its size on the first boot if needed. + * + * Note, first UBI reserves some amount of physical eraseblocks for bad + * eraseblock handling, and then re-sizes the volume, not vice-versa. This + * means that the pool of reserved physical eraseblocks will always be present. + */ +enum { + UBI_VTBL_AUTORESIZE_FLG = 0x01, +}; + +/* + * Compatibility constants used by internal volumes. + * + * @UBI_COMPAT_DELETE: delete this internal volume before anything is written + * to the flash + * @UBI_COMPAT_RO: attach this device in read-only mode + * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its + * physical eraseblocks, don't allow the wear-leveling unit to move them + * @UBI_COMPAT_REJECT: reject this UBI image + */ +enum { + UBI_COMPAT_DELETE = 1, + UBI_COMPAT_RO = 2, + UBI_COMPAT_PRESERVE = 4, + UBI_COMPAT_REJECT = 5 +}; + +/* Sizes of UBI headers */ +#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) +#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) + +/* Sizes of UBI headers without the ending CRC */ +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32)) + +/** + * struct ubi_ec_hdr - UBI erase counter header. + * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) + * @version: version of UBI implementation which is supposed to accept this + * UBI image + * @padding1: reserved for future, zeroes + * @ec: the erase counter + * @vid_hdr_offset: where the VID header starts + * @data_offset: where the user data start + * @padding2: reserved for future, zeroes + * @hdr_crc: erase counter header CRC checksum + * + * The erase counter header takes 64 bytes and has a plenty of unused space for + * future usage. The unused fields are zeroed. The @version field is used to + * indicate the version of UBI implementation which is supposed to be able to + * work with this UBI image. If @version is greater then the current UBI + * version, the image is rejected. This may be useful in future if something + * is changed radically. This field is duplicated in the volume identifier + * header. + * + * The @vid_hdr_offset and @data_offset fields contain the offset of the the + * volume identifier header and user data, relative to the beginning of the + * physical eraseblock. These values have to be the same for all physical + * eraseblocks. + */ +struct ubi_ec_hdr { + __be32 magic; + __u8 version; + __u8 padding1[3]; + __be64 ec; /* Warning: the current limit is 31-bit anyway! */ + __be32 vid_hdr_offset; + __be32 data_offset; + __u8 padding2[36]; + __be32 hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr - on-flash UBI volume identifier header. + * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) + * @version: UBI implementation version which is supposed to accept this UBI + * image (%UBI_VERSION) + * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @copy_flag: if this logical eraseblock was copied from another physical + * eraseblock (for wear-leveling reasons) + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @vol_id: ID of this volume + * @lnum: logical eraseblock number + * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be + * removed, kept only for not breaking older UBI users) + * @data_size: how many bytes of data this logical eraseblock contains + * @used_ebs: total number of used logical eraseblocks in this volume + * @data_pad: how many bytes at the end of this physical eraseblock are not + * used + * @data_crc: CRC checksum of the data stored in this logical eraseblock + * @padding1: reserved for future, zeroes + * @sqnum: sequence number + * @padding2: reserved for future, zeroes + * @hdr_crc: volume identifier header CRC checksum + * + * The @sqnum is the value of the global sequence counter at the time when this + * VID header was created. The global sequence counter is incremented each time + * UBI writes a new VID header to the flash, i.e. when it maps a logical + * eraseblock to a new physical eraseblock. The global sequence counter is an + * unsigned 64-bit integer and we assume it never overflows. The @sqnum + * (sequence number) is used to distinguish between older and newer versions of + * logical eraseblocks. + * + * There are 2 situations when there may be more then one physical eraseblock + * corresponding to the same logical eraseblock, i.e., having the same @vol_id + * and @lnum values in the volume identifier header. Suppose we have a logical + * eraseblock L and it is mapped to the physical eraseblock P. + * + * 1. Because UBI may erase physical eraseblocks asynchronously, the following + * situation is possible: L is asynchronously erased, so P is scheduled for + * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, + * so P1 is written to, then an unclean reboot happens. Result - there are 2 + * physical eraseblocks P and P1 corresponding to the same logical eraseblock + * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the + * flash. + * + * 2. From time to time UBI moves logical eraseblocks to other physical + * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P + * to P1, and an unclean reboot happens before P is physically erased, there + * are two physical eraseblocks P and P1 corresponding to L and UBI has to + * select one of them when the flash is attached. The @sqnum field says which + * PEB is the original (obviously P will have lower @sqnum) and the copy. But + * it is not enough to select the physical eraseblock with the higher sequence + * number, because the unclean reboot could have happen in the middle of the + * copying process, so the data in P is corrupted. It is also not enough to + * just select the physical eraseblock with lower sequence number, because the + * data there may be old (consider a case if more data was added to P1 after + * the copying). Moreover, the unclean reboot may happen when the erasure of P + * was just started, so it result in unstable P, which is "mostly" OK, but + * still has unstable bits. + * + * UBI uses the @copy_flag field to indicate that this logical eraseblock is a + * copy. UBI also calculates data CRC when the data is moved and stores it at + * the @data_crc field of the copy (P1). So when UBI needs to pick one physical + * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is + * examined. If it is cleared, the situation* is simple and the newer one is + * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC + * checksum is correct, this physical eraseblock is selected (P1). Otherwise + * the older one (P) is selected. + * + * Note, there is an obsolete @leb_ver field which was used instead of @sqnum + * in the past. But it is not used anymore and we keep it in order to be able + * to deal with old UBI images. It will be removed at some point. + * + * There are 2 sorts of volumes in UBI: user volumes and internal volumes. + * Internal volumes are not seen from outside and are used for various internal + * UBI purposes. In this implementation there is only one internal volume - the + * layout volume. Internal volumes are the main mechanism of UBI extensions. + * For example, in future one may introduce a journal internal volume. Internal + * volumes have their own reserved range of IDs. + * + * The @compat field is only used for internal volumes and contains the "degree + * of their compatibility". It is always zero for user volumes. This field + * provides a mechanism to introduce UBI extensions and to be still compatible + * with older UBI binaries. For example, if someone introduced a journal in + * future, he would probably use %UBI_COMPAT_DELETE compatibility for the + * journal volume. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this volume and work perfectly + * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image + * - it just ignores the Ext3fs journal. + * + * The @data_crc field contains the CRC checksum of the contents of the logical + * eraseblock if this is a static volume. In case of dynamic volumes, it does + * not contain the CRC checksum as a rule. The only exception is when the + * data of the physical eraseblock was moved by the wear-leveling unit, then + * the wear-leveling unit calculates the data CRC and stores it in the + * @data_crc field. And of course, the @copy_flag is %in this case. + * + * The @data_size field is used only for static volumes because UBI has to know + * how many bytes of data are stored in this eraseblock. For dynamic volumes, + * this field usually contains zero. The only exception is when the data of the + * physical eraseblock was moved to another physical eraseblock for + * wear-leveling reasons. In this case, UBI calculates CRC checksum of the + * contents and uses both @data_crc and @data_size fields. In this case, the + * @data_size field contains data size. + * + * The @used_ebs field is used only for static volumes and indicates how many + * eraseblocks the data of the volume takes. For dynamic volumes this field is + * not used and always contains zero. + * + * The @data_pad is calculated when volumes are created using the alignment + * parameter. So, effectively, the @data_pad field reduces the size of logical + * eraseblocks of this volume. This is very handy when one uses block-oriented + * software (say, cramfs) on top of the UBI volume. + */ +struct ubi_vid_hdr { + __be32 magic; + __u8 version; + __u8 vol_type; + __u8 copy_flag; + __u8 compat; + __be32 vol_id; + __be32 lnum; + __be32 leb_ver; /* obsolete, to be removed, don't use */ + __be32 data_size; + __be32 used_ebs; + __be32 data_pad; + __be32 data_crc; + __u8 padding1[4]; + __be64 sqnum; + __u8 padding2[12]; + __be32 hdr_crc; +} __attribute__ ((packed)); + +/* Internal UBI volumes count */ +#define UBI_INT_VOL_COUNT 1 + +/* + * Starting ID of internal volumes. There is reserved room for 4096 internal + * volumes. + */ +#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) + +/* The layout volume contains the volume table */ + +#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START +#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC +#define UBI_LAYOUT_VOLUME_ALIGN 1 +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_LAYOUT_VOLUME_NAME "layout volume" +#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT + +/* The maximum number of volumes per one UBI device */ +#define UBI_MAX_VOLUMES 128 + +/* The maximum volume name length */ +#define UBI_VOL_NAME_MAX 127 + +/* Size of the volume table record */ +#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) + +/* Size of the volume table record without the ending CRC */ +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32)) + +/** + * struct ubi_vtbl_record - a record in the volume table. + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @upd_marker: if volume update was started but not finished + * @name_len: volume name length + * @name: the volume name + * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) + * @padding: reserved, zeroes + * @crc: a CRC32 checksum of the record + * + * The volume table records are stored in the volume table, which is stored in + * the layout volume. The layout volume consists of 2 logical eraseblock, each + * of which contains a copy of the volume table (i.e., the volume table is + * duplicated). The volume table is an array of &struct ubi_vtbl_record + * objects indexed by the volume ID. + * + * If the size of the logical eraseblock is large enough to fit + * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES + * records. Otherwise, it contains as many records as it can fit (i.e., size of + * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). + * + * The @upd_marker flag is used to implement volume update. It is set to %1 + * before update and set to %0 after the update. So if the update operation was + * interrupted, UBI knows that the volume is corrupted. + * + * The @alignment field is specified when the volume is created and cannot be + * later changed. It may be useful, for example, when a block-oriented file + * system works on top of UBI. The @data_pad field is calculated using the + * logical eraseblock size and @alignment. The alignment must be multiple to the + * minimal flash I/O unit. If @alignment is 1, all the available space of + * the physical eraseblocks is used. + * + * Empty records contain all zeroes and the CRC checksum of those zeroes. + */ +struct ubi_vtbl_record { + __be32 reserved_pebs; + __be32 alignment; + __be32 data_pad; + __u8 vol_type; + __u8 upd_marker; + __be16 name_len; + __u8 name[UBI_VOL_NAME_MAX+1]; + __u8 flags; + __u8 padding[23]; + __be32 crc; +} __attribute__ ((packed)); + +#endif /* !__UBI_MEDIA_H__ */ diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/ubi.h b/qemu/roms/u-boot/drivers/mtd/ubi/ubi.h new file mode 100644 index 000000000..f4f71655e --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/ubi.h @@ -0,0 +1,638 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_UBI_H__ +#define __UBI_UBI_H__ + +#ifdef UBI_LINUX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "ubi-media.h" +#include "scan.h" +#include "debug.h" + +/* Maximum number of supported UBI devices */ +#define UBI_MAX_DEVICES 32 + +/* UBI name used for character devices, sysfs, etc */ +#define UBI_NAME_STR "ubi" + +/* Normal UBI messages */ +#ifdef CONFIG_UBI_SILENCE_MSG +#define ubi_msg(fmt, ...) +#else +#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__) +#endif +/* UBI warning messages */ +#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \ + __func__, ##__VA_ARGS__) +/* UBI error messages */ +#define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \ + __func__, ##__VA_ARGS__) + +/* Lowest number PEBs reserved for bad PEB handling */ +#define MIN_RESEVED_PEBS 2 + +/* Background thread name pattern */ +#define UBI_BGT_NAME_PATTERN "ubi_bgt%dd" + +/* This marker in the EBA table means that the LEB is um-mapped */ +#define UBI_LEB_UNMAPPED -1 + +/* + * In case of errors, UBI tries to repeat the operation several times before + * returning error. The below constant defines how many times UBI re-tries. + */ +#define UBI_IO_RETRIES 3 + +/* + * Error codes returned by the I/O unit. + * + * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only + * 0xFF bytes + * UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a + * valid erase counter header, and the rest are %0xFF bytes + * UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC) + * UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or + * CRC) + * UBI_IO_BITFLIPS: bit-flips were detected and corrected + */ +enum { + UBI_IO_PEB_EMPTY = 1, + UBI_IO_PEB_FREE, + UBI_IO_BAD_EC_HDR, + UBI_IO_BAD_VID_HDR, + UBI_IO_BITFLIPS +}; + +/** + * struct ubi_wl_entry - wear-leveling entry. + * @rb: link in the corresponding RB-tree + * @ec: erase counter + * @pnum: physical eraseblock number + * + * This data structure is used in the WL unit. Each physical eraseblock has a + * corresponding &struct wl_entry object which may be kept in different + * RB-trees. See WL unit for details. + */ +struct ubi_wl_entry { + struct rb_node rb; + int ec; + int pnum; +}; + +/** + * struct ubi_ltree_entry - an entry in the lock tree. + * @rb: links RB-tree nodes + * @vol_id: volume ID of the locked logical eraseblock + * @lnum: locked logical eraseblock number + * @users: how many tasks are using this logical eraseblock or wait for it + * @mutex: read/write mutex to implement read/write access serialization to + * the (@vol_id, @lnum) logical eraseblock + * + * This data structure is used in the EBA unit to implement per-LEB locking. + * When a logical eraseblock is being locked - corresponding + * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree). + * See EBA unit for details. + */ +struct ubi_ltree_entry { + struct rb_node rb; + int vol_id; + int lnum; + int users; + struct rw_semaphore mutex; +}; + +struct ubi_volume_desc; + +/** + * struct ubi_volume - UBI volume description data structure. + * @dev: device object to make use of the the Linux device model + * @cdev: character device object to create character device + * @ubi: reference to the UBI device description object + * @vol_id: volume ID + * @ref_count: volume reference count + * @readers: number of users holding this volume in read-only mode + * @writers: number of users holding this volume in read-write mode + * @exclusive: whether somebody holds this volume in exclusive mode + * + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @usable_leb_size: logical eraseblock size without padding + * @used_ebs: how many logical eraseblocks in this volume contain data + * @last_eb_bytes: how many bytes are stored in the last logical eraseblock + * @used_bytes: how many bytes of data this volume contains + * @alignment: volume alignment + * @data_pad: how many bytes are not used at the end of physical eraseblocks to + * satisfy the requested alignment + * @name_len: volume name length + * @name: volume name + * + * @upd_ebs: how many eraseblocks are expected to be updated + * @ch_lnum: LEB number which is being changing by the atomic LEB change + * operation + * @ch_dtype: data persistency type which is being changing by the atomic LEB + * change operation + * @upd_bytes: how many bytes are expected to be received for volume update or + * atomic LEB change + * @upd_received: how many bytes were already received for volume update or + * atomic LEB change + * @upd_buf: update buffer which is used to collect update data or data for + * atomic LEB change + * + * @eba_tbl: EBA table of this volume (LEB->PEB mapping) + * @checked: %1 if this static volume was checked + * @corrupted: %1 if the volume is corrupted (static volumes only) + * @upd_marker: %1 if the update marker is set for this volume + * @updating: %1 if the volume is being updated + * @changing_leb: %1 if the atomic LEB change ioctl command is in progress + * + * @gluebi_desc: gluebi UBI volume descriptor + * @gluebi_refcount: reference count of the gluebi MTD device + * @gluebi_mtd: MTD device description object of the gluebi MTD device + * + * The @corrupted field indicates that the volume's contents is corrupted. + * Since UBI protects only static volumes, this field is not relevant to + * dynamic volumes - it is user's responsibility to assure their data + * integrity. + * + * The @upd_marker flag indicates that this volume is either being updated at + * the moment or is damaged because of an unclean reboot. + */ +struct ubi_volume { + struct device dev; + struct cdev cdev; + struct ubi_device *ubi; + int vol_id; + int ref_count; + int readers; + int writers; + int exclusive; + + int reserved_pebs; + int vol_type; + int usable_leb_size; + int used_ebs; + int last_eb_bytes; + long long used_bytes; + int alignment; + int data_pad; + int name_len; + char name[UBI_VOL_NAME_MAX+1]; + + int upd_ebs; + int ch_lnum; + int ch_dtype; + long long upd_bytes; + long long upd_received; + void *upd_buf; + + int *eba_tbl; + unsigned int checked:1; + unsigned int corrupted:1; + unsigned int upd_marker:1; + unsigned int updating:1; + unsigned int changing_leb:1; + +#ifdef CONFIG_MTD_UBI_GLUEBI + /* + * Gluebi-related stuff may be compiled out. + * TODO: this should not be built into UBI but should be a separate + * ubimtd driver which works on top of UBI and emulates MTD devices. + */ + struct ubi_volume_desc *gluebi_desc; + int gluebi_refcount; + struct mtd_info gluebi_mtd; +#endif +}; + +/** + * struct ubi_volume_desc - descriptor of the UBI volume returned when it is + * opened. + * @vol: reference to the corresponding volume description object + * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE) + */ +struct ubi_volume_desc { + struct ubi_volume *vol; + int mode; +}; + +struct ubi_wl_entry; + +/** + * struct ubi_device - UBI device description structure + * @dev: UBI device object to use the the Linux device model + * @cdev: character device object to create character device + * @ubi_num: UBI device number + * @ubi_name: UBI device name + * @vol_count: number of volumes in this UBI device + * @volumes: volumes of this UBI device + * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs, + * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, + * @vol->readers, @vol->writers, @vol->exclusive, + * @vol->ref_count, @vol->mapping and @vol->eba_tbl. + * @ref_count: count of references on the UBI device + * + * @rsvd_pebs: count of reserved physical eraseblocks + * @avail_pebs: count of available physical eraseblocks + * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB + * handling + * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling + * + * @autoresize_vol_id: ID of the volume which has to be auto-resized at the end + * of UBI ititializetion + * @vtbl_slots: how many slots are available in the volume table + * @vtbl_size: size of the volume table in bytes + * @vtbl: in-RAM volume table copy + * @volumes_mutex: protects on-flash volume table and serializes volume + * changes, like creation, deletion, update, resize + * + * @max_ec: current highest erase counter value + * @mean_ec: current mean erase counter value + * + * @global_sqnum: global sequence number + * @ltree_lock: protects the lock tree and @global_sqnum + * @ltree: the lock tree + * @alc_mutex: serializes "atomic LEB change" operations + * + * @used: RB-tree of used physical eraseblocks + * @free: RB-tree of free physical eraseblocks + * @scrub: RB-tree of physical eraseblocks which need scrubbing + * @prot: protection trees + * @prot.pnum: protection tree indexed by physical eraseblock numbers + * @prot.aec: protection tree indexed by absolute erase counter value + * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from, + * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works + * fields + * @move_mutex: serializes eraseblock moves + * @wl_scheduled: non-zero if the wear-leveling was scheduled + * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any + * physical eraseblock + * @abs_ec: absolute erase counter + * @move_from: physical eraseblock from where the data is being moved + * @move_to: physical eraseblock where the data is being moved to + * @move_to_put: if the "to" PEB was put + * @works: list of pending works + * @works_count: count of pending works + * @bgt_thread: background thread description object + * @thread_enabled: if the background thread is enabled + * @bgt_name: background thread name + * + * @flash_size: underlying MTD device size (in bytes) + * @peb_count: count of physical eraseblocks on the MTD device + * @peb_size: physical eraseblock size + * @bad_peb_count: count of bad physical eraseblocks + * @good_peb_count: count of good physical eraseblocks + * @min_io_size: minimal input/output unit size of the underlying MTD device + * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers + * @ro_mode: if the UBI device is in read-only mode + * @leb_size: logical eraseblock size + * @leb_start: starting offset of logical eraseblocks within physical + * eraseblocks + * @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size + * @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size + * @vid_hdr_offset: starting offset of the volume identifier header (might be + * unaligned) + * @vid_hdr_aloffset: starting offset of the VID header aligned to + * @hdrs_min_io_size + * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset + * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or + * not + * @mtd: MTD device descriptor + * + * @peb_buf1: a buffer of PEB size used for different purposes + * @peb_buf2: another buffer of PEB size used for different purposes + * @buf_mutex: proptects @peb_buf1 and @peb_buf2 + * @dbg_peb_buf: buffer of PEB size used for debugging + * @dbg_buf_mutex: proptects @dbg_peb_buf + */ +struct ubi_device { + struct cdev cdev; + struct device dev; + int ubi_num; + char ubi_name[sizeof(UBI_NAME_STR)+5]; + int vol_count; + struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; + spinlock_t volumes_lock; + int ref_count; + + int rsvd_pebs; + int avail_pebs; + int beb_rsvd_pebs; + int beb_rsvd_level; + + int autoresize_vol_id; + int vtbl_slots; + int vtbl_size; + struct ubi_vtbl_record *vtbl; + struct mutex volumes_mutex; + + int max_ec; + /* TODO: mean_ec is not updated run-time, fix */ + int mean_ec; + + /* EBA unit's stuff */ + unsigned long long global_sqnum; + spinlock_t ltree_lock; + struct rb_root ltree; + struct mutex alc_mutex; + + /* Wear-leveling unit's stuff */ + struct rb_root used; + struct rb_root free; + struct rb_root scrub; + struct { + struct rb_root pnum; + struct rb_root aec; + } prot; + spinlock_t wl_lock; + struct mutex move_mutex; + struct rw_semaphore work_sem; + int wl_scheduled; + struct ubi_wl_entry **lookuptbl; + unsigned long long abs_ec; + struct ubi_wl_entry *move_from; + struct ubi_wl_entry *move_to; + int move_to_put; + struct list_head works; + int works_count; + struct task_struct *bgt_thread; + int thread_enabled; + char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; + + /* I/O unit's stuff */ + long long flash_size; + int peb_count; + int peb_size; + int bad_peb_count; + int good_peb_count; + int min_io_size; + int hdrs_min_io_size; + int ro_mode; + int leb_size; + int leb_start; + int ec_hdr_alsize; + int vid_hdr_alsize; + int vid_hdr_offset; + int vid_hdr_aloffset; + int vid_hdr_shift; + int bad_allowed; + struct mtd_info *mtd; + + void *peb_buf1; + void *peb_buf2; + struct mutex buf_mutex; + struct mutex ckvol_mutex; +#ifdef CONFIG_MTD_UBI_DEBUG + void *dbg_peb_buf; + struct mutex dbg_buf_mutex; +#endif +}; + +extern struct kmem_cache *ubi_wl_entry_slab; +extern struct file_operations ubi_ctrl_cdev_operations; +extern struct file_operations ubi_cdev_operations; +extern struct file_operations ubi_vol_cdev_operations; +extern struct class *ubi_class; +extern struct mutex ubi_devices_mutex; + +/* vtbl.c */ +int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, + struct ubi_vtbl_record *vtbl_rec); +int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si); + +/* vmt.c */ +int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req); +int ubi_remove_volume(struct ubi_volume_desc *desc); +int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs); +int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol); +void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol); + +/* upd.c */ +int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes); +int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count); +int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + const struct ubi_leb_change_req *req); +int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count); + +/* misc.c */ +int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); +int ubi_check_volume(struct ubi_device *ubi, int vol_id); +void ubi_calculate_reserved(struct ubi_device *ubi); + +/* gluebi.c */ +#ifdef CONFIG_MTD_UBI_GLUEBI +int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol); +int ubi_destroy_gluebi(struct ubi_volume *vol); +void ubi_gluebi_updated(struct ubi_volume *vol); +#else +#define ubi_create_gluebi(ubi, vol) 0 + +static inline int ubi_destroy_gluebi(struct ubi_volume *vol) +{ + return 0; +} + +#define ubi_gluebi_updated(vol) +#endif + +/* eba.c */ +int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum); +int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int offset, int len, int check); +int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + const void *buf, int offset, int len, int dtype); +int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype, + int used_ebs); +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype); +int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + struct ubi_vid_hdr *vid_hdr); +int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); +void ubi_eba_close(const struct ubi_device *ubi); + +/* wl.c */ +int ubi_wl_get_peb(struct ubi_device *ubi, int dtype); +int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture); +int ubi_wl_flush(struct ubi_device *ubi); +int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum); +int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); +void ubi_wl_close(struct ubi_device *ubi); +int ubi_thread(void *u); + +/* io.c */ +int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + int len); +int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, + int len); +int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture); +int ubi_io_is_bad(const struct ubi_device *ubi, int pnum); +int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum); +int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr, int verbose); +int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr); +int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose); +int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr); + +/* build.c */ +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset); +int ubi_detach_mtd_dev(int ubi_num, int anyway); +struct ubi_device *ubi_get_device(int ubi_num); +void ubi_put_device(struct ubi_device *ubi); +struct ubi_device *ubi_get_by_major(int major); +int ubi_major2num(int major); + +/* + * ubi_rb_for_each_entry - walk an RB-tree. + * @rb: a pointer to type 'struct rb_node' to to use as a loop counter + * @pos: a pointer to RB-tree entry type to use as a loop counter + * @root: RB-tree's root + * @member: the name of the 'struct rb_node' within the RB-tree entry + */ +#define ubi_rb_for_each_entry(rb, pos, root, member) \ + for (rb = rb_first(root), \ + pos = (rb ? container_of(rb, typeof(*pos), member) : NULL); \ + rb; \ + rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member)) + +/** + * ubi_zalloc_vid_hdr - allocate a volume identifier header object. + * @ubi: UBI device description object + * @gfp_flags: GFP flags to allocate with + * + * This function returns a pointer to the newly allocated and zero-filled + * volume identifier header object in case of success and %NULL in case of + * failure. + */ +static inline struct ubi_vid_hdr * +ubi_zalloc_vid_hdr(const struct ubi_device *ubi, gfp_t gfp_flags) +{ + void *vid_hdr; + + vid_hdr = kzalloc(ubi->vid_hdr_alsize, gfp_flags); + if (!vid_hdr) + return NULL; + + /* + * VID headers may be stored at un-aligned flash offsets, so we shift + * the pointer. + */ + return vid_hdr + ubi->vid_hdr_shift; +} + +/** + * ubi_free_vid_hdr - free a volume identifier header object. + * @ubi: UBI device description object + * @vid_hdr: the object to free + */ +static inline void ubi_free_vid_hdr(const struct ubi_device *ubi, + struct ubi_vid_hdr *vid_hdr) +{ + void *p = vid_hdr; + + if (!p) + return; + + kfree(p - ubi->vid_hdr_shift); +} + +/* + * This function is equivalent to 'ubi_io_read()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +static inline int ubi_io_read_data(const struct ubi_device *ubi, void *buf, + int pnum, int offset, int len) +{ + ubi_assert(offset >= 0); + return ubi_io_read(ubi, buf, pnum, offset + ubi->leb_start, len); +} + +/* + * This function is equivalent to 'ubi_io_write()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +static inline int ubi_io_write_data(struct ubi_device *ubi, const void *buf, + int pnum, int offset, int len) +{ + ubi_assert(offset >= 0); + return ubi_io_write(ubi, buf, pnum, offset + ubi->leb_start, len); +} + +/** + * ubi_ro_mode - switch to read-only mode. + * @ubi: UBI device description object + */ +static inline void ubi_ro_mode(struct ubi_device *ubi) +{ + if (!ubi->ro_mode) { + ubi->ro_mode = 1; + ubi_warn("switch to read-only mode"); + } +} + +/** + * vol_id2idx - get table index by volume ID. + * @ubi: UBI device description object + * @vol_id: volume ID + */ +static inline int vol_id2idx(const struct ubi_device *ubi, int vol_id) +{ + if (vol_id >= UBI_INTERNAL_VOL_START) + return vol_id - UBI_INTERNAL_VOL_START + ubi->vtbl_slots; + else + return vol_id; +} + +/** + * idx2vol_id - get volume ID by table index. + * @ubi: UBI device description object + * @idx: table index + */ +static inline int idx2vol_id(const struct ubi_device *ubi, int idx) +{ + if (idx >= ubi->vtbl_slots) + return idx - ubi->vtbl_slots + UBI_INTERNAL_VOL_START; + else + return idx; +} + +#endif /* !__UBI_UBI_H__ */ diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/upd.c b/qemu/roms/u-boot/drivers/mtd/ubi/upd.c new file mode 100644 index 000000000..e597f82b8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/upd.c @@ -0,0 +1,429 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + * + * Jan 2007: Alexander Schmidt, hacked per-volume update. + */ + +/* + * This file contains implementation of the volume update and atomic LEB change + * functionality. + * + * The update operation is based on the per-volume update marker which is + * stored in the volume table. The update marker is set before the update + * starts, and removed after the update has been finished. So if the update was + * interrupted by an unclean re-boot or due to some other reasons, the update + * marker stays on the flash media and UBI finds it when it attaches the MTD + * device next time. If the update marker is set for a volume, the volume is + * treated as damaged and most I/O operations are prohibited. Only a new update + * operation is allowed. + * + * Note, in general it is possible to implement the update operation as a + * transaction with a roll-back capability. + */ + +#ifdef UBI_LINUX +#include +#include +#include +#endif + +#include +#include "ubi.h" + +/** + * set_update_marker - set update marker. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function sets the update marker flag for volume @vol. Returns zero + * in case of success and a negative error code in case of failure. + */ +static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) +{ + int err; + struct ubi_vtbl_record vtbl_rec; + + dbg_msg("set update marker for volume %d", vol->vol_id); + + if (vol->upd_marker) { + ubi_assert(ubi->vtbl[vol->vol_id].upd_marker); + dbg_msg("already set"); + return 0; + } + + memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], + sizeof(struct ubi_vtbl_record)); + vtbl_rec.upd_marker = 1; + + mutex_lock(&ubi->volumes_mutex); + err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); + mutex_unlock(&ubi->volumes_mutex); + vol->upd_marker = 1; + return err; +} + +/** + * clear_update_marker - clear update marker. + * @ubi: UBI device description object + * @vol: volume description object + * @bytes: new data size in bytes + * + * This function clears the update marker for volume @vol, sets new volume + * data size and clears the "corrupted" flag (static volumes only). Returns + * zero in case of success and a negative error code in case of failure. + */ +static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes) +{ + int err; + uint64_t tmp; + struct ubi_vtbl_record vtbl_rec; + + dbg_msg("clear update marker for volume %d", vol->vol_id); + + memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], + sizeof(struct ubi_vtbl_record)); + ubi_assert(vol->upd_marker && vtbl_rec.upd_marker); + vtbl_rec.upd_marker = 0; + + if (vol->vol_type == UBI_STATIC_VOLUME) { + vol->corrupted = 0; + vol->used_bytes = tmp = bytes; + vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size); + vol->used_ebs = tmp; + if (vol->last_eb_bytes) + vol->used_ebs += 1; + else + vol->last_eb_bytes = vol->usable_leb_size; + } + + mutex_lock(&ubi->volumes_mutex); + err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); + mutex_unlock(&ubi->volumes_mutex); + vol->upd_marker = 0; + return err; +} + +/** + * ubi_start_update - start volume update. + * @ubi: UBI device description object + * @vol: volume description object + * @bytes: update bytes + * + * This function starts volume update operation. If @bytes is zero, the volume + * is just wiped out. Returns zero in case of success and a negative error code + * in case of failure. + */ +int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes) +{ + int i, err; + uint64_t tmp; + + dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); + ubi_assert(!vol->updating && !vol->changing_leb); + vol->updating = 1; + + err = set_update_marker(ubi, vol); + if (err) + return err; + + /* Before updating - wipe out the volume */ + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol, i); + if (err) + return err; + } + + if (bytes == 0) { + err = clear_update_marker(ubi, vol, 0); + if (err) + return err; + err = ubi_wl_flush(ubi); + if (!err) + vol->updating = 0; + } + + vol->upd_buf = vmalloc(ubi->leb_size); + if (!vol->upd_buf) + return -ENOMEM; + + tmp = bytes; + vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size); + vol->upd_ebs += tmp; + vol->upd_bytes = bytes; + vol->upd_received = 0; + return 0; +} + +/** + * ubi_start_leb_change - start atomic LEB change. + * @ubi: UBI device description object + * @vol: volume description object + * @req: operation request + * + * This function starts atomic LEB change operation. Returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + const struct ubi_leb_change_req *req) +{ + ubi_assert(!vol->updating && !vol->changing_leb); + + dbg_msg("start changing LEB %d:%d, %u bytes", + vol->vol_id, req->lnum, req->bytes); + if (req->bytes == 0) + return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0, + req->dtype); + + vol->upd_bytes = req->bytes; + vol->upd_received = 0; + vol->changing_leb = 1; + vol->ch_lnum = req->lnum; + vol->ch_dtype = req->dtype; + + vol->upd_buf = vmalloc(req->bytes); + if (!vol->upd_buf) + return -ENOMEM; + + return 0; +} + +/** + * write_leb - write update data. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: data to write + * @len: data size + * @used_ebs: how many logical eraseblocks will this volume contain (static + * volumes only) + * + * This function writes update data to corresponding logical eraseblock. In + * case of dynamic volume, this function checks if the data contains 0xFF bytes + * at the end. If yes, the 0xFF bytes are cut and not written. So if the whole + * buffer contains only 0xFF bytes, the LEB is left unmapped. + * + * The reason why we skip the trailing 0xFF bytes in case of dynamic volume is + * that we want to make sure that more data may be appended to the logical + * eraseblock in future. Indeed, writing 0xFF bytes may have side effects and + * this PEB won't be writable anymore. So if one writes the file-system image + * to the UBI volume where 0xFFs mean free space - UBI makes sure this free + * space is writable after the update. + * + * We do not do this for static volumes because they are read-only. But this + * also cannot be done because we have to store per-LEB CRC and the correct + * data length. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int len, int used_ebs) +{ + int err; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + int l = ALIGN(len, ubi->min_io_size); + + memset(buf + len, 0xFF, l - len); + len = ubi_calc_data_len(ubi, buf, l); + if (len == 0) { + dbg_msg("all %d bytes contain 0xFF - skip", len); + return 0; + } + + err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); + } else { + /* + * When writing static volume, and this is the last logical + * eraseblock, the length (@len) does not have to be aligned to + * the minimal flash I/O unit. The 'ubi_eba_write_leb_st()' + * function accepts exact (unaligned) length and stores it in + * the VID header. And it takes care of proper alignment by + * padding the buffer. Here we just make sure the padding will + * contain zeros, not random trash. + */ + memset(buf + len, 0, vol->usable_leb_size - len); + err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len, + UBI_UNKNOWN, used_ebs); + } + + return err; +} + +/** + * ubi_more_update_data - write more update data. + * @vol: volume description object + * @buf: write data (user-space memory buffer) + * @count: how much bytes to write + * + * This function writes more data to the volume which is being updated. It may + * be called arbitrary number of times until all the update data arriveis. This + * function returns %0 in case of success, number of bytes written during the + * last call if the whole volume update has been successfully finished, and a + * negative error code in case of failure. + */ +int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count) +{ + uint64_t tmp; + int lnum, offs, err = 0, len, to_write = count; + + dbg_msg("write %d of %lld bytes, %lld already passed", + count, vol->upd_bytes, vol->upd_received); + + if (ubi->ro_mode) + return -EROFS; + + tmp = vol->upd_received; + offs = do_div(tmp, vol->usable_leb_size); + lnum = tmp; + + if (vol->upd_received + count > vol->upd_bytes) + to_write = count = vol->upd_bytes - vol->upd_received; + + /* + * When updating volumes, we accumulate whole logical eraseblock of + * data and write it at once. + */ + if (offs != 0) { + /* + * This is a write to the middle of the logical eraseblock. We + * copy the data to our update buffer and wait for more data or + * flush it if the whole eraseblock is written or the update + * is finished. + */ + + len = vol->usable_leb_size - offs; + if (len > count) + len = count; + + err = copy_from_user(vol->upd_buf + offs, buf, len); + if (err) + return -EFAULT; + + if (offs + len == vol->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { + int flush_len = offs + len; + + /* + * OK, we gathered either the whole eraseblock or this + * is the last chunk, it's time to flush the buffer. + */ + ubi_assert(flush_len <= vol->usable_leb_size); + err = write_leb(ubi, vol, lnum, vol->upd_buf, flush_len, + vol->upd_ebs); + if (err) + return err; + } + + vol->upd_received += len; + count -= len; + buf += len; + lnum += 1; + } + + /* + * If we've got more to write, let's continue. At this point we know we + * are starting from the beginning of an eraseblock. + */ + while (count) { + if (count > vol->usable_leb_size) + len = vol->usable_leb_size; + else + len = count; + + err = copy_from_user(vol->upd_buf, buf, len); + if (err) + return -EFAULT; + + if (len == vol->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { + err = write_leb(ubi, vol, lnum, vol->upd_buf, + len, vol->upd_ebs); + if (err) + break; + } + + vol->upd_received += len; + count -= len; + lnum += 1; + buf += len; + } + + ubi_assert(vol->upd_received <= vol->upd_bytes); + if (vol->upd_received == vol->upd_bytes) { + /* The update is finished, clear the update marker */ + err = clear_update_marker(ubi, vol, vol->upd_bytes); + if (err) + return err; + err = ubi_wl_flush(ubi); + if (err == 0) { + vol->updating = 0; + err = to_write; + vfree(vol->upd_buf); + } + } + + return err; +} + +/** + * ubi_more_leb_change_data - accept more data for atomic LEB change. + * @vol: volume description object + * @buf: write data (user-space memory buffer) + * @count: how much bytes to write + * + * This function accepts more data to the volume which is being under the + * "atomic LEB change" operation. It may be called arbitrary number of times + * until all data arrives. This function returns %0 in case of success, number + * of bytes written during the last call if the whole "atomic LEB change" + * operation has been successfully finished, and a negative error code in case + * of failure. + */ +int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count) +{ + int err; + + dbg_msg("write %d of %lld bytes, %lld already passed", + count, vol->upd_bytes, vol->upd_received); + + if (ubi->ro_mode) + return -EROFS; + + if (vol->upd_received + count > vol->upd_bytes) + count = vol->upd_bytes - vol->upd_received; + + err = copy_from_user(vol->upd_buf + vol->upd_received, buf, count); + if (err) + return -EFAULT; + + vol->upd_received += count; + + if (vol->upd_received == vol->upd_bytes) { + int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size); + + memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes); + len = ubi_calc_data_len(ubi, vol->upd_buf, len); + err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum, + vol->upd_buf, len, UBI_UNKNOWN); + if (err) + return err; + } + + ubi_assert(vol->upd_received <= vol->upd_bytes); + if (vol->upd_received == vol->upd_bytes) { + vol->changing_leb = 0; + err = count; + vfree(vol->upd_buf); + } + + return err; +} diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/vmt.c b/qemu/roms/u-boot/drivers/mtd/ubi/vmt.c new file mode 100644 index 000000000..c4e894b43 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/vmt.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * This file contains implementation of volume creation, deletion, updating and + * resizing. + */ + +#ifdef UBI_LINUX +#include +#include +#endif + +#include +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static void paranoid_check_volumes(struct ubi_device *ubi); +#else +#define paranoid_check_volumes(ubi) +#endif + +#ifdef UBI_LINUX +static ssize_t vol_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf); + +/* Device attributes corresponding to files in '//class/ubi/ubiX_Y' */ +static struct device_attribute attr_vol_reserved_ebs = + __ATTR(reserved_ebs, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_type = + __ATTR(type, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_name = + __ATTR(name, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_corrupted = + __ATTR(corrupted, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_alignment = + __ATTR(alignment, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_usable_eb_size = + __ATTR(usable_eb_size, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_data_bytes = + __ATTR(data_bytes, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_upd_marker = + __ATTR(upd_marker, S_IRUGO, vol_attribute_show, NULL); + +/* + * "Show" method for files in '//class/ubi/ubiX_Y/'. + * + * Consider a situation: + * A. process 1 opens a sysfs file related to volume Y, say + * //class/ubi/ubiX_Y/reserved_ebs; + * B. process 2 removes volume Y; + * C. process 1 starts reading the //class/ubi/ubiX_Y/reserved_ebs file; + * + * In this situation, this function will return %-ENODEV because it will find + * out that the volume was removed from the @ubi->volumes array. + */ +static ssize_t vol_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + struct ubi_device *ubi; + + ubi = ubi_get_device(vol->ubi->ubi_num); + if (!ubi) + return -ENODEV; + + spin_lock(&ubi->volumes_lock); + if (!ubi->volumes[vol->vol_id]) { + spin_unlock(&ubi->volumes_lock); + ubi_put_device(ubi); + return -ENODEV; + } + /* Take a reference to prevent volume removal */ + vol->ref_count += 1; + spin_unlock(&ubi->volumes_lock); + + if (attr == &attr_vol_reserved_ebs) + ret = sprintf(buf, "%d\n", vol->reserved_pebs); + else if (attr == &attr_vol_type) { + const char *tp; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + tp = "dynamic"; + else + tp = "static"; + ret = sprintf(buf, "%s\n", tp); + } else if (attr == &attr_vol_name) + ret = sprintf(buf, "%s\n", vol->name); + else if (attr == &attr_vol_corrupted) + ret = sprintf(buf, "%d\n", vol->corrupted); + else if (attr == &attr_vol_alignment) + ret = sprintf(buf, "%d\n", vol->alignment); + else if (attr == &attr_vol_usable_eb_size) + ret = sprintf(buf, "%d\n", vol->usable_leb_size); + else if (attr == &attr_vol_data_bytes) + ret = sprintf(buf, "%lld\n", vol->used_bytes); + else if (attr == &attr_vol_upd_marker) + ret = sprintf(buf, "%d\n", vol->upd_marker); + else + /* This must be a bug */ + ret = -EINVAL; + + /* We've done the operation, drop volume and UBI device references */ + spin_lock(&ubi->volumes_lock); + vol->ref_count -= 1; + ubi_assert(vol->ref_count >= 0); + spin_unlock(&ubi->volumes_lock); + ubi_put_device(ubi); + return ret; +} +#endif + +/* Release method for volume devices */ +static void vol_release(struct device *dev) +{ + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + + kfree(vol); +} + +#ifdef UBI_LINUX +/** + * volume_sysfs_init - initialize sysfs for new volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + * + * Note, this function does not free allocated resources in case of failure - + * the caller does it. This is because this would cause release() here and the + * caller would oops. + */ +static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol) +{ + int err; + + err = device_create_file(&vol->dev, &attr_vol_reserved_ebs); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_type); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_name); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_corrupted); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_alignment); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_usable_eb_size); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_data_bytes); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_upd_marker); + return err; +} + +/** + * volume_sysfs_close - close sysfs for a volume. + * @vol: volume description object + */ +static void volume_sysfs_close(struct ubi_volume *vol) +{ + device_remove_file(&vol->dev, &attr_vol_upd_marker); + device_remove_file(&vol->dev, &attr_vol_data_bytes); + device_remove_file(&vol->dev, &attr_vol_usable_eb_size); + device_remove_file(&vol->dev, &attr_vol_alignment); + device_remove_file(&vol->dev, &attr_vol_corrupted); + device_remove_file(&vol->dev, &attr_vol_name); + device_remove_file(&vol->dev, &attr_vol_type); + device_remove_file(&vol->dev, &attr_vol_reserved_ebs); + device_unregister(&vol->dev); +} +#endif + +/** + * ubi_create_volume - create volume. + * @ubi: UBI device description object + * @req: volume creation request + * + * This function creates volume described by @req. If @req->vol_id id + * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume + * and saves it in @req->vol_id. Returns zero in case of success and a negative + * error code in case of failure. Note, the caller has to have the + * @ubi->volumes_mutex locked. + */ +int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) +{ + int i, err, vol_id = req->vol_id, dont_free = 0; + struct ubi_volume *vol; + struct ubi_vtbl_record vtbl_rec; + uint64_t bytes; + dev_t dev; + + if (ubi->ro_mode) + return -EROFS; + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + spin_lock(&ubi->volumes_lock); + if (vol_id == UBI_VOL_NUM_AUTO) { + /* Find unused volume ID */ + dbg_msg("search for vacant volume ID"); + for (i = 0; i < ubi->vtbl_slots; i++) + if (!ubi->volumes[i]) { + vol_id = i; + break; + } + + if (vol_id == UBI_VOL_NUM_AUTO) { + dbg_err("out of volume IDs"); + err = -ENFILE; + goto out_unlock; + } + req->vol_id = vol_id; + } + + dbg_msg("volume ID %d, %llu bytes, type %d, name %s", + vol_id, (unsigned long long)req->bytes, + (int)req->vol_type, req->name); + + /* Ensure that this volume does not exist */ + err = -EEXIST; + if (ubi->volumes[vol_id]) { + dbg_err("volume %d already exists", vol_id); + goto out_unlock; + } + + /* Ensure that the name is unique */ + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i] && + ubi->volumes[i]->name_len == req->name_len && + !strcmp(ubi->volumes[i]->name, req->name)) { + dbg_err("volume \"%s\" exists (ID %d)", req->name, i); + goto out_unlock; + } + + /* Calculate how many eraseblocks are requested */ + vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment; + bytes = req->bytes; + if (do_div(bytes, vol->usable_leb_size)) + vol->reserved_pebs = 1; + vol->reserved_pebs += bytes; + + /* Reserve physical eraseblocks */ + if (vol->reserved_pebs > ubi->avail_pebs) { + dbg_err("not enough PEBs, only %d available", ubi->avail_pebs); + err = -ENOSPC; + goto out_unlock; + } + ubi->avail_pebs -= vol->reserved_pebs; + ubi->rsvd_pebs += vol->reserved_pebs; + spin_unlock(&ubi->volumes_lock); + + vol->vol_id = vol_id; + vol->alignment = req->alignment; + vol->data_pad = ubi->leb_size % vol->alignment; + vol->vol_type = req->vol_type; + vol->name_len = req->name_len; + memcpy(vol->name, req->name, vol->name_len + 1); + vol->ubi = ubi; + + /* + * Finish all pending erases because there may be some LEBs belonging + * to the same volume ID. + */ + err = ubi_wl_flush(ubi); + if (err) + goto out_acc; + + vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL); + if (!vol->eba_tbl) { + err = -ENOMEM; + goto out_acc; + } + + for (i = 0; i < vol->reserved_pebs; i++) + vol->eba_tbl[i] = UBI_LEB_UNMAPPED; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = + (long long)vol->used_ebs * vol->usable_leb_size; + } else { + bytes = vol->used_bytes; + vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size); + vol->used_ebs = bytes; + if (vol->last_eb_bytes) + vol->used_ebs += 1; + else + vol->last_eb_bytes = vol->usable_leb_size; + } + + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); + vol->cdev.owner = THIS_MODULE; + dev = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1); + err = cdev_add(&vol->cdev, dev, 1); + if (err) { + ubi_err("cannot add character device"); + goto out_mapping; + } + + err = ubi_create_gluebi(ubi, vol); + if (err) + goto out_cdev; + + vol->dev.release = vol_release; + vol->dev.parent = &ubi->dev; + vol->dev.devt = dev; + vol->dev.class = ubi_class; + + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + err = device_register(&vol->dev); + if (err) { + ubi_err("cannot register device"); + goto out_gluebi; + } + + err = volume_sysfs_init(ubi, vol); + if (err) + goto out_sysfs; + + /* Fill volume table record */ + memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record)); + vtbl_rec.reserved_pebs = cpu_to_be32(vol->reserved_pebs); + vtbl_rec.alignment = cpu_to_be32(vol->alignment); + vtbl_rec.data_pad = cpu_to_be32(vol->data_pad); + vtbl_rec.name_len = cpu_to_be16(vol->name_len); + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + vtbl_rec.vol_type = UBI_VID_DYNAMIC; + else + vtbl_rec.vol_type = UBI_VID_STATIC; + memcpy(vtbl_rec.name, vol->name, vol->name_len + 1); + + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + goto out_sysfs; + + spin_lock(&ubi->volumes_lock); + ubi->volumes[vol_id] = vol; + ubi->vol_count += 1; + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); + return 0; + +out_sysfs: + /* + * We have registered our device, we should not free the volume* + * description object in this function in case of an error - it is + * freed by the release function. + * + * Get device reference to prevent the release function from being + * called just after sysfs has been closed. + */ + dont_free = 1; + get_device(&vol->dev); + volume_sysfs_close(vol); +out_gluebi: + if (ubi_destroy_gluebi(vol)) + dbg_err("cannot destroy gluebi for volume %d:%d", + ubi->ubi_num, vol_id); +out_cdev: + cdev_del(&vol->cdev); +out_mapping: + kfree(vol->eba_tbl); +out_acc: + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= vol->reserved_pebs; + ubi->avail_pebs += vol->reserved_pebs; +out_unlock: + spin_unlock(&ubi->volumes_lock); + if (dont_free) + put_device(&vol->dev); + else + kfree(vol); + ubi_err("cannot create volume %d, error %d", vol_id, err); + return err; +} + +/** + * ubi_remove_volume - remove volume. + * @desc: volume descriptor + * + * This function removes volume described by @desc. The volume has to be opened + * in "exclusive" mode. Returns zero in case of success and a negative error + * code in case of failure. The caller has to have the @ubi->volumes_mutex + * locked. + */ +int ubi_remove_volume(struct ubi_volume_desc *desc) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs; + + dbg_msg("remove UBI volume %d", vol_id); + ubi_assert(desc->mode == UBI_EXCLUSIVE); + ubi_assert(vol == ubi->volumes[vol_id]); + + if (ubi->ro_mode) + return -EROFS; + + spin_lock(&ubi->volumes_lock); + if (vol->ref_count > 1) { + /* + * The volume is busy, probably someone is reading one of its + * sysfs files. + */ + err = -EBUSY; + goto out_unlock; + } + ubi->volumes[vol_id] = NULL; + spin_unlock(&ubi->volumes_lock); + + err = ubi_destroy_gluebi(vol); + if (err) + goto out_err; + + err = ubi_change_vtbl_record(ubi, vol_id, NULL); + if (err) + goto out_err; + + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol, i); + if (err) + goto out_err; + } + + kfree(vol->eba_tbl); + vol->eba_tbl = NULL; + cdev_del(&vol->cdev); + volume_sysfs_close(vol); + + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= reserved_pebs; + ubi->avail_pebs += reserved_pebs; + i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; + if (i > 0) { + i = ubi->avail_pebs >= i ? i : ubi->avail_pebs; + ubi->avail_pebs -= i; + ubi->rsvd_pebs += i; + ubi->beb_rsvd_pebs += i; + if (i > 0) + ubi_msg("reserve more %d PEBs", i); + } + ubi->vol_count -= 1; + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); + return 0; + +out_err: + ubi_err("cannot remove volume %d, error %d", vol_id, err); + spin_lock(&ubi->volumes_lock); + ubi->volumes[vol_id] = vol; +out_unlock: + spin_unlock(&ubi->volumes_lock); + return err; +} + +/** + * ubi_resize_volume - re-size volume. + * @desc: volume descriptor + * @reserved_pebs: new size in physical eraseblocks + * + * This function re-sizes the volume and returns zero in case of success, and a + * negative error code in case of failure. The caller has to have the + * @ubi->volumes_mutex locked. + */ +int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) +{ + int i, err, pebs, *new_mapping; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + struct ubi_vtbl_record vtbl_rec; + int vol_id = vol->vol_id; + + if (ubi->ro_mode) + return -EROFS; + + dbg_msg("re-size volume %d to from %d to %d PEBs", + vol_id, vol->reserved_pebs, reserved_pebs); + + if (vol->vol_type == UBI_STATIC_VOLUME && + reserved_pebs < vol->used_ebs) { + dbg_err("too small size %d, %d LEBs contain data", + reserved_pebs, vol->used_ebs); + return -EINVAL; + } + + /* If the size is the same, we have nothing to do */ + if (reserved_pebs == vol->reserved_pebs) + return 0; + + new_mapping = kmalloc(reserved_pebs * sizeof(int), GFP_KERNEL); + if (!new_mapping) + return -ENOMEM; + + for (i = 0; i < reserved_pebs; i++) + new_mapping[i] = UBI_LEB_UNMAPPED; + + spin_lock(&ubi->volumes_lock); + if (vol->ref_count > 1) { + spin_unlock(&ubi->volumes_lock); + err = -EBUSY; + goto out_free; + } + spin_unlock(&ubi->volumes_lock); + + /* Reserve physical eraseblocks */ + pebs = reserved_pebs - vol->reserved_pebs; + if (pebs > 0) { + spin_lock(&ubi->volumes_lock); + if (pebs > ubi->avail_pebs) { + dbg_err("not enough PEBs: requested %d, available %d", + pebs, ubi->avail_pebs); + spin_unlock(&ubi->volumes_lock); + err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= pebs; + ubi->rsvd_pebs += pebs; + for (i = 0; i < vol->reserved_pebs; i++) + new_mapping[i] = vol->eba_tbl[i]; + kfree(vol->eba_tbl); + vol->eba_tbl = new_mapping; + spin_unlock(&ubi->volumes_lock); + } + + /* Change volume table record */ + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs); + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + goto out_acc; + + if (pebs < 0) { + for (i = 0; i < -pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i); + if (err) + goto out_acc; + } + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs += pebs; + ubi->avail_pebs -= pebs; + pebs = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; + if (pebs > 0) { + pebs = ubi->avail_pebs >= pebs ? pebs : ubi->avail_pebs; + ubi->avail_pebs -= pebs; + ubi->rsvd_pebs += pebs; + ubi->beb_rsvd_pebs += pebs; + if (pebs > 0) + ubi_msg("reserve more %d PEBs", pebs); + } + for (i = 0; i < reserved_pebs; i++) + new_mapping[i] = vol->eba_tbl[i]; + kfree(vol->eba_tbl); + vol->eba_tbl = new_mapping; + spin_unlock(&ubi->volumes_lock); + } + + vol->reserved_pebs = reserved_pebs; + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = + (long long)vol->used_ebs * vol->usable_leb_size; + } + + paranoid_check_volumes(ubi); + return 0; + +out_acc: + if (pebs > 0) { + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= pebs; + ubi->avail_pebs += pebs; + spin_unlock(&ubi->volumes_lock); + } +out_free: + kfree(new_mapping); + return err; +} + +/** + * ubi_add_volume - add volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function adds an existing volume and initializes all its data + * structures. Returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) +{ + int err, vol_id = vol->vol_id; + dev_t dev; + + dbg_msg("add volume %d", vol_id); + ubi_dbg_dump_vol_info(vol); + + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); + vol->cdev.owner = THIS_MODULE; + dev = MKDEV(MAJOR(ubi->cdev.dev), vol->vol_id + 1); + err = cdev_add(&vol->cdev, dev, 1); + if (err) { + ubi_err("cannot add character device for volume %d, error %d", + vol_id, err); + return err; + } + + err = ubi_create_gluebi(ubi, vol); + if (err) + goto out_cdev; + + vol->dev.release = vol_release; + vol->dev.parent = &ubi->dev; + vol->dev.devt = dev; + vol->dev.class = ubi_class; + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + err = device_register(&vol->dev); + if (err) + goto out_gluebi; + + err = volume_sysfs_init(ubi, vol); + if (err) { + cdev_del(&vol->cdev); + err = ubi_destroy_gluebi(vol); + volume_sysfs_close(vol); + return err; + } + + paranoid_check_volumes(ubi); + return 0; + +out_gluebi: + err = ubi_destroy_gluebi(vol); +out_cdev: + cdev_del(&vol->cdev); + return err; +} + +/** + * ubi_free_volume - free volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function frees all resources for volume @vol but does not remove it. + * Used only when the UBI device is detached. + */ +void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) +{ + dbg_msg("free volume %d", vol->vol_id); + + ubi->volumes[vol->vol_id] = NULL; + ubi_destroy_gluebi(vol); + cdev_del(&vol->cdev); + volume_sysfs_close(vol); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_volume - check volume information. + * @ubi: UBI device description object + * @vol_id: volume ID + */ +static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) +{ + int idx = vol_id2idx(ubi, vol_id); + int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker; + const struct ubi_volume *vol; + long long n; + const char *name; + + spin_lock(&ubi->volumes_lock); + reserved_pebs = be32_to_cpu(ubi->vtbl[vol_id].reserved_pebs); + vol = ubi->volumes[idx]; + + if (!vol) { + if (reserved_pebs) { + ubi_err("no volume info, but volume exists"); + goto fail; + } + spin_unlock(&ubi->volumes_lock); + return; + } + + if (vol->exclusive) { + /* + * The volume may be being created at the moment, do not check + * it (e.g., it may be in the middle of ubi_create_volume(). + */ + spin_unlock(&ubi->volumes_lock); + return; + } + + if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 || + vol->name_len < 0) { + ubi_err("negative values"); + goto fail; + } + if (vol->alignment > ubi->leb_size || vol->alignment == 0) { + ubi_err("bad alignment"); + goto fail; + } + + n = vol->alignment & (ubi->min_io_size - 1); + if (vol->alignment != 1 && n) { + ubi_err("alignment is not multiple of min I/O unit"); + goto fail; + } + + n = ubi->leb_size % vol->alignment; + if (vol->data_pad != n) { + ubi_err("bad data_pad, has to be %lld", n); + goto fail; + } + + if (vol->vol_type != UBI_DYNAMIC_VOLUME && + vol->vol_type != UBI_STATIC_VOLUME) { + ubi_err("bad vol_type"); + goto fail; + } + + if (vol->upd_marker && vol->corrupted) { + dbg_err("update marker and corrupted simultaneously"); + goto fail; + } + + if (vol->reserved_pebs > ubi->good_peb_count) { + ubi_err("too large reserved_pebs"); + goto fail; + } + + n = ubi->leb_size - vol->data_pad; + if (vol->usable_leb_size != ubi->leb_size - vol->data_pad) { + ubi_err("bad usable_leb_size, has to be %lld", n); + goto fail; + } + + if (vol->name_len > UBI_VOL_NAME_MAX) { + ubi_err("too long volume name, max is %d", UBI_VOL_NAME_MAX); + goto fail; + } + + if (!vol->name) { + ubi_err("NULL volume name"); + goto fail; + } + + n = strnlen(vol->name, vol->name_len + 1); + if (n != vol->name_len) { + ubi_err("bad name_len %lld", n); + goto fail; + } + + n = (long long)vol->used_ebs * vol->usable_leb_size; + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + if (vol->corrupted) { + ubi_err("corrupted dynamic volume"); + goto fail; + } + if (vol->used_ebs != vol->reserved_pebs) { + ubi_err("bad used_ebs"); + goto fail; + } + if (vol->last_eb_bytes != vol->usable_leb_size) { + ubi_err("bad last_eb_bytes"); + goto fail; + } + if (vol->used_bytes != n) { + ubi_err("bad used_bytes"); + goto fail; + } + } else { + if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) { + ubi_err("bad used_ebs"); + goto fail; + } + if (vol->last_eb_bytes < 0 || + vol->last_eb_bytes > vol->usable_leb_size) { + ubi_err("bad last_eb_bytes"); + goto fail; + } + if (vol->used_bytes < 0 || vol->used_bytes > n || + vol->used_bytes < n - vol->usable_leb_size) { + ubi_err("bad used_bytes"); + goto fail; + } + } + + alignment = be32_to_cpu(ubi->vtbl[vol_id].alignment); + data_pad = be32_to_cpu(ubi->vtbl[vol_id].data_pad); + name_len = be16_to_cpu(ubi->vtbl[vol_id].name_len); + upd_marker = ubi->vtbl[vol_id].upd_marker; + name = &ubi->vtbl[vol_id].name[0]; + if (ubi->vtbl[vol_id].vol_type == UBI_VID_DYNAMIC) + vol_type = UBI_DYNAMIC_VOLUME; + else + vol_type = UBI_STATIC_VOLUME; + + if (alignment != vol->alignment || data_pad != vol->data_pad || + upd_marker != vol->upd_marker || vol_type != vol->vol_type || + name_len!= vol->name_len || strncmp(name, vol->name, name_len)) { + ubi_err("volume info is different"); + goto fail; + } + + spin_unlock(&ubi->volumes_lock); + return; + +fail: + ubi_err("paranoid check failed for volume %d", vol_id); + ubi_dbg_dump_vol_info(vol); + ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id); + spin_unlock(&ubi->volumes_lock); + BUG(); +} + +/** + * paranoid_check_volumes - check information about all volumes. + * @ubi: UBI device description object + */ +static void paranoid_check_volumes(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) + paranoid_check_volume(ubi, i); +} +#endif diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/vtbl.c b/qemu/roms/u-boot/drivers/mtd/ubi/vtbl.c new file mode 100644 index 000000000..3fbb4a0a9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/vtbl.c @@ -0,0 +1,826 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * This file includes volume table manipulation code. The volume table is an + * on-flash table containing volume meta-data like name, number of reserved + * physical eraseblocks, type, etc. The volume table is stored in the so-called + * "layout volume". + * + * The layout volume is an internal volume which is organized as follows. It + * consists of two logical eraseblocks - LEB 0 and LEB 1. Each logical + * eraseblock stores one volume table copy, i.e. LEB 0 and LEB 1 duplicate each + * other. This redundancy guarantees robustness to unclean reboots. The volume + * table is basically an array of volume table records. Each record contains + * full information about the volume and protected by a CRC checksum. + * + * The volume table is changed, it is first changed in RAM. Then LEB 0 is + * erased, and the updated volume table is written back to LEB 0. Then same for + * LEB 1. This scheme guarantees recoverability from unclean reboots. + * + * In this UBI implementation the on-flash volume table does not contain any + * information about how many data static volumes contain. This information may + * be found from the scanning data. + * + * But it would still be beneficial to store this information in the volume + * table. For example, suppose we have a static volume X, and all its physical + * eraseblocks became bad for some reasons. Suppose we are attaching the + * corresponding MTD device, the scanning has found no logical eraseblocks + * corresponding to the volume X. According to the volume table volume X does + * exist. So we don't know whether it is just empty or all its physical + * eraseblocks went bad. So we cannot alarm the user about this corruption. + * + * The volume table also stores so-called "update marker", which is used for + * volume updates. Before updating the volume, the update marker is set, and + * after the update operation is finished, the update marker is cleared. So if + * the update operation was interrupted (e.g. by an unclean reboot) - the + * update marker is still there and we know that the volume's contents is + * damaged. + */ + +#ifdef UBI_LINUX +#include +#include +#include +#endif + +#include +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static void paranoid_vtbl_check(const struct ubi_device *ubi); +#else +#define paranoid_vtbl_check(ubi) +#endif + +/* Empty volume table record */ +static struct ubi_vtbl_record empty_vtbl_record; + +/** + * ubi_change_vtbl_record - change volume table record. + * @ubi: UBI device description object + * @idx: table index to change + * @vtbl_rec: new volume table record + * + * This function changes volume table record @idx. If @vtbl_rec is %NULL, empty + * volume table record is written. The caller does not have to calculate CRC of + * the record as it is done by this function. Returns zero in case of success + * and a negative error code in case of failure. + */ +int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, + struct ubi_vtbl_record *vtbl_rec) +{ + int i, err; + uint32_t crc; + struct ubi_volume *layout_vol; + + ubi_assert(idx >= 0 && idx < ubi->vtbl_slots); + layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)]; + + if (!vtbl_rec) + vtbl_rec = &empty_vtbl_record; + else { + crc = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); + vtbl_rec->crc = cpu_to_be32(crc); + } + + memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record)); + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + err = ubi_eba_unmap_leb(ubi, layout_vol, i); + if (err) + return err; + + err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0, + ubi->vtbl_size, UBI_LONGTERM); + if (err) + return err; + } + + paranoid_vtbl_check(ubi); + return 0; +} + +/** + * vtbl_check - check if volume table is not corrupted and contains sensible + * data. + * @ubi: UBI device description object + * @vtbl: volume table + * + * This function returns zero if @vtbl is all right, %1 if CRC is incorrect, + * and %-EINVAL if it contains inconsistent data. + */ +static int vtbl_check(const struct ubi_device *ubi, + const struct ubi_vtbl_record *vtbl) +{ + int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len; + int upd_marker, err; + uint32_t crc; + const char *name; + + for (i = 0; i < ubi->vtbl_slots; i++) { + cond_resched(); + + reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs); + alignment = be32_to_cpu(vtbl[i].alignment); + data_pad = be32_to_cpu(vtbl[i].data_pad); + upd_marker = vtbl[i].upd_marker; + vol_type = vtbl[i].vol_type; + name_len = be16_to_cpu(vtbl[i].name_len); + name = (const char *) &vtbl[i].name[0]; + + crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC); + if (be32_to_cpu(vtbl[i].crc) != crc) { + ubi_err("bad CRC at record %u: %#08x, not %#08x", + i, crc, be32_to_cpu(vtbl[i].crc)); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + return 1; + } + + if (reserved_pebs == 0) { + if (memcmp(&vtbl[i], &empty_vtbl_record, + UBI_VTBL_RECORD_SIZE)) { + err = 2; + goto bad; + } + continue; + } + + if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 || + name_len < 0) { + err = 3; + goto bad; + } + + if (alignment > ubi->leb_size || alignment == 0) { + err = 4; + goto bad; + } + + n = alignment & (ubi->min_io_size - 1); + if (alignment != 1 && n) { + err = 5; + goto bad; + } + + n = ubi->leb_size % alignment; + if (data_pad != n) { + dbg_err("bad data_pad, has to be %d", n); + err = 6; + goto bad; + } + + if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { + err = 7; + goto bad; + } + + if (upd_marker != 0 && upd_marker != 1) { + err = 8; + goto bad; + } + + if (reserved_pebs > ubi->good_peb_count) { + dbg_err("too large reserved_pebs, good PEBs %d", + ubi->good_peb_count); + err = 9; + goto bad; + } + + if (name_len > UBI_VOL_NAME_MAX) { + err = 10; + goto bad; + } + + if (name[0] == '\0') { + err = 11; + goto bad; + } + + if (name_len != strnlen(name, name_len + 1)) { + err = 12; + goto bad; + } + } + + /* Checks that all names are unique */ + for (i = 0; i < ubi->vtbl_slots - 1; i++) { + for (n = i + 1; n < ubi->vtbl_slots; n++) { + int len1 = be16_to_cpu(vtbl[i].name_len); + int len2 = be16_to_cpu(vtbl[n].name_len); + + if (len1 > 0 && len1 == len2 && + !strncmp((char *)vtbl[i].name, (char *)vtbl[n].name, len1)) { + ubi_err("volumes %d and %d have the same name" + " \"%s\"", i, n, vtbl[i].name); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + ubi_dbg_dump_vtbl_record(&vtbl[n], n); + return -EINVAL; + } + } + } + + return 0; + +bad: + ubi_err("volume table check failed: record %d, error %d", i, err); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + return -EINVAL; +} + +/** + * create_vtbl - create a copy of volume table. + * @ubi: UBI device description object + * @si: scanning information + * @copy: number of the volume table copy + * @vtbl: contents of the volume table + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si, + int copy, void *vtbl) +{ + int err, tries = 0; + static struct ubi_vid_hdr *vid_hdr; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *new_seb, *old_seb = NULL; + + ubi_msg("create volume table (copy #%d)", copy + 1); + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + if (!vid_hdr) + return -ENOMEM; + + /* + * Check if there is a logical eraseblock which would have to contain + * this volume table copy was found during scanning. It has to be wiped + * out. + */ + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); + if (sv) + old_seb = ubi_scan_find_seb(sv, copy); + +retry: + new_seb = ubi_scan_get_free_peb(ubi, si); + if (IS_ERR(new_seb)) { + err = PTR_ERR(new_seb); + goto out_free; + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOLUME_ID); + vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT; + vid_hdr->data_size = vid_hdr->used_ebs = + vid_hdr->data_pad = cpu_to_be32(0); + vid_hdr->lnum = cpu_to_be32(copy); + vid_hdr->sqnum = cpu_to_be64(++si->max_sqnum); + vid_hdr->leb_ver = cpu_to_be32(old_seb ? old_seb->leb_ver + 1: 0); + + /* The EC header is already there, write the VID header */ + err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr); + if (err) + goto write_error; + + /* Write the layout volume contents */ + err = ubi_io_write_data(ubi, vtbl, new_seb->pnum, 0, ubi->vtbl_size); + if (err) + goto write_error; + + /* + * And add it to the scanning information. Don't delete the old + * @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'. + */ + err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec, + vid_hdr, 0); + kfree(new_seb); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + if (err == -EIO && ++tries <= 5) { + /* + * Probably this physical eraseblock went bad, try to pick + * another one. + */ + list_add_tail(&new_seb->u.list, &si->corr); + goto retry; + } + kfree(new_seb); +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +} + +/** + * process_lvol - process the layout volume. + * @ubi: UBI device description object + * @si: scanning information + * @sv: layout volume scanning information + * + * This function is responsible for reading the layout volume, ensuring it is + * not corrupted, and recovering from corruptions if needed. Returns volume + * table in case of success and a negative error code in case of failure. + */ +static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, + struct ubi_scan_info *si, + struct ubi_scan_volume *sv) +{ + int err; + struct rb_node *rb; + struct ubi_scan_leb *seb; + struct ubi_vtbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL }; + int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = {1, 1}; + + /* + * UBI goes through the following steps when it changes the layout + * volume: + * a. erase LEB 0; + * b. write new data to LEB 0; + * c. erase LEB 1; + * d. write new data to LEB 1. + * + * Before the change, both LEBs contain the same data. + * + * Due to unclean reboots, the contents of LEB 0 may be lost, but there + * should LEB 1. So it is OK if LEB 0 is corrupted while LEB 1 is not. + * Similarly, LEB 1 may be lost, but there should be LEB 0. And + * finally, unclean reboots may result in a situation when neither LEB + * 0 nor LEB 1 are corrupted, but they are different. In this case, LEB + * 0 contains more recent information. + * + * So the plan is to first check LEB 0. Then + * a. if LEB 0 is OK, it must be containing the most resent data; then + * we compare it with LEB 1, and if they are different, we copy LEB + * 0 to LEB 1; + * b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1 + * to LEB 0. + */ + + dbg_msg("check layout volume"); + + /* Read both LEB 0 and LEB 1 into memory */ + ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { + leb[seb->lnum] = vmalloc(ubi->vtbl_size); + if (!leb[seb->lnum]) { + err = -ENOMEM; + goto out_free; + } + memset(leb[seb->lnum], 0, ubi->vtbl_size); + + err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0, + ubi->vtbl_size); + if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err)) + /* + * Scrub the PEB later. Note, -EBADMSG indicates an + * uncorrectable ECC error, but we have our own CRC and + * the data will be checked later. If the data is OK, + * the PEB will be scrubbed (because we set + * seb->scrub). If the data is not OK, the contents of + * the PEB will be recovered from the second copy, and + * seb->scrub will be cleared in + * 'ubi_scan_add_used()'. + */ + seb->scrub = 1; + else if (err) + goto out_free; + } + + err = -EINVAL; + if (leb[0]) { + leb_corrupted[0] = vtbl_check(ubi, leb[0]); + if (leb_corrupted[0] < 0) + goto out_free; + } + + if (!leb_corrupted[0]) { + /* LEB 0 is OK */ + if (leb[1]) + leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size); + if (leb_corrupted[1]) { + ubi_warn("volume table copy #2 is corrupted"); + err = create_vtbl(ubi, si, 1, leb[0]); + if (err) + goto out_free; + ubi_msg("volume table was restored"); + } + + /* Both LEB 1 and LEB 2 are OK and consistent */ + vfree(leb[1]); + return leb[0]; + } else { + /* LEB 0 is corrupted or does not exist */ + if (leb[1]) { + leb_corrupted[1] = vtbl_check(ubi, leb[1]); + if (leb_corrupted[1] < 0) + goto out_free; + } + if (leb_corrupted[1]) { + /* Both LEB 0 and LEB 1 are corrupted */ + ubi_err("both volume tables are corrupted"); + goto out_free; + } + + ubi_warn("volume table copy #1 is corrupted"); + err = create_vtbl(ubi, si, 0, leb[1]); + if (err) + goto out_free; + ubi_msg("volume table was restored"); + + vfree(leb[0]); + return leb[1]; + } + +out_free: + vfree(leb[0]); + vfree(leb[1]); + return ERR_PTR(err); +} + +/** + * create_empty_lvol - create empty layout volume. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns volume table contents in case of success and a + * negative error code in case of failure. + */ +static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int i; + struct ubi_vtbl_record *vtbl; + + vtbl = vmalloc(ubi->vtbl_size); + if (!vtbl) + return ERR_PTR(-ENOMEM); + memset(vtbl, 0, ubi->vtbl_size); + + for (i = 0; i < ubi->vtbl_slots; i++) + memcpy(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE); + + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + int err; + + err = create_vtbl(ubi, si, i, vtbl); + if (err) { + vfree(vtbl); + return ERR_PTR(err); + } + } + + return vtbl; +} + +/** + * init_volumes - initialize volume information for existing volumes. + * @ubi: UBI device description object + * @si: scanning information + * @vtbl: volume table + * + * This function allocates volume description objects for existing volumes. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, + const struct ubi_vtbl_record *vtbl) +{ + int i, reserved_pebs = 0; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + + for (i = 0; i < ubi->vtbl_slots; i++) { + cond_resched(); + + if (be32_to_cpu(vtbl[i].reserved_pebs) == 0) + continue; /* Empty record */ + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + vol->reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs); + vol->alignment = be32_to_cpu(vtbl[i].alignment); + vol->data_pad = be32_to_cpu(vtbl[i].data_pad); + vol->upd_marker = vtbl[i].upd_marker; + vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + vol->name_len = be16_to_cpu(vtbl[i].name_len); + vol->usable_leb_size = ubi->leb_size - vol->data_pad; + memcpy(vol->name, vtbl[i].name, vol->name_len); + vol->name[vol->name_len] = '\0'; + vol->vol_id = i; + + if (vtbl[i].flags & UBI_VTBL_AUTORESIZE_FLG) { + /* Auto re-size flag may be set only for one volume */ + if (ubi->autoresize_vol_id != -1) { + ubi_err("more then one auto-resize volume (%d " + "and %d)", ubi->autoresize_vol_id, i); + kfree(vol); + return -EINVAL; + } + + ubi->autoresize_vol_id = i; + } + + ubi_assert(!ubi->volumes[i]); + ubi->volumes[i] = vol; + ubi->vol_count += 1; + vol->ubi = ubi; + reserved_pebs += vol->reserved_pebs; + + /* + * In case of dynamic volume UBI knows nothing about how many + * data is stored there. So assume the whole volume is used. + */ + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = + (long long)vol->used_ebs * vol->usable_leb_size; + continue; + } + + /* Static volumes only */ + sv = ubi_scan_find_sv(si, i); + if (!sv) { + /* + * No eraseblocks belonging to this volume found. We + * don't actually know whether this static volume is + * completely corrupted or just contains no data. And + * we cannot know this as long as data size is not + * stored on flash. So we just assume the volume is + * empty. FIXME: this should be handled. + */ + continue; + } + + if (sv->leb_count != sv->used_ebs) { + /* + * We found a static volume which misses several + * eraseblocks. Treat it as corrupted. + */ + ubi_warn("static volume %d misses %d LEBs - corrupted", + sv->vol_id, sv->used_ebs - sv->leb_count); + vol->corrupted = 1; + continue; + } + + vol->used_ebs = sv->used_ebs; + vol->used_bytes = + (long long)(vol->used_ebs - 1) * vol->usable_leb_size; + vol->used_bytes += sv->last_data_size; + vol->last_eb_bytes = sv->last_data_size; + } + + /* And add the layout volume */ + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + vol->reserved_pebs = UBI_LAYOUT_VOLUME_EBS; + vol->alignment = 1; + vol->vol_type = UBI_DYNAMIC_VOLUME; + vol->name_len = sizeof(UBI_LAYOUT_VOLUME_NAME) - 1; + memcpy(vol->name, UBI_LAYOUT_VOLUME_NAME, vol->name_len + 1); + vol->usable_leb_size = ubi->leb_size; + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->reserved_pebs; + vol->used_bytes = + (long long)vol->used_ebs * (ubi->leb_size - vol->data_pad); + vol->vol_id = UBI_LAYOUT_VOLUME_ID; + vol->ref_count = 1; + + ubi_assert(!ubi->volumes[i]); + ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol; + reserved_pebs += vol->reserved_pebs; + ubi->vol_count += 1; + vol->ubi = ubi; + + if (reserved_pebs > ubi->avail_pebs) + ubi_err("not enough PEBs, required %d, available %d", + reserved_pebs, ubi->avail_pebs); + ubi->rsvd_pebs += reserved_pebs; + ubi->avail_pebs -= reserved_pebs; + + return 0; +} + +/** + * check_sv - check volume scanning information. + * @vol: UBI volume description object + * @sv: volume scanning information + * + * This function returns zero if the volume scanning information is consistent + * to the data read from the volume tabla, and %-EINVAL if not. + */ +static int check_sv(const struct ubi_volume *vol, + const struct ubi_scan_volume *sv) +{ + int err; + + if (sv->highest_lnum >= vol->reserved_pebs) { + err = 1; + goto bad; + } + if (sv->leb_count > vol->reserved_pebs) { + err = 2; + goto bad; + } + if (sv->vol_type != vol->vol_type) { + err = 3; + goto bad; + } + if (sv->used_ebs > vol->reserved_pebs) { + err = 4; + goto bad; + } + if (sv->data_pad != vol->data_pad) { + err = 5; + goto bad; + } + return 0; + +bad: + ubi_err("bad scanning information, error %d", err); + ubi_dbg_dump_sv(sv); + ubi_dbg_dump_vol_info(vol); + return -EINVAL; +} + +/** + * check_scanning_info - check that scanning information. + * @ubi: UBI device description object + * @si: scanning information + * + * Even though we protect on-flash data by CRC checksums, we still don't trust + * the media. This function ensures that scanning information is consistent to + * the information read from the volume table. Returns zero if the scanning + * information is OK and %-EINVAL if it is not. + */ +static int check_scanning_info(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int err, i; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + + if (si->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) { + ubi_err("scanning found %d volumes, maximum is %d + %d", + si->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots); + return -EINVAL; + } + + if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT && + si->highest_vol_id < UBI_INTERNAL_VOL_START) { + ubi_err("too large volume ID %d found by scanning", + si->highest_vol_id); + return -EINVAL; + } + + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { + cond_resched(); + + sv = ubi_scan_find_sv(si, i); + vol = ubi->volumes[i]; + if (!vol) { + if (sv) + ubi_scan_rm_volume(si, sv); + continue; + } + + if (vol->reserved_pebs == 0) { + ubi_assert(i < ubi->vtbl_slots); + + if (!sv) + continue; + + /* + * During scanning we found a volume which does not + * exist according to the information in the volume + * table. This must have happened due to an unclean + * reboot while the volume was being removed. Discard + * these eraseblocks. + */ + ubi_msg("finish volume %d removal", sv->vol_id); + ubi_scan_rm_volume(si, sv); + } else if (sv) { + err = check_sv(vol, sv); + if (err) + return err; + } + } + + return 0; +} + +/** + * ubi_read_volume_table - read volume table. + * information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function reads volume table, checks it, recover from errors if needed, + * or creates it if needed. Returns zero in case of success and a negative + * error code in case of failure. + */ +int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int i, err; + struct ubi_scan_volume *sv; + + empty_vtbl_record.crc = cpu_to_be32(0xf116c36b); + + /* + * The number of supported volumes is limited by the eraseblock size + * and by the UBI_MAX_VOLUMES constant. + */ + ubi->vtbl_slots = ubi->leb_size / UBI_VTBL_RECORD_SIZE; + if (ubi->vtbl_slots > UBI_MAX_VOLUMES) + ubi->vtbl_slots = UBI_MAX_VOLUMES; + + ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE; + ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size); + + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); + if (!sv) { + /* + * No logical eraseblocks belonging to the layout volume were + * found. This could mean that the flash is just empty. In + * this case we create empty layout volume. + * + * But if flash is not empty this must be a corruption or the + * MTD device just contains garbage. + */ + if (si->is_empty) { + ubi->vtbl = create_empty_lvol(ubi, si); + if (IS_ERR(ubi->vtbl)) + return PTR_ERR(ubi->vtbl); + } else { + ubi_err("the layout volume was not found"); + return -EINVAL; + } + } else { + if (sv->leb_count > UBI_LAYOUT_VOLUME_EBS) { + /* This must not happen with proper UBI images */ + dbg_err("too many LEBs (%d) in layout volume", + sv->leb_count); + return -EINVAL; + } + + ubi->vtbl = process_lvol(ubi, si, sv); + if (IS_ERR(ubi->vtbl)) + return PTR_ERR(ubi->vtbl); + } + + ubi->avail_pebs = ubi->good_peb_count; + + /* + * The layout volume is OK, initialize the corresponding in-RAM data + * structures. + */ + err = init_volumes(ubi, si, ubi->vtbl); + if (err) + goto out_free; + + /* + * Get sure that the scanning information is consistent to the + * information stored in the volume table. + */ + err = check_scanning_info(ubi, si); + if (err) + goto out_free; + + return 0; + +out_free: + vfree(ubi->vtbl); + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) + if (ubi->volumes[i]) { + kfree(ubi->volumes[i]); + ubi->volumes[i] = NULL; + } + return err; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_vtbl_check - check volume table. + * @ubi: UBI device description object + */ +static void paranoid_vtbl_check(const struct ubi_device *ubi) +{ + if (vtbl_check(ubi, ubi->vtbl)) { + ubi_err("paranoid check failed"); + BUG(); + } +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/qemu/roms/u-boot/drivers/mtd/ubi/wl.c b/qemu/roms/u-boot/drivers/mtd/ubi/wl.c new file mode 100644 index 000000000..1eaa88b36 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/ubi/wl.c @@ -0,0 +1,1664 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Authors: Artem Bityutskiy (Битюцкий Артём), Thomas Gleixner + */ + +/* + * UBI wear-leveling unit. + * + * This unit is responsible for wear-leveling. It works in terms of physical + * eraseblocks and erase counters and knows nothing about logical eraseblocks, + * volumes, etc. From this unit's perspective all physical eraseblocks are of + * two types - used and free. Used physical eraseblocks are those that were + * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are + * those that were put by the 'ubi_wl_put_peb()' function. + * + * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter + * header. The rest of the physical eraseblock contains only 0xFF bytes. + * + * When physical eraseblocks are returned to the WL unit by means of the + * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is + * done asynchronously in context of the per-UBI device background thread, + * which is also managed by the WL unit. + * + * The wear-leveling is ensured by means of moving the contents of used + * physical eraseblocks with low erase counter to free physical eraseblocks + * with high erase counter. + * + * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick + * an "optimal" physical eraseblock. For example, when it is known that the + * physical eraseblock will be "put" soon because it contains short-term data, + * the WL unit may pick a free physical eraseblock with low erase counter, and + * so forth. + * + * If the WL unit fails to erase a physical eraseblock, it marks it as bad. + * + * This unit is also responsible for scrubbing. If a bit-flip is detected in a + * physical eraseblock, it has to be moved. Technically this is the same as + * moving it for wear-leveling reasons. + * + * As it was said, for the UBI unit all physical eraseblocks are either "free" + * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used + * eraseblocks are kept in a set of different RB-trees: @wl->used, + * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub. + * + * Note, in this implementation, we keep a small in-RAM object for each physical + * eraseblock. This is surely not a scalable solution. But it appears to be good + * enough for moderately large flashes and it is simple. In future, one may + * re-work this unit and make it more scalable. + * + * At the moment this unit does not utilize the sequence number, which was + * introduced relatively recently. But it would be wise to do this because the + * sequence number of a logical eraseblock characterizes how old is it. For + * example, when we move a PEB with low erase counter, and we need to pick the + * target PEB, we pick a PEB with the highest EC if our PEB is "old" and we + * pick target PEB with an average EC if our PEB is not very "old". This is a + * room for future re-works of the WL unit. + * + * FIXME: looks too complex, should be simplified (later). + */ + +#ifdef UBI_LINUX +#include +#include +#include +#include +#endif + +#include +#include "ubi.h" + +/* Number of physical eraseblocks reserved for wear-leveling purposes */ +#define WL_RESERVED_PEBS 1 + +/* + * How many erase cycles are short term, unknown, and long term physical + * eraseblocks protected. + */ +#define ST_PROTECTION 16 +#define U_PROTECTION 10 +#define LT_PROTECTION 4 + +/* + * Maximum difference between two erase counters. If this threshold is + * exceeded, the WL unit starts moving data from used physical eraseblocks with + * low erase counter to free physical eraseblocks with high erase counter. + */ +#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD + +/* + * When a physical eraseblock is moved, the WL unit has to pick the target + * physical eraseblock to move to. The simplest way would be just to pick the + * one with the highest erase counter. But in certain workloads this could lead + * to an unlimited wear of one or few physical eraseblock. Indeed, imagine a + * situation when the picked physical eraseblock is constantly erased after the + * data is written to it. So, we have a constant which limits the highest erase + * counter of the free physical eraseblock to pick. Namely, the WL unit does + * not pick eraseblocks with erase counter greater then the lowest erase + * counter plus %WL_FREE_MAX_DIFF. + */ +#define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD) + +/* + * Maximum number of consecutive background thread failures which is enough to + * switch to read-only mode. + */ +#define WL_MAX_FAILURES 32 + +/** + * struct ubi_wl_prot_entry - PEB protection entry. + * @rb_pnum: link in the @wl->prot.pnum RB-tree + * @rb_aec: link in the @wl->prot.aec RB-tree + * @abs_ec: the absolute erase counter value when the protection ends + * @e: the wear-leveling entry of the physical eraseblock under protection + * + * When the WL unit returns a physical eraseblock, the physical eraseblock is + * protected from being moved for some "time". For this reason, the physical + * eraseblock is not directly moved from the @wl->free tree to the @wl->used + * tree. There is one more tree in between where this physical eraseblock is + * temporarily stored (@wl->prot). + * + * All this protection stuff is needed because: + * o we don't want to move physical eraseblocks just after we have given them + * to the user; instead, we first want to let users fill them up with data; + * + * o there is a chance that the user will put the physical eraseblock very + * soon, so it makes sense not to move it for some time, but wait; this is + * especially important in case of "short term" physical eraseblocks. + * + * Physical eraseblocks stay protected only for limited time. But the "time" is + * measured in erase cycles in this case. This is implemented with help of the + * absolute erase counter (@wl->abs_ec). When it reaches certain value, the + * physical eraseblocks are moved from the protection trees (@wl->prot.*) to + * the @wl->used tree. + * + * Protected physical eraseblocks are searched by physical eraseblock number + * (when they are put) and by the absolute erase counter (to check if it is + * time to move them to the @wl->used tree). So there are actually 2 RB-trees + * storing the protected physical eraseblocks: @wl->prot.pnum and + * @wl->prot.aec. They are referred to as the "protection" trees. The + * first one is indexed by the physical eraseblock number. The second one is + * indexed by the absolute erase counter. Both trees store + * &struct ubi_wl_prot_entry objects. + * + * Each physical eraseblock has 2 main states: free and used. The former state + * corresponds to the @wl->free tree. The latter state is split up on several + * sub-states: + * o the WL movement is allowed (@wl->used tree); + * o the WL movement is temporarily prohibited (@wl->prot.pnum and + * @wl->prot.aec trees); + * o scrubbing is needed (@wl->scrub tree). + * + * Depending on the sub-state, wear-leveling entries of the used physical + * eraseblocks may be kept in one of those trees. + */ +struct ubi_wl_prot_entry { + struct rb_node rb_pnum; + struct rb_node rb_aec; + unsigned long long abs_ec; + struct ubi_wl_entry *e; +}; + +/** + * struct ubi_work - UBI work description data structure. + * @list: a link in the list of pending works + * @func: worker function + * @priv: private data of the worker function + * + * @e: physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * The @func pointer points to the worker function. If the @cancel argument is + * not zero, the worker has to free the resources and exit immediately. The + * worker has to return zero in case of success and a negative error code in + * case of failure. + */ +struct ubi_work { + struct list_head list; + int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel); + /* The below fields are only relevant to erasure works */ + struct ubi_wl_entry *e; + int torture; +}; + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec); +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, + struct rb_root *root); +#else +#define paranoid_check_ec(ubi, pnum, ec) 0 +#define paranoid_check_in_wl_tree(e, root) +#endif + +/** + * wl_tree_add - add a wear-leveling entry to a WL RB-tree. + * @e: the wear-leveling entry to add + * @root: the root of the tree + * + * Note, we use (erase counter, physical eraseblock number) pairs as keys in + * the @ubi->used and @ubi->free RB-trees. + */ +static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node **p, *parent = NULL; + + p = &root->rb_node; + while (*p) { + struct ubi_wl_entry *e1; + + parent = *p; + e1 = rb_entry(parent, struct ubi_wl_entry, rb); + + if (e->ec < e1->ec) + p = &(*p)->rb_left; + else if (e->ec > e1->ec) + p = &(*p)->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&e->rb, parent, p); + rb_insert_color(&e->rb, root); +} + +/** + * do_work - do one pending work. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int do_work(struct ubi_device *ubi) +{ + int err; + struct ubi_work *wrk; + + cond_resched(); + + /* + * @ubi->work_sem is used to synchronize with the workers. Workers take + * it in read mode, so many of them may be doing works at a time. But + * the queue flush code has to be sure the whole queue of works is + * done, and it takes the mutex in write mode. + */ + down_read(&ubi->work_sem); + spin_lock(&ubi->wl_lock); + if (list_empty(&ubi->works)) { + spin_unlock(&ubi->wl_lock); + up_read(&ubi->work_sem); + return 0; + } + + wrk = list_entry(ubi->works.next, struct ubi_work, list); + list_del(&wrk->list); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); + spin_unlock(&ubi->wl_lock); + + /* + * Call the worker function. Do not touch the work structure + * after this call as it will have been freed or reused by that + * time by the worker function. + */ + err = wrk->func(ubi, wrk, 0); + if (err) + ubi_err("work failed with error code %d", err); + up_read(&ubi->work_sem); + + return err; +} + +/** + * produce_free_peb - produce a free physical eraseblock. + * @ubi: UBI device description object + * + * This function tries to make a free PEB by means of synchronous execution of + * pending works. This may be needed if, for example the background thread is + * disabled. Returns zero in case of success and a negative error code in case + * of failure. + */ +static int produce_free_peb(struct ubi_device *ubi) +{ + int err; + + spin_lock(&ubi->wl_lock); + while (!ubi->free.rb_node) { + spin_unlock(&ubi->wl_lock); + + dbg_wl("do one work synchronously"); + err = do_work(ubi); + if (err) + return err; + + spin_lock(&ubi->wl_lock); + } + spin_unlock(&ubi->wl_lock); + + return 0; +} + +/** + * in_wl_tree - check if wear-leveling entry is present in a WL RB-tree. + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns non-zero if @e is in the @root RB-tree and zero if it + * is not. + */ +static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node *p; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + + if (e->pnum == e1->pnum) { + ubi_assert(e == e1); + return 1; + } + + if (e->ec < e1->ec) + p = p->rb_left; + else if (e->ec > e1->ec) + p = p->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + } + + return 0; +} + +/** + * prot_tree_add - add physical eraseblock to protection trees. + * @ubi: UBI device description object + * @e: the physical eraseblock to add + * @pe: protection entry object to use + * @abs_ec: absolute erase counter value when this physical eraseblock has + * to be removed from the protection trees. + * + * @wl->lock has to be locked. + */ +static void prot_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e, + struct ubi_wl_prot_entry *pe, int abs_ec) +{ + struct rb_node **p, *parent = NULL; + struct ubi_wl_prot_entry *pe1; + + pe->e = e; + pe->abs_ec = ubi->abs_ec + abs_ec; + + p = &ubi->prot.pnum.rb_node; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_pnum); + + if (e->pnum < pe1->e->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_pnum, parent, p); + rb_insert_color(&pe->rb_pnum, &ubi->prot.pnum); + + p = &ubi->prot.aec.rb_node; + parent = NULL; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec < pe1->abs_ec) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_aec, parent, p); + rb_insert_color(&pe->rb_aec, &ubi->prot.aec); +} + +/** + * find_wl_entry - find wear-leveling entry closest to certain erase counter. + * @root: the RB-tree where to look for + * @max: highest possible erase counter + * + * This function looks for a wear leveling entry with erase counter closest to + * @max and less then @max. + */ +static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) +{ + struct rb_node *p; + struct ubi_wl_entry *e; + + e = rb_entry(rb_first(root), struct ubi_wl_entry, rb); + max += e->ec; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + if (e1->ec >= max) + p = p->rb_left; + else { + p = p->rb_right; + e = e1; + } + } + + return e; +} + +/** + * ubi_wl_get_peb - get a physical eraseblock. + * @ubi: UBI device description object + * @dtype: type of data which will be stored in this physical eraseblock + * + * This function returns a physical eraseblock in case of success and a + * negative error code in case of failure. Might sleep. + */ +int ubi_wl_get_peb(struct ubi_device *ubi, int dtype) +{ + int err, protect, medium_ec; + struct ubi_wl_entry *e, *first, *last; + struct ubi_wl_prot_entry *pe; + + ubi_assert(dtype == UBI_LONGTERM || dtype == UBI_SHORTTERM || + dtype == UBI_UNKNOWN); + + pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); + if (!pe) + return -ENOMEM; + +retry: + spin_lock(&ubi->wl_lock); + if (!ubi->free.rb_node) { + if (ubi->works_count == 0) { + ubi_assert(list_empty(&ubi->works)); + ubi_err("no free eraseblocks"); + spin_unlock(&ubi->wl_lock); + kfree(pe); + return -ENOSPC; + } + spin_unlock(&ubi->wl_lock); + + err = produce_free_peb(ubi); + if (err < 0) { + kfree(pe); + return err; + } + goto retry; + } + + switch (dtype) { + case UBI_LONGTERM: + /* + * For long term data we pick a physical eraseblock + * with high erase counter. But the highest erase + * counter we can pick is bounded by the the lowest + * erase counter plus %WL_FREE_MAX_DIFF. + */ + e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + protect = LT_PROTECTION; + break; + case UBI_UNKNOWN: + /* + * For unknown data we pick a physical eraseblock with + * medium erase counter. But we by no means can pick a + * physical eraseblock with erase counter greater or + * equivalent than the lowest erase counter plus + * %WL_FREE_MAX_DIFF. + */ + first = rb_entry(rb_first(&ubi->free), + struct ubi_wl_entry, rb); + last = rb_entry(rb_last(&ubi->free), + struct ubi_wl_entry, rb); + + if (last->ec - first->ec < WL_FREE_MAX_DIFF) + e = rb_entry(ubi->free.rb_node, + struct ubi_wl_entry, rb); + else { + medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2; + e = find_wl_entry(&ubi->free, medium_ec); + } + protect = U_PROTECTION; + break; + case UBI_SHORTTERM: + /* + * For short term data we pick a physical eraseblock + * with the lowest erase counter as we expect it will + * be erased soon. + */ + e = rb_entry(rb_first(&ubi->free), + struct ubi_wl_entry, rb); + protect = ST_PROTECTION; + break; + default: + protect = 0; + e = NULL; + BUG(); + } + + /* + * Move the physical eraseblock to the protection trees where it will + * be protected from being moved for some time. + */ + paranoid_check_in_wl_tree(e, &ubi->free); + rb_erase(&e->rb, &ubi->free); + prot_tree_add(ubi, e, pe, protect); + + dbg_wl("PEB %d EC %d, protection %d", e->pnum, e->ec, protect); + spin_unlock(&ubi->wl_lock); + + return e->pnum; +} + +/** + * prot_tree_del - remove a physical eraseblock from the protection trees + * @ubi: UBI device description object + * @pnum: the physical eraseblock to remove + * + * This function returns PEB @pnum from the protection trees and returns zero + * in case of success and %-ENODEV if the PEB was not found in the protection + * trees. + */ +static int prot_tree_del(struct ubi_device *ubi, int pnum) +{ + struct rb_node *p; + struct ubi_wl_prot_entry *pe = NULL; + + p = ubi->prot.pnum.rb_node; + while (p) { + + pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum); + + if (pnum == pe->e->pnum) + goto found; + + if (pnum < pe->e->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + + return -ENODEV; + +found: + ubi_assert(pe->e->pnum == pnum); + rb_erase(&pe->rb_aec, &ubi->prot.aec); + rb_erase(&pe->rb_pnum, &ubi->prot.pnum); + kfree(pe); + return 0; +} + +/** + * sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @e: the the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + unsigned long long ec = e->ec; + + dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec); + + err = paranoid_check_ec(ubi, e->pnum, e->ec); + if (err > 0) + return -EINVAL; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_sync_erase(ubi, e->pnum, torture); + if (err < 0) + goto out_free; + + ec += err; + if (ec > UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. Upgrade UBI and use 64-bit + * erase counters internally. + */ + ubi_err("erase counter overflow at PEB %d, EC %llu", + e->pnum, ec); + err = -EINVAL; + goto out_free; + } + + dbg_wl("erased PEB %d, new EC %llu", e->pnum, ec); + + ec_hdr->ec = cpu_to_be64(ec); + + err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); + if (err) + goto out_free; + + e->ec = ec; + spin_lock(&ubi->wl_lock); + if (e->ec > ubi->max_ec) + ubi->max_ec = e->ec; + spin_unlock(&ubi->wl_lock); + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * check_protection_over - check if it is time to stop protecting some + * physical eraseblocks. + * @ubi: UBI device description object + * + * This function is called after each erase operation, when the absolute erase + * counter is incremented, to check if some physical eraseblock have not to be + * protected any longer. These physical eraseblocks are moved from the + * protection trees to the used tree. + */ +static void check_protection_over(struct ubi_device *ubi) +{ + struct ubi_wl_prot_entry *pe; + + /* + * There may be several protected physical eraseblock to remove, + * process them all. + */ + while (1) { + spin_lock(&ubi->wl_lock); + if (!ubi->prot.aec.rb_node) { + spin_unlock(&ubi->wl_lock); + break; + } + + pe = rb_entry(rb_first(&ubi->prot.aec), + struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec > ubi->abs_ec) { + spin_unlock(&ubi->wl_lock); + break; + } + + dbg_wl("PEB %d protection over, abs_ec %llu, PEB abs_ec %llu", + pe->e->pnum, ubi->abs_ec, pe->abs_ec); + rb_erase(&pe->rb_aec, &ubi->prot.aec); + rb_erase(&pe->rb_pnum, &ubi->prot.pnum); + wl_tree_add(pe->e, &ubi->used); + spin_unlock(&ubi->wl_lock); + + kfree(pe); + cond_resched(); + } +} + +/** + * schedule_ubi_work - schedule a work. + * @ubi: UBI device description object + * @wrk: the work to schedule + * + * This function enqueues a work defined by @wrk to the tail of the pending + * works list. + */ +static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk) +{ + spin_lock(&ubi->wl_lock); + list_add_tail(&wrk->list, &ubi->works); + ubi_assert(ubi->works_count >= 0); + ubi->works_count += 1; + + /* + * U-Boot special: We have no bgt_thread in U-Boot! + * So just call do_work() here directly. + */ + do_work(ubi); + + spin_unlock(&ubi->wl_lock); +} + +static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + int cancel); + +/** + * schedule_erase - schedule an erase work. + * @ubi: UBI device description object + * @e: the WL entry of the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a %-ENOMEM in case of + * failure. + */ +static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, + int torture) +{ + struct ubi_work *wl_wrk; + + dbg_wl("schedule erasure of PEB %d, EC %d, torture %d", + e->pnum, e->ec, torture); + + wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS); + if (!wl_wrk) + return -ENOMEM; + + wl_wrk->func = &erase_worker; + wl_wrk->e = e; + wl_wrk->torture = torture; + + schedule_ubi_work(ubi, wl_wrk); + return 0; +} + +/** + * wear_leveling_worker - wear-leveling worker function. + * @ubi: UBI device description object + * @wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function copies a more worn out physical eraseblock to a less worn out + * one. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + int cancel) +{ + int err, put = 0, scrubbing = 0, protect = 0; + struct ubi_wl_prot_entry *uninitialized_var(pe); + struct ubi_wl_entry *e1, *e2; + struct ubi_vid_hdr *vid_hdr; + + kfree(wrk); + + if (cancel) + return 0; + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; + + mutex_lock(&ubi->move_mutex); + spin_lock(&ubi->wl_lock); + ubi_assert(!ubi->move_from && !ubi->move_to); + ubi_assert(!ubi->move_to_put); + + if (!ubi->free.rb_node || + (!ubi->used.rb_node && !ubi->scrub.rb_node)) { + /* + * No free physical eraseblocks? Well, they must be waiting in + * the queue to be erased. Cancel movement - it will be + * triggered again when a free physical eraseblock appears. + * + * No used physical eraseblocks? They must be temporarily + * protected from being moved. They will be moved to the + * @ubi->used tree later and the wear-leveling will be + * triggered again. + */ + dbg_wl("cancel WL, a list is empty: free %d, used %d", + !ubi->free.rb_node, !ubi->used.rb_node); + goto out_cancel; + } + + if (!ubi->scrub.rb_node) { + /* + * Now pick the least worn-out used physical eraseblock and a + * highly worn-out free physical eraseblock. If the erase + * counters differ much enough, start wear-leveling. + */ + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { + dbg_wl("no WL needed: min used EC %d, max free EC %d", + e1->ec, e2->ec); + goto out_cancel; + } + paranoid_check_in_wl_tree(e1, &ubi->used); + rb_erase(&e1->rb, &ubi->used); + dbg_wl("move PEB %d EC %d to PEB %d EC %d", + e1->pnum, e1->ec, e2->pnum, e2->ec); + } else { + /* Perform scrubbing */ + scrubbing = 1; + e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + paranoid_check_in_wl_tree(e1, &ubi->scrub); + rb_erase(&e1->rb, &ubi->scrub); + dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum); + } + + paranoid_check_in_wl_tree(e2, &ubi->free); + rb_erase(&e2->rb, &ubi->free); + ubi->move_from = e1; + ubi->move_to = e2; + spin_unlock(&ubi->wl_lock); + + /* + * Now we are going to copy physical eraseblock @e1->pnum to @e2->pnum. + * We so far do not know which logical eraseblock our physical + * eraseblock (@e1) belongs to. We have to read the volume identifier + * header first. + * + * Note, we are protected from this PEB being unmapped and erased. The + * 'ubi_wl_put_peb()' would wait for moving to be finished if the PEB + * which is being moved was unmapped. + */ + + err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); + if (err && err != UBI_IO_BITFLIPS) { + if (err == UBI_IO_PEB_FREE) { + /* + * We are trying to move PEB without a VID header. UBI + * always write VID headers shortly after the PEB was + * given, so we have a situation when it did not have + * chance to write it down because it was preempted. + * Just re-schedule the work, so that next time it will + * likely have the VID header in place. + */ + dbg_wl("PEB %d has no VID header", e1->pnum); + goto out_not_moved; + } + + ubi_err("error %d while reading VID header from PEB %d", + err, e1->pnum); + if (err > 0) + err = -EIO; + goto out_error; + } + + err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); + if (err) { + + if (err < 0) + goto out_error; + if (err == 1) + goto out_not_moved; + + /* + * For some reason the LEB was not moved - it might be because + * the volume is being deleted. We should prevent this PEB from + * being selected for wear-levelling movement for some "time", + * so put it to the protection tree. + */ + + dbg_wl("cancelled moving PEB %d", e1->pnum); + pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); + if (!pe) { + err = -ENOMEM; + goto out_error; + } + + protect = 1; + } + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + if (protect) + prot_tree_add(ubi, e1, pe, protect); + if (!ubi->move_to_put) + wl_tree_add(e2, &ubi->used); + else + put = 1; + ubi->move_from = ubi->move_to = NULL; + ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + if (put) { + /* + * Well, the target PEB was put meanwhile, schedule it for + * erasure. + */ + dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); + err = schedule_erase(ubi, e2, 0); + if (err) + goto out_error; + } + + if (!protect) { + err = schedule_erase(ubi, e1, 0); + if (err) + goto out_error; + } + + + dbg_wl("done"); + mutex_unlock(&ubi->move_mutex); + return 0; + + /* + * For some reasons the LEB was not moved, might be an error, might be + * something else. @e1 was not changed, so return it back. @e2 might + * be changed, schedule it for erasure. + */ +out_not_moved: + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + if (scrubbing) + wl_tree_add(e1, &ubi->scrub); + else + wl_tree_add(e1, &ubi->used); + ubi->move_from = ubi->move_to = NULL; + ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + err = schedule_erase(ubi, e2, 0); + if (err) + goto out_error; + + mutex_unlock(&ubi->move_mutex); + return 0; + +out_error: + ubi_err("error %d while moving PEB %d to PEB %d", + err, e1->pnum, e2->pnum); + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + ubi->move_from = ubi->move_to = NULL; + ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + kmem_cache_free(ubi_wl_entry_slab, e1); + kmem_cache_free(ubi_wl_entry_slab, e2); + ubi_ro_mode(ubi); + + mutex_unlock(&ubi->move_mutex); + return err; + +out_cancel: + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + mutex_unlock(&ubi->move_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; +} + +/** + * ensure_wear_leveling - schedule wear-leveling if it is needed. + * @ubi: UBI device description object + * + * This function checks if it is time to start wear-leveling and schedules it + * if yes. This function returns zero in case of success and a negative error + * code in case of failure. + */ +static int ensure_wear_leveling(struct ubi_device *ubi) +{ + int err = 0; + struct ubi_wl_entry *e1; + struct ubi_wl_entry *e2; + struct ubi_work *wrk; + + spin_lock(&ubi->wl_lock); + if (ubi->wl_scheduled) + /* Wear-leveling is already in the work queue */ + goto out_unlock; + + /* + * If the ubi->scrub tree is not empty, scrubbing is needed, and the + * the WL worker has to be scheduled anyway. + */ + if (!ubi->scrub.rb_node) { + if (!ubi->used.rb_node || !ubi->free.rb_node) + /* No physical eraseblocks - no deal */ + goto out_unlock; + + /* + * We schedule wear-leveling only if the difference between the + * lowest erase counter of used physical eraseblocks and a high + * erase counter of free physical eraseblocks is greater then + * %UBI_WL_THRESHOLD. + */ + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) + goto out_unlock; + dbg_wl("schedule wear-leveling"); + } else + dbg_wl("schedule scrubbing"); + + ubi->wl_scheduled = 1; + spin_unlock(&ubi->wl_lock); + + wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS); + if (!wrk) { + err = -ENOMEM; + goto out_cancel; + } + + wrk->func = &wear_leveling_worker; + schedule_ubi_work(ubi, wrk); + return err; + +out_cancel: + spin_lock(&ubi->wl_lock); + ubi->wl_scheduled = 0; +out_unlock: + spin_unlock(&ubi->wl_lock); + return err; +} + +/** + * erase_worker - physical eraseblock erase worker function. + * @ubi: UBI device description object + * @wl_wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function erases a physical eraseblock and perform torture testing if + * needed. It also takes care about marking the physical eraseblock bad if + * needed. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + int cancel) +{ + struct ubi_wl_entry *e = wl_wrk->e; + int pnum = e->pnum, err, need; + + if (cancel) { + dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); + kfree(wl_wrk); + kmem_cache_free(ubi_wl_entry_slab, e); + return 0; + } + + dbg_wl("erase PEB %d EC %d", pnum, e->ec); + + err = sync_erase(ubi, e, wl_wrk->torture); + if (!err) { + /* Fine, we've erased it successfully */ + kfree(wl_wrk); + + spin_lock(&ubi->wl_lock); + ubi->abs_ec += 1; + wl_tree_add(e, &ubi->free); + spin_unlock(&ubi->wl_lock); + + /* + * One more erase operation has happened, take care about protected + * physical eraseblocks. + */ + check_protection_over(ubi); + + /* And take care about wear-leveling */ + err = ensure_wear_leveling(ubi); + return err; + } + + ubi_err("failed to erase PEB %d, error %d", pnum, err); + kfree(wl_wrk); + kmem_cache_free(ubi_wl_entry_slab, e); + + if (err == -EINTR || err == -ENOMEM || err == -EAGAIN || + err == -EBUSY) { + int err1; + + /* Re-schedule the LEB for erasure */ + err1 = schedule_erase(ubi, e, 0); + if (err1) { + err = err1; + goto out_ro; + } + return err; + } else if (err != -EIO) { + /* + * If this is not %-EIO, we have no idea what to do. Scheduling + * this physical eraseblock for erasure again would cause + * errors again and again. Well, lets switch to RO mode. + */ + goto out_ro; + } + + /* It is %-EIO, the PEB went bad */ + + if (!ubi->bad_allowed) { + ubi_err("bad physical eraseblock %d detected", pnum); + goto out_ro; + } + + spin_lock(&ubi->volumes_lock); + need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs + 1; + if (need > 0) { + need = ubi->avail_pebs >= need ? need : ubi->avail_pebs; + ubi->avail_pebs -= need; + ubi->rsvd_pebs += need; + ubi->beb_rsvd_pebs += need; + if (need > 0) + ubi_msg("reserve more %d PEBs", need); + } + + if (ubi->beb_rsvd_pebs == 0) { + spin_unlock(&ubi->volumes_lock); + ubi_err("no reserved physical eraseblocks"); + goto out_ro; + } + + spin_unlock(&ubi->volumes_lock); + ubi_msg("mark PEB %d as bad", pnum); + + err = ubi_io_mark_bad(ubi, pnum); + if (err) + goto out_ro; + + spin_lock(&ubi->volumes_lock); + ubi->beb_rsvd_pebs -= 1; + ubi->bad_peb_count += 1; + ubi->good_peb_count -= 1; + ubi_calculate_reserved(ubi); + if (ubi->beb_rsvd_pebs == 0) + ubi_warn("last PEB from the reserved pool was used"); + spin_unlock(&ubi->volumes_lock); + + return err; + +out_ro: + ubi_ro_mode(ubi); + return err; +} + +/** + * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit. + * @ubi: UBI device description object + * @pnum: physical eraseblock to return + * @torture: if this physical eraseblock has to be tortured + * + * This function is called to return physical eraseblock @pnum to the pool of + * free physical eraseblocks. The @torture flag has to be set if an I/O error + * occurred to this @pnum and it has to be tested. This function returns zero + * in case of success, and a negative error code in case of failure. + */ +int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) +{ + int err; + struct ubi_wl_entry *e; + + dbg_wl("PEB %d", pnum); + ubi_assert(pnum >= 0); + ubi_assert(pnum < ubi->peb_count); + +retry: + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + if (e == ubi->move_from) { + /* + * User is putting the physical eraseblock which was selected to + * be moved. It will be scheduled for erasure in the + * wear-leveling worker. + */ + dbg_wl("PEB %d is being moved, wait", pnum); + spin_unlock(&ubi->wl_lock); + + /* Wait for the WL worker by taking the @ubi->move_mutex */ + mutex_lock(&ubi->move_mutex); + mutex_unlock(&ubi->move_mutex); + goto retry; + } else if (e == ubi->move_to) { + /* + * User is putting the physical eraseblock which was selected + * as the target the data is moved to. It may happen if the EBA + * unit already re-mapped the LEB in 'ubi_eba_copy_leb()' but + * the WL unit has not put the PEB to the "used" tree yet, but + * it is about to do this. So we just set a flag which will + * tell the WL worker that the PEB is not needed anymore and + * should be scheduled for erasure. + */ + dbg_wl("PEB %d is the target of data moving", pnum); + ubi_assert(!ubi->move_to_put); + ubi->move_to_put = 1; + spin_unlock(&ubi->wl_lock); + return 0; + } else { + if (in_wl_tree(e, &ubi->used)) { + paranoid_check_in_wl_tree(e, &ubi->used); + rb_erase(&e->rb, &ubi->used); + } else if (in_wl_tree(e, &ubi->scrub)) { + paranoid_check_in_wl_tree(e, &ubi->scrub); + rb_erase(&e->rb, &ubi->scrub); + } else { + err = prot_tree_del(ubi, e->pnum); + if (err) { + ubi_err("PEB %d not found", pnum); + ubi_ro_mode(ubi); + spin_unlock(&ubi->wl_lock); + return err; + } + } + } + spin_unlock(&ubi->wl_lock); + + err = schedule_erase(ubi, e, torture); + if (err) { + spin_lock(&ubi->wl_lock); + wl_tree_add(e, &ubi->used); + spin_unlock(&ubi->wl_lock); + } + + return err; +} + +/** + * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to schedule + * + * If a bit-flip in a physical eraseblock is detected, this physical eraseblock + * needs scrubbing. This function schedules a physical eraseblock for + * scrubbing which is done in background. This function returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum) +{ + struct ubi_wl_entry *e; + + ubi_msg("schedule PEB %d for scrubbing", pnum); + +retry: + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + if (e == ubi->move_from || in_wl_tree(e, &ubi->scrub)) { + spin_unlock(&ubi->wl_lock); + return 0; + } + + if (e == ubi->move_to) { + /* + * This physical eraseblock was used to move data to. The data + * was moved but the PEB was not yet inserted to the proper + * tree. We should just wait a little and let the WL worker + * proceed. + */ + spin_unlock(&ubi->wl_lock); + dbg_wl("the PEB %d is not in proper tree, retry", pnum); + yield(); + goto retry; + } + + if (in_wl_tree(e, &ubi->used)) { + paranoid_check_in_wl_tree(e, &ubi->used); + rb_erase(&e->rb, &ubi->used); + } else { + int err; + + err = prot_tree_del(ubi, e->pnum); + if (err) { + ubi_err("PEB %d not found", pnum); + ubi_ro_mode(ubi); + spin_unlock(&ubi->wl_lock); + return err; + } + } + + wl_tree_add(e, &ubi->scrub); + spin_unlock(&ubi->wl_lock); + + /* + * Technically scrubbing is the same as wear-leveling, so it is done + * by the WL worker. + */ + return ensure_wear_leveling(ubi); +} + +/** + * ubi_wl_flush - flush all pending works. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_wl_flush(struct ubi_device *ubi) +{ + int err; + + /* + * Erase while the pending works queue is not empty, but not more then + * the number of currently pending works. + */ + dbg_wl("flush (%d pending works)", ubi->works_count); + while (ubi->works_count) { + err = do_work(ubi); + if (err) + return err; + } + + /* + * Make sure all the works which have been done in parallel are + * finished. + */ + down_write(&ubi->work_sem); + up_write(&ubi->work_sem); + + /* + * And in case last was the WL worker and it cancelled the LEB + * movement, flush again. + */ + while (ubi->works_count) { + dbg_wl("flush more (%d pending works)", ubi->works_count); + err = do_work(ubi); + if (err) + return err; + } + + return 0; +} + +/** + * tree_destroy - destroy an RB-tree. + * @root: the root of the tree to destroy + */ +static void tree_destroy(struct rb_root *root) +{ + struct rb_node *rb; + struct ubi_wl_entry *e; + + rb = root->rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + e = rb_entry(rb, struct ubi_wl_entry, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &e->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + kmem_cache_free(ubi_wl_entry_slab, e); + } + } +} + +/** + * ubi_thread - UBI background thread. + * @u: the UBI device description object pointer + */ +int ubi_thread(void *u) +{ + int failures = 0; + struct ubi_device *ubi = u; + + ubi_msg("background thread \"%s\" started, PID %d", + ubi->bgt_name, task_pid_nr(current)); + + set_freezable(); + for (;;) { + int err; + + if (kthread_should_stop()) + break; + + if (try_to_freeze()) + continue; + + spin_lock(&ubi->wl_lock); + if (list_empty(&ubi->works) || ubi->ro_mode || + !ubi->thread_enabled) { + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock(&ubi->wl_lock); + schedule(); + continue; + } + spin_unlock(&ubi->wl_lock); + + err = do_work(ubi); + if (err) { + ubi_err("%s: work failed with error code %d", + ubi->bgt_name, err); + if (failures++ > WL_MAX_FAILURES) { + /* + * Too many failures, disable the thread and + * switch to read-only mode. + */ + ubi_msg("%s: %d consecutive failures", + ubi->bgt_name, WL_MAX_FAILURES); + ubi_ro_mode(ubi); + break; + } + } else + failures = 0; + + cond_resched(); + } + + dbg_wl("background thread \"%s\" is killed", ubi->bgt_name); + return 0; +} + +/** + * cancel_pending - cancel all pending works. + * @ubi: UBI device description object + */ +static void cancel_pending(struct ubi_device *ubi) +{ + while (!list_empty(&ubi->works)) { + struct ubi_work *wrk; + + wrk = list_entry(ubi->works.next, struct ubi_work, list); + list_del(&wrk->list); + wrk->func(ubi, wrk, 1); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); + } +} + +/** + * ubi_wl_init_scan - initialize the wear-leveling unit using scanning + * information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int err; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb, *tmp; + struct ubi_wl_entry *e; + + + ubi->used = ubi->free = ubi->scrub = RB_ROOT; + ubi->prot.pnum = ubi->prot.aec = RB_ROOT; + spin_lock_init(&ubi->wl_lock); + mutex_init(&ubi->move_mutex); + init_rwsem(&ubi->work_sem); + ubi->max_ec = si->max_ec; + INIT_LIST_HEAD(&ubi->works); + + sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); + + err = -ENOMEM; + ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL); + if (!ubi->lookuptbl) + return err; + + list_for_each_entry_safe(seb, tmp, &si->erase, u.list) { + cond_resched(); + + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { + kmem_cache_free(ubi_wl_entry_slab, e); + goto out_free; + } + } + + list_for_each_entry(seb, &si->free, u.list) { + cond_resched(); + + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi_assert(e->ec >= 0); + wl_tree_add(e, &ubi->free); + ubi->lookuptbl[e->pnum] = e; + } + + list_for_each_entry(seb, &si->corr, u.list) { + cond_resched(); + + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { + kmem_cache_free(ubi_wl_entry_slab, e); + goto out_free; + } + } + + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + cond_resched(); + + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (!seb->scrub) { + dbg_wl("add PEB %d EC %d to the used tree", + e->pnum, e->ec); + wl_tree_add(e, &ubi->used); + } else { + dbg_wl("add PEB %d EC %d to the scrub tree", + e->pnum, e->ec); + wl_tree_add(e, &ubi->scrub); + } + } + } + + if (ubi->avail_pebs < WL_RESERVED_PEBS) { + ubi_err("no enough physical eraseblocks (%d, need %d)", + ubi->avail_pebs, WL_RESERVED_PEBS); + err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= WL_RESERVED_PEBS; + ubi->rsvd_pebs += WL_RESERVED_PEBS; + + /* Schedule wear-leveling if needed */ + err = ensure_wear_leveling(ubi); + if (err) + goto out_free; + + return 0; + +out_free: + cancel_pending(ubi); + tree_destroy(&ubi->used); + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); + return err; +} + +/** + * protection_trees_destroy - destroy the protection RB-trees. + * @ubi: UBI device description object + */ +static void protection_trees_destroy(struct ubi_device *ubi) +{ + struct rb_node *rb; + struct ubi_wl_prot_entry *pe; + + rb = ubi->prot.aec.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + pe = rb_entry(rb, struct ubi_wl_prot_entry, rb_aec); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &pe->rb_aec) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + kmem_cache_free(ubi_wl_entry_slab, pe->e); + kfree(pe); + } + } +} + +/** + * ubi_wl_close - close the wear-leveling unit. + * @ubi: UBI device description object + */ +void ubi_wl_close(struct ubi_device *ubi) +{ + dbg_wl("close the UBI wear-leveling unit"); + + cancel_pending(ubi); + protection_trees_destroy(ubi); + tree_destroy(&ubi->used); + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_ec - make sure that the erase counter of a physical eraseblock + * is correct. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * @ec: the erase counter to check + * + * This function returns zero if the erase counter of physical eraseblock @pnum + * is equivalent to @ec, %1 if not, and a negative error code if an error + * occurred. + */ +static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec) +{ + int err; + long long read_ec; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0); + if (err && err != UBI_IO_BITFLIPS) { + /* The header does not have to exist */ + err = 0; + goto out_free; + } + + read_ec = be64_to_cpu(ec_hdr->ec); + if (ec != read_ec) { + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_err("read EC is %lld, should be %d", read_ec, ec); + ubi_dbg_dump_stack(); + err = 1; + } else + err = 0; + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present + * in a WL RB-tree. + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns zero if @e is in the @root RB-tree and %1 if it + * is not. + */ +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, + struct rb_root *root) +{ + if (in_wl_tree(e, root)) + return 0; + + ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ", + e->pnum, e->ec, root); + ubi_dbg_dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/qemu/roms/u-boot/drivers/net/4xx_enet.c b/qemu/roms/u-boot/drivers/net/4xx_enet.c new file mode 100644 index 000000000..381ec4286 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/4xx_enet.c @@ -0,0 +1,2059 @@ +/* + * SPDX-License-Identifier: GPL-2.0 IBM-pibs + */ +/*-----------------------------------------------------------------------------+ + * + * File Name: enetemac.c + * + * Function: Device driver for the ethernet EMAC3 macro on the 405GP. + * + * Author: Mark Wisner + * + * Change Activity- + * + * Date Description of Change BY + * --------- --------------------- --- + * 05-May-99 Created MKW + * 27-Jun-99 Clean up JWB + * 16-Jul-99 Added MAL error recovery and better IP packet handling MKW + * 29-Jul-99 Added Full duplex support MKW + * 06-Aug-99 Changed names for Mal CR reg MKW + * 23-Aug-99 Turned off SYE when running at 10Mbs MKW + * 24-Aug-99 Marked descriptor empty after call_xlc MKW + * 07-Sep-99 Set MAL RX buffer size reg to ENET_MAX_MTU_ALIGNED / 16 MCG + * to avoid chaining maximum sized packets. Push starting + * RX descriptor address up to the next cache line boundary. + * 16-Jan-00 Added support for booting with IP of 0x0 MKW + * 15-Mar-00 Updated enetInit() to enable broadcast addresses in the + * EMAC0_RXM register. JWB + * 12-Mar-01 anne-sophie.harnois@nextream.fr + * - Variables are compatible with those already defined in + * include/net.h + * - Receive buffer descriptor ring is used to send buffers + * to the user + * - Info print about send/received/handled packet number if + * INFO_405_ENET is set + * 17-Apr-01 stefan.roese@esd-electronics.com + * - MAL reset in "eth_halt" included + * - Enet speed and duplex output now in one line + * 08-May-01 stefan.roese@esd-electronics.com + * - MAL error handling added (eth_init called again) + * 13-Nov-01 stefan.roese@esd-electronics.com + * - Set IST bit in EMAC0_MR1 reg upon 100MBit or full duplex + * 04-Jan-02 stefan.roese@esd-electronics.com + * - Wait for PHY auto negotiation to complete added + * 06-Feb-02 stefan.roese@esd-electronics.com + * - Bug fixed in waiting for auto negotiation to complete + * 26-Feb-02 stefan.roese@esd-electronics.com + * - rx and tx buffer descriptors now allocated (no fixed address + * used anymore) + * 17-Jun-02 stefan.roese@esd-electronics.com + * - MAL error debug printf 'M' removed (rx de interrupt may + * occur upon many incoming packets with only 4 rx buffers). + *-----------------------------------------------------------------------------* + * 17-Nov-03 travis.sawyer@sandburst.com + * - ported from 405gp_enet.c to utilized upto 4 EMAC ports + * in the 440GX. This port should work with the 440GP + * (2 EMACs) also + * 15-Aug-05 sr@denx.de + * - merged 405gp_enet.c and 440gx_enet.c to generic 4xx_enet.c + now handling all 4xx cpu's. + *-----------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */ +#define PHY_AUTONEGOTIATE_TIMEOUT 5000 /* 5000 ms autonegotiate timeout */ + +/* Ethernet Transmit and Receive Buffers */ +/* AS.HARNOIS + * In the same way ENET_MAX_MTU and ENET_MAX_MTU_ALIGNED are set from + * PKTSIZE and PKTSIZE_ALIGN (include/net.h) + */ +#define ENET_MAX_MTU PKTSIZE +#define ENET_MAX_MTU_ALIGNED PKTSIZE_ALIGN + +/*-----------------------------------------------------------------------------+ + * Defines for MAL/EMAC interrupt conditions as reported in the UIC (Universal + * Interrupt Controller). + *-----------------------------------------------------------------------------*/ +#define ETH_IRQ_NUM(dev) (VECNUM_ETH0 + ((dev) * VECNUM_ETH1_OFFS)) + +#if defined(CONFIG_HAS_ETH3) +#if !defined(CONFIG_440GX) +#define UIC_ETHx (UIC_MASK(ETH_IRQ_NUM(0)) || UIC_MASK(ETH_IRQ_NUM(1)) || \ + UIC_MASK(ETH_IRQ_NUM(2)) || UIC_MASK(ETH_IRQ_NUM(3))) +#else +/* Unfortunately 440GX spreads EMAC interrupts on multiple UIC's */ +#define UIC_ETHx (UIC_MASK(ETH_IRQ_NUM(0)) || UIC_MASK(ETH_IRQ_NUM(1))) +#define UIC_ETHxB (UIC_MASK(ETH_IRQ_NUM(2)) || UIC_MASK(ETH_IRQ_NUM(3))) +#endif /* !defined(CONFIG_440GX) */ +#elif defined(CONFIG_HAS_ETH2) +#define UIC_ETHx (UIC_MASK(ETH_IRQ_NUM(0)) || UIC_MASK(ETH_IRQ_NUM(1)) || \ + UIC_MASK(ETH_IRQ_NUM(2))) +#elif defined(CONFIG_HAS_ETH1) +#define UIC_ETHx (UIC_MASK(ETH_IRQ_NUM(0)) || UIC_MASK(ETH_IRQ_NUM(1))) +#else +#define UIC_ETHx UIC_MASK(ETH_IRQ_NUM(0)) +#endif + +/* + * Define a default version for UIC_ETHxB for non 440GX so that we can + * use common code for all 4xx variants + */ +#if !defined(UIC_ETHxB) +#define UIC_ETHxB 0 +#endif + +#define UIC_MAL_SERR UIC_MASK(VECNUM_MAL_SERR) +#define UIC_MAL_TXDE UIC_MASK(VECNUM_MAL_TXDE) +#define UIC_MAL_RXDE UIC_MASK(VECNUM_MAL_RXDE) +#define UIC_MAL_TXEOB UIC_MASK(VECNUM_MAL_TXEOB) +#define UIC_MAL_RXEOB UIC_MASK(VECNUM_MAL_RXEOB) + +#define MAL_UIC_ERR (UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE) +#define MAL_UIC_DEF (UIC_MAL_RXEOB | MAL_UIC_ERR) + +/* + * We have 3 different interrupt types: + * - MAL interrupts indicating successful transfer + * - MAL error interrupts indicating MAL related errors + * - EMAC interrupts indicating EMAC related errors + * + * All those interrupts can be on different UIC's, but since + * now at least all interrupts from one type are on the same + * UIC. Only exception is 440GX where the EMAC interrupts are + * spread over two UIC's! + */ +#if defined(CONFIG_440GX) +#define UIC_BASE_MAL UIC1_DCR_BASE +#define UIC_BASE_MAL_ERR UIC2_DCR_BASE +#define UIC_BASE_EMAC UIC2_DCR_BASE +#define UIC_BASE_EMAC_B UIC3_DCR_BASE +#else +#define UIC_BASE_MAL (UIC0_DCR_BASE + (UIC_NR(VECNUM_MAL_TXEOB) * 0x10)) +#define UIC_BASE_MAL_ERR (UIC0_DCR_BASE + (UIC_NR(VECNUM_MAL_SERR) * 0x10)) +#define UIC_BASE_EMAC (UIC0_DCR_BASE + (UIC_NR(ETH_IRQ_NUM(0)) * 0x10)) +#define UIC_BASE_EMAC_B (UIC0_DCR_BASE + (UIC_NR(ETH_IRQ_NUM(0)) * 0x10)) +#endif + +#undef INFO_4XX_ENET + +#define BI_PHYMODE_NONE 0 +#define BI_PHYMODE_ZMII 1 +#define BI_PHYMODE_RGMII 2 +#define BI_PHYMODE_GMII 3 +#define BI_PHYMODE_RTBI 4 +#define BI_PHYMODE_TBI 5 +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) +#define BI_PHYMODE_SMII 6 +#define BI_PHYMODE_MII 7 +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define BI_PHYMODE_RMII 8 +#endif +#endif +#define BI_PHYMODE_SGMII 9 + +#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) +#define SDR0_MFR_ETH_CLK_SEL_V(n) ((0x01<<27) / (n+1)) +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define SDR0_ETH_CFG_CLK_SEL_V(n) (0x01 << (8 + n)) +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define MAL_RX_CHAN_MUL 8 /* 460EX/GT uses MAL channel 8 for EMAC1 */ +#else +#define MAL_RX_CHAN_MUL 1 +#endif + +/*--------------------------------------------------------------------+ + * Fixed PHY (PHY-less) support for Ethernet Ports. + *--------------------------------------------------------------------*/ + +/* + * Some boards do not have a PHY for each ethernet port. These ports + * are known as Fixed PHY (or PHY-less) ports. For such ports, set + * the appropriate CONFIG_PHY_ADDR equal to CONFIG_FIXED_PHY and + * then define CONFIG_SYS_FIXED_PHY_PORTS to define what the speed and + * duplex should be for these ports in the board configuration + * file. + * + * For Example: + * #define CONFIG_FIXED_PHY 0xFFFFFFFF + * + * #define CONFIG_PHY_ADDR CONFIG_FIXED_PHY + * #define CONFIG_PHY1_ADDR 1 + * #define CONFIG_PHY2_ADDR CONFIG_FIXED_PHY + * #define CONFIG_PHY3_ADDR 3 + * + * #define CONFIG_SYS_FIXED_PHY_PORT(devnum,speed,duplex) \ + * {devnum, speed, duplex}, + * + * #define CONFIG_SYS_FIXED_PHY_PORTS \ + * CONFIG_SYS_FIXED_PHY_PORT(0,1000,FULL) \ + * CONFIG_SYS_FIXED_PHY_PORT(2,100,HALF) + */ + +#ifndef CONFIG_FIXED_PHY +#define CONFIG_FIXED_PHY 0xFFFFFFFF /* Fixed PHY (PHY-less) */ +#endif + +#ifndef CONFIG_SYS_FIXED_PHY_PORTS +#define CONFIG_SYS_FIXED_PHY_PORTS /* default is an empty array */ +#endif + +struct fixed_phy_port { + unsigned int devnum; /* ethernet port */ + unsigned int speed; /* specified speed 10,100 or 1000 */ + unsigned int duplex; /* specified duplex FULL or HALF */ +}; + +static const struct fixed_phy_port fixed_phy_port[] = { + CONFIG_SYS_FIXED_PHY_PORTS /* defined in board configuration file */ +}; + +/*-----------------------------------------------------------------------------+ + * Global variables. TX and RX descriptors and buffers. + *-----------------------------------------------------------------------------*/ + +/* + * Get count of EMAC devices (doesn't have to be the max. possible number + * supported by the cpu) + * + * CONFIG_BOARD_EMAC_COUNT added so now a "dynamic" way to configure the + * EMAC count is possible. As it is needed for the Kilauea/Haleakala + * 405EX/405EXr eval board, using the same binary. + */ +#if defined(CONFIG_BOARD_EMAC_COUNT) +#define LAST_EMAC_NUM board_emac_count() +#else /* CONFIG_BOARD_EMAC_COUNT */ +#if defined(CONFIG_HAS_ETH3) +#define LAST_EMAC_NUM 4 +#elif defined(CONFIG_HAS_ETH2) +#define LAST_EMAC_NUM 3 +#elif defined(CONFIG_HAS_ETH1) +#define LAST_EMAC_NUM 2 +#else +#define LAST_EMAC_NUM 1 +#endif +#endif /* CONFIG_BOARD_EMAC_COUNT */ + +/* normal boards start with EMAC0 */ +#if !defined(CONFIG_EMAC_NR_START) +#define CONFIG_EMAC_NR_START 0 +#endif + +#define MAL_RX_DESC_SIZE 2048 +#define MAL_TX_DESC_SIZE 2048 +#define MAL_ALLOC_SIZE (MAL_TX_DESC_SIZE + MAL_RX_DESC_SIZE) + +/*-----------------------------------------------------------------------------+ + * Prototypes and externals. + *-----------------------------------------------------------------------------*/ +static void enet_rcv (struct eth_device *dev, unsigned long malisr); + +int enetInt (struct eth_device *dev); +static void mal_err (struct eth_device *dev, unsigned long isr, + unsigned long uic, unsigned long maldef, + unsigned long mal_errr); +static void emac_err (struct eth_device *dev, unsigned long isr); + +extern int phy_setup_aneg (char *devname, unsigned char addr); +extern int emac4xx_miiphy_read (const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value); +extern int emac4xx_miiphy_write (const char *devname, unsigned char addr, + unsigned char reg, unsigned short value); + +int board_emac_count(void); + +static void emac_loopback_enable(EMAC_4XX_HW_PST hw_p) +{ +#if defined(CONFIG_440SPE) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_405EX) + u32 val; + + mfsdr(SDR0_MFR, val); + val |= SDR0_MFR_ETH_CLK_SEL_V(hw_p->devnum); + mtsdr(SDR0_MFR, val); +#elif defined(CONFIG_460EX) || defined(CONFIG_460GT) + u32 val; + + mfsdr(SDR0_ETH_CFG, val); + val |= SDR0_ETH_CFG_CLK_SEL_V(hw_p->devnum); + mtsdr(SDR0_ETH_CFG, val); +#endif +} + +static void emac_loopback_disable(EMAC_4XX_HW_PST hw_p) +{ +#if defined(CONFIG_440SPE) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_405EX) + u32 val; + + mfsdr(SDR0_MFR, val); + val &= ~SDR0_MFR_ETH_CLK_SEL_V(hw_p->devnum); + mtsdr(SDR0_MFR, val); +#elif defined(CONFIG_460EX) || defined(CONFIG_460GT) + u32 val; + + mfsdr(SDR0_ETH_CFG, val); + val &= ~SDR0_ETH_CFG_CLK_SEL_V(hw_p->devnum); + mtsdr(SDR0_ETH_CFG, val); +#endif +} + +/*-----------------------------------------------------------------------------+ +| ppc_4xx_eth_halt +| Disable MAL channel, and EMACn ++-----------------------------------------------------------------------------*/ +static void ppc_4xx_eth_halt (struct eth_device *dev) +{ + EMAC_4XX_HW_PST hw_p = dev->priv; + u32 val = 10000; + + out_be32((void *)EMAC0_IER + hw_p->hw_addr, 0x00000000); /* disable emac interrupts */ + + /* 1st reset MAL channel */ + /* Note: writing a 0 to a channel has no effect */ +#if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + mtdcr (MAL0_TXCARR, (MAL_CR_MMSR >> (hw_p->devnum * 2))); +#else + mtdcr (MAL0_TXCARR, (MAL_CR_MMSR >> hw_p->devnum)); +#endif + mtdcr (MAL0_RXCARR, (MAL_CR_MMSR >> hw_p->devnum)); + + /* wait for reset */ + while (mfdcr (MAL0_RXCASR) & (MAL_CR_MMSR >> hw_p->devnum)) { + udelay (1000); /* Delay 1 MS so as not to hammer the register */ + val--; + if (val == 0) + break; + } + + /* provide clocks for EMAC internal loopback */ + emac_loopback_enable(hw_p); + + /* EMAC RESET */ + out_be32((void *)EMAC0_MR0 + hw_p->hw_addr, EMAC_MR0_SRST); + + /* remove clocks for EMAC internal loopback */ + emac_loopback_disable(hw_p); + +#ifndef CONFIG_NETCONSOLE + hw_p->print_speed = 1; /* print speed message again next time */ +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + /* don't bypass the TAHOE0/TAHOE1 cores for Linux */ + mfsdr(SDR0_ETH_CFG, val); + val &= ~(SDR0_ETH_CFG_TAHOE0_BYPASS | SDR0_ETH_CFG_TAHOE1_BYPASS); + mtsdr(SDR0_ETH_CFG, val); +#endif + + return; +} + +#if defined (CONFIG_440GX) +int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis) +{ + unsigned long pfc1; + unsigned long zmiifer; + unsigned long rmiifer; + + mfsdr(SDR0_PFC1, pfc1); + pfc1 = SDR0_PFC1_EPS_DECODE(pfc1); + + zmiifer = 0; + rmiifer = 0; + + switch (pfc1) { + case 1: + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(2); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_ZMII; + break; + case 2: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(2); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_ZMII; + break; + case 3: + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 4: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + bis->bi_phymode[3] = BI_PHYMODE_RGMII; + break; + case 5: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_RGMII; + break; + case 6: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + break; + case 0: + default: + zmiifer = ZMII_FER_MII << ZMII_FER_V(devnum); + rmiifer = 0x0; + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_ZMII; + break; + } + + /* Ensure we setup mdio for this devnum and ONLY this devnum */ + zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum); + + out_be32((void *)ZMII0_FER, zmiifer); + out_be32((void *)RGMII_FER, rmiifer); + + return ((int)pfc1); +} +#endif /* CONFIG_440_GX */ + +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis) +{ + unsigned long zmiifer=0x0; + unsigned long pfc1; + + mfsdr(SDR0_PFC1, pfc1); + pfc1 &= SDR0_PFC1_SELECT_MASK; + + switch (pfc1) { + case SDR0_PFC1_SELECT_CONFIG_2: + /* 1 x GMII port */ + out_be32((void *)ZMII0_FER, 0x00); + out_be32((void *)RGMII_FER, 0x00000037); + bis->bi_phymode[0] = BI_PHYMODE_GMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case SDR0_PFC1_SELECT_CONFIG_4: + /* 2 x RGMII ports */ + out_be32((void *)ZMII0_FER, 0x00); + out_be32((void *)RGMII_FER, 0x00000055); + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + break; + case SDR0_PFC1_SELECT_CONFIG_6: + /* 2 x SMII ports */ + out_be32((void *)ZMII0_FER, + ((ZMII_FER_SMII) << ZMII_FER_V(0)) | + ((ZMII_FER_SMII) << ZMII_FER_V(1))); + out_be32((void *)RGMII_FER, 0x00000000); + bis->bi_phymode[0] = BI_PHYMODE_SMII; + bis->bi_phymode[1] = BI_PHYMODE_SMII; + break; + case SDR0_PFC1_SELECT_CONFIG_1_2: + /* only 1 x MII supported */ + out_be32((void *)ZMII0_FER, (ZMII_FER_MII) << ZMII_FER_V(0)); + out_be32((void *)RGMII_FER, 0x00000000); + bis->bi_phymode[0] = BI_PHYMODE_MII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + default: + break; + } + + /* Ensure we setup mdio for this devnum and ONLY this devnum */ + zmiifer = in_be32((void *)ZMII0_FER); + zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum); + out_be32((void *)ZMII0_FER, zmiifer); + + return ((int)0x0); +} +#endif /* CONFIG_440EPX */ + +#if defined(CONFIG_405EX) +int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis) +{ + u32 rgmiifer = 0; + + /* + * The 405EX(r)'s RGMII bridge can operate in one of several + * modes, only one of which (2 x RGMII) allows the + * simultaneous use of both EMACs on the 405EX. + */ + + switch (CONFIG_EMAC_PHY_MODE) { + + case EMAC_PHY_MODE_NONE: + /* No ports */ + rgmiifer |= RGMII_FER_DIS << 0; + rgmiifer |= RGMII_FER_DIS << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case EMAC_PHY_MODE_NONE_RGMII: + /* 1 x RGMII port on channel 0 */ + rgmiifer |= RGMII_FER_RGMII << 0; + rgmiifer |= RGMII_FER_DIS << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case EMAC_PHY_MODE_RGMII_NONE: + /* 1 x RGMII port on channel 1 */ + rgmiifer |= RGMII_FER_DIS << 0; + rgmiifer |= RGMII_FER_RGMII << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + break; + case EMAC_PHY_MODE_RGMII_RGMII: + /* 2 x RGMII ports */ + rgmiifer |= RGMII_FER_RGMII << 0; + rgmiifer |= RGMII_FER_RGMII << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + break; + case EMAC_PHY_MODE_NONE_GMII: + /* 1 x GMII port on channel 0 */ + rgmiifer |= RGMII_FER_GMII << 0; + rgmiifer |= RGMII_FER_DIS << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_GMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case EMAC_PHY_MODE_NONE_MII: + /* 1 x MII port on channel 0 */ + rgmiifer |= RGMII_FER_MII << 0; + rgmiifer |= RGMII_FER_DIS << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_MII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case EMAC_PHY_MODE_GMII_NONE: + /* 1 x GMII port on channel 1 */ + rgmiifer |= RGMII_FER_DIS << 0; + rgmiifer |= RGMII_FER_GMII << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_GMII; + break; + case EMAC_PHY_MODE_MII_NONE: + /* 1 x MII port on channel 1 */ + rgmiifer |= RGMII_FER_DIS << 0; + rgmiifer |= RGMII_FER_MII << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_MII; + break; + default: + break; + } + + /* Ensure we setup mdio for this devnum and ONLY this devnum */ + rgmiifer = in_be32((void *)RGMII_FER); + rgmiifer |= (1 << (19-devnum)); + out_be32((void *)RGMII_FER, rgmiifer); + + return ((int)0x0); +} +#endif /* CONFIG_405EX */ + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis) +{ + u32 eth_cfg; + u32 zmiifer; /* ZMII0_FER reg. */ + u32 rmiifer; /* RGMII0_FER reg. Bridge 0 */ + u32 rmiifer1; /* RGMII0_FER reg. Bridge 1 */ + int mode; + + zmiifer = 0; + rmiifer = 0; + rmiifer1 = 0; + +#if defined(CONFIG_460EX) + mode = 9; + mfsdr(SDR0_ETH_CFG, eth_cfg); + if (((eth_cfg & SDR0_ETH_CFG_SGMII0_ENABLE) > 0) && + ((eth_cfg & SDR0_ETH_CFG_SGMII1_ENABLE) > 0)) + mode = 11; /* config SGMII */ +#else + mode = 10; + mfsdr(SDR0_ETH_CFG, eth_cfg); + if (((eth_cfg & SDR0_ETH_CFG_SGMII0_ENABLE) > 0) && + ((eth_cfg & SDR0_ETH_CFG_SGMII1_ENABLE) > 0) && + ((eth_cfg & SDR0_ETH_CFG_SGMII2_ENABLE) > 0)) + mode = 12; /* config SGMII */ +#endif + + /* TODO: + * NOTE: 460GT has 2 RGMII bridge cores: + * emac0 ------ RGMII0_BASE + * | + * emac1 -----+ + * + * emac2 ------ RGMII1_BASE + * | + * emac3 -----+ + * + * 460EX has 1 RGMII bridge core: + * and RGMII1_BASE is disabled + * emac0 ------ RGMII0_BASE + * | + * emac1 -----+ + */ + + /* + * Right now only 2*RGMII is supported. Please extend when needed. + * sr - 2008-02-19 + * Add SGMII support. + * vg - 2008-07-28 + */ + switch (mode) { + case 1: + /* 1 MII - 460EX */ + /* GMC0 EMAC4_0, ZMII Bridge */ + zmiifer |= ZMII_FER_MII << ZMII_FER_V(0); + bis->bi_phymode[0] = BI_PHYMODE_MII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 2: + /* 2 MII - 460GT */ + /* GMC0 EMAC4_0, GMC1 EMAC4_2, ZMII Bridge */ + zmiifer |= ZMII_FER_MII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_MII << ZMII_FER_V(2); + bis->bi_phymode[0] = BI_PHYMODE_MII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_MII; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 3: + /* 2 RMII - 460EX */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, ZMII Bridge */ + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1); + bis->bi_phymode[0] = BI_PHYMODE_RMII; + bis->bi_phymode[1] = BI_PHYMODE_RMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 4: + /* 4 RMII - 460GT */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, GMC1 EMAC4_2, GMC1, EMAC4_3 */ + /* ZMII Bridge */ + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(2); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_RMII; + bis->bi_phymode[1] = BI_PHYMODE_RMII; + bis->bi_phymode[2] = BI_PHYMODE_RMII; + bis->bi_phymode[3] = BI_PHYMODE_RMII; + break; + case 5: + /* 2 SMII - 460EX */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, ZMII Bridge */ + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); + bis->bi_phymode[0] = BI_PHYMODE_SMII; + bis->bi_phymode[1] = BI_PHYMODE_SMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 6: + /* 4 SMII - 460GT */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, GMC0 EMAC4_3, GMC0 EMAC4_3 */ + /* ZMII Bridge */ + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(2); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_SMII; + bis->bi_phymode[1] = BI_PHYMODE_SMII; + bis->bi_phymode[2] = BI_PHYMODE_SMII; + bis->bi_phymode[3] = BI_PHYMODE_SMII; + break; + case 7: + /* This is the default mode that we want for board bringup - Maple */ + /* 1 GMII - 460EX */ + /* GMC0 EMAC4_0, RGMII Bridge 0 */ + rmiifer |= RGMII_FER_MDIO(0); + + if (devnum == 0) { + rmiifer |= RGMII_FER_GMII << RGMII_FER_V(2); /* CH0CFG - EMAC0 */ + bis->bi_phymode[0] = BI_PHYMODE_GMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + } else { + rmiifer |= RGMII_FER_GMII << RGMII_FER_V(3); /* CH1CFG - EMAC1 */ + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_GMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + } + break; + case 8: + /* 2 GMII - 460GT */ + /* GMC0 EMAC4_0, RGMII Bridge 0 */ + /* GMC1 EMAC4_2, RGMII Bridge 1 */ + rmiifer |= RGMII_FER_GMII << RGMII_FER_V(2); /* CH0CFG - EMAC0 */ + rmiifer1 |= RGMII_FER_GMII << RGMII_FER_V(2); /* CH0CFG - EMAC2 */ + rmiifer |= RGMII_FER_MDIO(0); /* enable MDIO - EMAC0 */ + rmiifer1 |= RGMII_FER_MDIO(0); /* enable MDIO - EMAC2 */ + + bis->bi_phymode[0] = BI_PHYMODE_GMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_GMII; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 9: + /* 2 RGMII - 460EX */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, RGMII Bridge 0 */ + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3); + rmiifer |= RGMII_FER_MDIO(0); /* enable MDIO - EMAC0 */ + + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 10: + /* 4 RGMII - 460GT */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, RGMII Bridge 0 */ + /* GMC1 EMAC4_2, GMC1 EMAC4_3, RGMII Bridge 1 */ + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3); + rmiifer1 |= RGMII_FER_RGMII << RGMII_FER_V(2); + rmiifer1 |= RGMII_FER_RGMII << RGMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + bis->bi_phymode[3] = BI_PHYMODE_RGMII; + break; + case 11: + /* 2 SGMII - 460EX */ + bis->bi_phymode[0] = BI_PHYMODE_SGMII; + bis->bi_phymode[1] = BI_PHYMODE_SGMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 12: + /* 3 SGMII - 460GT */ + bis->bi_phymode[0] = BI_PHYMODE_SGMII; + bis->bi_phymode[1] = BI_PHYMODE_SGMII; + bis->bi_phymode[2] = BI_PHYMODE_SGMII; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + default: + break; + } + + /* Set EMAC for MDIO */ + mfsdr(SDR0_ETH_CFG, eth_cfg); + eth_cfg |= SDR0_ETH_CFG_MDIO_SEL_EMAC0; + mtsdr(SDR0_ETH_CFG, eth_cfg); + + out_be32((void *)RGMII_FER, rmiifer); +#if defined(CONFIG_460GT) + out_be32((void *)RGMII_FER + RGMII1_BASE_OFFSET, rmiifer1); +#endif + + /* bypass the TAHOE0/TAHOE1 cores for U-Boot */ + mfsdr(SDR0_ETH_CFG, eth_cfg); + eth_cfg |= (SDR0_ETH_CFG_TAHOE0_BYPASS | SDR0_ETH_CFG_TAHOE1_BYPASS); + mtsdr(SDR0_ETH_CFG, eth_cfg); + + return 0; +} +#endif /* CONFIG_460EX || CONFIG_460GT */ + +static inline void *malloc_aligned(u32 size, u32 align) +{ + return (void *)(((u32)malloc(size + align) + align - 1) & + ~(align - 1)); +} + +static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis) +{ + int i; + unsigned long reg = 0; + unsigned long msr; + unsigned long speed; + unsigned long duplex; + unsigned long failsafe; + unsigned mode_reg; + unsigned short devnum; + unsigned short reg_short; +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + u32 opbfreq; + sys_info_t sysinfo; +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + __maybe_unused int ethgroup = -1; +#endif +#endif + u32 bd_cached; + u32 bd_uncached = 0; +#ifdef CONFIG_4xx_DCACHE + static u32 last_used_ea = 0; +#endif +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + int rgmii_channel; +#endif + + EMAC_4XX_HW_PST hw_p = dev->priv; + + /* before doing anything, figure out if we have a MAC address */ + /* if not, bail */ + if (memcmp (dev->enetaddr, "\0\0\0\0\0\0", 6) == 0) { + printf("ERROR: ethaddr not set!\n"); + return -1; + } + +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + /* Need to get the OPB frequency so we can access the PHY */ + get_sys_info (&sysinfo); +#endif + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); /* disable interrupts */ + + devnum = hw_p->devnum; + +#ifdef INFO_4XX_ENET + /* AS.HARNOIS + * We should have : + * hw_p->stats.pkts_handled <= hw_p->stats.pkts_rx <= hw_p->stats.pkts_handled+PKTBUFSRX + * In the most cases hw_p->stats.pkts_handled = hw_p->stats.pkts_rx, but it + * is possible that new packets (without relationship with + * current transfer) have got the time to arrived before + * netloop calls eth_halt + */ + printf ("About preceeding transfer (eth%d):\n" + "- Sent packet number %d\n" + "- Received packet number %d\n" + "- Handled packet number %d\n", + hw_p->devnum, + hw_p->stats.pkts_tx, + hw_p->stats.pkts_rx, hw_p->stats.pkts_handled); + + hw_p->stats.pkts_tx = 0; + hw_p->stats.pkts_rx = 0; + hw_p->stats.pkts_handled = 0; + hw_p->print_speed = 1; /* print speed message again next time */ +#endif + + hw_p->tx_err_index = 0; /* Transmit Error Index for tx_err_log */ + hw_p->rx_err_index = 0; /* Receive Error Index for rx_err_log */ + + hw_p->rx_slot = 0; /* MAL Receive Slot */ + hw_p->rx_i_index = 0; /* Receive Interrupt Queue Index */ + hw_p->rx_u_index = 0; /* Receive User Queue Index */ + + hw_p->tx_slot = 0; /* MAL Transmit Slot */ + hw_p->tx_i_index = 0; /* Transmit Interrupt Queue Index */ + hw_p->tx_u_index = 0; /* Transmit User Queue Index */ + +#if defined(CONFIG_440) && !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) + /* set RMII mode */ + /* NOTE: 440GX spec states that mode is mutually exclusive */ + /* NOTE: Therefore, disable all other EMACS, since we handle */ + /* NOTE: only one emac at a time */ + reg = 0; + out_be32((void *)ZMII0_FER, 0); + udelay (100); + +#if defined(CONFIG_440GP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + out_be32((void *)ZMII0_FER, (ZMII_FER_RMII | ZMII_FER_MDI) << ZMII_FER_V (devnum)); +#elif defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) + ethgroup = ppc_4xx_eth_setup_bridge(devnum, bis); +#endif + + out_be32((void *)ZMII0_SSR, ZMII0_SSR_SP << ZMII0_SSR_V(devnum)); +#endif /* defined(CONFIG_440) && !defined(CONFIG_440SP) */ +#if defined(CONFIG_405EX) + ethgroup = ppc_4xx_eth_setup_bridge(devnum, bis); +#endif + + sync(); + + /* provide clocks for EMAC internal loopback */ + emac_loopback_enable(hw_p); + + /* EMAC RESET */ + out_be32((void *)EMAC0_MR0 + hw_p->hw_addr, EMAC_MR0_SRST); + + /* remove clocks for EMAC internal loopback */ + emac_loopback_disable(hw_p); + + failsafe = 1000; + while ((in_be32((void *)EMAC0_MR0 + hw_p->hw_addr) & (EMAC_MR0_SRST)) && failsafe) { + udelay (1000); + failsafe--; + } + if (failsafe <= 0) + printf("\nProblem resetting EMAC!\n"); + +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + /* Whack the M1 register */ + mode_reg = 0x0; + mode_reg &= ~0x00000038; + opbfreq = sysinfo.freqOPB / 1000000; + if (opbfreq <= 50); + else if (opbfreq <= 66) + mode_reg |= EMAC_MR1_OBCI_66; + else if (opbfreq <= 83) + mode_reg |= EMAC_MR1_OBCI_83; + else if (opbfreq <= 100) + mode_reg |= EMAC_MR1_OBCI_100; + else + mode_reg |= EMAC_MR1_OBCI_GT100; + + out_be32((void *)EMAC0_MR1 + hw_p->hw_addr, mode_reg); +#endif /* defined(CONFIG_440GX) || defined(CONFIG_440SP) */ + +#if defined(CONFIG_GPCS_PHY_ADDR) || defined(CONFIG_GPCS_PHY1_ADDR) || \ + defined(CONFIG_GPCS_PHY2_ADDR) || defined(CONFIG_GPCS_PHY3_ADDR) + if (bis->bi_phymode[devnum] == BI_PHYMODE_SGMII) { + /* + * In SGMII mode, GPCS access is needed for + * communication with the internal SGMII SerDes. + */ + switch (devnum) { +#if defined(CONFIG_GPCS_PHY_ADDR) + case 0: + reg = CONFIG_GPCS_PHY_ADDR; + break; +#endif +#if defined(CONFIG_GPCS_PHY1_ADDR) + case 1: + reg = CONFIG_GPCS_PHY1_ADDR; + break; +#endif +#if defined(CONFIG_GPCS_PHY2_ADDR) + case 2: + reg = CONFIG_GPCS_PHY2_ADDR; + break; +#endif +#if defined(CONFIG_GPCS_PHY3_ADDR) + case 3: + reg = CONFIG_GPCS_PHY3_ADDR; + break; +#endif + } + + mode_reg = in_be32((void *)EMAC0_MR1 + hw_p->hw_addr); + mode_reg |= EMAC_MR1_MF_1000GPCS | EMAC_MR1_IPPA_SET(reg); + out_be32((void *)EMAC0_MR1 + hw_p->hw_addr, mode_reg); + + /* Configure GPCS interface to recommended setting for SGMII */ + miiphy_reset(dev->name, reg); + miiphy_write(dev->name, reg, 0x04, 0x8120); /* AsymPause, FDX */ + miiphy_write(dev->name, reg, 0x07, 0x2801); /* msg_pg, toggle */ + miiphy_write(dev->name, reg, 0x00, 0x0140); /* 1Gbps, FDX */ + } +#endif /* defined(CONFIG_GPCS_PHY_ADDR) */ + + /* wait for PHY to complete auto negotiation */ + reg_short = 0; + switch (devnum) { + case 0: + reg = CONFIG_PHY_ADDR; + break; +#if defined (CONFIG_PHY1_ADDR) + case 1: + reg = CONFIG_PHY1_ADDR; + break; +#endif +#if defined (CONFIG_PHY2_ADDR) + case 2: + reg = CONFIG_PHY2_ADDR; + break; +#endif +#if defined (CONFIG_PHY3_ADDR) + case 3: + reg = CONFIG_PHY3_ADDR; + break; +#endif + default: + reg = CONFIG_PHY_ADDR; + break; + } + + bis->bi_phynum[devnum] = reg; + + if (reg == CONFIG_FIXED_PHY) + goto get_speed; + +#if defined(CONFIG_PHY_RESET) + /* + * Reset the phy, only if its the first time through + * otherwise, just check the speeds & feeds + */ + if (hw_p->first_init == 0) { +#if defined(CONFIG_M88E1111_PHY) + miiphy_write (dev->name, reg, 0x14, 0x0ce3); + miiphy_write (dev->name, reg, 0x18, 0x4101); + miiphy_write (dev->name, reg, 0x09, 0x0e00); + miiphy_write (dev->name, reg, 0x04, 0x01e1); +#if defined(CONFIG_M88E1111_DISABLE_FIBER) + miiphy_read(dev->name, reg, 0x1b, ®_short); + reg_short |= 0x8000; + miiphy_write(dev->name, reg, 0x1b, reg_short); +#endif +#endif +#if defined(CONFIG_M88E1112_PHY) + if (bis->bi_phymode[devnum] == BI_PHYMODE_SGMII) { + /* + * Marvell 88E1112 PHY needs to have the SGMII MAC + * interace (page 2) properly configured to + * communicate with the 460EX/GT GPCS interface. + */ + + /* Set access to Page 2 */ + miiphy_write(dev->name, reg, 0x16, 0x0002); + + miiphy_write(dev->name, reg, 0x00, 0x0040); /* 1Gbps */ + miiphy_read(dev->name, reg, 0x1a, ®_short); + reg_short |= 0x8000; /* bypass Auto-Negotiation */ + miiphy_write(dev->name, reg, 0x1a, reg_short); + miiphy_reset(dev->name, reg); /* reset MAC interface */ + + /* Reset access to Page 0 */ + miiphy_write(dev->name, reg, 0x16, 0x0000); + } +#endif /* defined(CONFIG_M88E1112_PHY) */ + miiphy_reset (dev->name, reg); + +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + +#if defined(CONFIG_CIS8201_PHY) + /* + * Cicada 8201 PHY needs to have an extended register whacked + * for RGMII mode. + */ + if (((devnum == 2) || (devnum == 3)) && (4 == ethgroup)) { +#if defined(CONFIG_CIS8201_SHORT_ETCH) + miiphy_write (dev->name, reg, 23, 0x1300); +#else + miiphy_write (dev->name, reg, 23, 0x1000); +#endif + /* + * Vitesse VSC8201/Cicada CIS8201 errata: + * Interoperability problem with Intel 82547EI phys + * This work around (provided by Vitesse) changes + * the default timer convergence from 8ms to 12ms + */ + miiphy_write (dev->name, reg, 0x1f, 0x2a30); + miiphy_write (dev->name, reg, 0x08, 0x0200); + miiphy_write (dev->name, reg, 0x1f, 0x52b5); + miiphy_write (dev->name, reg, 0x02, 0x0004); + miiphy_write (dev->name, reg, 0x01, 0x0671); + miiphy_write (dev->name, reg, 0x00, 0x8fae); + miiphy_write (dev->name, reg, 0x1f, 0x2a30); + miiphy_write (dev->name, reg, 0x08, 0x0000); + miiphy_write (dev->name, reg, 0x1f, 0x0000); + /* end Vitesse/Cicada errata */ + } +#endif /* defined(CONFIG_CIS8201_PHY) */ + +#if defined(CONFIG_ET1011C_PHY) + /* + * Agere ET1011c PHY needs to have an extended register whacked + * for RGMII mode. + */ + if (((devnum == 2) || (devnum ==3)) && (4 == ethgroup)) { + miiphy_read (dev->name, reg, 0x16, ®_short); + reg_short &= ~(0x7); + reg_short |= 0x6; /* RGMII DLL Delay*/ + miiphy_write (dev->name, reg, 0x16, reg_short); + + miiphy_read (dev->name, reg, 0x17, ®_short); + reg_short &= ~(0x40); + miiphy_write (dev->name, reg, 0x17, reg_short); + + miiphy_write(dev->name, reg, 0x1c, 0x74f0); + } +#endif /* defined(CONFIG_ET1011C_PHY) */ + +#endif /* defined(CONFIG_440GX) ... */ + /* Start/Restart autonegotiation */ + phy_setup_aneg (dev->name, reg); + udelay (1000); + } +#endif /* defined(CONFIG_PHY_RESET) */ + + miiphy_read (dev->name, reg, MII_BMSR, ®_short); + + /* + * Wait if PHY is capable of autonegotiation and autonegotiation is not complete + */ + if ((reg_short & BMSR_ANEGCAPABLE) + && !(reg_short & BMSR_ANEGCOMPLETE)) { + puts ("Waiting for PHY auto negotiation to complete"); + i = 0; + while (!(reg_short & BMSR_ANEGCOMPLETE)) { + /* + * Timeout reached ? + */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts (" TIMEOUT !\n"); + break; + } + + if ((i++ % 1000) == 0) { + putc ('.'); + } + udelay (1000); /* 1 ms */ + miiphy_read (dev->name, reg, MII_BMSR, ®_short); + } + puts (" done\n"); + udelay (500000); /* another 500 ms (results in faster booting) */ + } + +get_speed: + if (reg == CONFIG_FIXED_PHY) { + for (i = 0; i < ARRAY_SIZE(fixed_phy_port); i++) { + if (devnum == fixed_phy_port[i].devnum) { + speed = fixed_phy_port[i].speed; + duplex = fixed_phy_port[i].duplex; + break; + } + } + + if (i == ARRAY_SIZE(fixed_phy_port)) { + printf("ERROR: PHY (%s) not configured correctly!\n", + dev->name); + return -1; + } + } else { + speed = miiphy_speed(dev->name, reg); + duplex = miiphy_duplex(dev->name, reg); + } + + if (hw_p->print_speed) { + hw_p->print_speed = 0; + printf ("ENET Speed is %d Mbps - %s duplex connection (EMAC%d)\n", + (int) speed, (duplex == HALF) ? "HALF" : "FULL", + hw_p->devnum); + } + +#if defined(CONFIG_440) && \ + !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \ + !defined(CONFIG_440EPX) && !defined(CONFIG_440GRX) && \ + !defined(CONFIG_460EX) && !defined(CONFIG_460GT) +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) + mfsdr(SDR0_MFR, reg); + if (speed == 100) { + reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_100M; + } else { + reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_10M; + } + mtsdr(SDR0_MFR, reg); +#endif + + /* Set ZMII/RGMII speed according to the phy link speed */ + reg = in_be32((void *)ZMII0_SSR); + if ( (speed == 100) || (speed == 1000) ) + out_be32((void *)ZMII0_SSR, reg | (ZMII0_SSR_SP << ZMII0_SSR_V (devnum))); + else + out_be32((void *)ZMII0_SSR, reg & (~(ZMII0_SSR_SP << ZMII0_SSR_V (devnum)))); + + if ((devnum == 2) || (devnum == 3)) { + if (speed == 1000) + reg = (RGMII_SSR_SP_1000MBPS << RGMII_SSR_V (devnum)); + else if (speed == 100) + reg = (RGMII_SSR_SP_100MBPS << RGMII_SSR_V (devnum)); + else if (speed == 10) + reg = (RGMII_SSR_SP_10MBPS << RGMII_SSR_V (devnum)); + else { + printf("Error in RGMII Speed\n"); + return -1; + } + out_be32((void *)RGMII_SSR, reg); + } +#endif /* defined(CONFIG_440) && !defined(CONFIG_440SP) */ + +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + if (devnum >= 2) + rgmii_channel = devnum - 2; + else + rgmii_channel = devnum; + + if (speed == 1000) + reg = (RGMII_SSR_SP_1000MBPS << RGMII_SSR_V(rgmii_channel)); + else if (speed == 100) + reg = (RGMII_SSR_SP_100MBPS << RGMII_SSR_V(rgmii_channel)); + else if (speed == 10) + reg = (RGMII_SSR_SP_10MBPS << RGMII_SSR_V(rgmii_channel)); + else { + printf("Error in RGMII Speed\n"); + return -1; + } + out_be32((void *)RGMII_SSR, reg); +#if defined(CONFIG_460GT) + if ((devnum == 2) || (devnum == 3)) + out_be32((void *)RGMII_SSR + RGMII1_BASE_OFFSET, reg); +#endif +#endif + + /* set the Mal configuration reg */ +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + mtdcr (MAL0_CFG, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | + MAL_CR_PLBLT_DEFAULT | MAL_CR_EOPIE | 0x00330000); +#else + mtdcr (MAL0_CFG, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | MAL_CR_PLBLT_DEFAULT); + /* Errata 1.12: MAL_1 -- Disable MAL bursting */ + if (get_pvr() == PVR_440GP_RB) { + mtdcr (MAL0_CFG, mfdcr(MAL0_CFG) & ~MAL_CR_PLBB); + } +#endif + + /* + * Malloc MAL buffer desciptors, make sure they are + * aligned on cache line boundary size + * (401/403/IOP480 = 16, 405 = 32) + * and doesn't cross cache block boundaries. + */ + if (hw_p->first_init == 0) { + debug("*** Allocating descriptor memory ***\n"); + + bd_cached = (u32)malloc_aligned(MAL_ALLOC_SIZE, 4096); + if (!bd_cached) { + printf("%s: Error allocating MAL descriptor buffers!\n", __func__); + return -1; + } + +#ifdef CONFIG_4xx_DCACHE + flush_dcache_range(bd_cached, bd_cached + MAL_ALLOC_SIZE); + if (!last_used_ea) +#if defined(CONFIG_SYS_MEM_TOP_HIDE) + bd_uncached = bis->bi_memsize + CONFIG_SYS_MEM_TOP_HIDE; +#else + bd_uncached = bis->bi_memsize; +#endif + else + bd_uncached = last_used_ea + MAL_ALLOC_SIZE; + + last_used_ea = bd_uncached; + program_tlb(bd_cached, bd_uncached, MAL_ALLOC_SIZE, + TLB_WORD2_I_ENABLE); +#else + bd_uncached = bd_cached; +#endif + hw_p->tx_phys = bd_cached; + hw_p->rx_phys = bd_cached + MAL_TX_DESC_SIZE; + hw_p->tx = (mal_desc_t *)(bd_uncached); + hw_p->rx = (mal_desc_t *)(bd_uncached + MAL_TX_DESC_SIZE); + debug("hw_p->tx=%p, hw_p->rx=%p\n", hw_p->tx, hw_p->rx); + } + + for (i = 0; i < NUM_TX_BUFF; i++) { + hw_p->tx[i].ctrl = 0; + hw_p->tx[i].data_len = 0; + if (hw_p->first_init == 0) + hw_p->txbuf_ptr = malloc_aligned(MAL_ALLOC_SIZE, + L1_CACHE_BYTES); + hw_p->tx[i].data_ptr = hw_p->txbuf_ptr; + if ((NUM_TX_BUFF - 1) == i) + hw_p->tx[i].ctrl |= MAL_TX_CTRL_WRAP; + hw_p->tx_run[i] = -1; + debug("TX_BUFF %d @ 0x%08x\n", i, (u32)hw_p->tx[i].data_ptr); + } + + for (i = 0; i < NUM_RX_BUFF; i++) { + hw_p->rx[i].ctrl = 0; + hw_p->rx[i].data_len = 0; + hw_p->rx[i].data_ptr = (char *)NetRxPackets[i]; + if ((NUM_RX_BUFF - 1) == i) + hw_p->rx[i].ctrl |= MAL_RX_CTRL_WRAP; + hw_p->rx[i].ctrl |= MAL_RX_CTRL_EMPTY | MAL_RX_CTRL_INTR; + hw_p->rx_ready[i] = -1; + debug("RX_BUFF %d @ 0x%08x\n", i, (u32)hw_p->rx[i].data_ptr); + } + + reg = 0x00000000; + + reg |= dev->enetaddr[0]; /* set high address */ + reg = reg << 8; + reg |= dev->enetaddr[1]; + + out_be32((void *)EMAC0_IAH + hw_p->hw_addr, reg); + + reg = 0x00000000; + reg |= dev->enetaddr[2]; /* set low address */ + reg = reg << 8; + reg |= dev->enetaddr[3]; + reg = reg << 8; + reg |= dev->enetaddr[4]; + reg = reg << 8; + reg |= dev->enetaddr[5]; + + out_be32((void *)EMAC0_IAL + hw_p->hw_addr, reg); + + switch (devnum) { + case 1: + /* setup MAL tx & rx channel pointers */ +#if defined (CONFIG_405EP) || defined (CONFIG_440EP) || defined (CONFIG_440GR) + mtdcr (MAL0_TXCTP2R, hw_p->tx_phys); +#else + mtdcr (MAL0_TXCTP1R, hw_p->tx_phys); +#endif +#if defined(CONFIG_440) + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + mtdcr (MAL0_RXCTP8R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS8, ENET_MAX_MTU_ALIGNED / 16); +#else + mtdcr (MAL0_RXCTP1R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS1, ENET_MAX_MTU_ALIGNED / 16); +#endif + break; +#if defined (CONFIG_440GX) + case 2: + /* setup MAL tx & rx channel pointers */ + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); + mtdcr (MAL0_TXCTP2R, hw_p->tx_phys); + mtdcr (MAL0_RXCTP2R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS2, ENET_MAX_MTU_ALIGNED / 16); + break; + case 3: + /* setup MAL tx & rx channel pointers */ + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_TXCTP3R, hw_p->tx_phys); + mtdcr (MAL0_RXBADDR, 0x0); + mtdcr (MAL0_RXCTP3R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS3, ENET_MAX_MTU_ALIGNED / 16); + break; +#endif /* CONFIG_440GX */ +#if defined (CONFIG_460GT) + case 2: + /* setup MAL tx & rx channel pointers */ + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); + mtdcr (MAL0_TXCTP2R, hw_p->tx_phys); + mtdcr (MAL0_RXCTP16R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS16, ENET_MAX_MTU_ALIGNED / 16); + break; + case 3: + /* setup MAL tx & rx channel pointers */ + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); + mtdcr (MAL0_TXCTP3R, hw_p->tx_phys); + mtdcr (MAL0_RXCTP24R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS24, ENET_MAX_MTU_ALIGNED / 16); + break; +#endif /* CONFIG_460GT */ + case 0: + default: + /* setup MAL tx & rx channel pointers */ +#if defined(CONFIG_440) + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); +#endif + mtdcr (MAL0_TXCTP0R, hw_p->tx_phys); + mtdcr (MAL0_RXCTP0R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS0, ENET_MAX_MTU_ALIGNED / 16); + break; + } + + /* Enable MAL transmit and receive channels */ +#if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + mtdcr (MAL0_TXCASR, (MAL_TXRX_CASR >> (hw_p->devnum*2))); +#else + mtdcr (MAL0_TXCASR, (MAL_TXRX_CASR >> hw_p->devnum)); +#endif + mtdcr (MAL0_RXCASR, (MAL_TXRX_CASR >> hw_p->devnum)); + + /* set transmit enable & receive enable */ + out_be32((void *)EMAC0_MR0 + hw_p->hw_addr, EMAC_MR0_TXE | EMAC_MR0_RXE); + + mode_reg = in_be32((void *)EMAC0_MR1 + hw_p->hw_addr); + + /* set rx-/tx-fifo size */ + mode_reg = (mode_reg & ~EMAC_MR1_FIFO_MASK) | EMAC_MR1_FIFO_SIZE; + + /* set speed */ + if (speed == _1000BASET) { +#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) + unsigned long pfc1; + + mfsdr (SDR0_PFC1, pfc1); + pfc1 |= SDR0_PFC1_EM_1000; + mtsdr (SDR0_PFC1, pfc1); +#endif + mode_reg = mode_reg | EMAC_MR1_MF_1000MBPS | EMAC_MR1_IST; + } else if (speed == _100BASET) + mode_reg = mode_reg | EMAC_MR1_MF_100MBPS | EMAC_MR1_IST; + else + mode_reg = mode_reg & ~0x00C00000; /* 10 MBPS */ + if (duplex == FULL) + mode_reg = mode_reg | 0x80000000 | EMAC_MR1_IST; + + out_be32((void *)EMAC0_MR1 + hw_p->hw_addr, mode_reg); + + /* Enable broadcast and indvidual address */ + /* TBS: enabling runts as some misbehaved nics will send runts */ + out_be32((void *)EMAC0_RXM + hw_p->hw_addr, EMAC_RMR_BAE | EMAC_RMR_IAE); + + /* we probably need to set the tx mode1 reg? maybe at tx time */ + + /* set transmit request threshold register */ + out_be32((void *)EMAC0_TRTR + hw_p->hw_addr, 0x18000000); /* 256 byte threshold */ + + /* set receive low/high water mark register */ +#if defined(CONFIG_440) + /* 440s has a 64 byte burst length */ + out_be32((void *)EMAC0_RX_HI_LO_WMARK + hw_p->hw_addr, 0x80009000); +#else + /* 405s have a 16 byte burst length */ + out_be32((void *)EMAC0_RX_HI_LO_WMARK + hw_p->hw_addr, 0x0f002000); +#endif /* defined(CONFIG_440) */ + out_be32((void *)EMAC0_TMR1 + hw_p->hw_addr, 0xf8640000); + + /* Set fifo limit entry in tx mode 0 */ + out_be32((void *)EMAC0_TMR0 + hw_p->hw_addr, 0x00000003); + /* Frame gap set */ + out_be32((void *)EMAC0_I_FRAME_GAP_REG + hw_p->hw_addr, 0x00000008); + + /* Set EMAC IER */ + hw_p->emac_ier = EMAC_ISR_PTLE | EMAC_ISR_BFCS | EMAC_ISR_ORE | EMAC_ISR_IRE; + if (speed == _100BASET) + hw_p->emac_ier = hw_p->emac_ier | EMAC_ISR_SYE; + + out_be32((void *)EMAC0_ISR + hw_p->hw_addr, 0xffffffff); /* clear pending interrupts */ + out_be32((void *)EMAC0_IER + hw_p->hw_addr, hw_p->emac_ier); + + if (hw_p->first_init == 0) { + /* + * Connect interrupt service routines + */ + irq_install_handler(ETH_IRQ_NUM(hw_p->devnum), + (interrupt_handler_t *) enetInt, dev); + } + + mtmsr (msr); /* enable interrupts again */ + + hw_p->bis = bis; + hw_p->first_init = 1; + + return 0; +} + + +static int ppc_4xx_eth_send(struct eth_device *dev, void *ptr, int len) +{ + struct enet_frame *ef_ptr; + ulong time_start, time_now; + unsigned long temp_txm0; + EMAC_4XX_HW_PST hw_p = dev->priv; + + ef_ptr = (struct enet_frame *) ptr; + + /*-----------------------------------------------------------------------+ + * Copy in our address into the frame. + *-----------------------------------------------------------------------*/ + (void) memcpy (ef_ptr->source_addr, dev->enetaddr, ENET_ADDR_LENGTH); + + /*-----------------------------------------------------------------------+ + * If frame is too long or too short, modify length. + *-----------------------------------------------------------------------*/ + /* TBS: where does the fragment go???? */ + if (len > ENET_MAX_MTU) + len = ENET_MAX_MTU; + + /* memcpy ((void *) &tx_buff[tx_slot], (const void *) ptr, len); */ + memcpy ((void *) hw_p->txbuf_ptr, (const void *) ptr, len); + flush_dcache_range((u32)hw_p->txbuf_ptr, (u32)hw_p->txbuf_ptr + len); + + /*-----------------------------------------------------------------------+ + * set TX Buffer busy, and send it + *-----------------------------------------------------------------------*/ + hw_p->tx[hw_p->tx_slot].ctrl = (MAL_TX_CTRL_LAST | + EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP) & + ~(EMAC_TX_CTRL_ISA | EMAC_TX_CTRL_RSA); + if ((NUM_TX_BUFF - 1) == hw_p->tx_slot) + hw_p->tx[hw_p->tx_slot].ctrl |= MAL_TX_CTRL_WRAP; + + hw_p->tx[hw_p->tx_slot].data_len = (short) len; + hw_p->tx[hw_p->tx_slot].ctrl |= MAL_TX_CTRL_READY; + + sync(); + + out_be32((void *)EMAC0_TMR0 + hw_p->hw_addr, + in_be32((void *)EMAC0_TMR0 + hw_p->hw_addr) | EMAC_TMR0_GNP0); +#ifdef INFO_4XX_ENET + hw_p->stats.pkts_tx++; +#endif + + /*-----------------------------------------------------------------------+ + * poll unitl the packet is sent and then make sure it is OK + *-----------------------------------------------------------------------*/ + time_start = get_timer (0); + while (1) { + temp_txm0 = in_be32((void *)EMAC0_TMR0 + hw_p->hw_addr); + /* loop until either TINT turns on or 3 seconds elapse */ + if ((temp_txm0 & EMAC_TMR0_GNP0) != 0) { + /* transmit is done, so now check for errors + * If there is an error, an interrupt should + * happen when we return + */ + time_now = get_timer (0); + if ((time_now - time_start) > 3000) { + return (-1); + } + } else { + return (len); + } + } +} + +int enetInt (struct eth_device *dev) +{ + int serviced; + int rc = -1; /* default to not us */ + u32 mal_isr; + u32 emac_isr = 0; + u32 mal_eob; + u32 uic_mal; + u32 uic_mal_err; + u32 uic_emac; + u32 uic_emac_b; + EMAC_4XX_HW_PST hw_p; + + /* + * Because the mal is generic, we need to get the current + * eth device + */ + dev = eth_get_dev(); + + hw_p = dev->priv; + + /* enter loop that stays in interrupt code until nothing to service */ + do { + serviced = 0; + + uic_mal = mfdcr(UIC_BASE_MAL + UIC_MSR); + uic_mal_err = mfdcr(UIC_BASE_MAL_ERR + UIC_MSR); + uic_emac = mfdcr(UIC_BASE_EMAC + UIC_MSR); + uic_emac_b = mfdcr(UIC_BASE_EMAC_B + UIC_MSR); + + if (!(uic_mal & (UIC_MAL_RXEOB | UIC_MAL_TXEOB)) + && !(uic_mal_err & (UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE)) + && !(uic_emac & UIC_ETHx) && !(uic_emac_b & UIC_ETHxB)) { + /* not for us */ + return (rc); + } + + /* get and clear controller status interrupts */ + /* look at MAL and EMAC error interrupts */ + if (uic_mal_err & (UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE)) { + /* we have a MAL error interrupt */ + mal_isr = mfdcr(MAL0_ESR); + mal_err(dev, mal_isr, uic_mal_err, + MAL_UIC_DEF, MAL_UIC_ERR); + + /* clear MAL error interrupt status bits */ + mtdcr(UIC_BASE_MAL_ERR + UIC_SR, + UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE); + + return -1; + } + + /* look for EMAC errors */ + if ((uic_emac & UIC_ETHx) || (uic_emac_b & UIC_ETHxB)) { + emac_isr = in_be32((void *)EMAC0_ISR + hw_p->hw_addr); + emac_err(dev, emac_isr); + + /* clear EMAC error interrupt status bits */ + mtdcr(UIC_BASE_EMAC + UIC_SR, UIC_ETHx); + mtdcr(UIC_BASE_EMAC_B + UIC_SR, UIC_ETHxB); + + return -1; + } + + /* handle MAX TX EOB interrupt from a tx */ + if (uic_mal & UIC_MAL_TXEOB) { + /* clear MAL interrupt status bits */ + mal_eob = mfdcr(MAL0_TXEOBISR); + mtdcr(MAL0_TXEOBISR, mal_eob); + mtdcr(UIC_BASE_MAL + UIC_SR, UIC_MAL_TXEOB); + + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + + /* handle MAL RX EOB interrupt from a receive */ + /* check for EOB on valid channels */ + if (uic_mal & UIC_MAL_RXEOB) { + mal_eob = mfdcr(MAL0_RXEOBISR); + if (mal_eob & + (0x80000000 >> (hw_p->devnum * MAL_RX_CHAN_MUL))) { + /* push packet to upper layer */ + enet_rcv(dev, emac_isr); + + /* clear MAL interrupt status bits */ + mtdcr(UIC_BASE_MAL + UIC_SR, UIC_MAL_RXEOB); + + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + } +#if defined(CONFIG_405EZ) + /* + * On 405EZ the RX-/TX-interrupts are coalesced into + * one IRQ bit in the UIC. We need to acknowledge the + * RX-/TX-interrupts in the SDR0_ICINTSTAT reg as well. + */ + mtsdr(SDR0_ICINTSTAT, + SDR_ICRX_STAT | SDR_ICTX0_STAT | SDR_ICTX1_STAT); +#endif /* defined(CONFIG_405EZ) */ + } while (serviced); + + return (rc); +} + +/*-----------------------------------------------------------------------------+ + * MAL Error Routine + *-----------------------------------------------------------------------------*/ +static void mal_err (struct eth_device *dev, unsigned long isr, + unsigned long uic, unsigned long maldef, + unsigned long mal_errr) +{ + EMAC_4XX_HW_PST hw_p = dev->priv; + + mtdcr (MAL0_ESR, isr); /* clear interrupt */ + + /* clear DE interrupt */ + mtdcr (MAL0_TXDEIR, 0xC0000000); + mtdcr (MAL0_RXDEIR, 0x80000000); + +#ifdef INFO_4XX_ENET + printf ("\nMAL error occured.... ISR = %lx UIC = = %lx MAL_DEF = %lx MAL_ERR= %lx \n", isr, uic, maldef, mal_errr); +#endif + + eth_init (hw_p->bis); /* start again... */ +} + +/*-----------------------------------------------------------------------------+ + * EMAC Error Routine + *-----------------------------------------------------------------------------*/ +static void emac_err (struct eth_device *dev, unsigned long isr) +{ + EMAC_4XX_HW_PST hw_p = dev->priv; + + printf ("EMAC%d error occured.... ISR = %lx\n", hw_p->devnum, isr); + out_be32((void *)EMAC0_ISR + hw_p->hw_addr, isr); +} + +/*-----------------------------------------------------------------------------+ + * enet_rcv() handles the ethernet receive data + *-----------------------------------------------------------------------------*/ +static void enet_rcv (struct eth_device *dev, unsigned long malisr) +{ + unsigned long data_len; + unsigned long rx_eob_isr; + EMAC_4XX_HW_PST hw_p = dev->priv; + + int handled = 0; + int i; + int loop_count = 0; + + rx_eob_isr = mfdcr (MAL0_RXEOBISR); + if ((0x80000000 >> (hw_p->devnum * MAL_RX_CHAN_MUL)) & rx_eob_isr) { + /* clear EOB */ + mtdcr (MAL0_RXEOBISR, rx_eob_isr); + + /* EMAC RX done */ + while (1) { /* do all */ + i = hw_p->rx_slot; + + if ((MAL_RX_CTRL_EMPTY & hw_p->rx[i].ctrl) + || (loop_count >= NUM_RX_BUFF)) + break; + + loop_count++; + handled++; + data_len = (unsigned long) hw_p->rx[i].data_len & 0x0fff; /* Get len */ + if (data_len) { + if (data_len > ENET_MAX_MTU) /* Check len */ + data_len = 0; + else { + if (EMAC_RX_ERRORS & hw_p->rx[i].ctrl) { /* Check Errors */ + data_len = 0; + hw_p->stats.rx_err_log[hw_p-> + rx_err_index] + = hw_p->rx[i].ctrl; + hw_p->rx_err_index++; + if (hw_p->rx_err_index == + MAX_ERR_LOG) + hw_p->rx_err_index = + 0; + } /* emac_erros */ + } /* data_len < max mtu */ + } /* if data_len */ + if (!data_len) { /* no data */ + hw_p->rx[i].ctrl |= MAL_RX_CTRL_EMPTY; /* Free Recv Buffer */ + + hw_p->stats.data_len_err++; /* Error at Rx */ + } + + /* !data_len */ + /* AS.HARNOIS */ + /* Check if user has already eaten buffer */ + /* if not => ERROR */ + else if (hw_p->rx_ready[hw_p->rx_i_index] != -1) { + if (hw_p->is_receiving) + printf ("ERROR : Receive buffers are full!\n"); + break; + } else { + hw_p->stats.rx_frames++; + hw_p->stats.rx += data_len; +#ifdef INFO_4XX_ENET + hw_p->stats.pkts_rx++; +#endif + /* AS.HARNOIS + * use ring buffer + */ + hw_p->rx_ready[hw_p->rx_i_index] = i; + hw_p->rx_i_index++; + if (NUM_RX_BUFF == hw_p->rx_i_index) + hw_p->rx_i_index = 0; + + hw_p->rx_slot++; + if (NUM_RX_BUFF == hw_p->rx_slot) + hw_p->rx_slot = 0; + + /* AS.HARNOIS + * free receive buffer only when + * buffer has been handled (eth_rx) + rx[i].ctrl |= MAL_RX_CTRL_EMPTY; + */ + } /* if data_len */ + } /* while */ + } /* if EMACK_RXCHL */ +} + + +static int ppc_4xx_eth_rx (struct eth_device *dev) +{ + int length; + int user_index; + unsigned long msr; + EMAC_4XX_HW_PST hw_p = dev->priv; + + hw_p->is_receiving = 1; /* tell driver */ + + for (;;) { + /* AS.HARNOIS + * use ring buffer and + * get index from rx buffer desciptor queue + */ + user_index = hw_p->rx_ready[hw_p->rx_u_index]; + if (user_index == -1) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); + + length = hw_p->rx[user_index].data_len & 0x0fff; + + /* Pass the packet up to the protocol layers. */ + /* NetReceive(NetRxPackets[rxIdx], length - 4); */ + /* NetReceive(NetRxPackets[i], length); */ + invalidate_dcache_range((u32)hw_p->rx[user_index].data_ptr, + (u32)hw_p->rx[user_index].data_ptr + + length - 4); + NetReceive (NetRxPackets[user_index], length - 4); + /* Free Recv Buffer */ + hw_p->rx[user_index].ctrl |= MAL_RX_CTRL_EMPTY; + /* Free rx buffer descriptor queue */ + hw_p->rx_ready[hw_p->rx_u_index] = -1; + hw_p->rx_u_index++; + if (NUM_RX_BUFF == hw_p->rx_u_index) + hw_p->rx_u_index = 0; + +#ifdef INFO_4XX_ENET + hw_p->stats.pkts_handled++; +#endif + + mtmsr (msr); /* Enable IRQ's */ + } + + hw_p->is_receiving = 0; /* tell driver */ + + return length; +} + +int ppc_4xx_eth_initialize (bd_t * bis) +{ + static int virgin = 0; + struct eth_device *dev; + int eth_num = 0; + EMAC_4XX_HW_PST hw = NULL; + u8 ethaddr[4 + CONFIG_EMAC_NR_START][6]; + u32 hw_addr[4]; + u32 mal_ier; + +#if defined(CONFIG_440GX) + unsigned long pfc1; + + mfsdr (SDR0_PFC1, pfc1); + pfc1 &= ~(0x01e00000); + pfc1 |= 0x01200000; + mtsdr (SDR0_PFC1, pfc1); +#endif + + /* first clear all mac-addresses */ + for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++) + memcpy(ethaddr[eth_num], "\0\0\0\0\0\0", 6); + + for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++) { + int ethaddr_idx = eth_num + CONFIG_EMAC_NR_START; + switch (eth_num) { + default: /* fall through */ + case 0: + eth_getenv_enetaddr("ethaddr", ethaddr[ethaddr_idx]); + hw_addr[eth_num] = 0x0; + break; +#ifdef CONFIG_HAS_ETH1 + case 1: + eth_getenv_enetaddr("eth1addr", ethaddr[ethaddr_idx]); + hw_addr[eth_num] = 0x100; + break; +#endif +#ifdef CONFIG_HAS_ETH2 + case 2: + eth_getenv_enetaddr("eth2addr", ethaddr[ethaddr_idx]); +#if defined(CONFIG_460GT) + hw_addr[eth_num] = 0x300; +#else + hw_addr[eth_num] = 0x400; +#endif + break; +#endif +#ifdef CONFIG_HAS_ETH3 + case 3: + eth_getenv_enetaddr("eth3addr", ethaddr[ethaddr_idx]); +#if defined(CONFIG_460GT) + hw_addr[eth_num] = 0x400; +#else + hw_addr[eth_num] = 0x600; +#endif + break; +#endif + } + } + + /* set phy num and mode */ + bis->bi_phynum[0] = CONFIG_PHY_ADDR; + bis->bi_phymode[0] = 0; + +#if defined(CONFIG_PHY1_ADDR) + bis->bi_phynum[1] = CONFIG_PHY1_ADDR; + bis->bi_phymode[1] = 0; +#endif +#if defined(CONFIG_440GX) + bis->bi_phynum[2] = CONFIG_PHY2_ADDR; + bis->bi_phynum[3] = CONFIG_PHY3_ADDR; + bis->bi_phymode[2] = 2; + bis->bi_phymode[3] = 2; +#endif + +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_405EX) + ppc_4xx_eth_setup_bridge(0, bis); +#endif + + for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++) { + /* + * See if we can actually bring up the interface, + * otherwise, skip it + */ + if (memcmp (ethaddr[eth_num], "\0\0\0\0\0\0", 6) == 0) { + bis->bi_phymode[eth_num] = BI_PHYMODE_NONE; + continue; + } + + /* Allocate device structure */ + dev = (struct eth_device *) malloc (sizeof (*dev)); + if (dev == NULL) { + printf ("ppc_4xx_eth_initialize: " + "Cannot allocate eth_device %d\n", eth_num); + return (-1); + } + memset(dev, 0, sizeof(*dev)); + + /* Allocate our private use data */ + hw = (EMAC_4XX_HW_PST) malloc (sizeof (*hw)); + if (hw == NULL) { + printf ("ppc_4xx_eth_initialize: " + "Cannot allocate private hw data for eth_device %d", + eth_num); + free (dev); + return (-1); + } + memset(hw, 0, sizeof(*hw)); + + hw->hw_addr = hw_addr[eth_num]; + memcpy (dev->enetaddr, ethaddr[eth_num], 6); + hw->devnum = eth_num; + hw->print_speed = 1; + + sprintf (dev->name, "ppc_4xx_eth%d", eth_num - CONFIG_EMAC_NR_START); + dev->priv = (void *) hw; + dev->init = ppc_4xx_eth_init; + dev->halt = ppc_4xx_eth_halt; + dev->send = ppc_4xx_eth_send; + dev->recv = ppc_4xx_eth_rx; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, + emac4xx_miiphy_read, emac4xx_miiphy_write); +#endif + + if (0 == virgin) { + /* set the MAL IER ??? names may change with new spec ??? */ +#if defined(CONFIG_440SPE) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + mal_ier = + MAL_IER_PT | MAL_IER_PRE | MAL_IER_PWE | + MAL_IER_DE | MAL_IER_OTE | MAL_IER_OE | MAL_IER_PE ; +#else + mal_ier = + MAL_IER_DE | MAL_IER_NE | MAL_IER_TE | + MAL_IER_OPBE | MAL_IER_PLBE; +#endif + mtdcr (MAL0_ESR, 0xffffffff); /* clear pending interrupts */ + mtdcr (MAL0_TXDEIR, 0xffffffff); /* clear pending interrupts */ + mtdcr (MAL0_RXDEIR, 0xffffffff); /* clear pending interrupts */ + mtdcr (MAL0_IER, mal_ier); + + /* install MAL interrupt handler */ + irq_install_handler (VECNUM_MAL_SERR, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MAL_TXEOB, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MAL_RXEOB, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MAL_TXDE, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MAL_RXDE, + (interrupt_handler_t *) enetInt, + dev); + virgin = 1; + } + } /* end for each supported device */ + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/8390.h b/qemu/roms/u-boot/drivers/net/8390.h new file mode 100644 index 000000000..f087217ed --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/8390.h @@ -0,0 +1,124 @@ +/* + +Ported to U-Boot by Christian Pellegrin + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + +*/ + +/* Generic NS8390 register definitions. */ +/* This file is part of Donald Becker's 8390 drivers, and is distributed + under the same license. Auto-loading of 8390.o only in v2.2 - Paul G. + Some of these names and comments originated from the Crynwr + packet drivers, which are distributed under the GPL. */ + +#ifndef _8390_h +#define _8390_h + +/* Some generic ethernet register configurations. */ +#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */ +#define E8390_RX_IRQ_MASK 0x5 +#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */ +#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */ +#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */ +#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */ + +/* Register accessed at EN_CMD, the 8390 base addr. */ +#define E8390_STOP 0x01 /* Stop and reset the chip */ +#define E8390_START 0x02 /* Start the chip, clear reset */ +#define E8390_TRANS 0x04 /* Transmit a frame */ +#define E8390_RREAD 0x08 /* Remote read */ +#define E8390_RWRITE 0x10 /* Remote write */ +#define E8390_NODMA 0x20 /* Remote DMA */ +#define E8390_PAGE0 0x00 /* Select page chip registers */ +#define E8390_PAGE1 0x40 /* using the two high-order bits */ +#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ + +/* + * Only generate indirect loads given a machine that needs them. + * - removed AMIGA_PCMCIA from this list, handled as ISA io now + */ + +#define n2k_inb(port) (*((volatile unsigned char *)(port+CONFIG_DRIVER_NE2000_BASE))) +#define n2k_outb(val,port) (*((volatile unsigned char *)(port+CONFIG_DRIVER_NE2000_BASE)) = val) + +#define EI_SHIFT(x) (x) + +#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */ +/* Page 0 register offsets. */ +#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */ +#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */ +#define EN0_CLDAHI EI_SHIFT(0x02) /* High byte of current local dma addr RD */ +#define EN0_STOPPG EI_SHIFT(0x02) /* Ending page +1 of ring bfr WR */ +#define EN0_BOUNDARY EI_SHIFT(0x03) /* Boundary page of ring bfr RD WR */ +#define EN0_TSR EI_SHIFT(0x04) /* Transmit status reg RD */ +#define EN0_TPSR EI_SHIFT(0x04) /* Transmit starting page WR */ +#define EN0_NCR EI_SHIFT(0x05) /* Number of collision reg RD */ +#define EN0_TCNTLO EI_SHIFT(0x05) /* Low byte of tx byte count WR */ +#define EN0_FIFO EI_SHIFT(0x06) /* FIFO RD */ +#define EN0_TCNTHI EI_SHIFT(0x06) /* High byte of tx byte count WR */ +#define EN0_ISR EI_SHIFT(0x07) /* Interrupt status reg RD WR */ +#define EN0_CRDALO EI_SHIFT(0x08) /* low byte of current remote dma address RD */ +#define EN0_RSARLO EI_SHIFT(0x08) /* Remote start address reg 0 */ +#define EN0_CRDAHI EI_SHIFT(0x09) /* high byte, current remote dma address RD */ +#define EN0_RSARHI EI_SHIFT(0x09) /* Remote start address reg 1 */ +#define EN0_RCNTLO EI_SHIFT(0x0a) /* Remote byte count reg WR */ +#define EN0_RCNTHI EI_SHIFT(0x0b) /* Remote byte count reg WR */ +#define EN0_RSR EI_SHIFT(0x0c) /* rx status reg RD */ +#define EN0_RXCR EI_SHIFT(0x0c) /* RX configuration reg WR */ +#define EN0_TXCR EI_SHIFT(0x0d) /* TX configuration reg WR */ +#define EN0_COUNTER0 EI_SHIFT(0x0d) /* Rcv alignment error counter RD */ +#define EN0_DCFG EI_SHIFT(0x0e) /* Data configuration reg WR */ +#define EN0_COUNTER1 EI_SHIFT(0x0e) /* Rcv CRC error counter RD */ +#define EN0_IMR EI_SHIFT(0x0f) /* Interrupt mask reg WR */ +#define EN0_COUNTER2 EI_SHIFT(0x0f) /* Rcv missed frame error counter RD */ + +/* Bits in EN0_ISR - Interrupt status register */ +#define ENISR_RX 0x01 /* Receiver, no error */ +#define ENISR_TX 0x02 /* Transmitter, no error */ +#define ENISR_RX_ERR 0x04 /* Receiver, with error */ +#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ +#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ +#define ENISR_COUNTERS 0x20 /* Counters need emptying */ +#define ENISR_RDC 0x40 /* remote dma complete */ +#define ENISR_RESET 0x80 /* Reset completed */ +#define ENISR_ALL 0x3f /* Interrupts we will enable */ + +/* Bits in EN0_DCFG - Data config register */ +#define ENDCFG_WTS 0x01 /* word transfer mode selection */ +#define ENDCFG_BOS 0x02 /* byte order selection */ +#define ENDCFG_AUTO_INIT 0x10 /* Auto-init to remove packets from ring */ +#define ENDCFG_FIFO 0x40 /* 8 bytes */ + +/* Page 1 register offsets. */ +#define EN1_PHYS EI_SHIFT(0x01) /* This board's physical enet addr RD WR */ +#define EN1_PHYS_SHIFT(i) EI_SHIFT(i+1) /* Get and set mac address */ +#define EN1_CURPAG EI_SHIFT(0x07) /* Current memory page RD WR */ +#define EN1_MULT EI_SHIFT(0x08) /* Multicast filter mask array (8 bytes) RD WR */ +#define EN1_MULT_SHIFT(i) EI_SHIFT(8+i) /* Get and set multicast filter */ + +/* Bits in received packet status byte and EN0_RSR*/ +#define ENRSR_RXOK 0x01 /* Received a good packet */ +#define ENRSR_CRC 0x02 /* CRC error */ +#define ENRSR_FAE 0x04 /* frame alignment error */ +#define ENRSR_FO 0x08 /* FIFO overrun */ +#define ENRSR_MPA 0x10 /* missed pkt */ +#define ENRSR_PHY 0x20 /* physical/multicast address */ +#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ +#define ENRSR_DEF 0x80 /* deferring */ + +/* Transmitted packet status, EN0_TSR. */ +#define ENTSR_PTX 0x01 /* Packet transmitted without error */ +#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04 /* The transmit collided at least once. */ +#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ +#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ +#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */ +#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ + +#define NIC_RECEIVE_MONITOR_MODE 0x20 + +#endif /* _8390_h */ diff --git a/qemu/roms/u-boot/drivers/net/Makefile b/qemu/roms/u-boot/drivers/net/Makefile new file mode 100644 index 000000000..6005f7e41 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/Makefile @@ -0,0 +1,66 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_PPC4xx_EMAC) += 4xx_enet.o +obj-$(CONFIG_ALTERA_TSE) += altera_tse.o +obj-$(CONFIG_ARMADA100_FEC) += armada100_fec.o +obj-$(CONFIG_DRIVER_AT91EMAC) += at91_emac.o +obj-$(CONFIG_DRIVER_AX88180) += ax88180.o +obj-$(CONFIG_BFIN_MAC) += bfin_mac.o +obj-$(CONFIG_CALXEDA_XGMAC) += calxedaxgmac.o +obj-$(CONFIG_CS8900) += cs8900.o +obj-$(CONFIG_TULIP) += dc2114x.o +obj-$(CONFIG_DESIGNWARE_ETH) += designware.o +obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o +obj-$(CONFIG_DNET) += dnet.o +obj-$(CONFIG_E1000) += e1000.o +obj-$(CONFIG_E1000_SPI) += e1000_spi.o +obj-$(CONFIG_EEPRO100) += eepro100.o +obj-$(CONFIG_ENC28J60) += enc28j60.o +obj-$(CONFIG_EP93XX) += ep93xx_eth.o +obj-$(CONFIG_ETHOC) += ethoc.o +obj-$(CONFIG_FEC_MXC) += fec_mxc.o +obj-$(CONFIG_FSLDMAFEC) += fsl_mcdmafec.o mcfmii.o +obj-$(CONFIG_FTGMAC100) += ftgmac100.o +obj-$(CONFIG_FTMAC110) += ftmac110.o +obj-$(CONFIG_FTMAC100) += ftmac100.o +obj-$(CONFIG_GRETH) += greth.o +obj-$(CONFIG_INCA_IP_SWITCH) += inca-ip_sw.o +obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o +obj-$(CONFIG_DRIVER_KS8695ETH) += ks8695eth.o +obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o +obj-$(CONFIG_LAN91C96) += lan91c96.o +obj-$(CONFIG_MACB) += macb.o +obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o +obj-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o +obj-$(CONFIG_MPC512x_FEC) += mpc512x_fec.o +obj-$(CONFIG_MVGBE) += mvgbe.o +obj-$(CONFIG_NATSEMI) += natsemi.o +obj-$(CONFIG_DRIVER_NE2000) += ne2000.o ne2000_base.o +obj-$(CONFIG_DRIVER_AX88796L) += ax88796.o ne2000_base.o +obj-$(CONFIG_NETCONSOLE) += netconsole.o +obj-$(CONFIG_NS8382X) += ns8382x.o +obj-$(CONFIG_PCNET) += pcnet.o +obj-$(CONFIG_PLB2800_ETHER) += plb2800_eth.o +obj-$(CONFIG_RTL8139) += rtl8139.o +obj-$(CONFIG_RTL8169) += rtl8169.o +obj-$(CONFIG_SH_ETHER) += sh_eth.o +obj-$(CONFIG_SMC91111) += smc91111.o +obj-$(CONFIG_SMC911X) += smc911x.o +obj-$(CONFIG_SUNXI_WEMAC) += sunxi_wemac.o +obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o +obj-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o +obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o +obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o +obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o +obj-$(CONFIG_ULI526X) += uli526x.o +obj-$(CONFIG_VSC7385_ENET) += vsc7385.o +obj-$(CONFIG_XILINX_AXIEMAC) += xilinx_axi_emac.o +obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o +obj-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o xilinx_ll_temac_mdio.o \ + xilinx_ll_temac_fifo.o xilinx_ll_temac_sdma.o +obj-$(CONFIG_ZYNQ_GEM) += zynq_gem.o diff --git a/qemu/roms/u-boot/drivers/net/altera_tse.c b/qemu/roms/u-boot/drivers/net/altera_tse.c new file mode 100644 index 000000000..de517f8da --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/altera_tse.c @@ -0,0 +1,971 @@ +/* + * Altera 10/100/1000 triple speed ethernet mac driver + * + * Copyright (C) 2008 Altera Corporation. + * Copyright (C) 2010 Thomas Chou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "altera_tse.h" + +/* sgdma debug - print descriptor */ +static void alt_sgdma_print_desc(volatile struct alt_sgdma_descriptor *desc) +{ + debug("SGDMA DEBUG :\n"); + debug("desc->source : 0x%x \n", (unsigned int)desc->source); + debug("desc->destination : 0x%x \n", (unsigned int)desc->destination); + debug("desc->next : 0x%x \n", (unsigned int)desc->next); + debug("desc->source_pad : 0x%x \n", (unsigned int)desc->source_pad); + debug("desc->destination_pad : 0x%x \n", + (unsigned int)desc->destination_pad); + debug("desc->next_pad : 0x%x \n", (unsigned int)desc->next_pad); + debug("desc->bytes_to_transfer : 0x%x \n", + (unsigned int)desc->bytes_to_transfer); + debug("desc->actual_bytes_transferred : 0x%x \n", + (unsigned int)desc->actual_bytes_transferred); + debug("desc->descriptor_status : 0x%x \n", + (unsigned int)desc->descriptor_status); + debug("desc->descriptor_control : 0x%x \n", + (unsigned int)desc->descriptor_control); +} + +/* This is a generic routine that the SGDMA mode-specific routines + * call to populate a descriptor. + * arg1 :pointer to first SGDMA descriptor. + * arg2 :pointer to next SGDMA descriptor. + * arg3 :Address to where data to be written. + * arg4 :Address from where data to be read. + * arg5 :no of byte to transaction. + * arg6 :variable indicating to generate start of packet or not + * arg7 :read fixed + * arg8 :write fixed + * arg9 :read burst + * arg10 :write burst + * arg11 :atlantic_channel number + */ +static void alt_sgdma_construct_descriptor_burst( + volatile struct alt_sgdma_descriptor *desc, + volatile struct alt_sgdma_descriptor *next, + unsigned int *read_addr, + unsigned int *write_addr, + unsigned short length_or_eop, + int generate_eop, + int read_fixed, + int write_fixed_or_sop, + int read_burst, + int write_burst, + unsigned char atlantic_channel) +{ + /* + * Mark the "next" descriptor as "not" owned by hardware. This prevents + * The SGDMA controller from continuing to process the chain. This is + * done as a single IO write to bypass cache, without flushing + * the entire descriptor, since only the 8-bit descriptor status must + * be flushed. + */ + if (!next) + debug("Next descriptor not defined!!\n"); + + next->descriptor_control = (next->descriptor_control & + ~ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK); + + desc->source = (unsigned int *)((unsigned int)read_addr & 0x1FFFFFFF); + desc->destination = + (unsigned int *)((unsigned int)write_addr & 0x1FFFFFFF); + desc->next = (unsigned int *)((unsigned int)next & 0x1FFFFFFF); + desc->source_pad = 0x0; + desc->destination_pad = 0x0; + desc->next_pad = 0x0; + desc->bytes_to_transfer = length_or_eop; + desc->actual_bytes_transferred = 0; + desc->descriptor_status = 0x0; + + /* SGDMA burst not currently supported */ + desc->read_burst = 0; + desc->write_burst = 0; + + /* + * Set the descriptor control block as follows: + * - Set "owned by hardware" bit + * - Optionally set "generate EOP" bit + * - Optionally set the "read from fixed address" bit + * - Optionally set the "write to fixed address bit (which serves + * serves as a "generate SOP" control bit in memory-to-stream mode). + * - Set the 4-bit atlantic channel, if specified + * + * Note this step is performed after all other descriptor information + * has been filled out so that, if the controller already happens to be + * pointing at this descriptor, it will not run (via the "owned by + * hardware" bit) until all other descriptor has been set up. + */ + + desc->descriptor_control = + ((ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK) | + (generate_eop ? + ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK : 0x0) | + (read_fixed ? + ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK : 0x0) | + (write_fixed_or_sop ? + ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK : 0x0) | + (atlantic_channel ? ((atlantic_channel & 0x0F) << 3) : 0) + ); +} + +static int alt_sgdma_do_sync_transfer(volatile struct alt_sgdma_registers *dev, + volatile struct alt_sgdma_descriptor *desc) +{ + unsigned int status; + int counter = 0; + + /* Wait for any pending transfers to complete */ + alt_sgdma_print_desc(desc); + status = dev->status; + + counter = 0; + while (dev->status & ALT_SGDMA_STATUS_BUSY_MSK) { + if (counter++ > ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + debug("Timeout waiting sgdma in do sync!\n"); + + /* + * Clear any (previous) status register information + * that might occlude our error checking later. + */ + dev->status = 0xFF; + + /* Point the controller at the descriptor */ + dev->next_descriptor_pointer = (unsigned int)desc & 0x1FFFFFFF; + debug("next desc in sgdma 0x%x\n", + (unsigned int)dev->next_descriptor_pointer); + + /* + * Set up SGDMA controller to: + * - Disable interrupt generation + * - Run once a valid descriptor is written to controller + * - Stop on an error with any particular descriptor + */ + dev->control = (ALT_SGDMA_CONTROL_RUN_MSK | + ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK); + + /* Wait for the descriptor (chain) to complete */ + status = dev->status; + debug("wait for sgdma...."); + while (dev->status & ALT_SGDMA_STATUS_BUSY_MSK) + ; + debug("done\n"); + + /* Clear Run */ + dev->control = (dev->control & (~ALT_SGDMA_CONTROL_RUN_MSK)); + + /* Get & clear status register contents */ + status = dev->status; + dev->status = 0xFF; + + /* we really should check if the transfer completes properly */ + debug("tx sgdma status = 0x%x", status); + return 0; +} + +static int alt_sgdma_do_async_transfer(volatile struct alt_sgdma_registers *dev, + volatile struct alt_sgdma_descriptor *desc) +{ + unsigned int status; + int counter = 0; + + /* Wait for any pending transfers to complete */ + alt_sgdma_print_desc(desc); + status = dev->status; + + counter = 0; + while (dev->status & ALT_SGDMA_STATUS_BUSY_MSK) { + if (counter++ > ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + debug("Timeout waiting sgdma in do async!\n"); + + /* + * Clear the RUN bit in the control register. This is needed + * to restart the SGDMA engine later on. + */ + dev->control = 0; + + /* + * Clear any (previous) status register information + * that might occlude our error checking later. + */ + dev->status = 0xFF; + + /* Point the controller at the descriptor */ + dev->next_descriptor_pointer = (unsigned int)desc & 0x1FFFFFFF; + + /* + * Set up SGDMA controller to: + * - Disable interrupt generation + * - Run once a valid descriptor is written to controller + * - Stop on an error with any particular descriptor + */ + dev->control = (ALT_SGDMA_CONTROL_RUN_MSK | + ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK); + + /* we really should check if the transfer completes properly */ + return 0; +} + +/* u-boot interface */ +static int tse_adjust_link(struct altera_tse_priv *priv) +{ + unsigned int refvar; + + refvar = priv->mac_dev->command_config.image; + + if (!(priv->duplexity)) + refvar |= ALTERA_TSE_CMD_HD_ENA_MSK; + else + refvar &= ~ALTERA_TSE_CMD_HD_ENA_MSK; + + switch (priv->speed) { + case 1000: + refvar |= ALTERA_TSE_CMD_ETH_SPEED_MSK; + refvar &= ~ALTERA_TSE_CMD_ENA_10_MSK; + break; + case 100: + refvar &= ~ALTERA_TSE_CMD_ETH_SPEED_MSK; + refvar &= ~ALTERA_TSE_CMD_ENA_10_MSK; + break; + case 10: + refvar &= ~ALTERA_TSE_CMD_ETH_SPEED_MSK; + refvar |= ALTERA_TSE_CMD_ENA_10_MSK; + break; + } + priv->mac_dev->command_config.image = refvar; + + return 0; +} + +static int tse_eth_send(struct eth_device *dev, void *packet, int length) +{ + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx; + volatile struct alt_sgdma_descriptor *tx_desc = + (volatile struct alt_sgdma_descriptor *)priv->tx_desc; + + volatile struct alt_sgdma_descriptor *tx_desc_cur = + (volatile struct alt_sgdma_descriptor *)&tx_desc[0]; + + flush_dcache_range((unsigned long)packet, + (unsigned long)packet + length); + alt_sgdma_construct_descriptor_burst( + (volatile struct alt_sgdma_descriptor *)&tx_desc[0], + (volatile struct alt_sgdma_descriptor *)&tx_desc[1], + (unsigned int *)packet, /* read addr */ + (unsigned int *)0, + length, /* length or EOP ,will change for each tx */ + 0x1, /* gen eop */ + 0x0, /* read fixed */ + 0x1, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + debug("TX Packet @ 0x%x,0x%x bytes", (unsigned int)packet, length); + + /* send the packet */ + debug("sending packet\n"); + alt_sgdma_do_sync_transfer(tx_sgdma, tx_desc_cur); + debug("sent %d bytes\n", tx_desc_cur->actual_bytes_transferred); + return tx_desc_cur->actual_bytes_transferred; +} + +static int tse_eth_rx(struct eth_device *dev) +{ + int packet_length = 0; + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_sgdma_descriptor *rx_desc = + (volatile struct alt_sgdma_descriptor *)priv->rx_desc; + volatile struct alt_sgdma_descriptor *rx_desc_cur = &rx_desc[0]; + + if (rx_desc_cur->descriptor_status & + ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK) { + debug("got packet\n"); + packet_length = rx_desc->actual_bytes_transferred; + NetReceive(NetRxPackets[0], packet_length); + + /* start descriptor again */ + flush_dcache_range((unsigned long)(NetRxPackets[0]), + (unsigned long)(NetRxPackets[0]) + PKTSIZE_ALIGN); + alt_sgdma_construct_descriptor_burst( + (volatile struct alt_sgdma_descriptor *)&rx_desc[0], + (volatile struct alt_sgdma_descriptor *)&rx_desc[1], + (unsigned int)0x0, /* read addr */ + (unsigned int *)NetRxPackets[0], + 0x0, /* length or EOP */ + 0x0, /* gen eop */ + 0x0, /* read fixed */ + 0x0, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + + /* setup the sgdma */ + alt_sgdma_do_async_transfer(priv->sgdma_rx, &rx_desc[0]); + + return packet_length; + } + + return -1; +} + +static void tse_eth_halt(struct eth_device *dev) +{ + /* don't do anything! */ + /* this gets called after each uboot */ + /* network command. don't need to reset the thing all of the time */ +} + +static void tse_eth_reset(struct eth_device *dev) +{ + /* stop sgdmas, disable tse receive */ + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_tse_mac *mac_dev = priv->mac_dev; + volatile struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx; + volatile struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx; + int counter; + volatile struct alt_sgdma_descriptor *rx_desc = + (volatile struct alt_sgdma_descriptor *)&priv->rx_desc[0]; + + /* clear rx desc & wait for sgdma to complete */ + rx_desc->descriptor_control = 0; + rx_sgdma->control = 0; + counter = 0; + while (rx_sgdma->status & ALT_SGDMA_STATUS_BUSY_MSK) { + if (counter++ > ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) { + debug("Timeout waiting for rx sgdma!\n"); + rx_sgdma->control = ALT_SGDMA_CONTROL_SOFTWARERESET_MSK; + rx_sgdma->control = ALT_SGDMA_CONTROL_SOFTWARERESET_MSK; + } + + counter = 0; + tx_sgdma->control = 0; + while (tx_sgdma->status & ALT_SGDMA_STATUS_BUSY_MSK) { + if (counter++ > ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) { + debug("Timeout waiting for tx sgdma!\n"); + tx_sgdma->control = ALT_SGDMA_CONTROL_SOFTWARERESET_MSK; + tx_sgdma->control = ALT_SGDMA_CONTROL_SOFTWARERESET_MSK; + } + /* reset the mac */ + mac_dev->command_config.bits.transmit_enable = 1; + mac_dev->command_config.bits.receive_enable = 1; + mac_dev->command_config.bits.software_reset = 1; + + counter = 0; + while (mac_dev->command_config.bits.software_reset) { + if (counter++ > ALT_TSE_SW_RESET_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SW_RESET_WATCHDOG_CNTR) + debug("TSEMAC SW reset bit never cleared!\n"); +} + +static int tse_mdio_read(struct altera_tse_priv *priv, unsigned int regnum) +{ + volatile struct alt_tse_mac *mac_dev; + unsigned int *mdio_regs; + unsigned int data; + u16 value; + + mac_dev = priv->mac_dev; + + /* set mdio address */ + mac_dev->mdio_phy1_addr = priv->phyaddr; + mdio_regs = (unsigned int *)&mac_dev->mdio_phy1; + + /* get the data */ + data = mdio_regs[regnum]; + + value = data & 0xffff; + + return value; +} + +static int tse_mdio_write(struct altera_tse_priv *priv, unsigned int regnum, + unsigned int value) +{ + volatile struct alt_tse_mac *mac_dev; + unsigned int *mdio_regs; + unsigned int data; + + mac_dev = priv->mac_dev; + + /* set mdio address */ + mac_dev->mdio_phy1_addr = priv->phyaddr; + mdio_regs = (unsigned int *)&mac_dev->mdio_phy1; + + /* get the data */ + data = (unsigned int)value; + + mdio_regs[regnum] = data; + + return 0; +} + +/* MDIO access to phy */ +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII) +static int altera_tse_miiphy_write(const char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct eth_device *dev; + struct altera_tse_priv *priv; + dev = eth_get_dev_by_name(devname); + priv = dev->priv; + + tse_mdio_write(priv, (uint) reg, (uint) value); + + return 0; +} + +static int altera_tse_miiphy_read(const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + struct eth_device *dev; + struct altera_tse_priv *priv; + volatile struct alt_tse_mac *mac_dev; + unsigned int *mdio_regs; + + dev = eth_get_dev_by_name(devname); + priv = dev->priv; + + mac_dev = priv->mac_dev; + mac_dev->mdio_phy1_addr = (int)addr; + mdio_regs = (unsigned int *)&mac_dev->mdio_phy1; + + *value = 0xffff & mdio_regs[reg]; + + return 0; + +} +#endif + +/* + * Also copied from tsec.c + */ +/* Parse the status register for link, and then do + * auto-negotiation + */ +static uint mii_parse_sr(uint mii_reg, struct altera_tse_priv *priv) +{ + /* + * Wait if the link is up, and autonegotiation is in progress + * (ie - we're capable and it's not done) + */ + mii_reg = tse_mdio_read(priv, MIIM_STATUS); + + if (!(mii_reg & MIIM_STATUS_LINK) && (mii_reg & BMSR_ANEGCAPABLE) + && !(mii_reg & BMSR_ANEGCOMPLETE)) { + int i = 0; + + puts("Waiting for PHY auto negotiation to complete"); + while (!(mii_reg & BMSR_ANEGCOMPLETE)) { + /* + * Timeout reached ? + */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts(" TIMEOUT !\n"); + priv->link = 0; + return 0; + } + + if ((i++ % 1000) == 0) + putc('.'); + udelay(1000); /* 1 ms */ + mii_reg = tse_mdio_read(priv, MIIM_STATUS); + } + puts(" done\n"); + priv->link = 1; + udelay(500000); /* another 500 ms (results in faster booting) */ + } else { + if (mii_reg & MIIM_STATUS_LINK) { + debug("Link is up\n"); + priv->link = 1; + } else { + debug("Link is down\n"); + priv->link = 0; + } + } + + return 0; +} + +/* Parse the 88E1011's status register for speed and duplex + * information + */ +static uint mii_parse_88E1011_psr(uint mii_reg, struct altera_tse_priv *priv) +{ + uint speed; + + mii_reg = tse_mdio_read(priv, MIIM_88E1011_PHY_STATUS); + + if ((mii_reg & MIIM_88E1011_PHYSTAT_LINK) && + !(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) { + int i = 0; + + puts("Waiting for PHY realtime link"); + while (!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) { + /* Timeout reached ? */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts(" TIMEOUT !\n"); + priv->link = 0; + break; + } + + if ((i++ == 1000) == 0) { + i = 0; + puts("."); + } + udelay(1000); /* 1 ms */ + mii_reg = tse_mdio_read(priv, MIIM_88E1011_PHY_STATUS); + } + puts(" done\n"); + udelay(500000); /* another 500 ms (results in faster booting) */ + } else { + if (mii_reg & MIIM_88E1011_PHYSTAT_LINK) + priv->link = 1; + else + priv->link = 0; + } + + if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX) + priv->duplexity = 1; + else + priv->duplexity = 0; + + speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED); + + switch (speed) { + case MIIM_88E1011_PHYSTAT_GBIT: + priv->speed = 1000; + debug("PHY Speed is 1000Mbit\n"); + break; + case MIIM_88E1011_PHYSTAT_100: + debug("PHY Speed is 100Mbit\n"); + priv->speed = 100; + break; + default: + debug("PHY Speed is 10Mbit\n"); + priv->speed = 10; + } + + return 0; +} + +static uint mii_m88e1111s_setmode_sr(uint mii_reg, struct altera_tse_priv *priv) +{ + uint mii_data = tse_mdio_read(priv, mii_reg); + mii_data &= 0xfff0; + if ((priv->flags >= 1) && (priv->flags <= 4)) + mii_data |= 0xb; + else if (priv->flags == 5) + mii_data |= 0x4; + + return mii_data; +} + +static uint mii_m88e1111s_setmode_cr(uint mii_reg, struct altera_tse_priv *priv) +{ + uint mii_data = tse_mdio_read(priv, mii_reg); + mii_data &= ~0x82; + if ((priv->flags >= 1) && (priv->flags <= 4)) + mii_data |= 0x82; + + return mii_data; +} + +/* + * Returns which value to write to the control register. + * For 10/100, the value is slightly different + */ +static uint mii_cr_init(uint mii_reg, struct altera_tse_priv *priv) +{ + return MIIM_CONTROL_INIT; +} + +/* + * PHY & MDIO code + * Need to add SGMII stuff + * + */ + +static struct phy_info phy_info_M88E1111S = { + 0x01410cc, + "Marvell 88E1111S", + 4, + (struct phy_cmd[]){ /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_88E1111_PHY_EXT_SR, 0x848f, + &mii_m88e1111s_setmode_sr}, + /* Delay RGMII TX and RX */ + {MIIM_88E1111_PHY_EXT_CR, 0x0cd2, + &mii_m88e1111s_setmode_cr}, + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]){ /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_88E1011_PHY_STATUS, miim_read, + &mii_parse_88E1011_psr}, + {miim_end,} + }, + (struct phy_cmd[]){ /* shutdown */ + {miim_end,} + }, +}; + +/* a generic flavor. */ +static struct phy_info phy_info_generic = { + 0, + "Unknown/Generic PHY", + 32, + (struct phy_cmd[]){ /* config */ + {MII_BMCR, BMCR_RESET, NULL}, + {MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART, NULL}, + {miim_end,} + }, + (struct phy_cmd[]){ /* startup */ + {MII_BMSR, miim_read, NULL}, + {MII_BMSR, miim_read, &mii_parse_sr}, + {miim_end,} + }, + (struct phy_cmd[]){ /* shutdown */ + {miim_end,} + } +}; + +static struct phy_info *phy_info[] = { + &phy_info_M88E1111S, + NULL +}; + + /* Grab the identifier of the device's PHY, and search through + * all of the known PHYs to see if one matches. If so, return + * it, if not, return NULL + */ +static struct phy_info *get_phy_info(struct eth_device *dev) +{ + struct altera_tse_priv *priv = (struct altera_tse_priv *)dev->priv; + uint phy_reg, phy_ID; + int i; + struct phy_info *theInfo = NULL; + + /* Grab the bits from PHYIR1, and put them in the upper half */ + phy_reg = tse_mdio_read(priv, MIIM_PHYIR1); + phy_ID = (phy_reg & 0xffff) << 16; + + /* Grab the bits from PHYIR2, and put them in the lower half */ + phy_reg = tse_mdio_read(priv, MIIM_PHYIR2); + phy_ID |= (phy_reg & 0xffff); + + /* loop through all the known PHY types, and find one that */ + /* matches the ID we read from the PHY. */ + for (i = 0; phy_info[i]; i++) { + if (phy_info[i]->id == (phy_ID >> phy_info[i]->shift)) { + theInfo = phy_info[i]; + break; + } + } + + if (theInfo == NULL) { + theInfo = &phy_info_generic; + debug("%s: No support for PHY id %x; assuming generic\n", + dev->name, phy_ID); + } else + debug("%s: PHY is %s (%x)\n", dev->name, theInfo->name, phy_ID); + + return theInfo; +} + +/* Execute the given series of commands on the given device's + * PHY, running functions as necessary + */ +static void phy_run_commands(struct altera_tse_priv *priv, struct phy_cmd *cmd) +{ + int i; + uint result; + + for (i = 0; cmd->mii_reg != miim_end; i++) { + if (cmd->mii_data == miim_read) { + result = tse_mdio_read(priv, cmd->mii_reg); + + if (cmd->funct != NULL) + (*(cmd->funct)) (result, priv); + + } else { + if (cmd->funct != NULL) + result = (*(cmd->funct)) (cmd->mii_reg, priv); + else + result = cmd->mii_data; + + tse_mdio_write(priv, cmd->mii_reg, result); + + } + cmd++; + } +} + +/* Phy init code */ +static int init_phy(struct eth_device *dev) +{ + struct altera_tse_priv *priv = (struct altera_tse_priv *)dev->priv; + struct phy_info *curphy; + + /* Get the cmd structure corresponding to the attached + * PHY */ + curphy = get_phy_info(dev); + + if (curphy == NULL) { + priv->phyinfo = NULL; + debug("%s: No PHY found\n", dev->name); + + return 0; + } else + debug("%s found\n", curphy->name); + priv->phyinfo = curphy; + + phy_run_commands(priv, priv->phyinfo->config); + + return 1; +} + +static int tse_set_mac_address(struct eth_device *dev) +{ + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_tse_mac *mac_dev = priv->mac_dev; + + debug("Setting MAC address to 0x%02x%02x%02x%02x%02x%02x\n", + dev->enetaddr[5], dev->enetaddr[4], + dev->enetaddr[3], dev->enetaddr[2], + dev->enetaddr[1], dev->enetaddr[0]); + mac_dev->mac_addr_0 = ((dev->enetaddr[3]) << 24 | + (dev->enetaddr[2]) << 16 | + (dev->enetaddr[1]) << 8 | (dev->enetaddr[0])); + + mac_dev->mac_addr_1 = ((dev->enetaddr[5] << 8 | + (dev->enetaddr[4])) & 0xFFFF); + + /* Set the MAC address */ + mac_dev->supp_mac_addr_0_0 = mac_dev->mac_addr_0; + mac_dev->supp_mac_addr_0_1 = mac_dev->mac_addr_1; + + /* Set the MAC address */ + mac_dev->supp_mac_addr_1_0 = mac_dev->mac_addr_0; + mac_dev->supp_mac_addr_1_1 = mac_dev->mac_addr_1; + + /* Set the MAC address */ + mac_dev->supp_mac_addr_2_0 = mac_dev->mac_addr_0; + mac_dev->supp_mac_addr_2_1 = mac_dev->mac_addr_1; + + /* Set the MAC address */ + mac_dev->supp_mac_addr_3_0 = mac_dev->mac_addr_0; + mac_dev->supp_mac_addr_3_1 = mac_dev->mac_addr_1; + return 0; +} + +static int tse_eth_init(struct eth_device *dev, bd_t * bd) +{ + int dat; + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_tse_mac *mac_dev = priv->mac_dev; + volatile struct alt_sgdma_descriptor *tx_desc = priv->tx_desc; + volatile struct alt_sgdma_descriptor *rx_desc = priv->rx_desc; + volatile struct alt_sgdma_descriptor *rx_desc_cur = + (volatile struct alt_sgdma_descriptor *)&rx_desc[0]; + + /* stop controller */ + debug("Reseting TSE & SGDMAs\n"); + tse_eth_reset(dev); + + /* start the phy */ + debug("Configuring PHY\n"); + phy_run_commands(priv, priv->phyinfo->startup); + + /* need to create sgdma */ + debug("Configuring tx desc\n"); + alt_sgdma_construct_descriptor_burst( + (volatile struct alt_sgdma_descriptor *)&tx_desc[0], + (volatile struct alt_sgdma_descriptor *)&tx_desc[1], + (unsigned int *)NULL, /* read addr */ + (unsigned int *)0, + 0, /* length or EOP ,will change for each tx */ + 0x1, /* gen eop */ + 0x0, /* read fixed */ + 0x1, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + debug("Configuring rx desc\n"); + flush_dcache_range((unsigned long)(NetRxPackets[0]), + (unsigned long)(NetRxPackets[0]) + PKTSIZE_ALIGN); + alt_sgdma_construct_descriptor_burst( + (volatile struct alt_sgdma_descriptor *)&rx_desc[0], + (volatile struct alt_sgdma_descriptor *)&rx_desc[1], + (unsigned int)0x0, /* read addr */ + (unsigned int *)NetRxPackets[0], + 0x0, /* length or EOP */ + 0x0, /* gen eop */ + 0x0, /* read fixed */ + 0x0, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + /* start rx async transfer */ + debug("Starting rx sgdma\n"); + alt_sgdma_do_async_transfer(priv->sgdma_rx, rx_desc_cur); + + /* start TSE */ + debug("Configuring TSE Mac\n"); + /* Initialize MAC registers */ + mac_dev->max_frame_length = PKTSIZE_ALIGN; + mac_dev->rx_almost_empty_threshold = 8; + mac_dev->rx_almost_full_threshold = 8; + mac_dev->tx_almost_empty_threshold = 8; + mac_dev->tx_almost_full_threshold = 3; + mac_dev->tx_sel_empty_threshold = + CONFIG_SYS_ALTERA_TSE_TX_FIFO - 16; + mac_dev->tx_sel_full_threshold = 0; + mac_dev->rx_sel_empty_threshold = + CONFIG_SYS_ALTERA_TSE_TX_FIFO - 16; + mac_dev->rx_sel_full_threshold = 0; + + /* NO Shift */ + mac_dev->rx_cmd_stat.bits.rx_shift16 = 0; + mac_dev->tx_cmd_stat.bits.tx_shift16 = 0; + + /* enable MAC */ + dat = 0; + dat = ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK; + + mac_dev->command_config.image = dat; + + /* configure the TSE core */ + /* -- output clocks, */ + /* -- and later config stuff for SGMII */ + if (priv->link) { + debug("Adjusting TSE to link speed\n"); + tse_adjust_link(priv); + } + + return priv->link ? 0 : -1; +} + +/* TSE init code */ +int altera_tse_initialize(u8 dev_num, int mac_base, + int sgdma_rx_base, int sgdma_tx_base, + u32 sgdma_desc_base, u32 sgdma_desc_size) +{ + struct altera_tse_priv *priv; + struct eth_device *dev; + struct alt_sgdma_descriptor *rx_desc; + struct alt_sgdma_descriptor *tx_desc; + unsigned long dma_handle; + + dev = (struct eth_device *)malloc(sizeof *dev); + + if (NULL == dev) + return 0; + + memset(dev, 0, sizeof *dev); + + priv = malloc(sizeof(*priv)); + + if (!priv) { + free(dev); + return 0; + } + if (sgdma_desc_size) { + if (sgdma_desc_size < (sizeof(*tx_desc) * (3 + PKTBUFSRX))) { + printf("ALTERA_TSE-%hu: " + "descriptor memory is too small\n", dev_num); + free(priv); + free(dev); + return 0; + } + tx_desc = (struct alt_sgdma_descriptor *)sgdma_desc_base; + } else { + tx_desc = dma_alloc_coherent(sizeof(*tx_desc) * (3 + PKTBUFSRX), + &dma_handle); + } + + rx_desc = tx_desc + 2; + debug("tx desc: address = 0x%x\n", (unsigned int)tx_desc); + debug("rx desc: address = 0x%x\n", (unsigned int)rx_desc); + + if (!tx_desc) { + free(priv); + free(dev); + return 0; + } + memset(rx_desc, 0, (sizeof *rx_desc) * (PKTBUFSRX + 1)); + memset(tx_desc, 0, (sizeof *tx_desc) * 2); + + /* initialize tse priv */ + priv->mac_dev = (volatile struct alt_tse_mac *)mac_base; + priv->sgdma_rx = (volatile struct alt_sgdma_registers *)sgdma_rx_base; + priv->sgdma_tx = (volatile struct alt_sgdma_registers *)sgdma_tx_base; + priv->phyaddr = CONFIG_SYS_ALTERA_TSE_PHY_ADDR; + priv->flags = CONFIG_SYS_ALTERA_TSE_FLAGS; + priv->rx_desc = rx_desc; + priv->tx_desc = tx_desc; + + /* init eth structure */ + dev->priv = priv; + dev->init = tse_eth_init; + dev->halt = tse_eth_halt; + dev->send = tse_eth_send; + dev->recv = tse_eth_rx; + dev->write_hwaddr = tse_set_mac_address; + sprintf(dev->name, "%s-%hu", "ALTERA_TSE", dev_num); + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII) + miiphy_register(dev->name, altera_tse_miiphy_read, + altera_tse_miiphy_write); +#endif + + init_phy(dev); + + return 1; +} diff --git a/qemu/roms/u-boot/drivers/net/altera_tse.h b/qemu/roms/u-boot/drivers/net/altera_tse.h new file mode 100644 index 000000000..8880bfc0f --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/altera_tse.h @@ -0,0 +1,492 @@ +/* + * Altera 10/100/1000 triple speed ethernet mac + * + * Copyright (C) 2008 Altera Corporation. + * Copyright (C) 2010 Thomas Chou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _ALTERA_TSE_H_ +#define _ALTERA_TSE_H_ + +#define __packed_1_ __attribute__ ((packed, aligned(1))) + +/* PHY Stuff */ +#define miim_end -2 +#define miim_read -1 + +#define PHY_AUTONEGOTIATE_TIMEOUT 5000 /* in ms */ + +#ifndef CONFIG_SYS_TBIPA_VALUE +#define CONFIG_SYS_TBIPA_VALUE 0x1f +#endif +#define MIIMCFG_INIT_VALUE 0x00000003 +#define MIIMCFG_RESET 0x80000000 + +#define MIIMIND_BUSY 0x00000001 +#define MIIMIND_NOTVALID 0x00000004 + +#define MIIM_CONTROL 0x00 +#define MIIM_CONTROL_RESET 0x00009140 +#define MIIM_CONTROL_INIT 0x00001140 +#define MIIM_CONTROL_RESTART 0x00001340 +#define MIIM_ANEN 0x00001000 + +#define MIIM_CR 0x00 +#define MIIM_CR_RST 0x00008000 +#define MIIM_CR_INIT 0x00001000 + +#define MIIM_STATUS 0x1 +#define MIIM_STATUS_AN_DONE 0x00000020 +#define MIIM_STATUS_LINK 0x0004 + +#define MIIM_PHYIR1 0x2 +#define MIIM_PHYIR2 0x3 + +#define MIIM_ANAR 0x4 +#define MIIM_ANAR_INIT 0x1e1 + +#define MIIM_TBI_ANLPBPA 0x5 +#define MIIM_TBI_ANLPBPA_HALF 0x00000040 +#define MIIM_TBI_ANLPBPA_FULL 0x00000020 + +#define MIIM_TBI_ANEX 0x6 +#define MIIM_TBI_ANEX_NP 0x00000004 +#define MIIM_TBI_ANEX_PRX 0x00000002 + +#define MIIM_GBIT_CONTROL 0x9 +#define MIIM_GBIT_CONTROL_INIT 0xe00 + +#define MIIM_EXT_PAGE_ACCESS 0x1f + +/* 88E1011 PHY Status Register */ +#define MIIM_88E1011_PHY_STATUS 0x11 +#define MIIM_88E1011_PHYSTAT_SPEED 0xc000 +#define MIIM_88E1011_PHYSTAT_GBIT 0x8000 +#define MIIM_88E1011_PHYSTAT_100 0x4000 +#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000 +#define MIIM_88E1011_PHYSTAT_SPDDONE 0x0800 +#define MIIM_88E1011_PHYSTAT_LINK 0x0400 + +#define MIIM_88E1011_PHY_SCR 0x10 +#define MIIM_88E1011_PHY_MDI_X_AUTO 0x0060 + +#define MIIM_88E1111_PHY_EXT_CR 0x14 +#define MIIM_88E1111_PHY_EXT_SR 0x1b + +/* 88E1111 PHY LED Control Register */ +#define MIIM_88E1111_PHY_LED_CONTROL 24 +#define MIIM_88E1111_PHY_LED_DIRECT 0x4100 +#define MIIM_88E1111_PHY_LED_COMBINE 0x411C + +#define MIIM_READ_COMMAND 0x00000001 + +/* struct phy_info: a structure which defines attributes for a PHY + * id will contain a number which represents the PHY. During + * startup, the driver will poll the PHY to find out what its + * UID--as defined by registers 2 and 3--is. The 32-bit result + * gotten from the PHY will be shifted right by "shift" bits to + * discard any bits which may change based on revision numbers + * unimportant to functionality + * + * The struct phy_cmd entries represent pointers to an arrays of + * commands which tell the driver what to do to the PHY. + */ +struct phy_info { + uint id; + char *name; + uint shift; + /* Called to configure the PHY, and modify the controller + * based on the results */ + struct phy_cmd *config; + + /* Called when starting up the controller */ + struct phy_cmd *startup; + + /* Called when bringing down the controller */ + struct phy_cmd *shutdown; +}; + +/* SGDMA Stuff */ +#define ALT_SGDMA_STATUS_ERROR_MSK (0x00000001) +#define ALT_SGDMA_STATUS_EOP_ENCOUNTERED_MSK (0x00000002) +#define ALT_SGDMA_STATUS_DESC_COMPLETED_MSK (0x00000004) +#define ALT_SGDMA_STATUS_CHAIN_COMPLETED_MSK (0x00000008) +#define ALT_SGDMA_STATUS_BUSY_MSK (0x00000010) + +#define ALT_SGDMA_CONTROL_IE_ERROR_MSK (0x00000001) +#define ALT_SGDMA_CONTROL_IE_EOP_ENCOUNTERED_MSK (0x00000002) +#define ALT_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK (0x00000004) +#define ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK (0x00000008) +#define ALT_SGDMA_CONTROL_IE_GLOBAL_MSK (0x00000010) +#define ALT_SGDMA_CONTROL_RUN_MSK (0x00000020) +#define ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK (0x00000040) +#define ALT_SGDMA_CONTROL_IE_MAX_DESC_PROCESSED_MSK (0x00000080) +#define ALT_SGDMA_CONTROL_MAX_DESC_PROCESSED_MSK (0x0000FF00) +#define ALT_SGDMA_CONTROL_SOFTWARERESET_MSK (0x00010000) +#define ALT_SGDMA_CONTROL_PARK_MSK (0x00020000) +#define ALT_SGDMA_CONTROL_CLEAR_INTERRUPT_MSK (0x80000000) + +#define ALTERA_TSE_SGDMA_INTR_MASK (ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK \ + | ALT_SGDMA_STATUS_DESC_COMPLETED_MSK \ + | ALT_SGDMA_CONTROL_IE_GLOBAL_MSK) + +/* + * Descriptor control bit masks & offsets + * + * Note: The control byte physically occupies bits [31:24] in memory. + * The following bit-offsets are expressed relative to the LSB of + * the control register bitfield. + */ +#define ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK (0x00000001) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK (0x00000002) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK (0x00000004) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_ATLANTIC_CHANNEL_MSK (0x00000008) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK (0x00000080) + +/* + * Descriptor status bit masks & offsets + * + * Note: The status byte physically occupies bits [23:16] in memory. + * The following bit-offsets are expressed relative to the LSB of + * the status register bitfield. + */ +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_CRC_MSK (0x00000001) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_PARITY_MSK (0x00000002) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_OVERFLOW_MSK (0x00000004) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_SYNC_MSK (0x00000008) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_UEOP_MSK (0x00000010) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MEOP_MSK (0x00000020) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MSOP_MSK (0x00000040) +#define ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK (0x00000080) +#define ALT_SGDMA_DESCRIPTOR_STATUS_ERROR_MSK (0x0000007F) + +/* + * The SGDMA controller buffer descriptor allocates + * 64 bits for each address. To support ANSI C, the + * struct implementing a descriptor places 32-bits + * of padding directly above each address; each pad must + * be cleared when initializing a descriptor. + */ + +/* + * Buffer Descriptor data structure + * + */ +struct alt_sgdma_descriptor { + unsigned int *source; /* the address of data to be read. */ + unsigned int source_pad; + + unsigned int *destination; /* the address to write data */ + unsigned int destination_pad; + + unsigned int *next; /* the next descriptor in the list. */ + unsigned int next_pad; + + unsigned short bytes_to_transfer; /* the number of bytes to transfer */ + unsigned char read_burst; + unsigned char write_burst; + + unsigned short actual_bytes_transferred;/* bytes transferred by DMA */ + unsigned char descriptor_status; + unsigned char descriptor_control; + +} __packed_1_; + +/* SG-DMA Control/Status Slave registers map */ + +struct alt_sgdma_registers { + unsigned int status; + unsigned int status_pad[3]; + unsigned int control; + unsigned int control_pad[3]; + unsigned int next_descriptor_pointer; + unsigned int descriptor_pad[3]; +}; + +/* TSE Stuff */ +#define ALTERA_TSE_CMD_TX_ENA_MSK (0x00000001) +#define ALTERA_TSE_CMD_RX_ENA_MSK (0x00000002) +#define ALTERA_TSE_CMD_XON_GEN_MSK (0x00000004) +#define ALTERA_TSE_CMD_ETH_SPEED_MSK (0x00000008) +#define ALTERA_TSE_CMD_PROMIS_EN_MSK (0x00000010) +#define ALTERA_TSE_CMD_PAD_EN_MSK (0x00000020) +#define ALTERA_TSE_CMD_CRC_FWD_MSK (0x00000040) +#define ALTERA_TSE_CMD_PAUSE_FWD_MSK (0x00000080) +#define ALTERA_TSE_CMD_PAUSE_IGNORE_MSK (0x00000100) +#define ALTERA_TSE_CMD_TX_ADDR_INS_MSK (0x00000200) +#define ALTERA_TSE_CMD_HD_ENA_MSK (0x00000400) +#define ALTERA_TSE_CMD_EXCESS_COL_MSK (0x00000800) +#define ALTERA_TSE_CMD_LATE_COL_MSK (0x00001000) +#define ALTERA_TSE_CMD_SW_RESET_MSK (0x00002000) +#define ALTERA_TSE_CMD_MHASH_SEL_MSK (0x00004000) +#define ALTERA_TSE_CMD_LOOPBACK_MSK (0x00008000) +/* Bits (18:16) = address select */ +#define ALTERA_TSE_CMD_TX_ADDR_SEL_MSK (0x00070000) +#define ALTERA_TSE_CMD_MAGIC_ENA_MSK (0x00080000) +#define ALTERA_TSE_CMD_SLEEP_MSK (0x00100000) +#define ALTERA_TSE_CMD_WAKEUP_MSK (0x00200000) +#define ALTERA_TSE_CMD_XOFF_GEN_MSK (0x00400000) +#define ALTERA_TSE_CMD_CNTL_FRM_ENA_MSK (0x00800000) +#define ALTERA_TSE_CMD_NO_LENGTH_CHECK_MSK (0x01000000) +#define ALTERA_TSE_CMD_ENA_10_MSK (0x02000000) +#define ALTERA_TSE_CMD_RX_ERR_DISC_MSK (0x04000000) +/* Bits (30..27) reserved */ +#define ALTERA_TSE_CMD_CNT_RESET_MSK (0x80000000) + +#define ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 (0x00040000) +#define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC (0x00020000) + +#define ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16 (0x02000000) + +#define ALT_TSE_SW_RESET_WATCHDOG_CNTR 10000 +#define ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR 90000000 + +/* Command_Config Register Bit Definitions */ + +typedef volatile union __alt_tse_command_config { + unsigned int image; + struct { + unsigned int + transmit_enable:1, /* bit 0 */ + receive_enable:1, /* bit 1 */ + pause_frame_xon_gen:1, /* bit 2 */ + ethernet_speed:1, /* bit 3 */ + promiscuous_enable:1, /* bit 4 */ + pad_enable:1, /* bit 5 */ + crc_forward:1, /* bit 6 */ + pause_frame_forward:1, /* bit 7 */ + pause_frame_ignore:1, /* bit 8 */ + set_mac_address_on_tx:1, /* bit 9 */ + halfduplex_enable:1, /* bit 10 */ + excessive_collision:1, /* bit 11 */ + late_collision:1, /* bit 12 */ + software_reset:1, /* bit 13 */ + multicast_hash_mode_sel:1, /* bit 14 */ + loopback_enable:1, /* bit 15 */ + src_mac_addr_sel_on_tx:3, /* bit 18:16 */ + magic_packet_detect:1, /* bit 19 */ + sleep_mode_enable:1, /* bit 20 */ + wake_up_request:1, /* bit 21 */ + pause_frame_xoff_gen:1, /* bit 22 */ + control_frame_enable:1, /* bit 23 */ + payload_len_chk_disable:1, /* bit 24 */ + enable_10mbps_intf:1, /* bit 25 */ + rx_error_discard_enable:1, /* bit 26 */ + reserved_bits:4, /* bit 30:27 */ + self_clear_counter_reset:1; /* bit 31 */ + } __packed_1_ bits; +} __packed_1_ alt_tse_command_config; + +/* Tx_Cmd_Stat Register Bit Definitions */ + +typedef volatile union __alt_tse_tx_cmd_stat { + unsigned int image; + struct { + unsigned int reserved_lsbs:17, /* bit 16:0 */ + omit_crc:1, /* bit 17 */ + tx_shift16:1, /* bit 18 */ + reserved_msbs:13; /* bit 31:19 */ + + } __packed_1_ bits; +} alt_tse_tx_cmd_stat; + +/* Rx_Cmd_Stat Register Bit Definitions */ + +typedef volatile union __alt_tse_rx_cmd_stat { + unsigned int image; + struct { + unsigned int reserved_lsbs:25, /* bit 24:0 */ + rx_shift16:1, /* bit 25 */ + reserved_msbs:6; /* bit 31:26 */ + + } __packed_1_ bits; +} alt_tse_rx_cmd_stat; + +struct alt_tse_mdio { + unsigned int control; /*PHY device operation control register */ + unsigned int status; /*PHY device operation status register */ + unsigned int phy_id1; /*Bits 31:16 of PHY identifier. */ + unsigned int phy_id2; /*Bits 15:0 of PHY identifier. */ + unsigned int auto_negotiation_advertisement; + unsigned int remote_partner_base_page_ability; + + unsigned int reg6; + unsigned int reg7; + unsigned int reg8; + unsigned int reg9; + unsigned int rega; + unsigned int regb; + unsigned int regc; + unsigned int regd; + unsigned int rege; + unsigned int regf; + unsigned int reg10; + unsigned int reg11; + unsigned int reg12; + unsigned int reg13; + unsigned int reg14; + unsigned int reg15; + unsigned int reg16; + unsigned int reg17; + unsigned int reg18; + unsigned int reg19; + unsigned int reg1a; + unsigned int reg1b; + unsigned int reg1c; + unsigned int reg1d; + unsigned int reg1e; + unsigned int reg1f; +}; + +/* MAC register Space */ + +struct alt_tse_mac { + unsigned int megacore_revision; + unsigned int scratch_pad; + alt_tse_command_config command_config; + unsigned int mac_addr_0; + unsigned int mac_addr_1; + unsigned int max_frame_length; + unsigned int pause_quanta; + unsigned int rx_sel_empty_threshold; + unsigned int rx_sel_full_threshold; + unsigned int tx_sel_empty_threshold; + unsigned int tx_sel_full_threshold; + unsigned int rx_almost_empty_threshold; + unsigned int rx_almost_full_threshold; + unsigned int tx_almost_empty_threshold; + unsigned int tx_almost_full_threshold; + unsigned int mdio_phy0_addr; + unsigned int mdio_phy1_addr; + + /* only if 100/1000 BaseX PCS, reserved otherwise */ + unsigned int reservedx44[5]; + + unsigned int reg_read_access_status; + unsigned int min_tx_ipg_length; + + /* IEEE 802.3 oEntity Managed Object Support */ + unsigned int aMACID_1; /*The MAC addresses */ + unsigned int aMACID_2; + unsigned int aFramesTransmittedOK; + unsigned int aFramesReceivedOK; + unsigned int aFramesCheckSequenceErrors; + unsigned int aAlignmentErrors; + unsigned int aOctetsTransmittedOK; + unsigned int aOctetsReceivedOK; + + /* IEEE 802.3 oPausedEntity Managed Object Support */ + unsigned int aTxPAUSEMACCtrlFrames; + unsigned int aRxPAUSEMACCtrlFrames; + + /* IETF MIB (MIB-II) Object Support */ + unsigned int ifInErrors; + unsigned int ifOutErrors; + unsigned int ifInUcastPkts; + unsigned int ifInMulticastPkts; + unsigned int ifInBroadcastPkts; + unsigned int ifOutDiscards; + unsigned int ifOutUcastPkts; + unsigned int ifOutMulticastPkts; + unsigned int ifOutBroadcastPkts; + + /* IETF RMON MIB Object Support */ + unsigned int etherStatsDropEvent; + unsigned int etherStatsOctets; + unsigned int etherStatsPkts; + unsigned int etherStatsUndersizePkts; + unsigned int etherStatsOversizePkts; + unsigned int etherStatsPkts64Octets; + unsigned int etherStatsPkts65to127Octets; + unsigned int etherStatsPkts128to255Octets; + unsigned int etherStatsPkts256to511Octets; + unsigned int etherStatsPkts512to1023Octets; + unsigned int etherStatsPkts1024to1518Octets; + + unsigned int etherStatsPkts1519toXOctets; + unsigned int etherStatsJabbers; + unsigned int etherStatsFragments; + + unsigned int reservedxE4; + + /*FIFO control register. */ + alt_tse_tx_cmd_stat tx_cmd_stat; + alt_tse_rx_cmd_stat rx_cmd_stat; + + unsigned int ipaccTxConf; + unsigned int ipaccRxConf; + unsigned int ipaccRxStat; + unsigned int ipaccRxStatSum; + + /*Multicast address resolution table */ + unsigned int hash_table[64]; + + /*Registers 0 to 31 within PHY device 0/1 */ + struct alt_tse_mdio mdio_phy0; + struct alt_tse_mdio mdio_phy1; + + /*4 Supplemental MAC Addresses */ + unsigned int supp_mac_addr_0_0; + unsigned int supp_mac_addr_0_1; + unsigned int supp_mac_addr_1_0; + unsigned int supp_mac_addr_1_1; + unsigned int supp_mac_addr_2_0; + unsigned int supp_mac_addr_2_1; + unsigned int supp_mac_addr_3_0; + unsigned int supp_mac_addr_3_1; + + unsigned int reservedx320[56]; +}; + +/* flags: TSE MII modes */ +/* GMII/MII = 0 */ +/* RGMII = 1 */ +/* RGMII_ID = 2 */ +/* RGMII_TXID = 3 */ +/* RGMII_RXID = 4 */ +/* SGMII = 5 */ +struct altera_tse_priv { + char devname[16]; + volatile struct alt_tse_mac *mac_dev; + volatile struct alt_sgdma_registers *sgdma_rx; + volatile struct alt_sgdma_registers *sgdma_tx; + unsigned int rx_sgdma_irq; + unsigned int tx_sgdma_irq; + unsigned int has_descriptor_mem; + unsigned int descriptor_mem_base; + unsigned int descriptor_mem_size; + volatile struct alt_sgdma_descriptor *rx_desc; + volatile struct alt_sgdma_descriptor *tx_desc; + volatile unsigned char *rx_buf; + struct phy_info *phyinfo; + unsigned int phyaddr; + unsigned int flags; + unsigned int link; + unsigned int duplexity; + unsigned int speed; +}; + +/* Phy stuff continued */ +/* + * struct phy_cmd: A command for reading or writing a PHY register + * + * mii_reg: The register to read or write + * + * mii_data: For writes, the value to put in the register. + * A value of -1 indicates this is a read. + * + * funct: A function pointer which is invoked for each command. + * For reads, this function will be passed the value read + * from the PHY, and process it. + * For writes, the result of this function will be written + * to the PHY register + */ +struct phy_cmd { + uint mii_reg; + uint mii_data; + uint(*funct) (uint mii_reg, struct altera_tse_priv *priv); +}; +#endif /* _ALTERA_TSE_H_ */ diff --git a/qemu/roms/u-boot/drivers/net/armada100_fec.c b/qemu/roms/u-boot/drivers/net/armada100_fec.c new file mode 100644 index 000000000..a8da6b17d --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/armada100_fec.c @@ -0,0 +1,726 @@ +/* + * (C) Copyright 2011 + * eInfochips Ltd. + * Written-by: Ajay Bhargav + * + * (C) Copyright 2010 + * Marvell Semiconductor + * Contributor: Mahavir Jain + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "armada100_fec.h" + +#define PHY_ADR_REQ 0xFF /* Magic number to read/write PHY address */ + +#ifdef DEBUG +static int eth_dump_regs(struct eth_device *dev) +{ + struct armdfec_device *darmdfec = to_darmdfec(dev); + struct armdfec_reg *regs = darmdfec->regs; + unsigned int i = 0; + + printf("\noffset: phy_adr, value: 0x%x\n", readl(®s->phyadr)); + printf("offset: smi, value: 0x%x\n", readl(®s->smi)); + for (i = 0x400; i <= 0x4e4; i += 4) + printf("offset: 0x%x, value: 0x%x\n", + i, readl(ARMD1_FEC_BASE + i)); + return 0; +} +#endif + +static int armdfec_phy_timeout(u32 *reg, u32 flag, int cond) +{ + u32 timeout = PHY_WAIT_ITERATIONS; + u32 reg_val; + + while (--timeout) { + reg_val = readl(reg); + if (cond && (reg_val & flag)) + break; + else if (!cond && !(reg_val & flag)) + break; + udelay(PHY_WAIT_MICRO_SECONDS); + } + return !timeout; +} + +static int smi_reg_read(const char *devname, u8 phy_addr, u8 phy_reg, + u16 *value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct armdfec_device *darmdfec = to_darmdfec(dev); + struct armdfec_reg *regs = darmdfec->regs; + u32 val; + + if (phy_addr == PHY_ADR_REQ && phy_reg == PHY_ADR_REQ) { + val = readl(®s->phyadr); + *value = val & 0x1f; + return 0; + } + + /* check parameters */ + if (phy_addr > PHY_MASK) { + printf("ARMD100 FEC: (%s) Invalid phy address: 0x%X\n", + __func__, phy_addr); + return -EINVAL; + } + if (phy_reg > PHY_MASK) { + printf("ARMD100 FEC: (%s) Invalid register offset: 0x%X\n", + __func__, phy_reg); + return -EINVAL; + } + + /* wait for the SMI register to become available */ + if (armdfec_phy_timeout(®s->smi, SMI_BUSY, false)) { + printf("ARMD100 FEC: (%s) PHY busy timeout\n", __func__); + return -1; + } + + writel((phy_addr << 16) | (phy_reg << 21) | SMI_OP_R, ®s->smi); + + /* now wait for the data to be valid */ + if (armdfec_phy_timeout(®s->smi, SMI_R_VALID, true)) { + val = readl(®s->smi); + printf("ARMD100 FEC: (%s) PHY Read timeout, val=0x%x\n", + __func__, val); + return -1; + } + val = readl(®s->smi); + *value = val & 0xffff; + + return 0; +} + +static int smi_reg_write(const char *devname, + u8 phy_addr, u8 phy_reg, u16 value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct armdfec_device *darmdfec = to_darmdfec(dev); + struct armdfec_reg *regs = darmdfec->regs; + + if (phy_addr == PHY_ADR_REQ && phy_reg == PHY_ADR_REQ) { + clrsetbits_le32(®s->phyadr, 0x1f, value & 0x1f); + return 0; + } + + /* check parameters */ + if (phy_addr > PHY_MASK) { + printf("ARMD100 FEC: (%s) Invalid phy address\n", __func__); + return -EINVAL; + } + if (phy_reg > PHY_MASK) { + printf("ARMD100 FEC: (%s) Invalid register offset\n", __func__); + return -EINVAL; + } + + /* wait for the SMI register to become available */ + if (armdfec_phy_timeout(®s->smi, SMI_BUSY, false)) { + printf("ARMD100 FEC: (%s) PHY busy timeout\n", __func__); + return -1; + } + + writel((phy_addr << 16) | (phy_reg << 21) | SMI_OP_W | (value & 0xffff), + ®s->smi); + return 0; +} + +/* + * Abort any transmit and receive operations and put DMA + * in idle state. AT and AR bits are cleared upon entering + * in IDLE state. So poll those bits to verify operation. + */ +static void abortdma(struct eth_device *dev) +{ + struct armdfec_device *darmdfec = to_darmdfec(dev); + struct armdfec_reg *regs = darmdfec->regs; + int delay; + int maxretries = 40; + u32 tmp; + + while (--maxretries) { + writel(SDMA_CMD_AR | SDMA_CMD_AT, ®s->sdma_cmd); + udelay(100); + + delay = 10; + while (--delay) { + tmp = readl(®s->sdma_cmd); + if (!(tmp & (SDMA_CMD_AR | SDMA_CMD_AT))) + break; + udelay(10); + } + if (delay) + break; + } + + if (!maxretries) + printf("ARMD100 FEC: (%s) DMA Stuck\n", __func__); +} + +static inline u32 nibble_swapping_32_bit(u32 x) +{ + return ((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4); +} + +static inline u32 nibble_swapping_16_bit(u32 x) +{ + return ((x & 0x0000f0f0) >> 4) | ((x & 0x00000f0f) << 4); +} + +static inline u32 flip_4_bits(u32 x) +{ + return ((x & 0x01) << 3) | ((x & 0x002) << 1) + | ((x & 0x04) >> 1) | ((x & 0x008) >> 3); +} + +/* + * This function will calculate the hash function of the address. + * depends on the hash mode and hash size. + * Inputs + * mach - the 2 most significant bytes of the MAC address. + * macl - the 4 least significant bytes of the MAC address. + * Outputs + * return the calculated entry. + */ +static u32 hash_function(u32 mach, u32 macl) +{ + u32 hashresult; + u32 addrh; + u32 addrl; + u32 addr0; + u32 addr1; + u32 addr2; + u32 addr3; + u32 addrhswapped; + u32 addrlswapped; + + addrh = nibble_swapping_16_bit(mach); + addrl = nibble_swapping_32_bit(macl); + + addrhswapped = flip_4_bits(addrh & 0xf) + + ((flip_4_bits((addrh >> 4) & 0xf)) << 4) + + ((flip_4_bits((addrh >> 8) & 0xf)) << 8) + + ((flip_4_bits((addrh >> 12) & 0xf)) << 12); + + addrlswapped = flip_4_bits(addrl & 0xf) + + ((flip_4_bits((addrl >> 4) & 0xf)) << 4) + + ((flip_4_bits((addrl >> 8) & 0xf)) << 8) + + ((flip_4_bits((addrl >> 12) & 0xf)) << 12) + + ((flip_4_bits((addrl >> 16) & 0xf)) << 16) + + ((flip_4_bits((addrl >> 20) & 0xf)) << 20) + + ((flip_4_bits((addrl >> 24) & 0xf)) << 24) + + ((flip_4_bits((addrl >> 28) & 0xf)) << 28); + + addrh = addrhswapped; + addrl = addrlswapped; + + addr0 = (addrl >> 2) & 0x03f; + addr1 = (addrl & 0x003) | (((addrl >> 8) & 0x7f) << 2); + addr2 = (addrl >> 15) & 0x1ff; + addr3 = ((addrl >> 24) & 0x0ff) | ((addrh & 1) << 8); + + hashresult = (addr0 << 9) | (addr1 ^ addr2 ^ addr3); + hashresult = hashresult & 0x07ff; + return hashresult; +} + +/* + * This function will add an entry to the address table. + * depends on the hash mode and hash size that was initialized. + * Inputs + * mach - the 2 most significant bytes of the MAC address. + * macl - the 4 least significant bytes of the MAC address. + * skip - if 1, skip this address. + * rd - the RD field in the address table. + * Outputs + * address table entry is added. + * 0 if success. + * -ENOSPC if table full + */ +static int add_del_hash_entry(struct armdfec_device *darmdfec, u32 mach, + u32 macl, u32 rd, u32 skip, int del) +{ + struct addr_table_entry_t *entry, *start; + u32 newhi; + u32 newlo; + u32 i; + + newlo = (((mach >> 4) & 0xf) << 15) + | (((mach >> 0) & 0xf) << 11) + | (((mach >> 12) & 0xf) << 7) + | (((mach >> 8) & 0xf) << 3) + | (((macl >> 20) & 0x1) << 31) + | (((macl >> 16) & 0xf) << 27) + | (((macl >> 28) & 0xf) << 23) + | (((macl >> 24) & 0xf) << 19) + | (skip << HTESKIP) | (rd << HTERDBIT) + | HTEVALID; + + newhi = (((macl >> 4) & 0xf) << 15) + | (((macl >> 0) & 0xf) << 11) + | (((macl >> 12) & 0xf) << 7) + | (((macl >> 8) & 0xf) << 3) + | (((macl >> 21) & 0x7) << 0); + + /* + * Pick the appropriate table, start scanning for free/reusable + * entries at the index obtained by hashing the specified MAC address + */ + start = (struct addr_table_entry_t *)(darmdfec->htpr); + entry = start + hash_function(mach, macl); + for (i = 0; i < HOP_NUMBER; i++) { + if (!(entry->lo & HTEVALID)) { + break; + } else { + /* if same address put in same position */ + if (((entry->lo & 0xfffffff8) == (newlo & 0xfffffff8)) + && (entry->hi == newhi)) + break; + } + if (entry == start + 0x7ff) + entry = start; + else + entry++; + } + + if (((entry->lo & 0xfffffff8) != (newlo & 0xfffffff8)) && + (entry->hi != newhi) && del) + return 0; + + if (i == HOP_NUMBER) { + if (!del) { + printf("ARMD100 FEC: (%s) table section is full\n", + __func__); + return -ENOSPC; + } else { + return 0; + } + } + + /* + * Update the selected entry + */ + if (del) { + entry->hi = 0; + entry->lo = 0; + } else { + entry->hi = newhi; + entry->lo = newlo; + } + + return 0; +} + +/* + * Create an addressTable entry from MAC address info + * found in the specifed net_device struct + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void update_hash_table_mac_address(struct armdfec_device *darmdfec, + u8 *oaddr, u8 *addr) +{ + u32 mach; + u32 macl; + + /* Delete old entry */ + if (oaddr) { + mach = (oaddr[0] << 8) | oaddr[1]; + macl = (oaddr[2] << 24) | (oaddr[3] << 16) | + (oaddr[4] << 8) | oaddr[5]; + add_del_hash_entry(darmdfec, mach, macl, 1, 0, HASH_DELETE); + } + + /* Add new entry */ + mach = (addr[0] << 8) | addr[1]; + macl = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; + add_del_hash_entry(darmdfec, mach, macl, 1, 0, HASH_ADD); +} + +/* Address Table Initialization */ +static void init_hashtable(struct eth_device *dev) +{ + struct armdfec_device *darmdfec = to_darmdfec(dev); + struct armdfec_reg *regs = darmdfec->regs; + memset(darmdfec->htpr, 0, HASH_ADDR_TABLE_SIZE); + writel((u32)darmdfec->htpr, ®s->htpr); +} + +/* + * This detects PHY chip from address 0-31 by reading PHY status + * registers. PHY chip can be connected at any of this address. + */ +static int ethernet_phy_detect(struct eth_device *dev) +{ + u32 val; + u16 tmp, mii_status; + u8 addr; + + for (addr = 0; addr < 32; addr++) { + if (miiphy_read(dev->name, addr, MII_BMSR, &mii_status) != 0) + /* try next phy */ + continue; + + /* invalid MII status. More validation required here... */ + if (mii_status == 0 || mii_status == 0xffff) + /* try next phy */ + continue; + + if (miiphy_read(dev->name, addr, MII_PHYSID1, &tmp) != 0) + /* try next phy */ + continue; + + val = tmp << 16; + if (miiphy_read(dev->name, addr, MII_PHYSID2, &tmp) != 0) + /* try next phy */ + continue; + + val |= tmp; + + if ((val & 0xfffffff0) != 0) + return addr; + } + return -1; +} + +static void armdfec_init_rx_desc_ring(struct armdfec_device *darmdfec) +{ + struct rx_desc *p_rx_desc; + int i; + + /* initialize the Rx descriptors ring */ + p_rx_desc = darmdfec->p_rxdesc; + for (i = 0; i < RINGSZ; i++) { + p_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT; + p_rx_desc->buf_size = PKTSIZE_ALIGN; + p_rx_desc->byte_cnt = 0; + p_rx_desc->buf_ptr = darmdfec->p_rxbuf + i * PKTSIZE_ALIGN; + if (i == (RINGSZ - 1)) { + p_rx_desc->nxtdesc_p = darmdfec->p_rxdesc; + } else { + p_rx_desc->nxtdesc_p = (struct rx_desc *) + ((u32)p_rx_desc + ARMDFEC_RXQ_DESC_ALIGNED_SIZE); + p_rx_desc = p_rx_desc->nxtdesc_p; + } + } + darmdfec->p_rxdesc_curr = darmdfec->p_rxdesc; +} + +static int armdfec_init(struct eth_device *dev, bd_t *bd) +{ + struct armdfec_device *darmdfec = to_darmdfec(dev); + struct armdfec_reg *regs = darmdfec->regs; + int phy_adr; + u32 temp; + + armdfec_init_rx_desc_ring(darmdfec); + + /* Disable interrupts */ + writel(0, ®s->im); + writel(0, ®s->ic); + /* Write to ICR to clear interrupts. */ + writel(0, ®s->iwc); + + /* + * Abort any transmit and receive operations and put DMA + * in idle state. + */ + abortdma(dev); + + /* Initialize address hash table */ + init_hashtable(dev); + + /* SDMA configuration */ + writel(SDCR_BSZ8 | /* Burst size = 32 bytes */ + SDCR_RIFB | /* Rx interrupt on frame */ + SDCR_BLMT | /* Little endian transmit */ + SDCR_BLMR | /* Little endian receive */ + SDCR_RC_MAX_RETRANS, /* Max retransmit count */ + ®s->sdma_conf); + /* Port Configuration */ + writel(PCR_HS, ®s->pconf); /* Hash size is 1/2kb */ + + /* Set extended port configuration */ + writel(PCXR_2BSM | /* Two byte suffix aligns IP hdr */ + PCXR_DSCP_EN | /* Enable DSCP in IP */ + PCXR_MFL_1536 | /* Set MTU = 1536 */ + PCXR_FLP | /* do not force link pass */ + PCXR_TX_HIGH_PRI, /* Transmit - high priority queue */ + ®s->pconf_ext); + + update_hash_table_mac_address(darmdfec, NULL, dev->enetaddr); + + /* Update TX and RX queue descriptor register */ + temp = (u32)®s->txcdp[TXQ]; + writel((u32)darmdfec->p_txdesc, temp); + temp = (u32)®s->rxfdp[RXQ]; + writel((u32)darmdfec->p_rxdesc, temp); + temp = (u32)®s->rxcdp[RXQ]; + writel((u32)darmdfec->p_rxdesc_curr, temp); + + /* Enable Interrupts */ + writel(ALL_INTS, ®s->im); + + /* Enable Ethernet Port */ + setbits_le32(®s->pconf, PCR_EN); + + /* Enable RX DMA engine */ + setbits_le32(®s->sdma_cmd, SDMA_CMD_ERD); + +#ifdef DEBUG + eth_dump_regs(dev); +#endif + +#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) + +#if defined(CONFIG_PHY_BASE_ADR) + miiphy_write(dev->name, PHY_ADR_REQ, PHY_ADR_REQ, CONFIG_PHY_BASE_ADR); +#else + /* Search phy address from range 0-31 */ + phy_adr = ethernet_phy_detect(dev); + if (phy_adr < 0) { + printf("ARMD100 FEC: PHY not detected at address range 0-31\n"); + return -1; + } else { + debug("ARMD100 FEC: PHY detected at addr %d\n", phy_adr); + miiphy_write(dev->name, PHY_ADR_REQ, PHY_ADR_REQ, phy_adr); + } +#endif + +#if defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN) + /* Wait up to 5s for the link status */ + for (i = 0; i < 5; i++) { + u16 phy_adr; + + miiphy_read(dev->name, 0xFF, 0xFF, &phy_adr); + /* Return if we get link up */ + if (miiphy_link(dev->name, phy_adr)) + return 0; + udelay(1000000); + } + + printf("ARMD100 FEC: No link on %s\n", dev->name); + return -1; +#endif +#endif + return 0; +} + +static void armdfec_halt(struct eth_device *dev) +{ + struct armdfec_device *darmdfec = to_darmdfec(dev); + struct armdfec_reg *regs = darmdfec->regs; + + /* Stop RX DMA */ + clrbits_le32(®s->sdma_cmd, SDMA_CMD_ERD); + + /* + * Abort any transmit and receive operations and put DMA + * in idle state. + */ + abortdma(dev); + + /* Disable interrupts */ + writel(0, ®s->im); + writel(0, ®s->ic); + writel(0, ®s->iwc); + + /* Disable Port */ + clrbits_le32(®s->pconf, PCR_EN); +} + +static int armdfec_send(struct eth_device *dev, void *dataptr, int datasize) +{ + struct armdfec_device *darmdfec = to_darmdfec(dev); + struct armdfec_reg *regs = darmdfec->regs; + struct tx_desc *p_txdesc = darmdfec->p_txdesc; + void *p = (void *)dataptr; + int retry = PHY_WAIT_ITERATIONS * PHY_WAIT_MICRO_SECONDS; + u32 cmd_sts, temp; + + /* Copy buffer if it's misaligned */ + if ((u32)dataptr & 0x07) { + if (datasize > PKTSIZE_ALIGN) { + printf("ARMD100 FEC: Non-aligned data too large (%d)\n", + datasize); + return -1; + } + memcpy(darmdfec->p_aligned_txbuf, p, datasize); + p = darmdfec->p_aligned_txbuf; + } + + p_txdesc->cmd_sts = TX_ZERO_PADDING | TX_GEN_CRC; + p_txdesc->cmd_sts |= TX_FIRST_DESC | TX_LAST_DESC; + p_txdesc->cmd_sts |= BUF_OWNED_BY_DMA; + p_txdesc->cmd_sts |= TX_EN_INT; + p_txdesc->buf_ptr = p; + p_txdesc->byte_cnt = datasize; + + /* Apply send command using high priority TX queue */ + temp = (u32)®s->txcdp[TXQ]; + writel((u32)p_txdesc, temp); + writel(SDMA_CMD_TXDL | SDMA_CMD_TXDH | SDMA_CMD_ERD, ®s->sdma_cmd); + + /* + * wait for packet xmit completion + */ + cmd_sts = readl(&p_txdesc->cmd_sts); + while (cmd_sts & BUF_OWNED_BY_DMA) { + /* return fail if error is detected */ + if ((cmd_sts & (TX_ERROR | TX_LAST_DESC)) == + (TX_ERROR | TX_LAST_DESC)) { + printf("ARMD100 FEC: (%s) in xmit packet\n", __func__); + return -1; + } + cmd_sts = readl(&p_txdesc->cmd_sts); + if (!(retry--)) { + printf("ARMD100 FEC: (%s) xmit packet timeout!\n", + __func__); + return -1; + } + } + + return 0; +} + +static int armdfec_recv(struct eth_device *dev) +{ + struct armdfec_device *darmdfec = to_darmdfec(dev); + struct rx_desc *p_rxdesc_curr = darmdfec->p_rxdesc_curr; + u32 cmd_sts; + u32 timeout = 0; + u32 temp; + + /* wait untill rx packet available or timeout */ + do { + if (timeout < PHY_WAIT_ITERATIONS * PHY_WAIT_MICRO_SECONDS) { + timeout++; + } else { + debug("ARMD100 FEC: %s time out...\n", __func__); + return -1; + } + } while (readl(&p_rxdesc_curr->cmd_sts) & BUF_OWNED_BY_DMA); + + if (p_rxdesc_curr->byte_cnt != 0) { + debug("ARMD100 FEC: %s: Received %d byte Packet @ 0x%x" + "(cmd_sts= %08x)\n", __func__, + (u32)p_rxdesc_curr->byte_cnt, + (u32)p_rxdesc_curr->buf_ptr, + (u32)p_rxdesc_curr->cmd_sts); + } + + /* + * In case received a packet without first/last bits on + * OR the error summary bit is on, + * the packets needs to be dropeed. + */ + cmd_sts = readl(&p_rxdesc_curr->cmd_sts); + + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != + (RX_FIRST_DESC | RX_LAST_DESC)) { + printf("ARMD100 FEC: (%s) Dropping packet spread on" + " multiple descriptors\n", __func__); + } else if (cmd_sts & RX_ERROR) { + printf("ARMD100 FEC: (%s) Dropping packet with errors\n", + __func__); + } else { + /* !!! call higher layer processing */ + debug("ARMD100 FEC: (%s) Sending Received packet to" + " upper layer (NetReceive)\n", __func__); + + /* + * let the upper layer handle the packet, subtract offset + * as two dummy bytes are added in received buffer see + * PORT_CONFIG_EXT register bit TWO_Byte_Stuff_Mode bit. + */ + NetReceive((p_rxdesc_curr->buf_ptr + RX_BUF_OFFSET), + (int)(p_rxdesc_curr->byte_cnt - RX_BUF_OFFSET)); + } + /* + * free these descriptors and point next in the ring + */ + p_rxdesc_curr->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT; + p_rxdesc_curr->buf_size = PKTSIZE_ALIGN; + p_rxdesc_curr->byte_cnt = 0; + + temp = (u32)&darmdfec->p_rxdesc_curr; + writel((u32)p_rxdesc_curr->nxtdesc_p, temp); + + return 0; +} + +int armada100_fec_register(unsigned long base_addr) +{ + struct armdfec_device *darmdfec; + struct eth_device *dev; + + darmdfec = malloc(sizeof(struct armdfec_device)); + if (!darmdfec) + goto error; + + memset(darmdfec, 0, sizeof(struct armdfec_device)); + + darmdfec->htpr = memalign(8, HASH_ADDR_TABLE_SIZE); + if (!darmdfec->htpr) + goto error1; + + darmdfec->p_rxdesc = memalign(PKTALIGN, + ARMDFEC_RXQ_DESC_ALIGNED_SIZE * RINGSZ + 1); + + if (!darmdfec->p_rxdesc) + goto error1; + + darmdfec->p_rxbuf = memalign(PKTALIGN, RINGSZ * PKTSIZE_ALIGN + 1); + if (!darmdfec->p_rxbuf) + goto error1; + + darmdfec->p_aligned_txbuf = memalign(8, PKTSIZE_ALIGN); + if (!darmdfec->p_aligned_txbuf) + goto error1; + + darmdfec->p_txdesc = memalign(PKTALIGN, sizeof(struct tx_desc) + 1); + if (!darmdfec->p_txdesc) + goto error1; + + dev = &darmdfec->dev; + /* Assign ARMADA100 Fast Ethernet Controller Base Address */ + darmdfec->regs = (void *)base_addr; + + /* must be less than sizeof(dev->name) */ + strcpy(dev->name, "armd-fec0"); + + dev->init = armdfec_init; + dev->halt = armdfec_halt; + dev->send = armdfec_send; + dev->recv = armdfec_recv; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, smi_reg_read, smi_reg_write); +#endif + return 0; + +error1: + free(darmdfec->p_aligned_txbuf); + free(darmdfec->p_rxbuf); + free(darmdfec->p_rxdesc); + free(darmdfec->htpr); +error: + free(darmdfec); + printf("AMD100 FEC: (%s) Failed to allocate memory\n", __func__); + return -1; +} diff --git a/qemu/roms/u-boot/drivers/net/armada100_fec.h b/qemu/roms/u-boot/drivers/net/armada100_fec.h new file mode 100644 index 000000000..5a0a3d982 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/armada100_fec.h @@ -0,0 +1,209 @@ +/* + * (C) Copyright 2011 + * eInfochips Ltd. + * Written-by: Ajay Bhargav + * + * (C) Copyright 2010 + * Marvell Semiconductor + * Contributor: Mahavir Jain + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ARMADA100_FEC_H__ +#define __ARMADA100_FEC_H__ + +#define PORT_NUM 0x0 + +/* RX & TX descriptor command */ +#define BUF_OWNED_BY_DMA (1<<31) + +/* RX descriptor status */ +#define RX_EN_INT (1<<23) +#define RX_FIRST_DESC (1<<17) +#define RX_LAST_DESC (1<<16) +#define RX_ERROR (1<<15) + +/* TX descriptor command */ +#define TX_EN_INT (1<<23) +#define TX_GEN_CRC (1<<22) +#define TX_ZERO_PADDING (1<<18) +#define TX_FIRST_DESC (1<<17) +#define TX_LAST_DESC (1<<16) +#define TX_ERROR (1<<15) + +/* smi register */ +#define SMI_BUSY (1<<28) /* 0 - Write, 1 - Read */ +#define SMI_R_VALID (1<<27) /* 0 - Write, 1 - Read */ +#define SMI_OP_W (0<<26) /* Write operation */ +#define SMI_OP_R (1<<26) /* Read operation */ + +#define HASH_ADD 0 +#define HASH_DELETE 1 +#define HASH_ADDR_TABLE_SIZE 0x4000 /* 16K (1/2K address - PCR_HS == 1) */ +#define HOP_NUMBER 12 + +#define PHY_WAIT_ITERATIONS 1000 /* 1000 iterations * 10uS = 10mS max */ +#define PHY_WAIT_MICRO_SECONDS 10 + +#define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */ +#define ETH_EXTRA_HEADER (6+6+2+4) + /* dest+src addr+protocol id+crc */ +#define MAX_PKT_SIZE 1536 + + +/* Bit definitions of the SDMA Config Reg */ +#define SDCR_BSZ_OFF 12 +#define SDCR_BSZ8 (3< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef MII_DEBUG +#undef ET_DEBUG + +#if (CONFIG_SYS_RX_ETH_BUFFER > 1024) +#error AT91 EMAC supports max 1024 RX buffers. \ + Please decrease the CONFIG_SYS_RX_ETH_BUFFER value +#endif + +#ifndef CONFIG_DRIVER_AT91EMAC_PHYADDR +#define CONFIG_DRIVER_AT91EMAC_PHYADDR 0 +#endif + +/* MDIO clock must not exceed 2.5 MHz, so enable MCK divider */ +#if (AT91C_MASTER_CLOCK > 80000000) + #define HCLK_DIV AT91_EMAC_CFG_MCLK_64 +#elif (AT91C_MASTER_CLOCK > 40000000) + #define HCLK_DIV AT91_EMAC_CFG_MCLK_32 +#elif (AT91C_MASTER_CLOCK > 20000000) + #define HCLK_DIV AT91_EMAC_CFG_MCLK_16 +#else + #define HCLK_DIV AT91_EMAC_CFG_MCLK_8 +#endif + +#ifdef ET_DEBUG +#define DEBUG_AT91EMAC 1 +#else +#define DEBUG_AT91EMAC 0 +#endif + +#ifdef MII_DEBUG +#define DEBUG_AT91PHY 1 +#else +#define DEBUG_AT91PHY 0 +#endif + +#ifndef CONFIG_DRIVER_AT91EMAC_QUIET +#define VERBOSEP 1 +#else +#define VERBOSEP 0 +#endif + +#define RBF_ADDR 0xfffffffc +#define RBF_OWNER (1<<0) +#define RBF_WRAP (1<<1) +#define RBF_BROADCAST (1<<31) +#define RBF_MULTICAST (1<<30) +#define RBF_UNICAST (1<<29) +#define RBF_EXTERNAL (1<<28) +#define RBF_UNKNOWN (1<<27) +#define RBF_SIZE 0x07ff +#define RBF_LOCAL4 (1<<26) +#define RBF_LOCAL3 (1<<25) +#define RBF_LOCAL2 (1<<24) +#define RBF_LOCAL1 (1<<23) + +#define RBF_FRAMEMAX CONFIG_SYS_RX_ETH_BUFFER +#define RBF_FRAMELEN 0x600 + +typedef struct { + unsigned long addr, size; +} rbf_t; + +typedef struct { + rbf_t rbfdt[RBF_FRAMEMAX]; + unsigned long rbindex; +} emac_device; + +void at91emac_EnableMDIO(at91_emac_t *at91mac) +{ + /* Mac CTRL reg set for MDIO enable */ + writel(readl(&at91mac->ctl) | AT91_EMAC_CTL_MPE, &at91mac->ctl); +} + +void at91emac_DisableMDIO(at91_emac_t *at91mac) +{ + /* Mac CTRL reg set for MDIO disable */ + writel(readl(&at91mac->ctl) & ~AT91_EMAC_CTL_MPE, &at91mac->ctl); +} + +int at91emac_read(at91_emac_t *at91mac, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + unsigned long netstat; + at91emac_EnableMDIO(at91mac); + + writel(AT91_EMAC_MAN_HIGH | AT91_EMAC_MAN_RW_R | + AT91_EMAC_MAN_REGA(reg) | AT91_EMAC_MAN_CODE_802_3 | + AT91_EMAC_MAN_PHYA(addr), + &at91mac->man); + + do { + netstat = readl(&at91mac->sr); + debug_cond(DEBUG_AT91PHY, "poll SR %08lx\n", netstat); + } while (!(netstat & AT91_EMAC_SR_IDLE)); + + *value = readl(&at91mac->man) & AT91_EMAC_MAN_DATA_MASK; + + at91emac_DisableMDIO(at91mac); + + debug_cond(DEBUG_AT91PHY, + "AT91PHY read %p REG(%d)=%x\n", at91mac, reg, *value); + + return 0; +} + +int at91emac_write(at91_emac_t *at91mac, unsigned char addr, + unsigned char reg, unsigned short value) +{ + unsigned long netstat; + debug_cond(DEBUG_AT91PHY, + "AT91PHY write %p REG(%d)=%p\n", at91mac, reg, &value); + + at91emac_EnableMDIO(at91mac); + + writel(AT91_EMAC_MAN_HIGH | AT91_EMAC_MAN_RW_W | + AT91_EMAC_MAN_REGA(reg) | AT91_EMAC_MAN_CODE_802_3 | + AT91_EMAC_MAN_PHYA(addr) | (value & AT91_EMAC_MAN_DATA_MASK), + &at91mac->man); + + do { + netstat = readl(&at91mac->sr); + debug_cond(DEBUG_AT91PHY, "poll SR %08lx\n", netstat); + } while (!(netstat & AT91_EMAC_SR_IDLE)); + + at91emac_DisableMDIO(at91mac); + + return 0; +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + +at91_emac_t *get_emacbase_by_name(const char *devname) +{ + struct eth_device *netdev; + + netdev = eth_get_dev_by_name(devname); + return (at91_emac_t *) netdev->iobase; +} + +int at91emac_mii_read(const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + at91_emac_t *emac; + + emac = get_emacbase_by_name(devname); + at91emac_read(emac , addr, reg, value); + return 0; +} + + +int at91emac_mii_write(const char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + at91_emac_t *emac; + + emac = get_emacbase_by_name(devname); + at91emac_write(emac, addr, reg, value); + return 0; +} + +#endif + +static int at91emac_phy_reset(struct eth_device *netdev) +{ + int i; + u16 status, adv; + at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + + adv = ADVERTISE_CSMA | ADVERTISE_ALL; + at91emac_write(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, + MII_ADVERTISE, adv); + debug_cond(VERBOSEP, "%s: Starting autonegotiation...\n", netdev->name); + at91emac_write(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, MII_BMCR, + (BMCR_ANENABLE | BMCR_ANRESTART)); + + for (i = 0; i < 30000; i++) { + at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, + MII_BMSR, &status); + if (status & BMSR_ANEGCOMPLETE) + break; + udelay(100); + } + + if (status & BMSR_ANEGCOMPLETE) { + debug_cond(VERBOSEP, + "%s: Autonegotiation complete\n", netdev->name); + } else { + printf("%s: Autonegotiation timed out (status=0x%04x)\n", + netdev->name, status); + return -1; + } + return 0; +} + +static int at91emac_phy_init(struct eth_device *netdev) +{ + u16 phy_id, status, adv, lpa; + int media, speed, duplex; + int i; + at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + + /* Check if the PHY is up to snuff... */ + at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, + MII_PHYSID1, &phy_id); + if (phy_id == 0xffff) { + printf("%s: No PHY present\n", netdev->name); + return -1; + } + + at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, + MII_BMSR, &status); + + if (!(status & BMSR_LSTATUS)) { + /* Try to re-negotiate if we don't have link already. */ + if (at91emac_phy_reset(netdev)) + return -2; + + for (i = 0; i < 100000 / 100; i++) { + at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, + MII_BMSR, &status); + if (status & BMSR_LSTATUS) + break; + udelay(100); + } + } + if (!(status & BMSR_LSTATUS)) { + debug_cond(VERBOSEP, "%s: link down\n", netdev->name); + return -3; + } else { + at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, + MII_ADVERTISE, &adv); + at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, + MII_LPA, &lpa); + media = mii_nway_result(lpa & adv); + speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) + ? 1 : 0); + duplex = (media & ADVERTISE_FULL) ? 1 : 0; + debug_cond(VERBOSEP, "%s: link up, %sMbps %s-duplex\n", + netdev->name, + speed ? "100" : "10", + duplex ? "full" : "half"); + } + return 0; +} + +int at91emac_UpdateLinkSpeed(at91_emac_t *emac) +{ + unsigned short stat1; + + at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, MII_BMSR, &stat1); + + if (!(stat1 & BMSR_LSTATUS)) /* link status up? */ + return -1; + + if (stat1 & BMSR_100FULL) { + /*set Emac for 100BaseTX and Full Duplex */ + writel(readl(&emac->cfg) | + AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD, + &emac->cfg); + return 0; + } + + if (stat1 & BMSR_10FULL) { + /*set MII for 10BaseT and Full Duplex */ + writel((readl(&emac->cfg) & + ~(AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD) + ) | AT91_EMAC_CFG_FD, + &emac->cfg); + return 0; + } + + if (stat1 & BMSR_100HALF) { + /*set MII for 100BaseTX and Half Duplex */ + writel((readl(&emac->cfg) & + ~(AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD) + ) | AT91_EMAC_CFG_SPD, + &emac->cfg); + return 0; + } + + if (stat1 & BMSR_10HALF) { + /*set MII for 10BaseT and Half Duplex */ + writel((readl(&emac->cfg) & + ~(AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD)), + &emac->cfg); + return 0; + } + return 0; +} + +static int at91emac_init(struct eth_device *netdev, bd_t *bd) +{ + int i; + u32 value; + emac_device *dev; + at91_emac_t *emac; + at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIO; + at91_pmc_t *pmc = (at91_pmc_t *) ATMEL_BASE_PMC; + + emac = (at91_emac_t *) netdev->iobase; + dev = (emac_device *) netdev->priv; + + /* PIO Disable Register */ + value = ATMEL_PMX_AA_EMDIO | ATMEL_PMX_AA_EMDC | + ATMEL_PMX_AA_ERXER | ATMEL_PMX_AA_ERX1 | + ATMEL_PMX_AA_ERX0 | ATMEL_PMX_AA_ECRS | + ATMEL_PMX_AA_ETX1 | ATMEL_PMX_AA_ETX0 | + ATMEL_PMX_AA_ETXEN | ATMEL_PMX_AA_EREFCK; + + writel(value, &pio->pioa.pdr); + writel(value, &pio->pioa.asr); + +#ifdef CONFIG_RMII + value = ATMEL_PMX_BA_ERXCK; +#else + value = ATMEL_PMX_BA_ERXCK | ATMEL_PMX_BA_ECOL | + ATMEL_PMX_BA_ERXDV | ATMEL_PMX_BA_ERX3 | + ATMEL_PMX_BA_ERX2 | ATMEL_PMX_BA_ETXER | + ATMEL_PMX_BA_ETX3 | ATMEL_PMX_BA_ETX2; +#endif + writel(value, &pio->piob.pdr); + writel(value, &pio->piob.bsr); + + writel(1 << ATMEL_ID_EMAC, &pmc->pcer); + writel(readl(&emac->ctl) | AT91_EMAC_CTL_CSR, &emac->ctl); + + /* Init Ethernet buffers */ + for (i = 0; i < RBF_FRAMEMAX; i++) { + dev->rbfdt[i].addr = (unsigned long) NetRxPackets[i]; + dev->rbfdt[i].size = 0; + } + dev->rbfdt[RBF_FRAMEMAX - 1].addr |= RBF_WRAP; + dev->rbindex = 0; + writel((u32) &(dev->rbfdt[0]), &emac->rbqp); + + writel(readl(&emac->rsr) & + ~(AT91_EMAC_RSR_OVR | AT91_EMAC_RSR_REC | AT91_EMAC_RSR_BNA), + &emac->rsr); + + value = AT91_EMAC_CFG_CAF | AT91_EMAC_CFG_NBC | + HCLK_DIV; +#ifdef CONFIG_RMII + value |= AT91_EMAC_CFG_RMII; +#endif + writel(value, &emac->cfg); + + writel(readl(&emac->ctl) | AT91_EMAC_CTL_TE | AT91_EMAC_CTL_RE, + &emac->ctl); + + if (!at91emac_phy_init(netdev)) { + at91emac_UpdateLinkSpeed(emac); + return 0; + } + return -1; +} + +static void at91emac_halt(struct eth_device *netdev) +{ + at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + writel(readl(&emac->ctl) & ~(AT91_EMAC_CTL_TE | AT91_EMAC_CTL_RE), + &emac->ctl); + debug_cond(DEBUG_AT91EMAC, "halt MAC\n"); +} + +static int at91emac_send(struct eth_device *netdev, void *packet, int length) +{ + at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + + while (!(readl(&emac->tsr) & AT91_EMAC_TSR_BNQ)) + ; + writel((u32) packet, &emac->tar); + writel(AT91_EMAC_TCR_LEN(length), &emac->tcr); + while (AT91_EMAC_TCR_LEN(readl(&emac->tcr))) + ; + debug_cond(DEBUG_AT91EMAC, "Send %d\n", length); + writel(readl(&emac->tsr) | AT91_EMAC_TSR_COMP, &emac->tsr); + return 0; +} + +static int at91emac_recv(struct eth_device *netdev) +{ + emac_device *dev; + at91_emac_t *emac; + rbf_t *rbfp; + int size; + + emac = (at91_emac_t *) netdev->iobase; + dev = (emac_device *) netdev->priv; + + rbfp = &dev->rbfdt[dev->rbindex]; + while (rbfp->addr & RBF_OWNER) { + size = rbfp->size & RBF_SIZE; + NetReceive(NetRxPackets[dev->rbindex], size); + + debug_cond(DEBUG_AT91EMAC, "Recv[%ld]: %d bytes @ %lx\n", + dev->rbindex, size, rbfp->addr); + + rbfp->addr &= ~RBF_OWNER; + rbfp->size = 0; + if (dev->rbindex < (RBF_FRAMEMAX-1)) + dev->rbindex++; + else + dev->rbindex = 0; + + rbfp = &(dev->rbfdt[dev->rbindex]); + if (!(rbfp->addr & RBF_OWNER)) + writel(readl(&emac->rsr) | AT91_EMAC_RSR_REC, + &emac->rsr); + } + + if (readl(&emac->isr) & AT91_EMAC_IxR_RBNA) { + /* EMAC silicon bug 41.3.1 workaround 1 */ + writel(readl(&emac->ctl) & ~AT91_EMAC_CTL_RE, &emac->ctl); + writel(readl(&emac->ctl) | AT91_EMAC_CTL_RE, &emac->ctl); + dev->rbindex = 0; + printf("%s: reset receiver (EMAC dead lock bug)\n", + netdev->name); + } + return 0; +} + +static int at91emac_write_hwaddr(struct eth_device *netdev) +{ + at91_emac_t *emac; + at91_pmc_t *pmc = (at91_pmc_t *) ATMEL_BASE_PMC; + emac = (at91_emac_t *) netdev->iobase; + + writel(1 << ATMEL_ID_EMAC, &pmc->pcer); + debug_cond(DEBUG_AT91EMAC, + "init MAC-ADDR %02x:%02x:%02x:%02x:%02x:%02x\n", + netdev->enetaddr[5], netdev->enetaddr[4], netdev->enetaddr[3], + netdev->enetaddr[2], netdev->enetaddr[1], netdev->enetaddr[0]); + writel( (netdev->enetaddr[0] | netdev->enetaddr[1] << 8 | + netdev->enetaddr[2] << 16 | netdev->enetaddr[3] << 24), + &emac->sa2l); + writel((netdev->enetaddr[4] | netdev->enetaddr[5] << 8), &emac->sa2h); + debug_cond(DEBUG_AT91EMAC, "init MAC-ADDR %x%x\n", + readl(&emac->sa2h), readl(&emac->sa2l)); + return 0; +} + +int at91emac_register(bd_t *bis, unsigned long iobase) +{ + emac_device *emac; + emac_device *emacfix; + struct eth_device *dev; + + if (iobase == 0) + iobase = ATMEL_BASE_EMAC; + emac = malloc(sizeof(*emac)+512); + if (emac == NULL) + return -1; + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + free(emac); + return -1; + } + /* alignment as per Errata (64 bytes) is insufficient! */ + emacfix = (emac_device *) (((unsigned long) emac + 0x1ff) & 0xFFFFFE00); + memset(emacfix, 0, sizeof(emac_device)); + + memset(dev, 0, sizeof(*dev)); + sprintf(dev->name, "emac"); + dev->iobase = iobase; + dev->priv = emacfix; + dev->init = at91emac_init; + dev->halt = at91emac_halt; + dev->send = at91emac_send; + dev->recv = at91emac_recv; + dev->write_hwaddr = at91emac_write_hwaddr; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, at91emac_mii_read, at91emac_mii_write); +#endif + return 1; +} diff --git a/qemu/roms/u-boot/drivers/net/ax88180.c b/qemu/roms/u-boot/drivers/net/ax88180.c new file mode 100644 index 000000000..7f0cfe594 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ax88180.c @@ -0,0 +1,757 @@ +/* + * ax88180: ASIX AX88180 Non-PCI Gigabit Ethernet u-boot driver + * + * This program is free software; you can distribute it and/or modify + * it under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * This program is distributed in the hope it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, + * USA. + */ + +/* + * ======================================================================== + * ASIX AX88180 Non-PCI 16/32-bit Gigabit Ethernet Linux Driver + * + * The AX88180 Ethernet controller is a high performance and highly + * integrated local CPU bus Ethernet controller with embedded 40K bytes + * SRAM and supports both 16-bit and 32-bit SRAM-Like interfaces for any + * embedded systems. + * The AX88180 is a single chip 10/100/1000Mbps Gigabit Ethernet + * controller that supports both MII and RGMII interfaces and is + * compliant to IEEE 802.3, IEEE 802.3u and IEEE 802.3z standards. + * + * Please visit ASIX's web site (http://www.asix.com.tw) for more + * details. + * + * Module Name : ax88180.c + * Date : 2008-07-07 + * History + * 09/06/2006 : New release for AX88180 US2 chip. + * 07/07/2008 : Fix up the coding style and using inline functions + * instead of macros + * ======================================================================== + */ +#include +#include +#include +#include +#include +#include "ax88180.h" + +/* + * =========================================================================== + * Local SubProgram Declaration + * =========================================================================== + */ +static void ax88180_rx_handler (struct eth_device *dev); +static int ax88180_phy_initial (struct eth_device *dev); +static void ax88180_media_config (struct eth_device *dev); +static unsigned long get_CicadaPHY_media_mode (struct eth_device *dev); +static unsigned long get_MarvellPHY_media_mode (struct eth_device *dev); +static unsigned short ax88180_mdio_read (struct eth_device *dev, + unsigned long regaddr); +static void ax88180_mdio_write (struct eth_device *dev, + unsigned long regaddr, unsigned short regdata); + +/* + * =========================================================================== + * Local SubProgram Bodies + * =========================================================================== + */ +static int ax88180_mdio_check_complete (struct eth_device *dev) +{ + int us_cnt = 10000; + unsigned short tmpval; + + /* MDIO read/write should not take more than 10 ms */ + while (--us_cnt) { + tmpval = INW (dev, MDIOCTRL); + if (((tmpval & READ_PHY) == 0) && ((tmpval & WRITE_PHY) == 0)) + break; + } + + return us_cnt; +} + +static unsigned short +ax88180_mdio_read (struct eth_device *dev, unsigned long regaddr) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long tmpval = 0; + + OUTW (dev, (READ_PHY | (regaddr << 8) | priv->PhyAddr), MDIOCTRL); + + if (ax88180_mdio_check_complete (dev)) + tmpval = INW (dev, MDIODP); + else + printf ("Failed to read PHY register!\n"); + + return (unsigned short)(tmpval & 0xFFFF); +} + +static void +ax88180_mdio_write (struct eth_device *dev, unsigned long regaddr, + unsigned short regdata) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + + OUTW (dev, regdata, MDIODP); + + OUTW (dev, (WRITE_PHY | (regaddr << 8) | priv->PhyAddr), MDIOCTRL); + + if (!ax88180_mdio_check_complete (dev)) + printf ("Failed to write PHY register!\n"); +} + +static int ax88180_phy_reset (struct eth_device *dev) +{ + unsigned short delay_cnt = 500; + + ax88180_mdio_write (dev, MII_BMCR, (BMCR_RESET | BMCR_ANENABLE)); + + /* Wait for the reset to complete, or time out (500 ms) */ + while (ax88180_mdio_read (dev, MII_BMCR) & BMCR_RESET) { + udelay (1000); + if (--delay_cnt == 0) { + printf ("Failed to reset PHY!\n"); + return -1; + } + } + + return 0; +} + +static void ax88180_mac_reset (struct eth_device *dev) +{ + unsigned long tmpval; + unsigned char i; + + struct { + unsigned short offset, value; + } program_seq[] = { + { + MISC, MISC_NORMAL}, { + RXINDICATOR, DEFAULT_RXINDICATOR}, { + TXCMD, DEFAULT_TXCMD}, { + TXBS, DEFAULT_TXBS}, { + TXDES0, DEFAULT_TXDES0}, { + TXDES1, DEFAULT_TXDES1}, { + TXDES2, DEFAULT_TXDES2}, { + TXDES3, DEFAULT_TXDES3}, { + TXCFG, DEFAULT_TXCFG}, { + MACCFG2, DEFAULT_MACCFG2}, { + MACCFG3, DEFAULT_MACCFG3}, { + TXLEN, DEFAULT_TXLEN}, { + RXBTHD0, DEFAULT_RXBTHD0}, { + RXBTHD1, DEFAULT_RXBTHD1}, { + RXFULTHD, DEFAULT_RXFULTHD}, { + DOGTHD0, DEFAULT_DOGTHD0}, { + DOGTHD1, DEFAULT_DOGTHD1},}; + + OUTW (dev, MISC_RESET_MAC, MISC); + tmpval = INW (dev, MISC); + + for (i = 0; i < ARRAY_SIZE(program_seq); i++) + OUTW (dev, program_seq[i].value, program_seq[i].offset); +} + +static int ax88180_poll_tx_complete (struct eth_device *dev) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long tmpval, txbs_txdp; + int TimeOutCnt = 10000; + + txbs_txdp = 1 << priv->NextTxDesc; + + while (TimeOutCnt--) { + + tmpval = INW (dev, TXBS); + + if ((tmpval & txbs_txdp) == 0) + break; + + udelay (100); + } + + if (TimeOutCnt) + return 0; + else + return -TimeOutCnt; +} + +static void ax88180_rx_handler (struct eth_device *dev) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long data_size; + unsigned short rxcurt_ptr, rxbound_ptr, next_ptr; + int i; +#if defined (CONFIG_DRIVER_AX88180_16BIT) + unsigned short *rxdata = (unsigned short *)NetRxPackets[0]; +#else + unsigned long *rxdata = (unsigned long *)NetRxPackets[0]; +#endif + unsigned short count; + + rxcurt_ptr = INW (dev, RXCURT); + rxbound_ptr = INW (dev, RXBOUND); + next_ptr = (rxbound_ptr + 1) & RX_PAGE_NUM_MASK; + + debug ("ax88180: RX original RXBOUND=0x%04x," + " RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr); + + while (next_ptr != rxcurt_ptr) { + + OUTW (dev, RX_START_READ, RXINDICATOR); + + data_size = READ_RXBUF (dev) & 0xFFFF; + + if ((data_size == 0) || (data_size > MAX_RX_SIZE)) { + + OUTW (dev, RX_STOP_READ, RXINDICATOR); + + ax88180_mac_reset (dev); + printf ("ax88180: Invalid Rx packet length!" + " (len=0x%04lx)\n", data_size); + + debug ("ax88180: RX RXBOUND=0x%04x," + "RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr); + return; + } + + rxbound_ptr += (((data_size + 0xF) & 0xFFF0) >> 4) + 1; + rxbound_ptr &= RX_PAGE_NUM_MASK; + + /* Comput access times */ + count = (data_size + priv->PadSize) >> priv->BusWidth; + + for (i = 0; i < count; i++) { + *(rxdata + i) = READ_RXBUF (dev); + } + + OUTW (dev, RX_STOP_READ, RXINDICATOR); + + /* Pass the packet up to the protocol layers. */ + NetReceive (NetRxPackets[0], data_size); + + OUTW (dev, rxbound_ptr, RXBOUND); + + rxcurt_ptr = INW (dev, RXCURT); + rxbound_ptr = INW (dev, RXBOUND); + next_ptr = (rxbound_ptr + 1) & RX_PAGE_NUM_MASK; + + debug ("ax88180: RX updated RXBOUND=0x%04x," + "RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr); + } + + return; +} + +static int ax88180_phy_initial (struct eth_device *dev) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long tmp_regval; + unsigned short phyaddr; + + /* Search for first avaliable PHY chipset */ +#ifdef CONFIG_PHY_ADDR + phyaddr = CONFIG_PHY_ADDR; +#else + for (phyaddr = 0; phyaddr < 32; ++phyaddr) +#endif + { + priv->PhyAddr = phyaddr; + priv->PhyID0 = ax88180_mdio_read(dev, MII_PHYSID1); + priv->PhyID1 = ax88180_mdio_read(dev, MII_PHYSID2); + + switch (priv->PhyID0) { + case MARVELL_ALASKA_PHYSID0: + debug("ax88180: Found Marvell Alaska PHY family." + " (PHY Addr=0x%x)\n", priv->PhyAddr); + + switch (priv->PhyID1) { + case MARVELL_88E1118_PHYSID1: + ax88180_mdio_write(dev, M88E1118_PAGE_SEL, 2); + ax88180_mdio_write(dev, M88E1118_CR, + M88E1118_CR_DEFAULT); + ax88180_mdio_write(dev, M88E1118_PAGE_SEL, 3); + ax88180_mdio_write(dev, M88E1118_LEDCTL, + M88E1118_LEDCTL_DEFAULT); + ax88180_mdio_write(dev, M88E1118_LEDMIX, + M88E1118_LEDMIX_LED050 | M88E1118_LEDMIX_LED150 | 0x15); + ax88180_mdio_write(dev, M88E1118_PAGE_SEL, 0); + default: /* Default to 88E1111 Phy */ + tmp_regval = ax88180_mdio_read(dev, M88E1111_EXT_SSR); + if ((tmp_regval & HWCFG_MODE_MASK) != RGMII_COPPER_MODE) + ax88180_mdio_write(dev, M88E1111_EXT_SCR, + DEFAULT_EXT_SCR); + } + + if (ax88180_phy_reset(dev) < 0) + return 0; + ax88180_mdio_write(dev, M88_IER, LINK_CHANGE_INT); + + return 1; + + case CICADA_CIS8201_PHYSID0: + debug("ax88180: Found CICADA CIS8201 PHY" + " chipset. (PHY Addr=0x%x)\n", priv->PhyAddr); + + ax88180_mdio_write(dev, CIS_IMR, + (CIS_INT_ENABLE | LINK_CHANGE_INT)); + + /* Set CIS_SMI_PRIORITY bit before force the media mode */ + tmp_regval = ax88180_mdio_read(dev, CIS_AUX_CTRL_STATUS); + tmp_regval &= ~CIS_SMI_PRIORITY; + ax88180_mdio_write(dev, CIS_AUX_CTRL_STATUS, tmp_regval); + + return 1; + + case 0xffff: + /* No PHY at this addr */ + break; + + default: + printf("ax88180: Unknown PHY chipset %#x at addr %#x\n", + priv->PhyID0, priv->PhyAddr); + break; + } + } + + printf("ax88180: Unknown PHY chipset!!\n"); + return 0; +} + +static void ax88180_media_config (struct eth_device *dev) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long bmcr_val, bmsr_val; + unsigned long rxcfg_val, maccfg0_val, maccfg1_val; + unsigned long RealMediaMode; + int i; + + /* Waiting 2 seconds for PHY link stable */ + for (i = 0; i < 20000; i++) { + bmsr_val = ax88180_mdio_read (dev, MII_BMSR); + if (bmsr_val & BMSR_LSTATUS) { + break; + } + udelay (100); + } + + bmsr_val = ax88180_mdio_read (dev, MII_BMSR); + debug ("ax88180: BMSR=0x%04x\n", (unsigned int)bmsr_val); + + if (bmsr_val & BMSR_LSTATUS) { + bmcr_val = ax88180_mdio_read (dev, MII_BMCR); + + if (bmcr_val & BMCR_ANENABLE) { + + /* + * Waiting for Auto-negotiation completion, this may + * take up to 5 seconds. + */ + debug ("ax88180: Auto-negotiation is " + "enabled. Waiting for NWay completion..\n"); + for (i = 0; i < 50000; i++) { + bmsr_val = ax88180_mdio_read (dev, MII_BMSR); + if (bmsr_val & BMSR_ANEGCOMPLETE) { + break; + } + udelay (100); + } + } else + debug ("ax88180: Auto-negotiation is disabled.\n"); + + debug ("ax88180: BMCR=0x%04x, BMSR=0x%04x\n", + (unsigned int)bmcr_val, (unsigned int)bmsr_val); + + /* Get real media mode here */ + switch (priv->PhyID0) { + case MARVELL_ALASKA_PHYSID0: + RealMediaMode = get_MarvellPHY_media_mode(dev); + break; + case CICADA_CIS8201_PHYSID0: + RealMediaMode = get_CicadaPHY_media_mode(dev); + break; + default: + RealMediaMode = MEDIA_1000FULL; + break; + } + + priv->LinkState = INS_LINK_UP; + + switch (RealMediaMode) { + case MEDIA_1000FULL: + debug ("ax88180: 1000Mbps Full-duplex mode.\n"); + rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG; + maccfg0_val = TXFLOW_ENABLE | DEFAULT_MACCFG0; + maccfg1_val = GIGA_MODE_EN | RXFLOW_EN | + FULLDUPLEX | DEFAULT_MACCFG1; + break; + + case MEDIA_1000HALF: + debug ("ax88180: 1000Mbps Half-duplex mode.\n"); + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = DEFAULT_MACCFG0; + maccfg1_val = GIGA_MODE_EN | DEFAULT_MACCFG1; + break; + + case MEDIA_100FULL: + debug ("ax88180: 100Mbps Full-duplex mode.\n"); + rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG; + maccfg0_val = SPEED100 | TXFLOW_ENABLE + | DEFAULT_MACCFG0; + maccfg1_val = RXFLOW_EN | FULLDUPLEX | DEFAULT_MACCFG1; + break; + + case MEDIA_100HALF: + debug ("ax88180: 100Mbps Half-duplex mode.\n"); + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = SPEED100 | DEFAULT_MACCFG0; + maccfg1_val = DEFAULT_MACCFG1; + break; + + case MEDIA_10FULL: + debug ("ax88180: 10Mbps Full-duplex mode.\n"); + rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG; + maccfg0_val = TXFLOW_ENABLE | DEFAULT_MACCFG0; + maccfg1_val = RXFLOW_EN | FULLDUPLEX | DEFAULT_MACCFG1; + break; + + case MEDIA_10HALF: + debug ("ax88180: 10Mbps Half-duplex mode.\n"); + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = DEFAULT_MACCFG0; + maccfg1_val = DEFAULT_MACCFG1; + break; + default: + debug ("ax88180: Unknow media mode.\n"); + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = DEFAULT_MACCFG0; + maccfg1_val = DEFAULT_MACCFG1; + + priv->LinkState = INS_LINK_DOWN; + break; + } + + } else { + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = DEFAULT_MACCFG0; + maccfg1_val = DEFAULT_MACCFG1; + + priv->LinkState = INS_LINK_DOWN; + } + + OUTW (dev, rxcfg_val, RXCFG); + OUTW (dev, maccfg0_val, MACCFG0); + OUTW (dev, maccfg1_val, MACCFG1); + + return; +} + +static unsigned long get_MarvellPHY_media_mode (struct eth_device *dev) +{ + unsigned long m88_ssr; + unsigned long MediaMode; + + m88_ssr = ax88180_mdio_read (dev, M88_SSR); + switch (m88_ssr & SSR_MEDIA_MASK) { + case SSR_1000FULL: + MediaMode = MEDIA_1000FULL; + break; + case SSR_1000HALF: + MediaMode = MEDIA_1000HALF; + break; + case SSR_100FULL: + MediaMode = MEDIA_100FULL; + break; + case SSR_100HALF: + MediaMode = MEDIA_100HALF; + break; + case SSR_10FULL: + MediaMode = MEDIA_10FULL; + break; + case SSR_10HALF: + MediaMode = MEDIA_10HALF; + break; + default: + MediaMode = MEDIA_UNKNOWN; + break; + } + + return MediaMode; +} + +static unsigned long get_CicadaPHY_media_mode (struct eth_device *dev) +{ + unsigned long tmp_regval; + unsigned long MediaMode; + + tmp_regval = ax88180_mdio_read (dev, CIS_AUX_CTRL_STATUS); + switch (tmp_regval & CIS_MEDIA_MASK) { + case CIS_1000FULL: + MediaMode = MEDIA_1000FULL; + break; + case CIS_1000HALF: + MediaMode = MEDIA_1000HALF; + break; + case CIS_100FULL: + MediaMode = MEDIA_100FULL; + break; + case CIS_100HALF: + MediaMode = MEDIA_100HALF; + break; + case CIS_10FULL: + MediaMode = MEDIA_10FULL; + break; + case CIS_10HALF: + MediaMode = MEDIA_10HALF; + break; + default: + MediaMode = MEDIA_UNKNOWN; + break; + } + + return MediaMode; +} + +static void ax88180_halt (struct eth_device *dev) +{ + /* Disable AX88180 TX/RX functions */ + OUTW (dev, WAKEMOD, CMD); +} + +static int ax88180_init (struct eth_device *dev, bd_t * bd) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned short tmp_regval; + + ax88180_mac_reset (dev); + + /* Disable interrupt */ + OUTW (dev, CLEAR_IMR, IMR); + + /* Disable AX88180 TX/RX functions */ + OUTW (dev, WAKEMOD, CMD); + + /* Fill the MAC address */ + tmp_regval = + dev->enetaddr[0] | (((unsigned short)dev->enetaddr[1]) << 8); + OUTW (dev, tmp_regval, MACID0); + + tmp_regval = + dev->enetaddr[2] | (((unsigned short)dev->enetaddr[3]) << 8); + OUTW (dev, tmp_regval, MACID1); + + tmp_regval = + dev->enetaddr[4] | (((unsigned short)dev->enetaddr[5]) << 8); + OUTW (dev, tmp_regval, MACID2); + + ax88180_media_config (dev); + + OUTW (dev, DEFAULT_RXFILTER, RXFILTER); + + /* Initial variables here */ + priv->FirstTxDesc = TXDP0; + priv->NextTxDesc = TXDP0; + + /* Check if there is any invalid interrupt status and clear it. */ + OUTW (dev, INW (dev, ISR), ISR); + + /* Start AX88180 TX/RX functions */ + OUTW (dev, (RXEN | TXEN | WAKEMOD), CMD); + + return 0; +} + +/* Get a data block via Ethernet */ +static int ax88180_recv (struct eth_device *dev) +{ + unsigned short ISR_Status; + unsigned short tmp_regval; + + /* Read and check interrupt status here. */ + ISR_Status = INW (dev, ISR); + + while (ISR_Status) { + /* Clear the interrupt status */ + OUTW (dev, ISR_Status, ISR); + + debug ("\nax88180: The interrupt status = 0x%04x\n", + ISR_Status); + + if (ISR_Status & ISR_PHY) { + /* Read ISR register once to clear PHY interrupt bit */ + tmp_regval = ax88180_mdio_read (dev, M88_ISR); + ax88180_media_config (dev); + } + + if ((ISR_Status & ISR_RX) || (ISR_Status & ISR_RXBUFFOVR)) { + ax88180_rx_handler (dev); + } + + /* Read and check interrupt status again */ + ISR_Status = INW (dev, ISR); + } + + return 0; +} + +/* Send a data block via Ethernet. */ +static int ax88180_send(struct eth_device *dev, void *packet, int length) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned short TXDES_addr; + unsigned short txcmd_txdp, txbs_txdp; + unsigned short tmp_data; + int i; +#if defined (CONFIG_DRIVER_AX88180_16BIT) + volatile unsigned short *txdata = (volatile unsigned short *)packet; +#else + volatile unsigned long *txdata = (volatile unsigned long *)packet; +#endif + unsigned short count; + + if (priv->LinkState != INS_LINK_UP) { + return 0; + } + + priv->FirstTxDesc = priv->NextTxDesc; + txbs_txdp = 1 << priv->FirstTxDesc; + + debug ("ax88180: TXDP%d is available\n", priv->FirstTxDesc); + + txcmd_txdp = priv->FirstTxDesc << 13; + TXDES_addr = TXDES0 + (priv->FirstTxDesc << 2); + + OUTW (dev, (txcmd_txdp | length | TX_START_WRITE), TXCMD); + + /* Comput access times */ + count = (length + priv->PadSize) >> priv->BusWidth; + + for (i = 0; i < count; i++) { + WRITE_TXBUF (dev, *(txdata + i)); + } + + OUTW (dev, txcmd_txdp | length, TXCMD); + OUTW (dev, txbs_txdp, TXBS); + OUTW (dev, (TXDPx_ENABLE | length), TXDES_addr); + + priv->NextTxDesc = (priv->NextTxDesc + 1) & TXDP_MASK; + + /* + * Check the available transmit descriptor, if we had exhausted all + * transmit descriptor ,then we have to wait for at least one free + * descriptor + */ + txbs_txdp = 1 << priv->NextTxDesc; + tmp_data = INW (dev, TXBS); + + if (tmp_data & txbs_txdp) { + if (ax88180_poll_tx_complete (dev) < 0) { + ax88180_mac_reset (dev); + priv->FirstTxDesc = TXDP0; + priv->NextTxDesc = TXDP0; + printf ("ax88180: Transmit time out occurred!\n"); + } + } + + return 0; +} + +static void ax88180_read_mac_addr (struct eth_device *dev) +{ + unsigned short macid0_val, macid1_val, macid2_val; + unsigned short tmp_regval; + unsigned short i; + + /* Reload MAC address from EEPROM */ + OUTW (dev, RELOAD_EEPROM, PROMCTRL); + + /* Waiting for reload eeprom completion */ + for (i = 0; i < 500; i++) { + tmp_regval = INW (dev, PROMCTRL); + if ((tmp_regval & RELOAD_EEPROM) == 0) + break; + udelay (1000); + } + + /* Get MAC addresses */ + macid0_val = INW (dev, MACID0); + macid1_val = INW (dev, MACID1); + macid2_val = INW (dev, MACID2); + + if (((macid0_val | macid1_val | macid2_val) != 0) && + ((macid0_val & 0x01) == 0)) { + dev->enetaddr[0] = (unsigned char)macid0_val; + dev->enetaddr[1] = (unsigned char)(macid0_val >> 8); + dev->enetaddr[2] = (unsigned char)macid1_val; + dev->enetaddr[3] = (unsigned char)(macid1_val >> 8); + dev->enetaddr[4] = (unsigned char)macid2_val; + dev->enetaddr[5] = (unsigned char)(macid2_val >> 8); + } +} + +/* +=========================================================================== +<<<<<< Exported SubProgram Bodies >>>>>> +=========================================================================== +*/ +int ax88180_initialize (bd_t * bis) +{ + struct eth_device *dev; + struct ax88180_private *priv; + + dev = (struct eth_device *)malloc (sizeof *dev); + + if (NULL == dev) + return 0; + + memset (dev, 0, sizeof *dev); + + priv = (struct ax88180_private *)malloc (sizeof (*priv)); + + if (NULL == priv) + return 0; + + memset (priv, 0, sizeof *priv); + + sprintf (dev->name, "ax88180"); + dev->iobase = AX88180_BASE; + dev->priv = priv; + dev->init = ax88180_init; + dev->halt = ax88180_halt; + dev->send = ax88180_send; + dev->recv = ax88180_recv; + + priv->BusWidth = BUS_WIDTH_32; + priv->PadSize = 3; +#if defined (CONFIG_DRIVER_AX88180_16BIT) + OUTW (dev, (START_BASE >> 8), BASE); + OUTW (dev, DECODE_EN, DECODE); + + priv->BusWidth = BUS_WIDTH_16; + priv->PadSize = 1; +#endif + + ax88180_mac_reset (dev); + + /* Disable interrupt */ + OUTW (dev, CLEAR_IMR, IMR); + + /* Disable AX88180 TX/RX functions */ + OUTW (dev, WAKEMOD, CMD); + + ax88180_read_mac_addr (dev); + + eth_register (dev); + + return ax88180_phy_initial (dev); + +} diff --git a/qemu/roms/u-boot/drivers/net/ax88180.h b/qemu/roms/u-boot/drivers/net/ax88180.h new file mode 100644 index 000000000..daf18e015 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ax88180.h @@ -0,0 +1,396 @@ +/* ax88180.h: ASIX AX88180 Non-PCI Gigabit Ethernet u-boot driver */ +/* + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + */ + +#ifndef _AX88180_H_ +#define _AX88180_H_ + +#include +#include +#include + +typedef enum _ax88180_link_state { + INS_LINK_DOWN, + INS_LINK_UP, + INS_LINK_UNKNOWN +} ax88180_link_state; + +struct ax88180_private { + unsigned char BusWidth; + unsigned char PadSize; + unsigned short PhyAddr; + unsigned short PhyID0; + unsigned short PhyID1; + unsigned short FirstTxDesc; + unsigned short NextTxDesc; + ax88180_link_state LinkState; +}; + +#define BUS_WIDTH_16 1 +#define BUS_WIDTH_32 2 + +#define ENABLE_JUMBO 1 +#define DISABLE_JUMBO 0 + +#define ENABLE_BURST 1 +#define DISABLE_BURST 0 + +#define NORMAL_RX_MODE 0 +#define RX_LOOPBACK_MODE 1 +#define RX_INIFINIT_LOOP_MODE 2 +#define TX_INIFINIT_LOOP_MODE 3 + +#define DEFAULT_ETH_MTU 1500 + +/* Jumbo packet size 4086 bytes included 4 bytes CRC*/ +#define MAX_JUMBO_MTU 4072 + +/* Max Tx Jumbo size 4086 bytes included 4 bytes CRC */ +#define MAX_TX_JUMBO_SIZE 4086 + +/* Max Rx Jumbo size is 15K Bytes */ +#define MAX_RX_SIZE 0x3C00 + +#define MARVELL_ALASKA_PHYSID0 0x141 +#define MARVELL_88E1118_PHYSID1 0xE40 + +#define CICADA_CIS8201_PHYSID0 0x000F + +#define MEDIA_AUTO 0 +#define MEDIA_1000FULL 1 +#define MEDIA_1000HALF 2 +#define MEDIA_100FULL 3 +#define MEDIA_100HALF 4 +#define MEDIA_10FULL 5 +#define MEDIA_10HALF 6 +#define MEDIA_UNKNOWN 7 + +#define AUTO_MEDIA 0 +#define FORCE_MEDIA 1 + +#define TXDP_MASK 3 +#define TXDP0 0 +#define TXDP1 1 +#define TXDP2 2 +#define TXDP3 3 + +#define CMD_MAP_SIZE 0x100 + +#if defined (CONFIG_DRIVER_AX88180_16BIT) + #define AX88180_MEMORY_SIZE 0x00004000 + #define START_BASE 0x1000 + + #define RX_BUF_SIZE 0x1000 + #define TX_BUF_SIZE 0x0F00 + + #define TX_BASE START_BASE + #define CMD_BASE (TX_BASE + TX_BUF_SIZE) + #define RX_BASE (CMD_BASE + CMD_MAP_SIZE) +#else + #define AX88180_MEMORY_SIZE 0x00010000 + + #define RX_BUF_SIZE 0x8000 + #define TX_BUF_SIZE 0x7C00 + + #define RX_BASE 0x0000 + #define TX_BASE (RX_BASE + RX_BUF_SIZE) + #define CMD_BASE (TX_BASE + TX_BUF_SIZE) +#endif + +/* AX88180 Memory Mapping Definition */ +#define RXBUFFER_START RX_BASE + #define RX_PACKET_LEN_OFFSET 0 + #define RX_PAGE_NUM_MASK 0x7FF /* RX pages 0~7FFh */ +#define TXBUFFER_START TX_BASE + +/* AX88180 MAC Register Definition */ +#define DECODE (0) + #define DECODE_EN 0x00000001 +#define BASE (6) +#define CMD (CMD_BASE + 0x0000) + #define WAKEMOD 0x00000001 + #define TXEN 0x00000100 + #define RXEN 0x00000200 + #define DEFAULT_CMD WAKEMOD +#define IMR (CMD_BASE + 0x0004) + #define IMR_RXBUFFOVR 0x00000001 + #define IMR_WATCHDOG 0x00000002 + #define IMR_TX 0x00000008 + #define IMR_RX 0x00000010 + #define IMR_PHY 0x00000020 + #define CLEAR_IMR 0x00000000 + #define DEFAULT_IMR (IMR_PHY | IMR_RX | IMR_TX |\ + IMR_RXBUFFOVR | IMR_WATCHDOG) +#define ISR (CMD_BASE + 0x0008) + #define ISR_RXBUFFOVR 0x00000001 + #define ISR_WATCHDOG 0x00000002 + #define ISR_TX 0x00000008 + #define ISR_RX 0x00000010 + #define ISR_PHY 0x00000020 +#define TXCFG (CMD_BASE + 0x0010) + #define AUTOPAD_CRC 0x00000050 + #define DEFAULT_TXCFG AUTOPAD_CRC +#define TXCMD (CMD_BASE + 0x0014) + #define TXCMD_TXDP_MASK 0x00006000 + #define TXCMD_TXDP0 0x00000000 + #define TXCMD_TXDP1 0x00002000 + #define TXCMD_TXDP2 0x00004000 + #define TXCMD_TXDP3 0x00006000 + #define TX_START_WRITE 0x00008000 + #define TX_STOP_WRITE 0x00000000 + #define DEFAULT_TXCMD 0x00000000 +#define TXBS (CMD_BASE + 0x0018) + #define TXDP0_USED 0x00000001 + #define TXDP1_USED 0x00000002 + #define TXDP2_USED 0x00000004 + #define TXDP3_USED 0x00000008 + #define DEFAULT_TXBS 0x00000000 +#define TXDES0 (CMD_BASE + 0x0020) + #define TXDPx_ENABLE 0x00008000 + #define TXDPx_LEN_MASK 0x00001FFF + #define DEFAULT_TXDES0 0x00000000 +#define TXDES1 (CMD_BASE + 0x0024) + #define TXDPx_ENABLE 0x00008000 + #define TXDPx_LEN_MASK 0x00001FFF + #define DEFAULT_TXDES1 0x00000000 +#define TXDES2 (CMD_BASE + 0x0028) + #define TXDPx_ENABLE 0x00008000 + #define TXDPx_LEN_MASK 0x00001FFF + #define DEFAULT_TXDES2 0x00000000 +#define TXDES3 (CMD_BASE + 0x002C) + #define TXDPx_ENABLE 0x00008000 + #define TXDPx_LEN_MASK 0x00001FFF + #define DEFAULT_TXDES3 0x00000000 +#define RXCFG (CMD_BASE + 0x0030) + #define RXBUFF_PROTECT 0x00000001 + #define RXTCPCRC_CHECK 0x00000010 + #define RXFLOW_ENABLE 0x00000100 + #define DEFAULT_RXCFG RXBUFF_PROTECT +#define RXCURT (CMD_BASE + 0x0034) + #define DEFAULT_RXCURT 0x00000000 +#define RXBOUND (CMD_BASE + 0x0038) + #define DEFAULT_RXBOUND 0x7FF /* RX pages 0~7FFh */ +#define MACCFG0 (CMD_BASE + 0x0040) + #define MACCFG0_BIT3_0 0x00000007 + #define IPGT_VAL 0x00000150 + #define TXFLOW_ENABLE 0x00001000 + #define SPEED100 0x00008000 + #define DEFAULT_MACCFG0 (IPGT_VAL | MACCFG0_BIT3_0) +#define MACCFG1 (CMD_BASE + 0x0044) + #define RGMII_EN 0x00000002 + #define RXFLOW_EN 0x00000020 + #define FULLDUPLEX 0x00000040 + #define MAX_JUMBO_LEN 0x00000780 + #define RXJUMBO_EN 0x00000800 + #define GIGA_MODE_EN 0x00001000 + #define RXCRC_CHECK 0x00002000 + #define RXPAUSE_DA_CHECK 0x00004000 + + #define JUMBO_LEN_4K 0x00000200 + #define JUMBO_LEN_15K 0x00000780 + #define DEFAULT_MACCFG1 (RXCRC_CHECK | RXPAUSE_DA_CHECK | \ + RGMII_EN) + #define CICADA_DEFAULT_MACCFG1 (RXCRC_CHECK | RXPAUSE_DA_CHECK) +#define MACCFG2 (CMD_BASE + 0x0048) + #define MACCFG2_BIT15_8 0x00000100 + #define JAM_LIMIT_MASK 0x000000FC + #define DEFAULT_JAM_LIMIT 0x00000064 + #define DEFAULT_MACCFG2 MACCFG2_BIT15_8 +#define MACCFG3 (CMD_BASE + 0x004C) + #define IPGR2_VAL 0x0000000E + #define IPGR1_VAL 0x00000600 + #define NOABORT 0x00008000 + #define DEFAULT_MACCFG3 (IPGR1_VAL | IPGR2_VAL) +#define TXPAUT (CMD_BASE + 0x0054) + #define DEFAULT_TXPAUT 0x001FE000 +#define RXBTHD0 (CMD_BASE + 0x0058) + #define DEFAULT_RXBTHD0 0x00000300 +#define RXBTHD1 (CMD_BASE + 0x005C) + #define DEFAULT_RXBTHD1 0x00000600 +#define RXFULTHD (CMD_BASE + 0x0060) + #define DEFAULT_RXFULTHD 0x00000100 +#define MISC (CMD_BASE + 0x0068) + /* Normal operation mode */ + #define MISC_NORMAL 0x00000003 + /* Clear bit 0 to reset MAC */ + #define MISC_RESET_MAC 0x00000002 + /* Clear bit 1 to reset PHY */ + #define MISC_RESET_PHY 0x00000001 + /* Clear bit 0 and 1 to reset MAC and PHY */ + #define MISC_RESET_MAC_PHY 0x00000000 + #define DEFAULT_MISC MISC_NORMAL +#define MACID0 (CMD_BASE + 0x0070) +#define MACID1 (CMD_BASE + 0x0074) +#define MACID2 (CMD_BASE + 0x0078) +#define TXLEN (CMD_BASE + 0x007C) + #define DEFAULT_TXLEN 0x000005FC +#define RXFILTER (CMD_BASE + 0x0080) + #define RX_RXANY 0x00000001 + #define RX_MULTICAST 0x00000002 + #define RX_UNICAST 0x00000004 + #define RX_BROADCAST 0x00000008 + #define RX_MULTI_HASH 0x00000010 + #define DISABLE_RXFILTER 0x00000000 + #define DEFAULT_RXFILTER (RX_BROADCAST + RX_UNICAST) +#define MDIOCTRL (CMD_BASE + 0x0084) + #define PHY_ADDR_MASK 0x0000001F + #define REG_ADDR_MASK 0x00001F00 + #define READ_PHY 0x00004000 + #define WRITE_PHY 0x00008000 +#define MDIODP (CMD_BASE + 0x0088) +#define GPIOCTRL (CMD_BASE + 0x008C) +#define RXINDICATOR (CMD_BASE + 0x0090) + #define RX_START_READ 0x00000001 + #define RX_STOP_READ 0x00000000 + #define DEFAULT_RXINDICATOR RX_STOP_READ +#define TXST (CMD_BASE + 0x0094) +#define MDCCLKPAT (CMD_BASE + 0x00A0) +#define RXIPCRCCNT (CMD_BASE + 0x00A4) +#define RXCRCCNT (CMD_BASE + 0x00A8) +#define TXFAILCNT (CMD_BASE + 0x00AC) +#define PROMDP (CMD_BASE + 0x00B0) +#define PROMCTRL (CMD_BASE + 0x00B4) + #define RELOAD_EEPROM 0x00000200 +#define MAXRXLEN (CMD_BASE + 0x00B8) +#define HASHTAB0 (CMD_BASE + 0x00C0) +#define HASHTAB1 (CMD_BASE + 0x00C4) +#define HASHTAB2 (CMD_BASE + 0x00C8) +#define HASHTAB3 (CMD_BASE + 0x00CC) +#define DOGTHD0 (CMD_BASE + 0x00E0) + #define DEFAULT_DOGTHD0 0x0000FFFF +#define DOGTHD1 (CMD_BASE + 0x00E4) + #define START_WATCHDOG_TIMER 0x00008000 + #define DEFAULT_DOGTHD1 0x00000FFF +#define SOFTRST (CMD_BASE + 0x00EC) + #define SOFTRST_NORMAL 0x00000003 + #define SOFTRST_RESET_MAC 0x00000002 + +/* Marvell 88E1111 Gigabit PHY Register Definition */ +#define M88_SSR 0x0011 + #define SSR_SPEED_MASK 0xC000 + #define SSR_SPEED_1000 0x8000 + #define SSR_SPEED_100 0x4000 + #define SSR_SPEED_10 0x0000 + #define SSR_DUPLEX 0x2000 + #define SSR_MEDIA_RESOLVED_OK 0x0800 + + #define SSR_MEDIA_MASK (SSR_SPEED_MASK | SSR_DUPLEX) + #define SSR_1000FULL (SSR_SPEED_1000 | SSR_DUPLEX) + #define SSR_1000HALF SSR_SPEED_1000 + #define SSR_100FULL (SSR_SPEED_100 | SSR_DUPLEX) + #define SSR_100HALF SSR_SPEED_100 + #define SSR_10FULL (SSR_SPEED_10 | SSR_DUPLEX) + #define SSR_10HALF SSR_SPEED_10 +#define M88_IER 0x0012 + #define LINK_CHANGE_INT 0x0400 +#define M88_ISR 0x0013 + #define LINK_CHANGE_STATUS 0x0400 +#define M88E1111_EXT_SCR 0x0014 + #define RGMII_RXCLK_DELAY 0x0080 + #define RGMII_TXCLK_DELAY 0x0002 + #define DEFAULT_EXT_SCR (RGMII_TXCLK_DELAY | RGMII_RXCLK_DELAY) +#define M88E1111_EXT_SSR 0x001B + #define HWCFG_MODE_MASK 0x000F + #define RGMII_COPPER_MODE 0x000B + +/* Marvell 88E1118 Gigabit PHY Register Definition */ +#define M88E1118_CR 0x14 + #define M88E1118_CR_RGMII_RXCLK_DELAY 0x0020 + #define M88E1118_CR_RGMII_TXCLK_DELAY 0x0010 + #define M88E1118_CR_DEFAULT (M88E1118_CR_RGMII_TXCLK_DELAY | \ + M88E1118_CR_RGMII_RXCLK_DELAY) +#define M88E1118_LEDCTL 0x10 /* Reg 16 on page 3 */ + #define M88E1118_LEDCTL_LED2INT 0x200 + #define M88E1118_LEDCTL_LED2BLNK 0x400 + #define M88E1118_LEDCTL_LED0DUALMODE1 0xc + #define M88E1118_LEDCTL_LED0DUALMODE2 0xd + #define M88E1118_LEDCTL_LED0DUALMODE3 0xe + #define M88E1118_LEDCTL_LED0DUALMODE4 0xf + #define M88E1118_LEDCTL_DEFAULT (M88E1118_LEDCTL_LED2BLNK | \ + M88E1118_LEDCTL_LED0DUALMODE4) + +#define M88E1118_LEDMIX 0x11 /* Reg 17 on page 3 */ + #define M88E1118_LEDMIX_LED050 0x4 + #define M88E1118_LEDMIX_LED150 0x8 + +#define M88E1118_PAGE_SEL 0x16 /* Reg page select */ + +/* CICADA CIS8201 Gigabit PHY Register Definition */ +#define CIS_IMR 0x0019 + #define CIS_INT_ENABLE 0x8000 + #define CIS_LINK_CHANGE_INT 0x2000 +#define CIS_ISR 0x001A + #define CIS_INT_PENDING 0x8000 + #define CIS_LINK_CHANGE_STATUS 0x2000 +#define CIS_AUX_CTRL_STATUS 0x001C + #define CIS_AUTONEG_COMPLETE 0x8000 + #define CIS_SPEED_MASK 0x0018 + #define CIS_SPEED_1000 0x0010 + #define CIS_SPEED_100 0x0008 + #define CIS_SPEED_10 0x0000 + #define CIS_DUPLEX 0x0020 + + #define CIS_MEDIA_MASK (CIS_SPEED_MASK | CIS_DUPLEX) + #define CIS_1000FULL (CIS_SPEED_1000 | CIS_DUPLEX) + #define CIS_1000HALF CIS_SPEED_1000 + #define CIS_100FULL (CIS_SPEED_100 | CIS_DUPLEX) + #define CIS_100HALF CIS_SPEED_100 + #define CIS_10FULL (CIS_SPEED_10 | CIS_DUPLEX) + #define CIS_10HALF CIS_SPEED_10 + #define CIS_SMI_PRIORITY 0x0004 + +static inline unsigned short INW (struct eth_device *dev, unsigned long addr) +{ + return le16_to_cpu(readw(addr + (void *)dev->iobase)); +} + +/* + Access RXBUFFER_START/TXBUFFER_START to read RX buffer/write TX buffer +*/ +#if defined (CONFIG_DRIVER_AX88180_16BIT) +static inline void OUTW (struct eth_device *dev, unsigned short command, unsigned long addr) +{ + writew(cpu_to_le16(command), addr + (void *)dev->iobase); +} + +static inline unsigned short READ_RXBUF (struct eth_device *dev) +{ + return le16_to_cpu(readw(RXBUFFER_START + (void *)dev->iobase)); +} + +static inline void WRITE_TXBUF (struct eth_device *dev, unsigned short data) +{ + writew(cpu_to_le16(data), TXBUFFER_START + (void *)dev->iobase); +} +#else +static inline void OUTW (struct eth_device *dev, unsigned short command, unsigned long addr) +{ + writel(cpu_to_le32(command), addr + (void *)dev->iobase); +} + +static inline unsigned long READ_RXBUF (struct eth_device *dev) +{ + return le32_to_cpu(readl(RXBUFFER_START + (void *)dev->iobase)); +} + +static inline void WRITE_TXBUF (struct eth_device *dev, unsigned long data) +{ + writel(cpu_to_le32(data), TXBUFFER_START + (void *)dev->iobase); +} +#endif + +#endif /* _AX88180_H_ */ diff --git a/qemu/roms/u-boot/drivers/net/ax88796.c b/qemu/roms/u-boot/drivers/net/ax88796.c new file mode 100644 index 000000000..c45f64618 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ax88796.c @@ -0,0 +1,144 @@ +/* + * (c) 2007 Nobuhiro Iwamatsu + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include "ax88796.h" + +/* + * Set 1 bit data + */ +static void ax88796_bitset(u32 bit) +{ + /* DATA1 */ + if( bit ) + EEDI_HIGH; + else + EEDI_LOW; + + EECLK_LOW; + udelay(1000); + EECLK_HIGH; + udelay(1000); + EEDI_LOW; +} + +/* + * Get 1 bit data + */ +static u8 ax88796_bitget(void) +{ + u8 bit; + + EECLK_LOW; + udelay(1000); + /* DATA */ + bit = EEDO; + EECLK_HIGH; + udelay(1000); + + return bit; +} + +/* + * Send COMMAND to EEPROM + */ +static void ax88796_eep_cmd(u8 cmd) +{ + ax88796_bitset(BIT_DUMMY); + switch(cmd){ + case MAC_EEP_READ: + ax88796_bitset(1); + ax88796_bitset(1); + ax88796_bitset(0); + break; + + case MAC_EEP_WRITE: + ax88796_bitset(1); + ax88796_bitset(0); + ax88796_bitset(1); + break; + + case MAC_EEP_ERACE: + ax88796_bitset(1); + ax88796_bitset(1); + ax88796_bitset(1); + break; + + case MAC_EEP_EWEN: + ax88796_bitset(1); + ax88796_bitset(0); + ax88796_bitset(0); + break; + + case MAC_EEP_EWDS: + ax88796_bitset(1); + ax88796_bitset(0); + ax88796_bitset(0); + break; + default: + break; + } +} + +static void ax88796_eep_setaddr(u16 addr) +{ + int i ; + + for( i = 7 ; i >= 0 ; i-- ) + ax88796_bitset(addr & (1 << i)); +} + +/* + * Get data from EEPROM + */ +static u16 ax88796_eep_getdata(void) +{ + ushort data = 0; + int i; + + ax88796_bitget(); /* DUMMY */ + for( i = 0 ; i < 16 ; i++ ){ + data <<= 1; + data |= ax88796_bitget(); + } + return data; +} + +static void ax88796_mac_read(u8 *buff) +{ + int i ; + u16 data; + u16 addr = 0; + + for( i = 0 ; i < 3; i++ ) + { + EECS_HIGH; + EEDI_LOW; + udelay(1000); + /* READ COMMAND */ + ax88796_eep_cmd(MAC_EEP_READ); + /* ADDRESS */ + ax88796_eep_setaddr(addr++); + /* GET DATA */ + data = ax88796_eep_getdata(); + *buff++ = (uchar)(data & 0xff); + *buff++ = (uchar)((data >> 8) & 0xff); + EECLK_LOW; + EEDI_LOW; + EECS_LOW; + } +} + +int get_prom(u8* mac_addr, u8* base_addr) +{ + u8 prom[32]; + int i; + + ax88796_mac_read(prom); + for (i = 0; i < 6; i++){ + mac_addr[i] = prom[i]; + } + return 1; +} diff --git a/qemu/roms/u-boot/drivers/net/ax88796.h b/qemu/roms/u-boot/drivers/net/ax88796.h new file mode 100644 index 000000000..2b4e05af3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ax88796.h @@ -0,0 +1,67 @@ +/* + * AX88796L(NE2000) support + * + * (c) 2007 Nobuhiro Iwamatsu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DRIVERS_AX88796L_H__ +#define __DRIVERS_AX88796L_H__ + +#define DP_DATA (0x10 << 1) +#define START_PG 0x40 /* First page of TX buffer */ +#define START_PG2 0x48 +#define STOP_PG 0x80 /* Last page +1 of RX ring */ +#define TX_PAGES 12 +#define RX_START (START_PG+TX_PAGES) +#define RX_END STOP_PG + +#define AX88796L_BASE_ADDRESS CONFIG_DRIVER_NE2000_BASE +#define AX88796L_BYTE_ACCESS 0x00001000 +#define AX88796L_OFFSET 0x00000400 +#define AX88796L_ADDRESS_BYTE AX88796L_BASE_ADDRESS + \ + AX88796L_BYTE_ACCESS + AX88796L_OFFSET +#define AX88796L_REG_MEMR AX88796L_ADDRESS_BYTE + (0x14<<1) +#define AX88796L_REG_CR AX88796L_ADDRESS_BYTE + (0x00<<1) + +#define AX88796L_CR (*(vu_short *)(AX88796L_REG_CR)) +#define AX88796L_MEMR (*(vu_short *)(AX88796L_REG_MEMR)) + +#define EECS_HIGH (AX88796L_MEMR |= 0x10) +#define EECS_LOW (AX88796L_MEMR &= 0xef) +#define EECLK_HIGH (AX88796L_MEMR |= 0x80) +#define EECLK_LOW (AX88796L_MEMR &= 0x7f) +#define EEDI_HIGH (AX88796L_MEMR |= 0x20) +#define EEDI_LOW (AX88796L_MEMR &= 0xdf) +#define EEDO ((AX88796L_MEMR & 0x40)>>6) + +#define PAGE0_SET (AX88796L_CR &= 0x3f) +#define PAGE1_SET (AX88796L_CR = (AX88796L_CR & 0x3f) | 0x40) + +#define BIT_DUMMY 0 +#define MAC_EEP_READ 1 +#define MAC_EEP_WRITE 2 +#define MAC_EEP_ERACE 3 +#define MAC_EEP_EWEN 4 +#define MAC_EEP_EWDS 5 + +/* R7780MP Specific code */ +#if defined(CONFIG_R7780MP) +#define ISA_OFFSET 0x1400 +#define DP_IN(_b_, _o_, _d_) (_d_) = \ + *( (vu_short *) ((_b_) + ((_o_) * 2) + ISA_OFFSET)) +#define DP_OUT(_b_, _o_, _d_) \ + *((vu_short *)((_b_) + ((_o_) * 2) + ISA_OFFSET)) = (_d_) +#define DP_IN_DATA(_b_, _d_) (_d_) = *( (vu_short *) ((_b_) + ISA_OFFSET)) +#define DP_OUT_DATA(_b_, _d_) *( (vu_short *) ((_b_)+ISA_OFFSET)) = (_d_) +#else +/* Please change for your target boards */ +#define ISA_OFFSET 0x0000 +#define DP_IN(_b_, _o_, _d_) (_d_) = *( (vu_short *)((_b_)+(_o_ )+ISA_OFFSET)) +#define DP_OUT(_b_, _o_, _d_) *((vu_short *)((_b_)+(_o_)+ISA_OFFSET)) = (_d_) +#define DP_IN_DATA(_b_, _d_) (_d_) = *( (vu_short *) ((_b_)+ISA_OFFSET)) +#define DP_OUT_DATA(_b_, _d_) *( (vu_short *) ((_b_)+ISA_OFFSET)) = (_d_) +#endif + +#endif /* __DRIVERS_AX88796L_H__ */ diff --git a/qemu/roms/u-boot/drivers/net/bfin_mac.c b/qemu/roms/u-boot/drivers/net/bfin_mac.c new file mode 100644 index 000000000..0c2d2ef1a --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/bfin_mac.c @@ -0,0 +1,498 @@ +/* + * Driver for Blackfin On-Chip MAC device + * + * Copyright (c) 2005-2008 Analog Device, Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "bfin_mac.h" + +#ifndef CONFIG_PHY_ADDR +# define CONFIG_PHY_ADDR 1 +#endif +#ifndef CONFIG_PHY_CLOCK_FREQ +# define CONFIG_PHY_CLOCK_FREQ 2500000 +#endif + +#ifdef CONFIG_POST +#include +#endif + +#define RXBUF_BASE_ADDR 0xFF900000 +#define TXBUF_BASE_ADDR 0xFF800000 +#define TX_BUF_CNT 1 + +#define TOUT_LOOP 1000000 + +static ADI_ETHER_BUFFER *txbuf[TX_BUF_CNT]; +static ADI_ETHER_BUFFER *rxbuf[PKTBUFSRX]; +static u16 txIdx; /* index of the current RX buffer */ +static u16 rxIdx; /* index of the current TX buffer */ + +/* DMAx_CONFIG values at DMA Restart */ +static const union { + u16 data; + ADI_DMA_CONFIG_REG reg; +} txdmacfg = { + .reg = { + .b_DMA_EN = 1, /* enabled */ + .b_WNR = 0, /* read from memory */ + .b_WDSIZE = 2, /* wordsize is 32 bits */ + .b_DMA2D = 0, + .b_RESTART = 0, + .b_DI_SEL = 0, + .b_DI_EN = 0, /* no interrupt */ + .b_NDSIZE = 5, /* 5 half words is desc size */ + .b_FLOW = 7 /* large desc flow */ + }, +}; + +static int bfin_miiphy_wait(void) +{ + /* poll the STABUSY bit */ + while (bfin_read_EMAC_STAADD() & STABUSY) + continue; + return 0; +} + +static int bfin_miiphy_read(const char *devname, uchar addr, uchar reg, ushort *val) +{ + if (bfin_miiphy_wait()) + return 1; + bfin_write_EMAC_STAADD(SET_PHYAD(addr) | SET_REGAD(reg) | STABUSY); + if (bfin_miiphy_wait()) + return 1; + *val = bfin_read_EMAC_STADAT(); + return 0; +} + +static int bfin_miiphy_write(const char *devname, uchar addr, uchar reg, ushort val) +{ + if (bfin_miiphy_wait()) + return 1; + bfin_write_EMAC_STADAT(val); + bfin_write_EMAC_STAADD(SET_PHYAD(addr) | SET_REGAD(reg) | STAOP | STABUSY); + return 0; +} + +int bfin_EMAC_initialize(bd_t *bis) +{ + struct eth_device *dev; + dev = malloc(sizeof(*dev)); + if (dev == NULL) + hang(); + + memset(dev, 0, sizeof(*dev)); + strcpy(dev->name, "bfin_mac"); + + dev->iobase = 0; + dev->priv = 0; + dev->init = bfin_EMAC_init; + dev->halt = bfin_EMAC_halt; + dev->send = bfin_EMAC_send; + dev->recv = bfin_EMAC_recv; + dev->write_hwaddr = bfin_EMAC_setup_addr; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, bfin_miiphy_read, bfin_miiphy_write); +#endif + + return 0; +} + +static int bfin_EMAC_send(struct eth_device *dev, void *packet, int length) +{ + int i; + int result = 0; + + if (length <= 0) { + printf("Ethernet: bad packet size: %d\n", length); + goto out; + } + + if (bfin_read_DMA2_IRQ_STATUS() & DMA_ERR) { + printf("Ethernet: tx DMA error\n"); + goto out; + } + + for (i = 0; (bfin_read_DMA2_IRQ_STATUS() & DMA_RUN); ++i) { + if (i > TOUT_LOOP) { + puts("Ethernet: tx time out\n"); + goto out; + } + } + txbuf[txIdx]->FrmData->NoBytes = length; + memcpy(txbuf[txIdx]->FrmData->Dest, (void *)packet, length); + txbuf[txIdx]->Dma[0].START_ADDR = (u32) txbuf[txIdx]->FrmData; + bfin_write_DMA2_NEXT_DESC_PTR(txbuf[txIdx]->Dma); + bfin_write_DMA2_CONFIG(txdmacfg.data); + bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE); + + for (i = 0; (txbuf[txIdx]->StatusWord & TX_COMP) == 0; i++) { + if (i > TOUT_LOOP) { + puts("Ethernet: tx error\n"); + goto out; + } + } + result = txbuf[txIdx]->StatusWord; + txbuf[txIdx]->StatusWord = 0; + if ((txIdx + 1) >= TX_BUF_CNT) + txIdx = 0; + else + txIdx++; + out: + debug("BFIN EMAC send: length = %d\n", length); + return result; +} + +static int bfin_EMAC_recv(struct eth_device *dev) +{ + int length = 0; + + for (;;) { + if ((rxbuf[rxIdx]->StatusWord & RX_COMP) == 0) { + length = -1; + break; + } + if ((rxbuf[rxIdx]->StatusWord & RX_DMAO) != 0) { + printf("Ethernet: rx dma overrun\n"); + break; + } + if ((rxbuf[rxIdx]->StatusWord & RX_OK) == 0) { + printf("Ethernet: rx error\n"); + break; + } + length = rxbuf[rxIdx]->StatusWord & 0x000007FF; + if (length <= 4) { + printf("Ethernet: bad frame\n"); + break; + } + + debug("%s: len = %d\n", __func__, length - 4); + + NetRxPackets[rxIdx] = rxbuf[rxIdx]->FrmData->Dest; + NetReceive(NetRxPackets[rxIdx], length - 4); + bfin_write_DMA1_IRQ_STATUS(DMA_DONE | DMA_ERR); + rxbuf[rxIdx]->StatusWord = 0x00000000; + if ((rxIdx + 1) >= PKTBUFSRX) + rxIdx = 0; + else + rxIdx++; + } + + return length; +} + +/************************************************************** + * + * Ethernet Initialization Routine + * + *************************************************************/ + +/* MDC = SCLK / MDC_freq / 2 - 1 */ +#define MDC_FREQ_TO_DIV(mdc_freq) (get_sclk() / (mdc_freq) / 2 - 1) + +#ifndef CONFIG_BFIN_MAC_PINS +# ifdef CONFIG_RMII +# define CONFIG_BFIN_MAC_PINS P_RMII0 +# else +# define CONFIG_BFIN_MAC_PINS P_MII0 +# endif +#endif + +static int bfin_miiphy_init(struct eth_device *dev, int *opmode) +{ + const unsigned short pins[] = CONFIG_BFIN_MAC_PINS; + u16 phydat; + size_t count; + + /* Enable PHY output */ + bfin_write_VR_CTL(bfin_read_VR_CTL() | CLKBUFOE); + + /* Set all the pins to peripheral mode */ + peripheral_request_list(pins, "bfin_mac"); + + /* Odd word alignment for Receive Frame DMA word */ + /* Configure checksum support and rcve frame word alignment */ + bfin_write_EMAC_SYSCTL(RXDWA | RXCKS | SET_MDCDIV(MDC_FREQ_TO_DIV(CONFIG_PHY_CLOCK_FREQ))); + + /* turn on auto-negotiation and wait for link to come up */ + bfin_miiphy_write(dev->name, CONFIG_PHY_ADDR, MII_BMCR, BMCR_ANENABLE); + count = 0; + while (1) { + ++count; + if (bfin_miiphy_read(dev->name, CONFIG_PHY_ADDR, MII_BMSR, &phydat)) + return -1; + if (phydat & BMSR_LSTATUS) + break; + if (count > 30000) { + printf("%s: link down, check cable\n", dev->name); + return -1; + } + udelay(100); + } + + /* see what kind of link we have */ + if (bfin_miiphy_read(dev->name, CONFIG_PHY_ADDR, MII_LPA, &phydat)) + return -1; + if (phydat & LPA_DUPLEX) + *opmode = FDMODE; + else + *opmode = 0; + + bfin_write_EMAC_MMC_CTL(RSTC | CROLL); + bfin_write_EMAC_VLAN1(EMAC_VLANX_DEF_VAL); + bfin_write_EMAC_VLAN2(EMAC_VLANX_DEF_VAL); + + /* Initialize the TX DMA channel registers */ + bfin_write_DMA2_X_COUNT(0); + bfin_write_DMA2_X_MODIFY(4); + bfin_write_DMA2_Y_COUNT(0); + bfin_write_DMA2_Y_MODIFY(0); + + /* Initialize the RX DMA channel registers */ + bfin_write_DMA1_X_COUNT(0); + bfin_write_DMA1_X_MODIFY(4); + bfin_write_DMA1_Y_COUNT(0); + bfin_write_DMA1_Y_MODIFY(0); + + return 0; +} + +static int bfin_EMAC_setup_addr(struct eth_device *dev) +{ + bfin_write_EMAC_ADDRLO( + dev->enetaddr[0] | + dev->enetaddr[1] << 8 | + dev->enetaddr[2] << 16 | + dev->enetaddr[3] << 24 + ); + bfin_write_EMAC_ADDRHI( + dev->enetaddr[4] | + dev->enetaddr[5] << 8 + ); + return 0; +} + +static int bfin_EMAC_init(struct eth_device *dev, bd_t *bd) +{ + u32 opmode; + int dat; + int i; + debug("Eth_init: ......\n"); + + txIdx = 0; + rxIdx = 0; + + /* Initialize System Register */ + if (bfin_miiphy_init(dev, &dat) < 0) + return -1; + + /* Initialize EMAC address */ + bfin_EMAC_setup_addr(dev); + + /* Initialize TX and RX buffer */ + for (i = 0; i < PKTBUFSRX; i++) { + rxbuf[i] = SetupRxBuffer(i); + if (i > 0) { + rxbuf[i - 1]->Dma[1].NEXT_DESC_PTR = rxbuf[i]->Dma; + if (i == (PKTBUFSRX - 1)) + rxbuf[i]->Dma[1].NEXT_DESC_PTR = rxbuf[0]->Dma; + } + } + for (i = 0; i < TX_BUF_CNT; i++) { + txbuf[i] = SetupTxBuffer(i); + if (i > 0) { + txbuf[i - 1]->Dma[1].NEXT_DESC_PTR = txbuf[i]->Dma; + if (i == (TX_BUF_CNT - 1)) + txbuf[i]->Dma[1].NEXT_DESC_PTR = txbuf[0]->Dma; + } + } + + /* Set RX DMA */ + bfin_write_DMA1_NEXT_DESC_PTR(rxbuf[0]->Dma); + bfin_write_DMA1_CONFIG(rxbuf[0]->Dma[0].CONFIG_DATA); + + /* Wait MII done */ + bfin_miiphy_wait(); + + /* We enable only RX here */ + /* ASTP : Enable Automatic Pad Stripping + PR : Promiscuous Mode for test + PSF : Receive frames with total length less than 64 bytes. + FDMODE : Full Duplex Mode + LB : Internal Loopback for test + RE : Receiver Enable */ + if (dat == FDMODE) + opmode = ASTP | FDMODE | PSF; + else + opmode = ASTP | PSF; + opmode |= RE; +#ifdef CONFIG_RMII + opmode |= TE | RMII; +#endif + /* Turn on the EMAC */ + bfin_write_EMAC_OPMODE(opmode); + return 0; +} + +static void bfin_EMAC_halt(struct eth_device *dev) +{ + debug("Eth_halt: ......\n"); + /* Turn off the EMAC */ + bfin_write_EMAC_OPMODE(0); + /* Turn off the EMAC RX DMA */ + bfin_write_DMA1_CONFIG(0); + bfin_write_DMA2_CONFIG(0); +} + +ADI_ETHER_BUFFER *SetupRxBuffer(int no) +{ + ADI_ETHER_FRAME_BUFFER *frmbuf; + ADI_ETHER_BUFFER *buf; + int nobytes_buffer = sizeof(ADI_ETHER_BUFFER[2]) / 2; /* ensure a multi. of 4 */ + int total_size = nobytes_buffer + RECV_BUFSIZE; + + buf = (void *) (RXBUF_BASE_ADDR + no * total_size); + frmbuf = (void *) (RXBUF_BASE_ADDR + no * total_size + nobytes_buffer); + + memset(buf, 0x00, nobytes_buffer); + buf->FrmData = frmbuf; + memset(frmbuf, 0xfe, RECV_BUFSIZE); + + /* set up first desc to point to receive frame buffer */ + buf->Dma[0].NEXT_DESC_PTR = &(buf->Dma[1]); + buf->Dma[0].START_ADDR = (u32) buf->FrmData; + buf->Dma[0].CONFIG.b_DMA_EN = 1; /* enabled */ + buf->Dma[0].CONFIG.b_WNR = 1; /* Write to memory */ + buf->Dma[0].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */ + buf->Dma[0].CONFIG.b_NDSIZE = 5; /* 5 half words is desc size. */ + buf->Dma[0].CONFIG.b_FLOW = 7; /* large desc flow */ + + /* set up second desc to point to status word */ + buf->Dma[1].NEXT_DESC_PTR = buf->Dma; + buf->Dma[1].START_ADDR = (u32) & buf->IPHdrChksum; + buf->Dma[1].CONFIG.b_DMA_EN = 1; /* enabled */ + buf->Dma[1].CONFIG.b_WNR = 1; /* Write to memory */ + buf->Dma[1].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */ + buf->Dma[1].CONFIG.b_DI_EN = 1; /* enable interrupt */ + buf->Dma[1].CONFIG.b_NDSIZE = 5; /* must be 0 when FLOW is 0 */ + buf->Dma[1].CONFIG.b_FLOW = 7; /* stop */ + + return buf; +} + +ADI_ETHER_BUFFER *SetupTxBuffer(int no) +{ + ADI_ETHER_FRAME_BUFFER *frmbuf; + ADI_ETHER_BUFFER *buf; + int nobytes_buffer = sizeof(ADI_ETHER_BUFFER[2]) / 2; /* ensure a multi. of 4 */ + int total_size = nobytes_buffer + RECV_BUFSIZE; + + buf = (void *) (TXBUF_BASE_ADDR + no * total_size); + frmbuf = (void *) (TXBUF_BASE_ADDR + no * total_size + nobytes_buffer); + + memset(buf, 0x00, nobytes_buffer); + buf->FrmData = frmbuf; + memset(frmbuf, 0x00, RECV_BUFSIZE); + + /* set up first desc to point to receive frame buffer */ + buf->Dma[0].NEXT_DESC_PTR = &(buf->Dma[1]); + buf->Dma[0].START_ADDR = (u32) buf->FrmData; + buf->Dma[0].CONFIG.b_DMA_EN = 1; /* enabled */ + buf->Dma[0].CONFIG.b_WNR = 0; /* Read to memory */ + buf->Dma[0].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */ + buf->Dma[0].CONFIG.b_NDSIZE = 5; /* 5 half words is desc size. */ + buf->Dma[0].CONFIG.b_FLOW = 7; /* large desc flow */ + + /* set up second desc to point to status word */ + buf->Dma[1].NEXT_DESC_PTR = &(buf->Dma[0]); + buf->Dma[1].START_ADDR = (u32) & buf->StatusWord; + buf->Dma[1].CONFIG.b_DMA_EN = 1; /* enabled */ + buf->Dma[1].CONFIG.b_WNR = 1; /* Write to memory */ + buf->Dma[1].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */ + buf->Dma[1].CONFIG.b_DI_EN = 1; /* enable interrupt */ + buf->Dma[1].CONFIG.b_NDSIZE = 0; /* must be 0 when FLOW is 0 */ + buf->Dma[1].CONFIG.b_FLOW = 0; /* stop */ + + return buf; +} + +#if defined(CONFIG_POST) && defined(CONFIG_SYS_POST_ETHER) +int ether_post_test(int flags) +{ + uchar buf[64]; + int i, value = 0; + int length; + uint addr; + + printf("\n--------"); + bfin_EMAC_init(NULL, NULL); + /* construct the package */ + addr = bfin_read_EMAC_ADDRLO(); + buf[0] = buf[6] = addr; + buf[1] = buf[7] = addr >> 8; + buf[2] = buf[8] = addr >> 16; + buf[3] = buf[9] = addr >> 24; + addr = bfin_read_EMAC_ADDRHI(); + buf[4] = buf[10] = addr; + buf[5] = buf[11] = addr >> 8; + buf[12] = 0x08; /* Type: ARP */ + buf[13] = 0x06; + buf[14] = 0x00; /* Hardware type: Ethernet */ + buf[15] = 0x01; + buf[16] = 0x08; /* Protocal type: IP */ + buf[17] = 0x00; + buf[18] = 0x06; /* Hardware size */ + buf[19] = 0x04; /* Protocol size */ + buf[20] = 0x00; /* Opcode: request */ + buf[21] = 0x01; + + for (i = 0; i < 42; i++) + buf[i + 22] = i; + printf("--------Send 64 bytes......\n"); + bfin_EMAC_send(NULL, buf, 64); + for (i = 0; i < 100; i++) { + udelay(10000); + if ((rxbuf[rxIdx]->StatusWord & RX_COMP) != 0) { + value = 1; + break; + } + } + if (value == 0) { + printf("--------EMAC can't receive any data\n"); + eth_halt(); + return -1; + } + length = rxbuf[rxIdx]->StatusWord & 0x000007FF - 4; + for (i = 0; i < length; i++) { + if (rxbuf[rxIdx]->FrmData->Dest[i] != buf[i]) { + printf("--------EMAC receive error data!\n"); + eth_halt(); + return -1; + } + } + printf("--------receive %d bytes, matched\n", length); + bfin_EMAC_halt(NULL); + return 0; +} +#endif diff --git a/qemu/roms/u-boot/drivers/net/bfin_mac.h b/qemu/roms/u-boot/drivers/net/bfin_mac.h new file mode 100644 index 000000000..54ffb3830 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/bfin_mac.h @@ -0,0 +1,65 @@ +/* + * bfin_mac.h - some defines/structures for the Blackfin on-chip MAC. + * + * Copyright (c) 2005-2008 Analog Device, Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __BFIN_MAC_H__ +#define __BFIN_MAC_H__ + +#define RECV_BUFSIZE (0x614) + +typedef struct ADI_DMA_CONFIG_REG { + u16 b_DMA_EN:1; /* 0 Enabled */ + u16 b_WNR:1; /* 1 Direction */ + u16 b_WDSIZE:2; /* 2:3 Transfer word size */ + u16 b_DMA2D:1; /* 4 DMA mode */ + u16 b_RESTART:1; /* 5 Retain FIFO */ + u16 b_DI_SEL:1; /* 6 Data interrupt timing select */ + u16 b_DI_EN:1; /* 7 Data interrupt enabled */ + u16 b_NDSIZE:4; /* 8:11 Flex descriptor size */ + u16 b_FLOW:3; /* 12:14Flow */ +} ADI_DMA_CONFIG_REG; + +typedef struct adi_ether_frame_buffer { + u16 NoBytes; /* the no. of following bytes */ + u8 Dest[6]; /* destination MAC address */ + u8 Srce[6]; /* source MAC address */ + u16 LTfield; /* length/type field */ + u8 Data[0]; /* payload bytes */ +} ADI_ETHER_FRAME_BUFFER; +/* 16 bytes/struct */ + +typedef struct dma_descriptor { + struct dma_descriptor *NEXT_DESC_PTR; + u32 START_ADDR; + union { + u16 CONFIG_DATA; + ADI_DMA_CONFIG_REG CONFIG; + }; +} DMA_DESCRIPTOR; +/* 10 bytes/struct in 12 bytes */ + +typedef struct adi_ether_buffer { + DMA_DESCRIPTOR Dma[2]; /* first for the frame, second for the status */ + ADI_ETHER_FRAME_BUFFER *FrmData;/* pointer to data */ + struct adi_ether_buffer *pNext; /* next buffer */ + struct adi_ether_buffer *pPrev; /* prev buffer */ + u16 IPHdrChksum; /* the IP header checksum */ + u16 IPPayloadChksum; /* the IP header and payload checksum */ + volatile u32 StatusWord; /* the frame status word */ +} ADI_ETHER_BUFFER; +/* 40 bytes/struct in 44 bytes */ + +static ADI_ETHER_BUFFER *SetupRxBuffer(int no); +static ADI_ETHER_BUFFER *SetupTxBuffer(int no); + +static int bfin_EMAC_init(struct eth_device *dev, bd_t *bd); +static void bfin_EMAC_halt(struct eth_device *dev); +static int bfin_EMAC_send(struct eth_device *dev, void *packet, int length); +static int bfin_EMAC_recv(struct eth_device *dev); +static int bfin_EMAC_setup_addr(struct eth_device *dev); + +#endif diff --git a/qemu/roms/u-boot/drivers/net/calxedaxgmac.c b/qemu/roms/u-boot/drivers/net/calxedaxgmac.c new file mode 100644 index 000000000..ff94865c5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/calxedaxgmac.c @@ -0,0 +1,544 @@ +/* + * Copyright 2010-2011 Calxeda, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define TX_NUM_DESC 1 +#define RX_NUM_DESC 32 + +#define MAC_TIMEOUT (5*CONFIG_SYS_HZ) + +#define ETH_BUF_SZ 2048 +#define TX_BUF_SZ (ETH_BUF_SZ * TX_NUM_DESC) +#define RX_BUF_SZ (ETH_BUF_SZ * RX_NUM_DESC) + +#define RXSTART 0x00000002 +#define TXSTART 0x00002000 + +#define RXENABLE 0x00000004 +#define TXENABLE 0x00000008 + +#define XGMAC_CONTROL_SPD 0x40000000 +#define XGMAC_CONTROL_SPD_MASK 0x60000000 +#define XGMAC_CONTROL_SARC 0x10000000 +#define XGMAC_CONTROL_SARK_MASK 0x18000000 +#define XGMAC_CONTROL_CAR 0x04000000 +#define XGMAC_CONTROL_CAR_MASK 0x06000000 +#define XGMAC_CONTROL_CAR_SHIFT 25 +#define XGMAC_CONTROL_DP 0x01000000 +#define XGMAC_CONTROL_WD 0x00800000 +#define XGMAC_CONTROL_JD 0x00400000 +#define XGMAC_CONTROL_JE 0x00100000 +#define XGMAC_CONTROL_LM 0x00001000 +#define XGMAC_CONTROL_IPC 0x00000400 +#define XGMAC_CONTROL_ACS 0x00000080 +#define XGMAC_CONTROL_DDIC 0x00000010 +#define XGMAC_CONTROL_TE 0x00000008 +#define XGMAC_CONTROL_RE 0x00000004 + +#define XGMAC_DMA_BUSMODE_RESET 0x00000001 +#define XGMAC_DMA_BUSMODE_DSL 0x00000004 +#define XGMAC_DMA_BUSMODE_DSL_MASK 0x0000007c +#define XGMAC_DMA_BUSMODE_DSL_SHIFT 2 +#define XGMAC_DMA_BUSMODE_ATDS 0x00000080 +#define XGMAC_DMA_BUSMODE_PBL_MASK 0x00003f00 +#define XGMAC_DMA_BUSMODE_PBL_SHIFT 8 +#define XGMAC_DMA_BUSMODE_FB 0x00010000 +#define XGMAC_DMA_BUSMODE_USP 0x00800000 +#define XGMAC_DMA_BUSMODE_8PBL 0x01000000 +#define XGMAC_DMA_BUSMODE_AAL 0x02000000 + +#define XGMAC_DMA_AXIMODE_ENLPI 0x80000000 +#define XGMAC_DMA_AXIMODE_MGK 0x40000000 +#define XGMAC_DMA_AXIMODE_WROSR 0x00100000 +#define XGMAC_DMA_AXIMODE_WROSR_MASK 0x00F00000 +#define XGMAC_DMA_AXIMODE_WROSR_SHIFT 20 +#define XGMAC_DMA_AXIMODE_RDOSR 0x00010000 +#define XGMAC_DMA_AXIMODE_RDOSR_MASK 0x000F0000 +#define XGMAC_DMA_AXIMODE_RDOSR_SHIFT 16 +#define XGMAC_DMA_AXIMODE_AAL 0x00001000 +#define XGMAC_DMA_AXIMODE_BLEN256 0x00000080 +#define XGMAC_DMA_AXIMODE_BLEN128 0x00000040 +#define XGMAC_DMA_AXIMODE_BLEN64 0x00000020 +#define XGMAC_DMA_AXIMODE_BLEN32 0x00000010 +#define XGMAC_DMA_AXIMODE_BLEN16 0x00000008 +#define XGMAC_DMA_AXIMODE_BLEN8 0x00000004 +#define XGMAC_DMA_AXIMODE_BLEN4 0x00000002 +#define XGMAC_DMA_AXIMODE_UNDEF 0x00000001 + +#define XGMAC_CORE_OMR_RTC_SHIFT 3 +#define XGMAC_CORE_OMR_RTC_MASK 0x00000018 +#define XGMAC_CORE_OMR_RTC 0x00000010 +#define XGMAC_CORE_OMR_RSF 0x00000020 +#define XGMAC_CORE_OMR_DT 0x00000040 +#define XGMAC_CORE_OMR_FEF 0x00000080 +#define XGMAC_CORE_OMR_EFC 0x00000100 +#define XGMAC_CORE_OMR_RFA_SHIFT 9 +#define XGMAC_CORE_OMR_RFA_MASK 0x00000E00 +#define XGMAC_CORE_OMR_RFD_SHIFT 12 +#define XGMAC_CORE_OMR_RFD_MASK 0x00007000 +#define XGMAC_CORE_OMR_TTC_SHIFT 16 +#define XGMAC_CORE_OMR_TTC_MASK 0x00030000 +#define XGMAC_CORE_OMR_TTC 0x00020000 +#define XGMAC_CORE_OMR_FTF 0x00100000 +#define XGMAC_CORE_OMR_TSF 0x00200000 + +#define FIFO_MINUS_1K 0x0 +#define FIFO_MINUS_2K 0x1 +#define FIFO_MINUS_3K 0x2 +#define FIFO_MINUS_4K 0x3 +#define FIFO_MINUS_6K 0x4 +#define FIFO_MINUS_8K 0x5 +#define FIFO_MINUS_12K 0x6 +#define FIFO_MINUS_16K 0x7 + +#define XGMAC_CORE_FLOW_PT_SHIFT 16 +#define XGMAC_CORE_FLOW_PT_MASK 0xFFFF0000 +#define XGMAC_CORE_FLOW_PT 0x00010000 +#define XGMAC_CORE_FLOW_DZQP 0x00000080 +#define XGMAC_CORE_FLOW_PLT_SHIFT 4 +#define XGMAC_CORE_FLOW_PLT_MASK 0x00000030 +#define XGMAC_CORE_FLOW_PLT 0x00000010 +#define XGMAC_CORE_FLOW_UP 0x00000008 +#define XGMAC_CORE_FLOW_RFE 0x00000004 +#define XGMAC_CORE_FLOW_TFE 0x00000002 +#define XGMAC_CORE_FLOW_FCB 0x00000001 + +/* XGMAC Descriptor Defines */ +#define MAX_DESC_BUF_SZ (0x2000 - 8) + +#define RXDESC_EXT_STATUS 0x00000001 +#define RXDESC_CRC_ERR 0x00000002 +#define RXDESC_RX_ERR 0x00000008 +#define RXDESC_RX_WDOG 0x00000010 +#define RXDESC_FRAME_TYPE 0x00000020 +#define RXDESC_GIANT_FRAME 0x00000080 +#define RXDESC_LAST_SEG 0x00000100 +#define RXDESC_FIRST_SEG 0x00000200 +#define RXDESC_VLAN_FRAME 0x00000400 +#define RXDESC_OVERFLOW_ERR 0x00000800 +#define RXDESC_LENGTH_ERR 0x00001000 +#define RXDESC_SA_FILTER_FAIL 0x00002000 +#define RXDESC_DESCRIPTOR_ERR 0x00004000 +#define RXDESC_ERROR_SUMMARY 0x00008000 +#define RXDESC_FRAME_LEN_OFFSET 16 +#define RXDESC_FRAME_LEN_MASK 0x3fff0000 +#define RXDESC_DA_FILTER_FAIL 0x40000000 + +#define RXDESC1_END_RING 0x00008000 + +#define RXDESC_IP_PAYLOAD_MASK 0x00000003 +#define RXDESC_IP_PAYLOAD_UDP 0x00000001 +#define RXDESC_IP_PAYLOAD_TCP 0x00000002 +#define RXDESC_IP_PAYLOAD_ICMP 0x00000003 +#define RXDESC_IP_HEADER_ERR 0x00000008 +#define RXDESC_IP_PAYLOAD_ERR 0x00000010 +#define RXDESC_IPV4_PACKET 0x00000040 +#define RXDESC_IPV6_PACKET 0x00000080 +#define TXDESC_UNDERFLOW_ERR 0x00000001 +#define TXDESC_JABBER_TIMEOUT 0x00000002 +#define TXDESC_LOCAL_FAULT 0x00000004 +#define TXDESC_REMOTE_FAULT 0x00000008 +#define TXDESC_VLAN_FRAME 0x00000010 +#define TXDESC_FRAME_FLUSHED 0x00000020 +#define TXDESC_IP_HEADER_ERR 0x00000040 +#define TXDESC_PAYLOAD_CSUM_ERR 0x00000080 +#define TXDESC_ERROR_SUMMARY 0x00008000 +#define TXDESC_SA_CTRL_INSERT 0x00040000 +#define TXDESC_SA_CTRL_REPLACE 0x00080000 +#define TXDESC_2ND_ADDR_CHAINED 0x00100000 +#define TXDESC_END_RING 0x00200000 +#define TXDESC_CSUM_IP 0x00400000 +#define TXDESC_CSUM_IP_PAYLD 0x00800000 +#define TXDESC_CSUM_ALL 0x00C00000 +#define TXDESC_CRC_EN_REPLACE 0x01000000 +#define TXDESC_CRC_EN_APPEND 0x02000000 +#define TXDESC_DISABLE_PAD 0x04000000 +#define TXDESC_FIRST_SEG 0x10000000 +#define TXDESC_LAST_SEG 0x20000000 +#define TXDESC_INTERRUPT 0x40000000 + +#define DESC_OWN 0x80000000 +#define DESC_BUFFER1_SZ_MASK 0x00001fff +#define DESC_BUFFER2_SZ_MASK 0x1fff0000 +#define DESC_BUFFER2_SZ_OFFSET 16 + +struct xgmac_regs { + u32 config; + u32 framefilter; + u32 resv_1[4]; + u32 flow_control; + u32 vlantag; + u32 version; + u32 vlaninclude; + u32 resv_2[2]; + u32 pacestretch; + u32 vlanhash; + u32 resv_3; + u32 intreg; + struct { + u32 hi; /* 0x40 */ + u32 lo; /* 0x44 */ + } macaddr[16]; + u32 resv_4[0xd0]; + u32 core_opmode; /* 0x400 */ + u32 resv_5[0x2bf]; + u32 busmode; /* 0xf00 */ + u32 txpoll; + u32 rxpoll; + u32 rxdesclist; + u32 txdesclist; + u32 dma_status; + u32 dma_opmode; + u32 intenable; + u32 resv_6[2]; + u32 axi_mode; /* 0xf28 */ +}; + +struct xgmac_dma_desc { + __le32 flags; + __le32 buf_size; + __le32 buf1_addr; /* Buffer 1 Address Pointer */ + __le32 buf2_addr; /* Buffer 2 Address Pointer */ + __le32 ext_status; + __le32 res[3]; +}; + +/* XGMAC Descriptor Access Helpers */ +static inline void desc_set_buf_len(struct xgmac_dma_desc *p, u32 buf_sz) +{ + if (buf_sz > MAX_DESC_BUF_SZ) + p->buf_size = cpu_to_le32(MAX_DESC_BUF_SZ | + (buf_sz - MAX_DESC_BUF_SZ) << DESC_BUFFER2_SZ_OFFSET); + else + p->buf_size = cpu_to_le32(buf_sz); +} + +static inline int desc_get_buf_len(struct xgmac_dma_desc *p) +{ + u32 len = le32_to_cpu(p->buf_size); + return (len & DESC_BUFFER1_SZ_MASK) + + ((len & DESC_BUFFER2_SZ_MASK) >> DESC_BUFFER2_SZ_OFFSET); +} + +static inline void desc_init_rx_desc(struct xgmac_dma_desc *p, int ring_size, + int buf_sz) +{ + struct xgmac_dma_desc *end = p + ring_size - 1; + + memset(p, 0, sizeof(*p) * ring_size); + + for (; p <= end; p++) + desc_set_buf_len(p, buf_sz); + + end->buf_size |= cpu_to_le32(RXDESC1_END_RING); +} + +static inline void desc_init_tx_desc(struct xgmac_dma_desc *p, u32 ring_size) +{ + memset(p, 0, sizeof(*p) * ring_size); + p[ring_size - 1].flags = cpu_to_le32(TXDESC_END_RING); +} + +static inline int desc_get_owner(struct xgmac_dma_desc *p) +{ + return le32_to_cpu(p->flags) & DESC_OWN; +} + +static inline void desc_set_rx_owner(struct xgmac_dma_desc *p) +{ + /* Clear all fields and set the owner */ + p->flags = cpu_to_le32(DESC_OWN); +} + +static inline void desc_set_tx_owner(struct xgmac_dma_desc *p, u32 flags) +{ + u32 tmpflags = le32_to_cpu(p->flags); + tmpflags &= TXDESC_END_RING; + tmpflags |= flags | DESC_OWN; + p->flags = cpu_to_le32(tmpflags); +} + +static inline void *desc_get_buf_addr(struct xgmac_dma_desc *p) +{ + return (void *)le32_to_cpu(p->buf1_addr); +} + +static inline void desc_set_buf_addr(struct xgmac_dma_desc *p, + void *paddr, int len) +{ + p->buf1_addr = cpu_to_le32(paddr); + if (len > MAX_DESC_BUF_SZ) + p->buf2_addr = cpu_to_le32(paddr + MAX_DESC_BUF_SZ); +} + +static inline void desc_set_buf_addr_and_size(struct xgmac_dma_desc *p, + void *paddr, int len) +{ + desc_set_buf_len(p, len); + desc_set_buf_addr(p, paddr, len); +} + +static inline int desc_get_rx_frame_len(struct xgmac_dma_desc *p) +{ + u32 data = le32_to_cpu(p->flags); + u32 len = (data & RXDESC_FRAME_LEN_MASK) >> RXDESC_FRAME_LEN_OFFSET; + if (data & RXDESC_FRAME_TYPE) + len -= 4; + + return len; +} + +struct calxeda_eth_dev { + struct xgmac_dma_desc rx_chain[RX_NUM_DESC]; + struct xgmac_dma_desc tx_chain[TX_NUM_DESC]; + char rxbuffer[RX_BUF_SZ]; + + u32 tx_currdesc; + u32 rx_currdesc; + + struct eth_device *dev; +} __aligned(32); + +/* + * Initialize a descriptor ring. Calxeda XGMAC is configured to use + * advanced descriptors. + */ + +static void init_rx_desc(struct calxeda_eth_dev *priv) +{ + struct xgmac_dma_desc *rxdesc = priv->rx_chain; + struct xgmac_regs *regs = (struct xgmac_regs *)priv->dev->iobase; + void *rxbuffer = priv->rxbuffer; + int i; + + desc_init_rx_desc(rxdesc, RX_NUM_DESC, ETH_BUF_SZ); + writel((ulong)rxdesc, ®s->rxdesclist); + + for (i = 0; i < RX_NUM_DESC; i++) { + desc_set_buf_addr(rxdesc + i, rxbuffer + (i * ETH_BUF_SZ), + ETH_BUF_SZ); + desc_set_rx_owner(rxdesc + i); + } +} + +static void init_tx_desc(struct calxeda_eth_dev *priv) +{ + struct xgmac_regs *regs = (struct xgmac_regs *)priv->dev->iobase; + + desc_init_tx_desc(priv->tx_chain, TX_NUM_DESC); + writel((ulong)priv->tx_chain, ®s->txdesclist); +} + +static int xgmac_reset(struct eth_device *dev) +{ + struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase; + int timeout = MAC_TIMEOUT; + u32 value; + + value = readl(®s->config) & XGMAC_CONTROL_SPD_MASK; + + writel(XGMAC_DMA_BUSMODE_RESET, ®s->busmode); + while ((timeout-- >= 0) && + (readl(®s->busmode) & XGMAC_DMA_BUSMODE_RESET)) + udelay(1); + + writel(value, ®s->config); + + return timeout; +} + +static void xgmac_hwmacaddr(struct eth_device *dev) +{ + struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase; + u32 macaddr[2]; + + memcpy(macaddr, dev->enetaddr, 6); + writel(macaddr[1], ®s->macaddr[0].hi); + writel(macaddr[0], ®s->macaddr[0].lo); +} + +static int xgmac_init(struct eth_device *dev, bd_t * bis) +{ + struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase; + struct calxeda_eth_dev *priv = dev->priv; + int value; + + if (xgmac_reset(dev) < 0) + return -1; + + /* set the hardware MAC address */ + xgmac_hwmacaddr(dev); + + /* set the AXI bus modes */ + value = XGMAC_DMA_BUSMODE_ATDS | + (16 << XGMAC_DMA_BUSMODE_PBL_SHIFT) | + XGMAC_DMA_BUSMODE_FB | XGMAC_DMA_BUSMODE_AAL; + writel(value, ®s->busmode); + + value = XGMAC_DMA_AXIMODE_AAL | XGMAC_DMA_AXIMODE_BLEN16 | + XGMAC_DMA_AXIMODE_BLEN8 | XGMAC_DMA_AXIMODE_BLEN4; + writel(value, ®s->axi_mode); + + /* set flow control parameters and store and forward mode */ + value = (FIFO_MINUS_12K << XGMAC_CORE_OMR_RFD_SHIFT) | + (FIFO_MINUS_4K << XGMAC_CORE_OMR_RFA_SHIFT) | + XGMAC_CORE_OMR_EFC | XGMAC_CORE_OMR_TSF; + writel(value, ®s->core_opmode); + + /* enable pause frames */ + value = (1024 << XGMAC_CORE_FLOW_PT_SHIFT) | + (1 << XGMAC_CORE_FLOW_PLT_SHIFT) | + XGMAC_CORE_FLOW_UP | XGMAC_CORE_FLOW_RFE | XGMAC_CORE_FLOW_TFE; + writel(value, ®s->flow_control); + + /* Initialize the descriptor chains */ + init_rx_desc(priv); + init_tx_desc(priv); + + /* must set to 0, or when started up will cause issues */ + priv->tx_currdesc = 0; + priv->rx_currdesc = 0; + + /* set default core values */ + value = readl(®s->config); + value &= XGMAC_CONTROL_SPD_MASK; + value |= XGMAC_CONTROL_DDIC | XGMAC_CONTROL_ACS | + XGMAC_CONTROL_IPC | XGMAC_CONTROL_CAR; + + /* Everything is ready enable both mac and DMA */ + value |= RXENABLE | TXENABLE; + writel(value, ®s->config); + + value = readl(®s->dma_opmode); + value |= RXSTART | TXSTART; + writel(value, ®s->dma_opmode); + + return 0; +} + +static int xgmac_tx(struct eth_device *dev, void *packet, int length) +{ + struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase; + struct calxeda_eth_dev *priv = dev->priv; + u32 currdesc = priv->tx_currdesc; + struct xgmac_dma_desc *txdesc = &priv->tx_chain[currdesc]; + int timeout; + + desc_set_buf_addr_and_size(txdesc, packet, length); + desc_set_tx_owner(txdesc, TXDESC_FIRST_SEG | + TXDESC_LAST_SEG | TXDESC_CRC_EN_APPEND); + + /* write poll demand */ + writel(1, ®s->txpoll); + + timeout = 1000000; + while (desc_get_owner(txdesc)) { + if (timeout-- < 0) { + printf("xgmac: TX timeout\n"); + return -ETIMEDOUT; + } + udelay(1); + } + + priv->tx_currdesc = (currdesc + 1) & (TX_NUM_DESC - 1); + return 0; +} + +static int xgmac_rx(struct eth_device *dev) +{ + struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase; + struct calxeda_eth_dev *priv = dev->priv; + u32 currdesc = priv->rx_currdesc; + struct xgmac_dma_desc *rxdesc = &priv->rx_chain[currdesc]; + int length = 0; + + /* check if the host has the desc */ + if (desc_get_owner(rxdesc)) + return -1; /* something bad happened */ + + length = desc_get_rx_frame_len(rxdesc); + + NetReceive(desc_get_buf_addr(rxdesc), length); + + /* set descriptor back to owned by XGMAC */ + desc_set_rx_owner(rxdesc); + writel(1, ®s->rxpoll); + + priv->rx_currdesc = (currdesc + 1) & (RX_NUM_DESC - 1); + + return length; +} + +static void xgmac_halt(struct eth_device *dev) +{ + struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase; + struct calxeda_eth_dev *priv = dev->priv; + int value; + + /* Disable TX/RX */ + value = readl(®s->config); + value &= ~(RXENABLE | TXENABLE); + writel(value, ®s->config); + + /* Disable DMA */ + value = readl(®s->dma_opmode); + value &= ~(RXSTART | TXSTART); + writel(value, ®s->dma_opmode); + + /* must set to 0, or when started up will cause issues */ + priv->tx_currdesc = 0; + priv->rx_currdesc = 0; +} + +int calxedaxgmac_initialize(u32 id, ulong base_addr) +{ + struct eth_device *dev; + struct calxeda_eth_dev *priv; + struct xgmac_regs *regs; + u32 macaddr[2]; + + regs = (struct xgmac_regs *)base_addr; + + /* check hardware version */ + if (readl(®s->version) != 0x1012) + return -1; + + dev = malloc(sizeof(*dev)); + if (!dev) + return 0; + memset(dev, 0, sizeof(*dev)); + + /* Structure must be aligned, because it contains the descriptors */ + priv = memalign(32, sizeof(*priv)); + if (!priv) { + free(dev); + return 0; + } + + dev->iobase = (int)base_addr; + dev->priv = priv; + priv->dev = dev; + sprintf(dev->name, "xgmac%d", id); + + /* The MAC address is already configured, so read it from registers. */ + macaddr[1] = readl(®s->macaddr[0].hi); + macaddr[0] = readl(®s->macaddr[0].lo); + memcpy(dev->enetaddr, macaddr, 6); + + dev->init = xgmac_init; + dev->send = xgmac_tx; + dev->recv = xgmac_rx; + dev->halt = xgmac_halt; + + eth_register(dev); + + return 1; +} diff --git a/qemu/roms/u-boot/drivers/net/cpsw.c b/qemu/roms/u-boot/drivers/net/cpsw.c new file mode 100644 index 000000000..bd5fba21c --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/cpsw.c @@ -0,0 +1,1020 @@ +/* + * CPSW Ethernet Switch Driver + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BITMASK(bits) (BIT(bits) - 1) +#define PHY_REG_MASK 0x1f +#define PHY_ID_MASK 0x1f +#define NUM_DESCS (PKTBUFSRX * 2) +#define PKT_MIN 60 +#define PKT_MAX (1500 + 14 + 4 + 4) +#define CLEAR_BIT 1 +#define GIGABITEN BIT(7) +#define FULLDUPLEXEN BIT(0) +#define MIIEN BIT(15) + +/* DMA Registers */ +#define CPDMA_TXCONTROL 0x004 +#define CPDMA_RXCONTROL 0x014 +#define CPDMA_SOFTRESET 0x01c +#define CPDMA_RXFREE 0x0e0 +#define CPDMA_TXHDP_VER1 0x100 +#define CPDMA_TXHDP_VER2 0x200 +#define CPDMA_RXHDP_VER1 0x120 +#define CPDMA_RXHDP_VER2 0x220 +#define CPDMA_TXCP_VER1 0x140 +#define CPDMA_TXCP_VER2 0x240 +#define CPDMA_RXCP_VER1 0x160 +#define CPDMA_RXCP_VER2 0x260 + +/* Descriptor mode bits */ +#define CPDMA_DESC_SOP BIT(31) +#define CPDMA_DESC_EOP BIT(30) +#define CPDMA_DESC_OWNER BIT(29) +#define CPDMA_DESC_EOQ BIT(28) + +/* + * This timeout definition is a worst-case ultra defensive measure against + * unexpected controller lock ups. Ideally, we should never ever hit this + * scenario in practice. + */ +#define MDIO_TIMEOUT 100 /* msecs */ +#define CPDMA_TIMEOUT 100 /* msecs */ + +struct cpsw_mdio_regs { + u32 version; + u32 control; +#define CONTROL_IDLE BIT(31) +#define CONTROL_ENABLE BIT(30) + + u32 alive; + u32 link; + u32 linkintraw; + u32 linkintmasked; + u32 __reserved_0[2]; + u32 userintraw; + u32 userintmasked; + u32 userintmaskset; + u32 userintmaskclr; + u32 __reserved_1[20]; + + struct { + u32 access; + u32 physel; +#define USERACCESS_GO BIT(31) +#define USERACCESS_WRITE BIT(30) +#define USERACCESS_ACK BIT(29) +#define USERACCESS_READ (0) +#define USERACCESS_DATA (0xffff) + } user[0]; +}; + +struct cpsw_regs { + u32 id_ver; + u32 control; + u32 soft_reset; + u32 stat_port_en; + u32 ptype; +}; + +struct cpsw_slave_regs { + u32 max_blks; + u32 blk_cnt; + u32 flow_thresh; + u32 port_vlan; + u32 tx_pri_map; +#ifdef CONFIG_AM33XX + u32 gap_thresh; +#elif defined(CONFIG_TI814X) + u32 ts_ctl; + u32 ts_seq_ltype; + u32 ts_vlan; +#endif + u32 sa_lo; + u32 sa_hi; +}; + +struct cpsw_host_regs { + u32 max_blks; + u32 blk_cnt; + u32 flow_thresh; + u32 port_vlan; + u32 tx_pri_map; + u32 cpdma_tx_pri_map; + u32 cpdma_rx_chan_map; +}; + +struct cpsw_sliver_regs { + u32 id_ver; + u32 mac_control; + u32 mac_status; + u32 soft_reset; + u32 rx_maxlen; + u32 __reserved_0; + u32 rx_pause; + u32 tx_pause; + u32 __reserved_1; + u32 rx_pri_map; +}; + +#define ALE_ENTRY_BITS 68 +#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32) + +/* ALE Registers */ +#define ALE_CONTROL 0x08 +#define ALE_UNKNOWNVLAN 0x18 +#define ALE_TABLE_CONTROL 0x20 +#define ALE_TABLE 0x34 +#define ALE_PORTCTL 0x40 + +#define ALE_TABLE_WRITE BIT(31) + +#define ALE_TYPE_FREE 0 +#define ALE_TYPE_ADDR 1 +#define ALE_TYPE_VLAN 2 +#define ALE_TYPE_VLAN_ADDR 3 + +#define ALE_UCAST_PERSISTANT 0 +#define ALE_UCAST_UNTOUCHED 1 +#define ALE_UCAST_OUI 2 +#define ALE_UCAST_TOUCHED 3 + +#define ALE_MCAST_FWD 0 +#define ALE_MCAST_BLOCK_LEARN_FWD 1 +#define ALE_MCAST_FWD_LEARN 2 +#define ALE_MCAST_FWD_2 3 + +enum cpsw_ale_port_state { + ALE_PORT_STATE_DISABLE = 0x00, + ALE_PORT_STATE_BLOCK = 0x01, + ALE_PORT_STATE_LEARN = 0x02, + ALE_PORT_STATE_FORWARD = 0x03, +}; + +/* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */ +#define ALE_SECURE 1 +#define ALE_BLOCKED 2 + +struct cpsw_slave { + struct cpsw_slave_regs *regs; + struct cpsw_sliver_regs *sliver; + int slave_num; + u32 mac_control; + struct cpsw_slave_data *data; +}; + +struct cpdma_desc { + /* hardware fields */ + u32 hw_next; + u32 hw_buffer; + u32 hw_len; + u32 hw_mode; + /* software fields */ + u32 sw_buffer; + u32 sw_len; +}; + +struct cpdma_chan { + struct cpdma_desc *head, *tail; + void *hdp, *cp, *rxfree; +}; + +#define desc_write(desc, fld, val) __raw_writel((u32)(val), &(desc)->fld) +#define desc_read(desc, fld) __raw_readl(&(desc)->fld) +#define desc_read_ptr(desc, fld) ((void *)__raw_readl(&(desc)->fld)) + +#define chan_write(chan, fld, val) __raw_writel((u32)(val), (chan)->fld) +#define chan_read(chan, fld) __raw_readl((chan)->fld) +#define chan_read_ptr(chan, fld) ((void *)__raw_readl((chan)->fld)) + +#define for_each_slave(slave, priv) \ + for (slave = (priv)->slaves; slave != (priv)->slaves + \ + (priv)->data.slaves; slave++) + +struct cpsw_priv { + struct eth_device *dev; + struct cpsw_platform_data data; + int host_port; + + struct cpsw_regs *regs; + void *dma_regs; + struct cpsw_host_regs *host_port_regs; + void *ale_regs; + + struct cpdma_desc *descs; + struct cpdma_desc *desc_free; + struct cpdma_chan rx_chan, tx_chan; + + struct cpsw_slave *slaves; + struct phy_device *phydev; + struct mii_dev *bus; + + u32 mdio_link; + u32 phy_mask; +}; + +static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits) +{ + int idx; + + idx = start / 32; + start -= idx * 32; + idx = 2 - idx; /* flip */ + return (ale_entry[idx] >> start) & BITMASK(bits); +} + +static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits, + u32 value) +{ + int idx; + + value &= BITMASK(bits); + idx = start / 32; + start -= idx * 32; + idx = 2 - idx; /* flip */ + ale_entry[idx] &= ~(BITMASK(bits) << start); + ale_entry[idx] |= (value << start); +} + +#define DEFINE_ALE_FIELD(name, start, bits) \ +static inline int cpsw_ale_get_##name(u32 *ale_entry) \ +{ \ + return cpsw_ale_get_field(ale_entry, start, bits); \ +} \ +static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \ +{ \ + cpsw_ale_set_field(ale_entry, start, bits, value); \ +} + +DEFINE_ALE_FIELD(entry_type, 60, 2) +DEFINE_ALE_FIELD(mcast_state, 62, 2) +DEFINE_ALE_FIELD(port_mask, 66, 3) +DEFINE_ALE_FIELD(ucast_type, 62, 2) +DEFINE_ALE_FIELD(port_num, 66, 2) +DEFINE_ALE_FIELD(blocked, 65, 1) +DEFINE_ALE_FIELD(secure, 64, 1) +DEFINE_ALE_FIELD(mcast, 40, 1) + +/* The MAC address field in the ALE entry cannot be macroized as above */ +static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr) +{ + int i; + + for (i = 0; i < 6; i++) + addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8); +} + +static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr) +{ + int i; + + for (i = 0; i < 6; i++) + cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]); +} + +static int cpsw_ale_read(struct cpsw_priv *priv, int idx, u32 *ale_entry) +{ + int i; + + __raw_writel(idx, priv->ale_regs + ALE_TABLE_CONTROL); + + for (i = 0; i < ALE_ENTRY_WORDS; i++) + ale_entry[i] = __raw_readl(priv->ale_regs + ALE_TABLE + 4 * i); + + return idx; +} + +static int cpsw_ale_write(struct cpsw_priv *priv, int idx, u32 *ale_entry) +{ + int i; + + for (i = 0; i < ALE_ENTRY_WORDS; i++) + __raw_writel(ale_entry[i], priv->ale_regs + ALE_TABLE + 4 * i); + + __raw_writel(idx | ALE_TABLE_WRITE, priv->ale_regs + ALE_TABLE_CONTROL); + + return idx; +} + +static int cpsw_ale_match_addr(struct cpsw_priv *priv, u8* addr) +{ + u32 ale_entry[ALE_ENTRY_WORDS]; + int type, idx; + + for (idx = 0; idx < priv->data.ale_entries; idx++) { + u8 entry_addr[6]; + + cpsw_ale_read(priv, idx, ale_entry); + type = cpsw_ale_get_entry_type(ale_entry); + if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) + continue; + cpsw_ale_get_addr(ale_entry, entry_addr); + if (memcmp(entry_addr, addr, 6) == 0) + return idx; + } + return -ENOENT; +} + +static int cpsw_ale_match_free(struct cpsw_priv *priv) +{ + u32 ale_entry[ALE_ENTRY_WORDS]; + int type, idx; + + for (idx = 0; idx < priv->data.ale_entries; idx++) { + cpsw_ale_read(priv, idx, ale_entry); + type = cpsw_ale_get_entry_type(ale_entry); + if (type == ALE_TYPE_FREE) + return idx; + } + return -ENOENT; +} + +static int cpsw_ale_find_ageable(struct cpsw_priv *priv) +{ + u32 ale_entry[ALE_ENTRY_WORDS]; + int type, idx; + + for (idx = 0; idx < priv->data.ale_entries; idx++) { + cpsw_ale_read(priv, idx, ale_entry); + type = cpsw_ale_get_entry_type(ale_entry); + if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) + continue; + if (cpsw_ale_get_mcast(ale_entry)) + continue; + type = cpsw_ale_get_ucast_type(ale_entry); + if (type != ALE_UCAST_PERSISTANT && + type != ALE_UCAST_OUI) + return idx; + } + return -ENOENT; +} + +static int cpsw_ale_add_ucast(struct cpsw_priv *priv, u8 *addr, + int port, int flags) +{ + u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int idx; + + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + cpsw_ale_set_addr(ale_entry, addr); + cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT); + cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0); + cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0); + cpsw_ale_set_port_num(ale_entry, port); + + idx = cpsw_ale_match_addr(priv, addr); + if (idx < 0) + idx = cpsw_ale_match_free(priv); + if (idx < 0) + idx = cpsw_ale_find_ageable(priv); + if (idx < 0) + return -ENOMEM; + + cpsw_ale_write(priv, idx, ale_entry); + return 0; +} + +static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask) +{ + u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int idx, mask; + + idx = cpsw_ale_match_addr(priv, addr); + if (idx >= 0) + cpsw_ale_read(priv, idx, ale_entry); + + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + cpsw_ale_set_addr(ale_entry, addr); + cpsw_ale_set_mcast_state(ale_entry, ALE_MCAST_FWD_2); + + mask = cpsw_ale_get_port_mask(ale_entry); + port_mask |= mask; + cpsw_ale_set_port_mask(ale_entry, port_mask); + + if (idx < 0) + idx = cpsw_ale_match_free(priv); + if (idx < 0) + idx = cpsw_ale_find_ageable(priv); + if (idx < 0) + return -ENOMEM; + + cpsw_ale_write(priv, idx, ale_entry); + return 0; +} + +static inline void cpsw_ale_control(struct cpsw_priv *priv, int bit, int val) +{ + u32 tmp, mask = BIT(bit); + + tmp = __raw_readl(priv->ale_regs + ALE_CONTROL); + tmp &= ~mask; + tmp |= val ? mask : 0; + __raw_writel(tmp, priv->ale_regs + ALE_CONTROL); +} + +#define cpsw_ale_enable(priv, val) cpsw_ale_control(priv, 31, val) +#define cpsw_ale_clear(priv, val) cpsw_ale_control(priv, 30, val) +#define cpsw_ale_vlan_aware(priv, val) cpsw_ale_control(priv, 2, val) + +static inline void cpsw_ale_port_state(struct cpsw_priv *priv, int port, + int val) +{ + int offset = ALE_PORTCTL + 4 * port; + u32 tmp, mask = 0x3; + + tmp = __raw_readl(priv->ale_regs + offset); + tmp &= ~mask; + tmp |= val & mask; + __raw_writel(tmp, priv->ale_regs + offset); +} + +static struct cpsw_mdio_regs *mdio_regs; + +/* wait until hardware is ready for another user access */ +static inline u32 wait_for_user_access(void) +{ + u32 reg = 0; + int timeout = MDIO_TIMEOUT; + + while (timeout-- && + ((reg = __raw_readl(&mdio_regs->user[0].access)) & USERACCESS_GO)) + udelay(10); + + if (timeout == -1) { + printf("wait_for_user_access Timeout\n"); + return -ETIMEDOUT; + } + return reg; +} + +/* wait until hardware state machine is idle */ +static inline void wait_for_idle(void) +{ + int timeout = MDIO_TIMEOUT; + + while (timeout-- && + ((__raw_readl(&mdio_regs->control) & CONTROL_IDLE) == 0)) + udelay(10); + + if (timeout == -1) + printf("wait_for_idle Timeout\n"); +} + +static int cpsw_mdio_read(struct mii_dev *bus, int phy_id, + int dev_addr, int phy_reg) +{ + int data; + u32 reg; + + if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) + return -EINVAL; + + wait_for_user_access(); + reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) | + (phy_id << 16)); + __raw_writel(reg, &mdio_regs->user[0].access); + reg = wait_for_user_access(); + + data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1; + return data; +} + +static int cpsw_mdio_write(struct mii_dev *bus, int phy_id, int dev_addr, + int phy_reg, u16 data) +{ + u32 reg; + + if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) + return -EINVAL; + + wait_for_user_access(); + reg = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) | + (phy_id << 16) | (data & USERACCESS_DATA)); + __raw_writel(reg, &mdio_regs->user[0].access); + wait_for_user_access(); + + return 0; +} + +static void cpsw_mdio_init(char *name, u32 mdio_base, u32 div) +{ + struct mii_dev *bus = mdio_alloc(); + + mdio_regs = (struct cpsw_mdio_regs *)mdio_base; + + /* set enable and clock divider */ + __raw_writel(div | CONTROL_ENABLE, &mdio_regs->control); + + /* + * wait for scan logic to settle: + * the scan time consists of (a) a large fixed component, and (b) a + * small component that varies with the mii bus frequency. These + * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x + * silicon. Since the effect of (b) was found to be largely + * negligible, we keep things simple here. + */ + udelay(1000); + + bus->read = cpsw_mdio_read; + bus->write = cpsw_mdio_write; + sprintf(bus->name, name); + + mdio_register(bus); +} + +/* Set a self-clearing bit in a register, and wait for it to clear */ +static inline void setbit_and_wait_for_clear32(void *addr) +{ + __raw_writel(CLEAR_BIT, addr); + while (__raw_readl(addr) & CLEAR_BIT) + ; +} + +#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ + ((mac)[2] << 16) | ((mac)[3] << 24)) +#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) + +static void cpsw_set_slave_mac(struct cpsw_slave *slave, + struct cpsw_priv *priv) +{ + __raw_writel(mac_hi(priv->dev->enetaddr), &slave->regs->sa_hi); + __raw_writel(mac_lo(priv->dev->enetaddr), &slave->regs->sa_lo); +} + +static void cpsw_slave_update_link(struct cpsw_slave *slave, + struct cpsw_priv *priv, int *link) +{ + struct phy_device *phy; + u32 mac_control = 0; + + phy = priv->phydev; + + if (!phy) + return; + + phy_startup(phy); + *link = phy->link; + + if (*link) { /* link up */ + mac_control = priv->data.mac_control; + if (phy->speed == 1000) + mac_control |= GIGABITEN; + if (phy->duplex == DUPLEX_FULL) + mac_control |= FULLDUPLEXEN; + if (phy->speed == 100) + mac_control |= MIIEN; + } + + if (mac_control == slave->mac_control) + return; + + if (mac_control) { + printf("link up on port %d, speed %d, %s duplex\n", + slave->slave_num, phy->speed, + (phy->duplex == DUPLEX_FULL) ? "full" : "half"); + } else { + printf("link down on port %d\n", slave->slave_num); + } + + __raw_writel(mac_control, &slave->sliver->mac_control); + slave->mac_control = mac_control; +} + +static int cpsw_update_link(struct cpsw_priv *priv) +{ + int link = 0; + struct cpsw_slave *slave; + + for_each_slave(slave, priv) + cpsw_slave_update_link(slave, priv, &link); + priv->mdio_link = readl(&mdio_regs->link); + return link; +} + +static int cpsw_check_link(struct cpsw_priv *priv) +{ + u32 link = 0; + + link = __raw_readl(&mdio_regs->link) & priv->phy_mask; + if ((link) && (link == priv->mdio_link)) + return 1; + + return cpsw_update_link(priv); +} + +static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) +{ + if (priv->host_port == 0) + return slave_num + 1; + else + return slave_num; +} + +static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) +{ + u32 slave_port; + + setbit_and_wait_for_clear32(&slave->sliver->soft_reset); + + /* setup priority mapping */ + __raw_writel(0x76543210, &slave->sliver->rx_pri_map); + __raw_writel(0x33221100, &slave->regs->tx_pri_map); + + /* setup max packet size, and mac address */ + __raw_writel(PKT_MAX, &slave->sliver->rx_maxlen); + cpsw_set_slave_mac(slave, priv); + + slave->mac_control = 0; /* no link yet */ + + /* enable forwarding */ + slave_port = cpsw_get_slave_port(priv, slave->slave_num); + cpsw_ale_port_state(priv, slave_port, ALE_PORT_STATE_FORWARD); + + cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << slave_port); + + priv->phy_mask |= 1 << slave->data->phy_addr; +} + +static struct cpdma_desc *cpdma_desc_alloc(struct cpsw_priv *priv) +{ + struct cpdma_desc *desc = priv->desc_free; + + if (desc) + priv->desc_free = desc_read_ptr(desc, hw_next); + return desc; +} + +static void cpdma_desc_free(struct cpsw_priv *priv, struct cpdma_desc *desc) +{ + if (desc) { + desc_write(desc, hw_next, priv->desc_free); + priv->desc_free = desc; + } +} + +static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, + void *buffer, int len) +{ + struct cpdma_desc *desc, *prev; + u32 mode; + + desc = cpdma_desc_alloc(priv); + if (!desc) + return -ENOMEM; + + if (len < PKT_MIN) + len = PKT_MIN; + + mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; + + desc_write(desc, hw_next, 0); + desc_write(desc, hw_buffer, buffer); + desc_write(desc, hw_len, len); + desc_write(desc, hw_mode, mode | len); + desc_write(desc, sw_buffer, buffer); + desc_write(desc, sw_len, len); + + if (!chan->head) { + /* simple case - first packet enqueued */ + chan->head = desc; + chan->tail = desc; + chan_write(chan, hdp, desc); + goto done; + } + + /* not the first packet - enqueue at the tail */ + prev = chan->tail; + desc_write(prev, hw_next, desc); + chan->tail = desc; + + /* next check if EOQ has been triggered already */ + if (desc_read(prev, hw_mode) & CPDMA_DESC_EOQ) + chan_write(chan, hdp, desc); + +done: + if (chan->rxfree) + chan_write(chan, rxfree, 1); + return 0; +} + +static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan, + void **buffer, int *len) +{ + struct cpdma_desc *desc = chan->head; + u32 status; + + if (!desc) + return -ENOENT; + + status = desc_read(desc, hw_mode); + + if (len) + *len = status & 0x7ff; + + if (buffer) + *buffer = desc_read_ptr(desc, sw_buffer); + + if (status & CPDMA_DESC_OWNER) { + if (chan_read(chan, hdp) == 0) { + if (desc_read(desc, hw_mode) & CPDMA_DESC_OWNER) + chan_write(chan, hdp, desc); + } + + return -EBUSY; + } + + chan->head = desc_read_ptr(desc, hw_next); + chan_write(chan, cp, desc); + + cpdma_desc_free(priv, desc); + return 0; +} + +static int cpsw_init(struct eth_device *dev, bd_t *bis) +{ + struct cpsw_priv *priv = dev->priv; + struct cpsw_slave *slave; + int i, ret; + + /* soft reset the controller and initialize priv */ + setbit_and_wait_for_clear32(&priv->regs->soft_reset); + + /* initialize and reset the address lookup engine */ + cpsw_ale_enable(priv, 1); + cpsw_ale_clear(priv, 1); + cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */ + + /* setup host port priority mapping */ + __raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map); + __raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map); + + /* disable priority elevation and enable statistics on all ports */ + __raw_writel(0, &priv->regs->ptype); + + /* enable statistics collection only on the host port */ + __raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en); + __raw_writel(0x7, &priv->regs->stat_port_en); + + cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD); + + cpsw_ale_add_ucast(priv, priv->dev->enetaddr, priv->host_port, + ALE_SECURE); + cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << priv->host_port); + + for_each_slave(slave, priv) + cpsw_slave_init(slave, priv); + + cpsw_update_link(priv); + + /* init descriptor pool */ + for (i = 0; i < NUM_DESCS; i++) { + desc_write(&priv->descs[i], hw_next, + (i == (NUM_DESCS - 1)) ? 0 : &priv->descs[i+1]); + } + priv->desc_free = &priv->descs[0]; + + /* initialize channels */ + if (priv->data.version == CPSW_CTRL_VERSION_2) { + memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan)); + priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER2; + priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER2; + priv->rx_chan.rxfree = priv->dma_regs + CPDMA_RXFREE; + + memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan)); + priv->tx_chan.hdp = priv->dma_regs + CPDMA_TXHDP_VER2; + priv->tx_chan.cp = priv->dma_regs + CPDMA_TXCP_VER2; + } else { + memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan)); + priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER1; + priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER1; + priv->rx_chan.rxfree = priv->dma_regs + CPDMA_RXFREE; + + memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan)); + priv->tx_chan.hdp = priv->dma_regs + CPDMA_TXHDP_VER1; + priv->tx_chan.cp = priv->dma_regs + CPDMA_TXCP_VER1; + } + + /* clear dma state */ + setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET); + + if (priv->data.version == CPSW_CTRL_VERSION_2) { + for (i = 0; i < priv->data.channels; i++) { + __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4 + * i); + __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4 + * i); + __raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER2 + 4 + * i); + __raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER2 + 4 + * i); + __raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER2 + 4 + * i); + } + } else { + for (i = 0; i < priv->data.channels; i++) { + __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER1 + 4 + * i); + __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4 + * i); + __raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER1 + 4 + * i); + __raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER1 + 4 + * i); + __raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER1 + 4 + * i); + + } + } + + __raw_writel(1, priv->dma_regs + CPDMA_TXCONTROL); + __raw_writel(1, priv->dma_regs + CPDMA_RXCONTROL); + + /* submit rx descs */ + for (i = 0; i < PKTBUFSRX; i++) { + ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i], + PKTSIZE); + if (ret < 0) { + printf("error %d submitting rx desc\n", ret); + break; + } + } + + return 0; +} + +static void cpsw_halt(struct eth_device *dev) +{ + struct cpsw_priv *priv = dev->priv; + + writel(0, priv->dma_regs + CPDMA_TXCONTROL); + writel(0, priv->dma_regs + CPDMA_RXCONTROL); + + /* soft reset the controller and initialize priv */ + setbit_and_wait_for_clear32(&priv->regs->soft_reset); + + /* clear dma state */ + setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET); + + priv->data.control(0); +} + +static int cpsw_send(struct eth_device *dev, void *packet, int length) +{ + struct cpsw_priv *priv = dev->priv; + void *buffer; + int len; + int timeout = CPDMA_TIMEOUT; + + if (!cpsw_check_link(priv)) + return -EIO; + + flush_dcache_range((unsigned long)packet, + (unsigned long)packet + length); + + /* first reap completed packets */ + while (timeout-- && + (cpdma_process(priv, &priv->tx_chan, &buffer, &len) >= 0)) + ; + + if (timeout == -1) { + printf("cpdma_process timeout\n"); + return -ETIMEDOUT; + } + + return cpdma_submit(priv, &priv->tx_chan, packet, length); +} + +static int cpsw_recv(struct eth_device *dev) +{ + struct cpsw_priv *priv = dev->priv; + void *buffer; + int len; + + cpsw_check_link(priv); + + while (cpdma_process(priv, &priv->rx_chan, &buffer, &len) >= 0) { + invalidate_dcache_range((unsigned long)buffer, + (unsigned long)buffer + PKTSIZE_ALIGN); + NetReceive(buffer, len); + cpdma_submit(priv, &priv->rx_chan, buffer, PKTSIZE); + } + + return 0; +} + +static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, + struct cpsw_priv *priv) +{ + void *regs = priv->regs; + struct cpsw_slave_data *data = priv->data.slave_data + slave_num; + slave->slave_num = slave_num; + slave->data = data; + slave->regs = regs + data->slave_reg_ofs; + slave->sliver = regs + data->sliver_reg_ofs; +} + +static int cpsw_phy_init(struct eth_device *dev, struct cpsw_slave *slave) +{ + struct cpsw_priv *priv = (struct cpsw_priv *)dev->priv; + struct phy_device *phydev; + u32 supported = PHY_GBIT_FEATURES; + + phydev = phy_connect(priv->bus, + slave->data->phy_addr, + dev, + slave->data->phy_if); + + if (!phydev) + return -1; + + phydev->supported &= supported; + phydev->advertising = phydev->supported; + + priv->phydev = phydev; + phy_config(phydev); + + return 1; +} + +int cpsw_register(struct cpsw_platform_data *data) +{ + struct cpsw_priv *priv; + struct cpsw_slave *slave; + void *regs = (void *)data->cpsw_base; + struct eth_device *dev; + + dev = calloc(sizeof(*dev), 1); + if (!dev) + return -ENOMEM; + + priv = calloc(sizeof(*priv), 1); + if (!priv) { + free(dev); + return -ENOMEM; + } + + priv->data = *data; + priv->dev = dev; + + priv->slaves = malloc(sizeof(struct cpsw_slave) * data->slaves); + if (!priv->slaves) { + free(dev); + free(priv); + return -ENOMEM; + } + + priv->host_port = data->host_port_num; + priv->regs = regs; + priv->host_port_regs = regs + data->host_port_reg_ofs; + priv->dma_regs = regs + data->cpdma_reg_ofs; + priv->ale_regs = regs + data->ale_reg_ofs; + priv->descs = (void *)regs + data->bd_ram_ofs; + + int idx = 0; + + for_each_slave(slave, priv) { + cpsw_slave_setup(slave, idx, priv); + idx = idx + 1; + } + + strcpy(dev->name, "cpsw"); + dev->iobase = 0; + dev->init = cpsw_init; + dev->halt = cpsw_halt; + dev->send = cpsw_send; + dev->recv = cpsw_recv; + dev->priv = priv; + + eth_register(dev); + + cpsw_mdio_init(dev->name, data->mdio_base, data->mdio_div); + priv->bus = miiphy_get_dev_by_name(dev->name); + for_each_slave(slave, priv) + cpsw_phy_init(dev, slave); + + return 1; +} diff --git a/qemu/roms/u-boot/drivers/net/cs8900.c b/qemu/roms/u-boot/drivers/net/cs8900.c new file mode 100644 index 000000000..84963c1f2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/cs8900.c @@ -0,0 +1,320 @@ +/* + * Cirrus Logic CS8900A Ethernet + * + * (C) 2009 Ben Warren , biggerbadderben@gmail.com + * Converted to use CONFIG_NET_MULTI API + * + * (C) 2003 Wolfgang Denk, wd@denx.de + * Extension to synchronize ethaddr environment variable + * against value in EEPROM + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * Copyright (C) 1999 Ben Williamson + * + * This program is loaded into SRAM in bootstrap mode, where it waits + * for commands on UART1 to read and write memory, jump to code etc. + * A design goal for this program is to be entirely independent of the + * target board. Anything with a CL-PS7111 or EP7211 should be able to run + * this code in bootstrap mode. All the board specifics can be handled on + * the host. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include "cs8900.h" + +#undef DEBUG + +/* packet page register access functions */ + +#ifdef CONFIG_CS8900_BUS32 + +#define REG_WRITE(v, a) writel((v),(a)) +#define REG_READ(a) readl((a)) + +/* we don't need 16 bit initialisation on 32 bit bus */ +#define get_reg_init_bus(r,d) get_reg((r),(d)) + +#else + +#define REG_WRITE(v, a) writew((v),(a)) +#define REG_READ(a) readw((a)) + +static u16 get_reg_init_bus(struct eth_device *dev, int regno) +{ + /* force 16 bit busmode */ + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + uint8_t volatile * const iob = (uint8_t volatile * const)dev->iobase; + + readb(iob); + readb(iob + 1); + readb(iob); + readb(iob + 1); + readb(iob); + + REG_WRITE(regno, &priv->regs->pptr); + return REG_READ(&priv->regs->pdata); +} +#endif + +static u16 get_reg(struct eth_device *dev, int regno) +{ + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + REG_WRITE(regno, &priv->regs->pptr); + return REG_READ(&priv->regs->pdata); +} + + +static void put_reg(struct eth_device *dev, int regno, u16 val) +{ + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + REG_WRITE(regno, &priv->regs->pptr); + REG_WRITE(val, &priv->regs->pdata); +} + +static void cs8900_reset(struct eth_device *dev) +{ + int tmo; + u16 us; + + /* reset NIC */ + put_reg(dev, PP_SelfCTL, get_reg(dev, PP_SelfCTL) | PP_SelfCTL_Reset); + + /* wait for 200ms */ + udelay(200000); + /* Wait until the chip is reset */ + + tmo = get_timer(0) + 1 * CONFIG_SYS_HZ; + while ((((us = get_reg_init_bus(dev, PP_SelfSTAT)) & + PP_SelfSTAT_InitD) == 0) && tmo < get_timer(0)) + /*NOP*/; +} + +static void cs8900_reginit(struct eth_device *dev) +{ + /* receive only error free packets addressed to this card */ + put_reg(dev, PP_RxCTL, + PP_RxCTL_IA | PP_RxCTL_Broadcast | PP_RxCTL_RxOK); + /* do not generate any interrupts on receive operations */ + put_reg(dev, PP_RxCFG, 0); + /* do not generate any interrupts on transmit operations */ + put_reg(dev, PP_TxCFG, 0); + /* do not generate any interrupts on buffer operations */ + put_reg(dev, PP_BufCFG, 0); + /* enable transmitter/receiver mode */ + put_reg(dev, PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx); +} + +void cs8900_get_enetaddr(struct eth_device *dev) +{ + int i; + + /* verify chip id */ + if (get_reg_init_bus(dev, PP_ChipID) != 0x630e) + return; + cs8900_reset(dev); + if ((get_reg(dev, PP_SelfSTAT) & + (PP_SelfSTAT_EEPROM | PP_SelfSTAT_EEPROM_OK)) == + (PP_SelfSTAT_EEPROM | PP_SelfSTAT_EEPROM_OK)) { + + /* Load the MAC from EEPROM */ + for (i = 0; i < 3; i++) { + u32 Addr; + + Addr = get_reg(dev, PP_IA + i * 2); + dev->enetaddr[i * 2] = Addr & 0xFF; + dev->enetaddr[i * 2 + 1] = Addr >> 8; + } + } +} + +void cs8900_halt(struct eth_device *dev) +{ + /* disable transmitter/receiver mode */ + put_reg(dev, PP_LineCTL, 0); + + /* "shutdown" to show ChipID or kernel wouldn't find he cs8900 ... */ + get_reg_init_bus(dev, PP_ChipID); +} + +static int cs8900_init(struct eth_device *dev, bd_t * bd) +{ + uchar *enetaddr = dev->enetaddr; + u16 id; + + /* verify chip id */ + id = get_reg_init_bus(dev, PP_ChipID); + if (id != 0x630e) { + printf ("CS8900 Ethernet chip not found: " + "ID=0x%04x instead 0x%04x\n", id, 0x630e); + return 1; + } + + cs8900_reset (dev); + /* set the ethernet address */ + put_reg(dev, PP_IA + 0, enetaddr[0] | (enetaddr[1] << 8)); + put_reg(dev, PP_IA + 2, enetaddr[2] | (enetaddr[3] << 8)); + put_reg(dev, PP_IA + 4, enetaddr[4] | (enetaddr[5] << 8)); + + cs8900_reginit(dev); + return 0; +} + +/* Get a data block via Ethernet */ +static int cs8900_recv(struct eth_device *dev) +{ + int i; + u16 rxlen; + u16 *addr; + u16 status; + + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + + status = get_reg(dev, PP_RER); + + if ((status & PP_RER_RxOK) == 0) + return 0; + + status = REG_READ(&priv->regs->rtdata); + rxlen = REG_READ(&priv->regs->rtdata); + + if (rxlen > PKTSIZE_ALIGN + PKTALIGN) + debug("packet too big!\n"); + for (addr = (u16 *) NetRxPackets[0], i = rxlen >> 1; i > 0; + i--) + *addr++ = REG_READ(&priv->regs->rtdata); + if (rxlen & 1) + *addr++ = REG_READ(&priv->regs->rtdata); + + /* Pass the packet up to the protocol layers. */ + NetReceive (NetRxPackets[0], rxlen); + return rxlen; +} + +/* Send a data block via Ethernet. */ +static int cs8900_send(struct eth_device *dev, void *packet, int length) +{ + volatile u16 *addr; + int tmo; + u16 s; + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + +retry: + /* initiate a transmit sequence */ + REG_WRITE(PP_TxCmd_TxStart_Full, &priv->regs->txcmd); + REG_WRITE(length, &priv->regs->txlen); + + /* Test to see if the chip has allocated memory for the packet */ + if ((get_reg(dev, PP_BusSTAT) & PP_BusSTAT_TxRDY) == 0) { + /* Oops... this should not happen! */ + debug("cs: unable to send packet; retrying...\n"); + for (tmo = get_timer(0) + 5 * CONFIG_SYS_HZ; + get_timer(0) < tmo;) + /*NOP*/; + cs8900_reset(dev); + cs8900_reginit(dev); + goto retry; + } + + /* Write the contents of the packet */ + /* assume even number of bytes */ + for (addr = packet; length > 0; length -= 2) + REG_WRITE(*addr++, &priv->regs->rtdata); + + /* wait for transfer to succeed */ + tmo = get_timer(0) + 5 * CONFIG_SYS_HZ; + while ((s = get_reg(dev, PP_TER) & ~0x1F) == 0) { + if (get_timer(0) >= tmo) + break; + } + + /* nothing */ ; + if((s & (PP_TER_CRS | PP_TER_TxOK)) != PP_TER_TxOK) { + debug("\ntransmission error %#x\n", s); + } + + return 0; +} + +static void cs8900_e2prom_ready(struct eth_device *dev) +{ + while (get_reg(dev, PP_SelfSTAT) & SI_BUSY) + ; +} + +/***********************************************************/ +/* read a 16-bit word out of the EEPROM */ +/***********************************************************/ + +int cs8900_e2prom_read(struct eth_device *dev, + u8 addr, u16 *value) +{ + cs8900_e2prom_ready(dev); + put_reg(dev, PP_EECMD, EEPROM_READ_CMD | addr); + cs8900_e2prom_ready(dev); + *value = get_reg(dev, PP_EEData); + + return 0; +} + + +/***********************************************************/ +/* write a 16-bit word into the EEPROM */ +/***********************************************************/ + +int cs8900_e2prom_write(struct eth_device *dev, u8 addr, u16 value) +{ + cs8900_e2prom_ready(dev); + put_reg(dev, PP_EECMD, EEPROM_WRITE_EN); + cs8900_e2prom_ready(dev); + put_reg(dev, PP_EEData, value); + put_reg(dev, PP_EECMD, EEPROM_WRITE_CMD | addr); + cs8900_e2prom_ready(dev); + put_reg(dev, PP_EECMD, EEPROM_WRITE_DIS); + cs8900_e2prom_ready(dev); + + return 0; +} + +int cs8900_initialize(u8 dev_num, int base_addr) +{ + struct eth_device *dev; + struct cs8900_priv *priv; + + dev = malloc(sizeof(*dev)); + if (!dev) { + return 0; + } + memset(dev, 0, sizeof(*dev)); + + priv = malloc(sizeof(*priv)); + if (!priv) { + free(dev); + return 0; + } + memset(priv, 0, sizeof(*priv)); + priv->regs = (struct cs8900_regs *)base_addr; + + dev->iobase = base_addr; + dev->priv = priv; + dev->init = cs8900_init; + dev->halt = cs8900_halt; + dev->send = cs8900_send; + dev->recv = cs8900_recv; + + /* Load MAC address from EEPROM */ + cs8900_get_enetaddr(dev); + + sprintf(dev->name, "%s-%hu", CS8900_DRIVERNAME, dev_num); + + eth_register(dev); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/cs8900.h b/qemu/roms/u-boot/drivers/net/cs8900.h new file mode 100644 index 000000000..79877dd57 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/cs8900.h @@ -0,0 +1,249 @@ +#ifndef CS8900_H +#define CS8900_H +/* + * Cirrus Logic CS8900A Ethernet + * + * (C) 2009 Ben Warren , biggerbadderben@gmail.com + * Converted to use CONFIG_NET_MULTI API + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * Copyright (C) 1999 Ben Williamson + * + * This program is loaded into SRAM in bootstrap mode, where it waits + * for commands on UART1 to read and write memory, jump to code etc. + * A design goal for this program is to be entirely independent of the + * target board. Anything with a CL-PS7111 or EP7211 should be able to run + * this code in bootstrap mode. All the board specifics can be handled on + * the host. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#define CS8900_DRIVERNAME "CS8900" +/* although the registers are 16 bit, they are 32-bit aligned on the + EDB7111. so we have to read them as 32-bit registers and ignore the + upper 16-bits. i'm not sure if this holds for the EDB7211. */ + +#ifdef CONFIG_CS8900_BUS16 + /* 16 bit aligned registers, 16 bit wide */ + #define CS8900_REG u16 +#elif defined(CONFIG_CS8900_BUS32) + /* 32 bit aligned registers, 16 bit wide (we ignore upper 16 bits) */ + #define CS8900_REG u32 +#else + #error unknown bussize ... +#endif + +struct cs8900_regs { + CS8900_REG rtdata; + CS8900_REG pad0; + CS8900_REG txcmd; + CS8900_REG txlen; + CS8900_REG isq; + CS8900_REG pptr; + CS8900_REG pdata; +}; + +struct cs8900_priv { + struct cs8900_regs *regs; +}; + +#define ISQ_RxEvent 0x04 +#define ISQ_TxEvent 0x08 +#define ISQ_BufEvent 0x0C +#define ISQ_RxMissEvent 0x10 +#define ISQ_TxColEvent 0x12 +#define ISQ_EventMask 0x3F + +/* packet page register offsets */ + +/* bus interface registers */ +#define PP_ChipID 0x0000 /* Chip identifier - must be 0x630E */ +#define PP_ChipRev 0x0002 /* Chip revision, model codes */ + +#define PP_IntReg 0x0022 /* Interrupt configuration */ +#define PP_IntReg_IRQ0 0x0000 /* Use INTR0 pin */ +#define PP_IntReg_IRQ1 0x0001 /* Use INTR1 pin */ +#define PP_IntReg_IRQ2 0x0002 /* Use INTR2 pin */ +#define PP_IntReg_IRQ3 0x0003 /* Use INTR3 pin */ + +/* status and control registers */ + +#define PP_RxCFG 0x0102 /* Receiver configuration */ +#define PP_RxCFG_Skip1 0x0040 /* Skip (i.e. discard) current frame */ +#define PP_RxCFG_Stream 0x0080 /* Enable streaming mode */ +#define PP_RxCFG_RxOK 0x0100 /* RxOK interrupt enable */ +#define PP_RxCFG_RxDMAonly 0x0200 /* Use RxDMA for all frames */ +#define PP_RxCFG_AutoRxDMA 0x0400 /* Select RxDMA automatically */ +#define PP_RxCFG_BufferCRC 0x0800 /* Include CRC characters in frame */ +#define PP_RxCFG_CRC 0x1000 /* Enable interrupt on CRC error */ +#define PP_RxCFG_RUNT 0x2000 /* Enable interrupt on RUNT frames */ +#define PP_RxCFG_EXTRA 0x4000 /* Enable interrupt on frames with extra data */ + +#define PP_RxCTL 0x0104 /* Receiver control */ +#define PP_RxCTL_IAHash 0x0040 /* Accept frames that match hash */ +#define PP_RxCTL_Promiscuous 0x0080 /* Accept any frame */ +#define PP_RxCTL_RxOK 0x0100 /* Accept well formed frames */ +#define PP_RxCTL_Multicast 0x0200 /* Accept multicast frames */ +#define PP_RxCTL_IA 0x0400 /* Accept frame that matches IA */ +#define PP_RxCTL_Broadcast 0x0800 /* Accept broadcast frames */ +#define PP_RxCTL_CRC 0x1000 /* Accept frames with bad CRC */ +#define PP_RxCTL_RUNT 0x2000 /* Accept runt frames */ +#define PP_RxCTL_EXTRA 0x4000 /* Accept frames that are too long */ + +#define PP_TxCFG 0x0106 /* Transmit configuration */ +#define PP_TxCFG_CRS 0x0040 /* Enable interrupt on loss of carrier */ +#define PP_TxCFG_SQE 0x0080 /* Enable interrupt on Signal Quality Error */ +#define PP_TxCFG_TxOK 0x0100 /* Enable interrupt on successful xmits */ +#define PP_TxCFG_Late 0x0200 /* Enable interrupt on "out of window" */ +#define PP_TxCFG_Jabber 0x0400 /* Enable interrupt on jabber detect */ +#define PP_TxCFG_Collision 0x0800 /* Enable interrupt if collision */ +#define PP_TxCFG_16Collisions 0x8000 /* Enable interrupt if > 16 collisions */ + +#define PP_TxCmd 0x0108 /* Transmit command status */ +#define PP_TxCmd_TxStart_5 0x0000 /* Start after 5 bytes in buffer */ +#define PP_TxCmd_TxStart_381 0x0040 /* Start after 381 bytes in buffer */ +#define PP_TxCmd_TxStart_1021 0x0080 /* Start after 1021 bytes in buffer */ +#define PP_TxCmd_TxStart_Full 0x00C0 /* Start after all bytes loaded */ +#define PP_TxCmd_Force 0x0100 /* Discard any pending packets */ +#define PP_TxCmd_OneCollision 0x0200 /* Abort after a single collision */ +#define PP_TxCmd_NoCRC 0x1000 /* Do not add CRC */ +#define PP_TxCmd_NoPad 0x2000 /* Do not pad short packets */ + +#define PP_BufCFG 0x010A /* Buffer configuration */ +#define PP_BufCFG_SWI 0x0040 /* Force interrupt via software */ +#define PP_BufCFG_RxDMA 0x0080 /* Enable interrupt on Rx DMA */ +#define PP_BufCFG_TxRDY 0x0100 /* Enable interrupt when ready for Tx */ +#define PP_BufCFG_TxUE 0x0200 /* Enable interrupt in Tx underrun */ +#define PP_BufCFG_RxMiss 0x0400 /* Enable interrupt on missed Rx packets */ +#define PP_BufCFG_Rx128 0x0800 /* Enable Rx interrupt after 128 bytes */ +#define PP_BufCFG_TxCol 0x1000 /* Enable int on Tx collision ctr overflow */ +#define PP_BufCFG_Miss 0x2000 /* Enable int on Rx miss ctr overflow */ +#define PP_BufCFG_RxDest 0x8000 /* Enable int on Rx dest addr match */ + +#define PP_LineCTL 0x0112 /* Line control */ +#define PP_LineCTL_Rx 0x0040 /* Enable receiver */ +#define PP_LineCTL_Tx 0x0080 /* Enable transmitter */ +#define PP_LineCTL_AUIonly 0x0100 /* AUI interface only */ +#define PP_LineCTL_AutoAUI10BT 0x0200 /* Autodetect AUI or 10BaseT interface */ +#define PP_LineCTL_ModBackoffE 0x0800 /* Enable modified backoff algorithm */ +#define PP_LineCTL_PolarityDis 0x1000 /* Disable Rx polarity autodetect */ +#define PP_LineCTL_2partDefDis 0x2000 /* Disable two-part defferal */ +#define PP_LineCTL_LoRxSquelch 0x4000 /* Reduce receiver squelch threshold */ + +#define PP_SelfCTL 0x0114 /* Chip self control */ +#define PP_SelfCTL_Reset 0x0040 /* Self-clearing reset */ +#define PP_SelfCTL_SWSuspend 0x0100 /* Initiate suspend mode */ +#define PP_SelfCTL_HWSleepE 0x0200 /* Enable SLEEP input */ +#define PP_SelfCTL_HWStandbyE 0x0400 /* Enable standby mode */ +#define PP_SelfCTL_HC0E 0x1000 /* use HCB0 for LINK LED */ +#define PP_SelfCTL_HC1E 0x2000 /* use HCB1 for BSTATUS LED */ +#define PP_SelfCTL_HCB0 0x4000 /* control LINK LED if HC0E set */ +#define PP_SelfCTL_HCB1 0x8000 /* control BSTATUS LED if HC1E set */ + +#define PP_BusCTL 0x0116 /* Bus control */ +#define PP_BusCTL_ResetRxDMA 0x0040 /* Reset RxDMA pointer */ +#define PP_BusCTL_DMAextend 0x0100 /* Extend DMA cycle */ +#define PP_BusCTL_UseSA 0x0200 /* Assert MEMCS16 on address decode */ +#define PP_BusCTL_MemoryE 0x0400 /* Enable memory mode */ +#define PP_BusCTL_DMAburst 0x0800 /* Limit DMA access burst */ +#define PP_BusCTL_IOCHRDYE 0x1000 /* Set IOCHRDY high impedence */ +#define PP_BusCTL_RxDMAsize 0x2000 /* Set DMA buffer size 64KB */ +#define PP_BusCTL_EnableIRQ 0x8000 /* Generate interrupt on interrupt event */ + +#define PP_TestCTL 0x0118 /* Test control */ +#define PP_TestCTL_DisableLT 0x0080 /* Disable link status */ +#define PP_TestCTL_ENDECloop 0x0200 /* Internal loopback */ +#define PP_TestCTL_AUIloop 0x0400 /* AUI loopback */ +#define PP_TestCTL_DisBackoff 0x0800 /* Disable backoff algorithm */ +#define PP_TestCTL_FDX 0x4000 /* Enable full duplex mode */ + +#define PP_ISQ 0x0120 /* Interrupt Status Queue */ + +#define PP_RER 0x0124 /* Receive event */ +#define PP_RER_IAHash 0x0040 /* Frame hash match */ +#define PP_RER_Dribble 0x0080 /* Frame had 1-7 extra bits after last byte */ +#define PP_RER_RxOK 0x0100 /* Frame received with no errors */ +#define PP_RER_Hashed 0x0200 /* Frame address hashed OK */ +#define PP_RER_IA 0x0400 /* Frame address matched IA */ +#define PP_RER_Broadcast 0x0800 /* Broadcast frame */ +#define PP_RER_CRC 0x1000 /* Frame had CRC error */ +#define PP_RER_RUNT 0x2000 /* Runt frame */ +#define PP_RER_EXTRA 0x4000 /* Frame was too long */ + +#define PP_TER 0x0128 /* Transmit event */ +#define PP_TER_CRS 0x0040 /* Carrier lost */ +#define PP_TER_SQE 0x0080 /* Signal Quality Error */ +#define PP_TER_TxOK 0x0100 /* Packet sent without error */ +#define PP_TER_Late 0x0200 /* Out of window */ +#define PP_TER_Jabber 0x0400 /* Stuck transmit? */ +#define PP_TER_NumCollisions 0x7800 /* Number of collisions */ +#define PP_TER_16Collisions 0x8000 /* > 16 collisions */ + +#define PP_BER 0x012C /* Buffer event */ +#define PP_BER_SWint 0x0040 /* Software interrupt */ +#define PP_BER_RxDMAFrame 0x0080 /* Received framed DMAed */ +#define PP_BER_Rdy4Tx 0x0100 /* Ready for transmission */ +#define PP_BER_TxUnderrun 0x0200 /* Transmit underrun */ +#define PP_BER_RxMiss 0x0400 /* Received frame missed */ +#define PP_BER_Rx128 0x0800 /* 128 bytes received */ +#define PP_BER_RxDest 0x8000 /* Received framed passed address filter */ + +#define PP_RxMiss 0x0130 /* Receiver miss counter */ + +#define PP_TxCol 0x0132 /* Transmit collision counter */ + +#define PP_LineSTAT 0x0134 /* Line status */ +#define PP_LineSTAT_LinkOK 0x0080 /* Line is connected and working */ +#define PP_LineSTAT_AUI 0x0100 /* Connected via AUI */ +#define PP_LineSTAT_10BT 0x0200 /* Connected via twisted pair */ +#define PP_LineSTAT_Polarity 0x1000 /* Line polarity OK (10BT only) */ +#define PP_LineSTAT_CRS 0x4000 /* Frame being received */ + +#define PP_SelfSTAT 0x0136 /* Chip self status */ +#define PP_SelfSTAT_33VActive 0x0040 /* supply voltage is 3.3V */ +#define PP_SelfSTAT_InitD 0x0080 /* Chip initialization complete */ +#define PP_SelfSTAT_SIBSY 0x0100 /* EEPROM is busy */ +#define PP_SelfSTAT_EEPROM 0x0200 /* EEPROM present */ +#define PP_SelfSTAT_EEPROM_OK 0x0400 /* EEPROM checks out */ +#define PP_SelfSTAT_ELPresent 0x0800 /* External address latch logic available */ +#define PP_SelfSTAT_EEsize 0x1000 /* Size of EEPROM */ + +#define PP_BusSTAT 0x0138 /* Bus status */ +#define PP_BusSTAT_TxBid 0x0080 /* Tx error */ +#define PP_BusSTAT_TxRDY 0x0100 /* Ready for Tx data */ + +#define PP_TDR 0x013C /* AUI Time Domain Reflectometer */ + +/* initiate transmit registers */ + +#define PP_TxCommand 0x0144 /* Tx Command */ +#define PP_TxLength 0x0146 /* Tx Length */ + + +/* address filter registers */ + +#define PP_LAF 0x0150 /* Logical address filter (6 bytes) */ +#define PP_IA 0x0158 /* Individual address (MAC) */ + +/* EEPROM Kram */ +#define SI_BUSY 0x0100 +#define PP_EECMD 0x0040 /* NVR Interface Command register */ +#define PP_EEData 0x0042 /* NVR Interface Data Register */ +#define EEPROM_WRITE_EN 0x00F0 +#define EEPROM_WRITE_DIS 0x0000 +#define EEPROM_WRITE_CMD 0x0100 +#define EEPROM_READ_CMD 0x0200 +#define EEPROM_ERASE_CMD 0x0300 + +/* Exported functions */ +int cs8900_e2prom_read(struct eth_device *dev, uchar, ushort *); +int cs8900_e2prom_write(struct eth_device *dev, uchar, ushort); + +#endif /* CS8900_H */ diff --git a/qemu/roms/u-boot/drivers/net/davinci_emac.c b/qemu/roms/u-boot/drivers/net/davinci_emac.c new file mode 100644 index 000000000..439f8ae99 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/davinci_emac.c @@ -0,0 +1,894 @@ +/* + * Ethernet driver for TI TMS320DM644x (DaVinci) chips. + * + * Copyright (C) 2007 Sergey Kubushyn + * + * Parts shamelessly stolen from TI's dm644x_emac.c. Original copyright + * follows: + * + * ---------------------------------------------------------------------------- + * + * dm644x_emac.c + * + * TI DaVinci (DM644X) EMAC peripheral driver source for DV-EVM + * + * Copyright (C) 2005 Texas Instruments. + * + * ---------------------------------------------------------------------------- + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Modifications: + * ver. 1.0: Sep 2005, Anant Gole - Created EMAC version for uBoot. + * ver 1.1: Nov 2005, Anant Gole - Extended the RX logic for multiple descriptors + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "davinci_emac.h" + +unsigned int emac_dbg = 0; +#define debug_emac(fmt,args...) if (emac_dbg) printf(fmt,##args) + +#ifdef EMAC_HW_RAM_ADDR +static inline unsigned long BD_TO_HW(unsigned long x) +{ + if (x == 0) + return 0; + + return x - EMAC_WRAPPER_RAM_ADDR + EMAC_HW_RAM_ADDR; +} + +static inline unsigned long HW_TO_BD(unsigned long x) +{ + if (x == 0) + return 0; + + return x - EMAC_HW_RAM_ADDR + EMAC_WRAPPER_RAM_ADDR; +} +#else +#define BD_TO_HW(x) (x) +#define HW_TO_BD(x) (x) +#endif + +#ifdef DAVINCI_EMAC_GIG_ENABLE +#define emac_gigabit_enable(phy_addr) davinci_eth_gigabit_enable(phy_addr) +#else +#define emac_gigabit_enable(phy_addr) /* no gigabit to enable */ +#endif + +#if !defined(CONFIG_SYS_EMAC_TI_CLKDIV) +#define CONFIG_SYS_EMAC_TI_CLKDIV ((EMAC_MDIO_BUS_FREQ / \ + EMAC_MDIO_CLOCK_FREQ) - 1) +#endif + +static void davinci_eth_mdio_enable(void); + +static int gen_init_phy(int phy_addr); +static int gen_is_phy_connected(int phy_addr); +static int gen_get_link_speed(int phy_addr); +static int gen_auto_negotiate(int phy_addr); + +void eth_mdio_enable(void) +{ + davinci_eth_mdio_enable(); +} + +/* EMAC Addresses */ +static volatile emac_regs *adap_emac = (emac_regs *)EMAC_BASE_ADDR; +static volatile ewrap_regs *adap_ewrap = (ewrap_regs *)EMAC_WRAPPER_BASE_ADDR; +static volatile mdio_regs *adap_mdio = (mdio_regs *)EMAC_MDIO_BASE_ADDR; + +/* EMAC descriptors */ +static volatile emac_desc *emac_rx_desc = (emac_desc *)(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE); +static volatile emac_desc *emac_tx_desc = (emac_desc *)(EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE); +static volatile emac_desc *emac_rx_active_head = 0; +static volatile emac_desc *emac_rx_active_tail = 0; +static int emac_rx_queue_active = 0; + +/* Receive packet buffers */ +static unsigned char emac_rx_buffers[EMAC_MAX_RX_BUFFERS * EMAC_RXBUF_SIZE] + __aligned(ARCH_DMA_MINALIGN); + +#ifndef CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT +#define CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT 3 +#endif + +/* PHY address for a discovered PHY (0xff - not found) */ +static u_int8_t active_phy_addr[CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT]; + +/* number of PHY found active */ +static u_int8_t num_phy; + +phy_t phy[CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT]; + +static inline void davinci_flush_rx_descs(void) +{ + /* flush the whole RX descs area */ + flush_dcache_range(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE, + EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE); +} + +static inline void davinci_invalidate_rx_descs(void) +{ + /* invalidate the whole RX descs area */ + invalidate_dcache_range(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE, + EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE); +} + +static inline void davinci_flush_desc(emac_desc *desc) +{ + flush_dcache_range((unsigned long)desc, + (unsigned long)desc + sizeof(*desc)); +} + +static int davinci_eth_set_mac_addr(struct eth_device *dev) +{ + unsigned long mac_hi; + unsigned long mac_lo; + + /* + * Set MAC Addresses & Init multicast Hash to 0 (disable any multicast + * receive) + * Using channel 0 only - other channels are disabled + * */ + writel(0, &adap_emac->MACINDEX); + mac_hi = (dev->enetaddr[3] << 24) | + (dev->enetaddr[2] << 16) | + (dev->enetaddr[1] << 8) | + (dev->enetaddr[0]); + mac_lo = (dev->enetaddr[5] << 8) | + (dev->enetaddr[4]); + + writel(mac_hi, &adap_emac->MACADDRHI); +#if defined(DAVINCI_EMAC_VERSION2) + writel(mac_lo | EMAC_MAC_ADDR_IS_VALID | EMAC_MAC_ADDR_MATCH, + &adap_emac->MACADDRLO); +#else + writel(mac_lo, &adap_emac->MACADDRLO); +#endif + + writel(0, &adap_emac->MACHASH1); + writel(0, &adap_emac->MACHASH2); + + /* Set source MAC address - REQUIRED */ + writel(mac_hi, &adap_emac->MACSRCADDRHI); + writel(mac_lo, &adap_emac->MACSRCADDRLO); + + + return 0; +} + +static void davinci_eth_mdio_enable(void) +{ + u_int32_t clkdiv; + + clkdiv = CONFIG_SYS_EMAC_TI_CLKDIV; + + writel((clkdiv & 0xff) | + MDIO_CONTROL_ENABLE | + MDIO_CONTROL_FAULT | + MDIO_CONTROL_FAULT_ENABLE, + &adap_mdio->CONTROL); + + while (readl(&adap_mdio->CONTROL) & MDIO_CONTROL_IDLE) + ; +} + +/* + * Tries to find an active connected PHY. Returns 1 if address if found. + * If no active PHY (or more than one PHY) found returns 0. + * Sets active_phy_addr variable. + */ +static int davinci_eth_phy_detect(void) +{ + u_int32_t phy_act_state; + int i; + int j; + unsigned int count = 0; + + for (i = 0; i < CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT; i++) + active_phy_addr[i] = 0xff; + + udelay(1000); + phy_act_state = readl(&adap_mdio->ALIVE); + + if (phy_act_state == 0) + return 0; /* No active PHYs */ + + debug_emac("davinci_eth_phy_detect(), ALIVE = 0x%08x\n", phy_act_state); + + for (i = 0, j = 0; i < 32; i++) + if (phy_act_state & (1 << i)) { + count++; + if (count <= CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT) { + active_phy_addr[j++] = i; + } else { + printf("%s: to many PHYs detected.\n", + __func__); + count = 0; + break; + } + } + + num_phy = count; + + return count; +} + + +/* Read a PHY register via MDIO inteface. Returns 1 on success, 0 otherwise */ +int davinci_eth_phy_read(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t *data) +{ + int tmp; + + while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO) + ; + + writel(MDIO_USERACCESS0_GO | + MDIO_USERACCESS0_WRITE_READ | + ((reg_num & 0x1f) << 21) | + ((phy_addr & 0x1f) << 16), + &adap_mdio->USERACCESS0); + + /* Wait for command to complete */ + while ((tmp = readl(&adap_mdio->USERACCESS0)) & MDIO_USERACCESS0_GO) + ; + + if (tmp & MDIO_USERACCESS0_ACK) { + *data = tmp & 0xffff; + return(1); + } + + *data = -1; + return(0); +} + +/* Write to a PHY register via MDIO inteface. Blocks until operation is complete. */ +int davinci_eth_phy_write(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t data) +{ + + while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO) + ; + + writel(MDIO_USERACCESS0_GO | + MDIO_USERACCESS0_WRITE_WRITE | + ((reg_num & 0x1f) << 21) | + ((phy_addr & 0x1f) << 16) | + (data & 0xffff), + &adap_mdio->USERACCESS0); + + /* Wait for command to complete */ + while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO) + ; + + return(1); +} + +/* PHY functions for a generic PHY */ +static int gen_init_phy(int phy_addr) +{ + int ret = 1; + + if (gen_get_link_speed(phy_addr)) { + /* Try another time */ + ret = gen_get_link_speed(phy_addr); + } + + return(ret); +} + +static int gen_is_phy_connected(int phy_addr) +{ + u_int16_t dummy; + + return davinci_eth_phy_read(phy_addr, MII_PHYSID1, &dummy); +} + +static int get_active_phy(void) +{ + int i; + + for (i = 0; i < num_phy; i++) + if (phy[i].get_link_speed(active_phy_addr[i])) + return i; + + return -1; /* Return error if no link */ +} + +static int gen_get_link_speed(int phy_addr) +{ + u_int16_t tmp; + + if (davinci_eth_phy_read(phy_addr, MII_STATUS_REG, &tmp) && + (tmp & 0x04)) { +#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \ + defined(CONFIG_MACH_DAVINCI_DA850_EVM) + davinci_eth_phy_read(phy_addr, MII_LPA, &tmp); + + /* Speed doesn't matter, there is no setting for it in EMAC. */ + if (tmp & (LPA_100FULL | LPA_10FULL)) { + /* set EMAC for Full Duplex */ + writel(EMAC_MACCONTROL_MIIEN_ENABLE | + EMAC_MACCONTROL_FULLDUPLEX_ENABLE, + &adap_emac->MACCONTROL); + } else { + /*set EMAC for Half Duplex */ + writel(EMAC_MACCONTROL_MIIEN_ENABLE, + &adap_emac->MACCONTROL); + } + + if (tmp & (LPA_100FULL | LPA_100HALF)) + writel(readl(&adap_emac->MACCONTROL) | + EMAC_MACCONTROL_RMIISPEED_100, + &adap_emac->MACCONTROL); + else + writel(readl(&adap_emac->MACCONTROL) & + ~EMAC_MACCONTROL_RMIISPEED_100, + &adap_emac->MACCONTROL); +#endif + return(1); + } + + return(0); +} + +static int gen_auto_negotiate(int phy_addr) +{ + u_int16_t tmp; + u_int16_t val; + unsigned long cntr = 0; + + if (!davinci_eth_phy_read(phy_addr, MII_BMCR, &tmp)) + return 0; + + val = tmp | BMCR_FULLDPLX | BMCR_ANENABLE | + BMCR_SPEED100; + davinci_eth_phy_write(phy_addr, MII_BMCR, val); + + if (!davinci_eth_phy_read(phy_addr, MII_ADVERTISE, &val)) + return 0; + + val |= (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | + ADVERTISE_10HALF); + davinci_eth_phy_write(phy_addr, MII_ADVERTISE, val); + + if (!davinci_eth_phy_read(phy_addr, MII_BMCR, &tmp)) + return(0); + + /* Restart Auto_negotiation */ + tmp |= BMCR_ANRESTART; + davinci_eth_phy_write(phy_addr, MII_BMCR, tmp); + + /*check AutoNegotiate complete */ + do { + udelay(40000); + if (!davinci_eth_phy_read(phy_addr, MII_BMSR, &tmp)) + return 0; + + if (tmp & BMSR_ANEGCOMPLETE) + break; + + cntr++; + } while (cntr < 200); + + if (!davinci_eth_phy_read(phy_addr, MII_BMSR, &tmp)) + return(0); + + if (!(tmp & BMSR_ANEGCOMPLETE)) + return(0); + + return(gen_get_link_speed(phy_addr)); +} +/* End of generic PHY functions */ + + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +static int davinci_mii_phy_read(const char *devname, unsigned char addr, unsigned char reg, unsigned short *value) +{ + return(davinci_eth_phy_read(addr, reg, value) ? 0 : 1); +} + +static int davinci_mii_phy_write(const char *devname, unsigned char addr, unsigned char reg, unsigned short value) +{ + return(davinci_eth_phy_write(addr, reg, value) ? 0 : 1); +} +#endif + +static void __attribute__((unused)) davinci_eth_gigabit_enable(int phy_addr) +{ + u_int16_t data; + + if (davinci_eth_phy_read(phy_addr, 0, &data)) { + if (data & (1 << 6)) { /* speed selection MSB */ + /* + * Check if link detected is giga-bit + * If Gigabit mode detected, enable gigbit in MAC + */ + writel(readl(&adap_emac->MACCONTROL) | + EMAC_MACCONTROL_GIGFORCE | + EMAC_MACCONTROL_GIGABIT_ENABLE, + &adap_emac->MACCONTROL); + } + } +} + +/* Eth device open */ +static int davinci_eth_open(struct eth_device *dev, bd_t *bis) +{ + dv_reg_p addr; + u_int32_t clkdiv, cnt; + volatile emac_desc *rx_desc; + int index; + + debug_emac("+ emac_open\n"); + + /* Reset EMAC module and disable interrupts in wrapper */ + writel(1, &adap_emac->SOFTRESET); + while (readl(&adap_emac->SOFTRESET) != 0) + ; +#if defined(DAVINCI_EMAC_VERSION2) + writel(1, &adap_ewrap->softrst); + while (readl(&adap_ewrap->softrst) != 0) + ; +#else + writel(0, &adap_ewrap->EWCTL); + for (cnt = 0; cnt < 5; cnt++) { + clkdiv = readl(&adap_ewrap->EWCTL); + } +#endif + +#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \ + defined(CONFIG_MACH_DAVINCI_DA850_EVM) + adap_ewrap->c0rxen = adap_ewrap->c1rxen = adap_ewrap->c2rxen = 0; + adap_ewrap->c0txen = adap_ewrap->c1txen = adap_ewrap->c2txen = 0; + adap_ewrap->c0miscen = adap_ewrap->c1miscen = adap_ewrap->c2miscen = 0; +#endif + rx_desc = emac_rx_desc; + + writel(1, &adap_emac->TXCONTROL); + writel(1, &adap_emac->RXCONTROL); + + davinci_eth_set_mac_addr(dev); + + /* Set DMA 8 TX / 8 RX Head pointers to 0 */ + addr = &adap_emac->TX0HDP; + for(cnt = 0; cnt < 16; cnt++) + writel(0, addr++); + + addr = &adap_emac->RX0HDP; + for(cnt = 0; cnt < 16; cnt++) + writel(0, addr++); + + /* Clear Statistics (do this before setting MacControl register) */ + addr = &adap_emac->RXGOODFRAMES; + for(cnt = 0; cnt < EMAC_NUM_STATS; cnt++) + writel(0, addr++); + + /* No multicast addressing */ + writel(0, &adap_emac->MACHASH1); + writel(0, &adap_emac->MACHASH2); + + /* Create RX queue and set receive process in place */ + emac_rx_active_head = emac_rx_desc; + for (cnt = 0; cnt < EMAC_MAX_RX_BUFFERS; cnt++) { + rx_desc->next = BD_TO_HW((u_int32_t)(rx_desc + 1)); + rx_desc->buffer = &emac_rx_buffers[cnt * EMAC_RXBUF_SIZE]; + rx_desc->buff_off_len = EMAC_MAX_ETHERNET_PKT_SIZE; + rx_desc->pkt_flag_len = EMAC_CPPI_OWNERSHIP_BIT; + rx_desc++; + } + + /* Finalize the rx desc list */ + rx_desc--; + rx_desc->next = 0; + emac_rx_active_tail = rx_desc; + emac_rx_queue_active = 1; + + davinci_flush_rx_descs(); + + /* Enable TX/RX */ + writel(EMAC_MAX_ETHERNET_PKT_SIZE, &adap_emac->RXMAXLEN); + writel(0, &adap_emac->RXBUFFEROFFSET); + + /* + * No fancy configs - Use this for promiscous debug + * - EMAC_RXMBPENABLE_RXCAFEN_ENABLE + */ + writel(EMAC_RXMBPENABLE_RXBROADEN, &adap_emac->RXMBPENABLE); + + /* Enable ch 0 only */ + writel(1, &adap_emac->RXUNICASTSET); + + /* Enable MII interface and Full duplex mode */ +#if defined(CONFIG_SOC_DA8XX) || \ + (defined(CONFIG_OMAP34XX) && defined(CONFIG_DRIVER_TI_EMAC_USE_RMII)) + writel((EMAC_MACCONTROL_MIIEN_ENABLE | + EMAC_MACCONTROL_FULLDUPLEX_ENABLE | + EMAC_MACCONTROL_RMIISPEED_100), + &adap_emac->MACCONTROL); +#else + writel((EMAC_MACCONTROL_MIIEN_ENABLE | + EMAC_MACCONTROL_FULLDUPLEX_ENABLE), + &adap_emac->MACCONTROL); +#endif + + /* Init MDIO & get link state */ + clkdiv = CONFIG_SYS_EMAC_TI_CLKDIV; + writel((clkdiv & 0xff) | MDIO_CONTROL_ENABLE | MDIO_CONTROL_FAULT, + &adap_mdio->CONTROL); + + /* We need to wait for MDIO to start */ + udelay(1000); + + index = get_active_phy(); + if (index == -1) + return(0); + + emac_gigabit_enable(active_phy_addr[index]); + + /* Start receive process */ + writel(BD_TO_HW((u_int32_t)emac_rx_desc), &adap_emac->RX0HDP); + + debug_emac("- emac_open\n"); + + return(1); +} + +/* EMAC Channel Teardown */ +static void davinci_eth_ch_teardown(int ch) +{ + dv_reg dly = 0xff; + dv_reg cnt; + + debug_emac("+ emac_ch_teardown\n"); + + if (ch == EMAC_CH_TX) { + /* Init TX channel teardown */ + writel(0, &adap_emac->TXTEARDOWN); + do { + /* + * Wait here for Tx teardown completion interrupt to + * occur. Note: A task delay can be called here to pend + * rather than occupying CPU cycles - anyway it has + * been found that teardown takes very few cpu cycles + * and does not affect functionality + */ + dly--; + udelay(1); + if (dly == 0) + break; + cnt = readl(&adap_emac->TX0CP); + } while (cnt != 0xfffffffc); + writel(cnt, &adap_emac->TX0CP); + writel(0, &adap_emac->TX0HDP); + } else { + /* Init RX channel teardown */ + writel(0, &adap_emac->RXTEARDOWN); + do { + /* + * Wait here for Rx teardown completion interrupt to + * occur. Note: A task delay can be called here to pend + * rather than occupying CPU cycles - anyway it has + * been found that teardown takes very few cpu cycles + * and does not affect functionality + */ + dly--; + udelay(1); + if (dly == 0) + break; + cnt = readl(&adap_emac->RX0CP); + } while (cnt != 0xfffffffc); + writel(cnt, &adap_emac->RX0CP); + writel(0, &adap_emac->RX0HDP); + } + + debug_emac("- emac_ch_teardown\n"); +} + +/* Eth device close */ +static void davinci_eth_close(struct eth_device *dev) +{ + debug_emac("+ emac_close\n"); + + davinci_eth_ch_teardown(EMAC_CH_TX); /* TX Channel teardown */ + davinci_eth_ch_teardown(EMAC_CH_RX); /* RX Channel teardown */ + + /* Reset EMAC module and disable interrupts in wrapper */ + writel(1, &adap_emac->SOFTRESET); +#if defined(DAVINCI_EMAC_VERSION2) + writel(1, &adap_ewrap->softrst); +#else + writel(0, &adap_ewrap->EWCTL); +#endif + +#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \ + defined(CONFIG_MACH_DAVINCI_DA850_EVM) + adap_ewrap->c0rxen = adap_ewrap->c1rxen = adap_ewrap->c2rxen = 0; + adap_ewrap->c0txen = adap_ewrap->c1txen = adap_ewrap->c2txen = 0; + adap_ewrap->c0miscen = adap_ewrap->c1miscen = adap_ewrap->c2miscen = 0; +#endif + debug_emac("- emac_close\n"); +} + +static int tx_send_loop = 0; + +/* + * This function sends a single packet on the network and returns + * positive number (number of bytes transmitted) or negative for error + */ +static int davinci_eth_send_packet (struct eth_device *dev, + void *packet, int length) +{ + int ret_status = -1; + int index; + tx_send_loop = 0; + + index = get_active_phy(); + if (index == -1) { + printf(" WARN: emac_send_packet: No link\n"); + return (ret_status); + } + + emac_gigabit_enable(active_phy_addr[index]); + + /* Check packet size and if < EMAC_MIN_ETHERNET_PKT_SIZE, pad it up */ + if (length < EMAC_MIN_ETHERNET_PKT_SIZE) { + length = EMAC_MIN_ETHERNET_PKT_SIZE; + } + + /* Populate the TX descriptor */ + emac_tx_desc->next = 0; + emac_tx_desc->buffer = (u_int8_t *) packet; + emac_tx_desc->buff_off_len = (length & 0xffff); + emac_tx_desc->pkt_flag_len = ((length & 0xffff) | + EMAC_CPPI_SOP_BIT | + EMAC_CPPI_OWNERSHIP_BIT | + EMAC_CPPI_EOP_BIT); + + flush_dcache_range((unsigned long)packet, + (unsigned long)packet + length); + davinci_flush_desc(emac_tx_desc); + + /* Send the packet */ + writel(BD_TO_HW((unsigned long)emac_tx_desc), &adap_emac->TX0HDP); + + /* Wait for packet to complete or link down */ + while (1) { + if (!phy[index].get_link_speed(active_phy_addr[index])) { + davinci_eth_ch_teardown (EMAC_CH_TX); + return (ret_status); + } + + emac_gigabit_enable(active_phy_addr[index]); + + if (readl(&adap_emac->TXINTSTATRAW) & 0x01) { + ret_status = length; + break; + } + tx_send_loop++; + } + + return (ret_status); +} + +/* + * This function handles receipt of a packet from the network + */ +static int davinci_eth_rcv_packet (struct eth_device *dev) +{ + volatile emac_desc *rx_curr_desc; + volatile emac_desc *curr_desc; + volatile emac_desc *tail_desc; + int status, ret = -1; + + davinci_invalidate_rx_descs(); + + rx_curr_desc = emac_rx_active_head; + status = rx_curr_desc->pkt_flag_len; + if ((rx_curr_desc) && ((status & EMAC_CPPI_OWNERSHIP_BIT) == 0)) { + if (status & EMAC_CPPI_RX_ERROR_FRAME) { + /* Error in packet - discard it and requeue desc */ + printf ("WARN: emac_rcv_pkt: Error in packet\n"); + } else { + unsigned long tmp = (unsigned long)rx_curr_desc->buffer; + + invalidate_dcache_range(tmp, tmp + EMAC_RXBUF_SIZE); + NetReceive (rx_curr_desc->buffer, + (rx_curr_desc->buff_off_len & 0xffff)); + ret = rx_curr_desc->buff_off_len & 0xffff; + } + + /* Ack received packet descriptor */ + writel(BD_TO_HW((ulong)rx_curr_desc), &adap_emac->RX0CP); + curr_desc = rx_curr_desc; + emac_rx_active_head = + (volatile emac_desc *) (HW_TO_BD(rx_curr_desc->next)); + + if (status & EMAC_CPPI_EOQ_BIT) { + if (emac_rx_active_head) { + writel(BD_TO_HW((ulong)emac_rx_active_head), + &adap_emac->RX0HDP); + } else { + emac_rx_queue_active = 0; + printf ("INFO:emac_rcv_packet: RX Queue not active\n"); + } + } + + /* Recycle RX descriptor */ + rx_curr_desc->buff_off_len = EMAC_MAX_ETHERNET_PKT_SIZE; + rx_curr_desc->pkt_flag_len = EMAC_CPPI_OWNERSHIP_BIT; + rx_curr_desc->next = 0; + davinci_flush_desc(rx_curr_desc); + + if (emac_rx_active_head == 0) { + printf ("INFO: emac_rcv_pkt: active queue head = 0\n"); + emac_rx_active_head = curr_desc; + emac_rx_active_tail = curr_desc; + if (emac_rx_queue_active != 0) { + writel(BD_TO_HW((ulong)emac_rx_active_head), + &adap_emac->RX0HDP); + printf ("INFO: emac_rcv_pkt: active queue head = 0, HDP fired\n"); + emac_rx_queue_active = 1; + } + } else { + tail_desc = emac_rx_active_tail; + emac_rx_active_tail = curr_desc; + tail_desc->next = BD_TO_HW((ulong) curr_desc); + status = tail_desc->pkt_flag_len; + if (status & EMAC_CPPI_EOQ_BIT) { + davinci_flush_desc(tail_desc); + writel(BD_TO_HW((ulong)curr_desc), + &adap_emac->RX0HDP); + status &= ~EMAC_CPPI_EOQ_BIT; + tail_desc->pkt_flag_len = status; + } + davinci_flush_desc(tail_desc); + } + return (ret); + } + return (0); +} + +/* + * This function initializes the emac hardware. It does NOT initialize + * EMAC modules power or pin multiplexors, that is done by board_init() + * much earlier in bootup process. Returns 1 on success, 0 otherwise. + */ +int davinci_emac_initialize(void) +{ + u_int32_t phy_id; + u_int16_t tmp; + int i; + int ret; + struct eth_device *dev; + + dev = malloc(sizeof *dev); + + if (dev == NULL) + return -1; + + memset(dev, 0, sizeof *dev); + sprintf(dev->name, "DaVinci-EMAC"); + + dev->iobase = 0; + dev->init = davinci_eth_open; + dev->halt = davinci_eth_close; + dev->send = davinci_eth_send_packet; + dev->recv = davinci_eth_rcv_packet; + dev->write_hwaddr = davinci_eth_set_mac_addr; + + eth_register(dev); + + davinci_eth_mdio_enable(); + + /* let the EMAC detect the PHYs */ + udelay(5000); + + for (i = 0; i < 256; i++) { + if (readl(&adap_mdio->ALIVE)) + break; + udelay(1000); + } + + if (i >= 256) { + printf("No ETH PHY detected!!!\n"); + return(0); + } + + /* Find if PHY(s) is/are connected */ + ret = davinci_eth_phy_detect(); + if (!ret) + return(0); + else + debug_emac(" %d ETH PHY detected\n", ret); + + /* Get PHY ID and initialize phy_ops for a detected PHY */ + for (i = 0; i < num_phy; i++) { + if (!davinci_eth_phy_read(active_phy_addr[i], MII_PHYSID1, + &tmp)) { + active_phy_addr[i] = 0xff; + continue; + } + + phy_id = (tmp << 16) & 0xffff0000; + + if (!davinci_eth_phy_read(active_phy_addr[i], MII_PHYSID2, + &tmp)) { + active_phy_addr[i] = 0xff; + continue; + } + + phy_id |= tmp & 0x0000ffff; + + switch (phy_id) { +#ifdef PHY_KSZ8873 + case PHY_KSZ8873: + sprintf(phy[i].name, "KSZ8873 @ 0x%02x", + active_phy_addr[i]); + phy[i].init = ksz8873_init_phy; + phy[i].is_phy_connected = ksz8873_is_phy_connected; + phy[i].get_link_speed = ksz8873_get_link_speed; + phy[i].auto_negotiate = ksz8873_auto_negotiate; + break; +#endif +#ifdef PHY_LXT972 + case PHY_LXT972: + sprintf(phy[i].name, "LXT972 @ 0x%02x", + active_phy_addr[i]); + phy[i].init = lxt972_init_phy; + phy[i].is_phy_connected = lxt972_is_phy_connected; + phy[i].get_link_speed = lxt972_get_link_speed; + phy[i].auto_negotiate = lxt972_auto_negotiate; + break; +#endif +#ifdef PHY_DP83848 + case PHY_DP83848: + sprintf(phy[i].name, "DP83848 @ 0x%02x", + active_phy_addr[i]); + phy[i].init = dp83848_init_phy; + phy[i].is_phy_connected = dp83848_is_phy_connected; + phy[i].get_link_speed = dp83848_get_link_speed; + phy[i].auto_negotiate = dp83848_auto_negotiate; + break; +#endif +#ifdef PHY_ET1011C + case PHY_ET1011C: + sprintf(phy[i].name, "ET1011C @ 0x%02x", + active_phy_addr[i]); + phy[i].init = gen_init_phy; + phy[i].is_phy_connected = gen_is_phy_connected; + phy[i].get_link_speed = et1011c_get_link_speed; + phy[i].auto_negotiate = gen_auto_negotiate; + break; +#endif + default: + sprintf(phy[i].name, "GENERIC @ 0x%02x", + active_phy_addr[i]); + phy[i].init = gen_init_phy; + phy[i].is_phy_connected = gen_is_phy_connected; + phy[i].get_link_speed = gen_get_link_speed; + phy[i].auto_negotiate = gen_auto_negotiate; + } + + debug("Ethernet PHY: %s\n", phy[i].name); + + miiphy_register(phy[i].name, davinci_mii_phy_read, + davinci_mii_phy_write); + } + +#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \ + defined(CONFIG_MACH_DAVINCI_DA850_EVM) && \ + !defined(CONFIG_DRIVER_TI_EMAC_RMII_NO_NEGOTIATE) + for (i = 0; i < num_phy; i++) { + if (phy[i].is_phy_connected(i)) + phy[i].auto_negotiate(i); + } +#endif + return(1); +} diff --git a/qemu/roms/u-boot/drivers/net/davinci_emac.h b/qemu/roms/u-boot/drivers/net/davinci_emac.h new file mode 100644 index 000000000..13cd68f04 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/davinci_emac.h @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2011 Ilya Yanok, Emcraft Systems + * + * Based on: mach-davinci/emac_defs.h + * Copyright (C) 2007 Sergey Kubushyn + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DAVINCI_EMAC_H_ +#define _DAVINCI_EMAC_H_ +/* Ethernet Min/Max packet size */ +#define EMAC_MIN_ETHERNET_PKT_SIZE 60 +#define EMAC_MAX_ETHERNET_PKT_SIZE 1518 +/* Buffer size (should be aligned on 32 byte and cache line) */ +#define EMAC_RXBUF_SIZE ALIGN(ALIGN(EMAC_MAX_ETHERNET_PKT_SIZE, 32),\ + ARCH_DMA_MINALIGN) + +/* Number of RX packet buffers + * NOTE: Only 1 buffer supported as of now + */ +#define EMAC_MAX_RX_BUFFERS 10 + + +/*********************************************** + ******** Internally used macros *************** + ***********************************************/ + +#define EMAC_CH_TX 1 +#define EMAC_CH_RX 0 + +/* Each descriptor occupies 4 words, lets start RX desc's at 0 and + * reserve space for 64 descriptors max + */ +#define EMAC_RX_DESC_BASE 0x0 +#define EMAC_TX_DESC_BASE 0x1000 + +/* EMAC Teardown value */ +#define EMAC_TEARDOWN_VALUE 0xfffffffc + +/* MII Status Register */ +#define MII_STATUS_REG 1 + +/* Number of statistics registers */ +#define EMAC_NUM_STATS 36 + + +/* EMAC Descriptor */ +typedef volatile struct _emac_desc +{ + u_int32_t next; /* Pointer to next descriptor + in chain */ + u_int8_t *buffer; /* Pointer to data buffer */ + u_int32_t buff_off_len; /* Buffer Offset(MSW) and Length(LSW) */ + u_int32_t pkt_flag_len; /* Packet Flags(MSW) and Length(LSW) */ +} emac_desc; + +/* CPPI bit positions */ +#define EMAC_CPPI_SOP_BIT (0x80000000) +#define EMAC_CPPI_EOP_BIT (0x40000000) +#define EMAC_CPPI_OWNERSHIP_BIT (0x20000000) +#define EMAC_CPPI_EOQ_BIT (0x10000000) +#define EMAC_CPPI_TEARDOWN_COMPLETE_BIT (0x08000000) +#define EMAC_CPPI_PASS_CRC_BIT (0x04000000) + +#define EMAC_CPPI_RX_ERROR_FRAME (0x03fc0000) + +#define EMAC_MACCONTROL_MIIEN_ENABLE (0x20) +#define EMAC_MACCONTROL_FULLDUPLEX_ENABLE (0x1) +#define EMAC_MACCONTROL_GIGABIT_ENABLE (1 << 7) +#define EMAC_MACCONTROL_GIGFORCE (1 << 17) +#define EMAC_MACCONTROL_RMIISPEED_100 (1 << 15) + +#define EMAC_MAC_ADDR_MATCH (1 << 19) +#define EMAC_MAC_ADDR_IS_VALID (1 << 20) + +#define EMAC_RXMBPENABLE_RXCAFEN_ENABLE (0x200000) +#define EMAC_RXMBPENABLE_RXBROADEN (0x2000) + + +#define MDIO_CONTROL_IDLE (0x80000000) +#define MDIO_CONTROL_ENABLE (0x40000000) +#define MDIO_CONTROL_FAULT_ENABLE (0x40000) +#define MDIO_CONTROL_FAULT (0x80000) +#define MDIO_USERACCESS0_GO (0x80000000) +#define MDIO_USERACCESS0_WRITE_READ (0x0) +#define MDIO_USERACCESS0_WRITE_WRITE (0x40000000) +#define MDIO_USERACCESS0_ACK (0x20000000) + +/* Ethernet MAC Registers Structure */ +typedef struct { + dv_reg TXIDVER; + dv_reg TXCONTROL; + dv_reg TXTEARDOWN; + u_int8_t RSVD0[4]; + dv_reg RXIDVER; + dv_reg RXCONTROL; + dv_reg RXTEARDOWN; + u_int8_t RSVD1[100]; + dv_reg TXINTSTATRAW; + dv_reg TXINTSTATMASKED; + dv_reg TXINTMASKSET; + dv_reg TXINTMASKCLEAR; + dv_reg MACINVECTOR; + u_int8_t RSVD2[12]; + dv_reg RXINTSTATRAW; + dv_reg RXINTSTATMASKED; + dv_reg RXINTMASKSET; + dv_reg RXINTMASKCLEAR; + dv_reg MACINTSTATRAW; + dv_reg MACINTSTATMASKED; + dv_reg MACINTMASKSET; + dv_reg MACINTMASKCLEAR; + u_int8_t RSVD3[64]; + dv_reg RXMBPENABLE; + dv_reg RXUNICASTSET; + dv_reg RXUNICASTCLEAR; + dv_reg RXMAXLEN; + dv_reg RXBUFFEROFFSET; + dv_reg RXFILTERLOWTHRESH; + u_int8_t RSVD4[8]; + dv_reg RX0FLOWTHRESH; + dv_reg RX1FLOWTHRESH; + dv_reg RX2FLOWTHRESH; + dv_reg RX3FLOWTHRESH; + dv_reg RX4FLOWTHRESH; + dv_reg RX5FLOWTHRESH; + dv_reg RX6FLOWTHRESH; + dv_reg RX7FLOWTHRESH; + dv_reg RX0FREEBUFFER; + dv_reg RX1FREEBUFFER; + dv_reg RX2FREEBUFFER; + dv_reg RX3FREEBUFFER; + dv_reg RX4FREEBUFFER; + dv_reg RX5FREEBUFFER; + dv_reg RX6FREEBUFFER; + dv_reg RX7FREEBUFFER; + dv_reg MACCONTROL; + dv_reg MACSTATUS; + dv_reg EMCONTROL; + dv_reg FIFOCONTROL; + dv_reg MACCONFIG; + dv_reg SOFTRESET; + u_int8_t RSVD5[88]; + dv_reg MACSRCADDRLO; + dv_reg MACSRCADDRHI; + dv_reg MACHASH1; + dv_reg MACHASH2; + dv_reg BOFFTEST; + dv_reg TPACETEST; + dv_reg RXPAUSE; + dv_reg TXPAUSE; + u_int8_t RSVD6[16]; + dv_reg RXGOODFRAMES; + dv_reg RXBCASTFRAMES; + dv_reg RXMCASTFRAMES; + dv_reg RXPAUSEFRAMES; + dv_reg RXCRCERRORS; + dv_reg RXALIGNCODEERRORS; + dv_reg RXOVERSIZED; + dv_reg RXJABBER; + dv_reg RXUNDERSIZED; + dv_reg RXFRAGMENTS; + dv_reg RXFILTERED; + dv_reg RXQOSFILTERED; + dv_reg RXOCTETS; + dv_reg TXGOODFRAMES; + dv_reg TXBCASTFRAMES; + dv_reg TXMCASTFRAMES; + dv_reg TXPAUSEFRAMES; + dv_reg TXDEFERRED; + dv_reg TXCOLLISION; + dv_reg TXSINGLECOLL; + dv_reg TXMULTICOLL; + dv_reg TXEXCESSIVECOLL; + dv_reg TXLATECOLL; + dv_reg TXUNDERRUN; + dv_reg TXCARRIERSENSE; + dv_reg TXOCTETS; + dv_reg FRAME64; + dv_reg FRAME65T127; + dv_reg FRAME128T255; + dv_reg FRAME256T511; + dv_reg FRAME512T1023; + dv_reg FRAME1024TUP; + dv_reg NETOCTETS; + dv_reg RXSOFOVERRUNS; + dv_reg RXMOFOVERRUNS; + dv_reg RXDMAOVERRUNS; + u_int8_t RSVD7[624]; + dv_reg MACADDRLO; + dv_reg MACADDRHI; + dv_reg MACINDEX; + u_int8_t RSVD8[244]; + dv_reg TX0HDP; + dv_reg TX1HDP; + dv_reg TX2HDP; + dv_reg TX3HDP; + dv_reg TX4HDP; + dv_reg TX5HDP; + dv_reg TX6HDP; + dv_reg TX7HDP; + dv_reg RX0HDP; + dv_reg RX1HDP; + dv_reg RX2HDP; + dv_reg RX3HDP; + dv_reg RX4HDP; + dv_reg RX5HDP; + dv_reg RX6HDP; + dv_reg RX7HDP; + dv_reg TX0CP; + dv_reg TX1CP; + dv_reg TX2CP; + dv_reg TX3CP; + dv_reg TX4CP; + dv_reg TX5CP; + dv_reg TX6CP; + dv_reg TX7CP; + dv_reg RX0CP; + dv_reg RX1CP; + dv_reg RX2CP; + dv_reg RX3CP; + dv_reg RX4CP; + dv_reg RX5CP; + dv_reg RX6CP; + dv_reg RX7CP; +} emac_regs; + +/* EMAC Wrapper Registers Structure */ +typedef struct { +#ifdef DAVINCI_EMAC_VERSION2 + dv_reg idver; + dv_reg softrst; + dv_reg emctrl; + dv_reg c0rxthreshen; + dv_reg c0rxen; + dv_reg c0txen; + dv_reg c0miscen; + dv_reg c1rxthreshen; + dv_reg c1rxen; + dv_reg c1txen; + dv_reg c1miscen; + dv_reg c2rxthreshen; + dv_reg c2rxen; + dv_reg c2txen; + dv_reg c2miscen; + dv_reg c0rxthreshstat; + dv_reg c0rxstat; + dv_reg c0txstat; + dv_reg c0miscstat; + dv_reg c1rxthreshstat; + dv_reg c1rxstat; + dv_reg c1txstat; + dv_reg c1miscstat; + dv_reg c2rxthreshstat; + dv_reg c2rxstat; + dv_reg c2txstat; + dv_reg c2miscstat; + dv_reg c0rximax; + dv_reg c0tximax; + dv_reg c1rximax; + dv_reg c1tximax; + dv_reg c2rximax; + dv_reg c2tximax; +#else + u_int8_t RSVD0[4100]; + dv_reg EWCTL; + dv_reg EWINTTCNT; +#endif +} ewrap_regs; + +/* EMAC MDIO Registers Structure */ +typedef struct { + dv_reg VERSION; + dv_reg CONTROL; + dv_reg ALIVE; + dv_reg LINK; + dv_reg LINKINTRAW; + dv_reg LINKINTMASKED; + u_int8_t RSVD0[8]; + dv_reg USERINTRAW; + dv_reg USERINTMASKED; + dv_reg USERINTMASKSET; + dv_reg USERINTMASKCLEAR; + u_int8_t RSVD1[80]; + dv_reg USERACCESS0; + dv_reg USERPHYSEL0; + dv_reg USERACCESS1; + dv_reg USERPHYSEL1; +} mdio_regs; + +int davinci_eth_phy_read(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t *data); +int davinci_eth_phy_write(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t data); + +typedef struct { + char name[64]; + int (*init)(int phy_addr); + int (*is_phy_connected)(int phy_addr); + int (*get_link_speed)(int phy_addr); + int (*auto_negotiate)(int phy_addr); +} phy_t; + +#endif /* _DAVINCI_EMAC_H_ */ diff --git a/qemu/roms/u-boot/drivers/net/dc2114x.c b/qemu/roms/u-boot/drivers/net/dc2114x.c new file mode 100644 index 000000000..799839c4f --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/dc2114x.c @@ -0,0 +1,760 @@ +/* + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#undef DEBUG_SROM +#undef DEBUG_SROM2 + +#undef UPDATE_SROM + +/* PCI Registers. + */ +#define PCI_CFDA_PSM 0x43 + +#define CFRV_RN 0x000000f0 /* Revision Number */ + +#define WAKEUP 0x00 /* Power Saving Wakeup */ +#define SLEEP 0x80 /* Power Saving Sleep Mode */ + +#define DC2114x_BRK 0x0020 /* CFRV break between DC21142 & DC21143 */ + +/* Ethernet chip registers. + */ +#define DE4X5_BMR 0x000 /* Bus Mode Register */ +#define DE4X5_TPD 0x008 /* Transmit Poll Demand Reg */ +#define DE4X5_RRBA 0x018 /* RX Ring Base Address Reg */ +#define DE4X5_TRBA 0x020 /* TX Ring Base Address Reg */ +#define DE4X5_STS 0x028 /* Status Register */ +#define DE4X5_OMR 0x030 /* Operation Mode Register */ +#define DE4X5_SICR 0x068 /* SIA Connectivity Register */ +#define DE4X5_APROM 0x048 /* Ethernet Address PROM */ + +/* Register bits. + */ +#define BMR_SWR 0x00000001 /* Software Reset */ +#define STS_TS 0x00700000 /* Transmit Process State */ +#define STS_RS 0x000e0000 /* Receive Process State */ +#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ +#define OMR_SR 0x00000002 /* Start/Stop Receive */ +#define OMR_PS 0x00040000 /* Port Select */ +#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ +#define OMR_PM 0x00000080 /* Pass All Multicast */ + +/* Descriptor bits. + */ +#define R_OWN 0x80000000 /* Own Bit */ +#define RD_RER 0x02000000 /* Receive End Of Ring */ +#define RD_LS 0x00000100 /* Last Descriptor */ +#define RD_ES 0x00008000 /* Error Summary */ +#define TD_TER 0x02000000 /* Transmit End Of Ring */ +#define T_OWN 0x80000000 /* Own Bit */ +#define TD_LS 0x40000000 /* Last Segment */ +#define TD_FS 0x20000000 /* First Segment */ +#define TD_ES 0x00008000 /* Error Summary */ +#define TD_SET 0x08000000 /* Setup Packet */ + +/* The EEPROM commands include the alway-set leading bit. */ +#define SROM_WRITE_CMD 5 +#define SROM_READ_CMD 6 +#define SROM_ERASE_CMD 7 + +#define SROM_HWADD 0x0014 /* Hardware Address offset in SROM */ +#define SROM_RD 0x00004000 /* Read from Boot ROM */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x4801 +#define EE_WRITE_1 0x4805 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define SROM_SR 0x00000800 /* Select Serial ROM when set */ + +#define DT_IN 0x00000004 /* Serial Data In */ +#define DT_CLK 0x00000002 /* Serial ROM Clock */ +#define DT_CS 0x00000001 /* Serial ROM Chip Select */ + +#define POLL_DEMAND 1 + +#ifdef CONFIG_TULIP_FIX_DAVICOM +#define RESET_DM9102(dev) {\ + unsigned long i;\ + i=INL(dev, 0x0);\ + udelay(1000);\ + OUTL(dev, i | BMR_SWR, DE4X5_BMR);\ + udelay(1000);\ +} +#else +#define RESET_DE4X5(dev) {\ + int i;\ + i=INL(dev, DE4X5_BMR);\ + udelay(1000);\ + OUTL(dev, i | BMR_SWR, DE4X5_BMR);\ + udelay(1000);\ + OUTL(dev, i, DE4X5_BMR);\ + udelay(1000);\ + for (i=0;i<5;i++) {INL(dev, DE4X5_BMR); udelay(10000);}\ + udelay(1000);\ +} +#endif + +#define START_DE4X5(dev) {\ + s32 omr; \ + omr = INL(dev, DE4X5_OMR);\ + omr |= OMR_ST | OMR_SR;\ + OUTL(dev, omr, DE4X5_OMR); /* Enable the TX and/or RX */\ +} + +#define STOP_DE4X5(dev) {\ + s32 omr; \ + omr = INL(dev, DE4X5_OMR);\ + omr &= ~(OMR_ST|OMR_SR);\ + OUTL(dev, omr, DE4X5_OMR); /* Disable the TX and/or RX */ \ +} + +#define NUM_RX_DESC PKTBUFSRX +#ifndef CONFIG_TULIP_FIX_DAVICOM + #define NUM_TX_DESC 1 /* Number of TX descriptors */ +#else + #define NUM_TX_DESC 4 +#endif +#define RX_BUFF_SZ PKTSIZE_ALIGN + +#define TOUT_LOOP 1000000 + +#define SETUP_FRAME_LEN 192 +#define ETH_ALEN 6 + +struct de4x5_desc { + volatile s32 status; + u32 des1; + u32 buf; + u32 next; +}; + +static struct de4x5_desc rx_ring[NUM_RX_DESC] __attribute__ ((aligned(32))); /* RX descriptor ring */ +static struct de4x5_desc tx_ring[NUM_TX_DESC] __attribute__ ((aligned(32))); /* TX descriptor ring */ +static int rx_new; /* RX descriptor ring pointer */ +static int tx_new; /* TX descriptor ring pointer */ + +static char rxRingSize; +static char txRingSize; + +#if defined(UPDATE_SROM) || !defined(CONFIG_TULIP_FIX_DAVICOM) +static void sendto_srom(struct eth_device* dev, u_int command, u_long addr); +static int getfrom_srom(struct eth_device* dev, u_long addr); +static int do_eeprom_cmd(struct eth_device *dev, u_long ioaddr,int cmd,int cmd_len); +static int do_read_eeprom(struct eth_device *dev,u_long ioaddr,int location,int addr_len); +#endif /* UPDATE_SROM || !CONFIG_TULIP_FIX_DAVICOM */ +#ifdef UPDATE_SROM +static int write_srom(struct eth_device *dev, u_long ioaddr, int index, int new_value); +static void update_srom(struct eth_device *dev, bd_t *bis); +#endif +#ifndef CONFIG_TULIP_FIX_DAVICOM +static int read_srom(struct eth_device *dev, u_long ioaddr, int index); +static void read_hw_addr(struct eth_device* dev, bd_t * bis); +#endif /* CONFIG_TULIP_FIX_DAVICOM */ +static void send_setup_frame(struct eth_device* dev, bd_t * bis); + +static int dc21x4x_init(struct eth_device* dev, bd_t* bis); +static int dc21x4x_send(struct eth_device *dev, void *packet, int length); +static int dc21x4x_recv(struct eth_device* dev); +static void dc21x4x_halt(struct eth_device* dev); +#ifdef CONFIG_TULIP_SELECT_MEDIA +extern void dc21x4x_select_media(struct eth_device* dev); +#endif + +#if defined(CONFIG_E500) +#define phys_to_bus(a) (a) +#else +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) +#endif + +static int INL(struct eth_device* dev, u_long addr) +{ + return le32_to_cpu(*(volatile u_long *)(addr + dev->iobase)); +} + +static void OUTL(struct eth_device* dev, int command, u_long addr) +{ + *(volatile u_long *)(addr + dev->iobase) = cpu_to_le32(command); +} + +static struct pci_device_id supported[] = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142 }, +#ifdef CONFIG_TULIP_FIX_DAVICOM + { PCI_VENDOR_ID_DAVICOM, PCI_DEVICE_ID_DAVICOM_DM9102A }, +#endif + { } +}; + +int dc21x4x_initialize(bd_t *bis) +{ + int idx=0; + int card_number = 0; + unsigned int cfrv; + unsigned char timer; + pci_dev_t devbusfn; + unsigned int iobase; + unsigned short status; + struct eth_device* dev; + + while(1) { + devbusfn = pci_find_devices(supported, idx++); + if (devbusfn == -1) { + break; + } + + /* Get the chip configuration revision register. */ + pci_read_config_dword(devbusfn, PCI_REVISION_ID, &cfrv); + +#ifndef CONFIG_TULIP_FIX_DAVICOM + if ((cfrv & CFRV_RN) < DC2114x_BRK ) { + printf("Error: The chip is not DC21143.\n"); + continue; + } +#endif + + pci_read_config_word(devbusfn, PCI_COMMAND, &status); + status |= +#ifdef CONFIG_TULIP_USE_IO + PCI_COMMAND_IO | +#else + PCI_COMMAND_MEMORY | +#endif + PCI_COMMAND_MASTER; + pci_write_config_word(devbusfn, PCI_COMMAND, status); + + pci_read_config_word(devbusfn, PCI_COMMAND, &status); +#ifdef CONFIG_TULIP_USE_IO + if (!(status & PCI_COMMAND_IO)) { + printf("Error: Can not enable I/O access.\n"); + continue; + } +#else + if (!(status & PCI_COMMAND_MEMORY)) { + printf("Error: Can not enable MEMORY access.\n"); + continue; + } +#endif + + if (!(status & PCI_COMMAND_MASTER)) { + printf("Error: Can not enable Bus Mastering.\n"); + continue; + } + + /* Check the latency timer for values >= 0x60. */ + pci_read_config_byte(devbusfn, PCI_LATENCY_TIMER, &timer); + + if (timer < 0x60) { + pci_write_config_byte(devbusfn, PCI_LATENCY_TIMER, 0x60); + } + +#ifdef CONFIG_TULIP_USE_IO + /* read BAR for memory space access */ + pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &iobase); + iobase &= PCI_BASE_ADDRESS_IO_MASK; +#else + /* read BAR for memory space access */ + pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_1, &iobase); + iobase &= PCI_BASE_ADDRESS_MEM_MASK; +#endif + debug ("dc21x4x: DEC 21142 PCI Device @0x%x\n", iobase); + + dev = (struct eth_device*) malloc(sizeof *dev); + + if (!dev) { + printf("Can not allocalte memory of dc21x4x\n"); + break; + } + memset(dev, 0, sizeof(*dev)); + +#ifdef CONFIG_TULIP_FIX_DAVICOM + sprintf(dev->name, "Davicom#%d", card_number); +#else + sprintf(dev->name, "dc21x4x#%d", card_number); +#endif + +#ifdef CONFIG_TULIP_USE_IO + dev->iobase = pci_io_to_phys(devbusfn, iobase); +#else + dev->iobase = pci_mem_to_phys(devbusfn, iobase); +#endif + dev->priv = (void*) devbusfn; + dev->init = dc21x4x_init; + dev->halt = dc21x4x_halt; + dev->send = dc21x4x_send; + dev->recv = dc21x4x_recv; + + /* Ensure we're not sleeping. */ + pci_write_config_byte(devbusfn, PCI_CFDA_PSM, WAKEUP); + + udelay(10 * 1000); + +#ifndef CONFIG_TULIP_FIX_DAVICOM + read_hw_addr(dev, bis); +#endif + eth_register(dev); + + card_number++; + } + + return card_number; +} + +static int dc21x4x_init(struct eth_device* dev, bd_t* bis) +{ + int i; + int devbusfn = (int) dev->priv; + + /* Ensure we're not sleeping. */ + pci_write_config_byte(devbusfn, PCI_CFDA_PSM, WAKEUP); + +#ifdef CONFIG_TULIP_FIX_DAVICOM + RESET_DM9102(dev); +#else + RESET_DE4X5(dev); +#endif + + if ((INL(dev, DE4X5_STS) & (STS_TS | STS_RS)) != 0) { + printf("Error: Cannot reset ethernet controller.\n"); + return -1; + } + +#ifdef CONFIG_TULIP_SELECT_MEDIA + dc21x4x_select_media(dev); +#else + OUTL(dev, OMR_SDP | OMR_PS | OMR_PM, DE4X5_OMR); +#endif + + for (i = 0; i < NUM_RX_DESC; i++) { + rx_ring[i].status = cpu_to_le32(R_OWN); + rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + rx_ring[i].buf = cpu_to_le32(phys_to_bus((u32) NetRxPackets[i])); +#ifdef CONFIG_TULIP_FIX_DAVICOM + rx_ring[i].next = cpu_to_le32(phys_to_bus((u32) &rx_ring[(i+1) % NUM_RX_DESC])); +#else + rx_ring[i].next = 0; +#endif + } + + for (i=0; i < NUM_TX_DESC; i++) { + tx_ring[i].status = 0; + tx_ring[i].des1 = 0; + tx_ring[i].buf = 0; + +#ifdef CONFIG_TULIP_FIX_DAVICOM + tx_ring[i].next = cpu_to_le32(phys_to_bus((u32) &tx_ring[(i+1) % NUM_TX_DESC])); +#else + tx_ring[i].next = 0; +#endif + } + + rxRingSize = NUM_RX_DESC; + txRingSize = NUM_TX_DESC; + + /* Write the end of list marker to the descriptor lists. */ + rx_ring[rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); + tx_ring[txRingSize - 1].des1 |= cpu_to_le32(TD_TER); + + /* Tell the adapter where the TX/RX rings are located. */ + OUTL(dev, phys_to_bus((u32) &rx_ring), DE4X5_RRBA); + OUTL(dev, phys_to_bus((u32) &tx_ring), DE4X5_TRBA); + + START_DE4X5(dev); + + tx_new = 0; + rx_new = 0; + + send_setup_frame(dev, bis); + + return 0; +} + +static int dc21x4x_send(struct eth_device *dev, void *packet, int length) +{ + int status = -1; + int i; + + if (length <= 0) { + printf("%s: bad packet size: %d\n", dev->name, length); + goto Done; + } + + for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) { + if (i >= TOUT_LOOP) { + printf("%s: tx error buffer not ready\n", dev->name); + goto Done; + } + } + + tx_ring[tx_new].buf = cpu_to_le32(phys_to_bus((u32) packet)); + tx_ring[tx_new].des1 = cpu_to_le32(TD_TER | TD_LS | TD_FS | length); + tx_ring[tx_new].status = cpu_to_le32(T_OWN); + + OUTL(dev, POLL_DEMAND, DE4X5_TPD); + + for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) { + if (i >= TOUT_LOOP) { + printf(".%s: tx buffer not ready\n", dev->name); + goto Done; + } + } + + if (le32_to_cpu(tx_ring[tx_new].status) & TD_ES) { +#if 0 /* test-only */ + printf("TX error status = 0x%08X\n", + le32_to_cpu(tx_ring[tx_new].status)); +#endif + tx_ring[tx_new].status = 0x0; + goto Done; + } + + status = length; + + Done: + tx_new = (tx_new+1) % NUM_TX_DESC; + return status; +} + +static int dc21x4x_recv(struct eth_device* dev) +{ + s32 status; + int length = 0; + + for ( ; ; ) { + status = (s32)le32_to_cpu(rx_ring[rx_new].status); + + if (status & R_OWN) { + break; + } + + if (status & RD_LS) { + /* Valid frame status. + */ + if (status & RD_ES) { + + /* There was an error. + */ + printf("RX error status = 0x%08X\n", status); + } else { + /* A valid frame received. + */ + length = (le32_to_cpu(rx_ring[rx_new].status) >> 16); + + /* Pass the packet up to the protocol + * layers. + */ + NetReceive(NetRxPackets[rx_new], length - 4); + } + + /* Change buffer ownership for this frame, back + * to the adapter. + */ + rx_ring[rx_new].status = cpu_to_le32(R_OWN); + } + + /* Update entry information. + */ + rx_new = (rx_new + 1) % rxRingSize; + } + + return length; +} + +static void dc21x4x_halt(struct eth_device* dev) +{ + int devbusfn = (int) dev->priv; + + STOP_DE4X5(dev); + OUTL(dev, 0, DE4X5_SICR); + + pci_write_config_byte(devbusfn, PCI_CFDA_PSM, SLEEP); +} + +static void send_setup_frame(struct eth_device* dev, bd_t *bis) +{ + int i; + char setup_frame[SETUP_FRAME_LEN]; + char *pa = &setup_frame[0]; + + memset(pa, 0xff, SETUP_FRAME_LEN); + + for (i = 0; i < ETH_ALEN; i++) { + *(pa + (i & 1)) = dev->enetaddr[i]; + if (i & 0x01) { + pa += 4; + } + } + + for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) { + if (i >= TOUT_LOOP) { + printf("%s: tx error buffer not ready\n", dev->name); + goto Done; + } + } + + tx_ring[tx_new].buf = cpu_to_le32(phys_to_bus((u32) &setup_frame[0])); + tx_ring[tx_new].des1 = cpu_to_le32(TD_TER | TD_SET| SETUP_FRAME_LEN); + tx_ring[tx_new].status = cpu_to_le32(T_OWN); + + OUTL(dev, POLL_DEMAND, DE4X5_TPD); + + for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) { + if (i >= TOUT_LOOP) { + printf("%s: tx buffer not ready\n", dev->name); + goto Done; + } + } + + if (le32_to_cpu(tx_ring[tx_new].status) != 0x7FFFFFFF) { + printf("TX error status2 = 0x%08X\n", le32_to_cpu(tx_ring[tx_new].status)); + } + tx_new = (tx_new+1) % NUM_TX_DESC; + +Done: + return; +} + +#if defined(UPDATE_SROM) || !defined(CONFIG_TULIP_FIX_DAVICOM) +/* SROM Read and write routines. + */ +static void +sendto_srom(struct eth_device* dev, u_int command, u_long addr) +{ + OUTL(dev, command, addr); + udelay(1); +} + +static int +getfrom_srom(struct eth_device* dev, u_long addr) +{ + s32 tmp; + + tmp = INL(dev, addr); + udelay(1); + + return tmp; +} + +/* Note: this routine returns extra data bits for size detection. */ +static int do_read_eeprom(struct eth_device *dev, u_long ioaddr, int location, int addr_len) +{ + int i; + unsigned retval = 0; + int read_cmd = location | (SROM_READ_CMD << addr_len); + + sendto_srom(dev, SROM_RD | SROM_SR, ioaddr); + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr); + +#ifdef DEBUG_SROM + printf(" EEPROM read at %d ", location); +#endif + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS | dataval, ioaddr); + udelay(10); + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS | dataval | DT_CLK, ioaddr); + udelay(10); +#ifdef DEBUG_SROM2 + printf("%X", getfrom_srom(dev, ioaddr) & 15); +#endif + retval = (retval << 1) | ((getfrom_srom(dev, ioaddr) & EE_DATA_READ) ? 1 : 0); + } + + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr); + +#ifdef DEBUG_SROM2 + printf(" :%X:", getfrom_srom(dev, ioaddr) & 15); +#endif + + for (i = 16; i > 0; i--) { + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS | DT_CLK, ioaddr); + udelay(10); +#ifdef DEBUG_SROM2 + printf("%X", getfrom_srom(dev, ioaddr) & 15); +#endif + retval = (retval << 1) | ((getfrom_srom(dev, ioaddr) & EE_DATA_READ) ? 1 : 0); + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr); + udelay(10); + } + + /* Terminate the EEPROM access. */ + sendto_srom(dev, SROM_RD | SROM_SR, ioaddr); + +#ifdef DEBUG_SROM2 + printf(" EEPROM value at %d is %5.5x.\n", location, retval); +#endif + + return retval; +} +#endif /* UPDATE_SROM || !CONFIG_TULIP_FIX_DAVICOM */ + +/* This executes a generic EEPROM command, typically a write or write + * enable. It returns the data output from the EEPROM, and thus may + * also be used for reads. + */ +#if defined(UPDATE_SROM) || !defined(CONFIG_TULIP_FIX_DAVICOM) +static int do_eeprom_cmd(struct eth_device *dev, u_long ioaddr, int cmd, int cmd_len) +{ + unsigned retval = 0; + +#ifdef DEBUG_SROM + printf(" EEPROM op 0x%x: ", cmd); +#endif + + sendto_srom(dev,SROM_RD | SROM_SR | DT_CS | DT_CLK, ioaddr); + + /* Shift the command bits out. */ + do { + short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0; + sendto_srom(dev,dataval, ioaddr); + udelay(10); + +#ifdef DEBUG_SROM2 + printf("%X", getfrom_srom(dev,ioaddr) & 15); +#endif + + sendto_srom(dev,dataval | DT_CLK, ioaddr); + udelay(10); + retval = (retval << 1) | ((getfrom_srom(dev,ioaddr) & EE_DATA_READ) ? 1 : 0); + } while (--cmd_len >= 0); + sendto_srom(dev,SROM_RD | SROM_SR | DT_CS, ioaddr); + + /* Terminate the EEPROM access. */ + sendto_srom(dev,SROM_RD | SROM_SR, ioaddr); + +#ifdef DEBUG_SROM + printf(" EEPROM result is 0x%5.5x.\n", retval); +#endif + + return retval; +} +#endif /* UPDATE_SROM || !CONFIG_TULIP_FIX_DAVICOM */ + +#ifndef CONFIG_TULIP_FIX_DAVICOM +static int read_srom(struct eth_device *dev, u_long ioaddr, int index) +{ + int ee_addr_size = do_read_eeprom(dev, ioaddr, 0xff, 8) & 0x40000 ? 8 : 6; + + return do_eeprom_cmd(dev, ioaddr, + (((SROM_READ_CMD << ee_addr_size) | index) << 16) + | 0xffff, 3 + ee_addr_size + 16); +} +#endif /* CONFIG_TULIP_FIX_DAVICOM */ + +#ifdef UPDATE_SROM +static int write_srom(struct eth_device *dev, u_long ioaddr, int index, int new_value) +{ + int ee_addr_size = do_read_eeprom(dev, ioaddr, 0xff, 8) & 0x40000 ? 8 : 6; + int i; + unsigned short newval; + + udelay(10*1000); /* test-only */ + +#ifdef DEBUG_SROM + printf("ee_addr_size=%d.\n", ee_addr_size); + printf("Writing new entry 0x%4.4x to offset %d.\n", new_value, index); +#endif + + /* Enable programming modes. */ + do_eeprom_cmd(dev, ioaddr, (0x4f << (ee_addr_size-4)), 3+ee_addr_size); + + /* Do the actual write. */ + do_eeprom_cmd(dev, ioaddr, + (((SROM_WRITE_CMD<enetaddr[0]); + int i, j = 0; + + for (i = 0; i < (ETH_ALEN >> 1); i++) { + tmp = read_srom(dev, DE4X5_APROM, ((SROM_HWADD >> 1) + i)); + *p = le16_to_cpu(tmp); + j += *p++; + } + + if ((j == 0) || (j == 0x2fffd)) { + memset (dev->enetaddr, 0, ETH_ALEN); + debug ("Warning: can't read HW address from SROM.\n"); + goto Done; + } + + return; + +Done: +#ifdef UPDATE_SROM + update_srom(dev, bis); +#endif + return; +} +#endif /* CONFIG_TULIP_FIX_DAVICOM */ + +#ifdef UPDATE_SROM +static void update_srom(struct eth_device *dev, bd_t *bis) +{ + int i; + static unsigned short eeprom[0x40] = { + 0x140b, 0x6610, 0x0000, 0x0000, /* 00 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 04 */ + 0x00a3, 0x0103, 0x0000, 0x0000, /* 08 */ + 0x0000, 0x1f00, 0x0000, 0x0000, /* 0c */ + 0x0108, 0x038d, 0x0000, 0x0000, /* 10 */ + 0xe078, 0x0001, 0x0040, 0x0018, /* 14 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 18 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 1c */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 20 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 2c */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 30 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 34 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 38 */ + 0x0000, 0x0000, 0x0000, 0x4e07, /* 3c */ + }; + uchar enetaddr[6]; + + /* Ethernet Addr... */ + if (!eth_getenv_enetaddr("ethaddr", enetaddr)) + return; + eeprom[0x0a] = (enetaddr[1] << 8) | enetaddr[0]; + eeprom[0x0b] = (enetaddr[3] << 8) | enetaddr[2]; + eeprom[0x0c] = (enetaddr[5] << 8) | enetaddr[4]; + + for (i=0; i<0x40; i++) { + write_srom(dev, DE4X5_APROM, i, eeprom[i]); + } +} +#endif /* UPDATE_SROM */ diff --git a/qemu/roms/u-boot/drivers/net/designware.c b/qemu/roms/u-boot/drivers/net/designware.c new file mode 100644 index 000000000..78751b260 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/designware.c @@ -0,0 +1,449 @@ +/* + * (C) Copyright 2010 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Designware ethernet IP driver for u-boot + */ + +#include +#include +#include +#include +#include +#include +#include "designware.h" + +#if !defined(CONFIG_PHYLIB) +# error "DesignWare Ether MAC requires PHYLIB - missing CONFIG_PHYLIB" +#endif + +static int dw_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct eth_mac_regs *mac_p = bus->priv; + ulong start; + u16 miiaddr; + int timeout = CONFIG_MDIO_TIMEOUT; + + miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) | + ((reg << MIIREGSHIFT) & MII_REGMSK); + + writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr); + + start = get_timer(0); + while (get_timer(start) < timeout) { + if (!(readl(&mac_p->miiaddr) & MII_BUSY)) + return readl(&mac_p->miidata); + udelay(10); + }; + + return -1; +} + +static int dw_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct eth_mac_regs *mac_p = bus->priv; + ulong start; + u16 miiaddr; + int ret = -1, timeout = CONFIG_MDIO_TIMEOUT; + + writel(val, &mac_p->miidata); + miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) | + ((reg << MIIREGSHIFT) & MII_REGMSK) | MII_WRITE; + + writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr); + + start = get_timer(0); + while (get_timer(start) < timeout) { + if (!(readl(&mac_p->miiaddr) & MII_BUSY)) { + ret = 0; + break; + } + udelay(10); + }; + + return ret; +} + +static int dw_mdio_init(char *name, struct eth_mac_regs *mac_regs_p) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) { + printf("Failed to allocate MDIO bus\n"); + return -1; + } + + bus->read = dw_mdio_read; + bus->write = dw_mdio_write; + sprintf(bus->name, name); + + bus->priv = (void *)mac_regs_p; + + return mdio_register(bus); +} + +static void tx_descs_init(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + struct dmamacdescr *desc_table_p = &priv->tx_mac_descrtable[0]; + char *txbuffs = &priv->txbuffs[0]; + struct dmamacdescr *desc_p; + u32 idx; + + for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) { + desc_p = &desc_table_p[idx]; + desc_p->dmamac_addr = &txbuffs[idx * CONFIG_ETH_BUFSIZE]; + desc_p->dmamac_next = &desc_table_p[idx + 1]; + +#if defined(CONFIG_DW_ALTDESCRIPTOR) + desc_p->txrx_status &= ~(DESC_TXSTS_TXINT | DESC_TXSTS_TXLAST | + DESC_TXSTS_TXFIRST | DESC_TXSTS_TXCRCDIS | \ + DESC_TXSTS_TXCHECKINSCTRL | \ + DESC_TXSTS_TXRINGEND | DESC_TXSTS_TXPADDIS); + + desc_p->txrx_status |= DESC_TXSTS_TXCHAIN; + desc_p->dmamac_cntl = 0; + desc_p->txrx_status &= ~(DESC_TXSTS_MSK | DESC_TXSTS_OWNBYDMA); +#else + desc_p->dmamac_cntl = DESC_TXCTRL_TXCHAIN; + desc_p->txrx_status = 0; +#endif + } + + /* Correcting the last pointer of the chain */ + desc_p->dmamac_next = &desc_table_p[0]; + + /* Flush all Tx buffer descriptors at once */ + flush_dcache_range((unsigned int)priv->tx_mac_descrtable, + (unsigned int)priv->tx_mac_descrtable + + sizeof(priv->tx_mac_descrtable)); + + writel((ulong)&desc_table_p[0], &dma_p->txdesclistaddr); + priv->tx_currdescnum = 0; +} + +static void rx_descs_init(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + struct dmamacdescr *desc_table_p = &priv->rx_mac_descrtable[0]; + char *rxbuffs = &priv->rxbuffs[0]; + struct dmamacdescr *desc_p; + u32 idx; + + /* Before passing buffers to GMAC we need to make sure zeros + * written there right after "priv" structure allocation were + * flushed into RAM. + * Otherwise there's a chance to get some of them flushed in RAM when + * GMAC is already pushing data to RAM via DMA. This way incoming from + * GMAC data will be corrupted. */ + flush_dcache_range((unsigned int)rxbuffs, (unsigned int)rxbuffs + + RX_TOTAL_BUFSIZE); + + for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) { + desc_p = &desc_table_p[idx]; + desc_p->dmamac_addr = &rxbuffs[idx * CONFIG_ETH_BUFSIZE]; + desc_p->dmamac_next = &desc_table_p[idx + 1]; + + desc_p->dmamac_cntl = + (MAC_MAX_FRAME_SZ & DESC_RXCTRL_SIZE1MASK) | \ + DESC_RXCTRL_RXCHAIN; + + desc_p->txrx_status = DESC_RXSTS_OWNBYDMA; + } + + /* Correcting the last pointer of the chain */ + desc_p->dmamac_next = &desc_table_p[0]; + + /* Flush all Rx buffer descriptors at once */ + flush_dcache_range((unsigned int)priv->rx_mac_descrtable, + (unsigned int)priv->rx_mac_descrtable + + sizeof(priv->rx_mac_descrtable)); + + writel((ulong)&desc_table_p[0], &dma_p->rxdesclistaddr); + priv->rx_currdescnum = 0; +} + +static int dw_write_hwaddr(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_mac_regs *mac_p = priv->mac_regs_p; + u32 macid_lo, macid_hi; + u8 *mac_id = &dev->enetaddr[0]; + + macid_lo = mac_id[0] + (mac_id[1] << 8) + (mac_id[2] << 16) + + (mac_id[3] << 24); + macid_hi = mac_id[4] + (mac_id[5] << 8); + + writel(macid_hi, &mac_p->macaddr0hi); + writel(macid_lo, &mac_p->macaddr0lo); + + return 0; +} + +static void dw_adjust_link(struct eth_mac_regs *mac_p, + struct phy_device *phydev) +{ + u32 conf = readl(&mac_p->conf) | FRAMEBURSTENABLE | DISABLERXOWN; + + if (!phydev->link) { + printf("%s: No link.\n", phydev->dev->name); + return; + } + + if (phydev->speed != 1000) + conf |= MII_PORTSELECT; + + if (phydev->speed == 100) + conf |= FES_100; + + if (phydev->duplex) + conf |= FULLDPLXMODE; + + writel(conf, &mac_p->conf); + + printf("Speed: %d, %s duplex%s\n", phydev->speed, + (phydev->duplex) ? "full" : "half", + (phydev->port == PORT_FIBRE) ? ", fiber mode" : ""); +} + +static void dw_eth_halt(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_mac_regs *mac_p = priv->mac_regs_p; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + + writel(readl(&mac_p->conf) & ~(RXENABLE | TXENABLE), &mac_p->conf); + writel(readl(&dma_p->opmode) & ~(RXSTART | TXSTART), &dma_p->opmode); + + phy_shutdown(priv->phydev); +} + +static int dw_eth_init(struct eth_device *dev, bd_t *bis) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_mac_regs *mac_p = priv->mac_regs_p; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + unsigned int start; + + writel(readl(&dma_p->busmode) | DMAMAC_SRST, &dma_p->busmode); + + start = get_timer(0); + while (readl(&dma_p->busmode) & DMAMAC_SRST) { + if (get_timer(start) >= CONFIG_MACRESET_TIMEOUT) + return -1; + + mdelay(100); + }; + + /* Soft reset above clears HW address registers. + * So we have to set it here once again */ + dw_write_hwaddr(dev); + + rx_descs_init(dev); + tx_descs_init(dev); + + writel(FIXEDBURST | PRIORXTX_41 | BURST_16, &dma_p->busmode); + + writel(readl(&dma_p->opmode) | FLUSHTXFIFO | STOREFORWARD, + &dma_p->opmode); + + writel(readl(&dma_p->opmode) | RXSTART | TXSTART, &dma_p->opmode); + + /* Start up the PHY */ + if (phy_startup(priv->phydev)) { + printf("Could not initialize PHY %s\n", + priv->phydev->dev->name); + return -1; + } + + dw_adjust_link(mac_p, priv->phydev); + + if (!priv->phydev->link) + return -1; + + writel(readl(&mac_p->conf) | RXENABLE | TXENABLE, &mac_p->conf); + + return 0; +} + +static int dw_eth_send(struct eth_device *dev, void *packet, int length) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + u32 desc_num = priv->tx_currdescnum; + struct dmamacdescr *desc_p = &priv->tx_mac_descrtable[desc_num]; + + /* Invalidate only "status" field for the following check */ + invalidate_dcache_range((unsigned long)&desc_p->txrx_status, + (unsigned long)&desc_p->txrx_status + + sizeof(desc_p->txrx_status)); + + /* Check if the descriptor is owned by CPU */ + if (desc_p->txrx_status & DESC_TXSTS_OWNBYDMA) { + printf("CPU not owner of tx frame\n"); + return -1; + } + + memcpy((void *)desc_p->dmamac_addr, packet, length); + + /* Flush data to be sent */ + flush_dcache_range((unsigned long)desc_p->dmamac_addr, + (unsigned long)desc_p->dmamac_addr + length); + +#if defined(CONFIG_DW_ALTDESCRIPTOR) + desc_p->txrx_status |= DESC_TXSTS_TXFIRST | DESC_TXSTS_TXLAST; + desc_p->dmamac_cntl |= (length << DESC_TXCTRL_SIZE1SHFT) & \ + DESC_TXCTRL_SIZE1MASK; + + desc_p->txrx_status &= ~(DESC_TXSTS_MSK); + desc_p->txrx_status |= DESC_TXSTS_OWNBYDMA; +#else + desc_p->dmamac_cntl |= ((length << DESC_TXCTRL_SIZE1SHFT) & \ + DESC_TXCTRL_SIZE1MASK) | DESC_TXCTRL_TXLAST | \ + DESC_TXCTRL_TXFIRST; + + desc_p->txrx_status = DESC_TXSTS_OWNBYDMA; +#endif + + /* Flush modified buffer descriptor */ + flush_dcache_range((unsigned long)desc_p, + (unsigned long)desc_p + sizeof(struct dmamacdescr)); + + /* Test the wrap-around condition. */ + if (++desc_num >= CONFIG_TX_DESCR_NUM) + desc_num = 0; + + priv->tx_currdescnum = desc_num; + + /* Start the transmission */ + writel(POLL_DATA, &dma_p->txpolldemand); + + return 0; +} + +static int dw_eth_recv(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + u32 status, desc_num = priv->rx_currdescnum; + struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num]; + int length = 0; + + /* Invalidate entire buffer descriptor */ + invalidate_dcache_range((unsigned long)desc_p, + (unsigned long)desc_p + + sizeof(struct dmamacdescr)); + + status = desc_p->txrx_status; + + /* Check if the owner is the CPU */ + if (!(status & DESC_RXSTS_OWNBYDMA)) { + + length = (status & DESC_RXSTS_FRMLENMSK) >> \ + DESC_RXSTS_FRMLENSHFT; + + /* Invalidate received data */ + invalidate_dcache_range((unsigned long)desc_p->dmamac_addr, + (unsigned long)desc_p->dmamac_addr + + length); + + NetReceive(desc_p->dmamac_addr, length); + + /* + * Make the current descriptor valid again and go to + * the next one + */ + desc_p->txrx_status |= DESC_RXSTS_OWNBYDMA; + + /* Flush only status field - others weren't changed */ + flush_dcache_range((unsigned long)&desc_p->txrx_status, + (unsigned long)&desc_p->txrx_status + + sizeof(desc_p->txrx_status)); + + /* Test the wrap-around condition. */ + if (++desc_num >= CONFIG_RX_DESCR_NUM) + desc_num = 0; + } + + priv->rx_currdescnum = desc_num; + + return length; +} + +static int dw_phy_init(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct phy_device *phydev; + int mask = 0xffffffff; + +#ifdef CONFIG_PHY_ADDR + mask = 1 << CONFIG_PHY_ADDR; +#endif + + phydev = phy_find_by_mask(priv->bus, mask, priv->interface); + if (!phydev) + return -1; + + phy_connect_dev(phydev, dev); + + phydev->supported &= PHY_GBIT_FEATURES; + phydev->advertising = phydev->supported; + + priv->phydev = phydev; + phy_config(phydev); + + return 1; +} + +int designware_initialize(ulong base_addr, u32 interface) +{ + struct eth_device *dev; + struct dw_eth_dev *priv; + + dev = (struct eth_device *) malloc(sizeof(struct eth_device)); + if (!dev) + return -ENOMEM; + + /* + * Since the priv structure contains the descriptors which need a strict + * buswidth alignment, memalign is used to allocate memory + */ + priv = (struct dw_eth_dev *) memalign(16, sizeof(struct dw_eth_dev)); + if (!priv) { + free(dev); + return -ENOMEM; + } + + memset(dev, 0, sizeof(struct eth_device)); + memset(priv, 0, sizeof(struct dw_eth_dev)); + + sprintf(dev->name, "dwmac.%lx", base_addr); + dev->iobase = (int)base_addr; + dev->priv = priv; + + priv->dev = dev; + priv->mac_regs_p = (struct eth_mac_regs *)base_addr; + priv->dma_regs_p = (struct eth_dma_regs *)(base_addr + + DW_DMA_BASE_OFFSET); + + dev->init = dw_eth_init; + dev->send = dw_eth_send; + dev->recv = dw_eth_recv; + dev->halt = dw_eth_halt; + dev->write_hwaddr = dw_write_hwaddr; + + eth_register(dev); + + priv->interface = interface; + + dw_mdio_init(dev->name, priv->mac_regs_p); + priv->bus = miiphy_get_dev_by_name(dev->name); + + return dw_phy_init(dev); +} diff --git a/qemu/roms/u-boot/drivers/net/designware.h b/qemu/roms/u-boot/drivers/net/designware.h new file mode 100644 index 000000000..382b0c7f0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/designware.h @@ -0,0 +1,236 @@ +/* + * (C) Copyright 2010 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DW_ETH_H +#define _DW_ETH_H + +#define CONFIG_TX_DESCR_NUM 16 +#define CONFIG_RX_DESCR_NUM 16 +#define CONFIG_ETH_BUFSIZE 2048 +#define TX_TOTAL_BUFSIZE (CONFIG_ETH_BUFSIZE * CONFIG_TX_DESCR_NUM) +#define RX_TOTAL_BUFSIZE (CONFIG_ETH_BUFSIZE * CONFIG_RX_DESCR_NUM) + +#define CONFIG_MACRESET_TIMEOUT (3 * CONFIG_SYS_HZ) +#define CONFIG_MDIO_TIMEOUT (3 * CONFIG_SYS_HZ) + +struct eth_mac_regs { + u32 conf; /* 0x00 */ + u32 framefilt; /* 0x04 */ + u32 hashtablehigh; /* 0x08 */ + u32 hashtablelow; /* 0x0c */ + u32 miiaddr; /* 0x10 */ + u32 miidata; /* 0x14 */ + u32 flowcontrol; /* 0x18 */ + u32 vlantag; /* 0x1c */ + u32 version; /* 0x20 */ + u8 reserved_1[20]; + u32 intreg; /* 0x38 */ + u32 intmask; /* 0x3c */ + u32 macaddr0hi; /* 0x40 */ + u32 macaddr0lo; /* 0x44 */ +}; + +/* MAC configuration register definitions */ +#define FRAMEBURSTENABLE (1 << 21) +#define MII_PORTSELECT (1 << 15) +#define FES_100 (1 << 14) +#define DISABLERXOWN (1 << 13) +#define FULLDPLXMODE (1 << 11) +#define RXENABLE (1 << 2) +#define TXENABLE (1 << 3) + +/* MII address register definitions */ +#define MII_BUSY (1 << 0) +#define MII_WRITE (1 << 1) +#define MII_CLKRANGE_60_100M (0) +#define MII_CLKRANGE_100_150M (0x4) +#define MII_CLKRANGE_20_35M (0x8) +#define MII_CLKRANGE_35_60M (0xC) +#define MII_CLKRANGE_150_250M (0x10) +#define MII_CLKRANGE_250_300M (0x14) + +#define MIIADDRSHIFT (11) +#define MIIREGSHIFT (6) +#define MII_REGMSK (0x1F << 6) +#define MII_ADDRMSK (0x1F << 11) + + +struct eth_dma_regs { + u32 busmode; /* 0x00 */ + u32 txpolldemand; /* 0x04 */ + u32 rxpolldemand; /* 0x08 */ + u32 rxdesclistaddr; /* 0x0c */ + u32 txdesclistaddr; /* 0x10 */ + u32 status; /* 0x14 */ + u32 opmode; /* 0x18 */ + u32 intenable; /* 0x1c */ + u8 reserved[40]; + u32 currhosttxdesc; /* 0x48 */ + u32 currhostrxdesc; /* 0x4c */ + u32 currhosttxbuffaddr; /* 0x50 */ + u32 currhostrxbuffaddr; /* 0x54 */ +}; + +#define DW_DMA_BASE_OFFSET (0x1000) + +/* Bus mode register definitions */ +#define FIXEDBURST (1 << 16) +#define PRIORXTX_41 (3 << 14) +#define PRIORXTX_31 (2 << 14) +#define PRIORXTX_21 (1 << 14) +#define PRIORXTX_11 (0 << 14) +#define BURST_1 (1 << 8) +#define BURST_2 (2 << 8) +#define BURST_4 (4 << 8) +#define BURST_8 (8 << 8) +#define BURST_16 (16 << 8) +#define BURST_32 (32 << 8) +#define RXHIGHPRIO (1 << 1) +#define DMAMAC_SRST (1 << 0) + +/* Poll demand definitions */ +#define POLL_DATA (0xFFFFFFFF) + +/* Operation mode definitions */ +#define STOREFORWARD (1 << 21) +#define FLUSHTXFIFO (1 << 20) +#define TXSTART (1 << 13) +#define TXSECONDFRAME (1 << 2) +#define RXSTART (1 << 1) + +/* Descriptior related definitions */ +#define MAC_MAX_FRAME_SZ (1600) + +struct dmamacdescr { + u32 txrx_status; + u32 dmamac_cntl; + void *dmamac_addr; + struct dmamacdescr *dmamac_next; +} __aligned(ARCH_DMA_MINALIGN); + +/* + * txrx_status definitions + */ + +/* tx status bits definitions */ +#if defined(CONFIG_DW_ALTDESCRIPTOR) + +#define DESC_TXSTS_OWNBYDMA (1 << 31) +#define DESC_TXSTS_TXINT (1 << 30) +#define DESC_TXSTS_TXLAST (1 << 29) +#define DESC_TXSTS_TXFIRST (1 << 28) +#define DESC_TXSTS_TXCRCDIS (1 << 27) + +#define DESC_TXSTS_TXPADDIS (1 << 26) +#define DESC_TXSTS_TXCHECKINSCTRL (3 << 22) +#define DESC_TXSTS_TXRINGEND (1 << 21) +#define DESC_TXSTS_TXCHAIN (1 << 20) +#define DESC_TXSTS_MSK (0x1FFFF << 0) + +#else + +#define DESC_TXSTS_OWNBYDMA (1 << 31) +#define DESC_TXSTS_MSK (0x1FFFF << 0) + +#endif + +/* rx status bits definitions */ +#define DESC_RXSTS_OWNBYDMA (1 << 31) +#define DESC_RXSTS_DAFILTERFAIL (1 << 30) +#define DESC_RXSTS_FRMLENMSK (0x3FFF << 16) +#define DESC_RXSTS_FRMLENSHFT (16) + +#define DESC_RXSTS_ERROR (1 << 15) +#define DESC_RXSTS_RXTRUNCATED (1 << 14) +#define DESC_RXSTS_SAFILTERFAIL (1 << 13) +#define DESC_RXSTS_RXIPC_GIANTFRAME (1 << 12) +#define DESC_RXSTS_RXDAMAGED (1 << 11) +#define DESC_RXSTS_RXVLANTAG (1 << 10) +#define DESC_RXSTS_RXFIRST (1 << 9) +#define DESC_RXSTS_RXLAST (1 << 8) +#define DESC_RXSTS_RXIPC_GIANT (1 << 7) +#define DESC_RXSTS_RXCOLLISION (1 << 6) +#define DESC_RXSTS_RXFRAMEETHER (1 << 5) +#define DESC_RXSTS_RXWATCHDOG (1 << 4) +#define DESC_RXSTS_RXMIIERROR (1 << 3) +#define DESC_RXSTS_RXDRIBBLING (1 << 2) +#define DESC_RXSTS_RXCRC (1 << 1) + +/* + * dmamac_cntl definitions + */ + +/* tx control bits definitions */ +#if defined(CONFIG_DW_ALTDESCRIPTOR) + +#define DESC_TXCTRL_SIZE1MASK (0x1FFF << 0) +#define DESC_TXCTRL_SIZE1SHFT (0) +#define DESC_TXCTRL_SIZE2MASK (0x1FFF << 16) +#define DESC_TXCTRL_SIZE2SHFT (16) + +#else + +#define DESC_TXCTRL_TXINT (1 << 31) +#define DESC_TXCTRL_TXLAST (1 << 30) +#define DESC_TXCTRL_TXFIRST (1 << 29) +#define DESC_TXCTRL_TXCHECKINSCTRL (3 << 27) +#define DESC_TXCTRL_TXCRCDIS (1 << 26) +#define DESC_TXCTRL_TXRINGEND (1 << 25) +#define DESC_TXCTRL_TXCHAIN (1 << 24) + +#define DESC_TXCTRL_SIZE1MASK (0x7FF << 0) +#define DESC_TXCTRL_SIZE1SHFT (0) +#define DESC_TXCTRL_SIZE2MASK (0x7FF << 11) +#define DESC_TXCTRL_SIZE2SHFT (11) + +#endif + +/* rx control bits definitions */ +#if defined(CONFIG_DW_ALTDESCRIPTOR) + +#define DESC_RXCTRL_RXINTDIS (1 << 31) +#define DESC_RXCTRL_RXRINGEND (1 << 15) +#define DESC_RXCTRL_RXCHAIN (1 << 14) + +#define DESC_RXCTRL_SIZE1MASK (0x1FFF << 0) +#define DESC_RXCTRL_SIZE1SHFT (0) +#define DESC_RXCTRL_SIZE2MASK (0x1FFF << 16) +#define DESC_RXCTRL_SIZE2SHFT (16) + +#else + +#define DESC_RXCTRL_RXINTDIS (1 << 31) +#define DESC_RXCTRL_RXRINGEND (1 << 25) +#define DESC_RXCTRL_RXCHAIN (1 << 24) + +#define DESC_RXCTRL_SIZE1MASK (0x7FF << 0) +#define DESC_RXCTRL_SIZE1SHFT (0) +#define DESC_RXCTRL_SIZE2MASK (0x7FF << 11) +#define DESC_RXCTRL_SIZE2SHFT (11) + +#endif + +struct dw_eth_dev { + u32 interface; + u32 tx_currdescnum; + u32 rx_currdescnum; + + struct dmamacdescr tx_mac_descrtable[CONFIG_TX_DESCR_NUM]; + struct dmamacdescr rx_mac_descrtable[CONFIG_RX_DESCR_NUM]; + + char txbuffs[TX_TOTAL_BUFSIZE]; + char rxbuffs[RX_TOTAL_BUFSIZE]; + + struct eth_mac_regs *mac_regs_p; + struct eth_dma_regs *dma_regs_p; + + struct eth_device *dev; + struct phy_device *phydev; + struct mii_dev *bus; +}; + +#endif diff --git a/qemu/roms/u-boot/drivers/net/dm9000x.c b/qemu/roms/u-boot/drivers/net/dm9000x.c new file mode 100644 index 000000000..4de9d4164 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/dm9000x.c @@ -0,0 +1,643 @@ +/* + dm9000.c: Version 1.2 12/15/2003 + + A Davicom DM9000 ISA NIC fast Ethernet driver for Linux. + Copyright (C) 1997 Sten Wang + + * SPDX-License-Identifier: GPL-2.0+ + + (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. + +V0.11 06/20/2001 REG_0A bit3=1, default enable BP with DA match + 06/22/2001 Support DM9801 progrmming + E3: R25 = ((R24 + NF) & 0x00ff) | 0xf000 + E4: R25 = ((R24 + NF) & 0x00ff) | 0xc200 + R17 = (R17 & 0xfff0) | NF + 3 + E5: R25 = ((R24 + NF - 3) & 0x00ff) | 0xc200 + R17 = (R17 & 0xfff0) | NF + +v1.00 modify by simon 2001.9.5 + change for kernel 2.4.x + +v1.1 11/09/2001 fix force mode bug + +v1.2 03/18/2003 Weilun Huang : + Fixed phy reset. + Added tx/rx 32 bit mode. + Cleaned up for kernel merge. + +-------------------------------------- + + 12/15/2003 Initial port to u-boot by + Sascha Hauer + + 06/03/2008 Remy Bohmer + - Fixed the driver to work with DM9000A. + (check on ISR receive status bit before reading the + FIFO as described in DM9000 programming guide and + application notes) + - Added autodetect of databus width. + - Made debug code compile again. + - Adapt eth_send such that it matches the DM9000* + application notes. Needed to make it work properly + for DM9000A. + - Adapted reset procedure to match DM9000 application + notes (i.e. double reset) + - some minor code cleanups + These changes are tested with DM9000{A,EP,E} together + with a 200MHz Atmel AT91SAM9261 core + +TODO: external MII is not functional, only internal at the moment. +*/ + +#include +#include +#include +#include +#include + +#include "dm9000x.h" + +/* Board/System/Debug information/definition ---------------- */ + +/* #define CONFIG_DM9000_DEBUG */ + +#ifdef CONFIG_DM9000_DEBUG +#define DM9000_DBG(fmt,args...) printf(fmt, ##args) +#define DM9000_DMP_PACKET(func,packet,length) \ + do { \ + int i; \ + printf("%s: length: %d\n", func, length); \ + for (i = 0; i < length; i++) { \ + if (i % 8 == 0) \ + printf("\n%s: %02x: ", func, i); \ + printf("%02x ", ((unsigned char *) packet)[i]); \ + } printf("\n"); \ + } while(0) +#else +#define DM9000_DBG(fmt,args...) +#define DM9000_DMP_PACKET(func,packet,length) +#endif + +/* Structure/enum declaration ------------------------------- */ +typedef struct board_info { + u32 runt_length_counter; /* counter: RX length < 64byte */ + u32 long_length_counter; /* counter: RX length > 1514byte */ + u32 reset_counter; /* counter: RESET */ + u32 reset_tx_timeout; /* RESET caused by TX Timeout */ + u32 reset_rx_status; /* RESET caused by RX Statsus wrong */ + u16 tx_pkt_cnt; + u16 queue_start_addr; + u16 dbug_cnt; + u8 phy_addr; + u8 device_wait_reset; /* device state */ + unsigned char srom[128]; + void (*outblk)(volatile void *data_ptr, int count); + void (*inblk)(void *data_ptr, int count); + void (*rx_status)(u16 *RxStatus, u16 *RxLen); + struct eth_device netdev; +} board_info_t; +static board_info_t dm9000_info; + + +/* function declaration ------------------------------------- */ +static int dm9000_probe(void); +static u16 dm9000_phy_read(int); +static void dm9000_phy_write(int, u16); +static u8 DM9000_ior(int); +static void DM9000_iow(int reg, u8 value); + +/* DM9000 network board routine ---------------------------- */ +#ifndef CONFIG_DM9000_BYTE_SWAPPED +#define DM9000_outb(d,r) writeb(d, (volatile u8 *)(r)) +#define DM9000_outw(d,r) writew(d, (volatile u16 *)(r)) +#define DM9000_outl(d,r) writel(d, (volatile u32 *)(r)) +#define DM9000_inb(r) readb((volatile u8 *)(r)) +#define DM9000_inw(r) readw((volatile u16 *)(r)) +#define DM9000_inl(r) readl((volatile u32 *)(r)) +#else +#define DM9000_outb(d, r) __raw_writeb(d, r) +#define DM9000_outw(d, r) __raw_writew(d, r) +#define DM9000_outl(d, r) __raw_writel(d, r) +#define DM9000_inb(r) __raw_readb(r) +#define DM9000_inw(r) __raw_readw(r) +#define DM9000_inl(r) __raw_readl(r) +#endif + +#ifdef CONFIG_DM9000_DEBUG +static void +dump_regs(void) +{ + DM9000_DBG("\n"); + DM9000_DBG("NCR (0x00): %02x\n", DM9000_ior(0)); + DM9000_DBG("NSR (0x01): %02x\n", DM9000_ior(1)); + DM9000_DBG("TCR (0x02): %02x\n", DM9000_ior(2)); + DM9000_DBG("TSRI (0x03): %02x\n", DM9000_ior(3)); + DM9000_DBG("TSRII (0x04): %02x\n", DM9000_ior(4)); + DM9000_DBG("RCR (0x05): %02x\n", DM9000_ior(5)); + DM9000_DBG("RSR (0x06): %02x\n", DM9000_ior(6)); + DM9000_DBG("ISR (0xFE): %02x\n", DM9000_ior(DM9000_ISR)); + DM9000_DBG("\n"); +} +#endif + +static void dm9000_outblk_8bit(volatile void *data_ptr, int count) +{ + int i; + for (i = 0; i < count; i++) + DM9000_outb((((u8 *) data_ptr)[i] & 0xff), DM9000_DATA); +} + +static void dm9000_outblk_16bit(volatile void *data_ptr, int count) +{ + int i; + u32 tmplen = (count + 1) / 2; + + for (i = 0; i < tmplen; i++) + DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA); +} +static void dm9000_outblk_32bit(volatile void *data_ptr, int count) +{ + int i; + u32 tmplen = (count + 3) / 4; + + for (i = 0; i < tmplen; i++) + DM9000_outl(((u32 *) data_ptr)[i], DM9000_DATA); +} + +static void dm9000_inblk_8bit(void *data_ptr, int count) +{ + int i; + for (i = 0; i < count; i++) + ((u8 *) data_ptr)[i] = DM9000_inb(DM9000_DATA); +} + +static void dm9000_inblk_16bit(void *data_ptr, int count) +{ + int i; + u32 tmplen = (count + 1) / 2; + + for (i = 0; i < tmplen; i++) + ((u16 *) data_ptr)[i] = DM9000_inw(DM9000_DATA); +} +static void dm9000_inblk_32bit(void *data_ptr, int count) +{ + int i; + u32 tmplen = (count + 3) / 4; + + for (i = 0; i < tmplen; i++) + ((u32 *) data_ptr)[i] = DM9000_inl(DM9000_DATA); +} + +static void dm9000_rx_status_32bit(u16 *RxStatus, u16 *RxLen) +{ + u32 tmpdata; + + DM9000_outb(DM9000_MRCMD, DM9000_IO); + + tmpdata = DM9000_inl(DM9000_DATA); + *RxStatus = __le16_to_cpu(tmpdata); + *RxLen = __le16_to_cpu(tmpdata >> 16); +} + +static void dm9000_rx_status_16bit(u16 *RxStatus, u16 *RxLen) +{ + DM9000_outb(DM9000_MRCMD, DM9000_IO); + + *RxStatus = __le16_to_cpu(DM9000_inw(DM9000_DATA)); + *RxLen = __le16_to_cpu(DM9000_inw(DM9000_DATA)); +} + +static void dm9000_rx_status_8bit(u16 *RxStatus, u16 *RxLen) +{ + DM9000_outb(DM9000_MRCMD, DM9000_IO); + + *RxStatus = + __le16_to_cpu(DM9000_inb(DM9000_DATA) + + (DM9000_inb(DM9000_DATA) << 8)); + *RxLen = + __le16_to_cpu(DM9000_inb(DM9000_DATA) + + (DM9000_inb(DM9000_DATA) << 8)); +} + +/* + Search DM9000 board, allocate space and register it +*/ +int +dm9000_probe(void) +{ + u32 id_val; + id_val = DM9000_ior(DM9000_VIDL); + id_val |= DM9000_ior(DM9000_VIDH) << 8; + id_val |= DM9000_ior(DM9000_PIDL) << 16; + id_val |= DM9000_ior(DM9000_PIDH) << 24; + if (id_val == DM9000_ID) { + printf("dm9000 i/o: 0x%x, id: 0x%x \n", CONFIG_DM9000_BASE, + id_val); + return 0; + } else { + printf("dm9000 not found at 0x%08x id: 0x%08x\n", + CONFIG_DM9000_BASE, id_val); + return -1; + } +} + +/* General Purpose dm9000 reset routine */ +static void +dm9000_reset(void) +{ + DM9000_DBG("resetting DM9000\n"); + + /* Reset DM9000, + see DM9000 Application Notes V1.22 Jun 11, 2004 page 29 */ + + /* DEBUG: Make all GPIO0 outputs, all others inputs */ + DM9000_iow(DM9000_GPCR, GPCR_GPIO0_OUT); + /* Step 1: Power internal PHY by writing 0 to GPIO0 pin */ + DM9000_iow(DM9000_GPR, 0); + /* Step 2: Software reset */ + DM9000_iow(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST)); + + do { + DM9000_DBG("resetting the DM9000, 1st reset\n"); + udelay(25); /* Wait at least 20 us */ + } while (DM9000_ior(DM9000_NCR) & 1); + + DM9000_iow(DM9000_NCR, 0); + DM9000_iow(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST)); /* Issue a second reset */ + + do { + DM9000_DBG("resetting the DM9000, 2nd reset\n"); + udelay(25); /* Wait at least 20 us */ + } while (DM9000_ior(DM9000_NCR) & 1); + + /* Check whether the ethernet controller is present */ + if ((DM9000_ior(DM9000_PIDL) != 0x0) || + (DM9000_ior(DM9000_PIDH) != 0x90)) + printf("ERROR: resetting DM9000 -> not responding\n"); +} + +/* Initialize dm9000 board +*/ +static int dm9000_init(struct eth_device *dev, bd_t *bd) +{ + int i, oft, lnk; + u8 io_mode; + struct board_info *db = &dm9000_info; + + DM9000_DBG("%s\n", __func__); + + /* RESET device */ + dm9000_reset(); + + if (dm9000_probe() < 0) + return -1; + + /* Auto-detect 8/16/32 bit mode, ISR Bit 6+7 indicate bus width */ + io_mode = DM9000_ior(DM9000_ISR) >> 6; + + switch (io_mode) { + case 0x0: /* 16-bit mode */ + printf("DM9000: running in 16 bit mode\n"); + db->outblk = dm9000_outblk_16bit; + db->inblk = dm9000_inblk_16bit; + db->rx_status = dm9000_rx_status_16bit; + break; + case 0x01: /* 32-bit mode */ + printf("DM9000: running in 32 bit mode\n"); + db->outblk = dm9000_outblk_32bit; + db->inblk = dm9000_inblk_32bit; + db->rx_status = dm9000_rx_status_32bit; + break; + case 0x02: /* 8 bit mode */ + printf("DM9000: running in 8 bit mode\n"); + db->outblk = dm9000_outblk_8bit; + db->inblk = dm9000_inblk_8bit; + db->rx_status = dm9000_rx_status_8bit; + break; + default: + /* Assume 8 bit mode, will probably not work anyway */ + printf("DM9000: Undefined IO-mode:0x%x\n", io_mode); + db->outblk = dm9000_outblk_8bit; + db->inblk = dm9000_inblk_8bit; + db->rx_status = dm9000_rx_status_8bit; + break; + } + + /* Program operating register, only internal phy supported */ + DM9000_iow(DM9000_NCR, 0x0); + /* TX Polling clear */ + DM9000_iow(DM9000_TCR, 0); + /* Less 3Kb, 200us */ + DM9000_iow(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US); + /* Flow Control : High/Low Water */ + DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8)); + /* SH FIXME: This looks strange! Flow Control */ + DM9000_iow(DM9000_FCR, 0x0); + /* Special Mode */ + DM9000_iow(DM9000_SMCR, 0); + /* clear TX status */ + DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END); + /* Clear interrupt status */ + DM9000_iow(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS); + + printf("MAC: %pM\n", dev->enetaddr); + if (!is_valid_ether_addr(dev->enetaddr)) { +#ifdef CONFIG_RANDOM_MACADDR + printf("Bad MAC address (uninitialized EEPROM?), randomizing\n"); + eth_random_addr(dev->enetaddr); + printf("MAC: %pM\n", dev->enetaddr); +#else + printf("WARNING: Bad MAC address (uninitialized EEPROM?)\n"); +#endif + } + + /* fill device MAC address registers */ + for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++) + DM9000_iow(oft, dev->enetaddr[i]); + for (i = 0, oft = 0x16; i < 8; i++, oft++) + DM9000_iow(oft, 0xff); + + /* read back mac, just to be sure */ + for (i = 0, oft = 0x10; i < 6; i++, oft++) + DM9000_DBG("%02x:", DM9000_ior(oft)); + DM9000_DBG("\n"); + + /* Activate DM9000 */ + /* RX enable */ + DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN); + /* Enable TX/RX interrupt mask */ + DM9000_iow(DM9000_IMR, IMR_PAR); + + i = 0; + while (!(dm9000_phy_read(1) & 0x20)) { /* autonegation complete bit */ + udelay(1000); + i++; + if (i == 10000) { + printf("could not establish link\n"); + return 0; + } + } + + /* see what we've got */ + lnk = dm9000_phy_read(17) >> 12; + printf("operating at "); + switch (lnk) { + case 1: + printf("10M half duplex "); + break; + case 2: + printf("10M full duplex "); + break; + case 4: + printf("100M half duplex "); + break; + case 8: + printf("100M full duplex "); + break; + default: + printf("unknown: %d ", lnk); + break; + } + printf("mode\n"); + return 0; +} + +/* + Hardware start transmission. + Send a packet to media from the upper layer. +*/ +static int dm9000_send(struct eth_device *netdev, void *packet, int length) +{ + int tmo; + struct board_info *db = &dm9000_info; + + DM9000_DMP_PACKET(__func__ , packet, length); + + DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */ + + /* Move data to DM9000 TX RAM */ + DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */ + + /* push the data to the TX-fifo */ + (db->outblk)(packet, length); + + /* Set TX length to DM9000 */ + DM9000_iow(DM9000_TXPLL, length & 0xff); + DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff); + + /* Issue TX polling command */ + DM9000_iow(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */ + + /* wait for end of transmission */ + tmo = get_timer(0) + 5 * CONFIG_SYS_HZ; + while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) || + !(DM9000_ior(DM9000_ISR) & IMR_PTM) ) { + if (get_timer(0) >= tmo) { + printf("transmission timeout\n"); + break; + } + } + DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */ + + DM9000_DBG("transmit done\n\n"); + return 0; +} + +/* + Stop the interface. + The interface is stopped when it is brought. +*/ +static void dm9000_halt(struct eth_device *netdev) +{ + DM9000_DBG("%s\n", __func__); + + /* RESET devie */ + dm9000_phy_write(0, 0x8000); /* PHY RESET */ + DM9000_iow(DM9000_GPR, 0x01); /* Power-Down PHY */ + DM9000_iow(DM9000_IMR, 0x80); /* Disable all interrupt */ + DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */ +} + +/* + Received a packet and pass to upper layer +*/ +static int dm9000_rx(struct eth_device *netdev) +{ + u8 rxbyte, *rdptr = (u8 *) NetRxPackets[0]; + u16 RxStatus, RxLen = 0; + struct board_info *db = &dm9000_info; + + /* Check packet ready or not, we must check + the ISR status first for DM9000A */ + if (!(DM9000_ior(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */ + return 0; + + DM9000_iow(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */ + + /* There is _at least_ 1 package in the fifo, read them all */ + for (;;) { + DM9000_ior(DM9000_MRCMDX); /* Dummy read */ + + /* Get most updated data, + only look at bits 0:1, See application notes DM9000 */ + rxbyte = DM9000_inb(DM9000_DATA) & 0x03; + + /* Status check: this byte must be 0 or 1 */ + if (rxbyte > DM9000_PKT_RDY) { + DM9000_iow(DM9000_RCR, 0x00); /* Stop Device */ + DM9000_iow(DM9000_ISR, 0x80); /* Stop INT request */ + printf("DM9000 error: status check fail: 0x%x\n", + rxbyte); + return 0; + } + + if (rxbyte != DM9000_PKT_RDY) + return 0; /* No packet received, ignore */ + + DM9000_DBG("receiving packet\n"); + + /* A packet ready now & Get status/length */ + (db->rx_status)(&RxStatus, &RxLen); + + DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen); + + /* Move data from DM9000 */ + /* Read received packet from RX SRAM */ + (db->inblk)(rdptr, RxLen); + + if ((RxStatus & 0xbf00) || (RxLen < 0x40) + || (RxLen > DM9000_PKT_MAX)) { + if (RxStatus & 0x100) { + printf("rx fifo error\n"); + } + if (RxStatus & 0x200) { + printf("rx crc error\n"); + } + if (RxStatus & 0x8000) { + printf("rx length error\n"); + } + if (RxLen > DM9000_PKT_MAX) { + printf("rx length too big\n"); + dm9000_reset(); + } + } else { + DM9000_DMP_PACKET(__func__ , rdptr, RxLen); + + DM9000_DBG("passing packet to upper layer\n"); + NetReceive(NetRxPackets[0], RxLen); + } + } + return 0; +} + +/* + Read a word data from SROM +*/ +#if !defined(CONFIG_DM9000_NO_SROM) +void dm9000_read_srom_word(int offset, u8 *to) +{ + DM9000_iow(DM9000_EPAR, offset); + DM9000_iow(DM9000_EPCR, 0x4); + udelay(8000); + DM9000_iow(DM9000_EPCR, 0x0); + to[0] = DM9000_ior(DM9000_EPDRL); + to[1] = DM9000_ior(DM9000_EPDRH); +} + +void dm9000_write_srom_word(int offset, u16 val) +{ + DM9000_iow(DM9000_EPAR, offset); + DM9000_iow(DM9000_EPDRH, ((val >> 8) & 0xff)); + DM9000_iow(DM9000_EPDRL, (val & 0xff)); + DM9000_iow(DM9000_EPCR, 0x12); + udelay(8000); + DM9000_iow(DM9000_EPCR, 0); +} +#endif + +static void dm9000_get_enetaddr(struct eth_device *dev) +{ +#if !defined(CONFIG_DM9000_NO_SROM) + int i; + for (i = 0; i < 3; i++) + dm9000_read_srom_word(i, dev->enetaddr + (2 * i)); +#endif +} + +/* + Read a byte from I/O port +*/ +static u8 +DM9000_ior(int reg) +{ + DM9000_outb(reg, DM9000_IO); + return DM9000_inb(DM9000_DATA); +} + +/* + Write a byte to I/O port +*/ +static void +DM9000_iow(int reg, u8 value) +{ + DM9000_outb(reg, DM9000_IO); + DM9000_outb(value, DM9000_DATA); +} + +/* + Read a word from phyxcer +*/ +static u16 +dm9000_phy_read(int reg) +{ + u16 val; + + /* Fill the phyxcer register into REG_0C */ + DM9000_iow(DM9000_EPAR, DM9000_PHY | reg); + DM9000_iow(DM9000_EPCR, 0xc); /* Issue phyxcer read command */ + udelay(100); /* Wait read complete */ + DM9000_iow(DM9000_EPCR, 0x0); /* Clear phyxcer read command */ + val = (DM9000_ior(DM9000_EPDRH) << 8) | DM9000_ior(DM9000_EPDRL); + + /* The read data keeps on REG_0D & REG_0E */ + DM9000_DBG("dm9000_phy_read(0x%x): 0x%x\n", reg, val); + return val; +} + +/* + Write a word to phyxcer +*/ +static void +dm9000_phy_write(int reg, u16 value) +{ + + /* Fill the phyxcer register into REG_0C */ + DM9000_iow(DM9000_EPAR, DM9000_PHY | reg); + + /* Fill the written data into REG_0D & REG_0E */ + DM9000_iow(DM9000_EPDRL, (value & 0xff)); + DM9000_iow(DM9000_EPDRH, ((value >> 8) & 0xff)); + DM9000_iow(DM9000_EPCR, 0xa); /* Issue phyxcer write command */ + udelay(500); /* Wait write complete */ + DM9000_iow(DM9000_EPCR, 0x0); /* Clear phyxcer write command */ + DM9000_DBG("dm9000_phy_write(reg:0x%x, value:0x%x)\n", reg, value); +} + +int dm9000_initialize(bd_t *bis) +{ + struct eth_device *dev = &(dm9000_info.netdev); + + /* Load MAC address from EEPROM */ + dm9000_get_enetaddr(dev); + + dev->init = dm9000_init; + dev->halt = dm9000_halt; + dev->send = dm9000_send; + dev->recv = dm9000_rx; + sprintf(dev->name, "dm9000"); + + eth_register(dev); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/dm9000x.h b/qemu/roms/u-boot/drivers/net/dm9000x.h new file mode 100644 index 000000000..0d123e2e1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/dm9000x.h @@ -0,0 +1,140 @@ +/* + * dm9000 Ethernet + */ + +#ifdef CONFIG_DRIVER_DM9000 + +#define DM9000_ID 0x90000A46 +#define DM9000_PKT_MAX 1536 /* Received packet max size */ +#define DM9000_PKT_RDY 0x01 /* Packet ready to receive */ + +/* although the registers are 16 bit, they are 32-bit aligned. + */ + +#define DM9000_NCR 0x00 +#define DM9000_NSR 0x01 +#define DM9000_TCR 0x02 +#define DM9000_TSR1 0x03 +#define DM9000_TSR2 0x04 +#define DM9000_RCR 0x05 +#define DM9000_RSR 0x06 +#define DM9000_ROCR 0x07 +#define DM9000_BPTR 0x08 +#define DM9000_FCTR 0x09 +#define DM9000_FCR 0x0A +#define DM9000_EPCR 0x0B +#define DM9000_EPAR 0x0C +#define DM9000_EPDRL 0x0D +#define DM9000_EPDRH 0x0E +#define DM9000_WCR 0x0F + +#define DM9000_PAR 0x10 +#define DM9000_MAR 0x16 + +#define DM9000_GPCR 0x1e +#define DM9000_GPR 0x1f +#define DM9000_TRPAL 0x22 +#define DM9000_TRPAH 0x23 +#define DM9000_RWPAL 0x24 +#define DM9000_RWPAH 0x25 + +#define DM9000_VIDL 0x28 +#define DM9000_VIDH 0x29 +#define DM9000_PIDL 0x2A +#define DM9000_PIDH 0x2B + +#define DM9000_CHIPR 0x2C +#define DM9000_SMCR 0x2F + +#define DM9000_PHY 0x40 /* PHY address 0x01 */ + +#define DM9000_MRCMDX 0xF0 +#define DM9000_MRCMD 0xF2 +#define DM9000_MRRL 0xF4 +#define DM9000_MRRH 0xF5 +#define DM9000_MWCMDX 0xF6 +#define DM9000_MWCMD 0xF8 +#define DM9000_MWRL 0xFA +#define DM9000_MWRH 0xFB +#define DM9000_TXPLL 0xFC +#define DM9000_TXPLH 0xFD +#define DM9000_ISR 0xFE +#define DM9000_IMR 0xFF + +#define NCR_EXT_PHY (1<<7) +#define NCR_WAKEEN (1<<6) +#define NCR_FCOL (1<<4) +#define NCR_FDX (1<<3) +#define NCR_LBK (3<<1) +#define NCR_LBK_INT_MAC (1<<1) +#define NCR_LBK_INT_PHY (2<<1) +#define NCR_RST (1<<0) + +#define NSR_SPEED (1<<7) +#define NSR_LINKST (1<<6) +#define NSR_WAKEST (1<<5) +#define NSR_TX2END (1<<3) +#define NSR_TX1END (1<<2) +#define NSR_RXOV (1<<1) + +#define TCR_TJDIS (1<<6) +#define TCR_EXCECM (1<<5) +#define TCR_PAD_DIS2 (1<<4) +#define TCR_CRC_DIS2 (1<<3) +#define TCR_PAD_DIS1 (1<<2) +#define TCR_CRC_DIS1 (1<<1) +#define TCR_TXREQ (1<<0) + +#define TSR_TJTO (1<<7) +#define TSR_LC (1<<6) +#define TSR_NC (1<<5) +#define TSR_LCOL (1<<4) +#define TSR_COL (1<<3) +#define TSR_EC (1<<2) + +#define RCR_WTDIS (1<<6) +#define RCR_DIS_LONG (1<<5) +#define RCR_DIS_CRC (1<<4) +#define RCR_ALL (1<<3) +#define RCR_RUNT (1<<2) +#define RCR_PRMSC (1<<1) +#define RCR_RXEN (1<<0) + +#define RSR_RF (1<<7) +#define RSR_MF (1<<6) +#define RSR_LCS (1<<5) +#define RSR_RWTO (1<<4) +#define RSR_PLE (1<<3) +#define RSR_AE (1<<2) +#define RSR_CE (1<<1) +#define RSR_FOE (1<<0) + +#define EPCR_EPOS_PHY (1<<3) +#define EPCR_EPOS_EE (0<<3) +#define EPCR_ERPRR (1<<2) +#define EPCR_ERPRW (1<<1) +#define EPCR_ERRE (1<<0) + +#define FCTR_HWOT(ot) (( ot & 0xf ) << 4 ) +#define FCTR_LWOT(ot) ( ot & 0xf ) + +#define BPTR_BPHW(x) ((x) << 4) +#define BPTR_JPT_200US (0x07) +#define BPTR_JPT_600US (0x0f) + +#define IMR_PAR (1<<7) +#define IMR_ROOM (1<<3) +#define IMR_ROM (1<<2) +#define IMR_PTM (1<<1) +#define IMR_PRM (1<<0) + +#define ISR_ROOS (1<<3) +#define ISR_ROS (1<<2) +#define ISR_PTS (1<<1) +#define ISR_PRS (1<<0) + +#define GPCR_GPIO0_OUT (1<<0) + +#define GPR_PHY_PWROFF (1<<0) + +#endif diff --git a/qemu/roms/u-boot/drivers/net/dnet.c b/qemu/roms/u-boot/drivers/net/dnet.c new file mode 100644 index 000000000..944a0c046 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/dnet.c @@ -0,0 +1,392 @@ +/* + * Dave Ethernet Controller driver + * + * Copyright (C) 2008 Dave S.r.l. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#ifndef CONFIG_DNET_AUTONEG_TIMEOUT +#define CONFIG_DNET_AUTONEG_TIMEOUT 5000000 /* default value */ +#endif + +#include +#include +#include + +#include +#include +#include + +#include "dnet.h" + +struct dnet_device { + struct dnet_registers *regs; + const struct device *dev; + struct eth_device netdev; + unsigned short phy_addr; +}; + +/* get struct dnet_device from given struct netdev */ +#define to_dnet(_nd) container_of(_nd, struct dnet_device, netdev) + +/* function for reading internal MAC register */ +u16 dnet_readw_mac(struct dnet_device *dnet, u16 reg) +{ + u16 data_read; + + /* issue a read */ + writel(reg, &dnet->regs->MACREG_ADDR); + + /* since a read/write op to the MAC is very slow, + * we must wait before reading the data */ + udelay(1); + + /* read data read from the MAC register */ + data_read = readl(&dnet->regs->MACREG_DATA); + + /* all done */ + return data_read; +} + +/* function for writing internal MAC register */ +void dnet_writew_mac(struct dnet_device *dnet, u16 reg, u16 val) +{ + /* load data to write */ + writel(val, &dnet->regs->MACREG_DATA); + + /* issue a write */ + writel(reg | DNET_INTERNAL_WRITE, &dnet->regs->MACREG_ADDR); + + /* since a read/write op to the MAC is very slow, + * we must wait before exiting */ + udelay(1); +} + +static void dnet_mdio_write(struct dnet_device *dnet, u8 reg, u16 value) +{ + u16 tmp; + + debug(DRIVERNAME "dnet_mdio_write %02x:%02x <- %04x\n", + dnet->phy_addr, reg, value); + + while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & + DNET_INTERNAL_GMII_MNG_CMD_FIN)) + ; + + /* prepare for a write operation */ + tmp = (1 << 13); + + /* only 5 bits allowed for register offset */ + reg &= 0x1f; + + /* prepare reg_value for a write */ + tmp |= (dnet->phy_addr << 8); + tmp |= reg; + + /* write data to write first */ + dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG, value); + + /* write control word */ + dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, tmp); + + while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & + DNET_INTERNAL_GMII_MNG_CMD_FIN)) + ; +} + +static u16 dnet_mdio_read(struct dnet_device *dnet, u8 reg) +{ + u16 value; + + while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & + DNET_INTERNAL_GMII_MNG_CMD_FIN)) + ; + + /* only 5 bits allowed for register offset*/ + reg &= 0x1f; + + /* prepare reg_value for a read */ + value = (dnet->phy_addr << 8); + value |= reg; + + /* write control word */ + dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, value); + + /* wait for end of transfer */ + while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & + DNET_INTERNAL_GMII_MNG_CMD_FIN)) + ; + + value = dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG); + + debug(DRIVERNAME "dnet_mdio_read %02x:%02x <- %04x\n", + dnet->phy_addr, reg, value); + + return value; +} + +static int dnet_send(struct eth_device *netdev, void *packet, int length) +{ + struct dnet_device *dnet = to_dnet(netdev); + int i, wrsz; + unsigned int *bufp; + unsigned int tx_cmd; + + debug(DRIVERNAME "[%s] Sending %u bytes\n", __func__, length); + + bufp = (unsigned int *) (((u32)packet) & 0xFFFFFFFC); + wrsz = (u32)length + 3; + wrsz += ((u32)packet) & 0x3; + wrsz >>= 2; + tx_cmd = ((((unsigned int)(packet)) & 0x03) << 16) | (u32)length; + + /* check if there is enough room for the current frame */ + if (wrsz < (DNET_FIFO_SIZE - readl(&dnet->regs->TX_FIFO_WCNT))) { + for (i = 0; i < wrsz; i++) + writel(*bufp++, &dnet->regs->TX_DATA_FIFO); + /* + * inform MAC that a packet's written and ready + * to be shipped out + */ + writel(tx_cmd, &dnet->regs->TX_LEN_FIFO); + } else { + printf(DRIVERNAME "No free space (actual %d, required %d " + "(words))\n", DNET_FIFO_SIZE - + readl(&dnet->regs->TX_FIFO_WCNT), wrsz); + } + + /* No one cares anyway */ + return 0; +} + + +static int dnet_recv(struct eth_device *netdev) +{ + struct dnet_device *dnet = to_dnet(netdev); + unsigned int *data_ptr; + int pkt_len, poll, i; + u32 cmd_word; + + debug("Waiting for pkt (polling)\n"); + poll = 50; + while ((readl(&dnet->regs->RX_FIFO_WCNT) >> 16) == 0) { + udelay(10); /* wait 10 usec */ + if (--poll == 0) + return 0; /* no pkt available */ + } + + cmd_word = readl(&dnet->regs->RX_LEN_FIFO); + pkt_len = cmd_word & 0xFFFF; + + debug("Got pkt with size %d bytes\n", pkt_len); + + if (cmd_word & 0xDF180000) + printf("%s packet receive error %x\n", __func__, cmd_word); + + data_ptr = (unsigned int *) NetRxPackets[0]; + + for (i = 0; i < (pkt_len + 3) >> 2; i++) + *data_ptr++ = readl(&dnet->regs->RX_DATA_FIFO); + + NetReceive(NetRxPackets[0], pkt_len + 5); /* ok + 5 ?? */ + + return 0; +} + +static void dnet_set_hwaddr(struct eth_device *netdev) +{ + struct dnet_device *dnet = to_dnet(netdev); + u16 tmp; + + tmp = get_unaligned_be16(netdev->enetaddr); + dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_0_REG, tmp); + tmp = get_unaligned_be16(&netdev->enetaddr[2]); + dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_1_REG, tmp); + tmp = get_unaligned_be16(&netdev->enetaddr[4]); + dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_2_REG, tmp); +} + +static void dnet_phy_reset(struct dnet_device *dnet) +{ + struct eth_device *netdev = &dnet->netdev; + int i; + u16 status, adv; + + adv = ADVERTISE_CSMA | ADVERTISE_ALL; + dnet_mdio_write(dnet, MII_ADVERTISE, adv); + printf("%s: Starting autonegotiation...\n", netdev->name); + dnet_mdio_write(dnet, MII_BMCR, (BMCR_ANENABLE + | BMCR_ANRESTART)); + + for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) { + status = dnet_mdio_read(dnet, MII_BMSR); + if (status & BMSR_ANEGCOMPLETE) + break; + udelay(100); + } + + if (status & BMSR_ANEGCOMPLETE) + printf("%s: Autonegotiation complete\n", netdev->name); + else + printf("%s: Autonegotiation timed out (status=0x%04x)\n", + netdev->name, status); +} + +static int dnet_phy_init(struct dnet_device *dnet) +{ + struct eth_device *netdev = &dnet->netdev; + u16 phy_id, status, adv, lpa; + int media, speed, duplex; + int i; + u32 ctl_reg; + + /* Find a PHY */ + for (i = 0; i < 32; i++) { + dnet->phy_addr = i; + phy_id = dnet_mdio_read(dnet, MII_PHYSID1); + if (phy_id != 0xffff) { + /* ok we found it */ + printf("Found PHY at address %d PHYID (%04x:%04x)\n", + i, phy_id, + dnet_mdio_read(dnet, MII_PHYSID2)); + break; + } + } + + /* Check if the PHY is up to snuff... */ + phy_id = dnet_mdio_read(dnet, MII_PHYSID1); + if (phy_id == 0xffff) { + printf("%s: No PHY present\n", netdev->name); + return -1; + } + + status = dnet_mdio_read(dnet, MII_BMSR); + if (!(status & BMSR_LSTATUS)) { + /* Try to re-negotiate if we don't have link already. */ + dnet_phy_reset(dnet); + + for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) { + status = dnet_mdio_read(dnet, MII_BMSR); + if (status & BMSR_LSTATUS) + break; + udelay(100); + } + } + + if (!(status & BMSR_LSTATUS)) { + printf("%s: link down (status: 0x%04x)\n", + netdev->name, status); + return -1; + } else { + adv = dnet_mdio_read(dnet, MII_ADVERTISE); + lpa = dnet_mdio_read(dnet, MII_LPA); + media = mii_nway_result(lpa & adv); + speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) + ? 1 : 0); + duplex = (media & ADVERTISE_FULL) ? 1 : 0; + /* 1000BaseT ethernet is not supported */ + printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n", + netdev->name, + speed ? "100" : "10", + duplex ? "full" : "half", + lpa); + + ctl_reg = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG); + + if (duplex) + ctl_reg &= ~(DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP); + else + ctl_reg |= DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP; + + dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, ctl_reg); + + return 0; + } +} + +static int dnet_init(struct eth_device *netdev, bd_t *bd) +{ + struct dnet_device *dnet = to_dnet(netdev); + u32 config; + + /* + * dnet_halt should have been called at some point before now, + * so we'll assume the controller is idle. + */ + + /* set hardware address */ + dnet_set_hwaddr(netdev); + + if (dnet_phy_init(dnet) < 0) + return -1; + + /* flush rx/tx fifos */ + writel(DNET_SYS_CTL_RXFIFOFLUSH | DNET_SYS_CTL_TXFIFOFLUSH, + &dnet->regs->SYS_CTL); + udelay(1000); + writel(0, &dnet->regs->SYS_CTL); + + config = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG); + + config |= DNET_INTERNAL_RXTX_CONTROL_RXPAUSE | + DNET_INTERNAL_RXTX_CONTROL_RXBROADCAST | + DNET_INTERNAL_RXTX_CONTROL_DROPCONTROL | + DNET_INTERNAL_RXTX_CONTROL_DISCFXFCS; + + dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, config); + + /* Enable TX and RX */ + dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG, + DNET_INTERNAL_MODE_RXEN | DNET_INTERNAL_MODE_TXEN); + + return 0; +} + +static void dnet_halt(struct eth_device *netdev) +{ + struct dnet_device *dnet = to_dnet(netdev); + + /* Disable TX and RX */ + dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG, 0); +} + +int dnet_eth_initialize(int id, void *regs, unsigned int phy_addr) +{ + struct dnet_device *dnet; + struct eth_device *netdev; + unsigned int dev_capa; + + dnet = malloc(sizeof(struct dnet_device)); + if (!dnet) { + printf("Error: Failed to allocate memory for DNET%d\n", id); + return -1; + } + memset(dnet, 0, sizeof(struct dnet_device)); + + netdev = &dnet->netdev; + + dnet->regs = (struct dnet_registers *)regs; + dnet->phy_addr = phy_addr; + + sprintf(netdev->name, "dnet%d", id); + netdev->init = dnet_init; + netdev->halt = dnet_halt; + netdev->send = dnet_send; + netdev->recv = dnet_recv; + + dev_capa = readl(&dnet->regs->VERCAPS) & 0xFFFF; + debug("%s: has %smdio, %sirq, %sgigabit, %sdma \n", netdev->name, + (dev_capa & DNET_HAS_MDIO) ? "" : "no ", + (dev_capa & DNET_HAS_IRQ) ? "" : "no ", + (dev_capa & DNET_HAS_GIGABIT) ? "" : "no ", + (dev_capa & DNET_HAS_DMA) ? "" : "no "); + + eth_register(netdev); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/dnet.h b/qemu/roms/u-boot/drivers/net/dnet.h new file mode 100644 index 000000000..fdb4fd2d3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/dnet.h @@ -0,0 +1,166 @@ +/* + * Dave Ethernet Controller driver + * + * Copyright (C) 2008 Dave S.r.l. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVERS_DNET_H__ +#define __DRIVERS_DNET_H__ + +#define DRIVERNAME "dnet" + +struct dnet_registers { + /* ALL DNET FIFO REGISTERS */ + u32 RX_LEN_FIFO; + u32 RX_DATA_FIFO; + u32 TX_LEN_FIFO; + u32 TX_DATA_FIFO; + u32 pad1[0x3c]; + /* ALL DNET CONTROL/STATUS REGISTERS */ + u32 VERCAPS; + u32 INTR_SRC; + u32 INTR_ENB; + u32 RX_STATUS; + u32 TX_STATUS; + u32 RX_FRAMES_CNT; + u32 TX_FRAMES_CNT; + u32 RX_FIFO_TH; + u32 TX_FIFO_TH; + u32 SYS_CTL; + u32 PAUSE_TMR; + u32 RX_FIFO_WCNT; + u32 TX_FIFO_WCNT; + u32 pad2[0x33]; + /* ALL DNET MAC REGISTERS */ + u32 MACREG_DATA; /* Mac-Reg Data */ + u32 MACREG_ADDR; /* Mac-Reg Addr */ + u32 pad3[0x3e]; + /* ALL DNET RX STATISTICS COUNTERS */ + u32 RX_PKT_IGNR_CNT; + u32 RX_LEN_CHK_ERR_CNT; + u32 RX_LNG_FRM_CNT; + u32 RX_SHRT_FRM_CNT; + u32 RX_IPG_VIOL_CNT; + u32 RX_CRC_ERR_CNT; + u32 RX_OK_PKT_CNT; + u32 RX_CTL_FRM_CNT; + u32 RX_PAUSE_FRM_CNT; + u32 RX_MULTICAST_CNT; + u32 RX_BROADCAST_CNT; + u32 RX_VLAN_TAG_CNT; + u32 RX_PRE_SHRINK_CNT; + u32 RX_DRIB_NIB_CNT; + u32 RX_UNSUP_OPCD_CNT; + u32 RX_BYTE_CNT; + u32 pad4[0x30]; + /* DNET TX STATISTICS COUNTERS */ + u32 TX_UNICAST_CNT; + u32 TX_PAUSE_FRM_CNT; + u32 TX_MULTICAST_CNT; + u32 TX_BRDCAST_CNT; + u32 TX_VLAN_TAG_CNT; + u32 TX_BAD_FCS_CNT; + u32 TX_JUMBO_CNT; + u32 TX_BYTE_CNT; +}; + +/* SOME INTERNAL MAC-CORE REGISTER */ +#define DNET_INTERNAL_MODE_REG 0x0 +#define DNET_INTERNAL_RXTX_CONTROL_REG 0x2 +#define DNET_INTERNAL_MAX_PKT_SIZE_REG 0x4 +#define DNET_INTERNAL_IGP_REG 0x8 +#define DNET_INTERNAL_MAC_ADDR_0_REG 0xa +#define DNET_INTERNAL_MAC_ADDR_1_REG 0xc +#define DNET_INTERNAL_MAC_ADDR_2_REG 0xe +#define DNET_INTERNAL_TX_RX_STS_REG 0x12 +#define DNET_INTERNAL_GMII_MNG_CTL_REG 0x14 +#define DNET_INTERNAL_GMII_MNG_DAT_REG 0x16 + +#define DNET_INTERNAL_GMII_MNG_CMD_FIN (1 << 14) + +#define DNET_INTERNAL_WRITE (1 << 31) + +/* MAC-CORE REGISTER FIELDS */ + +/* MAC-CORE MODE REGISTER FIELDS */ +#define DNET_INTERNAL_MODE_GBITEN (1 << 0) +#define DNET_INTERNAL_MODE_FCEN (1 << 1) +#define DNET_INTERNAL_MODE_RXEN (1 << 2) +#define DNET_INTERNAL_MODE_TXEN (1 << 3) + +/* MAC-CORE RXTX CONTROL REGISTER FIELDS */ +#define DNET_INTERNAL_RXTX_CONTROL_RXSHORTFRAME (1 << 8) +#define DNET_INTERNAL_RXTX_CONTROL_RXBROADCAST (1 << 7) +#define DNET_INTERNAL_RXTX_CONTROL_RXMULTICAST (1 << 4) +#define DNET_INTERNAL_RXTX_CONTROL_RXPAUSE (1 << 3) +#define DNET_INTERNAL_RXTX_CONTROL_DISTXFCS (1 << 2) +#define DNET_INTERNAL_RXTX_CONTROL_DISCFXFCS (1 << 1) +#define DNET_INTERNAL_RXTX_CONTROL_ENPROMISC (1 << 0) +#define DNET_INTERNAL_RXTX_CONTROL_DROPCONTROL (1 << 6) +#define DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP (1 << 5) + +/* SYSTEM CONTROL REGISTER FIELDS */ +#define DNET_SYS_CTL_IGNORENEXTPKT (1 << 0) +#define DNET_SYS_CTL_SENDPAUSE (1 << 2) +#define DNET_SYS_CTL_RXFIFOFLUSH (1 << 3) +#define DNET_SYS_CTL_TXFIFOFLUSH (1 << 4) + +/* TX STATUS REGISTER FIELDS */ +#define DNET_TX_STATUS_FIFO_ALMOST_EMPTY (1 << 2) +#define DNET_TX_STATUS_FIFO_ALMOST_FULL (1 << 1) + +/* INTERRUPT SOURCE REGISTER FIELDS */ +#define DNET_INTR_SRC_TX_PKTSENT (1 << 0) +#define DNET_INTR_SRC_TX_FIFOAF (1 << 1) +#define DNET_INTR_SRC_TX_FIFOAE (1 << 2) +#define DNET_INTR_SRC_TX_DISCFRM (1 << 3) +#define DNET_INTR_SRC_TX_FIFOFULL (1 << 4) +#define DNET_INTR_SRC_RX_CMDFIFOAF (1 << 8) +#define DNET_INTR_SRC_RX_CMDFIFOFF (1 << 9) +#define DNET_INTR_SRC_RX_DATAFIFOFF (1 << 10) +#define DNET_INTR_SRC_TX_SUMMARY (1 << 16) +#define DNET_INTR_SRC_RX_SUMMARY (1 << 17) +#define DNET_INTR_SRC_PHY (1 << 19) + +/* INTERRUPT ENABLE REGISTER FIELDS */ +#define DNET_INTR_ENB_TX_PKTSENT (1 << 0) +#define DNET_INTR_ENB_TX_FIFOAF (1 << 1) +#define DNET_INTR_ENB_TX_FIFOAE (1 << 2) +#define DNET_INTR_ENB_TX_DISCFRM (1 << 3) +#define DNET_INTR_ENB_TX_FIFOFULL (1 << 4) +#define DNET_INTR_ENB_RX_PKTRDY (1 << 8) +#define DNET_INTR_ENB_RX_FIFOAF (1 << 9) +#define DNET_INTR_ENB_RX_FIFOERR (1 << 10) +#define DNET_INTR_ENB_RX_ERROR (1 << 11) +#define DNET_INTR_ENB_RX_FIFOFULL (1 << 12) +#define DNET_INTR_ENB_RX_FIFOAE (1 << 13) +#define DNET_INTR_ENB_TX_SUMMARY (1 << 16) +#define DNET_INTR_ENB_RX_SUMMARY (1 << 17) +#define DNET_INTR_ENB_GLOBAL_ENABLE (1 << 18) + +/* + * Capabilities. Used by the driver to know the capabilities that + * the ethernet controller inside the FPGA have. + */ + +#define DNET_HAS_MDIO (1 << 0) +#define DNET_HAS_IRQ (1 << 1) +#define DNET_HAS_GIGABIT (1 << 2) +#define DNET_HAS_DMA (1 << 3) + +#define DNET_HAS_MII (1 << 4) /* or GMII */ +#define DNET_HAS_RMII (1 << 5) /* or RGMII */ + +#define DNET_CAPS_MASK 0xFFFF + +#define DNET_FIFO_SIZE 2048 /* 2K x 32 bit */ +#define DNET_FIFO_TX_DATA_AF_TH (DNET_FIFO_SIZE - 384) /* 384 = 1536 / 4 */ +#define DNET_FIFO_TX_DATA_AE_TH (384) + +#define DNET_FIFO_RX_CMD_AF_TH (1 << 16) /* just one frame inside the FIFO */ + +#endif diff --git a/qemu/roms/u-boot/drivers/net/e1000.c b/qemu/roms/u-boot/drivers/net/e1000.c new file mode 100644 index 000000000..9d9b259d6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/e1000.c @@ -0,0 +1,5307 @@ +/************************************************************************** +Intel Pro 1000 for ppcboot/das-u-boot +Drivers are port from Intel's Linux driver e1000-4.3.15 +and from Etherboot pro 1000 driver by mrakes at vivato dot net +tested on both gig copper and gig fiber boards +***************************************************************************/ +/******************************************************************************* + + + Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved. + + * SPDX-License-Identifier: GPL-2.0+ + + Contact Information: + Linux NICS + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ +/* + * Copyright (C) Archway Digital Solutions. + * + * written by Chrsitopher Li or + * 2/9/2002 + * + * Copyright (C) Linux Networx. + * Massive upgrade to work with the new intel gigabit NICs. + * + * + * Copyright 2011 Freescale Semiconductor, Inc. + */ + +#include "e1000.h" + +#define TOUT_LOOP 100000 + +#define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v)) +#define bus_to_phys(devno, a) pci_mem_to_phys(devno, a) + +#define E1000_DEFAULT_PCI_PBA 0x00000030 +#define E1000_DEFAULT_PCIE_PBA 0x000a0026 + +/* NIC specific static variables go here */ + +static char tx_pool[128 + 16]; +static char rx_pool[128 + 16]; +static char packet[2096]; + +static struct e1000_tx_desc *tx_base; +static struct e1000_rx_desc *rx_base; + +static int tx_tail; +static int rx_tail, rx_last; + +static struct pci_device_id e1000_supported[] = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82542}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82543GC_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82543GC_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82544EI_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82544EI_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82544GC_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82544GC_LOM}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82540EM}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82545EM_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82545GM_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82546EB_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82545EM_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82546EB_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82546GB_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82540EM_LOM}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82541ER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82541GI_LF}, + /* E1000 PCIe card */ + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_FIBER }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_SERDES }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_QUAD_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571PT_QUAD_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_QUAD_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_QUAD_COPPER_LOWPROFILE}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_SERDES_DUAL}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_SERDES_QUAD}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82572EI_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82572EI_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82572EI_SERDES}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82572EI}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82573E}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82573E_IAMT}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82573L}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82574L}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82546GB_QUAD_COPPER_KSP3}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_COPPER_DPT}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_SERDES_DPT}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_COPPER_SPT}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_SERDES_SPT}, + {} +}; + +/* Function forward declarations */ +static int e1000_setup_link(struct eth_device *nic); +static int e1000_setup_fiber_link(struct eth_device *nic); +static int e1000_setup_copper_link(struct eth_device *nic); +static int e1000_phy_setup_autoneg(struct e1000_hw *hw); +static void e1000_config_collision_dist(struct e1000_hw *hw); +static int e1000_config_mac_to_phy(struct e1000_hw *hw); +static int e1000_config_fc_after_link_up(struct e1000_hw *hw); +static int e1000_check_for_link(struct eth_device *nic); +static int e1000_wait_autoneg(struct e1000_hw *hw); +static int e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t * speed, + uint16_t * duplex); +static int e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, + uint16_t * phy_data); +static int e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, + uint16_t phy_data); +static int32_t e1000_phy_hw_reset(struct e1000_hw *hw); +static int e1000_phy_reset(struct e1000_hw *hw); +static int e1000_detect_gig_phy(struct e1000_hw *hw); +static void e1000_set_media_type(struct e1000_hw *hw); + +static int32_t e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask); +static int32_t e1000_check_phy_reset_block(struct e1000_hw *hw); + +#ifndef CONFIG_E1000_NO_NVM +static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw); +static int32_t e1000_read_eeprom(struct e1000_hw *hw, uint16_t offset, + uint16_t words, + uint16_t *data); +/****************************************************************************** + * Raises the EEPROM's clock input. + * + * hw - Struct containing variables accessed by shared code + * eecd - EECD's current value + *****************************************************************************/ +void e1000_raise_ee_clk(struct e1000_hw *hw, uint32_t * eecd) +{ + /* Raise the clock input to the EEPROM (by setting the SK bit), and then + * wait 50 microseconds. + */ + *eecd = *eecd | E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, *eecd); + E1000_WRITE_FLUSH(hw); + udelay(50); +} + +/****************************************************************************** + * Lowers the EEPROM's clock input. + * + * hw - Struct containing variables accessed by shared code + * eecd - EECD's current value + *****************************************************************************/ +void e1000_lower_ee_clk(struct e1000_hw *hw, uint32_t * eecd) +{ + /* Lower the clock input to the EEPROM (by clearing the SK bit), and then + * wait 50 microseconds. + */ + *eecd = *eecd & ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, *eecd); + E1000_WRITE_FLUSH(hw); + udelay(50); +} + +/****************************************************************************** + * Shift data bits out to the EEPROM. + * + * hw - Struct containing variables accessed by shared code + * data - data to send to the EEPROM + * count - number of bits to shift out + *****************************************************************************/ +static void +e1000_shift_out_ee_bits(struct e1000_hw *hw, uint16_t data, uint16_t count) +{ + uint32_t eecd; + uint32_t mask; + + /* We need to shift "count" bits out to the EEPROM. So, value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + */ + mask = 0x01 << (count - 1); + eecd = E1000_READ_REG(hw, EECD); + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + do { + /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1", + * and then raising and then lowering the clock (the SK bit controls + * the clock input to the EEPROM). A "0" is shifted out to the EEPROM + * by setting "DI" to "0" and then raising and then lowering the clock. + */ + eecd &= ~E1000_EECD_DI; + + if (data & mask) + eecd |= E1000_EECD_DI; + + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + + udelay(50); + + e1000_raise_ee_clk(hw, &eecd); + e1000_lower_ee_clk(hw, &eecd); + + mask = mask >> 1; + + } while (mask); + + /* We leave the "DI" bit set to "0" when we leave this routine. */ + eecd &= ~E1000_EECD_DI; + E1000_WRITE_REG(hw, EECD, eecd); +} + +/****************************************************************************** + * Shift data bits in from the EEPROM + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static uint16_t +e1000_shift_in_ee_bits(struct e1000_hw *hw, uint16_t count) +{ + uint32_t eecd; + uint32_t i; + uint16_t data; + + /* In order to read a register from the EEPROM, we need to shift 'count' + * bits in from the EEPROM. Bits are "shifted in" by raising the clock + * input to the EEPROM (setting the SK bit), and then reading the + * value of the "DO" bit. During this "shifting in" process the + * "DI" bit should always be clear. + */ + + eecd = E1000_READ_REG(hw, EECD); + + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + data = 0; + + for (i = 0; i < count; i++) { + data = data << 1; + e1000_raise_ee_clk(hw, &eecd); + + eecd = E1000_READ_REG(hw, EECD); + + eecd &= ~(E1000_EECD_DI); + if (eecd & E1000_EECD_DO) + data |= 1; + + e1000_lower_ee_clk(hw, &eecd); + } + + return data; +} + +/****************************************************************************** + * Returns EEPROM to a "standby" state + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +void e1000_standby_eeprom(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd; + + eecd = E1000_READ_REG(hw, EECD); + + if (eeprom->type == e1000_eeprom_microwire) { + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + + /* Clock high */ + eecd |= E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + + /* Select EEPROM */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + + /* Clock low */ + eecd &= ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + } else if (eeprom->type == e1000_eeprom_spi) { + /* Toggle CS to flush commands */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + eecd &= ~E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + } +} + +/*************************************************************************** +* Description: Determines if the onboard NVM is FLASH or EEPROM. +* +* hw - Struct containing variables accessed by shared code +****************************************************************************/ +static bool e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw) +{ + uint32_t eecd = 0; + + DEBUGFUNC(); + + if (hw->mac_type == e1000_ich8lan) + return false; + + if (hw->mac_type == e1000_82573 || hw->mac_type == e1000_82574) { + eecd = E1000_READ_REG(hw, EECD); + + /* Isolate bits 15 & 16 */ + eecd = ((eecd >> 15) & 0x03); + + /* If both bits are set, device is Flash type */ + if (eecd == 0x03) + return false; + } + return true; +} + +/****************************************************************************** + * Prepares EEPROM for access + * + * hw - Struct containing variables accessed by shared code + * + * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This + * function should be called before issuing a command to the EEPROM. + *****************************************************************************/ +int32_t e1000_acquire_eeprom(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd, i = 0; + + DEBUGFUNC(); + + if (e1000_swfw_sync_acquire(hw, E1000_SWFW_EEP_SM)) + return -E1000_ERR_SWFW_SYNC; + eecd = E1000_READ_REG(hw, EECD); + + if (hw->mac_type != e1000_82573 || hw->mac_type != e1000_82574) { + /* Request EEPROM Access */ + if (hw->mac_type > e1000_82544) { + eecd |= E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + eecd = E1000_READ_REG(hw, EECD); + while ((!(eecd & E1000_EECD_GNT)) && + (i < E1000_EEPROM_GRANT_ATTEMPTS)) { + i++; + udelay(5); + eecd = E1000_READ_REG(hw, EECD); + } + if (!(eecd & E1000_EECD_GNT)) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + DEBUGOUT("Could not acquire EEPROM grant\n"); + return -E1000_ERR_EEPROM; + } + } + } + + /* Setup EEPROM for Read/Write */ + + if (eeprom->type == e1000_eeprom_microwire) { + /* Clear SK and DI */ + eecd &= ~(E1000_EECD_DI | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + + /* Set CS */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + } else if (eeprom->type == e1000_eeprom_spi) { + /* Clear SK and CS */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + udelay(1); + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Sets up eeprom variables in the hw struct. Must be called after mac_type + * is configured. Additionally, if this is ICH8, the flash controller GbE + * registers must be mapped, or this will crash. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t e1000_init_eeprom_params(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd = E1000_READ_REG(hw, EECD); + int32_t ret_val = E1000_SUCCESS; + uint16_t eeprom_size; + + DEBUGFUNC(); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + eeprom->type = e1000_eeprom_microwire; + eeprom->word_size = 64; + eeprom->opcode_bits = 3; + eeprom->address_bits = 6; + eeprom->delay_usec = 50; + eeprom->use_eerd = false; + eeprom->use_eewr = false; + break; + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + eeprom->type = e1000_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if (eecd & E1000_EECD_SIZE) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + eeprom->use_eerd = false; + eeprom->use_eewr = false; + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + if (eecd & E1000_EECD_TYPE) { + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + } else { + eeprom->type = e1000_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + } + eeprom->use_eerd = false; + eeprom->use_eewr = false; + break; + case e1000_82571: + case e1000_82572: + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = false; + eeprom->use_eewr = false; + break; + case e1000_82573: + case e1000_82574: + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = true; + eeprom->use_eewr = true; + if (e1000_is_onboard_nvm_eeprom(hw) == false) { + eeprom->type = e1000_eeprom_flash; + eeprom->word_size = 2048; + + /* Ensure that the Autonomous FLASH update bit is cleared due to + * Flash update issue on parts which use a FLASH for NVM. */ + eecd &= ~E1000_EECD_AUPDEN; + E1000_WRITE_REG(hw, EECD, eecd); + } + break; + case e1000_80003es2lan: + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = true; + eeprom->use_eewr = false; + break; + + /* ich8lan does not support currently. if needed, please + * add corresponding code and functions. + */ +#if 0 + case e1000_ich8lan: + { + int32_t i = 0; + + eeprom->type = e1000_eeprom_ich8; + eeprom->use_eerd = false; + eeprom->use_eewr = false; + eeprom->word_size = E1000_SHADOW_RAM_WORDS; + uint32_t flash_size = E1000_READ_ICH_FLASH_REG(hw, + ICH_FLASH_GFPREG); + /* Zero the shadow RAM structure. But don't load it from NVM + * so as to save time for driver init */ + if (hw->eeprom_shadow_ram != NULL) { + for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { + hw->eeprom_shadow_ram[i].modified = false; + hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF; + } + } + + hw->flash_base_addr = (flash_size & ICH_GFPREG_BASE_MASK) * + ICH_FLASH_SECTOR_SIZE; + + hw->flash_bank_size = ((flash_size >> 16) + & ICH_GFPREG_BASE_MASK) + 1; + hw->flash_bank_size -= (flash_size & ICH_GFPREG_BASE_MASK); + + hw->flash_bank_size *= ICH_FLASH_SECTOR_SIZE; + + hw->flash_bank_size /= 2 * sizeof(uint16_t); + break; + } +#endif + default: + break; + } + + if (eeprom->type == e1000_eeprom_spi) { + /* eeprom_size will be an enum [0..8] that maps + * to eeprom sizes 128B to + * 32KB (incremented by powers of 2). + */ + if (hw->mac_type <= e1000_82547_rev_2) { + /* Set to default value for initial eeprom read. */ + eeprom->word_size = 64; + ret_val = e1000_read_eeprom(hw, EEPROM_CFG, 1, + &eeprom_size); + if (ret_val) + return ret_val; + eeprom_size = (eeprom_size & EEPROM_SIZE_MASK) + >> EEPROM_SIZE_SHIFT; + /* 256B eeprom size was not supported in earlier + * hardware, so we bump eeprom_size up one to + * ensure that "1" (which maps to 256B) is never + * the result used in the shifting logic below. */ + if (eeprom_size) + eeprom_size++; + } else { + eeprom_size = (uint16_t)((eecd & + E1000_EECD_SIZE_EX_MASK) >> + E1000_EECD_SIZE_EX_SHIFT); + } + + eeprom->word_size = 1 << (eeprom_size + EEPROM_WORD_SIZE_SHIFT); + } + return ret_val; +} + +/****************************************************************************** + * Polls the status bit (bit 1) of the EERD to determine when the read is done. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd) +{ + uint32_t attempts = 100000; + uint32_t i, reg = 0; + int32_t done = E1000_ERR_EEPROM; + + for (i = 0; i < attempts; i++) { + if (eerd == E1000_EEPROM_POLL_READ) + reg = E1000_READ_REG(hw, EERD); + else + reg = E1000_READ_REG(hw, EEWR); + + if (reg & E1000_EEPROM_RW_REG_DONE) { + done = E1000_SUCCESS; + break; + } + udelay(5); + } + + return done; +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM using the EERD register. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +static int32_t +e1000_read_eeprom_eerd(struct e1000_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + uint32_t i, eerd = 0; + int32_t error = 0; + + for (i = 0; i < words; i++) { + eerd = ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) + + E1000_EEPROM_RW_REG_START; + + E1000_WRITE_REG(hw, EERD, eerd); + error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_READ); + + if (error) + break; + data[i] = (E1000_READ_REG(hw, EERD) >> + E1000_EEPROM_RW_REG_DATA); + + } + + return error; +} + +void e1000_release_eeprom(struct e1000_hw *hw) +{ + uint32_t eecd; + + DEBUGFUNC(); + + eecd = E1000_READ_REG(hw, EECD); + + if (hw->eeprom.type == e1000_eeprom_spi) { + eecd |= E1000_EECD_CS; /* Pull CS high */ + eecd &= ~E1000_EECD_SK; /* Lower SCK */ + + E1000_WRITE_REG(hw, EECD, eecd); + + udelay(hw->eeprom.delay_usec); + } else if (hw->eeprom.type == e1000_eeprom_microwire) { + /* cleanup eeprom */ + + /* CS on Microwire is active-high */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_DI); + + E1000_WRITE_REG(hw, EECD, eecd); + + /* Rising edge of clock */ + eecd |= E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(hw->eeprom.delay_usec); + + /* Falling edge of clock */ + eecd &= ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(hw->eeprom.delay_usec); + } + + /* Stop requesting EEPROM access */ + if (hw->mac_type > e1000_82544) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + } +} +/****************************************************************************** + * Reads a 16 bit word from the EEPROM. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +e1000_spi_eeprom_ready(struct e1000_hw *hw) +{ + uint16_t retry_count = 0; + uint8_t spi_stat_reg; + + DEBUGFUNC(); + + /* Read "Status Register" repeatedly until the LSB is cleared. The + * EEPROM will signal that the command has been completed by clearing + * bit 0 of the internal status register. If it's not cleared within + * 5 milliseconds, then error out. + */ + retry_count = 0; + do { + e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI, + hw->eeprom.opcode_bits); + spi_stat_reg = (uint8_t)e1000_shift_in_ee_bits(hw, 8); + if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI)) + break; + + udelay(5); + retry_count += 5; + + e1000_standby_eeprom(hw); + } while (retry_count < EEPROM_MAX_RETRY_SPI); + + /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and + * only 0-5mSec on 5V devices) + */ + if (retry_count >= EEPROM_MAX_RETRY_SPI) { + DEBUGOUT("SPI EEPROM Status error\n"); + return -E1000_ERR_EEPROM; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + *****************************************************************************/ +static int32_t +e1000_read_eeprom(struct e1000_hw *hw, uint16_t offset, + uint16_t words, uint16_t *data) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t i = 0; + + DEBUGFUNC(); + + /* If eeprom is not yet detected, do so now */ + if (eeprom->word_size == 0) + e1000_init_eeprom_params(hw); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. + */ + if ((offset >= eeprom->word_size) || + (words > eeprom->word_size - offset) || + (words == 0)) { + DEBUGOUT("\"words\" parameter out of bounds." + "Words = %d, size = %d\n", offset, eeprom->word_size); + return -E1000_ERR_EEPROM; + } + + /* EEPROM's that don't use EERD to read require us to bit-bang the SPI + * directly. In this case, we need to acquire the EEPROM so that + * FW or other port software does not interrupt. + */ + if (e1000_is_onboard_nvm_eeprom(hw) == true && + hw->eeprom.use_eerd == false) { + + /* Prepare the EEPROM for bit-bang reading */ + if (e1000_acquire_eeprom(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + } + + /* Eerd register EEPROM access requires no eeprom aquire/release */ + if (eeprom->use_eerd == true) + return e1000_read_eeprom_eerd(hw, offset, words, data); + + /* ich8lan does not support currently. if needed, please + * add corresponding code and functions. + */ +#if 0 + /* ICH EEPROM access is done via the ICH flash controller */ + if (eeprom->type == e1000_eeprom_ich8) + return e1000_read_eeprom_ich8(hw, offset, words, data); +#endif + /* Set up the SPI or Microwire EEPROM for bit-bang reading. We have + * acquired the EEPROM at this point, so any returns should relase it */ + if (eeprom->type == e1000_eeprom_spi) { + uint16_t word_in; + uint8_t read_opcode = EEPROM_READ_OPCODE_SPI; + + if (e1000_spi_eeprom_ready(hw)) { + e1000_release_eeprom(hw); + return -E1000_ERR_EEPROM; + } + + e1000_standby_eeprom(hw); + + /* Some SPI eeproms use the 8th address bit embedded in + * the opcode */ + if ((eeprom->address_bits == 8) && (offset >= 128)) + read_opcode |= EEPROM_A8_OPCODE_SPI; + + /* Send the READ command (opcode + addr) */ + e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits); + e1000_shift_out_ee_bits(hw, (uint16_t)(offset*2), + eeprom->address_bits); + + /* Read the data. The address of the eeprom internally + * increments with each byte (spi) being read, saving on the + * overhead of eeprom setup and tear-down. The address + * counter will roll over if reading beyond the size of + * the eeprom, thus allowing the entire memory to be read + * starting from any offset. */ + for (i = 0; i < words; i++) { + word_in = e1000_shift_in_ee_bits(hw, 16); + data[i] = (word_in >> 8) | (word_in << 8); + } + } else if (eeprom->type == e1000_eeprom_microwire) { + for (i = 0; i < words; i++) { + /* Send the READ command (opcode + addr) */ + e1000_shift_out_ee_bits(hw, + EEPROM_READ_OPCODE_MICROWIRE, + eeprom->opcode_bits); + e1000_shift_out_ee_bits(hw, (uint16_t)(offset + i), + eeprom->address_bits); + + /* Read the data. For microwire, each word requires + * the overhead of eeprom setup and tear-down. */ + data[i] = e1000_shift_in_ee_bits(hw, 16); + e1000_standby_eeprom(hw); + } + } + + /* End this read operation */ + e1000_release_eeprom(hw); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Verifies that the EEPROM has a valid checksum + * + * hw - Struct containing variables accessed by shared code + * + * Reads the first 64 16 bit words of the EEPROM and sums the values read. + * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is + * valid. + *****************************************************************************/ +static int e1000_validate_eeprom_checksum(struct e1000_hw *hw) +{ + uint16_t i, checksum, checksum_reg, *buf; + + DEBUGFUNC(); + + /* Allocate a temporary buffer */ + buf = malloc(sizeof(buf[0]) * (EEPROM_CHECKSUM_REG + 1)); + if (!buf) { + E1000_ERR(hw->nic, "Unable to allocate EEPROM buffer!\n"); + return -E1000_ERR_EEPROM; + } + + /* Read the EEPROM */ + if (e1000_read_eeprom(hw, 0, EEPROM_CHECKSUM_REG + 1, buf) < 0) { + E1000_ERR(hw->nic, "Unable to read EEPROM!\n"); + return -E1000_ERR_EEPROM; + } + + /* Compute the checksum */ + checksum = 0; + for (i = 0; i < EEPROM_CHECKSUM_REG; i++) + checksum += buf[i]; + checksum = ((uint16_t)EEPROM_SUM) - checksum; + checksum_reg = buf[i]; + + /* Verify it! */ + if (checksum == checksum_reg) + return 0; + + /* Hrm, verification failed, print an error */ + E1000_ERR(hw->nic, "EEPROM checksum is incorrect!\n"); + E1000_ERR(hw->nic, " ...register was 0x%04hx, calculated 0x%04hx\n", + checksum_reg, checksum); + + return -E1000_ERR_EEPROM; +} +#endif /* CONFIG_E1000_NO_NVM */ + +/***************************************************************************** + * Set PHY to class A mode + * Assumes the following operations will follow to enable the new class mode. + * 1. Do a PHY soft reset + * 2. Restart auto-negotiation or force link. + * + * hw - Struct containing variables accessed by shared code + ****************************************************************************/ +static int32_t +e1000_set_phy_mode(struct e1000_hw *hw) +{ +#ifndef CONFIG_E1000_NO_NVM + int32_t ret_val; + uint16_t eeprom_data; + + DEBUGFUNC(); + + if ((hw->mac_type == e1000_82545_rev_3) && + (hw->media_type == e1000_media_type_copper)) { + ret_val = e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, + 1, &eeprom_data); + if (ret_val) + return ret_val; + + if ((eeprom_data != EEPROM_RESERVED_WORD) && + (eeprom_data & EEPROM_PHY_CLASS_A)) { + ret_val = e1000_write_phy_reg(hw, + M88E1000_PHY_PAGE_SELECT, 0x000B); + if (ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, + M88E1000_PHY_GEN_CONTROL, 0x8104); + if (ret_val) + return ret_val; + + hw->phy_reset_disable = false; + } + } +#endif + return E1000_SUCCESS; +} + +#ifndef CONFIG_E1000_NO_NVM +/*************************************************************************** + * + * Obtaining software semaphore bit (SMBI) before resetting PHY. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_RESET if fail to obtain semaphore. + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +static int32_t +e1000_get_software_semaphore(struct e1000_hw *hw) +{ + int32_t timeout = hw->eeprom.word_size + 1; + uint32_t swsm; + + DEBUGFUNC(); + + if (hw->mac_type != e1000_80003es2lan) + return E1000_SUCCESS; + + while (timeout) { + swsm = E1000_READ_REG(hw, SWSM); + /* If SMBI bit cleared, it is now set and we hold + * the semaphore */ + if (!(swsm & E1000_SWSM_SMBI)) + break; + mdelay(1); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); + return -E1000_ERR_RESET; + } + + return E1000_SUCCESS; +} +#endif + +/*************************************************************************** + * This function clears HW semaphore bits. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - None. + * + ***************************************************************************/ +static void +e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw) +{ +#ifndef CONFIG_E1000_NO_NVM + uint32_t swsm; + + DEBUGFUNC(); + + if (!hw->eeprom_semaphore_present) + return; + + swsm = E1000_READ_REG(hw, SWSM); + if (hw->mac_type == e1000_80003es2lan) { + /* Release both semaphores. */ + swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); + } else + swsm &= ~(E1000_SWSM_SWESMBI); + E1000_WRITE_REG(hw, SWSM, swsm); +#endif +} + +/*************************************************************************** + * + * Using the combination of SMBI and SWESMBI semaphore bits when resetting + * adapter or Eeprom access. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_EEPROM if fail to access EEPROM. + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +static int32_t +e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw) +{ +#ifndef CONFIG_E1000_NO_NVM + int32_t timeout; + uint32_t swsm; + + DEBUGFUNC(); + + if (!hw->eeprom_semaphore_present) + return E1000_SUCCESS; + + if (hw->mac_type == e1000_80003es2lan) { + /* Get the SW semaphore. */ + if (e1000_get_software_semaphore(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + } + + /* Get the FW semaphore. */ + timeout = hw->eeprom.word_size + 1; + while (timeout) { + swsm = E1000_READ_REG(hw, SWSM); + swsm |= E1000_SWSM_SWESMBI; + E1000_WRITE_REG(hw, SWSM, swsm); + /* if we managed to set the bit we got the semaphore. */ + swsm = E1000_READ_REG(hw, SWSM); + if (swsm & E1000_SWSM_SWESMBI) + break; + + udelay(50); + timeout--; + } + + if (!timeout) { + /* Release semaphores */ + e1000_put_hw_eeprom_semaphore(hw); + DEBUGOUT("Driver can't access the Eeprom - " + "SWESMBI bit is set.\n"); + return -E1000_ERR_EEPROM; + } +#endif + return E1000_SUCCESS; +} + +static int32_t +e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask) +{ + uint32_t swfw_sync = 0; + uint32_t swmask = mask; + uint32_t fwmask = mask << 16; + int32_t timeout = 200; + + DEBUGFUNC(); + while (timeout) { + if (e1000_get_hw_eeprom_semaphore(hw)) + return -E1000_ERR_SWFW_SYNC; + + swfw_sync = E1000_READ_REG(hw, SW_FW_SYNC); + if (!(swfw_sync & (fwmask | swmask))) + break; + + /* firmware currently using resource (fwmask) */ + /* or other software thread currently using resource (swmask) */ + e1000_put_hw_eeprom_semaphore(hw); + mdelay(5); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + return -E1000_ERR_SWFW_SYNC; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, SW_FW_SYNC, swfw_sync); + + e1000_put_hw_eeprom_semaphore(hw); + return E1000_SUCCESS; +} + +static bool e1000_is_second_port(struct e1000_hw *hw) +{ + switch (hw->mac_type) { + case e1000_80003es2lan: + case e1000_82546: + case e1000_82571: + if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) + return true; + /* Fallthrough */ + default: + return false; + } +} + +#ifndef CONFIG_E1000_NO_NVM +/****************************************************************************** + * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the + * second function of dual function devices + * + * nic - Struct containing variables accessed by shared code + *****************************************************************************/ +static int +e1000_read_mac_addr(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint16_t offset; + uint16_t eeprom_data; + int i; + + DEBUGFUNC(); + + for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) { + offset = i >> 1; + if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + nic->enetaddr[i] = eeprom_data & 0xff; + nic->enetaddr[i + 1] = (eeprom_data >> 8) & 0xff; + } + + /* Invert the last bit if this is the second device */ + if (e1000_is_second_port(hw)) + nic->enetaddr[5] ^= 1; + +#ifdef CONFIG_E1000_FALLBACK_MAC + if (!is_valid_ether_addr(nic->enetaddr)) { + unsigned char fb_mac[NODE_ADDRESS_SIZE] = CONFIG_E1000_FALLBACK_MAC; + + memcpy (nic->enetaddr, fb_mac, NODE_ADDRESS_SIZE); + } +#endif + return 0; +} +#endif + +/****************************************************************************** + * Initializes receive address filters. + * + * hw - Struct containing variables accessed by shared code + * + * Places the MAC address in receive address register 0 and clears the rest + * of the receive addresss registers. Clears the multicast table. Assumes + * the receiver is in reset when the routine is called. + *****************************************************************************/ +static void +e1000_init_rx_addrs(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint32_t i; + uint32_t addr_low; + uint32_t addr_high; + + DEBUGFUNC(); + + /* Setup the receive address. */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + addr_low = (nic->enetaddr[0] | + (nic->enetaddr[1] << 8) | + (nic->enetaddr[2] << 16) | (nic->enetaddr[3] << 24)); + + addr_high = (nic->enetaddr[4] | (nic->enetaddr[5] << 8) | E1000_RAH_AV); + + E1000_WRITE_REG_ARRAY(hw, RA, 0, addr_low); + E1000_WRITE_REG_ARRAY(hw, RA, 1, addr_high); + + /* Zero out the other 15 receive addresses. */ + DEBUGOUT("Clearing RAR[1-15]\n"); + for (i = 1; i < E1000_RAR_ENTRIES; i++) { + E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); + E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); + } +} + +/****************************************************************************** + * Clears the VLAN filer table + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_clear_vfta(struct e1000_hw *hw) +{ + uint32_t offset; + + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, 0); +} + +/****************************************************************************** + * Set the mac type member in the hw struct. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_set_mac_type(struct e1000_hw *hw) +{ + DEBUGFUNC(); + + switch (hw->device_id) { + case E1000_DEV_ID_82542: + switch (hw->revision_id) { + case E1000_82542_2_0_REV_ID: + hw->mac_type = e1000_82542_rev2_0; + break; + case E1000_82542_2_1_REV_ID: + hw->mac_type = e1000_82542_rev2_1; + break; + default: + /* Invalid 82542 revision ID */ + return -E1000_ERR_MAC_TYPE; + } + break; + case E1000_DEV_ID_82543GC_FIBER: + case E1000_DEV_ID_82543GC_COPPER: + hw->mac_type = e1000_82543; + break; + case E1000_DEV_ID_82544EI_COPPER: + case E1000_DEV_ID_82544EI_FIBER: + case E1000_DEV_ID_82544GC_COPPER: + case E1000_DEV_ID_82544GC_LOM: + hw->mac_type = e1000_82544; + break; + case E1000_DEV_ID_82540EM: + case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82540EP: + case E1000_DEV_ID_82540EP_LOM: + case E1000_DEV_ID_82540EP_LP: + hw->mac_type = e1000_82540; + break; + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + hw->mac_type = e1000_82545; + break; + case E1000_DEV_ID_82545GM_COPPER: + case E1000_DEV_ID_82545GM_FIBER: + case E1000_DEV_ID_82545GM_SERDES: + hw->mac_type = e1000_82545_rev_3; + break; + case E1000_DEV_ID_82546EB_COPPER: + case E1000_DEV_ID_82546EB_FIBER: + case E1000_DEV_ID_82546EB_QUAD_COPPER: + hw->mac_type = e1000_82546; + break; + case E1000_DEV_ID_82546GB_COPPER: + case E1000_DEV_ID_82546GB_FIBER: + case E1000_DEV_ID_82546GB_SERDES: + case E1000_DEV_ID_82546GB_PCIE: + case E1000_DEV_ID_82546GB_QUAD_COPPER: + case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: + hw->mac_type = e1000_82546_rev_3; + break; + case E1000_DEV_ID_82541EI: + case E1000_DEV_ID_82541EI_MOBILE: + case E1000_DEV_ID_82541ER_LOM: + hw->mac_type = e1000_82541; + break; + case E1000_DEV_ID_82541ER: + case E1000_DEV_ID_82541GI: + case E1000_DEV_ID_82541GI_LF: + case E1000_DEV_ID_82541GI_MOBILE: + hw->mac_type = e1000_82541_rev_2; + break; + case E1000_DEV_ID_82547EI: + case E1000_DEV_ID_82547EI_MOBILE: + hw->mac_type = e1000_82547; + break; + case E1000_DEV_ID_82547GI: + hw->mac_type = e1000_82547_rev_2; + break; + case E1000_DEV_ID_82571EB_COPPER: + case E1000_DEV_ID_82571EB_FIBER: + case E1000_DEV_ID_82571EB_SERDES: + case E1000_DEV_ID_82571EB_SERDES_DUAL: + case E1000_DEV_ID_82571EB_SERDES_QUAD: + case E1000_DEV_ID_82571EB_QUAD_COPPER: + case E1000_DEV_ID_82571PT_QUAD_COPPER: + case E1000_DEV_ID_82571EB_QUAD_FIBER: + case E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE: + hw->mac_type = e1000_82571; + break; + case E1000_DEV_ID_82572EI_COPPER: + case E1000_DEV_ID_82572EI_FIBER: + case E1000_DEV_ID_82572EI_SERDES: + case E1000_DEV_ID_82572EI: + hw->mac_type = e1000_82572; + break; + case E1000_DEV_ID_82573E: + case E1000_DEV_ID_82573E_IAMT: + case E1000_DEV_ID_82573L: + hw->mac_type = e1000_82573; + break; + case E1000_DEV_ID_82574L: + hw->mac_type = e1000_82574; + break; + case E1000_DEV_ID_80003ES2LAN_COPPER_SPT: + case E1000_DEV_ID_80003ES2LAN_SERDES_SPT: + case E1000_DEV_ID_80003ES2LAN_COPPER_DPT: + case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: + hw->mac_type = e1000_80003es2lan; + break; + case E1000_DEV_ID_ICH8_IGP_M_AMT: + case E1000_DEV_ID_ICH8_IGP_AMT: + case E1000_DEV_ID_ICH8_IGP_C: + case E1000_DEV_ID_ICH8_IFE: + case E1000_DEV_ID_ICH8_IFE_GT: + case E1000_DEV_ID_ICH8_IFE_G: + case E1000_DEV_ID_ICH8_IGP_M: + hw->mac_type = e1000_ich8lan; + break; + default: + /* Should never have loaded on this device */ + return -E1000_ERR_MAC_TYPE; + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reset the transmit and receive units; mask and clear all interrupts. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +void +e1000_reset_hw(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint32_t ctrl_ext; + uint32_t manc; + uint32_t pba = 0; + + DEBUGFUNC(); + + /* get the correct pba value for both PCI and PCIe*/ + if (hw->mac_type < e1000_82571) + pba = E1000_DEFAULT_PCI_PBA; + else + pba = E1000_DEFAULT_PCIE_PBA; + + /* For 82542 (rev 2.0), disable MWI before issuing a device reset */ + if (hw->mac_type == e1000_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + pci_write_config_word(hw->pdev, PCI_COMMAND, + hw->pci_cmd_word & ~PCI_COMMAND_INVALIDATE); + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, IMC, 0xffffffff); + + /* Disable the Transmit and Receive units. Then delay to allow + * any pending transactions to complete before we hit the MAC with + * the global reset. + */ + E1000_WRITE_REG(hw, RCTL, 0); + E1000_WRITE_REG(hw, TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */ + hw->tbi_compatibility_on = false; + + /* Delay to allow any outstanding PCI transactions to complete before + * resetting the device + */ + mdelay(10); + + /* Issue a global reset to the MAC. This will reset the chip's + * transmit, receive, DMA, and link units. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + DEBUGOUT("Issuing a global reset to MAC\n"); + ctrl = E1000_READ_REG(hw, CTRL); + + E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); + + /* Force a reload from the EEPROM if necessary */ + if (hw->mac_type < e1000_82540) { + /* Wait for reset to complete */ + udelay(10); + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + /* Wait for EEPROM reload */ + mdelay(2); + } else { + /* Wait for EEPROM reload (it happens automatically) */ + mdelay(4); + /* Dissable HW ARPs on ASF enabled adapters */ + manc = E1000_READ_REG(hw, MANC); + manc &= ~(E1000_MANC_ARP_EN); + E1000_WRITE_REG(hw, MANC, manc); + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, IMC, 0xffffffff); + + /* Clear any pending interrupt events. */ + E1000_READ_REG(hw, ICR); + + /* If MWI was previously enabled, reenable it. */ + if (hw->mac_type == e1000_82542_rev2_0) { + pci_write_config_word(hw->pdev, PCI_COMMAND, hw->pci_cmd_word); + } + E1000_WRITE_REG(hw, PBA, pba); +} + +/****************************************************************************** + * + * Initialize a number of hardware-dependent bits + * + * hw: Struct containing variables accessed by shared code + * + * This function contains hardware limitation workarounds for PCI-E adapters + * + *****************************************************************************/ +static void +e1000_initialize_hardware_bits(struct e1000_hw *hw) +{ + if ((hw->mac_type >= e1000_82571) && + (!hw->initialize_hw_bits_disable)) { + /* Settings common to all PCI-express silicon */ + uint32_t reg_ctrl, reg_ctrl_ext; + uint32_t reg_tarc0, reg_tarc1; + uint32_t reg_tctl; + uint32_t reg_txdctl, reg_txdctl1; + + /* link autonegotiation/sync workarounds */ + reg_tarc0 = E1000_READ_REG(hw, TARC0); + reg_tarc0 &= ~((1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)); + + /* Enable not-done TX descriptor counting */ + reg_txdctl = E1000_READ_REG(hw, TXDCTL); + reg_txdctl |= E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, TXDCTL, reg_txdctl); + + reg_txdctl1 = E1000_READ_REG(hw, TXDCTL1); + reg_txdctl1 |= E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, TXDCTL1, reg_txdctl1); + + switch (hw->mac_type) { + case e1000_82571: + case e1000_82572: + /* Clear PHY TX compatible mode bits */ + reg_tarc1 = E1000_READ_REG(hw, TARC1); + reg_tarc1 &= ~((1 << 30)|(1 << 29)); + + /* link autonegotiation/sync workarounds */ + reg_tarc0 |= ((1 << 26)|(1 << 25)|(1 << 24)|(1 << 23)); + + /* TX ring control fixes */ + reg_tarc1 |= ((1 << 26)|(1 << 25)|(1 << 24)); + + /* Multiple read bit is reversed polarity */ + reg_tctl = E1000_READ_REG(hw, TCTL); + if (reg_tctl & E1000_TCTL_MULR) + reg_tarc1 &= ~(1 << 28); + else + reg_tarc1 |= (1 << 28); + + E1000_WRITE_REG(hw, TARC1, reg_tarc1); + break; + case e1000_82573: + case e1000_82574: + reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + reg_ctrl_ext &= ~(1 << 23); + reg_ctrl_ext |= (1 << 22); + + /* TX byte count fix */ + reg_ctrl = E1000_READ_REG(hw, CTRL); + reg_ctrl &= ~(1 << 29); + + E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext); + E1000_WRITE_REG(hw, CTRL, reg_ctrl); + break; + case e1000_80003es2lan: + /* improve small packet performace for fiber/serdes */ + if ((hw->media_type == e1000_media_type_fiber) + || (hw->media_type == + e1000_media_type_internal_serdes)) { + reg_tarc0 &= ~(1 << 20); + } + + /* Multiple read bit is reversed polarity */ + reg_tctl = E1000_READ_REG(hw, TCTL); + reg_tarc1 = E1000_READ_REG(hw, TARC1); + if (reg_tctl & E1000_TCTL_MULR) + reg_tarc1 &= ~(1 << 28); + else + reg_tarc1 |= (1 << 28); + + E1000_WRITE_REG(hw, TARC1, reg_tarc1); + break; + case e1000_ich8lan: + /* Reduce concurrent DMA requests to 3 from 4 */ + if ((hw->revision_id < 3) || + ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) && + (hw->device_id != E1000_DEV_ID_ICH8_IGP_M))) + reg_tarc0 |= ((1 << 29)|(1 << 28)); + + reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + reg_ctrl_ext |= (1 << 22); + E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext); + + /* workaround TX hang with TSO=on */ + reg_tarc0 |= ((1 << 27)|(1 << 26)|(1 << 24)|(1 << 23)); + + /* Multiple read bit is reversed polarity */ + reg_tctl = E1000_READ_REG(hw, TCTL); + reg_tarc1 = E1000_READ_REG(hw, TARC1); + if (reg_tctl & E1000_TCTL_MULR) + reg_tarc1 &= ~(1 << 28); + else + reg_tarc1 |= (1 << 28); + + /* workaround TX hang with TSO=on */ + reg_tarc1 |= ((1 << 30)|(1 << 26)|(1 << 24)); + + E1000_WRITE_REG(hw, TARC1, reg_tarc1); + break; + default: + break; + } + + E1000_WRITE_REG(hw, TARC0, reg_tarc0); + } +} + +/****************************************************************************** + * Performs basic configuration of the adapter. + * + * hw - Struct containing variables accessed by shared code + * + * Assumes that the controller has previously been reset and is in a + * post-reset uninitialized state. Initializes the receive address registers, + * multicast table, and VLAN filter table. Calls routines to setup link + * configuration and flow control settings. Clears all on-chip counters. Leaves + * the transmit and receive units disabled and uninitialized. + *****************************************************************************/ +static int +e1000_init_hw(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint32_t ctrl; + uint32_t i; + int32_t ret_val; + uint16_t pcix_cmd_word; + uint16_t pcix_stat_hi_word; + uint16_t cmd_mmrbc; + uint16_t stat_mmrbc; + uint32_t mta_size; + uint32_t reg_data; + uint32_t ctrl_ext; + DEBUGFUNC(); + /* force full DMA clock frequency for 10/100 on ICH8 A0-B0 */ + if ((hw->mac_type == e1000_ich8lan) && + ((hw->revision_id < 3) || + ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) && + (hw->device_id != E1000_DEV_ID_ICH8_IGP_M)))) { + reg_data = E1000_READ_REG(hw, STATUS); + reg_data &= ~0x80000000; + E1000_WRITE_REG(hw, STATUS, reg_data); + } + /* Do not need initialize Identification LED */ + + /* Set the media type and TBI compatibility */ + e1000_set_media_type(hw); + + /* Must be called after e1000_set_media_type + * because media_type is used */ + e1000_initialize_hardware_bits(hw); + + /* Disabling VLAN filtering. */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + /* VET hardcoded to standard value and VFTA removed in ICH8 LAN */ + if (hw->mac_type != e1000_ich8lan) { + if (hw->mac_type < e1000_82545_rev_3) + E1000_WRITE_REG(hw, VET, 0); + e1000_clear_vfta(hw); + } + + /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */ + if (hw->mac_type == e1000_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + pci_write_config_word(hw->pdev, PCI_COMMAND, + hw-> + pci_cmd_word & ~PCI_COMMAND_INVALIDATE); + E1000_WRITE_REG(hw, RCTL, E1000_RCTL_RST); + E1000_WRITE_FLUSH(hw); + mdelay(5); + } + + /* Setup the receive address. This involves initializing all of the Receive + * Address Registers (RARs 0 - 15). + */ + e1000_init_rx_addrs(nic); + + /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */ + if (hw->mac_type == e1000_82542_rev2_0) { + E1000_WRITE_REG(hw, RCTL, 0); + E1000_WRITE_FLUSH(hw); + mdelay(1); + pci_write_config_word(hw->pdev, PCI_COMMAND, hw->pci_cmd_word); + } + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + mta_size = E1000_MC_TBL_SIZE; + if (hw->mac_type == e1000_ich8lan) + mta_size = E1000_MC_TBL_SIZE_ICH8LAN; + for (i = 0; i < mta_size; i++) { + E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); + /* use write flush to prevent Memory Write Block (MWB) from + * occuring when accessing our register space */ + E1000_WRITE_FLUSH(hw); + } +#if 0 + /* Set the PCI priority bit correctly in the CTRL register. This + * determines if the adapter gives priority to receives, or if it + * gives equal priority to transmits and receives. Valid only on + * 82542 and 82543 silicon. + */ + if (hw->dma_fairness && hw->mac_type <= e1000_82543) { + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR); + } +#endif + switch (hw->mac_type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + break; + default: + /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */ + if (hw->bus_type == e1000_bus_type_pcix) { + pci_read_config_word(hw->pdev, PCIX_COMMAND_REGISTER, + &pcix_cmd_word); + pci_read_config_word(hw->pdev, PCIX_STATUS_REGISTER_HI, + &pcix_stat_hi_word); + cmd_mmrbc = + (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >> + PCIX_COMMAND_MMRBC_SHIFT; + stat_mmrbc = + (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >> + PCIX_STATUS_HI_MMRBC_SHIFT; + if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K) + stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K; + if (cmd_mmrbc > stat_mmrbc) { + pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK; + pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT; + pci_write_config_word(hw->pdev, PCIX_COMMAND_REGISTER, + pcix_cmd_word); + } + } + break; + } + + /* More time needed for PHY to initialize */ + if (hw->mac_type == e1000_ich8lan) + mdelay(15); + + /* Call a subroutine to configure the link and setup flow control. */ + ret_val = e1000_setup_link(nic); + + /* Set the transmit descriptor write-back policy */ + if (hw->mac_type > e1000_82544) { + ctrl = E1000_READ_REG(hw, TXDCTL); + ctrl = + (ctrl & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB; + E1000_WRITE_REG(hw, TXDCTL, ctrl); + } + + /* Set the receive descriptor write back policy */ + + if (hw->mac_type >= e1000_82571) { + ctrl = E1000_READ_REG(hw, RXDCTL); + ctrl = + (ctrl & ~E1000_RXDCTL_WTHRESH) | + E1000_RXDCTL_FULL_RX_DESC_WB; + E1000_WRITE_REG(hw, RXDCTL, ctrl); + } + + switch (hw->mac_type) { + default: + break; + case e1000_80003es2lan: + /* Enable retransmit on late collisions */ + reg_data = E1000_READ_REG(hw, TCTL); + reg_data |= E1000_TCTL_RTLC; + E1000_WRITE_REG(hw, TCTL, reg_data); + + /* Configure Gigabit Carry Extend Padding */ + reg_data = E1000_READ_REG(hw, TCTL_EXT); + reg_data &= ~E1000_TCTL_EXT_GCEX_MASK; + reg_data |= DEFAULT_80003ES2LAN_TCTL_EXT_GCEX; + E1000_WRITE_REG(hw, TCTL_EXT, reg_data); + + /* Configure Transmit Inter-Packet Gap */ + reg_data = E1000_READ_REG(hw, TIPG); + reg_data &= ~E1000_TIPG_IPGT_MASK; + reg_data |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; + E1000_WRITE_REG(hw, TIPG, reg_data); + + reg_data = E1000_READ_REG_ARRAY(hw, FFLT, 0x0001); + reg_data &= ~0x00100000; + E1000_WRITE_REG_ARRAY(hw, FFLT, 0x0001, reg_data); + /* Fall through */ + case e1000_82571: + case e1000_82572: + case e1000_ich8lan: + ctrl = E1000_READ_REG(hw, TXDCTL1); + ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) + | E1000_TXDCTL_FULL_TX_DESC_WB; + E1000_WRITE_REG(hw, TXDCTL1, ctrl); + break; + case e1000_82573: + case e1000_82574: + reg_data = E1000_READ_REG(hw, GCR); + reg_data |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX; + E1000_WRITE_REG(hw, GCR, reg_data); + } + +#if 0 + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs(hw); + + /* ICH8 No-snoop bits are opposite polarity. + * Set to snoop by default after reset. */ + if (hw->mac_type == e1000_ich8lan) + e1000_set_pci_ex_no_snoop(hw, PCI_EX_82566_SNOOP_ALL); +#endif + + if (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER || + hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3) { + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + /* Relaxed ordering must be disabled to avoid a parity + * error crash in a PCI slot. */ + ctrl_ext |= E1000_CTRL_EXT_RO_DIS; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + } + + return ret_val; +} + +/****************************************************************************** + * Configures flow control and link settings. + * + * hw - Struct containing variables accessed by shared code + * + * Determines which flow control settings to use. Calls the apropriate media- + * specific link configuration function. Configures the flow control settings. + * Assuming the adapter has a valid link partner, a valid link should be + * established. Assumes the hardware has previously been reset and the + * transmitter and receiver are not enabled. + *****************************************************************************/ +static int +e1000_setup_link(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + int32_t ret_val; +#ifndef CONFIG_E1000_NO_NVM + uint32_t ctrl_ext; + uint16_t eeprom_data; +#endif + + DEBUGFUNC(); + + /* In the case of the phy reset being blocked, we already have a link. + * We do not have to set it up again. */ + if (e1000_check_phy_reset_block(hw)) + return E1000_SUCCESS; + +#ifndef CONFIG_E1000_NO_NVM + /* Read and store word 0x0F of the EEPROM. This word contains bits + * that determine the hardware's default PAUSE (flow control) mode, + * a bit that determines whether the HW defaults to enabling or + * disabling auto-negotiation, and the direction of the + * SW defined pins. If there is no SW over-ride of the flow + * control setting, then the variable hw->fc will + * be initialized based on a value in the EEPROM. + */ + if (e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, 1, + &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } +#endif + if (hw->fc == e1000_fc_default) { + switch (hw->mac_type) { + case e1000_ich8lan: + case e1000_82573: + case e1000_82574: + hw->fc = e1000_fc_full; + break; + default: +#ifndef CONFIG_E1000_NO_NVM + ret_val = e1000_read_eeprom(hw, + EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data); + if (ret_val) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0) + hw->fc = e1000_fc_none; + else if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == + EEPROM_WORD0F_ASM_DIR) + hw->fc = e1000_fc_tx_pause; + else +#endif + hw->fc = e1000_fc_full; + break; + } + } + + /* We want to save off the original Flow Control configuration just + * in case we get disconnected and then reconnected into a different + * hub or switch with different Flow Control capabilities. + */ + if (hw->mac_type == e1000_82542_rev2_0) + hw->fc &= (~e1000_fc_tx_pause); + + if ((hw->mac_type < e1000_82543) && (hw->report_tx_early == 1)) + hw->fc &= (~e1000_fc_rx_pause); + + hw->original_fc = hw->fc; + + DEBUGOUT("After fix-ups FlowControl is now = %x\n", hw->fc); + +#ifndef CONFIG_E1000_NO_NVM + /* Take the 4 bits from EEPROM word 0x0F that determine the initial + * polarity value for the SW controlled pins, and setup the + * Extended Device Control reg with that info. + * This is needed because one of the SW controlled pins is used for + * signal detection. So this should be done before e1000_setup_pcs_link() + * or e1000_phy_setup() is called. + */ + if (hw->mac_type == e1000_82543) { + ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) << + SWDPIO__EXT_SHIFT); + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + } +#endif + + /* Call the necessary subroutine to configure the link. */ + ret_val = (hw->media_type == e1000_media_type_fiber) ? + e1000_setup_fiber_link(nic) : e1000_setup_copper_link(nic); + if (ret_val < 0) { + return ret_val; + } + + /* Initialize the flow control address, type, and PAUSE timer + * registers to their default values. This is done even if flow + * control is disabled, because it does not hurt anything to + * initialize these registers. + */ + DEBUGOUT("Initializing the Flow Control address, type" + "and timer regs\n"); + + /* FCAL/H and FCT are hardcoded to standard values in e1000_ich8lan. */ + if (hw->mac_type != e1000_ich8lan) { + E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE); + E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, FCAL, FLOW_CONTROL_ADDRESS_LOW); + } + + E1000_WRITE_REG(hw, FCTTV, hw->fc_pause_time); + + /* Set the flow control receive threshold registers. Normally, + * these registers will be set to a default threshold that may be + * adjusted later by the driver's runtime code. However, if the + * ability to transmit pause frames in not enabled, then these + * registers will be set to 0. + */ + if (!(hw->fc & e1000_fc_tx_pause)) { + E1000_WRITE_REG(hw, FCRTL, 0); + E1000_WRITE_REG(hw, FCRTH, 0); + } else { + /* We need to set up the Receive Threshold high and low water marks + * as well as (optionally) enabling the transmission of XON frames. + */ + if (hw->fc_send_xon) { + E1000_WRITE_REG(hw, FCRTL, + (hw->fc_low_water | E1000_FCRTL_XONE)); + E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + } else { + E1000_WRITE_REG(hw, FCRTL, hw->fc_low_water); + E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + } + } + return ret_val; +} + +/****************************************************************************** + * Sets up link for a fiber based adapter + * + * hw - Struct containing variables accessed by shared code + * + * Manipulates Physical Coding Sublayer functions in order to configure + * link. Assumes the hardware has been previously reset and the transmitter + * and receiver are not enabled. + *****************************************************************************/ +static int +e1000_setup_fiber_link(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint32_t ctrl; + uint32_t status; + uint32_t txcw = 0; + uint32_t i; + uint32_t signal; + int32_t ret_val; + + DEBUGFUNC(); + /* On adapters with a MAC newer that 82544, SW Defineable pin 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal + */ + ctrl = E1000_READ_REG(hw, CTRL); + if ((hw->mac_type > e1000_82544) && !(ctrl & E1000_CTRL_ILOS)) + signal = E1000_CTRL_SWDPIN1; + else + signal = 0; + + printf("signal for %s is %x (ctrl %08x)!!!!\n", nic->name, signal, + ctrl); + /* Take the link out of reset */ + ctrl &= ~(E1000_CTRL_LRST); + + e1000_config_collision_dist(hw); + + /* Check for a software override of the flow control settings, and setup + * the device accordingly. If auto-negotiation is enabled, then software + * will have to set the "PAUSE" bits to the correct value in the Tranmsit + * Config Word Register (TXCW) and re-start auto-negotiation. However, if + * auto-negotiation is disabled, then software will have to manually + * configure the two flow control enable bits in the CTRL register. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, but + * not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but we do + * not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + */ + switch (hw->fc) { + case e1000_fc_none: + /* Flow control is completely disabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case e1000_fc_rx_pause: + /* RX Flow control is enabled and TX Flow control is disabled by a + * software over-ride. Since there really isn't a way to advertise + * that we are capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + case e1000_fc_tx_pause: + /* TX Flow control is enabled, and RX Flow control is disabled, by a + * software over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); + break; + case e1000_fc_full: + /* Flow control (both RX and TX) is enabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + break; + } + + /* Since auto-negotiation is enabled, take the link out of reset (the link + * will be in reset, because we previously reset the chip). This will + * restart auto-negotiation. If auto-neogtiation is successful then the + * link-up status bit will be set and the flow control enable bits (RFCE + * and TFCE) will be set according to their negotiated value. + */ + DEBUGOUT("Auto-negotiation enabled (%#x)\n", txcw); + + E1000_WRITE_REG(hw, TXCW, txcw); + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + hw->txcw = txcw; + mdelay(1); + + /* If we have a signal (the cable is plugged in) then poll for a "Link-Up" + * indication in the Device Status Register. Time-out if a link isn't + * seen in 500 milliseconds seconds (Auto-negotiation should complete in + * less than 500 milliseconds even if the other end is doing it in SW). + */ + if ((E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) { + DEBUGOUT("Looking for Link\n"); + for (i = 0; i < (LINK_UP_TIMEOUT / 10); i++) { + mdelay(10); + status = E1000_READ_REG(hw, STATUS); + if (status & E1000_STATUS_LU) + break; + } + if (i == (LINK_UP_TIMEOUT / 10)) { + /* AutoNeg failed to achieve a link, so we'll call + * e1000_check_for_link. This routine will force the link up if we + * detect a signal. This will allow us to communicate with + * non-autonegotiating link partners. + */ + DEBUGOUT("Never got a valid link from auto-neg!!!\n"); + hw->autoneg_failed = 1; + ret_val = e1000_check_for_link(nic); + if (ret_val < 0) { + DEBUGOUT("Error while checking for link\n"); + return ret_val; + } + hw->autoneg_failed = 0; + } else { + hw->autoneg_failed = 0; + DEBUGOUT("Valid Link Found\n"); + } + } else { + DEBUGOUT("No Signal Detected\n"); + return -E1000_ERR_NOLINK; + } + return 0; +} + +/****************************************************************************** +* Make sure we have a valid PHY and change PHY mode before link setup. +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_copper_link_preconfig(struct e1000_hw *hw) +{ + uint32_t ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + ctrl = E1000_READ_REG(hw, CTRL); + /* With 82543, we need to force speed and duplex on the MAC equal to what + * the PHY speed and duplex configuration is. In addition, we need to + * perform a hardware reset on the PHY to take it out of reset. + */ + if (hw->mac_type > e1000_82543) { + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, CTRL, ctrl); + } else { + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX + | E1000_CTRL_SLU); + E1000_WRITE_REG(hw, CTRL, ctrl); + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) + return ret_val; + } + + /* Make sure we have a valid PHY */ + ret_val = e1000_detect_gig_phy(hw); + if (ret_val) { + DEBUGOUT("Error, did not detect valid phy.\n"); + return ret_val; + } + DEBUGOUT("Phy ID = %x \n", hw->phy_id); + + /* Set PHY to class A mode (if necessary) */ + ret_val = e1000_set_phy_mode(hw); + if (ret_val) + return ret_val; + if ((hw->mac_type == e1000_82545_rev_3) || + (hw->mac_type == e1000_82546_rev_3)) { + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, + &phy_data); + phy_data |= 0x00000008; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, + phy_data); + } + + if (hw->mac_type <= e1000_82543 || + hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 || + hw->mac_type == e1000_82541_rev_2 + || hw->mac_type == e1000_82547_rev_2) + hw->phy_reset_disable = false; + + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * This function sets the lplu state according to the active flag. When + * activating lplu this function also disables smart speed and vise versa. + * lplu will not be activated unless the device autonegotiation advertisment + * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. + * hw: Struct containing variables accessed by shared code + * active - true to enable lplu false to disable lplu. + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +static int32_t +e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) +{ + uint32_t phy_ctrl = 0; + int32_t ret_val; + uint16_t phy_data; + DEBUGFUNC(); + + if (hw->phy_type != e1000_phy_igp && hw->phy_type != e1000_phy_igp_2 + && hw->phy_type != e1000_phy_igp_3) + return E1000_SUCCESS; + + /* During driver activity LPLU should not be used or it will attain link + * from the lowest speeds starting from 10Mbps. The capability is used + * for Dx transitions and states */ + if (hw->mac_type == e1000_82541_rev_2 + || hw->mac_type == e1000_82547_rev_2) { + ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, + &phy_data); + if (ret_val) + return ret_val; + } else if (hw->mac_type == e1000_ich8lan) { + /* MAC writes into PHY register based on the state transition + * and start auto-negotiation. SW driver can overwrite the + * settings in CSR PHY power control E1000_PHY_CTRL register. */ + phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + } else { + ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, + &phy_data); + if (ret_val) + return ret_val; + } + + if (!active) { + if (hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) { + phy_data &= ~IGP01E1000_GMII_FLEX_SPD; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + phy_data); + if (ret_val) + return ret_val; + } else { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data &= ~IGP02E1000_PM_D3_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + } + + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during + * Dx states where the power conservation is most important. During + * driver activity we should enable SmartSpeed, so performance is + * maintained. */ + if (hw->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + } else if (hw->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + } + + } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) + || (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL) || + (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) { + + if (hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) { + phy_data |= IGP01E1000_GMII_FLEX_SPD; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_GMII_FIFO, phy_data); + if (ret_val) + return ret_val; + } else { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data |= IGP02E1000_PM_D3_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + } + + /* When LPLU is enabled we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + } + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * This function sets the lplu d0 state according to the active flag. When + * activating lplu this function also disables smart speed and vise versa. + * lplu will not be activated unless the device autonegotiation advertisment + * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. + * hw: Struct containing variables accessed by shared code + * active - true to enable lplu false to disable lplu. + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +static int32_t +e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) +{ + uint32_t phy_ctrl = 0; + int32_t ret_val; + uint16_t phy_data; + DEBUGFUNC(); + + if (hw->mac_type <= e1000_82547_rev_2) + return E1000_SUCCESS; + + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + } else { + ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, + &phy_data); + if (ret_val) + return ret_val; + } + + if (!active) { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data &= ~IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during + * Dx states where the power conservation is most important. During + * driver activity we should enable SmartSpeed, so performance is + * maintained. */ + if (hw->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + } else if (hw->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + } + + + } else { + + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data |= IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + + /* When LPLU is enabled we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + + } + return E1000_SUCCESS; +} + +/******************************************************************** +* Copper link setup for e1000_phy_igp series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_igp_setup(struct e1000_hw *hw) +{ + uint32_t led_ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + if (hw->phy_reset_disable) + return E1000_SUCCESS; + + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + /* Wait 15ms for MAC to configure PHY from eeprom settings */ + mdelay(15); + if (hw->mac_type != e1000_ich8lan) { + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } + + /* The NVM settings will configure LPLU in D3 for IGP2 and IGP3 PHYs */ + if (hw->phy_type == e1000_phy_igp) { + /* disable lplu d3 during driver init */ + ret_val = e1000_set_d3_lplu_state(hw, false); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D3\n"); + return ret_val; + } + } + + /* disable lplu d0 during driver init */ + ret_val = e1000_set_d0_lplu_state(hw, false); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D0\n"); + return ret_val; + } + /* Configure mdi-mdix settings */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + hw->dsp_config_state = e1000_dsp_config_disabled; + /* Force MDI for earlier revs of the IGP PHY */ + phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX + | IGP01E1000_PSCR_FORCE_MDI_MDIX); + hw->mdix = 1; + + } else { + hw->dsp_config_state = e1000_dsp_config_enabled; + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + + switch (hw->mdix) { + case 1: + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 2: + phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 0: + default: + phy_data |= IGP01E1000_PSCR_AUTO_MDIX; + break; + } + } + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* set auto-master slave resolution settings */ + if (hw->autoneg) { + e1000_ms_type phy_ms_setting = hw->master_slave; + + if (hw->ffe_config_state == e1000_ffe_config_active) + hw->ffe_config_state = e1000_ffe_config_enabled; + + if (hw->dsp_config_state == e1000_dsp_config_activated) + hw->dsp_config_state = e1000_dsp_config_enabled; + + /* when autonegotiation advertisment is only 1000Mbps then we + * should disable SmartSpeed and enable Auto MasterSlave + * resolution as hardware default. */ + if (hw->autoneg_advertised == ADVERTISE_1000_FULL) { + /* Disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + /* Set auto Master/Slave resolution process */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, + &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~CR_1000T_MS_ENABLE; + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, + phy_data); + if (ret_val) + return ret_val; + } + + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* load defaults for future use */ + hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ? + ((phy_data & CR_1000T_MS_VALUE) ? + e1000_ms_force_master : + e1000_ms_force_slave) : + e1000_ms_auto; + + switch (phy_ms_setting) { + case e1000_ms_force_master: + phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + break; + case e1000_ms_force_slave: + phy_data |= CR_1000T_MS_ENABLE; + phy_data &= ~(CR_1000T_MS_VALUE); + break; + case e1000_ms_auto: + phy_data &= ~CR_1000T_MS_ENABLE; + default: + break; + } + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/***************************************************************************** + * This function checks the mode of the firmware. + * + * returns - true when the mode is IAMT or false. + ****************************************************************************/ +bool +e1000_check_mng_mode(struct e1000_hw *hw) +{ + uint32_t fwsm; + DEBUGFUNC(); + + fwsm = E1000_READ_REG(hw, FWSM); + + if (hw->mac_type == e1000_ich8lan) { + if ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_ICH_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) + return true; + } else if ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) + return true; + + return false; +} + +static int32_t +e1000_write_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data) +{ + uint16_t swfw = E1000_SWFW_PHY0_SM; + uint32_t reg_val; + DEBUGFUNC(); + + if (e1000_is_second_port(hw)) + swfw = E1000_SWFW_PHY1_SM; + + if (e1000_swfw_sync_acquire(hw, swfw)) + return -E1000_ERR_SWFW_SYNC; + + reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) + & E1000_KUMCTRLSTA_OFFSET) | data; + E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val); + udelay(2); + + return E1000_SUCCESS; +} + +static int32_t +e1000_read_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *data) +{ + uint16_t swfw = E1000_SWFW_PHY0_SM; + uint32_t reg_val; + DEBUGFUNC(); + + if (e1000_is_second_port(hw)) + swfw = E1000_SWFW_PHY1_SM; + + if (e1000_swfw_sync_acquire(hw, swfw)) + return -E1000_ERR_SWFW_SYNC; + + /* Write register address */ + reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) & + E1000_KUMCTRLSTA_OFFSET) | E1000_KUMCTRLSTA_REN; + E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val); + udelay(2); + + /* Read the data returned */ + reg_val = E1000_READ_REG(hw, KUMCTRLSTA); + *data = (uint16_t)reg_val; + + return E1000_SUCCESS; +} + +/******************************************************************** +* Copper link setup for e1000_phy_gg82563 series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_ggp_setup(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + uint32_t reg_data; + + DEBUGFUNC(); + + if (!hw->phy_reset_disable) { + /* Enable CRS on TX for half-duplex operation. */ + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_MAC_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; + /* Use 25MHz for both link down and 1000BASE-T for Tx clock */ + phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ; + + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_MAC_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK; + + switch (hw->mdix) { + case 1: + phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDI; + break; + case 2: + phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDIX; + break; + case 0: + default: + phy_data |= GG82563_PSCR_CROSSOVER_MODE_AUTO; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_SPEC_CTRL, phy_data); + + if (ret_val) + return ret_val; + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + } /* phy_reset_disable */ + + if (hw->mac_type == e1000_80003es2lan) { + /* Bypass RX and TX FIFO's */ + ret_val = e1000_write_kmrn_reg(hw, + E1000_KUMCTRLSTA_OFFSET_FIFO_CTRL, + E1000_KUMCTRLSTA_FIFO_CTRL_RX_BYPASS + | E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_SPEC_CTRL_2, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_SPEC_CTRL_2, phy_data); + + if (ret_val) + return ret_val; + + reg_data = E1000_READ_REG(hw, CTRL_EXT); + reg_data &= ~(E1000_CTRL_EXT_LINK_MODE_MASK); + E1000_WRITE_REG(hw, CTRL_EXT, reg_data); + + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_PWR_MGMT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* Do not init these registers when the HW is in IAMT mode, since the + * firmware will have already initialized them. We only initialize + * them if the HW is not in IAMT mode. + */ + if (e1000_check_mng_mode(hw) == false) { + /* Enable Electrical Idle on the PHY */ + phy_data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_PWR_MGMT_CTRL, phy_data); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_KMRN_MODE_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_KMRN_MODE_CTRL, phy_data); + + if (ret_val) + return ret_val; + } + + /* Workaround: Disable padding in Kumeran interface in the MAC + * and in the PHY to avoid CRC errors. + */ + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_INBAND_CTRL, &phy_data); + if (ret_val) + return ret_val; + phy_data |= GG82563_ICR_DIS_PADDING; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_INBAND_CTRL, phy_data); + if (ret_val) + return ret_val; + } + return E1000_SUCCESS; +} + +/******************************************************************** +* Copper link setup for e1000_phy_m88 series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_mgp_setup(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + if (hw->phy_reset_disable) + return E1000_SUCCESS; + + /* Enable CRS on TX. This must be set for half-duplex operation. */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (hw->mdix) { + case 1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + if (hw->phy_revision < M88E1011_I_REV_4) { + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = e1000_read_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + if ((hw->phy_revision == E1000_REVISION_2) && + (hw->phy_id == M88E1111_I_PHY_ID)) { + /* Vidalia Phy, set the downshift counter to 5x */ + phy_data &= ~(M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK); + phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X; + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + } else { + /* Configure Master and Slave downshift values */ + phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK + | M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); + phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X + | M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + } + } + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + return E1000_SUCCESS; +} + +/******************************************************************** +* Setup auto-negotiation and flow control advertisements, +* and then perform auto-negotiation. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_autoneg(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + /* Perform some bounds checking on the hw->autoneg_advertised + * parameter. If this variable is zero, then set it to the default. + */ + hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if (hw->autoneg_advertised == 0) + hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* IFE phy only supports 10/100 */ + if (hw->phy_type == e1000_phy_ife) + hw->autoneg_advertised &= AUTONEG_ADVERTISE_10_100_ALL; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + ret_val = e1000_phy_setup_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error Setting up Auto-Negotiation\n"); + return ret_val; + } + DEBUGOUT("Restarting Auto-Neg\n"); + + /* Restart auto-negotiation by setting the Auto Neg Enable bit and + * the Auto Neg Restart bit in the PHY control register. + */ + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + /* If we do not wait for autonegtation to complete I + * do not see a valid link status. + * wait_autoneg_complete = 1 . + */ + if (hw->wait_autoneg_complete) { + ret_val = e1000_wait_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error while waiting for autoneg" + "to complete\n"); + return ret_val; + } + } + + hw->get_link_status = true; + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Config the MAC and the PHY after link is up. +* 1) Set up the MAC to the current PHY speed/duplex +* if we are on 82543. If we +* are on newer silicon, we only need to configure +* collision distance in the Transmit Control Register. +* 2) Set up flow control on the MAC to that established with +* the link partner. +* 3) Config DSP to improve Gigabit link quality for some PHY revisions. +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_copper_link_postconfig(struct e1000_hw *hw) +{ + int32_t ret_val; + DEBUGFUNC(); + + if (hw->mac_type >= e1000_82544) { + e1000_config_collision_dist(hw); + } else { + ret_val = e1000_config_mac_to_phy(hw); + if (ret_val) { + DEBUGOUT("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val) { + DEBUGOUT("Error Configuring Flow Control\n"); + return ret_val; + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Detects which PHY is present and setup the speed and duplex +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int +e1000_setup_copper_link(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + int32_t ret_val; + uint16_t i; + uint16_t phy_data; + uint16_t reg_data; + + DEBUGFUNC(); + + switch (hw->mac_type) { + case e1000_80003es2lan: + case e1000_ich8lan: + /* Set the mac to wait the maximum time between each + * iteration and increase the max iterations when + * polling the phy; this fixes erroneous timeouts at 10Mbps. */ + ret_val = e1000_write_kmrn_reg(hw, + GG82563_REG(0x34, 4), 0xFFFF); + if (ret_val) + return ret_val; + ret_val = e1000_read_kmrn_reg(hw, + GG82563_REG(0x34, 9), ®_data); + if (ret_val) + return ret_val; + reg_data |= 0x3F; + ret_val = e1000_write_kmrn_reg(hw, + GG82563_REG(0x34, 9), reg_data); + if (ret_val) + return ret_val; + default: + break; + } + + /* Check if it is a valid PHY and set PHY mode if necessary. */ + ret_val = e1000_copper_link_preconfig(hw); + if (ret_val) + return ret_val; + switch (hw->mac_type) { + case e1000_80003es2lan: + /* Kumeran registers are written-only */ + reg_data = + E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT; + reg_data |= E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING; + ret_val = e1000_write_kmrn_reg(hw, + E1000_KUMCTRLSTA_OFFSET_INB_CTRL, reg_data); + if (ret_val) + return ret_val; + break; + default: + break; + } + + if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || + hw->phy_type == e1000_phy_igp_2) { + ret_val = e1000_copper_link_igp_setup(hw); + if (ret_val) + return ret_val; + } else if (hw->phy_type == e1000_phy_m88) { + ret_val = e1000_copper_link_mgp_setup(hw); + if (ret_val) + return ret_val; + } else if (hw->phy_type == e1000_phy_gg82563) { + ret_val = e1000_copper_link_ggp_setup(hw); + if (ret_val) + return ret_val; + } + + /* always auto */ + /* Setup autoneg and flow control advertisement + * and perform autonegotiation */ + ret_val = e1000_copper_link_autoneg(hw); + if (ret_val) + return ret_val; + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + for (i = 0; i < 10; i++) { + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if (phy_data & MII_SR_LINK_STATUS) { + /* Config the MAC and PHY after link is up */ + ret_val = e1000_copper_link_postconfig(hw); + if (ret_val) + return ret_val; + + DEBUGOUT("Valid link established!!!\n"); + return E1000_SUCCESS; + } + udelay(10); + } + + DEBUGOUT("Unable to establish link!!!\n"); + return E1000_SUCCESS; +} + +/****************************************************************************** +* Configures PHY autoneg and flow control advertisement settings +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +e1000_phy_setup_autoneg(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t mii_autoneg_adv_reg; + uint16_t mii_1000t_ctrl_reg; + + DEBUGFUNC(); + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + if (hw->phy_type != e1000_phy_ife) { + /* Read the MII 1000Base-T Control Register (Address 9). */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, + &mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } else + mii_1000t_ctrl_reg = 0; + + /* Need to parse both autoneg_advertised and fc and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ + + /* First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). + */ + mii_autoneg_adv_reg &= ~REG4_SPEED_MASK; + mii_1000t_ctrl_reg &= ~REG9_SPEED_MASK; + + DEBUGOUT("autoneg_advertised %x\n", hw->autoneg_advertised); + + /* Do we want to advertise 10 Mb Half Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_10_HALF) { + DEBUGOUT("Advertise 10mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; + } + + /* Do we want to advertise 10 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_10_FULL) { + DEBUGOUT("Advertise 10mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; + } + + /* Do we want to advertise 100 Mb Half Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_100_HALF) { + DEBUGOUT("Advertise 100mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; + } + + /* Do we want to advertise 100 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_100_FULL) { + DEBUGOUT("Advertise 100mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; + } + + /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ + if (hw->autoneg_advertised & ADVERTISE_1000_HALF) { + DEBUGOUT + ("Advertise 1000mb Half duplex requested, request denied!\n"); + } + + /* Do we want to advertise 1000 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_1000_FULL) { + DEBUGOUT("Advertise 1000mb Full duplex\n"); + mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; + } + + /* Check for a software override of the flow control settings, and + * setup the PHY advertisement registers accordingly. If + * auto-negotiation is enabled, then software will have to set the + * "PAUSE" bits to the correct value in the Auto-Negotiation + * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-negotiation. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * but we do not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + * other: No software override. The flow control configuration + * in the EEPROM is used. + */ + switch (hw->fc) { + case e1000_fc_none: /* 0 */ + /* Flow control (RX & TX) is completely disabled by a + * software over-ride. + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_rx_pause: /* 1 */ + /* RX Flow control is enabled, and TX Flow control is + * disabled, by a software over-ride. + */ + /* Since there really isn't a way to advertise that we are + * capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later + * (in e1000_config_fc_after_link_up) we will disable the + *hw's ability to send PAUSE frames. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_tx_pause: /* 2 */ + /* TX Flow control is enabled, and RX Flow control is + * disabled, by a software over-ride. + */ + mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; + mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; + break; + case e1000_fc_full: /* 3 */ + /* Flow control (both RX and TX) is enabled by a software + * over-ride. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + DEBUGOUT("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + + if (hw->phy_type != e1000_phy_ife) { + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, + mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Sets the collision distance in the Transmit Control register +* +* hw - Struct containing variables accessed by shared code +* +* Link should have been established previously. Reads the speed and duplex +* information from the Device Status register. +******************************************************************************/ +static void +e1000_config_collision_dist(struct e1000_hw *hw) +{ + uint32_t tctl, coll_dist; + + DEBUGFUNC(); + + if (hw->mac_type < e1000_82543) + coll_dist = E1000_COLLISION_DISTANCE_82542; + else + coll_dist = E1000_COLLISION_DISTANCE; + + tctl = E1000_READ_REG(hw, TCTL); + + tctl &= ~E1000_TCTL_COLD; + tctl |= coll_dist << E1000_COLD_SHIFT; + + E1000_WRITE_REG(hw, TCTL, tctl); + E1000_WRITE_FLUSH(hw); +} + +/****************************************************************************** +* Sets MAC speed and duplex settings to reflect the those in the PHY +* +* hw - Struct containing variables accessed by shared code +* mii_reg - data to write to the MII control register +* +* The contents of the PHY register containing the needed information need to +* be passed in. +******************************************************************************/ +static int +e1000_config_mac_to_phy(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint16_t phy_data; + + DEBUGFUNC(); + + /* Read the Device Control Register and set the bits to Force Speed + * and Duplex. + */ + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS); + + /* Set up duplex in the Device Control and Transmit Control + * registers depending on negotiated values. + */ + if (e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (phy_data & M88E1000_PSSR_DPLX) + ctrl |= E1000_CTRL_FD; + else + ctrl &= ~E1000_CTRL_FD; + + e1000_config_collision_dist(hw); + + /* Set up speed in the Device Control register depending on + * negotiated values. + */ + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) + ctrl |= E1000_CTRL_SPD_1000; + else if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS) + ctrl |= E1000_CTRL_SPD_100; + /* Write the configured values back to the Device Control Reg. */ + E1000_WRITE_REG(hw, CTRL, ctrl); + return 0; +} + +/****************************************************************************** + * Forces the MAC's flow control settings. + * + * hw - Struct containing variables accessed by shared code + * + * Sets the TFCE and RFCE bits in the device control register to reflect + * the adapter settings. TFCE and RFCE need to be explicitly set by + * software when a Copper PHY is used because autonegotiation is managed + * by the PHY rather than the MAC. Software must also configure these + * bits when link is forced on a fiber connection. + *****************************************************************************/ +static int +e1000_force_mac_fc(struct e1000_hw *hw) +{ + uint32_t ctrl; + + DEBUGFUNC(); + + /* Get the current configuration of the Device Control Register */ + ctrl = E1000_READ_REG(hw, CTRL); + + /* Because we didn't get link via the internal auto-negotiation + * mechanism (we either forced link or we got link via PHY + * auto-neg), we have to manually enable/disable transmit an + * receive flow control. + * + * The "Case" statement below enables/disable flow control + * according to the "hw->fc" parameter. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause + * frames but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * frames but we do not receive pause frames). + * 3: Both Rx and TX flow control (symmetric) is enabled. + * other: No other values should be possible at this point. + */ + + switch (hw->fc) { + case e1000_fc_none: + ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); + break; + case e1000_fc_rx_pause: + ctrl &= (~E1000_CTRL_TFCE); + ctrl |= E1000_CTRL_RFCE; + break; + case e1000_fc_tx_pause: + ctrl &= (~E1000_CTRL_RFCE); + ctrl |= E1000_CTRL_TFCE; + break; + case e1000_fc_full: + ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + /* Disable TX Flow Control for 82542 (rev 2.0) */ + if (hw->mac_type == e1000_82542_rev2_0) + ctrl &= (~E1000_CTRL_TFCE); + + E1000_WRITE_REG(hw, CTRL, ctrl); + return 0; +} + +/****************************************************************************** + * Configures flow control settings after link is established + * + * hw - Struct containing variables accessed by shared code + * + * Should be called immediately after a valid link has been established. + * Forces MAC flow control settings if link was forced. When in MII/GMII mode + * and autonegotiation is enabled, the MAC flow control settings will be set + * based on the flow control negotiated by the PHY. In TBI mode, the TFCE + * and RFCE bits will be automaticaly set to the negotiated flow control mode. + *****************************************************************************/ +static int32_t +e1000_config_fc_after_link_up(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t mii_status_reg; + uint16_t mii_nway_adv_reg; + uint16_t mii_nway_lp_ability_reg; + uint16_t speed; + uint16_t duplex; + + DEBUGFUNC(); + + /* Check for the case where we have fiber media and auto-neg failed + * so we had to force link. In this case, we need to force the + * configuration of the MAC to match the "fc" parameter. + */ + if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) + || ((hw->media_type == e1000_media_type_internal_serdes) + && (hw->autoneg_failed)) + || ((hw->media_type == e1000_media_type_copper) + && (!hw->autoneg))) { + ret_val = e1000_force_mac_fc(hw); + if (ret_val < 0) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } + + /* Check for the case where we have copper media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if (hw->media_type == e1000_media_type_copper) { + /* Read the MII Status Register and check to see if AutoNeg + * has completed. We read this twice because this reg has + * some "sticky" (latched) bits. + */ + if (e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) { + DEBUGOUT("PHY Read Error \n"); + return -E1000_ERR_PHY; + } + if (e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) { + DEBUGOUT("PHY Read Error \n"); + return -E1000_ERR_PHY; + } + + if (mii_status_reg & MII_SR_AUTONEG_COMPLETE) { + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement Register + * (Address 4) and the Auto_Negotiation Base Page Ability + * Register (Address 5) to determine how flow control was + * negotiated. + */ + if (e1000_read_phy_reg + (hw, PHY_AUTONEG_ADV, &mii_nway_adv_reg) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (e1000_read_phy_reg + (hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + + /* Two bits in the Auto Negotiation Advertisement Register + * (Address 4) and two bits in the Auto Negotiation Base + * Page Ability Register (Address 5) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | e1000_fc_none + * 0 | 1 | 0 | DC | e1000_fc_none + * 0 | 1 | 1 | 0 | e1000_fc_none + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * 1 | 0 | 0 | DC | e1000_fc_none + * 1 | DC | 1 | DC | e1000_fc_full + * 1 | 1 | 0 | 0 | e1000_fc_none + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + */ + /* Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | e1000_fc_full + * + */ + if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { + /* Now we need to check if the user selected RX ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise RX + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->original_fc == e1000_fc_full) { + hw->fc = e1000_fc_full; + DEBUGOUT("Flow Control = FULL.\r\n"); + } else { + hw->fc = e1000_fc_rx_pause; + DEBUGOUT + ("Flow Control = RX PAUSE frames only.\r\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * + */ + else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) + { + hw->fc = e1000_fc_tx_pause; + DEBUGOUT + ("Flow Control = TX PAUSE frames only.\r\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + */ + else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) + { + hw->fc = e1000_fc_rx_pause; + DEBUGOUT + ("Flow Control = RX PAUSE frames only.\r\n"); + } + /* Per the IEEE spec, at this point flow control should be + * disabled. However, we want to consider that we could + * be connected to a legacy switch that doesn't advertise + * desired flow control, but can be forced on the link + * partner. So if we advertised no flow control, that is + * what we will resolve to. If we advertised some kind of + * receive capability (Rx Pause Only or Full Flow Control) + * and the link partner advertised none, we will configure + * ourselves to enable Rx Flow Control only. We can do + * this safely for two reasons: If the link partner really + * didn't want flow control enabled, and we enable Rx, no + * harm done since we won't be receiving any PAUSE frames + * anyway. If the intent on the link partner was to have + * flow control enabled, then by us enabling RX only, we + * can at least receive pause frames and process them. + * This is a good idea because in most cases, since we are + * predominantly a server NIC, more times than not we will + * be asked to delay transmission of packets than asking + * our link partner to pause transmission of frames. + */ + else if (hw->original_fc == e1000_fc_none || + hw->original_fc == e1000_fc_tx_pause) { + hw->fc = e1000_fc_none; + DEBUGOUT("Flow Control = NONE.\r\n"); + } else { + hw->fc = e1000_fc_rx_pause; + DEBUGOUT + ("Flow Control = RX PAUSE frames only.\r\n"); + } + + /* Now we need to do one last check... If we auto- + * negotiated to HALF DUPLEX, flow control should not be + * enabled per IEEE 802.3 spec. + */ + e1000_get_speed_and_duplex(hw, &speed, &duplex); + + if (duplex == HALF_DUPLEX) + hw->fc = e1000_fc_none; + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + ret_val = e1000_force_mac_fc(hw); + if (ret_val < 0) { + DEBUGOUT + ("Error forcing flow control settings\n"); + return ret_val; + } + } else { + DEBUGOUT + ("Copper PHY and Auto Neg has not completed.\r\n"); + } + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Checks to see if the link status of the hardware has changed. + * + * hw - Struct containing variables accessed by shared code + * + * Called by any function that needs to check the link status of the adapter. + *****************************************************************************/ +static int +e1000_check_for_link(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint32_t rxcw; + uint32_t ctrl; + uint32_t status; + uint32_t rctl; + uint32_t signal; + int32_t ret_val; + uint16_t phy_data; + uint16_t lp_capability; + + DEBUGFUNC(); + + /* On adapters with a MAC newer that 82544, SW Defineable pin 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal + */ + ctrl = E1000_READ_REG(hw, CTRL); + if ((hw->mac_type > e1000_82544) && !(ctrl & E1000_CTRL_ILOS)) + signal = E1000_CTRL_SWDPIN1; + else + signal = 0; + + status = E1000_READ_REG(hw, STATUS); + rxcw = E1000_READ_REG(hw, RXCW); + DEBUGOUT("ctrl: %#08x status %#08x rxcw %#08x\n", ctrl, status, rxcw); + + /* If we have a copper PHY then we only want to go out to the PHY + * registers to see if Auto-Neg has completed and/or if our link + * status has changed. The get_link_status flag will be set if we + * receive a Link Status Change interrupt or we have Rx Sequence + * Errors. + */ + if ((hw->media_type == e1000_media_type_copper) && hw->get_link_status) { + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + * Read the register twice since the link bit is sticky. + */ + if (e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + + if (phy_data & MII_SR_LINK_STATUS) { + hw->get_link_status = false; + } else { + /* No link detected */ + return -E1000_ERR_NOLINK; + } + + /* We have a M88E1000 PHY and Auto-Neg is enabled. If we + * have Si on board that is 82544 or newer, Auto + * Speed Detection takes care of MAC speed/duplex + * configuration. So we only need to configure Collision + * Distance in the MAC. Otherwise, we need to force + * speed/duplex on the MAC to the current PHY speed/duplex + * settings. + */ + if (hw->mac_type >= e1000_82544) + e1000_config_collision_dist(hw); + else { + ret_val = e1000_config_mac_to_phy(hw); + if (ret_val < 0) { + DEBUGOUT + ("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + + /* Configure Flow Control now that Auto-Neg has completed. First, we + * need to restore the desired flow control settings because we may + * have had to re-autoneg with a different link partner. + */ + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val < 0) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + + /* At this point we know that we are on copper and we have + * auto-negotiated link. These are conditions for checking the link + * parter capability register. We use the link partner capability to + * determine if TBI Compatibility needs to be turned on or off. If + * the link partner advertises any speed in addition to Gigabit, then + * we assume that they are GMII-based, and TBI compatibility is not + * needed. If no other speeds are advertised, we assume the link + * partner is TBI-based, and we turn on TBI Compatibility. + */ + if (hw->tbi_compatibility_en) { + if (e1000_read_phy_reg + (hw, PHY_LP_ABILITY, &lp_capability) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (lp_capability & (NWAY_LPAR_10T_HD_CAPS | + NWAY_LPAR_10T_FD_CAPS | + NWAY_LPAR_100TX_HD_CAPS | + NWAY_LPAR_100TX_FD_CAPS | + NWAY_LPAR_100T4_CAPS)) { + /* If our link partner advertises anything in addition to + * gigabit, we do not need to enable TBI compatibility. + */ + if (hw->tbi_compatibility_on) { + /* If we previously were in the mode, turn it off. */ + rctl = E1000_READ_REG(hw, RCTL); + rctl &= ~E1000_RCTL_SBP; + E1000_WRITE_REG(hw, RCTL, rctl); + hw->tbi_compatibility_on = false; + } + } else { + /* If TBI compatibility is was previously off, turn it on. For + * compatibility with a TBI link partner, we will store bad + * packets. Some frames have an additional byte on the end and + * will look like CRC errors to to the hardware. + */ + if (!hw->tbi_compatibility_on) { + hw->tbi_compatibility_on = true; + rctl = E1000_READ_REG(hw, RCTL); + rctl |= E1000_RCTL_SBP; + E1000_WRITE_REG(hw, RCTL, rctl); + } + } + } + } + /* If we don't have link (auto-negotiation failed or link partner cannot + * auto-negotiate), the cable is plugged in (we have signal), and our + * link partner is not trying to auto-negotiate with us (we are receiving + * idles or data), we need to force link up. We also need to give + * auto-negotiation time to complete, in case the cable was just plugged + * in. The autoneg_failed flag does this. + */ + else if ((hw->media_type == e1000_media_type_fiber) && + (!(status & E1000_STATUS_LU)) && + ((ctrl & E1000_CTRL_SWDPIN1) == signal) && + (!(rxcw & E1000_RXCW_C))) { + if (hw->autoneg_failed == 0) { + hw->autoneg_failed = 1; + return 0; + } + DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\r\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, TXCW, (hw->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val < 0) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + } + /* If we are forcing link and we are receiving /C/ ordered sets, re-enable + * auto-negotiation in the TXCW register and disable forced link in the + * Device Control register in an attempt to auto-negotiate with our link + * partner. + */ + else if ((hw->media_type == e1000_media_type_fiber) && + (ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + DEBUGOUT + ("RXing /C/, enable AutoNeg and stop forcing link.\r\n"); + E1000_WRITE_REG(hw, TXCW, hw->txcw); + E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU)); + } + return 0; +} + +/****************************************************************************** +* Configure the MAC-to-PHY interface for 10/100Mbps +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, uint16_t duplex) +{ + int32_t ret_val = E1000_SUCCESS; + uint32_t tipg; + uint16_t reg_data; + + DEBUGFUNC(); + + reg_data = E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT; + ret_val = e1000_write_kmrn_reg(hw, + E1000_KUMCTRLSTA_OFFSET_HD_CTRL, reg_data); + if (ret_val) + return ret_val; + + /* Configure Transmit Inter-Packet Gap */ + tipg = E1000_READ_REG(hw, TIPG); + tipg &= ~E1000_TIPG_IPGT_MASK; + tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_10_100; + E1000_WRITE_REG(hw, TIPG, tipg); + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + + if (ret_val) + return ret_val; + + if (duplex == HALF_DUPLEX) + reg_data |= GG82563_KMCR_PASS_FALSE_CARRIER; + else + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + + return ret_val; +} + +static int32_t +e1000_configure_kmrn_for_1000(struct e1000_hw *hw) +{ + int32_t ret_val = E1000_SUCCESS; + uint16_t reg_data; + uint32_t tipg; + + DEBUGFUNC(); + + reg_data = E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT; + ret_val = e1000_write_kmrn_reg(hw, + E1000_KUMCTRLSTA_OFFSET_HD_CTRL, reg_data); + if (ret_val) + return ret_val; + + /* Configure Transmit Inter-Packet Gap */ + tipg = E1000_READ_REG(hw, TIPG); + tipg &= ~E1000_TIPG_IPGT_MASK; + tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; + E1000_WRITE_REG(hw, TIPG, tipg); + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + + if (ret_val) + return ret_val; + + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + + return ret_val; +} + +/****************************************************************************** + * Detects the current speed and duplex settings of the hardware. + * + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + *****************************************************************************/ +static int +e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t *speed, + uint16_t *duplex) +{ + uint32_t status; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + if (hw->mac_type >= e1000_82543) { + status = E1000_READ_REG(hw, STATUS); + if (status & E1000_STATUS_SPEED_1000) { + *speed = SPEED_1000; + DEBUGOUT("1000 Mbs, "); + } else if (status & E1000_STATUS_SPEED_100) { + *speed = SPEED_100; + DEBUGOUT("100 Mbs, "); + } else { + *speed = SPEED_10; + DEBUGOUT("10 Mbs, "); + } + + if (status & E1000_STATUS_FD) { + *duplex = FULL_DUPLEX; + DEBUGOUT("Full Duplex\r\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT(" Half Duplex\r\n"); + } + } else { + DEBUGOUT("1000 Mbs, Full Duplex\r\n"); + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + } + + /* IGP01 PHY may advertise full duplex operation after speed downgrade + * even if it is operating at half duplex. Here we set the duplex + * settings to match the duplex in the link partner's capabilities. + */ + if (hw->phy_type == e1000_phy_igp && hw->speed_downgraded) { + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data); + if (ret_val) + return ret_val; + + if (!(phy_data & NWAY_ER_LP_NWAY_CAPS)) + *duplex = HALF_DUPLEX; + else { + ret_val = e1000_read_phy_reg(hw, + PHY_LP_ABILITY, &phy_data); + if (ret_val) + return ret_val; + if ((*speed == SPEED_100 && + !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) + || (*speed == SPEED_10 + && !(phy_data & NWAY_LPAR_10T_FD_CAPS))) + *duplex = HALF_DUPLEX; + } + } + + if ((hw->mac_type == e1000_80003es2lan) && + (hw->media_type == e1000_media_type_copper)) { + if (*speed == SPEED_1000) + ret_val = e1000_configure_kmrn_for_1000(hw); + else + ret_val = e1000_configure_kmrn_for_10_100(hw, *duplex); + if (ret_val) + return ret_val; + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Blocks until autoneg completes or times out (~4.5 seconds) +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int +e1000_wait_autoneg(struct e1000_hw *hw) +{ + uint16_t i; + uint16_t phy_data; + + DEBUGFUNC(); + DEBUGOUT("Waiting for Auto-Neg to complete.\n"); + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + for (i = PHY_AUTO_NEG_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg + * Complete bit to be set. + */ + if (e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (phy_data & MII_SR_AUTONEG_COMPLETE) { + DEBUGOUT("Auto-Neg complete.\n"); + return 0; + } + mdelay(100); + } + DEBUGOUT("Auto-Neg timedout.\n"); + return -E1000_ERR_TIMEOUT; +} + +/****************************************************************************** +* Raises the Management Data Clock +* +* hw - Struct containing variables accessed by shared code +* ctrl - Device control register's current value +******************************************************************************/ +static void +e1000_raise_mdi_clk(struct e1000_hw *hw, uint32_t * ctrl) +{ + /* Raise the clock input to the Management Data Clock (by setting the MDC + * bit), and then delay 2 microseconds. + */ + E1000_WRITE_REG(hw, CTRL, (*ctrl | E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + udelay(2); +} + +/****************************************************************************** +* Lowers the Management Data Clock +* +* hw - Struct containing variables accessed by shared code +* ctrl - Device control register's current value +******************************************************************************/ +static void +e1000_lower_mdi_clk(struct e1000_hw *hw, uint32_t * ctrl) +{ + /* Lower the clock input to the Management Data Clock (by clearing the MDC + * bit), and then delay 2 microseconds. + */ + E1000_WRITE_REG(hw, CTRL, (*ctrl & ~E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + udelay(2); +} + +/****************************************************************************** +* Shifts data bits out to the PHY +* +* hw - Struct containing variables accessed by shared code +* data - Data to send out to the PHY +* count - Number of bits to shift out +* +* Bits are shifted out in MSB to LSB order. +******************************************************************************/ +static void +e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data, uint16_t count) +{ + uint32_t ctrl; + uint32_t mask; + + /* We need to shift "count" number of bits out to the PHY. So, the value + * in the "data" parameter will be shifted out to the PHY one bit at a + * time. In order to do this, "data" must be broken down into bits. + */ + mask = 0x01; + mask <<= (count - 1); + + ctrl = E1000_READ_REG(hw, CTRL); + + /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */ + ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR); + + while (mask) { + /* A "1" is shifted out to the PHY by setting the MDIO bit to "1" and + * then raising and lowering the Management Data Clock. A "0" is + * shifted out to the PHY by setting the MDIO bit to "0" and then + * raising and lowering the clock. + */ + if (data & mask) + ctrl |= E1000_CTRL_MDIO; + else + ctrl &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + udelay(2); + + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + mask = mask >> 1; + } +} + +/****************************************************************************** +* Shifts data bits in from the PHY +* +* hw - Struct containing variables accessed by shared code +* +* Bits are shifted in in MSB to LSB order. +******************************************************************************/ +static uint16_t +e1000_shift_in_mdi_bits(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint16_t data = 0; + uint8_t i; + + /* In order to read a register from the PHY, we need to shift in a total + * of 18 bits from the PHY. The first two bit (turnaround) times are used + * to avoid contention on the MDIO pin when a read operation is performed. + * These two bits are ignored by us and thrown away. Bits are "shifted in" + * by raising the input to the Management Data Clock (setting the MDC bit), + * and then reading the value of the MDIO bit. + */ + ctrl = E1000_READ_REG(hw, CTRL); + + /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as input. */ + ctrl &= ~E1000_CTRL_MDIO_DIR; + ctrl &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + /* Raise and Lower the clock before reading in the data. This accounts for + * the turnaround bits. The first clock occurred when we clocked out the + * last bit of the Register Address. + */ + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + for (data = 0, i = 0; i < 16; i++) { + data = data << 1; + e1000_raise_mdi_clk(hw, &ctrl); + ctrl = E1000_READ_REG(hw, CTRL); + /* Check to see if we shifted in a "1". */ + if (ctrl & E1000_CTRL_MDIO) + data |= 1; + e1000_lower_mdi_clk(hw, &ctrl); + } + + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + return data; +} + +/***************************************************************************** +* Reads the value from a PHY register +* +* hw - Struct containing variables accessed by shared code +* reg_addr - address of the PHY register to read +******************************************************************************/ +static int +e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t * phy_data) +{ + uint32_t i; + uint32_t mdic = 0; + const uint32_t phy_addr = 1; + + if (reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT("PHY Address %d is out of range\n", reg_addr); + return -E1000_ERR_PARAM; + } + + if (hw->mac_type > e1000_82543) { + /* Set up Op-code, Phy Address, and register address in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) | + (phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_READ)); + + E1000_WRITE_REG(hw, MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < 64; i++) { + udelay(10); + mdic = E1000_READ_REG(hw, MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (mdic & E1000_MDIC_ERROR) { + DEBUGOUT("MDI Error\n"); + return -E1000_ERR_PHY; + } + *phy_data = (uint16_t) mdic; + } else { + /* We must first send a preamble through the MDIO pin to signal the + * beginning of an MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the next few fields that are required for a read + * operation. We use this method instead of calling the + * e1000_shift_out_mdi_bits routine five different times. The format of + * a MII read instruction consists of a shift out of 14 bits and is + * defined as follows: + * + * followed by a shift in of 18 bits. This first two bits shifted in + * are TurnAround bits used to avoid contention on the MDIO pin when a + * READ operation is performed. These two bits are thrown away + * followed by a shift in of 16 bits which contains the desired data. + */ + mdic = ((reg_addr) | (phy_addr << 5) | + (PHY_OP_READ << 10) | (PHY_SOF << 12)); + + e1000_shift_out_mdi_bits(hw, mdic, 14); + + /* Now that we've shifted out the read command to the MII, we need to + * "shift in" the 16-bit value (18 total bits) of the requested PHY + * register address. + */ + *phy_data = e1000_shift_in_mdi_bits(hw); + } + return 0; +} + +/****************************************************************************** +* Writes a value to a PHY register +* +* hw - Struct containing variables accessed by shared code +* reg_addr - address of the PHY register to write +* data - data to write to the PHY +******************************************************************************/ +static int +e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t phy_data) +{ + uint32_t i; + uint32_t mdic = 0; + const uint32_t phy_addr = 1; + + if (reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT("PHY Address %d is out of range\n", reg_addr); + return -E1000_ERR_PARAM; + } + + if (hw->mac_type > e1000_82543) { + /* Set up Op-code, Phy Address, register address, and data intended + * for the PHY register in the MDI Control register. The MAC will take + * care of interfacing with the PHY to send the desired data. + */ + mdic = (((uint32_t) phy_data) | + (reg_addr << E1000_MDIC_REG_SHIFT) | + (phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_WRITE)); + + E1000_WRITE_REG(hw, MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < 64; i++) { + udelay(10); + mdic = E1000_READ_REG(hw, MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Write did not complete\n"); + return -E1000_ERR_PHY; + } + } else { + /* We'll need to use the SW defined pins to shift the write command + * out to the PHY. We first send a preamble to the PHY to signal the + * beginning of the MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the remaining required fields that will indicate a + * write operation. We use this method instead of calling the + * e1000_shift_out_mdi_bits routine for each field in the command. The + * format of a MII write instruction is as follows: + * . + */ + mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) | + (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); + mdic <<= 16; + mdic |= (uint32_t) phy_data; + + e1000_shift_out_mdi_bits(hw, mdic, 32); + } + return 0; +} + +/****************************************************************************** + * Checks if PHY reset is blocked due to SOL/IDER session, for example. + * Returning E1000_BLK_PHY_RESET isn't necessarily an error. But it's up to + * the caller to figure out how to deal with it. + * + * hw - Struct containing variables accessed by shared code + * + * returns: - E1000_BLK_PHY_RESET + * E1000_SUCCESS + * + *****************************************************************************/ +int32_t +e1000_check_phy_reset_block(struct e1000_hw *hw) +{ + uint32_t manc = 0; + uint32_t fwsm = 0; + + if (hw->mac_type == e1000_ich8lan) { + fwsm = E1000_READ_REG(hw, FWSM); + return (fwsm & E1000_FWSM_RSPCIPHY) ? E1000_SUCCESS + : E1000_BLK_PHY_RESET; + } + + if (hw->mac_type > e1000_82547_rev_2) + manc = E1000_READ_REG(hw, MANC); + return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? + E1000_BLK_PHY_RESET : E1000_SUCCESS; +} + +/*************************************************************************** + * Checks if the PHY configuration is done + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_RESET if fail to reset MAC + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +static int32_t +e1000_get_phy_cfg_done(struct e1000_hw *hw) +{ + int32_t timeout = PHY_CFG_TIMEOUT; + uint32_t cfg_mask = E1000_EEPROM_CFG_DONE; + + DEBUGFUNC(); + + switch (hw->mac_type) { + default: + mdelay(10); + break; + + case e1000_80003es2lan: + /* Separate *_CFG_DONE_* bit for each port */ + if (e1000_is_second_port(hw)) + cfg_mask = E1000_EEPROM_CFG_DONE_PORT_1; + /* Fall Through */ + + case e1000_82571: + case e1000_82572: + while (timeout) { + if (E1000_READ_REG(hw, EEMNGCTL) & cfg_mask) + break; + else + mdelay(1); + timeout--; + } + if (!timeout) { + DEBUGOUT("MNG configuration cycle has not " + "completed.\n"); + return -E1000_ERR_RESET; + } + break; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Returns the PHY to the power-on reset state +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +e1000_phy_hw_reset(struct e1000_hw *hw) +{ + uint16_t swfw = E1000_SWFW_PHY0_SM; + uint32_t ctrl, ctrl_ext; + uint32_t led_ctrl; + int32_t ret_val; + + DEBUGFUNC(); + + /* In the case of the phy reset being blocked, it's not an error, we + * simply return success without performing the reset. */ + ret_val = e1000_check_phy_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + + DEBUGOUT("Resetting Phy...\n"); + + if (hw->mac_type > e1000_82543) { + if (e1000_is_second_port(hw)) + swfw = E1000_SWFW_PHY1_SM; + + if (e1000_swfw_sync_acquire(hw, swfw)) { + DEBUGOUT("Unable to acquire swfw sync\n"); + return -E1000_ERR_SWFW_SYNC; + } + + /* Read the device control register and assert the E1000_CTRL_PHY_RST + * bit. Then, take it out of reset. + */ + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(hw); + + if (hw->mac_type < e1000_82571) + udelay(10); + else + udelay(100); + + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + if (hw->mac_type >= e1000_82571) + mdelay(10); + + } else { + /* Read the Extended Device Control Register, assert the PHY_RESET_DIR + * bit to put the PHY into reset. Then, take it out of reset. + */ + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR; + ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + mdelay(10); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + } + udelay(150); + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } + + /* Wait for FW to finish PHY configuration. */ + ret_val = e1000_get_phy_cfg_done(hw); + if (ret_val != E1000_SUCCESS) + return ret_val; + + return ret_val; +} + +/****************************************************************************** + * IGP phy init script - initializes the GbE PHY + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_phy_init_script(struct e1000_hw *hw) +{ + uint32_t ret_val; + uint16_t phy_saved_data; + DEBUGFUNC(); + + if (hw->phy_init_script) { + mdelay(20); + + /* Save off the current value of register 0x2F5B to be + * restored at the end of this routine. */ + ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + /* Disabled the PHY transmitter */ + e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + + mdelay(20); + + e1000_write_phy_reg(hw, 0x0000, 0x0140); + + mdelay(5); + + switch (hw->mac_type) { + case e1000_82541: + case e1000_82547: + e1000_write_phy_reg(hw, 0x1F95, 0x0001); + + e1000_write_phy_reg(hw, 0x1F71, 0xBD21); + + e1000_write_phy_reg(hw, 0x1F79, 0x0018); + + e1000_write_phy_reg(hw, 0x1F30, 0x1600); + + e1000_write_phy_reg(hw, 0x1F31, 0x0014); + + e1000_write_phy_reg(hw, 0x1F32, 0x161C); + + e1000_write_phy_reg(hw, 0x1F94, 0x0003); + + e1000_write_phy_reg(hw, 0x1F96, 0x003F); + + e1000_write_phy_reg(hw, 0x2010, 0x0008); + break; + + case e1000_82541_rev_2: + case e1000_82547_rev_2: + e1000_write_phy_reg(hw, 0x1F73, 0x0099); + break; + default: + break; + } + + e1000_write_phy_reg(hw, 0x0000, 0x3300); + + mdelay(20); + + /* Now enable the transmitter */ + if (!ret_val) + e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if (hw->mac_type == e1000_82547) { + uint16_t fused, fine, coarse; + + /* Move to analog registers page */ + e1000_read_phy_reg(hw, + IGP01E1000_ANALOG_SPARE_FUSE_STATUS, &fused); + + if (!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) { + e1000_read_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_STATUS, &fused); + + fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK; + coarse = fused + & IGP01E1000_ANALOG_FUSE_COARSE_MASK; + + if (coarse > + IGP01E1000_ANALOG_FUSE_COARSE_THRESH) { + coarse -= + IGP01E1000_ANALOG_FUSE_COARSE_10; + fine -= IGP01E1000_ANALOG_FUSE_FINE_1; + } else if (coarse + == IGP01E1000_ANALOG_FUSE_COARSE_THRESH) + fine -= IGP01E1000_ANALOG_FUSE_FINE_10; + + fused = (fused + & IGP01E1000_ANALOG_FUSE_POLY_MASK) | + (fine + & IGP01E1000_ANALOG_FUSE_FINE_MASK) | + (coarse + & IGP01E1000_ANALOG_FUSE_COARSE_MASK); + + e1000_write_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_CONTROL, fused); + e1000_write_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_BYPASS, + IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL); + } + } + } +} + +/****************************************************************************** +* Resets the PHY +* +* hw - Struct containing variables accessed by shared code +* +* Sets bit 15 of the MII Control register +******************************************************************************/ +int32_t +e1000_phy_reset(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + /* In the case of the phy reset being blocked, it's not an error, we + * simply return success without performing the reset. */ + ret_val = e1000_check_phy_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + + switch (hw->phy_type) { + case e1000_phy_igp: + case e1000_phy_igp_2: + case e1000_phy_igp_3: + case e1000_phy_ife: + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) + return ret_val; + break; + default: + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= MII_CR_RESET; + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if (ret_val) + return ret_val; + + udelay(1); + break; + } + + if (hw->phy_type == e1000_phy_igp || hw->phy_type == e1000_phy_igp_2) + e1000_phy_init_script(hw); + + return E1000_SUCCESS; +} + +static int e1000_set_phy_type (struct e1000_hw *hw) +{ + DEBUGFUNC (); + + if (hw->mac_type == e1000_undefined) + return -E1000_ERR_PHY_TYPE; + + switch (hw->phy_id) { + case M88E1000_E_PHY_ID: + case M88E1000_I_PHY_ID: + case M88E1011_I_PHY_ID: + case M88E1111_I_PHY_ID: + hw->phy_type = e1000_phy_m88; + break; + case IGP01E1000_I_PHY_ID: + if (hw->mac_type == e1000_82541 || + hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547 || + hw->mac_type == e1000_82547_rev_2) { + hw->phy_type = e1000_phy_igp; + break; + } + case IGP03E1000_E_PHY_ID: + hw->phy_type = e1000_phy_igp_3; + break; + case IFE_E_PHY_ID: + case IFE_PLUS_E_PHY_ID: + case IFE_C_E_PHY_ID: + hw->phy_type = e1000_phy_ife; + break; + case GG82563_E_PHY_ID: + if (hw->mac_type == e1000_80003es2lan) { + hw->phy_type = e1000_phy_gg82563; + break; + } + case BME1000_E_PHY_ID: + hw->phy_type = e1000_phy_bm; + break; + /* Fall Through */ + default: + /* Should never have loaded on this device */ + hw->phy_type = e1000_phy_undefined; + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Probes the expected PHY address for known PHY IDs +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_detect_gig_phy(struct e1000_hw *hw) +{ + int32_t phy_init_status, ret_val; + uint16_t phy_id_high, phy_id_low; + bool match = false; + + DEBUGFUNC(); + + /* The 82571 firmware may still be configuring the PHY. In this + * case, we cannot access the PHY until the configuration is done. So + * we explicitly set the PHY values. */ + if (hw->mac_type == e1000_82571 || + hw->mac_type == e1000_82572) { + hw->phy_id = IGP01E1000_I_PHY_ID; + hw->phy_type = e1000_phy_igp_2; + return E1000_SUCCESS; + } + + /* ESB-2 PHY reads require e1000_phy_gg82563 to be set because of a + * work- around that forces PHY page 0 to be set or the reads fail. + * The rest of the code in this routine uses e1000_read_phy_reg to + * read the PHY ID. So for ESB-2 we need to have this set so our + * reads won't fail. If the attached PHY is not a e1000_phy_gg82563, + * the routines below will figure this out as well. */ + if (hw->mac_type == e1000_80003es2lan) + hw->phy_type = e1000_phy_gg82563; + + /* Read the PHY ID Registers to identify which PHY is onboard. */ + ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high); + if (ret_val) + return ret_val; + + hw->phy_id = (uint32_t) (phy_id_high << 16); + udelay(20); + ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low); + if (ret_val) + return ret_val; + + hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK); + hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK; + + switch (hw->mac_type) { + case e1000_82543: + if (hw->phy_id == M88E1000_E_PHY_ID) + match = true; + break; + case e1000_82544: + if (hw->phy_id == M88E1000_I_PHY_ID) + match = true; + break; + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + if (hw->phy_id == M88E1011_I_PHY_ID) + match = true; + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + if(hw->phy_id == IGP01E1000_I_PHY_ID) + match = true; + + break; + case e1000_82573: + if (hw->phy_id == M88E1111_I_PHY_ID) + match = true; + break; + case e1000_82574: + if (hw->phy_id == BME1000_E_PHY_ID) + match = true; + break; + case e1000_80003es2lan: + if (hw->phy_id == GG82563_E_PHY_ID) + match = true; + break; + case e1000_ich8lan: + if (hw->phy_id == IGP03E1000_E_PHY_ID) + match = true; + if (hw->phy_id == IFE_E_PHY_ID) + match = true; + if (hw->phy_id == IFE_PLUS_E_PHY_ID) + match = true; + if (hw->phy_id == IFE_C_E_PHY_ID) + match = true; + break; + default: + DEBUGOUT("Invalid MAC type %d\n", hw->mac_type); + return -E1000_ERR_CONFIG; + } + + phy_init_status = e1000_set_phy_type(hw); + + if ((match) && (phy_init_status == E1000_SUCCESS)) { + DEBUGOUT("PHY ID 0x%X detected\n", hw->phy_id); + return 0; + } + DEBUGOUT("Invalid PHY ID 0x%X\n", hw->phy_id); + return -E1000_ERR_PHY; +} + +/***************************************************************************** + * Set media type and TBI compatibility. + * + * hw - Struct containing variables accessed by shared code + * **************************************************************************/ +void +e1000_set_media_type(struct e1000_hw *hw) +{ + uint32_t status; + + DEBUGFUNC(); + + if (hw->mac_type != e1000_82543) { + /* tbi_compatibility is only valid on 82543 */ + hw->tbi_compatibility_en = false; + } + + switch (hw->device_id) { + case E1000_DEV_ID_82545GM_SERDES: + case E1000_DEV_ID_82546GB_SERDES: + case E1000_DEV_ID_82571EB_SERDES: + case E1000_DEV_ID_82571EB_SERDES_DUAL: + case E1000_DEV_ID_82571EB_SERDES_QUAD: + case E1000_DEV_ID_82572EI_SERDES: + case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: + hw->media_type = e1000_media_type_internal_serdes; + break; + default: + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + hw->media_type = e1000_media_type_fiber; + break; + case e1000_ich8lan: + case e1000_82573: + case e1000_82574: + /* The STATUS_TBIMODE bit is reserved or reused + * for the this device. + */ + hw->media_type = e1000_media_type_copper; + break; + default: + status = E1000_READ_REG(hw, STATUS); + if (status & E1000_STATUS_TBIMODE) { + hw->media_type = e1000_media_type_fiber; + /* tbi_compatibility not valid on fiber */ + hw->tbi_compatibility_en = false; + } else { + hw->media_type = e1000_media_type_copper; + } + break; + } + } +} + +/** + * e1000_sw_init - Initialize general software structures (struct e1000_adapter) + * + * e1000_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + **/ + +static int +e1000_sw_init(struct eth_device *nic) +{ + struct e1000_hw *hw = (typeof(hw)) nic->priv; + int result; + + /* PCI config space info */ + pci_read_config_word(hw->pdev, PCI_VENDOR_ID, &hw->vendor_id); + pci_read_config_word(hw->pdev, PCI_DEVICE_ID, &hw->device_id); + pci_read_config_word(hw->pdev, PCI_SUBSYSTEM_VENDOR_ID, + &hw->subsystem_vendor_id); + pci_read_config_word(hw->pdev, PCI_SUBSYSTEM_ID, &hw->subsystem_id); + + pci_read_config_byte(hw->pdev, PCI_REVISION_ID, &hw->revision_id); + pci_read_config_word(hw->pdev, PCI_COMMAND, &hw->pci_cmd_word); + + /* identify the MAC */ + result = e1000_set_mac_type(hw); + if (result) { + E1000_ERR(hw->nic, "Unknown MAC Type\n"); + return result; + } + + switch (hw->mac_type) { + default: + break; + case e1000_82541: + case e1000_82547: + case e1000_82541_rev_2: + case e1000_82547_rev_2: + hw->phy_init_script = 1; + break; + } + + /* flow control settings */ + hw->fc_high_water = E1000_FC_HIGH_THRESH; + hw->fc_low_water = E1000_FC_LOW_THRESH; + hw->fc_pause_time = E1000_FC_PAUSE_TIME; + hw->fc_send_xon = 1; + + /* Media type - copper or fiber */ + e1000_set_media_type(hw); + + if (hw->mac_type >= e1000_82543) { + uint32_t status = E1000_READ_REG(hw, STATUS); + + if (status & E1000_STATUS_TBIMODE) { + DEBUGOUT("fiber interface\n"); + hw->media_type = e1000_media_type_fiber; + } else { + DEBUGOUT("copper interface\n"); + hw->media_type = e1000_media_type_copper; + } + } else { + hw->media_type = e1000_media_type_fiber; + } + + hw->tbi_compatibility_en = true; + hw->wait_autoneg_complete = true; + if (hw->mac_type < e1000_82543) + hw->report_tx_early = 0; + else + hw->report_tx_early = 1; + + return E1000_SUCCESS; +} + +void +fill_rx(struct e1000_hw *hw) +{ + struct e1000_rx_desc *rd; + + rx_last = rx_tail; + rd = rx_base + rx_tail; + rx_tail = (rx_tail + 1) % 8; + memset(rd, 0, 16); + rd->buffer_addr = cpu_to_le64((u32) & packet); + E1000_WRITE_REG(hw, RDT, rx_tail); +} + +/** + * e1000_configure_tx - Configure 8254x Transmit Unit after Reset + * @adapter: board private structure + * + * Configure the Tx unit of the MAC after a reset. + **/ + +static void +e1000_configure_tx(struct e1000_hw *hw) +{ + unsigned long ptr; + unsigned long tctl; + unsigned long tipg, tarc; + uint32_t ipgr1, ipgr2; + + ptr = (u32) tx_pool; + if (ptr & 0xf) + ptr = (ptr + 0x10) & (~0xf); + + tx_base = (typeof(tx_base)) ptr; + + E1000_WRITE_REG(hw, TDBAL, (u32) tx_base); + E1000_WRITE_REG(hw, TDBAH, 0); + + E1000_WRITE_REG(hw, TDLEN, 128); + + /* Setup the HW Tx Head and Tail descriptor pointers */ + E1000_WRITE_REG(hw, TDH, 0); + E1000_WRITE_REG(hw, TDT, 0); + tx_tail = 0; + + /* Set the default values for the Tx Inter Packet Gap timer */ + if (hw->mac_type <= e1000_82547_rev_2 && + (hw->media_type == e1000_media_type_fiber || + hw->media_type == e1000_media_type_internal_serdes)) + tipg = DEFAULT_82543_TIPG_IPGT_FIBER; + else + tipg = DEFAULT_82543_TIPG_IPGT_COPPER; + + /* Set the default values for the Tx Inter Packet Gap timer */ + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + tipg = DEFAULT_82542_TIPG_IPGT; + ipgr1 = DEFAULT_82542_TIPG_IPGR1; + ipgr2 = DEFAULT_82542_TIPG_IPGR2; + break; + case e1000_80003es2lan: + ipgr1 = DEFAULT_82543_TIPG_IPGR1; + ipgr2 = DEFAULT_80003ES2LAN_TIPG_IPGR2; + break; + default: + ipgr1 = DEFAULT_82543_TIPG_IPGR1; + ipgr2 = DEFAULT_82543_TIPG_IPGR2; + break; + } + tipg |= ipgr1 << E1000_TIPG_IPGR1_SHIFT; + tipg |= ipgr2 << E1000_TIPG_IPGR2_SHIFT; + E1000_WRITE_REG(hw, TIPG, tipg); + /* Program the Transmit Control Register */ + tctl = E1000_READ_REG(hw, TCTL); + tctl &= ~E1000_TCTL_CT; + tctl |= E1000_TCTL_EN | E1000_TCTL_PSP | + (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT); + + if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572) { + tarc = E1000_READ_REG(hw, TARC0); + /* set the speed mode bit, we'll clear it if we're not at + * gigabit link later */ + /* git bit can be set to 1*/ + } else if (hw->mac_type == e1000_80003es2lan) { + tarc = E1000_READ_REG(hw, TARC0); + tarc |= 1; + E1000_WRITE_REG(hw, TARC0, tarc); + tarc = E1000_READ_REG(hw, TARC1); + tarc |= 1; + E1000_WRITE_REG(hw, TARC1, tarc); + } + + + e1000_config_collision_dist(hw); + /* Setup Transmit Descriptor Settings for eop descriptor */ + hw->txd_cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_IFCS; + + /* Need to set up RS bit */ + if (hw->mac_type < e1000_82543) + hw->txd_cmd |= E1000_TXD_CMD_RPS; + else + hw->txd_cmd |= E1000_TXD_CMD_RS; + E1000_WRITE_REG(hw, TCTL, tctl); +} + +/** + * e1000_setup_rctl - configure the receive control register + * @adapter: Board private structure + **/ +static void +e1000_setup_rctl(struct e1000_hw *hw) +{ + uint32_t rctl; + + rctl = E1000_READ_REG(hw, RCTL); + + rctl &= ~(3 << E1000_RCTL_MO_SHIFT); + + rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO + | E1000_RCTL_RDMTS_HALF; /* | + (hw.mc_filter_type << E1000_RCTL_MO_SHIFT); */ + + if (hw->tbi_compatibility_on == 1) + rctl |= E1000_RCTL_SBP; + else + rctl &= ~E1000_RCTL_SBP; + + rctl &= ~(E1000_RCTL_SZ_4096); + rctl |= E1000_RCTL_SZ_2048; + rctl &= ~(E1000_RCTL_BSEX | E1000_RCTL_LPE); + E1000_WRITE_REG(hw, RCTL, rctl); +} + +/** + * e1000_configure_rx - Configure 8254x Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Rx unit of the MAC after a reset. + **/ +static void +e1000_configure_rx(struct e1000_hw *hw) +{ + unsigned long ptr; + unsigned long rctl, ctrl_ext; + rx_tail = 0; + /* make sure receives are disabled while setting up the descriptors */ + rctl = E1000_READ_REG(hw, RCTL); + E1000_WRITE_REG(hw, RCTL, rctl & ~E1000_RCTL_EN); + if (hw->mac_type >= e1000_82540) { + /* Set the interrupt throttling rate. Value is calculated + * as DEFAULT_ITR = 1/(MAX_INTS_PER_SEC * 256ns) */ +#define MAX_INTS_PER_SEC 8000 +#define DEFAULT_ITR 1000000000/(MAX_INTS_PER_SEC * 256) + E1000_WRITE_REG(hw, ITR, DEFAULT_ITR); + } + + if (hw->mac_type >= e1000_82571) { + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + /* Reset delay timers after every interrupt */ + ctrl_ext |= E1000_CTRL_EXT_INT_TIMER_CLR; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + } + /* Setup the Base and Length of the Rx Descriptor Ring */ + ptr = (u32) rx_pool; + if (ptr & 0xf) + ptr = (ptr + 0x10) & (~0xf); + rx_base = (typeof(rx_base)) ptr; + E1000_WRITE_REG(hw, RDBAL, (u32) rx_base); + E1000_WRITE_REG(hw, RDBAH, 0); + + E1000_WRITE_REG(hw, RDLEN, 128); + + /* Setup the HW Rx Head and Tail Descriptor Pointers */ + E1000_WRITE_REG(hw, RDH, 0); + E1000_WRITE_REG(hw, RDT, 0); + /* Enable Receives */ + + E1000_WRITE_REG(hw, RCTL, rctl); + fill_rx(hw); +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int +e1000_poll(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + struct e1000_rx_desc *rd; + /* return true if there's an ethernet packet ready to read */ + rd = rx_base + rx_last; + if (!(le32_to_cpu(rd->status)) & E1000_RXD_STAT_DD) + return 0; + /*DEBUGOUT("recv: packet len=%d \n", rd->length); */ + NetReceive((uchar *)packet, le32_to_cpu(rd->length)); + fill_rx(hw); + return 1; +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static int e1000_transmit(struct eth_device *nic, void *packet, int length) +{ + void *nv_packet = (void *)packet; + struct e1000_hw *hw = nic->priv; + struct e1000_tx_desc *txp; + int i = 0; + + txp = tx_base + tx_tail; + tx_tail = (tx_tail + 1) % 8; + + txp->buffer_addr = cpu_to_le64(virt_to_bus(hw->pdev, nv_packet)); + txp->lower.data = cpu_to_le32(hw->txd_cmd | length); + txp->upper.data = 0; + E1000_WRITE_REG(hw, TDT, tx_tail); + + E1000_WRITE_FLUSH(hw); + while (!(le32_to_cpu(txp->upper.data) & E1000_TXD_STAT_DD)) { + if (i++ > TOUT_LOOP) { + DEBUGOUT("e1000: tx timeout\n"); + return 0; + } + udelay(10); /* give the nic a chance to write to the register */ + } + return 1; +} + +/*reset function*/ +static inline int +e1000_reset(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + + e1000_reset_hw(hw); + if (hw->mac_type >= e1000_82544) { + E1000_WRITE_REG(hw, WUC, 0); + } + return e1000_init_hw(nic); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void +e1000_disable(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + + /* Turn off the ethernet interface */ + E1000_WRITE_REG(hw, RCTL, 0); + E1000_WRITE_REG(hw, TCTL, 0); + + /* Clear the transmit ring */ + E1000_WRITE_REG(hw, TDH, 0); + E1000_WRITE_REG(hw, TDT, 0); + + /* Clear the receive ring */ + E1000_WRITE_REG(hw, RDH, 0); + E1000_WRITE_REG(hw, RDT, 0); + + /* put the card in its initial state */ +#if 0 + E1000_WRITE_REG(hw, CTRL, E1000_CTRL_RST); +#endif + mdelay(10); + +} + +/************************************************************************** +INIT - set up ethernet interface(s) +***************************************************************************/ +static int +e1000_init(struct eth_device *nic, bd_t * bis) +{ + struct e1000_hw *hw = nic->priv; + int ret_val = 0; + + ret_val = e1000_reset(nic); + if (ret_val < 0) { + if ((ret_val == -E1000_ERR_NOLINK) || + (ret_val == -E1000_ERR_TIMEOUT)) { + E1000_ERR(hw->nic, "Valid Link not detected\n"); + } else { + E1000_ERR(hw->nic, "Hardware Initialization Failed\n"); + } + return 0; + } + e1000_configure_tx(hw); + e1000_setup_rctl(hw); + e1000_configure_rx(hw); + return 1; +} + +/****************************************************************************** + * Gets the current PCI bus type of hardware + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +void e1000_get_bus_type(struct e1000_hw *hw) +{ + uint32_t status; + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + hw->bus_type = e1000_bus_type_pci; + break; + case e1000_82571: + case e1000_82572: + case e1000_82573: + case e1000_82574: + case e1000_80003es2lan: + hw->bus_type = e1000_bus_type_pci_express; + break; + case e1000_ich8lan: + hw->bus_type = e1000_bus_type_pci_express; + break; + default: + status = E1000_READ_REG(hw, STATUS); + hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ? + e1000_bus_type_pcix : e1000_bus_type_pci; + break; + } +} + +/* A list of all registered e1000 devices */ +static LIST_HEAD(e1000_hw_list); + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +You should omit the last argument struct pci_device * for a non-PCI NIC +***************************************************************************/ +int +e1000_initialize(bd_t * bis) +{ + unsigned int i; + pci_dev_t devno; + + DEBUGFUNC(); + + /* Find and probe all the matching PCI devices */ + for (i = 0; (devno = pci_find_devices(e1000_supported, i)) >= 0; i++) { + u32 val; + + /* + * These will never get freed due to errors, this allows us to + * perform SPI EEPROM programming from U-boot, for example. + */ + struct eth_device *nic = malloc(sizeof(*nic)); + struct e1000_hw *hw = malloc(sizeof(*hw)); + if (!nic || !hw) { + printf("e1000#%u: Out of Memory!\n", i); + free(nic); + free(hw); + continue; + } + + /* Make sure all of the fields are initially zeroed */ + memset(nic, 0, sizeof(*nic)); + memset(hw, 0, sizeof(*hw)); + + /* Assign the passed-in values */ + hw->cardnum = i; + hw->pdev = devno; + hw->nic = nic; + nic->priv = hw; + + /* Generate a card name */ + sprintf(nic->name, "e1000#%u", hw->cardnum); + + /* Print a debug message with the IO base address */ + pci_read_config_dword(devno, PCI_BASE_ADDRESS_0, &val); + E1000_DBG(nic, "iobase 0x%08x\n", val & 0xfffffff0); + + /* Try to enable I/O accesses and bus-mastering */ + val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + pci_write_config_dword(devno, PCI_COMMAND, val); + + /* Make sure it worked */ + pci_read_config_dword(devno, PCI_COMMAND, &val); + if (!(val & PCI_COMMAND_MEMORY)) { + E1000_ERR(nic, "Can't enable I/O memory\n"); + continue; + } + if (!(val & PCI_COMMAND_MASTER)) { + E1000_ERR(nic, "Can't enable bus-mastering\n"); + continue; + } + + /* Are these variables needed? */ + hw->fc = e1000_fc_default; + hw->original_fc = e1000_fc_default; + hw->autoneg_failed = 0; + hw->autoneg = 1; + hw->get_link_status = true; + hw->hw_addr = pci_map_bar(devno, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); + hw->mac_type = e1000_undefined; + + /* MAC and Phy settings */ + if (e1000_sw_init(nic) < 0) { + E1000_ERR(nic, "Software init failed\n"); + continue; + } + if (e1000_check_phy_reset_block(hw)) + E1000_ERR(nic, "PHY Reset is blocked!\n"); + + /* Basic init was OK, reset the hardware and allow SPI access */ + e1000_reset_hw(hw); + list_add_tail(&hw->list_node, &e1000_hw_list); + +#ifndef CONFIG_E1000_NO_NVM + /* Validate the EEPROM and get chipset information */ +#if !defined(CONFIG_MVBC_1G) + if (e1000_init_eeprom_params(hw)) { + E1000_ERR(nic, "EEPROM is invalid!\n"); + continue; + } + if (e1000_validate_eeprom_checksum(hw)) + continue; +#endif + e1000_read_mac_addr(nic); +#endif + e1000_get_bus_type(hw); + +#ifndef CONFIG_E1000_NO_NVM + printf("e1000: %02x:%02x:%02x:%02x:%02x:%02x\n ", + nic->enetaddr[0], nic->enetaddr[1], nic->enetaddr[2], + nic->enetaddr[3], nic->enetaddr[4], nic->enetaddr[5]); +#else + memset(nic->enetaddr, 0, 6); + printf("e1000: no NVM\n"); +#endif + + /* Set up the function pointers and register the device */ + nic->init = e1000_init; + nic->recv = e1000_poll; + nic->send = e1000_transmit; + nic->halt = e1000_disable; + eth_register(nic); + } + + return i; +} + +struct e1000_hw *e1000_find_card(unsigned int cardnum) +{ + struct e1000_hw *hw; + + list_for_each_entry(hw, &e1000_hw_list, list_node) + if (hw->cardnum == cardnum) + return hw; + + return NULL; +} + +#ifdef CONFIG_CMD_E1000 +static int do_e1000(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct e1000_hw *hw; + + if (argc < 3) { + cmd_usage(cmdtp); + return 1; + } + + /* Make sure we can find the requested e1000 card */ + hw = e1000_find_card(simple_strtoul(argv[1], NULL, 10)); + if (!hw) { + printf("e1000: ERROR: No such device: e1000#%s\n", argv[1]); + return 1; + } + + if (!strcmp(argv[2], "print-mac-address")) { + unsigned char *mac = hw->nic->enetaddr; + printf("%02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return 0; + } + +#ifdef CONFIG_E1000_SPI + /* Handle the "SPI" subcommand */ + if (!strcmp(argv[2], "spi")) + return do_e1000_spi(cmdtp, hw, argc - 3, argv + 3); +#endif + + cmd_usage(cmdtp); + return 1; +} + +U_BOOT_CMD( + e1000, 7, 0, do_e1000, + "Intel e1000 controller management", + /* */" print-mac-address\n" +#ifdef CONFIG_E1000_SPI + "e1000 spi show [ []]\n" + "e1000 spi dump \n" + "e1000 spi program \n" + "e1000 spi checksum [update]\n" +#endif + " - Manage the Intel E1000 PCI device" +); +#endif /* not CONFIG_CMD_E1000 */ diff --git a/qemu/roms/u-boot/drivers/net/e1000.h b/qemu/roms/u-boot/drivers/net/e1000.h new file mode 100644 index 000000000..ff87af2ef --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/e1000.h @@ -0,0 +1,2601 @@ +/******************************************************************************* + + + Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved. + Copyright 2011 Freescale Semiconductor, Inc. + + * SPDX-License-Identifier: GPL-2.0+ + + Contact Information: + Linux NICS + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* e1000_hw.h + * Structures, enums, and macros for the MAC + */ + +#ifndef _E1000_HW_H_ +#define _E1000_HW_H_ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_E1000_SPI +#include +#endif + +#define E1000_ERR(NIC, fmt, args...) \ + printf("e1000: %s: ERROR: " fmt, (NIC)->name ,##args) + +#ifdef E1000_DEBUG +#define E1000_DBG(NIC, fmt, args...) \ + printf("e1000: %s: DEBUG: " fmt, (NIC)->name ,##args) +#define DEBUGOUT(fmt, args...) printf(fmt ,##args) +#define DEBUGFUNC() printf("%s\n", __func__); +#else +#define E1000_DBG(HW, args...) do { } while (0) +#define DEBUGFUNC() do { } while (0) +#define DEBUGOUT(fmt, args...) do { } while (0) +#endif + +/* I/O wrapper functions */ +#define E1000_WRITE_REG(a, reg, value) \ + writel((value), ((a)->hw_addr + E1000_##reg)) +#define E1000_READ_REG(a, reg) \ + readl((a)->hw_addr + E1000_##reg) +#define E1000_WRITE_REG_ARRAY(a, reg, offset, value) \ + writel((value), ((a)->hw_addr + E1000_##reg + ((offset) << 2))) +#define E1000_READ_REG_ARRAY(a, reg, offset) \ + readl((a)->hw_addr + E1000_##reg + ((offset) << 2)) +#define E1000_WRITE_FLUSH(a) \ + do { E1000_READ_REG(a, STATUS); } while (0) + +/* Forward declarations of structures used by the shared code */ +struct e1000_hw; +struct e1000_hw_stats; + +/* Internal E1000 helper functions */ +struct e1000_hw *e1000_find_card(unsigned int cardnum); + +#ifndef CONFIG_E1000_NO_NVM +int32_t e1000_acquire_eeprom(struct e1000_hw *hw); +void e1000_standby_eeprom(struct e1000_hw *hw); +void e1000_release_eeprom(struct e1000_hw *hw); +void e1000_raise_ee_clk(struct e1000_hw *hw, uint32_t *eecd); +void e1000_lower_ee_clk(struct e1000_hw *hw, uint32_t *eecd); +#endif + +#ifdef CONFIG_E1000_SPI +int do_e1000_spi(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char * const argv[]); +#endif + +/* Enumerated types specific to the e1000 hardware */ +/* Media Access Controlers */ +typedef enum { + e1000_undefined = 0, + e1000_82542_rev2_0, + e1000_82542_rev2_1, + e1000_82543, + e1000_82544, + e1000_82540, + e1000_82545, + e1000_82545_rev_3, + e1000_82546, + e1000_82546_rev_3, + e1000_82541, + e1000_82541_rev_2, + e1000_82547, + e1000_82547_rev_2, + e1000_82571, + e1000_82572, + e1000_82573, + e1000_82574, + e1000_80003es2lan, + e1000_ich8lan, + e1000_num_macs +} e1000_mac_type; + +/* Media Types */ +typedef enum { + e1000_media_type_copper = 0, + e1000_media_type_fiber = 1, + e1000_media_type_internal_serdes = 2, + e1000_num_media_types +} e1000_media_type; + +typedef enum { + e1000_eeprom_uninitialized = 0, + e1000_eeprom_spi, + e1000_eeprom_microwire, + e1000_eeprom_flash, + e1000_eeprom_ich8, + e1000_eeprom_none, /* No NVM support */ + e1000_num_eeprom_types +} e1000_eeprom_type; + +typedef enum { + e1000_10_half = 0, + e1000_10_full = 1, + e1000_100_half = 2, + e1000_100_full = 3 +} e1000_speed_duplex_type; + +/* Flow Control Settings */ +typedef enum { + e1000_fc_none = 0, + e1000_fc_rx_pause = 1, + e1000_fc_tx_pause = 2, + e1000_fc_full = 3, + e1000_fc_default = 0xFF +} e1000_fc_type; + +/* PCI bus types */ +typedef enum { + e1000_bus_type_unknown = 0, + e1000_bus_type_pci, + e1000_bus_type_pcix, + e1000_bus_type_pci_express, + e1000_bus_type_reserved +} e1000_bus_type; + +/* PCI bus speeds */ +typedef enum { + e1000_bus_speed_unknown = 0, + e1000_bus_speed_33, + e1000_bus_speed_66, + e1000_bus_speed_100, + e1000_bus_speed_133, + e1000_bus_speed_reserved +} e1000_bus_speed; + +/* PCI bus widths */ +typedef enum { + e1000_bus_width_unknown = 0, + e1000_bus_width_32, + e1000_bus_width_64 +} e1000_bus_width; + +/* PHY status info structure and supporting enums */ +typedef enum { + e1000_cable_length_50 = 0, + e1000_cable_length_50_80, + e1000_cable_length_80_110, + e1000_cable_length_110_140, + e1000_cable_length_140, + e1000_cable_length_undefined = 0xFF +} e1000_cable_length; + +typedef enum { + e1000_10bt_ext_dist_enable_normal = 0, + e1000_10bt_ext_dist_enable_lower, + e1000_10bt_ext_dist_enable_undefined = 0xFF +} e1000_10bt_ext_dist_enable; + +typedef enum { + e1000_rev_polarity_normal = 0, + e1000_rev_polarity_reversed, + e1000_rev_polarity_undefined = 0xFF +} e1000_rev_polarity; + +typedef enum { + e1000_polarity_reversal_enabled = 0, + e1000_polarity_reversal_disabled, + e1000_polarity_reversal_undefined = 0xFF +} e1000_polarity_reversal; + +typedef enum { + e1000_auto_x_mode_manual_mdi = 0, + e1000_auto_x_mode_manual_mdix, + e1000_auto_x_mode_auto1, + e1000_auto_x_mode_auto2, + e1000_auto_x_mode_undefined = 0xFF +} e1000_auto_x_mode; + +typedef enum { + e1000_1000t_rx_status_not_ok = 0, + e1000_1000t_rx_status_ok, + e1000_1000t_rx_status_undefined = 0xFF +} e1000_1000t_rx_status; + +typedef enum { + e1000_phy_m88 = 0, + e1000_phy_igp, + e1000_phy_igp_2, + e1000_phy_gg82563, + e1000_phy_igp_3, + e1000_phy_ife, + e1000_phy_bm, + e1000_phy_undefined = 0xFF +} e1000_phy_type; + +struct e1000_phy_info { + e1000_cable_length cable_length; + e1000_10bt_ext_dist_enable extended_10bt_distance; + e1000_rev_polarity cable_polarity; + e1000_polarity_reversal polarity_correction; + e1000_auto_x_mode mdix_mode; + e1000_1000t_rx_status local_rx; + e1000_1000t_rx_status remote_rx; +}; + +struct e1000_phy_stats { + uint32_t idle_errors; + uint32_t receive_errors; +}; + +/* Error Codes */ +#define E1000_SUCCESS 0 +#define E1000_ERR_EEPROM 1 +#define E1000_ERR_PHY 2 +#define E1000_ERR_CONFIG 3 +#define E1000_ERR_PARAM 4 +#define E1000_ERR_MAC_TYPE 5 +#define E1000_ERR_PHY_TYPE 6 +#define E1000_ERR_NOLINK 7 +#define E1000_ERR_TIMEOUT 8 +#define E1000_ERR_RESET 9 +#define E1000_ERR_MASTER_REQUESTS_PENDING 10 +#define E1000_ERR_HOST_INTERFACE_COMMAND 11 +#define E1000_BLK_PHY_RESET 12 +#define E1000_ERR_SWFW_SYNC 13 + +/* PCI Device IDs */ +#define E1000_DEV_ID_82542 0x1000 +#define E1000_DEV_ID_82543GC_FIBER 0x1001 +#define E1000_DEV_ID_82543GC_COPPER 0x1004 +#define E1000_DEV_ID_82544EI_COPPER 0x1008 +#define E1000_DEV_ID_82544EI_FIBER 0x1009 +#define E1000_DEV_ID_82544GC_COPPER 0x100C +#define E1000_DEV_ID_82544GC_LOM 0x100D +#define E1000_DEV_ID_82540EM 0x100E +#define E1000_DEV_ID_82540EM_LOM 0x1015 +#define E1000_DEV_ID_82540EP_LOM 0x1016 +#define E1000_DEV_ID_82540EP 0x1017 +#define E1000_DEV_ID_82540EP_LP 0x101E +#define E1000_DEV_ID_82545EM_COPPER 0x100F +#define E1000_DEV_ID_82545EM_FIBER 0x1011 +#define E1000_DEV_ID_82545GM_COPPER 0x1026 +#define E1000_DEV_ID_82545GM_FIBER 0x1027 +#define E1000_DEV_ID_82545GM_SERDES 0x1028 +#define E1000_DEV_ID_82546EB_COPPER 0x1010 +#define E1000_DEV_ID_82546EB_FIBER 0x1012 +#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D +#define E1000_DEV_ID_82541EI 0x1013 +#define E1000_DEV_ID_82541EI_MOBILE 0x1018 +#define E1000_DEV_ID_82541ER_LOM 0x1014 +#define E1000_DEV_ID_82541ER 0x1078 +#define E1000_DEV_ID_82547GI 0x1075 +#define E1000_DEV_ID_82541GI 0x1076 +#define E1000_DEV_ID_82541GI_MOBILE 0x1077 +#define E1000_DEV_ID_82541GI_LF 0x107C +#define E1000_DEV_ID_82546GB_COPPER 0x1079 +#define E1000_DEV_ID_82546GB_FIBER 0x107A +#define E1000_DEV_ID_82546GB_SERDES 0x107B +#define E1000_DEV_ID_82546GB_PCIE 0x108A +#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 +#define E1000_DEV_ID_82547EI 0x1019 +#define E1000_DEV_ID_82547EI_MOBILE 0x101A +#define E1000_DEV_ID_82571EB_COPPER 0x105E +#define E1000_DEV_ID_82571EB_FIBER 0x105F +#define E1000_DEV_ID_82571EB_SERDES 0x1060 +#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4 +#define E1000_DEV_ID_82571PT_QUAD_COPPER 0x10D5 +#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5 +#define E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE 0x10BC +#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9 +#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA +#define E1000_DEV_ID_82572EI_COPPER 0x107D +#define E1000_DEV_ID_82572EI_FIBER 0x107E +#define E1000_DEV_ID_82572EI_SERDES 0x107F +#define E1000_DEV_ID_82572EI 0x10B9 +#define E1000_DEV_ID_82573E 0x108B +#define E1000_DEV_ID_82573E_IAMT 0x108C +#define E1000_DEV_ID_82573L 0x109A +#define E1000_DEV_ID_82574L 0x10D3 +#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 +#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 +#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 +#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA +#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB + +#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 +#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A +#define E1000_DEV_ID_ICH8_IGP_C 0x104B +#define E1000_DEV_ID_ICH8_IFE 0x104C +#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4 +#define E1000_DEV_ID_ICH8_IFE_G 0x10C5 +#define E1000_DEV_ID_ICH8_IGP_M 0x104D + +#define IGP03E1000_E_PHY_ID 0x02A80390 +#define IFE_E_PHY_ID 0x02A80330 /* 10/100 PHY */ +#define IFE_PLUS_E_PHY_ID 0x02A80320 +#define IFE_C_E_PHY_ID 0x02A80310 + +#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 /* 100BaseTx Extended Status, + Control and Address */ +#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY special + control register */ +#define IFE_PHY_RCV_FALSE_CARRIER 0x13 /* 100BaseTx Receive false + Carrier Counter */ +#define IFE_PHY_RCV_DISCONNECT 0x14 /* 100BaseTx Receive Disconnet + Counter */ +#define IFE_PHY_RCV_ERROT_FRAME 0x15 /* 100BaseTx Receive Error + Frame Counter */ +#define IFE_PHY_RCV_SYMBOL_ERR 0x16 /* Receive Symbol Error + Counter */ +#define IFE_PHY_PREM_EOF_ERR 0x17 /* 100BaseTx Receive + Premature End Of Frame + Error Counter */ +#define IFE_PHY_RCV_EOF_ERR 0x18 /* 10BaseT Receive End Of + Frame Error Counter */ +#define IFE_PHY_TX_JABBER_DETECT 0x19 /* 10BaseT Transmit Jabber + Detect Counter */ +#define IFE_PHY_EQUALIZER 0x1A /* PHY Equalizer Control and + Status */ +#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY special control and + LED configuration */ +#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control register */ +#define IFE_PHY_HWI_CONTROL 0x1D /* Hardware Integrity Control + (HWI) */ + +#define IFE_PESC_REDUCED_POWER_DOWN_DISABLE 0x2000 /* Defaut 1 = Disable auto + reduced power down */ +#define IFE_PESC_100BTX_POWER_DOWN 0x0400 /* Indicates the power + state of 100BASE-TX */ +#define IFE_PESC_10BTX_POWER_DOWN 0x0200 /* Indicates the power + state of 10BASE-T */ +#define IFE_PESC_POLARITY_REVERSED 0x0100 /* Indicates 10BASE-T + polarity */ +#define IFE_PESC_PHY_ADDR_MASK 0x007C /* Bit 6:2 for sampled PHY + address */ +#define IFE_PESC_SPEED 0x0002 /* Auto-negotiation speed + result 1=100Mbs, 0=10Mbs */ +#define IFE_PESC_DUPLEX 0x0001 /* Auto-negotiation + duplex result 1=Full, 0=Half */ +#define IFE_PESC_POLARITY_REVERSED_SHIFT 8 + +#define IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN 0x0100 /* 1 = Dyanmic Power Down + disabled */ +#define IFE_PSC_FORCE_POLARITY 0x0020 /* 1=Reversed Polarity, + 0=Normal */ +#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 /* 1=Auto Polarity + Disabled, 0=Enabled */ +#define IFE_PSC_JABBER_FUNC_DISABLE 0x0001 /* 1=Jabber Disabled, + 0=Normal Jabber Operation */ +#define IFE_PSC_FORCE_POLARITY_SHIFT 5 +#define IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT 4 + +#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable MDI/MDI-X + feature, default 0=disabled */ +#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDIX-X, + 0=force MDI */ +#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ +#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorithm + is completed */ +#define IFE_PMC_MDIX_MODE_SHIFT 6 +#define IFE_PHC_MDIX_RESET_ALL_MASK 0x0000 /* Disable auto MDI-X */ + +#define IFE_PHC_HWI_ENABLE 0x8000 /* Enable the HWI + feature */ +#define IFE_PHC_ABILITY_CHECK 0x4000 /* 1= Test Passed, + 0=failed */ +#define IFE_PHC_TEST_EXEC 0x2000 /* PHY launch test pulses + on the wire */ +#define IFE_PHC_HIGHZ 0x0200 /* 1 = Open Circuit */ +#define IFE_PHC_LOWZ 0x0400 /* 1 = Short Circuit */ +#define IFE_PHC_LOW_HIGH_Z_MASK 0x0600 /* Mask for indication + type of problem on the line */ +#define IFE_PHC_DISTANCE_MASK 0x01FF /* Mask for distance to + the cable problem, in 80cm granularity */ +#define IFE_PHC_RESET_ALL_MASK 0x0000 /* Disable HWI */ +#define IFE_PSCL_PROBE_MODE 0x0020 /* LED Probe mode */ +#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 + off */ +#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */ + + +#define NUM_DEV_IDS 16 + +#define NODE_ADDRESS_SIZE 6 +#define ETH_LENGTH_OF_ADDRESS 6 + +/* MAC decode size is 128K - This is the size of BAR0 */ +#define MAC_DECODE_SIZE (128 * 1024) + +#define E1000_82542_2_0_REV_ID 2 +#define E1000_82542_2_1_REV_ID 3 +#define E1000_REVISION_0 0 +#define E1000_REVISION_1 1 +#define E1000_REVISION_2 2 +#define E1000_REVISION_3 3 + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +/* The sizes (in bytes) of a ethernet packet */ +#define ENET_HEADER_SIZE 14 +#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* With FCS */ +#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* With FCS */ +#define ETHERNET_FCS_SIZE 4 +#define MAXIMUM_ETHERNET_PACKET_SIZE \ + (MAXIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE) +#define MINIMUM_ETHERNET_PACKET_SIZE \ + (MINIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE) +#define CRC_LENGTH ETHERNET_FCS_SIZE +#define MAX_JUMBO_FRAME_SIZE 0x3F00 + +/* 802.1q VLAN Packet Sizes */ +#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMAed) */ + +/* Ethertype field values */ +#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ +#define ETHERNET_IP_TYPE 0x0800 /* IP packets */ +#define ETHERNET_ARP_TYPE 0x0806 /* Address Resolution Protocol (ARP) */ + +/* Packet Header defines */ +#define IP_PROTOCOL_TCP 6 +#define IP_PROTOCOL_UDP 0x11 + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + */ +#define POLL_IMS_ENABLE_MASK ( \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ) + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXT0 = Receiver Timer Interrupt (ring 0) + * o TXDW = Transmit Descriptor Written Back + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + * o LSC = Link Status Change + */ +#define IMS_ENABLE_MASK ( \ + E1000_IMS_RXT0 | \ + E1000_IMS_TXDW | \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ | \ + E1000_IMS_LSC) + +/* The number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. We + * reserve one of these spots for our directed address, allowing us room for + * E1000_RAR_ENTRIES - 1 multicast addresses. + */ +#define E1000_RAR_ENTRIES 16 + +#define MIN_NUMBER_OF_DESCRIPTORS 8 +#define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8 + +/* Receive Descriptor */ +struct e1000_rx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + uint16_t length; /* Length of data DMAed into data buffer */ + uint16_t csum; /* Packet checksum */ + uint8_t status; /* Descriptor status */ + uint8_t errors; /* Descriptor Errors */ + uint16_t special; +}; + +/* Receive Decriptor bit definitions */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ +#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ +#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ +#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ +#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ +#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ +#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ +#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ +#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ +#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ +#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ +#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ +#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ +#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ +#define E1000_RXD_SPC_PRI_SHIFT 0x000D /* Priority is in upper 3 of 16 */ +#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ +#define E1000_RXD_SPC_CFI_SHIFT 0x000C /* CFI is bit 12 */ + +/* mask to determine if packets should be dropped due to frame errors */ +#define E1000_RXD_ERR_FRAME_ERR_MASK ( \ + E1000_RXD_ERR_CE | \ + E1000_RXD_ERR_SE | \ + E1000_RXD_ERR_SEQ | \ + E1000_RXD_ERR_CXE | \ + E1000_RXD_ERR_RXE) + +/* Transmit Descriptor */ +struct e1000_tx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + } fields; + } upper; +}; + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + +/* Offload Context Descriptor */ +struct e1000_context_desc { + union { + uint32_t ip_config; + struct { + uint8_t ipcss; /* IP checksum start */ + uint8_t ipcso; /* IP checksum offset */ + uint16_t ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + uint32_t tcp_config; + struct { + uint8_t tucss; /* TCP checksum start */ + uint8_t tucso; /* TCP checksum offset */ + uint16_t tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + uint32_t cmd_and_length; /* */ + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t hdr_len; /* Header length */ + uint16_t mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Offload data descriptor */ +struct e1000_data_desc { + uint64_t buffer_addr; /* Address of the descriptor's buffer address */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t typ_len_ext; /* */ + uint8_t cmd; /* */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t popts; /* Packet Options */ + uint16_t special; /* */ + } fields; + } upper; +}; + +/* Filters */ +#define E1000_NUM_UNICAST 16 /* Unicast filter entries */ +#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ + +/* Receive Address Register */ +struct e1000_rar { + volatile uint32_t low; /* receive address low */ + volatile uint32_t high; /* receive address high */ +}; + +/* The number of entries in the Multicast Table Array (MTA). */ +#define E1000_NUM_MTA_REGISTERS 128 + +/* IPv4 Address Table Entry */ +struct e1000_ipv4_at_entry { + volatile uint32_t ipv4_addr; /* IP Address (RW) */ + volatile uint32_t reserved; +}; + +/* Four wakeup IP addresses are supported */ +#define E1000_WAKEUP_IP_ADDRESS_COUNT_MAX 4 +#define E1000_IP4AT_SIZE E1000_WAKEUP_IP_ADDRESS_COUNT_MAX +#define E1000_IP6AT_SIZE 1 + +/* IPv6 Address Table Entry */ +struct e1000_ipv6_at_entry { + volatile uint8_t ipv6_addr[16]; +}; + +/* Flexible Filter Length Table Entry */ +struct e1000_fflt_entry { + volatile uint32_t length; /* Flexible Filter Length (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Mask Table Entry */ +struct e1000_ffmt_entry { + volatile uint32_t mask; /* Flexible Filter Mask (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Value Table Entry */ +struct e1000_ffvt_entry { + volatile uint32_t value; /* Flexible Filter Value (RW) */ + volatile uint32_t reserved; +}; + +/* Four Flexible Filters are supported */ +#define E1000_FLEXIBLE_FILTER_COUNT_MAX 4 + +/* Each Flexible Filter is at most 128 (0x80) bytes in length */ +#define E1000_FLEXIBLE_FILTER_SIZE_MAX 128 + +#define E1000_FFLT_SIZE E1000_FLEXIBLE_FILTER_COUNT_MAX +#define E1000_FFMT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX +#define E1000_FFVT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX + +/* Register Set. (82543, 82544) + * + * Registers are defined to be 32 bits and should be accessed as 32 bit values. + * These registers are physically located on the NIC, but are mapped into the + * host memory address space. + * + * RW - register is both readable and writable + * RO - register is read only + * WO - register is write only + * R/clr - register is read only and is cleared when read + * A - register array + */ +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ +#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_TBT 0x00448 /* TX Burst Timer - RW */ +#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ +#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ +#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ +#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ +#define FEXTNVM_SW_CONFIG 0x0001 +#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_FLASH_UPDATES 1000 +#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ +#define E1000_FLASHT 0x01028 /* FLASH Timer Register */ +#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_FLSWCTL 0x01030 /* FLASH control register */ +#define E1000_FLSWDATA 0x01034 /* FLASH data register */ +#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ +#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ +#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */ +#define E1000_RXDCTL 0x02828 /* RX Descriptor Control - RW */ +#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ +#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ +#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ +#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ +#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ +#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ +#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ +#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ +#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */ +#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */ +#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */ +#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */ +#define E1000_TARC0 0x03840 /* TX Arbitration Count (0) */ +#define E1000_TDBAL1 0x03900 /* TX Desc Base Address Low (1) - RW */ +#define E1000_TDBAH1 0x03904 /* TX Desc Base Address High (1) - RW */ +#define E1000_TDLEN1 0x03908 /* TX Desc Length (1) - RW */ +#define E1000_TDH1 0x03910 /* TX Desc Head (1) - RW */ +#define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */ +#define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */ +#define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */ +#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ +#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ +#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ +#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ +#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ +#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ +#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ +#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ +#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ +#define E1000_COLC 0x04028 /* Collision Count - R/clr */ +#define E1000_DC 0x04030 /* Defer Count - R/clr */ +#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ +#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ +#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ +#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ +#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ +#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ +#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ +#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ +#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ +#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ +#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ +#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ +#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ +#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ +#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ +#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ +#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ +#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ +#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ +#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ +#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ +#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ +#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ +#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ +#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ +#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ +#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ +#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ +#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ +#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ +#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ +#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ +#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ +#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ +#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ +#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ +#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ +#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ +#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ +#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ +#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ +#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ +#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ +#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ +#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ +#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ +#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */ +#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_WUC 0x05800 /* Wakeup Control - RW */ +#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define E1000_WUS 0x05810 /* Wakeup Status - RO */ +#define E1000_MANC 0x05820 /* Management Control - RW */ +#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ +#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ +#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ +#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ +#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ +#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ +#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ + +/* Register Set (82542) + * + * Some of the 82542 registers are located at different offsets than they are + * in more current versions of the 8254x. Despite the difference in location, + * the registers function in the same manner. + */ +#define E1000_82542_CTRL E1000_CTRL +#define E1000_82542_STATUS E1000_STATUS +#define E1000_82542_EECD E1000_EECD +#define E1000_82542_EERD E1000_EERD +#define E1000_82542_CTRL_EXT E1000_CTRL_EXT +#define E1000_82542_MDIC E1000_MDIC +#define E1000_82542_FCAL E1000_FCAL +#define E1000_82542_FCAH E1000_FCAH +#define E1000_82542_FCT E1000_FCT +#define E1000_82542_VET E1000_VET +#define E1000_82542_RA 0x00040 +#define E1000_82542_ICR E1000_ICR +#define E1000_82542_ITR E1000_ITR +#define E1000_82542_ICS E1000_ICS +#define E1000_82542_IMS E1000_IMS +#define E1000_82542_IMC E1000_IMC +#define E1000_82542_RCTL E1000_RCTL +#define E1000_82542_RDTR 0x00108 +#define E1000_82542_RDBAL 0x00110 +#define E1000_82542_RDBAH 0x00114 +#define E1000_82542_RDLEN 0x00118 +#define E1000_82542_RDH 0x00120 +#define E1000_82542_RDT 0x00128 +#define E1000_82542_FCRTH 0x00160 +#define E1000_82542_FCRTL 0x00168 +#define E1000_82542_FCTTV E1000_FCTTV +#define E1000_82542_TXCW E1000_TXCW +#define E1000_82542_RXCW E1000_RXCW +#define E1000_82542_MTA 0x00200 +#define E1000_82542_TCTL E1000_TCTL +#define E1000_82542_TIPG E1000_TIPG +#define E1000_82542_TDBAL 0x00420 +#define E1000_82542_TDBAH 0x00424 +#define E1000_82542_TDLEN 0x00428 +#define E1000_82542_TDH 0x00430 +#define E1000_82542_TDT 0x00438 +#define E1000_82542_TIDV 0x00440 +#define E1000_82542_TBT E1000_TBT +#define E1000_82542_AIT E1000_AIT +#define E1000_82542_VFTA 0x00600 +#define E1000_82542_LEDCTL E1000_LEDCTL +#define E1000_82542_PBA E1000_PBA +#define E1000_82542_RXDCTL E1000_RXDCTL +#define E1000_82542_RADV E1000_RADV +#define E1000_82542_RSRPD E1000_RSRPD +#define E1000_82542_TXDMAC E1000_TXDMAC +#define E1000_82542_TXDCTL E1000_TXDCTL +#define E1000_82542_TADV E1000_TADV +#define E1000_82542_TSPMT E1000_TSPMT +#define E1000_82542_CRCERRS E1000_CRCERRS +#define E1000_82542_ALGNERRC E1000_ALGNERRC +#define E1000_82542_SYMERRS E1000_SYMERRS +#define E1000_82542_RXERRC E1000_RXERRC +#define E1000_82542_MPC E1000_MPC +#define E1000_82542_SCC E1000_SCC +#define E1000_82542_ECOL E1000_ECOL +#define E1000_82542_MCC E1000_MCC +#define E1000_82542_LATECOL E1000_LATECOL +#define E1000_82542_COLC E1000_COLC +#define E1000_82542_DC E1000_DC +#define E1000_82542_TNCRS E1000_TNCRS +#define E1000_82542_SEC E1000_SEC +#define E1000_82542_CEXTERR E1000_CEXTERR +#define E1000_82542_RLEC E1000_RLEC +#define E1000_82542_XONRXC E1000_XONRXC +#define E1000_82542_XONTXC E1000_XONTXC +#define E1000_82542_XOFFRXC E1000_XOFFRXC +#define E1000_82542_XOFFTXC E1000_XOFFTXC +#define E1000_82542_FCRUC E1000_FCRUC +#define E1000_82542_PRC64 E1000_PRC64 +#define E1000_82542_PRC127 E1000_PRC127 +#define E1000_82542_PRC255 E1000_PRC255 +#define E1000_82542_PRC511 E1000_PRC511 +#define E1000_82542_PRC1023 E1000_PRC1023 +#define E1000_82542_PRC1522 E1000_PRC1522 +#define E1000_82542_GPRC E1000_GPRC +#define E1000_82542_BPRC E1000_BPRC +#define E1000_82542_MPRC E1000_MPRC +#define E1000_82542_GPTC E1000_GPTC +#define E1000_82542_GORCL E1000_GORCL +#define E1000_82542_GORCH E1000_GORCH +#define E1000_82542_GOTCL E1000_GOTCL +#define E1000_82542_GOTCH E1000_GOTCH +#define E1000_82542_RNBC E1000_RNBC +#define E1000_82542_RUC E1000_RUC +#define E1000_82542_RFC E1000_RFC +#define E1000_82542_ROC E1000_ROC +#define E1000_82542_RJC E1000_RJC +#define E1000_82542_MGTPRC E1000_MGTPRC +#define E1000_82542_MGTPDC E1000_MGTPDC +#define E1000_82542_MGTPTC E1000_MGTPTC +#define E1000_82542_TORL E1000_TORL +#define E1000_82542_TORH E1000_TORH +#define E1000_82542_TOTL E1000_TOTL +#define E1000_82542_TOTH E1000_TOTH +#define E1000_82542_TPR E1000_TPR +#define E1000_82542_TPT E1000_TPT +#define E1000_82542_PTC64 E1000_PTC64 +#define E1000_82542_PTC127 E1000_PTC127 +#define E1000_82542_PTC255 E1000_PTC255 +#define E1000_82542_PTC511 E1000_PTC511 +#define E1000_82542_PTC1023 E1000_PTC1023 +#define E1000_82542_PTC1522 E1000_PTC1522 +#define E1000_82542_MPTC E1000_MPTC +#define E1000_82542_BPTC E1000_BPTC +#define E1000_82542_TSCTC E1000_TSCTC +#define E1000_82542_TSCTFC E1000_TSCTFC +#define E1000_82542_RXCSUM E1000_RXCSUM +#define E1000_82542_WUC E1000_WUC +#define E1000_82542_WUFC E1000_WUFC +#define E1000_82542_WUS E1000_WUS +#define E1000_82542_MANC E1000_MANC +#define E1000_82542_IPAV E1000_IPAV +#define E1000_82542_IP4AT E1000_IP4AT +#define E1000_82542_IP6AT E1000_IP6AT +#define E1000_82542_WUPL E1000_WUPL +#define E1000_82542_WUPM E1000_WUPM +#define E1000_82542_FFLT E1000_FFLT +#define E1000_82542_FFMT E1000_FFMT +#define E1000_82542_FFVT E1000_FFVT + +/* Statistics counters collected by the MAC */ +struct e1000_hw_stats { + uint64_t crcerrs; + uint64_t algnerrc; + uint64_t symerrs; + uint64_t rxerrc; + uint64_t mpc; + uint64_t scc; + uint64_t ecol; + uint64_t mcc; + uint64_t latecol; + uint64_t colc; + uint64_t dc; + uint64_t tncrs; + uint64_t sec; + uint64_t cexterr; + uint64_t rlec; + uint64_t xonrxc; + uint64_t xontxc; + uint64_t xoffrxc; + uint64_t xofftxc; + uint64_t fcruc; + uint64_t prc64; + uint64_t prc127; + uint64_t prc255; + uint64_t prc511; + uint64_t prc1023; + uint64_t prc1522; + uint64_t gprc; + uint64_t bprc; + uint64_t mprc; + uint64_t gptc; + uint64_t gorcl; + uint64_t gorch; + uint64_t gotcl; + uint64_t gotch; + uint64_t rnbc; + uint64_t ruc; + uint64_t rfc; + uint64_t roc; + uint64_t rjc; + uint64_t mgprc; + uint64_t mgpdc; + uint64_t mgptc; + uint64_t torl; + uint64_t torh; + uint64_t totl; + uint64_t toth; + uint64_t tpr; + uint64_t tpt; + uint64_t ptc64; + uint64_t ptc127; + uint64_t ptc255; + uint64_t ptc511; + uint64_t ptc1023; + uint64_t ptc1522; + uint64_t mptc; + uint64_t bptc; + uint64_t tsctc; + uint64_t tsctfc; +}; + +#ifndef CONFIG_E1000_NO_NVM +struct e1000_eeprom_info { +e1000_eeprom_type type; + uint16_t word_size; + uint16_t opcode_bits; + uint16_t address_bits; + uint16_t delay_usec; + uint16_t page_size; + bool use_eerd; + bool use_eewr; +}; +#endif + +typedef enum { + e1000_smart_speed_default = 0, + e1000_smart_speed_on, + e1000_smart_speed_off +} e1000_smart_speed; + +typedef enum { + e1000_dsp_config_disabled = 0, + e1000_dsp_config_enabled, + e1000_dsp_config_activated, + e1000_dsp_config_undefined = 0xFF +} e1000_dsp_config; + +typedef enum { + e1000_ms_hw_default = 0, + e1000_ms_force_master, + e1000_ms_force_slave, + e1000_ms_auto +} e1000_ms_type; + +typedef enum { + e1000_ffe_config_enabled = 0, + e1000_ffe_config_active, + e1000_ffe_config_blocked +} e1000_ffe_config; + + +/* Structure containing variables used by the shared code (e1000_hw.c) */ +struct e1000_hw { + struct list_head list_node; + struct eth_device *nic; +#ifdef CONFIG_E1000_SPI + struct spi_slave spi; +#endif + unsigned int cardnum; + + pci_dev_t pdev; + uint8_t *hw_addr; + e1000_mac_type mac_type; + e1000_phy_type phy_type; + uint32_t phy_init_script; + uint32_t txd_cmd; + e1000_media_type media_type; + e1000_fc_type fc; + e1000_bus_type bus_type; +#if 0 + e1000_bus_speed bus_speed; + e1000_bus_width bus_width; + uint32_t io_base; +#endif + uint32_t asf_firmware_present; +#ifndef CONFIG_E1000_NO_NVM + uint32_t eeprom_semaphore_present; +#endif + uint32_t swfw_sync_present; + uint32_t swfwhw_semaphore_present; +#ifndef CONFIG_E1000_NO_NVM + struct e1000_eeprom_info eeprom; +#endif + e1000_ms_type master_slave; + e1000_ms_type original_master_slave; + e1000_ffe_config ffe_config_state; + uint32_t phy_id; + uint32_t phy_revision; + uint32_t phy_addr; + uint32_t original_fc; + uint32_t txcw; + uint32_t autoneg_failed; +#if 0 + uint32_t max_frame_size; + uint32_t min_frame_size; + uint32_t mc_filter_type; + uint32_t num_mc_addrs; + uint32_t collision_delta; + uint32_t tx_packet_delta; + uint32_t ledctl_default; + uint32_t ledctl_mode1; + uint32_t ledctl_mode2; +#endif + uint16_t autoneg_advertised; + uint16_t pci_cmd_word; + uint16_t fc_high_water; + uint16_t fc_low_water; + uint16_t fc_pause_time; +#if 0 + uint16_t current_ifs_val; + uint16_t ifs_min_val; + uint16_t ifs_max_val; + uint16_t ifs_step_size; + uint16_t ifs_ratio; +#endif + uint16_t device_id; + uint16_t vendor_id; + uint16_t subsystem_id; + uint16_t subsystem_vendor_id; + uint8_t revision_id; + uint8_t autoneg; + uint8_t mdix; + uint8_t forced_speed_duplex; + uint8_t wait_autoneg_complete; + uint8_t dma_fairness; +#if 0 + uint8_t perm_mac_addr[NODE_ADDRESS_SIZE]; +#endif + bool disable_polarity_correction; + bool speed_downgraded; + bool get_link_status; + bool tbi_compatibility_en; + bool tbi_compatibility_on; + bool fc_strict_ieee; + bool fc_send_xon; + bool report_tx_early; + bool phy_reset_disable; + bool initialize_hw_bits_disable; +#if 0 + bool adaptive_ifs; + bool ifs_params_forced; + bool in_ifs_mode; +#endif + e1000_smart_speed smart_speed; + e1000_dsp_config dsp_config_state; +}; + +#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ +#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ +#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM + read/write registers */ +#define E1000_EEPROM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */ +#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start + operation */ +#define E1000_EEPROM_RW_ADDR_SHIFT 2 /* Shift to the address bits */ +#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write + complete */ +#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ +#define EEPROM_RESERVED_WORD 0xFFFF + +/* Register Bit Masks */ +/* Device Control */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ +#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ +#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ +#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ + +/* Device Status */ +#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ +#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ +#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ +#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ +#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ +#define E1000_STATUS_SPEED_MASK 0x000000C0 +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ +#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ +#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ +#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ +#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ +#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ + +/* Constants used to intrepret the masked PCI-X bus speed. */ +#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */ +#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */ +#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */ + +/* EEPROM/Flash Control */ +#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ +#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ +#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */ +#define E1000_EECD_FWE_MASK 0x00000030 +#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ +#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ +#define E1000_EECD_FWE_SHIFT 4 +#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ +#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ +#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type + * (0-small, 1-large) */ + +#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */ +#ifndef E1000_EEPROM_GRANT_ATTEMPTS +#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */ +#endif +#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */ +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */ +#define E1000_EECD_SIZE_EX_SHIFT 11 +#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */ +#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */ +#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */ +#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ +#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ +#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ +#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ +#define E1000_EECD_SECVAL_SHIFT 22 +#define E1000_STM_OPCODE 0xDB00 +#define E1000_HICR_FW_RESET 0xC0 + +#define E1000_SHADOW_RAM_WORDS 2048 +#define E1000_ICH_NVM_SIG_WORD 0x13 +#define E1000_ICH_NVM_SIG_MASK 0xC0 + +/* EEPROM Read */ +#define E1000_EERD_START 0x00000001 /* Start Read */ +#define E1000_EERD_DONE 0x00000010 /* Read Done */ +#define E1000_EERD_ADDR_SHIFT 8 +#define E1000_EERD_ADDR_MASK 0x0000FF00 /* Read Address */ +#define E1000_EERD_DATA_SHIFT 16 +#define E1000_EERD_DATA_MASK 0xFFFF0000 /* Read Data */ + +/* EEPROM Commands - Microwire */ +#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ +#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */ +#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */ +#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */ + +/* EEPROM Commands - SPI */ +#define EEPROM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */ +#define EEPROM_READ_OPCODE_SPI 0x03 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_SPI 0x02 /* EEPROM write opcode */ +#define EEPROM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */ +#define EEPROM_WREN_OPCODE_SPI 0x06 /* EEPROM set Write Enable latch */ +#define EEPROM_WRDI_OPCODE_SPI 0x04 /* EEPROM reset Write Enable latch */ +#define EEPROM_RDSR_OPCODE_SPI 0x05 /* EEPROM read Status register */ +#define EEPROM_WRSR_OPCODE_SPI 0x01 /* EEPROM write Status register */ +#define EEPROM_ERASE4K_OPCODE_SPI 0x20 /* EEPROM ERASE 4KB */ +#define EEPROM_ERASE64K_OPCODE_SPI 0xD8 /* EEPROM ERASE 64KB */ +#define EEPROM_ERASE256_OPCODE_SPI 0xDB /* EEPROM ERASE 256B */ + +/* EEPROM Size definitions */ +#define EEPROM_WORD_SIZE_SHIFT 6 +#define EEPROM_SIZE_SHIFT 10 +#define EEPROM_SIZE_MASK 0x1C00 + +/* EEPROM Word Offsets */ +#define EEPROM_COMPAT 0x0003 +#define EEPROM_ID_LED_SETTINGS 0x0004 +#define EEPROM_VERSION 0x0005 +#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude + adjustment. */ +#define EEPROM_PHY_CLASS_WORD 0x0007 +#define EEPROM_INIT_CONTROL1_REG 0x000A +#define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_SWDEF_PINS_CTRL_PORT_1 0x0010 +#define EEPROM_INIT_CONTROL3_PORT_B 0x0014 +#define EEPROM_INIT_3GIO_3 0x001A +#define EEPROM_SWDEF_PINS_CTRL_PORT_0 0x0020 +#define EEPROM_INIT_CONTROL3_PORT_A 0x0024 +#define EEPROM_CFG 0x0012 +#define EEPROM_FLASH_VERSION 0x0032 +#define EEPROM_CHECKSUM_REG 0x003F + +#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ +#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ + +/* Extended Device Control */ +#define E1000_CTRL_EXT_GPI0_EN 0x00000001 /* Maps SDP4 to GPI0 */ +#define E1000_CTRL_EXT_GPI1_EN 0x00000002 /* Maps SDP5 to GPI1 */ +#define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN +#define E1000_CTRL_EXT_GPI2_EN 0x00000004 /* Maps SDP6 to GPI2 */ +#define E1000_CTRL_EXT_GPI3_EN 0x00000008 /* Maps SDP7 to GPI3 */ +#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable + Pin 4 */ +#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable + Pin 5 */ +#define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA +#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */ +#define E1000_CTRL_EXT_SWDPIN6 0x00000040 /* SWDPIN 6 value */ +#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */ +#define E1000_CTRL_EXT_SWDPIN7 0x00000080 /* SWDPIN 7 value */ +#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */ +#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */ +#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ +#define E1000_CTRL_EXT_SWDPIO6 0x00000400 /* SWDPIN 6 Input or output */ +#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */ +#define E1000_CTRL_EXT_SWDPIO7 0x00000800 /* SWDPIN 7 Input or output */ +#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ +#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */ +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ +#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ +#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000 +#define E1000_CTRL_EXT_WR_WMARK_MASK 0x03000000 +#define E1000_CTRL_EXT_WR_WMARK_256 0x00000000 +#define E1000_CTRL_EXT_WR_WMARK_320 0x01000000 +#define E1000_CTRL_EXT_WR_WMARK_384 0x02000000 +#define E1000_CTRL_EXT_WR_WMARK_448 0x03000000 + +/* MDI Control */ +#define E1000_MDIC_DATA_MASK 0x0000FFFF +#define E1000_MDIC_REG_MASK 0x001F0000 +#define E1000_MDIC_REG_SHIFT 16 +#define E1000_MDIC_PHY_MASK 0x03E00000 +#define E1000_MDIC_PHY_SHIFT 21 +#define E1000_MDIC_OP_WRITE 0x04000000 +#define E1000_MDIC_OP_READ 0x08000000 +#define E1000_MDIC_READY 0x10000000 +#define E1000_MDIC_INT_EN 0x20000000 +#define E1000_MDIC_ERROR 0x40000000 + +#define E1000_PHY_CTRL_SPD_EN 0x00000001 +#define E1000_PHY_CTRL_D0A_LPLU 0x00000002 +#define E1000_PHY_CTRL_NOND0A_LPLU 0x00000004 +#define E1000_PHY_CTRL_NOND0A_GBE_DISABLE 0x00000008 +#define E1000_PHY_CTRL_GBE_DISABLE 0x00000040 +#define E1000_PHY_CTRL_B2B_EN 0x00000080 +/* LED Control */ +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_IVRT 0x00000040 +#define E1000_LEDCTL_LED0_BLINK 0x00000080 +#define E1000_LEDCTL_LED1_MODE_MASK 0x00000F00 +#define E1000_LEDCTL_LED1_MODE_SHIFT 8 +#define E1000_LEDCTL_LED1_IVRT 0x00004000 +#define E1000_LEDCTL_LED1_BLINK 0x00008000 +#define E1000_LEDCTL_LED2_MODE_MASK 0x000F0000 +#define E1000_LEDCTL_LED2_MODE_SHIFT 16 +#define E1000_LEDCTL_LED2_IVRT 0x00400000 +#define E1000_LEDCTL_LED2_BLINK 0x00800000 +#define E1000_LEDCTL_LED3_MODE_MASK 0x0F000000 +#define E1000_LEDCTL_LED3_MODE_SHIFT 24 +#define E1000_LEDCTL_LED3_IVRT 0x40000000 +#define E1000_LEDCTL_LED3_BLINK 0x80000000 + +#define E1000_LEDCTL_MODE_LINK_10_1000 0x0 +#define E1000_LEDCTL_MODE_LINK_100_1000 0x1 +#define E1000_LEDCTL_MODE_LINK_UP 0x2 +#define E1000_LEDCTL_MODE_ACTIVITY 0x3 +#define E1000_LEDCTL_MODE_LINK_ACTIVITY 0x4 +#define E1000_LEDCTL_MODE_LINK_10 0x5 +#define E1000_LEDCTL_MODE_LINK_100 0x6 +#define E1000_LEDCTL_MODE_LINK_1000 0x7 +#define E1000_LEDCTL_MODE_PCIX_MODE 0x8 +#define E1000_LEDCTL_MODE_FULL_DUPLEX 0x9 +#define E1000_LEDCTL_MODE_COLLISION 0xA +#define E1000_LEDCTL_MODE_BUS_SPEED 0xB +#define E1000_LEDCTL_MODE_BUS_SIZE 0xC +#define E1000_LEDCTL_MODE_PAUSED 0xD +#define E1000_LEDCTL_MODE_LED_ON 0xE +#define E1000_LEDCTL_MODE_LED_OFF 0xF + +/* Receive Address */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ + +/* Interrupt Cause Read */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ +#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ +#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXD_LOW 0x00008000 +#define E1000_ICR_SRPD 0x00010000 + +/* Interrupt Cause Set */ +#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_ICS_SRPD E1000_ICR_SRPD + +/* Interrupt Mask Set */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMS_SRPD E1000_ICR_SRPD + +/* Interrupt Mask Clear */ +#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMC_SRPD E1000_ICR_SRPD + +/* Receive Control */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ +#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ +#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ + +/* SW_W_SYNC definitions */ +#define E1000_SWFW_EEP_SM 0x0001 +#define E1000_SWFW_PHY0_SM 0x0002 +#define E1000_SWFW_PHY1_SM 0x0004 +#define E1000_SWFW_MAC_CSR_SM 0x0008 + +/* Receive Descriptor */ +#define E1000_RDT_DELAY 0x0000ffff /* Delay timer (1=1024us) */ +#define E1000_RDT_FPDB 0x80000000 /* Flush descriptor block */ +#define E1000_RDLEN_LEN 0x0007ff80 /* descriptor length */ +#define E1000_RDH_RDH 0x0000ffff /* receive descriptor head */ +#define E1000_RDT_RDT 0x0000ffff /* receive descriptor tail */ + +/* Flow Control */ +#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */ +#define E1000_FCRTH_XFCE 0x80000000 /* External Flow Control Enable */ +#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */ +#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ + +/* Receive Descriptor Control */ +#define E1000_RXDCTL_PTHRESH 0x0000003F /* RXDCTL Prefetch Threshold */ +#define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */ +#define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */ +#define E1000_RXDCTL_GRAN 0x01000000 /* RXDCTL Granularity */ +#define E1000_RXDCTL_FULL_RX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */ + +/* Transmit Descriptor Control */ +#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */ +#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */ +#define E1000_TXDCTL_LWTHRESH 0xFE000000 /* TXDCTL Low Threshold */ +#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */ +#define E1000_TXDCTL_COUNT_DESC 0x00400000 /* Enable the counting of desc. + still to be processed. */ + +/* Transmit Configuration Word */ +#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */ +#define E1000_TXCW_HD 0x00000040 /* TXCW half duplex */ +#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ +#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ +#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */ +#define E1000_TXCW_RF 0x00003000 /* TXCW remote fault */ +#define E1000_TXCW_NP 0x00008000 /* TXCW next page */ +#define E1000_TXCW_CW 0x0000ffff /* TxConfigWord mask */ +#define E1000_TXCW_TXC 0x40000000 /* Transmit Config control */ +#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */ + +/* Receive Configuration Word */ +#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */ +#define E1000_RXCW_NC 0x04000000 /* Receive config no carrier */ +#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */ +#define E1000_RXCW_CC 0x10000000 /* Receive config change */ +#define E1000_RXCW_C 0x20000000 /* Receive config */ +#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */ +#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* software reset */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ + +/* Receive Checksum Control */ +#define E1000_RXCSUM_PCSS_MASK 0x000000FF /* Packet Checksum Start */ +#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */ +#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */ +#define E1000_RXCSUM_IPV6OFL 0x00000400 /* IPv6 checksum offload */ + +/* Definitions for power management and wakeup registers */ +/* Wake Up Control */ +#define E1000_WUC_APME 0x00000001 /* APM Enable */ +#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ +#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ +#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ + +/* Wake Up Filter Control */ +#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ +#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ +#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */ +#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */ +#define E1000_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */ +#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */ +#define E1000_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */ +#define E1000_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */ +#define E1000_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */ +#define E1000_WUFC_ALL_FILTERS 0x000F00FF /* Mask for all wakeup filters */ +#define E1000_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */ +#define E1000_WUFC_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* Wake Up Status */ +#define E1000_WUS_LNKC 0x00000001 /* Link Status Changed */ +#define E1000_WUS_MAG 0x00000002 /* Magic Packet Received */ +#define E1000_WUS_EX 0x00000004 /* Directed Exact Received */ +#define E1000_WUS_MC 0x00000008 /* Directed Multicast Received */ +#define E1000_WUS_BC 0x00000010 /* Broadcast Received */ +#define E1000_WUS_ARP 0x00000020 /* ARP Request Packet Received */ +#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Received */ +#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Received */ +#define E1000_WUS_FLX0 0x00010000 /* Flexible Filter 0 Match */ +#define E1000_WUS_FLX1 0x00020000 /* Flexible Filter 1 Match */ +#define E1000_WUS_FLX2 0x00040000 /* Flexible Filter 2 Match */ +#define E1000_WUS_FLX3 0x00080000 /* Flexible Filter 3 Match */ +#define E1000_WUS_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* Management Control */ +#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ +#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ +#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ +#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ +#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ +#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ +#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ +#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ +#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ +#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery + * Filtering */ +#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ +#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ +#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ +#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ +#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ +#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ +#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ +#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ +#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ + +#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ +#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ + +/* Wake Up Packet Length */ +#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */ + +#define E1000_MDALIGN 4096 + +/* EEPROM Commands */ +#define EEPROM_READ_OPCODE 0x6 /* EERPOM read opcode */ +#define EEPROM_WRITE_OPCODE 0x5 /* EERPOM write opcode */ +#define EEPROM_ERASE_OPCODE 0x7 /* EERPOM erase opcode */ +#define EEPROM_EWEN_OPCODE 0x13 /* EERPOM erase/write enable */ +#define EEPROM_EWDS_OPCODE 0x10 /* EERPOM erast/write disable */ + +/* Word definitions for ID LED Settings */ +#define ID_LED_RESERVED_0000 0x0000 +#define ID_LED_RESERVED_FFFF 0xFFFF +#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \ + (ID_LED_OFF1_OFF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_DEF1_DEF2)) +#define ID_LED_DEF1_DEF2 0x1 +#define ID_LED_DEF1_ON2 0x2 +#define ID_LED_DEF1_OFF2 0x3 +#define ID_LED_ON1_DEF2 0x4 +#define ID_LED_ON1_ON2 0x5 +#define ID_LED_ON1_OFF2 0x6 +#define ID_LED_OFF1_DEF2 0x7 +#define ID_LED_OFF1_ON2 0x8 +#define ID_LED_OFF1_OFF2 0x9 + +/* Mask bits for fields in Word 0x03 of the EEPROM */ +#define EEPROM_COMPAT_SERVER 0x0400 +#define EEPROM_COMPAT_CLIENT 0x0200 + +/* Mask bits for fields in Word 0x0a of the EEPROM */ +#define EEPROM_WORD0A_ILOS 0x0010 +#define EEPROM_WORD0A_SWDPIO 0x01E0 +#define EEPROM_WORD0A_LRST 0x0200 +#define EEPROM_WORD0A_FD 0x0400 +#define EEPROM_WORD0A_66MHZ 0x0800 + +/* Mask bits for fields in Word 0x0f of the EEPROM */ +#define EEPROM_WORD0F_PAUSE_MASK 0x3000 +#define EEPROM_WORD0F_PAUSE 0x1000 +#define EEPROM_WORD0F_ASM_DIR 0x2000 +#define EEPROM_WORD0F_ANE 0x0800 +#define EEPROM_WORD0F_SWPDIO_EXT 0x00F0 + +/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */ +#define EEPROM_SUM 0xBABA + +/* EEPROM Map defines (WORD OFFSETS)*/ +#define EEPROM_NODE_ADDRESS_BYTE_0 0 +#define EEPROM_PBA_BYTE_1 8 + +/* EEPROM Map Sizes (Byte Counts) */ +#define PBA_SIZE 4 + +/* Collision related configuration parameters */ +#define E1000_COLLISION_THRESHOLD 0xF +#define E1000_CT_SHIFT 4 +#define E1000_COLLISION_DISTANCE 63 +#define E1000_COLLISION_DISTANCE_82542 64 +#define E1000_FDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE +#define E1000_HDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE +#define E1000_GB_HDX_COLLISION_DISTANCE 512 +#define E1000_COLD_SHIFT 12 + +/* The number of Transmit and Receive Descriptors must be a multiple of 8 */ +#define REQ_TX_DESCRIPTOR_MULTIPLE 8 +#define REQ_RX_DESCRIPTOR_MULTIPLE 8 + +/* Default values for the transmit IPG register */ +#define DEFAULT_82542_TIPG_IPGT 10 +#define DEFAULT_82543_TIPG_IPGT_FIBER 9 +#define DEFAULT_82543_TIPG_IPGT_COPPER 8 + +#define E1000_TIPG_IPGT_MASK 0x000003FF +#define E1000_TIPG_IPGR1_MASK 0x000FFC00 +#define E1000_TIPG_IPGR2_MASK 0x3FF00000 + +#define DEFAULT_82542_TIPG_IPGR1 2 +#define DEFAULT_82543_TIPG_IPGR1 8 +#define E1000_TIPG_IPGR1_SHIFT 10 + +#define DEFAULT_82542_TIPG_IPGR2 10 +#define DEFAULT_82543_TIPG_IPGR2 6 +#define DEFAULT_80003ES2LAN_TIPG_IPGR2 7 +#define E1000_TIPG_IPGR2_SHIFT 20 + +#define E1000_TXDMAC_DPP 0x00000001 + +/* Adaptive IFS defines */ +#define TX_THRESHOLD_START 8 +#define TX_THRESHOLD_INCREMENT 10 +#define TX_THRESHOLD_DECREMENT 1 +#define TX_THRESHOLD_STOP 190 +#define TX_THRESHOLD_DISABLE 0 +#define TX_THRESHOLD_TIMER_MS 10000 +#define MIN_NUM_XMITS 1000 +#define IFS_MAX 80 +#define IFS_STEP 10 +#define IFS_MIN 40 +#define IFS_RATIO 4 + +/* PBA constants */ +#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ +#define E1000_PBA_24K 0x0018 +#define E1000_PBA_38K 0x0026 +#define E1000_PBA_40K 0x0028 +#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ + +/* Flow Control Constants */ +#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001 +#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 +#define FLOW_CONTROL_TYPE 0x8808 + +/* The historical defaults for the flow control values are given below. */ +#define FC_DEFAULT_HI_THRESH (0x8000) /* 32KB */ +#define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */ +#define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */ + +/* Flow Control High-Watermark: 43464 bytes */ +#define E1000_FC_HIGH_THRESH 0xA9C8 +/* Flow Control Low-Watermark: 43456 bytes */ +#define E1000_FC_LOW_THRESH 0xA9C0 +/* Flow Control Pause Time: 858 usec */ +#define E1000_FC_PAUSE_TIME 0x0680 + +/* PCIX Config space */ +#define PCIX_COMMAND_REGISTER 0xE6 +#define PCIX_STATUS_REGISTER_LO 0xE8 +#define PCIX_STATUS_REGISTER_HI 0xEA + +#define PCIX_COMMAND_MMRBC_MASK 0x000C +#define PCIX_COMMAND_MMRBC_SHIFT 0x2 +#define PCIX_STATUS_HI_MMRBC_MASK 0x0060 +#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5 +#define PCIX_STATUS_HI_MMRBC_4K 0x3 +#define PCIX_STATUS_HI_MMRBC_2K 0x2 + +/* The number of bits that we need to shift right to move the "pause" + * bits from the EEPROM (bits 13:12) to the "pause" (bits 8:7) field + * in the TXCW register + */ +#define PAUSE_SHIFT 5 + +/* The number of bits that we need to shift left to move the "SWDPIO" + * bits from the EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field + * in the CTRL register + */ +#define SWDPIO_SHIFT 17 + +/* The number of bits that we need to shift left to move the "SWDPIO_EXT" + * bits from the EEPROM word F (bits 7:4) to the bits 11:8 of The + * Extended CTRL register. + * in the CTRL register + */ +#define SWDPIO__EXT_SHIFT 4 + +/* The number of bits that we need to shift left to move the "ILOS" + * bit from the EEPROM (bit 4) to the "ILOS" (bit 7) field + * in the CTRL register + */ +#define ILOS_SHIFT 3 + +#define RECEIVE_BUFFER_ALIGN_SIZE (256) + +/* The number of milliseconds we wait for auto-negotiation to complete */ +#define LINK_UP_TIMEOUT 500 + +#define E1000_TX_BUFFER_SIZE ((uint32_t)1514) + +/* The carrier extension symbol, as received by the NIC. */ +#define CARRIER_EXTENSION 0x0F + +/* TBI_ACCEPT macro definition: + * + * This macro requires: + * adapter = a pointer to struct e1000_hw + * status = the 8 bit status field of the RX descriptor with EOP set + * error = the 8 bit error field of the RX descriptor with EOP set + * length = the sum of all the length fields of the RX descriptors that + * make up the current frame + * last_byte = the last byte of the frame DMAed by the hardware + * max_frame_length = the maximum frame length we want to accept. + * min_frame_length = the minimum frame length we want to accept. + * + * This macro is a conditional that should be used in the interrupt + * handler's Rx processing routine when RxErrors have been detected. + * + * Typical use: + * ... + * if (TBI_ACCEPT) { + * accept_frame = true; + * e1000_tbi_adjust_stats(adapter, MacAddress); + * frame_length--; + * } else { + * accept_frame = false; + * } + * ... + */ + +#define TBI_ACCEPT(adapter, status, errors, length, last_byte) \ + ((adapter)->tbi_compatibility_on && \ + (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \ + ((last_byte) == CARRIER_EXTENSION) && \ + (((status) & E1000_RXD_STAT_VP) ? \ + (((length) > ((adapter)->min_frame_size - VLAN_TAG_SIZE)) && \ + ((length) <= ((adapter)->max_frame_size + 1))) : \ + (((length) > (adapter)->min_frame_size) && \ + ((length) <= ((adapter)->max_frame_size + VLAN_TAG_SIZE + 1))))) + +/* Structures, enums, and macros for the PHY */ + +/* Bit definitions for the Management Data IO (MDIO) and Management Data + * Clock (MDC) pins in the Device Control Register. + */ +#define E1000_CTRL_PHY_RESET_DIR E1000_CTRL_SWDPIO0 +#define E1000_CTRL_PHY_RESET E1000_CTRL_SWDPIN0 +#define E1000_CTRL_MDIO_DIR E1000_CTRL_SWDPIO2 +#define E1000_CTRL_MDIO E1000_CTRL_SWDPIN2 +#define E1000_CTRL_MDC_DIR E1000_CTRL_SWDPIO3 +#define E1000_CTRL_MDC E1000_CTRL_SWDPIN3 +#define E1000_CTRL_PHY_RESET_DIR4 E1000_CTRL_EXT_SDP4_DIR +#define E1000_CTRL_PHY_RESET4 E1000_CTRL_EXT_SDP4_DATA + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ +#define PHY_CTRL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Regiser */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +/* M88E1000 Specific Registers */ +#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ +#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ +#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */ +#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */ +#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */ +#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ + +#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */ +#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */ + +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ + +/* M88EC018 Rev 2 specific DownShift settings */ +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_1X 0x0000 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_2X 0x0200 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_3X 0x0400 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_4X 0x0600 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_6X 0x0A00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_7X 0x0C00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_8X 0x0E00 + +/* IGP01E1000 specifics */ +#define IGP01E1000_IEEE_REGS_PAGE 0x0000 +#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300 +#define IGP01E1000_IEEE_FORCE_GIGA 0x0140 + +/* IGP01E1000 Specific Registers */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* PHY Specific Control Register */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */ +#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO Register */ +#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */ +#define IGP02E1000_PHY_POWER_MGMT 0x19 +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* PHY Page Select Core Register */ + +/* IGP01E1000 AGC Registers - stores the cable length values*/ +#define IGP01E1000_PHY_AGC_A 0x1172 +#define IGP01E1000_PHY_AGC_B 0x1272 +#define IGP01E1000_PHY_AGC_C 0x1472 +#define IGP01E1000_PHY_AGC_D 0x1872 + +/* IGP01E1000 Specific Port Config Register - R/W */ +#define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT 0x0010 +#define IGP01E1000_PSCFR_PRE_EN 0x0020 +#define IGP01E1000_PSCFR_SMART_SPEED 0x0080 +#define IGP01E1000_PSCFR_DISABLE_TPLOOPBACK 0x0100 +#define IGP01E1000_PSCFR_DISABLE_JABBER 0x0400 +#define IGP01E1000_PSCFR_DISABLE_TRANSMIT 0x2000 +/* IGP02E1000 AGC Registers for cable length values */ +#define IGP02E1000_PHY_AGC_A 0x11B1 +#define IGP02E1000_PHY_AGC_B 0x12B1 +#define IGP02E1000_PHY_AGC_C 0x14B1 +#define IGP02E1000_PHY_AGC_D 0x18B1 + +#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */ +#define IGP02E1000_PM_D3_LPLU 0x0004 /* Enable LPLU in + non-D0a modes */ +#define IGP02E1000_PM_D0_LPLU 0x0002 /* Enable LPLU in + D0a mode */ + +/* IGP01E1000 DSP Reset Register */ +#define IGP01E1000_PHY_DSP_RESET 0x1F33 +#define IGP01E1000_PHY_DSP_SET 0x1F71 +#define IGP01E1000_PHY_DSP_FFE 0x1F35 + +#define IGP01E1000_PHY_CHANNEL_NUM 4 +#define IGP02E1000_PHY_CHANNEL_NUM 4 + +#define IGP01E1000_PHY_AGC_PARAM_A 0x1171 +#define IGP01E1000_PHY_AGC_PARAM_B 0x1271 +#define IGP01E1000_PHY_AGC_PARAM_C 0x1471 +#define IGP01E1000_PHY_AGC_PARAM_D 0x1871 + +#define IGP01E1000_PHY_EDAC_MU_INDEX 0xC000 +#define IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS 0x8000 + +#define IGP01E1000_PHY_ANALOG_TX_STATE 0x2890 +#define IGP01E1000_PHY_ANALOG_CLASS_A 0x2000 +#define IGP01E1000_PHY_FORCE_ANALOG_ENABLE 0x0004 +#define IGP01E1000_PHY_DSP_FFE_CM_CP 0x0069 + +#define IGP01E1000_PHY_DSP_FFE_DEFAULT 0x002A +/* IGP01E1000 PCS Initialization register - stores the polarity status when + * speed = 1000 Mbps. */ +#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4 +#define IGP01E1000_PHY_PCS_CTRL_REG 0x00B5 + +#define IGP01E1000_ANALOG_REGS_PAGE 0x20C0 + +/* IGP01E1000 GMII FIFO Register */ +#define IGP01E1000_GMII_FLEX_SPD 0x10 /* Enable flexible speed + * on Link-Up */ +#define IGP01E1000_GMII_SPD 0x20 /* Enable SPD */ + +/* IGP01E1000 Analog Register */ +#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS 0x20D1 +#define IGP01E1000_ANALOG_FUSE_STATUS 0x20D0 +#define IGP01E1000_ANALOG_FUSE_CONTROL 0x20DC +#define IGP01E1000_ANALOG_FUSE_BYPASS 0x20DE + +#define IGP01E1000_ANALOG_FUSE_POLY_MASK 0xF000 +#define IGP01E1000_ANALOG_FUSE_FINE_MASK 0x0F80 +#define IGP01E1000_ANALOG_FUSE_COARSE_MASK 0x0070 +#define IGP01E1000_ANALOG_SPARE_FUSE_ENABLED 0x0100 +#define IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL 0x0002 + +#define IGP01E1000_ANALOG_FUSE_COARSE_THRESH 0x0040 +#define IGP01E1000_ANALOG_FUSE_COARSE_10 0x0010 +#define IGP01E1000_ANALOG_FUSE_FINE_1 0x0080 +#define IGP01E1000_ANALOG_FUSE_FINE_10 0x0500 + +/* IGP01E1000 Specific Port Control Register - R/W */ +#define IGP01E1000_PSCR_TP_LOOPBACK 0x0010 +#define IGP01E1000_PSCR_CORRECT_NC_SCMBLR 0x0200 +#define IGP01E1000_PSCR_TEN_CRS_SELECT 0x0400 +#define IGP01E1000_PSCR_FLIP_CHIP 0x0800 +#define IGP01E1000_PSCR_AUTO_MDIX 0x1000 +#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0-MDI, 1-MDIX */ +/* GG82563 PHY Specific Status Register (Page 0, Register 16 */ +#define GG82563_PSCR_DISABLE_JABBER 0x0001 /* 1=Disable Jabber */ +#define GG82563_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Polarity Reversal + Disabled */ +#define GG82563_PSCR_POWER_DOWN 0x0004 /* 1=Power Down */ +#define GG82563_PSCR_COPPER_TRANSMITER_DISABLE 0x0008 /* 1=Transmitter + Disabled */ +#define GG82563_PSCR_CROSSOVER_MODE_MASK 0x0060 +#define GG82563_PSCR_CROSSOVER_MODE_MDI 0x0000 /* 00=Manual MDI + configuration */ +#define GG82563_PSCR_CROSSOVER_MODE_MDIX 0x0020 /* 01=Manual MDIX + configuration */ +#define GG82563_PSCR_CROSSOVER_MODE_AUTO 0x0060 /* 11=Automatic + crossover */ +#define GG82563_PSCR_ENALBE_EXTENDED_DISTANCE 0x0080 /* 1=Enable Extended + Distance */ +#define GG82563_PSCR_ENERGY_DETECT_MASK 0x0300 +#define GG82563_PSCR_ENERGY_DETECT_OFF 0x0000 /* 00,01=Off */ +#define GG82563_PSCR_ENERGY_DETECT_RX 0x0200 /* 10=Sense on Rx only + (Energy Detect) */ +#define GG82563_PSCR_ENERGY_DETECT_RX_TM 0x0300 /* 11=Sense and Tx NLP */ +#define GG82563_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force Link Good */ +#define GG82563_PSCR_DOWNSHIFT_ENABLE 0x0800 /* 1=Enable Downshift */ +#define GG82563_PSCR_DOWNSHIFT_COUNTER_MASK 0x7000 +#define GG82563_PSCR_DOWNSHIFT_COUNTER_SHIFT 12 + +/* PHY Specific Status Register (Page 0, Register 17) */ +#define GG82563_PSSR_JABBER 0x0001 /* 1=Jabber */ +#define GG82563_PSSR_POLARITY 0x0002 /* 1=Polarity Reversed */ +#define GG82563_PSSR_LINK 0x0008 /* 1=Link is Up */ +#define GG82563_PSSR_ENERGY_DETECT 0x0010 /* 1=Sleep, 0=Active */ +#define GG82563_PSSR_DOWNSHIFT 0x0020 /* 1=Downshift */ +#define GG82563_PSSR_CROSSOVER_STATUS 0x0040 /* 1=MDIX, 0=MDI */ +#define GG82563_PSSR_RX_PAUSE_ENABLED 0x0100 /* 1=Receive Pause Enabled */ +#define GG82563_PSSR_TX_PAUSE_ENABLED 0x0200 /* 1=Transmit Pause Enabled */ +#define GG82563_PSSR_LINK_UP 0x0400 /* 1=Link Up */ +#define GG82563_PSSR_SPEED_DUPLEX_RESOLVED 0x0800 /* 1=Resolved */ +#define GG82563_PSSR_PAGE_RECEIVED 0x1000 /* 1=Page Received */ +#define GG82563_PSSR_DUPLEX 0x2000 /* 1-Full-Duplex */ +#define GG82563_PSSR_SPEED_MASK 0xC000 +#define GG82563_PSSR_SPEED_10MBPS 0x0000 /* 00=10Mbps */ +#define GG82563_PSSR_SPEED_100MBPS 0x4000 /* 01=100Mbps */ +#define GG82563_PSSR_SPEED_1000MBPS 0x8000 /* 10=1000Mbps */ + +/* PHY Specific Status Register 2 (Page 0, Register 19) */ +#define GG82563_PSSR2_JABBER 0x0001 /* 1=Jabber */ +#define GG82563_PSSR2_POLARITY_CHANGED 0x0002 /* 1=Polarity Changed */ +#define GG82563_PSSR2_ENERGY_DETECT_CHANGED 0x0010 /* 1=Energy Detect Changed */ +#define GG82563_PSSR2_DOWNSHIFT_INTERRUPT 0x0020 /* 1=Downshift Detected */ +#define GG82563_PSSR2_MDI_CROSSOVER_CHANGE 0x0040 /* 1=Crossover Changed */ +#define GG82563_PSSR2_FALSE_CARRIER 0x0100 /* 1=false Carrier */ +#define GG82563_PSSR2_SYMBOL_ERROR 0x0200 /* 1=Symbol Error */ +#define GG82563_PSSR2_LINK_STATUS_CHANGED 0x0400 /* 1=Link Status Changed */ +#define GG82563_PSSR2_AUTO_NEG_COMPLETED 0x0800 /* 1=Auto-Neg Completed */ +#define GG82563_PSSR2_PAGE_RECEIVED 0x1000 /* 1=Page Received */ +#define GG82563_PSSR2_DUPLEX_CHANGED 0x2000 /* 1=Duplex Changed */ +#define GG82563_PSSR2_SPEED_CHANGED 0x4000 /* 1=Speed Changed */ +#define GG82563_PSSR2_AUTO_NEG_ERROR 0x8000 /* 1=Auto-Neg Error */ + +/* PHY Specific Control Register 2 (Page 0, Register 26) */ +#define GG82563_PSCR2_10BT_POLARITY_FORCE 0x0002 /* 1=Force Negative + Polarity */ +#define GG82563_PSCR2_1000MB_TEST_SELECT_MASK 0x000C +#define GG82563_PSCR2_1000MB_TEST_SELECT_NORMAL 0x0000 /* 00,01=Normal + Operation */ +#define GG82563_PSCR2_1000MB_TEST_SELECT_112NS 0x0008 /* 10=Select 112ns + Sequence */ +#define GG82563_PSCR2_1000MB_TEST_SELECT_16NS 0x000C /* 11=Select 16ns + Sequence */ +#define GG82563_PSCR2_REVERSE_AUTO_NEG 0x2000 /* 1=Reverse + Auto-Negotiation */ +#define GG82563_PSCR2_1000BT_DISABLE 0x4000 /* 1=Disable + 1000BASE-T */ +#define GG82563_PSCR2_TRANSMITER_TYPE_MASK 0x8000 +#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_B 0x0000 /* 0=Class B */ +#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_A 0x8000 /* 1=Class A */ + +/* MAC Specific Control Register (Page 2, Register 21) */ +/* Tx clock speed for Link Down and 1000BASE-T for the following speeds */ +#define GG82563_MSCR_TX_CLK_MASK 0x0007 +#define GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ 0x0004 +#define GG82563_MSCR_TX_CLK_100MBPS_25MHZ 0x0005 +#define GG82563_MSCR_TX_CLK_1000MBPS_2_5MHZ 0x0006 +#define GG82563_MSCR_TX_CLK_1000MBPS_25MHZ 0x0007 + +#define GG82563_MSCR_ASSERT_CRS_ON_TX 0x0010 /* 1=Assert */ + +/* DSP Distance Register (Page 5, Register 26) */ +#define GG82563_DSPD_CABLE_LENGTH 0x0007 /* 0 = <50M; + 1 = 50-80M; + 2 = 80-110M; + 3 = 110-140M; + 4 = >140M */ + +/* Kumeran Mode Control Register (Page 193, Register 16) */ +#define GG82563_KMCR_PHY_LEDS_EN 0x0020 /* 1=PHY LEDs, + 0=Kumeran Inband LEDs */ +#define GG82563_KMCR_FORCE_LINK_UP 0x0040 /* 1=Force Link Up */ +#define GG82563_KMCR_SUPPRESS_SGMII_EPD_EXT 0x0080 +#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT_MASK 0x0400 +#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT 0x0400 /* 1=6.25MHz, + 0=0.8MHz */ +#define GG82563_KMCR_PASS_FALSE_CARRIER 0x0800 + +/* Power Management Control Register (Page 193, Register 20) */ +#define GG82563_PMCR_ENABLE_ELECTRICAL_IDLE 0x0001 /* 1=Enalbe SERDES + Electrical Idle */ +#define GG82563_PMCR_DISABLE_PORT 0x0002 /* 1=Disable Port */ +#define GG82563_PMCR_DISABLE_SERDES 0x0004 /* 1=Disable SERDES */ +#define GG82563_PMCR_REVERSE_AUTO_NEG 0x0008 /* 1=Enable Reverse + Auto-Negotiation */ +#define GG82563_PMCR_DISABLE_1000_NON_D0 0x0010 /* 1=Disable 1000Mbps + Auto-Neg in non D0 */ +#define GG82563_PMCR_DISABLE_1000 0x0020 /* 1=Disable 1000Mbps + Auto-Neg Always */ +#define GG82563_PMCR_REVERSE_AUTO_NEG_D0A 0x0040 /* 1=Enable D0a + Reverse Auto-Negotiation */ +#define GG82563_PMCR_FORCE_POWER_STATE 0x0080 /* 1=Force Power State */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_MASK 0x0300 +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_DR 0x0000 /* 00=Dr */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0U 0x0100 /* 01=D0u */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0A 0x0200 /* 10=D0a */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D3 0x0300 /* 11=D3 */ + +/* In-Band Control Register (Page 194, Register 18) */ +#define GG82563_ICR_DIS_PADDING 0x0010 /* Disable Padding Use */ + + +/* Bits... + * 15-5: page + * 4-0: register offset + */ +#define GG82563_PAGE_SHIFT 5 +#define GG82563_REG(page, reg) \ + (((page) << GG82563_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) +#define GG82563_MIN_ALT_REG 30 + +/* GG82563 Specific Registers */ +#define GG82563_PHY_SPEC_CTRL \ + GG82563_REG(0, 16) /* PHY Specific Control */ +#define GG82563_PHY_SPEC_STATUS \ + GG82563_REG(0, 17) /* PHY Specific Status */ +#define GG82563_PHY_INT_ENABLE \ + GG82563_REG(0, 18) /* Interrupt Enable */ +#define GG82563_PHY_SPEC_STATUS_2 \ + GG82563_REG(0, 19) /* PHY Specific Status 2 */ +#define GG82563_PHY_RX_ERR_CNTR \ + GG82563_REG(0, 21) /* Receive Error Counter */ +#define GG82563_PHY_PAGE_SELECT \ + GG82563_REG(0, 22) /* Page Select */ +#define GG82563_PHY_SPEC_CTRL_2 \ + GG82563_REG(0, 26) /* PHY Specific Control 2 */ +#define GG82563_PHY_PAGE_SELECT_ALT \ + GG82563_REG(0, 29) /* Alternate Page Select */ +#define GG82563_PHY_TEST_CLK_CTRL \ + GG82563_REG(0, 30) /* Test Clock Control (use reg. 29 to select) */ + +#define GG82563_PHY_MAC_SPEC_CTRL \ + GG82563_REG(2, 21) /* MAC Specific Control Register */ +#define GG82563_PHY_MAC_SPEC_CTRL_2 \ + GG82563_REG(2, 26) /* MAC Specific Control 2 */ + +#define GG82563_PHY_DSP_DISTANCE \ + GG82563_REG(5, 26) /* DSP Distance */ + +/* Page 193 - Port Control Registers */ +#define GG82563_PHY_KMRN_MODE_CTRL \ + GG82563_REG(193, 16) /* Kumeran Mode Control */ +#define GG82563_PHY_PORT_RESET \ + GG82563_REG(193, 17) /* Port Reset */ +#define GG82563_PHY_REVISION_ID \ + GG82563_REG(193, 18) /* Revision ID */ +#define GG82563_PHY_DEVICE_ID \ + GG82563_REG(193, 19) /* Device ID */ +#define GG82563_PHY_PWR_MGMT_CTRL \ + GG82563_REG(193, 20) /* Power Management Control */ +#define GG82563_PHY_RATE_ADAPT_CTRL \ + GG82563_REG(193, 25) /* Rate Adaptation Control */ + +/* Page 194 - KMRN Registers */ +#define GG82563_PHY_KMRN_FIFO_CTRL_STAT \ + GG82563_REG(194, 16) /* FIFO's Control/Status */ +#define GG82563_PHY_KMRN_CTRL \ + GG82563_REG(194, 17) /* Control */ +#define GG82563_PHY_INBAND_CTRL \ + GG82563_REG(194, 18) /* Inband Control */ +#define GG82563_PHY_KMRN_DIAGNOSTIC \ + GG82563_REG(194, 19) /* Diagnostic */ +#define GG82563_PHY_ACK_TIMEOUTS \ + GG82563_REG(194, 20) /* Acknowledge Timeouts */ +#define GG82563_PHY_ADV_ABILITY \ + GG82563_REG(194, 21) /* Advertised Ability */ +#define GG82563_PHY_LINK_PARTNER_ADV_ABILITY \ + GG82563_REG(194, 23) /* Link Partner Advertised Ability */ +#define GG82563_PHY_ADV_NEXT_PAGE \ + GG82563_REG(194, 24) /* Advertised Next Page */ +#define GG82563_PHY_LINK_PARTNER_ADV_NEXT_PAGE \ + GG82563_REG(194, 25) /* Link Partner Advertised Next page */ +#define GG82563_PHY_KMRN_MISC \ + GG82563_REG(194, 26) /* Misc. */ + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0100 /* LP is 100TX Full Duplex Capable */ + +/* Next Page TX Register */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* Link Partner Next Page Register */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* 1000BASE-T Control Register */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 + +/* Extended Status Register */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ + +/* M88E1000 PHY Specific Control Register */ +#define M88E1000_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ +#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ +#define M88E1000_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ +#define M88E1000_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, + * 0=CLK125 toggling + */ +#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */ + /* Manual MDI configuration */ +#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +#define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, + * 100BASE-TX/10BASE-T: + * MDI Mode + */ +#define M88E1000_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled + * all speeds. + */ +#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE 0x0080 + /* 1=Enable Extended 10BASE-T distance + * (Lower 10BASE-T RX Threshold) + * 0=Normal 10BASE-T RX Threshold */ +#define M88E1000_PSCR_MII_5BIT_ENABLE 0x0100 + /* 1=5-Bit interface in 100BASE-TX + * 0=MII interface in 100BASE-TX */ +#define M88E1000_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ +#define M88E1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ +#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ + +#define M88E1000_PSCR_POLARITY_REVERSAL_SHIFT 1 +#define M88E1000_PSCR_AUTO_X_MODE_SHIFT 5 +#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 + +/* M88E1000 PHY Specific Status Register */ +#define M88E1000_PSSR_JABBER 0x0001 /* 1=Jabber */ +#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */ +#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */ +#define M88E1000_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M; + * 3=110-140M;4=>140M */ +#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */ +#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define M88E1000_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */ +#define M88E1000_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define M88E1000_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define M88E1000_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +#define M88E1000_PSSR_REV_POLARITY_SHIFT 1 +#define M88E1000_PSSR_MDIX_SHIFT 6 +#define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7 + +/* M88E1000 Extended PHY Specific Control Register */ +#define M88E1000_EPSCR_FIBER_LOOPBACK 0x4000 /* 1=Fiber loopback */ +#define M88E1000_EPSCR_DOWN_NO_IDLE 0x8000 /* 1=Lost lock detect enabled. + * Will assert lost lock and bring + * link down if idle not seen + * within 1ms in 1000BASE-T + */ +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the master */ +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X 0x0000 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_2X 0x0400 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_3X 0x0800 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_4X 0x0C00 +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the slave */ +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_DIS 0x0000 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_2X 0x0200 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_3X 0x0300 +#define M88E1000_EPSCR_TX_CLK_2_5 0x0060 /* 2.5 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */ + +/* Bit definitions for valid PHY IDs. */ +#define M88E1000_E_PHY_ID 0x01410C50 +#define M88E1000_I_PHY_ID 0x01410C30 +#define M88E1011_I_PHY_ID 0x01410C20 +#define M88E1000_12_PHY_ID M88E1000_E_PHY_ID +#define M88E1000_14_PHY_ID M88E1000_E_PHY_ID +#define IGP01E1000_I_PHY_ID 0x02A80380 +#define M88E1011_I_REV_4 0x04 +#define M88E1111_I_PHY_ID 0x01410CC0 +#define L1LXT971A_PHY_ID 0x001378E0 +#define GG82563_E_PHY_ID 0x01410CA0 + +#define BME1000_E_PHY_ID 0x01410CB0 + +/* Miscellaneous PHY bit definitions. */ +#define PHY_PREAMBLE 0xFFFFFFFF +#define PHY_SOF 0x01 +#define PHY_OP_READ 0x02 +#define PHY_OP_WRITE 0x01 +#define PHY_TURNAROUND 0x02 +#define PHY_PREAMBLE_SIZE 32 +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 +#define E1000_PHY_ADDRESS 0x01 +#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ +#define PHY_FORCE_TIME 20 /* 2.0 Seconds */ +#define PHY_REVISION_MASK 0xFFFFFFF0 +#define DEVICE_SPEED_MASK 0x00000300 /* Device Ctrl Reg Speed Mask */ +#define REG4_SPEED_MASK 0x01E0 +#define REG9_SPEED_MASK 0x0300 +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 +#define ADVERTISE_1000_FULL 0x0020 + +#define ICH_FLASH_GFPREG 0x0000 +#define ICH_FLASH_HSFSTS 0x0004 +#define ICH_FLASH_HSFCTL 0x0006 +#define ICH_FLASH_FADDR 0x0008 +#define ICH_FLASH_FDATA0 0x0010 +#define ICH_FLASH_FRACC 0x0050 +#define ICH_FLASH_FREG0 0x0054 +#define ICH_FLASH_FREG1 0x0058 +#define ICH_FLASH_FREG2 0x005C +#define ICH_FLASH_FREG3 0x0060 +#define ICH_FLASH_FPR0 0x0074 +#define ICH_FLASH_FPR1 0x0078 +#define ICH_FLASH_SSFSTS 0x0090 +#define ICH_FLASH_SSFCTL 0x0092 +#define ICH_FLASH_PREOP 0x0094 +#define ICH_FLASH_OPTYPE 0x0096 +#define ICH_FLASH_OPMENU 0x0098 + +#define ICH_FLASH_REG_MAPSIZE 0x00A0 +#define ICH_FLASH_SECTOR_SIZE 4096 +#define ICH_GFPREG_BASE_MASK 0x1FFF +#define ICH_FLASH_LINEAR_ADDR_MASK 0x00FFFFFF + +#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ + +/* SPI EEPROM Status Register */ +#define EEPROM_STATUS_RDY_SPI 0x01 +#define EEPROM_STATUS_WEN_SPI 0x02 +#define EEPROM_STATUS_BP0_SPI 0x04 +#define EEPROM_STATUS_BP1_SPI 0x08 +#define EEPROM_STATUS_WPEN_SPI 0x80 + +/* SW Semaphore Register */ +#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ +#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ +#define E1000_SWSM_WMNG 0x00000004 /* Wake MNG Clock */ +#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */ + +/* FW Semaphore Register */ +#define E1000_FWSM_MODE_MASK 0x0000000E /* FW mode */ +#define E1000_FWSM_MODE_SHIFT 1 +#define E1000_FWSM_FW_VALID 0x00008000 /* FW established a valid mode */ + +#define E1000_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI reset */ +#define E1000_FWSM_DISSW 0x10000000 /* FW disable SW Write Access */ +#define E1000_FWSM_SKUSEL_MASK 0x60000000 /* LAN SKU select */ +#define E1000_FWSM_SKUEL_SHIFT 29 +#define E1000_FWSM_SKUSEL_EMB 0x0 /* Embedded SKU */ +#define E1000_FWSM_SKUSEL_CONS 0x1 /* Consumer SKU */ +#define E1000_FWSM_SKUSEL_PERF_100 0x2 /* Perf & Corp 10/100 SKU */ +#define E1000_FWSM_SKUSEL_PERF_GBE 0x3 /* Perf & Copr GbE SKU */ + +#define E1000_GCR 0x05B00 /* PCI-Ex Control */ +#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ +#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ +#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ +#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ +#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ +#define E1000_SWSM 0x05B50 /* SW Semaphore */ +#define E1000_FWSM 0x05B54 /* FW Semaphore */ +#define E1000_FFLT_DBG 0x05F04 /* Debug Register */ +#define E1000_HICR 0x08F00 /* Host Inteface Control */ + +#define IGP_ACTIVITY_LED_MASK 0xFFFFF0FF +#define IGP_ACTIVITY_LED_ENABLE 0x0300 +#define IGP_LED3_MODE 0x07000000 + +/* Mask bit for PHY class in Word 7 of the EEPROM */ +#define EEPROM_PHY_CLASS_A 0x8000 +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ +#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds*/ +#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds*/ + +#define E1000_KUMCTRLSTA_MASK 0x0000FFFF +#define E1000_KUMCTRLSTA_OFFSET 0x001F0000 +#define E1000_KUMCTRLSTA_OFFSET_SHIFT 16 +#define E1000_KUMCTRLSTA_REN 0x00200000 + +#define E1000_KUMCTRLSTA_OFFSET_FIFO_CTRL 0x00000000 +#define E1000_KUMCTRLSTA_OFFSET_CTRL 0x00000001 +#define E1000_KUMCTRLSTA_OFFSET_INB_CTRL 0x00000002 +#define E1000_KUMCTRLSTA_OFFSET_DIAG 0x00000003 +#define E1000_KUMCTRLSTA_OFFSET_TIMEOUTS 0x00000004 +#define E1000_KUMCTRLSTA_OFFSET_INB_PARAM 0x00000009 +#define E1000_KUMCTRLSTA_OFFSET_HD_CTRL 0x00000010 +#define E1000_KUMCTRLSTA_OFFSET_M2P_SERDES 0x0000001E +#define E1000_KUMCTRLSTA_OFFSET_M2P_MODES 0x0000001F + +/* FIFO Control */ +#define E1000_KUMCTRLSTA_FIFO_CTRL_RX_BYPASS 0x00000008 +#define E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS 0x00000800 + +/* In-Band Control */ +#define E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT 0x00000500 +#define E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING 0x00000010 + +/* Half-Duplex Control */ +#define E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT 0x00000004 +#define E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT 0x00000000 + +#define E1000_KUMCTRLSTA_OFFSET_K0S_CTRL 0x0000001E + +#define E1000_KUMCTRLSTA_DIAG_FELPBK 0x2000 +#define E1000_KUMCTRLSTA_DIAG_NELPBK 0x1000 + +#define E1000_KUMCTRLSTA_K0S_100_EN 0x2000 +#define E1000_KUMCTRLSTA_K0S_GBE_EN 0x1000 +#define E1000_KUMCTRLSTA_K0S_ENTRY_LATENCY_MASK 0x0003 + +#define E1000_MNG_ICH_IAMT_MODE 0x2 +#define E1000_MNG_IAMT_MODE 0x3 +#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ +#define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */ +/* Number of milliseconds we wait for PHY configuration done after MAC reset */ +#define PHY_CFG_TIMEOUT 100 +#define DEFAULT_80003ES2LAN_TIPG_IPGT_10_100 0x00000009 +#define DEFAULT_80003ES2LAN_TIPG_IPGT_1000 0x00000008 +#define AUTO_ALL_MODES 0 + +#ifndef E1000_MASTER_SLAVE +/* Switch to override PHY master/slave setting */ +#define E1000_MASTER_SLAVE e1000_ms_hw_default +#endif +/* Extended Transmit Control */ +#define E1000_TCTL_EXT_BST_MASK 0x000003FF /* Backoff Slot Time */ +#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */ + +#define DEFAULT_80003ES2LAN_TCTL_EXT_GCEX 0x00010000 + +#define PCI_EX_82566_SNOOP_ALL PCI_EX_NO_SNOOP_ALL + +#define E1000_GCR_L1_ACT_WITHOUT_L0S_RX 0x08000000 +#define E1000_MC_TBL_SIZE_ICH8LAN 32 + +#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers + after IMS clear */ +#endif /* _E1000_HW_H_ */ diff --git a/qemu/roms/u-boot/drivers/net/e1000_spi.c b/qemu/roms/u-boot/drivers/net/e1000_spi.c new file mode 100644 index 000000000..93043a1ad --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/e1000_spi.c @@ -0,0 +1,577 @@ +#include "e1000.h" +#include + +/*----------------------------------------------------------------------- + * SPI transfer + * + * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks + * "bitlen" bits in the SPI MISO port. That's just the way SPI works. + * + * The source of the outgoing bits is the "dout" parameter and the + * destination of the input bits is the "din" parameter. Note that "dout" + * and "din" can point to the same memory location, in which case the + * input data overwrites the output data (since both are buffered by + * temporary variables, this is OK). + * + * This may be interrupted with Ctrl-C if "intr" is true, otherwise it will + * never return an error. + */ +static int e1000_spi_xfer(struct e1000_hw *hw, unsigned int bitlen, + const void *dout_mem, void *din_mem, bool intr) +{ + const uint8_t *dout = dout_mem; + uint8_t *din = din_mem; + + uint8_t mask = 0; + uint32_t eecd; + unsigned long i; + + /* Pre-read the control register */ + eecd = E1000_READ_REG(hw, EECD); + + /* Iterate over each bit */ + for (i = 0, mask = 0x80; i < bitlen; i++, mask = (mask >> 1)?:0x80) { + /* Check for interrupt */ + if (intr && ctrlc()) + return -1; + + /* Determine the output bit */ + if (dout && dout[i >> 3] & mask) + eecd |= E1000_EECD_DI; + else + eecd &= ~E1000_EECD_DI; + + /* Write the output bit and wait 50us */ + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(50); + + /* Poke the clock (waits 50us) */ + e1000_raise_ee_clk(hw, &eecd); + + /* Now read the input bit */ + eecd = E1000_READ_REG(hw, EECD); + if (din) { + if (eecd & E1000_EECD_DO) + din[i >> 3] |= mask; + else + din[i >> 3] &= ~mask; + } + + /* Poke the clock again (waits 50us) */ + e1000_lower_ee_clk(hw, &eecd); + } + + /* Now clear any remaining bits of the input */ + if (din && (i & 7)) + din[i >> 3] &= ~((mask << 1) - 1); + + return 0; +} + +#ifdef CONFIG_E1000_SPI_GENERIC +static inline struct e1000_hw *e1000_hw_from_spi(struct spi_slave *spi) +{ + return container_of(spi, struct e1000_hw, spi); +} + +/* Not sure why all of these are necessary */ +void spi_init_r(void) { /* Nothing to do */ } +void spi_init_f(void) { /* Nothing to do */ } +void spi_init(void) { /* Nothing to do */ } + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + /* Find the right PCI device */ + struct e1000_hw *hw = e1000_find_card(bus); + if (!hw) { + printf("ERROR: No such e1000 device: e1000#%u\n", bus); + return NULL; + } + + /* Make sure it has an SPI chip */ + if (hw->eeprom.type != e1000_eeprom_spi) { + E1000_ERR(hw->nic, "No attached SPI EEPROM found!\n"); + return NULL; + } + + /* Argument sanity checks */ + if (cs != 0) { + E1000_ERR(hw->nic, "No such SPI chip: %u\n", cs); + return NULL; + } + if (mode != SPI_MODE_0) { + E1000_ERR(hw->nic, "Only SPI MODE-0 is supported!\n"); + return NULL; + } + + /* TODO: Use max_hz somehow */ + E1000_DBG(hw->nic, "EEPROM SPI access requested\n"); + return &hw->spi; +} + +void spi_free_slave(struct spi_slave *spi) +{ + __maybe_unused struct e1000_hw *hw = e1000_hw_from_spi(spi); + E1000_DBG(hw->nic, "EEPROM SPI access released\n"); +} + +int spi_claim_bus(struct spi_slave *spi) +{ + struct e1000_hw *hw = e1000_hw_from_spi(spi); + + if (e1000_acquire_eeprom(hw)) { + E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n"); + return -1; + } + + return 0; +} + +void spi_release_bus(struct spi_slave *spi) +{ + struct e1000_hw *hw = e1000_hw_from_spi(spi); + e1000_release_eeprom(hw); +} + +/* Skinny wrapper around e1000_spi_xfer */ +int spi_xfer(struct spi_slave *spi, unsigned int bitlen, + const void *dout_mem, void *din_mem, unsigned long flags) +{ + struct e1000_hw *hw = e1000_hw_from_spi(spi); + int ret; + + if (flags & SPI_XFER_BEGIN) + e1000_standby_eeprom(hw); + + ret = e1000_spi_xfer(hw, bitlen, dout_mem, din_mem, true); + + if (flags & SPI_XFER_END) + e1000_standby_eeprom(hw); + + return ret; +} + +#endif /* not CONFIG_E1000_SPI_GENERIC */ + +#ifdef CONFIG_CMD_E1000 + +/* The EEPROM opcodes */ +#define SPI_EEPROM_ENABLE_WR 0x06 +#define SPI_EEPROM_DISABLE_WR 0x04 +#define SPI_EEPROM_WRITE_STATUS 0x01 +#define SPI_EEPROM_READ_STATUS 0x05 +#define SPI_EEPROM_WRITE_PAGE 0x02 +#define SPI_EEPROM_READ_PAGE 0x03 + +/* The EEPROM status bits */ +#define SPI_EEPROM_STATUS_BUSY 0x01 +#define SPI_EEPROM_STATUS_WREN 0x02 + +static int e1000_spi_eeprom_enable_wr(struct e1000_hw *hw, bool intr) +{ + u8 op[] = { SPI_EEPROM_ENABLE_WR }; + e1000_standby_eeprom(hw); + return e1000_spi_xfer(hw, 8*sizeof(op), op, NULL, intr); +} + +/* + * These have been tested to perform correctly, but they are not used by any + * of the EEPROM commands at this time. + */ +#if 0 +static int e1000_spi_eeprom_disable_wr(struct e1000_hw *hw, bool intr) +{ + u8 op[] = { SPI_EEPROM_DISABLE_WR }; + e1000_standby_eeprom(hw); + return e1000_spi_xfer(hw, 8*sizeof(op), op, NULL, intr); +} + +static int e1000_spi_eeprom_write_status(struct e1000_hw *hw, + u8 status, bool intr) +{ + u8 op[] = { SPI_EEPROM_WRITE_STATUS, status }; + e1000_standby_eeprom(hw); + return e1000_spi_xfer(hw, 8*sizeof(op), op, NULL, intr); +} +#endif + +static int e1000_spi_eeprom_read_status(struct e1000_hw *hw, bool intr) +{ + u8 op[] = { SPI_EEPROM_READ_STATUS, 0 }; + e1000_standby_eeprom(hw); + if (e1000_spi_xfer(hw, 8*sizeof(op), op, op, intr)) + return -1; + return op[1]; +} + +static int e1000_spi_eeprom_write_page(struct e1000_hw *hw, + const void *data, u16 off, u16 len, bool intr) +{ + u8 op[] = { + SPI_EEPROM_WRITE_PAGE, + (off >> (hw->eeprom.address_bits - 8)) & 0xff, off & 0xff + }; + + e1000_standby_eeprom(hw); + + if (e1000_spi_xfer(hw, 8 + hw->eeprom.address_bits, op, NULL, intr)) + return -1; + if (e1000_spi_xfer(hw, len << 3, data, NULL, intr)) + return -1; + + return 0; +} + +static int e1000_spi_eeprom_read_page(struct e1000_hw *hw, + void *data, u16 off, u16 len, bool intr) +{ + u8 op[] = { + SPI_EEPROM_READ_PAGE, + (off >> (hw->eeprom.address_bits - 8)) & 0xff, off & 0xff + }; + + e1000_standby_eeprom(hw); + + if (e1000_spi_xfer(hw, 8 + hw->eeprom.address_bits, op, NULL, intr)) + return -1; + if (e1000_spi_xfer(hw, len << 3, NULL, data, intr)) + return -1; + + return 0; +} + +static int e1000_spi_eeprom_poll_ready(struct e1000_hw *hw, bool intr) +{ + int status; + while ((status = e1000_spi_eeprom_read_status(hw, intr)) >= 0) { + if (!(status & SPI_EEPROM_STATUS_BUSY)) + return 0; + } + return -1; +} + +static int e1000_spi_eeprom_dump(struct e1000_hw *hw, + void *data, u16 off, unsigned int len, bool intr) +{ + /* Interruptibly wait for the EEPROM to be ready */ + if (e1000_spi_eeprom_poll_ready(hw, intr)) + return -1; + + /* Dump each page in sequence */ + while (len) { + /* Calculate the data bytes on this page */ + u16 pg_off = off & (hw->eeprom.page_size - 1); + u16 pg_len = hw->eeprom.page_size - pg_off; + if (pg_len > len) + pg_len = len; + + /* Now dump the page */ + if (e1000_spi_eeprom_read_page(hw, data, off, pg_len, intr)) + return -1; + + /* Otherwise go on to the next page */ + len -= pg_len; + off += pg_len; + data += pg_len; + } + + /* We're done! */ + return 0; +} + +static int e1000_spi_eeprom_program(struct e1000_hw *hw, + const void *data, u16 off, u16 len, bool intr) +{ + /* Program each page in sequence */ + while (len) { + /* Calculate the data bytes on this page */ + u16 pg_off = off & (hw->eeprom.page_size - 1); + u16 pg_len = hw->eeprom.page_size - pg_off; + if (pg_len > len) + pg_len = len; + + /* Interruptibly wait for the EEPROM to be ready */ + if (e1000_spi_eeprom_poll_ready(hw, intr)) + return -1; + + /* Enable write access */ + if (e1000_spi_eeprom_enable_wr(hw, intr)) + return -1; + + /* Now program the page */ + if (e1000_spi_eeprom_write_page(hw, data, off, pg_len, intr)) + return -1; + + /* Otherwise go on to the next page */ + len -= pg_len; + off += pg_len; + data += pg_len; + } + + /* Wait for the last write to complete */ + if (e1000_spi_eeprom_poll_ready(hw, intr)) + return -1; + + /* We're done! */ + return 0; +} + +static int do_e1000_spi_show(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char * const argv[]) +{ + unsigned int length = 0; + u16 i, offset = 0; + u8 *buffer; + int err; + + if (argc > 2) { + cmd_usage(cmdtp); + return 1; + } + + /* Parse the offset and length */ + if (argc >= 1) + offset = simple_strtoul(argv[0], NULL, 0); + if (argc == 2) + length = simple_strtoul(argv[1], NULL, 0); + else if (offset < (hw->eeprom.word_size << 1)) + length = (hw->eeprom.word_size << 1) - offset; + + /* Extra sanity checks */ + if (!length) { + E1000_ERR(hw->nic, "Requested zero-sized dump!\n"); + return 1; + } + if ((0x10000 < length) || (0x10000 - length < offset)) { + E1000_ERR(hw->nic, "Can't dump past 0xFFFF!\n"); + return 1; + } + + /* Allocate a buffer to hold stuff */ + buffer = malloc(length); + if (!buffer) { + E1000_ERR(hw->nic, "Out of Memory!\n"); + return 1; + } + + /* Acquire the EEPROM and perform the dump */ + if (e1000_acquire_eeprom(hw)) { + E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n"); + free(buffer); + return 1; + } + err = e1000_spi_eeprom_dump(hw, buffer, offset, length, true); + e1000_release_eeprom(hw); + if (err) { + E1000_ERR(hw->nic, "Interrupted!\n"); + free(buffer); + return 1; + } + + /* Now hexdump the result */ + printf("%s: ===== Intel e1000 EEPROM (0x%04hX - 0x%04hX) =====", + hw->nic->name, offset, offset + length - 1); + for (i = 0; i < length; i++) { + if ((i & 0xF) == 0) + printf("\n%s: %04hX: ", hw->nic->name, offset + i); + else if ((i & 0xF) == 0x8) + printf(" "); + printf(" %02hx", buffer[i]); + } + printf("\n"); + + /* Success! */ + free(buffer); + return 0; +} + +static int do_e1000_spi_dump(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char * const argv[]) +{ + unsigned int length; + u16 offset; + void *dest; + + if (argc != 3) { + cmd_usage(cmdtp); + return 1; + } + + /* Parse the arguments */ + dest = (void *)simple_strtoul(argv[0], NULL, 16); + offset = simple_strtoul(argv[1], NULL, 0); + length = simple_strtoul(argv[2], NULL, 0); + + /* Extra sanity checks */ + if (!length) { + E1000_ERR(hw->nic, "Requested zero-sized dump!\n"); + return 1; + } + if ((0x10000 < length) || (0x10000 - length < offset)) { + E1000_ERR(hw->nic, "Can't dump past 0xFFFF!\n"); + return 1; + } + + /* Acquire the EEPROM */ + if (e1000_acquire_eeprom(hw)) { + E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n"); + return 1; + } + + /* Perform the programming operation */ + if (e1000_spi_eeprom_dump(hw, dest, offset, length, true) < 0) { + E1000_ERR(hw->nic, "Interrupted!\n"); + e1000_release_eeprom(hw); + return 1; + } + + e1000_release_eeprom(hw); + printf("%s: ===== EEPROM DUMP COMPLETE =====\n", hw->nic->name); + return 0; +} + +static int do_e1000_spi_program(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char * const argv[]) +{ + unsigned int length; + const void *source; + u16 offset; + + if (argc != 3) { + cmd_usage(cmdtp); + return 1; + } + + /* Parse the arguments */ + source = (const void *)simple_strtoul(argv[0], NULL, 16); + offset = simple_strtoul(argv[1], NULL, 0); + length = simple_strtoul(argv[2], NULL, 0); + + /* Acquire the EEPROM */ + if (e1000_acquire_eeprom(hw)) { + E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n"); + return 1; + } + + /* Perform the programming operation */ + if (e1000_spi_eeprom_program(hw, source, offset, length, true) < 0) { + E1000_ERR(hw->nic, "Interrupted!\n"); + e1000_release_eeprom(hw); + return 1; + } + + e1000_release_eeprom(hw); + printf("%s: ===== EEPROM PROGRAMMED =====\n", hw->nic->name); + return 0; +} + +static int do_e1000_spi_checksum(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char * const argv[]) +{ + uint16_t i, length, checksum = 0, checksum_reg; + uint16_t *buffer; + bool upd; + + if (argc == 0) + upd = 0; + else if ((argc == 1) && !strcmp(argv[0], "update")) + upd = 1; + else { + cmd_usage(cmdtp); + return 1; + } + + /* Allocate a temporary buffer */ + length = sizeof(uint16_t) * (EEPROM_CHECKSUM_REG + 1); + buffer = malloc(length); + if (!buffer) { + E1000_ERR(hw->nic, "Unable to allocate EEPROM buffer!\n"); + return 1; + } + + /* Acquire the EEPROM */ + if (e1000_acquire_eeprom(hw)) { + E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n"); + return 1; + } + + /* Read the EEPROM */ + if (e1000_spi_eeprom_dump(hw, buffer, 0, length, true) < 0) { + E1000_ERR(hw->nic, "Interrupted!\n"); + e1000_release_eeprom(hw); + return 1; + } + + /* Compute the checksum and read the expected value */ + for (i = 0; i < EEPROM_CHECKSUM_REG; i++) + checksum += le16_to_cpu(buffer[i]); + checksum = ((uint16_t)EEPROM_SUM) - checksum; + checksum_reg = le16_to_cpu(buffer[i]); + + /* Verify it! */ + if (checksum_reg == checksum) { + printf("%s: INFO: EEPROM checksum is correct! (0x%04hx)\n", + hw->nic->name, checksum); + e1000_release_eeprom(hw); + return 0; + } + + /* Hrm, verification failed, print an error */ + E1000_ERR(hw->nic, "EEPROM checksum is incorrect!\n"); + E1000_ERR(hw->nic, " ...register was 0x%04hx, calculated 0x%04hx\n", + checksum_reg, checksum); + + /* If they didn't ask us to update it, just return an error */ + if (!upd) { + e1000_release_eeprom(hw); + return 1; + } + + /* Ok, correct it! */ + printf("%s: Reprogramming the EEPROM checksum...\n", hw->nic->name); + buffer[i] = cpu_to_le16(checksum); + if (e1000_spi_eeprom_program(hw, &buffer[i], i * sizeof(uint16_t), + sizeof(uint16_t), true)) { + E1000_ERR(hw->nic, "Interrupted!\n"); + e1000_release_eeprom(hw); + return 1; + } + + e1000_release_eeprom(hw); + return 0; +} + +int do_e1000_spi(cmd_tbl_t *cmdtp, struct e1000_hw *hw, + int argc, char * const argv[]) +{ + if (argc < 1) { + cmd_usage(cmdtp); + return 1; + } + + /* Make sure it has an SPI chip */ + if (hw->eeprom.type != e1000_eeprom_spi) { + E1000_ERR(hw->nic, "No attached SPI EEPROM found!\n"); + return 1; + } + + /* Check the eeprom sub-sub-command arguments */ + if (!strcmp(argv[0], "show")) + return do_e1000_spi_show(cmdtp, hw, argc - 1, argv + 1); + + if (!strcmp(argv[0], "dump")) + return do_e1000_spi_dump(cmdtp, hw, argc - 1, argv + 1); + + if (!strcmp(argv[0], "program")) + return do_e1000_spi_program(cmdtp, hw, argc - 1, argv + 1); + + if (!strcmp(argv[0], "checksum")) + return do_e1000_spi_checksum(cmdtp, hw, argc - 1, argv + 1); + + cmd_usage(cmdtp); + return 1; +} + +#endif /* not CONFIG_CMD_E1000 */ diff --git a/qemu/roms/u-boot/drivers/net/eepro100.c b/qemu/roms/u-boot/drivers/net/eepro100.c new file mode 100644 index 000000000..1e4ea0c89 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/eepro100.c @@ -0,0 +1,931 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + + /* Ethernet chip registers. + */ +#define SCBStatus 0 /* Rx/Command Unit Status *Word* */ +#define SCBIntAckByte 1 /* Rx/Command Unit STAT/ACK byte */ +#define SCBCmd 2 /* Rx/Command Unit Command *Word* */ +#define SCBIntrCtlByte 3 /* Rx/Command Unit Intr.Control Byte */ +#define SCBPointer 4 /* General purpose pointer. */ +#define SCBPort 8 /* Misc. commands and operands. */ +#define SCBflash 12 /* Flash memory control. */ +#define SCBeeprom 14 /* EEPROM memory control. */ +#define SCBCtrlMDI 16 /* MDI interface control. */ +#define SCBEarlyRx 20 /* Early receive byte count. */ +#define SCBGenControl 28 /* 82559 General Control Register */ +#define SCBGenStatus 29 /* 82559 General Status register */ + + /* 82559 SCB status word defnitions + */ +#define SCB_STATUS_CX 0x8000 /* CU finished command (transmit) */ +#define SCB_STATUS_FR 0x4000 /* frame received */ +#define SCB_STATUS_CNA 0x2000 /* CU left active state */ +#define SCB_STATUS_RNR 0x1000 /* receiver left ready state */ +#define SCB_STATUS_MDI 0x0800 /* MDI read/write cycle done */ +#define SCB_STATUS_SWI 0x0400 /* software generated interrupt */ +#define SCB_STATUS_FCP 0x0100 /* flow control pause interrupt */ + +#define SCB_INTACK_MASK 0xFD00 /* all the above */ + +#define SCB_INTACK_TX (SCB_STATUS_CX | SCB_STATUS_CNA) +#define SCB_INTACK_RX (SCB_STATUS_FR | SCB_STATUS_RNR) + + /* System control block commands + */ +/* CU Commands */ +#define CU_NOP 0x0000 +#define CU_START 0x0010 +#define CU_RESUME 0x0020 +#define CU_STATSADDR 0x0040 /* Load Dump Statistics ctrs addr */ +#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */ +#define CU_ADDR_LOAD 0x0060 /* Base address to add to CU commands */ +#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */ + +/* RUC Commands */ +#define RUC_NOP 0x0000 +#define RUC_START 0x0001 +#define RUC_RESUME 0x0002 +#define RUC_ABORT 0x0004 +#define RUC_ADDR_LOAD 0x0006 /* (seems not to clear on acceptance) */ +#define RUC_RESUMENR 0x0007 + +#define CU_CMD_MASK 0x00f0 +#define RU_CMD_MASK 0x0007 + +#define SCB_M 0x0100 /* 0 = enable interrupt, 1 = disable */ +#define SCB_SWI 0x0200 /* 1 - cause device to interrupt */ + +#define CU_STATUS_MASK 0x00C0 +#define RU_STATUS_MASK 0x003C + +#define RU_STATUS_IDLE (0<<2) +#define RU_STATUS_SUS (1<<2) +#define RU_STATUS_NORES (2<<2) +#define RU_STATUS_READY (4<<2) +#define RU_STATUS_NO_RBDS_SUS ((1<<2)|(8<<2)) +#define RU_STATUS_NO_RBDS_NORES ((2<<2)|(8<<2)) +#define RU_STATUS_NO_RBDS_READY ((4<<2)|(8<<2)) + + /* 82559 Port interface commands. + */ +#define I82559_RESET 0x00000000 /* Software reset */ +#define I82559_SELFTEST 0x00000001 /* 82559 Selftest command */ +#define I82559_SELECTIVE_RESET 0x00000002 +#define I82559_DUMP 0x00000003 +#define I82559_DUMP_WAKEUP 0x00000007 + + /* 82559 Eeprom interface. + */ +#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) +#define EE_CMD_BITS 3 +#define EE_DATA_BITS 16 + + /* The EEPROM commands include the alway-set leading bit. + */ +#define EE_EWENB_CMD (4 << addr_len) +#define EE_WRITE_CMD (5 << addr_len) +#define EE_READ_CMD (6 << addr_len) +#define EE_ERASE_CMD (7 << addr_len) + + /* Receive frame descriptors. + */ +struct RxFD { + volatile u16 status; + volatile u16 control; + volatile u32 link; /* struct RxFD * */ + volatile u32 rx_buf_addr; /* void * */ + volatile u32 count; + + volatile u8 data[PKTSIZE_ALIGN]; +}; + +#define RFD_STATUS_C 0x8000 /* completion of received frame */ +#define RFD_STATUS_OK 0x2000 /* frame received with no errors */ + +#define RFD_CONTROL_EL 0x8000 /* 1=last RFD in RFA */ +#define RFD_CONTROL_S 0x4000 /* 1=suspend RU after receiving frame */ +#define RFD_CONTROL_H 0x0010 /* 1=RFD is a header RFD */ +#define RFD_CONTROL_SF 0x0008 /* 0=simplified, 1=flexible mode */ + +#define RFD_COUNT_MASK 0x3fff +#define RFD_COUNT_F 0x4000 +#define RFD_COUNT_EOF 0x8000 + +#define RFD_RX_CRC 0x0800 /* crc error */ +#define RFD_RX_ALIGNMENT 0x0400 /* alignment error */ +#define RFD_RX_RESOURCE 0x0200 /* out of space, no resources */ +#define RFD_RX_DMA_OVER 0x0100 /* DMA overrun */ +#define RFD_RX_SHORT 0x0080 /* short frame error */ +#define RFD_RX_LENGTH 0x0020 +#define RFD_RX_ERROR 0x0010 /* receive error */ +#define RFD_RX_NO_ADR_MATCH 0x0004 /* no address match */ +#define RFD_RX_IA_MATCH 0x0002 /* individual address does not match */ +#define RFD_RX_TCO 0x0001 /* TCO indication */ + + /* Transmit frame descriptors + */ +struct TxFD { /* Transmit frame descriptor set. */ + volatile u16 status; + volatile u16 command; + volatile u32 link; /* void * */ + volatile u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */ + volatile s32 count; + + volatile u32 tx_buf_addr0; /* void *, frame to be transmitted. */ + volatile s32 tx_buf_size0; /* Length of Tx frame. */ + volatile u32 tx_buf_addr1; /* void *, frame to be transmitted. */ + volatile s32 tx_buf_size1; /* Length of Tx frame. */ +}; + +#define TxCB_CMD_TRANSMIT 0x0004 /* transmit command */ +#define TxCB_CMD_SF 0x0008 /* 0=simplified, 1=flexible mode */ +#define TxCB_CMD_NC 0x0010 /* 0=CRC insert by controller */ +#define TxCB_CMD_I 0x2000 /* generate interrupt on completion */ +#define TxCB_CMD_S 0x4000 /* suspend on completion */ +#define TxCB_CMD_EL 0x8000 /* last command block in CBL */ + +#define TxCB_COUNT_MASK 0x3fff +#define TxCB_COUNT_EOF 0x8000 + + /* The Speedo3 Rx and Tx frame/buffer descriptors. + */ +struct descriptor { /* A generic descriptor. */ + volatile u16 status; + volatile u16 command; + volatile u32 link; /* struct descriptor * */ + + unsigned char params[0]; +}; + +#define CONFIG_SYS_CMD_EL 0x8000 +#define CONFIG_SYS_CMD_SUSPEND 0x4000 +#define CONFIG_SYS_CMD_INT 0x2000 +#define CONFIG_SYS_CMD_IAS 0x0001 /* individual address setup */ +#define CONFIG_SYS_CMD_CONFIGURE 0x0002 /* configure */ + +#define CONFIG_SYS_STATUS_C 0x8000 +#define CONFIG_SYS_STATUS_OK 0x2000 + + /* Misc. + */ +#define NUM_RX_DESC PKTBUFSRX +#define NUM_TX_DESC 1 /* Number of TX descriptors */ + +#define TOUT_LOOP 1000000 + +#define ETH_ALEN 6 + +static struct RxFD rx_ring[NUM_RX_DESC]; /* RX descriptor ring */ +static struct TxFD tx_ring[NUM_TX_DESC]; /* TX descriptor ring */ +static int rx_next; /* RX descriptor ring pointer */ +static int tx_next; /* TX descriptor ring pointer */ +static int tx_threshold; + +/* + * The parameters for a CmdConfigure operation. + * There are so many options that it would be difficult to document + * each bit. We mostly use the default or recommended settings. + */ +static const char i82557_config_cmd[] = { + 22, 0x08, 0, 0, 0, 0, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 0, 0x2E, 0, 0x60, 0, + 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */ + 0x3f, 0x05, +}; +static const char i82558_config_cmd[] = { + 22, 0x08, 0, 1, 0, 0, 0x22, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 0, 0x2E, 0, 0x60, 0x08, 0x88, + 0x68, 0, 0x40, 0xf2, 0x84, /* Disable FC */ + 0x31, 0x05, +}; + +static void init_rx_ring (struct eth_device *dev); +static void purge_tx_ring (struct eth_device *dev); + +static void read_hw_addr (struct eth_device *dev, bd_t * bis); + +static int eepro100_init (struct eth_device *dev, bd_t * bis); +static int eepro100_send(struct eth_device *dev, void *packet, int length); +static int eepro100_recv (struct eth_device *dev); +static void eepro100_halt (struct eth_device *dev); + +#if defined(CONFIG_E500) || defined(CONFIG_DB64360) || defined(CONFIG_DB64460) +#define bus_to_phys(a) (a) +#define phys_to_bus(a) (a) +#else +#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) +#endif + +static inline int INW (struct eth_device *dev, u_long addr) +{ + return le16_to_cpu (*(volatile u16 *) (addr + dev->iobase)); +} + +static inline void OUTW (struct eth_device *dev, int command, u_long addr) +{ + *(volatile u16 *) ((addr + dev->iobase)) = cpu_to_le16 (command); +} + +static inline void OUTL (struct eth_device *dev, int command, u_long addr) +{ + *(volatile u32 *) ((addr + dev->iobase)) = cpu_to_le32 (command); +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +static inline int INL (struct eth_device *dev, u_long addr) +{ + return le32_to_cpu (*(volatile u32 *) (addr + dev->iobase)); +} + +static int get_phyreg (struct eth_device *dev, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + int cmd; + int timeout = 50; + + /* read requested data */ + cmd = (2 << 26) | ((addr & 0x1f) << 21) | ((reg & 0x1f) << 16); + OUTL (dev, cmd, SCBCtrlMDI); + + do { + udelay(1000); + cmd = INL (dev, SCBCtrlMDI); + } while (!(cmd & (1 << 28)) && (--timeout)); + + if (timeout == 0) + return -1; + + *value = (unsigned short) (cmd & 0xffff); + + return 0; +} + +static int set_phyreg (struct eth_device *dev, unsigned char addr, + unsigned char reg, unsigned short value) +{ + int cmd; + int timeout = 50; + + /* write requested data */ + cmd = (1 << 26) | ((addr & 0x1f) << 21) | ((reg & 0x1f) << 16); + OUTL (dev, cmd | value, SCBCtrlMDI); + + while (!(INL (dev, SCBCtrlMDI) & (1 << 28)) && (--timeout)) + udelay(1000); + + if (timeout == 0) + return -1; + + return 0; +} + +/* Check if given phyaddr is valid, i.e. there is a PHY connected. + * Do this by checking model value field from ID2 register. + */ +static struct eth_device* verify_phyaddr (const char *devname, + unsigned char addr) +{ + struct eth_device *dev; + unsigned short value; + unsigned char model; + + dev = eth_get_dev_by_name(devname); + if (dev == NULL) { + printf("%s: no such device\n", devname); + return NULL; + } + + /* read id2 register */ + if (get_phyreg(dev, addr, MII_PHYSID2, &value) != 0) { + printf("%s: mii read timeout!\n", devname); + return NULL; + } + + /* get model */ + model = (unsigned char)((value >> 4) & 0x003f); + + if (model == 0) { + printf("%s: no PHY at address %d\n", devname, addr); + return NULL; + } + + return dev; +} + +static int eepro100_miiphy_read(const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + struct eth_device *dev; + + dev = verify_phyaddr(devname, addr); + if (dev == NULL) + return -1; + + if (get_phyreg(dev, addr, reg, value) != 0) { + printf("%s: mii read timeout!\n", devname); + return -1; + } + + return 0; +} + +static int eepro100_miiphy_write(const char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct eth_device *dev; + + dev = verify_phyaddr(devname, addr); + if (dev == NULL) + return -1; + + if (set_phyreg(dev, addr, reg, value) != 0) { + printf("%s: mii write timeout!\n", devname); + return -1; + } + + return 0; +} + +#endif + +/* Wait for the chip get the command. +*/ +static int wait_for_eepro100 (struct eth_device *dev) +{ + int i; + + for (i = 0; INW (dev, SCBCmd) & (CU_CMD_MASK | RU_CMD_MASK); i++) { + if (i >= TOUT_LOOP) { + return 0; + } + } + + return 1; +} + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82557}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559ER}, + {} +}; + +int eepro100_initialize (bd_t * bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase, status; + int idx = 0; + + while (1) { + /* Find PCI device + */ + if ((devno = pci_find_devices (supported, idx++)) < 0) { + break; + } + + pci_read_config_dword (devno, PCI_BASE_ADDRESS_0, &iobase); + iobase &= ~0xf; + +#ifdef DEBUG + printf ("eepro100: Intel i82559 PCI EtherExpressPro @0x%x\n", + iobase); +#endif + + pci_write_config_dword (devno, + PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Check if I/O accesses and Bus Mastering are enabled. + */ + pci_read_config_dword (devno, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_MEMORY)) { + printf ("Error: Can not enable MEM access.\n"); + continue; + } + + if (!(status & PCI_COMMAND_MASTER)) { + printf ("Error: Can not enable Bus Mastering.\n"); + continue; + } + + dev = (struct eth_device *) malloc (sizeof *dev); + if (!dev) { + printf("eepro100: Can not allocate memory\n"); + break; + } + memset(dev, 0, sizeof(*dev)); + + sprintf (dev->name, "i82559#%d", card_number); + dev->priv = (void *) devno; /* this have to come before bus_to_phys() */ + dev->iobase = bus_to_phys (iobase); + dev->init = eepro100_init; + dev->halt = eepro100_halt; + dev->send = eepro100_send; + dev->recv = eepro100_recv; + + eth_register (dev); + +#if defined (CONFIG_MII) || defined(CONFIG_CMD_MII) + /* register mii command access routines */ + miiphy_register(dev->name, + eepro100_miiphy_read, eepro100_miiphy_write); +#endif + + card_number++; + + /* Set the latency timer for value. + */ + pci_write_config_byte (devno, PCI_LATENCY_TIMER, 0x20); + + udelay (10 * 1000); + + read_hw_addr (dev, bis); + } + + return card_number; +} + + +static int eepro100_init (struct eth_device *dev, bd_t * bis) +{ + int i, status = -1; + int tx_cur; + struct descriptor *ias_cmd, *cfg_cmd; + + /* Reset the ethernet controller + */ + OUTL (dev, I82559_SELECTIVE_RESET, SCBPort); + udelay (20); + + OUTL (dev, I82559_RESET, SCBPort); + udelay (20); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + OUTL (dev, 0, SCBPointer); + OUTW (dev, SCB_M | RUC_ADDR_LOAD, SCBCmd); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + OUTL (dev, 0, SCBPointer); + OUTW (dev, SCB_M | CU_ADDR_LOAD, SCBCmd); + + /* Initialize Rx and Tx rings. + */ + init_rx_ring (dev); + purge_tx_ring (dev); + + /* Tell the adapter where the RX ring is located. + */ + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + + OUTL (dev, phys_to_bus ((u32) & rx_ring[rx_next]), SCBPointer); + OUTW (dev, SCB_M | RUC_START, SCBCmd); + + /* Send the Configure frame */ + tx_cur = tx_next; + tx_next = ((tx_next + 1) % NUM_TX_DESC); + + cfg_cmd = (struct descriptor *) &tx_ring[tx_cur]; + cfg_cmd->command = cpu_to_le16 ((CONFIG_SYS_CMD_SUSPEND | CONFIG_SYS_CMD_CONFIGURE)); + cfg_cmd->status = 0; + cfg_cmd->link = cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_next])); + + memcpy (cfg_cmd->params, i82558_config_cmd, + sizeof (i82558_config_cmd)); + + if (!wait_for_eepro100 (dev)) { + printf ("Error---CONFIG_SYS_CMD_CONFIGURE: Can not reset ethernet controller.\n"); + goto Done; + } + + OUTL (dev, phys_to_bus ((u32) & tx_ring[tx_cur]), SCBPointer); + OUTW (dev, SCB_M | CU_START, SCBCmd); + + for (i = 0; + !(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_C); + i++) { + if (i >= TOUT_LOOP) { + printf ("%s: Tx error buffer not ready\n", dev->name); + goto Done; + } + } + + if (!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_OK)) { + printf ("TX error status = 0x%08X\n", + le16_to_cpu (tx_ring[tx_cur].status)); + goto Done; + } + + /* Send the Individual Address Setup frame + */ + tx_cur = tx_next; + tx_next = ((tx_next + 1) % NUM_TX_DESC); + + ias_cmd = (struct descriptor *) &tx_ring[tx_cur]; + ias_cmd->command = cpu_to_le16 ((CONFIG_SYS_CMD_SUSPEND | CONFIG_SYS_CMD_IAS)); + ias_cmd->status = 0; + ias_cmd->link = cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_next])); + + memcpy (ias_cmd->params, dev->enetaddr, 6); + + /* Tell the adapter where the TX ring is located. + */ + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + + OUTL (dev, phys_to_bus ((u32) & tx_ring[tx_cur]), SCBPointer); + OUTW (dev, SCB_M | CU_START, SCBCmd); + + for (i = 0; !(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_C); + i++) { + if (i >= TOUT_LOOP) { + printf ("%s: Tx error buffer not ready\n", + dev->name); + goto Done; + } + } + + if (!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_OK)) { + printf ("TX error status = 0x%08X\n", + le16_to_cpu (tx_ring[tx_cur].status)); + goto Done; + } + + status = 0; + + Done: + return status; +} + +static int eepro100_send(struct eth_device *dev, void *packet, int length) +{ + int i, status = -1; + int tx_cur; + + if (length <= 0) { + printf ("%s: bad packet size: %d\n", dev->name, length); + goto Done; + } + + tx_cur = tx_next; + tx_next = (tx_next + 1) % NUM_TX_DESC; + + tx_ring[tx_cur].command = cpu_to_le16 ( TxCB_CMD_TRANSMIT | + TxCB_CMD_SF | + TxCB_CMD_S | + TxCB_CMD_EL ); + tx_ring[tx_cur].status = 0; + tx_ring[tx_cur].count = cpu_to_le32 (tx_threshold); + tx_ring[tx_cur].link = + cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_next])); + tx_ring[tx_cur].tx_desc_addr = + cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_cur].tx_buf_addr0)); + tx_ring[tx_cur].tx_buf_addr0 = + cpu_to_le32 (phys_to_bus ((u_long) packet)); + tx_ring[tx_cur].tx_buf_size0 = cpu_to_le32 (length); + + if (!wait_for_eepro100 (dev)) { + printf ("%s: Tx error ethernet controller not ready.\n", + dev->name); + goto Done; + } + + /* Send the packet. + */ + OUTL (dev, phys_to_bus ((u32) & tx_ring[tx_cur]), SCBPointer); + OUTW (dev, SCB_M | CU_START, SCBCmd); + + for (i = 0; !(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_C); + i++) { + if (i >= TOUT_LOOP) { + printf ("%s: Tx error buffer not ready\n", dev->name); + goto Done; + } + } + + if (!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_OK)) { + printf ("TX error status = 0x%08X\n", + le16_to_cpu (tx_ring[tx_cur].status)); + goto Done; + } + + status = length; + + Done: + return status; +} + +static int eepro100_recv (struct eth_device *dev) +{ + u16 status, stat; + int rx_prev, length = 0; + + stat = INW (dev, SCBStatus); + OUTW (dev, stat & SCB_STATUS_RNR, SCBStatus); + + for (;;) { + status = le16_to_cpu (rx_ring[rx_next].status); + + if (!(status & RFD_STATUS_C)) { + break; + } + + /* Valid frame status. + */ + if ((status & RFD_STATUS_OK)) { + /* A valid frame received. + */ + length = le32_to_cpu (rx_ring[rx_next].count) & 0x3fff; + + /* Pass the packet up to the protocol + * layers. + */ + NetReceive((u8 *)rx_ring[rx_next].data, length); + } else { + /* There was an error. + */ + printf ("RX error status = 0x%08X\n", status); + } + + rx_ring[rx_next].control = cpu_to_le16 (RFD_CONTROL_S); + rx_ring[rx_next].status = 0; + rx_ring[rx_next].count = cpu_to_le32 (PKTSIZE_ALIGN << 16); + + rx_prev = (rx_next + NUM_RX_DESC - 1) % NUM_RX_DESC; + rx_ring[rx_prev].control = 0; + + /* Update entry information. + */ + rx_next = (rx_next + 1) % NUM_RX_DESC; + } + + if (stat & SCB_STATUS_RNR) { + + printf ("%s: Receiver is not ready, restart it !\n", dev->name); + + /* Reinitialize Rx ring. + */ + init_rx_ring (dev); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not restart ethernet controller.\n"); + goto Done; + } + + OUTL (dev, phys_to_bus ((u32) & rx_ring[rx_next]), SCBPointer); + OUTW (dev, SCB_M | RUC_START, SCBCmd); + } + + Done: + return length; +} + +static void eepro100_halt (struct eth_device *dev) +{ + /* Reset the ethernet controller + */ + OUTL (dev, I82559_SELECTIVE_RESET, SCBPort); + udelay (20); + + OUTL (dev, I82559_RESET, SCBPort); + udelay (20); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + OUTL (dev, 0, SCBPointer); + OUTW (dev, SCB_M | RUC_ADDR_LOAD, SCBCmd); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + OUTL (dev, 0, SCBPointer); + OUTW (dev, SCB_M | CU_ADDR_LOAD, SCBCmd); + + Done: + return; +} + + /* SROM Read. + */ +static int read_eeprom (struct eth_device *dev, int location, int addr_len) +{ + unsigned short retval = 0; + int read_cmd = location | EE_READ_CMD; + int i; + + OUTW (dev, EE_ENB & ~EE_CS, SCBeeprom); + OUTW (dev, EE_ENB, SCBeeprom); + + /* Shift the read command bits out. */ + for (i = 12; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + + OUTW (dev, EE_ENB | dataval, SCBeeprom); + udelay (1); + OUTW (dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom); + udelay (1); + } + OUTW (dev, EE_ENB, SCBeeprom); + + for (i = 15; i >= 0; i--) { + OUTW (dev, EE_ENB | EE_SHIFT_CLK, SCBeeprom); + udelay (1); + retval = (retval << 1) | + ((INW (dev, SCBeeprom) & EE_DATA_READ) ? 1 : 0); + OUTW (dev, EE_ENB, SCBeeprom); + udelay (1); + } + + /* Terminate the EEPROM access. */ + OUTW (dev, EE_ENB & ~EE_CS, SCBeeprom); + return retval; +} + +#ifdef CONFIG_EEPRO100_SROM_WRITE +int eepro100_write_eeprom (struct eth_device* dev, int location, int addr_len, unsigned short data) +{ + unsigned short dataval; + int enable_cmd = 0x3f | EE_EWENB_CMD; + int write_cmd = location | EE_WRITE_CMD; + int i; + unsigned long datalong, tmplong; + + OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB, SCBeeprom); + + /* Shift the enable command bits out. */ + for (i = (addr_len+EE_CMD_BITS-1); i >= 0; i--) + { + dataval = (enable_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + OUTW(dev, EE_ENB | dataval, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom); + udelay(1); + } + + OUTW(dev, EE_ENB, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB, SCBeeprom); + + + /* Shift the write command bits out. */ + for (i = (addr_len+EE_CMD_BITS-1); i >= 0; i--) + { + dataval = (write_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + OUTW(dev, EE_ENB | dataval, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom); + udelay(1); + } + + /* Write the data */ + datalong= (unsigned long) ((((data) & 0x00ff) << 8) | ( (data) >> 8)); + + for (i = 0; i< EE_DATA_BITS; i++) + { + /* Extract and move data bit to bit DI */ + dataval = ((datalong & 0x8000)>>13) ? EE_DATA_WRITE : 0; + + OUTW(dev, EE_ENB | dataval, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB | dataval, SCBeeprom); + udelay(1); + + datalong = datalong << 1; /* Adjust significant data bit*/ + } + + /* Finish up command (toggle CS) */ + OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom); + udelay(1); /* delay for more than 250 ns */ + OUTW(dev, EE_ENB, SCBeeprom); + + /* Wait for programming ready (D0 = 1) */ + tmplong = 10; + do + { + dataval = INW(dev, SCBeeprom); + if (dataval & EE_DATA_READ) + break; + udelay(10000); + } + while (-- tmplong); + + if (tmplong == 0) + { + printf ("Write i82559 eeprom timed out (100 ms waiting for data ready.\n"); + return -1; + } + + /* Terminate the EEPROM access. */ + OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom); + + return 0; +} +#endif + +static void init_rx_ring (struct eth_device *dev) +{ + int i; + + for (i = 0; i < NUM_RX_DESC; i++) { + rx_ring[i].status = 0; + rx_ring[i].control = + (i == NUM_RX_DESC - 1) ? cpu_to_le16 (RFD_CONTROL_S) : 0; + rx_ring[i].link = + cpu_to_le32 (phys_to_bus + ((u32) & rx_ring[(i + 1) % NUM_RX_DESC])); + rx_ring[i].rx_buf_addr = 0xffffffff; + rx_ring[i].count = cpu_to_le32 (PKTSIZE_ALIGN << 16); + } + + rx_next = 0; +} + +static void purge_tx_ring (struct eth_device *dev) +{ + int i; + + tx_next = 0; + tx_threshold = 0x01208000; + + for (i = 0; i < NUM_TX_DESC; i++) { + tx_ring[i].status = 0; + tx_ring[i].command = 0; + tx_ring[i].link = 0; + tx_ring[i].tx_desc_addr = 0; + tx_ring[i].count = 0; + + tx_ring[i].tx_buf_addr0 = 0; + tx_ring[i].tx_buf_size0 = 0; + tx_ring[i].tx_buf_addr1 = 0; + tx_ring[i].tx_buf_size1 = 0; + } +} + +static void read_hw_addr (struct eth_device *dev, bd_t * bis) +{ + u16 sum = 0; + int i, j; + int addr_len = read_eeprom (dev, 0, 6) == 0xffff ? 8 : 6; + + for (j = 0, i = 0; i < 0x40; i++) { + u16 value = read_eeprom (dev, i, addr_len); + + sum += value; + if (i < 3) { + dev->enetaddr[j++] = value; + dev->enetaddr[j++] = value >> 8; + } + } + + if (sum != 0xBABA) { + memset (dev->enetaddr, 0, ETH_ALEN); +#ifdef DEBUG + printf ("%s: Invalid EEPROM checksum %#4.4x, " + "check settings before activating this device!\n", + dev->name, sum); +#endif + } +} diff --git a/qemu/roms/u-boot/drivers/net/enc28j60.c b/qemu/roms/u-boot/drivers/net/enc28j60.c new file mode 100644 index 000000000..ec33764f5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/enc28j60.c @@ -0,0 +1,963 @@ +/* + * (C) Copyright 2010 + * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de + * Martin Krause, Martin.Krause@tqs.de + * reworked original enc28j60.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include "enc28j60.h" + +/* + * IMPORTANT: spi_claim_bus() and spi_release_bus() + * are called at begin and end of each of the following functions: + * enc_miiphy_read(), enc_miiphy_write(), enc_write_hwaddr(), + * enc_init(), enc_recv(), enc_send(), enc_halt() + * ALL other functions assume that the bus has already been claimed! + * Since NetReceive() might call enc_send() in return, the bus must be + * released, NetReceive() called and claimed again. + */ + +/* + * Controller memory layout. + * We only allow 1 frame for transmission and reserve the rest + * for reception to handle as many broadcast packets as possible. + * Also use the memory from 0x0000 for receiver buffer. See errata pt. 5 + * 0x0000 - 0x19ff 6656 bytes receive buffer + * 0x1a00 - 0x1fff 1536 bytes transmit buffer = + * control(1)+frame(1518)+status(7)+reserve(10). + */ +#define ENC_RX_BUF_START 0x0000 +#define ENC_RX_BUF_END 0x19ff +#define ENC_TX_BUF_START 0x1a00 +#define ENC_TX_BUF_END 0x1fff +#define ENC_MAX_FRM_LEN 1518 +#define RX_RESET_COUNTER 1000 + +/* + * For non data transfer functions, like phy read/write, set hwaddr, init + * we do not need a full, time consuming init including link ready wait. + * This enum helps to bring the chip through the minimum necessary inits. + */ +enum enc_initstate {none=0, setupdone, linkready}; +typedef struct enc_device { + struct eth_device *dev; /* back pointer */ + struct spi_slave *slave; + int rx_reset_counter; + u16 next_pointer; + u8 bank; /* current bank in enc28j60 */ + enum enc_initstate initstate; +} enc_dev_t; + +/* + * enc_bset: set bits in a common register + * enc_bclr: clear bits in a common register + * + * making the reg parameter u8 will give a compile time warning if the + * functions are called with a register not accessible in all Banks + */ +static void enc_bset(enc_dev_t *enc, const u8 reg, const u8 data) +{ + u8 dout[2]; + + dout[0] = CMD_BFS(reg); + dout[1] = data; + spi_xfer(enc->slave, 2 * 8, dout, NULL, + SPI_XFER_BEGIN | SPI_XFER_END); +} + +static void enc_bclr(enc_dev_t *enc, const u8 reg, const u8 data) +{ + u8 dout[2]; + + dout[0] = CMD_BFC(reg); + dout[1] = data; + spi_xfer(enc->slave, 2 * 8, dout, NULL, + SPI_XFER_BEGIN | SPI_XFER_END); +} + +/* + * high byte of the register contains bank number: + * 0: no bank switch necessary + * 1: switch to bank 0 + * 2: switch to bank 1 + * 3: switch to bank 2 + * 4: switch to bank 3 + */ +static void enc_set_bank(enc_dev_t *enc, const u16 reg) +{ + u8 newbank = reg >> 8; + + if (newbank == 0 || newbank == enc->bank) + return; + switch (newbank) { + case 1: + enc_bclr(enc, CTL_REG_ECON1, + ENC_ECON1_BSEL0 | ENC_ECON1_BSEL1); + break; + case 2: + enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_BSEL0); + enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_BSEL1); + break; + case 3: + enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_BSEL0); + enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_BSEL1); + break; + case 4: + enc_bset(enc, CTL_REG_ECON1, + ENC_ECON1_BSEL0 | ENC_ECON1_BSEL1); + break; + } + enc->bank = newbank; +} + +/* + * local functions to access SPI + * + * reg: register inside ENC28J60 + * data: 8/16 bits to write + * c: number of retries + * + * enc_r8: read 8 bits + * enc_r16: read 16 bits + * enc_w8: write 8 bits + * enc_w16: write 16 bits + * enc_w8_retry: write 8 bits, verify and retry + * enc_rbuf: read from ENC28J60 into buffer + * enc_wbuf: write from buffer into ENC28J60 + */ + +/* + * MAC and MII registers need a 3 byte SPI transfer to read, + * all other registers need a 2 byte SPI transfer. + */ +static int enc_reg2nbytes(const u16 reg) +{ + /* check if MAC or MII register */ + return ((reg >= CTL_REG_MACON1 && reg <= CTL_REG_MIRDH) || + (reg >= CTL_REG_MAADR1 && reg <= CTL_REG_MAADR4) || + (reg == CTL_REG_MISTAT)) ? 3 : 2; +} + +/* + * Read a byte register + */ +static u8 enc_r8(enc_dev_t *enc, const u16 reg) +{ + u8 dout[3]; + u8 din[3]; + int nbytes = enc_reg2nbytes(reg); + + enc_set_bank(enc, reg); + dout[0] = CMD_RCR(reg); + spi_xfer(enc->slave, nbytes * 8, dout, din, + SPI_XFER_BEGIN | SPI_XFER_END); + return din[nbytes-1]; +} + +/* + * Read a L/H register pair and return a word. + * Must be called with the L register's address. + */ +static u16 enc_r16(enc_dev_t *enc, const u16 reg) +{ + u8 dout[3]; + u8 din[3]; + u16 result; + int nbytes = enc_reg2nbytes(reg); + + enc_set_bank(enc, reg); + dout[0] = CMD_RCR(reg); + spi_xfer(enc->slave, nbytes * 8, dout, din, + SPI_XFER_BEGIN | SPI_XFER_END); + result = din[nbytes-1]; + dout[0]++; /* next register */ + spi_xfer(enc->slave, nbytes * 8, dout, din, + SPI_XFER_BEGIN | SPI_XFER_END); + result |= din[nbytes-1] << 8; + return result; +} + +/* + * Write a byte register + */ +static void enc_w8(enc_dev_t *enc, const u16 reg, const u8 data) +{ + u8 dout[2]; + + enc_set_bank(enc, reg); + dout[0] = CMD_WCR(reg); + dout[1] = data; + spi_xfer(enc->slave, 2 * 8, dout, NULL, + SPI_XFER_BEGIN | SPI_XFER_END); +} + +/* + * Write a L/H register pair. + * Must be called with the L register's address. + */ +static void enc_w16(enc_dev_t *enc, const u16 reg, const u16 data) +{ + u8 dout[2]; + + enc_set_bank(enc, reg); + dout[0] = CMD_WCR(reg); + dout[1] = data; + spi_xfer(enc->slave, 2 * 8, dout, NULL, + SPI_XFER_BEGIN | SPI_XFER_END); + dout[0]++; /* next register */ + dout[1] = data >> 8; + spi_xfer(enc->slave, 2 * 8, dout, NULL, + SPI_XFER_BEGIN | SPI_XFER_END); +} + +/* + * Write a byte register, verify and retry + */ +static void enc_w8_retry(enc_dev_t *enc, const u16 reg, const u8 data, const int c) +{ + u8 dout[2]; + u8 readback; + int i; + + enc_set_bank(enc, reg); + for (i = 0; i < c; i++) { + dout[0] = CMD_WCR(reg); + dout[1] = data; + spi_xfer(enc->slave, 2 * 8, dout, NULL, + SPI_XFER_BEGIN | SPI_XFER_END); + readback = enc_r8(enc, reg); + if (readback == data) + break; + /* wait 1ms */ + udelay(1000); + } + if (i == c) { + printf("%s: write reg 0x%03x failed\n", enc->dev->name, reg); + } +} + +/* + * Read ENC RAM into buffer + */ +static void enc_rbuf(enc_dev_t *enc, const u16 length, u8 *buf) +{ + u8 dout[1]; + + dout[0] = CMD_RBM; + spi_xfer(enc->slave, 8, dout, NULL, SPI_XFER_BEGIN); + spi_xfer(enc->slave, length * 8, NULL, buf, SPI_XFER_END); +#ifdef DEBUG + puts("Rx:\n"); + print_buffer(0, buf, 1, length, 0); +#endif +} + +/* + * Write buffer into ENC RAM + */ +static void enc_wbuf(enc_dev_t *enc, const u16 length, const u8 *buf, const u8 control) +{ + u8 dout[2]; + dout[0] = CMD_WBM; + dout[1] = control; + spi_xfer(enc->slave, 2 * 8, dout, NULL, SPI_XFER_BEGIN); + spi_xfer(enc->slave, length * 8, buf, NULL, SPI_XFER_END); +#ifdef DEBUG + puts("Tx:\n"); + print_buffer(0, buf, 1, length, 0); +#endif +} + +/* + * Try to claim the SPI bus. + * Print error message on failure. + */ +static int enc_claim_bus(enc_dev_t *enc) +{ + int rc = spi_claim_bus(enc->slave); + if (rc) + printf("%s: failed to claim SPI bus\n", enc->dev->name); + return rc; +} + +/* + * Release previously claimed SPI bus. + * This function is mainly for symmetry to enc_claim_bus(). + * Let the toolchain decide to inline it... + */ +static void enc_release_bus(enc_dev_t *enc) +{ + spi_release_bus(enc->slave); +} + +/* + * Read PHY register + */ +static u16 enc_phy_read(enc_dev_t *enc, const u8 addr) +{ + uint64_t etime; + u8 status; + + enc_w8(enc, CTL_REG_MIREGADR, addr); + enc_w8(enc, CTL_REG_MICMD, ENC_MICMD_MIIRD); + /* 1 second timeout - only happens on hardware problem */ + etime = get_ticks() + get_tbclk(); + /* poll MISTAT.BUSY bit until operation is complete */ + do + { + status = enc_r8(enc, CTL_REG_MISTAT); + } while (get_ticks() <= etime && (status & ENC_MISTAT_BUSY)); + if (status & ENC_MISTAT_BUSY) { + printf("%s: timeout reading phy\n", enc->dev->name); + return 0; + } + enc_w8(enc, CTL_REG_MICMD, 0); + return enc_r16(enc, CTL_REG_MIRDL); +} + +/* + * Write PHY register + */ +static void enc_phy_write(enc_dev_t *enc, const u8 addr, const u16 data) +{ + uint64_t etime; + u8 status; + + enc_w8(enc, CTL_REG_MIREGADR, addr); + enc_w16(enc, CTL_REG_MIWRL, data); + /* 1 second timeout - only happens on hardware problem */ + etime = get_ticks() + get_tbclk(); + /* poll MISTAT.BUSY bit until operation is complete */ + do + { + status = enc_r8(enc, CTL_REG_MISTAT); + } while (get_ticks() <= etime && (status & ENC_MISTAT_BUSY)); + if (status & ENC_MISTAT_BUSY) { + printf("%s: timeout writing phy\n", enc->dev->name); + return; + } +} + +/* + * Verify link status, wait if necessary + * + * Note: with a 10 MBit/s only PHY there is no autonegotiation possible, + * half/full duplex is a pure setup matter. For the time being, this driver + * will setup in half duplex mode only. + */ +static int enc_phy_link_wait(enc_dev_t *enc) +{ + u16 status; + int duplex; + uint64_t etime; + +#ifdef CONFIG_ENC_SILENTLINK + /* check if we have a link, then just return */ + status = enc_phy_read(enc, PHY_REG_PHSTAT1); + if (status & ENC_PHSTAT1_LLSTAT) + return 0; +#endif + + /* wait for link with 1 second timeout */ + etime = get_ticks() + get_tbclk(); + while (get_ticks() <= etime) { + status = enc_phy_read(enc, PHY_REG_PHSTAT1); + if (status & ENC_PHSTAT1_LLSTAT) { + /* now we have a link */ + status = enc_phy_read(enc, PHY_REG_PHSTAT2); + duplex = (status & ENC_PHSTAT2_DPXSTAT) ? 1 : 0; + printf("%s: link up, 10Mbps %s-duplex\n", + enc->dev->name, duplex ? "full" : "half"); + return 0; + } + udelay(1000); + } + + /* timeout occured */ + printf("%s: link down\n", enc->dev->name); + return 1; +} + +/* + * This function resets the receiver only. + */ +static void enc_reset_rx(enc_dev_t *enc) +{ + u8 econ1; + + econ1 = enc_r8(enc, CTL_REG_ECON1); + if ((econ1 & ENC_ECON1_RXRST) == 0) { + enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_RXRST); + enc->rx_reset_counter = RX_RESET_COUNTER; + } +} + +/* + * Reset receiver and reenable it. + */ +static void enc_reset_rx_call(enc_dev_t *enc) +{ + enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_RXRST); + enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_RXEN); +} + +/* + * Copy a packet from the receive ring and forward it to + * the protocol stack. + */ +static void enc_receive(enc_dev_t *enc) +{ + u8 *packet = (u8 *)NetRxPackets[0]; + u16 pkt_len; + u16 copy_len; + u16 status; + u8 pkt_cnt = 0; + u16 rxbuf_rdpt; + u8 hbuf[6]; + + enc_w16(enc, CTL_REG_ERDPTL, enc->next_pointer); + do { + enc_rbuf(enc, 6, hbuf); + enc->next_pointer = hbuf[0] | (hbuf[1] << 8); + pkt_len = hbuf[2] | (hbuf[3] << 8); + status = hbuf[4] | (hbuf[5] << 8); + debug("next_pointer=$%04x pkt_len=%u status=$%04x\n", + enc->next_pointer, pkt_len, status); + if (pkt_len <= ENC_MAX_FRM_LEN) + copy_len = pkt_len; + else + copy_len = 0; + if ((status & (1L << 7)) == 0) /* check Received Ok bit */ + copy_len = 0; + /* check if next pointer is resonable */ + if (enc->next_pointer >= ENC_TX_BUF_START) + copy_len = 0; + if (copy_len > 0) { + enc_rbuf(enc, copy_len, packet); + } + /* advance read pointer to next pointer */ + enc_w16(enc, CTL_REG_ERDPTL, enc->next_pointer); + /* decrease packet counter */ + enc_bset(enc, CTL_REG_ECON2, ENC_ECON2_PKTDEC); + /* + * Only odd values should be written to ERXRDPTL, + * see errata B4 pt.13 + */ + rxbuf_rdpt = enc->next_pointer - 1; + if ((rxbuf_rdpt < enc_r16(enc, CTL_REG_ERXSTL)) || + (rxbuf_rdpt > enc_r16(enc, CTL_REG_ERXNDL))) { + enc_w16(enc, CTL_REG_ERXRDPTL, + enc_r16(enc, CTL_REG_ERXNDL)); + } else { + enc_w16(enc, CTL_REG_ERXRDPTL, rxbuf_rdpt); + } + /* read pktcnt */ + pkt_cnt = enc_r8(enc, CTL_REG_EPKTCNT); + if (copy_len == 0) { + (void)enc_r8(enc, CTL_REG_EIR); + enc_reset_rx(enc); + printf("%s: receive copy_len=0\n", enc->dev->name); + continue; + } + /* + * Because NetReceive() might call enc_send(), we need to + * release the SPI bus, call NetReceive(), reclaim the bus + */ + enc_release_bus(enc); + NetReceive(packet, pkt_len); + if (enc_claim_bus(enc)) + return; + (void)enc_r8(enc, CTL_REG_EIR); + } while (pkt_cnt); + /* Use EPKTCNT not EIR.PKTIF flag, see errata pt. 6 */ +} + +/* + * Poll for completely received packets. + */ +static void enc_poll(enc_dev_t *enc) +{ + u8 eir_reg; + u8 pkt_cnt; + +#ifdef CONFIG_USE_IRQ + /* clear global interrupt enable bit in enc28j60 */ + enc_bclr(enc, CTL_REG_EIE, ENC_EIE_INTIE); +#endif + (void)enc_r8(enc, CTL_REG_ESTAT); + eir_reg = enc_r8(enc, CTL_REG_EIR); + if (eir_reg & ENC_EIR_TXIF) { + /* clear TXIF bit in EIR */ + enc_bclr(enc, CTL_REG_EIR, ENC_EIR_TXIF); + } + /* We have to use pktcnt and not pktif bit, see errata pt. 6 */ + pkt_cnt = enc_r8(enc, CTL_REG_EPKTCNT); + if (pkt_cnt > 0) { + if ((eir_reg & ENC_EIR_PKTIF) == 0) { + debug("enc_poll: pkt cnt > 0, but pktif not set\n"); + } + enc_receive(enc); + /* + * clear PKTIF bit in EIR, this should not need to be done + * but it seems like we get problems if we do not + */ + enc_bclr(enc, CTL_REG_EIR, ENC_EIR_PKTIF); + } + if (eir_reg & ENC_EIR_RXERIF) { + printf("%s: rx error\n", enc->dev->name); + enc_bclr(enc, CTL_REG_EIR, ENC_EIR_RXERIF); + } + if (eir_reg & ENC_EIR_TXERIF) { + printf("%s: tx error\n", enc->dev->name); + enc_bclr(enc, CTL_REG_EIR, ENC_EIR_TXERIF); + } +#ifdef CONFIG_USE_IRQ + /* set global interrupt enable bit in enc28j60 */ + enc_bset(enc, CTL_REG_EIE, ENC_EIE_INTIE); +#endif +} + +/* + * Completely Reset the ENC + */ +static void enc_reset(enc_dev_t *enc) +{ + u8 dout[1]; + + dout[0] = CMD_SRC; + spi_xfer(enc->slave, 8, dout, NULL, + SPI_XFER_BEGIN | SPI_XFER_END); + /* sleep 1 ms. See errata pt. 2 */ + udelay(1000); +} + +/* + * Initialisation data for most of the ENC registers + */ +static const u16 enc_initdata[] = { + /* + * Setup the buffer space. The reset values are valid for the + * other pointers. + * + * We shall not write to ERXST, see errata pt. 5. Instead we + * have to make sure that ENC_RX_BUS_START is 0. + */ + CTL_REG_ERXSTL, ENC_RX_BUF_START, + CTL_REG_ERXSTH, ENC_RX_BUF_START >> 8, + CTL_REG_ERXNDL, ENC_RX_BUF_END, + CTL_REG_ERXNDH, ENC_RX_BUF_END >> 8, + CTL_REG_ERDPTL, ENC_RX_BUF_START, + CTL_REG_ERDPTH, ENC_RX_BUF_START >> 8, + /* + * Set the filter to receive only good-CRC, unicast and broadcast + * frames. + * Note: some DHCP servers return their answers as broadcasts! + * So its unwise to remove broadcast from this. This driver + * might incur receiver overruns with packet loss on a broadcast + * flooded network. + */ + CTL_REG_ERXFCON, ENC_RFR_BCEN | ENC_RFR_UCEN | ENC_RFR_CRCEN, + + /* enable MAC to receive frames */ + CTL_REG_MACON1, + ENC_MACON1_MARXEN | ENC_MACON1_TXPAUS | ENC_MACON1_RXPAUS, + + /* configure pad, tx-crc and duplex */ + CTL_REG_MACON3, + ENC_MACON3_PADCFG0 | ENC_MACON3_TXCRCEN | + ENC_MACON3_FRMLNEN, + + /* Allow infinite deferals if the medium is continously busy */ + CTL_REG_MACON4, ENC_MACON4_DEFER, + + /* Late collisions occur beyond 63 bytes */ + CTL_REG_MACLCON2, 63, + + /* + * Set (low byte) Non-Back-to_Back Inter-Packet Gap. + * Recommended 0x12 + */ + CTL_REG_MAIPGL, 0x12, + + /* + * Set (high byte) Non-Back-to_Back Inter-Packet Gap. + * Recommended 0x0c for half-duplex. Nothing for full-duplex + */ + CTL_REG_MAIPGH, 0x0C, + + /* set maximum frame length */ + CTL_REG_MAMXFLL, ENC_MAX_FRM_LEN, + CTL_REG_MAMXFLH, ENC_MAX_FRM_LEN >> 8, + + /* + * Set MAC back-to-back inter-packet gap. + * Recommended 0x12 for half duplex + * and 0x15 for full duplex. + */ + CTL_REG_MABBIPG, 0x12, + + /* end of table */ + 0xffff +}; + +/* + * Wait for the XTAL oscillator to become ready + */ +static int enc_clock_wait(enc_dev_t *enc) +{ + uint64_t etime; + + /* one second timeout */ + etime = get_ticks() + get_tbclk(); + + /* + * Wait for CLKRDY to become set (i.e., check that we can + * communicate with the ENC) + */ + do + { + if (enc_r8(enc, CTL_REG_ESTAT) & ENC_ESTAT_CLKRDY) + return 0; + } while (get_ticks() <= etime); + + printf("%s: timeout waiting for CLKRDY\n", enc->dev->name); + return -1; +} + +/* + * Write the MAC address into the ENC + */ +static int enc_write_macaddr(enc_dev_t *enc) +{ + unsigned char *p = enc->dev->enetaddr; + + enc_w8_retry(enc, CTL_REG_MAADR5, *p++, 5); + enc_w8_retry(enc, CTL_REG_MAADR4, *p++, 5); + enc_w8_retry(enc, CTL_REG_MAADR3, *p++, 5); + enc_w8_retry(enc, CTL_REG_MAADR2, *p++, 5); + enc_w8_retry(enc, CTL_REG_MAADR1, *p++, 5); + enc_w8_retry(enc, CTL_REG_MAADR0, *p, 5); + return 0; +} + +/* + * Setup most of the ENC registers + */ +static int enc_setup(enc_dev_t *enc) +{ + u16 phid1 = 0; + u16 phid2 = 0; + const u16 *tp; + + /* reset enc struct values */ + enc->next_pointer = ENC_RX_BUF_START; + enc->rx_reset_counter = RX_RESET_COUNTER; + enc->bank = 0xff; /* invalidate current bank in enc28j60 */ + + /* verify PHY identification */ + phid1 = enc_phy_read(enc, PHY_REG_PHID1); + phid2 = enc_phy_read(enc, PHY_REG_PHID2) & ENC_PHID2_MASK; + if (phid1 != ENC_PHID1_VALUE || phid2 != ENC_PHID2_VALUE) { + printf("%s: failed to identify PHY. Found %04x:%04x\n", + enc->dev->name, phid1, phid2); + return -1; + } + + /* now program registers */ + for (tp = enc_initdata; *tp != 0xffff; tp += 2) + enc_w8_retry(enc, tp[0], tp[1], 10); + + /* + * Prevent automatic loopback of data beeing transmitted by setting + * ENC_PHCON2_HDLDIS + */ + enc_phy_write(enc, PHY_REG_PHCON2, (1<<8)); + + /* + * LEDs configuration + * LEDA: LACFG = 0100 -> display link status + * LEDB: LBCFG = 0111 -> display TX & RX activity + * STRCH = 1 -> LED pulses + */ + enc_phy_write(enc, PHY_REG_PHLCON, 0x0472); + + /* Reset PDPXMD-bit => half duplex */ + enc_phy_write(enc, PHY_REG_PHCON1, 0); + +#ifdef CONFIG_USE_IRQ + /* enable interrupts */ + enc_bset(enc, CTL_REG_EIE, ENC_EIE_PKTIE); + enc_bset(enc, CTL_REG_EIE, ENC_EIE_TXIE); + enc_bset(enc, CTL_REG_EIE, ENC_EIE_RXERIE); + enc_bset(enc, CTL_REG_EIE, ENC_EIE_TXERIE); + enc_bset(enc, CTL_REG_EIE, ENC_EIE_INTIE); +#endif + + return 0; +} + +/* + * Check if ENC has been initialized. + * If not, try to initialize it. + * Remember initialized state in struct. + */ +static int enc_initcheck(enc_dev_t *enc, const enum enc_initstate requiredstate) +{ + if (enc->initstate >= requiredstate) + return 0; + + if (enc->initstate < setupdone) { + /* Initialize the ENC only */ + enc_reset(enc); + /* if any of functions fails, skip the rest and return an error */ + if (enc_clock_wait(enc) || enc_setup(enc) || enc_write_macaddr(enc)) { + return -1; + } + enc->initstate = setupdone; + } + /* if that's all we need, return here */ + if (enc->initstate >= requiredstate) + return 0; + + /* now wait for link ready condition */ + if (enc_phy_link_wait(enc)) { + return -1; + } + enc->initstate = linkready; + return 0; +} + +#if defined(CONFIG_CMD_MII) +/* + * Read a PHY register. + * + * This function is registered with miiphy_register(). + */ +int enc_miiphy_read(const char *devname, u8 phy_adr, u8 reg, u16 *value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + enc_dev_t *enc; + + if (!dev || phy_adr != 0) + return -1; + + enc = dev->priv; + if (enc_claim_bus(enc)) + return -1; + if (enc_initcheck(enc, setupdone)) { + enc_release_bus(enc); + return -1; + } + *value = enc_phy_read(enc, reg); + enc_release_bus(enc); + return 0; +} + +/* + * Write a PHY register. + * + * This function is registered with miiphy_register(). + */ +int enc_miiphy_write(const char *devname, u8 phy_adr, u8 reg, u16 value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + enc_dev_t *enc; + + if (!dev || phy_adr != 0) + return -1; + + enc = dev->priv; + if (enc_claim_bus(enc)) + return -1; + if (enc_initcheck(enc, setupdone)) { + enc_release_bus(enc); + return -1; + } + enc_phy_write(enc, reg, value); + enc_release_bus(enc); + return 0; +} +#endif + +/* + * Write hardware (MAC) address. + * + * This function entered into eth_device structure. + */ +static int enc_write_hwaddr(struct eth_device *dev) +{ + enc_dev_t *enc = dev->priv; + + if (enc_claim_bus(enc)) + return -1; + if (enc_initcheck(enc, setupdone)) { + enc_release_bus(enc); + return -1; + } + enc_release_bus(enc); + return 0; +} + +/* + * Initialize ENC28J60 for use. + * + * This function entered into eth_device structure. + */ +static int enc_init(struct eth_device *dev, bd_t *bis) +{ + enc_dev_t *enc = dev->priv; + + if (enc_claim_bus(enc)) + return -1; + if (enc_initcheck(enc, linkready)) { + enc_release_bus(enc); + return -1; + } + /* enable receive */ + enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_RXEN); + enc_release_bus(enc); + return 0; +} + +/* + * Check for received packets. + * + * This function entered into eth_device structure. + */ +static int enc_recv(struct eth_device *dev) +{ + enc_dev_t *enc = dev->priv; + + if (enc_claim_bus(enc)) + return -1; + if (enc_initcheck(enc, linkready)) { + enc_release_bus(enc); + return -1; + } + /* Check for dead receiver */ + if (enc->rx_reset_counter > 0) + enc->rx_reset_counter--; + else + enc_reset_rx_call(enc); + enc_poll(enc); + enc_release_bus(enc); + return 0; +} + +/* + * Send a packet. + * + * This function entered into eth_device structure. + * + * Should we wait here until we have a Link? Or shall we leave that to + * protocol retries? + */ +static int enc_send( + struct eth_device *dev, + void *packet, + int length) +{ + enc_dev_t *enc = dev->priv; + + if (enc_claim_bus(enc)) + return -1; + if (enc_initcheck(enc, linkready)) { + enc_release_bus(enc); + return -1; + } + /* setup transmit pointers */ + enc_w16(enc, CTL_REG_EWRPTL, ENC_TX_BUF_START); + enc_w16(enc, CTL_REG_ETXNDL, length + ENC_TX_BUF_START); + enc_w16(enc, CTL_REG_ETXSTL, ENC_TX_BUF_START); + /* write packet to ENC */ + enc_wbuf(enc, length, (u8 *) packet, 0x00); + /* + * Check that the internal transmit logic has not been altered + * by excessive collisions. Reset transmitter if so. + * See Errata B4 12 and 14. + */ + if (enc_r8(enc, CTL_REG_EIR) & ENC_EIR_TXERIF) { + enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_TXRST); + enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_TXRST); + } + enc_bclr(enc, CTL_REG_EIR, (ENC_EIR_TXERIF | ENC_EIR_TXIF)); + /* start transmitting */ + enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_TXRTS); + enc_release_bus(enc); + return 0; +} + +/* + * Finish use of ENC. + * + * This function entered into eth_device structure. + */ +static void enc_halt(struct eth_device *dev) +{ + enc_dev_t *enc = dev->priv; + + if (enc_claim_bus(enc)) + return; + /* Just disable receiver */ + enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_RXEN); + enc_release_bus(enc); +} + +/* + * This is the only exported function. + * + * It may be called several times with different bus:cs combinations. + */ +int enc28j60_initialize(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct eth_device *dev; + enc_dev_t *enc; + + /* try to allocate, check and clear eth_device object */ + dev = malloc(sizeof(*dev)); + if (!dev) { + return -1; + } + memset(dev, 0, sizeof(*dev)); + + /* try to allocate, check and clear enc_dev_t object */ + enc = malloc(sizeof(*enc)); + if (!enc) { + free(dev); + return -1; + } + memset(enc, 0, sizeof(*enc)); + + /* try to setup the SPI slave */ + enc->slave = spi_setup_slave(bus, cs, max_hz, mode); + if (!enc->slave) { + printf("enc28j60: invalid SPI device %i:%i\n", bus, cs); + free(enc); + free(dev); + return -1; + } + + enc->dev = dev; + /* now fill the eth_device object */ + dev->priv = enc; + dev->init = enc_init; + dev->halt = enc_halt; + dev->send = enc_send; + dev->recv = enc_recv; + dev->write_hwaddr = enc_write_hwaddr; + sprintf(dev->name, "enc%i.%i", bus, cs); + eth_register(dev); +#if defined(CONFIG_CMD_MII) + miiphy_register(dev->name, enc_miiphy_read, enc_miiphy_write); +#endif + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/enc28j60.h b/qemu/roms/u-boot/drivers/net/enc28j60.h new file mode 100644 index 000000000..289e41288 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/enc28j60.h @@ -0,0 +1,238 @@ +/* + * (X) extracted from enc28j60.c + * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _enc28j60_h +#define _enc28j60_h + +/* + * SPI Commands + * + * Bits 7-5: Command + * Bits 4-0: Register + */ +#define CMD_RCR(x) (0x00+((x)&0x1f)) /* Read Control Register */ +#define CMD_RBM 0x3a /* Read Buffer Memory */ +#define CMD_WCR(x) (0x40+((x)&0x1f)) /* Write Control Register */ +#define CMD_WBM 0x7a /* Write Buffer Memory */ +#define CMD_BFS(x) (0x80+((x)&0x1f)) /* Bit Field Set */ +#define CMD_BFC(x) (0xa0+((x)&0x1f)) /* Bit Field Clear */ +#define CMD_SRC 0xff /* System Reset Command */ + +/* NEW: encode (bank number+1) in upper byte */ + +/* Common Control Registers accessible in all Banks */ +#define CTL_REG_EIE 0x01B +#define CTL_REG_EIR 0x01C +#define CTL_REG_ESTAT 0x01D +#define CTL_REG_ECON2 0x01E +#define CTL_REG_ECON1 0x01F + +/* Control Registers accessible in Bank 0 */ +#define CTL_REG_ERDPTL 0x100 +#define CTL_REG_ERDPTH 0x101 +#define CTL_REG_EWRPTL 0x102 +#define CTL_REG_EWRPTH 0x103 +#define CTL_REG_ETXSTL 0x104 +#define CTL_REG_ETXSTH 0x105 +#define CTL_REG_ETXNDL 0x106 +#define CTL_REG_ETXNDH 0x107 +#define CTL_REG_ERXSTL 0x108 +#define CTL_REG_ERXSTH 0x109 +#define CTL_REG_ERXNDL 0x10A +#define CTL_REG_ERXNDH 0x10B +#define CTL_REG_ERXRDPTL 0x10C +#define CTL_REG_ERXRDPTH 0x10D +#define CTL_REG_ERXWRPTL 0x10E +#define CTL_REG_ERXWRPTH 0x10F +#define CTL_REG_EDMASTL 0x110 +#define CTL_REG_EDMASTH 0x111 +#define CTL_REG_EDMANDL 0x112 +#define CTL_REG_EDMANDH 0x113 +#define CTL_REG_EDMADSTL 0x114 +#define CTL_REG_EDMADSTH 0x115 +#define CTL_REG_EDMACSL 0x116 +#define CTL_REG_EDMACSH 0x117 + +/* Control Registers accessible in Bank 1 */ +#define CTL_REG_EHT0 0x200 +#define CTL_REG_EHT1 0x201 +#define CTL_REG_EHT2 0x202 +#define CTL_REG_EHT3 0x203 +#define CTL_REG_EHT4 0x204 +#define CTL_REG_EHT5 0x205 +#define CTL_REG_EHT6 0x206 +#define CTL_REG_EHT7 0x207 +#define CTL_REG_EPMM0 0x208 +#define CTL_REG_EPMM1 0x209 +#define CTL_REG_EPMM2 0x20A +#define CTL_REG_EPMM3 0x20B +#define CTL_REG_EPMM4 0x20C +#define CTL_REG_EPMM5 0x20D +#define CTL_REG_EPMM6 0x20E +#define CTL_REG_EPMM7 0x20F +#define CTL_REG_EPMCSL 0x210 +#define CTL_REG_EPMCSH 0x211 +#define CTL_REG_EPMOL 0x214 +#define CTL_REG_EPMOH 0x215 +#define CTL_REG_EWOLIE 0x216 +#define CTL_REG_EWOLIR 0x217 +#define CTL_REG_ERXFCON 0x218 +#define CTL_REG_EPKTCNT 0x219 + +/* Control Registers accessible in Bank 2 */ +#define CTL_REG_MACON1 0x300 +#define CTL_REG_MACON2 0x301 +#define CTL_REG_MACON3 0x302 +#define CTL_REG_MACON4 0x303 +#define CTL_REG_MABBIPG 0x304 +#define CTL_REG_MAIPGL 0x306 +#define CTL_REG_MAIPGH 0x307 +#define CTL_REG_MACLCON1 0x308 +#define CTL_REG_MACLCON2 0x309 +#define CTL_REG_MAMXFLL 0x30A +#define CTL_REG_MAMXFLH 0x30B +#define CTL_REG_MAPHSUP 0x30D +#define CTL_REG_MICON 0x311 +#define CTL_REG_MICMD 0x312 +#define CTL_REG_MIREGADR 0x314 +#define CTL_REG_MIWRL 0x316 +#define CTL_REG_MIWRH 0x317 +#define CTL_REG_MIRDL 0x318 +#define CTL_REG_MIRDH 0x319 + +/* Control Registers accessible in Bank 3 */ +#define CTL_REG_MAADR1 0x400 +#define CTL_REG_MAADR0 0x401 +#define CTL_REG_MAADR3 0x402 +#define CTL_REG_MAADR2 0x403 +#define CTL_REG_MAADR5 0x404 +#define CTL_REG_MAADR4 0x405 +#define CTL_REG_EBSTSD 0x406 +#define CTL_REG_EBSTCON 0x407 +#define CTL_REG_EBSTCSL 0x408 +#define CTL_REG_EBSTCSH 0x409 +#define CTL_REG_MISTAT 0x40A +#define CTL_REG_EREVID 0x412 +#define CTL_REG_ECOCON 0x415 +#define CTL_REG_EFLOCON 0x417 +#define CTL_REG_EPAUSL 0x418 +#define CTL_REG_EPAUSH 0x419 + +/* PHY Register */ +#define PHY_REG_PHCON1 0x00 +#define PHY_REG_PHSTAT1 0x01 +#define PHY_REG_PHID1 0x02 +#define PHY_REG_PHID2 0x03 +#define PHY_REG_PHCON2 0x10 +#define PHY_REG_PHSTAT2 0x11 +#define PHY_REG_PHLCON 0x14 + +/* Receive Filter Register (ERXFCON) bits */ +#define ENC_RFR_UCEN 0x80 +#define ENC_RFR_ANDOR 0x40 +#define ENC_RFR_CRCEN 0x20 +#define ENC_RFR_PMEN 0x10 +#define ENC_RFR_MPEN 0x08 +#define ENC_RFR_HTEN 0x04 +#define ENC_RFR_MCEN 0x02 +#define ENC_RFR_BCEN 0x01 + +/* ECON1 Register Bits */ +#define ENC_ECON1_TXRST 0x80 +#define ENC_ECON1_RXRST 0x40 +#define ENC_ECON1_DMAST 0x20 +#define ENC_ECON1_CSUMEN 0x10 +#define ENC_ECON1_TXRTS 0x08 +#define ENC_ECON1_RXEN 0x04 +#define ENC_ECON1_BSEL1 0x02 +#define ENC_ECON1_BSEL0 0x01 + +/* ECON2 Register Bits */ +#define ENC_ECON2_AUTOINC 0x80 +#define ENC_ECON2_PKTDEC 0x40 +#define ENC_ECON2_PWRSV 0x20 +#define ENC_ECON2_VRPS 0x08 + +/* EIR Register Bits */ +#define ENC_EIR_PKTIF 0x40 +#define ENC_EIR_DMAIF 0x20 +#define ENC_EIR_LINKIF 0x10 +#define ENC_EIR_TXIF 0x08 +#define ENC_EIR_WOLIF 0x04 +#define ENC_EIR_TXERIF 0x02 +#define ENC_EIR_RXERIF 0x01 + +/* ESTAT Register Bits */ +#define ENC_ESTAT_INT 0x80 +#define ENC_ESTAT_LATECOL 0x10 +#define ENC_ESTAT_RXBUSY 0x04 +#define ENC_ESTAT_TXABRT 0x02 +#define ENC_ESTAT_CLKRDY 0x01 + +/* EIE Register Bits */ +#define ENC_EIE_INTIE 0x80 +#define ENC_EIE_PKTIE 0x40 +#define ENC_EIE_DMAIE 0x20 +#define ENC_EIE_LINKIE 0x10 +#define ENC_EIE_TXIE 0x08 +#define ENC_EIE_WOLIE 0x04 +#define ENC_EIE_TXERIE 0x02 +#define ENC_EIE_RXERIE 0x01 + +/* MACON1 Register Bits */ +#define ENC_MACON1_LOOPBK 0x10 +#define ENC_MACON1_TXPAUS 0x08 +#define ENC_MACON1_RXPAUS 0x04 +#define ENC_MACON1_PASSALL 0x02 +#define ENC_MACON1_MARXEN 0x01 + +/* MACON2 Register Bits */ +#define ENC_MACON2_MARST 0x80 +#define ENC_MACON2_RNDRST 0x40 +#define ENC_MACON2_MARXRST 0x08 +#define ENC_MACON2_RFUNRST 0x04 +#define ENC_MACON2_MATXRST 0x02 +#define ENC_MACON2_TFUNRST 0x01 + +/* MACON3 Register Bits */ +#define ENC_MACON3_PADCFG2 0x80 +#define ENC_MACON3_PADCFG1 0x40 +#define ENC_MACON3_PADCFG0 0x20 +#define ENC_MACON3_TXCRCEN 0x10 +#define ENC_MACON3_PHDRLEN 0x08 +#define ENC_MACON3_HFRMEN 0x04 +#define ENC_MACON3_FRMLNEN 0x02 +#define ENC_MACON3_FULDPX 0x01 + +/* MACON4 Register Bits */ +#define ENC_MACON4_DEFER 0x40 + +/* MICMD Register Bits */ +#define ENC_MICMD_MIISCAN 0x02 +#define ENC_MICMD_MIIRD 0x01 + +/* MISTAT Register Bits */ +#define ENC_MISTAT_NVALID 0x04 +#define ENC_MISTAT_SCAN 0x02 +#define ENC_MISTAT_BUSY 0x01 + +/* PHID1 and PHID2 values */ +#define ENC_PHID1_VALUE 0x0083 +#define ENC_PHID2_VALUE 0x1400 +#define ENC_PHID2_MASK 0xFC00 + +/* PHCON1 values */ +#define ENC_PHCON1_PDPXMD 0x0100 + +/* PHSTAT1 values */ +#define ENC_PHSTAT1_LLSTAT 0x0004 + +/* PHSTAT2 values */ +#define ENC_PHSTAT2_LSTAT 0x0400 +#define ENC_PHSTAT2_DPXSTAT 0x0200 + +#endif diff --git a/qemu/roms/u-boot/drivers/net/ep93xx_eth.c b/qemu/roms/u-boot/drivers/net/ep93xx_eth.c new file mode 100644 index 000000000..1c09f1004 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ep93xx_eth.c @@ -0,0 +1,639 @@ +/* + * Cirrus Logic EP93xx ethernet MAC / MII driver. + * + * Copyright (C) 2010, 2009 + * Matthias Kaehlcke + * + * Copyright (C) 2004, 2005 + * Cory T. Tusar, Videon Central, Inc., + * + * Based on the original eth.[ch] Cirrus Logic EP93xx Rev D. Ethernet Driver, + * which is + * + * (C) Copyright 2002 2003 + * Adam Bezanson, Network Audio Technologies, Inc. + * + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ep93xx_eth.h" + +#define GET_PRIV(eth_dev) ((struct ep93xx_priv *)(eth_dev)->priv) +#define GET_REGS(eth_dev) (GET_PRIV(eth_dev)->regs) + +/* ep93xx_miiphy ops forward declarations */ +static int ep93xx_miiphy_read(const char * const dev, unsigned char const addr, + unsigned char const reg, unsigned short * const value); +static int ep93xx_miiphy_write(const char * const dev, unsigned char const addr, + unsigned char const reg, unsigned short const value); + +#if defined(EP93XX_MAC_DEBUG) +/** + * Dump ep93xx_mac values to the terminal. + */ +static void dump_dev(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_dev()\n"); + printf(" rx_dq.base %p\n", priv->rx_dq.base); + printf(" rx_dq.current %p\n", priv->rx_dq.current); + printf(" rx_dq.end %p\n", priv->rx_dq.end); + printf(" rx_sq.base %p\n", priv->rx_sq.base); + printf(" rx_sq.current %p\n", priv->rx_sq.current); + printf(" rx_sq.end %p\n", priv->rx_sq.end); + + for (i = 0; i < NUMRXDESC; i++) + printf(" rx_buffer[%2.d] %p\n", i, NetRxPackets[i]); + + printf(" tx_dq.base %p\n", priv->tx_dq.base); + printf(" tx_dq.current %p\n", priv->tx_dq.current); + printf(" tx_dq.end %p\n", priv->tx_dq.end); + printf(" tx_sq.base %p\n", priv->tx_sq.base); + printf(" tx_sq.current %p\n", priv->tx_sq.current); + printf(" tx_sq.end %p\n", priv->tx_sq.end); +} + +/** + * Dump all RX status queue entries to the terminal. + */ +static void dump_rx_status_queue(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_rx_status_queue()\n"); + printf(" descriptor address word1 word2\n"); + for (i = 0; i < NUMRXDESC; i++) { + printf(" [ %p ] %08X %08X\n", + priv->rx_sq.base + i, + (priv->rx_sq.base + i)->word1, + (priv->rx_sq.base + i)->word2); + } +} + +/** + * Dump all RX descriptor queue entries to the terminal. + */ +static void dump_rx_descriptor_queue(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_rx_descriptor_queue()\n"); + printf(" descriptor address word1 word2\n"); + for (i = 0; i < NUMRXDESC; i++) { + printf(" [ %p ] %08X %08X\n", + priv->rx_dq.base + i, + (priv->rx_dq.base + i)->word1, + (priv->rx_dq.base + i)->word2); + } +} + +/** + * Dump all TX descriptor queue entries to the terminal. + */ +static void dump_tx_descriptor_queue(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_tx_descriptor_queue()\n"); + printf(" descriptor address word1 word2\n"); + for (i = 0; i < NUMTXDESC; i++) { + printf(" [ %p ] %08X %08X\n", + priv->tx_dq.base + i, + (priv->tx_dq.base + i)->word1, + (priv->tx_dq.base + i)->word2); + } +} + +/** + * Dump all TX status queue entries to the terminal. + */ +static void dump_tx_status_queue(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_tx_status_queue()\n"); + printf(" descriptor address word1\n"); + for (i = 0; i < NUMTXDESC; i++) { + printf(" [ %p ] %08X\n", + priv->rx_sq.base + i, + (priv->rx_sq.base + i)->word1); + } +} +#else +#define dump_dev(x) +#define dump_rx_descriptor_queue(x) +#define dump_rx_status_queue(x) +#define dump_tx_descriptor_queue(x) +#define dump_tx_status_queue(x) +#endif /* defined(EP93XX_MAC_DEBUG) */ + +/** + * Reset the EP93xx MAC by twiddling the soft reset bit and spinning until + * it's cleared. + */ +static void ep93xx_mac_reset(struct eth_device *dev) +{ + struct mac_regs *mac = GET_REGS(dev); + uint32_t value; + + debug("+ep93xx_mac_reset"); + + value = readl(&mac->selfctl); + value |= SELFCTL_RESET; + writel(value, &mac->selfctl); + + while (readl(&mac->selfctl) & SELFCTL_RESET) + ; /* noop */ + + debug("-ep93xx_mac_reset"); +} + +/* Eth device open */ +static int ep93xx_eth_open(struct eth_device *dev, bd_t *bd) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + struct mac_regs *mac = GET_REGS(dev); + uchar *mac_addr = dev->enetaddr; + int i; + + debug("+ep93xx_eth_open"); + + /* Reset the MAC */ + ep93xx_mac_reset(dev); + + /* Reset the descriptor queues' current and end address values */ + priv->tx_dq.current = priv->tx_dq.base; + priv->tx_dq.end = (priv->tx_dq.base + NUMTXDESC); + + priv->tx_sq.current = priv->tx_sq.base; + priv->tx_sq.end = (priv->tx_sq.base + NUMTXDESC); + + priv->rx_dq.current = priv->rx_dq.base; + priv->rx_dq.end = (priv->rx_dq.base + NUMRXDESC); + + priv->rx_sq.current = priv->rx_sq.base; + priv->rx_sq.end = (priv->rx_sq.base + NUMRXDESC); + + /* + * Set the transmit descriptor and status queues' base address, + * current address, and length registers. Set the maximum frame + * length and threshold. Enable the transmit descriptor processor. + */ + writel((uint32_t)priv->tx_dq.base, &mac->txdq.badd); + writel((uint32_t)priv->tx_dq.base, &mac->txdq.curadd); + writel(sizeof(struct tx_descriptor) * NUMTXDESC, &mac->txdq.blen); + + writel((uint32_t)priv->tx_sq.base, &mac->txstsq.badd); + writel((uint32_t)priv->tx_sq.base, &mac->txstsq.curadd); + writel(sizeof(struct tx_status) * NUMTXDESC, &mac->txstsq.blen); + + writel(0x00040000, &mac->txdthrshld); + writel(0x00040000, &mac->txststhrshld); + + writel((TXSTARTMAX << 0) | (PKTSIZE_ALIGN << 16), &mac->maxfrmlen); + writel(BMCTL_TXEN, &mac->bmctl); + + /* + * Set the receive descriptor and status queues' base address, + * current address, and length registers. Enable the receive + * descriptor processor. + */ + writel((uint32_t)priv->rx_dq.base, &mac->rxdq.badd); + writel((uint32_t)priv->rx_dq.base, &mac->rxdq.curadd); + writel(sizeof(struct rx_descriptor) * NUMRXDESC, &mac->rxdq.blen); + + writel((uint32_t)priv->rx_sq.base, &mac->rxstsq.badd); + writel((uint32_t)priv->rx_sq.base, &mac->rxstsq.curadd); + writel(sizeof(struct rx_status) * NUMRXDESC, &mac->rxstsq.blen); + + writel(0x00040000, &mac->rxdthrshld); + + writel(BMCTL_RXEN, &mac->bmctl); + + writel(0x00040000, &mac->rxststhrshld); + + /* Wait until the receive descriptor processor is active */ + while (!(readl(&mac->bmsts) & BMSTS_RXACT)) + ; /* noop */ + + /* + * Initialize the RX descriptor queue. Clear the TX descriptor queue. + * Clear the RX and TX status queues. Enqueue the RX descriptor and + * status entries to the MAC. + */ + for (i = 0; i < NUMRXDESC; i++) { + /* set buffer address */ + (priv->rx_dq.base + i)->word1 = (uint32_t)NetRxPackets[i]; + + /* set buffer length, clear buffer index and NSOF */ + (priv->rx_dq.base + i)->word2 = PKTSIZE_ALIGN; + } + + memset(priv->tx_dq.base, 0, + (sizeof(struct tx_descriptor) * NUMTXDESC)); + memset(priv->rx_sq.base, 0, + (sizeof(struct rx_status) * NUMRXDESC)); + memset(priv->tx_sq.base, 0, + (sizeof(struct tx_status) * NUMTXDESC)); + + writel(NUMRXDESC, &mac->rxdqenq); + writel(NUMRXDESC, &mac->rxstsqenq); + + /* Set the primary MAC address */ + writel(AFP_IAPRIMARY, &mac->afp); + writel(mac_addr[0] | (mac_addr[1] << 8) | + (mac_addr[2] << 16) | (mac_addr[3] << 24), + &mac->indad); + writel(mac_addr[4] | (mac_addr[5] << 8), &mac->indad_upper); + + /* Turn on RX and TX */ + writel(RXCTL_IA0 | RXCTL_BA | RXCTL_SRXON | + RXCTL_RCRCA | RXCTL_MA, &mac->rxctl); + writel(TXCTL_STXON, &mac->txctl); + + /* Dump data structures if we're debugging */ + dump_dev(dev); + dump_rx_descriptor_queue(dev); + dump_rx_status_queue(dev); + dump_tx_descriptor_queue(dev); + dump_tx_status_queue(dev); + + debug("-ep93xx_eth_open"); + + return 1; +} + +/** + * Halt EP93xx MAC transmit and receive by clearing the TxCTL and RxCTL + * registers. + */ +static void ep93xx_eth_close(struct eth_device *dev) +{ + struct mac_regs *mac = GET_REGS(dev); + + debug("+ep93xx_eth_close"); + + writel(0x00000000, &mac->rxctl); + writel(0x00000000, &mac->txctl); + + debug("-ep93xx_eth_close"); +} + +/** + * Copy a frame of data from the MAC into the protocol layer for further + * processing. + */ +static int ep93xx_eth_rcv_packet(struct eth_device *dev) +{ + struct mac_regs *mac = GET_REGS(dev); + struct ep93xx_priv *priv = GET_PRIV(dev); + int len = -1; + + debug("+ep93xx_eth_rcv_packet"); + + if (RX_STATUS_RFP(priv->rx_sq.current)) { + if (RX_STATUS_RWE(priv->rx_sq.current)) { + /* + * We have a good frame. Extract the frame's length + * from the current rx_status_queue entry, and copy + * the frame's data into NetRxPackets[] of the + * protocol stack. We track the total number of + * bytes in the frame (nbytes_frame) which will be + * used when we pass the data off to the protocol + * layer via NetReceive(). + */ + len = RX_STATUS_FRAME_LEN(priv->rx_sq.current); + + NetReceive((uchar *)priv->rx_dq.current->word1, len); + + debug("reporting %d bytes...\n", len); + } else { + /* Do we have an erroneous packet? */ + error("packet rx error, status %08X %08X", + priv->rx_sq.current->word1, + priv->rx_sq.current->word2); + dump_rx_descriptor_queue(dev); + dump_rx_status_queue(dev); + } + + /* + * Clear the associated status queue entry, and + * increment our current pointers to the next RX + * descriptor and status queue entries (making sure + * we wrap properly). + */ + memset((void *)priv->rx_sq.current, 0, + sizeof(struct rx_status)); + + priv->rx_sq.current++; + if (priv->rx_sq.current >= priv->rx_sq.end) + priv->rx_sq.current = priv->rx_sq.base; + + priv->rx_dq.current++; + if (priv->rx_dq.current >= priv->rx_dq.end) + priv->rx_dq.current = priv->rx_dq.base; + + /* + * Finally, return the RX descriptor and status entries + * back to the MAC engine, and loop again, checking for + * more descriptors to process. + */ + writel(1, &mac->rxdqenq); + writel(1, &mac->rxstsqenq); + } else { + len = 0; + } + + debug("-ep93xx_eth_rcv_packet %d", len); + return len; +} + +/** + * Send a block of data via ethernet. + */ +static int ep93xx_eth_send_packet(struct eth_device *dev, + void * const packet, int const length) +{ + struct mac_regs *mac = GET_REGS(dev); + struct ep93xx_priv *priv = GET_PRIV(dev); + int ret = -1; + + debug("+ep93xx_eth_send_packet"); + + /* Parameter check */ + BUG_ON(packet == NULL); + + /* + * Initialize the TX descriptor queue with the new packet's info. + * Clear the associated status queue entry. Enqueue the packet + * to the MAC for transmission. + */ + + /* set buffer address */ + priv->tx_dq.current->word1 = (uint32_t)packet; + + /* set buffer length and EOF bit */ + priv->tx_dq.current->word2 = length | TX_DESC_EOF; + + /* clear tx status */ + priv->tx_sq.current->word1 = 0; + + /* enqueue the TX descriptor */ + writel(1, &mac->txdqenq); + + /* wait for the frame to become processed */ + while (!TX_STATUS_TXFP(priv->tx_sq.current)) + ; /* noop */ + + if (!TX_STATUS_TXWE(priv->tx_sq.current)) { + error("packet tx error, status %08X", + priv->tx_sq.current->word1); + dump_tx_descriptor_queue(dev); + dump_tx_status_queue(dev); + + /* TODO: Add better error handling? */ + goto eth_send_out; + } + + ret = 0; + /* Fall through */ + +eth_send_out: + debug("-ep93xx_eth_send_packet %d", ret); + return ret; +} + +#if defined(CONFIG_MII) +int ep93xx_miiphy_initialize(bd_t * const bd) +{ + miiphy_register("ep93xx_eth0", ep93xx_miiphy_read, ep93xx_miiphy_write); + return 0; +} +#endif + +/** + * Initialize the EP93xx MAC. The MAC hardware is reset. Buffers are + * allocated, if necessary, for the TX and RX descriptor and status queues, + * as well as for received packets. The EP93XX MAC hardware is initialized. + * Transmit and receive operations are enabled. + */ +int ep93xx_eth_initialize(u8 dev_num, int base_addr) +{ + int ret = -1; + struct eth_device *dev; + struct ep93xx_priv *priv; + + debug("+ep93xx_eth_initialize"); + + priv = malloc(sizeof(*priv)); + if (!priv) { + error("malloc() failed"); + goto eth_init_failed_0; + } + memset(priv, 0, sizeof(*priv)); + + priv->regs = (struct mac_regs *)base_addr; + + priv->tx_dq.base = calloc(NUMTXDESC, + sizeof(struct tx_descriptor)); + if (priv->tx_dq.base == NULL) { + error("calloc() failed"); + goto eth_init_failed_1; + } + + priv->tx_sq.base = calloc(NUMTXDESC, + sizeof(struct tx_status)); + if (priv->tx_sq.base == NULL) { + error("calloc() failed"); + goto eth_init_failed_2; + } + + priv->rx_dq.base = calloc(NUMRXDESC, + sizeof(struct rx_descriptor)); + if (priv->rx_dq.base == NULL) { + error("calloc() failed"); + goto eth_init_failed_3; + } + + priv->rx_sq.base = calloc(NUMRXDESC, + sizeof(struct rx_status)); + if (priv->rx_sq.base == NULL) { + error("calloc() failed"); + goto eth_init_failed_4; + } + + dev = malloc(sizeof *dev); + if (dev == NULL) { + error("malloc() failed"); + goto eth_init_failed_5; + } + memset(dev, 0, sizeof *dev); + + dev->iobase = base_addr; + dev->priv = priv; + dev->init = ep93xx_eth_open; + dev->halt = ep93xx_eth_close; + dev->send = ep93xx_eth_send_packet; + dev->recv = ep93xx_eth_rcv_packet; + + sprintf(dev->name, "ep93xx_eth-%hu", dev_num); + + eth_register(dev); + + /* Done! */ + ret = 1; + goto eth_init_done; + +eth_init_failed_5: + free(priv->rx_sq.base); + /* Fall through */ + +eth_init_failed_4: + free(priv->rx_dq.base); + /* Fall through */ + +eth_init_failed_3: + free(priv->tx_sq.base); + /* Fall through */ + +eth_init_failed_2: + free(priv->tx_dq.base); + /* Fall through */ + +eth_init_failed_1: + free(priv); + /* Fall through */ + +eth_init_failed_0: + /* Fall through */ + +eth_init_done: + debug("-ep93xx_eth_initialize %d", ret); + return ret; +} + +#if defined(CONFIG_MII) + +/** + * Maximum MII address we support + */ +#define MII_ADDRESS_MAX 31 + +/** + * Maximum MII register address we support + */ +#define MII_REGISTER_MAX 31 + +/** + * Read a 16-bit value from an MII register. + */ +static int ep93xx_miiphy_read(const char * const dev, unsigned char const addr, + unsigned char const reg, unsigned short * const value) +{ + struct mac_regs *mac = (struct mac_regs *)MAC_BASE; + int ret = -1; + uint32_t self_ctl; + + debug("+ep93xx_miiphy_read"); + + /* Parameter checks */ + BUG_ON(dev == NULL); + BUG_ON(addr > MII_ADDRESS_MAX); + BUG_ON(reg > MII_REGISTER_MAX); + BUG_ON(value == NULL); + + /* + * Save the current SelfCTL register value. Set MAC to suppress + * preamble bits. Wait for any previous MII command to complete + * before issuing the new command. + */ + self_ctl = readl(&mac->selfctl); +#if defined(CONFIG_MII_SUPPRESS_PREAMBLE) + writel(self_ctl & ~(1 << 8), &mac->selfctl); +#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ + + while (readl(&mac->miists) & MIISTS_BUSY) + ; /* noop */ + + /* + * Issue the MII 'read' command. Wait for the command to complete. + * Read the MII data value. + */ + writel(MIICMD_OPCODE_READ | ((uint32_t)addr << 5) | (uint32_t)reg, + &mac->miicmd); + while (readl(&mac->miists) & MIISTS_BUSY) + ; /* noop */ + + *value = (unsigned short)readl(&mac->miidata); + + /* Restore the saved SelfCTL value and return. */ + writel(self_ctl, &mac->selfctl); + + ret = 0; + /* Fall through */ + + debug("-ep93xx_miiphy_read"); + return ret; +} + +/** + * Write a 16-bit value to an MII register. + */ +static int ep93xx_miiphy_write(const char * const dev, unsigned char const addr, + unsigned char const reg, unsigned short const value) +{ + struct mac_regs *mac = (struct mac_regs *)MAC_BASE; + int ret = -1; + uint32_t self_ctl; + + debug("+ep93xx_miiphy_write"); + + /* Parameter checks */ + BUG_ON(dev == NULL); + BUG_ON(addr > MII_ADDRESS_MAX); + BUG_ON(reg > MII_REGISTER_MAX); + + /* + * Save the current SelfCTL register value. Set MAC to suppress + * preamble bits. Wait for any previous MII command to complete + * before issuing the new command. + */ + self_ctl = readl(&mac->selfctl); +#if defined(CONFIG_MII_SUPPRESS_PREAMBLE) + writel(self_ctl & ~(1 << 8), &mac->selfctl); +#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ + + while (readl(&mac->miists) & MIISTS_BUSY) + ; /* noop */ + + /* Issue the MII 'write' command. Wait for the command to complete. */ + writel((uint32_t)value, &mac->miidata); + writel(MIICMD_OPCODE_WRITE | ((uint32_t)addr << 5) | (uint32_t)reg, + &mac->miicmd); + while (readl(&mac->miists) & MIISTS_BUSY) + ; /* noop */ + + /* Restore the saved SelfCTL value and return. */ + writel(self_ctl, &mac->selfctl); + + ret = 0; + /* Fall through */ + + debug("-ep93xx_miiphy_write"); + return ret; +} +#endif /* defined(CONFIG_MII) */ diff --git a/qemu/roms/u-boot/drivers/net/ep93xx_eth.h b/qemu/roms/u-boot/drivers/net/ep93xx_eth.h new file mode 100644 index 000000000..e6c949ffc --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ep93xx_eth.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2009 Matthias Kaehlcke + * + * Copyright (C) 2004, 2005 + * Cory T. Tusar, Videon Central, Inc., + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _EP93XX_ETH_H +#define _EP93XX_ETH_H + +#include + +/** + * #define this to dump device status and queue info during initialization and + * following errors. + */ +#undef EP93XX_MAC_DEBUG + +/** + * Number of descriptor and status entries in our RX queues. + * It must be power of 2 ! + */ +#define NUMRXDESC PKTBUFSRX + +/** + * Number of descriptor and status entries in our TX queues. + */ +#define NUMTXDESC 1 + +/** + * 944 = (1024 - 64) - 16, Fifo size - Minframesize - 16 (Chip FACT) + */ +#define TXSTARTMAX 944 + +/** + * Receive descriptor queue entry + */ +struct rx_descriptor { + uint32_t word1; + uint32_t word2; +}; + +/** + * Receive status queue entry + */ +struct rx_status { + uint32_t word1; + uint32_t word2; +}; + +#define RX_STATUS_RWE(rx_status) ((rx_status->word1 >> 30) & 0x01) +#define RX_STATUS_RFP(rx_status) ((rx_status->word1 >> 31) & 0x01) +#define RX_STATUS_FRAME_LEN(rx_status) (rx_status->word2 & 0xFFFF) + +/** + * Transmit descriptor queue entry + */ +struct tx_descriptor { + uint32_t word1; + uint32_t word2; +}; + +#define TX_DESC_EOF (1 << 31) + +/** + * Transmit status queue entry + */ +struct tx_status { + uint32_t word1; +}; + +#define TX_STATUS_TXWE(tx_status) (((tx_status)->word1 >> 30) & 0x01) +#define TX_STATUS_TXFP(tx_status) (((tx_status)->word1 >> 31) & 0x01) + +/** + * Transmit descriptor queue + */ +struct tx_descriptor_queue { + struct tx_descriptor *base; + struct tx_descriptor *current; + struct tx_descriptor *end; +}; + +/** + * Transmit status queue + */ +struct tx_status_queue { + struct tx_status *base; + volatile struct tx_status *current; + struct tx_status *end; +}; + +/** + * Receive descriptor queue + */ +struct rx_descriptor_queue { + struct rx_descriptor *base; + struct rx_descriptor *current; + struct rx_descriptor *end; +}; + +/** + * Receive status queue + */ +struct rx_status_queue { + struct rx_status *base; + volatile struct rx_status *current; + struct rx_status *end; +}; + +/** + * EP93xx MAC private data structure + */ +struct ep93xx_priv { + struct rx_descriptor_queue rx_dq; + struct rx_status_queue rx_sq; + void *rx_buffer[NUMRXDESC]; + + struct tx_descriptor_queue tx_dq; + struct tx_status_queue tx_sq; + + struct mac_regs *regs; +}; + +#endif diff --git a/qemu/roms/u-boot/drivers/net/ethoc.c b/qemu/roms/u-boot/drivers/net/ethoc.c new file mode 100644 index 000000000..af06d4fb8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ethoc.c @@ -0,0 +1,511 @@ +/* + * Opencore 10/100 ethernet mac driver + * + * Copyright (C) 2007-2008 Avionic Design Development GmbH + * Copyright (C) 2008-2009 Avionic Design GmbH + * Thierry Reding + * Copyright (C) 2010 Thomas Chou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* register offsets */ +#define MODER 0x00 +#define INT_SOURCE 0x04 +#define INT_MASK 0x08 +#define IPGT 0x0c +#define IPGR1 0x10 +#define IPGR2 0x14 +#define PACKETLEN 0x18 +#define COLLCONF 0x1c +#define TX_BD_NUM 0x20 +#define CTRLMODER 0x24 +#define MIIMODER 0x28 +#define MIICOMMAND 0x2c +#define MIIADDRESS 0x30 +#define MIITX_DATA 0x34 +#define MIIRX_DATA 0x38 +#define MIISTATUS 0x3c +#define MAC_ADDR0 0x40 +#define MAC_ADDR1 0x44 +#define ETH_HASH0 0x48 +#define ETH_HASH1 0x4c +#define ETH_TXCTRL 0x50 + +/* mode register */ +#define MODER_RXEN (1 << 0) /* receive enable */ +#define MODER_TXEN (1 << 1) /* transmit enable */ +#define MODER_NOPRE (1 << 2) /* no preamble */ +#define MODER_BRO (1 << 3) /* broadcast address */ +#define MODER_IAM (1 << 4) /* individual address mode */ +#define MODER_PRO (1 << 5) /* promiscuous mode */ +#define MODER_IFG (1 << 6) /* interframe gap for incoming frames */ +#define MODER_LOOP (1 << 7) /* loopback */ +#define MODER_NBO (1 << 8) /* no back-off */ +#define MODER_EDE (1 << 9) /* excess defer enable */ +#define MODER_FULLD (1 << 10) /* full duplex */ +#define MODER_RESET (1 << 11) /* FIXME: reset (undocumented) */ +#define MODER_DCRC (1 << 12) /* delayed CRC enable */ +#define MODER_CRC (1 << 13) /* CRC enable */ +#define MODER_HUGE (1 << 14) /* huge packets enable */ +#define MODER_PAD (1 << 15) /* padding enabled */ +#define MODER_RSM (1 << 16) /* receive small packets */ + +/* interrupt source and mask registers */ +#define INT_MASK_TXF (1 << 0) /* transmit frame */ +#define INT_MASK_TXE (1 << 1) /* transmit error */ +#define INT_MASK_RXF (1 << 2) /* receive frame */ +#define INT_MASK_RXE (1 << 3) /* receive error */ +#define INT_MASK_BUSY (1 << 4) +#define INT_MASK_TXC (1 << 5) /* transmit control frame */ +#define INT_MASK_RXC (1 << 6) /* receive control frame */ + +#define INT_MASK_TX (INT_MASK_TXF | INT_MASK_TXE) +#define INT_MASK_RX (INT_MASK_RXF | INT_MASK_RXE) + +#define INT_MASK_ALL ( \ + INT_MASK_TXF | INT_MASK_TXE | \ + INT_MASK_RXF | INT_MASK_RXE | \ + INT_MASK_TXC | INT_MASK_RXC | \ + INT_MASK_BUSY \ + ) + +/* packet length register */ +#define PACKETLEN_MIN(min) (((min) & 0xffff) << 16) +#define PACKETLEN_MAX(max) (((max) & 0xffff) << 0) +#define PACKETLEN_MIN_MAX(min, max) (PACKETLEN_MIN(min) | \ + PACKETLEN_MAX(max)) + +/* transmit buffer number register */ +#define TX_BD_NUM_VAL(x) (((x) <= 0x80) ? (x) : 0x80) + +/* control module mode register */ +#define CTRLMODER_PASSALL (1 << 0) /* pass all receive frames */ +#define CTRLMODER_RXFLOW (1 << 1) /* receive control flow */ +#define CTRLMODER_TXFLOW (1 << 2) /* transmit control flow */ + +/* MII mode register */ +#define MIIMODER_CLKDIV(x) ((x) & 0xfe) /* needs to be an even number */ +#define MIIMODER_NOPRE (1 << 8) /* no preamble */ + +/* MII command register */ +#define MIICOMMAND_SCAN (1 << 0) /* scan status */ +#define MIICOMMAND_READ (1 << 1) /* read status */ +#define MIICOMMAND_WRITE (1 << 2) /* write control data */ + +/* MII address register */ +#define MIIADDRESS_FIAD(x) (((x) & 0x1f) << 0) +#define MIIADDRESS_RGAD(x) (((x) & 0x1f) << 8) +#define MIIADDRESS_ADDR(phy, reg) (MIIADDRESS_FIAD(phy) | \ + MIIADDRESS_RGAD(reg)) + +/* MII transmit data register */ +#define MIITX_DATA_VAL(x) ((x) & 0xffff) + +/* MII receive data register */ +#define MIIRX_DATA_VAL(x) ((x) & 0xffff) + +/* MII status register */ +#define MIISTATUS_LINKFAIL (1 << 0) +#define MIISTATUS_BUSY (1 << 1) +#define MIISTATUS_INVALID (1 << 2) + +/* TX buffer descriptor */ +#define TX_BD_CS (1 << 0) /* carrier sense lost */ +#define TX_BD_DF (1 << 1) /* defer indication */ +#define TX_BD_LC (1 << 2) /* late collision */ +#define TX_BD_RL (1 << 3) /* retransmission limit */ +#define TX_BD_RETRY_MASK (0x00f0) +#define TX_BD_RETRY(x) (((x) & 0x00f0) >> 4) +#define TX_BD_UR (1 << 8) /* transmitter underrun */ +#define TX_BD_CRC (1 << 11) /* TX CRC enable */ +#define TX_BD_PAD (1 << 12) /* pad enable */ +#define TX_BD_WRAP (1 << 13) +#define TX_BD_IRQ (1 << 14) /* interrupt request enable */ +#define TX_BD_READY (1 << 15) /* TX buffer ready */ +#define TX_BD_LEN(x) (((x) & 0xffff) << 16) +#define TX_BD_LEN_MASK (0xffff << 16) + +#define TX_BD_STATS (TX_BD_CS | TX_BD_DF | TX_BD_LC | \ + TX_BD_RL | TX_BD_RETRY_MASK | TX_BD_UR) + +/* RX buffer descriptor */ +#define RX_BD_LC (1 << 0) /* late collision */ +#define RX_BD_CRC (1 << 1) /* RX CRC error */ +#define RX_BD_SF (1 << 2) /* short frame */ +#define RX_BD_TL (1 << 3) /* too long */ +#define RX_BD_DN (1 << 4) /* dribble nibble */ +#define RX_BD_IS (1 << 5) /* invalid symbol */ +#define RX_BD_OR (1 << 6) /* receiver overrun */ +#define RX_BD_MISS (1 << 7) +#define RX_BD_CF (1 << 8) /* control frame */ +#define RX_BD_WRAP (1 << 13) +#define RX_BD_IRQ (1 << 14) /* interrupt request enable */ +#define RX_BD_EMPTY (1 << 15) +#define RX_BD_LEN(x) (((x) & 0xffff) << 16) + +#define RX_BD_STATS (RX_BD_LC | RX_BD_CRC | RX_BD_SF | RX_BD_TL | \ + RX_BD_DN | RX_BD_IS | RX_BD_OR | RX_BD_MISS) + +#define ETHOC_BUFSIZ 1536 +#define ETHOC_ZLEN 64 +#define ETHOC_BD_BASE 0x400 +#define ETHOC_TIMEOUT (HZ / 2) +#define ETHOC_MII_TIMEOUT (1 + (HZ / 5)) + +/** + * struct ethoc - driver-private device structure + * @num_tx: number of send buffers + * @cur_tx: last send buffer written + * @dty_tx: last buffer actually sent + * @num_rx: number of receive buffers + * @cur_rx: current receive buffer + */ +struct ethoc { + u32 num_tx; + u32 cur_tx; + u32 dty_tx; + u32 num_rx; + u32 cur_rx; +}; + +/** + * struct ethoc_bd - buffer descriptor + * @stat: buffer statistics + * @addr: physical memory address + */ +struct ethoc_bd { + u32 stat; + u32 addr; +}; + +static inline u32 ethoc_read(struct eth_device *dev, loff_t offset) +{ + return readl(dev->iobase + offset); +} + +static inline void ethoc_write(struct eth_device *dev, loff_t offset, u32 data) +{ + writel(data, dev->iobase + offset); +} + +static inline void ethoc_read_bd(struct eth_device *dev, int index, + struct ethoc_bd *bd) +{ + loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); + bd->stat = ethoc_read(dev, offset + 0); + bd->addr = ethoc_read(dev, offset + 4); +} + +static inline void ethoc_write_bd(struct eth_device *dev, int index, + const struct ethoc_bd *bd) +{ + loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); + ethoc_write(dev, offset + 0, bd->stat); + ethoc_write(dev, offset + 4, bd->addr); +} + +static int ethoc_set_mac_address(struct eth_device *dev) +{ + u8 *mac = dev->enetaddr; + + ethoc_write(dev, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) | + (mac[4] << 8) | (mac[5] << 0)); + ethoc_write(dev, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0)); + return 0; +} + +static inline void ethoc_ack_irq(struct eth_device *dev, u32 mask) +{ + ethoc_write(dev, INT_SOURCE, mask); +} + +static inline void ethoc_enable_rx_and_tx(struct eth_device *dev) +{ + u32 mode = ethoc_read(dev, MODER); + mode |= MODER_RXEN | MODER_TXEN; + ethoc_write(dev, MODER, mode); +} + +static inline void ethoc_disable_rx_and_tx(struct eth_device *dev) +{ + u32 mode = ethoc_read(dev, MODER); + mode &= ~(MODER_RXEN | MODER_TXEN); + ethoc_write(dev, MODER, mode); +} + +static int ethoc_init_ring(struct eth_device *dev) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + struct ethoc_bd bd; + int i; + + priv->cur_tx = 0; + priv->dty_tx = 0; + priv->cur_rx = 0; + + /* setup transmission buffers */ + bd.stat = TX_BD_IRQ | TX_BD_CRC; + + for (i = 0; i < priv->num_tx; i++) { + if (i == priv->num_tx - 1) + bd.stat |= TX_BD_WRAP; + + ethoc_write_bd(dev, i, &bd); + } + + bd.stat = RX_BD_EMPTY | RX_BD_IRQ; + + for (i = 0; i < priv->num_rx; i++) { + bd.addr = (u32)NetRxPackets[i]; + if (i == priv->num_rx - 1) + bd.stat |= RX_BD_WRAP; + + flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN); + ethoc_write_bd(dev, priv->num_tx + i, &bd); + } + + return 0; +} + +static int ethoc_reset(struct eth_device *dev) +{ + u32 mode; + + /* TODO: reset controller? */ + + ethoc_disable_rx_and_tx(dev); + + /* TODO: setup registers */ + + /* enable FCS generation and automatic padding */ + mode = ethoc_read(dev, MODER); + mode |= MODER_CRC | MODER_PAD; + ethoc_write(dev, MODER, mode); + + /* set full-duplex mode */ + mode = ethoc_read(dev, MODER); + mode |= MODER_FULLD; + ethoc_write(dev, MODER, mode); + ethoc_write(dev, IPGT, 0x15); + + ethoc_ack_irq(dev, INT_MASK_ALL); + ethoc_enable_rx_and_tx(dev); + return 0; +} + +static int ethoc_init(struct eth_device *dev, bd_t * bd) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + printf("ethoc\n"); + + priv->num_tx = 1; + priv->num_rx = PKTBUFSRX; + ethoc_write(dev, TX_BD_NUM, priv->num_tx); + ethoc_init_ring(dev); + ethoc_reset(dev); + + return 0; +} + +static int ethoc_update_rx_stats(struct ethoc_bd *bd) +{ + int ret = 0; + + if (bd->stat & RX_BD_TL) { + debug("ETHOC: " "RX: frame too long\n"); + ret++; + } + + if (bd->stat & RX_BD_SF) { + debug("ETHOC: " "RX: frame too short\n"); + ret++; + } + + if (bd->stat & RX_BD_DN) + debug("ETHOC: " "RX: dribble nibble\n"); + + if (bd->stat & RX_BD_CRC) { + debug("ETHOC: " "RX: wrong CRC\n"); + ret++; + } + + if (bd->stat & RX_BD_OR) { + debug("ETHOC: " "RX: overrun\n"); + ret++; + } + + if (bd->stat & RX_BD_LC) { + debug("ETHOC: " "RX: late collision\n"); + ret++; + } + + return ret; +} + +static int ethoc_rx(struct eth_device *dev, int limit) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + int count; + + for (count = 0; count < limit; ++count) { + u32 entry; + struct ethoc_bd bd; + + entry = priv->num_tx + (priv->cur_rx % priv->num_rx); + ethoc_read_bd(dev, entry, &bd); + if (bd.stat & RX_BD_EMPTY) + break; + + debug("%s(): RX buffer %d, %x received\n", + __func__, priv->cur_rx, bd.stat); + if (ethoc_update_rx_stats(&bd) == 0) { + int size = bd.stat >> 16; + size -= 4; /* strip the CRC */ + NetReceive((void *)bd.addr, size); + } + + /* clear the buffer descriptor so it can be reused */ + flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN); + bd.stat &= ~RX_BD_STATS; + bd.stat |= RX_BD_EMPTY; + ethoc_write_bd(dev, entry, &bd); + priv->cur_rx++; + } + + return count; +} + +static int ethoc_update_tx_stats(struct ethoc_bd *bd) +{ + if (bd->stat & TX_BD_LC) + debug("ETHOC: " "TX: late collision\n"); + + if (bd->stat & TX_BD_RL) + debug("ETHOC: " "TX: retransmit limit\n"); + + if (bd->stat & TX_BD_UR) + debug("ETHOC: " "TX: underrun\n"); + + if (bd->stat & TX_BD_CS) + debug("ETHOC: " "TX: carrier sense lost\n"); + + return 0; +} + +static void ethoc_tx(struct eth_device *dev) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + u32 entry = priv->dty_tx % priv->num_tx; + struct ethoc_bd bd; + + ethoc_read_bd(dev, entry, &bd); + if ((bd.stat & TX_BD_READY) == 0) + (void)ethoc_update_tx_stats(&bd); +} + +static int ethoc_send(struct eth_device *dev, void *packet, int length) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + struct ethoc_bd bd; + u32 entry; + u32 pending; + int tmo; + + entry = priv->cur_tx % priv->num_tx; + ethoc_read_bd(dev, entry, &bd); + if (unlikely(length < ETHOC_ZLEN)) + bd.stat |= TX_BD_PAD; + else + bd.stat &= ~TX_BD_PAD; + bd.addr = (u32)packet; + + flush_dcache_range(bd.addr, bd.addr + length); + bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK); + bd.stat |= TX_BD_LEN(length); + ethoc_write_bd(dev, entry, &bd); + + /* start transmit */ + bd.stat |= TX_BD_READY; + ethoc_write_bd(dev, entry, &bd); + + /* wait for transfer to succeed */ + tmo = get_timer(0) + 5 * CONFIG_SYS_HZ; + while (1) { + pending = ethoc_read(dev, INT_SOURCE); + ethoc_ack_irq(dev, pending & ~INT_MASK_RX); + if (pending & INT_MASK_BUSY) + debug("%s(): packet dropped\n", __func__); + + if (pending & INT_MASK_TX) { + ethoc_tx(dev); + break; + } + if (get_timer(0) >= tmo) { + debug("%s(): timed out\n", __func__); + return -1; + } + } + + debug("%s(): packet sent\n", __func__); + return 0; +} + +static void ethoc_halt(struct eth_device *dev) +{ + ethoc_disable_rx_and_tx(dev); +} + +static int ethoc_recv(struct eth_device *dev) +{ + u32 pending; + + pending = ethoc_read(dev, INT_SOURCE); + ethoc_ack_irq(dev, pending); + if (pending & INT_MASK_BUSY) + debug("%s(): packet dropped\n", __func__); + if (pending & INT_MASK_RX) { + debug("%s(): rx irq\n", __func__); + ethoc_rx(dev, PKTBUFSRX); + } + + return 0; +} + +int ethoc_initialize(u8 dev_num, int base_addr) +{ + struct ethoc *priv; + struct eth_device *dev; + + priv = malloc(sizeof(*priv)); + if (!priv) + return 0; + dev = malloc(sizeof(*dev)); + if (!dev) { + free(priv); + return 0; + } + + memset(dev, 0, sizeof(*dev)); + dev->priv = priv; + dev->iobase = base_addr; + dev->init = ethoc_init; + dev->halt = ethoc_halt; + dev->send = ethoc_send; + dev->recv = ethoc_recv; + dev->write_hwaddr = ethoc_set_mac_address; + sprintf(dev->name, "%s-%hu", "ETHOC", dev_num); + + eth_register(dev); + return 1; +} diff --git a/qemu/roms/u-boot/drivers/net/fec_mxc.c b/qemu/roms/u-boot/drivers/net/fec_mxc.c new file mode 100644 index 000000000..4cefda48e --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fec_mxc.c @@ -0,0 +1,1102 @@ +/* + * (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd + * (C) Copyright 2008,2009 Eric Jarrige + * (C) Copyright 2008 Armadeus Systems nc + * (C) Copyright 2007 Pengutronix, Sascha Hauer + * (C) Copyright 2007 Pengutronix, Juergen Beisert + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "fec_mxc.h" + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Timeout the transfer after 5 mS. This is usually a bit more, since + * the code in the tightloops this timeout is used in adds some overhead. + */ +#define FEC_XFER_TIMEOUT 5000 + +#ifndef CONFIG_MII +#error "CONFIG_MII has to be defined!" +#endif + +#ifndef CONFIG_FEC_XCV_TYPE +#define CONFIG_FEC_XCV_TYPE MII100 +#endif + +/* + * The i.MX28 operates with packets in big endian. We need to swap them before + * sending and after receiving. + */ +#ifdef CONFIG_MX28 +#define CONFIG_FEC_MXC_SWAP_PACKET +#endif + +#define RXDESC_PER_CACHELINE (ARCH_DMA_MINALIGN/sizeof(struct fec_bd)) + +/* Check various alignment issues at compile time */ +#if ((ARCH_DMA_MINALIGN < 16) || (ARCH_DMA_MINALIGN % 16 != 0)) +#error "ARCH_DMA_MINALIGN must be multiple of 16!" +#endif + +#if ((PKTALIGN < ARCH_DMA_MINALIGN) || \ + (PKTALIGN % ARCH_DMA_MINALIGN != 0)) +#error "PKTALIGN must be multiple of ARCH_DMA_MINALIGN!" +#endif + +#undef DEBUG + +struct nbuf { + uint8_t data[1500]; /**< actual data */ + int length; /**< actual length */ + int used; /**< buffer in use or not */ + uint8_t head[16]; /**< MAC header(6 + 6 + 2) + 2(aligned) */ +}; + +#ifdef CONFIG_FEC_MXC_SWAP_PACKET +static void swap_packet(uint32_t *packet, int length) +{ + int i; + + for (i = 0; i < DIV_ROUND_UP(length, 4); i++) + packet[i] = __swab32(packet[i]); +} +#endif + +/* + * MII-interface related functions + */ +static int fec_mdio_read(struct ethernet_regs *eth, uint8_t phyAddr, + uint8_t regAddr) +{ + uint32_t reg; /* convenient holder for the PHY register */ + uint32_t phy; /* convenient holder for the PHY */ + uint32_t start; + int val; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + writel(FEC_IEVENT_MII, ð->ievent); + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + writel(FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | + phy | reg, ð->mii_data); + + /* + * wait for the related interrupt + */ + start = get_timer(0); + while (!(readl(ð->ievent) & FEC_IEVENT_MII)) { + if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { + printf("Read MDIO failed...\n"); + return -1; + } + } + + /* + * clear mii interrupt bit + */ + writel(FEC_IEVENT_MII, ð->ievent); + + /* + * it's now safe to read the PHY's register + */ + val = (unsigned short)readl(ð->mii_data); + debug("%s: phy: %02x reg:%02x val:%#x\n", __func__, phyAddr, + regAddr, val); + return val; +} + +static void fec_mii_setspeed(struct ethernet_regs *eth) +{ + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + register u32 speed = DIV_ROUND_UP(imx_get_fecclk(), 5000000); +#ifdef FEC_QUIRK_ENET_MAC + speed--; +#endif + speed <<= 1; + writel(speed, ð->mii_speed); + debug("%s: mii_speed %08x\n", __func__, readl(ð->mii_speed)); +} + +static int fec_mdio_write(struct ethernet_regs *eth, uint8_t phyAddr, + uint8_t regAddr, uint16_t data) +{ + uint32_t reg; /* convenient holder for the PHY register */ + uint32_t phy; /* convenient holder for the PHY */ + uint32_t start; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + writel(FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | phy | reg | data, ð->mii_data); + + /* + * wait for the MII interrupt + */ + start = get_timer(0); + while (!(readl(ð->ievent) & FEC_IEVENT_MII)) { + if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { + printf("Write MDIO failed...\n"); + return -1; + } + } + + /* + * clear MII interrupt bit + */ + writel(FEC_IEVENT_MII, ð->ievent); + debug("%s: phy: %02x reg:%02x val:%#x\n", __func__, phyAddr, + regAddr, data); + + return 0; +} + +int fec_phy_read(struct mii_dev *bus, int phyAddr, int dev_addr, int regAddr) +{ + return fec_mdio_read(bus->priv, phyAddr, regAddr); +} + +int fec_phy_write(struct mii_dev *bus, int phyAddr, int dev_addr, int regAddr, + u16 data) +{ + return fec_mdio_write(bus->priv, phyAddr, regAddr, data); +} + +#ifndef CONFIG_PHYLIB +static int miiphy_restart_aneg(struct eth_device *dev) +{ + int ret = 0; +#if !defined(CONFIG_FEC_MXC_NO_ANEG) + struct fec_priv *fec = (struct fec_priv *)dev->priv; + struct ethernet_regs *eth = fec->bus->priv; + + /* + * Wake up from sleep if necessary + * Reset PHY, then delay 300ns + */ +#ifdef CONFIG_MX27 + fec_mdio_write(eth, fec->phy_id, MII_DCOUNTER, 0x00FF); +#endif + fec_mdio_write(eth, fec->phy_id, MII_BMCR, BMCR_RESET); + udelay(1000); + + /* + * Set the auto-negotiation advertisement register bits + */ + fec_mdio_write(eth, fec->phy_id, MII_ADVERTISE, + LPA_100FULL | LPA_100HALF | LPA_10FULL | + LPA_10HALF | PHY_ANLPAR_PSB_802_3); + fec_mdio_write(eth, fec->phy_id, MII_BMCR, + BMCR_ANENABLE | BMCR_ANRESTART); + + if (fec->mii_postcall) + ret = fec->mii_postcall(fec->phy_id); + +#endif + return ret; +} + +static int miiphy_wait_aneg(struct eth_device *dev) +{ + uint32_t start; + int status; + struct fec_priv *fec = (struct fec_priv *)dev->priv; + struct ethernet_regs *eth = fec->bus->priv; + + /* + * Wait for AN completion + */ + start = get_timer(0); + do { + if (get_timer(start) > (CONFIG_SYS_HZ * 5)) { + printf("%s: Autonegotiation timeout\n", dev->name); + return -1; + } + + status = fec_mdio_read(eth, fec->phy_id, MII_BMSR); + if (status < 0) { + printf("%s: Autonegotiation failed. status: %d\n", + dev->name, status); + return -1; + } + } while (!(status & BMSR_LSTATUS)); + + return 0; +} +#endif + +static int fec_rx_task_enable(struct fec_priv *fec) +{ + writel(FEC_R_DES_ACTIVE_RDAR, &fec->eth->r_des_active); + return 0; +} + +static int fec_rx_task_disable(struct fec_priv *fec) +{ + return 0; +} + +static int fec_tx_task_enable(struct fec_priv *fec) +{ + writel(FEC_X_DES_ACTIVE_TDAR, &fec->eth->x_des_active); + return 0; +} + +static int fec_tx_task_disable(struct fec_priv *fec) +{ + return 0; +} + +/** + * Initialize receive task's buffer descriptors + * @param[in] fec all we know about the device yet + * @param[in] count receive buffer count to be allocated + * @param[in] dsize desired size of each receive buffer + * @return 0 on success + * + * Init all RX descriptors to default values. + */ +static void fec_rbd_init(struct fec_priv *fec, int count, int dsize) +{ + uint32_t size; + uint8_t *data; + int i; + + /* + * Reload the RX descriptors with default values and wipe + * the RX buffers. + */ + size = roundup(dsize, ARCH_DMA_MINALIGN); + for (i = 0; i < count; i++) { + data = (uint8_t *)fec->rbd_base[i].data_pointer; + memset(data, 0, dsize); + flush_dcache_range((uint32_t)data, (uint32_t)data + size); + + fec->rbd_base[i].status = FEC_RBD_EMPTY; + fec->rbd_base[i].data_length = 0; + } + + /* Mark the last RBD to close the ring. */ + fec->rbd_base[i - 1].status = FEC_RBD_WRAP | FEC_RBD_EMPTY; + fec->rbd_index = 0; + + flush_dcache_range((unsigned)fec->rbd_base, + (unsigned)fec->rbd_base + size); +} + +/** + * Initialize transmit task's buffer descriptors + * @param[in] fec all we know about the device yet + * + * Transmit buffers are created externally. We only have to init the BDs here.\n + * Note: There is a race condition in the hardware. When only one BD is in + * use it must be marked with the WRAP bit to use it for every transmitt. + * This bit in combination with the READY bit results into double transmit + * of each data buffer. It seems the state machine checks READY earlier then + * resetting it after the first transfer. + * Using two BDs solves this issue. + */ +static void fec_tbd_init(struct fec_priv *fec) +{ + unsigned addr = (unsigned)fec->tbd_base; + unsigned size = roundup(2 * sizeof(struct fec_bd), + ARCH_DMA_MINALIGN); + + memset(fec->tbd_base, 0, size); + fec->tbd_base[0].status = 0; + fec->tbd_base[1].status = FEC_TBD_WRAP; + fec->tbd_index = 0; + flush_dcache_range(addr, addr + size); +} + +/** + * Mark the given read buffer descriptor as free + * @param[in] last 1 if this is the last buffer descriptor in the chain, else 0 + * @param[in] pRbd buffer descriptor to mark free again + */ +static void fec_rbd_clean(int last, struct fec_bd *pRbd) +{ + unsigned short flags = FEC_RBD_EMPTY; + if (last) + flags |= FEC_RBD_WRAP; + writew(flags, &pRbd->status); + writew(0, &pRbd->data_length); +} + +static int fec_get_hwaddr(struct eth_device *dev, int dev_id, + unsigned char *mac) +{ + imx_get_mac_from_fuse(dev_id, mac); + return !is_valid_ether_addr(mac); +} + +static int fec_set_hwaddr(struct eth_device *dev) +{ + uchar *mac = dev->enetaddr; + struct fec_priv *fec = (struct fec_priv *)dev->priv; + + writel(0, &fec->eth->iaddr1); + writel(0, &fec->eth->iaddr2); + writel(0, &fec->eth->gaddr1); + writel(0, &fec->eth->gaddr2); + + /* + * Set physical address + */ + writel((mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3], + &fec->eth->paddr1); + writel((mac[4] << 24) + (mac[5] << 16) + 0x8808, &fec->eth->paddr2); + + return 0; +} + +/* + * Do initial configuration of the FEC registers + */ +static void fec_reg_setup(struct fec_priv *fec) +{ + uint32_t rcntrl; + + /* + * Set interrupt mask register + */ + writel(0x00000000, &fec->eth->imask); + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + writel(0xffffffff, &fec->eth->ievent); + + + /* + * Set FEC-Lite receive control register(R_CNTRL): + */ + + /* Start with frame length = 1518, common for all modes. */ + rcntrl = PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT; + if (fec->xcv_type != SEVENWIRE) /* xMII modes */ + rcntrl |= FEC_RCNTRL_FCE | FEC_RCNTRL_MII_MODE; + if (fec->xcv_type == RGMII) + rcntrl |= FEC_RCNTRL_RGMII; + else if (fec->xcv_type == RMII) + rcntrl |= FEC_RCNTRL_RMII; + + writel(rcntrl, &fec->eth->r_cntrl); +} + +/** + * Start the FEC engine + * @param[in] dev Our device to handle + */ +static int fec_open(struct eth_device *edev) +{ + struct fec_priv *fec = (struct fec_priv *)edev->priv; + int speed; + uint32_t addr, size; + int i; + + debug("fec_open: fec_open(dev)\n"); + /* full-duplex, heartbeat disabled */ + writel(1 << 2, &fec->eth->x_cntrl); + fec->rbd_index = 0; + + /* Invalidate all descriptors */ + for (i = 0; i < FEC_RBD_NUM - 1; i++) + fec_rbd_clean(0, &fec->rbd_base[i]); + fec_rbd_clean(1, &fec->rbd_base[i]); + + /* Flush the descriptors into RAM */ + size = roundup(FEC_RBD_NUM * sizeof(struct fec_bd), + ARCH_DMA_MINALIGN); + addr = (uint32_t)fec->rbd_base; + flush_dcache_range(addr, addr + size); + +#ifdef FEC_QUIRK_ENET_MAC + /* Enable ENET HW endian SWAP */ + writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_DBSWAP, + &fec->eth->ecntrl); + /* Enable ENET store and forward mode */ + writel(readl(&fec->eth->x_wmrk) | FEC_X_WMRK_STRFWD, + &fec->eth->x_wmrk); +#endif + /* + * Enable FEC-Lite controller + */ + writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_ETHER_EN, + &fec->eth->ecntrl); +#if defined(CONFIG_MX25) || defined(CONFIG_MX53) || defined(CONFIG_MX6SL) + udelay(100); + /* + * setup the MII gasket for RMII mode + */ + + /* disable the gasket */ + writew(0, &fec->eth->miigsk_enr); + + /* wait for the gasket to be disabled */ + while (readw(&fec->eth->miigsk_enr) & MIIGSK_ENR_READY) + udelay(2); + + /* configure gasket for RMII, 50 MHz, no loopback, and no echo */ + writew(MIIGSK_CFGR_IF_MODE_RMII, &fec->eth->miigsk_cfgr); + + /* re-enable the gasket */ + writew(MIIGSK_ENR_EN, &fec->eth->miigsk_enr); + + /* wait until MII gasket is ready */ + int max_loops = 10; + while ((readw(&fec->eth->miigsk_enr) & MIIGSK_ENR_READY) == 0) { + if (--max_loops <= 0) { + printf("WAIT for MII Gasket ready timed out\n"); + break; + } + } +#endif + +#ifdef CONFIG_PHYLIB + { + /* Start up the PHY */ + int ret = phy_startup(fec->phydev); + + if (ret) { + printf("Could not initialize PHY %s\n", + fec->phydev->dev->name); + return ret; + } + speed = fec->phydev->speed; + } +#else + miiphy_wait_aneg(edev); + speed = miiphy_speed(edev->name, fec->phy_id); + miiphy_duplex(edev->name, fec->phy_id); +#endif + +#ifdef FEC_QUIRK_ENET_MAC + { + u32 ecr = readl(&fec->eth->ecntrl) & ~FEC_ECNTRL_SPEED; + u32 rcr = readl(&fec->eth->r_cntrl) & ~FEC_RCNTRL_RMII_10T; + if (speed == _1000BASET) + ecr |= FEC_ECNTRL_SPEED; + else if (speed != _100BASET) + rcr |= FEC_RCNTRL_RMII_10T; + writel(ecr, &fec->eth->ecntrl); + writel(rcr, &fec->eth->r_cntrl); + } +#endif + debug("%s:Speed=%i\n", __func__, speed); + + /* + * Enable SmartDMA receive task + */ + fec_rx_task_enable(fec); + + udelay(100000); + return 0; +} + +static int fec_init(struct eth_device *dev, bd_t* bd) +{ + struct fec_priv *fec = (struct fec_priv *)dev->priv; + uint32_t mib_ptr = (uint32_t)&fec->eth->rmon_t_drop; + int i; + + /* Initialize MAC address */ + fec_set_hwaddr(dev); + + /* + * Setup transmit descriptors, there are two in total. + */ + fec_tbd_init(fec); + + /* Setup receive descriptors. */ + fec_rbd_init(fec, FEC_RBD_NUM, FEC_MAX_PKT_SIZE); + + fec_reg_setup(fec); + + if (fec->xcv_type != SEVENWIRE) + fec_mii_setspeed(fec->bus->priv); + + /* + * Set Opcode/Pause Duration Register + */ + writel(0x00010020, &fec->eth->op_pause); /* FIXME 0xffff0020; */ + writel(0x2, &fec->eth->x_wmrk); + /* + * Set multicast address filter + */ + writel(0x00000000, &fec->eth->gaddr1); + writel(0x00000000, &fec->eth->gaddr2); + + + /* clear MIB RAM */ + for (i = mib_ptr; i <= mib_ptr + 0xfc; i += 4) + writel(0, i); + + /* FIFO receive start register */ + writel(0x520, &fec->eth->r_fstart); + + /* size and address of each buffer */ + writel(FEC_MAX_PKT_SIZE, &fec->eth->emrbr); + writel((uint32_t)fec->tbd_base, &fec->eth->etdsr); + writel((uint32_t)fec->rbd_base, &fec->eth->erdsr); + +#ifndef CONFIG_PHYLIB + if (fec->xcv_type != SEVENWIRE) + miiphy_restart_aneg(dev); +#endif + fec_open(dev); + return 0; +} + +/** + * Halt the FEC engine + * @param[in] dev Our device to handle + */ +static void fec_halt(struct eth_device *dev) +{ + struct fec_priv *fec = (struct fec_priv *)dev->priv; + int counter = 0xffff; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + writel(FEC_TCNTRL_GTS | readl(&fec->eth->x_cntrl), + &fec->eth->x_cntrl); + + debug("eth_halt: wait for stop regs\n"); + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(readl(&fec->eth->ievent) & FEC_IEVENT_GRA))) + udelay(1); + + /* + * Disable SmartDMA tasks + */ + fec_tx_task_disable(fec); + fec_rx_task_disable(fec); + + /* + * Disable the Ethernet Controller + * Note: this will also reset the BD index counter! + */ + writel(readl(&fec->eth->ecntrl) & ~FEC_ECNTRL_ETHER_EN, + &fec->eth->ecntrl); + fec->rbd_index = 0; + fec->tbd_index = 0; + debug("eth_halt: done\n"); +} + +/** + * Transmit one frame + * @param[in] dev Our ethernet device to handle + * @param[in] packet Pointer to the data to be transmitted + * @param[in] length Data count in bytes + * @return 0 on success + */ +static int fec_send(struct eth_device *dev, void *packet, int length) +{ + unsigned int status; + uint32_t size, end; + uint32_t addr; + int timeout = FEC_XFER_TIMEOUT; + int ret = 0; + + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + struct fec_priv *fec = (struct fec_priv *)dev->priv; + + /* + * Check for valid length of data. + */ + if ((length > 1500) || (length <= 0)) { + printf("Payload (%d) too large\n", length); + return -1; + } + + /* + * Setup the transmit buffer. We are always using the first buffer for + * transmission, the second will be empty and only used to stop the DMA + * engine. We also flush the packet to RAM here to avoid cache trouble. + */ +#ifdef CONFIG_FEC_MXC_SWAP_PACKET + swap_packet((uint32_t *)packet, length); +#endif + + addr = (uint32_t)packet; + end = roundup(addr + length, ARCH_DMA_MINALIGN); + addr &= ~(ARCH_DMA_MINALIGN - 1); + flush_dcache_range(addr, end); + + writew(length, &fec->tbd_base[fec->tbd_index].data_length); + writel(addr, &fec->tbd_base[fec->tbd_index].data_pointer); + + /* + * update BD's status now + * This block: + * - is always the last in a chain (means no chain) + * - should transmitt the CRC + * - might be the last BD in the list, so the address counter should + * wrap (-> keep the WRAP flag) + */ + status = readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_WRAP; + status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + writew(status, &fec->tbd_base[fec->tbd_index].status); + + /* + * Flush data cache. This code flushes both TX descriptors to RAM. + * After this code, the descriptors will be safely in RAM and we + * can start DMA. + */ + size = roundup(2 * sizeof(struct fec_bd), ARCH_DMA_MINALIGN); + addr = (uint32_t)fec->tbd_base; + flush_dcache_range(addr, addr + size); + + /* + * Below we read the DMA descriptor's last four bytes back from the + * DRAM. This is important in order to make sure that all WRITE + * operations on the bus that were triggered by previous cache FLUSH + * have completed. + * + * Otherwise, on MX28, it is possible to observe a corruption of the + * DMA descriptors. Please refer to schematic "Figure 1-2" in MX28RM + * for the bus structure of MX28. The scenario is as follows: + * + * 1) ARM core triggers a series of WRITEs on the AHB_ARB2 bus going + * to DRAM due to flush_dcache_range() + * 2) ARM core writes the FEC registers via AHB_ARB2 + * 3) FEC DMA starts reading/writing from/to DRAM via AHB_ARB3 + * + * Note that 2) does sometimes finish before 1) due to reordering of + * WRITE accesses on the AHB bus, therefore triggering 3) before the + * DMA descriptor is fully written into DRAM. This results in occasional + * corruption of the DMA descriptor. + */ + readl(addr + size - 4); + + /* + * Enable SmartDMA transmit task + */ + fec_tx_task_enable(fec); + + /* + * Wait until frame is sent. On each turn of the wait cycle, we must + * invalidate data cache to see what's really in RAM. Also, we need + * barrier here. + */ + while (--timeout) { + if (!(readl(&fec->eth->x_des_active) & FEC_X_DES_ACTIVE_TDAR)) + break; + } + + if (!timeout) + ret = -EINVAL; + + invalidate_dcache_range(addr, addr + size); + if (readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_READY) + ret = -EINVAL; + + debug("fec_send: status 0x%x index %d ret %i\n", + readw(&fec->tbd_base[fec->tbd_index].status), + fec->tbd_index, ret); + /* for next transmission use the other buffer */ + if (fec->tbd_index) + fec->tbd_index = 0; + else + fec->tbd_index = 1; + + return ret; +} + +/** + * Pull one frame from the card + * @param[in] dev Our ethernet device to handle + * @return Length of packet read + */ +static int fec_recv(struct eth_device *dev) +{ + struct fec_priv *fec = (struct fec_priv *)dev->priv; + struct fec_bd *rbd = &fec->rbd_base[fec->rbd_index]; + unsigned long ievent; + int frame_length, len = 0; + struct nbuf *frame; + uint16_t bd_status; + uint32_t addr, size, end; + int i; + ALLOC_CACHE_ALIGN_BUFFER(uchar, buff, FEC_MAX_PKT_SIZE); + + /* + * Check if any critical events have happened + */ + ievent = readl(&fec->eth->ievent); + writel(ievent, &fec->eth->ievent); + debug("fec_recv: ievent 0x%lx\n", ievent); + if (ievent & FEC_IEVENT_BABR) { + fec_halt(dev); + fec_init(dev, fec->bd); + printf("some error: 0x%08lx\n", ievent); + return 0; + } + if (ievent & FEC_IEVENT_HBERR) { + /* Heartbeat error */ + writel(0x00000001 | readl(&fec->eth->x_cntrl), + &fec->eth->x_cntrl); + } + if (ievent & FEC_IEVENT_GRA) { + /* Graceful stop complete */ + if (readl(&fec->eth->x_cntrl) & 0x00000001) { + fec_halt(dev); + writel(~0x00000001 & readl(&fec->eth->x_cntrl), + &fec->eth->x_cntrl); + fec_init(dev, fec->bd); + } + } + + /* + * Read the buffer status. Before the status can be read, the data cache + * must be invalidated, because the data in RAM might have been changed + * by DMA. The descriptors are properly aligned to cachelines so there's + * no need to worry they'd overlap. + * + * WARNING: By invalidating the descriptor here, we also invalidate + * the descriptors surrounding this one. Therefore we can NOT change the + * contents of this descriptor nor the surrounding ones. The problem is + * that in order to mark the descriptor as processed, we need to change + * the descriptor. The solution is to mark the whole cache line when all + * descriptors in the cache line are processed. + */ + addr = (uint32_t)rbd; + addr &= ~(ARCH_DMA_MINALIGN - 1); + size = roundup(sizeof(struct fec_bd), ARCH_DMA_MINALIGN); + invalidate_dcache_range(addr, addr + size); + + bd_status = readw(&rbd->status); + debug("fec_recv: status 0x%x\n", bd_status); + + if (!(bd_status & FEC_RBD_EMPTY)) { + if ((bd_status & FEC_RBD_LAST) && !(bd_status & FEC_RBD_ERR) && + ((readw(&rbd->data_length) - 4) > 14)) { + /* + * Get buffer address and size + */ + frame = (struct nbuf *)readl(&rbd->data_pointer); + frame_length = readw(&rbd->data_length) - 4; + /* + * Invalidate data cache over the buffer + */ + addr = (uint32_t)frame; + end = roundup(addr + frame_length, ARCH_DMA_MINALIGN); + addr &= ~(ARCH_DMA_MINALIGN - 1); + invalidate_dcache_range(addr, end); + + /* + * Fill the buffer and pass it to upper layers + */ +#ifdef CONFIG_FEC_MXC_SWAP_PACKET + swap_packet((uint32_t *)frame->data, frame_length); +#endif + memcpy(buff, frame->data, frame_length); + NetReceive(buff, frame_length); + len = frame_length; + } else { + if (bd_status & FEC_RBD_ERR) + printf("error frame: 0x%08lx 0x%08x\n", + (ulong)rbd->data_pointer, + bd_status); + } + + /* + * Free the current buffer, restart the engine and move forward + * to the next buffer. Here we check if the whole cacheline of + * descriptors was already processed and if so, we mark it free + * as whole. + */ + size = RXDESC_PER_CACHELINE - 1; + if ((fec->rbd_index & size) == size) { + i = fec->rbd_index - size; + addr = (uint32_t)&fec->rbd_base[i]; + for (; i <= fec->rbd_index ; i++) { + fec_rbd_clean(i == (FEC_RBD_NUM - 1), + &fec->rbd_base[i]); + } + flush_dcache_range(addr, + addr + ARCH_DMA_MINALIGN); + } + + fec_rx_task_enable(fec); + fec->rbd_index = (fec->rbd_index + 1) % FEC_RBD_NUM; + } + debug("fec_recv: stop\n"); + + return len; +} + +static void fec_set_dev_name(char *dest, int dev_id) +{ + sprintf(dest, (dev_id == -1) ? "FEC" : "FEC%i", dev_id); +} + +static int fec_alloc_descs(struct fec_priv *fec) +{ + unsigned int size; + int i; + uint8_t *data; + + /* Allocate TX descriptors. */ + size = roundup(2 * sizeof(struct fec_bd), ARCH_DMA_MINALIGN); + fec->tbd_base = memalign(ARCH_DMA_MINALIGN, size); + if (!fec->tbd_base) + goto err_tx; + + /* Allocate RX descriptors. */ + size = roundup(FEC_RBD_NUM * sizeof(struct fec_bd), ARCH_DMA_MINALIGN); + fec->rbd_base = memalign(ARCH_DMA_MINALIGN, size); + if (!fec->rbd_base) + goto err_rx; + + memset(fec->rbd_base, 0, size); + + /* Allocate RX buffers. */ + + /* Maximum RX buffer size. */ + size = roundup(FEC_MAX_PKT_SIZE, ARCH_DMA_MINALIGN); + for (i = 0; i < FEC_RBD_NUM; i++) { + data = memalign(ARCH_DMA_MINALIGN, size); + if (!data) { + printf("%s: error allocating rxbuf %d\n", __func__, i); + goto err_ring; + } + + memset(data, 0, size); + + fec->rbd_base[i].data_pointer = (uint32_t)data; + fec->rbd_base[i].status = FEC_RBD_EMPTY; + fec->rbd_base[i].data_length = 0; + /* Flush the buffer to memory. */ + flush_dcache_range((uint32_t)data, (uint32_t)data + size); + } + + /* Mark the last RBD to close the ring. */ + fec->rbd_base[i - 1].status = FEC_RBD_WRAP | FEC_RBD_EMPTY; + + fec->rbd_index = 0; + fec->tbd_index = 0; + + return 0; + +err_ring: + for (; i >= 0; i--) + free((void *)fec->rbd_base[i].data_pointer); + free(fec->rbd_base); +err_rx: + free(fec->tbd_base); +err_tx: + return -ENOMEM; +} + +static void fec_free_descs(struct fec_priv *fec) +{ + int i; + + for (i = 0; i < FEC_RBD_NUM; i++) + free((void *)fec->rbd_base[i].data_pointer); + free(fec->rbd_base); + free(fec->tbd_base); +} + +#ifdef CONFIG_PHYLIB +int fec_probe(bd_t *bd, int dev_id, uint32_t base_addr, + struct mii_dev *bus, struct phy_device *phydev) +#else +static int fec_probe(bd_t *bd, int dev_id, uint32_t base_addr, + struct mii_dev *bus, int phy_id) +#endif +{ + struct eth_device *edev; + struct fec_priv *fec; + unsigned char ethaddr[6]; + uint32_t start; + int ret = 0; + + /* create and fill edev struct */ + edev = (struct eth_device *)malloc(sizeof(struct eth_device)); + if (!edev) { + puts("fec_mxc: not enough malloc memory for eth_device\n"); + ret = -ENOMEM; + goto err1; + } + + fec = (struct fec_priv *)malloc(sizeof(struct fec_priv)); + if (!fec) { + puts("fec_mxc: not enough malloc memory for fec_priv\n"); + ret = -ENOMEM; + goto err2; + } + + memset(edev, 0, sizeof(*edev)); + memset(fec, 0, sizeof(*fec)); + + ret = fec_alloc_descs(fec); + if (ret) + goto err3; + + edev->priv = fec; + edev->init = fec_init; + edev->send = fec_send; + edev->recv = fec_recv; + edev->halt = fec_halt; + edev->write_hwaddr = fec_set_hwaddr; + + fec->eth = (struct ethernet_regs *)base_addr; + fec->bd = bd; + + fec->xcv_type = CONFIG_FEC_XCV_TYPE; + + /* Reset chip. */ + writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_RESET, &fec->eth->ecntrl); + start = get_timer(0); + while (readl(&fec->eth->ecntrl) & FEC_ECNTRL_RESET) { + if (get_timer(start) > (CONFIG_SYS_HZ * 5)) { + printf("FEC MXC: Timeout reseting chip\n"); + goto err4; + } + udelay(10); + } + + fec_reg_setup(fec); + fec_set_dev_name(edev->name, dev_id); + fec->dev_id = (dev_id == -1) ? 0 : dev_id; + fec->bus = bus; + fec_mii_setspeed(bus->priv); +#ifdef CONFIG_PHYLIB + fec->phydev = phydev; + phy_connect_dev(phydev, edev); + /* Configure phy */ + phy_config(phydev); +#else + fec->phy_id = phy_id; +#endif + eth_register(edev); + + if (fec_get_hwaddr(edev, dev_id, ethaddr) == 0) { + debug("got MAC%d address from fuse: %pM\n", dev_id, ethaddr); + memcpy(edev->enetaddr, ethaddr, 6); + if (!getenv("ethaddr")) + eth_setenv_enetaddr("ethaddr", ethaddr); + } + return ret; +err4: + fec_free_descs(fec); +err3: + free(fec); +err2: + free(edev); +err1: + return ret; +} + +struct mii_dev *fec_get_miibus(uint32_t base_addr, int dev_id) +{ + struct ethernet_regs *eth = (struct ethernet_regs *)base_addr; + struct mii_dev *bus; + int ret; + + bus = mdio_alloc(); + if (!bus) { + printf("mdio_alloc failed\n"); + return NULL; + } + bus->read = fec_phy_read; + bus->write = fec_phy_write; + bus->priv = eth; + fec_set_dev_name(bus->name, dev_id); + + ret = mdio_register(bus); + if (ret) { + printf("mdio_register failed\n"); + free(bus); + return NULL; + } + fec_mii_setspeed(eth); + return bus; +} + +int fecmxc_initialize_multi(bd_t *bd, int dev_id, int phy_id, uint32_t addr) +{ + uint32_t base_mii; + struct mii_dev *bus = NULL; +#ifdef CONFIG_PHYLIB + struct phy_device *phydev = NULL; +#endif + int ret; + +#ifdef CONFIG_MX28 + /* + * The i.MX28 has two ethernet interfaces, but they are not equal. + * Only the first one can access the MDIO bus. + */ + base_mii = MXS_ENET0_BASE; +#else + base_mii = addr; +#endif + debug("eth_init: fec_probe(bd, %i, %i) @ %08x\n", dev_id, phy_id, addr); + bus = fec_get_miibus(base_mii, dev_id); + if (!bus) + return -ENOMEM; +#ifdef CONFIG_PHYLIB + phydev = phy_find_by_mask(bus, 1 << phy_id, PHY_INTERFACE_MODE_RGMII); + if (!phydev) { + free(bus); + return -ENOMEM; + } + ret = fec_probe(bd, dev_id, addr, bus, phydev); +#else + ret = fec_probe(bd, dev_id, addr, bus, phy_id); +#endif + if (ret) { +#ifdef CONFIG_PHYLIB + free(phydev); +#endif + free(bus); + } + return ret; +} + +#ifdef CONFIG_FEC_MXC_PHYADDR +int fecmxc_initialize(bd_t *bd) +{ + return fecmxc_initialize_multi(bd, -1, CONFIG_FEC_MXC_PHYADDR, + IMX_FEC_BASE); +} +#endif + +#ifndef CONFIG_PHYLIB +int fecmxc_register_mii_postcall(struct eth_device *dev, int (*cb)(int)) +{ + struct fec_priv *fec = (struct fec_priv *)dev->priv; + fec->mii_postcall = cb; + return 0; +} +#endif diff --git a/qemu/roms/u-boot/drivers/net/fec_mxc.h b/qemu/roms/u-boot/drivers/net/fec_mxc.h new file mode 100644 index 000000000..0717cc6c3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fec_mxc.h @@ -0,0 +1,323 @@ +/* + * (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd + * (C) Copyright 2008 Armadeus Systems, nc + * (C) Copyright 2008 Eric Jarrige + * (C) Copyright 2007 Pengutronix, Sascha Hauer + * (C) Copyright 2007 Pengutronix, Juergen Beisert + * + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.h + * (C) Copyright Motorola, Inc., 2000 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#ifndef __FEC_MXC_H +#define __FEC_MXC_H + +void imx_get_mac_from_fuse(int dev_id, unsigned char *mac); + +/** + * Layout description of the FEC + */ +struct ethernet_regs { + +/* [10:2]addr = 00 */ + +/* Control and status Registers (offset 000-1FF) */ + + uint32_t res0[1]; /* MBAR_ETH + 0x000 */ + uint32_t ievent; /* MBAR_ETH + 0x004 */ + uint32_t imask; /* MBAR_ETH + 0x008 */ + + uint32_t res1[1]; /* MBAR_ETH + 0x00C */ + uint32_t r_des_active; /* MBAR_ETH + 0x010 */ + uint32_t x_des_active; /* MBAR_ETH + 0x014 */ + uint32_t res2[3]; /* MBAR_ETH + 0x018-20 */ + uint32_t ecntrl; /* MBAR_ETH + 0x024 */ + + uint32_t res3[6]; /* MBAR_ETH + 0x028-03C */ + uint32_t mii_data; /* MBAR_ETH + 0x040 */ + uint32_t mii_speed; /* MBAR_ETH + 0x044 */ + uint32_t res4[7]; /* MBAR_ETH + 0x048-60 */ + uint32_t mib_control; /* MBAR_ETH + 0x064 */ + + uint32_t res5[7]; /* MBAR_ETH + 0x068-80 */ + uint32_t r_cntrl; /* MBAR_ETH + 0x084 */ + uint32_t res6[15]; /* MBAR_ETH + 0x088-C0 */ + uint32_t x_cntrl; /* MBAR_ETH + 0x0C4 */ + uint32_t res7[7]; /* MBAR_ETH + 0x0C8-E0 */ + uint32_t paddr1; /* MBAR_ETH + 0x0E4 */ + uint32_t paddr2; /* MBAR_ETH + 0x0E8 */ + uint32_t op_pause; /* MBAR_ETH + 0x0EC */ + + uint32_t res8[10]; /* MBAR_ETH + 0x0F0-114 */ + uint32_t iaddr1; /* MBAR_ETH + 0x118 */ + uint32_t iaddr2; /* MBAR_ETH + 0x11C */ + uint32_t gaddr1; /* MBAR_ETH + 0x120 */ + uint32_t gaddr2; /* MBAR_ETH + 0x124 */ + uint32_t res9[7]; /* MBAR_ETH + 0x128-140 */ + + uint32_t x_wmrk; /* MBAR_ETH + 0x144 */ + uint32_t res10[1]; /* MBAR_ETH + 0x148 */ + uint32_t r_bound; /* MBAR_ETH + 0x14C */ + uint32_t r_fstart; /* MBAR_ETH + 0x150 */ + uint32_t res11[11]; /* MBAR_ETH + 0x154-17C */ + uint32_t erdsr; /* MBAR_ETH + 0x180 */ + uint32_t etdsr; /* MBAR_ETH + 0x184 */ + uint32_t emrbr; /* MBAR_ETH + 0x188 */ + uint32_t res12[29]; /* MBAR_ETH + 0x18C-1FC */ + +/* MIB COUNTERS (Offset 200-2FF) */ + + uint32_t rmon_t_drop; /* MBAR_ETH + 0x200 */ + uint32_t rmon_t_packets; /* MBAR_ETH + 0x204 */ + uint32_t rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ + uint32_t rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ + uint32_t rmon_t_crc_align; /* MBAR_ETH + 0x210 */ + uint32_t rmon_t_undersize; /* MBAR_ETH + 0x214 */ + uint32_t rmon_t_oversize; /* MBAR_ETH + 0x218 */ + uint32_t rmon_t_frag; /* MBAR_ETH + 0x21C */ + uint32_t rmon_t_jab; /* MBAR_ETH + 0x220 */ + uint32_t rmon_t_col; /* MBAR_ETH + 0x224 */ + uint32_t rmon_t_p64; /* MBAR_ETH + 0x228 */ + uint32_t rmon_t_p65to127; /* MBAR_ETH + 0x22C */ + uint32_t rmon_t_p128to255; /* MBAR_ETH + 0x230 */ + uint32_t rmon_t_p256to511; /* MBAR_ETH + 0x234 */ + uint32_t rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ + uint32_t rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */ + uint32_t rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ + uint32_t rmon_t_octets; /* MBAR_ETH + 0x244 */ + uint32_t ieee_t_drop; /* MBAR_ETH + 0x248 */ + uint32_t ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ + uint32_t ieee_t_1col; /* MBAR_ETH + 0x250 */ + uint32_t ieee_t_mcol; /* MBAR_ETH + 0x254 */ + uint32_t ieee_t_def; /* MBAR_ETH + 0x258 */ + uint32_t ieee_t_lcol; /* MBAR_ETH + 0x25C */ + uint32_t ieee_t_excol; /* MBAR_ETH + 0x260 */ + uint32_t ieee_t_macerr; /* MBAR_ETH + 0x264 */ + uint32_t ieee_t_cserr; /* MBAR_ETH + 0x268 */ + uint32_t ieee_t_sqe; /* MBAR_ETH + 0x26C */ + uint32_t t_fdxfc; /* MBAR_ETH + 0x270 */ + uint32_t ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ + + uint32_t res13[2]; /* MBAR_ETH + 0x278-27C */ + uint32_t rmon_r_drop; /* MBAR_ETH + 0x280 */ + uint32_t rmon_r_packets; /* MBAR_ETH + 0x284 */ + uint32_t rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ + uint32_t rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ + uint32_t rmon_r_crc_align; /* MBAR_ETH + 0x290 */ + uint32_t rmon_r_undersize; /* MBAR_ETH + 0x294 */ + uint32_t rmon_r_oversize; /* MBAR_ETH + 0x298 */ + uint32_t rmon_r_frag; /* MBAR_ETH + 0x29C */ + uint32_t rmon_r_jab; /* MBAR_ETH + 0x2A0 */ + + uint32_t rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ + + uint32_t rmon_r_p64; /* MBAR_ETH + 0x2A8 */ + uint32_t rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ + uint32_t rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ + uint32_t rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ + uint32_t rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ + uint32_t rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */ + uint32_t rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ + uint32_t rmon_r_octets; /* MBAR_ETH + 0x2C4 */ + uint32_t ieee_r_drop; /* MBAR_ETH + 0x2C8 */ + uint32_t ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ + uint32_t ieee_r_crc; /* MBAR_ETH + 0x2D0 */ + uint32_t ieee_r_align; /* MBAR_ETH + 0x2D4 */ + uint32_t r_macerr; /* MBAR_ETH + 0x2D8 */ + uint32_t r_fdxfc; /* MBAR_ETH + 0x2DC */ + uint32_t ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ + + uint32_t res14[7]; /* MBAR_ETH + 0x2E4-2FC */ + +#if defined(CONFIG_MX25) || defined(CONFIG_MX53) || defined(CONFIG_MX6SL) + uint16_t miigsk_cfgr; /* MBAR_ETH + 0x300 */ + uint16_t res15[3]; /* MBAR_ETH + 0x302-306 */ + uint16_t miigsk_enr; /* MBAR_ETH + 0x308 */ + uint16_t res16[3]; /* MBAR_ETH + 0x30a-30e */ + uint32_t res17[60]; /* MBAR_ETH + 0x300-3FF */ +#else + uint32_t res15[64]; /* MBAR_ETH + 0x300-3FF */ +#endif +}; + +#define FEC_IEVENT_HBERR 0x80000000 +#define FEC_IEVENT_BABR 0x40000000 +#define FEC_IEVENT_BABT 0x20000000 +#define FEC_IEVENT_GRA 0x10000000 +#define FEC_IEVENT_TXF 0x08000000 +#define FEC_IEVENT_TXB 0x04000000 +#define FEC_IEVENT_RXF 0x02000000 +#define FEC_IEVENT_RXB 0x01000000 +#define FEC_IEVENT_MII 0x00800000 +#define FEC_IEVENT_EBERR 0x00400000 +#define FEC_IEVENT_LC 0x00200000 +#define FEC_IEVENT_RL 0x00100000 +#define FEC_IEVENT_UN 0x00080000 + +#define FEC_IMASK_HBERR 0x80000000 +#define FEC_IMASK_BABR 0x40000000 +#define FEC_IMASKT_BABT 0x20000000 +#define FEC_IMASK_GRA 0x10000000 +#define FEC_IMASKT_TXF 0x08000000 +#define FEC_IMASK_TXB 0x04000000 +#define FEC_IMASKT_RXF 0x02000000 +#define FEC_IMASK_RXB 0x01000000 +#define FEC_IMASK_MII 0x00800000 +#define FEC_IMASK_EBERR 0x00400000 +#define FEC_IMASK_LC 0x00200000 +#define FEC_IMASKT_RL 0x00100000 +#define FEC_IMASK_UN 0x00080000 + + +#define FEC_RCNTRL_MAX_FL_SHIFT 16 +#define FEC_RCNTRL_LOOP 0x00000001 +#define FEC_RCNTRL_DRT 0x00000002 +#define FEC_RCNTRL_MII_MODE 0x00000004 +#define FEC_RCNTRL_PROM 0x00000008 +#define FEC_RCNTRL_BC_REJ 0x00000010 +#define FEC_RCNTRL_FCE 0x00000020 +#define FEC_RCNTRL_RGMII 0x00000040 +#define FEC_RCNTRL_RMII 0x00000100 +#define FEC_RCNTRL_RMII_10T 0x00000200 + +#define FEC_TCNTRL_GTS 0x00000001 +#define FEC_TCNTRL_HBC 0x00000002 +#define FEC_TCNTRL_FDEN 0x00000004 +#define FEC_TCNTRL_TFC_PAUSE 0x00000008 +#define FEC_TCNTRL_RFC_PAUSE 0x00000010 + +#define FEC_ECNTRL_RESET 0x00000001 /* reset the FEC */ +#define FEC_ECNTRL_ETHER_EN 0x00000002 /* enable the FEC */ +#define FEC_ECNTRL_SPEED 0x00000020 +#define FEC_ECNTRL_DBSWAP 0x00000100 + +#define FEC_X_WMRK_STRFWD 0x00000100 + +#define FEC_X_DES_ACTIVE_TDAR 0x01000000 +#define FEC_R_DES_ACTIVE_RDAR 0x01000000 + +#if defined(CONFIG_MX25) || defined(CONFIG_MX53) || defined(CONFIG_MX6SL) +/* defines for MIIGSK */ +/* RMII frequency control: 0=50MHz, 1=5MHz */ +#define MIIGSK_CFGR_FRCONT (1 << 6) +/* loopback mode */ +#define MIIGSK_CFGR_LBMODE (1 << 4) +/* echo mode */ +#define MIIGSK_CFGR_EMODE (1 << 3) +/* MII gasket mode field */ +#define MIIGSK_CFGR_IF_MODE_MASK (3 << 0) +/* MMI/7-Wire mode */ +#define MIIGSK_CFGR_IF_MODE_MII (0 << 0) +/* RMII mode */ +#define MIIGSK_CFGR_IF_MODE_RMII (1 << 0) +/* reflects MIIGSK Enable bit (RO) */ +#define MIIGSK_ENR_READY (1 << 2) +/* enable MIGSK (set by default) */ +#define MIIGSK_ENR_EN (1 << 1) +#endif + +/** + * @brief Receive & Transmit Buffer Descriptor definitions + * + * Note: The first BD must be aligned (see DB_ALIGNMENT) + */ +struct fec_bd { + uint16_t data_length; /* payload's length in bytes */ + uint16_t status; /* BD's staus (see datasheet) */ + uint32_t data_pointer; /* payload's buffer address */ +}; + +/** + * Supported phy types on this platform + */ +enum xceiver_type { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100, /* MII 100Mbps */ + RMII, /* RMII */ + RGMII, /* RGMII */ +}; + +/** + * @brief i.MX27-FEC private structure + */ +struct fec_priv { + struct ethernet_regs *eth; /* pointer to register'S base */ + enum xceiver_type xcv_type; /* transceiver type */ + struct fec_bd *rbd_base; /* RBD ring */ + int rbd_index; /* next receive BD to read */ + struct fec_bd *tbd_base; /* TBD ring */ + int tbd_index; /* next transmit BD to write */ + bd_t *bd; + uint8_t *tdb_ptr; + int dev_id; + struct mii_dev *bus; +#ifdef CONFIG_PHYLIB + struct phy_device *phydev; +#else + int phy_id; + int (*mii_postcall)(int); +#endif +}; + +/** + * @brief Numbers of buffer descriptors for receiving + * + * The number defines the stocked memory buffers for the receiving task. + * Larger values makes no sense in this limited environment. + */ +#define FEC_RBD_NUM 64 + +/** + * @brief Define the ethernet packet size limit in memory + * + * Note: Do not shrink this number. This will force the FEC to spread larger + * frames in more than one BD. This is nothing to worry about, but the current + * driver can't handle it. + */ +#define FEC_MAX_PKT_SIZE 1536 + +/* Receive BD status bits */ +#define FEC_RBD_EMPTY 0x8000 /* Receive BD status: Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Receive BD status: Last BD in ring */ +/* Receive BD status: Buffer is last in frame (useless here!) */ +#define FEC_RBD_LAST 0x0800 +#define FEC_RBD_MISS 0x0100 /* Receive BD status: Miss bit for prom mode */ +/* Receive BD status: The received frame is broadcast frame */ +#define FEC_RBD_BC 0x0080 +/* Receive BD status: The received frame is multicast frame */ +#define FEC_RBD_MC 0x0040 +#define FEC_RBD_LG 0x0020 /* Receive BD status: Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Receive BD status: Nonoctet align frame */ +#define FEC_RBD_CR 0x0004 /* Receive BD status: CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive BD status: Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Receive BD status: Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* Transmit BD status bits */ +#define FEC_TBD_READY 0x8000 /* Tansmit BD status: Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Tansmit BD status: Mark as last BD in ring */ +#define FEC_TBD_LAST 0x0800 /* Tansmit BD status: Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Tansmit BD status: Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Tansmit BD status: Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __FEC_MXC_H */ diff --git a/qemu/roms/u-boot/drivers/net/fm/Makefile b/qemu/roms/u-boot/drivers/net/fm/Makefile new file mode 100644 index 000000000..5ae3b167a --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/Makefile @@ -0,0 +1,37 @@ +# +# Copyright 2009-2011 Freescale Semiconductor, Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += dtsec.o +obj-y += eth.o +obj-y += fm.o +obj-y += init.o +obj-y += tgec.o +obj-y += tgec_phy.o + +# Soc have FMAN v3 with mEMAC +obj-$(CONFIG_SYS_FMAN_V3) += memac_phy.o +obj-$(CONFIG_SYS_FMAN_V3) += memac.o + +# SoC specific SERDES support +obj-$(CONFIG_P1017) += p1023.o +obj-$(CONFIG_P1023) += p1023.o +# The P204x, P304x, and P5020 are the same +obj-$(CONFIG_PPC_P2041) += p5020.o +obj-$(CONFIG_PPC_P3041) += p5020.o +obj-$(CONFIG_PPC_P4080) += p4080.o +obj-$(CONFIG_PPC_P5020) += p5020.o +obj-$(CONFIG_PPC_P5040) += p5040.o +obj-$(CONFIG_PPC_T1040) += t1040.o +obj-$(CONFIG_PPC_T1042) += t1040.o +obj-$(CONFIG_PPC_T1020) += t1040.o +obj-$(CONFIG_PPC_T1022) += t1040.o +obj-$(CONFIG_PPC_T2080) += t2080.o +obj-$(CONFIG_PPC_T2081) += t2080.o +obj-$(CONFIG_PPC_T4240) += t4240.o +obj-$(CONFIG_PPC_T4160) += t4240.o +obj-$(CONFIG_PPC_T4080) += t4240.o +obj-$(CONFIG_PPC_B4420) += b4860.o +obj-$(CONFIG_PPC_B4860) += b4860.o diff --git a/qemu/roms/u-boot/drivers/net/fm/b4860.c b/qemu/roms/u-boot/drivers/net/fm/b4860.c new file mode 100644 index 000000000..373cc4f42 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/b4860.c @@ -0,0 +1,75 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Roy Zang + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include + +u32 port_to_devdisr[] = { + [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, + [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2, + [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3, + [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4, + [FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5, + [FM1_DTSEC6] = FSL_CORENET_DEVDISR2_DTSEC1_6, + [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1_1, + [FM1_10GEC2] = FSL_CORENET_DEVDISR2_10GEC1_2, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return port_to_devdisr[port] & devdisr2; +} + +void fman_disable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + setbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +void fman_enable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + clrbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +phy_interface_t fman_port_enet_if(enum fm_port port) +{ + if (is_device_disabled(port)) + return PHY_INTERFACE_MODE_NONE; + + /*B4860 has two 10Gig Mac*/ + if ((port == FM1_10GEC1 || port == FM1_10GEC2) && + ((is_serdes_configured(XAUI_FM1_MAC9)) || + (is_serdes_configured(XAUI_FM1_MAC10)))) + return PHY_INTERFACE_MODE_XGMII; + + /* Fix me need to handle RGMII here first */ + + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + case FM1_DTSEC5: + case FM1_DTSEC6: + if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + default: + return PHY_INTERFACE_MODE_NONE; + } + + return PHY_INTERFACE_MODE_NONE; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/dtsec.c b/qemu/roms/u-boot/drivers/net/fm/dtsec.c new file mode 100644 index 000000000..78bbd439f --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/dtsec.c @@ -0,0 +1,168 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "fm.h" + +#define RCTRL_INIT (RCTRL_GRS | RCTRL_UPROM) +#define TCTRL_INIT TCTRL_GTS +#define MACCFG1_INIT MACCFG1_SOFT_RST + +#define MACCFG2_INIT (MACCFG2_PRE_LEN(0x7) | MACCFG2_LEN_CHECK | \ + MACCFG2_PAD_CRC | MACCFG2_FULL_DUPLEX | \ + MACCFG2_IF_MODE_NIBBLE) + +/* MAXFRM - maximum frame length register */ +#define MAXFRM_MASK 0x00003fff + +static void dtsec_init_mac(struct fsl_enet_mac *mac) +{ + struct dtsec *regs = mac->base; + + /* soft reset */ + out_be32(®s->maccfg1, MACCFG1_SOFT_RST); + udelay(1000); + + /* clear soft reset, Rx/Tx MAC disable */ + out_be32(®s->maccfg1, 0); + + /* graceful stop rx */ + out_be32(®s->rctrl, RCTRL_INIT); + udelay(1000); + + /* graceful stop tx */ + out_be32(®s->tctrl, TCTRL_INIT); + udelay(1000); + + /* disable all interrupts */ + out_be32(®s->imask, IMASK_MASK_ALL); + + /* clear all events */ + out_be32(®s->ievent, IEVENT_CLEAR_ALL); + + /* set the max Rx length */ + out_be32(®s->maxfrm, mac->max_rx_len & MAXFRM_MASK); + + /* set the ecntrl to reset value */ + out_be32(®s->ecntrl, ECNTRL_DEFAULT); + + /* + * Rx length check, no strip CRC for Rx, pad and append CRC for Tx, + * full duplex + */ + out_be32(®s->maccfg2, MACCFG2_INIT); +} + +static void dtsec_enable_mac(struct fsl_enet_mac *mac) +{ + struct dtsec *regs = mac->base; + + /* enable Rx/Tx MAC */ + setbits_be32(®s->maccfg1, MACCFG1_RXTX_EN); + + /* clear the graceful Rx stop */ + clrbits_be32(®s->rctrl, RCTRL_GRS); + + /* clear the graceful Tx stop */ + clrbits_be32(®s->tctrl, TCTRL_GTS); +} + +static void dtsec_disable_mac(struct fsl_enet_mac *mac) +{ + struct dtsec *regs = mac->base; + + /* graceful Rx stop */ + setbits_be32(®s->rctrl, RCTRL_GRS); + + /* graceful Tx stop */ + setbits_be32(®s->tctrl, TCTRL_GTS); + + /* disable Rx/Tx MAC */ + clrbits_be32(®s->maccfg1, MACCFG1_RXTX_EN); +} + +static void dtsec_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr) +{ + struct dtsec *regs = mac->base; + u32 mac_addr1, mac_addr2; + + /* + * if a station address of 0x12345678ABCD, perform a write to + * MACSTNADDR1 of 0xCDAB7856, MACSTNADDR2 of 0x34120000 + */ + mac_addr1 = (mac_addr[5] << 24) | (mac_addr[4] << 16) | \ + (mac_addr[3] << 8) | (mac_addr[2]); + out_be32(®s->macstnaddr1, mac_addr1); + + mac_addr2 = ((mac_addr[1] << 24) | (mac_addr[0] << 16)) & 0xffff0000; + out_be32(®s->macstnaddr2, mac_addr2); +} + +static void dtsec_set_interface_mode(struct fsl_enet_mac *mac, + phy_interface_t type, int speed) +{ + struct dtsec *regs = mac->base; + u32 ecntrl, maccfg2; + + /* clear all bits relative with interface mode */ + ecntrl = in_be32(®s->ecntrl); + ecntrl &= ~(ECNTRL_TBIM | ECNTRL_GMIIM | ECNTRL_RPM | + ECNTRL_R100M | ECNTRL_SGMIIM); + + maccfg2 = in_be32(®s->maccfg2); + maccfg2 &= ~MACCFG2_IF_MODE_MASK; + + if (speed == SPEED_1000) + maccfg2 |= MACCFG2_IF_MODE_BYTE; + else + maccfg2 |= MACCFG2_IF_MODE_NIBBLE; + + /* set interface mode */ + switch (type) { + case PHY_INTERFACE_MODE_GMII: + ecntrl |= ECNTRL_GMIIM; + break; + case PHY_INTERFACE_MODE_RGMII: + ecntrl |= (ECNTRL_GMIIM | ECNTRL_RPM); + if (speed == SPEED_100) + ecntrl |= ECNTRL_R100M; + break; + case PHY_INTERFACE_MODE_RMII: + if (speed == SPEED_100) + ecntrl |= ECNTRL_R100M; + break; + case PHY_INTERFACE_MODE_SGMII: + ecntrl |= (ECNTRL_SGMIIM | ECNTRL_TBIM); + if (speed == SPEED_100) + ecntrl |= ECNTRL_R100M; + break; + default: + break; + } + + out_be32(®s->ecntrl, ecntrl); + out_be32(®s->maccfg2, maccfg2); +} + +void init_dtsec(struct fsl_enet_mac *mac, void *base, + void *phyregs, int max_rx_len) +{ + mac->base = base; + mac->phyregs = phyregs; + mac->max_rx_len = max_rx_len; + mac->init_mac = dtsec_init_mac; + mac->enable_mac = dtsec_enable_mac; + mac->disable_mac = dtsec_disable_mac; + mac->set_mac_addr = dtsec_set_mac_addr; + mac->set_if_mode = dtsec_set_interface_mode; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/eth.c b/qemu/roms/u-boot/drivers/net/fm/eth.c new file mode 100644 index 000000000..218a5ed17 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/eth.c @@ -0,0 +1,712 @@ +/* + * Copyright 2009-2012 Freescale Semiconductor, Inc. + * Dave Liu + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fm.h" + +static struct eth_device *devlist[NUM_FM_PORTS]; +static int num_controllers; + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII) + +#define TBIANA_SETTINGS (TBIANA_ASYMMETRIC_PAUSE | TBIANA_SYMMETRIC_PAUSE | \ + TBIANA_FULL_DUPLEX) + +#define TBIANA_SGMII_ACK 0x4001 + +#define TBICR_SETTINGS (TBICR_ANEG_ENABLE | TBICR_RESTART_ANEG | \ + TBICR_FULL_DUPLEX | TBICR_SPEED1_SET) + +/* Configure the TBI for SGMII operation */ +static void dtsec_configure_serdes(struct fm_eth *priv) +{ +#ifdef CONFIG_SYS_FMAN_V3 + u32 value; + struct mii_dev bus; + bus.priv = priv->mac->phyregs; + + /* SGMII IF mode + AN enable */ + value = PHY_SGMII_IF_MODE_AN | PHY_SGMII_IF_MODE_SGMII; + memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x14, value); + + /* Dev ability according to SGMII specification */ + value = PHY_SGMII_DEV_ABILITY_SGMII; + memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x4, value); + + /* Adjust link timer for SGMII - + 1.6 ms in units of 8 ns = 2 * 10^5 = 0x30d40 */ + memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x3); + memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0xd40); + + /* Restart AN */ + value = PHY_SGMII_CR_DEF_VAL | PHY_SGMII_CR_RESET_AN; + memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0, value); +#else + struct dtsec *regs = priv->mac->base; + struct tsec_mii_mng *phyregs = priv->mac->phyregs; + + /* + * Access TBI PHY registers at given TSEC register offset as + * opposed to the register offset used for external PHY accesses + */ + tsec_local_mdio_write(phyregs, in_be32(®s->tbipa), 0, TBI_TBICON, + TBICON_CLK_SELECT); + tsec_local_mdio_write(phyregs, in_be32(®s->tbipa), 0, TBI_ANA, + TBIANA_SGMII_ACK); + tsec_local_mdio_write(phyregs, in_be32(®s->tbipa), 0, + TBI_CR, TBICR_SETTINGS); +#endif +} + +static void dtsec_init_phy(struct eth_device *dev) +{ + struct fm_eth *fm_eth = dev->priv; +#ifndef CONFIG_SYS_FMAN_V3 + struct dtsec *regs = (struct dtsec *)CONFIG_SYS_FSL_FM1_DTSEC1_ADDR; + + /* Assign a Physical address to the TBI */ + out_be32(®s->tbipa, CONFIG_SYS_TBIPA_VALUE); +#endif + + if (fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII) + dtsec_configure_serdes(fm_eth); +} + +static int tgec_is_fibre(struct eth_device *dev) +{ + struct fm_eth *fm = dev->priv; + char phyopt[20]; + + sprintf(phyopt, "fsl_fm%d_xaui_phy", fm->fm_index + 1); + + return hwconfig_arg_cmp(phyopt, "xfi"); +} +#endif + +static u16 muram_readw(u16 *addr) +{ + u32 base = (u32)addr & ~0x3; + u32 val32 = *(u32 *)base; + int byte_pos; + u16 ret; + + byte_pos = (u32)addr & 0x3; + if (byte_pos) + ret = (u16)(val32 & 0x0000ffff); + else + ret = (u16)((val32 & 0xffff0000) >> 16); + + return ret; +} + +static void muram_writew(u16 *addr, u16 val) +{ + u32 base = (u32)addr & ~0x3; + u32 org32 = *(u32 *)base; + u32 val32; + int byte_pos; + + byte_pos = (u32)addr & 0x3; + if (byte_pos) + val32 = (org32 & 0xffff0000) | val; + else + val32 = (org32 & 0x0000ffff) | ((u32)val << 16); + + *(u32 *)base = val32; +} + +static void bmi_rx_port_disable(struct fm_bmi_rx_port *rx_port) +{ + int timeout = 1000000; + + clrbits_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_EN); + + /* wait until the rx port is not busy */ + while ((in_be32(&rx_port->fmbm_rst) & FMBM_RST_BSY) && timeout--) + ; +} + +static void bmi_rx_port_init(struct fm_bmi_rx_port *rx_port) +{ + /* set BMI to independent mode, Rx port disable */ + out_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_IM); + /* clear FOF in IM case */ + out_be32(&rx_port->fmbm_rim, 0); + /* Rx frame next engine -RISC */ + out_be32(&rx_port->fmbm_rfne, NIA_ENG_RISC | NIA_RISC_AC_IM_RX); + /* Rx command attribute - no order, MR[3] = 1 */ + clrbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_ORDER | FMBM_RFCA_MR_MASK); + setbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_MR(4)); + /* enable Rx statistic counters */ + out_be32(&rx_port->fmbm_rstc, FMBM_RSTC_EN); + /* disable Rx performance counters */ + out_be32(&rx_port->fmbm_rpc, 0); +} + +static void bmi_tx_port_disable(struct fm_bmi_tx_port *tx_port) +{ + int timeout = 1000000; + + clrbits_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_EN); + + /* wait until the tx port is not busy */ + while ((in_be32(&tx_port->fmbm_tst) & FMBM_TST_BSY) && timeout--) + ; +} + +static void bmi_tx_port_init(struct fm_bmi_tx_port *tx_port) +{ + /* set BMI to independent mode, Tx port disable */ + out_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_IM); + /* Tx frame next engine -RISC */ + out_be32(&tx_port->fmbm_tfne, NIA_ENG_RISC | NIA_RISC_AC_IM_TX); + out_be32(&tx_port->fmbm_tfene, NIA_ENG_RISC | NIA_RISC_AC_IM_TX); + /* Tx command attribute - no order, MR[3] = 1 */ + clrbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_ORDER | FMBM_TFCA_MR_MASK); + setbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_MR(4)); + /* enable Tx statistic counters */ + out_be32(&tx_port->fmbm_tstc, FMBM_TSTC_EN); + /* disable Tx performance counters */ + out_be32(&tx_port->fmbm_tpc, 0); +} + +static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth) +{ + struct fm_port_global_pram *pram; + u32 pram_page_offset; + void *rx_bd_ring_base; + void *rx_buf_pool; + struct fm_port_bd *rxbd; + struct fm_port_qd *rxqd; + struct fm_bmi_rx_port *bmi_rx_port = fm_eth->rx_port; + int i; + + /* alloc global parameter ram at MURAM */ + pram = (struct fm_port_global_pram *)fm_muram_alloc(fm_eth->fm_index, + FM_PRAM_SIZE, FM_PRAM_ALIGN); + fm_eth->rx_pram = pram; + + /* parameter page offset to MURAM */ + pram_page_offset = (u32)pram - fm_muram_base(fm_eth->fm_index); + + /* enable global mode- snooping data buffers and BDs */ + pram->mode = PRAM_MODE_GLOBAL; + + /* init the Rx queue descriptor pionter */ + pram->rxqd_ptr = pram_page_offset + 0x20; + + /* set the max receive buffer length, power of 2 */ + muram_writew(&pram->mrblr, MAX_RXBUF_LOG2); + + /* alloc Rx buffer descriptors from main memory */ + rx_bd_ring_base = malloc(sizeof(struct fm_port_bd) + * RX_BD_RING_SIZE); + if (!rx_bd_ring_base) + return 0; + memset(rx_bd_ring_base, 0, sizeof(struct fm_port_bd) + * RX_BD_RING_SIZE); + + /* alloc Rx buffer from main memory */ + rx_buf_pool = malloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE); + if (!rx_buf_pool) + return 0; + memset(rx_buf_pool, 0, MAX_RXBUF_LEN * RX_BD_RING_SIZE); + + /* save them to fm_eth */ + fm_eth->rx_bd_ring = rx_bd_ring_base; + fm_eth->cur_rxbd = rx_bd_ring_base; + fm_eth->rx_buf = rx_buf_pool; + + /* init Rx BDs ring */ + rxbd = (struct fm_port_bd *)rx_bd_ring_base; + for (i = 0; i < RX_BD_RING_SIZE; i++) { + rxbd->status = RxBD_EMPTY; + rxbd->len = 0; + rxbd->buf_ptr_hi = 0; + rxbd->buf_ptr_lo = (u32)rx_buf_pool + i * MAX_RXBUF_LEN; + rxbd++; + } + + /* set the Rx queue descriptor */ + rxqd = &pram->rxqd; + muram_writew(&rxqd->gen, 0); + muram_writew(&rxqd->bd_ring_base_hi, 0); + rxqd->bd_ring_base_lo = (u32)rx_bd_ring_base; + muram_writew(&rxqd->bd_ring_size, sizeof(struct fm_port_bd) + * RX_BD_RING_SIZE); + muram_writew(&rxqd->offset_in, 0); + muram_writew(&rxqd->offset_out, 0); + + /* set IM parameter ram pointer to Rx Frame Queue ID */ + out_be32(&bmi_rx_port->fmbm_rfqid, pram_page_offset); + + return 1; +} + +static int fm_eth_tx_port_parameter_init(struct fm_eth *fm_eth) +{ + struct fm_port_global_pram *pram; + u32 pram_page_offset; + void *tx_bd_ring_base; + struct fm_port_bd *txbd; + struct fm_port_qd *txqd; + struct fm_bmi_tx_port *bmi_tx_port = fm_eth->tx_port; + int i; + + /* alloc global parameter ram at MURAM */ + pram = (struct fm_port_global_pram *)fm_muram_alloc(fm_eth->fm_index, + FM_PRAM_SIZE, FM_PRAM_ALIGN); + fm_eth->tx_pram = pram; + + /* parameter page offset to MURAM */ + pram_page_offset = (u32)pram - fm_muram_base(fm_eth->fm_index); + + /* enable global mode- snooping data buffers and BDs */ + pram->mode = PRAM_MODE_GLOBAL; + + /* init the Tx queue descriptor pionter */ + pram->txqd_ptr = pram_page_offset + 0x40; + + /* alloc Tx buffer descriptors from main memory */ + tx_bd_ring_base = malloc(sizeof(struct fm_port_bd) + * TX_BD_RING_SIZE); + if (!tx_bd_ring_base) + return 0; + memset(tx_bd_ring_base, 0, sizeof(struct fm_port_bd) + * TX_BD_RING_SIZE); + /* save it to fm_eth */ + fm_eth->tx_bd_ring = tx_bd_ring_base; + fm_eth->cur_txbd = tx_bd_ring_base; + + /* init Tx BDs ring */ + txbd = (struct fm_port_bd *)tx_bd_ring_base; + for (i = 0; i < TX_BD_RING_SIZE; i++) { + txbd->status = TxBD_LAST; + txbd->len = 0; + txbd->buf_ptr_hi = 0; + txbd->buf_ptr_lo = 0; + } + + /* set the Tx queue decriptor */ + txqd = &pram->txqd; + muram_writew(&txqd->bd_ring_base_hi, 0); + txqd->bd_ring_base_lo = (u32)tx_bd_ring_base; + muram_writew(&txqd->bd_ring_size, sizeof(struct fm_port_bd) + * TX_BD_RING_SIZE); + muram_writew(&txqd->offset_in, 0); + muram_writew(&txqd->offset_out, 0); + + /* set IM parameter ram pointer to Tx Confirmation Frame Queue ID */ + out_be32(&bmi_tx_port->fmbm_tcfqid, pram_page_offset); + + return 1; +} + +static int fm_eth_init(struct fm_eth *fm_eth) +{ + + if (!fm_eth_rx_port_parameter_init(fm_eth)) + return 0; + + if (!fm_eth_tx_port_parameter_init(fm_eth)) + return 0; + + return 1; +} + +static int fm_eth_startup(struct fm_eth *fm_eth) +{ + struct fsl_enet_mac *mac; + mac = fm_eth->mac; + + /* Rx/TxBDs, Rx/TxQDs, Rx buff and parameter ram init */ + if (!fm_eth_init(fm_eth)) + return 0; + /* setup the MAC controller */ + mac->init_mac(mac); + + /* For some reason we need to set SPEED_100 */ + if (((fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII) || + (fm_eth->enet_if == PHY_INTERFACE_MODE_QSGMII)) && + mac->set_if_mode) + mac->set_if_mode(mac, fm_eth->enet_if, SPEED_100); + + /* init bmi rx port, IM mode and disable */ + bmi_rx_port_init(fm_eth->rx_port); + /* init bmi tx port, IM mode and disable */ + bmi_tx_port_init(fm_eth->tx_port); + + return 1; +} + +static void fmc_tx_port_graceful_stop_enable(struct fm_eth *fm_eth) +{ + struct fm_port_global_pram *pram; + + pram = fm_eth->tx_pram; + /* graceful stop transmission of frames */ + pram->mode |= PRAM_MODE_GRACEFUL_STOP; + sync(); +} + +static void fmc_tx_port_graceful_stop_disable(struct fm_eth *fm_eth) +{ + struct fm_port_global_pram *pram; + + pram = fm_eth->tx_pram; + /* re-enable transmission of frames */ + pram->mode &= ~PRAM_MODE_GRACEFUL_STOP; + sync(); +} + +static int fm_eth_open(struct eth_device *dev, bd_t *bd) +{ + struct fm_eth *fm_eth; + struct fsl_enet_mac *mac; +#ifdef CONFIG_PHYLIB + int ret; +#endif + + fm_eth = (struct fm_eth *)dev->priv; + mac = fm_eth->mac; + + /* setup the MAC address */ + if (dev->enetaddr[0] & 0x01) { + printf("%s: MacAddress is multcast address\n", __func__); + return 1; + } + mac->set_mac_addr(mac, dev->enetaddr); + + /* enable bmi Rx port */ + setbits_be32(&fm_eth->rx_port->fmbm_rcfg, FMBM_RCFG_EN); + /* enable MAC rx/tx port */ + mac->enable_mac(mac); + /* enable bmi Tx port */ + setbits_be32(&fm_eth->tx_port->fmbm_tcfg, FMBM_TCFG_EN); + /* re-enable transmission of frame */ + fmc_tx_port_graceful_stop_disable(fm_eth); + +#ifdef CONFIG_PHYLIB + ret = phy_startup(fm_eth->phydev); + if (ret) { + printf("%s: Could not initialize\n", fm_eth->phydev->dev->name); + return ret; + } +#else + fm_eth->phydev->speed = SPEED_1000; + fm_eth->phydev->link = 1; + fm_eth->phydev->duplex = DUPLEX_FULL; +#endif + + /* set the MAC-PHY mode */ + mac->set_if_mode(mac, fm_eth->enet_if, fm_eth->phydev->speed); + + if (!fm_eth->phydev->link) + printf("%s: No link.\n", fm_eth->phydev->dev->name); + + return fm_eth->phydev->link ? 0 : -1; +} + +static void fm_eth_halt(struct eth_device *dev) +{ + struct fm_eth *fm_eth; + struct fsl_enet_mac *mac; + + fm_eth = (struct fm_eth *)dev->priv; + mac = fm_eth->mac; + + /* graceful stop the transmission of frames */ + fmc_tx_port_graceful_stop_enable(fm_eth); + /* disable bmi Tx port */ + bmi_tx_port_disable(fm_eth->tx_port); + /* disable MAC rx/tx port */ + mac->disable_mac(mac); + /* disable bmi Rx port */ + bmi_rx_port_disable(fm_eth->rx_port); + + phy_shutdown(fm_eth->phydev); +} + +static int fm_eth_send(struct eth_device *dev, void *buf, int len) +{ + struct fm_eth *fm_eth; + struct fm_port_global_pram *pram; + struct fm_port_bd *txbd, *txbd_base; + u16 offset_in; + int i; + + fm_eth = (struct fm_eth *)dev->priv; + pram = fm_eth->tx_pram; + txbd = fm_eth->cur_txbd; + + /* find one empty TxBD */ + for (i = 0; txbd->status & TxBD_READY; i++) { + udelay(100); + if (i > 0x1000) { + printf("%s: Tx buffer not ready\n", dev->name); + return 0; + } + } + /* setup TxBD */ + txbd->buf_ptr_hi = 0; + txbd->buf_ptr_lo = (u32)buf; + txbd->len = len; + sync(); + txbd->status = TxBD_READY | TxBD_LAST; + sync(); + + /* update TxQD, let RISC to send the packet */ + offset_in = muram_readw(&pram->txqd.offset_in); + offset_in += sizeof(struct fm_port_bd); + if (offset_in >= muram_readw(&pram->txqd.bd_ring_size)) + offset_in = 0; + muram_writew(&pram->txqd.offset_in, offset_in); + sync(); + + /* wait for buffer to be transmitted */ + for (i = 0; txbd->status & TxBD_READY; i++) { + udelay(100); + if (i > 0x10000) { + printf("%s: Tx error\n", dev->name); + return 0; + } + } + + /* advance the TxBD */ + txbd++; + txbd_base = (struct fm_port_bd *)fm_eth->tx_bd_ring; + if (txbd >= (txbd_base + TX_BD_RING_SIZE)) + txbd = txbd_base; + /* update current txbd */ + fm_eth->cur_txbd = (void *)txbd; + + return 1; +} + +static int fm_eth_recv(struct eth_device *dev) +{ + struct fm_eth *fm_eth; + struct fm_port_global_pram *pram; + struct fm_port_bd *rxbd, *rxbd_base; + u16 status, len; + u8 *data; + u16 offset_out; + + fm_eth = (struct fm_eth *)dev->priv; + pram = fm_eth->rx_pram; + rxbd = fm_eth->cur_rxbd; + status = rxbd->status; + + while (!(status & RxBD_EMPTY)) { + if (!(status & RxBD_ERROR)) { + data = (u8 *)rxbd->buf_ptr_lo; + len = rxbd->len; + NetReceive(data, len); + } else { + printf("%s: Rx error\n", dev->name); + return 0; + } + + /* clear the RxBDs */ + rxbd->status = RxBD_EMPTY; + rxbd->len = 0; + sync(); + + /* advance RxBD */ + rxbd++; + rxbd_base = (struct fm_port_bd *)fm_eth->rx_bd_ring; + if (rxbd >= (rxbd_base + RX_BD_RING_SIZE)) + rxbd = rxbd_base; + /* read next status */ + status = rxbd->status; + + /* update RxQD */ + offset_out = muram_readw(&pram->rxqd.offset_out); + offset_out += sizeof(struct fm_port_bd); + if (offset_out >= muram_readw(&pram->rxqd.bd_ring_size)) + offset_out = 0; + muram_writew(&pram->rxqd.offset_out, offset_out); + sync(); + } + fm_eth->cur_rxbd = (void *)rxbd; + + return 1; +} + +static int fm_eth_init_mac(struct fm_eth *fm_eth, struct ccsr_fman *reg) +{ + struct fsl_enet_mac *mac; + int num; + void *base, *phyregs = NULL; + + num = fm_eth->num; + +#ifdef CONFIG_SYS_FMAN_V3 + if (fm_eth->type == FM_ETH_10G_E) { + /* 10GEC1/10GEC2 use mEMAC9/mEMAC10 + * 10GEC3/10GEC4 use mEMAC1/mEMAC2 + * so it needs to change the num. + */ + if (fm_eth->num >= 2) + num -= 2; + else + num += 8; + } + base = ®->memac[num].fm_memac; + phyregs = ®->memac[num].fm_memac_mdio; +#else + /* Get the mac registers base address */ + if (fm_eth->type == FM_ETH_1G_E) { + base = ®->mac_1g[num].fm_dtesc; + phyregs = ®->mac_1g[num].fm_mdio.miimcfg; + } else { + base = ®->mac_10g[num].fm_10gec; + phyregs = ®->mac_10g[num].fm_10gec_mdio; + } +#endif + + /* alloc mac controller */ + mac = malloc(sizeof(struct fsl_enet_mac)); + if (!mac) + return 0; + memset(mac, 0, sizeof(struct fsl_enet_mac)); + + /* save the mac to fm_eth struct */ + fm_eth->mac = mac; + +#ifdef CONFIG_SYS_FMAN_V3 + init_memac(mac, base, phyregs, MAX_RXBUF_LEN); +#else + if (fm_eth->type == FM_ETH_1G_E) + init_dtsec(mac, base, phyregs, MAX_RXBUF_LEN); + else + init_tgec(mac, base, phyregs, MAX_RXBUF_LEN); +#endif + + return 1; +} + +static int init_phy(struct eth_device *dev) +{ + struct fm_eth *fm_eth = dev->priv; + struct phy_device *phydev = NULL; + u32 supported; + +#ifdef CONFIG_PHYLIB + if (fm_eth->type == FM_ETH_1G_E) + dtsec_init_phy(dev); + + if (fm_eth->bus) { + phydev = phy_connect(fm_eth->bus, fm_eth->phyaddr, dev, + fm_eth->enet_if); + } + + if (!phydev) { + printf("Failed to connect\n"); + return -1; + } + + if (fm_eth->type == FM_ETH_1G_E) { + supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full); + } else { + supported = SUPPORTED_10000baseT_Full; + + if (tgec_is_fibre(dev)) + phydev->port = PORT_FIBRE; + } + + phydev->supported &= supported; + phydev->advertising = phydev->supported; + + fm_eth->phydev = phydev; + + phy_config(phydev); +#endif + + return 0; +} + +int fm_eth_initialize(struct ccsr_fman *reg, struct fm_eth_info *info) +{ + struct eth_device *dev; + struct fm_eth *fm_eth; + int i, num = info->num; + + /* alloc eth device */ + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + if (!dev) + return 0; + memset(dev, 0, sizeof(struct eth_device)); + + /* alloc the FMan ethernet private struct */ + fm_eth = (struct fm_eth *)malloc(sizeof(struct fm_eth)); + if (!fm_eth) + return 0; + memset(fm_eth, 0, sizeof(struct fm_eth)); + + /* save off some things we need from the info struct */ + fm_eth->fm_index = info->index - 1; /* keep as 0 based for muram */ + fm_eth->num = num; + fm_eth->type = info->type; + + fm_eth->rx_port = (void *)®->port[info->rx_port_id - 1].fm_bmi; + fm_eth->tx_port = (void *)®->port[info->tx_port_id - 1].fm_bmi; + + /* set the ethernet max receive length */ + fm_eth->max_rx_len = MAX_RXBUF_LEN; + + /* init global mac structure */ + if (!fm_eth_init_mac(fm_eth, reg)) + return 0; + + /* keep same as the manual, we call FMAN1, FMAN2, DTSEC1, DTSEC2, etc */ + if (fm_eth->type == FM_ETH_1G_E) + sprintf(dev->name, "FM%d@DTSEC%d", info->index, num + 1); + else + sprintf(dev->name, "FM%d@TGEC%d", info->index, num + 1); + + devlist[num_controllers++] = dev; + dev->iobase = 0; + dev->priv = (void *)fm_eth; + dev->init = fm_eth_open; + dev->halt = fm_eth_halt; + dev->send = fm_eth_send; + dev->recv = fm_eth_recv; + fm_eth->dev = dev; + fm_eth->bus = info->bus; + fm_eth->phyaddr = info->phy_addr; + fm_eth->enet_if = info->enet_if; + + /* startup the FM im */ + if (!fm_eth_startup(fm_eth)) + return 0; + + if (init_phy(dev)) + return 0; + + /* clear the ethernet address */ + for (i = 0; i < 6; i++) + dev->enetaddr[i] = 0; + eth_register(dev); + + return 1; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/fm.c b/qemu/roms/u-boot/drivers/net/fm/fm.c new file mode 100644 index 000000000..400e9dd5e --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/fm.c @@ -0,0 +1,420 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * Dave Liu + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include + +#include "fm.h" +#include "../../qe/qe.h" /* For struct qe_firmware */ + +#ifdef CONFIG_SYS_QE_FMAN_FW_IN_NAND +#include +#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH) +#include +#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_MMC) +#include +#endif + +struct fm_muram muram[CONFIG_SYS_NUM_FMAN]; + +u32 fm_muram_base(int fm_idx) +{ + return muram[fm_idx].base; +} + +u32 fm_muram_alloc(int fm_idx, u32 size, u32 align) +{ + u32 ret; + u32 align_mask, off; + u32 save; + + align_mask = align - 1; + save = muram[fm_idx].alloc; + + off = save & align_mask; + if (off != 0) + muram[fm_idx].alloc += (align - off); + off = size & align_mask; + if (off != 0) + size += (align - off); + if ((muram[fm_idx].alloc + size) >= muram[fm_idx].top) { + muram[fm_idx].alloc = save; + printf("%s: run out of ram.\n", __func__); + } + + ret = muram[fm_idx].alloc; + muram[fm_idx].alloc += size; + memset((void *)ret, 0, size); + + return ret; +} + +static void fm_init_muram(int fm_idx, void *reg) +{ + u32 base = (u32)reg; + + muram[fm_idx].base = base; + muram[fm_idx].size = CONFIG_SYS_FM_MURAM_SIZE; + muram[fm_idx].alloc = base + FM_MURAM_RES_SIZE; + muram[fm_idx].top = base + CONFIG_SYS_FM_MURAM_SIZE; +} + +/* + * fm_upload_ucode - Fman microcode upload worker function + * + * This function does the actual uploading of an Fman microcode + * to an Fman. + */ +static void fm_upload_ucode(int fm_idx, struct fm_imem *imem, + u32 *ucode, unsigned int size) +{ + unsigned int i; + unsigned int timeout = 1000000; + + /* enable address auto increase */ + out_be32(&imem->iadd, IRAM_IADD_AIE); + /* write microcode to IRAM */ + for (i = 0; i < size / 4; i++) + out_be32(&imem->idata, ucode[i]); + + /* verify if the writing is over */ + out_be32(&imem->iadd, 0); + while ((in_be32(&imem->idata) != ucode[0]) && --timeout) + ; + if (!timeout) + printf("Fman%u: microcode upload timeout\n", fm_idx + 1); + + /* enable microcode from IRAM */ + out_be32(&imem->iready, IRAM_READY); +} + +/* + * Upload an Fman firmware + * + * This function is similar to qe_upload_firmware(), exception that it uploads + * a microcode to the Fman instead of the QE. + * + * Because the process for uploading a microcode to the Fman is similar for + * that of the QE, the QE firmware binary format is used for Fman microcode. + * It should be possible to unify these two functions, but for now we keep them + * separate. + */ +static int fman_upload_firmware(int fm_idx, + struct fm_imem *fm_imem, + const struct qe_firmware *firmware) +{ + unsigned int i; + u32 crc; + size_t calc_size = sizeof(struct qe_firmware); + size_t length; + const struct qe_header *hdr; + + if (!firmware) { + printf("Fman%u: Invalid address for firmware\n", fm_idx + 1); + return -EINVAL; + } + + hdr = &firmware->header; + length = be32_to_cpu(hdr->length); + + /* Check the magic */ + if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || + (hdr->magic[2] != 'F')) { + printf("Fman%u: Data at %p is not a firmware\n", fm_idx + 1, + firmware); + return -EPERM; + } + + /* Check the version */ + if (hdr->version != 1) { + printf("Fman%u: Unsupported firmware version %u\n", fm_idx + 1, + hdr->version); + return -EPERM; + } + + /* Validate some of the fields */ + if ((firmware->count != 1)) { + printf("Fman%u: Invalid data in firmware header\n", fm_idx + 1); + return -EINVAL; + } + + /* Validate the length and check if there's a CRC */ + calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); + + for (i = 0; i < firmware->count; i++) + /* + * For situations where the second RISC uses the same microcode + * as the first, the 'code_offset' and 'count' fields will be + * zero, so it's okay to add those. + */ + calc_size += sizeof(u32) * + be32_to_cpu(firmware->microcode[i].count); + + /* Validate the length */ + if (length != calc_size + sizeof(u32)) { + printf("Fman%u: Invalid length in firmware header\n", + fm_idx + 1); + return -EPERM; + } + + /* + * Validate the CRC. We would normally call crc32_no_comp(), but that + * function isn't available unless you turn on JFFS support. + */ + crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size)); + if (crc != (crc32(-1, (const void *)firmware, calc_size) ^ -1)) { + printf("Fman%u: Firmware CRC is invalid\n", fm_idx + 1); + return -EIO; + } + + /* Loop through each microcode. */ + for (i = 0; i < firmware->count; i++) { + const struct qe_microcode *ucode = &firmware->microcode[i]; + + /* Upload a microcode if it's present */ + if (ucode->code_offset) { + u32 ucode_size; + u32 *code; + printf("Fman%u: Uploading microcode version %u.%u.%u\n", + fm_idx + 1, ucode->major, ucode->minor, + ucode->revision); + code = (void *)firmware + ucode->code_offset; + ucode_size = sizeof(u32) * ucode->count; + fm_upload_ucode(fm_idx, fm_imem, code, ucode_size); + } + } + + return 0; +} + +static u32 fm_assign_risc(int port_id) +{ + u32 risc_sel, val; + risc_sel = (port_id & 0x1) ? FMFPPRC_RISC2 : FMFPPRC_RISC1; + val = (port_id << FMFPPRC_PORTID_SHIFT) & FMFPPRC_PORTID_MASK; + val |= ((risc_sel << FMFPPRC_ORA_SHIFT) | risc_sel); + + return val; +} + +static void fm_init_fpm(struct fm_fpm *fpm) +{ + int i, port_id; + u32 val; + + setbits_be32(&fpm->fmfpee, FMFPEE_EHM | FMFPEE_UEC | + FMFPEE_CER | FMFPEE_DER); + + /* IM mode, each even port ID to RISC#1, each odd port ID to RISC#2 */ + + /* offline/parser port */ + for (i = 0; i < MAX_NUM_OH_PORT; i++) { + port_id = OH_PORT_ID_BASE + i; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + } + /* Rx 1G port */ + for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) { + port_id = RX_PORT_1G_BASE + i; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + } + /* Tx 1G port */ + for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) { + port_id = TX_PORT_1G_BASE + i; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + } + /* Rx 10G port */ + port_id = RX_PORT_10G_BASE; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + /* Tx 10G port */ + port_id = TX_PORT_10G_BASE; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + + /* disable the dispatch limit in IM case */ + out_be32(&fpm->fpmflc, FMFP_FLC_DISP_LIM_NONE); + /* clear events */ + out_be32(&fpm->fmfpee, FMFPEE_CLEAR_EVENT); + + /* clear risc events */ + for (i = 0; i < 4; i++) + out_be32(&fpm->fpmcev[i], 0xffffffff); + + /* clear error */ + out_be32(&fpm->fpmrcr, FMFP_RCR_MDEC | FMFP_RCR_IDEC); +} + +static int fm_init_bmi(int fm_idx, struct fm_bmi_common *bmi) +{ + int blk, i, port_id; + u32 val, offset, base; + + /* alloc free buffer pool in MURAM */ + base = fm_muram_alloc(fm_idx, FM_FREE_POOL_SIZE, FM_FREE_POOL_ALIGN); + if (!base) { + printf("%s: no muram for free buffer pool\n", __func__); + return -ENOMEM; + } + offset = base - fm_muram_base(fm_idx); + + /* Need 128KB total free buffer pool size */ + val = offset / 256; + blk = FM_FREE_POOL_SIZE / 256; + /* in IM, we must not begin from offset 0 in MURAM */ + val |= ((blk - 1) << FMBM_CFG1_FBPS_SHIFT); + out_be32(&bmi->fmbm_cfg1, val); + + /* disable all BMI interrupt */ + out_be32(&bmi->fmbm_ier, FMBM_IER_DISABLE_ALL); + + /* clear all events */ + out_be32(&bmi->fmbm_ievr, FMBM_IEVR_CLEAR_ALL); + + /* + * set port parameters - FMBM_PP_x + * max tasks 10G Rx/Tx=12, 1G Rx/Tx 4, others is 1 + * max dma 10G Rx/Tx=3, others is 1 + * set port FIFO size - FMBM_PFS_x + * 4KB for all Rx and Tx ports + */ + /* offline/parser port */ + for (i = 0; i < MAX_NUM_OH_PORT; i++) { + port_id = OH_PORT_ID_BASE + i - 1; + /* max tasks=1, max dma=1, no extra */ + out_be32(&bmi->fmbm_pp[port_id], 0); + /* port FIFO size - 256 bytes, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], 0); + } + /* Rx 1G port */ + for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) { + port_id = RX_PORT_1G_BASE + i - 1; + /* max tasks=4, max dma=1, no extra */ + out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4)); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); + } + /* Tx 1G port FIFO size - 4KB, no extra */ + for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) { + port_id = TX_PORT_1G_BASE + i - 1; + /* max tasks=4, max dma=1, no extra */ + out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4)); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); + } + /* Rx 10G port */ + port_id = RX_PORT_10G_BASE - 1; + /* max tasks=12, max dma=3, no extra */ + out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3)); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); + + /* Tx 10G port */ + port_id = TX_PORT_10G_BASE - 1; + /* max tasks=12, max dma=3, no extra */ + out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3)); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); + + /* initialize internal buffers data base (linked list) */ + out_be32(&bmi->fmbm_init, FMBM_INIT_START); + + return 0; +} + +static void fm_init_qmi(struct fm_qmi_common *qmi) +{ + /* disable enqueue and dequeue of QMI */ + clrbits_be32(&qmi->fmqm_gc, FMQM_GC_ENQ_EN | FMQM_GC_DEQ_EN); + + /* disable all error interrupts */ + out_be32(&qmi->fmqm_eien, FMQM_EIEN_DISABLE_ALL); + /* clear all error events */ + out_be32(&qmi->fmqm_eie, FMQM_EIE_CLEAR_ALL); + + /* disable all interrupts */ + out_be32(&qmi->fmqm_ien, FMQM_IEN_DISABLE_ALL); + /* clear all interrupts */ + out_be32(&qmi->fmqm_ie, FMQM_IE_CLEAR_ALL); +} + +/* Init common part of FM, index is fm num# like fm as above */ +int fm_init_common(int index, struct ccsr_fman *reg) +{ + int rc; +#if defined(CONFIG_SYS_QE_FMAN_FW_IN_NOR) + void *addr = (void *)CONFIG_SYS_FMAN_FW_ADDR; +#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_NAND) + size_t fw_length = CONFIG_SYS_QE_FMAN_FW_LENGTH; + void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH); + + rc = nand_read(&nand_info[0], (loff_t)CONFIG_SYS_FMAN_FW_ADDR, + &fw_length, (u_char *)addr); + if (rc == -EUCLEAN) { + printf("NAND read of FMAN firmware at offset 0x%x failed %d\n", + CONFIG_SYS_FMAN_FW_ADDR, rc); + } +#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH) + struct spi_flash *ucode_flash; + void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH); + int ret = 0; + + ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (!ucode_flash) + printf("SF: probe for ucode failed\n"); + else { + ret = spi_flash_read(ucode_flash, CONFIG_SYS_FMAN_FW_ADDR, + CONFIG_SYS_QE_FMAN_FW_LENGTH, addr); + if (ret) + printf("SF: read for ucode failed\n"); + spi_flash_free(ucode_flash); + } +#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_MMC) + int dev = CONFIG_SYS_MMC_ENV_DEV; + void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH); + u32 cnt = CONFIG_SYS_QE_FMAN_FW_LENGTH / 512; + u32 blk = CONFIG_SYS_FMAN_FW_ADDR / 512; + struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); + + if (!mmc) + printf("\nMMC cannot find device for ucode\n"); + else { + printf("\nMMC read: dev # %u, block # %u, count %u ...\n", + dev, blk, cnt); + mmc_init(mmc); + (void)mmc->block_dev.block_read(dev, blk, cnt, addr); + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); + } +#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_REMOTE) + void *addr = (void *)CONFIG_SYS_FMAN_FW_ADDR; +#else + void *addr = NULL; +#endif + + /* Upload the Fman microcode if it's present */ + rc = fman_upload_firmware(index, ®->fm_imem, addr); + if (rc) + return rc; + setenv_addr("fman_ucode", addr); + + fm_init_muram(index, ®->muram); + fm_init_qmi(®->fm_qmi_common); + fm_init_fpm(®->fm_fpm); + + /* clear DMA status */ + setbits_be32(®->fm_dma.fmdmsr, FMDMSR_CLEAR_ALL); + + /* set DMA mode */ + setbits_be32(®->fm_dma.fmdmmr, FMDMMR_SBER); + + return fm_init_bmi(index, ®->fm_bmi_common); +} diff --git a/qemu/roms/u-boot/drivers/net/fm/fm.h b/qemu/roms/u-boot/drivers/net/fm/fm.h new file mode 100644 index 000000000..43de114b5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/fm.h @@ -0,0 +1,148 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __FM_H__ +#define __FM_H__ + +#include +#include +#include +#include + +/* Port ID */ +#define OH_PORT_ID_BASE 0x01 +#define MAX_NUM_OH_PORT 7 +#define RX_PORT_1G_BASE 0x08 +#define MAX_NUM_RX_PORT_1G CONFIG_SYS_NUM_FM1_DTSEC +#define RX_PORT_10G_BASE 0x10 +#define RX_PORT_10G_BASE2 0x08 +#define TX_PORT_1G_BASE 0x28 +#define MAX_NUM_TX_PORT_1G CONFIG_SYS_NUM_FM1_DTSEC +#define TX_PORT_10G_BASE 0x30 +#define TX_PORT_10G_BASE2 0x28 +#define MIIM_TIMEOUT 0xFFFF + +struct fm_muram { + u32 base; + u32 top; + u32 size; + u32 alloc; +}; +#define FM_MURAM_RES_SIZE 0x01000 + +/* Rx/Tx buffer descriptor */ +struct fm_port_bd { + u16 status; + u16 len; + u32 res0; + u16 res1; + u16 buf_ptr_hi; + u32 buf_ptr_lo; +}; + +/* Common BD flags */ +#define BD_LAST 0x0800 + +/* Rx BD status flags */ +#define RxBD_EMPTY 0x8000 +#define RxBD_LAST BD_LAST +#define RxBD_FIRST 0x0400 +#define RxBD_PHYS_ERR 0x0008 +#define RxBD_SIZE_ERR 0x0004 +#define RxBD_ERROR (RxBD_PHYS_ERR | RxBD_SIZE_ERR) + +/* Tx BD status flags */ +#define TxBD_READY 0x8000 +#define TxBD_LAST BD_LAST + +/* Rx/Tx queue descriptor */ +struct fm_port_qd { + u16 gen; + u16 bd_ring_base_hi; + u32 bd_ring_base_lo; + u16 bd_ring_size; + u16 offset_in; + u16 offset_out; + u16 res0; + u32 res1[0x4]; +}; + +/* IM global parameter RAM */ +struct fm_port_global_pram { + u32 mode; /* independent mode register */ + u32 rxqd_ptr; /* Rx queue descriptor pointer */ + u32 txqd_ptr; /* Tx queue descriptor pointer */ + u16 mrblr; /* max Rx buffer length */ + u16 rxqd_bsy_cnt; /* RxQD busy counter, should be cleared */ + u32 res0[0x4]; + struct fm_port_qd rxqd; /* Rx queue descriptor */ + struct fm_port_qd txqd; /* Tx queue descriptor */ + u32 res1[0x28]; +}; + +#define FM_PRAM_SIZE sizeof(struct fm_port_global_pram) +#define FM_PRAM_ALIGN 256 +#define PRAM_MODE_GLOBAL 0x20000000 +#define PRAM_MODE_GRACEFUL_STOP 0x00800000 + +#if defined(CONFIG_P1017) || defined(CONFIG_P1023) +#define FM_FREE_POOL_SIZE 0x2000 /* 8K bytes */ +#else +#define FM_FREE_POOL_SIZE 0x20000 /* 128K bytes */ +#endif +#define FM_FREE_POOL_ALIGN 256 + +u32 fm_muram_alloc(int fm_idx, u32 size, u32 align); +u32 fm_muram_base(int fm_idx); +int fm_init_common(int index, struct ccsr_fman *reg); +int fm_eth_initialize(struct ccsr_fman *reg, struct fm_eth_info *info); +phy_interface_t fman_port_enet_if(enum fm_port port); +void fman_disable_port(enum fm_port port); +void fman_enable_port(enum fm_port port); + +struct fsl_enet_mac { + void *base; /* MAC controller registers base address */ + void *phyregs; + int max_rx_len; + void (*init_mac)(struct fsl_enet_mac *mac); + void (*enable_mac)(struct fsl_enet_mac *mac); + void (*disable_mac)(struct fsl_enet_mac *mac); + void (*set_mac_addr)(struct fsl_enet_mac *mac, u8 *mac_addr); + void (*set_if_mode)(struct fsl_enet_mac *mac, phy_interface_t type, + int speed); +}; + +/* Fman ethernet private struct */ +struct fm_eth { + int fm_index; /* Fman index */ + u32 num; /* 0..n-1 for give type */ + struct fm_bmi_tx_port *tx_port; + struct fm_bmi_rx_port *rx_port; + enum fm_eth_type type; /* 1G or 10G ethernet */ + phy_interface_t enet_if; + struct fsl_enet_mac *mac; /* MAC controller */ + struct mii_dev *bus; + struct phy_device *phydev; + int phyaddr; + struct eth_device *dev; + int max_rx_len; + struct fm_port_global_pram *rx_pram; /* Rx parameter table */ + struct fm_port_global_pram *tx_pram; /* Tx parameter table */ + void *rx_bd_ring; /* Rx BD ring base */ + void *cur_rxbd; /* current Rx BD */ + void *rx_buf; /* Rx buffer base */ + void *tx_bd_ring; /* Tx BD ring base */ + void *cur_txbd; /* current Tx BD */ +}; + +#define RX_BD_RING_SIZE 8 +#define TX_BD_RING_SIZE 8 +#define MAX_RXBUF_LOG2 11 +#define MAX_RXBUF_LEN (1 << MAX_RXBUF_LOG2) + +#define PORT_IS_ENABLED(port) fm_info[fm_port_to_index(port)].enabled + +#endif /* __FM_H__ */ diff --git a/qemu/roms/u-boot/drivers/net/fm/init.c b/qemu/roms/u-boot/drivers/net/fm/init.c new file mode 100644 index 000000000..cd787f4ee --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/init.c @@ -0,0 +1,338 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include + +#include "fm.h" + +struct fm_eth_info fm_info[] = { +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 1) + FM_DTSEC_INFO_INITIALIZER(1, 1), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 2) + FM_DTSEC_INFO_INITIALIZER(1, 2), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 3) + FM_DTSEC_INFO_INITIALIZER(1, 3), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 4) + FM_DTSEC_INFO_INITIALIZER(1, 4), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 5) + FM_DTSEC_INFO_INITIALIZER(1, 5), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 6) + FM_DTSEC_INFO_INITIALIZER(1, 6), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 7) + FM_DTSEC_INFO_INITIALIZER(1, 9), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 8) + FM_DTSEC_INFO_INITIALIZER(1, 10), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 1) + FM_DTSEC_INFO_INITIALIZER(2, 1), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 2) + FM_DTSEC_INFO_INITIALIZER(2, 2), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 3) + FM_DTSEC_INFO_INITIALIZER(2, 3), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 4) + FM_DTSEC_INFO_INITIALIZER(2, 4), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 5) + FM_DTSEC_INFO_INITIALIZER(2, 5), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 6) + FM_DTSEC_INFO_INITIALIZER(2, 6), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 7) + FM_DTSEC_INFO_INITIALIZER(2, 9), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 8) + FM_DTSEC_INFO_INITIALIZER(2, 10), +#endif +#if (CONFIG_SYS_NUM_FM1_10GEC >= 1) + FM_TGEC_INFO_INITIALIZER(1, 1), +#endif +#if (CONFIG_SYS_NUM_FM1_10GEC >= 2) + FM_TGEC_INFO_INITIALIZER(1, 2), +#endif +#if (CONFIG_SYS_NUM_FM1_10GEC >= 3) + FM_TGEC_INFO_INITIALIZER2(1, 3), +#endif +#if (CONFIG_SYS_NUM_FM1_10GEC >= 4) + FM_TGEC_INFO_INITIALIZER2(1, 4), +#endif +#if (CONFIG_SYS_NUM_FM2_10GEC >= 1) + FM_TGEC_INFO_INITIALIZER(2, 1), +#endif +#if (CONFIG_SYS_NUM_FM2_10GEC >= 2) + FM_TGEC_INFO_INITIALIZER(2, 2), +#endif +}; + +int fm_standard_init(bd_t *bis) +{ + int i; + struct ccsr_fman *reg; + + reg = (void *)CONFIG_SYS_FSL_FM1_ADDR; + if (fm_init_common(0, reg)) + return 0; + + for (i = 0; i < ARRAY_SIZE(fm_info); i++) { + if ((fm_info[i].enabled) && (fm_info[i].index == 1)) + fm_eth_initialize(reg, &fm_info[i]); + } + +#if (CONFIG_SYS_NUM_FMAN == 2) + reg = (void *)CONFIG_SYS_FSL_FM2_ADDR; + if (fm_init_common(1, reg)) + return 0; + + for (i = 0; i < ARRAY_SIZE(fm_info); i++) { + if ((fm_info[i].enabled) && (fm_info[i].index == 2)) + fm_eth_initialize(reg, &fm_info[i]); + } +#endif + + return 1; +} + +/* simple linear search to map from port to array index */ +static int fm_port_to_index(enum fm_port port) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fm_info); i++) { + if (fm_info[i].port == port) + return i; + } + + return -1; +} + +/* + * Determine if an interface is actually active based on HW config + * we expect fman_port_enet_if() to report PHY_INTERFACE_MODE_NONE if + * the interface is not active based on HW cfg of the SoC + */ +void fman_enet_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fm_info); i++) { + phy_interface_t enet_if; + + enet_if = fman_port_enet_if(fm_info[i].port); + if (enet_if != PHY_INTERFACE_MODE_NONE) { + fm_info[i].enabled = 1; + fm_info[i].enet_if = enet_if; + } else { + fm_info[i].enabled = 0; + } + } + + return ; +} + +void fm_disable_port(enum fm_port port) +{ + int i = fm_port_to_index(port); + + fm_info[i].enabled = 0; + fman_disable_port(port); +} + +void fm_enable_port(enum fm_port port) +{ + int i = fm_port_to_index(port); + + fm_info[i].enabled = 1; + fman_enable_port(port); +} + +void fm_info_set_mdio(enum fm_port port, struct mii_dev *bus) +{ + int i = fm_port_to_index(port); + + if (i == -1) + return; + + fm_info[i].bus = bus; +} + +void fm_info_set_phy_address(enum fm_port port, int address) +{ + int i = fm_port_to_index(port); + + if (i == -1) + return; + + fm_info[i].phy_addr = address; +} + +/* + * Returns the PHY address for a given Fman port + * + * The port must be set via a prior call to fm_info_set_phy_address(). + * A negative error code is returned if the port is invalid. + */ +int fm_info_get_phy_address(enum fm_port port) +{ + int i = fm_port_to_index(port); + + if (i == -1) + return -1; + + return fm_info[i].phy_addr; +} + +/* + * Returns the type of the data interface between the given MAC and its PHY. + * This is typically determined by the RCW. + */ +phy_interface_t fm_info_get_enet_if(enum fm_port port) +{ + int i = fm_port_to_index(port); + + if (i == -1) + return PHY_INTERFACE_MODE_NONE; + + if (fm_info[i].enabled) + return fm_info[i].enet_if; + + return PHY_INTERFACE_MODE_NONE; +} + +static void +__def_board_ft_fman_fixup_port(void *blob, char * prop, phys_addr_t pa, + enum fm_port port, int offset) +{ + return ; +} + +void board_ft_fman_fixup_port(void *blob, char * prop, phys_addr_t pa, + enum fm_port port, int offset) + __attribute__((weak, alias("__def_board_ft_fman_fixup_port"))); + +static void ft_fixup_port(void *blob, struct fm_eth_info *info, char *prop) +{ + int off; + uint32_t ph; + phys_addr_t paddr = CONFIG_SYS_CCSRBAR_PHYS + info->compat_offset; + u64 dtsec1_addr = (u64)CONFIG_SYS_CCSRBAR_PHYS + + CONFIG_SYS_FSL_FM1_DTSEC1_OFFSET; + + off = fdt_node_offset_by_compat_reg(blob, prop, paddr); + + if (info->enabled) { + fdt_fixup_phy_connection(blob, off, info->enet_if); + board_ft_fman_fixup_port(blob, prop, paddr, info->port, off); + return ; + } + +#ifdef CONFIG_SYS_FMAN_V3 + /* + * Physically FM1_DTSEC9 and FM1_10GEC1 use the same dual-role MAC, when + * FM1_10GEC1 is enabled and FM1_DTSEC9 is disabled, ensure that the + * dual-role MAC is not disabled, ditto for other dual-role MACs. + */ + if (((info->port == FM1_DTSEC9) && (PORT_IS_ENABLED(FM1_10GEC1))) || + ((info->port == FM1_DTSEC10) && (PORT_IS_ENABLED(FM1_10GEC2))) || + ((info->port == FM1_DTSEC1) && (PORT_IS_ENABLED(FM1_10GEC3))) || + ((info->port == FM1_DTSEC2) && (PORT_IS_ENABLED(FM1_10GEC4))) || + ((info->port == FM1_10GEC1) && (PORT_IS_ENABLED(FM1_DTSEC9))) || + ((info->port == FM1_10GEC2) && (PORT_IS_ENABLED(FM1_DTSEC10))) || + ((info->port == FM1_10GEC3) && (PORT_IS_ENABLED(FM1_DTSEC1))) || + ((info->port == FM1_10GEC4) && (PORT_IS_ENABLED(FM1_DTSEC2))) +#if (CONFIG_SYS_NUM_FMAN == 2) + || + ((info->port == FM2_DTSEC9) && (PORT_IS_ENABLED(FM2_10GEC1))) || + ((info->port == FM2_DTSEC10) && (PORT_IS_ENABLED(FM2_10GEC2))) || + ((info->port == FM2_10GEC1) && (PORT_IS_ENABLED(FM2_DTSEC9))) || + ((info->port == FM2_10GEC2) && (PORT_IS_ENABLED(FM2_DTSEC10))) +#endif + ) + return; +#endif + /* board code might have caused offset to change */ + off = fdt_node_offset_by_compat_reg(blob, prop, paddr); + + /* Don't disable FM1-DTSEC1 MAC as its used for MDIO */ + if (paddr != dtsec1_addr) + fdt_status_disabled(blob, off); /* disable the MAC node */ + + /* disable the fsl,dpa-ethernet node that points to the MAC */ + ph = fdt_get_phandle(blob, off); + do_fixup_by_prop(blob, "fsl,fman-mac", &ph, sizeof(ph), + "status", "disabled", strlen("disabled") + 1, 1); +} + +void fdt_fixup_fman_ethernet(void *blob) +{ + int i; + +#ifdef CONFIG_SYS_FMAN_V3 + for (i = 0; i < ARRAY_SIZE(fm_info); i++) + ft_fixup_port(blob, &fm_info[i], "fsl,fman-memac"); +#else + for (i = 0; i < ARRAY_SIZE(fm_info); i++) { + if (fm_info[i].type == FM_ETH_1G_E) + ft_fixup_port(blob, &fm_info[i], "fsl,fman-1g-mac"); + else + ft_fixup_port(blob, &fm_info[i], "fsl,fman-10g-mac"); + } +#endif +} + +/*QSGMII Riser Card can work in SGMII mode, but the PHY address is different. + *This function scans which Riser Card being used(QSGMII or SGMII Riser Card), + *then set the correct PHY address + */ +void set_sgmii_phy(struct mii_dev *bus, enum fm_port base_port, + unsigned int port_num, int phy_base_addr) +{ + unsigned int regnum = 0; + int qsgmii; + int i; + int phy_real_addr; + + qsgmii = is_qsgmii_riser_card(bus, phy_base_addr, port_num, regnum); + + if (!qsgmii) + return; + + for (i = base_port; i < base_port + port_num; i++) { + if (fm_info_get_enet_if(i) == PHY_INTERFACE_MODE_SGMII) { + phy_real_addr = phy_base_addr + i - base_port; + fm_info_set_phy_address(i, phy_real_addr); + } + } +} + +/*to check whether qsgmii riser card is used*/ +int is_qsgmii_riser_card(struct mii_dev *bus, int phy_base_addr, + unsigned int port_num, unsigned regnum) +{ + int i; + int val; + + if (!bus) + return 0; + + for (i = phy_base_addr; i < phy_base_addr + port_num; i++) { + val = bus->read(bus, i, MDIO_DEVAD_NONE, regnum); + if (val != MIIM_TIMEOUT) + return 1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/memac.c b/qemu/roms/u-boot/drivers/net/fm/memac.c new file mode 100644 index 000000000..592a67f2a --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/memac.c @@ -0,0 +1,137 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Roy Zang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* MAXFRM - maximum frame length */ +#define MAXFRM_MASK 0x0000ffff + +#include +#include +#include +#include +#include +#include + +#include "fm.h" + +static void memac_init_mac(struct fsl_enet_mac *mac) +{ + struct memac *regs = mac->base; + + /* mask all interrupt */ + out_be32(®s->imask, IMASK_MASK_ALL); + + /* clear all events */ + out_be32(®s->ievent, IEVENT_CLEAR_ALL); + + /* set the max receive length */ + out_be32(®s->maxfrm, mac->max_rx_len & MAXFRM_MASK); + + /* multicast frame reception for the hash entry disable */ + out_be32(®s->hashtable_ctrl, 0); +} + +static void memac_enable_mac(struct fsl_enet_mac *mac) +{ + struct memac *regs = mac->base; + + setbits_be32(®s->command_config, MEMAC_CMD_CFG_RXTX_EN); +} + +static void memac_disable_mac(struct fsl_enet_mac *mac) +{ + struct memac *regs = mac->base; + + clrbits_be32(®s->command_config, MEMAC_CMD_CFG_RXTX_EN); +} + +static void memac_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr) +{ + struct memac *regs = mac->base; + u32 mac_addr0, mac_addr1; + + /* + * if a station address of 0x12345678ABCD, perform a write to + * MAC_ADDR0 of 0x78563412, MAC_ADDR1 of 0x0000CDAB + */ + mac_addr0 = (mac_addr[3] << 24) | (mac_addr[2] << 16) | \ + (mac_addr[1] << 8) | (mac_addr[0]); + out_be32(®s->mac_addr_0, mac_addr0); + + mac_addr1 = ((mac_addr[5] << 8) | mac_addr[4]) & 0x0000ffff; + out_be32(®s->mac_addr_1, mac_addr1); +} + +static void memac_set_interface_mode(struct fsl_enet_mac *mac, + phy_interface_t type, int speed) +{ + /* Roy need more work here */ + + struct memac *regs = mac->base; + u32 if_mode, if_status; + + /* clear all bits relative with interface mode */ + if_mode = in_be32(®s->if_mode); + if_status = in_be32(®s->if_status); + + /* set interface mode */ + switch (type) { + case PHY_INTERFACE_MODE_GMII: + if_mode &= ~IF_MODE_MASK; + if_mode |= IF_MODE_GMII; + break; + case PHY_INTERFACE_MODE_RGMII: + if_mode |= (IF_MODE_GMII | IF_MODE_RG); + break; + case PHY_INTERFACE_MODE_RMII: + if_mode |= (IF_MODE_GMII | IF_MODE_RM); + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + if_mode &= ~IF_MODE_MASK; + if_mode |= (IF_MODE_GMII); + break; + default: + break; + } + /* Enable automatic speed selection */ + if_mode |= IF_MODE_EN_AUTO; + + if (type == PHY_INTERFACE_MODE_RGMII) { + if_mode &= ~IF_MODE_EN_AUTO; + if_mode &= ~IF_MODE_SETSP_MASK; + switch (speed) { + case SPEED_1000: + if_mode |= IF_MODE_SETSP_1000M; + break; + case SPEED_100: + if_mode |= IF_MODE_SETSP_100M; + break; + case SPEED_10: + if_mode |= IF_MODE_SETSP_10M; + default: + break; + } + } + + debug(" %s, if_mode = %x\n", __func__, if_mode); + debug(" %s, if_status = %x\n", __func__, if_status); + out_be32(®s->if_mode, if_mode); + return; +} + +void init_memac(struct fsl_enet_mac *mac, void *base, + void *phyregs, int max_rx_len) +{ + mac->base = base; + mac->phyregs = phyregs; + mac->max_rx_len = max_rx_len; + mac->init_mac = memac_init_mac; + mac->enable_mac = memac_enable_mac; + mac->disable_mac = memac_disable_mac; + mac->set_mac_addr = memac_set_mac_addr; + mac->set_if_mode = memac_set_interface_mode; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/memac_phy.c b/qemu/roms/u-boot/drivers/net/fm/memac_phy.c new file mode 100644 index 000000000..de9c0e9cd --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/memac_phy.c @@ -0,0 +1,145 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Andy Fleming + * Roy Zang + * + * SPDX-License-Identifier: GPL-2.0+ + * Some part is taken from tsec.c + */ +#include +#include +#include +#include +#include +#include + +/* + * Write value to the PHY for this device to the register at regnum, waiting + * until the write is done before it returns. All PHY configuration has to be + * done through the TSEC1 MIIM regs + */ +int memac_mdio_write(struct mii_dev *bus, int port_addr, int dev_addr, + int regnum, u16 value) +{ + u32 mdio_ctl; + struct memac_mdio_controller *regs = bus->priv; + u32 c45 = 1; /* Default to 10G interface */ + + if (dev_addr == MDIO_DEVAD_NONE) { + c45 = 0; /* clause 22 */ + dev_addr = regnum & 0x1f; + clrbits_be32(®s->mdio_stat, MDIO_STAT_ENC); + } else + setbits_be32(®s->mdio_stat, MDIO_STAT_ENC); + + /* Wait till the bus is free */ + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Set the port and dev addr */ + mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr); + out_be32(®s->mdio_ctl, mdio_ctl); + + /* Set the register address */ + if (c45) + out_be32(®s->mdio_addr, regnum & 0xffff); + + /* Wait till the bus is free */ + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Write the value to the register */ + out_be32(®s->mdio_data, MDIO_DATA(value)); + + /* Wait till the MDIO write is complete */ + while ((in_be32(®s->mdio_data)) & MDIO_DATA_BSY) + ; + + return 0; +} + +/* + * Reads from register regnum in the PHY for device dev, returning the value. + * Clears miimcom first. All PHY configuration has to be done through the + * TSEC1 MIIM regs + */ +int memac_mdio_read(struct mii_dev *bus, int port_addr, int dev_addr, + int regnum) +{ + u32 mdio_ctl; + struct memac_mdio_controller *regs = bus->priv; + u32 c45 = 1; + + if (dev_addr == MDIO_DEVAD_NONE) { + c45 = 0; /* clause 22 */ + dev_addr = regnum & 0x1f; + clrbits_be32(®s->mdio_stat, MDIO_STAT_ENC); + } else + setbits_be32(®s->mdio_stat, MDIO_STAT_ENC); + + /* Wait till the bus is free */ + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Set the Port and Device Addrs */ + mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr); + out_be32(®s->mdio_ctl, mdio_ctl); + + /* Set the register address */ + if (c45) + out_be32(®s->mdio_addr, regnum & 0xffff); + + /* Wait till the bus is free */ + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Initiate the read */ + mdio_ctl |= MDIO_CTL_READ; + out_be32(®s->mdio_ctl, mdio_ctl); + + /* Wait till the MDIO write is complete */ + while ((in_be32(®s->mdio_data)) & MDIO_DATA_BSY) + ; + + /* Return all Fs if nothing was there */ + if (in_be32(®s->mdio_stat) & MDIO_STAT_RD_ER) + return 0xffff; + + return in_be32(®s->mdio_data) & 0xffff; +} + +int memac_mdio_reset(struct mii_dev *bus) +{ + return 0; +} + +int fm_memac_mdio_init(bd_t *bis, struct memac_mdio_info *info) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) { + printf("Failed to allocate FM TGEC MDIO bus\n"); + return -1; + } + + bus->read = memac_mdio_read; + bus->write = memac_mdio_write; + bus->reset = memac_mdio_reset; + sprintf(bus->name, info->name); + + bus->priv = info->regs; + + /* + * On some platforms like B4860, default value of MDIO_CLK_DIV bits + * in mdio_stat(mdio_cfg) register generates MDIO clock too high + * (much higher than 2.5MHz), violating the IEEE specs. + * On other platforms like T1040, default value of MDIO_CLK_DIV bits + * is zero, so MDIO clock is disabled. + * So, for proper functioning of MDIO, MDIO_CLK_DIV bits needs to + * be properly initialized. + */ + setbits_be32(&((struct memac_mdio_controller *)info->regs)->mdio_stat, + MDIO_STAT_CLKDIV(258)); + + return mdio_register(bus); +} diff --git a/qemu/roms/u-boot/drivers/net/fm/p1023.c b/qemu/roms/u-boot/drivers/net/fm/p1023.c new file mode 100644 index 000000000..b25d10ae0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/p1023.c @@ -0,0 +1,73 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include + +static u32 port_to_devdisr[] = { + [FM1_DTSEC1] = MPC85xx_DEVDISR_TSEC1, + [FM1_DTSEC2] = MPC85xx_DEVDISR_TSEC2, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr = in_be32(&gur->devdisr); + + return port_to_devdisr[port] & devdisr; +} + +void fman_disable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + /* don't allow disabling of DTSEC1 as its needed for MDIO */ + if (port == FM1_DTSEC1) + return; + + setbits_be32(&gur->devdisr, port_to_devdisr[port]); +} + +void fman_enable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + clrbits_be32(&gur->devdisr, port_to_devdisr[port]); +} + +phy_interface_t fman_port_enet_if(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 pordevsr = in_be32(&gur->pordevsr); + + if (is_device_disabled(port)) + return PHY_INTERFACE_MODE_NONE; + + /* DTSEC1 can be SGMII, RGMII or RMII */ + if (port == FM1_DTSEC1) { + if (is_serdes_configured(SGMII_FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + if (pordevsr & MPC85xx_PORDEVSR_SGMII1_DIS) { + if (pordevsr & MPC85xx_PORDEVSR_TSEC1_PRTC) + return PHY_INTERFACE_MODE_RGMII; + else + return PHY_INTERFACE_MODE_RMII; + } + } + + /* DTSEC2 only supports SGMII or RGMII */ + if (port == FM1_DTSEC2) { + if (is_serdes_configured(SGMII_FM1_DTSEC2)) + return PHY_INTERFACE_MODE_SGMII; + if (pordevsr & MPC85xx_PORDEVSR_SGMII2_DIS) + return PHY_INTERFACE_MODE_RGMII; + } + + return PHY_INTERFACE_MODE_NONE; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/p4080.c b/qemu/roms/u-boot/drivers/net/fm/p4080.c new file mode 100644 index 000000000..de7191135 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/p4080.c @@ -0,0 +1,99 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include + +static u32 port_to_devdisr[] = { + [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, + [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2, + [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3, + [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4, + [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1, + [FM2_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC2_1, + [FM2_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC2_2, + [FM2_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC2_3, + [FM2_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC2_4, + [FM2_10GEC1] = FSL_CORENET_DEVDISR2_10GEC2, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return port_to_devdisr[port] & devdisr2; +} + +void fman_disable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + /* don't allow disabling of DTSEC1 as its needed for MDIO */ + if (port == FM1_DTSEC1) + return; + + setbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +void fman_enable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + clrbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +phy_interface_t fman_port_enet_if(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 rcwsr11 = in_be32(&gur->rcwsr[11]); + + if (is_device_disabled(port)) + return PHY_INTERFACE_MODE_NONE; + + if ((port == FM1_10GEC1) && (is_serdes_configured(XAUI_FM1))) + return PHY_INTERFACE_MODE_XGMII; + + if ((port == FM2_10GEC1) && (is_serdes_configured(XAUI_FM2))) + return PHY_INTERFACE_MODE_XGMII; + + /* handle RGMII first */ + if ((port == FM1_DTSEC1) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) == + FSL_CORENET_RCWSR11_EC1_FM1_DTSEC1)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM1_DTSEC2) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) == + FSL_CORENET_RCWSR11_EC2_FM1_DTSEC2)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM2_DTSEC1) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) == + FSL_CORENET_RCWSR11_EC2_FM2_DTSEC1)) + return PHY_INTERFACE_MODE_RGMII; + + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + case FM2_DTSEC1: + case FM2_DTSEC2: + case FM2_DTSEC3: + case FM2_DTSEC4: + if (is_serdes_configured(SGMII_FM2_DTSEC1 + port - FM2_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + default: + return PHY_INTERFACE_MODE_NONE; + } + + return PHY_INTERFACE_MODE_NONE; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/p5020.c b/qemu/roms/u-boot/drivers/net/fm/p5020.c new file mode 100644 index 000000000..5c158cd5d --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/p5020.c @@ -0,0 +1,90 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include + +static u32 port_to_devdisr[] = { + [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, + [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2, + [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3, + [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4, + [FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5, + [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return port_to_devdisr[port] & devdisr2; +} + +void fman_disable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + /* don't allow disabling of DTSEC1 as its needed for MDIO */ + if (port == FM1_DTSEC1) + return; + + setbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +void fman_enable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + clrbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +phy_interface_t fman_port_enet_if(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 rcwsr11 = in_be32(&gur->rcwsr[11]); + + if (is_device_disabled(port)) + return PHY_INTERFACE_MODE_NONE; + + if ((port == FM1_10GEC1) && (is_serdes_configured(XAUI_FM1))) + return PHY_INTERFACE_MODE_XGMII; + + /* handle RGMII first */ + if ((port == FM1_DTSEC4) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) == + FSL_CORENET_RCWSR11_EC1_FM1_DTSEC4_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM1_DTSEC4) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) == + FSL_CORENET_RCWSR11_EC1_FM1_DTSEC4_MII)) + return PHY_INTERFACE_MODE_MII; + + if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) == + FSL_CORENET_RCWSR11_EC2_FM1_DTSEC5_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) == + FSL_CORENET_RCWSR11_EC2_FM1_DTSEC5_MII)) + return PHY_INTERFACE_MODE_MII; + + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + case FM1_DTSEC5: + if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + default: + return PHY_INTERFACE_MODE_NONE; + } + + return PHY_INTERFACE_MODE_NONE; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/p5040.c b/qemu/roms/u-boot/drivers/net/fm/p5040.c new file mode 100644 index 000000000..403d7d794 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/p5040.c @@ -0,0 +1,107 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include + +u32 port_to_devdisr[] = { + [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, + [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2, + [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3, + [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4, + [FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5, + [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1, + [FM2_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC2_1, + [FM2_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC2_2, + [FM2_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC2_3, + [FM2_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC2_4, + [FM2_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC2_5, + [FM2_10GEC1] = FSL_CORENET_DEVDISR2_10GEC2, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return port_to_devdisr[port] & devdisr2; +} + +void fman_disable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + /* don't allow disabling of DTSEC1 as its needed for MDIO */ + if (port == FM1_DTSEC1) + return; + + setbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +void fman_enable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + clrbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +phy_interface_t fman_port_enet_if(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 rcwsr11 = in_be32(&gur->rcwsr[11]); + + if (is_device_disabled(port)) + return PHY_INTERFACE_MODE_NONE; + + if ((port == FM1_10GEC1) && (is_serdes_configured(XAUI_FM1))) + return PHY_INTERFACE_MODE_XGMII; + + if ((port == FM2_10GEC1) && (is_serdes_configured(XAUI_FM2))) + return PHY_INTERFACE_MODE_XGMII; + + /* handle RGMII first */ + if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) == + FSL_CORENET_RCWSR11_EC1_FM1_DTSEC5_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) == + FSL_CORENET_RCWSR11_EC1_FM1_DTSEC5_MII)) + return PHY_INTERFACE_MODE_MII; + + if ((port == FM2_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) == + FSL_CORENET_RCWSR11_EC2_FM2_DTSEC5_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM2_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) == + FSL_CORENET_RCWSR11_EC2_FM2_DTSEC5_MII)) + return PHY_INTERFACE_MODE_MII; + + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + case FM1_DTSEC5: + if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + case FM2_DTSEC1: + case FM2_DTSEC2: + case FM2_DTSEC3: + case FM2_DTSEC4: + case FM2_DTSEC5: + if (is_serdes_configured(SGMII_FM2_DTSEC1 + port - FM2_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + default: + return PHY_INTERFACE_MODE_NONE; + } + + return PHY_INTERFACE_MODE_NONE; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/t1040.c b/qemu/roms/u-boot/drivers/net/fm/t1040.c new file mode 100644 index 000000000..bcc871d84 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/t1040.c @@ -0,0 +1,72 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include + +phy_interface_t fman_port_enet_if(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 rcwsr13 = in_be32(&gur->rcwsr[13]); + + /* handle RGMII first */ + if ((port == FM1_DTSEC2) && + ((rcwsr13 & FSL_CORENET_RCWSR13_MAC2_GMII_SEL) == + FSL_CORENET_RCWSR13_MAC2_GMII_SEL_ENET_PORT)) { + if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) == + FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_RGMII) + return PHY_INTERFACE_MODE_RGMII; + else if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) == + FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_MII) + return PHY_INTERFACE_MODE_MII; + else + return PHY_INTERFACE_MODE_NONE; + } + + if ((port == FM1_DTSEC4) && + ((rcwsr13 & FSL_CORENET_RCWSR13_MAC2_GMII_SEL) == + FSL_CORENET_RCWSR13_MAC2_GMII_SEL_L2_SWITCH)) { + if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) == + FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_RGMII) + return PHY_INTERFACE_MODE_RGMII; + else if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) == + FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_MII) + return PHY_INTERFACE_MODE_MII; + else + return PHY_INTERFACE_MODE_NONE; + } + + if (port == FM1_DTSEC5) { + if ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) == + FSL_CORENET_RCWSR13_EC2_FM1_DTSEC5_RGMII) + return PHY_INTERFACE_MODE_RGMII; + else if ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) == + FSL_CORENET_RCWSR13_EC2_FM1_DTSEC5_MII) + return PHY_INTERFACE_MODE_MII; + else + return PHY_INTERFACE_MODE_NONE; + } + + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + if (is_serdes_configured(QSGMII_SW1_A + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_QSGMII; + case FM1_DTSEC3: + case FM1_DTSEC4: + case FM1_DTSEC5: + if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + default: + return PHY_INTERFACE_MODE_NONE; + } + + return PHY_INTERFACE_MODE_NONE; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/t2080.c b/qemu/roms/u-boot/drivers/net/fm/t2080.c new file mode 100644 index 000000000..3b6212f85 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/t2080.c @@ -0,0 +1,93 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * Shengzhou Liu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +u32 port_to_devdisr[] = { + [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, + [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2, + [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3, + [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4, + [FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5, + [FM1_DTSEC6] = FSL_CORENET_DEVDISR2_DTSEC1_6, + [FM1_DTSEC9] = FSL_CORENET_DEVDISR2_DTSEC1_9, + [FM1_DTSEC10] = FSL_CORENET_DEVDISR2_DTSEC1_10, + [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1_1, + [FM1_10GEC2] = FSL_CORENET_DEVDISR2_10GEC1_2, + [FM1_10GEC3] = FSL_CORENET_DEVDISR2_10GEC1_3, + [FM1_10GEC4] = FSL_CORENET_DEVDISR2_10GEC1_4, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return port_to_devdisr[port] & devdisr2; +} + +void fman_disable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + setbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +phy_interface_t fman_port_enet_if(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 rcwsr13 = in_be32(&gur->rcwsr[13]); + + if (is_device_disabled(port)) + return PHY_INTERFACE_MODE_NONE; + + if ((port == FM1_10GEC1 || port == FM1_10GEC2) && + ((is_serdes_configured(XAUI_FM1_MAC9)) || + (is_serdes_configured(XFI_FM1_MAC9)) || + (is_serdes_configured(XFI_FM1_MAC10)))) + return PHY_INTERFACE_MODE_XGMII; + + if ((port == FM1_10GEC3 || port == FM1_10GEC4) && + ((is_serdes_configured(XFI_FM1_MAC1)) || + (is_serdes_configured(XFI_FM1_MAC2)))) + return PHY_INTERFACE_MODE_XGMII; + + if ((port == FM1_DTSEC3) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) == + FSL_CORENET_RCWSR13_EC1_DTSEC3_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM1_DTSEC4) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) == + FSL_CORENET_RCWSR13_EC2_DTSEC4_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM1_DTSEC10) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) == + FSL_CORENET_RCWSR13_EC2_DTSEC10_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + case FM1_DTSEC5: + case FM1_DTSEC6: + case FM1_DTSEC9: + case FM1_DTSEC10: + if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + default: + return PHY_INTERFACE_MODE_NONE; + } + + return PHY_INTERFACE_MODE_NONE; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/t4240.c b/qemu/roms/u-boot/drivers/net/fm/t4240.c new file mode 100644 index 000000000..1eacb2284 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/t4240.c @@ -0,0 +1,166 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Roy Zang + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include + +u32 port_to_devdisr[] = { + [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, + [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2, + [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3, + [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4, + [FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5, + [FM1_DTSEC6] = FSL_CORENET_DEVDISR2_DTSEC1_6, + [FM1_DTSEC9] = FSL_CORENET_DEVDISR2_DTSEC1_9, + [FM1_DTSEC10] = FSL_CORENET_DEVDISR2_DTSEC1_10, + [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1_1, + [FM1_10GEC2] = FSL_CORENET_DEVDISR2_10GEC1_2, + [FM2_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC2_1, + [FM2_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC2_2, + [FM2_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC2_3, + [FM2_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC2_4, + [FM2_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC2_5, + [FM2_DTSEC6] = FSL_CORENET_DEVDISR2_DTSEC2_6, + [FM2_DTSEC9] = FSL_CORENET_DEVDISR2_DTSEC2_9, + [FM2_DTSEC10] = FSL_CORENET_DEVDISR2_DTSEC2_10, + [FM2_10GEC1] = FSL_CORENET_DEVDISR2_10GEC2_1, + [FM2_10GEC2] = FSL_CORENET_DEVDISR2_10GEC2_2, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return port_to_devdisr[port] & devdisr2; +} + +void fman_disable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + setbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +void fman_enable_port(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + clrbits_be32(&gur->devdisr2, port_to_devdisr[port]); +} + +phy_interface_t fman_port_enet_if(enum fm_port port) +{ + ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 rcwsr13 = in_be32(&gur->rcwsr[13]); + + if (is_device_disabled(port)) + return PHY_INTERFACE_MODE_NONE; + + if ((port == FM1_10GEC1 || port == FM1_10GEC2) && + ((is_serdes_configured(XAUI_FM1_MAC9)) || + (is_serdes_configured(XAUI_FM1_MAC10)) || + (is_serdes_configured(XFI_FM1_MAC9)) || + (is_serdes_configured(XFI_FM1_MAC10)))) + return PHY_INTERFACE_MODE_XGMII; + + if ((port == FM2_10GEC1 || port == FM2_10GEC2) && + ((is_serdes_configured(XAUI_FM2_MAC9)) || + (is_serdes_configured(XAUI_FM2_MAC10)) || + (is_serdes_configured(XFI_FM2_MAC9)) || + (is_serdes_configured(XFI_FM2_MAC10)))) + return PHY_INTERFACE_MODE_XGMII; + +#define FSL_CORENET_RCWSR13_EC1 0x60000000 /* bits 417..418 */ +#define FSL_CORENET_RCWSR13_EC1_FM2_DTSEC5_RGMII 0x00000000 +#define FSL_CORENET_RCWSR13_EC1_FM2_GPIO 0x40000000 +#define FSL_CORENET_RCWSR13_EC2 0x18000000 /* bits 419..420 */ +#define FSL_CORENET_RCWSR13_EC2_FM1_DTSEC5_RGMII 0x00000000 +#define FSL_CORENET_RCWSR13_EC2_FM2_DTSEC6_RGMII 0x08000000 +#define FSL_CORENET_RCWSR13_EC2_FM1_GPIO 0x10000000 + /* handle RGMII first */ + if ((port == FM2_DTSEC5) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) == + FSL_CORENET_RCWSR13_EC1_FM2_DTSEC5_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM1_DTSEC5) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) == + FSL_CORENET_RCWSR13_EC2_FM1_DTSEC5_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + + if ((port == FM2_DTSEC6) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) == + FSL_CORENET_RCWSR13_EC2_FM2_DTSEC6_RGMII)) + return PHY_INTERFACE_MODE_RGMII; + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + case FM1_DTSEC5: + case FM1_DTSEC6: + case FM1_DTSEC9: + case FM1_DTSEC10: + if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + case FM2_DTSEC1: + case FM2_DTSEC2: + case FM2_DTSEC3: + case FM2_DTSEC4: + case FM2_DTSEC5: + case FM2_DTSEC6: + case FM2_DTSEC9: + case FM2_DTSEC10: + if (is_serdes_configured(SGMII_FM2_DTSEC1 + port - FM2_DTSEC1)) + return PHY_INTERFACE_MODE_SGMII; + break; + default: + break; + } + + /* handle QSGMII */ + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + /* check lane G on SerDes1 */ + if (is_serdes_configured(QSGMII_FM1_A)) + return PHY_INTERFACE_MODE_QSGMII; + break; + case FM1_DTSEC5: + case FM1_DTSEC6: + case FM1_DTSEC9: + case FM1_DTSEC10: + /* check lane C on SerDes1 */ + if (is_serdes_configured(QSGMII_FM1_B)) + return PHY_INTERFACE_MODE_QSGMII; + break; + case FM2_DTSEC1: + case FM2_DTSEC2: + case FM2_DTSEC3: + case FM2_DTSEC4: + /* check lane G on SerDes2 */ + if (is_serdes_configured(QSGMII_FM2_A)) + return PHY_INTERFACE_MODE_QSGMII; + break; + case FM2_DTSEC5: + case FM2_DTSEC6: + case FM2_DTSEC9: + case FM2_DTSEC10: + /* check lane C on SerDes2 */ + if (is_serdes_configured(QSGMII_FM2_B)) + return PHY_INTERFACE_MODE_QSGMII; + break; + default: + break; + } + + return PHY_INTERFACE_MODE_NONE; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/tgec.c b/qemu/roms/u-boot/drivers/net/fm/tgec.c new file mode 100644 index 000000000..f450f800e --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/tgec.c @@ -0,0 +1,106 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * Dave Liu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* MAXFRM - maximum frame length */ +#define MAXFRM_MASK 0x0000ffff + +#include +#include +#include +#include +#include +#include + +#include "fm.h" + +#define TGEC_CMD_CFG_INIT (TGEC_CMD_CFG_NO_LEN_CHK | \ + TGEC_CMD_CFG_RX_ER_DISC | \ + TGEC_CMD_CFG_STAT_CLR | \ + TGEC_CMD_CFG_PAUSE_IGNORE | \ + TGEC_CMD_CFG_CRC_FWD) +#define TGEC_CMD_CFG_FINAL (TGEC_CMD_CFG_NO_LEN_CHK | \ + TGEC_CMD_CFG_RX_ER_DISC | \ + TGEC_CMD_CFG_PAUSE_IGNORE | \ + TGEC_CMD_CFG_CRC_FWD) + +static void tgec_init_mac(struct fsl_enet_mac *mac) +{ + struct tgec *regs = mac->base; + + /* mask all interrupt */ + out_be32(®s->imask, IMASK_MASK_ALL); + + /* clear all events */ + out_be32(®s->ievent, IEVENT_CLEAR_ALL); + + /* set the max receive length */ + out_be32(®s->maxfrm, mac->max_rx_len & MAXFRM_MASK); + + /* + * 1588 disable, insert second mac disable payload length check + * disable, normal operation, any rx error frame is discarded, clear + * counters, pause frame ignore, no promiscuous, LAN mode Rx CRC no + * strip, Tx CRC append, Rx disable and Tx disable + */ + out_be32(®s->command_config, TGEC_CMD_CFG_INIT); + udelay(1000); + out_be32(®s->command_config, TGEC_CMD_CFG_FINAL); + + /* multicast frame reception for the hash entry disable */ + out_be32(®s->hashtable_ctrl, 0); +} + +static void tgec_enable_mac(struct fsl_enet_mac *mac) +{ + struct tgec *regs = mac->base; + + setbits_be32(®s->command_config, TGEC_CMD_CFG_RXTX_EN); +} + +static void tgec_disable_mac(struct fsl_enet_mac *mac) +{ + struct tgec *regs = mac->base; + + clrbits_be32(®s->command_config, TGEC_CMD_CFG_RXTX_EN); +} + +static void tgec_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr) +{ + struct tgec *regs = mac->base; + u32 mac_addr0, mac_addr1; + + /* + * if a station address of 0x12345678ABCD, perform a write to + * MAC_ADDR0 of 0x78563412, MAC_ADDR1 of 0x0000CDAB + */ + mac_addr0 = (mac_addr[3] << 24) | (mac_addr[2] << 16) | \ + (mac_addr[1] << 8) | (mac_addr[0]); + out_be32(®s->mac_addr_0, mac_addr0); + + mac_addr1 = ((mac_addr[5] << 8) | mac_addr[4]) & 0x0000ffff; + out_be32(®s->mac_addr_1, mac_addr1); +} + +static void tgec_set_interface_mode(struct fsl_enet_mac *mac, + phy_interface_t type, int speed) +{ + /* nothing right now */ + return; +} + +void init_tgec(struct fsl_enet_mac *mac, void *base, + void *phyregs, int max_rx_len) +{ + mac->base = base; + mac->phyregs = phyregs; + mac->max_rx_len = max_rx_len; + mac->init_mac = tgec_init_mac; + mac->enable_mac = tgec_enable_mac; + mac->disable_mac = tgec_disable_mac; + mac->set_mac_addr = tgec_set_mac_addr; + mac->set_if_mode = tgec_set_interface_mode; +} diff --git a/qemu/roms/u-boot/drivers/net/fm/tgec_phy.c b/qemu/roms/u-boot/drivers/net/fm/tgec_phy.c new file mode 100644 index 000000000..faec317a0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fm/tgec_phy.c @@ -0,0 +1,126 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * Andy Fleming + * + * SPDX-License-Identifier: GPL-2.0+ + * Some part is taken from tsec.c + */ +#include +#include +#include +#include +#include +#include + +/* + * Write value to the PHY for this device to the register at regnum, waiting + * until the write is done before it returns. All PHY configuration has to be + * done through the TSEC1 MIIM regs + */ +static int tgec_mdio_write(struct mii_dev *bus, int port_addr, int dev_addr, + int regnum, u16 value) +{ + u32 mdio_ctl; + u32 stat_val; + struct tgec_mdio_controller *regs = bus->priv; + + if (dev_addr == MDIO_DEVAD_NONE) + return 0; + + /* Wait till the bus is free */ + stat_val = MDIO_STAT_CLKDIV(100); + out_be32(®s->mdio_stat, stat_val); + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Set the port and dev addr */ + mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr); + out_be32(®s->mdio_ctl, mdio_ctl); + + /* Set the register address */ + out_be32(®s->mdio_addr, regnum & 0xffff); + + /* Wait till the bus is free */ + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Write the value to the register */ + out_be32(®s->mdio_data, MDIO_DATA(value)); + + /* Wait till the MDIO write is complete */ + while ((in_be32(®s->mdio_data)) & MDIO_DATA_BSY) + ; + + return 0; +} + +/* + * Reads from register regnum in the PHY for device dev, returning the value. + * Clears miimcom first. All PHY configuration has to be done through the + * TSEC1 MIIM regs + */ +static int tgec_mdio_read(struct mii_dev *bus, int port_addr, int dev_addr, + int regnum) +{ + u32 mdio_ctl; + u32 stat_val; + struct tgec_mdio_controller *regs = bus->priv; + + if (dev_addr == MDIO_DEVAD_NONE) + return 0xffff; + + stat_val = MDIO_STAT_CLKDIV(100); + out_be32(®s->mdio_stat, stat_val); + /* Wait till the bus is free */ + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Set the Port and Device Addrs */ + mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr); + out_be32(®s->mdio_ctl, mdio_ctl); + + /* Set the register address */ + out_be32(®s->mdio_addr, regnum & 0xffff); + + /* Wait till the bus is free */ + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Initiate the read */ + mdio_ctl |= MDIO_CTL_READ; + out_be32(®s->mdio_ctl, mdio_ctl); + + /* Wait till the MDIO write is complete */ + while ((in_be32(®s->mdio_data)) & MDIO_DATA_BSY) + ; + + /* Return all Fs if nothing was there */ + if (in_be32(®s->mdio_stat) & MDIO_STAT_RD_ER) + return 0xffff; + + return in_be32(®s->mdio_data) & 0xffff; +} + +static int tgec_mdio_reset(struct mii_dev *bus) +{ + return 0; +} + +int fm_tgec_mdio_init(bd_t *bis, struct tgec_mdio_info *info) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) { + printf("Failed to allocate FM TGEC MDIO bus\n"); + return -1; + } + + bus->read = tgec_mdio_read; + bus->write = tgec_mdio_write; + bus->reset = tgec_mdio_reset; + sprintf(bus->name, info->name); + + bus->priv = info->regs; + + return mdio_register(bus); +} diff --git a/qemu/roms/u-boot/drivers/net/fsl_mcdmafec.c b/qemu/roms/u-boot/drivers/net/fsl_mcdmafec.c new file mode 100644 index 000000000..6391f9b32 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fsl_mcdmafec.c @@ -0,0 +1,571 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#undef ET_DEBUG +#undef MII_DEBUG + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1536 +#define LAST_PKTBUFSRX PKTBUFSRX - 1 +#define BD_ENET_RX_W_E (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY) +#define BD_ENET_TX_RDY_LST (BD_ENET_TX_READY | BD_ENET_TX_LAST) +#define FIFO_ERRSTAT (FIFO_STAT_RXW | FIFO_STAT_UF | FIFO_STAT_OF) + +/* RxBD bits definitions */ +#define BD_ENET_RX_ERR (BD_ENET_RX_LG | BD_ENET_RX_NO | BD_ENET_RX_CR | \ + BD_ENET_RX_OV | BD_ENET_RX_TR) + +#include +#include + +#include "MCD_dma.h" + +DECLARE_GLOBAL_DATA_PTR; + +struct fec_info_dma fec_info[] = { +#ifdef CONFIG_SYS_FEC0_IOBASE + { + 0, /* index */ + CONFIG_SYS_FEC0_IOBASE, /* io base */ + CONFIG_SYS_FEC0_PINMUX, /* gpio pin muxing */ + CONFIG_SYS_FEC0_MIIBASE, /* mii base */ + -1, /* phy_addr */ + 0, /* duplex and speed */ + 0, /* phy name */ + 0, /* phyname init */ + 0, /* RX BD */ + 0, /* TX BD */ + 0, /* rx Index */ + 0, /* tx Index */ + 0, /* tx buffer */ + 0, /* initialized flag */ + (struct fec_info_dma *)-1, /* next */ + FEC0_RX_TASK, /* rxTask */ + FEC0_TX_TASK, /* txTask */ + FEC0_RX_PRIORITY, /* rxPri */ + FEC0_TX_PRIORITY, /* txPri */ + FEC0_RX_INIT, /* rxInit */ + FEC0_TX_INIT, /* txInit */ + 0, /* usedTbdIndex */ + 0, /* cleanTbdNum */ + }, +#endif +#ifdef CONFIG_SYS_FEC1_IOBASE + { + 1, /* index */ + CONFIG_SYS_FEC1_IOBASE, /* io base */ + CONFIG_SYS_FEC1_PINMUX, /* gpio pin muxing */ + CONFIG_SYS_FEC1_MIIBASE, /* mii base */ + -1, /* phy_addr */ + 0, /* duplex and speed */ + 0, /* phy name */ + 0, /* phy name init */ +#ifdef CONFIG_SYS_DMA_USE_INTSRAM + (cbd_t *)DBUF_LENGTH, /* RX BD */ +#else + 0, /* RX BD */ +#endif + 0, /* TX BD */ + 0, /* rx Index */ + 0, /* tx Index */ + 0, /* tx buffer */ + 0, /* initialized flag */ + (struct fec_info_dma *)-1, /* next */ + FEC1_RX_TASK, /* rxTask */ + FEC1_TX_TASK, /* txTask */ + FEC1_RX_PRIORITY, /* rxPri */ + FEC1_TX_PRIORITY, /* txPri */ + FEC1_RX_INIT, /* rxInit */ + FEC1_TX_INIT, /* txInit */ + 0, /* usedTbdIndex */ + 0, /* cleanTbdNum */ + } +#endif +}; + +static int fec_send(struct eth_device *dev, void *packet, int length); +static int fec_recv(struct eth_device *dev); +static int fec_init(struct eth_device *dev, bd_t * bd); +static void fec_halt(struct eth_device *dev); + +#ifdef ET_DEBUG +static void dbg_fec_regs(struct eth_device *dev) +{ + struct fec_info_dma *info = dev->priv; + volatile fecdma_t *fecp = (fecdma_t *) (info->iobase); + + printf("=====\n"); + printf("ievent %x - %x\n", (int)&fecp->eir, fecp->eir); + printf("imask %x - %x\n", (int)&fecp->eimr, fecp->eimr); + printf("ecntrl %x - %x\n", (int)&fecp->ecr, fecp->ecr); + printf("mii_mframe %x - %x\n", (int)&fecp->mmfr, fecp->mmfr); + printf("mii_speed %x - %x\n", (int)&fecp->mscr, fecp->mscr); + printf("mii_ctrlstat %x - %x\n", (int)&fecp->mibc, fecp->mibc); + printf("r_cntrl %x - %x\n", (int)&fecp->rcr, fecp->rcr); + printf("r hash %x - %x\n", (int)&fecp->rhr, fecp->rhr); + printf("x_cntrl %x - %x\n", (int)&fecp->tcr, fecp->tcr); + printf("padr_l %x - %x\n", (int)&fecp->palr, fecp->palr); + printf("padr_u %x - %x\n", (int)&fecp->paur, fecp->paur); + printf("op_pause %x - %x\n", (int)&fecp->opd, fecp->opd); + printf("iadr_u %x - %x\n", (int)&fecp->iaur, fecp->iaur); + printf("iadr_l %x - %x\n", (int)&fecp->ialr, fecp->ialr); + printf("gadr_u %x - %x\n", (int)&fecp->gaur, fecp->gaur); + printf("gadr_l %x - %x\n", (int)&fecp->galr, fecp->galr); + printf("x_wmrk %x - %x\n", (int)&fecp->tfwr, fecp->tfwr); + printf("r_fdata %x - %x\n", (int)&fecp->rfdr, fecp->rfdr); + printf("r_fstat %x - %x\n", (int)&fecp->rfsr, fecp->rfsr); + printf("r_fctrl %x - %x\n", (int)&fecp->rfcr, fecp->rfcr); + printf("r_flrfp %x - %x\n", (int)&fecp->rlrfp, fecp->rlrfp); + printf("r_flwfp %x - %x\n", (int)&fecp->rlwfp, fecp->rlwfp); + printf("r_frfar %x - %x\n", (int)&fecp->rfar, fecp->rfar); + printf("r_frfrp %x - %x\n", (int)&fecp->rfrp, fecp->rfrp); + printf("r_frfwp %x - %x\n", (int)&fecp->rfwp, fecp->rfwp); + printf("t_fdata %x - %x\n", (int)&fecp->tfdr, fecp->tfdr); + printf("t_fstat %x - %x\n", (int)&fecp->tfsr, fecp->tfsr); + printf("t_fctrl %x - %x\n", (int)&fecp->tfcr, fecp->tfcr); + printf("t_flrfp %x - %x\n", (int)&fecp->tlrfp, fecp->tlrfp); + printf("t_flwfp %x - %x\n", (int)&fecp->tlwfp, fecp->tlwfp); + printf("t_ftfar %x - %x\n", (int)&fecp->tfar, fecp->tfar); + printf("t_ftfrp %x - %x\n", (int)&fecp->tfrp, fecp->tfrp); + printf("t_ftfwp %x - %x\n", (int)&fecp->tfwp, fecp->tfwp); + printf("frst %x - %x\n", (int)&fecp->frst, fecp->frst); + printf("ctcwr %x - %x\n", (int)&fecp->ctcwr, fecp->ctcwr); +} +#endif + +static void set_fec_duplex_speed(volatile fecdma_t * fecp, bd_t * bd, + int dup_spd) +{ + if ((dup_spd >> 16) == FULL) { + /* Set maximum frame length */ + fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | FEC_RCR_MII_MODE | + FEC_RCR_PROM | 0x100; + fecp->tcr = FEC_TCR_FDEN; + } else { + /* Half duplex mode */ + fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | + FEC_RCR_MII_MODE | FEC_RCR_DRT; + fecp->tcr &= ~FEC_TCR_FDEN; + } + + if ((dup_spd & 0xFFFF) == _100BASET) { +#ifdef MII_DEBUG + printf("100Mbps\n"); +#endif + bd->bi_ethspeed = 100; + } else { +#ifdef MII_DEBUG + printf("10Mbps\n"); +#endif + bd->bi_ethspeed = 10; + } +} + +static int fec_send(struct eth_device *dev, void *packet, int length) +{ + struct fec_info_dma *info = dev->priv; + cbd_t *pTbd, *pUsedTbd; + u16 phyStatus; + + miiphy_read(dev->name, info->phy_addr, MII_BMSR, &phyStatus); + + /* process all the consumed TBDs */ + while (info->cleanTbdNum < CONFIG_SYS_TX_ETH_BUFFER) { + pUsedTbd = &info->txbd[info->usedTbdIdx]; + if (pUsedTbd->cbd_sc & BD_ENET_TX_READY) { +#ifdef ET_DEBUG + printf("Cannot clean TBD %d, in use\n", + info->cleanTbdNum); +#endif + return 0; + } + + /* clean this buffer descriptor */ + if (info->usedTbdIdx == (CONFIG_SYS_TX_ETH_BUFFER - 1)) + pUsedTbd->cbd_sc = BD_ENET_TX_WRAP; + else + pUsedTbd->cbd_sc = 0; + + /* update some indeces for a correct handling of the TBD ring */ + info->cleanTbdNum++; + info->usedTbdIdx = (info->usedTbdIdx + 1) % CONFIG_SYS_TX_ETH_BUFFER; + } + + /* Check for valid length of data. */ + if ((length > 1500) || (length <= 0)) { + return -1; + } + + /* Check the number of vacant TxBDs. */ + if (info->cleanTbdNum < 1) { + printf("No available TxBDs ...\n"); + return -1; + } + + /* Get the first TxBD to send the mac header */ + pTbd = &info->txbd[info->txIdx]; + pTbd->cbd_datlen = length; + pTbd->cbd_bufaddr = (u32) packet; + pTbd->cbd_sc |= BD_ENET_TX_LAST | BD_ENET_TX_TC | BD_ENET_TX_READY; + info->txIdx = (info->txIdx + 1) % CONFIG_SYS_TX_ETH_BUFFER; + + /* Enable DMA transmit task */ + MCD_continDma(info->txTask); + + info->cleanTbdNum -= 1; + + /* wait until frame is sent . */ + while (pTbd->cbd_sc & BD_ENET_TX_READY) { + udelay(10); + } + + return (int)(info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_STATS); +} + +static int fec_recv(struct eth_device *dev) +{ + struct fec_info_dma *info = dev->priv; + volatile fecdma_t *fecp = (fecdma_t *) (info->iobase); + + cbd_t *pRbd = &info->rxbd[info->rxIdx]; + u32 ievent; + int frame_length, len = 0; + + /* Check if any critical events have happened */ + ievent = fecp->eir; + if (ievent != 0) { + fecp->eir = ievent; + + if (ievent & (FEC_EIR_BABT | FEC_EIR_TXERR | FEC_EIR_RXERR)) { + printf("fec_recv: error\n"); + fec_halt(dev); + fec_init(dev, NULL); + return 0; + } + + if (ievent & FEC_EIR_HBERR) { + /* Heartbeat error */ + fecp->tcr |= FEC_TCR_GTS; + } + + if (ievent & FEC_EIR_GRA) { + /* Graceful stop complete */ + if (fecp->tcr & FEC_TCR_GTS) { + printf("fec_recv: tcr_gts\n"); + fec_halt(dev); + fecp->tcr &= ~FEC_TCR_GTS; + fec_init(dev, NULL); + } + } + } + + if (!(pRbd->cbd_sc & BD_ENET_RX_EMPTY)) { + if ((pRbd->cbd_sc & BD_ENET_RX_LAST) + && !(pRbd->cbd_sc & BD_ENET_RX_ERR) + && ((pRbd->cbd_datlen - 4) > 14)) { + + /* Get buffer address and size */ + frame_length = pRbd->cbd_datlen - 4; + + /* Fill the buffer and pass it to upper layers */ + NetReceive((uchar *)pRbd->cbd_bufaddr, frame_length); + len = frame_length; + } + + /* Reset buffer descriptor as empty */ + if ((info->rxIdx) == (PKTBUFSRX - 1)) + pRbd->cbd_sc = (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); + else + pRbd->cbd_sc = BD_ENET_RX_EMPTY; + + pRbd->cbd_datlen = PKTSIZE_ALIGN; + + /* Now, we have an empty RxBD, restart the DMA receive task */ + MCD_continDma(info->rxTask); + + /* Increment BD count */ + info->rxIdx = (info->rxIdx + 1) % PKTBUFSRX; + } + + return len; +} + +static void fec_set_hwaddr(volatile fecdma_t * fecp, u8 * mac) +{ + u8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + u32 crc = 0xffffffff; /* initial value */ + + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* Set individual hash table register */ + if (crc >= 32) { + fecp->ialr = (1 << (crc - 32)); + fecp->iaur = 0; + } else { + fecp->ialr = 0; + fecp->iaur = (1 << crc); + } + + /* Set physical address */ + fecp->palr = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + fecp->paur = (mac[4] << 24) + (mac[5] << 16) + 0x8808; + + /* Clear multicast address hash table */ + fecp->gaur = 0; + fecp->galr = 0; +} + +static int fec_init(struct eth_device *dev, bd_t * bd) +{ + struct fec_info_dma *info = dev->priv; + volatile fecdma_t *fecp = (fecdma_t *) (info->iobase); + int i; + uchar enetaddr[6]; + +#ifdef ET_DEBUG + printf("fec_init: iobase 0x%08x ...\n", info->iobase); +#endif + + fecpin_setclear(dev, 1); + + fec_halt(dev); + +#if defined(CONFIG_CMD_MII) || defined (CONFIG_MII) || \ + defined (CONFIG_SYS_DISCOVER_PHY) + + mii_init(); + + set_fec_duplex_speed(fecp, bd, info->dup_spd); +#else +#ifndef CONFIG_SYS_DISCOVER_PHY + set_fec_duplex_speed(fecp, bd, (FECDUPLEX << 16) | FECSPEED); +#endif /* ifndef CONFIG_SYS_DISCOVER_PHY */ +#endif /* CONFIG_CMD_MII || CONFIG_MII */ + + /* We use strictly polling mode only */ + fecp->eimr = 0; + + /* Clear any pending interrupt */ + fecp->eir = 0xffffffff; + + /* Set station address */ + if ((u32) fecp == CONFIG_SYS_FEC0_IOBASE) + eth_getenv_enetaddr("ethaddr", enetaddr); + else + eth_getenv_enetaddr("eth1addr", enetaddr); + fec_set_hwaddr(fecp, enetaddr); + + /* Set Opcode/Pause Duration Register */ + fecp->opd = 0x00010020; + + /* Setup Buffers and Buffer Desriptors */ + info->rxIdx = 0; + info->txIdx = 0; + + /* Setup Receiver Buffer Descriptors (13.14.24.18) + * Settings: Empty, Wrap */ + for (i = 0; i < PKTBUFSRX; i++) { + info->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + info->rxbd[i].cbd_datlen = PKTSIZE_ALIGN; + info->rxbd[i].cbd_bufaddr = (uint) NetRxPackets[i]; + } + info->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) + * Settings: Last, Tx CRC */ + for (i = 0; i < CONFIG_SYS_TX_ETH_BUFFER; i++) { + info->txbd[i].cbd_sc = 0; + info->txbd[i].cbd_datlen = 0; + info->txbd[i].cbd_bufaddr = (uint) (&info->txbuf[0]); + } + info->txbd[CONFIG_SYS_TX_ETH_BUFFER - 1].cbd_sc |= BD_ENET_TX_WRAP; + + info->usedTbdIdx = 0; + info->cleanTbdNum = CONFIG_SYS_TX_ETH_BUFFER; + + /* Set Rx FIFO alarm and granularity value */ + fecp->rfcr = 0x0c000000; + fecp->rfar = 0x0000030c; + + /* Set Tx FIFO granularity value */ + fecp->tfcr = FIFO_CTRL_FRAME | FIFO_CTRL_GR(6) | 0x00040000; + fecp->tfar = 0x00000080; + + fecp->tfwr = 0x2; + fecp->ctcwr = 0x03000000; + + /* Enable DMA receive task */ + MCD_startDma(info->rxTask, /* Dma channel */ + (s8 *) info->rxbd, /*Source Address */ + 0, /* Source increment */ + (s8 *) (&fecp->rfdr), /* dest */ + 4, /* dest increment */ + 0, /* DMA size */ + 4, /* xfer size */ + info->rxInit, /* initiator */ + info->rxPri, /* priority */ + (MCD_FECRX_DMA | MCD_TT_FLAGS_DEF), /* Flags */ + (MCD_NO_CSUM | MCD_NO_BYTE_SWAP) /* Function description */ + ); + + /* Enable DMA tx task with no ready buffer descriptors */ + MCD_startDma(info->txTask, /* Dma channel */ + (s8 *) info->txbd, /*Source Address */ + 0, /* Source increment */ + (s8 *) (&fecp->tfdr), /* dest */ + 4, /* dest incr */ + 0, /* DMA size */ + 4, /* xfer size */ + info->txInit, /* initiator */ + info->txPri, /* priority */ + (MCD_FECTX_DMA | MCD_TT_FLAGS_DEF), /* Flags */ + (MCD_NO_CSUM | MCD_NO_BYTE_SWAP) /* Function description */ + ); + + /* Now enable the transmit and receive processing */ + fecp->ecr |= FEC_ECR_ETHER_EN; + + return 1; +} + +static void fec_halt(struct eth_device *dev) +{ + struct fec_info_dma *info = dev->priv; + volatile fecdma_t *fecp = (fecdma_t *) (info->iobase); + int counter = 0xffff; + + /* issue graceful stop command to the FEC transmitter if necessary */ + fecp->tcr |= FEC_TCR_GTS; + + /* wait for graceful stop to register */ + while ((counter--) && (!(fecp->eir & FEC_EIR_GRA))) ; + + /* Disable DMA tasks */ + MCD_killDma(info->txTask); + MCD_killDma(info->rxTask);; + + /* Disable the Ethernet Controller */ + fecp->ecr &= ~FEC_ECR_ETHER_EN; + + /* Clear FIFO status registers */ + fecp->rfsr &= FIFO_ERRSTAT; + fecp->tfsr &= FIFO_ERRSTAT; + + fecp->frst = 0x01000000; + + /* Issue a reset command to the FEC chip */ + fecp->ecr |= FEC_ECR_RESET; + + /* wait at least 20 clock cycles */ + udelay(10000); + +#ifdef ET_DEBUG + printf("Ethernet task stopped\n"); +#endif +} + +int mcdmafec_initialize(bd_t * bis) +{ + struct eth_device *dev; + int i; +#ifdef CONFIG_SYS_DMA_USE_INTSRAM + u32 tmp = CONFIG_SYS_INTSRAM + 0x2000; +#endif + + for (i = 0; i < ARRAY_SIZE(fec_info); i++) { + + dev = + (struct eth_device *)memalign(CONFIG_SYS_CACHELINE_SIZE, + sizeof *dev); + if (dev == NULL) + hang(); + + memset(dev, 0, sizeof(*dev)); + + sprintf(dev->name, "FEC%d", fec_info[i].index); + + dev->priv = &fec_info[i]; + dev->init = fec_init; + dev->halt = fec_halt; + dev->send = fec_send; + dev->recv = fec_recv; + + /* setup Receive and Transmit buffer descriptor */ +#ifdef CONFIG_SYS_DMA_USE_INTSRAM + fec_info[i].rxbd = (cbd_t *)((u32)fec_info[i].rxbd + tmp); + tmp = (u32)fec_info[i].rxbd; + fec_info[i].txbd = + (cbd_t *)((u32)fec_info[i].txbd + tmp + + (PKTBUFSRX * sizeof(cbd_t))); + tmp = (u32)fec_info[i].txbd; + fec_info[i].txbuf = + (char *)((u32)fec_info[i].txbuf + tmp + + (CONFIG_SYS_TX_ETH_BUFFER * sizeof(cbd_t))); + tmp = (u32)fec_info[i].txbuf; +#else + fec_info[i].rxbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + (PKTBUFSRX * sizeof(cbd_t))); + fec_info[i].txbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + (CONFIG_SYS_TX_ETH_BUFFER * sizeof(cbd_t))); + fec_info[i].txbuf = + (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, DBUF_LENGTH); +#endif + +#ifdef ET_DEBUG + printf("rxbd %x txbd %x\n", + (int)fec_info[i].rxbd, (int)fec_info[i].txbd); +#endif + + fec_info[i].phy_name = (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, 32); + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, + mcffec_miiphy_read, mcffec_miiphy_write); +#endif + + if (i > 0) + fec_info[i - 1].next = &fec_info[i]; + } + fec_info[i - 1].next = &fec_info[0]; + + /* default speed */ + bis->bi_ethspeed = 10; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/fsl_mdio.c b/qemu/roms/u-boot/drivers/net/fsl_mdio.c new file mode 100644 index 000000000..1d88e6504 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/fsl_mdio.c @@ -0,0 +1,110 @@ +/* + * Copyright 2009-2010, 2013 Freescale Semiconductor, Inc. + * Jun-jie Zhang + * Mingkai Hu + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include +#include + +void tsec_local_mdio_write(struct tsec_mii_mng __iomem *phyregs, int port_addr, + int dev_addr, int regnum, int value) +{ + int timeout = 1000000; + + out_be32(&phyregs->miimadd, (port_addr << 8) | (regnum & 0x1f)); + out_be32(&phyregs->miimcon, value); + asm("sync"); + + while ((in_be32(&phyregs->miimind) & MIIMIND_BUSY) && timeout--) + ; +} + +int tsec_local_mdio_read(struct tsec_mii_mng __iomem *phyregs, int port_addr, + int dev_addr, int regnum) +{ + int value; + int timeout = 1000000; + + /* Put the address of the phy, and the register + * number into MIIMADD */ + out_be32(&phyregs->miimadd, (port_addr << 8) | (regnum & 0x1f)); + + /* Clear the command register, and wait */ + out_be32(&phyregs->miimcom, 0); + asm("sync"); + + /* Initiate a read command, and wait */ + out_be32(&phyregs->miimcom, MIIMCOM_READ_CYCLE); + asm("sync"); + + /* Wait for the the indication that the read is done */ + while ((in_be32(&phyregs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY)) + && timeout--) + ; + + /* Grab the value read from the PHY */ + value = in_be32(&phyregs->miimstat); + + return value; +} + +static int fsl_pq_mdio_reset(struct mii_dev *bus) +{ + struct tsec_mii_mng __iomem *regs = + (struct tsec_mii_mng __iomem *)bus->priv; + + /* Reset MII (due to new addresses) */ + out_be32(®s->miimcfg, MIIMCFG_RESET_MGMT); + + out_be32(®s->miimcfg, MIIMCFG_INIT_VALUE); + + while (in_be32(®s->miimind) & MIIMIND_BUSY) + ; + + return 0; +} + +int tsec_phy_read(struct mii_dev *bus, int addr, int dev_addr, int regnum) +{ + struct tsec_mii_mng __iomem *phyregs = + (struct tsec_mii_mng __iomem *)bus->priv; + + return tsec_local_mdio_read(phyregs, addr, dev_addr, regnum); +} + +int tsec_phy_write(struct mii_dev *bus, int addr, int dev_addr, int regnum, + u16 value) +{ + struct tsec_mii_mng __iomem *phyregs = + (struct tsec_mii_mng __iomem *)bus->priv; + + tsec_local_mdio_write(phyregs, addr, dev_addr, regnum, value); + + return 0; +} + +int fsl_pq_mdio_init(bd_t *bis, struct fsl_pq_mdio_info *info) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) { + printf("Failed to allocate FSL MDIO bus\n"); + return -1; + } + + bus->read = tsec_phy_read; + bus->write = tsec_phy_write; + bus->reset = fsl_pq_mdio_reset; + sprintf(bus->name, info->name); + + bus->priv = (void *)info->regs; + + return mdio_register(bus); +} diff --git a/qemu/roms/u-boot/drivers/net/ftgmac100.c b/qemu/roms/u-boot/drivers/net/ftgmac100.c new file mode 100644 index 000000000..85193140a --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ftgmac100.c @@ -0,0 +1,583 @@ +/* + * Faraday FTGMAC100 Ethernet + * + * (C) Copyright 2009 Faraday Technology + * Po-Yu Chuang + * + * (C) Copyright 2010 Andes Technology + * Macpaul Lin + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ftgmac100.h" + +#define ETH_ZLEN 60 +#define CFG_XBUF_SIZE 1536 + +/* RBSR - hw default init value is also 0x640 */ +#define RBSR_DEFAULT_VALUE 0x640 + +/* PKTBUFSTX/PKTBUFSRX must both be power of 2 */ +#define PKTBUFSTX 4 /* must be power of 2 */ + +struct ftgmac100_data { + ulong txdes_dma; + struct ftgmac100_txdes *txdes; + ulong rxdes_dma; + struct ftgmac100_rxdes *rxdes; + int tx_index; + int rx_index; + int phy_addr; +}; + +/* + * struct mii_bus functions + */ +static int ftgmac100_mdiobus_read(struct eth_device *dev, int phy_addr, + int regnum) +{ + struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; + int phycr; + int i; + + phycr = readl(&ftgmac100->phycr); + + /* preserve MDC cycle threshold */ + phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK; + + phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) + | FTGMAC100_PHYCR_REGAD(regnum) + | FTGMAC100_PHYCR_MIIRD; + + writel(phycr, &ftgmac100->phycr); + + for (i = 0; i < 10; i++) { + phycr = readl(&ftgmac100->phycr); + + if ((phycr & FTGMAC100_PHYCR_MIIRD) == 0) { + int data; + + data = readl(&ftgmac100->phydata); + return FTGMAC100_PHYDATA_MIIRDATA(data); + } + + mdelay(10); + } + + debug("mdio read timed out\n"); + return -1; +} + +static int ftgmac100_mdiobus_write(struct eth_device *dev, int phy_addr, + int regnum, u16 value) +{ + struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; + int phycr; + int data; + int i; + + phycr = readl(&ftgmac100->phycr); + + /* preserve MDC cycle threshold */ + phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK; + + phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) + | FTGMAC100_PHYCR_REGAD(regnum) + | FTGMAC100_PHYCR_MIIWR; + + data = FTGMAC100_PHYDATA_MIIWDATA(value); + + writel(data, &ftgmac100->phydata); + writel(phycr, &ftgmac100->phycr); + + for (i = 0; i < 10; i++) { + phycr = readl(&ftgmac100->phycr); + + if ((phycr & FTGMAC100_PHYCR_MIIWR) == 0) { + debug("(phycr & FTGMAC100_PHYCR_MIIWR) == 0: " \ + "phy_addr: %x\n", phy_addr); + return 0; + } + + mdelay(1); + } + + debug("mdio write timed out\n"); + return -1; +} + +int ftgmac100_phy_read(struct eth_device *dev, int addr, int reg, u16 *value) +{ + *value = ftgmac100_mdiobus_read(dev , addr, reg); + + if (*value == -1) + return -1; + + return 0; +} + +int ftgmac100_phy_write(struct eth_device *dev, int addr, int reg, u16 value) +{ + if (ftgmac100_mdiobus_write(dev, addr, reg, value) == -1) + return -1; + + return 0; +} + +static int ftgmac100_phy_reset(struct eth_device *dev) +{ + struct ftgmac100_data *priv = dev->priv; + int i; + u16 status, adv; + + adv = ADVERTISE_CSMA | ADVERTISE_ALL; + + ftgmac100_phy_write(dev, priv->phy_addr, MII_ADVERTISE, adv); + + printf("%s: Starting autonegotiation...\n", dev->name); + + ftgmac100_phy_write(dev, priv->phy_addr, + MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART)); + + for (i = 0; i < 100000 / 100; i++) { + ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &status); + + if (status & BMSR_ANEGCOMPLETE) + break; + mdelay(1); + } + + if (status & BMSR_ANEGCOMPLETE) { + printf("%s: Autonegotiation complete\n", dev->name); + } else { + printf("%s: Autonegotiation timed out (status=0x%04x)\n", + dev->name, status); + return 0; + } + + return 1; +} + +static int ftgmac100_phy_init(struct eth_device *dev) +{ + struct ftgmac100_data *priv = dev->priv; + + int phy_addr; + u16 phy_id, status, adv, lpa, stat_ge; + int media, speed, duplex; + int i; + + /* Check if the PHY is up to snuff... */ + for (phy_addr = 0; phy_addr < CONFIG_PHY_MAX_ADDR; phy_addr++) { + + ftgmac100_phy_read(dev, phy_addr, MII_PHYSID1, &phy_id); + + /* + * When it is unable to found PHY, + * the interface usually return 0xffff or 0x0000 + */ + if (phy_id != 0xffff && phy_id != 0x0) { + printf("%s: found PHY at 0x%02x\n", + dev->name, phy_addr); + priv->phy_addr = phy_addr; + break; + } + } + + if (phy_id == 0xffff || phy_id == 0x0) { + printf("%s: no PHY present\n", dev->name); + return 0; + } + + ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &status); + + if (!(status & BMSR_LSTATUS)) { + /* Try to re-negotiate if we don't have link already. */ + ftgmac100_phy_reset(dev); + + for (i = 0; i < 100000 / 100; i++) { + ftgmac100_phy_read(dev, priv->phy_addr, + MII_BMSR, &status); + if (status & BMSR_LSTATUS) + break; + udelay(100); + } + } + + if (!(status & BMSR_LSTATUS)) { + printf("%s: link down\n", dev->name); + return 0; + } + +#ifdef CONFIG_FTGMAC100_EGIGA + /* 1000 Base-T Status Register */ + ftgmac100_phy_read(dev, priv->phy_addr, + MII_STAT1000, &stat_ge); + + speed = (stat_ge & (LPA_1000FULL | LPA_1000HALF) + ? 1 : 0); + + duplex = ((stat_ge & LPA_1000FULL) + ? 1 : 0); + + if (speed) { /* Speed is 1000 */ + printf("%s: link up, 1000bps %s-duplex\n", + dev->name, duplex ? "full" : "half"); + return 0; + } +#endif + + ftgmac100_phy_read(dev, priv->phy_addr, MII_ADVERTISE, &adv); + ftgmac100_phy_read(dev, priv->phy_addr, MII_LPA, &lpa); + + media = mii_nway_result(lpa & adv); + speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? 1 : 0); + duplex = (media & ADVERTISE_FULL) ? 1 : 0; + + printf("%s: link up, %sMbps %s-duplex\n", + dev->name, speed ? "100" : "10", duplex ? "full" : "half"); + + return 1; +} + +static int ftgmac100_update_link_speed(struct eth_device *dev) +{ + struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; + struct ftgmac100_data *priv = dev->priv; + + unsigned short stat_fe; + unsigned short stat_ge; + unsigned int maccr; + +#ifdef CONFIG_FTGMAC100_EGIGA + /* 1000 Base-T Status Register */ + ftgmac100_phy_read(dev, priv->phy_addr, MII_STAT1000, &stat_ge); +#endif + + ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &stat_fe); + + if (!(stat_fe & BMSR_LSTATUS)) /* link status up? */ + return 0; + + /* read MAC control register and clear related bits */ + maccr = readl(&ftgmac100->maccr) & + ~(FTGMAC100_MACCR_GIGA_MODE | + FTGMAC100_MACCR_FAST_MODE | + FTGMAC100_MACCR_FULLDUP); + +#ifdef CONFIG_FTGMAC100_EGIGA + if (stat_ge & LPA_1000FULL) { + /* set gmac for 1000BaseTX and Full Duplex */ + maccr |= FTGMAC100_MACCR_GIGA_MODE | FTGMAC100_MACCR_FULLDUP; + } + + if (stat_ge & LPA_1000HALF) { + /* set gmac for 1000BaseTX and Half Duplex */ + maccr |= FTGMAC100_MACCR_GIGA_MODE; + } +#endif + + if (stat_fe & BMSR_100FULL) { + /* set MII for 100BaseTX and Full Duplex */ + maccr |= FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_FULLDUP; + } + + if (stat_fe & BMSR_10FULL) { + /* set MII for 10BaseT and Full Duplex */ + maccr |= FTGMAC100_MACCR_FULLDUP; + } + + if (stat_fe & BMSR_100HALF) { + /* set MII for 100BaseTX and Half Duplex */ + maccr |= FTGMAC100_MACCR_FAST_MODE; + } + + if (stat_fe & BMSR_10HALF) { + /* set MII for 10BaseT and Half Duplex */ + /* we have already clear these bits, do nothing */ + ; + } + + /* update MII config into maccr */ + writel(maccr, &ftgmac100->maccr); + + return 1; +} + +/* + * Reset MAC + */ +static void ftgmac100_reset(struct eth_device *dev) +{ + struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; + + debug("%s()\n", __func__); + + writel(FTGMAC100_MACCR_SW_RST, &ftgmac100->maccr); + + while (readl(&ftgmac100->maccr) & FTGMAC100_MACCR_SW_RST) + ; +} + +/* + * Set MAC address + */ +static void ftgmac100_set_mac(struct eth_device *dev, + const unsigned char *mac) +{ + struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; + unsigned int maddr = mac[0] << 8 | mac[1]; + unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; + + debug("%s(%x %x)\n", __func__, maddr, laddr); + + writel(maddr, &ftgmac100->mac_madr); + writel(laddr, &ftgmac100->mac_ladr); +} + +static void ftgmac100_set_mac_from_env(struct eth_device *dev) +{ + eth_getenv_enetaddr("ethaddr", dev->enetaddr); + + ftgmac100_set_mac(dev, dev->enetaddr); +} + +/* + * disable transmitter, receiver + */ +static void ftgmac100_halt(struct eth_device *dev) +{ + struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; + + debug("%s()\n", __func__); + + writel(0, &ftgmac100->maccr); +} + +static int ftgmac100_init(struct eth_device *dev, bd_t *bd) +{ + struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; + struct ftgmac100_data *priv = dev->priv; + struct ftgmac100_txdes *txdes; + struct ftgmac100_rxdes *rxdes; + unsigned int maccr; + void *buf; + int i; + + debug("%s()\n", __func__); + + if (!priv->txdes) { + txdes = dma_alloc_coherent( + sizeof(*txdes) * PKTBUFSTX, &priv->txdes_dma); + if (!txdes) + panic("ftgmac100: out of memory\n"); + memset(txdes, 0, sizeof(*txdes) * PKTBUFSTX); + priv->txdes = txdes; + } + txdes = priv->txdes; + + if (!priv->rxdes) { + rxdes = dma_alloc_coherent( + sizeof(*rxdes) * PKTBUFSRX, &priv->rxdes_dma); + if (!rxdes) + panic("ftgmac100: out of memory\n"); + memset(rxdes, 0, sizeof(*rxdes) * PKTBUFSRX); + priv->rxdes = rxdes; + } + rxdes = priv->rxdes; + + /* set the ethernet address */ + ftgmac100_set_mac_from_env(dev); + + /* disable all interrupts */ + writel(0, &ftgmac100->ier); + + /* initialize descriptors */ + priv->tx_index = 0; + priv->rx_index = 0; + + txdes[PKTBUFSTX - 1].txdes0 = FTGMAC100_TXDES0_EDOTR; + rxdes[PKTBUFSRX - 1].rxdes0 = FTGMAC100_RXDES0_EDORR; + + for (i = 0; i < PKTBUFSTX; i++) { + /* TXBUF_BADR */ + if (!txdes[i].txdes2) { + buf = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE); + if (!buf) + panic("ftgmac100: out of memory\n"); + txdes[i].txdes3 = virt_to_phys(buf); + txdes[i].txdes2 = (uint)buf; + } + txdes[i].txdes1 = 0; + } + + for (i = 0; i < PKTBUFSRX; i++) { + /* RXBUF_BADR */ + if (!rxdes[i].rxdes2) { + buf = NetRxPackets[i]; + rxdes[i].rxdes3 = virt_to_phys(buf); + rxdes[i].rxdes2 = (uint)buf; + } + rxdes[i].rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY; + } + + /* transmit ring */ + writel(priv->txdes_dma, &ftgmac100->txr_badr); + + /* receive ring */ + writel(priv->rxdes_dma, &ftgmac100->rxr_badr); + + /* poll receive descriptor automatically */ + writel(FTGMAC100_APTC_RXPOLL_CNT(1), &ftgmac100->aptc); + + /* config receive buffer size register */ + writel(FTGMAC100_RBSR_SIZE(RBSR_DEFAULT_VALUE), &ftgmac100->rbsr); + + /* enable transmitter, receiver */ + maccr = FTGMAC100_MACCR_TXMAC_EN | + FTGMAC100_MACCR_RXMAC_EN | + FTGMAC100_MACCR_TXDMA_EN | + FTGMAC100_MACCR_RXDMA_EN | + FTGMAC100_MACCR_CRC_APD | + FTGMAC100_MACCR_FULLDUP | + FTGMAC100_MACCR_RX_RUNT | + FTGMAC100_MACCR_RX_BROADPKT; + + writel(maccr, &ftgmac100->maccr); + + if (!ftgmac100_phy_init(dev)) { + if (!ftgmac100_update_link_speed(dev)) + return -1; + } + + return 0; +} + +/* + * Get a data block via Ethernet + */ +static int ftgmac100_recv(struct eth_device *dev) +{ + struct ftgmac100_data *priv = dev->priv; + struct ftgmac100_rxdes *curr_des; + unsigned short rxlen; + + curr_des = &priv->rxdes[priv->rx_index]; + + if (!(curr_des->rxdes0 & FTGMAC100_RXDES0_RXPKT_RDY)) + return -1; + + if (curr_des->rxdes0 & (FTGMAC100_RXDES0_RX_ERR | + FTGMAC100_RXDES0_CRC_ERR | + FTGMAC100_RXDES0_FTL | + FTGMAC100_RXDES0_RUNT | + FTGMAC100_RXDES0_RX_ODD_NB)) { + return -1; + } + + rxlen = FTGMAC100_RXDES0_VDBC(curr_des->rxdes0); + + debug("%s(): RX buffer %d, %x received\n", + __func__, priv->rx_index, rxlen); + + /* invalidate d-cache */ + dma_map_single((void *)curr_des->rxdes2, rxlen, DMA_FROM_DEVICE); + + /* pass the packet up to the protocol layers. */ + NetReceive((void *)curr_des->rxdes2, rxlen); + + /* release buffer to DMA */ + curr_des->rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY; + + priv->rx_index = (priv->rx_index + 1) % PKTBUFSRX; + + return 0; +} + +/* + * Send a data block via Ethernet + */ +static int ftgmac100_send(struct eth_device *dev, void *packet, int length) +{ + struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; + struct ftgmac100_data *priv = dev->priv; + struct ftgmac100_txdes *curr_des = &priv->txdes[priv->tx_index]; + + if (curr_des->txdes0 & FTGMAC100_TXDES0_TXDMA_OWN) { + debug("%s(): no TX descriptor available\n", __func__); + return -1; + } + + debug("%s(%x, %x)\n", __func__, (int)packet, length); + + length = (length < ETH_ZLEN) ? ETH_ZLEN : length; + + memcpy((void *)curr_des->txdes2, (void *)packet, length); + dma_map_single((void *)curr_des->txdes2, length, DMA_TO_DEVICE); + + /* only one descriptor on TXBUF */ + curr_des->txdes0 &= FTGMAC100_TXDES0_EDOTR; + curr_des->txdes0 |= FTGMAC100_TXDES0_FTS | + FTGMAC100_TXDES0_LTS | + FTGMAC100_TXDES0_TXBUF_SIZE(length) | + FTGMAC100_TXDES0_TXDMA_OWN ; + + /* start transmit */ + writel(1, &ftgmac100->txpd); + + debug("%s(): packet sent\n", __func__); + + priv->tx_index = (priv->tx_index + 1) % PKTBUFSTX; + + return 0; +} + +int ftgmac100_initialize(bd_t *bd) +{ + struct eth_device *dev; + struct ftgmac100_data *priv; + + dev = malloc(sizeof *dev); + if (!dev) { + printf("%s(): failed to allocate dev\n", __func__); + goto out; + } + + /* Transmit and receive descriptors should align to 16 bytes */ + priv = memalign(16, sizeof(struct ftgmac100_data)); + if (!priv) { + printf("%s(): failed to allocate priv\n", __func__); + goto free_dev; + } + + memset(dev, 0, sizeof(*dev)); + memset(priv, 0, sizeof(*priv)); + + sprintf(dev->name, "FTGMAC100"); + dev->iobase = CONFIG_FTGMAC100_BASE; + dev->init = ftgmac100_init; + dev->halt = ftgmac100_halt; + dev->send = ftgmac100_send; + dev->recv = ftgmac100_recv; + dev->priv = priv; + + eth_register(dev); + + ftgmac100_reset(dev); + + return 1; + +free_dev: + free(dev); +out: + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/ftgmac100.h b/qemu/roms/u-boot/drivers/net/ftgmac100.h new file mode 100644 index 000000000..71121ba9d --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ftgmac100.h @@ -0,0 +1,243 @@ +/* + * Faraday FTGMAC100 Ethernet + * + * (C) Copyright 2010 Faraday Technology + * Po-Yu Chuang + * + * (C) Copyright 2010 Andes Technology + * Macpaul Lin + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __FTGMAC100_H +#define __FTGMAC100_H + +/* The registers offset table of ftgmac100 */ +struct ftgmac100 { + unsigned int isr; /* 0x00 */ + unsigned int ier; /* 0x04 */ + unsigned int mac_madr; /* 0x08 */ + unsigned int mac_ladr; /* 0x0c */ + unsigned int maht0; /* 0x10 */ + unsigned int maht1; /* 0x14 */ + unsigned int txpd; /* 0x18 */ + unsigned int rxpd; /* 0x1c */ + unsigned int txr_badr; /* 0x20 */ + unsigned int rxr_badr; /* 0x24 */ + unsigned int hptxpd; /* 0x28 */ + unsigned int hptxpd_badr; /* 0x2c */ + unsigned int itc; /* 0x30 */ + unsigned int aptc; /* 0x34 */ + unsigned int dblac; /* 0x38 */ + unsigned int dmafifos; /* 0x3c */ + unsigned int revr; /* 0x40 */ + unsigned int fear; /* 0x44 */ + unsigned int tpafcr; /* 0x48 */ + unsigned int rbsr; /* 0x4c */ + unsigned int maccr; /* 0x50 */ + unsigned int macsr; /* 0x54 */ + unsigned int tm; /* 0x58 */ + unsigned int resv1; /* 0x5c */ /* not defined in spec */ + unsigned int phycr; /* 0x60 */ + unsigned int phydata; /* 0x64 */ + unsigned int fcr; /* 0x68 */ + unsigned int bpr; /* 0x6c */ + unsigned int wolcr; /* 0x70 */ + unsigned int wolsr; /* 0x74 */ + unsigned int wfcrc; /* 0x78 */ + unsigned int resv2; /* 0x7c */ /* not defined in spec */ + unsigned int wfbm1; /* 0x80 */ + unsigned int wfbm2; /* 0x84 */ + unsigned int wfbm3; /* 0x88 */ + unsigned int wfbm4; /* 0x8c */ + unsigned int nptxr_ptr; /* 0x90 */ + unsigned int hptxr_ptr; /* 0x94 */ + unsigned int rxr_ptr; /* 0x98 */ + unsigned int resv3; /* 0x9c */ /* not defined in spec */ + unsigned int tx; /* 0xa0 */ + unsigned int tx_mcol_scol; /* 0xa4 */ + unsigned int tx_ecol_fail; /* 0xa8 */ + unsigned int tx_lcol_und; /* 0xac */ + unsigned int rx; /* 0xb0 */ + unsigned int rx_bc; /* 0xb4 */ + unsigned int rx_mc; /* 0xb8 */ + unsigned int rx_pf_aep; /* 0xbc */ + unsigned int rx_runt; /* 0xc0 */ + unsigned int rx_crcer_ftl; /* 0xc4 */ + unsigned int rx_col_lost; /* 0xc8 */ +}; + +/* + * Interrupt status register & interrupt enable register + */ +#define FTGMAC100_INT_RPKT_BUF (1 << 0) +#define FTGMAC100_INT_RPKT_FIFO (1 << 1) +#define FTGMAC100_INT_NO_RXBUF (1 << 2) +#define FTGMAC100_INT_RPKT_LOST (1 << 3) +#define FTGMAC100_INT_XPKT_ETH (1 << 4) +#define FTGMAC100_INT_XPKT_FIFO (1 << 5) +#define FTGMAC100_INT_NO_NPTXBUF (1 << 6) +#define FTGMAC100_INT_XPKT_LOST (1 << 7) +#define FTGMAC100_INT_AHB_ERR (1 << 8) +#define FTGMAC100_INT_PHYSTS_CHG (1 << 9) +#define FTGMAC100_INT_NO_HPTXBUF (1 << 10) + +/* + * Interrupt timer control register + */ +#define FTGMAC100_ITC_RXINT_CNT(x) (((x) & 0xf) << 0) +#define FTGMAC100_ITC_RXINT_THR(x) (((x) & 0x7) << 4) +#define FTGMAC100_ITC_RXINT_TIME_SEL (1 << 7) +#define FTGMAC100_ITC_TXINT_CNT(x) (((x) & 0xf) << 8) +#define FTGMAC100_ITC_TXINT_THR(x) (((x) & 0x7) << 12) +#define FTGMAC100_ITC_TXINT_TIME_SEL (1 << 15) + +/* + * Automatic polling timer control register + */ +#define FTGMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0) +#define FTGMAC100_APTC_RXPOLL_TIME_SEL (1 << 4) +#define FTGMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8) +#define FTGMAC100_APTC_TXPOLL_TIME_SEL (1 << 12) + +/* + * DMA burst length and arbitration control register + */ +#define FTGMAC100_DBLAC_RXFIFO_LTHR(x) (((x) & 0x7) << 0) +#define FTGMAC100_DBLAC_RXFIFO_HTHR(x) (((x) & 0x7) << 3) +#define FTGMAC100_DBLAC_RX_THR_EN (1 << 6) +#define FTGMAC100_DBLAC_RXBURST_SIZE(x) (((x) & 0x3) << 8) +#define FTGMAC100_DBLAC_TXBURST_SIZE(x) (((x) & 0x3) << 10) +#define FTGMAC100_DBLAC_RXDES_SIZE(x) (((x) & 0xf) << 12) +#define FTGMAC100_DBLAC_TXDES_SIZE(x) (((x) & 0xf) << 16) +#define FTGMAC100_DBLAC_IFG_CNT(x) (((x) & 0x7) << 20) +#define FTGMAC100_DBLAC_IFG_INC (1 << 23) + +/* + * DMA FIFO status register + */ +#define FTGMAC100_DMAFIFOS_RXDMA1_SM(dmafifos) ((dmafifos) & 0xf) +#define FTGMAC100_DMAFIFOS_RXDMA2_SM(dmafifos) (((dmafifos) >> 4) & 0xf) +#define FTGMAC100_DMAFIFOS_RXDMA3_SM(dmafifos) (((dmafifos) >> 8) & 0x7) +#define FTGMAC100_DMAFIFOS_TXDMA1_SM(dmafifos) (((dmafifos) >> 12) & 0xf) +#define FTGMAC100_DMAFIFOS_TXDMA2_SM(dmafifos) (((dmafifos) >> 16) & 0x3) +#define FTGMAC100_DMAFIFOS_TXDMA3_SM(dmafifos) (((dmafifos) >> 18) & 0xf) +#define FTGMAC100_DMAFIFOS_RXFIFO_EMPTY (1 << 26) +#define FTGMAC100_DMAFIFOS_TXFIFO_EMPTY (1 << 27) +#define FTGMAC100_DMAFIFOS_RXDMA_GRANT (1 << 28) +#define FTGMAC100_DMAFIFOS_TXDMA_GRANT (1 << 29) +#define FTGMAC100_DMAFIFOS_RXDMA_REQ (1 << 30) +#define FTGMAC100_DMAFIFOS_TXDMA_REQ (1 << 31) + +/* + * Receive buffer size register + */ +#define FTGMAC100_RBSR_SIZE(x) ((x) & 0x3fff) + +/* + * MAC control register + */ +#define FTGMAC100_MACCR_TXDMA_EN (1 << 0) +#define FTGMAC100_MACCR_RXDMA_EN (1 << 1) +#define FTGMAC100_MACCR_TXMAC_EN (1 << 2) +#define FTGMAC100_MACCR_RXMAC_EN (1 << 3) +#define FTGMAC100_MACCR_RM_VLAN (1 << 4) +#define FTGMAC100_MACCR_HPTXR_EN (1 << 5) +#define FTGMAC100_MACCR_LOOP_EN (1 << 6) +#define FTGMAC100_MACCR_ENRX_IN_HALFTX (1 << 7) +#define FTGMAC100_MACCR_FULLDUP (1 << 8) +#define FTGMAC100_MACCR_GIGA_MODE (1 << 9) +#define FTGMAC100_MACCR_CRC_APD (1 << 10) +#define FTGMAC100_MACCR_RX_RUNT (1 << 12) +#define FTGMAC100_MACCR_JUMBO_LF (1 << 13) +#define FTGMAC100_MACCR_RX_ALL (1 << 14) +#define FTGMAC100_MACCR_HT_MULTI_EN (1 << 15) +#define FTGMAC100_MACCR_RX_MULTIPKT (1 << 16) +#define FTGMAC100_MACCR_RX_BROADPKT (1 << 17) +#define FTGMAC100_MACCR_DISCARD_CRCERR (1 << 18) +#define FTGMAC100_MACCR_FAST_MODE (1 << 19) +#define FTGMAC100_MACCR_SW_RST (1 << 31) + +/* + * PHY control register + */ +#define FTGMAC100_PHYCR_MDC_CYCTHR_MASK 0x3f +#define FTGMAC100_PHYCR_MDC_CYCTHR(x) ((x) & 0x3f) +#define FTGMAC100_PHYCR_PHYAD(x) (((x) & 0x1f) << 16) +#define FTGMAC100_PHYCR_REGAD(x) (((x) & 0x1f) << 21) +#define FTGMAC100_PHYCR_MIIRD (1 << 26) +#define FTGMAC100_PHYCR_MIIWR (1 << 27) + +/* + * PHY data register + */ +#define FTGMAC100_PHYDATA_MIIWDATA(x) ((x) & 0xffff) +#define FTGMAC100_PHYDATA_MIIRDATA(phydata) (((phydata) >> 16) & 0xffff) + +/* + * Transmit descriptor, aligned to 16 bytes + */ +struct ftgmac100_txdes { + unsigned int txdes0; + unsigned int txdes1; + unsigned int txdes2; /* not used by HW */ + unsigned int txdes3; /* TXBUF_BADR */ +} __attribute__ ((aligned(16))); + +#define FTGMAC100_TXDES0_TXBUF_SIZE(x) ((x) & 0x3fff) +#define FTGMAC100_TXDES0_EDOTR (1 << 15) +#define FTGMAC100_TXDES0_CRC_ERR (1 << 19) +#define FTGMAC100_TXDES0_LTS (1 << 28) +#define FTGMAC100_TXDES0_FTS (1 << 29) +#define FTGMAC100_TXDES0_TXDMA_OWN (1 << 31) + +#define FTGMAC100_TXDES1_VLANTAG_CI(x) ((x) & 0xffff) +#define FTGMAC100_TXDES1_INS_VLANTAG (1 << 16) +#define FTGMAC100_TXDES1_TCP_CHKSUM (1 << 17) +#define FTGMAC100_TXDES1_UDP_CHKSUM (1 << 18) +#define FTGMAC100_TXDES1_IP_CHKSUM (1 << 19) +#define FTGMAC100_TXDES1_LLC (1 << 22) +#define FTGMAC100_TXDES1_TX2FIC (1 << 30) +#define FTGMAC100_TXDES1_TXIC (1 << 31) + +/* + * Receive descriptor, aligned to 16 bytes + */ +struct ftgmac100_rxdes { + unsigned int rxdes0; + unsigned int rxdes1; + unsigned int rxdes2; /* not used by HW */ + unsigned int rxdes3; /* RXBUF_BADR */ +} __attribute__ ((aligned(16))); + +#define FTGMAC100_RXDES0_VDBC(x) ((x) & 0x3fff) +#define FTGMAC100_RXDES0_EDORR (1 << 15) +#define FTGMAC100_RXDES0_MULTICAST (1 << 16) +#define FTGMAC100_RXDES0_BROADCAST (1 << 17) +#define FTGMAC100_RXDES0_RX_ERR (1 << 18) +#define FTGMAC100_RXDES0_CRC_ERR (1 << 19) +#define FTGMAC100_RXDES0_FTL (1 << 20) +#define FTGMAC100_RXDES0_RUNT (1 << 21) +#define FTGMAC100_RXDES0_RX_ODD_NB (1 << 22) +#define FTGMAC100_RXDES0_FIFO_FULL (1 << 23) +#define FTGMAC100_RXDES0_PAUSE_OPCODE (1 << 24) +#define FTGMAC100_RXDES0_PAUSE_FRAME (1 << 25) +#define FTGMAC100_RXDES0_LRS (1 << 28) +#define FTGMAC100_RXDES0_FRS (1 << 29) +#define FTGMAC100_RXDES0_RXPKT_RDY (1 << 31) + +#define FTGMAC100_RXDES1_VLANTAG_CI 0xffff +#define FTGMAC100_RXDES1_PROT_MASK (0x3 << 20) +#define FTGMAC100_RXDES1_PROT_NONIP (0x0 << 20) +#define FTGMAC100_RXDES1_PROT_IP (0x1 << 20) +#define FTGMAC100_RXDES1_PROT_TCPIP (0x2 << 20) +#define FTGMAC100_RXDES1_PROT_UDPIP (0x3 << 20) +#define FTGMAC100_RXDES1_LLC (1 << 22) +#define FTGMAC100_RXDES1_DF (1 << 23) +#define FTGMAC100_RXDES1_VLANTAG_AVAIL (1 << 24) +#define FTGMAC100_RXDES1_TCP_CHKSUM_ERR (1 << 25) +#define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26) +#define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27) + +#endif /* __FTGMAC100_H */ diff --git a/qemu/roms/u-boot/drivers/net/ftmac100.c b/qemu/roms/u-boot/drivers/net/ftmac100.c new file mode 100644 index 000000000..3e148db5c --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ftmac100.c @@ -0,0 +1,265 @@ +/* + * Faraday FTMAC100 Ethernet + * + * (C) Copyright 2009 Faraday Technology + * Po-Yu Chuang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include "ftmac100.h" + +#define ETH_ZLEN 60 + +struct ftmac100_data { + struct ftmac100_txdes txdes[1]; + struct ftmac100_rxdes rxdes[PKTBUFSRX]; + int rx_index; +}; + +/* + * Reset MAC + */ +static void ftmac100_reset (struct eth_device *dev) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + + debug ("%s()\n", __func__); + + writel (FTMAC100_MACCR_SW_RST, &ftmac100->maccr); + + while (readl (&ftmac100->maccr) & FTMAC100_MACCR_SW_RST) + ; +} + +/* + * Set MAC address + */ +static void ftmac100_set_mac (struct eth_device *dev, const unsigned char *mac) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + unsigned int maddr = mac[0] << 8 | mac[1]; + unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; + + debug ("%s(%x %x)\n", __func__, maddr, laddr); + + writel (maddr, &ftmac100->mac_madr); + writel (laddr, &ftmac100->mac_ladr); +} + +static void ftmac100_set_mac_from_env (struct eth_device *dev) +{ + eth_getenv_enetaddr ("ethaddr", dev->enetaddr); + + ftmac100_set_mac (dev, dev->enetaddr); +} + +/* + * disable transmitter, receiver + */ +static void ftmac100_halt (struct eth_device *dev) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + + debug ("%s()\n", __func__); + + writel (0, &ftmac100->maccr); +} + +static int ftmac100_init (struct eth_device *dev, bd_t *bd) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + struct ftmac100_data *priv = dev->priv; + struct ftmac100_txdes *txdes = priv->txdes; + struct ftmac100_rxdes *rxdes = priv->rxdes; + unsigned int maccr; + int i; + + debug ("%s()\n", __func__); + + ftmac100_reset (dev); + + /* set the ethernet address */ + + ftmac100_set_mac_from_env (dev); + + /* disable all interrupts */ + + writel (0, &ftmac100->imr); + + /* initialize descriptors */ + + priv->rx_index = 0; + + txdes[0].txdes1 = FTMAC100_TXDES1_EDOTR; + rxdes[PKTBUFSRX - 1].rxdes1 = FTMAC100_RXDES1_EDORR; + + for (i = 0; i < PKTBUFSRX; i++) { + /* RXBUF_BADR */ + rxdes[i].rxdes2 = (unsigned int)NetRxPackets[i]; + rxdes[i].rxdes1 |= FTMAC100_RXDES1_RXBUF_SIZE (PKTSIZE_ALIGN); + rxdes[i].rxdes0 = FTMAC100_RXDES0_RXDMA_OWN; + } + + /* transmit ring */ + + writel ((unsigned int)txdes, &ftmac100->txr_badr); + + /* receive ring */ + + writel ((unsigned int)rxdes, &ftmac100->rxr_badr); + + /* poll receive descriptor automatically */ + + writel (FTMAC100_APTC_RXPOLL_CNT (1), &ftmac100->aptc); + + /* enable transmitter, receiver */ + + maccr = FTMAC100_MACCR_XMT_EN | + FTMAC100_MACCR_RCV_EN | + FTMAC100_MACCR_XDMA_EN | + FTMAC100_MACCR_RDMA_EN | + FTMAC100_MACCR_CRC_APD | + FTMAC100_MACCR_ENRX_IN_HALFTX | + FTMAC100_MACCR_RX_RUNT | + FTMAC100_MACCR_RX_BROADPKT; + + writel (maccr, &ftmac100->maccr); + + return 0; +} + +/* + * Get a data block via Ethernet + */ +static int ftmac100_recv (struct eth_device *dev) +{ + struct ftmac100_data *priv = dev->priv; + struct ftmac100_rxdes *curr_des; + unsigned short rxlen; + + curr_des = &priv->rxdes[priv->rx_index]; + + if (curr_des->rxdes0 & FTMAC100_RXDES0_RXDMA_OWN) + return -1; + + if (curr_des->rxdes0 & (FTMAC100_RXDES0_RX_ERR | + FTMAC100_RXDES0_CRC_ERR | + FTMAC100_RXDES0_FTL | + FTMAC100_RXDES0_RUNT | + FTMAC100_RXDES0_RX_ODD_NB)) { + return -1; + } + + rxlen = FTMAC100_RXDES0_RFL (curr_des->rxdes0); + + debug ("%s(): RX buffer %d, %x received\n", + __func__, priv->rx_index, rxlen); + + /* pass the packet up to the protocol layers. */ + + NetReceive ((void *)curr_des->rxdes2, rxlen); + + /* release buffer to DMA */ + + curr_des->rxdes0 |= FTMAC100_RXDES0_RXDMA_OWN; + + priv->rx_index = (priv->rx_index + 1) % PKTBUFSRX; + + return 0; +} + +/* + * Send a data block via Ethernet + */ +static int ftmac100_send(struct eth_device *dev, void *packet, int length) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + struct ftmac100_data *priv = dev->priv; + struct ftmac100_txdes *curr_des = priv->txdes; + ulong start; + + if (curr_des->txdes0 & FTMAC100_TXDES0_TXDMA_OWN) { + debug ("%s(): no TX descriptor available\n", __func__); + return -1; + } + + debug ("%s(%x, %x)\n", __func__, (int)packet, length); + + length = (length < ETH_ZLEN) ? ETH_ZLEN : length; + + /* initiate a transmit sequence */ + + curr_des->txdes2 = (unsigned int)packet; /* TXBUF_BADR */ + + curr_des->txdes1 &= FTMAC100_TXDES1_EDOTR; + curr_des->txdes1 |= FTMAC100_TXDES1_FTS | + FTMAC100_TXDES1_LTS | + FTMAC100_TXDES1_TXBUF_SIZE (length); + + curr_des->txdes0 = FTMAC100_TXDES0_TXDMA_OWN; + + /* start transmit */ + + writel (1, &ftmac100->txpd); + + /* wait for transfer to succeed */ + + start = get_timer(0); + while (curr_des->txdes0 & FTMAC100_TXDES0_TXDMA_OWN) { + if (get_timer(start) >= 5) { + debug ("%s(): timed out\n", __func__); + return -1; + } + } + + debug ("%s(): packet sent\n", __func__); + + return 0; +} + +int ftmac100_initialize (bd_t *bd) +{ + struct eth_device *dev; + struct ftmac100_data *priv; + + dev = malloc (sizeof *dev); + if (!dev) { + printf ("%s(): failed to allocate dev\n", __func__); + goto out; + } + + /* Transmit and receive descriptors should align to 16 bytes */ + + priv = memalign (16, sizeof (struct ftmac100_data)); + if (!priv) { + printf ("%s(): failed to allocate priv\n", __func__); + goto free_dev; + } + + memset (dev, 0, sizeof (*dev)); + memset (priv, 0, sizeof (*priv)); + + sprintf (dev->name, "FTMAC100"); + dev->iobase = CONFIG_FTMAC100_BASE; + dev->init = ftmac100_init; + dev->halt = ftmac100_halt; + dev->send = ftmac100_send; + dev->recv = ftmac100_recv; + dev->priv = priv; + + eth_register (dev); + + return 1; + +free_dev: + free (dev); +out: + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/ftmac100.h b/qemu/roms/u-boot/drivers/net/ftmac100.h new file mode 100644 index 000000000..b674d0291 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ftmac100.h @@ -0,0 +1,142 @@ +/* + * Faraday FTMAC100 Ethernet + * + * (C) Copyright 2009 Faraday Technology + * Po-Yu Chuang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __FTMAC100_H +#define __FTMAC100_H + +struct ftmac100 { + unsigned int isr; /* 0x00 */ + unsigned int imr; /* 0x04 */ + unsigned int mac_madr; /* 0x08 */ + unsigned int mac_ladr; /* 0x0c */ + unsigned int maht0; /* 0x10 */ + unsigned int maht1; /* 0x14 */ + unsigned int txpd; /* 0x18 */ + unsigned int rxpd; /* 0x1c */ + unsigned int txr_badr; /* 0x20 */ + unsigned int rxr_badr; /* 0x24 */ + unsigned int itc; /* 0x28 */ + unsigned int aptc; /* 0x2c */ + unsigned int dblac; /* 0x30 */ + unsigned int pad1[3]; /* 0x34 - 0x3c */ + unsigned int pad2[16]; /* 0x40 - 0x7c */ + unsigned int pad3[2]; /* 0x80 - 0x84 */ + unsigned int maccr; /* 0x88 */ + unsigned int macsr; /* 0x8c */ + unsigned int phycr; /* 0x90 */ + unsigned int phywdata; /* 0x94 */ + unsigned int fcr; /* 0x98 */ + unsigned int bpr; /* 0x9c */ + unsigned int pad4[8]; /* 0xa0 - 0xbc */ + unsigned int pad5; /* 0xc0 */ + unsigned int ts; /* 0xc4 */ + unsigned int dmafifos; /* 0xc8 */ + unsigned int tm; /* 0xcc */ + unsigned int pad6; /* 0xd0 */ + unsigned int tx_mcol_scol; /* 0xd4 */ + unsigned int rpf_aep; /* 0xd8 */ + unsigned int xm_pg; /* 0xdc */ + unsigned int runt_tlcc; /* 0xe0 */ + unsigned int crcer_ftl; /* 0xe4 */ + unsigned int rlc_rcc; /* 0xe8 */ + unsigned int broc; /* 0xec */ + unsigned int mulca; /* 0xf0 */ + unsigned int rp; /* 0xf4 */ + unsigned int xp; /* 0xf8 */ +}; + +/* + * Interrupt status register & interrupt mask register + */ +#define FTMAC100_INT_RPKT_FINISH (1 << 0) +#define FTMAC100_INT_NORXBUF (1 << 1) +#define FTMAC100_INT_XPKT_FINISH (1 << 2) +#define FTMAC100_INT_NOTXBUF (1 << 3) +#define FTMAC100_INT_XPKT_OK (1 << 4) +#define FTMAC100_INT_XPKT_LOST (1 << 5) +#define FTMAC100_INT_RPKT_SAV (1 << 6) +#define FTMAC100_INT_RPKT_LOST (1 << 7) +#define FTMAC100_INT_AHB_ERR (1 << 8) +#define FTMAC100_INT_PHYSTS_CHG (1 << 9) + +/* + * Automatic polling timer control register + */ +#define FTMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0) +#define FTMAC100_APTC_RXPOLL_TIME_SEL (1 << 4) +#define FTMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8) +#define FTMAC100_APTC_TXPOLL_TIME_SEL (1 << 12) + +/* + * MAC control register + */ +#define FTMAC100_MACCR_XDMA_EN (1 << 0) +#define FTMAC100_MACCR_RDMA_EN (1 << 1) +#define FTMAC100_MACCR_SW_RST (1 << 2) +#define FTMAC100_MACCR_LOOP_EN (1 << 3) +#define FTMAC100_MACCR_CRC_DIS (1 << 4) +#define FTMAC100_MACCR_XMT_EN (1 << 5) +#define FTMAC100_MACCR_ENRX_IN_HALFTX (1 << 6) +#define FTMAC100_MACCR_RCV_EN (1 << 8) +#define FTMAC100_MACCR_HT_MULTI_EN (1 << 9) +#define FTMAC100_MACCR_RX_RUNT (1 << 10) +#define FTMAC100_MACCR_RX_FTL (1 << 11) +#define FTMAC100_MACCR_RCV_ALL (1 << 12) +#define FTMAC100_MACCR_CRC_APD (1 << 14) +#define FTMAC100_MACCR_FULLDUP (1 << 15) +#define FTMAC100_MACCR_RX_MULTIPKT (1 << 16) +#define FTMAC100_MACCR_RX_BROADPKT (1 << 17) + +/* + * Transmit descriptor, aligned to 16 bytes + */ +struct ftmac100_txdes { + unsigned int txdes0; + unsigned int txdes1; + unsigned int txdes2; /* TXBUF_BADR */ + unsigned int txdes3; /* not used by HW */ +} __attribute__ ((aligned(16))); + +#define FTMAC100_TXDES0_TXPKT_LATECOL (1 << 0) +#define FTMAC100_TXDES0_TXPKT_EXSCOL (1 << 1) +#define FTMAC100_TXDES0_TXDMA_OWN (1 << 31) + +#define FTMAC100_TXDES1_TXBUF_SIZE(x) ((x) & 0x7ff) +#define FTMAC100_TXDES1_LTS (1 << 27) +#define FTMAC100_TXDES1_FTS (1 << 28) +#define FTMAC100_TXDES1_TX2FIC (1 << 29) +#define FTMAC100_TXDES1_TXIC (1 << 30) +#define FTMAC100_TXDES1_EDOTR (1 << 31) + +/* + * Receive descriptor, aligned to 16 bytes + */ +struct ftmac100_rxdes { + unsigned int rxdes0; + unsigned int rxdes1; + unsigned int rxdes2; /* RXBUF_BADR */ + unsigned int rxdes3; /* not used by HW */ +} __attribute__ ((aligned(16))); + +#define FTMAC100_RXDES0_RFL(des) ((des) & 0x7ff) +#define FTMAC100_RXDES0_MULTICAST (1 << 16) +#define FTMAC100_RXDES0_BROADCAST (1 << 17) +#define FTMAC100_RXDES0_RX_ERR (1 << 18) +#define FTMAC100_RXDES0_CRC_ERR (1 << 19) +#define FTMAC100_RXDES0_FTL (1 << 20) +#define FTMAC100_RXDES0_RUNT (1 << 21) +#define FTMAC100_RXDES0_RX_ODD_NB (1 << 22) +#define FTMAC100_RXDES0_LRS (1 << 28) +#define FTMAC100_RXDES0_FRS (1 << 29) +#define FTMAC100_RXDES0_RXDMA_OWN (1 << 31) + +#define FTMAC100_RXDES1_RXBUF_SIZE(x) ((x) & 0x7ff) +#define FTMAC100_RXDES1_EDORR (1 << 31) + +#endif /* __FTMAC100_H */ diff --git a/qemu/roms/u-boot/drivers/net/ftmac110.c b/qemu/roms/u-boot/drivers/net/ftmac110.c new file mode 100644 index 000000000..98c4f0962 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ftmac110.c @@ -0,0 +1,480 @@ +/* + * Faraday 10/100Mbps Ethernet Controller + * + * (C) Copyright 2013 Faraday Technology + * Dante Su + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +#include +#endif + +#include "ftmac110.h" + +#define CFG_RXDES_NUM 8 +#define CFG_TXDES_NUM 2 +#define CFG_XBUF_SIZE 1536 + +#define CFG_MDIORD_TIMEOUT (CONFIG_SYS_HZ >> 1) /* 500 ms */ +#define CFG_MDIOWR_TIMEOUT (CONFIG_SYS_HZ >> 1) /* 500 ms */ +#define CFG_LINKUP_TIMEOUT (CONFIG_SYS_HZ << 2) /* 4 sec */ + +/* + * FTMAC110 DMA design issue + * + * Its DMA engine has a weird restriction that its Rx DMA engine + * accepts only 16-bits aligned address, 32-bits aligned is not + * acceptable. However this restriction does not apply to Tx DMA. + * + * Conclusion: + * (1) Tx DMA Buffer Address: + * 1 bytes aligned: Invalid + * 2 bytes aligned: O.K + * 4 bytes aligned: O.K (-> u-boot ZeroCopy is possible) + * (2) Rx DMA Buffer Address: + * 1 bytes aligned: Invalid + * 2 bytes aligned: O.K + * 4 bytes aligned: Invalid + */ + +struct ftmac110_chip { + void __iomem *regs; + uint32_t imr; + uint32_t maccr; + uint32_t lnkup; + uint32_t phy_addr; + + struct ftmac110_desc *rxd; + ulong rxd_dma; + uint32_t rxd_idx; + + struct ftmac110_desc *txd; + ulong txd_dma; + uint32_t txd_idx; +}; + +static int ftmac110_reset(struct eth_device *dev); + +static uint16_t mdio_read(struct eth_device *dev, + uint8_t phyaddr, uint8_t phyreg) +{ + struct ftmac110_chip *chip = dev->priv; + struct ftmac110_regs *regs = chip->regs; + uint32_t tmp, ts; + uint16_t ret = 0xffff; + + tmp = PHYCR_READ + | (phyaddr << PHYCR_ADDR_SHIFT) + | (phyreg << PHYCR_REG_SHIFT); + + writel(tmp, ®s->phycr); + + for (ts = get_timer(0); get_timer(ts) < CFG_MDIORD_TIMEOUT; ) { + tmp = readl(®s->phycr); + if (tmp & PHYCR_READ) + continue; + break; + } + + if (tmp & PHYCR_READ) + printf("ftmac110: mdio read timeout\n"); + else + ret = (uint16_t)(tmp & 0xffff); + + return ret; +} + +static void mdio_write(struct eth_device *dev, + uint8_t phyaddr, uint8_t phyreg, uint16_t phydata) +{ + struct ftmac110_chip *chip = dev->priv; + struct ftmac110_regs *regs = chip->regs; + uint32_t tmp, ts; + + tmp = PHYCR_WRITE + | (phyaddr << PHYCR_ADDR_SHIFT) + | (phyreg << PHYCR_REG_SHIFT); + + writel(phydata, ®s->phydr); + writel(tmp, ®s->phycr); + + for (ts = get_timer(0); get_timer(ts) < CFG_MDIOWR_TIMEOUT; ) { + if (readl(®s->phycr) & PHYCR_WRITE) + continue; + break; + } + + if (readl(®s->phycr) & PHYCR_WRITE) + printf("ftmac110: mdio write timeout\n"); +} + +static uint32_t ftmac110_phyqry(struct eth_device *dev) +{ + ulong ts; + uint32_t maccr; + uint16_t pa, tmp, bmsr, bmcr; + struct ftmac110_chip *chip = dev->priv; + + /* Default = 100Mbps Full */ + maccr = MACCR_100M | MACCR_FD; + + /* 1. find the phy device */ + for (pa = 0; pa < 32; ++pa) { + tmp = mdio_read(dev, pa, MII_PHYSID1); + if (tmp == 0xFFFF || tmp == 0x0000) + continue; + chip->phy_addr = pa; + break; + } + if (pa >= 32) { + puts("ftmac110: phy device not found!\n"); + goto exit; + } + + /* 2. wait until link-up & auto-negotiation complete */ + chip->lnkup = 0; + bmcr = mdio_read(dev, chip->phy_addr, MII_BMCR); + ts = get_timer(0); + do { + bmsr = mdio_read(dev, chip->phy_addr, MII_BMSR); + chip->lnkup = (bmsr & BMSR_LSTATUS) ? 1 : 0; + if (!chip->lnkup) + continue; + if (!(bmcr & BMCR_ANENABLE) || (bmsr & BMSR_ANEGCOMPLETE)) + break; + } while (get_timer(ts) < CFG_LINKUP_TIMEOUT); + if (!chip->lnkup) { + puts("ftmac110: link down\n"); + goto exit; + } + if (!(bmcr & BMCR_ANENABLE)) + puts("ftmac110: auto negotiation disabled\n"); + else if (!(bmsr & BMSR_ANEGCOMPLETE)) + puts("ftmac110: auto negotiation timeout\n"); + + /* 3. derive MACCR */ + if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_ANEGCOMPLETE)) { + tmp = mdio_read(dev, chip->phy_addr, MII_ADVERTISE); + tmp &= mdio_read(dev, chip->phy_addr, MII_LPA); + if (tmp & LPA_100FULL) /* 100Mbps full-duplex */ + maccr = MACCR_100M | MACCR_FD; + else if (tmp & LPA_100HALF) /* 100Mbps half-duplex */ + maccr = MACCR_100M; + else if (tmp & LPA_10FULL) /* 10Mbps full-duplex */ + maccr = MACCR_FD; + else if (tmp & LPA_10HALF) /* 10Mbps half-duplex */ + maccr = 0; + } else { + if (bmcr & BMCR_SPEED100) + maccr = MACCR_100M; + else + maccr = 0; + if (bmcr & BMCR_FULLDPLX) + maccr |= MACCR_FD; + } + +exit: + printf("ftmac110: %d Mbps, %s\n", + (maccr & MACCR_100M) ? 100 : 10, + (maccr & MACCR_FD) ? "Full" : "half"); + return maccr; +} + +static int ftmac110_reset(struct eth_device *dev) +{ + uint8_t *a; + uint32_t i, maccr; + struct ftmac110_chip *chip = dev->priv; + struct ftmac110_regs *regs = chip->regs; + + /* 1. MAC reset */ + writel(MACCR_RESET, ®s->maccr); + for (i = get_timer(0); get_timer(i) < 1000; ) { + if (readl(®s->maccr) & MACCR_RESET) + continue; + break; + } + if (readl(®s->maccr) & MACCR_RESET) { + printf("ftmac110: reset failed\n"); + return -ENXIO; + } + + /* 1-1. Init tx ring */ + for (i = 0; i < CFG_TXDES_NUM; ++i) { + /* owned by SW */ + chip->txd[i].ctrl &= cpu_to_le64(FTMAC110_TXD_CLRMASK); + } + chip->txd_idx = 0; + + /* 1-2. Init rx ring */ + for (i = 0; i < CFG_RXDES_NUM; ++i) { + /* owned by HW */ + chip->rxd[i].ctrl &= cpu_to_le64(FTMAC110_RXD_CLRMASK); + chip->rxd[i].ctrl |= cpu_to_le64(FTMAC110_RXD_OWNER); + } + chip->rxd_idx = 0; + + /* 2. PHY status query */ + maccr = ftmac110_phyqry(dev); + + /* 3. Fix up the MACCR value */ + chip->maccr = maccr | MACCR_CRCAPD | MACCR_RXALL | MACCR_RXRUNT + | MACCR_RXEN | MACCR_TXEN | MACCR_RXDMAEN | MACCR_TXDMAEN; + + /* 4. MAC address setup */ + a = dev->enetaddr; + writel(a[1] | (a[0] << 8), ®s->mac[0]); + writel(a[5] | (a[4] << 8) | (a[3] << 16) + | (a[2] << 24), ®s->mac[1]); + + /* 5. MAC registers setup */ + writel(chip->rxd_dma, ®s->rxba); + writel(chip->txd_dma, ®s->txba); + /* interrupt at each tx/rx */ + writel(ITC_DEFAULT, ®s->itc); + /* no tx pool, rx poll = 1 normal cycle */ + writel(APTC_DEFAULT, ®s->aptc); + /* rx threshold = [6/8 fifo, 2/8 fifo] */ + writel(DBLAC_DEFAULT, ®s->dblac); + /* disable & clear all interrupt status */ + chip->imr = 0; + writel(ISR_ALL, ®s->isr); + writel(chip->imr, ®s->imr); + /* enable mac */ + writel(chip->maccr, ®s->maccr); + + return 0; +} + +static int ftmac110_probe(struct eth_device *dev, bd_t *bis) +{ + debug("ftmac110: probe\n"); + + if (ftmac110_reset(dev)) + return -1; + + return 0; +} + +static void ftmac110_halt(struct eth_device *dev) +{ + struct ftmac110_chip *chip = dev->priv; + struct ftmac110_regs *regs = chip->regs; + + writel(0, ®s->imr); + writel(0, ®s->maccr); + + debug("ftmac110: halt\n"); +} + +static int ftmac110_send(struct eth_device *dev, void *pkt, int len) +{ + struct ftmac110_chip *chip = dev->priv; + struct ftmac110_regs *regs = chip->regs; + struct ftmac110_desc *txd; + uint64_t ctrl; + + if (!chip->lnkup) + return 0; + + if (len <= 0 || len > CFG_XBUF_SIZE) { + printf("ftmac110: bad tx pkt len(%d)\n", len); + return 0; + } + + len = max(60, len); + + txd = &chip->txd[chip->txd_idx]; + ctrl = le64_to_cpu(txd->ctrl); + if (ctrl & FTMAC110_TXD_OWNER) { + /* kick-off Tx DMA */ + writel(0xffffffff, ®s->txpd); + printf("ftmac110: out of txd\n"); + return 0; + } + + memcpy(txd->vbuf, (void *)pkt, len); + dma_map_single(txd->vbuf, len, DMA_TO_DEVICE); + + /* clear control bits */ + ctrl &= FTMAC110_TXD_CLRMASK; + /* set len, fts and lts */ + ctrl |= FTMAC110_TXD_LEN(len) | FTMAC110_TXD_FTS | FTMAC110_TXD_LTS; + /* set owner bit */ + ctrl |= FTMAC110_TXD_OWNER; + /* write back to descriptor */ + txd->ctrl = cpu_to_le64(ctrl); + + /* kick-off Tx DMA */ + writel(0xffffffff, ®s->txpd); + + chip->txd_idx = (chip->txd_idx + 1) % CFG_TXDES_NUM; + + return len; +} + +static int ftmac110_recv(struct eth_device *dev) +{ + struct ftmac110_chip *chip = dev->priv; + struct ftmac110_desc *rxd; + uint32_t len, rlen = 0; + uint64_t ctrl; + uint8_t *buf; + + if (!chip->lnkup) + return 0; + + do { + rxd = &chip->rxd[chip->rxd_idx]; + ctrl = le64_to_cpu(rxd->ctrl); + if (ctrl & FTMAC110_RXD_OWNER) + break; + + len = (uint32_t)FTMAC110_RXD_LEN(ctrl); + buf = rxd->vbuf; + + if (ctrl & FTMAC110_RXD_ERRMASK) { + printf("ftmac110: rx error\n"); + } else { + dma_map_single(buf, len, DMA_FROM_DEVICE); + NetReceive(buf, len); + rlen += len; + } + + /* owned by hardware */ + ctrl &= FTMAC110_RXD_CLRMASK; + ctrl |= FTMAC110_RXD_OWNER; + rxd->ctrl |= cpu_to_le64(ctrl); + + chip->rxd_idx = (chip->rxd_idx + 1) % CFG_RXDES_NUM; + } while (0); + + return rlen; +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + +static int ftmac110_mdio_read( + const char *devname, uint8_t addr, uint8_t reg, uint16_t *value) +{ + int ret = 0; + struct eth_device *dev; + + dev = eth_get_dev_by_name(devname); + if (dev == NULL) { + printf("%s: no such device\n", devname); + ret = -1; + } else { + *value = mdio_read(dev, addr, reg); + } + + return ret; +} + +static int ftmac110_mdio_write( + const char *devname, uint8_t addr, uint8_t reg, uint16_t value) +{ + int ret = 0; + struct eth_device *dev; + + dev = eth_get_dev_by_name(devname); + if (dev == NULL) { + printf("%s: no such device\n", devname); + ret = -1; + } else { + mdio_write(dev, addr, reg, value); + } + + return ret; +} + +#endif /* #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) */ + +int ftmac110_initialize(bd_t *bis) +{ + int i, card_nr = 0; + struct eth_device *dev; + struct ftmac110_chip *chip; + + dev = malloc(sizeof(*dev) + sizeof(*chip)); + if (dev == NULL) { + panic("ftmac110: out of memory 1\n"); + return -1; + } + chip = (struct ftmac110_chip *)(dev + 1); + memset(dev, 0, sizeof(*dev) + sizeof(*chip)); + + sprintf(dev->name, "FTMAC110#%d", card_nr); + + dev->iobase = CONFIG_FTMAC110_BASE; + chip->regs = (void __iomem *)dev->iobase; + dev->priv = chip; + dev->init = ftmac110_probe; + dev->halt = ftmac110_halt; + dev->send = ftmac110_send; + dev->recv = ftmac110_recv; + + if (!eth_getenv_enetaddr_by_index("eth", card_nr, dev->enetaddr)) + eth_random_addr(dev->enetaddr); + + /* allocate tx descriptors (it must be 16 bytes aligned) */ + chip->txd = dma_alloc_coherent( + sizeof(struct ftmac110_desc) * CFG_TXDES_NUM, &chip->txd_dma); + if (!chip->txd) + panic("ftmac110: out of memory 3\n"); + memset(chip->txd, 0, + sizeof(struct ftmac110_desc) * CFG_TXDES_NUM); + for (i = 0; i < CFG_TXDES_NUM; ++i) { + void *va = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE); + + if (!va) + panic("ftmac110: out of memory 4\n"); + chip->txd[i].vbuf = va; + chip->txd[i].pbuf = cpu_to_le32(virt_to_phys(va)); + chip->txd[i].ctrl = 0; /* owned by SW */ + } + chip->txd[i - 1].ctrl |= cpu_to_le64(FTMAC110_TXD_END); + chip->txd_idx = 0; + + /* allocate rx descriptors (it must be 16 bytes aligned) */ + chip->rxd = dma_alloc_coherent( + sizeof(struct ftmac110_desc) * CFG_RXDES_NUM, &chip->rxd_dma); + if (!chip->rxd) + panic("ftmac110: out of memory 4\n"); + memset((void *)chip->rxd, 0, + sizeof(struct ftmac110_desc) * CFG_RXDES_NUM); + for (i = 0; i < CFG_RXDES_NUM; ++i) { + void *va = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE + 2); + + if (!va) + panic("ftmac110: out of memory 5\n"); + /* it needs to be exactly 2 bytes aligned */ + va = ((uint8_t *)va + 2); + chip->rxd[i].vbuf = va; + chip->rxd[i].pbuf = cpu_to_le32(virt_to_phys(va)); + chip->rxd[i].ctrl = cpu_to_le64(FTMAC110_RXD_OWNER + | FTMAC110_RXD_BUFSZ(CFG_XBUF_SIZE)); + } + chip->rxd[i - 1].ctrl |= cpu_to_le64(FTMAC110_RXD_END); + chip->rxd_idx = 0; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, ftmac110_mdio_read, ftmac110_mdio_write); +#endif + + card_nr++; + + return card_nr; +} diff --git a/qemu/roms/u-boot/drivers/net/ftmac110.h b/qemu/roms/u-boot/drivers/net/ftmac110.h new file mode 100644 index 000000000..2772ae7b7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ftmac110.h @@ -0,0 +1,176 @@ +/* + * Faraday 10/100Mbps Ethernet Controller + * + * (C) Copyright 2013 Faraday Technology + * Dante Su + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _FTMAC110_H +#define _FTMAC110_H + +struct ftmac110_regs { + uint32_t isr; /* 0x00: Interrups Status Register */ + uint32_t imr; /* 0x04: Interrupt Mask Register */ + uint32_t mac[2]; /* 0x08: MAC Address */ + uint32_t mht[2]; /* 0x10: Multicast Hash Table Register */ + uint32_t txpd; /* 0x18: Tx Poll Demand Register */ + uint32_t rxpd; /* 0x1c: Rx Poll Demand Register */ + uint32_t txba; /* 0x20: Tx Ring Base Address Register */ + uint32_t rxba; /* 0x24: Rx Ring Base Address Register */ + uint32_t itc; /* 0x28: Interrupt Timer Control Register */ + uint32_t aptc; /* 0x2C: Automatic Polling Timer Control Register */ + uint32_t dblac; /* 0x30: DMA Burst Length&Arbitration Control */ + uint32_t revr; /* 0x34: Revision Register */ + uint32_t fear; /* 0x38: Feature Register */ + uint32_t rsvd[19]; + uint32_t maccr; /* 0x88: MAC Control Register */ + uint32_t macsr; /* 0x8C: MAC Status Register */ + uint32_t phycr; /* 0x90: PHY Control Register */ + uint32_t phydr; /* 0x94: PHY Data Register */ + uint32_t fcr; /* 0x98: Flow Control Register */ + uint32_t bpr; /* 0x9C: Back Pressure Register */ +}; + +/* + * Interrupt status/mask register(ISR/IMR) bits + */ +#define ISR_ALL 0x3ff +#define ISR_PHYSTCHG (1 << 9) /* phy status change */ +#define ISR_AHBERR (1 << 8) /* bus error */ +#define ISR_RXLOST (1 << 7) /* rx lost */ +#define ISR_RXFIFO (1 << 6) /* rx to fifo */ +#define ISR_TXLOST (1 << 5) /* tx lost */ +#define ISR_TXOK (1 << 4) /* tx to ethernet */ +#define ISR_NOTXBUF (1 << 3) /* out of tx buffer */ +#define ISR_TXFIFO (1 << 2) /* tx to fifo */ +#define ISR_NORXBUF (1 << 1) /* out of rx buffer */ +#define ISR_RXOK (1 << 0) /* rx to buffer */ + +/* + * MACCR control bits + */ +#define MACCR_100M (1 << 18) /* 100Mbps mode */ +#define MACCR_RXBCST (1 << 17) /* rx broadcast packet */ +#define MACCR_RXMCST (1 << 16) /* rx multicast packet */ +#define MACCR_FD (1 << 15) /* full duplex */ +#define MACCR_CRCAPD (1 << 14) /* tx crc append */ +#define MACCR_RXALL (1 << 12) /* rx all packets */ +#define MACCR_RXFTL (1 << 11) /* rx packet even it's > 1518 byte */ +#define MACCR_RXRUNT (1 << 10) /* rx packet even it's < 64 byte */ +#define MACCR_RXMCSTHT (1 << 9) /* rx multicast hash table */ +#define MACCR_RXEN (1 << 8) /* rx enable */ +#define MACCR_RXINHDTX (1 << 6) /* rx in half duplex tx */ +#define MACCR_TXEN (1 << 5) /* tx enable */ +#define MACCR_CRCDIS (1 << 4) /* tx packet even it's crc error */ +#define MACCR_LOOPBACK (1 << 3) /* loop-back */ +#define MACCR_RESET (1 << 2) /* reset */ +#define MACCR_RXDMAEN (1 << 1) /* rx dma enable */ +#define MACCR_TXDMAEN (1 << 0) /* tx dma enable */ + +/* + * PHYCR control bits + */ +#define PHYCR_READ (1 << 26) +#define PHYCR_WRITE (1 << 27) +#define PHYCR_REG_SHIFT 21 +#define PHYCR_ADDR_SHIFT 16 + +/* + * ITC control bits + */ + +/* Tx Cycle Length */ +#define ITC_TX_CYCLONG (1 << 15) /* 100Mbps=81.92us; 10Mbps=819.2us */ +#define ITC_TX_CYCNORM (0 << 15) /* 100Mbps=5.12us; 10Mbps=51.2us */ +/* Tx Threshold: Aggregate n interrupts as 1 interrupt */ +#define ITC_TX_THR(n) (((n) & 0x7) << 12) +/* Tx Interrupt Timeout = n * Tx Cycle */ +#define ITC_TX_ITMO(n) (((n) & 0xf) << 8) +/* Rx Cycle Length */ +#define ITC_RX_CYCLONG (1 << 7) /* 100Mbps=81.92us; 10Mbps=819.2us */ +#define ITC_RX_CYCNORM (0 << 7) /* 100Mbps=5.12us; 10Mbps=51.2us */ +/* Rx Threshold: Aggregate n interrupts as 1 interrupt */ +#define ITC_RX_THR(n) (((n) & 0x7) << 4) +/* Rx Interrupt Timeout = n * Rx Cycle */ +#define ITC_RX_ITMO(n) (((n) & 0xf) << 0) + +#define ITC_DEFAULT \ + (ITC_TX_THR(1) | ITC_TX_ITMO(0) | ITC_RX_THR(1) | ITC_RX_ITMO(0)) + +/* + * APTC contrl bits + */ + +/* Tx Cycle Length */ +#define APTC_TX_CYCLONG (1 << 12) /* 100Mbps=81.92us; 10Mbps=819.2us */ +#define APTC_TX_CYCNORM (0 << 12) /* 100Mbps=5.12us; 10Mbps=51.2us */ +/* Tx Poll Timeout = n * Tx Cycle, 0=No auto polling */ +#define APTC_TX_PTMO(n) (((n) & 0xf) << 8) +/* Rx Cycle Length */ +#define APTC_RX_CYCLONG (1 << 4) /* 100Mbps=81.92us; 10Mbps=819.2us */ +#define APTC_RX_CYCNORM (0 << 4) /* 100Mbps=5.12us; 10Mbps=51.2us */ +/* Rx Poll Timeout = n * Rx Cycle, 0=No auto polling */ +#define APTC_RX_PTMO(n) (((n) & 0xf) << 0) + +#define APTC_DEFAULT (APTC_TX_PTMO(0) | APTC_RX_PTMO(1)) + +/* + * DBLAC contrl bits + */ +#define DBLAC_BURST_MAX_ANY (0 << 14) /* un-limited */ +#define DBLAC_BURST_MAX_32X4 (2 << 14) /* max = 32 x 4 bytes */ +#define DBLAC_BURST_MAX_64X4 (3 << 14) /* max = 64 x 4 bytes */ +#define DBLAC_RXTHR_EN (1 << 9) /* enable rx threshold arbitration */ +#define DBLAC_RXTHR_HIGH(n) (((n) & 0x7) << 6) /* upper bound = n/8 fifo */ +#define DBLAC_RXTHR_LOW(n) (((n) & 0x7) << 3) /* lower bound = n/8 fifo */ +#define DBLAC_BURST_CAP16 (1 << 2) /* support burst 16 */ +#define DBLAC_BURST_CAP8 (1 << 1) /* support burst 8 */ +#define DBLAC_BURST_CAP4 (1 << 0) /* support burst 4 */ + +#define DBLAC_DEFAULT \ + (DBLAC_RXTHR_EN | DBLAC_RXTHR_HIGH(6) | DBLAC_RXTHR_LOW(2)) + +/* + * descriptor structure + */ +struct ftmac110_desc { + uint64_t ctrl; + uint32_t pbuf; + void *vbuf; +}; + +#define FTMAC110_RXD_END ((uint64_t)1 << 63) +#define FTMAC110_RXD_BUFSZ(x) (((uint64_t)(x) & 0x7ff) << 32) + +#define FTMAC110_RXD_OWNER ((uint64_t)1 << 31) /* owner: 1=HW, 0=SW */ +#define FTMAC110_RXD_FRS ((uint64_t)1 << 29) /* first pkt desc */ +#define FTMAC110_RXD_LRS ((uint64_t)1 << 28) /* last pkt desc */ +#define FTMAC110_RXD_ODDNB ((uint64_t)1 << 22) /* odd nibble */ +#define FTMAC110_RXD_RUNT ((uint64_t)1 << 21) /* runt pkt */ +#define FTMAC110_RXD_FTL ((uint64_t)1 << 20) /* frame too long */ +#define FTMAC110_RXD_CRC ((uint64_t)1 << 19) /* pkt crc error */ +#define FTMAC110_RXD_ERR ((uint64_t)1 << 18) /* bus error */ +#define FTMAC110_RXD_ERRMASK ((uint64_t)0x1f << 18) +#define FTMAC110_RXD_BCST ((uint64_t)1 << 17) /* Bcst pkt */ +#define FTMAC110_RXD_MCST ((uint64_t)1 << 16) /* Mcst pkt */ +#define FTMAC110_RXD_LEN(x) ((uint64_t)((x) & 0x7ff)) + +#define FTMAC110_RXD_CLRMASK \ + (FTMAC110_RXD_END | FTMAC110_RXD_BUFSZ(0x7ff)) + +#define FTMAC110_TXD_END ((uint64_t)1 << 63) /* end of ring */ +#define FTMAC110_TXD_TXIC ((uint64_t)1 << 62) /* tx done interrupt */ +#define FTMAC110_TXD_TX2FIC ((uint64_t)1 << 61) /* tx fifo interrupt */ +#define FTMAC110_TXD_FTS ((uint64_t)1 << 60) /* first pkt desc */ +#define FTMAC110_TXD_LTS ((uint64_t)1 << 59) /* last pkt desc */ +#define FTMAC110_TXD_LEN(x) ((uint64_t)((x) & 0x7ff) << 32) + +#define FTMAC110_TXD_OWNER ((uint64_t)1 << 31) /* owner: 1=HW, 0=SW */ +#define FTMAC110_TXD_COL ((uint64_t)3) /* collision */ + +#define FTMAC110_TXD_CLRMASK \ + (FTMAC110_TXD_END) + +#endif /* FTMAC110_H */ diff --git a/qemu/roms/u-boot/drivers/net/greth.c b/qemu/roms/u-boot/drivers/net/greth.c new file mode 100644 index 000000000..c817af4da --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/greth.c @@ -0,0 +1,670 @@ +/* Gaisler.com GRETH 10/100/1000 Ethernet MAC driver + * + * Driver use polling mode (no Interrupt) + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "greth.h" + +/* Default to 3s timeout on autonegotiation */ +#ifndef GRETH_PHY_TIMEOUT_MS +#define GRETH_PHY_TIMEOUT_MS 3000 +#endif + +/* Default to PHY adrress 0 not not specified */ +#ifdef CONFIG_SYS_GRLIB_GRETH_PHYADDR +#define GRETH_PHY_ADR_DEFAULT CONFIG_SYS_GRLIB_GRETH_PHYADDR +#else +#define GRETH_PHY_ADR_DEFAULT 0 +#endif + +/* ByPass Cache when reading regs */ +#define GRETH_REGLOAD(addr) SPARC_NOCACHE_READ(addr) +/* Write-through cache ==> no bypassing needed on writes */ +#define GRETH_REGSAVE(addr,data) (*(volatile unsigned int *)(addr) = (data)) +#define GRETH_REGORIN(addr,data) GRETH_REGSAVE(addr,GRETH_REGLOAD(addr)|data) +#define GRETH_REGANDIN(addr,data) GRETH_REGSAVE(addr,GRETH_REGLOAD(addr)&data) + +#define GRETH_RXBD_CNT 4 +#define GRETH_TXBD_CNT 1 + +#define GRETH_RXBUF_SIZE 1540 +#define GRETH_BUF_ALIGN 4 +#define GRETH_RXBUF_EFF_SIZE \ + ( (GRETH_RXBUF_SIZE&~(GRETH_BUF_ALIGN-1))+GRETH_BUF_ALIGN ) + +typedef struct { + greth_regs *regs; + int irq; + struct eth_device *dev; + + /* Hardware info */ + unsigned char phyaddr; + int gbit_mac; + + /* Current operating Mode */ + int gb; /* GigaBit */ + int fd; /* Full Duplex */ + int sp; /* 10/100Mbps speed (1=100,0=10) */ + int auto_neg; /* Auto negotiate done */ + + unsigned char hwaddr[6]; /* MAC Address */ + + /* Descriptors */ + greth_bd *rxbd_base, *rxbd_max; + greth_bd *txbd_base, *txbd_max; + + greth_bd *rxbd_curr; + + /* rx buffers in rx descriptors */ + void *rxbuf_base; /* (GRETH_RXBUF_SIZE+ALIGNBYTES) * GRETH_RXBD_CNT */ + + /* unused for gbit_mac, temp buffer for sending packets with unligned + * start. + * Pointer to packet allocated with malloc. + */ + void *txbuf; + + struct { + /* rx status */ + unsigned int rx_packets, + rx_crc_errors, rx_frame_errors, rx_length_errors, rx_errors; + + /* tx stats */ + unsigned int tx_packets, + tx_latecol_errors, + tx_underrun_errors, tx_limit_errors, tx_errors; + } stats; +} greth_priv; + +/* Read MII register 'addr' from core 'regs' */ +static int read_mii(int phyaddr, int regaddr, volatile greth_regs * regs) +{ + while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { + } + + GRETH_REGSAVE(®s->mdio, ((phyaddr & 0x1F) << 11) | ((regaddr & 0x1F) << 6) | 2); + + while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { + } + + if (!(GRETH_REGLOAD(®s->mdio) & GRETH_MII_NVALID)) { + return (GRETH_REGLOAD(®s->mdio) >> 16) & 0xFFFF; + } else { + return -1; + } +} + +static void write_mii(int phyaddr, int regaddr, int data, volatile greth_regs * regs) +{ + while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { + } + + GRETH_REGSAVE(®s->mdio, + ((data & 0xFFFF) << 16) | ((phyaddr & 0x1F) << 11) | + ((regaddr & 0x1F) << 6) | 1); + + while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { + } + +} + +/* init/start hardware and allocate descriptor buffers for rx side + * + */ +int greth_init(struct eth_device *dev, bd_t * bis) +{ + int i; + + greth_priv *greth = dev->priv; + greth_regs *regs = greth->regs; + + debug("greth_init\n"); + + /* Reset core */ + GRETH_REGSAVE(®s->control, (GRETH_RESET | (greth->gb << 8) | + (greth->sp << 7) | (greth->fd << 4))); + + /* Wait for Reset to complete */ + while ( GRETH_REGLOAD(®s->control) & GRETH_RESET) ; + + GRETH_REGSAVE(®s->control, + ((greth->gb << 8) | (greth->sp << 7) | (greth->fd << 4))); + + if (!greth->rxbd_base) { + + /* allocate descriptors */ + greth->rxbd_base = (greth_bd *) + memalign(0x1000, GRETH_RXBD_CNT * sizeof(greth_bd)); + greth->txbd_base = (greth_bd *) + memalign(0x1000, GRETH_TXBD_CNT * sizeof(greth_bd)); + + /* allocate buffers to all descriptors */ + greth->rxbuf_base = + malloc(GRETH_RXBUF_EFF_SIZE * GRETH_RXBD_CNT); + } + + /* initate rx decriptors */ + for (i = 0; i < GRETH_RXBD_CNT; i++) { + greth->rxbd_base[i].addr = (unsigned int) + greth->rxbuf_base + (GRETH_RXBUF_EFF_SIZE * i); + /* enable desciptor & set wrap bit if last descriptor */ + if (i >= (GRETH_RXBD_CNT - 1)) { + greth->rxbd_base[i].stat = GRETH_BD_EN | GRETH_BD_WR; + } else { + greth->rxbd_base[i].stat = GRETH_BD_EN; + } + } + + /* initiate indexes */ + greth->rxbd_curr = greth->rxbd_base; + greth->rxbd_max = greth->rxbd_base + (GRETH_RXBD_CNT - 1); + greth->txbd_max = greth->txbd_base + (GRETH_TXBD_CNT - 1); + /* + * greth->txbd_base->addr = 0; + * greth->txbd_base->stat = GRETH_BD_WR; + */ + + /* initate tx decriptors */ + for (i = 0; i < GRETH_TXBD_CNT; i++) { + greth->txbd_base[i].addr = 0; + /* enable desciptor & set wrap bit if last descriptor */ + if (i >= (GRETH_TXBD_CNT - 1)) { + greth->txbd_base[i].stat = GRETH_BD_WR; + } else { + greth->txbd_base[i].stat = 0; + } + } + + /**** SET HARDWARE REGS ****/ + + /* Set pointer to tx/rx descriptor areas */ + GRETH_REGSAVE(®s->rx_desc_p, (unsigned int)&greth->rxbd_base[0]); + GRETH_REGSAVE(®s->tx_desc_p, (unsigned int)&greth->txbd_base[0]); + + /* Enable Transmitter, GRETH will now scan descriptors for packets + * to transmitt */ + debug("greth_init: enabling receiver\n"); + GRETH_REGORIN(®s->control, GRETH_RXEN); + + return 0; +} + +/* Initiate PHY to a relevant speed + * return: + * - 0 = success + * - 1 = timeout/fail + */ +int greth_init_phy(greth_priv * dev, bd_t * bis) +{ + greth_regs *regs = dev->regs; + int tmp, tmp1, tmp2, i; + unsigned int start, timeout; + int phyaddr = GRETH_PHY_ADR_DEFAULT; + +#ifndef CONFIG_SYS_GRLIB_GRETH_PHYADDR + /* If BSP doesn't provide a hardcoded PHY address the driver will + * try to autodetect PHY address by stopping the search on the first + * PHY address which has REG0 implemented. + */ + for (i=0; i<32; i++) { + tmp = read_mii(i, 0, regs); + if ( (tmp != 0) && (tmp != 0xffff) ) { + phyaddr = i; + break; + } + } +#endif + + /* Save PHY Address */ + dev->phyaddr = phyaddr; + + debug("GRETH PHY ADDRESS: %d\n", phyaddr); + + /* X msecs to ticks */ + timeout = usec2ticks(GRETH_PHY_TIMEOUT_MS * 1000); + + /* Get system timer0 current value + * Total timeout is 5s + */ + start = get_timer(0); + + /* get phy control register default values */ + + while ((tmp = read_mii(phyaddr, 0, regs)) & 0x8000) { + if (get_timer(start) > timeout) { + debug("greth_init_phy: PHY read 1 failed\n"); + return 1; /* Fail */ + } + } + + /* reset PHY and wait for completion */ + write_mii(phyaddr, 0, 0x8000 | tmp, regs); + + while (((tmp = read_mii(phyaddr, 0, regs))) & 0x8000) { + if (get_timer(start) > timeout) { + debug("greth_init_phy: PHY read 2 failed\n"); + return 1; /* Fail */ + } + } + + /* Check if PHY is autoneg capable and then determine operating + * mode, otherwise force it to 10 Mbit halfduplex + */ + dev->gb = 0; + dev->fd = 0; + dev->sp = 0; + dev->auto_neg = 0; + if (!((tmp >> 12) & 1)) { + write_mii(phyaddr, 0, 0, regs); + } else { + /* wait for auto negotiation to complete and then check operating mode */ + dev->auto_neg = 1; + i = 0; + while (!(((tmp = read_mii(phyaddr, 1, regs)) >> 5) & 1)) { + if (get_timer(start) > timeout) { + printf("Auto negotiation timed out. " + "Selecting default config\n"); + tmp = read_mii(phyaddr, 0, regs); + dev->gb = ((tmp >> 6) & 1) + && !((tmp >> 13) & 1); + dev->sp = !((tmp >> 6) & 1) + && ((tmp >> 13) & 1); + dev->fd = (tmp >> 8) & 1; + goto auto_neg_done; + } + } + if ((tmp >> 8) & 1) { + tmp1 = read_mii(phyaddr, 9, regs); + tmp2 = read_mii(phyaddr, 10, regs); + if ((tmp1 & GRETH_MII_EXTADV_1000FD) && + (tmp2 & GRETH_MII_EXTPRT_1000FD)) { + dev->gb = 1; + dev->fd = 1; + } + if ((tmp1 & GRETH_MII_EXTADV_1000HD) && + (tmp2 & GRETH_MII_EXTPRT_1000HD)) { + dev->gb = 1; + dev->fd = 0; + } + } + if ((dev->gb == 0) || ((dev->gb == 1) && (dev->gbit_mac == 0))) { + tmp1 = read_mii(phyaddr, 4, regs); + tmp2 = read_mii(phyaddr, 5, regs); + if ((tmp1 & GRETH_MII_100TXFD) && + (tmp2 & GRETH_MII_100TXFD)) { + dev->sp = 1; + dev->fd = 1; + } + if ((tmp1 & GRETH_MII_100TXHD) && + (tmp2 & GRETH_MII_100TXHD)) { + dev->sp = 1; + dev->fd = 0; + } + if ((tmp1 & GRETH_MII_10FD) && (tmp2 & GRETH_MII_10FD)) { + dev->fd = 1; + } + if ((dev->gb == 1) && (dev->gbit_mac == 0)) { + dev->gb = 0; + dev->fd = 0; + write_mii(phyaddr, 0, dev->sp << 13, regs); + } + } + + } + auto_neg_done: + debug("%s GRETH Ethermac at [0x%x] irq %d. Running \ + %d Mbps %s duplex\n", dev->gbit_mac ? "10/100/1000" : "10/100", (unsigned int)(regs), (unsigned int)(dev->irq), dev->gb ? 1000 : (dev->sp ? 100 : 10), dev->fd ? "full" : "half"); + /* Read out PHY info if extended registers are available */ + if (tmp & 1) { + tmp1 = read_mii(phyaddr, 2, regs); + tmp2 = read_mii(phyaddr, 3, regs); + tmp1 = (tmp1 << 6) | ((tmp2 >> 10) & 0x3F); + tmp = tmp2 & 0xF; + + tmp2 = (tmp2 >> 4) & 0x3F; + debug("PHY: Vendor %x Device %x Revision %d\n", tmp1, + tmp2, tmp); + } else { + printf("PHY info not available\n"); + } + + /* set speed and duplex bits in control register */ + GRETH_REGORIN(®s->control, + (dev->gb << 8) | (dev->sp << 7) | (dev->fd << 4)); + + return 0; +} + +void greth_halt(struct eth_device *dev) +{ + greth_priv *greth; + greth_regs *regs; + int i; + + debug("greth_halt\n"); + + if (!dev || !dev->priv) + return; + + greth = dev->priv; + regs = greth->regs; + + if (!regs) + return; + + /* disable receiver/transmitter by clearing the enable bits */ + GRETH_REGANDIN(®s->control, ~(GRETH_RXEN | GRETH_TXEN)); + + /* reset rx/tx descriptors */ + if (greth->rxbd_base) { + for (i = 0; i < GRETH_RXBD_CNT; i++) { + greth->rxbd_base[i].stat = + (i >= (GRETH_RXBD_CNT - 1)) ? GRETH_BD_WR : 0; + } + } + + if (greth->txbd_base) { + for (i = 0; i < GRETH_TXBD_CNT; i++) { + greth->txbd_base[i].stat = + (i >= (GRETH_TXBD_CNT - 1)) ? GRETH_BD_WR : 0; + } + } +} + +int greth_send(struct eth_device *dev, void *eth_data, int data_length) +{ + greth_priv *greth = dev->priv; + greth_regs *regs = greth->regs; + greth_bd *txbd; + void *txbuf; + unsigned int status; + + debug("greth_send\n"); + + /* send data, wait for data to be sent, then return */ + if (((unsigned int)eth_data & (GRETH_BUF_ALIGN - 1)) + && !greth->gbit_mac) { + /* data not aligned as needed by GRETH 10/100, solve this by allocating 4 byte aligned buffer + * and copy data to before giving it to GRETH. + */ + if (!greth->txbuf) { + greth->txbuf = malloc(GRETH_RXBUF_SIZE); + } + + txbuf = greth->txbuf; + + /* copy data info buffer */ + memcpy((char *)txbuf, (char *)eth_data, data_length); + + /* keep buffer to next time */ + } else { + txbuf = (void *)eth_data; + } + /* get descriptor to use, only 1 supported... hehe easy */ + txbd = greth->txbd_base; + + /* setup descriptor to wrap around to it self */ + txbd->addr = (unsigned int)txbuf; + txbd->stat = GRETH_BD_EN | GRETH_BD_WR | data_length; + + /* Remind Core which descriptor to use when sending */ + GRETH_REGSAVE(®s->tx_desc_p, (unsigned int)txbd); + + /* initate send by enabling transmitter */ + GRETH_REGORIN(®s->control, GRETH_TXEN); + + /* Wait for data to be sent */ + while ((status = GRETH_REGLOAD(&txbd->stat)) & GRETH_BD_EN) { + ; + } + + /* was the packet transmitted succesfully? */ + if (status & GRETH_TXBD_ERR_AL) { + greth->stats.tx_limit_errors++; + } + + if (status & GRETH_TXBD_ERR_UE) { + greth->stats.tx_underrun_errors++; + } + + if (status & GRETH_TXBD_ERR_LC) { + greth->stats.tx_latecol_errors++; + } + + if (status & + (GRETH_TXBD_ERR_LC | GRETH_TXBD_ERR_UE | GRETH_TXBD_ERR_AL)) { + /* any error */ + greth->stats.tx_errors++; + return -1; + } + + /* bump tx packet counter */ + greth->stats.tx_packets++; + + /* return succefully */ + return 0; +} + +int greth_recv(struct eth_device *dev) +{ + greth_priv *greth = dev->priv; + greth_regs *regs = greth->regs; + greth_bd *rxbd; + unsigned int status, len = 0, bad; + char *d; + int enable = 0; + int i; + + /* Receive One packet only, but clear as many error packets as there are + * available. + */ + { + /* current receive descriptor */ + rxbd = greth->rxbd_curr; + + /* get status of next received packet */ + status = GRETH_REGLOAD(&rxbd->stat); + + bad = 0; + + /* stop if no more packets received */ + if (status & GRETH_BD_EN) { + goto done; + } + + debug("greth_recv: packet 0x%x, 0x%x, len: %d\n", + (unsigned int)rxbd, status, status & GRETH_BD_LEN); + + /* Check status for errors. + */ + if (status & GRETH_RXBD_ERR_FT) { + greth->stats.rx_length_errors++; + bad = 1; + } + if (status & (GRETH_RXBD_ERR_AE | GRETH_RXBD_ERR_OE)) { + greth->stats.rx_frame_errors++; + bad = 1; + } + if (status & GRETH_RXBD_ERR_CRC) { + greth->stats.rx_crc_errors++; + bad = 1; + } + if (bad) { + greth->stats.rx_errors++; + printf + ("greth_recv: Bad packet (%d, %d, %d, 0x%08x, %d)\n", + greth->stats.rx_length_errors, + greth->stats.rx_frame_errors, + greth->stats.rx_crc_errors, status, + greth->stats.rx_packets); + /* print all rx descriptors */ + for (i = 0; i < GRETH_RXBD_CNT; i++) { + printf("[%d]: Stat=0x%lx, Addr=0x%lx\n", i, + GRETH_REGLOAD(&greth->rxbd_base[i].stat), + GRETH_REGLOAD(&greth->rxbd_base[i].addr)); + } + } else { + /* Process the incoming packet. */ + len = status & GRETH_BD_LEN; + d = (char *)rxbd->addr; + + debug + ("greth_recv: new packet, length: %d. data: %x %x %x %x %x %x %x %x\n", + len, d[0], d[1], d[2], d[3], d[4], d[5], d[6], + d[7]); + + /* flush all data cache to make sure we're not reading old packet data */ + sparc_dcache_flush_all(); + + /* pass packet on to network subsystem */ + NetReceive((void *)d, len); + + /* bump stats counters */ + greth->stats.rx_packets++; + + /* bad is now 0 ==> will stop loop */ + } + + /* reenable descriptor to receive more packet with this descriptor, wrap around if needed */ + rxbd->stat = + GRETH_BD_EN | + (((unsigned int)greth->rxbd_curr >= + (unsigned int)greth->rxbd_max) ? GRETH_BD_WR : 0); + enable = 1; + + /* increase index */ + greth->rxbd_curr = + ((unsigned int)greth->rxbd_curr >= + (unsigned int)greth->rxbd_max) ? greth-> + rxbd_base : (greth->rxbd_curr + 1); + + } + + if (enable) { + GRETH_REGORIN(®s->control, GRETH_RXEN); + } + done: + /* return positive length of packet or 0 if non received */ + return len; +} + +void greth_set_hwaddr(greth_priv * greth, unsigned char *mac) +{ + /* save new MAC address */ + greth->dev->enetaddr[0] = greth->hwaddr[0] = mac[0]; + greth->dev->enetaddr[1] = greth->hwaddr[1] = mac[1]; + greth->dev->enetaddr[2] = greth->hwaddr[2] = mac[2]; + greth->dev->enetaddr[3] = greth->hwaddr[3] = mac[3]; + greth->dev->enetaddr[4] = greth->hwaddr[4] = mac[4]; + greth->dev->enetaddr[5] = greth->hwaddr[5] = mac[5]; + greth->regs->esa_msb = (mac[0] << 8) | mac[1]; + greth->regs->esa_lsb = + (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5]; + + debug("GRETH: New MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + +int greth_initialize(bd_t * bis) +{ + greth_priv *greth; + ambapp_apbdev apbdev; + struct eth_device *dev; + int i; + char *addr_str, *end; + unsigned char addr[6]; + + debug("Scanning for GRETH\n"); + + /* Find Device & IRQ via AMBA Plug&Play information */ + if (ambapp_apb_first(VENDOR_GAISLER, GAISLER_ETHMAC, &apbdev) != 1) { + return -1; /* GRETH not found */ + } + + greth = (greth_priv *) malloc(sizeof(greth_priv)); + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + memset(dev, 0, sizeof(struct eth_device)); + memset(greth, 0, sizeof(greth_priv)); + + greth->regs = (greth_regs *) apbdev.address; + greth->irq = apbdev.irq; + debug("Found GRETH at %p, irq %d\n", greth->regs, greth->irq); + dev->priv = (void *)greth; + dev->iobase = (unsigned int)greth->regs; + dev->init = greth_init; + dev->halt = greth_halt; + dev->send = greth_send; + dev->recv = greth_recv; + greth->dev = dev; + + /* Reset Core */ + GRETH_REGSAVE(&greth->regs->control, GRETH_RESET); + + /* Wait for core to finish reset cycle */ + while (GRETH_REGLOAD(&greth->regs->control) & GRETH_RESET) ; + + /* Get the phy address which assumed to have been set + correctly with the reset value in hardware */ + greth->phyaddr = (GRETH_REGLOAD(&greth->regs->mdio) >> 11) & 0x1F; + + /* Check if mac is gigabit capable */ + greth->gbit_mac = (GRETH_REGLOAD(&greth->regs->control) >> 27) & 1; + + /* Make descriptor string */ + if (greth->gbit_mac) { + sprintf(dev->name, "GRETH_10/100/GB"); + } else { + sprintf(dev->name, "GRETH_10/100"); + } + + /* initiate PHY, select speed/duplex depending on connected PHY */ + if (greth_init_phy(greth, bis)) { + /* Failed to init PHY (timedout) */ + debug("GRETH[%p]: Failed to init PHY\n", greth->regs); + return -1; + } + + /* Register Device to EtherNet subsystem */ + eth_register(dev); + + /* Get MAC address */ + if ((addr_str = getenv("ethaddr")) != NULL) { + for (i = 0; i < 6; i++) { + addr[i] = + addr_str ? simple_strtoul(addr_str, &end, 16) : 0; + if (addr_str) { + addr_str = (*end) ? end + 1 : end; + } + } + } else { + /* HW Address not found in environment, Set default HW address */ + addr[0] = GRETH_HWADDR_0; /* MSB */ + addr[1] = GRETH_HWADDR_1; + addr[2] = GRETH_HWADDR_2; + addr[3] = GRETH_HWADDR_3; + addr[4] = GRETH_HWADDR_4; + addr[5] = GRETH_HWADDR_5; /* LSB */ + } + + /* set and remember MAC address */ + greth_set_hwaddr(greth, addr); + + debug("GRETH[%p]: Initialized successfully\n", greth->regs); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/greth.h b/qemu/roms/u-boot/drivers/net/greth.h new file mode 100644 index 000000000..5299b2861 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/greth.h @@ -0,0 +1,81 @@ +/* Gaisler.com GRETH 10/100/1000 Ethernet MAC driver + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define GRETH_FD 0x10 +#define GRETH_RESET 0x40 +#define GRETH_MII_BUSY 0x8 +#define GRETH_MII_NVALID 0x10 + +/* MII registers */ +#define GRETH_MII_EXTADV_1000FD 0x00000200 +#define GRETH_MII_EXTADV_1000HD 0x00000100 +#define GRETH_MII_EXTPRT_1000FD 0x00000800 +#define GRETH_MII_EXTPRT_1000HD 0x00000400 + +#define GRETH_MII_100T4 0x00000200 +#define GRETH_MII_100TXFD 0x00000100 +#define GRETH_MII_100TXHD 0x00000080 +#define GRETH_MII_10FD 0x00000040 +#define GRETH_MII_10HD 0x00000020 + +#define GRETH_BD_EN 0x800 +#define GRETH_BD_WR 0x1000 +#define GRETH_BD_IE 0x2000 +#define GRETH_BD_LEN 0x7FF + +#define GRETH_TXEN 0x1 +#define GRETH_INT_TX 0x8 +#define GRETH_TXI 0x4 +#define GRETH_TXBD_STATUS 0x0001C000 +#define GRETH_TXBD_MORE 0x20000 +#define GRETH_TXBD_IPCS 0x40000 +#define GRETH_TXBD_TCPCS 0x80000 +#define GRETH_TXBD_UDPCS 0x100000 +#define GRETH_TXBD_ERR_LC 0x10000 +#define GRETH_TXBD_ERR_UE 0x4000 +#define GRETH_TXBD_ERR_AL 0x8000 +#define GRETH_TXBD_NUM 128 +#define GRETH_TXBD_NUM_MASK (GRETH_TXBD_NUM-1) +#define GRETH_TX_BUF_SIZE 2048 + +#define GRETH_INT_RX 0x4 +#define GRETH_RXEN 0x2 +#define GRETH_RXI 0x8 +#define GRETH_RXBD_STATUS 0xFFFFC000 +#define GRETH_RXBD_ERR_AE 0x4000 +#define GRETH_RXBD_ERR_FT 0x8000 +#define GRETH_RXBD_ERR_CRC 0x10000 +#define GRETH_RXBD_ERR_OE 0x20000 +#define GRETH_RXBD_ERR_LE 0x40000 +#define GRETH_RXBD_IP_DEC 0x80000 +#define GRETH_RXBD_IP_CSERR 0x100000 +#define GRETH_RXBD_UDP_DEC 0x200000 +#define GRETH_RXBD_UDP_CSERR 0x400000 +#define GRETH_RXBD_TCP_DEC 0x800000 +#define GRETH_RXBD_TCP_CSERR 0x1000000 + +#define GRETH_RXBD_NUM 128 +#define GRETH_RXBD_NUM_MASK (GRETH_RXBD_NUM-1) +#define GRETH_RX_BUF_SIZE 2048 + +/* Ethernet configuration registers */ +typedef struct _greth_regs { + volatile unsigned int control; + volatile unsigned int status; + volatile unsigned int esa_msb; + volatile unsigned int esa_lsb; + volatile unsigned int mdio; + volatile unsigned int tx_desc_p; + volatile unsigned int rx_desc_p; +} greth_regs; + +/* Ethernet buffer descriptor */ +typedef struct _greth_bd { + volatile unsigned int stat; + unsigned int addr; /* Buffer address not changed by HW */ +} greth_bd; diff --git a/qemu/roms/u-boot/drivers/net/keystone_net.c b/qemu/roms/u-boot/drivers/net/keystone_net.c new file mode 100644 index 000000000..f95c92807 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/keystone_net.c @@ -0,0 +1,716 @@ +/* + * Ethernet driver for TI K2HK EVM. + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include + +#include +#include +#include +#include +#include +#include + +unsigned int emac_dbg; + +unsigned int emac_open; +static unsigned int sys_has_mdio = 1; + +#ifdef KEYSTONE2_EMAC_GIG_ENABLE +#define emac_gigabit_enable(x) keystone2_eth_gigabit_enable(x) +#else +#define emac_gigabit_enable(x) /* no gigabit to enable */ +#endif + +#define RX_BUFF_NUMS 24 +#define RX_BUFF_LEN 1520 +#define MAX_SIZE_STREAM_BUFFER RX_BUFF_LEN + +static u8 rx_buffs[RX_BUFF_NUMS * RX_BUFF_LEN] __aligned(16); + +struct rx_buff_desc net_rx_buffs = { + .buff_ptr = rx_buffs, + .num_buffs = RX_BUFF_NUMS, + .buff_len = RX_BUFF_LEN, + .rx_flow = 22, +}; + +static void keystone2_eth_mdio_enable(void); + +static int gen_get_link_speed(int phy_addr); + +/* EMAC Addresses */ +static volatile struct emac_regs *adap_emac = + (struct emac_regs *)EMAC_EMACSL_BASE_ADDR; +static volatile struct mdio_regs *adap_mdio = + (struct mdio_regs *)EMAC_MDIO_BASE_ADDR; + +int keystone2_eth_read_mac_addr(struct eth_device *dev) +{ + struct eth_priv_t *eth_priv; + u32 maca = 0; + u32 macb = 0; + + eth_priv = (struct eth_priv_t *)dev->priv; + + /* Read the e-fuse mac address */ + if (eth_priv->slave_port == 1) { + maca = __raw_readl(MAC_ID_BASE_ADDR); + macb = __raw_readl(MAC_ID_BASE_ADDR + 4); + } + + dev->enetaddr[0] = (macb >> 8) & 0xff; + dev->enetaddr[1] = (macb >> 0) & 0xff; + dev->enetaddr[2] = (maca >> 24) & 0xff; + dev->enetaddr[3] = (maca >> 16) & 0xff; + dev->enetaddr[4] = (maca >> 8) & 0xff; + dev->enetaddr[5] = (maca >> 0) & 0xff; + + return 0; +} + +static void keystone2_eth_mdio_enable(void) +{ + u_int32_t clkdiv; + + clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; + + writel((clkdiv & 0xffff) | + MDIO_CONTROL_ENABLE | + MDIO_CONTROL_FAULT | + MDIO_CONTROL_FAULT_ENABLE, + &adap_mdio->control); + + while (readl(&adap_mdio->control) & MDIO_CONTROL_IDLE) + ; +} + +/* Read a PHY register via MDIO inteface. Returns 1 on success, 0 otherwise */ +int keystone2_eth_phy_read(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t *data) +{ + int tmp; + + while (readl(&adap_mdio->useraccess0) & MDIO_USERACCESS0_GO) + ; + + writel(MDIO_USERACCESS0_GO | + MDIO_USERACCESS0_WRITE_READ | + ((reg_num & 0x1f) << 21) | + ((phy_addr & 0x1f) << 16), + &adap_mdio->useraccess0); + + /* Wait for command to complete */ + while ((tmp = readl(&adap_mdio->useraccess0)) & MDIO_USERACCESS0_GO) + ; + + if (tmp & MDIO_USERACCESS0_ACK) { + *data = tmp & 0xffff; + return 0; + } + + *data = -1; + return -1; +} + +/* + * Write to a PHY register via MDIO inteface. + * Blocks until operation is complete. + */ +int keystone2_eth_phy_write(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t data) +{ + while (readl(&adap_mdio->useraccess0) & MDIO_USERACCESS0_GO) + ; + + writel(MDIO_USERACCESS0_GO | + MDIO_USERACCESS0_WRITE_WRITE | + ((reg_num & 0x1f) << 21) | + ((phy_addr & 0x1f) << 16) | + (data & 0xffff), + &adap_mdio->useraccess0); + + /* Wait for command to complete */ + while (readl(&adap_mdio->useraccess0) & MDIO_USERACCESS0_GO) + ; + + return 0; +} + +/* PHY functions for a generic PHY */ +static int gen_get_link_speed(int phy_addr) +{ + u_int16_t tmp; + + if ((!keystone2_eth_phy_read(phy_addr, MII_STATUS_REG, &tmp)) && + (tmp & 0x04)) { + return 0; + } + + return -1; +} + +static void __attribute__((unused)) + keystone2_eth_gigabit_enable(struct eth_device *dev) +{ + u_int16_t data; + struct eth_priv_t *eth_priv = (struct eth_priv_t *)dev->priv; + + if (sys_has_mdio) { + if (keystone2_eth_phy_read(eth_priv->phy_addr, 0, &data) || + !(data & (1 << 6))) /* speed selection MSB */ + return; + } + + /* + * Check if link detected is giga-bit + * If Gigabit mode detected, enable gigbit in MAC + */ + writel(readl(&(adap_emac[eth_priv->slave_port - 1].maccontrol)) | + EMAC_MACCONTROL_GIGFORCE | EMAC_MACCONTROL_GIGABIT_ENABLE, + &(adap_emac[eth_priv->slave_port - 1].maccontrol)) + ; +} + +int keystone_sgmii_link_status(int port) +{ + u32 status = 0; + + status = __raw_readl(SGMII_STATUS_REG(port)); + + return status & SGMII_REG_STATUS_LINK; +} + + +int keystone_get_link_status(struct eth_device *dev) +{ + struct eth_priv_t *eth_priv = (struct eth_priv_t *)dev->priv; + int sgmii_link; + int link_state = 0; +#if CONFIG_GET_LINK_STATUS_ATTEMPTS > 1 + int j; + + for (j = 0; (j < CONFIG_GET_LINK_STATUS_ATTEMPTS) && (link_state == 0); + j++) { +#endif + sgmii_link = + keystone_sgmii_link_status(eth_priv->slave_port - 1); + + if (sgmii_link) { + link_state = 1; + + if (eth_priv->sgmii_link_type == SGMII_LINK_MAC_PHY) + if (gen_get_link_speed(eth_priv->phy_addr)) + link_state = 0; + } +#if CONFIG_GET_LINK_STATUS_ATTEMPTS > 1 + } +#endif + return link_state; +} + +int keystone_sgmii_config(int port, int interface) +{ + unsigned int i, status, mask; + unsigned int mr_adv_ability, control; + + switch (interface) { + case SGMII_LINK_MAC_MAC_AUTONEG: + mr_adv_ability = (SGMII_REG_MR_ADV_ENABLE | + SGMII_REG_MR_ADV_LINK | + SGMII_REG_MR_ADV_FULL_DUPLEX | + SGMII_REG_MR_ADV_GIG_MODE); + control = (SGMII_REG_CONTROL_MASTER | + SGMII_REG_CONTROL_AUTONEG); + + break; + case SGMII_LINK_MAC_PHY: + case SGMII_LINK_MAC_PHY_FORCED: + mr_adv_ability = SGMII_REG_MR_ADV_ENABLE; + control = SGMII_REG_CONTROL_AUTONEG; + + break; + case SGMII_LINK_MAC_MAC_FORCED: + mr_adv_ability = (SGMII_REG_MR_ADV_ENABLE | + SGMII_REG_MR_ADV_LINK | + SGMII_REG_MR_ADV_FULL_DUPLEX | + SGMII_REG_MR_ADV_GIG_MODE); + control = SGMII_REG_CONTROL_MASTER; + + break; + case SGMII_LINK_MAC_FIBER: + mr_adv_ability = 0x20; + control = SGMII_REG_CONTROL_AUTONEG; + + break; + default: + mr_adv_ability = SGMII_REG_MR_ADV_ENABLE; + control = SGMII_REG_CONTROL_AUTONEG; + } + + __raw_writel(0, SGMII_CTL_REG(port)); + + /* + * Wait for the SerDes pll to lock, + * but don't trap if lock is never read + */ + for (i = 0; i < 1000; i++) { + udelay(2000); + status = __raw_readl(SGMII_STATUS_REG(port)); + if ((status & SGMII_REG_STATUS_LOCK) != 0) + break; + } + + __raw_writel(mr_adv_ability, SGMII_MRADV_REG(port)); + __raw_writel(control, SGMII_CTL_REG(port)); + + + mask = SGMII_REG_STATUS_LINK; + + if (control & SGMII_REG_CONTROL_AUTONEG) + mask |= SGMII_REG_STATUS_AUTONEG; + + for (i = 0; i < 1000; i++) { + status = __raw_readl(SGMII_STATUS_REG(port)); + if ((status & mask) == mask) + break; + } + + return 0; +} + +int mac_sl_reset(u32 port) +{ + u32 i, v; + + if (port >= DEVICE_N_GMACSL_PORTS) + return GMACSL_RET_INVALID_PORT; + + /* Set the soft reset bit */ + DEVICE_REG32_W(DEVICE_EMACSL_BASE(port) + + CPGMACSL_REG_RESET, CPGMAC_REG_RESET_VAL_RESET); + + /* Wait for the bit to clear */ + for (i = 0; i < DEVICE_EMACSL_RESET_POLL_COUNT; i++) { + v = DEVICE_REG32_R(DEVICE_EMACSL_BASE(port) + + CPGMACSL_REG_RESET); + if ((v & CPGMAC_REG_RESET_VAL_RESET_MASK) != + CPGMAC_REG_RESET_VAL_RESET) + return GMACSL_RET_OK; + } + + /* Timeout on the reset */ + return GMACSL_RET_WARN_RESET_INCOMPLETE; +} + +int mac_sl_config(u_int16_t port, struct mac_sl_cfg *cfg) +{ + u32 v, i; + int ret = GMACSL_RET_OK; + + if (port >= DEVICE_N_GMACSL_PORTS) + return GMACSL_RET_INVALID_PORT; + + if (cfg->max_rx_len > CPGMAC_REG_MAXLEN_LEN) { + cfg->max_rx_len = CPGMAC_REG_MAXLEN_LEN; + ret = GMACSL_RET_WARN_MAXLEN_TOO_BIG; + } + + /* Must wait if the device is undergoing reset */ + for (i = 0; i < DEVICE_EMACSL_RESET_POLL_COUNT; i++) { + v = DEVICE_REG32_R(DEVICE_EMACSL_BASE(port) + + CPGMACSL_REG_RESET); + if ((v & CPGMAC_REG_RESET_VAL_RESET_MASK) != + CPGMAC_REG_RESET_VAL_RESET) + break; + } + + if (i == DEVICE_EMACSL_RESET_POLL_COUNT) + return GMACSL_RET_CONFIG_FAIL_RESET_ACTIVE; + + DEVICE_REG32_W(DEVICE_EMACSL_BASE(port) + CPGMACSL_REG_MAXLEN, + cfg->max_rx_len); + + DEVICE_REG32_W(DEVICE_EMACSL_BASE(port) + CPGMACSL_REG_CTL, + cfg->ctl); + + return ret; +} + +int ethss_config(u32 ctl, u32 max_pkt_size) +{ + u32 i; + + /* Max length register */ + DEVICE_REG32_W(DEVICE_CPSW_BASE + CPSW_REG_MAXLEN, max_pkt_size); + + /* Control register */ + DEVICE_REG32_W(DEVICE_CPSW_BASE + CPSW_REG_CTL, ctl); + + /* All statistics enabled by default */ + DEVICE_REG32_W(DEVICE_CPSW_BASE + CPSW_REG_STAT_PORT_EN, + CPSW_REG_VAL_STAT_ENABLE_ALL); + + /* Reset and enable the ALE */ + DEVICE_REG32_W(DEVICE_CPSW_BASE + CPSW_REG_ALE_CONTROL, + CPSW_REG_VAL_ALE_CTL_RESET_AND_ENABLE | + CPSW_REG_VAL_ALE_CTL_BYPASS); + + /* All ports put into forward mode */ + for (i = 0; i < DEVICE_CPSW_NUM_PORTS; i++) + DEVICE_REG32_W(DEVICE_CPSW_BASE + CPSW_REG_ALE_PORTCTL(i), + CPSW_REG_VAL_PORTCTL_FORWARD_MODE); + + return 0; +} + +int ethss_start(void) +{ + int i; + struct mac_sl_cfg cfg; + + cfg.max_rx_len = MAX_SIZE_STREAM_BUFFER; + cfg.ctl = GMACSL_ENABLE | GMACSL_RX_ENABLE_EXT_CTL; + + for (i = 0; i < DEVICE_N_GMACSL_PORTS; i++) { + mac_sl_reset(i); + mac_sl_config(i, &cfg); + } + + return 0; +} + +int ethss_stop(void) +{ + int i; + + for (i = 0; i < DEVICE_N_GMACSL_PORTS; i++) + mac_sl_reset(i); + + return 0; +} + +int32_t cpmac_drv_send(u32 *buffer, int num_bytes, int slave_port_num) +{ + if (num_bytes < EMAC_MIN_ETHERNET_PKT_SIZE) + num_bytes = EMAC_MIN_ETHERNET_PKT_SIZE; + + return netcp_send(buffer, num_bytes, (slave_port_num) << 16); +} + +/* Eth device open */ +static int keystone2_eth_open(struct eth_device *dev, bd_t *bis) +{ + u_int32_t clkdiv; + int link; + struct eth_priv_t *eth_priv = (struct eth_priv_t *)dev->priv; + + debug("+ emac_open\n"); + + net_rx_buffs.rx_flow = eth_priv->rx_flow; + + sys_has_mdio = + (eth_priv->sgmii_link_type == SGMII_LINK_MAC_PHY) ? 1 : 0; + + psc_enable_module(KS2_LPSC_PA); + psc_enable_module(KS2_LPSC_CPGMAC); + + sgmii_serdes_setup_156p25mhz(); + + if (sys_has_mdio) + keystone2_eth_mdio_enable(); + + keystone_sgmii_config(eth_priv->slave_port - 1, + eth_priv->sgmii_link_type); + + udelay(10000); + + /* On chip switch configuration */ + ethss_config(target_get_switch_ctl(), SWITCH_MAX_PKT_SIZE); + + /* TODO: add error handling code */ + if (qm_init()) { + printf("ERROR: qm_init()\n"); + return -1; + } + if (netcp_init(&net_rx_buffs)) { + qm_close(); + printf("ERROR: netcp_init()\n"); + return -1; + } + + /* + * Streaming switch configuration. If not present this + * statement is defined to void in target.h. + * If present this is usually defined to a series of register writes + */ + hw_config_streaming_switch(); + + if (sys_has_mdio) { + /* Init MDIO & get link state */ + clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; + writel((clkdiv & 0xff) | MDIO_CONTROL_ENABLE | + MDIO_CONTROL_FAULT, &adap_mdio->control) + ; + + /* We need to wait for MDIO to start */ + udelay(1000); + + link = keystone_get_link_status(dev); + if (link == 0) { + netcp_close(); + qm_close(); + return -1; + } + } + + emac_gigabit_enable(dev); + + ethss_start(); + + debug("- emac_open\n"); + + emac_open = 1; + + return 0; +} + +/* Eth device close */ +void keystone2_eth_close(struct eth_device *dev) +{ + debug("+ emac_close\n"); + + if (!emac_open) + return; + + ethss_stop(); + + netcp_close(); + qm_close(); + + emac_open = 0; + + debug("- emac_close\n"); +} + +static int tx_send_loop; + +/* + * This function sends a single packet on the network and returns + * positive number (number of bytes transmitted) or negative for error + */ +static int keystone2_eth_send_packet(struct eth_device *dev, + void *packet, int length) +{ + int ret_status = -1; + struct eth_priv_t *eth_priv = (struct eth_priv_t *)dev->priv; + + tx_send_loop = 0; + + if (keystone_get_link_status(dev) == 0) + return -1; + + emac_gigabit_enable(dev); + + if (cpmac_drv_send((u32 *)packet, length, eth_priv->slave_port) != 0) + return ret_status; + + if (keystone_get_link_status(dev) == 0) + return -1; + + emac_gigabit_enable(dev); + + return length; +} + +/* + * This function handles receipt of a packet from the network + */ +static int keystone2_eth_rcv_packet(struct eth_device *dev) +{ + void *hd; + int pkt_size; + u32 *pkt; + + hd = netcp_recv(&pkt, &pkt_size); + if (hd == NULL) + return 0; + + NetReceive((uchar *)pkt, pkt_size); + + netcp_release_rxhd(hd); + + return pkt_size; +} + +/* + * This function initializes the EMAC hardware. + */ +int keystone2_emac_initialize(struct eth_priv_t *eth_priv) +{ + struct eth_device *dev; + + dev = malloc(sizeof(struct eth_device)); + if (dev == NULL) + return -1; + + memset(dev, 0, sizeof(struct eth_device)); + + strcpy(dev->name, eth_priv->int_name); + dev->priv = eth_priv; + + keystone2_eth_read_mac_addr(dev); + + dev->iobase = 0; + dev->init = keystone2_eth_open; + dev->halt = keystone2_eth_close; + dev->send = keystone2_eth_send_packet; + dev->recv = keystone2_eth_rcv_packet; + + eth_register(dev); + + return 0; +} + +void sgmii_serdes_setup_156p25mhz(void) +{ + unsigned int cnt; + + /* + * configure Serializer/Deserializer (SerDes) hardware. SerDes IP + * hardware vendor published only register addresses and their values + * to be used for configuring SerDes. So had to use hardcoded values + * below. + */ + clrsetbits_le32(0x0232a000, 0xffff0000, 0x00800000); + clrsetbits_le32(0x0232a014, 0x0000ffff, 0x00008282); + clrsetbits_le32(0x0232a060, 0x00ffffff, 0x00142438); + clrsetbits_le32(0x0232a064, 0x00ffff00, 0x00c3c700); + clrsetbits_le32(0x0232a078, 0x0000ff00, 0x0000c000); + + clrsetbits_le32(0x0232a204, 0xff0000ff, 0x38000080); + clrsetbits_le32(0x0232a208, 0x000000ff, 0x00000000); + clrsetbits_le32(0x0232a20c, 0xff000000, 0x02000000); + clrsetbits_le32(0x0232a210, 0xff000000, 0x1b000000); + clrsetbits_le32(0x0232a214, 0x0000ffff, 0x00006fb8); + clrsetbits_le32(0x0232a218, 0xffff00ff, 0x758000e4); + clrsetbits_le32(0x0232a2ac, 0x0000ff00, 0x00004400); + clrsetbits_le32(0x0232a22c, 0x00ffff00, 0x00200800); + clrsetbits_le32(0x0232a280, 0x00ff00ff, 0x00820082); + clrsetbits_le32(0x0232a284, 0xffffffff, 0x1d0f0385); + + clrsetbits_le32(0x0232a404, 0xff0000ff, 0x38000080); + clrsetbits_le32(0x0232a408, 0x000000ff, 0x00000000); + clrsetbits_le32(0x0232a40c, 0xff000000, 0x02000000); + clrsetbits_le32(0x0232a410, 0xff000000, 0x1b000000); + clrsetbits_le32(0x0232a414, 0x0000ffff, 0x00006fb8); + clrsetbits_le32(0x0232a418, 0xffff00ff, 0x758000e4); + clrsetbits_le32(0x0232a4ac, 0x0000ff00, 0x00004400); + clrsetbits_le32(0x0232a42c, 0x00ffff00, 0x00200800); + clrsetbits_le32(0x0232a480, 0x00ff00ff, 0x00820082); + clrsetbits_le32(0x0232a484, 0xffffffff, 0x1d0f0385); + + clrsetbits_le32(0x0232a604, 0xff0000ff, 0x38000080); + clrsetbits_le32(0x0232a608, 0x000000ff, 0x00000000); + clrsetbits_le32(0x0232a60c, 0xff000000, 0x02000000); + clrsetbits_le32(0x0232a610, 0xff000000, 0x1b000000); + clrsetbits_le32(0x0232a614, 0x0000ffff, 0x00006fb8); + clrsetbits_le32(0x0232a618, 0xffff00ff, 0x758000e4); + clrsetbits_le32(0x0232a6ac, 0x0000ff00, 0x00004400); + clrsetbits_le32(0x0232a62c, 0x00ffff00, 0x00200800); + clrsetbits_le32(0x0232a680, 0x00ff00ff, 0x00820082); + clrsetbits_le32(0x0232a684, 0xffffffff, 0x1d0f0385); + + clrsetbits_le32(0x0232a804, 0xff0000ff, 0x38000080); + clrsetbits_le32(0x0232a808, 0x000000ff, 0x00000000); + clrsetbits_le32(0x0232a80c, 0xff000000, 0x02000000); + clrsetbits_le32(0x0232a810, 0xff000000, 0x1b000000); + clrsetbits_le32(0x0232a814, 0x0000ffff, 0x00006fb8); + clrsetbits_le32(0x0232a818, 0xffff00ff, 0x758000e4); + clrsetbits_le32(0x0232a8ac, 0x0000ff00, 0x00004400); + clrsetbits_le32(0x0232a82c, 0x00ffff00, 0x00200800); + clrsetbits_le32(0x0232a880, 0x00ff00ff, 0x00820082); + clrsetbits_le32(0x0232a884, 0xffffffff, 0x1d0f0385); + + clrsetbits_le32(0x0232aa00, 0x0000ff00, 0x00000800); + clrsetbits_le32(0x0232aa08, 0xffff0000, 0x38a20000); + clrsetbits_le32(0x0232aa30, 0x00ffff00, 0x008a8a00); + clrsetbits_le32(0x0232aa84, 0x0000ff00, 0x00000600); + clrsetbits_le32(0x0232aa94, 0xff000000, 0x10000000); + clrsetbits_le32(0x0232aaa0, 0xff000000, 0x81000000); + clrsetbits_le32(0x0232aabc, 0xff000000, 0xff000000); + clrsetbits_le32(0x0232aac0, 0x000000ff, 0x0000008b); + clrsetbits_le32(0x0232ab08, 0xffff0000, 0x583f0000); + clrsetbits_le32(0x0232ab0c, 0x000000ff, 0x0000004e); + clrsetbits_le32(0x0232a000, 0x000000ff, 0x00000003); + clrsetbits_le32(0x0232aa00, 0x000000ff, 0x0000005f); + + clrsetbits_le32(0x0232aa48, 0x00ffff00, 0x00fd8c00); + clrsetbits_le32(0x0232aa54, 0x00ffffff, 0x002fec72); + clrsetbits_le32(0x0232aa58, 0xffffff00, 0x00f92100); + clrsetbits_le32(0x0232aa5c, 0xffffffff, 0x00040060); + clrsetbits_le32(0x0232aa60, 0xffffffff, 0x00008000); + clrsetbits_le32(0x0232aa64, 0xffffffff, 0x0c581220); + clrsetbits_le32(0x0232aa68, 0xffffffff, 0xe13b0602); + clrsetbits_le32(0x0232aa6c, 0xffffffff, 0xb8074cc1); + clrsetbits_le32(0x0232aa70, 0xffffffff, 0x3f02e989); + clrsetbits_le32(0x0232aa74, 0x000000ff, 0x00000001); + clrsetbits_le32(0x0232ab20, 0x00ff0000, 0x00370000); + clrsetbits_le32(0x0232ab1c, 0xff000000, 0x37000000); + clrsetbits_le32(0x0232ab20, 0x000000ff, 0x0000005d); + + /*Bring SerDes out of Reset if SerDes is Shutdown & is in Reset Mode*/ + clrbits_le32(0x0232a010, 1 << 28); + + /* Enable TX and RX via the LANExCTL_STS 0x0000 + x*4 */ + clrbits_le32(0x0232a228, 1 << 29); + writel(0xF800F8C0, 0x0232bfe0); + clrbits_le32(0x0232a428, 1 << 29); + writel(0xF800F8C0, 0x0232bfe4); + clrbits_le32(0x0232a628, 1 << 29); + writel(0xF800F8C0, 0x0232bfe8); + clrbits_le32(0x0232a828, 1 << 29); + writel(0xF800F8C0, 0x0232bfec); + + /*Enable pll via the pll_ctrl 0x0014*/ + writel(0xe0000000, 0x0232bff4) + ; + + /*Waiting for SGMII Serdes PLL lock.*/ + for (cnt = 10000; cnt > 0 && ((readl(0x02090114) & 0x10) == 0); cnt--) + ; + + for (cnt = 10000; cnt > 0 && ((readl(0x02090214) & 0x10) == 0); cnt--) + ; + + for (cnt = 10000; cnt > 0 && ((readl(0x02090414) & 0x10) == 0); cnt--) + ; + + for (cnt = 10000; cnt > 0 && ((readl(0x02090514) & 0x10) == 0); cnt--) + ; + + udelay(45000); +} + +void sgmii_serdes_shutdown(void) +{ + /* + * shutdown SerDes hardware. SerDes hardware vendor published only + * register addresses and their values. So had to use hardcoded + * values below. + */ + clrbits_le32(0x0232bfe0, 3 << 29 | 3 << 13); + setbits_le32(0x02320228, 1 << 29); + clrbits_le32(0x0232bfe4, 3 << 29 | 3 << 13); + setbits_le32(0x02320428, 1 << 29); + clrbits_le32(0x0232bfe8, 3 << 29 | 3 << 13); + setbits_le32(0x02320628, 1 << 29); + clrbits_le32(0x0232bfec, 3 << 29 | 3 << 13); + setbits_le32(0x02320828, 1 << 29); + + clrbits_le32(0x02320034, 3 << 29); + setbits_le32(0x02320010, 1 << 28); +} diff --git a/qemu/roms/u-boot/drivers/net/ks8695eth.c b/qemu/roms/u-boot/drivers/net/ks8695eth.c new file mode 100644 index 000000000..b4822e950 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ks8695eth.c @@ -0,0 +1,229 @@ +/* + * ks8695eth.c -- KS8695 ethernet driver + * + * (C) Copyright 2004-2005, Greg Ungerer + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/****************************************************************************/ + +#include +#include +#include +#include +#include + +/****************************************************************************/ + +/* + * Hardware register access to the KS8695 LAN ethernet port + * (well, it is the 4 port switch really). + */ +#define ks8695_read(a) *((volatile unsigned long *) (KS8695_IO_BASE + (a))) +#define ks8695_write(a,v) *((volatile unsigned long *) (KS8695_IO_BASE + (a))) = (v) + +/****************************************************************************/ + +/* + * Define the descriptor in-memory data structures. + */ +struct ks8695_txdesc { + uint32_t owner; + uint32_t ctrl; + uint32_t addr; + uint32_t next; +}; + +struct ks8695_rxdesc { + uint32_t status; + uint32_t ctrl; + uint32_t addr; + uint32_t next; +}; + +/****************************************************************************/ + +/* + * Allocate local data structures to use for receiving and sending + * packets. Just to keep it all nice and simple. + */ + +#define TXDESCS 4 +#define RXDESCS 4 +#define BUFSIZE 2048 + +volatile struct ks8695_txdesc ks8695_tx[TXDESCS] __attribute__((aligned(256))); +volatile struct ks8695_rxdesc ks8695_rx[RXDESCS] __attribute__((aligned(256))); +volatile uint8_t ks8695_bufs[BUFSIZE*(TXDESCS+RXDESCS)] __attribute__((aligned(2048)));; + +/****************************************************************************/ + +/* + * Ideally we want to use the MAC address stored in flash. + * But we do some sanity checks in case they are not present + * first. + */ +unsigned char eth_mac[] = { + 0x00, 0x13, 0xc6, 0x00, 0x00, 0x00 +}; + +void ks8695_getmac(void) +{ + unsigned char *fp; + int i; + + /* Check if flash MAC is valid */ + fp = (unsigned char *) 0x0201c000; + for (i = 0; (i < 6); i++) { + if ((fp[i] != 0) && (fp[i] != 0xff)) + break; + } + + /* If we found a valid looking MAC address then use it */ + if (i < 6) + memcpy(ð_mac[0], fp, 6); +} + +/****************************************************************************/ + +static int ks8695_eth_init(struct eth_device *dev, bd_t *bd) +{ + int i; + + debug ("%s(%d): eth_reset()\n", __FILE__, __LINE__); + + /* Reset the ethernet engines first */ + ks8695_write(KS8695_LAN_DMA_TX, 0x80000000); + ks8695_write(KS8695_LAN_DMA_RX, 0x80000000); + + ks8695_getmac(); + + /* Set MAC address */ + ks8695_write(KS8695_LAN_MAC_LOW, (eth_mac[5] | (eth_mac[4] << 8) | + (eth_mac[3] << 16) | (eth_mac[2] << 24))); + ks8695_write(KS8695_LAN_MAC_HIGH, (eth_mac[1] | (eth_mac[0] << 8))); + + /* Turn the 4 port switch on */ + i = ks8695_read(KS8695_SWITCH_CTRL0); + ks8695_write(KS8695_SWITCH_CTRL0, (i | 0x1)); + /* ks8695_write(KS8695_WAN_CONTROL, 0x3f000066); */ + + /* Initialize descriptor rings */ + for (i = 0; (i < TXDESCS); i++) { + ks8695_tx[i].owner = 0; + ks8695_tx[i].ctrl = 0; + ks8695_tx[i].addr = (uint32_t) &ks8695_bufs[i*BUFSIZE]; + ks8695_tx[i].next = (uint32_t) &ks8695_tx[i+1]; + } + ks8695_tx[TXDESCS-1].ctrl = 0x02000000; + ks8695_tx[TXDESCS-1].next = (uint32_t) &ks8695_tx[0]; + + for (i = 0; (i < RXDESCS); i++) { + ks8695_rx[i].status = 0x80000000; + ks8695_rx[i].ctrl = BUFSIZE - 4; + ks8695_rx[i].addr = (uint32_t) &ks8695_bufs[(i+TXDESCS)*BUFSIZE]; + ks8695_rx[i].next = (uint32_t) &ks8695_rx[i+1]; + } + ks8695_rx[RXDESCS-1].ctrl |= 0x00080000; + ks8695_rx[RXDESCS-1].next = (uint32_t) &ks8695_rx[0]; + + /* The KS8695 is pretty slow reseting the ethernets... */ + udelay(2000000); + + /* Enable the ethernet engine */ + ks8695_write(KS8695_LAN_TX_LIST, (uint32_t) &ks8695_tx[0]); + ks8695_write(KS8695_LAN_RX_LIST, (uint32_t) &ks8695_rx[0]); + ks8695_write(KS8695_LAN_DMA_TX, 0x3); + ks8695_write(KS8695_LAN_DMA_RX, 0x71); + ks8695_write(KS8695_LAN_DMA_RX_START, 0x1); + + printf("KS8695 ETHERNET: %pM\n", eth_mac); + return 0; +} + +/****************************************************************************/ + +static void ks8695_eth_halt(struct eth_device *dev) +{ + debug ("%s(%d): eth_halt()\n", __FILE__, __LINE__); + + /* Reset the ethernet engines */ + ks8695_write(KS8695_LAN_DMA_TX, 0x80000000); + ks8695_write(KS8695_LAN_DMA_RX, 0x80000000); +} + +/****************************************************************************/ + +static int ks8695_eth_recv(struct eth_device *dev) +{ + volatile struct ks8695_rxdesc *dp; + int i, len = 0; + + debug ("%s(%d): eth_rx()\n", __FILE__, __LINE__); + + for (i = 0; (i < RXDESCS); i++) { + dp= &ks8695_rx[i]; + if ((dp->status & 0x80000000) == 0) { + len = (dp->status & 0x7ff) - 4; + NetReceive((void *) dp->addr, len); + dp->status = 0x80000000; + ks8695_write(KS8695_LAN_DMA_RX_START, 0x1); + break; + } + } + + return len; +} + +/****************************************************************************/ + +static int ks8695_eth_send(struct eth_device *dev, void *packet, int len) +{ + volatile struct ks8695_txdesc *dp; + static int next = 0; + + debug ("%s(%d): eth_send(packet=%p,len=%d)\n", __FILE__, __LINE__, + packet, len); + + dp = &ks8695_tx[next]; + memcpy((void *) dp->addr, (void *) packet, len); + + if (len < 64) { + memset((void *) (dp->addr + len), 0, 64-len); + len = 64; + } + + dp->ctrl = len | 0xe0000000; + dp->owner = 0x80000000; + + ks8695_write(KS8695_LAN_DMA_TX, 0x3); + ks8695_write(KS8695_LAN_DMA_TX_START, 0x1); + + if (++next >= TXDESCS) + next = 0; + + return 0; +} + +/****************************************************************************/ + +int ks8695_eth_initialize(void) +{ + struct eth_device *dev; + + dev = malloc(sizeof(*dev)); + if (dev == NULL) + return -1; + memset(dev, 0, sizeof(*dev)); + + dev->iobase = KS8695_IO_BASE + KS8695_LAN_DMA_TX; + dev->init = ks8695_eth_init; + dev->halt = ks8695_eth_halt; + dev->send = ks8695_eth_send; + dev->recv = ks8695_eth_recv; + strcpy(dev->name, "ks8695eth"); + + eth_register(dev); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/ks8851_mll.c b/qemu/roms/u-boot/drivers/net/ks8851_mll.c new file mode 100644 index 000000000..05e5b14d2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ks8851_mll.c @@ -0,0 +1,633 @@ +/* + * Micrel KS8851_MLL 16bit Network driver + * Copyright (c) 2011 Roberto Cerati + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include "ks8851_mll.h" + +#define DRIVERNAME "ks8851_mll" + +#define MAX_RECV_FRAMES 32 +#define MAX_BUF_SIZE 2048 +#define TX_BUF_SIZE 2000 +#define RX_BUF_SIZE 2000 + +static const struct chip_id chip_ids[] = { + {CIDER_ID, "KSZ8851"}, + {0, NULL}, +}; + +/* + * union ks_tx_hdr - tx header data + * @txb: The header as bytes + * @txw: The header as 16bit, little-endian words + * + * A dual representation of the tx header data to allow + * access to individual bytes, and to allow 16bit accesses + * with 16bit alignment. + */ +union ks_tx_hdr { + u8 txb[4]; + __le16 txw[2]; +}; + +/* + * struct ks_net - KS8851 driver private data + * @net_device : The network device we're bound to + * @txh : temporaly buffer to save status/length. + * @frame_head_info : frame header information for multi-pkt rx. + * @statelock : Lock on this structure for tx list. + * @msg_enable : The message flags controlling driver output (see ethtool). + * @frame_cnt : number of frames received. + * @bus_width : i/o bus width. + * @irq : irq number assigned to this device. + * @rc_rxqcr : Cached copy of KS_RXQCR. + * @rc_txcr : Cached copy of KS_TXCR. + * @rc_ier : Cached copy of KS_IER. + * @sharedbus : Multipex(addr and data bus) mode indicator. + * @cmd_reg_cache : command register cached. + * @cmd_reg_cache_int : command register cached. Used in the irq handler. + * @promiscuous : promiscuous mode indicator. + * @all_mcast : mutlicast indicator. + * @mcast_lst_size : size of multicast list. + * @mcast_lst : multicast list. + * @mcast_bits : multicast enabed. + * @mac_addr : MAC address assigned to this device. + * @fid : frame id. + * @extra_byte : number of extra byte prepended rx pkt. + * @enabled : indicator this device works. + */ + +/* Receive multiplex framer header info */ +struct type_frame_head { + u16 sts; /* Frame status */ + u16 len; /* Byte count */ +} fr_h_i[MAX_RECV_FRAMES]; + +struct ks_net { + struct net_device *netdev; + union ks_tx_hdr txh; + struct type_frame_head *frame_head_info; + u32 msg_enable; + u32 frame_cnt; + int bus_width; + int irq; + u16 rc_rxqcr; + u16 rc_txcr; + u16 rc_ier; + u16 sharedbus; + u16 cmd_reg_cache; + u16 cmd_reg_cache_int; + u16 promiscuous; + u16 all_mcast; + u16 mcast_lst_size; + u8 mcast_lst[MAX_MCAST_LST][MAC_ADDR_LEN]; + u8 mcast_bits[HW_MCAST_SIZE]; + u8 mac_addr[6]; + u8 fid; + u8 extra_byte; + u8 enabled; +} ks_str, *ks; + +#define BE3 0x8000 /* Byte Enable 3 */ +#define BE2 0x4000 /* Byte Enable 2 */ +#define BE1 0x2000 /* Byte Enable 1 */ +#define BE0 0x1000 /* Byte Enable 0 */ + +static u8 ks_rdreg8(struct eth_device *dev, u16 offset) +{ + u8 shift_bit = offset & 0x03; + u8 shift_data = (offset & 1) << 3; + + writew(offset | (BE0 << shift_bit), dev->iobase + 2); + + return (u8)(readw(dev->iobase) >> shift_data); +} + +static u16 ks_rdreg16(struct eth_device *dev, u16 offset) +{ + writew(offset | ((BE1 | BE0) << (offset & 0x02)), dev->iobase + 2); + + return readw(dev->iobase); +} + +static void ks_wrreg8(struct eth_device *dev, u16 offset, u8 val) +{ + u8 shift_bit = (offset & 0x03); + u16 value_write = (u16)(val << ((offset & 1) << 3)); + + writew(offset | (BE0 << shift_bit), dev->iobase + 2); + writew(value_write, dev->iobase); +} + +static void ks_wrreg16(struct eth_device *dev, u16 offset, u16 val) +{ + writew(offset | ((BE1 | BE0) << (offset & 0x02)), dev->iobase + 2); + writew(val, dev->iobase); +} + +/* + * ks_inblk - read a block of data from QMU. This is called after sudo DMA mode + * enabled. + * @ks: The chip state + * @wptr: buffer address to save data + * @len: length in byte to read + */ +static inline void ks_inblk(struct eth_device *dev, u16 *wptr, u32 len) +{ + len >>= 1; + + while (len--) + *wptr++ = readw(dev->iobase); +} + +/* + * ks_outblk - write data to QMU. This is called after sudo DMA mode enabled. + * @ks: The chip information + * @wptr: buffer address + * @len: length in byte to write + */ +static inline void ks_outblk(struct eth_device *dev, u16 *wptr, u32 len) +{ + len >>= 1; + + while (len--) + writew(*wptr++, dev->iobase); +} + +static void ks_enable_int(struct eth_device *dev) +{ + ks_wrreg16(dev, KS_IER, ks->rc_ier); +} + +static void ks_set_powermode(struct eth_device *dev, unsigned pwrmode) +{ + unsigned pmecr; + + ks_rdreg16(dev, KS_GRR); + pmecr = ks_rdreg16(dev, KS_PMECR); + pmecr &= ~PMECR_PM_MASK; + pmecr |= pwrmode; + + ks_wrreg16(dev, KS_PMECR, pmecr); +} + +/* + * ks_read_config - read chip configuration of bus width. + * @ks: The chip information + */ +static void ks_read_config(struct eth_device *dev) +{ + u16 reg_data = 0; + + /* Regardless of bus width, 8 bit read should always work. */ + reg_data = ks_rdreg8(dev, KS_CCR) & 0x00FF; + reg_data |= ks_rdreg8(dev, KS_CCR + 1) << 8; + + /* addr/data bus are multiplexed */ + ks->sharedbus = (reg_data & CCR_SHARED) == CCR_SHARED; + + /* + * There are garbage data when reading data from QMU, + * depending on bus-width. + */ + if (reg_data & CCR_8BIT) { + ks->bus_width = ENUM_BUS_8BIT; + ks->extra_byte = 1; + } else if (reg_data & CCR_16BIT) { + ks->bus_width = ENUM_BUS_16BIT; + ks->extra_byte = 2; + } else { + ks->bus_width = ENUM_BUS_32BIT; + ks->extra_byte = 4; + } +} + +/* + * ks_soft_reset - issue one of the soft reset to the device + * @ks: The device state. + * @op: The bit(s) to set in the GRR + * + * Issue the relevant soft-reset command to the device's GRR register + * specified by @op. + * + * Note, the delays are in there as a caution to ensure that the reset + * has time to take effect and then complete. Since the datasheet does + * not currently specify the exact sequence, we have chosen something + * that seems to work with our device. + */ +static void ks_soft_reset(struct eth_device *dev, unsigned op) +{ + /* Disable interrupt first */ + ks_wrreg16(dev, KS_IER, 0x0000); + ks_wrreg16(dev, KS_GRR, op); + mdelay(10); /* wait a short time to effect reset */ + ks_wrreg16(dev, KS_GRR, 0); + mdelay(1); /* wait for condition to clear */ +} + +void ks_enable_qmu(struct eth_device *dev) +{ + u16 w; + + w = ks_rdreg16(dev, KS_TXCR); + + /* Enables QMU Transmit (TXCR). */ + ks_wrreg16(dev, KS_TXCR, w | TXCR_TXE); + + /* Enable RX Frame Count Threshold and Auto-Dequeue RXQ Frame */ + w = ks_rdreg16(dev, KS_RXQCR); + ks_wrreg16(dev, KS_RXQCR, w | RXQCR_RXFCTE); + + /* Enables QMU Receive (RXCR1). */ + w = ks_rdreg16(dev, KS_RXCR1); + ks_wrreg16(dev, KS_RXCR1, w | RXCR1_RXE); +} + +static void ks_disable_qmu(struct eth_device *dev) +{ + u16 w; + + w = ks_rdreg16(dev, KS_TXCR); + + /* Disables QMU Transmit (TXCR). */ + w &= ~TXCR_TXE; + ks_wrreg16(dev, KS_TXCR, w); + + /* Disables QMU Receive (RXCR1). */ + w = ks_rdreg16(dev, KS_RXCR1); + w &= ~RXCR1_RXE; + ks_wrreg16(dev, KS_RXCR1, w); +} + +static inline void ks_read_qmu(struct eth_device *dev, u16 *buf, u32 len) +{ + u32 r = ks->extra_byte & 0x1; + u32 w = ks->extra_byte - r; + + /* 1. set sudo DMA mode */ + ks_wrreg16(dev, KS_RXFDPR, RXFDPR_RXFPAI); + ks_wrreg8(dev, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff); + + /* + * 2. read prepend data + * + * read 4 + extra bytes and discard them. + * extra bytes for dummy, 2 for status, 2 for len + */ + + if (r) + ks_rdreg8(dev, 0); + + ks_inblk(dev, buf, w + 2 + 2); + + /* 3. read pkt data */ + ks_inblk(dev, buf, ALIGN(len, 4)); + + /* 4. reset sudo DMA Mode */ + ks_wrreg8(dev, KS_RXQCR, (ks->rc_rxqcr & ~RXQCR_SDA) & 0xff); +} + +static void ks_rcv(struct eth_device *dev, uchar **pv_data) +{ + struct type_frame_head *frame_hdr = ks->frame_head_info; + int i; + + ks->frame_cnt = ks_rdreg16(dev, KS_RXFCTR) >> 8; + + /* read all header information */ + for (i = 0; i < ks->frame_cnt; i++) { + /* Checking Received packet status */ + frame_hdr->sts = ks_rdreg16(dev, KS_RXFHSR); + /* Get packet len from hardware */ + frame_hdr->len = ks_rdreg16(dev, KS_RXFHBCR); + frame_hdr++; + } + + frame_hdr = ks->frame_head_info; + while (ks->frame_cnt--) { + if ((frame_hdr->sts & RXFSHR_RXFV) && + (frame_hdr->len < RX_BUF_SIZE) && + frame_hdr->len) { + /* read data block including CRC 4 bytes */ + ks_read_qmu(dev, (u16 *)(*pv_data), frame_hdr->len); + + /* NetRxPackets buffer size is ok (*pv_data pointer) */ + NetReceive(*pv_data, frame_hdr->len); + pv_data++; + } else { + ks_wrreg16(dev, KS_RXQCR, (ks->rc_rxqcr | RXQCR_RRXEF)); + printf(DRIVERNAME ": bad packet\n"); + } + frame_hdr++; + } +} + +/* + * ks_read_selftest - read the selftest memory info. + * @ks: The device state + * + * Read and check the TX/RX memory selftest information. + */ +static int ks_read_selftest(struct eth_device *dev) +{ + u16 both_done = MBIR_TXMBF | MBIR_RXMBF; + u16 mbir; + int ret = 0; + + mbir = ks_rdreg16(dev, KS_MBIR); + + if ((mbir & both_done) != both_done) { + printf(DRIVERNAME ": Memory selftest not finished\n"); + return 0; + } + + if (mbir & MBIR_TXMBFA) { + printf(DRIVERNAME ": TX memory selftest fails\n"); + ret |= 1; + } + + if (mbir & MBIR_RXMBFA) { + printf(DRIVERNAME ": RX memory selftest fails\n"); + ret |= 2; + } + + debug(DRIVERNAME ": the selftest passes\n"); + + return ret; +} + +static void ks_setup(struct eth_device *dev) +{ + u16 w; + + /* Setup Transmit Frame Data Pointer Auto-Increment (TXFDPR) */ + ks_wrreg16(dev, KS_TXFDPR, TXFDPR_TXFPAI); + + /* Setup Receive Frame Data Pointer Auto-Increment */ + ks_wrreg16(dev, KS_RXFDPR, RXFDPR_RXFPAI); + + /* Setup Receive Frame Threshold - 1 frame (RXFCTFC) */ + ks_wrreg16(dev, KS_RXFCTR, 1 & RXFCTR_THRESHOLD_MASK); + + /* Setup RxQ Command Control (RXQCR) */ + ks->rc_rxqcr = RXQCR_CMD_CNTL; + ks_wrreg16(dev, KS_RXQCR, ks->rc_rxqcr); + + /* + * set the force mode to half duplex, default is full duplex + * because if the auto-negotiation fails, most switch uses + * half-duplex. + */ + w = ks_rdreg16(dev, KS_P1MBCR); + w &= ~P1MBCR_FORCE_FDX; + ks_wrreg16(dev, KS_P1MBCR, w); + + w = TXCR_TXFCE | TXCR_TXPE | TXCR_TXCRC | TXCR_TCGIP; + ks_wrreg16(dev, KS_TXCR, w); + + w = RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXUE | RXCR1_RXME | RXCR1_RXIPFCC; + + /* Normal mode */ + w |= RXCR1_RXPAFMA; + + ks_wrreg16(dev, KS_RXCR1, w); +} + +static void ks_setup_int(struct eth_device *dev) +{ + ks->rc_ier = 0x00; + + /* Clear the interrupts status of the hardware. */ + ks_wrreg16(dev, KS_ISR, 0xffff); + + /* Enables the interrupts of the hardware. */ + ks->rc_ier = (IRQ_LCI | IRQ_TXI | IRQ_RXI); +} + +static int ks8851_mll_detect_chip(struct eth_device *dev) +{ + unsigned short val, i; + + ks_read_config(dev); + + val = ks_rdreg16(dev, KS_CIDER); + + if (val == 0xffff) { + /* Special case -- no chip present */ + printf(DRIVERNAME ": is chip mounted ?\n"); + return -1; + } else if ((val & 0xfff0) != CIDER_ID) { + printf(DRIVERNAME ": Invalid chip id 0x%04x\n", val); + return -1; + } + + debug("Read back KS8851 id 0x%x\n", val); + + /* only one entry in the table */ + val &= 0xfff0; + for (i = 0; chip_ids[i].id != 0; i++) { + if (chip_ids[i].id == val) + break; + } + if (!chip_ids[i].id) { + printf(DRIVERNAME ": Unknown chip ID %04x\n", val); + return -1; + } + + dev->priv = (void *)&chip_ids[i]; + + return 0; +} + +static void ks8851_mll_reset(struct eth_device *dev) +{ + /* wake up powermode to normal mode */ + ks_set_powermode(dev, PMECR_PM_NORMAL); + mdelay(1); /* wait for normal mode to take effect */ + + /* Disable interrupt and reset */ + ks_soft_reset(dev, GRR_GSR); + + /* turn off the IRQs and ack any outstanding */ + ks_wrreg16(dev, KS_IER, 0x0000); + ks_wrreg16(dev, KS_ISR, 0xffff); + + /* shutdown RX/TX QMU */ + ks_disable_qmu(dev); +} + +static void ks8851_mll_phy_configure(struct eth_device *dev) +{ + u16 data; + + ks_setup(dev); + ks_setup_int(dev); + + /* Probing the phy */ + data = ks_rdreg16(dev, KS_OBCR); + ks_wrreg16(dev, KS_OBCR, data | OBCR_ODS_16MA); + + debug(DRIVERNAME ": phy initialized\n"); +} + +static void ks8851_mll_enable(struct eth_device *dev) +{ + ks_wrreg16(dev, KS_ISR, 0xffff); + ks_enable_int(dev); + ks_enable_qmu(dev); +} + +static int ks8851_mll_init(struct eth_device *dev, bd_t *bd) +{ + struct chip_id *id = dev->priv; + + debug(DRIVERNAME ": detected %s controller\n", id->name); + + if (ks_read_selftest(dev)) { + printf(DRIVERNAME ": Selftest failed\n"); + return -1; + } + + ks8851_mll_reset(dev); + + /* Configure the PHY, initialize the link state */ + ks8851_mll_phy_configure(dev); + + /* static allocation of private informations */ + ks->frame_head_info = fr_h_i; + + /* Turn on Tx + Rx */ + ks8851_mll_enable(dev); + + return 0; +} + +static void ks_write_qmu(struct eth_device *dev, u8 *pdata, u16 len) +{ + /* start header at txb[0] to align txw entries */ + ks->txh.txw[0] = 0; + ks->txh.txw[1] = cpu_to_le16(len); + + /* 1. set sudo-DMA mode */ + ks_wrreg16(dev, KS_TXFDPR, TXFDPR_TXFPAI); + ks_wrreg8(dev, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff); + /* 2. write status/lenth info */ + ks_outblk(dev, ks->txh.txw, 4); + /* 3. write pkt data */ + ks_outblk(dev, (u16 *)pdata, ALIGN(len, 4)); + /* 4. reset sudo-DMA mode */ + ks_wrreg8(dev, KS_RXQCR, (ks->rc_rxqcr & ~RXQCR_SDA) & 0xff); + /* 5. Enqueue Tx(move the pkt from TX buffer into TXQ) */ + ks_wrreg16(dev, KS_TXQCR, TXQCR_METFE); + /* 6. wait until TXQCR_METFE is auto-cleared */ + do { } while (ks_rdreg16(dev, KS_TXQCR) & TXQCR_METFE); +} + +static int ks8851_mll_send(struct eth_device *dev, void *packet, int length) +{ + u8 *data = (u8 *)packet; + u16 tmplen = (u16)length; + u16 retv; + + /* + * Extra space are required: + * 4 byte for alignment, 4 for status/length, 4 for CRC + */ + retv = ks_rdreg16(dev, KS_TXMIR) & 0x1fff; + if (retv >= tmplen + 12) { + ks_write_qmu(dev, data, tmplen); + return 0; + } else { + printf(DRIVERNAME ": failed to send packet: No buffer\n"); + return -1; + } +} + +static void ks8851_mll_halt(struct eth_device *dev) +{ + ks8851_mll_reset(dev); +} + +/* + * Maximum receive ring size; that is, the number of packets + * we can buffer before overflow happens. Basically, this just + * needs to be enough to prevent a packet being discarded while + * we are processing the previous one. + */ +static int ks8851_mll_recv(struct eth_device *dev) +{ + u16 status; + + status = ks_rdreg16(dev, KS_ISR); + + ks_wrreg16(dev, KS_ISR, status); + + if ((status & IRQ_RXI)) + ks_rcv(dev, (uchar **)NetRxPackets); + + if ((status & IRQ_LDI)) { + u16 pmecr = ks_rdreg16(dev, KS_PMECR); + pmecr &= ~PMECR_WKEVT_MASK; + ks_wrreg16(dev, KS_PMECR, pmecr | PMECR_WKEVT_LINK); + } + + return 0; +} + +static int ks8851_mll_write_hwaddr(struct eth_device *dev) +{ + u16 addrl, addrm, addrh; + + addrh = (dev->enetaddr[0] << 8) | dev->enetaddr[1]; + addrm = (dev->enetaddr[2] << 8) | dev->enetaddr[3]; + addrl = (dev->enetaddr[4] << 8) | dev->enetaddr[5]; + + ks_wrreg16(dev, KS_MARH, addrh); + ks_wrreg16(dev, KS_MARM, addrm); + ks_wrreg16(dev, KS_MARL, addrl); + + return 0; +} + +int ks8851_mll_initialize(u8 dev_num, int base_addr) +{ + struct eth_device *dev; + + dev = malloc(sizeof(*dev)); + if (!dev) { + printf("Error: Failed to allocate memory\n"); + return -1; + } + memset(dev, 0, sizeof(*dev)); + + dev->iobase = base_addr; + + ks = &ks_str; + + /* Try to detect chip. Will fail if not present. */ + if (ks8851_mll_detect_chip(dev)) { + free(dev); + return -1; + } + + dev->init = ks8851_mll_init; + dev->halt = ks8851_mll_halt; + dev->send = ks8851_mll_send; + dev->recv = ks8851_mll_recv; + dev->write_hwaddr = ks8851_mll_write_hwaddr; + sprintf(dev->name, "%s-%hu", DRIVERNAME, dev_num); + + eth_register(dev); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/ks8851_mll.h b/qemu/roms/u-boot/drivers/net/ks8851_mll.h new file mode 100644 index 000000000..7f90ae4e5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ks8851_mll.h @@ -0,0 +1,357 @@ +/* + * drivers/net/ks8851_mll.c + * + * Supports: + * KS8851 16bit MLL chip from Micrel Inc. + * + * Copyright (c) 2009 Micrel Inc. + * + * modified by + * (c) 2011 Bticino s.p.a, Roberto Cerati + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef _KS8851_MLL_H_ +#define _KS8851_MLL_H_ + +#include + +#define KS_CCR 0x08 +#define CCR_EEPROM (1 << 9) +#define CCR_SPI (1 << 8) +#define CCR_8BIT (1 << 7) +#define CCR_16BIT (1 << 6) +#define CCR_32BIT (1 << 5) +#define CCR_SHARED (1 << 4) +#define CCR_32PIN (1 << 0) + +/* MAC address registers */ +#define KS_MARL 0x10 +#define KS_MARM 0x12 +#define KS_MARH 0x14 + +#define KS_OBCR 0x20 +#define OBCR_ODS_16MA (1 << 6) + +#define KS_EEPCR 0x22 +#define EEPCR_EESA (1 << 4) +#define EEPCR_EESB (1 << 3) +#define EEPCR_EEDO (1 << 2) +#define EEPCR_EESCK (1 << 1) +#define EEPCR_EECS (1 << 0) + +#define KS_MBIR 0x24 +#define MBIR_TXMBF (1 << 12) +#define MBIR_TXMBFA (1 << 11) +#define MBIR_RXMBF (1 << 4) +#define MBIR_RXMBFA (1 << 3) + +#define KS_GRR 0x26 +#define GRR_QMU (1 << 1) +#define GRR_GSR (1 << 0) + +#define KS_WFCR 0x2A +#define WFCR_MPRXE (1 << 7) +#define WFCR_WF3E (1 << 3) +#define WFCR_WF2E (1 << 2) +#define WFCR_WF1E (1 << 1) +#define WFCR_WF0E (1 << 0) + +#define KS_WF0CRC0 0x30 +#define KS_WF0CRC1 0x32 +#define KS_WF0BM0 0x34 +#define KS_WF0BM1 0x36 +#define KS_WF0BM2 0x38 +#define KS_WF0BM3 0x3A + +#define KS_WF1CRC0 0x40 +#define KS_WF1CRC1 0x42 +#define KS_WF1BM0 0x44 +#define KS_WF1BM1 0x46 +#define KS_WF1BM2 0x48 +#define KS_WF1BM3 0x4A + +#define KS_WF2CRC0 0x50 +#define KS_WF2CRC1 0x52 +#define KS_WF2BM0 0x54 +#define KS_WF2BM1 0x56 +#define KS_WF2BM2 0x58 +#define KS_WF2BM3 0x5A + +#define KS_WF3CRC0 0x60 +#define KS_WF3CRC1 0x62 +#define KS_WF3BM0 0x64 +#define KS_WF3BM1 0x66 +#define KS_WF3BM2 0x68 +#define KS_WF3BM3 0x6A + +#define KS_TXCR 0x70 +#define TXCR_TCGICMP (1 << 8) +#define TXCR_TCGUDP (1 << 7) +#define TXCR_TCGTCP (1 << 6) +#define TXCR_TCGIP (1 << 5) +#define TXCR_FTXQ (1 << 4) +#define TXCR_TXFCE (1 << 3) +#define TXCR_TXPE (1 << 2) +#define TXCR_TXCRC (1 << 1) +#define TXCR_TXE (1 << 0) + +#define KS_TXSR 0x72 +#define TXSR_TXLC (1 << 13) +#define TXSR_TXMC (1 << 12) +#define TXSR_TXFID_MASK (0x3f << 0) +#define TXSR_TXFID_SHIFT (0) +#define TXSR_TXFID_GET(_v) (((_v) >> 0) & 0x3f) + + +#define KS_RXCR1 0x74 +#define RXCR1_FRXQ (1 << 15) +#define RXCR1_RXUDPFCC (1 << 14) +#define RXCR1_RXTCPFCC (1 << 13) +#define RXCR1_RXIPFCC (1 << 12) +#define RXCR1_RXPAFMA (1 << 11) +#define RXCR1_RXFCE (1 << 10) +#define RXCR1_RXEFE (1 << 9) +#define RXCR1_RXMAFMA (1 << 8) +#define RXCR1_RXBE (1 << 7) +#define RXCR1_RXME (1 << 6) +#define RXCR1_RXUE (1 << 5) +#define RXCR1_RXAE (1 << 4) +#define RXCR1_RXINVF (1 << 1) +#define RXCR1_RXE (1 << 0) +#define RXCR1_FILTER_MASK (RXCR1_RXINVF | RXCR1_RXAE | \ + RXCR1_RXMAFMA | RXCR1_RXPAFMA) + +#define KS_RXCR2 0x76 +#define RXCR2_SRDBL_MASK (0x7 << 5) +#define RXCR2_SRDBL_SHIFT (5) +#define RXCR2_SRDBL_4B (0x0 << 5) +#define RXCR2_SRDBL_8B (0x1 << 5) +#define RXCR2_SRDBL_16B (0x2 << 5) +#define RXCR2_SRDBL_32B (0x3 << 5) +/* #define RXCR2_SRDBL_FRAME (0x4 << 5) */ +#define RXCR2_IUFFP (1 << 4) +#define RXCR2_RXIUFCEZ (1 << 3) +#define RXCR2_UDPLFE (1 << 2) +#define RXCR2_RXICMPFCC (1 << 1) +#define RXCR2_RXSAF (1 << 0) + +#define KS_TXMIR 0x78 + +#define KS_RXFHSR 0x7C +#define RXFSHR_RXFV (1 << 15) +#define RXFSHR_RXICMPFCS (1 << 13) +#define RXFSHR_RXIPFCS (1 << 12) +#define RXFSHR_RXTCPFCS (1 << 11) +#define RXFSHR_RXUDPFCS (1 << 10) +#define RXFSHR_RXBF (1 << 7) +#define RXFSHR_RXMF (1 << 6) +#define RXFSHR_RXUF (1 << 5) +#define RXFSHR_RXMR (1 << 4) +#define RXFSHR_RXFT (1 << 3) +#define RXFSHR_RXFTL (1 << 2) +#define RXFSHR_RXRF (1 << 1) +#define RXFSHR_RXCE (1 << 0) +#define RXFSHR_ERR (RXFSHR_RXCE | RXFSHR_RXRF |\ + RXFSHR_RXFTL | RXFSHR_RXMR |\ + RXFSHR_RXICMPFCS | RXFSHR_RXIPFCS |\ + RXFSHR_RXTCPFCS) +#define KS_RXFHBCR 0x7E +#define RXFHBCR_CNT_MASK 0x0FFF + +#define KS_TXQCR 0x80 +#define TXQCR_AETFE (1 << 2) +#define TXQCR_TXQMAM (1 << 1) +#define TXQCR_METFE (1 << 0) + +#define KS_RXQCR 0x82 +#define RXQCR_RXDTTS (1 << 12) +#define RXQCR_RXDBCTS (1 << 11) +#define RXQCR_RXFCTS (1 << 10) +#define RXQCR_RXIPHTOE (1 << 9) +#define RXQCR_RXDTTE (1 << 7) +#define RXQCR_RXDBCTE (1 << 6) +#define RXQCR_RXFCTE (1 << 5) +#define RXQCR_ADRFE (1 << 4) +#define RXQCR_SDA (1 << 3) +#define RXQCR_RRXEF (1 << 0) +#define RXQCR_CMD_CNTL (RXQCR_RXFCTE|RXQCR_ADRFE) + +#define KS_TXFDPR 0x84 +#define TXFDPR_TXFPAI (1 << 14) +#define TXFDPR_TXFP_MASK (0x7ff << 0) +#define TXFDPR_TXFP_SHIFT (0) + +#define KS_RXFDPR 0x86 +#define RXFDPR_RXFPAI (1 << 14) + +#define KS_RXDTTR 0x8C +#define KS_RXDBCTR 0x8E + +#define KS_IER 0x90 +#define KS_ISR 0x92 +#define IRQ_LCI (1 << 15) +#define IRQ_TXI (1 << 14) +#define IRQ_RXI (1 << 13) +#define IRQ_RXOI (1 << 11) +#define IRQ_TXPSI (1 << 9) +#define IRQ_RXPSI (1 << 8) +#define IRQ_TXSAI (1 << 6) +#define IRQ_RXWFDI (1 << 5) +#define IRQ_RXMPDI (1 << 4) +#define IRQ_LDI (1 << 3) +#define IRQ_EDI (1 << 2) +#define IRQ_SPIBEI (1 << 1) +#define IRQ_DEDI (1 << 0) + +#define KS_RXFCTR 0x9C +#define RXFCTR_THRESHOLD_MASK 0x00FF + +#define KS_RXFC 0x9D +#define RXFCTR_RXFC_MASK (0xff << 8) +#define RXFCTR_RXFC_SHIFT (8) +#define RXFCTR_RXFC_GET(_v) (((_v) >> 8) & 0xff) +#define RXFCTR_RXFCT_MASK (0xff << 0) +#define RXFCTR_RXFCT_SHIFT (0) + +#define KS_TXNTFSR 0x9E + +#define KS_MAHTR0 0xA0 +#define KS_MAHTR1 0xA2 +#define KS_MAHTR2 0xA4 +#define KS_MAHTR3 0xA6 + +#define KS_FCLWR 0xB0 +#define KS_FCHWR 0xB2 +#define KS_FCOWR 0xB4 + +#define KS_CIDER 0xC0 +#define CIDER_ID 0x8870 +#define CIDER_REV_MASK (0x7 << 1) +#define CIDER_REV_SHIFT (1) +#define CIDER_REV_GET(_v) (((_v) >> 1) & 0x7) + +#define KS_CGCR 0xC6 +#define KS_IACR 0xC8 +#define IACR_RDEN (1 << 12) +#define IACR_TSEL_MASK (0x3 << 10) +#define IACR_TSEL_SHIFT (10) +#define IACR_TSEL_MIB (0x3 << 10) +#define IACR_ADDR_MASK (0x1f << 0) +#define IACR_ADDR_SHIFT (0) + +#define KS_IADLR 0xD0 +#define KS_IAHDR 0xD2 + +#define KS_PMECR 0xD4 +#define PMECR_PME_DELAY (1 << 14) +#define PMECR_PME_POL (1 << 12) +#define PMECR_WOL_WAKEUP (1 << 11) +#define PMECR_WOL_MAGICPKT (1 << 10) +#define PMECR_WOL_LINKUP (1 << 9) +#define PMECR_WOL_ENERGY (1 << 8) +#define PMECR_AUTO_WAKE_EN (1 << 7) +#define PMECR_WAKEUP_NORMAL (1 << 6) +#define PMECR_WKEVT_MASK (0xf << 2) +#define PMECR_WKEVT_SHIFT (2) +#define PMECR_WKEVT_GET(_v) (((_v) >> 2) & 0xf) +#define PMECR_WKEVT_ENERGY (0x1 << 2) +#define PMECR_WKEVT_LINK (0x2 << 2) +#define PMECR_WKEVT_MAGICPKT (0x4 << 2) +#define PMECR_WKEVT_FRAME (0x8 << 2) +#define PMECR_PM_MASK (0x3 << 0) +#define PMECR_PM_SHIFT (0) +#define PMECR_PM_NORMAL (0x0 << 0) +#define PMECR_PM_ENERGY (0x1 << 0) +#define PMECR_PM_SOFTDOWN (0x2 << 0) +#define PMECR_PM_POWERSAVE (0x3 << 0) + +/* Standard MII PHY data */ +#define KS_P1MBCR 0xE4 +#define P1MBCR_FORCE_FDX (1 << 8) + +#define KS_P1MBSR 0xE6 +#define P1MBSR_AN_COMPLETE (1 << 5) +#define P1MBSR_AN_CAPABLE (1 << 3) +#define P1MBSR_LINK_UP (1 << 2) + +#define KS_PHY1ILR 0xE8 +#define KS_PHY1IHR 0xEA +#define KS_P1ANAR 0xEC +#define KS_P1ANLPR 0xEE + +#define KS_P1SCLMD 0xF4 +#define P1SCLMD_LEDOFF (1 << 15) +#define P1SCLMD_TXIDS (1 << 14) +#define P1SCLMD_RESTARTAN (1 << 13) +#define P1SCLMD_DISAUTOMDIX (1 << 10) +#define P1SCLMD_FORCEMDIX (1 << 9) +#define P1SCLMD_AUTONEGEN (1 << 7) +#define P1SCLMD_FORCE100 (1 << 6) +#define P1SCLMD_FORCEFDX (1 << 5) +#define P1SCLMD_ADV_FLOW (1 << 4) +#define P1SCLMD_ADV_100BT_FDX (1 << 3) +#define P1SCLMD_ADV_100BT_HDX (1 << 2) +#define P1SCLMD_ADV_10BT_FDX (1 << 1) +#define P1SCLMD_ADV_10BT_HDX (1 << 0) + +#define KS_P1CR 0xF6 +#define P1CR_HP_MDIX (1 << 15) +#define P1CR_REV_POL (1 << 13) +#define P1CR_OP_100M (1 << 10) +#define P1CR_OP_FDX (1 << 9) +#define P1CR_OP_MDI (1 << 7) +#define P1CR_AN_DONE (1 << 6) +#define P1CR_LINK_GOOD (1 << 5) +#define P1CR_PNTR_FLOW (1 << 4) +#define P1CR_PNTR_100BT_FDX (1 << 3) +#define P1CR_PNTR_100BT_HDX (1 << 2) +#define P1CR_PNTR_10BT_FDX (1 << 1) +#define P1CR_PNTR_10BT_HDX (1 << 0) + +/* TX Frame control */ +#define TXFR_TXIC (1 << 15) +#define TXFR_TXFID_MASK (0x3f << 0) +#define TXFR_TXFID_SHIFT (0) + +#define KS_P1SR 0xF8 +#define P1SR_HP_MDIX (1 << 15) +#define P1SR_REV_POL (1 << 13) +#define P1SR_OP_100M (1 << 10) +#define P1SR_OP_FDX (1 << 9) +#define P1SR_OP_MDI (1 << 7) +#define P1SR_AN_DONE (1 << 6) +#define P1SR_LINK_GOOD (1 << 5) +#define P1SR_PNTR_FLOW (1 << 4) +#define P1SR_PNTR_100BT_FDX (1 << 3) +#define P1SR_PNTR_100BT_HDX (1 << 2) +#define P1SR_PNTR_10BT_FDX (1 << 1) +#define P1SR_PNTR_10BT_HDX (1 << 0) + +#define ENUM_BUS_NONE 0 +#define ENUM_BUS_8BIT 1 +#define ENUM_BUS_16BIT 2 +#define ENUM_BUS_32BIT 3 + +#define MAX_MCAST_LST 32 +#define HW_MCAST_SIZE 8 +#define MAC_ADDR_LEN 6 + +/* Chip ID values */ +struct chip_id { + u16 id; + char *name; +}; + +#endif diff --git a/qemu/roms/u-boot/drivers/net/lan91c96.c b/qemu/roms/u-boot/drivers/net/lan91c96.c new file mode 100644 index 000000000..229658abc --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/lan91c96.c @@ -0,0 +1,804 @@ +/*------------------------------------------------------------------------ + * lan91c96.c + * This is a driver for SMSC's LAN91C96 single-chip Ethernet device, based + * on the SMC91111 driver from U-boot. + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Rolf Offermanns + * + * Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + * Developed by Simple Network Magic Corporation (SNMC) + * Copyright (C) 1996 by Erik Stahlman (ES) + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Information contained in this file was obtained from the LAN91C96 + * manual from SMC. To get a copy, if you really want one, you can find + * information under www.smsc.com. + * + * "Features" of the SMC chip: + * 6144 byte packet memory. ( for the 91C96 ) + * EEPROM for configuration + * AUI/TP selection ( mine has 10Base2/10BaseT select ) + * + * Arguments: + * io = for the base address + * irq = for the IRQ + * + * author: + * Erik Stahlman ( erik@vt.edu ) + * Daris A Nevil ( dnevil@snmc.com ) + * + * + * Hardware multicast code from Peter Cammaert ( pc@denkart.be ) + * + * Sources: + * o SMSC LAN91C96 databook (www.smsc.com) + * o smc91111.c (u-boot driver) + * o smc9194.c (linux kernel driver) + * o lan91c96.c (Intel Diagnostic Manager driver) + * + * History: + * 04/30/03 Mathijs Haarman Modified smc91111.c (u-boot version) + * for lan91c96 + *--------------------------------------------------------------------------- + */ + +#include +#include +#include +#include "lan91c96.h" +#include +#include + +/*------------------------------------------------------------------------ + * + * Configuration options, for the experienced user to change. + * + -------------------------------------------------------------------------*/ + +/* Use power-down feature of the chip */ +#define POWER_DOWN 0 + +/* + * Wait time for memory to be free. This probably shouldn't be + * tuned that much, as waiting for this means nothing else happens + * in the system +*/ +#define MEMORY_WAIT_TIME 16 + +#define SMC_DEBUG 0 + +#if (SMC_DEBUG > 2 ) +#define PRINTK3(args...) printf(args) +#else +#define PRINTK3(args...) +#endif + +#if SMC_DEBUG > 1 +#define PRINTK2(args...) printf(args) +#else +#define PRINTK2(args...) +#endif + +#ifdef SMC_DEBUG +#define PRINTK(args...) printf(args) +#else +#define PRINTK(args...) +#endif + + +/*------------------------------------------------------------------------ + * + * The internal workings of the driver. If you are changing anything + * here with the SMC stuff, you should have the datasheet and know + * what you are doing. + * + *------------------------------------------------------------------------ + */ +#define DRIVER_NAME "LAN91C96" +#define SMC_ALLOC_MAX_TRY 5 +#define SMC_TX_TIMEOUT 30 + +#define ETH_ZLEN 60 + +#ifdef CONFIG_LAN91C96_USE_32_BIT +#define USE_32_BIT 1 +#else +#undef USE_32_BIT +#endif + +/* See if a MAC address is defined in the current environment. If so use it. If not + . print a warning and set the environment and other globals with the default. + . If an EEPROM is present it really should be consulted. +*/ +static int smc_get_ethaddr(bd_t *bd, struct eth_device *dev); +static int get_rom_mac(struct eth_device *dev, unsigned char *v_rom_mac); + +/* ------------------------------------------------------------ + * Internal routines + * ------------------------------------------------------------ + */ + +static unsigned char smc_mac_addr[] = { 0xc0, 0x00, 0x00, 0x1b, 0x62, 0x9c }; + +/* + * This function must be called before smc_open() if you want to override + * the default mac address. + */ + +static void smc_set_mac_addr(const unsigned char *addr) +{ + int i; + + for (i = 0; i < sizeof (smc_mac_addr); i++) { + smc_mac_addr[i] = addr[i]; + } +} + +/*********************************************** + * Show available memory * + ***********************************************/ +void dump_memory_info(struct eth_device *dev) +{ + __maybe_unused word mem_info; + word old_bank; + + old_bank = SMC_inw(dev, LAN91C96_BANK_SELECT) & 0xF; + + SMC_SELECT_BANK(dev, 0); + mem_info = SMC_inw(dev, LAN91C96_MIR); + PRINTK2 ("Memory: %4d available\n", (mem_info >> 8) * 2048); + + SMC_SELECT_BANK(dev, old_bank); +} + +/* + * A rather simple routine to print out a packet for debugging purposes. + */ +#if SMC_DEBUG > 2 +static void print_packet (byte *, int); +#endif + +static int poll4int (struct eth_device *dev, byte mask, int timeout) +{ + int tmo = get_timer (0) + timeout * CONFIG_SYS_HZ; + int is_timeout = 0; + word old_bank = SMC_inw(dev, LAN91C96_BANK_SELECT); + + PRINTK2 ("Polling...\n"); + SMC_SELECT_BANK(dev, 2); + while ((SMC_inw(dev, LAN91C96_INT_STATS) & mask) == 0) { + if (get_timer (0) >= tmo) { + is_timeout = 1; + break; + } + } + + /* restore old bank selection */ + SMC_SELECT_BANK(dev, old_bank); + + if (is_timeout) + return 1; + else + return 0; +} + +/* + * Function: smc_reset + * Purpose: + * This sets the SMC91111 chip to its normal state, hopefully from whatever + * mess that any other DOS driver has put it in. + * + * Maybe I should reset more registers to defaults in here? SOFTRST should + * do that for me. + * + * Method: + * 1. send a SOFT RESET + * 2. wait for it to finish + * 3. enable autorelease mode + * 4. reset the memory management unit + * 5. clear all interrupts + * +*/ +static void smc_reset(struct eth_device *dev) +{ + PRINTK2("%s:smc_reset\n", dev->name); + + /* This resets the registers mostly to defaults, but doesn't + affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK(dev, 0); + SMC_outw(dev, LAN91C96_RCR_SOFT_RST, LAN91C96_RCR); + + udelay (10); + + /* Disable transmit and receive functionality */ + SMC_outw(dev, 0, LAN91C96_RCR); + SMC_outw(dev, 0, LAN91C96_TCR); + + /* set the control register */ + SMC_SELECT_BANK(dev, 1); + SMC_outw(dev, SMC_inw(dev, LAN91C96_CONTROL) | LAN91C96_CTR_BIT_8, + LAN91C96_CONTROL); + + /* Disable all interrupts */ + SMC_outb(dev, 0, LAN91C96_INT_MASK); +} + +/* + * Function: smc_enable + * Purpose: let the chip talk to the outside work + * Method: + * 1. Initialize the Memory Configuration Register + * 2. Enable the transmitter + * 3. Enable the receiver +*/ +static void smc_enable(struct eth_device *dev) +{ + PRINTK2("%s:smc_enable\n", dev->name); + SMC_SELECT_BANK(dev, 0); + + /* Initialize the Memory Configuration Register. See page + 49 of the LAN91C96 data sheet for details. */ + SMC_outw(dev, LAN91C96_MCR_TRANSMIT_PAGES, LAN91C96_MCR); + + /* Initialize the Transmit Control Register */ + SMC_outw(dev, LAN91C96_TCR_TXENA, LAN91C96_TCR); + /* Initialize the Receive Control Register + * FIXME: + * The promiscuous bit set because I could not receive ARP reply + * packets from the server when I send a ARP request. It only works + * when I set the promiscuous bit + */ + SMC_outw(dev, LAN91C96_RCR_RXEN | LAN91C96_RCR_PRMS, LAN91C96_RCR); +} + +/* + * Function: smc_shutdown + * Purpose: closes down the SMC91xxx chip. + * Method: + * 1. zero the interrupt mask + * 2. clear the enable receive flag + * 3. clear the enable xmit flags + * + * TODO: + * (1) maybe utilize power down mode. + * Why not yet? Because while the chip will go into power down mode, + * the manual says that it will wake up in response to any I/O requests + * in the register space. Empirical results do not show this working. + */ +static void smc_shutdown(struct eth_device *dev) +{ + PRINTK2("%s:smc_shutdown\n", dev->name); + + /* no more interrupts for me */ + SMC_SELECT_BANK(dev, 2); + SMC_outb(dev, 0, LAN91C96_INT_MASK); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK(dev, 0); + SMC_outb(dev, 0, LAN91C96_RCR); + SMC_outb(dev, 0, LAN91C96_TCR); +} + + +/* + * Function: smc_hardware_send_packet(struct net_device * ) + * Purpose: + * This sends the actual packet to the SMC9xxx chip. + * + * Algorithm: + * First, see if a saved_skb is available. + * ( this should NOT be called if there is no 'saved_skb' + * Now, find the packet number that the chip allocated + * Point the data pointers at it in memory + * Set the length word in the chip's memory + * Dump the packet to chip memory + * Check if a last byte is needed ( odd length packet ) + * if so, set the control flag right + * Tell the card to send it + * Enable the transmit interrupt, so I know if it failed + * Free the kernel data if I actually sent it. + */ +static int smc_send_packet(struct eth_device *dev, void *packet, + int packet_length) +{ + byte packet_no; + byte *buf; + int length; + int numPages; + int try = 0; + int time_out; + byte status; + + + PRINTK3("%s:smc_hardware_send_packet\n", dev->name); + + length = ETH_ZLEN < packet_length ? packet_length : ETH_ZLEN; + + /* allocate memory + ** The MMU wants the number of pages to be the number of 256 bytes + ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) + ** + ** The 91C111 ignores the size bits, but the code is left intact + ** for backwards and future compatibility. + ** + ** Pkt size for allocating is data length +6 (for additional status + ** words, length and ctl!) + ** + ** If odd size then last byte is included in this header. + */ + numPages = ((length & 0xfffe) + 6); + numPages >>= 8; /* Divide by 256 */ + + if (numPages > 7) { + printf("%s: Far too big packet error. \n", dev->name); + return 0; + } + + /* now, try to allocate the memory */ + + SMC_SELECT_BANK(dev, 2); + SMC_outw(dev, LAN91C96_MMUCR_ALLOC_TX | numPages, LAN91C96_MMU); + + again: + try++; + time_out = MEMORY_WAIT_TIME; + do { + status = SMC_inb(dev, LAN91C96_INT_STATS); + if (status & LAN91C96_IST_ALLOC_INT) { + + SMC_outb(dev, LAN91C96_IST_ALLOC_INT, + LAN91C96_INT_STATS); + break; + } + } while (--time_out); + + if (!time_out) { + PRINTK2 ("%s: memory allocation, try %d failed ...\n", + dev->name, try); + if (try < SMC_ALLOC_MAX_TRY) + goto again; + else + return 0; + } + + PRINTK2 ("%s: memory allocation, try %d succeeded ...\n", + dev->name, try); + + /* I can send the packet now.. */ + buf = (byte *) packet; + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = SMC_inb(dev, LAN91C96_ARR); + if (packet_no & LAN91C96_ARR_FAILED) { + /* or isn't there? BAD CHIP! */ + printf("%s: Memory allocation failed. \n", dev->name); + return 0; + } + + /* we have a packet address, so tell the card to use it */ + SMC_outb(dev, packet_no, LAN91C96_PNR); + + /* point to the beginning of the packet */ + SMC_outw(dev, LAN91C96_PTR_AUTO_INCR, LAN91C96_POINTER); + + PRINTK3("%s: Trying to xmit packet of length %x\n", + dev->name, length); + +#if SMC_DEBUG > 2 + printf ("Transmitting Packet\n"); + print_packet (buf, length); +#endif + + /* send the packet length ( +6 for status, length and ctl byte ) + and the status word ( set to zeros ) */ +#ifdef USE_32_BIT + SMC_outl(dev, (length + 6) << 16, LAN91C96_DATA_HIGH); +#else + SMC_outw(dev, 0, LAN91C96_DATA_HIGH); + /* send the packet length ( +6 for status words, length, and ctl */ + SMC_outw(dev, (length + 6), LAN91C96_DATA_HIGH); +#endif /* USE_32_BIT */ + + /* send the actual data + * I _think_ it's faster to send the longs first, and then + * mop up by sending the last word. It depends heavily + * on alignment, at least on the 486. Maybe it would be + * a good idea to check which is optimal? But that could take + * almost as much time as is saved? + */ +#ifdef USE_32_BIT + SMC_outsl(dev, LAN91C96_DATA_HIGH, buf, length >> 2); + if (length & 0x2) + SMC_outw(dev, *((word *) (buf + (length & 0xFFFFFFFC))), + LAN91C96_DATA_HIGH); +#else + SMC_outsw(dev, LAN91C96_DATA_HIGH, buf, (length) >> 1); +#endif /* USE_32_BIT */ + + /* Send the last byte, if there is one. */ + if ((length & 1) == 0) { + SMC_outw(dev, 0, LAN91C96_DATA_HIGH); + } else { + SMC_outw(dev, buf[length - 1] | 0x2000, LAN91C96_DATA_HIGH); + } + + /* and let the chipset deal with it */ + SMC_outw(dev, LAN91C96_MMUCR_ENQUEUE, LAN91C96_MMU); + + /* poll for TX INT */ + if (poll4int (dev, LAN91C96_MSK_TX_INT, SMC_TX_TIMEOUT)) { + /* sending failed */ + PRINTK2("%s: TX timeout, sending failed...\n", dev->name); + + /* release packet */ + SMC_outw(dev, LAN91C96_MMUCR_RELEASE_TX, LAN91C96_MMU); + + /* wait for MMU getting ready (low) */ + while (SMC_inw(dev, LAN91C96_MMU) & LAN91C96_MMUCR_NO_BUSY) + udelay (10); + + PRINTK2("MMU ready\n"); + + + return 0; + } else { + /* ack. int */ + SMC_outw(dev, LAN91C96_IST_TX_INT, LAN91C96_INT_STATS); + + PRINTK2("%s: Sent packet of length %d \n", dev->name, length); + + /* release packet */ + SMC_outw(dev, LAN91C96_MMUCR_RELEASE_TX, LAN91C96_MMU); + + /* wait for MMU getting ready (low) */ + while (SMC_inw(dev, LAN91C96_MMU) & LAN91C96_MMUCR_NO_BUSY) + udelay (10); + + PRINTK2 ("MMU ready\n"); + } + + return length; +} + + +/* + * Open and Initialize the board + * + * Set up everything, reset the card, etc .. + * + */ +static int smc_open(bd_t *bd, struct eth_device *dev) +{ + int i, err; /* used to set hw ethernet address */ + + PRINTK2("%s:smc_open\n", dev->name); + + /* reset the hardware */ + + smc_reset(dev); + smc_enable(dev); + + SMC_SELECT_BANK(dev, 1); + /* set smc_mac_addr, and sync it with u-boot globals */ + err = smc_get_ethaddr(bd, dev); + if (err < 0) + return -1; +#ifdef USE_32_BIT + for (i = 0; i < 6; i += 2) { + word address; + + address = smc_mac_addr[i + 1] << 8; + address |= smc_mac_addr[i]; + SMC_outw(dev, address, LAN91C96_IA0 + i); + } +#else + for (i = 0; i < 6; i++) + SMC_outb(dev, smc_mac_addr[i], LAN91C96_IA0 + i); +#endif + return 0; +} + +/*------------------------------------------------------------- + * + * smc_rcv - receive a packet from the card + * + * There is ( at least ) a packet waiting to be read from + * chip-memory. + * + * o Read the status + * o If an error, record it + * o otherwise, read in the packet + *------------------------------------------------------------- + */ +static int smc_rcv(struct eth_device *dev) +{ + int packet_number; + word status; + word packet_length; + int is_error = 0; + +#ifdef USE_32_BIT + dword stat_len; +#endif + + + SMC_SELECT_BANK(dev, 2); + packet_number = SMC_inw(dev, LAN91C96_FIFO); + + if (packet_number & LAN91C96_FIFO_RXEMPTY) { + return 0; + } + + PRINTK3("%s:smc_rcv\n", dev->name); + /* start reading from the start of the packet */ + SMC_outw(dev, LAN91C96_PTR_READ | LAN91C96_PTR_RCV | + LAN91C96_PTR_AUTO_INCR, LAN91C96_POINTER); + + /* First two words are status and packet_length */ +#ifdef USE_32_BIT + stat_len = SMC_inl(dev, LAN91C96_DATA_HIGH); + status = stat_len & 0xffff; + packet_length = stat_len >> 16; +#else + status = SMC_inw(dev, LAN91C96_DATA_HIGH); + packet_length = SMC_inw(dev, LAN91C96_DATA_HIGH); +#endif + + packet_length &= 0x07ff; /* mask off top bits */ + + PRINTK2 ("RCV: STATUS %4x LENGTH %4x\n", status, packet_length); + + if (!(status & FRAME_FILTER)) { + /* Adjust for having already read the first two words */ + packet_length -= 4; /*4; */ + + + /* set odd length for bug in LAN91C111, */ + /* which never sets RS_ODDFRAME */ + /* TODO ? */ + + +#ifdef USE_32_BIT + PRINTK3 (" Reading %d dwords (and %d bytes) \n", + packet_length >> 2, packet_length & 3); + /* QUESTION: Like in the TX routine, do I want + to send the DWORDs or the bytes first, or some + mixture. A mixture might improve already slow PIO + performance */ + SMC_insl(dev, LAN91C96_DATA_HIGH, NetRxPackets[0], + packet_length >> 2); + /* read the left over bytes */ + if (packet_length & 3) { + int i; + + byte *tail = (byte *) (NetRxPackets[0] + (packet_length & ~3)); + dword leftover = SMC_inl(dev, LAN91C96_DATA_HIGH); + + for (i = 0; i < (packet_length & 3); i++) + *tail++ = (byte) (leftover >> (8 * i)) & 0xff; + } +#else + PRINTK3 (" Reading %d words and %d byte(s) \n", + (packet_length >> 1), packet_length & 1); + SMC_insw(dev, LAN91C96_DATA_HIGH, NetRxPackets[0], + packet_length >> 1); + +#endif /* USE_32_BIT */ + +#if SMC_DEBUG > 2 + printf ("Receiving Packet\n"); + print_packet((byte *)NetRxPackets[0], packet_length); +#endif + } else { + /* error ... */ + /* TODO ? */ + is_error = 1; + } + + while (SMC_inw(dev, LAN91C96_MMU) & LAN91C96_MMUCR_NO_BUSY) + udelay (1); /* Wait until not busy */ + + /* error or good, tell the card to get rid of this packet */ + SMC_outw(dev, LAN91C96_MMUCR_RELEASE_RX, LAN91C96_MMU); + + while (SMC_inw(dev, LAN91C96_MMU) & LAN91C96_MMUCR_NO_BUSY) + udelay (1); /* Wait until not busy */ + + if (!is_error) { + /* Pass the packet up to the protocol layers. */ + NetReceive (NetRxPackets[0], packet_length); + return packet_length; + } else { + return 0; + } + +} + +/*---------------------------------------------------- + * smc_close + * + * this makes the board clean up everything that it can + * and not talk to the outside world. Caused by + * an 'ifconfig ethX down' + * + -----------------------------------------------------*/ +static int smc_close(struct eth_device *dev) +{ + PRINTK2("%s:smc_close\n", dev->name); + + /* clear everything */ + smc_shutdown(dev); + + return 0; +} + +#if SMC_DEBUG > 2 +static void print_packet(byte *buf, int length) +{ +#if 0 + int i; + int remainder; + int lines; + + printf ("Packet of length %d \n", length); + + lines = length / 16; + remainder = length % 16; + + for (i = 0; i < lines; i++) { + int cur; + + for (cur = 0; cur < 8; cur++) { + byte a, b; + + a = *(buf++); + b = *(buf++); + printf ("%02x%02x ", a, b); + } + printf ("\n"); + } + for (i = 0; i < remainder / 2; i++) { + byte a, b; + + a = *(buf++); + b = *(buf++); + printf ("%02x%02x ", a, b); + } + printf ("\n"); +#endif /* 0 */ +} +#endif /* SMC_DEBUG > 2 */ + +static int lan91c96_init(struct eth_device *dev, bd_t *bd) +{ + return smc_open(bd, dev); +} + +static void lan91c96_halt(struct eth_device *dev) +{ + smc_close(dev); +} + +static int lan91c96_recv(struct eth_device *dev) +{ + return smc_rcv(dev); +} + +static int lan91c96_send(struct eth_device *dev, void *packet, + int length) +{ + return smc_send_packet(dev, packet, length); +} + +/* smc_get_ethaddr + * + * This checks both the environment and the ROM for an ethernet address. If + * found, the environment takes precedence. + */ + +static int smc_get_ethaddr(bd_t *bd, struct eth_device *dev) +{ + uchar v_mac[6]; + + if (!eth_getenv_enetaddr("ethaddr", v_mac)) { + /* get ROM mac value if any */ + if (!get_rom_mac(dev, v_mac)) { + printf("\n*** ERROR: ethaddr is NOT set !!\n"); + return -1; + } + eth_setenv_enetaddr("ethaddr", v_mac); + } + + smc_set_mac_addr(v_mac); /* use old function to update smc default */ + PRINTK("Using MAC Address %pM\n", v_mac); + return 0; +} + +/* + * get_rom_mac() + * Note, this has omly been tested for the OMAP730 P2. + */ + +static int get_rom_mac(struct eth_device *dev, unsigned char *v_rom_mac) +{ +#ifdef HARDCODE_MAC /* used for testing or to supress run time warnings */ + char hw_mac_addr[] = { 0x02, 0x80, 0xad, 0x20, 0x31, 0xb8 }; + + memcpy (v_rom_mac, hw_mac_addr, 6); + return (1); +#else + int i; + SMC_SELECT_BANK(dev, 1); + for (i=0; i<6; i++) + { + v_rom_mac[i] = SMC_inb(dev, LAN91C96_IA0 + i); + } + return (1); +#endif +} + +/* Structure to detect the device IDs */ +struct id_type { + u8 id; + char *name; +}; +static struct id_type supported_chips[] = { + {0, ""}, /* Dummy entry to prevent id check failure */ + {9, "LAN91C110"}, + {8, "LAN91C100FD"}, + {7, "LAN91C100"}, + {5, "LAN91C95"}, + {4, "LAN91C94/96"}, + {3, "LAN91C90/92"}, +}; +/* lan91c96_detect_chip + * See: + * http://www.embeddedsys.com/subpages/resources/images/documents/LAN91C96_datasheet.pdf + * page 71 - that is the closest we get to detect this device + */ +static int lan91c96_detect_chip(struct eth_device *dev) +{ + u8 chip_id; + int r; + SMC_SELECT_BANK(dev, 3); + chip_id = (SMC_inw(dev, 0xA) & LAN91C96_REV_CHIPID) >> 4; + SMC_SELECT_BANK(dev, 0); + for (r = 0; r < ARRAY_SIZE(supported_chips); r++) + if (chip_id == supported_chips[r].id) + return r; + return 0; +} + +int lan91c96_initialize(u8 dev_num, int base_addr) +{ + struct eth_device *dev; + int r = 0; + + dev = malloc(sizeof(*dev)); + if (!dev) { + return 0; + } + memset(dev, 0, sizeof(*dev)); + + dev->iobase = base_addr; + + /* Try to detect chip. Will fail if not present. */ + r = lan91c96_detect_chip(dev); + if (!r) { + free(dev); + return 0; + } + get_rom_mac(dev, dev->enetaddr); + + dev->init = lan91c96_init; + dev->halt = lan91c96_halt; + dev->send = lan91c96_send; + dev->recv = lan91c96_recv; + sprintf(dev->name, "%s-%hu", supported_chips[r].name, dev_num); + + eth_register(dev); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/lan91c96.h b/qemu/roms/u-boot/drivers/net/lan91c96.h new file mode 100644 index 000000000..3e914ce5a --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/lan91c96.h @@ -0,0 +1,617 @@ +/*------------------------------------------------------------------------ + * lan91c96.h + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Rolf Offermanns + * Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + * Developed by Simple Network Magic Corporation (SNMC) + * Copyright (C) 1996 by Erik Stahlman (ES) + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This file contains register information and access macros for + * the LAN91C96 single chip ethernet controller. It is a modified + * version of the smc9111.h file. + * + * Information contained in this file was obtained from the LAN91C96 + * manual from SMC. To get a copy, if you really want one, you can find + * information under www.smsc.com. + * + * Authors + * Erik Stahlman ( erik@vt.edu ) + * Daris A Nevil ( dnevil@snmc.com ) + * + * History + * 04/30/03 Mathijs Haarman Modified smc91111.h (u-boot version) + * for lan91c96 + *------------------------------------------------------------------------- + */ +#ifndef _LAN91C96_H_ +#define _LAN91C96_H_ + +#include +#include +#include + +/* I want some simple types */ + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long int dword; + +/* + * DEBUGGING LEVELS + * + * 0 for normal operation + * 1 for slightly more details + * >2 for various levels of increasingly useless information + * 2 for interrupt tracking, status flags + * 3 for packet info + * 4 for complete packet dumps + */ +/*#define SMC_DEBUG 0 */ + +/* Because of bank switching, the LAN91xxx uses only 16 I/O ports */ + +#define SMC_IO_EXTENT 16 + +#ifdef CONFIG_CPU_PXA25X + +#define SMC_IO_SHIFT 0 + +#define SMCREG(edev, r) ((edev)->iobase+((r)<>= 8; \ + else __v &= 0xff; \ + __v; }) + +#define SMC_outl(edev, d, r) (*((volatile dword *)SMCREG(edev, r)) = d) +#define SMC_outw(edev, d, r) (*((volatile word *)SMCREG(edev, r)) = d) +#define SMC_outb(edev, d, r) ({ word __d = (byte)(d); \ + word __w = SMC_inw(edev, (r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw(edev, __w, (r)&~1); \ + }) + +#define SMC_outsl(edev, r, b, l) ({ int __i; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outl(edev, *(__b2 + __i),\ + r); \ + } \ + }) + +#define SMC_outsw(edev, r, b, l) ({ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw(edev, *(__b2 + __i),\ + r); \ + } \ + }) + +#define SMC_insl(edev, r, b, l) ({ int __i ; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inl(edev,\ + r); \ + SMC_inl(edev, 0); \ + }; \ + }) + +#define SMC_insw(edev, r, b, l) ({ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw(edev,\ + r); \ + SMC_inw(edev, 0); \ + }; \ + }) + +#define SMC_insb(edev, r, b, l) ({ int __i ; \ + byte *__b2; \ + __b2 = (byte *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inb(edev,\ + r); \ + SMC_inb(edev, 0); \ + }; \ + }) + +#else /* if not CONFIG_CPU_PXA25X */ + +/* + * We have only 16 Bit PCMCIA access on Socket 0 + */ + +#define SMC_inw(edev, r) (*((volatile word *)((edev)->iobase+(r)))) +#define SMC_inb(edev, r) (((r)&1) ? SMC_inw(edev, (r)&~1)>>8 :\ + SMC_inw(edev, r)&0xFF) + +#define SMC_outw(edev, d, r) (*((volatile word *)((edev)->iobase+(r))) = d) +#define SMC_outb(edev, d, r) ({ word __d = (byte)(d); \ + word __w = SMC_inw(edev, (r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw(edev, __w, (r)&~1); \ + }) +#define SMC_outsw(edev, r, b, l) ({ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw(edev, *(__b2 + __i),\ + r); \ + } \ + }) + +#define SMC_insw(edev, r, b, l) ({ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw(edev,\ + r); \ + SMC_inw(edev, 0); \ + }; \ + }) + +#endif + +/* + **************************************************************************** + * Bank Select Field + **************************************************************************** + */ +#define LAN91C96_BANK_SELECT 14 /* Bank Select Register */ +#define LAN91C96_BANKSELECT (0x3UC << 0) +#define BANK0 0x00 +#define BANK1 0x01 +#define BANK2 0x02 +#define BANK3 0x03 +#define BANK4 0x04 + +/* + **************************************************************************** + * EEPROM Addresses. + **************************************************************************** + */ +#define EEPROM_MAC_OFFSET_1 0x6020 +#define EEPROM_MAC_OFFSET_2 0x6021 +#define EEPROM_MAC_OFFSET_3 0x6022 + +/* + **************************************************************************** + * Bank 0 Register Map in I/O Space + **************************************************************************** + */ +#define LAN91C96_TCR 0 /* Transmit Control Register */ +#define LAN91C96_EPH_STATUS 2 /* EPH Status Register */ +#define LAN91C96_RCR 4 /* Receive Control Register */ +#define LAN91C96_COUNTER 6 /* Counter Register */ +#define LAN91C96_MIR 8 /* Memory Information Register */ +#define LAN91C96_MCR 10 /* Memory Configuration Register */ + +/* + **************************************************************************** + * Transmit Control Register - Bank 0 - Offset 0 + **************************************************************************** + */ +#define LAN91C96_TCR_TXENA (0x1U << 0) +#define LAN91C96_TCR_LOOP (0x1U << 1) +#define LAN91C96_TCR_FORCOL (0x1U << 2) +#define LAN91C96_TCR_TXP_EN (0x1U << 3) +#define LAN91C96_TCR_PAD_EN (0x1U << 7) +#define LAN91C96_TCR_NOCRC (0x1U << 8) +#define LAN91C96_TCR_MON_CSN (0x1U << 10) +#define LAN91C96_TCR_FDUPLX (0x1U << 11) +#define LAN91C96_TCR_STP_SQET (0x1U << 12) +#define LAN91C96_TCR_EPH_LOOP (0x1U << 13) +#define LAN91C96_TCR_ETEN_TYPE (0x1U << 14) +#define LAN91C96_TCR_FDSE (0x1U << 15) + +/* + **************************************************************************** + * EPH Status Register - Bank 0 - Offset 2 + **************************************************************************** + */ +#define LAN91C96_EPHSR_TX_SUC (0x1U << 0) +#define LAN91C96_EPHSR_SNGL_COL (0x1U << 1) +#define LAN91C96_EPHSR_MUL_COL (0x1U << 2) +#define LAN91C96_EPHSR_LTX_MULT (0x1U << 3) +#define LAN91C96_EPHSR_16COL (0x1U << 4) +#define LAN91C96_EPHSR_SQET (0x1U << 5) +#define LAN91C96_EPHSR_LTX_BRD (0x1U << 6) +#define LAN91C96_EPHSR_TX_DEFR (0x1U << 7) +#define LAN91C96_EPHSR_WAKEUP (0x1U << 8) +#define LAN91C96_EPHSR_LATCOL (0x1U << 9) +#define LAN91C96_EPHSR_LOST_CARR (0x1U << 10) +#define LAN91C96_EPHSR_EXC_DEF (0x1U << 11) +#define LAN91C96_EPHSR_CTR_ROL (0x1U << 12) + +#define LAN91C96_EPHSR_LINK_OK (0x1U << 14) +#define LAN91C96_EPHSR_TX_UNRN (0x1U << 15) + +#define LAN91C96_EPHSR_ERRORS (LAN91C96_EPHSR_SNGL_COL | \ + LAN91C96_EPHSR_MUL_COL | \ + LAN91C96_EPHSR_16COL | \ + LAN91C96_EPHSR_SQET | \ + LAN91C96_EPHSR_TX_DEFR | \ + LAN91C96_EPHSR_LATCOL | \ + LAN91C96_EPHSR_LOST_CARR | \ + LAN91C96_EPHSR_EXC_DEF | \ + LAN91C96_EPHSR_LINK_OK | \ + LAN91C96_EPHSR_TX_UNRN) + +/* + **************************************************************************** + * Receive Control Register - Bank 0 - Offset 4 + **************************************************************************** + */ +#define LAN91C96_RCR_RX_ABORT (0x1U << 0) +#define LAN91C96_RCR_PRMS (0x1U << 1) +#define LAN91C96_RCR_ALMUL (0x1U << 2) +#define LAN91C96_RCR_RXEN (0x1U << 8) +#define LAN91C96_RCR_STRIP_CRC (0x1U << 9) +#define LAN91C96_RCR_FILT_CAR (0x1U << 14) +#define LAN91C96_RCR_SOFT_RST (0x1U << 15) + +/* + **************************************************************************** + * Counter Register - Bank 0 - Offset 6 + **************************************************************************** + */ +#define LAN91C96_ECR_SNGL_COL (0xFU << 0) +#define LAN91C96_ECR_MULT_COL (0xFU << 5) +#define LAN91C96_ECR_DEF_TX (0xFU << 8) +#define LAN91C96_ECR_EXC_DEF_TX (0xFU << 12) + +/* + **************************************************************************** + * Memory Information Register - Bank 0 - OFfset 8 + **************************************************************************** + */ +#define LAN91C96_MIR_SIZE (0x18 << 0) /* 6144 bytes */ + +/* + **************************************************************************** + * Memory Configuration Register - Bank 0 - Offset 10 + **************************************************************************** + */ +#define LAN91C96_MCR_MEM_RES (0xFFU << 0) +#define LAN91C96_MCR_MEM_MULT (0x3U << 9) +#define LAN91C96_MCR_HIGH_ID (0x3U << 12) + +#define LAN91C96_MCR_TRANSMIT_PAGES 0x6 + +/* + **************************************************************************** + * Bank 1 Register Map in I/O Space + **************************************************************************** + */ +#define LAN91C96_CONFIG 0 /* Configuration Register */ +#define LAN91C96_BASE 2 /* Base Address Register */ +#define LAN91C96_IA0 4 /* Individual Address Register - 0 */ +#define LAN91C96_IA1 5 /* Individual Address Register - 1 */ +#define LAN91C96_IA2 6 /* Individual Address Register - 2 */ +#define LAN91C96_IA3 7 /* Individual Address Register - 3 */ +#define LAN91C96_IA4 8 /* Individual Address Register - 4 */ +#define LAN91C96_IA5 9 /* Individual Address Register - 5 */ +#define LAN91C96_GEN_PURPOSE 10 /* General Address Registers */ +#define LAN91C96_CONTROL 12 /* Control Register */ + +/* + **************************************************************************** + * Configuration Register - Bank 1 - Offset 0 + **************************************************************************** + */ +#define LAN91C96_CR_INT_SEL0 (0x1U << 1) +#define LAN91C96_CR_INT_SEL1 (0x1U << 2) +#define LAN91C96_CR_RES (0x3U << 3) +#define LAN91C96_CR_DIS_LINK (0x1U << 6) +#define LAN91C96_CR_16BIT (0x1U << 7) +#define LAN91C96_CR_AUI_SELECT (0x1U << 8) +#define LAN91C96_CR_SET_SQLCH (0x1U << 9) +#define LAN91C96_CR_FULL_STEP (0x1U << 10) +#define LAN91C96_CR_NO_WAIT (0x1U << 12) + +/* + **************************************************************************** + * Base Address Register - Bank 1 - Offset 2 + **************************************************************************** + */ +#define LAN91C96_BAR_RA_BITS (0x27U << 0) +#define LAN91C96_BAR_ROM_SIZE (0x1U << 6) +#define LAN91C96_BAR_A_BITS (0xFFU << 8) + +/* + **************************************************************************** + * Control Register - Bank 1 - Offset 12 + **************************************************************************** + */ +#define LAN91C96_CTR_STORE (0x1U << 0) +#define LAN91C96_CTR_RELOAD (0x1U << 1) +#define LAN91C96_CTR_EEPROM (0x1U << 2) +#define LAN91C96_CTR_TE_ENABLE (0x1U << 5) +#define LAN91C96_CTR_CR_ENABLE (0x1U << 6) +#define LAN91C96_CTR_LE_ENABLE (0x1U << 7) +#define LAN91C96_CTR_BIT_8 (0x1U << 8) +#define LAN91C96_CTR_AUTO_RELEASE (0x1U << 11) +#define LAN91C96_CTR_WAKEUP_EN (0x1U << 12) +#define LAN91C96_CTR_PWRDN (0x1U << 13) +#define LAN91C96_CTR_RCV_BAD (0x1U << 14) + +/* + **************************************************************************** + * Bank 2 Register Map in I/O Space + **************************************************************************** + */ +#define LAN91C96_MMU 0 /* MMU Command Register */ +#define LAN91C96_AUTO_TX_START 1 /* Auto Tx Start Register */ +#define LAN91C96_PNR 2 /* Packet Number Register */ +#define LAN91C96_ARR 3 /* Allocation Result Register */ +#define LAN91C96_FIFO 4 /* FIFO Ports Register */ +#define LAN91C96_POINTER 6 /* Pointer Register */ +#define LAN91C96_DATA_HIGH 8 /* Data High Register */ +#define LAN91C96_DATA_LOW 10 /* Data Low Register */ +#define LAN91C96_INT_STATS 12 /* Interrupt Status Register - RO */ +#define LAN91C96_INT_ACK 12 /* Interrupt Acknowledge Register -WO */ +#define LAN91C96_INT_MASK 13 /* Interrupt Mask Register */ + +/* + **************************************************************************** + * MMU Command Register - Bank 2 - Offset 0 + **************************************************************************** + */ +#define LAN91C96_MMUCR_NO_BUSY (0x1U << 0) +#define LAN91C96_MMUCR_N1 (0x1U << 1) +#define LAN91C96_MMUCR_N2 (0x1U << 2) +#define LAN91C96_MMUCR_COMMAND (0xFU << 4) +#define LAN91C96_MMUCR_ALLOC_TX (0x2U << 4) /* WXYZ = 0010 */ +#define LAN91C96_MMUCR_RESET_MMU (0x4U << 4) /* WXYZ = 0100 */ +#define LAN91C96_MMUCR_REMOVE_RX (0x6U << 4) /* WXYZ = 0110 */ +#define LAN91C96_MMUCR_REMOVE_TX (0x7U << 4) /* WXYZ = 0111 */ +#define LAN91C96_MMUCR_RELEASE_RX (0x8U << 4) /* WXYZ = 1000 */ +#define LAN91C96_MMUCR_RELEASE_TX (0xAU << 4) /* WXYZ = 1010 */ +#define LAN91C96_MMUCR_ENQUEUE (0xCU << 4) /* WXYZ = 1100 */ +#define LAN91C96_MMUCR_RESET_TX (0xEU << 4) /* WXYZ = 1110 */ + +/* + **************************************************************************** + * Auto Tx Start Register - Bank 2 - Offset 1 + **************************************************************************** + */ +#define LAN91C96_AUTOTX (0xFFU << 0) + +/* + **************************************************************************** + * Packet Number Register - Bank 2 - Offset 2 + **************************************************************************** + */ +#define LAN91C96_PNR_TX (0x1FU << 0) + +/* + **************************************************************************** + * Allocation Result Register - Bank 2 - Offset 3 + **************************************************************************** + */ +#define LAN91C96_ARR_ALLOC_PN (0x7FU << 0) +#define LAN91C96_ARR_FAILED (0x1U << 7) + +/* + **************************************************************************** + * FIFO Ports Register - Bank 2 - Offset 4 + **************************************************************************** + */ +#define LAN91C96_FIFO_TX_DONE_PN (0x1FU << 0) +#define LAN91C96_FIFO_TEMPTY (0x1U << 7) +#define LAN91C96_FIFO_RX_DONE_PN (0x1FU << 8) +#define LAN91C96_FIFO_RXEMPTY (0x1U << 15) + +/* + **************************************************************************** + * Pointer Register - Bank 2 - Offset 6 + **************************************************************************** + */ +#define LAN91C96_PTR_LOW (0xFFU << 0) +#define LAN91C96_PTR_HIGH (0x7U << 8) +#define LAN91C96_PTR_AUTO_TX (0x1U << 11) +#define LAN91C96_PTR_ETEN (0x1U << 12) +#define LAN91C96_PTR_READ (0x1U << 13) +#define LAN91C96_PTR_AUTO_INCR (0x1U << 14) +#define LAN91C96_PTR_RCV (0x1U << 15) + +#define LAN91C96_PTR_RX_FRAME (LAN91C96_PTR_RCV | \ + LAN91C96_PTR_AUTO_INCR | \ + LAN91C96_PTR_READ) + +/* + **************************************************************************** + * Data Register - Bank 2 - Offset 8 + **************************************************************************** + */ +#define LAN91C96_CONTROL_CRC (0x1U << 4) /* CRC bit */ +#define LAN91C96_CONTROL_ODD (0x1U << 5) /* ODD bit */ + +/* + **************************************************************************** + * Interrupt Status Register - Bank 2 - Offset 12 + **************************************************************************** + */ +#define LAN91C96_IST_RCV_INT (0x1U << 0) +#define LAN91C96_IST_TX_INT (0x1U << 1) +#define LAN91C96_IST_TX_EMPTY_INT (0x1U << 2) +#define LAN91C96_IST_ALLOC_INT (0x1U << 3) +#define LAN91C96_IST_RX_OVRN_INT (0x1U << 4) +#define LAN91C96_IST_EPH_INT (0x1U << 5) +#define LAN91C96_IST_ERCV_INT (0x1U << 6) +#define LAN91C96_IST_RX_IDLE_INT (0x1U << 7) + +/* + **************************************************************************** + * Interrupt Acknowledge Register - Bank 2 - Offset 12 + **************************************************************************** + */ +#define LAN91C96_ACK_TX_INT (0x1U << 1) +#define LAN91C96_ACK_TX_EMPTY_INT (0x1U << 2) +#define LAN91C96_ACK_RX_OVRN_INT (0x1U << 4) +#define LAN91C96_ACK_ERCV_INT (0x1U << 6) + +/* + **************************************************************************** + * Interrupt Mask Register - Bank 2 - Offset 13 + **************************************************************************** + */ +#define LAN91C96_MSK_RCV_INT (0x1U << 0) +#define LAN91C96_MSK_TX_INT (0x1U << 1) +#define LAN91C96_MSK_TX_EMPTY_INT (0x1U << 2) +#define LAN91C96_MSK_ALLOC_INT (0x1U << 3) +#define LAN91C96_MSK_RX_OVRN_INT (0x1U << 4) +#define LAN91C96_MSK_EPH_INT (0x1U << 5) +#define LAN91C96_MSK_ERCV_INT (0x1U << 6) +#define LAN91C96_MSK_TX_IDLE_INT (0x1U << 7) + +/* + **************************************************************************** + * Bank 3 Register Map in I/O Space + ************************************************************************** + */ +#define LAN91C96_MGMT_MDO (0x1U << 0) +#define LAN91C96_MGMT_MDI (0x1U << 1) +#define LAN91C96_MGMT_MCLK (0x1U << 2) +#define LAN91C96_MGMT_MDOE (0x1U << 3) +#define LAN91C96_MGMT_LOW_ID (0x3U << 4) +#define LAN91C96_MGMT_IOS0 (0x1U << 8) +#define LAN91C96_MGMT_IOS1 (0x1U << 9) +#define LAN91C96_MGMT_IOS2 (0x1U << 10) +#define LAN91C96_MGMT_nXNDEC (0x1U << 11) +#define LAN91C96_MGMT_HIGH_ID (0x3U << 12) + +/* + **************************************************************************** + * Revision Register - Bank 3 - Offset 10 + **************************************************************************** + */ +#define LAN91C96_REV_REVID (0xFU << 0) +#define LAN91C96_REV_CHIPID (0xFU << 4) + +/* + **************************************************************************** + * Early RCV Register - Bank 3 - Offset 12 + **************************************************************************** + */ +#define LAN91C96_ERCV_THRESHOLD (0x1FU << 0) +#define LAN91C96_ERCV_RCV_DISCRD (0x1U << 7) + +/* + **************************************************************************** + * PCMCIA Configuration Registers + **************************************************************************** + */ +#define LAN91C96_ECOR 0x8000 /* Ethernet Configuration Register */ +#define LAN91C96_ECSR 0x8002 /* Ethernet Configuration and Status */ + +/* + **************************************************************************** + * PCMCIA Ethernet Configuration Option Register (ECOR) + **************************************************************************** + */ +#define LAN91C96_ECOR_ENABLE (0x1U << 0) +#define LAN91C96_ECOR_WR_ATTRIB (0x1U << 2) +#define LAN91C96_ECOR_LEVEL_REQ (0x1U << 6) +#define LAN91C96_ECOR_SRESET (0x1U << 7) + +/* + **************************************************************************** + * PCMCIA Ethernet Configuration and Status Register (ECSR) + **************************************************************************** + */ +#define LAN91C96_ECSR_INTR (0x1U << 1) +#define LAN91C96_ECSR_PWRDWN (0x1U << 2) +#define LAN91C96_ECSR_IOIS8 (0x1U << 5) + +/* + **************************************************************************** + * Receive Frame Status Word - See page 38 of the LAN91C96 specification. + **************************************************************************** + */ +#define LAN91C96_TOO_SHORT (0x1U << 10) +#define LAN91C96_TOO_LONG (0x1U << 11) +#define LAN91C96_ODD_FRM (0x1U << 12) +#define LAN91C96_BAD_CRC (0x1U << 13) +#define LAN91C96_BROD_CAST (0x1U << 14) +#define LAN91C96_ALGN_ERR (0x1U << 15) + +#define FRAME_FILTER (LAN91C96_TOO_SHORT | LAN91C96_TOO_LONG | LAN91C96_BAD_CRC | LAN91C96_ALGN_ERR) + +/* + **************************************************************************** + * Default MAC Address + **************************************************************************** + */ +#define MAC_DEF_HI 0x0800 +#define MAC_DEF_MED 0x3333 +#define MAC_DEF_LO 0x0100 + +/* + **************************************************************************** + * Default I/O Signature - 0x33 + **************************************************************************** + */ +#define LAN91C96_LOW_SIGNATURE (0x33U << 0) +#define LAN91C96_HIGH_SIGNATURE (0x33U << 8) +#define LAN91C96_SIGNATURE (LAN91C96_HIGH_SIGNATURE | LAN91C96_LOW_SIGNATURE) + +#define LAN91C96_MAX_PAGES 6 /* Maximum number of 256 pages. */ +#define ETHERNET_MAX_LENGTH 1514 + + +/*------------------------------------------------------------------------- + * I define some macros to make it easier to do somewhat common + * or slightly complicated, repeated tasks. + *------------------------------------------------------------------------- + */ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(edev, x) { SMC_outw(edev, x, LAN91C96_BANK_SELECT); } + +/* this enables an interrupt in the interrupt mask register */ +#define SMC_ENABLE_INT(edev, x) {\ + unsigned char mask;\ + SMC_SELECT_BANK(edev, 2);\ + mask = SMC_inb(edev, LAN91C96_INT_MASK);\ + mask |= (x);\ + SMC_outb(edev, mask, LAN91C96_INT_MASK); \ +} + +/* this disables an interrupt from the interrupt mask register */ + +#define SMC_DISABLE_INT(edev, x) {\ + unsigned char mask;\ + SMC_SELECT_BANK(edev, 2);\ + mask = SMC_inb(edev, LAN91C96_INT_MASK);\ + mask &= ~(x);\ + SMC_outb(edev, mask, LAN91C96_INT_MASK); \ +} + +/*---------------------------------------------------------------------- + * Define the interrupts that I want to receive from the card + * + * I want: + * LAN91C96_IST_EPH_INT, for nasty errors + * LAN91C96_IST_RCV_INT, for happy received packets + * LAN91C96_IST_RX_OVRN_INT, because I have to kick the receiver + *------------------------------------------------------------------------- + */ +#define SMC_INTERRUPT_MASK (LAN91C96_IST_EPH_INT | LAN91C96_IST_RX_OVRN_INT | LAN91C96_IST_RCV_INT) + +#endif /* _LAN91C96_H_ */ diff --git a/qemu/roms/u-boot/drivers/net/macb.c b/qemu/roms/u-boot/drivers/net/macb.c new file mode 100644 index 000000000..781a272cf --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/macb.c @@ -0,0 +1,702 @@ +/* + * Copyright (C) 2005-2006 Atmel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include + +/* + * The u-boot networking stack is a little weird. It seems like the + * networking core allocates receive buffers up front without any + * regard to the hardware that's supposed to actually receive those + * packets. + * + * The MACB receives packets into 128-byte receive buffers, so the + * buffers allocated by the core isn't very practical to use. We'll + * allocate our own, but we need one such buffer in case a packet + * wraps around the DMA ring so that we have to copy it. + * + * Therefore, define CONFIG_SYS_RX_ETH_BUFFER to 1 in the board-specific + * configuration header. This way, the core allocates one RX buffer + * and one TX buffer, each of which can hold a ethernet packet of + * maximum size. + * + * For some reason, the networking core unconditionally specifies a + * 32-byte packet "alignment" (which really should be called + * "padding"). MACB shouldn't need that, but we'll refrain from any + * core modifications here... + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "macb.h" + +#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096 +#define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128) +#define CONFIG_SYS_MACB_TX_RING_SIZE 16 +#define CONFIG_SYS_MACB_TX_TIMEOUT 1000 +#define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000 + +struct macb_dma_desc { + u32 addr; + u32 ctrl; +}; + +#define RXADDR_USED 0x00000001 +#define RXADDR_WRAP 0x00000002 + +#define RXBUF_FRMLEN_MASK 0x00000fff +#define RXBUF_FRAME_START 0x00004000 +#define RXBUF_FRAME_END 0x00008000 +#define RXBUF_TYPEID_MATCH 0x00400000 +#define RXBUF_ADDR4_MATCH 0x00800000 +#define RXBUF_ADDR3_MATCH 0x01000000 +#define RXBUF_ADDR2_MATCH 0x02000000 +#define RXBUF_ADDR1_MATCH 0x04000000 +#define RXBUF_BROADCAST 0x80000000 + +#define TXBUF_FRMLEN_MASK 0x000007ff +#define TXBUF_FRAME_END 0x00008000 +#define TXBUF_NOCRC 0x00010000 +#define TXBUF_EXHAUSTED 0x08000000 +#define TXBUF_UNDERRUN 0x10000000 +#define TXBUF_MAXRETRY 0x20000000 +#define TXBUF_WRAP 0x40000000 +#define TXBUF_USED 0x80000000 + +struct macb_device { + void *regs; + + unsigned int rx_tail; + unsigned int tx_head; + unsigned int tx_tail; + + void *rx_buffer; + void *tx_buffer; + struct macb_dma_desc *rx_ring; + struct macb_dma_desc *tx_ring; + + unsigned long rx_buffer_dma; + unsigned long rx_ring_dma; + unsigned long tx_ring_dma; + + const struct device *dev; + struct eth_device netdev; + unsigned short phy_addr; + struct mii_dev *bus; +}; +#define to_macb(_nd) container_of(_nd, struct macb_device, netdev) + +static int macb_is_gem(struct macb_device *macb) +{ + return MACB_BFEXT(IDNUM, macb_readl(macb, MID)) == 0x2; +} + +static void macb_mdio_write(struct macb_device *macb, u8 reg, u16 value) +{ + unsigned long netctl; + unsigned long netstat; + unsigned long frame; + + netctl = macb_readl(macb, NCR); + netctl |= MACB_BIT(MPE); + macb_writel(macb, NCR, netctl); + + frame = (MACB_BF(SOF, 1) + | MACB_BF(RW, 1) + | MACB_BF(PHYA, macb->phy_addr) + | MACB_BF(REGA, reg) + | MACB_BF(CODE, 2) + | MACB_BF(DATA, value)); + macb_writel(macb, MAN, frame); + + do { + netstat = macb_readl(macb, NSR); + } while (!(netstat & MACB_BIT(IDLE))); + + netctl = macb_readl(macb, NCR); + netctl &= ~MACB_BIT(MPE); + macb_writel(macb, NCR, netctl); +} + +static u16 macb_mdio_read(struct macb_device *macb, u8 reg) +{ + unsigned long netctl; + unsigned long netstat; + unsigned long frame; + + netctl = macb_readl(macb, NCR); + netctl |= MACB_BIT(MPE); + macb_writel(macb, NCR, netctl); + + frame = (MACB_BF(SOF, 1) + | MACB_BF(RW, 2) + | MACB_BF(PHYA, macb->phy_addr) + | MACB_BF(REGA, reg) + | MACB_BF(CODE, 2)); + macb_writel(macb, MAN, frame); + + do { + netstat = macb_readl(macb, NSR); + } while (!(netstat & MACB_BIT(IDLE))); + + frame = macb_readl(macb, MAN); + + netctl = macb_readl(macb, NCR); + netctl &= ~MACB_BIT(MPE); + macb_writel(macb, NCR, netctl); + + return MACB_BFEXT(DATA, frame); +} + +void __weak arch_get_mdio_control(const char *name) +{ + return; +} + +#if defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) + +int macb_miiphy_read(const char *devname, u8 phy_adr, u8 reg, u16 *value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct macb_device *macb = to_macb(dev); + + if ( macb->phy_addr != phy_adr ) + return -1; + + arch_get_mdio_control(devname); + *value = macb_mdio_read(macb, reg); + + return 0; +} + +int macb_miiphy_write(const char *devname, u8 phy_adr, u8 reg, u16 value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct macb_device *macb = to_macb(dev); + + if ( macb->phy_addr != phy_adr ) + return -1; + + arch_get_mdio_control(devname); + macb_mdio_write(macb, reg, value); + + return 0; +} +#endif + + +#if defined(CONFIG_CMD_NET) + +static int macb_send(struct eth_device *netdev, void *packet, int length) +{ + struct macb_device *macb = to_macb(netdev); + unsigned long paddr, ctrl; + unsigned int tx_head = macb->tx_head; + int i; + + paddr = dma_map_single(packet, length, DMA_TO_DEVICE); + + ctrl = length & TXBUF_FRMLEN_MASK; + ctrl |= TXBUF_FRAME_END; + if (tx_head == (CONFIG_SYS_MACB_TX_RING_SIZE - 1)) { + ctrl |= TXBUF_WRAP; + macb->tx_head = 0; + } else + macb->tx_head++; + + macb->tx_ring[tx_head].ctrl = ctrl; + macb->tx_ring[tx_head].addr = paddr; + barrier(); + macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART)); + + /* + * I guess this is necessary because the networking core may + * re-use the transmit buffer as soon as we return... + */ + for (i = 0; i <= CONFIG_SYS_MACB_TX_TIMEOUT; i++) { + barrier(); + ctrl = macb->tx_ring[tx_head].ctrl; + if (ctrl & TXBUF_USED) + break; + udelay(1); + } + + dma_unmap_single(packet, length, paddr); + + if (i <= CONFIG_SYS_MACB_TX_TIMEOUT) { + if (ctrl & TXBUF_UNDERRUN) + printf("%s: TX underrun\n", netdev->name); + if (ctrl & TXBUF_EXHAUSTED) + printf("%s: TX buffers exhausted in mid frame\n", + netdev->name); + } else { + printf("%s: TX timeout\n", netdev->name); + } + + /* No one cares anyway */ + return 0; +} + +static void reclaim_rx_buffers(struct macb_device *macb, + unsigned int new_tail) +{ + unsigned int i; + + i = macb->rx_tail; + while (i > new_tail) { + macb->rx_ring[i].addr &= ~RXADDR_USED; + i++; + if (i > CONFIG_SYS_MACB_RX_RING_SIZE) + i = 0; + } + + while (i < new_tail) { + macb->rx_ring[i].addr &= ~RXADDR_USED; + i++; + } + + barrier(); + macb->rx_tail = new_tail; +} + +static int macb_recv(struct eth_device *netdev) +{ + struct macb_device *macb = to_macb(netdev); + unsigned int rx_tail = macb->rx_tail; + void *buffer; + int length; + int wrapped = 0; + u32 status; + + for (;;) { + if (!(macb->rx_ring[rx_tail].addr & RXADDR_USED)) + return -1; + + status = macb->rx_ring[rx_tail].ctrl; + if (status & RXBUF_FRAME_START) { + if (rx_tail != macb->rx_tail) + reclaim_rx_buffers(macb, rx_tail); + wrapped = 0; + } + + if (status & RXBUF_FRAME_END) { + buffer = macb->rx_buffer + 128 * macb->rx_tail; + length = status & RXBUF_FRMLEN_MASK; + if (wrapped) { + unsigned int headlen, taillen; + + headlen = 128 * (CONFIG_SYS_MACB_RX_RING_SIZE + - macb->rx_tail); + taillen = length - headlen; + memcpy((void *)NetRxPackets[0], + buffer, headlen); + memcpy((void *)NetRxPackets[0] + headlen, + macb->rx_buffer, taillen); + buffer = (void *)NetRxPackets[0]; + } + + NetReceive(buffer, length); + if (++rx_tail >= CONFIG_SYS_MACB_RX_RING_SIZE) + rx_tail = 0; + reclaim_rx_buffers(macb, rx_tail); + } else { + if (++rx_tail >= CONFIG_SYS_MACB_RX_RING_SIZE) { + wrapped = 1; + rx_tail = 0; + } + } + barrier(); + } + + return 0; +} + +static void macb_phy_reset(struct macb_device *macb) +{ + struct eth_device *netdev = &macb->netdev; + int i; + u16 status, adv; + + adv = ADVERTISE_CSMA | ADVERTISE_ALL; + macb_mdio_write(macb, MII_ADVERTISE, adv); + printf("%s: Starting autonegotiation...\n", netdev->name); + macb_mdio_write(macb, MII_BMCR, (BMCR_ANENABLE + | BMCR_ANRESTART)); + + for (i = 0; i < CONFIG_SYS_MACB_AUTONEG_TIMEOUT / 100; i++) { + status = macb_mdio_read(macb, MII_BMSR); + if (status & BMSR_ANEGCOMPLETE) + break; + udelay(100); + } + + if (status & BMSR_ANEGCOMPLETE) + printf("%s: Autonegotiation complete\n", netdev->name); + else + printf("%s: Autonegotiation timed out (status=0x%04x)\n", + netdev->name, status); +} + +#ifdef CONFIG_MACB_SEARCH_PHY +static int macb_phy_find(struct macb_device *macb) +{ + int i; + u16 phy_id; + + /* Search for PHY... */ + for (i = 0; i < 32; i++) { + macb->phy_addr = i; + phy_id = macb_mdio_read(macb, MII_PHYSID1); + if (phy_id != 0xffff) { + printf("%s: PHY present at %d\n", macb->netdev.name, i); + return 1; + } + } + + /* PHY isn't up to snuff */ + printf("%s: PHY not found\n", macb->netdev.name); + + return 0; +} +#endif /* CONFIG_MACB_SEARCH_PHY */ + + +static int macb_phy_init(struct macb_device *macb) +{ + struct eth_device *netdev = &macb->netdev; +#ifdef CONFIG_PHYLIB + struct phy_device *phydev; +#endif + u32 ncfgr; + u16 phy_id, status, adv, lpa; + int media, speed, duplex; + int i; + + arch_get_mdio_control(netdev->name); +#ifdef CONFIG_MACB_SEARCH_PHY + /* Auto-detect phy_addr */ + if (!macb_phy_find(macb)) { + return 0; + } +#endif /* CONFIG_MACB_SEARCH_PHY */ + + /* Check if the PHY is up to snuff... */ + phy_id = macb_mdio_read(macb, MII_PHYSID1); + if (phy_id == 0xffff) { + printf("%s: No PHY present\n", netdev->name); + return 0; + } + +#ifdef CONFIG_PHYLIB + /* need to consider other phy interface mode */ + phydev = phy_connect(macb->bus, macb->phy_addr, netdev, + PHY_INTERFACE_MODE_RGMII); + if (!phydev) { + printf("phy_connect failed\n"); + return -ENODEV; + } + + phy_config(phydev); +#endif + + status = macb_mdio_read(macb, MII_BMSR); + if (!(status & BMSR_LSTATUS)) { + /* Try to re-negotiate if we don't have link already. */ + macb_phy_reset(macb); + + for (i = 0; i < CONFIG_SYS_MACB_AUTONEG_TIMEOUT / 100; i++) { + status = macb_mdio_read(macb, MII_BMSR); + if (status & BMSR_LSTATUS) + break; + udelay(100); + } + } + + if (!(status & BMSR_LSTATUS)) { + printf("%s: link down (status: 0x%04x)\n", + netdev->name, status); + return 0; + } + + /* First check for GMAC */ + if (macb_is_gem(macb)) { + lpa = macb_mdio_read(macb, MII_STAT1000); + if (lpa & (1 << 11)) { + speed = 1000; + duplex = 1; + } else { + if (lpa & (1 << 10)) { + speed = 1000; + duplex = 1; + } else { + speed = 0; + } + } + + if (speed == 1000) { + printf("%s: link up, %dMbps %s-duplex (lpa: 0x%04x)\n", + netdev->name, + speed, + duplex ? "full" : "half", + lpa); + + ncfgr = macb_readl(macb, NCFGR); + ncfgr &= ~(GEM_BIT(GBE) | MACB_BIT(SPD) | MACB_BIT(FD)); + if (speed) + ncfgr |= GEM_BIT(GBE); + if (duplex) + ncfgr |= MACB_BIT(FD); + macb_writel(macb, NCFGR, ncfgr); + + return 1; + } + } + + /* fall back for EMAC checking */ + adv = macb_mdio_read(macb, MII_ADVERTISE); + lpa = macb_mdio_read(macb, MII_LPA); + media = mii_nway_result(lpa & adv); + speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) + ? 1 : 0); + duplex = (media & ADVERTISE_FULL) ? 1 : 0; + printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n", + netdev->name, + speed ? "100" : "10", + duplex ? "full" : "half", + lpa); + + ncfgr = macb_readl(macb, NCFGR); + ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); + if (speed) + ncfgr |= MACB_BIT(SPD); + if (duplex) + ncfgr |= MACB_BIT(FD); + macb_writel(macb, NCFGR, ncfgr); + + return 1; +} + +static int macb_init(struct eth_device *netdev, bd_t *bd) +{ + struct macb_device *macb = to_macb(netdev); + unsigned long paddr; + int i; + + /* + * macb_halt should have been called at some point before now, + * so we'll assume the controller is idle. + */ + + /* initialize DMA descriptors */ + paddr = macb->rx_buffer_dma; + for (i = 0; i < CONFIG_SYS_MACB_RX_RING_SIZE; i++) { + if (i == (CONFIG_SYS_MACB_RX_RING_SIZE - 1)) + paddr |= RXADDR_WRAP; + macb->rx_ring[i].addr = paddr; + macb->rx_ring[i].ctrl = 0; + paddr += 128; + } + for (i = 0; i < CONFIG_SYS_MACB_TX_RING_SIZE; i++) { + macb->tx_ring[i].addr = 0; + if (i == (CONFIG_SYS_MACB_TX_RING_SIZE - 1)) + macb->tx_ring[i].ctrl = TXBUF_USED | TXBUF_WRAP; + else + macb->tx_ring[i].ctrl = TXBUF_USED; + } + macb->rx_tail = macb->tx_head = macb->tx_tail = 0; + + macb_writel(macb, RBQP, macb->rx_ring_dma); + macb_writel(macb, TBQP, macb->tx_ring_dma); + + if (macb_is_gem(macb)) { +#ifdef CONFIG_RGMII + gem_writel(macb, UR, GEM_BIT(RGMII)); +#else + gem_writel(macb, UR, 0); +#endif + } else { + /* choose RMII or MII mode. This depends on the board */ +#ifdef CONFIG_RMII +#ifdef CONFIG_AT91FAMILY + macb_writel(macb, USRIO, MACB_BIT(RMII) | MACB_BIT(CLKEN)); +#else + macb_writel(macb, USRIO, 0); +#endif +#else +#ifdef CONFIG_AT91FAMILY + macb_writel(macb, USRIO, MACB_BIT(CLKEN)); +#else + macb_writel(macb, USRIO, MACB_BIT(MII)); +#endif +#endif /* CONFIG_RMII */ + } + + if (!macb_phy_init(macb)) + return -1; + + /* Enable TX and RX */ + macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE)); + + return 0; +} + +static void macb_halt(struct eth_device *netdev) +{ + struct macb_device *macb = to_macb(netdev); + u32 ncr, tsr; + + /* Halt the controller and wait for any ongoing transmission to end. */ + ncr = macb_readl(macb, NCR); + ncr |= MACB_BIT(THALT); + macb_writel(macb, NCR, ncr); + + do { + tsr = macb_readl(macb, TSR); + } while (tsr & MACB_BIT(TGO)); + + /* Disable TX and RX, and clear statistics */ + macb_writel(macb, NCR, MACB_BIT(CLRSTAT)); +} + +static int macb_write_hwaddr(struct eth_device *dev) +{ + struct macb_device *macb = to_macb(dev); + u32 hwaddr_bottom; + u16 hwaddr_top; + + /* set hardware address */ + hwaddr_bottom = dev->enetaddr[0] | dev->enetaddr[1] << 8 | + dev->enetaddr[2] << 16 | dev->enetaddr[3] << 24; + macb_writel(macb, SA1B, hwaddr_bottom); + hwaddr_top = dev->enetaddr[4] | dev->enetaddr[5] << 8; + macb_writel(macb, SA1T, hwaddr_top); + return 0; +} + +static u32 macb_mdc_clk_div(int id, struct macb_device *macb) +{ + u32 config; + unsigned long macb_hz = get_macb_pclk_rate(id); + + if (macb_hz < 20000000) + config = MACB_BF(CLK, MACB_CLK_DIV8); + else if (macb_hz < 40000000) + config = MACB_BF(CLK, MACB_CLK_DIV16); + else if (macb_hz < 80000000) + config = MACB_BF(CLK, MACB_CLK_DIV32); + else + config = MACB_BF(CLK, MACB_CLK_DIV64); + + return config; +} + +static u32 gem_mdc_clk_div(int id, struct macb_device *macb) +{ + u32 config; + unsigned long macb_hz = get_macb_pclk_rate(id); + + if (macb_hz < 20000000) + config = GEM_BF(CLK, GEM_CLK_DIV8); + else if (macb_hz < 40000000) + config = GEM_BF(CLK, GEM_CLK_DIV16); + else if (macb_hz < 80000000) + config = GEM_BF(CLK, GEM_CLK_DIV32); + else if (macb_hz < 120000000) + config = GEM_BF(CLK, GEM_CLK_DIV48); + else if (macb_hz < 160000000) + config = GEM_BF(CLK, GEM_CLK_DIV64); + else + config = GEM_BF(CLK, GEM_CLK_DIV96); + + return config; +} + +/* + * Get the DMA bus width field of the network configuration register that we + * should program. We find the width from decoding the design configuration + * register to find the maximum supported data bus width. + */ +static u32 macb_dbw(struct macb_device *macb) +{ + switch (GEM_BFEXT(DBWDEF, gem_readl(macb, DCFG1))) { + case 4: + return GEM_BF(DBW, GEM_DBW128); + case 2: + return GEM_BF(DBW, GEM_DBW64); + case 1: + default: + return GEM_BF(DBW, GEM_DBW32); + } +} + +int macb_eth_initialize(int id, void *regs, unsigned int phy_addr) +{ + struct macb_device *macb; + struct eth_device *netdev; + u32 ncfgr; + + macb = malloc(sizeof(struct macb_device)); + if (!macb) { + printf("Error: Failed to allocate memory for MACB%d\n", id); + return -1; + } + memset(macb, 0, sizeof(struct macb_device)); + + netdev = &macb->netdev; + + macb->rx_buffer = dma_alloc_coherent(CONFIG_SYS_MACB_RX_BUFFER_SIZE, + &macb->rx_buffer_dma); + macb->rx_ring = dma_alloc_coherent(CONFIG_SYS_MACB_RX_RING_SIZE + * sizeof(struct macb_dma_desc), + &macb->rx_ring_dma); + macb->tx_ring = dma_alloc_coherent(CONFIG_SYS_MACB_TX_RING_SIZE + * sizeof(struct macb_dma_desc), + &macb->tx_ring_dma); + + macb->regs = regs; + macb->phy_addr = phy_addr; + + if (macb_is_gem(macb)) + sprintf(netdev->name, "gmac%d", id); + else + sprintf(netdev->name, "macb%d", id); + + netdev->init = macb_init; + netdev->halt = macb_halt; + netdev->send = macb_send; + netdev->recv = macb_recv; + netdev->write_hwaddr = macb_write_hwaddr; + + /* + * Do some basic initialization so that we at least can talk + * to the PHY + */ + if (macb_is_gem(macb)) { + ncfgr = gem_mdc_clk_div(id, macb); + ncfgr |= macb_dbw(macb); + } else { + ncfgr = macb_mdc_clk_div(id, macb); + } + + macb_writel(macb, NCFGR, ncfgr); + + eth_register(netdev); + +#if defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) + miiphy_register(netdev->name, macb_miiphy_read, macb_miiphy_write); + macb->bus = miiphy_get_dev_by_name(netdev->name); +#endif + return 0; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/net/macb.h b/qemu/roms/u-boot/drivers/net/macb.h new file mode 100644 index 000000000..06f7c66df --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/macb.h @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2005-2006 Atmel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef __DRIVERS_MACB_H__ +#define __DRIVERS_MACB_H__ + +/* MACB register offsets */ +#define MACB_NCR 0x0000 +#define MACB_NCFGR 0x0004 +#define MACB_NSR 0x0008 +#define GEM_UR 0x000c +#define MACB_TSR 0x0014 +#define MACB_RBQP 0x0018 +#define MACB_TBQP 0x001c +#define MACB_RSR 0x0020 +#define MACB_ISR 0x0024 +#define MACB_IER 0x0028 +#define MACB_IDR 0x002c +#define MACB_IMR 0x0030 +#define MACB_MAN 0x0034 +#define MACB_PTR 0x0038 +#define MACB_PFR 0x003c +#define MACB_FTO 0x0040 +#define MACB_SCF 0x0044 +#define MACB_MCF 0x0048 +#define MACB_FRO 0x004c +#define MACB_FCSE 0x0050 +#define MACB_ALE 0x0054 +#define MACB_DTF 0x0058 +#define MACB_LCOL 0x005c +#define MACB_EXCOL 0x0060 +#define MACB_TUND 0x0064 +#define MACB_CSE 0x0068 +#define MACB_RRE 0x006c +#define MACB_ROVR 0x0070 +#define MACB_RSE 0x0074 +#define MACB_ELE 0x0078 +#define MACB_RJA 0x007c +#define MACB_USF 0x0080 +#define MACB_STE 0x0084 +#define MACB_RLE 0x0088 +#define MACB_TPF 0x008c +#define MACB_HRB 0x0090 +#define MACB_HRT 0x0094 +#define MACB_SA1B 0x0098 +#define MACB_SA1T 0x009c +#define MACB_SA2B 0x00a0 +#define MACB_SA2T 0x00a4 +#define MACB_SA3B 0x00a8 +#define MACB_SA3T 0x00ac +#define MACB_SA4B 0x00b0 +#define MACB_SA4T 0x00b4 +#define MACB_TID 0x00b8 +#define MACB_TPQ 0x00bc +#define MACB_USRIO 0x00c0 +#define MACB_WOL 0x00c4 +#define MACB_MID 0x00fc + +/* GEM specific register offsets */ +#define GEM_DCFG1 0x0280 + +/* Bitfields in NCR */ +#define MACB_LB_OFFSET 0 +#define MACB_LB_SIZE 1 +#define MACB_LLB_OFFSET 1 +#define MACB_LLB_SIZE 1 +#define MACB_RE_OFFSET 2 +#define MACB_RE_SIZE 1 +#define MACB_TE_OFFSET 3 +#define MACB_TE_SIZE 1 +#define MACB_MPE_OFFSET 4 +#define MACB_MPE_SIZE 1 +#define MACB_CLRSTAT_OFFSET 5 +#define MACB_CLRSTAT_SIZE 1 +#define MACB_INCSTAT_OFFSET 6 +#define MACB_INCSTAT_SIZE 1 +#define MACB_WESTAT_OFFSET 7 +#define MACB_WESTAT_SIZE 1 +#define MACB_BP_OFFSET 8 +#define MACB_BP_SIZE 1 +#define MACB_TSTART_OFFSET 9 +#define MACB_TSTART_SIZE 1 +#define MACB_THALT_OFFSET 10 +#define MACB_THALT_SIZE 1 +#define MACB_NCR_TPF_OFFSET 11 +#define MACB_NCR_TPF_SIZE 1 +#define MACB_TZQ_OFFSET 12 +#define MACB_TZQ_SIZE 1 + +/* Bitfields in NCFGR */ +#define MACB_SPD_OFFSET 0 +#define MACB_SPD_SIZE 1 +#define MACB_FD_OFFSET 1 +#define MACB_FD_SIZE 1 +#define MACB_BIT_RATE_OFFSET 2 +#define MACB_BIT_RATE_SIZE 1 +#define MACB_JFRAME_OFFSET 3 +#define MACB_JFRAME_SIZE 1 +#define MACB_CAF_OFFSET 4 +#define MACB_CAF_SIZE 1 +#define MACB_NBC_OFFSET 5 +#define MACB_NBC_SIZE 1 +#define MACB_NCFGR_MTI_OFFSET 6 +#define MACB_NCFGR_MTI_SIZE 1 +#define MACB_UNI_OFFSET 7 +#define MACB_UNI_SIZE 1 +#define MACB_BIG_OFFSET 8 +#define MACB_BIG_SIZE 1 +#define MACB_EAE_OFFSET 9 +#define MACB_EAE_SIZE 1 +#define MACB_CLK_OFFSET 10 +#define MACB_CLK_SIZE 2 +#define MACB_RTY_OFFSET 12 +#define MACB_RTY_SIZE 1 +#define MACB_PAE_OFFSET 13 +#define MACB_PAE_SIZE 1 +#define MACB_RBOF_OFFSET 14 +#define MACB_RBOF_SIZE 2 +#define MACB_RLCE_OFFSET 16 +#define MACB_RLCE_SIZE 1 +#define MACB_DRFCS_OFFSET 17 +#define MACB_DRFCS_SIZE 1 +#define MACB_EFRHD_OFFSET 18 +#define MACB_EFRHD_SIZE 1 +#define MACB_IRXFCS_OFFSET 19 +#define MACB_IRXFCS_SIZE 1 + +#define GEM_GBE_OFFSET 10 +#define GEM_GBE_SIZE 1 +#define GEM_CLK_OFFSET 18 +#define GEM_CLK_SIZE 3 +#define GEM_DBW_OFFSET 21 +#define GEM_DBW_SIZE 2 + +/* Bitfields in NSR */ +#define MACB_NSR_LINK_OFFSET 0 +#define MACB_NSR_LINK_SIZE 1 +#define MACB_MDIO_OFFSET 1 +#define MACB_MDIO_SIZE 1 +#define MACB_IDLE_OFFSET 2 +#define MACB_IDLE_SIZE 1 + +/* Bitfields in UR */ +#define GEM_RGMII_OFFSET 0 +#define GEM_RGMII_SIZE 1 + +/* Bitfields in TSR */ +#define MACB_UBR_OFFSET 0 +#define MACB_UBR_SIZE 1 +#define MACB_COL_OFFSET 1 +#define MACB_COL_SIZE 1 +#define MACB_TSR_RLE_OFFSET 2 +#define MACB_TSR_RLE_SIZE 1 +#define MACB_TGO_OFFSET 3 +#define MACB_TGO_SIZE 1 +#define MACB_BEX_OFFSET 4 +#define MACB_BEX_SIZE 1 +#define MACB_COMP_OFFSET 5 +#define MACB_COMP_SIZE 1 +#define MACB_UND_OFFSET 6 +#define MACB_UND_SIZE 1 + +/* Bitfields in RSR */ +#define MACB_BNA_OFFSET 0 +#define MACB_BNA_SIZE 1 +#define MACB_REC_OFFSET 1 +#define MACB_REC_SIZE 1 +#define MACB_OVR_OFFSET 2 +#define MACB_OVR_SIZE 1 + +/* Bitfields in ISR/IER/IDR/IMR */ +#define MACB_MFD_OFFSET 0 +#define MACB_MFD_SIZE 1 +#define MACB_RCOMP_OFFSET 1 +#define MACB_RCOMP_SIZE 1 +#define MACB_RXUBR_OFFSET 2 +#define MACB_RXUBR_SIZE 1 +#define MACB_TXUBR_OFFSET 3 +#define MACB_TXUBR_SIZE 1 +#define MACB_ISR_TUND_OFFSET 4 +#define MACB_ISR_TUND_SIZE 1 +#define MACB_ISR_RLE_OFFSET 5 +#define MACB_ISR_RLE_SIZE 1 +#define MACB_TXERR_OFFSET 6 +#define MACB_TXERR_SIZE 1 +#define MACB_TCOMP_OFFSET 7 +#define MACB_TCOMP_SIZE 1 +#define MACB_ISR_LINK_OFFSET 9 +#define MACB_ISR_LINK_SIZE 1 +#define MACB_ISR_ROVR_OFFSET 10 +#define MACB_ISR_ROVR_SIZE 1 +#define MACB_HRESP_OFFSET 11 +#define MACB_HRESP_SIZE 1 +#define MACB_PFR_OFFSET 12 +#define MACB_PFR_SIZE 1 +#define MACB_PTZ_OFFSET 13 +#define MACB_PTZ_SIZE 1 + +/* Bitfields in MAN */ +#define MACB_DATA_OFFSET 0 +#define MACB_DATA_SIZE 16 +#define MACB_CODE_OFFSET 16 +#define MACB_CODE_SIZE 2 +#define MACB_REGA_OFFSET 18 +#define MACB_REGA_SIZE 5 +#define MACB_PHYA_OFFSET 23 +#define MACB_PHYA_SIZE 5 +#define MACB_RW_OFFSET 28 +#define MACB_RW_SIZE 2 +#define MACB_SOF_OFFSET 30 +#define MACB_SOF_SIZE 2 + +/* Bitfields in USRIO */ +#define MACB_MII_OFFSET 0 +#define MACB_MII_SIZE 1 +#define MACB_EAM_OFFSET 1 +#define MACB_EAM_SIZE 1 +#define MACB_TX_PAUSE_OFFSET 2 +#define MACB_TX_PAUSE_SIZE 1 +#define MACB_TX_PAUSE_ZERO_OFFSET 3 +#define MACB_TX_PAUSE_ZERO_SIZE 1 + +/* Bitfields in USRIO (AT91) */ +#define MACB_RMII_OFFSET 0 +#define MACB_RMII_SIZE 1 +#define MACB_CLKEN_OFFSET 1 +#define MACB_CLKEN_SIZE 1 + +/* Bitfields in WOL */ +#define MACB_IP_OFFSET 0 +#define MACB_IP_SIZE 16 +#define MACB_MAG_OFFSET 16 +#define MACB_MAG_SIZE 1 +#define MACB_ARP_OFFSET 17 +#define MACB_ARP_SIZE 1 +#define MACB_SA1_OFFSET 18 +#define MACB_SA1_SIZE 1 +#define MACB_WOL_MTI_OFFSET 19 +#define MACB_WOL_MTI_SIZE 1 + +/* Bitfields in MID */ +#define MACB_IDNUM_OFFSET 16 +#define MACB_IDNUM_SIZE 16 + +/* Bitfields in DCFG1 */ +#define GEM_DBWDEF_OFFSET 25 +#define GEM_DBWDEF_SIZE 3 + +/* constants for data bus width */ +#define GEM_DBW32 0 +#define GEM_DBW64 1 +#define GEM_DBW128 2 + +/* Constants for CLK */ +#define MACB_CLK_DIV8 0 +#define MACB_CLK_DIV16 1 +#define MACB_CLK_DIV32 2 +#define MACB_CLK_DIV64 3 + +/* GEM specific constants for CLK */ +#define GEM_CLK_DIV8 0 +#define GEM_CLK_DIV16 1 +#define GEM_CLK_DIV32 2 +#define GEM_CLK_DIV48 3 +#define GEM_CLK_DIV64 4 +#define GEM_CLK_DIV96 5 + +/* Constants for MAN register */ +#define MACB_MAN_SOF 1 +#define MACB_MAN_WRITE 1 +#define MACB_MAN_READ 2 +#define MACB_MAN_CODE 2 + +/* Bit manipulation macros */ +#define MACB_BIT(name) \ + (1 << MACB_##name##_OFFSET) +#define MACB_BF(name, value) \ + (((value) & ((1 << MACB_##name##_SIZE) - 1)) \ + << MACB_##name##_OFFSET) +#define MACB_BFEXT(name, value)\ + (((value) >> MACB_##name##_OFFSET) \ + & ((1 << MACB_##name##_SIZE) - 1)) +#define MACB_BFINS(name, value, old) \ + (((old) & ~(((1 << MACB_##name##_SIZE) - 1) \ + << MACB_##name##_OFFSET)) \ + | MACB_BF(name, value)) + +#define GEM_BIT(name) \ + (1 << GEM_##name##_OFFSET) +#define GEM_BF(name, value) \ + (((value) & ((1 << GEM_##name##_SIZE) - 1)) \ + << GEM_##name##_OFFSET) +#define GEM_BFEXT(name, value)\ + (((value) >> GEM_##name##_OFFSET) \ + & ((1 << GEM_##name##_SIZE) - 1)) +#define GEM_BFINS(name, value, old) \ + (((old) & ~(((1 << GEM_##name##_SIZE) - 1) \ + << GEM_##name##_OFFSET)) \ + | GEM_BF(name, value)) + +/* Register access macros */ +#define macb_readl(port, reg) \ + readl((port)->regs + MACB_##reg) +#define macb_writel(port, reg, value) \ + writel((value), (port)->regs + MACB_##reg) +#define gem_readl(port, reg) \ + readl((port)->regs + GEM_##reg) +#define gem_writel(port, reg, value) \ + writel((value), (port)->regs + GEM_##reg) + +#endif /* __DRIVERS_MACB_H__ */ diff --git a/qemu/roms/u-boot/drivers/net/mcffec.c b/qemu/roms/u-boot/drivers/net/mcffec.c new file mode 100644 index 000000000..7c4b210b0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/mcffec.c @@ -0,0 +1,609 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#undef ET_DEBUG +#undef MII_DEBUG + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 +#define TX_BUF_CNT 2 +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 +#define LAST_PKTBUFSRX PKTBUFSRX - 1 +#define BD_ENET_RX_W_E (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY) +#define BD_ENET_TX_RDY_LST (BD_ENET_TX_READY | BD_ENET_TX_LAST) + +DECLARE_GLOBAL_DATA_PTR; + +struct fec_info_s fec_info[] = { +#ifdef CONFIG_SYS_FEC0_IOBASE + { + 0, /* index */ + CONFIG_SYS_FEC0_IOBASE, /* io base */ + CONFIG_SYS_FEC0_PINMUX, /* gpio pin muxing */ + CONFIG_SYS_FEC0_MIIBASE, /* mii base */ + -1, /* phy_addr */ + 0, /* duplex and speed */ + 0, /* phy name */ + 0, /* phyname init */ + 0, /* RX BD */ + 0, /* TX BD */ + 0, /* rx Index */ + 0, /* tx Index */ + 0, /* tx buffer */ + 0, /* initialized flag */ + (struct fec_info_s *)-1, + }, +#endif +#ifdef CONFIG_SYS_FEC1_IOBASE + { + 1, /* index */ + CONFIG_SYS_FEC1_IOBASE, /* io base */ + CONFIG_SYS_FEC1_PINMUX, /* gpio pin muxing */ + CONFIG_SYS_FEC1_MIIBASE, /* mii base */ + -1, /* phy_addr */ + 0, /* duplex and speed */ + 0, /* phy name */ + 0, /* phy name init */ +#ifdef CONFIG_SYS_FEC_BUF_USE_SRAM + (cbd_t *)DBUF_LENGTH, /* RX BD */ +#else + 0, /* RX BD */ +#endif + 0, /* TX BD */ + 0, /* rx Index */ + 0, /* tx Index */ + 0, /* tx buffer */ + 0, /* initialized flag */ + (struct fec_info_s *)-1, + } +#endif +}; + +int fec_recv(struct eth_device *dev); +int fec_init(struct eth_device *dev, bd_t * bd); +void fec_halt(struct eth_device *dev); +void fec_reset(struct eth_device *dev); + +void setFecDuplexSpeed(volatile fec_t * fecp, bd_t * bd, int dup_spd) +{ + if ((dup_spd >> 16) == FULL) { + /* Set maximum frame length */ + fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | FEC_RCR_MII_MODE | + FEC_RCR_PROM | 0x100; + fecp->tcr = FEC_TCR_FDEN; + } else { + /* Half duplex mode */ + fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | + FEC_RCR_MII_MODE | FEC_RCR_DRT; + fecp->tcr &= ~FEC_TCR_FDEN; + } + + if ((dup_spd & 0xFFFF) == _100BASET) { +#ifdef CONFIG_MCF5445x + fecp->rcr &= ~0x200; /* disabled 10T base */ +#endif +#ifdef MII_DEBUG + printf("100Mbps\n"); +#endif + bd->bi_ethspeed = 100; + } else { +#ifdef CONFIG_MCF5445x + fecp->rcr |= 0x200; /* enabled 10T base */ +#endif +#ifdef MII_DEBUG + printf("10Mbps\n"); +#endif + bd->bi_ethspeed = 10; + } +} + +static int fec_send(struct eth_device *dev, void *packet, int length) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + int j, rc; + u16 phyStatus; + + miiphy_read(dev->name, info->phy_addr, MII_BMSR, &phyStatus); + + /* section 16.9.23.3 + * Wait for ready + */ + j = 0; + while ((info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_READY) && + (j < MCFFEC_TOUT_LOOP)) { + udelay(1); + j++; + } + if (j >= MCFFEC_TOUT_LOOP) { + printf("TX not ready\n"); + } + + info->txbd[info->txIdx].cbd_bufaddr = (uint) packet; + info->txbd[info->txIdx].cbd_datlen = length; + info->txbd[info->txIdx].cbd_sc |= BD_ENET_TX_RDY_LST; + + /* Activate transmit Buffer Descriptor polling */ + fecp->tdar = 0x01000000; /* Descriptor polling active */ + +#ifndef CONFIG_SYS_FEC_BUF_USE_SRAM + /* + * FEC unable to initial transmit data packet. + * A nop will ensure the descriptor polling active completed. + * CF Internal RAM has shorter cycle access than DRAM. If use + * DRAM as Buffer descriptor and data, a nop is a must. + * Affect only V2 and V3. + */ + __asm__ ("nop"); + +#endif + +#ifdef CONFIG_SYS_UNIFY_CACHE + icache_invalid(); +#endif + + j = 0; + while ((info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_READY) && + (j < MCFFEC_TOUT_LOOP)) { + udelay(1); + j++; + } + if (j >= MCFFEC_TOUT_LOOP) { + printf("TX timeout\n"); + } + +#ifdef ET_DEBUG + printf("%s[%d] %s: cycles: %d status: %x retry cnt: %d\n", + __FILE__, __LINE__, __FUNCTION__, j, + info->txbd[info->txIdx].cbd_sc, + (info->txbd[info->txIdx].cbd_sc & 0x003C) >> 2); +#endif + + /* return only status bits */ + rc = (info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_STATS); + info->txIdx = (info->txIdx + 1) % TX_BUF_CNT; + + return rc; +} + +int fec_recv(struct eth_device *dev) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + int length; + + for (;;) { +#ifndef CONFIG_SYS_FEC_BUF_USE_SRAM +#endif +#ifdef CONFIG_SYS_UNIFY_CACHE + icache_invalid(); +#endif + /* section 16.9.23.2 */ + if (info->rxbd[info->rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + length = info->rxbd[info->rxIdx].cbd_datlen; + + if (info->rxbd[info->rxIdx].cbd_sc & 0x003f) { + printf("%s[%d] err: %x\n", + __FUNCTION__, __LINE__, + info->rxbd[info->rxIdx].cbd_sc); +#ifdef ET_DEBUG + printf("%s[%d] err: %x\n", + __FUNCTION__, __LINE__, + info->rxbd[info->rxIdx].cbd_sc); +#endif + } else { + + length -= 4; + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[info->rxIdx], length); + + fecp->eir |= FEC_EIR_RXF; + } + + /* Give the buffer back to the FEC. */ + info->rxbd[info->rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if (info->rxIdx == LAST_PKTBUFSRX) { + info->rxbd[PKTBUFSRX - 1].cbd_sc = BD_ENET_RX_W_E; + info->rxIdx = 0; + } else { + info->rxbd[info->rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + info->rxIdx++; + } + + /* Try to fill Buffer Descriptors */ + fecp->rdar = 0x01000000; /* Descriptor polling active */ + } + + return length; +} + +#ifdef ET_DEBUG +void dbgFecRegs(struct eth_device *dev) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + + printf("=====\n"); + printf("ievent %x - %x\n", (int)&fecp->eir, fecp->eir); + printf("imask %x - %x\n", (int)&fecp->eimr, fecp->eimr); + printf("r_des_active %x - %x\n", (int)&fecp->rdar, fecp->rdar); + printf("x_des_active %x - %x\n", (int)&fecp->tdar, fecp->tdar); + printf("ecntrl %x - %x\n", (int)&fecp->ecr, fecp->ecr); + printf("mii_mframe %x - %x\n", (int)&fecp->mmfr, fecp->mmfr); + printf("mii_speed %x - %x\n", (int)&fecp->mscr, fecp->mscr); + printf("mii_ctrlstat %x - %x\n", (int)&fecp->mibc, fecp->mibc); + printf("r_cntrl %x - %x\n", (int)&fecp->rcr, fecp->rcr); + printf("x_cntrl %x - %x\n", (int)&fecp->tcr, fecp->tcr); + printf("padr_l %x - %x\n", (int)&fecp->palr, fecp->palr); + printf("padr_u %x - %x\n", (int)&fecp->paur, fecp->paur); + printf("op_pause %x - %x\n", (int)&fecp->opd, fecp->opd); + printf("iadr_u %x - %x\n", (int)&fecp->iaur, fecp->iaur); + printf("iadr_l %x - %x\n", (int)&fecp->ialr, fecp->ialr); + printf("gadr_u %x - %x\n", (int)&fecp->gaur, fecp->gaur); + printf("gadr_l %x - %x\n", (int)&fecp->galr, fecp->galr); + printf("x_wmrk %x - %x\n", (int)&fecp->tfwr, fecp->tfwr); + printf("r_bound %x - %x\n", (int)&fecp->frbr, fecp->frbr); + printf("r_fstart %x - %x\n", (int)&fecp->frsr, fecp->frsr); + printf("r_drng %x - %x\n", (int)&fecp->erdsr, fecp->erdsr); + printf("x_drng %x - %x\n", (int)&fecp->etdsr, fecp->etdsr); + printf("r_bufsz %x - %x\n", (int)&fecp->emrbr, fecp->emrbr); + + printf("\n"); + printf("rmon_t_drop %x - %x\n", (int)&fecp->rmon_t_drop, + fecp->rmon_t_drop); + printf("rmon_t_packets %x - %x\n", (int)&fecp->rmon_t_packets, + fecp->rmon_t_packets); + printf("rmon_t_bc_pkt %x - %x\n", (int)&fecp->rmon_t_bc_pkt, + fecp->rmon_t_bc_pkt); + printf("rmon_t_mc_pkt %x - %x\n", (int)&fecp->rmon_t_mc_pkt, + fecp->rmon_t_mc_pkt); + printf("rmon_t_crc_align %x - %x\n", (int)&fecp->rmon_t_crc_align, + fecp->rmon_t_crc_align); + printf("rmon_t_undersize %x - %x\n", (int)&fecp->rmon_t_undersize, + fecp->rmon_t_undersize); + printf("rmon_t_oversize %x - %x\n", (int)&fecp->rmon_t_oversize, + fecp->rmon_t_oversize); + printf("rmon_t_frag %x - %x\n", (int)&fecp->rmon_t_frag, + fecp->rmon_t_frag); + printf("rmon_t_jab %x - %x\n", (int)&fecp->rmon_t_jab, + fecp->rmon_t_jab); + printf("rmon_t_col %x - %x\n", (int)&fecp->rmon_t_col, + fecp->rmon_t_col); + printf("rmon_t_p64 %x - %x\n", (int)&fecp->rmon_t_p64, + fecp->rmon_t_p64); + printf("rmon_t_p65to127 %x - %x\n", (int)&fecp->rmon_t_p65to127, + fecp->rmon_t_p65to127); + printf("rmon_t_p128to255 %x - %x\n", (int)&fecp->rmon_t_p128to255, + fecp->rmon_t_p128to255); + printf("rmon_t_p256to511 %x - %x\n", (int)&fecp->rmon_t_p256to511, + fecp->rmon_t_p256to511); + printf("rmon_t_p512to1023 %x - %x\n", (int)&fecp->rmon_t_p512to1023, + fecp->rmon_t_p512to1023); + printf("rmon_t_p1024to2047 %x - %x\n", (int)&fecp->rmon_t_p1024to2047, + fecp->rmon_t_p1024to2047); + printf("rmon_t_p_gte2048 %x - %x\n", (int)&fecp->rmon_t_p_gte2048, + fecp->rmon_t_p_gte2048); + printf("rmon_t_octets %x - %x\n", (int)&fecp->rmon_t_octets, + fecp->rmon_t_octets); + + printf("\n"); + printf("ieee_t_drop %x - %x\n", (int)&fecp->ieee_t_drop, + fecp->ieee_t_drop); + printf("ieee_t_frame_ok %x - %x\n", (int)&fecp->ieee_t_frame_ok, + fecp->ieee_t_frame_ok); + printf("ieee_t_1col %x - %x\n", (int)&fecp->ieee_t_1col, + fecp->ieee_t_1col); + printf("ieee_t_mcol %x - %x\n", (int)&fecp->ieee_t_mcol, + fecp->ieee_t_mcol); + printf("ieee_t_def %x - %x\n", (int)&fecp->ieee_t_def, + fecp->ieee_t_def); + printf("ieee_t_lcol %x - %x\n", (int)&fecp->ieee_t_lcol, + fecp->ieee_t_lcol); + printf("ieee_t_excol %x - %x\n", (int)&fecp->ieee_t_excol, + fecp->ieee_t_excol); + printf("ieee_t_macerr %x - %x\n", (int)&fecp->ieee_t_macerr, + fecp->ieee_t_macerr); + printf("ieee_t_cserr %x - %x\n", (int)&fecp->ieee_t_cserr, + fecp->ieee_t_cserr); + printf("ieee_t_sqe %x - %x\n", (int)&fecp->ieee_t_sqe, + fecp->ieee_t_sqe); + printf("ieee_t_fdxfc %x - %x\n", (int)&fecp->ieee_t_fdxfc, + fecp->ieee_t_fdxfc); + printf("ieee_t_octets_ok %x - %x\n", (int)&fecp->ieee_t_octets_ok, + fecp->ieee_t_octets_ok); + + printf("\n"); + printf("rmon_r_drop %x - %x\n", (int)&fecp->rmon_r_drop, + fecp->rmon_r_drop); + printf("rmon_r_packets %x - %x\n", (int)&fecp->rmon_r_packets, + fecp->rmon_r_packets); + printf("rmon_r_bc_pkt %x - %x\n", (int)&fecp->rmon_r_bc_pkt, + fecp->rmon_r_bc_pkt); + printf("rmon_r_mc_pkt %x - %x\n", (int)&fecp->rmon_r_mc_pkt, + fecp->rmon_r_mc_pkt); + printf("rmon_r_crc_align %x - %x\n", (int)&fecp->rmon_r_crc_align, + fecp->rmon_r_crc_align); + printf("rmon_r_undersize %x - %x\n", (int)&fecp->rmon_r_undersize, + fecp->rmon_r_undersize); + printf("rmon_r_oversize %x - %x\n", (int)&fecp->rmon_r_oversize, + fecp->rmon_r_oversize); + printf("rmon_r_frag %x - %x\n", (int)&fecp->rmon_r_frag, + fecp->rmon_r_frag); + printf("rmon_r_jab %x - %x\n", (int)&fecp->rmon_r_jab, + fecp->rmon_r_jab); + printf("rmon_r_p64 %x - %x\n", (int)&fecp->rmon_r_p64, + fecp->rmon_r_p64); + printf("rmon_r_p65to127 %x - %x\n", (int)&fecp->rmon_r_p65to127, + fecp->rmon_r_p65to127); + printf("rmon_r_p128to255 %x - %x\n", (int)&fecp->rmon_r_p128to255, + fecp->rmon_r_p128to255); + printf("rmon_r_p256to511 %x - %x\n", (int)&fecp->rmon_r_p256to511, + fecp->rmon_r_p256to511); + printf("rmon_r_p512to1023 %x - %x\n", (int)&fecp->rmon_r_p512to1023, + fecp->rmon_r_p512to1023); + printf("rmon_r_p1024to2047 %x - %x\n", (int)&fecp->rmon_r_p1024to2047, + fecp->rmon_r_p1024to2047); + printf("rmon_r_p_gte2048 %x - %x\n", (int)&fecp->rmon_r_p_gte2048, + fecp->rmon_r_p_gte2048); + printf("rmon_r_octets %x - %x\n", (int)&fecp->rmon_r_octets, + fecp->rmon_r_octets); + + printf("\n"); + printf("ieee_r_drop %x - %x\n", (int)&fecp->ieee_r_drop, + fecp->ieee_r_drop); + printf("ieee_r_frame_ok %x - %x\n", (int)&fecp->ieee_r_frame_ok, + fecp->ieee_r_frame_ok); + printf("ieee_r_crc %x - %x\n", (int)&fecp->ieee_r_crc, + fecp->ieee_r_crc); + printf("ieee_r_align %x - %x\n", (int)&fecp->ieee_r_align, + fecp->ieee_r_align); + printf("ieee_r_macerr %x - %x\n", (int)&fecp->ieee_r_macerr, + fecp->ieee_r_macerr); + printf("ieee_r_fdxfc %x - %x\n", (int)&fecp->ieee_r_fdxfc, + fecp->ieee_r_fdxfc); + printf("ieee_r_octets_ok %x - %x\n", (int)&fecp->ieee_r_octets_ok, + fecp->ieee_r_octets_ok); + + printf("\n\n\n"); +} +#endif + +int fec_init(struct eth_device *dev, bd_t * bd) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + int i; + uchar ea[6]; + + fecpin_setclear(dev, 1); + + fec_reset(dev); + +#if defined(CONFIG_CMD_MII) || defined (CONFIG_MII) || \ + defined (CONFIG_SYS_DISCOVER_PHY) + + mii_init(); + + setFecDuplexSpeed(fecp, bd, info->dup_spd); +#else +#ifndef CONFIG_SYS_DISCOVER_PHY + setFecDuplexSpeed(fecp, bd, (FECDUPLEX << 16) | FECSPEED); +#endif /* ifndef CONFIG_SYS_DISCOVER_PHY */ +#endif /* CONFIG_CMD_MII || CONFIG_MII */ + + /* We use strictly polling mode only */ + fecp->eimr = 0; + + /* Clear any pending interrupt */ + fecp->eir = 0xffffffff; + + /* Set station address */ + if ((u32) fecp == CONFIG_SYS_FEC0_IOBASE) { +#ifdef CONFIG_SYS_FEC1_IOBASE + volatile fec_t *fecp1 = (fec_t *) (CONFIG_SYS_FEC1_IOBASE); + eth_getenv_enetaddr("eth1addr", ea); + fecp1->palr = + (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + fecp1->paur = (ea[4] << 24) | (ea[5] << 16); +#endif + eth_getenv_enetaddr("ethaddr", ea); + fecp->palr = + (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + fecp->paur = (ea[4] << 24) | (ea[5] << 16); + } else { +#ifdef CONFIG_SYS_FEC0_IOBASE + volatile fec_t *fecp0 = (fec_t *) (CONFIG_SYS_FEC0_IOBASE); + eth_getenv_enetaddr("ethaddr", ea); + fecp0->palr = + (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + fecp0->paur = (ea[4] << 24) | (ea[5] << 16); +#endif +#ifdef CONFIG_SYS_FEC1_IOBASE + eth_getenv_enetaddr("eth1addr", ea); + fecp->palr = + (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + fecp->paur = (ea[4] << 24) | (ea[5] << 16); +#endif + } + + /* Clear unicast address hash table */ + fecp->iaur = 0; + fecp->ialr = 0; + + /* Clear multicast address hash table */ + fecp->gaur = 0; + fecp->galr = 0; + + /* Set maximum receive buffer size. */ + fecp->emrbr = PKT_MAXBLR_SIZE; + + /* + * Setup Buffers and Buffer Desriptors + */ + info->rxIdx = 0; + info->txIdx = 0; + + /* + * Setup Receiver Buffer Descriptors (13.14.24.18) + * Settings: + * Empty, Wrap + */ + for (i = 0; i < PKTBUFSRX; i++) { + info->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + info->rxbd[i].cbd_datlen = 0; /* Reset */ + info->rxbd[i].cbd_bufaddr = (uint) NetRxPackets[i]; + } + info->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* + * Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) + * Settings: + * Last, Tx CRC + */ + for (i = 0; i < TX_BUF_CNT; i++) { + info->txbd[i].cbd_sc = BD_ENET_TX_LAST | BD_ENET_TX_TC; + info->txbd[i].cbd_datlen = 0; /* Reset */ + info->txbd[i].cbd_bufaddr = (uint) (&info->txbuf[0]); + } + info->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* Set receive and transmit descriptor base */ + fecp->erdsr = (unsigned int)(&info->rxbd[0]); + fecp->etdsr = (unsigned int)(&info->txbd[0]); + + /* Now enable the transmit and receive processing */ + fecp->ecr |= FEC_ECR_ETHER_EN; + + /* And last, try to fill Rx Buffer Descriptors */ + fecp->rdar = 0x01000000; /* Descriptor polling active */ + + return 1; +} + +void fec_reset(struct eth_device *dev) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + int i; + + fecp->ecr = FEC_ECR_RESET; + for (i = 0; (fecp->ecr & FEC_ECR_RESET) && (i < FEC_RESET_DELAY); ++i) { + udelay(1); + } + if (i == FEC_RESET_DELAY) { + printf("FEC_RESET_DELAY timeout\n"); + } +} + +void fec_halt(struct eth_device *dev) +{ + struct fec_info_s *info = dev->priv; + + fec_reset(dev); + + fecpin_setclear(dev, 0); + + info->rxIdx = info->txIdx = 0; + memset(info->rxbd, 0, PKTBUFSRX * sizeof(cbd_t)); + memset(info->txbd, 0, TX_BUF_CNT * sizeof(cbd_t)); + memset(info->txbuf, 0, DBUF_LENGTH); +} + +int mcffec_initialize(bd_t * bis) +{ + struct eth_device *dev; + int i; +#ifdef CONFIG_SYS_FEC_BUF_USE_SRAM + u32 tmp = CONFIG_SYS_INIT_RAM_ADDR + 0x1000; +#endif + + for (i = 0; i < ARRAY_SIZE(fec_info); i++) { + + dev = + (struct eth_device *)memalign(CONFIG_SYS_CACHELINE_SIZE, + sizeof *dev); + if (dev == NULL) + hang(); + + memset(dev, 0, sizeof(*dev)); + + sprintf(dev->name, "FEC%d", fec_info[i].index); + + dev->priv = &fec_info[i]; + dev->init = fec_init; + dev->halt = fec_halt; + dev->send = fec_send; + dev->recv = fec_recv; + + /* setup Receive and Transmit buffer descriptor */ +#ifdef CONFIG_SYS_FEC_BUF_USE_SRAM + fec_info[i].rxbd = (cbd_t *)((u32)fec_info[i].rxbd + tmp); + tmp = (u32)fec_info[i].rxbd; + fec_info[i].txbd = + (cbd_t *)((u32)fec_info[i].txbd + tmp + + (PKTBUFSRX * sizeof(cbd_t))); + tmp = (u32)fec_info[i].txbd; + fec_info[i].txbuf = + (char *)((u32)fec_info[i].txbuf + tmp + + (CONFIG_SYS_TX_ETH_BUFFER * sizeof(cbd_t))); + tmp = (u32)fec_info[i].txbuf; +#else + fec_info[i].rxbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + (PKTBUFSRX * sizeof(cbd_t))); + fec_info[i].txbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + (TX_BUF_CNT * sizeof(cbd_t))); + fec_info[i].txbuf = + (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, DBUF_LENGTH); +#endif + +#ifdef ET_DEBUG + printf("rxbd %x txbd %x\n", + (int)fec_info[i].rxbd, (int)fec_info[i].txbd); +#endif + + fec_info[i].phy_name = (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, 32); + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, + mcffec_miiphy_read, mcffec_miiphy_write); +#endif + if (i > 0) + fec_info[i - 1].next = &fec_info[i]; + } + fec_info[i - 1].next = &fec_info[0]; + + /* default speed */ + bis->bi_ethspeed = 10; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/mcfmii.c b/qemu/roms/u-boot/drivers/net/mcfmii.c new file mode 100644 index 000000000..17a780c85 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/mcfmii.c @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2004-2008 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#ifdef CONFIG_MCF547x_8x +#include +#else +#include +#endif +#include + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CMD_NET) +#undef MII_DEBUG +#undef ET_DEBUG + +/*extern int fecpin_setclear(struct eth_device *dev, int setclear);*/ + +#if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_CMD_MII) +#include + +/* Make MII read/write commands for the FEC. */ +#define mk_mii_read(ADDR, REG) (0x60020000 | ((ADDR << 23) | \ + (REG & 0x1f) << 18)) +#define mk_mii_write(ADDR, REG, VAL) (0x50020000 | ((ADDR << 23) | \ + (REG & 0x1f) << 18) | (VAL & 0xffff)) + +#ifndef CONFIG_SYS_UNSPEC_PHYID +# define CONFIG_SYS_UNSPEC_PHYID 0 +#endif +#ifndef CONFIG_SYS_UNSPEC_STRID +# define CONFIG_SYS_UNSPEC_STRID 0 +#endif + +#ifdef CONFIG_MCF547x_8x +typedef struct fec_info_dma FEC_INFO_T; +#define FEC_T fecdma_t +#else +typedef struct fec_info_s FEC_INFO_T; +#define FEC_T fec_t +#endif + +typedef struct phy_info_struct { + u32 phyid; + char *strid; +} phy_info_t; + +phy_info_t phyinfo[] = { + {0x0022561B, "AMD79C784VC"}, /* AMD 79C784VC */ + {0x00406322, "BCM5222"}, /* Broadcom 5222 */ + {0x02a80150, "Intel82555"}, /* Intel 82555 */ + {0x0016f870, "LSI80225"}, /* LSI 80225 */ + {0x0016f880, "LSI80225/B"}, /* LSI 80225/B */ + {0x78100000, "LXT970"}, /* LXT970 */ + {0x001378e0, "LXT971"}, /* LXT971 and 972 */ + {0x00221619, "KS8721BL"}, /* Micrel KS8721BL/SL */ + {0x00221512, "KSZ8041NL"}, /* Micrel KSZ8041NL */ + {0x20005CE1, "N83640"}, /* National 83640 */ + {0x20005C90, "N83848"}, /* National 83848 */ + {0x20005CA2, "N83849"}, /* National 83849 */ + {0x01814400, "QS6612"}, /* QS6612 */ +#if defined(CONFIG_SYS_UNSPEC_PHYID) && defined(CONFIG_SYS_UNSPEC_STRID) + {CONFIG_SYS_UNSPEC_PHYID, CONFIG_SYS_UNSPEC_STRID}, +#endif + {0, 0} +}; + +/* + * mii_init -- Initialize the MII for MII command without ethernet + * This function is a subset of eth_init + */ +void mii_reset(FEC_INFO_T *info) +{ + volatile FEC_T *fecp = (FEC_T *) (info->miibase); + int i; + + fecp->ecr = FEC_ECR_RESET; + + for (i = 0; (fecp->ecr & FEC_ECR_RESET) && (i < FEC_RESET_DELAY); ++i) { + udelay(1); + } + if (i == FEC_RESET_DELAY) + printf("FEC_RESET_DELAY timeout\n"); +} + +/* send command to phy using mii, wait for result */ +uint mii_send(uint mii_cmd) +{ + FEC_INFO_T *info; + volatile FEC_T *ep; + struct eth_device *dev; + uint mii_reply; + int j = 0; + + /* retrieve from register structure */ + dev = eth_get_dev(); + info = dev->priv; + + ep = (FEC_T *) info->miibase; + + ep->mmfr = mii_cmd; /* command to phy */ + + /* wait for mii complete */ + while (!(ep->eir & FEC_EIR_MII) && (j < MCFFEC_TOUT_LOOP)) { + udelay(1); + j++; + } + if (j >= MCFFEC_TOUT_LOOP) { + printf("MII not complete\n"); + return -1; + } + + mii_reply = ep->mmfr; /* result from phy */ + ep->eir = FEC_EIR_MII; /* clear MII complete */ +#ifdef ET_DEBUG + printf("%s[%d] %s: sent=0x%8.8x, reply=0x%8.8x\n", + __FILE__, __LINE__, __FUNCTION__, mii_cmd, mii_reply); +#endif + + return (mii_reply & 0xffff); /* data read from phy */ +} +#endif /* CONFIG_SYS_DISCOVER_PHY || (CONFIG_MII) */ + +#if defined(CONFIG_SYS_DISCOVER_PHY) +int mii_discover_phy(struct eth_device *dev) +{ +#define MAX_PHY_PASSES 11 + FEC_INFO_T *info = dev->priv; + int phyaddr, pass; + uint phyno, phytype; + int i, found = 0; + + if (info->phyname_init) + return info->phy_addr; + + phyaddr = -1; /* didn't find a PHY yet */ + for (pass = 1; pass <= MAX_PHY_PASSES && phyaddr < 0; ++pass) { + if (pass > 1) { + /* PHY may need more time to recover from reset. + * The LXT970 needs 50ms typical, no maximum is + * specified, so wait 10ms before try again. + * With 11 passes this gives it 100ms to wake up. + */ + udelay(10000); /* wait 10ms */ + } + + for (phyno = 0; phyno < 32 && phyaddr < 0; ++phyno) { + + phytype = mii_send(mk_mii_read(phyno, MII_PHYSID1)); +#ifdef ET_DEBUG + printf("PHY type 0x%x pass %d type\n", phytype, pass); +#endif + if (phytype == 0xffff) + continue; + phyaddr = phyno; + phytype <<= 16; + phytype |= + mii_send(mk_mii_read(phyno, MII_PHYSID2)); + +#ifdef ET_DEBUG + printf("PHY @ 0x%x pass %d\n", phyno, pass); +#endif + + for (i = 0; (i < ARRAY_SIZE(phyinfo)) + && (phyinfo[i].phyid != 0); i++) { + if (phyinfo[i].phyid == phytype) { +#ifdef ET_DEBUG + printf("phyid %x - %s\n", + phyinfo[i].phyid, + phyinfo[i].strid); +#endif + strcpy(info->phy_name, phyinfo[i].strid); + info->phyname_init = 1; + found = 1; + break; + } + } + + if (!found) { +#ifdef ET_DEBUG + printf("0x%08x\n", phytype); +#endif + strcpy(info->phy_name, "unknown"); + info->phyname_init = 1; + break; + } + } + } + + if (phyaddr < 0) + printf("No PHY device found.\n"); + + return phyaddr; +} +#endif /* CONFIG_SYS_DISCOVER_PHY */ + +void mii_init(void) __attribute__((weak,alias("__mii_init"))); + +void __mii_init(void) +{ + FEC_INFO_T *info; + volatile FEC_T *fecp; + struct eth_device *dev; + int miispd = 0, i = 0; + u16 status = 0; + u16 linkgood = 0; + + /* retrieve from register structure */ + dev = eth_get_dev(); + info = dev->priv; + + fecp = (FEC_T *) info->miibase; + + fecpin_setclear(dev, 1); + + mii_reset(info); + + /* We use strictly polling mode only */ + fecp->eimr = 0; + + /* Clear any pending interrupt */ + fecp->eir = 0xffffffff; + + /* Set MII speed */ + miispd = (gd->bus_clk / 1000000) / 5; + fecp->mscr = miispd << 1; + + info->phy_addr = mii_discover_phy(dev); + + while (i < MCFFEC_TOUT_LOOP) { + status = 0; + i++; + /* Read PHY control register */ + miiphy_read(dev->name, info->phy_addr, MII_BMCR, &status); + + /* If phy set to autonegotiate, wait for autonegotiation done, + * if phy is not autonegotiating, just wait for link up. + */ + if ((status & BMCR_ANENABLE) == BMCR_ANENABLE) { + linkgood = (BMSR_ANEGCOMPLETE | BMSR_LSTATUS); + } else { + linkgood = BMSR_LSTATUS; + } + /* Read PHY status register */ + miiphy_read(dev->name, info->phy_addr, MII_BMSR, &status); + if ((status & linkgood) == linkgood) + break; + + udelay(1); + } + if (i >= MCFFEC_TOUT_LOOP) { + printf("Link UP timeout\n"); + } + + /* adapt to the duplex and speed settings of the phy */ + info->dup_spd = miiphy_duplex(dev->name, info->phy_addr) << 16; + info->dup_spd |= miiphy_speed(dev->name, info->phy_addr); +} + +/* + * Read and write a MII PHY register, routines used by MII Utilities + * + * FIXME: These routines are expected to return 0 on success, but mii_send + * does _not_ return an error code. Maybe 0xFFFF means error, i.e. + * no PHY connected... + * For now always return 0. + * FIXME: These routines only work after calling eth_init() at least once! + * Otherwise they hang in mii_send() !!! Sorry! + */ + +int mcffec_miiphy_read(const char *devname, unsigned char addr, unsigned char reg, + unsigned short *value) +{ + short rdreg; /* register working value */ + +#ifdef MII_DEBUG + printf("miiphy_read(0x%x) @ 0x%x = ", reg, addr); +#endif + rdreg = mii_send(mk_mii_read(addr, reg)); + + *value = rdreg; + +#ifdef MII_DEBUG + printf("0x%04x\n", *value); +#endif + + return 0; +} + +int mcffec_miiphy_write(const char *devname, unsigned char addr, unsigned char reg, + unsigned short value) +{ +#ifdef MII_DEBUG + printf("miiphy_write(0x%x) @ 0x%x = ", reg, addr); +#endif + + mii_send(mk_mii_write(addr, reg, value)); + +#ifdef MII_DEBUG + printf("0x%04x\n", value); +#endif + + return 0; +} + +#endif /* CONFIG_CMD_NET */ diff --git a/qemu/roms/u-boot/drivers/net/mpc512x_fec.c b/qemu/roms/u-boot/drivers/net/mpc512x_fec.c new file mode 100644 index 000000000..427e0b8b4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/mpc512x_fec.c @@ -0,0 +1,754 @@ +/* + * (C) Copyright 2003-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Derived from the MPC8xx FEC driver. + * Adapted for MPC512x by Grzegorz Bernacki + */ + +#include +#include +#include +#include +#include +#include +#include "mpc512x_fec.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define DEBUG 0 + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +int fec512x_miiphy_read(const char *devname, u8 phyAddr, u8 regAddr, u16 * retVal); +int fec512x_miiphy_write(const char *devname, u8 phyAddr, u8 regAddr, u16 data); +int mpc512x_fec_init_phy(struct eth_device *dev, bd_t * bis); + +static uchar rx_buff[FEC_BUFFER_SIZE]; +static int rx_buff_idx = 0; + +/********************************************************************/ +#if (DEBUG & 0x2) +static void mpc512x_fec_phydump (char *devname) +{ + u16 phyStatus, i; + u8 phyAddr = CONFIG_PHY_ADDR; + u8 reg_mask[] = { + /* regs to print: 0...8, 21,27,31 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, + }; + + for (i = 0; i < 32; i++) { + if (reg_mask[i]) { + miiphy_read (devname, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x\n", i, phyStatus); + } + } +} +#endif + +/********************************************************************/ +static int mpc512x_fec_bd_init (mpc512x_fec_priv *fec) +{ + int ix; + + /* + * Receive BDs init + */ + for (ix = 0; ix < FEC_RBD_NUM; ix++) { + fec->bdBase->rbd[ix].dataPointer = + (u32)&fec->bdBase->recv_frames[ix]; + fec->bdBase->rbd[ix].status = FEC_RBD_EMPTY; + fec->bdBase->rbd[ix].dataLength = 0; + } + + /* + * have the last RBD to close the ring + */ + fec->bdBase->rbd[ix - 1].status |= FEC_RBD_WRAP; + fec->rbdIndex = 0; + + /* + * Trasmit BDs init + */ + for (ix = 0; ix < FEC_TBD_NUM; ix++) { + fec->bdBase->tbd[ix].status = 0; + } + + /* + * Have the last TBD to close the ring + */ + fec->bdBase->tbd[ix - 1].status |= FEC_TBD_WRAP; + + /* + * Initialize some indices + */ + fec->tbdIndex = 0; + fec->usedTbdIndex = 0; + fec->cleanTbdNum = FEC_TBD_NUM; + + return 0; +} + +/********************************************************************/ +static void mpc512x_fec_rbd_clean (mpc512x_fec_priv *fec, volatile FEC_RBD * pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) + pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); + else + pRbd->status = FEC_RBD_EMPTY; + + pRbd->dataLength = 0; + + /* + * Increment BD count + */ + fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; + + /* + * Now, we have an empty RxBD, notify FEC + * Set Descriptor polling active + */ + out_be32(&fec->eth->r_des_active, 0x01000000); +} + +/********************************************************************/ +static void mpc512x_fec_tbd_scrub (mpc512x_fec_priv *fec) +{ + volatile FEC_TBD *pUsedTbd; + +#if (DEBUG & 0x1) + printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", + fec->cleanTbdNum, fec->usedTbdIndex); +#endif + + /* + * process all the consumed TBDs + */ + while (fec->cleanTbdNum < FEC_TBD_NUM) { + pUsedTbd = &fec->bdBase->tbd[fec->usedTbdIndex]; + if (pUsedTbd->status & FEC_TBD_READY) { +#if (DEBUG & 0x20) + printf ("Cannot clean TBD %d, in use\n", fec->usedTbdIndex); +#endif + return; + } + + /* + * clean this buffer descriptor + */ + if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) + pUsedTbd->status = FEC_TBD_WRAP; + else + pUsedTbd->status = 0; + + /* + * update some indeces for a correct handling of the TBD ring + */ + fec->cleanTbdNum++; + fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; + } +} + +/********************************************************************/ +static void mpc512x_fec_set_hwaddr (mpc512x_fec_priv *fec, unsigned char *mac) +{ + u8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + u32 crc = 0xffffffff; /* initial value */ + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if (crc >= 32) { + out_be32(&fec->eth->iaddr1, (1 << (crc - 32))); + out_be32(&fec->eth->iaddr2, 0); + } else { + out_be32(&fec->eth->iaddr1, 0); + out_be32(&fec->eth->iaddr2, (1 << crc)); + } + + /* + * Set physical address + */ + out_be32(&fec->eth->paddr1, (mac[0] << 24) + (mac[1] << 16) + + (mac[2] << 8) + mac[3]); + out_be32(&fec->eth->paddr2, (mac[4] << 24) + (mac[5] << 16) + + 0x8808); +} + +/********************************************************************/ +static int mpc512x_fec_init (struct eth_device *dev, bd_t * bis) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init... Begin\n"); +#endif + + mpc512x_fec_set_hwaddr (fec, dev->enetaddr); + out_be32(&fec->eth->gaddr1, 0x00000000); + out_be32(&fec->eth->gaddr2, 0x00000000); + + mpc512x_fec_init_phy (dev, bis); + + /* Set interrupt mask register */ + out_be32(&fec->eth->imask, 0x00000000); + + /* Clear FEC-Lite interrupt event register(IEVENT) */ + out_be32(&fec->eth->ievent, 0xffffffff); + + /* Set transmit fifo watermark register(X_WMRK), default = 64 */ + out_be32(&fec->eth->x_wmrk, 0x0); + + /* Set Opcode/Pause Duration Register */ + out_be32(&fec->eth->op_pause, 0x00010020); + + /* Frame length=1522; MII mode */ + out_be32(&fec->eth->r_cntrl, (FEC_MAX_FRAME_LEN << 16) | 0x24); + + /* Half-duplex, heartbeat disabled */ + out_be32(&fec->eth->x_cntrl, 0x00000000); + + /* Enable MIB counters */ + out_be32(&fec->eth->mib_control, 0x0); + + /* Setup recv fifo start and buff size */ + out_be32(&fec->eth->r_fstart, 0x500); + out_be32(&fec->eth->r_buff_size, FEC_BUFFER_SIZE); + + /* Setup BD base addresses */ + out_be32(&fec->eth->r_des_start, (u32)fec->bdBase->rbd); + out_be32(&fec->eth->x_des_start, (u32)fec->bdBase->tbd); + + /* DMA Control */ + out_be32(&fec->eth->dma_control, 0xc0000000); + + /* Enable FEC */ + setbits_be32(&fec->eth->ecntrl, 0x00000006); + + /* Initilize addresses and status words of BDs */ + mpc512x_fec_bd_init (fec); + + /* Descriptor polling active */ + out_be32(&fec->eth->r_des_active, 0x01000000); + +#if (DEBUG & 0x1) + printf("mpc512x_fec_init... Done \n"); +#endif + return 1; +} + +/********************************************************************/ +int mpc512x_fec_init_phy (struct eth_device *dev, bd_t * bis) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + const u8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + int timeout = 1; + u16 phyStatus; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init_phy... Begin\n"); +#endif + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + out_be32(&fec->eth->ievent, 0xffffffff); + + /* + * Set interrupt mask register + */ + out_be32(&fec->eth->imask, 0x00000000); + + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + out_be32(&fec->eth->mii_speed, + (((gd->arch.ips_clk / 1000000) / 5) + 1) << 1); + + /* + * Reset PHY, then delay 300ns + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x8000); + udelay (1000); + + if (fec->xcv_type == MII10) { + /* + * Force 10Base-T, FDX operation + */ +#if (DEBUG & 0x2) + printf ("Forcing 10 Mbps ethernet link... "); +#endif + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); + + miiphy_write (dev->name, phyAddr, 0x0, 0x0180); + + timeout = 20; + do { /* wait for link status to go down */ + udelay (10000); + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf ("hmmm, should not have waited..."); +#endif + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf ("="); +#endif + } while ((phyStatus & 0x0004)); /* !link up */ + + timeout = 1000; + do { /* wait for link status to come back up */ + udelay (10000); + if ((timeout--) == 0) { + printf ("failed. Link is down.\n"); + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf ("+"); +#endif + } while (!(phyStatus & 0x0004)); /* !link up */ + +#if (DEBUG & 0x2) + printf ("done.\n"); +#endif + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write (dev->name, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 2500; + do { + udelay (1000); + + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf ("PHY auto neg 0 failed...\n"); +#endif + return -1; + } + + if (miiphy_read (dev->name, phyAddr, 0x1, &phyStatus) != 0) { +#if (DEBUG & 0x2) + printf ("PHY auto neg 1 failed 0x%04x...\n", phyStatus); +#endif + return -1; + } + } while (!(phyStatus & 0x0004)); + +#if (DEBUG & 0x2) + printf ("PHY auto neg complete! \n"); +#endif + } + } + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc512x_fec_phydump (dev->name); +#endif + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init_phy... Done \n"); +#endif + return 1; +} + +/********************************************************************/ +static void mpc512x_fec_halt (struct eth_device *dev) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + int counter = 0xffff; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc512x_fec_phydump (dev->name); +#endif + + /* + * mask FEC chip interrupts + */ + out_be32(&fec->eth->imask, 0); + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + setbits_be32(&fec->eth->x_cntrl, 0x00000001); + + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(in_be32(&fec->eth->ievent) & 0x10000000))) + ; + + /* + * Disable the Ethernet Controller + */ + clrbits_be32(&fec->eth->ecntrl, 0x00000002); + + /* + * Issue a reset command to the FEC chip + */ + setbits_be32(&fec->eth->ecntrl, 0x1); + + /* + * wait at least 16 clock cycles + */ + udelay (10); +#if (DEBUG & 0x3) + printf ("Ethernet task stopped\n"); +#endif +} + +/********************************************************************/ + +static int mpc512x_fec_send(struct eth_device *dev, void *eth_data, + int data_length) +{ + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + volatile FEC_TBD *pTbd; + +#if (DEBUG & 0x20) + printf("tbd status: 0x%04x\n", fec->tbdBase[fec->tbdIndex].status); +#endif + + /* + * Clear Tx BD ring at first + */ + mpc512x_fec_tbd_scrub (fec); + + /* + * Check for valid length of data. + */ + if ((data_length > 1500) || (data_length <= 0)) { + return -1; + } + + /* + * Check the number of vacant TxBDs. + */ + if (fec->cleanTbdNum < 1) { +#if (DEBUG & 0x20) + printf ("No available TxBDs ...\n"); +#endif + return -1; + } + + /* + * Get the first TxBD to send the mac header + */ + pTbd = &fec->bdBase->tbd[fec->tbdIndex]; + pTbd->dataLength = data_length; + pTbd->dataPointer = (u32)eth_data; + pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; + + /* Activate transmit Buffer Descriptor polling */ + out_be32(&fec->eth->x_des_active, 0x01000000); + +#if (DEBUG & 0x8) + printf ( "+" ); +#endif + + fec->cleanTbdNum -= 1; + + /* + * wait until frame is sent . + */ + while (pTbd->status & FEC_TBD_READY) { + udelay (10); +#if (DEBUG & 0x8) + printf ("TDB status = %04x\n", pTbd->status); +#endif + } + + return 0; +} + + +/********************************************************************/ +static int mpc512x_fec_recv (struct eth_device *dev) +{ + /* + * This command pulls one frame from the card + */ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + volatile FEC_RBD *pRbd = &fec->bdBase->rbd[fec->rbdIndex]; + unsigned long ievent; + int frame_length = 0; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_recv %d Start...\n", fec->rbdIndex); +#endif +#if (DEBUG & 0x8) + printf( "-" ); +#endif + + /* + * Check if any critical events have happened + */ + ievent = in_be32(&fec->eth->ievent); + out_be32(&fec->eth->ievent, ievent); + if (ievent & 0x20060000) { + /* BABT, Rx/Tx FIFO errors */ + mpc512x_fec_halt (dev); + mpc512x_fec_init (dev, NULL); + return 0; + } + if (ievent & 0x80000000) { + /* Heartbeat error */ + setbits_be32(&fec->eth->x_cntrl, 0x00000001); + } + if (ievent & 0x10000000) { + /* Graceful stop complete */ + if (in_be32(&fec->eth->x_cntrl) & 0x00000001) { + mpc512x_fec_halt (dev); + clrbits_be32(&fec->eth->x_cntrl, 0x00000001);; + mpc512x_fec_init (dev, NULL); + } + } + + if (!(pRbd->status & FEC_RBD_EMPTY)) { + if (!(pRbd->status & FEC_RBD_ERR) && + ((pRbd->dataLength - 4) > 14)) { + + /* + * Get buffer size + */ + if (pRbd->status & FEC_RBD_LAST) + frame_length = pRbd->dataLength - 4; + else + frame_length = pRbd->dataLength; +#if (DEBUG & 0x20) + { + int i; + printf ("recv data length 0x%08x data hdr: ", + pRbd->dataLength); + for (i = 0; i < 14; i++) + printf ("%x ", *((u8*)pRbd->dataPointer + i)); + printf("\n"); + } +#endif + /* + * Fill the buffer and pass it to upper layers + */ + memcpy (&rx_buff[rx_buff_idx], (void*)pRbd->dataPointer, + frame_length - rx_buff_idx); + rx_buff_idx = frame_length; + + if (pRbd->status & FEC_RBD_LAST) { + NetReceive ((uchar*)rx_buff, frame_length); + rx_buff_idx = 0; + } + } + + /* + * Reset buffer descriptor as empty + */ + mpc512x_fec_rbd_clean (fec, pRbd); + } + + /* Try to fill Buffer Descriptors */ + out_be32(&fec->eth->r_des_active, 0x01000000); + + return frame_length; +} + +/********************************************************************/ +int mpc512x_fec_initialize (bd_t * bis) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + mpc512x_fec_priv *fec; + struct eth_device *dev; + void * bd; + + fec = (mpc512x_fec_priv *) malloc (sizeof(*fec)); + dev = (struct eth_device *) malloc (sizeof(*dev)); + memset (dev, 0, sizeof *dev); + + fec->eth = &im->fec; + +# ifndef CONFIG_FEC_10MBIT + fec->xcv_type = MII100; +# else + fec->xcv_type = MII10; +# endif + dev->priv = (void *)fec; + dev->iobase = (int)&im->fec; + dev->init = mpc512x_fec_init; + dev->halt = mpc512x_fec_halt; + dev->send = mpc512x_fec_send; + dev->recv = mpc512x_fec_recv; + + sprintf (dev->name, "FEC"); + eth_register (dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, + fec512x_miiphy_read, fec512x_miiphy_write); +#endif + + /* Clean up space FEC's MIB and FIFO RAM ...*/ + memset ((void *)&im->fec.mib, 0x00, sizeof(im->fec.mib)); + memset ((void *)&im->fec.fifo, 0x00, sizeof(im->fec.fifo)); + + /* + * Malloc space for BDs (must be quad word-aligned) + * this pointer is lost, so cannot be freed + */ + bd = malloc (sizeof(mpc512x_buff_descs) + 0x1f); + fec->bdBase = (mpc512x_buff_descs*)((u32)bd & 0xfffffff0); + memset ((void *) bd, 0x00, sizeof(mpc512x_buff_descs) + 0x1f); + + /* + * Set interrupt mask register + */ + out_be32(&fec->eth->imask, 0x00000000); + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + out_be32(&fec->eth->ievent, 0xffffffff); + + return 1; +} + +/* MII-interface related functions */ +/********************************************************************/ +int fec512x_miiphy_read(const char *devname, u8 phyAddr, u8 regAddr, u16 *retVal) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile fec512x_t *eth = &im->fec; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + out_be32(ð->mii_data, FEC_MII_DATA_ST | + FEC_MII_DATA_OP_RD | + FEC_MII_DATA_TA | + phy | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(in_be32(ð->ievent) & 0x00800000))) + ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Read MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear mii interrupt bit + */ + out_be32(ð->ievent, 0x00800000); + + /* + * it's now safe to read the PHY's register + */ + *retVal = (u16) in_be32(ð->mii_data); + + return 0; +} + +/********************************************************************/ +int fec512x_miiphy_write(const char *devname, u8 phyAddr, u8 regAddr, u16 data) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile fec512x_t *eth = &im->fec; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + out_be32(ð->mii_data, FEC_MII_DATA_ST | + FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | + phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(in_be32(ð->ievent) & 0x00800000))) + ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Write MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear MII interrupt bit + */ + out_be32(ð->ievent, 0x00800000); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/mpc512x_fec.h b/qemu/roms/u-boot/drivers/net/mpc512x_fec.h new file mode 100644 index 000000000..a083cca2f --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/mpc512x_fec.h @@ -0,0 +1,98 @@ +/* + * (C) Copyright 2003 - 2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Derived from the MPC8xx driver's header file. + */ + +#ifndef __MPC512X_FEC_H +#define __MPC512X_FEC_H + +#include + +/* Receive & Transmit Buffer Descriptor definitions */ +typedef struct BufferDescriptor { + u16 status; + u16 dataLength; + u32 dataPointer; +} FEC_RBD; + +typedef struct { + u16 status; + u16 dataLength; + u32 dataPointer; +} FEC_TBD; + +/* private structure */ +typedef enum { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +} xceiver_type; + +/* BD Numer definitions */ +#define FEC_TBD_NUM 48 /* The user can adjust this value */ +#define FEC_RBD_NUM 32 /* The user can adjust this value */ + +/* packet size limit */ +#define FEC_MAX_FRAME_LEN 1522 /* recommended default value */ + +/* Buffer size must be evenly divisible by 16 */ +#define FEC_BUFFER_SIZE ((FEC_MAX_FRAME_LEN + 0x10) & (~0xf)) + +typedef struct { + u8 frame[FEC_BUFFER_SIZE]; +} mpc512x_frame; + +typedef struct { + FEC_RBD rbd[FEC_RBD_NUM]; /* RBD ring */ + FEC_TBD tbd[FEC_TBD_NUM]; /* TBD ring */ + mpc512x_frame recv_frames[FEC_RBD_NUM]; /* receive buff */ +} mpc512x_buff_descs; + +typedef struct { + volatile fec512x_t *eth; + xceiver_type xcv_type; /* transceiver type */ + mpc512x_buff_descs *bdBase; /* BD rings and recv buffer */ + u16 rbdIndex; /* next receive BD to read */ + u16 tbdIndex; /* next transmit BD to send */ + u16 usedTbdIndex; /* next transmit BD to clean */ + u16 cleanTbdNum; /* the number of available transmit BDs */ +} mpc512x_fec_priv; + +/* RBD bits definitions */ +#define FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define FEC_RBD_LG 0x0020 /* Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define FEC_RBD_SH 0x0008 /* Short frame */ +#define FEC_RBD_CR 0x0004 /* CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* TBD bits definitions */ +#define FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __MPC512X_FEC_H */ diff --git a/qemu/roms/u-boot/drivers/net/mpc5xxx_fec.c b/qemu/roms/u-boot/drivers/net/mpc5xxx_fec.c new file mode 100644 index 000000000..1093ba59d --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/mpc5xxx_fec.c @@ -0,0 +1,1027 @@ +/* + * (C) Copyright 2003-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.c, + * (C) Copyright Motorola, Inc., 2000 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "mpc5xxx_fec.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* #define DEBUG 0x28 */ + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#if (DEBUG & 0x60) +static void tfifo_print(char *devname, mpc5xxx_fec_priv *fec); +static void rfifo_print(char *devname, mpc5xxx_fec_priv *fec); +#endif /* DEBUG */ + +typedef struct { + uint8 data[1500]; /* actual data */ + int length; /* actual length */ + int used; /* buffer in use or not */ + uint8 head[16]; /* MAC header(6 + 6 + 2) + 2(aligned) */ +} NBUF; + +int fec5xxx_miiphy_read(const char *devname, uint8 phyAddr, uint8 regAddr, uint16 *retVal); +int fec5xxx_miiphy_write(const char *devname, uint8 phyAddr, uint8 regAddr, uint16 data); + +static int mpc5xxx_fec_init_phy(struct eth_device *dev, bd_t * bis); + +/********************************************************************/ +#if (DEBUG & 0x2) +static void mpc5xxx_fec_phydump (char *devname) +{ + uint16 phyStatus, i; + uint8 phyAddr = CONFIG_PHY_ADDR; + uint8 reg_mask[] = { +#if CONFIG_PHY_TYPE == 0x79c874 /* AMD Am79C874 */ + /* regs to print: 0...7, 16...19, 21, 23, 24 */ + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, +#else + /* regs to print: 0...8, 16...20 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif + }; + + for (i = 0; i < 32; i++) { + if (reg_mask[i]) { + miiphy_read(devname, phyAddr, i, &phyStatus); + printf("Mii reg %d: 0x%04x\n", i, phyStatus); + } + } +} +#endif + +/********************************************************************/ +static int mpc5xxx_fec_rbd_init(mpc5xxx_fec_priv *fec) +{ + int ix; + char *data; + static int once = 0; + + for (ix = 0; ix < FEC_RBD_NUM; ix++) { + if (!once) { + data = (char *)malloc(FEC_MAX_PKT_SIZE); + if (data == NULL) { + printf ("RBD INIT FAILED\n"); + return -1; + } + fec->rbdBase[ix].dataPointer = (uint32)data; + } + fec->rbdBase[ix].status = FEC_RBD_EMPTY; + fec->rbdBase[ix].dataLength = 0; + } + once ++; + + /* + * have the last RBD to close the ring + */ + fec->rbdBase[ix - 1].status |= FEC_RBD_WRAP; + fec->rbdIndex = 0; + + return 0; +} + +/********************************************************************/ +static void mpc5xxx_fec_tbd_init(mpc5xxx_fec_priv *fec) +{ + int ix; + + for (ix = 0; ix < FEC_TBD_NUM; ix++) { + fec->tbdBase[ix].status = 0; + } + + /* + * Have the last TBD to close the ring + */ + fec->tbdBase[ix - 1].status |= FEC_TBD_WRAP; + + /* + * Initialize some indices + */ + fec->tbdIndex = 0; + fec->usedTbdIndex = 0; + fec->cleanTbdNum = FEC_TBD_NUM; +} + +/********************************************************************/ +static void mpc5xxx_fec_rbd_clean(mpc5xxx_fec_priv *fec, volatile FEC_RBD * pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) + pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); + else + pRbd->status = FEC_RBD_EMPTY; + + pRbd->dataLength = 0; + + /* + * Now, we have an empty RxBD, restart the SmartDMA receive task + */ + SDMA_TASK_ENABLE(FEC_RECV_TASK_NO); + + /* + * Increment BD count + */ + fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; +} + +/********************************************************************/ +static void mpc5xxx_fec_tbd_scrub(mpc5xxx_fec_priv *fec) +{ + volatile FEC_TBD *pUsedTbd; + +#if (DEBUG & 0x1) + printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", + fec->cleanTbdNum, fec->usedTbdIndex); +#endif + + /* + * process all the consumed TBDs + */ + while (fec->cleanTbdNum < FEC_TBD_NUM) { + pUsedTbd = &fec->tbdBase[fec->usedTbdIndex]; + if (pUsedTbd->status & FEC_TBD_READY) { +#if (DEBUG & 0x20) + printf("Cannot clean TBD %d, in use\n", fec->cleanTbdNum); +#endif + return; + } + + /* + * clean this buffer descriptor + */ + if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) + pUsedTbd->status = FEC_TBD_WRAP; + else + pUsedTbd->status = 0; + + /* + * update some indeces for a correct handling of the TBD ring + */ + fec->cleanTbdNum++; + fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; + } +} + +/********************************************************************/ +static void mpc5xxx_fec_set_hwaddr(mpc5xxx_fec_priv *fec, char *mac) +{ + uint8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + uint32 crc = 0xffffffff; /* initial value */ + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if (crc >= 32) { + fec->eth->iaddr1 = (1 << (crc - 32)); + fec->eth->iaddr2 = 0; + } else { + fec->eth->iaddr1 = 0; + fec->eth->iaddr2 = (1 << crc); + } + + /* + * Set physical address + */ + fec->eth->paddr1 = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + fec->eth->paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; +} + +/********************************************************************/ +static int mpc5xxx_fec_init(struct eth_device *dev, bd_t * bis) +{ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5XXX_SDMA; + +#if (DEBUG & 0x1) + printf ("mpc5xxx_fec_init... Begin\n"); +#endif + + mpc5xxx_fec_init_phy(dev, bis); + + /* + * Call board-specific PHY fixups (if any) + */ +#ifdef CONFIG_RESET_PHY_R + reset_phy(); +#endif + + /* + * Initialize RxBD/TxBD rings + */ + mpc5xxx_fec_rbd_init(fec); + mpc5xxx_fec_tbd_init(fec); + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + + /* + * Set FEC-Lite receive control register(R_CNTRL): + */ + if (fec->xcv_type == SEVENWIRE) { + /* + * Frame length=1518; 7-wire mode + */ + fec->eth->r_cntrl = 0x05ee0020; /*0x05ee0000;FIXME */ + } else { + /* + * Frame length=1518; MII mode; + */ + fec->eth->r_cntrl = 0x05ee0024; /*0x05ee0004;FIXME */ + } + + fec->eth->x_cntrl = 0x00000000; /* half-duplex, heartbeat disabled */ + + /* + * Set Opcode/Pause Duration Register + */ + fec->eth->op_pause = 0x00010020; /*FIXME 0xffff0020; */ + + /* + * Set Rx FIFO alarm and granularity value + */ + fec->eth->rfifo_cntrl = 0x0c000000 + | (fec->eth->rfifo_cntrl & ~0x0f000000); + fec->eth->rfifo_alarm = 0x0000030c; +#if (DEBUG & 0x22) + if (fec->eth->rfifo_status & 0x00700000 ) { + printf("mpc5xxx_fec_init() RFIFO error\n"); + } +#endif + + /* + * Set Tx FIFO granularity value + */ + fec->eth->tfifo_cntrl = 0x0c000000 + | (fec->eth->tfifo_cntrl & ~0x0f000000); +#if (DEBUG & 0x2) + printf("tfifo_status: 0x%08x\n", fec->eth->tfifo_status); + printf("tfifo_alarm: 0x%08x\n", fec->eth->tfifo_alarm); +#endif + + /* + * Set transmit fifo watermark register(X_WMRK), default = 64 + */ + fec->eth->tfifo_alarm = 0x00000080; + fec->eth->x_wmrk = 0x2; + + /* + * Set individual address filter for unicast address + * and set physical address registers. + */ + mpc5xxx_fec_set_hwaddr(fec, (char *)dev->enetaddr); + + /* + * Set multicast address filter + */ + fec->eth->gaddr1 = 0x00000000; + fec->eth->gaddr2 = 0x00000000; + + /* + * Turn ON cheater FSM: ???? + */ + fec->eth->xmit_fsm = 0x03000000; + + /* + * Turn off COMM bus prefetch in the MPC5200 BestComm. It doesn't + * work w/ the current receive task. + */ + sdma->PtdCntrl |= 0x00000001; + + /* + * Set priority of different initiators + */ + sdma->IPR0 = 7; /* always */ + sdma->IPR3 = 6; /* Eth RX */ + sdma->IPR4 = 5; /* Eth Tx */ + + /* + * Clear SmartDMA task interrupt pending bits + */ + SDMA_CLEAR_IEVENT(FEC_RECV_TASK_NO); + + /* + * Initialize SmartDMA parameters stored in SRAM + */ + *(volatile int *)FEC_TBD_BASE = (int)fec->tbdBase; + *(volatile int *)FEC_RBD_BASE = (int)fec->rbdBase; + *(volatile int *)FEC_TBD_NEXT = (int)fec->tbdBase; + *(volatile int *)FEC_RBD_NEXT = (int)fec->rbdBase; + + /* + * Enable FEC-Lite controller + */ + fec->eth->ecntrl |= 0x00000006; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc5xxx_fec_phydump (dev->name); +#endif + + /* + * Enable SmartDMA receive task + */ + SDMA_TASK_ENABLE(FEC_RECV_TASK_NO); + +#if (DEBUG & 0x1) + printf("mpc5xxx_fec_init... Done \n"); +#endif + + return 1; +} + +/********************************************************************/ +static int mpc5xxx_fec_init_phy(struct eth_device *dev, bd_t * bis) +{ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + const uint8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + static int initialized = 0; + + if(initialized) + return 0; + initialized = 1; + +#if (DEBUG & 0x1) + printf ("mpc5xxx_fec_init_phy... Begin\n"); +#endif + + /* + * Initialize GPIO pins + */ + if (fec->xcv_type == SEVENWIRE) { + /* 10MBit with 7-wire operation */ +#if defined(CONFIG_TOTAL5200) + /* 7-wire and USB2 on Ethernet */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00030000; +#else /* !CONFIG_TOTAL5200 */ + /* 7-wire only */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00020000; +#endif /* CONFIG_TOTAL5200 */ + } else { + /* 100MBit with MD operation */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00050000; + } + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + +/* + * In original Promess-provided code PHY initialization is disabled with the + * following comment: "Phy initialization is DISABLED for now. There was a + * problem with running 100 Mbps on PRO board". Thus we temporarily disable + * PHY initialization for the Motion-PRO board, until a proper fix is found. + */ + + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + * No MII for 7-wire mode + */ + fec->eth->mii_speed = (((gd->arch.ipb_clk >> 20) / 5) << 1); + } + + if (fec->xcv_type != SEVENWIRE) { + /* + * Initialize PHY(LXT971A): + * + * Generally, on power up, the LXT971A reads its configuration + * pins to check for forced operation, If not cofigured for + * forced operation, it uses auto-negotiation/parallel detection + * to automatically determine line operating conditions. + * If the PHY device on the other side of the link supports + * auto-negotiation, the LXT971A auto-negotiates with it + * using Fast Link Pulse(FLP) Bursts. If the PHY partner does not + * support auto-negotiation, the LXT971A automatically detects + * the presence of either link pulses(10Mbps PHY) or Idle + * symbols(100Mbps) and sets its operating conditions accordingly. + * + * When auto-negotiation is controlled by software, the following + * steps are recommended. + * + * Note: + * The physical address is dependent on hardware configuration. + * + */ + int timeout = 1; + uint16 phyStatus; + + /* + * Reset PHY, then delay 300ns + */ + miiphy_write(dev->name, phyAddr, 0x0, 0x8000); + udelay(1000); + +#if defined(CONFIG_UC101) || defined(CONFIG_MUCMC52) + /* Set the LED configuration Register for the UC101 + and MUCMC52 Board */ + miiphy_write(dev->name, phyAddr, 0x14, 0x4122); +#endif + if (fec->xcv_type == MII10) { + /* + * Force 10Base-T, FDX operation + */ +#if (DEBUG & 0x2) + printf("Forcing 10 Mbps ethernet link... "); +#endif + miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); + /* + miiphy_write(dev->name, fec, phyAddr, 0x0, 0x0100); + */ + miiphy_write(dev->name, phyAddr, 0x0, 0x0180); + + timeout = 20; + do { /* wait for link status to go down */ + udelay(10000); + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf("hmmm, should not have waited..."); +#endif + break; + } + miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf("="); +#endif + } while ((phyStatus & 0x0004)); /* !link up */ + + timeout = 1000; + do { /* wait for link status to come back up */ + udelay(10000); + if ((timeout--) == 0) { + printf("failed. Link is down.\n"); + break; + } + miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf("+"); +#endif + } while (!(phyStatus & 0x0004)); /* !link up */ + +#if (DEBUG & 0x2) + printf ("done.\n"); +#endif + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write(dev->name, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + miiphy_write(dev->name, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 5000; + do { + udelay(1000); + + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf("PHY auto neg 0 failed...\n"); +#endif + return -1; + } + + if (miiphy_read(dev->name, phyAddr, 0x1, &phyStatus) != 0) { +#if (DEBUG & 0x2) + printf("PHY auto neg 1 failed 0x%04x...\n", phyStatus); +#endif + return -1; + } + } while (!(phyStatus & 0x0004)); + +#if (DEBUG & 0x2) + printf("PHY auto neg complete! \n"); +#endif + } + + } + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc5xxx_fec_phydump (dev->name); +#endif + + +#if (DEBUG & 0x1) + printf("mpc5xxx_fec_init_phy... Done \n"); +#endif + + return 1; +} + +/********************************************************************/ +static void mpc5xxx_fec_halt(struct eth_device *dev) +{ + struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5XXX_SDMA; + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + int counter = 0xffff; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc5xxx_fec_phydump (dev->name); +#endif + + /* + * mask FEC chip interrupts + */ + fec->eth->imask = 0; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + fec->eth->x_cntrl |= 0x00000001; + + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(fec->eth->ievent & 0x10000000))) ; + + /* + * Disable SmartDMA tasks + */ + SDMA_TASK_DISABLE (FEC_XMIT_TASK_NO); + SDMA_TASK_DISABLE (FEC_RECV_TASK_NO); + + /* + * Turn on COMM bus prefetch in the MPC5200 BestComm after we're + * done. It doesn't work w/ the current receive task. + */ + sdma->PtdCntrl &= ~0x00000001; + + /* + * Disable the Ethernet Controller + */ + fec->eth->ecntrl &= 0xfffffffd; + + /* + * Clear FIFO status registers + */ + fec->eth->rfifo_status &= 0x00700000; + fec->eth->tfifo_status &= 0x00700000; + + fec->eth->reset_cntrl = 0x01000000; + + /* + * Issue a reset command to the FEC chip + */ + fec->eth->ecntrl |= 0x1; + + /* + * wait at least 16 clock cycles + */ + udelay(10); + + /* don't leave the MII speed set to zero */ + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + * No MII for 7-wire mode + */ + fec->eth->mii_speed = (((gd->arch.ipb_clk >> 20) / 5) << 1); + } + +#if (DEBUG & 0x3) + printf("Ethernet task stopped\n"); +#endif +} + +#if (DEBUG & 0x60) +/********************************************************************/ + +static void tfifo_print(char *devname, mpc5xxx_fec_priv *fec) +{ + uint16 phyAddr = CONFIG_PHY_ADDR; + uint16 phyStatus; + + if ((fec->eth->tfifo_lrf_ptr != fec->eth->tfifo_lwf_ptr) + || (fec->eth->tfifo_rdptr != fec->eth->tfifo_wrptr)) { + + miiphy_read(devname, phyAddr, 0x1, &phyStatus); + printf("\nphyStatus: 0x%04x\n", phyStatus); + printf("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf("ievent: 0x%08x\n", fec->eth->ievent); + printf("x_status: 0x%08x\n", fec->eth->x_status); + printf("tfifo: status 0x%08x\n", fec->eth->tfifo_status); + + printf(" control 0x%08x\n", fec->eth->tfifo_cntrl); + printf(" lrfp 0x%08x\n", fec->eth->tfifo_lrf_ptr); + printf(" lwfp 0x%08x\n", fec->eth->tfifo_lwf_ptr); + printf(" alarm 0x%08x\n", fec->eth->tfifo_alarm); + printf(" readptr 0x%08x\n", fec->eth->tfifo_rdptr); + printf(" writptr 0x%08x\n", fec->eth->tfifo_wrptr); + } +} + +static void rfifo_print(char *devname, mpc5xxx_fec_priv *fec) +{ + uint16 phyAddr = CONFIG_PHY_ADDR; + uint16 phyStatus; + + if ((fec->eth->rfifo_lrf_ptr != fec->eth->rfifo_lwf_ptr) + || (fec->eth->rfifo_rdptr != fec->eth->rfifo_wrptr)) { + + miiphy_read(devname, phyAddr, 0x1, &phyStatus); + printf("\nphyStatus: 0x%04x\n", phyStatus); + printf("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf("ievent: 0x%08x\n", fec->eth->ievent); + printf("x_status: 0x%08x\n", fec->eth->x_status); + printf("rfifo: status 0x%08x\n", fec->eth->rfifo_status); + + printf(" control 0x%08x\n", fec->eth->rfifo_cntrl); + printf(" lrfp 0x%08x\n", fec->eth->rfifo_lrf_ptr); + printf(" lwfp 0x%08x\n", fec->eth->rfifo_lwf_ptr); + printf(" alarm 0x%08x\n", fec->eth->rfifo_alarm); + printf(" readptr 0x%08x\n", fec->eth->rfifo_rdptr); + printf(" writptr 0x%08x\n", fec->eth->rfifo_wrptr); + } +} +#endif /* DEBUG */ + +/********************************************************************/ + +static int mpc5xxx_fec_send(struct eth_device *dev, void *eth_data, + int data_length) +{ + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + volatile FEC_TBD *pTbd; + +#if (DEBUG & 0x20) + printf("tbd status: 0x%04x\n", fec->tbdBase[0].status); + tfifo_print(dev->name, fec); +#endif + + /* + * Clear Tx BD ring at first + */ + mpc5xxx_fec_tbd_scrub(fec); + + /* + * Check for valid length of data. + */ + if ((data_length > 1500) || (data_length <= 0)) { + return -1; + } + + /* + * Check the number of vacant TxBDs. + */ + if (fec->cleanTbdNum < 1) { +#if (DEBUG & 0x20) + printf("No available TxBDs ...\n"); +#endif + return -1; + } + + /* + * Get the first TxBD to send the mac header + */ + pTbd = &fec->tbdBase[fec->tbdIndex]; + pTbd->dataLength = data_length; + pTbd->dataPointer = (uint32)eth_data; + pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; + +#if (DEBUG & 0x100) + printf("SDMA_TASK_ENABLE, fec->tbdIndex = %d \n", fec->tbdIndex); +#endif + + /* + * Kick the MII i/f + */ + if (fec->xcv_type != SEVENWIRE) { + uint16 phyStatus; + miiphy_read(dev->name, 0, 0x1, &phyStatus); + } + + /* + * Enable SmartDMA transmit task + */ + +#if (DEBUG & 0x20) + tfifo_print(dev->name, fec); +#endif + SDMA_TASK_ENABLE (FEC_XMIT_TASK_NO); +#if (DEBUG & 0x20) + tfifo_print(dev->name, fec); +#endif +#if (DEBUG & 0x8) + printf( "+" ); +#endif + + fec->cleanTbdNum -= 1; + +#if (DEBUG & 0x129) && (DEBUG & 0x80000000) + printf ("smartDMA ethernet Tx task enabled\n"); +#endif + /* + * wait until frame is sent . + */ + while (pTbd->status & FEC_TBD_READY) { + udelay(10); +#if (DEBUG & 0x8) + printf ("TDB status = %04x\n", pTbd->status); +#endif + } + + return 0; +} + + +/********************************************************************/ +static int mpc5xxx_fec_recv(struct eth_device *dev) +{ + /* + * This command pulls one frame from the card + */ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + volatile FEC_RBD *pRbd = &fec->rbdBase[fec->rbdIndex]; + unsigned long ievent; + int frame_length, len = 0; + NBUF *frame; + uchar buff[FEC_MAX_PKT_SIZE]; + +#if (DEBUG & 0x1) + printf ("mpc5xxx_fec_recv %d Start...\n", fec->rbdIndex); +#endif +#if (DEBUG & 0x8) + printf( "-" ); +#endif + + /* + * Check if any critical events have happened + */ + ievent = fec->eth->ievent; + fec->eth->ievent = ievent; + if (ievent & 0x20060000) { + /* BABT, Rx/Tx FIFO errors */ + mpc5xxx_fec_halt(dev); + mpc5xxx_fec_init(dev, NULL); + return 0; + } + if (ievent & 0x80000000) { + /* Heartbeat error */ + fec->eth->x_cntrl |= 0x00000001; + } + if (ievent & 0x10000000) { + /* Graceful stop complete */ + if (fec->eth->x_cntrl & 0x00000001) { + mpc5xxx_fec_halt(dev); + fec->eth->x_cntrl &= ~0x00000001; + mpc5xxx_fec_init(dev, NULL); + } + } + + if (!(pRbd->status & FEC_RBD_EMPTY)) { + if ((pRbd->status & FEC_RBD_LAST) && !(pRbd->status & FEC_RBD_ERR) && + ((pRbd->dataLength - 4) > 14)) { + + /* + * Get buffer address and size + */ + frame = (NBUF *)pRbd->dataPointer; + frame_length = pRbd->dataLength - 4; + +#if (DEBUG & 0x20) + { + int i; + printf("recv data hdr:"); + for (i = 0; i < 14; i++) + printf("%x ", *(frame->head + i)); + printf("\n"); + } +#endif + /* + * Fill the buffer and pass it to upper layers + */ + memcpy(buff, frame->head, 14); + memcpy(buff + 14, frame->data, frame_length); + NetReceive(buff, frame_length); + len = frame_length; + } + /* + * Reset buffer descriptor as empty + */ + mpc5xxx_fec_rbd_clean(fec, pRbd); + } + SDMA_CLEAR_IEVENT (FEC_RECV_TASK_NO); + return len; +} + + +/********************************************************************/ +int mpc5xxx_fec_initialize(bd_t * bis) +{ + mpc5xxx_fec_priv *fec; + struct eth_device *dev; + char *tmp, *end; + char env_enetaddr[6]; + int i; + + fec = (mpc5xxx_fec_priv *)malloc(sizeof(*fec)); + dev = (struct eth_device *)malloc(sizeof(*dev)); + memset(dev, 0, sizeof *dev); + + fec->eth = (ethernet_regs *)MPC5XXX_FEC; + fec->tbdBase = (FEC_TBD *)FEC_BD_BASE; + fec->rbdBase = (FEC_RBD *)(FEC_BD_BASE + FEC_TBD_NUM * sizeof(FEC_TBD)); +#if defined(CONFIG_MPC5xxx_FEC_MII100) + fec->xcv_type = MII100; +#elif defined(CONFIG_MPC5xxx_FEC_MII10) + fec->xcv_type = MII10; +#elif defined(CONFIG_MPC5xxx_FEC_SEVENWIRE) + fec->xcv_type = SEVENWIRE; +#else +#error fec->xcv_type not initialized. +#endif + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + * No MII for 7-wire mode + */ + fec->eth->mii_speed = (((gd->arch.ipb_clk >> 20) / 5) << 1); + } + + dev->priv = (void *)fec; + dev->iobase = MPC5XXX_FEC; + dev->init = mpc5xxx_fec_init; + dev->halt = mpc5xxx_fec_halt; + dev->send = mpc5xxx_fec_send; + dev->recv = mpc5xxx_fec_recv; + + sprintf(dev->name, "FEC"); + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, + fec5xxx_miiphy_read, fec5xxx_miiphy_write); +#endif + + /* + * Try to set the mac address now. The fec mac address is + * a garbage after reset. When not using fec for booting + * the Linux fec driver will try to work with this garbage. + */ + tmp = getenv("ethaddr"); + if (tmp) { + for (i=0; i<6; i++) { + env_enetaddr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end+1 : end; + } + mpc5xxx_fec_set_hwaddr(fec, env_enetaddr); + } + + return 1; +} + +/* MII-interface related functions */ +/********************************************************************/ +int fec5xxx_miiphy_read(const char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal) +{ + ethernet_regs *eth = (ethernet_regs *)MPC5XXX_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | phy | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Read MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear mii interrupt bit + */ + eth->ievent = 0x00800000; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (uint16) eth->mii_data; + + return 0; +} + +/********************************************************************/ +int fec5xxx_miiphy_write(const char *devname, uint8 phyAddr, uint8 regAddr, uint16 data) +{ + ethernet_regs *eth = (ethernet_regs *)MPC5XXX_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Write MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear MII interrupt bit + */ + eth->ievent = 0x00800000; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/mpc5xxx_fec.h b/qemu/roms/u-boot/drivers/net/mpc5xxx_fec.h new file mode 100644 index 000000000..16c3e8e91 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/mpc5xxx_fec.h @@ -0,0 +1,282 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.h + * (C) Copyright Motorola, Inc., 2000 + * + * odin ethernet header file + */ + +#ifndef __MPC5XXX_FEC_H +#define __MPC5XXX_FEC_H + +typedef unsigned long uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +typedef struct ethernet_register_set { + +/* [10:2]addr = 00 */ + +/* Control and status Registers (offset 000-1FF) */ + + volatile uint32 fec_id; /* MBAR_ETH + 0x000 */ + volatile uint32 ievent; /* MBAR_ETH + 0x004 */ + volatile uint32 imask; /* MBAR_ETH + 0x008 */ + + volatile uint32 RES0[1]; /* MBAR_ETH + 0x00C */ + volatile uint32 r_des_active; /* MBAR_ETH + 0x010 */ + volatile uint32 x_des_active; /* MBAR_ETH + 0x014 */ + volatile uint32 r_des_active_cl; /* MBAR_ETH + 0x018 */ + volatile uint32 x_des_active_cl; /* MBAR_ETH + 0x01C */ + volatile uint32 ivent_set; /* MBAR_ETH + 0x020 */ + volatile uint32 ecntrl; /* MBAR_ETH + 0x024 */ + + volatile uint32 RES1[6]; /* MBAR_ETH + 0x028-03C */ + volatile uint32 mii_data; /* MBAR_ETH + 0x040 */ + volatile uint32 mii_speed; /* MBAR_ETH + 0x044 */ + volatile uint32 mii_status; /* MBAR_ETH + 0x048 */ + + volatile uint32 RES2[5]; /* MBAR_ETH + 0x04C-05C */ + volatile uint32 mib_data; /* MBAR_ETH + 0x060 */ + volatile uint32 mib_control; /* MBAR_ETH + 0x064 */ + + volatile uint32 RES3[6]; /* MBAR_ETH + 0x068-7C */ + volatile uint32 r_activate; /* MBAR_ETH + 0x080 */ + volatile uint32 r_cntrl; /* MBAR_ETH + 0x084 */ + volatile uint32 r_hash; /* MBAR_ETH + 0x088 */ + volatile uint32 r_data; /* MBAR_ETH + 0x08C */ + volatile uint32 ar_done; /* MBAR_ETH + 0x090 */ + volatile uint32 r_test; /* MBAR_ETH + 0x094 */ + volatile uint32 r_mib; /* MBAR_ETH + 0x098 */ + volatile uint32 r_da_low; /* MBAR_ETH + 0x09C */ + volatile uint32 r_da_high; /* MBAR_ETH + 0x0A0 */ + + volatile uint32 RES4[7]; /* MBAR_ETH + 0x0A4-0BC */ + volatile uint32 x_activate; /* MBAR_ETH + 0x0C0 */ + volatile uint32 x_cntrl; /* MBAR_ETH + 0x0C4 */ + volatile uint32 backoff; /* MBAR_ETH + 0x0C8 */ + volatile uint32 x_data; /* MBAR_ETH + 0x0CC */ + volatile uint32 x_status; /* MBAR_ETH + 0x0D0 */ + volatile uint32 x_mib; /* MBAR_ETH + 0x0D4 */ + volatile uint32 x_test; /* MBAR_ETH + 0x0D8 */ + volatile uint32 fdxfc_da1; /* MBAR_ETH + 0x0DC */ + volatile uint32 fdxfc_da2; /* MBAR_ETH + 0x0E0 */ + volatile uint32 paddr1; /* MBAR_ETH + 0x0E4 */ + volatile uint32 paddr2; /* MBAR_ETH + 0x0E8 */ + volatile uint32 op_pause; /* MBAR_ETH + 0x0EC */ + + volatile uint32 RES5[4]; /* MBAR_ETH + 0x0F0-0FC */ + volatile uint32 instr_reg; /* MBAR_ETH + 0x100 */ + volatile uint32 context_reg; /* MBAR_ETH + 0x104 */ + volatile uint32 test_cntrl; /* MBAR_ETH + 0x108 */ + volatile uint32 acc_reg; /* MBAR_ETH + 0x10C */ + volatile uint32 ones; /* MBAR_ETH + 0x110 */ + volatile uint32 zeros; /* MBAR_ETH + 0x114 */ + volatile uint32 iaddr1; /* MBAR_ETH + 0x118 */ + volatile uint32 iaddr2; /* MBAR_ETH + 0x11C */ + volatile uint32 gaddr1; /* MBAR_ETH + 0x120 */ + volatile uint32 gaddr2; /* MBAR_ETH + 0x124 */ + volatile uint32 random; /* MBAR_ETH + 0x128 */ + volatile uint32 rand1; /* MBAR_ETH + 0x12C */ + volatile uint32 tmp; /* MBAR_ETH + 0x130 */ + + volatile uint32 RES6[3]; /* MBAR_ETH + 0x134-13C */ + volatile uint32 fifo_id; /* MBAR_ETH + 0x140 */ + volatile uint32 x_wmrk; /* MBAR_ETH + 0x144 */ + volatile uint32 fcntrl; /* MBAR_ETH + 0x148 */ + volatile uint32 r_bound; /* MBAR_ETH + 0x14C */ + volatile uint32 r_fstart; /* MBAR_ETH + 0x150 */ + volatile uint32 r_count; /* MBAR_ETH + 0x154 */ + volatile uint32 r_lag; /* MBAR_ETH + 0x158 */ + volatile uint32 r_read; /* MBAR_ETH + 0x15C */ + volatile uint32 r_write; /* MBAR_ETH + 0x160 */ + volatile uint32 x_count; /* MBAR_ETH + 0x164 */ + volatile uint32 x_lag; /* MBAR_ETH + 0x168 */ + volatile uint32 x_retry; /* MBAR_ETH + 0x16C */ + volatile uint32 x_write; /* MBAR_ETH + 0x170 */ + volatile uint32 x_read; /* MBAR_ETH + 0x174 */ + + volatile uint32 RES7[2]; /* MBAR_ETH + 0x178-17C */ + volatile uint32 fm_cntrl; /* MBAR_ETH + 0x180 */ + volatile uint32 rfifo_data; /* MBAR_ETH + 0x184 */ + volatile uint32 rfifo_status; /* MBAR_ETH + 0x188 */ + volatile uint32 rfifo_cntrl; /* MBAR_ETH + 0x18C */ + volatile uint32 rfifo_lrf_ptr; /* MBAR_ETH + 0x190 */ + volatile uint32 rfifo_lwf_ptr; /* MBAR_ETH + 0x194 */ + volatile uint32 rfifo_alarm; /* MBAR_ETH + 0x198 */ + volatile uint32 rfifo_rdptr; /* MBAR_ETH + 0x19C */ + volatile uint32 rfifo_wrptr; /* MBAR_ETH + 0x1A0 */ + volatile uint32 tfifo_data; /* MBAR_ETH + 0x1A4 */ + volatile uint32 tfifo_status; /* MBAR_ETH + 0x1A8 */ + volatile uint32 tfifo_cntrl; /* MBAR_ETH + 0x1AC */ + volatile uint32 tfifo_lrf_ptr; /* MBAR_ETH + 0x1B0 */ + volatile uint32 tfifo_lwf_ptr; /* MBAR_ETH + 0x1B4 */ + volatile uint32 tfifo_alarm; /* MBAR_ETH + 0x1B8 */ + volatile uint32 tfifo_rdptr; /* MBAR_ETH + 0x1BC */ + volatile uint32 tfifo_wrptr; /* MBAR_ETH + 0x1C0 */ + + volatile uint32 reset_cntrl; /* MBAR_ETH + 0x1C4 */ + volatile uint32 xmit_fsm; /* MBAR_ETH + 0x1C8 */ + + volatile uint32 RES8[3]; /* MBAR_ETH + 0x1CC-1D4 */ + volatile uint32 rdes_data0; /* MBAR_ETH + 0x1D8 */ + volatile uint32 rdes_data1; /* MBAR_ETH + 0x1DC */ + volatile uint32 r_length; /* MBAR_ETH + 0x1E0 */ + volatile uint32 x_length; /* MBAR_ETH + 0x1E4 */ + volatile uint32 x_addr; /* MBAR_ETH + 0x1E8 */ + volatile uint32 cdes_data; /* MBAR_ETH + 0x1EC */ + volatile uint32 status; /* MBAR_ETH + 0x1F0 */ + volatile uint32 dma_control; /* MBAR_ETH + 0x1F4 */ + volatile uint32 des_cmnd; /* MBAR_ETH + 0x1F8 */ + volatile uint32 data; /* MBAR_ETH + 0x1FC */ + +/* MIB COUNTERS (Offset 200-2FF) */ + + volatile uint32 rmon_t_drop; /* MBAR_ETH + 0x200 */ + volatile uint32 rmon_t_packets; /* MBAR_ETH + 0x204 */ + volatile uint32 rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ + volatile uint32 rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ + volatile uint32 rmon_t_crc_align; /* MBAR_ETH + 0x210 */ + volatile uint32 rmon_t_undersize; /* MBAR_ETH + 0x214 */ + volatile uint32 rmon_t_oversize; /* MBAR_ETH + 0x218 */ + volatile uint32 rmon_t_frag; /* MBAR_ETH + 0x21C */ + volatile uint32 rmon_t_jab; /* MBAR_ETH + 0x220 */ + volatile uint32 rmon_t_col; /* MBAR_ETH + 0x224 */ + volatile uint32 rmon_t_p64; /* MBAR_ETH + 0x228 */ + volatile uint32 rmon_t_p65to127; /* MBAR_ETH + 0x22C */ + volatile uint32 rmon_t_p128to255; /* MBAR_ETH + 0x230 */ + volatile uint32 rmon_t_p256to511; /* MBAR_ETH + 0x234 */ + volatile uint32 rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ + volatile uint32 rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */ + volatile uint32 rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ + volatile uint32 rmon_t_octets; /* MBAR_ETH + 0x244 */ + volatile uint32 ieee_t_drop; /* MBAR_ETH + 0x248 */ + volatile uint32 ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ + volatile uint32 ieee_t_1col; /* MBAR_ETH + 0x250 */ + volatile uint32 ieee_t_mcol; /* MBAR_ETH + 0x254 */ + volatile uint32 ieee_t_def; /* MBAR_ETH + 0x258 */ + volatile uint32 ieee_t_lcol; /* MBAR_ETH + 0x25C */ + volatile uint32 ieee_t_excol; /* MBAR_ETH + 0x260 */ + volatile uint32 ieee_t_macerr; /* MBAR_ETH + 0x264 */ + volatile uint32 ieee_t_cserr; /* MBAR_ETH + 0x268 */ + volatile uint32 ieee_t_sqe; /* MBAR_ETH + 0x26C */ + volatile uint32 t_fdxfc; /* MBAR_ETH + 0x270 */ + volatile uint32 ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ + + volatile uint32 RES9[2]; /* MBAR_ETH + 0x278-27C */ + volatile uint32 rmon_r_drop; /* MBAR_ETH + 0x280 */ + volatile uint32 rmon_r_packets; /* MBAR_ETH + 0x284 */ + volatile uint32 rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ + volatile uint32 rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ + volatile uint32 rmon_r_crc_align; /* MBAR_ETH + 0x290 */ + volatile uint32 rmon_r_undersize; /* MBAR_ETH + 0x294 */ + volatile uint32 rmon_r_oversize; /* MBAR_ETH + 0x298 */ + volatile uint32 rmon_r_frag; /* MBAR_ETH + 0x29C */ + volatile uint32 rmon_r_jab; /* MBAR_ETH + 0x2A0 */ + + volatile uint32 rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ + + volatile uint32 rmon_r_p64; /* MBAR_ETH + 0x2A8 */ + volatile uint32 rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ + volatile uint32 rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ + volatile uint32 rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ + volatile uint32 rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ + volatile uint32 rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */ + volatile uint32 rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ + volatile uint32 rmon_r_octets; /* MBAR_ETH + 0x2C4 */ + volatile uint32 ieee_r_drop; /* MBAR_ETH + 0x2C8 */ + volatile uint32 ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ + volatile uint32 ieee_r_crc; /* MBAR_ETH + 0x2D0 */ + volatile uint32 ieee_r_align; /* MBAR_ETH + 0x2D4 */ + volatile uint32 r_macerr; /* MBAR_ETH + 0x2D8 */ + volatile uint32 r_fdxfc; /* MBAR_ETH + 0x2DC */ + volatile uint32 ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ + + volatile uint32 RES10[6]; /* MBAR_ETH + 0x2E4-2FC */ + + volatile uint32 RES11[64]; /* MBAR_ETH + 0x300-3FF */ +} ethernet_regs; + +/* Receive & Transmit Buffer Descriptor definitions */ +typedef struct BufferDescriptor { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_RBD; +typedef struct { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_TBD; + +/* private structure */ +typedef enum { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +} xceiver_type; + +typedef struct { + ethernet_regs *eth; + xceiver_type xcv_type; /* transceiver type */ + FEC_RBD *rbdBase; /* RBD ring */ + FEC_TBD *tbdBase; /* TBD ring */ + uint16 rbdIndex; /* next receive BD to read */ + uint16 tbdIndex; /* next transmit BD to send */ + uint16 usedTbdIndex; /* next transmit BD to clean */ + uint16 cleanTbdNum; /* the number of available transmit BDs */ +} mpc5xxx_fec_priv; + +/* Ethernet parameter area */ +#define FEC_TBD_BASE (FEC_PARAM_BASE + 0x00) +#define FEC_TBD_NEXT (FEC_PARAM_BASE + 0x04) +#define FEC_RBD_BASE (FEC_PARAM_BASE + 0x08) +#define FEC_RBD_NEXT (FEC_PARAM_BASE + 0x0c) + +/* BD Numer definitions */ +#define FEC_TBD_NUM 48 /* The user can adjust this value */ +#define FEC_RBD_NUM 32 /* The user can adjust this value */ + +/* packet size limit */ +#define FEC_MAX_PKT_SIZE 1536 + +/* RBD bits definitions */ +#define FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_RBD_INT 0x1000 /* Interrupt */ +#define FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define FEC_RBD_LG 0x0020 /* Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define FEC_RBD_SH 0x0008 /* Short frame */ +#define FEC_RBD_CR 0x0004 /* CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* TBD bits definitions */ +#define FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_TBD_INT 0x1000 /* Interrupt */ +#define FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __MPC5XXX_FEC_H */ diff --git a/qemu/roms/u-boot/drivers/net/mvgbe.c b/qemu/roms/u-boot/drivers/net/mvgbe.c new file mode 100644 index 000000000..0cd06b6a6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/mvgbe.c @@ -0,0 +1,788 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * (C) Copyright 2003 + * Ingo Assmus + * + * based on - Driver for MV64360X ethernet ports + * Copyright (C) 2002 rabeeh@galileo.co.il + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_KIRKWOOD) +#include +#elif defined(CONFIG_ORION5X) +#include +#elif defined(CONFIG_DOVE) +#include +#endif + +#include "mvgbe.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define MV_PHY_ADR_REQUEST 0xee +#define MVGBE_SMI_REG (((struct mvgbe_registers *)MVGBE0_BASE)->smi) + +#if defined(CONFIG_PHYLIB) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +/* + * smi_reg_read - miiphy_read callback function. + * + * Returns 16bit phy register value, or 0xffff on error + */ +static int smi_reg_read(const char *devname, u8 phy_adr, u8 reg_ofs, u16 * data) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct mvgbe_device *dmvgbe = to_mvgbe(dev); + struct mvgbe_registers *regs = dmvgbe->regs; + u32 smi_reg; + u32 timeout; + + /* Phyadr read request */ + if (phy_adr == MV_PHY_ADR_REQUEST && + reg_ofs == MV_PHY_ADR_REQUEST) { + /* */ + *data = (u16) (MVGBE_REG_RD(regs->phyadr) & PHYADR_MASK); + return 0; + } + /* check parameters */ + if (phy_adr > PHYADR_MASK) { + printf("Err..(%s) Invalid PHY address %d\n", + __FUNCTION__, phy_adr); + return -EFAULT; + } + if (reg_ofs > PHYREG_MASK) { + printf("Err..(%s) Invalid register offset %d\n", + __FUNCTION__, reg_ofs); + return -EFAULT; + } + + timeout = MVGBE_PHY_SMI_TIMEOUT; + /* wait till the SMI is not busy */ + do { + /* read smi register */ + smi_reg = MVGBE_REG_RD(MVGBE_SMI_REG); + if (timeout-- == 0) { + printf("Err..(%s) SMI busy timeout\n", __FUNCTION__); + return -EFAULT; + } + } while (smi_reg & MVGBE_PHY_SMI_BUSY_MASK); + + /* fill the phy address and regiser offset and read opcode */ + smi_reg = (phy_adr << MVGBE_PHY_SMI_DEV_ADDR_OFFS) + | (reg_ofs << MVGBE_SMI_REG_ADDR_OFFS) + | MVGBE_PHY_SMI_OPCODE_READ; + + /* write the smi register */ + MVGBE_REG_WR(MVGBE_SMI_REG, smi_reg); + + /*wait till read value is ready */ + timeout = MVGBE_PHY_SMI_TIMEOUT; + + do { + /* read smi register */ + smi_reg = MVGBE_REG_RD(MVGBE_SMI_REG); + if (timeout-- == 0) { + printf("Err..(%s) SMI read ready timeout\n", + __FUNCTION__); + return -EFAULT; + } + } while (!(smi_reg & MVGBE_PHY_SMI_READ_VALID_MASK)); + + /* Wait for the data to update in the SMI register */ + for (timeout = 0; timeout < MVGBE_PHY_SMI_TIMEOUT; timeout++) + ; + + *data = (u16) (MVGBE_REG_RD(MVGBE_SMI_REG) & MVGBE_PHY_SMI_DATA_MASK); + + debug("%s:(adr %d, off %d) value= %04x\n", __FUNCTION__, phy_adr, + reg_ofs, *data); + + return 0; +} + +/* + * smi_reg_write - imiiphy_write callback function. + * + * Returns 0 if write succeed, -EINVAL on bad parameters + * -ETIME on timeout + */ +static int smi_reg_write(const char *devname, u8 phy_adr, u8 reg_ofs, u16 data) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct mvgbe_device *dmvgbe = to_mvgbe(dev); + struct mvgbe_registers *regs = dmvgbe->regs; + u32 smi_reg; + u32 timeout; + + /* Phyadr write request*/ + if (phy_adr == MV_PHY_ADR_REQUEST && + reg_ofs == MV_PHY_ADR_REQUEST) { + MVGBE_REG_WR(regs->phyadr, data); + return 0; + } + + /* check parameters */ + if (phy_adr > PHYADR_MASK) { + printf("Err..(%s) Invalid phy address\n", __FUNCTION__); + return -EINVAL; + } + if (reg_ofs > PHYREG_MASK) { + printf("Err..(%s) Invalid register offset\n", __FUNCTION__); + return -EINVAL; + } + + /* wait till the SMI is not busy */ + timeout = MVGBE_PHY_SMI_TIMEOUT; + do { + /* read smi register */ + smi_reg = MVGBE_REG_RD(MVGBE_SMI_REG); + if (timeout-- == 0) { + printf("Err..(%s) SMI busy timeout\n", __FUNCTION__); + return -ETIME; + } + } while (smi_reg & MVGBE_PHY_SMI_BUSY_MASK); + + /* fill the phy addr and reg offset and write opcode and data */ + smi_reg = (data << MVGBE_PHY_SMI_DATA_OFFS); + smi_reg |= (phy_adr << MVGBE_PHY_SMI_DEV_ADDR_OFFS) + | (reg_ofs << MVGBE_SMI_REG_ADDR_OFFS); + smi_reg &= ~MVGBE_PHY_SMI_OPCODE_READ; + + /* write the smi register */ + MVGBE_REG_WR(MVGBE_SMI_REG, smi_reg); + + return 0; +} +#endif + +#if defined(CONFIG_PHYLIB) +int mvgbe_phy_read(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr) +{ + u16 data; + int ret; + ret = smi_reg_read(bus->name, phy_addr, reg_addr, &data); + if (ret) + return ret; + return data; +} + +int mvgbe_phy_write(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr, u16 data) +{ + return smi_reg_write(bus->name, phy_addr, reg_addr, data); +} +#endif + +/* Stop and checks all queues */ +static void stop_queue(u32 * qreg) +{ + u32 reg_data; + + reg_data = readl(qreg); + + if (reg_data & 0xFF) { + /* Issue stop command for active channels only */ + writel((reg_data << 8), qreg); + + /* Wait for all queue activity to terminate. */ + do { + /* + * Check port cause register that all queues + * are stopped + */ + reg_data = readl(qreg); + } + while (reg_data & 0xFF); + } +} + +/* + * set_access_control - Config address decode parameters for Ethernet unit + * + * This function configures the address decode parameters for the Gigabit + * Ethernet Controller according the given parameters struct. + * + * @regs Register struct pointer. + * @param Address decode parameter struct. + */ +static void set_access_control(struct mvgbe_registers *regs, + struct mvgbe_winparam *param) +{ + u32 access_prot_reg; + + /* Set access control register */ + access_prot_reg = MVGBE_REG_RD(regs->epap); + /* clear window permission */ + access_prot_reg &= (~(3 << (param->win * 2))); + access_prot_reg |= (param->access_ctrl << (param->win * 2)); + MVGBE_REG_WR(regs->epap, access_prot_reg); + + /* Set window Size reg (SR) */ + MVGBE_REG_WR(regs->barsz[param->win].size, + (((param->size / 0x10000) - 1) << 16)); + + /* Set window Base address reg (BA) */ + MVGBE_REG_WR(regs->barsz[param->win].bar, + (param->target | param->attrib | param->base_addr)); + /* High address remap reg (HARR) */ + if (param->win < 4) + MVGBE_REG_WR(regs->ha_remap[param->win], param->high_addr); + + /* Base address enable reg (BARER) */ + if (param->enable == 1) + MVGBE_REG_BITS_RESET(regs->bare, (1 << param->win)); + else + MVGBE_REG_BITS_SET(regs->bare, (1 << param->win)); +} + +static void set_dram_access(struct mvgbe_registers *regs) +{ + struct mvgbe_winparam win_param; + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + /* Set access parameters for DRAM bank i */ + win_param.win = i; /* Use Ethernet window i */ + /* Window target - DDR */ + win_param.target = MVGBE_TARGET_DRAM; + /* Enable full access */ + win_param.access_ctrl = EWIN_ACCESS_FULL; + win_param.high_addr = 0; + /* Get bank base and size */ + win_param.base_addr = gd->bd->bi_dram[i].start; + win_param.size = gd->bd->bi_dram[i].size; + if (win_param.size == 0) + win_param.enable = 0; + else + win_param.enable = 1; /* Enable the access */ + + /* Enable DRAM bank */ + switch (i) { + case 0: + win_param.attrib = EBAR_DRAM_CS0; + break; + case 1: + win_param.attrib = EBAR_DRAM_CS1; + break; + case 2: + win_param.attrib = EBAR_DRAM_CS2; + break; + case 3: + win_param.attrib = EBAR_DRAM_CS3; + break; + default: + /* invalid bank, disable access */ + win_param.enable = 0; + win_param.attrib = 0; + break; + } + /* Set the access control for address window(EPAPR) RD/WR */ + set_access_control(regs, &win_param); + } +} + +/* + * port_init_mac_tables - Clear all entrance in the UC, SMC and OMC tables + * + * Go through all the DA filter tables (Unicast, Special Multicast & Other + * Multicast) and set each entry to 0. + */ +static void port_init_mac_tables(struct mvgbe_registers *regs) +{ + int table_index; + + /* Clear DA filter unicast table (Ex_dFUT) */ + for (table_index = 0; table_index < 4; ++table_index) + MVGBE_REG_WR(regs->dfut[table_index], 0); + + for (table_index = 0; table_index < 64; ++table_index) { + /* Clear DA filter special multicast table (Ex_dFSMT) */ + MVGBE_REG_WR(regs->dfsmt[table_index], 0); + /* Clear DA filter other multicast table (Ex_dFOMT) */ + MVGBE_REG_WR(regs->dfomt[table_index], 0); + } +} + +/* + * port_uc_addr - This function Set the port unicast address table + * + * This function locates the proper entry in the Unicast table for the + * specified MAC nibble and sets its properties according to function + * parameters. + * This function add/removes MAC addresses from the port unicast address + * table. + * + * @uc_nibble Unicast MAC Address last nibble. + * @option 0 = Add, 1 = remove address. + * + * RETURN: 1 if output succeeded. 0 if option parameter is invalid. + */ +static int port_uc_addr(struct mvgbe_registers *regs, u8 uc_nibble, + int option) +{ + u32 unicast_reg; + u32 tbl_offset; + u32 reg_offset; + + /* Locate the Unicast table entry */ + uc_nibble = (0xf & uc_nibble); + /* Register offset from unicast table base */ + tbl_offset = (uc_nibble / 4); + /* Entry offset within the above register */ + reg_offset = uc_nibble % 4; + + switch (option) { + case REJECT_MAC_ADDR: + /* + * Clear accepts frame bit at specified unicast + * DA table entry + */ + unicast_reg = MVGBE_REG_RD(regs->dfut[tbl_offset]); + unicast_reg &= (0xFF << (8 * reg_offset)); + MVGBE_REG_WR(regs->dfut[tbl_offset], unicast_reg); + break; + case ACCEPT_MAC_ADDR: + /* Set accepts frame bit at unicast DA filter table entry */ + unicast_reg = MVGBE_REG_RD(regs->dfut[tbl_offset]); + unicast_reg &= (0xFF << (8 * reg_offset)); + unicast_reg |= ((0x01 | (RXUQ << 1)) << (8 * reg_offset)); + MVGBE_REG_WR(regs->dfut[tbl_offset], unicast_reg); + break; + default: + return 0; + } + return 1; +} + +/* + * port_uc_addr_set - This function Set the port Unicast address. + */ +static void port_uc_addr_set(struct mvgbe_registers *regs, u8 * p_addr) +{ + u32 mac_h; + u32 mac_l; + + mac_l = (p_addr[4] << 8) | (p_addr[5]); + mac_h = (p_addr[0] << 24) | (p_addr[1] << 16) | (p_addr[2] << 8) | + (p_addr[3] << 0); + + MVGBE_REG_WR(regs->macal, mac_l); + MVGBE_REG_WR(regs->macah, mac_h); + + /* Accept frames of this address */ + port_uc_addr(regs, p_addr[5], ACCEPT_MAC_ADDR); +} + +/* + * mvgbe_init_rx_desc_ring - Curve a Rx chain desc list and buffer in memory. + */ +static void mvgbe_init_rx_desc_ring(struct mvgbe_device *dmvgbe) +{ + struct mvgbe_rxdesc *p_rx_desc; + int i; + + /* initialize the Rx descriptors ring */ + p_rx_desc = dmvgbe->p_rxdesc; + for (i = 0; i < RINGSZ; i++) { + p_rx_desc->cmd_sts = + MVGBE_BUFFER_OWNED_BY_DMA | MVGBE_RX_EN_INTERRUPT; + p_rx_desc->buf_size = PKTSIZE_ALIGN; + p_rx_desc->byte_cnt = 0; + p_rx_desc->buf_ptr = dmvgbe->p_rxbuf + i * PKTSIZE_ALIGN; + if (i == (RINGSZ - 1)) + p_rx_desc->nxtdesc_p = dmvgbe->p_rxdesc; + else { + p_rx_desc->nxtdesc_p = (struct mvgbe_rxdesc *) + ((u32) p_rx_desc + MV_RXQ_DESC_ALIGNED_SIZE); + p_rx_desc = p_rx_desc->nxtdesc_p; + } + } + dmvgbe->p_rxdesc_curr = dmvgbe->p_rxdesc; +} + +static int mvgbe_init(struct eth_device *dev) +{ + struct mvgbe_device *dmvgbe = to_mvgbe(dev); + struct mvgbe_registers *regs = dmvgbe->regs; +#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) && \ + !defined(CONFIG_PHYLIB) && \ + defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN) + int i; +#endif + /* setup RX rings */ + mvgbe_init_rx_desc_ring(dmvgbe); + + /* Clear the ethernet port interrupts */ + MVGBE_REG_WR(regs->ic, 0); + MVGBE_REG_WR(regs->ice, 0); + /* Unmask RX buffer and TX end interrupt */ + MVGBE_REG_WR(regs->pim, INT_CAUSE_UNMASK_ALL); + /* Unmask phy and link status changes interrupts */ + MVGBE_REG_WR(regs->peim, INT_CAUSE_UNMASK_ALL_EXT); + + set_dram_access(regs); + port_init_mac_tables(regs); + port_uc_addr_set(regs, dmvgbe->dev.enetaddr); + + /* Assign port configuration and command. */ + MVGBE_REG_WR(regs->pxc, PRT_CFG_VAL); + MVGBE_REG_WR(regs->pxcx, PORT_CFG_EXTEND_VALUE); + MVGBE_REG_WR(regs->psc0, PORT_SERIAL_CONTROL_VALUE); + + /* Assign port SDMA configuration */ + MVGBE_REG_WR(regs->sdc, PORT_SDMA_CFG_VALUE); + MVGBE_REG_WR(regs->tqx[0].qxttbc, QTKNBKT_DEF_VAL); + MVGBE_REG_WR(regs->tqx[0].tqxtbc, + (QMTBS_DEF_VAL << 16) | QTKNRT_DEF_VAL); + /* Turn off the port/RXUQ bandwidth limitation */ + MVGBE_REG_WR(regs->pmtu, 0); + + /* Set maximum receive buffer to 9700 bytes */ + MVGBE_REG_WR(regs->psc0, MVGBE_MAX_RX_PACKET_9700BYTE + | (MVGBE_REG_RD(regs->psc0) & MRU_MASK)); + + /* Enable port initially */ + MVGBE_REG_BITS_SET(regs->psc0, MVGBE_SERIAL_PORT_EN); + + /* + * Set ethernet MTU for leaky bucket mechanism to 0 - this will + * disable the leaky bucket mechanism . + */ + MVGBE_REG_WR(regs->pmtu, 0); + + /* Assignment of Rx CRDB of given RXUQ */ + MVGBE_REG_WR(regs->rxcdp[RXUQ], (u32) dmvgbe->p_rxdesc_curr); + /* ensure previous write is done before enabling Rx DMA */ + isb(); + /* Enable port Rx. */ + MVGBE_REG_WR(regs->rqc, (1 << RXUQ)); + +#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) && \ + !defined(CONFIG_PHYLIB) && \ + defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN) + /* Wait up to 5s for the link status */ + for (i = 0; i < 5; i++) { + u16 phyadr; + + miiphy_read(dev->name, MV_PHY_ADR_REQUEST, + MV_PHY_ADR_REQUEST, &phyadr); + /* Return if we get link up */ + if (miiphy_link(dev->name, phyadr)) + return 0; + udelay(1000000); + } + + printf("No link on %s\n", dev->name); + return -1; +#endif + return 0; +} + +static int mvgbe_halt(struct eth_device *dev) +{ + struct mvgbe_device *dmvgbe = to_mvgbe(dev); + struct mvgbe_registers *regs = dmvgbe->regs; + + /* Disable all gigE address decoder */ + MVGBE_REG_WR(regs->bare, 0x3f); + + stop_queue(®s->tqc); + stop_queue(®s->rqc); + + /* Disable port */ + MVGBE_REG_BITS_RESET(regs->psc0, MVGBE_SERIAL_PORT_EN); + /* Set port is not reset */ + MVGBE_REG_BITS_RESET(regs->psc1, 1 << 4); +#ifdef CONFIG_SYS_MII_MODE + /* Set MMI interface up */ + MVGBE_REG_BITS_RESET(regs->psc1, 1 << 3); +#endif + /* Disable & mask ethernet port interrupts */ + MVGBE_REG_WR(regs->ic, 0); + MVGBE_REG_WR(regs->ice, 0); + MVGBE_REG_WR(regs->pim, 0); + MVGBE_REG_WR(regs->peim, 0); + + return 0; +} + +static int mvgbe_write_hwaddr(struct eth_device *dev) +{ + struct mvgbe_device *dmvgbe = to_mvgbe(dev); + struct mvgbe_registers *regs = dmvgbe->regs; + + /* Programs net device MAC address after initialization */ + port_uc_addr_set(regs, dmvgbe->dev.enetaddr); + return 0; +} + +static int mvgbe_send(struct eth_device *dev, void *dataptr, int datasize) +{ + struct mvgbe_device *dmvgbe = to_mvgbe(dev); + struct mvgbe_registers *regs = dmvgbe->regs; + struct mvgbe_txdesc *p_txdesc = dmvgbe->p_txdesc; + void *p = (void *)dataptr; + u32 cmd_sts; + u32 txuq0_reg_addr; + + /* Copy buffer if it's misaligned */ + if ((u32) dataptr & 0x07) { + if (datasize > PKTSIZE_ALIGN) { + printf("Non-aligned data too large (%d)\n", + datasize); + return -1; + } + + memcpy(dmvgbe->p_aligned_txbuf, p, datasize); + p = dmvgbe->p_aligned_txbuf; + } + + p_txdesc->cmd_sts = MVGBE_ZERO_PADDING | MVGBE_GEN_CRC; + p_txdesc->cmd_sts |= MVGBE_TX_FIRST_DESC | MVGBE_TX_LAST_DESC; + p_txdesc->cmd_sts |= MVGBE_BUFFER_OWNED_BY_DMA; + p_txdesc->cmd_sts |= MVGBE_TX_EN_INTERRUPT; + p_txdesc->buf_ptr = (u8 *) p; + p_txdesc->byte_cnt = datasize; + + /* Set this tc desc as zeroth TXUQ */ + txuq0_reg_addr = (u32)®s->tcqdp[TXUQ]; + writel((u32) p_txdesc, txuq0_reg_addr); + + /* ensure tx desc writes above are performed before we start Tx DMA */ + isb(); + + /* Apply send command using zeroth TXUQ */ + MVGBE_REG_WR(regs->tqc, (1 << TXUQ)); + + /* + * wait for packet xmit completion + */ + cmd_sts = readl(&p_txdesc->cmd_sts); + while (cmd_sts & MVGBE_BUFFER_OWNED_BY_DMA) { + /* return fail if error is detected */ + if ((cmd_sts & (MVGBE_ERROR_SUMMARY | MVGBE_TX_LAST_FRAME)) == + (MVGBE_ERROR_SUMMARY | MVGBE_TX_LAST_FRAME) && + cmd_sts & (MVGBE_UR_ERROR | MVGBE_RL_ERROR)) { + printf("Err..(%s) in xmit packet\n", __FUNCTION__); + return -1; + } + cmd_sts = readl(&p_txdesc->cmd_sts); + }; + return 0; +} + +static int mvgbe_recv(struct eth_device *dev) +{ + struct mvgbe_device *dmvgbe = to_mvgbe(dev); + struct mvgbe_rxdesc *p_rxdesc_curr = dmvgbe->p_rxdesc_curr; + u32 cmd_sts; + u32 timeout = 0; + u32 rxdesc_curr_addr; + + /* wait untill rx packet available or timeout */ + do { + if (timeout < MVGBE_PHY_SMI_TIMEOUT) + timeout++; + else { + debug("%s time out...\n", __FUNCTION__); + return -1; + } + } while (readl(&p_rxdesc_curr->cmd_sts) & MVGBE_BUFFER_OWNED_BY_DMA); + + if (p_rxdesc_curr->byte_cnt != 0) { + debug("%s: Received %d byte Packet @ 0x%x (cmd_sts= %08x)\n", + __FUNCTION__, (u32) p_rxdesc_curr->byte_cnt, + (u32) p_rxdesc_curr->buf_ptr, + (u32) p_rxdesc_curr->cmd_sts); + } + + /* + * In case received a packet without first/last bits on + * OR the error summary bit is on, + * the packets needs to be dropeed. + */ + cmd_sts = readl(&p_rxdesc_curr->cmd_sts); + + if ((cmd_sts & + (MVGBE_RX_FIRST_DESC | MVGBE_RX_LAST_DESC)) + != (MVGBE_RX_FIRST_DESC | MVGBE_RX_LAST_DESC)) { + + printf("Err..(%s) Dropping packet spread on" + " multiple descriptors\n", __FUNCTION__); + + } else if (cmd_sts & MVGBE_ERROR_SUMMARY) { + + printf("Err..(%s) Dropping packet with errors\n", + __FUNCTION__); + + } else { + /* !!! call higher layer processing */ + debug("%s: Sending Received packet to" + " upper layer (NetReceive)\n", __FUNCTION__); + + /* let the upper layer handle the packet */ + NetReceive((p_rxdesc_curr->buf_ptr + RX_BUF_OFFSET), + (int)(p_rxdesc_curr->byte_cnt - RX_BUF_OFFSET)); + } + /* + * free these descriptors and point next in the ring + */ + p_rxdesc_curr->cmd_sts = + MVGBE_BUFFER_OWNED_BY_DMA | MVGBE_RX_EN_INTERRUPT; + p_rxdesc_curr->buf_size = PKTSIZE_ALIGN; + p_rxdesc_curr->byte_cnt = 0; + + rxdesc_curr_addr = (u32)&dmvgbe->p_rxdesc_curr; + writel((unsigned)p_rxdesc_curr->nxtdesc_p, rxdesc_curr_addr); + + return 0; +} + +#if defined(CONFIG_PHYLIB) +int mvgbe_phylib_init(struct eth_device *dev, int phyid) +{ + struct mii_dev *bus; + struct phy_device *phydev; + int ret; + + bus = mdio_alloc(); + if (!bus) { + printf("mdio_alloc failed\n"); + return -ENOMEM; + } + bus->read = mvgbe_phy_read; + bus->write = mvgbe_phy_write; + sprintf(bus->name, dev->name); + + ret = mdio_register(bus); + if (ret) { + printf("mdio_register failed\n"); + free(bus); + return -ENOMEM; + } + + /* Set phy address of the port */ + mvgbe_phy_write(bus, MV_PHY_ADR_REQUEST, 0, MV_PHY_ADR_REQUEST, phyid); + + phydev = phy_connect(bus, phyid, dev, PHY_INTERFACE_MODE_RGMII); + if (!phydev) { + printf("phy_connect failed\n"); + return -ENODEV; + } + + phy_config(phydev); + phy_startup(phydev); + + return 0; +} +#endif + +int mvgbe_initialize(bd_t *bis) +{ + struct mvgbe_device *dmvgbe; + struct eth_device *dev; + int devnum; + u8 used_ports[MAX_MVGBE_DEVS] = CONFIG_MVGBE_PORTS; + + for (devnum = 0; devnum < MAX_MVGBE_DEVS; devnum++) { + /*skip if port is configured not to use */ + if (used_ports[devnum] == 0) + continue; + + dmvgbe = malloc(sizeof(struct mvgbe_device)); + + if (!dmvgbe) + goto error1; + + memset(dmvgbe, 0, sizeof(struct mvgbe_device)); + + dmvgbe->p_rxdesc = + (struct mvgbe_rxdesc *)memalign(PKTALIGN, + MV_RXQ_DESC_ALIGNED_SIZE*RINGSZ + 1); + + if (!dmvgbe->p_rxdesc) + goto error2; + + dmvgbe->p_rxbuf = (u8 *) memalign(PKTALIGN, + RINGSZ*PKTSIZE_ALIGN + 1); + + if (!dmvgbe->p_rxbuf) + goto error3; + + dmvgbe->p_aligned_txbuf = memalign(8, PKTSIZE_ALIGN); + + if (!dmvgbe->p_aligned_txbuf) + goto error4; + + dmvgbe->p_txdesc = (struct mvgbe_txdesc *) memalign( + PKTALIGN, sizeof(struct mvgbe_txdesc) + 1); + + if (!dmvgbe->p_txdesc) { + free(dmvgbe->p_aligned_txbuf); +error4: + free(dmvgbe->p_rxbuf); +error3: + free(dmvgbe->p_rxdesc); +error2: + free(dmvgbe); +error1: + printf("Err.. %s Failed to allocate memory\n", + __FUNCTION__); + return -1; + } + + dev = &dmvgbe->dev; + + /* must be less than sizeof(dev->name) */ + sprintf(dev->name, "egiga%d", devnum); + + switch (devnum) { + case 0: + dmvgbe->regs = (void *)MVGBE0_BASE; + break; +#if defined(MVGBE1_BASE) + case 1: + dmvgbe->regs = (void *)MVGBE1_BASE; + break; +#endif + default: /* this should never happen */ + printf("Err..(%s) Invalid device number %d\n", + __FUNCTION__, devnum); + return -1; + } + + dev->init = (void *)mvgbe_init; + dev->halt = (void *)mvgbe_halt; + dev->send = (void *)mvgbe_send; + dev->recv = (void *)mvgbe_recv; + dev->write_hwaddr = (void *)mvgbe_write_hwaddr; + + eth_register(dev); + +#if defined(CONFIG_PHYLIB) + mvgbe_phylib_init(dev, PHY_BASE_ADR + devnum); +#elif defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, smi_reg_read, smi_reg_write); + /* Set phy address of the port */ + miiphy_write(dev->name, MV_PHY_ADR_REQUEST, + MV_PHY_ADR_REQUEST, PHY_BASE_ADR + devnum); +#endif + } + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/mvgbe.h b/qemu/roms/u-boot/drivers/net/mvgbe.h new file mode 100644 index 000000000..27a3f41e8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/mvgbe.h @@ -0,0 +1,498 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * based on - Driver for MV64360X ethernet ports + * Copyright (C) 2002 rabeeh@galileo.co.il + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MVGBE_H__ +#define __MVGBE_H__ + +/* PHY_BASE_ADR is board specific and can be configured */ +#if defined (CONFIG_PHY_BASE_ADR) +#define PHY_BASE_ADR CONFIG_PHY_BASE_ADR +#else +#define PHY_BASE_ADR 0x08 /* default phy base addr */ +#endif + +/* Constants */ +#define INT_CAUSE_UNMASK_ALL 0x0007ffff +#define INT_CAUSE_UNMASK_ALL_EXT 0x0011ffff +#define MRU_MASK 0xfff1ffff +#define PHYADR_MASK 0x0000001f +#define PHYREG_MASK 0x0000001f +#define QTKNBKT_DEF_VAL 0x3fffffff +#define QMTBS_DEF_VAL 0x000003ff +#define QTKNRT_DEF_VAL 0x0000fcff +#define RXUQ 0 /* Used Rx queue */ +#define TXUQ 0 /* Used Rx queue */ + +#define to_mvgbe(_d) container_of(_d, struct mvgbe_device, dev) +#define MVGBE_REG_WR(adr, val) writel(val, &adr) +#define MVGBE_REG_RD(adr) readl(&adr) +#define MVGBE_REG_BITS_RESET(adr, val) writel(readl(&adr) & ~(val), &adr) +#define MVGBE_REG_BITS_SET(adr, val) writel(readl(&adr) | val, &adr) + +/* Default port configuration value */ +#define PRT_CFG_VAL ( \ + MVGBE_UCAST_MOD_NRML | \ + MVGBE_DFLT_RXQ(RXUQ) | \ + MVGBE_DFLT_RX_ARPQ(RXUQ) | \ + MVGBE_RX_BC_IF_NOT_IP_OR_ARP | \ + MVGBE_RX_BC_IF_IP | \ + MVGBE_RX_BC_IF_ARP | \ + MVGBE_CPTR_TCP_FRMS_DIS | \ + MVGBE_CPTR_UDP_FRMS_DIS | \ + MVGBE_DFLT_RX_TCPQ(RXUQ) | \ + MVGBE_DFLT_RX_UDPQ(RXUQ) | \ + MVGBE_DFLT_RX_BPDUQ(RXUQ)) + +/* Default port extend configuration value */ +#define PORT_CFG_EXTEND_VALUE \ + MVGBE_SPAN_BPDU_PACKETS_AS_NORMAL | \ + MVGBE_PARTITION_DIS | \ + MVGBE_TX_CRC_GENERATION_EN + +#define GT_MVGBE_IPG_INT_RX(value) ((value & 0x3fff) << 8) + +/* Default sdma control value */ +#define PORT_SDMA_CFG_VALUE ( \ + MVGBE_RX_BURST_SIZE_16_64BIT | \ + MVGBE_BLM_RX_NO_SWAP | \ + MVGBE_BLM_TX_NO_SWAP | \ + GT_MVGBE_IPG_INT_RX(RXUQ) | \ + MVGBE_TX_BURST_SIZE_16_64BIT) + +/* Default port serial control value */ +#ifndef PORT_SERIAL_CONTROL_VALUE +#define PORT_SERIAL_CONTROL_VALUE ( \ + MVGBE_FORCE_LINK_PASS | \ + MVGBE_DIS_AUTO_NEG_FOR_DUPLX | \ + MVGBE_DIS_AUTO_NEG_FOR_FLOW_CTRL | \ + MVGBE_ADV_NO_FLOW_CTRL | \ + MVGBE_FORCE_FC_MODE_NO_PAUSE_DIS_TX | \ + MVGBE_FORCE_BP_MODE_NO_JAM | \ + (1 << 9) /* Reserved bit has to be 1 */ | \ + MVGBE_DO_NOT_FORCE_LINK_FAIL | \ + MVGBE_EN_AUTO_NEG_SPEED_GMII | \ + MVGBE_DTE_ADV_0 | \ + MVGBE_MIIPHY_MAC_MODE | \ + MVGBE_AUTO_NEG_NO_CHANGE | \ + MVGBE_MAX_RX_PACKET_1552BYTE | \ + MVGBE_CLR_EXT_LOOPBACK | \ + MVGBE_SET_FULL_DUPLEX_MODE | \ + MVGBE_DIS_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX) +#endif + +/* Tx WRR confoguration macros */ +#define PORT_MAX_TRAN_UNIT 0x24 /* MTU register (default) 9KByte */ +#define PORT_MAX_TOKEN_BUCKET_SIZE 0x_FFFF /* PMTBS reg (default) */ +#define PORT_TOKEN_RATE 1023 /* PTTBRC reg (default) */ +/* MAC accepet/reject macros */ +#define ACCEPT_MAC_ADDR 0 +#define REJECT_MAC_ADDR 1 +/* Size of a Tx/Rx descriptor used in chain list data structure */ +#define MV_RXQ_DESC_ALIGNED_SIZE \ + (((sizeof(struct mvgbe_rxdesc) / PKTALIGN) + 1) * PKTALIGN) +/* Buffer offset from buffer pointer */ +#define RX_BUF_OFFSET 0x2 + +/* Port serial status reg (PSR) */ +#define MVGBE_INTERFACE_GMII_MII 0 +#define MVGBE_INTERFACE_PCM 1 +#define MVGBE_LINK_IS_DOWN 0 +#define MVGBE_LINK_IS_UP (1 << 1) +#define MVGBE_PORT_AT_HALF_DUPLEX 0 +#define MVGBE_PORT_AT_FULL_DUPLEX (1 << 2) +#define MVGBE_RX_FLOW_CTRL_DISD 0 +#define MVGBE_RX_FLOW_CTRL_ENBALED (1 << 3) +#define MVGBE_GMII_SPEED_100_10 0 +#define MVGBE_GMII_SPEED_1000 (1 << 4) +#define MVGBE_MII_SPEED_10 0 +#define MVGBE_MII_SPEED_100 (1 << 5) +#define MVGBE_NO_TX 0 +#define MVGBE_TX_IN_PROGRESS (1 << 7) +#define MVGBE_BYPASS_NO_ACTIVE 0 +#define MVGBE_BYPASS_ACTIVE (1 << 8) +#define MVGBE_PORT_NOT_AT_PARTN_STT 0 +#define MVGBE_PORT_AT_PARTN_STT (1 << 9) +#define MVGBE_PORT_TX_FIFO_NOT_EMPTY 0 +#define MVGBE_PORT_TX_FIFO_EMPTY (1 << 10) + +/* These macros describes the Port configuration reg (Px_cR) bits */ +#define MVGBE_UCAST_MOD_NRML 0 +#define MVGBE_UNICAST_PROMISCUOUS_MODE 1 +#define MVGBE_DFLT_RXQ(_x) (_x << 1) +#define MVGBE_DFLT_RX_ARPQ(_x) (_x << 4) +#define MVGBE_RX_BC_IF_NOT_IP_OR_ARP 0 +#define MVGBE_REJECT_BC_IF_NOT_IP_OR_ARP (1 << 7) +#define MVGBE_RX_BC_IF_IP 0 +#define MVGBE_REJECT_BC_IF_IP (1 << 8) +#define MVGBE_RX_BC_IF_ARP 0 +#define MVGBE_REJECT_BC_IF_ARP (1 << 9) +#define MVGBE_TX_AM_NO_UPDATE_ERR_SMRY (1 << 12) +#define MVGBE_CPTR_TCP_FRMS_DIS 0 +#define MVGBE_CPTR_TCP_FRMS_EN (1 << 14) +#define MVGBE_CPTR_UDP_FRMS_DIS 0 +#define MVGBE_CPTR_UDP_FRMS_EN (1 << 15) +#define MVGBE_DFLT_RX_TCPQ(_x) (_x << 16) +#define MVGBE_DFLT_RX_UDPQ(_x) (_x << 19) +#define MVGBE_DFLT_RX_BPDUQ(_x) (_x << 22) +#define MVGBE_DFLT_RX_TCP_CHKSUM_MODE (1 << 25) + +/* These macros describes the Port configuration extend reg (Px_cXR) bits*/ +#define MVGBE_CLASSIFY_EN 1 +#define MVGBE_SPAN_BPDU_PACKETS_AS_NORMAL 0 +#define MVGBE_SPAN_BPDU_PACKETS_TO_RX_Q7 (1 << 1) +#define MVGBE_PARTITION_DIS 0 +#define MVGBE_PARTITION_EN (1 << 2) +#define MVGBE_TX_CRC_GENERATION_EN 0 +#define MVGBE_TX_CRC_GENERATION_DIS (1 << 3) + +/* These macros describes the Port Sdma configuration reg (SDCR) bits */ +#define MVGBE_RIFB 1 +#define MVGBE_RX_BURST_SIZE_1_64BIT 0 +#define MVGBE_RX_BURST_SIZE_2_64BIT (1 << 1) +#define MVGBE_RX_BURST_SIZE_4_64BIT (1 << 2) +#define MVGBE_RX_BURST_SIZE_8_64BIT ((1 << 2) | (1 << 1)) +#define MVGBE_RX_BURST_SIZE_16_64BIT (1 << 3) +#define MVGBE_BLM_RX_NO_SWAP (1 << 4) +#define MVGBE_BLM_RX_BYTE_SWAP 0 +#define MVGBE_BLM_TX_NO_SWAP (1 << 5) +#define MVGBE_BLM_TX_BYTE_SWAP 0 +#define MVGBE_DESCRIPTORS_BYTE_SWAP (1 << 6) +#define MVGBE_DESCRIPTORS_NO_SWAP 0 +#define MVGBE_TX_BURST_SIZE_1_64BIT 0 +#define MVGBE_TX_BURST_SIZE_2_64BIT (1 << 22) +#define MVGBE_TX_BURST_SIZE_4_64BIT (1 << 23) +#define MVGBE_TX_BURST_SIZE_8_64BIT ((1 << 23) | (1 << 22)) +#define MVGBE_TX_BURST_SIZE_16_64BIT (1 << 24) + +/* These macros describes the Port serial control reg (PSCR) bits */ +#define MVGBE_SERIAL_PORT_DIS 0 +#define MVGBE_SERIAL_PORT_EN 1 +#define MVGBE_FORCE_LINK_PASS (1 << 1) +#define MVGBE_DO_NOT_FORCE_LINK_PASS 0 +#define MVGBE_EN_AUTO_NEG_FOR_DUPLX 0 +#define MVGBE_DIS_AUTO_NEG_FOR_DUPLX (1 << 2) +#define MVGBE_EN_AUTO_NEG_FOR_FLOW_CTRL 0 +#define MVGBE_DIS_AUTO_NEG_FOR_FLOW_CTRL (1 << 3) +#define MVGBE_ADV_NO_FLOW_CTRL 0 +#define MVGBE_ADV_SYMMETRIC_FLOW_CTRL (1 << 4) +#define MVGBE_FORCE_FC_MODE_NO_PAUSE_DIS_TX 0 +#define MVGBE_FORCE_FC_MODE_TX_PAUSE_DIS (1 << 5) +#define MVGBE_FORCE_BP_MODE_NO_JAM 0 +#define MVGBE_FORCE_BP_MODE_JAM_TX (1 << 7) +#define MVGBE_FORCE_BP_MODE_JAM_TX_ON_RX_ERR (1 << 8) +#define MVGBE_FORCE_LINK_FAIL 0 +#define MVGBE_DO_NOT_FORCE_LINK_FAIL (1 << 10) +#define MVGBE_DIS_AUTO_NEG_SPEED_GMII (1 << 13) +#define MVGBE_EN_AUTO_NEG_SPEED_GMII 0 +#define MVGBE_DTE_ADV_0 0 +#define MVGBE_DTE_ADV_1 (1 << 14) +#define MVGBE_MIIPHY_MAC_MODE 0 +#define MVGBE_MIIPHY_PHY_MODE (1 << 15) +#define MVGBE_AUTO_NEG_NO_CHANGE 0 +#define MVGBE_RESTART_AUTO_NEG (1 << 16) +#define MVGBE_MAX_RX_PACKET_1518BYTE 0 +#define MVGBE_MAX_RX_PACKET_1522BYTE (1 << 17) +#define MVGBE_MAX_RX_PACKET_1552BYTE (1 << 18) +#define MVGBE_MAX_RX_PACKET_9022BYTE ((1 << 18) | (1 << 17)) +#define MVGBE_MAX_RX_PACKET_9192BYTE (1 << 19) +#define MVGBE_MAX_RX_PACKET_9700BYTE ((1 << 19) | (1 << 17)) +#define MVGBE_SET_EXT_LOOPBACK (1 << 20) +#define MVGBE_CLR_EXT_LOOPBACK 0 +#define MVGBE_SET_FULL_DUPLEX_MODE (1 << 21) +#define MVGBE_SET_HALF_DUPLEX_MODE 0 +#define MVGBE_EN_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX (1 << 22) +#define MVGBE_DIS_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX 0 +#define MVGBE_SET_GMII_SPEED_TO_10_100 0 +#define MVGBE_SET_GMII_SPEED_TO_1000 (1 << 23) +#define MVGBE_SET_MII_SPEED_TO_10 0 +#define MVGBE_SET_MII_SPEED_TO_100 (1 << 24) + +/* SMI register fields */ +#define MVGBE_PHY_SMI_TIMEOUT 10000 +#define MVGBE_PHY_SMI_DATA_OFFS 0 /* Data */ +#define MVGBE_PHY_SMI_DATA_MASK (0xffff << MVGBE_PHY_SMI_DATA_OFFS) +#define MVGBE_PHY_SMI_DEV_ADDR_OFFS 16 /* PHY device address */ +#define MVGBE_PHY_SMI_DEV_ADDR_MASK \ + (PHYADR_MASK << MVGBE_PHY_SMI_DEV_ADDR_OFFS) +#define MVGBE_SMI_REG_ADDR_OFFS 21 /* PHY device reg addr */ +#define MVGBE_SMI_REG_ADDR_MASK \ + (PHYADR_MASK << MVGBE_SMI_REG_ADDR_OFFS) +#define MVGBE_PHY_SMI_OPCODE_OFFS 26 /* Write/Read opcode */ +#define MVGBE_PHY_SMI_OPCODE_MASK (3 << MVGBE_PHY_SMI_OPCODE_OFFS) +#define MVGBE_PHY_SMI_OPCODE_WRITE (0 << MVGBE_PHY_SMI_OPCODE_OFFS) +#define MVGBE_PHY_SMI_OPCODE_READ (1 << MVGBE_PHY_SMI_OPCODE_OFFS) +#define MVGBE_PHY_SMI_READ_VALID_MASK (1 << 27) /* Read Valid */ +#define MVGBE_PHY_SMI_BUSY_MASK (1 << 28) /* Busy */ + +/* SDMA command status fields macros */ +/* Tx & Rx descriptors status */ +#define MVGBE_ERROR_SUMMARY 1 +/* Tx & Rx descriptors command */ +#define MVGBE_BUFFER_OWNED_BY_DMA (1 << 31) +/* Tx descriptors status */ +#define MVGBE_LC_ERROR 0 +#define MVGBE_UR_ERROR (1 << 1) +#define MVGBE_RL_ERROR (1 << 2) +#define MVGBE_LLC_SNAP_FORMAT (1 << 9) +#define MVGBE_TX_LAST_FRAME (1 << 20) + +/* Rx descriptors status */ +#define MVGBE_CRC_ERROR 0 +#define MVGBE_OVERRUN_ERROR (1 << 1) +#define MVGBE_MAX_FRAME_LENGTH_ERROR (1 << 2) +#define MVGBE_RESOURCE_ERROR ((1 << 2) | (1 << 1)) +#define MVGBE_VLAN_TAGGED (1 << 19) +#define MVGBE_BPDU_FRAME (1 << 20) +#define MVGBE_TCP_FRAME_OVER_IP_V_4 0 +#define MVGBE_UDP_FRAME_OVER_IP_V_4 (1 << 21) +#define MVGBE_OTHER_FRAME_TYPE (1 << 22) +#define MVGBE_LAYER_2_IS_MVGBE_V_2 (1 << 23) +#define MVGBE_FRAME_TYPE_IP_V_4 (1 << 24) +#define MVGBE_FRAME_HEADER_OK (1 << 25) +#define MVGBE_RX_LAST_DESC (1 << 26) +#define MVGBE_RX_FIRST_DESC (1 << 27) +#define MVGBE_UNKNOWN_DESTINATION_ADDR (1 << 28) +#define MVGBE_RX_EN_INTERRUPT (1 << 29) +#define MVGBE_LAYER_4_CHECKSUM_OK (1 << 30) + +/* Rx descriptors byte count */ +#define MVGBE_FRAME_FRAGMENTED (1 << 2) + +/* Tx descriptors command */ +#define MVGBE_LAYER_4_CHECKSUM_FIRST_DESC (1 << 10) +#define MVGBE_FRAME_SET_TO_VLAN (1 << 15) +#define MVGBE_TCP_FRAME 0 +#define MVGBE_UDP_FRAME (1 << 16) +#define MVGBE_GEN_TCP_UDP_CHECKSUM (1 << 17) +#define MVGBE_GEN_IP_V_4_CHECKSUM (1 << 18) +#define MVGBE_ZERO_PADDING (1 << 19) +#define MVGBE_TX_LAST_DESC (1 << 20) +#define MVGBE_TX_FIRST_DESC (1 << 21) +#define MVGBE_GEN_CRC (1 << 22) +#define MVGBE_TX_EN_INTERRUPT (1 << 23) +#define MVGBE_AUTO_MODE (1 << 30) + +/* Address decode parameters */ +/* Ethernet Base Address Register bits */ +#define EBAR_TARGET_DRAM 0x00000000 +#define EBAR_TARGET_DEVICE 0x00000001 +#define EBAR_TARGET_CBS 0x00000002 +#define EBAR_TARGET_PCI0 0x00000003 +#define EBAR_TARGET_PCI1 0x00000004 +#define EBAR_TARGET_CUNIT 0x00000005 +#define EBAR_TARGET_AUNIT 0x00000006 +#define EBAR_TARGET_GUNIT 0x00000007 + +/* Window attrib */ +#if defined(CONFIG_DOVE) +#define EBAR_DRAM_CS0 0x00000000 +#define EBAR_DRAM_CS1 0x00000000 +#define EBAR_DRAM_CS2 0x00000000 +#define EBAR_DRAM_CS3 0x00000000 +#else +#define EBAR_DRAM_CS0 0x00000E00 +#define EBAR_DRAM_CS1 0x00000D00 +#define EBAR_DRAM_CS2 0x00000B00 +#define EBAR_DRAM_CS3 0x00000700 +#endif + +/* DRAM Target interface */ +#define EBAR_DRAM_NO_CACHE_COHERENCY 0x00000000 +#define EBAR_DRAM_CACHE_COHERENCY_WT 0x00001000 +#define EBAR_DRAM_CACHE_COHERENCY_WB 0x00002000 + +/* Device Bus Target interface */ +#define EBAR_DEVICE_DEVCS0 0x00001E00 +#define EBAR_DEVICE_DEVCS1 0x00001D00 +#define EBAR_DEVICE_DEVCS2 0x00001B00 +#define EBAR_DEVICE_DEVCS3 0x00001700 +#define EBAR_DEVICE_BOOTCS3 0x00000F00 + +/* PCI Target interface */ +#define EBAR_PCI_BYTE_SWAP 0x00000000 +#define EBAR_PCI_NO_SWAP 0x00000100 +#define EBAR_PCI_BYTE_WORD_SWAP 0x00000200 +#define EBAR_PCI_WORD_SWAP 0x00000300 +#define EBAR_PCI_NO_SNOOP_NOT_ASSERT 0x00000000 +#define EBAR_PCI_NO_SNOOP_ASSERT 0x00000400 +#define EBAR_PCI_IO_SPACE 0x00000000 +#define EBAR_PCI_MEMORY_SPACE 0x00000800 +#define EBAR_PCI_REQ64_FORCE 0x00000000 +#define EBAR_PCI_REQ64_SIZE 0x00001000 + +/* Window access control */ +#define EWIN_ACCESS_NOT_ALLOWED 0 +#define EWIN_ACCESS_READ_ONLY 1 +#define EWIN_ACCESS_FULL ((1 << 1) | 1) + +/* structures represents Controller registers */ +struct mvgbe_barsz { + u32 bar; + u32 size; +}; + +struct mvgbe_rxcdp { + struct mvgbe_rxdesc *rxcdp; + u32 rxcdp_pad[3]; +}; + +struct mvgbe_tqx { + u32 qxttbc; + u32 tqxtbc; + u32 tqxac; + u32 tqxpad; +}; + +struct mvgbe_registers { + u32 phyadr; + u32 smi; + u32 euda; + u32 eudid; + u8 pad1[0x080 - 0x00c - 4]; + u32 euic; + u32 euim; + u8 pad2[0x094 - 0x084 - 4]; + u32 euea; + u32 euiae; + u8 pad3[0x0b0 - 0x098 - 4]; + u32 euc; + u8 pad3a[0x200 - 0x0b0 - 4]; + struct mvgbe_barsz barsz[6]; + u8 pad4[0x280 - 0x22c - 4]; + u32 ha_remap[4]; + u32 bare; + u32 epap; + u8 pad5[0x400 - 0x294 - 4]; + u32 pxc; + u32 pxcx; + u32 mii_ser_params; + u8 pad6[0x410 - 0x408 - 4]; + u32 evlane; + u32 macal; + u32 macah; + u32 sdc; + u32 dscp[7]; + u32 psc0; + u32 vpt2p; + u32 ps0; + u32 tqc; + u32 psc1; + u32 ps1; + u32 mrvl_header; + u8 pad7[0x460 - 0x454 - 4]; + u32 ic; + u32 ice; + u32 pim; + u32 peim; + u8 pad8[0x474 - 0x46c - 4]; + u32 pxtfut; + u32 pad9; + u32 pxmfs; + u32 pad10; + u32 pxdfc; + u32 pxofc; + u8 pad11[0x494 - 0x488 - 4]; + u32 peuiae; + u8 pad12[0x4bc - 0x494 - 4]; + u32 eth_type_prio; + u8 pad13[0x4dc - 0x4bc - 4]; + u32 tqfpc; + u32 pttbrc; + u32 tqc1; + u32 pmtu; + u32 pmtbs; + u8 pad14[0x60c - 0x4ec - 4]; + struct mvgbe_rxcdp rxcdp[7]; + struct mvgbe_rxdesc *rxcdp7; + u32 rqc; + struct mvgbe_txdesc *tcsdp; + u8 pad15[0x6c0 - 0x684 - 4]; + struct mvgbe_txdesc *tcqdp[8]; + u8 pad16[0x700 - 0x6dc - 4]; + struct mvgbe_tqx tqx[8]; + u32 pttbc; + u8 pad17[0x7a8 - 0x780 - 4]; + u32 tqxipg0; + u32 pad18[3]; + u32 tqxipg1; + u8 pad19[0x7c0 - 0x7b8 - 4]; + u32 hitkninlopkt; + u32 hitkninasyncpkt; + u32 lotkninasyncpkt; + u32 pad20; + u32 ts; + u8 pad21[0x3000 - 0x27d0 - 4]; + u32 pad20_1[32]; /* mib counter registes */ + u8 pad22[0x3400 - 0x3000 - sizeof(u32) * 32]; + u32 dfsmt[64]; + u32 dfomt[64]; + u32 dfut[4]; + u8 pad23[0xe20c0 - 0x7360c - 4]; + u32 pmbus_top_arbiter; +}; + +/* structures/enums needed by driver */ +enum mvgbe_adrwin { + MVGBE_WIN0, + MVGBE_WIN1, + MVGBE_WIN2, + MVGBE_WIN3, + MVGBE_WIN4, + MVGBE_WIN5 +}; + +enum mvgbe_target { + MVGBE_TARGET_DRAM, + MVGBE_TARGET_DEV, + MVGBE_TARGET_CBS, + MVGBE_TARGET_PCI0, + MVGBE_TARGET_PCI1 +}; + +struct mvgbe_winparam { + enum mvgbe_adrwin win; /* Window number */ + enum mvgbe_target target; /* System targets */ + u16 attrib; /* BAR attrib. See above macros */ + u32 base_addr; /* Window base address in u32 form */ + u32 high_addr; /* Window high address in u32 form */ + u32 size; /* Size in MBytes. Must be % 64Kbyte. */ + int enable; /* Enable/disable access to the window. */ + u16 access_ctrl; /*Access ctrl register. see above macros */ +}; + +struct mvgbe_rxdesc { + u32 cmd_sts; /* Descriptor command status */ + u16 buf_size; /* Buffer size */ + u16 byte_cnt; /* Descriptor buffer byte count */ + u8 *buf_ptr; /* Descriptor buffer pointer */ + struct mvgbe_rxdesc *nxtdesc_p; /* Next descriptor pointer */ +}; + +struct mvgbe_txdesc { + u32 cmd_sts; /* Descriptor command status */ + u16 l4i_chk; /* CPU provided TCP Checksum */ + u16 byte_cnt; /* Descriptor buffer byte count */ + u8 *buf_ptr; /* Descriptor buffer ptr */ + struct mvgbe_txdesc *nxtdesc_p; /* Next descriptor ptr */ +}; + +/* port device data struct */ +struct mvgbe_device { + struct eth_device dev; + struct mvgbe_registers *regs; + struct mvgbe_txdesc *p_txdesc; + struct mvgbe_rxdesc *p_rxdesc; + struct mvgbe_rxdesc *p_rxdesc_curr; + u8 *p_rxbuf; + u8 *p_aligned_txbuf; +}; + +#endif /* __MVGBE_H__ */ diff --git a/qemu/roms/u-boot/drivers/net/natsemi.c b/qemu/roms/u-boot/drivers/net/natsemi.c new file mode 100644 index 000000000..04743bd2b --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/natsemi.c @@ -0,0 +1,882 @@ +/* + natsemi.c: A U-Boot driver for the NatSemi DP8381x series. + Author: Mark A. Rakes (mark_rakes@vivato.net) + + Adapted from an Etherboot driver written by: + + Copyright (C) 2001 Entity Cyber, Inc. + + This development of this Etherboot driver was funded by + + Sicom Systems: http://www.sicompos.com/ + + Author: Marty Connor (mdc@thinguin.org) + Adapted from a Linux driver which was written by Donald Becker + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + Original Copyright Notice: + + Written/copyright 1999-2001 by Donald Becker. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. License for under other terms may be + available. Contact the original author for details. + + The original author may be reached as becker@scyld.com, or at + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Support information and updates available at + http://www.scyld.com/network/netsemi.html + + References: + http://www.scyld.com/expert/100mbps.html + http://www.scyld.com/expert/NWay.html + Datasheet is available from: + http://www.national.com/pf/DP/DP83815.html +*/ + +/* Revision History + * October 2002 mar 1.0 + * Initial U-Boot Release. Tested with Netgear FA311 board + * and dp83815 chipset on custom board +*/ + +/* Includes */ +#include +#include +#include +#include +#include +#include + +/* defines */ +#define EEPROM_SIZE 0xb /*12 16-bit chunks, or 24 bytes*/ + +#define DSIZE 0x00000FFF +#define ETH_ALEN 6 +#define CRC_SIZE 4 +#define TOUT_LOOP 500000 +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ + +/* Offsets to the device registers. + Unlike software-only systems, device drivers interact with complex hardware. + It's not useful to define symbolic names for every register bit in the + device. */ +enum register_offsets { + ChipCmd = 0x00, + ChipConfig = 0x04, + EECtrl = 0x08, + IntrMask = 0x14, + IntrEnable = 0x18, + TxRingPtr = 0x20, + TxConfig = 0x24, + RxRingPtr = 0x30, + RxConfig = 0x34, + ClkRun = 0x3C, + RxFilterAddr = 0x48, + RxFilterData = 0x4C, + SiliconRev = 0x58, + PCIPM = 0x44, + BasicControl = 0x80, + BasicStatus = 0x84, + /* These are from the spec, around page 78... on a separate table. */ + PGSEL = 0xCC, + PMDCSR = 0xE4, + TSTDAT = 0xFC, + DSPCFG = 0xF4, + SDCFG = 0x8C +}; + +/* Bit in ChipCmd. */ +enum ChipCmdBits { + ChipReset = 0x100, + RxReset = 0x20, + TxReset = 0x10, + RxOff = 0x08, + RxOn = 0x04, + TxOff = 0x02, + TxOn = 0x01 +}; + +enum ChipConfigBits { + LinkSts = 0x80000000, + HundSpeed = 0x40000000, + FullDuplex = 0x20000000, + TenPolarity = 0x10000000, + AnegDone = 0x08000000, + AnegEnBothBoth = 0x0000E000, + AnegDis100Full = 0x0000C000, + AnegEn100Both = 0x0000A000, + AnegDis100Half = 0x00008000, + AnegEnBothHalf = 0x00006000, + AnegDis10Full = 0x00004000, + AnegEn10Both = 0x00002000, + DuplexMask = 0x00008000, + SpeedMask = 0x00004000, + AnegMask = 0x00002000, + AnegDis10Half = 0x00000000, + ExtPhy = 0x00001000, + PhyRst = 0x00000400, + PhyDis = 0x00000200, + BootRomDisable = 0x00000004, + BEMode = 0x00000001, +}; + +enum TxConfig_bits { + TxDrthMask = 0x3f, + TxFlthMask = 0x3f00, + TxMxdmaMask = 0x700000, + TxMxdma_512 = 0x0, + TxMxdma_4 = 0x100000, + TxMxdma_8 = 0x200000, + TxMxdma_16 = 0x300000, + TxMxdma_32 = 0x400000, + TxMxdma_64 = 0x500000, + TxMxdma_128 = 0x600000, + TxMxdma_256 = 0x700000, + TxCollRetry = 0x800000, + TxAutoPad = 0x10000000, + TxMacLoop = 0x20000000, + TxHeartIgn = 0x40000000, + TxCarrierIgn = 0x80000000 +}; + +enum RxConfig_bits { + RxDrthMask = 0x3e, + RxMxdmaMask = 0x700000, + RxMxdma_512 = 0x0, + RxMxdma_4 = 0x100000, + RxMxdma_8 = 0x200000, + RxMxdma_16 = 0x300000, + RxMxdma_32 = 0x400000, + RxMxdma_64 = 0x500000, + RxMxdma_128 = 0x600000, + RxMxdma_256 = 0x700000, + RxAcceptLong = 0x8000000, + RxAcceptTx = 0x10000000, + RxAcceptRunt = 0x40000000, + RxAcceptErr = 0x80000000 +}; + +/* Bits in the RxMode register. */ +enum rx_mode_bits { + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0xC0000000, + AcceptMulticast = 0x00200000, + AcceptAllMulticast = 0x20000000, + AcceptAllPhys = 0x10000000, + AcceptMyPhys = 0x08000000 +}; + +typedef struct _BufferDesc { + u32 link; + vu_long cmdsts; + u32 bufptr; + u32 software_use; +} BufferDesc; + +/* Bits in network_desc.status */ +enum desc_status_bits { + DescOwn = 0x80000000, DescMore = 0x40000000, DescIntr = 0x20000000, + DescNoCRC = 0x10000000, DescPktOK = 0x08000000, + DescSizeMask = 0xfff, + + DescTxAbort = 0x04000000, DescTxFIFO = 0x02000000, + DescTxCarrier = 0x01000000, DescTxDefer = 0x00800000, + DescTxExcDefer = 0x00400000, DescTxOOWCol = 0x00200000, + DescTxExcColl = 0x00100000, DescTxCollCount = 0x000f0000, + + DescRxAbort = 0x04000000, DescRxOver = 0x02000000, + DescRxDest = 0x01800000, DescRxLong = 0x00400000, + DescRxRunt = 0x00200000, DescRxInvalid = 0x00100000, + DescRxCRC = 0x00080000, DescRxAlign = 0x00040000, + DescRxLoop = 0x00020000, DesRxColl = 0x00010000, +}; + +/* Globals */ +#ifdef NATSEMI_DEBUG +static int natsemi_debug = 0; /* 1 verbose debugging, 0 normal */ +#endif +static u32 SavedClkRun; +static unsigned int cur_rx; +static unsigned int advertising; +static unsigned int rx_config; +static unsigned int tx_config; + +/* Note: transmit and receive buffers and descriptors must be + longword aligned */ +static BufferDesc txd __attribute__ ((aligned(4))); +static BufferDesc rxd[NUM_RX_DESC] __attribute__ ((aligned(4))); + +static unsigned char txb[TX_BUF_SIZE] __attribute__ ((aligned(4))); +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE] + __attribute__ ((aligned(4))); + +/* Function Prototypes */ +#if 0 +static void write_eeprom(struct eth_device *dev, long addr, int location, + short value); +#endif +static int read_eeprom(struct eth_device *dev, long addr, int location); +static int mdio_read(struct eth_device *dev, int phy_id, int location); +static int natsemi_init(struct eth_device *dev, bd_t * bis); +static void natsemi_reset(struct eth_device *dev); +static void natsemi_init_rxfilter(struct eth_device *dev); +static void natsemi_init_txd(struct eth_device *dev); +static void natsemi_init_rxd(struct eth_device *dev); +static void natsemi_set_rx_mode(struct eth_device *dev); +static void natsemi_check_duplex(struct eth_device *dev); +static int natsemi_send(struct eth_device *dev, void *packet, int length); +static int natsemi_poll(struct eth_device *dev); +static void natsemi_disable(struct eth_device *dev); + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_83815}, + {} +}; + +#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) + +static inline int +INW(struct eth_device *dev, u_long addr) +{ + return le16_to_cpu(*(vu_short *) (addr + dev->iobase)); +} + +static int +INL(struct eth_device *dev, u_long addr) +{ + return le32_to_cpu(*(vu_long *) (addr + dev->iobase)); +} + +static inline void +OUTW(struct eth_device *dev, int command, u_long addr) +{ + *(vu_short *) ((addr + dev->iobase)) = cpu_to_le16(command); +} + +static inline void +OUTL(struct eth_device *dev, int command, u_long addr) +{ + *(vu_long *) ((addr + dev->iobase)) = cpu_to_le32(command); +} + +/* + * Function: natsemi_initialize + * + * Description: Retrieves the MAC address of the card, and sets up some + * globals required by other routines, and initializes the NIC, making it + * ready to send and receive packets. + * + * Side effects: + * leaves the natsemi initialized, and ready to receive packets. + * + * Returns: struct eth_device *: pointer to NIC data structure + */ + +int +natsemi_initialize(bd_t * bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase, status, chip_config; + int i, idx = 0; + int prev_eedata; + u32 tmp; + + while (1) { + /* Find PCI device(s) */ + if ((devno = pci_find_devices(supported, idx++)) < 0) { + break; + } + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_0, &iobase); + iobase &= ~0x3; /* bit 1: unused and bit 0: I/O Space Indicator */ + + pci_write_config_dword(devno, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Check if I/O accesses and Bus Mastering are enabled. */ + pci_read_config_dword(devno, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_MEMORY)) { + printf("Error: Can not enable MEM access.\n"); + continue; + } else if (!(status & PCI_COMMAND_MASTER)) { + printf("Error: Can not enable Bus Mastering.\n"); + continue; + } + + dev = (struct eth_device *) malloc(sizeof *dev); + if (!dev) { + printf("natsemi: Can not allocate memory\n"); + break; + } + memset(dev, 0, sizeof(*dev)); + + sprintf(dev->name, "dp83815#%d", card_number); + dev->iobase = bus_to_phys(iobase); +#ifdef NATSEMI_DEBUG + printf("natsemi: NatSemi ns8381[56] @ %#x\n", dev->iobase); +#endif + dev->priv = (void *) devno; + dev->init = natsemi_init; + dev->halt = natsemi_disable; + dev->send = natsemi_send; + dev->recv = natsemi_poll; + + eth_register(dev); + + card_number++; + + /* Set the latency timer for value. */ + pci_write_config_byte(devno, PCI_LATENCY_TIMER, 0x20); + + udelay(10 * 1000); + + /* natsemi has a non-standard PM control register + * in PCI config space. Some boards apparently need + * to be brought to D0 in this manner. */ + pci_read_config_dword(devno, PCIPM, &tmp); + if (tmp & (0x03 | 0x100)) { + /* D0 state, disable PME assertion */ + u32 newtmp = tmp & ~(0x03 | 0x100); + pci_write_config_dword(devno, PCIPM, newtmp); + } + + printf("natsemi: EEPROM contents:\n"); + for (i = 0; i <= EEPROM_SIZE; i++) { + short eedata = read_eeprom(dev, EECtrl, i); + printf(" %04hx", eedata); + } + printf("\n"); + + /* get MAC address */ + prev_eedata = read_eeprom(dev, EECtrl, 6); + for (i = 0; i < 3; i++) { + int eedata = read_eeprom(dev, EECtrl, i + 7); + dev->enetaddr[i*2] = (eedata << 1) + (prev_eedata >> 15); + dev->enetaddr[i*2+1] = eedata >> 7; + prev_eedata = eedata; + } + + /* Reset the chip to erase any previous misconfiguration. */ + OUTL(dev, ChipReset, ChipCmd); + + advertising = mdio_read(dev, 1, 4); + chip_config = INL(dev, ChipConfig); +#ifdef NATSEMI_DEBUG + printf("%s: Transceiver status %#08X advertising %#08X\n", + dev->name, (int) INL(dev, BasicStatus), advertising); + printf("%s: Transceiver default autoneg. %s 10%s %s duplex.\n", + dev->name, chip_config & AnegMask ? "enabled, advertise" : + "disabled, force", chip_config & SpeedMask ? "0" : "", + chip_config & DuplexMask ? "full" : "half"); +#endif + chip_config |= AnegEnBothBoth; +#ifdef NATSEMI_DEBUG + printf("%s: changed to autoneg. %s 10%s %s duplex.\n", + dev->name, chip_config & AnegMask ? "enabled, advertise" : + "disabled, force", chip_config & SpeedMask ? "0" : "", + chip_config & DuplexMask ? "full" : "half"); +#endif + /*write new autoneg bits, reset phy*/ + OUTL(dev, (chip_config | PhyRst), ChipConfig); + /*un-reset phy*/ + OUTL(dev, chip_config, ChipConfig); + + /* Disable PME: + * The PME bit is initialized from the EEPROM contents. + * PCI cards probably have PME disabled, but motherboard + * implementations may have PME set to enable WakeOnLan. + * With PME set the chip will scan incoming packets but + * nothing will be written to memory. */ + SavedClkRun = INL(dev, ClkRun); + OUTL(dev, SavedClkRun & ~0x100, ClkRun); + } + return card_number; +} + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. + The EEPROM code is for common 93c06/46 EEPROMs w/ 6bit addresses. */ + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33MHz PCI, but future 66MHz + access may need a delay. */ +#define eeprom_delay(ee_addr) INL(dev, ee_addr) + +enum EEPROM_Ctrl_Bits { + EE_ShiftClk = 0x04, + EE_DataIn = 0x01, + EE_ChipSelect = 0x08, + EE_DataOut = 0x02 +}; + +#define EE_Write0 (EE_ChipSelect) +#define EE_Write1 (EE_ChipSelect | EE_DataIn) +/* The EEPROM commands include the alway-set leading bit. */ +enum EEPROM_Cmds { + EE_WrEnCmd = (4 << 6), EE_WriteCmd = (5 << 6), + EE_ReadCmd = (6 << 6), EE_EraseCmd = (7 << 6), +}; + +#if 0 +static void +write_eeprom(struct eth_device *dev, long addr, int location, short value) +{ + int i; + int ee_addr = (typeof(ee_addr))addr; + short wren_cmd = EE_WrEnCmd | 0x30; /*wren is 100 + 11XXXX*/ + short write_cmd = location | EE_WriteCmd; + +#ifdef NATSEMI_DEBUG + printf("write_eeprom: %08x, %04hx, %04hx\n", + dev->iobase + ee_addr, write_cmd, value); +#endif + /* Shift the write enable command bits out. */ + for (i = 9; i >= 0; i--) { + short cmdval = (wren_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + OUTL(dev, cmdval, ee_addr); + eeprom_delay(ee_addr); + OUTL(dev, cmdval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + + OUTL(dev, 0, ee_addr); /*bring chip select low*/ + OUTL(dev, EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + + /* Shift the write command bits out. */ + for (i = 9; i >= 0; i--) { + short cmdval = (write_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + OUTL(dev, cmdval, ee_addr); + eeprom_delay(ee_addr); + OUTL(dev, cmdval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + + for (i = 0; i < 16; i++) { + short cmdval = (value & (1 << i)) ? EE_Write1 : EE_Write0; + OUTL(dev, cmdval, ee_addr); + eeprom_delay(ee_addr); + OUTL(dev, cmdval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + + OUTL(dev, 0, ee_addr); /*bring chip select low*/ + OUTL(dev, EE_ShiftClk, ee_addr); + for (i = 0; i < 200000; i++) { + OUTL(dev, EE_Write0, ee_addr); /*poll for done*/ + if (INL(dev, ee_addr) & EE_DataOut) { + break; /*finished*/ + } + } + eeprom_delay(ee_addr); + + /* Terminate the EEPROM access. */ + OUTL(dev, EE_Write0, ee_addr); + OUTL(dev, 0, ee_addr); + return; +} +#endif + +static int +read_eeprom(struct eth_device *dev, long addr, int location) +{ + int i; + int retval = 0; + int ee_addr = (typeof(ee_addr))addr; + int read_cmd = location | EE_ReadCmd; + + OUTL(dev, EE_Write0, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + OUTL(dev, dataval, ee_addr); + eeprom_delay(ee_addr); + OUTL(dev, dataval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + OUTL(dev, EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + + for (i = 0; i < 16; i++) { + OUTL(dev, EE_ChipSelect | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + retval |= (INL(dev, ee_addr) & EE_DataOut) ? 1 << i : 0; + OUTL(dev, EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + } + + /* Terminate the EEPROM access. */ + OUTL(dev, EE_Write0, ee_addr); + OUTL(dev, 0, ee_addr); +#ifdef NATSEMI_DEBUG + if (natsemi_debug) + printf("read_eeprom: %08x, %08x, retval %08x\n", + dev->iobase + ee_addr, read_cmd, retval); +#endif + return retval; +} + +/* MII transceiver control section. + The 83815 series has an internal transceiver, and we present the + management registers as if they were MII connected. */ + +static int +mdio_read(struct eth_device *dev, int phy_id, int location) +{ + if (phy_id == 1 && location < 32) + return INL(dev, BasicControl+(location<<2))&0xffff; + else + return 0xffff; +} + +/* Function: natsemi_init + * + * Description: resets the ethernet controller chip and configures + * registers and data structures required for sending and receiving packets. + * + * Arguments: struct eth_device *dev: NIC data structure + * + * returns: int. + */ + +static int +natsemi_init(struct eth_device *dev, bd_t * bis) +{ + + natsemi_reset(dev); + + /* Disable PME: + * The PME bit is initialized from the EEPROM contents. + * PCI cards probably have PME disabled, but motherboard + * implementations may have PME set to enable WakeOnLan. + * With PME set the chip will scan incoming packets but + * nothing will be written to memory. */ + OUTL(dev, SavedClkRun & ~0x100, ClkRun); + + natsemi_init_rxfilter(dev); + natsemi_init_txd(dev); + natsemi_init_rxd(dev); + + /* Configure the PCI bus bursts and FIFO thresholds. */ + tx_config = TxAutoPad | TxCollRetry | TxMxdma_256 | (0x1002); + rx_config = RxMxdma_256 | 0x20; + +#ifdef NATSEMI_DEBUG + printf("%s: Setting TxConfig Register %#08X\n", dev->name, tx_config); + printf("%s: Setting RxConfig Register %#08X\n", dev->name, rx_config); +#endif + OUTL(dev, tx_config, TxConfig); + OUTL(dev, rx_config, RxConfig); + + natsemi_check_duplex(dev); + natsemi_set_rx_mode(dev); + + OUTL(dev, (RxOn | TxOn), ChipCmd); + return 1; +} + +/* + * Function: natsemi_reset + * + * Description: soft resets the controller chip + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: void. + */ +static void +natsemi_reset(struct eth_device *dev) +{ + OUTL(dev, ChipReset, ChipCmd); + + /* On page 78 of the spec, they recommend some settings for "optimum + performance" to be done in sequence. These settings optimize some + of the 100Mbit autodetection circuitry. Also, we only want to do + this for rev C of the chip. */ + if (INL(dev, SiliconRev) == 0x302) { + OUTW(dev, 0x0001, PGSEL); + OUTW(dev, 0x189C, PMDCSR); + OUTW(dev, 0x0000, TSTDAT); + OUTW(dev, 0x5040, DSPCFG); + OUTW(dev, 0x008C, SDCFG); + } + /* Disable interrupts using the mask. */ + OUTL(dev, 0, IntrMask); + OUTL(dev, 0, IntrEnable); +} + +/* Function: natsemi_init_rxfilter + * + * Description: sets receive filter address to our MAC address + * + * Arguments: struct eth_device *dev: NIC data structure + * + * returns: void. + */ + +static void +natsemi_init_rxfilter(struct eth_device *dev) +{ + int i; + + for (i = 0; i < ETH_ALEN; i += 2) { + OUTL(dev, i, RxFilterAddr); + OUTW(dev, dev->enetaddr[i] + (dev->enetaddr[i + 1] << 8), + RxFilterData); + } +} + +/* + * Function: natsemi_init_txd + * + * Description: initializes the Tx descriptor + * + * Arguments: struct eth_device *dev: NIC data structure + * + * returns: void. + */ + +static void +natsemi_init_txd(struct eth_device *dev) +{ + txd.link = (u32) 0; + txd.cmdsts = (u32) 0; + txd.bufptr = (u32) & txb[0]; + + /* load Transmit Descriptor Register */ + OUTL(dev, (u32) & txd, TxRingPtr); +#ifdef NATSEMI_DEBUG + printf("natsemi_init_txd: TX descriptor reg loaded with: %#08X\n", + INL(dev, TxRingPtr)); +#endif +} + +/* Function: natsemi_init_rxd + * + * Description: initializes the Rx descriptor ring + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: void. + */ + +static void +natsemi_init_rxd(struct eth_device *dev) +{ + int i; + + cur_rx = 0; + + /* init RX descriptor */ + for (i = 0; i < NUM_RX_DESC; i++) { + rxd[i].link = + cpu_to_le32((i + 1 < + NUM_RX_DESC) ? (u32) & rxd[i + + 1] : (u32) & + rxd[0]); + rxd[i].cmdsts = cpu_to_le32((u32) RX_BUF_SIZE); + rxd[i].bufptr = cpu_to_le32((u32) & rxb[i * RX_BUF_SIZE]); +#ifdef NATSEMI_DEBUG + printf + ("natsemi_init_rxd: rxd[%d]=%p link=%X cmdsts=%lX bufptr=%X\n", + i, &rxd[i], le32_to_cpu(rxd[i].link), + rxd[i].cmdsts, rxd[i].bufptr); +#endif + } + + /* load Receive Descriptor Register */ + OUTL(dev, (u32) & rxd[0], RxRingPtr); + +#ifdef NATSEMI_DEBUG + printf("natsemi_init_rxd: RX descriptor register loaded with: %X\n", + INL(dev, RxRingPtr)); +#endif +} + +/* Function: natsemi_set_rx_mode + * + * Description: + * sets the receive mode to accept all broadcast packets and packets + * with our MAC address, and reject all multicast packets. + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: void. + */ + +static void +natsemi_set_rx_mode(struct eth_device *dev) +{ + u32 rx_mode = AcceptBroadcast | AcceptMyPhys; + + OUTL(dev, rx_mode, RxFilterAddr); +} + +static void +natsemi_check_duplex(struct eth_device *dev) +{ + int duplex = INL(dev, ChipConfig) & FullDuplex ? 1 : 0; + +#ifdef NATSEMI_DEBUG + printf("%s: Setting %s-duplex based on negotiated link" + " capability.\n", dev->name, duplex ? "full" : "half"); +#endif + if (duplex) { + rx_config |= RxAcceptTx; + tx_config |= (TxCarrierIgn | TxHeartIgn); + } else { + rx_config &= ~RxAcceptTx; + tx_config &= ~(TxCarrierIgn | TxHeartIgn); + } + OUTL(dev, tx_config, TxConfig); + OUTL(dev, rx_config, RxConfig); +} + +/* Function: natsemi_send + * + * Description: transmits a packet and waits for completion or timeout. + * + * Returns: void. */ +static int natsemi_send(struct eth_device *dev, void *packet, int length) +{ + u32 i, status = 0; + u32 tx_status = 0; + u32 *tx_ptr = &tx_status; + vu_long *res = (vu_long *)tx_ptr; + + /* Stop the transmitter */ + OUTL(dev, TxOff, ChipCmd); + +#ifdef NATSEMI_DEBUG + if (natsemi_debug) + printf("natsemi_send: sending %d bytes\n", (int) length); +#endif + + /* set the transmit buffer descriptor and enable Transmit State Machine */ + txd.link = cpu_to_le32(0); + txd.bufptr = cpu_to_le32(phys_to_bus((u32) packet)); + txd.cmdsts = cpu_to_le32(DescOwn | length); + + /* load Transmit Descriptor Register */ + OUTL(dev, phys_to_bus((u32) & txd), TxRingPtr); +#ifdef NATSEMI_DEBUG + if (natsemi_debug) + printf("natsemi_send: TX descriptor register loaded with: %#08X\n", + INL(dev, TxRingPtr)); +#endif + /* restart the transmitter */ + OUTL(dev, TxOn, ChipCmd); + + for (i = 0; + (*res = le32_to_cpu(txd.cmdsts)) & DescOwn; + i++) { + if (i >= TOUT_LOOP) { + printf + ("%s: tx error buffer not ready: txd.cmdsts == %#X\n", + dev->name, tx_status); + goto Done; + } + } + + if (!(tx_status & DescPktOK)) { + printf("natsemi_send: Transmit error, Tx status %X.\n", + tx_status); + goto Done; + } + + status = 1; + Done: + return status; +} + +/* Function: natsemi_poll + * + * Description: checks for a received packet and returns it if found. + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: 1 if packet was received. + * 0 if no packet was received. + * + * Side effects: + * Returns (copies) the packet to the array dev->packet. + * Returns the length of the packet. + */ + +static int +natsemi_poll(struct eth_device *dev) +{ + int retstat = 0; + int length = 0; + u32 rx_status = le32_to_cpu(rxd[cur_rx].cmdsts); + + if (!(rx_status & (u32) DescOwn)) + return retstat; +#ifdef NATSEMI_DEBUG + if (natsemi_debug) + printf("natsemi_poll: got a packet: cur_rx:%d, status:%X\n", + cur_rx, rx_status); +#endif + length = (rx_status & DSIZE) - CRC_SIZE; + + if ((rx_status & (DescMore | DescPktOK | DescRxLong)) != DescPktOK) { + printf + ("natsemi_poll: Corrupted packet received, buffer status = %X\n", + rx_status); + retstat = 0; + } else { /* give packet to higher level routine */ + NetReceive((rxb + cur_rx * RX_BUF_SIZE), length); + retstat = 1; + } + + /* return the descriptor and buffer to receive ring */ + rxd[cur_rx].cmdsts = cpu_to_le32(RX_BUF_SIZE); + rxd[cur_rx].bufptr = cpu_to_le32((u32) & rxb[cur_rx * RX_BUF_SIZE]); + + if (++cur_rx == NUM_RX_DESC) + cur_rx = 0; + + /* re-enable the potentially idle receive state machine */ + OUTL(dev, RxOn, ChipCmd); + + return retstat; +} + +/* Function: natsemi_disable + * + * Description: Turns off interrupts and stops Tx and Rx engines + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: void. + */ + +static void +natsemi_disable(struct eth_device *dev) +{ + /* Disable interrupts using the mask. */ + OUTL(dev, 0, IntrMask); + OUTL(dev, 0, IntrEnable); + + /* Stop the chip's Tx and Rx processes. */ + OUTL(dev, RxOff | TxOff, ChipCmd); + + /* Restore PME enable bit */ + OUTL(dev, SavedClkRun, ClkRun); +} diff --git a/qemu/roms/u-boot/drivers/net/ne2000.c b/qemu/roms/u-boot/drivers/net/ne2000.c new file mode 100644 index 000000000..e6cd3e9ba --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ne2000.c @@ -0,0 +1,259 @@ +/* +Ported to U-Boot by Christian Pellegrin + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + +========================================================================== + +dev/if_dp83902a.c + +Ethernet device driver for NS DP83902a ethernet controller + +========================================================================== +####ECOSGPLCOPYRIGHTBEGIN#### +------------------------------------------- +This file is part of eCos, the Embedded Configurable Operating System. +Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + +eCos is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 or (at your option) any later version. + +eCos is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with eCos; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +As a special exception, if other files instantiate templates or use macros +or inline functions from this file, or you compile this file and link it +with other works to produce a work based on this file, this file does not +by itself cause the resulting work to be covered by the GNU General Public +License. However the source code for this file must still be made available +in accordance with section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work based on +this file might be covered by the GNU General Public License. + +Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +at http://sources.redhat.com/ecos/ecos-license/ +------------------------------------------- +####ECOSGPLCOPYRIGHTEND#### +####BSDCOPYRIGHTBEGIN#### + +------------------------------------------- + +Portions of this software may have been derived from OpenBSD or other sources, +and are covered by the appropriate copyright disclaimers included herein. + +------------------------------------------- + +####BSDCOPYRIGHTEND#### +========================================================================== +#####DESCRIPTIONBEGIN#### + +Author(s): gthomas +Contributors: gthomas, jskov, rsandifo +Date: 2001-06-13 +Purpose: +Description: + +FIXME: Will fail if pinged with large packets (1520 bytes) +Add promisc config +Add SNMP + +####DESCRIPTIONEND#### + +========================================================================== +*/ + +#include +#include + +/* NE2000 base header file */ +#include "ne2000_base.h" + +/* find prom (taken from pc_net_cs.c from Linux) */ + +#include "8390.h" +/* +typedef struct hw_info_t { + u_int offset; + u_char a0, a1, a2; + u_int flags; +} hw_info_t; +*/ +#define DELAY_OUTPUT 0x01 +#define HAS_MISC_REG 0x02 +#define USE_BIG_BUF 0x04 +#define HAS_IBM_MISC 0x08 +#define IS_DL10019 0x10 +#define IS_DL10022 0x20 +#define HAS_MII 0x40 +#define USE_SHMEM 0x80 /* autodetected */ + +#define AM79C9XX_HOME_PHY 0x00006B90 /* HomePNA PHY */ +#define AM79C9XX_ETH_PHY 0x00006B70 /* 10baseT PHY */ +#define MII_PHYID_REV_MASK 0xfffffff0 +#define MII_PHYID_REG1 0x02 +#define MII_PHYID_REG2 0x03 + +static hw_info_t hw_info[] = { + { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT }, + { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 }, + { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 }, + { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94, + DELAY_OUTPUT | HAS_IBM_MISC }, + { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 }, + { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 }, + { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 }, + { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 }, + { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 }, + { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 }, + { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 }, + { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 }, + { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0374, 0x08, 0x00, 0x17, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 }, + { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 }, + { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 }, + { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 }, + { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 }, + { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 }, + { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 }, + { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 }, + { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 }, + { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b, + DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF }, + { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 }, + { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 }, + { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 }, + { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 }, + { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 }, + { /* Qemu */ 0x0, 0x52, 0x54, 0x00, 0 }, + { /* RTL8019AS */ 0x0, 0x0, 0x18, 0x5f, 0 } +}; + +#define NR_INFO (sizeof(hw_info)/sizeof(hw_info_t)) + +#define PCNET_CMD 0x00 +#define PCNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */ +#define PCNET_RESET 0x1f /* Issue a read to reset, a write to clear. */ +#define PCNET_MISC 0x18 /* For IBM CCAE and Socket EA cards */ + +static void pcnet_reset_8390(u8* addr) +{ + int i, r; + + n2k_outb(E8390_NODMA + E8390_PAGE0+E8390_STOP, E8390_CMD); + PRINTK("cmd (at %lx) is %x\n", addr + E8390_CMD, n2k_inb(E8390_CMD)); + n2k_outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, E8390_CMD); + PRINTK("cmd (at %lx) is %x\n", addr + E8390_CMD, n2k_inb(E8390_CMD)); + n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD); + PRINTK("cmd (at %lx) is %x\n", addr + E8390_CMD, n2k_inb(E8390_CMD)); + n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD); + + n2k_outb(n2k_inb(PCNET_RESET), PCNET_RESET); + + for (i = 0; i < 100; i++) { + if ((r = (n2k_inb(EN0_ISR) & ENISR_RESET)) != 0) + break; + PRINTK("got %x in reset\n", r); + udelay(100); + } + n2k_outb(ENISR_RESET, EN0_ISR); /* Ack intr. */ + + if (i == 100) + printf("pcnet_reset_8390() did not complete.\n"); +} /* pcnet_reset_8390 */ + +int get_prom(u8* mac_addr, u8* base_addr) +{ + u8 prom[32]; + int i, j; + struct { + u_char value, offset; + } program_seq[] = { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ + {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {32, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ + {0x00, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + + PRINTK ("trying to get MAC via prom reading\n"); + + pcnet_reset_8390 (base_addr); + + mdelay (10); + + for (i = 0; i < ARRAY_SIZE(program_seq); i++) + n2k_outb (program_seq[i].value, program_seq[i].offset); + + PRINTK ("PROM:"); + for (i = 0; i < 32; i++) { + prom[i] = n2k_inb (PCNET_DATAPORT); + PRINTK (" %02x", prom[i]); + } + PRINTK ("\n"); + for (i = 0; i < NR_INFO; i++) { + if ((prom[0] == hw_info[i].a0) && + (prom[2] == hw_info[i].a1) && + (prom[4] == hw_info[i].a2)) { + PRINTK ("matched board %d\n", i); + break; + } + } + if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) { + PRINTK ("on exit i is %d/%ld\n", i, NR_INFO); + PRINTK ("MAC address is "); + for (j = 0; j < 6; j++) { + mac_addr[j] = prom[j << 1]; + PRINTK ("%02x:", mac_addr[i]); + } + PRINTK ("\n"); + return (i < NR_INFO) ? i : 0; + } + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/ne2000.h b/qemu/roms/u-boot/drivers/net/ne2000.h new file mode 100644 index 000000000..2cde6be43 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ne2000.h @@ -0,0 +1,94 @@ +/* +Ported to U-Boot by Christian Pellegrin + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + +========================================================================== + + dev/dp83902a.h + + National Semiconductor DP83902a ethernet chip + +========================================================================== +####ECOSGPLCOPYRIGHTBEGIN#### + ------------------------------------------- + This file is part of eCos, the Embedded Configurable Operating System. + Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + + eCos is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 or (at your option) any later version. + + eCos is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with eCos; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file does not + by itself cause the resulting work to be covered by the GNU General Public + License. However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based on + this file might be covered by the GNU General Public License. + + Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. + at http://sources.redhat.com/ecos/ecos-license/ + ------------------------------------------- +####ECOSGPLCOPYRIGHTEND#### +####BSDCOPYRIGHTBEGIN#### + + ------------------------------------------- + + Portions of this software may have been derived from OpenBSD or other sources, + and are covered by the appropriate copyright disclaimers included herein. + + ------------------------------------------- + +####BSDCOPYRIGHTEND#### +========================================================================== +#####DESCRIPTIONBEGIN#### + + Author(s): gthomas + Contributors: gthomas, jskov + Date: 2001-06-13 + Purpose: + Description: + +####DESCRIPTIONEND#### + +========================================================================== +*/ + +/* + * NE2000 support header file. + * Created by Nobuhiro Iwamatsu + */ + +#ifndef __DRIVERS_NE2000_H__ +#define __DRIVERS_NE2000_H__ + +/* Enable NE2000 basic init function */ +#define NE2000_BASIC_INIT + +#define DP_DATA 0x10 +#define START_PG 0x50 /* First page of TX buffer */ +#define START_PG2 0x48 +#define STOP_PG 0x80 /* Last page +1 of RX ring */ + +#define RX_START 0x50 +#define RX_END 0x80 + +#define DP_IN(_b_, _o_, _d_) (_d_) = *( (vu_char *) ((_b_)+(_o_))) +#define DP_OUT(_b_, _o_, _d_) *( (vu_char *) ((_b_)+(_o_))) = (_d_) +#define DP_IN_DATA(_b_, _d_) (_d_) = *( (vu_char *) ((_b_))) +#define DP_OUT_DATA(_b_, _d_) *( (vu_char *) ((_b_))) = (_d_) +#endif /* __DRIVERS_NE2000_H__ */ diff --git a/qemu/roms/u-boot/drivers/net/ne2000_base.c b/qemu/roms/u-boot/drivers/net/ne2000_base.c new file mode 100644 index 000000000..ef3592204 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ne2000_base.c @@ -0,0 +1,800 @@ +/* +Ported to U-Boot by Christian Pellegrin + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + +========================================================================== + +dev/if_dp83902a.c + +Ethernet device driver for NS DP83902a ethernet controller + +========================================================================== +####ECOSGPLCOPYRIGHTBEGIN#### +------------------------------------------- +This file is part of eCos, the Embedded Configurable Operating System. +Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + +eCos is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 or (at your option) any later version. + +eCos is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with eCos; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +As a special exception, if other files instantiate templates or use macros +or inline functions from this file, or you compile this file and link it +with other works to produce a work based on this file, this file does not +by itself cause the resulting work to be covered by the GNU General Public +License. However the source code for this file must still be made available +in accordance with section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work based on +this file might be covered by the GNU General Public License. + +Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +at http://sources.redhat.com/ecos/ecos-license/ +------------------------------------------- +####ECOSGPLCOPYRIGHTEND#### +####BSDCOPYRIGHTBEGIN#### + +------------------------------------------- + +Portions of this software may have been derived from OpenBSD or other sources, +and are covered by the appropriate copyright disclaimers included herein. + +------------------------------------------- + +####BSDCOPYRIGHTEND#### +========================================================================== +#####DESCRIPTIONBEGIN#### + +Author(s): gthomas +Contributors: gthomas, jskov, rsandifo +Date: 2001-06-13 +Purpose: +Description: + +FIXME: Will fail if pinged with large packets (1520 bytes) +Add promisc config +Add SNMP + +####DESCRIPTIONEND#### + +========================================================================== +*/ + +#include +#include +#include +#include +#include + +/* forward definition of function used for the uboot interface */ +void uboot_push_packet_len(int len); +void uboot_push_tx_done(int key, int val); + +/* NE2000 base header file */ +#include "ne2000_base.h" + +#if defined(CONFIG_DRIVER_AX88796L) +/* AX88796L support */ +#include "ax88796.h" +#else +/* Basic NE2000 chip support */ +#include "ne2000.h" +#endif + +static dp83902a_priv_data_t nic; /* just one instance of the card supported */ + +/** + * This function reads the MAC address from the serial EEPROM, + * used if PROM read fails. Does nothing for ax88796 chips (sh boards) + */ +static bool +dp83902a_init(unsigned char *enetaddr) +{ + dp83902a_priv_data_t *dp = &nic; + u8* base; +#if defined(NE2000_BASIC_INIT) + int i; +#endif + + DEBUG_FUNCTION(); + + base = dp->base; + if (!base) + return false; /* No device found */ + + DEBUG_LINE(); + +#if defined(NE2000_BASIC_INIT) + /* AX88796L doesn't need */ + /* Prepare ESA */ + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE1); /* Select page 1 */ + /* Use the address from the serial EEPROM */ + for (i = 0; i < 6; i++) + DP_IN(base, DP_P1_PAR0+i, dp->esa[i]); + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE0); /* Select page 0 */ + + printf("NE2000 - %s ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", + "eeprom", + dp->esa[0], + dp->esa[1], + dp->esa[2], + dp->esa[3], + dp->esa[4], + dp->esa[5] ); + + memcpy(enetaddr, dp->esa, 6); /* Use MAC from serial EEPROM */ +#endif /* NE2000_BASIC_INIT */ + return true; +} + +static void +dp83902a_stop(void) +{ + dp83902a_priv_data_t *dp = &nic; + u8 *base = dp->base; + + DEBUG_FUNCTION(); + + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_STOP); /* Brutal */ + DP_OUT(base, DP_ISR, 0xFF); /* Clear any pending interrupts */ + DP_OUT(base, DP_IMR, 0x00); /* Disable all interrupts */ + + dp->running = false; +} + +/* + * This function is called to "start up" the interface. It may be called + * multiple times, even when the hardware is already running. It will be + * called whenever something "hardware oriented" changes and should leave + * the hardware ready to send/receive packets. + */ +static void +dp83902a_start(u8 * enaddr) +{ + dp83902a_priv_data_t *dp = &nic; + u8 *base = dp->base; + int i; + + debug("The MAC is %pM\n", enaddr); + + DEBUG_FUNCTION(); + + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_STOP); /* Brutal */ + DP_OUT(base, DP_DCR, DP_DCR_INIT); + DP_OUT(base, DP_RBCH, 0); /* Remote byte count */ + DP_OUT(base, DP_RBCL, 0); + DP_OUT(base, DP_RCR, DP_RCR_MON); /* Accept no packets */ + DP_OUT(base, DP_TCR, DP_TCR_LOCAL); /* Transmitter [virtually] off */ + DP_OUT(base, DP_TPSR, dp->tx_buf1); /* Transmitter start page */ + dp->tx1 = dp->tx2 = 0; + dp->tx_next = dp->tx_buf1; + dp->tx_started = false; + dp->running = true; + DP_OUT(base, DP_PSTART, dp->rx_buf_start); /* Receive ring start page */ + DP_OUT(base, DP_BNDRY, dp->rx_buf_end - 1); /* Receive ring boundary */ + DP_OUT(base, DP_PSTOP, dp->rx_buf_end); /* Receive ring end page */ + dp->rx_next = dp->rx_buf_start - 1; + dp->running = true; + DP_OUT(base, DP_ISR, 0xFF); /* Clear any pending interrupts */ + DP_OUT(base, DP_IMR, DP_IMR_All); /* Enable all interrupts */ + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE1 | DP_CR_STOP); /* Select page 1 */ + DP_OUT(base, DP_P1_CURP, dp->rx_buf_start); /* Current page - next free page for Rx */ + dp->running = true; + for (i = 0; i < ETHER_ADDR_LEN; i++) { + /* FIXME */ + /*((vu_short*)( base + ((DP_P1_PAR0 + i) * 2) + + * 0x1400)) = enaddr[i];*/ + DP_OUT(base, DP_P1_PAR0+i, enaddr[i]); + } + /* Enable and start device */ + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + DP_OUT(base, DP_TCR, DP_TCR_NORMAL); /* Normal transmit operations */ + DP_OUT(base, DP_RCR, DP_RCR_AB); /* Accept broadcast, no errors, no multicast */ + dp->running = true; +} + +/* + * This routine is called to start the transmitter. It is split out from the + * data handling routine so it may be called either when data becomes first + * available or when an Tx interrupt occurs + */ + +static void +dp83902a_start_xmit(int start_page, int len) +{ + dp83902a_priv_data_t *dp = (dp83902a_priv_data_t *) &nic; + u8 *base = dp->base; + + DEBUG_FUNCTION(); + +#if DEBUG & 1 + printf("Tx pkt %d len %d\n", start_page, len); + if (dp->tx_started) + printf("TX already started?!?\n"); +#endif + + DP_OUT(base, DP_ISR, (DP_ISR_TxP | DP_ISR_TxE)); + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + DP_OUT(base, DP_TBCL, len & 0xFF); + DP_OUT(base, DP_TBCH, len >> 8); + DP_OUT(base, DP_TPSR, start_page); + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_TXPKT | DP_CR_START); + + dp->tx_started = true; +} + +/* + * This routine is called to send data to the hardware. It is known a-priori + * that there is free buffer space (dp->tx_next). + */ +static void +dp83902a_send(u8 *data, int total_len, u32 key) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + int len, start_page, pkt_len, i, isr; +#if DEBUG & 4 + int dx; +#endif + + DEBUG_FUNCTION(); + + len = pkt_len = total_len; + if (pkt_len < IEEE_8023_MIN_FRAME) + pkt_len = IEEE_8023_MIN_FRAME; + + start_page = dp->tx_next; + if (dp->tx_next == dp->tx_buf1) { + dp->tx1 = start_page; + dp->tx1_len = pkt_len; + dp->tx1_key = key; + dp->tx_next = dp->tx_buf2; + } else { + dp->tx2 = start_page; + dp->tx2_len = pkt_len; + dp->tx2_key = key; + dp->tx_next = dp->tx_buf1; + } + +#if DEBUG & 5 + printf("TX prep page %d len %d\n", start_page, pkt_len); +#endif + + DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ + { + /* + * Dummy read. The manual sez something slightly different, + * but the code is extended a bit to do what Hitachi's monitor + * does (i.e., also read data). + */ + + __maybe_unused u16 tmp; + int len = 1; + + DP_OUT(base, DP_RSAL, 0x100 - len); + DP_OUT(base, DP_RSAH, (start_page - 1) & 0xff); + DP_OUT(base, DP_RBCL, len); + DP_OUT(base, DP_RBCH, 0); + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_RDMA | DP_CR_START); + DP_IN_DATA(dp->data, tmp); + } + +#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA + /* + * Stall for a bit before continuing to work around random data + * corruption problems on some platforms. + */ + CYGACC_CALL_IF_DELAY_US(1); +#endif + + /* Send data to device buffer(s) */ + DP_OUT(base, DP_RSAL, 0); + DP_OUT(base, DP_RSAH, start_page); + DP_OUT(base, DP_RBCL, pkt_len & 0xFF); + DP_OUT(base, DP_RBCH, pkt_len >> 8); + DP_OUT(base, DP_CR, DP_CR_WDMA | DP_CR_START); + + /* Put data into buffer */ +#if DEBUG & 4 + printf(" sg buf %08lx len %08x\n ", (u32)data, len); + dx = 0; +#endif + while (len > 0) { +#if DEBUG & 4 + printf(" %02x", *data); + if (0 == (++dx % 16)) printf("\n "); +#endif + + DP_OUT_DATA(dp->data, *data++); + len--; + } +#if DEBUG & 4 + printf("\n"); +#endif + if (total_len < pkt_len) { +#if DEBUG & 4 + printf(" + %d bytes of padding\n", pkt_len - total_len); +#endif + /* Padding to 802.3 length was required */ + for (i = total_len; i < pkt_len;) { + i++; + DP_OUT_DATA(dp->data, 0); + } + } + +#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA + /* + * After last data write, delay for a bit before accessing the + * device again, or we may get random data corruption in the last + * datum (on some platforms). + */ + CYGACC_CALL_IF_DELAY_US(1); +#endif + + /* Wait for DMA to complete */ + do { + DP_IN(base, DP_ISR, isr); + } while ((isr & DP_ISR_RDC) == 0); + + /* Then disable DMA */ + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + + /* Start transmit if not already going */ + if (!dp->tx_started) { + if (start_page == dp->tx1) { + dp->tx_int = 1; /* Expecting interrupt from BUF1 */ + } else { + dp->tx_int = 2; /* Expecting interrupt from BUF2 */ + } + dp83902a_start_xmit(start_page, pkt_len); + } +} + +/* + * This function is called when a packet has been received. It's job is + * to prepare to unload the packet from the hardware. Once the length of + * the packet is known, the upper layer of the driver can be told. When + * the upper layer is ready to unload the packet, the internal function + * 'dp83902a_recv' will be called to actually fetch it from the hardware. + */ +static void +dp83902a_RxEvent(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + __maybe_unused u8 rsr; + u8 rcv_hdr[4]; + int i, len, pkt, cur; + + DEBUG_FUNCTION(); + + DP_IN(base, DP_RSR, rsr); + while (true) { + /* Read incoming packet header */ + DP_OUT(base, DP_CR, DP_CR_PAGE1 | DP_CR_NODMA | DP_CR_START); + DP_IN(base, DP_P1_CURP, cur); + DP_OUT(base, DP_P1_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + DP_IN(base, DP_BNDRY, pkt); + + pkt += 1; + if (pkt == dp->rx_buf_end) + pkt = dp->rx_buf_start; + + if (pkt == cur) { + break; + } + DP_OUT(base, DP_RBCL, sizeof(rcv_hdr)); + DP_OUT(base, DP_RBCH, 0); + DP_OUT(base, DP_RSAL, 0); + DP_OUT(base, DP_RSAH, pkt); + if (dp->rx_next == pkt) { + if (cur == dp->rx_buf_start) + DP_OUT(base, DP_BNDRY, dp->rx_buf_end - 1); + else + DP_OUT(base, DP_BNDRY, cur - 1); /* Update pointer */ + return; + } + dp->rx_next = pkt; + DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ + DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START); +#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA + CYGACC_CALL_IF_DELAY_US(10); +#endif + + /* read header (get data size)*/ + for (i = 0; i < sizeof(rcv_hdr);) { + DP_IN_DATA(dp->data, rcv_hdr[i++]); + } + +#if DEBUG & 5 + printf("rx hdr %02x %02x %02x %02x\n", + rcv_hdr[0], rcv_hdr[1], rcv_hdr[2], rcv_hdr[3]); +#endif + len = ((rcv_hdr[3] << 8) | rcv_hdr[2]) - sizeof(rcv_hdr); + + /* data read */ + uboot_push_packet_len(len); + + if (rcv_hdr[1] == dp->rx_buf_start) + DP_OUT(base, DP_BNDRY, dp->rx_buf_end - 1); + else + DP_OUT(base, DP_BNDRY, rcv_hdr[1] - 1); /* Update pointer */ + } +} + +/* + * This function is called as a result of the "eth_drv_recv()" call above. + * It's job is to actually fetch data for a packet from the hardware once + * memory buffers have been allocated for the packet. Note that the buffers + * may come in pieces, using a scatter-gather list. This allows for more + * efficient processing in the upper layers of the stack. + */ +static void +dp83902a_recv(u8 *data, int len) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + int i, mlen; + u8 saved_char = 0; + bool saved; +#if DEBUG & 4 + int dx; +#endif + + DEBUG_FUNCTION(); + +#if DEBUG & 5 + printf("Rx packet %d length %d\n", dp->rx_next, len); +#endif + + /* Read incoming packet data */ + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + DP_OUT(base, DP_RBCL, len & 0xFF); + DP_OUT(base, DP_RBCH, len >> 8); + DP_OUT(base, DP_RSAL, 4); /* Past header */ + DP_OUT(base, DP_RSAH, dp->rx_next); + DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ + DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START); +#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA + CYGACC_CALL_IF_DELAY_US(10); +#endif + + saved = false; + for (i = 0; i < 1; i++) { + if (data) { + mlen = len; +#if DEBUG & 4 + printf(" sg buf %08lx len %08x \n", (u32) data, mlen); + dx = 0; +#endif + while (0 < mlen) { + /* Saved byte from previous loop? */ + if (saved) { + *data++ = saved_char; + mlen--; + saved = false; + continue; + } + + { + u8 tmp; + DP_IN_DATA(dp->data, tmp); +#if DEBUG & 4 + printf(" %02x", tmp); + if (0 == (++dx % 16)) printf("\n "); +#endif + *data++ = tmp;; + mlen--; + } + } +#if DEBUG & 4 + printf("\n"); +#endif + } + } +} + +static void +dp83902a_TxEvent(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + __maybe_unused u8 tsr; + u32 key; + + DEBUG_FUNCTION(); + + DP_IN(base, DP_TSR, tsr); + if (dp->tx_int == 1) { + key = dp->tx1_key; + dp->tx1 = 0; + } else { + key = dp->tx2_key; + dp->tx2 = 0; + } + /* Start next packet if one is ready */ + dp->tx_started = false; + if (dp->tx1) { + dp83902a_start_xmit(dp->tx1, dp->tx1_len); + dp->tx_int = 1; + } else if (dp->tx2) { + dp83902a_start_xmit(dp->tx2, dp->tx2_len); + dp->tx_int = 2; + } else { + dp->tx_int = 0; + } + /* Tell higher level we sent this packet */ + uboot_push_tx_done(key, 0); +} + +/* + * Read the tally counters to clear them. Called in response to a CNT + * interrupt. + */ +static void +dp83902a_ClearCounters(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + __maybe_unused u8 cnt1, cnt2, cnt3; + + DP_IN(base, DP_FER, cnt1); + DP_IN(base, DP_CER, cnt2); + DP_IN(base, DP_MISSED, cnt3); + DP_OUT(base, DP_ISR, DP_ISR_CNT); +} + +/* + * Deal with an overflow condition. This code follows the procedure set + * out in section 7.0 of the datasheet. + */ +static void +dp83902a_Overflow(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)&nic; + u8 *base = dp->base; + u8 isr; + + /* Issue a stop command and wait 1.6ms for it to complete. */ + DP_OUT(base, DP_CR, DP_CR_STOP | DP_CR_NODMA); + CYGACC_CALL_IF_DELAY_US(1600); + + /* Clear the remote byte counter registers. */ + DP_OUT(base, DP_RBCL, 0); + DP_OUT(base, DP_RBCH, 0); + + /* Enter loopback mode while we clear the buffer. */ + DP_OUT(base, DP_TCR, DP_TCR_LOCAL); + DP_OUT(base, DP_CR, DP_CR_START | DP_CR_NODMA); + + /* + * Read in as many packets as we can and acknowledge any and receive + * interrupts. Since the buffer has overflowed, a receive event of + * some kind will have occured. + */ + dp83902a_RxEvent(); + DP_OUT(base, DP_ISR, DP_ISR_RxP|DP_ISR_RxE); + + /* Clear the overflow condition and leave loopback mode. */ + DP_OUT(base, DP_ISR, DP_ISR_OFLW); + DP_OUT(base, DP_TCR, DP_TCR_NORMAL); + + /* + * If a transmit command was issued, but no transmit event has occured, + * restart it here. + */ + DP_IN(base, DP_ISR, isr); + if (dp->tx_started && !(isr & (DP_ISR_TxP|DP_ISR_TxE))) { + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_TXPKT | DP_CR_START); + } +} + +static void +dp83902a_poll(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + u8 isr; + + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE0 | DP_CR_START); + DP_IN(base, DP_ISR, isr); + while (0 != isr) { + /* + * The CNT interrupt triggers when the MSB of one of the error + * counters is set. We don't much care about these counters, but + * we should read their values to reset them. + */ + if (isr & DP_ISR_CNT) { + dp83902a_ClearCounters(); + } + /* + * Check for overflow. It's a special case, since there's a + * particular procedure that must be followed to get back into + * a running state.a + */ + if (isr & DP_ISR_OFLW) { + dp83902a_Overflow(); + } else { + /* + * Other kinds of interrupts can be acknowledged simply by + * clearing the relevant bits of the ISR. Do that now, then + * handle the interrupts we care about. + */ + DP_OUT(base, DP_ISR, isr); /* Clear set bits */ + if (!dp->running) break; /* Is this necessary? */ + /* + * Check for tx_started on TX event since these may happen + * spuriously it seems. + */ + if (isr & (DP_ISR_TxP|DP_ISR_TxE) && dp->tx_started) { + dp83902a_TxEvent(); + } + if (isr & (DP_ISR_RxP|DP_ISR_RxE)) { + dp83902a_RxEvent(); + } + } + DP_IN(base, DP_ISR, isr); + } +} + + +/* U-boot specific routines */ +static u8 *pbuf = NULL; + +static int pkey = -1; +static int initialized = 0; + +void uboot_push_packet_len(int len) { + PRINTK("pushed len = %d\n", len); + if (len >= 2000) { + printf("NE2000: packet too big\n"); + return; + } + dp83902a_recv(&pbuf[0], len); + + /*Just pass it to the upper layer*/ + NetReceive(&pbuf[0], len); +} + +void uboot_push_tx_done(int key, int val) { + PRINTK("pushed key = %d\n", key); + pkey = key; +} + +/** + * Setup the driver and init MAC address according to doc/README.enetaddr + * Called by ne2k_register() before registering the driver @eth layer + * + * @param struct ethdevice of this instance of the driver for dev->enetaddr + * @return 0 on success, -1 on error (causing caller to print error msg) + */ +static int ne2k_setup_driver(struct eth_device *dev) +{ + PRINTK("### ne2k_setup_driver\n"); + + if (!pbuf) { + pbuf = malloc(2000); + if (!pbuf) { + printf("Cannot allocate rx buffer\n"); + return -1; + } + } + +#ifdef CONFIG_DRIVER_NE2000_CCR + { + vu_char *p = (vu_char *) CONFIG_DRIVER_NE2000_CCR; + + PRINTK("CCR before is %x\n", *p); + *p = CONFIG_DRIVER_NE2000_VAL; + PRINTK("CCR after is %x\n", *p); + } +#endif + + nic.base = (u8 *) CONFIG_DRIVER_NE2000_BASE; + + nic.data = nic.base + DP_DATA; + nic.tx_buf1 = START_PG; + nic.tx_buf2 = START_PG2; + nic.rx_buf_start = RX_START; + nic.rx_buf_end = RX_END; + + /* + * According to doc/README.enetaddr, drivers shall give priority + * to the MAC address value in the environment, so we do not read + * it from the prom or eeprom if it is specified in the environment. + */ + if (!eth_getenv_enetaddr("ethaddr", dev->enetaddr)) { + /* If the MAC address is not in the environment, get it: */ + if (!get_prom(dev->enetaddr, nic.base)) /* get MAC from prom */ + dp83902a_init(dev->enetaddr); /* fallback: seeprom */ + /* And write it into the environment otherwise eth_write_hwaddr + * returns -1 due to eth_getenv_enetaddr_by_index() failing, + * and this causes "Warning: failed to set MAC address", and + * cmd_bdinfo has no ethaddr value which it can show: */ + eth_setenv_enetaddr("ethaddr", dev->enetaddr); + } + return 0; +} + +static int ne2k_init(struct eth_device *dev, bd_t *bd) +{ + dp83902a_start(dev->enetaddr); + initialized = 1; + return 0; +} + +static void ne2k_halt(struct eth_device *dev) +{ + debug("### ne2k_halt\n"); + if(initialized) + dp83902a_stop(); + initialized = 0; +} + +static int ne2k_recv(struct eth_device *dev) +{ + dp83902a_poll(); + return 1; +} + +static int ne2k_send(struct eth_device *dev, void *packet, int length) +{ + int tmo; + + debug("### ne2k_send\n"); + + pkey = -1; + + dp83902a_send((u8 *) packet, length, 666); + tmo = get_timer (0) + TOUT * CONFIG_SYS_HZ; + while(1) { + dp83902a_poll(); + if (pkey != -1) { + PRINTK("Packet sucesfully sent\n"); + return 0; + } + if (get_timer (0) >= tmo) { + printf("transmission error (timoeut)\n"); + return 0; + } + + } + return 0; +} + +/** + * Setup the driver for use and register it with the eth layer + * @return 0 on success, -1 on error (causing caller to print error msg) + */ +int ne2k_register(void) +{ + struct eth_device *dev; + + dev = calloc(sizeof(*dev), 1); + if (dev == NULL) + return -1; + + if (ne2k_setup_driver(dev)) + return -1; + + dev->init = ne2k_init; + dev->halt = ne2k_halt; + dev->send = ne2k_send; + dev->recv = ne2k_recv; + + sprintf(dev->name, "NE2000"); + + return eth_register(dev); +} diff --git a/qemu/roms/u-boot/drivers/net/ne2000_base.h b/qemu/roms/u-boot/drivers/net/ne2000_base.h new file mode 100644 index 000000000..eee0956fd --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ne2000_base.h @@ -0,0 +1,304 @@ +/* +Ported to U-Boot by Christian Pellegrin + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + + +========================================================================== + + dev/dp83902a.h + + National Semiconductor DP83902a ethernet chip + +========================================================================== +####ECOSGPLCOPYRIGHTBEGIN#### + ------------------------------------------- + This file is part of eCos, the Embedded Configurable Operating System. + Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + + eCos is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 or (at your option) any later version. + + eCos is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with eCos; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file does not + by itself cause the resulting work to be covered by the GNU General Public + License. However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based on + this file might be covered by the GNU General Public License. + + Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. + at http://sources.redhat.com/ecos/ecos-license/ + ------------------------------------------- +####ECOSGPLCOPYRIGHTEND#### +####BSDCOPYRIGHTBEGIN#### + + ------------------------------------------- + + Portions of this software may have been derived from OpenBSD or other sources, + and are covered by the appropriate copyright disclaimers included herein. + + ------------------------------------------- + +####BSDCOPYRIGHTEND#### +========================================================================== +#####DESCRIPTIONBEGIN#### + + Author(s): gthomas + Contributors: gthomas, jskov + Date: 2001-06-13 + Purpose: + Description: + +####DESCRIPTIONEND#### + +========================================================================== + +*/ + +/* + ------------------------------------------------------------------------ + Macros for accessing DP registers + These can be overridden by the platform header +*/ + +#ifndef __NE2000_BASE_H__ +#define __NE2000_BASE_H__ + +/* + * Debugging details + * + * Set to perms of: + * 0 disables all debug output + * 1 for process debug output + * 2 for added data IO output: get_reg, put_reg + * 4 for packet allocation/free output + * 8 for only startup status, so we can tell we're installed OK + */ +#if 0 +#define DEBUG 0xf +#else +#define DEBUG 0 +#endif + +#if DEBUG & 1 +#define DEBUG_FUNCTION() do { printf("%s\n", __FUNCTION__); } while (0) +#define DEBUG_LINE() do { printf("%d\n", __LINE__); } while (0) +#define PRINTK(args...) printf(args) +#else +#define DEBUG_FUNCTION() do {} while(0) +#define DEBUG_LINE() do {} while(0) +#define PRINTK(args...) +#endif + +/* timeout for tx/rx in s */ +#define TOUT 5 +/* Ether MAC address size */ +#define ETHER_ADDR_LEN 6 + + +#define CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA 1 +#define CYGACC_CALL_IF_DELAY_US(X) udelay(X) + +/* H/W infomation struct */ +typedef struct hw_info_t { + u32 offset; + u8 a0, a1, a2; + u32 flags; +} hw_info_t; + +typedef struct dp83902a_priv_data { + u8* base; + u8* data; + u8* reset; + int tx_next; /* First free Tx page */ + int tx_int; /* Expecting interrupt from this buffer */ + int rx_next; /* First free Rx page */ + int tx1, tx2; /* Page numbers for Tx buffers */ + u32 tx1_key, tx2_key; /* Used to ack when packet sent */ + int tx1_len, tx2_len; + bool tx_started, running, hardwired_esa; + u8 esa[6]; + void* plf_priv; + + /* Buffer allocation */ + int tx_buf1, tx_buf2; + int rx_buf_start, rx_buf_end; +} dp83902a_priv_data_t; + +/* ------------------------------------------------------------------------ */ +/* Register offsets */ + +#define DP_CR 0x00 +#define DP_CLDA0 0x01 +#define DP_PSTART 0x01 /* write */ +#define DP_CLDA1 0x02 +#define DP_PSTOP 0x02 /* write */ +#define DP_BNDRY 0x03 +#define DP_TSR 0x04 +#define DP_TPSR 0x04 /* write */ +#define DP_NCR 0x05 +#define DP_TBCL 0x05 /* write */ +#define DP_FIFO 0x06 +#define DP_TBCH 0x06 /* write */ +#define DP_ISR 0x07 +#define DP_CRDA0 0x08 +#define DP_RSAL 0x08 /* write */ +#define DP_CRDA1 0x09 +#define DP_RSAH 0x09 /* write */ +#define DP_RBCL 0x0a /* write */ +#define DP_RBCH 0x0b /* write */ +#define DP_RSR 0x0c +#define DP_RCR 0x0c /* write */ +#define DP_FER 0x0d +#define DP_TCR 0x0d /* write */ +#define DP_CER 0x0e +#define DP_DCR 0x0e /* write */ +#define DP_MISSED 0x0f +#define DP_IMR 0x0f /* write */ +#define DP_DATAPORT 0x10 /* "eprom" data port */ + +#define DP_P1_CR 0x00 +#define DP_P1_PAR0 0x01 +#define DP_P1_PAR1 0x02 +#define DP_P1_PAR2 0x03 +#define DP_P1_PAR3 0x04 +#define DP_P1_PAR4 0x05 +#define DP_P1_PAR5 0x06 +#define DP_P1_CURP 0x07 +#define DP_P1_MAR0 0x08 +#define DP_P1_MAR1 0x09 +#define DP_P1_MAR2 0x0a +#define DP_P1_MAR3 0x0b +#define DP_P1_MAR4 0x0c +#define DP_P1_MAR5 0x0d +#define DP_P1_MAR6 0x0e +#define DP_P1_MAR7 0x0f + +#define DP_P2_CR 0x00 +#define DP_P2_PSTART 0x01 +#define DP_P2_CLDA0 0x01 /* write */ +#define DP_P2_PSTOP 0x02 +#define DP_P2_CLDA1 0x02 /* write */ +#define DP_P2_RNPP 0x03 +#define DP_P2_TPSR 0x04 +#define DP_P2_LNPP 0x05 +#define DP_P2_ACH 0x06 +#define DP_P2_ACL 0x07 +#define DP_P2_RCR 0x0c +#define DP_P2_TCR 0x0d +#define DP_P2_DCR 0x0e +#define DP_P2_IMR 0x0f + +/* Command register - common to all pages */ + +#define DP_CR_STOP 0x01 /* Stop: software reset */ +#define DP_CR_START 0x02 /* Start: initialize device */ +#define DP_CR_TXPKT 0x04 /* Transmit packet */ +#define DP_CR_RDMA 0x08 /* Read DMA (recv data from device) */ +#define DP_CR_WDMA 0x10 /* Write DMA (send data to device) */ +#define DP_CR_SEND 0x18 /* Send packet */ +#define DP_CR_NODMA 0x20 /* Remote (or no) DMA */ +#define DP_CR_PAGE0 0x00 /* Page select */ +#define DP_CR_PAGE1 0x40 +#define DP_CR_PAGE2 0x80 +#define DP_CR_PAGEMSK 0x3F /* Used to mask out page bits */ + +/* Data configuration register */ + +#define DP_DCR_WTS 0x01 /* 1=16 bit word transfers */ +#define DP_DCR_BOS 0x02 /* 1=Little Endian */ +#define DP_DCR_LAS 0x04 /* 1=Single 32 bit DMA mode */ +#define DP_DCR_LS 0x08 /* 1=normal mode, 0=loopback */ +#define DP_DCR_ARM 0x10 /* 0=no send command (program I/O) */ +#define DP_DCR_FIFO_1 0x00 /* FIFO threshold */ +#define DP_DCR_FIFO_2 0x20 +#define DP_DCR_FIFO_4 0x40 +#define DP_DCR_FIFO_6 0x60 + +#define DP_DCR_INIT (DP_DCR_LS|DP_DCR_FIFO_4) + +/* Interrupt status register */ + +#define DP_ISR_RxP 0x01 /* Packet received */ +#define DP_ISR_TxP 0x02 /* Packet transmitted */ +#define DP_ISR_RxE 0x04 /* Receive error */ +#define DP_ISR_TxE 0x08 /* Transmit error */ +#define DP_ISR_OFLW 0x10 /* Receive overflow */ +#define DP_ISR_CNT 0x20 /* Tally counters need emptying */ +#define DP_ISR_RDC 0x40 /* Remote DMA complete */ +#define DP_ISR_RESET 0x80 /* Device has reset (shutdown, error) */ + +/* Interrupt mask register */ + +#define DP_IMR_RxP 0x01 /* Packet received */ +#define DP_IMR_TxP 0x02 /* Packet transmitted */ +#define DP_IMR_RxE 0x04 /* Receive error */ +#define DP_IMR_TxE 0x08 /* Transmit error */ +#define DP_IMR_OFLW 0x10 /* Receive overflow */ +#define DP_IMR_CNT 0x20 /* Tall counters need emptying */ +#define DP_IMR_RDC 0x40 /* Remote DMA complete */ + +#define DP_IMR_All 0x3F /* Everything but remote DMA */ + +/* Receiver control register */ + +#define DP_RCR_SEP 0x01 /* Save bad(error) packets */ +#define DP_RCR_AR 0x02 /* Accept runt packets */ +#define DP_RCR_AB 0x04 /* Accept broadcast packets */ +#define DP_RCR_AM 0x08 /* Accept multicast packets */ +#define DP_RCR_PROM 0x10 /* Promiscuous mode */ +#define DP_RCR_MON 0x20 /* Monitor mode - 1=accept no packets */ + +/* Receiver status register */ + +#define DP_RSR_RxP 0x01 /* Packet received */ +#define DP_RSR_CRC 0x02 /* CRC error */ +#define DP_RSR_FRAME 0x04 /* Framing error */ +#define DP_RSR_FO 0x08 /* FIFO overrun */ +#define DP_RSR_MISS 0x10 /* Missed packet */ +#define DP_RSR_PHY 0x20 /* 0=pad match, 1=mad match */ +#define DP_RSR_DIS 0x40 /* Receiver disabled */ +#define DP_RSR_DFR 0x80 /* Receiver processing deferred */ + +/* Transmitter control register */ + +#define DP_TCR_NOCRC 0x01 /* 1=inhibit CRC */ +#define DP_TCR_NORMAL 0x00 /* Normal transmitter operation */ +#define DP_TCR_LOCAL 0x02 /* Internal NIC loopback */ +#define DP_TCR_INLOOP 0x04 /* Full internal loopback */ +#define DP_TCR_OUTLOOP 0x08 /* External loopback */ +#define DP_TCR_ATD 0x10 /* Auto transmit disable */ +#define DP_TCR_OFFSET 0x20 /* Collision offset adjust */ + +/* Transmit status register */ + +#define DP_TSR_TxP 0x01 /* Packet transmitted */ +#define DP_TSR_COL 0x04 /* Collision (at least one) */ +#define DP_TSR_ABT 0x08 /* Aborted because of too many collisions */ +#define DP_TSR_CRS 0x10 /* Lost carrier */ +#define DP_TSR_FU 0x20 /* FIFO underrun */ +#define DP_TSR_CDH 0x40 /* Collision Detect Heartbeat */ +#define DP_TSR_OWC 0x80 /* Collision outside normal window */ + +#define IEEE_8023_MAX_FRAME 1518 /* Largest possible ethernet frame */ +#define IEEE_8023_MIN_FRAME 64 /* Smallest possible ethernet frame */ + +/* Functions */ +int get_prom(u8* mac_addr, u8* base_addr); + +#endif /* __NE2000_BASE_H__ */ diff --git a/qemu/roms/u-boot/drivers/net/netconsole.c b/qemu/roms/u-boot/drivers/net/netconsole.c new file mode 100644 index 000000000..65c747e14 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/netconsole.c @@ -0,0 +1,331 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_NETCONSOLE_BUFFER_SIZE +#define CONFIG_NETCONSOLE_BUFFER_SIZE 512 +#endif + +static char input_buffer[CONFIG_NETCONSOLE_BUFFER_SIZE]; +static int input_size; /* char count in input buffer */ +static int input_offset; /* offset to valid chars in input buffer */ +static int input_recursion; +static int output_recursion; +static int net_timeout; +static uchar nc_ether[6]; /* server enet address */ +static IPaddr_t nc_ip; /* server ip */ +static short nc_out_port; /* target output port */ +static short nc_in_port; /* source input port */ +static const char *output_packet; /* used by first send udp */ +static int output_packet_len; +/* + * Start with a default last protocol. + * We are only interested in NETCONS or not. + */ +enum proto_t net_loop_last_protocol = BOOTP; + +static void nc_wait_arp_handler(uchar *pkt, unsigned dest, + IPaddr_t sip, unsigned src, + unsigned len) +{ + net_set_state(NETLOOP_SUCCESS); /* got arp reply - quit net loop */ +} + +static void nc_handler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, + unsigned len) +{ + if (input_size) + net_set_state(NETLOOP_SUCCESS); /* got input - quit net loop */ +} + +static void nc_timeout(void) +{ + net_set_state(NETLOOP_SUCCESS); +} + +static int is_broadcast(IPaddr_t ip) +{ + static IPaddr_t netmask; + static IPaddr_t our_ip; + static int env_changed_id; + int env_id = get_env_id(); + + /* update only when the environment has changed */ + if (env_changed_id != env_id) { + netmask = getenv_IPaddr("netmask"); + our_ip = getenv_IPaddr("ipaddr"); + + env_changed_id = env_id; + } + + return (ip == ~0 || /* 255.255.255.255 */ + ((netmask & our_ip) == (netmask & ip) && /* on the same net */ + (netmask | ip) == ~0)); /* broadcast to our net */ +} + +static int refresh_settings_from_env(void) +{ + const char *p; + static int env_changed_id; + int env_id = get_env_id(); + + /* update only when the environment has changed */ + if (env_changed_id != env_id) { + if (getenv("ncip")) { + nc_ip = getenv_IPaddr("ncip"); + if (!nc_ip) + return -1; /* ncip is 0.0.0.0 */ + p = strchr(getenv("ncip"), ':'); + if (p != NULL) { + nc_out_port = simple_strtoul(p + 1, NULL, 10); + nc_in_port = nc_out_port; + } + } else + nc_ip = ~0; /* ncip is not set, so broadcast */ + + p = getenv("ncoutport"); + if (p != NULL) + nc_out_port = simple_strtoul(p, NULL, 10); + p = getenv("ncinport"); + if (p != NULL) + nc_in_port = simple_strtoul(p, NULL, 10); + + if (is_broadcast(nc_ip)) + /* broadcast MAC address */ + memset(nc_ether, 0xff, sizeof(nc_ether)); + else + /* force arp request */ + memset(nc_ether, 0, sizeof(nc_ether)); + } + return 0; +} + +/** + * Called from NetLoop in net/net.c before each packet + */ +void NcStart(void) +{ + refresh_settings_from_env(); + if (!output_packet_len || memcmp(nc_ether, NetEtherNullAddr, 6)) { + /* going to check for input packet */ + net_set_udp_handler(nc_handler); + NetSetTimeout(net_timeout, nc_timeout); + } else { + /* send arp request */ + uchar *pkt; + net_set_arp_handler(nc_wait_arp_handler); + pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE; + memcpy(pkt, output_packet, output_packet_len); + NetSendUDPPacket(nc_ether, nc_ip, nc_out_port, nc_in_port, + output_packet_len); + } +} + +int nc_input_packet(uchar *pkt, IPaddr_t src_ip, unsigned dest_port, + unsigned src_port, unsigned len) +{ + int end, chunk; + + if (dest_port != nc_in_port || !len) + return 0; /* not for us */ + + if (src_ip != nc_ip && !is_broadcast(nc_ip)) + return 0; /* not from our client */ + + debug_cond(DEBUG_DEV_PKT, "input: \"%*.*s\"\n", len, len, pkt); + + if (input_size == sizeof(input_buffer)) + return 1; /* no space */ + if (len > sizeof(input_buffer) - input_size) + len = sizeof(input_buffer) - input_size; + + end = input_offset + input_size; + if (end > sizeof(input_buffer)) + end -= sizeof(input_buffer); + + chunk = len; + if (end + len > sizeof(input_buffer)) { + chunk = sizeof(input_buffer) - end; + memcpy(input_buffer, pkt + chunk, len - chunk); + } + memcpy(input_buffer + end, pkt, chunk); + + input_size += len; + + return 1; +} + +static void nc_send_packet(const char *buf, int len) +{ + struct eth_device *eth; + int inited = 0; + uchar *pkt; + uchar *ether; + IPaddr_t ip; + + debug_cond(DEBUG_DEV_PKT, "output: \"%*.*s\"\n", len, len, buf); + + eth = eth_get_dev(); + if (eth == NULL) + return; + + if (!memcmp(nc_ether, NetEtherNullAddr, 6)) { + if (eth->state == ETH_STATE_ACTIVE) + return; /* inside net loop */ + output_packet = buf; + output_packet_len = len; + input_recursion = 1; + NetLoop(NETCONS); /* wait for arp reply and send packet */ + input_recursion = 0; + output_packet_len = 0; + return; + } + + if (eth->state != ETH_STATE_ACTIVE) { + if (eth_is_on_demand_init()) { + if (eth_init(gd->bd) < 0) + return; + eth_set_last_protocol(NETCONS); + } else + eth_init_state_only(gd->bd); + + inited = 1; + } + pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE; + memcpy(pkt, buf, len); + ether = nc_ether; + ip = nc_ip; + NetSendUDPPacket(ether, ip, nc_out_port, nc_in_port, len); + + if (inited) { + if (eth_is_on_demand_init()) + eth_halt(); + else + eth_halt_state_only(); + } +} + +static int nc_start(void) +{ + int retval; + + nc_out_port = 6666; /* default port */ + nc_in_port = nc_out_port; + + retval = refresh_settings_from_env(); + if (retval != 0) + return retval; + + /* + * Initialize the static IP settings and buffer pointers + * incase we call NetSendUDPPacket before NetLoop + */ + net_init(); + + return 0; +} + +static void nc_putc(char c) +{ + if (output_recursion) + return; + output_recursion = 1; + + nc_send_packet(&c, 1); + + output_recursion = 0; +} + +static void nc_puts(const char *s) +{ + int len; + + if (output_recursion) + return; + output_recursion = 1; + + len = strlen(s); + while (len) { + int send_len = min(len, sizeof(input_buffer)); + nc_send_packet(s, send_len); + len -= send_len; + s += send_len; + } + + output_recursion = 0; +} + +static int nc_getc(void) +{ + uchar c; + + input_recursion = 1; + + net_timeout = 0; /* no timeout */ + while (!input_size) + NetLoop(NETCONS); + + input_recursion = 0; + + c = input_buffer[input_offset++]; + + if (input_offset >= sizeof(input_buffer)) + input_offset -= sizeof(input_buffer); + input_size--; + + return c; +} + +static int nc_tstc(void) +{ + struct eth_device *eth; + + if (input_recursion) + return 0; + + if (input_size) + return 1; + + eth = eth_get_dev(); + if (eth && eth->state == ETH_STATE_ACTIVE) + return 0; /* inside net loop */ + + input_recursion = 1; + + net_timeout = 1; + NetLoop(NETCONS); /* kind of poll */ + + input_recursion = 0; + + return input_size != 0; +} + +int drv_nc_init(void) +{ + struct stdio_dev dev; + int rc; + + memset(&dev, 0, sizeof(dev)); + + strcpy(dev.name, "nc"); + dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + dev.start = nc_start; + dev.putc = nc_putc; + dev.puts = nc_puts; + dev.getc = nc_getc; + dev.tstc = nc_tstc; + + rc = stdio_register(&dev); + + return (rc == 0) ? 1 : rc; +} diff --git a/qemu/roms/u-boot/drivers/net/ns8382x.c b/qemu/roms/u-boot/drivers/net/ns8382x.c new file mode 100644 index 000000000..cfe1f349d --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/ns8382x.c @@ -0,0 +1,851 @@ +/* + ns8382x.c: A U-Boot driver for the NatSemi DP8382[01]. + ported by: Mark A. Rakes (mark_rakes@vivato.net) + + Adapted from: + 1. an Etherboot driver for DP8381[56] written by: + Copyright (C) 2001 Entity Cyber, Inc. + + This development of this Etherboot driver was funded by + Sicom Systems: http://www.sicompos.com/ + + Author: Marty Connor (mdc@thinguin.org) + Adapted from a Linux driver which was written by Donald Becker + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + 2. A Linux driver by Donald Becker, ns820.c: + Written/copyright 1999-2002 by Donald Becker. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. License for under other terms may be + available. Contact the original author for details. + + The original author may be reached as becker@scyld.com, or at + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Support information and updates available at + http://www.scyld.com/network/netsemi.html + + Datasheets available from: + http://www.national.com/pf/DP/DP83820.html + http://www.national.com/pf/DP/DP83821.html +*/ + +/* Revision History + * October 2002 mar 1.0 + * Initial U-Boot Release. + * Tested with Netgear GA622T (83820) + * and SMC9452TX (83821) + * NOTE: custom boards with these chips may (likely) require + * a programmed EEPROM device (if present) in order to work + * correctly. +*/ + +/* Includes */ +#include +#include +#include +#include +#include +#include + +/* defines */ +#define DSIZE 0x00000FFF +#define ETH_ALEN 6 +#define CRC_SIZE 4 +#define TOUT_LOOP 500000 +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ + +enum register_offsets { + ChipCmd = 0x00, + ChipConfig = 0x04, + EECtrl = 0x08, + IntrMask = 0x14, + IntrEnable = 0x18, + TxRingPtr = 0x20, + TxRingPtrHi = 0x24, + TxConfig = 0x28, + RxRingPtr = 0x30, + RxRingPtrHi = 0x34, + RxConfig = 0x38, + PriQueue = 0x3C, + RxFilterAddr = 0x48, + RxFilterData = 0x4C, + ClkRun = 0xCC, + PCIPM = 0x44, +}; + +enum ChipCmdBits { + ChipReset = 0x100, + RxReset = 0x20, + TxReset = 0x10, + RxOff = 0x08, + RxOn = 0x04, + TxOff = 0x02, + TxOn = 0x01 +}; + +enum ChipConfigBits { + LinkSts = 0x80000000, + GigSpeed = 0x40000000, + HundSpeed = 0x20000000, + FullDuplex = 0x10000000, + TBIEn = 0x01000000, + Mode1000 = 0x00400000, + T64En = 0x00004000, + D64En = 0x00001000, + M64En = 0x00000800, + PhyRst = 0x00000400, + PhyDis = 0x00000200, + ExtStEn = 0x00000100, + BEMode = 0x00000001, +}; +#define SpeedStatus_Polarity ( GigSpeed | HundSpeed | FullDuplex) + +enum TxConfig_bits { + TxDrthMask = 0x000000ff, + TxFlthMask = 0x0000ff00, + TxMxdmaMask = 0x00700000, + TxMxdma_8 = 0x00100000, + TxMxdma_16 = 0x00200000, + TxMxdma_32 = 0x00300000, + TxMxdma_64 = 0x00400000, + TxMxdma_128 = 0x00500000, + TxMxdma_256 = 0x00600000, + TxMxdma_512 = 0x00700000, + TxMxdma_1024 = 0x00000000, + TxCollRetry = 0x00800000, + TxAutoPad = 0x10000000, + TxMacLoop = 0x20000000, + TxHeartIgn = 0x40000000, + TxCarrierIgn = 0x80000000 +}; + +enum RxConfig_bits { + RxDrthMask = 0x0000003e, + RxMxdmaMask = 0x00700000, + RxMxdma_8 = 0x00100000, + RxMxdma_16 = 0x00200000, + RxMxdma_32 = 0x00300000, + RxMxdma_64 = 0x00400000, + RxMxdma_128 = 0x00500000, + RxMxdma_256 = 0x00600000, + RxMxdma_512 = 0x00700000, + RxMxdma_1024 = 0x00000000, + RxAcceptLenErr = 0x04000000, + RxAcceptLong = 0x08000000, + RxAcceptTx = 0x10000000, + RxStripCRC = 0x20000000, + RxAcceptRunt = 0x40000000, + RxAcceptErr = 0x80000000, +}; + +/* Bits in the RxMode register. */ +enum rx_mode_bits { + RxFilterEnable = 0x80000000, + AcceptAllBroadcast = 0x40000000, + AcceptAllMulticast = 0x20000000, + AcceptAllUnicast = 0x10000000, + AcceptPerfectMatch = 0x08000000, +}; + +typedef struct _BufferDesc { + u32 link; + u32 bufptr; + vu_long cmdsts; + u32 extsts; /*not used here */ +} BufferDesc; + +/* Bits in network_desc.status */ +enum desc_status_bits { + DescOwn = 0x80000000, DescMore = 0x40000000, DescIntr = 0x20000000, + DescNoCRC = 0x10000000, DescPktOK = 0x08000000, + DescSizeMask = 0xfff, + + DescTxAbort = 0x04000000, DescTxFIFO = 0x02000000, + DescTxCarrier = 0x01000000, DescTxDefer = 0x00800000, + DescTxExcDefer = 0x00400000, DescTxOOWCol = 0x00200000, + DescTxExcColl = 0x00100000, DescTxCollCount = 0x000f0000, + + DescRxAbort = 0x04000000, DescRxOver = 0x02000000, + DescRxDest = 0x01800000, DescRxLong = 0x00400000, + DescRxRunt = 0x00200000, DescRxInvalid = 0x00100000, + DescRxCRC = 0x00080000, DescRxAlign = 0x00040000, + DescRxLoop = 0x00020000, DesRxColl = 0x00010000, +}; + +/* Bits in MEAR */ +enum mii_reg_bits { + MDIO_ShiftClk = 0x0040, + MDIO_EnbOutput = 0x0020, + MDIO_Data = 0x0010, +}; + +/* PHY Register offsets. */ +enum phy_reg_offsets { + BMCR = 0x00, + BMSR = 0x01, + PHYIDR1 = 0x02, + PHYIDR2 = 0x03, + ANAR = 0x04, + KTCR = 0x09, +}; + +/* basic mode control register bits */ +enum bmcr_bits { + Bmcr_Reset = 0x8000, + Bmcr_Loop = 0x4000, + Bmcr_Speed0 = 0x2000, + Bmcr_AutoNegEn = 0x1000, /*if set ignores Duplex, Speed[01] */ + Bmcr_RstAutoNeg = 0x0200, + Bmcr_Duplex = 0x0100, + Bmcr_Speed1 = 0x0040, + Bmcr_Force10H = 0x0000, + Bmcr_Force10F = 0x0100, + Bmcr_Force100H = 0x2000, + Bmcr_Force100F = 0x2100, + Bmcr_Force1000H = 0x0040, + Bmcr_Force1000F = 0x0140, +}; + +/* auto negotiation advertisement register */ +enum anar_bits { + anar_adv_100F = 0x0100, + anar_adv_100H = 0x0080, + anar_adv_10F = 0x0040, + anar_adv_10H = 0x0020, + anar_ieee_8023 = 0x0001, +}; + +/* 1K-base T control register */ +enum ktcr_bits { + ktcr_adv_1000H = 0x0100, + ktcr_adv_1000F = 0x0200, +}; + +/* Globals */ +static u32 SavedClkRun; +static unsigned int cur_rx; +static unsigned int rx_config; +static unsigned int tx_config; + +/* Note: transmit and receive buffers and descriptors must be + long long word aligned */ +static BufferDesc txd __attribute__ ((aligned(8))); +static BufferDesc rxd[NUM_RX_DESC] __attribute__ ((aligned(8))); +static unsigned char txb[TX_BUF_SIZE] __attribute__ ((aligned(8))); +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE] + __attribute__ ((aligned(8))); + +/* Function Prototypes */ +static int mdio_read(struct eth_device *dev, int phy_id, int addr); +static void mdio_write(struct eth_device *dev, int phy_id, int addr, int value); +static void mdio_sync(struct eth_device *dev, u32 offset); +static int ns8382x_init(struct eth_device *dev, bd_t * bis); +static void ns8382x_reset(struct eth_device *dev); +static void ns8382x_init_rxfilter(struct eth_device *dev); +static void ns8382x_init_txd(struct eth_device *dev); +static void ns8382x_init_rxd(struct eth_device *dev); +static void ns8382x_set_rx_mode(struct eth_device *dev); +static void ns8382x_check_duplex(struct eth_device *dev); +static int ns8382x_send(struct eth_device *dev, void *packet, int length); +static int ns8382x_poll(struct eth_device *dev); +static void ns8382x_disable(struct eth_device *dev); + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_83820}, + {} +}; + +#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) + +static inline int +INW(struct eth_device *dev, u_long addr) +{ + return le16_to_cpu(*(vu_short *) (addr + dev->iobase)); +} + +static int +INL(struct eth_device *dev, u_long addr) +{ + return le32_to_cpu(*(vu_long *) (addr + dev->iobase)); +} + +static inline void +OUTW(struct eth_device *dev, int command, u_long addr) +{ + *(vu_short *) ((addr + dev->iobase)) = cpu_to_le16(command); +} + +static inline void +OUTL(struct eth_device *dev, int command, u_long addr) +{ + *(vu_long *) ((addr + dev->iobase)) = cpu_to_le32(command); +} + +/* Function: ns8382x_initialize + * Description: Retrieves the MAC address of the card, and sets up some + * globals required by other routines, and initializes the NIC, making it + * ready to send and receive packets. + * Side effects: initializes ns8382xs, ready to receive packets. + * Returns: int: number of cards found + */ + +int +ns8382x_initialize(bd_t * bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase, status; + int i, idx = 0; + u32 phyAddress; + u32 tmp; + u32 chip_config; + + while (1) { /* Find PCI device(s) */ + if ((devno = pci_find_devices(supported, idx++)) < 0) + break; + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); + iobase &= ~0x3; /* 1: unused and 0:I/O Space Indicator */ + + debug("ns8382x: NatSemi dp8382x @ 0x%x\n", iobase); + + pci_write_config_dword(devno, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Check if I/O accesses and Bus Mastering are enabled. */ + pci_read_config_dword(devno, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_MEMORY)) { + printf("Error: Can not enable MEM access.\n"); + continue; + } else if (!(status & PCI_COMMAND_MASTER)) { + printf("Error: Can not enable Bus Mastering.\n"); + continue; + } + + dev = (struct eth_device *) malloc(sizeof *dev); + if (!dev) { + printf("ns8382x: Can not allocate memory\n"); + break; + } + memset(dev, 0, sizeof(*dev)); + + sprintf(dev->name, "dp8382x#%d", card_number); + dev->iobase = bus_to_phys(iobase); + dev->priv = (void *) devno; + dev->init = ns8382x_init; + dev->halt = ns8382x_disable; + dev->send = ns8382x_send; + dev->recv = ns8382x_poll; + + /* ns8382x has a non-standard PM control register + * in PCI config space. Some boards apparently need + * to be brought to D0 in this manner. */ + pci_read_config_dword(devno, PCIPM, &tmp); + if (tmp & (0x03 | 0x100)) { /* D0 state, disable PME assertion */ + u32 newtmp = tmp & ~(0x03 | 0x100); + pci_write_config_dword(devno, PCIPM, newtmp); + } + + /* get MAC address */ + for (i = 0; i < 3; i++) { + u32 data; + char *mac = (char *)&dev->enetaddr[i * 2]; + + OUTL(dev, i * 2, RxFilterAddr); + data = INL(dev, RxFilterData); + *mac++ = data; + *mac++ = data >> 8; + } + /* get PHY address, can't be zero */ + for (phyAddress = 1; phyAddress < 32; phyAddress++) { + u32 rev, phy1; + + phy1 = mdio_read(dev, phyAddress, PHYIDR1); + if (phy1 == 0x2000) { /*check for 83861/91 */ + rev = mdio_read(dev, phyAddress, PHYIDR2); + if ((rev & ~(0x000f)) == 0x00005c50 || + (rev & ~(0x000f)) == 0x00005c60) { + debug("phy rev is %x\n", rev); + debug("phy address is %x\n", + phyAddress); + break; + } + } + } + + /* set phy to autonegotiate && advertise everything */ + mdio_write(dev, phyAddress, KTCR, + (ktcr_adv_1000H | ktcr_adv_1000F)); + mdio_write(dev, phyAddress, ANAR, + (anar_adv_100F | anar_adv_100H | anar_adv_10H | + anar_adv_10F | anar_ieee_8023)); + mdio_write(dev, phyAddress, BMCR, 0x0); /*restore */ + mdio_write(dev, phyAddress, BMCR, + (Bmcr_AutoNegEn | Bmcr_RstAutoNeg)); + /* Reset the chip to erase any previous misconfiguration. */ + OUTL(dev, (ChipReset), ChipCmd); + + chip_config = INL(dev, ChipConfig); + /* reset the phy */ + OUTL(dev, (chip_config | PhyRst), ChipConfig); + /* power up and initialize transceiver */ + OUTL(dev, (chip_config & ~(PhyDis)), ChipConfig); + + mdio_sync(dev, EECtrl); + + { + u32 chpcfg = + INL(dev, ChipConfig) ^ SpeedStatus_Polarity; + + debug("%s: Transceiver 10%s %s duplex.\n", dev->name, + (chpcfg & GigSpeed) ? "00" : (chpcfg & HundSpeed) + ? "0" : "", + chpcfg & FullDuplex ? "full" : "half"); + debug("%s: %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, + dev->enetaddr[0], dev->enetaddr[1], + dev->enetaddr[2], dev->enetaddr[3], + dev->enetaddr[4], dev->enetaddr[5]); + } + + /* Disable PME: + * The PME bit is initialized from the EEPROM contents. + * PCI cards probably have PME disabled, but motherboard + * implementations may have PME set to enable WakeOnLan. + * With PME set the chip will scan incoming packets but + * nothing will be written to memory. */ + SavedClkRun = INL(dev, ClkRun); + OUTL(dev, SavedClkRun & ~0x100, ClkRun); + + eth_register(dev); + + card_number++; + + pci_write_config_byte(devno, PCI_LATENCY_TIMER, 0x60); + + udelay(10 * 1000); + } + return card_number; +} + +/* MII transceiver control section. + Read and write MII registers using software-generated serial MDIO + protocol. See the MII specifications or DP83840A data sheet for details. + + The maximum data clock rate is 2.5 MHz. To meet minimum timing we + must flush writes to the PCI bus with a PCI read. */ +#define mdio_delay(mdio_addr) INL(dev, mdio_addr) + +#define MDIO_EnbIn (0) +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void +mdio_sync(struct eth_device *dev, u32 offset) +{ + int bits = 32; + + /* Establish sync by sending at least 32 logic ones. */ + while (--bits >= 0) { + OUTL(dev, MDIO_WRITE1, offset); + mdio_delay(offset); + OUTL(dev, MDIO_WRITE1 | MDIO_ShiftClk, offset); + mdio_delay(offset); + } +} + +static int +mdio_read(struct eth_device *dev, int phy_id, int addr) +{ + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | addr; + int i, retval = 0; + + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + OUTL(dev, dataval, EECtrl); + mdio_delay(EECtrl); + OUTL(dev, dataval | MDIO_ShiftClk, EECtrl); + mdio_delay(EECtrl); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + OUTL(dev, MDIO_EnbIn, EECtrl); + mdio_delay(EECtrl); + retval = + (retval << 1) | ((INL(dev, EECtrl) & MDIO_Data) ? 1 : 0); + OUTL(dev, MDIO_EnbIn | MDIO_ShiftClk, EECtrl); + mdio_delay(EECtrl); + } + return (retval >> 1) & 0xffff; +} + +static void +mdio_write(struct eth_device *dev, int phy_id, int addr, int value) +{ + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (addr << 18) | value; + int i; + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + OUTL(dev, dataval, EECtrl); + mdio_delay(EECtrl); + OUTL(dev, dataval | MDIO_ShiftClk, EECtrl); + mdio_delay(EECtrl); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + OUTL(dev, MDIO_EnbIn, EECtrl); + mdio_delay(EECtrl); + OUTL(dev, MDIO_EnbIn | MDIO_ShiftClk, EECtrl); + mdio_delay(EECtrl); + } + return; +} + +/* Function: ns8382x_init + * Description: resets the ethernet controller chip and configures + * registers and data structures required for sending and receiving packets. + * Arguments: struct eth_device *dev: NIC data structure + * returns: int. + */ + +static int +ns8382x_init(struct eth_device *dev, bd_t * bis) +{ + u32 config; + + ns8382x_reset(dev); + + /* Disable PME: + * The PME bit is initialized from the EEPROM contents. + * PCI cards probably have PME disabled, but motherboard + * implementations may have PME set to enable WakeOnLan. + * With PME set the chip will scan incoming packets but + * nothing will be written to memory. */ + OUTL(dev, SavedClkRun & ~0x100, ClkRun); + + ns8382x_init_rxfilter(dev); + ns8382x_init_txd(dev); + ns8382x_init_rxd(dev); + + /*set up ChipConfig */ + config = INL(dev, ChipConfig); + /*turn off 64 bit ops && Ten-bit interface + * && big-endian mode && extended status */ + config &= ~(TBIEn | Mode1000 | T64En | D64En | M64En | BEMode | PhyDis | ExtStEn); + OUTL(dev, config, ChipConfig); + + /* Configure the PCI bus bursts and FIFO thresholds. */ + tx_config = TxCarrierIgn | TxHeartIgn | TxAutoPad + | TxCollRetry | TxMxdma_1024 | (0x1002); + rx_config = RxMxdma_1024 | 0x20; + + debug("%s: Setting TxConfig Register %#08X\n", dev->name, tx_config); + debug("%s: Setting RxConfig Register %#08X\n", dev->name, rx_config); + + OUTL(dev, tx_config, TxConfig); + OUTL(dev, rx_config, RxConfig); + + /*turn off priority queueing */ + OUTL(dev, 0x0, PriQueue); + + ns8382x_check_duplex(dev); + ns8382x_set_rx_mode(dev); + + OUTL(dev, (RxOn | TxOn), ChipCmd); + return 1; +} + +/* Function: ns8382x_reset + * Description: soft resets the controller chip + * Arguments: struct eth_device *dev: NIC data structure + * Returns: void. + */ +static void +ns8382x_reset(struct eth_device *dev) +{ + OUTL(dev, ChipReset, ChipCmd); + while (INL(dev, ChipCmd)) + /*wait until done */ ; + OUTL(dev, 0, IntrMask); + OUTL(dev, 0, IntrEnable); +} + +/* Function: ns8382x_init_rxfilter + * Description: sets receive filter address to our MAC address + * Arguments: struct eth_device *dev: NIC data structure + * returns: void. + */ + +static void +ns8382x_init_rxfilter(struct eth_device *dev) +{ + int i; + + for (i = 0; i < ETH_ALEN; i += 2) { + OUTL(dev, i, RxFilterAddr); + OUTW(dev, dev->enetaddr[i] + (dev->enetaddr[i + 1] << 8), + RxFilterData); + } +} + +/* Function: ns8382x_init_txd + * Description: initializes the Tx descriptor + * Arguments: struct eth_device *dev: NIC data structure + * returns: void. + */ + +static void +ns8382x_init_txd(struct eth_device *dev) +{ + txd.link = (u32) 0; + txd.bufptr = cpu_to_le32((u32) & txb[0]); + txd.cmdsts = (u32) 0; + txd.extsts = (u32) 0; + + OUTL(dev, 0x0, TxRingPtrHi); + OUTL(dev, phys_to_bus((u32)&txd), TxRingPtr); + + debug("ns8382x_init_txd: TX descriptor register loaded with: %#08X (&txd: %p)\n", + INL(dev, TxRingPtr), &txd); +} + +/* Function: ns8382x_init_rxd + * Description: initializes the Rx descriptor ring + * Arguments: struct eth_device *dev: NIC data structure + * Returns: void. + */ + +static void +ns8382x_init_rxd(struct eth_device *dev) +{ + int i; + + OUTL(dev, 0x0, RxRingPtrHi); + + cur_rx = 0; + for (i = 0; i < NUM_RX_DESC; i++) { + rxd[i].link = + cpu_to_le32((i + 1 < + NUM_RX_DESC) ? (u32) & rxd[i + + 1] : (u32) & + rxd[0]); + rxd[i].extsts = cpu_to_le32((u32) 0x0); + rxd[i].cmdsts = cpu_to_le32((u32) RX_BUF_SIZE); + rxd[i].bufptr = cpu_to_le32((u32) & rxb[i * RX_BUF_SIZE]); + + debug + ("ns8382x_init_rxd: rxd[%d]=%p link=%X cmdsts=%X bufptr=%X\n", + i, &rxd[i], le32_to_cpu(rxd[i].link), + le32_to_cpu(rxd[i].cmdsts), le32_to_cpu(rxd[i].bufptr)); + } + OUTL(dev, phys_to_bus((u32) & rxd), RxRingPtr); + + debug("ns8382x_init_rxd: RX descriptor register loaded with: %X\n", + INL(dev, RxRingPtr)); +} + +/* Function: ns8382x_set_rx_mode + * Description: + * sets the receive mode to accept all broadcast packets and packets + * with our MAC address, and reject all multicast packets. + * Arguments: struct eth_device *dev: NIC data structure + * Returns: void. + */ + +static void +ns8382x_set_rx_mode(struct eth_device *dev) +{ + u32 rx_mode = 0x0; + /*spec says RxFilterEnable has to be 0 for rest of + * this stuff to be properly configured. Linux driver + * seems to support this*/ +/* OUTL(dev, rx_mode, RxFilterAddr);*/ + rx_mode = (RxFilterEnable | AcceptAllBroadcast | AcceptPerfectMatch); + OUTL(dev, rx_mode, RxFilterAddr); + printf("ns8382x_set_rx_mode: set to %X\n", rx_mode); + /*now we turn RxFilterEnable back on */ + /*rx_mode |= RxFilterEnable; + OUTL(dev, rx_mode, RxFilterAddr);*/ +} + +static void +ns8382x_check_duplex(struct eth_device *dev) +{ + int gig = 0; + int hun = 0; + int duplex = 0; + int config = (INL(dev, ChipConfig) ^ SpeedStatus_Polarity); + + duplex = (config & FullDuplex) ? 1 : 0; + gig = (config & GigSpeed) ? 1 : 0; + hun = (config & HundSpeed) ? 1 : 0; + + debug("%s: Setting 10%s %s-duplex based on negotiated link" + " capability.\n", dev->name, (gig) ? "00" : (hun) ? "0" : "", + duplex ? "full" : "half"); + + if (duplex) { + rx_config |= RxAcceptTx; + tx_config |= (TxCarrierIgn | TxHeartIgn); + } else { + rx_config &= ~RxAcceptTx; + tx_config &= ~(TxCarrierIgn | TxHeartIgn); + } + + debug("%s: Resetting TxConfig Register %#08X\n", dev->name, tx_config); + debug("%s: Resetting RxConfig Register %#08X\n", dev->name, rx_config); + + OUTL(dev, tx_config, TxConfig); + OUTL(dev, rx_config, RxConfig); + + /*if speed is 10 or 100, remove MODE1000, + * if it's 1000, then set it */ + config = INL(dev, ChipConfig); + if (gig) + config |= Mode1000; + else + config &= ~Mode1000; + + debug("%s: %setting Mode1000\n", dev->name, (gig) ? "S" : "Uns"); + + OUTL(dev, config, ChipConfig); +} + +/* Function: ns8382x_send + * Description: transmits a packet and waits for completion or timeout. + * Returns: void. */ +static int ns8382x_send(struct eth_device *dev, void *packet, int length) +{ + u32 i, status = 0; + vu_long tx_stat = 0; + + /* Stop the transmitter */ + OUTL(dev, TxOff, ChipCmd); + + debug("ns8382x_send: sending %d bytes\n", (int)length); + + /* set the transmit buffer descriptor and enable Transmit State Machine */ + txd.link = cpu_to_le32(0x0); + txd.bufptr = cpu_to_le32(phys_to_bus((u32)packet)); + txd.extsts = cpu_to_le32(0x0); + txd.cmdsts = cpu_to_le32(DescOwn | length); + + /* load Transmit Descriptor Register */ + OUTL(dev, phys_to_bus((u32) & txd), TxRingPtr); + + debug("ns8382x_send: TX descriptor register loaded with: %#08X\n", + INL(dev, TxRingPtr)); + debug("\ttxd.link:%X\tbufp:%X\texsts:%X\tcmdsts:%X\n", + le32_to_cpu(txd.link), le32_to_cpu(txd.bufptr), + le32_to_cpu(txd.extsts), le32_to_cpu(txd.cmdsts)); + + /* restart the transmitter */ + OUTL(dev, TxOn, ChipCmd); + + for (i = 0; (tx_stat = le32_to_cpu(txd.cmdsts)) & DescOwn; i++) { + if (i >= TOUT_LOOP) { + printf ("%s: tx error buffer not ready: txd.cmdsts %#lX\n", + dev->name, tx_stat); + goto Done; + } + } + + if (!(tx_stat & DescPktOK)) { + printf("ns8382x_send: Transmit error, Tx status %lX.\n", tx_stat); + goto Done; + } + + debug("ns8382x_send: tx_stat: %#08lX\n", tx_stat); + + status = 1; +Done: + return status; +} + +/* Function: ns8382x_poll + * Description: checks for a received packet and returns it if found. + * Arguments: struct eth_device *dev: NIC data structure + * Returns: 1 if packet was received. + * 0 if no packet was received. + * Side effects: + * Returns (copies) the packet to the array dev->packet. + * Returns the length of the packet. + */ + +static int +ns8382x_poll(struct eth_device *dev) +{ + int retstat = 0; + int length = 0; + vu_long rx_status = le32_to_cpu(rxd[cur_rx].cmdsts); + + if (!(rx_status & (u32) DescOwn)) + return retstat; + + debug("ns8382x_poll: got a packet: cur_rx:%u, status:%lx\n", + cur_rx, rx_status); + + length = (rx_status & DSIZE) - CRC_SIZE; + + if ((rx_status & (DescMore | DescPktOK | DescRxLong)) != DescPktOK) { + /* corrupted packet received */ + printf("ns8382x_poll: Corrupted packet, status:%lx\n", rx_status); + retstat = 0; + } else { + /* give packet to higher level routine */ + NetReceive((rxb + cur_rx * RX_BUF_SIZE), length); + retstat = 1; + } + + /* return the descriptor and buffer to receive ring */ + rxd[cur_rx].cmdsts = cpu_to_le32(RX_BUF_SIZE); + rxd[cur_rx].bufptr = cpu_to_le32((u32) & rxb[cur_rx * RX_BUF_SIZE]); + + if (++cur_rx == NUM_RX_DESC) + cur_rx = 0; + + /* re-enable the potentially idle receive state machine */ + OUTL(dev, RxOn, ChipCmd); + + return retstat; +} + +/* Function: ns8382x_disable + * Description: Turns off interrupts and stops Tx and Rx engines + * Arguments: struct eth_device *dev: NIC data structure + * Returns: void. + */ + +static void +ns8382x_disable(struct eth_device *dev) +{ + /* Disable interrupts using the mask. */ + OUTL(dev, 0, IntrMask); + OUTL(dev, 0, IntrEnable); + + /* Stop the chip's Tx and Rx processes. */ + OUTL(dev, (RxOff | TxOff), ChipCmd); + + /* Restore PME enable bit */ + OUTL(dev, SavedClkRun, ClkRun); +} diff --git a/qemu/roms/u-boot/drivers/net/pcnet.c b/qemu/roms/u-boot/drivers/net/pcnet.c new file mode 100644 index 000000000..237fbba51 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/pcnet.c @@ -0,0 +1,542 @@ +/* + * (C) Copyright 2002 Wolfgang Grandegger, wg@denx.de. + * + * This driver for AMD PCnet network controllers is derived from the + * Linux driver pcnet32.c written 1996-1999 by Thomas Bogendoerfer. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#define PCNET_DEBUG_LEVEL 0 /* 0=off, 1=init, 2=rx/tx */ + +#define PCNET_DEBUG1(fmt,args...) \ + debug_cond(PCNET_DEBUG_LEVEL > 0, fmt ,##args) +#define PCNET_DEBUG2(fmt,args...) \ + debug_cond(PCNET_DEBUG_LEVEL > 1, fmt ,##args) + +#if !defined(CONF_PCNET_79C973) && defined(CONF_PCNET_79C975) +#error "Macro for PCnet chip version is not defined!" +#endif + +/* + * Set the number of Tx and Rx buffers, using Log_2(# buffers). + * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. + * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). + */ +#define PCNET_LOG_TX_BUFFERS 0 +#define PCNET_LOG_RX_BUFFERS 2 + +#define TX_RING_SIZE (1 << (PCNET_LOG_TX_BUFFERS)) +#define TX_RING_LEN_BITS ((PCNET_LOG_TX_BUFFERS) << 12) + +#define RX_RING_SIZE (1 << (PCNET_LOG_RX_BUFFERS)) +#define RX_RING_LEN_BITS ((PCNET_LOG_RX_BUFFERS) << 4) + +#define PKT_BUF_SZ 1544 + +/* The PCNET Rx and Tx ring descriptors. */ +struct pcnet_rx_head { + u32 base; + s16 buf_length; + s16 status; + u32 msg_length; + u32 reserved; +}; + +struct pcnet_tx_head { + u32 base; + s16 length; + s16 status; + u32 misc; + u32 reserved; +}; + +/* The PCNET 32-Bit initialization block, described in databook. */ +struct pcnet_init_block { + u16 mode; + u16 tlen_rlen; + u8 phys_addr[6]; + u16 reserved; + u32 filter[2]; + /* Receive and transmit ring base, along with extra bits. */ + u32 rx_ring; + u32 tx_ring; + u32 reserved2; +}; + +struct pcnet_uncached_priv { + struct pcnet_rx_head rx_ring[RX_RING_SIZE]; + struct pcnet_tx_head tx_ring[TX_RING_SIZE]; + struct pcnet_init_block init_block; +}; + +typedef struct pcnet_priv { + struct pcnet_uncached_priv *uc; + /* Receive Buffer space */ + unsigned char (*rx_buf)[RX_RING_SIZE][PKT_BUF_SZ + 4]; + int cur_rx; + int cur_tx; +} pcnet_priv_t; + +static pcnet_priv_t *lp; + +/* Offsets from base I/O address for WIO mode */ +#define PCNET_RDP 0x10 +#define PCNET_RAP 0x12 +#define PCNET_RESET 0x14 +#define PCNET_BDP 0x16 + +static u16 pcnet_read_csr(struct eth_device *dev, int index) +{ + outw(index, dev->iobase + PCNET_RAP); + return inw(dev->iobase + PCNET_RDP); +} + +static void pcnet_write_csr(struct eth_device *dev, int index, u16 val) +{ + outw(index, dev->iobase + PCNET_RAP); + outw(val, dev->iobase + PCNET_RDP); +} + +static u16 pcnet_read_bcr(struct eth_device *dev, int index) +{ + outw(index, dev->iobase + PCNET_RAP); + return inw(dev->iobase + PCNET_BDP); +} + +static void pcnet_write_bcr(struct eth_device *dev, int index, u16 val) +{ + outw(index, dev->iobase + PCNET_RAP); + outw(val, dev->iobase + PCNET_BDP); +} + +static void pcnet_reset(struct eth_device *dev) +{ + inw(dev->iobase + PCNET_RESET); +} + +static int pcnet_check(struct eth_device *dev) +{ + outw(88, dev->iobase + PCNET_RAP); + return inw(dev->iobase + PCNET_RAP) == 88; +} + +static int pcnet_init (struct eth_device *dev, bd_t * bis); +static int pcnet_send(struct eth_device *dev, void *packet, int length); +static int pcnet_recv (struct eth_device *dev); +static void pcnet_halt (struct eth_device *dev); +static int pcnet_probe (struct eth_device *dev, bd_t * bis, int dev_num); + +#define PCI_TO_MEM(d, a) pci_virt_to_mem((pci_dev_t)d->priv, (a)) +#define PCI_TO_MEM_LE(d,a) (u32)(cpu_to_le32(PCI_TO_MEM(d,a))) + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE}, + {} +}; + + +int pcnet_initialize(bd_t *bis) +{ + pci_dev_t devbusfn; + struct eth_device *dev; + u16 command, status; + int dev_nr = 0; + + PCNET_DEBUG1("\npcnet_initialize...\n"); + + for (dev_nr = 0;; dev_nr++) { + + /* + * Find the PCnet PCI device(s). + */ + devbusfn = pci_find_devices(supported, dev_nr); + if (devbusfn < 0) + break; + + /* + * Allocate and pre-fill the device structure. + */ + dev = (struct eth_device *)malloc(sizeof(*dev)); + if (!dev) { + printf("pcnet: Can not allocate memory\n"); + break; + } + memset(dev, 0, sizeof(*dev)); + dev->priv = (void *)devbusfn; + sprintf(dev->name, "pcnet#%d", dev_nr); + + /* + * Setup the PCI device. + */ + pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, + (unsigned int *)&dev->iobase); + dev->iobase = pci_io_to_phys(devbusfn, dev->iobase); + dev->iobase &= ~0xf; + + PCNET_DEBUG1("%s: devbusfn=0x%x iobase=0x%x: ", + dev->name, devbusfn, dev->iobase); + + command = PCI_COMMAND_IO | PCI_COMMAND_MASTER; + pci_write_config_word(devbusfn, PCI_COMMAND, command); + pci_read_config_word(devbusfn, PCI_COMMAND, &status); + if ((status & command) != command) { + printf("%s: Couldn't enable IO access or Bus Mastering\n", + dev->name); + free(dev); + continue; + } + + pci_write_config_byte(devbusfn, PCI_LATENCY_TIMER, 0x40); + + /* + * Probe the PCnet chip. + */ + if (pcnet_probe(dev, bis, dev_nr) < 0) { + free(dev); + continue; + } + + /* + * Setup device structure and register the driver. + */ + dev->init = pcnet_init; + dev->halt = pcnet_halt; + dev->send = pcnet_send; + dev->recv = pcnet_recv; + + eth_register(dev); + } + + udelay(10 * 1000); + + return dev_nr; +} + +static int pcnet_probe(struct eth_device *dev, bd_t *bis, int dev_nr) +{ + int chip_version; + char *chipname; + +#ifdef PCNET_HAS_PROM + int i; +#endif + + /* Reset the PCnet controller */ + pcnet_reset(dev); + + /* Check if register access is working */ + if (pcnet_read_csr(dev, 0) != 4 || !pcnet_check(dev)) { + printf("%s: CSR register access check failed\n", dev->name); + return -1; + } + + /* Identify the chip */ + chip_version = + pcnet_read_csr(dev, 88) | (pcnet_read_csr(dev, 89) << 16); + if ((chip_version & 0xfff) != 0x003) + return -1; + chip_version = (chip_version >> 12) & 0xffff; + switch (chip_version) { + case 0x2621: + chipname = "PCnet/PCI II 79C970A"; /* PCI */ + break; +#ifdef CONFIG_PCNET_79C973 + case 0x2625: + chipname = "PCnet/FAST III 79C973"; /* PCI */ + break; +#endif +#ifdef CONFIG_PCNET_79C975 + case 0x2627: + chipname = "PCnet/FAST III 79C975"; /* PCI */ + break; +#endif + default: + printf("%s: PCnet version %#x not supported\n", + dev->name, chip_version); + return -1; + } + + PCNET_DEBUG1("AMD %s\n", chipname); + +#ifdef PCNET_HAS_PROM + /* + * In most chips, after a chip reset, the ethernet address is read from + * the station address PROM at the base address and programmed into the + * "Physical Address Registers" CSR12-14. + */ + for (i = 0; i < 3; i++) { + unsigned int val; + + val = pcnet_read_csr(dev, i + 12) & 0x0ffff; + /* There may be endianness issues here. */ + dev->enetaddr[2 * i] = val & 0x0ff; + dev->enetaddr[2 * i + 1] = (val >> 8) & 0x0ff; + } +#endif /* PCNET_HAS_PROM */ + + return 0; +} + +static int pcnet_init(struct eth_device *dev, bd_t *bis) +{ + struct pcnet_uncached_priv *uc; + int i, val; + u32 addr; + + PCNET_DEBUG1("%s: pcnet_init...\n", dev->name); + + /* Switch pcnet to 32bit mode */ + pcnet_write_bcr(dev, 20, 2); + + /* Set/reset autoselect bit */ + val = pcnet_read_bcr(dev, 2) & ~2; + val |= 2; + pcnet_write_bcr(dev, 2, val); + + /* Enable auto negotiate, setup, disable fd */ + val = pcnet_read_bcr(dev, 32) & ~0x98; + val |= 0x20; + pcnet_write_bcr(dev, 32, val); + + /* + * Enable NOUFLO on supported controllers, with the transmit + * start point set to the full packet. This will cause entire + * packets to be buffered by the ethernet controller before + * transmission, eliminating underflows which are common on + * slower devices. Controllers which do not support NOUFLO will + * simply be left with a larger transmit FIFO threshold. + */ + val = pcnet_read_bcr(dev, 18); + val |= 1 << 11; + pcnet_write_bcr(dev, 18, val); + val = pcnet_read_csr(dev, 80); + val |= 0x3 << 10; + pcnet_write_csr(dev, 80, val); + + /* + * We only maintain one structure because the drivers will never + * be used concurrently. In 32bit mode the RX and TX ring entries + * must be aligned on 16-byte boundaries. + */ + if (lp == NULL) { + addr = (u32)malloc(sizeof(pcnet_priv_t) + 0x10); + addr = (addr + 0xf) & ~0xf; + lp = (pcnet_priv_t *)addr; + + addr = (u32)memalign(ARCH_DMA_MINALIGN, sizeof(*lp->uc)); + flush_dcache_range(addr, addr + sizeof(*lp->uc)); + addr = UNCACHED_SDRAM(addr); + lp->uc = (struct pcnet_uncached_priv *)addr; + + addr = (u32)memalign(ARCH_DMA_MINALIGN, sizeof(*lp->rx_buf)); + flush_dcache_range(addr, addr + sizeof(*lp->rx_buf)); + lp->rx_buf = (void *)addr; + } + + uc = lp->uc; + + uc->init_block.mode = cpu_to_le16(0x0000); + uc->init_block.filter[0] = 0x00000000; + uc->init_block.filter[1] = 0x00000000; + + /* + * Initialize the Rx ring. + */ + lp->cur_rx = 0; + for (i = 0; i < RX_RING_SIZE; i++) { + uc->rx_ring[i].base = PCI_TO_MEM_LE(dev, (*lp->rx_buf)[i]); + uc->rx_ring[i].buf_length = cpu_to_le16(-PKT_BUF_SZ); + uc->rx_ring[i].status = cpu_to_le16(0x8000); + PCNET_DEBUG1 + ("Rx%d: base=0x%x buf_length=0x%hx status=0x%hx\n", i, + uc->rx_ring[i].base, uc->rx_ring[i].buf_length, + uc->rx_ring[i].status); + } + + /* + * Initialize the Tx ring. The Tx buffer address is filled in as + * needed, but we do need to clear the upper ownership bit. + */ + lp->cur_tx = 0; + for (i = 0; i < TX_RING_SIZE; i++) { + uc->tx_ring[i].base = 0; + uc->tx_ring[i].status = 0; + } + + /* + * Setup Init Block. + */ + PCNET_DEBUG1("Init block at 0x%p: MAC", &lp->uc->init_block); + + for (i = 0; i < 6; i++) { + lp->uc->init_block.phys_addr[i] = dev->enetaddr[i]; + PCNET_DEBUG1(" %02x", lp->uc->init_block.phys_addr[i]); + } + + uc->init_block.tlen_rlen = cpu_to_le16(TX_RING_LEN_BITS | + RX_RING_LEN_BITS); + uc->init_block.rx_ring = PCI_TO_MEM_LE(dev, uc->rx_ring); + uc->init_block.tx_ring = PCI_TO_MEM_LE(dev, uc->tx_ring); + + PCNET_DEBUG1("\ntlen_rlen=0x%x rx_ring=0x%x tx_ring=0x%x\n", + uc->init_block.tlen_rlen, + uc->init_block.rx_ring, uc->init_block.tx_ring); + + /* + * Tell the controller where the Init Block is located. + */ + barrier(); + addr = PCI_TO_MEM(dev, &lp->uc->init_block); + pcnet_write_csr(dev, 1, addr & 0xffff); + pcnet_write_csr(dev, 2, (addr >> 16) & 0xffff); + + pcnet_write_csr(dev, 4, 0x0915); + pcnet_write_csr(dev, 0, 0x0001); /* start */ + + /* Wait for Init Done bit */ + for (i = 10000; i > 0; i--) { + if (pcnet_read_csr(dev, 0) & 0x0100) + break; + udelay(10); + } + if (i <= 0) { + printf("%s: TIMEOUT: controller init failed\n", dev->name); + pcnet_reset(dev); + return -1; + } + + /* + * Finally start network controller operation. + */ + pcnet_write_csr(dev, 0, 0x0002); + + return 0; +} + +static int pcnet_send(struct eth_device *dev, void *packet, int pkt_len) +{ + int i, status; + struct pcnet_tx_head *entry = &lp->uc->tx_ring[lp->cur_tx]; + + PCNET_DEBUG2("Tx%d: %d bytes from 0x%p ", lp->cur_tx, pkt_len, + packet); + + flush_dcache_range((unsigned long)packet, + (unsigned long)packet + pkt_len); + + /* Wait for completion by testing the OWN bit */ + for (i = 1000; i > 0; i--) { + status = readw(&entry->status); + if ((status & 0x8000) == 0) + break; + udelay(100); + PCNET_DEBUG2("."); + } + if (i <= 0) { + printf("%s: TIMEOUT: Tx%d failed (status = 0x%x)\n", + dev->name, lp->cur_tx, status); + pkt_len = 0; + goto failure; + } + + /* + * Setup Tx ring. Caution: the write order is important here, + * set the status with the "ownership" bits last. + */ + writew(-pkt_len, &entry->length); + writel(0, &entry->misc); + writel(PCI_TO_MEM(dev, packet), &entry->base); + writew(0x8300, &entry->status); + + /* Trigger an immediate send poll. */ + pcnet_write_csr(dev, 0, 0x0008); + + failure: + if (++lp->cur_tx >= TX_RING_SIZE) + lp->cur_tx = 0; + + PCNET_DEBUG2("done\n"); + return pkt_len; +} + +static int pcnet_recv (struct eth_device *dev) +{ + struct pcnet_rx_head *entry; + unsigned char *buf; + int pkt_len = 0; + u16 status, err_status; + + while (1) { + entry = &lp->uc->rx_ring[lp->cur_rx]; + /* + * If we own the next entry, it's a new packet. Send it up. + */ + status = readw(&entry->status); + if ((status & 0x8000) != 0) + break; + err_status = status >> 8; + + if (err_status != 0x03) { /* There was an error. */ + printf("%s: Rx%d", dev->name, lp->cur_rx); + PCNET_DEBUG1(" (status=0x%x)", err_status); + if (err_status & 0x20) + printf(" Frame"); + if (err_status & 0x10) + printf(" Overflow"); + if (err_status & 0x08) + printf(" CRC"); + if (err_status & 0x04) + printf(" Fifo"); + printf(" Error\n"); + status &= 0x03ff; + + } else { + pkt_len = (readl(&entry->msg_length) & 0xfff) - 4; + if (pkt_len < 60) { + printf("%s: Rx%d: invalid packet length %d\n", + dev->name, lp->cur_rx, pkt_len); + } else { + buf = (*lp->rx_buf)[lp->cur_rx]; + invalidate_dcache_range((unsigned long)buf, + (unsigned long)buf + pkt_len); + NetReceive(buf, pkt_len); + PCNET_DEBUG2("Rx%d: %d bytes from 0x%p\n", + lp->cur_rx, pkt_len, buf); + } + } + + status |= 0x8000; + writew(status, &entry->status); + + if (++lp->cur_rx >= RX_RING_SIZE) + lp->cur_rx = 0; + } + return pkt_len; +} + +static void pcnet_halt(struct eth_device *dev) +{ + int i; + + PCNET_DEBUG1("%s: pcnet_halt...\n", dev->name); + + /* Reset the PCnet controller */ + pcnet_reset(dev); + + /* Wait for Stop bit */ + for (i = 1000; i > 0; i--) { + if (pcnet_read_csr(dev, 0) & 0x4) + break; + udelay(10); + } + if (i <= 0) + printf("%s: TIMEOUT: controller reset failed\n", dev->name); +} diff --git a/qemu/roms/u-boot/drivers/net/phy/Makefile b/qemu/roms/u-boot/drivers/net/phy/Makefile new file mode 100644 index 000000000..dbf7bf705 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/Makefile @@ -0,0 +1,26 @@ +# +# (C) Copyright 2008 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_BITBANGMII) += miiphybb.o +obj-$(CONFIG_MV88E61XX_SWITCH) += mv88e61xx.o +obj-$(CONFIG_MV88E6352_SWITCH) += mv88e6352.o + +obj-$(CONFIG_PHYLIB) += phy.o +obj-$(CONFIG_PHYLIB_10G) += generic_10g.o +obj-$(CONFIG_PHY_ATHEROS) += atheros.o +obj-$(CONFIG_PHY_BROADCOM) += broadcom.o +obj-$(CONFIG_PHY_DAVICOM) += davicom.o +obj-$(CONFIG_PHY_ET1011C) += et1011c.o +obj-$(CONFIG_PHY_ICPLUS) += icplus.o +obj-$(CONFIG_PHY_LXT) += lxt.o +obj-$(CONFIG_PHY_MARVELL) += marvell.o +obj-$(CONFIG_PHY_MICREL) += micrel.o +obj-$(CONFIG_PHY_NATSEMI) += natsemi.o +obj-$(CONFIG_PHY_REALTEK) += realtek.o +obj-$(CONFIG_PHY_SMSC) += smsc.o +obj-$(CONFIG_PHY_TERANETICS) += teranetics.o +obj-$(CONFIG_PHY_VITESSE) += vitesse.o diff --git a/qemu/roms/u-boot/drivers/net/phy/atheros.c b/qemu/roms/u-boot/drivers/net/phy/atheros.c new file mode 100644 index 000000000..d509e30d3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/atheros.c @@ -0,0 +1,76 @@ +/* + * Atheros PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2011, 2013 Freescale Semiconductor, Inc. + * author Andy Fleming + */ +#include + +static int ar8021_config(struct phy_device *phydev) +{ + phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x05); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x3D47); + + phydev->supported = phydev->drv->features; + return 0; +} + +static int ar8035_config(struct phy_device *phydev) +{ + int regval; + + phy_write(phydev, MDIO_DEVAD_NONE, 0xd, 0x0007); + phy_write(phydev, MDIO_DEVAD_NONE, 0xe, 0x8016); + phy_write(phydev, MDIO_DEVAD_NONE, 0xd, 0x4007); + regval = phy_read(phydev, MDIO_DEVAD_NONE, 0xe); + phy_write(phydev, MDIO_DEVAD_NONE, 0xe, (regval|0x0018)); + + phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x05); + regval = phy_read(phydev, MDIO_DEVAD_NONE, 0x1e); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, (regval|0x0100)); + + phydev->supported = phydev->drv->features; + + return 0; +} + +static struct phy_driver AR8021_driver = { + .name = "AR8021", + .uid = 0x4dd040, + .mask = 0x4ffff0, + .features = PHY_GBIT_FEATURES, + .config = ar8021_config, + .startup = genphy_startup, + .shutdown = genphy_shutdown, +}; + +static struct phy_driver AR8031_driver = { + .name = "AR8031/AR8033", + .uid = 0x4dd074, + .mask = 0xffffffef, + .features = PHY_GBIT_FEATURES, + .config = ar8021_config, + .startup = genphy_startup, + .shutdown = genphy_shutdown, +}; + +static struct phy_driver AR8035_driver = { + .name = "AR8035", + .uid = 0x4dd072, + .mask = 0xffffffef, + .features = PHY_GBIT_FEATURES, + .config = ar8035_config, + .startup = genphy_startup, + .shutdown = genphy_shutdown, +}; + +int phy_atheros_init(void) +{ + phy_register(&AR8021_driver); + phy_register(&AR8031_driver); + phy_register(&AR8035_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/broadcom.c b/qemu/roms/u-boot/drivers/net/phy/broadcom.c new file mode 100644 index 000000000..4512763b5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/broadcom.c @@ -0,0 +1,274 @@ +/* + * Broadcom PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * author Andy Fleming + */ +#include +#include +#include + +/* Broadcom BCM54xx -- taken from linux sungem_phy */ +#define MIIM_BCM54xx_AUXCNTL 0x18 +#define MIIM_BCM54xx_AUXCNTL_ENCODE(val) (((val & 0x7) << 12)|(val & 0x7)) +#define MIIM_BCM54xx_AUXSTATUS 0x19 +#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK 0x0700 +#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT 8 + +#define MIIM_BCM54XX_SHD 0x1c +#define MIIM_BCM54XX_SHD_WRITE 0x8000 +#define MIIM_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10) +#define MIIM_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0) +#define MIIM_BCM54XX_SHD_WR_ENCODE(val, data) \ + (MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \ + MIIM_BCM54XX_SHD_DATA(data)) + +#define MIIM_BCM54XX_EXP_DATA 0x15 /* Expansion register data */ +#define MIIM_BCM54XX_EXP_SEL 0x17 /* Expansion register select */ +#define MIIM_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */ +#define MIIM_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */ + +/* Broadcom BCM5461S */ +static int bcm5461_config(struct phy_device *phydev) +{ + genphy_config_aneg(phydev); + + phy_reset(phydev); + + return 0; +} + +static int bcm54xx_parse_status(struct phy_device *phydev) +{ + unsigned int mii_reg; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS); + + switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >> + MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) { + case 1: + phydev->duplex = DUPLEX_HALF; + phydev->speed = SPEED_10; + break; + case 2: + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_10; + break; + case 3: + phydev->duplex = DUPLEX_HALF; + phydev->speed = SPEED_100; + break; + case 5: + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_100; + break; + case 6: + phydev->duplex = DUPLEX_HALF; + phydev->speed = SPEED_1000; + break; + case 7: + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_1000; + break; + default: + printf("Auto-neg error, defaulting to 10BT/HD\n"); + phydev->duplex = DUPLEX_HALF; + phydev->speed = SPEED_10; + break; + } + + return 0; +} + +static int bcm54xx_startup(struct phy_device *phydev) +{ + /* Read the Status (2x to make sure link is right) */ + genphy_update_link(phydev); + bcm54xx_parse_status(phydev); + + return 0; +} + +/* Broadcom BCM5482S */ +/* + * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain + * circumstances. eg a gigabit TSEC connected to a gigabit switch with + * a 4-wire ethernet cable. Both ends advertise gigabit, but can't + * link. "Ethernet@Wirespeed" reduces advertised speed until link + * can be achieved. + */ +static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg) +{ + return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010; +} + +static int bcm5482_config(struct phy_device *phydev) +{ + unsigned int reg; + + /* reset the PHY */ + reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + reg |= BMCR_RESET; + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg); + + /* Setup read from auxilary control shadow register 7 */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, + MIIM_BCM54xx_AUXCNTL_ENCODE(7)); + /* Read Misc Control register and or in Ethernet@Wirespeed */ + reg = bcm5482_read_wirespeed(phydev, MIIM_BCM54xx_AUXCNTL); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg); + + /* Initial config/enable of secondary SerDes interface */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD, + MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf)); + /* Write intial value to secondary SerDes Contol */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, + MIIM_BCM54XX_EXP_SEL_SSD | 0); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA, + BMCR_ANRESTART); + /* Enable copper/fiber auto-detect */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD, + MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201)); + + genphy_config_aneg(phydev); + + return 0; +} + +/* + * Find out if PHY is in copper or serdes mode by looking at Expansion Reg + * 0x42 - "Operating Mode Status Register" + */ +static int bcm5482_is_serdes(struct phy_device *phydev) +{ + u16 val; + int serdes = 0; + + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, + MIIM_BCM54XX_EXP_SEL_ER | 0x42); + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA); + + switch (val & 0x1f) { + case 0x0d: /* RGMII-to-100Base-FX */ + case 0x0e: /* RGMII-to-SGMII */ + case 0x0f: /* RGMII-to-SerDes */ + case 0x12: /* SGMII-to-SerDes */ + case 0x13: /* SGMII-to-100Base-FX */ + case 0x16: /* SerDes-to-Serdes */ + serdes = 1; + break; + case 0x6: /* RGMII-to-Copper */ + case 0x14: /* SGMII-to-Copper */ + case 0x17: /* SerDes-to-Copper */ + break; + default: + printf("ERROR, invalid PHY mode (0x%x\n)", val); + break; + } + + return serdes; +} + +/* + * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating + * Mode Status Register" + */ +static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev) +{ + u16 val; + int i = 0; + + /* Wait 1s for link - Clause 37 autonegotiation happens very fast */ + while (1) { + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, + MIIM_BCM54XX_EXP_SEL_ER | 0x42); + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA); + + if (val & 0x8000) + break; + + if (i++ > 1000) { + phydev->link = 0; + return 1; + } + + udelay(1000); /* 1 ms */ + } + + phydev->link = 1; + switch ((val >> 13) & 0x3) { + case (0x00): + phydev->speed = 10; + break; + case (0x01): + phydev->speed = 100; + break; + case (0x02): + phydev->speed = 1000; + break; + } + + phydev->duplex = (val & 0x1000) == 0x1000; + + return 0; +} + +/* + * Figure out if BCM5482 is in serdes or copper mode and determine link + * configuration accordingly + */ +static int bcm5482_startup(struct phy_device *phydev) +{ + if (bcm5482_is_serdes(phydev)) { + bcm5482_parse_serdes_sr(phydev); + phydev->port = PORT_FIBRE; + } else { + /* Wait for auto-negotiation to complete or fail */ + genphy_update_link(phydev); + /* Parse BCM54xx copper aux status register */ + bcm54xx_parse_status(phydev); + } + + return 0; +} + +static struct phy_driver BCM5461S_driver = { + .name = "Broadcom BCM5461S", + .uid = 0x2060c0, + .mask = 0xfffff0, + .features = PHY_GBIT_FEATURES, + .config = &bcm5461_config, + .startup = &bcm54xx_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver BCM5464S_driver = { + .name = "Broadcom BCM5464S", + .uid = 0x2060b0, + .mask = 0xfffff0, + .features = PHY_GBIT_FEATURES, + .config = &bcm5461_config, + .startup = &bcm54xx_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver BCM5482S_driver = { + .name = "Broadcom BCM5482S", + .uid = 0x143bcb0, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &bcm5482_config, + .startup = &bcm5482_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_broadcom_init(void) +{ + phy_register(&BCM5482S_driver); + phy_register(&BCM5464S_driver); + phy_register(&BCM5461S_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/davicom.c b/qemu/roms/u-boot/drivers/net/phy/davicom.c new file mode 100644 index 000000000..0c039fe79 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/davicom.c @@ -0,0 +1,84 @@ +/* + * Davicom PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * author Andy Fleming + */ +#include + +#define MIIM_DM9161_SCR 0x10 +#define MIIM_DM9161_SCR_INIT 0x0610 + +/* DM9161 Specified Configuration and Status Register */ +#define MIIM_DM9161_SCSR 0x11 +#define MIIM_DM9161_SCSR_100F 0x8000 +#define MIIM_DM9161_SCSR_100H 0x4000 +#define MIIM_DM9161_SCSR_10F 0x2000 +#define MIIM_DM9161_SCSR_10H 0x1000 + +/* DM9161 10BT Configuration/Status */ +#define MIIM_DM9161_10BTCSR 0x12 +#define MIIM_DM9161_10BTCSR_INIT 0x7800 + + +/* Davicom DM9161E */ +static int dm9161_config(struct phy_device *phydev) +{ + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_ISOLATE); + /* Do not bypass the scrambler/descrambler */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_DM9161_SCR, + MIIM_DM9161_SCR_INIT); + /* Clear 10BTCSR to default */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_DM9161_10BTCSR, + MIIM_DM9161_10BTCSR_INIT); + + genphy_config_aneg(phydev); + + return 0; +} + +static int dm9161_parse_status(struct phy_device *phydev) +{ + int mii_reg; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_DM9161_SCSR); + + if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H)) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + + if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F)) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + return 0; +} + +static int dm9161_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + dm9161_parse_status(phydev); + + return 0; +} + +static struct phy_driver DM9161_driver = { + .name = "Davicom DM9161E", + .uid = 0x181b880, + .mask = 0xffffff0, + .features = PHY_BASIC_FEATURES, + .config = &dm9161_config, + .startup = &dm9161_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_davicom_init(void) +{ + phy_register(&DM9161_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/et1011c.c b/qemu/roms/u-boot/drivers/net/phy/et1011c.c new file mode 100644 index 000000000..70c15e2f2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/et1011c.c @@ -0,0 +1,101 @@ +/* + * ET1011C PHY driver + * + * Derived from Linux kernel driver by Chaithrika U S + * Copyright (C) 2013, Texas Instruments, Incorporated - http://www.ti.com/ + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include + +#define ET1011C_CONFIG_REG (0x16) +#define ET1011C_TX_FIFO_MASK (0x3 << 12) +#define ET1011C_TX_FIFO_DEPTH_8 (0x0 << 12) +#define ET1011C_TX_FIFO_DEPTH_16 (0x1 << 12) +#define ET1011C_INTERFACE_MASK (0x7 << 0) +#define ET1011C_GMII_INTERFACE (0x2 << 0) +#define ET1011C_SYS_CLK_EN (0x1 << 4) +#define ET1011C_TX_CLK_EN (0x1 << 5) + +#define ET1011C_STATUS_REG (0x1A) +#define ET1011C_DUPLEX_STATUS (0x1 << 7) +#define ET1011C_SPEED_MASK (0x3 << 8) +#define ET1011C_SPEED_1000 (0x2 << 8) +#define ET1011C_SPEED_100 (0x1 << 8) +#define ET1011C_SPEED_10 (0x0 << 8) + +static int et1011c_config(struct phy_device *phydev) +{ + int ctl = 0; + ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + if (ctl < 0) + return ctl; + ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | + BMCR_ANENABLE); + /* First clear the PHY */ + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl | BMCR_RESET); + + return genphy_config_aneg(phydev); +} + +static int et1011c_parse_status(struct phy_device *phydev) +{ + int mii_reg; + int speed; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, ET1011C_STATUS_REG); + + if (mii_reg & ET1011C_DUPLEX_STATUS) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + speed = mii_reg & ET1011C_SPEED_MASK; + switch (speed) { + case ET1011C_SPEED_1000: + phydev->speed = SPEED_1000; + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, ET1011C_CONFIG_REG); + mii_reg &= ~ET1011C_TX_FIFO_MASK; + phy_write(phydev, MDIO_DEVAD_NONE, ET1011C_CONFIG_REG, + mii_reg | + ET1011C_GMII_INTERFACE | + ET1011C_SYS_CLK_EN | +#ifdef CONFIG_PHY_ET1011C_TX_CLK_FIX + ET1011C_TX_CLK_EN | +#endif + ET1011C_TX_FIFO_DEPTH_16); + break; + case ET1011C_SPEED_100: + phydev->speed = SPEED_100; + break; + case ET1011C_SPEED_10: + phydev->speed = SPEED_10; + break; + } + + return 0; +} + +static int et1011c_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + et1011c_parse_status(phydev); + return 0; +} + +static struct phy_driver et1011c_driver = { + .name = "ET1011C", + .uid = 0x0282f014, + .mask = 0xfffffff0, + .features = PHY_GBIT_FEATURES, + .config = &et1011c_config, + .startup = &et1011c_startup, +}; + +int phy_et1011c_init(void) +{ + phy_register(&et1011c_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/generic_10g.c b/qemu/roms/u-boot/drivers/net/phy/generic_10g.c new file mode 100644 index 000000000..ed3dcd91d --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/generic_10g.c @@ -0,0 +1,94 @@ +/* + * Generic PHY Management code + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2011 Freescale Semiconductor, Inc. + * author Andy Fleming + * + * Based loosely off of Linux's PHY Lib + */ + +#include +#include +#include +#include + +int gen10g_shutdown(struct phy_device *phydev) +{ + return 0; +} + +int gen10g_startup(struct phy_device *phydev) +{ + int devad, reg; + u32 mmd_mask = phydev->mmds & MDIO_DEVS_LINK; + + phydev->link = 1; + + /* For now just lie and say it's 10G all the time */ + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + + /* + * Go through all the link-reporting devices, and make sure + * they're all up and happy + */ + for (devad = 0; mmd_mask; devad++, mmd_mask = mmd_mask >> 1) { + if (!(mmd_mask & 1)) + continue; + + /* Read twice because link state is latched and a + * read moves the current state into the register */ + phy_read(phydev, devad, MDIO_STAT1); + reg = phy_read(phydev, devad, MDIO_STAT1); + if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS)) + phydev->link = 0; + } + + return 0; +} + +int gen10g_discover_mmds(struct phy_device *phydev) +{ + int mmd, stat2, devs1, devs2; + + /* Assume PHY must have at least one of PMA/PMD, WIS, PCS, PHY + * XS or DTE XS; give up if none is present. */ + for (mmd = 1; mmd <= 5; mmd++) { + /* Is this MMD present? */ + stat2 = phy_read(phydev, mmd, MDIO_STAT2); + if (stat2 < 0 || + (stat2 & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL) + continue; + + /* It should tell us about all the other MMDs */ + devs1 = phy_read(phydev, mmd, MDIO_DEVS1); + devs2 = phy_read(phydev, mmd, MDIO_DEVS2); + if (devs1 < 0 || devs2 < 0) + continue; + + phydev->mmds = devs1 | (devs2 << 16); + return 0; + } + + return 0; +} + +int gen10g_config(struct phy_device *phydev) +{ + /* For now, assume 10000baseT. Fill in later */ + phydev->supported = phydev->advertising = SUPPORTED_10000baseT_Full; + + return gen10g_discover_mmds(phydev); +} + +struct phy_driver gen10g_driver = { + .uid = 0xffffffff, + .mask = 0xffffffff, + .name = "Generic 10G PHY", + .features = 0, + .config = gen10g_config, + .startup = gen10g_startup, + .shutdown = gen10g_shutdown, +}; diff --git a/qemu/roms/u-boot/drivers/net/phy/icplus.c b/qemu/roms/u-boot/drivers/net/phy/icplus.c new file mode 100644 index 000000000..597195580 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/icplus.c @@ -0,0 +1,80 @@ +/* + * ICPlus PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + */ +#include + +/* IP101A/G - IP1001 */ +#define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */ +#define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */ +#define IP1001_PHASE_SEL_MASK 3 /* IP1001 RX/TXPHASE_SEL */ +#define IP1001_APS_ON 11 /* IP1001 APS Mode bit */ +#define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */ +#define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */ +#define IP101A_G_IRQ_PIN_USED (1<<15) /* INTR pin used */ +#define IP101A_G_IRQ_DEFAULT IP101A_G_IRQ_PIN_USED + +static int ip1001_config(struct phy_device *phydev) +{ + int c; + + /* Enable Auto Power Saving mode */ + c = phy_read(phydev, MDIO_DEVAD_NONE, IP1001_SPEC_CTRL_STATUS_2); + if (c < 0) + return c; + c |= IP1001_APS_ON; + c = phy_write(phydev, MDIO_DEVAD_NONE, IP1001_SPEC_CTRL_STATUS_2, c); + if (c < 0) + return c; + + /* INTR pin used: speed/link/duplex will cause an interrupt */ + c = phy_write(phydev, MDIO_DEVAD_NONE, IP101A_G_IRQ_CONF_STATUS, + IP101A_G_IRQ_DEFAULT); + if (c < 0) + return c; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { + /* + * Additional delay (2ns) used to adjust RX clock phase + * at RGMII interface + */ + c = phy_read(phydev, MDIO_DEVAD_NONE, IP10XX_SPEC_CTRL_STATUS); + if (c < 0) + return c; + + c |= IP1001_PHASE_SEL_MASK; + c = phy_write(phydev, MDIO_DEVAD_NONE, IP10XX_SPEC_CTRL_STATUS, + c); + if (c < 0) + return c; + } + + return 0; +} + +static int ip1001_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + genphy_parse_link(phydev); + + return 0; +} +static struct phy_driver IP1001_driver = { + .name = "ICPlus IP1001", + .uid = 0x02430d90, + .mask = 0x0ffffff0, + .features = PHY_GBIT_FEATURES, + .config = &ip1001_config, + .startup = &ip1001_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_icplus_init(void) +{ + phy_register(&IP1001_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/lxt.c b/qemu/roms/u-boot/drivers/net/phy/lxt.c new file mode 100644 index 000000000..91838ce5e --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/lxt.c @@ -0,0 +1,73 @@ +/* + * LXT PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * author Andy Fleming + */ +#include + +/* LXT971 Status 2 registers */ +#define MIIM_LXT971_SR2 0x11 /* Status Register 2 */ +#define MIIM_LXT971_SR2_SPEED_MASK 0x4200 +#define MIIM_LXT971_SR2_10HDX 0x0000 /* 10 Mbit half duplex selected */ +#define MIIM_LXT971_SR2_10FDX 0x0200 /* 10 Mbit full duplex selected */ +#define MIIM_LXT971_SR2_100HDX 0x4000 /* 100 Mbit half duplex selected */ +#define MIIM_LXT971_SR2_100FDX 0x4200 /* 100 Mbit full duplex selected */ + + +/* LXT971 */ +static int lxt971_parse_status(struct phy_device *phydev) +{ + int mii_reg; + int speed; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_LXT971_SR2); + speed = mii_reg & MIIM_LXT971_SR2_SPEED_MASK; + + switch (speed) { + case MIIM_LXT971_SR2_10HDX: + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + break; + case MIIM_LXT971_SR2_10FDX: + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_FULL; + break; + case MIIM_LXT971_SR2_100HDX: + phydev->speed = SPEED_100; + phydev->duplex = DUPLEX_HALF; + break; + default: + phydev->speed = SPEED_100; + phydev->duplex = DUPLEX_FULL; + } + + return 0; +} + +static int lxt971_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + lxt971_parse_status(phydev); + + return 0; +} + +static struct phy_driver LXT971_driver = { + .name = "LXT971", + .uid = 0x1378e0, + .mask = 0xfffff0, + .features = PHY_BASIC_FEATURES, + .config = &genphy_config_aneg, + .startup = &lxt971_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_lxt_init(void) +{ + phy_register(&LXT971_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/marvell.c b/qemu/roms/u-boot/drivers/net/phy/marvell.c new file mode 100644 index 000000000..d2ecadc89 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/marvell.c @@ -0,0 +1,524 @@ +/* + * Marvell PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * author Andy Fleming + */ +#include +#include +#include + +#define PHY_AUTONEGOTIATE_TIMEOUT 5000 + +/* 88E1011 PHY Status Register */ +#define MIIM_88E1xxx_PHY_STATUS 0x11 +#define MIIM_88E1xxx_PHYSTAT_SPEED 0xc000 +#define MIIM_88E1xxx_PHYSTAT_GBIT 0x8000 +#define MIIM_88E1xxx_PHYSTAT_100 0x4000 +#define MIIM_88E1xxx_PHYSTAT_DUPLEX 0x2000 +#define MIIM_88E1xxx_PHYSTAT_SPDDONE 0x0800 +#define MIIM_88E1xxx_PHYSTAT_LINK 0x0400 + +#define MIIM_88E1xxx_PHY_SCR 0x10 +#define MIIM_88E1xxx_PHY_MDI_X_AUTO 0x0060 + +/* 88E1111 PHY LED Control Register */ +#define MIIM_88E1111_PHY_LED_CONTROL 24 +#define MIIM_88E1111_PHY_LED_DIRECT 0x4100 +#define MIIM_88E1111_PHY_LED_COMBINE 0x411C + +/* 88E1111 Extended PHY Specific Control Register */ +#define MIIM_88E1111_PHY_EXT_CR 0x14 +#define MIIM_88E1111_RX_DELAY 0x80 +#define MIIM_88E1111_TX_DELAY 0x2 + +/* 88E1111 Extended PHY Specific Status Register */ +#define MIIM_88E1111_PHY_EXT_SR 0x1b +#define MIIM_88E1111_HWCFG_MODE_MASK 0xf +#define MIIM_88E1111_HWCFG_MODE_COPPER_RGMII 0xb +#define MIIM_88E1111_HWCFG_MODE_FIBER_RGMII 0x3 +#define MIIM_88E1111_HWCFG_MODE_SGMII_NO_CLK 0x4 +#define MIIM_88E1111_HWCFG_MODE_COPPER_RTBI 0x9 +#define MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO 0x8000 +#define MIIM_88E1111_HWCFG_FIBER_COPPER_RES 0x2000 + +#define MIIM_88E1111_COPPER 0 +#define MIIM_88E1111_FIBER 1 + +/* 88E1118 PHY defines */ +#define MIIM_88E1118_PHY_PAGE 22 +#define MIIM_88E1118_PHY_LED_PAGE 3 + +/* 88E1121 PHY LED Control Register */ +#define MIIM_88E1121_PHY_LED_CTRL 16 +#define MIIM_88E1121_PHY_LED_PAGE 3 +#define MIIM_88E1121_PHY_LED_DEF 0x0030 + +/* 88E1121 PHY IRQ Enable/Status Register */ +#define MIIM_88E1121_PHY_IRQ_EN 18 +#define MIIM_88E1121_PHY_IRQ_STATUS 19 + +#define MIIM_88E1121_PHY_PAGE 22 + +/* 88E1145 Extended PHY Specific Control Register */ +#define MIIM_88E1145_PHY_EXT_CR 20 +#define MIIM_M88E1145_RGMII_RX_DELAY 0x0080 +#define MIIM_M88E1145_RGMII_TX_DELAY 0x0002 + +#define MIIM_88E1145_PHY_LED_CONTROL 24 +#define MIIM_88E1145_PHY_LED_DIRECT 0x4100 + +#define MIIM_88E1145_PHY_PAGE 29 +#define MIIM_88E1145_PHY_CAL_OV 30 + +#define MIIM_88E1149_PHY_PAGE 29 + +/* 88E1310 PHY defines */ +#define MIIM_88E1310_PHY_LED_CTRL 16 +#define MIIM_88E1310_PHY_IRQ_EN 18 +#define MIIM_88E1310_PHY_RGMII_CTRL 21 +#define MIIM_88E1310_PHY_PAGE 22 + +/* Marvell 88E1011S */ +static int m88e1011s_config(struct phy_device *phydev) +{ + /* Reset and configure the PHY */ + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + + phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x1f); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x200c); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x5); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100); + + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + + genphy_config_aneg(phydev); + + return 0; +} + +/* Parse the 88E1011's status register for speed and duplex + * information + */ +static uint m88e1xxx_parse_status(struct phy_device *phydev) +{ + unsigned int speed; + unsigned int mii_reg; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1xxx_PHY_STATUS); + + if ((mii_reg & MIIM_88E1xxx_PHYSTAT_LINK) && + !(mii_reg & MIIM_88E1xxx_PHYSTAT_SPDDONE)) { + int i = 0; + + puts("Waiting for PHY realtime link"); + while (!(mii_reg & MIIM_88E1xxx_PHYSTAT_SPDDONE)) { + /* Timeout reached ? */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts(" TIMEOUT !\n"); + phydev->link = 0; + break; + } + + if ((i++ % 1000) == 0) + putc('.'); + udelay(1000); + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, + MIIM_88E1xxx_PHY_STATUS); + } + puts(" done\n"); + udelay(500000); /* another 500 ms (results in faster booting) */ + } else { + if (mii_reg & MIIM_88E1xxx_PHYSTAT_LINK) + phydev->link = 1; + else + phydev->link = 0; + } + + if (mii_reg & MIIM_88E1xxx_PHYSTAT_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + speed = mii_reg & MIIM_88E1xxx_PHYSTAT_SPEED; + + switch (speed) { + case MIIM_88E1xxx_PHYSTAT_GBIT: + phydev->speed = SPEED_1000; + break; + case MIIM_88E1xxx_PHYSTAT_100: + phydev->speed = SPEED_100; + break; + default: + phydev->speed = SPEED_10; + break; + } + + return 0; +} + +static int m88e1011s_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + m88e1xxx_parse_status(phydev); + + return 0; +} + +/* Marvell 88E1111S */ +static int m88e1111s_config(struct phy_device *phydev) +{ + int reg; + int timeout; + + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { + reg = phy_read(phydev, + MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR); + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)) { + reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY); + } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { + reg &= ~MIIM_88E1111_TX_DELAY; + reg |= MIIM_88E1111_RX_DELAY; + } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { + reg &= ~MIIM_88E1111_RX_DELAY; + reg |= MIIM_88E1111_TX_DELAY; + } + + phy_write(phydev, + MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR, reg); + + reg = phy_read(phydev, + MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_SR); + + reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK); + + if (reg & MIIM_88E1111_HWCFG_FIBER_COPPER_RES) + reg |= MIIM_88E1111_HWCFG_MODE_FIBER_RGMII; + else + reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RGMII; + + phy_write(phydev, + MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_SR, reg); + } + + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + reg = phy_read(phydev, + MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_SR); + + reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK); + reg |= MIIM_88E1111_HWCFG_MODE_SGMII_NO_CLK; + reg |= MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO; + + phy_write(phydev, MDIO_DEVAD_NONE, + MIIM_88E1111_PHY_EXT_SR, reg); + } + + if (phydev->interface == PHY_INTERFACE_MODE_RTBI) { + reg = phy_read(phydev, + MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR); + reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY); + phy_write(phydev, + MDIO_DEVAD_NONE, MIIM_88E1111_PHY_EXT_CR, reg); + + reg = phy_read(phydev, MDIO_DEVAD_NONE, + MIIM_88E1111_PHY_EXT_SR); + reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK | + MIIM_88E1111_HWCFG_FIBER_COPPER_RES); + reg |= 0x7 | MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO; + phy_write(phydev, MDIO_DEVAD_NONE, + MIIM_88E1111_PHY_EXT_SR, reg); + + /* soft reset */ + timeout = 1000; + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + udelay(1000); + reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + while ((reg & BMCR_RESET) && --timeout) { + udelay(1000); + reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + } + if (!timeout) + printf("%s: phy soft reset timeout\n", __func__); + + reg = phy_read(phydev, MDIO_DEVAD_NONE, + MIIM_88E1111_PHY_EXT_SR); + reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK | + MIIM_88E1111_HWCFG_FIBER_COPPER_RES); + reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RTBI | + MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO; + phy_write(phydev, MDIO_DEVAD_NONE, + MIIM_88E1111_PHY_EXT_SR, reg); + } + + /* soft reset */ + timeout = 1000; + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + udelay(1000); + reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + while ((reg & BMCR_RESET) && --timeout) { + udelay(1000); + reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + } + if (!timeout) + printf("%s: phy soft reset timeout\n", __func__); + + genphy_config_aneg(phydev); + + phy_reset(phydev); + + return 0; +} + +/* Marvell 88E1118 */ +static int m88e1118_config(struct phy_device *phydev) +{ + /* Change Page Number */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0002); + /* Delay RGMII TX and RX */ + phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x1070); + /* Change Page Number */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0003); + /* Adjust LED control */ + phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x021e); + /* Change Page Number */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000); + + genphy_config_aneg(phydev); + + phy_reset(phydev); + + return 0; +} + +static int m88e1118_startup(struct phy_device *phydev) +{ + /* Change Page Number */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000); + + genphy_update_link(phydev); + m88e1xxx_parse_status(phydev); + + return 0; +} + +/* Marvell 88E1121R */ +static int m88e1121_config(struct phy_device *phydev) +{ + int pg; + + /* Configure the PHY */ + genphy_config_aneg(phydev); + + /* Switch the page to access the led register */ + pg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_PAGE); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_PAGE, + MIIM_88E1121_PHY_LED_PAGE); + /* Configure leds */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_LED_CTRL, + MIIM_88E1121_PHY_LED_DEF); + /* Restore the page pointer */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_PAGE, pg); + + /* Disable IRQs and de-assert interrupt */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_IRQ_EN, 0); + phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_IRQ_STATUS); + + return 0; +} + +/* Marvell 88E1145 */ +static int m88e1145_config(struct phy_device *phydev) +{ + int reg; + + /* Errata E0, E1 */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_PAGE, 0x001b); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_CAL_OV, 0x418f); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_PAGE, 0x0016); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_CAL_OV, 0xa2da); + + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1xxx_PHY_SCR, + MIIM_88E1xxx_PHY_MDI_X_AUTO); + + reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_EXT_CR); + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + reg |= MIIM_M88E1145_RGMII_RX_DELAY | + MIIM_M88E1145_RGMII_TX_DELAY; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_EXT_CR, reg); + + genphy_config_aneg(phydev); + + phy_reset(phydev); + + return 0; +} + +static int m88e1145_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_LED_CONTROL, + MIIM_88E1145_PHY_LED_DIRECT); + m88e1xxx_parse_status(phydev); + + return 0; +} + +/* Marvell 88E1149S */ +static int m88e1149_config(struct phy_device *phydev) +{ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1149_PHY_PAGE, 0x1f); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x200c); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1149_PHY_PAGE, 0x5); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x0); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100); + + genphy_config_aneg(phydev); + + phy_reset(phydev); + + return 0; +} + +/* Marvell 88E1310 */ +static int m88e1310_config(struct phy_device *phydev) +{ + u16 reg; + + /* LED link and activity */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0003); + reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_LED_CTRL); + reg = (reg & ~0xf) | 0x1; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_LED_CTRL, reg); + + /* Set LED2/INT to INT mode, low active */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0003); + reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_IRQ_EN); + reg = (reg & 0x77ff) | 0x0880; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_IRQ_EN, reg); + + /* Set RGMII delay */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0002); + reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_RGMII_CTRL); + reg |= 0x0030; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_RGMII_CTRL, reg); + + /* Ensure to return to page 0 */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0000); + + genphy_config_aneg(phydev); + phy_reset(phydev); + + return 0; +} + +static struct phy_driver M88E1011S_driver = { + .name = "Marvell 88E1011S", + .uid = 0x1410c60, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1011s_config, + .startup = &m88e1011s_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver M88E1111S_driver = { + .name = "Marvell 88E1111S", + .uid = 0x1410cc0, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1111s_config, + .startup = &m88e1011s_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver M88E1118_driver = { + .name = "Marvell 88E1118", + .uid = 0x1410e10, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1118_config, + .startup = &m88e1118_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver M88E1118R_driver = { + .name = "Marvell 88E1118R", + .uid = 0x1410e40, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1118_config, + .startup = &m88e1118_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver M88E1121R_driver = { + .name = "Marvell 88E1121R", + .uid = 0x1410cb0, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1121_config, + .startup = &genphy_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver M88E1145_driver = { + .name = "Marvell 88E1145", + .uid = 0x1410cd0, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1145_config, + .startup = &m88e1145_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver M88E1149S_driver = { + .name = "Marvell 88E1149S", + .uid = 0x1410ca0, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1149_config, + .startup = &m88e1011s_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver M88E1518_driver = { + .name = "Marvell 88E1518", + .uid = 0x1410dd1, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1111s_config, + .startup = &m88e1011s_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver M88E1310_driver = { + .name = "Marvell 88E1310", + .uid = 0x01410e90, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .config = &m88e1310_config, + .startup = &m88e1011s_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_marvell_init(void) +{ + phy_register(&M88E1310_driver); + phy_register(&M88E1149S_driver); + phy_register(&M88E1145_driver); + phy_register(&M88E1121R_driver); + phy_register(&M88E1118_driver); + phy_register(&M88E1118R_driver); + phy_register(&M88E1111S_driver); + phy_register(&M88E1011S_driver); + phy_register(&M88E1518_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/micrel.c b/qemu/roms/u-boot/drivers/net/phy/micrel.c new file mode 100644 index 000000000..5d7e3be52 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/micrel.c @@ -0,0 +1,226 @@ +/* + * Micrel PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * author Andy Fleming + * (C) 2012 NetModule AG, David Andrey, added KSZ9031 + */ +#include +#include +#include +#include + +static struct phy_driver KSZ804_driver = { + .name = "Micrel KSZ804", + .uid = 0x221510, + .mask = 0xfffff0, + .features = PHY_BASIC_FEATURES, + .config = &genphy_config, + .startup = &genphy_startup, + .shutdown = &genphy_shutdown, +}; + +#ifndef CONFIG_PHY_MICREL_KSZ9021 +/* + * I can't believe Micrel used the exact same part number + * for the KSZ9021 + * Shame Micrel, Shame!!!!! + */ +static struct phy_driver KS8721_driver = { + .name = "Micrel KS8721BL", + .uid = 0x221610, + .mask = 0xfffff0, + .features = PHY_BASIC_FEATURES, + .config = &genphy_config, + .startup = &genphy_startup, + .shutdown = &genphy_shutdown, +}; +#endif + + +/** + * KSZ9021 - KSZ9031 common + */ + +#define MII_KSZ90xx_PHY_CTL 0x1f +#define MIIM_KSZ90xx_PHYCTL_1000 (1 << 6) +#define MIIM_KSZ90xx_PHYCTL_100 (1 << 5) +#define MIIM_KSZ90xx_PHYCTL_10 (1 << 4) +#define MIIM_KSZ90xx_PHYCTL_DUPLEX (1 << 3) + +static int ksz90xx_startup(struct phy_device *phydev) +{ + unsigned phy_ctl; + genphy_update_link(phydev); + phy_ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ90xx_PHY_CTL); + + if (phy_ctl & MIIM_KSZ90xx_PHYCTL_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + if (phy_ctl & MIIM_KSZ90xx_PHYCTL_1000) + phydev->speed = SPEED_1000; + else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_100) + phydev->speed = SPEED_100; + else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_10) + phydev->speed = SPEED_10; + return 0; +} +#ifdef CONFIG_PHY_MICREL_KSZ9021 + +/* + * KSZ9021 + */ + +/* PHY Registers */ +#define MII_KSZ9021_EXTENDED_CTRL 0x0b +#define MII_KSZ9021_EXTENDED_DATAW 0x0c +#define MII_KSZ9021_EXTENDED_DATAR 0x0d + +#define CTRL1000_PREFER_MASTER (1 << 10) +#define CTRL1000_CONFIG_MASTER (1 << 11) +#define CTRL1000_MANUAL_CONFIG (1 << 12) + +int ksz9021_phy_extended_write(struct phy_device *phydev, int regnum, u16 val) +{ + /* extended registers */ + phy_write(phydev, MDIO_DEVAD_NONE, + MII_KSZ9021_EXTENDED_CTRL, regnum | 0x8000); + return phy_write(phydev, MDIO_DEVAD_NONE, + MII_KSZ9021_EXTENDED_DATAW, val); +} + +int ksz9021_phy_extended_read(struct phy_device *phydev, int regnum) +{ + /* extended registers */ + phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_EXTENDED_CTRL, regnum); + return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_EXTENDED_DATAR); +} + + +static int ksz9021_phy_extread(struct phy_device *phydev, int addr, int devaddr, + int regnum) +{ + return ksz9021_phy_extended_read(phydev, regnum); +} + +static int ksz9021_phy_extwrite(struct phy_device *phydev, int addr, + int devaddr, int regnum, u16 val) +{ + return ksz9021_phy_extended_write(phydev, regnum, val); +} + +/* Micrel ksz9021 */ +static int ksz9021_config(struct phy_device *phydev) +{ + unsigned ctrl1000 = 0; + const unsigned master = CTRL1000_PREFER_MASTER | + CTRL1000_CONFIG_MASTER | CTRL1000_MANUAL_CONFIG; + unsigned features = phydev->drv->features; + + if (getenv("disable_giga")) + features &= ~(SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full); + /* force master mode for 1000BaseT due to chip errata */ + if (features & SUPPORTED_1000baseT_Half) + ctrl1000 |= ADVERTISE_1000HALF | master; + if (features & SUPPORTED_1000baseT_Full) + ctrl1000 |= ADVERTISE_1000FULL | master; + phydev->advertising = phydev->supported = features; + phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, ctrl1000); + genphy_config_aneg(phydev); + genphy_restart_aneg(phydev); + return 0; +} + +static struct phy_driver ksz9021_driver = { + .name = "Micrel ksz9021", + .uid = 0x221610, + .mask = 0xfffff0, + .features = PHY_GBIT_FEATURES, + .config = &ksz9021_config, + .startup = &ksz90xx_startup, + .shutdown = &genphy_shutdown, + .writeext = &ksz9021_phy_extwrite, + .readext = &ksz9021_phy_extread, +}; +#endif + +/** + * KSZ9031 + */ +/* PHY Registers */ +#define MII_KSZ9031_MMD_ACCES_CTRL 0x0d +#define MII_KSZ9031_MMD_REG_DATA 0x0e + +/* Accessors to extended registers*/ +int ksz9031_phy_extended_write(struct phy_device *phydev, + int devaddr, int regnum, u16 mode, u16 val) +{ + /*select register addr for mmd*/ + phy_write(phydev, MDIO_DEVAD_NONE, + MII_KSZ9031_MMD_ACCES_CTRL, devaddr); + /*select register for mmd*/ + phy_write(phydev, MDIO_DEVAD_NONE, + MII_KSZ9031_MMD_REG_DATA, regnum); + /*setup mode*/ + phy_write(phydev, MDIO_DEVAD_NONE, + MII_KSZ9031_MMD_ACCES_CTRL, (mode | devaddr)); + /*write the value*/ + return phy_write(phydev, MDIO_DEVAD_NONE, + MII_KSZ9031_MMD_REG_DATA, val); +} + +int ksz9031_phy_extended_read(struct phy_device *phydev, int devaddr, + int regnum, u16 mode) +{ + phy_write(phydev, MDIO_DEVAD_NONE, + MII_KSZ9031_MMD_ACCES_CTRL, devaddr); + phy_write(phydev, MDIO_DEVAD_NONE, + MII_KSZ9031_MMD_REG_DATA, regnum); + phy_write(phydev, MDIO_DEVAD_NONE, + MII_KSZ9031_MMD_ACCES_CTRL, (devaddr | mode)); + return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9031_MMD_REG_DATA); +} + +static int ksz9031_phy_extread(struct phy_device *phydev, int addr, int devaddr, + int regnum) +{ + return ksz9031_phy_extended_read(phydev, devaddr, regnum, + MII_KSZ9031_MOD_DATA_NO_POST_INC); +}; + +static int ksz9031_phy_extwrite(struct phy_device *phydev, int addr, + int devaddr, int regnum, u16 val) +{ + return ksz9031_phy_extended_write(phydev, devaddr, regnum, + MII_KSZ9031_MOD_DATA_POST_INC_RW, val); +}; + + +static struct phy_driver ksz9031_driver = { + .name = "Micrel ksz9031", + .uid = 0x221620, + .mask = 0xfffff0, + .features = PHY_GBIT_FEATURES, + .config = &genphy_config, + .startup = &ksz90xx_startup, + .shutdown = &genphy_shutdown, + .writeext = &ksz9031_phy_extwrite, + .readext = &ksz9031_phy_extread, +}; + +int phy_micrel_init(void) +{ + phy_register(&KSZ804_driver); +#ifdef CONFIG_PHY_MICREL_KSZ9021 + phy_register(&ksz9021_driver); +#else + phy_register(&KS8721_driver); +#endif + phy_register(&ksz9031_driver); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/miiphybb.c b/qemu/roms/u-boot/drivers/net/phy/miiphybb.c new file mode 100644 index 000000000..5cda0b846 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/miiphybb.c @@ -0,0 +1,364 @@ +/* + * (C) Copyright 2009 Industrie Dial Face S.p.A. + * Luigi 'Comio' Mantellini + * + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This provides a bit-banged interface to the ethernet MII management + * channel. + */ + +#include +#include +#include +#include + +#define BB_MII_RELOCATE(v,off) (v += (v?off:0)) + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_BITBANGMII_MULTI + +/* + * If CONFIG_BITBANGMII_MULTI is not defined we use a + * compatibility layer with the previous miiphybb implementation + * based on macros usage. + * + */ +static int bb_mii_init_wrap(struct bb_miiphy_bus *bus) +{ +#ifdef MII_INIT + MII_INIT; +#endif + return 0; +} + +static int bb_mdio_active_wrap(struct bb_miiphy_bus *bus) +{ +#ifdef MDIO_DECLARE + MDIO_DECLARE; +#endif + MDIO_ACTIVE; + return 0; +} + +static int bb_mdio_tristate_wrap(struct bb_miiphy_bus *bus) +{ +#ifdef MDIO_DECLARE + MDIO_DECLARE; +#endif + MDIO_TRISTATE; + return 0; +} + +static int bb_set_mdio_wrap(struct bb_miiphy_bus *bus, int v) +{ +#ifdef MDIO_DECLARE + MDIO_DECLARE; +#endif + MDIO(v); + return 0; +} + +static int bb_get_mdio_wrap(struct bb_miiphy_bus *bus, int *v) +{ +#ifdef MDIO_DECLARE + MDIO_DECLARE; +#endif + *v = MDIO_READ; + return 0; +} + +static int bb_set_mdc_wrap(struct bb_miiphy_bus *bus, int v) +{ +#ifdef MDC_DECLARE + MDC_DECLARE; +#endif + MDC(v); + return 0; +} + +static int bb_delay_wrap(struct bb_miiphy_bus *bus) +{ + MIIDELAY; + return 0; +} + +struct bb_miiphy_bus bb_miiphy_buses[] = { + { + .name = BB_MII_DEVNAME, + .init = bb_mii_init_wrap, + .mdio_active = bb_mdio_active_wrap, + .mdio_tristate = bb_mdio_tristate_wrap, + .set_mdio = bb_set_mdio_wrap, + .get_mdio = bb_get_mdio_wrap, + .set_mdc = bb_set_mdc_wrap, + .delay = bb_delay_wrap, + } +}; + +int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) / + sizeof(bb_miiphy_buses[0]); +#endif + +void bb_miiphy_init(void) +{ + int i; + + for (i = 0; i < bb_miiphy_buses_num; i++) { +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + /* Relocate the hook pointers*/ + BB_MII_RELOCATE(bb_miiphy_buses[i].init, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_active, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_tristate, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdio, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].get_mdio, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdc, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].delay, gd->reloc_off); +#endif + if (bb_miiphy_buses[i].init != NULL) { + bb_miiphy_buses[i].init(&bb_miiphy_buses[i]); + } + } +} + +static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname) +{ +#ifdef CONFIG_BITBANGMII_MULTI + int i; + + /* Search the correct bus */ + for (i = 0; i < bb_miiphy_buses_num; i++) { + if (!strcmp(bb_miiphy_buses[i].name, devname)) { + return &bb_miiphy_buses[i]; + } + } + return NULL; +#else + /* We have just one bitbanging bus */ + return &bb_miiphy_buses[0]; +#endif +} + +/***************************************************************************** + * + * Utility to send the preamble, address, and register (common to read + * and write). + */ +static void miiphy_pre(struct bb_miiphy_bus *bus, char read, + unsigned char addr, unsigned char reg) +{ + int j; + + /* + * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure. + * The IEEE spec says this is a PHY optional requirement. The AMD + * 79C874 requires one after power up and one after a MII communications + * error. This means that we are doing more preambles than we need, + * but it is safer and will be much more robust. + */ + + bus->mdio_active(bus); + bus->set_mdio(bus, 1); + for (j = 0; j < 32; j++) { + bus->set_mdc(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + } + + /* send the start bit (01) and the read opcode (10) or write (10) */ + bus->set_mdc(bus, 0); + bus->set_mdio(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->set_mdio(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->set_mdio(bus, read); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->set_mdio(bus, !read); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + /* send the PHY address */ + for (j = 0; j < 5; j++) { + bus->set_mdc(bus, 0); + if ((addr & 0x10) == 0) { + bus->set_mdio(bus, 0); + } else { + bus->set_mdio(bus, 1); + } + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + addr <<= 1; + } + + /* send the register address */ + for (j = 0; j < 5; j++) { + bus->set_mdc(bus, 0); + if ((reg & 0x10) == 0) { + bus->set_mdio(bus, 0); + } else { + bus->set_mdio(bus, 1); + } + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + reg <<= 1; + } +} + +/***************************************************************************** + * + * Read a MII PHY register. + * + * Returns: + * 0 on success + */ +int bb_miiphy_read(const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + short rdreg; /* register working value */ + int v; + int j; /* counter */ + struct bb_miiphy_bus *bus; + + bus = bb_miiphy_getbus(devname); + if (bus == NULL) { + return -1; + } + + if (value == NULL) { + puts("NULL value pointer\n"); + return -1; + } + + miiphy_pre (bus, 1, addr, reg); + + /* tri-state our MDIO I/O pin so we can read */ + bus->set_mdc(bus, 0); + bus->mdio_tristate(bus); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + /* check the turnaround bit: the PHY should be driving it to zero */ + bus->get_mdio(bus, &v); + if (v != 0) { + /* puts ("PHY didn't drive TA low\n"); */ + for (j = 0; j < 32; j++) { + bus->set_mdc(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + } + /* There is no PHY, set value to 0xFFFF and return */ + *value = 0xFFFF; + return -1; + } + + bus->set_mdc(bus, 0); + bus->delay(bus); + + /* read 16 bits of register data, MSB first */ + rdreg = 0; + for (j = 0; j < 16; j++) { + bus->set_mdc(bus, 1); + bus->delay(bus); + rdreg <<= 1; + bus->get_mdio(bus, &v); + rdreg |= (v & 0x1); + bus->set_mdc(bus, 0); + bus->delay(bus); + } + + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + *value = rdreg; + +#ifdef DEBUG + printf ("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, *value); +#endif + + return 0; +} + + +/***************************************************************************** + * + * Write a MII PHY register. + * + * Returns: + * 0 on success + */ +int bb_miiphy_write (const char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct bb_miiphy_bus *bus; + int j; /* counter */ + + bus = bb_miiphy_getbus(devname); + if (bus == NULL) { + /* Bus not found! */ + return -1; + } + + miiphy_pre (bus, 0, addr, reg); + + /* send the turnaround (10) */ + bus->set_mdc(bus, 0); + bus->set_mdio(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->set_mdio(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + /* write 16 bits of register data, MSB first */ + for (j = 0; j < 16; j++) { + bus->set_mdc(bus, 0); + if ((value & 0x00008000) == 0) { + bus->set_mdio(bus, 0); + } else { + bus->set_mdio(bus, 1); + } + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + value <<= 1; + } + + /* + * Tri-state the MDIO line. + */ + bus->mdio_tristate(bus); + bus->set_mdc(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/mv88e61xx.c b/qemu/roms/u-boot/drivers/net/phy/mv88e61xx.c new file mode 100644 index 000000000..302abe86c --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/mv88e61xx.c @@ -0,0 +1,537 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Prafulla Wadaskar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include "mv88e61xx.h" + +/* + * Uncomment either of the following line for local debug control; + * otherwise global debug control will apply. + */ + +/* #undef DEBUG */ +/* #define DEBUG */ + +#ifdef CONFIG_MV88E61XX_MULTICHIP_ADRMODE +/* Chip Address mode + * The Switch support two modes of operation + * 1. single chip mode and + * 2. Multi-chip mode + * Refer section 9.2 &9.3 in chip datasheet-02 for more details + * + * By default single chip mode is configured + * multichip mode operation can be configured in board header + */ +static int mv88e61xx_busychk_multic(char *name, u32 devaddr) +{ + u16 reg = 0; + u32 timeout = MV88E61XX_PHY_TIMEOUT; + + /* Poll till SMIBusy bit is clear */ + do { + miiphy_read(name, devaddr, 0x0, ®); + if (timeout-- == 0) { + printf("SMI busy timeout\n"); + return -1; + } + } while (reg & (1 << 15)); + return 0; +} + +static void mv88e61xx_switch_write(char *name, u32 phy_adr, + u32 reg_ofs, u16 data) +{ + u16 mii_dev_addr; + + /* command to read PHY dev address */ + if (miiphy_read(name, 0xEE, 0xEE, &mii_dev_addr)) { + printf("Error..could not read PHY dev address\n"); + return; + } + mv88e61xx_busychk_multic(name, mii_dev_addr); + /* Write data to Switch indirect data register */ + miiphy_write(name, mii_dev_addr, 0x1, data); + /* Write command to Switch indirect command register (write) */ + miiphy_write(name, mii_dev_addr, 0x0, + reg_ofs | (phy_adr << 5) | (1 << 10) | (1 << 12) | (1 << + 15)); +} + +static void mv88e61xx_switch_read(char *name, u32 phy_adr, + u32 reg_ofs, u16 *data) +{ + u16 mii_dev_addr; + + /* command to read PHY dev address */ + if (miiphy_read(name, 0xEE, 0xEE, &mii_dev_addr)) { + printf("Error..could not read PHY dev address\n"); + return; + } + mv88e61xx_busychk_multic(name, mii_dev_addr); + /* Write command to Switch indirect command register (read) */ + miiphy_write(name, mii_dev_addr, 0x0, + reg_ofs | (phy_adr << 5) | (1 << 11) | (1 << 12) | (1 << + 15)); + mv88e61xx_busychk_multic(name, mii_dev_addr); + /* Read data from Switch indirect data register */ + miiphy_read(name, mii_dev_addr, 0x1, data); +} +#endif /* CONFIG_MV88E61XX_MULTICHIP_ADRMODE */ + +/* + * Convenience macros for switch device/port reads/writes + * These macros output valid 'mv88e61xx' U_BOOT_CMDs + */ + +#ifndef DEBUG +#define WR_SWITCH_REG wr_switch_reg +#define RD_SWITCH_REG rd_switch_reg +#define WR_SWITCH_PORT_REG(n, p, r, d) \ + WR_SWITCH_REG(n, (MV88E61XX_PRT_OFST+p), r, d) +#define RD_SWITCH_PORT_REG(n, p, r, d) \ + RD_SWITCH_REG(n, (MV88E61XX_PRT_OFST+p), r, d) +#else +static void WR_SWITCH_REG(char *name, u32 dev_adr, u32 reg_ofs, u16 data) +{ + printf("mv88e61xx %s dev %02x reg %02x write %04x\n", + name, dev_adr, reg_ofs, data); + wr_switch_reg(name, dev_adr, reg_ofs, data); +} +static void RD_SWITCH_REG(char *name, u32 dev_adr, u32 reg_ofs, u16 *data) +{ + rd_switch_reg(name, dev_adr, reg_ofs, data); + printf("mv88e61xx %s dev %02x reg %02x read %04x\n", + name, dev_adr, reg_ofs, *data); +} +static void WR_SWITCH_PORT_REG(char *name, u32 prt_adr, u32 reg_ofs, + u16 data) +{ + printf("mv88e61xx %s port %02x reg %02x write %04x\n", + name, prt_adr, reg_ofs, data); + wr_switch_reg(name, (MV88E61XX_PRT_OFST+prt_adr), reg_ofs, data); +} +static void RD_SWITCH_PORT_REG(char *name, u32 prt_adr, u32 reg_ofs, + u16 *data) +{ + rd_switch_reg(name, (MV88E61XX_PRT_OFST+prt_adr), reg_ofs, data); + printf("mv88e61xx %s port %02x reg %02x read %04x\n", + name, prt_adr, reg_ofs, *data); +} +#endif + +/* + * Local functions to read/write registers on the switch PHYs. + * NOTE! This goes through switch, not direct miiphy, writes and reads! + */ + +/* + * Make sure SMIBusy bit cleared before another + * SMI operation can take place + */ +static int mv88e61xx_busychk(char *name) +{ + u16 reg = 0; + u32 timeout = MV88E61XX_PHY_TIMEOUT; + do { + rd_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, + MV88E61XX_PHY_CMD, ®); + if (timeout-- == 0) { + printf("SMI busy timeout\n"); + return -1; + } + } while (reg & 1 << 15); /* busy mask */ + return 0; +} + +static inline int mv88e61xx_switch_miiphy_write(char *name, u32 phy, + u32 reg, u16 data) +{ + /* write switch data reg then cmd reg then check completion */ + wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, + data); + wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_CMD, + (MV88E61XX_PHY_WRITE_CMD | (phy << 5) | reg)); + return mv88e61xx_busychk(name); +} + +static inline int mv88e61xx_switch_miiphy_read(char *name, u32 phy, + u32 reg, u16 *data) +{ + /* write switch cmd reg, check for completion */ + wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_CMD, + (MV88E61XX_PHY_READ_CMD | (phy << 5) | reg)); + if (mv88e61xx_busychk(name)) + return -1; + /* read switch data reg and return success */ + rd_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, data); + return 0; +} + +/* + * Convenience macros for switch PHY reads/writes + */ + +#ifndef DEBUG +#define WR_SWITCH_PHY_REG mv88e61xx_switch_miiphy_write +#define RD_SWITCH_PHY_REG mv88e61xx_switch_miiphy_read +#else +static inline int WR_SWITCH_PHY_REG(char *name, u32 phy_adr, + u32 reg_ofs, u16 data) +{ + int r = mv88e61xx_switch_miiphy_write(name, phy_adr, reg_ofs, data); + if (r) + printf("** ERROR writing mv88e61xx %s phy %02x reg %02x\n", + name, phy_adr, reg_ofs); + else + printf("mv88e61xx %s phy %02x reg %02x write %04x\n", + name, phy_adr, reg_ofs, data); + return r; +} +static inline int RD_SWITCH_PHY_REG(char *name, u32 phy_adr, + u32 reg_ofs, u16 *data) +{ + int r = mv88e61xx_switch_miiphy_read(name, phy_adr, reg_ofs, data); + if (r) + printf("** ERROR reading mv88e61xx %s phy %02x reg %02x\n", + name, phy_adr, reg_ofs); + else + printf("mv88e61xx %s phy %02x reg %02x read %04x\n", + name, phy_adr, reg_ofs, *data); + return r; +} +#endif + +static void mv88e61xx_port_vlan_config(struct mv88e61xx_config *swconfig) +{ + u32 prt; + u16 reg; + char *name = swconfig->name; + u32 port_mask = swconfig->ports_enabled; + + /* apply internal vlan config */ + for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { + /* only for enabled ports */ + if ((1 << prt) & port_mask) { + /* take vlan map from swconfig */ + u8 vlanmap = swconfig->vlancfg[prt]; + /* remove disabled ports from vlan map */ + vlanmap &= swconfig->ports_enabled; + /* apply vlan map to port */ + RD_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_VMAP_REG, ®); + reg &= ~((1 << MV88E61XX_MAX_PORTS_NUM) - 1); + reg |= vlanmap; + WR_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_VMAP_REG, reg); + } + } +} + +/* + * Power up the specified port and reset PHY + */ +static int mv88361xx_powerup(struct mv88e61xx_config *swconfig, u32 phy) +{ + char *name = swconfig->name; + + /* Write Copper Specific control reg1 (0x10) for- + * Enable Phy power up + * Energy Detect on (sense&Xmit NLP Periodically + * reset other settings default + */ + if (WR_SWITCH_PHY_REG(name, phy, 0x10, 0x3360)) + return -1; + + /* Write PHY ctrl reg (0x0) to apply + * Phy reset (set bit 15 low) + * reset other default values + */ + if (WR_SWITCH_PHY_REG(name, phy, 0x00, 0x9140)) + return -1; + + return 0; +} + +/* + * Default Setup for LED[0]_Control (ref: Table 46 Datasheet-3) + * is set to "On-1000Mb/s Link, Off Else" + * This function sets it to "On-Link, Blink-Activity, Off-NoLink" + * + * This is optional settings may be needed on some boards + * to setup PHY LEDs default configuration to detect 10/100/1000Mb/s + * Link status + */ +static int mv88361xx_led_init(struct mv88e61xx_config *swconfig, u32 phy) +{ + char *name = swconfig->name; + + if (swconfig->led_init != MV88E61XX_LED_INIT_EN) + return 0; + + /* set page address to 3 */ + if (WR_SWITCH_PHY_REG(name, phy, 0x16, 0x0003)) + return -1; + + /* + * set LED Func Ctrl reg + * value 0x0001 = LED[0] On-Link, Blink-Activity, Off-NoLink + */ + if (WR_SWITCH_PHY_REG(name, phy, 0x10, 0x0001)) + return -1; + + /* set page address to 0 */ + if (WR_SWITCH_PHY_REG(name, phy, 0x16, 0x0000)) + return -1; + + return 0; +} + +/* + * Reverse Transmit polarity for Media Dependent Interface + * Pins (MDIP) bits in Copper Specific Control Register 3 + * (Page 0, Reg 20 for each phy (except cpu port) + * Reference: Section 1.1 Switch datasheet-3 + * + * This is optional settings may be needed on some boards + * for PHY<->magnetics h/w tuning + */ +static int mv88361xx_reverse_mdipn(struct mv88e61xx_config *swconfig, u32 phy) +{ + char *name = swconfig->name; + + if (swconfig->mdip != MV88E61XX_MDIP_REVERSE) + return 0; + + /*Reverse MDIP/N[3:0] bits */ + if (WR_SWITCH_PHY_REG(name, phy, 0x14, 0x000f)) + return -1; + + return 0; +} + +/* + * Marvell 88E61XX Switch initialization + */ +int mv88e61xx_switch_initialize(struct mv88e61xx_config *swconfig) +{ + u32 prt; + u16 reg; + char *idstr; + char *name = swconfig->name; + int time; + + if (miiphy_set_current_dev(name)) { + printf("%s failed\n", __FUNCTION__); + return -1; + } + + if (!(swconfig->cpuport & ((1 << 4) | (1 << 5)))) { + swconfig->cpuport = (1 << 5); + printf("Invalid cpu port config, using default port5\n"); + } + + RD_SWITCH_PORT_REG(name, 0, MII_PHYSID2, ®); + switch (reg &= 0xfff0) { + case 0x1610: + idstr = "88E6161"; + break; + case 0x1650: + idstr = "88E6165"; + break; + case 0x1210: + idstr = "88E6123"; + /* ports 2,3,4 not available */ + swconfig->ports_enabled &= 0x023; + break; + default: + /* Could not detect switch id */ + idstr = "88E61??"; + break; + } + + /* be sure all ports are disabled */ + for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { + RD_SWITCH_PORT_REG(name, prt, MV88E61XX_PRT_CTRL_REG, ®); + reg &= ~0x3; + WR_SWITCH_PORT_REG(name, prt, MV88E61XX_PRT_CTRL_REG, reg); + } + + /* wait 2 ms for queues to drain */ + udelay(2000); + + /* reset switch */ + RD_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGCR, ®); + reg |= 0x8000; + WR_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGCR, reg); + + /* wait up to 1 second for switch reset complete */ + for (time = 1000; time; time--) { + RD_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGSR, + ®); + if ((reg & 0xc800) == 0xc800) + break; + udelay(1000); + } + if (!time) + return -1; + + /* Port based VLANs configuration */ + mv88e61xx_port_vlan_config(swconfig); + + if (swconfig->rgmii_delay == MV88E61XX_RGMII_DELAY_EN) { + /* + * Enable RGMII delay on Tx and Rx for CPU port + * Ref: sec 9.5 of chip datasheet-02 + */ + /*Force port link down */ + WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x10); + /* configure port RGMII delay */ + WR_SWITCH_PORT_REG(name, 4, + MV88E61XX_RGMII_TIMECTRL_REG, 0x81e7); + RD_SWITCH_PORT_REG(name, 5, + MV88E61XX_RGMII_TIMECTRL_REG, ®); + WR_SWITCH_PORT_REG(name, 5, + MV88E61XX_RGMII_TIMECTRL_REG, reg | 0x18); + WR_SWITCH_PORT_REG(name, 4, + MV88E61XX_RGMII_TIMECTRL_REG, 0xc1e7); + /* Force port to RGMII FDX 1000Base then up */ + WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x1e); + WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x3e); + } + + for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { + + /* configure port's PHY */ + if (!((1 << prt) & swconfig->cpuport)) { + /* port 4 has phy 6, not 4 */ + int phy = (prt == 4) ? 6 : prt; + if (mv88361xx_powerup(swconfig, phy)) + return -1; + if (mv88361xx_reverse_mdipn(swconfig, phy)) + return -1; + if (mv88361xx_led_init(swconfig, phy)) + return -1; + } + + /* set port VID to port+1 except for cpu port */ + if (!((1 << prt) & swconfig->cpuport)) { + RD_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_VID_REG, ®); + WR_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_VID_REG, + (reg & ~1023) | (prt+1)); + } + + /*Program port state */ + RD_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_CTRL_REG, ®); + WR_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_CTRL_REG, + reg | (swconfig->portstate & 0x03)); + + } + + printf("%s Initialized on %s\n", idstr, name); + return 0; +} + +#ifdef CONFIG_MV88E61XX_CMD +static int +do_switch(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *name, *endp; + int write = 0; + enum { dev, prt, phy } target = dev; + u32 addrlo, addrhi, addr; + u32 reglo, reghi, reg; + u16 data, rdata; + + if (argc < 7) + return -1; + + name = argv[1]; + + if (strcmp(argv[2], "phy") == 0) + target = phy; + else if (strcmp(argv[2], "port") == 0) + target = prt; + else if (strcmp(argv[2], "dev") != 0) + return 1; + + addrlo = simple_strtoul(argv[3], &endp, 16); + + if (!*endp) { + addrhi = addrlo; + } else { + while (*endp < '0' || *endp > '9') + endp++; + addrhi = simple_strtoul(endp, NULL, 16); + } + + reglo = simple_strtoul(argv[5], &endp, 16); + if (!*endp) { + reghi = reglo; + } else { + while (*endp < '0' || *endp > '9') + endp++; + reghi = simple_strtoul(endp, NULL, 16); + } + + if (strcmp(argv[6], "write") == 0) + write = 1; + else if (strcmp(argv[6], "read") != 0) + return 1; + + data = simple_strtoul(argv[7], NULL, 16); + + for (addr = addrlo; addr <= addrhi; addr++) { + for (reg = reglo; reg <= reghi; reg++) { + if (write) { + if (target == phy) + mv88e61xx_switch_miiphy_write( + name, addr, reg, data); + else if (target == prt) + wr_switch_reg(name, + addr+MV88E61XX_PRT_OFST, + reg, data); + else + wr_switch_reg(name, addr, reg, data); + } else { + if (target == phy) + mv88e61xx_switch_miiphy_read( + name, addr, reg, &rdata); + else if (target == prt) + rd_switch_reg(name, + addr+MV88E61XX_PRT_OFST, + reg, &rdata); + else + rd_switch_reg(name, addr, reg, &rdata); + printf("%s %s %s %02x %s %02x %s %04x\n", + argv[0], argv[1], argv[2], addr, + argv[4], reg, argv[6], rdata); + if (write && argc == 7 && rdata != data) + return 1; + } + } + } + return 0; +} + +U_BOOT_CMD(mv88e61xx, 8, 0, do_switch, + "Read or write mv88e61xx switch registers", + " dev|port|phy reg write \n" + " dev|port|phy reg read []\n" + " - read/write switch device, port or phy at (addr,reg)\n" + " addr=0..0x1C for dev, 0..5 for port or phy.\n" + " reg=0..0x1F.\n" + " data=0..0xFFFF (tested if present against actual read).\n" + " All numeric parameters are assumed to be hex.\n" + " and < arguments can be ranges (x..y)" +); +#endif /* CONFIG_MV88E61XX_CMD */ diff --git a/qemu/roms/u-boot/drivers/net/phy/mv88e61xx.h b/qemu/roms/u-boot/drivers/net/phy/mv88e61xx.h new file mode 100644 index 000000000..9c62e4a77 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/mv88e61xx.h @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Prafulla Wadaskar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _MV88E61XX_H +#define _MV88E61XX_H + +#include + +#define MV88E61XX_CPU_PORT 0x5 + +#define MV88E61XX_PHY_TIMEOUT 100000 + +/* port dev-addr (= port + 0x10) */ +#define MV88E61XX_PRT_OFST 0x10 +/* port registers */ +#define MV88E61XX_PCS_CTRL_REG 0x1 +#define MV88E61XX_PRT_CTRL_REG 0x4 +#define MV88E61XX_PRT_VMAP_REG 0x6 +#define MV88E61XX_PRT_VID_REG 0x7 +#define MV88E61XX_RGMII_TIMECTRL_REG 0x1A + +/* global registers dev-addr */ +#define MV88E61XX_GLBREG_DEVADR 0x1B +/* global registers */ +#define MV88E61XX_SGSR 0x00 +#define MV88E61XX_SGCR 0x04 + +/* global 2 registers dev-addr */ +#define MV88E61XX_GLB2REG_DEVADR 0x1C +/* global 2 registers */ +#define MV88E61XX_PHY_CMD 0x18 +#define MV88E61XX_PHY_DATA 0x19 +/* global 2 phy commands */ +#define MV88E61XX_PHY_WRITE_CMD 0x9400 +#define MV88E61XX_PHY_READ_CMD 0x9800 + +#define MV88E61XX_BUSY_OFST 15 +#define MV88E61XX_MODE_OFST 12 +#define MV88E61XX_OP_OFST 10 +#define MV88E61XX_ADDR_OFST 5 + +#ifdef CONFIG_MV88E61XX_MULTICHIP_ADRMODE +static int mv88e61xx_busychk_multic(char *name, u32 devaddr); +static void mv88e61xx_switch_write(char *name, u32 phy_adr, + u32 reg_ofs, u16 data); +static void mv88e61xx_switch_read(char *name, u32 phy_adr, + u32 reg_ofs, u16 *data); +#define wr_switch_reg mv88e61xx_switch_write +#define rd_switch_reg mv88e61xx_switch_read +#else +/* switch appears a s simple PHY and can thus use miiphy */ +#define wr_switch_reg miiphy_write +#define rd_switch_reg miiphy_read +#endif /* CONFIG_MV88E61XX_MULTICHIP_ADRMODE */ + +#endif /* _MV88E61XX_H */ diff --git a/qemu/roms/u-boot/drivers/net/phy/mv88e6352.c b/qemu/roms/u-boot/drivers/net/phy/mv88e6352.c new file mode 100644 index 000000000..f639a42fa --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/mv88e6352.c @@ -0,0 +1,302 @@ +/* + * (C) Copyright 2012 + * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define SMI_HDR ((0x8 | 0x1) << 12) +#define SMI_BUSY_MASK (0x8000) +#define SMIRD_OP (0x2 << 10) +#define SMIWR_OP (0x1 << 10) +#define SMI_MASK 0x1f +#define PORT_SHIFT 5 + +#define COMMAND_REG 0 +#define DATA_REG 1 + +/* global registers */ +#define GLOBAL 0x1b + +#define GLOBAL_STATUS 0x00 +#define PPU_STATE 0x8000 + +#define GLOBAL_CTRL 0x04 +#define SW_RESET 0x8000 +#define PPU_ENABLE 0x4000 + +static int sw_wait_rdy(const char *devname, u8 phy_addr) +{ + u16 command; + u32 timeout = 100; + int ret; + + /* wait till the SMI is not busy */ + do { + /* read command register */ + ret = miiphy_read(devname, phy_addr, COMMAND_REG, &command); + if (ret < 0) { + printf("%s: Error reading command register\n", + __func__); + return ret; + } + if (timeout-- == 0) { + printf("Err..(%s) SMI busy timeout\n", __func__); + return -EFAULT; + } + } while (command & SMI_BUSY_MASK); + + return 0; +} + +static int sw_reg_read(const char *devname, u8 phy_addr, u8 port, + u8 reg, u16 *data) +{ + int ret; + u16 command; + + ret = sw_wait_rdy(devname, phy_addr); + if (ret) + return ret; + + command = SMI_HDR | SMIRD_OP | ((port&SMI_MASK) << PORT_SHIFT) | + (reg & SMI_MASK); + debug("%s: write to command: %#x\n", __func__, command); + ret = miiphy_write(devname, phy_addr, COMMAND_REG, command); + if (ret) + return ret; + + ret = sw_wait_rdy(devname, phy_addr); + if (ret) + return ret; + + ret = miiphy_read(devname, phy_addr, DATA_REG, data); + + return ret; +} + +static int sw_reg_write(const char *devname, u8 phy_addr, u8 port, + u8 reg, u16 data) +{ + int ret; + u16 value; + + ret = sw_wait_rdy(devname, phy_addr); + if (ret) + return ret; + + debug("%s: write to data: %#x\n", __func__, data); + ret = miiphy_write(devname, phy_addr, DATA_REG, data); + if (ret) + return ret; + + value = SMI_HDR | SMIWR_OP | ((port & SMI_MASK) << PORT_SHIFT) | + (reg & SMI_MASK); + debug("%s: write to command: %#x\n", __func__, value); + ret = miiphy_write(devname, phy_addr, COMMAND_REG, value); + if (ret) + return ret; + + ret = sw_wait_rdy(devname, phy_addr); + if (ret) + return ret; + + return 0; +} + +static int ppu_enable(const char *devname, u8 phy_addr) +{ + int i, ret = 0; + u16 reg; + + ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, ®); + if (ret) { + printf("%s: Error reading global ctrl reg\n", __func__); + return ret; + } + + reg |= PPU_ENABLE; + + ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg); + if (ret) { + printf("%s: Error writing global ctrl reg\n", __func__); + return ret; + } + + for (i = 0; i < 1000; i++) { + sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS, + ®); + if ((reg & 0xc000) == 0xc000) + return 0; + udelay(1000); + } + + return -ETIMEDOUT; +} + +static int ppu_disable(const char *devname, u8 phy_addr) +{ + int i, ret = 0; + u16 reg; + + ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, ®); + if (ret) { + printf("%s: Error reading global ctrl reg\n", __func__); + return ret; + } + + reg &= ~PPU_ENABLE; + + ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg); + if (ret) { + printf("%s: Error writing global ctrl reg\n", __func__); + return ret; + } + + for (i = 0; i < 1000; i++) { + sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS, + ®); + if ((reg & 0xc000) != 0xc000) + return 0; + udelay(1000); + } + + return -ETIMEDOUT; +} + +int mv88e_sw_program(const char *devname, u8 phy_addr, + struct mv88e_sw_reg *regs, int regs_nb) +{ + int i, ret = 0; + + /* first we need to disable the PPU */ + ret = ppu_disable(devname, phy_addr); + if (ret) { + printf("%s: Error disabling PPU\n", __func__); + return ret; + } + + for (i = 0; i < regs_nb; i++) { + ret = sw_reg_write(devname, phy_addr, regs[i].port, + regs[i].reg, regs[i].value); + if (ret) { + printf("%s: Error configuring switch\n", __func__); + ppu_enable(devname, phy_addr); + return ret; + } + } + + /* re-enable the PPU */ + ret = ppu_enable(devname, phy_addr); + if (ret) { + printf("%s: Error enabling PPU\n", __func__); + return ret; + } + + return 0; +} + +int mv88e_sw_reset(const char *devname, u8 phy_addr) +{ + int i, ret = 0; + u16 reg; + + ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, ®); + if (ret) { + printf("%s: Error reading global ctrl reg\n", __func__); + return ret; + } + + reg = SW_RESET | PPU_ENABLE | 0x0400; + + ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg); + if (ret) { + printf("%s: Error writing global ctrl reg\n", __func__); + return ret; + } + + for (i = 0; i < 1000; i++) { + sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS, + ®); + if ((reg & 0xc800) != 0xc800) + return 0; + udelay(1000); + } + + return -ETIMEDOUT; +} + +int do_mvsw_reg_read(const char *name, int argc, char * const argv[]) +{ + u16 value = 0, phyaddr, reg, port; + int ret; + + phyaddr = simple_strtoul(argv[1], NULL, 10); + port = simple_strtoul(argv[2], NULL, 10); + reg = simple_strtoul(argv[3], NULL, 10); + + ret = sw_reg_read(name, phyaddr, port, reg, &value); + printf("%#x\n", value); + + return ret; +} + +int do_mvsw_reg_write(const char *name, int argc, char * const argv[]) +{ + u16 value = 0, phyaddr, reg, port; + int ret; + + phyaddr = simple_strtoul(argv[1], NULL, 10); + port = simple_strtoul(argv[2], NULL, 10); + reg = simple_strtoul(argv[3], NULL, 10); + value = simple_strtoul(argv[4], NULL, 16); + + ret = sw_reg_write(name, phyaddr, port, reg, value); + + return ret; +} + + +int do_mvsw_reg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret; + const char *cmd, *ethname; + + if (argc < 2) + return cmd_usage(cmdtp); + + cmd = argv[1]; + --argc; + ++argv; + + if (strcmp(cmd, "read") == 0) { + if (argc < 5) + return cmd_usage(cmdtp); + ethname = argv[1]; + --argc; + ++argv; + ret = do_mvsw_reg_read(ethname, argc, argv); + } else if (strcmp(cmd, "write") == 0) { + if (argc < 6) + return cmd_usage(cmdtp); + ethname = argv[1]; + --argc; + ++argv; + ret = do_mvsw_reg_write(ethname, argc, argv); + } else + return cmd_usage(cmdtp); + + return ret; +} + +U_BOOT_CMD( + mvsw_reg, 7, 1, do_mvsw_reg, + "marvell 88e6352 switch register access", + "write ethname phyaddr port reg value\n" + "mvsw_reg read ethname phyaddr port reg\n" + ); diff --git a/qemu/roms/u-boot/drivers/net/phy/natsemi.c b/qemu/roms/u-boot/drivers/net/phy/natsemi.c new file mode 100644 index 000000000..ea9fe833e --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/natsemi.c @@ -0,0 +1,119 @@ +/* + * National Semiconductor PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * author Andy Fleming + */ +#include + +/* NatSemi DP83630 */ + +#define DP83630_PHY_PAGESEL_REG 0x13 +#define DP83630_PHY_PTP_COC_REG 0x14 +#define DP83630_PHY_PTP_CLKOUT_EN (1<<15) +#define DP83630_PHY_RBR_REG 0x17 + +static int dp83630_config(struct phy_device *phydev) +{ + int ptp_coc_reg; + + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + phy_write(phydev, MDIO_DEVAD_NONE, DP83630_PHY_PAGESEL_REG, 0x6); + ptp_coc_reg = phy_read(phydev, MDIO_DEVAD_NONE, + DP83630_PHY_PTP_COC_REG); + ptp_coc_reg &= ~DP83630_PHY_PTP_CLKOUT_EN; + phy_write(phydev, MDIO_DEVAD_NONE, DP83630_PHY_PTP_COC_REG, + ptp_coc_reg); + phy_write(phydev, MDIO_DEVAD_NONE, DP83630_PHY_PAGESEL_REG, 0); + + genphy_config_aneg(phydev); + + return 0; +} + +static struct phy_driver DP83630_driver = { + .name = "NatSemi DP83630", + .uid = 0x20005ce1, + .mask = 0xfffffff0, + .features = PHY_BASIC_FEATURES, + .config = &dp83630_config, + .startup = &genphy_startup, + .shutdown = &genphy_shutdown, +}; + + +/* DP83865 Link and Auto-Neg Status Register */ +#define MIIM_DP83865_LANR 0x11 +#define MIIM_DP83865_SPD_MASK 0x0018 +#define MIIM_DP83865_SPD_1000 0x0010 +#define MIIM_DP83865_SPD_100 0x0008 +#define MIIM_DP83865_DPX_FULL 0x0002 + + +/* NatSemi DP83865 */ +static int dp83865_config(struct phy_device *phydev) +{ + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + genphy_config_aneg(phydev); + + return 0; +} + +static int dp83865_parse_status(struct phy_device *phydev) +{ + int mii_reg; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_DP83865_LANR); + + switch (mii_reg & MIIM_DP83865_SPD_MASK) { + + case MIIM_DP83865_SPD_1000: + phydev->speed = SPEED_1000; + break; + + case MIIM_DP83865_SPD_100: + phydev->speed = SPEED_100; + break; + + default: + phydev->speed = SPEED_10; + break; + + } + + if (mii_reg & MIIM_DP83865_DPX_FULL) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + return 0; +} + +static int dp83865_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + dp83865_parse_status(phydev); + + return 0; +} + + +static struct phy_driver DP83865_driver = { + .name = "NatSemi DP83865", + .uid = 0x20005c70, + .mask = 0xfffffff0, + .features = PHY_GBIT_FEATURES, + .config = &dp83865_config, + .startup = &dp83865_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_natsemi_init(void) +{ + phy_register(&DP83630_driver); + phy_register(&DP83865_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/phy.c b/qemu/roms/u-boot/drivers/net/phy/phy.c new file mode 100644 index 000000000..230ed97dd --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/phy.c @@ -0,0 +1,817 @@ +/* + * Generic PHY Management code + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2011 Freescale Semiconductor, Inc. + * author Andy Fleming + * + * Based loosely off of Linux's PHY Lib + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Generic PHY support and helper functions */ + +/** + * genphy_config_advert - sanitize and advertise auto-negotation parameters + * @phydev: target phy_device struct + * + * Description: Writes MII_ADVERTISE with the appropriate values, + * after sanitizing the values to make sure we only advertise + * what is supported. Returns < 0 on error, 0 if the PHY's advertisement + * hasn't changed, and > 0 if it has changed. + */ +static int genphy_config_advert(struct phy_device *phydev) +{ + u32 advertise; + int oldadv, adv; + int err, changed = 0; + + /* Only allow advertising what + * this PHY supports */ + phydev->advertising &= phydev->supported; + advertise = phydev->advertising; + + /* Setup standard advertisement */ + oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); + + if (adv < 0) + return adv; + + adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + if (advertise & ADVERTISED_10baseT_Half) + adv |= ADVERTISE_10HALF; + if (advertise & ADVERTISED_10baseT_Full) + adv |= ADVERTISE_10FULL; + if (advertise & ADVERTISED_100baseT_Half) + adv |= ADVERTISE_100HALF; + if (advertise & ADVERTISED_100baseT_Full) + adv |= ADVERTISE_100FULL; + if (advertise & ADVERTISED_Pause) + adv |= ADVERTISE_PAUSE_CAP; + if (advertise & ADVERTISED_Asym_Pause) + adv |= ADVERTISE_PAUSE_ASYM; + if (advertise & ADVERTISED_1000baseX_Half) + adv |= ADVERTISE_1000XHALF; + if (advertise & ADVERTISED_1000baseX_Full) + adv |= ADVERTISE_1000XFULL; + + if (adv != oldadv) { + err = phy_write(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE, adv); + + if (err < 0) + return err; + changed = 1; + } + + /* Configure gigabit if it's supported */ + if (phydev->supported & (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full)) { + oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); + + if (adv < 0) + return adv; + + adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + if (advertise & SUPPORTED_1000baseT_Half) + adv |= ADVERTISE_1000HALF; + if (advertise & SUPPORTED_1000baseT_Full) + adv |= ADVERTISE_1000FULL; + + if (adv != oldadv) { + err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, + adv); + + if (err < 0) + return err; + changed = 1; + } + } + + return changed; +} + + +/** + * genphy_setup_forced - configures/forces speed/duplex from @phydev + * @phydev: target phy_device struct + * + * Description: Configures MII_BMCR to force speed/duplex + * to the values in phydev. Assumes that the values are valid. + */ +static int genphy_setup_forced(struct phy_device *phydev) +{ + int err; + int ctl = 0; + + phydev->pause = phydev->asym_pause = 0; + + if (SPEED_1000 == phydev->speed) + ctl |= BMCR_SPEED1000; + else if (SPEED_100 == phydev->speed) + ctl |= BMCR_SPEED100; + + if (DUPLEX_FULL == phydev->duplex) + ctl |= BMCR_FULLDPLX; + + err = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl); + + return err; +} + + +/** + * genphy_restart_aneg - Enable and Restart Autonegotiation + * @phydev: target phy_device struct + */ +int genphy_restart_aneg(struct phy_device *phydev) +{ + int ctl; + + ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + + if (ctl < 0) + return ctl; + + ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); + + /* Don't isolate the PHY if we're negotiating */ + ctl &= ~(BMCR_ISOLATE); + + ctl = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl); + + return ctl; +} + + +/** + * genphy_config_aneg - restart auto-negotiation or write BMCR + * @phydev: target phy_device struct + * + * Description: If auto-negotiation is enabled, we configure the + * advertising, and then restart auto-negotiation. If it is not + * enabled, then we write the BMCR. + */ +int genphy_config_aneg(struct phy_device *phydev) +{ + int result; + + if (AUTONEG_ENABLE != phydev->autoneg) + return genphy_setup_forced(phydev); + + result = genphy_config_advert(phydev); + + if (result < 0) /* error */ + return result; + + if (result == 0) { + /* Advertisment hasn't changed, but maybe aneg was never on to + * begin with? Or maybe phy was isolated? */ + int ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + + if (ctl < 0) + return ctl; + + if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) + result = 1; /* do restart aneg */ + } + + /* Only restart aneg if we are advertising something different + * than we were before. */ + if (result > 0) + result = genphy_restart_aneg(phydev); + + return result; +} + +/** + * genphy_update_link - update link status in @phydev + * @phydev: target phy_device struct + * + * Description: Update the value in phydev->link to reflect the + * current link value. In order to do this, we need to read + * the status register twice, keeping the second value. + */ +int genphy_update_link(struct phy_device *phydev) +{ + unsigned int mii_reg; + + /* + * Wait if the link is up, and autonegotiation is in progress + * (ie - we're capable and it's not done) + */ + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + + /* + * If we already saw the link up, and it hasn't gone down, then + * we don't need to wait for autoneg again + */ + if (phydev->link && mii_reg & BMSR_LSTATUS) + return 0; + + if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) { + int i = 0; + + printf("%s Waiting for PHY auto negotiation to complete", + phydev->dev->name); + while (!(mii_reg & BMSR_ANEGCOMPLETE)) { + /* + * Timeout reached ? + */ + if (i > PHY_ANEG_TIMEOUT) { + printf(" TIMEOUT !\n"); + phydev->link = 0; + return 0; + } + + if (ctrlc()) { + puts("user interrupt!\n"); + phydev->link = 0; + return -EINTR; + } + + if ((i++ % 500) == 0) + printf("."); + + udelay(1000); /* 1 ms */ + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + } + printf(" done\n"); + phydev->link = 1; + } else { + /* Read the link a second time to clear the latched state */ + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + + if (mii_reg & BMSR_LSTATUS) + phydev->link = 1; + else + phydev->link = 0; + } + + return 0; +} + +/* + * Generic function which updates the speed and duplex. If + * autonegotiation is enabled, it uses the AND of the link + * partner's advertised capabilities and our advertised + * capabilities. If autonegotiation is disabled, we use the + * appropriate bits in the control register. + * + * Stolen from Linux's mii.c and phy_device.c + */ +int genphy_parse_link(struct phy_device *phydev) +{ + int mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + + /* We're using autonegotiation */ + if (phydev->supported & SUPPORTED_Autoneg) { + u32 lpa = 0; + int gblpa = 0; + u32 estatus = 0; + + /* Check for gigabit capability */ + if (phydev->supported & (SUPPORTED_1000baseT_Full | + SUPPORTED_1000baseT_Half)) { + /* We want a list of states supported by + * both PHYs in the link + */ + gblpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000); + if (gblpa < 0) { + debug("Could not read MII_STAT1000. Ignoring gigabit capability\n"); + gblpa = 0; + } + gblpa &= phy_read(phydev, + MDIO_DEVAD_NONE, MII_CTRL1000) << 2; + } + + /* Set the baseline so we only have to set them + * if they're different + */ + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + /* Check the gigabit fields */ + if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) { + phydev->speed = SPEED_1000; + + if (gblpa & PHY_1000BTSR_1000FD) + phydev->duplex = DUPLEX_FULL; + + /* We're done! */ + return 0; + } + + lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); + lpa &= phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA); + + if (lpa & (LPA_100FULL | LPA_100HALF)) { + phydev->speed = SPEED_100; + + if (lpa & LPA_100FULL) + phydev->duplex = DUPLEX_FULL; + + } else if (lpa & LPA_10FULL) + phydev->duplex = DUPLEX_FULL; + + /* + * Extended status may indicate that the PHY supports + * 1000BASE-T/X even though the 1000BASE-T registers + * are missing. In this case we can't tell whether the + * peer also supports it, so we only check extended + * status if the 1000BASE-T registers are actually + * missing. + */ + if ((mii_reg & BMSR_ESTATEN) && !(mii_reg & BMSR_ERCAP)) + estatus = phy_read(phydev, MDIO_DEVAD_NONE, + MII_ESTATUS); + + if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_XHALF | + ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) { + phydev->speed = SPEED_1000; + if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_TFULL)) + phydev->duplex = DUPLEX_FULL; + } + + } else { + u32 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (bmcr & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + + if (bmcr & BMCR_SPEED1000) + phydev->speed = SPEED_1000; + else if (bmcr & BMCR_SPEED100) + phydev->speed = SPEED_100; + } + + return 0; +} + +int genphy_config(struct phy_device *phydev) +{ + int val; + u32 features; + + /* For now, I'll claim that the generic driver supports + * all possible port types */ + features = (SUPPORTED_TP | SUPPORTED_MII + | SUPPORTED_AUI | SUPPORTED_FIBRE | + SUPPORTED_BNC); + + /* Do we support autonegotiation? */ + val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + + if (val < 0) + return val; + + if (val & BMSR_ANEGCAPABLE) + features |= SUPPORTED_Autoneg; + + if (val & BMSR_100FULL) + features |= SUPPORTED_100baseT_Full; + if (val & BMSR_100HALF) + features |= SUPPORTED_100baseT_Half; + if (val & BMSR_10FULL) + features |= SUPPORTED_10baseT_Full; + if (val & BMSR_10HALF) + features |= SUPPORTED_10baseT_Half; + + if (val & BMSR_ESTATEN) { + val = phy_read(phydev, MDIO_DEVAD_NONE, MII_ESTATUS); + + if (val < 0) + return val; + + if (val & ESTATUS_1000_TFULL) + features |= SUPPORTED_1000baseT_Full; + if (val & ESTATUS_1000_THALF) + features |= SUPPORTED_1000baseT_Half; + if (val & ESTATUS_1000_XFULL) + features |= SUPPORTED_1000baseX_Full; + if (val & ESTATUS_1000_XHALF) + features |= SUPPORTED_1000baseX_Half; + } + + phydev->supported = features; + phydev->advertising = features; + + genphy_config_aneg(phydev); + + return 0; +} + +int genphy_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + genphy_parse_link(phydev); + + return 0; +} + +int genphy_shutdown(struct phy_device *phydev) +{ + return 0; +} + +static struct phy_driver genphy_driver = { + .uid = 0xffffffff, + .mask = 0xffffffff, + .name = "Generic PHY", + .features = 0, + .config = genphy_config, + .startup = genphy_startup, + .shutdown = genphy_shutdown, +}; + +static LIST_HEAD(phy_drivers); + +int phy_init(void) +{ +#ifdef CONFIG_PHY_ATHEROS + phy_atheros_init(); +#endif +#ifdef CONFIG_PHY_BROADCOM + phy_broadcom_init(); +#endif +#ifdef CONFIG_PHY_DAVICOM + phy_davicom_init(); +#endif +#ifdef CONFIG_PHY_ET1011C + phy_et1011c_init(); +#endif +#ifdef CONFIG_PHY_ICPLUS + phy_icplus_init(); +#endif +#ifdef CONFIG_PHY_LXT + phy_lxt_init(); +#endif +#ifdef CONFIG_PHY_MARVELL + phy_marvell_init(); +#endif +#ifdef CONFIG_PHY_MICREL + phy_micrel_init(); +#endif +#ifdef CONFIG_PHY_NATSEMI + phy_natsemi_init(); +#endif +#ifdef CONFIG_PHY_REALTEK + phy_realtek_init(); +#endif +#ifdef CONFIG_PHY_SMSC + phy_smsc_init(); +#endif +#ifdef CONFIG_PHY_TERANETICS + phy_teranetics_init(); +#endif +#ifdef CONFIG_PHY_VITESSE + phy_vitesse_init(); +#endif + + return 0; +} + +int phy_register(struct phy_driver *drv) +{ + INIT_LIST_HEAD(&drv->list); + list_add_tail(&drv->list, &phy_drivers); + + return 0; +} + +static int phy_probe(struct phy_device *phydev) +{ + int err = 0; + + phydev->advertising = phydev->supported = phydev->drv->features; + phydev->mmds = phydev->drv->mmds; + + if (phydev->drv->probe) + err = phydev->drv->probe(phydev); + + return err; +} + +static struct phy_driver *generic_for_interface(phy_interface_t interface) +{ +#ifdef CONFIG_PHYLIB_10G + if (is_10g_interface(interface)) + return &gen10g_driver; +#endif + + return &genphy_driver; +} + +static struct phy_driver *get_phy_driver(struct phy_device *phydev, + phy_interface_t interface) +{ + struct list_head *entry; + int phy_id = phydev->phy_id; + struct phy_driver *drv = NULL; + + list_for_each(entry, &phy_drivers) { + drv = list_entry(entry, struct phy_driver, list); + if ((drv->uid & drv->mask) == (phy_id & drv->mask)) + return drv; + } + + /* If we made it here, there's no driver for this PHY */ + return generic_for_interface(interface); +} + +static struct phy_device *phy_device_create(struct mii_dev *bus, int addr, + int phy_id, + phy_interface_t interface) +{ + struct phy_device *dev; + + /* We allocate the device, and initialize the + * default values */ + dev = malloc(sizeof(*dev)); + if (!dev) { + printf("Failed to allocate PHY device for %s:%d\n", + bus->name, addr); + return NULL; + } + + memset(dev, 0, sizeof(*dev)); + + dev->duplex = -1; + dev->link = 1; + dev->interface = interface; + + dev->autoneg = AUTONEG_ENABLE; + + dev->addr = addr; + dev->phy_id = phy_id; + dev->bus = bus; + + dev->drv = get_phy_driver(dev, interface); + + phy_probe(dev); + + bus->phymap[addr] = dev; + + return dev; +} + +/** + * get_phy_id - reads the specified addr for its ID. + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * @phy_id: where to store the ID retrieved. + * + * Description: Reads the ID registers of the PHY at @addr on the + * @bus, stores it in @phy_id and returns zero on success. + */ +int __weak get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) +{ + int phy_reg; + + /* Grab the bits from PHYIR1, and put them + * in the upper half */ + phy_reg = bus->read(bus, addr, devad, MII_PHYSID1); + + if (phy_reg < 0) + return -EIO; + + *phy_id = (phy_reg & 0xffff) << 16; + + /* Grab the bits from PHYIR2, and put them in the lower half */ + phy_reg = bus->read(bus, addr, devad, MII_PHYSID2); + + if (phy_reg < 0) + return -EIO; + + *phy_id |= (phy_reg & 0xffff); + + return 0; +} + +static struct phy_device *create_phy_by_mask(struct mii_dev *bus, + unsigned phy_mask, int devad, phy_interface_t interface) +{ + u32 phy_id = 0xffffffff; + while (phy_mask) { + int addr = ffs(phy_mask) - 1; + int r = get_phy_id(bus, addr, devad, &phy_id); + if (r < 0) + return ERR_PTR(r); + /* If the PHY ID is mostly f's, we didn't find anything */ + if ((phy_id & 0x1fffffff) != 0x1fffffff) + return phy_device_create(bus, addr, phy_id, interface); + phy_mask &= ~(1 << addr); + } + return NULL; +} + +static struct phy_device *search_for_existing_phy(struct mii_dev *bus, + unsigned phy_mask, phy_interface_t interface) +{ + /* If we have one, return the existing device, with new interface */ + while (phy_mask) { + int addr = ffs(phy_mask) - 1; + if (bus->phymap[addr]) { + bus->phymap[addr]->interface = interface; + return bus->phymap[addr]; + } + phy_mask &= ~(1 << addr); + } + return NULL; +} + +static struct phy_device *get_phy_device_by_mask(struct mii_dev *bus, + unsigned phy_mask, phy_interface_t interface) +{ + int i; + struct phy_device *phydev; + + phydev = search_for_existing_phy(bus, phy_mask, interface); + if (phydev) + return phydev; + /* Try Standard (ie Clause 22) access */ + /* Otherwise we have to try Clause 45 */ + for (i = 0; i < 5; i++) { + phydev = create_phy_by_mask(bus, phy_mask, + i ? i : MDIO_DEVAD_NONE, interface); + if (IS_ERR(phydev)) + return NULL; + if (phydev) + return phydev; + } + printf("Phy not found\n"); + return phy_device_create(bus, ffs(phy_mask) - 1, 0xffffffff, interface); +} + +/** + * get_phy_device - reads the specified PHY device and returns its @phy_device struct + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * + * Description: Reads the ID registers of the PHY at @addr on the + * @bus, then allocates and returns the phy_device to represent it. + */ +static struct phy_device *get_phy_device(struct mii_dev *bus, int addr, + phy_interface_t interface) +{ + return get_phy_device_by_mask(bus, 1 << addr, interface); +} + +int phy_reset(struct phy_device *phydev) +{ + int reg; + int timeout = 500; + int devad = MDIO_DEVAD_NONE; + +#ifdef CONFIG_PHYLIB_10G + /* If it's 10G, we need to issue reset through one of the MMDs */ + if (is_10g_interface(phydev->interface)) { + if (!phydev->mmds) + gen10g_discover_mmds(phydev); + + devad = ffs(phydev->mmds) - 1; + } +#endif + + reg = phy_read(phydev, devad, MII_BMCR); + if (reg < 0) { + debug("PHY status read failed\n"); + return -1; + } + + reg |= BMCR_RESET; + + if (phy_write(phydev, devad, MII_BMCR, reg) < 0) { + debug("PHY reset failed\n"); + return -1; + } + +#ifdef CONFIG_PHY_RESET_DELAY + udelay(CONFIG_PHY_RESET_DELAY); /* Intel LXT971A needs this */ +#endif + /* + * Poll the control register for the reset bit to go to 0 (it is + * auto-clearing). This should happen within 0.5 seconds per the + * IEEE spec. + */ + while ((reg & BMCR_RESET) && timeout--) { + reg = phy_read(phydev, devad, MII_BMCR); + + if (reg < 0) { + debug("PHY status read failed\n"); + return -1; + } + udelay(1000); + } + + if (reg & BMCR_RESET) { + puts("PHY reset timed out\n"); + return -1; + } + + return 0; +} + +int miiphy_reset(const char *devname, unsigned char addr) +{ + struct mii_dev *bus = miiphy_get_dev_by_name(devname); + struct phy_device *phydev; + + /* + * miiphy_reset was only used on standard PHYs, so we'll fake it here. + * If later code tries to connect with the right interface, this will + * be corrected by get_phy_device in phy_connect() + */ + phydev = get_phy_device(bus, addr, PHY_INTERFACE_MODE_MII); + + return phy_reset(phydev); +} + +struct phy_device *phy_find_by_mask(struct mii_dev *bus, unsigned phy_mask, + phy_interface_t interface) +{ + /* Reset the bus */ + if (bus->reset) + bus->reset(bus); + + /* Wait 15ms to make sure the PHY has come out of hard reset */ + udelay(15000); + return get_phy_device_by_mask(bus, phy_mask, interface); +} + +void phy_connect_dev(struct phy_device *phydev, struct eth_device *dev) +{ + /* Soft Reset the PHY */ + phy_reset(phydev); + if (phydev->dev) { + printf("%s:%d is connected to %s. Reconnecting to %s\n", + phydev->bus->name, phydev->addr, + phydev->dev->name, dev->name); + } + phydev->dev = dev; + debug("%s connected to %s\n", dev->name, phydev->drv->name); +} + +struct phy_device *phy_connect(struct mii_dev *bus, int addr, + struct eth_device *dev, phy_interface_t interface) +{ + struct phy_device *phydev; + + phydev = phy_find_by_mask(bus, 1 << addr, interface); + if (phydev) + phy_connect_dev(phydev, dev); + else + printf("Could not get PHY for %s: addr %d\n", bus->name, addr); + return phydev; +} + +/* + * Start the PHY. Returns 0 on success, or a negative error code. + */ +int phy_startup(struct phy_device *phydev) +{ + if (phydev->drv->startup) + return phydev->drv->startup(phydev); + + return 0; +} + +static int __board_phy_config(struct phy_device *phydev) +{ + if (phydev->drv->config) + return phydev->drv->config(phydev); + return 0; +} + +int board_phy_config(struct phy_device *phydev) + __attribute__((weak, alias("__board_phy_config"))); + +int phy_config(struct phy_device *phydev) +{ + /* Invoke an optional board-specific helper */ + board_phy_config(phydev); + + return 0; +} + +int phy_shutdown(struct phy_device *phydev) +{ + if (phydev->drv->shutdown) + phydev->drv->shutdown(phydev); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/realtek.c b/qemu/roms/u-boot/drivers/net/phy/realtek.c new file mode 100644 index 000000000..a3ace6852 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/realtek.c @@ -0,0 +1,141 @@ +/* + * RealTek PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * author Andy Fleming + */ +#include +#include +#include + +#define PHY_AUTONEGOTIATE_TIMEOUT 5000 + +/* RTL8211x PHY Status Register */ +#define MIIM_RTL8211x_PHY_STATUS 0x11 +#define MIIM_RTL8211x_PHYSTAT_SPEED 0xc000 +#define MIIM_RTL8211x_PHYSTAT_GBIT 0x8000 +#define MIIM_RTL8211x_PHYSTAT_100 0x4000 +#define MIIM_RTL8211x_PHYSTAT_DUPLEX 0x2000 +#define MIIM_RTL8211x_PHYSTAT_SPDDONE 0x0800 +#define MIIM_RTL8211x_PHYSTAT_LINK 0x0400 + + +/* RealTek RTL8211x */ +static int rtl8211x_config(struct phy_device *phydev) +{ + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); + + genphy_config_aneg(phydev); + + return 0; +} + +static int rtl8211x_parse_status(struct phy_device *phydev) +{ + unsigned int speed; + unsigned int mii_reg; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_STATUS); + + if (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) { + int i = 0; + + /* in case of timeout ->link is cleared */ + phydev->link = 1; + puts("Waiting for PHY realtime link"); + while (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) { + /* Timeout reached ? */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts(" TIMEOUT !\n"); + phydev->link = 0; + break; + } + + if ((i++ % 1000) == 0) + putc('.'); + udelay(1000); /* 1 ms */ + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, + MIIM_RTL8211x_PHY_STATUS); + } + puts(" done\n"); + udelay(500000); /* another 500 ms (results in faster booting) */ + } else { + if (mii_reg & MIIM_RTL8211x_PHYSTAT_LINK) + phydev->link = 1; + else + phydev->link = 0; + } + + if (mii_reg & MIIM_RTL8211x_PHYSTAT_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + speed = (mii_reg & MIIM_RTL8211x_PHYSTAT_SPEED); + + switch (speed) { + case MIIM_RTL8211x_PHYSTAT_GBIT: + phydev->speed = SPEED_1000; + break; + case MIIM_RTL8211x_PHYSTAT_100: + phydev->speed = SPEED_100; + break; + default: + phydev->speed = SPEED_10; + } + + return 0; +} + +static int rtl8211x_startup(struct phy_device *phydev) +{ + /* Read the Status (2x to make sure link is right) */ + genphy_update_link(phydev); + rtl8211x_parse_status(phydev); + + return 0; +} + +/* Support for RTL8211B PHY */ +static struct phy_driver RTL8211B_driver = { + .name = "RealTek RTL8211B", + .uid = 0x1cc910, + .mask = 0xffffff, + .features = PHY_GBIT_FEATURES, + .config = &rtl8211x_config, + .startup = &rtl8211x_startup, + .shutdown = &genphy_shutdown, +}; + +/* Support for RTL8211E-VB-CG, RTL8211E-VL-CG and RTL8211EG-VB-CG PHYs */ +static struct phy_driver RTL8211E_driver = { + .name = "RealTek RTL8211E", + .uid = 0x1cc915, + .mask = 0xffffff, + .features = PHY_GBIT_FEATURES, + .config = &rtl8211x_config, + .startup = &rtl8211x_startup, + .shutdown = &genphy_shutdown, +}; + +/* Support for RTL8211DN PHY */ +static struct phy_driver RTL8211DN_driver = { + .name = "RealTek RTL8211DN", + .uid = 0x1cc914, + .mask = 0xffffff, + .features = PHY_GBIT_FEATURES, + .config = &rtl8211x_config, + .startup = &rtl8211x_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_realtek_init(void) +{ + phy_register(&RTL8211B_driver); + phy_register(&RTL8211E_driver); + phy_register(&RTL8211DN_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/smsc.c b/qemu/roms/u-boot/drivers/net/phy/smsc.c new file mode 100644 index 000000000..bfd9815ab --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/smsc.c @@ -0,0 +1,79 @@ +/* + * SMSC PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Base code from drivers/net/phy/davicom.c + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * author Andy Fleming + * + * Some code copied from linux kernel + * Copyright (c) 2006 Herbert Valerio Riedel + */ +#include + +/* This code does not check the partner abilities. */ +static int smsc_parse_status(struct phy_device *phydev) +{ + int mii_reg; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + + if (mii_reg & (BMSR_100FULL | BMSR_100HALF)) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + + if (mii_reg & (BMSR_10FULL | BMSR_100FULL)) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + return 0; +} + +static int smsc_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + smsc_parse_status(phydev); + return 0; +} + +static struct phy_driver lan8700_driver = { + .name = "SMSC LAN8700", + .uid = 0x0007c0c0, + .mask = 0xffff0, + .features = PHY_BASIC_FEATURES, + .config = &genphy_config_aneg, + .startup = &smsc_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver lan911x_driver = { + .name = "SMSC LAN911x Internal PHY", + .uid = 0x0007c0d0, + .mask = 0xffff0, + .features = PHY_BASIC_FEATURES, + .config = &genphy_config_aneg, + .startup = &smsc_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver lan8710_driver = { + .name = "SMSC LAN8710/LAN8720", + .uid = 0x0007c0f0, + .mask = 0xffff0, + .features = PHY_BASIC_FEATURES, + .config = &genphy_config_aneg, + .startup = &genphy_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_smsc_init(void) +{ + phy_register(&lan8710_driver); + phy_register(&lan911x_driver); + phy_register(&lan8700_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/teranetics.c b/qemu/roms/u-boot/drivers/net/phy/teranetics.c new file mode 100644 index 000000000..93d5ac3d1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/teranetics.c @@ -0,0 +1,112 @@ +/* + * Teranetics PHY drivers + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * author Andy Fleming + */ +#include +#include +#include + +#ifndef CONFIG_PHYLIB_10G +#error The Teranetics PHY needs 10G support +#endif + +int tn2020_config(struct phy_device *phydev) +{ + if (phydev->port == PORT_FIBRE) { + unsigned short restart_an = (MDIO_AN_CTRL1_RESTART | + MDIO_AN_CTRL1_ENABLE | + MDIO_AN_CTRL1_XNP); + u8 phy_hwversion; + + /* + * bit 15:12 of register 30.32 indicates PHY hardware + * version. It can be used to distinguish TN80xx from + * TN2020. TN2020 needs write 0x2 to 30.93, but TN80xx + * needs 0x1. + */ + phy_hwversion = (phy_read(phydev, 30, 32) >> 12) & 0xf; + if (phy_hwversion <= 3) { + phy_write(phydev, 30, 93, 2); + phy_write(phydev, MDIO_MMD_AN, MDIO_CTRL1, restart_an); + } else { + phy_write(phydev, 30, 93, 1); + } + } + + return 0; +} + +int tn2020_startup(struct phy_device *phydev) +{ + unsigned int timeout = 5 * 1000; /* 5 second timeout */ + +#define MDIO_PHYXS_LANE_READY (MDIO_PHYXS_LNSTAT_SYNC0 | \ + MDIO_PHYXS_LNSTAT_SYNC1 | \ + MDIO_PHYXS_LNSTAT_SYNC2 | \ + MDIO_PHYXS_LNSTAT_SYNC3 | \ + MDIO_PHYXS_LNSTAT_ALIGN) + + /* + * Wait for the XAUI-SERDES lanes to align first. Under normal + * circumstances, this can take up to three seconds. + */ + while (--timeout) { + int reg = phy_read(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_LNSTAT); + if (reg < 0) { + printf("TN2020: Error reading from PHY at " + "address %u\n", phydev->addr); + break; + } + if ((reg & MDIO_PHYXS_LANE_READY) == MDIO_PHYXS_LANE_READY) + break; + udelay(1000); + } + if (!timeout) { + /* + * A timeout is bad, but it may not be fatal, so don't + * return an error. Display a warning instead. + */ + printf("TN2020: Timeout waiting for PHY at address %u to " + "align.\n", phydev->addr); + } + + if (phydev->port != PORT_FIBRE) + return gen10g_startup(phydev); + + /* + * The TN2020 only pretends to support fiber. + * It works, but it doesn't look like it works, + * so the link status reports no link. + */ + phydev->link = 1; + + /* For now just lie and say it's 10G all the time */ + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + + return 0; +} + +struct phy_driver tn2020_driver = { + .name = "Teranetics TN2020", + .uid = PHY_UID_TN2020, + .mask = 0xfffffff0, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | + MDIO_DEVS_PHYXS | MDIO_DEVS_AN | + MDIO_DEVS_VEND1 | MDIO_DEVS_VEND2), + .config = &tn2020_config, + .startup = &tn2020_startup, + .shutdown = &gen10g_shutdown, +}; + +int phy_teranetics_init(void) +{ + phy_register(&tn2020_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/phy/vitesse.c b/qemu/roms/u-boot/drivers/net/phy/vitesse.c new file mode 100644 index 000000000..3a55d271a --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/phy/vitesse.c @@ -0,0 +1,373 @@ +/* + * Vitesse PHY drivers + * + * Copyright 2010-2012 Freescale Semiconductor, Inc. + * Author: Andy Fleming + * Add vsc8662 phy support - Priyanka Jain + * SPDX-License-Identifier: GPL-2.0+ + */ +#include + +/* Cicada Auxiliary Control/Status Register */ +#define MIIM_CIS82xx_AUX_CONSTAT 0x1c +#define MIIM_CIS82xx_AUXCONSTAT_INIT 0x0004 +#define MIIM_CIS82xx_AUXCONSTAT_DUPLEX 0x0020 +#define MIIM_CIS82xx_AUXCONSTAT_SPEED 0x0018 +#define MIIM_CIS82xx_AUXCONSTAT_GBIT 0x0010 +#define MIIM_CIS82xx_AUXCONSTAT_100 0x0008 + +/* Cicada Extended Control Register 1 */ +#define MIIM_CIS82xx_EXT_CON1 0x17 +#define MIIM_CIS8201_EXTCON1_INIT 0x0000 + +/* Cicada 8204 Extended PHY Control Register 1 */ +#define MIIM_CIS8204_EPHY_CON 0x17 +#define MIIM_CIS8204_EPHYCON_INIT 0x0006 +#define MIIM_CIS8204_EPHYCON_RGMII 0x1100 + +/* Cicada 8204 Serial LED Control Register */ +#define MIIM_CIS8204_SLED_CON 0x1b +#define MIIM_CIS8204_SLEDCON_INIT 0x1115 + +/* Vitesse VSC8601 Extended PHY Control Register 1 */ +#define MIIM_VSC8601_EPHY_CON 0x17 +#define MIIM_VSC8601_EPHY_CON_INIT_SKEW 0x1120 +#define MIIM_VSC8601_SKEW_CTRL 0x1c + +#define PHY_EXT_PAGE_ACCESS 0x1f +#define PHY_EXT_PAGE_ACCESS_GENERAL 0x10 +#define PHY_EXT_PAGE_ACCESS_EXTENDED3 0x3 + +/* Vitesse VSC8574 control register */ +#define MIIM_VSC8574_MAC_SERDES_CON 0x10 +#define MIIM_VSC8574_MAC_SERDES_ANEG 0x80 +#define MIIM_VSC8574_GENERAL18 0x12 +#define MIIM_VSC8574_GENERAL19 0x13 + +/* Vitesse VSC8574 gerenal purpose register 18 */ +#define MIIM_VSC8574_18G_SGMII 0x80f0 +#define MIIM_VSC8574_18G_QSGMII 0x80e0 +#define MIIM_VSC8574_18G_CMDSTAT 0x8000 + +/* Vitesse VSC8514 control register */ +#define MIIM_VSC8514_GENERAL18 0x12 +#define MIIM_VSC8514_GENERAL19 0x13 +#define MIIM_VSC8514_GENERAL23 0x17 + +/* Vitesse VSC8514 gerenal purpose register 18 */ +#define MIIM_VSC8514_18G_QSGMII 0x80e0 +#define MIIM_VSC8514_18G_CMDSTAT 0x8000 + +/* CIS8201 */ +static int vitesse_config(struct phy_device *phydev) +{ + /* Override PHY config settings */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_AUX_CONSTAT, + MIIM_CIS82xx_AUXCONSTAT_INIT); + /* Set up the interface mode */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_EXT_CON1, + MIIM_CIS8201_EXTCON1_INIT); + + genphy_config_aneg(phydev); + + return 0; +} + +static int vitesse_parse_status(struct phy_device *phydev) +{ + int speed; + int mii_reg; + + mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_AUX_CONSTAT); + + if (mii_reg & MIIM_CIS82xx_AUXCONSTAT_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + speed = mii_reg & MIIM_CIS82xx_AUXCONSTAT_SPEED; + switch (speed) { + case MIIM_CIS82xx_AUXCONSTAT_GBIT: + phydev->speed = SPEED_1000; + break; + case MIIM_CIS82xx_AUXCONSTAT_100: + phydev->speed = SPEED_100; + break; + default: + phydev->speed = SPEED_10; + break; + } + + return 0; +} + +static int vitesse_startup(struct phy_device *phydev) +{ + genphy_update_link(phydev); + vitesse_parse_status(phydev); + + return 0; +} + +static int cis8204_config(struct phy_device *phydev) +{ + /* Override PHY config settings */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_AUX_CONSTAT, + MIIM_CIS82xx_AUXCONSTAT_INIT); + + genphy_config_aneg(phydev); + + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)) + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS8204_EPHY_CON, + MIIM_CIS8204_EPHYCON_INIT | + MIIM_CIS8204_EPHYCON_RGMII); + else + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS8204_EPHY_CON, + MIIM_CIS8204_EPHYCON_INIT); + + return 0; +} + +/* Vitesse VSC8601 */ +static int vsc8601_config(struct phy_device *phydev) +{ + /* Configure some basic stuff */ +#ifdef CONFIG_SYS_VSC8601_SKEWFIX + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8601_EPHY_CON, + MIIM_VSC8601_EPHY_CON_INIT_SKEW); +#if defined(CONFIG_SYS_VSC8601_SKEW_TX) && defined(CONFIG_SYS_VSC8601_SKEW_RX) + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 1); +#define VSC8101_SKEW \ + ((CONFIG_SYS_VSC8601_SKEW_TX << 14) \ + | (CONFIG_SYS_VSC8601_SKEW_RX << 12)) + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8601_SKEW_CTRL, + VSC8101_SKEW); + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0); +#endif +#endif + + genphy_config_aneg(phydev); + + return 0; +} + +static int vsc8574_config(struct phy_device *phydev) +{ + u32 val; + /* configure register 19G for MAC */ + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, + PHY_EXT_PAGE_ACCESS_GENERAL); + + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL19); + if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) { + /* set bit 15:14 to '01' for QSGMII mode */ + val = (val & 0x3fff) | (1 << 14); + phy_write(phydev, MDIO_DEVAD_NONE, + MIIM_VSC8574_GENERAL19, val); + /* Enable 4 ports MAC QSGMII */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18, + MIIM_VSC8574_18G_QSGMII); + } else { + /* set bit 15:14 to '00' for SGMII mode */ + val = val & 0x3fff; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL19, val); + /* Enable 4 ports MAC SGMII */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18, + MIIM_VSC8574_18G_SGMII); + } + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18); + /* When bit 15 is cleared the command has completed */ + while (val & MIIM_VSC8574_18G_CMDSTAT) + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18); + + /* Enable Serdes Auto-negotiation */ + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, + PHY_EXT_PAGE_ACCESS_EXTENDED3); + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_MAC_SERDES_CON); + val = val | MIIM_VSC8574_MAC_SERDES_ANEG; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_MAC_SERDES_CON, val); + + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0); + + genphy_config_aneg(phydev); + + return 0; +} + +static int vsc8514_config(struct phy_device *phydev) +{ + u32 val; + int timeout = 1000000; + + /* configure register to access 19G */ + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, + PHY_EXT_PAGE_ACCESS_GENERAL); + + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL19); + if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) { + /* set bit 15:14 to '01' for QSGMII mode */ + val = (val & 0x3fff) | (1 << 14); + phy_write(phydev, MDIO_DEVAD_NONE, + MIIM_VSC8514_GENERAL19, val); + /* Enable 4 ports MAC QSGMII */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL18, + MIIM_VSC8514_18G_QSGMII); + } else { + /*TODO Add SGMII functionality once spec sheet + * for VSC8514 defines complete functionality + */ + } + + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL18); + /* When bit 15 is cleared the command has completed */ + while ((val & MIIM_VSC8514_18G_CMDSTAT) && timeout--) + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL18); + + if (0 == timeout) { + printf("PHY 8514 config failed\n"); + return -1; + } + + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0); + + /* configure register to access 23 */ + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL23); + /* set bits 10:8 to '000' */ + val = (val & 0xf8ff); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8514_GENERAL23, val); + + genphy_config_aneg(phydev); + + return 0; +} + +static struct phy_driver VSC8211_driver = { + .name = "Vitesse VSC8211", + .uid = 0xfc4b0, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &vitesse_config, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver VSC8221_driver = { + .name = "Vitesse VSC8221", + .uid = 0xfc550, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &genphy_config_aneg, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver VSC8244_driver = { + .name = "Vitesse VSC8244", + .uid = 0xfc6c0, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &genphy_config_aneg, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver VSC8234_driver = { + .name = "Vitesse VSC8234", + .uid = 0xfc620, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &genphy_config_aneg, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver VSC8574_driver = { + .name = "Vitesse VSC8574", + .uid = 0x704a0, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &vsc8574_config, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver VSC8514_driver = { + .name = "Vitesse VSC8514", + .uid = 0x70670, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &vsc8514_config, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver VSC8601_driver = { + .name = "Vitesse VSC8601", + .uid = 0x70420, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &vsc8601_config, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver VSC8641_driver = { + .name = "Vitesse VSC8641", + .uid = 0x70430, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &genphy_config_aneg, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver VSC8662_driver = { + .name = "Vitesse VSC8662", + .uid = 0x70660, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &genphy_config_aneg, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +/* Vitesse bought Cicada, so we'll put these here */ +static struct phy_driver cis8201_driver = { + .name = "CIS8201", + .uid = 0xfc410, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &vitesse_config, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +static struct phy_driver cis8204_driver = { + .name = "Cicada Cis8204", + .uid = 0xfc440, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &cis8204_config, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_vitesse_init(void) +{ + phy_register(&VSC8641_driver); + phy_register(&VSC8601_driver); + phy_register(&VSC8234_driver); + phy_register(&VSC8244_driver); + phy_register(&VSC8211_driver); + phy_register(&VSC8221_driver); + phy_register(&VSC8574_driver); + phy_register(&VSC8514_driver); + phy_register(&VSC8662_driver); + phy_register(&cis8201_driver); + phy_register(&cis8204_driver); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/plb2800_eth.c b/qemu/roms/u-boot/drivers/net/plb2800_eth.c new file mode 100644 index 000000000..f869514f2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/plb2800_eth.c @@ -0,0 +1,373 @@ +/* + * PLB2800 internal switch ethernet driver. + * + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + + +#define NUM_RX_DESC PKTBUFSRX +#define TOUT_LOOP 1000000 + +#define LONG_REF(addr) (*((volatile unsigned long*)addr)) + +#define CMAC_CRX_CTRL LONG_REF(0xb800c870) +#define CMAC_CTX_CTRL LONG_REF(0xb800c874) +#define SYS_MAC_ADDR_0 LONG_REF(0xb800c878) +#define SYS_MAC_ADDR_1 LONG_REF(0xb800c87c) +#define MIPS_H_MASK LONG_REF(0xB800C810) + +#define MA_LEARN LONG_REF(0xb8008004) +#define DA_LOOKUP LONG_REF(0xb8008008) + +#define CMAC_CRX_CTRL_PD 0x00000001 +#define CMAC_CRX_CTRL_CG 0x00000002 +#define CMAC_CRX_CTRL_PL_SHIFT 2 +#define CMAC_CRIT 0x0 +#define CMAC_NON_CRIT 0x1 +#define MBOX_STAT_ID_SHF 28 +#define MBOX_STAT_CP 0x80000000 +#define MBOX_STAT_MB 0x00000001 +#define EN_MA_LEARN 0x02000000 +#define EN_DA_LKUP 0x01000000 +#define MA_DEST_SHF 11 +#define DA_DEST_SHF 11 +#define DA_STATE_SHF 19 +#define TSTAMP_MS 0x00000000 +#define SW_H_MBOX4_MASK 0x08000000 +#define SW_H_MBOX3_MASK 0x04000000 +#define SW_H_MBOX2_MASK 0x02000000 +#define SW_H_MBOX1_MASK 0x01000000 + +typedef volatile struct { + unsigned int stat; + unsigned int cmd; + unsigned int cnt; + unsigned int adr; +} mailbox_t; + +#define MBOX_REG(mb) ((mailbox_t*)(0xb800c830+(mb<<4))) + +typedef volatile struct { + unsigned int word0; + unsigned int word1; + unsigned int word2; +} mbhdr_t; + +#define MBOX_MEM(mb) ((void*)(0xb800a000+((3-mb)<<11))) + + +static int plb2800_eth_init(struct eth_device *dev, bd_t * bis); +static int plb2800_eth_send(struct eth_device *dev, void *packet, int length); +static int plb2800_eth_recv(struct eth_device *dev); +static void plb2800_eth_halt(struct eth_device *dev); + +static void plb2800_set_mac_addr(struct eth_device *dev, unsigned char * addr); +static unsigned char * plb2800_get_mac_addr(void); + +static int rx_new; +static int mac_addr_set = 0; + + +int plb2800_eth_initialize(bd_t * bis) +{ + struct eth_device *dev; + ulong temp; + +#ifdef DEBUG + printf("Entered plb2800_eth_initialize()\n"); +#endif + + if (!(dev = (struct eth_device *) malloc (sizeof *dev))) + { + printf("Failed to allocate memory\n"); + return -1; + } + memset(dev, 0, sizeof(*dev)); + + sprintf(dev->name, "PLB2800 Switch"); + dev->init = plb2800_eth_init; + dev->halt = plb2800_eth_halt; + dev->send = plb2800_eth_send; + dev->recv = plb2800_eth_recv; + + eth_register(dev); + + /* bug fix */ + *(ulong *)0xb800e800 = 0x838; + + /* Set MBOX ownership */ + temp = CMAC_CRIT << MBOX_STAT_ID_SHF; + MBOX_REG(0)->stat = temp; + MBOX_REG(1)->stat = temp; + + temp = CMAC_NON_CRIT << MBOX_STAT_ID_SHF; + MBOX_REG(2)->stat = temp; + MBOX_REG(3)->stat = temp; + + plb2800_set_mac_addr(dev, plb2800_get_mac_addr()); + + /* Disable all Mbox interrupt */ + temp = MIPS_H_MASK; + temp &= ~ (SW_H_MBOX1_MASK | SW_H_MBOX2_MASK | SW_H_MBOX3_MASK | SW_H_MBOX4_MASK) ; + MIPS_H_MASK = temp; + +#ifdef DEBUG + printf("Leaving plb2800_eth_initialize()\n"); +#endif + + return 0; +} + +static int plb2800_eth_init(struct eth_device *dev, bd_t * bis) +{ +#ifdef DEBUG + printf("Entering plb2800_eth_init()\n"); +#endif + + plb2800_set_mac_addr(dev, dev->enetaddr); + + rx_new = 0; + +#ifdef DEBUG + printf("Leaving plb2800_eth_init()\n"); +#endif + + return 0; +} + + +static int plb2800_eth_send(struct eth_device *dev, void *packet, int length) +{ + int i; + int res = -1; + u32 temp; + mailbox_t * mb = MBOX_REG(0); + char * mem = MBOX_MEM(0); + +#ifdef DEBUG + printf("Entered plb2800_eth_send()\n"); +#endif + + if (length <= 0) + { + printf ("%s: bad packet size: %d\n", dev->name, length); + goto Done; + } + + if (length < 64) + { + length = 64; + } + + temp = CMAC_CRX_CTRL_CG | ((length + 4) << CMAC_CRX_CTRL_PL_SHIFT); + +#ifdef DEBUG + printf("0 mb->stat = 0x%x\n", mb->stat); +#endif + + for(i = 0; mb->stat & (MBOX_STAT_CP | MBOX_STAT_MB); i++) + { + if (i >= TOUT_LOOP) + { + printf("%s: tx buffer not ready\n", dev->name); + printf("1 mb->stat = 0x%x\n", mb->stat); + goto Done; + } + } + + /* For some strange reason, memcpy doesn't work, here! + */ + do + { + int words = (length >> 2) + 1; + unsigned int* dst = (unsigned int*)(mem); + unsigned int* src = (unsigned int*)(packet); + for (i = 0; i < words; i++) + { + *dst = *src; + dst++; + src++; + }; + } while(0); + + CMAC_CRX_CTRL = temp; + mb->cmd = MBOX_STAT_CP; + +#ifdef DEBUG + printf("2 mb->stat = 0x%x\n", mb->stat); +#endif + + res = length; +Done: + +#ifdef DEBUG + printf("Leaving plb2800_eth_send()\n"); +#endif + + return res; +} + + +static int plb2800_eth_recv(struct eth_device *dev) +{ + int length = 0; + mailbox_t * mbox = MBOX_REG(3); + unsigned char * hdr = MBOX_MEM(3); + unsigned int stat; + +#ifdef DEBUG + printf("Entered plb2800_eth_recv()\n"); +#endif + + for (;;) + { + stat = mbox->stat; + + if (!(stat & MBOX_STAT_CP)) + { + break; + } + + length = ((*(hdr + 6) & 0x3f) << 8) + *(hdr + 7); + memcpy((void *)NetRxPackets[rx_new], hdr + 12, length); + + stat &= ~MBOX_STAT_CP; + mbox->stat = stat; +#ifdef DEBUG + { + int i; + for (i=0;i +#include +#include +#include +#include +#include + +#define RTL_TIMEOUT 100000 + +#define ETH_FRAME_LEN 1514 +#define ETH_ALEN 6 +#define ETH_ZLEN 60 + +/* PCI Tuning Parameters + Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ +#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */ +#define TX_DMA_BURST 4 /* Calculate as 16<priv, a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) + +/* Symbolic offsets to registers. */ +enum RTL8139_registers { + MAC0=0, /* Ethernet hardware address. */ + MAR0=8, /* Multicast filter. */ + TxStatus0=0x10, /* Transmit status (four 32bit registers). */ + TxAddr0=0x20, /* Tx descriptors (also four 32bit). */ + RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36, + ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A, + IntrMask=0x3C, IntrStatus=0x3E, + TxConfig=0x40, RxConfig=0x44, + Timer=0x48, /* general-purpose counter. */ + RxMissed=0x4C, /* 24 bits valid, write clears. */ + Cfg9346=0x50, Config0=0x51, Config1=0x52, + TimerIntrReg=0x54, /* intr if gp counter reaches this value */ + MediaStatus=0x58, + Config3=0x59, + MultiIntr=0x5C, + RevisionID=0x5E, /* revision of the RTL8139 chip */ + TxSummary=0x60, + MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, + NWayExpansion=0x6A, + DisconnectCnt=0x6C, FalseCarrierCnt=0x6E, + NWayTestReg=0x70, + RxCnt=0x72, /* packet received counter */ + CSCR=0x74, /* chip status and configuration register */ + PhyParm1=0x78,TwisterParm=0x7c,PhyParm2=0x80, /* undocumented */ + /* from 0x84 onwards are a number of power management/wakeup frame + * definitions we will probably never need to know about. */ +}; + +enum ChipCmdBits { + CmdReset=0x10, CmdRxEnb=0x08, CmdTxEnb=0x04, RxBufEmpty=0x01, }; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { + PCIErr=0x8000, PCSTimeout=0x4000, CableLenChange= 0x2000, + RxFIFOOver=0x40, RxUnderrun=0x20, RxOverflow=0x10, + TxErr=0x08, TxOK=0x04, RxErr=0x02, RxOK=0x01, +}; +enum TxStatusBits { + TxHostOwns=0x2000, TxUnderrun=0x4000, TxStatOK=0x8000, + TxOutOfWindow=0x20000000, TxAborted=0x40000000, + TxCarrierLost=0x80000000, +}; +enum RxStatusBits { + RxMulticast=0x8000, RxPhysical=0x4000, RxBroadcast=0x2000, + RxBadSymbol=0x0020, RxRunt=0x0010, RxTooLong=0x0008, RxCRCErr=0x0004, + RxBadAlign=0x0002, RxStatusOK=0x0001, +}; + +enum MediaStatusBits { + MSRTxFlowEnable=0x80, MSRRxFlowEnable=0x40, MSRSpeed10=0x08, + MSRLinkFail=0x04, MSRRxPauseFlag=0x02, MSRTxPauseFlag=0x01, +}; + +enum MIIBMCRBits { + BMCRReset=0x8000, BMCRSpeed100=0x2000, BMCRNWayEnable=0x1000, + BMCRRestartNWay=0x0200, BMCRDuplex=0x0100, +}; + +enum CSCRBits { + CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800, + CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0, + CSCR_LinkDownCmd=0x0f3c0, +}; + +/* Bits in RxConfig. */ +enum rx_mode_bits { + RxCfgWrap=0x80, + AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08, + AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01, +}; + +static int ioaddr; +static unsigned int cur_rx,cur_tx; + +/* The RTL8139 can only transmit from a contiguous, aligned memory block. */ +static unsigned char tx_buffer[TX_BUF_SIZE] __attribute__((aligned(4))); +static unsigned char rx_ring[RX_BUF_LEN+16] __attribute__((aligned(4))); + +static int rtl8139_probe(struct eth_device *dev, bd_t *bis); +static int read_eeprom(int location, int addr_len); +static void rtl_reset(struct eth_device *dev); +static int rtl_transmit(struct eth_device *dev, void *packet, int length); +static int rtl_poll(struct eth_device *dev); +static void rtl_disable(struct eth_device *dev); +#ifdef CONFIG_MCAST_TFTP/* This driver already accepts all b/mcast */ +static int rtl_bcast_addr(struct eth_device *dev, const u8 *bcast_mac, u8 set) +{ + return (0); +} +#endif + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139}, + {PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_8139}, + {} +}; + +int rtl8139_initialize(bd_t *bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase; + int idx=0; + + while(1){ + /* Find RTL8139 */ + if ((devno = pci_find_devices(supported, idx++)) < 0) + break; + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); + iobase &= ~0xf; + + debug ("rtl8139: REALTEK RTL8139 @0x%x\n", iobase); + + dev = (struct eth_device *)malloc(sizeof *dev); + if (!dev) { + printf("Can not allocate memory of rtl8139\n"); + break; + } + memset(dev, 0, sizeof(*dev)); + + sprintf (dev->name, "RTL8139#%d", card_number); + + dev->priv = (void *) devno; + dev->iobase = (int)bus_to_phys(iobase); + dev->init = rtl8139_probe; + dev->halt = rtl_disable; + dev->send = rtl_transmit; + dev->recv = rtl_poll; +#ifdef CONFIG_MCAST_TFTP + dev->mcast = rtl_bcast_addr; +#endif + + eth_register (dev); + + card_number++; + + pci_write_config_byte (devno, PCI_LATENCY_TIMER, 0x20); + + udelay (10 * 1000); + } + + return card_number; +} + +static int rtl8139_probe(struct eth_device *dev, bd_t *bis) +{ + int i; + int addr_len; + unsigned short *ap = (unsigned short *)dev->enetaddr; + + ioaddr = dev->iobase; + + /* Bring the chip out of low-power mode. */ + outb(0x00, ioaddr + Config1); + + addr_len = read_eeprom(0,8) == 0x8129 ? 8 : 6; + for (i = 0; i < 3; i++) + *ap++ = le16_to_cpu (read_eeprom(i + 7, addr_len)); + + rtl_reset(dev); + + if (inb(ioaddr + MediaStatus) & MSRLinkFail) { + printf("Cable not connected or other link failure\n"); + return -1 ; + } + + return 0; +} + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* + Delay between EEPROM clock transitions. + No extra delay is needed with 33MHz PCI, but 66MHz may change this. +*/ + +#define eeprom_delay() inl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +static int read_eeprom(int location, int addr_len) +{ + int i; + unsigned int retval = 0; + long ee_addr = ioaddr + Cfg9346; + int read_cmd = location | (EE_READ_CMD << addr_len); + + outb(EE_ENB & ~EE_CS, ee_addr); + outb(EE_ENB, ee_addr); + eeprom_delay(); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outb(EE_ENB | dataval, ee_addr); + eeprom_delay(); + outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + } + outb(EE_ENB, ee_addr); + eeprom_delay(); + + for (i = 16; i > 0; i--) { + outb(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); + outb(EE_ENB, ee_addr); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outb(~EE_CS, ee_addr); + eeprom_delay(); + return retval; +} + +static const unsigned int rtl8139_rx_config = + (RX_BUF_LEN_IDX << 11) | + (RX_FIFO_THRESH << 13) | + (RX_DMA_BURST << 8); + +static void set_rx_mode(struct eth_device *dev) { + unsigned int mc_filter[2]; + int rx_mode; + /* !IFF_PROMISC */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + + outl(rtl8139_rx_config | rx_mode, ioaddr + RxConfig); + + outl(mc_filter[0], ioaddr + MAR0 + 0); + outl(mc_filter[1], ioaddr + MAR0 + 4); +} + +static void rtl_reset(struct eth_device *dev) +{ + int i; + + outb(CmdReset, ioaddr + ChipCmd); + + cur_rx = 0; + cur_tx = 0; + + /* Give the chip 10ms to finish the reset. */ + for (i=0; i<100; ++i){ + if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) break; + udelay (100); /* wait 100us */ + } + + + for (i = 0; i < ETH_ALEN; i++) + outb(dev->enetaddr[i], ioaddr + MAC0 + i); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + outl((RX_FIFO_THRESH<<13) | (RX_BUF_LEN_IDX<<11) | (RX_DMA_BURST<<8), + ioaddr + RxConfig); /* accept no frames yet! */ + outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig); + + /* The Linux driver changes Config1 here to use a different LED pattern + * for half duplex or full/autodetect duplex (for full/autodetect, the + * outputs are TX/RX, Link10/100, FULL, while for half duplex it uses + * TX/RX, Link100, Link10). This is messy, because it doesn't match + * the inscription on the mounting bracket. It should not be changed + * from the configuration EEPROM default, because the card manufacturer + * should have set that to match the card. */ + + debug_cond(DEBUG_RX, + "rx ring address is %lX\n",(unsigned long)rx_ring); + flush_cache((unsigned long)rx_ring, RX_BUF_LEN); + outl(phys_to_bus((int)rx_ring), ioaddr + RxBuf); + + /* If we add multicast support, the MAR0 register would have to be + * initialized to 0xffffffffffffffff (two 32 bit accesses). Etherboot + * only needs broadcast (for ARP/RARP/BOOTP/DHCP) and unicast. */ + + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + + outl(rtl8139_rx_config, ioaddr + RxConfig); + + /* Start the chip's Tx and Rx process. */ + outl(0, ioaddr + RxMissed); + + /* set_rx_mode */ + set_rx_mode(dev); + + /* Disable all known interrupts by setting the interrupt mask. */ + outw(0, ioaddr + IntrMask); +} + +static int rtl_transmit(struct eth_device *dev, void *packet, int length) +{ + unsigned int status; + unsigned long txstatus; + unsigned int len = length; + int i = 0; + + ioaddr = dev->iobase; + + memcpy((char *)tx_buffer, (char *)packet, (int)length); + + debug_cond(DEBUG_TX, "sending %d bytes\n", len); + + /* Note: RTL8139 doesn't auto-pad, send minimum payload (another 4 + * bytes are sent automatically for the FCS, totalling to 64 bytes). */ + while (len < ETH_ZLEN) { + tx_buffer[len++] = '\0'; + } + + flush_cache((unsigned long)tx_buffer, length); + outl(phys_to_bus((int)tx_buffer), ioaddr + TxAddr0 + cur_tx*4); + outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | len, + ioaddr + TxStatus0 + cur_tx*4); + + do { + status = inw(ioaddr + IntrStatus); + /* Only acknlowledge interrupt sources we can properly handle + * here - the RxOverflow/RxFIFOOver MUST be handled in the + * rtl_poll() function. */ + outw(status & (TxOK | TxErr | PCIErr), ioaddr + IntrStatus); + if ((status & (TxOK | TxErr | PCIErr)) != 0) break; + udelay(10); + } while (i++ < RTL_TIMEOUT); + + txstatus = inl(ioaddr + TxStatus0 + cur_tx*4); + + if (status & TxOK) { + cur_tx = (cur_tx + 1) % NUM_TX_DESC; + + debug_cond(DEBUG_TX, + "tx done, status %hX txstatus %lX\n", + status, txstatus); + + return length; + } else { + + debug_cond(DEBUG_TX, + "tx timeout/error (%d usecs), status %hX txstatus %lX\n", + 10*i, status, txstatus); + + rtl_reset(dev); + + return 0; + } +} + +static int rtl_poll(struct eth_device *dev) +{ + unsigned int status; + unsigned int ring_offs; + unsigned int rx_size, rx_status; + int length=0; + + ioaddr = dev->iobase; + + if (inb(ioaddr + ChipCmd) & RxBufEmpty) { + return 0; + } + + status = inw(ioaddr + IntrStatus); + /* See below for the rest of the interrupt acknowledges. */ + outw(status & ~(RxFIFOOver | RxOverflow | RxOK), ioaddr + IntrStatus); + + debug_cond(DEBUG_RX, "rtl_poll: int %hX ", status); + + ring_offs = cur_rx % RX_BUF_LEN; + /* ring_offs is guaranteed being 4-byte aligned */ + rx_status = le32_to_cpu(*(unsigned int *)(rx_ring + ring_offs)); + rx_size = rx_status >> 16; + rx_status &= 0xffff; + + if ((rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) || + (rx_size < ETH_ZLEN) || (rx_size > ETH_FRAME_LEN + 4)) { + printf("rx error %hX\n", rx_status); + rtl_reset(dev); /* this clears all interrupts still pending */ + return 0; + } + + /* Received a good packet */ + length = rx_size - 4; /* no one cares about the FCS */ + if (ring_offs+4+rx_size-4 > RX_BUF_LEN) { + int semi_count = RX_BUF_LEN - ring_offs - 4; + unsigned char rxdata[RX_BUF_LEN]; + + memcpy(rxdata, rx_ring + ring_offs + 4, semi_count); + memcpy(&(rxdata[semi_count]), rx_ring, rx_size-4-semi_count); + + NetReceive(rxdata, length); + debug_cond(DEBUG_RX, "rx packet %d+%d bytes", + semi_count, rx_size-4-semi_count); + } else { + NetReceive(rx_ring + ring_offs + 4, length); + debug_cond(DEBUG_RX, "rx packet %d bytes", rx_size-4); + } + flush_cache((unsigned long)rx_ring, RX_BUF_LEN); + + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; + outw(cur_rx - 16, ioaddr + RxBufPtr); + /* See RTL8139 Programming Guide V0.1 for the official handling of + * Rx overflow situations. The document itself contains basically no + * usable information, except for a few exception handling rules. */ + outw(status & (RxFIFOOver | RxOverflow | RxOK), ioaddr + IntrStatus); + return length; +} + +static void rtl_disable(struct eth_device *dev) +{ + int i; + + ioaddr = dev->iobase; + + /* reset the chip */ + outb(CmdReset, ioaddr + ChipCmd); + + /* Give the chip 10ms to finish the reset. */ + for (i=0; i<100; ++i){ + if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) break; + udelay (100); /* wait 100us */ + } +} diff --git a/qemu/roms/u-boot/drivers/net/rtl8169.c b/qemu/roms/u-boot/drivers/net/rtl8169.c new file mode 100644 index 000000000..d040ab171 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/rtl8169.c @@ -0,0 +1,967 @@ +/* + * rtl8169.c : U-Boot driver for the RealTek RTL8169 + * + * Masami Komiya (mkomiya@sonare.it) + * + * Most part is taken from r8169.c of etherboot + * + */ + +/************************************************************************** +* r8169.c: Etherboot device driver for the RealTek RTL-8169 Gigabit +* Written 2003 by Timothy Legge +* + * SPDX-License-Identifier: GPL-2.0+ +* +* Portions of this code based on: +* r8169.c: A RealTek RTL-8169 Gigabit Ethernet driver +* for Linux kernel 2.4.x. +* +* Written 2002 ShuChen +* See Linux Driver for full information +* +* Linux Driver Version 1.27a, 10.02.2002 +* +* Thanks to: +* Jean Chen of RealTek Semiconductor Corp. for +* providing the evaluation NIC used to develop +* this driver. RealTek's support for Etherboot +* is appreciated. +* +* REVISION HISTORY: +* ================ +* +* v1.0 11-26-2003 timlegge Initial port of Linux driver +* v1.5 01-17-2004 timlegge Initial driver output cleanup +* +* Indent Options: indent -kr -i8 +***************************************************************************/ +/* + * 26 August 2006 Mihai Georgian + * Modified to use le32_to_cpu and cpu_to_le32 properly + */ +#include +#include +#include +#include +#include +#include + +#undef DEBUG_RTL8169 +#undef DEBUG_RTL8169_TX +#undef DEBUG_RTL8169_RX + +#define drv_version "v1.5" +#define drv_date "01-17-2004" + +static u32 ioaddr; + +/* Condensed operations for readability. */ +#define currticks() get_timer(0) + +/* media options */ +#define MAX_UNITS 8 +static int media[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; + +/* MAC address length*/ +#define MAC_ADDR_LEN 6 + +/* max supported gigabit ethernet frame size -- must be at least (dev->mtu+14+4).*/ +#define MAX_ETH_FRAME_SIZE 1536 + +#define TX_FIFO_THRESH 256 /* In bytes */ + +#define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define EarlyTxThld 0x3F /* 0x3F means NO early transmit */ +#define RxPacketMaxSize 0x0800 /* Maximum size supported is 16K-1 */ +#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ + +#define NUM_TX_DESC 1 /* Number of Tx descriptor registers */ +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#define RX_BUF_SIZE 1536 /* Rx Buffer size */ +#define RX_BUF_LEN 8192 + +#define RTL_MIN_IO_SIZE 0x80 +#define TX_TIMEOUT (6*HZ) + +/* write/read MMIO register. Notice: {read,write}[wl] do the necessary swapping */ +#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) +#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg)) +#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg)) +#define RTL_R8(reg) readb (ioaddr + (reg)) +#define RTL_R16(reg) readw (ioaddr + (reg)) +#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg))) + +#define ETH_FRAME_LEN MAX_ETH_FRAME_SIZE +#define ETH_ALEN MAC_ADDR_LEN +#define ETH_ZLEN 60 + +#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, (pci_addr_t)a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, (phys_addr_t)a) + +enum RTL8169_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAR0 = 8, /* Multicast filter. */ + TxDescStartAddrLow = 0x20, + TxDescStartAddrHigh = 0x24, + TxHDescStartAddrLow = 0x28, + TxHDescStartAddrHigh = 0x2c, + FLASH = 0x30, + ERSR = 0x36, + ChipCmd = 0x37, + TxPoll = 0x38, + IntrMask = 0x3C, + IntrStatus = 0x3E, + TxConfig = 0x40, + RxConfig = 0x44, + RxMissed = 0x4C, + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + Config2 = 0x53, + Config3 = 0x54, + Config4 = 0x55, + Config5 = 0x56, + MultiIntr = 0x5C, + PHYAR = 0x60, + TBICSR = 0x64, + TBI_ANAR = 0x68, + TBI_LPAR = 0x6A, + PHYstatus = 0x6C, + RxMaxSize = 0xDA, + CPlusCmd = 0xE0, + RxDescStartAddrLow = 0xE4, + RxDescStartAddrHigh = 0xE8, + EarlyTxThres = 0xEC, + FuncEvent = 0xF0, + FuncEventMask = 0xF4, + FuncPresetState = 0xF8, + FuncForceEvent = 0xFC, +}; + +enum RTL8169_register_content { + /*InterruptStatusBits */ + SYSErr = 0x8000, + PCSTimeout = 0x4000, + SWInt = 0x0100, + TxDescUnavail = 0x80, + RxFIFOOver = 0x40, + RxUnderrun = 0x20, + RxOverflow = 0x10, + TxErr = 0x08, + TxOK = 0x04, + RxErr = 0x02, + RxOK = 0x01, + + /*RxStatusDesc */ + RxRES = 0x00200000, + RxCRC = 0x00080000, + RxRUNT = 0x00100000, + RxRWT = 0x00400000, + + /*ChipCmdBits */ + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, + + /*Cfg9346Bits */ + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xC0, + + /*rx_mode_bits */ + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, + + /*RxConfigBits */ + RxCfgFIFOShift = 13, + RxCfgDMAShift = 8, + + /*TxConfigBits */ + TxInterFrameGapShift = 24, + TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + + /*rtl8169_PHYstatus */ + TBI_Enable = 0x80, + TxFlowCtrl = 0x40, + RxFlowCtrl = 0x20, + _1000bpsF = 0x10, + _100bps = 0x08, + _10bps = 0x04, + LinkStatus = 0x02, + FullDup = 0x01, + + /*GIGABIT_PHY_registers */ + PHY_CTRL_REG = 0, + PHY_STAT_REG = 1, + PHY_AUTO_NEGO_REG = 4, + PHY_1000_CTRL_REG = 9, + + /*GIGABIT_PHY_REG_BIT */ + PHY_Restart_Auto_Nego = 0x0200, + PHY_Enable_Auto_Nego = 0x1000, + + /* PHY_STAT_REG = 1; */ + PHY_Auto_Nego_Comp = 0x0020, + + /* PHY_AUTO_NEGO_REG = 4; */ + PHY_Cap_10_Half = 0x0020, + PHY_Cap_10_Full = 0x0040, + PHY_Cap_100_Half = 0x0080, + PHY_Cap_100_Full = 0x0100, + + /* PHY_1000_CTRL_REG = 9; */ + PHY_Cap_1000_Full = 0x0200, + + PHY_Cap_Null = 0x0, + + /*_MediaType*/ + _10_Half = 0x01, + _10_Full = 0x02, + _100_Half = 0x04, + _100_Full = 0x08, + _1000_Full = 0x10, + + /*_TBICSRBit*/ + TBILinkOK = 0x02000000, +}; + +static struct { + const char *name; + u8 version; /* depend on RTL8169 docs */ + u32 RxConfigMask; /* should clear the bits supported by this chip */ +} rtl_chip_info[] = { + {"RTL-8169", 0x00, 0xff7e1880,}, + {"RTL-8169", 0x04, 0xff7e1880,}, + {"RTL-8169", 0x00, 0xff7e1880,}, + {"RTL-8169s/8110s", 0x02, 0xff7e1880,}, + {"RTL-8169s/8110s", 0x04, 0xff7e1880,}, + {"RTL-8169sb/8110sb", 0x10, 0xff7e1880,}, + {"RTL-8169sc/8110sc", 0x18, 0xff7e1880,}, + {"RTL-8168b/8111sb", 0x30, 0xff7e1880,}, + {"RTL-8168b/8111sb", 0x38, 0xff7e1880,}, + {"RTL-8168d/8111d", 0x28, 0xff7e1880,}, + {"RTL-8168evl/8111evl", 0x2e, 0xff7e1880,}, + {"RTL-8101e", 0x34, 0xff7e1880,}, + {"RTL-8100e", 0x32, 0xff7e1880,}, +}; + +enum _DescStatusBit { + OWNbit = 0x80000000, + EORbit = 0x40000000, + FSbit = 0x20000000, + LSbit = 0x10000000, +}; + +struct TxDesc { + u32 status; + u32 vlan_tag; + u32 buf_addr; + u32 buf_Haddr; +}; + +struct RxDesc { + u32 status; + u32 vlan_tag; + u32 buf_addr; + u32 buf_Haddr; +}; + +/* Define the TX Descriptor */ +static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256]; +/* __attribute__ ((aligned(256))); */ + +/* Create a static buffer of size RX_BUF_SZ for each +TX Descriptor. All descriptors point to a +part of this buffer */ +static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE]; + +/* Define the RX Descriptor */ +static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256]; + /* __attribute__ ((aligned(256))); */ + +/* Create a static buffer of size RX_BUF_SZ for each +RX Descriptor All descriptors point to a +part of this buffer */ +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; + +struct rtl8169_private { + void *mmio_addr; /* memory map physical address */ + int chipset; + unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ + unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ + unsigned long dirty_tx; + unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ + unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ + struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ + struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ + unsigned char *RxBufferRings; /* Index of Rx Buffer */ + unsigned char *RxBufferRing[NUM_RX_DESC]; /* Index of Rx Buffer array */ + unsigned char *Tx_skbuff[NUM_TX_DESC]; +} tpx; + +static struct rtl8169_private *tpc; + +static const u16 rtl8169_intr_mask = + SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | + TxOK | RxErr | RxOK; +static const unsigned int rtl8169_rx_config = + (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_REALTEK, 0x8167}, + {PCI_VENDOR_ID_REALTEK, 0x8168}, + {PCI_VENDOR_ID_REALTEK, 0x8169}, + {} +}; + +void mdio_write(int RegAddr, int value) +{ + int i; + + RTL_W32(PHYAR, 0x80000000 | (RegAddr & 0xFF) << 16 | value); + udelay(1000); + + for (i = 2000; i > 0; i--) { + /* Check if the RTL8169 has completed writing to the specified MII register */ + if (!(RTL_R32(PHYAR) & 0x80000000)) { + break; + } else { + udelay(100); + } + } +} + +int mdio_read(int RegAddr) +{ + int i, value = -1; + + RTL_W32(PHYAR, 0x0 | (RegAddr & 0xFF) << 16); + udelay(1000); + + for (i = 2000; i > 0; i--) { + /* Check if the RTL8169 has completed retrieving data from the specified MII register */ + if (RTL_R32(PHYAR) & 0x80000000) { + value = (int) (RTL_R32(PHYAR) & 0xFFFF); + break; + } else { + udelay(100); + } + } + return value; +} + +static int rtl8169_init_board(struct eth_device *dev) +{ + int i; + u32 tmp; + +#ifdef DEBUG_RTL8169 + printf ("%s\n", __FUNCTION__); +#endif + ioaddr = dev->iobase; + + /* Soft reset the chip. */ + RTL_W8(ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) + if ((RTL_R8(ChipCmd) & CmdReset) == 0) + break; + else + udelay(10); + + /* identify chip attached to board */ + tmp = RTL_R32(TxConfig); + tmp = ((tmp & 0x7c000000) + ((tmp & 0x00800000) << 2)) >> 24; + + for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--){ + if (tmp == rtl_chip_info[i].version) { + tpc->chipset = i; + goto match; + } + } + + /* if unknown chip, assume array element #0, original RTL-8169 in this case */ + printf("PCI device %s: unknown chip version, assuming RTL-8169\n", dev->name); + printf("PCI device: TxConfig = 0x%lX\n", (unsigned long) RTL_R32(TxConfig)); + tpc->chipset = 0; + +match: + return 0; +} + +/* + * Cache maintenance functions. These are simple wrappers around the more + * general purpose flush_cache() and invalidate_dcache_range() functions. + */ + +static void rtl_inval_rx_desc(struct RxDesc *desc) +{ + unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); + unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +} + +static void rtl_flush_rx_desc(struct RxDesc *desc) +{ + flush_cache((unsigned long)desc, sizeof(*desc)); +} + +static void rtl_inval_tx_desc(struct TxDesc *desc) +{ + unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); + unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +} + +static void rtl_flush_tx_desc(struct TxDesc *desc) +{ + flush_cache((unsigned long)desc, sizeof(*desc)); +} + +static void rtl_inval_buffer(void *buf, size_t size) +{ + unsigned long start = (unsigned long)buf & ~(ARCH_DMA_MINALIGN - 1); + unsigned long end = ALIGN(start + size, ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +} + +static void rtl_flush_buffer(void *buf, size_t size) +{ + flush_cache((unsigned long)buf, size); +} + +/************************************************************************** +RECV - Receive a frame +***************************************************************************/ +static int rtl_recv(struct eth_device *dev) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + int cur_rx; + int length = 0; + +#ifdef DEBUG_RTL8169_RX + printf ("%s\n", __FUNCTION__); +#endif + ioaddr = dev->iobase; + + cur_rx = tpc->cur_rx; + + rtl_inval_rx_desc(&tpc->RxDescArray[cur_rx]); + + if ((le32_to_cpu(tpc->RxDescArray[cur_rx].status) & OWNbit) == 0) { + if (!(le32_to_cpu(tpc->RxDescArray[cur_rx].status) & RxRES)) { + unsigned char rxdata[RX_BUF_LEN]; + length = (int) (le32_to_cpu(tpc->RxDescArray[cur_rx]. + status) & 0x00001FFF) - 4; + + rtl_inval_buffer(tpc->RxBufferRing[cur_rx], length); + memcpy(rxdata, tpc->RxBufferRing[cur_rx], length); + NetReceive(rxdata, length); + + if (cur_rx == NUM_RX_DESC - 1) + tpc->RxDescArray[cur_rx].status = + cpu_to_le32((OWNbit | EORbit) + RX_BUF_SIZE); + else + tpc->RxDescArray[cur_rx].status = + cpu_to_le32(OWNbit + RX_BUF_SIZE); + tpc->RxDescArray[cur_rx].buf_addr = + cpu_to_le32(bus_to_phys(tpc->RxBufferRing[cur_rx])); + rtl_flush_rx_desc(&tpc->RxDescArray[cur_rx]); + } else { + puts("Error Rx"); + } + cur_rx = (cur_rx + 1) % NUM_RX_DESC; + tpc->cur_rx = cur_rx; + return 1; + + } else { + ushort sts = RTL_R8(IntrStatus); + RTL_W8(IntrStatus, sts & ~(TxErr | RxErr | SYSErr)); + udelay(100); /* wait */ + } + tpc->cur_rx = cur_rx; + return (0); /* initially as this is called to flush the input */ +} + +#define HZ 1000 +/************************************************************************** +SEND - Transmit a frame +***************************************************************************/ +static int rtl_send(struct eth_device *dev, void *packet, int length) +{ + /* send the packet to destination */ + + u32 to; + u8 *ptxb; + int entry = tpc->cur_tx % NUM_TX_DESC; + u32 len = length; + int ret; + +#ifdef DEBUG_RTL8169_TX + int stime = currticks(); + printf ("%s\n", __FUNCTION__); + printf("sending %d bytes\n", len); +#endif + + ioaddr = dev->iobase; + + /* point to the current txb incase multiple tx_rings are used */ + ptxb = tpc->Tx_skbuff[entry * MAX_ETH_FRAME_SIZE]; + memcpy(ptxb, (char *)packet, (int)length); + rtl_flush_buffer(ptxb, length); + + while (len < ETH_ZLEN) + ptxb[len++] = '\0'; + + tpc->TxDescArray[entry].buf_Haddr = 0; + tpc->TxDescArray[entry].buf_addr = cpu_to_le32(bus_to_phys(ptxb)); + if (entry != (NUM_TX_DESC - 1)) { + tpc->TxDescArray[entry].status = + cpu_to_le32((OWNbit | FSbit | LSbit) | + ((len > ETH_ZLEN) ? len : ETH_ZLEN)); + } else { + tpc->TxDescArray[entry].status = + cpu_to_le32((OWNbit | EORbit | FSbit | LSbit) | + ((len > ETH_ZLEN) ? len : ETH_ZLEN)); + } + rtl_flush_tx_desc(&tpc->TxDescArray[entry]); + RTL_W8(TxPoll, 0x40); /* set polling bit */ + + tpc->cur_tx++; + to = currticks() + TX_TIMEOUT; + do { + rtl_inval_tx_desc(&tpc->TxDescArray[entry]); + } while ((le32_to_cpu(tpc->TxDescArray[entry].status) & OWNbit) + && (currticks() < to)); /* wait */ + + if (currticks() >= to) { +#ifdef DEBUG_RTL8169_TX + puts("tx timeout/error\n"); + printf("%s elapsed time : %lu\n", __func__, currticks()-stime); +#endif + ret = 0; + } else { +#ifdef DEBUG_RTL8169_TX + puts("tx done\n"); +#endif + ret = length; + } + /* Delay to make net console (nc) work properly */ + udelay(20); + return ret; +} + +static void rtl8169_set_rx_mode(struct eth_device *dev) +{ + u32 mc_filter[2]; /* Multicast hash filter */ + int rx_mode; + u32 tmp = 0; + +#ifdef DEBUG_RTL8169 + printf ("%s\n", __FUNCTION__); +#endif + + /* IFF_ALLMULTI */ + /* Too many to filter perfectly -- accept all multicasts. */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + + tmp = rtl8169_rx_config | rx_mode | (RTL_R32(RxConfig) & + rtl_chip_info[tpc->chipset].RxConfigMask); + + RTL_W32(RxConfig, tmp); + RTL_W32(MAR0 + 0, mc_filter[0]); + RTL_W32(MAR0 + 4, mc_filter[1]); +} + +static void rtl8169_hw_start(struct eth_device *dev) +{ + u32 i; + +#ifdef DEBUG_RTL8169 + int stime = currticks(); + printf ("%s\n", __FUNCTION__); +#endif + +#if 0 + /* Soft reset the chip. */ + RTL_W8(ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) { + if ((RTL_R8(ChipCmd) & CmdReset) == 0) + break; + else + udelay(10); + } +#endif + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + /* RTL-8169sb/8110sb or previous version */ + if (tpc->chipset <= 5) + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + /* For gigabit rtl8169 */ + RTL_W16(RxMaxSize, RxPacketMaxSize); + + /* Set Rx Config register */ + i = rtl8169_rx_config | (RTL_R32(RxConfig) & + rtl_chip_info[tpc->chipset].RxConfigMask); + RTL_W32(RxConfig, i); + + /* Set DMA burst size and Interframe Gap Time */ + RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) | + (InterFrameGap << TxInterFrameGapShift)); + + + tpc->cur_rx = 0; + + RTL_W32(TxDescStartAddrLow, bus_to_phys(tpc->TxDescArray)); + RTL_W32(TxDescStartAddrHigh, (unsigned long)0); + RTL_W32(RxDescStartAddrLow, bus_to_phys(tpc->RxDescArray)); + RTL_W32(RxDescStartAddrHigh, (unsigned long)0); + + /* RTL-8169sc/8110sc or later version */ + if (tpc->chipset > 5) + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W8(Cfg9346, Cfg9346_Lock); + udelay(10); + + RTL_W32(RxMissed, 0); + + rtl8169_set_rx_mode(dev); + + /* no early-rx interrupts */ + RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); + +#ifdef DEBUG_RTL8169 + printf("%s elapsed time : %lu\n", __func__, currticks()-stime); +#endif +} + +static void rtl8169_init_ring(struct eth_device *dev) +{ + int i; + +#ifdef DEBUG_RTL8169 + int stime = currticks(); + printf ("%s\n", __FUNCTION__); +#endif + + tpc->cur_rx = 0; + tpc->cur_tx = 0; + tpc->dirty_tx = 0; + memset(tpc->TxDescArray, 0x0, NUM_TX_DESC * sizeof(struct TxDesc)); + memset(tpc->RxDescArray, 0x0, NUM_RX_DESC * sizeof(struct RxDesc)); + + for (i = 0; i < NUM_TX_DESC; i++) { + tpc->Tx_skbuff[i] = &txb[i]; + } + + for (i = 0; i < NUM_RX_DESC; i++) { + if (i == (NUM_RX_DESC - 1)) + tpc->RxDescArray[i].status = + cpu_to_le32((OWNbit | EORbit) + RX_BUF_SIZE); + else + tpc->RxDescArray[i].status = + cpu_to_le32(OWNbit + RX_BUF_SIZE); + + tpc->RxBufferRing[i] = &rxb[i * RX_BUF_SIZE]; + tpc->RxDescArray[i].buf_addr = + cpu_to_le32(bus_to_phys(tpc->RxBufferRing[i])); + rtl_flush_rx_desc(&tpc->RxDescArray[i]); + } + +#ifdef DEBUG_RTL8169 + printf("%s elapsed time : %lu\n", __func__, currticks()-stime); +#endif +} + +/************************************************************************** +RESET - Finish setting up the ethernet interface +***************************************************************************/ +static int rtl_reset(struct eth_device *dev, bd_t *bis) +{ + int i; + +#ifdef DEBUG_RTL8169 + int stime = currticks(); + printf ("%s\n", __FUNCTION__); +#endif + + tpc->TxDescArrays = tx_ring; + /* Tx Desscriptor needs 256 bytes alignment; */ + tpc->TxDescArray = (struct TxDesc *) ((unsigned long)(tpc->TxDescArrays + + 255) & ~255); + + tpc->RxDescArrays = rx_ring; + /* Rx Desscriptor needs 256 bytes alignment; */ + tpc->RxDescArray = (struct RxDesc *) ((unsigned long)(tpc->RxDescArrays + + 255) & ~255); + + rtl8169_init_ring(dev); + rtl8169_hw_start(dev); + /* Construct a perfect filter frame with the mac address as first match + * and broadcast for all others */ + for (i = 0; i < 192; i++) + txb[i] = 0xFF; + + txb[0] = dev->enetaddr[0]; + txb[1] = dev->enetaddr[1]; + txb[2] = dev->enetaddr[2]; + txb[3] = dev->enetaddr[3]; + txb[4] = dev->enetaddr[4]; + txb[5] = dev->enetaddr[5]; + +#ifdef DEBUG_RTL8169 + printf("%s elapsed time : %lu\n", __func__, currticks()-stime); +#endif + return 0; +} + +/************************************************************************** +HALT - Turn off ethernet interface +***************************************************************************/ +static void rtl_halt(struct eth_device *dev) +{ + int i; + +#ifdef DEBUG_RTL8169 + printf ("%s\n", __FUNCTION__); +#endif + + ioaddr = dev->iobase; + + /* Stop the chip's Tx and Rx DMA processes. */ + RTL_W8(ChipCmd, 0x00); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16(IntrMask, 0x0000); + + RTL_W32(RxMissed, 0); + + tpc->TxDescArrays = NULL; + tpc->RxDescArrays = NULL; + tpc->TxDescArray = NULL; + tpc->RxDescArray = NULL; + for (i = 0; i < NUM_RX_DESC; i++) { + tpc->RxBufferRing[i] = NULL; + } +} + +/************************************************************************** +INIT - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +#define board_found 1 +#define valid_link 0 +static int rtl_init(struct eth_device *dev, bd_t *bis) +{ + static int board_idx = -1; + int i, rc; + int option = -1, Cap10_100 = 0, Cap1000 = 0; + +#ifdef DEBUG_RTL8169 + printf ("%s\n", __FUNCTION__); +#endif + + ioaddr = dev->iobase; + + board_idx++; + + /* point to private storage */ + tpc = &tpx; + + rc = rtl8169_init_board(dev); + if (rc) + return rc; + + /* Get MAC address. FIXME: read EEPROM */ + for (i = 0; i < MAC_ADDR_LEN; i++) + dev->enetaddr[i] = RTL_R8(MAC0 + i); + +#ifdef DEBUG_RTL8169 + printf("chipset = %d\n", tpc->chipset); + printf("MAC Address"); + for (i = 0; i < MAC_ADDR_LEN; i++) + printf(":%02x", dev->enetaddr[i]); + putc('\n'); +#endif + +#ifdef DEBUG_RTL8169 + /* Print out some hardware info */ + printf("%s: at ioaddr 0x%x\n", dev->name, ioaddr); +#endif + + /* if TBI is not endbled */ + if (!(RTL_R8(PHYstatus) & TBI_Enable)) { + int val = mdio_read(PHY_AUTO_NEGO_REG); + + option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; + /* Force RTL8169 in 10/100/1000 Full/Half mode. */ + if (option > 0) { +#ifdef DEBUG_RTL8169 + printf("%s: Force-mode Enabled.\n", dev->name); +#endif + Cap10_100 = 0, Cap1000 = 0; + switch (option) { + case _10_Half: + Cap10_100 = PHY_Cap_10_Half; + Cap1000 = PHY_Cap_Null; + break; + case _10_Full: + Cap10_100 = PHY_Cap_10_Full; + Cap1000 = PHY_Cap_Null; + break; + case _100_Half: + Cap10_100 = PHY_Cap_100_Half; + Cap1000 = PHY_Cap_Null; + break; + case _100_Full: + Cap10_100 = PHY_Cap_100_Full; + Cap1000 = PHY_Cap_Null; + break; + case _1000_Full: + Cap10_100 = PHY_Cap_Null; + Cap1000 = PHY_Cap_1000_Full; + break; + default: + break; + } + mdio_write(PHY_AUTO_NEGO_REG, Cap10_100 | (val & 0x1F)); /* leave PHY_AUTO_NEGO_REG bit4:0 unchanged */ + mdio_write(PHY_1000_CTRL_REG, Cap1000); + } else { +#ifdef DEBUG_RTL8169 + printf("%s: Auto-negotiation Enabled.\n", + dev->name); +#endif + /* enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged */ + mdio_write(PHY_AUTO_NEGO_REG, + PHY_Cap_10_Half | PHY_Cap_10_Full | + PHY_Cap_100_Half | PHY_Cap_100_Full | + (val & 0x1F)); + + /* enable 1000 Full Mode */ + mdio_write(PHY_1000_CTRL_REG, PHY_Cap_1000_Full); + + } + + /* Enable auto-negotiation and restart auto-nigotiation */ + mdio_write(PHY_CTRL_REG, + PHY_Enable_Auto_Nego | PHY_Restart_Auto_Nego); + udelay(100); + + /* wait for auto-negotiation process */ + for (i = 10000; i > 0; i--) { + /* check if auto-negotiation complete */ + if (mdio_read(PHY_STAT_REG) & PHY_Auto_Nego_Comp) { + udelay(100); + option = RTL_R8(PHYstatus); + if (option & _1000bpsF) { +#ifdef DEBUG_RTL8169 + printf("%s: 1000Mbps Full-duplex operation.\n", + dev->name); +#endif + } else { +#ifdef DEBUG_RTL8169 + printf("%s: %sMbps %s-duplex operation.\n", + dev->name, + (option & _100bps) ? "100" : + "10", + (option & FullDup) ? "Full" : + "Half"); +#endif + } + break; + } else { + udelay(100); + } + } /* end for-loop to wait for auto-negotiation process */ + + } else { + udelay(100); +#ifdef DEBUG_RTL8169 + printf + ("%s: 1000Mbps Full-duplex operation, TBI Link %s!\n", + dev->name, + (RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed"); +#endif + } + + return 1; +} + +int rtl8169_initialize(bd_t *bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase; + int idx=0; + + while(1){ + unsigned int region; + u16 device; + + /* Find RTL8169 */ + if ((devno = pci_find_devices(supported, idx++)) < 0) + break; + + pci_read_config_word(devno, PCI_DEVICE_ID, &device); + switch (device) { + case 0x8168: + region = 2; + break; + + default: + region = 1; + break; + } + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_0 + (region * 4), &iobase); + iobase &= ~0xf; + + debug ("rtl8169: REALTEK RTL8169 @0x%x\n", iobase); + + dev = (struct eth_device *)malloc(sizeof *dev); + if (!dev) { + printf("Can not allocate memory of rtl8169\n"); + break; + } + + memset(dev, 0, sizeof(*dev)); + sprintf (dev->name, "RTL8169#%d", card_number); + + dev->priv = (void *) devno; + dev->iobase = (int)pci_mem_to_phys(devno, iobase); + + dev->init = rtl_reset; + dev->halt = rtl_halt; + dev->send = rtl_send; + dev->recv = rtl_recv; + + eth_register (dev); + + rtl_init(dev, bis); + + card_number++; + } + return card_number; +} diff --git a/qemu/roms/u-boot/drivers/net/sh_eth.c b/qemu/roms/u-boot/drivers/net/sh_eth.c new file mode 100644 index 000000000..5e132f2b5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/sh_eth.c @@ -0,0 +1,655 @@ +/* + * sh_eth.c - Driver for Renesas ethernet controler. + * + * Copyright (C) 2008, 2011 Renesas Solutions Corp. + * Copyright (c) 2008, 2011 Nobuhiro Iwamatsu + * Copyright (c) 2007 Carlos Munoz + * Copyright (C) 2013 Renesas Electronics Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sh_eth.h" + +#ifndef CONFIG_SH_ETHER_USE_PORT +# error "Please define CONFIG_SH_ETHER_USE_PORT" +#endif +#ifndef CONFIG_SH_ETHER_PHY_ADDR +# error "Please define CONFIG_SH_ETHER_PHY_ADDR" +#endif + +#if defined(CONFIG_SH_ETHER_CACHE_WRITEBACK) && !defined(CONFIG_SYS_DCACHE_OFF) +#define flush_cache_wback(addr, len) \ + flush_dcache_range((u32)addr, (u32)(addr + len - 1)) +#else +#define flush_cache_wback(...) +#endif + +#if defined(CONFIG_SH_ETHER_CACHE_INVALIDATE) && defined(CONFIG_ARM) +#define invalidate_cache(addr, len) \ + { \ + u32 line_size = CONFIG_SH_ETHER_ALIGNE_SIZE; \ + u32 start, end; \ + \ + start = (u32)addr; \ + end = start + len; \ + start &= ~(line_size - 1); \ + end = ((end + line_size - 1) & ~(line_size - 1)); \ + \ + invalidate_dcache_range(start, end); \ + } +#else +#define invalidate_cache(...) +#endif + +#define TIMEOUT_CNT 1000 + +int sh_eth_send(struct eth_device *dev, void *packet, int len) +{ + struct sh_eth_dev *eth = dev->priv; + int port = eth->port, ret = 0, timeout; + struct sh_eth_info *port_info = ð->port_info[port]; + + if (!packet || len > 0xffff) { + printf(SHETHER_NAME ": %s: Invalid argument\n", __func__); + ret = -EINVAL; + goto err; + } + + /* packet must be a 4 byte boundary */ + if ((int)packet & 3) { + printf(SHETHER_NAME ": %s: packet not 4 byte alligned\n", __func__); + ret = -EFAULT; + goto err; + } + + /* Update tx descriptor */ + flush_cache_wback(packet, len); + port_info->tx_desc_cur->td2 = ADDR_TO_PHY(packet); + port_info->tx_desc_cur->td1 = len << 16; + /* Must preserve the end of descriptor list indication */ + if (port_info->tx_desc_cur->td0 & TD_TDLE) + port_info->tx_desc_cur->td0 = TD_TACT | TD_TFP | TD_TDLE; + else + port_info->tx_desc_cur->td0 = TD_TACT | TD_TFP; + + /* Restart the transmitter if disabled */ + if (!(sh_eth_read(eth, EDTRR) & EDTRR_TRNS)) + sh_eth_write(eth, EDTRR_TRNS, EDTRR); + + /* Wait until packet is transmitted */ + timeout = TIMEOUT_CNT; + do { + invalidate_cache(port_info->tx_desc_cur, + sizeof(struct tx_desc_s)); + udelay(100); + } while (port_info->tx_desc_cur->td0 & TD_TACT && timeout--); + + if (timeout < 0) { + printf(SHETHER_NAME ": transmit timeout\n"); + ret = -ETIMEDOUT; + goto err; + } + + port_info->tx_desc_cur++; + if (port_info->tx_desc_cur >= port_info->tx_desc_base + NUM_TX_DESC) + port_info->tx_desc_cur = port_info->tx_desc_base; + +err: + return ret; +} + +int sh_eth_recv(struct eth_device *dev) +{ + struct sh_eth_dev *eth = dev->priv; + int port = eth->port, len = 0; + struct sh_eth_info *port_info = ð->port_info[port]; + uchar *packet; + + /* Check if the rx descriptor is ready */ + invalidate_cache(port_info->rx_desc_cur, sizeof(struct rx_desc_s)); + if (!(port_info->rx_desc_cur->rd0 & RD_RACT)) { + /* Check for errors */ + if (!(port_info->rx_desc_cur->rd0 & RD_RFE)) { + len = port_info->rx_desc_cur->rd1 & 0xffff; + packet = (uchar *) + ADDR_TO_P2(port_info->rx_desc_cur->rd2); + invalidate_cache(packet, len); + NetReceive(packet, len); + } + + /* Make current descriptor available again */ + if (port_info->rx_desc_cur->rd0 & RD_RDLE) + port_info->rx_desc_cur->rd0 = RD_RACT | RD_RDLE; + else + port_info->rx_desc_cur->rd0 = RD_RACT; + /* Point to the next descriptor */ + port_info->rx_desc_cur++; + if (port_info->rx_desc_cur >= + port_info->rx_desc_base + NUM_RX_DESC) + port_info->rx_desc_cur = port_info->rx_desc_base; + } + + /* Restart the receiver if disabled */ + if (!(sh_eth_read(eth, EDRRR) & EDRRR_R)) + sh_eth_write(eth, EDRRR_R, EDRRR); + + return len; +} + +static int sh_eth_reset(struct sh_eth_dev *eth) +{ +#if defined(SH_ETH_TYPE_GETHER) + int ret = 0, i; + + /* Start e-dmac transmitter and receiver */ + sh_eth_write(eth, EDSR_ENALL, EDSR); + + /* Perform a software reset and wait for it to complete */ + sh_eth_write(eth, EDMR_SRST, EDMR); + for (i = 0; i < TIMEOUT_CNT ; i++) { + if (!(sh_eth_read(eth, EDMR) & EDMR_SRST)) + break; + udelay(1000); + } + + if (i == TIMEOUT_CNT) { + printf(SHETHER_NAME ": Software reset timeout\n"); + ret = -EIO; + } + + return ret; +#else + sh_eth_write(eth, sh_eth_read(eth, EDMR) | EDMR_SRST, EDMR); + udelay(3000); + sh_eth_write(eth, sh_eth_read(eth, EDMR) & ~EDMR_SRST, EDMR); + + return 0; +#endif +} + +static int sh_eth_tx_desc_init(struct sh_eth_dev *eth) +{ + int port = eth->port, i, ret = 0; + u32 tmp_addr; + struct sh_eth_info *port_info = ð->port_info[port]; + struct tx_desc_s *cur_tx_desc; + + /* + * Allocate tx descriptors. They must be TX_DESC_SIZE bytes aligned + */ + port_info->tx_desc_malloc = malloc(NUM_TX_DESC * + sizeof(struct tx_desc_s) + + TX_DESC_SIZE - 1); + if (!port_info->tx_desc_malloc) { + printf(SHETHER_NAME ": malloc failed\n"); + ret = -ENOMEM; + goto err; + } + + tmp_addr = (u32) (((int)port_info->tx_desc_malloc + TX_DESC_SIZE - 1) & + ~(TX_DESC_SIZE - 1)); + flush_cache_wback(tmp_addr, NUM_TX_DESC * sizeof(struct tx_desc_s)); + /* Make sure we use a P2 address (non-cacheable) */ + port_info->tx_desc_base = (struct tx_desc_s *)ADDR_TO_P2(tmp_addr); + port_info->tx_desc_cur = port_info->tx_desc_base; + + /* Initialize all descriptors */ + for (cur_tx_desc = port_info->tx_desc_base, i = 0; i < NUM_TX_DESC; + cur_tx_desc++, i++) { + cur_tx_desc->td0 = 0x00; + cur_tx_desc->td1 = 0x00; + cur_tx_desc->td2 = 0x00; + } + + /* Mark the end of the descriptors */ + cur_tx_desc--; + cur_tx_desc->td0 |= TD_TDLE; + + /* Point the controller to the tx descriptor list. Must use physical + addresses */ + sh_eth_write(eth, ADDR_TO_PHY(port_info->tx_desc_base), TDLAR); +#if defined(SH_ETH_TYPE_GETHER) + sh_eth_write(eth, ADDR_TO_PHY(port_info->tx_desc_base), TDFAR); + sh_eth_write(eth, ADDR_TO_PHY(cur_tx_desc), TDFXR); + sh_eth_write(eth, 0x01, TDFFR);/* Last discriptor bit */ +#endif + +err: + return ret; +} + +static int sh_eth_rx_desc_init(struct sh_eth_dev *eth) +{ + int port = eth->port, i , ret = 0; + struct sh_eth_info *port_info = ð->port_info[port]; + struct rx_desc_s *cur_rx_desc; + u32 tmp_addr; + u8 *rx_buf; + + /* + * Allocate rx descriptors. They must be RX_DESC_SIZE bytes aligned + */ + port_info->rx_desc_malloc = malloc(NUM_RX_DESC * + sizeof(struct rx_desc_s) + + RX_DESC_SIZE - 1); + if (!port_info->rx_desc_malloc) { + printf(SHETHER_NAME ": malloc failed\n"); + ret = -ENOMEM; + goto err; + } + + tmp_addr = (u32) (((int)port_info->rx_desc_malloc + RX_DESC_SIZE - 1) & + ~(RX_DESC_SIZE - 1)); + flush_cache_wback(tmp_addr, NUM_RX_DESC * sizeof(struct rx_desc_s)); + /* Make sure we use a P2 address (non-cacheable) */ + port_info->rx_desc_base = (struct rx_desc_s *)ADDR_TO_P2(tmp_addr); + + port_info->rx_desc_cur = port_info->rx_desc_base; + + /* + * Allocate rx data buffers. They must be 32 bytes aligned and in + * P2 area + */ + port_info->rx_buf_malloc = malloc( + NUM_RX_DESC * MAX_BUF_SIZE + RX_BUF_ALIGNE_SIZE - 1); + if (!port_info->rx_buf_malloc) { + printf(SHETHER_NAME ": malloc failed\n"); + ret = -ENOMEM; + goto err_buf_malloc; + } + + tmp_addr = (u32)(((int)port_info->rx_buf_malloc + + (RX_BUF_ALIGNE_SIZE - 1)) & + ~(RX_BUF_ALIGNE_SIZE - 1)); + port_info->rx_buf_base = (u8 *)ADDR_TO_P2(tmp_addr); + + /* Initialize all descriptors */ + for (cur_rx_desc = port_info->rx_desc_base, + rx_buf = port_info->rx_buf_base, i = 0; + i < NUM_RX_DESC; cur_rx_desc++, rx_buf += MAX_BUF_SIZE, i++) { + cur_rx_desc->rd0 = RD_RACT; + cur_rx_desc->rd1 = MAX_BUF_SIZE << 16; + cur_rx_desc->rd2 = (u32) ADDR_TO_PHY(rx_buf); + } + + /* Mark the end of the descriptors */ + cur_rx_desc--; + cur_rx_desc->rd0 |= RD_RDLE; + + /* Point the controller to the rx descriptor list */ + sh_eth_write(eth, ADDR_TO_PHY(port_info->rx_desc_base), RDLAR); +#if defined(SH_ETH_TYPE_GETHER) + sh_eth_write(eth, ADDR_TO_PHY(port_info->rx_desc_base), RDFAR); + sh_eth_write(eth, ADDR_TO_PHY(cur_rx_desc), RDFXR); + sh_eth_write(eth, RDFFR_RDLF, RDFFR); +#endif + + return ret; + +err_buf_malloc: + free(port_info->rx_desc_malloc); + port_info->rx_desc_malloc = NULL; + +err: + return ret; +} + +static void sh_eth_tx_desc_free(struct sh_eth_dev *eth) +{ + int port = eth->port; + struct sh_eth_info *port_info = ð->port_info[port]; + + if (port_info->tx_desc_malloc) { + free(port_info->tx_desc_malloc); + port_info->tx_desc_malloc = NULL; + } +} + +static void sh_eth_rx_desc_free(struct sh_eth_dev *eth) +{ + int port = eth->port; + struct sh_eth_info *port_info = ð->port_info[port]; + + if (port_info->rx_desc_malloc) { + free(port_info->rx_desc_malloc); + port_info->rx_desc_malloc = NULL; + } + + if (port_info->rx_buf_malloc) { + free(port_info->rx_buf_malloc); + port_info->rx_buf_malloc = NULL; + } +} + +static int sh_eth_desc_init(struct sh_eth_dev *eth) +{ + int ret = 0; + + ret = sh_eth_tx_desc_init(eth); + if (ret) + goto err_tx_init; + + ret = sh_eth_rx_desc_init(eth); + if (ret) + goto err_rx_init; + + return ret; +err_rx_init: + sh_eth_tx_desc_free(eth); + +err_tx_init: + return ret; +} + +static int sh_eth_phy_config(struct sh_eth_dev *eth) +{ + int port = eth->port, ret = 0; + struct sh_eth_info *port_info = ð->port_info[port]; + struct eth_device *dev = port_info->dev; + struct phy_device *phydev; + + phydev = phy_connect( + miiphy_get_dev_by_name(dev->name), + port_info->phy_addr, dev, CONFIG_SH_ETHER_PHY_MODE); + port_info->phydev = phydev; + phy_config(phydev); + + return ret; +} + +static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd) +{ + int port = eth->port, ret = 0; + u32 val; + struct sh_eth_info *port_info = ð->port_info[port]; + struct eth_device *dev = port_info->dev; + struct phy_device *phy; + + /* Configure e-dmac registers */ + sh_eth_write(eth, (sh_eth_read(eth, EDMR) & ~EMDR_DESC_R) | + (EMDR_DESC | EDMR_EL), EDMR); + + sh_eth_write(eth, 0, EESIPR); + sh_eth_write(eth, 0, TRSCER); + sh_eth_write(eth, 0, TFTR); + sh_eth_write(eth, (FIFO_SIZE_T | FIFO_SIZE_R), FDR); + sh_eth_write(eth, RMCR_RST, RMCR); +#if defined(SH_ETH_TYPE_GETHER) + sh_eth_write(eth, 0, RPADIR); +#endif + sh_eth_write(eth, (FIFO_F_D_RFF | FIFO_F_D_RFD), FCFTR); + + /* Configure e-mac registers */ + sh_eth_write(eth, 0, ECSIPR); + + /* Set Mac address */ + val = dev->enetaddr[0] << 24 | dev->enetaddr[1] << 16 | + dev->enetaddr[2] << 8 | dev->enetaddr[3]; + sh_eth_write(eth, val, MAHR); + + val = dev->enetaddr[4] << 8 | dev->enetaddr[5]; + sh_eth_write(eth, val, MALR); + + sh_eth_write(eth, RFLR_RFL_MIN, RFLR); +#if defined(SH_ETH_TYPE_GETHER) + sh_eth_write(eth, 0, PIPR); + sh_eth_write(eth, APR_AP, APR); + sh_eth_write(eth, MPR_MP, MPR); + sh_eth_write(eth, TPAUSER_TPAUSE, TPAUSER); +#endif + +#if defined(CONFIG_CPU_SH7734) || defined(CONFIG_R8A7740) + sh_eth_write(eth, CONFIG_SH_ETHER_SH7734_MII, RMII_MII); +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) + sh_eth_write(eth, sh_eth_read(eth, RMIIMR) | 0x1, RMIIMR); +#endif + /* Configure phy */ + ret = sh_eth_phy_config(eth); + if (ret) { + printf(SHETHER_NAME ": phy config timeout\n"); + goto err_phy_cfg; + } + phy = port_info->phydev; + ret = phy_startup(phy); + if (ret) { + printf(SHETHER_NAME ": phy startup failure\n"); + return ret; + } + + val = 0; + + /* Set the transfer speed */ + if (phy->speed == 100) { + printf(SHETHER_NAME ": 100Base/"); +#if defined(SH_ETH_TYPE_GETHER) + sh_eth_write(eth, GECMR_100B, GECMR); +#elif defined(CONFIG_CPU_SH7757) || defined(CONFIG_CPU_SH7752) + sh_eth_write(eth, 1, RTRATE); +#elif defined(CONFIG_CPU_SH7724) || defined(CONFIG_R8A7790) || \ + defined(CONFIG_R8A7791) + val = ECMR_RTM; +#endif + } else if (phy->speed == 10) { + printf(SHETHER_NAME ": 10Base/"); +#if defined(SH_ETH_TYPE_GETHER) + sh_eth_write(eth, GECMR_10B, GECMR); +#elif defined(CONFIG_CPU_SH7757) || defined(CONFIG_CPU_SH7752) + sh_eth_write(eth, 0, RTRATE); +#endif + } +#if defined(SH_ETH_TYPE_GETHER) + else if (phy->speed == 1000) { + printf(SHETHER_NAME ": 1000Base/"); + sh_eth_write(eth, GECMR_1000B, GECMR); + } +#endif + + /* Check if full duplex mode is supported by the phy */ + if (phy->duplex) { + printf("Full\n"); + sh_eth_write(eth, val | (ECMR_CHG_DM|ECMR_RE|ECMR_TE|ECMR_DM), + ECMR); + } else { + printf("Half\n"); + sh_eth_write(eth, val | (ECMR_CHG_DM|ECMR_RE|ECMR_TE), ECMR); + } + + return ret; + +err_phy_cfg: + return ret; +} + +static void sh_eth_start(struct sh_eth_dev *eth) +{ + /* + * Enable the e-dmac receiver only. The transmitter will be enabled when + * we have something to transmit + */ + sh_eth_write(eth, EDRRR_R, EDRRR); +} + +static void sh_eth_stop(struct sh_eth_dev *eth) +{ + sh_eth_write(eth, ~EDRRR_R, EDRRR); +} + +int sh_eth_init(struct eth_device *dev, bd_t *bd) +{ + int ret = 0; + struct sh_eth_dev *eth = dev->priv; + + ret = sh_eth_reset(eth); + if (ret) + goto err; + + ret = sh_eth_desc_init(eth); + if (ret) + goto err; + + ret = sh_eth_config(eth, bd); + if (ret) + goto err_config; + + sh_eth_start(eth); + + return ret; + +err_config: + sh_eth_tx_desc_free(eth); + sh_eth_rx_desc_free(eth); + +err: + return ret; +} + +void sh_eth_halt(struct eth_device *dev) +{ + struct sh_eth_dev *eth = dev->priv; + sh_eth_stop(eth); +} + +int sh_eth_initialize(bd_t *bd) +{ + int ret = 0; + struct sh_eth_dev *eth = NULL; + struct eth_device *dev = NULL; + + eth = (struct sh_eth_dev *)malloc(sizeof(struct sh_eth_dev)); + if (!eth) { + printf(SHETHER_NAME ": %s: malloc failed\n", __func__); + ret = -ENOMEM; + goto err; + } + + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + if (!dev) { + printf(SHETHER_NAME ": %s: malloc failed\n", __func__); + ret = -ENOMEM; + goto err; + } + memset(dev, 0, sizeof(struct eth_device)); + memset(eth, 0, sizeof(struct sh_eth_dev)); + + eth->port = CONFIG_SH_ETHER_USE_PORT; + eth->port_info[eth->port].phy_addr = CONFIG_SH_ETHER_PHY_ADDR; + + dev->priv = (void *)eth; + dev->iobase = 0; + dev->init = sh_eth_init; + dev->halt = sh_eth_halt; + dev->send = sh_eth_send; + dev->recv = sh_eth_recv; + eth->port_info[eth->port].dev = dev; + + sprintf(dev->name, SHETHER_NAME); + + /* Register Device to EtherNet subsystem */ + eth_register(dev); + + bb_miiphy_buses[0].priv = eth; + miiphy_register(dev->name, bb_miiphy_read, bb_miiphy_write); + + if (!eth_getenv_enetaddr("ethaddr", dev->enetaddr)) + puts("Please set MAC address\n"); + + return ret; + +err: + if (dev) + free(dev); + + if (eth) + free(eth); + + printf(SHETHER_NAME ": Failed\n"); + return ret; +} + +/******* for bb_miiphy *******/ +static int sh_eth_bb_init(struct bb_miiphy_bus *bus) +{ + return 0; +} + +static int sh_eth_bb_mdio_active(struct bb_miiphy_bus *bus) +{ + struct sh_eth_dev *eth = bus->priv; + + sh_eth_write(eth, sh_eth_read(eth, PIR) | PIR_MMD, PIR); + + return 0; +} + +static int sh_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus) +{ + struct sh_eth_dev *eth = bus->priv; + + sh_eth_write(eth, sh_eth_read(eth, PIR) & ~PIR_MMD, PIR); + + return 0; +} + +static int sh_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v) +{ + struct sh_eth_dev *eth = bus->priv; + + if (v) + sh_eth_write(eth, sh_eth_read(eth, PIR) | PIR_MDO, PIR); + else + sh_eth_write(eth, sh_eth_read(eth, PIR) & ~PIR_MDO, PIR); + + return 0; +} + +static int sh_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) +{ + struct sh_eth_dev *eth = bus->priv; + + *v = (sh_eth_read(eth, PIR) & PIR_MDI) >> 3; + + return 0; +} + +static int sh_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v) +{ + struct sh_eth_dev *eth = bus->priv; + + if (v) + sh_eth_write(eth, sh_eth_read(eth, PIR) | PIR_MDC, PIR); + else + sh_eth_write(eth, sh_eth_read(eth, PIR) & ~PIR_MDC, PIR); + + return 0; +} + +static int sh_eth_bb_delay(struct bb_miiphy_bus *bus) +{ + udelay(10); + + return 0; +} + +struct bb_miiphy_bus bb_miiphy_buses[] = { + { + .name = "sh_eth", + .init = sh_eth_bb_init, + .mdio_active = sh_eth_bb_mdio_active, + .mdio_tristate = sh_eth_bb_mdio_tristate, + .set_mdio = sh_eth_bb_set_mdio, + .get_mdio = sh_eth_bb_get_mdio, + .set_mdc = sh_eth_bb_set_mdc, + .delay = sh_eth_bb_delay, + } +}; +int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); diff --git a/qemu/roms/u-boot/drivers/net/sh_eth.h b/qemu/roms/u-boot/drivers/net/sh_eth.h new file mode 100644 index 000000000..331c07cb5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/sh_eth.h @@ -0,0 +1,624 @@ +/* + * sh_eth.h - Driver for Renesas SuperH ethernet controler. + * + * Copyright (C) 2008 - 2012 Renesas Solutions Corp. + * Copyright (c) 2008 - 2012 Nobuhiro Iwamatsu + * Copyright (c) 2007 Carlos Munoz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#define SHETHER_NAME "sh_eth" + +#if defined(CONFIG_SH) +/* Malloc returns addresses in the P1 area (cacheable). However we need to + use area P2 (non-cacheable) */ +#define ADDR_TO_P2(addr) ((((int)(addr) & ~0xe0000000) | 0xa0000000)) + +/* The ethernet controller needs to use physical addresses */ +#if defined(CONFIG_SH_32BIT) +#define ADDR_TO_PHY(addr) ((((int)(addr) & ~0xe0000000) | 0x40000000)) +#else +#define ADDR_TO_PHY(addr) ((int)(addr) & ~0xe0000000) +#endif +#elif defined(CONFIG_ARM) +#define inl readl +#define outl writel +#define ADDR_TO_PHY(addr) ((int)(addr)) +#define ADDR_TO_P2(addr) (addr) +#endif /* defined(CONFIG_SH) */ + +/* base padding size is 16 */ +#ifndef CONFIG_SH_ETHER_ALIGNE_SIZE +#define CONFIG_SH_ETHER_ALIGNE_SIZE 16 +#endif + +/* Number of supported ports */ +#define MAX_PORT_NUM 2 + +/* Buffers must be big enough to hold the largest ethernet frame. Also, rx + buffers must be a multiple of 32 bytes */ +#define MAX_BUF_SIZE (48 * 32) + +/* The number of tx descriptors must be large enough to point to 5 or more + frames. If each frame uses 2 descriptors, at least 10 descriptors are needed. + We use one descriptor per frame */ +#define NUM_TX_DESC 8 + +/* The size of the tx descriptor is determined by how much padding is used. + 4, 20, or 52 bytes of padding can be used */ +#define TX_DESC_PADDING (CONFIG_SH_ETHER_ALIGNE_SIZE - 12) +/* same as CONFIG_SH_ETHER_ALIGNE_SIZE */ +#define TX_DESC_SIZE (12 + TX_DESC_PADDING) + +/* Tx descriptor. We always use 3 bytes of padding */ +struct tx_desc_s { + volatile u32 td0; + u32 td1; + u32 td2; /* Buffer start */ + u8 padding[TX_DESC_PADDING]; /* aligned cache line size */ +}; + +/* There is no limitation in the number of rx descriptors */ +#define NUM_RX_DESC 8 + +/* The size of the rx descriptor is determined by how much padding is used. + 4, 20, or 52 bytes of padding can be used */ +#define RX_DESC_PADDING (CONFIG_SH_ETHER_ALIGNE_SIZE - 12) +/* same as CONFIG_SH_ETHER_ALIGNE_SIZE */ +#define RX_DESC_SIZE (12 + RX_DESC_PADDING) +/* aligned cache line size */ +#define RX_BUF_ALIGNE_SIZE (CONFIG_SH_ETHER_ALIGNE_SIZE > 32 ? 64 : 32) + +/* Rx descriptor. We always use 4 bytes of padding */ +struct rx_desc_s { + volatile u32 rd0; + volatile u32 rd1; + u32 rd2; /* Buffer start */ + u8 padding[TX_DESC_PADDING]; /* aligned cache line size */ +}; + +struct sh_eth_info { + struct tx_desc_s *tx_desc_malloc; + struct tx_desc_s *tx_desc_base; + struct tx_desc_s *tx_desc_cur; + struct rx_desc_s *rx_desc_malloc; + struct rx_desc_s *rx_desc_base; + struct rx_desc_s *rx_desc_cur; + u8 *rx_buf_malloc; + u8 *rx_buf_base; + u8 mac_addr[6]; + u8 phy_addr; + struct eth_device *dev; + struct phy_device *phydev; +}; + +struct sh_eth_dev { + int port; + struct sh_eth_info port_info[MAX_PORT_NUM]; +}; + +/* from linux/drivers/net/ethernet/renesas/sh_eth.h */ +enum { + /* E-DMAC registers */ + EDSR = 0, + EDMR, + EDTRR, + EDRRR, + EESR, + EESIPR, + TDLAR, + TDFAR, + TDFXR, + TDFFR, + RDLAR, + RDFAR, + RDFXR, + RDFFR, + TRSCER, + RMFCR, + TFTR, + FDR, + RMCR, + EDOCR, + TFUCR, + RFOCR, + FCFTR, + RPADIR, + TRIMD, + RBWAR, + TBRAR, + + /* Ether registers */ + ECMR, + ECSR, + ECSIPR, + PIR, + PSR, + RDMLR, + PIPR, + RFLR, + IPGR, + APR, + MPR, + PFTCR, + PFRCR, + RFCR, + RFCF, + TPAUSER, + TPAUSECR, + BCFR, + BCFRR, + GECMR, + BCULR, + MAHR, + MALR, + TROCR, + CDCR, + LCCR, + CNDCR, + CEFCR, + FRECR, + TSFRCR, + TLFRCR, + CERCR, + CEECR, + RMIIMR, /* R8A7790 */ + MAFCR, + RTRATE, + CSMR, + RMII_MII, + + /* This value must be written at last. */ + SH_ETH_MAX_REGISTER_OFFSET, +}; + +static const u16 sh_eth_offset_gigabit[SH_ETH_MAX_REGISTER_OFFSET] = { + [EDSR] = 0x0000, + [EDMR] = 0x0400, + [EDTRR] = 0x0408, + [EDRRR] = 0x0410, + [EESR] = 0x0428, + [EESIPR] = 0x0430, + [TDLAR] = 0x0010, + [TDFAR] = 0x0014, + [TDFXR] = 0x0018, + [TDFFR] = 0x001c, + [RDLAR] = 0x0030, + [RDFAR] = 0x0034, + [RDFXR] = 0x0038, + [RDFFR] = 0x003c, + [TRSCER] = 0x0438, + [RMFCR] = 0x0440, + [TFTR] = 0x0448, + [FDR] = 0x0450, + [RMCR] = 0x0458, + [RPADIR] = 0x0460, + [FCFTR] = 0x0468, + [CSMR] = 0x04E4, + + [ECMR] = 0x0500, + [ECSR] = 0x0510, + [ECSIPR] = 0x0518, + [PIR] = 0x0520, + [PSR] = 0x0528, + [PIPR] = 0x052c, + [RFLR] = 0x0508, + [APR] = 0x0554, + [MPR] = 0x0558, + [PFTCR] = 0x055c, + [PFRCR] = 0x0560, + [TPAUSER] = 0x0564, + [GECMR] = 0x05b0, + [BCULR] = 0x05b4, + [MAHR] = 0x05c0, + [MALR] = 0x05c8, + [TROCR] = 0x0700, + [CDCR] = 0x0708, + [LCCR] = 0x0710, + [CEFCR] = 0x0740, + [FRECR] = 0x0748, + [TSFRCR] = 0x0750, + [TLFRCR] = 0x0758, + [RFCR] = 0x0760, + [CERCR] = 0x0768, + [CEECR] = 0x0770, + [MAFCR] = 0x0778, + [RMII_MII] = 0x0790, +}; + +static const u16 sh_eth_offset_fast_sh4[SH_ETH_MAX_REGISTER_OFFSET] = { + [ECMR] = 0x0100, + [RFLR] = 0x0108, + [ECSR] = 0x0110, + [ECSIPR] = 0x0118, + [PIR] = 0x0120, + [PSR] = 0x0128, + [RDMLR] = 0x0140, + [IPGR] = 0x0150, + [APR] = 0x0154, + [MPR] = 0x0158, + [TPAUSER] = 0x0164, + [RFCF] = 0x0160, + [TPAUSECR] = 0x0168, + [BCFRR] = 0x016c, + [MAHR] = 0x01c0, + [MALR] = 0x01c8, + [TROCR] = 0x01d0, + [CDCR] = 0x01d4, + [LCCR] = 0x01d8, + [CNDCR] = 0x01dc, + [CEFCR] = 0x01e4, + [FRECR] = 0x01e8, + [TSFRCR] = 0x01ec, + [TLFRCR] = 0x01f0, + [RFCR] = 0x01f4, + [MAFCR] = 0x01f8, + [RTRATE] = 0x01fc, + + [EDMR] = 0x0000, + [EDTRR] = 0x0008, + [EDRRR] = 0x0010, + [TDLAR] = 0x0018, + [RDLAR] = 0x0020, + [EESR] = 0x0028, + [EESIPR] = 0x0030, + [TRSCER] = 0x0038, + [RMFCR] = 0x0040, + [TFTR] = 0x0048, + [FDR] = 0x0050, + [RMCR] = 0x0058, + [TFUCR] = 0x0064, + [RFOCR] = 0x0068, + [RMIIMR] = 0x006C, + [FCFTR] = 0x0070, + [RPADIR] = 0x0078, + [TRIMD] = 0x007c, + [RBWAR] = 0x00c8, + [RDFAR] = 0x00cc, + [TBRAR] = 0x00d4, + [TDFAR] = 0x00d8, +}; + +/* Register Address */ +#if defined(CONFIG_CPU_SH7763) || defined(CONFIG_CPU_SH7734) +#define SH_ETH_TYPE_GETHER +#define BASE_IO_ADDR 0xfee00000 +#elif defined(CONFIG_CPU_SH7757) || \ + defined(CONFIG_CPU_SH7752) || \ + defined(CONFIG_CPU_SH7753) +#if defined(CONFIG_SH_ETHER_USE_GETHER) +#define SH_ETH_TYPE_GETHER +#define BASE_IO_ADDR 0xfee00000 +#else +#define SH_ETH_TYPE_ETHER +#define BASE_IO_ADDR 0xfef00000 +#endif +#elif defined(CONFIG_CPU_SH7724) +#define SH_ETH_TYPE_ETHER +#define BASE_IO_ADDR 0xA4600000 +#elif defined(CONFIG_R8A7740) +#define SH_ETH_TYPE_GETHER +#define BASE_IO_ADDR 0xE9A00000 +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +#define SH_ETH_TYPE_ETHER +#define BASE_IO_ADDR 0xEE700200 +#endif + +/* + * Register's bits + * Copy from Linux driver source code + */ +#if defined(SH_ETH_TYPE_GETHER) +/* EDSR */ +enum EDSR_BIT { + EDSR_ENT = 0x01, EDSR_ENR = 0x02, +}; +#define EDSR_ENALL (EDSR_ENT|EDSR_ENR) +#endif + +/* EDMR */ +enum DMAC_M_BIT { + EDMR_DL1 = 0x20, EDMR_DL0 = 0x10, +#if defined(SH_ETH_TYPE_GETHER) + EDMR_SRST = 0x03, /* Receive/Send reset */ + EMDR_DESC_R = 0x30, /* Descriptor reserve size */ + EDMR_EL = 0x40, /* Litte endian */ +#elif defined(SH_ETH_TYPE_ETHER) + EDMR_SRST = 0x01, + EMDR_DESC_R = 0x30, /* Descriptor reserve size */ + EDMR_EL = 0x40, /* Litte endian */ +#else + EDMR_SRST = 0x01, +#endif +}; + +#if CONFIG_SH_ETHER_ALIGNE_SIZE == 64 +# define EMDR_DESC EDMR_DL1 +#elif CONFIG_SH_ETHER_ALIGNE_SIZE == 32 +# define EMDR_DESC EDMR_DL0 +#elif CONFIG_SH_ETHER_ALIGNE_SIZE == 16 /* Default */ +# define EMDR_DESC 0 +#endif + +/* RFLR */ +#define RFLR_RFL_MIN 0x05EE /* Recv Frame length 1518 byte */ + +/* EDTRR */ +enum DMAC_T_BIT { +#if defined(SH_ETH_TYPE_GETHER) + EDTRR_TRNS = 0x03, +#else + EDTRR_TRNS = 0x01, +#endif +}; + +/* GECMR */ +enum GECMR_BIT { +#if defined(CONFIG_CPU_SH7757) || \ + defined(CONFIG_CPU_SH7752) || \ + defined(CONFIG_CPU_SH7753) + GECMR_1000B = 0x20, GECMR_100B = 0x01, GECMR_10B = 0x00, +#else + GECMR_1000B = 0x01, GECMR_100B = 0x04, GECMR_10B = 0x00, +#endif +}; + +/* EDRRR*/ +enum EDRRR_R_BIT { + EDRRR_R = 0x01, +}; + +/* TPAUSER */ +enum TPAUSER_BIT { + TPAUSER_TPAUSE = 0x0000ffff, + TPAUSER_UNLIMITED = 0, +}; + +/* BCFR */ +enum BCFR_BIT { + BCFR_RPAUSE = 0x0000ffff, + BCFR_UNLIMITED = 0, +}; + +/* PIR */ +enum PIR_BIT { + PIR_MDI = 0x08, PIR_MDO = 0x04, PIR_MMD = 0x02, PIR_MDC = 0x01, +}; + +/* PSR */ +enum PHY_STATUS_BIT { PHY_ST_LINK = 0x01, }; + +/* EESR */ +enum EESR_BIT { + +#if defined(SH_ETH_TYPE_ETHER) + EESR_TWB = 0x40000000, +#else + EESR_TWB = 0xC0000000, + EESR_TC1 = 0x20000000, + EESR_TUC = 0x10000000, + EESR_ROC = 0x80000000, +#endif + EESR_TABT = 0x04000000, + EESR_RABT = 0x02000000, EESR_RFRMER = 0x01000000, +#if defined(SH_ETH_TYPE_ETHER) + EESR_ADE = 0x00800000, +#endif + EESR_ECI = 0x00400000, + EESR_FTC = 0x00200000, EESR_TDE = 0x00100000, + EESR_TFE = 0x00080000, EESR_FRC = 0x00040000, + EESR_RDE = 0x00020000, EESR_RFE = 0x00010000, +#if defined(SH_ETH_TYPE_ETHER) + EESR_CND = 0x00000800, +#endif + EESR_DLC = 0x00000400, + EESR_CD = 0x00000200, EESR_RTO = 0x00000100, + EESR_RMAF = 0x00000080, EESR_CEEF = 0x00000040, + EESR_CELF = 0x00000020, EESR_RRF = 0x00000010, + rESR_RTLF = 0x00000008, EESR_RTSF = 0x00000004, + EESR_PRE = 0x00000002, EESR_CERF = 0x00000001, +}; + + +#if defined(SH_ETH_TYPE_GETHER) +# define TX_CHECK (EESR_TC1 | EESR_FTC) +# define EESR_ERR_CHECK (EESR_TWB | EESR_TABT | EESR_RABT | EESR_RDE \ + | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI) +# define TX_ERROR_CEHCK (EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE) + +#else +# define TX_CHECK (EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO) +# define EESR_ERR_CHECK (EESR_TWB | EESR_TABT | EESR_RABT | EESR_RDE \ + | EESR_RFRMER | EESR_ADE | EESR_TFE | EESR_TDE | EESR_ECI) +# define TX_ERROR_CEHCK (EESR_TWB | EESR_TABT | EESR_ADE | EESR_TDE | EESR_TFE) +#endif + +/* EESIPR */ +enum DMAC_IM_BIT { + DMAC_M_TWB = 0x40000000, DMAC_M_TABT = 0x04000000, + DMAC_M_RABT = 0x02000000, + DMAC_M_RFRMER = 0x01000000, DMAC_M_ADF = 0x00800000, + DMAC_M_ECI = 0x00400000, DMAC_M_FTC = 0x00200000, + DMAC_M_TDE = 0x00100000, DMAC_M_TFE = 0x00080000, + DMAC_M_FRC = 0x00040000, DMAC_M_RDE = 0x00020000, + DMAC_M_RFE = 0x00010000, DMAC_M_TINT4 = 0x00000800, + DMAC_M_TINT3 = 0x00000400, DMAC_M_TINT2 = 0x00000200, + DMAC_M_TINT1 = 0x00000100, DMAC_M_RINT8 = 0x00000080, + DMAC_M_RINT5 = 0x00000010, DMAC_M_RINT4 = 0x00000008, + DMAC_M_RINT3 = 0x00000004, DMAC_M_RINT2 = 0x00000002, + DMAC_M_RINT1 = 0x00000001, +}; + +/* Receive descriptor bit */ +enum RD_STS_BIT { + RD_RACT = 0x80000000, RD_RDLE = 0x40000000, + RD_RFP1 = 0x20000000, RD_RFP0 = 0x10000000, + RD_RFE = 0x08000000, RD_RFS10 = 0x00000200, + RD_RFS9 = 0x00000100, RD_RFS8 = 0x00000080, + RD_RFS7 = 0x00000040, RD_RFS6 = 0x00000020, + RD_RFS5 = 0x00000010, RD_RFS4 = 0x00000008, + RD_RFS3 = 0x00000004, RD_RFS2 = 0x00000002, + RD_RFS1 = 0x00000001, +}; +#define RDF1ST RD_RFP1 +#define RDFEND RD_RFP0 +#define RD_RFP (RD_RFP1|RD_RFP0) + +/* RDFFR*/ +enum RDFFR_BIT { + RDFFR_RDLF = 0x01, +}; + +/* FCFTR */ +enum FCFTR_BIT { + FCFTR_RFF2 = 0x00040000, FCFTR_RFF1 = 0x00020000, + FCFTR_RFF0 = 0x00010000, FCFTR_RFD2 = 0x00000004, + FCFTR_RFD1 = 0x00000002, FCFTR_RFD0 = 0x00000001, +}; +#define FIFO_F_D_RFF (FCFTR_RFF2|FCFTR_RFF1|FCFTR_RFF0) +#define FIFO_F_D_RFD (FCFTR_RFD2|FCFTR_RFD1|FCFTR_RFD0) + +/* Transfer descriptor bit */ +enum TD_STS_BIT { +#if defined(SH_ETH_TYPE_GETHER) || defined(SH_ETH_TYPE_ETHER) + TD_TACT = 0x80000000, +#else + TD_TACT = 0x7fffffff, +#endif + TD_TDLE = 0x40000000, TD_TFP1 = 0x20000000, + TD_TFP0 = 0x10000000, +}; +#define TDF1ST TD_TFP1 +#define TDFEND TD_TFP0 +#define TD_TFP (TD_TFP1|TD_TFP0) + +/* RMCR */ +enum RECV_RST_BIT { RMCR_RST = 0x01, }; +/* ECMR */ +enum FELIC_MODE_BIT { +#if defined(SH_ETH_TYPE_GETHER) + ECMR_TRCCM=0x04000000, ECMR_RCSC= 0x00800000, ECMR_DPAD= 0x00200000, + ECMR_RZPF = 0x00100000, +#endif + ECMR_ZPF = 0x00080000, ECMR_PFR = 0x00040000, ECMR_RXF = 0x00020000, + ECMR_TXF = 0x00010000, ECMR_MCT = 0x00002000, ECMR_PRCEF = 0x00001000, + ECMR_PMDE = 0x00000200, ECMR_RE = 0x00000040, ECMR_TE = 0x00000020, + ECMR_ILB = 0x00000008, ECMR_ELB = 0x00000004, ECMR_DM = 0x00000002, + ECMR_PRM = 0x00000001, +#ifdef CONFIG_CPU_SH7724 + ECMR_RTM = 0x00000010, +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) + ECMR_RTM = 0x00000004, +#endif + +}; + +#if defined(SH_ETH_TYPE_GETHER) +#define ECMR_CHG_DM (ECMR_TRCCM | ECMR_RZPF | ECMR_ZPF | ECMR_PFR | ECMR_RXF | \ + ECMR_TXF | ECMR_MCT) +#elif defined(SH_ETH_TYPE_ETHER) +#define ECMR_CHG_DM (ECMR_ZPF | ECMR_PFR | ECMR_RXF | ECMR_TXF) +#else +#define ECMR_CHG_DM (ECMR_ZPF | ECMR_PFR | ECMR_RXF | ECMR_TXF | ECMR_MCT) +#endif + +/* ECSR */ +enum ECSR_STATUS_BIT { +#if defined(SH_ETH_TYPE_ETHER) + ECSR_BRCRX = 0x20, ECSR_PSRTO = 0x10, +#endif + ECSR_LCHNG = 0x04, + ECSR_MPD = 0x02, ECSR_ICD = 0x01, +}; + +#if defined(SH_ETH_TYPE_GETHER) +# define ECSR_INIT (ECSR_ICD | ECSIPR_MPDIP) +#else +# define ECSR_INIT (ECSR_BRCRX | ECSR_PSRTO | \ + ECSR_LCHNG | ECSR_ICD | ECSIPR_MPDIP) +#endif + +/* ECSIPR */ +enum ECSIPR_STATUS_MASK_BIT { +#if defined(SH_ETH_TYPE_ETHER) + ECSIPR_BRCRXIP = 0x20, + ECSIPR_PSRTOIP = 0x10, +#elif defined(SH_ETY_TYPE_GETHER) + ECSIPR_PSRTOIP = 0x10, + ECSIPR_PHYIP = 0x08, +#endif + ECSIPR_LCHNGIP = 0x04, + ECSIPR_MPDIP = 0x02, + ECSIPR_ICDIP = 0x01, +}; + +#if defined(SH_ETH_TYPE_GETHER) +# define ECSIPR_INIT (ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP) +#else +# define ECSIPR_INIT (ECSIPR_BRCRXIP | ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | \ + ECSIPR_ICDIP | ECSIPR_MPDIP) +#endif + +/* APR */ +enum APR_BIT { + APR_AP = 0x00000004, +}; + +/* MPR */ +enum MPR_BIT { + MPR_MP = 0x00000006, +}; + +/* TRSCER */ +enum DESC_I_BIT { + DESC_I_TINT4 = 0x0800, DESC_I_TINT3 = 0x0400, DESC_I_TINT2 = 0x0200, + DESC_I_TINT1 = 0x0100, DESC_I_RINT8 = 0x0080, DESC_I_RINT5 = 0x0010, + DESC_I_RINT4 = 0x0008, DESC_I_RINT3 = 0x0004, DESC_I_RINT2 = 0x0002, + DESC_I_RINT1 = 0x0001, +}; + +/* RPADIR */ +enum RPADIR_BIT { + RPADIR_PADS1 = 0x20000, RPADIR_PADS0 = 0x10000, + RPADIR_PADR = 0x0003f, +}; + +#if defined(SH_ETH_TYPE_GETHER) +# define RPADIR_INIT (0x00) +#else +# define RPADIR_INIT (RPADIR_PADS1) +#endif + +/* FDR */ +enum FIFO_SIZE_BIT { + FIFO_SIZE_T = 0x00000700, FIFO_SIZE_R = 0x00000007, +}; + +static inline unsigned long sh_eth_reg_addr(struct sh_eth_dev *eth, + int enum_index) +{ +#if defined(SH_ETH_TYPE_GETHER) + const u16 *reg_offset = sh_eth_offset_gigabit; +#elif defined(SH_ETH_TYPE_ETHER) + const u16 *reg_offset = sh_eth_offset_fast_sh4; +#else +#error +#endif + return BASE_IO_ADDR + reg_offset[enum_index] + 0x800 * eth->port; +} + +static inline void sh_eth_write(struct sh_eth_dev *eth, unsigned long data, + int enum_index) +{ + outl(data, sh_eth_reg_addr(eth, enum_index)); +} + +static inline unsigned long sh_eth_read(struct sh_eth_dev *eth, + int enum_index) +{ + return inl(sh_eth_reg_addr(eth, enum_index)); +} diff --git a/qemu/roms/u-boot/drivers/net/smc91111.c b/qemu/roms/u-boot/drivers/net/smc91111.c new file mode 100644 index 000000000..57c667a58 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/smc91111.c @@ -0,0 +1,1355 @@ +/*------------------------------------------------------------------------ + . smc91111.c + . This is a driver for SMSC's 91C111 single-chip Ethernet device. + . + . (C) Copyright 2002 + . Sysgo Real-Time Solutions, GmbH + . Rolf Offermanns + . + . Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + . Developed by Simple Network Magic Corporation (SNMC) + . Copyright (C) 1996 by Erik Stahlman (ES) + . + * SPDX-License-Identifier: GPL-2.0+ + . + . Information contained in this file was obtained from the LAN91C111 + . manual from SMC. To get a copy, if you really want one, you can find + . information under www.smsc.com. + . + . + . "Features" of the SMC chip: + . Integrated PHY/MAC for 10/100BaseT Operation + . Supports internal and external MII + . Integrated 8K packet memory + . EEPROM interface for configuration + . + . Arguments: + . io = for the base address + . irq = for the IRQ + . + . author: + . Erik Stahlman ( erik@vt.edu ) + . Daris A Nevil ( dnevil@snmc.com ) + . + . + . Hardware multicast code from Peter Cammaert ( pc@denkart.be ) + . + . Sources: + . o SMSC LAN91C111 databook (www.smsc.com) + . o smc9194.c by Erik Stahlman + . o skeleton.c by Donald Becker ( becker@cesdis.gsfc.nasa.gov ) + . + . History: + . 06/19/03 Richard Woodruff Made u-boot environment aware and added mac addr checks. + . 10/17/01 Marco Hasewinkel Modify for DNP/1110 + . 07/25/01 Woojung Huh Modify for ADS Bitsy + . 04/25/01 Daris A Nevil Initial public release through SMSC + . 03/16/01 Daris A Nevil Modified smc9194.c for use with LAN91C111 + ----------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include "smc91111.h" +#include + +/* Use power-down feature of the chip */ +#define POWER_DOWN 0 + +#define NO_AUTOPROBE + +#define SMC_DEBUG 0 + +#if SMC_DEBUG > 1 +static const char version[] = + "smc91111.c:v1.0 04/25/01 by Daris A Nevil (dnevil@snmc.com)\n"; +#endif + +/* Autonegotiation timeout in seconds */ +#ifndef CONFIG_SMC_AUTONEG_TIMEOUT +#define CONFIG_SMC_AUTONEG_TIMEOUT 10 +#endif + +/*------------------------------------------------------------------------ + . + . Configuration options, for the experienced user to change. + . + -------------------------------------------------------------------------*/ + +/* + . Wait time for memory to be free. This probably shouldn't be + . tuned that much, as waiting for this means nothing else happens + . in the system +*/ +#define MEMORY_WAIT_TIME 16 + + +#if (SMC_DEBUG > 2 ) +#define PRINTK3(args...) printf(args) +#else +#define PRINTK3(args...) +#endif + +#if SMC_DEBUG > 1 +#define PRINTK2(args...) printf(args) +#else +#define PRINTK2(args...) +#endif + +#ifdef SMC_DEBUG +#define PRINTK(args...) printf(args) +#else +#define PRINTK(args...) +#endif + + +/*------------------------------------------------------------------------ + . + . The internal workings of the driver. If you are changing anything + . here with the SMC stuff, you should have the datasheet and know + . what you are doing. + . + -------------------------------------------------------------------------*/ + +/* Memory sizing constant */ +#define LAN91C111_MEMORY_MULTIPLIER (1024*2) + +#ifndef CONFIG_SMC91111_BASE +#error "SMC91111 Base address must be passed to initialization funciton" +/* #define CONFIG_SMC91111_BASE 0x20000300 */ +#endif + +#define SMC_DEV_NAME "SMC91111" +#define SMC_PHY_ADDR 0x0000 +#define SMC_ALLOC_MAX_TRY 5 +#define SMC_TX_TIMEOUT 30 + +#define SMC_PHY_CLOCK_DELAY 1000 + +#define ETH_ZLEN 60 + +#ifdef CONFIG_SMC_USE_32_BIT +#define USE_32_BIT 1 +#else +#undef USE_32_BIT +#endif + +#ifdef SHARED_RESOURCES +extern void swap_to(int device_id); +#else +# define swap_to(x) +#endif + +#ifndef CONFIG_SMC91111_EXT_PHY +static void smc_phy_configure(struct eth_device *dev); +#endif /* !CONFIG_SMC91111_EXT_PHY */ + +/* + ------------------------------------------------------------ + . + . Internal routines + . + ------------------------------------------------------------ +*/ + +#ifdef CONFIG_SMC_USE_IOFUNCS +/* + * input and output functions + * + * Implemented due to inx,outx macros accessing the device improperly + * and putting the device into an unkown state. + * + * For instance, on Sharp LPD7A400 SDK, affects were chip memory + * could not be free'd (hence the alloc failures), duplicate packets, + * packets being corrupt (shifted) on the wire, etc. Switching to the + * inx,outx functions fixed this problem. + */ + +static inline word SMC_inw(struct eth_device *dev, dword offset) +{ + word v; + v = *((volatile word*)(dev->iobase + offset)); + barrier(); *(volatile u32*)(0xc0000000); + return v; +} + +static inline void SMC_outw(struct eth_device *dev, word value, dword offset) +{ + *((volatile word*)(dev->iobase + offset)) = value; + barrier(); *(volatile u32*)(0xc0000000); +} + +static inline byte SMC_inb(struct eth_device *dev, dword offset) +{ + word _w; + + _w = SMC_inw(dev, offset & ~((dword)1)); + return (offset & 1) ? (byte)(_w >> 8) : (byte)(_w); +} + +static inline void SMC_outb(struct eth_device *dev, byte value, dword offset) +{ + word _w; + + _w = SMC_inw(dev, offset & ~((dword)1)); + if (offset & 1) + *((volatile word*)(dev->iobase + (offset & ~((dword)1)))) = + (value<<8) | (_w & 0x00ff); + else + *((volatile word*)(dev->iobase + offset)) = + value | (_w & 0xff00); +} + +static inline void SMC_insw(struct eth_device *dev, dword offset, + volatile uchar* buf, dword len) +{ + volatile word *p = (volatile word *)buf; + + while (len-- > 0) { + *p++ = SMC_inw(dev, offset); + barrier(); + *((volatile u32*)(0xc0000000)); + } +} + +static inline void SMC_outsw(struct eth_device *dev, dword offset, + uchar* buf, dword len) +{ + volatile word *p = (volatile word *)buf; + + while (len-- > 0) { + SMC_outw(dev, *p++, offset); + barrier(); + *(volatile u32*)(0xc0000000); + } +} +#endif /* CONFIG_SMC_USE_IOFUNCS */ + +/* + . A rather simple routine to print out a packet for debugging purposes. +*/ +#if SMC_DEBUG > 2 +static void print_packet( byte *, int ); +#endif + +#define tx_done(dev) 1 + +static int poll4int (struct eth_device *dev, byte mask, int timeout) +{ + int tmo = get_timer (0) + timeout * CONFIG_SYS_HZ; + int is_timeout = 0; + word old_bank = SMC_inw (dev, BSR_REG); + + PRINTK2 ("Polling...\n"); + SMC_SELECT_BANK (dev, 2); + while ((SMC_inw (dev, SMC91111_INT_REG) & mask) == 0) { + if (get_timer (0) >= tmo) { + is_timeout = 1; + break; + } + } + + /* restore old bank selection */ + SMC_SELECT_BANK (dev, old_bank); + + if (is_timeout) + return 1; + else + return 0; +} + +/* Only one release command at a time, please */ +static inline void smc_wait_mmu_release_complete (struct eth_device *dev) +{ + int count = 0; + + /* assume bank 2 selected */ + while (SMC_inw (dev, MMU_CMD_REG) & MC_BUSY) { + udelay (1); /* Wait until not busy */ + if (++count > 200) + break; + } +} + +/* + . Function: smc_reset( void ) + . Purpose: + . This sets the SMC91111 chip to its normal state, hopefully from whatever + . mess that any other DOS driver has put it in. + . + . Maybe I should reset more registers to defaults in here? SOFTRST should + . do that for me. + . + . Method: + . 1. send a SOFT RESET + . 2. wait for it to finish + . 3. enable autorelease mode + . 4. reset the memory management unit + . 5. clear all interrupts + . +*/ +static void smc_reset (struct eth_device *dev) +{ + PRINTK2 ("%s: smc_reset\n", SMC_DEV_NAME); + + /* This resets the registers mostly to defaults, but doesn't + affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK (dev, 0); + SMC_outw (dev, RCR_SOFTRST, RCR_REG); + + /* Setup the Configuration Register */ + /* This is necessary because the CONFIG_REG is not affected */ + /* by a soft reset */ + + SMC_SELECT_BANK (dev, 1); +#if defined(CONFIG_SMC91111_EXT_PHY) + SMC_outw (dev, CONFIG_DEFAULT | CONFIG_EXT_PHY, CONFIG_REG); +#else + SMC_outw (dev, CONFIG_DEFAULT, CONFIG_REG); +#endif + + + /* Release from possible power-down state */ + /* Configuration register is not affected by Soft Reset */ + SMC_outw (dev, SMC_inw (dev, CONFIG_REG) | CONFIG_EPH_POWER_EN, + CONFIG_REG); + + SMC_SELECT_BANK (dev, 0); + + /* this should pause enough for the chip to be happy */ + udelay (10); + + /* Disable transmit and receive functionality */ + SMC_outw (dev, RCR_CLEAR, RCR_REG); + SMC_outw (dev, TCR_CLEAR, TCR_REG); + + /* set the control register */ + SMC_SELECT_BANK (dev, 1); + SMC_outw (dev, CTL_DEFAULT, CTL_REG); + + /* Reset the MMU */ + SMC_SELECT_BANK (dev, 2); + smc_wait_mmu_release_complete (dev); + SMC_outw (dev, MC_RESET, MMU_CMD_REG); + while (SMC_inw (dev, MMU_CMD_REG) & MC_BUSY) + udelay (1); /* Wait until not busy */ + + /* Note: It doesn't seem that waiting for the MMU busy is needed here, + but this is a place where future chipsets _COULD_ break. Be wary + of issuing another MMU command right after this */ + + /* Disable all interrupts */ + SMC_outb (dev, 0, IM_REG); +} + +/* + . Function: smc_enable + . Purpose: let the chip talk to the outside work + . Method: + . 1. Enable the transmitter + . 2. Enable the receiver + . 3. Enable interrupts +*/ +static void smc_enable(struct eth_device *dev) +{ + PRINTK2("%s: smc_enable\n", SMC_DEV_NAME); + SMC_SELECT_BANK( dev, 0 ); + /* see the header file for options in TCR/RCR DEFAULT*/ + SMC_outw( dev, TCR_DEFAULT, TCR_REG ); + SMC_outw( dev, RCR_DEFAULT, RCR_REG ); + + /* clear MII_DIS */ +/* smc_write_phy_register(PHY_CNTL_REG, 0x0000); */ +} + +/* + . Function: smc_halt + . Purpose: closes down the SMC91xxx chip. + . Method: + . 1. zero the interrupt mask + . 2. clear the enable receive flag + . 3. clear the enable xmit flags + . + . TODO: + . (1) maybe utilize power down mode. + . Why not yet? Because while the chip will go into power down mode, + . the manual says that it will wake up in response to any I/O requests + . in the register space. Empirical results do not show this working. +*/ +static void smc_halt(struct eth_device *dev) +{ + PRINTK2("%s: smc_halt\n", SMC_DEV_NAME); + + /* no more interrupts for me */ + SMC_SELECT_BANK( dev, 2 ); + SMC_outb( dev, 0, IM_REG ); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK( dev, 0 ); + SMC_outb( dev, RCR_CLEAR, RCR_REG ); + SMC_outb( dev, TCR_CLEAR, TCR_REG ); + + swap_to(FLASH); +} + + +/* + . Function: smc_send(struct net_device * ) + . Purpose: + . This sends the actual packet to the SMC9xxx chip. + . + . Algorithm: + . First, see if a saved_skb is available. + . ( this should NOT be called if there is no 'saved_skb' + . Now, find the packet number that the chip allocated + . Point the data pointers at it in memory + . Set the length word in the chip's memory + . Dump the packet to chip memory + . Check if a last byte is needed ( odd length packet ) + . if so, set the control flag right + . Tell the card to send it + . Enable the transmit interrupt, so I know if it failed + . Free the kernel data if I actually sent it. +*/ +static int smc_send(struct eth_device *dev, void *packet, int packet_length) +{ + byte packet_no; + byte *buf; + int length; + int numPages; + int try = 0; + int time_out; + byte status; + byte saved_pnr; + word saved_ptr; + + /* save PTR and PNR registers before manipulation */ + SMC_SELECT_BANK (dev, 2); + saved_pnr = SMC_inb( dev, PN_REG ); + saved_ptr = SMC_inw( dev, PTR_REG ); + + PRINTK3 ("%s: smc_hardware_send_packet\n", SMC_DEV_NAME); + + length = ETH_ZLEN < packet_length ? packet_length : ETH_ZLEN; + + /* allocate memory + ** The MMU wants the number of pages to be the number of 256 bytes + ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) + ** + ** The 91C111 ignores the size bits, but the code is left intact + ** for backwards and future compatibility. + ** + ** Pkt size for allocating is data length +6 (for additional status + ** words, length and ctl!) + ** + ** If odd size then last byte is included in this header. + */ + numPages = ((length & 0xfffe) + 6); + numPages >>= 8; /* Divide by 256 */ + + if (numPages > 7) { + printf ("%s: Far too big packet error. \n", SMC_DEV_NAME); + return 0; + } + + /* now, try to allocate the memory */ + SMC_SELECT_BANK (dev, 2); + SMC_outw (dev, MC_ALLOC | numPages, MMU_CMD_REG); + + /* FIXME: the ALLOC_INT bit never gets set * + * so the following will always give a * + * memory allocation error. * + * same code works in armboot though * + * -ro + */ + +again: + try++; + time_out = MEMORY_WAIT_TIME; + do { + status = SMC_inb (dev, SMC91111_INT_REG); + if (status & IM_ALLOC_INT) { + /* acknowledge the interrupt */ + SMC_outb (dev, IM_ALLOC_INT, SMC91111_INT_REG); + break; + } + } while (--time_out); + + if (!time_out) { + PRINTK2 ("%s: memory allocation, try %d failed ...\n", + SMC_DEV_NAME, try); + if (try < SMC_ALLOC_MAX_TRY) + goto again; + else + return 0; + } + + PRINTK2 ("%s: memory allocation, try %d succeeded ...\n", + SMC_DEV_NAME, try); + + buf = (byte *) packet; + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = SMC_inb (dev, AR_REG); + if (packet_no & AR_FAILED) { + /* or isn't there? BAD CHIP! */ + printf ("%s: Memory allocation failed. \n", SMC_DEV_NAME); + return 0; + } + + /* we have a packet address, so tell the card to use it */ +#ifndef CONFIG_XAENIAX + SMC_outb (dev, packet_no, PN_REG); +#else + /* On Xaeniax board, we can't use SMC_outb here because that way + * the Allocate MMU command will end up written to the command register + * as well, which will lead to a problem. + */ + SMC_outl (dev, packet_no << 16, 0); +#endif + /* do not write new ptr value if Write data fifo not empty */ + while ( saved_ptr & PTR_NOTEMPTY ) + printf ("Write data fifo not empty!\n"); + + /* point to the beginning of the packet */ + SMC_outw (dev, PTR_AUTOINC, PTR_REG); + + PRINTK3 ("%s: Trying to xmit packet of length %x\n", + SMC_DEV_NAME, length); + +#if SMC_DEBUG > 2 + printf ("Transmitting Packet\n"); + print_packet (buf, length); +#endif + + /* send the packet length ( +6 for status, length and ctl byte ) + and the status word ( set to zeros ) */ +#ifdef USE_32_BIT + SMC_outl (dev, (length + 6) << 16, SMC91111_DATA_REG); +#else + SMC_outw (dev, 0, SMC91111_DATA_REG); + /* send the packet length ( +6 for status words, length, and ctl */ + SMC_outw (dev, (length + 6), SMC91111_DATA_REG); +#endif + + /* send the actual data + . I _think_ it's faster to send the longs first, and then + . mop up by sending the last word. It depends heavily + . on alignment, at least on the 486. Maybe it would be + . a good idea to check which is optimal? But that could take + . almost as much time as is saved? + */ +#ifdef USE_32_BIT + SMC_outsl (dev, SMC91111_DATA_REG, buf, length >> 2); +#ifndef CONFIG_XAENIAX + if (length & 0x2) + SMC_outw (dev, *((word *) (buf + (length & 0xFFFFFFFC))), + SMC91111_DATA_REG); +#else + /* On XANEIAX, we can only use 32-bit writes, so we need to handle + * unaligned tail part specially. The standard code doesn't work. + */ + if ((length & 3) == 3) { + u16 * ptr = (u16*) &buf[length-3]; + SMC_outl(dev, (*ptr) | ((0x2000 | buf[length-1]) << 16), + SMC91111_DATA_REG); + } else if ((length & 2) == 2) { + u16 * ptr = (u16*) &buf[length-2]; + SMC_outl(dev, *ptr, SMC91111_DATA_REG); + } else if (length & 1) { + SMC_outl(dev, (0x2000 | buf[length-1]), SMC91111_DATA_REG); + } else { + SMC_outl(dev, 0, SMC91111_DATA_REG); + } +#endif +#else + SMC_outsw (dev, SMC91111_DATA_REG, buf, (length) >> 1); +#endif /* USE_32_BIT */ + +#ifndef CONFIG_XAENIAX + /* Send the last byte, if there is one. */ + if ((length & 1) == 0) { + SMC_outw (dev, 0, SMC91111_DATA_REG); + } else { + SMC_outw (dev, buf[length - 1] | 0x2000, SMC91111_DATA_REG); + } +#endif + + /* and let the chipset deal with it */ + SMC_outw (dev, MC_ENQUEUE, MMU_CMD_REG); + + /* poll for TX INT */ + /* if (poll4int (dev, IM_TX_INT, SMC_TX_TIMEOUT)) { */ + /* poll for TX_EMPTY INT - autorelease enabled */ + if (poll4int(dev, IM_TX_EMPTY_INT, SMC_TX_TIMEOUT)) { + /* sending failed */ + PRINTK2 ("%s: TX timeout, sending failed...\n", SMC_DEV_NAME); + + /* release packet */ + /* no need to release, MMU does that now */ +#ifdef CONFIG_XAENIAX + SMC_outw (dev, MC_FREEPKT, MMU_CMD_REG); +#endif + + /* wait for MMU getting ready (low) */ + while (SMC_inw (dev, MMU_CMD_REG) & MC_BUSY) { + udelay (10); + } + + PRINTK2 ("MMU ready\n"); + + + return 0; + } else { + /* ack. int */ + SMC_outb (dev, IM_TX_EMPTY_INT, SMC91111_INT_REG); + /* SMC_outb (IM_TX_INT, SMC91111_INT_REG); */ + PRINTK2 ("%s: Sent packet of length %d \n", SMC_DEV_NAME, + length); + + /* release packet */ + /* no need to release, MMU does that now */ +#ifdef CONFIG_XAENIAX + SMC_outw (dev, MC_FREEPKT, MMU_CMD_REG); +#endif + + /* wait for MMU getting ready (low) */ + while (SMC_inw (dev, MMU_CMD_REG) & MC_BUSY) { + udelay (10); + } + + PRINTK2 ("MMU ready\n"); + + + } + + /* restore previously saved registers */ +#ifndef CONFIG_XAENIAX + SMC_outb( dev, saved_pnr, PN_REG ); +#else + /* On Xaeniax board, we can't use SMC_outb here because that way + * the Allocate MMU command will end up written to the command register + * as well, which will lead to a problem. + */ + SMC_outl(dev, saved_pnr << 16, 0); +#endif + SMC_outw( dev, saved_ptr, PTR_REG ); + + return length; +} + +static int smc_write_hwaddr(struct eth_device *dev) +{ + int i; + + swap_to(ETHERNET); + SMC_SELECT_BANK (dev, 1); +#ifdef USE_32_BIT + for (i = 0; i < 6; i += 2) { + word address; + + address = dev->enetaddr[i + 1] << 8; + address |= dev->enetaddr[i]; + SMC_outw(dev, address, (ADDR0_REG + i)); + } +#else + for (i = 0; i < 6; i++) + SMC_outb(dev, dev->enetaddr[i], (ADDR0_REG + i)); +#endif + swap_to(FLASH); + return 0; +} + +/* + * Open and Initialize the board + * + * Set up everything, reset the card, etc .. + * + */ +static int smc_init(struct eth_device *dev, bd_t *bd) +{ + swap_to(ETHERNET); + + PRINTK2 ("%s: smc_init\n", SMC_DEV_NAME); + + /* reset the hardware */ + smc_reset (dev); + smc_enable (dev); + + /* Configure the PHY */ +#ifndef CONFIG_SMC91111_EXT_PHY + smc_phy_configure (dev); +#endif + + /* conservative setting (10Mbps, HalfDuplex, no AutoNeg.) */ +/* SMC_SELECT_BANK(dev, 0); */ +/* SMC_outw(dev, 0, RPC_REG); */ + + printf(SMC_DEV_NAME ": MAC %pM\n", dev->enetaddr); + + return 0; +} + +/*------------------------------------------------------------- + . + . smc_rcv - receive a packet from the card + . + . There is ( at least ) a packet waiting to be read from + . chip-memory. + . + . o Read the status + . o If an error, record it + . o otherwise, read in the packet + -------------------------------------------------------------- +*/ +static int smc_rcv(struct eth_device *dev) +{ + int packet_number; + word status; + word packet_length; + int is_error = 0; +#ifdef USE_32_BIT + dword stat_len; +#endif + byte saved_pnr; + word saved_ptr; + + SMC_SELECT_BANK(dev, 2); + /* save PTR and PTR registers */ + saved_pnr = SMC_inb( dev, PN_REG ); + saved_ptr = SMC_inw( dev, PTR_REG ); + + packet_number = SMC_inw( dev, RXFIFO_REG ); + + if ( packet_number & RXFIFO_REMPTY ) { + + return 0; + } + + PRINTK3("%s: smc_rcv\n", SMC_DEV_NAME); + /* start reading from the start of the packet */ + SMC_outw( dev, PTR_READ | PTR_RCV | PTR_AUTOINC, PTR_REG ); + + /* First two words are status and packet_length */ +#ifdef USE_32_BIT + stat_len = SMC_inl(dev, SMC91111_DATA_REG); + status = stat_len & 0xffff; + packet_length = stat_len >> 16; +#else + status = SMC_inw( dev, SMC91111_DATA_REG ); + packet_length = SMC_inw( dev, SMC91111_DATA_REG ); +#endif + + packet_length &= 0x07ff; /* mask off top bits */ + + PRINTK2("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ); + + if ( !(status & RS_ERRORS ) ){ + /* Adjust for having already read the first two words */ + packet_length -= 4; /*4; */ + + + /* set odd length for bug in LAN91C111, */ + /* which never sets RS_ODDFRAME */ + /* TODO ? */ + + +#ifdef USE_32_BIT + PRINTK3(" Reading %d dwords (and %d bytes) \n", + packet_length >> 2, packet_length & 3 ); + /* QUESTION: Like in the TX routine, do I want + to send the DWORDs or the bytes first, or some + mixture. A mixture might improve already slow PIO + performance */ + SMC_insl( dev, SMC91111_DATA_REG, NetRxPackets[0], + packet_length >> 2 ); + /* read the left over bytes */ + if (packet_length & 3) { + int i; + + byte *tail = (byte *)(NetRxPackets[0] + + (packet_length & ~3)); + dword leftover = SMC_inl(dev, SMC91111_DATA_REG); + for (i=0; i<(packet_length & 3); i++) + *tail++ = (byte) (leftover >> (8*i)) & 0xff; + } +#else + PRINTK3(" Reading %d words and %d byte(s) \n", + (packet_length >> 1 ), packet_length & 1 ); + SMC_insw(dev, SMC91111_DATA_REG , NetRxPackets[0], + packet_length >> 1); + +#endif /* USE_32_BIT */ + +#if SMC_DEBUG > 2 + printf("Receiving Packet\n"); + print_packet( NetRxPackets[0], packet_length ); +#endif + } else { + /* error ... */ + /* TODO ? */ + is_error = 1; + } + + while ( SMC_inw( dev, MMU_CMD_REG ) & MC_BUSY ) + udelay(1); /* Wait until not busy */ + + /* error or good, tell the card to get rid of this packet */ + SMC_outw( dev, MC_RELEASE, MMU_CMD_REG ); + + while ( SMC_inw( dev, MMU_CMD_REG ) & MC_BUSY ) + udelay(1); /* Wait until not busy */ + + /* restore saved registers */ +#ifndef CONFIG_XAENIAX + SMC_outb( dev, saved_pnr, PN_REG ); +#else + /* On Xaeniax board, we can't use SMC_outb here because that way + * the Allocate MMU command will end up written to the command register + * as well, which will lead to a problem. + */ + SMC_outl( dev, saved_pnr << 16, 0); +#endif + SMC_outw( dev, saved_ptr, PTR_REG ); + + if (!is_error) { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[0], packet_length); + return packet_length; + } else { + return 0; + } + +} + + +#if 0 +/*------------------------------------------------------------ + . Modify a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static word smc_modify_regbit(struct eth_device *dev, int bank, int ioaddr, int reg, + unsigned int bit, int val) +{ + word regval; + + SMC_SELECT_BANK( dev, bank ); + + regval = SMC_inw( dev, reg ); + if (val) + regval |= bit; + else + regval &= ~bit; + + SMC_outw( dev, regval, 0 ); + return(regval); +} + + +/*------------------------------------------------------------ + . Retrieve a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static int smc_get_regbit(struct eth_device *dev, int bank, int ioaddr, int reg, unsigned int bit) +{ + SMC_SELECT_BANK( dev, bank ); + if ( SMC_inw( dev, reg ) & bit) + return(1); + else + return(0); +} + + +/*------------------------------------------------------------ + . Modify a LAN91C111 register (word access only) + .-------------------------------------------------------------*/ +static void smc_modify_reg(struct eth_device *dev, int bank, int ioaddr, int reg, word val) +{ + SMC_SELECT_BANK( dev, bank ); + SMC_outw( dev, val, reg ); +} + + +/*------------------------------------------------------------ + . Retrieve a LAN91C111 register (word access only) + .-------------------------------------------------------------*/ +static int smc_get_reg(struct eth_device *dev, int bank, int ioaddr, int reg) +{ + SMC_SELECT_BANK( dev, bank ); + return(SMC_inw( dev, reg )); +} + +#endif /* 0 */ + +/*---PHY CONTROL AND CONFIGURATION----------------------------------------- */ + +#if (SMC_DEBUG > 2 ) + +/*------------------------------------------------------------ + . Debugging function for viewing MII Management serial bitstream + .-------------------------------------------------------------*/ +static void smc_dump_mii_stream (byte * bits, int size) +{ + int i; + + printf ("BIT#:"); + for (i = 0; i < size; ++i) { + printf ("%d", i % 10); + } + + printf ("\nMDOE:"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDOE) + printf ("1"); + else + printf ("0"); + } + + printf ("\nMDO :"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDO) + printf ("1"); + else + printf ("0"); + } + + printf ("\nMDI :"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDI) + printf ("1"); + else + printf ("0"); + } + + printf ("\n"); +} +#endif + +/*------------------------------------------------------------ + . Reads a register from the MII Management serial interface + .-------------------------------------------------------------*/ +#ifndef CONFIG_SMC91111_EXT_PHY +static word smc_read_phy_register (struct eth_device *dev, byte phyreg) +{ + int oldBank; + int i; + byte mask; + word mii_reg; + byte bits[64]; + int clk_idx = 0; + int input_idx; + word phydata; + byte phyaddr = SMC_PHY_ADDR; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Start code <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Read command <10> */ + bits[clk_idx++] = MII_MDOE | MII_MDO; + bits[clk_idx++] = MII_MDOE; + + /* Output the PHY address, msb first */ + mask = (byte) 0x10; + for (i = 0; i < 5; ++i) { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Output the phy register number, msb first */ + mask = (byte) 0x10; + for (i = 0; i < 5; ++i) { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Tristate and turnaround (2 bit times) */ + bits[clk_idx++] = 0; + /*bits[clk_idx++] = 0; */ + + /* Input starts at this bit time */ + input_idx = clk_idx; + + /* Will input 16 bits */ + for (i = 0; i < 16; ++i) + bits[clk_idx++] = 0; + + /* Final clock bit */ + bits[clk_idx++] = 0; + + /* Save the current bank */ + oldBank = SMC_inw (dev, BANK_SELECT); + + /* Select bank 3 */ + SMC_SELECT_BANK (dev, 3); + + /* Get the current MII register value */ + mii_reg = SMC_inw (dev, MII_REG); + + /* Turn off all MII Interface bits */ + mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO); + + /* Clock all 64 cycles */ + for (i = 0; i < sizeof bits; ++i) { + /* Clock Low - output data */ + SMC_outw (dev, mii_reg | bits[i], MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + + + /* Clock Hi - input data */ + SMC_outw (dev, mii_reg | bits[i] | MII_MCLK, MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + bits[i] |= SMC_inw (dev, MII_REG) & MII_MDI; + } + + /* Return to idle state */ + /* Set clock to low, data to low, and output tristated */ + SMC_outw (dev, mii_reg, MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + + /* Restore original bank select */ + SMC_SELECT_BANK (dev, oldBank); + + /* Recover input data */ + phydata = 0; + for (i = 0; i < 16; ++i) { + phydata <<= 1; + + if (bits[input_idx++] & MII_MDI) + phydata |= 0x0001; + } + +#if (SMC_DEBUG > 2 ) + printf ("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); + smc_dump_mii_stream (bits, sizeof bits); +#endif + + return (phydata); +} + + +/*------------------------------------------------------------ + . Writes a register to the MII Management serial interface + .-------------------------------------------------------------*/ +static void smc_write_phy_register (struct eth_device *dev, byte phyreg, + word phydata) +{ + int oldBank; + int i; + word mask; + word mii_reg; + byte bits[65]; + int clk_idx = 0; + byte phyaddr = SMC_PHY_ADDR; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Start code <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Write command <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Output the PHY address, msb first */ + mask = (byte) 0x10; + for (i = 0; i < 5; ++i) { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Output the phy register number, msb first */ + mask = (byte) 0x10; + for (i = 0; i < 5; ++i) { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Tristate and turnaround (2 bit times) */ + bits[clk_idx++] = 0; + bits[clk_idx++] = 0; + + /* Write out 16 bits of data, msb first */ + mask = 0x8000; + for (i = 0; i < 16; ++i) { + if (phydata & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Final clock bit (tristate) */ + bits[clk_idx++] = 0; + + /* Save the current bank */ + oldBank = SMC_inw (dev, BANK_SELECT); + + /* Select bank 3 */ + SMC_SELECT_BANK (dev, 3); + + /* Get the current MII register value */ + mii_reg = SMC_inw (dev, MII_REG); + + /* Turn off all MII Interface bits */ + mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO); + + /* Clock all cycles */ + for (i = 0; i < sizeof bits; ++i) { + /* Clock Low - output data */ + SMC_outw (dev, mii_reg | bits[i], MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + + + /* Clock Hi - input data */ + SMC_outw (dev, mii_reg | bits[i] | MII_MCLK, MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + bits[i] |= SMC_inw (dev, MII_REG) & MII_MDI; + } + + /* Return to idle state */ + /* Set clock to low, data to low, and output tristated */ + SMC_outw (dev, mii_reg, MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + + /* Restore original bank select */ + SMC_SELECT_BANK (dev, oldBank); + +#if (SMC_DEBUG > 2 ) + printf ("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); + smc_dump_mii_stream (bits, sizeof bits); +#endif +} +#endif /* !CONFIG_SMC91111_EXT_PHY */ + + +/*------------------------------------------------------------ + . Configures the specified PHY using Autonegotiation. Calls + . smc_phy_fixed() if the user has requested a certain config. + .-------------------------------------------------------------*/ +#ifndef CONFIG_SMC91111_EXT_PHY +static void smc_phy_configure (struct eth_device *dev) +{ + int timeout; + word my_phy_caps; /* My PHY capabilities */ + word my_ad_caps; /* My Advertised capabilities */ + word status = 0; /*;my status = 0 */ + + PRINTK3 ("%s: smc_program_phy()\n", SMC_DEV_NAME); + + /* Reset the PHY, setting all other bits to zero */ + smc_write_phy_register (dev, PHY_CNTL_REG, PHY_CNTL_RST); + + /* Wait for the reset to complete, or time out */ + timeout = 6; /* Wait up to 3 seconds */ + while (timeout--) { + if (!(smc_read_phy_register (dev, PHY_CNTL_REG) + & PHY_CNTL_RST)) { + /* reset complete */ + break; + } + + mdelay(500); /* wait 500 millisecs */ + } + + if (timeout < 1) { + printf ("%s:PHY reset timed out\n", SMC_DEV_NAME); + goto smc_phy_configure_exit; + } + + /* Read PHY Register 18, Status Output */ + /* lp->lastPhy18 = smc_read_phy_register(PHY_INT_REG); */ + + /* Enable PHY Interrupts (for register 18) */ + /* Interrupts listed here are disabled */ + smc_write_phy_register (dev, PHY_MASK_REG, 0xffff); + + /* Configure the Receive/Phy Control register */ + SMC_SELECT_BANK (dev, 0); + SMC_outw (dev, RPC_DEFAULT, RPC_REG); + + /* Copy our capabilities from PHY_STAT_REG to PHY_AD_REG */ + my_phy_caps = smc_read_phy_register (dev, PHY_STAT_REG); + my_ad_caps = PHY_AD_CSMA; /* I am CSMA capable */ + + if (my_phy_caps & PHY_STAT_CAP_T4) + my_ad_caps |= PHY_AD_T4; + + if (my_phy_caps & PHY_STAT_CAP_TXF) + my_ad_caps |= PHY_AD_TX_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TXH) + my_ad_caps |= PHY_AD_TX_HDX; + + if (my_phy_caps & PHY_STAT_CAP_TF) + my_ad_caps |= PHY_AD_10_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TH) + my_ad_caps |= PHY_AD_10_HDX; + + /* Update our Auto-Neg Advertisement Register */ + smc_write_phy_register (dev, PHY_AD_REG, my_ad_caps); + + /* Read the register back. Without this, it appears that when */ + /* auto-negotiation is restarted, sometimes it isn't ready and */ + /* the link does not come up. */ + smc_read_phy_register(dev, PHY_AD_REG); + + PRINTK2 ("%s: phy caps=%x\n", SMC_DEV_NAME, my_phy_caps); + PRINTK2 ("%s: phy advertised caps=%x\n", SMC_DEV_NAME, my_ad_caps); + + /* Restart auto-negotiation process in order to advertise my caps */ + smc_write_phy_register (dev, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST); + + /* Wait for the auto-negotiation to complete. This may take from */ + /* 2 to 3 seconds. */ + /* Wait for the reset to complete, or time out */ + timeout = CONFIG_SMC_AUTONEG_TIMEOUT * 2; + while (timeout--) { + + status = smc_read_phy_register (dev, PHY_STAT_REG); + if (status & PHY_STAT_ANEG_ACK) { + /* auto-negotiate complete */ + break; + } + + mdelay(500); /* wait 500 millisecs */ + + /* Restart auto-negotiation if remote fault */ + if (status & PHY_STAT_REM_FLT) { + printf ("%s: PHY remote fault detected\n", + SMC_DEV_NAME); + + /* Restart auto-negotiation */ + printf ("%s: PHY restarting auto-negotiation\n", + SMC_DEV_NAME); + smc_write_phy_register (dev, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | + PHY_CNTL_ANEG_RST | + PHY_CNTL_SPEED | + PHY_CNTL_DPLX); + } + } + + if (timeout < 1) { + printf ("%s: PHY auto-negotiate timed out\n", SMC_DEV_NAME); + } + + /* Fail if we detected an auto-negotiate remote fault */ + if (status & PHY_STAT_REM_FLT) { + printf ("%s: PHY remote fault detected\n", SMC_DEV_NAME); + } + + /* Re-Configure the Receive/Phy Control register */ + SMC_outw (dev, RPC_DEFAULT, RPC_REG); + +smc_phy_configure_exit: ; + +} +#endif /* !CONFIG_SMC91111_EXT_PHY */ + + +#if SMC_DEBUG > 2 +static void print_packet( byte * buf, int length ) +{ + int i; + int remainder; + int lines; + + printf("Packet of length %d \n", length ); + +#if SMC_DEBUG > 3 + lines = length / 16; + remainder = length % 16; + + for ( i = 0; i < lines ; i ++ ) { + int cur; + + for ( cur = 0; cur < 8; cur ++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printf("%02x%02x ", a, b ); + } + printf("\n"); + } + for ( i = 0; i < remainder/2 ; i++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printf("%02x%02x ", a, b ); + } + printf("\n"); +#endif +} +#endif + +int smc91111_initialize(u8 dev_num, int base_addr) +{ + struct smc91111_priv *priv; + struct eth_device *dev; + int i; + + priv = malloc(sizeof(*priv)); + if (!priv) + return 0; + dev = malloc(sizeof(*dev)); + if (!dev) { + free(priv); + return 0; + } + + memset(dev, 0, sizeof(*dev)); + priv->dev_num = dev_num; + dev->priv = priv; + dev->iobase = base_addr; + + swap_to(ETHERNET); + SMC_SELECT_BANK(dev, 1); + for (i = 0; i < 6; ++i) + dev->enetaddr[i] = SMC_inb(dev, (ADDR0_REG + i)); + swap_to(FLASH); + + dev->init = smc_init; + dev->halt = smc_halt; + dev->send = smc_send; + dev->recv = smc_rcv; + dev->write_hwaddr = smc_write_hwaddr; + sprintf(dev->name, "%s-%hu", SMC_DEV_NAME, dev_num); + + eth_register(dev); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/smc91111.h b/qemu/roms/u-boot/drivers/net/smc91111.h new file mode 100644 index 000000000..d9135cb57 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/smc91111.h @@ -0,0 +1,789 @@ +/*------------------------------------------------------------------------ + . smc91111.h - macros for the LAN91C111 Ethernet Driver + . + . (C) Copyright 2002 + . Sysgo Real-Time Solutions, GmbH + . Rolf Offermanns + . Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + . Developed by Simple Network Magic Corporation (SNMC) + . Copyright (C) 1996 by Erik Stahlman (ES) + . + * SPDX-License-Identifier: GPL-2.0+ + . + . This file contains register information and access macros for + . the LAN91C111 single chip ethernet controller. It is a modified + . version of the smc9194.h file. + . + . Information contained in this file was obtained from the LAN91C111 + . manual from SMC. To get a copy, if you really want one, you can find + . information under www.smsc.com. + . + . Authors + . Erik Stahlman ( erik@vt.edu ) + . Daris A Nevil ( dnevil@snmc.com ) + . + . History + . 03/16/01 Daris A Nevil Modified for use with LAN91C111 device + . + ---------------------------------------------------------------------------*/ +#ifndef _SMC91111_H_ +#define _SMC91111_H_ + +#include +#include + +/* + * This function may be called by the board specific initialisation code + * in order to override the default mac address. + */ + +void smc_set_mac_addr (const unsigned char *addr); + + +/* I want some simple types */ + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long int dword; + +struct smc91111_priv{ + u8 dev_num; +}; + +/* + . DEBUGGING LEVELS + . + . 0 for normal operation + . 1 for slightly more details + . >2 for various levels of increasingly useless information + . 2 for interrupt tracking, status flags + . 3 for packet info + . 4 for complete packet dumps +*/ +/*#define SMC_DEBUG 0 */ + +/* Because of bank switching, the LAN91xxx uses only 16 I/O ports */ + +#define SMC_IO_EXTENT 16 + +#ifdef CONFIG_CPU_PXA25X + +#ifdef CONFIG_XSENGINE +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+((r)<<1)))) +#define SMC_inw(a,r) (*((volatile word *)((a)->iobase+((r)<<1)))) +#define SMC_inb(a,p) ({ \ + unsigned int __p = (unsigned int)((a)->iobase + ((p)<<1)); \ + unsigned int __v = *(volatile unsigned short *)((__p) & ~2); \ + if (__p & 2) __v >>= 8; \ + else __v &= 0xff; \ + __v; }) +#elif defined(CONFIG_XAENIAX) +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+(r)))) +#define SMC_inw(a,z) ({ \ + unsigned int __p = (unsigned int)((a)->iobase + (z)); \ + unsigned int __v = *(volatile unsigned int *)((__p) & ~3); \ + if (__p & 3) __v >>= 16; \ + else __v &= 0xffff; \ + __v; }) +#define SMC_inb(a,p) ({ \ + unsigned int ___v = SMC_inw((a),(p) & ~1); \ + if ((p) & 1) ___v >>= 8; \ + else ___v &= 0xff; \ + ___v; }) +#else +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+(r)))) +#define SMC_inw(a,r) (*((volatile word *)((a)->iobase+(r)))) +#define SMC_inb(a,p) ({ \ + unsigned int __p = (unsigned int)((a)->iobase + (p)); \ + unsigned int __v = *(volatile unsigned short *)((__p) & ~1); \ + if (__p & 1) __v >>= 8; \ + else __v &= 0xff; \ + __v; }) +#endif + +#ifdef CONFIG_XSENGINE +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r<<1))) = d) +#define SMC_outw(a,d,r) (*((volatile word *)((a)->iobase+(r<<1))) = d) +#elif defined (CONFIG_XAENIAX) +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r))) = d) +#define SMC_outw(a,d,p) ({ \ + dword __dwo = SMC_inl((a),(p) & ~3); \ + dword __dwn = (word)(d); \ + __dwo &= ((p) & 3) ? 0x0000ffff : 0xffff0000; \ + __dwo |= ((p) & 3) ? __dwn << 16 : __dwn; \ + SMC_outl((a), __dwo, (p) & ~3); \ +}) +#else +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r))) = d) +#define SMC_outw(a,d,r) (*((volatile word *)((a)->iobase+(r))) = d) +#endif + +#define SMC_outb(a,d,r) ({ word __d = (byte)(d); \ + word __w = SMC_inw((a),(r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw((a),__w,(r)&~1); \ + }) + +#define SMC_outsl(a,r,b,l) ({ int __i; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outl((a), *(__b2 + __i), r); \ + } \ + }) + +#define SMC_outsw(a,r,b,l) ({ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw((a), *(__b2 + __i), r); \ + } \ + }) + +#define SMC_insl(a,r,b,l) ({ int __i ; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inl((a),(r)); \ + SMC_inl((a),0); \ + }; \ + }) + +#define SMC_insw(a,r,b,l) ({ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw((a),(r)); \ + SMC_inw((a),0); \ + }; \ + }) + +#define SMC_insb(a,r,b,l) ({ int __i ; \ + byte *__b2; \ + __b2 = (byte *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inb((a),(r)); \ + SMC_inb((a),0); \ + }; \ + }) + +#elif defined(CONFIG_LEON) /* if not CONFIG_CPU_PXA25X */ + +#define SMC_LEON_SWAP16(_x_) ({ word _x = (_x_); ((_x << 8) | (_x >> 8)); }) + +#define SMC_LEON_SWAP32(_x_) \ + ({ dword _x = (_x_); \ + ((_x << 24) | \ + ((0x0000FF00UL & _x) << 8) | \ + ((0x00FF0000UL & _x) >> 8) | \ + (_x >> 24)); }) + +#define SMC_inl(a,r) (SMC_LEON_SWAP32((*(volatile dword *)((a)->iobase+((r)<<0))))) +#define SMC_inl_nosw(a,r) ((*(volatile dword *)((a)->iobase+((r)<<0)))) +#define SMC_inw(a,r) (SMC_LEON_SWAP16((*(volatile word *)((a)->iobase+((r)<<0))))) +#define SMC_inw_nosw(a,r) ((*(volatile word *)((a)->iobase+((r)<<0)))) +#define SMC_inb(a,p) ({ \ + word ___v = SMC_inw((a),(p) & ~1); \ + if ((p) & 1) ___v >>= 8; \ + else ___v &= 0xff; \ + ___v; }) + +#define SMC_outl(a,d,r) (*(volatile dword *)((a)->iobase+((r)<<0))=SMC_LEON_SWAP32(d)) +#define SMC_outl_nosw(a,d,r) (*(volatile dword *)((a)->iobase+((r)<<0))=(d)) +#define SMC_outw(a,d,r) (*(volatile word *)((a)->iobase+((r)<<0))=SMC_LEON_SWAP16(d)) +#define SMC_outw_nosw(a,d,r) (*(volatile word *)((a)->iobase+((r)<<0))=(d)) +#define SMC_outb(a,d,r) do{ word __d = (byte)(d); \ + word __w = SMC_inw((a),(r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw((a),__w,(r)&~1); \ + }while(0) +#define SMC_outsl(a,r,b,l) do{ int __i; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outl_nosw((a), *(__b2 + __i), r); \ + } \ + }while(0) +#define SMC_outsw(a,r,b,l) do{ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw_nosw((a), *(__b2 + __i), r); \ + } \ + }while(0) +#define SMC_insl(a,r,b,l) do{ int __i ; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inl_nosw((a),(r)); \ + }; \ + }while(0) + +#define SMC_insw(a,r,b,l) do{ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw_nosw((a),(r)); \ + }; \ + }while(0) + +#define SMC_insb(a,r,b,l) do{ int __i ; \ + byte *__b2; \ + __b2 = (byte *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inb((a),(r)); \ + }; \ + }while(0) + +#else /* if not CONFIG_CPU_PXA25X and not CONFIG_LEON */ + +#ifndef CONFIG_SMC_USE_IOFUNCS /* these macros don't work on some boards */ +/* + * We have only 16 Bit PCMCIA access on Socket 0 + */ + +#ifdef CONFIG_ADNPESC1 +#define SMC_inw(a,r) (*((volatile word *)((a)->iobase+((r)<<1)))) +#elif CONFIG_BLACKFIN +#define SMC_inw(a,r) ({ word __v = (*((volatile word *)((a)->iobase+(r)))); SSYNC(); __v;}) +#elif CONFIG_ARM64 +#define SMC_inw(a, r) (*((volatile word*)((a)->iobase+((dword)(r))))) +#else +#define SMC_inw(a, r) (*((volatile word*)((a)->iobase+(r)))) +#endif +#define SMC_inb(a,r) (((r)&1) ? SMC_inw((a),(r)&~1)>>8 : SMC_inw((a),(r)&0xFF)) + +#ifdef CONFIG_ADNPESC1 +#define SMC_outw(a,d,r) (*((volatile word *)((a)->iobase+((r)<<1))) = d) +#elif CONFIG_BLACKFIN +#define SMC_outw(a, d, r) \ + ({ (*((volatile word*)((a)->iobase+((r)))) = d); \ + SSYNC(); \ + }) +#elif CONFIG_ARM64 +#define SMC_outw(a, d, r) \ + (*((volatile word*)((a)->iobase+((dword)(r)))) = d) +#else +#define SMC_outw(a, d, r) \ + (*((volatile word*)((a)->iobase+(r))) = d) +#endif +#define SMC_outb(a,d,r) ({ word __d = (byte)(d); \ + word __w = SMC_inw((a),(r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw((a),__w,(r)&~1); \ + }) +#if 0 +#define SMC_outsw(a,r,b,l) outsw((a)->iobase+(r), (b), (l)) +#else +#define SMC_outsw(a,r,b,l) ({ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw((a), *(__b2 + __i), r); \ + } \ + }) +#endif + +#if 0 +#define SMC_insw(a,r,b,l) insw((a)->iobase+(r), (b), (l)) +#else +#define SMC_insw(a,r,b,l) ({ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw((a),(r)); \ + SMC_inw((a),0); \ + }; \ + }) +#endif + +#endif /* CONFIG_SMC_USE_IOFUNCS */ + +#if defined(CONFIG_SMC_USE_32_BIT) + +#ifdef CONFIG_XSENGINE +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+(r<<1)))) +#else +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+(r)))) +#endif + +#define SMC_insl(a,r,b,l) ({ int __i ; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inl((a),(r)); \ + SMC_inl((a),0); \ + }; \ + }) + +#ifdef CONFIG_XSENGINE +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r<<1))) = d) +#else +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r))) = d) +#endif +#define SMC_outsl(a,r,b,l) ({ int __i; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outl((a), *(__b2 + __i), r); \ + } \ + }) + +#endif /* CONFIG_SMC_USE_32_BIT */ + +#endif + +/*--------------------------------------------------------------- + . + . A description of the SMSC registers is probably in order here, + . although for details, the SMC datasheet is invaluable. + . + . Basically, the chip has 4 banks of registers ( 0 to 3 ), which + . are accessed by writing a number into the BANK_SELECT register + . ( I also use a SMC_SELECT_BANK macro for this ). + . + . The banks are configured so that for most purposes, bank 2 is all + . that is needed for simple run time tasks. + -----------------------------------------------------------------------*/ + +/* + . Bank Select Register: + . + . yyyy yyyy 0000 00xx + . xx = bank number + . yyyy yyyy = 0x33, for identification purposes. +*/ +#define BANK_SELECT 14 + +/* Transmit Control Register */ +/* BANK 0 */ +#define TCR_REG 0x0000 /* transmit control register */ +#define TCR_ENABLE 0x0001 /* When 1 we can transmit */ +#define TCR_LOOP 0x0002 /* Controls output pin LBK */ +#define TCR_FORCOL 0x0004 /* When 1 will force a collision */ +#define TCR_PAD_EN 0x0080 /* When 1 will pad tx frames < 64 bytes w/0 */ +#define TCR_NOCRC 0x0100 /* When 1 will not append CRC to tx frames */ +#define TCR_MON_CSN 0x0400 /* When 1 tx monitors carrier */ +#define TCR_FDUPLX 0x0800 /* When 1 enables full duplex operation */ +#define TCR_STP_SQET 0x1000 /* When 1 stops tx if Signal Quality Error */ +#define TCR_EPH_LOOP 0x2000 /* When 1 enables EPH block loopback */ +#define TCR_SWFDUP 0x8000 /* When 1 enables Switched Full Duplex mode */ + +#define TCR_CLEAR 0 /* do NOTHING */ +/* the default settings for the TCR register : */ +/* QUESTION: do I want to enable padding of short packets ? */ +#define TCR_DEFAULT TCR_ENABLE + + +/* EPH Status Register */ +/* BANK 0 */ +#define EPH_STATUS_REG 0x0002 +#define ES_TX_SUC 0x0001 /* Last TX was successful */ +#define ES_SNGL_COL 0x0002 /* Single collision detected for last tx */ +#define ES_MUL_COL 0x0004 /* Multiple collisions detected for last tx */ +#define ES_LTX_MULT 0x0008 /* Last tx was a multicast */ +#define ES_16COL 0x0010 /* 16 Collisions Reached */ +#define ES_SQET 0x0020 /* Signal Quality Error Test */ +#define ES_LTXBRD 0x0040 /* Last tx was a broadcast */ +#define ES_TXDEFR 0x0080 /* Transmit Deferred */ +#define ES_LATCOL 0x0200 /* Late collision detected on last tx */ +#define ES_LOSTCARR 0x0400 /* Lost Carrier Sense */ +#define ES_EXC_DEF 0x0800 /* Excessive Deferral */ +#define ES_CTR_ROL 0x1000 /* Counter Roll Over indication */ +#define ES_LINK_OK 0x4000 /* Driven by inverted value of nLNK pin */ +#define ES_TXUNRN 0x8000 /* Tx Underrun */ + + +/* Receive Control Register */ +/* BANK 0 */ +#define RCR_REG 0x0004 +#define RCR_RX_ABORT 0x0001 /* Set if a rx frame was aborted */ +#define RCR_PRMS 0x0002 /* Enable promiscuous mode */ +#define RCR_ALMUL 0x0004 /* When set accepts all multicast frames */ +#define RCR_RXEN 0x0100 /* IFF this is set, we can receive packets */ +#define RCR_STRIP_CRC 0x0200 /* When set strips CRC from rx packets */ +#define RCR_ABORT_ENB 0x0200 /* When set will abort rx on collision */ +#define RCR_FILT_CAR 0x0400 /* When set filters leading 12 bit s of carrier */ +#define RCR_SOFTRST 0x8000 /* resets the chip */ + +/* the normal settings for the RCR register : */ +#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN) +#define RCR_CLEAR 0x0 /* set it to a base state */ + +/* Counter Register */ +/* BANK 0 */ +#define COUNTER_REG 0x0006 + +/* Memory Information Register */ +/* BANK 0 */ +#define MIR_REG 0x0008 + +/* Receive/Phy Control Register */ +/* BANK 0 */ +#define RPC_REG 0x000A +#define RPC_SPEED 0x2000 /* When 1 PHY is in 100Mbps mode. */ +#define RPC_DPLX 0x1000 /* When 1 PHY is in Full-Duplex Mode */ +#define RPC_ANEG 0x0800 /* When 1 PHY is in Auto-Negotiate Mode */ +#define RPC_LSXA_SHFT 5 /* Bits to shift LS2A,LS1A,LS0A to lsb */ +#define RPC_LSXB_SHFT 2 /* Bits to get LS2B,LS1B,LS0B to lsb */ +#define RPC_LED_100_10 (0x00) /* LED = 100Mbps OR's with 10Mbps link detect */ +#define RPC_LED_RES (0x01) /* LED = Reserved */ +#define RPC_LED_10 (0x02) /* LED = 10Mbps link detect */ +#define RPC_LED_FD (0x03) /* LED = Full Duplex Mode */ +#define RPC_LED_TX_RX (0x04) /* LED = TX or RX packet occurred */ +#define RPC_LED_100 (0x05) /* LED = 100Mbps link dectect */ +#define RPC_LED_TX (0x06) /* LED = TX packet occurred */ +#define RPC_LED_RX (0x07) /* LED = RX packet occurred */ +#if defined(CONFIG_DK1C20) || defined(CONFIG_DK1S10) +/* buggy schematic: LEDa -> yellow, LEDb --> green */ +#define RPC_DEFAULT ( RPC_SPEED | RPC_DPLX | RPC_ANEG \ + | (RPC_LED_TX_RX << RPC_LSXA_SHFT) \ + | (RPC_LED_100_10 << RPC_LSXB_SHFT) ) +#elif defined(CONFIG_ADNPESC1) +/* SSV ADNP/ESC1 has only one LED: LEDa -> Rx/Tx indicator */ +#define RPC_DEFAULT ( RPC_SPEED | RPC_DPLX | RPC_ANEG \ + | (RPC_LED_TX_RX << RPC_LSXA_SHFT) \ + | (RPC_LED_100_10 << RPC_LSXB_SHFT) ) +#else +/* SMSC reference design: LEDa --> green, LEDb --> yellow */ +#define RPC_DEFAULT ( RPC_SPEED | RPC_DPLX | RPC_ANEG \ + | (RPC_LED_100_10 << RPC_LSXA_SHFT) \ + | (RPC_LED_TX_RX << RPC_LSXB_SHFT) ) +#endif + +/* Bank 0 0x000C is reserved */ + +/* Bank Select Register */ +/* All Banks */ +#define BSR_REG 0x000E + + +/* Configuration Reg */ +/* BANK 1 */ +#define CONFIG_REG 0x0000 +#define CONFIG_EXT_PHY 0x0200 /* 1=external MII, 0=internal Phy */ +#define CONFIG_GPCNTRL 0x0400 /* Inverse value drives pin nCNTRL */ +#define CONFIG_NO_WAIT 0x1000 /* When 1 no extra wait states on ISA bus */ +#define CONFIG_EPH_POWER_EN 0x8000 /* When 0 EPH is placed into low power mode. */ + +/* Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low */ +#define CONFIG_DEFAULT (CONFIG_EPH_POWER_EN) + + +/* Base Address Register */ +/* BANK 1 */ +#define BASE_REG 0x0002 + + +/* Individual Address Registers */ +/* BANK 1 */ +#define ADDR0_REG 0x0004 +#define ADDR1_REG 0x0006 +#define ADDR2_REG 0x0008 + + +/* General Purpose Register */ +/* BANK 1 */ +#define GP_REG 0x000A + + +/* Control Register */ +/* BANK 1 */ +#define CTL_REG 0x000C +#define CTL_RCV_BAD 0x4000 /* When 1 bad CRC packets are received */ +#define CTL_AUTO_RELEASE 0x0800 /* When 1 tx pages are released automatically */ +#define CTL_LE_ENABLE 0x0080 /* When 1 enables Link Error interrupt */ +#define CTL_CR_ENABLE 0x0040 /* When 1 enables Counter Rollover interrupt */ +#define CTL_TE_ENABLE 0x0020 /* When 1 enables Transmit Error interrupt */ +#define CTL_EEPROM_SELECT 0x0004 /* Controls EEPROM reload & store */ +#define CTL_RELOAD 0x0002 /* When set reads EEPROM into registers */ +#define CTL_STORE 0x0001 /* When set stores registers into EEPROM */ +#define CTL_DEFAULT (0x1A10) /* Autorelease enabled*/ + +/* MMU Command Register */ +/* BANK 2 */ +#define MMU_CMD_REG 0x0000 +#define MC_BUSY 1 /* When 1 the last release has not completed */ +#define MC_NOP (0<<5) /* No Op */ +#define MC_ALLOC (1<<5) /* OR with number of 256 byte packets */ +#define MC_RESET (2<<5) /* Reset MMU to initial state */ +#define MC_REMOVE (3<<5) /* Remove the current rx packet */ +#define MC_RELEASE (4<<5) /* Remove and release the current rx packet */ +#define MC_FREEPKT (5<<5) /* Release packet in PNR register */ +#define MC_ENQUEUE (6<<5) /* Enqueue the packet for transmit */ +#define MC_RSTTXFIFO (7<<5) /* Reset the TX FIFOs */ + + +/* Packet Number Register */ +/* BANK 2 */ +#define PN_REG 0x0002 + + +/* Allocation Result Register */ +/* BANK 2 */ +#define AR_REG 0x0003 +#define AR_FAILED 0x80 /* Alocation Failed */ + + +/* RX FIFO Ports Register */ +/* BANK 2 */ +#define RXFIFO_REG 0x0004 /* Must be read as a word */ +#define RXFIFO_REMPTY 0x8000 /* RX FIFO Empty */ + + +/* TX FIFO Ports Register */ +/* BANK 2 */ +#define TXFIFO_REG RXFIFO_REG /* Must be read as a word */ +#define TXFIFO_TEMPTY 0x80 /* TX FIFO Empty */ + + +/* Pointer Register */ +/* BANK 2 */ +#define PTR_REG 0x0006 +#define PTR_RCV 0x8000 /* 1=Receive area, 0=Transmit area */ +#define PTR_AUTOINC 0x4000 /* Auto increment the pointer on each access */ +#define PTR_READ 0x2000 /* When 1 the operation is a read */ +#define PTR_NOTEMPTY 0x0800 /* When 1 _do not_ write fifo DATA REG */ + + +/* Data Register */ +/* BANK 2 */ +#define SMC91111_DATA_REG 0x0008 + + +/* Interrupt Status/Acknowledge Register */ +/* BANK 2 */ +#define SMC91111_INT_REG 0x000C + + +/* Interrupt Mask Register */ +/* BANK 2 */ +#define IM_REG 0x000D +#define IM_MDINT 0x80 /* PHY MI Register 18 Interrupt */ +#define IM_ERCV_INT 0x40 /* Early Receive Interrupt */ +#define IM_EPH_INT 0x20 /* Set by Etheret Protocol Handler section */ +#define IM_RX_OVRN_INT 0x10 /* Set by Receiver Overruns */ +#define IM_ALLOC_INT 0x08 /* Set when allocation request is completed */ +#define IM_TX_EMPTY_INT 0x04 /* Set if the TX FIFO goes empty */ +#define IM_TX_INT 0x02 /* Transmit Interrrupt */ +#define IM_RCV_INT 0x01 /* Receive Interrupt */ + + +/* Multicast Table Registers */ +/* BANK 3 */ +#define MCAST_REG1 0x0000 +#define MCAST_REG2 0x0002 +#define MCAST_REG3 0x0004 +#define MCAST_REG4 0x0006 + + +/* Management Interface Register (MII) */ +/* BANK 3 */ +#define MII_REG 0x0008 +#define MII_MSK_CRS100 0x4000 /* Disables CRS100 detection during tx half dup */ +#define MII_MDOE 0x0008 /* MII Output Enable */ +#define MII_MCLK 0x0004 /* MII Clock, pin MDCLK */ +#define MII_MDI 0x0002 /* MII Input, pin MDI */ +#define MII_MDO 0x0001 /* MII Output, pin MDO */ + + +/* Revision Register */ +/* BANK 3 */ +#define REV_REG 0x000A /* ( hi: chip id low: rev # ) */ + + +/* Early RCV Register */ +/* BANK 3 */ +/* this is NOT on SMC9192 */ +#define ERCV_REG 0x000C +#define ERCV_RCV_DISCRD 0x0080 /* When 1 discards a packet being received */ +#define ERCV_THRESHOLD 0x001F /* ERCV Threshold Mask */ + +/* External Register */ +/* BANK 7 */ +#define EXT_REG 0x0000 + + +#define CHIP_9192 3 +#define CHIP_9194 4 +#define CHIP_9195 5 +#define CHIP_9196 6 +#define CHIP_91100 7 +#define CHIP_91100FD 8 +#define CHIP_91111FD 9 + +#if 0 +static const char * chip_ids[ 15 ] = { + NULL, NULL, NULL, + /* 3 */ "SMC91C90/91C92", + /* 4 */ "SMC91C94", + /* 5 */ "SMC91C95", + /* 6 */ "SMC91C96", + /* 7 */ "SMC91C100", + /* 8 */ "SMC91C100FD", + /* 9 */ "SMC91C111", + NULL, NULL, + NULL, NULL, NULL}; +#endif + +/* + . Transmit status bits +*/ +#define TS_SUCCESS 0x0001 +#define TS_LOSTCAR 0x0400 +#define TS_LATCOL 0x0200 +#define TS_16COL 0x0010 + +/* + . Receive status bits +*/ +#define RS_ALGNERR 0x8000 +#define RS_BRODCAST 0x4000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 /* bug: the LAN91C111 never sets this on receive */ +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + + +/* PHY Types */ +enum { + PHY_LAN83C183 = 1, /* LAN91C111 Internal PHY */ + PHY_LAN83C180 +}; + + +/* PHY Register Addresses (LAN91C111 Internal PHY) */ + +/* PHY Control Register */ +#define PHY_CNTL_REG 0x00 +#define PHY_CNTL_RST 0x8000 /* 1=PHY Reset */ +#define PHY_CNTL_LPBK 0x4000 /* 1=PHY Loopback */ +#define PHY_CNTL_SPEED 0x2000 /* 1=100Mbps, 0=10Mpbs */ +#define PHY_CNTL_ANEG_EN 0x1000 /* 1=Enable Auto negotiation */ +#define PHY_CNTL_PDN 0x0800 /* 1=PHY Power Down mode */ +#define PHY_CNTL_MII_DIS 0x0400 /* 1=MII 4 bit interface disabled */ +#define PHY_CNTL_ANEG_RST 0x0200 /* 1=Reset Auto negotiate */ +#define PHY_CNTL_DPLX 0x0100 /* 1=Full Duplex, 0=Half Duplex */ +#define PHY_CNTL_COLTST 0x0080 /* 1= MII Colision Test */ + +/* PHY Status Register */ +#define PHY_STAT_REG 0x01 +#define PHY_STAT_CAP_T4 0x8000 /* 1=100Base-T4 capable */ +#define PHY_STAT_CAP_TXF 0x4000 /* 1=100Base-X full duplex capable */ +#define PHY_STAT_CAP_TXH 0x2000 /* 1=100Base-X half duplex capable */ +#define PHY_STAT_CAP_TF 0x1000 /* 1=10Mbps full duplex capable */ +#define PHY_STAT_CAP_TH 0x0800 /* 1=10Mbps half duplex capable */ +#define PHY_STAT_CAP_SUPR 0x0040 /* 1=recv mgmt frames with not preamble */ +#define PHY_STAT_ANEG_ACK 0x0020 /* 1=ANEG has completed */ +#define PHY_STAT_REM_FLT 0x0010 /* 1=Remote Fault detected */ +#define PHY_STAT_CAP_ANEG 0x0008 /* 1=Auto negotiate capable */ +#define PHY_STAT_LINK 0x0004 /* 1=valid link */ +#define PHY_STAT_JAB 0x0002 /* 1=10Mbps jabber condition */ +#define PHY_STAT_EXREG 0x0001 /* 1=extended registers implemented */ + +/* PHY Identifier Registers */ +#define PHY_ID1_REG 0x02 /* PHY Identifier 1 */ +#define PHY_ID2_REG 0x03 /* PHY Identifier 2 */ + +/* PHY Auto-Negotiation Advertisement Register */ +#define PHY_AD_REG 0x04 +#define PHY_AD_NP 0x8000 /* 1=PHY requests exchange of Next Page */ +#define PHY_AD_ACK 0x4000 /* 1=got link code word from remote */ +#define PHY_AD_RF 0x2000 /* 1=advertise remote fault */ +#define PHY_AD_T4 0x0200 /* 1=PHY is capable of 100Base-T4 */ +#define PHY_AD_TX_FDX 0x0100 /* 1=PHY is capable of 100Base-TX FDPLX */ +#define PHY_AD_TX_HDX 0x0080 /* 1=PHY is capable of 100Base-TX HDPLX */ +#define PHY_AD_10_FDX 0x0040 /* 1=PHY is capable of 10Base-T FDPLX */ +#define PHY_AD_10_HDX 0x0020 /* 1=PHY is capable of 10Base-T HDPLX */ +#define PHY_AD_CSMA 0x0001 /* 1=PHY is capable of 802.3 CMSA */ + +/* PHY Auto-negotiation Remote End Capability Register */ +#define PHY_RMT_REG 0x05 +/* Uses same bit definitions as PHY_AD_REG */ + +/* PHY Configuration Register 1 */ +#define PHY_CFG1_REG 0x10 +#define PHY_CFG1_LNKDIS 0x8000 /* 1=Rx Link Detect Function disabled */ +#define PHY_CFG1_XMTDIS 0x4000 /* 1=TP Transmitter Disabled */ +#define PHY_CFG1_XMTPDN 0x2000 /* 1=TP Transmitter Powered Down */ +#define PHY_CFG1_BYPSCR 0x0400 /* 1=Bypass scrambler/descrambler */ +#define PHY_CFG1_UNSCDS 0x0200 /* 1=Unscramble Idle Reception Disable */ +#define PHY_CFG1_EQLZR 0x0100 /* 1=Rx Equalizer Disabled */ +#define PHY_CFG1_CABLE 0x0080 /* 1=STP(150ohm), 0=UTP(100ohm) */ +#define PHY_CFG1_RLVL0 0x0040 /* 1=Rx Squelch level reduced by 4.5db */ +#define PHY_CFG1_TLVL_SHIFT 2 /* Transmit Output Level Adjust */ +#define PHY_CFG1_TLVL_MASK 0x003C +#define PHY_CFG1_TRF_MASK 0x0003 /* Transmitter Rise/Fall time */ + + +/* PHY Configuration Register 2 */ +#define PHY_CFG2_REG 0x11 +#define PHY_CFG2_APOLDIS 0x0020 /* 1=Auto Polarity Correction disabled */ +#define PHY_CFG2_JABDIS 0x0010 /* 1=Jabber disabled */ +#define PHY_CFG2_MREG 0x0008 /* 1=Multiple register access (MII mgt) */ +#define PHY_CFG2_INTMDIO 0x0004 /* 1=Interrupt signaled with MDIO pulseo */ + +/* PHY Status Output (and Interrupt status) Register */ +#define PHY_INT_REG 0x12 /* Status Output (Interrupt Status) */ +#define PHY_INT_INT 0x8000 /* 1=bits have changed since last read */ +#define PHY_INT_LNKFAIL 0x4000 /* 1=Link Not detected */ +#define PHY_INT_LOSSSYNC 0x2000 /* 1=Descrambler has lost sync */ +#define PHY_INT_CWRD 0x1000 /* 1=Invalid 4B5B code detected on rx */ +#define PHY_INT_SSD 0x0800 /* 1=No Start Of Stream detected on rx */ +#define PHY_INT_ESD 0x0400 /* 1=No End Of Stream detected on rx */ +#define PHY_INT_RPOL 0x0200 /* 1=Reverse Polarity detected */ +#define PHY_INT_JAB 0x0100 /* 1=Jabber detected */ +#define PHY_INT_SPDDET 0x0080 /* 1=100Base-TX mode, 0=10Base-T mode */ +#define PHY_INT_DPLXDET 0x0040 /* 1=Device in Full Duplex */ + +/* PHY Interrupt/Status Mask Register */ +#define PHY_MASK_REG 0x13 /* Interrupt Mask */ +/* Uses the same bit definitions as PHY_INT_REG */ + + +/*------------------------------------------------------------------------- + . I define some macros to make it easier to do somewhat common + . or slightly complicated, repeated tasks. + --------------------------------------------------------------------------*/ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(a,x) { SMC_outw((a), (x), BANK_SELECT ); } + +/* this enables an interrupt in the interrupt mask register */ +#define SMC_ENABLE_INT(a,x) {\ + unsigned char mask;\ + SMC_SELECT_BANK((a),2);\ + mask = SMC_inb((a), IM_REG );\ + mask |= (x);\ + SMC_outb( (a), mask, IM_REG ); \ +} + +/* this disables an interrupt from the interrupt mask register */ + +#define SMC_DISABLE_INT(a,x) {\ + unsigned char mask;\ + SMC_SELECT_BANK(2);\ + mask = SMC_inb( (a), IM_REG );\ + mask &= ~(x);\ + SMC_outb( (a), mask, IM_REG ); \ +} + +/*---------------------------------------------------------------------- + . Define the interrupts that I want to receive from the card + . + . I want: + . IM_EPH_INT, for nasty errors + . IM_RCV_INT, for happy received packets + . IM_RX_OVRN_INT, because I have to kick the receiver + . IM_MDINT, for PHY Register 18 Status Changes + --------------------------------------------------------------------------*/ +#define SMC_INTERRUPT_MASK (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | \ + IM_MDINT) + +#endif /* _SMC_91111_H_ */ diff --git a/qemu/roms/u-boot/drivers/net/smc911x.c b/qemu/roms/u-boot/drivers/net/smc911x.c new file mode 100644 index 000000000..b097c1a56 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/smc911x.c @@ -0,0 +1,282 @@ +/* + * SMSC LAN9[12]1[567] Network driver + * + * (c) 2007 Pengutronix, Sascha Hauer + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include "smc911x.h" + +u32 pkt_data_pull(struct eth_device *dev, u32 addr) \ + __attribute__ ((weak, alias ("smc911x_reg_read"))); +void pkt_data_push(struct eth_device *dev, u32 addr, u32 val) \ + __attribute__ ((weak, alias ("smc911x_reg_write"))); + +static void smc911x_handle_mac_address(struct eth_device *dev) +{ + unsigned long addrh, addrl; + uchar *m = dev->enetaddr; + + addrl = m[0] | (m[1] << 8) | (m[2] << 16) | (m[3] << 24); + addrh = m[4] | (m[5] << 8); + smc911x_set_mac_csr(dev, ADDRL, addrl); + smc911x_set_mac_csr(dev, ADDRH, addrh); + + printf(DRIVERNAME ": MAC %pM\n", m); +} + +static int smc911x_eth_phy_read(struct eth_device *dev, + u8 phy, u8 reg, u16 *val) +{ + while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) + ; + + smc911x_set_mac_csr(dev, MII_ACC, phy << 11 | reg << 6 | + MII_ACC_MII_BUSY); + + while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) + ; + + *val = smc911x_get_mac_csr(dev, MII_DATA); + + return 0; +} + +static int smc911x_eth_phy_write(struct eth_device *dev, + u8 phy, u8 reg, u16 val) +{ + while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) + ; + + smc911x_set_mac_csr(dev, MII_DATA, val); + smc911x_set_mac_csr(dev, MII_ACC, + phy << 11 | reg << 6 | MII_ACC_MII_BUSY | MII_ACC_MII_WRITE); + + while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) + ; + return 0; +} + +static int smc911x_phy_reset(struct eth_device *dev) +{ + u32 reg; + + reg = smc911x_reg_read(dev, PMT_CTRL); + reg &= ~0xfffff030; + reg |= PMT_CTRL_PHY_RST; + smc911x_reg_write(dev, PMT_CTRL, reg); + + mdelay(100); + + return 0; +} + +static void smc911x_phy_configure(struct eth_device *dev) +{ + int timeout; + u16 status; + + smc911x_phy_reset(dev); + + smc911x_eth_phy_write(dev, 1, MII_BMCR, BMCR_RESET); + mdelay(1); + smc911x_eth_phy_write(dev, 1, MII_ADVERTISE, 0x01e1); + smc911x_eth_phy_write(dev, 1, MII_BMCR, BMCR_ANENABLE | + BMCR_ANRESTART); + + timeout = 5000; + do { + mdelay(1); + if ((timeout--) == 0) + goto err_out; + + if (smc911x_eth_phy_read(dev, 1, MII_BMSR, &status) != 0) + goto err_out; + } while (!(status & BMSR_LSTATUS)); + + printf(DRIVERNAME ": phy initialized\n"); + + return; + +err_out: + printf(DRIVERNAME ": autonegotiation timed out\n"); +} + +static void smc911x_enable(struct eth_device *dev) +{ + /* Enable TX */ + smc911x_reg_write(dev, HW_CFG, 8 << 16 | HW_CFG_SF); + + smc911x_reg_write(dev, GPT_CFG, GPT_CFG_TIMER_EN | 10000); + + smc911x_reg_write(dev, TX_CFG, TX_CFG_TX_ON); + + /* no padding to start of packets */ + smc911x_reg_write(dev, RX_CFG, 0); + + smc911x_set_mac_csr(dev, MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | + MAC_CR_HBDIS); + +} + +static int smc911x_init(struct eth_device *dev, bd_t * bd) +{ + struct chip_id *id = dev->priv; + + printf(DRIVERNAME ": detected %s controller\n", id->name); + + smc911x_reset(dev); + + /* Configure the PHY, initialize the link state */ + smc911x_phy_configure(dev); + + smc911x_handle_mac_address(dev); + + /* Turn on Tx + Rx */ + smc911x_enable(dev); + + return 0; +} + +static int smc911x_send(struct eth_device *dev, void *packet, int length) +{ + u32 *data = (u32*)packet; + u32 tmplen; + u32 status; + + smc911x_reg_write(dev, TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG | + TX_CMD_A_INT_LAST_SEG | length); + smc911x_reg_write(dev, TX_DATA_FIFO, length); + + tmplen = (length + 3) / 4; + + while (tmplen--) + pkt_data_push(dev, TX_DATA_FIFO, *data++); + + /* wait for transmission */ + while (!((smc911x_reg_read(dev, TX_FIFO_INF) & + TX_FIFO_INF_TSUSED) >> 16)); + + /* get status. Ignore 'no carrier' error, it has no meaning for + * full duplex operation + */ + status = smc911x_reg_read(dev, TX_STATUS_FIFO) & + (TX_STS_LOC | TX_STS_LATE_COLL | TX_STS_MANY_COLL | + TX_STS_MANY_DEFER | TX_STS_UNDERRUN); + + if (!status) + return 0; + + printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n", + status & TX_STS_LOC ? "TX_STS_LOC " : "", + status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "", + status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "", + status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "", + status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : ""); + + return -1; +} + +static void smc911x_halt(struct eth_device *dev) +{ + smc911x_reset(dev); +} + +static int smc911x_rx(struct eth_device *dev) +{ + u32 *data = (u32 *)NetRxPackets[0]; + u32 pktlen, tmplen; + u32 status; + + if ((smc911x_reg_read(dev, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED) >> 16) { + status = smc911x_reg_read(dev, RX_STATUS_FIFO); + pktlen = (status & RX_STS_PKT_LEN) >> 16; + + smc911x_reg_write(dev, RX_CFG, 0); + + tmplen = (pktlen + 3) / 4; + while (tmplen--) + *data++ = pkt_data_pull(dev, RX_DATA_FIFO); + + if (status & RX_STS_ES) + printf(DRIVERNAME + ": dropped bad packet. Status: 0x%08x\n", + status); + else + NetReceive(NetRxPackets[0], pktlen); + } + + return 0; +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +/* wrapper for smc911x_eth_phy_read */ +static int smc911x_miiphy_read(const char *devname, u8 phy, u8 reg, u16 *val) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + if (dev) + return smc911x_eth_phy_read(dev, phy, reg, val); + return -1; +} +/* wrapper for smc911x_eth_phy_write */ +static int smc911x_miiphy_write(const char *devname, u8 phy, u8 reg, u16 val) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + if (dev) + return smc911x_eth_phy_write(dev, phy, reg, val); + return -1; +} +#endif + +int smc911x_initialize(u8 dev_num, int base_addr) +{ + unsigned long addrl, addrh; + struct eth_device *dev; + + dev = malloc(sizeof(*dev)); + if (!dev) { + return -1; + } + memset(dev, 0, sizeof(*dev)); + + dev->iobase = base_addr; + + /* Try to detect chip. Will fail if not present. */ + if (smc911x_detect_chip(dev)) { + free(dev); + return 0; + } + + addrh = smc911x_get_mac_csr(dev, ADDRH); + addrl = smc911x_get_mac_csr(dev, ADDRL); + if (!(addrl == 0xffffffff && addrh == 0x0000ffff)) { + /* address is obtained from optional eeprom */ + dev->enetaddr[0] = addrl; + dev->enetaddr[1] = addrl >> 8; + dev->enetaddr[2] = addrl >> 16; + dev->enetaddr[3] = addrl >> 24; + dev->enetaddr[4] = addrh; + dev->enetaddr[5] = addrh >> 8; + } + + dev->init = smc911x_init; + dev->halt = smc911x_halt; + dev->send = smc911x_send; + dev->recv = smc911x_rx; + sprintf(dev->name, "%s-%hu", DRIVERNAME, dev_num); + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, smc911x_miiphy_read, smc911x_miiphy_write); +#endif + + return 1; +} diff --git a/qemu/roms/u-boot/drivers/net/smc911x.h b/qemu/roms/u-boot/drivers/net/smc911x.h new file mode 100644 index 000000000..acae0cfb8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/smc911x.h @@ -0,0 +1,500 @@ +/* + * SMSC LAN9[12]1[567] Network driver + * + * (c) 2007 Pengutronix, Sascha Hauer + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SMC911X_H_ +#define _SMC911X_H_ + +#include + +#define DRIVERNAME "smc911x" + +#if defined (CONFIG_SMC911X_32_BIT) && \ + defined (CONFIG_SMC911X_16_BIT) +#error "SMC911X: Only one of CONFIG_SMC911X_32_BIT and \ + CONFIG_SMC911X_16_BIT shall be set" +#endif + +#if defined (CONFIG_SMC911X_32_BIT) +static inline u32 __smc911x_reg_read(struct eth_device *dev, u32 offset) +{ + return *(volatile u32*)(dev->iobase + offset); +} +u32 smc911x_reg_read(struct eth_device *dev, u32 offset) + __attribute__((weak, alias("__smc911x_reg_read"))); + +static inline void __smc911x_reg_write(struct eth_device *dev, + u32 offset, u32 val) +{ + *(volatile u32*)(dev->iobase + offset) = val; +} +void smc911x_reg_write(struct eth_device *dev, u32 offset, u32 val) + __attribute__((weak, alias("__smc911x_reg_write"))); +#elif defined (CONFIG_SMC911X_16_BIT) +static inline u32 smc911x_reg_read(struct eth_device *dev, u32 offset) +{ + volatile u16 *addr_16 = (u16 *)(dev->iobase + offset); + return ((*addr_16 & 0x0000ffff) | (*(addr_16 + 1) << 16)); +} +static inline void smc911x_reg_write(struct eth_device *dev, + u32 offset, u32 val) +{ + *(volatile u16 *)(dev->iobase + offset) = (u16)val; + *(volatile u16 *)(dev->iobase + offset + 2) = (u16)(val >> 16); +} +#else +#error "SMC911X: undefined bus width" +#endif /* CONFIG_SMC911X_16_BIT */ + +/* Below are the register offsets and bit definitions + * of the Lan911x memory space + */ +#define RX_DATA_FIFO 0x00 + +#define TX_DATA_FIFO 0x20 +#define TX_CMD_A_INT_ON_COMP 0x80000000 +#define TX_CMD_A_INT_BUF_END_ALGN 0x03000000 +#define TX_CMD_A_INT_4_BYTE_ALGN 0x00000000 +#define TX_CMD_A_INT_16_BYTE_ALGN 0x01000000 +#define TX_CMD_A_INT_32_BYTE_ALGN 0x02000000 +#define TX_CMD_A_INT_DATA_OFFSET 0x001F0000 +#define TX_CMD_A_INT_FIRST_SEG 0x00002000 +#define TX_CMD_A_INT_LAST_SEG 0x00001000 +#define TX_CMD_A_BUF_SIZE 0x000007FF +#define TX_CMD_B_PKT_TAG 0xFFFF0000 +#define TX_CMD_B_ADD_CRC_DISABLE 0x00002000 +#define TX_CMD_B_DISABLE_PADDING 0x00001000 +#define TX_CMD_B_PKT_BYTE_LENGTH 0x000007FF + +#define RX_STATUS_FIFO 0x40 +#define RX_STS_PKT_LEN 0x3FFF0000 +#define RX_STS_ES 0x00008000 +#define RX_STS_BCST 0x00002000 +#define RX_STS_LEN_ERR 0x00001000 +#define RX_STS_RUNT_ERR 0x00000800 +#define RX_STS_MCAST 0x00000400 +#define RX_STS_TOO_LONG 0x00000080 +#define RX_STS_COLL 0x00000040 +#define RX_STS_ETH_TYPE 0x00000020 +#define RX_STS_WDOG_TMT 0x00000010 +#define RX_STS_MII_ERR 0x00000008 +#define RX_STS_DRIBBLING 0x00000004 +#define RX_STS_CRC_ERR 0x00000002 +#define RX_STATUS_FIFO_PEEK 0x44 +#define TX_STATUS_FIFO 0x48 +#define TX_STS_TAG 0xFFFF0000 +#define TX_STS_ES 0x00008000 +#define TX_STS_LOC 0x00000800 +#define TX_STS_NO_CARR 0x00000400 +#define TX_STS_LATE_COLL 0x00000200 +#define TX_STS_MANY_COLL 0x00000100 +#define TX_STS_COLL_CNT 0x00000078 +#define TX_STS_MANY_DEFER 0x00000004 +#define TX_STS_UNDERRUN 0x00000002 +#define TX_STS_DEFERRED 0x00000001 +#define TX_STATUS_FIFO_PEEK 0x4C +#define ID_REV 0x50 +#define ID_REV_CHIP_ID 0xFFFF0000 /* RO */ +#define ID_REV_REV_ID 0x0000FFFF /* RO */ + +#define INT_CFG 0x54 +#define INT_CFG_INT_DEAS 0xFF000000 /* R/W */ +#define INT_CFG_INT_DEAS_CLR 0x00004000 +#define INT_CFG_INT_DEAS_STS 0x00002000 +#define INT_CFG_IRQ_INT 0x00001000 /* RO */ +#define INT_CFG_IRQ_EN 0x00000100 /* R/W */ + /* R/W Not Affected by SW Reset */ +#define INT_CFG_IRQ_POL 0x00000010 + /* R/W Not Affected by SW Reset */ +#define INT_CFG_IRQ_TYPE 0x00000001 + +#define INT_STS 0x58 +#define INT_STS_SW_INT 0x80000000 /* R/WC */ +#define INT_STS_TXSTOP_INT 0x02000000 /* R/WC */ +#define INT_STS_RXSTOP_INT 0x01000000 /* R/WC */ +#define INT_STS_RXDFH_INT 0x00800000 /* R/WC */ +#define INT_STS_RXDF_INT 0x00400000 /* R/WC */ +#define INT_STS_TX_IOC 0x00200000 /* R/WC */ +#define INT_STS_RXD_INT 0x00100000 /* R/WC */ +#define INT_STS_GPT_INT 0x00080000 /* R/WC */ +#define INT_STS_PHY_INT 0x00040000 /* RO */ +#define INT_STS_PME_INT 0x00020000 /* R/WC */ +#define INT_STS_TXSO 0x00010000 /* R/WC */ +#define INT_STS_RWT 0x00008000 /* R/WC */ +#define INT_STS_RXE 0x00004000 /* R/WC */ +#define INT_STS_TXE 0x00002000 /* R/WC */ +/*#define INT_STS_ERX 0x00001000*/ /* R/WC */ +#define INT_STS_TDFU 0x00000800 /* R/WC */ +#define INT_STS_TDFO 0x00000400 /* R/WC */ +#define INT_STS_TDFA 0x00000200 /* R/WC */ +#define INT_STS_TSFF 0x00000100 /* R/WC */ +#define INT_STS_TSFL 0x00000080 /* R/WC */ +/*#define INT_STS_RXDF 0x00000040*/ /* R/WC */ +#define INT_STS_RDFO 0x00000040 /* R/WC */ +#define INT_STS_RDFL 0x00000020 /* R/WC */ +#define INT_STS_RSFF 0x00000010 /* R/WC */ +#define INT_STS_RSFL 0x00000008 /* R/WC */ +#define INT_STS_GPIO2_INT 0x00000004 /* R/WC */ +#define INT_STS_GPIO1_INT 0x00000002 /* R/WC */ +#define INT_STS_GPIO0_INT 0x00000001 /* R/WC */ +#define INT_EN 0x5C +#define INT_EN_SW_INT_EN 0x80000000 /* R/W */ +#define INT_EN_TXSTOP_INT_EN 0x02000000 /* R/W */ +#define INT_EN_RXSTOP_INT_EN 0x01000000 /* R/W */ +#define INT_EN_RXDFH_INT_EN 0x00800000 /* R/W */ +/*#define INT_EN_RXDF_INT_EN 0x00400000*/ /* R/W */ +#define INT_EN_TIOC_INT_EN 0x00200000 /* R/W */ +#define INT_EN_RXD_INT_EN 0x00100000 /* R/W */ +#define INT_EN_GPT_INT_EN 0x00080000 /* R/W */ +#define INT_EN_PHY_INT_EN 0x00040000 /* R/W */ +#define INT_EN_PME_INT_EN 0x00020000 /* R/W */ +#define INT_EN_TXSO_EN 0x00010000 /* R/W */ +#define INT_EN_RWT_EN 0x00008000 /* R/W */ +#define INT_EN_RXE_EN 0x00004000 /* R/W */ +#define INT_EN_TXE_EN 0x00002000 /* R/W */ +/*#define INT_EN_ERX_EN 0x00001000*/ /* R/W */ +#define INT_EN_TDFU_EN 0x00000800 /* R/W */ +#define INT_EN_TDFO_EN 0x00000400 /* R/W */ +#define INT_EN_TDFA_EN 0x00000200 /* R/W */ +#define INT_EN_TSFF_EN 0x00000100 /* R/W */ +#define INT_EN_TSFL_EN 0x00000080 /* R/W */ +/*#define INT_EN_RXDF_EN 0x00000040*/ /* R/W */ +#define INT_EN_RDFO_EN 0x00000040 /* R/W */ +#define INT_EN_RDFL_EN 0x00000020 /* R/W */ +#define INT_EN_RSFF_EN 0x00000010 /* R/W */ +#define INT_EN_RSFL_EN 0x00000008 /* R/W */ +#define INT_EN_GPIO2_INT 0x00000004 /* R/W */ +#define INT_EN_GPIO1_INT 0x00000002 /* R/W */ +#define INT_EN_GPIO0_INT 0x00000001 /* R/W */ + +#define BYTE_TEST 0x64 +#define FIFO_INT 0x68 +#define FIFO_INT_TX_AVAIL_LEVEL 0xFF000000 /* R/W */ +#define FIFO_INT_TX_STS_LEVEL 0x00FF0000 /* R/W */ +#define FIFO_INT_RX_AVAIL_LEVEL 0x0000FF00 /* R/W */ +#define FIFO_INT_RX_STS_LEVEL 0x000000FF /* R/W */ + +#define RX_CFG 0x6C +#define RX_CFG_RX_END_ALGN 0xC0000000 /* R/W */ +#define RX_CFG_RX_END_ALGN4 0x00000000 /* R/W */ +#define RX_CFG_RX_END_ALGN16 0x40000000 /* R/W */ +#define RX_CFG_RX_END_ALGN32 0x80000000 /* R/W */ +#define RX_CFG_RX_DMA_CNT 0x0FFF0000 /* R/W */ +#define RX_CFG_RX_DUMP 0x00008000 /* R/W */ +#define RX_CFG_RXDOFF 0x00001F00 /* R/W */ +/*#define RX_CFG_RXBAD 0x00000001*/ /* R/W */ + +#define TX_CFG 0x70 +/*#define TX_CFG_TX_DMA_LVL 0xE0000000*/ /* R/W */ + /* R/W Self Clearing */ +/*#define TX_CFG_TX_DMA_CNT 0x0FFF0000*/ +#define TX_CFG_TXS_DUMP 0x00008000 /* Self Clearing */ +#define TX_CFG_TXD_DUMP 0x00004000 /* Self Clearing */ +#define TX_CFG_TXSAO 0x00000004 /* R/W */ +#define TX_CFG_TX_ON 0x00000002 /* R/W */ +#define TX_CFG_STOP_TX 0x00000001 /* Self Clearing */ + +#define HW_CFG 0x74 +#define HW_CFG_TTM 0x00200000 /* R/W */ +#define HW_CFG_SF 0x00100000 /* R/W */ +#define HW_CFG_TX_FIF_SZ 0x000F0000 /* R/W */ +#define HW_CFG_TR 0x00003000 /* R/W */ +#define HW_CFG_PHY_CLK_SEL 0x00000060 /* R/W */ +#define HW_CFG_PHY_CLK_SEL_INT_PHY 0x00000000 /* R/W */ +#define HW_CFG_PHY_CLK_SEL_EXT_PHY 0x00000020 /* R/W */ +#define HW_CFG_PHY_CLK_SEL_CLK_DIS 0x00000040 /* R/W */ +#define HW_CFG_SMI_SEL 0x00000010 /* R/W */ +#define HW_CFG_EXT_PHY_DET 0x00000008 /* RO */ +#define HW_CFG_EXT_PHY_EN 0x00000004 /* R/W */ +#define HW_CFG_32_16_BIT_MODE 0x00000004 /* RO */ +#define HW_CFG_SRST_TO 0x00000002 /* RO */ +#define HW_CFG_SRST 0x00000001 /* Self Clearing */ + +#define RX_DP_CTRL 0x78 +#define RX_DP_CTRL_RX_FFWD 0x80000000 /* R/W */ +#define RX_DP_CTRL_FFWD_BUSY 0x80000000 /* RO */ + +#define RX_FIFO_INF 0x7C +#define RX_FIFO_INF_RXSUSED 0x00FF0000 /* RO */ +#define RX_FIFO_INF_RXDUSED 0x0000FFFF /* RO */ + +#define TX_FIFO_INF 0x80 +#define TX_FIFO_INF_TSUSED 0x00FF0000 /* RO */ +#define TX_FIFO_INF_TDFREE 0x0000FFFF /* RO */ + +#define PMT_CTRL 0x84 +#define PMT_CTRL_PM_MODE 0x00003000 /* Self Clearing */ +#define PMT_CTRL_PHY_RST 0x00000400 /* Self Clearing */ +#define PMT_CTRL_WOL_EN 0x00000200 /* R/W */ +#define PMT_CTRL_ED_EN 0x00000100 /* R/W */ + /* R/W Not Affected by SW Reset */ +#define PMT_CTRL_PME_TYPE 0x00000040 +#define PMT_CTRL_WUPS 0x00000030 /* R/WC */ +#define PMT_CTRL_WUPS_NOWAKE 0x00000000 /* R/WC */ +#define PMT_CTRL_WUPS_ED 0x00000010 /* R/WC */ +#define PMT_CTRL_WUPS_WOL 0x00000020 /* R/WC */ +#define PMT_CTRL_WUPS_MULTI 0x00000030 /* R/WC */ +#define PMT_CTRL_PME_IND 0x00000008 /* R/W */ +#define PMT_CTRL_PME_POL 0x00000004 /* R/W */ + /* R/W Not Affected by SW Reset */ +#define PMT_CTRL_PME_EN 0x00000002 +#define PMT_CTRL_READY 0x00000001 /* RO */ + +#define GPIO_CFG 0x88 +#define GPIO_CFG_LED3_EN 0x40000000 /* R/W */ +#define GPIO_CFG_LED2_EN 0x20000000 /* R/W */ +#define GPIO_CFG_LED1_EN 0x10000000 /* R/W */ +#define GPIO_CFG_GPIO2_INT_POL 0x04000000 /* R/W */ +#define GPIO_CFG_GPIO1_INT_POL 0x02000000 /* R/W */ +#define GPIO_CFG_GPIO0_INT_POL 0x01000000 /* R/W */ +#define GPIO_CFG_EEPR_EN 0x00700000 /* R/W */ +#define GPIO_CFG_GPIOBUF2 0x00040000 /* R/W */ +#define GPIO_CFG_GPIOBUF1 0x00020000 /* R/W */ +#define GPIO_CFG_GPIOBUF0 0x00010000 /* R/W */ +#define GPIO_CFG_GPIODIR2 0x00000400 /* R/W */ +#define GPIO_CFG_GPIODIR1 0x00000200 /* R/W */ +#define GPIO_CFG_GPIODIR0 0x00000100 /* R/W */ +#define GPIO_CFG_GPIOD4 0x00000010 /* R/W */ +#define GPIO_CFG_GPIOD3 0x00000008 /* R/W */ +#define GPIO_CFG_GPIOD2 0x00000004 /* R/W */ +#define GPIO_CFG_GPIOD1 0x00000002 /* R/W */ +#define GPIO_CFG_GPIOD0 0x00000001 /* R/W */ + +#define GPT_CFG 0x8C +#define GPT_CFG_TIMER_EN 0x20000000 /* R/W */ +#define GPT_CFG_GPT_LOAD 0x0000FFFF /* R/W */ + +#define GPT_CNT 0x90 +#define GPT_CNT_GPT_CNT 0x0000FFFF /* RO */ + +#define ENDIAN 0x98 +#define FREE_RUN 0x9C +#define RX_DROP 0xA0 +#define MAC_CSR_CMD 0xA4 +#define MAC_CSR_CMD_CSR_BUSY 0x80000000 /* Self Clearing */ +#define MAC_CSR_CMD_R_NOT_W 0x40000000 /* R/W */ +#define MAC_CSR_CMD_CSR_ADDR 0x000000FF /* R/W */ + +#define MAC_CSR_DATA 0xA8 +#define AFC_CFG 0xAC +#define AFC_CFG_AFC_HI 0x00FF0000 /* R/W */ +#define AFC_CFG_AFC_LO 0x0000FF00 /* R/W */ +#define AFC_CFG_BACK_DUR 0x000000F0 /* R/W */ +#define AFC_CFG_FCMULT 0x00000008 /* R/W */ +#define AFC_CFG_FCBRD 0x00000004 /* R/W */ +#define AFC_CFG_FCADD 0x00000002 /* R/W */ +#define AFC_CFG_FCANY 0x00000001 /* R/W */ + +#define E2P_CMD 0xB0 +#define E2P_CMD_EPC_BUSY 0x80000000 /* Self Clearing */ +#define E2P_CMD_EPC_CMD 0x70000000 /* R/W */ +#define E2P_CMD_EPC_CMD_READ 0x00000000 /* R/W */ +#define E2P_CMD_EPC_CMD_EWDS 0x10000000 /* R/W */ +#define E2P_CMD_EPC_CMD_EWEN 0x20000000 /* R/W */ +#define E2P_CMD_EPC_CMD_WRITE 0x30000000 /* R/W */ +#define E2P_CMD_EPC_CMD_WRAL 0x40000000 /* R/W */ +#define E2P_CMD_EPC_CMD_ERASE 0x50000000 /* R/W */ +#define E2P_CMD_EPC_CMD_ERAL 0x60000000 /* R/W */ +#define E2P_CMD_EPC_CMD_RELOAD 0x70000000 /* R/W */ +#define E2P_CMD_EPC_TIMEOUT 0x00000200 /* RO */ +#define E2P_CMD_MAC_ADDR_LOADED 0x00000100 /* RO */ +#define E2P_CMD_EPC_ADDR 0x000000FF /* R/W */ + +#define E2P_DATA 0xB4 +#define E2P_DATA_EEPROM_DATA 0x000000FF /* R/W */ +/* end of LAN register offsets and bit definitions */ + +/* MAC Control and Status registers */ +#define MAC_CR 0x01 /* R/W */ + +/* MAC_CR - MAC Control Register */ +#define MAC_CR_RXALL 0x80000000 +/* TODO: delete this bit? It is not described in the data sheet. */ +#define MAC_CR_HBDIS 0x10000000 +#define MAC_CR_RCVOWN 0x00800000 +#define MAC_CR_LOOPBK 0x00200000 +#define MAC_CR_FDPX 0x00100000 +#define MAC_CR_MCPAS 0x00080000 +#define MAC_CR_PRMS 0x00040000 +#define MAC_CR_INVFILT 0x00020000 +#define MAC_CR_PASSBAD 0x00010000 +#define MAC_CR_HFILT 0x00008000 +#define MAC_CR_HPFILT 0x00002000 +#define MAC_CR_LCOLL 0x00001000 +#define MAC_CR_BCAST 0x00000800 +#define MAC_CR_DISRTY 0x00000400 +#define MAC_CR_PADSTR 0x00000100 +#define MAC_CR_BOLMT_MASK 0x000000C0 +#define MAC_CR_DFCHK 0x00000020 +#define MAC_CR_TXEN 0x00000008 +#define MAC_CR_RXEN 0x00000004 + +#define ADDRH 0x02 /* R/W mask 0x0000FFFFUL */ +#define ADDRL 0x03 /* R/W mask 0xFFFFFFFFUL */ +#define HASHH 0x04 /* R/W */ +#define HASHL 0x05 /* R/W */ + +#define MII_ACC 0x06 /* R/W */ +#define MII_ACC_PHY_ADDR 0x0000F800 +#define MII_ACC_MIIRINDA 0x000007C0 +#define MII_ACC_MII_WRITE 0x00000002 +#define MII_ACC_MII_BUSY 0x00000001 + +#define MII_DATA 0x07 /* R/W mask 0x0000FFFFUL */ + +#define FLOW 0x08 /* R/W */ +#define FLOW_FCPT 0xFFFF0000 +#define FLOW_FCPASS 0x00000004 +#define FLOW_FCEN 0x00000002 +#define FLOW_FCBSY 0x00000001 + +#define VLAN1 0x09 /* R/W mask 0x0000FFFFUL */ +#define VLAN1_VTI1 0x0000ffff + +#define VLAN2 0x0A /* R/W mask 0x0000FFFFUL */ +#define VLAN2_VTI2 0x0000ffff + +#define WUFF 0x0B /* WO */ + +#define WUCSR 0x0C /* R/W */ +#define WUCSR_GUE 0x00000200 +#define WUCSR_WUFR 0x00000040 +#define WUCSR_MPR 0x00000020 +#define WUCSR_WAKE_EN 0x00000004 +#define WUCSR_MPEN 0x00000002 + +/* Chip ID values */ +#define CHIP_89218 0x218a +#define CHIP_9115 0x115 +#define CHIP_9116 0x116 +#define CHIP_9117 0x117 +#define CHIP_9118 0x118 +#define CHIP_9211 0x9211 +#define CHIP_9215 0x115a +#define CHIP_9216 0x116a +#define CHIP_9217 0x117a +#define CHIP_9218 0x118a +#define CHIP_9220 0x9220 +#define CHIP_9221 0x9221 + +struct chip_id { + u16 id; + char *name; +}; + +static const struct chip_id chip_ids[] = { + { CHIP_89218, "LAN89218" }, + { CHIP_9115, "LAN9115" }, + { CHIP_9116, "LAN9116" }, + { CHIP_9117, "LAN9117" }, + { CHIP_9118, "LAN9118" }, + { CHIP_9211, "LAN9211" }, + { CHIP_9215, "LAN9215" }, + { CHIP_9216, "LAN9216" }, + { CHIP_9217, "LAN9217" }, + { CHIP_9218, "LAN9218" }, + { CHIP_9220, "LAN9220" }, + { CHIP_9221, "LAN9221" }, + { 0, NULL }, +}; + +static u32 smc911x_get_mac_csr(struct eth_device *dev, u8 reg) +{ + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + smc911x_reg_write(dev, MAC_CSR_CMD, + MAC_CSR_CMD_CSR_BUSY | MAC_CSR_CMD_R_NOT_W | reg); + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + + return smc911x_reg_read(dev, MAC_CSR_DATA); +} + +static void smc911x_set_mac_csr(struct eth_device *dev, u8 reg, u32 data) +{ + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + smc911x_reg_write(dev, MAC_CSR_DATA, data); + smc911x_reg_write(dev, MAC_CSR_CMD, MAC_CSR_CMD_CSR_BUSY | reg); + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; +} + +static int smc911x_detect_chip(struct eth_device *dev) +{ + unsigned long val, i; + + val = smc911x_reg_read(dev, BYTE_TEST); + if (val == 0xffffffff) { + /* Special case -- no chip present */ + return -1; + } else if (val != 0x87654321) { + printf(DRIVERNAME ": Invalid chip endian 0x%08lx\n", val); + return -1; + } + + val = smc911x_reg_read(dev, ID_REV) >> 16; + for (i = 0; chip_ids[i].id != 0; i++) { + if (chip_ids[i].id == val) break; + } + if (!chip_ids[i].id) { + printf(DRIVERNAME ": Unknown chip ID %04lx\n", val); + return -1; + } + + dev->priv = (void *)&chip_ids[i]; + + return 0; +} + +static void smc911x_reset(struct eth_device *dev) +{ + int timeout; + + /* + * Take out of PM setting first + * Device is already wake up if PMT_CTRL_READY bit is set + */ + if ((smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY) == 0) { + /* Write to the bytetest will take out of powerdown */ + smc911x_reg_write(dev, BYTE_TEST, 0x0); + + timeout = 10; + + while (timeout-- && + !(smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY)) + udelay(10); + if (timeout < 0) { + printf(DRIVERNAME + ": timeout waiting for PM restore\n"); + return; + } + } + + /* Disable interrupts */ + smc911x_reg_write(dev, INT_EN, 0); + + smc911x_reg_write(dev, HW_CFG, HW_CFG_SRST); + + timeout = 1000; + while (timeout-- && smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) + udelay(10); + + if (timeout < 0) { + printf(DRIVERNAME ": reset timeout\n"); + return; + } + + /* Reset the FIFO level and flow control settings */ + smc911x_set_mac_csr(dev, FLOW, FLOW_FCPT | FLOW_FCEN); + smc911x_reg_write(dev, AFC_CFG, 0x0050287F); + + /* Set to LED outputs */ + smc911x_reg_write(dev, GPIO_CFG, 0x70070000); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/net/sunxi_wemac.c b/qemu/roms/u-boot/drivers/net/sunxi_wemac.c new file mode 100644 index 000000000..699a38158 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/sunxi_wemac.c @@ -0,0 +1,525 @@ +/* + * sunxi_wemac.c -- Allwinner A10 ethernet driver + * + * (C) Copyright 2012, Stefan Roese + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* EMAC register */ +struct wemac_regs { + u32 ctl; /* 0x00 */ + u32 tx_mode; /* 0x04 */ + u32 tx_flow; /* 0x08 */ + u32 tx_ctl0; /* 0x0c */ + u32 tx_ctl1; /* 0x10 */ + u32 tx_ins; /* 0x14 */ + u32 tx_pl0; /* 0x18 */ + u32 tx_pl1; /* 0x1c */ + u32 tx_sta; /* 0x20 */ + u32 tx_io_data; /* 0x24 */ + u32 tx_io_data1; /* 0x28 */ + u32 tx_tsvl0; /* 0x2c */ + u32 tx_tsvh0; /* 0x30 */ + u32 tx_tsvl1; /* 0x34 */ + u32 tx_tsvh1; /* 0x38 */ + u32 rx_ctl; /* 0x3c */ + u32 rx_hash0; /* 0x40 */ + u32 rx_hash1; /* 0x44 */ + u32 rx_sta; /* 0x48 */ + u32 rx_io_data; /* 0x4c */ + u32 rx_fbc; /* 0x50 */ + u32 int_ctl; /* 0x54 */ + u32 int_sta; /* 0x58 */ + u32 mac_ctl0; /* 0x5c */ + u32 mac_ctl1; /* 0x60 */ + u32 mac_ipgt; /* 0x64 */ + u32 mac_ipgr; /* 0x68 */ + u32 mac_clrt; /* 0x6c */ + u32 mac_maxf; /* 0x70 */ + u32 mac_supp; /* 0x74 */ + u32 mac_test; /* 0x78 */ + u32 mac_mcfg; /* 0x7c */ + u32 mac_mcmd; /* 0x80 */ + u32 mac_madr; /* 0x84 */ + u32 mac_mwtd; /* 0x88 */ + u32 mac_mrdd; /* 0x8c */ + u32 mac_mind; /* 0x90 */ + u32 mac_ssrr; /* 0x94 */ + u32 mac_a0; /* 0x98 */ + u32 mac_a1; /* 0x9c */ +}; + +/* SRAMC register */ +struct sunxi_sramc_regs { + u32 ctrl0; + u32 ctrl1; +}; + +/* 0: Disable 1: Aborted frame enable(default) */ +#define EMAC_TX_AB_M (0x1 << 0) +/* 0: CPU 1: DMA(default) */ +#define EMAC_TX_TM (0x1 << 1) + +#define EMAC_TX_SETUP (0) + +/* 0: DRQ asserted 1: DRQ automatically(default) */ +#define EMAC_RX_DRQ_MODE (0x1 << 1) +/* 0: CPU 1: DMA(default) */ +#define EMAC_RX_TM (0x1 << 2) +/* 0: Normal(default) 1: Pass all Frames */ +#define EMAC_RX_PA (0x1 << 4) +/* 0: Normal(default) 1: Pass Control Frames */ +#define EMAC_RX_PCF (0x1 << 5) +/* 0: Normal(default) 1: Pass Frames with CRC Error */ +#define EMAC_RX_PCRCE (0x1 << 6) +/* 0: Normal(default) 1: Pass Frames with Length Error */ +#define EMAC_RX_PLE (0x1 << 7) +/* 0: Normal 1: Pass Frames length out of range(default) */ +#define EMAC_RX_POR (0x1 << 8) +/* 0: Not accept 1: Accept unicast Packets(default) */ +#define EMAC_RX_UCAD (0x1 << 16) +/* 0: Normal(default) 1: DA Filtering */ +#define EMAC_RX_DAF (0x1 << 17) +/* 0: Not accept 1: Accept multicast Packets(default) */ +#define EMAC_RX_MCO (0x1 << 20) +/* 0: Disable(default) 1: Enable Hash filter */ +#define EMAC_RX_MHF (0x1 << 21) +/* 0: Not accept 1: Accept Broadcast Packets(default) */ +#define EMAC_RX_BCO (0x1 << 22) +/* 0: Disable(default) 1: Enable SA Filtering */ +#define EMAC_RX_SAF (0x1 << 24) +/* 0: Normal(default) 1: Inverse Filtering */ +#define EMAC_RX_SAIF (0x1 << 25) + +#define EMAC_RX_SETUP (EMAC_RX_POR | EMAC_RX_UCAD | EMAC_RX_DAF | \ + EMAC_RX_MCO | EMAC_RX_BCO) + +/* 0: Disable 1: Enable Receive Flow Control(default) */ +#define EMAC_MAC_CTL0_RFC (0x1 << 2) +/* 0: Disable 1: Enable Transmit Flow Control(default) */ +#define EMAC_MAC_CTL0_TFC (0x1 << 3) + +#define EMAC_MAC_CTL0_SETUP (EMAC_MAC_CTL0_RFC | EMAC_MAC_CTL0_TFC) + +/* 0: Disable 1: Enable MAC Frame Length Checking(default) */ +#define EMAC_MAC_CTL1_FLC (0x1 << 1) +/* 0: Disable(default) 1: Enable Huge Frame */ +#define EMAC_MAC_CTL1_HF (0x1 << 2) +/* 0: Disable(default) 1: Enable MAC Delayed CRC */ +#define EMAC_MAC_CTL1_DCRC (0x1 << 3) +/* 0: Disable 1: Enable MAC CRC(default) */ +#define EMAC_MAC_CTL1_CRC (0x1 << 4) +/* 0: Disable 1: Enable MAC PAD Short frames(default) */ +#define EMAC_MAC_CTL1_PC (0x1 << 5) +/* 0: Disable(default) 1: Enable MAC PAD Short frames and append CRC */ +#define EMAC_MAC_CTL1_VC (0x1 << 6) +/* 0: Disable(default) 1: Enable MAC auto detect Short frames */ +#define EMAC_MAC_CTL1_ADP (0x1 << 7) +/* 0: Disable(default) 1: Enable */ +#define EMAC_MAC_CTL1_PRE (0x1 << 8) +/* 0: Disable(default) 1: Enable */ +#define EMAC_MAC_CTL1_LPE (0x1 << 9) +/* 0: Disable(default) 1: Enable no back off */ +#define EMAC_MAC_CTL1_NB (0x1 << 12) +/* 0: Disable(default) 1: Enable */ +#define EMAC_MAC_CTL1_BNB (0x1 << 13) +/* 0: Disable(default) 1: Enable */ +#define EMAC_MAC_CTL1_ED (0x1 << 14) + +#define EMAC_MAC_CTL1_SETUP (EMAC_MAC_CTL1_FLC | EMAC_MAC_CTL1_CRC | \ + EMAC_MAC_CTL1_PC) + +#define EMAC_MAC_IPGT 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 DMA_CPU_TRRESHOLD 2000 + +struct wemac_eth_dev { + u32 speed; + u32 duplex; + u32 phy_configured; + int link_printed; +}; + +struct wemac_rxhdr { + s16 rx_len; + u16 rx_status; +}; + +static void wemac_inblk_32bit(void *reg, void *data, int count) +{ + int cnt = (count + 3) >> 2; + + if (cnt) { + u32 *buf = data; + + do { + u32 x = readl(reg); + *buf++ = x; + } while (--cnt); + } +} + +static void wemac_outblk_32bit(void *reg, void *data, int count) +{ + int cnt = (count + 3) >> 2; + + if (cnt) { + const u32 *buf = data; + + do { + writel(*buf++, reg); + } while (--cnt); + } +} + +/* + * Read a word from phyxcer + */ +static int wemac_phy_read(const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; + + /* issue the phy address and reg */ + writel(addr << 8 | reg, ®s->mac_madr); + + /* pull up the phy io line */ + writel(0x1, ®s->mac_mcmd); + + /* Wait read complete */ + mdelay(1); + + /* push down the phy io line */ + writel(0x0, ®s->mac_mcmd); + + /* and write data */ + *value = readl(®s->mac_mrdd); + + return 0; +} + +/* + * Write a word to phyxcer + */ +static int wemac_phy_write(const char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; + + /* issue the phy address and reg */ + writel(addr << 8 | reg, ®s->mac_madr); + + /* pull up the phy io line */ + writel(0x1, ®s->mac_mcmd); + + /* Wait write complete */ + mdelay(1); + + /* push down the phy io line */ + writel(0x0, ®s->mac_mcmd); + + /* and write data */ + writel(value, ®s->mac_mwtd); + + return 0; +} + +static void emac_setup(struct eth_device *dev) +{ + struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; + u32 reg_val; + u16 phy_val; + u32 duplex_flag; + + /* Set up TX */ + writel(EMAC_TX_SETUP, ®s->tx_mode); + + /* Set up RX */ + writel(EMAC_RX_SETUP, ®s->rx_ctl); + + /* Set MAC */ + /* Set MAC CTL0 */ + writel(EMAC_MAC_CTL0_SETUP, ®s->mac_ctl0); + + /* Set MAC CTL1 */ + wemac_phy_read(dev->name, 1, 0, &phy_val); + debug("PHY SETUP, reg 0 value: %x\n", phy_val); + duplex_flag = !!(phy_val & (1 << 8)); + + reg_val = 0; + if (duplex_flag) + reg_val = (0x1 << 0); + writel(EMAC_MAC_CTL1_SETUP | reg_val, ®s->mac_ctl1); + + /* Set up IPGT */ + writel(EMAC_MAC_IPGT, ®s->mac_ipgt); + + /* Set up IPGR */ + writel(EMAC_MAC_NBTB_IPG2 | (EMAC_MAC_NBTB_IPG1 << 8), ®s->mac_ipgr); + + /* Set up Collison window */ + writel(EMAC_MAC_RM | (EMAC_MAC_CW << 8), ®s->mac_clrt); + + /* Set up Max Frame Length */ + writel(EMAC_MAC_MFL, ®s->mac_maxf); +} + +static void wemac_reset(struct eth_device *dev) +{ + struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; + + debug("resetting device\n"); + + /* RESET device */ + writel(0, ®s->ctl); + udelay(200); + + writel(1, ®s->ctl); + udelay(200); +} + +static int sunxi_wemac_eth_init(struct eth_device *dev, bd_t *bd) +{ + struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; + struct wemac_eth_dev *priv = dev->priv; + u16 phy_reg; + + /* Init EMAC */ + + /* Flush RX FIFO */ + setbits_le32(®s->rx_ctl, 0x8); + udelay(1); + + /* Init MAC */ + + /* Soft reset MAC */ + clrbits_le32(®s->mac_ctl0, 1 << 15); + + /* Set MII clock */ + clrsetbits_le32(®s->mac_mcfg, 0xf << 2, 0xd << 2); + + /* Clear RX counter */ + writel(0x0, ®s->rx_fbc); + udelay(1); + + /* Set up EMAC */ + emac_setup(dev); + + writel(dev->enetaddr[0] << 16 | dev->enetaddr[1] << 8 | + dev->enetaddr[2], ®s->mac_a1); + writel(dev->enetaddr[3] << 16 | dev->enetaddr[4] << 8 | + dev->enetaddr[5], ®s->mac_a0); + + mdelay(1); + + wemac_reset(dev); + + /* PHY POWER UP */ + wemac_phy_read(dev->name, 1, 0, &phy_reg); + wemac_phy_write(dev->name, 1, 0, phy_reg & (~(1 << 11))); + mdelay(1); + + wemac_phy_read(dev->name, 1, 0, &phy_reg); + + priv->speed = miiphy_speed(dev->name, 0); + priv->duplex = miiphy_duplex(dev->name, 0); + + /* Print link status only once */ + if (!priv->link_printed) { + printf("ENET Speed is %d Mbps - %s duplex connection\n", + priv->speed, (priv->duplex == HALF) ? "HALF" : "FULL"); + priv->link_printed = 1; + } + + /* Set EMAC SPEED depend on PHY */ + clrsetbits_le32(®s->mac_supp, 1 << 8, + ((phy_reg & (1 << 13)) >> 13) << 8); + + /* Set duplex depend on phy */ + clrsetbits_le32(®s->mac_ctl1, 1 << 0, + ((phy_reg & (1 << 8)) >> 8) << 0); + + /* Enable RX/TX */ + setbits_le32(®s->ctl, 0x7); + + return 0; +} + +static void sunxi_wemac_eth_halt(struct eth_device *dev) +{ + /* Nothing to do here */ +} + +static int sunxi_wemac_eth_recv(struct eth_device *dev) +{ + struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; + struct wemac_rxhdr rxhdr; + u32 rxcount; + u32 reg_val; + int rx_len; + int rx_status; + int good_packet; + + /* Check packet ready or not */ + + /* + * Race warning: The first packet might arrive with + * the interrupts disabled, but the second will fix + */ + rxcount = readl(®s->rx_fbc); + if (!rxcount) { + /* Had one stuck? */ + rxcount = readl(®s->rx_fbc); + if (!rxcount) + return 0; + } + + reg_val = readl(®s->rx_io_data); + if (reg_val != 0x0143414d) { + /* Disable RX */ + clrbits_le32(®s->ctl, 1 << 2); + + /* Flush RX FIFO */ + setbits_le32(®s->rx_ctl, 1 << 3); + while (readl(®s->rx_ctl) & (1 << 3)) + ; + + /* Enable RX */ + setbits_le32(®s->ctl, 1 << 2); + + return 0; + } + + /* + * A packet ready now + * Get status/length + */ + good_packet = 1; + + wemac_inblk_32bit(®s->rx_io_data, &rxhdr, sizeof(rxhdr)); + + rx_len = rxhdr.rx_len; + rx_status = rxhdr.rx_status; + + /* Packet Status check */ + if (rx_len < 0x40) { + good_packet = 0; + debug("RX: Bad Packet (runt)\n"); + } + + /* rx_status is identical to RSR register. */ + if (0 & rx_status & (EMAC_CRCERR | EMAC_LENERR)) { + good_packet = 0; + if (rx_status & EMAC_CRCERR) + printf("crc error\n"); + if (rx_status & EMAC_LENERR) + printf("length error\n"); + } + + /* Move data from WEMAC */ + if (good_packet) { + if (rx_len > DMA_CPU_TRRESHOLD) { + printf("Received packet is too big (len=%d)\n", rx_len); + } else { + wemac_inblk_32bit((void *)®s->rx_io_data, + NetRxPackets[0], rx_len); + + /* Pass to upper layer */ + NetReceive(NetRxPackets[0], rx_len); + return rx_len; + } + } + + return 0; +} + +static int sunxi_wemac_eth_send(struct eth_device *dev, void *packet, int len) +{ + struct wemac_regs *regs = (struct wemac_regs *)dev->iobase; + + /* Select channel 0 */ + writel(0, ®s->tx_ins); + + /* Write packet */ + wemac_outblk_32bit((void *)®s->tx_io_data, packet, len); + + /* Set TX len */ + writel(len, ®s->tx_pl0); + + /* Start translate from fifo to phy */ + setbits_le32(®s->tx_ctl0, 1); + + return 0; +} + +int sunxi_wemac_initialize(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_sramc_regs *sram = + (struct sunxi_sramc_regs *)SUNXI_SRAMC_BASE; + struct eth_device *dev; + struct wemac_eth_dev *priv; + int pin; + + dev = malloc(sizeof(*dev)); + if (dev == NULL) + return -ENOMEM; + + priv = (struct wemac_eth_dev *)malloc(sizeof(struct wemac_eth_dev)); + if (!priv) { + free(dev); + return -ENOMEM; + } + + memset(dev, 0, sizeof(*dev)); + memset(priv, 0, sizeof(struct wemac_eth_dev)); + + /* Map SRAM to EMAC */ + setbits_le32(&sram->ctrl1, 0x5 << 2); + + /* Configure pin mux settings for MII Ethernet */ + for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(17); pin++) + sunxi_gpio_set_cfgpin(pin, 2); + + /* Set up clock gating */ + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_EMAC); + + dev->iobase = SUNXI_EMAC_BASE; + dev->priv = priv; + dev->init = sunxi_wemac_eth_init; + dev->halt = sunxi_wemac_eth_halt; + dev->send = sunxi_wemac_eth_send; + dev->recv = sunxi_wemac_eth_recv; + strcpy(dev->name, "wemac"); + + eth_register(dev); + + miiphy_register(dev->name, wemac_phy_read, wemac_phy_write); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/tsec.c b/qemu/roms/u-boot/drivers/net/tsec.c new file mode 100644 index 000000000..e9138f033 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/tsec.c @@ -0,0 +1,688 @@ +/* + * Freescale Three Speed Ethernet Controller driver + * + * This software may be used and distributed according to the + * terms of the GNU Public License, Version 2, incorporated + * herein by reference. + * + * Copyright 2004-2011, 2013 Freescale Semiconductor, Inc. + * (C) Copyright 2003, Motorola, Inc. + * author Andy Fleming + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define TX_BUF_CNT 2 + +static uint rx_idx; /* index of the current RX buffer */ +static uint tx_idx; /* index of the current TX buffer */ + +#ifdef __GNUC__ +static struct txbd8 __iomem txbd[TX_BUF_CNT] __aligned(8); +static struct rxbd8 __iomem rxbd[PKTBUFSRX] __aligned(8); + +#else +#error "rtx must be 64-bit aligned" +#endif + +static int tsec_send(struct eth_device *dev, void *packet, int length); + +/* Default initializations for TSEC controllers. */ + +static struct tsec_info_struct tsec_info[] = { +#ifdef CONFIG_TSEC1 + STD_TSEC_INFO(1), /* TSEC1 */ +#endif +#ifdef CONFIG_TSEC2 + STD_TSEC_INFO(2), /* TSEC2 */ +#endif +#ifdef CONFIG_MPC85XX_FEC + { + .regs = TSEC_GET_REGS(2, 0x2000), + .devname = CONFIG_MPC85XX_FEC_NAME, + .phyaddr = FEC_PHY_ADDR, + .flags = FEC_FLAGS, + .mii_devname = DEFAULT_MII_NAME + }, /* FEC */ +#endif +#ifdef CONFIG_TSEC3 + STD_TSEC_INFO(3), /* TSEC3 */ +#endif +#ifdef CONFIG_TSEC4 + STD_TSEC_INFO(4), /* TSEC4 */ +#endif +}; + +#define TBIANA_SETTINGS ( \ + TBIANA_ASYMMETRIC_PAUSE \ + | TBIANA_SYMMETRIC_PAUSE \ + | TBIANA_FULL_DUPLEX \ + ) + +/* By default force the TBI PHY into 1000Mbps full duplex when in SGMII mode */ +#ifndef CONFIG_TSEC_TBICR_SETTINGS +#define CONFIG_TSEC_TBICR_SETTINGS ( \ + TBICR_PHY_RESET \ + | TBICR_ANEG_ENABLE \ + | TBICR_FULL_DUPLEX \ + | TBICR_SPEED1_SET \ + ) +#endif /* CONFIG_TSEC_TBICR_SETTINGS */ + +/* Configure the TBI for SGMII operation */ +static void tsec_configure_serdes(struct tsec_private *priv) +{ + /* Access TBI PHY registers at given TSEC register offset as opposed + * to the register offset used for external PHY accesses */ + tsec_local_mdio_write(priv->phyregs_sgmii, in_be32(&priv->regs->tbipa), + 0, TBI_ANA, TBIANA_SETTINGS); + tsec_local_mdio_write(priv->phyregs_sgmii, in_be32(&priv->regs->tbipa), + 0, TBI_TBICON, TBICON_CLK_SELECT); + tsec_local_mdio_write(priv->phyregs_sgmii, in_be32(&priv->regs->tbipa), + 0, TBI_CR, CONFIG_TSEC_TBICR_SETTINGS); +} + +#ifdef CONFIG_MCAST_TFTP + +/* CREDITS: linux gianfar driver, slightly adjusted... thanx. */ + +/* Set the appropriate hash bit for the given addr */ + +/* The algorithm works like so: + * 1) Take the Destination Address (ie the multicast address), and + * do a CRC on it (little endian), and reverse the bits of the + * result. + * 2) Use the 8 most significant bits as a hash into a 256-entry + * table. The table is controlled through 8 32-bit registers: + * gaddr0-7. gaddr0's MSB is entry 0, and gaddr7's LSB is entry + * 255. This means that the 3 most significant bits in the + * hash index which gaddr register to use, and the 5 other bits + * indicate which bit (assuming an IBM numbering scheme, which + * for PowerPC (tm) is usually the case) in the register holds + * the entry. */ +static int +tsec_mcast_addr(struct eth_device *dev, const u8 *mcast_mac, u8 set) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + u32 result, value; + u8 whichbit, whichreg; + + result = ether_crc(MAC_ADDR_LEN, mcast_mac); + whichbit = (result >> 24) & 0x1f; /* the 5 LSB = which bit to set */ + whichreg = result >> 29; /* the 3 MSB = which reg to set it in */ + + value = 1 << (31-whichbit); + + if (set) + setbits_be32(®s->hash.gaddr0 + whichreg, value); + else + clrbits_be32(®s->hash.gaddr0 + whichreg, value); + + return 0; +} +#endif /* Multicast TFTP ? */ + +/* Initialized required registers to appropriate values, zeroing + * those we don't care about (unless zero is bad, in which case, + * choose a more appropriate value) + */ +static void init_registers(struct tsec __iomem *regs) +{ + /* Clear IEVENT */ + out_be32(®s->ievent, IEVENT_INIT_CLEAR); + + out_be32(®s->imask, IMASK_INIT_CLEAR); + + out_be32(®s->hash.iaddr0, 0); + out_be32(®s->hash.iaddr1, 0); + out_be32(®s->hash.iaddr2, 0); + out_be32(®s->hash.iaddr3, 0); + out_be32(®s->hash.iaddr4, 0); + out_be32(®s->hash.iaddr5, 0); + out_be32(®s->hash.iaddr6, 0); + out_be32(®s->hash.iaddr7, 0); + + out_be32(®s->hash.gaddr0, 0); + out_be32(®s->hash.gaddr1, 0); + out_be32(®s->hash.gaddr2, 0); + out_be32(®s->hash.gaddr3, 0); + out_be32(®s->hash.gaddr4, 0); + out_be32(®s->hash.gaddr5, 0); + out_be32(®s->hash.gaddr6, 0); + out_be32(®s->hash.gaddr7, 0); + + out_be32(®s->rctrl, 0x00000000); + + /* Init RMON mib registers */ + memset((void *)®s->rmon, 0, sizeof(regs->rmon)); + + out_be32(®s->rmon.cam1, 0xffffffff); + out_be32(®s->rmon.cam2, 0xffffffff); + + out_be32(®s->mrblr, MRBLR_INIT_SETTINGS); + + out_be32(®s->minflr, MINFLR_INIT_SETTINGS); + + out_be32(®s->attr, ATTR_INIT_SETTINGS); + out_be32(®s->attreli, ATTRELI_INIT_SETTINGS); + +} + +/* Configure maccfg2 based on negotiated speed and duplex + * reported by PHY handling code + */ +static void adjust_link(struct tsec_private *priv, struct phy_device *phydev) +{ + struct tsec __iomem *regs = priv->regs; + u32 ecntrl, maccfg2; + + if (!phydev->link) { + printf("%s: No link.\n", phydev->dev->name); + return; + } + + /* clear all bits relative with interface mode */ + ecntrl = in_be32(®s->ecntrl); + ecntrl &= ~ECNTRL_R100; + + maccfg2 = in_be32(®s->maccfg2); + maccfg2 &= ~(MACCFG2_IF | MACCFG2_FULL_DUPLEX); + + if (phydev->duplex) + maccfg2 |= MACCFG2_FULL_DUPLEX; + + switch (phydev->speed) { + case 1000: + maccfg2 |= MACCFG2_GMII; + break; + case 100: + case 10: + maccfg2 |= MACCFG2_MII; + + /* Set R100 bit in all modes although + * it is only used in RGMII mode + */ + if (phydev->speed == 100) + ecntrl |= ECNTRL_R100; + break; + default: + printf("%s: Speed was bad\n", phydev->dev->name); + break; + } + + out_be32(®s->ecntrl, ecntrl); + out_be32(®s->maccfg2, maccfg2); + + printf("Speed: %d, %s duplex%s\n", phydev->speed, + (phydev->duplex) ? "full" : "half", + (phydev->port == PORT_FIBRE) ? ", fiber mode" : ""); +} + +#ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 +/* + * When MACCFG1[Rx_EN] is enabled during system boot as part + * of the eTSEC port initialization sequence, + * the eTSEC Rx logic may not be properly initialized. + */ +void redundant_init(struct eth_device *dev) +{ + struct tsec_private *priv = dev->priv; + struct tsec __iomem *regs = priv->regs; + uint t, count = 0; + int fail = 1; + static const u8 pkt[] = { + 0x00, 0x1e, 0x4f, 0x12, 0xcb, 0x2c, 0x00, 0x25, + 0x64, 0xbb, 0xd1, 0xab, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x5c, 0xdd, 0x22, 0x00, 0x00, 0x80, 0x01, + 0x1f, 0x71, 0x0a, 0xc1, 0x14, 0x22, 0x0a, 0xc1, + 0x14, 0x6a, 0x08, 0x00, 0xef, 0x7e, 0x02, 0x00, + 0x94, 0x05, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72}; + + /* Enable promiscuous mode */ + setbits_be32(®s->rctrl, 0x8); + /* Enable loopback mode */ + setbits_be32(®s->maccfg1, MACCFG1_LOOPBACK); + /* Enable transmit and receive */ + setbits_be32(®s->maccfg1, MACCFG1_RX_EN | MACCFG1_TX_EN); + + /* Tell the DMA it is clear to go */ + setbits_be32(®s->dmactrl, DMACTRL_INIT_SETTINGS); + out_be32(®s->tstat, TSTAT_CLEAR_THALT); + out_be32(®s->rstat, RSTAT_CLEAR_RHALT); + clrbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); + + do { + uint16_t status; + tsec_send(dev, (void *)pkt, sizeof(pkt)); + + /* Wait for buffer to be received */ + for (t = 0; in_be16(&rxbd[rx_idx].status) & RXBD_EMPTY; t++) { + if (t >= 10 * TOUT_LOOP) { + printf("%s: tsec: rx error\n", dev->name); + break; + } + } + + if (!memcmp(pkt, (void *)NetRxPackets[rx_idx], sizeof(pkt))) + fail = 0; + + out_be16(&rxbd[rx_idx].length, 0); + status = RXBD_EMPTY; + if ((rx_idx + 1) == PKTBUFSRX) + status |= RXBD_WRAP; + out_be16(&rxbd[rx_idx].status, status); + rx_idx = (rx_idx + 1) % PKTBUFSRX; + + if (in_be32(®s->ievent) & IEVENT_BSY) { + out_be32(®s->ievent, IEVENT_BSY); + out_be32(®s->rstat, RSTAT_CLEAR_RHALT); + } + if (fail) { + printf("loopback recv packet error!\n"); + clrbits_be32(®s->maccfg1, MACCFG1_RX_EN); + udelay(1000); + setbits_be32(®s->maccfg1, MACCFG1_RX_EN); + } + } while ((count++ < 4) && (fail == 1)); + + if (fail) + panic("eTSEC init fail!\n"); + /* Disable promiscuous mode */ + clrbits_be32(®s->rctrl, 0x8); + /* Disable loopback mode */ + clrbits_be32(®s->maccfg1, MACCFG1_LOOPBACK); +} +#endif + +/* Set up the buffers and their descriptors, and bring up the + * interface + */ +static void startup_tsec(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + uint16_t status; + int i; + + /* reset the indices to zero */ + rx_idx = 0; + tx_idx = 0; +#ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 + uint svr; +#endif + + /* Point to the buffer descriptors */ + out_be32(®s->tbase, (u32)&txbd[0]); + out_be32(®s->rbase, (u32)&rxbd[0]); + + /* Initialize the Rx Buffer descriptors */ + for (i = 0; i < PKTBUFSRX; i++) { + out_be16(&rxbd[i].status, RXBD_EMPTY); + out_be16(&rxbd[i].length, 0); + out_be32(&rxbd[i].bufptr, (u32)NetRxPackets[i]); + } + status = in_be16(&rxbd[PKTBUFSRX - 1].status); + out_be16(&rxbd[PKTBUFSRX - 1].status, status | RXBD_WRAP); + + /* Initialize the TX Buffer Descriptors */ + for (i = 0; i < TX_BUF_CNT; i++) { + out_be16(&txbd[i].status, 0); + out_be16(&txbd[i].length, 0); + out_be32(&txbd[i].bufptr, 0); + } + status = in_be16(&txbd[TX_BUF_CNT - 1].status); + out_be16(&txbd[TX_BUF_CNT - 1].status, status | TXBD_WRAP); + +#ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 + svr = get_svr(); + if ((SVR_MAJ(svr) == 1) || IS_SVR_REV(svr, 2, 0)) + redundant_init(dev); +#endif + /* Enable Transmit and Receive */ + setbits_be32(®s->maccfg1, MACCFG1_RX_EN | MACCFG1_TX_EN); + + /* Tell the DMA it is clear to go */ + setbits_be32(®s->dmactrl, DMACTRL_INIT_SETTINGS); + out_be32(®s->tstat, TSTAT_CLEAR_THALT); + out_be32(®s->rstat, RSTAT_CLEAR_RHALT); + clrbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); +} + +/* This returns the status bits of the device. The return value + * is never checked, and this is what the 8260 driver did, so we + * do the same. Presumably, this would be zero if there were no + * errors + */ +static int tsec_send(struct eth_device *dev, void *packet, int length) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + uint16_t status; + int result = 0; + int i; + + /* Find an empty buffer descriptor */ + for (i = 0; in_be16(&txbd[tx_idx].status) & TXBD_READY; i++) { + if (i >= TOUT_LOOP) { + debug("%s: tsec: tx buffers full\n", dev->name); + return result; + } + } + + out_be32(&txbd[tx_idx].bufptr, (u32)packet); + out_be16(&txbd[tx_idx].length, length); + status = in_be16(&txbd[tx_idx].status); + out_be16(&txbd[tx_idx].status, status | + (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT)); + + /* Tell the DMA to go */ + out_be32(®s->tstat, TSTAT_CLEAR_THALT); + + /* Wait for buffer to be transmitted */ + for (i = 0; in_be16(&txbd[tx_idx].status) & TXBD_READY; i++) { + if (i >= TOUT_LOOP) { + debug("%s: tsec: tx error\n", dev->name); + return result; + } + } + + tx_idx = (tx_idx + 1) % TX_BUF_CNT; + result = in_be16(&txbd[tx_idx].status) & TXBD_STATS; + + return result; +} + +static int tsec_recv(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + + while (!(in_be16(&rxbd[rx_idx].status) & RXBD_EMPTY)) { + int length = in_be16(&rxbd[rx_idx].length); + uint16_t status = in_be16(&rxbd[rx_idx].status); + + /* Send the packet up if there were no errors */ + if (!(status & RXBD_STATS)) + NetReceive(NetRxPackets[rx_idx], length - 4); + else + printf("Got error %x\n", (status & RXBD_STATS)); + + out_be16(&rxbd[rx_idx].length, 0); + + status = RXBD_EMPTY; + /* Set the wrap bit if this is the last element in the list */ + if ((rx_idx + 1) == PKTBUFSRX) + status |= RXBD_WRAP; + out_be16(&rxbd[rx_idx].status, status); + + rx_idx = (rx_idx + 1) % PKTBUFSRX; + } + + if (in_be32(®s->ievent) & IEVENT_BSY) { + out_be32(®s->ievent, IEVENT_BSY); + out_be32(®s->rstat, RSTAT_CLEAR_RHALT); + } + + return -1; + +} + +/* Stop the interface */ +static void tsec_halt(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + + clrbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); + setbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); + + while ((in_be32(®s->ievent) & (IEVENT_GRSC | IEVENT_GTSC)) + != (IEVENT_GRSC | IEVENT_GTSC)) + ; + + clrbits_be32(®s->maccfg1, MACCFG1_TX_EN | MACCFG1_RX_EN); + + /* Shut down the PHY, as needed */ + phy_shutdown(priv->phydev); +} + +/* Initializes data structures and registers for the controller, + * and brings the interface up. Returns the link status, meaning + * that it returns success if the link is up, failure otherwise. + * This allows u-boot to find the first active controller. + */ +static int tsec_init(struct eth_device *dev, bd_t * bd) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + u32 tempval; + int ret; + + /* Make sure the controller is stopped */ + tsec_halt(dev); + + /* Init MACCFG2. Defaults to GMII */ + out_be32(®s->maccfg2, MACCFG2_INIT_SETTINGS); + + /* Init ECNTRL */ + out_be32(®s->ecntrl, ECNTRL_INIT_SETTINGS); + + /* Copy the station address into the address registers. + * For a station address of 0x12345678ABCD in transmission + * order (BE), MACnADDR1 is set to 0xCDAB7856 and + * MACnADDR2 is set to 0x34120000. + */ + tempval = (dev->enetaddr[5] << 24) | (dev->enetaddr[4] << 16) | + (dev->enetaddr[3] << 8) | dev->enetaddr[2]; + + out_be32(®s->macstnaddr1, tempval); + + tempval = (dev->enetaddr[1] << 24) | (dev->enetaddr[0] << 16); + + out_be32(®s->macstnaddr2, tempval); + + /* Clear out (for the most part) the other registers */ + init_registers(regs); + + /* Ready the device for tx/rx */ + startup_tsec(dev); + + /* Start up the PHY */ + ret = phy_startup(priv->phydev); + if (ret) { + printf("Could not initialize PHY %s\n", + priv->phydev->dev->name); + return ret; + } + + adjust_link(priv, priv->phydev); + + /* If there's no link, fail */ + return priv->phydev->link ? 0 : -1; +} + +static phy_interface_t tsec_get_interface(struct tsec_private *priv) +{ + struct tsec __iomem *regs = priv->regs; + u32 ecntrl; + + ecntrl = in_be32(®s->ecntrl); + + if (ecntrl & ECNTRL_SGMII_MODE) + return PHY_INTERFACE_MODE_SGMII; + + if (ecntrl & ECNTRL_TBI_MODE) { + if (ecntrl & ECNTRL_REDUCED_MODE) + return PHY_INTERFACE_MODE_RTBI; + else + return PHY_INTERFACE_MODE_TBI; + } + + if (ecntrl & ECNTRL_REDUCED_MODE) { + if (ecntrl & ECNTRL_REDUCED_MII_MODE) + return PHY_INTERFACE_MODE_RMII; + else { + phy_interface_t interface = priv->interface; + + /* + * This isn't autodetected, so it must + * be set by the platform code. + */ + if ((interface == PHY_INTERFACE_MODE_RGMII_ID) || + (interface == PHY_INTERFACE_MODE_RGMII_TXID) || + (interface == PHY_INTERFACE_MODE_RGMII_RXID)) + return interface; + + return PHY_INTERFACE_MODE_RGMII; + } + } + + if (priv->flags & TSEC_GIGABIT) + return PHY_INTERFACE_MODE_GMII; + + return PHY_INTERFACE_MODE_MII; +} + + +/* Discover which PHY is attached to the device, and configure it + * properly. If the PHY is not recognized, then return 0 + * (failure). Otherwise, return 1 + */ +static int init_phy(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct phy_device *phydev; + struct tsec __iomem *regs = priv->regs; + u32 supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full); + + if (priv->flags & TSEC_GIGABIT) + supported |= SUPPORTED_1000baseT_Full; + + /* Assign a Physical address to the TBI */ + out_be32(®s->tbipa, CONFIG_SYS_TBIPA_VALUE); + + priv->interface = tsec_get_interface(priv); + + if (priv->interface == PHY_INTERFACE_MODE_SGMII) + tsec_configure_serdes(priv); + + phydev = phy_connect(priv->bus, priv->phyaddr, dev, priv->interface); + + phydev->supported &= supported; + phydev->advertising = phydev->supported; + + priv->phydev = phydev; + + phy_config(phydev); + + return 1; +} + +/* Initialize device structure. Returns success if PHY + * initialization succeeded (i.e. if it recognizes the PHY) + */ +static int tsec_initialize(bd_t *bis, struct tsec_info_struct *tsec_info) +{ + struct eth_device *dev; + int i; + struct tsec_private *priv; + + dev = (struct eth_device *)malloc(sizeof *dev); + + if (NULL == dev) + return 0; + + memset(dev, 0, sizeof *dev); + + priv = (struct tsec_private *)malloc(sizeof(*priv)); + + if (NULL == priv) + return 0; + + priv->regs = tsec_info->regs; + priv->phyregs_sgmii = tsec_info->miiregs_sgmii; + + priv->phyaddr = tsec_info->phyaddr; + priv->flags = tsec_info->flags; + + sprintf(dev->name, tsec_info->devname); + priv->interface = tsec_info->interface; + priv->bus = miiphy_get_dev_by_name(tsec_info->mii_devname); + dev->iobase = 0; + dev->priv = priv; + dev->init = tsec_init; + dev->halt = tsec_halt; + dev->send = tsec_send; + dev->recv = tsec_recv; +#ifdef CONFIG_MCAST_TFTP + dev->mcast = tsec_mcast_addr; +#endif + + /* Tell u-boot to get the addr from the env */ + for (i = 0; i < 6; i++) + dev->enetaddr[i] = 0; + + eth_register(dev); + + /* Reset the MAC */ + setbits_be32(&priv->regs->maccfg1, MACCFG1_SOFT_RESET); + udelay(2); /* Soft Reset must be asserted for 3 TX clocks */ + clrbits_be32(&priv->regs->maccfg1, MACCFG1_SOFT_RESET); + + /* Try to initialize PHY here, and return */ + return init_phy(dev); +} + +/* + * Initialize all the TSEC devices + * + * Returns the number of TSEC devices that were initialized + */ +int tsec_eth_init(bd_t *bis, struct tsec_info_struct *tsecs, int num) +{ + int i; + int ret, count = 0; + + for (i = 0; i < num; i++) { + ret = tsec_initialize(bis, &tsecs[i]); + if (ret > 0) + count += ret; + } + + return count; +} + +int tsec_standard_init(bd_t *bis) +{ + struct fsl_pq_mdio_info info; + + info.regs = TSEC_GET_MDIO_REGS_BASE(1); + info.name = DEFAULT_MII_NAME; + + fsl_pq_mdio_init(bis, &info); + + return tsec_eth_init(bis, tsec_info, ARRAY_SIZE(tsec_info)); +} diff --git a/qemu/roms/u-boot/drivers/net/tsi108_eth.c b/qemu/roms/u-boot/drivers/net/tsi108_eth.c new file mode 100644 index 000000000..72b8159d8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/tsi108_eth.c @@ -0,0 +1,1016 @@ +/*********************************************************************** + * + * Copyright (c) 2005 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Description: + * Ethernet interface for Tundra TSI108 bridge chip + * + ***********************************************************************/ + +#include + +#if !defined(CONFIG_TSI108_ETH_NUM_PORTS) || (CONFIG_TSI108_ETH_NUM_PORTS > 2) +#error "CONFIG_TSI108_ETH_NUM_PORTS must be defined as 1 or 2" +#endif + +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define TSI108_ETH_DEBUG 7 +#else +#define TSI108_ETH_DEBUG 0 +#endif + +#if TSI108_ETH_DEBUG > 0 +#define debug_lev(lev, fmt, args...) \ +if (lev <= TSI108_ETH_DEBUG) \ +printf ("%s %d: " fmt, __FUNCTION__, __LINE__, ##args) +#else +#define debug_lev(lev, fmt, args...) do{}while(0) +#endif + +#define RX_PRINT_ERRORS +#define TX_PRINT_ERRORS + +#define ETH_BASE (CONFIG_SYS_TSI108_CSR_BASE + 0x6000) + +#define ETH_PORT_OFFSET 0x400 + +#define __REG32(base, offset) (*((volatile u32 *)((char *)(base) + (offset)))) + +#define reg_MAC_CONFIG_1(base) __REG32(base, 0x00000000) +#define MAC_CONFIG_1_TX_ENABLE (0x00000001) +#define MAC_CONFIG_1_SYNC_TX_ENABLE (0x00000002) +#define MAC_CONFIG_1_RX_ENABLE (0x00000004) +#define MAC_CONFIG_1_SYNC_RX_ENABLE (0x00000008) +#define MAC_CONFIG_1_TX_FLOW_CONTROL (0x00000010) +#define MAC_CONFIG_1_RX_FLOW_CONTROL (0x00000020) +#define MAC_CONFIG_1_LOOP_BACK (0x00000100) +#define MAC_CONFIG_1_RESET_TX_FUNCTION (0x00010000) +#define MAC_CONFIG_1_RESET_RX_FUNCTION (0x00020000) +#define MAC_CONFIG_1_RESET_TX_MAC (0x00040000) +#define MAC_CONFIG_1_RESET_RX_MAC (0x00080000) +#define MAC_CONFIG_1_SIM_RESET (0x40000000) +#define MAC_CONFIG_1_SOFT_RESET (0x80000000) + +#define reg_MAC_CONFIG_2(base) __REG32(base, 0x00000004) +#define MAC_CONFIG_2_FULL_DUPLEX (0x00000001) +#define MAC_CONFIG_2_CRC_ENABLE (0x00000002) +#define MAC_CONFIG_2_PAD_CRC (0x00000004) +#define MAC_CONFIG_2_LENGTH_CHECK (0x00000010) +#define MAC_CONFIG_2_HUGE_FRAME (0x00000020) +#define MAC_CONFIG_2_INTERFACE_MODE(val) (((val) & 0x3) << 8) +#define MAC_CONFIG_2_PREAMBLE_LENGTH(val) (((val) & 0xf) << 12) +#define INTERFACE_MODE_NIBBLE 1 /* 10/100 Mb/s MII) */ +#define INTERFACE_MODE_BYTE 2 /* 1000 Mb/s GMII/TBI */ + +#define reg_MAXIMUM_FRAME_LENGTH(base) __REG32(base, 0x00000010) + +#define reg_MII_MGMT_CONFIG(base) __REG32(base, 0x00000020) +#define MII_MGMT_CONFIG_MGMT_CLOCK_SELECT(val) ((val) & 0x7) +#define MII_MGMT_CONFIG_NO_PREAMBLE (0x00000010) +#define MII_MGMT_CONFIG_SCAN_INCREMENT (0x00000020) +#define MII_MGMT_CONFIG_RESET_MGMT (0x80000000) + +#define reg_MII_MGMT_COMMAND(base) __REG32(base, 0x00000024) +#define MII_MGMT_COMMAND_READ_CYCLE (0x00000001) +#define MII_MGMT_COMMAND_SCAN_CYCLE (0x00000002) + +#define reg_MII_MGMT_ADDRESS(base) __REG32(base, 0x00000028) +#define reg_MII_MGMT_CONTROL(base) __REG32(base, 0x0000002c) +#define reg_MII_MGMT_STATUS(base) __REG32(base, 0x00000030) + +#define reg_MII_MGMT_INDICATORS(base) __REG32(base, 0x00000034) +#define MII_MGMT_INDICATORS_BUSY (0x00000001) +#define MII_MGMT_INDICATORS_SCAN (0x00000002) +#define MII_MGMT_INDICATORS_NOT_VALID (0x00000004) + +#define reg_INTERFACE_STATUS(base) __REG32(base, 0x0000003c) +#define INTERFACE_STATUS_LINK_FAIL (0x00000008) +#define INTERFACE_STATUS_EXCESS_DEFER (0x00000200) + +#define reg_STATION_ADDRESS_1(base) __REG32(base, 0x00000040) +#define reg_STATION_ADDRESS_2(base) __REG32(base, 0x00000044) + +#define reg_PORT_CONTROL(base) __REG32(base, 0x00000200) +#define PORT_CONTROL_PRI (0x00000001) +#define PORT_CONTROL_BPT (0x00010000) +#define PORT_CONTROL_SPD (0x00040000) +#define PORT_CONTROL_RBC (0x00080000) +#define PORT_CONTROL_PRB (0x00200000) +#define PORT_CONTROL_DIS (0x00400000) +#define PORT_CONTROL_TBI (0x00800000) +#define PORT_CONTROL_STE (0x10000000) +#define PORT_CONTROL_ZOR (0x20000000) +#define PORT_CONTROL_CLR (0x40000000) +#define PORT_CONTROL_SRT (0x80000000) + +#define reg_TX_CONFIG(base) __REG32(base, 0x00000220) +#define TX_CONFIG_START_Q (0x00000003) +#define TX_CONFIG_EHP (0x00400000) +#define TX_CONFIG_CHP (0x00800000) +#define TX_CONFIG_RST (0x80000000) + +#define reg_TX_CONTROL(base) __REG32(base, 0x00000224) +#define TX_CONTROL_GO (0x00008000) +#define TX_CONTROL_MP (0x01000000) +#define TX_CONTROL_EAI (0x20000000) +#define TX_CONTROL_ABT (0x40000000) +#define TX_CONTROL_EII (0x80000000) + +#define reg_TX_STATUS(base) __REG32(base, 0x00000228) +#define TX_STATUS_QUEUE_USABLE (0x0000000f) +#define TX_STATUS_CURR_Q (0x00000300) +#define TX_STATUS_ACT (0x00008000) +#define TX_STATUS_QUEUE_IDLE (0x000f0000) +#define TX_STATUS_EOQ_PENDING (0x0f000000) + +#define reg_TX_EXTENDED_STATUS(base) __REG32(base, 0x0000022c) +#define TX_EXTENDED_STATUS_END_OF_QUEUE_CONDITION (0x0000000f) +#define TX_EXTENDED_STATUS_END_OF_FRAME_CONDITION (0x00000f00) +#define TX_EXTENDED_STATUS_DESCRIPTOR_INTERRUPT_CONDITION (0x000f0000) +#define TX_EXTENDED_STATUS_ERROR_FLAG (0x0f000000) + +#define reg_TX_THRESHOLDS(base) __REG32(base, 0x00000230) + +#define reg_TX_DIAGNOSTIC_ADDR(base) __REG32(base, 0x00000270) +#define TX_DIAGNOSTIC_ADDR_INDEX (0x0000007f) +#define TX_DIAGNOSTIC_ADDR_DFR (0x40000000) +#define TX_DIAGNOSTIC_ADDR_AI (0x80000000) + +#define reg_TX_DIAGNOSTIC_DATA(base) __REG32(base, 0x00000274) + +#define reg_TX_ERROR_STATUS(base) __REG32(base, 0x00000278) +#define TX_ERROR_STATUS (0x00000278) +#define TX_ERROR_STATUS_QUEUE_0_ERROR_RESPONSE (0x0000000f) +#define TX_ERROR_STATUS_TEA_ON_QUEUE_0 (0x00000010) +#define TX_ERROR_STATUS_RER_ON_QUEUE_0 (0x00000020) +#define TX_ERROR_STATUS_TER_ON_QUEUE_0 (0x00000040) +#define TX_ERROR_STATUS_DER_ON_QUEUE_0 (0x00000080) +#define TX_ERROR_STATUS_QUEUE_1_ERROR_RESPONSE (0x00000f00) +#define TX_ERROR_STATUS_TEA_ON_QUEUE_1 (0x00001000) +#define TX_ERROR_STATUS_RER_ON_QUEUE_1 (0x00002000) +#define TX_ERROR_STATUS_TER_ON_QUEUE_1 (0x00004000) +#define TX_ERROR_STATUS_DER_ON_QUEUE_1 (0x00008000) +#define TX_ERROR_STATUS_QUEUE_2_ERROR_RESPONSE (0x000f0000) +#define TX_ERROR_STATUS_TEA_ON_QUEUE_2 (0x00100000) +#define TX_ERROR_STATUS_RER_ON_QUEUE_2 (0x00200000) +#define TX_ERROR_STATUS_TER_ON_QUEUE_2 (0x00400000) +#define TX_ERROR_STATUS_DER_ON_QUEUE_2 (0x00800000) +#define TX_ERROR_STATUS_QUEUE_3_ERROR_RESPONSE (0x0f000000) +#define TX_ERROR_STATUS_TEA_ON_QUEUE_3 (0x10000000) +#define TX_ERROR_STATUS_RER_ON_QUEUE_3 (0x20000000) +#define TX_ERROR_STATUS_TER_ON_QUEUE_3 (0x40000000) +#define TX_ERROR_STATUS_DER_ON_QUEUE_3 (0x80000000) + +#define reg_TX_QUEUE_0_CONFIG(base) __REG32(base, 0x00000280) +#define TX_QUEUE_0_CONFIG_OCN_PORT (0x0000003f) +#define TX_QUEUE_0_CONFIG_BSWP (0x00000400) +#define TX_QUEUE_0_CONFIG_WSWP (0x00000800) +#define TX_QUEUE_0_CONFIG_AM (0x00004000) +#define TX_QUEUE_0_CONFIG_GVI (0x00008000) +#define TX_QUEUE_0_CONFIG_EEI (0x00010000) +#define TX_QUEUE_0_CONFIG_ELI (0x00020000) +#define TX_QUEUE_0_CONFIG_ENI (0x00040000) +#define TX_QUEUE_0_CONFIG_ESI (0x00080000) +#define TX_QUEUE_0_CONFIG_EDI (0x00100000) + +#define reg_TX_QUEUE_0_BUF_CONFIG(base) __REG32(base, 0x00000284) +#define TX_QUEUE_0_BUF_CONFIG_OCN_PORT (0x0000003f) +#define TX_QUEUE_0_BUF_CONFIG_BURST (0x00000300) +#define TX_QUEUE_0_BUF_CONFIG_BSWP (0x00000400) +#define TX_QUEUE_0_BUF_CONFIG_WSWP (0x00000800) + +#define OCN_PORT_HLP 0 /* HLP Interface */ +#define OCN_PORT_PCI_X 1 /* PCI-X Interface */ +#define OCN_PORT_PROCESSOR_MASTER 2 /* Processor Interface (master) */ +#define OCN_PORT_PROCESSOR_SLAVE 3 /* Processor Interface (slave) */ +#define OCN_PORT_MEMORY 4 /* Memory Controller */ +#define OCN_PORT_DMA 5 /* DMA Controller */ +#define OCN_PORT_ETHERNET 6 /* Ethernet Controller */ +#define OCN_PORT_PRINT 7 /* Print Engine Interface */ + +#define reg_TX_QUEUE_0_PTR_LOW(base) __REG32(base, 0x00000288) + +#define reg_TX_QUEUE_0_PTR_HIGH(base) __REG32(base, 0x0000028c) +#define TX_QUEUE_0_PTR_HIGH_VALID (0x80000000) + +#define reg_RX_CONFIG(base) __REG32(base, 0x00000320) +#define RX_CONFIG_DEF_Q (0x00000003) +#define RX_CONFIG_EMF (0x00000100) +#define RX_CONFIG_EUF (0x00000200) +#define RX_CONFIG_BFE (0x00000400) +#define RX_CONFIG_MFE (0x00000800) +#define RX_CONFIG_UFE (0x00001000) +#define RX_CONFIG_SE (0x00002000) +#define RX_CONFIG_ABF (0x00200000) +#define RX_CONFIG_APE (0x00400000) +#define RX_CONFIG_CHP (0x00800000) +#define RX_CONFIG_RST (0x80000000) + +#define reg_RX_CONTROL(base) __REG32(base, 0x00000324) +#define GE_E0_RX_CONTROL_QUEUE_ENABLES (0x0000000f) +#define GE_E0_RX_CONTROL_GO (0x00008000) +#define GE_E0_RX_CONTROL_EAI (0x20000000) +#define GE_E0_RX_CONTROL_ABT (0x40000000) +#define GE_E0_RX_CONTROL_EII (0x80000000) + +#define reg_RX_EXTENDED_STATUS(base) __REG32(base, 0x0000032c) +#define RX_EXTENDED_STATUS (0x0000032c) +#define RX_EXTENDED_STATUS_EOQ (0x0000000f) +#define RX_EXTENDED_STATUS_EOQ_0 (0x00000001) +#define RX_EXTENDED_STATUS_EOF (0x00000f00) +#define RX_EXTENDED_STATUS_DESCRIPTOR_INTERRUPT_CONDITION (0x000f0000) +#define RX_EXTENDED_STATUS_ERROR_FLAG (0x0f000000) + +#define reg_RX_THRESHOLDS(base) __REG32(base, 0x00000330) + +#define reg_RX_DIAGNOSTIC_ADDR(base) __REG32(base, 0x00000370) +#define RX_DIAGNOSTIC_ADDR_INDEX (0x0000007f) +#define RX_DIAGNOSTIC_ADDR_DFR (0x40000000) +#define RX_DIAGNOSTIC_ADDR_AI (0x80000000) + +#define reg_RX_DIAGNOSTIC_DATA(base) __REG32(base, 0x00000374) + +#define reg_RX_QUEUE_0_CONFIG(base) __REG32(base, 0x00000380) +#define RX_QUEUE_0_CONFIG_OCN_PORT (0x0000003f) +#define RX_QUEUE_0_CONFIG_BSWP (0x00000400) +#define RX_QUEUE_0_CONFIG_WSWP (0x00000800) +#define RX_QUEUE_0_CONFIG_AM (0x00004000) +#define RX_QUEUE_0_CONFIG_EEI (0x00010000) +#define RX_QUEUE_0_CONFIG_ELI (0x00020000) +#define RX_QUEUE_0_CONFIG_ENI (0x00040000) +#define RX_QUEUE_0_CONFIG_ESI (0x00080000) +#define RX_QUEUE_0_CONFIG_EDI (0x00100000) + +#define reg_RX_QUEUE_0_BUF_CONFIG(base) __REG32(base, 0x00000384) +#define RX_QUEUE_0_BUF_CONFIG_OCN_PORT (0x0000003f) +#define RX_QUEUE_0_BUF_CONFIG_BURST (0x00000300) +#define RX_QUEUE_0_BUF_CONFIG_BSWP (0x00000400) +#define RX_QUEUE_0_BUF_CONFIG_WSWP (0x00000800) + +#define reg_RX_QUEUE_0_PTR_LOW(base) __REG32(base, 0x00000388) + +#define reg_RX_QUEUE_0_PTR_HIGH(base) __REG32(base, 0x0000038c) +#define RX_QUEUE_0_PTR_HIGH_VALID (0x80000000) + +/* + * PHY register definitions + */ +/* the first 15 PHY registers are standard. */ +#define PHY_CTRL_REG 0 /* Control Register */ +#define PHY_STATUS_REG 1 /* Status Regiser */ +#define PHY_ID1_REG 2 /* Phy Id Reg (word 1) */ +#define PHY_ID2_REG 3 /* Phy Id Reg (word 2) */ +#define PHY_AN_ADV_REG 4 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY_REG 5 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP_REG 6 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX_REG 7 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE_REG 8 /* Link Partner Next Page */ +#define PHY_1000T_CTRL_REG 9 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS_REG 10 /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS_REG 11 /* Extended Status Reg */ + +/* + * PHY Register bit masks. + */ +#define PHY_CTRL_RESET (1 << 15) +#define PHY_CTRL_LOOPBACK (1 << 14) +#define PHY_CTRL_SPEED0 (1 << 13) +#define PHY_CTRL_AN_EN (1 << 12) +#define PHY_CTRL_PWR_DN (1 << 11) +#define PHY_CTRL_ISOLATE (1 << 10) +#define PHY_CTRL_RESTART_AN (1 << 9) +#define PHY_CTRL_FULL_DUPLEX (1 << 8) +#define PHY_CTRL_CT_EN (1 << 7) +#define PHY_CTRL_SPEED1 (1 << 6) + +#define PHY_STAT_100BASE_T4 (1 << 15) +#define PHY_STAT_100BASE_X_FD (1 << 14) +#define PHY_STAT_100BASE_X_HD (1 << 13) +#define PHY_STAT_10BASE_T_FD (1 << 12) +#define PHY_STAT_10BASE_T_HD (1 << 11) +#define PHY_STAT_100BASE_T2_FD (1 << 10) +#define PHY_STAT_100BASE_T2_HD (1 << 9) +#define PHY_STAT_EXT_STAT (1 << 8) +#define PHY_STAT_RESERVED (1 << 7) +#define PHY_STAT_MFPS (1 << 6) /* Management Frames Preamble Suppression */ +#define PHY_STAT_AN_COMPLETE (1 << 5) +#define PHY_STAT_REM_FAULT (1 << 4) +#define PHY_STAT_AN_CAP (1 << 3) +#define PHY_STAT_LINK_UP (1 << 2) +#define PHY_STAT_JABBER (1 << 1) +#define PHY_STAT_EXT_CAP (1 << 0) + +#define TBI_CONTROL_2 0x11 +#define TBI_CONTROL_2_ENABLE_COMMA_DETECT 0x0001 +#define TBI_CONTROL_2_ENABLE_WRAP 0x0002 +#define TBI_CONTROL_2_G_MII_MODE 0x0010 +#define TBI_CONTROL_2_RECEIVE_CLOCK_SELECT 0x0020 +#define TBI_CONTROL_2_AUTO_NEGOTIATION_SENSE 0x0100 +#define TBI_CONTROL_2_DISABLE_TRANSMIT_RUNNING_DISPARITY 0x1000 +#define TBI_CONTROL_2_DISABLE_RECEIVE_RUNNING_DISPARITY 0x2000 +#define TBI_CONTROL_2_SHORTCUT_LINK_TIMER 0x4000 +#define TBI_CONTROL_2_SOFT_RESET 0x8000 + +/* marvel specific */ +#define MV1111_EXT_CTRL1_REG 16 /* PHY Specific Control Reg */ +#define MV1111_SPEC_STAT_REG 17 /* PHY Specific Status Reg */ +#define MV1111_EXT_CTRL2_REG 20 /* Extended PHY Specific Control Reg */ + +/* + * MARVELL 88E1111 PHY register bit masks + */ +/* PHY Specific Status Register (MV1111_EXT_CTRL1_REG) */ + +#define SPEC_STAT_SPEED_MASK (3 << 14) +#define SPEC_STAT_FULL_DUP (1 << 13) +#define SPEC_STAT_PAGE_RCVD (1 << 12) +#define SPEC_STAT_RESOLVED (1 << 11) /* Speed and Duplex Resolved */ +#define SPEC_STAT_LINK_UP (1 << 10) +#define SPEC_STAT_CABLE_LEN_MASK (7 << 7)/* Cable Length (100/1000 modes only) */ +#define SPEC_STAT_MDIX (1 << 6) +#define SPEC_STAT_POLARITY (1 << 1) +#define SPEC_STAT_JABBER (1 << 0) + +#define SPEED_1000 (2 << 14) +#define SPEED_100 (1 << 14) +#define SPEED_10 (0 << 14) + +#define TBI_ADDR 0x1E /* Ten Bit Interface address */ + +/* negotiated link parameters */ +#define LINK_SPEED_UNKNOWN 0 +#define LINK_SPEED_10 1 +#define LINK_SPEED_100 2 +#define LINK_SPEED_1000 3 + +#define LINK_DUPLEX_UNKNOWN 0 +#define LINK_DUPLEX_HALF 1 +#define LINK_DUPLEX_FULL 2 + +static unsigned int phy_address[] = { 8, 9 }; + +#define vuint32 volatile u32 + +/* TX/RX buffer descriptors. MUST be cache line aligned in memory. (32 byte) + * This structure is accessed by the ethernet DMA engine which means it + * MUST be in LITTLE ENDIAN format */ +struct dma_descriptor { + vuint32 start_addr0; /* buffer address, least significant bytes. */ + vuint32 start_addr1; /* buffer address, most significant bytes. */ + vuint32 next_descr_addr0;/* next descriptor address, least significant bytes. Must be 64-bit aligned. */ + vuint32 next_descr_addr1;/* next descriptor address, most significant bytes. */ + vuint32 vlan_byte_count;/* VLAN tag(top 2 bytes) and byte countt (bottom 2 bytes). */ + vuint32 config_status; /* Configuration/Status. */ + vuint32 reserved1; /* reserved to make the descriptor cache line aligned. */ + vuint32 reserved2; /* reserved to make the descriptor cache line aligned. */ +}; + +/* last next descriptor address flag */ +#define DMA_DESCR_LAST (1 << 31) + +/* TX DMA descriptor config status bits */ +#define DMA_DESCR_TX_EOF (1 << 0) /* end of frame */ +#define DMA_DESCR_TX_SOF (1 << 1) /* start of frame */ +#define DMA_DESCR_TX_PFVLAN (1 << 2) +#define DMA_DESCR_TX_HUGE (1 << 3) +#define DMA_DESCR_TX_PAD (1 << 4) +#define DMA_DESCR_TX_CRC (1 << 5) +#define DMA_DESCR_TX_DESCR_INT (1 << 14) +#define DMA_DESCR_TX_RETRY_COUNT 0x000F0000 +#define DMA_DESCR_TX_ONE_COLLISION (1 << 20) +#define DMA_DESCR_TX_LATE_COLLISION (1 << 24) +#define DMA_DESCR_TX_UNDERRUN (1 << 25) +#define DMA_DESCR_TX_RETRY_LIMIT (1 << 26) +#define DMA_DESCR_TX_OK (1 << 30) +#define DMA_DESCR_TX_OWNER (1 << 31) + +/* RX DMA descriptor status bits */ +#define DMA_DESCR_RX_EOF (1 << 0) +#define DMA_DESCR_RX_SOF (1 << 1) +#define DMA_DESCR_RX_VTF (1 << 2) +#define DMA_DESCR_RX_FRAME_IS_TYPE (1 << 3) +#define DMA_DESCR_RX_SHORT_FRAME (1 << 4) +#define DMA_DESCR_RX_HASH_MATCH (1 << 7) +#define DMA_DESCR_RX_BAD_FRAME (1 << 8) +#define DMA_DESCR_RX_OVERRUN (1 << 9) +#define DMA_DESCR_RX_MAX_FRAME_LEN (1 << 11) +#define DMA_DESCR_RX_CRC_ERROR (1 << 12) +#define DMA_DESCR_RX_DESCR_INT (1 << 13) +#define DMA_DESCR_RX_OWNER (1 << 15) + +#define RX_BUFFER_SIZE PKTSIZE +#define NUM_RX_DESC PKTBUFSRX + +static struct dma_descriptor tx_descriptor __attribute__ ((aligned(32))); + +static struct dma_descriptor rx_descr_array[NUM_RX_DESC] + __attribute__ ((aligned(32))); + +static struct dma_descriptor *rx_descr_current; + +static int tsi108_eth_probe (struct eth_device *dev, bd_t * bis); +static int tsi108_eth_send(struct eth_device *dev, void *packet, int length); +static int tsi108_eth_recv (struct eth_device *dev); +static void tsi108_eth_halt (struct eth_device *dev); +static unsigned int read_phy (unsigned int base, + unsigned int phy_addr, unsigned int phy_reg); +static void write_phy (unsigned int base, + unsigned int phy_addr, + unsigned int phy_reg, unsigned int phy_data); + +#if TSI108_ETH_DEBUG > 100 +/* + * print phy debug infomation + */ +static void dump_phy_regs (unsigned int phy_addr) +{ + int i; + + printf ("PHY %d registers\n", phy_addr); + for (i = 0; i <= 30; i++) { + printf ("%2d 0x%04x\n", i, read_phy (ETH_BASE, phy_addr, i)); + } + printf ("\n"); + +} +#else +#define dump_phy_regs(base) do{}while(0) +#endif + +#if TSI108_ETH_DEBUG > 100 +/* + * print debug infomation + */ +static void tx_diag_regs (unsigned int base) +{ + int i; + unsigned long dummy; + + printf ("TX diagnostics registers\n"); + reg_TX_DIAGNOSTIC_ADDR(base) = 0x00 | TX_DIAGNOSTIC_ADDR_AI; + udelay (1000); + dummy = reg_TX_DIAGNOSTIC_DATA(base); + for (i = 0x00; i <= 0x05; i++) { + udelay (1000); + printf ("0x%02x 0x%08x\n", i, reg_TX_DIAGNOSTIC_DATA(base)); + } + reg_TX_DIAGNOSTIC_ADDR(base) = 0x40 | TX_DIAGNOSTIC_ADDR_AI; + udelay (1000); + dummy = reg_TX_DIAGNOSTIC_DATA(base); + for (i = 0x40; i <= 0x47; i++) { + udelay (1000); + printf ("0x%02x 0x%08x\n", i, reg_TX_DIAGNOSTIC_DATA(base)); + } + printf ("\n"); + +} +#else +#define tx_diag_regs(base) do{}while(0) +#endif + +#if TSI108_ETH_DEBUG > 100 +/* + * print debug infomation + */ +static void rx_diag_regs (unsigned int base) +{ + int i; + unsigned long dummy; + + printf ("RX diagnostics registers\n"); + reg_RX_DIAGNOSTIC_ADDR(base) = 0x00 | RX_DIAGNOSTIC_ADDR_AI; + udelay (1000); + dummy = reg_RX_DIAGNOSTIC_DATA(base); + for (i = 0x00; i <= 0x05; i++) { + udelay (1000); + printf ("0x%02x 0x%08x\n", i, reg_RX_DIAGNOSTIC_DATA(base)); + } + reg_RX_DIAGNOSTIC_ADDR(base) = 0x40 | RX_DIAGNOSTIC_ADDR_AI; + udelay (1000); + dummy = reg_RX_DIAGNOSTIC_DATA(base); + for (i = 0x08; i <= 0x0a; i++) { + udelay (1000); + printf ("0x%02x 0x%08x\n", i, reg_RX_DIAGNOSTIC_DATA(base)); + } + printf ("\n"); + +} +#else +#define rx_diag_regs(base) do{}while(0) +#endif + +#if TSI108_ETH_DEBUG > 100 +/* + * print debug infomation + */ +static void debug_mii_regs (unsigned int base) +{ + printf ("MII_MGMT_CONFIG 0x%08x\n", reg_MII_MGMT_CONFIG(base)); + printf ("MII_MGMT_COMMAND 0x%08x\n", reg_MII_MGMT_COMMAND(base)); + printf ("MII_MGMT_ADDRESS 0x%08x\n", reg_MII_MGMT_ADDRESS(base)); + printf ("MII_MGMT_CONTROL 0x%08x\n", reg_MII_MGMT_CONTROL(base)); + printf ("MII_MGMT_STATUS 0x%08x\n", reg_MII_MGMT_STATUS(base)); + printf ("MII_MGMT_INDICATORS 0x%08x\n", reg_MII_MGMT_INDICATORS(base)); + printf ("\n"); + +} +#else +#define debug_mii_regs(base) do{}while(0) +#endif + +/* + * Wait until the phy bus is non-busy + */ +static void phy_wait (unsigned int base, unsigned int condition) +{ + int timeout; + + timeout = 0; + while (reg_MII_MGMT_INDICATORS(base) & condition) { + udelay (10); + if (++timeout > 10000) { + printf ("ERROR: timeout waiting for phy bus (%d)\n", + condition); + break; + } + } +} + +/* + * read phy register + */ +static unsigned int read_phy (unsigned int base, + unsigned int phy_addr, unsigned int phy_reg) +{ + unsigned int value; + + phy_wait (base, MII_MGMT_INDICATORS_BUSY); + + reg_MII_MGMT_ADDRESS(base) = (phy_addr << 8) | phy_reg; + + /* Ensure that the Read Cycle bit is cleared prior to next read cycle */ + reg_MII_MGMT_COMMAND(base) = 0; + + /* start the read */ + reg_MII_MGMT_COMMAND(base) = MII_MGMT_COMMAND_READ_CYCLE; + + /* wait for the read to complete */ + phy_wait (base, + MII_MGMT_INDICATORS_NOT_VALID | MII_MGMT_INDICATORS_BUSY); + + value = reg_MII_MGMT_STATUS(base); + + reg_MII_MGMT_COMMAND(base) = 0; + + return value; +} + +/* + * write phy register + */ +static void write_phy (unsigned int base, + unsigned int phy_addr, + unsigned int phy_reg, unsigned int phy_data) +{ + phy_wait (base, MII_MGMT_INDICATORS_BUSY); + + reg_MII_MGMT_ADDRESS(base) = (phy_addr << 8) | phy_reg; + + /* Ensure that the Read Cycle bit is cleared prior to next cycle */ + reg_MII_MGMT_COMMAND(base) = 0; + + /* start the write */ + reg_MII_MGMT_CONTROL(base) = phy_data; +} + +/* + * configure the marvell 88e1111 phy + */ +static int marvell_88e_phy_config (struct eth_device *dev, int *speed, + int *duplex) +{ + unsigned long base; + unsigned long phy_addr; + unsigned int phy_status; + unsigned int phy_spec_status; + int timeout; + int phy_speed; + int phy_duplex; + unsigned int value; + + phy_speed = LINK_SPEED_UNKNOWN; + phy_duplex = LINK_DUPLEX_UNKNOWN; + + base = dev->iobase; + phy_addr = (unsigned long)dev->priv; + + /* Take the PHY out of reset. */ + write_phy (ETH_BASE, phy_addr, PHY_CTRL_REG, PHY_CTRL_RESET); + + /* Wait for the reset process to complete. */ + udelay (10); + timeout = 0; + while ((phy_status = + read_phy (ETH_BASE, phy_addr, PHY_CTRL_REG)) & PHY_CTRL_RESET) { + udelay (10); + if (++timeout > 10000) { + printf ("ERROR: timeout waiting for phy reset\n"); + break; + } + } + + /* TBI Configuration. */ + write_phy (base, TBI_ADDR, TBI_CONTROL_2, TBI_CONTROL_2_G_MII_MODE | + TBI_CONTROL_2_RECEIVE_CLOCK_SELECT); + /* Wait for the link to be established. */ + timeout = 0; + do { + udelay (20000); + phy_status = read_phy (ETH_BASE, phy_addr, PHY_STATUS_REG); + if (++timeout > 100) { + debug_lev(1, "ERROR: unable to establish link!!!\n"); + break; + } + } while ((phy_status & PHY_STAT_LINK_UP) == 0); + + if ((phy_status & PHY_STAT_LINK_UP) == 0) + return 0; + + value = 0; + phy_spec_status = read_phy (ETH_BASE, phy_addr, MV1111_SPEC_STAT_REG); + if (phy_spec_status & SPEC_STAT_RESOLVED) { + switch (phy_spec_status & SPEC_STAT_SPEED_MASK) { + case SPEED_1000: + phy_speed = LINK_SPEED_1000; + value |= PHY_CTRL_SPEED1; + break; + case SPEED_100: + phy_speed = LINK_SPEED_100; + value |= PHY_CTRL_SPEED0; + break; + case SPEED_10: + phy_speed = LINK_SPEED_10; + break; + } + if (phy_spec_status & SPEC_STAT_FULL_DUP) { + phy_duplex = LINK_DUPLEX_FULL; + value |= PHY_CTRL_FULL_DUPLEX; + } else + phy_duplex = LINK_DUPLEX_HALF; + } + /* set TBI speed */ + write_phy (base, TBI_ADDR, PHY_CTRL_REG, value); + write_phy (base, TBI_ADDR, PHY_AN_ADV_REG, 0x0060); + +#if TSI108_ETH_DEBUG > 0 + printf ("%s link is up", dev->name); + phy_spec_status = read_phy (ETH_BASE, phy_addr, MV1111_SPEC_STAT_REG); + if (phy_spec_status & SPEC_STAT_RESOLVED) { + switch (phy_speed) { + case LINK_SPEED_1000: + printf (", 1000 Mbps"); + break; + case LINK_SPEED_100: + printf (", 100 Mbps"); + break; + case LINK_SPEED_10: + printf (", 10 Mbps"); + break; + } + if (phy_duplex == LINK_DUPLEX_FULL) + printf (", Full duplex"); + else + printf (", Half duplex"); + } + printf ("\n"); +#endif + + dump_phy_regs (TBI_ADDR); + if (speed) + *speed = phy_speed; + if (duplex) + *duplex = phy_duplex; + + return 1; +} + +/* + * External interface + * + * register the tsi108 ethernet controllers with the multi-ethernet system + */ +int tsi108_eth_initialize (bd_t * bis) +{ + struct eth_device *dev; + int index; + + for (index = 0; index < CONFIG_TSI108_ETH_NUM_PORTS; index++) { + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + if (!dev) { + printf("tsi108: Can not allocate memory\n"); + break; + } + memset(dev, 0, sizeof(*dev)); + sprintf (dev->name, "TSI108_eth%d", index); + + dev->iobase = ETH_BASE + (index * ETH_PORT_OFFSET); + dev->priv = (void *)(phy_address[index]); + dev->init = tsi108_eth_probe; + dev->halt = tsi108_eth_halt; + dev->send = tsi108_eth_send; + dev->recv = tsi108_eth_recv; + + eth_register(dev); + } + return index; +} + +/* + * probe for and initialize a single ethernet interface + */ +static int tsi108_eth_probe (struct eth_device *dev, bd_t * bis) +{ + unsigned long base; + unsigned long value; + int index; + struct dma_descriptor *tx_descr; + struct dma_descriptor *rx_descr; + int speed; + int duplex; + + base = dev->iobase; + + reg_PORT_CONTROL(base) = PORT_CONTROL_STE | PORT_CONTROL_BPT; + + /* Bring DMA/FIFO out of reset. */ + reg_TX_CONFIG(base) = 0x00000000; + reg_RX_CONFIG(base) = 0x00000000; + + reg_TX_THRESHOLDS(base) = (192 << 16) | 192; + reg_RX_THRESHOLDS(base) = (192 << 16) | 112; + + /* Bring MAC out of reset. */ + reg_MAC_CONFIG_1(base) = 0x00000000; + + /* DMA MAC configuration. */ + reg_MAC_CONFIG_1(base) = + MAC_CONFIG_1_RX_ENABLE | MAC_CONFIG_1_TX_ENABLE; + + reg_MII_MGMT_CONFIG(base) = MII_MGMT_CONFIG_NO_PREAMBLE; + reg_MAXIMUM_FRAME_LENGTH(base) = RX_BUFFER_SIZE; + + /* Note: Early tsi108 manual did not have correct byte order + * for the station address.*/ + reg_STATION_ADDRESS_1(base) = (dev->enetaddr[5] << 24) | + (dev->enetaddr[4] << 16) | + (dev->enetaddr[3] << 8) | (dev->enetaddr[2] << 0); + + reg_STATION_ADDRESS_2(base) = (dev->enetaddr[1] << 24) | + (dev->enetaddr[0] << 16); + + if (marvell_88e_phy_config(dev, &speed, &duplex) == 0) + return -1; + + value = + MAC_CONFIG_2_PREAMBLE_LENGTH(7) | MAC_CONFIG_2_PAD_CRC | + MAC_CONFIG_2_CRC_ENABLE; + if (speed == LINK_SPEED_1000) + value |= MAC_CONFIG_2_INTERFACE_MODE(INTERFACE_MODE_BYTE); + else { + value |= MAC_CONFIG_2_INTERFACE_MODE(INTERFACE_MODE_NIBBLE); + reg_PORT_CONTROL(base) |= PORT_CONTROL_SPD; + } + if (duplex == LINK_DUPLEX_FULL) { + value |= MAC_CONFIG_2_FULL_DUPLEX; + reg_PORT_CONTROL(base) &= ~PORT_CONTROL_BPT; + } else + reg_PORT_CONTROL(base) |= PORT_CONTROL_BPT; + reg_MAC_CONFIG_2(base) = value; + + reg_RX_CONFIG(base) = RX_CONFIG_SE; + reg_RX_QUEUE_0_CONFIG(base) = OCN_PORT_MEMORY; + reg_RX_QUEUE_0_BUF_CONFIG(base) = OCN_PORT_MEMORY; + + /* initialize the RX DMA descriptors */ + rx_descr = &rx_descr_array[0]; + rx_descr_current = rx_descr; + for (index = 0; index < NUM_RX_DESC; index++) { + /* make sure the receive buffers are not in cache */ + invalidate_dcache_range((unsigned long)NetRxPackets[index], + (unsigned long)NetRxPackets[index] + + RX_BUFFER_SIZE); + rx_descr->start_addr0 = + cpu_to_le32((vuint32) NetRxPackets[index]); + rx_descr->start_addr1 = 0; + rx_descr->next_descr_addr0 = + cpu_to_le32((vuint32) (rx_descr + 1)); + rx_descr->next_descr_addr1 = 0; + rx_descr->vlan_byte_count = 0; + rx_descr->config_status = cpu_to_le32((RX_BUFFER_SIZE << 16) | + DMA_DESCR_RX_OWNER); + rx_descr++; + } + rx_descr--; + rx_descr->next_descr_addr0 = 0; + rx_descr->next_descr_addr1 = cpu_to_le32(DMA_DESCR_LAST); + /* Push the descriptors to RAM so the ethernet DMA can see them */ + invalidate_dcache_range((unsigned long)rx_descr_array, + (unsigned long)rx_descr_array + + sizeof(rx_descr_array)); + + /* enable RX queue */ + reg_RX_CONTROL(base) = TX_CONTROL_GO | 0x01; + reg_RX_QUEUE_0_PTR_LOW(base) = (u32) rx_descr_current; + /* enable receive DMA */ + reg_RX_QUEUE_0_PTR_HIGH(base) = RX_QUEUE_0_PTR_HIGH_VALID; + + reg_TX_QUEUE_0_CONFIG(base) = OCN_PORT_MEMORY; + reg_TX_QUEUE_0_BUF_CONFIG(base) = OCN_PORT_MEMORY; + + /* initialize the TX DMA descriptor */ + tx_descr = &tx_descriptor; + + tx_descr->start_addr0 = 0; + tx_descr->start_addr1 = 0; + tx_descr->next_descr_addr0 = 0; + tx_descr->next_descr_addr1 = cpu_to_le32(DMA_DESCR_LAST); + tx_descr->vlan_byte_count = 0; + tx_descr->config_status = cpu_to_le32(DMA_DESCR_TX_OK | + DMA_DESCR_TX_SOF | + DMA_DESCR_TX_EOF); + /* enable TX queue */ + reg_TX_CONTROL(base) = TX_CONTROL_GO | 0x01; + + return 0; +} + +/* + * send a packet + */ +static int tsi108_eth_send(struct eth_device *dev, void *packet, int length) +{ + unsigned long base; + int timeout; + struct dma_descriptor *tx_descr; + unsigned long status; + + base = dev->iobase; + tx_descr = &tx_descriptor; + + /* Wait until the last packet has been transmitted. */ + timeout = 0; + do { + /* make sure we see the changes made by the DMA engine */ + invalidate_dcache_range((unsigned long)tx_descr, + (unsigned long)tx_descr + + sizeof(struct dma_descriptor)); + + if (timeout != 0) + udelay (15); + if (++timeout > 10000) { + tx_diag_regs(base); + debug_lev(1, + "ERROR: timeout waiting for last transmit packet to be sent\n"); + return 0; + } + } while (tx_descr->config_status & cpu_to_le32(DMA_DESCR_TX_OWNER)); + + status = le32_to_cpu(tx_descr->config_status); + if ((status & DMA_DESCR_TX_OK) == 0) { +#ifdef TX_PRINT_ERRORS + printf ("TX packet error: 0x%08lx\n %s%s%s%s\n", status, + status & DMA_DESCR_TX_OK ? "tx error, " : "", + status & DMA_DESCR_TX_RETRY_LIMIT ? + "retry limit reached, " : "", + status & DMA_DESCR_TX_UNDERRUN ? "underrun, " : "", + status & DMA_DESCR_TX_LATE_COLLISION ? "late collision, " + : ""); +#endif + } + + debug_lev (9, "sending packet %d\n", length); + tx_descr->start_addr0 = cpu_to_le32((vuint32) packet); + tx_descr->start_addr1 = 0; + tx_descr->next_descr_addr0 = 0; + tx_descr->next_descr_addr1 = cpu_to_le32(DMA_DESCR_LAST); + tx_descr->vlan_byte_count = cpu_to_le32(length); + tx_descr->config_status = cpu_to_le32(DMA_DESCR_TX_OWNER | + DMA_DESCR_TX_CRC | + DMA_DESCR_TX_PAD | + DMA_DESCR_TX_SOF | + DMA_DESCR_TX_EOF); + + invalidate_dcache_range((unsigned long)tx_descr, + (unsigned long)tx_descr + + sizeof(struct dma_descriptor)); + + invalidate_dcache_range((unsigned long)packet, + (unsigned long)packet + length); + + reg_TX_QUEUE_0_PTR_LOW(base) = (u32) tx_descr; + reg_TX_QUEUE_0_PTR_HIGH(base) = TX_QUEUE_0_PTR_HIGH_VALID; + + return length; +} + +/* + * Check for received packets and send them up the protocal stack + */ +static int tsi108_eth_recv (struct eth_device *dev) +{ + struct dma_descriptor *rx_descr; + unsigned long base; + int length = 0; + unsigned long status; + uchar *buffer; + + base = dev->iobase; + + /* make sure we see the changes made by the DMA engine */ + invalidate_dcache_range ((unsigned long)rx_descr_array, + (unsigned long)rx_descr_array + + sizeof(rx_descr_array)); + + /* process all of the received packets */ + rx_descr = rx_descr_current; + while ((rx_descr->config_status & cpu_to_le32(DMA_DESCR_RX_OWNER)) == 0) { + /* check for error */ + status = le32_to_cpu(rx_descr->config_status); + if (status & DMA_DESCR_RX_BAD_FRAME) { +#ifdef RX_PRINT_ERRORS + printf ("RX packet error: 0x%08lx\n %s%s%s%s%s%s\n", + status, + status & DMA_DESCR_RX_FRAME_IS_TYPE ? "too big, " + : "", + status & DMA_DESCR_RX_SHORT_FRAME ? "too short, " + : "", + status & DMA_DESCR_RX_BAD_FRAME ? "bad frame, " : + "", + status & DMA_DESCR_RX_OVERRUN ? "overrun, " : "", + status & DMA_DESCR_RX_MAX_FRAME_LEN ? + "max length, " : "", + status & DMA_DESCR_RX_CRC_ERROR ? "CRC error, " : + ""); +#endif + } else { + length = + le32_to_cpu(rx_descr->vlan_byte_count) & 0xFFFF; + + /*** process packet ***/ + buffer = (uchar *)(le32_to_cpu(rx_descr->start_addr0)); + NetReceive(buffer, length); + + invalidate_dcache_range ((unsigned long)buffer, + (unsigned long)buffer + + RX_BUFFER_SIZE); + } + /* Give this buffer back to the DMA engine */ + rx_descr->vlan_byte_count = 0; + rx_descr->config_status = cpu_to_le32 ((RX_BUFFER_SIZE << 16) | + DMA_DESCR_RX_OWNER); + /* move descriptor pointer forward */ + rx_descr = + (struct dma_descriptor + *)(le32_to_cpu (rx_descr->next_descr_addr0)); + if (rx_descr == 0) + rx_descr = &rx_descr_array[0]; + } + /* remember where we are for next time */ + rx_descr_current = rx_descr; + + /* If the DMA engine has reached the end of the queue + * start over at the begining */ + if (reg_RX_EXTENDED_STATUS(base) & RX_EXTENDED_STATUS_EOQ_0) { + + reg_RX_EXTENDED_STATUS(base) = RX_EXTENDED_STATUS_EOQ_0; + reg_RX_QUEUE_0_PTR_LOW(base) = (u32) & rx_descr_array[0]; + reg_RX_QUEUE_0_PTR_HIGH(base) = RX_QUEUE_0_PTR_HIGH_VALID; + } + + return length; +} + +/* + * disable an ethernet interface + */ +static void tsi108_eth_halt (struct eth_device *dev) +{ + unsigned long base; + + base = dev->iobase; + + /* Put DMA/FIFO into reset state. */ + reg_TX_CONFIG(base) = TX_CONFIG_RST; + reg_RX_CONFIG(base) = RX_CONFIG_RST; + + /* Put MAC into reset state. */ + reg_MAC_CONFIG_1(base) = MAC_CONFIG_1_SOFT_RESET; +} diff --git a/qemu/roms/u-boot/drivers/net/uli526x.c b/qemu/roms/u-boot/drivers/net/uli526x.c new file mode 100644 index 000000000..538f11e3e --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/uli526x.c @@ -0,0 +1,996 @@ +/* + * Copyright 2007, 2010 Freescale Semiconductor, Inc. + * + * Author: Roy Zang , Sep, 2007 + * + * Description: + * ULI 526x Ethernet port driver. + * Based on the Linux driver: drivers/net/tulip/uli526x.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +/* some kernel function compatible define */ + +#undef DEBUG + +/* Board/System/Debug information/definition */ +#define ULI_VENDOR_ID 0x10B9 +#define ULI5261_DEVICE_ID 0x5261 +#define ULI5263_DEVICE_ID 0x5263 +/* ULi M5261 ID*/ +#define PCI_ULI5261_ID (ULI5261_DEVICE_ID << 16 | ULI_VENDOR_ID) +/* ULi M5263 ID*/ +#define PCI_ULI5263_ID (ULI5263_DEVICE_ID << 16 | ULI_VENDOR_ID) + +#define ULI526X_IO_SIZE 0x100 +#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ +#define RX_DESC_CNT PKTBUFSRX /* Allocated Rx descriptors */ +#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */ +#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */ +#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT) +#define TX_BUF_ALLOC 0x300 +#define RX_ALLOC_SIZE PKTSIZE +#define ULI526X_RESET 1 +#define CR0_DEFAULT 0 +#define CR6_DEFAULT 0x22200000 +#define CR7_DEFAULT 0x180c1 +#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ +#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ +#define MAX_PACKET_SIZE 1514 +#define ULI5261_MAX_MULTICAST 14 +#define RX_COPY_SIZE 100 +#define MAX_CHECK_PACKET 0x8000 + +#define ULI526X_10MHF 0 +#define ULI526X_100MHF 1 +#define ULI526X_10MFD 4 +#define ULI526X_100MFD 5 +#define ULI526X_AUTO 8 + +#define ULI526X_TXTH_72 0x400000 /* TX TH 72 byte */ +#define ULI526X_TXTH_96 0x404000 /* TX TH 96 byte */ +#define ULI526X_TXTH_128 0x0000 /* TX TH 128 byte */ +#define ULI526X_TXTH_256 0x4000 /* TX TH 256 byte */ +#define ULI526X_TXTH_512 0x8000 /* TX TH 512 byte */ +#define ULI526X_TXTH_1K 0xC000 /* TX TH 1K byte */ + +/* CR9 definition: SROM/MII */ +#define CR9_SROM_READ 0x4800 +#define CR9_SRCS 0x1 +#define CR9_SRCLK 0x2 +#define CR9_CRDOUT 0x8 +#define SROM_DATA_0 0x0 +#define SROM_DATA_1 0x4 +#define PHY_DATA_1 0x20000 +#define PHY_DATA_0 0x00000 +#define MDCLKH 0x10000 + +#define PHY_POWER_DOWN 0x800 + +#define SROM_V41_CODE 0x14 + +#define SROM_CLK_WRITE(data, ioaddr) do { \ + outl(data|CR9_SROM_READ|CR9_SRCS, ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK, ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS, ioaddr); \ + udelay(5); \ + } while (0) + +/* Structure/enum declaration */ + +struct tx_desc { + u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ + char *tx_buf_ptr; /* Data for us */ + struct tx_desc *next_tx_desc; +}; + +struct rx_desc { + u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ + char *rx_buf_ptr; /* Data for us */ + struct rx_desc *next_rx_desc; +}; + +struct uli526x_board_info { + u32 chip_id; /* Chip vendor/Device ID */ + pci_dev_t pdev; + + long ioaddr; /* I/O base address */ + u32 cr0_data; + u32 cr5_data; + u32 cr6_data; + u32 cr7_data; + u32 cr15_data; + + /* pointer for memory physical address */ + dma_addr_t buf_pool_dma_ptr; /* Tx buffer pool memory */ + dma_addr_t buf_pool_dma_start; /* Tx buffer pool align dword */ + dma_addr_t desc_pool_dma_ptr; /* descriptor pool memory */ + dma_addr_t first_tx_desc_dma; + dma_addr_t first_rx_desc_dma; + + /* descriptor pointer */ + unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ + unsigned char *buf_pool_start; /* Tx buffer pool align dword */ + unsigned char *desc_pool_ptr; /* descriptor pool memory */ + struct tx_desc *first_tx_desc; + struct tx_desc *tx_insert_ptr; + struct tx_desc *tx_remove_ptr; + struct rx_desc *first_rx_desc; + struct rx_desc *rx_ready_ptr; /* packet come pointer */ + unsigned long tx_packet_cnt; /* transmitted packet count */ + + u16 PHY_reg4; /* Saved Phyxcer register 4 value */ + + u8 media_mode; /* user specify media mode */ + u8 op_mode; /* real work dedia mode */ + u8 phy_addr; + + /* NIC SROM data */ + unsigned char srom[128]; +}; + +enum uli526x_offsets { + DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, + DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, + DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, + DCR15 = 0x78 +}; + +enum uli526x_CR6_bits { + CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, + CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000, + CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000 +}; + +/* Global variable declaration -- */ + +static unsigned char uli526x_media_mode = ULI526X_AUTO; + +static struct tx_desc desc_pool_array[DESC_ALL_CNT + 0x20] + __attribute__ ((aligned(32))); +static char buf_pool[TX_BUF_ALLOC * TX_DESC_CNT + 4]; + +/* For module input parameter */ +static int mode = 8; + +/* function declaration -- */ +static int uli526x_start_xmit(struct eth_device *dev, void *packet, int length); +static const struct ethtool_ops netdev_ethtool_ops; +static u16 read_srom_word(long, int); +static void uli526x_descriptor_init(struct uli526x_board_info *, unsigned long); +static void allocate_rx_buffer(struct uli526x_board_info *); +static void update_cr6(u32, unsigned long); +static u16 uli_phy_read(unsigned long, u8, u8, u32); +static u16 phy_readby_cr10(unsigned long, u8, u8); +static void uli_phy_write(unsigned long, u8, u8, u16, u32); +static void phy_writeby_cr10(unsigned long, u8, u8, u16); +static void phy_write_1bit(unsigned long, u32, u32); +static u16 phy_read_1bit(unsigned long, u32); +static int uli526x_rx_packet(struct eth_device *); +static void uli526x_free_tx_pkt(struct eth_device *, + struct uli526x_board_info *); +static void uli526x_reuse_buf(struct rx_desc *); +static void uli526x_init(struct eth_device *); +static void uli526x_set_phyxcer(struct uli526x_board_info *); + + +static int uli526x_init_one(struct eth_device *, bd_t *); +static void uli526x_disable(struct eth_device *); +static void set_mac_addr(struct eth_device *); + +static struct pci_device_id uli526x_pci_tbl[] = { + { ULI_VENDOR_ID, ULI5261_DEVICE_ID}, /* 5261 device */ + { ULI_VENDOR_ID, ULI5263_DEVICE_ID}, /* 5263 device */ + {} +}; + +/* ULI526X network board routine */ + +/* + * Search ULI526X board, register it + */ + +int uli526x_initialize(bd_t *bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + struct uli526x_board_info *db; /* board information structure */ + + u32 iobase; + int idx = 0; + + while (1) { + /* Find PCI device */ + devno = pci_find_devices(uli526x_pci_tbl, idx++); + if (devno < 0) + break; + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); + iobase &= ~0xf; + + dev = (struct eth_device *)malloc(sizeof *dev); + if (!dev) { + printf("uli526x: Can not allocate memory\n"); + break; + } + memset(dev, 0, sizeof(*dev)); + sprintf(dev->name, "uli526x#%d", card_number); + db = (struct uli526x_board_info *) + malloc(sizeof(struct uli526x_board_info)); + + dev->priv = db; + db->pdev = devno; + dev->iobase = iobase; + + dev->init = uli526x_init_one; + dev->halt = uli526x_disable; + dev->send = uli526x_start_xmit; + dev->recv = uli526x_rx_packet; + + /* init db */ + db->ioaddr = dev->iobase; + /* get chip id */ + + pci_read_config_dword(devno, PCI_VENDOR_ID, &db->chip_id); +#ifdef DEBUG + printf("uli526x: uli526x @0x%x\n", iobase); + printf("uli526x: chip_id%x\n", db->chip_id); +#endif + eth_register(dev); + card_number++; + pci_write_config_byte(devno, PCI_LATENCY_TIMER, 0x20); + udelay(10 * 1000); + } + return card_number; +} + +static int uli526x_init_one(struct eth_device *dev, bd_t *bis) +{ + + struct uli526x_board_info *db = dev->priv; + int i; + + switch (mode) { + case ULI526X_10MHF: + case ULI526X_100MHF: + case ULI526X_10MFD: + case ULI526X_100MFD: + uli526x_media_mode = mode; + break; + default: + uli526x_media_mode = ULI526X_AUTO; + break; + } + + /* Allocate Tx/Rx descriptor memory */ + db->desc_pool_ptr = (uchar *)&desc_pool_array[0]; + db->desc_pool_dma_ptr = (dma_addr_t)&desc_pool_array[0]; + if (db->desc_pool_ptr == NULL) + return -1; + + db->buf_pool_ptr = (uchar *)&buf_pool[0]; + db->buf_pool_dma_ptr = (dma_addr_t)&buf_pool[0]; + if (db->buf_pool_ptr == NULL) + return -1; + + db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; + db->first_tx_desc_dma = db->desc_pool_dma_ptr; + + db->buf_pool_start = db->buf_pool_ptr; + db->buf_pool_dma_start = db->buf_pool_dma_ptr; + +#ifdef DEBUG + printf("%s(): db->ioaddr= 0x%x\n", + __FUNCTION__, db->ioaddr); + printf("%s(): media_mode= 0x%x\n", + __FUNCTION__, uli526x_media_mode); + printf("%s(): db->desc_pool_ptr= 0x%x\n", + __FUNCTION__, db->desc_pool_ptr); + printf("%s(): db->desc_pool_dma_ptr= 0x%x\n", + __FUNCTION__, db->desc_pool_dma_ptr); + printf("%s(): db->buf_pool_ptr= 0x%x\n", + __FUNCTION__, db->buf_pool_ptr); + printf("%s(): db->buf_pool_dma_ptr= 0x%x\n", + __FUNCTION__, db->buf_pool_dma_ptr); +#endif + + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, + i)); + + /* Set Node address */ + if (((db->srom[0] == 0xff) && (db->srom[1] == 0xff)) || + ((db->srom[0] == 0x00) && (db->srom[1] == 0x00))) + /* SROM absent, so write MAC address to ID Table */ + set_mac_addr(dev); + else { /*Exist SROM*/ + for (i = 0; i < 6; i++) + dev->enetaddr[i] = db->srom[20 + i]; + } +#ifdef DEBUG + for (i = 0; i < 6; i++) + printf("%c%02x", i ? ':' : ' ', dev->enetaddr[i]); +#endif + db->PHY_reg4 = 0x1e0; + + /* system variable init */ + db->cr6_data = CR6_DEFAULT ; + db->cr6_data |= ULI526X_TXTH_256; + db->cr0_data = CR0_DEFAULT; + uli526x_init(dev); + return 0; +} + +static void uli526x_disable(struct eth_device *dev) +{ +#ifdef DEBUG + printf("uli526x_disable\n"); +#endif + struct uli526x_board_info *db = dev->priv; + + if (!((inl(db->ioaddr + DCR12)) & 0x8)) { + /* Reset & stop ULI526X board */ + outl(ULI526X_RESET, db->ioaddr + DCR0); + udelay(5); + uli_phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); + + /* reset the board */ + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */ + update_cr6(db->cr6_data, dev->iobase); + outl(0, dev->iobase + DCR7); /* Disable Interrupt */ + outl(inl(dev->iobase + DCR5), dev->iobase + DCR5); + } +} + +/* Initialize ULI526X board + * Reset ULI526X board + * Initialize TX/Rx descriptor chain structure + * Send the set-up frame + * Enable Tx/Rx machine + */ + +static void uli526x_init(struct eth_device *dev) +{ + + struct uli526x_board_info *db = dev->priv; + u8 phy_tmp; + u16 phy_value; + u16 phy_reg_reset; + + /* Reset M526x MAC controller */ + outl(ULI526X_RESET, db->ioaddr + DCR0); /* RESET MAC */ + udelay(100); + outl(db->cr0_data, db->ioaddr + DCR0); + udelay(5); + + /* Phy addr : In some boards,M5261/M5263 phy address != 1 */ + db->phy_addr = 1; + db->tx_packet_cnt = 0; + for (phy_tmp = 0; phy_tmp < 32; phy_tmp++) { + /* peer add */ + phy_value = uli_phy_read(db->ioaddr, phy_tmp, 3, db->chip_id); + if (phy_value != 0xffff && phy_value != 0) { + db->phy_addr = phy_tmp; + break; + } + } + +#ifdef DEBUG + printf("%s(): db->ioaddr= 0x%x\n", __FUNCTION__, db->ioaddr); + printf("%s(): db->phy_addr= 0x%x\n", __FUNCTION__, db->phy_addr); +#endif + if (phy_tmp == 32) + printf("Can not find the phy address!!!"); + + /* Parser SROM and media mode */ + db->media_mode = uli526x_media_mode; + + if (!(inl(db->ioaddr + DCR12) & 0x8)) { + /* Phyxcer capability setting */ + phy_reg_reset = uli_phy_read(db->ioaddr, + db->phy_addr, 0, db->chip_id); + phy_reg_reset = (phy_reg_reset | 0x8000); + uli_phy_write(db->ioaddr, db->phy_addr, 0, + phy_reg_reset, db->chip_id); + udelay(500); + + /* Process Phyxcer Media Mode */ + uli526x_set_phyxcer(db); + } + /* Media Mode Process */ + if (!(db->media_mode & ULI526X_AUTO)) + db->op_mode = db->media_mode; /* Force Mode */ + + /* Initialize Transmit/Receive decriptor and CR3/4 */ + uli526x_descriptor_init(db, db->ioaddr); + + /* Init CR6 to program M526X operation */ + update_cr6(db->cr6_data, db->ioaddr); + + /* Init CR7, interrupt active bit */ + db->cr7_data = CR7_DEFAULT; + outl(db->cr7_data, db->ioaddr + DCR7); + + /* Init CR15, Tx jabber and Rx watchdog timer */ + outl(db->cr15_data, db->ioaddr + DCR15); + + /* Enable ULI526X Tx/Rx function */ + db->cr6_data |= CR6_RXSC | CR6_TXSC; + update_cr6(db->cr6_data, db->ioaddr); + while (!(inl(db->ioaddr + DCR12) & 0x8)) + udelay(10); +} + +/* + * Hardware start transmission. + * Send a packet to media from the upper layer. + */ + +static int uli526x_start_xmit(struct eth_device *dev, void *packet, int length) +{ + struct uli526x_board_info *db = dev->priv; + struct tx_desc *txptr; + unsigned int len = length; + /* Too large packet check */ + if (len > MAX_PACKET_SIZE) { + printf(": big packet = %d\n", len); + return 0; + } + + /* No Tx resource check, it never happen nromally */ + if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) { + printf("No Tx resource %ld\n", db->tx_packet_cnt); + return 0; + } + + /* Disable NIC interrupt */ + outl(0, dev->iobase + DCR7); + + /* transmit this packet */ + txptr = db->tx_insert_ptr; + memcpy((char *)txptr->tx_buf_ptr, (char *)packet, (int)length); + txptr->tdes1 = cpu_to_le32(0xe1000000 | length); + + /* Point to next transmit free descriptor */ + db->tx_insert_ptr = txptr->next_tx_desc; + + /* Transmit Packet Process */ + if ((db->tx_packet_cnt < TX_DESC_CNT)) { + txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ + db->tx_packet_cnt++; /* Ready to send */ + outl(0x1, dev->iobase + DCR1); /* Issue Tx polling */ + } + + /* Got ULI526X status */ + db->cr5_data = inl(db->ioaddr + DCR5); + outl(db->cr5_data, db->ioaddr + DCR5); + +#ifdef TX_DEBUG + printf("%s(): length = 0x%x\n", __FUNCTION__, length); + printf("%s(): cr5_data=%x\n", __FUNCTION__, db->cr5_data); +#endif + + outl(db->cr7_data, dev->iobase + DCR7); + uli526x_free_tx_pkt(dev, db); + + return length; +} + +/* + * Free TX resource after TX complete + */ + +static void uli526x_free_tx_pkt(struct eth_device *dev, + struct uli526x_board_info *db) +{ + struct tx_desc *txptr; + u32 tdes0; + + txptr = db->tx_remove_ptr; + while (db->tx_packet_cnt) { + tdes0 = le32_to_cpu(txptr->tdes0); + /* printf(DRV_NAME ": tdes0=%x\n", tdes0); */ + if (tdes0 & 0x80000000) + break; + + /* A packet sent completed */ + db->tx_packet_cnt--; + + if (tdes0 != 0x7fffffff) { +#ifdef TX_DEBUG + printf("%s()tdes0=%x\n", __FUNCTION__, tdes0); +#endif + if (tdes0 & TDES0_ERR_MASK) { + if (tdes0 & 0x0002) { /* UnderRun */ + if (!(db->cr6_data & CR6_SFT)) { + db->cr6_data = db->cr6_data | + CR6_SFT; + update_cr6(db->cr6_data, + db->ioaddr); + } + } + } + } + + txptr = txptr->next_tx_desc; + }/* End of while */ + + /* Update TX remove pointer to next */ + db->tx_remove_ptr = txptr; +} + + +/* + * Receive the come packet and pass to upper layer + */ + +static int uli526x_rx_packet(struct eth_device *dev) +{ + struct uli526x_board_info *db = dev->priv; + struct rx_desc *rxptr; + int rxlen = 0; + u32 rdes0; + + rxptr = db->rx_ready_ptr; + + rdes0 = le32_to_cpu(rxptr->rdes0); +#ifdef RX_DEBUG + printf("%s(): rxptr->rdes0=%x:%x\n", __FUNCTION__, rxptr->rdes0); +#endif + if (!(rdes0 & 0x80000000)) { /* packet owner check */ + if ((rdes0 & 0x300) != 0x300) { + /* A packet without First/Last flag */ + /* reuse this buf */ + printf("A packet without First/Last flag"); + uli526x_reuse_buf(rxptr); + } else { + /* A packet with First/Last flag */ + rxlen = ((rdes0 >> 16) & 0x3fff) - 4; +#ifdef RX_DEBUG + printf("%s(): rxlen =%x\n", __FUNCTION__, rxlen); +#endif + /* error summary bit check */ + if (rdes0 & 0x8000) { + /* This is a error packet */ + printf("Error: rdes0: %x\n", rdes0); + } + + if (!(rdes0 & 0x8000) || + ((db->cr6_data & CR6_PM) && (rxlen > 6))) { + +#ifdef RX_DEBUG + printf("%s(): rx_skb_ptr =%x\n", + __FUNCTION__, rxptr->rx_buf_ptr); + printf("%s(): rxlen =%x\n", + __FUNCTION__, rxlen); + + printf("%s(): buf addr =%x\n", + __FUNCTION__, rxptr->rx_buf_ptr); + printf("%s(): rxlen =%x\n", + __FUNCTION__, rxlen); + int i; + for (i = 0; i < 0x20; i++) + printf("%s(): data[%x] =%x\n", + __FUNCTION__, i, rxptr->rx_buf_ptr[i]); +#endif + + NetReceive((uchar *)rxptr->rx_buf_ptr, rxlen); + uli526x_reuse_buf(rxptr); + + } else { + /* Reuse SKB buffer when the packet is error */ + printf("Reuse buffer, rdes0"); + uli526x_reuse_buf(rxptr); + } + } + + rxptr = rxptr->next_rx_desc; + } + + db->rx_ready_ptr = rxptr; + return rxlen; +} + +/* + * Reuse the RX buffer + */ + +static void uli526x_reuse_buf(struct rx_desc *rxptr) +{ + + if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) + rxptr->rdes0 = cpu_to_le32(0x80000000); + else + printf("Buffer reuse method error"); +} +/* + * Initialize transmit/Receive descriptor + * Using Chain structure, and allocate Tx/Rx buffer + */ + +static void uli526x_descriptor_init(struct uli526x_board_info *db, + unsigned long ioaddr) +{ + struct tx_desc *tmp_tx; + struct rx_desc *tmp_rx; + unsigned char *tmp_buf; + dma_addr_t tmp_tx_dma, tmp_rx_dma; + dma_addr_t tmp_buf_dma; + int i; + /* tx descriptor start pointer */ + db->tx_insert_ptr = db->first_tx_desc; + db->tx_remove_ptr = db->first_tx_desc; + + outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */ + + /* rx descriptor start pointer */ + db->first_rx_desc = (void *)db->first_tx_desc + + sizeof(struct tx_desc) * TX_DESC_CNT; + db->first_rx_desc_dma = db->first_tx_desc_dma + + sizeof(struct tx_desc) * TX_DESC_CNT; + db->rx_ready_ptr = db->first_rx_desc; + outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */ +#ifdef DEBUG + printf("%s(): db->first_tx_desc= 0x%x\n", + __FUNCTION__, db->first_tx_desc); + printf("%s(): db->first_rx_desc_dma= 0x%x\n", + __FUNCTION__, db->first_rx_desc_dma); +#endif + /* Init Transmit chain */ + tmp_buf = db->buf_pool_start; + tmp_buf_dma = db->buf_pool_dma_start; + tmp_tx_dma = db->first_tx_desc_dma; + for (tmp_tx = db->first_tx_desc, i = 0; + i < TX_DESC_CNT; i++, tmp_tx++) { + tmp_tx->tx_buf_ptr = (char *)tmp_buf; + tmp_tx->tdes0 = cpu_to_le32(0); + tmp_tx->tdes1 = cpu_to_le32(0x81000000); /* IC, chain */ + tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma); + tmp_tx_dma += sizeof(struct tx_desc); + tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma); + tmp_tx->next_tx_desc = tmp_tx + 1; + tmp_buf = tmp_buf + TX_BUF_ALLOC; + tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC; + } + (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma); + tmp_tx->next_tx_desc = db->first_tx_desc; + + /* Init Receive descriptor chain */ + tmp_rx_dma = db->first_rx_desc_dma; + for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; + i++, tmp_rx++) { + tmp_rx->rdes0 = cpu_to_le32(0); + tmp_rx->rdes1 = cpu_to_le32(0x01000600); + tmp_rx_dma += sizeof(struct rx_desc); + tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma); + tmp_rx->next_rx_desc = tmp_rx + 1; + } + (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma); + tmp_rx->next_rx_desc = db->first_rx_desc; + + /* pre-allocate Rx buffer */ + allocate_rx_buffer(db); +} + +/* + * Update CR6 value + * Firstly stop ULI526X, then written value and start + */ + +static void update_cr6(u32 cr6_data, unsigned long ioaddr) +{ + + outl(cr6_data, ioaddr + DCR6); + udelay(5); +} + +/* + * Allocate rx buffer, + */ + +static void allocate_rx_buffer(struct uli526x_board_info *db) +{ + int index; + struct rx_desc *rxptr; + rxptr = db->first_rx_desc; + u32 addr; + + for (index = 0; index < RX_DESC_CNT; index++) { + addr = (u32)NetRxPackets[index]; + addr += (16 - (addr & 15)); + rxptr->rx_buf_ptr = (char *) addr; + rxptr->rdes2 = cpu_to_le32(addr); + rxptr->rdes0 = cpu_to_le32(0x80000000); +#ifdef DEBUG + printf("%s(): Number 0x%x:\n", __FUNCTION__, index); + printf("%s(): addr 0x%x:\n", __FUNCTION__, addr); + printf("%s(): rxptr address = 0x%x\n", __FUNCTION__, rxptr); + printf("%s(): rxptr buf address = 0x%x\n", \ + __FUNCTION__, rxptr->rx_buf_ptr); + printf("%s(): rdes2 = 0x%x\n", __FUNCTION__, rxptr->rdes2); +#endif + rxptr = rxptr->next_rx_desc; + } +} + +/* + * Read one word data from the serial ROM + */ + +static u16 read_srom_word(long ioaddr, int offset) +{ + int i; + u16 srom_data = 0; + long cr9_ioaddr = ioaddr + DCR9; + + outl(CR9_SROM_READ, cr9_ioaddr); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + /* Send the Read Command 110b */ + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); + + /* Send the offset */ + for (i = 5; i >= 0; i--) { + srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; + SROM_CLK_WRITE(srom_data, cr9_ioaddr); + } + + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + for (i = 16; i > 0; i--) { + outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); + udelay(5); + srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) + ? 1 : 0); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + udelay(5); + } + + outl(CR9_SROM_READ, cr9_ioaddr); + return srom_data; +} + +/* + * Set 10/100 phyxcer capability + * AUTO mode : phyxcer register4 is NIC capability + * Force mode: phyxcer register4 is the force media + */ + +static void uli526x_set_phyxcer(struct uli526x_board_info *db) +{ + u16 phy_reg; + + /* Phyxcer capability setting */ + phy_reg = uli_phy_read(db->ioaddr, + db->phy_addr, 4, db->chip_id) & ~0x01e0; + + if (db->media_mode & ULI526X_AUTO) { + /* AUTO Mode */ + phy_reg |= db->PHY_reg4; + } else { + /* Force Mode */ + switch (db->media_mode) { + case ULI526X_10MHF: phy_reg |= 0x20; break; + case ULI526X_10MFD: phy_reg |= 0x40; break; + case ULI526X_100MHF: phy_reg |= 0x80; break; + case ULI526X_100MFD: phy_reg |= 0x100; break; + } + + } + + /* Write new capability to Phyxcer Reg4 */ + if (!(phy_reg & 0x01e0)) { + phy_reg |= db->PHY_reg4; + db->media_mode |= ULI526X_AUTO; + } + uli_phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); + + /* Restart Auto-Negotiation */ + uli_phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id); + udelay(50); +} + +/* + * Write a word to Phy register + */ + +static void uli_phy_write(unsigned long iobase, u8 phy_addr, u8 offset, + u16 phy_data, u32 chip_id) +{ + u16 i; + unsigned long ioaddr; + + if (chip_id == PCI_ULI5263_ID) { + phy_writeby_cr10(iobase, phy_addr, offset, phy_data); + return; + } + /* M5261/M5263 Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send write command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send Phy address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* Send register address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* written trasnition */ + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + + /* Write a word data to PHY controller */ + for (i = 0x8000; i > 0; i >>= 1) + phy_write_1bit(ioaddr, phy_data & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); +} + +/* + * Read a word data from phy register + */ + +static u16 uli_phy_read(unsigned long iobase, u8 phy_addr, u8 offset, + u32 chip_id) +{ + int i; + u16 phy_data; + unsigned long ioaddr; + + if (chip_id == PCI_ULI5263_ID) + return phy_readby_cr10(iobase, phy_addr, offset); + /* M5261/M5263 Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send read command(10) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + + /* Send Phy address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* Send register address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* Skip transition state */ + phy_read_1bit(ioaddr, chip_id); + + /* read 16bit data */ + for (phy_data = 0, i = 0; i < 16; i++) { + phy_data <<= 1; + phy_data |= phy_read_1bit(ioaddr, chip_id); + } + + return phy_data; +} + +static u16 phy_readby_cr10(unsigned long iobase, u8 phy_addr, u8 offset) +{ + unsigned long ioaddr, cr10_value; + + ioaddr = iobase + DCR10; + cr10_value = phy_addr; + cr10_value = (cr10_value<<5) + offset; + cr10_value = (cr10_value<<16) + 0x08000000; + outl(cr10_value, ioaddr); + udelay(1); + while (1) { + cr10_value = inl(ioaddr); + if (cr10_value & 0x10000000) + break; + } + return (cr10_value&0x0ffff); +} + +static void phy_writeby_cr10(unsigned long iobase, u8 phy_addr, + u8 offset, u16 phy_data) +{ + unsigned long ioaddr, cr10_value; + + ioaddr = iobase + DCR10; + cr10_value = phy_addr; + cr10_value = (cr10_value<<5) + offset; + cr10_value = (cr10_value<<16) + 0x04000000 + phy_data; + outl(cr10_value, ioaddr); + udelay(1); +} +/* + * Write one bit data to Phy Controller + */ + +static void phy_write_1bit(unsigned long ioaddr, u32 phy_data, u32 chip_id) +{ + outl(phy_data , ioaddr); /* MII Clock Low */ + udelay(1); + outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ + udelay(1); + outl(phy_data , ioaddr); /* MII Clock Low */ + udelay(1); +} + +/* + * Read one bit phy data from PHY controller + */ + +static u16 phy_read_1bit(unsigned long ioaddr, u32 chip_id) +{ + u16 phy_data; + + outl(0x50000 , ioaddr); + udelay(1); + phy_data = (inl(ioaddr) >> 19) & 0x1; + outl(0x40000 , ioaddr); + udelay(1); + + return phy_data; +} + +/* + * Set MAC address to ID Table + */ + +static void set_mac_addr(struct eth_device *dev) +{ + int i; + u16 addr; + struct uli526x_board_info *db = dev->priv; + outl(0x10000, db->ioaddr + DCR0); /* Diagnosis mode */ + /* Reset dianostic pointer port */ + outl(0x1c0, db->ioaddr + DCR13); + outl(0, db->ioaddr + DCR14); /* Clear reset port */ + outl(0x10, db->ioaddr + DCR14); /* Reset ID Table pointer */ + outl(0, db->ioaddr + DCR14); /* Clear reset port */ + outl(0, db->ioaddr + DCR13); /* Clear CR13 */ + /* Select ID Table access port */ + outl(0x1b0, db->ioaddr + DCR13); + /* Read MAC address from CR14 */ + for (i = 0; i < 3; i++) { + addr = dev->enetaddr[2 * i] | (dev->enetaddr[2 * i + 1] << 8); + outl(addr, db->ioaddr + DCR14); + } + /* write end */ + outl(0, db->ioaddr + DCR13); /* Clear CR13 */ + outl(0, db->ioaddr + DCR0); /* Clear CR0 */ + udelay(10); + return; +} diff --git a/qemu/roms/u-boot/drivers/net/vsc7385.c b/qemu/roms/u-boot/drivers/net/vsc7385.c new file mode 100644 index 000000000..a5110e516 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/vsc7385.c @@ -0,0 +1,97 @@ +/* + * Vitesse 7385 Switch Firmware Upload + * + * Author: Timur Tabi + * + * Copyright 2008 Freescale Semiconductor, Inc. This file is licensed + * under the terms of the GNU General Public License version 2. This + * program is licensed "as is" without any warranty of any kind, whether + * express or implied. + * + * This module uploads proprietary firmware for the Vitesse VSC7385 5-port + * switch. + */ + +#include +#include +#include +#include +#include "vsc7385.h" + +/* + * Upload a Vitesse VSC7385 firmware image to the hardware + * + * This function takes a pointer to a VSC7385 firmware image and a size, and + * uploads that firmware to the VSC7385. + * + * This firmware is typically located at a board-specific flash address, + * and the size is typically 8KB. + * + * The firmware is Vitesse proprietary. + * + * Further details on the register information can be obtained from Vitesse. + */ +int vsc7385_upload_firmware(void *firmware, unsigned int size) +{ + u8 *fw = firmware; + unsigned int i; + + u32 *gloreset = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c050); + u32 *icpu_ctrl = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c040); + u32 *icpu_addr = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c044); + u32 *icpu_data = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c048); + u32 *icpu_rom_map = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c070); +#ifdef DEBUG + u32 *chipid = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c060); +#endif + + out_be32(gloreset, 3); + udelay(200); + + out_be32(icpu_ctrl, 0x8E); + udelay(20); + + out_be32(icpu_rom_map, 1); + udelay(20); + + /* Write the firmware to I-RAM */ + out_be32(icpu_addr, 0); + udelay(20); + + for (i = 0; i < size; i++) { + out_be32(icpu_data, fw[i]); + udelay(20); + if (ctrlc()) + return -EINTR; + } + + /* Read back and compare */ + out_be32(icpu_addr, 0); + udelay(20); + + for (i = 0; i < size; i++) { + u8 value; + + value = (u8) in_be32(icpu_data); + udelay(20); + if (value != fw[i]) { + debug("VSC7385: Upload mismatch: address 0x%x, " + "read value 0x%x, image value 0x%x\n", + i, value, fw[i]); + + return -EIO; + } + if (ctrlc()) + break; + } + + out_be32(icpu_ctrl, 0x0B); + udelay(20); + +#ifdef DEBUG + printf("VSC7385: Chip ID is %08x\n", in_be32(chipid)); + udelay(20); +#endif + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/xilinx_axi_emac.c b/qemu/roms/u-boot/drivers/net/xilinx_axi_emac.c new file mode 100644 index 000000000..262b67b6c --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_axi_emac.c @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2011 Michal Simek + * Copyright (C) 2011 PetaLogix + * Copyright (C) 2010 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(CONFIG_PHYLIB) +# error AXI_ETHERNET requires PHYLIB +#endif + +/* Link setup */ +#define XAE_EMMC_LINKSPEED_MASK 0xC0000000 /* Link speed */ +#define XAE_EMMC_LINKSPD_10 0x00000000 /* Link Speed mask for 10 Mbit */ +#define XAE_EMMC_LINKSPD_100 0x40000000 /* Link Speed mask for 100 Mbit */ +#define XAE_EMMC_LINKSPD_1000 0x80000000 /* Link Speed mask for 1000 Mbit */ + +/* Interrupt Status/Enable/Mask Registers bit definitions */ +#define XAE_INT_RXRJECT_MASK 0x00000008 /* Rx frame rejected */ +#define XAE_INT_MGTRDY_MASK 0x00000080 /* MGT clock Lock */ + +/* Receive Configuration Word 1 (RCW1) Register bit definitions */ +#define XAE_RCW1_RX_MASK 0x10000000 /* Receiver enable */ + +/* Transmitter Configuration (TC) Register bit definitions */ +#define XAE_TC_TX_MASK 0x10000000 /* Transmitter enable */ + +#define XAE_UAW1_UNICASTADDR_MASK 0x0000FFFF + +/* MDIO Management Configuration (MC) Register bit definitions */ +#define XAE_MDIO_MC_MDIOEN_MASK 0x00000040 /* MII management enable*/ + +/* MDIO Management Control Register (MCR) Register bit definitions */ +#define XAE_MDIO_MCR_PHYAD_MASK 0x1F000000 /* Phy Address Mask */ +#define XAE_MDIO_MCR_PHYAD_SHIFT 24 /* Phy Address Shift */ +#define XAE_MDIO_MCR_REGAD_MASK 0x001F0000 /* Reg Address Mask */ +#define XAE_MDIO_MCR_REGAD_SHIFT 16 /* Reg Address Shift */ +#define XAE_MDIO_MCR_OP_READ_MASK 0x00008000 /* Op Code Read Mask */ +#define XAE_MDIO_MCR_OP_WRITE_MASK 0x00004000 /* Op Code Write Mask */ +#define XAE_MDIO_MCR_INITIATE_MASK 0x00000800 /* Ready Mask */ +#define XAE_MDIO_MCR_READY_MASK 0x00000080 /* Ready Mask */ + +#define XAE_MDIO_DIV_DFT 29 /* Default MDIO clock divisor */ + +/* DMA macros */ +/* Bitmasks of XAXIDMA_CR_OFFSET register */ +#define XAXIDMA_CR_RUNSTOP_MASK 0x00000001 /* Start/stop DMA channel */ +#define XAXIDMA_CR_RESET_MASK 0x00000004 /* Reset DMA engine */ + +/* Bitmasks of XAXIDMA_SR_OFFSET register */ +#define XAXIDMA_HALTED_MASK 0x00000001 /* DMA channel halted */ + +/* Bitmask for interrupts */ +#define XAXIDMA_IRQ_IOC_MASK 0x00001000 /* Completion intr */ +#define XAXIDMA_IRQ_DELAY_MASK 0x00002000 /* Delay interrupt */ +#define XAXIDMA_IRQ_ALL_MASK 0x00007000 /* All interrupts */ + +/* Bitmasks of XAXIDMA_BD_CTRL_OFFSET register */ +#define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */ +#define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */ + +#define DMAALIGN 128 + +static u8 rxframe[PKTSIZE_ALIGN] __attribute((aligned(DMAALIGN))); + +/* Reflect dma offsets */ +struct axidma_reg { + u32 control; /* DMACR */ + u32 status; /* DMASR */ + u32 current; /* CURDESC */ + u32 reserved; + u32 tail; /* TAILDESC */ +}; + +/* Private driver structures */ +struct axidma_priv { + struct axidma_reg *dmatx; + struct axidma_reg *dmarx; + int phyaddr; + + struct phy_device *phydev; + struct mii_dev *bus; +}; + +/* BD descriptors */ +struct axidma_bd { + u32 next; /* Next descriptor pointer */ + u32 reserved1; + u32 phys; /* Buffer address */ + u32 reserved2; + u32 reserved3; + u32 reserved4; + u32 cntrl; /* Control */ + u32 status; /* Status */ + u32 app0; + u32 app1; /* TX start << 16 | insert */ + u32 app2; /* TX csum seed */ + u32 app3; + u32 app4; + u32 sw_id_offset; + u32 reserved5; + u32 reserved6; +}; + +/* Static BDs - driver uses only one BD */ +static struct axidma_bd tx_bd __attribute((aligned(DMAALIGN))); +static struct axidma_bd rx_bd __attribute((aligned(DMAALIGN))); + +struct axi_regs { + u32 reserved[3]; + u32 is; /* 0xC: Interrupt status */ + u32 reserved2; + u32 ie; /* 0x14: Interrupt enable */ + u32 reserved3[251]; + u32 rcw1; /* 0x404: Rx Configuration Word 1 */ + u32 tc; /* 0x408: Tx Configuration */ + u32 reserved4; + u32 emmc; /* 0x410: EMAC mode configuration */ + u32 reserved5[59]; + u32 mdio_mc; /* 0x500: MII Management Config */ + u32 mdio_mcr; /* 0x504: MII Management Control */ + u32 mdio_mwd; /* 0x508: MII Management Write Data */ + u32 mdio_mrd; /* 0x50C: MII Management Read Data */ + u32 reserved6[124]; + u32 uaw0; /* 0x700: Unicast address word 0 */ + u32 uaw1; /* 0x704: Unicast address word 1 */ +}; + +/* Use MII register 1 (MII status register) to detect PHY */ +#define PHY_DETECT_REG 1 + +/* + * Mask used to verify certain PHY features (or register contents) + * in the register above: + * 0x1000: 10Mbps full duplex support + * 0x0800: 10Mbps half duplex support + * 0x0008: Auto-negotiation support + */ +#define PHY_DETECT_MASK 0x1808 + +static inline int mdio_wait(struct eth_device *dev) +{ + struct axi_regs *regs = (struct axi_regs *)dev->iobase; + u32 timeout = 200; + + /* Wait till MDIO interface is ready to accept a new transaction. */ + while (timeout && (!(in_be32(®s->mdio_mcr) + & XAE_MDIO_MCR_READY_MASK))) { + timeout--; + udelay(1); + } + if (!timeout) { + printf("%s: Timeout\n", __func__); + return 1; + } + return 0; +} + +static u32 phyread(struct eth_device *dev, u32 phyaddress, u32 registernum, + u16 *val) +{ + struct axi_regs *regs = (struct axi_regs *)dev->iobase; + u32 mdioctrlreg = 0; + + if (mdio_wait(dev)) + return 1; + + mdioctrlreg = ((phyaddress << XAE_MDIO_MCR_PHYAD_SHIFT) & + XAE_MDIO_MCR_PHYAD_MASK) | + ((registernum << XAE_MDIO_MCR_REGAD_SHIFT) + & XAE_MDIO_MCR_REGAD_MASK) | + XAE_MDIO_MCR_INITIATE_MASK | + XAE_MDIO_MCR_OP_READ_MASK; + + out_be32(®s->mdio_mcr, mdioctrlreg); + + if (mdio_wait(dev)) + return 1; + + /* Read data */ + *val = in_be32(®s->mdio_mrd); + return 0; +} + +static u32 phywrite(struct eth_device *dev, u32 phyaddress, u32 registernum, + u32 data) +{ + struct axi_regs *regs = (struct axi_regs *)dev->iobase; + u32 mdioctrlreg = 0; + + if (mdio_wait(dev)) + return 1; + + mdioctrlreg = ((phyaddress << XAE_MDIO_MCR_PHYAD_SHIFT) & + XAE_MDIO_MCR_PHYAD_MASK) | + ((registernum << XAE_MDIO_MCR_REGAD_SHIFT) + & XAE_MDIO_MCR_REGAD_MASK) | + XAE_MDIO_MCR_INITIATE_MASK | + XAE_MDIO_MCR_OP_WRITE_MASK; + + /* Write data */ + out_be32(®s->mdio_mwd, data); + + out_be32(®s->mdio_mcr, mdioctrlreg); + + if (mdio_wait(dev)) + return 1; + + return 0; +} + +/* Setting axi emac and phy to proper setting */ +static int setup_phy(struct eth_device *dev) +{ + u16 phyreg; + u32 i, speed, emmc_reg, ret; + struct axidma_priv *priv = dev->priv; + struct axi_regs *regs = (struct axi_regs *)dev->iobase; + struct phy_device *phydev; + + u32 supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full; + + if (priv->phyaddr == -1) { + /* Detect the PHY address */ + for (i = 31; i >= 0; i--) { + ret = phyread(dev, i, PHY_DETECT_REG, &phyreg); + if (!ret && (phyreg != 0xFFFF) && + ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { + /* Found a valid PHY address */ + priv->phyaddr = i; + debug("axiemac: Found valid phy address, %x\n", + phyreg); + break; + } + } + } + + /* Interface - look at tsec */ + phydev = phy_connect(priv->bus, priv->phyaddr, dev, 0); + + phydev->supported &= supported; + phydev->advertising = phydev->supported; + priv->phydev = phydev; + phy_config(phydev); + if (phy_startup(phydev)) { + printf("axiemac: could not initialize PHY %s\n", + phydev->dev->name); + return 0; + } + if (!phydev->link) { + printf("%s: No link.\n", phydev->dev->name); + return 0; + } + + switch (phydev->speed) { + case 1000: + speed = XAE_EMMC_LINKSPD_1000; + break; + case 100: + speed = XAE_EMMC_LINKSPD_100; + break; + case 10: + speed = XAE_EMMC_LINKSPD_10; + break; + default: + return 0; + } + + /* Setup the emac for the phy speed */ + emmc_reg = in_be32(®s->emmc); + emmc_reg &= ~XAE_EMMC_LINKSPEED_MASK; + emmc_reg |= speed; + + /* Write new speed setting out to Axi Ethernet */ + out_be32(®s->emmc, emmc_reg); + + /* + * Setting the operating speed of the MAC needs a delay. There + * doesn't seem to be register to poll, so please consider this + * during your application design. + */ + udelay(1); + + return 1; +} + +/* STOP DMA transfers */ +static void axiemac_halt(struct eth_device *dev) +{ + struct axidma_priv *priv = dev->priv; + u32 temp; + + /* Stop the hardware */ + temp = in_be32(&priv->dmatx->control); + temp &= ~XAXIDMA_CR_RUNSTOP_MASK; + out_be32(&priv->dmatx->control, temp); + + temp = in_be32(&priv->dmarx->control); + temp &= ~XAXIDMA_CR_RUNSTOP_MASK; + out_be32(&priv->dmarx->control, temp); + + debug("axiemac: Halted\n"); +} + +static int axi_ethernet_init(struct eth_device *dev) +{ + struct axi_regs *regs = (struct axi_regs *)dev->iobase; + u32 timeout = 200; + + /* + * Check the status of the MgtRdy bit in the interrupt status + * registers. This must be done to allow the MGT clock to become stable + * for the Sgmii and 1000BaseX PHY interfaces. No other register reads + * will be valid until this bit is valid. + * The bit is always a 1 for all other PHY interfaces. + */ + while (timeout && (!(in_be32(®s->is) & XAE_INT_MGTRDY_MASK))) { + timeout--; + udelay(1); + } + if (!timeout) { + printf("%s: Timeout\n", __func__); + return 1; + } + + /* Stop the device and reset HW */ + /* Disable interrupts */ + out_be32(®s->ie, 0); + + /* Disable the receiver */ + out_be32(®s->rcw1, in_be32(®s->rcw1) & ~XAE_RCW1_RX_MASK); + + /* + * Stopping the receiver in mid-packet causes a dropped packet + * indication from HW. Clear it. + */ + /* Set the interrupt status register to clear the interrupt */ + out_be32(®s->is, XAE_INT_RXRJECT_MASK); + + /* Setup HW */ + /* Set default MDIO divisor */ + out_be32(®s->mdio_mc, XAE_MDIO_DIV_DFT | XAE_MDIO_MC_MDIOEN_MASK); + + debug("axiemac: InitHw done\n"); + return 0; +} + +static int axiemac_setup_mac(struct eth_device *dev) +{ + struct axi_regs *regs = (struct axi_regs *)dev->iobase; + + /* Set the MAC address */ + int val = ((dev->enetaddr[3] << 24) | (dev->enetaddr[2] << 16) | + (dev->enetaddr[1] << 8) | (dev->enetaddr[0])); + out_be32(®s->uaw0, val); + + val = (dev->enetaddr[5] << 8) | dev->enetaddr[4] ; + val |= in_be32(®s->uaw1) & ~XAE_UAW1_UNICASTADDR_MASK; + out_be32(®s->uaw1, val); + return 0; +} + +/* Reset DMA engine */ +static void axi_dma_init(struct eth_device *dev) +{ + struct axidma_priv *priv = dev->priv; + u32 timeout = 500; + + /* Reset the engine so the hardware starts from a known state */ + out_be32(&priv->dmatx->control, XAXIDMA_CR_RESET_MASK); + out_be32(&priv->dmarx->control, XAXIDMA_CR_RESET_MASK); + + /* At the initialization time, hardware should finish reset quickly */ + while (timeout--) { + /* Check transmit/receive channel */ + /* Reset is done when the reset bit is low */ + if (!(in_be32(&priv->dmatx->control) | + in_be32(&priv->dmarx->control)) + & XAXIDMA_CR_RESET_MASK) { + break; + } + } + if (!timeout) + printf("%s: Timeout\n", __func__); +} + +static int axiemac_init(struct eth_device *dev, bd_t * bis) +{ + struct axidma_priv *priv = dev->priv; + struct axi_regs *regs = (struct axi_regs *)dev->iobase; + u32 temp; + + debug("axiemac: Init started\n"); + /* + * Initialize AXIDMA engine. AXIDMA engine must be initialized before + * AxiEthernet. During AXIDMA engine initialization, AXIDMA hardware is + * reset, and since AXIDMA reset line is connected to AxiEthernet, this + * would ensure a reset of AxiEthernet. + */ + axi_dma_init(dev); + + /* Initialize AxiEthernet hardware. */ + if (axi_ethernet_init(dev)) + return -1; + + /* Disable all RX interrupts before RxBD space setup */ + temp = in_be32(&priv->dmarx->control); + temp &= ~XAXIDMA_IRQ_ALL_MASK; + out_be32(&priv->dmarx->control, temp); + + /* Start DMA RX channel. Now it's ready to receive data.*/ + out_be32(&priv->dmarx->current, (u32)&rx_bd); + + /* Setup the BD. */ + memset(&rx_bd, 0, sizeof(rx_bd)); + rx_bd.next = (u32)&rx_bd; + rx_bd.phys = (u32)&rxframe; + rx_bd.cntrl = sizeof(rxframe); + /* Flush the last BD so DMA core could see the updates */ + flush_cache((u32)&rx_bd, sizeof(rx_bd)); + + /* It is necessary to flush rxframe because if you don't do it + * then cache can contain uninitialized data */ + flush_cache((u32)&rxframe, sizeof(rxframe)); + + /* Start the hardware */ + temp = in_be32(&priv->dmarx->control); + temp |= XAXIDMA_CR_RUNSTOP_MASK; + out_be32(&priv->dmarx->control, temp); + + /* Rx BD is ready - start */ + out_be32(&priv->dmarx->tail, (u32)&rx_bd); + + /* Enable TX */ + out_be32(®s->tc, XAE_TC_TX_MASK); + /* Enable RX */ + out_be32(®s->rcw1, XAE_RCW1_RX_MASK); + + /* PHY setup */ + if (!setup_phy(dev)) { + axiemac_halt(dev); + return -1; + } + + debug("axiemac: Init complete\n"); + return 0; +} + +static int axiemac_send(struct eth_device *dev, void *ptr, int len) +{ + struct axidma_priv *priv = dev->priv; + u32 timeout; + + if (len > PKTSIZE_ALIGN) + len = PKTSIZE_ALIGN; + + /* Flush packet to main memory to be trasfered by DMA */ + flush_cache((u32)ptr, len); + + /* Setup Tx BD */ + memset(&tx_bd, 0, sizeof(tx_bd)); + /* At the end of the ring, link the last BD back to the top */ + tx_bd.next = (u32)&tx_bd; + tx_bd.phys = (u32)ptr; + /* Save len */ + tx_bd.cntrl = len | XAXIDMA_BD_CTRL_TXSOF_MASK | + XAXIDMA_BD_CTRL_TXEOF_MASK; + + /* Flush the last BD so DMA core could see the updates */ + flush_cache((u32)&tx_bd, sizeof(tx_bd)); + + if (in_be32(&priv->dmatx->status) & XAXIDMA_HALTED_MASK) { + u32 temp; + out_be32(&priv->dmatx->current, (u32)&tx_bd); + /* Start the hardware */ + temp = in_be32(&priv->dmatx->control); + temp |= XAXIDMA_CR_RUNSTOP_MASK; + out_be32(&priv->dmatx->control, temp); + } + + /* Start transfer */ + out_be32(&priv->dmatx->tail, (u32)&tx_bd); + + /* Wait for transmission to complete */ + debug("axiemac: Waiting for tx to be done\n"); + timeout = 200; + while (timeout && (!in_be32(&priv->dmatx->status) & + (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) { + timeout--; + udelay(1); + } + if (!timeout) { + printf("%s: Timeout\n", __func__); + return 1; + } + + debug("axiemac: Sending complete\n"); + return 0; +} + +static int isrxready(struct eth_device *dev) +{ + u32 status; + struct axidma_priv *priv = dev->priv; + + /* Read pending interrupts */ + status = in_be32(&priv->dmarx->status); + + /* Acknowledge pending interrupts */ + out_be32(&priv->dmarx->status, status & XAXIDMA_IRQ_ALL_MASK); + + /* + * If Reception done interrupt is asserted, call RX call back function + * to handle the processed BDs and then raise the according flag. + */ + if ((status & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) + return 1; + + return 0; +} + +static int axiemac_recv(struct eth_device *dev) +{ + u32 length; + struct axidma_priv *priv = dev->priv; + u32 temp; + + /* Wait for an incoming packet */ + if (!isrxready(dev)) + return 0; + + debug("axiemac: RX data ready\n"); + + /* Disable IRQ for a moment till packet is handled */ + temp = in_be32(&priv->dmarx->control); + temp &= ~XAXIDMA_IRQ_ALL_MASK; + out_be32(&priv->dmarx->control, temp); + + length = rx_bd.app4 & 0xFFFF; /* max length mask */ +#ifdef DEBUG + print_buffer(&rxframe, &rxframe[0], 1, length, 16); +#endif + /* Pass the received frame up for processing */ + if (length) + NetReceive(rxframe, length); + +#ifdef DEBUG + /* It is useful to clear buffer to be sure that it is consistent */ + memset(rxframe, 0, sizeof(rxframe)); +#endif + /* Setup RxBD */ + /* Clear the whole buffer and setup it again - all flags are cleared */ + memset(&rx_bd, 0, sizeof(rx_bd)); + rx_bd.next = (u32)&rx_bd; + rx_bd.phys = (u32)&rxframe; + rx_bd.cntrl = sizeof(rxframe); + + /* Write bd to HW */ + flush_cache((u32)&rx_bd, sizeof(rx_bd)); + + /* It is necessary to flush rxframe because if you don't do it + * then cache will contain previous packet */ + flush_cache((u32)&rxframe, sizeof(rxframe)); + + /* Rx BD is ready - start again */ + out_be32(&priv->dmarx->tail, (u32)&rx_bd); + + debug("axiemac: RX completed, framelength = %d\n", length); + + return length; +} + +static int axiemac_miiphy_read(const char *devname, uchar addr, + uchar reg, ushort *val) +{ + struct eth_device *dev = eth_get_dev(); + u32 ret; + + ret = phyread(dev, addr, reg, val); + debug("axiemac: Read MII 0x%x, 0x%x, 0x%x\n", addr, reg, *val); + return ret; +} + +static int axiemac_miiphy_write(const char *devname, uchar addr, + uchar reg, ushort val) +{ + struct eth_device *dev = eth_get_dev(); + + debug("axiemac: Write MII 0x%x, 0x%x, 0x%x\n", addr, reg, val); + return phywrite(dev, addr, reg, val); +} + +static int axiemac_bus_reset(struct mii_dev *bus) +{ + debug("axiemac: Bus reset\n"); + return 0; +} + +int xilinx_axiemac_initialize(bd_t *bis, unsigned long base_addr, + unsigned long dma_addr) +{ + struct eth_device *dev; + struct axidma_priv *priv; + + dev = calloc(1, sizeof(struct eth_device)); + if (dev == NULL) + return -1; + + dev->priv = calloc(1, sizeof(struct axidma_priv)); + if (dev->priv == NULL) { + free(dev); + return -1; + } + priv = dev->priv; + + sprintf(dev->name, "aximac.%lx", base_addr); + + dev->iobase = base_addr; + priv->dmatx = (struct axidma_reg *)dma_addr; + /* RX channel offset is 0x30 */ + priv->dmarx = (struct axidma_reg *)(dma_addr + 0x30); + dev->init = axiemac_init; + dev->halt = axiemac_halt; + dev->send = axiemac_send; + dev->recv = axiemac_recv; + dev->write_hwaddr = axiemac_setup_mac; + +#ifdef CONFIG_PHY_ADDR + priv->phyaddr = CONFIG_PHY_ADDR; +#else + priv->phyaddr = -1; +#endif + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) + miiphy_register(dev->name, axiemac_miiphy_read, axiemac_miiphy_write); + priv->bus = miiphy_get_dev_by_name(dev->name); + priv->bus->reset = axiemac_bus_reset; +#endif + return 1; +} diff --git a/qemu/roms/u-boot/drivers/net/xilinx_emaclite.c b/qemu/roms/u-boot/drivers/net/xilinx_emaclite.c new file mode 100644 index 000000000..2a5cc4455 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_emaclite.c @@ -0,0 +1,392 @@ +/* + * (C) Copyright 2007-2009 Michal Simek + * (C) Copyright 2003 Xilinx Inc. + * + * Michal SIMEK + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#define ENET_ADDR_LENGTH 6 + +/* EmacLite constants */ +#define XEL_BUFFER_OFFSET 0x0800 /* Next buffer's offset */ +#define XEL_TPLR_OFFSET 0x07F4 /* Tx packet length */ +#define XEL_TSR_OFFSET 0x07FC /* Tx status */ +#define XEL_RSR_OFFSET 0x17FC /* Rx status */ +#define XEL_RXBUFF_OFFSET 0x1000 /* Receive Buffer */ + +/* Xmit complete */ +#define XEL_TSR_XMIT_BUSY_MASK 0x00000001UL +/* Xmit interrupt enable bit */ +#define XEL_TSR_XMIT_IE_MASK 0x00000008UL +/* Buffer is active, SW bit only */ +#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000UL +/* Program the MAC address */ +#define XEL_TSR_PROGRAM_MASK 0x00000002UL +/* define for programming the MAC address into the EMAC Lite */ +#define XEL_TSR_PROG_MAC_ADDR (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK) + +/* Transmit packet length upper byte */ +#define XEL_TPLR_LENGTH_MASK_HI 0x0000FF00UL +/* Transmit packet length lower byte */ +#define XEL_TPLR_LENGTH_MASK_LO 0x000000FFUL + +/* Recv complete */ +#define XEL_RSR_RECV_DONE_MASK 0x00000001UL +/* Recv interrupt enable bit */ +#define XEL_RSR_RECV_IE_MASK 0x00000008UL + +struct xemaclite { + u32 nexttxbuffertouse; /* Next TX buffer to write to */ + u32 nextrxbuffertouse; /* Next RX buffer to read from */ + u32 txpp; /* TX ping pong buffer */ + u32 rxpp; /* RX ping pong buffer */ +}; + +static u32 etherrxbuff[PKTSIZE_ALIGN/4]; /* Receive buffer */ + +static void xemaclite_alignedread(u32 *srcptr, void *destptr, u32 bytecount) +{ + u32 i; + u32 alignbuffer; + u32 *to32ptr; + u32 *from32ptr; + u8 *to8ptr; + u8 *from8ptr; + + from32ptr = (u32 *) srcptr; + + /* Word aligned buffer, no correction needed. */ + to32ptr = (u32 *) destptr; + while (bytecount > 3) { + *to32ptr++ = *from32ptr++; + bytecount -= 4; + } + to8ptr = (u8 *) to32ptr; + + alignbuffer = *from32ptr++; + from8ptr = (u8 *) &alignbuffer; + + for (i = 0; i < bytecount; i++) + *to8ptr++ = *from8ptr++; +} + +static void xemaclite_alignedwrite(void *srcptr, u32 destptr, u32 bytecount) +{ + u32 i; + u32 alignbuffer; + u32 *to32ptr = (u32 *) destptr; + u32 *from32ptr; + u8 *to8ptr; + u8 *from8ptr; + + from32ptr = (u32 *) srcptr; + while (bytecount > 3) { + + *to32ptr++ = *from32ptr++; + bytecount -= 4; + } + + alignbuffer = 0; + to8ptr = (u8 *) &alignbuffer; + from8ptr = (u8 *) from32ptr; + + for (i = 0; i < bytecount; i++) + *to8ptr++ = *from8ptr++; + + *to32ptr++ = alignbuffer; +} + +static void emaclite_halt(struct eth_device *dev) +{ + debug("eth_halt\n"); +} + +static int emaclite_init(struct eth_device *dev, bd_t *bis) +{ + struct xemaclite *emaclite = dev->priv; + debug("EmacLite Initialization Started\n"); + +/* + * TX - TX_PING & TX_PONG initialization + */ + /* Restart PING TX */ + out_be32 (dev->iobase + XEL_TSR_OFFSET, 0); + /* Copy MAC address */ + xemaclite_alignedwrite(dev->enetaddr, dev->iobase, ENET_ADDR_LENGTH); + /* Set the length */ + out_be32 (dev->iobase + XEL_TPLR_OFFSET, ENET_ADDR_LENGTH); + /* Update the MAC address in the EMAC Lite */ + out_be32 (dev->iobase + XEL_TSR_OFFSET, XEL_TSR_PROG_MAC_ADDR); + /* Wait for EMAC Lite to finish with the MAC address update */ + while ((in_be32 (dev->iobase + XEL_TSR_OFFSET) & + XEL_TSR_PROG_MAC_ADDR) != 0) + ; + + if (emaclite->txpp) { + /* The same operation with PONG TX */ + out_be32 (dev->iobase + XEL_TSR_OFFSET + XEL_BUFFER_OFFSET, 0); + xemaclite_alignedwrite(dev->enetaddr, dev->iobase + + XEL_BUFFER_OFFSET, ENET_ADDR_LENGTH); + out_be32 (dev->iobase + XEL_TPLR_OFFSET, ENET_ADDR_LENGTH); + out_be32 (dev->iobase + XEL_TSR_OFFSET + XEL_BUFFER_OFFSET, + XEL_TSR_PROG_MAC_ADDR); + while ((in_be32 (dev->iobase + XEL_TSR_OFFSET + + XEL_BUFFER_OFFSET) & XEL_TSR_PROG_MAC_ADDR) != 0) + ; + } + +/* + * RX - RX_PING & RX_PONG initialization + */ + /* Write out the value to flush the RX buffer */ + out_be32 (dev->iobase + XEL_RSR_OFFSET, XEL_RSR_RECV_IE_MASK); + + if (emaclite->rxpp) + out_be32 (dev->iobase + XEL_RSR_OFFSET + XEL_BUFFER_OFFSET, + XEL_RSR_RECV_IE_MASK); + + debug("EmacLite Initialization complete\n"); + return 0; +} + +static int xemaclite_txbufferavailable(struct eth_device *dev) +{ + u32 reg; + u32 txpingbusy; + u32 txpongbusy; + struct xemaclite *emaclite = dev->priv; + + /* + * Read the other buffer register + * and determine if the other buffer is available + */ + reg = in_be32 (dev->iobase + + emaclite->nexttxbuffertouse + 0); + txpingbusy = ((reg & XEL_TSR_XMIT_BUSY_MASK) == + XEL_TSR_XMIT_BUSY_MASK); + + reg = in_be32 (dev->iobase + + (emaclite->nexttxbuffertouse ^ XEL_TSR_OFFSET) + 0); + txpongbusy = ((reg & XEL_TSR_XMIT_BUSY_MASK) == + XEL_TSR_XMIT_BUSY_MASK); + + return !(txpingbusy && txpongbusy); +} + +static int emaclite_send(struct eth_device *dev, void *ptr, int len) +{ + u32 reg; + u32 baseaddress; + struct xemaclite *emaclite = dev->priv; + + u32 maxtry = 1000; + + if (len > PKTSIZE) + len = PKTSIZE; + + while (!xemaclite_txbufferavailable(dev) && maxtry) { + udelay(10); + maxtry--; + } + + if (!maxtry) { + printf("Error: Timeout waiting for ethernet TX buffer\n"); + /* Restart PING TX */ + out_be32 (dev->iobase + XEL_TSR_OFFSET, 0); + if (emaclite->txpp) { + out_be32 (dev->iobase + XEL_TSR_OFFSET + + XEL_BUFFER_OFFSET, 0); + } + return -1; + } + + /* Determine the expected TX buffer address */ + baseaddress = (dev->iobase + emaclite->nexttxbuffertouse); + + /* Determine if the expected buffer address is empty */ + reg = in_be32 (baseaddress + XEL_TSR_OFFSET); + if (((reg & XEL_TSR_XMIT_BUSY_MASK) == 0) + && ((in_be32 ((baseaddress) + XEL_TSR_OFFSET) + & XEL_TSR_XMIT_ACTIVE_MASK) == 0)) { + + if (emaclite->txpp) + emaclite->nexttxbuffertouse ^= XEL_BUFFER_OFFSET; + + debug("Send packet from 0x%x\n", baseaddress); + /* Write the frame to the buffer */ + xemaclite_alignedwrite(ptr, baseaddress, len); + out_be32 (baseaddress + XEL_TPLR_OFFSET,(len & + (XEL_TPLR_LENGTH_MASK_HI | XEL_TPLR_LENGTH_MASK_LO))); + reg = in_be32 (baseaddress + XEL_TSR_OFFSET); + reg |= XEL_TSR_XMIT_BUSY_MASK; + if ((reg & XEL_TSR_XMIT_IE_MASK) != 0) + reg |= XEL_TSR_XMIT_ACTIVE_MASK; + out_be32 (baseaddress + XEL_TSR_OFFSET, reg); + return 0; + } + + if (emaclite->txpp) { + /* Switch to second buffer */ + baseaddress ^= XEL_BUFFER_OFFSET; + /* Determine if the expected buffer address is empty */ + reg = in_be32 (baseaddress + XEL_TSR_OFFSET); + if (((reg & XEL_TSR_XMIT_BUSY_MASK) == 0) + && ((in_be32 ((baseaddress) + XEL_TSR_OFFSET) + & XEL_TSR_XMIT_ACTIVE_MASK) == 0)) { + debug("Send packet from 0x%x\n", baseaddress); + /* Write the frame to the buffer */ + xemaclite_alignedwrite(ptr, baseaddress, len); + out_be32 (baseaddress + XEL_TPLR_OFFSET, (len & + (XEL_TPLR_LENGTH_MASK_HI | + XEL_TPLR_LENGTH_MASK_LO))); + reg = in_be32 (baseaddress + XEL_TSR_OFFSET); + reg |= XEL_TSR_XMIT_BUSY_MASK; + if ((reg & XEL_TSR_XMIT_IE_MASK) != 0) + reg |= XEL_TSR_XMIT_ACTIVE_MASK; + out_be32 (baseaddress + XEL_TSR_OFFSET, reg); + return 0; + } + } + + puts("Error while sending frame\n"); + return -1; +} + +static int emaclite_recv(struct eth_device *dev) +{ + u32 length; + u32 reg; + u32 baseaddress; + struct xemaclite *emaclite = dev->priv; + + baseaddress = dev->iobase + emaclite->nextrxbuffertouse; + reg = in_be32 (baseaddress + XEL_RSR_OFFSET); + debug("Testing data at address 0x%x\n", baseaddress); + if ((reg & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { + if (emaclite->rxpp) + emaclite->nextrxbuffertouse ^= XEL_BUFFER_OFFSET; + } else { + + if (!emaclite->rxpp) { + debug("No data was available - address 0x%x\n", + baseaddress); + return 0; + } else { + baseaddress ^= XEL_BUFFER_OFFSET; + reg = in_be32 (baseaddress + XEL_RSR_OFFSET); + if ((reg & XEL_RSR_RECV_DONE_MASK) != + XEL_RSR_RECV_DONE_MASK) { + debug("No data was available - address 0x%x\n", + baseaddress); + return 0; + } + } + } + /* Get the length of the frame that arrived */ + switch(((ntohl(in_be32 (baseaddress + XEL_RXBUFF_OFFSET + 0xC))) & + 0xFFFF0000 ) >> 16) { + case 0x806: + length = 42 + 20; /* FIXME size of ARP */ + debug("ARP Packet\n"); + break; + case 0x800: + length = 14 + 14 + + (((ntohl(in_be32 (baseaddress + XEL_RXBUFF_OFFSET + + 0x10))) & 0xFFFF0000) >> 16); + /* FIXME size of IP packet */ + debug ("IP Packet\n"); + break; + default: + debug("Other Packet\n"); + length = PKTSIZE; + break; + } + + xemaclite_alignedread((u32 *) (baseaddress + XEL_RXBUFF_OFFSET), + etherrxbuff, length); + + /* Acknowledge the frame */ + reg = in_be32 (baseaddress + XEL_RSR_OFFSET); + reg &= ~XEL_RSR_RECV_DONE_MASK; + out_be32 (baseaddress + XEL_RSR_OFFSET, reg); + + debug("Packet receive from 0x%x, length %dB\n", baseaddress, length); + NetReceive((uchar *) etherrxbuff, length); + return length; + +} + +int xilinx_emaclite_initialize(bd_t *bis, unsigned long base_addr, + int txpp, int rxpp) +{ + struct eth_device *dev; + struct xemaclite *emaclite; + + dev = calloc(1, sizeof(*dev)); + if (dev == NULL) + return -1; + + emaclite = calloc(1, sizeof(struct xemaclite)); + if (emaclite == NULL) { + free(dev); + return -1; + } + + dev->priv = emaclite; + + emaclite->txpp = txpp; + emaclite->rxpp = rxpp; + + sprintf(dev->name, "Xelite.%lx", base_addr); + + dev->iobase = base_addr; + dev->init = emaclite_init; + dev->halt = emaclite_halt; + dev->send = emaclite_send; + dev->recv = emaclite_recv; + + eth_register(dev); + + return 1; +} + +#ifdef CONFIG_OF_CONTROL +int xilinx_emaclite_of_init(const void *blob) +{ + int offset = 0; + u32 ret = 0; + u32 reg; + + do { + offset = fdt_node_offset_by_compatible(blob, offset, + "xlnx,xps-ethernetlite-1.00.a"); + if (offset != -1) { + reg = fdtdec_get_addr(blob, offset, "reg"); + if (reg != FDT_ADDR_T_NONE) { + u32 rxpp = fdtdec_get_int(blob, offset, + "xlnx,rx-ping-pong", 0); + u32 txpp = fdtdec_get_int(blob, offset, + "xlnx,tx-ping-pong", 0); + ret |= xilinx_emaclite_initialize(NULL, reg, + txpp, rxpp); + } else { + debug("EMACLITE: Can't get base address\n"); + return -1; + } + } + } while (offset != -1); + + return ret; +} +#endif diff --git a/qemu/roms/u-boot/drivers/net/xilinx_ll_temac.c b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac.c new file mode 100644 index 000000000..dab78d073 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac.c @@ -0,0 +1,402 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * supports SDMA or FIFO access and MDIO bus communication + * + * Copyright (C) 2011 - 2012 Stephan Linz + * Copyright (C) 2008 - 2011 Michal Simek + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "xilinx_ll_temac.h" +#include "xilinx_ll_temac_fifo.h" +#include "xilinx_ll_temac_sdma.h" +#include "xilinx_ll_temac_mdio.h" + +#if !defined(CONFIG_MII) +# error "LL_TEMAC requires MII -- missing CONFIG_MII" +#endif + +#if !defined(CONFIG_PHYLIB) +# error "LL_TEMAC requires PHYLIB -- missing CONFIG_PHYLIB" +#endif + +struct ll_temac_info { + int flags; + unsigned long base_addr; + unsigned long ctrl_addr; + char *devname; + unsigned int phyaddr; + char *mdio_busname; +}; + +/* Ethernet interface ready status */ +int ll_temac_check_status(struct temac_reg *regs, u32 mask) +{ + unsigned timeout = 50; /* 1usec * 50 = 50usec */ + + /* + * Quote from LL TEMAC documentation: The bits in the RDY + * register are asserted when there is no access in progress. + * When an access is in progress, a bit corresponding to the + * type of access is automatically de-asserted. The bit is + * automatically re-asserted when the access is complete. + */ + while (timeout && (!(in_be32(®s->rdy) & mask))) { + timeout--; + udelay(1); + } + + if (!timeout) { + printf("%s: Timeout on 0x%08x @%p\n", __func__, + mask, ®s->rdy); + return 1; + } + + return 0; +} + +/* + * Indirect write to ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 23, second paragraph, The use of CTL0 register or CTL1 register + */ +int ll_temac_indirect_set(struct temac_reg *regs, u16 regn, u32 reg_data) +{ + out_be32(®s->lsw, (reg_data & MLSW_MASK)); + out_be32(®s->ctl, CTL_WEN | (regn & CTL_ADDR_MASK)); + + if (ll_temac_check_status(regs, RSE_CFG_WR)) + return 0; + + return 1; +} + +/* + * Indirect read from ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 23, second paragraph, The use of CTL0 register or CTL1 register + */ +int ll_temac_indirect_get(struct temac_reg *regs, u16 regn, u32* reg_data) +{ + out_be32(®s->ctl, (regn & CTL_ADDR_MASK)); + + if (ll_temac_check_status(regs, RSE_CFG_RR)) + return 0; + + *reg_data = in_be32(®s->lsw) & MLSW_MASK; + return 1; +} + +/* setting sub-controller and ll_temac to proper setting */ +static int ll_temac_setup_ctrl(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + + if (ll_temac->ctrlreset && ll_temac->ctrlreset(dev)) + return 0; + + if (ll_temac->ctrlinit && ll_temac->ctrlinit(dev)) + return 0; + + /* Promiscuous mode disable */ + if (!ll_temac_indirect_set(regs, TEMAC_AFM, 0)) + return 0; + + /* Enable Receiver - RX bit */ + if (!ll_temac_indirect_set(regs, TEMAC_RCW1, RCW1_RX)) + return 0; + + /* Enable Transmitter - TX bit */ + if (!ll_temac_indirect_set(regs, TEMAC_TC, TC_TX)) + return 0; + + return 1; +} + +/* + * Configure ll_temac based on negotiated speed and duplex + * reported by PHY handling code + */ +static int ll_temac_adjust_link(struct eth_device *dev) +{ + unsigned int speed, emmc_reg; + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + struct ll_temac *ll_temac = dev->priv; + struct phy_device *phydev = ll_temac->phydev; + + if (!phydev->link) { + printf("%s: No link.\n", phydev->dev->name); + return 0; + } + + switch (phydev->speed) { + case 1000: + speed = EMMC_LSPD_1000; + break; + case 100: + speed = EMMC_LSPD_100; + break; + case 10: + speed = EMMC_LSPD_10; + break; + default: + return 0; + } + + if (!ll_temac_indirect_get(regs, TEMAC_EMMC, &emmc_reg)) + return 0; + + emmc_reg &= ~EMMC_LSPD_MASK; + emmc_reg |= speed; + + if (!ll_temac_indirect_set(regs, TEMAC_EMMC, emmc_reg)) + return 0; + + printf("%s: PHY is %s with %dbase%s, %s%s\n", + dev->name, phydev->drv->name, + phydev->speed, (phydev->port == PORT_TP) ? "T" : "X", + (phydev->duplex) ? "FDX" : "HDX", + (phydev->port == PORT_OTHER) ? ", unkown mode" : ""); + + return 1; +} + +/* setup mac addr */ +static int ll_temac_setup_mac_addr(struct eth_device *dev) +{ + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + u32 val; + + /* set up unicast MAC address filter */ + val = ((dev->enetaddr[3] << 24) | (dev->enetaddr[2] << 16) | + (dev->enetaddr[1] << 8) | (dev->enetaddr[0])); + val &= UAW0_UADDR_MASK; + + if (!ll_temac_indirect_set(regs, TEMAC_UAW0, val)) + return 1; + + val = ((dev->enetaddr[5] << 8) | dev->enetaddr[4]); + val &= UAW1_UADDR_MASK; + + if (!ll_temac_indirect_set(regs, TEMAC_UAW1, val)) + return 1; + + return 0; +} + +/* halt device */ +static void ll_temac_halt(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + + /* Disable Receiver */ + ll_temac_indirect_set(regs, TEMAC_RCW0, 0); + + /* Disable Transmitter */ + ll_temac_indirect_set(regs, TEMAC_TC, 0); + + if (ll_temac->ctrlhalt) + ll_temac->ctrlhalt(dev); + + /* Shut down the PHY, as needed */ + phy_shutdown(ll_temac->phydev); +} + +static int ll_temac_init(struct eth_device *dev, bd_t *bis) +{ + struct ll_temac *ll_temac = dev->priv; + int ret; + + printf("%s: Xilinx XPS LocalLink Tri-Mode Ether MAC #%d at 0x%08X.\n", + dev->name, dev->index, dev->iobase); + + if (!ll_temac_setup_ctrl(dev)) + return -1; + + /* Start up the PHY */ + ret = phy_startup(ll_temac->phydev); + if (ret) { + printf("%s: Could not initialize PHY %s\n", + dev->name, ll_temac->phydev->dev->name); + return ret; + } + + if (!ll_temac_adjust_link(dev)) { + ll_temac_halt(dev); + return -1; + } + + /* If there's no link, fail */ + return ll_temac->phydev->link ? 0 : -1; +} + +/* + * Discover which PHY is attached to the device, and configure it + * properly. If the PHY is not recognized, then return 0 + * (failure). Otherwise, return 1 + */ +static int ll_temac_phy_init(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct phy_device *phydev; + unsigned int supported = PHY_GBIT_FEATURES; + + /* interface - look at driver/net/tsec.c */ + phydev = phy_connect(ll_temac->bus, ll_temac->phyaddr, + dev, PHY_INTERFACE_MODE_NONE); + + phydev->supported &= supported; + phydev->advertising = phydev->supported; + + ll_temac->phydev = phydev; + + phy_config(phydev); + + return 1; +} + +/* + * Initialize a single ll_temac devices + * + * Returns the result of ll_temac phy interface that were initialized + */ +int xilinx_ll_temac_initialize(bd_t *bis, struct ll_temac_info *devinf) +{ + struct eth_device *dev; + struct ll_temac *ll_temac; + + dev = calloc(1, sizeof(*dev)); + if (dev == NULL) + return 0; + + ll_temac = calloc(1, sizeof(struct ll_temac)); + if (ll_temac == NULL) { + free(dev); + return 0; + } + + /* use given name or generate its own unique name */ + if (devinf->devname) { + strncpy(dev->name, devinf->devname, sizeof(dev->name)); + } else { + snprintf(dev->name, sizeof(dev->name), "lltemac.%lx", devinf->base_addr); + devinf->devname = dev->name; + } + + dev->iobase = devinf->base_addr; + + dev->priv = ll_temac; + dev->init = ll_temac_init; + dev->halt = ll_temac_halt; + dev->write_hwaddr = ll_temac_setup_mac_addr; + + ll_temac->ctrladdr = devinf->ctrl_addr; + if (devinf->flags & XILINX_LL_TEMAC_M_SDMA_PLB) { +#if defined(CONFIG_XILINX_440) || defined(CONFIG_XILINX_405) + if (devinf->flags & XILINX_LL_TEMAC_M_SDMA_DCR) { + ll_temac_collect_xldcr_sdma_reg_addr(dev); + ll_temac->in32 = ll_temac_xldcr_in32; + ll_temac->out32 = ll_temac_xldcr_out32; + } else +#endif + { + ll_temac_collect_xlplb_sdma_reg_addr(dev); + ll_temac->in32 = ll_temac_xlplb_in32; + ll_temac->out32 = ll_temac_xlplb_out32; + } + ll_temac->ctrlinit = ll_temac_init_sdma; + ll_temac->ctrlhalt = ll_temac_halt_sdma; + ll_temac->ctrlreset = ll_temac_reset_sdma; + dev->recv = ll_temac_recv_sdma; + dev->send = ll_temac_send_sdma; + } else { + ll_temac->in32 = NULL; + ll_temac->out32 = NULL; + ll_temac->ctrlinit = NULL; + ll_temac->ctrlhalt = NULL; + ll_temac->ctrlreset = ll_temac_reset_fifo; + dev->recv = ll_temac_recv_fifo; + dev->send = ll_temac_send_fifo; + } + + /* Link to specified MDIO bus */ + strncpy(ll_temac->mdio_busname, devinf->mdio_busname, MDIO_NAME_LEN); + ll_temac->bus = miiphy_get_dev_by_name(ll_temac->mdio_busname); + + /* Looking for a valid PHY address if it is not yet set */ + if (devinf->phyaddr == -1) + ll_temac->phyaddr = ll_temac_phy_addr(ll_temac->bus); + else + ll_temac->phyaddr = devinf->phyaddr; + + eth_register(dev); + + /* Try to initialize PHY here, and return */ + return ll_temac_phy_init(dev); +} + +/* + * Initialize a single ll_temac device with its mdio bus behind ll_temac + * + * Returns 1 if the ll_temac device and the mdio bus were initialized + * otherwise returns 0 + */ +int xilinx_ll_temac_eth_init(bd_t *bis, unsigned long base_addr, int flags, + unsigned long ctrl_addr) +{ + struct ll_temac_info devinf; + struct ll_temac_mdio_info mdioinf; + int ret; + + /* prepare the internal driver informations */ + devinf.flags = flags; + devinf.base_addr = base_addr; + devinf.ctrl_addr = ctrl_addr; + devinf.devname = NULL; + devinf.phyaddr = -1; + + mdioinf.name = devinf.mdio_busname = NULL; + mdioinf.regs = (struct temac_reg *)devinf.base_addr; + + ret = xilinx_ll_temac_mdio_initialize(bis, &mdioinf); + if (ret >= 0) { + + /* + * If there was no MDIO bus name then take over the + * new automaticaly generated by the MDIO init code. + */ + if (mdioinf.name != devinf.mdio_busname) + devinf.mdio_busname = mdioinf.name; + + ret = xilinx_ll_temac_initialize(bis, &devinf); + if (ret > 0) + return 1; + + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/xilinx_ll_temac.h b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac.h new file mode 100644 index 000000000..56362ba20 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac.h @@ -0,0 +1,307 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * LL_TEMAC interface + * + * Copyright (C) 2011 - 2012 Stephan Linz + * Copyright (C) 2008 - 2011 Michal Simek + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ +#ifndef _XILINX_LL_TEMAC_ +#define _XILINX_LL_TEMAC_ + +#include +#include +#include +#include + +#include +#include + +#include "xilinx_ll_temac_sdma.h" + +#if !defined(__BIG_ENDIAN) +# error LL_TEMAC requires big endianess +#endif + +/* + * TEMAC Memory and Register Definition + * + * [1]: [0]/ip_documentation/xps_ll_temac.pdf + * page 19, Memory and Register Descriptions + */ +struct temac_reg { + /* direct soft registers (low part) */ + u32 raf; /* Reset and Address Filter */ + u32 tpf; /* Transmit Pause Frame */ + u32 ifgp; /* Transmit Inter Frame Gap Adjustment */ + u32 is; /* Interrupt Status */ + u32 ip; /* Interrupt Pending */ + u32 ie; /* Interrupt Enable */ + u32 ttag; /* Transmit VLAN Tag */ + u32 rtag; /* Receive VLAN Tag */ + /* hard TEMAC registers */ + u32 msw; /* Most Significant Word Data */ + u32 lsw; /* Least Significant Word Data */ + u32 ctl; /* Control */ + u32 rdy; /* Ready Status */ + /* direct soft registers (high part) */ + u32 uawl; /* Unicast Address Word Lower */ + u32 uawu; /* Unicast Address Word Upper */ + u32 tpid0; /* VLAN TPID Word 0 */ + u32 tpid1; /* VLAN TPID Word 1 */ +}; + +/* Reset and Address Filter Registers (raf), [1] p25 */ +#define RAF_SR (1 << 13) +#define RAF_EMFE (1 << 12) +#define RAF_NFE (1 << 11) +#define RAF_RVSTM_POS 9 +#define RAF_RVSTM_MASK (3 << RAF_RVSTM_POS) +#define RAF_TVSTM_POS 7 +#define RAF_TVSTM_MASK (3 << RAF_TVSTM_POS) +#define RAF_RVTM_POS 5 +#define RAF_RVTM_MASK (3 << RAF_RVTM_POS) +#define RAF_TVTM_POS 3 +#define RAF_TVTM_MASK (3 << RAF_TVTM_POS) +#define RAF_BCREJ (1 << 2) +#define RAF_MCREJ (1 << 1) +#define RAF_HTRST (1 << 0) + +/* Transmit Pause Frame Registers (tpf), [1] p28 */ +#define TPF_TPFV_POS 0 +#define TPF_TPFV_MASK (0xFFFF << TPF_TPFV_POS) + +/* Transmit Inter Frame Gap Adjustment Registers (ifgp), [1] p28 */ +#define IFGP_POS 0 +#define IFGP_MASK (0xFF << IFGP_POS) + +/* Interrupt Status, Pending, Enable Registers (is, ip, ie), [1] p29-33 */ +#define ISPE_MR (1 << 7) +#define ISPE_RDL (1 << 6) +#define ISPE_TC (1 << 5) +#define ISPE_RFO (1 << 4) +#define ISPE_RR (1 << 3) +#define ISPE_RC (1 << 2) +#define ISPE_AN (1 << 1) +#define ISPE_HAC (1 << 0) + +/* Transmit, Receive VLAN Tag Registers (ttag, rtag), [1] p34-35 */ +#define TRTAG_TPID_POS 16 +#define TRTAG_TPID_MASK (0xFFFF << TRTAG_TPID_POS) +#define TRTAG_PRIO_POS 13 +#define TRTAG_PRIO_MASK (7 << TRTAG_PRIO_POS) +#define TRTAG_CFI (1 << 12) +#define TRTAG_VID_POS 0 +#define TRTAG_VID_MASK (0xFFF << TRTAG_VID_POS) + +/* Most, Least Significant Word Data Register (msw, lsw), [1] p46 */ +#define MLSW_POS 0 +#define MLSW_MASK (~0UL << MLSW_POS) + +/* LSW Data Register for PHY addresses (lsw), [1] p66 */ +#define LSW_REGAD_POS 0 +#define LSW_REGAD_MASK (0x1F << LSW_REGAD_POS) +#define LSW_PHYAD_POS 5 +#define LSW_PHYAD_MASK (0x1F << LSW_PHYAD_POS) + +/* LSW Data Register for PHY data (lsw), [1] p66 */ +#define LSW_REGDAT_POS 0 +#define LSW_REGDAT_MASK (0xFFFF << LSW_REGDAT_POS) + +/* Control Register (ctl), [1] p47 */ +#define CTL_WEN (1 << 15) +#define CTL_ADDR_POS 0 +#define CTL_ADDR_MASK (0x3FF << CTL_ADDR_POS) + +/* Ready Status Register Ethernet (rdy), [1] p48 */ +#define RSE_HACS_RDY (1 << 14) +#define RSE_CFG_WR (1 << 6) +#define RSE_CFG_RR (1 << 5) +#define RSE_AF_WR (1 << 4) +#define RSE_AF_RR (1 << 3) +#define RSE_MIIM_WR (1 << 2) +#define RSE_MIIM_RR (1 << 1) +#define RSE_FABR_RR (1 << 0) + +/* Unicast Address Word Lower, Upper Registers (uawl, uawu), [1] p35-36 */ +#define UAWL_UADDR_POS 0 +#define UAWL_UADDR_MASK (~0UL << UAWL_UADDR_POS) +#define UAWU_UADDR_POS 0 +#define UAWU_UADDR_MASK (0xFFFF << UAWU_UADDR_POS) + +/* VLAN TPID Word 0, 1 Registers (tpid0, tpid1), [1] p37 */ +#define TPID0_V0_POS 0 +#define TPID0_V0_MASK (0xFFFF << TPID0_V0_POS) +#define TPID0_V1_POS 16 +#define TPID0_V1_MASK (0xFFFF << TPID0_V1_POS) +#define TPID1_V2_POS 0 +#define TPID1_V2_MASK (0xFFFF << TPID1_V2_POS) +#define TPID1_V3_POS 16 +#define TPID1_V3_MASK (0xFFFF << TPID1_V3_POS) + +/* + * TEMAC Indirectly Addressable Register Index Enumeration + * + * [0]: http://www.xilinx.com/support/documentation + * + * [1]: [0]/ip_documentation/xps_ll_temac.pdf + * page 23, PLB Indirectly Addressable TEMAC Registers + */ +enum temac_ctrl { + TEMAC_RCW0 = 0x200, + TEMAC_RCW1 = 0x240, + TEMAC_TC = 0x280, + TEMAC_FCC = 0x2C0, + TEMAC_EMMC = 0x300, + TEMAC_PHYC = 0x320, + TEMAC_MC = 0x340, + TEMAC_UAW0 = 0x380, + TEMAC_UAW1 = 0x384, + TEMAC_MAW0 = 0x388, + TEMAC_MAW1 = 0x38C, + TEMAC_AFM = 0x390, + TEMAC_TIS = 0x3A0, + TEMAC_TIE = 0x3A4, + TEMAC_MIIMWD = 0x3B0, + TEMAC_MIIMAI = 0x3B4 +}; + +/* Receive Configuration Word 0, 1 Registers (RCW0, RCW1), [1] p50-51 */ +#define RCW0_PADDR_POS 0 +#define RCW0_PADDR_MASK (~0UL << RCW_PADDR_POS) +#define RCW1_RST (1 << 31) +#define RCW1_JUM (1 << 30) +#define RCW1_FCS (1 << 29) +#define RCW1_RX (1 << 28) +#define RCW1_VLAN (1 << 27) +#define RCW1_HD (1 << 26) +#define RCW1_LT_DIS (1 << 25) +#define RCW1_PADDR_POS 0 +#define RCW1_PADDR_MASK (0xFFFF << RCW_PADDR_POS) + +/* Transmit Configuration Registers (TC), [1] p52 */ +#define TC_RST (1 << 31) +#define TC_JUM (1 << 30) +#define TC_FCS (1 << 29) +#define TC_TX (1 << 28) +#define TC_VLAN (1 << 27) +#define TC_HD (1 << 26) +#define TC_IFG (1 << 25) + +/* Flow Control Configuration Registers (FCC), [1] p54 */ +#define FCC_FCTX (1 << 30) +#define FCC_FCRX (1 << 29) + +/* Ethernet MAC Mode Configuration Registers (EMMC), [1] p54 */ +#define EMMC_LSPD_POS 30 +#define EMMC_LSPD_MASK (3 << EMMC_LSPD_POS) +#define EMMC_LSPD_1000 (2 << EMMC_LSPD_POS) +#define EMMC_LSPD_100 (1 << EMMC_LSPD_POS) +#define EMMC_LSPD_10 0 +#define EMMC_RGMII (1 << 29) +#define EMMC_SGMII (1 << 28) +#define EMMC_GPCS (1 << 27) +#define EMMC_HOST (1 << 26) +#define EMMC_TX16 (1 << 25) +#define EMMC_RX16 (1 << 24) + +/* RGMII/SGMII Configuration Registers (PHYC), [1] p56 */ +#define PHYC_SLSPD_POS 30 +#define PHYC_SLSPD_MASK (3 << EMMC_SLSPD_POS) +#define PHYC_SLSPD_1000 (2 << EMMC_SLSPD_POS) +#define PHYC_SLSPD_100 (1 << EMMC_SLSPD_POS) +#define PHYC_SLSPD_10 0 +#define PHYC_RLSPD_POS 2 +#define PHYC_RLSPD_MASK (3 << EMMC_RLSPD_POS) +#define PHYC_RLSPD_1000 (2 << EMMC_RLSPD_POS) +#define PHYC_RLSPD_100 (1 << EMMC_RLSPD_POS) +#define PHYC_RLSPD_10 0 +#define PHYC_RGMII_HD (1 << 1) +#define PHYC_RGMII_LINK (1 << 0) + +/* Management Configuration Registers (MC), [1] p57 */ +#define MC_MDIOEN (1 << 6) +#define MC_CLKDIV_POS 0 +#define MC_CLKDIV_MASK (0x3F << MC_CLKDIV_POS) + +/* + * fHOSTCLK fMDC = fHOSTCLK + * fMDC = ------------------- ---------> MC_CLKDIV = -------- - 1 + * (1 + MC_CLKDIV) * 2 2.5 MHz 5MHz + */ +#define MC_CLKDIV(f, m) ((f / (2 * m)) - 1) +#define MC_CLKDIV_25(f) MC_CLKDIV(f, 2500000) +#define MC_CLKDIV_20(f) MC_CLKDIV(f, 2000000) +#define MC_CLKDIV_15(f) MC_CLKDIV(f, 1500000) +#define MC_CLKDIV_10(f) MC_CLKDIV(f, 1000000) + +/* Unicast Address Word 0, 1 Registers (UAW0, UAW1), [1] p58-59 */ +#define UAW0_UADDR_POS 0 +#define UAW0_UADDR_MASK (~0UL << UAW0_UADDR_POS) +#define UAW1_UADDR_POS 0 +#define UAW1_UADDR_MASK (0xFFFF << UAW1_UADDR_POS) + +/* Multicast Address Word 0, 1 Registers (MAW0, MAW1), [1] p60 */ +#define MAW0_MADDR_POS 0 +#define MAW0_MADDR_MASK (~0UL << MAW0_MADDR_POS) +#define MAW1_RNW (1 << 23) +#define MAW1_MAIDX_POS 16 +#define MAW1_MAIDX_MASK (3 << MAW1_MAIDX_POS) +#define MAW1_MADDR_POS 0 +#define MAW1_MADDR_MASK (0xFFFF << MAW1_MADDR_POS) + +/* Address Filter Mode Registers (AFM), [1] p63 */ +#define AFM_PM (1 << 31) + +/* Interrupt Status, Enable Registers (TIS, TIE), [1] p63-65 */ +#define TISE_CFG_W (1 << 6) +#define TISE_CFG_R (1 << 5) +#define TISE_AF_W (1 << 4) +#define TISE_AF_R (1 << 3) +#define TISE_MIIM_W (1 << 2) +#define TISE_MIIM_R (1 << 1) +#define TISE_FABR_R (1 << 0) + +/* MII Management Write Data Registers (MIIMWD), [1] p66 */ +#define MIIMWD_DATA_POS 0 +#define MIIMWD_DATA_MASK (0xFFFF << MIIMWD_DATA_POS) + +/* Ethernet interface ready status */ +int ll_temac_check_status(struct temac_reg *regs, u32 mask); + +/* Indirect write to ll_temac. */ +int ll_temac_indirect_set(struct temac_reg *regs, u16 regn, u32 reg_data); + +/* Indirect read from ll_temac. */ +int ll_temac_indirect_get(struct temac_reg *regs, u16 regn, u32* reg_data); + +struct ll_temac { + phys_addr_t ctrladdr; + phys_addr_t sdma_reg_addr[SDMA_CTRL_REGNUMS]; + + unsigned (*in32)(phys_addr_t); + void (*out32)(phys_addr_t, unsigned); + + int (*ctrlinit) (struct eth_device *); + int (*ctrlhalt) (struct eth_device *); + int (*ctrlreset) (struct eth_device *); + + int phyaddr; + struct phy_device *phydev; + struct mii_dev *bus; + char mdio_busname[MDIO_NAME_LEN]; +}; + +#endif /* _XILINX_LL_TEMAC_ */ diff --git a/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_fifo.c b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_fifo.c new file mode 100644 index 000000000..b8993cdb2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_fifo.c @@ -0,0 +1,139 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * FIFO sub-controller + * + * Copyright (C) 2011 - 2012 Stephan Linz + * Copyright (C) 2008 - 2011 Michal Simek + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * CREDITS: tsec driver + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [F]: [0]/ip_documentation/xps_ll_fifo.pdf + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ + +#include +#include +#include + +#include +#include + +#include "xilinx_ll_temac.h" +#include "xilinx_ll_temac_fifo.h" + +int ll_temac_reset_fifo(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct fifo_ctrl *fifo_ctrl = (void *)ll_temac->ctrladdr; + + out_be32(&fifo_ctrl->tdfr, LL_FIFO_TDFR_KEY); + out_be32(&fifo_ctrl->rdfr, LL_FIFO_RDFR_KEY); + out_be32(&fifo_ctrl->isr, ~0UL); + out_be32(&fifo_ctrl->ier, 0); + + return 0; +} + +int ll_temac_recv_fifo(struct eth_device *dev) +{ + int i, length = 0; + u32 *buf = (u32 *)NetRxPackets[0]; + struct ll_temac *ll_temac = dev->priv; + struct fifo_ctrl *fifo_ctrl = (void *)ll_temac->ctrladdr; + + if (in_be32(&fifo_ctrl->isr) & LL_FIFO_ISR_RC) { + + /* reset isr */ + out_be32(&fifo_ctrl->isr, ~0UL); + + /* + * MAYBE here: + * while (fifo_ctrl->isr); + */ + + /* + * The length is written (into RLR) by the XPS LL FIFO + * when the packet is received across the RX LocalLink + * interface and the receive data FIFO had enough + * locations that all of the packet data has been saved. + * The RLR should only be read when a receive packet is + * available for processing (the receive occupancy is + * not zero). Once the RLR is read, the receive packet + * data should be read from the receive data FIFO before + * the RLR is read again. + * + * [F] page 17, Receive Length Register (RLR) + */ + if (in_be32(&fifo_ctrl->rdfo) & LL_FIFO_RDFO_MASK) { + length = in_be32(&fifo_ctrl->rlf) & LL_FIFO_RLF_MASK; + } else { + printf("%s: Got error, no receive occupancy\n", + __func__); + return -1; + } + + if (length > PKTSIZE_ALIGN) { + printf("%s: Got error, receive package too big (%i)\n", + __func__, length); + ll_temac_reset_fifo(dev); + return -1; + } + + for (i = 0; i < length; i += 4) + *buf++ = in_be32(&fifo_ctrl->rdfd); + + NetReceive(NetRxPackets[0], length); + } + + return 0; +} + +int ll_temac_send_fifo(struct eth_device *dev, void *packet, int length) +{ + int i; + u32 *buf = (u32 *)packet; + struct ll_temac *ll_temac = dev->priv; + struct fifo_ctrl *fifo_ctrl = (void *)ll_temac->ctrladdr; + + if (length < LL_FIFO_TLF_MIN) { + printf("%s: Got error, transmit package too small (%i)\n", + __func__, length); + return -1; + } + + if (length > LL_FIFO_TLF_MAX) { + printf("%s: Got error, transmit package too big (%i)\n", + __func__, length); + return -1; + } + + for (i = 0; i < length; i += 4) + out_be32(&fifo_ctrl->tdfd, *buf++); + + /* + * Once the packet length is written to the TLR it is + * automatically moved to the transmit data FIFO with + * the packet data freeing up the TLR for another value. + * The packet length must be written to the TLR after + * the packet data is written to the transmit data FIFO. + * It is not valid to write data for multiple packets + * to the transmit data FIFO before writing the packet + * length values. + * + * [F] page 17, Transmit Length Register (TLR) + */ + out_be32(&fifo_ctrl->tlf, length); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_fifo.h b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_fifo.h new file mode 100644 index 000000000..c1bf7cc64 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_fifo.h @@ -0,0 +1,118 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * FIFO sub-controller interface + * + * Copyright (C) 2011 - 2012 Stephan Linz + * Copyright (C) 2008 - 2011 Michal Simek + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ +#ifndef _XILINX_LL_TEMAC_FIFO_ +#define _XILINX_LL_TEMAC_FIFO_ + +#include + +#include +#include + +#if !defined(__BIG_ENDIAN) +# error LL_TEMAC requires big endianess +#endif + +/* + * FIFO Register Definition + * + * Used for memory mapped access from and to (Rd/Td) the LocalLink (LL) + * Tri-Mode Ether MAC (TEMAC) via the 2 kb full duplex FIFO Controller, + * one for each. + * + * [1]: [0]/ip_documentation/xps_ll_fifo.pdf + * page 10, Registers Definition + */ +struct fifo_ctrl { + u32 isr; /* Interrupt Status Register (RW) */ + u32 ier; /* Interrupt Enable Register (RW) */ + u32 tdfr; /* Transmit Data FIFO Reset (WO) */ + u32 tdfv; /* Transmit Data FIFO Vacancy (RO) */ + u32 tdfd; /* Transmit Data FIFO 32bit wide Data write port (WO) */ + u32 tlf; /* Transmit Length FIFO (WO) */ + u32 rdfr; /* Receive Data FIFO Reset (WO) */ + u32 rdfo; /* Receive Data FIFO Occupancy (RO) */ + u32 rdfd; /* Receive Data FIFO 32bit wide Data read port (RO) */ + u32 rlf; /* Receive Length FIFO (RO) */ + u32 llr; /* LocalLink Reset (WO) */ +}; + +/* Interrupt Status Register (ISR), [1] p11 */ +#define LL_FIFO_ISR_RPURE (1 << 31) /* Receive Packet Underrun Read Err */ +#define LL_FIFO_ISR_RPORE (1 << 30) /* Receive Packet Overrun Read Err */ +#define LL_FIFO_ISR_RPUE (1 << 29) /* Receive Packet Underrun Error */ +#define LL_FIFO_ISR_TPOE (1 << 28) /* Transmit Packet Overrun Error */ +#define LL_FIFO_ISR_TC (1 << 27) /* Transmit Complete */ +#define LL_FIFO_ISR_RC (1 << 26) /* Receive Complete */ +#define LL_FIFO_ISR_TSE (1 << 25) /* Transmit Size Error */ +#define LL_FIFO_ISR_TRC (1 << 24) /* Transmit Reset Complete */ +#define LL_FIFO_ISR_RRC (1 << 23) /* Receive Reset Complete */ + +/* Interrupt Enable Register (IER), [1] p12/p13 */ +#define LL_FIFO_IER_RPURE (1 << 31) /* Receive Packet Underrun Read Err */ +#define LL_FIFO_IER_RPORE (1 << 30) /* Receive Packet Overrun Read Err */ +#define LL_FIFO_IER_RPUE (1 << 29) /* Receive Packet Underrun Error */ +#define LL_FIFO_IER_TPOE (1 << 28) /* Transmit Packet Overrun Error */ +#define LL_FIFO_IER_TC (1 << 27) /* Transmit Complete */ +#define LL_FIFO_IER_RC (1 << 26) /* Receive Complete */ +#define LL_FIFO_IER_TSE (1 << 25) /* Transmit Size Error */ +#define LL_FIFO_IER_TRC (1 << 24) /* Transmit Reset Complete */ +#define LL_FIFO_IER_RRC (1 << 23) /* Receive Reset Complete */ + +/* Transmit Data FIFO Reset (TDFR), [1] p13/p14 */ +#define LL_FIFO_TDFR_KEY 0x000000A5UL + +/* Transmit Data FIFO Vacancy (TDFV), [1] p14 */ +#define LL_FIFO_TDFV_POS 0 +#define LL_FIFO_TDFV_MASK (0x000001FFUL << LL_FIFO_TDFV_POS) + +/* Transmit Length FIFO (TLF), [1] p16/p17 */ +#define LL_FIFO_TLF_POS 0 +#define LL_FIFO_TLF_MASK (0x000007FFUL << LL_FIFO_TLF_POS) +#define LL_FIFO_TLF_MIN ((4 * sizeof(u32)) & LL_FIFO_TLF_MASK) +#define LL_FIFO_TLF_MAX ((510 * sizeof(u32)) & LL_FIFO_TLF_MASK) + +/* Receive Data FIFO Reset (RDFR), [1] p15 */ +#define LL_FIFO_RDFR_KEY 0x000000A5UL + +/* Receive Data FIFO Occupancy (RDFO), [1] p16 */ +#define LL_FIFO_RDFO_POS 0 +#define LL_FIFO_RDFO_MASK (0x000001FFUL << LL_FIFO_RDFO_POS) + +/* Receive Length FIFO (RLF), [1] p17/p18 */ +#define LL_FIFO_RLF_POS 0 +#define LL_FIFO_RLF_MASK (0x000007FFUL << LL_FIFO_RLF_POS) +#define LL_FIFO_RLF_MIN ((4 * sizeof(uint32)) & LL_FIFO_RLF_MASK) +#define LL_FIFO_RLF_MAX ((510 * sizeof(uint32)) & LL_FIFO_RLF_MASK) + +/* LocalLink Reset (LLR), [1] p18 */ +#define LL_FIFO_LLR_KEY 0x000000A5UL + + +/* reset FIFO and IRQ, disable interrupts */ +int ll_temac_reset_fifo(struct eth_device *dev); + +/* receive buffered data from FIFO (polling ISR) */ +int ll_temac_recv_fifo(struct eth_device *dev); + +/* send buffered data to FIFO */ +int ll_temac_send_fifo(struct eth_device *dev, void *packet, int length); + +#endif /* _XILINX_LL_TEMAC_FIFO_ */ diff --git a/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_mdio.c b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_mdio.c new file mode 100644 index 000000000..b7bab794e --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_mdio.c @@ -0,0 +1,177 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * MDIO bus access + * + * Copyright (C) 2011 - 2012 Stephan Linz + * Copyright (C) 2008 - 2011 Michal Simek + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * CREDITS: tsec driver + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ + +#include +#include +#include +#include +#include +#include + +#include "xilinx_ll_temac.h" +#include "xilinx_ll_temac_mdio.h" + +#if !defined(CONFIG_MII) +# error "LL_TEMAC requires MII -- missing CONFIG_MII" +#endif + +#if !defined(CONFIG_PHYLIB) +# error "LL_TEMAC requires PHYLIB -- missing CONFIG_PHYLIB" +#endif + +/* + * Prior to PHY access, the MDIO clock must be setup. This driver will set a + * safe default that should work with PLB bus speeds of up to 150 MHz and keep + * the MDIO clock below 2.5 MHz. If the user wishes faster access to the PHY + * then the clock divisor can be set to a different value by setting the + * correct bus speed value with CONFIG_XILINX_LL_TEMAC_CLK. + */ +#if !defined(CONFIG_XILINX_LL_TEMAC_CLK) +#define MDIO_CLOCK_DIV MC_CLKDIV_10(150000000) +#else +#define MDIO_CLOCK_DIV MC_CLKDIV_25(CONFIG_XILINX_LL_TEMAC_CLK) +#endif + +static int ll_temac_mdio_setup(struct mii_dev *bus) +{ + struct temac_reg *regs = (struct temac_reg *)bus->priv; + + /* setup MDIO clock */ + ll_temac_indirect_set(regs, TEMAC_MC, + MC_MDIOEN | (MDIO_CLOCK_DIV & MC_CLKDIV_MASK)); + + return 0; +} + +/* + * Indirect MII PHY read via ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 67, Using the MII Management to Access PHY Registers + */ +int ll_temac_local_mdio_read(struct temac_reg *regs, int addr, int devad, + int regnum) +{ + out_be32(®s->lsw, + ((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) | + (regnum & LSW_REGAD_MASK)); + out_be32(®s->ctl, TEMAC_MIIMAI); + + ll_temac_check_status(regs, RSE_MIIM_RR); + + return in_be32(®s->lsw) & LSW_REGDAT_MASK; +} + +/* + * Indirect MII PHY write via ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 67, Using the MII Management to Access PHY Registers + */ +void ll_temac_local_mdio_write(struct temac_reg *regs, int addr, int devad, + int regnum, u16 value) +{ + out_be32(®s->lsw, (value & LSW_REGDAT_MASK)); + out_be32(®s->ctl, CTL_WEN | TEMAC_MIIMWD); + + out_be32(®s->lsw, + ((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) | + (regnum & LSW_REGAD_MASK)); + out_be32(®s->ctl, CTL_WEN | TEMAC_MIIMAI); + + ll_temac_check_status(regs, RSE_MIIM_WR); +} + +int ll_temac_phy_read(struct mii_dev *bus, int addr, int devad, int regnum) +{ + struct temac_reg *regs = (struct temac_reg *)bus->priv; + + return ll_temac_local_mdio_read(regs, addr, devad, regnum); +} + +int ll_temac_phy_write(struct mii_dev *bus, int addr, int devad, int regnum, + u16 value) +{ + struct temac_reg *regs = (struct temac_reg *)bus->priv; + + ll_temac_local_mdio_write(regs, addr, devad, regnum, value); + + return 0; +} + +/* + * Use MII register 1 (MII status register) to detect PHY + * + * A Mask used to verify certain PHY features (register content) + * in the PHY detection register: + * Auto-negotiation support, 10Mbps half/full duplex support + */ +#define PHY_DETECT_REG MII_BMSR +#define PHY_DETECT_MASK (BMSR_10FULL | BMSR_10HALF | BMSR_ANEGCAPABLE) + +/* Looking for a valid PHY address */ +int ll_temac_phy_addr(struct mii_dev *bus) +{ + struct temac_reg *regs = (struct temac_reg *)bus->priv; + unsigned short val; + unsigned int phy; + + for (phy = PHY_MAX_ADDR; phy >= 0; phy--) { + val = ll_temac_local_mdio_read(regs, phy, 0, PHY_DETECT_REG); + if ((val != 0xFFFF) && + ((val & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { + /* Found a valid PHY address */ + return phy; + } + } + + return -1; +} + +int xilinx_ll_temac_mdio_initialize(bd_t *bis, struct ll_temac_mdio_info *info) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) { + printf("Failed to allocate LL_TEMAC MDIO bus: %s\n", + info->name); + return -1; + } + + bus->read = ll_temac_phy_read; + bus->write = ll_temac_phy_write; + bus->reset = NULL; + + /* use given name or generate its own unique name */ + if (info->name) { + strncpy(bus->name, info->name, MDIO_NAME_LEN); + } else { + snprintf(bus->name, MDIO_NAME_LEN, "lltemii.%p", info->regs); + info->name = bus->name; + } + + bus->priv = info->regs; + + ll_temac_mdio_setup(bus); + return mdio_register(bus); +} diff --git a/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_mdio.h b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_mdio.h new file mode 100644 index 000000000..0603c6445 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_mdio.h @@ -0,0 +1,50 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * MDIO bus access interface + * + * Copyright (C) 2011 - 2012 Stephan Linz + * Copyright (C) 2008 - 2011 Michal Simek + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ +#ifndef _XILINX_LL_TEMAC_MDIO_ +#define _XILINX_LL_TEMAC_MDIO_ + +#include +#include + +#include +#include + +#include "xilinx_ll_temac.h" + +int ll_temac_local_mdio_read(struct temac_reg *regs, int addr, int devad, + int regnum); +void ll_temac_local_mdio_write(struct temac_reg *regs, int addr, int devad, + int regnum, u16 value); + +int ll_temac_phy_read(struct mii_dev *bus, int addr, int devad, int regnum); +int ll_temac_phy_write(struct mii_dev *bus, int addr, int devad, int regnum, + u16 value); + +int ll_temac_phy_addr(struct mii_dev *bus); + +struct ll_temac_mdio_info { + struct temac_reg *regs; + char *name; +}; + +int xilinx_ll_temac_mdio_initialize(bd_t *bis, struct ll_temac_mdio_info *info); + +#endif /* _XILINX_LL_TEMAC_MDIO_ */ diff --git a/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_sdma.c b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_sdma.c new file mode 100644 index 000000000..32a822eea --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_sdma.c @@ -0,0 +1,366 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * SDMA sub-controller + * + * Copyright (C) 2011 - 2012 Stephan Linz + * Copyright (C) 2008 - 2011 Michal Simek + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * CREDITS: tsec driver + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [M]: [0]/ip_documentation/mpmc.pdf + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ + +#include +#include +#include + +#include +#include + +#include "xilinx_ll_temac.h" +#include "xilinx_ll_temac_sdma.h" + +#define TX_BUF_CNT 2 + +static unsigned int rx_idx; /* index of the current RX buffer */ +static unsigned int tx_idx; /* index of the current TX buffer */ + +struct rtx_cdmac_bd { + struct cdmac_bd rx[PKTBUFSRX]; + struct cdmac_bd tx[TX_BUF_CNT]; +}; + +/* + * DMA Buffer Descriptor alignment + * + * If the address contained in the Next Descriptor Pointer register is not + * 8-word aligned or reaches beyond the range of available memory, the SDMA + * halts processing and sets the CDMAC_BD_STCTRL_ERROR bit in the respective + * status register (tx_chnl_sts or rx_chnl_sts). + * + * [1]: [0]/ip_documentation/mpmc.pdf + * page 161, Next Descriptor Pointer + */ +static struct rtx_cdmac_bd cdmac_bd __aligned(32); + +#if defined(CONFIG_XILINX_440) || defined(CONFIG_XILINX_405) + +/* + * Indirect DCR access operations mi{ft}dcr_xilinx() espacialy + * for Xilinx PowerPC implementations on FPGA. + * + * FIXME: This part should go up to arch/powerpc -- but where? + */ +#include +#define XILINX_INDIRECT_DCR_ADDRESS_REG 0 +#define XILINX_INDIRECT_DCR_ACCESS_REG 1 +inline unsigned mifdcr_xilinx(const unsigned dcrn) +{ + mtdcr(XILINX_INDIRECT_DCR_ADDRESS_REG, dcrn); + return mfdcr(XILINX_INDIRECT_DCR_ACCESS_REG); +} +inline void mitdcr_xilinx(const unsigned dcrn, int val) +{ + mtdcr(XILINX_INDIRECT_DCR_ADDRESS_REG, dcrn); + mtdcr(XILINX_INDIRECT_DCR_ACCESS_REG, val); +} + +/* Xilinx Device Control Register (DCR) in/out accessors */ +inline unsigned ll_temac_xldcr_in32(phys_addr_t addr) +{ + return mifdcr_xilinx((const unsigned)addr); +} +inline void ll_temac_xldcr_out32(phys_addr_t addr, unsigned value) +{ + mitdcr_xilinx((const unsigned)addr, value); +} + +void ll_temac_collect_xldcr_sdma_reg_addr(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + phys_addr_t dmac_ctrl = ll_temac->ctrladdr; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + ra[TX_NXTDESC_PTR] = dmac_ctrl + TX_NXTDESC_PTR; + ra[TX_CURBUF_ADDR] = dmac_ctrl + TX_CURBUF_ADDR; + ra[TX_CURBUF_LENGTH] = dmac_ctrl + TX_CURBUF_LENGTH; + ra[TX_CURDESC_PTR] = dmac_ctrl + TX_CURDESC_PTR; + ra[TX_TAILDESC_PTR] = dmac_ctrl + TX_TAILDESC_PTR; + ra[TX_CHNL_CTRL] = dmac_ctrl + TX_CHNL_CTRL; + ra[TX_IRQ_REG] = dmac_ctrl + TX_IRQ_REG; + ra[TX_CHNL_STS] = dmac_ctrl + TX_CHNL_STS; + ra[RX_NXTDESC_PTR] = dmac_ctrl + RX_NXTDESC_PTR; + ra[RX_CURBUF_ADDR] = dmac_ctrl + RX_CURBUF_ADDR; + ra[RX_CURBUF_LENGTH] = dmac_ctrl + RX_CURBUF_LENGTH; + ra[RX_CURDESC_PTR] = dmac_ctrl + RX_CURDESC_PTR; + ra[RX_TAILDESC_PTR] = dmac_ctrl + RX_TAILDESC_PTR; + ra[RX_CHNL_CTRL] = dmac_ctrl + RX_CHNL_CTRL; + ra[RX_IRQ_REG] = dmac_ctrl + RX_IRQ_REG; + ra[RX_CHNL_STS] = dmac_ctrl + RX_CHNL_STS; + ra[DMA_CONTROL_REG] = dmac_ctrl + DMA_CONTROL_REG; +} + +#endif /* CONFIG_XILINX_440 || ONFIG_XILINX_405 */ + +/* Xilinx Processor Local Bus (PLB) in/out accessors */ +inline unsigned ll_temac_xlplb_in32(phys_addr_t addr) +{ + return in_be32((void *)addr); +} +inline void ll_temac_xlplb_out32(phys_addr_t addr, unsigned value) +{ + out_be32((void *)addr, value); +} + +/* collect all register addresses for Xilinx PLB in/out accessors */ +void ll_temac_collect_xlplb_sdma_reg_addr(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct sdma_ctrl *sdma_ctrl = (void *)ll_temac->ctrladdr; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + ra[TX_NXTDESC_PTR] = (phys_addr_t)&sdma_ctrl->tx_nxtdesc_ptr; + ra[TX_CURBUF_ADDR] = (phys_addr_t)&sdma_ctrl->tx_curbuf_addr; + ra[TX_CURBUF_LENGTH] = (phys_addr_t)&sdma_ctrl->tx_curbuf_length; + ra[TX_CURDESC_PTR] = (phys_addr_t)&sdma_ctrl->tx_curdesc_ptr; + ra[TX_TAILDESC_PTR] = (phys_addr_t)&sdma_ctrl->tx_taildesc_ptr; + ra[TX_CHNL_CTRL] = (phys_addr_t)&sdma_ctrl->tx_chnl_ctrl; + ra[TX_IRQ_REG] = (phys_addr_t)&sdma_ctrl->tx_irq_reg; + ra[TX_CHNL_STS] = (phys_addr_t)&sdma_ctrl->tx_chnl_sts; + ra[RX_NXTDESC_PTR] = (phys_addr_t)&sdma_ctrl->rx_nxtdesc_ptr; + ra[RX_CURBUF_ADDR] = (phys_addr_t)&sdma_ctrl->rx_curbuf_addr; + ra[RX_CURBUF_LENGTH] = (phys_addr_t)&sdma_ctrl->rx_curbuf_length; + ra[RX_CURDESC_PTR] = (phys_addr_t)&sdma_ctrl->rx_curdesc_ptr; + ra[RX_TAILDESC_PTR] = (phys_addr_t)&sdma_ctrl->rx_taildesc_ptr; + ra[RX_CHNL_CTRL] = (phys_addr_t)&sdma_ctrl->rx_chnl_ctrl; + ra[RX_IRQ_REG] = (phys_addr_t)&sdma_ctrl->rx_irq_reg; + ra[RX_CHNL_STS] = (phys_addr_t)&sdma_ctrl->rx_chnl_sts; + ra[DMA_CONTROL_REG] = (phys_addr_t)&sdma_ctrl->dma_control_reg; +} + +/* Check for TX and RX channel errors. */ +static inline int ll_temac_sdma_error(struct eth_device *dev) +{ + int err; + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + err = ll_temac->in32(ra[TX_CHNL_STS]) & CHNL_STS_ERROR; + err |= ll_temac->in32(ra[RX_CHNL_STS]) & CHNL_STS_ERROR; + + return err; +} + +int ll_temac_init_sdma(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct cdmac_bd *rx_dp; + struct cdmac_bd *tx_dp; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + int i; + + printf("%s: SDMA: %d Rx buffers, %d Tx buffers\n", + dev->name, PKTBUFSRX, TX_BUF_CNT); + + /* Initialize the Rx Buffer descriptors */ + for (i = 0; i < PKTBUFSRX; i++) { + rx_dp = &cdmac_bd.rx[i]; + memset(rx_dp, 0, sizeof(*rx_dp)); + rx_dp->next_p = rx_dp; + rx_dp->buf_len = PKTSIZE_ALIGN; + rx_dp->phys_buf_p = (u8 *)NetRxPackets[i]; + flush_cache((u32)rx_dp->phys_buf_p, PKTSIZE_ALIGN); + } + flush_cache((u32)cdmac_bd.rx, sizeof(cdmac_bd.rx)); + + /* Initialize the TX Buffer Descriptors */ + for (i = 0; i < TX_BUF_CNT; i++) { + tx_dp = &cdmac_bd.tx[i]; + memset(tx_dp, 0, sizeof(*tx_dp)); + tx_dp->next_p = tx_dp; + } + flush_cache((u32)cdmac_bd.tx, sizeof(cdmac_bd.tx)); + + /* Reset index counter to the Rx and Tx Buffer descriptors */ + rx_idx = tx_idx = 0; + + /* initial Rx DMA start by writing to respective TAILDESC_PTR */ + ll_temac->out32(ra[RX_CURDESC_PTR], (int)&cdmac_bd.rx[rx_idx]); + ll_temac->out32(ra[RX_TAILDESC_PTR], (int)&cdmac_bd.rx[rx_idx]); + + return 0; +} + +int ll_temac_halt_sdma(struct eth_device *dev) +{ + unsigned timeout = 50; /* 1usec * 50 = 50usec */ + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + /* + * Soft reset the DMA + * + * Quote from MPMC documentation: Writing a 1 to this field + * forces the DMA engine to shutdown and reset itself. After + * setting this bit, software must poll it until the bit is + * cleared by the DMA. This indicates that the reset process + * is done and the pipeline has been flushed. + */ + ll_temac->out32(ra[DMA_CONTROL_REG], DMA_CONTROL_RESET); + while (timeout && (ll_temac->in32(ra[DMA_CONTROL_REG]) + & DMA_CONTROL_RESET)) { + timeout--; + udelay(1); + } + + if (!timeout) { + printf("%s: Timeout\n", __func__); + return -1; + } + + return 0; +} + +int ll_temac_reset_sdma(struct eth_device *dev) +{ + u32 r; + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + /* Soft reset the DMA. */ + if (ll_temac_halt_sdma(dev)) + return -1; + + /* Now clear the interrupts. */ + r = ll_temac->in32(ra[TX_CHNL_CTRL]); + r &= ~CHNL_CTRL_IRQ_MASK; + ll_temac->out32(ra[TX_CHNL_CTRL], r); + + r = ll_temac->in32(ra[RX_CHNL_CTRL]); + r &= ~CHNL_CTRL_IRQ_MASK; + ll_temac->out32(ra[RX_CHNL_CTRL], r); + + /* Now ACK pending IRQs. */ + ll_temac->out32(ra[TX_IRQ_REG], IRQ_REG_IRQ_MASK); + ll_temac->out32(ra[RX_IRQ_REG], IRQ_REG_IRQ_MASK); + + /* Set tail-ptr mode, disable errors for both channels. */ + ll_temac->out32(ra[DMA_CONTROL_REG], + /* Enable use of tail pointer register */ + DMA_CONTROL_TPE | + /* Disable error when 2 or 4 bit coalesce cnt overfl */ + DMA_CONTROL_RXOCEID | + /* Disable error when 2 or 4 bit coalesce cnt overfl */ + DMA_CONTROL_TXOCEID); + + return 0; +} + +int ll_temac_recv_sdma(struct eth_device *dev) +{ + int length, pb_idx; + struct cdmac_bd *rx_dp = &cdmac_bd.rx[rx_idx]; + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + if (ll_temac_sdma_error(dev)) { + + if (ll_temac_reset_sdma(dev)) + return -1; + + ll_temac_init_sdma(dev); + } + + flush_cache((u32)rx_dp, sizeof(*rx_dp)); + + if (!(rx_dp->sca.stctrl & CDMAC_BD_STCTRL_COMPLETED)) + return 0; + + if (rx_dp->sca.stctrl & (CDMAC_BD_STCTRL_SOP | CDMAC_BD_STCTRL_EOP)) { + pb_idx = rx_idx; + length = rx_dp->sca.app[4] & CDMAC_BD_APP4_RXBYTECNT_MASK; + } else { + pb_idx = -1; + length = 0; + printf("%s: Got part of package, unsupported (%x)\n", + __func__, rx_dp->sca.stctrl); + } + + /* flip the buffer */ + flush_cache((u32)rx_dp->phys_buf_p, length); + + /* reset the current descriptor */ + rx_dp->sca.stctrl = 0; + rx_dp->sca.app[4] = 0; + flush_cache((u32)rx_dp, sizeof(*rx_dp)); + + /* Find next empty buffer descriptor, preparation for next iteration */ + rx_idx = (rx_idx + 1) % PKTBUFSRX; + rx_dp = &cdmac_bd.rx[rx_idx]; + flush_cache((u32)rx_dp, sizeof(*rx_dp)); + + /* DMA start by writing to respective TAILDESC_PTR */ + ll_temac->out32(ra[RX_CURDESC_PTR], (int)&cdmac_bd.rx[rx_idx]); + ll_temac->out32(ra[RX_TAILDESC_PTR], (int)&cdmac_bd.rx[rx_idx]); + + if (length > 0 && pb_idx != -1) + NetReceive(NetRxPackets[pb_idx], length); + + return 0; +} + +int ll_temac_send_sdma(struct eth_device *dev, void *packet, int length) +{ + unsigned timeout = 50; /* 1usec * 50 = 50usec */ + struct cdmac_bd *tx_dp = &cdmac_bd.tx[tx_idx]; + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + if (ll_temac_sdma_error(dev)) { + + if (ll_temac_reset_sdma(dev)) + return -1; + + ll_temac_init_sdma(dev); + } + + tx_dp->phys_buf_p = (u8 *)packet; + tx_dp->buf_len = length; + tx_dp->sca.stctrl = CDMAC_BD_STCTRL_SOP | CDMAC_BD_STCTRL_EOP | + CDMAC_BD_STCTRL_STOP_ON_END; + + flush_cache((u32)packet, length); + flush_cache((u32)tx_dp, sizeof(*tx_dp)); + + /* DMA start by writing to respective TAILDESC_PTR */ + ll_temac->out32(ra[TX_CURDESC_PTR], (int)tx_dp); + ll_temac->out32(ra[TX_TAILDESC_PTR], (int)tx_dp); + + /* Find next empty buffer descriptor, preparation for next iteration */ + tx_idx = (tx_idx + 1) % TX_BUF_CNT; + tx_dp = &cdmac_bd.tx[tx_idx]; + + do { + flush_cache((u32)tx_dp, sizeof(*tx_dp)); + udelay(1); + } while (timeout-- && !(tx_dp->sca.stctrl & CDMAC_BD_STCTRL_COMPLETED)); + + if (!timeout) { + printf("%s: Timeout\n", __func__); + return -1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_sdma.h b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_sdma.h new file mode 100644 index 000000000..41659c0ee --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/xilinx_ll_temac_sdma.h @@ -0,0 +1,277 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * SDMA sub-controller interface + * + * Copyright (C) 2011 - 2012 Stephan Linz + * Copyright (C) 2008 - 2011 Michal Simek + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ +#ifndef _XILINX_LL_TEMAC_SDMA_ +#define _XILINX_LL_TEMAC_SDMA_ + +#include + +#include +#include + +#include + +#if !defined(__BIG_ENDIAN) +# error LL_TEMAC requires big endianess +#endif + +/* + * DMA Buffer Descriptor for CDMAC + * + * Used for data connection from and to (Rx/Tx) the LocalLink (LL) TEMAC via + * the Communications Direct Memory Access Controller (CDMAC) -- one for each. + * + * overview: + * ftp://ftp.xilinx.com/pub/documentation/misc/mpmc_getting_started.pdf + * + * [1]: [0]/ip_documentation/mpmc.pdf + * page 140, DMA Operation Descriptors + * + * [2]: [0]/user_guides/ug200.pdf + * page 229, DMA Controller -- Descriptor Format + * + * [3]: [0]/ip_documentation/xps_ll_temac.pdf + * page 72, Transmit LocalLink Frame Format + * page 73, Receive LocalLink Frame Format + */ +struct cdmac_bd { + struct cdmac_bd *next_p; /* Next Descriptor Pointer */ + u8 *phys_buf_p; /* Buffer Address */ + u32 buf_len; /* Buffer Length */ + union { + u8 stctrl; /* Status/Control the DMA transfer */ + u32 app[5]; /* application specific data */ + } __packed __aligned(1) sca; +}; + +/* CDMAC Descriptor Status and Control (stctrl), [1] p140, [2] p230 */ +#define CDMAC_BD_STCTRL_ERROR (1 << 7) +#define CDMAC_BD_STCTRL_IRQ_ON_END (1 << 6) +#define CDMAC_BD_STCTRL_STOP_ON_END (1 << 5) +#define CDMAC_BD_STCTRL_COMPLETED (1 << 4) +#define CDMAC_BD_STCTRL_SOP (1 << 3) +#define CDMAC_BD_STCTRL_EOP (1 << 2) +#define CDMAC_BD_STCTRL_DMACHBUSY (1 << 1) + +/* CDMAC Descriptor APP0: Transmit LocalLink Footer Word 3, [3] p72 */ +#define CDMAC_BD_APP0_TXCSCNTRL (1 << 0) + +/* CDMAC Descriptor APP1: Transmit LocalLink Footer Word 4, [3] p73 */ +#define CDMAC_BD_APP1_TXCSBEGIN_POS 16 +#define CDMAC_BD_APP1_TXCSBEGIN_MASK (0xFFFF << CDMAC_BD_APP1_TXCSBEGIN_POS) +#define CDMAC_BD_APP1_TXCSINSERT_POS 0 +#define CDMAC_BD_APP1_TXCSINSERT_MASK (0xFFFF << CDMAC_BD_APP1_TXCSINSERT_POS) + +/* CDMAC Descriptor APP2: Transmit LocalLink Footer Word 5, [3] p73 */ +#define CDMAC_BD_APP2_TXCSINIT_POS 0 +#define CDMAC_BD_APP2_TXCSINIT_MASK (0xFFFF << CDMAC_BD_APP2_TXCSINIT_POS) + +/* CDMAC Descriptor APP0: Receive LocalLink Footer Word 3, [3] p73 */ +#define CDMAC_BD_APP0_MADDRU_POS 0 +#define CDMAC_BD_APP0_MADDRU_MASK (0xFFFF << CDMAC_BD_APP0_MADDRU_POS) + +/* CDMAC Descriptor APP1: Receive LocalLink Footer Word 4, [3] p74 */ +#define CDMAC_BD_APP1_MADDRL_POS 0 +#define CDMAC_BD_APP1_MADDRL_MASK (~0UL << CDMAC_BD_APP1_MADDRL_POS) + +/* CDMAC Descriptor APP2: Receive LocalLink Footer Word 5, [3] p74 */ +#define CDMAC_BD_APP2_BCAST_FRAME (1 << 2) +#define CDMAC_BD_APP2_IPC_MCAST_FRAME (1 << 1) +#define CDMAC_BD_APP2_MAC_MCAST_FRAME (1 << 0) + +/* CDMAC Descriptor APP3: Receive LocalLink Footer Word 6, [3] p74 */ +#define CDMAC_BD_APP3_TLTPID_POS 16 +#define CDMAC_BD_APP3_TLTPID_MASK (0xFFFF << CDMAC_BD_APP3_TLTPID_POS) +#define CDMAC_BD_APP3_RXCSRAW_POS 0 +#define CDMAC_BD_APP3_RXCSRAW_MASK (0xFFFF << CDMAC_BD_APP3_RXCSRAW_POS) + +/* CDMAC Descriptor APP4: Receive LocalLink Footer Word 7, [3] p74 */ +#define CDMAC_BD_APP4_VLANTAG_POS 16 +#define CDMAC_BD_APP4_VLANTAG_MASK (0xFFFF << CDMAC_BD_APP4_VLANTAG_POS) +#define CDMAC_BD_APP4_RXBYTECNT_POS 0 +#define CDMAC_BD_APP4_RXBYTECNT_MASK (0x3FFF << CDMAC_BD_APP4_RXBYTECNT_POS) + +/* + * SDMA Register Definition + * + * [0]: http://www.xilinx.com/support/documentation + * + * [1]: [0]/ip_documentation/mpmc.pdf + * page 54, SDMA Register Summary + * page 160, SDMA Registers + * + * [2]: [0]/user_guides/ug200.pdf + * page 244, DMA Controller -- Programming Interface and Registers + */ +#define SDMA_CTRL_REGTYPE u32 +#define SDMA_CTRL_REGSIZE sizeof(SDMA_CTRL_REGTYPE) +struct sdma_ctrl { + /* Transmit Registers */ + SDMA_CTRL_REGTYPE tx_nxtdesc_ptr; /* TX Next Description Pointer */ + SDMA_CTRL_REGTYPE tx_curbuf_addr; /* TX Current Buffer Address */ + SDMA_CTRL_REGTYPE tx_curbuf_length; /* TX Current Buffer Length */ + SDMA_CTRL_REGTYPE tx_curdesc_ptr; /* TX Current Descriptor Pointer */ + SDMA_CTRL_REGTYPE tx_taildesc_ptr; /* TX Tail Descriptor Pointer */ + SDMA_CTRL_REGTYPE tx_chnl_ctrl; /* TX Channel Control */ + SDMA_CTRL_REGTYPE tx_irq_reg; /* TX Interrupt Register */ + SDMA_CTRL_REGTYPE tx_chnl_sts; /* TX Status Register */ + /* Receive Registers */ + SDMA_CTRL_REGTYPE rx_nxtdesc_ptr; /* RX Next Descriptor Pointer */ + SDMA_CTRL_REGTYPE rx_curbuf_addr; /* RX Current Buffer Address */ + SDMA_CTRL_REGTYPE rx_curbuf_length; /* RX Current Buffer Length */ + SDMA_CTRL_REGTYPE rx_curdesc_ptr; /* RX Current Descriptor Pointer */ + SDMA_CTRL_REGTYPE rx_taildesc_ptr; /* RX Tail Descriptor Pointer */ + SDMA_CTRL_REGTYPE rx_chnl_ctrl; /* RX Channel Control */ + SDMA_CTRL_REGTYPE rx_irq_reg; /* RX Interrupt Register */ + SDMA_CTRL_REGTYPE rx_chnl_sts; /* RX Status Register */ + /* Control Registers */ + SDMA_CTRL_REGTYPE dma_control_reg; /* DMA Control Register */ +}; + +#define SDMA_CTRL_REGNUMS sizeof(struct sdma_ctrl)/SDMA_CTRL_REGSIZE + +/* + * DMAC Register Index Enumeration + * + * [2]: http://www.xilinx.com/support/documentation/user_guides/ug200.pdf + * page 244, DMA Controller -- Programming Interface and Registers + */ +enum dmac_ctrl { + /* Transmit Registers */ + TX_NXTDESC_PTR = 0, /* TX Next Description Pointer */ + TX_CURBUF_ADDR, /* TX Current Buffer Address */ + TX_CURBUF_LENGTH, /* TX Current Buffer Length */ + TX_CURDESC_PTR, /* TX Current Descriptor Pointer */ + TX_TAILDESC_PTR, /* TX Tail Descriptor Pointer */ + TX_CHNL_CTRL, /* TX Channel Control */ + TX_IRQ_REG, /* TX Interrupt Register */ + TX_CHNL_STS, /* TX Status Register */ + /* Receive Registers */ + RX_NXTDESC_PTR, /* RX Next Descriptor Pointer */ + RX_CURBUF_ADDR, /* RX Current Buffer Address */ + RX_CURBUF_LENGTH, /* RX Current Buffer Length */ + RX_CURDESC_PTR, /* RX Current Descriptor Pointer */ + RX_TAILDESC_PTR, /* RX Tail Descriptor Pointer */ + RX_CHNL_CTRL, /* RX Channel Control */ + RX_IRQ_REG, /* RX Interrupt Register */ + RX_CHNL_STS, /* RX Status Register */ + /* Control Registers */ + DMA_CONTROL_REG /* DMA Control Register */ +}; + +/* Rx/Tx Channel Control Register (*_chnl_ctrl), [1] p163, [2] p246/p252 */ +#define CHNL_CTRL_ITO_POS 24 +#define CHNL_CTRL_ITO_MASK (0xFF << CHNL_CTRL_ITO_POS) +#define CHNL_CTRL_IC_POS 16 +#define CHNL_CTRL_IC_MASK (0xFF << CHNL_CTRL_IC_POS) +#define CHNL_CTRL_MSBADDR_POS 12 +#define CHNL_CTRL_MSBADDR_MASK (0xF << CHNL_CTRL_MSBADDR_POS) +#define CHNL_CTRL_AME (1 << 11) +#define CHNL_CTRL_OBWC (1 << 10) +#define CHNL_CTRL_IOE (1 << 9) +#define CHNL_CTRL_LIC (1 << 8) +#define CHNL_CTRL_IE (1 << 7) +#define CHNL_CTRL_IEE (1 << 2) +#define CHNL_CTRL_IDE (1 << 1) +#define CHNL_CTRL_ICE (1 << 0) + +/* All interrupt enable bits */ +#define CHNL_CTRL_IRQ_MASK (CHNL_CTRL_IE | \ + CHNL_CTRL_IEE | \ + CHNL_CTRL_IDE | \ + CHNL_CTRL_ICE) + +/* Rx/Tx Interrupt Status Register (*_irq_reg), [1] p164, [2] p247/p253 */ +#define IRQ_REG_DTV_POS 24 +#define IRQ_REG_DTV_MASK (0xFF << IRQ_REG_DTV_POS) +#define IRQ_REG_CCV_POS 16 +#define IRQ_REG_CCV_MASK (0xFF << IRQ_REG_CCV_POS) +#define IRQ_REG_WRCQ_EMPTY (1 << 14) +#define IRQ_REG_CIC_POS 10 +#define IRQ_REG_CIC_MASK (0xF << IRQ_REG_CIC_POS) +#define IRQ_REG_DIC_POS 8 +#define IRQ_REG_DIC_MASK (3 << 8) +#define IRQ_REG_PLB_RD_NMI (1 << 4) +#define IRQ_REG_PLB_WR_NMI (1 << 3) +#define IRQ_REG_EI (1 << 2) +#define IRQ_REG_DI (1 << 1) +#define IRQ_REG_CI (1 << 0) + +/* All interrupt bits */ +#define IRQ_REG_IRQ_MASK (IRQ_REG_PLB_RD_NMI | \ + IRQ_REG_PLB_WR_NMI | \ + IRQ_REG_EI | IRQ_REG_DI | IRQ_REG_CI) + +/* Rx/Tx Channel Status Register (*_chnl_sts), [1] p165, [2] p249/p255 */ +#define CHNL_STS_ERROR_TAIL (1 << 21) +#define CHNL_STS_ERROR_CMP (1 << 20) +#define CHNL_STS_ERROR_ADDR (1 << 19) +#define CHNL_STS_ERROR_NXTP (1 << 18) +#define CHNL_STS_ERROR_CURP (1 << 17) +#define CHNL_STS_ERROR_BSYWR (1 << 16) +#define CHNL_STS_ERROR (1 << 7) +#define CHNL_STS_IOE (1 << 6) +#define CHNL_STS_SOE (1 << 5) +#define CHNL_STS_CMPLT (1 << 4) +#define CHNL_STS_SOP (1 << 3) +#define CHNL_STS_EOP (1 << 2) +#define CHNL_STS_EBUSY (1 << 1) + +/* DMA Control Register (dma_control_reg), [1] p166, [2] p256 */ +#define DMA_CONTROL_PLBED (1 << 5) +#define DMA_CONTROL_RXOCEID (1 << 4) +#define DMA_CONTROL_TXOCEID (1 << 3) +#define DMA_CONTROL_TPE (1 << 2) +#define DMA_CONTROL_RESET (1 << 0) + +#if defined(CONFIG_XILINX_440) || defined(CONFIG_XILINX_405) + +/* Xilinx Device Control Register (DCR) in/out accessors */ +unsigned ll_temac_xldcr_in32(phys_addr_t addr); +void ll_temac_xldcr_out32(phys_addr_t addr, unsigned value); + +/* collect all register addresses for Xilinx DCR in/out accessors */ +void ll_temac_collect_xldcr_sdma_reg_addr(struct eth_device *dev); + +#endif /* CONFIG_XILINX_440 || CONFIG_XILINX_405 */ + +/* Xilinx Processor Local Bus (PLB) in/out accessors */ +unsigned ll_temac_xlplb_in32(phys_addr_t base); +void ll_temac_xlplb_out32(phys_addr_t base, unsigned value); + +/* collect all register addresses for Xilinx PLB in/out accessors */ +void ll_temac_collect_xlplb_sdma_reg_addr(struct eth_device *dev); + +/* initialize both Rx/Tx buffer descriptors */ +int ll_temac_init_sdma(struct eth_device *dev); + +/* halt both Rx/Tx transfers */ +int ll_temac_halt_sdma(struct eth_device *dev); + +/* reset SDMA and IRQ, disable interrupts and errors */ +int ll_temac_reset_sdma(struct eth_device *dev); + +/* receive buffered data from SDMA (polling ISR) */ +int ll_temac_recv_sdma(struct eth_device *dev); + +/* send buffered data to SDMA */ +int ll_temac_send_sdma(struct eth_device *dev, void *packet, int length); + +#endif /* _XILINX_LL_TEMAC_SDMA_ */ diff --git a/qemu/roms/u-boot/drivers/net/zynq_gem.c b/qemu/roms/u-boot/drivers/net/zynq_gem.c new file mode 100644 index 000000000..3cadd23bb --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/zynq_gem.c @@ -0,0 +1,580 @@ +/* + * (C) Copyright 2011 Michal Simek + * + * Michal SIMEK + * + * Based on Xilinx gmac driver: + * (C) Copyright 2011 Xilinx + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(CONFIG_PHYLIB) +# error XILINX_GEM_ETHERNET requires PHYLIB +#endif + +/* Bit/mask specification */ +#define ZYNQ_GEM_PHYMNTNC_OP_MASK 0x40020000 /* operation mask bits */ +#define ZYNQ_GEM_PHYMNTNC_OP_R_MASK 0x20000000 /* read operation */ +#define ZYNQ_GEM_PHYMNTNC_OP_W_MASK 0x10000000 /* write operation */ +#define ZYNQ_GEM_PHYMNTNC_PHYAD_SHIFT_MASK 23 /* Shift bits for PHYAD */ +#define ZYNQ_GEM_PHYMNTNC_PHREG_SHIFT_MASK 18 /* Shift bits for PHREG */ + +#define ZYNQ_GEM_RXBUF_EOF_MASK 0x00008000 /* End of frame. */ +#define ZYNQ_GEM_RXBUF_SOF_MASK 0x00004000 /* Start of frame. */ +#define ZYNQ_GEM_RXBUF_LEN_MASK 0x00003FFF /* Mask for length field */ + +#define ZYNQ_GEM_RXBUF_WRAP_MASK 0x00000002 /* Wrap bit, last BD */ +#define ZYNQ_GEM_RXBUF_NEW_MASK 0x00000001 /* Used bit.. */ +#define ZYNQ_GEM_RXBUF_ADD_MASK 0xFFFFFFFC /* Mask for address */ + +/* Wrap bit, last descriptor */ +#define ZYNQ_GEM_TXBUF_WRAP_MASK 0x40000000 +#define ZYNQ_GEM_TXBUF_LAST_MASK 0x00008000 /* Last buffer */ + +#define ZYNQ_GEM_NWCTRL_TXEN_MASK 0x00000008 /* Enable transmit */ +#define ZYNQ_GEM_NWCTRL_RXEN_MASK 0x00000004 /* Enable receive */ +#define ZYNQ_GEM_NWCTRL_MDEN_MASK 0x00000010 /* Enable MDIO port */ +#define ZYNQ_GEM_NWCTRL_STARTTX_MASK 0x00000200 /* Start tx (tx_go) */ + +#define ZYNQ_GEM_NWCFG_SPEED100 0x000000001 /* 100 Mbps operation */ +#define ZYNQ_GEM_NWCFG_SPEED1000 0x000000400 /* 1Gbps operation */ +#define ZYNQ_GEM_NWCFG_FDEN 0x000000002 /* Full Duplex mode */ +#define ZYNQ_GEM_NWCFG_FSREM 0x000020000 /* FCS removal */ +#define ZYNQ_GEM_NWCFG_MDCCLKDIV 0x000080000 /* Div pclk by 32, 80MHz */ +#define ZYNQ_GEM_NWCFG_MDCCLKDIV2 0x0000c0000 /* Div pclk by 48, 120MHz */ + +#define ZYNQ_GEM_NWCFG_INIT (ZYNQ_GEM_NWCFG_FDEN | \ + ZYNQ_GEM_NWCFG_FSREM | \ + ZYNQ_GEM_NWCFG_MDCCLKDIV) + +#define ZYNQ_GEM_NWSR_MDIOIDLE_MASK 0x00000004 /* PHY management idle */ + +#define ZYNQ_GEM_DMACR_BLENGTH 0x00000004 /* INCR4 AHB bursts */ +/* Use full configured addressable space (8 Kb) */ +#define ZYNQ_GEM_DMACR_RXSIZE 0x00000300 +/* Use full configured addressable space (4 Kb) */ +#define ZYNQ_GEM_DMACR_TXSIZE 0x00000400 +/* Set with binary 00011000 to use 1536 byte(1*max length frame/buffer) */ +#define ZYNQ_GEM_DMACR_RXBUF 0x00180000 + +#define ZYNQ_GEM_DMACR_INIT (ZYNQ_GEM_DMACR_BLENGTH | \ + ZYNQ_GEM_DMACR_RXSIZE | \ + ZYNQ_GEM_DMACR_TXSIZE | \ + ZYNQ_GEM_DMACR_RXBUF) + +/* Use MII register 1 (MII status register) to detect PHY */ +#define PHY_DETECT_REG 1 + +/* Mask used to verify certain PHY features (or register contents) + * in the register above: + * 0x1000: 10Mbps full duplex support + * 0x0800: 10Mbps half duplex support + * 0x0008: Auto-negotiation support + */ +#define PHY_DETECT_MASK 0x1808 + +/* TX BD status masks */ +#define ZYNQ_GEM_TXBUF_FRMLEN_MASK 0x000007ff +#define ZYNQ_GEM_TXBUF_EXHAUSTED 0x08000000 +#define ZYNQ_GEM_TXBUF_UNDERRUN 0x10000000 + +/* Clock frequencies for different speeds */ +#define ZYNQ_GEM_FREQUENCY_10 2500000UL +#define ZYNQ_GEM_FREQUENCY_100 25000000UL +#define ZYNQ_GEM_FREQUENCY_1000 125000000UL + +/* Device registers */ +struct zynq_gem_regs { + u32 nwctrl; /* Network Control reg */ + u32 nwcfg; /* Network Config reg */ + u32 nwsr; /* Network Status reg */ + u32 reserved1; + u32 dmacr; /* DMA Control reg */ + u32 txsr; /* TX Status reg */ + u32 rxqbase; /* RX Q Base address reg */ + u32 txqbase; /* TX Q Base address reg */ + u32 rxsr; /* RX Status reg */ + u32 reserved2[2]; + u32 idr; /* Interrupt Disable reg */ + u32 reserved3; + u32 phymntnc; /* Phy Maintaince reg */ + u32 reserved4[18]; + u32 hashl; /* Hash Low address reg */ + u32 hashh; /* Hash High address reg */ +#define LADDR_LOW 0 +#define LADDR_HIGH 1 + u32 laddr[4][LADDR_HIGH + 1]; /* Specific1 addr low/high reg */ + u32 match[4]; /* Type ID1 Match reg */ + u32 reserved6[18]; + u32 stat[44]; /* Octects transmitted Low reg - stat start */ +}; + +/* BD descriptors */ +struct emac_bd { + u32 addr; /* Next descriptor pointer */ + u32 status; +}; + +#define RX_BUF 3 +/* Page table entries are set to 1MB, or multiples of 1MB + * (not < 1MB). driver uses less bd's so use 1MB bdspace. + */ +#define BD_SPACE 0x100000 +/* BD separation space */ +#define BD_SEPRN_SPACE 64 + +/* Initialized, rxbd_current, rx_first_buf must be 0 after init */ +struct zynq_gem_priv { + struct emac_bd *tx_bd; + struct emac_bd *rx_bd; + char *rxbuffers; + u32 rxbd_current; + u32 rx_first_buf; + int phyaddr; + u32 emio; + int init; + struct phy_device *phydev; + struct mii_dev *bus; +}; + +static inline int mdio_wait(struct eth_device *dev) +{ + struct zynq_gem_regs *regs = (struct zynq_gem_regs *)dev->iobase; + u32 timeout = 200; + + /* Wait till MDIO interface is ready to accept a new transaction. */ + while (--timeout) { + if (readl(®s->nwsr) & ZYNQ_GEM_NWSR_MDIOIDLE_MASK) + break; + WATCHDOG_RESET(); + } + + if (!timeout) { + printf("%s: Timeout\n", __func__); + return 1; + } + + return 0; +} + +static u32 phy_setup_op(struct eth_device *dev, u32 phy_addr, u32 regnum, + u32 op, u16 *data) +{ + u32 mgtcr; + struct zynq_gem_regs *regs = (struct zynq_gem_regs *)dev->iobase; + + if (mdio_wait(dev)) + return 1; + + /* Construct mgtcr mask for the operation */ + mgtcr = ZYNQ_GEM_PHYMNTNC_OP_MASK | op | + (phy_addr << ZYNQ_GEM_PHYMNTNC_PHYAD_SHIFT_MASK) | + (regnum << ZYNQ_GEM_PHYMNTNC_PHREG_SHIFT_MASK) | *data; + + /* Write mgtcr and wait for completion */ + writel(mgtcr, ®s->phymntnc); + + if (mdio_wait(dev)) + return 1; + + if (op == ZYNQ_GEM_PHYMNTNC_OP_R_MASK) + *data = readl(®s->phymntnc); + + return 0; +} + +static u32 phyread(struct eth_device *dev, u32 phy_addr, u32 regnum, u16 *val) +{ + return phy_setup_op(dev, phy_addr, regnum, + ZYNQ_GEM_PHYMNTNC_OP_R_MASK, val); +} + +static u32 phywrite(struct eth_device *dev, u32 phy_addr, u32 regnum, u16 data) +{ + return phy_setup_op(dev, phy_addr, regnum, + ZYNQ_GEM_PHYMNTNC_OP_W_MASK, &data); +} + +static void phy_detection(struct eth_device *dev) +{ + int i; + u16 phyreg; + struct zynq_gem_priv *priv = dev->priv; + + if (priv->phyaddr != -1) { + phyread(dev, priv->phyaddr, PHY_DETECT_REG, &phyreg); + if ((phyreg != 0xFFFF) && + ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { + /* Found a valid PHY address */ + debug("Default phy address %d is valid\n", + priv->phyaddr); + return; + } else { + debug("PHY address is not setup correctly %d\n", + priv->phyaddr); + priv->phyaddr = -1; + } + } + + debug("detecting phy address\n"); + if (priv->phyaddr == -1) { + /* detect the PHY address */ + for (i = 31; i >= 0; i--) { + phyread(dev, i, PHY_DETECT_REG, &phyreg); + if ((phyreg != 0xFFFF) && + ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { + /* Found a valid PHY address */ + priv->phyaddr = i; + debug("Found valid phy address, %d\n", i); + return; + } + } + } + printf("PHY is not detected\n"); +} + +static int zynq_gem_setup_mac(struct eth_device *dev) +{ + u32 i, macaddrlow, macaddrhigh; + struct zynq_gem_regs *regs = (struct zynq_gem_regs *)dev->iobase; + + /* Set the MAC bits [31:0] in BOT */ + macaddrlow = dev->enetaddr[0]; + macaddrlow |= dev->enetaddr[1] << 8; + macaddrlow |= dev->enetaddr[2] << 16; + macaddrlow |= dev->enetaddr[3] << 24; + + /* Set MAC bits [47:32] in TOP */ + macaddrhigh = dev->enetaddr[4]; + macaddrhigh |= dev->enetaddr[5] << 8; + + for (i = 0; i < 4; i++) { + writel(0, ®s->laddr[i][LADDR_LOW]); + writel(0, ®s->laddr[i][LADDR_HIGH]); + /* Do not use MATCHx register */ + writel(0, ®s->match[i]); + } + + writel(macaddrlow, ®s->laddr[0][LADDR_LOW]); + writel(macaddrhigh, ®s->laddr[0][LADDR_HIGH]); + + return 0; +} + +static int zynq_gem_init(struct eth_device *dev, bd_t * bis) +{ + u32 i; + unsigned long clk_rate = 0; + struct phy_device *phydev; + const u32 stat_size = (sizeof(struct zynq_gem_regs) - + offsetof(struct zynq_gem_regs, stat)) / 4; + struct zynq_gem_regs *regs = (struct zynq_gem_regs *)dev->iobase; + struct zynq_gem_priv *priv = dev->priv; + const u32 supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full; + + if (!priv->init) { + /* Disable all interrupts */ + writel(0xFFFFFFFF, ®s->idr); + + /* Disable the receiver & transmitter */ + writel(0, ®s->nwctrl); + writel(0, ®s->txsr); + writel(0, ®s->rxsr); + writel(0, ®s->phymntnc); + + /* Clear the Hash registers for the mac address + * pointed by AddressPtr + */ + writel(0x0, ®s->hashl); + /* Write bits [63:32] in TOP */ + writel(0x0, ®s->hashh); + + /* Clear all counters */ + for (i = 0; i <= stat_size; i++) + readl(®s->stat[i]); + + /* Setup RxBD space */ + memset(priv->rx_bd, 0, RX_BUF * sizeof(struct emac_bd)); + + for (i = 0; i < RX_BUF; i++) { + priv->rx_bd[i].status = 0xF0000000; + priv->rx_bd[i].addr = + ((u32)(priv->rxbuffers) + + (i * PKTSIZE_ALIGN)); + } + /* WRAP bit to last BD */ + priv->rx_bd[--i].addr |= ZYNQ_GEM_RXBUF_WRAP_MASK; + /* Write RxBDs to IP */ + writel((u32)priv->rx_bd, ®s->rxqbase); + + /* Setup for DMA Configuration register */ + writel(ZYNQ_GEM_DMACR_INIT, ®s->dmacr); + + /* Setup for Network Control register, MDIO, Rx and Tx enable */ + setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_MDEN_MASK); + + priv->init++; + } + + phy_detection(dev); + + /* interface - look at tsec */ + phydev = phy_connect(priv->bus, priv->phyaddr, dev, + PHY_INTERFACE_MODE_MII); + + phydev->supported = supported | ADVERTISED_Pause | + ADVERTISED_Asym_Pause; + phydev->advertising = phydev->supported; + priv->phydev = phydev; + phy_config(phydev); + phy_startup(phydev); + + if (!phydev->link) { + printf("%s: No link.\n", phydev->dev->name); + return -1; + } + + switch (phydev->speed) { + case SPEED_1000: + writel(ZYNQ_GEM_NWCFG_INIT | ZYNQ_GEM_NWCFG_SPEED1000, + ®s->nwcfg); + clk_rate = ZYNQ_GEM_FREQUENCY_1000; + break; + case SPEED_100: + clrsetbits_le32(®s->nwcfg, ZYNQ_GEM_NWCFG_SPEED1000, + ZYNQ_GEM_NWCFG_INIT | ZYNQ_GEM_NWCFG_SPEED100); + clk_rate = ZYNQ_GEM_FREQUENCY_100; + break; + case SPEED_10: + clk_rate = ZYNQ_GEM_FREQUENCY_10; + break; + } + + /* Change the rclk and clk only not using EMIO interface */ + if (!priv->emio) + zynq_slcr_gem_clk_setup(dev->iobase != + ZYNQ_GEM_BASEADDR0, clk_rate); + + setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_RXEN_MASK | + ZYNQ_GEM_NWCTRL_TXEN_MASK); + + return 0; +} + +static int zynq_gem_send(struct eth_device *dev, void *ptr, int len) +{ + u32 addr, size; + struct zynq_gem_priv *priv = dev->priv; + struct zynq_gem_regs *regs = (struct zynq_gem_regs *)dev->iobase; + + /* setup BD */ + writel((u32)priv->tx_bd, ®s->txqbase); + + /* Setup Tx BD */ + memset(priv->tx_bd, 0, sizeof(struct emac_bd)); + + priv->tx_bd->addr = (u32)ptr; + priv->tx_bd->status = (len & ZYNQ_GEM_TXBUF_FRMLEN_MASK) | + ZYNQ_GEM_TXBUF_LAST_MASK; + + addr = (u32) ptr; + addr &= ~(ARCH_DMA_MINALIGN - 1); + size = roundup(len, ARCH_DMA_MINALIGN); + flush_dcache_range(addr, addr + size); + barrier(); + + /* Start transmit */ + setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_STARTTX_MASK); + + /* Read TX BD status */ + if (priv->tx_bd->status & ZYNQ_GEM_TXBUF_UNDERRUN) + printf("TX underrun\n"); + if (priv->tx_bd->status & ZYNQ_GEM_TXBUF_EXHAUSTED) + printf("TX buffers exhausted in mid frame\n"); + + return 0; +} + +/* Do not check frame_recd flag in rx_status register 0x20 - just poll BD */ +static int zynq_gem_recv(struct eth_device *dev) +{ + int frame_len; + struct zynq_gem_priv *priv = dev->priv; + struct emac_bd *current_bd = &priv->rx_bd[priv->rxbd_current]; + struct emac_bd *first_bd; + + if (!(current_bd->addr & ZYNQ_GEM_RXBUF_NEW_MASK)) + return 0; + + if (!(current_bd->status & + (ZYNQ_GEM_RXBUF_SOF_MASK | ZYNQ_GEM_RXBUF_EOF_MASK))) { + printf("GEM: SOF or EOF not set for last buffer received!\n"); + return 0; + } + + frame_len = current_bd->status & ZYNQ_GEM_RXBUF_LEN_MASK; + if (frame_len) { + u32 addr = current_bd->addr & ZYNQ_GEM_RXBUF_ADD_MASK; + addr &= ~(ARCH_DMA_MINALIGN - 1); + u32 size = roundup(frame_len, ARCH_DMA_MINALIGN); + invalidate_dcache_range(addr, addr + size); + + NetReceive((u8 *)addr, frame_len); + + if (current_bd->status & ZYNQ_GEM_RXBUF_SOF_MASK) + priv->rx_first_buf = priv->rxbd_current; + else { + current_bd->addr &= ~ZYNQ_GEM_RXBUF_NEW_MASK; + current_bd->status = 0xF0000000; /* FIXME */ + } + + if (current_bd->status & ZYNQ_GEM_RXBUF_EOF_MASK) { + first_bd = &priv->rx_bd[priv->rx_first_buf]; + first_bd->addr &= ~ZYNQ_GEM_RXBUF_NEW_MASK; + first_bd->status = 0xF0000000; + } + + if ((++priv->rxbd_current) >= RX_BUF) + priv->rxbd_current = 0; + } + + return frame_len; +} + +static void zynq_gem_halt(struct eth_device *dev) +{ + struct zynq_gem_regs *regs = (struct zynq_gem_regs *)dev->iobase; + + clrsetbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_RXEN_MASK | + ZYNQ_GEM_NWCTRL_TXEN_MASK, 0); +} + +static int zynq_gem_miiphyread(const char *devname, uchar addr, + uchar reg, ushort *val) +{ + struct eth_device *dev = eth_get_dev(); + int ret; + + ret = phyread(dev, addr, reg, val); + debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, *val); + return ret; +} + +static int zynq_gem_miiphy_write(const char *devname, uchar addr, + uchar reg, ushort val) +{ + struct eth_device *dev = eth_get_dev(); + + debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, val); + return phywrite(dev, addr, reg, val); +} + +int zynq_gem_initialize(bd_t *bis, int base_addr, int phy_addr, u32 emio) +{ + struct eth_device *dev; + struct zynq_gem_priv *priv; + void *bd_space; + + dev = calloc(1, sizeof(*dev)); + if (dev == NULL) + return -1; + + dev->priv = calloc(1, sizeof(struct zynq_gem_priv)); + if (dev->priv == NULL) { + free(dev); + return -1; + } + priv = dev->priv; + + /* Align rxbuffers to ARCH_DMA_MINALIGN */ + priv->rxbuffers = memalign(ARCH_DMA_MINALIGN, RX_BUF * PKTSIZE_ALIGN); + memset(priv->rxbuffers, 0, RX_BUF * PKTSIZE_ALIGN); + + /* Align bd_space to 1MB */ + bd_space = memalign(1 << MMU_SECTION_SHIFT, BD_SPACE); + mmu_set_region_dcache_behaviour((u32)bd_space, BD_SPACE, DCACHE_OFF); + + /* Initialize the bd spaces for tx and rx bd's */ + priv->tx_bd = (struct emac_bd *)bd_space; + priv->rx_bd = (struct emac_bd *)((u32)bd_space + BD_SEPRN_SPACE); + + priv->phyaddr = phy_addr; + priv->emio = emio; + + sprintf(dev->name, "Gem.%x", base_addr); + + dev->iobase = base_addr; + + dev->init = zynq_gem_init; + dev->halt = zynq_gem_halt; + dev->send = zynq_gem_send; + dev->recv = zynq_gem_recv; + dev->write_hwaddr = zynq_gem_setup_mac; + + eth_register(dev); + + miiphy_register(dev->name, zynq_gem_miiphyread, zynq_gem_miiphy_write); + priv->bus = miiphy_get_dev_by_name(dev->name); + + return 1; +} + +#ifdef CONFIG_OF_CONTROL +int zynq_gem_of_init(const void *blob) +{ + int offset = 0; + u32 ret = 0; + u32 reg, phy_reg; + + debug("ZYNQ GEM: Initialization\n"); + + do { + offset = fdt_node_offset_by_compatible(blob, offset, + "xlnx,ps7-ethernet-1.00.a"); + if (offset != -1) { + reg = fdtdec_get_addr(blob, offset, "reg"); + if (reg != FDT_ADDR_T_NONE) { + offset = fdtdec_lookup_phandle(blob, offset, + "phy-handle"); + if (offset != -1) + phy_reg = fdtdec_get_addr(blob, offset, + "reg"); + else + phy_reg = 0; + + debug("ZYNQ GEM: addr %x, phyaddr %x\n", + reg, phy_reg); + + ret |= zynq_gem_initialize(NULL, reg, + phy_reg, 0); + + } else { + debug("ZYNQ GEM: Can't get base address\n"); + return -1; + } + } + } while (offset != -1); + + return ret; +} +#endif diff --git a/qemu/roms/u-boot/drivers/pci/Makefile b/qemu/roms/u-boot/drivers/pci/Makefile new file mode 100644 index 000000000..e73a49861 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/Makefile @@ -0,0 +1,19 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o +obj-$(CONFIG_PCI) += pci.o pci_auto.o +obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o +obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o +obj-$(CONFIG_PCI_MSC01) += pci_msc01.o +obj-$(CONFIG_PCIE_IMX) += pcie_imx.o +obj-$(CONFIG_FTPCI100) += pci_ftpci100.o +obj-$(CONFIG_SH4_PCI) += pci_sh4.o +obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o +obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o +obj-$(CONFIG_TSI108_PCI) += tsi108_pci.o +obj-$(CONFIG_WINBOND_83C553) += w83c553f.o diff --git a/qemu/roms/u-boot/drivers/pci/fsl_pci_init.c b/qemu/roms/u-boot/drivers/pci/fsl_pci_init.c new file mode 100644 index 000000000..6317fb132 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/fsl_pci_init.c @@ -0,0 +1,882 @@ +/* + * Copyright 2007-2012 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* + * PCI/PCIE Controller initialization for mpc85xx/mpc86xx soc's + * + * Initialize controller and call the common driver/pci pci_hose_scan to + * scan for bridges and devices. + * + * Hose fields which need to be pre-initialized by board specific code: + * regions[] + * first_busno + * + * Fields updated: + * last_busno + */ + +#include +#include +#include + +#ifndef CONFIG_SYS_PCI_MEMORY_BUS +#define CONFIG_SYS_PCI_MEMORY_BUS 0 +#endif + +#ifndef CONFIG_SYS_PCI_MEMORY_PHYS +#define CONFIG_SYS_PCI_MEMORY_PHYS 0 +#endif + +#if defined(CONFIG_SYS_PCI_64BIT) && !defined(CONFIG_SYS_PCI64_MEMORY_BUS) +#define CONFIG_SYS_PCI64_MEMORY_BUS (64ull*1024*1024*1024) +#endif + +/* Setup one inbound ATMU window. + * + * We let the caller decide what the window size should be + */ +static void set_inbound_window(volatile pit_t *pi, + struct pci_region *r, + u64 size) +{ + u32 sz = (__ilog2_u64(size) - 1); + u32 flag = PIWAR_EN | PIWAR_LOCAL | + PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP; + + out_be32(&pi->pitar, r->phys_start >> 12); + out_be32(&pi->piwbar, r->bus_start >> 12); +#ifdef CONFIG_SYS_PCI_64BIT + out_be32(&pi->piwbear, r->bus_start >> 44); +#else + out_be32(&pi->piwbear, 0); +#endif + if (r->flags & PCI_REGION_PREFETCH) + flag |= PIWAR_PF; + out_be32(&pi->piwar, flag | sz); +} + +int fsl_setup_hose(struct pci_controller *hose, unsigned long addr) +{ + volatile ccsr_fsl_pci_t *pci = (ccsr_fsl_pci_t *) addr; + + /* Reset hose to make sure its in a clean state */ + memset(hose, 0, sizeof(struct pci_controller)); + + pci_setup_indirect(hose, (u32)&pci->cfg_addr, (u32)&pci->cfg_data); + + return fsl_is_pci_agent(hose); +} + +static int fsl_pci_setup_inbound_windows(struct pci_controller *hose, + u64 out_lo, u8 pcie_cap, + volatile pit_t *pi) +{ + struct pci_region *r = hose->regions + hose->region_count; + u64 sz = min((u64)gd->ram_size, (1ull << 32)); + + phys_addr_t phys_start = CONFIG_SYS_PCI_MEMORY_PHYS; + pci_addr_t bus_start = CONFIG_SYS_PCI_MEMORY_BUS; + pci_size_t pci_sz; + + /* we have no space available for inbound memory mapping */ + if (bus_start > out_lo) { + printf ("no space for inbound mapping of memory\n"); + return 0; + } + + /* limit size */ + if ((bus_start + sz) > out_lo) { + sz = out_lo - bus_start; + debug ("limiting size to %llx\n", sz); + } + + pci_sz = 1ull << __ilog2_u64(sz); + /* + * we can overlap inbound/outbound windows on PCI-E since RX & TX + * links a separate + */ + if ((pcie_cap == PCI_CAP_ID_EXP) && (pci_sz < sz)) { + debug ("R0 bus_start: %llx phys_start: %llx size: %llx\n", + (u64)bus_start, (u64)phys_start, (u64)sz); + pci_set_region(r, bus_start, phys_start, sz, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | + PCI_REGION_PREFETCH); + + /* if we aren't an exact power of two match, pci_sz is smaller + * round it up to the next power of two. We report the actual + * size to pci region tracking. + */ + if (pci_sz != sz) + sz = 2ull << __ilog2_u64(sz); + + set_inbound_window(pi--, r++, sz); + sz = 0; /* make sure we dont set the R2 window */ + } else { + debug ("R0 bus_start: %llx phys_start: %llx size: %llx\n", + (u64)bus_start, (u64)phys_start, (u64)pci_sz); + pci_set_region(r, bus_start, phys_start, pci_sz, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | + PCI_REGION_PREFETCH); + set_inbound_window(pi--, r++, pci_sz); + + sz -= pci_sz; + bus_start += pci_sz; + phys_start += pci_sz; + + pci_sz = 1ull << __ilog2_u64(sz); + if (sz) { + debug ("R1 bus_start: %llx phys_start: %llx size: %llx\n", + (u64)bus_start, (u64)phys_start, (u64)pci_sz); + pci_set_region(r, bus_start, phys_start, pci_sz, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | + PCI_REGION_PREFETCH); + set_inbound_window(pi--, r++, pci_sz); + sz -= pci_sz; + bus_start += pci_sz; + phys_start += pci_sz; + } + } + +#if defined(CONFIG_PHYS_64BIT) && defined(CONFIG_SYS_PCI_64BIT) + /* + * On 64-bit capable systems, set up a mapping for all of DRAM + * in high pci address space. + */ + pci_sz = 1ull << __ilog2_u64(gd->ram_size); + /* round up to the next largest power of two */ + if (gd->ram_size > pci_sz) + pci_sz = 1ull << (__ilog2_u64(gd->ram_size) + 1); + debug ("R64 bus_start: %llx phys_start: %llx size: %llx\n", + (u64)CONFIG_SYS_PCI64_MEMORY_BUS, + (u64)CONFIG_SYS_PCI_MEMORY_PHYS, + (u64)pci_sz); + pci_set_region(r, + CONFIG_SYS_PCI64_MEMORY_BUS, + CONFIG_SYS_PCI_MEMORY_PHYS, + pci_sz, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | + PCI_REGION_PREFETCH); + set_inbound_window(pi--, r++, pci_sz); +#else + pci_sz = 1ull << __ilog2_u64(sz); + if (sz) { + debug ("R2 bus_start: %llx phys_start: %llx size: %llx\n", + (u64)bus_start, (u64)phys_start, (u64)pci_sz); + pci_set_region(r, bus_start, phys_start, pci_sz, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | + PCI_REGION_PREFETCH); + sz -= pci_sz; + bus_start += pci_sz; + phys_start += pci_sz; + set_inbound_window(pi--, r++, pci_sz); + } +#endif + +#ifdef CONFIG_PHYS_64BIT + if (sz && (((u64)gd->ram_size) < (1ull << 32))) + printf("Was not able to map all of memory via " + "inbound windows -- %lld remaining\n", sz); +#endif + + hose->region_count = r - hose->regions; + + return 1; +} + +#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER +static void fsl_pcie_boot_master(pit_t *pi) +{ + /* configure inbound window for slave's u-boot image */ + debug("PCIEBOOT - MASTER: Inbound window for slave's image; " + "Local = 0x%llx, Bus = 0x%llx, Size = 0x%x\n", + (u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS, + (u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS1, + CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE); + struct pci_region r_inbound; + u32 sz_inbound = __ilog2_u64(CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE) + - 1; + pci_set_region(&r_inbound, + CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS1, + CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS, + sz_inbound, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + set_inbound_window(pi--, &r_inbound, + CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE); + + /* configure inbound window for slave's u-boot image */ + debug("PCIEBOOT - MASTER: Inbound window for slave's image; " + "Local = 0x%llx, Bus = 0x%llx, Size = 0x%x\n", + (u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS, + (u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS2, + CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE); + pci_set_region(&r_inbound, + CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS2, + CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS, + sz_inbound, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + set_inbound_window(pi--, &r_inbound, + CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE); + + /* configure inbound window for slave's ucode and ENV */ + debug("PCIEBOOT - MASTER: Inbound window for slave's " + "ucode and ENV; " + "Local = 0x%llx, Bus = 0x%llx, Size = 0x%x\n", + (u64)CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_PHYS, + (u64)CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_BUS, + CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_SIZE); + sz_inbound = __ilog2_u64(CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_SIZE) + - 1; + pci_set_region(&r_inbound, + CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_BUS, + CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_PHYS, + sz_inbound, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + set_inbound_window(pi--, &r_inbound, + CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_SIZE); +} + +static void fsl_pcie_boot_master_release_slave(int port) +{ + unsigned long release_addr; + + /* now release slave's core 0 */ + switch (port) { + case 1: + release_addr = CONFIG_SYS_PCIE1_MEM_VIRT + + CONFIG_SRIO_PCIE_BOOT_BRR_OFFSET; + break; +#ifdef CONFIG_SYS_PCIE2_MEM_VIRT + case 2: + release_addr = CONFIG_SYS_PCIE2_MEM_VIRT + + CONFIG_SRIO_PCIE_BOOT_BRR_OFFSET; + break; +#endif +#ifdef CONFIG_SYS_PCIE3_MEM_VIRT + case 3: + release_addr = CONFIG_SYS_PCIE3_MEM_VIRT + + CONFIG_SRIO_PCIE_BOOT_BRR_OFFSET; + break; +#endif + default: + release_addr = 0; + break; + } + if (release_addr != 0) { + out_be32((void *)release_addr, + CONFIG_SRIO_PCIE_BOOT_RELEASE_MASK); + debug("PCIEBOOT - MASTER: " + "Release slave successfully! Now the slave should start up!\n"); + } else { + debug("PCIEBOOT - MASTER: " + "Release slave failed!\n"); + } +} +#endif + +void fsl_pci_init(struct pci_controller *hose, struct fsl_pci_info *pci_info) +{ + u32 cfg_addr = (u32)&((ccsr_fsl_pci_t *)pci_info->regs)->cfg_addr; + u32 cfg_data = (u32)&((ccsr_fsl_pci_t *)pci_info->regs)->cfg_data; + u16 temp16; + u32 temp32; + u32 block_rev; + int enabled, r, inbound = 0; + u16 ltssm; + u8 temp8, pcie_cap; + int pcie_cap_pos; + int pci_dcr; + int pci_dsr; + int pci_lsr; + +#if defined(CONFIG_FSL_PCIE_DISABLE_ASPM) + int pci_lcr; +#endif + + volatile ccsr_fsl_pci_t *pci = (ccsr_fsl_pci_t *)cfg_addr; + struct pci_region *reg = hose->regions + hose->region_count; + pci_dev_t dev = PCI_BDF(hose->first_busno, 0, 0); + + /* Initialize ATMU registers based on hose regions and flags */ + volatile pot_t *po = &pci->pot[1]; /* skip 0 */ + volatile pit_t *pi; + + u64 out_hi = 0, out_lo = -1ULL; + u32 pcicsrbar, pcicsrbar_sz; + + pci_setup_indirect(hose, cfg_addr, cfg_data); + + block_rev = in_be32(&pci->block_rev1); + if (PEX_IP_BLK_REV_2_2 <= block_rev) { + pi = &pci->pit[2]; /* 0xDC0 */ + } else { + pi = &pci->pit[3]; /* 0xDE0 */ + } + + /* Handle setup of outbound windows first */ + for (r = 0; r < hose->region_count; r++) { + unsigned long flags = hose->regions[r].flags; + u32 sz = (__ilog2_u64((u64)hose->regions[r].size) - 1); + + flags &= PCI_REGION_SYS_MEMORY|PCI_REGION_TYPE; + if (flags != PCI_REGION_SYS_MEMORY) { + u64 start = hose->regions[r].bus_start; + u64 end = start + hose->regions[r].size; + + out_be32(&po->powbar, hose->regions[r].phys_start >> 12); + out_be32(&po->potar, start >> 12); +#ifdef CONFIG_SYS_PCI_64BIT + out_be32(&po->potear, start >> 44); +#else + out_be32(&po->potear, 0); +#endif + if (hose->regions[r].flags & PCI_REGION_IO) { + out_be32(&po->powar, POWAR_EN | sz | + POWAR_IO_READ | POWAR_IO_WRITE); + } else { + out_be32(&po->powar, POWAR_EN | sz | + POWAR_MEM_READ | POWAR_MEM_WRITE); + out_lo = min(start, out_lo); + out_hi = max(end, out_hi); + } + po++; + } + } + debug("Outbound memory range: %llx:%llx\n", out_lo, out_hi); + + /* setup PCSRBAR/PEXCSRBAR */ + pci_hose_write_config_dword(hose, dev, PCI_BASE_ADDRESS_0, 0xffffffff); + pci_hose_read_config_dword (hose, dev, PCI_BASE_ADDRESS_0, &pcicsrbar_sz); + pcicsrbar_sz = ~pcicsrbar_sz + 1; + + if (out_hi < (0x100000000ull - pcicsrbar_sz) || + (out_lo > 0x100000000ull)) + pcicsrbar = 0x100000000ull - pcicsrbar_sz; + else + pcicsrbar = (out_lo - pcicsrbar_sz) & -pcicsrbar_sz; + pci_hose_write_config_dword(hose, dev, PCI_BASE_ADDRESS_0, pcicsrbar); + + out_lo = min(out_lo, (u64)pcicsrbar); + + debug("PCICSRBAR @ 0x%x\n", pcicsrbar); + + pci_set_region(reg++, pcicsrbar, CONFIG_SYS_CCSRBAR_PHYS, + pcicsrbar_sz, PCI_REGION_SYS_MEMORY); + hose->region_count++; + + /* see if we are a PCIe or PCI controller */ + pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP); + pci_dcr = pcie_cap_pos + 0x08; + pci_dsr = pcie_cap_pos + 0x0a; + pci_lsr = pcie_cap_pos + 0x12; + + pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap); + +#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER + /* boot from PCIE --master */ + char *s = getenv("bootmaster"); + char pcie[6]; + sprintf(pcie, "PCIE%d", pci_info->pci_num); + + if (s && (strcmp(s, pcie) == 0)) { + debug("PCIEBOOT - MASTER: Master port [ %d ] for pcie boot.\n", + pci_info->pci_num); + fsl_pcie_boot_master((pit_t *)pi); + } else { + /* inbound */ + inbound = fsl_pci_setup_inbound_windows(hose, + out_lo, pcie_cap, pi); + } +#else + /* inbound */ + inbound = fsl_pci_setup_inbound_windows(hose, out_lo, pcie_cap, pi); +#endif + + for (r = 0; r < hose->region_count; r++) + debug("PCI reg:%d %016llx:%016llx %016llx %08lx\n", r, + (u64)hose->regions[r].phys_start, + (u64)hose->regions[r].bus_start, + (u64)hose->regions[r].size, + hose->regions[r].flags); + + pci_register_hose(hose); + pciauto_config_init(hose); /* grab pci_{mem,prefetch,io} */ + hose->current_busno = hose->first_busno; + + out_be32(&pci->pedr, 0xffffffff); /* Clear any errors */ + out_be32(&pci->peer, ~0x20140); /* Enable All Error Interrupts except + * - Master abort (pci) + * - Master PERR (pci) + * - ICCA (PCIe) + */ + pci_hose_read_config_dword(hose, dev, pci_dcr, &temp32); + temp32 |= 0xf000e; /* set URR, FER, NFER (but not CER) */ + pci_hose_write_config_dword(hose, dev, pci_dcr, temp32); + +#if defined(CONFIG_FSL_PCIE_DISABLE_ASPM) + pci_lcr = pcie_cap_pos + 0x10; + temp32 = 0; + pci_hose_read_config_dword(hose, dev, pci_lcr, &temp32); + temp32 &= ~0x03; /* Disable ASPM */ + pci_hose_write_config_dword(hose, dev, pci_lcr, temp32); + udelay(1); +#endif + if (pcie_cap == PCI_CAP_ID_EXP) { + if (block_rev >= PEX_IP_BLK_REV_3_0) { +#define PEX_CSR0_LTSSM_MASK 0xFC +#define PEX_CSR0_LTSSM_SHIFT 2 + ltssm = (in_be32(&pci->pex_csr0) + & PEX_CSR0_LTSSM_MASK) >> PEX_CSR0_LTSSM_SHIFT; + enabled = (ltssm == 0x11) ? 1 : 0; + } else { + /* pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm); */ + /* enabled = ltssm >= PCI_LTSSM_L0; */ + pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm); + enabled = ltssm >= PCI_LTSSM_L0; + +#ifdef CONFIG_FSL_PCIE_RESET + if (ltssm == 1) { + int i; + debug("....PCIe link error. " "LTSSM=0x%02x.", ltssm); + /* assert PCIe reset */ + setbits_be32(&pci->pdb_stat, 0x08000000); + (void) in_be32(&pci->pdb_stat); + udelay(100); + debug(" Asserting PCIe reset @%p = %x\n", + &pci->pdb_stat, in_be32(&pci->pdb_stat)); + /* clear PCIe reset */ + clrbits_be32(&pci->pdb_stat, 0x08000000); + asm("sync;isync"); + for (i=0; i<100 && ltssm < PCI_LTSSM_L0; i++) { + pci_hose_read_config_word(hose, dev, PCI_LTSSM, + <ssm); + udelay(1000); + debug("....PCIe link error. " + "LTSSM=0x%02x.\n", ltssm); + } + enabled = ltssm >= PCI_LTSSM_L0; + + /* we need to re-write the bar0 since a reset will + * clear it + */ + pci_hose_write_config_dword(hose, dev, + PCI_BASE_ADDRESS_0, pcicsrbar); + } +#endif + } + +#ifdef CONFIG_SYS_P4080_ERRATUM_PCIE_A003 + if (enabled == 0) { + serdes_corenet_t *srds_regs = (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR; + temp32 = in_be32(&srds_regs->srdspccr0); + + if ((temp32 >> 28) == 3) { + int i; + + out_be32(&srds_regs->srdspccr0, 2 << 28); + setbits_be32(&pci->pdb_stat, 0x08000000); + in_be32(&pci->pdb_stat); + udelay(100); + clrbits_be32(&pci->pdb_stat, 0x08000000); + asm("sync;isync"); + for (i=0; i < 100 && ltssm < PCI_LTSSM_L0; i++) { + pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm); + udelay(1000); + } + enabled = ltssm >= PCI_LTSSM_L0; + } + } +#endif + if (!enabled) { + /* Let the user know there's no PCIe link */ + printf("no link, regs @ 0x%lx\n", pci_info->regs); + hose->last_busno = hose->first_busno; + return; + } + + out_be32(&pci->pme_msg_det, 0xffffffff); + out_be32(&pci->pme_msg_int_en, 0xffffffff); + + /* Print the negotiated PCIe link width */ + pci_hose_read_config_word(hose, dev, pci_lsr, &temp16); + printf("x%d gen%d, regs @ 0x%lx\n", (temp16 & 0x3f0) >> 4, + (temp16 & 0xf), pci_info->regs); + + hose->current_busno++; /* Start scan with secondary */ + pciauto_prescan_setup_bridge(hose, dev, hose->current_busno); + } + + /* Use generic setup_device to initialize standard pci regs, + * but do not allocate any windows since any BAR found (such + * as PCSRBAR) is not in this cpu's memory space. + */ + pciauto_setup_device(hose, dev, 0, hose->pci_mem, + hose->pci_prefetch, hose->pci_io); + + if (inbound) { + pci_hose_read_config_word(hose, dev, PCI_COMMAND, &temp16); + pci_hose_write_config_word(hose, dev, PCI_COMMAND, + temp16 | PCI_COMMAND_MEMORY); + } + +#ifndef CONFIG_PCI_NOSCAN + if (!fsl_is_pci_agent(hose)) { + debug(" Scanning PCI bus %02x\n", + hose->current_busno); + hose->last_busno = pci_hose_scan_bus(hose, hose->current_busno); + } else { + debug(" Not scanning PCI bus %02x. PI=%x\n", + hose->current_busno, temp8); + hose->last_busno = hose->current_busno; + } + + /* if we are PCIe - update limit regs and subordinate busno + * for the virtual P2P bridge + */ + if (pcie_cap == PCI_CAP_ID_EXP) { + pciauto_postscan_setup_bridge(hose, dev, hose->last_busno); + } +#else + hose->last_busno = hose->current_busno; +#endif + + /* Clear all error indications */ + if (pcie_cap == PCI_CAP_ID_EXP) + out_be32(&pci->pme_msg_det, 0xffffffff); + out_be32(&pci->pedr, 0xffffffff); + + pci_hose_read_config_word(hose, dev, pci_dsr, &temp16); + if (temp16) { + pci_hose_write_config_word(hose, dev, pci_dsr, 0xffff); + } + + pci_hose_read_config_word (hose, dev, PCI_SEC_STATUS, &temp16); + if (temp16) { + pci_hose_write_config_word(hose, dev, PCI_SEC_STATUS, 0xffff); + } +} + +int fsl_is_pci_agent(struct pci_controller *hose) +{ + int pcie_cap_pos; + u8 pcie_cap; + pci_dev_t dev = PCI_BDF(hose->first_busno, 0, 0); + + pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP); + pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap); + if (pcie_cap == PCI_CAP_ID_EXP) { + u8 header_type; + + pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, + &header_type); + return (header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL; + } else { + u8 prog_if; + + pci_hose_read_config_byte(hose, dev, PCI_CLASS_PROG, &prog_if); + /* Programming Interface (PCI_CLASS_PROG) + * 0 == pci host or pcie root-complex, + * 1 == pci agent or pcie end-point + */ + return (prog_if == FSL_PROG_IF_AGENT); + } +} + +int fsl_pci_init_port(struct fsl_pci_info *pci_info, + struct pci_controller *hose, int busno) +{ + volatile ccsr_fsl_pci_t *pci; + struct pci_region *r; + pci_dev_t dev = PCI_BDF(busno,0,0); + int pcie_cap_pos; + u8 pcie_cap; + + pci = (ccsr_fsl_pci_t *) pci_info->regs; + + /* on non-PCIe controllers we don't have pme_msg_det so this code + * should do nothing since the read will return 0 + */ + if (in_be32(&pci->pme_msg_det)) { + out_be32(&pci->pme_msg_det, 0xffffffff); + debug (" with errors. Clearing. Now 0x%08x", + pci->pme_msg_det); + } + + r = hose->regions + hose->region_count; + + /* outbound memory */ + pci_set_region(r++, + pci_info->mem_bus, + pci_info->mem_phys, + pci_info->mem_size, + PCI_REGION_MEM); + + /* outbound io */ + pci_set_region(r++, + pci_info->io_bus, + pci_info->io_phys, + pci_info->io_size, + PCI_REGION_IO); + + hose->region_count = r - hose->regions; + hose->first_busno = busno; + + fsl_pci_init(hose, pci_info); + + if (fsl_is_pci_agent(hose)) { + fsl_pci_config_unlock(hose); + hose->last_busno = hose->first_busno; +#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER + } else { + /* boot from PCIE --master releases slave's core 0 */ + char *s = getenv("bootmaster"); + char pcie[6]; + sprintf(pcie, "PCIE%d", pci_info->pci_num); + + if (s && (strcmp(s, pcie) == 0)) + fsl_pcie_boot_master_release_slave(pci_info->pci_num); +#endif + } + + pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP); + pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap); + printf("PCI%s%x: Bus %02x - %02x\n", pcie_cap == PCI_CAP_ID_EXP ? + "e" : "", pci_info->pci_num, + hose->first_busno, hose->last_busno); + return(hose->last_busno + 1); +} + +/* Enable inbound PCI config cycles for agent/endpoint interface */ +void fsl_pci_config_unlock(struct pci_controller *hose) +{ + pci_dev_t dev = PCI_BDF(hose->first_busno,0,0); + int pcie_cap_pos; + u8 pcie_cap; + u16 pbfr; + + if (!fsl_is_pci_agent(hose)) + return; + + pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP); + pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap); + if (pcie_cap != 0x0) { + /* PCIe - set CFG_READY bit of Configuration Ready Register */ + pci_hose_write_config_byte(hose, dev, FSL_PCIE_CFG_RDY, 0x1); + } else { + /* PCI - clear ACL bit of PBFR */ + pci_hose_read_config_word(hose, dev, FSL_PCI_PBFR, &pbfr); + pbfr &= ~0x20; + pci_hose_write_config_word(hose, dev, FSL_PCI_PBFR, pbfr); + } +} + +#if defined(CONFIG_PCIE1) || defined(CONFIG_PCIE2) || \ + defined(CONFIG_PCIE3) || defined(CONFIG_PCIE4) +int fsl_configure_pcie(struct fsl_pci_info *info, + struct pci_controller *hose, + const char *connected, int busno) +{ + int is_endpoint; + + set_next_law(info->mem_phys, law_size_bits(info->mem_size), info->law); + set_next_law(info->io_phys, law_size_bits(info->io_size), info->law); + + is_endpoint = fsl_setup_hose(hose, info->regs); + printf("PCIe%u: %s", info->pci_num, + is_endpoint ? "Endpoint" : "Root Complex"); + if (connected) + printf(" of %s", connected); + puts(", "); + + return fsl_pci_init_port(info, hose, busno); +} + +#if defined(CONFIG_FSL_CORENET) +#ifdef CONFIG_SYS_FSL_QORIQ_CHASSIS2 + #define _DEVDISR_PCIE1 FSL_CORENET_DEVDISR3_PCIE1 + #define _DEVDISR_PCIE2 FSL_CORENET_DEVDISR3_PCIE2 + #define _DEVDISR_PCIE3 FSL_CORENET_DEVDISR3_PCIE3 + #define _DEVDISR_PCIE4 FSL_CORENET_DEVDISR3_PCIE4 +#else + #define _DEVDISR_PCIE1 FSL_CORENET_DEVDISR_PCIE1 + #define _DEVDISR_PCIE2 FSL_CORENET_DEVDISR_PCIE2 + #define _DEVDISR_PCIE3 FSL_CORENET_DEVDISR_PCIE3 + #define _DEVDISR_PCIE4 FSL_CORENET_DEVDISR_PCIE4 +#endif + #define CONFIG_SYS_MPC8xxx_GUTS_ADDR CONFIG_SYS_MPC85xx_GUTS_ADDR +#elif defined(CONFIG_MPC85xx) + #define _DEVDISR_PCIE1 MPC85xx_DEVDISR_PCIE + #define _DEVDISR_PCIE2 MPC85xx_DEVDISR_PCIE2 + #define _DEVDISR_PCIE3 MPC85xx_DEVDISR_PCIE3 + #define _DEVDISR_PCIE4 0 + #define CONFIG_SYS_MPC8xxx_GUTS_ADDR CONFIG_SYS_MPC85xx_GUTS_ADDR +#elif defined(CONFIG_MPC86xx) + #define _DEVDISR_PCIE1 MPC86xx_DEVDISR_PCIE1 + #define _DEVDISR_PCIE2 MPC86xx_DEVDISR_PCIE2 + #define _DEVDISR_PCIE3 0 + #define _DEVDISR_PCIE4 0 + #define CONFIG_SYS_MPC8xxx_GUTS_ADDR \ + (&((immap_t *)CONFIG_SYS_IMMR)->im_gur) +#else +#error "No defines for DEVDISR_PCIE" +#endif + +/* Implement a dummy function for those platforms w/o SERDES */ +static const char *__board_serdes_name(enum srds_prtcl device) +{ + switch (device) { +#ifdef CONFIG_SYS_PCIE1_NAME + case PCIE1: + return CONFIG_SYS_PCIE1_NAME; +#endif +#ifdef CONFIG_SYS_PCIE2_NAME + case PCIE2: + return CONFIG_SYS_PCIE2_NAME; +#endif +#ifdef CONFIG_SYS_PCIE3_NAME + case PCIE3: + return CONFIG_SYS_PCIE3_NAME; +#endif +#ifdef CONFIG_SYS_PCIE4_NAME + case PCIE4: + return CONFIG_SYS_PCIE4_NAME; +#endif + default: + return NULL; + } + + return NULL; +} + +__attribute__((weak, alias("__board_serdes_name"))) const char * +board_serdes_name(enum srds_prtcl device); + +static u32 devdisr_mask[] = { + _DEVDISR_PCIE1, + _DEVDISR_PCIE2, + _DEVDISR_PCIE3, + _DEVDISR_PCIE4, +}; + +int fsl_pcie_init_ctrl(int busno, u32 devdisr, enum srds_prtcl dev, + struct fsl_pci_info *pci_info) +{ + struct pci_controller *hose; + int num = dev - PCIE1; + + hose = calloc(1, sizeof(struct pci_controller)); + if (!hose) + return busno; + + if (is_serdes_configured(dev) && !(devdisr & devdisr_mask[num])) { + busno = fsl_configure_pcie(pci_info, hose, + board_serdes_name(dev), busno); + } else { + printf("PCIe%d: disabled\n", num + 1); + } + + return busno; +} + +int fsl_pcie_init_board(int busno) +{ + struct fsl_pci_info pci_info; + ccsr_gur_t *gur = (void *)CONFIG_SYS_MPC8xxx_GUTS_ADDR; + u32 devdisr; + u32 *addr; + +#ifdef CONFIG_SYS_FSL_QORIQ_CHASSIS2 + addr = &gur->devdisr3; +#else + addr = &gur->devdisr; +#endif + devdisr = in_be32(addr); + +#ifdef CONFIG_PCIE1 + SET_STD_PCIE_INFO(pci_info, 1); + busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE1, &pci_info); +#else + setbits_be32(addr, _DEVDISR_PCIE1); /* disable */ +#endif + +#ifdef CONFIG_PCIE2 + SET_STD_PCIE_INFO(pci_info, 2); + busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE2, &pci_info); +#else + setbits_be32(addr, _DEVDISR_PCIE2); /* disable */ +#endif + +#ifdef CONFIG_PCIE3 + SET_STD_PCIE_INFO(pci_info, 3); + busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE3, &pci_info); +#else + setbits_be32(addr, _DEVDISR_PCIE3); /* disable */ +#endif + +#ifdef CONFIG_PCIE4 + SET_STD_PCIE_INFO(pci_info, 4); + busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE4, &pci_info); +#else + setbits_be32(addr, _DEVDISR_PCIE4); /* disable */ +#endif + + return busno; +} +#else +int fsl_pcie_init_ctrl(int busno, u32 devdisr, enum srds_prtcl dev, + struct fsl_pci_info *pci_info) +{ + return busno; +} + +int fsl_pcie_init_board(int busno) +{ + return busno; +} +#endif + +#ifdef CONFIG_OF_BOARD_SETUP +#include +#include + +void ft_fsl_pci_setup(void *blob, const char *pci_compat, + unsigned long ctrl_addr) +{ + int off; + u32 bus_range[2]; + phys_addr_t p_ctrl_addr = (phys_addr_t)ctrl_addr; + struct pci_controller *hose; + + hose = find_hose_by_cfg_addr((void *)(ctrl_addr)); + + /* convert ctrl_addr to true physical address */ + p_ctrl_addr = (phys_addr_t)ctrl_addr - CONFIG_SYS_CCSRBAR; + p_ctrl_addr += CONFIG_SYS_CCSRBAR_PHYS; + + off = fdt_node_offset_by_compat_reg(blob, pci_compat, p_ctrl_addr); + + if (off < 0) + return; + + /* We assume a cfg_addr not being set means we didn't setup the controller */ + if ((hose == NULL) || (hose->cfg_addr == NULL)) { + fdt_del_node(blob, off); + } else { + bus_range[0] = 0; + bus_range[1] = hose->last_busno - hose->first_busno; + fdt_setprop(blob, off, "bus-range", &bus_range[0], 2*4); + fdt_pci_dma_ranges(blob, off, hose); + } +} +#endif diff --git a/qemu/roms/u-boot/drivers/pci/pci.c b/qemu/roms/u-boot/drivers/pci/pci.c new file mode 100644 index 000000000..ed113bf40 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pci.c @@ -0,0 +1,789 @@ +/* + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH + * Andreas Heppel + * + * (C) Copyright 2002, 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * PCI routines + */ + +#include + +#include +#include +#include +#include + +#define PCI_HOSE_OP(rw, size, type) \ +int pci_hose_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, \ + int offset, type value) \ +{ \ + return hose->rw##_##size(hose, dev, offset, value); \ +} + +PCI_HOSE_OP(read, byte, u8 *) +PCI_HOSE_OP(read, word, u16 *) +PCI_HOSE_OP(read, dword, u32 *) +PCI_HOSE_OP(write, byte, u8) +PCI_HOSE_OP(write, word, u16) +PCI_HOSE_OP(write, dword, u32) + +#define PCI_OP(rw, size, type, error_code) \ +int pci_##rw##_config_##size(pci_dev_t dev, int offset, type value) \ +{ \ + struct pci_controller *hose = pci_bus_to_hose(PCI_BUS(dev)); \ + \ + if (!hose) \ + { \ + error_code; \ + return -1; \ + } \ + \ + return pci_hose_##rw##_config_##size(hose, dev, offset, value); \ +} + +PCI_OP(read, byte, u8 *, *value = 0xff) +PCI_OP(read, word, u16 *, *value = 0xffff) +PCI_OP(read, dword, u32 *, *value = 0xffffffff) +PCI_OP(write, byte, u8, ) +PCI_OP(write, word, u16, ) +PCI_OP(write, dword, u32, ) + +#define PCI_READ_VIA_DWORD_OP(size, type, off_mask) \ +int pci_hose_read_config_##size##_via_dword(struct pci_controller *hose,\ + pci_dev_t dev, \ + int offset, type val) \ +{ \ + u32 val32; \ + \ + if (pci_hose_read_config_dword(hose, dev, offset & 0xfc, &val32) < 0) { \ + *val = -1; \ + return -1; \ + } \ + \ + *val = (val32 >> ((offset & (int)off_mask) * 8)); \ + \ + return 0; \ +} + +#define PCI_WRITE_VIA_DWORD_OP(size, type, off_mask, val_mask) \ +int pci_hose_write_config_##size##_via_dword(struct pci_controller *hose,\ + pci_dev_t dev, \ + int offset, type val) \ +{ \ + u32 val32, mask, ldata, shift; \ + \ + if (pci_hose_read_config_dword(hose, dev, offset & 0xfc, &val32) < 0)\ + return -1; \ + \ + shift = ((offset & (int)off_mask) * 8); \ + ldata = (((unsigned long)val) & val_mask) << shift; \ + mask = val_mask << shift; \ + val32 = (val32 & ~mask) | ldata; \ + \ + if (pci_hose_write_config_dword(hose, dev, offset & 0xfc, val32) < 0)\ + return -1; \ + \ + return 0; \ +} + +PCI_READ_VIA_DWORD_OP(byte, u8 *, 0x03) +PCI_READ_VIA_DWORD_OP(word, u16 *, 0x02) +PCI_WRITE_VIA_DWORD_OP(byte, u8, 0x03, 0x000000ff) +PCI_WRITE_VIA_DWORD_OP(word, u16, 0x02, 0x0000ffff) + +/* Get a virtual address associated with a BAR region */ +void *pci_map_bar(pci_dev_t pdev, int bar, int flags) +{ + pci_addr_t pci_bus_addr; + u32 bar_response; + + /* read BAR address */ + pci_read_config_dword(pdev, bar, &bar_response); + pci_bus_addr = (pci_addr_t)(bar_response & ~0xf); + + /* + * Pass "0" as the length argument to pci_bus_to_virt. The arg + * isn't actualy used on any platform because u-boot assumes a static + * linear mapping. In the future, this could read the BAR size + * and pass that as the size if needed. + */ + return pci_bus_to_virt(pdev, pci_bus_addr, flags, 0, MAP_NOCACHE); +} + +/* + * + */ + +static struct pci_controller* hose_head; + +void pci_register_hose(struct pci_controller* hose) +{ + struct pci_controller **phose = &hose_head; + + while(*phose) + phose = &(*phose)->next; + + hose->next = NULL; + + *phose = hose; +} + +struct pci_controller *pci_bus_to_hose(int bus) +{ + struct pci_controller *hose; + + for (hose = hose_head; hose; hose = hose->next) { + if (bus >= hose->first_busno && bus <= hose->last_busno) + return hose; + } + + printf("pci_bus_to_hose() failed\n"); + return NULL; +} + +struct pci_controller *find_hose_by_cfg_addr(void *cfg_addr) +{ + struct pci_controller *hose; + + for (hose = hose_head; hose; hose = hose->next) { + if (hose->cfg_addr == cfg_addr) + return hose; + } + + return NULL; +} + +int pci_last_busno(void) +{ + struct pci_controller *hose = hose_head; + + if (!hose) + return -1; + + while (hose->next) + hose = hose->next; + + return hose->last_busno; +} + +pci_dev_t pci_find_devices(struct pci_device_id *ids, int index) +{ + struct pci_controller * hose; + u16 vendor, device; + u8 header_type; + pci_dev_t bdf; + int i, bus, found_multi = 0; + + for (hose = hose_head; hose; hose = hose->next) { +#ifdef CONFIG_SYS_SCSI_SCAN_BUS_REVERSE + for (bus = hose->last_busno; bus >= hose->first_busno; bus--) +#else + for (bus = hose->first_busno; bus <= hose->last_busno; bus++) +#endif + for (bdf = PCI_BDF(bus, 0, 0); +#if defined(CONFIG_ELPPC) || defined(CONFIG_PPMC7XX) + bdf < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1, + PCI_MAX_PCI_FUNCTIONS - 1); +#else + bdf < PCI_BDF(bus + 1, 0, 0); +#endif + bdf += PCI_BDF(0, 0, 1)) { + if (!PCI_FUNC(bdf)) { + pci_read_config_byte(bdf, + PCI_HEADER_TYPE, + &header_type); + + found_multi = header_type & 0x80; + } else { + if (!found_multi) + continue; + } + + pci_read_config_word(bdf, + PCI_VENDOR_ID, + &vendor); + pci_read_config_word(bdf, + PCI_DEVICE_ID, + &device); + + for (i = 0; ids[i].vendor != 0; i++) { + if (vendor == ids[i].vendor && + device == ids[i].device) { + if (index <= 0) + return bdf; + + index--; + } + } + } + } + + return -1; +} + +pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index) +{ + static struct pci_device_id ids[2] = {{}, {0, 0}}; + + ids[0].vendor = vendor; + ids[0].device = device; + + return pci_find_devices(ids, index); +} + +/* + * + */ + +int __pci_hose_phys_to_bus(struct pci_controller *hose, + phys_addr_t phys_addr, + unsigned long flags, + unsigned long skip_mask, + pci_addr_t *ba) +{ + struct pci_region *res; + pci_addr_t bus_addr; + int i; + + for (i = 0; i < hose->region_count; i++) { + res = &hose->regions[i]; + + if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0) + continue; + + if (res->flags & skip_mask) + continue; + + bus_addr = phys_addr - res->phys_start + res->bus_start; + + if (bus_addr >= res->bus_start && + bus_addr < res->bus_start + res->size) { + *ba = bus_addr; + return 0; + } + } + + return 1; +} + +pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose, + phys_addr_t phys_addr, + unsigned long flags) +{ + pci_addr_t bus_addr = 0; + int ret; + + if (!hose) { + puts("pci_hose_phys_to_bus: invalid hose\n"); + return bus_addr; + } + + /* + * if PCI_REGION_MEM is set we do a two pass search with preference + * on matches that don't have PCI_REGION_SYS_MEMORY set + */ + if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) { + ret = __pci_hose_phys_to_bus(hose, phys_addr, + flags, PCI_REGION_SYS_MEMORY, &bus_addr); + if (!ret) + return bus_addr; + } + + ret = __pci_hose_phys_to_bus(hose, phys_addr, flags, 0, &bus_addr); + + if (ret) + puts("pci_hose_phys_to_bus: invalid physical address\n"); + + return bus_addr; +} + +int __pci_hose_bus_to_phys(struct pci_controller *hose, + pci_addr_t bus_addr, + unsigned long flags, + unsigned long skip_mask, + phys_addr_t *pa) +{ + struct pci_region *res; + int i; + + for (i = 0; i < hose->region_count; i++) { + res = &hose->regions[i]; + + if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0) + continue; + + if (res->flags & skip_mask) + continue; + + if (bus_addr >= res->bus_start && + bus_addr < res->bus_start + res->size) { + *pa = (bus_addr - res->bus_start + res->phys_start); + return 0; + } + } + + return 1; +} + +phys_addr_t pci_hose_bus_to_phys(struct pci_controller* hose, + pci_addr_t bus_addr, + unsigned long flags) +{ + phys_addr_t phys_addr = 0; + int ret; + + if (!hose) { + puts("pci_hose_bus_to_phys: invalid hose\n"); + return phys_addr; + } + + /* + * if PCI_REGION_MEM is set we do a two pass search with preference + * on matches that don't have PCI_REGION_SYS_MEMORY set + */ + if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) { + ret = __pci_hose_bus_to_phys(hose, bus_addr, + flags, PCI_REGION_SYS_MEMORY, &phys_addr); + if (!ret) + return phys_addr; + } + + ret = __pci_hose_bus_to_phys(hose, bus_addr, flags, 0, &phys_addr); + + if (ret) + puts("pci_hose_bus_to_phys: invalid physical address\n"); + + return phys_addr; +} + +/* + * + */ + +int pci_hose_config_device(struct pci_controller *hose, + pci_dev_t dev, + unsigned long io, + pci_addr_t mem, + unsigned long command) +{ + u32 bar_response; + unsigned int old_command; + pci_addr_t bar_value; + pci_size_t bar_size; + unsigned char pin; + int bar, found_mem64; + + debug("PCI Config: I/O=0x%lx, Memory=0x%llx, Command=0x%lx\n", io, + (u64)mem, command); + + pci_hose_write_config_dword(hose, dev, PCI_COMMAND, 0); + + for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar += 4) { + pci_hose_write_config_dword(hose, dev, bar, 0xffffffff); + pci_hose_read_config_dword(hose, dev, bar, &bar_response); + + if (!bar_response) + continue; + + found_mem64 = 0; + + /* Check the BAR type and set our address mask */ + if (bar_response & PCI_BASE_ADDRESS_SPACE) { + bar_size = ~(bar_response & PCI_BASE_ADDRESS_IO_MASK) + 1; + /* round up region base address to a multiple of size */ + io = ((io - 1) | (bar_size - 1)) + 1; + bar_value = io; + /* compute new region base address */ + io = io + bar_size; + } else { + if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) { + u32 bar_response_upper; + u64 bar64; + pci_hose_write_config_dword(hose, dev, bar + 4, + 0xffffffff); + pci_hose_read_config_dword(hose, dev, bar + 4, + &bar_response_upper); + + bar64 = ((u64)bar_response_upper << 32) | bar_response; + + bar_size = ~(bar64 & PCI_BASE_ADDRESS_MEM_MASK) + 1; + found_mem64 = 1; + } else { + bar_size = (u32)(~(bar_response & PCI_BASE_ADDRESS_MEM_MASK) + 1); + } + + /* round up region base address to multiple of size */ + mem = ((mem - 1) | (bar_size - 1)) + 1; + bar_value = mem; + /* compute new region base address */ + mem = mem + bar_size; + } + + /* Write it out and update our limit */ + pci_hose_write_config_dword (hose, dev, bar, (u32)bar_value); + + if (found_mem64) { + bar += 4; +#ifdef CONFIG_SYS_PCI_64BIT + pci_hose_write_config_dword(hose, dev, bar, + (u32)(bar_value >> 32)); +#else + pci_hose_write_config_dword(hose, dev, bar, 0x00000000); +#endif + } + } + + /* Configure Cache Line Size Register */ + pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08); + + /* Configure Latency Timer */ + pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80); + + /* Disable interrupt line, if device says it wants to use interrupts */ + pci_hose_read_config_byte(hose, dev, PCI_INTERRUPT_PIN, &pin); + if (pin != 0) { + pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE, 0xff); + } + + pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &old_command); + pci_hose_write_config_dword(hose, dev, PCI_COMMAND, + (old_command & 0xffff0000) | command); + + return 0; +} + +/* + * + */ + +struct pci_config_table *pci_find_config(struct pci_controller *hose, + unsigned short class, + unsigned int vendor, + unsigned int device, + unsigned int bus, + unsigned int dev, + unsigned int func) +{ + struct pci_config_table *table; + + for (table = hose->config_table; table && table->vendor; table++) { + if ((table->vendor == PCI_ANY_ID || table->vendor == vendor) && + (table->device == PCI_ANY_ID || table->device == device) && + (table->class == PCI_ANY_ID || table->class == class) && + (table->bus == PCI_ANY_ID || table->bus == bus) && + (table->dev == PCI_ANY_ID || table->dev == dev) && + (table->func == PCI_ANY_ID || table->func == func)) { + return table; + } + } + + return NULL; +} + +void pci_cfgfunc_config_device(struct pci_controller *hose, + pci_dev_t dev, + struct pci_config_table *entry) +{ + pci_hose_config_device(hose, dev, entry->priv[0], entry->priv[1], + entry->priv[2]); +} + +void pci_cfgfunc_do_nothing(struct pci_controller *hose, + pci_dev_t dev, struct pci_config_table *entry) +{ +} + +/* + * HJF: Changed this to return int. I think this is required + * to get the correct result when scanning bridges + */ +extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev); + +#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI_SCAN_SHOW) +const char * pci_class_str(u8 class) +{ + switch (class) { + case PCI_CLASS_NOT_DEFINED: + return "Build before PCI Rev2.0"; + break; + case PCI_BASE_CLASS_STORAGE: + return "Mass storage controller"; + break; + case PCI_BASE_CLASS_NETWORK: + return "Network controller"; + break; + case PCI_BASE_CLASS_DISPLAY: + return "Display controller"; + break; + case PCI_BASE_CLASS_MULTIMEDIA: + return "Multimedia device"; + break; + case PCI_BASE_CLASS_MEMORY: + return "Memory controller"; + break; + case PCI_BASE_CLASS_BRIDGE: + return "Bridge device"; + break; + case PCI_BASE_CLASS_COMMUNICATION: + return "Simple comm. controller"; + break; + case PCI_BASE_CLASS_SYSTEM: + return "Base system peripheral"; + break; + case PCI_BASE_CLASS_INPUT: + return "Input device"; + break; + case PCI_BASE_CLASS_DOCKING: + return "Docking station"; + break; + case PCI_BASE_CLASS_PROCESSOR: + return "Processor"; + break; + case PCI_BASE_CLASS_SERIAL: + return "Serial bus controller"; + break; + case PCI_BASE_CLASS_INTELLIGENT: + return "Intelligent controller"; + break; + case PCI_BASE_CLASS_SATELLITE: + return "Satellite controller"; + break; + case PCI_BASE_CLASS_CRYPT: + return "Cryptographic device"; + break; + case PCI_BASE_CLASS_SIGNAL_PROCESSING: + return "DSP"; + break; + case PCI_CLASS_OTHERS: + return "Does not fit any class"; + break; + default: + return "???"; + break; + }; +} +#endif /* CONFIG_CMD_PCI || CONFIG_PCI_SCAN_SHOW */ + +int __pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) +{ + /* + * Check if pci device should be skipped in configuration + */ + if (dev == PCI_BDF(hose->first_busno, 0, 0)) { +#if defined(CONFIG_PCI_CONFIG_HOST_BRIDGE) /* don't skip host bridge */ + /* + * Only skip configuration if "pciconfighost" is not set + */ + if (getenv("pciconfighost") == NULL) + return 1; +#else + return 1; +#endif + } + + return 0; +} +int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) + __attribute__((weak, alias("__pci_skip_dev"))); + +#ifdef CONFIG_PCI_SCAN_SHOW +int __pci_print_dev(struct pci_controller *hose, pci_dev_t dev) +{ + if (dev == PCI_BDF(hose->first_busno, 0, 0)) + return 0; + + return 1; +} +int pci_print_dev(struct pci_controller *hose, pci_dev_t dev) + __attribute__((weak, alias("__pci_print_dev"))); +#endif /* CONFIG_PCI_SCAN_SHOW */ + +int pci_hose_scan_bus(struct pci_controller *hose, int bus) +{ + unsigned int sub_bus, found_multi = 0; + unsigned short vendor, device, class; + unsigned char header_type; +#ifndef CONFIG_PCI_PNP + struct pci_config_table *cfg; +#endif + pci_dev_t dev; +#ifdef CONFIG_PCI_SCAN_SHOW + static int indent = 0; +#endif + + sub_bus = bus; + + for (dev = PCI_BDF(bus,0,0); + dev < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1, + PCI_MAX_PCI_FUNCTIONS - 1); + dev += PCI_BDF(0, 0, 1)) { + + if (pci_skip_dev(hose, dev)) + continue; + + if (PCI_FUNC(dev) && !found_multi) + continue; + + pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, &header_type); + + pci_hose_read_config_word(hose, dev, PCI_VENDOR_ID, &vendor); + + if (vendor == 0xffff || vendor == 0x0000) + continue; + + if (!PCI_FUNC(dev)) + found_multi = header_type & 0x80; + + debug("PCI Scan: Found Bus %d, Device %d, Function %d\n", + PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev)); + + pci_hose_read_config_word(hose, dev, PCI_DEVICE_ID, &device); + pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class); + +#ifdef CONFIG_PCI_SCAN_SHOW + indent++; + + /* Print leading space, including bus indentation */ + printf("%*c", indent + 1, ' '); + + if (pci_print_dev(hose, dev)) { + printf("%02x:%02x.%-*x - %04x:%04x - %s\n", + PCI_BUS(dev), PCI_DEV(dev), 6 - indent, PCI_FUNC(dev), + vendor, device, pci_class_str(class >> 8)); + } +#endif + +#ifdef CONFIG_PCI_PNP + sub_bus = max(pciauto_config_device(hose, dev), sub_bus); +#else + cfg = pci_find_config(hose, class, vendor, device, + PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev)); + if (cfg) { + cfg->config_device(hose, dev, cfg); + sub_bus = max(sub_bus, hose->current_busno); + } +#endif + +#ifdef CONFIG_PCI_SCAN_SHOW + indent--; +#endif + + if (hose->fixup_irq) + hose->fixup_irq(hose, dev); + } + + return sub_bus; +} + +int pci_hose_scan(struct pci_controller *hose) +{ +#if defined(CONFIG_PCI_BOOTDELAY) + static int pcidelay_done; + char *s; + int i; + + if (!pcidelay_done) { + /* wait "pcidelay" ms (if defined)... */ + s = getenv("pcidelay"); + if (s) { + int val = simple_strtoul(s, NULL, 10); + for (i = 0; i < val; i++) + udelay(1000); + } + pcidelay_done = 1; + } +#endif /* CONFIG_PCI_BOOTDELAY */ + + /* + * Start scan at current_busno. + * PCIe will start scan at first_busno+1. + */ + /* For legacy support, ensure current >= first */ + if (hose->first_busno > hose->current_busno) + hose->current_busno = hose->first_busno; +#ifdef CONFIG_PCI_PNP + pciauto_config_init(hose); +#endif + return pci_hose_scan_bus(hose, hose->current_busno); +} + +void pci_init(void) +{ + hose_head = NULL; + + /* now call board specific pci_init()... */ + pci_init_board(); +} + +/* Returns the address of the requested capability structure within the + * device's PCI configuration space or 0 in case the device does not + * support it. + * */ +int pci_hose_find_capability(struct pci_controller *hose, pci_dev_t dev, + int cap) +{ + int pos; + u8 hdr_type; + + pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, &hdr_type); + + pos = pci_hose_find_cap_start(hose, dev, hdr_type & 0x7F); + + if (pos) + pos = pci_find_cap(hose, dev, pos, cap); + + return pos; +} + +/* Find the header pointer to the Capabilities*/ +int pci_hose_find_cap_start(struct pci_controller *hose, pci_dev_t dev, + u8 hdr_type) +{ + u16 status; + + pci_hose_read_config_word(hose, dev, PCI_STATUS, &status); + + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + + switch (hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + case PCI_HEADER_TYPE_BRIDGE: + return PCI_CAPABILITY_LIST; + case PCI_HEADER_TYPE_CARDBUS: + return PCI_CB_CAPABILITY_LIST; + default: + return 0; + } +} + +int pci_find_cap(struct pci_controller *hose, pci_dev_t dev, int pos, int cap) +{ + int ttl = PCI_FIND_CAP_TTL; + u8 id; + u8 next_pos; + + while (ttl--) { + pci_hose_read_config_byte(hose, dev, pos, &next_pos); + if (next_pos < CAP_START_POS) + break; + next_pos &= ~3; + pos = (int) next_pos; + pci_hose_read_config_byte(hose, dev, + pos + PCI_CAP_LIST_ID, &id); + if (id == 0xff) + break; + if (id == cap) + return pos; + pos += PCI_CAP_LIST_NEXT; + } + return 0; +} diff --git a/qemu/roms/u-boot/drivers/pci/pci_auto.c b/qemu/roms/u-boot/drivers/pci/pci_auto.c new file mode 100644 index 000000000..86ba6b523 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pci_auto.c @@ -0,0 +1,451 @@ +/* + * arch/powerpc/kernel/pci_auto.c + * + * PCI autoconfiguration library + * + * Author: Matt Porter + * + * Copyright 2000 MontaVista Software Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include + +#undef DEBUG +#ifdef DEBUG +#define DEBUGF(x...) printf(x) +#else +#define DEBUGF(x...) +#endif /* DEBUG */ + +#define PCIAUTO_IDE_MODE_MASK 0x05 + +/* the user can define CONFIG_SYS_PCI_CACHE_LINE_SIZE to avoid problems */ +#ifndef CONFIG_SYS_PCI_CACHE_LINE_SIZE +#define CONFIG_SYS_PCI_CACHE_LINE_SIZE 8 +#endif + +/* + * + */ + +void pciauto_region_init(struct pci_region *res) +{ + /* + * Avoid allocating PCI resources from address 0 -- this is illegal + * according to PCI 2.1 and moreover, this is known to cause Linux IDE + * drivers to fail. Use a reasonable starting value of 0x1000 instead. + */ + res->bus_lower = res->bus_start ? res->bus_start : 0x1000; +} + +void pciauto_region_align(struct pci_region *res, pci_size_t size) +{ + res->bus_lower = ((res->bus_lower - 1) | (size - 1)) + 1; +} + +int pciauto_region_allocate(struct pci_region *res, pci_size_t size, + pci_addr_t *bar) +{ + pci_addr_t addr; + + if (!res) { + DEBUGF("No resource"); + goto error; + } + + addr = ((res->bus_lower - 1) | (size - 1)) + 1; + + if (addr - res->bus_start + size > res->size) { + DEBUGF("No room in resource"); + goto error; + } + + res->bus_lower = addr + size; + + DEBUGF("address=0x%llx bus_lower=0x%llx", (u64)addr, (u64)res->bus_lower); + + *bar = addr; + return 0; + + error: + *bar = (pci_addr_t)-1; + return -1; +} + +/* + * + */ + +void pciauto_setup_device(struct pci_controller *hose, + pci_dev_t dev, int bars_num, + struct pci_region *mem, + struct pci_region *prefetch, + struct pci_region *io) +{ + u32 bar_response; + pci_size_t bar_size; + u16 cmdstat = 0; + int bar, bar_nr = 0; +#ifndef CONFIG_PCI_ENUM_ONLY + pci_addr_t bar_value; + struct pci_region *bar_res; + int found_mem64 = 0; +#endif + + pci_hose_read_config_word(hose, dev, PCI_COMMAND, &cmdstat); + cmdstat = (cmdstat & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) | PCI_COMMAND_MASTER; + + for (bar = PCI_BASE_ADDRESS_0; + bar < PCI_BASE_ADDRESS_0 + (bars_num * 4); bar += 4) { + /* Tickle the BAR and get the response */ +#ifndef CONFIG_PCI_ENUM_ONLY + pci_hose_write_config_dword(hose, dev, bar, 0xffffffff); +#endif + pci_hose_read_config_dword(hose, dev, bar, &bar_response); + + /* If BAR is not implemented go to the next BAR */ + if (!bar_response) + continue; + +#ifndef CONFIG_PCI_ENUM_ONLY + found_mem64 = 0; +#endif + + /* Check the BAR type and set our address mask */ + if (bar_response & PCI_BASE_ADDRESS_SPACE) { + bar_size = ((~(bar_response & PCI_BASE_ADDRESS_IO_MASK)) + & 0xffff) + 1; +#ifndef CONFIG_PCI_ENUM_ONLY + bar_res = io; +#endif + + DEBUGF("PCI Autoconfig: BAR %d, I/O, size=0x%llx, ", bar_nr, (u64)bar_size); + } else { + if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) { + u32 bar_response_upper; + u64 bar64; + +#ifndef CONFIG_PCI_ENUM_ONLY + pci_hose_write_config_dword(hose, dev, bar + 4, + 0xffffffff); +#endif + pci_hose_read_config_dword(hose, dev, bar + 4, + &bar_response_upper); + + bar64 = ((u64)bar_response_upper << 32) | bar_response; + + bar_size = ~(bar64 & PCI_BASE_ADDRESS_MEM_MASK) + 1; +#ifndef CONFIG_PCI_ENUM_ONLY + found_mem64 = 1; +#endif + } else { + bar_size = (u32)(~(bar_response & PCI_BASE_ADDRESS_MEM_MASK) + 1); + } +#ifndef CONFIG_PCI_ENUM_ONLY + if (prefetch && (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH)) + bar_res = prefetch; + else + bar_res = mem; +#endif + + DEBUGF("PCI Autoconfig: BAR %d, Mem, size=0x%llx, ", bar_nr, (u64)bar_size); + } + +#ifndef CONFIG_PCI_ENUM_ONLY + if (pciauto_region_allocate(bar_res, bar_size, &bar_value) == 0) { + /* Write it out and update our limit */ + pci_hose_write_config_dword(hose, dev, bar, (u32)bar_value); + + if (found_mem64) { + bar += 4; +#ifdef CONFIG_SYS_PCI_64BIT + pci_hose_write_config_dword(hose, dev, bar, (u32)(bar_value>>32)); +#else + /* + * If we are a 64-bit decoder then increment to the + * upper 32 bits of the bar and force it to locate + * in the lower 4GB of memory. + */ + pci_hose_write_config_dword(hose, dev, bar, 0x00000000); +#endif + } + + } +#endif + cmdstat |= (bar_response & PCI_BASE_ADDRESS_SPACE) ? + PCI_COMMAND_IO : PCI_COMMAND_MEMORY; + + DEBUGF("\n"); + + bar_nr++; + } + + pci_hose_write_config_word(hose, dev, PCI_COMMAND, cmdstat); + pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, + CONFIG_SYS_PCI_CACHE_LINE_SIZE); + pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80); +} + +void pciauto_prescan_setup_bridge(struct pci_controller *hose, + pci_dev_t dev, int sub_bus) +{ + struct pci_region *pci_mem = hose->pci_mem; + struct pci_region *pci_prefetch = hose->pci_prefetch; + struct pci_region *pci_io = hose->pci_io; + u16 cmdstat; + + pci_hose_read_config_word(hose, dev, PCI_COMMAND, &cmdstat); + + /* Configure bus number registers */ + pci_hose_write_config_byte(hose, dev, PCI_PRIMARY_BUS, + PCI_BUS(dev) - hose->first_busno); + pci_hose_write_config_byte(hose, dev, PCI_SECONDARY_BUS, + sub_bus - hose->first_busno); + pci_hose_write_config_byte(hose, dev, PCI_SUBORDINATE_BUS, 0xff); + + if (pci_mem) { + /* Round memory allocator to 1MB boundary */ + pciauto_region_align(pci_mem, 0x100000); + + /* Set up memory and I/O filter limits, assume 32-bit I/O space */ + pci_hose_write_config_word(hose, dev, PCI_MEMORY_BASE, + (pci_mem->bus_lower & 0xfff00000) >> 16); + + cmdstat |= PCI_COMMAND_MEMORY; + } + + if (pci_prefetch) { + /* Round memory allocator to 1MB boundary */ + pciauto_region_align(pci_prefetch, 0x100000); + + /* Set up memory and I/O filter limits, assume 32-bit I/O space */ + pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_BASE, + (pci_prefetch->bus_lower & 0xfff00000) >> 16); + + cmdstat |= PCI_COMMAND_MEMORY; + } else { + /* We don't support prefetchable memory for now, so disable */ + pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_BASE, 0x1000); + pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_LIMIT, 0x0); + } + + if (pci_io) { + /* Round I/O allocator to 4KB boundary */ + pciauto_region_align(pci_io, 0x1000); + + pci_hose_write_config_byte(hose, dev, PCI_IO_BASE, + (pci_io->bus_lower & 0x0000f000) >> 8); + pci_hose_write_config_word(hose, dev, PCI_IO_BASE_UPPER16, + (pci_io->bus_lower & 0xffff0000) >> 16); + + cmdstat |= PCI_COMMAND_IO; + } + + /* Enable memory and I/O accesses, enable bus master */ + pci_hose_write_config_word(hose, dev, PCI_COMMAND, + cmdstat | PCI_COMMAND_MASTER); +} + +void pciauto_postscan_setup_bridge(struct pci_controller *hose, + pci_dev_t dev, int sub_bus) +{ + struct pci_region *pci_mem = hose->pci_mem; + struct pci_region *pci_prefetch = hose->pci_prefetch; + struct pci_region *pci_io = hose->pci_io; + + /* Configure bus number registers */ + pci_hose_write_config_byte(hose, dev, PCI_SUBORDINATE_BUS, + sub_bus - hose->first_busno); + + if (pci_mem) { + /* Round memory allocator to 1MB boundary */ + pciauto_region_align(pci_mem, 0x100000); + + pci_hose_write_config_word(hose, dev, PCI_MEMORY_LIMIT, + (pci_mem->bus_lower - 1) >> 16); + } + + if (pci_prefetch) { + /* Round memory allocator to 1MB boundary */ + pciauto_region_align(pci_prefetch, 0x100000); + + pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_LIMIT, + (pci_prefetch->bus_lower - 1) >> 16); + } + + if (pci_io) { + /* Round I/O allocator to 4KB boundary */ + pciauto_region_align(pci_io, 0x1000); + + pci_hose_write_config_byte(hose, dev, PCI_IO_LIMIT, + ((pci_io->bus_lower - 1) & 0x0000f000) >> 8); + pci_hose_write_config_word(hose, dev, PCI_IO_LIMIT_UPPER16, + ((pci_io->bus_lower - 1) & 0xffff0000) >> 16); + } +} + +/* + * + */ + +void pciauto_config_init(struct pci_controller *hose) +{ + int i; + + hose->pci_io = hose->pci_mem = hose->pci_prefetch = NULL; + + for (i = 0; i < hose->region_count; i++) { + switch(hose->regions[i].flags) { + case PCI_REGION_IO: + if (!hose->pci_io || + hose->pci_io->size < hose->regions[i].size) + hose->pci_io = hose->regions + i; + break; + case PCI_REGION_MEM: + if (!hose->pci_mem || + hose->pci_mem->size < hose->regions[i].size) + hose->pci_mem = hose->regions + i; + break; + case (PCI_REGION_MEM | PCI_REGION_PREFETCH): + if (!hose->pci_prefetch || + hose->pci_prefetch->size < hose->regions[i].size) + hose->pci_prefetch = hose->regions + i; + break; + } + } + + + if (hose->pci_mem) { + pciauto_region_init(hose->pci_mem); + + DEBUGF("PCI Autoconfig: Bus Memory region: [0x%llx-0x%llx],\n" + "\t\tPhysical Memory [%llx-%llxx]\n", + (u64)hose->pci_mem->bus_start, + (u64)(hose->pci_mem->bus_start + hose->pci_mem->size - 1), + (u64)hose->pci_mem->phys_start, + (u64)(hose->pci_mem->phys_start + hose->pci_mem->size - 1)); + } + + if (hose->pci_prefetch) { + pciauto_region_init(hose->pci_prefetch); + + DEBUGF("PCI Autoconfig: Bus Prefetchable Mem: [0x%llx-0x%llx],\n" + "\t\tPhysical Memory [%llx-%llx]\n", + (u64)hose->pci_prefetch->bus_start, + (u64)(hose->pci_prefetch->bus_start + + hose->pci_prefetch->size - 1), + (u64)hose->pci_prefetch->phys_start, + (u64)(hose->pci_prefetch->phys_start + + hose->pci_prefetch->size - 1)); + } + + if (hose->pci_io) { + pciauto_region_init(hose->pci_io); + + DEBUGF("PCI Autoconfig: Bus I/O region: [0x%llx-0x%llx],\n" + "\t\tPhysical Memory: [%llx-%llx]\n", + (u64)hose->pci_io->bus_start, + (u64)(hose->pci_io->bus_start + hose->pci_io->size - 1), + (u64)hose->pci_io->phys_start, + (u64)(hose->pci_io->phys_start + hose->pci_io->size - 1)); + + } +} + +/* + * HJF: Changed this to return int. I think this is required + * to get the correct result when scanning bridges + */ +int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev) +{ + unsigned int sub_bus = PCI_BUS(dev); + unsigned short class; + unsigned char prg_iface; + int n; + + pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class); + + switch (class) { + case PCI_CLASS_BRIDGE_PCI: + hose->current_busno++; + pciauto_setup_device(hose, dev, 2, hose->pci_mem, + hose->pci_prefetch, hose->pci_io); + + DEBUGF("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_DEV(dev)); + + /* Passing in current_busno allows for sibling P2P bridges */ + pciauto_prescan_setup_bridge(hose, dev, hose->current_busno); + /* + * need to figure out if this is a subordinate bridge on the bus + * to be able to properly set the pri/sec/sub bridge registers. + */ + n = pci_hose_scan_bus(hose, hose->current_busno); + + /* figure out the deepest we've gone for this leg */ + sub_bus = max(n, sub_bus); + pciauto_postscan_setup_bridge(hose, dev, sub_bus); + + sub_bus = hose->current_busno; + break; + + case PCI_CLASS_STORAGE_IDE: + pci_hose_read_config_byte(hose, dev, PCI_CLASS_PROG, &prg_iface); + if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) { + DEBUGF("PCI Autoconfig: Skipping legacy mode IDE controller\n"); + return sub_bus; + } + + pciauto_setup_device(hose, dev, 6, hose->pci_mem, + hose->pci_prefetch, hose->pci_io); + break; + + case PCI_CLASS_BRIDGE_CARDBUS: + /* + * just do a minimal setup of the bridge, + * let the OS take care of the rest + */ + pciauto_setup_device(hose, dev, 0, hose->pci_mem, + hose->pci_prefetch, hose->pci_io); + + DEBUGF("PCI Autoconfig: Found P2CardBus bridge, device %d\n", + PCI_DEV(dev)); + + hose->current_busno++; + break; + +#if defined(CONFIG_PCIAUTO_SKIP_HOST_BRIDGE) + case PCI_CLASS_BRIDGE_OTHER: + DEBUGF("PCI Autoconfig: Skipping bridge device %d\n", + PCI_DEV(dev)); + break; +#endif +#if defined(CONFIG_MPC834x) && !defined(CONFIG_VME8349) + case PCI_CLASS_BRIDGE_OTHER: + /* + * The host/PCI bridge 1 seems broken in 8349 - it presents + * itself as 'PCI_CLASS_BRIDGE_OTHER' and appears as an _agent_ + * device claiming resources io/mem/irq.. we only allow for + * the PIMMR window to be allocated (BAR0 - 1MB size) + */ + DEBUGF("PCI Autoconfig: Broken bridge found, only minimal config\n"); + pciauto_setup_device(hose, dev, 0, hose->pci_mem, + hose->pci_prefetch, hose->pci_io); + break; +#endif + + case PCI_CLASS_PROCESSOR_POWERPC: /* an agent or end-point */ + DEBUGF("PCI AutoConfig: Found PowerPC device\n"); + + default: + pciauto_setup_device(hose, dev, 6, hose->pci_mem, + hose->pci_prefetch, hose->pci_io); + break; + } + + return sub_bus; +} diff --git a/qemu/roms/u-boot/drivers/pci/pci_ftpci100.c b/qemu/roms/u-boot/drivers/pci/pci_ftpci100.c new file mode 100644 index 000000000..5ee8b6b4d --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pci_ftpci100.c @@ -0,0 +1,318 @@ +/* + * Faraday FTPCI100 PCI Bridge Controller Device Driver Implementation + * + * Copyright (C) 2011 Andes Technology Corporation + * Gavin Guo, Andes Technology Corporation + * Macpaul Lin, Andes Technology Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include + +#include + +#include +#include /* u32, u16.... used by pci.h */ + +struct ftpci100_data { + unsigned int reg_base; + unsigned int io_base; + unsigned int mem_base; + unsigned int mmio_base; + unsigned int ndevs; +}; + +static struct pci_config devs[FTPCI100_MAX_FUNCTIONS]; +static struct pci_controller local_hose; + +static void setup_pci_bar(unsigned int bus, unsigned int dev, unsigned func, + unsigned char header, struct ftpci100_data *priv) +{ + struct pci_controller *hose = (struct pci_controller *)&local_hose; + unsigned int i, tmp32, bar_no, iovsmem = 1; + pci_dev_t dev_nu; + + /* A device is present, add an entry to the array */ + devs[priv->ndevs].bus = bus; + devs[priv->ndevs].dev = dev; + devs[priv->ndevs].func = func; + + dev_nu = PCI_BDF(bus, dev, func); + + if ((header & 0x7f) == 0x01) + /* PCI-PCI Bridge */ + bar_no = 2; + else + bar_no = 6; + + /* Allocate address spaces by configuring BARs */ + for (i = 0; i < bar_no; i++) { + pci_hose_write_config_dword(hose, dev_nu, + PCI_BASE_ADDRESS_0 + i * 4, 0xffffffff); + pci_hose_read_config_dword(hose, dev_nu, + PCI_BASE_ADDRESS_0 + i * 4, &tmp32); + + if (tmp32 == 0x0) + continue; + + /* IO space */ + if (tmp32 & 0x1) { + iovsmem = 0; + unsigned int size_mask = ~(tmp32 & 0xfffffffc); + + if (priv->io_base & size_mask) + priv->io_base = (priv->io_base & ~size_mask) + \ + size_mask + 1; + + devs[priv->ndevs].bar[i].addr = priv->io_base; + devs[priv->ndevs].bar[i].size = size_mask + 1; + + pci_hose_write_config_dword(hose, dev_nu, + PCI_BASE_ADDRESS_0 + i * 4, + priv->io_base); + + debug("Allocated IO address 0x%X-" \ + "0x%X for Bus %d, Device %d, Function %d\n", + priv->io_base, + priv->io_base + size_mask, bus, dev, func); + + priv->io_base += size_mask + 1; + } else { + /* Memory space */ + unsigned int is_64bit = ((tmp32 & 0x6) == 0x4); + unsigned int is_pref = tmp32 & 0x8; + unsigned int size_mask = ~(tmp32 & 0xfffffff0); + unsigned int alloc_base; + unsigned int *addr_mem_base; + + if (is_pref) + addr_mem_base = &priv->mem_base; + else + addr_mem_base = &priv->mmio_base; + + alloc_base = *addr_mem_base; + + if (alloc_base & size_mask) + alloc_base = (alloc_base & ~size_mask) \ + + size_mask + 1; + + pci_hose_write_config_dword(hose, dev_nu, + PCI_BASE_ADDRESS_0 + i * 4, alloc_base); + + debug("Allocated %s address 0x%X-" \ + "0x%X for Bus %d, Device %d, Function %d\n", + is_pref ? "MEM" : "MMIO", alloc_base, + alloc_base + size_mask, bus, dev, func); + + devs[priv->ndevs].bar[i].addr = alloc_base; + devs[priv->ndevs].bar[i].size = size_mask + 1; + + debug("BAR address BAR size\n"); + debug("%010x %08d\n", + devs[priv->ndevs].bar[0].addr, + devs[priv->ndevs].bar[0].size); + + alloc_base += size_mask + 1; + *addr_mem_base = alloc_base; + + if (is_64bit) { + i++; + pci_hose_write_config_dword(hose, dev_nu, + PCI_BASE_ADDRESS_0 + i * 4, 0x0); + } + } + } + + /* Enable Bus Master, Memory Space, and IO Space */ + pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32); + pci_hose_write_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, 0x08); + pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32); + + pci_hose_read_config_dword(hose, dev_nu, PCI_COMMAND, &tmp32); + + tmp32 &= 0xffff; + + if (iovsmem == 0) + tmp32 |= 0x5; + else + tmp32 |= 0x6; + + pci_hose_write_config_dword(hose, dev_nu, PCI_COMMAND, tmp32); +} + +static void pci_bus_scan(struct ftpci100_data *priv) +{ + struct pci_controller *hose = (struct pci_controller *)&local_hose; + unsigned int bus, dev, func; + pci_dev_t dev_nu; + unsigned int data32; + unsigned int tmp; + unsigned char header; + unsigned char int_pin; + unsigned int niobars; + unsigned int nmbars; + + priv->ndevs = 1; + + nmbars = 0; + niobars = 0; + + for (bus = 0; bus < MAX_BUS_NUM; bus++) + for (dev = 0; dev < MAX_DEV_NUM; dev++) + for (func = 0; func < MAX_FUN_NUM; func++) { + dev_nu = PCI_BDF(bus, dev, func); + pci_hose_read_config_dword(hose, dev_nu, + PCI_VENDOR_ID, &data32); + + /* + * some broken boards return 0 or ~0, + * if a slot is empty. + */ + if (data32 == 0xffffffff || + data32 == 0x00000000 || + data32 == 0x0000ffff || + data32 == 0xffff0000) + continue; + + pci_hose_read_config_dword(hose, dev_nu, + PCI_HEADER_TYPE, &tmp); + header = (unsigned char)tmp; + setup_pci_bar(bus, dev, func, header, priv); + + devs[priv->ndevs].v_id = (u16)(data32 & \ + 0x0000ffff); + + devs[priv->ndevs].d_id = (u16)((data32 & \ + 0xffff0000) >> 16); + + /* Figure out what INTX# line the card uses */ + pci_hose_read_config_byte(hose, dev_nu, + PCI_INTERRUPT_PIN, &int_pin); + + /* assign the appropriate irq line */ + if (int_pin > PCI_IRQ_LINES) { + printf("more irq lines than expect\n"); + } else if (int_pin != 0) { + /* This device uses an interrupt line */ + devs[priv->ndevs].pin = int_pin; + } + + pci_hose_read_config_dword(hose, dev_nu, + PCI_CLASS_DEVICE, &data32); + + debug("%06d %03d %03d " \ + "%04d %08x %08x " \ + "%03d %08x %06d %08x\n", + priv->ndevs, devs[priv->ndevs].bus, + devs[priv->ndevs].dev, + devs[priv->ndevs].func, + devs[priv->ndevs].d_id, + devs[priv->ndevs].v_id, + devs[priv->ndevs].pin, + devs[priv->ndevs].bar[0].addr, + devs[priv->ndevs].bar[0].size, + data32 >> 8); + + priv->ndevs++; + } +} + +static void ftpci_preinit(struct ftpci100_data *priv) +{ + struct ftpci100_ahbc *ftpci100; + struct pci_controller *hose = (struct pci_controller *)&local_hose; + u32 pci_config_addr; + u32 pci_config_data; + + priv->reg_base = CONFIG_FTPCI100_BASE; + priv->io_base = CONFIG_FTPCI100_BASE + CONFIG_FTPCI100_IO_SIZE; + priv->mmio_base = CONFIG_FTPCI100_MEM_BASE; + priv->mem_base = CONFIG_FTPCI100_MEM_BASE + CONFIG_FTPCI100_MEM_SIZE; + + ftpci100 = (struct ftpci100_ahbc *)priv->reg_base; + + pci_config_addr = (u32) &ftpci100->conf; + pci_config_data = (u32) &ftpci100->data; + + /* print device name */ + printf("FTPCI100\n"); + + /* dump basic configuration */ + debug("%s: Config addr is %08X, data port is %08X\n", + __func__, pci_config_addr, pci_config_data); + + /* PCI memory space */ + pci_set_region(hose->regions + 0, + CONFIG_PCI_MEM_BUS, + CONFIG_PCI_MEM_PHYS, + CONFIG_PCI_MEM_SIZE, + PCI_REGION_MEM); + hose->region_count++; + + /* PCI IO space */ + pci_set_region(hose->regions + 1, + CONFIG_PCI_IO_BUS, + CONFIG_PCI_IO_PHYS, + CONFIG_PCI_IO_SIZE, + PCI_REGION_IO); + hose->region_count++; + +#if defined(CONFIG_PCI_SYS_BUS) + /* PCI System Memory space */ + pci_set_region(hose->regions + 2, + CONFIG_PCI_SYS_BUS, + CONFIG_PCI_SYS_PHYS, + CONFIG_PCI_SYS_SIZE, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + hose->region_count++; +#endif + + /* setup indirect read/write function */ + pci_setup_indirect(hose, pci_config_addr, pci_config_data); + + /* register hose */ + pci_register_hose(hose); +} + +void pci_ftpci_init(void) +{ + struct ftpci100_data *priv = NULL; + struct pci_controller *hose = (struct pci_controller *)&local_hose; + pci_dev_t bridge_num; + + struct pci_device_id bridge_ids[] = { + {FTPCI100_BRIDGE_VENDORID, FTPCI100_BRIDGE_DEVICEID}, + {0, 0} + }; + + priv = malloc(sizeof(struct ftpci100_data)); + + if (!priv) { + printf("%s(): failed to malloc priv\n", __func__); + return; + } + + memset(priv, 0, sizeof(struct ftpci100_data)); + + ftpci_preinit(priv); + + debug("Device bus dev func deviceID vendorID pin address" \ + " size class\n"); + + pci_bus_scan(priv); + + /* + * Setup the PCI Bridge Window to 1GB, + * it will cause USB OHCI Host controller Unrecoverable Error + * if it is not set. + */ + bridge_num = pci_find_devices(bridge_ids, 0); + if (bridge_num == -1) { + printf("PCI Bridge not found\n"); + return; + } + pci_hose_write_config_dword(hose, bridge_num, PCI_MEM_BASE_SIZE1, + FTPCI100_BASE_ADR_SIZE(1024)); +} diff --git a/qemu/roms/u-boot/drivers/pci/pci_gt64120.c b/qemu/roms/u-boot/drivers/pci/pci_gt64120.c new file mode 100644 index 000000000..4756f1424 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pci_gt64120.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2013 Gabor Juhos + * + * Based on the Linux implementation. + * Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc. + * Authors: Carsten Langgaard + * Maciej W. Rozycki + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include + +#include + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +struct gt64120_regs { + u8 unused_000[0xc18]; + u32 intrcause; + u8 unused_c1c[0x0dc]; + u32 pci0_cfgaddr; + u32 pci0_cfgdata; +}; + +struct gt64120_pci_controller { + struct pci_controller hose; + struct gt64120_regs *regs; +}; + +static inline struct gt64120_pci_controller * +hose_to_gt64120(struct pci_controller *hose) +{ + return container_of(hose, struct gt64120_pci_controller, hose); +} + +#define GT_INTRCAUSE_ABORT_BITS \ + (GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT) + +static int gt_config_access(struct gt64120_pci_controller *gt, + unsigned char access_type, pci_dev_t bdf, + int where, u32 *data) +{ + unsigned int bus = PCI_BUS(bdf); + unsigned int dev = PCI_DEV(bdf); + unsigned int devfn = PCI_DEV(bdf) << 3 | PCI_FUNC(bdf); + u32 intr; + u32 addr; + u32 val; + + if (bus == 0 && dev >= 31) { + /* Because of a bug in the galileo (for slot 31). */ + return -1; + } + + if (access_type == PCI_ACCESS_WRITE) + debug("PCI WR %02x:%02x.%x reg:%02d data:%08x\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), where, *data); + + /* Clear cause register bits */ + writel(~GT_INTRCAUSE_ABORT_BITS, >->regs->intrcause); + + addr = GT_PCI0_CFGADDR_CONFIGEN_BIT; + addr |= bus << GT_PCI0_CFGADDR_BUSNUM_SHF; + addr |= devfn << GT_PCI0_CFGADDR_FUNCTNUM_SHF; + addr |= (where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF; + + /* Setup address */ + writel(addr, >->regs->pci0_cfgaddr); + + if (access_type == PCI_ACCESS_WRITE) { + if (bus == 0 && dev == 0) { + /* + * The Galileo system controller is acting + * differently than other devices. + */ + val = *data; + } else { + val = cpu_to_le32(*data); + } + + writel(val, >->regs->pci0_cfgdata); + } else { + val = readl(>->regs->pci0_cfgdata); + + if (bus == 0 && dev == 0) { + /* + * The Galileo system controller is acting + * differently than other devices. + */ + *data = val; + } else { + *data = le32_to_cpu(val); + } + } + + /* Check for master or target abort */ + intr = readl(>->regs->intrcause); + if (intr & GT_INTRCAUSE_ABORT_BITS) { + /* Error occurred, clear abort bits */ + writel(~GT_INTRCAUSE_ABORT_BITS, >->regs->intrcause); + return -1; + } + + if (access_type == PCI_ACCESS_READ) + debug("PCI RD %02x:%02x.%x reg:%02d data:%08x\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), where, *data); + + return 0; +} + +static int gt_read_config_dword(struct pci_controller *hose, pci_dev_t dev, + int where, u32 *value) +{ + struct gt64120_pci_controller *gt = hose_to_gt64120(hose); + + *value = 0xffffffff; + return gt_config_access(gt, PCI_ACCESS_READ, dev, where, value); +} + +static int gt_write_config_dword(struct pci_controller *hose, pci_dev_t dev, + int where, u32 value) +{ + struct gt64120_pci_controller *gt = hose_to_gt64120(hose); + u32 data = value; + + return gt_config_access(gt, PCI_ACCESS_WRITE, dev, where, &data); +} + +void gt64120_pci_init(void *regs, unsigned long sys_bus, unsigned long sys_phys, + unsigned long sys_size, unsigned long mem_bus, + unsigned long mem_phys, unsigned long mem_size, + unsigned long io_bus, unsigned long io_phys, + unsigned long io_size) +{ + static struct gt64120_pci_controller global_gt; + struct gt64120_pci_controller *gt; + struct pci_controller *hose; + + gt = &global_gt; + gt->regs = regs; + + hose = >->hose; + + hose->first_busno = 0; + hose->last_busno = 0; + + /* System memory space */ + pci_set_region(&hose->regions[0], sys_bus, sys_phys, sys_size, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + /* PCI memory space */ + pci_set_region(&hose->regions[1], mem_bus, mem_phys, mem_size, + PCI_REGION_MEM); + + /* PCI I/O space */ + pci_set_region(&hose->regions[2], io_bus, io_phys, io_size, + PCI_REGION_IO); + + hose->region_count = 3; + + pci_set_ops(hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + gt_read_config_dword, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + gt_write_config_dword); + + pci_register_hose(hose); + hose->last_busno = pci_hose_scan(hose); +} diff --git a/qemu/roms/u-boot/drivers/pci/pci_indirect.c b/qemu/roms/u-boot/drivers/pci/pci_indirect.c new file mode 100644 index 000000000..aee0bd6d9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pci_indirect.c @@ -0,0 +1,125 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#if !defined(__I386__) + +#include +#include +#include + +#define cfg_read(val, addr, type, op) *val = op((type)(addr)) +#define cfg_write(val, addr, type, op) op((type *)(addr), (val)) + +#if defined(CONFIG_MPC8260) +#define INDIRECT_PCI_OP(rw, size, type, op, mask) \ +static int \ +indirect_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + u32 b, d,f; \ + b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev); \ + b = b - hose->first_busno; \ + dev = PCI_BDF(b, d, f); \ + out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); \ + sync(); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + return 0; \ +} +#elif defined(CONFIG_E500) || defined(CONFIG_MPC86xx) +#define INDIRECT_PCI_OP(rw, size, type, op, mask) \ +static int \ +indirect_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + u32 b, d,f; \ + b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev); \ + b = b - hose->first_busno; \ + dev = PCI_BDF(b, d, f); \ + *(hose->cfg_addr) = dev | (offset & 0xfc) | ((offset & 0xf00) << 16) | 0x80000000; \ + sync(); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + return 0; \ +} +#elif defined(CONFIG_440GX) || defined(CONFIG_440GP) || defined(CONFIG_440SP) || \ + defined(CONFIG_440SPE) || defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define INDIRECT_PCI_OP(rw, size, type, op, mask) \ +static int \ +indirect_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + u32 b, d,f; \ + b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev); \ + b = b - hose->first_busno; \ + dev = PCI_BDF(b, d, f); \ + if (PCI_BUS(dev) > 0) \ + out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000001); \ + else \ + out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + return 0; \ +} +#else +#define INDIRECT_PCI_OP(rw, size, type, op, mask) \ +static int \ +indirect_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + u32 b, d,f; \ + b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev); \ + b = b - hose->first_busno; \ + dev = PCI_BDF(b, d, f); \ + out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + return 0; \ +} +#endif + +#define INDIRECT_PCI_OP_ERRATA6(rw, size, type, op, mask) \ +static int \ +indirect_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + unsigned int msr = mfmsr(); \ + mtmsr(msr & ~(MSR_EE | MSR_CE)); \ + out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + out_le32(hose->cfg_addr, 0x00000000); \ + mtmsr(msr); \ + return 0; \ +} + +INDIRECT_PCI_OP(read, byte, u8 *, in_8, 3) +INDIRECT_PCI_OP(read, word, u16 *, in_le16, 2) +INDIRECT_PCI_OP(read, dword, u32 *, in_le32, 0) +#ifdef CONFIG_405GP +INDIRECT_PCI_OP_ERRATA6(write, byte, u8, out_8, 3) +INDIRECT_PCI_OP_ERRATA6(write, word, u16, out_le16, 2) +INDIRECT_PCI_OP_ERRATA6(write, dword, u32, out_le32, 0) +#else +INDIRECT_PCI_OP(write, byte, u8, out_8, 3) +INDIRECT_PCI_OP(write, word, u16, out_le16, 2) +INDIRECT_PCI_OP(write, dword, u32, out_le32, 0) +#endif + +void pci_setup_indirect(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) +{ + pci_set_ops(hose, + indirect_read_config_byte, + indirect_read_config_word, + indirect_read_config_dword, + indirect_write_config_byte, + indirect_write_config_word, + indirect_write_config_dword); + + hose->cfg_addr = (unsigned int *) cfg_addr; + hose->cfg_data = (unsigned char *) cfg_data; +} + +#endif /* !__I386__ */ diff --git a/qemu/roms/u-boot/drivers/pci/pci_msc01.c b/qemu/roms/u-boot/drivers/pci/pci_msc01.c new file mode 100644 index 000000000..284ffa09b --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pci_msc01.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013 Imagination Technologies + * Author: Paul Burton + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +struct msc01_pci_controller { + struct pci_controller hose; + void *base; +}; + +static inline struct msc01_pci_controller * +hose_to_msc01(struct pci_controller *hose) +{ + return container_of(hose, struct msc01_pci_controller, hose); +} + +static int msc01_config_access(struct msc01_pci_controller *msc01, + unsigned char access_type, pci_dev_t bdf, + int where, u32 *data) +{ + const u32 aborts = MSC01_PCI_INTSTAT_MA_MSK | MSC01_PCI_INTSTAT_TA_MSK; + void *intstat = msc01->base + MSC01_PCI_INTSTAT_OFS; + void *cfgdata = msc01->base + MSC01_PCI_CFGDATA_OFS; + unsigned int bus = PCI_BUS(bdf); + unsigned int dev = PCI_DEV(bdf); + unsigned int devfn = PCI_DEV(bdf) << 3 | PCI_FUNC(bdf); + + /* clear abort status */ + __raw_writel(aborts, intstat); + + /* setup address */ + __raw_writel((bus << MSC01_PCI_CFGADDR_BNUM_SHF) | + (dev << MSC01_PCI_CFGADDR_DNUM_SHF) | + (devfn << MSC01_PCI_CFGADDR_FNUM_SHF) | + ((where / 4) << MSC01_PCI_CFGADDR_RNUM_SHF), + msc01->base + MSC01_PCI_CFGADDR_OFS); + + /* perform access */ + if (access_type == PCI_ACCESS_WRITE) + __raw_writel(*data, cfgdata); + else + *data = __raw_readl(cfgdata); + + /* check for aborts */ + if (__raw_readl(intstat) & aborts) { + /* clear abort status */ + __raw_writel(aborts, intstat); + return -1; + } + + return 0; +} + +static int msc01_read_config_dword(struct pci_controller *hose, pci_dev_t dev, + int where, u32 *value) +{ + struct msc01_pci_controller *msc01 = hose_to_msc01(hose); + + *value = 0xffffffff; + return msc01_config_access(msc01, PCI_ACCESS_READ, dev, where, value); +} + +static int msc01_write_config_dword(struct pci_controller *hose, pci_dev_t dev, + int where, u32 value) +{ + struct msc01_pci_controller *gt = hose_to_msc01(hose); + u32 data = value; + + return msc01_config_access(gt, PCI_ACCESS_WRITE, dev, where, &data); +} + +void msc01_pci_init(void *base, unsigned long sys_bus, unsigned long sys_phys, + unsigned long sys_size, unsigned long mem_bus, + unsigned long mem_phys, unsigned long mem_size, + unsigned long io_bus, unsigned long io_phys, + unsigned long io_size) +{ + static struct msc01_pci_controller global_msc01; + struct msc01_pci_controller *msc01; + struct pci_controller *hose; + + msc01 = &global_msc01; + msc01->base = base; + + hose = &msc01->hose; + + hose->first_busno = 0; + hose->last_busno = 0; + + /* System memory space */ + pci_set_region(&hose->regions[0], sys_bus, sys_phys, sys_size, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + /* PCI memory space */ + pci_set_region(&hose->regions[1], mem_bus, mem_phys, mem_size, + PCI_REGION_MEM); + + /* PCI I/O space */ + pci_set_region(&hose->regions[2], io_bus, io_phys, io_size, + PCI_REGION_IO); + + hose->region_count = 3; + + pci_set_ops(hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + msc01_read_config_dword, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + msc01_write_config_dword); + + pci_register_hose(hose); + hose->last_busno = pci_hose_scan(hose); +} diff --git a/qemu/roms/u-boot/drivers/pci/pci_sh4.c b/qemu/roms/u-boot/drivers/pci/pci_sh4.c new file mode 100644 index 000000000..d7f43c2bd --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pci_sh4.c @@ -0,0 +1,82 @@ +/* + * SH4 PCI Controller (PCIC) for U-Boot. + * (C) Dustin McIntire (dustin@sensoria.com) + * (C) 2007,2008 Nobuhiro Iwamatsu + * (C) 2008 Yusuke Goda + * + * u-boot/arch/sh/cpu/sh4/pci-sh4.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include +#include +#include +#include + +int pci_sh4_init(struct pci_controller *hose) +{ + hose->first_busno = 0; + hose->region_count = 0; + hose->last_busno = 0xff; + + /* PCI memory space */ + pci_set_region(hose->regions + 0, + CONFIG_PCI_MEM_BUS, + CONFIG_PCI_MEM_PHYS, + CONFIG_PCI_MEM_SIZE, + PCI_REGION_MEM); + hose->region_count++; + + /* PCI IO space */ + pci_set_region(hose->regions + 1, + CONFIG_PCI_IO_BUS, + CONFIG_PCI_IO_PHYS, + CONFIG_PCI_IO_SIZE, + PCI_REGION_IO); + hose->region_count++; + +#if defined(CONFIG_PCI_SYS_BUS) + /* PCI System Memory space */ + pci_set_region(hose->regions + 2, + CONFIG_PCI_SYS_BUS, + CONFIG_PCI_SYS_PHYS, + CONFIG_PCI_SYS_SIZE, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + hose->region_count++; +#endif + + udelay(1000); + + pci_set_ops(hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + pci_sh4_read_config_dword, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + pci_sh4_write_config_dword); + + pci_register_hose(hose); + + udelay(1000); + +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + hose->last_busno = pci_hose_scan(hose); + return 0; +} + +int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) +{ + return 0; +} + +#ifdef CONFIG_PCI_SCAN_SHOW +int pci_print_dev(struct pci_controller *hose, pci_dev_t dev) +{ + return 1; +} +#endif /* CONFIG_PCI_SCAN_SHOW */ diff --git a/qemu/roms/u-boot/drivers/pci/pci_sh7751.c b/qemu/roms/u-boot/drivers/pci/pci_sh7751.c new file mode 100644 index 000000000..f189ed89c --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pci_sh7751.c @@ -0,0 +1,187 @@ +/* + * SH7751 PCI Controller (PCIC) for U-Boot. + * (C) Dustin McIntire (dustin@sensoria.com) + * (C) 2007,2008 Nobuhiro Iwamatsu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/* Register addresses and such */ +#define SH7751_BCR1 (vu_long *)0xFF800000 +#define SH7751_BCR2 (vu_short *)0xFF800004 +#define SH7751_WCR1 (vu_long *)0xFF800008 +#define SH7751_WCR2 (vu_long *)0xFF80000C +#define SH7751_WCR3 (vu_long *)0xFF800010 +#define SH7751_MCR (vu_long *)0xFF800014 +#define SH7751_BCR3 (vu_short *)0xFF800050 +#define SH7751_PCICONF0 (vu_long *)0xFE200000 +#define SH7751_PCICONF1 (vu_long *)0xFE200004 +#define SH7751_PCICONF2 (vu_long *)0xFE200008 +#define SH7751_PCICONF3 (vu_long *)0xFE20000C +#define SH7751_PCICONF4 (vu_long *)0xFE200010 +#define SH7751_PCICONF5 (vu_long *)0xFE200014 +#define SH7751_PCICONF6 (vu_long *)0xFE200018 +#define SH7751_PCICR (vu_long *)0xFE200100 +#define SH7751_PCILSR0 (vu_long *)0xFE200104 +#define SH7751_PCILSR1 (vu_long *)0xFE200108 +#define SH7751_PCILAR0 (vu_long *)0xFE20010C +#define SH7751_PCILAR1 (vu_long *)0xFE200110 +#define SH7751_PCIMBR (vu_long *)0xFE2001C4 +#define SH7751_PCIIOBR (vu_long *)0xFE2001C8 +#define SH7751_PCIPINT (vu_long *)0xFE2001CC +#define SH7751_PCIPINTM (vu_long *)0xFE2001D0 +#define SH7751_PCICLKR (vu_long *)0xFE2001D4 +#define SH7751_PCIBCR1 (vu_long *)0xFE2001E0 +#define SH7751_PCIBCR2 (vu_long *)0xFE2001E4 +#define SH7751_PCIWCR1 (vu_long *)0xFE2001E8 +#define SH7751_PCIWCR2 (vu_long *)0xFE2001EC +#define SH7751_PCIWCR3 (vu_long *)0xFE2001F0 +#define SH7751_PCIMCR (vu_long *)0xFE2001F4 +#define SH7751_PCIBCR3 (vu_long *)0xFE2001F8 + +#define BCR1_BREQEN 0x00080000 +#define PCI_SH7751_ID 0x35051054 +#define PCI_SH7751R_ID 0x350E1054 +#define SH7751_PCICONF1_WCC 0x00000080 +#define SH7751_PCICONF1_PER 0x00000040 +#define SH7751_PCICONF1_BUM 0x00000004 +#define SH7751_PCICONF1_MES 0x00000002 +#define SH7751_PCICONF1_CMDS 0x000000C6 +#define SH7751_PCI_HOST_BRIDGE 0x6 +#define SH7751_PCICR_PREFIX 0xa5000000 +#define SH7751_PCICR_PRST 0x00000002 +#define SH7751_PCICR_CFIN 0x00000001 +#define SH7751_PCIPINT_D3 0x00000002 +#define SH7751_PCIPINT_D0 0x00000001 +#define SH7751_PCICLKR_PREFIX 0xa5000000 + +#define SH7751_PCI_MEM_BASE 0xFD000000 +#define SH7751_PCI_MEM_SIZE 0x01000000 +#define SH7751_PCI_IO_BASE 0xFE240000 +#define SH7751_PCI_IO_SIZE 0x00040000 + +#define SH7751_CS3_BASE_ADDR 0x0C000000 +#define SH7751_P2CS3_BASE_ADDR 0xAC000000 + +#define SH7751_PCIPAR (vu_long *)0xFE2001C0 +#define SH7751_PCIPDR (vu_long *)0xFE200220 + +#define p4_in(addr) (*addr) +#define p4_out(data, addr) (*addr) = (data) + +/* Double word */ +int pci_sh4_read_config_dword(struct pci_controller *hose, + pci_dev_t dev, int offset, u32 *value) +{ + u32 par_data = 0x80000000 | dev; + + p4_out(par_data | (offset & 0xfc), SH7751_PCIPAR); + *value = p4_in(SH7751_PCIPDR); + + return 0; +} + +int pci_sh4_write_config_dword(struct pci_controller *hose, + pci_dev_t dev, int offset, u32 value) +{ + u32 par_data = 0x80000000 | dev; + + p4_out(par_data | (offset & 0xfc), SH7751_PCIPAR); + p4_out(value, SH7751_PCIPDR); + + return 0; +} + +int pci_sh7751_init(struct pci_controller *hose) +{ + /* Double-check that we're a 7751 or 7751R chip */ + if (p4_in(SH7751_PCICONF0) != PCI_SH7751_ID + && p4_in(SH7751_PCICONF0) != PCI_SH7751R_ID) { + printf("PCI: Unknown PCI host bridge.\n"); + return 1; + } + printf("PCI: SH7751 PCI host bridge found.\n"); + + /* Double-check some BSC config settings */ + /* (Area 3 non-MPX 32-bit, PCI bus pins) */ + if ((p4_in(SH7751_BCR1) & 0x20008) == 0x20000) { + printf("SH7751_BCR1 value is wrong(0x%08X)\n", + (unsigned int)p4_in(SH7751_BCR1)); + return 2; + } + if ((p4_in(SH7751_BCR2) & 0xC0) != 0xC0) { + printf("SH7751_BCR2 value is wrong(0x%08X)\n", + (unsigned int)p4_in(SH7751_BCR2)); + return 3; + } + if (p4_in(SH7751_BCR2) & 0x01) { + printf("SH7751_BCR2 value is wrong(0x%08X)\n", + (unsigned int)p4_in(SH7751_BCR2)); + return 4; + } + + /* Force BREQEN in BCR1 to allow PCIC access */ + p4_out((p4_in(SH7751_BCR1) | BCR1_BREQEN), SH7751_BCR1); + + /* Toggle PCI reset pin */ + p4_out((SH7751_PCICR_PREFIX | SH7751_PCICR_PRST), SH7751_PCICR); + udelay(32); + p4_out(SH7751_PCICR_PREFIX, SH7751_PCICR); + + /* Set cmd bits: WCC, PER, BUM, MES */ + /* (Addr/Data stepping, Parity enabled, Bus Master, Memory enabled) */ + p4_out(0xfb900047, SH7751_PCICONF1); /* K.Kino */ + + /* Define this host as the host bridge */ + p4_out((SH7751_PCI_HOST_BRIDGE << 24), SH7751_PCICONF2); + + /* Force PCI clock(s) on */ + p4_out(0, SH7751_PCICLKR); + p4_out(0x03, SH7751_PCICLKR); + + /* Clear powerdown IRQs, also mask them (unused) */ + p4_out((SH7751_PCIPINT_D0 | SH7751_PCIPINT_D3), SH7751_PCIPINT); + p4_out(0, SH7751_PCIPINTM); + + p4_out(0xab000001, SH7751_PCICONF4); + + /* Set up target memory mappings (for external DMA access) */ + /* Map both P0 and P2 range to Area 3 RAM for ease of use */ + p4_out((64 - 1) << 20, SH7751_PCILSR0); + p4_out(SH7751_CS3_BASE_ADDR, SH7751_PCILAR0); + p4_out(0, SH7751_PCILSR1); + p4_out(0, SH7751_PCILAR1); + p4_out(SH7751_CS3_BASE_ADDR, SH7751_PCICONF5); + p4_out(0xd0000000, SH7751_PCICONF6); + + /* Map memory window to same address on PCI bus */ + p4_out(SH7751_PCI_MEM_BASE, SH7751_PCIMBR); + + /* Map IO window to same address on PCI bus */ + p4_out(0x2000 & 0xfffc0000, SH7751_PCIIOBR); + + /* set BREQEN */ + p4_out(inl(SH7751_BCR1) | 0x00080000, SH7751_BCR1); + + /* Copy BSC registers into PCI BSC */ + p4_out(inl(SH7751_BCR1), SH7751_PCIBCR1); + p4_out(inw(SH7751_BCR2), SH7751_PCIBCR2); + p4_out(inw(SH7751_BCR3), SH7751_PCIBCR3); + p4_out(inl(SH7751_WCR1), SH7751_PCIWCR1); + p4_out(inl(SH7751_WCR2), SH7751_PCIWCR2); + p4_out(inl(SH7751_WCR3), SH7751_PCIWCR3); + p4_out(inl(SH7751_MCR), SH7751_PCIMCR); + + /* Finally, set central function init complete */ + p4_out((SH7751_PCICR_PREFIX | SH7751_PCICR_CFIN), SH7751_PCICR); + + pci_sh4_init(hose); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/pci/pci_sh7780.c b/qemu/roms/u-boot/drivers/pci/pci_sh7780.c new file mode 100644 index 000000000..5c739edad --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pci_sh7780.c @@ -0,0 +1,92 @@ +/* + * SH7780 PCI Controller (PCIC) for U-Boot. + * (C) Dustin McIntire (dustin@sensoria.com) + * (C) 2007,2008 Nobuhiro Iwamatsu + * (C) 2008 Yusuke Goda + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include +#include +#include +#include + +#define SH7780_VENDOR_ID 0x1912 +#define SH7780_DEVICE_ID 0x0002 +#define SH7780_PCICR_PREFIX 0xA5000000 +#define SH7780_PCICR_PFCS 0x00000800 +#define SH7780_PCICR_FTO 0x00000400 +#define SH7780_PCICR_PFE 0x00000200 +#define SH7780_PCICR_TBS 0x00000100 +#define SH7780_PCICR_ARBM 0x00000040 +#define SH7780_PCICR_IOCS 0x00000004 +#define SH7780_PCICR_PRST 0x00000002 +#define SH7780_PCICR_CFIN 0x00000001 + +#define p4_in(addr) (*(vu_long *)addr) +#define p4_out(data, addr) (*(vu_long *)addr) = (data) +#define p4_inw(addr) (*(vu_short *)addr) +#define p4_outw(data, addr) (*(vu_short *)addr) = (data) + +int pci_sh4_read_config_dword(struct pci_controller *hose, + pci_dev_t dev, int offset, u32 *value) +{ + u32 par_data = 0x80000000 | dev; + + p4_out(par_data | (offset & 0xfc), SH7780_PCIPAR); + *value = p4_in(SH7780_PCIPDR); + + return 0; +} + +int pci_sh4_write_config_dword(struct pci_controller *hose, + pci_dev_t dev, int offset, u32 value) +{ + u32 par_data = 0x80000000 | dev; + + p4_out(par_data | (offset & 0xfc), SH7780_PCIPAR); + p4_out(value, SH7780_PCIPDR); + return 0; +} + +int pci_sh7780_init(struct pci_controller *hose) +{ + p4_out(0x01, SH7780_PCIECR); + + if (p4_inw(SH7780_PCIVID) != SH7780_VENDOR_ID + && p4_inw(SH7780_PCIDID) != SH7780_DEVICE_ID) { + printf("PCI: Unknown PCI host bridge.\n"); + return -1; + } + printf("PCI: SH7780 PCI host bridge found.\n"); + + /* Toggle PCI reset pin */ + p4_out((SH7780_PCICR_PREFIX | SH7780_PCICR_PRST), SH7780_PCICR); + udelay(100000); + p4_out(SH7780_PCICR_PREFIX, SH7780_PCICR); + p4_outw(0x0047, SH7780_PCICMD); + + p4_out(CONFIG_SH7780_PCI_LSR, SH7780_PCILSR0); + p4_out(CONFIG_SH7780_PCI_LAR, SH7780_PCILAR0); + p4_out(0x00000000, SH7780_PCILSR1); + p4_out(0, SH7780_PCILAR1); + p4_out(CONFIG_SH7780_PCI_BAR, SH7780_PCIMBAR0); + p4_out(0x00000000, SH7780_PCIMBAR1); + + p4_out(0xFD000000, SH7780_PCIMBR0); + p4_out(0x00FC0000, SH7780_PCIMBMR0); + + /* if use Operand Cache then enable PCICSCR Soonp bits. */ + p4_out(0x08000000, SH7780_PCICSAR0); + p4_out(0x0000001B, SH7780_PCICSCR0); /* Snoop bit :On */ + + p4_out((SH7780_PCICR_PREFIX | SH7780_PCICR_CFIN | SH7780_PCICR_ARBM + | SH7780_PCICR_FTO | SH7780_PCICR_PFCS | SH7780_PCICR_PFE), + SH7780_PCICR); + + pci_sh4_init(hose); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/pci/pcie_imx.c b/qemu/roms/u-boot/drivers/pci/pcie_imx.c new file mode 100644 index 000000000..c48737e6c --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/pcie_imx.c @@ -0,0 +1,617 @@ +/* + * Freescale i.MX6 PCI Express Root-Complex driver + * + * Copyright (C) 2013 Marek Vasut + * + * Based on upstream Linux kernel driver: + * pci-imx6.c: Sean Cross + * pcie-designware.c: Jingoo Han + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +#define MX6_DBI_ADDR 0x01ffc000 +#define MX6_DBI_SIZE 0x4000 +#define MX6_IO_ADDR 0x01000000 +#define MX6_IO_SIZE 0x100000 +#define MX6_MEM_ADDR 0x01100000 +#define MX6_MEM_SIZE 0xe00000 +#define MX6_ROOT_ADDR 0x01f00000 +#define MX6_ROOT_SIZE 0xfc000 + +/* PCIe Port Logic registers (memory-mapped) */ +#define PL_OFFSET 0x700 +#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) +#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) +#define PCIE_PHY_DEBUG_R1_LINK_UP (1 << 4) +#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (1 << 29) + +#define PCIE_PHY_CTRL (PL_OFFSET + 0x114) +#define PCIE_PHY_CTRL_DATA_LOC 0 +#define PCIE_PHY_CTRL_CAP_ADR_LOC 16 +#define PCIE_PHY_CTRL_CAP_DAT_LOC 17 +#define PCIE_PHY_CTRL_WR_LOC 18 +#define PCIE_PHY_CTRL_RD_LOC 19 + +#define PCIE_PHY_STAT (PL_OFFSET + 0x110) +#define PCIE_PHY_STAT_DATA_LOC 0 +#define PCIE_PHY_STAT_ACK_LOC 16 + +/* PHY registers (not memory-mapped) */ +#define PCIE_PHY_RX_ASIC_OUT 0x100D + +#define PHY_RX_OVRD_IN_LO 0x1005 +#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) +#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) + +/* iATU registers */ +#define PCIE_ATU_VIEWPORT 0x900 +#define PCIE_ATU_REGION_INBOUND (0x1 << 31) +#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) +#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) +#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) +#define PCIE_ATU_CR1 0x904 +#define PCIE_ATU_TYPE_MEM (0x0 << 0) +#define PCIE_ATU_TYPE_IO (0x2 << 0) +#define PCIE_ATU_TYPE_CFG0 (0x4 << 0) +#define PCIE_ATU_TYPE_CFG1 (0x5 << 0) +#define PCIE_ATU_CR2 0x908 +#define PCIE_ATU_ENABLE (0x1 << 31) +#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) +#define PCIE_ATU_LOWER_BASE 0x90C +#define PCIE_ATU_UPPER_BASE 0x910 +#define PCIE_ATU_LIMIT 0x914 +#define PCIE_ATU_LOWER_TARGET 0x918 +#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) +#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) +#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) +#define PCIE_ATU_UPPER_TARGET 0x91C + +/* + * PHY access functions + */ +static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val) +{ + u32 val; + u32 max_iterations = 10; + u32 wait_counter = 0; + + do { + val = readl(dbi_base + PCIE_PHY_STAT); + val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1; + wait_counter++; + + if (val == exp_val) + return 0; + + udelay(1); + } while (wait_counter < max_iterations); + + return -ETIMEDOUT; +} + +static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr) +{ + u32 val; + int ret; + + val = addr << PCIE_PHY_CTRL_DATA_LOC; + writel(val, dbi_base + PCIE_PHY_CTRL); + + val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC); + writel(val, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 1); + if (ret) + return ret; + + val = addr << PCIE_PHY_CTRL_DATA_LOC; + writel(val, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 0); + if (ret) + return ret; + + return 0; +} + +/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */ +static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data) +{ + u32 val, phy_ctl; + int ret; + + ret = pcie_phy_wait_ack(dbi_base, addr); + if (ret) + return ret; + + /* assert Read signal */ + phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC; + writel(phy_ctl, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 1); + if (ret) + return ret; + + val = readl(dbi_base + PCIE_PHY_STAT); + *data = val & 0xffff; + + /* deassert Read signal */ + writel(0x00, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 0); + if (ret) + return ret; + + return 0; +} + +static int pcie_phy_write(void __iomem *dbi_base, int addr, int data) +{ + u32 var; + int ret; + + /* write addr */ + /* cap addr */ + ret = pcie_phy_wait_ack(dbi_base, addr); + if (ret) + return ret; + + var = data << PCIE_PHY_CTRL_DATA_LOC; + writel(var, dbi_base + PCIE_PHY_CTRL); + + /* capture data */ + var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC); + writel(var, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 1); + if (ret) + return ret; + + /* deassert cap data */ + var = data << PCIE_PHY_CTRL_DATA_LOC; + writel(var, dbi_base + PCIE_PHY_CTRL); + + /* wait for ack de-assertion */ + ret = pcie_phy_poll_ack(dbi_base, 0); + if (ret) + return ret; + + /* assert wr signal */ + var = 0x1 << PCIE_PHY_CTRL_WR_LOC; + writel(var, dbi_base + PCIE_PHY_CTRL); + + /* wait for ack */ + ret = pcie_phy_poll_ack(dbi_base, 1); + if (ret) + return ret; + + /* deassert wr signal */ + var = data << PCIE_PHY_CTRL_DATA_LOC; + writel(var, dbi_base + PCIE_PHY_CTRL); + + /* wait for ack de-assertion */ + ret = pcie_phy_poll_ack(dbi_base, 0); + if (ret) + return ret; + + writel(0x0, dbi_base + PCIE_PHY_CTRL); + + return 0; +} + +static int imx6_pcie_link_up(void) +{ + u32 rc, ltssm; + int rx_valid, temp; + + /* link is debug bit 36, debug register 1 starts at bit 32 */ + rc = readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R1); + if ((rc & PCIE_PHY_DEBUG_R1_LINK_UP) && + !(rc & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING)) + return -EAGAIN; + + /* + * From L0, initiate MAC entry to gen2 if EP/RC supports gen2. + * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2). + * If (MAC/LTSSM.state == Recovery.RcvrLock) + * && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition + * to gen2 is stuck + */ + pcie_phy_read((void *)MX6_DBI_ADDR, PCIE_PHY_RX_ASIC_OUT, &rx_valid); + ltssm = readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R0) & 0x3F; + + if (rx_valid & 0x01) + return 0; + + if (ltssm != 0x0d) + return 0; + + printf("transition to gen2 is stuck, reset PHY!\n"); + + pcie_phy_read((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, &temp); + temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); + pcie_phy_write((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, temp); + + udelay(3000); + + pcie_phy_read((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, &temp); + temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); + pcie_phy_write((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, temp); + + return 0; +} + +/* + * iATU region setup + */ +static int imx_pcie_regions_setup(void) +{ + /* + * i.MX6 defines 16MB in the AXI address map for PCIe. + * + * That address space excepted the pcie registers is + * split and defined into different regions by iATU, + * with sizes and offsets as follows: + * + * 0x0100_0000 --- 0x010F_FFFF 1MB IORESOURCE_IO + * 0x0110_0000 --- 0x01EF_FFFF 14MB IORESOURCE_MEM + * 0x01F0_0000 --- 0x01FF_FFFF 1MB Cfg + Registers + */ + + /* CMD reg:I/O space, MEM space, and Bus Master Enable */ + setbits_le32(MX6_DBI_ADDR | PCI_COMMAND, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */ + setbits_le32(MX6_DBI_ADDR + PCI_CLASS_REVISION, + PCI_CLASS_BRIDGE_PCI << 16); + + /* Region #0 is used for Outbound CFG space access. */ + writel(0, MX6_DBI_ADDR + PCIE_ATU_VIEWPORT); + + writel(MX6_ROOT_ADDR, MX6_DBI_ADDR + PCIE_ATU_LOWER_BASE); + writel(0, MX6_DBI_ADDR + PCIE_ATU_UPPER_BASE); + writel(MX6_ROOT_ADDR + MX6_ROOT_SIZE, MX6_DBI_ADDR + PCIE_ATU_LIMIT); + + writel(0, MX6_DBI_ADDR + PCIE_ATU_LOWER_TARGET); + writel(0, MX6_DBI_ADDR + PCIE_ATU_UPPER_TARGET); + writel(PCIE_ATU_TYPE_CFG0, MX6_DBI_ADDR + PCIE_ATU_CR1); + writel(PCIE_ATU_ENABLE, MX6_DBI_ADDR + PCIE_ATU_CR2); + + return 0; +} + +/* + * PCI Express accessors + */ +static uint32_t get_bus_address(pci_dev_t d, int where) +{ + uint32_t va_address; + + /* Reconfigure Region #0 */ + writel(0, MX6_DBI_ADDR + PCIE_ATU_VIEWPORT); + + if (PCI_BUS(d) < 2) + writel(PCIE_ATU_TYPE_CFG0, MX6_DBI_ADDR + PCIE_ATU_CR1); + else + writel(PCIE_ATU_TYPE_CFG1, MX6_DBI_ADDR + PCIE_ATU_CR1); + + if (PCI_BUS(d) == 0) { + va_address = MX6_DBI_ADDR; + } else { + writel(d << 8, MX6_DBI_ADDR + PCIE_ATU_LOWER_TARGET); + va_address = MX6_IO_ADDR + SZ_16M - SZ_1M; + } + + va_address += (where & ~0x3); + + return va_address; +} + +static int imx_pcie_addr_valid(pci_dev_t d) +{ + if ((PCI_BUS(d) == 0) && (PCI_DEV(d) > 1)) + return -EINVAL; + if ((PCI_BUS(d) == 1) && (PCI_DEV(d) > 0)) + return -EINVAL; + return 0; +} + +/* + * Replace the original ARM DABT handler with a simple jump-back one. + * + * The problem here is that if we have a PCIe bridge attached to this PCIe + * controller, but no PCIe device is connected to the bridges' downstream + * port, the attempt to read/write from/to the config space will produce + * a DABT. This is a behavior of the controller and can not be disabled + * unfortuatelly. + * + * To work around the problem, we backup the current DABT handler address + * and replace it with our own DABT handler, which only bounces right back + * into the code. + */ +static void imx_pcie_fix_dabt_handler(bool set) +{ + extern uint32_t *_data_abort; + uint32_t *data_abort_addr = (uint32_t *)&_data_abort; + + static const uint32_t data_abort_bounce_handler = 0xe25ef004; + uint32_t data_abort_bounce_addr = (uint32_t)&data_abort_bounce_handler; + + static uint32_t data_abort_backup; + + if (set) { + data_abort_backup = *data_abort_addr; + *data_abort_addr = data_abort_bounce_addr; + } else { + *data_abort_addr = data_abort_backup; + } +} + +static int imx_pcie_read_config(struct pci_controller *hose, pci_dev_t d, + int where, u32 *val) +{ + uint32_t va_address; + int ret; + + ret = imx_pcie_addr_valid(d); + if (ret) { + *val = 0xffffffff; + return ret; + } + + va_address = get_bus_address(d, where); + + /* + * Read the PCIe config space. We must replace the DABT handler + * here in case we got data abort from the PCIe controller, see + * imx_pcie_fix_dabt_handler() description. Note that writing the + * "val" with valid value is also imperative here as in case we + * did got DABT, the val would contain random value. + */ + imx_pcie_fix_dabt_handler(true); + writel(0xffffffff, val); + *val = readl(va_address); + imx_pcie_fix_dabt_handler(false); + + return 0; +} + +static int imx_pcie_write_config(struct pci_controller *hose, pci_dev_t d, + int where, u32 val) +{ + uint32_t va_address = 0; + int ret; + + ret = imx_pcie_addr_valid(d); + if (ret) + return ret; + + va_address = get_bus_address(d, where); + + /* + * Write the PCIe config space. We must replace the DABT handler + * here in case we got data abort from the PCIe controller, see + * imx_pcie_fix_dabt_handler() description. + */ + imx_pcie_fix_dabt_handler(true); + writel(val, va_address); + imx_pcie_fix_dabt_handler(false); + + return 0; +} + +/* + * Initial bus setup + */ +static int imx6_pcie_assert_core_reset(void) +{ + struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; + + setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN); + clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN); + + return 0; +} + +static int imx6_pcie_init_phy(void) +{ + struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; + + clrbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_APPS_LTSSM_ENABLE); + + clrsetbits_le32(&iomuxc_regs->gpr[12], + IOMUXC_GPR12_DEVICE_TYPE_MASK, + IOMUXC_GPR12_DEVICE_TYPE_RC); + clrsetbits_le32(&iomuxc_regs->gpr[12], + IOMUXC_GPR12_LOS_LEVEL_MASK, + IOMUXC_GPR12_LOS_LEVEL_9); + + writel((0x0 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN1_OFFSET) | + (0x0 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_3P5DB_OFFSET) | + (20 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_6DB_OFFSET) | + (127 << IOMUXC_GPR8_PCS_TX_SWING_FULL_OFFSET) | + (127 << IOMUXC_GPR8_PCS_TX_SWING_LOW_OFFSET), + &iomuxc_regs->gpr[8]); + + return 0; +} + +__weak int imx6_pcie_toggle_power(void) +{ +#ifdef CONFIG_PCIE_IMX_POWER_GPIO + gpio_direction_output(CONFIG_PCIE_IMX_POWER_GPIO, 0); + mdelay(20); + gpio_set_value(CONFIG_PCIE_IMX_POWER_GPIO, 1); + mdelay(20); +#endif + return 0; +} + +__weak int imx6_pcie_toggle_reset(void) +{ + /* + * See 'PCI EXPRESS BASE SPECIFICATION, REV 3.0, SECTION 6.6.1' + * for detailed understanding of the PCIe CR reset logic. + * + * The PCIe #PERST reset line _MUST_ be connected, otherwise your + * design does not conform to the specification. You must wait at + * least 20 mS after de-asserting the #PERST so the EP device can + * do self-initialisation. + * + * In case your #PERST pin is connected to a plain GPIO pin of the + * CPU, you can define CONFIG_PCIE_IMX_PERST_GPIO in your board's + * configuration file and the condition below will handle the rest + * of the reset toggling. + * + * In case your #PERST toggling logic is more complex, for example + * connected via CPLD or somesuch, you can override this function + * in your board file and implement reset logic as needed. You must + * not forget to wait at least 20 mS after de-asserting #PERST in + * this case either though. + * + * In case your #PERST line of the PCIe EP device is not connected + * at all, your design is broken and you should fix your design, + * otherwise you will observe problems like for example the link + * not coming up after rebooting the system back from running Linux + * that uses the PCIe as well OR the PCIe link might not come up in + * Linux at all in the first place since it's in some non-reset + * state due to being previously used in U-Boot. + */ +#ifdef CONFIG_PCIE_IMX_PERST_GPIO + gpio_direction_output(CONFIG_PCIE_IMX_PERST_GPIO, 0); + mdelay(20); + gpio_set_value(CONFIG_PCIE_IMX_PERST_GPIO, 1); + mdelay(20); +#else + puts("WARNING: Make sure the PCIe #PERST line is connected!\n"); +#endif + return 0; +} + +static int imx6_pcie_deassert_core_reset(void) +{ + struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; + + imx6_pcie_toggle_power(); + + /* Enable PCIe */ + clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN); + setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN); + + enable_pcie_clock(); + + /* + * Wait for the clock to settle a bit, when the clock are sourced + * from the CPU, we need about 30mS to settle. + */ + mdelay(50); + + imx6_pcie_toggle_reset(); + + return 0; +} + +static int imx_pcie_link_up(void) +{ + struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; + uint32_t tmp; + int count = 0; + + imx6_pcie_assert_core_reset(); + imx6_pcie_init_phy(); + imx6_pcie_deassert_core_reset(); + + imx_pcie_regions_setup(); + + /* + * FIXME: Force the PCIe RC to Gen1 operation + * The RC must be forced into Gen1 mode before bringing the link + * up, otherwise no downstream devices are detected. After the + * link is up, a managed Gen1->Gen2 transition can be initiated. + */ + tmp = readl(MX6_DBI_ADDR + 0x7c); + tmp &= ~0xf; + tmp |= 0x1; + writel(tmp, MX6_DBI_ADDR + 0x7c); + + /* LTSSM enable, starting link. */ + setbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_APPS_LTSSM_ENABLE); + + while (!imx6_pcie_link_up()) { + udelay(10); + count++; + if (count >= 2000) { + debug("phy link never came up\n"); + debug("DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", + readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R0), + readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R1)); + return -EINVAL; + } + } + + return 0; +} + +void imx_pcie_init(void) +{ + /* Static instance of the controller. */ + static struct pci_controller pcc; + struct pci_controller *hose = &pcc; + int ret; + + memset(&pcc, 0, sizeof(pcc)); + + /* PCI I/O space */ + pci_set_region(&hose->regions[0], + MX6_IO_ADDR, MX6_IO_ADDR, + MX6_IO_SIZE, PCI_REGION_IO); + + /* PCI memory space */ + pci_set_region(&hose->regions[1], + MX6_MEM_ADDR, MX6_MEM_ADDR, + MX6_MEM_SIZE, PCI_REGION_MEM); + + /* System memory space */ + pci_set_region(&hose->regions[2], + MMDC0_ARB_BASE_ADDR, MMDC0_ARB_BASE_ADDR, + 0xefffffff, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + hose->region_count = 3; + + pci_set_ops(hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + imx_pcie_read_config, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + imx_pcie_write_config); + + /* Start the controller. */ + ret = imx_pcie_link_up(); + + if (!ret) { + pci_register_hose(hose); + hose->last_busno = pci_hose_scan(hose); + } +} + +/* Probe function. */ +void pci_init_board(void) +{ + imx_pcie_init(); +} diff --git a/qemu/roms/u-boot/drivers/pci/tsi108_pci.c b/qemu/roms/u-boot/drivers/pci/tsi108_pci.c new file mode 100644 index 000000000..d48e1e6fe --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/tsi108_pci.c @@ -0,0 +1,167 @@ +/* + * (C) Copyright 2004 Tundra Semiconductor Corp. + * Alex Bounine + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * PCI initialisation for the Tsi108 EMU board. + */ + +#include + +#include +#include +#include +#include +#if defined(CONFIG_OF_LIBFDT) +#include +#include +#endif + +struct pci_controller local_hose; + +void tsi108_clear_pci_error (void) +{ + u32 err_stat, err_addr, pci_stat; + + /* + * Quietly clear errors signalled as result of PCI/X configuration read + * requests. + */ + /* Read PB Error Log Registers */ + err_stat = *(volatile u32 *)(CONFIG_SYS_TSI108_CSR_BASE + + TSI108_PB_REG_OFFSET + PB_ERRCS); + err_addr = *(volatile u32 *)(CONFIG_SYS_TSI108_CSR_BASE + + TSI108_PB_REG_OFFSET + PB_AERR); + if (err_stat & PB_ERRCS_ES) { + /* Clear PCI/X bus errors if applicable */ + if ((err_addr & 0xFF000000) == CONFIG_SYS_PCI_CFG_BASE) { + /* Clear error flag */ + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + + TSI108_PB_REG_OFFSET + PB_ERRCS) = + PB_ERRCS_ES; + + /* Clear read error reported in PB_ISR */ + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + + TSI108_PB_REG_OFFSET + PB_ISR) = + PB_ISR_PBS_RD_ERR; + + /* Clear errors reported by PCI CSR (Normally Master Abort) */ + pci_stat = *(volatile u32 *)(CONFIG_SYS_TSI108_CSR_BASE + + TSI108_PCI_REG_OFFSET + + PCI_CSR); + *(volatile u32 *)(CONFIG_SYS_TSI108_CSR_BASE + + TSI108_PCI_REG_OFFSET + PCI_CSR) = + pci_stat; + + *(volatile u32 *)(CONFIG_SYS_TSI108_CSR_BASE + + TSI108_PCI_REG_OFFSET + + PCI_IRP_STAT) = PCI_IRP_STAT_P_CSR; + } + } + + return; +} + +unsigned int __get_pci_config_dword (u32 addr) +{ + unsigned int retval; + + __asm__ __volatile__ (" lwbrx %0,0,%1\n" + "1: eieio\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: li %0,-1\n" + " b 2b\n" + ".section __ex_table,\"a\"\n" + " .align 2\n" + " .long 1b,3b\n" + ".section .text.__get_pci_config_dword" + : "=r"(retval) : "r"(addr)); + + return (retval); +} + +static int tsi108_read_config_dword (struct pci_controller *hose, + pci_dev_t dev, int offset, u32 * value) +{ + dev &= (CONFIG_SYS_PCI_CFG_SIZE - 1); + dev |= (CONFIG_SYS_PCI_CFG_BASE | (offset & 0xfc)); + *value = __get_pci_config_dword(dev); + if (0xFFFFFFFF == *value) + tsi108_clear_pci_error (); + return 0; +} + +static int tsi108_write_config_dword (struct pci_controller *hose, + pci_dev_t dev, int offset, u32 value) +{ + dev &= (CONFIG_SYS_PCI_CFG_SIZE - 1); + dev |= (CONFIG_SYS_PCI_CFG_BASE | (offset & 0xfc)); + + out_le32 ((volatile unsigned *)dev, value); + + return 0; +} + +void pci_init_board (void) +{ + struct pci_controller *hose = (struct pci_controller *)&local_hose; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + pci_set_region (hose->regions + 0, + CONFIG_SYS_PCI_MEMORY_BUS, + CONFIG_SYS_PCI_MEMORY_PHYS, + CONFIG_SYS_PCI_MEMORY_SIZE, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + /* PCI memory space */ + pci_set_region (hose->regions + 1, + CONFIG_SYS_PCI_MEM_BUS, + CONFIG_SYS_PCI_MEM_PHYS, CONFIG_SYS_PCI_MEM_SIZE, PCI_REGION_MEM); + + /* PCI I/O space */ + pci_set_region (hose->regions + 2, + CONFIG_SYS_PCI_IO_BUS, + CONFIG_SYS_PCI_IO_PHYS, CONFIG_SYS_PCI_IO_SIZE, PCI_REGION_IO); + + hose->region_count = 3; + + pci_set_ops (hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + tsi108_read_config_dword, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + tsi108_write_config_dword); + + pci_register_hose (hose); + + hose->last_busno = pci_hose_scan (hose); + + debug ("Done PCI initialization\n"); + return; +} + +#if defined(CONFIG_OF_LIBFDT) +void ft_pci_setup(void *blob, bd_t *bd) +{ + int nodeoffset; + int tmp[2]; + const char *path; + + nodeoffset = fdt_path_offset(blob, "/aliases"); + if (nodeoffset >= 0) { + path = fdt_getprop(blob, nodeoffset, "pci", NULL); + if (path) { + tmp[0] = cpu_to_be32(local_hose.first_busno); + tmp[1] = cpu_to_be32(local_hose.last_busno); + do_fixup_by_path(blob, path, "bus-range", + &tmp, sizeof(tmp), 1); + } + } +} +#endif /* CONFIG_OF_LIBFDT */ diff --git a/qemu/roms/u-boot/drivers/pci/w83c553f.c b/qemu/roms/u-boot/drivers/pci/w83c553f.c new file mode 100644 index 000000000..1192f0f64 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pci/w83c553f.c @@ -0,0 +1,206 @@ +/* + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH + * Andreas Heppel + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Initialisation of the PCI-to-ISA bridge and disabling the BIOS + * write protection (for flash) in function 0 of the chip. + * Enabling function 1 (IDE controller of the chip. + */ + +#include +#include + +#include +#include + +#include + +#define out8(addr,val) do { \ + out_8((u8*) (addr),(val)); udelay(1); \ + } while (0) +#define out16(addr,val) do { \ + out_be16((u16*) (addr),(val)); udelay(1); \ + } while (0) + +extern uint ide_bus_offset[CONFIG_SYS_IDE_MAXBUS]; + +void initialise_pic(void); +void initialise_dma(void); + +void initialise_w83c553f(void) +{ + pci_dev_t devbusfn; + unsigned char reg8; + unsigned short reg16; + unsigned int reg32; + + devbusfn = pci_find_device(W83C553F_VID, W83C553F_DID, 0); + if (devbusfn == -1) + { + printf("Error: Cannot find W83C553F controller on any PCI bus."); + return; + } + + pci_read_config_word(devbusfn, PCI_COMMAND, ®16); + reg16 |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + pci_write_config_word(devbusfn, PCI_COMMAND, reg16); + + pci_read_config_byte(devbusfn, WINBOND_IPADCR, ®8); + /* 16 MB ISA memory space */ + reg8 |= (IPADCR_IPATOM4 | IPADCR_IPATOM5 | IPADCR_IPATOM6 | IPADCR_IPATOM7); + reg8 &= ~IPADCR_MBE512; + pci_write_config_byte(devbusfn, WINBOND_IPADCR, reg8); + + pci_read_config_byte(devbusfn, WINBOND_CSCR, ®8); + /* switch off BIOS write protection */ + reg8 |= CSCR_UBIOSCSE; + reg8 &= ~CSCR_BIOSWP; + pci_write_config_byte(devbusfn, WINBOND_CSCR, reg8); + + /* + * Interrupt routing: + * - IDE -> IRQ 9/0 + * - INTA -> IRQ 10 + * - INTB -> IRQ 11 + * - INTC -> IRQ 14 + * - INTD -> IRQ 15 + */ + pci_write_config_byte(devbusfn, WINBOND_IDEIRCR, 0x90); + pci_write_config_word(devbusfn, WINBOND_PCIIRCR, 0xABEF); + + /* + * Read IDE bus offsets from function 1 device. + * We must unmask the LSB indicating that ist is an IO address. + */ + devbusfn |= PCI_BDF(0,0,1); + + /* + * Switch off legacy IRQ for IDE and IDE port 1. + */ + pci_write_config_byte(devbusfn, 0x09, 0x8F); + + pci_read_config_dword(devbusfn, WINDOND_IDECSR, ®32); + reg32 &= ~(IDECSR_LEGIRQ | IDECSR_P1EN | IDECSR_P1F16); + pci_write_config_dword(devbusfn, WINDOND_IDECSR, reg32); + + pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &ide_bus_offset[0]); + ide_bus_offset[0] &= ~1; +#if CONFIG_SYS_IDE_MAXBUS > 1 + pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_2, &ide_bus_offset[1]); + ide_bus_offset[1] &= ~1; +#endif + + /* + * Enable function 1, IDE -> busmastering and IO space access + */ + pci_read_config_word(devbusfn, PCI_COMMAND, ®16); + reg16 |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; + pci_write_config_word(devbusfn, PCI_COMMAND, reg16); + + /* + * Initialise ISA interrupt controller + */ + initialise_pic(); + + /* + * Initialise DMA controller + */ + initialise_dma(); +} + +void initialise_pic(void) +{ + out8(W83C553F_PIC1_ICW1, 0x11); + out8(W83C553F_PIC1_ICW2, 0x08); + out8(W83C553F_PIC1_ICW3, 0x04); + out8(W83C553F_PIC1_ICW4, 0x01); + out8(W83C553F_PIC1_OCW1, 0xfb); + out8(W83C553F_PIC1_ELC, 0x20); + + out8(W83C553F_PIC2_ICW1, 0x11); + out8(W83C553F_PIC2_ICW2, 0x08); + out8(W83C553F_PIC2_ICW3, 0x02); + out8(W83C553F_PIC2_ICW4, 0x01); + out8(W83C553F_PIC2_OCW1, 0xff); + out8(W83C553F_PIC2_ELC, 0xce); + + out8(W83C553F_TMR1_CMOD, 0x74); + + out8(W83C553F_PIC2_OCW1, 0x20); + out8(W83C553F_PIC1_OCW1, 0x20); + + out8(W83C553F_PIC2_OCW1, 0x2b); + out8(W83C553F_PIC1_OCW1, 0x2b); +} + +void initialise_dma(void) +{ + unsigned int channel; + unsigned int rvalue1, rvalue2; + + /* perform a H/W reset of the devices */ + + out8(W83C553F_DMA1 + W83C553F_DMA1_MC, 0x00); + out16(W83C553F_DMA2 + W83C553F_DMA2_MC, 0x0000); + + /* initialise all channels to a sane state */ + + for (channel = 0; channel < 4; channel++) { + /* + * dependent upon the channel, setup the specifics: + * + * demand + * address-increment + * autoinitialize-disable + * verify-transfer + */ + + switch (channel) { + case 0: + rvalue1 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH0SEL|W83C553F_MODE_TT_VERIFY); + rvalue2 = (W83C553F_MODE_TM_CASCADE|W83C553F_MODE_CH0SEL); + break; + case 1: + rvalue1 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH1SEL|W83C553F_MODE_TT_VERIFY); + rvalue2 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH1SEL|W83C553F_MODE_TT_VERIFY); + break; + case 2: + rvalue1 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH2SEL|W83C553F_MODE_TT_VERIFY); + rvalue2 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH2SEL|W83C553F_MODE_TT_VERIFY); + break; + case 3: + rvalue1 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH3SEL|W83C553F_MODE_TT_VERIFY); + rvalue2 = (W83C553F_MODE_TM_DEMAND|W83C553F_MODE_CH3SEL|W83C553F_MODE_TT_VERIFY); + break; + default: + rvalue1 = 0x00; + rvalue2 = 0x00; + break; + } + + /* write to write mode registers */ + + out8(W83C553F_DMA1 + W83C553F_DMA1_WM, rvalue1 & 0xFF); + out16(W83C553F_DMA2 + W83C553F_DMA2_WM, rvalue2 & 0x00FF); + } + + /* enable all channels */ + + out8(W83C553F_DMA1 + W83C553F_DMA1_CM, 0x00); + out16(W83C553F_DMA2 + W83C553F_DMA2_CM, 0x0000); + /* + * initialize the global DMA configuration + * + * DACK# active low + * DREQ active high + * fixed priority + * channel group enable + */ + + out8(W83C553F_DMA1 + W83C553F_DMA1_CS, 0x00); + out16(W83C553F_DMA2 + W83C553F_DMA2_CS, 0x0000); +} diff --git a/qemu/roms/u-boot/drivers/pcmcia/Makefile b/qemu/roms/u-boot/drivers/pcmcia/Makefile new file mode 100644 index 000000000..ae3cafbea --- /dev/null +++ b/qemu/roms/u-boot/drivers/pcmcia/Makefile @@ -0,0 +1,13 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_I82365) += i82365.o +obj-$(CONFIG_8xx) += mpc8xx_pcmcia.o +obj-y += rpx_pcmcia.o +obj-$(CONFIG_IDE_TI_CARDBUS) += ti_pci1410a.o +obj-y += tqm8xx_pcmcia.o +obj-$(CONFIG_MARUBUN_PCCARD) += marubun_pcmcia.o diff --git a/qemu/roms/u-boot/drivers/pcmcia/i82365.c b/qemu/roms/u-boot/drivers/pcmcia/i82365.c new file mode 100644 index 000000000..84a3d2ecb --- /dev/null +++ b/qemu/roms/u-boot/drivers/pcmcia/i82365.c @@ -0,0 +1,989 @@ +/* + * (C) Copyright 2003-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + ******************************************************************** + * + * Lots of code copied from: + * + * i82365.c 1.352 - Linux driver for Intel 82365 and compatible + * PC Card controllers, and Yenta-compatible PCI-to-CardBus controllers. + * (C) 1999 David A. Hinds + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#ifdef CONFIG_CPC45 +#include +#else +#include +#endif + +static struct pci_device_id supported[] = { +#ifdef CONFIG_CPC45 + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729}, +#else + {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1510}, +#endif + {0, 0} +}; + +#define CYCLE_TIME 120 + +#ifdef CONFIG_CPC45 +extern int SPD67290Init (void); +#endif + +#ifdef DEBUG +static void i82365_dump_regions (pci_dev_t dev); +#endif + +typedef struct socket_info_t { + pci_dev_t dev; + u_short bcr; + u_char pci_lat, cb_lat, sub_bus, cache; + u_int cb_phys; + + socket_cap_t cap; + u_short type; + u_int flags; +#ifdef CONFIG_CPC45 + cirrus_state_t c_state; +#else + ti113x_state_t state; +#endif +} socket_info_t; + +#ifdef CONFIG_CPC45 +/* These definitions must match the pcic table! */ +typedef enum pcic_id { + IS_PD6710, IS_PD672X, IS_VT83C469 +} pcic_id; + +typedef struct pcic_t { + char *name; +} pcic_t; + +static pcic_t pcic[] = { + {" Cirrus PD6710: "}, + {" Cirrus PD672x: "}, + {" VIA VT83C469: "}, +}; +#endif + +static socket_info_t socket; +static socket_state_t state; +static struct pccard_mem_map mem; +static struct pccard_io_map io; + +/*====================================================================*/ + +/* Some PCI shortcuts */ + +static int pci_readb (socket_info_t * s, int r, u_char * v) +{ + return pci_read_config_byte (s->dev, r, v); +} +static int pci_writeb (socket_info_t * s, int r, u_char v) +{ + return pci_write_config_byte (s->dev, r, v); +} +static int pci_readw (socket_info_t * s, int r, u_short * v) +{ + return pci_read_config_word (s->dev, r, v); +} +static int pci_writew (socket_info_t * s, int r, u_short v) +{ + return pci_write_config_word (s->dev, r, v); +} +#ifndef CONFIG_CPC45 +static int pci_readl (socket_info_t * s, int r, u_int * v) +{ + return pci_read_config_dword (s->dev, r, v); +} +static int pci_writel (socket_info_t * s, int r, u_int v) +{ + return pci_write_config_dword (s->dev, r, v); +} +#endif /* !CONFIG_CPC45 */ + +/*====================================================================*/ + +#ifdef CONFIG_CPC45 + +#define cb_readb(s) readb((s)->cb_phys + 1) +#define cb_writeb(s, v) writeb(v, (s)->cb_phys) +#define cb_writeb2(s, v) writeb(v, (s)->cb_phys + 1) +#define cb_readl(s, r) readl((s)->cb_phys + (r)) +#define cb_writel(s, r, v) writel(v, (s)->cb_phys + (r)) + + +static u_char i365_get (socket_info_t * s, u_short reg) +{ + u_char val; +#ifdef CONFIG_PCMCIA_SLOT_A + int slot = 0; +#else + int slot = 1; +#endif + + val = I365_REG (slot, reg); + + cb_writeb (s, val); + val = cb_readb (s); + + debug ("i365_get slot:%x reg: %x val: %x\n", slot, reg, val); + return val; +} + +static void i365_set (socket_info_t * s, u_short reg, u_char data) +{ +#ifdef CONFIG_PCMCIA_SLOT_A + int slot = 0; +#else + int slot = 1; +#endif + u_char val; + + val = I365_REG (slot, reg); + + cb_writeb (s, val); + cb_writeb2 (s, data); + + debug ("i365_set slot:%x reg: %x data:%x\n", slot, reg, data); +} + +#else /* ! CONFIG_CPC45 */ + +#define cb_readb(s, r) readb((s)->cb_phys + (r)) +#define cb_readl(s, r) readl((s)->cb_phys + (r)) +#define cb_writeb(s, r, v) writeb(v, (s)->cb_phys + (r)) +#define cb_writel(s, r, v) writel(v, (s)->cb_phys + (r)) + +static u_char i365_get (socket_info_t * s, u_short reg) +{ + return cb_readb (s, 0x0800 + reg); +} + +static void i365_set (socket_info_t * s, u_short reg, u_char data) +{ + cb_writeb (s, 0x0800 + reg, data); +} +#endif /* CONFIG_CPC45 */ + +static void i365_bset (socket_info_t * s, u_short reg, u_char mask) +{ + i365_set (s, reg, i365_get (s, reg) | mask); +} + +static void i365_bclr (socket_info_t * s, u_short reg, u_char mask) +{ + i365_set (s, reg, i365_get (s, reg) & ~mask); +} + +#if 0 /* not used */ +static void i365_bflip (socket_info_t * s, u_short reg, u_char mask, int b) +{ + u_char d = i365_get (s, reg); + + i365_set (s, reg, (b) ? (d | mask) : (d & ~mask)); +} + +static u_short i365_get_pair (socket_info_t * s, u_short reg) +{ + return (i365_get (s, reg) + (i365_get (s, reg + 1) << 8)); +} +#endif /* not used */ + +static void i365_set_pair (socket_info_t * s, u_short reg, u_short data) +{ + i365_set (s, reg, data & 0xff); + i365_set (s, reg + 1, data >> 8); +} + +#ifdef CONFIG_CPC45 +/*====================================================================== + + Code to save and restore global state information for Cirrus + PD67xx controllers, and to set and report global configuration + options. + +======================================================================*/ + +#define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b)))) + +static void cirrus_get_state (socket_info_t * s) +{ + int i; + cirrus_state_t *p = &s->c_state; + + p->misc1 = i365_get (s, PD67_MISC_CTL_1); + p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA); + p->misc2 = i365_get (s, PD67_MISC_CTL_2); + for (i = 0; i < 6; i++) + p->timer[i] = i365_get (s, PD67_TIME_SETUP (0) + i); + +} + +static void cirrus_set_state (socket_info_t * s) +{ + int i; + u_char misc; + cirrus_state_t *p = &s->c_state; + + misc = i365_get (s, PD67_MISC_CTL_2); + i365_set (s, PD67_MISC_CTL_2, p->misc2); + if (misc & PD67_MC2_SUSPEND) + udelay (50000); + misc = i365_get (s, PD67_MISC_CTL_1); + misc &= ~(PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA); + i365_set (s, PD67_MISC_CTL_1, misc | p->misc1); + for (i = 0; i < 6; i++) + i365_set (s, PD67_TIME_SETUP (0) + i, p->timer[i]); +} + +static u_int cirrus_set_opts (socket_info_t * s) +{ + cirrus_state_t *p = &s->c_state; + u_int mask = 0xffff; + char buf[200] = {0}; + + if (has_ring == -1) + has_ring = 1; + flip (p->misc2, PD67_MC2_IRQ15_RI, has_ring); + flip (p->misc2, PD67_MC2_DYNAMIC_MODE, dynamic_mode); +#if DEBUG + if (p->misc2 & PD67_MC2_IRQ15_RI) + strcat (buf, " [ring]"); + if (p->misc2 & PD67_MC2_DYNAMIC_MODE) + strcat (buf, " [dyn mode]"); + if (p->misc1 & PD67_MC1_INPACK_ENA) + strcat (buf, " [inpack]"); +#endif + + if (p->misc2 & PD67_MC2_IRQ15_RI) + mask &= ~0x8000; + if (has_led > 0) { +#if DEBUG + strcat (buf, " [led]"); +#endif + mask &= ~0x1000; + } + if (has_dma > 0) { +#if DEBUG + strcat (buf, " [dma]"); +#endif + mask &= ~0x0600; + flip (p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass); +#if DEBUG + if (p->misc2 & PD67_MC2_FREQ_BYPASS) + strcat (buf, " [freq bypass]"); +#endif + } + + if (setup_time >= 0) + p->timer[0] = p->timer[3] = setup_time; + if (cmd_time > 0) { + p->timer[1] = cmd_time; + p->timer[4] = cmd_time * 2 + 4; + } + if (p->timer[1] == 0) { + p->timer[1] = 6; + p->timer[4] = 16; + if (p->timer[0] == 0) + p->timer[0] = p->timer[3] = 1; + } + if (recov_time >= 0) + p->timer[2] = p->timer[5] = recov_time; + + debug ("i82365 Opt: %s [%d/%d/%d] [%d/%d/%d]\n", + buf, + p->timer[0], p->timer[1], p->timer[2], + p->timer[3], p->timer[4], p->timer[5]); + + return mask; +} + +#else /* !CONFIG_CPC45 */ + +/*====================================================================== + + Code to save and restore global state information for TI 1130 and + TI 1131 controllers, and to set and report global configuration + options. + +======================================================================*/ + +static void ti113x_get_state (socket_info_t * s) +{ + ti113x_state_t *p = &s->state; + + pci_readl (s, TI113X_SYSTEM_CONTROL, &p->sysctl); + pci_readb (s, TI113X_CARD_CONTROL, &p->cardctl); + pci_readb (s, TI113X_DEVICE_CONTROL, &p->devctl); + pci_readb (s, TI1250_DIAGNOSTIC, &p->diag); + pci_readl (s, TI12XX_IRQMUX, &p->irqmux); +} + +static void ti113x_set_state (socket_info_t * s) +{ + ti113x_state_t *p = &s->state; + + pci_writel (s, TI113X_SYSTEM_CONTROL, p->sysctl); + pci_writeb (s, TI113X_CARD_CONTROL, p->cardctl); + pci_writeb (s, TI113X_DEVICE_CONTROL, p->devctl); + pci_writeb (s, TI1250_MULTIMEDIA_CTL, 0); + pci_writeb (s, TI1250_DIAGNOSTIC, p->diag); + pci_writel (s, TI12XX_IRQMUX, p->irqmux); + i365_set_pair (s, TI113X_IO_OFFSET (0), 0); + i365_set_pair (s, TI113X_IO_OFFSET (1), 0); +} + +static u_int ti113x_set_opts (socket_info_t * s) +{ + ti113x_state_t *p = &s->state; + u_int mask = 0xffff; + + p->cardctl &= ~TI113X_CCR_ZVENABLE; + p->cardctl |= TI113X_CCR_SPKROUTEN; + + return mask; +} +#endif /* CONFIG_CPC45 */ + +/*====================================================================== + + Routines to handle common CardBus options + +======================================================================*/ + +/* Default settings for PCI command configuration register */ +#define CMD_DFLT (PCI_COMMAND_IO|PCI_COMMAND_MEMORY| \ + PCI_COMMAND_MASTER|PCI_COMMAND_WAIT) + +static void cb_get_state (socket_info_t * s) +{ + pci_readb (s, PCI_CACHE_LINE_SIZE, &s->cache); + pci_readb (s, PCI_LATENCY_TIMER, &s->pci_lat); + pci_readb (s, CB_LATENCY_TIMER, &s->cb_lat); + pci_readb (s, CB_CARDBUS_BUS, &s->cap.cardbus); + pci_readb (s, CB_SUBORD_BUS, &s->sub_bus); + pci_readw (s, CB_BRIDGE_CONTROL, &s->bcr); +} + +static void cb_set_state (socket_info_t * s) +{ +#ifndef CONFIG_CPC45 + pci_writel (s, CB_LEGACY_MODE_BASE, 0); + pci_writel (s, PCI_BASE_ADDRESS_0, s->cb_phys); +#endif + pci_writew (s, PCI_COMMAND, CMD_DFLT); + pci_writeb (s, PCI_CACHE_LINE_SIZE, s->cache); + pci_writeb (s, PCI_LATENCY_TIMER, s->pci_lat); + pci_writeb (s, CB_LATENCY_TIMER, s->cb_lat); + pci_writeb (s, CB_CARDBUS_BUS, s->cap.cardbus); + pci_writeb (s, CB_SUBORD_BUS, s->sub_bus); + pci_writew (s, CB_BRIDGE_CONTROL, s->bcr); +} + +static void cb_set_opts (socket_info_t * s) +{ +#ifndef CONFIG_CPC45 + if (s->cache == 0) + s->cache = 8; + if (s->pci_lat == 0) + s->pci_lat = 0xa8; + if (s->cb_lat == 0) + s->cb_lat = 0xb0; +#endif +} + +/*====================================================================== + + Power control for Cardbus controllers: used both for 16-bit and + Cardbus cards. + +======================================================================*/ + +static int cb_set_power (socket_info_t * s, socket_state_t * state) +{ + u_int reg = 0; + +#ifdef CONFIG_CPC45 + + reg = I365_PWR_NORESET; + if (state->flags & SS_PWR_AUTO) + reg |= I365_PWR_AUTO; + if (state->flags & SS_OUTPUT_ENA) + reg |= I365_PWR_OUT; + if (state->Vpp != 0) { + if (state->Vpp == 120) { + reg |= I365_VPP1_12V; + puts (" 12V card found: "); + } else if (state->Vpp == state->Vcc) { + reg |= I365_VPP1_5V; + } else { + puts (" power not found: "); + return -1; + } + } + if (state->Vcc != 0) { + reg |= I365_VCC_5V; + if (state->Vcc == 33) { + puts (" 3.3V card found: "); + i365_bset (s, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); + } else if (state->Vcc == 50) { + puts (" 5V card found: "); + i365_bclr (s, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); + } else { + puts (" power not found: "); + return -1; + } + } + + if (reg != i365_get (s, I365_POWER)) { + reg = (I365_PWR_OUT | I365_PWR_NORESET | I365_VCC_5V | I365_VPP1_5V); + i365_set (s, I365_POWER, reg); + } + +#else /* ! CONFIG_CPC45 */ + + /* restart card voltage detection if it seems appropriate */ + if ((state->Vcc == 0) && (state->Vpp == 0) && + !(cb_readl (s, CB_SOCKET_STATE) & CB_SS_VSENSE)) + cb_writel (s, CB_SOCKET_FORCE, CB_SF_CVSTEST); + switch (state->Vcc) { + case 0: + reg = 0; + break; + case 33: + reg = CB_SC_VCC_3V; + break; + case 50: + reg = CB_SC_VCC_5V; + break; + default: + return -1; + } + switch (state->Vpp) { + case 0: + break; + case 33: + reg |= CB_SC_VPP_3V; + break; + case 50: + reg |= CB_SC_VPP_5V; + break; + case 120: + reg |= CB_SC_VPP_12V; + break; + default: + return -1; + } + if (reg != cb_readl (s, CB_SOCKET_CONTROL)) + cb_writel (s, CB_SOCKET_CONTROL, reg); +#endif /* CONFIG_CPC45 */ + return 0; +} + +/*====================================================================== + + Generic routines to get and set controller options + +======================================================================*/ + +static void get_bridge_state (socket_info_t * s) +{ +#ifdef CONFIG_CPC45 + cirrus_get_state (s); +#else + ti113x_get_state (s); +#endif + cb_get_state (s); +} + +static void set_bridge_state (socket_info_t * s) +{ + cb_set_state (s); + i365_set (s, I365_GBLCTL, 0x00); + i365_set (s, I365_GENCTL, 0x00); +#ifdef CONFIG_CPC45 + cirrus_set_state (s); +#else + ti113x_set_state (s); +#endif +} + +static void set_bridge_opts (socket_info_t * s) +{ +#ifdef CONFIG_CPC45 + cirrus_set_opts (s); +#else + ti113x_set_opts (s); +#endif + cb_set_opts (s); +} + +/*====================================================================*/ +#define PD67_EXT_INDEX 0x2e /* Extension index */ +#define PD67_EXT_DATA 0x2f /* Extension data */ +#define PD67_EXD_VS1(s) (0x01 << ((s)<<1)) + +#define pd67_ext_get(s, r) \ + (i365_set(s, PD67_EXT_INDEX, r), i365_get(s, PD67_EXT_DATA)) + +static int i365_get_status (socket_info_t * s, u_int * value) +{ + u_int status; +#ifdef CONFIG_CPC45 + u_char val; + u_char power, vcc, vpp; + u_int powerstate; +#endif + + status = i365_get (s, I365_IDENT); + status = i365_get (s, I365_STATUS); + *value = ((status & I365_CS_DETECT) == I365_CS_DETECT) ? SS_DETECT : 0; + if (i365_get (s, I365_INTCTL) & I365_PC_IOCARD) { + *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG; + } else { + *value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD; + *value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN; + } + *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0; + *value |= (status & I365_CS_READY) ? SS_READY : 0; + *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0; + +#ifdef CONFIG_CPC45 + /* Check for Cirrus CL-PD67xx chips */ + i365_set (s, PD67_CHIP_INFO, 0); + val = i365_get (s, PD67_CHIP_INFO); + s->type = -1; + if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) { + val = i365_get (s, PD67_CHIP_INFO); + if ((val & PD67_INFO_CHIP_ID) == 0) { + s->type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710; + i365_set (s, PD67_EXT_INDEX, 0xe5); + if (i365_get (s, PD67_EXT_INDEX) != 0xe5) + s->type = IS_VT83C469; + } + } else { + printf ("no Cirrus Chip found\n"); + *value = 0; + return -1; + } + + power = i365_get (s, I365_POWER); + state.flags |= (power & I365_PWR_AUTO) ? SS_PWR_AUTO : 0; + state.flags |= (power & I365_PWR_OUT) ? SS_OUTPUT_ENA : 0; + vcc = power & I365_VCC_MASK; + vpp = power & I365_VPP1_MASK; + state.Vcc = state.Vpp = 0; + if((vcc== 0) || (vpp == 0)) { + /* + * On the Cirrus we get the info which card voltage + * we have in EXTERN DATA and write it to MISC_CTL1 + */ + powerstate = pd67_ext_get(s, PD67_EXTERN_DATA); + if (powerstate & PD67_EXD_VS1(0)) { + /* 5V Card */ + i365_bclr (s, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); + } else { + /* 3.3V Card */ + i365_bset (s, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); + } + i365_set (s, I365_POWER, (I365_PWR_OUT | I365_PWR_NORESET | I365_VCC_5V | I365_VPP1_5V)); + power = i365_get (s, I365_POWER); + } + if (power & I365_VCC_5V) { + state.Vcc = (i365_get(s, PD67_MISC_CTL_1) & PD67_MC1_VCC_3V) ? 33 : 50; + } + + if (power == I365_VPP1_12V) + state.Vpp = 120; + + /* IO card, RESET flags, IO interrupt */ + power = i365_get (s, I365_INTCTL); + state.flags |= (power & I365_PC_RESET) ? 0 : SS_RESET; + if (power & I365_PC_IOCARD) + state.flags |= SS_IOCARD; + state.io_irq = power & I365_IRQ_MASK; + + /* Card status change mask */ + power = i365_get (s, I365_CSCINT); + state.csc_mask = (power & I365_CSC_DETECT) ? SS_DETECT : 0; + if (state.flags & SS_IOCARD) + state.csc_mask |= (power & I365_CSC_STSCHG) ? SS_STSCHG : 0; + else { + state.csc_mask |= (power & I365_CSC_BVD1) ? SS_BATDEAD : 0; + state.csc_mask |= (power & I365_CSC_BVD2) ? SS_BATWARN : 0; + state.csc_mask |= (power & I365_CSC_READY) ? SS_READY : 0; + } + debug ("i82365: GetStatus(0) = flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x\n", state.flags, + state.Vcc, state.Vpp, state.io_irq, state.csc_mask); + +#else /* !CONFIG_CPC45 */ + + status = cb_readl (s, CB_SOCKET_STATE); + *value |= (status & CB_SS_32BIT) ? SS_CARDBUS : 0; + *value |= (status & CB_SS_3VCARD) ? SS_3VCARD : 0; + *value |= (status & CB_SS_XVCARD) ? SS_XVCARD : 0; + *value |= (status & CB_SS_VSENSE) ? 0 : SS_PENDING; + /* For now, ignore cards with unsupported voltage keys */ + if (*value & SS_XVCARD) + *value &= ~(SS_DETECT | SS_3VCARD | SS_XVCARD); +#endif /* CONFIG_CPC45 */ + return 0; +} /* i365_get_status */ + +static int i365_set_socket (socket_info_t * s, socket_state_t * state) +{ + u_char reg; + + set_bridge_state (s); + + /* IO card, RESET flag */ + reg = 0; + reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET; + reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0; + i365_set (s, I365_INTCTL, reg); + +#ifdef CONFIG_CPC45 + cb_set_power (s, state); + +#if 0 + /* Card status change interrupt mask */ + reg = s->cs_irq << 4; + if (state->csc_mask & SS_DETECT) + reg |= I365_CSC_DETECT; + if (state->flags & SS_IOCARD) { + if (state->csc_mask & SS_STSCHG) + reg |= I365_CSC_STSCHG; + } else { + if (state->csc_mask & SS_BATDEAD) + reg |= I365_CSC_BVD1; + if (state->csc_mask & SS_BATWARN) + reg |= I365_CSC_BVD2; + if (state->csc_mask & SS_READY) + reg |= I365_CSC_READY; + } + i365_set (s, I365_CSCINT, reg); + i365_get (s, I365_CSC); +#endif /* 0 */ + +#else /* !CONFIG_CPC45 */ + + reg = I365_PWR_NORESET; + if (state->flags & SS_PWR_AUTO) + reg |= I365_PWR_AUTO; + if (state->flags & SS_OUTPUT_ENA) + reg |= I365_PWR_OUT; + + cb_set_power (s, state); + reg |= i365_get (s, I365_POWER) & (I365_VCC_MASK | I365_VPP1_MASK); + + if (reg != i365_get (s, I365_POWER)) + i365_set (s, I365_POWER, reg); +#endif /* CONFIG_CPC45 */ + + return 0; +} /* i365_set_socket */ + +/*====================================================================*/ + +static int i365_set_mem_map (socket_info_t * s, struct pccard_mem_map *mem) +{ + u_short base, i; + u_char map; + + debug ("i82365: SetMemMap(%d, %#2.2x, %d ns, %#5.5lx-%#5.5lx, %#5.5x)\n", + mem->map, mem->flags, mem->speed, + mem->sys_start, mem->sys_stop, mem->card_start); + + map = mem->map; + if ((map > 4) || + (mem->card_start > 0x3ffffff) || + (mem->sys_start > mem->sys_stop) || + (mem->speed > 1000)) { + return -1; + } + + /* Turn off the window before changing anything */ + if (i365_get (s, I365_ADDRWIN) & I365_ENA_MEM (map)) + i365_bclr (s, I365_ADDRWIN, I365_ENA_MEM (map)); + + /* Take care of high byte, for PCI controllers */ + i365_set (s, CB_MEM_PAGE (map), mem->sys_start >> 24); + + base = I365_MEM (map); + i = (mem->sys_start >> 12) & 0x0fff; + if (mem->flags & MAP_16BIT) + i |= I365_MEM_16BIT; + if (mem->flags & MAP_0WS) + i |= I365_MEM_0WS; + i365_set_pair (s, base + I365_W_START, i); + + i = (mem->sys_stop >> 12) & 0x0fff; + switch (mem->speed / CYCLE_TIME) { + case 0: + break; + case 1: + i |= I365_MEM_WS0; + break; + case 2: + i |= I365_MEM_WS1; + break; + default: + i |= I365_MEM_WS1 | I365_MEM_WS0; + break; + } + i365_set_pair (s, base + I365_W_STOP, i); + +#ifdef CONFIG_CPC45 + i = 0; +#else + i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; +#endif + if (mem->flags & MAP_WRPROT) + i |= I365_MEM_WRPROT; + if (mem->flags & MAP_ATTRIB) + i |= I365_MEM_REG; + i365_set_pair (s, base + I365_W_OFF, i); + +#ifdef CONFIG_CPC45 + /* set System Memory map Upper Adress */ + i365_set(s, PD67_EXT_INDEX, PD67_MEM_PAGE(map)); + i365_set(s, PD67_EXT_DATA, ((mem->sys_start >> 24) & 0xff)); +#endif + + /* Turn on the window if necessary */ + if (mem->flags & MAP_ACTIVE) + i365_bset (s, I365_ADDRWIN, I365_ENA_MEM (map)); + return 0; +} /* i365_set_mem_map */ + +static int i365_set_io_map (socket_info_t * s, struct pccard_io_map *io) +{ + u_char map, ioctl; + + map = io->map; + /* comment out: comparison is always false due to limited range of data type */ + if ((map > 1) || /* (io->start > 0xffff) || (io->stop > 0xffff) || */ + (io->stop < io->start)) + return -1; + /* Turn off the window before changing anything */ + if (i365_get (s, I365_ADDRWIN) & I365_ENA_IO (map)) + i365_bclr (s, I365_ADDRWIN, I365_ENA_IO (map)); + i365_set_pair (s, I365_IO (map) + I365_W_START, io->start); + i365_set_pair (s, I365_IO (map) + I365_W_STOP, io->stop); + ioctl = i365_get (s, I365_IOCTL) & ~I365_IOCTL_MASK (map); + if (io->speed) + ioctl |= I365_IOCTL_WAIT (map); + if (io->flags & MAP_0WS) + ioctl |= I365_IOCTL_0WS (map); + if (io->flags & MAP_16BIT) + ioctl |= I365_IOCTL_16BIT (map); + if (io->flags & MAP_AUTOSZ) + ioctl |= I365_IOCTL_IOCS16 (map); + i365_set (s, I365_IOCTL, ioctl); + /* Turn on the window if necessary */ + if (io->flags & MAP_ACTIVE) + i365_bset (s, I365_ADDRWIN, I365_ENA_IO (map)); + return 0; +} /* i365_set_io_map */ + +/*====================================================================*/ + +int i82365_init (void) +{ + u_int val; + int i; + +#ifdef CONFIG_CPC45 + if (SPD67290Init () != 0) + return 1; +#endif + if ((socket.dev = pci_find_devices (supported, 0)) < 0) { + /* Controller not found */ + return 1; + } + debug ("i82365 Device Found!\n"); + + pci_read_config_dword (socket.dev, PCI_BASE_ADDRESS_0, &socket.cb_phys); + socket.cb_phys &= ~0xf; + +#ifdef CONFIG_CPC45 + /* + 0xfe000000 see MPC 8245 Users Manual Adress Map B */ + socket.cb_phys += 0xfe000000; +#endif + + get_bridge_state (&socket); + set_bridge_opts (&socket); + + i = i365_get_status (&socket, &val); + +#ifdef CONFIG_CPC45 + if (i > -1) { + puts (pcic[socket.type].name); + } else { + printf ("i82365: Controller not found.\n"); + return 1; + } + if((val & SS_DETECT) != SS_DETECT){ + puts ("No card\n"); + return 1; + } +#else /* !CONFIG_CPC45 */ + if (val & SS_DETECT) { + if (val & SS_3VCARD) { + state.Vcc = state.Vpp = 33; + puts (" 3.3V card found: "); + } else if (!(val & SS_XVCARD)) { + state.Vcc = state.Vpp = 50; + puts (" 5.0V card found: "); + } else { + puts ("i82365: unsupported voltage key\n"); + state.Vcc = state.Vpp = 0; + } + } else { + /* No card inserted */ + puts ("No card\n"); + return 1; + } +#endif /* CONFIG_CPC45 */ + +#ifdef CONFIG_CPC45 + state.flags |= SS_OUTPUT_ENA; +#else + state.flags = SS_IOCARD | SS_OUTPUT_ENA; + state.csc_mask = 0; + state.io_irq = 0; +#endif + + i365_set_socket (&socket, &state); + + for (i = 500; i; i--) { + if ((i365_get (&socket, I365_STATUS) & I365_CS_READY)) + break; + udelay (1000); + } + + if (i == 0) { + /* PC Card not ready for data transfer */ + puts ("i82365 PC Card not ready for data transfer\n"); + return 1; + } + debug (" PC Card ready for data transfer: "); + + mem.map = 0; + mem.flags = MAP_ATTRIB | MAP_ACTIVE; + mem.speed = 300; + mem.sys_start = CONFIG_SYS_PCMCIA_MEM_ADDR; + mem.sys_stop = CONFIG_SYS_PCMCIA_MEM_ADDR + CONFIG_SYS_PCMCIA_MEM_SIZE - 1; + mem.card_start = 0; + i365_set_mem_map (&socket, &mem); + +#ifdef CONFIG_CPC45 + mem.map = 1; + mem.flags = MAP_ACTIVE; + mem.speed = 300; + mem.sys_start = CONFIG_SYS_PCMCIA_MEM_ADDR + CONFIG_SYS_PCMCIA_MEM_SIZE; + mem.sys_stop = CONFIG_SYS_PCMCIA_MEM_ADDR + (2 * CONFIG_SYS_PCMCIA_MEM_SIZE) - 1; + mem.card_start = 0; + i365_set_mem_map (&socket, &mem); + +#else /* !CONFIG_CPC45 */ + + io.map = 0; + io.flags = MAP_AUTOSZ | MAP_ACTIVE; + io.speed = 0; + io.start = 0x0100; + io.stop = 0x010F; + i365_set_io_map (&socket, &io); + +#endif /* CONFIG_CPC45 */ + +#ifdef DEBUG + i82365_dump_regions (socket.dev); +#endif + + return 0; +} + +void i82365_exit (void) +{ + io.map = 0; + io.flags = 0; + io.speed = 0; + io.start = 0; + io.stop = 0x1; + + i365_set_io_map (&socket, &io); + + mem.map = 0; + mem.flags = 0; + mem.speed = 0; + mem.sys_start = 0; + mem.sys_stop = 0x1000; + mem.card_start = 0; + + i365_set_mem_map (&socket, &mem); + +#ifdef CONFIG_CPC45 + mem.map = 1; + mem.flags = 0; + mem.speed = 0; + mem.sys_start = 0; + mem.sys_stop = 0x1000; + mem.card_start = 0; + + i365_set_mem_map (&socket, &mem); +#else /* !CONFIG_CPC45 */ + socket.state.sysctl &= 0xFFFF00FF; +#endif + state.Vcc = state.Vpp = 0; + + i365_set_socket (&socket, &state); +} + +/*====================================================================== + + Debug stuff + +======================================================================*/ + +#ifdef DEBUG +static void i82365_dump_regions (pci_dev_t dev) +{ + u_int tmp[2]; + u_int *mem = (void *) socket.cb_phys; + u_char *cis = (void *) CONFIG_SYS_PCMCIA_MEM_ADDR; + u_char *ide = (void *) (CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_REG_OFFSET); + + pci_read_config_dword (dev, 0x00, tmp + 0); + pci_read_config_dword (dev, 0x80, tmp + 1); + + printf ("PCI CONF: %08X ... %08X\n", + tmp[0], tmp[1]); + printf ("PCI MEM: ... %08X ... %08X\n", + mem[0x8 / 4], mem[0x800 / 4]); + printf ("CIS: ...%c%c%c%c%c%c%c%c...\n", + cis[0x38], cis[0x3a], cis[0x3c], cis[0x3e], + cis[0x40], cis[0x42], cis[0x44], cis[0x48]); + printf ("CIS CONF: %02X %02X %02X ...\n", + cis[0x200], cis[0x202], cis[0x204]); + printf ("IDE: %02X %02X %02X %02X %02X %02X %02X %02X\n", + ide[0], ide[1], ide[2], ide[3], + ide[4], ide[5], ide[6], ide[7]); +} +#endif /* DEBUG */ diff --git a/qemu/roms/u-boot/drivers/pcmcia/marubun_pcmcia.c b/qemu/roms/u-boot/drivers/pcmcia/marubun_pcmcia.c new file mode 100644 index 000000000..afd6df644 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pcmcia/marubun_pcmcia.c @@ -0,0 +1,100 @@ +/* + * Marubun MR-SHPC-01 PCMCIA controller device driver + * + * (c) 2007 Nobuhiro Iwamatsu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#undef CONFIG_PCMCIA + +#if defined(CONFIG_CMD_PCMCIA) +#define CONFIG_PCMCIA +#endif + +#if defined(CONFIG_CMD_IDE) +#define CONFIG_PCMCIA +#endif + +#if defined(CONFIG_PCMCIA) + +/* MR-SHPC-01 register */ +#define MRSHPC_MODE (CONFIG_SYS_MARUBUN_MRSHPC + 4) +#define MRSHPC_OPTION (CONFIG_SYS_MARUBUN_MRSHPC + 6) +#define MRSHPC_CSR (CONFIG_SYS_MARUBUN_MRSHPC + 8) +#define MRSHPC_ISR (CONFIG_SYS_MARUBUN_MRSHPC + 10) +#define MRSHPC_ICR (CONFIG_SYS_MARUBUN_MRSHPC + 12) +#define MRSHPC_CPWCR (CONFIG_SYS_MARUBUN_MRSHPC + 14) +#define MRSHPC_MW0CR1 (CONFIG_SYS_MARUBUN_MRSHPC + 16) +#define MRSHPC_MW1CR1 (CONFIG_SYS_MARUBUN_MRSHPC + 18) +#define MRSHPC_IOWCR1 (CONFIG_SYS_MARUBUN_MRSHPC + 20) +#define MRSHPC_MW0CR2 (CONFIG_SYS_MARUBUN_MRSHPC + 22) +#define MRSHPC_MW1CR2 (CONFIG_SYS_MARUBUN_MRSHPC + 24) +#define MRSHPC_IOWCR2 (CONFIG_SYS_MARUBUN_MRSHPC + 26) +#define MRSHPC_CDCR (CONFIG_SYS_MARUBUN_MRSHPC + 28) +#define MRSHPC_PCIC_INFO (CONFIG_SYS_MARUBUN_MRSHPC + 30) + +int pcmcia_on (void) +{ + printf("Enable PCMCIA " PCMCIA_SLOT_MSG "\n"); + + /* Init */ + outw( 0x0000 , MRSHPC_MODE ); + + if ((inw(MRSHPC_CSR) & 0x000c) == 0){ /* if card detect is true */ + if ((inw(MRSHPC_CSR) & 0x0080) == 0){ + outw(0x0674 ,MRSHPC_CPWCR); /* Card Vcc is 3.3v? */ + }else{ + outw(0x0678 ,MRSHPC_CPWCR); /* Card Vcc is 5V */ + } + udelay( 100000 ); /* wait for power on */ + }else{ + return 1; + } + /* + * PC-Card window open + * flag == COMMON/ATTRIBUTE/IO + */ + /* common window open */ + outw(0x8a84,MRSHPC_MW0CR1); /* window 0xb8400000 */ + if ((inw(MRSHPC_CSR) & 0x4000) != 0) + outw(0x0b00,MRSHPC_MW0CR2); /* common mode & bus width 16bit SWAP = 1 */ + else + outw(0x0300,MRSHPC_MW0CR2); /* common mode & bus width 16bit SWAP = 0 */ + + /* attribute window open */ + outw(0x8a85,MRSHPC_MW1CR1); /* window 0xb8500000 */ + if ((inw(MRSHPC_CSR) & 0x4000) != 0) + outw(0x0a00,MRSHPC_MW1CR2); /* attribute mode & bus width 16bit SWAP = 1 */ + else + outw(0x0200,MRSHPC_MW1CR2); /* attribute mode & bus width 16bit SWAP = 0 */ + + /* I/O window open */ + outw(0x8a86,MRSHPC_IOWCR1); /* I/O window 0xb8600000 */ + outw(0x0008,MRSHPC_CDCR); /* I/O card mode */ + if ((inw(MRSHPC_CSR) & 0x4000) != 0) + outw(0x0a00,MRSHPC_IOWCR2); /* bus width 16bit SWAP = 1 */ + else + outw(0x0200,MRSHPC_IOWCR2); /* bus width 16bit SWAP = 0 */ + + outw(0x0000,MRSHPC_ISR); + outw(0x2000,MRSHPC_ICR); + outb(0x00,(CONFIG_SYS_MARUBUN_MW2 + 0x206)); + outb(0x42,(CONFIG_SYS_MARUBUN_MW2 + 0x200)); + + return 0; +} + +int pcmcia_off (void) +{ + printf ("Disable PCMCIA " PCMCIA_SLOT_MSG "\n"); + + return 0; +} + +#endif /* CONFIG_PCMCIA */ diff --git a/qemu/roms/u-boot/drivers/pcmcia/mpc8xx_pcmcia.c b/qemu/roms/u-boot/drivers/pcmcia/mpc8xx_pcmcia.c new file mode 100644 index 000000000..663827780 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pcmcia/mpc8xx_pcmcia.c @@ -0,0 +1,274 @@ +#include +#include +#include +#include + +#undef CONFIG_PCMCIA + +#if defined(CONFIG_CMD_PCMCIA) +#define CONFIG_PCMCIA +#endif + +#if defined(CONFIG_CMD_IDE) && defined(CONFIG_IDE_8xx_PCCARD) +#define CONFIG_PCMCIA +#endif + +#if defined(CONFIG_PCMCIA) + +#if defined(CONFIG_IDE_8xx_PCCARD) +extern int check_ide_device (int slot); +#endif + +extern int pcmcia_hardware_enable (int slot); +extern int pcmcia_voltage_set(int slot, int vcc, int vpp); + +#if defined(CONFIG_CMD_PCMCIA) +extern int pcmcia_hardware_disable(int slot); +#endif + +static u_int m8xx_get_graycode(u_int size); +#if 0 /* Disabled */ +static u_int m8xx_get_speed(u_int ns, u_int is_io); +#endif + +/* look up table for pgcrx registers */ +u_int *pcmcia_pgcrx[2] = { + &((immap_t *)CONFIG_SYS_IMMR)->im_pcmcia.pcmc_pgcra, + &((immap_t *)CONFIG_SYS_IMMR)->im_pcmcia.pcmc_pgcrb, +}; + +/* + * Search this table to see if the windowsize is + * supported... + */ + +#define M8XX_SIZES_NO 32 + +static const u_int m8xx_size_to_gray[M8XX_SIZES_NO] = +{ 0x00000001, 0x00000002, 0x00000008, 0x00000004, + 0x00000080, 0x00000040, 0x00000010, 0x00000020, + 0x00008000, 0x00004000, 0x00001000, 0x00002000, + 0x00000100, 0x00000200, 0x00000800, 0x00000400, + + 0x0fffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x01000000, 0x02000000, 0xffffffff, 0x04000000, + 0x00010000, 0x00020000, 0x00080000, 0x00040000, + 0x00800000, 0x00400000, 0x00100000, 0x00200000 }; + + +/* -------------------------------------------------------------------- */ + +#if defined(CONFIG_LWMON) || defined(CONFIG_NSCU) +#define CONFIG_SYS_PCMCIA_TIMING ( PCMCIA_SHT(9) \ + | PCMCIA_SST(3) \ + | PCMCIA_SL(12)) +#else +#define CONFIG_SYS_PCMCIA_TIMING ( PCMCIA_SHT(2) \ + | PCMCIA_SST(4) \ + | PCMCIA_SL(9)) +#endif + +/* -------------------------------------------------------------------- */ + +int pcmcia_on (void) +{ + u_long reg, base; + pcmcia_win_t *win; + u_int rc, slot; + __maybe_unused u_int slotbit; + int i; + + debug ("Enable PCMCIA " PCMCIA_SLOT_MSG "\n"); + + /* intialize the fixed memory windows */ + win = (pcmcia_win_t *)(&((immap_t *)CONFIG_SYS_IMMR)->im_pcmcia.pcmc_pbr0); + base = CONFIG_SYS_PCMCIA_MEM_ADDR; + + if((reg = m8xx_get_graycode(CONFIG_SYS_PCMCIA_MEM_SIZE)) == -1) { + printf ("Cannot set window size to 0x%08x\n", + CONFIG_SYS_PCMCIA_MEM_SIZE); + return (1); + } + + slotbit = PCMCIA_SLOT_x; + for (i=0; ibr = base; + +#if (PCMCIA_SOCKETS_NO == 2) + if (i == 4) /* Another slot starting from win 4 */ + slotbit = (slotbit ? PCMCIA_PSLOT_A : PCMCIA_PSLOT_B); +#endif + switch (i) { +#ifdef CONFIG_IDE_8xx_PCCARD + case 4: + case 0: { /* map attribute memory */ + win->or = ( PCMCIA_BSIZE_64M + | PCMCIA_PPS_8 + | PCMCIA_PRS_ATTR + | slotbit + | PCMCIA_PV + | CONFIG_SYS_PCMCIA_TIMING ); + break; + } + case 5: + case 1: { /* map I/O window for data reg */ + win->or = ( PCMCIA_BSIZE_1K + | PCMCIA_PPS_16 + | PCMCIA_PRS_IO + | slotbit + | PCMCIA_PV + | CONFIG_SYS_PCMCIA_TIMING ); + break; + } + case 6: + case 2: { /* map I/O window for cmd/ctrl reg block */ + win->or = ( PCMCIA_BSIZE_1K + | PCMCIA_PPS_8 + | PCMCIA_PRS_IO + | slotbit + | PCMCIA_PV + | CONFIG_SYS_PCMCIA_TIMING ); + break; + } +#endif /* CONFIG_IDE_8xx_PCCARD */ + default: /* set to not valid */ + win->or = 0; + break; + } + + debug ("MemWin %d: PBR 0x%08lX POR %08lX\n", + i, win->br, win->or); + base += CONFIG_SYS_PCMCIA_MEM_SIZE; + ++win; + } + + for (i=0, rc=0, slot=_slot_; iim_pcmcia.pcmc_pscr = PCMCIA_MASK(_slot_); + ((immap_t *)CONFIG_SYS_IMMR)->im_pcmcia.pcmc_per &= ~PCMCIA_MASK(_slot_); + + /* turn off interrupt and disable CxOE */ + PCMCIA_PGCRX(_slot_) = __MY_PCMCIA_GCRX_CXOE; + + /* turn off memory windows */ + win = (pcmcia_win_t *)(&((immap_t *)CONFIG_SYS_IMMR)->im_pcmcia.pcmc_pbr0); + + for (i=0; ior = 0; + ++win; + } + + /* turn off voltage */ + pcmcia_voltage_set(_slot_, 0, 0); + + /* disable external hardware */ + printf ("Shutdown and Poweroff " PCMCIA_SLOT_MSG "\n"); + pcmcia_hardware_disable(_slot_); + return 0; +} +#endif + + +static u_int m8xx_get_graycode(u_int size) +{ + u_int k; + + for (k = 0; k < M8XX_SIZES_NO; k++) { + if(m8xx_size_to_gray[k] == size) + break; + } + + if((k == M8XX_SIZES_NO) || (m8xx_size_to_gray[k] == -1)) + k = -1; + + return k; +} + +#if 0 + +#if defined(CONFIG_RPXLITE) + +/* The RPX boards seems to have it's bus monitor timeout set to 6*8 clocks. + * SYPCR is write once only, therefore must the slowest memory be faster + * than the bus monitor or we will get a machine check due to the bus timeout. + */ +#undef PCMCIA_BMT_LIMIT +#define PCMCIA_BMT_LIMIT (6*8) +#endif + +static u_int m8xx_get_speed(u_int ns, u_int is_io) +{ + u_int reg, clocks, psst, psl, psht; + + if(!ns) { + + /* + * We get called with IO maps setup to 0ns + * if not specified by the user. + * They should be 255ns. + */ + + if(is_io) + ns = 255; + else + ns = 100; /* fast memory if 0 */ + } + + /* + * In PSST, PSL, PSHT fields we tell the controller + * timing parameters in CLKOUT clock cycles. + * CLKOUT is the same as GCLK2_50. + */ + + /* how we want to adjust the timing - in percent */ + +#define ADJ 180 /* 80 % longer accesstime - to be sure */ + + clocks = ((M8XX_BUSFREQ / 1000) * ns) / 1000; + clocks = (clocks * ADJ) / (100*1000); + + if(clocks >= PCMCIA_BMT_LIMIT) { + DEBUG(0, "Max access time limit reached\n"); + clocks = PCMCIA_BMT_LIMIT-1; + } + + psst = clocks / 7; /* setup time */ + psht = clocks / 7; /* hold time */ + psl = (clocks * 5) / 7; /* strobe length */ + + psst += clocks - (psst + psht + psl); + + reg = psst << 12; + reg |= psl << 7; + reg |= psht << 16; + + return reg; +} +#endif /* 0 */ + +#endif /* CONFIG_PCMCIA */ diff --git a/qemu/roms/u-boot/drivers/pcmcia/rpx_pcmcia.c b/qemu/roms/u-boot/drivers/pcmcia/rpx_pcmcia.c new file mode 100644 index 000000000..5b24f0bfb --- /dev/null +++ b/qemu/roms/u-boot/drivers/pcmcia/rpx_pcmcia.c @@ -0,0 +1,73 @@ +/* -------------------------------------------------------------------- */ +/* RPX Boards from Embedded Planet */ +/* -------------------------------------------------------------------- */ +#include +#ifdef CONFIG_8xx +#include +#endif +#include + +#undef CONFIG_PCMCIA + +#if defined(CONFIG_CMD_PCMCIA) +#define CONFIG_PCMCIA +#endif + +#if defined(CONFIG_CMD_IDE) && defined(CONFIG_IDE_8xx_PCCARD) +#define CONFIG_PCMCIA +#endif + +#if defined(CONFIG_PCMCIA) \ + && defined(CONFIG_RPXLITE) + +#define PCMCIA_BOARD_MSG "RPX CLASSIC or RPX LITE" + +int pcmcia_voltage_set(int slot, int vcc, int vpp) +{ + u_long reg = 0; + + switch(vcc) { + case 0: break; + case 33: reg |= BCSR1_PCVCTL4; break; + case 50: reg |= BCSR1_PCVCTL5; break; + default: return 1; + } + + switch(vpp) { + case 0: break; + case 33: + case 50: + if(vcc == vpp) + reg |= BCSR1_PCVCTL6; + else + return 1; + break; + case 120: + reg |= BCSR1_PCVCTL7; + default: return 1; + } + + /* first, turn off all power */ + *((uint *)RPX_CSR_ADDR) &= ~(BCSR1_PCVCTL4 | BCSR1_PCVCTL5 + | BCSR1_PCVCTL6 | BCSR1_PCVCTL7); + + /* enable new powersettings */ + *((uint *)RPX_CSR_ADDR) |= reg; + + return 0; +} + +int pcmcia_hardware_enable (int slot) +{ + return 0; /* No hardware to enable */ +} + +#if defined(CONFIG_CMD_PCMCIA) +static int pcmcia_hardware_disable(int slot) +{ + return 0; /* No hardware to disable */ +} +#endif + + +#endif /* CONFIG_PCMCIA && CONFIG_RPXLITE */ diff --git a/qemu/roms/u-boot/drivers/pcmcia/ti_pci1410a.c b/qemu/roms/u-boot/drivers/pcmcia/ti_pci1410a.c new file mode 100644 index 000000000..d83db3f02 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pcmcia/ti_pci1410a.c @@ -0,0 +1,623 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB + * + * SPDX-License-Identifier: GPL-2.0+ + * + ******************************************************************** + * + * Lots of code copied from: + * + * m8xx_pcmcia.c - Linux PCMCIA socket driver for the mpc8xx series. + * (C) 1999-2000 Magnus Damm + * + * "The ExCA standard specifies that socket controllers should provide + * two IO and five memory windows per socket, which can be independently + * configured and positioned in the host address space and mapped to + * arbitrary segments of card address space. " - David A Hinds. 1999 + * + * This controller does _not_ meet the ExCA standard. + * + * m8xx pcmcia controller brief info: + * + 8 windows (attrib, mem, i/o) + * + up to two slots (SLOT_A and SLOT_B) + * + inputpins, outputpins, event and mask registers. + * - no offset register. sigh. + * + * Because of the lacking offset register we must map the whole card. + * We assign each memory window PCMCIA_MEM_WIN_SIZE address space. + * Make sure there is (PCMCIA_MEM_WIN_SIZE * PCMCIA_MEM_WIN_NO + * * PCMCIA_SOCKETS_NO) bytes at PCMCIA_MEM_WIN_BASE. + * The i/o windows are dynamically allocated at PCMCIA_IO_WIN_BASE. + * They are maximum 64KByte each... + */ + + +#undef DEBUG /**/ + +/* + * PCMCIA support + */ +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_CMD_PCMCIA) + +int pcmcia_on(int ide_base_bus); + +static int hardware_disable(int slot); +static int hardware_enable(int slot); +static int voltage_set(int slot, int vcc, int vpp); +static void print_funcid(int func); +static void print_fixed(volatile char *p); +static int identify(volatile char *p); +static int check_ide_device(int slot, int ide_base_bus); + + +/* ------------------------------------------------------------------------- */ + + +const char *indent = "\t "; + +/* ------------------------------------------------------------------------- */ + + +static struct pci_device_id supported[] = { + { PCI_VENDOR_ID_TI, 0xac50 }, /* Ti PCI1410A */ + { PCI_VENDOR_ID_TI, 0xac56 }, /* Ti PCI1510 */ + { } +}; + +static pci_dev_t devbusfn; +static u32 socket_base; +static u32 pcmcia_cis_ptr; + +int pcmcia_on(int ide_base_bus) +{ + u16 dev_id; + u32 socket_status; + int slot = 0; + int cis_len; + u16 io_base; + u16 io_len; + + /* + * Find the CardBus PCI device(s). + */ + if ((devbusfn = pci_find_devices(supported, 0)) < 0) { + printf("Ti CardBus: not found\n"); + return 1; + } + + pci_read_config_word(devbusfn, PCI_DEVICE_ID, &dev_id); + + if (dev_id == 0xac56) { + debug("Enable PCMCIA Ti PCI1510\n"); + } else { + debug("Enable PCMCIA Ti PCI1410A\n"); + } + + pcmcia_cis_ptr = CONFIG_SYS_PCMCIA_CIS_WIN; + cis_len = CONFIG_SYS_PCMCIA_CIS_WIN_SIZE; + + io_base = CONFIG_SYS_PCMCIA_IO_WIN; + io_len = CONFIG_SYS_PCMCIA_IO_WIN_SIZE; + + /* + * Setup the PCI device. + */ + pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &socket_base); + socket_base &= ~0xf; + + socket_status = readl(socket_base+8); + if ((socket_status & 6) == 0) { + printf("Card Present: "); + + switch (socket_status & 0x3c00) { + + case 0x400: + printf("5V "); + break; + case 0x800: + printf("3.3V "); + break; + case 0xc00: + printf("3.3/5V "); + break; + default: + printf("unsupported Vcc "); + break; + } + switch (socket_status & 0x30) { + case 0x10: + printf("16bit PC-Card\n"); + break; + case 0x20: + printf("32bit CardBus Card\n"); + break; + default: + printf("8bit PC-Card\n"); + break; + } + } + + + writeb(0x41, socket_base + 0x806); /* Enable I/O window 0 and memory window 0 */ + writeb(0x0e, socket_base + 0x807); /* Reset I/O window options */ + + /* Careful: the linux yenta driver do not seem to reset the offset + * in the i/o windows, so leaving them non-zero is a problem */ + + writeb(io_base & 0xff, socket_base + 0x808); /* I/O window 0 base address */ + writeb(io_base>>8, socket_base + 0x809); + writeb((io_base + io_len - 1) & 0xff, socket_base + 0x80a); /* I/O window 0 end address */ + writeb((io_base + io_len - 1)>>8, socket_base + 0x80b); + writeb(0x00, socket_base + 0x836); /* I/O window 0 offset address 0x000 */ + writeb(0x00, socket_base + 0x837); + + + writeb((pcmcia_cis_ptr&0x000ff000) >> 12, + socket_base + 0x810); /* Memory window 0 start address bits 19-12 */ + writeb((pcmcia_cis_ptr&0x00f00000) >> 20, + socket_base + 0x811); /* Memory window 0 start address bits 23-20 */ + writeb(((pcmcia_cis_ptr+cis_len-1) & 0x000ff000) >> 12, + socket_base + 0x812); /* Memory window 0 end address bits 19-12*/ + writeb(((pcmcia_cis_ptr+cis_len-1) & 0x00f00000) >> 20, + socket_base + 0x813); /* Memory window 0 end address bits 23-20*/ + writeb(0x00, socket_base + 0x814); /* Memory window 0 offset bits 19-12 */ + writeb(0x40, socket_base + 0x815); /* Memory window 0 offset bits 23-20 and + * options (read/write, attribute access) */ + writeb(0x00, socket_base + 0x816); /* ExCA card-detect and general control */ + writeb(0x00, socket_base + 0x81e); /* ExCA global control (interrupt modes) */ + + writeb((pcmcia_cis_ptr & 0xff000000) >> 24, + socket_base + 0x840); /* Memory window address bits 31-24 */ + + + /* turn off voltage */ + if (voltage_set(slot, 0, 0)) { + return 1; + } + + /* Enable external hardware */ + if (hardware_enable(slot)) { + return 1; + } + + if (check_ide_device(slot, ide_base_bus)) { + return 1; + } + + return 0; +} + +/* ------------------------------------------------------------------------- */ + + +#if defined(CONFIG_CMD_PCMCIA) +int pcmcia_off (void) +{ + int slot = 0; + + writeb(0x00, socket_base + 0x806); /* disable all I/O and memory windows */ + + writeb(0x00, socket_base + 0x808); /* I/O window 0 base address */ + writeb(0x00, socket_base + 0x809); + writeb(0x00, socket_base + 0x80a); /* I/O window 0 end address */ + writeb(0x00, socket_base + 0x80b); + writeb(0x00, socket_base + 0x836); /* I/O window 0 offset address */ + writeb(0x00, socket_base + 0x837); + + writeb(0x00, socket_base + 0x80c); /* I/O window 1 base address */ + writeb(0x00, socket_base + 0x80d); + writeb(0x00, socket_base + 0x80e); /* I/O window 1 end address */ + writeb(0x00, socket_base + 0x80f); + writeb(0x00, socket_base + 0x838); /* I/O window 1 offset address */ + writeb(0x00, socket_base + 0x839); + + writeb(0x00, socket_base + 0x810); /* Memory window 0 start address */ + writeb(0x00, socket_base + 0x811); + writeb(0x00, socket_base + 0x812); /* Memory window 0 end address */ + writeb(0x00, socket_base + 0x813); + writeb(0x00, socket_base + 0x814); /* Memory window 0 offset */ + writeb(0x00, socket_base + 0x815); + + writeb(0xc0, socket_base + 0x840); /* Memory window 0 page address */ + + + /* turn off voltage */ + voltage_set(slot, 0, 0); + + /* disable external hardware */ + printf ("Shutdown and Poweroff Ti PCI1410A\n"); + hardware_disable(slot); + + return 0; +} + +#endif + +/* ------------------------------------------------------------------------- */ + + +#define MAX_TUPEL_SZ 512 +#define MAX_FEATURES 4 +int ide_devices_found; +static int check_ide_device(int slot, int ide_base_bus) +{ + volatile char *ident = NULL; + volatile char *feature_p[MAX_FEATURES]; + volatile char *p, *start; + int n_features = 0; + uchar func_id = ~0; + uchar code, len; + ushort config_base = 0; + int found = 0; + int i; + u32 socket_status; + + debug ("PCMCIA MEM: %08X\n", pcmcia_cis_ptr); + + socket_status = readl(socket_base+8); + + if ((socket_status & 6) != 0 || (socket_status & 0x20) != 0) { + printf("no card or CardBus card\n"); + return 1; + } + + start = p = (volatile char *) pcmcia_cis_ptr; + + while ((p - start) < MAX_TUPEL_SZ) { + + code = *p; p += 2; + + if (code == 0xFF) { /* End of chain */ + break; + } + + len = *p; p += 2; +#if defined(DEBUG) && (DEBUG > 1) + { + volatile uchar *q = p; + printf ("\nTuple code %02x length %d\n\tData:", + code, len); + + for (i = 0; i < len; ++i) { + printf (" %02x", *q); + q+= 2; + } + } +#endif /* DEBUG */ + switch (code) { + case CISTPL_VERS_1: + ident = p + 4; + break; + case CISTPL_FUNCID: + /* Fix for broken SanDisk which may have 0x80 bit set */ + func_id = *p & 0x7F; + break; + case CISTPL_FUNCE: + if (n_features < MAX_FEATURES) + feature_p[n_features++] = p; + break; + case CISTPL_CONFIG: + config_base = (*(p+6) << 8) + (*(p+4)); + debug ("\n## Config_base = %04x ###\n", config_base); + default: + break; + } + p += 2 * len; + } + + found = identify(ident); + + if (func_id != ((uchar)~0)) { + print_funcid (func_id); + + if (func_id == CISTPL_FUNCID_FIXED) + found = 1; + else + return 1; /* no disk drive */ + } + + for (i=0; i id_str) { + if (*t == ' ') { + *t = '\0'; + } else { + break; + } + } + puts(id_str); + putc('\n'); + + for (card=known_cards; *card; ++card) { + debug ("## Compare against \"%s\"\n", *card); + if (strcmp(*card, id_str) == 0) { /* found! */ + debug ("## CARD FOUND ##\n"); + return 1; + } + } + + return 0; /* don't know */ +} + +#endif /* CONFIG_CMD_PCMCIA */ diff --git a/qemu/roms/u-boot/drivers/pcmcia/tqm8xx_pcmcia.c b/qemu/roms/u-boot/drivers/pcmcia/tqm8xx_pcmcia.c new file mode 100644 index 000000000..dda7d3744 --- /dev/null +++ b/qemu/roms/u-boot/drivers/pcmcia/tqm8xx_pcmcia.c @@ -0,0 +1,305 @@ +/* -------------------------------------------------------------------- */ +/* TQM8xxL Boards by TQ Components */ +/* SC8xx Boards by SinoVee Microsystems */ +/* -------------------------------------------------------------------- */ +#include +#include +#ifdef CONFIG_8xx +#include +#endif +#include + +#undef CONFIG_PCMCIA + +#if defined(CONFIG_CMD_PCMCIA) +#define CONFIG_PCMCIA +#endif + +#if defined(CONFIG_CMD_IDE) && defined(CONFIG_IDE_8xx_PCCARD) +#define CONFIG_PCMCIA +#endif + +#if defined(CONFIG_PCMCIA) \ + && (defined(CONFIG_TQM8xxL) || defined(CONFIG_SVM_SC8xx)) + +#if defined(CONFIG_VIRTLAB2) +#define PCMCIA_BOARD_MSG "Virtlab2" +#elif defined(CONFIG_TQM8xxL) +#define PCMCIA_BOARD_MSG "TQM8xxL" +#elif defined(CONFIG_SVM_SC8xx) +#define PCMCIA_BOARD_MSG "SC8xx" +#endif + +#if defined(CONFIG_NSCU) + +static inline void power_config(int slot) {} +static inline void power_off(int slot) {} +static inline void power_on_5_0(int slot) {} +static inline void power_on_3_3(int slot) {} + +#elif defined(CONFIG_VIRTLAB2) + +static inline void power_config(int slot) {} + +static inline void power_off(int slot) +{ + volatile unsigned __iomem *addr; + addr = (volatile unsigned __iomem *)PCMCIA_CTRL; + + out_be32(addr, 0); +} + +static inline void power_on_5_0(int slot) +{ + volatile unsigned __iomem *addr; + addr = (volatile unsigned __iomem *)PCMCIA_CTRL; + + /* Enable 5V Vccout */ + out_be32(addr, 2); +} + +static inline void power_on_3_3(int slot) +{ + volatile unsigned __iomem *addr; + addr = (volatile unsigned __iomem *)PCMCIA_CTRL; + + /* Enable 3.3V Vccout */ + out_be32(addr, 1); +} + +#else + +static inline void power_config(int slot) +{ + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + /* + * Configure Port C pins for + * 5 Volts Enable and 3 Volts enable + */ + clrbits_be16(&immap->im_ioport.iop_pcpar, 0x0002 | 0x0004); + clrbits_be16(&immap->im_ioport.iop_pcso, 0x0002 | 0x0004); +} + +static inline void power_off(int slot) +{ + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + clrbits_be16(&immap->im_ioport.iop_pcdat, 0x0002 | 0x0004); +} + +static inline void power_on_5_0(int slot) +{ + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + setbits_be16(&immap->im_ioport.iop_pcdat, 0x0004); + setbits_be16(&immap->im_ioport.iop_pcdir, 0x0002 | 0x0004); +} + +static inline void power_on_3_3(int slot) +{ + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + setbits_be16(&immap->im_ioport.iop_pcdat, 0x0002); + setbits_be16(&immap->im_ioport.iop_pcdir, 0x0002 | 0x0004); +} + +#endif + +/* + * Function to retrieve the PIPR register, used for debuging purposes. + */ +static inline uint32_t debug_get_pipr(void) +{ + uint32_t pipr = 0; +#ifdef DEBUG + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + pipr = in_be32(&immap->im_pcmcia.pcmc_pipr); +#endif + return pipr; +} + + +static inline int check_card_is_absent(int slot) +{ + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + uint32_t pipr = in_be32(&immap->im_pcmcia.pcmc_pipr); + return pipr & (0x18000000 >> (slot << 4)); +} + +#ifdef NSCU_OE_INV +#define NSCU_GCRX_CXOE 0 +#else +#define NSCU_GCRX_CXOE __MY_PCMCIA_GCRX_CXOE +#endif + +int pcmcia_hardware_enable(int slot) +{ + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + uint reg, mask; + + debug("hardware_enable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + udelay(10000); + + /* + * Configure SIUMCR to enable PCMCIA port B + * (VFLS[0:1] are not used for debugging, we connect FRZ# instead) + */ + + /* Set DBGC to 00 */ + clrbits_be32(&immap->im_siu_conf.sc_siumcr, SIUMCR_DBGC11); + + /* Clear interrupt state, and disable interrupts */ + out_be32(&immap->im_pcmcia.pcmc_pscr, PCMCIA_MASK(slot)); + clrbits_be32(&immap->im_pcmcia.pcmc_per, PCMCIA_MASK(slot)); + + /* + * Disable interrupts, DMA, and PCMCIA buffers + * (isolate the interface) and assert RESET signal + */ + debug("Disable PCMCIA buffers and assert RESET\n"); + reg = 0; + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= NSCU_GCRX_CXOE; + + PCMCIA_PGCRX(slot) = reg; + udelay(500); + + power_config(slot); + power_off(slot); + + /* + * Make sure there is a card in the slot, then configure the interface. + */ + udelay(10000); + reg = debug_get_pipr(); + debug("[%d] %s: PIPR(%p)=0x%x\n", __LINE__, __FUNCTION__, + &immap->im_pcmcia.pcmc_pipr, reg); + + if (check_card_is_absent(slot)) { + printf (" No Card found\n"); + return (1); + } + + /* + * Power On. + */ + mask = PCMCIA_VS1(slot) | PCMCIA_VS2(slot); + reg = in_be32(&immap->im_pcmcia.pcmc_pipr); + debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n", + reg, + (reg & PCMCIA_VS1(slot)) ? "n" : "ff", + (reg & PCMCIA_VS2(slot)) ? "n" : "ff"); + + if ((reg & mask) == mask) { + power_on_5_0(slot); + puts (" 5.0V card found: "); + } else { + power_on_3_3(slot); + puts (" 3.3V card found: "); + } + +#if 0 + /* VCC switch error flag, PCMCIA slot INPACK_ pin */ + cp->cp_pbdir &= ~(0x0020 | 0x0010); + cp->cp_pbpar &= ~(0x0020 | 0x0010); + udelay(500000); +#endif + + udelay(1000); + debug("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(slot); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + reg &= ~NSCU_GCRX_CXOE; + + PCMCIA_PGCRX(slot) = reg; + + udelay(250000); /* some cards need >150 ms to come up :-( */ + + debug("# hardware_enable done\n"); + + return (0); +} + + +#if defined(CONFIG_CMD_PCMCIA) +int pcmcia_hardware_disable(int slot) +{ + u_long reg; + + debug("hardware_disable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + /* remove all power */ + power_off(slot); + + debug("Disable PCMCIA buffers and assert RESET\n"); + reg = 0; + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= NSCU_GCRX_CXOE; /* active low */ + + PCMCIA_PGCRX(slot) = reg; + + udelay(10000); + + return (0); +} +#endif + +int pcmcia_voltage_set(int slot, int vcc, int vpp) +{ +#ifndef CONFIG_NSCU + u_long reg; + uint32_t pipr = 0; + + debug("voltage_set: " PCMCIA_BOARD_MSG + " Slot %c, Vcc=%d.%d, Vpp=%d.%d\n", + 'A'+slot, vcc/10, vcc%10, vpp/10, vcc%10); + + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(slot); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + reg |= NSCU_GCRX_CXOE; /* active low */ + + PCMCIA_PGCRX(slot) = reg; + udelay(500); + + debug("PCMCIA power OFF\n"); + power_config(slot); + power_off(slot); + + switch(vcc) { + case 0: break; + case 33: power_on_3_3(slot); break; + case 50: power_on_5_0(slot); break; + default: goto done; + } + + /* Checking supported voltages */ + pipr = debug_get_pipr(); + debug("PIPR: 0x%x --> %s\n", pipr, + (pipr & 0x00008000) ? "only 5 V" : "can do 3.3V"); + + if (vcc) + debug("PCMCIA powered at %sV\n", (vcc == 50) ? "5.0" : "3.3"); + else + debug("PCMCIA powered down\n"); + +done: + debug("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(slot); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + reg &= ~NSCU_GCRX_CXOE; /* active low */ + + PCMCIA_PGCRX(slot) = reg; + udelay(500); + + debug("voltage_set: " PCMCIA_BOARD_MSG " Slot %c, DONE\n", slot+'A'); +#endif /* CONFIG_NSCU */ + return 0; +} + +#endif /* CONFIG_PCMCIA && (CONFIG_TQM8xxL || CONFIG_SVM_SC8xx) */ diff --git a/qemu/roms/u-boot/drivers/power/Makefile b/qemu/roms/u-boot/drivers/power/Makefile new file mode 100644 index 000000000..53ff97d74 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/Makefile @@ -0,0 +1,19 @@ +# +# Copyright (c) 2009 Wind River Systems, Inc. +# Tom Rix +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o +obj-$(CONFIG_FTPMU010_POWER) += ftpmu010.o +obj-$(CONFIG_TPS6586X_POWER) += tps6586x.o +obj-$(CONFIG_TWL4030_POWER) += twl4030.o +obj-$(CONFIG_TWL6030_POWER) += twl6030.o +obj-$(CONFIG_PALMAS_POWER) += palmas.o + +obj-$(CONFIG_POWER) += power_core.o +obj-$(CONFIG_DIALOG_POWER) += power_dialog.o +obj-$(CONFIG_POWER_FSL) += power_fsl.o +obj-$(CONFIG_POWER_I2C) += power_i2c.o +obj-$(CONFIG_POWER_SPI) += power_spi.o diff --git a/qemu/roms/u-boot/drivers/power/battery/Makefile b/qemu/roms/u-boot/drivers/power/battery/Makefile new file mode 100644 index 000000000..f864f0439 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/battery/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_POWER_BATTERY_TRATS) += bat_trats.o +obj-$(CONFIG_POWER_BATTERY_TRATS2) += bat_trats2.o diff --git a/qemu/roms/u-boot/drivers/power/battery/bat_trats.c b/qemu/roms/u-boot/drivers/power/battery/bat_trats.c new file mode 100644 index 000000000..41b179fc5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/battery/bat_trats.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static struct battery battery_trats; + +static int power_battery_charge(struct pmic *bat) +{ + struct power_battery *p_bat = bat->pbat; + struct battery *battery = p_bat->bat; + int k; + + if (bat->chrg->chrg_state(p_bat->chrg, CHARGER_ENABLE, 450)) + return -1; + + for (k = 0; bat->chrg->chrg_bat_present(p_bat->chrg) && + bat->chrg->chrg_type(p_bat->muic) && + battery->state_of_chrg < 100; k++) { + udelay(2000000); + if (!(k % 5)) + puts("."); + bat->fg->fg_battery_update(p_bat->fg, bat); + + if (k == 200) { + debug(" %d [V]", battery->voltage_uV); + puts("\n"); + k = 0; + } + + if (ctrlc()) { + printf("\nCharging disabled on request.\n"); + goto exit; + } + } + exit: + bat->chrg->chrg_state(p_bat->chrg, CHARGER_DISABLE, 0); + + return 0; +} + +static int power_battery_init_trats(struct pmic *bat_, + struct pmic *fg_, + struct pmic *chrg_, + struct pmic *muic_) +{ + bat_->pbat->fg = fg_; + bat_->pbat->chrg = chrg_; + bat_->pbat->muic = muic_; + + bat_->fg = fg_->fg; + bat_->chrg = chrg_->chrg; + bat_->chrg->chrg_type = muic_->chrg->chrg_type; + return 0; +} + +static struct power_battery power_bat_trats = { + .bat = &battery_trats, + .battery_init = power_battery_init_trats, + .battery_charge = power_battery_charge, +}; + +int power_bat_init(unsigned char bus) +{ + static const char name[] = "BAT_TRATS"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board BAT init\n"); + + p->interface = PMIC_NONE; + p->name = name; + p->bus = bus; + + p->pbat = &power_bat_trats; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/battery/bat_trats2.c b/qemu/roms/u-boot/drivers/power/battery/bat_trats2.c new file mode 100644 index 000000000..94015aa41 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/battery/bat_trats2.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Piotr Wilczek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static struct battery battery_trats; + +static int power_battery_charge(struct pmic *bat) +{ + struct power_battery *p_bat = bat->pbat; + + if (bat->chrg->chrg_state(p_bat->chrg, CHARGER_ENABLE, 450)) + return -1; + + return 0; +} + +static int power_battery_init_trats2(struct pmic *bat_, + struct pmic *fg_, + struct pmic *chrg_, + struct pmic *muic_) +{ + bat_->pbat->fg = fg_; + bat_->pbat->chrg = chrg_; + bat_->pbat->muic = muic_; + + bat_->fg = fg_->fg; + bat_->chrg = chrg_->chrg; + bat_->chrg->chrg_type = muic_->chrg->chrg_type; + return 0; +} + +static struct power_battery power_bat_trats2 = { + .bat = &battery_trats, + .battery_init = power_battery_init_trats2, + .battery_charge = power_battery_charge, +}; + +int power_bat_init(unsigned char bus) +{ + static const char name[] = "BAT_TRATS2"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board BAT init\n"); + + p->interface = PMIC_NONE; + p->name = name; + p->bus = bus; + + p->pbat = &power_bat_trats2; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/exynos-tmu.c b/qemu/roms/u-boot/drivers/power/exynos-tmu.c new file mode 100644 index 000000000..9a093a5bd --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/exynos-tmu.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Akshay Saraswat + * + * EXYNOS - Thermal Management Unit + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#define TRIMINFO_RELOAD 1 +#define CORE_EN 1 +#define THERM_TRIP_EN (1 << 12) + +#define INTEN_RISE0 1 +#define INTEN_RISE1 (1 << 4) +#define INTEN_RISE2 (1 << 8) +#define INTEN_FALL0 (1 << 16) +#define INTEN_FALL1 (1 << 20) +#define INTEN_FALL2 (1 << 24) + +#define TRIM_INFO_MASK 0xff + +#define INTCLEAR_RISE0 1 +#define INTCLEAR_RISE1 (1 << 4) +#define INTCLEAR_RISE2 (1 << 8) +#define INTCLEAR_FALL0 (1 << 16) +#define INTCLEAR_FALL1 (1 << 20) +#define INTCLEAR_FALL2 (1 << 24) +#define INTCLEARALL (INTCLEAR_RISE0 | INTCLEAR_RISE1 | \ + INTCLEAR_RISE2 | INTCLEAR_FALL0 | \ + INTCLEAR_FALL1 | INTCLEAR_FALL2) + +/* Tmeperature threshold values for various thermal events */ +struct temperature_params { + /* minimum value in temperature code range */ + unsigned min_val; + /* maximum value in temperature code range */ + unsigned max_val; + /* temperature threshold to start warning */ + unsigned start_warning; + /* temperature threshold CPU tripping */ + unsigned start_tripping; + /* temperature threshold for HW tripping */ + unsigned hardware_tripping; +}; + +/* Pre-defined values and thresholds for calibration of current temperature */ +struct tmu_data { + /* pre-defined temperature thresholds */ + struct temperature_params ts; + /* pre-defined efuse range minimum value */ + unsigned efuse_min_value; + /* pre-defined efuse value for temperature calibration */ + unsigned efuse_value; + /* pre-defined efuse range maximum value */ + unsigned efuse_max_value; + /* current temperature sensing slope */ + unsigned slope; +}; + +/* TMU device specific details and status */ +struct tmu_info { + /* base Address for the TMU */ + struct exynos5_tmu_reg *tmu_base; + /* mux Address for the TMU */ + int tmu_mux; + /* pre-defined values for calibration and thresholds */ + struct tmu_data data; + /* value required for triminfo_25 calibration */ + unsigned te1; + /* value required for triminfo_85 calibration */ + unsigned te2; + /* Value for measured data calibration */ + int dc_value; + /* enum value indicating status of the TMU */ + int tmu_state; +}; + +/* Global struct tmu_info variable to store init values */ +static struct tmu_info gbl_info; + +/* + * Get current temperature code from register, + * then calculate and calibrate it's value + * in degree celsius. + * + * @return current temperature of the chip as sensed by TMU + */ +static int get_cur_temp(struct tmu_info *info) +{ + struct exynos5_tmu_reg *reg = info->tmu_base; + ulong start; + int cur_temp = 0; + + /* + * Temperature code range between min 25 and max 125. + * May run more than once for first call as initial sensing + * has not yet happened. + */ + if (info->tmu_state == TMU_STATUS_NORMAL) { + start = get_timer(0); + do { + cur_temp = readl(®->current_temp) & 0xff; + } while ((cur_temp == 0) || (get_timer(start) > 100)); + } + + if (cur_temp == 0) + return cur_temp; + + /* Calibrate current temperature */ + cur_temp = cur_temp - info->te1 + info->dc_value; + + return cur_temp; +} + +/* + * Monitors status of the TMU device and exynos temperature + * + * @param temp pointer to the current temperature value + * @return enum tmu_status_t value, code indicating event to execute + */ +enum tmu_status_t tmu_monitor(int *temp) +{ + int cur_temp; + struct tmu_data *data = &gbl_info.data; + + if (gbl_info.tmu_state == TMU_STATUS_INIT) + return TMU_STATUS_INIT; + + /* Read current temperature of the SOC */ + cur_temp = get_cur_temp(&gbl_info); + + if (!cur_temp) + goto out; + + *temp = cur_temp; + + /* Temperature code lies between min 25 and max 125 */ + if ((cur_temp >= data->ts.start_tripping) && + (cur_temp <= data->ts.max_val)) + return TMU_STATUS_TRIPPED; + + if (cur_temp >= data->ts.start_warning) + return TMU_STATUS_WARNING; + + if ((cur_temp < data->ts.start_warning) && + (cur_temp >= data->ts.min_val)) + return TMU_STATUS_NORMAL; + + out: + /* Temperature code does not lie between min 25 and max 125 */ + gbl_info.tmu_state = TMU_STATUS_INIT; + debug("EXYNOS_TMU: Thermal reading failed\n"); + return TMU_STATUS_INIT; +} + +/* + * Get TMU specific pre-defined values from FDT + * + * @param info pointer to the tmu_info struct + * @param blob FDT blob + * @return int value, 0 for success + */ +static int get_tmu_fdt_values(struct tmu_info *info, const void *blob) +{ +#ifdef CONFIG_OF_CONTROL + fdt_addr_t addr; + int node; + int error = 0; + + /* Get the node from FDT for TMU */ + node = fdtdec_next_compatible(blob, 0, + COMPAT_SAMSUNG_EXYNOS_TMU); + if (node < 0) { + debug("EXYNOS_TMU: No node for tmu in device tree\n"); + return -1; + } + + /* + * Get the pre-defined TMU specific values from FDT. + * All of these are expected to be correct otherwise + * miscalculation of register values in tmu_setup_parameters + * may result in misleading current temperature. + */ + addr = fdtdec_get_addr(blob, node, "reg"); + if (addr == FDT_ADDR_T_NONE) { + debug("%s: Missing tmu-base\n", __func__); + return -1; + } + info->tmu_base = (struct exynos5_tmu_reg *)addr; + + /* Optional field. */ + info->tmu_mux = fdtdec_get_int(blob, + node, "samsung,mux", -1); + /* Take default value as per the user manual b(110) */ + if (info->tmu_mux == -1) + info->tmu_mux = 0x6; + + info->data.ts.min_val = fdtdec_get_int(blob, + node, "samsung,min-temp", -1); + error |= (info->data.ts.min_val == -1); + info->data.ts.max_val = fdtdec_get_int(blob, + node, "samsung,max-temp", -1); + error |= (info->data.ts.max_val == -1); + info->data.ts.start_warning = fdtdec_get_int(blob, + node, "samsung,start-warning", -1); + error |= (info->data.ts.start_warning == -1); + info->data.ts.start_tripping = fdtdec_get_int(blob, + node, "samsung,start-tripping", -1); + error |= (info->data.ts.start_tripping == -1); + info->data.ts.hardware_tripping = fdtdec_get_int(blob, + node, "samsung,hw-tripping", -1); + error |= (info->data.ts.hardware_tripping == -1); + info->data.efuse_min_value = fdtdec_get_int(blob, + node, "samsung,efuse-min-value", -1); + error |= (info->data.efuse_min_value == -1); + info->data.efuse_value = fdtdec_get_int(blob, + node, "samsung,efuse-value", -1); + error |= (info->data.efuse_value == -1); + info->data.efuse_max_value = fdtdec_get_int(blob, + node, "samsung,efuse-max-value", -1); + error |= (info->data.efuse_max_value == -1); + info->data.slope = fdtdec_get_int(blob, + node, "samsung,slope", -1); + error |= (info->data.slope == -1); + info->dc_value = fdtdec_get_int(blob, + node, "samsung,dc-value", -1); + error |= (info->dc_value == -1); + + if (error) { + debug("fail to get tmu node properties\n"); + return -1; + } +#else + /* Non DT support may never be added. Just in case */ + return -1; +#endif + + return 0; +} + +/* + * Calibrate and calculate threshold values and + * enable interrupt levels + * + * @param info pointer to the tmu_info struct + */ +static void tmu_setup_parameters(struct tmu_info *info) +{ + unsigned te_code, con; + unsigned warning_code, trip_code, hwtrip_code; + unsigned cooling_temp; + unsigned rising_value; + struct tmu_data *data = &info->data; + struct exynos5_tmu_reg *reg = info->tmu_base; + + /* Must reload for reading efuse value from triminfo register */ + writel(TRIMINFO_RELOAD, ®->triminfo_control); + + /* Get the compensation parameter */ + te_code = readl(®->triminfo); + info->te1 = te_code & TRIM_INFO_MASK; + info->te2 = ((te_code >> 8) & TRIM_INFO_MASK); + + if ((data->efuse_min_value > info->te1) || + (info->te1 > data->efuse_max_value) + || (info->te2 != 0)) + info->te1 = data->efuse_value; + + /* Get RISING & FALLING Threshold value */ + warning_code = data->ts.start_warning + + info->te1 - info->dc_value; + trip_code = data->ts.start_tripping + + info->te1 - info->dc_value; + hwtrip_code = data->ts.hardware_tripping + + info->te1 - info->dc_value; + + cooling_temp = 0; + + rising_value = ((warning_code << 8) | + (trip_code << 16) | + (hwtrip_code << 24)); + + /* Set interrupt level */ + writel(rising_value, ®->threshold_temp_rise); + writel(cooling_temp, ®->threshold_temp_fall); + + /* + * Init TMU control tuning parameters + * [28:24] VREF - Voltage reference + * [15:13] THERM_TRIP_MODE - Tripping mode + * [12] THERM_TRIP_EN - Thermal tripping enable + * [11:8] BUF_SLOPE_SEL - Gain of amplifier + * [6] THERM_TRIP_BY_TQ_EN - Tripping by TQ pin + */ + writel(data->slope, ®->tmu_control); + + writel(INTCLEARALL, ®->intclear); + + /* TMU core enable */ + con = readl(®->tmu_control); + con |= THERM_TRIP_EN | CORE_EN | (info->tmu_mux << 20); + + writel(con, ®->tmu_control); + + /* Enable HW thermal trip */ + set_hw_thermal_trip(); + + /* LEV1 LEV2 interrupt enable */ + writel(INTEN_RISE1 | INTEN_RISE2, ®->inten); +} + +/* + * Initialize TMU device + * + * @param blob FDT blob + * @return int value, 0 for success + */ +int tmu_init(const void *blob) +{ + gbl_info.tmu_state = TMU_STATUS_INIT; + if (get_tmu_fdt_values(&gbl_info, blob) < 0) + goto ret; + + tmu_setup_parameters(&gbl_info); + gbl_info.tmu_state = TMU_STATUS_NORMAL; +ret: + return gbl_info.tmu_state; +} diff --git a/qemu/roms/u-boot/drivers/power/ftpmu010.c b/qemu/roms/u-boot/drivers/power/ftpmu010.c new file mode 100644 index 000000000..78128c641 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/ftpmu010.c @@ -0,0 +1,88 @@ +/* + * (C) Copyright 2009 Faraday Technology + * Po-Yu Chuang + * + * Copyright (C) 2010 Andes Technology Corporation + * Shawn Lin, Andes Technology Corporation + * Macpaul Lin, Andes Technology Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +/* OSCC: OSC Control Register */ +void ftpmu010_32768osc_enable(void) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int oscc; + + /* enable the 32768Hz oscillator */ + oscc = readl(&pmu->OSCC); + oscc &= ~(FTPMU010_OSCC_OSCL_OFF | FTPMU010_OSCC_OSCL_TRI); + writel(oscc, &pmu->OSCC); + + /* wait until ready */ + while (!(readl(&pmu->OSCC) & FTPMU010_OSCC_OSCL_STABLE)) + ; + + /* select 32768Hz oscillator */ + oscc = readl(&pmu->OSCC); + oscc |= FTPMU010_OSCC_OSCL_RTCLSEL; + writel(oscc, &pmu->OSCC); +} + +/* MFPSR: Multi-Function Port Setting Register */ +void ftpmu010_mfpsr_select_dev(unsigned int dev) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int mfpsr; + + mfpsr = readl(&pmu->MFPSR); + mfpsr |= dev; + writel(mfpsr, &pmu->MFPSR); +} + +void ftpmu010_mfpsr_diselect_dev(unsigned int dev) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int mfpsr; + + mfpsr = readl(&pmu->MFPSR); + mfpsr &= ~dev; + writel(mfpsr, &pmu->MFPSR); +} + +/* PDLLCR0: PLL/DLL Control Register 0 */ +void ftpmu010_dlldis_disable(void) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int pdllcr0; + + pdllcr0 = readl(&pmu->PDLLCR0); + pdllcr0 |= FTPMU010_PDLLCR0_DLLDIS; + writel(pdllcr0, &pmu->PDLLCR0); +} + +void ftpmu010_sdram_clk_disable(unsigned int cr0) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int pdllcr0; + + pdllcr0 = readl(&pmu->PDLLCR0); + pdllcr0 |= FTPMU010_PDLLCR0_HCLKOUTDIS(cr0); + writel(pdllcr0, &pmu->PDLLCR0); +} + +/* SDRAMHTC: SDRAM Signal Hold Time Control */ +void ftpmu010_sdramhtc_set(unsigned int val) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int sdramhtc; + + sdramhtc = readl(&pmu->SDRAMHTC); + sdramhtc |= val; + writel(sdramhtc, &pmu->SDRAMHTC); +} diff --git a/qemu/roms/u-boot/drivers/power/fuel_gauge/Makefile b/qemu/roms/u-boot/drivers/power/fuel_gauge/Makefile new file mode 100644 index 000000000..3b349f939 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/fuel_gauge/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_POWER_FG_MAX17042) += fg_max17042.o diff --git a/qemu/roms/u-boot/drivers/power/fuel_gauge/fg_max17042.c b/qemu/roms/u-boot/drivers/power/fuel_gauge/fg_max17042.c new file mode 100644 index 000000000..154ca6a69 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/fuel_gauge/fg_max17042.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int fg_write_regs(struct pmic *p, u8 addr, u16 *data, int num) +{ + int ret = 0; + int i; + + for (i = 0; i < num; i++, addr++) { + ret = pmic_reg_write(p, addr, *(data + i)); + if (ret) + return ret; + } + + return 0; +} + +static int fg_read_regs(struct pmic *p, u8 addr, u16 *data, int num) +{ + unsigned int dat; + int ret = 0; + int i; + + for (i = 0; i < num; i++, addr++) { + ret = pmic_reg_read(p, addr, &dat); + if (ret) + return ret; + + *(data + i) = (u16)dat; + } + + return 0; +} + +static int fg_write_and_verify(struct pmic *p, u8 addr, u16 data) +{ + unsigned int val = data; + int ret = 0; + + ret |= pmic_reg_write(p, addr, val); + ret |= pmic_reg_read(p, addr, &val); + + if (ret) + return ret; + + if (((u16) val) == data) + return 0; + + return -1; +} + +static void por_fuelgauge_init(struct pmic *p) +{ + u16 r_data0[16], r_data1[16], r_data2[16]; + u32 rewrite_count = 5; + u32 check_count; + u32 lock_count; + u32 i = 0; + u32 val; + s32 ret = 0; + char *status_msg; + + /* Delay 500 ms */ + mdelay(500); + /* Initilize Configuration */ + pmic_reg_write(p, MAX17042_CONFIG, 0x2310); + +rewrite_model: + check_count = 5; + lock_count = 5; + + if (!rewrite_count--) { + status_msg = "init failed!"; + goto error; + } + + /* Unlock Model Access */ + pmic_reg_write(p, MAX17042_MLOCKReg1, MODEL_UNLOCK1); + pmic_reg_write(p, MAX17042_MLOCKReg2, MODEL_UNLOCK2); + + /* Write/Read/Verify the Custom Model */ + ret = fg_write_regs(p, MAX17042_MODEL1, cell_character0, + ARRAY_SIZE(cell_character0)); + if (ret) + goto rewrite_model; + + ret = fg_write_regs(p, MAX17042_MODEL2, cell_character1, + ARRAY_SIZE(cell_character1)); + if (ret) + goto rewrite_model; + + ret = fg_write_regs(p, MAX17042_MODEL3, cell_character2, + ARRAY_SIZE(cell_character2)); + if (ret) + goto rewrite_model; + +check_model: + if (!check_count--) { + if (rewrite_count) + goto rewrite_model; + else + status_msg = "check failed!"; + + goto error; + } + + ret = fg_read_regs(p, MAX17042_MODEL1, r_data0, ARRAY_SIZE(r_data0)); + if (ret) + goto check_model; + + ret = fg_read_regs(p, MAX17042_MODEL2, r_data1, ARRAY_SIZE(r_data1)); + if (ret) + goto check_model; + + ret = fg_read_regs(p, MAX17042_MODEL3, r_data2, ARRAY_SIZE(r_data2)); + if (ret) + goto check_model; + + for (i = 0; i < 16; i++) { + if ((cell_character0[i] != r_data0[i]) + || (cell_character1[i] != r_data1[i]) + || (cell_character2[i] != r_data2[i])) + goto rewrite_model; + } + +lock_model: + if (!lock_count--) { + if (rewrite_count) + goto rewrite_model; + else + status_msg = "lock failed!"; + + goto error; + } + + /* Lock model access */ + pmic_reg_write(p, MAX17042_MLOCKReg1, MODEL_LOCK1); + pmic_reg_write(p, MAX17042_MLOCKReg2, MODEL_LOCK2); + + /* Verify the model access is locked */ + ret = fg_read_regs(p, MAX17042_MODEL1, r_data0, ARRAY_SIZE(r_data0)); + if (ret) + goto lock_model; + + ret = fg_read_regs(p, MAX17042_MODEL2, r_data1, ARRAY_SIZE(r_data1)); + if (ret) + goto lock_model; + + ret = fg_read_regs(p, MAX17042_MODEL3, r_data2, ARRAY_SIZE(r_data2)); + if (ret) + goto lock_model; + + for (i = 0; i < ARRAY_SIZE(r_data0); i++) { + /* Check if model locked */ + if (r_data0[i] || r_data1[i] || r_data2[i]) + goto lock_model; + } + + /* Write Custom Parameters */ + fg_write_and_verify(p, MAX17042_RCOMP0, RCOMP0); + fg_write_and_verify(p, MAX17042_TEMPCO, TempCo); + + /* Delay at least 350mS */ + mdelay(350); + + /* Initialization Complete */ + pmic_reg_read(p, MAX17042_STATUS, &val); + /* Write and Verify Status with POR bit Cleared */ + fg_write_and_verify(p, MAX17042_STATUS, val & ~MAX17042_POR); + + /* Delay at least 350 ms */ + mdelay(350); + + status_msg = "OK!"; +error: + debug("%s: model init status: %s\n", p->name, status_msg); + return; +} + +static int power_update_battery(struct pmic *p, struct pmic *bat) +{ + struct power_battery *pb = bat->pbat; + unsigned int val; + int ret = 0; + + if (pmic_probe(p)) { + puts("Can't find max17042 fuel gauge\n"); + return -1; + } + + ret |= pmic_reg_read(p, MAX17042_VFSOC, &val); + pb->bat->state_of_chrg = (val >> 8); + + pmic_reg_read(p, MAX17042_VCELL, &val); + debug("vfsoc: 0x%x\n", val); + pb->bat->voltage_uV = ((val & 0xFFUL) >> 3) + ((val & 0xFF00) >> 3); + pb->bat->voltage_uV = (pb->bat->voltage_uV * 625); + + pmic_reg_read(p, 0x05, &val); + pb->bat->capacity = val >> 2; + + return ret; +} + +static int power_check_battery(struct pmic *p, struct pmic *bat) +{ + struct power_battery *pb = bat->pbat; + unsigned int val; + int ret = 0; + + if (pmic_probe(p)) { + puts("Can't find max17042 fuel gauge\n"); + return -1; + } + + ret |= pmic_reg_read(p, MAX17042_STATUS, &val); + debug("fg status: 0x%x\n", val); + + if (val & MAX17042_POR) + por_fuelgauge_init(p); + + ret |= pmic_reg_read(p, MAX17042_VERSION, &val); + pb->bat->version = val; + + power_update_battery(p, bat); + debug("fg ver: 0x%x\n", pb->bat->version); + printf("BAT: state_of_charge(SOC):%d%%\n", + pb->bat->state_of_chrg); + + printf(" voltage: %d.%6.6d [V] (expected to be %d [mAh])\n", + pb->bat->voltage_uV / 1000000, + pb->bat->voltage_uV % 1000000, + pb->bat->capacity); + + if (pb->bat->voltage_uV > 3850000) + pb->bat->state = EXT_SOURCE; + else if (pb->bat->voltage_uV < 3600000 || pb->bat->state_of_chrg < 5) + pb->bat->state = CHARGE; + else + pb->bat->state = NORMAL; + + return ret; +} + +static struct power_fg power_fg_ops = { + .fg_battery_check = power_check_battery, + .fg_battery_update = power_update_battery, +}; + +int power_fg_init(unsigned char bus) +{ + static const char name[] = "MAX17042_FG"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board Fuel Gauge init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = FG_NUM_OF_REGS; + p->hw.i2c.addr = MAX17042_I2C_ADDR; + p->hw.i2c.tx_num = 2; + p->sensor_byte_order = PMIC_SENSOR_BYTE_ORDER_BIG; + p->bus = bus; + + p->fg = &power_fg_ops; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/mfd/Makefile b/qemu/roms/u-boot/drivers/power/mfd/Makefile new file mode 100644 index 000000000..43afe842a --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/mfd/Makefile @@ -0,0 +1,10 @@ +# +# Copyright (C) 2013 Samsung Electronics +# Piotr Wilczek +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_POWER_PMIC_MAX77693) += pmic_max77693.o +obj-$(CONFIG_POWER_MUIC_MAX77693) += muic_max77693.o +obj-$(CONFIG_POWER_FG_MAX77693) += fg_max77693.o diff --git a/qemu/roms/u-boot/drivers/power/mfd/fg_max77693.c b/qemu/roms/u-boot/drivers/power/mfd/fg_max77693.c new file mode 100644 index 000000000..4519fed49 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/mfd/fg_max77693.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Piotr Wilczek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int max77693_get_vcell(u32 *vcell) +{ + u16 value; + u8 ret; + + ret = i2c_read(MAX77693_FUEL_I2C_ADDR, MAX77693_VCELL, 1, + (u8 *)&value, 2); + if (ret) + return ret; + + *vcell = (u32)(value >> 3); + *vcell = *vcell * 625; + + return 0; +} + +static int max77693_get_soc(u32 *soc) +{ + u16 value; + u8 ret; + + ret = i2c_read(MAX77693_FUEL_I2C_ADDR, MAX77693_VFSOC, 1, + (u8 *)&value, 2); + if (ret) + return ret; + + *soc = (u32)(value >> 8); + + return 0; +} + +static int power_update_battery(struct pmic *p, struct pmic *bat) +{ + struct power_battery *pb = bat->pbat; + int ret; + + if (pmic_probe(p)) { + puts("Can't find max77693 fuel gauge\n"); + return -1; + } + + ret = max77693_get_soc(&pb->bat->state_of_chrg); + if (ret) + return ret; + + max77693_get_vcell(&pb->bat->voltage_uV); + if (ret) + return ret; + + return 0; +} + +static int power_check_battery(struct pmic *p, struct pmic *bat) +{ + struct power_battery *pb = bat->pbat; + unsigned int val; + int ret = 0; + + if (pmic_probe(p)) { + puts("Can't find max77693 fuel gauge\n"); + return -1; + } + + ret = pmic_reg_read(p, MAX77693_STATUS, &val); + if (ret) + return ret; + debug("fg status: 0x%x\n", val); + + ret = pmic_reg_read(p, MAX77693_VERSION, &pb->bat->version); + if (ret) + return ret; + + ret = power_update_battery(p, bat); + if (ret) + return ret; + debug("fg ver: 0x%x\n", pb->bat->version); + printf("BAT: state_of_charge(SOC):%d%%\n", + pb->bat->state_of_chrg); + + printf(" voltage: %d.%6.6d [V] (expected to be %d [mAh])\n", + pb->bat->voltage_uV / 1000000, + pb->bat->voltage_uV % 1000000, + pb->bat->capacity); + + if (pb->bat->voltage_uV > 3850000) + pb->bat->state = EXT_SOURCE; + else if (pb->bat->voltage_uV < 3600000 || pb->bat->state_of_chrg < 5) + pb->bat->state = CHARGE; + else + pb->bat->state = NORMAL; + + return 0; +} + +static struct power_fg power_fg_ops = { + .fg_battery_check = power_check_battery, + .fg_battery_update = power_update_battery, +}; + +int power_fg_init(unsigned char bus) +{ + static const char name[] = "MAX77693_FG"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board Fuel Gauge init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = FG_NUM_OF_REGS; + p->hw.i2c.addr = MAX77693_FUEL_I2C_ADDR; + p->hw.i2c.tx_num = 2; + p->sensor_byte_order = PMIC_SENSOR_BYTE_ORDER_BIG; + p->bus = bus; + + p->fg = &power_fg_ops; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/mfd/muic_max77693.c b/qemu/roms/u-boot/drivers/power/mfd/muic_max77693.c new file mode 100644 index 000000000..e71012de1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/mfd/muic_max77693.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Piotr Wilczek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +static int power_chrg_get_type(struct pmic *p) +{ + unsigned int val; + unsigned int charge_type, charger; + + /* if probe failed, return cable none */ + if (pmic_probe(p)) + return CHARGER_NO; + + pmic_reg_read(p, MAX77693_MUIC_STATUS2, &val); + + charge_type = val & MAX77693_MUIC_CHG_MASK; + + switch (charge_type) { + case MAX77693_MUIC_CHG_NO: + charger = CHARGER_NO; + break; + case MAX77693_MUIC_CHG_USB: + case MAX77693_MUIC_CHG_USB_D: + charger = CHARGER_USB; + break; + case MAX77693_MUIC_CHG_TA: + case MAX77693_MUIC_CHG_TA_1A: + charger = CHARGER_TA; + break; + case MAX77693_MUIC_CHG_TA_500: + charger = CHARGER_TA_500; + break; + default: + charger = CHARGER_UNKNOWN; + break; + } + + return charger; +} + +static struct power_chrg power_chrg_muic_ops = { + .chrg_type = power_chrg_get_type, +}; + +int power_muic_init(unsigned int bus) +{ + static const char name[] = "MAX77693_MUIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board Micro USB Interface Controller init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = MUIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX77693_MUIC_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + p->chrg = &power_chrg_muic_ops; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/mfd/pmic_max77693.c b/qemu/roms/u-boot/drivers/power/mfd/pmic_max77693.c new file mode 100644 index 000000000..1a4416b54 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/mfd/pmic_max77693.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Piotr Wilczek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static int max77693_charger_state(struct pmic *p, int state, int current) +{ + unsigned int val; + + if (pmic_probe(p)) + return -1; + + /* unlock write capability */ + val = MAX77693_CHG_UNLOCK; + pmic_reg_write(p, MAX77693_CHG_CNFG_06, val); + + if (state == CHARGER_DISABLE) { + puts("Disable the charger.\n"); + pmic_reg_read(p, MAX77693_CHG_CNFG_00, &val); + val &= ~0x01; + pmic_reg_write(p, MAX77693_CHG_CNFG_00, val); + return -1; + } + + if (current < CHARGER_MIN_CURRENT || current > CHARGER_MAX_CURRENT) { + printf("%s: Wrong charge current: %d [mA]\n", + __func__, current); + return -1; + } + + /* set charging current */ + pmic_reg_read(p, MAX77693_CHG_CNFG_02, &val); + val &= ~MAX77693_CHG_CC; + val |= current * 10 / 333; /* 0.1A/3 steps */ + pmic_reg_write(p, MAX77693_CHG_CNFG_02, val); + + /* enable charging */ + val = MAX77693_CHG_MODE_ON; + pmic_reg_write(p, MAX77693_CHG_CNFG_00, val); + + /* check charging current */ + pmic_reg_read(p, MAX77693_CHG_CNFG_02, &val); + val &= 0x3f; + printf("Enable the charger @ %d [mA]\n", val * 333 / 10); + + return 0; +} + +static int max77693_charger_bat_present(struct pmic *p) +{ + unsigned int val; + + if (pmic_probe(p)) + return -1; + + pmic_reg_read(p, MAX77693_CHG_INT_OK, &val); + + return !(val & MAX77693_CHG_DETBAT); +} + +static struct power_chrg power_chrg_pmic_ops = { + .chrg_bat_present = max77693_charger_bat_present, + .chrg_state = max77693_charger_state, +}; + +int pmic_init_max77693(unsigned char bus) +{ + static const char name[] = "MAX77693_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board PMIC init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX77693_PMIC_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + p->chrg = &power_chrg_pmic_ops; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/palmas.c b/qemu/roms/u-boot/drivers/power/palmas.c new file mode 100644 index 000000000..cfbc9dc52 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/palmas.c @@ -0,0 +1,160 @@ +/* + * (C) Copyright 2012-2013 + * Texas Instruments, + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include + +void palmas_init_settings(void) +{ +#ifdef CONFIG_PALMAS_SMPS7_FPWM + int err; + /* + * Set SMPS7 (1.8 V I/O supply on platforms with TWL6035/37) to + * forced PWM mode. This reduces noise (but affects efficiency). + */ + u8 val = SMPS_MODE_SLP_FPWM | SMPS_MODE_ACT_FPWM; + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS7_CTRL, val); + if (err) + printf("palmas: could not force PWM for SMPS7: err = %d\n", + err); +#endif +} + +int palmas_mmc1_poweron_ldo(void) +{ + u8 val = 0; + +#if defined(CONFIG_DRA7XX) + /* + * Currently valid for the dra7xx_evm board: + * Set TPS659038 LDO1 to 3.0 V + */ + val = LDO_VOLT_3V0; + if (palmas_i2c_write_u8(TPS65903X_CHIP_P1, LDO1_VOLTAGE, val)) { + printf("tps65903x: could not set LDO1 voltage.\n"); + return 1; + } + /* TURN ON LDO1 */ + val = RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + if (palmas_i2c_write_u8(TPS65903X_CHIP_P1, LDO1_CTRL, val)) { + printf("tps65903x: could not turn on LDO1.\n"); + return 1; + } + return 0; +#else + /* + * We assume that this is a OMAP543X + TWL603X board: + * Set TWL6035/37 LDO9 to 3.0 V + */ + val = LDO_VOLT_3V0; + return twl603x_mmc1_set_ldo9(val); +#endif +} + +/* + * On some OMAP5 + TWL603X hardware the SD card socket and LDO9_IN are + * powered by an external 3.3 V regulator, while the output of LDO9 + * supplies VDDS_SDCARD for the OMAP5 interface only. This implies that + * LDO9 could be set to 'bypass' mode when required (e.g. for 3.3 V cards). + */ +int twl603x_mmc1_set_ldo9(u8 vsel) +{ + u8 cval = 0, vval = 0; /* Off by default */ + int err; + + if (vsel) { + /* Turn on */ + if (vsel > LDO_VOLT_3V3) { + /* Put LDO9 in bypass */ + cval = LDO9_BYP_EN | RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + vval = LDO_VOLT_3V3; + } else { + cval = RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + vval = vsel & 0x3f; + } + } + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, LDO9_VOLTAGE, vval); + if (err) { + printf("twl603x: could not set LDO9 %s: err = %d\n", + vsel > LDO_VOLT_3V3 ? "bypass" : "voltage", err); + return err; + } + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, LDO9_CTRL, cval); + if (err) + printf("twl603x: could not turn %s LDO9: err = %d\n", + cval ? "on" : "off", err); + return err; +} + +#ifdef CONFIG_PALMAS_AUDPWR +/* + * Turn audio codec power and 32 kHz clock on/off. Use for + * testing OMAP543X + TWL603X + TWL604X boards only. + */ +int twl603x_audio_power(u8 on) +{ + u8 cval = 0, vval = 0, c32k = 0; + int err; + + if (on) { + vval = SMPS_VOLT_2V1; + cval = SMPS_MODE_SLP_AUTO | SMPS_MODE_ACT_AUTO; + c32k = RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + } + /* Set SMPS9 to 2.1 V (for TWL604x), or to 0 (off) */ + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS9_VOLTAGE, vval); + if (err) { + printf("twl603x: could not set SMPS9 voltage: err = %d\n", + err); + return err; + } + /* Turn on or off SMPS9 */ + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS9_CTRL, cval); + if (err) { + printf("twl603x: could not turn SMPS9 %s: err = %d\n", + cval ? "on" : "off", err); + return err; + } + /* Output 32 kHz clock on or off */ + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, CLK32KGAUDIO_CTRL, c32k); + if (err) + printf("twl603x: could not turn CLK32KGAUDIO %s: err = %d\n", + c32k ? "on" : "off", err); + return err; +} +#endif + +#ifdef CONFIG_PALMAS_USB_SS_PWR +/** + * @brief palmas_enable_ss_ldo - Configure EVM board specific configurations + * for the USB Super speed SMPS10 regulator. + * + * @return 0 + */ +int palmas_enable_ss_ldo(void) +{ + /* Enable smps10 regulator */ + return palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS10_CTRL, + SMPS10_MODE_ACTIVE_D); +} +#endif + +/* + * Enable/disable back-up battery (or super cap) charging on TWL6035/37. + * Please use defined BB_xxx values. + */ +int twl603x_enable_bb_charge(u8 bb_fields) +{ + u8 val = bb_fields & 0x0f; + int err; + + val |= (VRTC_EN_SLP | VRTC_EN_OFF | VRTC_PWEN); + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, BB_VRTC_CTRL, val); + if (err) + printf("twl603x: could not set BB_VRTC_CTRL to 0x%02x: err = %d\n", + val, err); + return err; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/Makefile b/qemu/roms/u-boot/drivers/power/pmic/Makefile new file mode 100644 index 000000000..4129bdabf --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/Makefile @@ -0,0 +1,14 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o +obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o +obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o +obj-$(CONFIG_POWER_MAX77686) += pmic_max77686.o +obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o +obj-$(CONFIG_POWER_TPS65217) += pmic_tps65217.o +obj-$(CONFIG_POWER_TPS65910) += pmic_tps65910.o diff --git a/qemu/roms/u-boot/drivers/power/pmic/muic_max8997.c b/qemu/roms/u-boot/drivers/power/pmic/muic_max8997.c new file mode 100644 index 000000000..720342e5e --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/muic_max8997.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +static int power_chrg_get_type(struct pmic *p) +{ + unsigned int val; + unsigned char charge_type, charger; + + if (pmic_probe(p)) + return CHARGER_NO; + + pmic_reg_read(p, MAX8997_MUIC_STATUS2, &val); + charge_type = val & MAX8997_MUIC_CHG_MASK; + + switch (charge_type) { + case MAX8997_MUIC_CHG_NO: + charger = CHARGER_NO; + break; + case MAX8997_MUIC_CHG_USB: + case MAX8997_MUIC_CHG_USB_D: + charger = CHARGER_USB; + break; + case MAX8997_MUIC_CHG_TA: + case MAX8997_MUIC_CHG_TA_1A: + charger = CHARGER_TA; + break; + case MAX8997_MUIC_CHG_TA_500: + charger = CHARGER_TA_500; + break; + default: + charger = CHARGER_UNKNOWN; + break; + } + + return charger; +} + +static struct power_chrg power_chrg_muic_ops = { + .chrg_type = power_chrg_get_type, +}; + +int power_muic_init(unsigned int bus) +{ + static const char name[] = "MAX8997_MUIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board Micro USB Interface Controller init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = MUIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX8997_MUIC_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + p->chrg = &power_chrg_muic_ops; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_max77686.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_max77686.c new file mode 100644 index 000000000..d4c430e22 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_max77686.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Rajeshwari Shinde + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static const char max77686_buck_addr[] = { + 0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38 +}; + +static unsigned int max77686_ldo_volt2hex(int ldo, ulong uV) +{ + unsigned int hex = 0; + + switch (ldo) { + case 1: + case 2: + case 6: + case 7: + case 8: + case 15: + hex = (uV - 800000) / 25000; + break; + default: + hex = (uV - 800000) / 50000; + } + + if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX) + return hex; + + debug("%s: %ld is wrong voltage value for LDO%d\n", __func__, uV, ldo); + return 0; +} + +int max77686_set_ldo_voltage(struct pmic *p, int ldo, ulong uV) +{ + unsigned int val, ret, hex, adr; + + if (ldo < 1 && ldo > 26) { + printf("%s: %d is wrong ldo number\n", __func__, ldo); + return -1; + } + + adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1; + hex = max77686_ldo_volt2hex(ldo, uV); + + if (!hex) + return -1; + + ret = pmic_reg_read(p, adr, &val); + if (ret) + return ret; + + val &= ~MAX77686_LDO_VOLT_MASK; + val |= hex; + ret |= pmic_reg_write(p, adr, val); + + return ret; +} + +int max77686_set_ldo_mode(struct pmic *p, int ldo, char opmode) +{ + unsigned int val, ret, adr, mode; + + if (ldo < 1 && 26 < ldo) { + printf("%s: %d is wrong ldo number\n", __func__, ldo); + return -1; + } + + adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1; + + /* mode */ + switch (opmode) { + case OPMODE_OFF: + mode = MAX77686_LDO_MODE_OFF; + break; + case OPMODE_STANDBY: + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + mode = MAX77686_LDO_MODE_STANDBY; + break; + default: + mode = 0xff; + } + break; + case OPMODE_LPM: + mode = MAX77686_LDO_MODE_LPM; + break; + case OPMODE_ON: + mode = MAX77686_LDO_MODE_ON; + break; + default: + mode = 0xff; + } + + if (mode == 0xff) { + printf("%s: %d is not supported on LDO%d\n", + __func__, opmode, ldo); + return -1; + } + + ret = pmic_reg_read(p, adr, &val); + if (ret) + return ret; + + val &= ~MAX77686_LDO_MODE_MASK; + val |= mode; + ret |= pmic_reg_write(p, adr, val); + + return ret; +} + +int max77686_set_buck_mode(struct pmic *p, int buck, char opmode) +{ + unsigned int val, ret, mask, adr, size, mode, mode_shift; + + size = ARRAY_SIZE(max77686_buck_addr); + if (buck >= size) { + printf("%s: %d is wrong buck number\n", __func__, buck); + return -1; + } + + adr = max77686_buck_addr[buck]; + + /* mask */ + switch (buck) { + case 2: + case 3: + case 4: + mode_shift = MAX77686_BUCK_MODE_SHIFT_2; + break; + default: + mode_shift = MAX77686_BUCK_MODE_SHIFT_1; + } + + mask = MAX77686_BUCK_MODE_MASK << mode_shift; + + /* mode */ + switch (opmode) { + case OPMODE_OFF: + mode = MAX77686_BUCK_MODE_OFF; + break; + case OPMODE_STANDBY: + switch (buck) { + case 1: + case 2: + case 3: + case 4: + mode = MAX77686_BUCK_MODE_STANDBY << mode_shift; + break; + default: + mode = 0xff; + } + break; + case OPMODE_LPM: + switch (buck) { + case 2: + case 3: + case 4: + mode = MAX77686_BUCK_MODE_LPM << mode_shift; + break; + default: + mode = 0xff; + } + break; + case OPMODE_ON: + mode = MAX77686_BUCK_MODE_ON << mode_shift; + break; + default: + mode = 0xff; + } + + if (mode == 0xff) { + printf("%s: %d is not supported on BUCK%d\n", + __func__, opmode, buck); + return -1; + } + + ret = pmic_reg_read(p, adr, &val); + if (ret) + return ret; + + val &= ~mask; + val |= mode; + ret |= pmic_reg_write(p, adr, val); + + return ret; +} + +int pmic_init(unsigned char bus) +{ + static const char name[] = "MAX77686_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + +#ifdef CONFIG_OF_CONTROL + const void *blob = gd->fdt_blob; + int node, parent; + + node = fdtdec_next_compatible(blob, 0, COMPAT_MAXIM_MAX77686_PMIC); + if (node < 0) { + debug("PMIC: No node for PMIC Chip in device tree\n"); + debug("node = %d\n", node); + return -1; + } + + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + p->bus = i2c_get_bus_num_fdt(parent); + if (p->bus < 0) { + debug("%s: Cannot find I2C bus\n", __func__); + return -1; + } + p->hw.i2c.addr = fdtdec_get_int(blob, node, "reg", 9); +#else + p->bus = bus; + p->hw.i2c.addr = MAX77686_I2C_ADDR; +#endif + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.tx_num = 1; + + puts("Board PMIC init\n"); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_max8997.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_max8997.c new file mode 100644 index 000000000..ba0169232 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_max8997.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +unsigned char max8997_reg_ldo(int uV) +{ + unsigned char ret; + if (uV <= 800000) + return 0; + if (uV >= 3950000) + return MAX8997_LDO_MAX_VAL; + ret = (uV - 800000) / 50000; + if (ret > MAX8997_LDO_MAX_VAL) { + printf("MAX8997 LDO SETTING ERROR (%duV) -> %u\n", uV, ret); + ret = MAX8997_LDO_MAX_VAL; + } + + return ret; +} + +static int pmic_charger_state(struct pmic *p, int state, int current) +{ + unsigned char fc; + u32 val = 0; + + if (pmic_probe(p)) + return -1; + + if (state == CHARGER_DISABLE) { + puts("Disable the charger.\n"); + pmic_reg_read(p, MAX8997_REG_MBCCTRL2, &val); + val &= ~(MBCHOSTEN | VCHGR_FC); + pmic_reg_write(p, MAX8997_REG_MBCCTRL2, val); + + return -1; + } + + if (current < CHARGER_MIN_CURRENT || current > CHARGER_MAX_CURRENT) { + printf("%s: Wrong charge current: %d [mA]\n", + __func__, current); + return -1; + } + + fc = (current - CHARGER_MIN_CURRENT) / CHARGER_CURRENT_RESOLUTION; + fc = fc & 0xf; /* up to 950 mA */ + + printf("Enable the charger @ %d [mA]\n", fc * CHARGER_CURRENT_RESOLUTION + + CHARGER_MIN_CURRENT); + + val = fc | MBCICHFCSET; + pmic_reg_write(p, MAX8997_REG_MBCCTRL4, val); + + pmic_reg_read(p, MAX8997_REG_MBCCTRL2, &val); + val = MBCHOSTEN | VCHGR_FC; /* enable charger & fast charge */ + pmic_reg_write(p, MAX8997_REG_MBCCTRL2, val); + + return 0; +} + +static int pmic_charger_bat_present(struct pmic *p) +{ + u32 val; + + if (pmic_probe(p)) + return -1; + + pmic_reg_read(p, MAX8997_REG_STATUS4, &val); + + return !(val & DETBAT); +} + +static struct power_chrg power_chrg_pmic_ops = { + .chrg_bat_present = pmic_charger_bat_present, + .chrg_state = pmic_charger_state, +}; + +int pmic_init(unsigned char bus) +{ + static const char name[] = "MAX8997_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board PMIC init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX8997_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + p->chrg = &power_chrg_pmic_ops; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_max8998.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_max8998.c new file mode 100644 index 000000000..ca2f504cc --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_max8998.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +int pmic_init(unsigned char bus) +{ + static const char name[] = "MAX8998_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + puts("Board PMIC init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX8998_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_pfuze100.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_pfuze100.c new file mode 100644 index 000000000..22c1f15ee --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_pfuze100.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Gateworks Corporation + * Tim Harvey + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +int pmic_init(unsigned char bus) +{ + static const char name[] = "PFUZE100_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = CONFIG_POWER_PFUZE100_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65217.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65217.c new file mode 100644 index 000000000..36e9024bf --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65217.c @@ -0,0 +1,109 @@ +/* + * (C) Copyright 2011-2013 + * Texas Instruments, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +/** + * tps65217_reg_read() - Generic function that can read a TPS65217 register + * @src_reg: Source register address + * @src_val: Address of destination variable + * @return: 0 for success, not 0 on failure. + */ +int tps65217_reg_read(uchar src_reg, uchar *src_val) +{ + return i2c_read(TPS65217_CHIP_PM, src_reg, 1, src_val, 1); +} + +/** + * tps65217_reg_write() - Generic function that can write a TPS65217 PMIC + * register or bit field regardless of protection + * level. + * + * @prot_level: Register password protection. Use + * TPS65217_PROT_LEVEL_NONE, + * TPS65217_PROT_LEVEL_1 or TPS65217_PROT_LEVEL_2 + * @dest_reg: Register address to write. + * @dest_val: Value to write. + * @mask: Bit mask (8 bits) to be applied. Function will only + * change bits that are set in the bit mask. + * + * @return: 0 for success, not 0 on failure, as per the i2c API + */ +int tps65217_reg_write(uchar prot_level, uchar dest_reg, uchar dest_val, + uchar mask) +{ + uchar read_val; + uchar xor_reg; + int ret; + + /* + * If we are affecting only a bit field, read dest_reg and apply the + * mask + */ + if (mask != TPS65217_MASK_ALL_BITS) { + ret = i2c_read(TPS65217_CHIP_PM, dest_reg, 1, &read_val, 1); + if (ret) + return ret; + read_val &= (~mask); + read_val |= (dest_val & mask); + dest_val = read_val; + } + + if (prot_level > 0) { + xor_reg = dest_reg ^ TPS65217_PASSWORD_UNLOCK; + ret = i2c_write(TPS65217_CHIP_PM, TPS65217_PASSWORD, 1, + &xor_reg, 1); + if (ret) + return ret; + } + + ret = i2c_write(TPS65217_CHIP_PM, dest_reg, 1, &dest_val, 1); + if (ret) + return ret; + + if (prot_level == TPS65217_PROT_LEVEL_2) { + ret = i2c_write(TPS65217_CHIP_PM, TPS65217_PASSWORD, 1, + &xor_reg, 1); + if (ret) + return ret; + + ret = i2c_write(TPS65217_CHIP_PM, dest_reg, 1, &dest_val, 1); + if (ret) + return ret; + } + + return 0; +} + +/** + * tps65217_voltage_update() - Function to change a voltage level, as this + * is a multi-step process. + * @dc_cntrl_reg: DC voltage control register to change. + * @volt_sel: New value for the voltage register + * @return: 0 for success, not 0 on failure. + */ +int tps65217_voltage_update(uchar dc_cntrl_reg, uchar volt_sel) +{ + if ((dc_cntrl_reg != TPS65217_DEFDCDC1) && + (dc_cntrl_reg != TPS65217_DEFDCDC2) && + (dc_cntrl_reg != TPS65217_DEFDCDC3)) + return 1; + + /* set voltage level */ + if (tps65217_reg_write(TPS65217_PROT_LEVEL_2, dc_cntrl_reg, volt_sel, + TPS65217_MASK_ALL_BITS)) + return 1; + + /* set GO bit to initiate voltage transition */ + if (tps65217_reg_write(TPS65217_PROT_LEVEL_2, TPS65217_DEFSLEW, + TPS65217_DCDC_GO, TPS65217_DCDC_GO)) + return 1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65910.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65910.c new file mode 100644 index 000000000..7ee1160e0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65910.c @@ -0,0 +1,83 @@ +/* + * (C) Copyright 2011-2013 + * Texas Instruments, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +/* + * tps65910_set_i2c_control() - Set the TPS65910 to be controlled via the I2C + * interface. + * @return: 0 on success, not 0 on failure + */ +int tps65910_set_i2c_control(void) +{ + int ret; + uchar buf; + + /* VDD1/2 voltage selection register access by control i/f */ + ret = i2c_read(TPS65910_CTRL_I2C_ADDR, TPS65910_DEVCTRL_REG, 1, + &buf, 1); + + if (ret) + return ret; + + buf |= TPS65910_DEVCTRL_REG_SR_CTL_I2C_SEL_CTL_I2C; + + return i2c_write(TPS65910_CTRL_I2C_ADDR, TPS65910_DEVCTRL_REG, 1, + &buf, 1); +} + +/* + * tps65910_voltage_update() - Voltage switching for MPU frequency switching. + * @module: mpu - 0, core - 1 + * @vddx_op_vol_sel: vdd voltage to set + * @return: 0 on success, not 0 on failure + */ +int tps65910_voltage_update(unsigned int module, unsigned char vddx_op_vol_sel) +{ + uchar buf; + unsigned int reg_offset; + int ret; + + if (module == MPU) + reg_offset = TPS65910_VDD1_OP_REG; + else + reg_offset = TPS65910_VDD2_OP_REG; + + /* Select VDDx OP */ + ret = i2c_read(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + buf &= ~TPS65910_OP_REG_CMD_MASK; + + ret = i2c_write(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + /* Configure VDDx OP Voltage */ + ret = i2c_read(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + buf &= ~TPS65910_OP_REG_SEL_MASK; + buf |= vddx_op_vol_sel; + + ret = i2c_write(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + ret = i2c_read(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + if ((buf & TPS65910_OP_REG_SEL_MASK) != vddx_op_vol_sel) + return 1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/power_core.c b/qemu/roms/u-boot/drivers/power/power_core.c new file mode 100644 index 000000000..fe1f31602 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_core.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * (C) Copyright 2008-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static LIST_HEAD(pmic_list); + +int check_reg(struct pmic *p, u32 reg) +{ + if (reg >= p->number_of_regs) { + printf(" = %d is invalid. Should be less than %d\n", + reg, p->number_of_regs); + return -1; + } + + return 0; +} + +int pmic_set_output(struct pmic *p, u32 reg, int out, int on) +{ + u32 val; + + if (pmic_reg_read(p, reg, &val)) + return -1; + + if (on) + val |= out; + else + val &= ~out; + + if (pmic_reg_write(p, reg, val)) + return -1; + + return 0; +} + +static void pmic_show_info(struct pmic *p) +{ + printf("PMIC: %s\n", p->name); +} + +static int pmic_dump(struct pmic *p) +{ + int i, ret; + u32 val; + + if (!p) { + puts("Wrong PMIC name!\n"); + return -1; + } + + pmic_show_info(p); + for (i = 0; i < p->number_of_regs; i++) { + ret = pmic_reg_read(p, i, &val); + if (ret) + puts("PMIC: Registers dump failed\n"); + + if (!(i % 8)) + printf("\n0x%02x: ", i); + + printf("%08x ", val); + } + puts("\n"); + return 0; +} + +struct pmic *pmic_alloc(void) +{ + struct pmic *p; + + p = calloc(sizeof(*p), 1); + if (!p) { + printf("%s: No available memory for allocation!\n", __func__); + return NULL; + } + + list_add_tail(&p->list, &pmic_list); + + debug("%s: new pmic struct: 0x%p\n", __func__, p); + + return p; +} + +struct pmic *pmic_get(const char *s) +{ + struct pmic *p; + + list_for_each_entry(p, &pmic_list, list) { + if (strcmp(p->name, s) == 0) { + debug("%s: pmic %s -> 0x%p\n", __func__, p->name, p); + return p; + } + } + + return NULL; +} + +const char *power_get_interface(int interface) +{ + const char *power_interface[] = {"I2C", "SPI", "|+|-|"}; + return power_interface[interface]; +} + +static void pmic_list_names(void) +{ + struct pmic *p; + + puts("PMIC devices:\n"); + list_for_each_entry(p, &pmic_list, list) { + printf("name: %s bus: %s_%d\n", p->name, + power_get_interface(p->interface), p->bus); + } +} + +int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + u32 ret, reg, val; + char *cmd, *name; + struct pmic *p; + + /* at least two arguments please */ + if (argc < 2) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "list") == 0) { + pmic_list_names(); + return CMD_RET_SUCCESS; + } + + if (argc < 3) + return CMD_RET_USAGE; + + name = argv[1]; + cmd = argv[2]; + + debug("%s: name: %s cmd: %s\n", __func__, name, cmd); + p = pmic_get(name); + if (!p) + return CMD_RET_FAILURE; + + if (strcmp(cmd, "dump") == 0) { + if (pmic_dump(p)) + return CMD_RET_FAILURE; + return CMD_RET_SUCCESS; + } + + if (strcmp(cmd, "read") == 0) { + if (argc < 4) + return CMD_RET_USAGE; + + reg = simple_strtoul(argv[3], NULL, 16); + ret = pmic_reg_read(p, reg, &val); + + if (ret) + puts("PMIC: Register read failed\n"); + + printf("\n0x%02x: 0x%08x\n", reg, val); + + return CMD_RET_SUCCESS; + } + + if (strcmp(cmd, "write") == 0) { + if (argc < 5) + return CMD_RET_USAGE; + + reg = simple_strtoul(argv[3], NULL, 16); + val = simple_strtoul(argv[4], NULL, 16); + pmic_reg_write(p, reg, val); + + return CMD_RET_SUCCESS; + } + + if (strcmp(cmd, "bat") == 0) { + if (argc < 4) + return CMD_RET_USAGE; + + if (!p->pbat) { + printf("%s is not a battery\n", p->name); + return CMD_RET_FAILURE; + } + + if (strcmp(argv[3], "state") == 0) + p->fg->fg_battery_check(p->pbat->fg, p); + + if (strcmp(argv[3], "charge") == 0) { + printf("BAT: %s charging (ctrl+c to break)\n", + p->name); + if (p->low_power_mode) + p->low_power_mode(); + if (p->pbat->battery_charge) + p->pbat->battery_charge(p); + } + + return CMD_RET_SUCCESS; + } + + /* No subcommand found */ + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + pmic, CONFIG_SYS_MAXARGS, 1, do_pmic, + "PMIC", + "list - list available PMICs\n" + "pmic name dump - dump named PMIC registers\n" + "pmic name read - read register\n" + "pmic name write - write register\n" + "pmic name bat state - write register\n" + "pmic name bat charge - write register\n" +); diff --git a/qemu/roms/u-boot/drivers/power/power_dialog.c b/qemu/roms/u-boot/drivers/power/power_dialog.c new file mode 100644 index 000000000..fc95d5a10 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_dialog.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +int pmic_dialog_init(unsigned char bus) +{ + static const char name[] = "DIALOG_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->number_of_regs = DIALOG_NUM_OF_REGS; + + p->interface = PMIC_I2C; + p->hw.i2c.addr = CONFIG_SYS_DIALOG_PMIC_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/power_fsl.c b/qemu/roms/u-boot/drivers/power/power_fsl.c new file mode 100644 index 000000000..ac0b541d7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_fsl.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_PMIC_FSL_MC13892) +#define FSL_PMIC_I2C_LENGTH 3 +#elif defined(CONFIG_PMIC_FSL_MC34704) +#define FSL_PMIC_I2C_LENGTH 1 +#endif + +#if defined(CONFIG_POWER_SPI) +static u32 pmic_spi_prepare_tx(u32 reg, u32 *val, u32 write) +{ + return (write << 31) | (reg << 25) | (*val & 0x00FFFFFF); +} +#endif + +int pmic_init(unsigned char bus) +{ + static const char name[] = "FSL_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->bus = bus; + +#if defined(CONFIG_POWER_SPI) + p->interface = PMIC_SPI; + p->hw.spi.cs = CONFIG_FSL_PMIC_CS; + p->hw.spi.clk = CONFIG_FSL_PMIC_CLK; + p->hw.spi.mode = CONFIG_FSL_PMIC_MODE; + p->hw.spi.bitlen = CONFIG_FSL_PMIC_BITLEN; + p->hw.spi.flags = SPI_XFER_BEGIN | SPI_XFER_END; + p->hw.spi.prepare_tx = pmic_spi_prepare_tx; +#elif defined(CONFIG_POWER_I2C) + p->interface = PMIC_I2C; + p->hw.i2c.addr = CONFIG_SYS_FSL_PMIC_I2C_ADDR; + p->hw.i2c.tx_num = FSL_PMIC_I2C_LENGTH; +#else +#error "You must select CONFIG_POWER_SPI or CONFIG_PMIC_I2C" +#endif + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/power_i2c.c b/qemu/roms/u-boot/drivers/power/power_i2c.c new file mode 100644 index 000000000..ac768708e --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_i2c.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * (C) Copyright 2008-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +int pmic_reg_write(struct pmic *p, u32 reg, u32 val) +{ + unsigned char buf[4] = { 0 }; + + if (check_reg(p, reg)) + return -1; + + switch (pmic_i2c_tx_num) { + case 3: + if (p->sensor_byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) { + buf[2] = (cpu_to_le32(val) >> 16) & 0xff; + buf[1] = (cpu_to_le32(val) >> 8) & 0xff; + buf[0] = cpu_to_le32(val) & 0xff; + } else { + buf[0] = (cpu_to_le32(val) >> 16) & 0xff; + buf[1] = (cpu_to_le32(val) >> 8) & 0xff; + buf[2] = cpu_to_le32(val) & 0xff; + } + break; + case 2: + if (p->sensor_byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) { + buf[1] = (cpu_to_le32(val) >> 8) & 0xff; + buf[0] = cpu_to_le32(val) & 0xff; + } else { + buf[0] = (cpu_to_le32(val) >> 8) & 0xff; + buf[1] = cpu_to_le32(val) & 0xff; + } + break; + case 1: + buf[0] = cpu_to_le32(val) & 0xff; + break; + default: + printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num); + return -1; + } + + if (i2c_write(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num)) + return -1; + + return 0; +} + +int pmic_reg_read(struct pmic *p, u32 reg, u32 *val) +{ + unsigned char buf[4] = { 0 }; + u32 ret_val = 0; + + if (check_reg(p, reg)) + return -1; + + if (i2c_read(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num)) + return -1; + + switch (pmic_i2c_tx_num) { + case 3: + if (p->sensor_byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) + ret_val = le32_to_cpu(buf[2] << 16 + | buf[1] << 8 | buf[0]); + else + ret_val = le32_to_cpu(buf[0] << 16 | + buf[1] << 8 | buf[2]); + break; + case 2: + if (p->sensor_byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) + ret_val = le32_to_cpu(buf[1] << 8 | buf[0]); + else + ret_val = le32_to_cpu(buf[0] << 8 | buf[1]); + break; + case 1: + ret_val = le32_to_cpu(buf[0]); + break; + default: + printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num); + return -1; + } + memcpy(val, &ret_val, sizeof(ret_val)); + + return 0; +} + +int pmic_probe(struct pmic *p) +{ + i2c_set_bus_num(p->bus); + debug("Bus: %d PMIC:%s probed!\n", p->bus, p->name); + if (i2c_probe(pmic_i2c_addr)) { + printf("Can't find PMIC:%s\n", p->name); + return -1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/power_spi.c b/qemu/roms/u-boot/drivers/power/power_spi.c new file mode 100644 index 000000000..fb455a006 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_spi.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * (C) Copyright 2008-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static struct spi_slave *slave; + +void pmic_spi_free(struct spi_slave *slave) +{ + if (slave) + spi_free_slave(slave); +} + +struct spi_slave *pmic_spi_probe(struct pmic *p) +{ + return spi_setup_slave(p->bus, + p->hw.spi.cs, + p->hw.spi.clk, + p->hw.spi.mode); +} + +static u32 pmic_reg(struct pmic *p, u32 reg, u32 *val, u32 write) +{ + u32 pmic_tx, pmic_rx; + u32 tmp; + + if (!slave) { + slave = pmic_spi_probe(p); + + if (!slave) + return -1; + } + + if (check_reg(p, reg)) + return -1; + + if (spi_claim_bus(slave)) + return -1; + + pmic_tx = p->hw.spi.prepare_tx(reg, val, write); + + tmp = cpu_to_be32(pmic_tx); + + if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx, + pmic_spi_flags)) { + spi_release_bus(slave); + return -1; + } + + if (write) { + pmic_tx = p->hw.spi.prepare_tx(reg, val, 0); + tmp = cpu_to_be32(pmic_tx); + if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx, + pmic_spi_flags)) { + spi_release_bus(slave); + return -1; + } + } + + spi_release_bus(slave); + *val = cpu_to_be32(pmic_rx); + + return 0; +} + +int pmic_reg_write(struct pmic *p, u32 reg, u32 val) +{ + if (pmic_reg(p, reg, &val, 1)) + return -1; + + return 0; +} + +int pmic_reg_read(struct pmic *p, u32 reg, u32 *val) +{ + if (pmic_reg(p, reg, val, 0)) + return -1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/tps6586x.c b/qemu/roms/u-boot/drivers/power/tps6586x.c new file mode 100644 index 000000000..d29d96953 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/tps6586x.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010,2011 NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static int bus_num; /* I2C bus we are on */ +#define I2C_ADDRESS 0x34 /* chip requires this address */ +static char inited; /* 1 if we have been inited */ + +enum { + /* Registers that we access */ + SUPPLY_CONTROL1 = 0x20, + SUPPLY_CONTROL2, + SM1_VOLTAGE_V1 = 0x23, + SM1_VOLTAGE_V2, + SM0_VOLTAGE_V1 = 0x26, + SM0_VOLTAGE_V2, + PFM_MODE = 0x47, + + /* Bits in the supply control registers */ + CTRL_SM1_RAMP = 0x01, + CTRL_SM1_SUPPLY2 = 0x02, + CTRL_SM0_RAMP = 0x04, + CTRL_SM0_SUPPLY2 = 0x08, +}; + +#define MAX_I2C_RETRY 3 +static int tps6586x_read(int reg) +{ + int i; + uchar data; + int retval = -1; + int old_bus_num; + + old_bus_num = i2c_get_bus_num(); + i2c_set_bus_num(bus_num); + + for (i = 0; i < MAX_I2C_RETRY; ++i) { + if (!i2c_read(I2C_ADDRESS, reg, 1, &data, 1)) { + retval = (int)data; + goto exit; + } + + /* i2c access failed, retry */ + udelay(100); + } + +exit: + i2c_set_bus_num(old_bus_num); + debug("pmu_read %x=%x\n", reg, retval); + if (retval < 0) + debug("%s: failed to read register %#x: %d\n", __func__, reg, + retval); + return retval; +} + +static int tps6586x_write(int reg, uchar *data, uint len) +{ + int i; + int retval = -1; + int old_bus_num; + + old_bus_num = i2c_get_bus_num(); + i2c_set_bus_num(bus_num); + + for (i = 0; i < MAX_I2C_RETRY; ++i) { + if (!i2c_write(I2C_ADDRESS, reg, 1, data, len)) { + retval = 0; + goto exit; + } + + /* i2c access failed, retry */ + udelay(100); + } + +exit: + i2c_set_bus_num(old_bus_num); + debug("pmu_write %x=%x: ", reg, retval); + for (i = 0; i < len; i++) + debug("%x ", data[i]); + if (retval) + debug("%s: failed to write register %#x\n", __func__, reg); + return retval; +} + +/* + * Get current voltage of SM0 and SM1 + * + * @param sm0 Place to put SM0 voltage + * @param sm1 Place to put SM1 voltage + * @return 0 if ok, -1 on error + */ +static int read_voltages(int *sm0, int *sm1) +{ + int ctrl1, ctrl2; + int is_v2; + + /* + * Each vdd has two supply sources, ie, v1 and v2. + * The supply control reg1 and reg2 determine the current selection. + */ + ctrl1 = tps6586x_read(SUPPLY_CONTROL1); + ctrl2 = tps6586x_read(SUPPLY_CONTROL2); + if (ctrl1 == -1 || ctrl2 == -1) + return -1; + + /* Figure out whether V1 or V2 is selected */ + is_v2 = (ctrl1 | ctrl2) & CTRL_SM0_SUPPLY2; + *sm0 = tps6586x_read(is_v2 ? SM0_VOLTAGE_V2 : SM0_VOLTAGE_V1); + *sm1 = tps6586x_read(is_v2 ? SM1_VOLTAGE_V2 : SM1_VOLTAGE_V1); + if (*sm0 == -1 || *sm1 == -1) + return -1; + + return 0; +} + +static int set_voltage(int reg, int data, int rate) +{ + uchar control_bit; + uchar buff[3]; + + control_bit = (reg == SM0_VOLTAGE_V1 ? CTRL_SM0_RAMP : CTRL_SM1_RAMP); + + /* + * Only one supply is needed in u-boot. set both v1 and v2 to + * same value. + * + * When both v1 and v2 are set to same value, we just need to set + * control1 reg to trigger the supply selection. + */ + buff[0] = buff[1] = (uchar)data; + buff[2] = rate; + + /* write v1, v2 and rate, then trigger */ + if (tps6586x_write(reg, buff, 3) || + tps6586x_write(SUPPLY_CONTROL1, &control_bit, 1)) + return -1; + + return 0; +} + +static int calculate_next_voltage(int voltage, int target, int step) +{ + int diff = voltage < target ? step : -step; + + if (abs(target - voltage) > step) + voltage += diff; + else + voltage = target; + + return voltage; +} + +int tps6586x_set_pwm_mode(int mask) +{ + uchar val; + int ret; + + assert(inited); + ret = tps6586x_read(PFM_MODE); + if (ret != -1) { + val = (uchar)ret; + val |= mask; + + ret = tps6586x_write(PFM_MODE, &val, 1); + } + + if (ret == -1) + debug("%s: Failed to read/write PWM mode reg\n", __func__); + + return ret; +} + +int tps6586x_adjust_sm0_sm1(int sm0_target, int sm1_target, int step, int rate, + int min_sm0_over_sm1) +{ + int sm0, sm1; + int bad; + + assert(inited); + + /* get current voltage settings */ + if (read_voltages(&sm0, &sm1)) { + debug("%s: Cannot read voltage settings\n", __func__); + return -1; + } + + /* + * if vdd_core < vdd_cpu + rel + * skip + * + * This condition may happen when system reboots due to kernel crash. + */ + if (min_sm0_over_sm1 != -1 && sm0 < sm1 + min_sm0_over_sm1) { + debug("%s: SM0 is %d, SM1 is %d, but min_sm0_over_sm1 is %d\n", + __func__, sm0, sm1, min_sm0_over_sm1); + return -1; + } + + /* + * Since vdd_core and vdd_cpu may both stand at either greater or less + * than their nominal voltage, the adjustment may go either directions. + * + * Make sure vdd_core is always higher than vdd_cpu with certain margin. + * So, find out which vdd to adjust first in each step. + * + * case 1: both sm0 and sm1 need to move up + * adjust sm0 before sm1 + * + * case 2: both sm0 and sm1 need to move down + * adjust sm1 before sm0 + * + * case 3: sm0 moves down and sm1 moves up + * adjusting either one first is fine. + * + * Adjust vdd_core and vdd_cpu one step at a time until they reach + * their nominal values. + */ + bad = 0; + while (!bad && (sm0 != sm0_target || sm1 != sm1_target)) { + int adjust_sm0_late = 0; /* flag to adjust vdd_core later */ + + debug("%d-%d %d-%d ", sm0, sm0_target, sm1, sm1_target); + + if (sm0 != sm0_target) { + /* + * if case 1 and case 3, set new sm0 first. + * otherwise, hold down until new sm1 is set. + */ + sm0 = calculate_next_voltage(sm0, sm0_target, step); + if (sm1 < sm1_target) + bad |= set_voltage(SM0_VOLTAGE_V1, sm0, rate); + else + adjust_sm0_late = 1; + } + + if (sm1 != sm1_target) { + sm1 = calculate_next_voltage(sm1, sm1_target, step); + bad |= set_voltage(SM1_VOLTAGE_V1, sm1, rate); + } + + if (adjust_sm0_late) + bad |= set_voltage(SM0_VOLTAGE_V1, sm0, rate); + debug("%d\n", adjust_sm0_late); + } + debug("%d-%d %d-%d done\n", sm0, sm0_target, sm1, sm1_target); + + return bad ? -1 : 0; +} + +int tps6586x_init(int bus) +{ + bus_num = bus; + inited = 1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/twl4030.c b/qemu/roms/u-boot/drivers/power/twl4030.c new file mode 100644 index 000000000..3e5031046 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/twl4030.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix + * + * SPDX-License-Identifier: GPL-2.0+ + * + * twl4030_power_reset_init is derived from code on omapzoom, + * git://git.omapzoom.com/repo/u-boot.git + * + * Copyright (C) 2007-2009 Texas Instruments, Inc. + * + * twl4030_power_init is from cpu/omap3/common.c, power_init_r + * + * (C) Copyright 2004-2008 + * Texas Instruments, + * + * Author : + * Sunil Kumar + * Shashi Ranjan + * + * Derived from Beagle Board and 3430 SDP code by + * Richard Woodruff + * Syed Mohammed Khasim + */ + +#include + +/* + * Power Reset + */ +void twl4030_power_reset_init(void) +{ + u8 val = 0; + if (twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_P1_SW_EVENTS, &val)) { + printf("Error:TWL4030: failed to read the power register\n"); + printf("Could not initialize hardware reset\n"); + } else { + val |= TWL4030_PM_MASTER_SW_EVENTS_STOPON_PWRON; + if (twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_P1_SW_EVENTS, val)) { + printf("Error:TWL4030: failed to write the power register\n"); + printf("Could not initialize hardware reset\n"); + } + } +} + +/* + * Set Device Group and Voltage + */ +void twl4030_pmrecv_vsel_cfg(u8 vsel_reg, u8 vsel_val, + u8 dev_grp, u8 dev_grp_sel) +{ + int ret; + + /* Select the Voltage */ + ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, vsel_reg, + vsel_val); + if (ret != 0) { + printf("Could not write vsel to reg %02x (%d)\n", + vsel_reg, ret); + return; + } + + /* Select the Device Group (enable the supply if dev_grp_sel != 0) */ + ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, dev_grp, + dev_grp_sel); + if (ret != 0) + printf("Could not write grp_sel to reg %02x (%d)\n", + dev_grp, ret); +} + +void twl4030_power_init(void) +{ + /* set VAUX3 to 2.8V */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VAUX3_DEDICATED, + TWL4030_PM_RECEIVER_VAUX3_VSEL_28, + TWL4030_PM_RECEIVER_VAUX3_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_P1); + + /* set VPLL2 to 1.8V */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VPLL2_DEDICATED, + TWL4030_PM_RECEIVER_VPLL2_VSEL_18, + TWL4030_PM_RECEIVER_VPLL2_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_ALL); + + /* set VDAC to 1.8V */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VDAC_DEDICATED, + TWL4030_PM_RECEIVER_VDAC_VSEL_18, + TWL4030_PM_RECEIVER_VDAC_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_P1); +} + +void twl4030_power_mmc_init(void) +{ + /* Set VMMC1 to 3.15 Volts */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VMMC1_DEDICATED, + TWL4030_PM_RECEIVER_VMMC1_VSEL_32, + TWL4030_PM_RECEIVER_VMMC1_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_P1); +} diff --git a/qemu/roms/u-boot/drivers/power/twl6030.c b/qemu/roms/u-boot/drivers/power/twl6030.c new file mode 100644 index 000000000..a1c6663a2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/twl6030.c @@ -0,0 +1,235 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#ifdef CONFIG_TWL6030_POWER + +#include + +static struct twl6030_data *twl; + +static struct twl6030_data twl6030_info = { + .chip_type = chip_TWL6030, + .adc_rbase = GPCH0_LSB, + .adc_ctrl = CTRL_P2, + .adc_enable = CTRL_P2_SP2, + .vbat_mult = TWL6030_VBAT_MULT, + .vbat_shift = TWL6030_VBAT_SHIFT, +}; + +static struct twl6030_data twl6032_info = { + .chip_type = chip_TWL6032, + .adc_rbase = TWL6032_GPCH0_LSB, + .adc_ctrl = TWL6032_CTRL_P1, + .adc_enable = CTRL_P1_SP1, + .vbat_mult = TWL6032_VBAT_MULT, + .vbat_shift = TWL6032_VBAT_SHIFT, +}; + +static int twl6030_gpadc_read_channel(u8 channel_no) +{ + u8 lsb = 0; + u8 msb = 0; + int ret = 0; + + ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, + twl->adc_rbase + channel_no * 2, &lsb); + if (ret) + return ret; + + ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, + twl->adc_rbase + 1 + channel_no * 2, &msb); + if (ret) + return ret; + + return (msb << 8) | lsb; +} + +static int twl6030_gpadc_sw2_trigger(void) +{ + u8 val; + int ret = 0; + + ret = twl6030_i2c_write_u8(TWL6030_CHIP_ADC, + twl->adc_ctrl, twl->adc_enable); + if (ret) + return ret; + + /* Waiting until the SW1 conversion ends*/ + val = CTRL_P2_BUSY; + + while (!((val & CTRL_P2_EOCP2) && (!(val & CTRL_P2_BUSY)))) { + ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, + twl->adc_ctrl, &val); + if (ret) + return ret; + udelay(1000); + } + + return 0; +} + +void twl6030_stop_usb_charging(void) +{ + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CONTROLLER_CTRL1, 0); + + return; +} + +void twl6030_start_usb_charging(void) +{ + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_VICHRG, CHARGERUSB_VICHRG_1500); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_CINLIMIT, CHARGERUSB_CIN_LIMIT_NONE); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CONTROLLER_INT_MASK, MBAT_TEMP); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_INT_MASK, MASK_MCHARGERUSB_THMREG); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_VOREG, CHARGERUSB_VOREG_4P0); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_CTRL2, CHARGERUSB_CTRL2_VITERM_400); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_CTRL1, TERM); + /* Enable USB charging */ + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CONTROLLER_CTRL1, CONTROLLER_CTRL1_EN_CHARGER); + return; +} + +int twl6030_get_battery_current(void) +{ + int battery_current = 0; + u8 msb = 0; + u8 lsb = 0; + + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, FG_REG_11, &msb); + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, FG_REG_10, &lsb); + battery_current = ((msb << 8) | lsb); + + /* convert 10 bit signed number to 16 bit signed number */ + if (battery_current >= 0x2000) + battery_current = (battery_current - 0x4000); + + battery_current = battery_current * 3000 / 4096; + printf("Battery Current: %d mA\n", battery_current); + + return battery_current; +} + +int twl6030_get_battery_voltage(void) +{ + int battery_volt = 0; + int ret = 0; + u8 vbatch; + + if (twl->chip_type == chip_TWL6030) { + vbatch = TWL6030_GPADC_VBAT_CHNL; + } else { + ret = twl6030_i2c_write_u8(TWL6030_CHIP_ADC, + TWL6032_GPSELECT_ISB, + TWL6032_GPADC_VBAT_CHNL); + if (ret) + return ret; + vbatch = 0; + } + + /* Start GPADC SW conversion */ + ret = twl6030_gpadc_sw2_trigger(); + if (ret) { + printf("Failed to convert battery voltage\n"); + return ret; + } + + /* measure Vbat voltage */ + battery_volt = twl6030_gpadc_read_channel(vbatch); + if (battery_volt < 0) { + printf("Failed to read battery voltage\n"); + return ret; + } + battery_volt = (battery_volt * twl->vbat_mult) >> twl->vbat_shift; + printf("Battery Voltage: %d mV\n", battery_volt); + + return battery_volt; +} + +void twl6030_init_battery_charging(void) +{ + u8 val = 0; + int battery_volt = 0; + int ret = 0; + + ret = twl6030_i2c_read_u8(TWL6030_CHIP_USB, USB_PRODUCT_ID_LSB, &val); + if (ret) { + puts("twl6030_init_battery_charging(): could not determine chip!\n"); + return; + } + if (val == 0x30) { + twl = &twl6030_info; + } else if (val == 0x32) { + twl = &twl6032_info; + } else { + puts("twl6030_init_battery_charging(): unsupported chip type\n"); + return; + } + + /* Enable VBAT measurement */ + if (twl->chip_type == chip_TWL6030) { + twl6030_i2c_write_u8(TWL6030_CHIP_PM, MISC1, VBAT_MEAS); + twl6030_i2c_write_u8(TWL6030_CHIP_ADC, + TWL6030_GPADC_CTRL, + GPADC_CTRL_SCALER_DIV4); + } else { + twl6030_i2c_write_u8(TWL6030_CHIP_ADC, + TWL6032_GPADC_CTRL2, + GPADC_CTRL2_CH18_SCALER_EN); + } + + /* Enable GPADC module */ + ret = twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, TOGGLE1, FGS | GPADCS); + if (ret) { + printf("Failed to enable GPADC\n"); + return; + } + + battery_volt = twl6030_get_battery_voltage(); + if (battery_volt < 0) + return; + + if (battery_volt < 3000) + printf("Main battery voltage too low!\n"); + + /* Check for the presence of USB charger */ + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, CONTROLLER_STAT1, &val); + + /* check for battery presence indirectly via Fuel gauge */ + if ((val & VBUS_DET) && (battery_volt < 3300)) + twl6030_start_usb_charging(); + + return; +} + +void twl6030_power_mmc_init() +{ + /* set voltage to 3.0 and turnon for APP */ + twl6030_i2c_write_u8(TWL6030_CHIP_PM, VMMC_CFG_VOLTATE, 0x15); + twl6030_i2c_write_u8(TWL6030_CHIP_PM, VMMC_CFG_STATE, 0x21); +} + +void twl6030_usb_device_settings() +{ + u8 data = 0; + + /* Select APP Group and set state to ON */ + twl6030_i2c_write_u8(TWL6030_CHIP_PM, VUSB_CFG_STATE, 0x21); + + twl6030_i2c_read_u8(TWL6030_CHIP_PM, MISC2, &data); + data |= 0x10; + + /* Select the input supply for VBUS regulator */ + twl6030_i2c_write_u8(TWL6030_CHIP_PM, MISC2, data); +} +#endif diff --git a/qemu/roms/u-boot/drivers/qe/Makefile b/qemu/roms/u-boot/drivers/qe/Makefile new file mode 100644 index 000000000..7f1bd0692 --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (C) 2006 Freescale Semiconductor, Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y := qe.o uccf.o uec.o uec_phy.o +obj-$(CONFIG_OF_LIBFDT) += fdt.o diff --git a/qemu/roms/u-boot/drivers/qe/fdt.c b/qemu/roms/u-boot/drivers/qe/fdt.c new file mode 100644 index 000000000..d9a7d8206 --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/fdt.c @@ -0,0 +1,74 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "qe.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* + * If a QE firmware has been uploaded, then add the 'firmware' node under + * the 'qe' node. + */ +void fdt_fixup_qe_firmware(void *blob) +{ + struct qe_firmware_info *qe_fw_info; + int node, ret; + + qe_fw_info = qe_get_firmware_info(); + if (!qe_fw_info) + return; + + node = fdt_path_offset(blob, "/qe"); + if (node < 0) + return; + + /* We assume the node doesn't exist yet */ + node = fdt_add_subnode(blob, node, "firmware"); + if (node < 0) + return; + + ret = fdt_setprop(blob, node, "extended-modes", + &qe_fw_info->extended_modes, sizeof(u64)); + if (ret < 0) + goto error; + + ret = fdt_setprop_string(blob, node, "id", qe_fw_info->id); + if (ret < 0) + goto error; + + ret = fdt_setprop(blob, node, "virtual-traps", qe_fw_info->vtraps, + sizeof(qe_fw_info->vtraps)); + if (ret < 0) + goto error; + + return; + +error: + fdt_del_node(blob, node); +} + +void ft_qe_setup(void *blob) +{ + do_fixup_by_prop_u32(blob, "device_type", "qe", 4, + "bus-frequency", gd->arch.qe_clk, 1); + do_fixup_by_prop_u32(blob, "device_type", "qe", 4, + "brg-frequency", gd->arch.brg_clk, 1); + do_fixup_by_compat_u32(blob, "fsl,qe", + "clock-frequency", gd->arch.qe_clk, 1); + do_fixup_by_compat_u32(blob, "fsl,qe", + "bus-frequency", gd->arch.qe_clk, 1); + do_fixup_by_compat_u32(blob, "fsl,qe", + "brg-frequency", gd->arch.brg_clk, 1); + do_fixup_by_compat_u32(blob, "fsl,qe-gtm", + "clock-frequency", gd->arch.qe_clk / 2, 1); + fdt_fixup_qe_firmware(blob); +} diff --git a/qemu/roms/u-boot/drivers/qe/qe.c b/qemu/roms/u-boot/drivers/qe/qe.c new file mode 100644 index 000000000..9c5fbd1d6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/qe.c @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2006-2009 Freescale Semiconductor, Inc. + * + * Dave Liu + * based on source code of Shlomi Gridish + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "common.h" +#include +#include "asm/errno.h" +#include "asm/io.h" +#include "asm/immap_qe.h" +#include "qe.h" + +#define MPC85xx_DEVDISR_QE_DISABLE 0x1 + +qe_map_t *qe_immr = NULL; +static qe_snum_t snums[QE_NUM_OF_SNUM]; + +DECLARE_GLOBAL_DATA_PTR; + +void qe_issue_cmd(uint cmd, uint sbc, u8 mcn, u32 cmd_data) +{ + u32 cecr; + + if (cmd == QE_RESET) { + out_be32(&qe_immr->cp.cecr,(u32) (cmd | QE_CR_FLG)); + } else { + out_be32(&qe_immr->cp.cecdr, cmd_data); + out_be32(&qe_immr->cp.cecr, (sbc | QE_CR_FLG | + ((u32) mcn<cp.cecr); + } while (cecr & QE_CR_FLG); + + return; +} + +uint qe_muram_alloc(uint size, uint align) +{ + uint retloc; + uint align_mask, off; + uint savebase; + + align_mask = align - 1; + savebase = gd->arch.mp_alloc_base; + + off = gd->arch.mp_alloc_base & align_mask; + if (off != 0) + gd->arch.mp_alloc_base += (align - off); + + if ((off = size & align_mask) != 0) + size += (align - off); + + if ((gd->arch.mp_alloc_base + size) >= gd->arch.mp_alloc_top) { + gd->arch.mp_alloc_base = savebase; + printf("%s: ran out of ram.\n", __FUNCTION__); + } + + retloc = gd->arch.mp_alloc_base; + gd->arch.mp_alloc_base += size; + + memset((void *)&qe_immr->muram[retloc], 0, size); + + __asm__ __volatile__("sync"); + + return retloc; +} + +void *qe_muram_addr(uint offset) +{ + return (void *)&qe_immr->muram[offset]; +} + +static void qe_sdma_init(void) +{ + volatile sdma_t *p; + uint sdma_buffer_base; + + p = (volatile sdma_t *)&qe_immr->sdma; + + /* All of DMA transaction in bus 1 */ + out_be32(&p->sdaqr, 0); + out_be32(&p->sdaqmr, 0); + + /* Allocate 2KB temporary buffer for sdma */ + sdma_buffer_base = qe_muram_alloc(2048, 4096); + out_be32(&p->sdwbcr, sdma_buffer_base & QE_SDEBCR_BA_MASK); + + /* Clear sdma status */ + out_be32(&p->sdsr, 0x03000000); + + /* Enable global mode on bus 1, and 2KB buffer size */ + out_be32(&p->sdmr, QE_SDMR_GLB_1_MSK | (0x3 << QE_SDMR_CEN_SHIFT)); +} + +/* This table is a list of the serial numbers of the Threads, taken from the + * "SNUM Table" chart in the QE Reference Manual. The order is not important, + * we just need to know what the SNUMs are for the threads. + */ +static u8 thread_snum[] = { +/* Evthreads 16-29 are not supported in MPC8309 */ +#if !defined(CONFIG_MPC8309) + 0x04, 0x05, 0x0c, 0x0d, + 0x14, 0x15, 0x1c, 0x1d, + 0x24, 0x25, 0x2c, 0x2d, + 0x34, 0x35, +#endif + 0x88, 0x89, 0x98, 0x99, + 0xa8, 0xa9, 0xb8, 0xb9, + 0xc8, 0xc9, 0xd8, 0xd9, + 0xe8, 0xe9, 0x08, 0x09, + 0x18, 0x19, 0x28, 0x29, + 0x38, 0x39, 0x48, 0x49, + 0x58, 0x59, 0x68, 0x69, + 0x78, 0x79, 0x80, 0x81 +}; + +static void qe_snums_init(void) +{ + int i; + + for (i = 0; i < QE_NUM_OF_SNUM; i++) { + snums[i].state = QE_SNUM_STATE_FREE; + snums[i].num = thread_snum[i]; + } +} + +int qe_get_snum(void) +{ + int snum = -EBUSY; + int i; + + for (i = 0; i < QE_NUM_OF_SNUM; i++) { + if (snums[i].state == QE_SNUM_STATE_FREE) { + snums[i].state = QE_SNUM_STATE_USED; + snum = snums[i].num; + break; + } + } + + return snum; +} + +void qe_put_snum(u8 snum) +{ + int i; + + for (i = 0; i < QE_NUM_OF_SNUM; i++) { + if (snums[i].num == snum) { + snums[i].state = QE_SNUM_STATE_FREE; + break; + } + } +} + +void qe_init(uint qe_base) +{ + /* Init the QE IMMR base */ + qe_immr = (qe_map_t *)qe_base; + +#ifdef CONFIG_SYS_QE_FMAN_FW_IN_NOR + /* + * Upload microcode to IRAM for those SOCs which do not have ROM in QE. + */ + qe_upload_firmware((const void *)CONFIG_SYS_QE_FW_ADDR); + + /* enable the microcode in IRAM */ + out_be32(&qe_immr->iram.iready,QE_IRAM_READY); +#endif + + gd->arch.mp_alloc_base = QE_DATAONLY_BASE; + gd->arch.mp_alloc_top = gd->arch.mp_alloc_base + QE_DATAONLY_SIZE; + + qe_sdma_init(); + qe_snums_init(); +} + +void qe_reset(void) +{ + qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID, + (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0); +} + +void qe_assign_page(uint snum, uint para_ram_base) +{ + u32 cecr; + + out_be32(&qe_immr->cp.cecdr, para_ram_base); + out_be32(&qe_immr->cp.cecr, ((u32) snum<cp.cecr); + } while (cecr & QE_CR_FLG ); + + return; +} + +/* + * brg: 0~15 as BRG1~BRG16 + rate: baud rate + * BRG input clock comes from the BRGCLK (internal clock generated from + the QE clock, it is one-half of the QE clock), If need the clock source + from CLKn pin, we have te change the function. + */ + +#define BRG_CLK (gd->arch.brg_clk) + +int qe_set_brg(uint brg, uint rate) +{ + volatile uint *bp; + u32 divisor; + int div16 = 0; + + if (brg >= QE_NUM_OF_BRGS) + return -EINVAL; + bp = (uint *)&qe_immr->brg.brgc1; + bp += brg; + + divisor = (BRG_CLK / rate); + if (divisor > QE_BRGC_DIVISOR_MAX + 1) { + div16 = 1; + divisor /= 16; + } + + *bp = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE; + __asm__ __volatile__("sync"); + + if (div16) { + *bp |= QE_BRGC_DIV16; + __asm__ __volatile__("sync"); + } + + return 0; +} + +/* Set ethernet MII clock master +*/ +int qe_set_mii_clk_src(int ucc_num) +{ + u32 cmxgcr; + + /* check if the UCC number is in range. */ + if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) { + printf("%s: ucc num not in ranges\n", __FUNCTION__); + return -EINVAL; + } + + cmxgcr = in_be32(&qe_immr->qmx.cmxgcr); + cmxgcr &= ~QE_CMXGCR_MII_ENET_MNG_MASK; + cmxgcr |= (ucc_num <qmx.cmxgcr, cmxgcr); + + return 0; +} + +/* Firmware information stored here for qe_get_firmware_info() */ +static struct qe_firmware_info qe_firmware_info; + +/* + * Set to 1 if QE firmware has been uploaded, and therefore + * qe_firmware_info contains valid data. + */ +static int qe_firmware_uploaded; + +/* + * Upload a QE microcode + * + * This function is a worker function for qe_upload_firmware(). It does + * the actual uploading of the microcode. + */ +static void qe_upload_microcode(const void *base, + const struct qe_microcode *ucode) +{ + const u32 *code = base + be32_to_cpu(ucode->code_offset); + unsigned int i; + + if (ucode->major || ucode->minor || ucode->revision) + printf("QE: uploading microcode '%s' version %u.%u.%u\n", + ucode->id, ucode->major, ucode->minor, ucode->revision); + else + printf("QE: uploading microcode '%s'\n", ucode->id); + + /* Use auto-increment */ + out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) | + QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR); + + for (i = 0; i < be32_to_cpu(ucode->count); i++) + out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i])); +} + +/* + * Upload a microcode to the I-RAM at a specific address. + * + * See docs/README.qe_firmware for information on QE microcode uploading. + * + * Currently, only version 1 is supported, so the 'version' field must be + * set to 1. + * + * The SOC model and revision are not validated, they are only displayed for + * informational purposes. + * + * 'calc_size' is the calculated size, in bytes, of the firmware structure and + * all of the microcode structures, minus the CRC. + * + * 'length' is the size that the structure says it is, including the CRC. + */ +int qe_upload_firmware(const struct qe_firmware *firmware) +{ + unsigned int i; + unsigned int j; + u32 crc; + size_t calc_size = sizeof(struct qe_firmware); + size_t length; + const struct qe_header *hdr; +#ifdef CONFIG_DEEP_SLEEP + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); +#endif + if (!firmware) { + printf("Invalid address\n"); + return -EINVAL; + } + + hdr = &firmware->header; + length = be32_to_cpu(hdr->length); + + /* Check the magic */ + if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || + (hdr->magic[2] != 'F')) { + printf("Not a microcode\n"); +#ifdef CONFIG_DEEP_SLEEP + setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_QE_DISABLE); +#endif + return -EPERM; + } + + /* Check the version */ + if (hdr->version != 1) { + printf("Unsupported version\n"); + return -EPERM; + } + + /* Validate some of the fields */ + if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) { + printf("Invalid data\n"); + return -EINVAL; + } + + /* Validate the length and check if there's a CRC */ + calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); + + for (i = 0; i < firmware->count; i++) + /* + * For situations where the second RISC uses the same microcode + * as the first, the 'code_offset' and 'count' fields will be + * zero, so it's okay to add those. + */ + calc_size += sizeof(u32) * + be32_to_cpu(firmware->microcode[i].count); + + /* Validate the length */ + if (length != calc_size + sizeof(u32)) { + printf("Invalid length\n"); + return -EPERM; + } + + /* + * Validate the CRC. We would normally call crc32_no_comp(), but that + * function isn't available unless you turn on JFFS support. + */ + crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size)); + if (crc != (crc32(-1, (const void *) firmware, calc_size) ^ -1)) { + printf("Firmware CRC is invalid\n"); + return -EIO; + } + + /* + * If the microcode calls for it, split the I-RAM. + */ + if (!firmware->split) { + out_be16(&qe_immr->cp.cercr, + in_be16(&qe_immr->cp.cercr) | QE_CP_CERCR_CIR); + } + + if (firmware->soc.model) + printf("Firmware '%s' for %u V%u.%u\n", + firmware->id, be16_to_cpu(firmware->soc.model), + firmware->soc.major, firmware->soc.minor); + else + printf("Firmware '%s'\n", firmware->id); + + /* + * The QE only supports one microcode per RISC, so clear out all the + * saved microcode information and put in the new. + */ + memset(&qe_firmware_info, 0, sizeof(qe_firmware_info)); + strcpy(qe_firmware_info.id, (char *)firmware->id); + qe_firmware_info.extended_modes = firmware->extended_modes; + memcpy(qe_firmware_info.vtraps, firmware->vtraps, + sizeof(firmware->vtraps)); + qe_firmware_uploaded = 1; + + /* Loop through each microcode. */ + for (i = 0; i < firmware->count; i++) { + const struct qe_microcode *ucode = &firmware->microcode[i]; + + /* Upload a microcode if it's present */ + if (ucode->code_offset) + qe_upload_microcode(firmware, ucode); + + /* Program the traps for this processor */ + for (j = 0; j < 16; j++) { + u32 trap = be32_to_cpu(ucode->traps[j]); + + if (trap) + out_be32(&qe_immr->rsp[i].tibcr[j], trap); + } + + /* Enable traps */ + out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr)); + } + + return 0; +} + +struct qe_firmware_info *qe_get_firmware_info(void) +{ + return qe_firmware_uploaded ? &qe_firmware_info : NULL; +} + +static int qe_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong addr; + + if (argc < 3) + return cmd_usage(cmdtp); + + if (strcmp(argv[1], "fw") == 0) { + addr = simple_strtoul(argv[2], NULL, 16); + + if (!addr) { + printf("Invalid address\n"); + return -EINVAL; + } + + /* + * If a length was supplied, compare that with the 'length' + * field. + */ + + if (argc > 3) { + ulong length = simple_strtoul(argv[3], NULL, 16); + struct qe_firmware *firmware = (void *) addr; + + if (length != be32_to_cpu(firmware->header.length)) { + printf("Length mismatch\n"); + return -EINVAL; + } + } + + return qe_upload_firmware((const struct qe_firmware *) addr); + } + + return cmd_usage(cmdtp); +} + +U_BOOT_CMD( + qe, 4, 0, qe_cmd, + "QUICC Engine commands", + "fw [] - Upload firmware binary at address to " + "the QE,\n" + "\twith optional length verification." +); diff --git a/qemu/roms/u-boot/drivers/qe/qe.h b/qemu/roms/u-boot/drivers/qe/qe.h new file mode 100644 index 000000000..ebb7c5f12 --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/qe.h @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2006-2009 Freescale Semiconductor, Inc. + * + * Dave Liu + * based on source code of Shlomi Gridish + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __QE_H__ +#define __QE_H__ + +#include "common.h" + +#define QE_NUM_OF_BRGS 16 +#define UCC_MAX_NUM 8 + +#define QE_DATAONLY_BASE 0 +#define QE_DATAONLY_SIZE (QE_MURAM_SIZE - QE_DATAONLY_BASE) + +/* QE threads SNUM +*/ +typedef enum qe_snum_state { + QE_SNUM_STATE_USED, /* used */ + QE_SNUM_STATE_FREE /* free */ +} qe_snum_state_e; + +typedef struct qe_snum { + u8 num; /* snum */ + qe_snum_state_e state; /* state */ +} qe_snum_t; + +/* QE RISC allocation +*/ +#define QE_RISC_ALLOCATION_RISC1 0x1 /* RISC 1 */ +#define QE_RISC_ALLOCATION_RISC2 0x2 /* RISC 2 */ +#define QE_RISC_ALLOCATION_RISC3 0x4 /* RISC 3 */ +#define QE_RISC_ALLOCATION_RISC4 0x8 /* RISC 4 */ +#define QE_RISC_ALLOCATION_RISC1_AND_RISC2 (QE_RISC_ALLOCATION_RISC1 | \ + QE_RISC_ALLOCATION_RISC2) +#define QE_RISC_ALLOCATION_FOUR_RISCS (QE_RISC_ALLOCATION_RISC1 | \ + QE_RISC_ALLOCATION_RISC2 | \ + QE_RISC_ALLOCATION_RISC3 | \ + QE_RISC_ALLOCATION_RISC4) + +/* QE CECR commands for UCC fast. +*/ +#define QE_CR_FLG 0x00010000 +#define QE_RESET 0x80000000 +#define QE_INIT_TX_RX 0x00000000 +#define QE_INIT_RX 0x00000001 +#define QE_INIT_TX 0x00000002 +#define QE_ENTER_HUNT_MODE 0x00000003 +#define QE_STOP_TX 0x00000004 +#define QE_GRACEFUL_STOP_TX 0x00000005 +#define QE_RESTART_TX 0x00000006 +#define QE_SWITCH_COMMAND 0x00000007 +#define QE_SET_GROUP_ADDRESS 0x00000008 +#define QE_INSERT_CELL 0x00000009 +#define QE_ATM_TRANSMIT 0x0000000a +#define QE_CELL_POOL_GET 0x0000000b +#define QE_CELL_POOL_PUT 0x0000000c +#define QE_IMA_HOST_CMD 0x0000000d +#define QE_ATM_MULTI_THREAD_INIT 0x00000011 +#define QE_ASSIGN_PAGE 0x00000012 +#define QE_START_FLOW_CONTROL 0x00000014 +#define QE_STOP_FLOW_CONTROL 0x00000015 +#define QE_ASSIGN_PAGE_TO_DEVICE 0x00000016 +#define QE_GRACEFUL_STOP_RX 0x0000001a +#define QE_RESTART_RX 0x0000001b + +/* QE CECR Sub Block Code - sub block code of QE command. +*/ +#define QE_CR_SUBBLOCK_INVALID 0x00000000 +#define QE_CR_SUBBLOCK_USB 0x03200000 +#define QE_CR_SUBBLOCK_UCCFAST1 0x02000000 +#define QE_CR_SUBBLOCK_UCCFAST2 0x02200000 +#define QE_CR_SUBBLOCK_UCCFAST3 0x02400000 +#define QE_CR_SUBBLOCK_UCCFAST4 0x02600000 +#define QE_CR_SUBBLOCK_UCCFAST5 0x02800000 +#define QE_CR_SUBBLOCK_UCCFAST6 0x02a00000 +#define QE_CR_SUBBLOCK_UCCFAST7 0x02c00000 +#define QE_CR_SUBBLOCK_UCCFAST8 0x02e00000 +#define QE_CR_SUBBLOCK_UCCSLOW1 0x00000000 +#define QE_CR_SUBBLOCK_UCCSLOW2 0x00200000 +#define QE_CR_SUBBLOCK_UCCSLOW3 0x00400000 +#define QE_CR_SUBBLOCK_UCCSLOW4 0x00600000 +#define QE_CR_SUBBLOCK_UCCSLOW5 0x00800000 +#define QE_CR_SUBBLOCK_UCCSLOW6 0x00a00000 +#define QE_CR_SUBBLOCK_UCCSLOW7 0x00c00000 +#define QE_CR_SUBBLOCK_UCCSLOW8 0x00e00000 +#define QE_CR_SUBBLOCK_MCC1 0x03800000 +#define QE_CR_SUBBLOCK_MCC2 0x03a00000 +#define QE_CR_SUBBLOCK_MCC3 0x03000000 +#define QE_CR_SUBBLOCK_IDMA1 0x02800000 +#define QE_CR_SUBBLOCK_IDMA2 0x02a00000 +#define QE_CR_SUBBLOCK_IDMA3 0x02c00000 +#define QE_CR_SUBBLOCK_IDMA4 0x02e00000 +#define QE_CR_SUBBLOCK_HPAC 0x01e00000 +#define QE_CR_SUBBLOCK_SPI1 0x01400000 +#define QE_CR_SUBBLOCK_SPI2 0x01600000 +#define QE_CR_SUBBLOCK_RAND 0x01c00000 +#define QE_CR_SUBBLOCK_TIMER 0x01e00000 +#define QE_CR_SUBBLOCK_GENERAL 0x03c00000 + +/* QE CECR Protocol - For non-MCC, specifies mode for QE CECR command. +*/ +#define QE_CR_PROTOCOL_UNSPECIFIED 0x00 /* For all other protocols */ +#define QE_CR_PROTOCOL_HDLC_TRANSPARENT 0x00 +#define QE_CR_PROTOCOL_ATM_POS 0x0A +#define QE_CR_PROTOCOL_ETHERNET 0x0C +#define QE_CR_PROTOCOL_L2_SWITCH 0x0D +#define QE_CR_PROTOCOL_SHIFT 6 + +/* QE ASSIGN PAGE command +*/ +#define QE_CR_ASSIGN_PAGE_SNUM_SHIFT 17 + +/* Communication Direction. +*/ +typedef enum comm_dir { + COMM_DIR_NONE = 0, + COMM_DIR_RX = 1, + COMM_DIR_TX = 2, + COMM_DIR_RX_AND_TX = 3 +} comm_dir_e; + +/* Clocks and BRG's +*/ +typedef enum qe_clock { + QE_CLK_NONE = 0, + QE_BRG1, /* Baud Rate Generator 1 */ + QE_BRG2, /* Baud Rate Generator 2 */ + QE_BRG3, /* Baud Rate Generator 3 */ + QE_BRG4, /* Baud Rate Generator 4 */ + QE_BRG5, /* Baud Rate Generator 5 */ + QE_BRG6, /* Baud Rate Generator 6 */ + QE_BRG7, /* Baud Rate Generator 7 */ + QE_BRG8, /* Baud Rate Generator 8 */ + QE_BRG9, /* Baud Rate Generator 9 */ + QE_BRG10, /* Baud Rate Generator 10 */ + QE_BRG11, /* Baud Rate Generator 11 */ + QE_BRG12, /* Baud Rate Generator 12 */ + QE_BRG13, /* Baud Rate Generator 13 */ + QE_BRG14, /* Baud Rate Generator 14 */ + QE_BRG15, /* Baud Rate Generator 15 */ + QE_BRG16, /* Baud Rate Generator 16 */ + QE_CLK1, /* Clock 1 */ + QE_CLK2, /* Clock 2 */ + QE_CLK3, /* Clock 3 */ + QE_CLK4, /* Clock 4 */ + QE_CLK5, /* Clock 5 */ + QE_CLK6, /* Clock 6 */ + QE_CLK7, /* Clock 7 */ + QE_CLK8, /* Clock 8 */ + QE_CLK9, /* Clock 9 */ + QE_CLK10, /* Clock 10 */ + QE_CLK11, /* Clock 11 */ + QE_CLK12, /* Clock 12 */ + QE_CLK13, /* Clock 13 */ + QE_CLK14, /* Clock 14 */ + QE_CLK15, /* Clock 15 */ + QE_CLK16, /* Clock 16 */ + QE_CLK17, /* Clock 17 */ + QE_CLK18, /* Clock 18 */ + QE_CLK19, /* Clock 19 */ + QE_CLK20, /* Clock 20 */ + QE_CLK21, /* Clock 21 */ + QE_CLK22, /* Clock 22 */ + QE_CLK23, /* Clock 23 */ + QE_CLK24, /* Clock 24 */ + QE_CLK_DUMMY +} qe_clock_e; + +/* QE CMXGCR register +*/ +#define QE_CMXGCR_MII_ENET_MNG_MASK 0x00007000 +#define QE_CMXGCR_MII_ENET_MNG_SHIFT 12 + +/* QE CMXUCR registers + */ +#define QE_CMXUCR_TX_CLK_SRC_MASK 0x0000000F + +/* QE BRG configuration register +*/ +#define QE_BRGC_ENABLE 0x00010000 +#define QE_BRGC_DIVISOR_SHIFT 1 +#define QE_BRGC_DIVISOR_MAX 0xFFF +#define QE_BRGC_DIV16 1 + +/* QE SDMA registers +*/ +#define QE_SDSR_BER1 0x02000000 +#define QE_SDSR_BER2 0x01000000 + +#define QE_SDMR_GLB_1_MSK 0x80000000 +#define QE_SDMR_ADR_SEL 0x20000000 +#define QE_SDMR_BER1_MSK 0x02000000 +#define QE_SDMR_BER2_MSK 0x01000000 +#define QE_SDMR_EB1_MSK 0x00800000 +#define QE_SDMR_ER1_MSK 0x00080000 +#define QE_SDMR_ER2_MSK 0x00040000 +#define QE_SDMR_CEN_MASK 0x0000E000 +#define QE_SDMR_SBER_1 0x00000200 +#define QE_SDMR_SBER_2 0x00000200 +#define QE_SDMR_EB1_PR_MASK 0x000000C0 +#define QE_SDMR_ER1_PR 0x00000008 + +#define QE_SDMR_CEN_SHIFT 13 +#define QE_SDMR_EB1_PR_SHIFT 6 + +#define QE_SDTM_MSNUM_SHIFT 24 + +#define QE_SDEBCR_BA_MASK 0x01FFFFFF + +/* Communication Processor */ +#define QE_CP_CERCR_MEE 0x8000 /* Multi-user RAM ECC enable */ +#define QE_CP_CERCR_IEE 0x4000 /* Instruction RAM ECC enable */ +#define QE_CP_CERCR_CIR 0x0800 /* Common instruction RAM */ + +/* I-RAM */ +#define QE_IRAM_IADD_AIE 0x80000000 /* Auto Increment Enable */ +#define QE_IRAM_IADD_BADDR 0x00080000 /* Base Address */ +#define QE_IRAM_READY 0x80000000 + +/* Structure that defines QE firmware binary files. + * + * See doc/README.qe_firmware for a description of these fields. + */ +struct qe_firmware { + struct qe_header { + u32 length; /* Length of the entire structure, in bytes */ + u8 magic[3]; /* Set to { 'Q', 'E', 'F' } */ + u8 version; /* Version of this layout. First ver is '1' */ + } header; + u8 id[62]; /* Null-terminated identifier string */ + u8 split; /* 0 = shared I-RAM, 1 = split I-RAM */ + u8 count; /* Number of microcode[] structures */ + struct { + u16 model; /* The SOC model */ + u8 major; /* The SOC revision major */ + u8 minor; /* The SOC revision minor */ + } __attribute__ ((packed)) soc; + u8 padding[4]; /* Reserved, for alignment */ + u64 extended_modes; /* Extended modes */ + u32 vtraps[8]; /* Virtual trap addresses */ + u8 reserved[4]; /* Reserved, for future expansion */ + struct qe_microcode { + u8 id[32]; /* Null-terminated identifier */ + u32 traps[16]; /* Trap addresses, 0 == ignore */ + u32 eccr; /* The value for the ECCR register */ + u32 iram_offset;/* Offset into I-RAM for the code */ + u32 count; /* Number of 32-bit words of the code */ + u32 code_offset;/* Offset of the actual microcode */ + u8 major; /* The microcode version major */ + u8 minor; /* The microcode version minor */ + u8 revision; /* The microcode version revision */ + u8 padding; /* Reserved, for alignment */ + u8 reserved[4]; /* Reserved, for future expansion */ + } __attribute__ ((packed)) microcode[1]; + /* All microcode binaries should be located here */ + /* CRC32 should be located here, after the microcode binaries */ +} __attribute__ ((packed)); + +struct qe_firmware_info { + char id[64]; /* Firmware name */ + u32 vtraps[8]; /* Virtual trap addresses */ + u64 extended_modes; /* Extended modes */ +}; + +void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign); +void qe_issue_cmd(uint cmd, uint sbc, u8 mcn, u32 cmd_data); +uint qe_muram_alloc(uint size, uint align); +void *qe_muram_addr(uint offset); +int qe_get_snum(void); +void qe_put_snum(u8 snum); +void qe_init(uint qe_base); +void qe_reset(void); +void qe_assign_page(uint snum, uint para_ram_base); +int qe_set_brg(uint brg, uint rate); +int qe_set_mii_clk_src(int ucc_num); +int qe_upload_firmware(const struct qe_firmware *firmware); +struct qe_firmware_info *qe_get_firmware_info(void); +void ft_qe_setup(void *blob); +void qe_init(uint qe_base); +void qe_reset(void); + +#endif /* __QE_H__ */ diff --git a/qemu/roms/u-boot/drivers/qe/uccf.c b/qemu/roms/u-boot/drivers/qe/uccf.c new file mode 100644 index 000000000..593d96d1a --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/uccf.c @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu + * based on source code of Shlomi Gridish + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "common.h" +#include "malloc.h" +#include "asm/errno.h" +#include "asm/io.h" +#include "asm/immap_qe.h" +#include "qe.h" +#include "uccf.h" + +void ucc_fast_transmit_on_demand(ucc_fast_private_t *uccf) +{ + out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD); +} + +u32 ucc_fast_get_qe_cr_subblock(int ucc_num) +{ + switch (ucc_num) { + case 0: return QE_CR_SUBBLOCK_UCCFAST1; + case 1: return QE_CR_SUBBLOCK_UCCFAST2; + case 2: return QE_CR_SUBBLOCK_UCCFAST3; + case 3: return QE_CR_SUBBLOCK_UCCFAST4; + case 4: return QE_CR_SUBBLOCK_UCCFAST5; + case 5: return QE_CR_SUBBLOCK_UCCFAST6; + case 6: return QE_CR_SUBBLOCK_UCCFAST7; + case 7: return QE_CR_SUBBLOCK_UCCFAST8; + default: return QE_CR_SUBBLOCK_INVALID; + } +} + +static void ucc_get_cmxucr_reg(int ucc_num, volatile u32 **p_cmxucr, + u8 *reg_num, u8 *shift) +{ + switch (ucc_num) { + case 0: /* UCC1 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr1); + *reg_num = 1; + *shift = 16; + break; + case 2: /* UCC3 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr1); + *reg_num = 1; + *shift = 0; + break; + case 4: /* UCC5 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr2); + *reg_num = 2; + *shift = 16; + break; + case 6: /* UCC7 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr2); + *reg_num = 2; + *shift = 0; + break; + case 1: /* UCC2 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr3); + *reg_num = 3; + *shift = 16; + break; + case 3: /* UCC4 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr3); + *reg_num = 3; + *shift = 0; + break; + case 5: /* UCC6 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr4); + *reg_num = 4; + *shift = 16; + break; + case 7: /* UCC8 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr4); + *reg_num = 4; + *shift = 0; + break; + default: + break; + } +} + +static int ucc_set_clk_src(int ucc_num, qe_clock_e clock, comm_dir_e mode) +{ + volatile u32 *p_cmxucr = NULL; + u8 reg_num = 0; + u8 shift = 0; + u32 clockBits; + u32 clockMask; + int source = -1; + + /* check if the UCC number is in range. */ + if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) + return -EINVAL; + + if (! ((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) { + printf("%s: bad comm mode type passed\n", __FUNCTION__); + return -EINVAL; + } + + ucc_get_cmxucr_reg(ucc_num, &p_cmxucr, ®_num, &shift); + + switch (reg_num) { + case 1: + switch (clock) { + case QE_BRG1: source = 1; break; + case QE_BRG2: source = 2; break; + case QE_BRG7: source = 3; break; + case QE_BRG8: source = 4; break; + case QE_CLK9: source = 5; break; + case QE_CLK10: source = 6; break; + case QE_CLK11: source = 7; break; + case QE_CLK12: source = 8; break; + case QE_CLK15: source = 9; break; + case QE_CLK16: source = 10; break; + default: source = -1; break; + } + break; + case 2: + switch (clock) { + case QE_BRG5: source = 1; break; + case QE_BRG6: source = 2; break; + case QE_BRG7: source = 3; break; + case QE_BRG8: source = 4; break; + case QE_CLK13: source = 5; break; + case QE_CLK14: source = 6; break; + case QE_CLK19: source = 7; break; + case QE_CLK20: source = 8; break; + case QE_CLK15: source = 9; break; + case QE_CLK16: source = 10; break; + default: source = -1; break; + } + break; + case 3: + switch (clock) { + case QE_BRG9: source = 1; break; + case QE_BRG10: source = 2; break; + case QE_BRG15: source = 3; break; + case QE_BRG16: source = 4; break; + case QE_CLK3: source = 5; break; + case QE_CLK4: source = 6; break; + case QE_CLK17: source = 7; break; + case QE_CLK18: source = 8; break; + case QE_CLK7: source = 9; break; + case QE_CLK8: source = 10; break; + case QE_CLK16: source = 11; break; + default: source = -1; break; + } + break; + case 4: + switch (clock) { + case QE_BRG13: source = 1; break; + case QE_BRG14: source = 2; break; + case QE_BRG15: source = 3; break; + case QE_BRG16: source = 4; break; + case QE_CLK5: source = 5; break; + case QE_CLK6: source = 6; break; + case QE_CLK21: source = 7; break; + case QE_CLK22: source = 8; break; + case QE_CLK7: source = 9; break; + case QE_CLK8: source = 10; break; + case QE_CLK16: source = 11; break; + default: source = -1; break; + } + break; + default: + source = -1; + break; + } + + if (source == -1) { + printf("%s: Bad combination of clock and UCC\n", __FUNCTION__); + return -ENOENT; + } + + clockBits = (u32) source; + clockMask = QE_CMXUCR_TX_CLK_SRC_MASK; + if (mode == COMM_DIR_RX) { + clockBits <<= 4; /* Rx field is 4 bits to left of Tx field */ + clockMask <<= 4; /* Rx field is 4 bits to left of Tx field */ + } + clockBits <<= shift; + clockMask <<= shift; + + out_be32(p_cmxucr, (in_be32(p_cmxucr) & ~clockMask) | clockBits); + + return 0; +} + +static uint ucc_get_reg_baseaddr(int ucc_num) +{ + uint base = 0; + + /* check if the UCC number is in range */ + if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) { + printf("%s: the UCC num not in ranges\n", __FUNCTION__); + return 0; + } + + switch (ucc_num) { + case 0: base = 0x00002000; break; + case 1: base = 0x00003000; break; + case 2: base = 0x00002200; break; + case 3: base = 0x00003200; break; + case 4: base = 0x00002400; break; + case 5: base = 0x00003400; break; + case 6: base = 0x00002600; break; + case 7: base = 0x00003600; break; + default: break; + } + + base = (uint)qe_immr + base; + return base; +} + +void ucc_fast_enable(ucc_fast_private_t *uccf, comm_dir_e mode) +{ + ucc_fast_t *uf_regs; + u32 gumr; + + uf_regs = uccf->uf_regs; + + /* Enable reception and/or transmission on this UCC. */ + gumr = in_be32(&uf_regs->gumr); + if (mode & COMM_DIR_TX) { + gumr |= UCC_FAST_GUMR_ENT; + uccf->enabled_tx = 1; + } + if (mode & COMM_DIR_RX) { + gumr |= UCC_FAST_GUMR_ENR; + uccf->enabled_rx = 1; + } + out_be32(&uf_regs->gumr, gumr); +} + +void ucc_fast_disable(ucc_fast_private_t *uccf, comm_dir_e mode) +{ + ucc_fast_t *uf_regs; + u32 gumr; + + uf_regs = uccf->uf_regs; + + /* Disable reception and/or transmission on this UCC. */ + gumr = in_be32(&uf_regs->gumr); + if (mode & COMM_DIR_TX) { + gumr &= ~UCC_FAST_GUMR_ENT; + uccf->enabled_tx = 0; + } + if (mode & COMM_DIR_RX) { + gumr &= ~UCC_FAST_GUMR_ENR; + uccf->enabled_rx = 0; + } + out_be32(&uf_regs->gumr, gumr); +} + +int ucc_fast_init(ucc_fast_info_t *uf_info, ucc_fast_private_t **uccf_ret) +{ + ucc_fast_private_t *uccf; + ucc_fast_t *uf_regs; + + if (!uf_info) + return -EINVAL; + + if ((uf_info->ucc_num < 0) || (uf_info->ucc_num > UCC_MAX_NUM - 1)) { + printf("%s: Illagal UCC number!\n", __FUNCTION__); + return -EINVAL; + } + + uccf = (ucc_fast_private_t *)malloc(sizeof(ucc_fast_private_t)); + if (!uccf) { + printf("%s: No memory for UCC fast data structure!\n", + __FUNCTION__); + return -ENOMEM; + } + memset(uccf, 0, sizeof(ucc_fast_private_t)); + + /* Save fast UCC structure */ + uccf->uf_info = uf_info; + uccf->uf_regs = (ucc_fast_t *)ucc_get_reg_baseaddr(uf_info->ucc_num); + + if (uccf->uf_regs == NULL) { + printf("%s: No memory map for UCC fast controller!\n", + __FUNCTION__); + return -ENOMEM; + } + + uccf->enabled_tx = 0; + uccf->enabled_rx = 0; + + uf_regs = uccf->uf_regs; + uccf->p_ucce = (u32 *) &(uf_regs->ucce); + uccf->p_uccm = (u32 *) &(uf_regs->uccm); + + /* Init GUEMR register, UCC both Rx and Tx is Fast protocol */ + out_8(&uf_regs->guemr, UCC_GUEMR_SET_RESERVED3 | UCC_GUEMR_MODE_FAST_RX + | UCC_GUEMR_MODE_FAST_TX); + + /* Set GUMR, disable UCC both Rx and Tx, Ethernet protocol */ + out_be32(&uf_regs->gumr, UCC_FAST_GUMR_ETH); + + /* Set the Giga ethernet VFIFO stuff */ + if (uf_info->eth_type == GIGA_ETH) { + /* Allocate memory for Tx Virtual Fifo */ + uccf->ucc_fast_tx_virtual_fifo_base_offset = + qe_muram_alloc(UCC_GETH_UTFS_GIGA_INIT, + UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + + /* Allocate memory for Rx Virtual Fifo */ + uccf->ucc_fast_rx_virtual_fifo_base_offset = + qe_muram_alloc(UCC_GETH_URFS_GIGA_INIT + + UCC_FAST_RX_VIRTUAL_FIFO_SIZE_PAD, + UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + + /* utfb, urfb are offsets from MURAM base */ + out_be32(&uf_regs->utfb, + uccf->ucc_fast_tx_virtual_fifo_base_offset); + out_be32(&uf_regs->urfb, + uccf->ucc_fast_rx_virtual_fifo_base_offset); + + /* Set Virtual Fifo registers */ + out_be16(&uf_regs->urfs, UCC_GETH_URFS_GIGA_INIT); + out_be16(&uf_regs->urfet, UCC_GETH_URFET_GIGA_INIT); + out_be16(&uf_regs->urfset, UCC_GETH_URFSET_GIGA_INIT); + out_be16(&uf_regs->utfs, UCC_GETH_UTFS_GIGA_INIT); + out_be16(&uf_regs->utfet, UCC_GETH_UTFET_GIGA_INIT); + out_be16(&uf_regs->utftt, UCC_GETH_UTFTT_GIGA_INIT); + } + + /* Set the Fast ethernet VFIFO stuff */ + if (uf_info->eth_type == FAST_ETH) { + /* Allocate memory for Tx Virtual Fifo */ + uccf->ucc_fast_tx_virtual_fifo_base_offset = + qe_muram_alloc(UCC_GETH_UTFS_INIT, + UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + + /* Allocate memory for Rx Virtual Fifo */ + uccf->ucc_fast_rx_virtual_fifo_base_offset = + qe_muram_alloc(UCC_GETH_URFS_INIT + + UCC_FAST_RX_VIRTUAL_FIFO_SIZE_PAD, + UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + + /* utfb, urfb are offsets from MURAM base */ + out_be32(&uf_regs->utfb, + uccf->ucc_fast_tx_virtual_fifo_base_offset); + out_be32(&uf_regs->urfb, + uccf->ucc_fast_rx_virtual_fifo_base_offset); + + /* Set Virtual Fifo registers */ + out_be16(&uf_regs->urfs, UCC_GETH_URFS_INIT); + out_be16(&uf_regs->urfet, UCC_GETH_URFET_INIT); + out_be16(&uf_regs->urfset, UCC_GETH_URFSET_INIT); + out_be16(&uf_regs->utfs, UCC_GETH_UTFS_INIT); + out_be16(&uf_regs->utfet, UCC_GETH_UTFET_INIT); + out_be16(&uf_regs->utftt, UCC_GETH_UTFTT_INIT); + } + + /* Rx clock routing */ + if (uf_info->rx_clock != QE_CLK_NONE) { + if (ucc_set_clk_src(uf_info->ucc_num, + uf_info->rx_clock, COMM_DIR_RX)) { + printf("%s: Illegal value for parameter 'RxClock'.\n", + __FUNCTION__); + return -EINVAL; + } + } + + /* Tx clock routing */ + if (uf_info->tx_clock != QE_CLK_NONE) { + if (ucc_set_clk_src(uf_info->ucc_num, + uf_info->tx_clock, COMM_DIR_TX)) { + printf("%s: Illegal value for parameter 'TxClock'.\n", + __FUNCTION__); + return -EINVAL; + } + } + + /* Clear interrupt mask register to disable all of interrupts */ + out_be32(&uf_regs->uccm, 0x0); + + /* Writing '1' to clear all of envents */ + out_be32(&uf_regs->ucce, 0xffffffff); + + *uccf_ret = uccf; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/qe/uccf.h b/qemu/roms/u-boot/drivers/qe/uccf.h new file mode 100644 index 000000000..0b57e2f42 --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/uccf.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu + * based on source code of Shlomi Gridish + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __UCCF_H__ +#define __UCCF_H__ + +#include "common.h" +#include "qe.h" +#include "asm/immap_qe.h" + +/* Fast or Giga ethernet +*/ +typedef enum enet_type { + FAST_ETH, + GIGA_ETH, +} enet_type_e; + +/* General UCC Extended Mode Register +*/ +#define UCC_GUEMR_MODE_MASK_RX 0x02 +#define UCC_GUEMR_MODE_MASK_TX 0x01 +#define UCC_GUEMR_MODE_FAST_RX 0x02 +#define UCC_GUEMR_MODE_FAST_TX 0x01 +#define UCC_GUEMR_MODE_SLOW_RX 0x00 +#define UCC_GUEMR_MODE_SLOW_TX 0x00 +#define UCC_GUEMR_SET_RESERVED3 0x10 /* Bit 3 must be set 1 */ + +/* General UCC FAST Mode Register +*/ +#define UCC_FAST_GUMR_TCI 0x20000000 +#define UCC_FAST_GUMR_TRX 0x10000000 +#define UCC_FAST_GUMR_TTX 0x08000000 +#define UCC_FAST_GUMR_CDP 0x04000000 +#define UCC_FAST_GUMR_CTSP 0x02000000 +#define UCC_FAST_GUMR_CDS 0x01000000 +#define UCC_FAST_GUMR_CTSS 0x00800000 +#define UCC_FAST_GUMR_TXSY 0x00020000 +#define UCC_FAST_GUMR_RSYN 0x00010000 +#define UCC_FAST_GUMR_RTSM 0x00002000 +#define UCC_FAST_GUMR_REVD 0x00000400 +#define UCC_FAST_GUMR_ENR 0x00000020 +#define UCC_FAST_GUMR_ENT 0x00000010 + +/* GUMR [MODE] bit maps +*/ +#define UCC_FAST_GUMR_HDLC 0x00000000 +#define UCC_FAST_GUMR_QMC 0x00000002 +#define UCC_FAST_GUMR_UART 0x00000004 +#define UCC_FAST_GUMR_BISYNC 0x00000008 +#define UCC_FAST_GUMR_ATM 0x0000000a +#define UCC_FAST_GUMR_ETH 0x0000000c + +/* Transmit On Demand (UTORD) +*/ +#define UCC_SLOW_TOD 0x8000 +#define UCC_FAST_TOD 0x8000 + +/* Fast Ethernet (10/100 Mbps) +*/ +#define UCC_GETH_URFS_INIT 512 /* Rx virtual FIFO size */ +#define UCC_GETH_URFET_INIT 256 /* 1/2 urfs */ +#define UCC_GETH_URFSET_INIT 384 /* 3/4 urfs */ +#define UCC_GETH_UTFS_INIT 512 /* Tx virtual FIFO size */ +#define UCC_GETH_UTFET_INIT 256 /* 1/2 utfs */ +#define UCC_GETH_UTFTT_INIT 128 + +/* Gigabit Ethernet (1000 Mbps) +*/ +#define UCC_GETH_URFS_GIGA_INIT 4096/*2048*/ /* Rx virtual FIFO size */ +#define UCC_GETH_URFET_GIGA_INIT 2048/*1024*/ /* 1/2 urfs */ +#define UCC_GETH_URFSET_GIGA_INIT 3072/*1536*/ /* 3/4 urfs */ +#define UCC_GETH_UTFS_GIGA_INIT 8192/*2048*/ /* Tx virtual FIFO size */ +#define UCC_GETH_UTFET_GIGA_INIT 4096/*1024*/ /* 1/2 utfs */ +#define UCC_GETH_UTFTT_GIGA_INIT 0x400/*0x40*/ /* */ + +/* UCC fast alignment +*/ +#define UCC_FAST_RX_ALIGN 4 +#define UCC_FAST_MRBLR_ALIGNMENT 4 +#define UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT 8 + +/* Sizes +*/ +#define UCC_FAST_RX_VIRTUAL_FIFO_SIZE_PAD 8 + +/* UCC fast structure. +*/ +typedef struct ucc_fast_info { + int ucc_num; + qe_clock_e rx_clock; + qe_clock_e tx_clock; + enet_type_e eth_type; +} ucc_fast_info_t; + +typedef struct ucc_fast_private { + ucc_fast_info_t *uf_info; + ucc_fast_t *uf_regs; /* a pointer to memory map of UCC regs */ + u32 *p_ucce; /* a pointer to the event register */ + u32 *p_uccm; /* a pointer to the mask register */ + int enabled_tx; /* whether UCC is enabled for Tx (ENT) */ + int enabled_rx; /* whether UCC is enabled for Rx (ENR) */ + u32 ucc_fast_tx_virtual_fifo_base_offset; + u32 ucc_fast_rx_virtual_fifo_base_offset; +} ucc_fast_private_t; + +void ucc_fast_transmit_on_demand(ucc_fast_private_t *uccf); +u32 ucc_fast_get_qe_cr_subblock(int ucc_num); +void ucc_fast_enable(ucc_fast_private_t *uccf, comm_dir_e mode); +void ucc_fast_disable(ucc_fast_private_t *uccf, comm_dir_e mode); +int ucc_fast_init(ucc_fast_info_t *uf_info, ucc_fast_private_t **uccf_ret); + +#endif /* __UCCF_H__ */ diff --git a/qemu/roms/u-boot/drivers/qe/uec.c b/qemu/roms/u-boot/drivers/qe/uec.c new file mode 100644 index 000000000..68045733b --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/uec.c @@ -0,0 +1,1421 @@ +/* + * Copyright (C) 2006-2011 Freescale Semiconductor, Inc. + * + * Dave Liu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "common.h" +#include "net.h" +#include "malloc.h" +#include "asm/errno.h" +#include "asm/io.h" +#include "asm/immap_qe.h" +#include "qe.h" +#include "uccf.h" +#include "uec.h" +#include "uec_phy.h" +#include "miiphy.h" +#include + +/* Default UTBIPAR SMI address */ +#ifndef CONFIG_UTBIPAR_INIT_TBIPA +#define CONFIG_UTBIPAR_INIT_TBIPA 0x1F +#endif + +static uec_info_t uec_info[] = { +#ifdef CONFIG_UEC_ETH1 + STD_UEC_INFO(1), /* UEC1 */ +#endif +#ifdef CONFIG_UEC_ETH2 + STD_UEC_INFO(2), /* UEC2 */ +#endif +#ifdef CONFIG_UEC_ETH3 + STD_UEC_INFO(3), /* UEC3 */ +#endif +#ifdef CONFIG_UEC_ETH4 + STD_UEC_INFO(4), /* UEC4 */ +#endif +#ifdef CONFIG_UEC_ETH5 + STD_UEC_INFO(5), /* UEC5 */ +#endif +#ifdef CONFIG_UEC_ETH6 + STD_UEC_INFO(6), /* UEC6 */ +#endif +#ifdef CONFIG_UEC_ETH7 + STD_UEC_INFO(7), /* UEC7 */ +#endif +#ifdef CONFIG_UEC_ETH8 + STD_UEC_INFO(8), /* UEC8 */ +#endif +}; + +#define MAXCONTROLLERS (8) + +static struct eth_device *devlist[MAXCONTROLLERS]; + +static int uec_mac_enable(uec_private_t *uec, comm_dir_e mode) +{ + uec_t *uec_regs; + u32 maccfg1; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + uec_regs = uec->uec_regs; + + maccfg1 = in_be32(&uec_regs->maccfg1); + + if (mode & COMM_DIR_TX) { + maccfg1 |= MACCFG1_ENABLE_TX; + out_be32(&uec_regs->maccfg1, maccfg1); + uec->mac_tx_enabled = 1; + } + + if (mode & COMM_DIR_RX) { + maccfg1 |= MACCFG1_ENABLE_RX; + out_be32(&uec_regs->maccfg1, maccfg1); + uec->mac_rx_enabled = 1; + } + + return 0; +} + +static int uec_mac_disable(uec_private_t *uec, comm_dir_e mode) +{ + uec_t *uec_regs; + u32 maccfg1; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + uec_regs = uec->uec_regs; + + maccfg1 = in_be32(&uec_regs->maccfg1); + + if (mode & COMM_DIR_TX) { + maccfg1 &= ~MACCFG1_ENABLE_TX; + out_be32(&uec_regs->maccfg1, maccfg1); + uec->mac_tx_enabled = 0; + } + + if (mode & COMM_DIR_RX) { + maccfg1 &= ~MACCFG1_ENABLE_RX; + out_be32(&uec_regs->maccfg1, maccfg1); + uec->mac_rx_enabled = 0; + } + + return 0; +} + +static int uec_graceful_stop_tx(uec_private_t *uec) +{ + ucc_fast_t *uf_regs; + u32 cecr_subblock; + u32 ucce; + + if (!uec || !uec->uccf) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + + uf_regs = uec->uccf->uf_regs; + + /* Clear the grace stop event */ + out_be32(&uf_regs->ucce, UCCE_GRA); + + /* Issue host command */ + cecr_subblock = + ucc_fast_get_qe_cr_subblock(uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_ETHERNET, 0); + + /* Wait for command to complete */ + do { + ucce = in_be32(&uf_regs->ucce); + } while (! (ucce & UCCE_GRA)); + + uec->grace_stopped_tx = 1; + + return 0; +} + +static int uec_graceful_stop_rx(uec_private_t *uec) +{ + u32 cecr_subblock; + u8 ack; + + if (!uec) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + + if (!uec->p_rx_glbl_pram) { + printf("%s: No init rx global parameter\n", __FUNCTION__); + return -EINVAL; + } + + /* Clear acknowledge bit */ + ack = uec->p_rx_glbl_pram->rxgstpack; + ack &= ~GRACEFUL_STOP_ACKNOWLEDGE_RX; + uec->p_rx_glbl_pram->rxgstpack = ack; + + /* Keep issuing cmd and checking ack bit until it is asserted */ + do { + /* Issue host command */ + cecr_subblock = + ucc_fast_get_qe_cr_subblock(uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(QE_GRACEFUL_STOP_RX, cecr_subblock, + (u8)QE_CR_PROTOCOL_ETHERNET, 0); + ack = uec->p_rx_glbl_pram->rxgstpack; + } while (! (ack & GRACEFUL_STOP_ACKNOWLEDGE_RX )); + + uec->grace_stopped_rx = 1; + + return 0; +} + +static int uec_restart_tx(uec_private_t *uec) +{ + u32 cecr_subblock; + + if (!uec || !uec->uec_info) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + + cecr_subblock = + ucc_fast_get_qe_cr_subblock(uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(QE_RESTART_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_ETHERNET, 0); + + uec->grace_stopped_tx = 0; + + return 0; +} + +static int uec_restart_rx(uec_private_t *uec) +{ + u32 cecr_subblock; + + if (!uec || !uec->uec_info) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + + cecr_subblock = + ucc_fast_get_qe_cr_subblock(uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(QE_RESTART_RX, cecr_subblock, + (u8)QE_CR_PROTOCOL_ETHERNET, 0); + + uec->grace_stopped_rx = 0; + + return 0; +} + +static int uec_open(uec_private_t *uec, comm_dir_e mode) +{ + ucc_fast_private_t *uccf; + + if (!uec || !uec->uccf) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + uccf = uec->uccf; + + /* check if the UCC number is in range. */ + if (uec->uec_info->uf_info.ucc_num >= UCC_MAX_NUM) { + printf("%s: ucc_num out of range.\n", __FUNCTION__); + return -EINVAL; + } + + /* Enable MAC */ + uec_mac_enable(uec, mode); + + /* Enable UCC fast */ + ucc_fast_enable(uccf, mode); + + /* RISC microcode start */ + if ((mode & COMM_DIR_TX) && uec->grace_stopped_tx) { + uec_restart_tx(uec); + } + if ((mode & COMM_DIR_RX) && uec->grace_stopped_rx) { + uec_restart_rx(uec); + } + + return 0; +} + +static int uec_stop(uec_private_t *uec, comm_dir_e mode) +{ + if (!uec || !uec->uccf) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + + /* check if the UCC number is in range. */ + if (uec->uec_info->uf_info.ucc_num >= UCC_MAX_NUM) { + printf("%s: ucc_num out of range.\n", __FUNCTION__); + return -EINVAL; + } + /* Stop any transmissions */ + if ((mode & COMM_DIR_TX) && !uec->grace_stopped_tx) { + uec_graceful_stop_tx(uec); + } + /* Stop any receptions */ + if ((mode & COMM_DIR_RX) && !uec->grace_stopped_rx) { + uec_graceful_stop_rx(uec); + } + + /* Disable the UCC fast */ + ucc_fast_disable(uec->uccf, mode); + + /* Disable the MAC */ + uec_mac_disable(uec, mode); + + return 0; +} + +static int uec_set_mac_duplex(uec_private_t *uec, int duplex) +{ + uec_t *uec_regs; + u32 maccfg2; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + uec_regs = uec->uec_regs; + + if (duplex == DUPLEX_HALF) { + maccfg2 = in_be32(&uec_regs->maccfg2); + maccfg2 &= ~MACCFG2_FDX; + out_be32(&uec_regs->maccfg2, maccfg2); + } + + if (duplex == DUPLEX_FULL) { + maccfg2 = in_be32(&uec_regs->maccfg2); + maccfg2 |= MACCFG2_FDX; + out_be32(&uec_regs->maccfg2, maccfg2); + } + + return 0; +} + +static int uec_set_mac_if_mode(uec_private_t *uec, + phy_interface_t if_mode, int speed) +{ + phy_interface_t enet_if_mode; + uec_t *uec_regs; + u32 upsmr; + u32 maccfg2; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + + uec_regs = uec->uec_regs; + enet_if_mode = if_mode; + + maccfg2 = in_be32(&uec_regs->maccfg2); + maccfg2 &= ~MACCFG2_INTERFACE_MODE_MASK; + + upsmr = in_be32(&uec->uccf->uf_regs->upsmr); + upsmr &= ~(UPSMR_RPM | UPSMR_TBIM | UPSMR_R10M | UPSMR_RMM); + + switch (speed) { + case SPEED_10: + maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; + switch (enet_if_mode) { + case PHY_INTERFACE_MODE_MII: + break; + case PHY_INTERFACE_MODE_RGMII: + upsmr |= (UPSMR_RPM | UPSMR_R10M); + break; + case PHY_INTERFACE_MODE_RMII: + upsmr |= (UPSMR_R10M | UPSMR_RMM); + break; + default: + return -EINVAL; + break; + } + break; + case SPEED_100: + maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; + switch (enet_if_mode) { + case PHY_INTERFACE_MODE_MII: + break; + case PHY_INTERFACE_MODE_RGMII: + upsmr |= UPSMR_RPM; + break; + case PHY_INTERFACE_MODE_RMII: + upsmr |= UPSMR_RMM; + break; + default: + return -EINVAL; + break; + } + break; + case SPEED_1000: + maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE; + switch (enet_if_mode) { + case PHY_INTERFACE_MODE_GMII: + break; + case PHY_INTERFACE_MODE_TBI: + upsmr |= UPSMR_TBIM; + break; + case PHY_INTERFACE_MODE_RTBI: + upsmr |= (UPSMR_RPM | UPSMR_TBIM); + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII: + upsmr |= UPSMR_RPM; + break; + case PHY_INTERFACE_MODE_SGMII: + upsmr |= UPSMR_SGMM; + break; + default: + return -EINVAL; + break; + } + break; + default: + return -EINVAL; + break; + } + + out_be32(&uec_regs->maccfg2, maccfg2); + out_be32(&uec->uccf->uf_regs->upsmr, upsmr); + + return 0; +} + +static int init_mii_management_configuration(uec_mii_t *uec_mii_regs) +{ + uint timeout = 0x1000; + u32 miimcfg = 0; + + miimcfg = in_be32(&uec_mii_regs->miimcfg); + miimcfg |= MIIMCFG_MNGMNT_CLC_DIV_INIT_VALUE; + out_be32(&uec_mii_regs->miimcfg, miimcfg); + + /* Wait until the bus is free */ + while ((in_be32(&uec_mii_regs->miimcfg) & MIIMIND_BUSY) && timeout--); + if (timeout <= 0) { + printf("%s: The MII Bus is stuck!", __FUNCTION__); + return -ETIMEDOUT; + } + + return 0; +} + +static int init_phy(struct eth_device *dev) +{ + uec_private_t *uec; + uec_mii_t *umii_regs; + struct uec_mii_info *mii_info; + struct phy_info *curphy; + int err; + + uec = (uec_private_t *)dev->priv; + umii_regs = uec->uec_mii_regs; + + uec->oldlink = 0; + uec->oldspeed = 0; + uec->oldduplex = -1; + + mii_info = malloc(sizeof(*mii_info)); + if (!mii_info) { + printf("%s: Could not allocate mii_info", dev->name); + return -ENOMEM; + } + memset(mii_info, 0, sizeof(*mii_info)); + + if (uec->uec_info->uf_info.eth_type == GIGA_ETH) { + mii_info->speed = SPEED_1000; + } else { + mii_info->speed = SPEED_100; + } + + mii_info->duplex = DUPLEX_FULL; + mii_info->pause = 0; + mii_info->link = 1; + + mii_info->advertising = (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full); + mii_info->autoneg = 1; + mii_info->mii_id = uec->uec_info->phy_address; + mii_info->dev = dev; + + mii_info->mdio_read = &uec_read_phy_reg; + mii_info->mdio_write = &uec_write_phy_reg; + + uec->mii_info = mii_info; + + qe_set_mii_clk_src(uec->uec_info->uf_info.ucc_num); + + if (init_mii_management_configuration(umii_regs)) { + printf("%s: The MII Bus is stuck!", dev->name); + err = -1; + goto bus_fail; + } + + /* get info for this PHY */ + curphy = uec_get_phy_info(uec->mii_info); + if (!curphy) { + printf("%s: No PHY found", dev->name); + err = -1; + goto no_phy; + } + + mii_info->phyinfo = curphy; + + /* Run the commands which initialize the PHY */ + if (curphy->init) { + err = curphy->init(uec->mii_info); + if (err) + goto phy_init_fail; + } + + return 0; + +phy_init_fail: +no_phy: +bus_fail: + free(mii_info); + return err; +} + +static void adjust_link(struct eth_device *dev) +{ + uec_private_t *uec = (uec_private_t *)dev->priv; + struct uec_mii_info *mii_info = uec->mii_info; + + extern void change_phy_interface_mode(struct eth_device *dev, + phy_interface_t mode, int speed); + + if (mii_info->link) { + /* Now we make sure that we can be in full duplex mode. + * If not, we operate in half-duplex mode. */ + if (mii_info->duplex != uec->oldduplex) { + if (!(mii_info->duplex)) { + uec_set_mac_duplex(uec, DUPLEX_HALF); + printf("%s: Half Duplex\n", dev->name); + } else { + uec_set_mac_duplex(uec, DUPLEX_FULL); + printf("%s: Full Duplex\n", dev->name); + } + uec->oldduplex = mii_info->duplex; + } + + if (mii_info->speed != uec->oldspeed) { + phy_interface_t mode = + uec->uec_info->enet_interface_type; + if (uec->uec_info->uf_info.eth_type == GIGA_ETH) { + switch (mii_info->speed) { + case SPEED_1000: + break; + case SPEED_100: + printf ("switching to rgmii 100\n"); + mode = PHY_INTERFACE_MODE_RGMII; + break; + case SPEED_10: + printf ("switching to rgmii 10\n"); + mode = PHY_INTERFACE_MODE_RGMII; + break; + default: + printf("%s: Ack,Speed(%d)is illegal\n", + dev->name, mii_info->speed); + break; + } + } + + /* change phy */ + change_phy_interface_mode(dev, mode, mii_info->speed); + /* change the MAC interface mode */ + uec_set_mac_if_mode(uec, mode, mii_info->speed); + + printf("%s: Speed %dBT\n", dev->name, mii_info->speed); + uec->oldspeed = mii_info->speed; + } + + if (!uec->oldlink) { + printf("%s: Link is up\n", dev->name); + uec->oldlink = 1; + } + + } else { /* if (mii_info->link) */ + if (uec->oldlink) { + printf("%s: Link is down\n", dev->name); + uec->oldlink = 0; + uec->oldspeed = 0; + uec->oldduplex = -1; + } + } +} + +static void phy_change(struct eth_device *dev) +{ + uec_private_t *uec = (uec_private_t *)dev->priv; + +#if defined(CONFIG_P1012) || defined(CONFIG_P1021) || defined(CONFIG_P1025) + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + /* QE9 and QE12 need to be set for enabling QE MII managment signals */ + setbits_be32(&gur->pmuxcr, MPC85xx_PMUXCR_QE9); + setbits_be32(&gur->pmuxcr, MPC85xx_PMUXCR_QE12); +#endif + + /* Update the link, speed, duplex */ + uec->mii_info->phyinfo->read_status(uec->mii_info); + +#if defined(CONFIG_P1012) || defined(CONFIG_P1021) || defined(CONFIG_P1025) + /* + * QE12 is muxed with LBCTL, it needs to be released for enabling + * LBCTL signal for LBC usage. + */ + clrbits_be32(&gur->pmuxcr, MPC85xx_PMUXCR_QE12); +#endif + + /* Adjust the interface according to speed */ + adjust_link(dev); +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + +/* + * Find a device index from the devlist by name + * + * Returns: + * The index where the device is located, -1 on error + */ +static int uec_miiphy_find_dev_by_name(const char *devname) +{ + int i; + + for (i = 0; i < MAXCONTROLLERS; i++) { + if (strncmp(devname, devlist[i]->name, strlen(devname)) == 0) { + break; + } + } + + /* If device cannot be found, returns -1 */ + if (i == MAXCONTROLLERS) { + debug ("%s: device %s not found in devlist\n", __FUNCTION__, devname); + i = -1; + } + + return i; +} + +/* + * Read a MII PHY register. + * + * Returns: + * 0 on success + */ +static int uec_miiphy_read(const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + int devindex = 0; + + if (devname == NULL || value == NULL) { + debug("%s: NULL pointer given\n", __FUNCTION__); + } else { + devindex = uec_miiphy_find_dev_by_name(devname); + if (devindex >= 0) { + *value = uec_read_phy_reg(devlist[devindex], addr, reg); + } + } + return 0; +} + +/* + * Write a MII PHY register. + * + * Returns: + * 0 on success + */ +static int uec_miiphy_write(const char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + int devindex = 0; + + if (devname == NULL) { + debug("%s: NULL pointer given\n", __FUNCTION__); + } else { + devindex = uec_miiphy_find_dev_by_name(devname); + if (devindex >= 0) { + uec_write_phy_reg(devlist[devindex], addr, reg, value); + } + } + return 0; +} +#endif + +static int uec_set_mac_address(uec_private_t *uec, u8 *mac_addr) +{ + uec_t *uec_regs; + u32 mac_addr1; + u32 mac_addr2; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + + uec_regs = uec->uec_regs; + + /* if a station address of 0x12345678ABCD, perform a write to + MACSTNADDR1 of 0xCDAB7856, + MACSTNADDR2 of 0x34120000 */ + + mac_addr1 = (mac_addr[5] << 24) | (mac_addr[4] << 16) | \ + (mac_addr[3] << 8) | (mac_addr[2]); + out_be32(&uec_regs->macstnaddr1, mac_addr1); + + mac_addr2 = ((mac_addr[1] << 24) | (mac_addr[0] << 16)) & 0xffff0000; + out_be32(&uec_regs->macstnaddr2, mac_addr2); + + return 0; +} + +static int uec_convert_threads_num(uec_num_of_threads_e threads_num, + int *threads_num_ret) +{ + int num_threads_numerica; + + switch (threads_num) { + case UEC_NUM_OF_THREADS_1: + num_threads_numerica = 1; + break; + case UEC_NUM_OF_THREADS_2: + num_threads_numerica = 2; + break; + case UEC_NUM_OF_THREADS_4: + num_threads_numerica = 4; + break; + case UEC_NUM_OF_THREADS_6: + num_threads_numerica = 6; + break; + case UEC_NUM_OF_THREADS_8: + num_threads_numerica = 8; + break; + default: + printf("%s: Bad number of threads value.", + __FUNCTION__); + return -EINVAL; + } + + *threads_num_ret = num_threads_numerica; + + return 0; +} + +static void uec_init_tx_parameter(uec_private_t *uec, int num_threads_tx) +{ + uec_info_t *uec_info; + u32 end_bd; + u8 bmrx = 0; + int i; + + uec_info = uec->uec_info; + + /* Alloc global Tx parameter RAM page */ + uec->tx_glbl_pram_offset = qe_muram_alloc( + sizeof(uec_tx_global_pram_t), + UEC_TX_GLOBAL_PRAM_ALIGNMENT); + uec->p_tx_glbl_pram = (uec_tx_global_pram_t *) + qe_muram_addr(uec->tx_glbl_pram_offset); + + /* Zero the global Tx prameter RAM */ + memset(uec->p_tx_glbl_pram, 0, sizeof(uec_tx_global_pram_t)); + + /* Init global Tx parameter RAM */ + + /* TEMODER, RMON statistics disable, one Tx queue */ + out_be16(&uec->p_tx_glbl_pram->temoder, TEMODER_INIT_VALUE); + + /* SQPTR */ + uec->send_q_mem_reg_offset = qe_muram_alloc( + sizeof(uec_send_queue_qd_t), + UEC_SEND_QUEUE_QUEUE_DESCRIPTOR_ALIGNMENT); + uec->p_send_q_mem_reg = (uec_send_queue_mem_region_t *) + qe_muram_addr(uec->send_q_mem_reg_offset); + out_be32(&uec->p_tx_glbl_pram->sqptr, uec->send_q_mem_reg_offset); + + /* Setup the table with TxBDs ring */ + end_bd = (u32)uec->p_tx_bd_ring + (uec_info->tx_bd_ring_len - 1) + * SIZEOFBD; + out_be32(&uec->p_send_q_mem_reg->sqqd[0].bd_ring_base, + (u32)(uec->p_tx_bd_ring)); + out_be32(&uec->p_send_q_mem_reg->sqqd[0].last_bd_completed_address, + end_bd); + + /* Scheduler Base Pointer, we have only one Tx queue, no need it */ + out_be32(&uec->p_tx_glbl_pram->schedulerbasepointer, 0); + + /* TxRMON Base Pointer, TxRMON disable, we don't need it */ + out_be32(&uec->p_tx_glbl_pram->txrmonbaseptr, 0); + + /* TSTATE, global snooping, big endian, the CSB bus selected */ + bmrx = BMR_INIT_VALUE; + out_be32(&uec->p_tx_glbl_pram->tstate, ((u32)(bmrx) << BMR_SHIFT)); + + /* IPH_Offset */ + for (i = 0; i < MAX_IPH_OFFSET_ENTRY; i++) { + out_8(&uec->p_tx_glbl_pram->iphoffset[i], 0); + } + + /* VTAG table */ + for (i = 0; i < UEC_TX_VTAG_TABLE_ENTRY_MAX; i++) { + out_be32(&uec->p_tx_glbl_pram->vtagtable[i], 0); + } + + /* TQPTR */ + uec->thread_dat_tx_offset = qe_muram_alloc( + num_threads_tx * sizeof(uec_thread_data_tx_t) + + 32 *(num_threads_tx == 1), UEC_THREAD_DATA_ALIGNMENT); + + uec->p_thread_data_tx = (uec_thread_data_tx_t *) + qe_muram_addr(uec->thread_dat_tx_offset); + out_be32(&uec->p_tx_glbl_pram->tqptr, uec->thread_dat_tx_offset); +} + +static void uec_init_rx_parameter(uec_private_t *uec, int num_threads_rx) +{ + u8 bmrx = 0; + int i; + uec_82xx_address_filtering_pram_t *p_af_pram; + + /* Allocate global Rx parameter RAM page */ + uec->rx_glbl_pram_offset = qe_muram_alloc( + sizeof(uec_rx_global_pram_t), UEC_RX_GLOBAL_PRAM_ALIGNMENT); + uec->p_rx_glbl_pram = (uec_rx_global_pram_t *) + qe_muram_addr(uec->rx_glbl_pram_offset); + + /* Zero Global Rx parameter RAM */ + memset(uec->p_rx_glbl_pram, 0, sizeof(uec_rx_global_pram_t)); + + /* Init global Rx parameter RAM */ + /* REMODER, Extended feature mode disable, VLAN disable, + LossLess flow control disable, Receive firmware statisic disable, + Extended address parsing mode disable, One Rx queues, + Dynamic maximum/minimum frame length disable, IP checksum check + disable, IP address alignment disable + */ + out_be32(&uec->p_rx_glbl_pram->remoder, REMODER_INIT_VALUE); + + /* RQPTR */ + uec->thread_dat_rx_offset = qe_muram_alloc( + num_threads_rx * sizeof(uec_thread_data_rx_t), + UEC_THREAD_DATA_ALIGNMENT); + uec->p_thread_data_rx = (uec_thread_data_rx_t *) + qe_muram_addr(uec->thread_dat_rx_offset); + out_be32(&uec->p_rx_glbl_pram->rqptr, uec->thread_dat_rx_offset); + + /* Type_or_Len */ + out_be16(&uec->p_rx_glbl_pram->typeorlen, 3072); + + /* RxRMON base pointer, we don't need it */ + out_be32(&uec->p_rx_glbl_pram->rxrmonbaseptr, 0); + + /* IntCoalescingPTR, we don't need it, no interrupt */ + out_be32(&uec->p_rx_glbl_pram->intcoalescingptr, 0); + + /* RSTATE, global snooping, big endian, the CSB bus selected */ + bmrx = BMR_INIT_VALUE; + out_8(&uec->p_rx_glbl_pram->rstate, bmrx); + + /* MRBLR */ + out_be16(&uec->p_rx_glbl_pram->mrblr, MAX_RXBUF_LEN); + + /* RBDQPTR */ + uec->rx_bd_qs_tbl_offset = qe_muram_alloc( + sizeof(uec_rx_bd_queues_entry_t) + \ + sizeof(uec_rx_prefetched_bds_t), + UEC_RX_BD_QUEUES_ALIGNMENT); + uec->p_rx_bd_qs_tbl = (uec_rx_bd_queues_entry_t *) + qe_muram_addr(uec->rx_bd_qs_tbl_offset); + + /* Zero it */ + memset(uec->p_rx_bd_qs_tbl, 0, sizeof(uec_rx_bd_queues_entry_t) + \ + sizeof(uec_rx_prefetched_bds_t)); + out_be32(&uec->p_rx_glbl_pram->rbdqptr, uec->rx_bd_qs_tbl_offset); + out_be32(&uec->p_rx_bd_qs_tbl->externalbdbaseptr, + (u32)uec->p_rx_bd_ring); + + /* MFLR */ + out_be16(&uec->p_rx_glbl_pram->mflr, MAX_FRAME_LEN); + /* MINFLR */ + out_be16(&uec->p_rx_glbl_pram->minflr, MIN_FRAME_LEN); + /* MAXD1 */ + out_be16(&uec->p_rx_glbl_pram->maxd1, MAX_DMA1_LEN); + /* MAXD2 */ + out_be16(&uec->p_rx_glbl_pram->maxd2, MAX_DMA2_LEN); + /* ECAM_PTR */ + out_be32(&uec->p_rx_glbl_pram->ecamptr, 0); + /* L2QT */ + out_be32(&uec->p_rx_glbl_pram->l2qt, 0); + /* L3QT */ + for (i = 0; i < 8; i++) { + out_be32(&uec->p_rx_glbl_pram->l3qt[i], 0); + } + + /* VLAN_TYPE */ + out_be16(&uec->p_rx_glbl_pram->vlantype, 0x8100); + /* TCI */ + out_be16(&uec->p_rx_glbl_pram->vlantci, 0); + + /* Clear PQ2 style address filtering hash table */ + p_af_pram = (uec_82xx_address_filtering_pram_t *) \ + uec->p_rx_glbl_pram->addressfiltering; + + p_af_pram->iaddr_h = 0; + p_af_pram->iaddr_l = 0; + p_af_pram->gaddr_h = 0; + p_af_pram->gaddr_l = 0; +} + +static int uec_issue_init_enet_rxtx_cmd(uec_private_t *uec, + int thread_tx, int thread_rx) +{ + uec_init_cmd_pram_t *p_init_enet_param; + u32 init_enet_param_offset; + uec_info_t *uec_info; + int i; + int snum; + u32 init_enet_offset; + u32 entry_val; + u32 command; + u32 cecr_subblock; + + uec_info = uec->uec_info; + + /* Allocate init enet command parameter */ + uec->init_enet_param_offset = qe_muram_alloc( + sizeof(uec_init_cmd_pram_t), 4); + init_enet_param_offset = uec->init_enet_param_offset; + uec->p_init_enet_param = (uec_init_cmd_pram_t *) + qe_muram_addr(uec->init_enet_param_offset); + + /* Zero init enet command struct */ + memset((void *)uec->p_init_enet_param, 0, sizeof(uec_init_cmd_pram_t)); + + /* Init the command struct */ + p_init_enet_param = uec->p_init_enet_param; + p_init_enet_param->resinit0 = ENET_INIT_PARAM_MAGIC_RES_INIT0; + p_init_enet_param->resinit1 = ENET_INIT_PARAM_MAGIC_RES_INIT1; + p_init_enet_param->resinit2 = ENET_INIT_PARAM_MAGIC_RES_INIT2; + p_init_enet_param->resinit3 = ENET_INIT_PARAM_MAGIC_RES_INIT3; + p_init_enet_param->resinit4 = ENET_INIT_PARAM_MAGIC_RES_INIT4; + p_init_enet_param->largestexternallookupkeysize = 0; + + p_init_enet_param->rgftgfrxglobal |= ((u32)uec_info->num_threads_rx) + << ENET_INIT_PARAM_RGF_SHIFT; + p_init_enet_param->rgftgfrxglobal |= ((u32)uec_info->num_threads_tx) + << ENET_INIT_PARAM_TGF_SHIFT; + + /* Init Rx global parameter pointer */ + p_init_enet_param->rgftgfrxglobal |= uec->rx_glbl_pram_offset | + (u32)uec_info->risc_rx; + + /* Init Rx threads */ + for (i = 0; i < (thread_rx + 1); i++) { + if ((snum = qe_get_snum()) < 0) { + printf("%s can not get snum\n", __FUNCTION__); + return -ENOMEM; + } + + if (i==0) { + init_enet_offset = 0; + } else { + init_enet_offset = qe_muram_alloc( + sizeof(uec_thread_rx_pram_t), + UEC_THREAD_RX_PRAM_ALIGNMENT); + } + + entry_val = ((u32)snum << ENET_INIT_PARAM_SNUM_SHIFT) | + init_enet_offset | (u32)uec_info->risc_rx; + p_init_enet_param->rxthread[i] = entry_val; + } + + /* Init Tx global parameter pointer */ + p_init_enet_param->txglobal = uec->tx_glbl_pram_offset | + (u32)uec_info->risc_tx; + + /* Init Tx threads */ + for (i = 0; i < thread_tx; i++) { + if ((snum = qe_get_snum()) < 0) { + printf("%s can not get snum\n", __FUNCTION__); + return -ENOMEM; + } + + init_enet_offset = qe_muram_alloc(sizeof(uec_thread_tx_pram_t), + UEC_THREAD_TX_PRAM_ALIGNMENT); + + entry_val = ((u32)snum << ENET_INIT_PARAM_SNUM_SHIFT) | + init_enet_offset | (u32)uec_info->risc_tx; + p_init_enet_param->txthread[i] = entry_val; + } + + __asm__ __volatile__("sync"); + + /* Issue QE command */ + command = QE_INIT_TX_RX; + cecr_subblock = ucc_fast_get_qe_cr_subblock( + uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(command, cecr_subblock, (u8) QE_CR_PROTOCOL_ETHERNET, + init_enet_param_offset); + + return 0; +} + +static int uec_startup(uec_private_t *uec) +{ + uec_info_t *uec_info; + ucc_fast_info_t *uf_info; + ucc_fast_private_t *uccf; + ucc_fast_t *uf_regs; + uec_t *uec_regs; + int num_threads_tx; + int num_threads_rx; + u32 utbipar; + u32 length; + u32 align; + qe_bd_t *bd; + u8 *buf; + int i; + + if (!uec || !uec->uec_info) { + printf("%s: uec or uec_info not initial\n", __FUNCTION__); + return -EINVAL; + } + + uec_info = uec->uec_info; + uf_info = &(uec_info->uf_info); + + /* Check if Rx BD ring len is illegal */ + if ((uec_info->rx_bd_ring_len < UEC_RX_BD_RING_SIZE_MIN) || \ + (uec_info->rx_bd_ring_len % UEC_RX_BD_RING_SIZE_ALIGNMENT)) { + printf("%s: Rx BD ring len must be multiple of 4, and > 8.\n", + __FUNCTION__); + return -EINVAL; + } + + /* Check if Tx BD ring len is illegal */ + if (uec_info->tx_bd_ring_len < UEC_TX_BD_RING_SIZE_MIN) { + printf("%s: Tx BD ring length must not be smaller than 2.\n", + __FUNCTION__); + return -EINVAL; + } + + /* Check if MRBLR is illegal */ + if ((MAX_RXBUF_LEN == 0) || (MAX_RXBUF_LEN % UEC_MRBLR_ALIGNMENT)) { + printf("%s: max rx buffer length must be mutliple of 128.\n", + __FUNCTION__); + return -EINVAL; + } + + /* Both Rx and Tx are stopped */ + uec->grace_stopped_rx = 1; + uec->grace_stopped_tx = 1; + + /* Init UCC fast */ + if (ucc_fast_init(uf_info, &uccf)) { + printf("%s: failed to init ucc fast\n", __FUNCTION__); + return -ENOMEM; + } + + /* Save uccf */ + uec->uccf = uccf; + + /* Convert the Tx threads number */ + if (uec_convert_threads_num(uec_info->num_threads_tx, + &num_threads_tx)) { + return -EINVAL; + } + + /* Convert the Rx threads number */ + if (uec_convert_threads_num(uec_info->num_threads_rx, + &num_threads_rx)) { + return -EINVAL; + } + + uf_regs = uccf->uf_regs; + + /* UEC register is following UCC fast registers */ + uec_regs = (uec_t *)(&uf_regs->ucc_eth); + + /* Save the UEC register pointer to UEC private struct */ + uec->uec_regs = uec_regs; + + /* Init UPSMR, enable hardware statistics (UCC) */ + out_be32(&uec->uccf->uf_regs->upsmr, UPSMR_INIT_VALUE); + + /* Init MACCFG1, flow control disable, disable Tx and Rx */ + out_be32(&uec_regs->maccfg1, MACCFG1_INIT_VALUE); + + /* Init MACCFG2, length check, MAC PAD and CRC enable */ + out_be32(&uec_regs->maccfg2, MACCFG2_INIT_VALUE); + + /* Setup MAC interface mode */ + uec_set_mac_if_mode(uec, uec_info->enet_interface_type, uec_info->speed); + + /* Setup MII management base */ +#ifndef CONFIG_eTSEC_MDIO_BUS + uec->uec_mii_regs = (uec_mii_t *)(&uec_regs->miimcfg); +#else + uec->uec_mii_regs = (uec_mii_t *) CONFIG_MIIM_ADDRESS; +#endif + + /* Setup MII master clock source */ + qe_set_mii_clk_src(uec_info->uf_info.ucc_num); + + /* Setup UTBIPAR */ + utbipar = in_be32(&uec_regs->utbipar); + utbipar &= ~UTBIPAR_PHY_ADDRESS_MASK; + + /* Initialize UTBIPAR address to CONFIG_UTBIPAR_INIT_TBIPA for ALL UEC. + * This frees up the remaining SMI addresses for use. + */ + utbipar |= CONFIG_UTBIPAR_INIT_TBIPA << UTBIPAR_PHY_ADDRESS_SHIFT; + out_be32(&uec_regs->utbipar, utbipar); + + /* Configure the TBI for SGMII operation */ + if ((uec->uec_info->enet_interface_type == PHY_INTERFACE_MODE_SGMII) && + (uec->uec_info->speed == SPEED_1000)) { + uec_write_phy_reg(uec->dev, uec_regs->utbipar, + ENET_TBI_MII_ANA, TBIANA_SETTINGS); + + uec_write_phy_reg(uec->dev, uec_regs->utbipar, + ENET_TBI_MII_TBICON, TBICON_CLK_SELECT); + + uec_write_phy_reg(uec->dev, uec_regs->utbipar, + ENET_TBI_MII_CR, TBICR_SETTINGS); + } + + /* Allocate Tx BDs */ + length = ((uec_info->tx_bd_ring_len * SIZEOFBD) / + UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT) * + UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT; + if ((uec_info->tx_bd_ring_len * SIZEOFBD) % + UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT) { + length += UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT; + } + + align = UEC_TX_BD_RING_ALIGNMENT; + uec->tx_bd_ring_offset = (u32)malloc((u32)(length + align)); + if (uec->tx_bd_ring_offset != 0) { + uec->p_tx_bd_ring = (u8 *)((uec->tx_bd_ring_offset + align) + & ~(align - 1)); + } + + /* Zero all of Tx BDs */ + memset((void *)(uec->tx_bd_ring_offset), 0, length + align); + + /* Allocate Rx BDs */ + length = uec_info->rx_bd_ring_len * SIZEOFBD; + align = UEC_RX_BD_RING_ALIGNMENT; + uec->rx_bd_ring_offset = (u32)(malloc((u32)(length + align))); + if (uec->rx_bd_ring_offset != 0) { + uec->p_rx_bd_ring = (u8 *)((uec->rx_bd_ring_offset + align) + & ~(align - 1)); + } + + /* Zero all of Rx BDs */ + memset((void *)(uec->rx_bd_ring_offset), 0, length + align); + + /* Allocate Rx buffer */ + length = uec_info->rx_bd_ring_len * MAX_RXBUF_LEN; + align = UEC_RX_DATA_BUF_ALIGNMENT; + uec->rx_buf_offset = (u32)malloc(length + align); + if (uec->rx_buf_offset != 0) { + uec->p_rx_buf = (u8 *)((uec->rx_buf_offset + align) + & ~(align - 1)); + } + + /* Zero all of the Rx buffer */ + memset((void *)(uec->rx_buf_offset), 0, length + align); + + /* Init TxBD ring */ + bd = (qe_bd_t *)uec->p_tx_bd_ring; + uec->txBd = bd; + + for (i = 0; i < uec_info->tx_bd_ring_len; i++) { + BD_DATA_CLEAR(bd); + BD_STATUS_SET(bd, 0); + BD_LENGTH_SET(bd, 0); + bd ++; + } + BD_STATUS_SET((--bd), TxBD_WRAP); + + /* Init RxBD ring */ + bd = (qe_bd_t *)uec->p_rx_bd_ring; + uec->rxBd = bd; + buf = uec->p_rx_buf; + for (i = 0; i < uec_info->rx_bd_ring_len; i++) { + BD_DATA_SET(bd, buf); + BD_LENGTH_SET(bd, 0); + BD_STATUS_SET(bd, RxBD_EMPTY); + buf += MAX_RXBUF_LEN; + bd ++; + } + BD_STATUS_SET((--bd), RxBD_WRAP | RxBD_EMPTY); + + /* Init global Tx parameter RAM */ + uec_init_tx_parameter(uec, num_threads_tx); + + /* Init global Rx parameter RAM */ + uec_init_rx_parameter(uec, num_threads_rx); + + /* Init ethernet Tx and Rx parameter command */ + if (uec_issue_init_enet_rxtx_cmd(uec, num_threads_tx, + num_threads_rx)) { + printf("%s issue init enet cmd failed\n", __FUNCTION__); + return -ENOMEM; + } + + return 0; +} + +static int uec_init(struct eth_device* dev, bd_t *bd) +{ + uec_private_t *uec; + int err, i; + struct phy_info *curphy; +#if defined(CONFIG_P1012) || defined(CONFIG_P1021) || defined(CONFIG_P1025) + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); +#endif + + uec = (uec_private_t *)dev->priv; + + if (uec->the_first_run == 0) { +#if defined(CONFIG_P1012) || defined(CONFIG_P1021) || defined(CONFIG_P1025) + /* QE9 and QE12 need to be set for enabling QE MII managment signals */ + setbits_be32(&gur->pmuxcr, MPC85xx_PMUXCR_QE9); + setbits_be32(&gur->pmuxcr, MPC85xx_PMUXCR_QE12); +#endif + + err = init_phy(dev); + if (err) { + printf("%s: Cannot initialize PHY, aborting.\n", + dev->name); + return err; + } + + curphy = uec->mii_info->phyinfo; + + if (curphy->config_aneg) { + err = curphy->config_aneg(uec->mii_info); + if (err) { + printf("%s: Can't negotiate PHY\n", dev->name); + return err; + } + } + + /* Give PHYs up to 5 sec to report a link */ + i = 50; + do { + err = curphy->read_status(uec->mii_info); + if (!(((i-- > 0) && !uec->mii_info->link) || err)) + break; + udelay(100000); + } while (1); + +#if defined(CONFIG_P1012) || defined(CONFIG_P1021) || defined(CONFIG_P1025) + /* QE12 needs to be released for enabling LBCTL signal*/ + clrbits_be32(&gur->pmuxcr, MPC85xx_PMUXCR_QE12); +#endif + + if (err || i <= 0) + printf("warning: %s: timeout on PHY link\n", dev->name); + + adjust_link(dev); + uec->the_first_run = 1; + } + + /* Set up the MAC address */ + if (dev->enetaddr[0] & 0x01) { + printf("%s: MacAddress is multcast address\n", + __FUNCTION__); + return -1; + } + uec_set_mac_address(uec, dev->enetaddr); + + + err = uec_open(uec, COMM_DIR_RX_AND_TX); + if (err) { + printf("%s: cannot enable UEC device\n", dev->name); + return -1; + } + + phy_change(dev); + + return (uec->mii_info->link ? 0 : -1); +} + +static void uec_halt(struct eth_device* dev) +{ + uec_private_t *uec = (uec_private_t *)dev->priv; + uec_stop(uec, COMM_DIR_RX_AND_TX); +} + +static int uec_send(struct eth_device *dev, void *buf, int len) +{ + uec_private_t *uec; + ucc_fast_private_t *uccf; + volatile qe_bd_t *bd; + u16 status; + int i; + int result = 0; + + uec = (uec_private_t *)dev->priv; + uccf = uec->uccf; + bd = uec->txBd; + + /* Find an empty TxBD */ + for (i = 0; bd->status & TxBD_READY; i++) { + if (i > 0x100000) { + printf("%s: tx buffer not ready\n", dev->name); + return result; + } + } + + /* Init TxBD */ + BD_DATA_SET(bd, buf); + BD_LENGTH_SET(bd, len); + status = bd->status; + status &= BD_WRAP; + status |= (TxBD_READY | TxBD_LAST); + BD_STATUS_SET(bd, status); + + /* Tell UCC to transmit the buffer */ + ucc_fast_transmit_on_demand(uccf); + + /* Wait for buffer to be transmitted */ + for (i = 0; bd->status & TxBD_READY; i++) { + if (i > 0x100000) { + printf("%s: tx error\n", dev->name); + return result; + } + } + + /* Ok, the buffer be transimitted */ + BD_ADVANCE(bd, status, uec->p_tx_bd_ring); + uec->txBd = bd; + result = 1; + + return result; +} + +static int uec_recv(struct eth_device* dev) +{ + uec_private_t *uec = dev->priv; + volatile qe_bd_t *bd; + u16 status; + u16 len; + u8 *data; + + bd = uec->rxBd; + status = bd->status; + + while (!(status & RxBD_EMPTY)) { + if (!(status & RxBD_ERROR)) { + data = BD_DATA(bd); + len = BD_LENGTH(bd); + NetReceive(data, len); + } else { + printf("%s: Rx error\n", dev->name); + } + status &= BD_CLEAN; + BD_LENGTH_SET(bd, 0); + BD_STATUS_SET(bd, status | RxBD_EMPTY); + BD_ADVANCE(bd, status, uec->p_rx_bd_ring); + status = bd->status; + } + uec->rxBd = bd; + + return 1; +} + +int uec_initialize(bd_t *bis, uec_info_t *uec_info) +{ + struct eth_device *dev; + int i; + uec_private_t *uec; + int err; + + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + if (!dev) + return 0; + memset(dev, 0, sizeof(struct eth_device)); + + /* Allocate the UEC private struct */ + uec = (uec_private_t *)malloc(sizeof(uec_private_t)); + if (!uec) { + return -ENOMEM; + } + memset(uec, 0, sizeof(uec_private_t)); + + /* Adjust uec_info */ +#if (MAX_QE_RISC == 4) + uec_info->risc_tx = QE_RISC_ALLOCATION_FOUR_RISCS; + uec_info->risc_rx = QE_RISC_ALLOCATION_FOUR_RISCS; +#endif + + devlist[uec_info->uf_info.ucc_num] = dev; + + uec->uec_info = uec_info; + uec->dev = dev; + + sprintf(dev->name, "UEC%d", uec_info->uf_info.ucc_num); + dev->iobase = 0; + dev->priv = (void *)uec; + dev->init = uec_init; + dev->halt = uec_halt; + dev->send = uec_send; + dev->recv = uec_recv; + + /* Clear the ethnet address */ + for (i = 0; i < 6; i++) + dev->enetaddr[i] = 0; + + eth_register(dev); + + err = uec_startup(uec); + if (err) { + printf("%s: Cannot configure net device, aborting.",dev->name); + return err; + } + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, uec_miiphy_read, uec_miiphy_write); +#endif + + return 1; +} + +int uec_eth_init(bd_t *bis, uec_info_t *uecs, int num) +{ + int i; + + for (i = 0; i < num; i++) + uec_initialize(bis, &uecs[i]); + + return 0; +} + +int uec_standard_init(bd_t *bis) +{ + return uec_eth_init(bis, uec_info, ARRAY_SIZE(uec_info)); +} diff --git a/qemu/roms/u-boot/drivers/qe/uec.h b/qemu/roms/u-boot/drivers/qe/uec.h new file mode 100644 index 000000000..48a163411 --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/uec.h @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2006-2010 Freescale Semiconductor, Inc. + * + * Dave Liu + * based on source code of Shlomi Gridish + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __UEC_H__ +#define __UEC_H__ + +#include "qe.h" +#include "uccf.h" +#include +#include + +#define MAX_TX_THREADS 8 +#define MAX_RX_THREADS 8 +#define MAX_TX_QUEUES 8 +#define MAX_RX_QUEUES 8 +#define MAX_PREFETCHED_BDS 4 +#define MAX_IPH_OFFSET_ENTRY 8 +#define MAX_ENET_INIT_PARAM_ENTRIES_RX 9 +#define MAX_ENET_INIT_PARAM_ENTRIES_TX 8 + +/* UEC UPSMR (Protocol Specific Mode Register) + */ +#define UPSMR_ECM 0x04000000 /* Enable CAM Miss */ +#define UPSMR_HSE 0x02000000 /* Hardware Statistics Enable */ +#define UPSMR_PRO 0x00400000 /* Promiscuous */ +#define UPSMR_CAP 0x00200000 /* CAM polarity */ +#define UPSMR_RSH 0x00100000 /* Receive Short Frames */ +#define UPSMR_RPM 0x00080000 /* Reduced Pin Mode interfaces */ +#define UPSMR_R10M 0x00040000 /* RGMII/RMII 10 Mode */ +#define UPSMR_RLPB 0x00020000 /* RMII Loopback Mode */ +#define UPSMR_TBIM 0x00010000 /* Ten-bit Interface Mode */ +#define UPSMR_RMM 0x00001000 /* RMII/RGMII Mode */ +#define UPSMR_CAM 0x00000400 /* CAM Address Matching */ +#define UPSMR_BRO 0x00000200 /* Broadcast Address */ +#define UPSMR_RES1 0x00002000 /* Reserved feild - must be 1 */ +#define UPSMR_SGMM 0x00000020 /* SGMII mode */ + +#define UPSMR_INIT_VALUE (UPSMR_HSE | UPSMR_RES1) + +/* UEC MACCFG1 (MAC Configuration 1 Register) + */ +#define MACCFG1_FLOW_RX 0x00000020 /* Flow Control Rx */ +#define MACCFG1_FLOW_TX 0x00000010 /* Flow Control Tx */ +#define MACCFG1_ENABLE_SYNCHED_RX 0x00000008 /* Enable Rx Sync */ +#define MACCFG1_ENABLE_RX 0x00000004 /* Enable Rx */ +#define MACCFG1_ENABLE_SYNCHED_TX 0x00000002 /* Enable Tx Sync */ +#define MACCFG1_ENABLE_TX 0x00000001 /* Enable Tx */ + +#define MACCFG1_INIT_VALUE (0) + +/* UEC MACCFG2 (MAC Configuration 2 Register) + */ +#define MACCFG2_PREL 0x00007000 +#define MACCFG2_PREL_SHIFT (31 - 19) +#define MACCFG2_PREL_MASK 0x0000f000 +#define MACCFG2_SRP 0x00000080 +#define MACCFG2_STP 0x00000040 +#define MACCFG2_RESERVED_1 0x00000020 /* must be set */ +#define MACCFG2_LC 0x00000010 /* Length Check */ +#define MACCFG2_MPE 0x00000008 +#define MACCFG2_FDX 0x00000001 /* Full Duplex */ +#define MACCFG2_FDX_MASK 0x00000001 +#define MACCFG2_PAD_CRC 0x00000004 +#define MACCFG2_CRC_EN 0x00000002 +#define MACCFG2_PAD_AND_CRC_MODE_NONE 0x00000000 +#define MACCFG2_PAD_AND_CRC_MODE_CRC_ONLY 0x00000002 +#define MACCFG2_PAD_AND_CRC_MODE_PAD_AND_CRC 0x00000004 +#define MACCFG2_INTERFACE_MODE_NIBBLE 0x00000100 +#define MACCFG2_INTERFACE_MODE_BYTE 0x00000200 +#define MACCFG2_INTERFACE_MODE_MASK 0x00000300 + +#define MACCFG2_INIT_VALUE (MACCFG2_PREL | MACCFG2_RESERVED_1 | \ + MACCFG2_LC | MACCFG2_PAD_CRC | MACCFG2_FDX) + +/* UEC Event Register +*/ +#define UCCE_MPD 0x80000000 +#define UCCE_SCAR 0x40000000 +#define UCCE_GRA 0x20000000 +#define UCCE_CBPR 0x10000000 +#define UCCE_BSY 0x08000000 +#define UCCE_RXC 0x04000000 +#define UCCE_TXC 0x02000000 +#define UCCE_TXE 0x01000000 +#define UCCE_TXB7 0x00800000 +#define UCCE_TXB6 0x00400000 +#define UCCE_TXB5 0x00200000 +#define UCCE_TXB4 0x00100000 +#define UCCE_TXB3 0x00080000 +#define UCCE_TXB2 0x00040000 +#define UCCE_TXB1 0x00020000 +#define UCCE_TXB0 0x00010000 +#define UCCE_RXB7 0x00008000 +#define UCCE_RXB6 0x00004000 +#define UCCE_RXB5 0x00002000 +#define UCCE_RXB4 0x00001000 +#define UCCE_RXB3 0x00000800 +#define UCCE_RXB2 0x00000400 +#define UCCE_RXB1 0x00000200 +#define UCCE_RXB0 0x00000100 +#define UCCE_RXF7 0x00000080 +#define UCCE_RXF6 0x00000040 +#define UCCE_RXF5 0x00000020 +#define UCCE_RXF4 0x00000010 +#define UCCE_RXF3 0x00000008 +#define UCCE_RXF2 0x00000004 +#define UCCE_RXF1 0x00000002 +#define UCCE_RXF0 0x00000001 + +#define UCCE_TXB (UCCE_TXB7 | UCCE_TXB6 | UCCE_TXB5 | UCCE_TXB4 | \ + UCCE_TXB3 | UCCE_TXB2 | UCCE_TXB1 | UCCE_TXB0) +#define UCCE_RXB (UCCE_RXB7 | UCCE_RXB6 | UCCE_RXB5 | UCCE_RXB4 | \ + UCCE_RXB3 | UCCE_RXB2 | UCCE_RXB1 | UCCE_RXB0) +#define UCCE_RXF (UCCE_RXF7 | UCCE_RXF6 | UCCE_RXF5 | UCCE_RXF4 | \ + UCCE_RXF3 | UCCE_RXF2 | UCCE_RXF1 | UCCE_RXF0) +#define UCCE_OTHER (UCCE_SCAR | UCCE_GRA | UCCE_CBPR | UCCE_BSY | \ + UCCE_RXC | UCCE_TXC | UCCE_TXE) + +/* UEC TEMODR Register +*/ +#define TEMODER_SCHEDULER_ENABLE 0x2000 +#define TEMODER_IP_CHECKSUM_GENERATE 0x0400 +#define TEMODER_PERFORMANCE_OPTIMIZATION_MODE1 0x0200 +#define TEMODER_RMON_STATISTICS 0x0100 +#define TEMODER_NUM_OF_QUEUES_SHIFT (15-15) + +#define TEMODER_INIT_VALUE 0xc000 + +/* UEC REMODR Register +*/ +#define REMODER_RX_RMON_STATISTICS_ENABLE 0x00001000 +#define REMODER_RX_EXTENDED_FEATURES 0x80000000 +#define REMODER_VLAN_OPERATION_TAGGED_SHIFT (31-9 ) +#define REMODER_VLAN_OPERATION_NON_TAGGED_SHIFT (31-10) +#define REMODER_RX_QOS_MODE_SHIFT (31-15) +#define REMODER_RMON_STATISTICS 0x00001000 +#define REMODER_RX_EXTENDED_FILTERING 0x00000800 +#define REMODER_NUM_OF_QUEUES_SHIFT (31-23) +#define REMODER_DYNAMIC_MAX_FRAME_LENGTH 0x00000008 +#define REMODER_DYNAMIC_MIN_FRAME_LENGTH 0x00000004 +#define REMODER_IP_CHECKSUM_CHECK 0x00000002 +#define REMODER_IP_ADDRESS_ALIGNMENT 0x00000001 + +#define REMODER_INIT_VALUE 0 + +/* BMRx - Bus Mode Register */ +#define BMR_GLB 0x20 +#define BMR_BO_BE 0x10 +#define BMR_DTB_SECONDARY_BUS 0x02 +#define BMR_BDB_SECONDARY_BUS 0x01 + +#define BMR_SHIFT 24 +#define BMR_INIT_VALUE (BMR_GLB | BMR_BO_BE) + +/* UEC UCCS (Ethernet Status Register) + */ +#define UCCS_BPR 0x02 +#define UCCS_PAU 0x02 +#define UCCS_MPD 0x01 + +/* UEC MIIMCFG (MII Management Configuration Register) + */ +#define MIIMCFG_RESET_MANAGEMENT 0x80000000 +#define MIIMCFG_NO_PREAMBLE 0x00000010 +#define MIIMCFG_CLOCK_DIVIDE_SHIFT (31 - 31) +#define MIIMCFG_CLOCK_DIVIDE_MASK 0x0000000f +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_4 0x00000001 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_6 0x00000002 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_8 0x00000003 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10 0x00000004 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_14 0x00000005 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_20 0x00000006 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_28 0x00000007 + +#define MIIMCFG_MNGMNT_CLC_DIV_INIT_VALUE \ + MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10 + +/* UEC MIIMCOM (MII Management Command Register) + */ +#define MIIMCOM_SCAN_CYCLE 0x00000002 /* Scan cycle */ +#define MIIMCOM_READ_CYCLE 0x00000001 /* Read cycle */ + +/* UEC MIIMADD (MII Management Address Register) + */ +#define MIIMADD_PHY_ADDRESS_SHIFT (31 - 23) +#define MIIMADD_PHY_REGISTER_SHIFT (31 - 31) + +/* UEC MIIMCON (MII Management Control Register) + */ +#define MIIMCON_PHY_CONTROL_SHIFT (31 - 31) +#define MIIMCON_PHY_STATUS_SHIFT (31 - 31) + +/* UEC MIIMIND (MII Management Indicator Register) + */ +#define MIIMIND_NOT_VALID 0x00000004 +#define MIIMIND_SCAN 0x00000002 +#define MIIMIND_BUSY 0x00000001 + +/* UEC UTBIPAR (Ten Bit Interface Physical Address Register) + */ +#define UTBIPAR_PHY_ADDRESS_SHIFT (31 - 31) +#define UTBIPAR_PHY_ADDRESS_MASK 0x0000001f + +/* UEC UESCR (Ethernet Statistics Control Register) + */ +#define UESCR_AUTOZ 0x8000 +#define UESCR_CLRCNT 0x4000 +#define UESCR_MAXCOV_SHIFT (15 - 7) +#define UESCR_SCOV_SHIFT (15 - 15) + +/****** Tx data struct collection ******/ +/* Tx thread data, each Tx thread has one this struct. +*/ +typedef struct uec_thread_data_tx { + u8 res0[136]; +} __attribute__ ((packed)) uec_thread_data_tx_t; + +/* Tx thread parameter, each Tx thread has one this struct. +*/ +typedef struct uec_thread_tx_pram { + u8 res0[64]; +} __attribute__ ((packed)) uec_thread_tx_pram_t; + +/* Send queue queue-descriptor, each Tx queue has one this QD +*/ +typedef struct uec_send_queue_qd { + u32 bd_ring_base; /* pointer to BD ring base address */ + u8 res0[0x8]; + u32 last_bd_completed_address; /* last entry in BD ring */ + u8 res1[0x30]; +} __attribute__ ((packed)) uec_send_queue_qd_t; + +/* Send queue memory region */ +typedef struct uec_send_queue_mem_region { + uec_send_queue_qd_t sqqd[MAX_TX_QUEUES]; +} __attribute__ ((packed)) uec_send_queue_mem_region_t; + +/* Scheduler struct +*/ +typedef struct uec_scheduler { + u16 cpucount0; /* CPU packet counter */ + u16 cpucount1; /* CPU packet counter */ + u16 cecount0; /* QE packet counter */ + u16 cecount1; /* QE packet counter */ + u16 cpucount2; /* CPU packet counter */ + u16 cpucount3; /* CPU packet counter */ + u16 cecount2; /* QE packet counter */ + u16 cecount3; /* QE packet counter */ + u16 cpucount4; /* CPU packet counter */ + u16 cpucount5; /* CPU packet counter */ + u16 cecount4; /* QE packet counter */ + u16 cecount5; /* QE packet counter */ + u16 cpucount6; /* CPU packet counter */ + u16 cpucount7; /* CPU packet counter */ + u16 cecount6; /* QE packet counter */ + u16 cecount7; /* QE packet counter */ + u32 weightstatus[MAX_TX_QUEUES]; /* accumulated weight factor */ + u32 rtsrshadow; /* temporary variable handled by QE */ + u32 time; /* temporary variable handled by QE */ + u32 ttl; /* temporary variable handled by QE */ + u32 mblinterval; /* max burst length interval */ + u16 nortsrbytetime; /* normalized value of byte time in tsr units */ + u8 fracsiz; + u8 res0[1]; + u8 strictpriorityq; /* Strict Priority Mask register */ + u8 txasap; /* Transmit ASAP register */ + u8 extrabw; /* Extra BandWidth register */ + u8 oldwfqmask; /* temporary variable handled by QE */ + u8 weightfactor[MAX_TX_QUEUES]; /**< weight factor for queues */ + u32 minw; /* temporary variable handled by QE */ + u8 res1[0x70-0x64]; +} __attribute__ ((packed)) uec_scheduler_t; + +/* Tx firmware counters +*/ +typedef struct uec_tx_firmware_statistics_pram { + u32 sicoltx; /* single collision */ + u32 mulcoltx; /* multiple collision */ + u32 latecoltxfr; /* late collision */ + u32 frabortduecol; /* frames aborted due to tx collision */ + u32 frlostinmactxer; /* frames lost due to internal MAC error tx */ + u32 carriersenseertx; /* carrier sense error */ + u32 frtxok; /* frames transmitted OK */ + u32 txfrexcessivedefer; + u32 txpkts256; /* total packets(including bad) 256~511 B */ + u32 txpkts512; /* total packets(including bad) 512~1023B */ + u32 txpkts1024; /* total packets(including bad) 1024~1518B */ + u32 txpktsjumbo; /* total packets(including bad) >1024 */ +} __attribute__ ((packed)) uec_tx_firmware_statistics_pram_t; + +/* Tx global parameter table +*/ +typedef struct uec_tx_global_pram { + u16 temoder; + u8 res0[0x38-0x02]; + u32 sqptr; + u32 schedulerbasepointer; + u32 txrmonbaseptr; + u32 tstate; + u8 iphoffset[MAX_IPH_OFFSET_ENTRY]; + u32 vtagtable[0x8]; + u32 tqptr; + u8 res2[0x80-0x74]; +} __attribute__ ((packed)) uec_tx_global_pram_t; + + +/****** Rx data struct collection ******/ +/* Rx thread data, each Rx thread has one this struct. +*/ +typedef struct uec_thread_data_rx { + u8 res0[40]; +} __attribute__ ((packed)) uec_thread_data_rx_t; + +/* Rx thread parameter, each Rx thread has one this struct. +*/ +typedef struct uec_thread_rx_pram { + u8 res0[128]; +} __attribute__ ((packed)) uec_thread_rx_pram_t; + +/* Rx firmware counters +*/ +typedef struct uec_rx_firmware_statistics_pram { + u32 frrxfcser; /* frames with crc error */ + u32 fraligner; /* frames with alignment error */ + u32 inrangelenrxer; /* in range length error */ + u32 outrangelenrxer; /* out of range length error */ + u32 frtoolong; /* frame too long */ + u32 runt; /* runt */ + u32 verylongevent; /* very long event */ + u32 symbolerror; /* symbol error */ + u32 dropbsy; /* drop because of BD not ready */ + u8 res0[0x8]; + u32 mismatchdrop; /* drop because of MAC filtering */ + u32 underpkts; /* total frames less than 64 octets */ + u32 pkts256; /* total frames(including bad)256~511 B */ + u32 pkts512; /* total frames(including bad)512~1023 B */ + u32 pkts1024; /* total frames(including bad)1024~1518 B */ + u32 pktsjumbo; /* total frames(including bad) >1024 B */ + u32 frlossinmacer; + u32 pausefr; /* pause frames */ + u8 res1[0x4]; + u32 removevlan; + u32 replacevlan; + u32 insertvlan; +} __attribute__ ((packed)) uec_rx_firmware_statistics_pram_t; + +/* Rx interrupt coalescing entry, each Rx queue has one this entry. +*/ +typedef struct uec_rx_interrupt_coalescing_entry { + u32 maxvalue; + u32 counter; +} __attribute__ ((packed)) uec_rx_interrupt_coalescing_entry_t; + +typedef struct uec_rx_interrupt_coalescing_table { + uec_rx_interrupt_coalescing_entry_t entry[MAX_RX_QUEUES]; +} __attribute__ ((packed)) uec_rx_interrupt_coalescing_table_t; + +/* RxBD queue entry, each Rx queue has one this entry. +*/ +typedef struct uec_rx_bd_queues_entry { + u32 bdbaseptr; /* BD base pointer */ + u32 bdptr; /* BD pointer */ + u32 externalbdbaseptr; /* external BD base pointer */ + u32 externalbdptr; /* external BD pointer */ +} __attribute__ ((packed)) uec_rx_bd_queues_entry_t; + +/* Rx global paramter table +*/ +typedef struct uec_rx_global_pram { + u32 remoder; /* ethernet mode reg. */ + u32 rqptr; /* base pointer to the Rx Queues */ + u32 res0[0x1]; + u8 res1[0x20-0xC]; + u16 typeorlen; + u8 res2[0x1]; + u8 rxgstpack; /* ack on GRACEFUL STOP RX command */ + u32 rxrmonbaseptr; /* Rx RMON statistics base */ + u8 res3[0x30-0x28]; + u32 intcoalescingptr; /* Interrupt coalescing table pointer */ + u8 res4[0x36-0x34]; + u8 rstate; + u8 res5[0x46-0x37]; + u16 mrblr; /* max receive buffer length reg. */ + u32 rbdqptr; /* RxBD parameter table description */ + u16 mflr; /* max frame length reg. */ + u16 minflr; /* min frame length reg. */ + u16 maxd1; /* max dma1 length reg. */ + u16 maxd2; /* max dma2 length reg. */ + u32 ecamptr; /* external CAM address */ + u32 l2qt; /* VLAN priority mapping table. */ + u32 l3qt[0x8]; /* IP priority mapping table. */ + u16 vlantype; /* vlan type */ + u16 vlantci; /* default vlan tci */ + u8 addressfiltering[64];/* address filtering data structure */ + u32 exfGlobalParam; /* extended filtering global parameters */ + u8 res6[0x100-0xC4]; /* Initialize to zero */ +} __attribute__ ((packed)) uec_rx_global_pram_t; + +#define GRACEFUL_STOP_ACKNOWLEDGE_RX 0x01 + + +/****** UEC common ******/ +/* UCC statistics - hardware counters +*/ +typedef struct uec_hardware_statistics { + u32 tx64; + u32 tx127; + u32 tx255; + u32 rx64; + u32 rx127; + u32 rx255; + u32 txok; + u16 txcf; + u32 tmca; + u32 tbca; + u32 rxfok; + u32 rxbok; + u32 rbyt; + u32 rmca; + u32 rbca; +} __attribute__ ((packed)) uec_hardware_statistics_t; + +/* InitEnet command parameter +*/ +typedef struct uec_init_cmd_pram { + u8 resinit0; + u8 resinit1; + u8 resinit2; + u8 resinit3; + u16 resinit4; + u8 res1[0x1]; + u8 largestexternallookupkeysize; + u32 rgftgfrxglobal; + u32 rxthread[MAX_ENET_INIT_PARAM_ENTRIES_RX]; /* rx threads */ + u8 res2[0x38 - 0x30]; + u32 txglobal; /* tx global */ + u32 txthread[MAX_ENET_INIT_PARAM_ENTRIES_TX]; /* tx threads */ + u8 res3[0x1]; +} __attribute__ ((packed)) uec_init_cmd_pram_t; + +#define ENET_INIT_PARAM_RGF_SHIFT (32 - 4) +#define ENET_INIT_PARAM_TGF_SHIFT (32 - 8) + +#define ENET_INIT_PARAM_RISC_MASK 0x0000003f +#define ENET_INIT_PARAM_PTR_MASK 0x00ffffc0 +#define ENET_INIT_PARAM_SNUM_MASK 0xff000000 +#define ENET_INIT_PARAM_SNUM_SHIFT 24 + +#define ENET_INIT_PARAM_MAGIC_RES_INIT0 0x06 +#define ENET_INIT_PARAM_MAGIC_RES_INIT1 0x30 +#define ENET_INIT_PARAM_MAGIC_RES_INIT2 0xff +#define ENET_INIT_PARAM_MAGIC_RES_INIT3 0x00 +#define ENET_INIT_PARAM_MAGIC_RES_INIT4 0x0400 + +/* structure representing 82xx Address Filtering Enet Address in PRAM +*/ +typedef struct uec_82xx_enet_address { + u8 res1[0x2]; + u16 h; /* address (MSB) */ + u16 m; /* address */ + u16 l; /* address (LSB) */ +} __attribute__ ((packed)) uec_82xx_enet_address_t; + +/* structure representing 82xx Address Filtering PRAM +*/ +typedef struct uec_82xx_address_filtering_pram { + u32 iaddr_h; /* individual address filter, high */ + u32 iaddr_l; /* individual address filter, low */ + u32 gaddr_h; /* group address filter, high */ + u32 gaddr_l; /* group address filter, low */ + uec_82xx_enet_address_t taddr; + uec_82xx_enet_address_t paddr[4]; + u8 res0[0x40-0x38]; +} __attribute__ ((packed)) uec_82xx_address_filtering_pram_t; + +/* Buffer Descriptor +*/ +typedef struct buffer_descriptor { + u16 status; + u16 len; + u32 data; +} __attribute__ ((packed)) qe_bd_t, *p_bd_t; + +#define SIZEOFBD sizeof(qe_bd_t) + +/* Common BD flags +*/ +#define BD_WRAP 0x2000 +#define BD_INT 0x1000 +#define BD_LAST 0x0800 +#define BD_CLEAN 0x3000 + +/* TxBD status flags +*/ +#define TxBD_READY 0x8000 +#define TxBD_PADCRC 0x4000 +#define TxBD_WRAP BD_WRAP +#define TxBD_INT BD_INT +#define TxBD_LAST BD_LAST +#define TxBD_TXCRC 0x0400 +#define TxBD_DEF 0x0200 +#define TxBD_PP 0x0100 +#define TxBD_LC 0x0080 +#define TxBD_RL 0x0040 +#define TxBD_RC 0x003C +#define TxBD_UNDERRUN 0x0002 +#define TxBD_TRUNC 0x0001 + +#define TxBD_ERROR (TxBD_UNDERRUN | TxBD_TRUNC) + +/* RxBD status flags +*/ +#define RxBD_EMPTY 0x8000 +#define RxBD_OWNER 0x4000 +#define RxBD_WRAP BD_WRAP +#define RxBD_INT BD_INT +#define RxBD_LAST BD_LAST +#define RxBD_FIRST 0x0400 +#define RxBD_CMR 0x0200 +#define RxBD_MISS 0x0100 +#define RxBD_BCAST 0x0080 +#define RxBD_MCAST 0x0040 +#define RxBD_LG 0x0020 +#define RxBD_NO 0x0010 +#define RxBD_SHORT 0x0008 +#define RxBD_CRCERR 0x0004 +#define RxBD_OVERRUN 0x0002 +#define RxBD_IPCH 0x0001 + +#define RxBD_ERROR (RxBD_LG | RxBD_NO | RxBD_SHORT | \ + RxBD_CRCERR | RxBD_OVERRUN) + +/* BD access macros +*/ +#define BD_STATUS(_bd) (((p_bd_t)(_bd))->status) +#define BD_STATUS_SET(_bd, _val) (((p_bd_t)(_bd))->status = _val) +#define BD_LENGTH(_bd) (((p_bd_t)(_bd))->len) +#define BD_LENGTH_SET(_bd, _val) (((p_bd_t)(_bd))->len = _val) +#define BD_DATA_CLEAR(_bd) (((p_bd_t)(_bd))->data = 0) +#define BD_IS_DATA(_bd) (((p_bd_t)(_bd))->data) +#define BD_DATA(_bd) ((u8 *)(((p_bd_t)(_bd))->data)) +#define BD_DATA_SET(_bd, _data) (((p_bd_t)(_bd))->data = (u32)(_data)) +#define BD_ADVANCE(_bd,_status,_base) \ + (((_status) & BD_WRAP) ? (_bd) = ((p_bd_t)(_base)) : ++(_bd)) + +/* Rx Prefetched BDs +*/ +typedef struct uec_rx_prefetched_bds { + qe_bd_t bd[MAX_PREFETCHED_BDS]; /* prefetched bd */ +} __attribute__ ((packed)) uec_rx_prefetched_bds_t; + +/* Alignments + */ +#define UEC_RX_GLOBAL_PRAM_ALIGNMENT 64 +#define UEC_TX_GLOBAL_PRAM_ALIGNMENT 64 +#define UEC_THREAD_RX_PRAM_ALIGNMENT 128 +#define UEC_THREAD_TX_PRAM_ALIGNMENT 64 +#define UEC_THREAD_DATA_ALIGNMENT 256 +#define UEC_SEND_QUEUE_QUEUE_DESCRIPTOR_ALIGNMENT 32 +#define UEC_SCHEDULER_ALIGNMENT 4 +#define UEC_TX_STATISTICS_ALIGNMENT 4 +#define UEC_RX_STATISTICS_ALIGNMENT 4 +#define UEC_RX_INTERRUPT_COALESCING_ALIGNMENT 4 +#define UEC_RX_BD_QUEUES_ALIGNMENT 8 +#define UEC_RX_PREFETCHED_BDS_ALIGNMENT 128 +#define UEC_RX_EXTENDED_FILTERING_GLOBAL_PARAMETERS_ALIGNMENT 4 +#define UEC_RX_BD_RING_ALIGNMENT 32 +#define UEC_TX_BD_RING_ALIGNMENT 32 +#define UEC_MRBLR_ALIGNMENT 128 +#define UEC_RX_BD_RING_SIZE_ALIGNMENT 4 +#define UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT 32 +#define UEC_RX_DATA_BUF_ALIGNMENT 64 + +#define UEC_VLAN_PRIORITY_MAX 8 +#define UEC_IP_PRIORITY_MAX 64 +#define UEC_TX_VTAG_TABLE_ENTRY_MAX 8 +#define UEC_RX_BD_RING_SIZE_MIN 8 +#define UEC_TX_BD_RING_SIZE_MIN 2 + +/* Ethernet speed +*/ +typedef enum enet_speed { + ENET_SPEED_10BT, /* 10 Base T */ + ENET_SPEED_100BT, /* 100 Base T */ + ENET_SPEED_1000BT /* 1000 Base T */ +} enet_speed_e; + +/* Ethernet Address Type. +*/ +typedef enum enet_addr_type { + ENET_ADDR_TYPE_INDIVIDUAL, + ENET_ADDR_TYPE_GROUP, + ENET_ADDR_TYPE_BROADCAST +} enet_addr_type_e; + +/* TBI / MII Set Register +*/ +typedef enum enet_tbi_mii_reg { + ENET_TBI_MII_CR = 0x00, + ENET_TBI_MII_SR = 0x01, + ENET_TBI_MII_ANA = 0x04, + ENET_TBI_MII_ANLPBPA = 0x05, + ENET_TBI_MII_ANEX = 0x06, + ENET_TBI_MII_ANNPT = 0x07, + ENET_TBI_MII_ANLPANP = 0x08, + ENET_TBI_MII_EXST = 0x0F, + ENET_TBI_MII_JD = 0x10, + ENET_TBI_MII_TBICON = 0x11 +} enet_tbi_mii_reg_e; + +/* TBI MDIO register bit fields*/ +#define TBICON_CLK_SELECT 0x0020 +#define TBIANA_ASYMMETRIC_PAUSE 0x0100 +#define TBIANA_SYMMETRIC_PAUSE 0x0080 +#define TBIANA_HALF_DUPLEX 0x0040 +#define TBIANA_FULL_DUPLEX 0x0020 +#define TBICR_PHY_RESET 0x8000 +#define TBICR_ANEG_ENABLE 0x1000 +#define TBICR_RESTART_ANEG 0x0200 +#define TBICR_FULL_DUPLEX 0x0100 +#define TBICR_SPEED1_SET 0x0040 + +#define TBIANA_SETTINGS ( \ + TBIANA_ASYMMETRIC_PAUSE \ + | TBIANA_SYMMETRIC_PAUSE \ + | TBIANA_FULL_DUPLEX \ + ) + +#define TBICR_SETTINGS ( \ + TBICR_PHY_RESET \ + | TBICR_ANEG_ENABLE \ + | TBICR_FULL_DUPLEX \ + | TBICR_SPEED1_SET \ + ) + +/* UEC number of threads +*/ +typedef enum uec_num_of_threads { + UEC_NUM_OF_THREADS_1 = 0x1, /* 1 */ + UEC_NUM_OF_THREADS_2 = 0x2, /* 2 */ + UEC_NUM_OF_THREADS_4 = 0x0, /* 4 */ + UEC_NUM_OF_THREADS_6 = 0x3, /* 6 */ + UEC_NUM_OF_THREADS_8 = 0x4 /* 8 */ +} uec_num_of_threads_e; + +/* UEC initialization info struct +*/ +#define STD_UEC_INFO(num) \ +{ \ + .uf_info = { \ + .ucc_num = CONFIG_SYS_UEC##num##_UCC_NUM,\ + .rx_clock = CONFIG_SYS_UEC##num##_RX_CLK, \ + .tx_clock = CONFIG_SYS_UEC##num##_TX_CLK, \ + .eth_type = CONFIG_SYS_UEC##num##_ETH_TYPE,\ + }, \ + .num_threads_tx = UEC_NUM_OF_THREADS_1, \ + .num_threads_rx = UEC_NUM_OF_THREADS_1, \ + .risc_tx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, \ + .risc_rx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, \ + .tx_bd_ring_len = 16, \ + .rx_bd_ring_len = 16, \ + .phy_address = CONFIG_SYS_UEC##num##_PHY_ADDR, \ + .enet_interface_type = CONFIG_SYS_UEC##num##_INTERFACE_TYPE, \ + .speed = CONFIG_SYS_UEC##num##_INTERFACE_SPEED, \ +} + +typedef struct uec_info { + ucc_fast_info_t uf_info; + uec_num_of_threads_e num_threads_tx; + uec_num_of_threads_e num_threads_rx; + unsigned int risc_tx; + unsigned int risc_rx; + u16 rx_bd_ring_len; + u16 tx_bd_ring_len; + u8 phy_address; + phy_interface_t enet_interface_type; + int speed; +} uec_info_t; + +/* UEC driver initialized info +*/ +#define MAX_RXBUF_LEN 1536 +#define MAX_FRAME_LEN 1518 +#define MIN_FRAME_LEN 64 +#define MAX_DMA1_LEN 1520 +#define MAX_DMA2_LEN 1520 + +/* UEC driver private struct +*/ +typedef struct uec_private { + uec_info_t *uec_info; + ucc_fast_private_t *uccf; + struct eth_device *dev; + uec_t *uec_regs; + uec_mii_t *uec_mii_regs; + /* enet init command parameter */ + uec_init_cmd_pram_t *p_init_enet_param; + u32 init_enet_param_offset; + /* Rx and Tx paramter */ + uec_rx_global_pram_t *p_rx_glbl_pram; + u32 rx_glbl_pram_offset; + uec_tx_global_pram_t *p_tx_glbl_pram; + u32 tx_glbl_pram_offset; + uec_send_queue_mem_region_t *p_send_q_mem_reg; + u32 send_q_mem_reg_offset; + uec_thread_data_tx_t *p_thread_data_tx; + u32 thread_dat_tx_offset; + uec_thread_data_rx_t *p_thread_data_rx; + u32 thread_dat_rx_offset; + uec_rx_bd_queues_entry_t *p_rx_bd_qs_tbl; + u32 rx_bd_qs_tbl_offset; + /* BDs specific */ + u8 *p_tx_bd_ring; + u32 tx_bd_ring_offset; + u8 *p_rx_bd_ring; + u32 rx_bd_ring_offset; + u8 *p_rx_buf; + u32 rx_buf_offset; + volatile qe_bd_t *txBd; + volatile qe_bd_t *rxBd; + /* Status */ + int mac_tx_enabled; + int mac_rx_enabled; + int grace_stopped_tx; + int grace_stopped_rx; + int the_first_run; + /* PHY specific */ + struct uec_mii_info *mii_info; + int oldspeed; + int oldduplex; + int oldlink; +} uec_private_t; + +int uec_initialize(bd_t *bis, uec_info_t *uec_info); +int uec_eth_init(bd_t *bis, uec_info_t *uecs, int num); +int uec_standard_init(bd_t *bis); +#endif /* __UEC_H__ */ diff --git a/qemu/roms/u-boot/drivers/qe/uec_phy.c b/qemu/roms/u-boot/drivers/qe/uec_phy.c new file mode 100644 index 000000000..5dc4641bf --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/uec_phy.c @@ -0,0 +1,912 @@ +/* + * Copyright (C) 2005,2010-2011 Freescale Semiconductor, Inc. + * + * Author: Shlomi Gridish + * + * Description: UCC GETH Driver -- PHY handling + * Driver for UEC on QE + * Based on 8260_io/fcc_enet.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "common.h" +#include "net.h" +#include "malloc.h" +#include "asm/errno.h" +#include "asm/immap_qe.h" +#include "asm/io.h" +#include "qe.h" +#include "uccf.h" +#include "uec.h" +#include "uec_phy.h" +#include "miiphy.h" +#include + +#define ugphy_printk(format, arg...) \ + printf(format "\n", ## arg) + +#define ugphy_dbg(format, arg...) \ + ugphy_printk(format , ## arg) +#define ugphy_err(format, arg...) \ + ugphy_printk(format , ## arg) +#define ugphy_info(format, arg...) \ + ugphy_printk(format , ## arg) +#define ugphy_warn(format, arg...) \ + ugphy_printk(format , ## arg) + +#ifdef UEC_VERBOSE_DEBUG +#define ugphy_vdbg ugphy_dbg +#else +#define ugphy_vdbg(ugeth, fmt, args...) do { } while (0) +#endif /* UEC_VERBOSE_DEBUG */ + +/*--------------------------------------------------------------------+ + * Fixed PHY (PHY-less) support for Ethernet Ports. + * + * Copied from arch/powerpc/cpu/ppc4xx/4xx_enet.c + *--------------------------------------------------------------------*/ + +/* + * Some boards do not have a PHY for each ethernet port. These ports are known + * as Fixed PHY (or PHY-less) ports. For such ports, set the appropriate + * CONFIG_SYS_UECx_PHY_ADDR equal to CONFIG_FIXED_PHY_ADDR (an unused address) + * When the drver tries to identify the PHYs, CONFIG_FIXED_PHY will be returned + * and the driver will search CONFIG_SYS_FIXED_PHY_PORTS to find what network + * speed and duplex should be for the port. + * + * Example board header configuration file: + * #define CONFIG_FIXED_PHY 0xFFFFFFFF + * #define CONFIG_SYS_FIXED_PHY_ADDR 0x1E (pick an unused phy address) + * + * #define CONFIG_SYS_UEC1_PHY_ADDR CONFIG_SYS_FIXED_PHY_ADDR + * #define CONFIG_SYS_UEC2_PHY_ADDR 0x02 + * #define CONFIG_SYS_UEC3_PHY_ADDR CONFIG_SYS_FIXED_PHY_ADDR + * #define CONFIG_SYS_UEC4_PHY_ADDR 0x04 + * + * #define CONFIG_SYS_FIXED_PHY_PORT(name,speed,duplex) \ + * {name, speed, duplex}, + * + * #define CONFIG_SYS_FIXED_PHY_PORTS \ + * CONFIG_SYS_FIXED_PHY_PORT("UEC0",SPEED_100,DUPLEX_FULL) \ + * CONFIG_SYS_FIXED_PHY_PORT("UEC2",SPEED_100,DUPLEX_HALF) + */ + +#ifndef CONFIG_FIXED_PHY +#define CONFIG_FIXED_PHY 0xFFFFFFFF /* Fixed PHY (PHY-less) */ +#endif + +#ifndef CONFIG_SYS_FIXED_PHY_PORTS +#define CONFIG_SYS_FIXED_PHY_PORTS /* default is an empty array */ +#endif + +struct fixed_phy_port { + char name[16]; /* ethernet port name */ + unsigned int speed; /* specified speed 10,100 or 1000 */ + unsigned int duplex; /* specified duplex FULL or HALF */ +}; + +static const struct fixed_phy_port fixed_phy_port[] = { + CONFIG_SYS_FIXED_PHY_PORTS /* defined in board configuration file */ +}; + +/*--------------------------------------------------------------------+ + * BitBang MII support for ethernet ports + * + * Based from MPC8560ADS implementation + *--------------------------------------------------------------------*/ +/* + * Example board header file to define bitbang ethernet ports: + * + * #define CONFIG_SYS_BITBANG_PHY_PORT(name) name, + * #define CONFIG_SYS_BITBANG_PHY_PORTS CONFIG_SYS_BITBANG_PHY_PORT("UEC0") +*/ +#ifndef CONFIG_SYS_BITBANG_PHY_PORTS +#define CONFIG_SYS_BITBANG_PHY_PORTS /* default is an empty array */ +#endif + +#if defined(CONFIG_BITBANGMII) +static const char *bitbang_phy_port[] = { + CONFIG_SYS_BITBANG_PHY_PORTS /* defined in board configuration file */ +}; +#endif /* CONFIG_BITBANGMII */ + +static void config_genmii_advert (struct uec_mii_info *mii_info); +static void genmii_setup_forced (struct uec_mii_info *mii_info); +static void genmii_restart_aneg (struct uec_mii_info *mii_info); +static int gbit_config_aneg (struct uec_mii_info *mii_info); +static int genmii_config_aneg (struct uec_mii_info *mii_info); +static int genmii_update_link (struct uec_mii_info *mii_info); +static int genmii_read_status (struct uec_mii_info *mii_info); +u16 uec_phy_read(struct uec_mii_info *mii_info, u16 regnum); +void uec_phy_write(struct uec_mii_info *mii_info, u16 regnum, u16 val); + +/* Write value to the PHY for this device to the register at regnum, */ +/* waiting until the write is done before it returns. All PHY */ +/* configuration has to be done through the TSEC1 MIIM regs */ +void uec_write_phy_reg (struct eth_device *dev, int mii_id, int regnum, int value) +{ + uec_private_t *ugeth = (uec_private_t *) dev->priv; + uec_mii_t *ug_regs; + enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e) regnum; + u32 tmp_reg; + + +#if defined(CONFIG_BITBANGMII) + u32 i = 0; + + for (i = 0; i < ARRAY_SIZE(bitbang_phy_port); i++) { + if (strncmp(dev->name, bitbang_phy_port[i], + sizeof(dev->name)) == 0) { + (void)bb_miiphy_write(NULL, mii_id, regnum, value); + return; + } + } +#endif /* CONFIG_BITBANGMII */ + + ug_regs = ugeth->uec_mii_regs; + + /* Stop the MII management read cycle */ + out_be32 (&ug_regs->miimcom, 0); + /* Setting up the MII Mangement Address Register */ + tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; + out_be32 (&ug_regs->miimadd, tmp_reg); + + /* Setting up the MII Mangement Control Register with the value */ + out_be32 (&ug_regs->miimcon, (u32) value); + sync(); + + /* Wait till MII management write is complete */ + while ((in_be32 (&ug_regs->miimind)) & MIIMIND_BUSY); +} + +/* Reads from register regnum in the PHY for device dev, */ +/* returning the value. Clears miimcom first. All PHY */ +/* configuration has to be done through the TSEC1 MIIM regs */ +int uec_read_phy_reg (struct eth_device *dev, int mii_id, int regnum) +{ + uec_private_t *ugeth = (uec_private_t *) dev->priv; + uec_mii_t *ug_regs; + enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e) regnum; + u32 tmp_reg; + u16 value; + + +#if defined(CONFIG_BITBANGMII) + u32 i = 0; + + for (i = 0; i < ARRAY_SIZE(bitbang_phy_port); i++) { + if (strncmp(dev->name, bitbang_phy_port[i], + sizeof(dev->name)) == 0) { + (void)bb_miiphy_read(NULL, mii_id, regnum, &value); + return (value); + } + } +#endif /* CONFIG_BITBANGMII */ + + ug_regs = ugeth->uec_mii_regs; + + /* Setting up the MII Mangement Address Register */ + tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; + out_be32 (&ug_regs->miimadd, tmp_reg); + + /* clear MII management command cycle */ + out_be32 (&ug_regs->miimcom, 0); + sync(); + + /* Perform an MII management read cycle */ + out_be32 (&ug_regs->miimcom, MIIMCOM_READ_CYCLE); + + /* Wait till MII management write is complete */ + while ((in_be32 (&ug_regs->miimind)) & + (MIIMIND_NOT_VALID | MIIMIND_BUSY)); + + /* Read MII management status */ + value = (u16) in_be32 (&ug_regs->miimstat); + if (value == 0xffff) + ugphy_vdbg + ("read wrong value : mii_id %d,mii_reg %d, base %08x", + mii_id, mii_reg, (u32) & (ug_regs->miimcfg)); + + return (value); +} + +void mii_clear_phy_interrupt (struct uec_mii_info *mii_info) +{ + if (mii_info->phyinfo->ack_interrupt) + mii_info->phyinfo->ack_interrupt (mii_info); +} + +void mii_configure_phy_interrupt (struct uec_mii_info *mii_info, + u32 interrupts) +{ + mii_info->interrupts = interrupts; + if (mii_info->phyinfo->config_intr) + mii_info->phyinfo->config_intr (mii_info); +} + +/* Writes MII_ADVERTISE with the appropriate values, after + * sanitizing advertise to make sure only supported features + * are advertised + */ +static void config_genmii_advert (struct uec_mii_info *mii_info) +{ + u32 advertise; + u16 adv; + + /* Only allow advertising what this PHY supports */ + mii_info->advertising &= mii_info->phyinfo->features; + advertise = mii_info->advertising; + + /* Setup standard advertisement */ + adv = uec_phy_read(mii_info, MII_ADVERTISE); + adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); + if (advertise & ADVERTISED_10baseT_Half) + adv |= ADVERTISE_10HALF; + if (advertise & ADVERTISED_10baseT_Full) + adv |= ADVERTISE_10FULL; + if (advertise & ADVERTISED_100baseT_Half) + adv |= ADVERTISE_100HALF; + if (advertise & ADVERTISED_100baseT_Full) + adv |= ADVERTISE_100FULL; + uec_phy_write(mii_info, MII_ADVERTISE, adv); +} + +static void genmii_setup_forced (struct uec_mii_info *mii_info) +{ + u16 ctrl; + u32 features = mii_info->phyinfo->features; + + ctrl = uec_phy_read(mii_info, MII_BMCR); + + ctrl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | + BMCR_SPEED1000 | BMCR_ANENABLE); + ctrl |= BMCR_RESET; + + switch (mii_info->speed) { + case SPEED_1000: + if (features & (SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full)) { + ctrl |= BMCR_SPEED1000; + break; + } + mii_info->speed = SPEED_100; + case SPEED_100: + if (features & (SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full)) { + ctrl |= BMCR_SPEED100; + break; + } + mii_info->speed = SPEED_10; + case SPEED_10: + if (features & (SUPPORTED_10baseT_Half + | SUPPORTED_10baseT_Full)) + break; + default: /* Unsupported speed! */ + ugphy_err ("%s: Bad speed!", mii_info->dev->name); + break; + } + + uec_phy_write(mii_info, MII_BMCR, ctrl); +} + +/* Enable and Restart Autonegotiation */ +static void genmii_restart_aneg (struct uec_mii_info *mii_info) +{ + u16 ctl; + + ctl = uec_phy_read(mii_info, MII_BMCR); + ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); + uec_phy_write(mii_info, MII_BMCR, ctl); +} + +static int gbit_config_aneg (struct uec_mii_info *mii_info) +{ + u16 adv; + u32 advertise; + + if (mii_info->autoneg) { + /* Configure the ADVERTISE register */ + config_genmii_advert (mii_info); + advertise = mii_info->advertising; + + adv = uec_phy_read(mii_info, MII_CTRL1000); + adv &= ~(ADVERTISE_1000FULL | + ADVERTISE_1000HALF); + if (advertise & SUPPORTED_1000baseT_Half) + adv |= ADVERTISE_1000HALF; + if (advertise & SUPPORTED_1000baseT_Full) + adv |= ADVERTISE_1000FULL; + uec_phy_write(mii_info, MII_CTRL1000, adv); + + /* Start/Restart aneg */ + genmii_restart_aneg (mii_info); + } else + genmii_setup_forced (mii_info); + + return 0; +} + +static int marvell_config_aneg (struct uec_mii_info *mii_info) +{ + /* The Marvell PHY has an errata which requires + * that certain registers get written in order + * to restart autonegotiation */ + uec_phy_write(mii_info, MII_BMCR, BMCR_RESET); + + uec_phy_write(mii_info, 0x1d, 0x1f); + uec_phy_write(mii_info, 0x1e, 0x200c); + uec_phy_write(mii_info, 0x1d, 0x5); + uec_phy_write(mii_info, 0x1e, 0); + uec_phy_write(mii_info, 0x1e, 0x100); + + gbit_config_aneg (mii_info); + + return 0; +} + +static int genmii_config_aneg (struct uec_mii_info *mii_info) +{ + if (mii_info->autoneg) { + /* Speed up the common case, if link is already up, speed and + duplex match, skip auto neg as it already matches */ + if (!genmii_read_status(mii_info) && mii_info->link) + if (mii_info->duplex == DUPLEX_FULL && + mii_info->speed == SPEED_100) + if (mii_info->advertising & + ADVERTISED_100baseT_Full) + return 0; + + config_genmii_advert (mii_info); + genmii_restart_aneg (mii_info); + } else + genmii_setup_forced (mii_info); + + return 0; +} + +static int genmii_update_link (struct uec_mii_info *mii_info) +{ + u16 status; + + /* Status is read once to clear old link state */ + uec_phy_read(mii_info, MII_BMSR); + + /* + * Wait if the link is up, and autonegotiation is in progress + * (ie - we're capable and it's not done) + */ + status = uec_phy_read(mii_info, MII_BMSR); + if ((status & BMSR_LSTATUS) && (status & BMSR_ANEGCAPABLE) + && !(status & BMSR_ANEGCOMPLETE)) { + int i = 0; + + while (!(status & BMSR_ANEGCOMPLETE)) { + /* + * Timeout reached ? + */ + if (i > UGETH_AN_TIMEOUT) { + mii_info->link = 0; + return 0; + } + + i++; + udelay(1000); /* 1 ms */ + status = uec_phy_read(mii_info, MII_BMSR); + } + mii_info->link = 1; + } else { + if (status & BMSR_LSTATUS) + mii_info->link = 1; + else + mii_info->link = 0; + } + + return 0; +} + +static int genmii_read_status (struct uec_mii_info *mii_info) +{ + u16 status; + int err; + + /* Update the link, but return if there + * was an error */ + err = genmii_update_link (mii_info); + if (err) + return err; + + if (mii_info->autoneg) { + status = uec_phy_read(mii_info, MII_STAT1000); + + if (status & (LPA_1000FULL | LPA_1000HALF)) { + mii_info->speed = SPEED_1000; + if (status & LPA_1000FULL) + mii_info->duplex = DUPLEX_FULL; + else + mii_info->duplex = DUPLEX_HALF; + } else { + status = uec_phy_read(mii_info, MII_LPA); + + if (status & (LPA_10FULL | LPA_100FULL)) + mii_info->duplex = DUPLEX_FULL; + else + mii_info->duplex = DUPLEX_HALF; + if (status & (LPA_100FULL | LPA_100HALF)) + mii_info->speed = SPEED_100; + else + mii_info->speed = SPEED_10; + } + mii_info->pause = 0; + } + /* On non-aneg, we assume what we put in BMCR is the speed, + * though magic-aneg shouldn't prevent this case from occurring + */ + + return 0; +} + +static int bcm_init(struct uec_mii_info *mii_info) +{ + struct eth_device *edev = mii_info->dev; + uec_private_t *uec = edev->priv; + + gbit_config_aneg(mii_info); + + if ((uec->uec_info->enet_interface_type == + PHY_INTERFACE_MODE_RGMII_RXID) && + (uec->uec_info->speed == SPEED_1000)) { + u16 val; + int cnt = 50; + + /* Wait for aneg to complete. */ + do + val = uec_phy_read(mii_info, MII_BMSR); + while (--cnt && !(val & BMSR_ANEGCOMPLETE)); + + /* Set RDX clk delay. */ + uec_phy_write(mii_info, 0x18, 0x7 | (7 << 12)); + + val = uec_phy_read(mii_info, 0x18); + /* Set RDX-RXC skew. */ + val |= (1 << 8); + val |= (7 | (7 << 12)); + /* Write bits 14:0. */ + val |= (1 << 15); + uec_phy_write(mii_info, 0x18, val); + } + + return 0; +} + +static int uec_marvell_init(struct uec_mii_info *mii_info) +{ + struct eth_device *edev = mii_info->dev; + uec_private_t *uec = edev->priv; + phy_interface_t iface = uec->uec_info->enet_interface_type; + int speed = uec->uec_info->speed; + + if ((speed == SPEED_1000) && + (iface == PHY_INTERFACE_MODE_RGMII_ID || + iface == PHY_INTERFACE_MODE_RGMII_RXID || + iface == PHY_INTERFACE_MODE_RGMII_TXID)) { + int temp; + + temp = uec_phy_read(mii_info, MII_M1111_PHY_EXT_CR); + if (iface == PHY_INTERFACE_MODE_RGMII_ID) { + temp |= MII_M1111_RX_DELAY | MII_M1111_TX_DELAY; + } else if (iface == PHY_INTERFACE_MODE_RGMII_RXID) { + temp &= ~MII_M1111_TX_DELAY; + temp |= MII_M1111_RX_DELAY; + } else if (iface == PHY_INTERFACE_MODE_RGMII_TXID) { + temp &= ~MII_M1111_RX_DELAY; + temp |= MII_M1111_TX_DELAY; + } + uec_phy_write(mii_info, MII_M1111_PHY_EXT_CR, temp); + + temp = uec_phy_read(mii_info, MII_M1111_PHY_EXT_SR); + temp &= ~MII_M1111_HWCFG_MODE_MASK; + temp |= MII_M1111_HWCFG_MODE_RGMII; + uec_phy_write(mii_info, MII_M1111_PHY_EXT_SR, temp); + + uec_phy_write(mii_info, MII_BMCR, BMCR_RESET); + } + + return 0; +} + +static int marvell_read_status (struct uec_mii_info *mii_info) +{ + u16 status; + int err; + + /* Update the link, but return if there + * was an error */ + err = genmii_update_link (mii_info); + if (err) + return err; + + /* If the link is up, read the speed and duplex */ + /* If we aren't autonegotiating, assume speeds + * are as set */ + if (mii_info->autoneg && mii_info->link) { + int speed; + + status = uec_phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS); + + /* Get the duplexity */ + if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX) + mii_info->duplex = DUPLEX_FULL; + else + mii_info->duplex = DUPLEX_HALF; + + /* Get the speed */ + speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK; + switch (speed) { + case MII_M1011_PHY_SPEC_STATUS_1000: + mii_info->speed = SPEED_1000; + break; + case MII_M1011_PHY_SPEC_STATUS_100: + mii_info->speed = SPEED_100; + break; + default: + mii_info->speed = SPEED_10; + break; + } + mii_info->pause = 0; + } + + return 0; +} + +static int marvell_ack_interrupt (struct uec_mii_info *mii_info) +{ + /* Clear the interrupts by reading the reg */ + uec_phy_read(mii_info, MII_M1011_IEVENT); + + return 0; +} + +static int marvell_config_intr (struct uec_mii_info *mii_info) +{ + if (mii_info->interrupts == MII_INTERRUPT_ENABLED) + uec_phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT); + else + uec_phy_write(mii_info, MII_M1011_IMASK, + MII_M1011_IMASK_CLEAR); + + return 0; +} + +static int dm9161_init (struct uec_mii_info *mii_info) +{ + /* Reset the PHY */ + uec_phy_write(mii_info, MII_BMCR, uec_phy_read(mii_info, MII_BMCR) | + BMCR_RESET); + /* PHY and MAC connect */ + uec_phy_write(mii_info, MII_BMCR, uec_phy_read(mii_info, MII_BMCR) & + ~BMCR_ISOLATE); + + uec_phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT); + + config_genmii_advert (mii_info); + /* Start/restart aneg */ + genmii_config_aneg (mii_info); + + return 0; +} + +static int dm9161_config_aneg (struct uec_mii_info *mii_info) +{ + return 0; +} + +static int dm9161_read_status (struct uec_mii_info *mii_info) +{ + u16 status; + int err; + + /* Update the link, but return if there was an error */ + err = genmii_update_link (mii_info); + if (err) + return err; + /* If the link is up, read the speed and duplex + If we aren't autonegotiating assume speeds are as set */ + if (mii_info->autoneg && mii_info->link) { + status = uec_phy_read(mii_info, MII_DM9161_SCSR); + if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H)) + mii_info->speed = SPEED_100; + else + mii_info->speed = SPEED_10; + + if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F)) + mii_info->duplex = DUPLEX_FULL; + else + mii_info->duplex = DUPLEX_HALF; + } + + return 0; +} + +static int dm9161_ack_interrupt (struct uec_mii_info *mii_info) +{ + /* Clear the interrupt by reading the reg */ + uec_phy_read(mii_info, MII_DM9161_INTR); + + return 0; +} + +static int dm9161_config_intr (struct uec_mii_info *mii_info) +{ + if (mii_info->interrupts == MII_INTERRUPT_ENABLED) + uec_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_INIT); + else + uec_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_STOP); + + return 0; +} + +static void dm9161_close (struct uec_mii_info *mii_info) +{ +} + +static int fixed_phy_aneg (struct uec_mii_info *mii_info) +{ + mii_info->autoneg = 0; /* Turn off auto negotiation for fixed phy */ + return 0; +} + +static int fixed_phy_read_status (struct uec_mii_info *mii_info) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(fixed_phy_port); i++) { + if (strncmp(mii_info->dev->name, fixed_phy_port[i].name, + strlen(mii_info->dev->name)) == 0) { + mii_info->speed = fixed_phy_port[i].speed; + mii_info->duplex = fixed_phy_port[i].duplex; + mii_info->link = 1; /* Link is always UP */ + mii_info->pause = 0; + break; + } + } + return 0; +} + +static int smsc_config_aneg (struct uec_mii_info *mii_info) +{ + return 0; +} + +static int smsc_read_status (struct uec_mii_info *mii_info) +{ + u16 status; + int err; + + /* Update the link, but return if there + * was an error */ + err = genmii_update_link (mii_info); + if (err) + return err; + + /* If the link is up, read the speed and duplex */ + /* If we aren't autonegotiating, assume speeds + * are as set */ + if (mii_info->autoneg && mii_info->link) { + int val; + + status = uec_phy_read(mii_info, 0x1f); + val = (status & 0x1c) >> 2; + + switch (val) { + case 1: + mii_info->duplex = DUPLEX_HALF; + mii_info->speed = SPEED_10; + break; + case 5: + mii_info->duplex = DUPLEX_FULL; + mii_info->speed = SPEED_10; + break; + case 2: + mii_info->duplex = DUPLEX_HALF; + mii_info->speed = SPEED_100; + break; + case 6: + mii_info->duplex = DUPLEX_FULL; + mii_info->speed = SPEED_100; + break; + } + mii_info->pause = 0; + } + + return 0; +} + +static struct phy_info phy_info_dm9161 = { + .phy_id = 0x0181b880, + .phy_id_mask = 0x0ffffff0, + .name = "Davicom DM9161E", + .init = dm9161_init, + .config_aneg = dm9161_config_aneg, + .read_status = dm9161_read_status, + .close = dm9161_close, +}; + +static struct phy_info phy_info_dm9161a = { + .phy_id = 0x0181b8a0, + .phy_id_mask = 0x0ffffff0, + .name = "Davicom DM9161A", + .features = MII_BASIC_FEATURES, + .init = dm9161_init, + .config_aneg = dm9161_config_aneg, + .read_status = dm9161_read_status, + .ack_interrupt = dm9161_ack_interrupt, + .config_intr = dm9161_config_intr, + .close = dm9161_close, +}; + +static struct phy_info phy_info_marvell = { + .phy_id = 0x01410c00, + .phy_id_mask = 0xffffff00, + .name = "Marvell 88E11x1", + .features = MII_GBIT_FEATURES, + .init = &uec_marvell_init, + .config_aneg = &marvell_config_aneg, + .read_status = &marvell_read_status, + .ack_interrupt = &marvell_ack_interrupt, + .config_intr = &marvell_config_intr, +}; + +static struct phy_info phy_info_bcm5481 = { + .phy_id = 0x0143bca0, + .phy_id_mask = 0xffffff0, + .name = "Broadcom 5481", + .features = MII_GBIT_FEATURES, + .read_status = genmii_read_status, + .init = bcm_init, +}; + +static struct phy_info phy_info_fixedphy = { + .phy_id = CONFIG_FIXED_PHY, + .phy_id_mask = CONFIG_FIXED_PHY, + .name = "Fixed PHY", + .config_aneg = fixed_phy_aneg, + .read_status = fixed_phy_read_status, +}; + +static struct phy_info phy_info_smsclan8700 = { + .phy_id = 0x0007c0c0, + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN8700", + .features = MII_BASIC_FEATURES, + .config_aneg = smsc_config_aneg, + .read_status = smsc_read_status, +}; + +static struct phy_info phy_info_genmii = { + .phy_id = 0x00000000, + .phy_id_mask = 0x00000000, + .name = "Generic MII", + .features = MII_BASIC_FEATURES, + .config_aneg = genmii_config_aneg, + .read_status = genmii_read_status, +}; + +static struct phy_info *phy_info[] = { + &phy_info_dm9161, + &phy_info_dm9161a, + &phy_info_marvell, + &phy_info_bcm5481, + &phy_info_smsclan8700, + &phy_info_fixedphy, + &phy_info_genmii, + NULL +}; + +u16 uec_phy_read(struct uec_mii_info *mii_info, u16 regnum) +{ + return mii_info->mdio_read (mii_info->dev, mii_info->mii_id, regnum); +} + +void uec_phy_write(struct uec_mii_info *mii_info, u16 regnum, u16 val) +{ + mii_info->mdio_write (mii_info->dev, mii_info->mii_id, regnum, val); +} + +/* Use the PHY ID registers to determine what type of PHY is attached + * to device dev. return a struct phy_info structure describing that PHY + */ +struct phy_info *uec_get_phy_info (struct uec_mii_info *mii_info) +{ + u16 phy_reg; + u32 phy_ID; + int i; + struct phy_info *theInfo = NULL; + + /* Grab the bits from PHYIR1, and put them in the upper half */ + phy_reg = uec_phy_read(mii_info, MII_PHYSID1); + phy_ID = (phy_reg & 0xffff) << 16; + + /* Grab the bits from PHYIR2, and put them in the lower half */ + phy_reg = uec_phy_read(mii_info, MII_PHYSID2); + phy_ID |= (phy_reg & 0xffff); + + /* loop through all the known PHY types, and find one that */ + /* matches the ID we read from the PHY. */ + for (i = 0; phy_info[i]; i++) + if (phy_info[i]->phy_id == + (phy_ID & phy_info[i]->phy_id_mask)) { + theInfo = phy_info[i]; + break; + } + + /* This shouldn't happen, as we have generic PHY support */ + if (theInfo == NULL) { + ugphy_info ("UEC: PHY id %x is not supported!", phy_ID); + return NULL; + } else { + ugphy_info ("UEC: PHY is %s (%x)", theInfo->name, phy_ID); + } + + return theInfo; +} + +void marvell_phy_interface_mode(struct eth_device *dev, phy_interface_t type, + int speed) +{ + uec_private_t *uec = (uec_private_t *) dev->priv; + struct uec_mii_info *mii_info; + u16 status; + + if (!uec->mii_info) { + printf ("%s: the PHY not initialized\n", __FUNCTION__); + return; + } + mii_info = uec->mii_info; + + if (type == PHY_INTERFACE_MODE_RGMII) { + if (speed == SPEED_100) { + uec_phy_write(mii_info, 0x00, 0x9140); + uec_phy_write(mii_info, 0x1d, 0x001f); + uec_phy_write(mii_info, 0x1e, 0x200c); + uec_phy_write(mii_info, 0x1d, 0x0005); + uec_phy_write(mii_info, 0x1e, 0x0000); + uec_phy_write(mii_info, 0x1e, 0x0100); + uec_phy_write(mii_info, 0x09, 0x0e00); + uec_phy_write(mii_info, 0x04, 0x01e1); + uec_phy_write(mii_info, 0x00, 0x9140); + uec_phy_write(mii_info, 0x00, 0x1000); + udelay (100000); + uec_phy_write(mii_info, 0x00, 0x2900); + uec_phy_write(mii_info, 0x14, 0x0cd2); + uec_phy_write(mii_info, 0x00, 0xa100); + uec_phy_write(mii_info, 0x09, 0x0000); + uec_phy_write(mii_info, 0x1b, 0x800b); + uec_phy_write(mii_info, 0x04, 0x05e1); + uec_phy_write(mii_info, 0x00, 0xa100); + uec_phy_write(mii_info, 0x00, 0x2100); + udelay (1000000); + } else if (speed == SPEED_10) { + uec_phy_write(mii_info, 0x14, 0x8e40); + uec_phy_write(mii_info, 0x1b, 0x800b); + uec_phy_write(mii_info, 0x14, 0x0c82); + uec_phy_write(mii_info, 0x00, 0x8100); + udelay (1000000); + } + } + + /* handle 88e1111 rev.B2 erratum 5.6 */ + if (mii_info->autoneg) { + status = uec_phy_read(mii_info, MII_BMCR); + uec_phy_write(mii_info, MII_BMCR, status | BMCR_ANENABLE); + } + /* now the B2 will correctly report autoneg completion status */ +} + +void change_phy_interface_mode (struct eth_device *dev, + phy_interface_t type, int speed) +{ +#ifdef CONFIG_PHY_MODE_NEED_CHANGE + marvell_phy_interface_mode (dev, type, speed); +#endif +} diff --git a/qemu/roms/u-boot/drivers/qe/uec_phy.h b/qemu/roms/u-boot/drivers/qe/uec_phy.h new file mode 100644 index 000000000..11cbc253b --- /dev/null +++ b/qemu/roms/u-boot/drivers/qe/uec_phy.h @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2005, 2011 Freescale Semiconductor, Inc. + * + * Author: Shlomi Gridish + * + * Description: UCC ethernet driver -- PHY handling + * Driver for UEC on QE + * Based on 8260_io/fcc_enet.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef __UEC_PHY_H__ +#define __UEC_PHY_H__ + +#define MII_end ((u32)-2) +#define MII_read ((u32)-1) + +#define MIIMIND_BUSY 0x00000001 +#define MIIMIND_NOTVALID 0x00000004 + +#define UGETH_AN_TIMEOUT 2000 + +/* Cicada Extended Control Register 1 */ +#define MII_CIS8201_EXT_CON1 0x17 +#define MII_CIS8201_EXTCON1_INIT 0x0000 + +/* Cicada Interrupt Mask Register */ +#define MII_CIS8201_IMASK 0x19 +#define MII_CIS8201_IMASK_IEN 0x8000 +#define MII_CIS8201_IMASK_SPEED 0x4000 +#define MII_CIS8201_IMASK_LINK 0x2000 +#define MII_CIS8201_IMASK_DUPLEX 0x1000 +#define MII_CIS8201_IMASK_MASK 0xf000 + +/* Cicada Interrupt Status Register */ +#define MII_CIS8201_ISTAT 0x1a +#define MII_CIS8201_ISTAT_STATUS 0x8000 +#define MII_CIS8201_ISTAT_SPEED 0x4000 +#define MII_CIS8201_ISTAT_LINK 0x2000 +#define MII_CIS8201_ISTAT_DUPLEX 0x1000 + +/* Cicada Auxiliary Control/Status Register */ +#define MII_CIS8201_AUX_CONSTAT 0x1c +#define MII_CIS8201_AUXCONSTAT_INIT 0x0004 +#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020 +#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018 +#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010 +#define MII_CIS8201_AUXCONSTAT_100 0x0008 + +/* 88E1011 PHY Status Register */ +#define MII_M1011_PHY_SPEC_STATUS 0x11 +#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000 +#define MII_M1011_PHY_SPEC_STATUS_100 0x4000 +#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000 +#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000 +#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800 +#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400 + +#define MII_M1011_IEVENT 0x13 +#define MII_M1011_IEVENT_CLEAR 0x0000 + +#define MII_M1011_IMASK 0x12 +#define MII_M1011_IMASK_INIT 0x6400 +#define MII_M1011_IMASK_CLEAR 0x0000 + +/* 88E1111 PHY Register */ +#define MII_M1111_PHY_EXT_CR 0x14 +#define MII_M1111_RX_DELAY 0x80 +#define MII_M1111_TX_DELAY 0x2 +#define MII_M1111_PHY_EXT_SR 0x1b +#define MII_M1111_HWCFG_MODE_MASK 0xf +#define MII_M1111_HWCFG_MODE_RGMII 0xb + +#define MII_DM9161_SCR 0x10 +#define MII_DM9161_SCR_INIT 0x0610 +#define MII_DM9161_SCR_RMII_INIT 0x0710 + +/* DM9161 Specified Configuration and Status Register */ +#define MII_DM9161_SCSR 0x11 +#define MII_DM9161_SCSR_100F 0x8000 +#define MII_DM9161_SCSR_100H 0x4000 +#define MII_DM9161_SCSR_10F 0x2000 +#define MII_DM9161_SCSR_10H 0x1000 + +/* DM9161 Interrupt Register */ +#define MII_DM9161_INTR 0x15 +#define MII_DM9161_INTR_PEND 0x8000 +#define MII_DM9161_INTR_DPLX_MASK 0x0800 +#define MII_DM9161_INTR_SPD_MASK 0x0400 +#define MII_DM9161_INTR_LINK_MASK 0x0200 +#define MII_DM9161_INTR_MASK 0x0100 +#define MII_DM9161_INTR_DPLX_CHANGE 0x0010 +#define MII_DM9161_INTR_SPD_CHANGE 0x0008 +#define MII_DM9161_INTR_LINK_CHANGE 0x0004 +#define MII_DM9161_INTR_INIT 0x0000 +#define MII_DM9161_INTR_STOP \ +(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \ + | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK) + +/* DM9161 10BT Configuration/Status */ +#define MII_DM9161_10BTCSR 0x12 +#define MII_DM9161_10BTCSR_INIT 0x7800 + +#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \ + SUPPORTED_10baseT_Full | \ + SUPPORTED_100baseT_Half | \ + SUPPORTED_100baseT_Full | \ + SUPPORTED_Autoneg | \ + SUPPORTED_TP | \ + SUPPORTED_MII) + +#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \ + SUPPORTED_1000baseT_Half | \ + SUPPORTED_1000baseT_Full) + +#define MII_READ_COMMAND 0x00000001 + +#define MII_INTERRUPT_DISABLED 0x0 +#define MII_INTERRUPT_ENABLED 0x1 + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 + +/* Indicates what features are supported by the interface. */ +#define SUPPORTED_10baseT_Half (1 << 0) +#define SUPPORTED_10baseT_Full (1 << 1) +#define SUPPORTED_100baseT_Half (1 << 2) +#define SUPPORTED_100baseT_Full (1 << 3) +#define SUPPORTED_1000baseT_Half (1 << 4) +#define SUPPORTED_1000baseT_Full (1 << 5) +#define SUPPORTED_Autoneg (1 << 6) +#define SUPPORTED_TP (1 << 7) +#define SUPPORTED_AUI (1 << 8) +#define SUPPORTED_MII (1 << 9) +#define SUPPORTED_FIBRE (1 << 10) +#define SUPPORTED_BNC (1 << 11) +#define SUPPORTED_10000baseT_Full (1 << 12) + +#define ADVERTISED_10baseT_Half (1 << 0) +#define ADVERTISED_10baseT_Full (1 << 1) +#define ADVERTISED_100baseT_Half (1 << 2) +#define ADVERTISED_100baseT_Full (1 << 3) +#define ADVERTISED_1000baseT_Half (1 << 4) +#define ADVERTISED_1000baseT_Full (1 << 5) +#define ADVERTISED_Autoneg (1 << 6) +#define ADVERTISED_TP (1 << 7) +#define ADVERTISED_AUI (1 << 8) +#define ADVERTISED_MII (1 << 9) +#define ADVERTISED_FIBRE (1 << 10) +#define ADVERTISED_BNC (1 << 11) +#define ADVERTISED_10000baseT_Full (1 << 12) + +/* Taken from mii_if_info and sungem_phy.h */ +struct uec_mii_info { + /* Information about the PHY type */ + /* And management functions */ + struct phy_info *phyinfo; + + struct eth_device *dev; + + /* forced speed & duplex (no autoneg) + * partner speed & duplex & pause (autoneg) + */ + int speed; + int duplex; + int pause; + + /* The most recently read link state */ + int link; + + /* Enabled Interrupts */ + u32 interrupts; + + u32 advertising; + int autoneg; + int mii_id; + + /* private data pointer */ + /* For use by PHYs to maintain extra state */ + void *priv; + + /* Provided by ethernet driver */ + int (*mdio_read) (struct eth_device * dev, int mii_id, int reg); + void (*mdio_write) (struct eth_device * dev, int mii_id, int reg, + int val); +}; + +/* struct phy_info: a structure which defines attributes for a PHY + * + * id will contain a number which represents the PHY. During + * startup, the driver will poll the PHY to find out what its + * UID--as defined by registers 2 and 3--is. The 32-bit result + * gotten from the PHY will be ANDed with phy_id_mask to + * discard any bits which may change based on revision numbers + * unimportant to functionality + * + * There are 6 commands which take a ugeth_mii_info structure. + * Each PHY must declare config_aneg, and read_status. + */ +struct phy_info { + u32 phy_id; + char *name; + unsigned int phy_id_mask; + u32 features; + + /* Called to initialize the PHY */ + int (*init) (struct uec_mii_info * mii_info); + + /* Called to suspend the PHY for power */ + int (*suspend) (struct uec_mii_info * mii_info); + + /* Reconfigures autonegotiation (or disables it) */ + int (*config_aneg) (struct uec_mii_info * mii_info); + + /* Determines the negotiated speed and duplex */ + int (*read_status) (struct uec_mii_info * mii_info); + + /* Clears any pending interrupts */ + int (*ack_interrupt) (struct uec_mii_info * mii_info); + + /* Enables or disables interrupts */ + int (*config_intr) (struct uec_mii_info * mii_info); + + /* Clears up any memory if needed */ + void (*close) (struct uec_mii_info * mii_info); +}; + +struct phy_info *uec_get_phy_info (struct uec_mii_info *mii_info); +void uec_write_phy_reg (struct eth_device *dev, int mii_id, int regnum, + int value); +int uec_read_phy_reg (struct eth_device *dev, int mii_id, int regnum); +void mii_clear_phy_interrupt (struct uec_mii_info *mii_info); +void mii_configure_phy_interrupt (struct uec_mii_info *mii_info, + u32 interrupts); +#endif /* __UEC_PHY_H__ */ diff --git a/qemu/roms/u-boot/drivers/rtc/Makefile b/qemu/roms/u-boot/drivers/rtc/Makefile new file mode 100644 index 000000000..003d322d2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/Makefile @@ -0,0 +1,52 @@ +# +# (C) Copyright 2001-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +#ccflags-y += -DDEBUG + +obj-$(CONFIG_RTC_AT91SAM9_RTT) += at91sam9_rtt.o +obj-$(CONFIG_RTC_BFIN) += bfin_rtc.o +obj-y += date.o +obj-$(CONFIG_RTC_DAVINCI) += davinci.o +obj-$(CONFIG_RTC_DS12887) += ds12887.o +obj-$(CONFIG_RTC_DS1302) += ds1302.o +obj-$(CONFIG_RTC_DS1306) += ds1306.o +obj-$(CONFIG_RTC_DS1307) += ds1307.o +obj-$(CONFIG_RTC_DS1338) += ds1307.o +obj-$(CONFIG_RTC_DS1337) += ds1337.o +obj-$(CONFIG_RTC_DS1374) += ds1374.o +obj-$(CONFIG_RTC_DS1388) += ds1337.o +obj-$(CONFIG_RTC_DS1556) += ds1556.o +obj-$(CONFIG_RTC_DS164x) += ds164x.o +obj-$(CONFIG_RTC_DS174x) += ds174x.o +obj-$(CONFIG_RTC_DS3231) += ds3231.o +obj-$(CONFIG_RTC_FTRTC010) += ftrtc010.o +obj-$(CONFIG_RTC_IMXDI) += imxdi.o +obj-$(CONFIG_RTC_ISL1208) += isl1208.o +obj-$(CONFIG_RTC_M41T11) += m41t11.o +obj-$(CONFIG_RTC_M41T60) += m41t60.o +obj-$(CONFIG_RTC_M41T62) += m41t62.o +obj-$(CONFIG_RTC_M41T94) += m41t94.o +obj-$(CONFIG_RTC_M48T35A) += m48t35ax.o +obj-$(CONFIG_RTC_MAX6900) += max6900.o +obj-$(CONFIG_RTC_MC13XXX) += mc13xxx-rtc.o +obj-$(CONFIG_RTC_MC146818) += mc146818.o +obj-$(CONFIG_MCFRTC) += mcfrtc.o +obj-$(CONFIG_RTC_MK48T59) += mk48t59.o +obj-$(CONFIG_RTC_MPC5200) += mpc5xxx.o +obj-$(CONFIG_RTC_MPC8xx) += mpc8xx.o +obj-$(CONFIG_RTC_MV) += mvrtc.o +obj-$(CONFIG_RTC_MX27) += mx27rtc.o +obj-$(CONFIG_RTC_MXS) += mxsrtc.o +obj-$(CONFIG_RTC_PCF8563) += pcf8563.o +obj-$(CONFIG_RTC_PL031) += pl031.o +obj-$(CONFIG_RTC_PT7C4338) += pt7c4338.o +obj-$(CONFIG_RTC_RS5C372A) += rs5c372.o +obj-$(CONFIG_RTC_RTC4543) += rtc4543.o +obj-$(CONFIG_RTC_RV3029) += rv3029.o +obj-$(CONFIG_RTC_RX8025) += rx8025.o +obj-$(CONFIG_RTC_S3C24X0) += s3c24x0_rtc.o +obj-$(CONFIG_RTC_X1205) += x1205.o diff --git a/qemu/roms/u-boot/drivers/rtc/at91sam9_rtt.c b/qemu/roms/u-boot/drivers/rtc/at91sam9_rtt.c new file mode 100644 index 000000000..714dd2a34 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/at91sam9_rtt.c @@ -0,0 +1,84 @@ +/* + * (C) Copyright 2010 + * Reinhard Meyer, reinhard.meyer@emk-elektronik.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for the internal Real-time Timer + * of AT91SAM9260 and compatibles. + * Compatible with the LinuX rtc driver workaround: + * The RTT cannot be written to, but only reset. + * The actual time is the sum of RTT and one of + * the four GPBR registers. + * + * The at91sam9260 has 4 GPBR (0-3). + * For their typical use see at91_gpbr.h ! + * + * make sure u-boot and kernel use the same GPBR ! + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +int rtc_get (struct rtc_time *tmp) +{ + at91_rtt_t *rtt = (at91_rtt_t *) ATMEL_BASE_RTT; + at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR; + ulong tim; + ulong tim2; + ulong off; + + do { + tim = readl(&rtt->vr); + tim2 = readl(&rtt->vr); + } while (tim!=tim2); + off = readl(&gpbr->reg[AT91_GPBR_INDEX_TIMEOFF]); + /* off==0 means time is invalid, but we ignore that */ + to_tm (tim+off, tmp); + return 0; +} + +int rtc_set (struct rtc_time *tmp) +{ + at91_rtt_t *rtt = (at91_rtt_t *) ATMEL_BASE_RTT; + at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR; + ulong tim; + + tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + /* clear alarm, set prescaler to 32768, clear counter */ + writel(32768+AT91_RTT_RTTRST, &rtt->mr); + writel(~0, &rtt->ar); + writel(tim, &gpbr->reg[AT91_GPBR_INDEX_TIMEOFF]); + /* wait for counter clear to happen, takes less than a 1/32768th second */ + while (readl(&rtt->vr) != 0) + ; + return 0; +} + +void rtc_reset (void) +{ + at91_rtt_t *rtt = (at91_rtt_t *) ATMEL_BASE_RTT; + at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR; + + /* clear alarm, set prescaler to 32768, clear counter */ + writel(32768+AT91_RTT_RTTRST, &rtt->mr); + writel(~0, &rtt->ar); + writel(0, &gpbr->reg[AT91_GPBR_INDEX_TIMEOFF]); + /* wait for counter clear to happen, takes less than a 1/32768th second */ + while (readl(&rtt->vr) != 0) + ; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/bfin_rtc.c b/qemu/roms/u-boot/drivers/rtc/bfin_rtc.c new file mode 100644 index 000000000..21a2189e2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/bfin_rtc.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2004-2008 Analog Devices Inc. + * + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +#include +#include + +#define pr_stamp() debug("%s:%s:%i: here i am\n", __FILE__, __func__, __LINE__) + +#define MIN_TO_SECS(x) (60 * (x)) +#define HRS_TO_SECS(x) (60 * MIN_TO_SECS(x)) +#define DAYS_TO_SECS(x) (24 * HRS_TO_SECS(x)) + +#define NUM_SECS_IN_MIN MIN_TO_SECS(1) +#define NUM_SECS_IN_HR HRS_TO_SECS(1) +#define NUM_SECS_IN_DAY DAYS_TO_SECS(1) + +/* Enable the RTC prescaler enable register */ +static void rtc_init(void) +{ + if (!(bfin_read_RTC_PREN() & 0x1)) + bfin_write_RTC_PREN(0x1); +} + +/* Our on-chip RTC has no notion of "reset" */ +void rtc_reset(void) +{ + rtc_init(); +} + +/* Wait for pending writes to complete */ +static void wait_for_complete(void) +{ + pr_stamp(); + while (!(bfin_read_RTC_ISTAT() & WRITE_COMPLETE)) + if (!(bfin_read_RTC_ISTAT() & WRITE_PENDING)) + break; + bfin_write_RTC_ISTAT(WRITE_COMPLETE); +} + +/* Set the time. Get the time_in_secs which is the number of seconds since Jan 1970 and set the RTC registers + * based on this value. + */ +int rtc_set(struct rtc_time *tmp) +{ + unsigned long remain, days, hrs, mins, secs; + + pr_stamp(); + + if (tmp == NULL) { + puts("Error setting the date/time\n"); + return -1; + } + + rtc_init(); + wait_for_complete(); + + /* Calculate number of seconds this incoming time represents */ + remain = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + /* Figure out how many days since epoch */ + days = remain / NUM_SECS_IN_DAY; + + /* From the remaining secs, compute the hrs(0-23), mins(0-59) and secs(0-59) */ + remain = remain % NUM_SECS_IN_DAY; + hrs = remain / NUM_SECS_IN_HR; + remain = remain % NUM_SECS_IN_HR; + mins = remain / NUM_SECS_IN_MIN; + secs = remain % NUM_SECS_IN_MIN; + + /* Encode these time values into our RTC_STAT register */ + bfin_write_RTC_STAT(SET_ALARM(days, hrs, mins, secs)); + + return 0; +} + +/* Read the time from the RTC_STAT. time_in_seconds is seconds since Jan 1970 */ +int rtc_get(struct rtc_time *tmp) +{ + uint32_t cur_rtc_stat; + int time_in_sec; + int tm_sec, tm_min, tm_hr, tm_day; + + pr_stamp(); + + if (tmp == NULL) { + puts("Error getting the date/time\n"); + return -1; + } + + rtc_init(); + wait_for_complete(); + + /* Read the RTC_STAT register */ + cur_rtc_stat = bfin_read_RTC_STAT(); + + /* Convert our encoded format into actual time values */ + tm_sec = (cur_rtc_stat & RTC_SEC) >> RTC_SEC_P; + tm_min = (cur_rtc_stat & RTC_MIN) >> RTC_MIN_P; + tm_hr = (cur_rtc_stat & RTC_HR ) >> RTC_HR_P; + tm_day = (cur_rtc_stat & RTC_DAY) >> RTC_DAY_P; + + /* Calculate the total number of seconds since epoch */ + time_in_sec = (tm_sec) + MIN_TO_SECS(tm_min) + HRS_TO_SECS(tm_hr) + DAYS_TO_SECS(tm_day); + to_tm(time_in_sec, tmp); + + return 0; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/date.c b/qemu/roms/u-boot/drivers/rtc/date.c new file mode 100644 index 000000000..15e6db06b --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/date.c @@ -0,0 +1,140 @@ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for Philips PCF8563 RTC + */ + +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) || defined(CONFIG_TIMESTAMP) + +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) + */ +void GregorianDay(struct rtc_time * tm) +{ + int leapsToDate; + int lastYear; + int day; + int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 }; + + lastYear=tm->tm_year-1; + + /* + * Number of leap corrections to apply up to end of last year + */ + leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; + + /* + * This year is a leap year if it is divisible by 4 except when it is + * divisible by 100 unless it is divisible by 400 + * + * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be + */ + if((tm->tm_year%4==0) && + ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && + (tm->tm_mon>2)) { + /* + * We are past Feb. 29 in a leap year + */ + day=1; + } else { + day=0; + } + + day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday; + + tm->tm_wday=day%7; +} + +void to_tm(int tim, struct rtc_time * tm) +{ + register int i; + register long hms, day; + + day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) { + day -= days_in_year(i); + } + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) { + days_in_month(FEBRUARY) = 29; + } + for (i = 1; day >= days_in_month(i); i++) { + day -= days_in_month(i); + } + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; + + /* + * Determine the day of week + */ + GregorianDay(tm); +} + +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +unsigned long +mktime (unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + + return ((( + (unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/davinci.c b/qemu/roms/u-boot/drivers/rtc/davinci.c new file mode 100644 index 000000000..f862e2f95 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/davinci.c @@ -0,0 +1,83 @@ +/* + * (C) Copyright 2011 DENX Software Engineering GmbH + * Heiko Schocher + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) +int rtc_get(struct rtc_time *tmp) +{ + struct davinci_rtc *rtc = (struct davinci_rtc *)DAVINCI_RTC_BASE; + unsigned long sec, min, hour, mday, wday, mon_cent, year; + unsigned long status; + + status = readl(&rtc->status); + if ((status & RTC_STATE_RUN) != RTC_STATE_RUN) { + printf("RTC doesn't run\n"); + return -1; + } + if ((status & RTC_STATE_BUSY) == RTC_STATE_BUSY) + udelay(20); + + sec = readl(&rtc->second); + min = readl(&rtc->minutes); + hour = readl(&rtc->hours); + mday = readl(&rtc->day); + wday = readl(&rtc->dotw); + mon_cent = readl(&rtc->month); + year = readl(&rtc->year); + + debug("Get RTC year: %02lx mon/cent: %02lx mday: %02lx wday: %02lx " + "hr: %02lx min: %02lx sec: %02lx\n", + year, mon_cent, mday, wday, + hour, min, sec); + + tmp->tm_sec = bcd2bin(sec & 0x7F); + tmp->tm_min = bcd2bin(min & 0x7F); + tmp->tm_hour = bcd2bin(hour & 0x3F); + tmp->tm_mday = bcd2bin(mday & 0x3F); + tmp->tm_mon = bcd2bin(mon_cent & 0x1F); + tmp->tm_year = bcd2bin(year) + 2000; + tmp->tm_wday = bcd2bin(wday & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst = 0; + + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +int rtc_set(struct rtc_time *tmp) +{ + struct davinci_rtc *rtc = (struct davinci_rtc *)DAVINCI_RTC_BASE; + + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + writel(bin2bcd(tmp->tm_year % 100), &rtc->year); + writel(bin2bcd(tmp->tm_mon), &rtc->month); + + writel(bin2bcd(tmp->tm_wday), &rtc->dotw); + writel(bin2bcd(tmp->tm_mday), &rtc->day); + writel(bin2bcd(tmp->tm_hour), &rtc->hours); + writel(bin2bcd(tmp->tm_min), &rtc->minutes); + writel(bin2bcd(tmp->tm_sec), &rtc->second); + return 0; +} + +void rtc_reset(void) +{ + struct davinci_rtc *rtc = (struct davinci_rtc *)DAVINCI_RTC_BASE; + + /* run RTC counter */ + writel(0x01, &rtc->ctrl); +} +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds12887.c b/qemu/roms/u-boot/drivers/rtc/ds12887.c new file mode 100644 index 000000000..d8a519b8f --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds12887.c @@ -0,0 +1,217 @@ +/* + * (C) Copyright 2003 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for the DS12887 RTC + */ + +#undef RTC_DEBUG + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +#define RTC_SECONDS 0x00 +#define RTC_SECONDS_ALARM 0x01 +#define RTC_MINUTES 0x02 +#define RTC_MINUTES_ALARM 0x03 +#define RTC_HOURS 0x04 +#define RTC_HOURS_ALARM 0x05 +#define RTC_DAY_OF_WEEK 0x06 +#define RTC_DATE_OF_MONTH 0x07 +#define RTC_MONTH 0x08 +#define RTC_YEAR 0x09 +#define RTC_CONTROL_A 0x0A +#define RTC_CONTROL_B 0x0B +#define RTC_CONTROL_C 0x0C +#define RTC_CONTROL_D 0x0D + +#define RTC_CA_UIP 0x80 +#define RTC_CB_DM 0x04 +#define RTC_CB_24_12 0x02 +#define RTC_CB_SET 0x80 + +#if defined(CONFIG_ATC) + +static uchar rtc_read (uchar reg) +{ + uchar val; + + *(volatile unsigned char*)(RTC_PORT_ADDR) = reg; + __asm__ __volatile__ ("sync"); + + val = *(volatile unsigned char*)(RTC_PORT_DATA); + return (val); +} + +static void rtc_write (uchar reg, uchar val) +{ + *(volatile unsigned char*)(RTC_PORT_ADDR) = reg; + __asm__ __volatile__ ("sync"); + + *(volatile unsigned char*)(RTC_PORT_DATA) = val; + __asm__ __volatile__ ("sync"); +} + +#else +# error Board specific rtc access functions should be supplied +#endif + +int rtc_get (struct rtc_time *tmp) +{ + uchar sec, min, hour, mday, wday, mon, year; + + /* check if rtc is available for access */ + while( rtc_read(RTC_CONTROL_A) & RTC_CA_UIP) + ; + + sec = rtc_read(RTC_SECONDS); + min = rtc_read(RTC_MINUTES); + hour = rtc_read(RTC_HOURS); + mday = rtc_read(RTC_DATE_OF_MONTH); + wday = rtc_read(RTC_DAY_OF_WEEK); + mon = rtc_read(RTC_MONTH); + year = rtc_read(RTC_YEAR); + +#ifdef RTC_DEBUG + printf( "Get RTC year: %d; mon: %d; mday: %d; wday: %d; " + "hr: %d; min: %d; sec: %d\n", + year, mon, mday, wday, hour, min, sec ); + + printf ( "Alarms: hour: %02x min: %02x sec: %02x\n", + rtc_read (RTC_HOURS_ALARM), + rtc_read (RTC_MINUTES_ALARM), + rtc_read (RTC_SECONDS_ALARM) ); +#endif + + if( !(rtc_read(RTC_CONTROL_B) & RTC_CB_DM)) + { /* Information is in BCD format */ +printf(" Get: Convert BSD to BIN\n"); + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon & 0x1F); + tmp->tm_year = bcd2bin (year); + tmp->tm_wday = bcd2bin (wday & 0x07); + } +else + { + tmp->tm_sec = sec & 0x7F; + tmp->tm_min = min & 0x7F; + tmp->tm_hour = hour & 0x3F; + tmp->tm_mday = mday & 0x3F; + tmp->tm_mon = mon & 0x1F; + tmp->tm_year = year; + tmp->tm_wday = wday & 0x07; + } + + + if(tmp->tm_year<70) + tmp->tm_year+=2000; + else + tmp->tm_year+=1900; + + tmp->tm_yday = 0; + tmp->tm_isdst= 0; +#ifdef RTC_DEBUG + printf ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + + return 0; +} + +int rtc_set (struct rtc_time *tmp) +{ + uchar save_ctrl_b; + uchar sec, min, hour, mday, wday, mon, year; + +#ifdef RTC_DEBUG + printf ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + + if( !(rtc_read(RTC_CONTROL_B) & RTC_CB_DM)) + { /* Information is in BCD format */ + year = bin2bcd(tmp->tm_year % 100); + mon = bin2bcd(tmp->tm_mon); + wday = bin2bcd(tmp->tm_wday); + mday = bin2bcd(tmp->tm_mday); + hour = bin2bcd(tmp->tm_hour); + min = bin2bcd(tmp->tm_min); + sec = bin2bcd(tmp->tm_sec); + } + else + { + year = tmp->tm_year % 100; + mon = tmp->tm_mon; + wday = tmp->tm_wday; + mday = tmp->tm_mday; + hour = tmp->tm_hour; + min = tmp->tm_min; + sec = tmp->tm_sec; + } + + /* disables the RTC to update the regs */ + save_ctrl_b = rtc_read(RTC_CONTROL_B); + save_ctrl_b |= RTC_CB_SET; + rtc_write(RTC_CONTROL_B, save_ctrl_b); + + rtc_write (RTC_YEAR, year); + rtc_write (RTC_MONTH, mon); + rtc_write (RTC_DAY_OF_WEEK, wday); + rtc_write (RTC_DATE_OF_MONTH, mday); + rtc_write (RTC_HOURS, hour); + rtc_write (RTC_MINUTES, min); + rtc_write (RTC_SECONDS, sec); + + /* enables the RTC to update the regs */ + save_ctrl_b &= ~RTC_CB_SET; + rtc_write(RTC_CONTROL_B, save_ctrl_b); + + return 0; +} + +void rtc_reset (void) +{ + struct rtc_time tmp; + uchar ctrl_rg; + + ctrl_rg = RTC_CB_SET; + rtc_write(RTC_CONTROL_B,ctrl_rg); + + tmp.tm_year = 1970 % 100; + tmp.tm_mon = 1; + tmp.tm_mday= 1; + tmp.tm_hour = 0; + tmp.tm_min = 0; + tmp.tm_sec = 0; + +#ifdef RTC_DEBUG + printf ( "RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", + tmp.tm_year, tmp.tm_mon, tmp.tm_mday, + tmp.tm_hour, tmp.tm_min, tmp.tm_sec); +#endif + + ctrl_rg = RTC_CB_SET | RTC_CB_24_12 | RTC_CB_DM; + rtc_write(RTC_CONTROL_B,ctrl_rg); + rtc_set(&tmp); + + rtc_write(RTC_HOURS_ALARM, 0), + rtc_write(RTC_MINUTES_ALARM, 0), + rtc_write(RTC_SECONDS_ALARM, 0); + + ctrl_rg = RTC_CB_24_12 | RTC_CB_DM; + rtc_write(RTC_CONTROL_B,ctrl_rg); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds1302.c b/qemu/roms/u-boot/drivers/rtc/ds1302.c new file mode 100644 index 000000000..87ddd0191 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds1302.c @@ -0,0 +1,332 @@ +/* + * ds1302.c - Support for the Dallas Semiconductor DS1302 Timekeeping Chip + * + * Rex G. Feany + * + */ + +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +/* GPP Pins */ +#define DATA 0x200 +#define SCLK 0x400 +#define RST 0x800 + +/* Happy Fun Defines(tm) */ +#define RESET rtc_go_low(RST), rtc_go_low(SCLK) +#define N_RESET rtc_go_high(RST), rtc_go_low(SCLK) + +#define CLOCK_HIGH rtc_go_high(SCLK) +#define CLOCK_LOW rtc_go_low(SCLK) + +#define DATA_HIGH rtc_go_high(DATA) +#define DATA_LOW rtc_go_low(DATA) +#define DATA_READ (GTREGREAD(GPP_VALUE) & DATA) + +#undef RTC_DEBUG + +#ifdef RTC_DEBUG +# define DPRINTF(x,args...) printf("ds1302: " x , ##args) +static inline void DUMP(const char *ptr, int num) +{ + while (num--) printf("%x ", *ptr++); + printf("]\n"); +} +#else +# define DPRINTF(x,args...) +# define DUMP(ptr, num) +#endif + +/* time data format for DS1302 */ +struct ds1302_st +{ + unsigned char CH:1; /* clock halt 1=stop 0=start */ + unsigned char sec10:3; + unsigned char sec:4; + + unsigned char zero0:1; + unsigned char min10:3; + unsigned char min:4; + + unsigned char fmt:1; /* 1=12 hour 0=24 hour */ + unsigned char zero1:1; + unsigned char hr10:2; /* 10 (0-2) or am/pm (am/pm, 0-1) */ + unsigned char hr:4; + + unsigned char zero2:2; + unsigned char date10:2; + unsigned char date:4; + + unsigned char zero3:3; + unsigned char month10:1; + unsigned char month:4; + + unsigned char zero4:5; + unsigned char day:3; /* day of week */ + + unsigned char year10:4; + unsigned char year:4; + + unsigned char WP:1; /* write protect 1=protect 0=unprot */ + unsigned char zero5:7; +}; + +static int ds1302_initted=0; + +/* Pin control */ +static inline void +rtc_go_high(unsigned int mask) +{ + unsigned int f = GTREGREAD(GPP_VALUE) | mask; + + GT_REG_WRITE(GPP_VALUE, f); +} + +static inline void +rtc_go_low(unsigned int mask) +{ + unsigned int f = GTREGREAD(GPP_VALUE) & ~mask; + + GT_REG_WRITE(GPP_VALUE, f); +} + +static inline void +rtc_go_input(unsigned int mask) +{ + unsigned int f = GTREGREAD(GPP_IO_CONTROL) & ~mask; + + GT_REG_WRITE(GPP_IO_CONTROL, f); +} + +static inline void +rtc_go_output(unsigned int mask) +{ + unsigned int f = GTREGREAD(GPP_IO_CONTROL) | mask; + + GT_REG_WRITE(GPP_IO_CONTROL, f); +} + +/* Access data in RTC */ + +static void +write_byte(unsigned char b) +{ + int i; + unsigned char mask=1; + + for(i=0;i<8;i++) { + CLOCK_LOW; /* Lower clock */ + (b&mask)?DATA_HIGH:DATA_LOW; /* set data */ + udelay(1); + CLOCK_HIGH; /* latch data with rising clock */ + udelay(1); + mask=mask<<1; + } +} + +static unsigned char +read_byte(void) +{ + int i; + unsigned char mask=1; + unsigned char b=0; + + for(i=0;i<8;i++) { + CLOCK_LOW; + udelay(1); + if (DATA_READ) b|=mask; /* if this bit is high, set in b */ + CLOCK_HIGH; /* clock out next bit */ + udelay(1); + mask=mask<<1; + } + return b; +} + +static void +read_ser_drv(unsigned char addr, unsigned char *buf, int count) +{ + int i; +#ifdef RTC_DEBUG + char *foo = buf; +#endif + + DPRINTF("READ 0x%x bytes @ 0x%x [ ", count, addr); + + addr|=1; /* READ */ + N_RESET; + udelay(4); + write_byte(addr); + rtc_go_input(DATA); /* Put gpp pin into input mode */ + udelay(1); + for(i=0;i9) { + printf("ds1302: Year was corrupted, fixing\n"); + bbclk.year10=100/10; /* 2000 - why not? ;) */ + bbclk.year=0; + mod=1; + } + + /* Write out the changes if needed */ + if (mod) { + /* enable write protect */ + bbclk.WP = 1; + write_ser_drv(0xbe,(unsigned char *)&bbclk,8); + } else { + /* Else just turn write protect on */ + b = 0x80; + write_ser_drv(0x8e,&b,1); + } + DPRINTF("init done\n"); + + ds1302_initted=1; +} + +void +rtc_reset(void) +{ + if(!ds1302_initted) rtc_init(); + /* TODO */ +} + +int +rtc_get(struct rtc_time *tmp) +{ + int rel = 0; + struct ds1302_st bbclk; + + if(!ds1302_initted) rtc_init(); + + read_ser_drv(0xbe,(unsigned char *)&bbclk, 8); /* read burst */ + + if (bbclk.CH) { + printf("ds1302: rtc_get: Clock was halted, clock probably " + "corrupt\n"); + rel = -1; + } + + tmp->tm_sec=10*bbclk.sec10+bbclk.sec; + tmp->tm_min=10*bbclk.min10+bbclk.min; + tmp->tm_hour=10*bbclk.hr10+bbclk.hr; + tmp->tm_wday=bbclk.day; + tmp->tm_mday=10*bbclk.date10+bbclk.date; + tmp->tm_mon=10*bbclk.month10+bbclk.month; + tmp->tm_year=10*bbclk.year10+bbclk.year + 1900; + + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + DPRINTF("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); + + return rel; +} + +int rtc_set(struct rtc_time *tmp) +{ + struct ds1302_st bbclk; + unsigned char b=0; + + if(!ds1302_initted) rtc_init(); + + DPRINTF("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + memset(&bbclk,0,sizeof(bbclk)); + bbclk.CH=0; /* dont halt */ + bbclk.WP=1; /* write protect when we're done */ + + bbclk.sec10=tmp->tm_sec/10; + bbclk.sec=tmp->tm_sec%10; + + bbclk.min10=tmp->tm_min/10; + bbclk.min=tmp->tm_min%10; + + bbclk.hr10=tmp->tm_hour/10; + bbclk.hr=tmp->tm_hour%10; + + bbclk.day=tmp->tm_wday; + + bbclk.date10=tmp->tm_mday/10; + bbclk.date=tmp->tm_mday%10; + + bbclk.month10=tmp->tm_mon/10; + bbclk.month=tmp->tm_mon%10; + + tmp->tm_year -= 1900; + bbclk.year10=tmp->tm_year/10; + bbclk.year=tmp->tm_year%10; + + write_ser_drv(0x8e,&b,1); /* disable write protect */ + write_ser_drv(0xbe,(unsigned char *)&bbclk, 8); /* write burst */ + + return 0; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds1306.c b/qemu/roms/u-boot/drivers/rtc/ds1306.c new file mode 100644 index 000000000..1ec1837cb --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds1306.c @@ -0,0 +1,443 @@ +/* + * (C) Copyright 2002 SIXNET, dge@sixnetio.com. + * + * (C) Copyright 2004, Li-Pro.Net + * Stephan Linz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for DS1306 RTC using SPI: + * + * - SXNI855T: it uses its own soft SPI here in this file + * - all other: use the external spi_xfer() function + * (see include/spi.h) + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +#define RTC_SECONDS 0x00 +#define RTC_MINUTES 0x01 +#define RTC_HOURS 0x02 +#define RTC_DAY_OF_WEEK 0x03 +#define RTC_DATE_OF_MONTH 0x04 +#define RTC_MONTH 0x05 +#define RTC_YEAR 0x06 + +#define RTC_SECONDS_ALARM0 0x07 +#define RTC_MINUTES_ALARM0 0x08 +#define RTC_HOURS_ALARM0 0x09 +#define RTC_DAY_OF_WEEK_ALARM0 0x0a + +#define RTC_SECONDS_ALARM1 0x0b +#define RTC_MINUTES_ALARM1 0x0c +#define RTC_HOURS_ALARM1 0x0d +#define RTC_DAY_OF_WEEK_ALARM1 0x0e + +#define RTC_CONTROL 0x0f +#define RTC_STATUS 0x10 +#define RTC_TRICKLE_CHARGER 0x11 + +#define RTC_USER_RAM_BASE 0x20 + +/* ************************************************************************* */ +#ifdef CONFIG_SXNI855T /* !!! SHOULD BE CHANGED TO NEW CODE !!! */ + +static void soft_spi_send (unsigned char n); +static unsigned char soft_spi_read (void); +static void init_spi (void); + +/*----------------------------------------------------------------------- + * Definitions + */ + +#define PB_SPISCK 0x00000002 /* PB 30 */ +#define PB_SPIMOSI 0x00000004 /* PB 29 */ +#define PB_SPIMISO 0x00000008 /* PB 28 */ +#define PB_SPI_CE 0x00010000 /* PB 15 */ + +/* ------------------------------------------------------------------------- */ + +/* read clock time from DS1306 and return it in *tmp */ +int rtc_get (struct rtc_time *tmp) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + unsigned char spi_byte; /* Data Byte */ + + init_spi (); /* set port B for software SPI */ + + /* Now we can enable the DS1306 RTC */ + immap->im_cpm.cp_pbdat |= PB_SPI_CE; + udelay (10); + + /* Shift out the address (0) of the time in the Clock Chip */ + soft_spi_send (0); + + /* Put the clock readings into the rtc_time structure */ + tmp->tm_sec = bcd2bin (soft_spi_read ()); /* Read seconds */ + tmp->tm_min = bcd2bin (soft_spi_read ()); /* Read minutes */ + + /* Hours are trickier */ + spi_byte = soft_spi_read (); /* Read Hours into temporary value */ + if (spi_byte & 0x40) { + /* 12 hour mode bit is set (time is in 1-12 format) */ + if (spi_byte & 0x20) { + /* since PM we add 11 to get 0-23 for hours */ + tmp->tm_hour = (bcd2bin (spi_byte & 0x1F)) + 11; + } else { + /* since AM we subtract 1 to get 0-23 for hours */ + tmp->tm_hour = (bcd2bin (spi_byte & 0x1F)) - 1; + } + } else { + /* Otherwise, 0-23 hour format */ + tmp->tm_hour = (bcd2bin (spi_byte & 0x3F)); + } + + soft_spi_read (); /* Read and discard Day of week */ + tmp->tm_mday = bcd2bin (soft_spi_read ()); /* Read Day of the Month */ + tmp->tm_mon = bcd2bin (soft_spi_read ()); /* Read Month */ + + /* Read Year and convert to this century */ + tmp->tm_year = bcd2bin (soft_spi_read ()) + 2000; + + /* Now we can disable the DS1306 RTC */ + immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ + udelay (10); + + GregorianDay (tmp); /* Determine the day of week */ + + debug ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* set clock time in DS1306 RTC and in MPC8xx RTC */ +int rtc_set (struct rtc_time *tmp) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + init_spi (); /* set port B for software SPI */ + + /* Now we can enable the DS1306 RTC */ + immap->im_cpm.cp_pbdat |= PB_SPI_CE; /* Enable DS1306 Chip */ + udelay (10); + + /* First disable write protect in the clock chip control register */ + soft_spi_send (0x8F); /* send address of the control register */ + soft_spi_send (0x00); /* send control register contents */ + + /* Now disable the DS1306 to terminate the write */ + immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; + udelay (10); + + /* Now enable the DS1306 to initiate a new write */ + immap->im_cpm.cp_pbdat |= PB_SPI_CE; + udelay (10); + + /* Next, send the address of the clock time write registers */ + soft_spi_send (0x80); /* send address of the first time register */ + + /* Use Burst Mode to send all of the time data to the clock */ + bin2bcd (tmp->tm_sec); + soft_spi_send (bin2bcd (tmp->tm_sec)); /* Send Seconds */ + soft_spi_send (bin2bcd (tmp->tm_min)); /* Send Minutes */ + soft_spi_send (bin2bcd (tmp->tm_hour)); /* Send Hour */ + soft_spi_send (bin2bcd (tmp->tm_wday)); /* Send Day of the Week */ + soft_spi_send (bin2bcd (tmp->tm_mday)); /* Send Day of Month */ + soft_spi_send (bin2bcd (tmp->tm_mon)); /* Send Month */ + soft_spi_send (bin2bcd (tmp->tm_year - 2000)); /* Send Year */ + + /* Now we can disable the Clock chip to terminate the burst write */ + immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ + udelay (10); + + /* Now we can enable the Clock chip to initiate a new write */ + immap->im_cpm.cp_pbdat |= PB_SPI_CE; /* Enable DS1306 Chip */ + udelay (10); + + /* First we Enable write protect in the clock chip control register */ + soft_spi_send (0x8F); /* send address of the control register */ + soft_spi_send (0x40); /* send out Control Register contents */ + + /* Now disable the DS1306 */ + immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ + udelay (10); + + /* Set standard MPC8xx clock to the same time so Linux will + * see the time even if it doesn't have a DS1306 clock driver. + * This helps with experimenting with standard kernels. + */ + { + ulong tim; + + tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + immap->im_sitk.sitk_rtck = KAPWR_KEY; + immap->im_sit.sit_rtc = tim; + } + + debug ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* Initialize Port B for software SPI */ +static void init_spi (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + /* Force output pins to begin at logic 0 */ + immap->im_cpm.cp_pbdat &= ~(PB_SPI_CE | PB_SPIMOSI | PB_SPISCK); + + /* Set these 3 signals as outputs */ + immap->im_cpm.cp_pbdir |= (PB_SPIMOSI | PB_SPI_CE | PB_SPISCK); + + immap->im_cpm.cp_pbdir &= ~PB_SPIMISO; /* Make MISO pin an input */ + udelay (10); +} + +/* ------------------------------------------------------------------------- */ + +/* NOTE: soft_spi_send() assumes that the I/O lines are configured already */ +static void soft_spi_send (unsigned char n) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + unsigned char bitpos; /* bit position to receive */ + unsigned char i; /* Loop Control */ + + /* bit position to send, start with most significant bit */ + bitpos = 0x80; + + /* Send 8 bits to software SPI */ + for (i = 0; i < 8; i++) { /* Loop for 8 bits */ + immap->im_cpm.cp_pbdat |= PB_SPISCK; /* Raise SCK */ + + if (n & bitpos) + immap->im_cpm.cp_pbdat |= PB_SPIMOSI; /* Set MOSI to 1 */ + else + immap->im_cpm.cp_pbdat &= ~PB_SPIMOSI; /* Set MOSI to 0 */ + udelay (10); + + immap->im_cpm.cp_pbdat &= ~PB_SPISCK; /* Lower SCK */ + udelay (10); + + bitpos >>= 1; /* Shift for next bit position */ + } +} + +/* ------------------------------------------------------------------------- */ + +/* NOTE: soft_spi_read() assumes that the I/O lines are configured already */ +static unsigned char soft_spi_read (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + unsigned char spi_byte = 0; /* Return value, assume success */ + unsigned char bitpos; /* bit position to receive */ + unsigned char i; /* Loop Control */ + + /* bit position to receive, start with most significant bit */ + bitpos = 0x80; + + /* Read 8 bits here */ + for (i = 0; i < 8; i++) { /* Do 8 bits in loop */ + immap->im_cpm.cp_pbdat |= PB_SPISCK; /* Raise SCK */ + udelay (10); + if (immap->im_cpm.cp_pbdat & PB_SPIMISO) /* Get a bit of data */ + spi_byte |= bitpos; /* Set data accordingly */ + immap->im_cpm.cp_pbdat &= ~PB_SPISCK; /* Lower SCK */ + udelay (10); + bitpos >>= 1; /* Shift for next bit position */ + } + + return spi_byte; /* Return the byte read */ +} + +/* ------------------------------------------------------------------------- */ + +void rtc_reset (void) +{ + return; /* nothing to do */ +} + +#else /* not CONFIG_SXNI855T */ +/* ************************************************************************* */ + +static unsigned char rtc_read (unsigned char reg); +static void rtc_write (unsigned char reg, unsigned char val); + +static struct spi_slave *slave; + +/* read clock time from DS1306 and return it in *tmp */ +int rtc_get (struct rtc_time *tmp) +{ + unsigned char sec, min, hour, mday, wday, mon, year; + + /* + * Assuming Vcc = 2.0V (lowest speed) + * + * REVISIT: If we add an rtc_init() function we can do this + * step just once. + */ + if (!slave) { + slave = spi_setup_slave(0, CONFIG_SYS_SPI_RTC_DEVID, 600000, + SPI_MODE_3 | SPI_CS_HIGH); + if (!slave) + return; + } + + if (spi_claim_bus(slave)) + return; + + sec = rtc_read (RTC_SECONDS); + min = rtc_read (RTC_MINUTES); + hour = rtc_read (RTC_HOURS); + mday = rtc_read (RTC_DATE_OF_MONTH); + wday = rtc_read (RTC_DAY_OF_WEEK); + mon = rtc_read (RTC_MONTH); + year = rtc_read (RTC_YEAR); + + spi_release_bus(slave); + + debug ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon, mday, wday, hour, min, sec); + debug ("Alarms[0]: wday: %02x hour: %02x min: %02x sec: %02x\n", + rtc_read (RTC_DAY_OF_WEEK_ALARM0), + rtc_read (RTC_HOURS_ALARM0), + rtc_read (RTC_MINUTES_ALARM0), rtc_read (RTC_SECONDS_ALARM0)); + debug ("Alarms[1]: wday: %02x hour: %02x min: %02x sec: %02x\n", + rtc_read (RTC_DAY_OF_WEEK_ALARM1), + rtc_read (RTC_HOURS_ALARM1), + rtc_read (RTC_MINUTES_ALARM1), rtc_read (RTC_SECONDS_ALARM1)); + + tmp->tm_sec = bcd2bin (sec & 0x7F); /* convert Seconds */ + tmp->tm_min = bcd2bin (min & 0x7F); /* convert Minutes */ + + /* convert Hours */ + tmp->tm_hour = (hour & 0x40) + ? ((hour & 0x20) /* 12 hour mode */ + ? bcd2bin (hour & 0x1F) + 11 /* PM */ + : bcd2bin (hour & 0x1F) - 1 /* AM */ + ) + : bcd2bin (hour & 0x3F); /* 24 hour mode */ + + tmp->tm_mday = bcd2bin (mday & 0x3F); /* convert Day of the Month */ + tmp->tm_mon = bcd2bin (mon & 0x1F); /* convert Month */ + tmp->tm_year = bcd2bin (year) + 2000; /* convert Year */ + tmp->tm_wday = bcd2bin (wday & 0x07) - 1; /* convert Day of the Week */ + tmp->tm_yday = 0; + tmp->tm_isdst = 0; + + debug ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* set clock time from *tmp in DS1306 RTC */ +int rtc_set (struct rtc_time *tmp) +{ + /* Assuming Vcc = 2.0V (lowest speed) */ + if (!slave) { + slave = spi_setup_slave(0, CONFIG_SYS_SPI_RTC_DEVID, 600000, + SPI_MODE_3 | SPI_CS_HIGH); + if (!slave) + return; + } + + if (spi_claim_bus(slave)) + return; + + debug ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + rtc_write (RTC_SECONDS, bin2bcd (tmp->tm_sec)); + rtc_write (RTC_MINUTES, bin2bcd (tmp->tm_min)); + rtc_write (RTC_HOURS, bin2bcd (tmp->tm_hour)); + rtc_write (RTC_DAY_OF_WEEK, bin2bcd (tmp->tm_wday + 1)); + rtc_write (RTC_DATE_OF_MONTH, bin2bcd (tmp->tm_mday)); + rtc_write (RTC_MONTH, bin2bcd (tmp->tm_mon)); + rtc_write (RTC_YEAR, bin2bcd (tmp->tm_year - 2000)); + + spi_release_bus(slave); +} + +/* ------------------------------------------------------------------------- */ + +/* reset the DS1306 */ +void rtc_reset (void) +{ + /* Assuming Vcc = 2.0V (lowest speed) */ + if (!slave) { + slave = spi_setup_slave(0, CONFIG_SYS_SPI_RTC_DEVID, 600000, + SPI_MODE_3 | SPI_CS_HIGH); + if (!slave) + return; + } + + if (spi_claim_bus(slave)) + return; + + /* clear the control register */ + rtc_write (RTC_CONTROL, 0x00); /* 1st step: reset WP */ + rtc_write (RTC_CONTROL, 0x00); /* 2nd step: reset 1Hz, AIE1, AIE0 */ + + /* reset all alarms */ + rtc_write (RTC_SECONDS_ALARM0, 0x00); + rtc_write (RTC_SECONDS_ALARM1, 0x00); + rtc_write (RTC_MINUTES_ALARM0, 0x00); + rtc_write (RTC_MINUTES_ALARM1, 0x00); + rtc_write (RTC_HOURS_ALARM0, 0x00); + rtc_write (RTC_HOURS_ALARM1, 0x00); + rtc_write (RTC_DAY_OF_WEEK_ALARM0, 0x00); + rtc_write (RTC_DAY_OF_WEEK_ALARM1, 0x00); + + spi_release_bus(slave); +} + +/* ------------------------------------------------------------------------- */ + +static unsigned char rtc_read (unsigned char reg) +{ + int ret; + + ret = spi_w8r8(slave, reg); + return ret < 0 ? 0 : ret; +} + +/* ------------------------------------------------------------------------- */ + +static void rtc_write (unsigned char reg, unsigned char val) +{ + unsigned char dout[2]; /* SPI Output Data Bytes */ + unsigned char din[2]; /* SPI Input Data Bytes */ + + dout[0] = 0x80 | reg; + dout[1] = val; + + spi_xfer (slave, 16, dout, din, SPI_XFER_BEGIN | SPI_XFER_END); +} + +#endif /* end of code exclusion (see #ifdef CONFIG_SXNI855T above) */ + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds1307.c b/qemu/roms/u-boot/drivers/rtc/ds1307.c new file mode 100644 index 000000000..1a2bad3c5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds1307.c @@ -0,0 +1,180 @@ +/* + * (C) Copyright 2001, 2002, 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Keith Outwater, keith_outwater@mvis.com` + * Steven Scholz, steven.scholz@imc-berlin.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support (no alarms) for Dallas Semiconductor (now Maxim) + * DS1307 and DS1338 Real Time Clock (RTC). + * + * based on ds1337.c + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +/*---------------------------------------------------------------------*/ +#undef DEBUG_RTC + +#ifdef DEBUG_RTC +#define DEBUGR(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGR(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + +#ifndef CONFIG_SYS_I2C_RTC_ADDR +# define CONFIG_SYS_I2C_RTC_ADDR 0x68 +#endif + +#if defined(CONFIG_RTC_DS1307) && (CONFIG_SYS_I2C_SPEED > 100000) +# error The DS1307 is specified only up to 100kHz! +#endif + +/* + * RTC register addresses + */ +#define RTC_SEC_REG_ADDR 0x00 +#define RTC_MIN_REG_ADDR 0x01 +#define RTC_HR_REG_ADDR 0x02 +#define RTC_DAY_REG_ADDR 0x03 +#define RTC_DATE_REG_ADDR 0x04 +#define RTC_MON_REG_ADDR 0x05 +#define RTC_YR_REG_ADDR 0x06 +#define RTC_CTL_REG_ADDR 0x07 + +#define RTC_SEC_BIT_CH 0x80 /* Clock Halt (in Register 0) */ + +#define RTC_CTL_BIT_RS0 0x01 /* Rate select 0 */ +#define RTC_CTL_BIT_RS1 0x02 /* Rate select 1 */ +#define RTC_CTL_BIT_SQWE 0x10 /* Square Wave Enable */ +#define RTC_CTL_BIT_OUT 0x80 /* Output Control */ + +static uchar rtc_read (uchar reg); +static void rtc_write (uchar reg, uchar val); + +/* + * Get the current time from the RTC + */ +int rtc_get (struct rtc_time *tmp) +{ + int rel = 0; + uchar sec, min, hour, mday, wday, mon, year; + + sec = rtc_read (RTC_SEC_REG_ADDR); + min = rtc_read (RTC_MIN_REG_ADDR); + hour = rtc_read (RTC_HR_REG_ADDR); + wday = rtc_read (RTC_DAY_REG_ADDR); + mday = rtc_read (RTC_DATE_REG_ADDR); + mon = rtc_read (RTC_MON_REG_ADDR); + year = rtc_read (RTC_YR_REG_ADDR); + + DEBUGR ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon, mday, wday, hour, min, sec); + + if (sec & RTC_SEC_BIT_CH) { + printf ("### Warning: RTC oscillator has stopped\n"); + /* clear the CH flag */ + rtc_write (RTC_SEC_REG_ADDR, + rtc_read (RTC_SEC_REG_ADDR) & ~RTC_SEC_BIT_CH); + rel = -1; + } + + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon & 0x1F); + tmp->tm_year = bcd2bin (year) + ( bcd2bin (year) >= 70 ? 1900 : 2000); + tmp->tm_wday = bcd2bin ((wday - 1) & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + DEBUGR ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return rel; +} + + +/* + * Set the RTC + */ +int rtc_set (struct rtc_time *tmp) +{ + DEBUGR ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + if (tmp->tm_year < 1970 || tmp->tm_year > 2069) + printf("WARNING: year should be between 1970 and 2069!\n"); + + rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100)); + rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon)); + rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday + 1)); + rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday)); + rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour)); + rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min)); + rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec)); + + return 0; +} + + +/* + * Reset the RTC. We setting the date back to 1970-01-01. + * We also enable the oscillator output on the SQW/OUT pin and program + * it for 32,768 Hz output. Note that according to the datasheet, turning + * on the square wave output increases the current drain on the backup + * battery to something between 480nA and 800nA. + */ +void rtc_reset (void) +{ + struct rtc_time tmp; + + rtc_write (RTC_SEC_REG_ADDR, 0x00); /* clearing Clock Halt */ + rtc_write (RTC_CTL_REG_ADDR, RTC_CTL_BIT_SQWE | RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS0); + + tmp.tm_year = 1970; + tmp.tm_mon = 1; + tmp.tm_mday= 1; + tmp.tm_hour = 0; + tmp.tm_min = 0; + tmp.tm_sec = 0; + + rtc_set(&tmp); + + printf ( "RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", + tmp.tm_year, tmp.tm_mon, tmp.tm_mday, + tmp.tm_hour, tmp.tm_min, tmp.tm_sec); + + return; +} + + +/* + * Helper functions + */ + +static +uchar rtc_read (uchar reg) +{ + return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg)); +} + + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); +} +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds1337.c b/qemu/roms/u-boot/drivers/rtc/ds1337.c new file mode 100644 index 000000000..dae1b3c5c --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds1337.c @@ -0,0 +1,195 @@ +/* + * (C) Copyright 2001-2008 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Keith Outwater, keith_outwater@mvis.com` + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support (no alarms) for Dallas Semiconductor (now Maxim) + * DS1337 Real Time Clock (RTC). + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +/* + * RTC register addresses + */ +#if defined CONFIG_RTC_DS1337 +#define RTC_SEC_REG_ADDR 0x0 +#define RTC_MIN_REG_ADDR 0x1 +#define RTC_HR_REG_ADDR 0x2 +#define RTC_DAY_REG_ADDR 0x3 +#define RTC_DATE_REG_ADDR 0x4 +#define RTC_MON_REG_ADDR 0x5 +#define RTC_YR_REG_ADDR 0x6 +#define RTC_CTL_REG_ADDR 0x0e +#define RTC_STAT_REG_ADDR 0x0f +#define RTC_TC_REG_ADDR 0x10 +#elif defined CONFIG_RTC_DS1388 +#define RTC_SEC_REG_ADDR 0x1 +#define RTC_MIN_REG_ADDR 0x2 +#define RTC_HR_REG_ADDR 0x3 +#define RTC_DAY_REG_ADDR 0x4 +#define RTC_DATE_REG_ADDR 0x5 +#define RTC_MON_REG_ADDR 0x6 +#define RTC_YR_REG_ADDR 0x7 +#define RTC_CTL_REG_ADDR 0x0c +#define RTC_STAT_REG_ADDR 0x0b +#define RTC_TC_REG_ADDR 0x0a +#endif + +/* + * RTC control register bits + */ +#define RTC_CTL_BIT_A1IE 0x1 /* Alarm 1 interrupt enable */ +#define RTC_CTL_BIT_A2IE 0x2 /* Alarm 2 interrupt enable */ +#define RTC_CTL_BIT_INTCN 0x4 /* Interrupt control */ +#define RTC_CTL_BIT_RS1 0x8 /* Rate select 1 */ +#define RTC_CTL_BIT_RS2 0x10 /* Rate select 2 */ +#define RTC_CTL_BIT_DOSC 0x80 /* Disable Oscillator */ + +/* + * RTC status register bits + */ +#define RTC_STAT_BIT_A1F 0x1 /* Alarm 1 flag */ +#define RTC_STAT_BIT_A2F 0x2 /* Alarm 2 flag */ +#define RTC_STAT_BIT_OSF 0x80 /* Oscillator stop flag */ + + +static uchar rtc_read (uchar reg); +static void rtc_write (uchar reg, uchar val); + +/* + * Get the current time from the RTC + */ +int rtc_get (struct rtc_time *tmp) +{ + int rel = 0; + uchar sec, min, hour, mday, wday, mon_cent, year, control, status; + + control = rtc_read (RTC_CTL_REG_ADDR); + status = rtc_read (RTC_STAT_REG_ADDR); + sec = rtc_read (RTC_SEC_REG_ADDR); + min = rtc_read (RTC_MIN_REG_ADDR); + hour = rtc_read (RTC_HR_REG_ADDR); + wday = rtc_read (RTC_DAY_REG_ADDR); + mday = rtc_read (RTC_DATE_REG_ADDR); + mon_cent = rtc_read (RTC_MON_REG_ADDR); + year = rtc_read (RTC_YR_REG_ADDR); + + /* No century bit, assume year 2000 */ +#ifdef CONFIG_RTC_DS1388 + mon_cent |= 0x80; +#endif + + debug("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x control: %02x status: %02x\n", + year, mon_cent, mday, wday, hour, min, sec, control, status); + + if (status & RTC_STAT_BIT_OSF) { + printf ("### Warning: RTC oscillator has stopped\n"); + /* clear the OSF flag */ + rtc_write (RTC_STAT_REG_ADDR, + rtc_read (RTC_STAT_REG_ADDR) & ~RTC_STAT_BIT_OSF); + rel = -1; + } + + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon_cent & 0x1F); + tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 2000 : 1900); + tmp->tm_wday = bcd2bin ((wday - 1) & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return rel; +} + + +/* + * Set the RTC + */ +int rtc_set (struct rtc_time *tmp) +{ + uchar century; + + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100)); + + century = (tmp->tm_year >= 2000) ? 0x80 : 0; + rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon) | century); + + rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday + 1)); + rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday)); + rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour)); + rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min)); + rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec)); + + return 0; +} + + +/* + * Reset the RTC. We also enable the oscillator output on the + * SQW/INTB* pin and program it for 32,768 Hz output. Note that + * according to the datasheet, turning on the square wave output + * increases the current drain on the backup battery from about + * 600 nA to 2uA. Define CONFIG_SYS_RTC_DS1337_NOOSC if you wish to turn + * off the OSC output. + */ + +#ifdef CONFIG_SYS_RTC_DS1337_NOOSC + #define RTC_DS1337_RESET_VAL \ + (RTC_CTL_BIT_INTCN | RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS2) +#else + #define RTC_DS1337_RESET_VAL (RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS2) +#endif +void rtc_reset (void) +{ +#ifdef CONFIG_SYS_RTC_DS1337 + rtc_write (RTC_CTL_REG_ADDR, RTC_DS1337_RESET_VAL); +#elif defined CONFIG_SYS_RTC_DS1388 + rtc_write(RTC_CTL_REG_ADDR, 0x0); /* hw default */ +#endif +#ifdef CONFIG_SYS_DS1339_TCR_VAL + rtc_write (RTC_TC_REG_ADDR, CONFIG_SYS_DS1339_TCR_VAL); +#endif +#ifdef CONFIG_SYS_DS1388_TCR_VAL + rtc_write(RTC_TC_REG_ADDR, CONFIG_SYS_DS1388_TCR_VAL); +#endif +} + + +/* + * Helper functions + */ + +static +uchar rtc_read (uchar reg) +{ + return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg)); +} + + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds1374.c b/qemu/roms/u-boot/drivers/rtc/ds1374.c new file mode 100644 index 000000000..427b1eb8d --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds1374.c @@ -0,0 +1,235 @@ +/* + * (C) Copyright 2001, 2002, 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Keith Outwater, keith_outwater@mvis.com` + * Steven Scholz, steven.scholz@imc-berlin.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support (no alarms) for Dallas Semiconductor (now Maxim) + * DS1374 Real Time Clock (RTC). + * + * based on ds1337.c + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +/*---------------------------------------------------------------------*/ +#undef DEBUG_RTC +#define DEBUG_RTC + +#ifdef DEBUG_RTC +#define DEBUGR(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGR(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + +#ifndef CONFIG_SYS_I2C_RTC_ADDR +# define CONFIG_SYS_I2C_RTC_ADDR 0x68 +#endif + +#if defined(CONFIG_RTC_DS1374) && (CONFIG_SYS_I2C_SPEED > 400000) +# error The DS1374 is specified up to 400kHz in fast mode! +#endif + +/* + * RTC register addresses + */ +#define RTC_TOD_CNT_BYTE0_ADDR 0x00 /* TimeOfDay */ +#define RTC_TOD_CNT_BYTE1_ADDR 0x01 +#define RTC_TOD_CNT_BYTE2_ADDR 0x02 +#define RTC_TOD_CNT_BYTE3_ADDR 0x03 + +#define RTC_WD_ALM_CNT_BYTE0_ADDR 0x04 +#define RTC_WD_ALM_CNT_BYTE1_ADDR 0x05 +#define RTC_WD_ALM_CNT_BYTE2_ADDR 0x06 + +#define RTC_CTL_ADDR 0x07 /* RTC-CoNTrol-register */ +#define RTC_SR_ADDR 0x08 /* RTC-StatusRegister */ +#define RTC_TCS_DS_ADDR 0x09 /* RTC-TrickleChargeSelect DiodeSelect-register */ + +#define RTC_CTL_BIT_AIE (1<<0) /* Bit 0 - Alarm Interrupt enable */ +#define RTC_CTL_BIT_RS1 (1<<1) /* Bit 1/2 - Rate Select square wave output */ +#define RTC_CTL_BIT_RS2 (1<<2) /* Bit 2/2 - Rate Select square wave output */ +#define RTC_CTL_BIT_WDSTR (1<<3) /* Bit 3 - Watchdog Reset Steering */ +#define RTC_CTL_BIT_BBSQW (1<<4) /* Bit 4 - Battery-Backed Square-Wave */ +#define RTC_CTL_BIT_WD_ALM (1<<5) /* Bit 5 - Watchdoc/Alarm Counter Select */ +#define RTC_CTL_BIT_WACE (1<<6) /* Bit 6 - Watchdog/Alarm Counter Enable WACE*/ +#define RTC_CTL_BIT_EN_OSC (1<<7) /* Bit 7 - Enable Oscilator */ + +#define RTC_SR_BIT_AF 0x01 /* Bit 0 = Alarm Flag */ +#define RTC_SR_BIT_OSF 0x80 /* Bit 7 - Osc Stop Flag */ + +const char RtcTodAddr[] = { + RTC_TOD_CNT_BYTE0_ADDR, + RTC_TOD_CNT_BYTE1_ADDR, + RTC_TOD_CNT_BYTE2_ADDR, + RTC_TOD_CNT_BYTE3_ADDR +}; + +static uchar rtc_read (uchar reg); +static void rtc_write(uchar reg, uchar val, bool set); +static void rtc_write_raw (uchar reg, uchar val); + +/* + * Get the current time from the RTC + */ +int rtc_get (struct rtc_time *tm){ + int rel = 0; + unsigned long time1, time2; + unsigned int limit; + unsigned char tmp; + unsigned int i; + + /* + * Since the reads are being performed one byte at a time, + * there is a chance that a carry will occur during the read. + * To detect this, 2 reads are performed and compared. + */ + limit = 10; + do { + i = 4; + time1 = 0; + while (i--) { + tmp = rtc_read(RtcTodAddr[i]); + time1 = (time1 << 8) | (tmp & 0xff); + } + + i = 4; + time2 = 0; + while (i--) { + tmp = rtc_read(RtcTodAddr[i]); + time2 = (time2 << 8) | (tmp & 0xff); + } + } while ((time1 != time2) && limit--); + + if (time1 != time2) { + printf("can't get consistent time from rtc chip\n"); + rel = -1; + } + + DEBUGR ("Get RTC s since 1.1.1970: %ld\n", time1); + + to_tm(time1, tm); /* To Gregorian Date */ + + if (rtc_read(RTC_SR_ADDR) & RTC_SR_BIT_OSF) { + printf ("### Warning: RTC oscillator has stopped\n"); + rel = -1; + } + + DEBUGR ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return rel; +} + +/* + * Set the RTC + */ +int rtc_set (struct rtc_time *tmp){ + + unsigned long time; + unsigned i; + + DEBUGR ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + if (tmp->tm_year < 1970 || tmp->tm_year > 2069) + printf("WARNING: year should be between 1970 and 2069!\n"); + + time = mktime(tmp->tm_year, tmp->tm_mon, + tmp->tm_mday, tmp->tm_hour, + tmp->tm_min, tmp->tm_sec); + + DEBUGR ("Set RTC s since 1.1.1970: %ld (0x%02lx)\n", time, time); + + /* write to RTC_TOD_CNT_BYTEn_ADDR */ + for (i = 0; i <= 3; i++) { + rtc_write_raw(RtcTodAddr[i], (unsigned char)(time & 0xff)); + time = time >> 8; + } + + /* Start clock */ + rtc_write(RTC_CTL_ADDR, RTC_CTL_BIT_EN_OSC, false); + + return 0; +} + +/* + * Reset the RTC. We setting the date back to 1970-01-01. + * We also enable the oscillator output on the SQW/OUT pin and program + * it for 32,768 Hz output. Note that according to the datasheet, turning + * on the square wave output increases the current drain on the backup + * battery to something between 480nA and 800nA. + */ +void rtc_reset (void){ + + struct rtc_time tmp; + + /* clear status flags */ + rtc_write(RTC_SR_ADDR, (RTC_SR_BIT_AF|RTC_SR_BIT_OSF), false); /* clearing OSF and AF */ + + /* Initialise DS1374 oriented to MPC8349E-ADS */ + rtc_write (RTC_CTL_ADDR, (RTC_CTL_BIT_EN_OSC + |RTC_CTL_BIT_WACE + |RTC_CTL_BIT_AIE), false);/* start osc, disable WACE, clear AIE + - set to 0 */ + rtc_write (RTC_CTL_ADDR, (RTC_CTL_BIT_WD_ALM + |RTC_CTL_BIT_WDSTR + |RTC_CTL_BIT_RS1 + |RTC_CTL_BIT_RS2 + |RTC_CTL_BIT_BBSQW), true);/* disable WD/ALM, WDSTR set to INT-pin, + set BBSQW and SQW to 32k + - set to 1 */ + tmp.tm_year = 1970; + tmp.tm_mon = 1; + tmp.tm_mday= 1; + tmp.tm_hour = 0; + tmp.tm_min = 0; + tmp.tm_sec = 0; + + rtc_set(&tmp); + + printf("RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", + tmp.tm_year, tmp.tm_mon, tmp.tm_mday, + tmp.tm_hour, tmp.tm_min, tmp.tm_sec); + + rtc_write(RTC_WD_ALM_CNT_BYTE2_ADDR, 0xAC, true); + rtc_write(RTC_WD_ALM_CNT_BYTE1_ADDR, 0xDE, true); + rtc_write(RTC_WD_ALM_CNT_BYTE2_ADDR, 0xAD, true); +} + +/* + * Helper functions + */ +static uchar rtc_read (uchar reg) +{ + return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg)); +} + +static void rtc_write(uchar reg, uchar val, bool set) +{ + if (set == true) { + val |= i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg); + i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); + } else { + val = i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg) & ~val; + i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); + } +} + +static void rtc_write_raw (uchar reg, uchar val) +{ + i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); +} +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds1556.c b/qemu/roms/u-boot/drivers/rtc/ds1556.c new file mode 100644 index 000000000..5b8d5effd --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds1556.c @@ -0,0 +1,180 @@ +/* + * (C) Copyright 2002 + * ARIO Data Networks, Inc. dchiu@ariodata.com + * + * modified for DS1556: + * Frank Panno , Delphin Technology AG + * + * Based on MontaVista DS1743 code and U-Boot mc146818 code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for the DS1556 RTC + */ + +/*#define RTC_DEBUG */ + +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +static uchar rtc_read( unsigned int addr ); +static void rtc_write( unsigned int addr, uchar val); + +#define RTC_BASE ( CONFIG_SYS_NVRAM_SIZE + CONFIG_SYS_NVRAM_BASE_ADDR ) + +#define RTC_YEAR ( RTC_BASE + 0xf ) +#define RTC_MONTH ( RTC_BASE + 0xe ) +#define RTC_DAY_OF_MONTH ( RTC_BASE + 0xd ) +#define RTC_DAY_OF_WEEK ( RTC_BASE + 0xc ) +#define RTC_HOURS ( RTC_BASE + 0xb ) +#define RTC_MINUTES ( RTC_BASE + 0xa ) +#define RTC_SECONDS ( RTC_BASE + 0x9 ) +#define RTC_CENTURY ( RTC_BASE + 0x8 ) + +#define RTC_CONTROLA RTC_CENTURY +#define RTC_CONTROLB RTC_SECONDS +#define RTC_CONTROLC RTC_BASE + +#define RTC_CA_WRITE 0x80 +#define RTC_CA_READ 0x40 + +#define RTC_CB_OSC_DISABLE 0x80 + +#define RTC_CC_BATTERY_FLAG 0x10 +#define RTC_CC_FREQ_TEST 0x40 + +/* ------------------------------------------------------------------------- */ + +int rtc_get( struct rtc_time *tmp ) +{ + uchar sec, min, hour; + uchar mday, wday, mon, year; + + int century; + + uchar reg_a; + + reg_a = rtc_read( RTC_CONTROLA ); + /* lock clock registers for read */ + rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_READ )); + + sec = rtc_read( RTC_SECONDS ); + min = rtc_read( RTC_MINUTES ); + hour = rtc_read( RTC_HOURS ); + mday = rtc_read( RTC_DAY_OF_MONTH ); + wday = rtc_read( RTC_DAY_OF_WEEK ); + mon = rtc_read( RTC_MONTH ); + year = rtc_read( RTC_YEAR ); + century = rtc_read( RTC_CENTURY ); + + /* unlock clock registers after read */ + rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_READ )); + +#ifdef RTC_DEBUG + printf( "Get RTC year: %02x mon/cent: %02x mon: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, century, mon, mday, wday, + hour, min, sec ); +#endif + tmp->tm_sec = bcd2bin( sec & 0x7F ); + tmp->tm_min = bcd2bin( min & 0x7F ); + tmp->tm_hour = bcd2bin( hour & 0x3F ); + tmp->tm_mday = bcd2bin( mday & 0x3F ); + tmp->tm_mon = bcd2bin( mon & 0x1F ); + tmp->tm_wday = bcd2bin( wday & 0x07 ); + + /* glue year from century and year in century */ + tmp->tm_year = bcd2bin( year ) + + ( bcd2bin( century & 0x3F ) * 100 ); + + tmp->tm_yday = 0; + tmp->tm_isdst= 0; +#ifdef RTC_DEBUG + printf( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); +#endif + return 0; +} + +int rtc_set( struct rtc_time *tmp ) +{ + uchar reg_a; +#ifdef RTC_DEBUG + printf( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + /* lock clock registers for write */ + reg_a = rtc_read( RTC_CONTROLA ); + rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_WRITE )); + + rtc_write( RTC_MONTH, bin2bcd( tmp->tm_mon )); + + rtc_write( RTC_DAY_OF_WEEK, bin2bcd( tmp->tm_wday )); + rtc_write( RTC_DAY_OF_MONTH, bin2bcd( tmp->tm_mday )); + rtc_write( RTC_HOURS, bin2bcd( tmp->tm_hour )); + rtc_write( RTC_MINUTES, bin2bcd( tmp->tm_min )); + rtc_write( RTC_SECONDS, bin2bcd( tmp->tm_sec )); + + /* break year up into century and year in century */ + rtc_write( RTC_YEAR, bin2bcd( tmp->tm_year % 100 )); + rtc_write( RTC_CENTURY, bin2bcd( tmp->tm_year / 100 )); + + /* unlock clock registers after read */ + rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_WRITE )); + + return 0; +} + +void rtc_reset (void) +{ + uchar reg_a, reg_b, reg_c; + + reg_a = rtc_read( RTC_CONTROLA ); + reg_b = rtc_read( RTC_CONTROLB ); + + if ( reg_b & RTC_CB_OSC_DISABLE ) + { + printf( "real-time-clock was stopped. Now starting...\n" ); + reg_a |= RTC_CA_WRITE; + reg_b &= ~RTC_CB_OSC_DISABLE; + + rtc_write( RTC_CONTROLA, reg_a ); + rtc_write( RTC_CONTROLB, reg_b ); + } + + /* make sure read/write clock register bits are cleared */ + reg_a &= ~( RTC_CA_WRITE | RTC_CA_READ ); + rtc_write( RTC_CONTROLA, reg_a ); + + reg_c = rtc_read( RTC_CONTROLC ); + if (( reg_c & RTC_CC_BATTERY_FLAG ) == 0 ) + printf( "RTC battery low. Clock setting may not be reliable.\n" ); +} + +/* ------------------------------------------------------------------------- */ + +static uchar rtc_read( unsigned int addr ) +{ + uchar val = *(volatile unsigned char*)(addr); +#ifdef RTC_DEBUG + printf( "rtc_read: %x:%x\n", addr, val ); +#endif + return( val ); +} + +static void rtc_write( unsigned int addr, uchar val ) +{ +#ifdef RTC_DEBUG + printf( "rtc_write: %x:%x\n", addr, val ); +#endif + *(volatile unsigned char*)(addr) = val; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds164x.c b/qemu/roms/u-boot/drivers/rtc/ds164x.c new file mode 100644 index 000000000..b91335442 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds164x.c @@ -0,0 +1,176 @@ +/* + * (C) Copyright 2002 + * ARIO Data Networks, Inc. dchiu@ariodata.com + * + * modified for DS164x: + * The LEOX team , http://www.leox.org + * + * Based on MontaVista DS1743 code and U-Boot mc146818 code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for the DS164x RTC + */ + +/* #define RTC_DEBUG */ + +#include +#include +#include + + +#if defined(CONFIG_CMD_DATE) + +static uchar rtc_read(unsigned int addr ); +static void rtc_write(unsigned int addr, uchar val); + +#define RTC_EPOCH 2000 /* century */ + +/* + * DS164x registers layout + */ +#define RTC_BASE ( CONFIG_SYS_NVRAM_BASE_ADDR + CONFIG_SYS_NVRAM_SIZE ) + +#define RTC_YEAR ( RTC_BASE + 0x07 ) +#define RTC_MONTH ( RTC_BASE + 0x06 ) +#define RTC_DAY_OF_MONTH ( RTC_BASE + 0x05 ) +#define RTC_DAY_OF_WEEK ( RTC_BASE + 0x04 ) +#define RTC_HOURS ( RTC_BASE + 0x03 ) +#define RTC_MINUTES ( RTC_BASE + 0x02 ) +#define RTC_SECONDS ( RTC_BASE + 0x01 ) +#define RTC_CONTROL ( RTC_BASE + 0x00 ) + +#define RTC_CONTROLA RTC_CONTROL /* W=bit6, R=bit5 */ +#define RTC_CA_WRITE 0x80 +#define RTC_CA_READ 0x40 +#define RTC_CONTROLB RTC_SECONDS /* OSC=bit7 */ +#define RTC_CB_OSC_DISABLE 0x80 +#define RTC_CONTROLC RTC_DAY_OF_WEEK /* FT=bit6 */ +#define RTC_CC_FREQ_TEST 0x40 + +/* ------------------------------------------------------------------------- */ + +int rtc_get( struct rtc_time *tmp ) +{ + uchar sec, min, hour; + uchar mday, wday, mon, year; + + uchar reg_a; + + reg_a = rtc_read( RTC_CONTROLA ); + /* lock clock registers for read */ + rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_READ )); + + sec = rtc_read( RTC_SECONDS ); + min = rtc_read( RTC_MINUTES ); + hour = rtc_read( RTC_HOURS ); + mday = rtc_read( RTC_DAY_OF_MONTH ); + wday = rtc_read( RTC_DAY_OF_WEEK ); + mon = rtc_read( RTC_MONTH ); + year = rtc_read( RTC_YEAR ); + + /* unlock clock registers after read */ + rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_READ )); + +#ifdef RTC_DEBUG + printf( "Get RTC year: %02x mon: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon, mday, wday, + hour, min, sec ); +#endif + tmp->tm_sec = bcd2bin( sec & 0x7F ); + tmp->tm_min = bcd2bin( min & 0x7F ); + tmp->tm_hour = bcd2bin( hour & 0x3F ); + tmp->tm_mday = bcd2bin( mday & 0x3F ); + tmp->tm_mon = bcd2bin( mon & 0x1F ); + tmp->tm_wday = bcd2bin( wday & 0x07 ); + + /* glue year in century (2000) */ + tmp->tm_year = bcd2bin( year ) + RTC_EPOCH; + + tmp->tm_yday = 0; + tmp->tm_isdst= 0; +#ifdef RTC_DEBUG + printf( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); +#endif + + return 0; +} + +int rtc_set( struct rtc_time *tmp ) +{ + uchar reg_a; + +#ifdef RTC_DEBUG + printf( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + /* lock clock registers for write */ + reg_a = rtc_read( RTC_CONTROLA ); + rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_WRITE )); + + rtc_write( RTC_MONTH, bin2bcd( tmp->tm_mon )); + + rtc_write( RTC_DAY_OF_WEEK, bin2bcd( tmp->tm_wday )); + rtc_write( RTC_DAY_OF_MONTH, bin2bcd( tmp->tm_mday )); + rtc_write( RTC_HOURS, bin2bcd( tmp->tm_hour )); + rtc_write( RTC_MINUTES, bin2bcd( tmp->tm_min )); + rtc_write( RTC_SECONDS, bin2bcd( tmp->tm_sec )); + + /* break year in century */ + rtc_write( RTC_YEAR, bin2bcd( tmp->tm_year % 100 )); + + /* unlock clock registers after read */ + rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_WRITE )); + + return 0; +} + +void rtc_reset (void) +{ + uchar reg_a, reg_b; + + reg_a = rtc_read( RTC_CONTROLA ); + reg_b = rtc_read( RTC_CONTROLB ); + + if ( reg_b & RTC_CB_OSC_DISABLE ) + { + printf( "real-time-clock was stopped. Now starting...\n" ); + reg_a |= RTC_CA_WRITE; + reg_b &= ~RTC_CB_OSC_DISABLE; + + rtc_write( RTC_CONTROLA, reg_a ); + rtc_write( RTC_CONTROLB, reg_b ); + } + + /* make sure read/write clock register bits are cleared */ + reg_a &= ~( RTC_CA_WRITE | RTC_CA_READ ); + rtc_write( RTC_CONTROLA, reg_a ); +} + +/* ------------------------------------------------------------------------- */ + +static uchar rtc_read( unsigned int addr ) +{ + uchar val = *(volatile unsigned char*)(addr); + +#ifdef RTC_DEBUG + printf( "rtc_read: %x:%x\n", addr, val ); +#endif + return( val ); +} + +static void rtc_write( unsigned int addr, uchar val ) +{ +#ifdef RTC_DEBUG + printf( "rtc_write: %x:%x\n", addr, val ); +#endif + *(volatile unsigned char*)(addr) = val; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds174x.c b/qemu/roms/u-boot/drivers/rtc/ds174x.c new file mode 100644 index 000000000..fc073e075 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds174x.c @@ -0,0 +1,177 @@ +/* + * (C) Copyright 2001 + * ARIO Data Networks, Inc. dchiu@ariodata.com + * + * Based on MontaVista DS1743 code and U-Boot mc146818 code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for the DS174x RTC + */ + +/*#define DEBUG*/ + +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +static uchar rtc_read( unsigned int addr ); +static void rtc_write( unsigned int addr, uchar val); + +#define RTC_BASE ( CONFIG_SYS_NVRAM_SIZE + CONFIG_SYS_NVRAM_BASE_ADDR ) + +#define RTC_YEAR ( RTC_BASE + 7 ) +#define RTC_MONTH ( RTC_BASE + 6 ) +#define RTC_DAY_OF_MONTH ( RTC_BASE + 5 ) +#define RTC_DAY_OF_WEEK ( RTC_BASE + 4 ) +#define RTC_HOURS ( RTC_BASE + 3 ) +#define RTC_MINUTES ( RTC_BASE + 2 ) +#define RTC_SECONDS ( RTC_BASE + 1 ) +#define RTC_CENTURY ( RTC_BASE + 0 ) + +#define RTC_CONTROLA RTC_CENTURY +#define RTC_CONTROLB RTC_SECONDS +#define RTC_CONTROLC RTC_DAY_OF_WEEK + +#define RTC_CA_WRITE 0x80 +#define RTC_CA_READ 0x40 + +#define RTC_CB_OSC_DISABLE 0x80 + +#define RTC_CC_BATTERY_FLAG 0x80 +#define RTC_CC_FREQ_TEST 0x40 + +/* ------------------------------------------------------------------------- */ + +int rtc_get( struct rtc_time *tmp ) +{ + uchar sec, min, hour; + uchar mday, wday, mon, year; + + int century; + + uchar reg_a; + + reg_a = rtc_read( RTC_CONTROLA ); + /* lock clock registers for read */ + rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_READ )); + + sec = rtc_read( RTC_SECONDS ); + min = rtc_read( RTC_MINUTES ); + hour = rtc_read( RTC_HOURS ); + mday = rtc_read( RTC_DAY_OF_MONTH ); + wday = rtc_read( RTC_DAY_OF_WEEK ); + mon = rtc_read( RTC_MONTH ); + year = rtc_read( RTC_YEAR ); + century = rtc_read( RTC_CENTURY ); + + /* unlock clock registers after read */ + rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_READ )); + +#ifdef RTC_DEBUG + printf( "Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon_cent, mday, wday, + hour, min, sec ); +#endif + tmp->tm_sec = bcd2bin( sec & 0x7F ); + tmp->tm_min = bcd2bin( min & 0x7F ); + tmp->tm_hour = bcd2bin( hour & 0x3F ); + tmp->tm_mday = bcd2bin( mday & 0x3F ); + tmp->tm_mon = bcd2bin( mon & 0x1F ); + tmp->tm_wday = bcd2bin( wday & 0x07 ); + + /* glue year from century and year in century */ + tmp->tm_year = bcd2bin( year ) + + ( bcd2bin( century & 0x3F ) * 100 ); + + tmp->tm_yday = 0; + tmp->tm_isdst= 0; +#ifdef RTC_DEBUG + printf( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); +#endif + return 0; +} + +int rtc_set( struct rtc_time *tmp ) +{ + uchar reg_a; +#ifdef RTC_DEBUG + printf( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + /* lock clock registers for write */ + reg_a = rtc_read( RTC_CONTROLA ); + rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_WRITE )); + + rtc_write( RTC_MONTH, bin2bcd( tmp->tm_mon )); + + rtc_write( RTC_DAY_OF_WEEK, bin2bcd( tmp->tm_wday )); + rtc_write( RTC_DAY_OF_MONTH, bin2bcd( tmp->tm_mday )); + rtc_write( RTC_HOURS, bin2bcd( tmp->tm_hour )); + rtc_write( RTC_MINUTES, bin2bcd( tmp->tm_min )); + rtc_write( RTC_SECONDS, bin2bcd( tmp->tm_sec )); + + /* break year up into century and year in century */ + rtc_write( RTC_YEAR, bin2bcd( tmp->tm_year % 100 )); + rtc_write( RTC_CENTURY, bin2bcd( tmp->tm_year / 100 )); + + /* unlock clock registers after read */ + rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_WRITE )); + + return 0; +} + +void rtc_reset (void) +{ + uchar reg_a, reg_b, reg_c; + + reg_a = rtc_read( RTC_CONTROLA ); + reg_b = rtc_read( RTC_CONTROLB ); + + if ( reg_b & RTC_CB_OSC_DISABLE ) + { + printf( "real-time-clock was stopped. Now starting...\n" ); + reg_a |= RTC_CA_WRITE; + reg_b &= ~RTC_CB_OSC_DISABLE; + + rtc_write( RTC_CONTROLA, reg_a ); + rtc_write( RTC_CONTROLB, reg_b ); + } + + /* make sure read/write clock register bits are cleared */ + reg_a &= ~( RTC_CA_WRITE | RTC_CA_READ ); + rtc_write( RTC_CONTROLA, reg_a ); + + reg_c = rtc_read( RTC_CONTROLC ); + if (( reg_c & RTC_CC_BATTERY_FLAG ) == 0 ) + printf( "RTC battery low. Clock setting may not be reliable.\n" ); +} + +/* ------------------------------------------------------------------------- */ + +static uchar rtc_read( unsigned int addr ) +{ + uchar val = in8( addr ); +#ifdef RTC_DEBUG + printf( "rtc_read: %x:%x\n", addr, val ); +#endif + return( val ); +} + +static void rtc_write( unsigned int addr, uchar val ) +{ +#ifdef RTC_DEBUG + printf( "rtc_write: %x:%x\n", addr, val ); +#endif + out8( addr, val ); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ds3231.c b/qemu/roms/u-boot/drivers/rtc/ds3231.c new file mode 100644 index 000000000..c84bbc647 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ds3231.c @@ -0,0 +1,161 @@ +/* + * (C) Copyright 2006 + * Markus Klotzbuecher, mk@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support (no alarms) for Dallas Semiconductor (now Maxim) + * Extremly Accurate DS3231 Real Time Clock (RTC). + * + * copied from ds1337.c + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +/* + * RTC register addresses + */ +#define RTC_SEC_REG_ADDR 0x0 +#define RTC_MIN_REG_ADDR 0x1 +#define RTC_HR_REG_ADDR 0x2 +#define RTC_DAY_REG_ADDR 0x3 +#define RTC_DATE_REG_ADDR 0x4 +#define RTC_MON_REG_ADDR 0x5 +#define RTC_YR_REG_ADDR 0x6 +#define RTC_CTL_REG_ADDR 0x0e +#define RTC_STAT_REG_ADDR 0x0f + + +/* + * RTC control register bits + */ +#define RTC_CTL_BIT_A1IE 0x1 /* Alarm 1 interrupt enable */ +#define RTC_CTL_BIT_A2IE 0x2 /* Alarm 2 interrupt enable */ +#define RTC_CTL_BIT_INTCN 0x4 /* Interrupt control */ +#define RTC_CTL_BIT_RS1 0x8 /* Rate select 1 */ +#define RTC_CTL_BIT_RS2 0x10 /* Rate select 2 */ +#define RTC_CTL_BIT_DOSC 0x80 /* Disable Oscillator */ + +/* + * RTC status register bits + */ +#define RTC_STAT_BIT_A1F 0x1 /* Alarm 1 flag */ +#define RTC_STAT_BIT_A2F 0x2 /* Alarm 2 flag */ +#define RTC_STAT_BIT_OSF 0x80 /* Oscillator stop flag */ + + +static uchar rtc_read (uchar reg); +static void rtc_write (uchar reg, uchar val); + + +/* + * Get the current time from the RTC + */ +int rtc_get (struct rtc_time *tmp) +{ + int rel = 0; + uchar sec, min, hour, mday, wday, mon_cent, year, control, status; + + control = rtc_read (RTC_CTL_REG_ADDR); + status = rtc_read (RTC_STAT_REG_ADDR); + sec = rtc_read (RTC_SEC_REG_ADDR); + min = rtc_read (RTC_MIN_REG_ADDR); + hour = rtc_read (RTC_HR_REG_ADDR); + wday = rtc_read (RTC_DAY_REG_ADDR); + mday = rtc_read (RTC_DATE_REG_ADDR); + mon_cent = rtc_read (RTC_MON_REG_ADDR); + year = rtc_read (RTC_YR_REG_ADDR); + + debug("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x control: %02x status: %02x\n", + year, mon_cent, mday, wday, hour, min, sec, control, status); + + if (status & RTC_STAT_BIT_OSF) { + printf ("### Warning: RTC oscillator has stopped\n"); + /* clear the OSF flag */ + rtc_write (RTC_STAT_REG_ADDR, + rtc_read (RTC_STAT_REG_ADDR) & ~RTC_STAT_BIT_OSF); + rel = -1; + } + + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon_cent & 0x1F); + tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 2000 : 1900); + tmp->tm_wday = bcd2bin ((wday - 1) & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return rel; +} + + +/* + * Set the RTC + */ +int rtc_set (struct rtc_time *tmp) +{ + uchar century; + + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100)); + + century = (tmp->tm_year >= 2000) ? 0x80 : 0; + rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon) | century); + + rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday + 1)); + rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday)); + rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour)); + rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min)); + rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec)); + + return 0; +} + + +/* + * Reset the RTC. We also enable the oscillator output on the + * SQW/INTB* pin and program it for 32,768 Hz output. Note that + * according to the datasheet, turning on the square wave output + * increases the current drain on the backup battery from about + * 600 nA to 2uA. + */ +void rtc_reset (void) +{ + rtc_write (RTC_CTL_REG_ADDR, RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS2); +} + + +/* + * Helper functions + */ + +static +uchar rtc_read (uchar reg) +{ + return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg)); +} + + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/ftrtc010.c b/qemu/roms/u-boot/drivers/rtc/ftrtc010.c new file mode 100644 index 000000000..713dad274 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/ftrtc010.c @@ -0,0 +1,123 @@ +/* + * Faraday FTRTC010 Real Time Clock + * + * (C) Copyright 2009 Faraday Technology + * Po-Yu Chuang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +struct ftrtc010 { + unsigned int sec; /* 0x00 */ + unsigned int min; /* 0x04 */ + unsigned int hour; /* 0x08 */ + unsigned int day; /* 0x0c */ + unsigned int alarm_sec; /* 0x10 */ + unsigned int alarm_min; /* 0x14 */ + unsigned int alarm_hour; /* 0x18 */ + unsigned int record; /* 0x1c */ + unsigned int cr; /* 0x20 */ + unsigned int wsec; /* 0x24 */ + unsigned int wmin; /* 0x28 */ + unsigned int whour; /* 0x2c */ + unsigned int wday; /* 0x30 */ + unsigned int intr; /* 0x34 */ + unsigned int div; /* 0x38 */ + unsigned int rev; /* 0x3c */ +}; + +/* + * RTC Control Register + */ +#define FTRTC010_CR_ENABLE (1 << 0) +#define FTRTC010_CR_INTERRUPT_SEC (1 << 1) /* per second irq */ +#define FTRTC010_CR_INTERRUPT_MIN (1 << 2) /* per minute irq */ +#define FTRTC010_CR_INTERRUPT_HR (1 << 3) /* per hour irq */ +#define FTRTC010_CR_INTERRUPT_DAY (1 << 4) /* per day irq */ + +static struct ftrtc010 *rtc = (struct ftrtc010 *)CONFIG_FTRTC010_BASE; + +static void ftrtc010_enable(void) +{ + writel(FTRTC010_CR_ENABLE, &rtc->cr); +} + +/* + * return current time in seconds + */ +static unsigned long ftrtc010_time(void) +{ + unsigned long day; + unsigned long hour; + unsigned long minute; + unsigned long second; + unsigned long second2; + + do { + second = readl(&rtc->sec); + day = readl(&rtc->day); + hour = readl(&rtc->hour); + minute = readl(&rtc->min); + second2 = readl(&rtc->sec); + } while (second != second2); + + return day * 24 * 60 * 60 + hour * 60 * 60 + minute * 60 + second; +} + +/* + * Get the current time from the RTC + */ + +int rtc_get(struct rtc_time *tmp) +{ + unsigned long now; + + debug("%s(): record register: %x\n", + __func__, readl(&rtc->record)); + +#ifdef CONFIG_FTRTC010_PCLK + now = (ftrtc010_time() + readl(&rtc->record)) / RTC_DIV_COUNT; +#else /* CONFIG_FTRTC010_EXTCLK */ + now = ftrtc010_time() + readl(&rtc->record); +#endif + + to_tm(now, tmp); + + return 0; +} + +/* + * Set the RTC + */ +int rtc_set(struct rtc_time *tmp) +{ + unsigned long new; + unsigned long now; + + debug("%s(): DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + __func__, + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + new = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_hour, + tmp->tm_min, tmp->tm_sec); + + now = ftrtc010_time(); + + debug("%s(): write %lx to record register\n", __func__, new - now); + + writel(new - now, &rtc->record); + + return 0; +} + +void rtc_reset(void) +{ + debug("%s()\n", __func__); + ftrtc010_enable(); +} diff --git a/qemu/roms/u-boot/drivers/rtc/imxdi.c b/qemu/roms/u-boot/drivers/rtc/imxdi.c new file mode 100644 index 000000000..0d7d736ef --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/imxdi.c @@ -0,0 +1,228 @@ +/* + * (C) Copyright 2009-2012 ADVANSEE + * Benoît Thébaudeau + * + * Based on the Linux rtc-imxdi.c driver, which is: + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2010 Orex Computed Radiography + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for Freescale i.MX DryIce RTC + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +#include +#include + +/* DryIce Register Definitions */ + +struct imxdi_regs { + u32 dtcmr; /* Time Counter MSB Reg */ + u32 dtclr; /* Time Counter LSB Reg */ + u32 dcamr; /* Clock Alarm MSB Reg */ + u32 dcalr; /* Clock Alarm LSB Reg */ + u32 dcr; /* Control Reg */ + u32 dsr; /* Status Reg */ + u32 dier; /* Interrupt Enable Reg */ +}; + +#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */ + +#define DCR_TCE (1 << 3) /* Time Counter Enable */ + +#define DSR_WBF (1 << 10) /* Write Busy Flag */ +#define DSR_WNF (1 << 9) /* Write Next Flag */ +#define DSR_WCF (1 << 8) /* Write Complete Flag */ +#define DSR_WEF (1 << 7) /* Write Error Flag */ +#define DSR_CAF (1 << 4) /* Clock Alarm Flag */ +#define DSR_NVF (1 << 1) /* Non-Valid Flag */ +#define DSR_SVF (1 << 0) /* Security Violation Flag */ + +#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */ +#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */ +#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */ +#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */ + +/* Driver Private Data */ + +struct imxdi_data { + struct imxdi_regs __iomem *regs; + int init_done; +}; + +static struct imxdi_data data; + +/* + * This function attempts to clear the dryice write-error flag. + * + * A dryice write error is similar to a bus fault and should not occur in + * normal operation. Clearing the flag requires another write, so the root + * cause of the problem may need to be fixed before the flag can be cleared. + */ +static void clear_write_error(void) +{ + int cnt; + + puts("### Warning: RTC - Register write error!\n"); + + /* clear the write error flag */ + __raw_writel(DSR_WEF, &data.regs->dsr); + + /* wait for it to take effect */ + for (cnt = 0; cnt < 1000; cnt++) { + if ((__raw_readl(&data.regs->dsr) & DSR_WEF) == 0) + return; + udelay(10); + } + puts("### Error: RTC - Cannot clear write-error flag!\n"); +} + +/* + * Write a dryice register and wait until it completes. + * + * Use interrupt flags to determine when the write has completed. + */ +#define DI_WRITE_WAIT(val, reg) \ +( \ + /* do the register write */ \ + __raw_writel((val), &data.regs->reg), \ + \ + di_write_wait((val), #reg) \ +) +static int di_write_wait(u32 val, const char *reg) +{ + int cnt; + int ret = 0; + int rc = 0; + + /* wait for the write to finish */ + for (cnt = 0; cnt < 100; cnt++) { + if ((__raw_readl(&data.regs->dsr) & (DSR_WCF | DSR_WEF)) != 0) { + ret = 1; + break; + } + udelay(10); + } + if (ret == 0) + printf("### Warning: RTC - Write-wait timeout " + "val = 0x%.8x reg = %s\n", val, reg); + + /* check for write error */ + if (__raw_readl(&data.regs->dsr) & DSR_WEF) { + clear_write_error(); + rc = -1; + } + + return rc; +} + +/* + * Initialize dryice hardware + */ +static int di_init(void) +{ + int rc = 0; + + data.regs = (struct imxdi_regs __iomem *)IMX_DRYICE_BASE; + + /* mask all interrupts */ + __raw_writel(0, &data.regs->dier); + + /* put dryice into valid state */ + if (__raw_readl(&data.regs->dsr) & DSR_NVF) { + rc = DI_WRITE_WAIT(DSR_NVF | DSR_SVF, dsr); + if (rc) + goto err; + } + + /* initialize alarm */ + rc = DI_WRITE_WAIT(DCAMR_UNSET, dcamr); + if (rc) + goto err; + rc = DI_WRITE_WAIT(0, dcalr); + if (rc) + goto err; + + /* clear alarm flag */ + if (__raw_readl(&data.regs->dsr) & DSR_CAF) { + rc = DI_WRITE_WAIT(DSR_CAF, dsr); + if (rc) + goto err; + } + + /* the timer won't count if it has never been written to */ + if (__raw_readl(&data.regs->dtcmr) == 0) { + rc = DI_WRITE_WAIT(0, dtcmr); + if (rc) + goto err; + } + + /* start keeping time */ + if (!(__raw_readl(&data.regs->dcr) & DCR_TCE)) { + rc = DI_WRITE_WAIT(__raw_readl(&data.regs->dcr) | DCR_TCE, dcr); + if (rc) + goto err; + } + + data.init_done = 1; + return 0; + +err: + return rc; +} + +int rtc_get(struct rtc_time *tmp) +{ + unsigned long now; + int rc = 0; + + if (!data.init_done) { + rc = di_init(); + if (rc) + goto err; + } + + now = __raw_readl(&data.regs->dtcmr); + to_tm(now, tmp); + +err: + return rc; +} + +int rtc_set(struct rtc_time *tmp) +{ + unsigned long now; + int rc; + + if (!data.init_done) { + rc = di_init(); + if (rc) + goto err; + } + + now = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + /* zero the fractional part first */ + rc = DI_WRITE_WAIT(0, dtclr); + if (rc == 0) + rc = DI_WRITE_WAIT(now, dtcmr); + +err: + return rc; +} + +void rtc_reset(void) +{ + di_init(); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/isl1208.c b/qemu/roms/u-boot/drivers/rtc/isl1208.c new file mode 100644 index 000000000..807e2e404 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/isl1208.c @@ -0,0 +1,147 @@ +/* + * (C) Copyright 2008 + * Tor Krill, Excito Elektronik i Skåne , tor@excito.com + * + * Modelled after the ds1337 driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support (no alarms) for Intersil + * ISL1208 Real Time Clock (RTC). + */ + +#include +#include +#include +#include + +/*---------------------------------------------------------------------*/ +#ifdef DEBUG_RTC +#define DEBUGR(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGR(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + +/* + * RTC register addresses + */ + +#define RTC_SEC_REG_ADDR 0x0 +#define RTC_MIN_REG_ADDR 0x1 +#define RTC_HR_REG_ADDR 0x2 +#define RTC_DATE_REG_ADDR 0x3 +#define RTC_MON_REG_ADDR 0x4 +#define RTC_YR_REG_ADDR 0x5 +#define RTC_DAY_REG_ADDR 0x6 +#define RTC_STAT_REG_ADDR 0x7 +/* + * RTC control register bits + */ + +/* + * RTC status register bits + */ +#define RTC_STAT_BIT_ARST 0x80 /* AUTO RESET ENABLE BIT */ +#define RTC_STAT_BIT_XTOSCB 0x40 /* CRYSTAL OSCILLATOR ENABLE BIT */ +#define RTC_STAT_BIT_WRTC 0x10 /* WRITE RTC ENABLE BIT */ +#define RTC_STAT_BIT_ALM 0x04 /* ALARM BIT */ +#define RTC_STAT_BIT_BAT 0x02 /* BATTERY BIT */ +#define RTC_STAT_BIT_RTCF 0x01 /* REAL TIME CLOCK FAIL BIT */ + +static uchar rtc_read (uchar reg); +static void rtc_write (uchar reg, uchar val); + +/* + * Get the current time from the RTC + */ + +int rtc_get (struct rtc_time *tmp) +{ + int rel = 0; + uchar sec, min, hour, mday, wday, mon, year, status; + + status = rtc_read (RTC_STAT_REG_ADDR); + sec = rtc_read (RTC_SEC_REG_ADDR); + min = rtc_read (RTC_MIN_REG_ADDR); + hour = rtc_read (RTC_HR_REG_ADDR); + wday = rtc_read (RTC_DAY_REG_ADDR); + mday = rtc_read (RTC_DATE_REG_ADDR); + mon = rtc_read (RTC_MON_REG_ADDR); + year = rtc_read (RTC_YR_REG_ADDR); + + DEBUGR ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x status: %02x\n", + year, mon, mday, wday, hour, min, sec, status); + + if (status & RTC_STAT_BIT_RTCF) { + printf ("### Warning: RTC oscillator has stopped\n"); + rtc_write(RTC_STAT_REG_ADDR, + rtc_read(RTC_STAT_REG_ADDR) &~ (RTC_STAT_BIT_BAT|RTC_STAT_BIT_RTCF)); + rel = -1; + } + + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon & 0x1F); + tmp->tm_year = bcd2bin (year)+2000; + tmp->tm_wday = bcd2bin (wday & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + DEBUGR ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return rel; +} + +/* + * Set the RTC + */ +int rtc_set (struct rtc_time *tmp) +{ + DEBUGR ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + /* enable write */ + rtc_write(RTC_STAT_REG_ADDR, + rtc_read(RTC_STAT_REG_ADDR) | RTC_STAT_BIT_WRTC); + + rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100)); + rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon)); + rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday)); + rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday)); + rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour) | 0x80 ); /* 24h clock */ + rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min)); + rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec)); + + /* disable write */ + rtc_write(RTC_STAT_REG_ADDR, + rtc_read(RTC_STAT_REG_ADDR) & ~RTC_STAT_BIT_WRTC); + + return 0; +} + +void rtc_reset (void) +{ +} + +/* + * Helper functions + */ + +static uchar rtc_read (uchar reg) +{ + return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg)); +} + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); +} diff --git a/qemu/roms/u-boot/drivers/rtc/m41t11.c b/qemu/roms/u-boot/drivers/rtc/m41t11.c new file mode 100644 index 000000000..fe0b5fb60 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/m41t11.c @@ -0,0 +1,171 @@ +/* + * (C) Copyright 2002 + * Andrew May, Viasat Inc, amay@viasat.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * M41T11 Serial Access Timekeeper(R) SRAM + * can you believe a trademark on that? + */ + +/* #define DEBUG 1 */ + +#include +#include +#include +#include + +/* + I Don't have an example config file but this + is what should be done. + +#define CONFIG_RTC_M41T11 1 +#define CONFIG_SYS_I2C_RTC_ADDR 0x68 +#if 0 +#define CONFIG_SYS_M41T11_EXT_CENTURY_DATA +#else +#define CONFIG_SYS_M41T11_BASE_YEAR 2000 +#endif +*/ + +#if defined(CONFIG_SYS_I2C_RTC_ADDR) && defined(CONFIG_CMD_DATE) + +/* ------------------------------------------------------------------------- */ +/* + these are simple defines for the chip local to here so they aren't too + verbose + DAY/DATE aren't nice but that is how they are on the data sheet +*/ +#define RTC_SEC_ADDR 0x0 +#define RTC_MIN_ADDR 0x1 +#define RTC_HOUR_ADDR 0x2 +#define RTC_DAY_ADDR 0x3 +#define RTC_DATE_ADDR 0x4 +#define RTC_MONTH_ADDR 0x5 +#define RTC_YEARS_ADDR 0x6 + +#define RTC_REG_CNT 7 + +#define RTC_CONTROL_ADDR 0x7 + + +#ifndef CONFIG_SYS_M41T11_EXT_CENTURY_DATA + +#define REG_CNT (RTC_REG_CNT+1) + +/* + you only get 00-99 for the year we will asume you + want from the year 2000 if you don't set the config +*/ +#ifndef CONFIG_SYS_M41T11_BASE_YEAR +#define CONFIG_SYS_M41T11_BASE_YEAR 2000 +#endif + +#else +/* we will store extra year info in byte 9*/ +#define M41T11_YEAR_DATA 0x8 +#define M41T11_YEAR_SIZE 1 +#define REG_CNT (RTC_REG_CNT+1+M41T11_YEAR_SIZE) +#endif + +#define M41T11_STORAGE_SZ (64-REG_CNT) + +int rtc_get (struct rtc_time *tmp) +{ + int rel = 0; + uchar data[RTC_REG_CNT]; + + i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, data, RTC_REG_CNT); + + if( data[RTC_SEC_ADDR] & 0x80 ){ + printf( "m41t11 RTC Clock stopped!!!\n" ); + rel = -1; + } + tmp->tm_sec = bcd2bin (data[RTC_SEC_ADDR] & 0x7F); + tmp->tm_min = bcd2bin (data[RTC_MIN_ADDR] & 0x7F); + tmp->tm_hour = bcd2bin (data[RTC_HOUR_ADDR] & 0x3F); + tmp->tm_mday = bcd2bin (data[RTC_DATE_ADDR] & 0x3F); + tmp->tm_mon = bcd2bin (data[RTC_MONTH_ADDR]& 0x1F); +#ifndef CONFIG_SYS_M41T11_EXT_CENTURY_DATA + tmp->tm_year = CONFIG_SYS_M41T11_BASE_YEAR + + bcd2bin(data[RTC_YEARS_ADDR]) + + ((data[RTC_HOUR_ADDR]&0x40) ? 100 : 0); +#else + { + unsigned char cent; + i2c_read(CONFIG_SYS_I2C_RTC_ADDR, M41T11_YEAR_DATA, 1, ¢, M41T11_YEAR_SIZE); + if( !(data[RTC_HOUR_ADDR] & 0x80) ){ + printf( "m41t11 RTC: cann't keep track of years without CEB set\n" ); + rel = -1; + } + if( (cent & 0x1) != ((data[RTC_HOUR_ADDR]&0x40)>>7) ){ + /*century flip store off new year*/ + cent += 1; + i2c_write(CONFIG_SYS_I2C_RTC_ADDR, M41T11_YEAR_DATA, 1, ¢, M41T11_YEAR_SIZE); + } + tmp->tm_year =((int)cent*100)+bcd2bin(data[RTC_YEARS_ADDR]); + } +#endif + tmp->tm_wday = bcd2bin (data[RTC_DAY_ADDR] & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return rel; +} + +int rtc_set (struct rtc_time *tmp) +{ + uchar data[RTC_REG_CNT]; + + debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + data[RTC_SEC_ADDR] = bin2bcd(tmp->tm_sec) & 0x7F;/*just in case*/ + data[RTC_MIN_ADDR] = bin2bcd(tmp->tm_min); + data[RTC_HOUR_ADDR] = bin2bcd(tmp->tm_hour) & 0x3F;/*handle cent stuff later*/ + data[RTC_DATE_ADDR] = bin2bcd(tmp->tm_mday) & 0x3F; + data[RTC_MONTH_ADDR] = bin2bcd(tmp->tm_mon); + data[RTC_DAY_ADDR] = bin2bcd(tmp->tm_wday) & 0x07; + + data[RTC_HOUR_ADDR] |= 0x80;/*we will always use CEB*/ + + data[RTC_YEARS_ADDR] = bin2bcd(tmp->tm_year%100);/*same thing either way*/ +#ifndef CONFIG_SYS_M41T11_EXT_CENTURY_DATA + if( ((tmp->tm_year - CONFIG_SYS_M41T11_BASE_YEAR) > 200) || + (tmp->tm_year < CONFIG_SYS_M41T11_BASE_YEAR) ){ + printf( "m41t11 RTC setting year out of range!!need recompile\n" ); + } + data[RTC_HOUR_ADDR] |= (tmp->tm_year - CONFIG_SYS_M41T11_BASE_YEAR) > 100 ? 0x40 : 0; +#else + { + unsigned char cent; + cent = tmp->tm_year ? tmp->tm_year / 100 : 0; + data[RTC_HOUR_ADDR] |= (cent & 0x1) ? 0x40 : 0; + i2c_write(CONFIG_SYS_I2C_RTC_ADDR, M41T11_YEAR_DATA, 1, ¢, M41T11_YEAR_SIZE); + } +#endif + i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, data, RTC_REG_CNT); + + return 0; +} + +void rtc_reset (void) +{ + unsigned char val; + /* clear all control & status registers */ + i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, &val, 1); + val = val & 0x7F;/*make sure we are running*/ + i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, &val, RTC_REG_CNT); + + i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_CONTROL_ADDR, 1, &val, 1); + val = val & 0x3F;/*turn off freq test keep calibration*/ + i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_CONTROL_ADDR, 1, &val, 1); +} +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/m41t60.c b/qemu/roms/u-boot/drivers/rtc/m41t60.c new file mode 100644 index 000000000..95083f0d7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/m41t60.c @@ -0,0 +1,241 @@ +/* + * (C) Copyright 2007 + * Larry Johnson, lrj@acm.org + * + * based on rtc/m41t11.c which is ... + * + * (C) Copyright 2002 + * Andrew May, Viasat Inc, amay@viasat.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * STMicroelectronics M41T60 serial access real-time clock + */ + +/* #define DEBUG 1 */ + +#include +#include +#include +#include + +#if defined(CONFIG_SYS_I2C_RTC_ADDR) && defined(CONFIG_CMD_DATE) + +/* + * Convert between century and "century bits" (CB1 and CB0). These routines + * assume years are in the range 1900 - 2299. + */ + +static unsigned char year2cb(unsigned const year) +{ + if (year < 1900 || year >= 2300) + printf("M41T60 RTC: year %d out of range\n", year); + + return (year / 100) & 0x3; +} + +static unsigned cb2year(unsigned const cb) +{ + return 1900 + 100 * ((cb + 1) & 0x3); +} + +/* + * These are simple defines for the chip local to here so they aren't too + * verbose. DAY/DATE aren't nice but that is how they are on the data sheet. + */ +#define RTC_SEC 0x0 +#define RTC_MIN 0x1 +#define RTC_HOUR 0x2 +#define RTC_DAY 0x3 +#define RTC_DATE 0x4 +#define RTC_MONTH 0x5 +#define RTC_YEAR 0x6 + +#define RTC_REG_CNT 7 + +#define RTC_CTRL 0x7 + +#if defined(DEBUG) +static void rtc_dump(char const *const label) +{ + uchar data[8]; + + if (i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, sizeof(data))) { + printf("I2C read failed in rtc_dump()\n"); + return; + } + printf("RTC dump %s: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", + label, data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7]); +} +#else +#define rtc_dump(label) +#endif + +static uchar *rtc_validate(void) +{ + /* + * This routine uses the OUT bit and the validity of the time values to + * determine whether there has been an initial power-up since the last + * time the routine was run. It assumes that the OUT bit is not being + * used for any other purpose. + */ + static const uchar daysInMonth[0x13] = { + 0x00, 0x31, 0x29, 0x31, 0x30, 0x31, 0x30, 0x31, + 0x31, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x30, 0x31 + }; + static uchar data[8]; + uchar min, date, month, years; + + rtc_dump("begin validate"); + if (i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, sizeof(data))) { + printf("I2C read failed in rtc_validate()\n"); + return 0; + } + /* + * If the OUT bit is "1", there has been a loss of power, so stop the + * oscillator so it can be "kick-started" as per data sheet. + */ + if (0x00 != (data[RTC_CTRL] & 0x80)) { + printf("M41T60 RTC clock lost power.\n"); + data[RTC_SEC] = 0x80; + if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC, 1, data, 1)) { + printf("I2C write failed in rtc_validate()\n"); + return 0; + } + } + /* + * If the oscillator is stopped or the date is invalid, then reset the + * OUT bit to "0", reset the date registers, and start the oscillator. + */ + min = data[RTC_MIN] & 0x7F; + date = data[RTC_DATE]; + month = data[RTC_MONTH] & 0x3F; + years = data[RTC_YEAR]; + if (0x59 < data[RTC_SEC] || 0x09 < (data[RTC_SEC] & 0x0F) || + 0x59 < min || 0x09 < (min & 0x0F) || + 0x23 < data[RTC_HOUR] || 0x09 < (data[RTC_HOUR] & 0x0F) || + 0x07 < data[RTC_DAY] || 0x00 == data[RTC_DAY] || + 0x12 < month || + 0x99 < years || 0x09 < (years & 0x0F) || + daysInMonth[month] < date || 0x09 < (date & 0x0F) || 0x00 == date || + (0x29 == date && 0x02 == month && + ((0x00 != (years & 0x03)) || + (0x00 == years && 0x00 != (data[RTC_MONTH] & 0xC0))))) { + printf("Resetting M41T60 RTC clock.\n"); + /* + * Set to 00:00:00 1900-01-01 (Monday) + */ + data[RTC_SEC] = 0x00; + data[RTC_MIN] &= 0x80; /* preserve OFIE bit */ + data[RTC_HOUR] = 0x00; + data[RTC_DAY] = 0x02; + data[RTC_DATE] = 0x01; + data[RTC_MONTH] = 0xC1; + data[RTC_YEAR] = 0x00; + data[RTC_CTRL] &= 0x7F; /* reset OUT bit */ + + if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, sizeof(data))) { + printf("I2C write failed in rtc_validate()\n"); + return 0; + } + } + return data; +} + +int rtc_get(struct rtc_time *tmp) +{ + uchar const *const data = rtc_validate(); + + if (!data) + return -1; + + tmp->tm_sec = bcd2bin(data[RTC_SEC] & 0x7F); + tmp->tm_min = bcd2bin(data[RTC_MIN] & 0x7F); + tmp->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3F); + tmp->tm_mday = bcd2bin(data[RTC_DATE] & 0x3F); + tmp->tm_mon = bcd2bin(data[RTC_MONTH] & 0x1F); + tmp->tm_year = cb2year(data[RTC_MONTH] >> 6) + bcd2bin(data[RTC_YEAR]); + tmp->tm_wday = bcd2bin(data[RTC_DAY] & 0x07) - 1; + tmp->tm_yday = 0; + tmp->tm_isdst = 0; + + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +int rtc_set(struct rtc_time *tmp) +{ + uchar *const data = rtc_validate(); + + if (!data) + return -1; + + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + data[RTC_SEC] = (data[RTC_SEC] & 0x80) | (bin2bcd(tmp->tm_sec) & 0x7F); + data[RTC_MIN] = (data[RTC_MIN] & 0X80) | (bin2bcd(tmp->tm_min) & 0X7F); + data[RTC_HOUR] = bin2bcd(tmp->tm_hour) & 0x3F; + data[RTC_DATE] = bin2bcd(tmp->tm_mday) & 0x3F; + data[RTC_MONTH] = bin2bcd(tmp->tm_mon) & 0x1F; + data[RTC_YEAR] = bin2bcd(tmp->tm_year % 100); + data[RTC_MONTH] |= year2cb(tmp->tm_year) << 6; + data[RTC_DAY] = bin2bcd(tmp->tm_wday + 1) & 0x07; + if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, RTC_REG_CNT)) { + printf("I2C write failed in rtc_set()\n"); + return -1; + } + + return 0; +} + +void rtc_reset(void) +{ + uchar *const data = rtc_validate(); + char const *const s = getenv("rtccal"); + + if (!data) + return; + + rtc_dump("begin reset"); + /* + * If environmental variable "rtccal" is present, it must be a hex value + * between 0x00 and 0x3F, inclusive. The five least-significan bits + * represent the calibration magnitude, and the sixth bit the sign bit. + * If these do not match the contents of the hardware register, that + * register is updated. The value 0x00 imples no correction. Consult + * the M41T60 documentation for further details. + */ + if (s) { + unsigned long const l = simple_strtoul(s, 0, 16); + + if (l <= 0x3F) { + if ((data[RTC_CTRL] & 0x3F) != l) { + printf("Setting RTC calibration to 0x%02lX\n", + l); + data[RTC_CTRL] &= 0xC0; + data[RTC_CTRL] |= (uchar) l; + } + } else + printf("environment parameter \"rtccal\" not valid: " + "ignoring\n"); + } + /* + * Turn off frequency test. + */ + data[RTC_CTRL] &= 0xBF; + if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_CTRL, 1, data + RTC_CTRL, 1)) { + printf("I2C write failed in rtc_reset()\n"); + return; + } + rtc_dump("end reset"); +} +#endif /* CONFIG_RTC_M41T60 && CONFIG_SYS_I2C_RTC_ADDR && CONFIG_CMD_DATE */ diff --git a/qemu/roms/u-boot/drivers/rtc/m41t62.c b/qemu/roms/u-boot/drivers/rtc/m41t62.c new file mode 100644 index 000000000..3829bc54e --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/m41t62.c @@ -0,0 +1,135 @@ +/* + * (C) Copyright 2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * based on a the Linux rtc-m41t80.c driver which is: + * Alexander Bigga , 2006 (c) mycable GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for STMicroelectronics M41T62 + */ + +/* #define DEBUG */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +#define M41T62_REG_SSEC 0 +#define M41T62_REG_SEC 1 +#define M41T62_REG_MIN 2 +#define M41T62_REG_HOUR 3 +#define M41T62_REG_WDAY 4 +#define M41T62_REG_DAY 5 +#define M41T62_REG_MON 6 +#define M41T62_REG_YEAR 7 +#define M41T62_REG_ALARM_MON 0xa +#define M41T62_REG_ALARM_DAY 0xb +#define M41T62_REG_ALARM_HOUR 0xc +#define M41T62_REG_ALARM_MIN 0xd +#define M41T62_REG_ALARM_SEC 0xe +#define M41T62_REG_FLAGS 0xf + +#define M41T62_DATETIME_REG_SIZE (M41T62_REG_YEAR + 1) +#define M41T62_ALARM_REG_SIZE \ + (M41T62_REG_ALARM_SEC + 1 - M41T62_REG_ALARM_MON) + +#define M41T62_SEC_ST (1 << 7) /* ST: Stop Bit */ +#define M41T62_ALMON_AFE (1 << 7) /* AFE: AF Enable Bit */ +#define M41T62_ALMON_SQWE (1 << 6) /* SQWE: SQW Enable Bit */ +#define M41T62_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */ +#define M41T62_FLAGS_AF (1 << 6) /* AF: Alarm Flag Bit */ +#define M41T62_FLAGS_BATT_LOW (1 << 4) /* BL: Battery Low Bit */ + +#define M41T62_FEATURE_HT (1 << 0) +#define M41T62_FEATURE_BL (1 << 1) + +#define M41T80_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */ + +int rtc_get(struct rtc_time *tm) +{ + u8 buf[M41T62_DATETIME_REG_SIZE]; + + i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, buf, M41T62_DATETIME_REG_SIZE); + + debug("%s: raw read data - sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n", + __FUNCTION__, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + tm->tm_sec = bcd2bin(buf[M41T62_REG_SEC] & 0x7f); + tm->tm_min = bcd2bin(buf[M41T62_REG_MIN] & 0x7f); + tm->tm_hour = bcd2bin(buf[M41T62_REG_HOUR] & 0x3f); + tm->tm_mday = bcd2bin(buf[M41T62_REG_DAY] & 0x3f); + tm->tm_wday = buf[M41T62_REG_WDAY] & 0x07; + tm->tm_mon = bcd2bin(buf[M41T62_REG_MON] & 0x1f); + + /* assume 20YY not 19YY, and ignore the Century Bit */ + /* U-Boot needs to add 1900 here */ + tm->tm_year = bcd2bin(buf[M41T62_REG_YEAR]) + 100 + 1900; + + debug("%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +int rtc_set(struct rtc_time *tm) +{ + u8 buf[M41T62_DATETIME_REG_SIZE]; + + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, buf, M41T62_DATETIME_REG_SIZE); + + /* Merge time-data and register flags into buf[0..7] */ + buf[M41T62_REG_SSEC] = 0; + buf[M41T62_REG_SEC] = + bin2bcd(tm->tm_sec) | (buf[M41T62_REG_SEC] & ~0x7f); + buf[M41T62_REG_MIN] = + bin2bcd(tm->tm_min) | (buf[M41T62_REG_MIN] & ~0x7f); + buf[M41T62_REG_HOUR] = + bin2bcd(tm->tm_hour) | (buf[M41T62_REG_HOUR] & ~0x3f) ; + buf[M41T62_REG_WDAY] = + (tm->tm_wday & 0x07) | (buf[M41T62_REG_WDAY] & ~0x07); + buf[M41T62_REG_DAY] = + bin2bcd(tm->tm_mday) | (buf[M41T62_REG_DAY] & ~0x3f); + buf[M41T62_REG_MON] = + bin2bcd(tm->tm_mon) | (buf[M41T62_REG_MON] & ~0x1f); + /* assume 20YY not 19YY */ + buf[M41T62_REG_YEAR] = bin2bcd(tm->tm_year % 100); + + if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, buf, M41T62_DATETIME_REG_SIZE)) { + printf("I2C write failed in %s()\n", __func__); + return -1; + } + + return 0; +} + +void rtc_reset(void) +{ + u8 val; + + /* + * M41T82: Make sure HT (Halt Update) bit is cleared. + * This bit is 0 in M41T62 so its save to clear it always. + */ + i2c_read(CONFIG_SYS_I2C_RTC_ADDR, M41T62_REG_ALARM_HOUR, 1, &val, 1); + val &= ~M41T80_ALHOUR_HT; + i2c_write(CONFIG_SYS_I2C_RTC_ADDR, M41T62_REG_ALARM_HOUR, 1, &val, 1); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/m41t94.c b/qemu/roms/u-boot/drivers/rtc/m41t94.c new file mode 100644 index 000000000..5b665bb01 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/m41t94.c @@ -0,0 +1,123 @@ +/* + * Driver for ST M41T94 SPI RTC + * + * Taken from the Linux kernel drivier: + * Copyright (C) 2008 Kim B. Heino + * + * Adaptation for U-Boot: + * Copyright (C) 2009 + * Albin Tonnerre, Free Electrons + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +static struct spi_slave *slave; + +#define M41T94_REG_SECONDS 0x01 +#define M41T94_REG_MINUTES 0x02 +#define M41T94_REG_HOURS 0x03 +#define M41T94_REG_WDAY 0x04 +#define M41T94_REG_DAY 0x05 +#define M41T94_REG_MONTH 0x06 +#define M41T94_REG_YEAR 0x07 +#define M41T94_REG_HT 0x0c + +#define M41T94_BIT_HALT 0x40 +#define M41T94_BIT_STOP 0x80 +#define M41T94_BIT_CB 0x40 +#define M41T94_BIT_CEB 0x80 + +int rtc_set(struct rtc_time *tm) +{ + u8 buf[8]; /* write cmd + 7 registers */ + int ret; + + if (!slave) { + slave = spi_setup_slave(CONFIG_M41T94_SPI_BUS, + CONFIG_M41T94_SPI_CS, 1000000, + SPI_MODE_3); + if (!slave) + return -1; + } + spi_claim_bus(slave); + + buf[0] = 0x80 | M41T94_REG_SECONDS; /* write time + date */ + buf[M41T94_REG_SECONDS] = bin2bcd(tm->tm_sec); + buf[M41T94_REG_MINUTES] = bin2bcd(tm->tm_min); + buf[M41T94_REG_HOURS] = bin2bcd(tm->tm_hour); + buf[M41T94_REG_WDAY] = bin2bcd(tm->tm_wday + 1); + buf[M41T94_REG_DAY] = bin2bcd(tm->tm_mday); + buf[M41T94_REG_MONTH] = bin2bcd(tm->tm_mon + 1); + + buf[M41T94_REG_HOURS] |= M41T94_BIT_CEB; + if (tm->tm_year >= 100) + buf[M41T94_REG_HOURS] |= M41T94_BIT_CB; + buf[M41T94_REG_YEAR] = bin2bcd(tm->tm_year % 100); + + ret = spi_xfer(slave, 64, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END); + spi_release_bus(slave); + return ret; +} + +int rtc_get(struct rtc_time *tm) +{ + u8 buf[2]; + int ret, hour; + + if (!slave) { + slave = spi_setup_slave(CONFIG_M41T94_SPI_BUS, + CONFIG_M41T94_SPI_CS, 1000000, + SPI_MODE_3); + if (!slave) + return -1; + } + spi_claim_bus(slave); + + /* clear halt update bit */ + ret = spi_w8r8(slave, M41T94_REG_HT); + if (ret < 0) + return ret; + if (ret & M41T94_BIT_HALT) { + buf[0] = 0x80 | M41T94_REG_HT; + buf[1] = ret & ~M41T94_BIT_HALT; + spi_xfer(slave, 16, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END); + } + + /* clear stop bit */ + ret = spi_w8r8(slave, M41T94_REG_SECONDS); + if (ret < 0) + return ret; + if (ret & M41T94_BIT_STOP) { + buf[0] = 0x80 | M41T94_REG_SECONDS; + buf[1] = ret & ~M41T94_BIT_STOP; + spi_xfer(slave, 16, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END); + } + + tm->tm_sec = bcd2bin(spi_w8r8(slave, M41T94_REG_SECONDS)); + tm->tm_min = bcd2bin(spi_w8r8(slave, M41T94_REG_MINUTES)); + hour = spi_w8r8(slave, M41T94_REG_HOURS); + tm->tm_hour = bcd2bin(hour & 0x3f); + tm->tm_wday = bcd2bin(spi_w8r8(slave, M41T94_REG_WDAY)) - 1; + tm->tm_mday = bcd2bin(spi_w8r8(slave, M41T94_REG_DAY)); + tm->tm_mon = bcd2bin(spi_w8r8(slave, M41T94_REG_MONTH)) - 1; + tm->tm_year = bcd2bin(spi_w8r8(slave, M41T94_REG_YEAR)); + if ((hour & M41T94_BIT_CB) || !(hour & M41T94_BIT_CEB)) + tm->tm_year += 100; + + spi_release_bus(slave); + return 0; +} + +void rtc_reset(void) +{ + /* + * Could not be tested as the reset pin is not wired on + * the sbc35-ag20 board + */ +} diff --git a/qemu/roms/u-boot/drivers/rtc/m48t35ax.c b/qemu/roms/u-boot/drivers/rtc/m48t35ax.c new file mode 100644 index 000000000..021b91f73 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/m48t35ax.c @@ -0,0 +1,142 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for ST Electronics M48T35Ax RTC + */ + +/*#define DEBUG */ + + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +static uchar rtc_read (uchar reg); +static void rtc_write (uchar reg, uchar val); + +/* ------------------------------------------------------------------------- */ + +int rtc_get (struct rtc_time *tmp) +{ + uchar sec, min, hour, cent_day, date, month, year; + uchar ccr; /* Clock control register */ + + /* Lock RTC for read using clock control register */ + ccr = rtc_read(0); + ccr = ccr | 0x40; + rtc_write(0, ccr); + + sec = rtc_read (0x1); + min = rtc_read (0x2); + hour = rtc_read (0x3); + cent_day= rtc_read (0x4); + date = rtc_read (0x5); + month = rtc_read (0x6); + year = rtc_read (0x7); + + /* UNLock RTC */ + ccr = rtc_read(0); + ccr = ccr & 0xBF; + rtc_write(0, ccr); + + debug ( "Get RTC year: %02x month: %02x date: %02x cent_day: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, month, date, cent_day, + hour, min, sec ); + + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (date & 0x3F); + tmp->tm_mon = bcd2bin (month & 0x1F); + tmp->tm_year = bcd2bin (year) + ((cent_day & 0x10) ? 2000 : 1900); + tmp->tm_wday = bcd2bin (cent_day & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +int rtc_set (struct rtc_time *tmp) +{ + uchar ccr; /* Clock control register */ + uchar century; + + debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + /* Lock RTC for write using clock control register */ + ccr = rtc_read(0); + ccr = ccr | 0x80; + rtc_write(0, ccr); + + rtc_write (0x07, bin2bcd(tmp->tm_year % 100)); + rtc_write (0x06, bin2bcd(tmp->tm_mon)); + rtc_write (0x05, bin2bcd(tmp->tm_mday)); + + century = ((tmp->tm_year >= 2000) ? 0x10 : 0) | 0x20; + rtc_write (0x04, bin2bcd(tmp->tm_wday) | century); + + rtc_write (0x03, bin2bcd(tmp->tm_hour)); + rtc_write (0x02, bin2bcd(tmp->tm_min )); + rtc_write (0x01, bin2bcd(tmp->tm_sec )); + + /* UNLock RTC */ + ccr = rtc_read(0); + ccr = ccr & 0x7F; + rtc_write(0, ccr); + + return 0; +} + +void rtc_reset (void) +{ + uchar val; + + /* Clear all clock control registers */ + rtc_write (0x0, 0x80); /* No Read Lock or calibration */ + + /* Clear stop bit */ + val = rtc_read (0x1); + val &= 0x7f; + rtc_write(0x1, val); + + /* Enable century / disable frequency test */ + val = rtc_read (0x4); + val = (val & 0xBF) | 0x20; + rtc_write(0x4, val); + + /* Clear write lock */ + rtc_write(0x0, 0); +} + +/* ------------------------------------------------------------------------- */ + +static uchar rtc_read (uchar reg) +{ + uchar val; + val = *(unsigned char *) + ((CONFIG_SYS_NVRAM_BASE_ADDR + CONFIG_SYS_NVRAM_SIZE - 8) + reg); + return val; +} + +static void rtc_write (uchar reg, uchar val) +{ + *(unsigned char *) + ((CONFIG_SYS_NVRAM_BASE_ADDR + CONFIG_SYS_NVRAM_SIZE - 8) + reg) = val; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/max6900.c b/qemu/roms/u-boot/drivers/rtc/max6900.c new file mode 100644 index 000000000..48ad0a0eb --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/max6900.c @@ -0,0 +1,109 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for MAXIM MAX6900 RTC + */ + +/* #define DEBUG */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +#ifndef CONFIG_SYS_I2C_RTC_ADDR +#define CONFIG_SYS_I2C_RTC_ADDR 0x50 +#endif + +/* ------------------------------------------------------------------------- */ + +static uchar rtc_read (uchar reg) +{ + return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg)); +} + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); + udelay(2500); +} + +/* ------------------------------------------------------------------------- */ + +int rtc_get (struct rtc_time *tmp) +{ + uchar sec, min, hour, mday, wday, mon, cent, year; + int retry = 1; + + do { + sec = rtc_read (0x80); + min = rtc_read (0x82); + hour = rtc_read (0x84); + mday = rtc_read (0x86); + mon = rtc_read (0x88); + wday = rtc_read (0x8a); + year = rtc_read (0x8c); + cent = rtc_read (0x92); + /* + * Check for seconds rollover + */ + if ((sec != 59) || (rtc_read(0x80) == sec)){ + retry = 0; + } + } while (retry); + + debug ( "Get RTC year: %02x mon: %02x cent: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon, cent, mday, wday, + hour, min, sec ); + + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon & 0x1F); + tmp->tm_year = bcd2bin (year) + bcd2bin(cent) * 100; + tmp->tm_wday = bcd2bin (wday & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +int rtc_set (struct rtc_time *tmp) +{ + + debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + rtc_write (0x9E, 0x00); + rtc_write (0x80, 0); /* Clear seconds to ensure no rollover */ + rtc_write (0x92, bin2bcd(tmp->tm_year / 100)); + rtc_write (0x8c, bin2bcd(tmp->tm_year % 100)); + rtc_write (0x8a, bin2bcd(tmp->tm_wday)); + rtc_write (0x88, bin2bcd(tmp->tm_mon)); + rtc_write (0x86, bin2bcd(tmp->tm_mday)); + rtc_write (0x84, bin2bcd(tmp->tm_hour)); + rtc_write (0x82, bin2bcd(tmp->tm_min )); + rtc_write (0x80, bin2bcd(tmp->tm_sec )); + + return 0; +} + +void rtc_reset (void) +{ +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/mc13xxx-rtc.c b/qemu/roms/u-boot/drivers/rtc/mc13xxx-rtc.c new file mode 100644 index 000000000..528247ac8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mc13xxx-rtc.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008, Guennadi Liakhovetski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +int rtc_get(struct rtc_time *rtc) +{ + u32 day1, day2, time; + int tim, i = 0; + struct pmic *p = pmic_get("FSL_PMIC"); + int ret; + + if (!p) + return -1; + do { + ret = pmic_reg_read(p, REG_RTC_DAY, &day1); + if (ret < 0) + return -1; + + ret = pmic_reg_read(p, REG_RTC_TIME, &time); + if (ret < 0) + return -1; + + ret = pmic_reg_read(p, REG_RTC_DAY, &day2); + if (ret < 0) + return -1; + + } while (day1 != day2 && i++ < 3); + + tim = day1 * 86400 + time; + + to_tm(tim, rtc); + + rtc->tm_yday = 0; + rtc->tm_isdst = 0; + + return 0; +} + +int rtc_set(struct rtc_time *rtc) +{ + u32 time, day; + struct pmic *p = pmic_get("FSL_PMIC"); + if (!p) + return -1; + + time = mktime(rtc->tm_year, rtc->tm_mon, rtc->tm_mday, + rtc->tm_hour, rtc->tm_min, rtc->tm_sec); + day = time / 86400; + time %= 86400; + + pmic_reg_write(p, REG_RTC_DAY, day); + pmic_reg_write(p, REG_RTC_TIME, time); + + return 0; +} + +void rtc_reset(void) +{ +} diff --git a/qemu/roms/u-boot/drivers/rtc/mc146818.c b/qemu/roms/u-boot/drivers/rtc/mc146818.c new file mode 100644 index 000000000..f7cf1064f --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mc146818.c @@ -0,0 +1,152 @@ +/* + * (C) Copyright 2001 + * Denis Peter MPL AG Switzerland. d.peter@mpl.ch + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for the MC146818 (PIXX4) RTC + */ + +/*#define DEBUG*/ + +#include +#include +#include + +#if defined(__I386__) || defined(CONFIG_MALTA) +#include +#define in8(p) inb(p) +#define out8(p, v) outb(v, p) +#endif + +#if defined(CONFIG_CMD_DATE) + +static uchar rtc_read (uchar reg); +static void rtc_write (uchar reg, uchar val); + +#define RTC_PORT_MC146818 CONFIG_SYS_ISA_IO_BASE_ADDRESS + 0x70 +#define RTC_SECONDS 0x00 +#define RTC_SECONDS_ALARM 0x01 +#define RTC_MINUTES 0x02 +#define RTC_MINUTES_ALARM 0x03 +#define RTC_HOURS 0x04 +#define RTC_HOURS_ALARM 0x05 +#define RTC_DAY_OF_WEEK 0x06 +#define RTC_DATE_OF_MONTH 0x07 +#define RTC_MONTH 0x08 +#define RTC_YEAR 0x09 +#define RTC_CONFIG_A 0x0A +#define RTC_CONFIG_B 0x0B +#define RTC_CONFIG_C 0x0C +#define RTC_CONFIG_D 0x0D + + +/* ------------------------------------------------------------------------- */ + +int rtc_get (struct rtc_time *tmp) +{ + uchar sec, min, hour, mday, wday, mon, year; + /* here check if rtc can be accessed */ + while((rtc_read(RTC_CONFIG_A)&0x80)==0x80); + sec = rtc_read (RTC_SECONDS); + min = rtc_read (RTC_MINUTES); + hour = rtc_read (RTC_HOURS); + mday = rtc_read (RTC_DATE_OF_MONTH); + wday = rtc_read (RTC_DAY_OF_WEEK); + mon = rtc_read (RTC_MONTH); + year = rtc_read (RTC_YEAR); +#ifdef RTC_DEBUG + printf ( "Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon, mday, wday, + hour, min, sec ); + printf ( "Alarms: month: %02x hour: %02x min: %02x sec: %02x\n", + rtc_read (RTC_CONFIG_D) & 0x3F, + rtc_read (RTC_HOURS_ALARM), + rtc_read (RTC_MINUTES_ALARM), + rtc_read (RTC_SECONDS_ALARM) ); +#endif + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon & 0x1F); + tmp->tm_year = bcd2bin (year); + tmp->tm_wday = bcd2bin (wday & 0x07); + if(tmp->tm_year<70) + tmp->tm_year+=2000; + else + tmp->tm_year+=1900; + tmp->tm_yday = 0; + tmp->tm_isdst= 0; +#ifdef RTC_DEBUG + printf ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + + return 0; +} + +int rtc_set (struct rtc_time *tmp) +{ +#ifdef RTC_DEBUG + printf ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + rtc_write(RTC_CONFIG_B,0x82); /* disables the RTC to update the regs */ + + rtc_write (RTC_YEAR, bin2bcd(tmp->tm_year % 100)); + rtc_write (RTC_MONTH, bin2bcd(tmp->tm_mon)); + rtc_write (RTC_DAY_OF_WEEK, bin2bcd(tmp->tm_wday)); + rtc_write (RTC_DATE_OF_MONTH, bin2bcd(tmp->tm_mday)); + rtc_write (RTC_HOURS, bin2bcd(tmp->tm_hour)); + rtc_write (RTC_MINUTES, bin2bcd(tmp->tm_min )); + rtc_write (RTC_SECONDS, bin2bcd(tmp->tm_sec )); + rtc_write(RTC_CONFIG_B,0x02); /* enables the RTC to update the regs */ + + return 0; +} + +void rtc_reset (void) +{ + rtc_write(RTC_CONFIG_B,0x82); /* disables the RTC to update the regs */ + rtc_write(RTC_CONFIG_A,0x20); /* Normal OP */ + rtc_write(RTC_CONFIG_B,0x00); + rtc_write(RTC_CONFIG_B,0x00); + rtc_write(RTC_CONFIG_B,0x02); /* enables the RTC to update the regs */ +} + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_SYS_RTC_REG_BASE_ADDR +/* + * use direct memory access + */ +static uchar rtc_read (uchar reg) +{ + return(in8(CONFIG_SYS_RTC_REG_BASE_ADDR+reg)); +} + +static void rtc_write (uchar reg, uchar val) +{ + out8(CONFIG_SYS_RTC_REG_BASE_ADDR+reg, val); +} +#else +static uchar rtc_read (uchar reg) +{ + out8(RTC_PORT_MC146818,reg); + return(in8(RTC_PORT_MC146818+1)); +} + +static void rtc_write (uchar reg, uchar val) +{ + out8(RTC_PORT_MC146818,reg); + out8(RTC_PORT_MC146818+1,val); +} +#endif + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/mcfrtc.c b/qemu/roms/u-boot/drivers/rtc/mcfrtc.c new file mode 100644 index 000000000..8961ca4f8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mcfrtc.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#if defined(CONFIG_CMD_DATE) + +#include +#include +#include +#include + +#undef RTC_DEBUG + +#ifndef CONFIG_SYS_MCFRTC_BASE +#error RTC_BASE is not defined! +#endif + +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) +#define STARTOFTIME 1970 + +int rtc_get(struct rtc_time *tmp) +{ + volatile rtc_t *rtc = (rtc_t *) (CONFIG_SYS_MCFRTC_BASE); + + int rtc_days, rtc_hrs, rtc_mins; + int tim; + + rtc_days = rtc->days; + rtc_hrs = rtc->hourmin >> 8; + rtc_mins = RTC_HOURMIN_MINUTES(rtc->hourmin); + + tim = (rtc_days * 24) + rtc_hrs; + tim = (tim * 60) + rtc_mins; + tim = (tim * 60) + rtc->seconds; + + to_tm(tim, tmp); + + tmp->tm_yday = 0; + tmp->tm_isdst = 0; + +#ifdef RTC_DEBUG + printf("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + + return 0; +} + +int rtc_set(struct rtc_time *tmp) +{ + volatile rtc_t *rtc = (rtc_t *) (CONFIG_SYS_MCFRTC_BASE); + + static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + int days, i, months; + + if (tmp->tm_year > 2037) { + printf("Unable to handle. Exceeding integer limitation!\n"); + tmp->tm_year = 2027; + } +#ifdef RTC_DEBUG + printf("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + + /* calculate days by years */ + for (i = STARTOFTIME, days = 0; i < tmp->tm_year; i++) { + days += 365 + isleap(i); + } + + /* calculate days by months */ + months = tmp->tm_mon - 1; + for (i = 0; i < months; i++) { + days += month_days[i]; + + if (i == 1) + days += isleap(i); + } + + days += tmp->tm_mday - 1; + + rtc->days = days; + rtc->hourmin = (tmp->tm_hour << 8) | tmp->tm_min; + rtc->seconds = tmp->tm_sec; + + return 0; +} + +void rtc_reset(void) +{ + volatile rtc_t *rtc = (rtc_t *) (CONFIG_SYS_MCFRTC_BASE); + + if ((rtc->cr & RTC_CR_EN) == 0) { + printf("real-time-clock was stopped. Now starting...\n"); + rtc->cr |= RTC_CR_EN; + } + + rtc->cr |= RTC_CR_SWR; +} + +#endif /* CONFIG_MCFRTC && CONFIG_CMD_DATE */ diff --git a/qemu/roms/u-boot/drivers/rtc/mk48t59.c b/qemu/roms/u-boot/drivers/rtc/mk48t59.c new file mode 100644 index 000000000..2164580ef --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mk48t59.c @@ -0,0 +1,180 @@ +/* + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH + * Andreas Heppel + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for the MK48T59 RTC + */ + +#undef RTC_DEBUG + +#include +#include +#include +#include +#include + +#if defined(CONFIG_BAB7xx) + +static uchar rtc_read (short reg) +{ + out8(RTC_PORT_ADDR0, reg & 0xFF); + out8(RTC_PORT_ADDR1, (reg>>8) & 0xFF); + return in8(RTC_PORT_DATA); +} + +static void rtc_write (short reg, uchar val) +{ + out8(RTC_PORT_ADDR0, reg & 0xFF); + out8(RTC_PORT_ADDR1, (reg>>8) & 0xFF); + out8(RTC_PORT_DATA, val); +} + +#elif defined(CONFIG_EVAL5200) + +static uchar rtc_read (short reg) +{ + return in8(RTC(reg)); +} + +static void rtc_write (short reg, uchar val) +{ + out8(RTC(reg),val); +} + +#else +# error Board specific rtc access functions should be supplied +#endif + +/* ------------------------------------------------------------------------- */ + +void *nvram_read(void *dest, const short src, size_t count) +{ + uchar *d = (uchar *) dest; + short s = src; + + while (count--) + *d++ = rtc_read(s++); + + return dest; +} + +void nvram_write(short dest, const void *src, size_t count) +{ + short d = dest; + uchar *s = (uchar *) src; + + while (count--) + rtc_write(d++, *s++); +} + +#if defined(CONFIG_CMD_DATE) + +/* ------------------------------------------------------------------------- */ + +int rtc_get (struct rtc_time *tmp) +{ + uchar save_ctrl_a; + uchar sec, min, hour, mday, wday, mon, year; + + /* Simple: freeze the clock, read it and allow updates again */ + save_ctrl_a = rtc_read(RTC_CONTROLA); + + /* Set the register to read the value. */ + save_ctrl_a |= RTC_CA_READ; + rtc_write(RTC_CONTROLA, save_ctrl_a); + + sec = rtc_read (RTC_SECONDS); + min = rtc_read (RTC_MINUTES); + hour = rtc_read (RTC_HOURS); + mday = rtc_read (RTC_DAY_OF_MONTH); + wday = rtc_read (RTC_DAY_OF_WEEK); + mon = rtc_read (RTC_MONTH); + year = rtc_read (RTC_YEAR); + + /* re-enable update */ + save_ctrl_a &= ~RTC_CA_READ; + rtc_write(RTC_CONTROLA, save_ctrl_a); + +#ifdef RTC_DEBUG + printf ( "Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon, mday, wday, + hour, min, sec ); +#endif + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon & 0x1F); + tmp->tm_year = bcd2bin (year); + tmp->tm_wday = bcd2bin (wday & 0x07); + if(tmp->tm_year<70) + tmp->tm_year+=2000; + else + tmp->tm_year+=1900; + tmp->tm_yday = 0; + tmp->tm_isdst= 0; +#ifdef RTC_DEBUG + printf ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + + return 0; +} + +int rtc_set (struct rtc_time *tmp) +{ + uchar save_ctrl_a; + +#ifdef RTC_DEBUG + printf ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + save_ctrl_a = rtc_read(RTC_CONTROLA); + + save_ctrl_a |= RTC_CA_WRITE; + rtc_write(RTC_CONTROLA, save_ctrl_a); /* disables the RTC to update the regs */ + + rtc_write (RTC_YEAR, bin2bcd(tmp->tm_year % 100)); + rtc_write (RTC_MONTH, bin2bcd(tmp->tm_mon)); + + rtc_write (RTC_DAY_OF_WEEK, bin2bcd(tmp->tm_wday)); + rtc_write (RTC_DAY_OF_MONTH, bin2bcd(tmp->tm_mday)); + rtc_write (RTC_HOURS, bin2bcd(tmp->tm_hour)); + rtc_write (RTC_MINUTES, bin2bcd(tmp->tm_min )); + rtc_write (RTC_SECONDS, bin2bcd(tmp->tm_sec )); + + save_ctrl_a &= ~RTC_CA_WRITE; + rtc_write(RTC_CONTROLA, save_ctrl_a); /* enables the RTC to update the regs */ + + return 0; +} + +void rtc_reset (void) +{ + uchar control_b; + + /* + * Start oscillator here. + */ + control_b = rtc_read(RTC_CONTROLB); + + control_b &= ~RTC_CB_STOP; + rtc_write(RTC_CONTROLB, control_b); +} + +void rtc_set_watchdog(short multi, short res) +{ + uchar wd_value; + + wd_value = RTC_WDS | ((multi & 0x1F) << 2) | (res & 0x3); + rtc_write(RTC_WATCHDOG, wd_value); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/mpc5xxx.c b/qemu/roms/u-boot/drivers/rtc/mpc5xxx.c new file mode 100644 index 000000000..929783e15 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mpc5xxx.c @@ -0,0 +1,128 @@ +/* + * (C) Copyright 2004 + * Reinhard Meyer, EMK Elektronik GmbH + * r.meyer@emk-elektronik.de + * www.emk-elektronik.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/***************************************************************************** + * Date & Time support for internal RTC of MPC52xx + *****************************************************************************/ +/*#define DEBUG*/ + +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +/***************************************************************************** + * this structure should be defined in mpc5200.h ... + *****************************************************************************/ +typedef struct rtc5200 { + volatile ulong tsr; /* MBAR+0x800: time set register */ + volatile ulong dsr; /* MBAR+0x804: data set register */ + volatile ulong nysr; /* MBAR+0x808: new year and stopwatch register */ + volatile ulong aier; /* MBAR+0x80C: alarm and interrupt enable register */ + volatile ulong ctr; /* MBAR+0x810: current time register */ + volatile ulong cdr; /* MBAR+0x814: current data register */ + volatile ulong asir; /* MBAR+0x818: alarm and stopwatch interrupt register */ + volatile ulong piber; /* MBAR+0x81C: periodic interrupt and bus error register */ + volatile ulong trdr; /* MBAR+0x820: test register/divides register */ +} RTC5200; + +#define RTC_SET 0x02000000 +#define RTC_PAUSE 0x01000000 + +/***************************************************************************** + * get time + *****************************************************************************/ +int rtc_get (struct rtc_time *tmp) +{ + RTC5200 *rtc = (RTC5200 *) (CONFIG_SYS_MBAR+0x800); + ulong time, date, time2; + + /* read twice to avoid getting a funny time when the second is just changing */ + do { + time = rtc->ctr; + date = rtc->cdr; + time2 = rtc->ctr; + } while (time != time2); + + tmp->tm_year = date & 0xfff; + tmp->tm_mon = (date >> 24) & 0xf; + tmp->tm_mday = (date >> 16) & 0x1f; + tmp->tm_wday = (date >> 21) & 7; + /* sunday is 7 in 5200 but 0 in rtc_time */ + if (tmp->tm_wday == 7) + tmp->tm_wday = 0; + tmp->tm_hour = (time >> 16) & 0x1f; + tmp->tm_min = (time >> 8) & 0x3f; + tmp->tm_sec = time & 0x3f; + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +/***************************************************************************** + * set time + *****************************************************************************/ +int rtc_set (struct rtc_time *tmp) +{ + RTC5200 *rtc = (RTC5200 *) (CONFIG_SYS_MBAR+0x800); + ulong time, date, year; + + debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + time = (tmp->tm_hour << 16) | (tmp->tm_min << 8) | tmp->tm_sec; + date = (tmp->tm_mon << 16) | tmp->tm_mday; + if (tmp->tm_wday == 0) + date |= (7 << 8); + else + date |= (tmp->tm_wday << 8); + year = tmp->tm_year; + + /* mask unwanted bits that might show up when rtc_time is corrupt */ + time &= 0x001f3f3f; + date &= 0x001f071f; + year &= 0x00000fff; + + /* pause and set the RTC */ + rtc->nysr = year; + rtc->dsr = date | RTC_PAUSE; + udelay (1000); + rtc->dsr = date | RTC_PAUSE | RTC_SET; + udelay (1000); + rtc->dsr = date | RTC_PAUSE; + udelay (1000); + rtc->dsr = date; + udelay (1000); + + rtc->tsr = time | RTC_PAUSE; + udelay (1000); + rtc->tsr = time | RTC_PAUSE | RTC_SET; + udelay (1000); + rtc->tsr = time | RTC_PAUSE; + udelay (1000); + rtc->tsr = time; + udelay (1000); + + return 0; +} + +/***************************************************************************** + * reset rtc circuit + *****************************************************************************/ +void rtc_reset (void) +{ + return; /* nothing to do */ +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/mpc8xx.c b/qemu/roms/u-boot/drivers/rtc/mpc8xx.c new file mode 100644 index 000000000..d239daee1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mpc8xx.c @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for internal RTC of MPC8xx + */ + +/*#define DEBUG*/ + +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +/* ------------------------------------------------------------------------- */ + +int rtc_get (struct rtc_time *tmp) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + ulong tim; + + tim = immr->im_sit.sit_rtc; + + to_tm (tim, tmp); + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +int rtc_set (struct rtc_time *tmp) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + ulong tim; + + debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + immr->im_sitk.sitk_rtck = KAPWR_KEY; + immr->im_sit.sit_rtc = tim; + + return 0; +} + +void rtc_reset (void) +{ + return; /* nothing to do */ +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/mvrtc.c b/qemu/roms/u-boot/drivers/rtc/mvrtc.c new file mode 100644 index 000000000..97dadd068 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mvrtc.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2011 + * Jason Cooper + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for Marvell Integrated RTC + */ + +#include +#include +#include +#include +#include "mvrtc.h" + +/* This RTC does not support century, so we assume 20 */ +#define CENTURY 20 + +int rtc_get(struct rtc_time *t) +{ + u32 time; + u32 date; + struct mvrtc_registers *mvrtc_regs; + + mvrtc_regs = (struct mvrtc_registers *)KW_RTC_BASE; + + /* read the time register */ + time = readl(&mvrtc_regs->time); + + /* read the date register */ + date = readl(&mvrtc_regs->date); + + /* test for 12 hour clock (can't tell if it's am/pm) */ + if (time & MVRTC_HRFMT_MSK) { + printf("Error: RTC in 12 hour mode, can't determine AM/PM.\n"); + return -1; + } + + /* time */ + t->tm_sec = bcd2bin((time >> MVRTC_SEC_SFT) & MVRTC_SEC_MSK); + t->tm_min = bcd2bin((time >> MVRTC_MIN_SFT) & MVRTC_MIN_MSK); + t->tm_hour = bcd2bin((time >> MVRTC_HOUR_SFT) & MVRTC_HOUR_MSK); + t->tm_wday = bcd2bin((time >> MVRTC_DAY_SFT) & MVRTC_DAY_MSK); + t->tm_wday--; + + /* date */ + t->tm_mday = bcd2bin((date >> MVRTC_DATE_SFT) & MVRTC_DATE_MSK); + t->tm_mon = bcd2bin((date >> MVRTC_MON_SFT) & MVRTC_MON_MSK); + t->tm_year = bcd2bin((date >> MVRTC_YEAR_SFT) & MVRTC_YEAR_MSK); + t->tm_year += CENTURY * 100; + + /* not supported in this RTC */ + t->tm_yday = 0; + t->tm_isdst = 0; + + return 0; +} + +int rtc_set(struct rtc_time *t) +{ + u32 time = 0; /* sets hour format bit to zero, 24hr format. */ + u32 date = 0; + struct mvrtc_registers *mvrtc_regs; + + mvrtc_regs = (struct mvrtc_registers *)KW_RTC_BASE; + + /* check that this code isn't 80+ years old ;-) */ + if ((t->tm_year / 100) != CENTURY) + printf("Warning: Only century %d supported.\n", CENTURY); + + /* time */ + time |= (bin2bcd(t->tm_sec) & MVRTC_SEC_MSK) << MVRTC_SEC_SFT; + time |= (bin2bcd(t->tm_min) & MVRTC_MIN_MSK) << MVRTC_MIN_SFT; + time |= (bin2bcd(t->tm_hour) & MVRTC_HOUR_MSK) << MVRTC_HOUR_SFT; + time |= (bin2bcd(t->tm_wday + 1) & MVRTC_DAY_MSK) << MVRTC_DAY_SFT; + + /* date */ + date |= (bin2bcd(t->tm_mday) & MVRTC_DATE_MSK) << MVRTC_DATE_SFT; + date |= (bin2bcd(t->tm_mon) & MVRTC_MON_MSK) << MVRTC_MON_SFT; + date |= (bin2bcd(t->tm_year % 100) & MVRTC_YEAR_MSK) << MVRTC_YEAR_SFT; + + /* write the time register */ + writel(time, &mvrtc_regs->time); + + /* write the date register */ + writel(date, &mvrtc_regs->date); + + return 0; +} + +void rtc_reset(void) +{ + u32 time; + u32 sec; + struct mvrtc_registers *mvrtc_regs; + + mvrtc_regs = (struct mvrtc_registers *)KW_RTC_BASE; + + /* no init routine for this RTC needed, just check that it's working */ + time = readl(&mvrtc_regs->time); + sec = bcd2bin((time >> MVRTC_SEC_SFT) & MVRTC_SEC_MSK); + udelay(1000000); + time = readl(&mvrtc_regs->time); + + if (sec == bcd2bin((time >> MVRTC_SEC_SFT) & MVRTC_SEC_MSK)) + printf("Error: RTC did not increment.\n"); +} diff --git a/qemu/roms/u-boot/drivers/rtc/mvrtc.h b/qemu/roms/u-boot/drivers/rtc/mvrtc.h new file mode 100644 index 000000000..ce7a69bab --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mvrtc.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011 + * Jason Cooper + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for Marvell Integrated RTC + */ + +#ifndef _MVRTC_H_ +#define _MVRTC_H_ + +#include +#include + +/* RTC registers */ +struct mvrtc_registers { + u32 time; + u32 date; +}; + +/* time register */ +#define MVRTC_SEC_SFT 0 +#define MVRTC_SEC_MSK 0x7f +#define MVRTC_MIN_SFT 8 +#define MVRTC_MIN_MSK 0x7f +#define MVRTC_HOUR_SFT 16 +#define MVRTC_HOUR_MSK 0x3f +#define MVRTC_DAY_SFT 24 +#define MVRTC_DAY_MSK 0x7 + +/* + * Hour format bit + * 1 = 12 hour clock + * 0 = 24 hour clock + */ +#define MVRTC_HRFMT_MSK 0x00400000 + +/* date register */ +#define MVRTC_DATE_SFT 0 +#define MVRTC_DATE_MSK 0x3f +#define MVRTC_MON_SFT 8 +#define MVRTC_MON_MSK 0x1f +#define MVRTC_YEAR_SFT 16 +#define MVRTC_YEAR_MSK 0xff + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/mx27rtc.c b/qemu/roms/u-boot/drivers/rtc/mx27rtc.c new file mode 100644 index 000000000..ae6595b86 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mx27rtc.c @@ -0,0 +1,70 @@ +/* + * Freescale i.MX27 RTC Driver + * + * Copyright (C) 2012 Philippe Reynes + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define HOUR_SHIFT 8 +#define HOUR_MASK 0x1f +#define MIN_SHIFT 0 +#define MIN_MASK 0x3f + +int rtc_get(struct rtc_time *time) +{ + struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE; + uint32_t day, hour, min, sec; + + day = readl(&rtc_regs->dayr); + hour = readl(&rtc_regs->hourmin); + sec = readl(&rtc_regs->seconds); + + min = (hour >> MIN_SHIFT) & MIN_MASK; + hour = (hour >> HOUR_SHIFT) & HOUR_MASK; + + sec += min * 60 + hour * 3600 + day * 24 * 3600; + + to_tm(sec, time); + + return 0; +} + +int rtc_set(struct rtc_time *time) +{ + struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE; + uint32_t day, hour, min, sec; + + sec = mktime(time->tm_year, time->tm_mon, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec); + + day = sec / (24 * 3600); + sec = sec % (24 * 3600); + hour = sec / 3600; + sec = sec % 3600; + min = sec / 60; + sec = sec % 60; + + hour = (hour & HOUR_MASK) << HOUR_SHIFT; + hour |= (min & MIN_MASK) << MIN_SHIFT; + + writel(day, &rtc_regs->dayr); + writel(hour, &rtc_regs->hourmin); + writel(sec, &rtc_regs->seconds); + + return 0; +} + +void rtc_reset(void) +{ + struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE; + + writel(0, &rtc_regs->dayr); + writel(0, &rtc_regs->hourmin); + writel(0, &rtc_regs->seconds); +} diff --git a/qemu/roms/u-boot/drivers/rtc/mxsrtc.c b/qemu/roms/u-boot/drivers/rtc/mxsrtc.c new file mode 100644 index 000000000..32ba8a306 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/mxsrtc.c @@ -0,0 +1,73 @@ +/* + * Freescale i.MX28 RTC Driver + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define MXS_RTC_MAX_TIMEOUT 1000000 + +/* Set time in seconds since 1970-01-01 */ +int mxs_rtc_set_time(uint32_t secs) +{ + struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE; + int ret; + + writel(secs, &rtc_regs->hw_rtc_seconds); + + /* + * The 0x80 here means seconds were copied to analog. This information + * is taken from the linux kernel driver for the STMP37xx RTC since + * documentation doesn't mention it. + */ + ret = mxs_wait_mask_clr(&rtc_regs->hw_rtc_stat_reg, + 0x80 << RTC_STAT_STALE_REGS_OFFSET, MXS_RTC_MAX_TIMEOUT); + + if (ret) + printf("MXS RTC: Timeout waiting for update\n"); + + return ret; +} + +int rtc_get(struct rtc_time *time) +{ + struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE; + uint32_t secs; + + secs = readl(&rtc_regs->hw_rtc_seconds); + to_tm(secs, time); + + return 0; +} + +int rtc_set(struct rtc_time *time) +{ + uint32_t secs; + + secs = mktime(time->tm_year, time->tm_mon, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec); + + return mxs_rtc_set_time(secs); +} + +void rtc_reset(void) +{ + struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE; + int ret; + + /* Set time to 1970-01-01 */ + mxs_rtc_set_time(0); + + /* Reset the RTC block */ + ret = mxs_reset_block(&rtc_regs->hw_rtc_ctrl_reg); + if (ret) + printf("MXS RTC: Block reset timeout\n"); +} diff --git a/qemu/roms/u-boot/drivers/rtc/pcf8563.c b/qemu/roms/u-boot/drivers/rtc/pcf8563.c new file mode 100644 index 000000000..25fa7c5b1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/pcf8563.c @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for Philips PCF8563 RTC + */ + +/* #define DEBUG */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +static uchar rtc_read (uchar reg); +static void rtc_write (uchar reg, uchar val); + +/* ------------------------------------------------------------------------- */ + +int rtc_get (struct rtc_time *tmp) +{ + int rel = 0; + uchar sec, min, hour, mday, wday, mon_cent, year; + + sec = rtc_read (0x02); + min = rtc_read (0x03); + hour = rtc_read (0x04); + mday = rtc_read (0x05); + wday = rtc_read (0x06); + mon_cent= rtc_read (0x07); + year = rtc_read (0x08); + + debug ( "Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon_cent, mday, wday, + hour, min, sec ); + debug ( "Alarms: wday: %02x day: %02x hour: %02x min: %02x\n", + rtc_read (0x0C), + rtc_read (0x0B), + rtc_read (0x0A), + rtc_read (0x09) ); + + if (sec & 0x80) { + puts ("### Warning: RTC Low Voltage - date/time not reliable\n"); + rel = -1; + } + + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon_cent & 0x1F); + tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 1900 : 2000); + tmp->tm_wday = bcd2bin (wday & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return rel; +} + +int rtc_set (struct rtc_time *tmp) +{ + uchar century; + + debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + rtc_write (0x08, bin2bcd(tmp->tm_year % 100)); + + century = (tmp->tm_year >= 2000) ? 0 : 0x80; + rtc_write (0x07, bin2bcd(tmp->tm_mon) | century); + + rtc_write (0x06, bin2bcd(tmp->tm_wday)); + rtc_write (0x05, bin2bcd(tmp->tm_mday)); + rtc_write (0x04, bin2bcd(tmp->tm_hour)); + rtc_write (0x03, bin2bcd(tmp->tm_min )); + rtc_write (0x02, bin2bcd(tmp->tm_sec )); + + return 0; +} + +void rtc_reset (void) +{ + /* clear all control & status registers */ + rtc_write (0x00, 0x00); + rtc_write (0x01, 0x00); + rtc_write (0x0D, 0x00); + + /* clear Voltage Low bit */ + rtc_write (0x02, rtc_read (0x02) & 0x7F); + + /* reset all alarms */ + rtc_write (0x09, 0x00); + rtc_write (0x0A, 0x00); + rtc_write (0x0B, 0x00); + rtc_write (0x0C, 0x00); +} + +/* ------------------------------------------------------------------------- */ + +static uchar rtc_read (uchar reg) +{ + return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg)); +} + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/pl031.c b/qemu/roms/u-boot/drivers/rtc/pl031.c new file mode 100644 index 000000000..c4d1259a8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/pl031.c @@ -0,0 +1,109 @@ +/* + * (C) Copyright 2008 + * Gururaja Hebbar gururajakr@sanyo.co.in + * + * reference linux-2.6.20.6/drivers/rtc/rtc-pl031.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +#ifndef CONFIG_SYS_RTC_PL031_BASE +#error CONFIG_SYS_RTC_PL031_BASE is not defined! +#endif + +/* + * Register definitions + */ +#define RTC_DR 0x00 /* Data read register */ +#define RTC_MR 0x04 /* Match register */ +#define RTC_LR 0x08 /* Data load register */ +#define RTC_CR 0x0c /* Control register */ +#define RTC_IMSC 0x10 /* Interrupt mask and set register */ +#define RTC_RIS 0x14 /* Raw interrupt status register */ +#define RTC_MIS 0x18 /* Masked interrupt status register */ +#define RTC_ICR 0x1c /* Interrupt clear register */ + +#define RTC_CR_START (1 << 0) + +#define RTC_WRITE_REG(addr, val) \ + (*(volatile unsigned int *)(CONFIG_SYS_RTC_PL031_BASE + (addr)) = (val)) +#define RTC_READ_REG(addr) \ + (*(volatile unsigned int *)(CONFIG_SYS_RTC_PL031_BASE + (addr))) + +static int pl031_initted = 0; + +/* Enable RTC Start in Control register*/ +void rtc_init(void) +{ + RTC_WRITE_REG(RTC_CR, RTC_CR_START); + + pl031_initted = 1; +} + +/* + * Reset the RTC. We set the date back to 1970-01-01. + */ +void rtc_reset(void) +{ + RTC_WRITE_REG(RTC_LR, 0x00); + if(!pl031_initted) + rtc_init(); +} + +/* + * Set the RTC +*/ +int rtc_set(struct rtc_time *tmp) +{ + unsigned long tim; + + if(!pl031_initted) + rtc_init(); + + if (tmp == NULL) { + puts("Error setting the date/time\n"); + return -1; + } + + /* Calculate number of seconds this incoming time represents */ + tim = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + RTC_WRITE_REG(RTC_LR, tim); + + return -1; +} + +/* + * Get the current time from the RTC + */ +int rtc_get(struct rtc_time *tmp) +{ + ulong tim; + + if(!pl031_initted) + rtc_init(); + + if (tmp == NULL) { + puts("Error getting the date/time\n"); + return -1; + } + + tim = RTC_READ_REG(RTC_DR); + + to_tm (tim, tmp); + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return 0; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/pt7c4338.c b/qemu/roms/u-boot/drivers/rtc/pt7c4338.c new file mode 100644 index 000000000..b1eb7d878 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/pt7c4338.c @@ -0,0 +1,128 @@ +/* + * Copyright 2010 Freescale Semiconductor, Inc. + * + * Author: Priyanka Jain + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This file provides Date & Time support (no alarms) for PT7C4338 chip. + * + * This file is based on drivers/rtc/ds1337.c + * + * PT7C4338 chip is manufactured by Pericom Technology Inc. + * It is a serial real-time clock which provides + * 1)Low-power clock/calendar. + * 2)Programmable square-wave output. + * It has 56 bytes of nonvolatile RAM. + */ + +#include +#include +#include +#include + +/* RTC register addresses */ +#define RTC_SEC_REG_ADDR 0x0 +#define RTC_MIN_REG_ADDR 0x1 +#define RTC_HR_REG_ADDR 0x2 +#define RTC_DAY_REG_ADDR 0x3 +#define RTC_DATE_REG_ADDR 0x4 +#define RTC_MON_REG_ADDR 0x5 +#define RTC_YR_REG_ADDR 0x6 +#define RTC_CTL_STAT_REG_ADDR 0x7 + +/* RTC second register address bit */ +#define RTC_SEC_BIT_CH 0x80 /* Clock Halt (in Register 0) */ + +/* RTC control and status register bits */ +#define RTC_CTL_STAT_BIT_RS0 0x1 /* Rate select 0 */ +#define RTC_CTL_STAT_BIT_RS1 0x2 /* Rate select 1 */ +#define RTC_CTL_STAT_BIT_SQWE 0x10 /* Square Wave Enable */ +#define RTC_CTL_STAT_BIT_OSF 0x20 /* Oscillator Stop Flag */ +#define RTC_CTL_STAT_BIT_OUT 0x80 /* Output Level Control */ + +/* RTC reset value */ +#define RTC_PT7C4338_RESET_VAL \ + (RTC_CTL_STAT_BIT_RS0 | RTC_CTL_STAT_BIT_RS1 | RTC_CTL_STAT_BIT_OUT) + +/****** Helper functions ****************************************/ +static u8 rtc_read(u8 reg) +{ + return i2c_reg_read(CONFIG_SYS_I2C_RTC_ADDR, reg); +} + +static void rtc_write(u8 reg, u8 val) +{ + i2c_reg_write(CONFIG_SYS_I2C_RTC_ADDR, reg, val); +} +/****************************************************************/ + +/* Get the current time from the RTC */ +int rtc_get(struct rtc_time *tmp) +{ + int ret = 0; + u8 sec, min, hour, mday, wday, mon, year, ctl_stat; + + ctl_stat = rtc_read(RTC_CTL_STAT_REG_ADDR); + sec = rtc_read(RTC_SEC_REG_ADDR); + min = rtc_read(RTC_MIN_REG_ADDR); + hour = rtc_read(RTC_HR_REG_ADDR); + wday = rtc_read(RTC_DAY_REG_ADDR); + mday = rtc_read(RTC_DATE_REG_ADDR); + mon = rtc_read(RTC_MON_REG_ADDR); + year = rtc_read(RTC_YR_REG_ADDR); + debug("Get RTC year: %02x mon: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x control_status: %02x\n", + year, mon, mday, wday, hour, min, sec, ctl_stat); + + if (ctl_stat & RTC_CTL_STAT_BIT_OSF) { + printf("### Warning: RTC oscillator has stopped\n"); + /* clear the OSF flag */ + rtc_write(RTC_CTL_STAT_REG_ADDR, + rtc_read(RTC_CTL_STAT_REG_ADDR)\ + & ~RTC_CTL_STAT_BIT_OSF); + ret = -1; + } + + tmp->tm_sec = bcd2bin(sec & 0x7F); + tmp->tm_min = bcd2bin(min & 0x7F); + tmp->tm_hour = bcd2bin(hour & 0x3F); + tmp->tm_mday = bcd2bin(mday & 0x3F); + tmp->tm_mon = bcd2bin(mon & 0x1F); + tmp->tm_year = bcd2bin(year) + 2000; + tmp->tm_wday = bcd2bin((wday - 1) & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst = 0; + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return ret; +} + +/* Set the RTC */ +int rtc_set(struct rtc_time *tmp) +{ + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + rtc_write(RTC_YR_REG_ADDR, bin2bcd(tmp->tm_year % 100)); + rtc_write(RTC_MON_REG_ADDR, bin2bcd(tmp->tm_mon)); + rtc_write(RTC_DAY_REG_ADDR, bin2bcd(tmp->tm_wday + 1)); + rtc_write(RTC_DATE_REG_ADDR, bin2bcd(tmp->tm_mday)); + rtc_write(RTC_HR_REG_ADDR, bin2bcd(tmp->tm_hour)); + rtc_write(RTC_MIN_REG_ADDR, bin2bcd(tmp->tm_min)); + rtc_write(RTC_SEC_REG_ADDR, bin2bcd(tmp->tm_sec)); + + return 0; +} + +/* Reset the RTC */ +void rtc_reset(void) +{ + rtc_write(RTC_SEC_REG_ADDR, 0x00); /* clearing Clock Halt */ + rtc_write(RTC_CTL_STAT_REG_ADDR, RTC_PT7C4338_RESET_VAL); +} diff --git a/qemu/roms/u-boot/drivers/rtc/rs5c372.c b/qemu/roms/u-boot/drivers/rtc/rs5c372.c new file mode 100644 index 000000000..65f45ea5e --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/rs5c372.c @@ -0,0 +1,281 @@ +/* + * rs5c372.c + * + * Device driver for Ricoh's Real Time Controller RS5C372A. + * + * Copyright (C) 2004 Gary Jennejohn garyj@denx.de + * + * Based in part in ds1307.c - + * (C) Copyright 2001, 2002, 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Keith Outwater, keith_outwater@mvis.com` + * Steven Scholz, steven.scholz@imc-berlin.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) +/* + * Reads are always done starting with register 15, which requires some + * jumping-through-hoops to access the data correctly. + * + * Writes are always done starting with register 0. + */ + +#define DEBUG 0 + +#if DEBUG +static unsigned int rtc_debug = DEBUG; +#else +#define rtc_debug 0 /* gcc will remove all the debug code for us */ +#endif + +#ifndef CONFIG_SYS_I2C_RTC_ADDR +#define CONFIG_SYS_I2C_RTC_ADDR 0x32 +#endif + +#define RS5C372_RAM_SIZE 0x10 +#define RATE_32000HZ 0x80 /* Rate Select 32.000KHz */ +#define RATE_32768HZ 0x00 /* Rate Select 32.768KHz */ + +#define STATUS_XPT 0x10 /* data invalid because voltage was 0 */ + +#define USE_24HOUR_MODE 0x20 +#define TWELVE_HOUR_MODE(n) ((((n) >> 5) & 1) == 0) +#define HOURS_AP(n) (((n) >> 5) & 1) +#define HOURS_12(n) bcd2bin((n) & 0x1F) +#define HOURS_24(n) bcd2bin((n) & 0x3F) + + +static int setup_done = 0; + +static int +rs5c372_readram(unsigned char *buf, int len) +{ + int ret; + + ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, len); + if (ret != 0) { + printf("%s: failed to read\n", __FUNCTION__); + return ret; + } + + if (buf[0] & STATUS_XPT) + printf("### Warning: RTC lost power\n"); + + return ret; +} + +static void +rs5c372_enable(void) +{ + unsigned char buf[RS5C372_RAM_SIZE + 1]; + int ret; + + /* note that this returns reg. 15 in buf[1] */ + ret = rs5c372_readram(&buf[1], RS5C372_RAM_SIZE); + if (ret != 0) { + printf("%s: failed\n", __FUNCTION__); + return; + } + + buf[0] = 0; + /* we want to start writing at register 0 so we have to copy the */ + /* register contents up one slot */ + for (ret = 2; ret < 9; ret++) + buf[ret - 1] = buf[ret]; + /* registers 0 to 6 (time values) are not touched */ + buf[8] = RATE_32768HZ; /* reg. 7 */ + buf[9] = 0; /* reg. 8 */ + buf[10] = 0; /* reg. 9 */ + buf[11] = 0; /* reg. 10 */ + buf[12] = 0; /* reg. 11 */ + buf[13] = 0; /* reg. 12 */ + buf[14] = 0; /* reg. 13 */ + buf[15] = 0; /* reg. 14 */ + buf[16] = USE_24HOUR_MODE; /* reg. 15 */ + ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, RS5C372_RAM_SIZE+1); + if (ret != 0) { + printf("%s: failed\n", __FUNCTION__); + return; + } + setup_done = 1; + + return; +} + +static void +rs5c372_convert_to_time(struct rtc_time *dt, unsigned char *buf) +{ + /* buf[0] is register 15 */ + dt->tm_sec = bcd2bin(buf[1]); + dt->tm_min = bcd2bin(buf[2]); + + if (TWELVE_HOUR_MODE(buf[0])) { + dt->tm_hour = HOURS_12(buf[3]); + if (HOURS_AP(buf[3])) /* PM */ + dt->tm_hour += 12; + } else /* 24-hour-mode */ + dt->tm_hour = HOURS_24(buf[3]); + + dt->tm_mday = bcd2bin(buf[5]); + dt->tm_mon = bcd2bin(buf[6]); + dt->tm_year = bcd2bin(buf[7]); + if (dt->tm_year >= 70) + dt->tm_year += 1900; + else + dt->tm_year += 2000; + /* 0 is Sunday */ + dt->tm_wday = bcd2bin(buf[4] & 0x07); + dt->tm_yday = 0; + dt->tm_isdst= 0; + + if(rtc_debug > 2) { + printf("rs5c372_convert_to_time: year = %d\n", dt->tm_year); + printf("rs5c372_convert_to_time: mon = %d\n", dt->tm_mon); + printf("rs5c372_convert_to_time: mday = %d\n", dt->tm_mday); + printf("rs5c372_convert_to_time: hour = %d\n", dt->tm_hour); + printf("rs5c372_convert_to_time: min = %d\n", dt->tm_min); + printf("rs5c372_convert_to_time: sec = %d\n", dt->tm_sec); + } +} + +/* + * Get the current time from the RTC + */ +int +rtc_get (struct rtc_time *tmp) +{ + unsigned char buf[RS5C372_RAM_SIZE]; + int ret; + + if (!setup_done) + rs5c372_enable(); + + if (!setup_done) + return -1; + + memset(buf, 0, sizeof(buf)); + + /* note that this returns reg. 15 in buf[0] */ + ret = rs5c372_readram(buf, RS5C372_RAM_SIZE); + if (ret != 0) { + printf("%s: failed\n", __FUNCTION__); + return -1; + } + + rs5c372_convert_to_time(tmp, buf); + + return 0; +} + +/* + * Set the RTC + */ +int rtc_set (struct rtc_time *tmp) +{ + unsigned char buf[8], reg15; + int ret; + + if (!setup_done) + rs5c372_enable(); + + if (!setup_done) + return -1; + + if(rtc_debug > 2) { + printf("rtc_set: tm_year = %d\n", tmp->tm_year); + printf("rtc_set: tm_mon = %d\n", tmp->tm_mon); + printf("rtc_set: tm_mday = %d\n", tmp->tm_mday); + printf("rtc_set: tm_hour = %d\n", tmp->tm_hour); + printf("rtc_set: tm_min = %d\n", tmp->tm_min); + printf("rtc_set: tm_sec = %d\n", tmp->tm_sec); + } + + memset(buf, 0, sizeof(buf)); + + /* only read register 15 */ + ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, 1); + + if (ret == 0) { + /* need to save register 15 */ + reg15 = buf[0]; + buf[0] = 0; /* register address on RS5C372 */ + buf[1] = bin2bcd(tmp->tm_sec); + buf[2] = bin2bcd(tmp->tm_min); + /* need to handle 12 hour mode */ + if (TWELVE_HOUR_MODE(reg15)) { + if (tmp->tm_hour >= 12) { /* PM */ + /* 12 PM is a special case */ + if (tmp->tm_hour == 12) + buf[3] = bin2bcd(tmp->tm_hour); + else + buf[3] = bin2bcd(tmp->tm_hour - 12); + buf[3] |= 0x20; + } + } else { + buf[3] = bin2bcd(tmp->tm_hour); + } + + buf[4] = bin2bcd(tmp->tm_wday); + buf[5] = bin2bcd(tmp->tm_mday); + buf[6] = bin2bcd(tmp->tm_mon); + if (tmp->tm_year < 1970 || tmp->tm_year > 2069) + printf("WARNING: year should be between 1970 and 2069!\n"); + buf[7] = bin2bcd(tmp->tm_year % 100); + + ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, 8); + if (ret != 0) { + printf("rs5c372_set_datetime(), i2c_master_send() returned %d\n",ret); + return -1; + } + } else { + return -1; + } + + return 0; +} + +/* + * Reset the RTC. We set the date back to 1970-01-01. + */ +void +rtc_reset (void) +{ + struct rtc_time tmp; + + if (!setup_done) + rs5c372_enable(); + + if (!setup_done) + return; + + tmp.tm_year = 1970; + tmp.tm_mon = 1; + /* Jan. 1, 1970 was a Thursday */ + tmp.tm_wday= 4; + tmp.tm_mday= 1; + tmp.tm_hour = 0; + tmp.tm_min = 0; + tmp.tm_sec = 0; + + rtc_set(&tmp); + + printf ("RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", + tmp.tm_year, tmp.tm_mon, tmp.tm_mday, + tmp.tm_hour, tmp.tm_min, tmp.tm_sec); + + return; +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/rtc4543.c b/qemu/roms/u-boot/drivers/rtc/rtc4543.c new file mode 100644 index 000000000..8d36edd65 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/rtc4543.c @@ -0,0 +1,101 @@ +/* + * (C) Copyright 2008, 2009 + * Andreas Pfefferle, DENX Software Engineering, ap@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +/* + * Note: The acrobatics below is due to the hideously ingenius idea of + * the chip designers. As the chip does not allow register + * addressing, all values need to be read and written in one go. Sure + * enough, the 'wday' field (0-6) is transferred using the economic + * number of 4 bits right in the middle of the packet..... + */ + +int rtc_get(struct rtc_time *tm) +{ + int rel = 0; + uchar buffer[7]; + + memset(buffer, 0, 7); + + /* Read 52 bits into our buffer */ + tws_read(buffer, 52); + + tm->tm_sec = bcd2bin( buffer[0] & 0x7F); + tm->tm_min = bcd2bin( buffer[1] & 0x7F); + tm->tm_hour = bcd2bin( buffer[2] & 0x3F); + tm->tm_wday = bcd2bin( buffer[3] & 0x07); + tm->tm_mday = bcd2bin((buffer[3] & 0xF0) >> 4 | (buffer[4] & 0x0F) << 4); + tm->tm_mon = bcd2bin((buffer[4] & 0x30) >> 4 | (buffer[5] & 0x0F) << 4); + tm->tm_year = bcd2bin((buffer[5] & 0xF0) >> 4 | (buffer[6] & 0x0F) << 4) + 2000; + tm->tm_yday = 0; + tm->tm_isdst = 0; + + if (tm->tm_sec & 0x80) { + puts("### Warning: RTC Low Voltage - date/time not reliable\n"); + rel = -1; + } + + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return rel; +} + +int rtc_set(struct rtc_time *tm) +{ + uchar buffer[7]; + uchar tmp; + + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + memset(buffer, 0, 7); + buffer[0] = bin2bcd(tm->tm_sec); + buffer[1] = bin2bcd(tm->tm_min); + buffer[2] = bin2bcd(tm->tm_hour); + buffer[3] = bin2bcd(tm->tm_wday); + tmp = bin2bcd(tm->tm_mday); + buffer[3] |= (tmp & 0x0F) << 4; + buffer[4] = (tmp & 0xF0) >> 4; + tmp = bin2bcd(tm->tm_mon); + buffer[4] |= (tmp & 0x0F) << 4; + buffer[5] = (tmp & 0xF0) >> 4; + tmp = bin2bcd(tm->tm_year % 100); + buffer[5] |= (tmp & 0x0F) << 4; + buffer[6] = (tmp & 0xF0) >> 4; + + /* Write the resulting 52 bits to device */ + tws_write(buffer, 52); + + return 0; +} + +void rtc_reset(void) +{ + struct rtc_time tmp; + + tmp.tm_sec = 0; + tmp.tm_min = 0; + tmp.tm_hour = 0; + tmp.tm_wday = 4; + tmp.tm_mday = 1; + tmp.tm_mon = 1; + tmp.tm_year = 2000; + rtc_set(&tmp); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/rv3029.c b/qemu/roms/u-boot/drivers/rtc/rv3029.c new file mode 100644 index 000000000..f08040a85 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/rv3029.c @@ -0,0 +1,190 @@ +/* + * (C) Copyright 2010 + * Heiko Schocher, DENX Software Engineering, hs@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include + +#define RTC_RV3029_CTRL1 0x00 +#define RTC_RV3029_CTRL1_EERE (1 << 3) + +#define RTC_RV3029_CTRL_STATUS 0x03 +#define RTC_RV3029_CTRLS_EEBUSY (1 << 7) + +#define RTC_RV3029_CTRL_RESET 0x04 +#define RTC_RV3029_CTRL_SYS_R (1 << 4) + +#define RTC_RV3029_CLOCK_PAGE 0x08 +#define RTC_RV3029_PAGE_LEN 7 + +#define RV3029C2_W_SECONDS 0x00 +#define RV3029C2_W_MINUTES 0x01 +#define RV3029C2_W_HOURS 0x02 +#define RV3029C2_W_DATE 0x03 +#define RV3029C2_W_DAYS 0x04 +#define RV3029C2_W_MONTHS 0x05 +#define RV3029C2_W_YEARS 0x06 + +#define RV3029C2_REG_HR_12_24 (1 << 6) /* 24h/12h mode */ +#define RV3029C2_REG_HR_PM (1 << 5) /* PM/AM bit in 12h mode */ + +#define RTC_RV3029_EEPROM_CTRL 0x30 +#define RTC_RV3029_TRICKLE_1K (1 << 4) +#define RTC_RV3029_TRICKLE_5K (1 << 5) +#define RTC_RV3029_TRICKLE_20K (1 << 6) +#define RTC_RV3029_TRICKLE_80K (1 << 7) + +int rtc_get( struct rtc_time *tmp ) +{ + int ret; + unsigned char buf[RTC_RV3029_PAGE_LEN]; + + ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CLOCK_PAGE, 1, buf, \ + RTC_RV3029_PAGE_LEN); + if (ret) { + printf("%s: error reading RTC: %x\n", __func__, ret); + return -1; + } + tmp->tm_sec = bcd2bin( buf[RV3029C2_W_SECONDS] & 0x7f); + tmp->tm_min = bcd2bin( buf[RV3029C2_W_MINUTES] & 0x7f); + if (buf[RV3029C2_W_HOURS] & RV3029C2_REG_HR_12_24) { + /* 12h format */ + tmp->tm_hour = bcd2bin(buf[RV3029C2_W_HOURS] & 0x1f); + if (buf[RV3029C2_W_HOURS] & RV3029C2_REG_HR_PM) + /* PM flag set */ + tmp->tm_hour += 12; + } else + tmp->tm_hour = bcd2bin(buf[RV3029C2_W_HOURS] & 0x3f); + + tmp->tm_mday = bcd2bin( buf[RV3029C2_W_DATE] & 0x3F ); + tmp->tm_mon = bcd2bin( buf[RV3029C2_W_MONTHS] & 0x1F ); + tmp->tm_wday = bcd2bin( buf[RV3029C2_W_DAYS] & 0x07 ); + /* RTC supports only years > 1999 */ + tmp->tm_year = bcd2bin( buf[RV3029C2_W_YEARS]) + 2000; + tmp->tm_yday = 0; + tmp->tm_isdst = 0; + + debug( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); + + return 0; +} + +int rtc_set( struct rtc_time *tmp ) +{ + int ret; + unsigned char buf[RTC_RV3029_PAGE_LEN]; + + debug( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + if (tmp->tm_year < 2000) { + printf("RTC: year %d < 2000 not possible\n", tmp->tm_year); + return -1; + } + buf[RV3029C2_W_SECONDS] = bin2bcd(tmp->tm_sec); + buf[RV3029C2_W_MINUTES] = bin2bcd(tmp->tm_min); + buf[RV3029C2_W_HOURS] = bin2bcd(tmp->tm_hour); + /* set 24h format */ + buf[RV3029C2_W_HOURS] &= ~RV3029C2_REG_HR_12_24; + buf[RV3029C2_W_DATE] = bin2bcd(tmp->tm_mday); + buf[RV3029C2_W_DAYS] = bin2bcd(tmp->tm_wday); + buf[RV3029C2_W_MONTHS] = bin2bcd(tmp->tm_mon); + tmp->tm_year -= 2000; + buf[RV3029C2_W_YEARS] = bin2bcd(tmp->tm_year); + ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CLOCK_PAGE, 1, + buf, RTC_RV3029_PAGE_LEN); + + /* give the RTC some time to update */ + udelay(1000); + return ret; +} + +/* sets EERE-Bit (automatic EEPROM refresh) */ +static void set_eere_bit(int state) +{ + unsigned char reg_ctrl1; + + (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, + ®_ctrl1, 1); + + if (state) + reg_ctrl1 |= RTC_RV3029_CTRL1_EERE; + else + reg_ctrl1 &= (~RTC_RV3029_CTRL1_EERE); + + (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, + ®_ctrl1, 1); +} + +/* waits until EEPROM page is no longer busy (times out after 10ms*loops) */ +static int wait_eebusy(int loops) +{ + int i; + unsigned char ctrl_status; + + for (i = 0; i < loops; i++) { + (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_STATUS, + 1, &ctrl_status, 1); + + if ((ctrl_status & RTC_RV3029_CTRLS_EEBUSY) == 0) + break; + udelay(10000); + } + return i; +} + +void rtc_reset (void) +{ + unsigned char buf[RTC_RV3029_PAGE_LEN]; + + buf[0] = RTC_RV3029_CTRL_SYS_R; + (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_RESET, 1, + buf, 1); + +#if defined(CONFIG_SYS_RV3029_TCR) + /* + * because EEPROM_CTRL register is in EEPROM page it is necessary to + * disable automatic EEPROM refresh and check if EEPROM is busy + * before EEPORM_CTRL register may be accessed + */ + set_eere_bit(0); + wait_eebusy(100); + /* read current trickle charger setting */ + (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_EEPROM_CTRL, + 1, buf, 1); + /* enable automatic EEPROM refresh again */ + set_eere_bit(1); + + /* + * to minimize EEPROM access write trickle charger setting only if it + * differs from current value + */ + if ((buf[0] & 0xF0) != CONFIG_SYS_RV3029_TCR) { + buf[0] = (buf[0] & 0x0F) | CONFIG_SYS_RV3029_TCR; + /* + * write trickle charger setting (disable autom. EEPROM + * refresh and wait until EEPROM is idle) + */ + set_eere_bit(0); + wait_eebusy(100); + (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, + RTC_RV3029_EEPROM_CTRL, 1, buf, 1); + /* + * it is necessary to wait 10ms before EEBUSY-Bit may be read + * (this is not documented in the data sheet yet, but the + * manufacturer recommends it) + */ + udelay(10000); + /* wait until EEPROM write access is finished */ + wait_eebusy(100); + set_eere_bit(1); + } +#endif +} diff --git a/qemu/roms/u-boot/drivers/rtc/rx8025.c b/qemu/roms/u-boot/drivers/rtc/rx8025.c new file mode 100644 index 000000000..b4a149b73 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/rx8025.c @@ -0,0 +1,211 @@ +/* + * (C) Copyright 2007 + * Matthias Fuchs, esd gmbh, matthias.fuchs@esd-electronics.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Epson RX8025 RTC driver. + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +/*---------------------------------------------------------------------*/ +#undef DEBUG_RTC + +#ifdef DEBUG_RTC +#define DEBUGR(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGR(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + +#ifndef CONFIG_SYS_I2C_RTC_ADDR +# define CONFIG_SYS_I2C_RTC_ADDR 0x32 +#endif + +/* + * RTC register addresses + */ +#define RTC_SEC_REG_ADDR 0x00 +#define RTC_MIN_REG_ADDR 0x01 +#define RTC_HR_REG_ADDR 0x02 +#define RTC_DAY_REG_ADDR 0x03 +#define RTC_DATE_REG_ADDR 0x04 +#define RTC_MON_REG_ADDR 0x05 +#define RTC_YR_REG_ADDR 0x06 + +#define RTC_CTL1_REG_ADDR 0x0e +#define RTC_CTL2_REG_ADDR 0x0f + +/* + * Control register 1 bits + */ +#define RTC_CTL1_BIT_2412 0x20 + +/* + * Control register 2 bits + */ +#define RTC_CTL2_BIT_PON 0x10 +#define RTC_CTL2_BIT_VDET 0x40 +#define RTC_CTL2_BIT_XST 0x20 +#define RTC_CTL2_BIT_VDSL 0x80 + +/* + * Note: the RX8025 I2C RTC requires register + * reads and write to consist of a single bus + * cycle. It is not allowed to write the register + * address in a first cycle that is terminated by + * a STOP condition. The chips needs a 'restart' + * sequence (start sequence without a prior stop). + * This driver has been written for a 4xx board. + * U-Boot's 4xx i2c driver is currently not capable + * to generate such cycles to some work arounds + * are used. + */ + +/* static uchar rtc_read (uchar reg); */ +#define rtc_read(reg) buf[((reg) + 1) & 0xf] + +static void rtc_write (uchar reg, uchar val); + +/* + * Get the current time from the RTC + */ +int rtc_get (struct rtc_time *tmp) +{ + int rel = 0; + uchar sec, min, hour, mday, wday, mon, year, ctl2; + uchar buf[16]; + + if (i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, 16)) + printf("Error reading from RTC\n"); + + sec = rtc_read(RTC_SEC_REG_ADDR); + min = rtc_read(RTC_MIN_REG_ADDR); + hour = rtc_read(RTC_HR_REG_ADDR); + wday = rtc_read(RTC_DAY_REG_ADDR); + mday = rtc_read(RTC_DATE_REG_ADDR); + mon = rtc_read(RTC_MON_REG_ADDR); + year = rtc_read(RTC_YR_REG_ADDR); + + DEBUGR ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon, mday, wday, hour, min, sec); + + /* dump status */ + ctl2 = rtc_read(RTC_CTL2_REG_ADDR); + if (ctl2 & RTC_CTL2_BIT_PON) { + printf("RTC: power-on detected\n"); + rel = -1; + } + + if (ctl2 & RTC_CTL2_BIT_VDET) { + printf("RTC: voltage drop detected\n"); + rel = -1; + } + + if (!(ctl2 & RTC_CTL2_BIT_XST)) { + printf("RTC: oscillator stop detected\n"); + rel = -1; + } + + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + if (rtc_read(RTC_CTL1_REG_ADDR) & RTC_CTL1_BIT_2412) + tmp->tm_hour = bcd2bin (hour & 0x3F); + else + tmp->tm_hour = bcd2bin (hour & 0x1F) % 12 + + ((hour & 0x20) ? 12 : 0); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon & 0x1F); + tmp->tm_year = bcd2bin (year) + ( bcd2bin (year) >= 70 ? 1900 : 2000); + tmp->tm_wday = bcd2bin (wday & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + DEBUGR ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return rel; +} + +/* + * Set the RTC + */ +int rtc_set (struct rtc_time *tmp) +{ + DEBUGR ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + if (tmp->tm_year < 1970 || tmp->tm_year > 2069) + printf("WARNING: year should be between 1970 and 2069!\n"); + + rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100)); + rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon)); + rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday)); + rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday)); + rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour)); + rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min)); + rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec)); + + rtc_write (RTC_CTL1_REG_ADDR, RTC_CTL1_BIT_2412); + + return 0; +} + +/* + * Reset the RTC. We setting the date back to 1970-01-01. + */ +void rtc_reset (void) +{ + struct rtc_time tmp; + uchar buf[16]; + uchar ctl2; + + if (i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, 16)) + printf("Error reading from RTC\n"); + + ctl2 = rtc_read(RTC_CTL2_REG_ADDR); + ctl2 &= ~(RTC_CTL2_BIT_PON | RTC_CTL2_BIT_VDET); + ctl2 |= RTC_CTL2_BIT_XST | RTC_CTL2_BIT_VDSL; + rtc_write (RTC_CTL2_REG_ADDR, ctl2); + + tmp.tm_year = 1970; + tmp.tm_mon = 1; + tmp.tm_mday= 1; + tmp.tm_hour = 0; + tmp.tm_min = 0; + tmp.tm_sec = 0; + + rtc_set(&tmp); + + printf ( "RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", + tmp.tm_year, tmp.tm_mon, tmp.tm_mday, + tmp.tm_hour, tmp.tm_min, tmp.tm_sec); + + return; +} + +/* + * Helper functions + */ +static void rtc_write (uchar reg, uchar val) +{ + uchar buf[2]; + buf[0] = reg << 4; + buf[1] = val; + if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, 2) != 0) + printf("Error writing to RTC\n"); + +} + +#endif /* CONFIG_RTC_RX8025 && CONFIG_CMD_DATE */ diff --git a/qemu/roms/u-boot/drivers/rtc/s3c24x0_rtc.c b/qemu/roms/u-boot/drivers/rtc/s3c24x0_rtc.c new file mode 100644 index 000000000..187620ac8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/s3c24x0_rtc.c @@ -0,0 +1,154 @@ +/* + * (C) Copyright 2003 + * David Müller ELSOFT AG Switzerland. d.mueller@elsoft.ch + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for the built-in Samsung S3C24X0 RTC + */ + +#include +#include + +#if (defined(CONFIG_CMD_DATE)) + +#include + +#include +#include +#include + +typedef enum { + RTC_ENABLE, + RTC_DISABLE +} RTC_ACCESS; + + +static inline void SetRTC_Access(RTC_ACCESS a) +{ + struct s3c24x0_rtc *rtc = s3c24x0_get_base_rtc(); + + switch (a) { + case RTC_ENABLE: + writeb(readb(&rtc->rtccon) | 0x01, &rtc->rtccon); + break; + + case RTC_DISABLE: + writeb(readb(&rtc->rtccon) & ~0x01, &rtc->rtccon); + break; + } +} + +/* ------------------------------------------------------------------------- */ + +int rtc_get(struct rtc_time *tmp) +{ + struct s3c24x0_rtc *rtc = s3c24x0_get_base_rtc(); + uchar sec, min, hour, mday, wday, mon, year; + __maybe_unused uchar a_sec, a_min, a_hour, a_date, + a_mon, a_year, a_armed; + + /* enable access to RTC registers */ + SetRTC_Access(RTC_ENABLE); + + /* read RTC registers */ + do { + sec = readb(&rtc->bcdsec); + min = readb(&rtc->bcdmin); + hour = readb(&rtc->bcdhour); + mday = readb(&rtc->bcddate); + wday = readb(&rtc->bcdday); + mon = readb(&rtc->bcdmon); + year = readb(&rtc->bcdyear); + } while (sec != readb(&rtc->bcdsec)); + + /* read ALARM registers */ + a_sec = readb(&rtc->almsec); + a_min = readb(&rtc->almmin); + a_hour = readb(&rtc->almhour); + a_date = readb(&rtc->almdate); + a_mon = readb(&rtc->almmon); + a_year = readb(&rtc->almyear); + a_armed = readb(&rtc->rtcalm); + + /* disable access to RTC registers */ + SetRTC_Access(RTC_DISABLE); + +#ifdef RTC_DEBUG + printf("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon, mday, wday, hour, min, sec); + printf("Alarms: %02x: year: %02x month: %02x date: %02x hour: " + "%02x min: %02x sec: %02x\n", + a_armed, a_year, a_mon, a_date, a_hour, a_min, a_sec); +#endif + + tmp->tm_sec = bcd2bin(sec & 0x7F); + tmp->tm_min = bcd2bin(min & 0x7F); + tmp->tm_hour = bcd2bin(hour & 0x3F); + tmp->tm_mday = bcd2bin(mday & 0x3F); + tmp->tm_mon = bcd2bin(mon & 0x1F); + tmp->tm_year = bcd2bin(year); + tmp->tm_wday = bcd2bin(wday & 0x07); + if (tmp->tm_year < 70) + tmp->tm_year += 2000; + else + tmp->tm_year += 1900; + tmp->tm_yday = 0; + tmp->tm_isdst = 0; +#ifdef RTC_DEBUG + printf("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + + return 0; +} + +int rtc_set(struct rtc_time *tmp) +{ + struct s3c24x0_rtc *rtc = s3c24x0_get_base_rtc(); + uchar sec, min, hour, mday, wday, mon, year; + +#ifdef RTC_DEBUG + printf("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + year = bin2bcd(tmp->tm_year % 100); + mon = bin2bcd(tmp->tm_mon); + wday = bin2bcd(tmp->tm_wday); + mday = bin2bcd(tmp->tm_mday); + hour = bin2bcd(tmp->tm_hour); + min = bin2bcd(tmp->tm_min); + sec = bin2bcd(tmp->tm_sec); + + /* enable access to RTC registers */ + SetRTC_Access(RTC_ENABLE); + + /* write RTC registers */ + writeb(sec, &rtc->bcdsec); + writeb(min, &rtc->bcdmin); + writeb(hour, &rtc->bcdhour); + writeb(mday, &rtc->bcddate); + writeb(wday, &rtc->bcdday); + writeb(mon, &rtc->bcdmon); + writeb(year, &rtc->bcdyear); + + /* disable access to RTC registers */ + SetRTC_Access(RTC_DISABLE); + + return 0; +} + +void rtc_reset(void) +{ + struct s3c24x0_rtc *rtc = s3c24x0_get_base_rtc(); + + writeb((readb(&rtc->rtccon) & ~0x06) | 0x08, &rtc->rtccon); + writeb(readb(&rtc->rtccon) & ~(0x08 | 0x01), &rtc->rtccon); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/rtc/x1205.c b/qemu/roms/u-boot/drivers/rtc/x1205.c new file mode 100644 index 000000000..c499c7a85 --- /dev/null +++ b/qemu/roms/u-boot/drivers/rtc/x1205.c @@ -0,0 +1,165 @@ +/* + * (C) Copyright 2007 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * based on a the Linux rtc-x1207.c driver which is: + * Copyright 2004 Karen Spearel + * Copyright 2005 Alessandro Zummo + * + * Information and datasheet: + * http://www.intersil.com/cda/deviceinfo/0,1477,X1205,00.html + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Date & Time support for Xicor/Intersil X1205 RTC + */ + +/* #define DEBUG */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) + +#define CCR_SEC 0 +#define CCR_MIN 1 +#define CCR_HOUR 2 +#define CCR_MDAY 3 +#define CCR_MONTH 4 +#define CCR_YEAR 5 +#define CCR_WDAY 6 +#define CCR_Y2K 7 + +#define X1205_REG_SR 0x3F /* status register */ +#define X1205_REG_Y2K 0x37 +#define X1205_REG_DW 0x36 +#define X1205_REG_YR 0x35 +#define X1205_REG_MO 0x34 +#define X1205_REG_DT 0x33 +#define X1205_REG_HR 0x32 +#define X1205_REG_MN 0x31 +#define X1205_REG_SC 0x30 +#define X1205_REG_DTR 0x13 +#define X1205_REG_ATR 0x12 +#define X1205_REG_INT 0x11 +#define X1205_REG_0 0x10 +#define X1205_REG_Y2K1 0x0F +#define X1205_REG_DWA1 0x0E +#define X1205_REG_YRA1 0x0D +#define X1205_REG_MOA1 0x0C +#define X1205_REG_DTA1 0x0B +#define X1205_REG_HRA1 0x0A +#define X1205_REG_MNA1 0x09 +#define X1205_REG_SCA1 0x08 +#define X1205_REG_Y2K0 0x07 +#define X1205_REG_DWA0 0x06 +#define X1205_REG_YRA0 0x05 +#define X1205_REG_MOA0 0x04 +#define X1205_REG_DTA0 0x03 +#define X1205_REG_HRA0 0x02 +#define X1205_REG_MNA0 0x01 +#define X1205_REG_SCA0 0x00 + +#define X1205_CCR_BASE 0x30 /* Base address of CCR */ +#define X1205_ALM0_BASE 0x00 /* Base address of ALARM0 */ + +#define X1205_SR_RTCF 0x01 /* Clock failure */ +#define X1205_SR_WEL 0x02 /* Write Enable Latch */ +#define X1205_SR_RWEL 0x04 /* Register Write Enable */ + +#define X1205_DTR_DTR0 0x01 +#define X1205_DTR_DTR1 0x02 +#define X1205_DTR_DTR2 0x04 + +#define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */ + +static void rtc_write(int reg, u8 val) +{ + i2c_write(CONFIG_SYS_I2C_RTC_ADDR, reg, 2, &val, 1); +} + +/* + * In the routines that deal directly with the x1205 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch + * Epoch is initialized as 2000. Time is set to UTC. + */ +int rtc_get(struct rtc_time *tm) +{ + u8 buf[8]; + + i2c_read(CONFIG_SYS_I2C_RTC_ADDR, X1205_CCR_BASE, 2, buf, 8); + + debug("%s: raw read data - sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n", + __FUNCTION__, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + tm->tm_sec = bcd2bin(buf[CCR_SEC]); + tm->tm_min = bcd2bin(buf[CCR_MIN]); + tm->tm_hour = bcd2bin(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */ + tm->tm_mday = bcd2bin(buf[CCR_MDAY]); + tm->tm_mon = bcd2bin(buf[CCR_MONTH]); /* mon is 0-11 */ + tm->tm_year = bcd2bin(buf[CCR_YEAR]) + + (bcd2bin(buf[CCR_Y2K]) * 100); + tm->tm_wday = buf[CCR_WDAY]; + + debug("%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +int rtc_set(struct rtc_time *tm) +{ + int i; + u8 buf[8]; + + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + buf[CCR_SEC] = bin2bcd(tm->tm_sec); + buf[CCR_MIN] = bin2bcd(tm->tm_min); + + /* set hour and 24hr bit */ + buf[CCR_HOUR] = bin2bcd(tm->tm_hour) | X1205_HR_MIL; + + buf[CCR_MDAY] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[CCR_MONTH] = bin2bcd(tm->tm_mon); + + /* year, since the rtc epoch*/ + buf[CCR_YEAR] = bin2bcd(tm->tm_year % 100); + buf[CCR_WDAY] = tm->tm_wday & 0x07; + buf[CCR_Y2K] = bin2bcd(tm->tm_year / 100); + + /* this sequence is required to unlock the chip */ + rtc_write(X1205_REG_SR, X1205_SR_WEL); + rtc_write(X1205_REG_SR, X1205_SR_WEL | X1205_SR_RWEL); + + /* write register's data */ + for (i = 0; i < 8; i++) + rtc_write(X1205_CCR_BASE + i, buf[i]); + + rtc_write(X1205_REG_SR, 0); + + return 0; +} + +void rtc_reset(void) +{ + /* + * Nothing to do + */ +} + +#endif diff --git a/qemu/roms/u-boot/drivers/serial/Makefile b/qemu/roms/u-boot/drivers/serial/Makefile new file mode 100644 index 000000000..571c18fa9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/Makefile @@ -0,0 +1,40 @@ +# +# (C) Copyright 2006-2009 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += serial.o + +obj-$(CONFIG_ALTERA_UART) += altera_uart.o +obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o +obj-$(CONFIG_ARM_DCC) += arm_dcc.o +obj-$(CONFIG_ATMEL_USART) += atmel_usart.o +obj-$(CONFIG_LPC32XX_HSUART) += lpc32xx_hsuart.o +obj-$(CONFIG_MCFUART) += mcfuart.o +obj-$(CONFIG_OPENCORES_YANU) += opencores_yanu.o +obj-$(CONFIG_SYS_NS16550) += ns16550.o +obj-$(CONFIG_S5P) += serial_s5p.o +obj-$(CONFIG_SYS_NS16550_SERIAL) += serial_ns16550.o +obj-$(CONFIG_IMX_SERIAL) += serial_imx.o +obj-$(CONFIG_KS8695_SERIAL) += serial_ks8695.o +obj-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o +obj-$(CONFIG_MXC_UART) += serial_mxc.o +obj-$(CONFIG_PL010_SERIAL) += serial_pl01x.o +obj-$(CONFIG_PL011_SERIAL) += serial_pl01x.o +obj-$(CONFIG_PXA_SERIAL) += serial_pxa.o +obj-$(CONFIG_SA1100_SERIAL) += serial_sa1100.o +obj-$(CONFIG_S3C24X0_SERIAL) += serial_s3c24x0.o +obj-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o +obj-$(CONFIG_SANDBOX_SERIAL) += sandbox.o +obj-$(CONFIG_SCIF_CONSOLE) += serial_sh.o +obj-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o +obj-$(CONFIG_BFIN_SERIAL) += serial_bfin.o +obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o +obj-$(CONFIG_MXS_AUART) += mxs_auart.o +obj-$(CONFIG_ARC_SERIAL) += serial_arc.o + +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_USB_TTY) += usbtty.o +endif diff --git a/qemu/roms/u-boot/drivers/serial/altera_jtag_uart.c b/qemu/roms/u-boot/drivers/serial/altera_jtag_uart.c new file mode 100644 index 000000000..0573c7a98 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/altera_jtag_uart.c @@ -0,0 +1,85 @@ +/* + * (C) Copyright 2004, Psyent Corporation + * Scott McNutt + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/*------------------------------------------------------------------ + * JTAG acts as the serial port + *-----------------------------------------------------------------*/ +static nios_jtag_t *jtag = (nios_jtag_t *)CONFIG_SYS_NIOS_CONSOLE; + +static void altera_jtag_serial_setbrg(void) +{ +} + +static int altera_jtag_serial_init(void) +{ + return 0; +} + +static void altera_jtag_serial_putc(char c) +{ + while (1) { + unsigned st = readl(&jtag->control); + if (NIOS_JTAG_WSPACE(st)) + break; +#ifdef CONFIG_ALTERA_JTAG_UART_BYPASS + if (!(st & NIOS_JTAG_AC)) /* no connection */ + return; +#endif + WATCHDOG_RESET(); + } + writel ((unsigned char)c, &jtag->data); +} + +static int altera_jtag_serial_tstc(void) +{ + return ( readl (&jtag->control) & NIOS_JTAG_RRDY); +} + +static int altera_jtag_serial_getc(void) +{ + int c; + unsigned val; + + while (1) { + WATCHDOG_RESET (); + val = readl (&jtag->data); + if (val & NIOS_JTAG_RVALID) + break; + } + c = val & 0x0ff; + return (c); +} + +static struct serial_device altera_jtag_serial_drv = { + .name = "altera_jtag_uart", + .start = altera_jtag_serial_init, + .stop = NULL, + .setbrg = altera_jtag_serial_setbrg, + .putc = altera_jtag_serial_putc, + .puts = default_serial_puts, + .getc = altera_jtag_serial_getc, + .tstc = altera_jtag_serial_tstc, +}; + +void altera_jtag_serial_initialize(void) +{ + serial_register(&altera_jtag_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &altera_jtag_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/altera_uart.c b/qemu/roms/u-boot/drivers/serial/altera_uart.c new file mode 100644 index 000000000..d620528e3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/altera_uart.c @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2004, Psyent Corporation + * Scott McNutt + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/*------------------------------------------------------------------ + * UART the serial port + *-----------------------------------------------------------------*/ + +static nios_uart_t *uart = (nios_uart_t *) CONFIG_SYS_NIOS_CONSOLE; + +#if defined(CONFIG_SYS_NIOS_FIXEDBAUD) + +/* + * Everything's already setup for fixed-baud PTF + * assignment + */ +static void altera_serial_setbrg(void) +{ +} + +static int altera_serial_init(void) +{ + return 0; +} + +#else + +static void altera_serial_setbrg(void) +{ + unsigned div; + + div = (CONFIG_SYS_CLK_FREQ/gd->baudrate)-1; + writel (div, &uart->divisor); +} + +static int altera_serial_init(void) +{ + serial_setbrg(); + return 0; +} + +#endif /* CONFIG_SYS_NIOS_FIXEDBAUD */ + +/*----------------------------------------------------------------------- + * UART CONSOLE + *---------------------------------------------------------------------*/ +static void altera_serial_putc(char c) +{ + if (c == '\n') + serial_putc ('\r'); + while ((readl (&uart->status) & NIOS_UART_TRDY) == 0) + WATCHDOG_RESET (); + writel ((unsigned char)c, &uart->txdata); +} + +static int altera_serial_tstc(void) +{ + return (readl (&uart->status) & NIOS_UART_RRDY); +} + +static int altera_serial_getc(void) +{ + while (serial_tstc () == 0) + WATCHDOG_RESET (); + return (readl (&uart->rxdata) & 0x00ff ); +} + +static struct serial_device altera_serial_drv = { + .name = "altera_serial", + .start = altera_serial_init, + .stop = NULL, + .setbrg = altera_serial_setbrg, + .putc = altera_serial_putc, + .puts = default_serial_puts, + .getc = altera_serial_getc, + .tstc = altera_serial_tstc, +}; + +void altera_serial_initialize(void) +{ + serial_register(&altera_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &altera_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/arm_dcc.c b/qemu/roms/u-boot/drivers/serial/arm_dcc.c new file mode 100644 index 000000000..5dfb02f47 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/arm_dcc.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2004-2007 ARM Limited. + * Copyright (C) 2008 Jean-Christophe PLAGNIOL-VILLARD + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + */ + +#include +#include + +#if defined(CONFIG_CPU_V6) +/* + * ARMV6 + */ +#define DCC_RBIT (1 << 30) +#define DCC_WBIT (1 << 29) + +#define write_dcc(x) \ + __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x)) + +#define read_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x)) + +#define status_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x)) + +#elif defined(CONFIG_CPU_XSCALE) +/* + * XSCALE + */ +#define DCC_RBIT (1 << 31) +#define DCC_WBIT (1 << 28) + +#define write_dcc(x) \ + __asm__ volatile ("mcr p14, 0, %0, c8, c0, 0\n" : : "r" (x)) + +#define read_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c9, c0, 0\n" : "=r" (x)) + +#define status_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c14, c0, 0\n" : "=r" (x)) + +#else +#define DCC_RBIT (1 << 0) +#define DCC_WBIT (1 << 1) + +#define write_dcc(x) \ + __asm__ volatile ("mcr p14, 0, %0, c1, c0, 0\n" : : "r" (x)) + +#define read_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c1, c0, 0\n" : "=r" (x)) + +#define status_dcc(x) \ + __asm__ volatile ("mrc p14, 0, %0, c0, c0, 0\n" : "=r" (x)) + +#endif + +#define can_read_dcc(x) do { \ + status_dcc(x); \ + x &= DCC_RBIT; \ + } while (0); + +#define can_write_dcc(x) do { \ + status_dcc(x); \ + x &= DCC_WBIT; \ + x = (x == 0); \ + } while (0); + +#define TIMEOUT_COUNT 0x4000000 + +static int arm_dcc_init(void) +{ + return 0; +} + +static int arm_dcc_getc(void) +{ + int ch; + register unsigned int reg; + + do { + can_read_dcc(reg); + } while (!reg); + read_dcc(ch); + + return ch; +} + +static void arm_dcc_putc(char ch) +{ + register unsigned int reg; + unsigned int timeout_count = TIMEOUT_COUNT; + + while (--timeout_count) { + can_write_dcc(reg); + if (reg) + break; + } + if (timeout_count == 0) + return; + else + write_dcc(ch); +} + +static int arm_dcc_tstc(void) +{ + register unsigned int reg; + + can_read_dcc(reg); + + return reg; +} + +static void arm_dcc_setbrg(void) +{ +} + +static struct serial_device arm_dcc_drv = { + .name = "arm_dcc", + .start = arm_dcc_init, + .stop = NULL, + .setbrg = arm_dcc_setbrg, + .putc = arm_dcc_putc, + .puts = default_serial_puts, + .getc = arm_dcc_getc, + .tstc = arm_dcc_tstc, +}; + +void arm_dcc_initialize(void) +{ + serial_register(&arm_dcc_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &arm_dcc_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/atmel_usart.c b/qemu/roms/u-boot/drivers/serial/atmel_usart.c new file mode 100644 index 000000000..8f0e3489a --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/atmel_usart.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2004-2006 Atmel Corporation + * + * Modified to support C structur SoC access by + * Andreas Bießmann + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include + +#include +#include +#include + +#include "atmel_usart.h" + +DECLARE_GLOBAL_DATA_PTR; + +static void atmel_serial_setbrg(void) +{ + atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; + unsigned long divisor; + unsigned long usart_hz; + + /* + * Master Clock + * Baud Rate = -------------- + * 16 * CD + */ + usart_hz = get_usart_clk_rate(CONFIG_USART_ID); + divisor = (usart_hz / 16 + gd->baudrate / 2) / gd->baudrate; + writel(USART3_BF(CD, divisor), &usart->brgr); +} + +static int atmel_serial_init(void) +{ + atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; + + /* + * Just in case: drain transmitter register + * 1000us is enough for baudrate >= 9600 + */ + if (!(readl(&usart->csr) & USART3_BIT(TXEMPTY))) + __udelay(1000); + + writel(USART3_BIT(RSTRX) | USART3_BIT(RSTTX), &usart->cr); + + serial_setbrg(); + + writel((USART3_BF(USART_MODE, USART3_USART_MODE_NORMAL) + | USART3_BF(USCLKS, USART3_USCLKS_MCK) + | USART3_BF(CHRL, USART3_CHRL_8) + | USART3_BF(PAR, USART3_PAR_NONE) + | USART3_BF(NBSTOP, USART3_NBSTOP_1)), + &usart->mr); + writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr); + /* 100us is enough for the new settings to be settled */ + __udelay(100); + + return 0; +} + +static void atmel_serial_putc(char c) +{ + atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; + + if (c == '\n') + serial_putc('\r'); + + while (!(readl(&usart->csr) & USART3_BIT(TXRDY))); + writel(c, &usart->thr); +} + +static int atmel_serial_getc(void) +{ + atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; + + while (!(readl(&usart->csr) & USART3_BIT(RXRDY))) + WATCHDOG_RESET(); + return readl(&usart->rhr); +} + +static int atmel_serial_tstc(void) +{ + atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; + return (readl(&usart->csr) & USART3_BIT(RXRDY)) != 0; +} + +static struct serial_device atmel_serial_drv = { + .name = "atmel_serial", + .start = atmel_serial_init, + .stop = NULL, + .setbrg = atmel_serial_setbrg, + .putc = atmel_serial_putc, + .puts = default_serial_puts, + .getc = atmel_serial_getc, + .tstc = atmel_serial_tstc, +}; + +void atmel_serial_initialize(void) +{ + serial_register(&atmel_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &atmel_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/atmel_usart.h b/qemu/roms/u-boot/drivers/serial/atmel_usart.h new file mode 100644 index 000000000..7f919a296 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/atmel_usart.h @@ -0,0 +1,294 @@ +/* + * Register definitions for the Atmel USART3 module. + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * Modified to support C structure SoC access by + * Andreas Bießmann + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef __DRIVERS_ATMEL_USART_H__ +#define __DRIVERS_ATMEL_USART_H__ + +/* USART3 register footprint */ +typedef struct atmel_usart3 { + u32 cr; + u32 mr; + u32 ier; + u32 idr; + u32 imr; + u32 csr; + u32 rhr; + u32 thr; + u32 brgr; + u32 rtor; + u32 ttgr; + u32 reserved0[5]; + u32 fidi; + u32 ner; + u32 reserved1; + u32 ifr; + u32 man; + u32 reserved2[54]; /* version and PDC not needed */ +} atmel_usart3_t; + +/* Bitfields in CR */ +#define USART3_RSTRX_OFFSET 2 +#define USART3_RSTRX_SIZE 1 +#define USART3_RSTTX_OFFSET 3 +#define USART3_RSTTX_SIZE 1 +#define USART3_RXEN_OFFSET 4 +#define USART3_RXEN_SIZE 1 +#define USART3_RXDIS_OFFSET 5 +#define USART3_RXDIS_SIZE 1 +#define USART3_TXEN_OFFSET 6 +#define USART3_TXEN_SIZE 1 +#define USART3_TXDIS_OFFSET 7 +#define USART3_TXDIS_SIZE 1 +#define USART3_RSTSTA_OFFSET 8 +#define USART3_RSTSTA_SIZE 1 +#define USART3_STTBRK_OFFSET 9 +#define USART3_STTBRK_SIZE 1 +#define USART3_STPBRK_OFFSET 10 +#define USART3_STPBRK_SIZE 1 +#define USART3_STTTO_OFFSET 11 +#define USART3_STTTO_SIZE 1 +#define USART3_SENDA_OFFSET 12 +#define USART3_SENDA_SIZE 1 +#define USART3_RSTIT_OFFSET 13 +#define USART3_RSTIT_SIZE 1 +#define USART3_RSTNACK_OFFSET 14 +#define USART3_RSTNACK_SIZE 1 +#define USART3_RETTO_OFFSET 15 +#define USART3_RETTO_SIZE 1 +#define USART3_DTREN_OFFSET 16 +#define USART3_DTREN_SIZE 1 +#define USART3_DTRDIS_OFFSET 17 +#define USART3_DTRDIS_SIZE 1 +#define USART3_RTSEN_OFFSET 18 +#define USART3_RTSEN_SIZE 1 +#define USART3_RTSDIS_OFFSET 19 +#define USART3_RTSDIS_SIZE 1 +#define USART3_COMM_TX_OFFSET 30 +#define USART3_COMM_TX_SIZE 1 +#define USART3_COMM_RX_OFFSET 31 +#define USART3_COMM_RX_SIZE 1 + +/* Bitfields in MR */ +#define USART3_USART_MODE_OFFSET 0 +#define USART3_USART_MODE_SIZE 4 +#define USART3_USCLKS_OFFSET 4 +#define USART3_USCLKS_SIZE 2 +#define USART3_CHRL_OFFSET 6 +#define USART3_CHRL_SIZE 2 +#define USART3_SYNC_OFFSET 8 +#define USART3_SYNC_SIZE 1 +#define USART3_PAR_OFFSET 9 +#define USART3_PAR_SIZE 3 +#define USART3_NBSTOP_OFFSET 12 +#define USART3_NBSTOP_SIZE 2 +#define USART3_CHMODE_OFFSET 14 +#define USART3_CHMODE_SIZE 2 +#define USART3_MSBF_OFFSET 16 +#define USART3_MSBF_SIZE 1 +#define USART3_MODE9_OFFSET 17 +#define USART3_MODE9_SIZE 1 +#define USART3_CLKO_OFFSET 18 +#define USART3_CLKO_SIZE 1 +#define USART3_OVER_OFFSET 19 +#define USART3_OVER_SIZE 1 +#define USART3_INACK_OFFSET 20 +#define USART3_INACK_SIZE 1 +#define USART3_DSNACK_OFFSET 21 +#define USART3_DSNACK_SIZE 1 +#define USART3_MAX_ITERATION_OFFSET 24 +#define USART3_MAX_ITERATION_SIZE 3 +#define USART3_FILTER_OFFSET 28 +#define USART3_FILTER_SIZE 1 + +/* Bitfields in CSR */ +#define USART3_RXRDY_OFFSET 0 +#define USART3_RXRDY_SIZE 1 +#define USART3_TXRDY_OFFSET 1 +#define USART3_TXRDY_SIZE 1 +#define USART3_RXBRK_OFFSET 2 +#define USART3_RXBRK_SIZE 1 +#define USART3_ENDRX_OFFSET 3 +#define USART3_ENDRX_SIZE 1 +#define USART3_ENDTX_OFFSET 4 +#define USART3_ENDTX_SIZE 1 +#define USART3_OVRE_OFFSET 5 +#define USART3_OVRE_SIZE 1 +#define USART3_FRAME_OFFSET 6 +#define USART3_FRAME_SIZE 1 +#define USART3_PARE_OFFSET 7 +#define USART3_PARE_SIZE 1 +#define USART3_TIMEOUT_OFFSET 8 +#define USART3_TIMEOUT_SIZE 1 +#define USART3_TXEMPTY_OFFSET 9 +#define USART3_TXEMPTY_SIZE 1 +#define USART3_ITERATION_OFFSET 10 +#define USART3_ITERATION_SIZE 1 +#define USART3_TXBUFE_OFFSET 11 +#define USART3_TXBUFE_SIZE 1 +#define USART3_RXBUFF_OFFSET 12 +#define USART3_RXBUFF_SIZE 1 +#define USART3_NACK_OFFSET 13 +#define USART3_NACK_SIZE 1 +#define USART3_RIIC_OFFSET 16 +#define USART3_RIIC_SIZE 1 +#define USART3_DSRIC_OFFSET 17 +#define USART3_DSRIC_SIZE 1 +#define USART3_DCDIC_OFFSET 18 +#define USART3_DCDIC_SIZE 1 +#define USART3_CTSIC_OFFSET 19 +#define USART3_CTSIC_SIZE 1 +#define USART3_RI_OFFSET 20 +#define USART3_RI_SIZE 1 +#define USART3_DSR_OFFSET 21 +#define USART3_DSR_SIZE 1 +#define USART3_DCD_OFFSET 22 +#define USART3_DCD_SIZE 1 +#define USART3_CTS_OFFSET 23 +#define USART3_CTS_SIZE 1 + +/* Bitfields in RHR */ +#define USART3_RXCHR_OFFSET 0 +#define USART3_RXCHR_SIZE 9 + +/* Bitfields in THR */ +#define USART3_TXCHR_OFFSET 0 +#define USART3_TXCHR_SIZE 9 + +/* Bitfields in BRGR */ +#define USART3_CD_OFFSET 0 +#define USART3_CD_SIZE 16 + +/* Bitfields in RTOR */ +#define USART3_TO_OFFSET 0 +#define USART3_TO_SIZE 16 + +/* Bitfields in TTGR */ +#define USART3_TG_OFFSET 0 +#define USART3_TG_SIZE 8 + +/* Bitfields in FIDI */ +#define USART3_FI_DI_RATIO_OFFSET 0 +#define USART3_FI_DI_RATIO_SIZE 11 + +/* Bitfields in NER */ +#define USART3_NB_ERRORS_OFFSET 0 +#define USART3_NB_ERRORS_SIZE 8 + +/* Bitfields in XXR */ +#define USART3_XOFF_OFFSET 0 +#define USART3_XOFF_SIZE 8 +#define USART3_XON_OFFSET 8 +#define USART3_XON_SIZE 8 + +/* Bitfields in IFR */ +#define USART3_IRDA_FILTER_OFFSET 0 +#define USART3_IRDA_FILTER_SIZE 8 + +/* Bitfields in RCR */ +#define USART3_RXCTR_OFFSET 0 +#define USART3_RXCTR_SIZE 16 + +/* Bitfields in TCR */ +#define USART3_TXCTR_OFFSET 0 +#define USART3_TXCTR_SIZE 16 + +/* Bitfields in RNCR */ +#define USART3_RXNCR_OFFSET 0 +#define USART3_RXNCR_SIZE 16 + +/* Bitfields in TNCR */ +#define USART3_TXNCR_OFFSET 0 +#define USART3_TXNCR_SIZE 16 + +/* Bitfields in PTCR */ +#define USART3_RXTEN_OFFSET 0 +#define USART3_RXTEN_SIZE 1 +#define USART3_RXTDIS_OFFSET 1 +#define USART3_RXTDIS_SIZE 1 +#define USART3_TXTEN_OFFSET 8 +#define USART3_TXTEN_SIZE 1 +#define USART3_TXTDIS_OFFSET 9 +#define USART3_TXTDIS_SIZE 1 + +/* Constants for USART_MODE */ +#define USART3_USART_MODE_NORMAL 0 +#define USART3_USART_MODE_RS485 1 +#define USART3_USART_MODE_HARDWARE 2 +#define USART3_USART_MODE_MODEM 3 +#define USART3_USART_MODE_ISO7816_T0 4 +#define USART3_USART_MODE_ISO7816_T1 6 +#define USART3_USART_MODE_IRDA 8 + +/* Constants for USCLKS */ +#define USART3_USCLKS_MCK 0 +#define USART3_USCLKS_MCK_DIV 1 +#define USART3_USCLKS_SCK 3 + +/* Constants for CHRL */ +#define USART3_CHRL_5 0 +#define USART3_CHRL_6 1 +#define USART3_CHRL_7 2 +#define USART3_CHRL_8 3 + +/* Constants for PAR */ +#define USART3_PAR_EVEN 0 +#define USART3_PAR_ODD 1 +#define USART3_PAR_SPACE 2 +#define USART3_PAR_MARK 3 +#define USART3_PAR_NONE 4 +#define USART3_PAR_MULTI 6 + +/* Constants for NBSTOP */ +#define USART3_NBSTOP_1 0 +#define USART3_NBSTOP_1_5 1 +#define USART3_NBSTOP_2 2 + +/* Constants for CHMODE */ +#define USART3_CHMODE_NORMAL 0 +#define USART3_CHMODE_ECHO 1 +#define USART3_CHMODE_LOCAL_LOOP 2 +#define USART3_CHMODE_REMOTE_LOOP 3 + +/* Constants for MSBF */ +#define USART3_MSBF_LSBF 0 +#define USART3_MSBF_MSBF 1 + +/* Constants for OVER */ +#define USART3_OVER_X16 0 +#define USART3_OVER_X8 1 + +/* Constants for CD */ +#define USART3_CD_DISABLE 0 +#define USART3_CD_BYPASS 1 + +/* Constants for TO */ +#define USART3_TO_DISABLE 0 + +/* Constants for TG */ +#define USART3_TG_DISABLE 0 + +/* Constants for FI_DI_RATIO */ +#define USART3_FI_DI_RATIO_DISABLE 0 + +/* Bit manipulation macros */ +#define USART3_BIT(name) \ + (1 << USART3_##name##_OFFSET) +#define USART3_BF(name,value) \ + (((value) & ((1 << USART3_##name##_SIZE) - 1)) \ + << USART3_##name##_OFFSET) +#define USART3_BFEXT(name,value) \ + (((value) >> USART3_##name##_OFFSET) \ + & ((1 << USART3_##name##_SIZE) - 1)) +#define USART3_BFINS(name,value,old) \ + (((old) & ~(((1 << USART3_##name##_SIZE) - 1) \ + << USART3_##name##_OFFSET)) \ + | USART3_BF(name,value)) + +#endif /* __DRIVERS_ATMEL_USART_H__ */ diff --git a/qemu/roms/u-boot/drivers/serial/lpc32xx_hsuart.c b/qemu/roms/u-boot/drivers/serial/lpc32xx_hsuart.c new file mode 100644 index 000000000..c8926a894 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/lpc32xx_hsuart.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2011 Vladimir Zapolskiy + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static struct hsuart_regs *hsuart = (struct hsuart_regs *)HS_UART_BASE; + +static void lpc32xx_serial_setbrg(void) +{ + u32 div; + + /* UART rate = PERIPH_CLK / ((HSU_RATE + 1) x 14) */ + div = (get_serial_clock() / 14 + gd->baudrate / 2) / gd->baudrate - 1; + if (div > 255) + div = 255; + + writel(div, &hsuart->rate); +} + +static int lpc32xx_serial_getc(void) +{ + while (!(readl(&hsuart->level) & HSUART_LEVEL_RX)) + /* NOP */; + + return readl(&hsuart->rx) & HSUART_RX_DATA; +} + +static void lpc32xx_serial_putc(const char c) +{ + if (c == '\n') + serial_putc('\r'); + + writel(c, &hsuart->tx); + + /* Wait for character to be sent */ + while (readl(&hsuart->level) & HSUART_LEVEL_TX) + /* NOP */; +} + +static int lpc32xx_serial_tstc(void) +{ + if (readl(&hsuart->level) & HSUART_LEVEL_RX) + return 1; + + return 0; +} + +static int lpc32xx_serial_init(void) +{ + lpc32xx_serial_setbrg(); + + /* Disable hardware RTS and CTS flow control, set up RX and TX FIFO */ + writel(HSUART_CTRL_TMO_16 | HSUART_CTRL_HSU_OFFSET(20) | + HSUART_CTRL_HSU_RX_TRIG_32 | HSUART_CTRL_HSU_TX_TRIG_0, + &hsuart->ctrl); + return 0; +} + +static struct serial_device lpc32xx_serial_drv = { + .name = "lpc32xx_serial", + .start = lpc32xx_serial_init, + .stop = NULL, + .setbrg = lpc32xx_serial_setbrg, + .putc = lpc32xx_serial_putc, + .puts = default_serial_puts, + .getc = lpc32xx_serial_getc, + .tstc = lpc32xx_serial_tstc, +}; + +void lpc32xx_serial_initialize(void) +{ + serial_register(&lpc32xx_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &lpc32xx_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/mcfuart.c b/qemu/roms/u-boot/drivers/serial/mcfuart.c new file mode 100644 index 000000000..407354fc4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/mcfuart.c @@ -0,0 +1,130 @@ +/* + * (C) Copyright 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew, Tsi-Chung.Liew@freescale.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Minimal serial functions needed to use one of the uart ports + * as serial console interface. + */ + +#include +#include +#include + +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +extern void uart_port_conf(int port); + +static int mcf_serial_init(void) +{ + volatile uart_t *uart; + u32 counter; + + uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE); + + uart_port_conf(CONFIG_SYS_UART_PORT); + + /* write to SICR: SIM2 = uart mode,dcd does not affect rx */ + uart->ucr = UART_UCR_RESET_RX; + uart->ucr = UART_UCR_RESET_TX; + uart->ucr = UART_UCR_RESET_ERROR; + uart->ucr = UART_UCR_RESET_MR; + __asm__("nop"); + + uart->uimr = 0; + + /* write to CSR: RX/TX baud rate from timers */ + uart->ucsr = (UART_UCSR_RCS_SYS_CLK | UART_UCSR_TCS_SYS_CLK); + + uart->umr = (UART_UMR_BC_8 | UART_UMR_PM_NONE); + uart->umr = UART_UMR_SB_STOP_BITS_1; + + /* Setting up BaudRate */ + counter = (u32) ((gd->bus_clk / 32) + (gd->baudrate / 2)); + counter = counter / gd->baudrate; + + /* write to CTUR: divide counter upper byte */ + uart->ubg1 = (u8) ((counter & 0xff00) >> 8); + /* write to CTLR: divide counter lower byte */ + uart->ubg2 = (u8) (counter & 0x00ff); + + uart->ucr = (UART_UCR_RX_ENABLED | UART_UCR_TX_ENABLED); + + return (0); +} + +static void mcf_serial_putc(const char c) +{ + volatile uart_t *uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE); + + if (c == '\n') + serial_putc('\r'); + + /* Wait for last character to go. */ + while (!(uart->usr & UART_USR_TXRDY)) ; + + uart->utb = c; +} + +static int mcf_serial_getc(void) +{ + volatile uart_t *uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE); + + /* Wait for a character to arrive. */ + while (!(uart->usr & UART_USR_RXRDY)) ; + return uart->urb; +} + +static int mcf_serial_tstc(void) +{ + volatile uart_t *uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE); + + return (uart->usr & UART_USR_RXRDY); +} + +static void mcf_serial_setbrg(void) +{ + volatile uart_t *uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE); + u32 counter; + + /* Setting up BaudRate */ + counter = (u32) ((gd->bus_clk / 32) + (gd->baudrate / 2)); + counter = counter / gd->baudrate; + + /* write to CTUR: divide counter upper byte */ + uart->ubg1 = ((counter & 0xff00) >> 8); + /* write to CTLR: divide counter lower byte */ + uart->ubg2 = (counter & 0x00ff); + + uart->ucr = UART_UCR_RESET_RX; + uart->ucr = UART_UCR_RESET_TX; + + uart->ucr = UART_UCR_RX_ENABLED | UART_UCR_TX_ENABLED; +} + +static struct serial_device mcf_serial_drv = { + .name = "mcf_serial", + .start = mcf_serial_init, + .stop = NULL, + .setbrg = mcf_serial_setbrg, + .putc = mcf_serial_putc, + .puts = default_serial_puts, + .getc = mcf_serial_getc, + .tstc = mcf_serial_tstc, +}; + +void mcf_serial_initialize(void) +{ + serial_register(&mcf_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &mcf_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/mxs_auart.c b/qemu/roms/u-boot/drivers/serial/mxs_auart.c new file mode 100644 index 000000000..fc0fa96a0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/mxs_auart.c @@ -0,0 +1,151 @@ +/* + * Freescale i.MX23/i.MX28 AUART driver + * + * Copyright (C) 2013 Andreas Wass + * + * Based on the MXC serial driver: + * + * (c) 2007 Sascha Hauer + * + * Further based on the Linux mxs-auart.c driver: + * + * Freescale STMP37XX/STMP38X Application UART drkiver + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_MXS_AUART_BASE +#error "CONFIG_MXS_AUART_BASE must be set to the base UART to use" +#endif + +/* AUART clock always supplied by XTAL and always 24MHz */ +#define MXS_AUART_CLK 24000000 + +static struct mxs_uartapp_regs *get_uartapp_registers(void) +{ + return (struct mxs_uartapp_regs *)CONFIG_MXS_AUART_BASE; +} + +/** + * Sets the baud rate and settings. + * The settings are: 8 data bits, no parit and 1 stop bit. + */ +static void mxs_auart_setbrg(void) +{ + u32 div; + u32 linectrl = 0; + struct mxs_uartapp_regs *regs = get_uartapp_registers(); + + if (!gd->baudrate) + gd->baudrate = CONFIG_BAUDRATE; + + /* + * From i.MX28 datasheet: + * div is calculated by calculating UARTCLK*32/baudrate, rounded to int + * div must be between 0xEC and 0x003FFFC0 inclusive + * Lowest 6 bits of div goes in BAUD_DIVFRAC part of LINECTRL register + * Next 16 bits goes in BAUD_DIVINT part of LINECTRL register + */ + div = (MXS_AUART_CLK * 32) / gd->baudrate; + if (div < 0xEC || div > 0x003FFFC0) + return; + + linectrl |= ((div & UARTAPP_LINECTRL_EXTRACT_BAUD_DIVFRAC_MASK) << + UARTAPP_LINECTRL_BAUD_DIVFRAC_OFFSET) & + UARTAPP_LINECTRL_BAUD_DIVFRAC_MASK; + linectrl |= ((div >> UARTAPP_LINECTRL_EXTRACT_BAUD_DIVINT_OFFSET) << + UARTAPP_LINECTRL_BAUD_DIVINT_OFFSET) & + UARTAPP_LINECTRL_BAUD_DIVINT_MASK; + + /* Word length: 8 bits */ + linectrl |= UARTAPP_LINECTRL_WLEN_8BITS; + + /* Enable FIFOs. */ + linectrl |= UARTAPP_LINECTRL_FEN_MASK; + + /* Write above settings, no parity, 1 stop bit */ + writel(linectrl, ®s->hw_uartapp_linectrl); +} + +static int mxs_auart_init(void) +{ + struct mxs_uartapp_regs *regs = get_uartapp_registers(); + /* Reset everything */ + mxs_reset_block(®s->hw_uartapp_ctrl0_reg); + /* Disable interrupts */ + writel(0, ®s->hw_uartapp_intr); + /* Set baud rate and settings */ + serial_setbrg(); + /* Disable RTS and CTS, ignore LINECTRL2 register */ + writel(UARTAPP_CTRL2_RTSEN_MASK | + UARTAPP_CTRL2_CTSEN_MASK | + UARTAPP_CTRL2_USE_LCR2_MASK, + ®s->hw_uartapp_ctrl2_clr); + /* Enable receiver, transmitter and UART */ + writel(UARTAPP_CTRL2_RXE_MASK | + UARTAPP_CTRL2_TXE_MASK | + UARTAPP_CTRL2_UARTEN_MASK, + ®s->hw_uartapp_ctrl2_set); + return 0; +} + +static void mxs_auart_putc(const char c) +{ + struct mxs_uartapp_regs *regs = get_uartapp_registers(); + /* Wait in loop while the transmit FIFO is full */ + while (readl(®s->hw_uartapp_stat) & UARTAPP_STAT_TXFF_MASK) + ; + + writel(c, ®s->hw_uartapp_data); + + if (c == '\n') + mxs_auart_putc('\r'); +} + +static int mxs_auart_tstc(void) +{ + struct mxs_uartapp_regs *regs = get_uartapp_registers(); + /* Checks if receive FIFO is empty */ + return !(readl(®s->hw_uartapp_stat) & UARTAPP_STAT_RXFE_MASK); +} + +static int mxs_auart_getc(void) +{ + struct mxs_uartapp_regs *regs = get_uartapp_registers(); + /* Wait until a character is available to read */ + while (!mxs_auart_tstc()) + ; + /* Read the character from the data register */ + return readl(®s->hw_uartapp_data) & 0xFF; +} + +static struct serial_device mxs_auart_drv = { + .name = "mxs_auart_serial", + .start = mxs_auart_init, + .stop = NULL, + .setbrg = mxs_auart_setbrg, + .putc = mxs_auart_putc, + .puts = default_serial_puts, + .getc = mxs_auart_getc, + .tstc = mxs_auart_tstc, +}; + +void mxs_auart_initialize(void) +{ + serial_register(&mxs_auart_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &mxs_auart_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/ns16550.c b/qemu/roms/u-boot/drivers/serial/ns16550.c new file mode 100644 index 000000000..f26979dbe --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/ns16550.c @@ -0,0 +1,147 @@ +/* + * COM1 NS16550 support + * originally from linux source (arch/powerpc/boot/ns16550.c) + * modified to use CONFIG_SYS_ISA_MEM and new defines + */ + +#include +#include +#include +#include +#include + +#define UART_LCRVAL UART_LCR_8N1 /* 8 data, 1 stop, no parity */ +#define UART_MCRVAL (UART_MCR_DTR | \ + UART_MCR_RTS) /* RTS/DTR */ +#define UART_FCRVAL (UART_FCR_FIFO_EN | \ + UART_FCR_RXSR | \ + UART_FCR_TXSR) /* Clear & enable FIFOs */ +#ifdef CONFIG_SYS_NS16550_PORT_MAPPED +#define serial_out(x, y) outb(x, (ulong)y) +#define serial_in(y) inb((ulong)y) +#elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE > 0) +#define serial_out(x, y) out_be32(y, x) +#define serial_in(y) in_be32(y) +#elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE < 0) +#define serial_out(x, y) out_le32(y, x) +#define serial_in(y) in_le32(y) +#else +#define serial_out(x, y) writeb(x, y) +#define serial_in(y) readb(y) +#endif + +#if defined(CONFIG_K2HK_EVM) +#define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0 +#define UART_REG_VAL_PWREMU_MGMT_UART_ENABLE ((1 << 14) | (1 << 13) | (1 << 0)) +#undef UART_MCRVAL +#ifdef CONFIG_SERIAL_HW_FLOW_CONTROL +#define UART_MCRVAL (UART_MCR_RTS | UART_MCR_AFE) +#else +#define UART_MCRVAL (UART_MCR_RTS) +#endif +#endif + +#ifndef CONFIG_SYS_NS16550_IER +#define CONFIG_SYS_NS16550_IER 0x00 +#endif /* CONFIG_SYS_NS16550_IER */ + +void NS16550_init(NS16550_t com_port, int baud_divisor) +{ +#if (defined(CONFIG_SPL_BUILD) && defined(CONFIG_OMAP34XX)) + /* + * On some OMAP3 devices when UART3 is configured for boot mode before + * SPL starts only THRE bit is set. We have to empty the transmitter + * before initialization starts. + */ + if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE)) + == UART_LSR_THRE) { + serial_out(UART_LCR_DLAB, &com_port->lcr); + serial_out(baud_divisor & 0xff, &com_port->dll); + serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); + serial_out(UART_LCRVAL, &com_port->lcr); + serial_out(0, &com_port->mdr1); + } +#endif + + while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT)) + ; + + serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); +#if defined(CONFIG_OMAP) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) + serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/ +#endif + serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); + serial_out(0, &com_port->dll); + serial_out(0, &com_port->dlm); + serial_out(UART_LCRVAL, &com_port->lcr); + serial_out(UART_MCRVAL, &com_port->mcr); + serial_out(UART_FCRVAL, &com_port->fcr); + serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); + serial_out(baud_divisor & 0xff, &com_port->dll); + serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); + serial_out(UART_LCRVAL, &com_port->lcr); +#if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \ + defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ + defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) + + /* /16 is proper to hit 115200 with 48MHz */ + serial_out(0, &com_port->mdr1); +#endif /* CONFIG_OMAP */ +#if defined(CONFIG_K2HK_EVM) + serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC); +#endif +} + +#ifndef CONFIG_NS16550_MIN_FUNCTIONS +void NS16550_reinit(NS16550_t com_port, int baud_divisor) +{ + serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); + serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); + serial_out(0, &com_port->dll); + serial_out(0, &com_port->dlm); + serial_out(UART_LCRVAL, &com_port->lcr); + serial_out(UART_MCRVAL, &com_port->mcr); + serial_out(UART_FCRVAL, &com_port->fcr); + serial_out(UART_LCR_BKSE, &com_port->lcr); + serial_out(baud_divisor & 0xff, &com_port->dll); + serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); + serial_out(UART_LCRVAL, &com_port->lcr); +} +#endif /* CONFIG_NS16550_MIN_FUNCTIONS */ + +void NS16550_putc(NS16550_t com_port, char c) +{ + while ((serial_in(&com_port->lsr) & UART_LSR_THRE) == 0) + ; + serial_out(c, &com_port->thr); + + /* + * Call watchdog_reset() upon newline. This is done here in putc + * since the environment code uses a single puts() to print the complete + * environment upon "printenv". So we can't put this watchdog call + * in puts(). + */ + if (c == '\n') + WATCHDOG_RESET(); +} + +#ifndef CONFIG_NS16550_MIN_FUNCTIONS +char NS16550_getc(NS16550_t com_port) +{ + while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) { +#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY) + extern void usbtty_poll(void); + usbtty_poll(); +#endif + WATCHDOG_RESET(); + } + return serial_in(&com_port->rbr); +} + +int NS16550_tstc(NS16550_t com_port) +{ + return (serial_in(&com_port->lsr) & UART_LSR_DR) != 0; +} + +#endif /* CONFIG_NS16550_MIN_FUNCTIONS */ diff --git a/qemu/roms/u-boot/drivers/serial/opencores_yanu.c b/qemu/roms/u-boot/drivers/serial/opencores_yanu.c new file mode 100644 index 000000000..d4ed60c30 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/opencores_yanu.c @@ -0,0 +1,157 @@ +/* + * Copyright 2010, Renato Andreola + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/*-----------------------------------------------------------------*/ +/* YANU Imagos serial port */ +/*-----------------------------------------------------------------*/ + +static yanu_uart_t *uart = (yanu_uart_t *)CONFIG_SYS_NIOS_CONSOLE; + +static void oc_serial_setbrg(void) +{ + int n, k; + const unsigned max_uns = 0xFFFFFFFF; + unsigned best_n, best_m, baud; + unsigned baudrate; + +#if defined(CONFIG_SYS_NIOS_FIXEDBAUD) + /* Everything's already setup for fixed-baud PTF assignment */ + baudrate = CONFIG_BAUDRATE; +#else + baudrate = gd->baudrate; +#endif + /* compute best N and M couple */ + best_n = YANU_MAX_PRESCALER_N; + for (n = YANU_MAX_PRESCALER_N; n >= 0; n--) { + if ((unsigned)CONFIG_SYS_CLK_FREQ / (1 << (n + 4)) >= + baudrate) { + best_n = n; + break; + } + } + for (k = 0;; k++) { + if (baudrate <= (max_uns >> (15+n-k))) + break; + } + best_m = + (baudrate * (1 << (15 + n - k))) / + ((unsigned)CONFIG_SYS_CLK_FREQ >> k); + + baud = best_m + best_n * YANU_BAUDE; + writel(baud, &uart->baud); + + return; +} + +static int oc_serial_init(void) +{ + unsigned action,control; + + /* status register cleanup */ + action = YANU_ACTION_RRRDY | + YANU_ACTION_RTRDY | + YANU_ACTION_ROE | + YANU_ACTION_RBRK | + YANU_ACTION_RFE | + YANU_ACTION_RPE | + YANU_ACTION_RFE | YANU_ACTION_RFIFO_CLEAR | YANU_ACTION_TFIFO_CLEAR; + + writel(action, &uart->action); + + /* + * control register cleanup + * no interrupts enabled + * one stop bit + * hardware flow control disabled + * 8 bits + */ + control = (0x7 << YANU_CONTROL_BITS_POS); + /* enven parity just to be clean */ + control |= YANU_CONTROL_PAREVEN; + /* we set threshold for fifo */ + control |= YANU_CONTROL_RDYDLY * YANU_RXFIFO_DLY; + control |= YANU_CONTROL_TXTHR * YANU_TXFIFO_THR; + + writel(control, &uart->control); + + /* to set baud rate */ + serial_setbrg(); + + return (0); +} + + +/*----------------------------------------------------------------------- + * YANU CONSOLE + *---------------------------------------------------------------------*/ +static void oc_serial_putc(char c) +{ + int tx_chars; + unsigned status; + + if (c == '\n') + serial_putc ('\r'); + + while (1) { + status = readl(&uart->status); + tx_chars = (status>>YANU_TFIFO_CHARS_POS) + & ((1<data); +} + +static int oc_serial_tstc(void) +{ + unsigned status ; + + status = readl(&uart->status); + return (((status >> YANU_RFIFO_CHARS_POS) & + ((1 << YANU_RFIFO_CHARS_N) - 1)) > 0); +} + +static int oc_serial_getc(void) +{ + while (serial_tstc() == 0) + WATCHDOG_RESET (); + + /* first we pull the char */ + writel(YANU_ACTION_RFIFO_PULL, &uart->action); + + return(readl(&uart->data) & YANU_DATA_CHAR_MASK); +} + +static struct serial_device oc_serial_drv = { + .name = "oc_serial", + .start = oc_serial_init, + .stop = NULL, + .setbrg = oc_serial_setbrg, + .putc = oc_serial_putc, + .puts = default_serial_puts, + .getc = oc_serial_getc, + .tstc = oc_serial_tstc, +}; + +void oc_serial_initialize(void) +{ + serial_register(&oc_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &oc_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/sandbox.c b/qemu/roms/u-boot/drivers/serial/sandbox.c new file mode 100644 index 000000000..51fd871df --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/sandbox.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This provide a test serial port. It provides an emulated serial port where + * a test program and read out the serial output and inject serial input for + * U-Boot. + */ + +#include +#include +#include +#include +#include +#include + +/* + * + * serial_buf: A buffer that holds keyboard characters for the + * Sandbox U-boot. + * + * invariants: + * serial_buf_write == serial_buf_read -> empty buffer + * (serial_buf_write + 1) % 16 == serial_buf_read -> full buffer + */ +static char serial_buf[16]; +static unsigned int serial_buf_write; +static unsigned int serial_buf_read; + +static int sandbox_serial_init(void) +{ + struct sandbox_state *state = state_get_current(); + + if (state->term_raw != STATE_TERM_COOKED) + os_tty_raw(0, state->term_raw == STATE_TERM_RAW_WITH_SIGS); + return 0; +} + +static void sandbox_serial_setbrg(void) +{ +} + +static void sandbox_serial_putc(const char ch) +{ + os_write(1, &ch, 1); +} + +static void sandbox_serial_puts(const char *str) +{ + os_write(1, str, strlen(str)); +} + +static unsigned int increment_buffer_index(unsigned int index) +{ + return (index + 1) % ARRAY_SIZE(serial_buf); +} + +static int sandbox_serial_tstc(void) +{ + const unsigned int next_index = + increment_buffer_index(serial_buf_write); + ssize_t count; + + os_usleep(100); +#ifdef CONFIG_LCD + lcd_sync(); +#endif + if (next_index == serial_buf_read) + return 1; /* buffer full */ + + count = os_read_no_block(0, &serial_buf[serial_buf_write], 1); + if (count == 1) + serial_buf_write = next_index; + return serial_buf_write != serial_buf_read; +} + +static int sandbox_serial_getc(void) +{ + int result; + + while (!sandbox_serial_tstc()) + ; /* buffer empty */ + + result = serial_buf[serial_buf_read]; + serial_buf_read = increment_buffer_index(serial_buf_read); + return result; +} + +static struct serial_device sandbox_serial_drv = { + .name = "sandbox_serial", + .start = sandbox_serial_init, + .stop = NULL, + .setbrg = sandbox_serial_setbrg, + .putc = sandbox_serial_putc, + .puts = sandbox_serial_puts, + .getc = sandbox_serial_getc, + .tstc = sandbox_serial_tstc, +}; + +void sandbox_serial_initialize(void) +{ + serial_register(&sandbox_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &sandbox_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial.c b/qemu/roms/u-boot/drivers/serial/serial.c new file mode 100644 index 000000000..fd61a5e54 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial.c @@ -0,0 +1,577 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static struct serial_device *serial_devices; +static struct serial_device *serial_current; +/* + * Table with supported baudrates (defined in config_xyz.h) + */ +static const unsigned long baudrate_table[] = CONFIG_SYS_BAUDRATE_TABLE; + +/** + * serial_null() - Void registration routine of a serial driver + * + * This routine implements a void registration routine of a serial + * driver. The registration routine of a particular driver is aliased + * to this empty function in case the driver is not compiled into + * U-Boot. + */ +static void serial_null(void) +{ +} + +/** + * on_baudrate() - Update the actual baudrate when the env var changes + * + * This will check for a valid baudrate and only apply it if valid. + */ +static int on_baudrate(const char *name, const char *value, enum env_op op, + int flags) +{ + int i; + int baudrate; + + switch (op) { + case env_op_create: + case env_op_overwrite: + /* + * Switch to new baudrate if new baudrate is supported + */ + baudrate = simple_strtoul(value, NULL, 10); + + /* Not actually changing */ + if (gd->baudrate == baudrate) + return 0; + + for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) { + if (baudrate == baudrate_table[i]) + break; + } + if (i == ARRAY_SIZE(baudrate_table)) { + if ((flags & H_FORCE) == 0) + printf("## Baudrate %d bps not supported\n", + baudrate); + return 1; + } + if ((flags & H_INTERACTIVE) != 0) { + printf("## Switch baudrate to %d" + " bps and press ENTER ...\n", baudrate); + udelay(50000); + } + + gd->baudrate = baudrate; + + serial_setbrg(); + + udelay(50000); + + if ((flags & H_INTERACTIVE) != 0) + while (1) { + if (getc() == '\r') + break; + } + + return 0; + case env_op_delete: + printf("## Baudrate may not be deleted\n"); + return 1; + default: + return 0; + } +} +U_BOOT_ENV_CALLBACK(baudrate, on_baudrate); + +/** + * serial_initfunc() - Forward declare of driver registration routine + * @name: Name of the real driver registration routine. + * + * This macro expands onto forward declaration of a driver registration + * routine, which is then used below in serial_initialize() function. + * The declaration is made weak and aliases to serial_null() so in case + * the driver is not compiled in, the function is still declared and can + * be used, but aliases to serial_null() and thus is optimized away. + */ +#define serial_initfunc(name) \ + void name(void) \ + __attribute__((weak, alias("serial_null"))); + +serial_initfunc(mpc8xx_serial_initialize); +serial_initfunc(ns16550_serial_initialize); +serial_initfunc(pxa_serial_initialize); +serial_initfunc(s3c24xx_serial_initialize); +serial_initfunc(s5p_serial_initialize); +serial_initfunc(zynq_serial_initialize); +serial_initfunc(bfin_serial_initialize); +serial_initfunc(bfin_jtag_initialize); +serial_initfunc(mpc512x_serial_initialize); +serial_initfunc(uartlite_serial_initialize); +serial_initfunc(au1x00_serial_initialize); +serial_initfunc(asc_serial_initialize); +serial_initfunc(jz_serial_initialize); +serial_initfunc(mpc5xx_serial_initialize); +serial_initfunc(mpc8260_scc_serial_initialize); +serial_initfunc(mpc8260_smc_serial_initialize); +serial_initfunc(mpc85xx_serial_initialize); +serial_initfunc(iop480_serial_initialize); +serial_initfunc(leon2_serial_initialize); +serial_initfunc(leon3_serial_initialize); +serial_initfunc(marvell_serial_initialize); +serial_initfunc(amirix_serial_initialize); +serial_initfunc(bmw_serial_initialize); +serial_initfunc(cogent_serial_initialize); +serial_initfunc(cpci750_serial_initialize); +serial_initfunc(evb64260_serial_initialize); +serial_initfunc(ml2_serial_initialize); +serial_initfunc(sconsole_serial_initialize); +serial_initfunc(p3mx_serial_initialize); +serial_initfunc(altera_jtag_serial_initialize); +serial_initfunc(altera_serial_initialize); +serial_initfunc(atmel_serial_initialize); +serial_initfunc(lpc32xx_serial_initialize); +serial_initfunc(mcf_serial_initialize); +serial_initfunc(oc_serial_initialize); +serial_initfunc(sandbox_serial_initialize); +serial_initfunc(clps7111_serial_initialize); +serial_initfunc(imx_serial_initialize); +serial_initfunc(ks8695_serial_initialize); +serial_initfunc(lh7a40x_serial_initialize); +serial_initfunc(max3100_serial_initialize); +serial_initfunc(mxc_serial_initialize); +serial_initfunc(pl01x_serial_initialize); +serial_initfunc(sa1100_serial_initialize); +serial_initfunc(sh_serial_initialize); +serial_initfunc(arm_dcc_initialize); +serial_initfunc(mxs_auart_initialize); +serial_initfunc(arc_serial_initialize); + +/** + * serial_register() - Register serial driver with serial driver core + * @dev: Pointer to the serial driver structure + * + * This function registers the serial driver supplied via @dev with + * serial driver core, thus making U-Boot aware of it and making it + * available for U-Boot to use. On platforms that still require manual + * relocation of constant variables, relocation of the supplied structure + * is performed. + */ +void serial_register(struct serial_device *dev) +{ +#ifdef CONFIG_NEEDS_MANUAL_RELOC + if (dev->start) + dev->start += gd->reloc_off; + if (dev->stop) + dev->stop += gd->reloc_off; + if (dev->setbrg) + dev->setbrg += gd->reloc_off; + if (dev->getc) + dev->getc += gd->reloc_off; + if (dev->tstc) + dev->tstc += gd->reloc_off; + if (dev->putc) + dev->putc += gd->reloc_off; + if (dev->puts) + dev->puts += gd->reloc_off; +#endif + + dev->next = serial_devices; + serial_devices = dev; +} + +/** + * serial_initialize() - Register all compiled-in serial port drivers + * + * This function registers all serial port drivers that are compiled + * into the U-Boot binary with the serial core, thus making them + * available to U-Boot to use. Lastly, this function assigns a default + * serial port to the serial core. That serial port is then used as a + * default output. + */ +void serial_initialize(void) +{ + mpc8xx_serial_initialize(); + ns16550_serial_initialize(); + pxa_serial_initialize(); + s3c24xx_serial_initialize(); + s5p_serial_initialize(); + mpc512x_serial_initialize(); + bfin_serial_initialize(); + bfin_jtag_initialize(); + uartlite_serial_initialize(); + zynq_serial_initialize(); + au1x00_serial_initialize(); + asc_serial_initialize(); + jz_serial_initialize(); + mpc5xx_serial_initialize(); + mpc8260_scc_serial_initialize(); + mpc8260_smc_serial_initialize(); + mpc85xx_serial_initialize(); + iop480_serial_initialize(); + leon2_serial_initialize(); + leon3_serial_initialize(); + marvell_serial_initialize(); + amirix_serial_initialize(); + bmw_serial_initialize(); + cogent_serial_initialize(); + cpci750_serial_initialize(); + evb64260_serial_initialize(); + ml2_serial_initialize(); + sconsole_serial_initialize(); + p3mx_serial_initialize(); + altera_jtag_serial_initialize(); + altera_serial_initialize(); + atmel_serial_initialize(); + lpc32xx_serial_initialize(); + mcf_serial_initialize(); + oc_serial_initialize(); + sandbox_serial_initialize(); + clps7111_serial_initialize(); + imx_serial_initialize(); + ks8695_serial_initialize(); + lh7a40x_serial_initialize(); + max3100_serial_initialize(); + mxc_serial_initialize(); + pl01x_serial_initialize(); + sa1100_serial_initialize(); + sh_serial_initialize(); + arm_dcc_initialize(); + mxs_auart_initialize(); + arc_serial_initialize(); + + serial_assign(default_serial_console()->name); +} + +/** + * serial_stdio_init() - Register serial ports with STDIO core + * + * This function generates a proxy driver for each serial port driver. + * These proxy drivers then register with the STDIO core, making the + * serial drivers available as STDIO devices. + */ +void serial_stdio_init(void) +{ + struct stdio_dev dev; + struct serial_device *s = serial_devices; + + while (s) { + memset(&dev, 0, sizeof(dev)); + + strcpy(dev.name, s->name); + dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; + + dev.start = s->start; + dev.stop = s->stop; + dev.putc = s->putc; + dev.puts = s->puts; + dev.getc = s->getc; + dev.tstc = s->tstc; + + stdio_register(&dev); + + s = s->next; + } +} + +/** + * serial_assign() - Select the serial output device by name + * @name: Name of the serial driver to be used as default output + * + * This function configures the serial output multiplexing by + * selecting which serial device will be used as default. In case + * the STDIO "serial" device is selected as stdin/stdout/stderr, + * the serial device previously configured by this function will be + * used for the particular operation. + * + * Returns 0 on success, negative on error. + */ +int serial_assign(const char *name) +{ + struct serial_device *s; + + for (s = serial_devices; s; s = s->next) { + if (strcmp(s->name, name)) + continue; + serial_current = s; + return 0; + } + + return -EINVAL; +} + +/** + * serial_reinit_all() - Reinitialize all compiled-in serial ports + * + * This function reinitializes all serial ports that are compiled + * into U-Boot by calling their serial_start() functions. + */ +void serial_reinit_all(void) +{ + struct serial_device *s; + + for (s = serial_devices; s; s = s->next) + s->start(); +} + +/** + * get_current() - Return pointer to currently selected serial port + * + * This function returns a pointer to currently selected serial port. + * The currently selected serial port is altered by serial_assign() + * function. + * + * In case this function is called before relocation or before any serial + * port is configured, this function calls default_serial_console() to + * determine the serial port. Otherwise, the configured serial port is + * returned. + * + * Returns pointer to the currently selected serial port on success, + * NULL on error. + */ +static struct serial_device *get_current(void) +{ + struct serial_device *dev; + + if (!(gd->flags & GD_FLG_RELOC)) + dev = default_serial_console(); + else if (!serial_current) + dev = default_serial_console(); + else + dev = serial_current; + + /* We must have a console device */ + if (!dev) { +#ifdef CONFIG_SPL_BUILD + puts("Cannot find console\n"); + hang(); +#else + panic("Cannot find console\n"); +#endif + } + + return dev; +} + +/** + * serial_init() - Initialize currently selected serial port + * + * This function initializes the currently selected serial port. This + * usually involves setting up the registers of that particular port, + * enabling clock and such. This function uses the get_current() call + * to determine which port is selected. + * + * Returns 0 on success, negative on error. + */ +int serial_init(void) +{ + return get_current()->start(); +} + +/** + * serial_setbrg() - Configure baud-rate of currently selected serial port + * + * This function configures the baud-rate of the currently selected + * serial port. The baud-rate is retrieved from global data within + * the serial port driver. This function uses the get_current() call + * to determine which port is selected. + * + * Returns 0 on success, negative on error. + */ +void serial_setbrg(void) +{ + get_current()->setbrg(); +} + +/** + * serial_getc() - Read character from currently selected serial port + * + * This function retrieves a character from currently selected serial + * port. In case there is no character waiting on the serial port, + * this function will block and wait for the character to appear. This + * function uses the get_current() call to determine which port is + * selected. + * + * Returns the character on success, negative on error. + */ +int serial_getc(void) +{ + return get_current()->getc(); +} + +/** + * serial_tstc() - Test if data is available on currently selected serial port + * + * This function tests if one or more characters are available on + * currently selected serial port. This function never blocks. This + * function uses the get_current() call to determine which port is + * selected. + * + * Returns positive if character is available, zero otherwise. + */ +int serial_tstc(void) +{ + return get_current()->tstc(); +} + +/** + * serial_putc() - Output character via currently selected serial port + * @c: Single character to be output from the serial port. + * + * This function outputs a character via currently selected serial + * port. This character is passed to the serial port driver responsible + * for controlling the hardware. The hardware may still be in process + * of transmitting another character, therefore this function may block + * for a short amount of time. This function uses the get_current() + * call to determine which port is selected. + */ +void serial_putc(const char c) +{ + get_current()->putc(c); +} + +/** + * serial_puts() - Output string via currently selected serial port + * @s: Zero-terminated string to be output from the serial port. + * + * This function outputs a zero-terminated string via currently + * selected serial port. This function behaves as an accelerator + * in case the hardware can queue multiple characters for transfer. + * The whole string that is to be output is available to the function + * implementing the hardware manipulation. Transmitting the whole + * string may take some time, thus this function may block for some + * amount of time. This function uses the get_current() call to + * determine which port is selected. + */ +void serial_puts(const char *s) +{ + get_current()->puts(s); +} + +/** + * default_serial_puts() - Output string by calling serial_putc() in loop + * @s: Zero-terminated string to be output from the serial port. + * + * This function outputs a zero-terminated string by calling serial_putc() + * in a loop. Most drivers do not support queueing more than one byte for + * transfer, thus this function precisely implements their serial_puts(). + * + * To optimize the number of get_current() calls, this function only + * calls get_current() once and then directly accesses the putc() call + * of the &struct serial_device . + */ +void default_serial_puts(const char *s) +{ + struct serial_device *dev = get_current(); + while (*s) + dev->putc(*s++); +} + +#if CONFIG_POST & CONFIG_SYS_POST_UART +static const int bauds[] = CONFIG_SYS_BAUDRATE_TABLE; + +/** + * uart_post_test() - Test the currently selected serial port using POST + * @flags: POST framework flags + * + * Do a loopback test of the currently selected serial port. This + * function is only useful in the context of the POST testing framwork. + * The serial port is firstly configured into loopback mode and then + * characters are sent through it. + * + * Returns 0 on success, value otherwise. + */ +/* Mark weak until post/cpu/.../uart.c migrate over */ +__weak +int uart_post_test(int flags) +{ + unsigned char c; + int ret, saved_baud, b; + struct serial_device *saved_dev, *s; + + /* Save current serial state */ + ret = 0; + saved_dev = serial_current; + saved_baud = gd->baudrate; + + for (s = serial_devices; s; s = s->next) { + /* If this driver doesn't support loop back, skip it */ + if (!s->loop) + continue; + + /* Test the next device */ + serial_current = s; + + ret = serial_init(); + if (ret) + goto done; + + /* Consume anything that happens to be queued */ + while (serial_tstc()) + serial_getc(); + + /* Enable loop back */ + s->loop(1); + + /* Test every available baud rate */ + for (b = 0; b < ARRAY_SIZE(bauds); ++b) { + gd->baudrate = bauds[b]; + serial_setbrg(); + + /* + * Stick to printable chars to avoid issues: + * - terminal corruption + * - serial program reacting to sequences and sending + * back random extra data + * - most serial drivers add in extra chars (like \r\n) + */ + for (c = 0x20; c < 0x7f; ++c) { + /* Send it out */ + serial_putc(c); + + /* Make sure it's the same one */ + ret = (c != serial_getc()); + if (ret) { + s->loop(0); + goto done; + } + + /* Clean up the output in case it was sent */ + serial_putc('\b'); + ret = ('\b' != serial_getc()); + if (ret) { + s->loop(0); + goto done; + } + } + } + + /* Disable loop back */ + s->loop(0); + + /* XXX: There is no serial_stop() !? */ + if (s->stop) + s->stop(); + } + + done: + /* Restore previous serial state */ + serial_current = saved_dev; + gd->baudrate = saved_baud; + serial_reinit_all(); + serial_setbrg(); + + return ret; +} +#endif diff --git a/qemu/roms/u-boot/drivers/serial/serial_arc.c b/qemu/roms/u-boot/drivers/serial/serial_arc.c new file mode 100644 index 000000000..2ddbf32a5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_arc.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct arc_serial_regs { + unsigned int id0; + unsigned int id1; + unsigned int id2; + unsigned int id3; + unsigned int data; + unsigned int status; + unsigned int baudl; + unsigned int baudh; +}; + +/* Bit definitions of STATUS register */ +#define UART_RXEMPTY (1 << 5) +#define UART_OVERFLOW_ERR (1 << 1) +#define UART_TXEMPTY (1 << 7) + +struct arc_serial_regs *regs; + +static void arc_serial_setbrg(void) +{ + int arc_console_baud; + + if (!gd->baudrate) + gd->baudrate = CONFIG_BAUDRATE; + + arc_console_baud = gd->cpu_clk / (gd->baudrate * 4) - 1; + writeb(arc_console_baud & 0xff, ®s->baudl); + +#ifdef CONFIG_ARC + /* + * UART ISS(Instruction Set simulator) emulation has a subtle bug: + * A existing value of Baudh = 0 is used as a indication to startup + * it's internal state machine. + * Thus if baudh is set to 0, 2 times, it chokes. + * This happens with BAUD=115200 and the formaula above + * Until that is fixed, when running on ISS, we will set baudh to !0 + */ + if (gd->arch.running_on_hw) + writeb((arc_console_baud & 0xff00) >> 8, ®s->baudh); + else + writeb(1, ®s->baudh); +#else + writeb((arc_console_baud & 0xff00) >> 8, ®s->baudh); +#endif +} + +static int arc_serial_init(void) +{ + regs = (struct arc_serial_regs *)CONFIG_ARC_UART_BASE; + serial_setbrg(); + return 0; +} + +static void arc_serial_putc(const char c) +{ + if (c == '\n') + arc_serial_putc('\r'); + + while (!(readb(®s->status) & UART_TXEMPTY)) + ; + + writeb(c, ®s->data); +} + +static int arc_serial_tstc(void) +{ + return !(readb(®s->status) & UART_RXEMPTY); +} + +static int arc_serial_getc(void) +{ + while (!arc_serial_tstc()) + ; + + /* Check for overflow errors */ + if (readb(®s->status) & UART_OVERFLOW_ERR) + return 0; + + return readb(®s->data) & 0xFF; +} + +static struct serial_device arc_serial_drv = { + .name = "arc_serial", + .start = arc_serial_init, + .stop = NULL, + .setbrg = arc_serial_setbrg, + .putc = arc_serial_putc, + .puts = default_serial_puts, + .getc = arc_serial_getc, + .tstc = arc_serial_tstc, +}; + +void arc_serial_initialize(void) +{ + serial_register(&arc_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &arc_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_bfin.c b/qemu/roms/u-boot/drivers/serial/serial_bfin.c new file mode 100644 index 000000000..0443b8427 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_bfin.c @@ -0,0 +1,411 @@ +/* + * U-boot - serial.c Blackfin Serial Driver + * + * Copyright (c) 2005-2008 Analog Devices Inc. + * + * Copyright (c) 2003 Bas Vermeulen , + * BuyWays B.V. (www.buyways.nl) + * + * Based heavily on: + * blkfinserial.c: Serial driver for BlackFin DSP internal USRTs. + * Copyright(c) 2003 Metrowerks + * Copyright(c) 2001 Tony Z. Kou + * Copyright(c) 2001-2002 Arcturus Networks Inc. + * + * Based on code from 68328 version serial driver imlpementation which was: + * Copyright (C) 1995 David S. Miller + * Copyright (C) 1998 Kenneth Albanowski + * Copyright (C) 1998, 1999 D. Jeff Dionne + * Copyright (C) 1999 Vladimir Gurevich + * + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Licensed under the GPL-2 or later. + */ + +/* Anomaly notes: + * 05000086 - we don't support autobaud + * 05000099 - we only use DR bit, so losing others is not a problem + * 05000100 - we don't use the UART_IIR register + * 05000215 - we poll the uart (no dma/interrupts) + * 05000225 - no workaround possible, but this shouldnt cause errors ... + * 05000230 - we tweak the baud rate calculation slightly + * 05000231 - we always use 1 stop bit + * 05000309 - we always enable the uart before we modify it in anyway + * 05000350 - we always enable the uart regardless of boot mode + * 05000363 - we don't support break signals, so don't generate one + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_UART_CONSOLE + +#ifdef CONFIG_DEBUG_SERIAL +static uart_lsr_t cached_lsr[256]; +static uart_lsr_t cached_rbr[256]; +static size_t cache_count; + +/* The LSR is read-to-clear on some parts, so we have to make sure status + * bits aren't inadvertently lost when doing various tests. This also + * works around anomaly 05000099 at the same time by keeping a cumulative + * tally of all the status bits. + */ +static uart_lsr_t uart_lsr_save; +static uart_lsr_t uart_lsr_read(uint32_t uart_base) +{ + uart_lsr_t lsr = _lsr_read(pUART); + uart_lsr_save |= (lsr & (OE|PE|FE|BI)); + return lsr | uart_lsr_save; +} +/* Just do the clear for everyone since it can't hurt. */ +static void uart_lsr_clear(uint32_t uart_base) +{ + uart_lsr_save = 0; + _lsr_write(pUART, -1); +} +#else +/* When debugging is disabled, we only care about the DR bit, so if other + * bits get set/cleared, we don't really care since we don't read them + * anyways (and thus anomaly 05000099 is irrelevant). + */ +static inline uart_lsr_t uart_lsr_read(uint32_t uart_base) +{ + return _lsr_read(pUART); +} +static void uart_lsr_clear(uint32_t uart_base) +{ + _lsr_write(pUART, -1); +} +#endif + +static void uart_putc(uint32_t uart_base, const char c) +{ + /* send a \r for compatibility */ + if (c == '\n') + serial_putc('\r'); + + WATCHDOG_RESET(); + + /* wait for the hardware fifo to clear up */ + while (!(uart_lsr_read(uart_base) & THRE)) + continue; + + /* queue the character for transmission */ + bfin_write(&pUART->thr, c); + SSYNC(); + + WATCHDOG_RESET(); +} + +static int uart_tstc(uint32_t uart_base) +{ + WATCHDOG_RESET(); + return (uart_lsr_read(uart_base) & DR) ? 1 : 0; +} + +static int uart_getc(uint32_t uart_base) +{ + uint16_t uart_rbr_val; + + /* wait for data ! */ + while (!uart_tstc(uart_base)) + continue; + + /* grab the new byte */ + uart_rbr_val = bfin_read(&pUART->rbr); + +#ifdef CONFIG_DEBUG_SERIAL + /* grab & clear the LSR */ + uart_lsr_t uart_lsr_val = uart_lsr_read(uart_base); + + cached_lsr[cache_count] = uart_lsr_val; + cached_rbr[cache_count] = uart_rbr_val; + cache_count = (cache_count + 1) % ARRAY_SIZE(cached_lsr); + + if (uart_lsr_val & (OE|PE|FE|BI)) { + printf("\n[SERIAL ERROR]\n"); + do { + --cache_count; + printf("\t%3zu: RBR=0x%02x LSR=0x%02x\n", cache_count, + cached_rbr[cache_count], cached_lsr[cache_count]); + } while (cache_count > 0); + return -1; + } +#endif + uart_lsr_clear(uart_base); + + return uart_rbr_val; +} + +#if CONFIG_POST & CONFIG_SYS_POST_UART +# define LOOP(x) x +#else +# define LOOP(x) +#endif + +#if BFIN_UART_HW_VER < 4 + +LOOP( +static void uart_loop(uint32_t uart_base, int state) +{ + u16 mcr; + + /* Drain the TX fifo first so bytes don't come back */ + while (!(uart_lsr_read(uart_base) & TEMT)) + continue; + + mcr = bfin_read(&pUART->mcr); + if (state) + mcr |= LOOP_ENA | MRTS; + else + mcr &= ~(LOOP_ENA | MRTS); + bfin_write(&pUART->mcr, mcr); +} +) + +#else + +LOOP( +static void uart_loop(uint32_t uart_base, int state) +{ + u32 control; + + /* Drain the TX fifo first so bytes don't come back */ + while (!(uart_lsr_read(uart_base) & TEMT)) + continue; + + control = bfin_read(&pUART->control); + if (state) + control |= LOOP_ENA | MRTS; + else + control &= ~(LOOP_ENA | MRTS); + bfin_write(&pUART->control, control); +} +) + +#endif + +static inline void __serial_set_baud(uint32_t uart_base, uint32_t baud) +{ +#ifdef CONFIG_DEBUG_EARLY_SERIAL + serial_early_set_baud(uart_base, baud); +#else + uint16_t divisor = (get_uart_clk() + (baud * 8)) / (baud * 16) + - ANOMALY_05000230; + + /* Program the divisor to get the baud rate we want */ + serial_set_divisor(uart_base, divisor); +#endif +} + +static void uart_puts(uint32_t uart_base, const char *s) +{ + while (*s) + uart_putc(uart_base, *s++); +} + +#define DECL_BFIN_UART(n) \ +static int uart##n##_init(void) \ +{ \ + const unsigned short pins[] = { _P_UART(n, RX), _P_UART(n, TX), 0, }; \ + peripheral_request_list(pins, "bfin-uart"); \ + uart_init(MMR_UART(n)); \ + __serial_set_baud(MMR_UART(n), gd->baudrate); \ + uart_lsr_clear(MMR_UART(n)); \ + return 0; \ +} \ +\ +static int uart##n##_uninit(void) \ +{ \ + return serial_early_uninit(MMR_UART(n)); \ +} \ +\ +static void uart##n##_setbrg(void) \ +{ \ + __serial_set_baud(MMR_UART(n), gd->baudrate); \ +} \ +\ +static int uart##n##_getc(void) \ +{ \ + return uart_getc(MMR_UART(n)); \ +} \ +\ +static int uart##n##_tstc(void) \ +{ \ + return uart_tstc(MMR_UART(n)); \ +} \ +\ +static void uart##n##_putc(const char c) \ +{ \ + uart_putc(MMR_UART(n), c); \ +} \ +\ +static void uart##n##_puts(const char *s) \ +{ \ + uart_puts(MMR_UART(n), s); \ +} \ +\ +LOOP( \ +static void uart##n##_loop(int state) \ +{ \ + uart_loop(MMR_UART(n), state); \ +} \ +) \ +\ +struct serial_device bfin_serial##n##_device = { \ + .name = "bfin_uart"#n, \ + .start = uart##n##_init, \ + .stop = uart##n##_uninit, \ + .setbrg = uart##n##_setbrg, \ + .getc = uart##n##_getc, \ + .tstc = uart##n##_tstc, \ + .putc = uart##n##_putc, \ + .puts = uart##n##_puts, \ + LOOP(.loop = uart##n##_loop) \ +}; + +#ifdef UART0_RBR +DECL_BFIN_UART(0) +#endif +#ifdef UART1_RBR +DECL_BFIN_UART(1) +#endif +#ifdef UART2_RBR +DECL_BFIN_UART(2) +#endif +#ifdef UART3_RBR +DECL_BFIN_UART(3) +#endif + +__weak struct serial_device *default_serial_console(void) +{ +#if CONFIG_UART_CONSOLE == 0 + return &bfin_serial0_device; +#elif CONFIG_UART_CONSOLE == 1 + return &bfin_serial1_device; +#elif CONFIG_UART_CONSOLE == 2 + return &bfin_serial2_device; +#elif CONFIG_UART_CONSOLE == 3 + return &bfin_serial3_device; +#endif +} + +void bfin_serial_initialize(void) +{ +#ifdef UART0_RBR + serial_register(&bfin_serial0_device); +#endif +#ifdef UART1_RBR + serial_register(&bfin_serial1_device); +#endif +#ifdef UART2_RBR + serial_register(&bfin_serial2_device); +#endif +#ifdef UART3_RBR + serial_register(&bfin_serial3_device); +#endif +} + +#ifdef CONFIG_DEBUG_EARLY_SERIAL +inline void uart_early_putc(uint32_t uart_base, const char c) +{ + /* send a \r for compatibility */ + if (c == '\n') + uart_early_putc(uart_base, '\r'); + + /* wait for the hardware fifo to clear up */ + while (!(_lsr_read(pUART) & THRE)) + continue; + + /* queue the character for transmission */ + bfin_write(&pUART->thr, c); + SSYNC(); +} + +void uart_early_puts(const char *s) +{ + while (*s) + uart_early_putc(UART_BASE, *s++); +} + +/* Symbol for our assembly to call. */ +void _serial_early_set_baud(uint32_t baud) +{ + serial_early_set_baud(UART_BASE, baud); +} + +/* Symbol for our assembly to call. */ +void _serial_early_init(void) +{ + serial_early_init(UART_BASE); +} +#endif + +#elif defined(CONFIG_UART_MEM) + +char serial_logbuf[CONFIG_UART_MEM]; +char *serial_logbuf_head = serial_logbuf; + +int serial_mem_init(void) +{ + serial_logbuf_head = serial_logbuf; + return 0; +} + +void serial_mem_setbrg(void) +{ +} + +int serial_mem_tstc(void) +{ + return 0; +} + +int serial_mem_getc(void) +{ + return 0; +} + +void serial_mem_putc(const char c) +{ + *serial_logbuf_head = c; + if (++serial_logbuf_head == serial_logbuf + CONFIG_UART_MEM) + serial_logbuf_head = serial_logbuf; +} + +void serial_mem_puts(const char *s) +{ + while (*s) + serial_putc(*s++); +} + +struct serial_device bfin_serial_mem_device = { + .name = "bfin_uart_mem", + .start = serial_mem_init, + .setbrg = serial_mem_setbrg, + .getc = serial_mem_getc, + .tstc = serial_mem_tstc, + .putc = serial_mem_putc, + .puts = serial_mem_puts, +}; + + +__weak struct serial_device *default_serial_console(void) +{ + return &bfin_serial_mem_device; +} + +void bfin_serial_initialize(void) +{ + serial_register(&bfin_serial_mem_device); +} +#endif /* CONFIG_UART_MEM */ diff --git a/qemu/roms/u-boot/drivers/serial/serial_imx.c b/qemu/roms/u-boot/drivers/serial/serial_imx.c new file mode 100644 index 000000000..d43a5fedc --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_imx.c @@ -0,0 +1,223 @@ +/* + * (c) 2004 Sascha Hauer + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#if defined CONFIG_IMX_SERIAL1 +#define UART_BASE IMX_UART1_BASE +#elif defined CONFIG_IMX_SERIAL2 +#define UART_BASE IMX_UART2_BASE +#else +#error "define CONFIG_IMX_SERIAL1, CONFIG_IMX_SERIAL2 or CONFIG_IMX_SERIAL_NONE" +#endif + +struct imx_serial { + volatile uint32_t urxd[16]; + volatile uint32_t utxd[16]; + volatile uint32_t ucr1; + volatile uint32_t ucr2; + volatile uint32_t ucr3; + volatile uint32_t ucr4; + volatile uint32_t ufcr; + volatile uint32_t usr1; + volatile uint32_t usr2; + volatile uint32_t uesc; + volatile uint32_t utim; + volatile uint32_t ubir; + volatile uint32_t ubmr; + volatile uint32_t ubrc; + volatile uint32_t bipr[4]; + volatile uint32_t bmpr[4]; + volatile uint32_t uts; +}; + +DECLARE_GLOBAL_DATA_PTR; + +static void imx_serial_setbrg(void) +{ + serial_init(); +} + +extern void imx_gpio_mode(int gpio_mode); + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + * + */ +static int imx_serial_init(void) +{ + volatile struct imx_serial* base = (struct imx_serial *)UART_BASE; + unsigned int ufcr_rfdiv; + unsigned int refclk; + +#ifdef CONFIG_IMX_SERIAL1 + imx_gpio_mode(PC11_PF_UART1_TXD); + imx_gpio_mode(PC12_PF_UART1_RXD); +#else + imx_gpio_mode(PB30_PF_UART2_TXD); + imx_gpio_mode(PB31_PF_UART2_RXD); +#endif + + /* Disable UART */ + base->ucr1 &= ~UCR1_UARTEN; + + /* Set to default POR state */ + + base->ucr1 = 0x00000004; + base->ucr2 = 0x00000000; + base->ucr3 = 0x00000000; + base->ucr4 = 0x00008040; + base->uesc = 0x0000002B; + base->utim = 0x00000000; + base->ubir = 0x00000000; + base->ubmr = 0x00000000; + base->uts = 0x00000000; + /* Set clocks */ + base->ucr4 |= UCR4_REF16; + + /* Configure FIFOs */ + base->ufcr = 0xa81; + + /* set the baud rate. + * + * baud * 16 x + * --------- = - + * refclk y + * + * x - 1 = UBIR + * y - 1 = UBMR + * + * each register is 16 bits wide. refclk max is 96 MHz + * + */ + + ufcr_rfdiv = ((base->ufcr) & UFCR_RFDIV) >> 7; + if (ufcr_rfdiv == 6) + ufcr_rfdiv = 7; + else + ufcr_rfdiv = 6 - ufcr_rfdiv; + + refclk = get_PERCLK1(); + refclk /= ufcr_rfdiv; + + /* Set the numerator value minus one of the BRM ratio */ + base->ubir = (gd->baudrate / 100) - 1; + + /* Set the denominator value minus one of the BRM ratio */ + base->ubmr = (refclk/(16 * 100)) - 1; + + /* Set to 8N1 */ + base->ucr2 &= ~UCR2_PREN; + base->ucr2 |= UCR2_WS; + base->ucr2 &= ~UCR2_STPB; + + /* Ignore RTS */ + base->ucr2 |= UCR2_IRTS; + + /* Enable UART */ + base->ucr1 |= UCR1_UARTEN | UCR1_UARTCLKEN; + + /* Enable FIFOs */ + base->ucr2 |= UCR2_SRST | UCR2_RXEN | UCR2_TXEN; + + /* Clear status flags */ + base->usr2 |= USR2_ADET | + USR2_DTRF | + USR2_IDLE | + USR2_IRINT | + USR2_WAKE | + USR2_RTSF | + USR2_BRCD | + USR2_ORE; + + /* Clear status flags */ + base->usr1 |= USR1_PARITYERR | + USR1_RTSD | + USR1_ESCF | + USR1_FRAMERR | + USR1_AIRINT | + USR1_AWAKE; + return (0); +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is successful, the character read is + * written into its argument c. + */ +static int imx_serial_getc(void) +{ + volatile struct imx_serial* base = (struct imx_serial *)UART_BASE; + unsigned char ch; + + while(base->uts & UTS_RXEMPTY); + + ch = (char)base->urxd[0]; + + return ch; +} + +#ifdef CONFIG_HWFLOW +static int hwflow = 0; /* turned off by default */ +int hwflow_onoff(int on) +{ +} +#endif + +/* + * Output a single byte to the serial port. + */ +static void imx_serial_putc(const char c) +{ + volatile struct imx_serial* base = (struct imx_serial *)UART_BASE; + + /* Wait for Tx FIFO not full */ + while (base->uts & UTS_TXFULL); + + base->utxd[0] = c; + + /* If \n, also do \r */ + if (c == '\n') + serial_putc ('\r'); +} + +/* + * Test whether a character is in the RX buffer + */ +static int imx_serial_tstc(void) +{ + volatile struct imx_serial* base = (struct imx_serial *)UART_BASE; + + /* If receive fifo is empty, return false */ + if (base->uts & UTS_RXEMPTY) + return 0; + return 1; +} + +static struct serial_device imx_serial_drv = { + .name = "imx_serial", + .start = imx_serial_init, + .stop = NULL, + .setbrg = imx_serial_setbrg, + .putc = imx_serial_putc, + .puts = default_serial_puts, + .getc = imx_serial_getc, + .tstc = imx_serial_tstc, +}; + +void imx_serial_initialize(void) +{ + serial_register(&imx_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &imx_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_ks8695.c b/qemu/roms/u-boot/drivers/serial/serial_ks8695.c new file mode 100644 index 000000000..13adabd11 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_ks8695.c @@ -0,0 +1,121 @@ +/* + * serial.c -- KS8695 serial driver + * + * (C) Copyright 2004, Greg Ungerer + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#ifndef CONFIG_SERIAL1 +#error "Bad: you didn't configure serial ..." +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Define the UART hardware register access structure. + */ +struct ks8695uart { + unsigned int RX; /* 0x00 - Receive data (r) */ + unsigned int TX; /* 0x04 - Transmit data (w) */ + unsigned int FCR; /* 0x08 - Fifo Control (r/w) */ + unsigned int LCR; /* 0x0c - Line Control (r/w) */ + unsigned int MCR; /* 0x10 - Modem Control (r/w) */ + unsigned int LSR; /* 0x14 - Line Status (r/w) */ + unsigned int MSR; /* 0x18 - Modem Status (r/w) */ + unsigned int BD; /* 0x1c - Baud Rate (r/w) */ + unsigned int SR; /* 0x20 - Status (r/w) */ +}; + +#define KS8695_UART_ADDR ((void *) (KS8695_IO_BASE + KS8695_UART_RX_BUFFER)) +#define KS8695_UART_CLK 25000000 + + +/* + * Under some circumstances we want to be "quiet" and not issue any + * serial output - though we want u-boot to otherwise work and behave + * the same. By default be noisy. + */ +int serial_console = 1; + + +static void ks8695_serial_setbrg(void) +{ + volatile struct ks8695uart *uartp = KS8695_UART_ADDR; + + /* Set to global baud rate and 8 data bits, no parity, 1 stop bit*/ + uartp->BD = KS8695_UART_CLK / gd->baudrate; + uartp->LCR = KS8695_UART_LINEC_WLEN8; +} + +static int ks8695_serial_init(void) +{ + serial_console = 1; + serial_setbrg(); + return 0; +} + +static void ks8695_serial_raw_putc(const char c) +{ + volatile struct ks8695uart *uartp = KS8695_UART_ADDR; + int i; + + for (i = 0; (i < 0x100000); i++) { + if (uartp->LSR & KS8695_UART_LINES_TXFE) + break; + } + + uartp->TX = c; +} + +static void ks8695_serial_putc(const char c) +{ + if (serial_console) { + ks8695_serial_raw_putc(c); + if (c == '\n') + ks8695_serial_raw_putc('\r'); + } +} + +static int ks8695_serial_tstc(void) +{ + volatile struct ks8695uart *uartp = KS8695_UART_ADDR; + if (serial_console) + return ((uartp->LSR & KS8695_UART_LINES_RXFE) ? 1 : 0); + return 0; +} + +static int ks8695_serial_getc(void) +{ + volatile struct ks8695uart *uartp = KS8695_UART_ADDR; + + while ((uartp->LSR & KS8695_UART_LINES_RXFE) == 0) + ; + return (uartp->RX); +} + +static struct serial_device ks8695_serial_drv = { + .name = "ks8695_serial", + .start = ks8695_serial_init, + .stop = NULL, + .setbrg = ks8695_serial_setbrg, + .putc = ks8695_serial_putc, + .puts = default_serial_puts, + .getc = ks8695_serial_getc, + .tstc = ks8695_serial_tstc, +}; + +void ks8695_serial_initialize(void) +{ + serial_register(&ks8695_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &ks8695_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_lpuart.c b/qemu/roms/u-boot/drivers/serial/serial_lpuart.c new file mode 100644 index 000000000..da5f9a21f --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_lpuart.c @@ -0,0 +1,119 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#define US1_TDRE (1 << 7) +#define US1_RDRF (1 << 5) +#define UC2_TE (1 << 3) +#define UC2_RE (1 << 2) + +DECLARE_GLOBAL_DATA_PTR; + +struct lpuart_fsl *base = (struct lpuart_fsl *)LPUART_BASE; + +static void lpuart_serial_setbrg(void) +{ + u32 clk = mxc_get_clock(MXC_UART_CLK); + u16 sbr; + + if (!gd->baudrate) + gd->baudrate = CONFIG_BAUDRATE; + + sbr = (u16)(clk / (16 * gd->baudrate)); + /* place adjustment later - n/32 BRFA */ + + __raw_writeb(sbr >> 8, &base->ubdh); + __raw_writeb(sbr & 0xff, &base->ubdl); +} + +static int lpuart_serial_getc(void) +{ + u8 status; + + while (!(__raw_readb(&base->us1) & US1_RDRF)) + WATCHDOG_RESET(); + + status = __raw_readb(&base->us1); + status |= US1_RDRF; + __raw_writeb(status, &base->us1); + + return __raw_readb(&base->ud); +} + +static void lpuart_serial_putc(const char c) +{ + if (c == '\n') + serial_putc('\r'); + + while (!(__raw_readb(&base->us1) & US1_TDRE)) + WATCHDOG_RESET(); + + __raw_writeb(c, &base->ud); +} + +/* + * Test whether a character is in the RX buffer + */ +static int lpuart_serial_tstc(void) +{ + if (__raw_readb(&base->urcfifo) == 0) + return 0; + + return 1; +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +static int lpuart_serial_init(void) +{ + u8 ctrl; + + ctrl = __raw_readb(&base->uc2); + ctrl &= ~UC2_RE; + ctrl &= ~UC2_TE; + __raw_writeb(ctrl, &base->uc2); + + __raw_writeb(0, &base->umodem); + __raw_writeb(0, &base->uc1); + + /* provide data bits, parity, stop bit, etc */ + + serial_setbrg(); + + __raw_writeb(UC2_RE | UC2_TE, &base->uc2); + + return 0; +} + +static struct serial_device lpuart_serial_drv = { + .name = "lpuart_serial", + .start = lpuart_serial_init, + .stop = NULL, + .setbrg = lpuart_serial_setbrg, + .putc = lpuart_serial_putc, + .puts = default_serial_puts, + .getc = lpuart_serial_getc, + .tstc = lpuart_serial_tstc, +}; + +void lpuart_serial_initialize(void) +{ + serial_register(&lpuart_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &lpuart_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_max3100.c b/qemu/roms/u-boot/drivers/serial/serial_max3100.c new file mode 100644 index 000000000..027d9194a --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_max3100.c @@ -0,0 +1,294 @@ +/* + * (C) Copyright 2003 + * + * Pantelis Antoniou + * Intracom S.A. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/**************************************************************/ + +/* convienient macros */ +#define MAX3100_SPI_RXD() (MAX3100_SPI_RXD_PORT & MAX3100_SPI_RXD_BIT) + +#define MAX3100_SPI_TXD(x) \ + do { \ + if (x) \ + MAX3100_SPI_TXD_PORT |= MAX3100_SPI_TXD_BIT; \ + else \ + MAX3100_SPI_TXD_PORT &= ~MAX3100_SPI_TXD_BIT; \ + } while(0) + +#define MAX3100_SPI_CLK(x) \ + do { \ + if (x) \ + MAX3100_SPI_CLK_PORT |= MAX3100_SPI_CLK_BIT; \ + else \ + MAX3100_SPI_CLK_PORT &= ~MAX3100_SPI_CLK_BIT; \ + } while(0) + +#define MAX3100_SPI_CLK_TOGGLE() (MAX3100_SPI_CLK_PORT ^= MAX3100_SPI_CLK_BIT) + +#define MAX3100_CS(x) \ + do { \ + if (x) \ + MAX3100_CS_PORT |= MAX3100_CS_BIT; \ + else \ + MAX3100_CS_PORT &= ~MAX3100_CS_BIT; \ + } while(0) + +/**************************************************************/ + +/* MAX3100 definitions */ + +#define MAX3100_WC (3 << 14) /* write configuration */ +#define MAX3100_RC (1 << 14) /* read configuration */ +#define MAX3100_WD (2 << 14) /* write data */ +#define MAX3100_RD (0 << 14) /* read data */ + +/* configuration register bits */ +#define MAX3100_FEN (1 << 13) /* FIFO enable */ +#define MAX3100_SHDN (1 << 12) /* shutdown bit */ +#define MAX3100_TM (1 << 11) /* T bit irq mask */ +#define MAX3100_RM (1 << 10) /* R bit irq mask */ +#define MAX3100_PM (1 << 9) /* P bit irq mask */ +#define MAX3100_RAM (1 << 8) /* mask for RA/FE bit */ +#define MAX3100_IR (1 << 7) /* IRDA timing mode */ +#define MAX3100_ST (1 << 6) /* transmit stop bit */ +#define MAX3100_PE (1 << 5) /* parity enable bit */ +#define MAX3100_L (1 << 4) /* Length bit */ +#define MAX3100_B_MASK (0x000F) /* baud rate bits mask */ +#define MAX3100_B(x) ((x) & 0x000F) /* baud rate select bits */ + +/* data register bits (write) */ +#define MAX3100_TE (1 << 10) /* transmit enable bit (active low) */ +#define MAX3100_RTS (1 << 9) /* request-to-send bit (inverted ~RTS pin) */ + +/* data register bits (read) */ +#define MAX3100_RA (1 << 10) /* receiver activity when in shutdown mode */ +#define MAX3100_FE (1 << 10) /* framing error when in normal mode */ +#define MAX3100_CTS (1 << 9) /* clear-to-send bit (inverted ~CTS pin) */ + +/* data register bits (both directions) */ +#define MAX3100_R (1 << 15) /* receive bit */ +#define MAX3100_T (1 << 14) /* transmit bit */ +#define MAX3100_P (1 << 8) /* parity bit */ +#define MAX3100_D_MASK 0x00FF /* data bits mask */ +#define MAX3100_D(x) ((x) & 0x00FF) /* data bits */ + +/* these definitions are valid only for fOSC = 3.6864MHz */ +#define MAX3100_B_230400 MAX3100_B(0) +#define MAX3100_B_115200 MAX3100_B(1) +#define MAX3100_B_57600 MAX3100_B(2) +#define MAX3100_B_38400 MAX3100_B(9) +#define MAX3100_B_19200 MAX3100_B(10) +#define MAX3100_B_9600 MAX3100_B(11) +#define MAX3100_B_4800 MAX3100_B(12) +#define MAX3100_B_2400 MAX3100_B(13) +#define MAX3100_B_1200 MAX3100_B(14) +#define MAX3100_B_600 MAX3100_B(15) + +/**************************************************************/ + +static inline unsigned int max3100_transfer(unsigned int val) +{ + unsigned int rx; + int b; + + MAX3100_SPI_CLK(0); + MAX3100_CS(0); + + rx = 0; b = 16; + while (--b >= 0) { + MAX3100_SPI_TXD(val & 0x8000); + val <<= 1; + MAX3100_SPI_CLK_TOGGLE(); + udelay(1); + rx <<= 1; + if (MAX3100_SPI_RXD()) + rx |= 1; + MAX3100_SPI_CLK_TOGGLE(); + udelay(1); + } + + MAX3100_SPI_CLK(1); + MAX3100_CS(1); + + return rx; +} + +/**************************************************************/ + +/* must be power of 2 */ +#define RXFIFO_SZ 16 + +static int rxfifo_cnt; +static int rxfifo_in; +static int rxfifo_out; +static unsigned char rxfifo_buf[16]; + +static void max3100_serial_putc_raw(int c) +{ + unsigned int rx; + + while (((rx = max3100_transfer(MAX3100_RC)) & MAX3100_T) == 0) + WATCHDOG_RESET(); + + rx = max3100_transfer(MAX3100_WD | (c & 0xff)); + if ((rx & MAX3100_RD) != 0 && rxfifo_cnt < RXFIFO_SZ) { + rxfifo_cnt++; + rxfifo_buf[rxfifo_in++] = rx & 0xff; + rxfifo_in &= RXFIFO_SZ - 1; + } +} + +static int max3100_serial_getc(void) +{ + int c; + unsigned int rx; + + while (rxfifo_cnt == 0) { + rx = max3100_transfer(MAX3100_RD); + if ((rx & MAX3100_R) != 0) { + do { + rxfifo_cnt++; + rxfifo_buf[rxfifo_in++] = rx & 0xff; + rxfifo_in &= RXFIFO_SZ - 1; + + if (rxfifo_cnt >= RXFIFO_SZ) + break; + } while (((rx = max3100_transfer(MAX3100_RD)) & MAX3100_R) != 0); + } + WATCHDOG_RESET(); + } + + rxfifo_cnt--; + c = rxfifo_buf[rxfifo_out++]; + rxfifo_out &= RXFIFO_SZ - 1; + return c; +} + +static int max3100_serial_tstc(void) +{ + unsigned int rx; + + if (rxfifo_cnt > 0) + return 1; + + rx = max3100_transfer(MAX3100_RD); + if ((rx & MAX3100_R) == 0) + return 0; + + do { + rxfifo_cnt++; + rxfifo_buf[rxfifo_in++] = rx & 0xff; + rxfifo_in &= RXFIFO_SZ - 1; + + if (rxfifo_cnt >= RXFIFO_SZ) + break; + } while (((rx = max3100_transfer(MAX3100_RD)) & MAX3100_R) != 0); + + return 1; +} + +static int max3100_serial_init(void) +{ + unsigned int wconf, rconf; + int i; + + wconf = 0; + + /* Set baud rate */ + switch (gd->baudrate) { + case 1200: + wconf = MAX3100_B_1200; + break; + case 2400: + wconf = MAX3100_B_2400; + break; + case 4800: + wconf = MAX3100_B_4800; + break; + case 9600: + wconf = MAX3100_B_9600; + break; + case 19200: + wconf = MAX3100_B_19200; + break; + case 38400: + wconf = MAX3100_B_38400; + break; + case 57600: + wconf = MAX3100_B_57600; + break; + default: + case 115200: + wconf = MAX3100_B_115200; + break; + case 230400: + wconf = MAX3100_B_230400; + break; + } + + /* try for 10ms, with a 100us gap */ + for (i = 0; i < 10000; i += 100) { + + max3100_transfer(MAX3100_WC | wconf); + rconf = max3100_transfer(MAX3100_RC) & 0x3fff; + + if (rconf == wconf) + break; + udelay(100); + } + + rxfifo_in = rxfifo_out = rxfifo_cnt = 0; + + return (0); +} + +static void max3100_serial_putc(const char c) +{ + if (c == '\n') + max3100_serial_putc_raw('\r'); + + max3100_serial_putc_raw(c); +} + +static void max3100_serial_puts(const char *s) +{ + while (*s) + max3100_serial_putc_raw(*s++); +} + +static void max3100_serial_setbrg(void) +{ +} + +static struct serial_device max3100_serial_drv = { + .name = "max3100_serial", + .start = max3100_serial_init, + .stop = NULL, + .setbrg = max3100_serial_setbrg, + .putc = max3100_serial_putc, + .puts = max3100_serial_puts, + .getc = max3100_serial_getc, + .tstc = max3100_serial_tstc, +}; + +void max3100_serial_initialize(void) +{ + serial_register(&max3100_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &max3100_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_mxc.c b/qemu/roms/u-boot/drivers/serial/serial_mxc.c new file mode 100644 index 000000000..56bee5520 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_mxc.c @@ -0,0 +1,224 @@ +/* + * (c) 2007 Sascha Hauer + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#define __REG(x) (*((volatile u32 *)(x))) + +#ifndef CONFIG_MXC_UART_BASE +#error "define CONFIG_MXC_UART_BASE to use the MXC UART driver" +#endif + +#define UART_PHYS CONFIG_MXC_UART_BASE + +/* Register definitions */ +#define URXD 0x0 /* Receiver Register */ +#define UTXD 0x40 /* Transmitter Register */ +#define UCR1 0x80 /* Control Register 1 */ +#define UCR2 0x84 /* Control Register 2 */ +#define UCR3 0x88 /* Control Register 3 */ +#define UCR4 0x8c /* Control Register 4 */ +#define UFCR 0x90 /* FIFO Control Register */ +#define USR1 0x94 /* Status Register 1 */ +#define USR2 0x98 /* Status Register 2 */ +#define UESC 0x9c /* Escape Character Register */ +#define UTIM 0xa0 /* Escape Timer Register */ +#define UBIR 0xa4 /* BRM Incremental Register */ +#define UBMR 0xa8 /* BRM Modulator Register */ +#define UBRC 0xac /* Baud Rate Count Register */ +#define UTS 0xb4 /* UART Test Register (mx31) */ + +/* UART Control Register Bit Fields.*/ +#define URXD_CHARRDY (1<<15) +#define URXD_ERR (1<<14) +#define URXD_OVRRUN (1<<13) +#define URXD_FRMERR (1<<12) +#define URXD_BRK (1<<11) +#define URXD_PRERR (1<<10) +#define URXD_RX_DATA (0xFF) +#define UCR1_ADEN (1<<15) /* Auto dectect interrupt */ +#define UCR1_ADBR (1<<14) /* Auto detect baud rate */ +#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ +#define UCR1_IDEN (1<<12) /* Idle condition interrupt */ +#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ +#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ +#define UCR1_IREN (1<<7) /* Infrared interface enable */ +#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */ +#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ +#define UCR1_SNDBRK (1<<4) /* Send break */ +#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ +#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */ +#define UCR1_DOZE (1<<1) /* Doze */ +#define UCR1_UARTEN (1<<0) /* UART enabled */ +#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */ +#define UCR2_IRTS (1<<14) /* Ignore RTS pin */ +#define UCR2_CTSC (1<<13) /* CTS pin control */ +#define UCR2_CTS (1<<12) /* Clear to send */ +#define UCR2_ESCEN (1<<11) /* Escape enable */ +#define UCR2_PREN (1<<8) /* Parity enable */ +#define UCR2_PROE (1<<7) /* Parity odd/even */ +#define UCR2_STPB (1<<6) /* Stop */ +#define UCR2_WS (1<<5) /* Word size */ +#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */ +#define UCR2_TXEN (1<<2) /* Transmitter enabled */ +#define UCR2_RXEN (1<<1) /* Receiver enabled */ +#define UCR2_SRST (1<<0) /* SW reset */ +#define UCR3_DTREN (1<<13) /* DTR interrupt enable */ +#define UCR3_PARERREN (1<<12) /* Parity enable */ +#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */ +#define UCR3_DSR (1<<10) /* Data set ready */ +#define UCR3_DCD (1<<9) /* Data carrier detect */ +#define UCR3_RI (1<<8) /* Ring indicator */ +#define UCR3_TIMEOUTEN (1<<7) /* Timeout interrupt enable */ +#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */ +#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */ +#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */ +#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz */ +#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz */ +#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ +#define UCR3_BPEN (1<<0) /* Preset registers enable */ +#define UCR4_CTSTL_32 (32<<10) /* CTS trigger level (32 chars) */ +#define UCR4_INVR (1<<9) /* Inverted infrared reception */ +#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ +#define UCR4_WKEN (1<<7) /* Wake interrupt enable */ +#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */ +#define UCR4_IRSC (1<<5) /* IR special case */ +#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */ +#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */ +#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */ +#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */ +#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ +#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */ +#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */ +#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */ +#define USR1_RTSS (1<<14) /* RTS pin status */ +#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */ +#define USR1_RTSD (1<<12) /* RTS delta */ +#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */ +#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ +#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ +#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ +#define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */ +#define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */ +#define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */ +#define USR2_ADET (1<<15) /* Auto baud rate detect complete */ +#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */ +#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */ +#define USR2_IDLE (1<<12) /* Idle condition */ +#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */ +#define USR2_WAKE (1<<7) /* Wake */ +#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */ +#define USR2_TXDC (1<<3) /* Transmitter complete */ +#define USR2_BRCD (1<<2) /* Break condition */ +#define USR2_ORE (1<<1) /* Overrun error */ +#define USR2_RDR (1<<0) /* Recv data ready */ +#define UTS_FRCPERR (1<<13) /* Force parity error */ +#define UTS_LOOP (1<<12) /* Loop tx and rx */ +#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */ +#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */ +#define UTS_TXFULL (1<<4) /* TxFIFO full */ +#define UTS_RXFULL (1<<3) /* RxFIFO full */ +#define UTS_SOFTRST (1<<0) /* Software reset */ + +DECLARE_GLOBAL_DATA_PTR; + +static void mxc_serial_setbrg(void) +{ + u32 clk = imx_get_uartclk(); + + if (!gd->baudrate) + gd->baudrate = CONFIG_BAUDRATE; + + __REG(UART_PHYS + UFCR) = 4 << 7; /* divide input clock by 2 */ + __REG(UART_PHYS + UBIR) = 0xf; + __REG(UART_PHYS + UBMR) = clk / (2 * gd->baudrate); + +} + +static int mxc_serial_getc(void) +{ + while (__REG(UART_PHYS + UTS) & UTS_RXEMPTY) + WATCHDOG_RESET(); + return (__REG(UART_PHYS + URXD) & URXD_RX_DATA); /* mask out status from upper word */ +} + +static void mxc_serial_putc(const char c) +{ + __REG(UART_PHYS + UTXD) = c; + + /* wait for transmitter to be ready */ + while (!(__REG(UART_PHYS + UTS) & UTS_TXEMPTY)) + WATCHDOG_RESET(); + + /* If \n, also do \r */ + if (c == '\n') + serial_putc ('\r'); +} + +/* + * Test whether a character is in the RX buffer + */ +static int mxc_serial_tstc(void) +{ + /* If receive fifo is empty, return false */ + if (__REG(UART_PHYS + UTS) & UTS_RXEMPTY) + return 0; + return 1; +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + * + */ +static int mxc_serial_init(void) +{ + __REG(UART_PHYS + UCR1) = 0x0; + __REG(UART_PHYS + UCR2) = 0x0; + + while (!(__REG(UART_PHYS + UCR2) & UCR2_SRST)); + + __REG(UART_PHYS + UCR3) = 0x0704; + __REG(UART_PHYS + UCR4) = 0x8000; + __REG(UART_PHYS + UESC) = 0x002b; + __REG(UART_PHYS + UTIM) = 0x0; + + __REG(UART_PHYS + UTS) = 0x0; + + serial_setbrg(); + + __REG(UART_PHYS + UCR2) = UCR2_WS | UCR2_IRTS | UCR2_RXEN | UCR2_TXEN | UCR2_SRST; + + __REG(UART_PHYS + UCR1) = UCR1_UARTEN; + + return 0; +} + +static struct serial_device mxc_serial_drv = { + .name = "mxc_serial", + .start = mxc_serial_init, + .stop = NULL, + .setbrg = mxc_serial_setbrg, + .putc = mxc_serial_putc, + .puts = default_serial_puts, + .getc = mxc_serial_getc, + .tstc = mxc_serial_tstc, +}; + +void mxc_serial_initialize(void) +{ + serial_register(&mxc_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &mxc_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_ns16550.c b/qemu/roms/u-boot/drivers/serial/serial_ns16550.c new file mode 100644 index 000000000..ba68d4694 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_ns16550.c @@ -0,0 +1,301 @@ +/* + * (C) Copyright 2000 + * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include +#ifdef CONFIG_NS87308 +#include +#endif + +#include + +#ifndef CONFIG_NS16550_MIN_FUNCTIONS + +DECLARE_GLOBAL_DATA_PTR; + +#if !defined(CONFIG_CONS_INDEX) +#elif (CONFIG_CONS_INDEX < 1) || (CONFIG_CONS_INDEX > 6) +#error "Invalid console index value." +#endif + +#if CONFIG_CONS_INDEX == 1 && !defined(CONFIG_SYS_NS16550_COM1) +#error "Console port 1 defined but not configured." +#elif CONFIG_CONS_INDEX == 2 && !defined(CONFIG_SYS_NS16550_COM2) +#error "Console port 2 defined but not configured." +#elif CONFIG_CONS_INDEX == 3 && !defined(CONFIG_SYS_NS16550_COM3) +#error "Console port 3 defined but not configured." +#elif CONFIG_CONS_INDEX == 4 && !defined(CONFIG_SYS_NS16550_COM4) +#error "Console port 4 defined but not configured." +#elif CONFIG_CONS_INDEX == 5 && !defined(CONFIG_SYS_NS16550_COM5) +#error "Console port 5 defined but not configured." +#elif CONFIG_CONS_INDEX == 6 && !defined(CONFIG_SYS_NS16550_COM6) +#error "Console port 6 defined but not configured." +#endif + +/* Note: The port number specified in the functions is 1 based. + * the array is 0 based. + */ +static NS16550_t serial_ports[6] = { +#ifdef CONFIG_SYS_NS16550_COM1 + (NS16550_t)CONFIG_SYS_NS16550_COM1, +#else + NULL, +#endif +#ifdef CONFIG_SYS_NS16550_COM2 + (NS16550_t)CONFIG_SYS_NS16550_COM2, +#else + NULL, +#endif +#ifdef CONFIG_SYS_NS16550_COM3 + (NS16550_t)CONFIG_SYS_NS16550_COM3, +#else + NULL, +#endif +#ifdef CONFIG_SYS_NS16550_COM4 + (NS16550_t)CONFIG_SYS_NS16550_COM4, +#else + NULL, +#endif +#ifdef CONFIG_SYS_NS16550_COM5 + (NS16550_t)CONFIG_SYS_NS16550_COM5, +#else + NULL, +#endif +#ifdef CONFIG_SYS_NS16550_COM6 + (NS16550_t)CONFIG_SYS_NS16550_COM6 +#else + NULL +#endif +}; + +#define PORT serial_ports[port-1] + +/* Multi serial device functions */ +#define DECLARE_ESERIAL_FUNCTIONS(port) \ + static int eserial##port##_init(void) \ + { \ + int clock_divisor; \ + clock_divisor = calc_divisor(serial_ports[port-1]); \ + NS16550_init(serial_ports[port-1], clock_divisor); \ + return 0 ; \ + } \ + static void eserial##port##_setbrg(void) \ + { \ + serial_setbrg_dev(port); \ + } \ + static int eserial##port##_getc(void) \ + { \ + return serial_getc_dev(port); \ + } \ + static int eserial##port##_tstc(void) \ + { \ + return serial_tstc_dev(port); \ + } \ + static void eserial##port##_putc(const char c) \ + { \ + serial_putc_dev(port, c); \ + } \ + static void eserial##port##_puts(const char *s) \ + { \ + serial_puts_dev(port, s); \ + } + +/* Serial device descriptor */ +#define INIT_ESERIAL_STRUCTURE(port, __name) { \ + .name = __name, \ + .start = eserial##port##_init, \ + .stop = NULL, \ + .setbrg = eserial##port##_setbrg, \ + .getc = eserial##port##_getc, \ + .tstc = eserial##port##_tstc, \ + .putc = eserial##port##_putc, \ + .puts = eserial##port##_puts, \ +} + +static int calc_divisor (NS16550_t port) +{ +#ifdef CONFIG_OMAP1510 + /* If can't cleanly clock 115200 set div to 1 */ + if ((CONFIG_SYS_NS16550_CLK == 12000000) && (gd->baudrate == 115200)) { + port->osc_12m_sel = OSC_12M_SEL; /* enable 6.5 * divisor */ + return (1); /* return 1 for base divisor */ + } + port->osc_12m_sel = 0; /* clear if previsouly set */ +#endif +#ifdef CONFIG_OMAP1610 + /* If can't cleanly clock 115200 set div to 1 */ + if ((CONFIG_SYS_NS16550_CLK == 48000000) && (gd->baudrate == 115200)) { + return (26); /* return 26 for base divisor */ + } +#endif + +#define MODE_X_DIV 16 + /* Compute divisor value. Normally, we should simply return: + * CONFIG_SYS_NS16550_CLK) / MODE_X_DIV / gd->baudrate + * but we need to round that value by adding 0.5. + * Rounding is especially important at high baud rates. + */ + return (CONFIG_SYS_NS16550_CLK + (gd->baudrate * (MODE_X_DIV / 2))) / + (MODE_X_DIV * gd->baudrate); +} + +void +_serial_putc(const char c,const int port) +{ + if (c == '\n') + NS16550_putc(PORT, '\r'); + + NS16550_putc(PORT, c); +} + +void +_serial_putc_raw(const char c,const int port) +{ + NS16550_putc(PORT, c); +} + +void +_serial_puts (const char *s,const int port) +{ + while (*s) { + _serial_putc (*s++,port); + } +} + + +int +_serial_getc(const int port) +{ + return NS16550_getc(PORT); +} + +int +_serial_tstc(const int port) +{ + return NS16550_tstc(PORT); +} + +void +_serial_setbrg (const int port) +{ + int clock_divisor; + + clock_divisor = calc_divisor(PORT); + NS16550_reinit(PORT, clock_divisor); +} + +static inline void +serial_putc_dev(unsigned int dev_index,const char c) +{ + _serial_putc(c,dev_index); +} + +static inline void +serial_putc_raw_dev(unsigned int dev_index,const char c) +{ + _serial_putc_raw(c,dev_index); +} + +static inline void +serial_puts_dev(unsigned int dev_index,const char *s) +{ + _serial_puts(s,dev_index); +} + +static inline int +serial_getc_dev(unsigned int dev_index) +{ + return _serial_getc(dev_index); +} + +static inline int +serial_tstc_dev(unsigned int dev_index) +{ + return _serial_tstc(dev_index); +} + +static inline void +serial_setbrg_dev(unsigned int dev_index) +{ + _serial_setbrg(dev_index); +} + +#if defined(CONFIG_SYS_NS16550_COM1) +DECLARE_ESERIAL_FUNCTIONS(1); +struct serial_device eserial1_device = + INIT_ESERIAL_STRUCTURE(1, "eserial0"); +#endif +#if defined(CONFIG_SYS_NS16550_COM2) +DECLARE_ESERIAL_FUNCTIONS(2); +struct serial_device eserial2_device = + INIT_ESERIAL_STRUCTURE(2, "eserial1"); +#endif +#if defined(CONFIG_SYS_NS16550_COM3) +DECLARE_ESERIAL_FUNCTIONS(3); +struct serial_device eserial3_device = + INIT_ESERIAL_STRUCTURE(3, "eserial2"); +#endif +#if defined(CONFIG_SYS_NS16550_COM4) +DECLARE_ESERIAL_FUNCTIONS(4); +struct serial_device eserial4_device = + INIT_ESERIAL_STRUCTURE(4, "eserial3"); +#endif +#if defined(CONFIG_SYS_NS16550_COM5) +DECLARE_ESERIAL_FUNCTIONS(5); +struct serial_device eserial5_device = + INIT_ESERIAL_STRUCTURE(5, "eserial4"); +#endif +#if defined(CONFIG_SYS_NS16550_COM6) +DECLARE_ESERIAL_FUNCTIONS(6); +struct serial_device eserial6_device = + INIT_ESERIAL_STRUCTURE(6, "eserial5"); +#endif + +__weak struct serial_device *default_serial_console(void) +{ +#if CONFIG_CONS_INDEX == 1 + return &eserial1_device; +#elif CONFIG_CONS_INDEX == 2 + return &eserial2_device; +#elif CONFIG_CONS_INDEX == 3 + return &eserial3_device; +#elif CONFIG_CONS_INDEX == 4 + return &eserial4_device; +#elif CONFIG_CONS_INDEX == 5 + return &eserial5_device; +#elif CONFIG_CONS_INDEX == 6 + return &eserial6_device; +#else +#error "Bad CONFIG_CONS_INDEX." +#endif +} + +void ns16550_serial_initialize(void) +{ +#if defined(CONFIG_SYS_NS16550_COM1) + serial_register(&eserial1_device); +#endif +#if defined(CONFIG_SYS_NS16550_COM2) + serial_register(&eserial2_device); +#endif +#if defined(CONFIG_SYS_NS16550_COM3) + serial_register(&eserial3_device); +#endif +#if defined(CONFIG_SYS_NS16550_COM4) + serial_register(&eserial4_device); +#endif +#if defined(CONFIG_SYS_NS16550_COM5) + serial_register(&eserial5_device); +#endif +#if defined(CONFIG_SYS_NS16550_COM6) + serial_register(&eserial6_device); +#endif +} + +#endif /* !CONFIG_NS16550_MIN_FUNCTIONS */ diff --git a/qemu/roms/u-boot/drivers/serial/serial_pl01x.c b/qemu/roms/u-boot/drivers/serial/serial_pl01x.c new file mode 100644 index 000000000..dfb610e1a --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_pl01x.c @@ -0,0 +1,252 @@ +/* + * (C) Copyright 2000 + * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. + * + * (C) Copyright 2004 + * ARM Ltd. + * Philippe Robin, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */ + +#include +#include +#include +#include +#include +#include "serial_pl01x.h" + +/* + * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1 + * Integrator CP has two UARTs, use the first one, at 38400-8-N-1 + * Versatile PB has four UARTs. + */ +#define CONSOLE_PORT CONFIG_CONS_INDEX +static volatile unsigned char *const port[] = CONFIG_PL01x_PORTS; +#define NUM_PORTS (sizeof(port)/sizeof(port[0])) + +static void pl01x_putc (int portnum, char c); +static int pl01x_getc (int portnum); +static int pl01x_tstc (int portnum); +unsigned int baudrate = CONFIG_BAUDRATE; +DECLARE_GLOBAL_DATA_PTR; + +static struct pl01x_regs *pl01x_get_regs(int portnum) +{ + return (struct pl01x_regs *) port[portnum]; +} + +#ifdef CONFIG_PL010_SERIAL + +static int pl01x_serial_init(void) +{ + struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT); + unsigned int divisor; + + /* First, disable everything */ + writel(0, ®s->pl010_cr); + + /* Set baud rate */ + switch (baudrate) { + case 9600: + divisor = UART_PL010_BAUD_9600; + break; + + case 19200: + divisor = UART_PL010_BAUD_9600; + break; + + case 38400: + divisor = UART_PL010_BAUD_38400; + break; + + case 57600: + divisor = UART_PL010_BAUD_57600; + break; + + case 115200: + divisor = UART_PL010_BAUD_115200; + break; + + default: + divisor = UART_PL010_BAUD_38400; + } + + writel((divisor & 0xf00) >> 8, ®s->pl010_lcrm); + writel(divisor & 0xff, ®s->pl010_lcrl); + + /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */ + writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN, ®s->pl010_lcrh); + + /* Finally, enable the UART */ + writel(UART_PL010_CR_UARTEN, ®s->pl010_cr); + + return 0; +} + +#endif /* CONFIG_PL010_SERIAL */ + +#ifdef CONFIG_PL011_SERIAL + +static int pl01x_serial_init(void) +{ + struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT); + unsigned int temp; + unsigned int divider; + unsigned int remainder; + unsigned int fraction; + unsigned int lcr; + +#ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT + /* Empty RX fifo if necessary */ + if (readl(®s->pl011_cr) & UART_PL011_CR_UARTEN) { + while (!(readl(®s->fr) & UART_PL01x_FR_RXFE)) + readl(®s->dr); + } +#endif + + /* First, disable everything */ + writel(0, ®s->pl011_cr); + + /* + * Set baud rate + * + * IBRD = UART_CLK / (16 * BAUD_RATE) + * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) / (16 * BAUD_RATE)) + */ + temp = 16 * baudrate; + divider = CONFIG_PL011_CLOCK / temp; + remainder = CONFIG_PL011_CLOCK % temp; + temp = (8 * remainder) / baudrate; + fraction = (temp >> 1) + (temp & 1); + + writel(divider, ®s->pl011_ibrd); + writel(fraction, ®s->pl011_fbrd); + + /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */ + lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN; + writel(lcr, ®s->pl011_lcrh); + +#ifdef CONFIG_PL011_SERIAL_RLCR + { + int i; + + /* + * Program receive line control register after waiting + * 10 bus cycles. Delay be writing to readonly register + * 10 times + */ + for (i = 0; i < 10; i++) + writel(lcr, ®s->fr); + + writel(lcr, ®s->pl011_rlcr); + /* lcrh needs to be set again for change to be effective */ + writel(lcr, ®s->pl011_lcrh); + } +#endif + /* Finally, enable the UART */ + writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE | + UART_PL011_CR_RTS, ®s->pl011_cr); + + return 0; +} + +#endif /* CONFIG_PL011_SERIAL */ + +static void pl01x_serial_putc(const char c) +{ + if (c == '\n') + pl01x_putc (CONSOLE_PORT, '\r'); + + pl01x_putc (CONSOLE_PORT, c); +} + +static int pl01x_serial_getc(void) +{ + return pl01x_getc (CONSOLE_PORT); +} + +static int pl01x_serial_tstc(void) +{ + return pl01x_tstc (CONSOLE_PORT); +} + +static void pl01x_serial_setbrg(void) +{ + struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT); + + baudrate = gd->baudrate; + /* + * Flush FIFO and wait for non-busy before changing baudrate to avoid + * crap in console + */ + while (!(readl(®s->fr) & UART_PL01x_FR_TXFE)) + WATCHDOG_RESET(); + while (readl(®s->fr) & UART_PL01x_FR_BUSY) + WATCHDOG_RESET(); + serial_init(); +} + +static void pl01x_putc (int portnum, char c) +{ + struct pl01x_regs *regs = pl01x_get_regs(portnum); + + /* Wait until there is space in the FIFO */ + while (readl(®s->fr) & UART_PL01x_FR_TXFF) + WATCHDOG_RESET(); + + /* Send the character */ + writel(c, ®s->dr); +} + +static int pl01x_getc (int portnum) +{ + struct pl01x_regs *regs = pl01x_get_regs(portnum); + unsigned int data; + + /* Wait until there is data in the FIFO */ + while (readl(®s->fr) & UART_PL01x_FR_RXFE) + WATCHDOG_RESET(); + + data = readl(®s->dr); + + /* Check for an error flag */ + if (data & 0xFFFFFF00) { + /* Clear the error */ + writel(0xFFFFFFFF, ®s->ecr); + return -1; + } + + return (int) data; +} + +static int pl01x_tstc (int portnum) +{ + struct pl01x_regs *regs = pl01x_get_regs(portnum); + + WATCHDOG_RESET(); + return !(readl(®s->fr) & UART_PL01x_FR_RXFE); +} + +static struct serial_device pl01x_serial_drv = { + .name = "pl01x_serial", + .start = pl01x_serial_init, + .stop = NULL, + .setbrg = pl01x_serial_setbrg, + .putc = pl01x_serial_putc, + .puts = default_serial_puts, + .getc = pl01x_serial_getc, + .tstc = pl01x_serial_tstc, +}; + +void pl01x_serial_initialize(void) +{ + serial_register(&pl01x_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &pl01x_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_pl01x.h b/qemu/roms/u-boot/drivers/serial/serial_pl01x.h new file mode 100644 index 000000000..288a4f19f --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_pl01x.h @@ -0,0 +1,128 @@ +/* + * (C) Copyright 2003, 2004 + * ARM Ltd. + * Philippe Robin, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * ARM PrimeCell UART's (PL010 & PL011) + * ------------------------------------ + * + * Definitions common to both PL010 & PL011 + * + */ + +#ifndef __ASSEMBLY__ +/* + * We can use a combined structure for PL010 and PL011, because they overlap + * only in common registers. + */ +struct pl01x_regs { + u32 dr; /* 0x00 Data register */ + u32 ecr; /* 0x04 Error clear register (Write) */ + u32 pl010_lcrh; /* 0x08 Line control register, high byte */ + u32 pl010_lcrm; /* 0x0C Line control register, middle byte */ + u32 pl010_lcrl; /* 0x10 Line control register, low byte */ + u32 pl010_cr; /* 0x14 Control register */ + u32 fr; /* 0x18 Flag register (Read only) */ +#ifdef CONFIG_PL011_SERIAL_RLCR + u32 pl011_rlcr; /* 0x1c Receive line control register */ +#else + u32 reserved; +#endif + u32 ilpr; /* 0x20 IrDA low-power counter register */ + u32 pl011_ibrd; /* 0x24 Integer baud rate register */ + u32 pl011_fbrd; /* 0x28 Fractional baud rate register */ + u32 pl011_lcrh; /* 0x2C Line control register */ + u32 pl011_cr; /* 0x30 Control register */ +}; +#endif + +#define UART_PL01x_RSR_OE 0x08 +#define UART_PL01x_RSR_BE 0x04 +#define UART_PL01x_RSR_PE 0x02 +#define UART_PL01x_RSR_FE 0x01 + +#define UART_PL01x_FR_TXFE 0x80 +#define UART_PL01x_FR_RXFF 0x40 +#define UART_PL01x_FR_TXFF 0x20 +#define UART_PL01x_FR_RXFE 0x10 +#define UART_PL01x_FR_BUSY 0x08 +#define UART_PL01x_FR_TMSK (UART_PL01x_FR_TXFF + UART_PL01x_FR_BUSY) + +/* + * PL010 definitions + * + */ +#define UART_PL010_CR_LPE (1 << 7) +#define UART_PL010_CR_RTIE (1 << 6) +#define UART_PL010_CR_TIE (1 << 5) +#define UART_PL010_CR_RIE (1 << 4) +#define UART_PL010_CR_MSIE (1 << 3) +#define UART_PL010_CR_IIRLP (1 << 2) +#define UART_PL010_CR_SIREN (1 << 1) +#define UART_PL010_CR_UARTEN (1 << 0) + +#define UART_PL010_LCRH_WLEN_8 (3 << 5) +#define UART_PL010_LCRH_WLEN_7 (2 << 5) +#define UART_PL010_LCRH_WLEN_6 (1 << 5) +#define UART_PL010_LCRH_WLEN_5 (0 << 5) +#define UART_PL010_LCRH_FEN (1 << 4) +#define UART_PL010_LCRH_STP2 (1 << 3) +#define UART_PL010_LCRH_EPS (1 << 2) +#define UART_PL010_LCRH_PEN (1 << 1) +#define UART_PL010_LCRH_BRK (1 << 0) + + +#define UART_PL010_BAUD_460800 1 +#define UART_PL010_BAUD_230400 3 +#define UART_PL010_BAUD_115200 7 +#define UART_PL010_BAUD_57600 15 +#define UART_PL010_BAUD_38400 23 +#define UART_PL010_BAUD_19200 47 +#define UART_PL010_BAUD_14400 63 +#define UART_PL010_BAUD_9600 95 +#define UART_PL010_BAUD_4800 191 +#define UART_PL010_BAUD_2400 383 +#define UART_PL010_BAUD_1200 767 +/* + * PL011 definitions + * + */ +#define UART_PL011_LCRH_SPS (1 << 7) +#define UART_PL011_LCRH_WLEN_8 (3 << 5) +#define UART_PL011_LCRH_WLEN_7 (2 << 5) +#define UART_PL011_LCRH_WLEN_6 (1 << 5) +#define UART_PL011_LCRH_WLEN_5 (0 << 5) +#define UART_PL011_LCRH_FEN (1 << 4) +#define UART_PL011_LCRH_STP2 (1 << 3) +#define UART_PL011_LCRH_EPS (1 << 2) +#define UART_PL011_LCRH_PEN (1 << 1) +#define UART_PL011_LCRH_BRK (1 << 0) + +#define UART_PL011_CR_CTSEN (1 << 15) +#define UART_PL011_CR_RTSEN (1 << 14) +#define UART_PL011_CR_OUT2 (1 << 13) +#define UART_PL011_CR_OUT1 (1 << 12) +#define UART_PL011_CR_RTS (1 << 11) +#define UART_PL011_CR_DTR (1 << 10) +#define UART_PL011_CR_RXE (1 << 9) +#define UART_PL011_CR_TXE (1 << 8) +#define UART_PL011_CR_LPE (1 << 7) +#define UART_PL011_CR_IIRLP (1 << 2) +#define UART_PL011_CR_SIREN (1 << 1) +#define UART_PL011_CR_UARTEN (1 << 0) + +#define UART_PL011_IMSC_OEIM (1 << 10) +#define UART_PL011_IMSC_BEIM (1 << 9) +#define UART_PL011_IMSC_PEIM (1 << 8) +#define UART_PL011_IMSC_FEIM (1 << 7) +#define UART_PL011_IMSC_RTIM (1 << 6) +#define UART_PL011_IMSC_TXIM (1 << 5) +#define UART_PL011_IMSC_RXIM (1 << 4) +#define UART_PL011_IMSC_DSRMIM (1 << 3) +#define UART_PL011_IMSC_DCDMIM (1 << 2) +#define UART_PL011_IMSC_CTSMIM (1 << 1) +#define UART_PL011_IMSC_RIMIM (1 << 0) diff --git a/qemu/roms/u-boot/drivers/serial/serial_pxa.c b/qemu/roms/u-boot/drivers/serial/serial_pxa.c new file mode 100644 index 000000000..d5140045b --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_pxa.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2011 Marek Vasut + * + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Alex Zuepke + * + * Copyright (C) 1999 2000 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* + * The numbering scheme differs here for PXA25x, PXA27x and PXA3xx so we can + * easily handle enabling of clock. + */ +#ifdef CONFIG_CPU_MONAHANS +#define UART_CLK_BASE CKENA_21_BTUART +#define UART_CLK_REG CKENA +#define BTUART_INDEX 0 +#define FFUART_INDEX 1 +#define STUART_INDEX 2 +#elif CONFIG_CPU_PXA25X +#define UART_CLK_BASE (1 << 4) /* HWUART */ +#define UART_CLK_REG CKEN +#define HWUART_INDEX 0 +#define STUART_INDEX 1 +#define FFUART_INDEX 2 +#define BTUART_INDEX 3 +#else /* PXA27x */ +#define UART_CLK_BASE CKEN5_STUART +#define UART_CLK_REG CKEN +#define STUART_INDEX 0 +#define FFUART_INDEX 1 +#define BTUART_INDEX 2 +#endif + +/* + * Only PXA250 has HWUART, to avoid poluting the code with more macros, + * artificially introduce this. + */ +#ifndef CONFIG_CPU_PXA25X +#define HWUART_INDEX 0xff +#endif + +static uint32_t pxa_uart_get_baud_divider(void) +{ + if (gd->baudrate == 1200) + return 768; + else if (gd->baudrate == 9600) + return 96; + else if (gd->baudrate == 19200) + return 48; + else if (gd->baudrate == 38400) + return 24; + else if (gd->baudrate == 57600) + return 16; + else if (gd->baudrate == 115200) + return 8; + else /* Unsupported baudrate */ + return 0; +} + +static struct pxa_uart_regs *pxa_uart_index_to_regs(uint32_t uart_index) +{ + switch (uart_index) { + case FFUART_INDEX: return (struct pxa_uart_regs *)FFUART_BASE; + case BTUART_INDEX: return (struct pxa_uart_regs *)BTUART_BASE; + case STUART_INDEX: return (struct pxa_uart_regs *)STUART_BASE; + case HWUART_INDEX: return (struct pxa_uart_regs *)HWUART_BASE; + default: + return NULL; + } +} + +static void pxa_uart_toggle_clock(uint32_t uart_index, int enable) +{ + uint32_t clk_reg, clk_offset, reg; + + clk_reg = UART_CLK_REG; + clk_offset = UART_CLK_BASE << uart_index; + + reg = readl(clk_reg); + + if (enable) + reg |= clk_offset; + else + reg &= ~clk_offset; + + writel(reg, clk_reg); +} + +/* + * Enable clock and set baud rate, parity etc. + */ +void pxa_setbrg_dev(uint32_t uart_index) +{ + uint32_t divider = 0; + struct pxa_uart_regs *uart_regs; + + divider = pxa_uart_get_baud_divider(); + if (!divider) + hang(); + + uart_regs = pxa_uart_index_to_regs(uart_index); + if (!uart_regs) + hang(); + + pxa_uart_toggle_clock(uart_index, 1); + + /* Disable interrupts and FIFOs */ + writel(0, &uart_regs->ier); + writel(0, &uart_regs->fcr); + + /* Set baud rate */ + writel(LCR_WLS0 | LCR_WLS1 | LCR_DLAB, &uart_regs->lcr); + writel(divider & 0xff, &uart_regs->dll); + writel(divider >> 8, &uart_regs->dlh); + writel(LCR_WLS0 | LCR_WLS1, &uart_regs->lcr); + + /* Enable UART */ + writel(IER_UUE, &uart_regs->ier); +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +int pxa_init_dev(unsigned int uart_index) +{ + pxa_setbrg_dev (uart_index); + return 0; +} + +/* + * Output a single byte to the serial port. + */ +void pxa_putc_dev(unsigned int uart_index, const char c) +{ + struct pxa_uart_regs *uart_regs; + + uart_regs = pxa_uart_index_to_regs(uart_index); + if (!uart_regs) + hang(); + + while (!(readl(&uart_regs->lsr) & LSR_TEMT)) + WATCHDOG_RESET(); + writel(c, &uart_regs->thr); + + /* If \n, also do \r */ + if (c == '\n') + pxa_putc_dev (uart_index,'\r'); +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int pxa_tstc_dev(unsigned int uart_index) +{ + struct pxa_uart_regs *uart_regs; + + uart_regs = pxa_uart_index_to_regs(uart_index); + if (!uart_regs) + return -1; + + return readl(&uart_regs->lsr) & LSR_DR; +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int pxa_getc_dev(unsigned int uart_index) +{ + struct pxa_uart_regs *uart_regs; + + uart_regs = pxa_uart_index_to_regs(uart_index); + if (!uart_regs) + return -1; + + while (!(readl(&uart_regs->lsr) & LSR_DR)) + WATCHDOG_RESET(); + return readl(&uart_regs->rbr) & 0xff; +} + +void pxa_puts_dev(unsigned int uart_index, const char *s) +{ + while (*s) + pxa_putc_dev(uart_index, *s++); +} + +#define pxa_uart(uart, UART) \ + int uart##_init(void) \ + { \ + return pxa_init_dev(UART##_INDEX); \ + } \ + \ + void uart##_setbrg(void) \ + { \ + return pxa_setbrg_dev(UART##_INDEX); \ + } \ + \ + void uart##_putc(const char c) \ + { \ + return pxa_putc_dev(UART##_INDEX, c); \ + } \ + \ + void uart##_puts(const char *s) \ + { \ + return pxa_puts_dev(UART##_INDEX, s); \ + } \ + \ + int uart##_getc(void) \ + { \ + return pxa_getc_dev(UART##_INDEX); \ + } \ + \ + int uart##_tstc(void) \ + { \ + return pxa_tstc_dev(UART##_INDEX); \ + } \ + +#define pxa_uart_desc(uart) \ + struct serial_device serial_##uart##_device = \ + { \ + .name = "serial_"#uart, \ + .start = uart##_init, \ + .stop = NULL, \ + .setbrg = uart##_setbrg, \ + .getc = uart##_getc, \ + .tstc = uart##_tstc, \ + .putc = uart##_putc, \ + .puts = uart##_puts, \ + }; + +#define pxa_uart_multi(uart, UART) \ + pxa_uart(uart, UART) \ + pxa_uart_desc(uart) + +#if defined(CONFIG_HWUART) + pxa_uart_multi(hwuart, HWUART) +#endif +#if defined(CONFIG_STUART) + pxa_uart_multi(stuart, STUART) +#endif +#if defined(CONFIG_FFUART) + pxa_uart_multi(ffuart, FFUART) +#endif +#if defined(CONFIG_BTUART) + pxa_uart_multi(btuart, BTUART) +#endif + +__weak struct serial_device *default_serial_console(void) +{ +#if CONFIG_CONS_INDEX == 1 + return &serial_hwuart_device; +#elif CONFIG_CONS_INDEX == 2 + return &serial_stuart_device; +#elif CONFIG_CONS_INDEX == 3 + return &serial_ffuart_device; +#elif CONFIG_CONS_INDEX == 4 + return &serial_btuart_device; +#else +#error "Bad CONFIG_CONS_INDEX." +#endif +} + +void pxa_serial_initialize(void) +{ +#if defined(CONFIG_FFUART) + serial_register(&serial_ffuart_device); +#endif +#if defined(CONFIG_BTUART) + serial_register(&serial_btuart_device); +#endif +#if defined(CONFIG_STUART) + serial_register(&serial_stuart_device); +#endif +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_s3c24x0.c b/qemu/roms/u-boot/drivers/serial/serial_s3c24x0.c new file mode 100644 index 000000000..c07f4c9b4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_s3c24x0.c @@ -0,0 +1,267 @@ +/* + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_SERIAL1 +#define UART_NR S3C24X0_UART0 + +#elif defined(CONFIG_SERIAL2) +#define UART_NR S3C24X0_UART1 + +#elif defined(CONFIG_SERIAL3) +#define UART_NR S3C24X0_UART2 + +#else +#error "Bad: you didn't configure serial ..." +#endif + +#include +#include + +/* Multi serial device functions */ +#define DECLARE_S3C_SERIAL_FUNCTIONS(port) \ + int s3serial##port##_init(void) \ + { \ + return serial_init_dev(port); \ + } \ + void s3serial##port##_setbrg(void) \ + { \ + serial_setbrg_dev(port); \ + } \ + int s3serial##port##_getc(void) \ + { \ + return serial_getc_dev(port); \ + } \ + int s3serial##port##_tstc(void) \ + { \ + return serial_tstc_dev(port); \ + } \ + void s3serial##port##_putc(const char c) \ + { \ + serial_putc_dev(port, c); \ + } \ + void s3serial##port##_puts(const char *s) \ + { \ + serial_puts_dev(port, s); \ + } + +#define INIT_S3C_SERIAL_STRUCTURE(port, __name) { \ + .name = __name, \ + .start = s3serial##port##_init, \ + .stop = NULL, \ + .setbrg = s3serial##port##_setbrg, \ + .getc = s3serial##port##_getc, \ + .tstc = s3serial##port##_tstc, \ + .putc = s3serial##port##_putc, \ + .puts = s3serial##port##_puts, \ +} + +#ifdef CONFIG_HWFLOW +static int hwflow; +#endif + +void _serial_setbrg(const int dev_index) +{ + struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index); + unsigned int reg = 0; + int i; + + /* value is calculated so : (int)(PCLK/16./baudrate) -1 */ + reg = get_PCLK() / (16 * gd->baudrate) - 1; + + writel(reg, &uart->ubrdiv); + for (i = 0; i < 100; i++) + /* Delay */ ; +} + +static inline void serial_setbrg_dev(unsigned int dev_index) +{ + _serial_setbrg(dev_index); +} + +/* Initialise the serial port. The settings are always 8 data bits, no parity, + * 1 stop bit, no start bits. + */ +static int serial_init_dev(const int dev_index) +{ + struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index); + +#ifdef CONFIG_HWFLOW + hwflow = 0; /* turned off by default */ +#endif + + /* FIFO enable, Tx/Rx FIFO clear */ + writel(0x07, &uart->ufcon); + writel(0x0, &uart->umcon); + + /* Normal,No parity,1 stop,8 bit */ + writel(0x3, &uart->ulcon); + /* + * tx=level,rx=edge,disable timeout int.,enable rx error int., + * normal,interrupt or polling + */ + writel(0x245, &uart->ucon); + +#ifdef CONFIG_HWFLOW + writel(0x1, &uart->umcon); /* rts up */ +#endif + + /* FIXME: This is sooooooooooooooooooo ugly */ +#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2) + /* we need auto hw flow control on the gsm and gps port */ + if (dev_index == 0 || dev_index == 1) + writel(0x10, &uart->umcon); +#endif + _serial_setbrg(dev_index); + + return (0); +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int _serial_getc(const int dev_index) +{ + struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index); + + while (!(readl(&uart->utrstat) & 0x1)) + /* wait for character to arrive */ ; + + return readb(&uart->urxh) & 0xff; +} + +static inline int serial_getc_dev(unsigned int dev_index) +{ + return _serial_getc(dev_index); +} + +#ifdef CONFIG_HWFLOW +int hwflow_onoff(int on) +{ + switch (on) { + case 0: + default: + break; /* return current */ + case 1: + hwflow = 1; /* turn on */ + break; + case -1: + hwflow = 0; /* turn off */ + break; + } + return hwflow; +} +#endif + +#ifdef CONFIG_MODEM_SUPPORT +static int be_quiet = 0; +void disable_putc(void) +{ + be_quiet = 1; +} + +void enable_putc(void) +{ + be_quiet = 0; +} +#endif + + +/* + * Output a single byte to the serial port. + */ +void _serial_putc(const char c, const int dev_index) +{ + struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index); +#ifdef CONFIG_MODEM_SUPPORT + if (be_quiet) + return; +#endif + + while (!(readl(&uart->utrstat) & 0x2)) + /* wait for room in the tx FIFO */ ; + +#ifdef CONFIG_HWFLOW + while (hwflow && !(readl(&uart->umstat) & 0x1)) + /* Wait for CTS up */ ; +#endif + + writeb(c, &uart->utxh); + + /* If \n, also do \r */ + if (c == '\n') + serial_putc('\r'); +} + +static inline void serial_putc_dev(unsigned int dev_index, const char c) +{ + _serial_putc(c, dev_index); +} + +/* + * Test whether a character is in the RX buffer + */ +int _serial_tstc(const int dev_index) +{ + struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index); + + return readl(&uart->utrstat) & 0x1; +} + +static inline int serial_tstc_dev(unsigned int dev_index) +{ + return _serial_tstc(dev_index); +} + +void _serial_puts(const char *s, const int dev_index) +{ + while (*s) { + _serial_putc(*s++, dev_index); + } +} + +static inline void serial_puts_dev(int dev_index, const char *s) +{ + _serial_puts(s, dev_index); +} + +DECLARE_S3C_SERIAL_FUNCTIONS(0); +struct serial_device s3c24xx_serial0_device = +INIT_S3C_SERIAL_STRUCTURE(0, "s3ser0"); +DECLARE_S3C_SERIAL_FUNCTIONS(1); +struct serial_device s3c24xx_serial1_device = +INIT_S3C_SERIAL_STRUCTURE(1, "s3ser1"); +DECLARE_S3C_SERIAL_FUNCTIONS(2); +struct serial_device s3c24xx_serial2_device = +INIT_S3C_SERIAL_STRUCTURE(2, "s3ser2"); + +__weak struct serial_device *default_serial_console(void) +{ +#if defined(CONFIG_SERIAL1) + return &s3c24xx_serial0_device; +#elif defined(CONFIG_SERIAL2) + return &s3c24xx_serial1_device; +#elif defined(CONFIG_SERIAL3) + return &s3c24xx_serial2_device; +#else +#error "CONFIG_SERIAL? missing." +#endif +} + +void s3c24xx_serial_initialize(void) +{ + serial_register(&s3c24xx_serial0_device); + serial_register(&s3c24xx_serial1_device); + serial_register(&s3c24xx_serial2_device); +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_s5p.c b/qemu/roms/u-boot/drivers/serial/serial_s5p.c new file mode 100644 index 000000000..98c62b4c1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_s5p.c @@ -0,0 +1,301 @@ +/* + * (C) Copyright 2009 SAMSUNG Electronics + * Minkyu Kang + * Heungjun Kim + * + * based on drivers/serial/s3c64xx.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define RX_FIFO_COUNT_MASK 0xff +#define RX_FIFO_FULL_MASK (1 << 8) +#define TX_FIFO_FULL_MASK (1 << 24) + +/* Information about a serial port */ +struct fdt_serial { + u32 base_addr; /* address of registers in physical memory */ + u8 port_id; /* uart port number */ + u8 enabled; /* 1 if enabled, 0 if disabled */ +} config __attribute__ ((section(".data"))); + +static inline struct s5p_uart *s5p_get_base_uart(int dev_index) +{ +#ifdef CONFIG_OF_CONTROL + return (struct s5p_uart *)(config.base_addr); +#else + u32 offset = dev_index * sizeof(struct s5p_uart); + return (struct s5p_uart *)(samsung_get_base_uart() + offset); +#endif +} + +/* + * The coefficient, used to calculate the baudrate on S5P UARTs is + * calculated as + * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT + * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1, + * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants: + */ +static const int udivslot[] = { + 0, + 0x0080, + 0x0808, + 0x0888, + 0x2222, + 0x4924, + 0x4a52, + 0x54aa, + 0x5555, + 0xd555, + 0xd5d5, + 0xddd5, + 0xdddd, + 0xdfdd, + 0xdfdf, + 0xffdf, +}; + +static void serial_setbrg_dev(const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + u32 uclk = get_uart_clk(dev_index); + u32 baudrate = gd->baudrate; + u32 val; + +#if defined(CONFIG_SILENT_CONSOLE) && \ + defined(CONFIG_OF_CONTROL) && \ + !defined(CONFIG_SPL_BUILD) + if (fdtdec_get_config_int(gd->fdt_blob, "silent_console", 0)) + gd->flags |= GD_FLG_SILENT; +#endif + + if (!config.enabled) + return; + + val = uclk / baudrate; + + writel(val / 16 - 1, &uart->ubrdiv); + + if (s5p_uart_divslot()) + writew(udivslot[val % 16], &uart->rest.slot); + else + writeb(val % 16, &uart->rest.value); +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +static int serial_init_dev(const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + + /* enable FIFOs, auto clear Rx FIFO */ + writel(0x3, &uart->ufcon); + writel(0, &uart->umcon); + /* 8N1 */ + writel(0x3, &uart->ulcon); + /* No interrupts, no DMA, pure polling */ + writel(0x245, &uart->ucon); + + serial_setbrg_dev(dev_index); + + return 0; +} + +static int serial_err_check(const int dev_index, int op) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + unsigned int mask; + + /* + * UERSTAT + * Break Detect [3] + * Frame Err [2] : receive operation + * Parity Err [1] : receive operation + * Overrun Err [0] : receive operation + */ + if (op) + mask = 0x8; + else + mask = 0xf; + + return readl(&uart->uerstat) & mask; +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +static int serial_getc_dev(const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + + if (!config.enabled) + return 0; + + /* wait for character to arrive */ + while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | + RX_FIFO_FULL_MASK))) { + if (serial_err_check(dev_index, 0)) + return 0; + } + + return (int)(readb(&uart->urxh) & 0xff); +} + +/* + * Output a single byte to the serial port. + */ +static void serial_putc_dev(const char c, const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + + if (!config.enabled) + return; + + /* wait for room in the tx FIFO */ + while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) { + if (serial_err_check(dev_index, 1)) + return; + } + + writeb(c, &uart->utxh); + + /* If \n, also do \r */ + if (c == '\n') + serial_putc('\r'); +} + +/* + * Test whether a character is in the RX buffer + */ +static int serial_tstc_dev(const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + + if (!config.enabled) + return 0; + + return (int)(readl(&uart->utrstat) & 0x1); +} + +static void serial_puts_dev(const char *s, const int dev_index) +{ + while (*s) + serial_putc_dev(*s++, dev_index); +} + +/* Multi serial device functions */ +#define DECLARE_S5P_SERIAL_FUNCTIONS(port) \ +static int s5p_serial##port##_init(void) { return serial_init_dev(port); } \ +static void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \ +static int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \ +static int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \ +static void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \ +static void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); } + +#define INIT_S5P_SERIAL_STRUCTURE(port, __name) { \ + .name = __name, \ + .start = s5p_serial##port##_init, \ + .stop = NULL, \ + .setbrg = s5p_serial##port##_setbrg, \ + .getc = s5p_serial##port##_getc, \ + .tstc = s5p_serial##port##_tstc, \ + .putc = s5p_serial##port##_putc, \ + .puts = s5p_serial##port##_puts, \ +} + +DECLARE_S5P_SERIAL_FUNCTIONS(0); +struct serial_device s5p_serial0_device = + INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0"); +DECLARE_S5P_SERIAL_FUNCTIONS(1); +struct serial_device s5p_serial1_device = + INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1"); +DECLARE_S5P_SERIAL_FUNCTIONS(2); +struct serial_device s5p_serial2_device = + INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2"); +DECLARE_S5P_SERIAL_FUNCTIONS(3); +struct serial_device s5p_serial3_device = + INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3"); + +#ifdef CONFIG_OF_CONTROL +int fdtdec_decode_console(int *index, struct fdt_serial *uart) +{ + const void *blob = gd->fdt_blob; + int node; + + node = fdt_path_offset(blob, "console"); + if (node < 0) + return node; + + uart->base_addr = fdtdec_get_addr(blob, node, "reg"); + if (uart->base_addr == FDT_ADDR_T_NONE) + return -FDT_ERR_NOTFOUND; + + uart->port_id = fdtdec_get_int(blob, node, "id", -1); + uart->enabled = fdtdec_get_is_enabled(blob, node); + + return 0; +} +#endif + +__weak struct serial_device *default_serial_console(void) +{ +#ifdef CONFIG_OF_CONTROL + int index = 0; + + if ((!config.base_addr) && (fdtdec_decode_console(&index, &config))) { + debug("Cannot decode default console node\n"); + return NULL; + } + + switch (config.port_id) { + case 0: + return &s5p_serial0_device; + case 1: + return &s5p_serial1_device; + case 2: + return &s5p_serial2_device; + case 3: + return &s5p_serial3_device; + default: + debug("Unknown config.port_id: %d", config.port_id); + break; + } + + return NULL; +#else + config.enabled = 1; +#if defined(CONFIG_SERIAL0) + return &s5p_serial0_device; +#elif defined(CONFIG_SERIAL1) + return &s5p_serial1_device; +#elif defined(CONFIG_SERIAL2) + return &s5p_serial2_device; +#elif defined(CONFIG_SERIAL3) + return &s5p_serial3_device; +#else +#error "CONFIG_SERIAL? missing." +#endif +#endif +} + +void s5p_serial_initialize(void) +{ + serial_register(&s5p_serial0_device); + serial_register(&s5p_serial1_device); + serial_register(&s5p_serial2_device); + serial_register(&s5p_serial3_device); +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_sa1100.c b/qemu/roms/u-boot/drivers/serial/serial_sa1100.c new file mode 100644 index 000000000..78f241d85 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_sa1100.c @@ -0,0 +1,162 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Alex Zuepke + * + * Copyright (C) 1999 2000 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static void sa1100_serial_setbrg(void) +{ + unsigned int reg = 0; + + if (gd->baudrate == 1200) + reg = 191; + else if (gd->baudrate == 9600) + reg = 23; + else if (gd->baudrate == 19200) + reg = 11; + else if (gd->baudrate == 38400) + reg = 5; + else if (gd->baudrate == 57600) + reg = 3; + else if (gd->baudrate == 115200) + reg = 1; + else + hang (); + +#ifdef CONFIG_SERIAL1 + /* SA1110 uart function */ + Ser1SDCR0 |= SDCR0_SUS; + + /* Wait until port is ready ... */ + while(Ser1UTSR1 & UTSR1_TBY) {} + + /* init serial serial 1 */ + Ser1UTCR3 = 0x00; + Ser1UTSR0 = 0xff; + Ser1UTCR0 = ( UTCR0_1StpBit | UTCR0_8BitData ); + Ser1UTCR1 = 0; + Ser1UTCR2 = (u32)reg; + Ser1UTCR3 = ( UTCR3_RXE | UTCR3_TXE ); +#elif defined(CONFIG_SERIAL3) + /* Wait until port is ready ... */ + while (Ser3UTSR1 & UTSR1_TBY) { + } + + /* init serial serial 3 */ + Ser3UTCR3 = 0x00; + Ser3UTSR0 = 0xff; + Ser3UTCR0 = (UTCR0_1StpBit | UTCR0_8BitData); + Ser3UTCR1 = 0; + Ser3UTCR2 = (u32) reg; + Ser3UTCR3 = (UTCR3_RXE | UTCR3_TXE); +#else +#error "Bad: you didn't configured serial ..." +#endif +} + + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + * + */ +static int sa1100_serial_init(void) +{ + serial_setbrg (); + + return (0); +} + + +/* + * Output a single byte to the serial port. + */ +static void sa1100_serial_putc(const char c) +{ +#ifdef CONFIG_SERIAL1 + /* wait for room in the tx FIFO on SERIAL1 */ + while ((Ser1UTSR0 & UTSR0_TFS) == 0); + + Ser1UTDR = c; +#elif defined(CONFIG_SERIAL3) + /* wait for room in the tx FIFO on SERIAL3 */ + while ((Ser3UTSR0 & UTSR0_TFS) == 0); + + Ser3UTDR = c; +#endif + + /* If \n, also do \r */ + if (c == '\n') + serial_putc ('\r'); +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +static int sa1100_serial_tstc(void) +{ +#ifdef CONFIG_SERIAL1 + return Ser1UTSR1 & UTSR1_RNE; +#elif defined(CONFIG_SERIAL3) + return Ser3UTSR1 & UTSR1_RNE; +#endif +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +static int sa1100_serial_getc(void) +{ +#ifdef CONFIG_SERIAL1 + while (!(Ser1UTSR1 & UTSR1_RNE)); + + return (char) Ser1UTDR & 0xff; +#elif defined(CONFIG_SERIAL3) + while (!(Ser3UTSR1 & UTSR1_RNE)); + + return (char) Ser3UTDR & 0xff; +#endif +} + +static struct serial_device sa1100_serial_drv = { + .name = "sa1100_serial", + .start = sa1100_serial_init, + .stop = NULL, + .setbrg = sa1100_serial_setbrg, + .putc = sa1100_serial_putc, + .puts = default_serial_puts, + .getc = sa1100_serial_getc, + .tstc = sa1100_serial_tstc, +}; + +void sa1100_serial_initialize(void) +{ + serial_register(&sa1100_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &sa1100_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_sh.c b/qemu/roms/u-boot/drivers/serial/serial_sh.c new file mode 100644 index 000000000..0826d59ab --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_sh.c @@ -0,0 +1,202 @@ +/* + * SuperH SCIF device driver. + * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2007,2008,2010 Nobuhiro Iwamatsu + * Copyright (C) 2002 - 2008 Paul Mundt + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "serial_sh.h" +#include +#include + +#if defined(CONFIG_CONS_SCIF0) +# define SCIF_BASE SCIF0_BASE +#elif defined(CONFIG_CONS_SCIF1) +# define SCIF_BASE SCIF1_BASE +#elif defined(CONFIG_CONS_SCIF2) +# define SCIF_BASE SCIF2_BASE +#elif defined(CONFIG_CONS_SCIF3) +# define SCIF_BASE SCIF3_BASE +#elif defined(CONFIG_CONS_SCIF4) +# define SCIF_BASE SCIF4_BASE +#elif defined(CONFIG_CONS_SCIF5) +# define SCIF_BASE SCIF5_BASE +#elif defined(CONFIG_CONS_SCIF6) +# define SCIF_BASE SCIF6_BASE +#elif defined(CONFIG_CONS_SCIF7) +# define SCIF_BASE SCIF7_BASE +#else +# error "Default SCIF doesn't set....." +#endif + +#if defined(CONFIG_SCIF_A) + #define SCIF_BASE_PORT PORT_SCIFA +#else + #define SCIF_BASE_PORT PORT_SCIF +#endif + +static struct uart_port sh_sci = { + .membase = (unsigned char*)SCIF_BASE, + .mapbase = SCIF_BASE, + .type = SCIF_BASE_PORT, +}; + +static void sh_serial_setbrg(void) +{ + DECLARE_GLOBAL_DATA_PTR; + + sci_out(&sh_sci, SCBRR, + SCBRR_VALUE(gd->baudrate, CONFIG_SH_SCIF_CLK_FREQ)); +} + +static int sh_serial_init(void) +{ + sci_out(&sh_sci, SCSCR , SCSCR_INIT(&sh_sci)); + sci_out(&sh_sci, SCSCR , SCSCR_INIT(&sh_sci)); + sci_out(&sh_sci, SCSMR, 0); + sci_out(&sh_sci, SCSMR, 0); + sci_out(&sh_sci, SCFCR, SCFCR_RFRST|SCFCR_TFRST); + sci_in(&sh_sci, SCFCR); + sci_out(&sh_sci, SCFCR, 0); + + serial_setbrg(); + return 0; +} + +#if defined(CONFIG_CPU_SH7760) || \ + defined(CONFIG_CPU_SH7780) || \ + defined(CONFIG_CPU_SH7785) || \ + defined(CONFIG_CPU_SH7786) +static int scif_rxfill(struct uart_port *port) +{ + return sci_in(port, SCRFDR) & 0xff; +} +#elif defined(CONFIG_CPU_SH7763) +static int scif_rxfill(struct uart_port *port) +{ + if ((port->mapbase == 0xffe00000) || + (port->mapbase == 0xffe08000)) { + /* SCIF0/1*/ + return sci_in(port, SCRFDR) & 0xff; + } else { + /* SCIF2 */ + return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; + } +} +#elif defined(CONFIG_ARCH_SH7372) +static int scif_rxfill(struct uart_port *port) +{ + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; + else + return sci_in(port, SCRFDR); +} +#else +static int scif_rxfill(struct uart_port *port) +{ + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; +} +#endif + +static int serial_rx_fifo_level(void) +{ + return scif_rxfill(&sh_sci); +} + +static void handle_error(void) +{ + sci_in(&sh_sci, SCxSR); + sci_out(&sh_sci, SCxSR, SCxSR_ERROR_CLEAR(&sh_sci)); + sci_in(&sh_sci, SCLSR); + sci_out(&sh_sci, SCLSR, 0x00); +} + +void serial_raw_putc(const char c) +{ + while (1) { + /* Tx fifo is empty */ + if (sci_in(&sh_sci, SCxSR) & SCxSR_TEND(&sh_sci)) + break; + } + + sci_out(&sh_sci, SCxTDR, c); + sci_out(&sh_sci, SCxSR, sci_in(&sh_sci, SCxSR) & ~SCxSR_TEND(&sh_sci)); +} + +static void sh_serial_putc(const char c) +{ + if (c == '\n') + serial_raw_putc('\r'); + serial_raw_putc(c); +} + +static int sh_serial_tstc(void) +{ + if (sci_in(&sh_sci, SCxSR) & SCIF_ERRORS) { + handle_error(); + return 0; + } + + return serial_rx_fifo_level() ? 1 : 0; +} + + +int serial_getc_check(void) +{ + unsigned short status; + + status = sci_in(&sh_sci, SCxSR); + + if (status & SCIF_ERRORS) + handle_error(); + if (sci_in(&sh_sci, SCLSR) & SCxSR_ORER(&sh_sci)) + handle_error(); + return status & (SCIF_DR | SCxSR_RDxF(&sh_sci)); +} + +static int sh_serial_getc(void) +{ + unsigned short status; + char ch; + + while (!serial_getc_check()) + ; + + ch = sci_in(&sh_sci, SCxRDR); + status = sci_in(&sh_sci, SCxSR); + + sci_out(&sh_sci, SCxSR, SCxSR_RDxF_CLEAR(&sh_sci)); + + if (status & SCIF_ERRORS) + handle_error(); + + if (sci_in(&sh_sci, SCLSR) & SCxSR_ORER(&sh_sci)) + handle_error(); + return ch; +} + +static struct serial_device sh_serial_drv = { + .name = "sh_serial", + .start = sh_serial_init, + .stop = NULL, + .setbrg = sh_serial_setbrg, + .putc = sh_serial_putc, + .puts = default_serial_puts, + .getc = sh_serial_getc, + .tstc = sh_serial_tstc, +}; + +void sh_serial_initialize(void) +{ + serial_register(&sh_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &sh_serial_drv; +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_sh.h b/qemu/roms/u-boot/drivers/serial/serial_sh.h new file mode 100644 index 000000000..f5e9854d1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_sh.h @@ -0,0 +1,740 @@ +/* + * Copy and modify from linux/drivers/serial/sh-sci.h + */ + +struct uart_port { + unsigned long iobase; /* in/out[bwl] */ + unsigned char *membase; /* read/write[bwl] */ + unsigned long mapbase; /* for ioremap */ + unsigned int type; /* port type */ +}; + +#define PORT_SCI 52 +#define PORT_SCIF 53 +#define PORT_SCIFA 83 +#define PORT_SCIFB 93 + +#if defined(CONFIG_H83007) || defined(CONFIG_H83068) +#include +#endif +#if defined(CONFIG_H8S2678) +#include +#endif + +#if defined(CONFIG_CPU_SH7706) || \ + defined(CONFIG_CPU_SH7707) || \ + defined(CONFIG_CPU_SH7708) || \ + defined(CONFIG_CPU_SH7709) +# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ +# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +#elif defined(CONFIG_CPU_SH7705) +# define SCIF0 0xA4400000 +# define SCIF2 0xA4410000 +# define SCSMR_Ir 0xA44A0000 +# define IRDA_SCIF SCIF0 +# define SCPCR 0xA4000116 +# define SCPDR 0xA4000136 + +/* Set the clock source, + * SCIF2 (0xA4410000) -> External clock, SCK pin used as clock input + * SCIF0 (0xA4400000) -> Internal clock, SCK pin as serial clock output + */ +# define SCSCR_INIT(port) (port->mapbase == SCIF2) ? 0xF3 : 0xF0 +#elif defined(CONFIG_CPU_SH7720) || \ + defined(CONFIG_CPU_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) || \ + defined(CONFIG_SH73A0) || \ + defined(CONFIG_R8A7740) +# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define PORT_PTCR 0xA405011EUL +# define PORT_PVCR 0xA4050122UL +# define SCIF_ORER 0x0200 /* overrun error bit */ +#elif defined(CONFIG_SH_RTS7751R2D) +# define SCSPTR1 0xFFE0001C /* 8 bit SCIF */ +# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH7750) || \ + defined(CONFIG_CPU_SH7750R) || \ + defined(CONFIG_CPU_SH7750S) || \ + defined(CONFIG_CPU_SH7091) || \ + defined(CONFIG_CPU_SH7751) || \ + defined(CONFIG_CPU_SH7751R) +# define SCSPTR1 0xffe0001c /* 8 bit SCI */ +# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) (((port)->type == PORT_SCI) ? \ + 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ + 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */) +#elif defined(CONFIG_CPU_SH7760) +# define SCSPTR0 0xfe600024 /* 16 bit SCIF */ +# define SCSPTR1 0xfe610024 /* 16 bit SCIF */ +# define SCSPTR2 0xfe620024 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH7710) || defined(CONFIG_CPU_SH7712) +# define SCSPTR0 0xA4400000 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define PACR 0xa4050100 +# define PBCR 0xa4050102 +# define SCSCR_INIT(port) 0x3B +#elif defined(CONFIG_CPU_SH7343) +# define SCSPTR0 0xffe00010 /* 16 bit SCIF */ +# define SCSPTR1 0xffe10010 /* 16 bit SCIF */ +# define SCSPTR2 0xffe20010 /* 16 bit SCIF */ +# define SCSPTR3 0xffe30010 /* 16 bit SCIF */ +# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */ +#elif defined(CONFIG_CPU_SH7722) +# define PADR 0xA4050120 +# undef PSDR +# define PSDR 0xA405013e +# define PWDR 0xA4050166 +# define PSCR 0xA405011E +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH7366) +# define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */ +# define SCSPTR0 SCPDR0 +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH7723) +# define SCSPTR0 0xa4050160 +# define SCSPTR1 0xa405013e +# define SCSPTR2 0xa4050160 +# define SCSPTR3 0xa405013e +# define SCSPTR4 0xa4050128 +# define SCSPTR5 0xa4050128 +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH7724) +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) ((port)->type == PORT_SCIFA ? \ + 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ + 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */) +#elif defined(CONFIG_CPU_SH7734) +# define SCSPTR0 0xFFE40020 +# define SCSPTR1 0xFFE41020 +# define SCSPTR2 0xFFE42020 +# define SCSPTR3 0xFFE43020 +# define SCSPTR4 0xFFE44020 +# define SCSPTR5 0xFFE45020 +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH4_202) +# define SCSPTR2 0xffe80020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH5_101) || defined(CONFIG_CPU_SH5_103) +# define SCIF_BASE_ADDR 0x01030000 +# define SCIF_ADDR_SH5 (PHYS_PERIPHERAL_BLOCK+SCIF_BASE_ADDR) +# define SCIF_PTR2_OFFS 0x0000020 +# define SCIF_LSR2_OFFS 0x0000024 +# define SCSPTR\ + ((port->mapbase)+SCIF_PTR2_OFFS) /* 16 bit SCIF */ +# define SCLSR2\ + ((port->mapbase)+SCIF_LSR2_OFFS) /* 16 bit SCIF */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0, TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_H83007) || defined(CONFIG_H83068) +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define H8300_SCI_DR(ch) (*(volatile char *)(P1DR + h8300_sci_pins[ch].port)) +#elif defined(CONFIG_H8S2678) +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define H8300_SCI_DR(ch) (*(volatile char *)(P1DR + h8300_sci_pins[ch].port)) +#elif defined(CONFIG_CPU_SH7757) || \ + defined(CONFIG_CPU_SH7752) || \ + defined(CONFIG_CPU_SH7753) +# define SCSPTR0 0xfe4b0020 +# define SCSPTR1 0xfe4b0020 +# define SCSPTR2 0xfe4b0020 +# define SCIF_ORER 0x0001 +# define SCSCR_INIT(port) 0x38 +# define SCIF_ONLY +#elif defined(CONFIG_CPU_SH7763) +# define SCSPTR0 0xffe00024 /* 16 bit SCIF */ +# define SCSPTR1 0xffe08024 /* 16 bit SCIF */ +# define SCSPTR2 0xffe10020 /* 16 bit SCIF/IRDA */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH7770) +# define SCSPTR0 0xff923020 /* 16 bit SCIF */ +# define SCSPTR1 0xff924020 /* 16 bit SCIF */ +# define SCSPTR2 0xff925020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x3c /* TIE=0,RIE=0,TE=1,RE=1,REIE=1,cke=2 */ +#elif defined(CONFIG_CPU_SH7780) +# define SCSPTR0 0xffe00024 /* 16 bit SCIF */ +# define SCSPTR1 0xffe10024 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* Overrun error bit */ + +#if defined(CONFIG_SH_SH2007) +/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=0 */ +# define SCSCR_INIT(port) 0x38 +#else +/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=1 */ +# define SCSCR_INIT(port) 0x3a +#endif + +#elif defined(CONFIG_CPU_SH7785) || \ + defined(CONFIG_CPU_SH7786) +# define SCSPTR0 0xffea0024 /* 16 bit SCIF */ +# define SCSPTR1 0xffeb0024 /* 16 bit SCIF */ +# define SCSPTR2 0xffec0024 /* 16 bit SCIF */ +# define SCSPTR3 0xffed0024 /* 16 bit SCIF */ +# define SCSPTR4 0xffee0024 /* 16 bit SCIF */ +# define SCSPTR5 0xffef0024 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* Overrun error bit */ +# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH7201) || \ + defined(CONFIG_CPU_SH7203) || \ + defined(CONFIG_CPU_SH7206) || \ + defined(CONFIG_CPU_SH7263) || \ + defined(CONFIG_CPU_SH7264) +# define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ +# define SCSPTR1 0xfffe8820 /* 16 bit SCIF */ +# define SCSPTR2 0xfffe9020 /* 16 bit SCIF */ +# define SCSPTR3 0xfffe9820 /* 16 bit SCIF */ +# if defined(CONFIG_CPU_SH7201) +# define SCSPTR4 0xfffeA020 /* 16 bit SCIF */ +# define SCSPTR5 0xfffeA820 /* 16 bit SCIF */ +# define SCSPTR6 0xfffeB020 /* 16 bit SCIF */ +# define SCSPTR7 0xfffeB820 /* 16 bit SCIF */ +# endif +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH7269) +# define SCSPTR0 0xe8007020 /* 16 bit SCIF */ +# define SCSPTR1 0xe8007820 /* 16 bit SCIF */ +# define SCSPTR2 0xe8008020 /* 16 bit SCIF */ +# define SCSPTR3 0xe8008820 /* 16 bit SCIF */ +# define SCSPTR4 0xe8009020 /* 16 bit SCIF */ +# define SCSPTR5 0xe8009820 /* 16 bit SCIF */ +# define SCSPTR6 0xe800a020 /* 16 bit SCIF */ +# define SCSPTR7 0xe800a820 /* 16 bit SCIF */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SH7619) +# define SCSPTR0 0xf8400020 /* 16 bit SCIF */ +# define SCSPTR1 0xf8410020 /* 16 bit SCIF */ +# define SCSPTR2 0xf8420020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SHX3) +# define SCSPTR0 0xffc30020 /* 16 bit SCIF */ +# define SCSPTR1 0xffc40020 /* 16 bit SCIF */ +# define SCSPTR2 0xffc50020 /* 16 bit SCIF */ +# define SCSPTR3 0xffc60020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* Overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +# define SCIF_ORER 0x0001 +# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0, */ +#else +# error CPU subtype not defined +#endif + +/* SCSCR */ +#define SCI_CTRL_FLAGS_TIE 0x80 /* all */ +#define SCI_CTRL_FLAGS_RIE 0x40 /* all */ +#define SCI_CTRL_FLAGS_TE 0x20 /* all */ +#define SCI_CTRL_FLAGS_RE 0x10 /* all */ +#if defined(CONFIG_CPU_SH7750) || \ + defined(CONFIG_CPU_SH7091) || \ + defined(CONFIG_CPU_SH7750R) || \ + defined(CONFIG_CPU_SH7722) || \ + defined(CONFIG_CPU_SH7734) || \ + defined(CONFIG_CPU_SH7750S) || \ + defined(CONFIG_CPU_SH7751) || \ + defined(CONFIG_CPU_SH7751R) || \ + defined(CONFIG_CPU_SH7763) || \ + defined(CONFIG_CPU_SH7780) || \ + defined(CONFIG_CPU_SH7785) || \ + defined(CONFIG_CPU_SH7786) || \ + defined(CONFIG_CPU_SHX3) +#define SCI_CTRL_FLAGS_REIE 0x08 /* 7750 SCIF */ +#elif defined(CONFIG_CPU_SH7724) +#define SCI_CTRL_FLAGS_REIE ((port)->type == PORT_SCIFA ? 0 : 8) +#else +#define SCI_CTRL_FLAGS_REIE 0 +#endif +/* SCI_CTRL_FLAGS_MPIE 0x08 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_CTRL_FLAGS_TEIE 0x04 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_CTRL_FLAGS_CKE1 0x02 * all */ +/* SCI_CTRL_FLAGS_CKE0 0x01 * 7707 SCI/SCIF, 7708 SCI, 7709 SCI/SCIF, 7750 SCI */ + +/* SCxSR SCI */ +#define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + +#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER) + +/* SCxSR SCIF */ +#define SCIF_ER 0x0080 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_TEND 0x0040 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_TDFE 0x0020 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_BRK 0x0010 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_FER 0x0008 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_PER 0x0004 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_RDF 0x0002 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_DR 0x0001 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + +#if defined(CONFIG_CPU_SH7705) || \ + defined(CONFIG_CPU_SH7720) || \ + defined(CONFIG_CPU_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) || \ + defined(CONFIG_SH73A0) || \ + defined(CONFIG_R8A7740) +# define SCIF_ORER 0x0200 +# define SCIF_ERRORS (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) +# define SCIF_RFDC_MASK 0x007f +# define SCIF_TXROOM_MAX 64 +#elif defined(CONFIG_CPU_SH7763) +# define SCIF_ERRORS (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) +# define SCIF_RFDC_MASK 0x007f +# define SCIF_TXROOM_MAX 64 +/* SH7763 SCIF2 support */ +# define SCIF2_RFDC_MASK 0x001f +# define SCIF2_TXROOM_MAX 16 +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +# define SCIF_ERRORS (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) +# define SCIF_RFDC_MASK 0x003f +#else +# define SCIF_ERRORS (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) +# define SCIF_RFDC_MASK 0x001f +# define SCIF_TXROOM_MAX 16 +#endif + +#ifndef SCIF_ORER +#define SCIF_ORER 0x0000 +#endif + +#define SCxSR_TEND(port)\ + (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) +#define SCxSR_ERRORS(port)\ + (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS) +#define SCxSR_RDxF(port)\ + (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) +#define SCxSR_TDxE(port)\ + (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) +#define SCxSR_FER(port)\ + (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER) +#define SCxSR_PER(port)\ + (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) +#define SCxSR_BRK(port)\ + ((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) +#define SCxSR_ORER(port)\ + (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER) + +#if defined(CONFIG_CPU_SH7705) || \ + defined(CONFIG_CPU_SH7720) || \ + defined(CONFIG_CPU_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) || \ + defined(CONFIG_SH73A0) || \ + defined(CONFIG_R8A7740) +# define SCxSR_RDxF_CLEAR(port) (sci_in(port, SCxSR) & 0xfffc) +# define SCxSR_ERROR_CLEAR(port) (sci_in(port, SCxSR) & 0xfd73) +# define SCxSR_TDxE_CLEAR(port) (sci_in(port, SCxSR) & 0xffdf) +# define SCxSR_BREAK_CLEAR(port) (sci_in(port, SCxSR) & 0xffe3) +#else +# define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc) +# define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073) +# define SCxSR_TDxE_CLEAR(port) (((port)->type == PORT_SCI) ? 0x78 : 0x00df) +# define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3) +#endif + +/* SCFCR */ +#define SCFCR_RFRST 0x0002 +#define SCFCR_TFRST 0x0004 +#define SCFCR_TCRST 0x4000 +#define SCFCR_MCE 0x0008 + +#define SCI_MAJOR 204 +#define SCI_MINOR_START 8 + +/* Generic serial flags */ +#define SCI_RX_THROTTLE 0x0000001 + +#define SCI_MAGIC 0xbabeface + +/* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ +#define SCI_EVENT_WRITE_WAKEUP 0 + +#define SCI_IN(size, offset)\ + if ((size) == 8) {\ + return readb(port->membase + (offset));\ + } else {\ + return readw(port->membase + (offset));\ + } +#define SCI_OUT(size, offset, value)\ + if ((size) == 8) {\ + writeb(value, port->membase + (offset));\ + } else if ((size) == 16) {\ + writew(value, port->membase + (offset));\ + } + +#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ + static inline unsigned int sci_##name##_in(struct uart_port *port) {\ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) {\ + SCI_IN(scif_size, scif_offset)\ + } else { /* PORT_SCI or PORT_SCIFA */\ + SCI_IN(sci_size, sci_offset);\ + }\ + }\ +static inline void sci_##name##_out(struct uart_port *port,\ + unsigned int value) {\ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) {\ + SCI_OUT(scif_size, scif_offset, value)\ + } else { /* PORT_SCI or PORT_SCIFA */\ + SCI_OUT(sci_size, sci_offset, value);\ + }\ +} + +#ifdef CONFIG_H8300 +/* h8300 don't have SCIF */ +#define CPU_SCIF_FNS(name) \ + static inline unsigned int sci_##name##_in(struct uart_port *port) {\ + return 0;\ + }\ + static inline void sci_##name##_out(struct uart_port *port,\ + unsigned int value) {\ + } +#else +#define CPU_SCIF_FNS(name, scif_offset, scif_size) \ + static inline unsigned int sci_##name##_in(struct uart_port *port) {\ + SCI_IN(scif_size, scif_offset);\ + }\ + static inline void sci_##name##_out(struct uart_port *port,\ + unsigned int value) {\ + SCI_OUT(scif_size, scif_offset, value);\ + } +#endif + +#define CPU_SCI_FNS(name, sci_offset, sci_size)\ + static inline unsigned int sci_##name##_in(struct uart_port *port) {\ + SCI_IN(sci_size, sci_offset);\ + }\ + static inline void sci_##name##_out(struct uart_port *port,\ + unsigned int value) {\ + SCI_OUT(sci_size, sci_offset, value);\ + } + +#if defined(CONFIG_SH3) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) || \ + defined(CONFIG_SH73A0) || \ + defined(CONFIG_R8A7740) +#if defined(CONFIG_CPU_SH7710) || defined(CONFIG_CPU_SH7712) +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size,\ + sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, \ + sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size,\ + sh4_scif_offset, sh4_scif_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size,\ + sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) +#elif defined(CONFIG_CPU_SH7705) || \ + defined(CONFIG_CPU_SH7720) || \ + defined(CONFIG_CPU_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_SH73A0) +#define SCIF_FNS(name, scif_offset, scif_size) \ + CPU_SCIF_FNS(name, scif_offset, scif_size) +#elif defined(CONFIG_ARCH_SH7372) || \ + defined(CONFIG_R8A7740) +#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size,\ + sh4_scifb_offset, sh4_scifb_size) \ + CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size,\ + sh4_scifb_offset, sh4_scifb_size) +#define SCIF_FNS(name, scif_offset, scif_size) \ + CPU_SCIF_FNS(name, scif_offset, scif_size) +#else +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size,\ + sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size,\ + sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size,\ + sh3_scif_offset, sh3_scif_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size,\ + sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) +#endif +#elif defined(__H8300H__) || defined(__H8300S__) +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size,\ + sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size,\ + sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size,\ + sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name) +#elif defined(CONFIG_CPU_SH7723) || defined(CONFIG_CPU_SH7724) + #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size,\ + sh4_scif_offset, sh4_scif_size) \ + CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size,\ + sh4_scif_offset, sh4_scif_size) + #define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) +#else +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size,\ + sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size,\ + sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size,\ + sh4_scif_offset, sh4_scif_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, \ + sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) +#endif + +#if defined(CONFIG_CPU_SH7705) || \ + defined(CONFIG_CPU_SH7720) || \ + defined(CONFIG_CPU_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_SH73A0) + +SCIF_FNS(SCSMR, 0x00, 16) +SCIF_FNS(SCBRR, 0x04, 8) +SCIF_FNS(SCSCR, 0x08, 16) +SCIF_FNS(SCTDSR, 0x0c, 8) +SCIF_FNS(SCFER, 0x10, 16) +SCIF_FNS(SCxSR, 0x14, 16) +SCIF_FNS(SCFCR, 0x18, 16) +SCIF_FNS(SCFDR, 0x1c, 16) +SCIF_FNS(SCxTDR, 0x20, 8) +SCIF_FNS(SCxRDR, 0x24, 8) +SCIF_FNS(SCLSR, 0x00, 0) +#elif defined(CONFIG_ARCH_SH7372) || \ + defined(CONFIG_R8A7740) +SCIF_FNS(SCSMR, 0x00, 16) +SCIF_FNS(SCBRR, 0x04, 8) +SCIF_FNS(SCSCR, 0x08, 16) +SCIF_FNS(SCTDSR, 0x0c, 16) +SCIF_FNS(SCFER, 0x10, 16) +SCIF_FNS(SCxSR, 0x14, 16) +SCIF_FNS(SCFCR, 0x18, 16) +SCIF_FNS(SCFDR, 0x1c, 16) +SCIF_FNS(SCTFDR, 0x38, 16) +SCIF_FNS(SCRFDR, 0x3c, 16) +SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8) +SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8) +SCIF_FNS(SCLSR, 0x00, 0) +#elif defined(CONFIG_CPU_SH7723) ||\ + defined(CONFIG_CPU_SH7724) +SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) +SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8) +SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16) +SCIx_FNS(SCxTDR, 0x20, 8, 0x0c, 8) +SCIx_FNS(SCxSR, 0x14, 16, 0x10, 16) +SCIx_FNS(SCxRDR, 0x24, 8, 0x14, 8) +SCIx_FNS(SCSPTR, 0, 0, 0, 0) +SCIF_FNS(SCTDSR, 0x0c, 8) +SCIF_FNS(SCFER, 0x10, 16) +SCIF_FNS(SCFCR, 0x18, 16) +SCIF_FNS(SCFDR, 0x1c, 16) +SCIF_FNS(SCLSR, 0x24, 16) +#else +/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/ +/* name off sz off sz off sz off sz off sz*/ +SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16, 0x00, 8) +SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8, 0x01, 8) +SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16, 0x02, 8) +SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8) +SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8) +SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8) +SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) +#if defined(CONFIG_CPU_SH7760) || \ + defined(CONFIG_CPU_SH7780) || \ + defined(CONFIG_CPU_SH7785) || \ + defined(CONFIG_CPU_SH7786) +SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) +SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) +SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) +SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) +SCIF_FNS(SCLSR, 0, 0, 0x28, 16) +#elif defined(CONFIG_CPU_SH7763) +SCIF_FNS(SCFDR, 0, 0, 0x1C, 16) +SCIF_FNS(SCSPTR2, 0, 0, 0x20, 16) +SCIF_FNS(SCLSR2, 0, 0, 0x24, 16) +SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) +SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) +SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) +SCIF_FNS(SCLSR, 0, 0, 0x28, 16) +#else +SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) +#if defined(CONFIG_CPU_SH7722) +SCIF_FNS(SCSPTR, 0, 0, 0, 0) +#else +SCIF_FNS(SCSPTR, 0, 0, 0x20, 16) +#endif +#if defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +SCIF_FNS(DL, 0, 0, 0x30, 16) +SCIF_FNS(CKS, 0, 0, 0x34, 16) +#endif +SCIF_FNS(SCLSR, 0, 0, 0x24, 16) +#endif +#endif +#define sci_in(port, reg) sci_##reg##_in(port) +#define sci_out(port, reg, value) sci_##reg##_out(port, value) + +/* H8/300 series SCI pins assignment */ +#if defined(__H8300H__) || defined(__H8300S__) +static const struct __attribute__((packed)) { + int port; /* GPIO port no */ + unsigned short rx, tx; /* GPIO bit no */ +} h8300_sci_pins[] = { +#if defined(CONFIG_H83007) || defined(CONFIG_H83068) + { /* SCI0 */ + .port = H8300_GPIO_P9, + .rx = H8300_GPIO_B2, + .tx = H8300_GPIO_B0, + }, + { /* SCI1 */ + .port = H8300_GPIO_P9, + .rx = H8300_GPIO_B3, + .tx = H8300_GPIO_B1, + }, + { /* SCI2 */ + .port = H8300_GPIO_PB, + .rx = H8300_GPIO_B7, + .tx = H8300_GPIO_B6, + } +#elif defined(CONFIG_H8S2678) + { /* SCI0 */ + .port = H8300_GPIO_P3, + .rx = H8300_GPIO_B2, + .tx = H8300_GPIO_B0, + }, + { /* SCI1 */ + .port = H8300_GPIO_P3, + .rx = H8300_GPIO_B3, + .tx = H8300_GPIO_B1, + }, + { /* SCI2 */ + .port = H8300_GPIO_P5, + .rx = H8300_GPIO_B1, + .tx = H8300_GPIO_B0, + } +#endif +}; +#endif + +#if defined(CONFIG_CPU_SH7706) || \ + defined(CONFIG_CPU_SH7707) || \ + defined(CONFIG_CPU_SH7708) || \ + defined(CONFIG_CPU_SH7709) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xfffffe80) + return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */ + return 1; +} +#elif defined(CONFIG_CPU_SH7750) || \ + defined(CONFIG_CPU_SH7751) || \ + defined(CONFIG_CPU_SH7751R) || \ + defined(CONFIG_CPU_SH7750R) || \ + defined(CONFIG_CPU_SH7750S) || \ + defined(CONFIG_CPU_SH7091) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xffe00000) + return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ + return 1; +} +#elif defined(__H8300H__) || defined(__H8300S__) +static inline int sci_rxd_in(struct uart_port *port) +{ + int ch = (port->mapbase - SMR0) >> 3; + return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0; +} +#else /* default case for non-SCI processors */ +static inline int sci_rxd_in(struct uart_port *port) +{ + return 1; +} +#endif + +/* + * Values for the BitRate Register (SCBRR) + * + * The values are actually divisors for a frequency which can + * be internal to the SH3 (14.7456MHz) or derived from an external + * clock source. This driver assumes the internal clock is used; + * to support using an external clock source, config options or + * possibly command-line options would need to be added. + * + * Also, to support speeds below 2400 (why?) the lower 2 bits of + * the SCSMR register would also need to be set to non-zero values. + * + * -- Greg Banks 27Feb2000 + * + * Answer: The SCBRR register is only eight bits, and the value in + * it gets larger with lower baud rates. At around 2400 (depending on + * the peripherial module clock) you run out of bits. However the + * lower two bits of SCSMR allow the module clock to be divided down, + * scaling the value which is needed in SCBRR. + * + * -- Stuart Menefy - 23 May 2000 + * + * I meant, why would anyone bother with bitrates below 2400. + * + * -- Greg Banks - 7Jul2000 + * + * You "speedist"! How will I use my 110bps ASR-33 teletype with paper + * tape reader as a console! + * + * -- Mitch Davis - 15 Jul 2000 + */ + +#if (defined(CONFIG_CPU_SH7780) || \ + defined(CONFIG_CPU_SH7785) || \ + defined(CONFIG_CPU_SH7786)) && \ + !defined(CONFIG_SH_SH2007) +#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1) +#elif defined(CONFIG_CPU_SH7705) || \ + defined(CONFIG_CPU_SH7720) || \ + defined(CONFIG_CPU_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) || \ + defined(CONFIG_SH73A0) || \ + defined(CONFIG_R8A7740) +#define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1) +#elif defined(CONFIG_CPU_SH7723) ||\ + defined(CONFIG_CPU_SH7724) +static inline int scbrr_calc(struct uart_port port, int bps, int clk) +{ + if (port.type == PORT_SCIF) + return (clk+16*bps)/(32*bps)-1; + else + return ((clk*2)+16*bps)/(16*bps)-1; +} +#define SCBRR_VALUE(bps, clk) scbrr_calc(sh_sci, bps, clk) +#elif defined(__H8300H__) || defined(__H8300S__) +#define SCBRR_VALUE(bps, clk) (((clk*1000/32)/bps)-1) +#elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +#define SCBRR DL +#define SCBRR_VALUE(bps, clk) (clk / bps / 16) +#else /* Generic SH */ +#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1) +#endif diff --git a/qemu/roms/u-boot/drivers/serial/serial_xuartlite.c b/qemu/roms/u-boot/drivers/serial/serial_xuartlite.c new file mode 100644 index 000000000..988438e75 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_xuartlite.c @@ -0,0 +1,162 @@ +/* + * (C) Copyright 2008-2011 Michal Simek + * Clean driver and add xilinx constant from header file + * + * (C) Copyright 2004 Atmark Techno, Inc. + * Yasushi SHOJI + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define SR_TX_FIFO_FULL 0x08 /* transmit FIFO full */ +#define SR_RX_FIFO_VALID_DATA 0x01 /* data in receive FIFO */ +#define SR_RX_FIFO_FULL 0x02 /* receive FIFO full */ + +#define ULITE_CONTROL_RST_TX 0x01 +#define ULITE_CONTROL_RST_RX 0x02 + +struct uartlite { + unsigned int rx_fifo; + unsigned int tx_fifo; + unsigned int status; + unsigned int control; +}; + +static struct uartlite *userial_ports[4] = { +#ifdef XILINX_UARTLITE_BASEADDR + [0] = (struct uartlite *)XILINX_UARTLITE_BASEADDR, +#endif +#ifdef XILINX_UARTLITE_BASEADDR1 + [1] = (struct uartlite *)XILINX_UARTLITE_BASEADDR1, +#endif +#ifdef XILINX_UARTLITE_BASEADDR2 + [2] = (struct uartlite *)XILINX_UARTLITE_BASEADDR2, +#endif +#ifdef XILINX_UARTLITE_BASEADDR3 + [3] = (struct uartlite *)XILINX_UARTLITE_BASEADDR3 +#endif +}; + +static void uartlite_serial_putc(const char c, const int port) +{ + struct uartlite *regs = userial_ports[port]; + + if (c == '\n') + uartlite_serial_putc('\r', port); + + while (in_be32(®s->status) & SR_TX_FIFO_FULL) + ; + out_be32(®s->tx_fifo, c & 0xff); +} + +static void uartlite_serial_puts(const char *s, const int port) +{ + while (*s) + uartlite_serial_putc(*s++, port); +} + +static int uartlite_serial_getc(const int port) +{ + struct uartlite *regs = userial_ports[port]; + + while (!(in_be32(®s->status) & SR_RX_FIFO_VALID_DATA)) + ; + return in_be32(®s->rx_fifo) & 0xff; +} + +static int uartlite_serial_tstc(const int port) +{ + struct uartlite *regs = userial_ports[port]; + + return in_be32(®s->status) & SR_RX_FIFO_VALID_DATA; +} + +static int uartlite_serial_init(const int port) +{ + struct uartlite *regs = userial_ports[port]; + + if (regs) { + out_be32(®s->control, 0); + out_be32(®s->control, + ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX); + in_be32(®s->control); + return 0; + } + + return -1; +} + +/* Multi serial device functions */ +#define DECLARE_ESERIAL_FUNCTIONS(port) \ + static int userial##port##_init(void) \ + { return uartlite_serial_init(port); } \ + static void userial##port##_setbrg(void) {} \ + static int userial##port##_getc(void) \ + { return uartlite_serial_getc(port); } \ + static int userial##port##_tstc(void) \ + { return uartlite_serial_tstc(port); } \ + static void userial##port##_putc(const char c) \ + { uartlite_serial_putc(c, port); } \ + static void userial##port##_puts(const char *s) \ + { uartlite_serial_puts(s, port); } + +/* Serial device descriptor */ +#define INIT_ESERIAL_STRUCTURE(port, __name) { \ + .name = __name, \ + .start = userial##port##_init, \ + .stop = NULL, \ + .setbrg = userial##port##_setbrg, \ + .getc = userial##port##_getc, \ + .tstc = userial##port##_tstc, \ + .putc = userial##port##_putc, \ + .puts = userial##port##_puts, \ +} + +DECLARE_ESERIAL_FUNCTIONS(0); +struct serial_device uartlite_serial0_device = + INIT_ESERIAL_STRUCTURE(0, "ttyUL0"); +DECLARE_ESERIAL_FUNCTIONS(1); +struct serial_device uartlite_serial1_device = + INIT_ESERIAL_STRUCTURE(1, "ttyUL1"); +DECLARE_ESERIAL_FUNCTIONS(2); +struct serial_device uartlite_serial2_device = + INIT_ESERIAL_STRUCTURE(2, "ttyUL2"); +DECLARE_ESERIAL_FUNCTIONS(3); +struct serial_device uartlite_serial3_device = + INIT_ESERIAL_STRUCTURE(3, "ttyUL3"); + +__weak struct serial_device *default_serial_console(void) +{ + if (userial_ports[0]) + return &uartlite_serial0_device; + if (userial_ports[1]) + return &uartlite_serial1_device; + if (userial_ports[2]) + return &uartlite_serial2_device; + if (userial_ports[3]) + return &uartlite_serial3_device; + + return NULL; +} + +void uartlite_serial_initialize(void) +{ +#ifdef XILINX_UARTLITE_BASEADDR + serial_register(&uartlite_serial0_device); +#endif /* XILINX_UARTLITE_BASEADDR */ +#ifdef XILINX_UARTLITE_BASEADDR1 + serial_register(&uartlite_serial1_device); +#endif /* XILINX_UARTLITE_BASEADDR1 */ +#ifdef XILINX_UARTLITE_BASEADDR2 + serial_register(&uartlite_serial2_device); +#endif /* XILINX_UARTLITE_BASEADDR2 */ +#ifdef XILINX_UARTLITE_BASEADDR3 + serial_register(&uartlite_serial3_device); +#endif /* XILINX_UARTLITE_BASEADDR3 */ +} diff --git a/qemu/roms/u-boot/drivers/serial/serial_zynq.c b/qemu/roms/u-boot/drivers/serial/serial_zynq.c new file mode 100644 index 000000000..1ff27d5f4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/serial_zynq.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2012 Michal Simek + * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define ZYNQ_UART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define ZYNQ_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ + +#define ZYNQ_UART_CR_TX_EN 0x00000010 /* TX enabled */ +#define ZYNQ_UART_CR_RX_EN 0x00000004 /* RX enabled */ +#define ZYNQ_UART_CR_TXRST 0x00000002 /* TX logic reset */ +#define ZYNQ_UART_CR_RXRST 0x00000001 /* RX logic reset */ + +#define ZYNQ_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */ + +struct uart_zynq { + u32 control; /* Control Register [8:0] */ + u32 mode; /* Mode Register [10:0] */ + u32 reserved1[4]; + u32 baud_rate_gen; /* Baud Rate Generator [15:0] */ + u32 reserved2[4]; + u32 channel_sts; /* Channel Status [11:0] */ + u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */ + u32 baud_rate_divider; /* Baud Rate Divider [7:0] */ +}; + +static struct uart_zynq *uart_zynq_ports[2] = { + [0] = (struct uart_zynq *)ZYNQ_SERIAL_BASEADDR0, + [1] = (struct uart_zynq *)ZYNQ_SERIAL_BASEADDR1, +}; + +#if !defined(CONFIG_ZYNQ_SERIAL_BAUDRATE0) +# define CONFIG_ZYNQ_SERIAL_BAUDRATE0 CONFIG_BAUDRATE +#endif +#if !defined(CONFIG_ZYNQ_SERIAL_BAUDRATE1) +# define CONFIG_ZYNQ_SERIAL_BAUDRATE1 CONFIG_BAUDRATE +#endif + +struct uart_zynq_params { + u32 baudrate; +}; + +static struct uart_zynq_params uart_zynq_ports_param[2] = { + [0].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE0, + [1].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE1, +}; + +/* Set up the baud rate in gd struct */ +static void uart_zynq_serial_setbrg(const int port) +{ + /* Calculation results. */ + unsigned int calc_bauderror, bdiv, bgen; + unsigned long calc_baud = 0; + unsigned long baud = uart_zynq_ports_param[port].baudrate; + unsigned long clock = get_uart_clk(port); + struct uart_zynq *regs = uart_zynq_ports[port]; + + /* master clock + * Baud rate = ------------------ + * bgen * (bdiv + 1) + * + * Find acceptable values for baud generation. + */ + for (bdiv = 4; bdiv < 255; bdiv++) { + bgen = clock / (baud * (bdiv + 1)); + if (bgen < 2 || bgen > 65535) + continue; + + calc_baud = clock / (bgen * (bdiv + 1)); + + /* + * Use first calculated baudrate with + * an acceptable (<3%) error + */ + if (baud > calc_baud) + calc_bauderror = baud - calc_baud; + else + calc_bauderror = calc_baud - baud; + if (((calc_bauderror * 100) / baud) < 3) + break; + } + + writel(bdiv, ®s->baud_rate_divider); + writel(bgen, ®s->baud_rate_gen); +} + +/* Initialize the UART, with...some settings. */ +static int uart_zynq_serial_init(const int port) +{ + struct uart_zynq *regs = uart_zynq_ports[port]; + + if (!regs) + return -1; + + /* RX/TX enabled & reset */ + writel(ZYNQ_UART_CR_TX_EN | ZYNQ_UART_CR_RX_EN | ZYNQ_UART_CR_TXRST | \ + ZYNQ_UART_CR_RXRST, ®s->control); + writel(ZYNQ_UART_MR_PARITY_NONE, ®s->mode); /* 8 bit, no parity */ + uart_zynq_serial_setbrg(port); + + return 0; +} + +static void uart_zynq_serial_putc(const char c, const int port) +{ + struct uart_zynq *regs = uart_zynq_ports[port]; + + while ((readl(®s->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0) + WATCHDOG_RESET(); + + if (c == '\n') { + writel('\r', ®s->tx_rx_fifo); + while ((readl(®s->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0) + WATCHDOG_RESET(); + } + writel(c, ®s->tx_rx_fifo); +} + +static void uart_zynq_serial_puts(const char *s, const int port) +{ + while (*s) + uart_zynq_serial_putc(*s++, port); +} + +static int uart_zynq_serial_tstc(const int port) +{ + struct uart_zynq *regs = uart_zynq_ports[port]; + + return (readl(®s->channel_sts) & ZYNQ_UART_SR_RXEMPTY) == 0; +} + +static int uart_zynq_serial_getc(const int port) +{ + struct uart_zynq *regs = uart_zynq_ports[port]; + + while (!uart_zynq_serial_tstc(port)) + WATCHDOG_RESET(); + return readl(®s->tx_rx_fifo); +} + +/* Multi serial device functions */ +#define DECLARE_PSSERIAL_FUNCTIONS(port) \ + static int uart_zynq##port##_init(void) \ + { return uart_zynq_serial_init(port); } \ + static void uart_zynq##port##_setbrg(void) \ + { return uart_zynq_serial_setbrg(port); } \ + static int uart_zynq##port##_getc(void) \ + { return uart_zynq_serial_getc(port); } \ + static int uart_zynq##port##_tstc(void) \ + { return uart_zynq_serial_tstc(port); } \ + static void uart_zynq##port##_putc(const char c) \ + { uart_zynq_serial_putc(c, port); } \ + static void uart_zynq##port##_puts(const char *s) \ + { uart_zynq_serial_puts(s, port); } + +/* Serial device descriptor */ +#define INIT_PSSERIAL_STRUCTURE(port, __name) { \ + .name = __name, \ + .start = uart_zynq##port##_init, \ + .stop = NULL, \ + .setbrg = uart_zynq##port##_setbrg, \ + .getc = uart_zynq##port##_getc, \ + .tstc = uart_zynq##port##_tstc, \ + .putc = uart_zynq##port##_putc, \ + .puts = uart_zynq##port##_puts, \ +} + +DECLARE_PSSERIAL_FUNCTIONS(0); +static struct serial_device uart_zynq_serial0_device = + INIT_PSSERIAL_STRUCTURE(0, "ttyPS0"); +DECLARE_PSSERIAL_FUNCTIONS(1); +static struct serial_device uart_zynq_serial1_device = + INIT_PSSERIAL_STRUCTURE(1, "ttyPS1"); + +#ifdef CONFIG_OF_CONTROL +__weak struct serial_device *default_serial_console(void) +{ + const void *blob = gd->fdt_blob; + int node; + unsigned int base_addr; + + node = fdt_path_offset(blob, "serial0"); + if (node < 0) + return NULL; + + base_addr = fdtdec_get_addr(blob, node, "reg"); + if (base_addr == FDT_ADDR_T_NONE) + return NULL; + + if (base_addr == ZYNQ_SERIAL_BASEADDR0) + return &uart_zynq_serial0_device; + + if (base_addr == ZYNQ_SERIAL_BASEADDR1) + return &uart_zynq_serial1_device; + + return NULL; +} +#else +__weak struct serial_device *default_serial_console(void) +{ +#if defined(CONFIG_ZYNQ_SERIAL_UART0) + if (uart_zynq_ports[0]) + return &uart_zynq_serial0_device; +#endif +#if defined(CONFIG_ZYNQ_SERIAL_UART1) + if (uart_zynq_ports[1]) + return &uart_zynq_serial1_device; +#endif + return NULL; +} +#endif + +void zynq_serial_initialize(void) +{ + serial_register(&uart_zynq_serial0_device); + serial_register(&uart_zynq_serial1_device); +} diff --git a/qemu/roms/u-boot/drivers/serial/usbtty.c b/qemu/roms/u-boot/drivers/serial/usbtty.c new file mode 100644 index 000000000..6b912efaf --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/usbtty.c @@ -0,0 +1,1046 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * (C) Copyright 2006 + * Bryan O'Donoghue, bodonoghue@codehermit.ie + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include "usbtty.h" +#include "usb_cdc_acm.h" +#include "usbdescriptors.h" + +#ifdef DEBUG +#define TTYDBG(fmt,args...)\ + serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args) +#else +#define TTYDBG(fmt,args...) do{}while(0) +#endif + +#if 1 +#define TTYERR(fmt,args...)\ + serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,\ + __LINE__,##args) +#else +#define TTYERR(fmt,args...) do{}while(0) +#endif + +/* + * Defines + */ +#define NUM_CONFIGS 1 +#define MAX_INTERFACES 2 +#define NUM_ENDPOINTS 3 +#define ACM_TX_ENDPOINT 3 +#define ACM_RX_ENDPOINT 2 +#define GSERIAL_TX_ENDPOINT 2 +#define GSERIAL_RX_ENDPOINT 1 +#define NUM_ACM_INTERFACES 2 +#define NUM_GSERIAL_INTERFACES 1 +#define CONFIG_USBD_DATA_INTERFACE_STR "Bulk Data Interface" +#define CONFIG_USBD_CTRL_INTERFACE_STR "Control Interface" + +/* + * Buffers to hold input and output data + */ +#define USBTTY_BUFFER_SIZE 2048 +static circbuf_t usbtty_input; +static circbuf_t usbtty_output; + + +/* + * Instance variables + */ +static struct stdio_dev usbttydev; +static struct usb_device_instance device_instance[1]; +static struct usb_bus_instance bus_instance[1]; +static struct usb_configuration_instance config_instance[NUM_CONFIGS]; +static struct usb_interface_instance interface_instance[MAX_INTERFACES]; +static struct usb_alternate_instance alternate_instance[MAX_INTERFACES]; +/* one extra for control endpoint */ +static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1]; + +/* + * Global flag + */ +int usbtty_configured_flag = 0; + +/* + * Serial number + */ +static char serial_number[16]; + + +/* + * Descriptors, Strings, Local variables. + */ + +/* defined and used by gadget/ep0.c */ +extern struct usb_string_descriptor **usb_strings; + +/* Indicies, References */ +static unsigned short rx_endpoint = 0; +static unsigned short tx_endpoint = 0; +static unsigned short interface_count = 0; +static struct usb_string_descriptor *usbtty_string_table[STR_COUNT]; + +/* USB Descriptor Strings */ +static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4}; +static u8 wstrManufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)]; +static u8 wstrProduct[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)]; +static u8 wstrSerial[2 + 2*(sizeof(serial_number) - 1)]; +static u8 wstrConfiguration[2 + 2*(sizeof(CONFIG_USBD_CONFIGURATION_STR)-1)]; +static u8 wstrDataInterface[2 + 2*(sizeof(CONFIG_USBD_DATA_INTERFACE_STR)-1)]; +static u8 wstrCtrlInterface[2 + 2*(sizeof(CONFIG_USBD_DATA_INTERFACE_STR)-1)]; + +/* Standard USB Data Structures */ +static struct usb_interface_descriptor interface_descriptors[MAX_INTERFACES]; +static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS]; +static struct usb_configuration_descriptor *configuration_descriptor = 0; +static struct usb_device_descriptor device_descriptor = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = cpu_to_le16(USB_BCD_VERSION), + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE, + .idVendor = cpu_to_le16(CONFIG_USBD_VENDORID), + .bcdDevice = cpu_to_le16(USBTTY_BCD_DEVICE), + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIAL, + .bNumConfigurations = NUM_CONFIGS +}; + + +#if defined(CONFIG_USBD_HS) +static struct usb_qualifier_descriptor qualifier_descriptor = { + .bLength = sizeof(struct usb_qualifier_descriptor), + .bDescriptorType = USB_DT_QUAL, + .bcdUSB = cpu_to_le16(USB_BCD_VERSION), + .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE, + .bNumConfigurations = NUM_CONFIGS +}; +#endif + +/* + * Static CDC ACM specific descriptors + */ + +struct acm_config_desc { + struct usb_configuration_descriptor configuration_desc; + + /* Master Interface */ + struct usb_interface_descriptor interface_desc; + + struct usb_class_header_function_descriptor usb_class_header; + struct usb_class_call_management_descriptor usb_class_call_mgt; + struct usb_class_abstract_control_descriptor usb_class_acm; + struct usb_class_union_function_descriptor usb_class_union; + struct usb_endpoint_descriptor notification_endpoint; + + /* Slave Interface */ + struct usb_interface_descriptor data_class_interface; + struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS-1]; +} __attribute__((packed)); + +static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { + { + .configuration_desc ={ + .bLength = + sizeof(struct usb_configuration_descriptor), + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = + cpu_to_le16(sizeof(struct acm_config_desc)), + .bNumInterfaces = NUM_ACM_INTERFACES, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG, + .bmAttributes = + BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, + .bMaxPower = USBTTY_MAXPOWER + }, + /* Interface 1 */ + .interface_desc = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0x01, + .bInterfaceClass = + COMMUNICATIONS_INTERFACE_CLASS_CONTROL, + .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS, + .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL, + .iInterface = STR_CTRL_INTERFACE, + }, + .usb_class_header = { + .bFunctionLength = + sizeof(struct usb_class_header_function_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_HEADER, + .bcdCDC = cpu_to_le16(110), + }, + .usb_class_call_mgt = { + .bFunctionLength = + sizeof(struct usb_class_call_management_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_CMF, + .bmCapabilities = 0x00, + .bDataInterface = 0x01, + }, + .usb_class_acm = { + .bFunctionLength = + sizeof(struct usb_class_abstract_control_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_ACMF, + .bmCapabilities = 0x00, + }, + .usb_class_union = { + .bFunctionLength = + sizeof(struct usb_class_union_function_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_UF, + .bMasterInterface = 0x00, + .bSlaveInterface0 = 0x01, + }, + .notification_endpoint = { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = UDC_INT_ENDPOINT | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize + = cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE), + .bInterval = 0xFF, + }, + + /* Interface 2 */ + .data_class_interface = { + .bLength = + sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = + COMMUNICATIONS_INTERFACE_CLASS_DATA, + .bInterfaceSubClass = DATA_INTERFACE_SUBCLASS_NONE, + .bInterfaceProtocol = DATA_INTERFACE_PROTOCOL_NONE, + .iInterface = STR_DATA_INTERFACE, + }, + .data_endpoints = { + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = UDC_OUT_ENDPOINT | USB_DIR_OUT, + .bmAttributes = + USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE), + .bInterval = 0xFF, + }, + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = UDC_IN_ENDPOINT | USB_DIR_IN, + .bmAttributes = + USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE), + .bInterval = 0xFF, + }, + }, + }, +}; + +static struct rs232_emu rs232_desc={ + .dter = 115200, + .stop_bits = 0x00, + .parity = 0x00, + .data_bits = 0x08 +}; + + +/* + * Static Generic Serial specific data + */ + + +struct gserial_config_desc { + + struct usb_configuration_descriptor configuration_desc; + struct usb_interface_descriptor interface_desc[NUM_GSERIAL_INTERFACES]; + struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS]; + +} __attribute__((packed)); + +static struct gserial_config_desc +gserial_configuration_descriptors[NUM_CONFIGS] ={ + { + .configuration_desc ={ + .bLength = sizeof(struct usb_configuration_descriptor), + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = + cpu_to_le16(sizeof(struct gserial_config_desc)), + .bNumInterfaces = NUM_GSERIAL_INTERFACES, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG, + .bmAttributes = + BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, + .bMaxPower = USBTTY_MAXPOWER + }, + .interface_desc = { + { + .bLength = + sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = NUM_ENDPOINTS, + .bInterfaceClass = + COMMUNICATIONS_INTERFACE_CLASS_VENDOR, + .bInterfaceSubClass = + COMMUNICATIONS_NO_SUBCLASS, + .bInterfaceProtocol = + COMMUNICATIONS_NO_PROTOCOL, + .iInterface = STR_DATA_INTERFACE + }, + }, + .data_endpoints = { + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = UDC_OUT_ENDPOINT | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_OUT_PKTSIZE), + .bInterval= 0xFF, + }, + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = UDC_IN_ENDPOINT | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_IN_PKTSIZE), + .bInterval = 0xFF, + }, + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = UDC_INT_ENDPOINT | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE), + .bInterval = 0xFF, + }, + }, + }, +}; + +/* + * Static Function Prototypes + */ + +static void usbtty_init_strings (void); +static void usbtty_init_instances (void); +static void usbtty_init_endpoints (void); +static void usbtty_init_terminal_type(short type); +static void usbtty_event_handler (struct usb_device_instance *device, + usb_device_event_t event, int data); +static int usbtty_cdc_setup(struct usb_device_request *request, + struct urb *urb); +static int usbtty_configured (void); +static int write_buffer (circbuf_t * buf); +static int fill_buffer (circbuf_t * buf); + +void usbtty_poll (void); + +/* utility function for converting char* to wide string used by USB */ +static void str2wide (char *str, u16 * wide) +{ + int i; + for (i = 0; i < strlen (str) && str[i]; i++){ + #if defined(__LITTLE_ENDIAN) + wide[i] = (u16) str[i]; + #elif defined(__BIG_ENDIAN) + wide[i] = ((u16)(str[i])<<8); + #else + #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined" + #endif + } +} + +/* + * Test whether a character is in the RX buffer + */ + +int usbtty_tstc (void) +{ + struct usb_endpoint_instance *endpoint = + &endpoint_instance[rx_endpoint]; + + /* If no input data exists, allow more RX to be accepted */ + if(usbtty_input.size <= 0){ + udc_unset_nak(endpoint->endpoint_address&0x03); + } + + usbtty_poll (); + return (usbtty_input.size > 0); +} + +/* + * Read a single byte from the usb client port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ + +int usbtty_getc (void) +{ + char c; + struct usb_endpoint_instance *endpoint = + &endpoint_instance[rx_endpoint]; + + while (usbtty_input.size <= 0) { + udc_unset_nak(endpoint->endpoint_address&0x03); + usbtty_poll (); + } + + buf_pop (&usbtty_input, &c, 1); + udc_set_nak(endpoint->endpoint_address&0x03); + + return c; +} + +/* + * Output a single byte to the usb client port. + */ +void usbtty_putc (const char c) +{ + if (!usbtty_configured ()) + return; + + buf_push (&usbtty_output, &c, 1); + /* If \n, also do \r */ + if (c == '\n') + buf_push (&usbtty_output, "\r", 1); + + /* Poll at end to handle new data... */ + if ((usbtty_output.size + 2) >= usbtty_output.totalsize) { + usbtty_poll (); + } +} + +/* usbtty_puts() helper function for finding the next '\n' in a string */ +static int next_nl_pos (const char *s) +{ + int i; + + for (i = 0; s[i] != '\0'; i++) { + if (s[i] == '\n') + return i; + } + return i; +} + +/* + * Output a string to the usb client port - implementing flow control + */ + +static void __usbtty_puts (const char *str, int len) +{ + int maxlen = usbtty_output.totalsize; + int space, n; + + /* break str into chunks < buffer size, if needed */ + while (len > 0) { + usbtty_poll (); + + space = maxlen - usbtty_output.size; + /* Empty buffer here, if needed, to ensure space... */ + if (space) { + write_buffer (&usbtty_output); + + n = MIN (space, MIN (len, maxlen)); + buf_push (&usbtty_output, str, n); + + str += n; + len -= n; + } + } +} + +void usbtty_puts (const char *str) +{ + int n; + int len; + + if (!usbtty_configured ()) + return; + + len = strlen (str); + /* add '\r' for each '\n' */ + while (len > 0) { + n = next_nl_pos (str); + + if (str[n] == '\n') { + __usbtty_puts (str, n + 1); + __usbtty_puts ("\r", 1); + str += (n + 1); + len -= (n + 1); + } else { + /* No \n found. All done. */ + __usbtty_puts (str, n); + break; + } + } + + /* Poll at end to handle new data... */ + usbtty_poll (); +} + +/* + * Initialize the usb client port. + * + */ +int drv_usbtty_init (void) +{ + int rc; + char * sn; + char * tt; + int snlen; + + /* Ger seiral number */ + if (!(sn = getenv("serial#"))) { + sn = "000000000000"; + } + snlen = strlen(sn); + if (snlen > sizeof(serial_number) - 1) { + printf ("Warning: serial number %s is too long (%d > %lu)\n", + sn, snlen, (ulong)(sizeof(serial_number) - 1)); + snlen = sizeof(serial_number) - 1; + } + memcpy (serial_number, sn, snlen); + serial_number[snlen] = '\0'; + + /* Decide on which type of UDC device to be. + */ + + if(!(tt = getenv("usbtty"))) { + tt = "generic"; + } + usbtty_init_terminal_type(strcmp(tt,"cdc_acm")); + + /* prepare buffers... */ + buf_init (&usbtty_input, USBTTY_BUFFER_SIZE); + buf_init (&usbtty_output, USBTTY_BUFFER_SIZE); + + /* Now, set up USB controller and infrastructure */ + udc_init (); /* Basic USB initialization */ + + usbtty_init_strings (); + usbtty_init_instances (); + + usbtty_init_endpoints (); + + udc_startup_events (device_instance);/* Enable dev, init udc pointers */ + udc_connect (); /* Enable pullup for host detection */ + + /* Device initialization */ + memset (&usbttydev, 0, sizeof (usbttydev)); + + strcpy (usbttydev.name, "usbtty"); + usbttydev.ext = 0; /* No extensions */ + usbttydev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_OUTPUT; + usbttydev.tstc = usbtty_tstc; /* 'tstc' function */ + usbttydev.getc = usbtty_getc; /* 'getc' function */ + usbttydev.putc = usbtty_putc; /* 'putc' function */ + usbttydev.puts = usbtty_puts; /* 'puts' function */ + + rc = stdio_register (&usbttydev); + + return (rc == 0) ? 1 : rc; +} + +static void usbtty_init_strings (void) +{ + struct usb_string_descriptor *string; + + usbtty_string_table[STR_LANG] = + (struct usb_string_descriptor*)wstrLang; + + string = (struct usb_string_descriptor *) wstrManufacturer; + string->bLength = sizeof(wstrManufacturer); + string->bDescriptorType = USB_DT_STRING; + str2wide (CONFIG_USBD_MANUFACTURER, string->wData); + usbtty_string_table[STR_MANUFACTURER]=string; + + + string = (struct usb_string_descriptor *) wstrProduct; + string->bLength = sizeof(wstrProduct); + string->bDescriptorType = USB_DT_STRING; + str2wide (CONFIG_USBD_PRODUCT_NAME, string->wData); + usbtty_string_table[STR_PRODUCT]=string; + + + string = (struct usb_string_descriptor *) wstrSerial; + string->bLength = sizeof(serial_number); + string->bDescriptorType = USB_DT_STRING; + str2wide (serial_number, string->wData); + usbtty_string_table[STR_SERIAL]=string; + + + string = (struct usb_string_descriptor *) wstrConfiguration; + string->bLength = sizeof(wstrConfiguration); + string->bDescriptorType = USB_DT_STRING; + str2wide (CONFIG_USBD_CONFIGURATION_STR, string->wData); + usbtty_string_table[STR_CONFIG]=string; + + + string = (struct usb_string_descriptor *) wstrDataInterface; + string->bLength = sizeof(wstrDataInterface); + string->bDescriptorType = USB_DT_STRING; + str2wide (CONFIG_USBD_DATA_INTERFACE_STR, string->wData); + usbtty_string_table[STR_DATA_INTERFACE]=string; + + string = (struct usb_string_descriptor *) wstrCtrlInterface; + string->bLength = sizeof(wstrCtrlInterface); + string->bDescriptorType = USB_DT_STRING; + str2wide (CONFIG_USBD_CTRL_INTERFACE_STR, string->wData); + usbtty_string_table[STR_CTRL_INTERFACE]=string; + + /* Now, initialize the string table for ep0 handling */ + usb_strings = usbtty_string_table; +} + +#define init_wMaxPacketSize(x) le16_to_cpu(get_unaligned(\ + &ep_descriptor_ptrs[(x) - 1]->wMaxPacketSize)); + +static void usbtty_init_instances (void) +{ + int i; + + /* initialize device instance */ + memset (device_instance, 0, sizeof (struct usb_device_instance)); + device_instance->device_state = STATE_INIT; + device_instance->device_descriptor = &device_descriptor; +#if defined(CONFIG_USBD_HS) + device_instance->qualifier_descriptor = &qualifier_descriptor; +#endif + device_instance->event = usbtty_event_handler; + device_instance->cdc_recv_setup = usbtty_cdc_setup; + device_instance->bus = bus_instance; + device_instance->configurations = NUM_CONFIGS; + device_instance->configuration_instance_array = config_instance; + + /* initialize bus instance */ + memset (bus_instance, 0, sizeof (struct usb_bus_instance)); + bus_instance->device = device_instance; + bus_instance->endpoint_array = endpoint_instance; + bus_instance->max_endpoints = 1; + bus_instance->maxpacketsize = 64; + bus_instance->serial_number_str = serial_number; + + /* configuration instance */ + memset (config_instance, 0, + sizeof (struct usb_configuration_instance)); + config_instance->interfaces = interface_count; + config_instance->configuration_descriptor = configuration_descriptor; + config_instance->interface_instance_array = interface_instance; + + /* interface instance */ + memset (interface_instance, 0, + sizeof (struct usb_interface_instance)); + interface_instance->alternates = 1; + interface_instance->alternates_instance_array = alternate_instance; + + /* alternates instance */ + memset (alternate_instance, 0, + sizeof (struct usb_alternate_instance)); + alternate_instance->interface_descriptor = interface_descriptors; + alternate_instance->endpoints = NUM_ENDPOINTS; + alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs; + + /* endpoint instances */ + memset (&endpoint_instance[0], 0, + sizeof (struct usb_endpoint_instance)); + endpoint_instance[0].endpoint_address = 0; + endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE; + endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL; + endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE; + endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL; + udc_setup_ep (device_instance, 0, &endpoint_instance[0]); + + for (i = 1; i <= NUM_ENDPOINTS; i++) { + memset (&endpoint_instance[i], 0, + sizeof (struct usb_endpoint_instance)); + + endpoint_instance[i].endpoint_address = + ep_descriptor_ptrs[i - 1]->bEndpointAddress; + + endpoint_instance[i].rcv_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + endpoint_instance[i].rcv_packetSize = init_wMaxPacketSize(i); + + endpoint_instance[i].tx_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + endpoint_instance[i].tx_packetSize = init_wMaxPacketSize(i); + + endpoint_instance[i].tx_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; + + urb_link_init (&endpoint_instance[i].rcv); + urb_link_init (&endpoint_instance[i].rdy); + urb_link_init (&endpoint_instance[i].tx); + urb_link_init (&endpoint_instance[i].done); + + if (endpoint_instance[i].endpoint_address & USB_DIR_IN) + endpoint_instance[i].tx_urb = + usbd_alloc_urb (device_instance, + &endpoint_instance[i]); + else + endpoint_instance[i].rcv_urb = + usbd_alloc_urb (device_instance, + &endpoint_instance[i]); + } +} + +static void usbtty_init_endpoints (void) +{ + int i; + + bus_instance->max_endpoints = NUM_ENDPOINTS + 1; + for (i = 1; i <= NUM_ENDPOINTS; i++) { + udc_setup_ep (device_instance, i, &endpoint_instance[i]); + } +} + +/* usbtty_init_terminal_type + * + * Do some late binding for our device type. + */ +static void usbtty_init_terminal_type(short type) +{ + switch(type){ + /* CDC ACM */ + case 0: + /* Assign endpoint descriptors */ + ep_descriptor_ptrs[0] = + &acm_configuration_descriptors[0].notification_endpoint; + ep_descriptor_ptrs[1] = + &acm_configuration_descriptors[0].data_endpoints[0]; + ep_descriptor_ptrs[2] = + &acm_configuration_descriptors[0].data_endpoints[1]; + + /* Enumerate Device Descriptor */ + device_descriptor.bDeviceClass = + COMMUNICATIONS_DEVICE_CLASS; + device_descriptor.idProduct = + cpu_to_le16(CONFIG_USBD_PRODUCTID_CDCACM); + +#if defined(CONFIG_USBD_HS) + qualifier_descriptor.bDeviceClass = + COMMUNICATIONS_DEVICE_CLASS; +#endif + /* Assign endpoint indices */ + tx_endpoint = ACM_TX_ENDPOINT; + rx_endpoint = ACM_RX_ENDPOINT; + + /* Configuration Descriptor */ + configuration_descriptor = + (struct usb_configuration_descriptor*) + &acm_configuration_descriptors; + + /* Interface count */ + interface_count = NUM_ACM_INTERFACES; + break; + + /* BULK IN/OUT & Default */ + case 1: + default: + /* Assign endpoint descriptors */ + ep_descriptor_ptrs[0] = + &gserial_configuration_descriptors[0].data_endpoints[0]; + ep_descriptor_ptrs[1] = + &gserial_configuration_descriptors[0].data_endpoints[1]; + ep_descriptor_ptrs[2] = + &gserial_configuration_descriptors[0].data_endpoints[2]; + + /* Enumerate Device Descriptor */ + device_descriptor.bDeviceClass = 0xFF; + device_descriptor.idProduct = + cpu_to_le16(CONFIG_USBD_PRODUCTID_GSERIAL); +#if defined(CONFIG_USBD_HS) + qualifier_descriptor.bDeviceClass = 0xFF; +#endif + /* Assign endpoint indices */ + tx_endpoint = GSERIAL_TX_ENDPOINT; + rx_endpoint = GSERIAL_RX_ENDPOINT; + + /* Configuration Descriptor */ + configuration_descriptor = + (struct usb_configuration_descriptor*) + &gserial_configuration_descriptors; + + /* Interface count */ + interface_count = NUM_GSERIAL_INTERFACES; + break; + } +} + +/******************************************************************************/ + +static struct urb *next_urb (struct usb_device_instance *device, + struct usb_endpoint_instance *endpoint) +{ + struct urb *current_urb = NULL; + int space; + + /* If there's a queue, then we should add to the last urb */ + if (!endpoint->tx_queue) { + current_urb = endpoint->tx_urb; + } else { + /* Last urb from tx chain */ + current_urb = + p2surround (struct urb, link, endpoint->tx.prev); + } + + /* Make sure this one has enough room */ + space = current_urb->buffer_length - current_urb->actual_length; + if (space > 0) { + return current_urb; + } else { /* No space here */ + /* First look at done list */ + current_urb = first_urb_detached (&endpoint->done); + if (!current_urb) { + current_urb = usbd_alloc_urb (device, endpoint); + } + + urb_append (&endpoint->tx, current_urb); + endpoint->tx_queue++; + } + return current_urb; +} + +static int write_buffer (circbuf_t * buf) +{ + if (!usbtty_configured ()) { + return 0; + } + + struct usb_endpoint_instance *endpoint = + &endpoint_instance[tx_endpoint]; + struct urb *current_urb = NULL; + + current_urb = next_urb (device_instance, endpoint); + /* TX data still exists - send it now + */ + if(endpoint->sent < current_urb->actual_length){ + if(udc_endpoint_write (endpoint)){ + /* Write pre-empted by RX */ + return -1; + } + } + + if (buf->size) { + char *dest; + + int space_avail; + int popnum, popped; + int total = 0; + + /* Break buffer into urb sized pieces, + * and link each to the endpoint + */ + while (buf->size > 0) { + + if (!current_urb) { + TTYERR ("current_urb is NULL, buf->size %d\n", + buf->size); + return total; + } + + dest = (char*)current_urb->buffer + + current_urb->actual_length; + + space_avail = + current_urb->buffer_length - + current_urb->actual_length; + popnum = MIN (space_avail, buf->size); + if (popnum == 0) + break; + + popped = buf_pop (buf, dest, popnum); + if (popped == 0) + break; + current_urb->actual_length += popped; + total += popped; + + /* If endpoint->last == 0, then transfers have + * not started on this endpoint + */ + if (endpoint->last == 0) { + if(udc_endpoint_write (endpoint)){ + /* Write pre-empted by RX */ + return -1; + } + } + + }/* end while */ + return total; + } + + return 0; +} + +static int fill_buffer (circbuf_t * buf) +{ + struct usb_endpoint_instance *endpoint = + &endpoint_instance[rx_endpoint]; + + if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) { + unsigned int nb = 0; + char *src = (char *) endpoint->rcv_urb->buffer; + unsigned int rx_avail = buf->totalsize - buf->size; + + if(rx_avail >= endpoint->rcv_urb->actual_length){ + + nb = endpoint->rcv_urb->actual_length; + buf_push (buf, src, nb); + endpoint->rcv_urb->actual_length = 0; + + } + return nb; + } + return 0; +} + +static int usbtty_configured (void) +{ + return usbtty_configured_flag; +} + +/******************************************************************************/ + +static void usbtty_event_handler (struct usb_device_instance *device, + usb_device_event_t event, int data) +{ +#if defined(CONFIG_USBD_HS) + int i; +#endif + switch (event) { + case DEVICE_RESET: + case DEVICE_BUS_INACTIVE: + usbtty_configured_flag = 0; + break; + case DEVICE_CONFIGURED: + usbtty_configured_flag = 1; + break; + + case DEVICE_ADDRESS_ASSIGNED: +#if defined(CONFIG_USBD_HS) + /* + * is_usbd_high_speed routine needs to be defined by + * specific gadget driver + * It returns true if device enumerates at High speed + * Retuns false otherwise + */ + for (i = 0; i < NUM_ENDPOINTS; i++) { + if (((ep_descriptor_ptrs[i]->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK) + && is_usbd_high_speed()) { + + ep_descriptor_ptrs[i]->wMaxPacketSize = + CONFIG_USBD_SERIAL_BULK_HS_PKTSIZE; + } + + endpoint_instance[i + 1].tx_packetSize = + ep_descriptor_ptrs[i]->wMaxPacketSize; + endpoint_instance[i + 1].rcv_packetSize = + ep_descriptor_ptrs[i]->wMaxPacketSize; + } +#endif + usbtty_init_endpoints (); + + default: + break; + } +} + +/******************************************************************************/ + +int usbtty_cdc_setup(struct usb_device_request *request, struct urb *urb) +{ + switch (request->bRequest){ + + case ACM_SET_CONTROL_LINE_STATE: /* Implies DTE ready */ + break; + case ACM_SEND_ENCAPSULATED_COMMAND : /* Required */ + break; + case ACM_SET_LINE_ENCODING : /* DTE stop/parity bits + * per character */ + break; + case ACM_GET_ENCAPSULATED_RESPONSE : /* request response */ + break; + case ACM_GET_LINE_ENCODING : /* request DTE rate, + * stop/parity bits */ + memcpy (urb->buffer , &rs232_desc, sizeof(rs232_desc)); + urb->actual_length = sizeof(rs232_desc); + + break; + default: + return 1; + } + return 0; +} + +/******************************************************************************/ + +/* + * Since interrupt handling has not yet been implemented, we use this function + * to handle polling. This is called by the tstc,getc,putc,puts routines to + * update the USB state. + */ +void usbtty_poll (void) +{ + /* New interrupts? */ + udc_irq(); + + /* Write any output data to host buffer + * (do this before checking interrupts to avoid missing one) + */ + if (usbtty_configured ()) { + write_buffer (&usbtty_output); + } + + /* New interrupts? */ + udc_irq(); + + /* Check for new data from host.. + * (do this after checking interrupts to get latest data) + */ + if (usbtty_configured ()) { + fill_buffer (&usbtty_input); + } + + /* New interrupts? */ + udc_irq(); + +} diff --git a/qemu/roms/u-boot/drivers/serial/usbtty.h b/qemu/roms/u-boot/drivers/serial/usbtty.h new file mode 100644 index 000000000..21a3ef4d9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/serial/usbtty.h @@ -0,0 +1,79 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * (C) Copyright 2006 + * Bryan O'Donoghue, bodonoghue@codehermit.ie, CodeHermit + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __USB_TTY_H__ +#define __USB_TTY_H__ + +#include +#if defined(CONFIG_PPC) +#include +#elif defined(CONFIG_OMAP1510) +#include +#elif defined(CONFIG_CPU_PXA27X) +#include +#elif defined(CONFIG_DW_UDC) +#include +#elif defined(CONFIG_CI_UDC) +#include +#endif + +#include +#include + +/* If no VendorID/ProductID is defined in config.h, pretend to be Linux + * DO NOT Reuse this Vendor/Product setup with protocol incompatible devices */ + +#ifndef CONFIG_USBD_VENDORID +#define CONFIG_USBD_VENDORID 0x0525 /* Linux/NetChip */ +#endif +#ifndef CONFIG_USBD_PRODUCTID_GSERIAL +#define CONFIG_USBD_PRODUCTID_GSERIAL 0xa4a6 /* gserial */ +#endif +#ifndef CONFIG_USBD_PRODUCTID_CDCACM +#define CONFIG_USBD_PRODUCTID_CDCACM 0xa4a7 /* CDC ACM */ +#endif +#ifndef CONFIG_USBD_MANUFACTURER +#define CONFIG_USBD_MANUFACTURER "Das U-Boot" +#endif +#ifndef CONFIG_USBD_PRODUCT_NAME +#define CONFIG_USBD_PRODUCT_NAME U_BOOT_VERSION +#endif + +#ifndef CONFIG_USBD_CONFIGURATION_STR +#define CONFIG_USBD_CONFIGURATION_STR "TTY via USB" +#endif + +#define CONFIG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT +#define CONFIG_USBD_SERIAL_OUT_PKTSIZE UDC_OUT_PACKET_SIZE +#define CONFIG_USBD_SERIAL_IN_ENDPOINT UDC_IN_ENDPOINT +#define CONFIG_USBD_SERIAL_IN_PKTSIZE UDC_IN_PACKET_SIZE +#define CONFIG_USBD_SERIAL_INT_ENDPOINT UDC_INT_ENDPOINT +#define CONFIG_USBD_SERIAL_INT_PKTSIZE UDC_INT_PACKET_SIZE +#define CONFIG_USBD_SERIAL_BULK_PKTSIZE UDC_BULK_PACKET_SIZE + +#if defined(CONFIG_USBD_HS) +#define CONFIG_USBD_SERIAL_BULK_HS_PKTSIZE UDC_BULK_HS_PACKET_SIZE +#endif + +#define USBTTY_DEVICE_CLASS COMMUNICATIONS_DEVICE_CLASS + +#define USBTTY_BCD_DEVICE 0x00 +#define USBTTY_MAXPOWER 0x00 + +#define STR_LANG 0x00 +#define STR_MANUFACTURER 0x01 +#define STR_PRODUCT 0x02 +#define STR_SERIAL 0x03 +#define STR_CONFIG 0x04 +#define STR_DATA_INTERFACE 0x05 +#define STR_CTRL_INTERFACE 0x06 +#define STR_COUNT 0x07 + +#endif diff --git a/qemu/roms/u-boot/drivers/sound/Makefile b/qemu/roms/u-boot/drivers/sound/Makefile new file mode 100644 index 000000000..981ed614b --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/Makefile @@ -0,0 +1,13 @@ +# +# Copyright (C) 2012 Samsung Electronics +# R. Chandrasekar +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_SOUND) += sound.o +obj-$(CONFIG_I2S) += sound-i2s.o +obj-$(CONFIG_I2S_SAMSUNG) += samsung-i2s.o +obj-$(CONFIG_SOUND_SANDBOX) += sandbox.o +obj-$(CONFIG_SOUND_WM8994) += wm8994.o +obj-$(CONFIG_SOUND_MAX98095) += max98095.o diff --git a/qemu/roms/u-boot/drivers/sound/max98095.c b/qemu/roms/u-boot/drivers/sound/max98095.c new file mode 100644 index 000000000..febf4195b --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/max98095.c @@ -0,0 +1,591 @@ +/* + * max98095.c -- MAX98095 ALSA SoC Audio driver + * + * Copyright 2011 Maxim Integrated Products + * + * Modified for uboot by R. Chandrasekar (rcsekar@samsung.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2s.h" +#include "max98095.h" + +enum max98095_type { + MAX98095, +}; + +struct max98095_priv { + enum max98095_type devtype; + unsigned int sysclk; + unsigned int rate; + unsigned int fmt; +}; + +static struct sound_codec_info g_codec_info; +struct max98095_priv g_max98095_info; +unsigned int g_max98095_i2c_dev_addr; + +/* Index 0 is reserved. */ +int rate_table[] = {0, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000}; + +/* + * Writes value to a device register through i2c + * + * @param reg reg number to be write + * @param data data to be writen to the above registor + * + * @return int value 1 for change, 0 for no change or negative error code. + */ +static int max98095_i2c_write(unsigned int reg, unsigned char data) +{ + debug("%s: Write Addr : 0x%02X, Data : 0x%02X\n", + __func__, reg, data); + return i2c_write(g_max98095_i2c_dev_addr, reg, 1, &data, 1); +} + +/* + * Read a value from a device register through i2c + * + * @param reg reg number to be read + * @param data address of read data to be stored + * + * @return int value 0 for success, -1 in case of error. + */ +static unsigned int max98095_i2c_read(unsigned int reg, unsigned char *data) +{ + int ret; + + ret = i2c_read(g_max98095_i2c_dev_addr, reg, 1, data, 1); + if (ret != 0) { + debug("%s: Error while reading register %#04x\n", + __func__, reg); + return -1; + } + + return 0; +} + +/* + * update device register bits through i2c + * + * @param reg codec register + * @param mask register mask + * @param value new value + * + * @return int value 0 for success, non-zero error code. + */ +static int max98095_update_bits(unsigned int reg, unsigned char mask, + unsigned char value) +{ + int change, ret = 0; + unsigned char old, new; + + if (max98095_i2c_read(reg, &old) != 0) + return -1; + new = (old & ~mask) | (value & mask); + change = (old != new) ? 1 : 0; + if (change) + ret = max98095_i2c_write(reg, new); + if (ret < 0) + return ret; + + return change; +} + +/* + * codec mclk clock divider coefficients based on sampling rate + * + * @param rate sampling rate + * @param value address of indexvalue to be stored + * + * @return 0 for success or negative error code. + */ +static int rate_value(int rate, u8 *value) +{ + int i; + + for (i = 1; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i] >= rate) { + *value = i; + return 0; + } + } + *value = 1; + + return -1; +} + +/* + * Sets hw params for max98095 + * + * @param max98095 max98095 information pointer + * @param rate Sampling rate + * @param bits_per_sample Bits per sample + * + * @return -1 for error and 0 Success. + */ +static int max98095_hw_params(struct max98095_priv *max98095, + enum en_max_audio_interface aif_id, + unsigned int rate, unsigned int bits_per_sample) +{ + u8 regval; + int error; + unsigned short M98095_DAI_CLKMODE; + unsigned short M98095_DAI_FORMAT; + unsigned short M98095_DAI_FILTERS; + + if (aif_id == AIF1) { + M98095_DAI_CLKMODE = M98095_027_DAI1_CLKMODE; + M98095_DAI_FORMAT = M98095_02A_DAI1_FORMAT; + M98095_DAI_FILTERS = M98095_02E_DAI1_FILTERS; + } else { + M98095_DAI_CLKMODE = M98095_031_DAI2_CLKMODE; + M98095_DAI_FORMAT = M98095_034_DAI2_FORMAT; + M98095_DAI_FILTERS = M98095_038_DAI2_FILTERS; + } + + switch (bits_per_sample) { + case 16: + error = max98095_update_bits(M98095_DAI_FORMAT, + M98095_DAI_WS, 0); + break; + case 24: + error = max98095_update_bits(M98095_DAI_FORMAT, + M98095_DAI_WS, M98095_DAI_WS); + break; + default: + debug("%s: Illegal bits per sample %d.\n", + __func__, bits_per_sample); + return -1; + } + + if (rate_value(rate, ®val)) { + debug("%s: Failed to set sample rate to %d.\n", + __func__, rate); + return -1; + } + max98095->rate = rate; + + error |= max98095_update_bits(M98095_DAI_CLKMODE, + M98095_CLKMODE_MASK, regval); + + /* Update sample rate mode */ + if (rate < 50000) + error |= max98095_update_bits(M98095_DAI_FILTERS, + M98095_DAI_DHF, 0); + else + error |= max98095_update_bits(M98095_DAI_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); + + if (error < 0) { + debug("%s: Error setting hardware params.\n", __func__); + return -1; + } + + return 0; +} + +/* + * Configures Audio interface system clock for the given frequency + * + * @param max98095 max98095 information + * @param freq Sampling frequency in Hz + * + * @return -1 for error and 0 success. + */ +static int max98095_set_sysclk(struct max98095_priv *max98095, + unsigned int freq) +{ + int error = 0; + + /* Requested clock frequency is already setup */ + if (freq == max98095->sysclk) + return 0; + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 40MHz).. + * 0x03 (when master clk is 40MHz to 60MHz).. + */ + if ((freq >= 10000000) && (freq < 20000000)) { + error = max98095_i2c_write(M98095_026_SYS_CLK, 0x10); + } else if ((freq >= 20000000) && (freq < 40000000)) { + error = max98095_i2c_write(M98095_026_SYS_CLK, 0x20); + } else if ((freq >= 40000000) && (freq < 60000000)) { + error = max98095_i2c_write(M98095_026_SYS_CLK, 0x30); + } else { + debug("%s: Invalid master clock frequency\n", __func__); + return -1; + } + + debug("%s: Clock at %uHz\n", __func__, freq); + + if (error < 0) + return -1; + + max98095->sysclk = freq; + return 0; +} + +/* + * Sets Max98095 I2S format + * + * @param max98095 max98095 information + * @param fmt i2S format - supports a subset of the options defined + * in i2s.h. + * + * @return -1 for error and 0 Success. + */ +static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, + enum en_max_audio_interface aif_id) +{ + u8 regval = 0; + int error = 0; + unsigned short M98095_DAI_CLKCFG_HI; + unsigned short M98095_DAI_CLKCFG_LO; + unsigned short M98095_DAI_FORMAT; + unsigned short M98095_DAI_CLOCK; + + if (fmt == max98095->fmt) + return 0; + + max98095->fmt = fmt; + + if (aif_id == AIF1) { + M98095_DAI_CLKCFG_HI = M98095_028_DAI1_CLKCFG_HI; + M98095_DAI_CLKCFG_LO = M98095_029_DAI1_CLKCFG_LO; + M98095_DAI_FORMAT = M98095_02A_DAI1_FORMAT; + M98095_DAI_CLOCK = M98095_02B_DAI1_CLOCK; + } else { + M98095_DAI_CLKCFG_HI = M98095_032_DAI2_CLKCFG_HI; + M98095_DAI_CLKCFG_LO = M98095_033_DAI2_CLKCFG_LO; + M98095_DAI_FORMAT = M98095_034_DAI2_FORMAT; + M98095_DAI_CLOCK = M98095_035_DAI2_CLOCK; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Slave mode PLL */ + error |= max98095_i2c_write(M98095_DAI_CLKCFG_HI, + 0x80); + error |= max98095_i2c_write(M98095_DAI_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + regval |= M98095_DAI_MAS; + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + debug("%s: Clock mode unsupported\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98095_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + debug("%s: Unrecognized format.\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98095_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98095_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98095_DAI_BCI | M98095_DAI_WCI; + break; + default: + debug("%s: Unrecognized inversion settings.\n", __func__); + return -1; + } + + error |= max98095_update_bits(M98095_DAI_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | + M98095_DAI_BCI | M98095_DAI_WCI, + regval); + + error |= max98095_i2c_write(M98095_DAI_CLOCK, + M98095_DAI_BSEL64); + + if (error < 0) { + debug("%s: Error setting i2s format.\n", __func__); + return -1; + } + + return 0; +} + +/* + * resets the audio codec + * + * @return -1 for error and 0 success. + */ +static int max98095_reset(void) +{ + int i, ret; + + /* + * Gracefully reset the DSP core and the codec hardware in a proper + * sequence. + */ + ret = max98095_i2c_write(M98095_00F_HOST_CFG, 0); + if (ret != 0) { + debug("%s: Failed to reset DSP: %d\n", __func__, ret); + return ret; + } + + ret = max98095_i2c_write(M98095_097_PWR_SYS, 0); + if (ret != 0) { + debug("%s: Failed to reset codec: %d\n", __func__, ret); + return ret; + } + + /* + * Reset to hardware default for registers, as there is not a soft + * reset hardware control register. + */ + for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) { + ret = max98095_i2c_write(i, 0); + if (ret < 0) { + debug("%s: Failed to reset: %d\n", __func__, ret); + return ret; + } + } + + return 0; +} + +/* + * Intialise max98095 codec device + * + * @param max98095 max98095 information + * + * @returns -1 for error and 0 Success. + */ +static int max98095_device_init(struct max98095_priv *max98095, + enum en_max_audio_interface aif_id) +{ + unsigned char id; + int error = 0; + + /* reset the codec, the DSP core, and disable all interrupts */ + error = max98095_reset(); + if (error != 0) { + debug("Reset\n"); + return error; + } + + /* initialize private data */ + max98095->sysclk = -1U; + max98095->rate = -1U; + max98095->fmt = -1U; + + error = max98095_i2c_read(M98095_0FF_REV_ID, &id); + if (error < 0) { + debug("%s: Failure reading hardware revision: %d\n", + __func__, id); + goto err_access; + } + debug("%s: Hardware revision: %c\n", __func__, (id - 0x40) + 'A'); + + error |= max98095_i2c_write(M98095_097_PWR_SYS, M98095_PWRSV); + + /* + * initialize registers to hardware default configuring audio + * interface2 to DAC + */ + if (aif_id == AIF1) + error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, + M98095_DAI1L_TO_DACL | + M98095_DAI1R_TO_DACR); + else + error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, + M98095_DAI2M_TO_DACL | + M98095_DAI2M_TO_DACR); + + error |= max98095_i2c_write(M98095_092_PWR_EN_OUT, + M98095_SPK_SPREADSPECTRUM); + error |= max98095_i2c_write(M98095_04E_CFG_HP, M98095_HPNORMAL); + if (aif_id == AIF1) + error |= max98095_i2c_write(M98095_02C_DAI1_IOCFG, + M98095_S1NORMAL | M98095_SDATA); + else + error |= max98095_i2c_write(M98095_036_DAI2_IOCFG, + M98095_S2NORMAL | M98095_SDATA); + + /* take the codec out of the shut down */ + error |= max98095_update_bits(M98095_097_PWR_SYS, M98095_SHDNRUN, + M98095_SHDNRUN); + /* route DACL and DACR output to HO and Spekers */ + error |= max98095_i2c_write(M98095_050_MIX_SPK_LEFT, 0x01); /* DACL */ + error |= max98095_i2c_write(M98095_051_MIX_SPK_RIGHT, 0x01);/* DACR */ + error |= max98095_i2c_write(M98095_04C_MIX_HP_LEFT, 0x01); /* DACL */ + error |= max98095_i2c_write(M98095_04D_MIX_HP_RIGHT, 0x01); /* DACR */ + + /* power Enable */ + error |= max98095_i2c_write(M98095_091_PWR_EN_OUT, 0xF3); + + /* set Volume */ + error |= max98095_i2c_write(M98095_064_LVL_HP_L, 15); + error |= max98095_i2c_write(M98095_065_LVL_HP_R, 15); + error |= max98095_i2c_write(M98095_067_LVL_SPK_L, 16); + error |= max98095_i2c_write(M98095_068_LVL_SPK_R, 16); + + /* Enable DAIs */ + error |= max98095_i2c_write(M98095_093_BIAS_CTRL, 0x30); + if (aif_id == AIF1) + error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x01); + else + error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x07); + +err_access: + if (error < 0) + return -1; + + return 0; +} + +static int max98095_do_init(struct sound_codec_info *pcodec_info, + enum en_max_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample) +{ + int ret = 0; + + /* Enable codec clock */ + set_xclkout(); + + /* shift the device address by 1 for 7 bit addressing */ + g_max98095_i2c_dev_addr = pcodec_info->i2c_dev_addr >> 1; + + if (pcodec_info->codec_type == CODEC_MAX_98095) { + g_max98095_info.devtype = MAX98095; + } else { + debug("%s: Codec id [%d] not defined\n", __func__, + pcodec_info->codec_type); + return -1; + } + + ret = max98095_device_init(&g_max98095_info, aif_id); + if (ret < 0) { + debug("%s: max98095 codec chip init failed\n", __func__); + return ret; + } + + ret = max98095_set_sysclk(&g_max98095_info, mclk_freq); + if (ret < 0) { + debug("%s: max98095 codec set sys clock failed\n", __func__); + return ret; + } + + ret = max98095_hw_params(&g_max98095_info, aif_id, sampling_rate, + bits_per_sample); + + if (ret == 0) { + ret = max98095_set_fmt(&g_max98095_info, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + aif_id); + } + + return ret; +} + +static int get_max98095_codec_values(struct sound_codec_info *pcodec_info, + const void *blob) +{ + int error = 0; +#ifdef CONFIG_OF_CONTROL + enum fdt_compat_id compat; + int node; + int parent; + + /* Get the node from FDT for codec */ + node = fdtdec_next_compatible(blob, 0, COMPAT_MAXIM_98095_CODEC); + if (node <= 0) { + debug("EXYNOS_SOUND: No node for codec in device tree\n"); + debug("node = %d\n", node); + return -1; + } + + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + compat = fdtdec_lookup(blob, parent); + switch (compat) { + case COMPAT_SAMSUNG_S3C2440_I2C: + pcodec_info->i2c_bus = i2c_get_bus_num_fdt(parent); + error |= pcodec_info->i2c_bus; + debug("i2c bus = %d\n", pcodec_info->i2c_bus); + pcodec_info->i2c_dev_addr = fdtdec_get_int(blob, node, + "reg", 0); + error |= pcodec_info->i2c_dev_addr; + debug("i2c dev addr = %x\n", pcodec_info->i2c_dev_addr); + break; + default: + debug("%s: Unknown compat id %d\n", __func__, compat); + return -1; + } +#else + pcodec_info->i2c_bus = AUDIO_I2C_BUS; + pcodec_info->i2c_dev_addr = AUDIO_I2C_REG; + debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr); +#endif + pcodec_info->codec_type = CODEC_MAX_98095; + if (error == -1) { + debug("fail to get max98095 codec node properties\n"); + return -1; + } + + return 0; +} + +/* max98095 Device Initialisation */ +int max98095_init(const void *blob, enum en_max_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample) +{ + int ret; + int old_bus = i2c_get_bus_num(); + struct sound_codec_info *pcodec_info = &g_codec_info; + + if (get_max98095_codec_values(pcodec_info, blob) < 0) { + debug("FDT Codec values failed\n"); + return -1; + } + + i2c_set_bus_num(pcodec_info->i2c_bus); + ret = max98095_do_init(pcodec_info, aif_id, sampling_rate, mclk_freq, + bits_per_sample); + i2c_set_bus_num(old_bus); + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/sound/max98095.h b/qemu/roms/u-boot/drivers/sound/max98095.h new file mode 100644 index 000000000..44b1e3a97 --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/max98095.h @@ -0,0 +1,317 @@ +/* + * max98095.h -- MAX98095 ALSA SoC Audio driver + * + * Copyright 2011 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98095_H +#define _MAX98095_H + +/* Available audio interface ports in wm8994 codec */ +enum en_max_audio_interface { + AIF1 = 1, + AIF2, +}; + +/* + * MAX98095 Registers Definition + */ + +#define M98095_000_HOST_DATA 0x00 +#define M98095_001_HOST_INT_STS 0x01 +#define M98095_002_HOST_RSP_STS 0x02 +#define M98095_003_HOST_CMD_STS 0x03 +#define M98095_004_CODEC_STS 0x04 +#define M98095_005_DAI1_ALC_STS 0x05 +#define M98095_006_DAI2_ALC_STS 0x06 +#define M98095_007_JACK_AUTO_STS 0x07 +#define M98095_008_JACK_MANUAL_STS 0x08 +#define M98095_009_JACK_VBAT_STS 0x09 +#define M98095_00A_ACC_ADC_STS 0x0A +#define M98095_00B_MIC_NG_AGC_STS 0x0B +#define M98095_00C_SPK_L_VOLT_STS 0x0C +#define M98095_00D_SPK_R_VOLT_STS 0x0D +#define M98095_00E_TEMP_SENSOR_STS 0x0E +#define M98095_00F_HOST_CFG 0x0F +#define M98095_010_HOST_INT_CFG 0x10 +#define M98095_011_HOST_INT_EN 0x11 +#define M98095_012_CODEC_INT_EN 0x12 +#define M98095_013_JACK_INT_EN 0x13 +#define M98095_014_JACK_INT_EN 0x14 +#define M98095_015_DEC 0x15 +#define M98095_016_RESERVED 0x16 +#define M98095_017_RESERVED 0x17 +#define M98095_018_KEYCODE3 0x18 +#define M98095_019_KEYCODE2 0x19 +#define M98095_01A_KEYCODE1 0x1A +#define M98095_01B_KEYCODE0 0x1B +#define M98095_01C_OEMCODE1 0x1C +#define M98095_01D_OEMCODE0 0x1D +#define M98095_01E_XCFG1 0x1E +#define M98095_01F_XCFG2 0x1F +#define M98095_020_XCFG3 0x20 +#define M98095_021_XCFG4 0x21 +#define M98095_022_XCFG5 0x22 +#define M98095_023_XCFG6 0x23 +#define M98095_024_XGPIO 0x24 +#define M98095_025_XCLKCFG 0x25 +#define M98095_026_SYS_CLK 0x26 +#define M98095_027_DAI1_CLKMODE 0x27 +#define M98095_028_DAI1_CLKCFG_HI 0x28 +#define M98095_029_DAI1_CLKCFG_LO 0x29 +#define M98095_02A_DAI1_FORMAT 0x2A +#define M98095_02B_DAI1_CLOCK 0x2B +#define M98095_02C_DAI1_IOCFG 0x2C +#define M98095_02D_DAI1_TDM 0x2D +#define M98095_02E_DAI1_FILTERS 0x2E +#define M98095_02F_DAI1_LVL1 0x2F +#define M98095_030_DAI1_LVL2 0x30 +#define M98095_031_DAI2_CLKMODE 0x31 +#define M98095_032_DAI2_CLKCFG_HI 0x32 +#define M98095_033_DAI2_CLKCFG_LO 0x33 +#define M98095_034_DAI2_FORMAT 0x34 +#define M98095_035_DAI2_CLOCK 0x35 +#define M98095_036_DAI2_IOCFG 0x36 +#define M98095_037_DAI2_TDM 0x37 +#define M98095_038_DAI2_FILTERS 0x38 +#define M98095_039_DAI2_LVL1 0x39 +#define M98095_03A_DAI2_LVL2 0x3A +#define M98095_03B_DAI3_CLKMODE 0x3B +#define M98095_03C_DAI3_CLKCFG_HI 0x3C +#define M98095_03D_DAI3_CLKCFG_LO 0x3D +#define M98095_03E_DAI3_FORMAT 0x3E +#define M98095_03F_DAI3_CLOCK 0x3F +#define M98095_040_DAI3_IOCFG 0x40 +#define M98095_041_DAI3_TDM 0x41 +#define M98095_042_DAI3_FILTERS 0x42 +#define M98095_043_DAI3_LVL1 0x43 +#define M98095_044_DAI3_LVL2 0x44 +#define M98095_045_CFG_DSP 0x45 +#define M98095_046_DAC_CTRL1 0x46 +#define M98095_047_DAC_CTRL2 0x47 +#define M98095_048_MIX_DAC_LR 0x48 +#define M98095_049_MIX_DAC_M 0x49 +#define M98095_04A_MIX_ADC_LEFT 0x4A +#define M98095_04B_MIX_ADC_RIGHT 0x4B +#define M98095_04C_MIX_HP_LEFT 0x4C +#define M98095_04D_MIX_HP_RIGHT 0x4D +#define M98095_04E_CFG_HP 0x4E +#define M98095_04F_MIX_RCV 0x4F +#define M98095_050_MIX_SPK_LEFT 0x50 +#define M98095_051_MIX_SPK_RIGHT 0x51 +#define M98095_052_MIX_SPK_CFG 0x52 +#define M98095_053_MIX_LINEOUT1 0x53 +#define M98095_054_MIX_LINEOUT2 0x54 +#define M98095_055_MIX_LINEOUT_CFG 0x55 +#define M98095_056_LVL_SIDETONE_DAI12 0x56 +#define M98095_057_LVL_SIDETONE_DAI3 0x57 +#define M98095_058_LVL_DAI1_PLAY 0x58 +#define M98095_059_LVL_DAI1_EQ 0x59 +#define M98095_05A_LVL_DAI2_PLAY 0x5A +#define M98095_05B_LVL_DAI2_EQ 0x5B +#define M98095_05C_LVL_DAI3_PLAY 0x5C +#define M98095_05D_LVL_ADC_L 0x5D +#define M98095_05E_LVL_ADC_R 0x5E +#define M98095_05F_LVL_MIC1 0x5F +#define M98095_060_LVL_MIC2 0x60 +#define M98095_061_LVL_LINEIN 0x61 +#define M98095_062_LVL_LINEOUT1 0x62 +#define M98095_063_LVL_LINEOUT2 0x63 +#define M98095_064_LVL_HP_L 0x64 +#define M98095_065_LVL_HP_R 0x65 +#define M98095_066_LVL_RCV 0x66 +#define M98095_067_LVL_SPK_L 0x67 +#define M98095_068_LVL_SPK_R 0x68 +#define M98095_069_MICAGC_CFG 0x69 +#define M98095_06A_MICAGC_THRESH 0x6A +#define M98095_06B_SPK_NOISEGATE 0x6B +#define M98095_06C_DAI1_ALC1_TIME 0x6C +#define M98095_06D_DAI1_ALC1_COMP 0x6D +#define M98095_06E_DAI1_ALC1_EXPN 0x6E +#define M98095_06F_DAI1_ALC1_GAIN 0x6F +#define M98095_070_DAI1_ALC2_TIME 0x70 +#define M98095_071_DAI1_ALC2_COMP 0x71 +#define M98095_072_DAI1_ALC2_EXPN 0x72 +#define M98095_073_DAI1_ALC2_GAIN 0x73 +#define M98095_074_DAI1_ALC3_TIME 0x74 +#define M98095_075_DAI1_ALC3_COMP 0x75 +#define M98095_076_DAI1_ALC3_EXPN 0x76 +#define M98095_077_DAI1_ALC3_GAIN 0x77 +#define M98095_078_DAI2_ALC1_TIME 0x78 +#define M98095_079_DAI2_ALC1_COMP 0x79 +#define M98095_07A_DAI2_ALC1_EXPN 0x7A +#define M98095_07B_DAI2_ALC1_GAIN 0x7B +#define M98095_07C_DAI2_ALC2_TIME 0x7C +#define M98095_07D_DAI2_ALC2_COMP 0x7D +#define M98095_07E_DAI2_ALC2_EXPN 0x7E +#define M98095_07F_DAI2_ALC2_GAIN 0x7F +#define M98095_080_DAI2_ALC3_TIME 0x80 +#define M98095_081_DAI2_ALC3_COMP 0x81 +#define M98095_082_DAI2_ALC3_EXPN 0x82 +#define M98095_083_DAI2_ALC3_GAIN 0x83 +#define M98095_084_HP_NOISE_GATE 0x84 +#define M98095_085_AUX_ADC 0x85 +#define M98095_086_CFG_LINE 0x86 +#define M98095_087_CFG_MIC 0x87 +#define M98095_088_CFG_LEVEL 0x88 +#define M98095_089_JACK_DET_AUTO 0x89 +#define M98095_08A_JACK_DET_MANUAL 0x8A +#define M98095_08B_JACK_KEYSCAN_DBC 0x8B +#define M98095_08C_JACK_KEYSCAN_DLY 0x8C +#define M98095_08D_JACK_KEY_THRESH 0x8D +#define M98095_08E_JACK_DC_SLEW 0x8E +#define M98095_08F_JACK_TEST_CFG 0x8F +#define M98095_090_PWR_EN_IN 0x90 +#define M98095_091_PWR_EN_OUT 0x91 +#define M98095_092_PWR_EN_OUT 0x92 +#define M98095_093_BIAS_CTRL 0x93 +#define M98095_094_PWR_DAC_21 0x94 +#define M98095_095_PWR_DAC_03 0x95 +#define M98095_096_PWR_DAC_CK 0x96 +#define M98095_097_PWR_SYS 0x97 + +#define M98095_0FF_REV_ID 0xFF + +#define M98095_REG_CNT (0xFF+1) +#define M98095_REG_MAX_CACHED 0X97 + +/* MAX98095 Registers Bit Fields */ + +/* M98095_00F_HOST_CFG */ +#define M98095_SEG (1<<0) +#define M98095_XTEN (1<<1) +#define M98095_MDLLEN (1<<2) + +/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */ +#define M98095_CLKMODE_MASK 0xFF + +/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */ +#define M98095_DAI_MAS (1<<7) +#define M98095_DAI_WCI (1<<6) +#define M98095_DAI_BCI (1<<5) +#define M98095_DAI_DLY (1<<4) +#define M98095_DAI_TDM (1<<2) +#define M98095_DAI_FSW (1<<1) +#define M98095_DAI_WS (1<<0) + +/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */ +#define M98095_DAI_BSEL64 (1<<0) +#define M98095_DAI_DOSR_DIV2 (0<<5) +#define M98095_DAI_DOSR_DIV4 (1<<5) + +/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */ +#define M98095_S1NORMAL (1<<6) +#define M98095_S2NORMAL (2<<6) +#define M98095_S3NORMAL (3<<6) +#define M98095_SDATA (3<<0) + +/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */ +#define M98095_DAI_DHF (1<<3) + +/* M98095_045_DSP_CFG */ +#define M98095_DSPNORMAL (5<<4) + +/* M98095_048_MIX_DAC_LR */ +#define M98095_DAI1L_TO_DACR (1<<7) +#define M98095_DAI1R_TO_DACR (1<<6) +#define M98095_DAI2M_TO_DACR (1<<5) +#define M98095_DAI1L_TO_DACL (1<<3) +#define M98095_DAI1R_TO_DACL (1<<2) +#define M98095_DAI2M_TO_DACL (1<<1) +#define M98095_DAI3M_TO_DACL (1<<0) + +/* M98095_049_MIX_DAC_M */ +#define M98095_DAI1L_TO_DACM (1<<3) +#define M98095_DAI1R_TO_DACM (1<<2) +#define M98095_DAI2M_TO_DACM (1<<1) +#define M98095_DAI3M_TO_DACM (1<<0) + +/* M98095_04E_MIX_HP_CFG */ +#define M98095_HPNORMAL (3<<4) + +/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */ +#define M98095_MICPRE_MASK (3<<5) +#define M98095_MICPRE_SHIFT 5 + +/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */ +#define M98095_HP_MUTE (1<<7) + +/* M98095_066_LVL_RCV */ +#define M98095_REC_MUTE (1<<7) + +/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */ +#define M98095_SP_MUTE (1<<7) + +/* M98095_087_CFG_MIC */ +#define M98095_MICSEL_MASK (3<<0) +#define M98095_DIGMIC_L (1<<2) +#define M98095_DIGMIC_R (1<<3) +#define M98095_DIGMIC2L (1<<4) +#define M98095_DIGMIC2R (1<<5) + +/* M98095_088_CFG_LEVEL */ +#define M98095_VSEN (1<<6) +#define M98095_ZDEN (1<<5) +#define M98095_BQ2EN (1<<3) +#define M98095_BQ1EN (1<<2) +#define M98095_EQ2EN (1<<1) +#define M98095_EQ1EN (1<<0) + +/* M98095_090_PWR_EN_IN */ +#define M98095_INEN (1<<7) +#define M98095_MB2EN (1<<3) +#define M98095_MB1EN (1<<2) +#define M98095_MBEN (3<<2) +#define M98095_ADREN (1<<1) +#define M98095_ADLEN (1<<0) + +/* M98095_091_PWR_EN_OUT */ +#define M98095_HPLEN (1<<7) +#define M98095_HPREN (1<<6) +#define M98095_SPLEN (1<<5) +#define M98095_SPREN (1<<4) +#define M98095_RECEN (1<<3) +#define M98095_DALEN (1<<1) +#define M98095_DAREN (1<<0) + +/* M98095_092_PWR_EN_OUT */ +#define M98095_SPK_FIXEDSPECTRUM (0<<4) +#define M98095_SPK_SPREADSPECTRUM (1<<4) + +/* M98095_097_PWR_SYS */ +#define M98095_SHDNRUN (1<<7) +#define M98095_PERFMODE (1<<3) +#define M98095_HPPLYBACK (1<<2) +#define M98095_PWRSV8K (1<<1) +#define M98095_PWRSV (1<<0) + +#define M98095_COEFS_PER_BAND 5 + +/* Equalizer filter coefficients */ +#define M98095_110_DAI1_EQ_BASE 0x10 +#define M98095_142_DAI2_EQ_BASE 0x42 + +/* Biquad filter coefficients */ +#define M98095_174_DAI1_BQ_BASE 0x74 +#define M98095_17E_DAI2_BQ_BASE 0x7E + +/* function prototype */ + +/* + * intialise max98095 sound codec device for the given configuration + * + * @param blob FDT node for codec values + * @param sampling_rate Sampling rate (Hz) + * @param mclk_freq MCLK Frequency (Hz) + * @param bits_per_sample bits per Sample (must be 16 or 24) + * + * @returns -1 for error and 0 Success. + */ +int max98095_init(const void *blob, enum en_max_audio_interface aif_id, + int sampling_rate, int mclk_freq, int bits_per_sample); + +#endif diff --git a/qemu/roms/u-boot/drivers/sound/samsung-i2s.c b/qemu/roms/u-boot/drivers/sound/samsung-i2s.c new file mode 100644 index 000000000..47f155f85 --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/samsung-i2s.c @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chandrasekar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) +#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) +#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf) +#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf) +#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f) + +#define TIMEOUT_I2S_TX 100 /* i2s transfer timeout */ + +/* + * Sets the frame size for I2S LR clock + * + * @param i2s_reg i2s regiter address + * @param rfs Frame Size + */ +static void i2s_set_lr_framesize(struct i2s_reg *i2s_reg, unsigned int rfs) +{ + unsigned int mod = readl(&i2s_reg->mod); + + mod &= ~MOD_RCLK_MASK; + + switch (rfs) { + case 768: + mod |= MOD_RCLK_768FS; + break; + case 512: + mod |= MOD_RCLK_512FS; + break; + case 384: + mod |= MOD_RCLK_384FS; + break; + default: + mod |= MOD_RCLK_256FS; + break; + } + + writel(mod, &i2s_reg->mod); +} + +/* + * Sets the i2s transfer control + * + * @param i2s_reg i2s regiter address + * @param on 1 enable tx , 0 disable tx transfer + */ +static void i2s_txctrl(struct i2s_reg *i2s_reg, int on) +{ + unsigned int con = readl(&i2s_reg->con); + unsigned int mod = readl(&i2s_reg->mod) & ~MOD_MASK; + + if (on) { + con |= CON_ACTIVE; + con &= ~CON_TXCH_PAUSE; + } else { + con |= CON_TXCH_PAUSE; + con &= ~CON_ACTIVE; + } + + writel(mod, &i2s_reg->mod); + writel(con, &i2s_reg->con); +} + +/* + * set the bit clock frame size (in multiples of LRCLK) + * + * @param i2s_reg i2s regiter address + * @param bfs bit Frame Size + */ +static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs) +{ + unsigned int mod = readl(&i2s_reg->mod); + + mod &= ~MOD_BCLK_MASK; + + switch (bfs) { + case 48: + mod |= MOD_BCLK_48FS; + break; + case 32: + mod |= MOD_BCLK_32FS; + break; + case 24: + mod |= MOD_BCLK_24FS; + break; + case 16: + mod |= MOD_BCLK_16FS; + break; + default: + return; + } + writel(mod, &i2s_reg->mod); +} + +/* + * flushes the i2stx fifo + * + * @param i2s_reg i2s regiter address + * @param flush Tx fifo flush command (0x00 - do not flush + * 0x80 - flush tx fifo) + */ +void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) +{ + /* Flush the FIFO */ + setbits_le32(&i2s_reg->fic, flush); + clrbits_le32(&i2s_reg->fic, flush); +} + +/* + * Set System Clock direction + * + * @param i2s_reg i2s regiter address + * @param dir Clock direction + * + * @return int value 0 for success, -1 in case of error + */ +int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) +{ + unsigned int mod = readl(&i2s_reg->mod); + + if (dir == SND_SOC_CLOCK_IN) + mod |= MOD_CDCLKCON; + else + mod &= ~MOD_CDCLKCON; + + writel(mod, &i2s_reg->mod); + + return 0; +} + +/* + * Sets I2S Clcok format + * + * @param fmt i2s clock properties + * @param i2s_reg i2s regiter address + * + * @return int value 0 for success, -1 in case of error + */ +int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) +{ + unsigned int mod = readl(&i2s_reg->mod); + unsigned int tmp = 0; + unsigned int ret = 0; + + /* Format is priority */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + tmp |= MOD_LR_RLOW; + tmp |= MOD_SDF_MSB; + break; + case SND_SOC_DAIFMT_LEFT_J: + tmp |= MOD_LR_RLOW; + tmp |= MOD_SDF_LSB; + break; + case SND_SOC_DAIFMT_I2S: + tmp |= MOD_SDF_IIS; + break; + default: + debug("%s: Invalid format priority [0x%x]\n", __func__, + (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); + return -1; + } + + /* + * INV flag is relative to the FORMAT flag - if set it simply + * flips the polarity specified by the Standard + */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + if (tmp & MOD_LR_RLOW) + tmp &= ~MOD_LR_RLOW; + else + tmp |= MOD_LR_RLOW; + break; + default: + debug("%s: Invalid clock ploarity input [0x%x]\n", __func__, + (fmt & SND_SOC_DAIFMT_INV_MASK)); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + tmp |= MOD_SLAVE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set default source clock in Master mode */ + ret = i2s_set_sysclk_dir(i2s_reg, SND_SOC_CLOCK_OUT); + if (ret != 0) { + debug("%s:set i2s clock direction failed\n", __func__); + return -1; + } + break; + default: + debug("%s: Invalid master selection [0x%x]\n", __func__, + (fmt & SND_SOC_DAIFMT_MASTER_MASK)); + return -1; + } + + mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE); + mod |= tmp; + writel(mod, &i2s_reg->mod); + + return 0; +} + +/* + * Sets the sample width in bits + * + * @param blc samplewidth (size of sample in bits) + * @param i2s_reg i2s regiter address + * + * @return int value 0 for success, -1 in case of error + */ +int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) +{ + unsigned int mod = readl(&i2s_reg->mod); + + mod &= ~MOD_BLCP_MASK; + mod &= ~MOD_BLC_MASK; + + switch (blc) { + case 8: + mod |= MOD_BLCP_8BIT; + mod |= MOD_BLC_8BIT; + break; + case 16: + mod |= MOD_BLCP_16BIT; + mod |= MOD_BLC_16BIT; + break; + case 24: + mod |= MOD_BLCP_24BIT; + mod |= MOD_BLC_24BIT; + break; + default: + debug("%s: Invalid sample size input [0x%x]\n", + __func__, blc); + return -1; + } + writel(mod, &i2s_reg->mod); + + return 0; +} + +int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned int *data, + unsigned long data_size) +{ + int i; + int start; + struct i2s_reg *i2s_reg = + (struct i2s_reg *)pi2s_tx->base_address; + + if (data_size < FIFO_LENGTH) { + debug("%s : Invalid data size\n", __func__); + return -1; /* invalid pcm data size */ + } + + /* fill the tx buffer before stating the tx transmit */ + for (i = 0; i < FIFO_LENGTH; i++) + writel(*data++, &i2s_reg->txd); + + data_size -= FIFO_LENGTH; + i2s_txctrl(i2s_reg, I2S_TX_ON); + + while (data_size > 0) { + start = get_timer(0); + if (!(CON_TXFIFO_FULL & (readl(&i2s_reg->con)))) { + writel(*data++, &i2s_reg->txd); + data_size--; + } else { + if (get_timer(start) > TIMEOUT_I2S_TX) { + i2s_txctrl(i2s_reg, I2S_TX_OFF); + debug("%s: I2S Transfer Timeout\n", __func__); + return -1; + } + } + } + i2s_txctrl(i2s_reg, I2S_TX_OFF); + + return 0; +} + +int i2s_tx_init(struct i2stx_info *pi2s_tx) +{ + int ret; + struct i2s_reg *i2s_reg = + (struct i2s_reg *)pi2s_tx->base_address; + if (pi2s_tx->id == 0) { + /* Initialize GPIO for I2S-0 */ + exynos_pinmux_config(PERIPH_ID_I2S0, 0); + + /* Set EPLL Clock */ + ret = set_epll_clk(pi2s_tx->samplingrate * pi2s_tx->rfs * 4); + } else if (pi2s_tx->id == 1) { + /* Initialize GPIO for I2S-1 */ + exynos_pinmux_config(PERIPH_ID_I2S1, 0); + + /* Set EPLL Clock */ + ret = set_epll_clk(pi2s_tx->audio_pll_clk); + } else { + debug("%s: unsupported i2s-%d bus\n", __func__, pi2s_tx->id); + return -1; + } + + if (ret != 0) { + debug("%s: epll clock set rate failed\n", __func__); + return -1; + } + + /* Select Clk Source for Audio 0 or 1 */ + ret = set_i2s_clk_source(pi2s_tx->id); + if (ret == -1) { + debug("%s: unsupported clock for i2s-%d\n", __func__, + pi2s_tx->id); + return -1; + } + + if (pi2s_tx->id == 0) { + /*Reset the i2s module */ + writel(CON_RESET, &i2s_reg->con); + + writel(MOD_OP_CLK | MOD_RCLKSRC, &i2s_reg->mod); + /* set i2s prescaler */ + writel(PSREN | PSVAL, &i2s_reg->psr); + } else { + /* Set Prescaler to get MCLK */ + ret = set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, + (pi2s_tx->samplingrate * (pi2s_tx->rfs)), + pi2s_tx->id); + } + if (ret == -1) { + debug("%s: unsupported prescalar for i2s-%d\n", __func__, + pi2s_tx->id); + return -1; + } + + /* Configure I2s format */ + ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM)); + if (ret == 0) { + i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs); + ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample); + if (ret != 0) { + debug("%s:set sample rate failed\n", __func__); + return -1; + } + + i2s_set_bitclk_framesize(i2s_reg, pi2s_tx->bfs); + /* disable i2s transfer flag and flush the fifo */ + i2s_txctrl(i2s_reg, I2S_TX_OFF); + i2s_fifo(i2s_reg, FIC_TXFLUSH); + } else { + debug("%s: failed\n", __func__); + } + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/sound/sandbox.c b/qemu/roms/u-boot/drivers/sound/sandbox.c new file mode 100644 index 000000000..fe5c9e9b3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/sandbox.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +int sound_play(uint32_t msec, uint32_t frequency) +{ + sandbox_sdl_sound_start(frequency); + mdelay(msec); + sandbox_sdl_sound_stop(); + + return 0; +} + +int sound_init(const void *blob) +{ + return sandbox_sdl_sound_init(); +} diff --git a/qemu/roms/u-boot/drivers/sound/sound-i2s.c b/qemu/roms/u-boot/drivers/sound/sound-i2s.c new file mode 100644 index 000000000..749bbbd03 --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/sound-i2s.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chandrasekar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wm8994.h" +#include "max98095.h" + +/* defines */ +#define SOUND_400_HZ 400 +#define SOUND_BITS_IN_BYTE 8 + +static struct i2stx_info g_i2stx_pri; + +/* + * get_sound_i2s_values gets values for i2s parameters + * + * @param i2stx_info i2s transmitter transfer param structure + * @param blob FDT blob if enabled else NULL + */ +static int get_sound_i2s_values(struct i2stx_info *i2s, const void *blob) +{ + int node; + int error = 0; + int base; + + node = fdt_path_offset(blob, "i2s"); + if (node <= 0) { + debug("EXYNOS_SOUND: No node for sound in device tree\n"); + return -1; + } + + /* + * Get the pre-defined sound specific values from FDT. + * All of these are expected to be correct otherwise + * wrong register values in i2s setup parameters + * may result in no sound play. + */ + base = fdtdec_get_addr(blob, node, "reg"); + if (base == FDT_ADDR_T_NONE) { + debug("%s: Missing i2s base\n", __func__); + return -1; + } + i2s->base_address = base; + + i2s->audio_pll_clk = fdtdec_get_int(blob, + node, "samsung,i2s-epll-clock-frequency", -1); + error |= i2s->audio_pll_clk; + debug("audio_pll_clk = %d\n", i2s->audio_pll_clk); + i2s->samplingrate = fdtdec_get_int(blob, + node, "samsung,i2s-sampling-rate", -1); + error |= i2s->samplingrate; + debug("samplingrate = %d\n", i2s->samplingrate); + i2s->bitspersample = fdtdec_get_int(blob, + node, "samsung,i2s-bits-per-sample", -1); + error |= i2s->bitspersample; + debug("bitspersample = %d\n", i2s->bitspersample); + i2s->channels = fdtdec_get_int(blob, + node, "samsung,i2s-channels", -1); + error |= i2s->channels; + debug("channels = %d\n", i2s->channels); + i2s->rfs = fdtdec_get_int(blob, + node, "samsung,i2s-lr-clk-framesize", -1); + error |= i2s->rfs; + debug("rfs = %d\n", i2s->rfs); + i2s->bfs = fdtdec_get_int(blob, + node, "samsung,i2s-bit-clk-framesize", -1); + error |= i2s->bfs; + debug("bfs = %d\n", i2s->bfs); + + i2s->id = fdtdec_get_int(blob, node, "samsung,i2s-id", -1); + error |= i2s->id; + debug("id = %d\n", i2s->id); + + if (error == -1) { + debug("fail to get sound i2s node properties\n"); + return -1; + } + + return 0; +} + +/* + * Init codec + * + * @param blob FDT blob + * @param pi2s_tx i2s parameters required by codec + * @return int value, 0 for success + */ +static int codec_init(const void *blob, struct i2stx_info *pi2s_tx) +{ + int ret; + const char *codectype; + int node; + + /* Get the node from FDT for sound */ + node = fdt_path_offset(blob, "i2s"); + if (node <= 0) { + debug("EXYNOS_SOUND: No node for sound in device tree\n"); + debug("node = %d\n", node); + return -1; + } + + /* + * Get the pre-defined sound codec specific values from FDT. + * All of these are expected to be correct otherwise sound + * can not be played + */ + codectype = fdt_getprop(blob, node, "samsung,codec-type", NULL); + debug("device = %s\n", codectype); + if (!strcmp(codectype, "wm8994")) { + /* Check the codec type and initialise the same */ + ret = wm8994_init(blob, pi2s_tx->id + 1, + pi2s_tx->samplingrate, + (pi2s_tx->samplingrate * (pi2s_tx->rfs)), + pi2s_tx->bitspersample, pi2s_tx->channels); + } else if (!strcmp(codectype, "max98095")) { + ret = max98095_init(blob, pi2s_tx->id + 1, + pi2s_tx->samplingrate, + (pi2s_tx->samplingrate * (pi2s_tx->rfs)), + pi2s_tx->bitspersample); + } else { + debug("%s: Unknown codec type %s\n", __func__, codectype); + return -1; + } + + if (ret) { + debug("%s: Codec init failed\n", __func__); + return -1; + } + + return 0; +} + +int sound_init(const void *blob) +{ + int ret; + struct i2stx_info *pi2s_tx = &g_i2stx_pri; + + /* Get the I2S Values */ + if (get_sound_i2s_values(pi2s_tx, blob) < 0) { + debug(" FDT I2S values failed\n"); + return -1; + } + + if (codec_init(blob, pi2s_tx) < 0) { + debug(" Codec init failed\n"); + return -1; + } + + ret = i2s_tx_init(pi2s_tx); + if (ret) { + debug("%s: Failed to init i2c transmit: ret=%d\n", __func__, + ret); + return ret; + } + + + return ret; +} + +int sound_play(uint32_t msec, uint32_t frequency) +{ + unsigned int *data; + unsigned long data_size; + unsigned int ret = 0; + + /*Buffer length computation */ + data_size = g_i2stx_pri.samplingrate * g_i2stx_pri.channels; + data_size *= (g_i2stx_pri.bitspersample / SOUND_BITS_IN_BYTE); + data = malloc(data_size); + + if (data == NULL) { + debug("%s: malloc failed\n", __func__); + return -1; + } + + sound_create_square_wave((unsigned short *)data, + data_size / sizeof(unsigned short), + frequency); + + while (msec >= 1000) { + ret = i2s_transfer_tx_data(&g_i2stx_pri, data, + (data_size / sizeof(int))); + msec -= 1000; + } + if (msec) { + unsigned long size = + (data_size * msec) / (sizeof(int) * 1000); + + ret = i2s_transfer_tx_data(&g_i2stx_pri, data, size); + } + + free(data); + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/sound/sound.c b/qemu/roms/u-boot/drivers/sound/sound.c new file mode 100644 index 000000000..9dda2dba8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/sound.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chandrasekar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +void sound_create_square_wave(unsigned short *data, int size, uint32_t freq) +{ + const int sample = 48000; + const unsigned short amplitude = 16000; /* between 1 and 32767 */ + const int period = freq ? sample / freq : 0; + const int half = period / 2; + + assert(freq); + + /* Make sure we don't overflow our buffer */ + if (size % 2) + size--; + + while (size) { + int i; + for (i = 0; size && i < half; i++) { + size -= 2; + *data++ = amplitude; + *data++ = amplitude; + } + for (i = 0; size && i < period - half; i++) { + size -= 2; + *data++ = -amplitude; + *data++ = -amplitude; + } + } +} diff --git a/qemu/roms/u-boot/drivers/sound/wm8994.c b/qemu/roms/u-boot/drivers/sound/wm8994.c new file mode 100644 index 000000000..f8e9a6ead --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/wm8994.c @@ -0,0 +1,915 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chandrasekar + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wm8994.h" +#include "wm8994_registers.h" + +/* defines for wm8994 system clock selection */ +#define SEL_MCLK1 0x00 +#define SEL_MCLK2 0x08 +#define SEL_FLL1 0x10 +#define SEL_FLL2 0x18 + +/* fll config to configure fll */ +struct wm8994_fll_config { + int src; /* Source */ + int in; /* Input frequency in Hz */ + int out; /* output frequency in Hz */ +}; + +/* codec private data */ +struct wm8994_priv { + enum wm8994_type type; /* codec type of wolfson */ + int revision; /* Revision */ + int sysclk[WM8994_MAX_AIF]; /* System clock frequency in Hz */ + int mclk[WM8994_MAX_AIF]; /* master clock frequency in Hz */ + int aifclk[WM8994_MAX_AIF]; /* audio interface clock in Hz */ + struct wm8994_fll_config fll[2]; /* fll config to configure fll */ +}; + +/* wm 8994 supported sampling rate values */ +static unsigned int src_rate[] = { + 8000, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 88200, 96000 +}; + +/* op clock divisions */ +static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 }; + +/* lr clock frame size ratio */ +static int fs_ratios[] = { + 64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536 +}; + +/* bit clock divisors */ +static int bclk_divs[] = { + 10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480, + 640, 880, 960, 1280, 1760, 1920 +}; + +static struct wm8994_priv g_wm8994_info; +static unsigned char g_wm8994_i2c_dev_addr; +static struct sound_codec_info g_codec_info; + +/* + * Initialise I2C for wm 8994 + * + * @param bus no i2c bus number in which wm8994 is connected + */ +static void wm8994_i2c_init(int bus_no) +{ + i2c_set_bus_num(bus_no); +} + +/* + * Writes value to a device register through i2c + * + * @param reg reg number to be write + * @param data data to be writen to the above registor + * + * @return int value 1 for change, 0 for no change or negative error code. + */ +static int wm8994_i2c_write(unsigned int reg, unsigned short data) +{ + unsigned char val[2]; + + val[0] = (unsigned char)((data >> 8) & 0xff); + val[1] = (unsigned char)(data & 0xff); + debug("Write Addr : 0x%04X, Data : 0x%04X\n", reg, data); + + return i2c_write(g_wm8994_i2c_dev_addr, reg, 2, val, 2); +} + +/* + * Read a value from a device register through i2c + * + * @param reg reg number to be read + * @param data address of read data to be stored + * + * @return int value 0 for success, -1 in case of error. + */ +static unsigned int wm8994_i2c_read(unsigned int reg , unsigned short *data) +{ + unsigned char val[2]; + int ret; + + ret = i2c_read(g_wm8994_i2c_dev_addr, reg, 2, val, 2); + if (ret != 0) { + debug("%s: Error while reading register %#04x\n", + __func__, reg); + return -1; + } + + *data = val[0]; + *data <<= 8; + *data |= val[1]; + + return 0; +} + +/* + * update device register bits through i2c + * + * @param reg codec register + * @param mask register mask + * @param value new value + * + * @return int value 1 if change in the register value, + * 0 for no change or negative error code. + */ +static int wm8994_update_bits(unsigned int reg, unsigned short mask, + unsigned short value) +{ + int change , ret = 0; + unsigned short old, new; + + if (wm8994_i2c_read(reg, &old) != 0) + return -1; + new = (old & ~mask) | (value & mask); + change = (old != new) ? 1 : 0; + if (change) + ret = wm8994_i2c_write(reg, new); + if (ret < 0) + return ret; + + return change; +} + +/* + * Sets i2s set format + * + * @param aif_id Interface ID + * @param fmt i2S format + * + * @return -1 for error and 0 Success. + */ +int wm8994_set_fmt(int aif_id, unsigned int fmt) +{ + int ms_reg; + int aif_reg; + int ms = 0; + int aif = 0; + int aif_clk = 0; + int error = 0; + + switch (aif_id) { + case 1: + ms_reg = WM8994_AIF1_MASTER_SLAVE; + aif_reg = WM8994_AIF1_CONTROL_1; + aif_clk = WM8994_AIF1_CLOCKING_1; + break; + case 2: + ms_reg = WM8994_AIF2_MASTER_SLAVE; + aif_reg = WM8994_AIF2_CONTROL_1; + aif_clk = WM8994_AIF2_CLOCKING_1; + break; + default: + debug("%s: Invalid audio interface selection\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + ms = WM8994_AIF1_MSTR; + break; + default: + debug("%s: Invalid i2s master selection\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif |= WM8994_AIF1_LRCLK_INV; + case SND_SOC_DAIFMT_DSP_A: + aif |= 0x18; + break; + case SND_SOC_DAIFMT_I2S: + aif |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif |= 0x8; + break; + default: + debug("%s: Invalid i2s format selection\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8994_AIF1_BCLK_INV; + break; + default: + debug("%s: Invalid i2s frame inverse selection\n", + __func__); + return -1; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8994_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif |= WM8994_AIF1_LRCLK_INV; + break; + default: + debug("%s: Invalid i2s clock polarity selection\n", + __func__); + return -1; + } + break; + default: + debug("%s: Invalid i2s format selection\n", __func__); + return -1; + } + + error = wm8994_update_bits(aif_reg, WM8994_AIF1_BCLK_INV | + WM8994_AIF1_LRCLK_INV_MASK | WM8994_AIF1_FMT_MASK, aif); + + error |= wm8994_update_bits(ms_reg, WM8994_AIF1_MSTR_MASK, ms); + error |= wm8994_update_bits(aif_clk, WM8994_AIF1CLK_ENA_MASK, + WM8994_AIF1CLK_ENA); + if (error < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Sets hw params FOR WM8994 + * + * @param wm8994 wm8994 information pointer + * @param aif_id Audio interface ID + * @param sampling_rate Sampling rate + * @param bits_per_sample Bits per sample + * @param Channels Channels in the given audio input + * + * @return -1 for error and 0 Success. + */ +static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, + unsigned int sampling_rate, unsigned int bits_per_sample, + unsigned int channels) +{ + int aif1_reg; + int aif2_reg; + int bclk_reg; + int bclk = 0; + int rate_reg; + int aif1 = 0; + int aif2 = 0; + int rate_val = 0; + int id = aif_id - 1; + int i, cur_val, best_val, bclk_rate, best; + unsigned short reg_data; + int ret = 0; + + switch (aif_id) { + case 1: + aif1_reg = WM8994_AIF1_CONTROL_1; + aif2_reg = WM8994_AIF1_CONTROL_2; + bclk_reg = WM8994_AIF1_BCLK; + rate_reg = WM8994_AIF1_RATE; + break; + case 2: + aif1_reg = WM8994_AIF2_CONTROL_1; + aif2_reg = WM8994_AIF2_CONTROL_2; + bclk_reg = WM8994_AIF2_BCLK; + rate_reg = WM8994_AIF2_RATE; + break; + default: + return -1; + } + + bclk_rate = sampling_rate * 32; + switch (bits_per_sample) { + case 16: + bclk_rate *= 16; + break; + case 20: + bclk_rate *= 20; + aif1 |= 0x20; + break; + case 24: + bclk_rate *= 24; + aif1 |= 0x40; + break; + case 32: + bclk_rate *= 32; + aif1 |= 0x60; + break; + default: + return -1; + } + + /* Try to find an appropriate sample rate; look for an exact match. */ + for (i = 0; i < ARRAY_SIZE(src_rate); i++) + if (src_rate[i] == sampling_rate) + break; + + if (i == ARRAY_SIZE(src_rate)) { + debug("%s: Could not get the best matching samplingrate\n", + __func__); + return -1; + } + + rate_val |= i << WM8994_AIF1_SR_SHIFT; + + /* AIFCLK/fs ratio; look for a close match in either direction */ + best = 0; + best_val = abs((fs_ratios[0] * sampling_rate) + - wm8994->aifclk[id]); + + for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) { + cur_val = abs((fs_ratios[i] * sampling_rate) + - wm8994->aifclk[id]); + if (cur_val >= best_val) + continue; + best = i; + best_val = cur_val; + } + + rate_val |= best; + + /* + * We may not get quite the right frequency if using + * approximate clocks so look for the closest match that is + * higher than the target (we need to ensure that there enough + * BCLKs to clock out the samples). + */ + best = 0; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate; + if (cur_val < 0) /* BCLK table is sorted */ + break; + best = i; + } + + if (i == ARRAY_SIZE(bclk_divs)) { + debug("%s: Could not get the best matching bclk division\n", + __func__); + return -1; + } + + bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best]; + bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT; + + if (wm8994_i2c_read(aif1_reg, ®_data) != 0) { + debug("%s: AIF1 register read Failed\n", __func__); + return -1; + } + + if ((channels == 1) && ((reg_data & 0x18) == 0x18)) + aif2 |= WM8994_AIF1_MONO; + + if (wm8994->aifclk[id] == 0) { + debug("%s:Audio interface clock not set\n", __func__); + return -1; + } + + ret = wm8994_update_bits(aif1_reg, WM8994_AIF1_WL_MASK, aif1); + ret |= wm8994_update_bits(aif2_reg, WM8994_AIF1_MONO, aif2); + ret |= wm8994_update_bits(bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk); + ret |= wm8994_update_bits(rate_reg, WM8994_AIF1_SR_MASK | + WM8994_AIF1CLK_RATE_MASK, rate_val); + + debug("rate vale = %x , bclk val= %x\n", rate_val, bclk); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Configures Audio interface Clock + * + * @param wm8994 wm8994 information pointer + * @param aif Audio Interface ID + * + * @return -1 for error and 0 Success. + */ +static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) +{ + int rate; + int reg1 = 0; + int offset; + int ret; + + /* AIF(1/0) register adress offset calculated */ + if (aif-1) + offset = 4; + else + offset = 0; + + switch (wm8994->sysclk[aif-1]) { + case WM8994_SYSCLK_MCLK1: + reg1 |= SEL_MCLK1; + rate = wm8994->mclk[0]; + break; + + case WM8994_SYSCLK_MCLK2: + reg1 |= SEL_MCLK2; + rate = wm8994->mclk[1]; + break; + + case WM8994_SYSCLK_FLL1: + reg1 |= SEL_FLL1; + rate = wm8994->fll[0].out; + break; + + case WM8994_SYSCLK_FLL2: + reg1 |= SEL_FLL2; + rate = wm8994->fll[1].out; + break; + + default: + debug("%s: Invalid input clock selection [%d]\n", + __func__, wm8994->sysclk[aif-1]); + return -1; + } + + /* if input clock frequenct is more than 135Mhz then divide */ + if (rate >= WM8994_MAX_INPUT_CLK_FREQ) { + rate /= 2; + reg1 |= WM8994_AIF1CLK_DIV; + } + + wm8994->aifclk[aif-1] = rate; + + ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_1 + offset, + WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, + reg1); + + if (aif == WM8994_AIF1) + ret |= wm8994_update_bits(WM8994_CLOCKING_1, + WM8994_AIF1DSPCLK_ENA_MASK | WM8994_SYSDSPCLK_ENA_MASK, + WM8994_AIF1DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); + else if (aif == WM8994_AIF2) + ret |= wm8994_update_bits(WM8994_CLOCKING_1, + WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA_MASK | + WM8994_SYSDSPCLK_ENA_MASK, WM8994_SYSCLK_SRC | + WM8994_AIF2DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Configures Audio interface for the given frequency + * + * @param wm8994 wm8994 information + * @param aif_id Audio Interface + * @param clk_id Input Clock ID + * @param freq Sampling frequency in Hz + * + * @return -1 for error and 0 success. + */ +static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, + int clk_id, unsigned int freq) +{ + int i; + int ret = 0; + + wm8994->sysclk[aif_id - 1] = clk_id; + + switch (clk_id) { + case WM8994_SYSCLK_MCLK1: + wm8994->mclk[0] = freq; + if (aif_id == 2) { + ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_2 , + WM8994_AIF2DAC_DIV_MASK , 0); + } + break; + + case WM8994_SYSCLK_MCLK2: + /* TODO: Set GPIO AF */ + wm8994->mclk[1] = freq; + break; + + case WM8994_SYSCLK_FLL1: + case WM8994_SYSCLK_FLL2: + break; + + case WM8994_SYSCLK_OPCLK: + /* + * Special case - a division (times 10) is given and + * no effect on main clocking. + */ + if (freq) { + for (i = 0; i < ARRAY_SIZE(opclk_divs); i++) + if (opclk_divs[i] == freq) + break; + if (i == ARRAY_SIZE(opclk_divs)) { + debug("%s frequency divisor not found\n", + __func__); + return -1; + } + ret = wm8994_update_bits(WM8994_CLOCKING_2, + WM8994_OPCLK_DIV_MASK, i); + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, WM8994_OPCLK_ENA); + } else { + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, 0); + } + + default: + debug("%s Invalid input clock selection [%d]\n", + __func__, clk_id); + return -1; + } + + ret |= configure_aif_clock(wm8994, aif_id); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Initializes Volume for AIF2 to HP path + * + * @returns -1 for error and 0 Success. + * + */ +static int wm8994_init_volume_aif2_dac1(void) +{ + int ret; + + /* Unmute AIF2DAC */ + ret = wm8994_update_bits(WM8994_AIF2_DAC_FILTERS_1, + WM8994_AIF2DAC_MUTE_MASK, 0); + + + ret |= wm8994_update_bits(WM8994_AIF2_DAC_LEFT_VOLUME, + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACL_VOL_MASK, + WM8994_AIF2DAC_VU | 0xff); + + ret |= wm8994_update_bits(WM8994_AIF2_DAC_RIGHT_VOLUME, + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACR_VOL_MASK, + WM8994_AIF2DAC_VU | 0xff); + + + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | + WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | + WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + /* Head Phone Volume */ + ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D); + ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Initializes Volume for AIF1 to HP path + * + * @returns -1 for error and 0 Success. + * + */ +static int wm8994_init_volume_aif1_dac1(void) +{ + int ret = 0; + + /* Unmute AIF1DAC */ + ret |= wm8994_i2c_write(WM8994_AIF1_DAC_FILTERS_1, 0x0000); + + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | + WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | + WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + /* Head Phone Volume */ + ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D); + ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Intialise wm8994 codec device + * + * @param wm8994 wm8994 information + * + * @returns -1 for error and 0 Success. + */ +static int wm8994_device_init(struct wm8994_priv *wm8994, + enum en_audio_interface aif_id) +{ + const char *devname; + unsigned short reg_data; + int ret; + + wm8994_i2c_write(WM8994_SOFTWARE_RESET, WM8994_SW_RESET);/* Reset */ + + ret = wm8994_i2c_read(WM8994_SOFTWARE_RESET, ®_data); + if (ret < 0) { + debug("Failed to read ID register\n"); + goto err; + } + + if (reg_data == WM8994_ID) { + devname = "WM8994"; + debug("Device registered as type %d\n", wm8994->type); + wm8994->type = WM8994; + } else { + debug("Device is not a WM8994, ID is %x\n", ret); + ret = -1; + goto err; + } + + ret = wm8994_i2c_read(WM8994_CHIP_REVISION, ®_data); + if (ret < 0) { + debug("Failed to read revision register: %d\n", ret); + goto err; + } + wm8994->revision = reg_data; + debug("%s revision %c\n", devname, 'A' + wm8994->revision); + + /* VMID Selection */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, + WM8994_VMID_SEL_MASK | WM8994_BIAS_ENA_MASK, 0x3); + + /* Charge Pump Enable */ + ret |= wm8994_update_bits(WM8994_CHARGE_PUMP_1, WM8994_CP_ENA_MASK, + WM8994_CP_ENA); + + /* Head Phone Power Enable */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, + WM8994_HPOUT1L_ENA_MASK, WM8994_HPOUT1L_ENA); + + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, + WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA); + + if (aif_id == WM8994_AIF1) { + ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_2, + WM8994_TSHUT_ENA | WM8994_MIXINL_ENA | + WM8994_MIXINR_ENA | WM8994_IN2L_ENA | + WM8994_IN2R_ENA); + + ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_4, + WM8994_ADCL_ENA | WM8994_ADCR_ENA | + WM8994_AIF1ADC1R_ENA | + WM8994_AIF1ADC1L_ENA); + + /* Power enable for AIF1 and DAC1 */ + ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_5, + WM8994_AIF1DACL_ENA | + WM8994_AIF1DACR_ENA | + WM8994_DAC1L_ENA | WM8994_DAC1R_ENA); + } else if (aif_id == WM8994_AIF2) { + /* Power enable for AIF2 and DAC1 */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5, + WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK | + WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK, + WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | + WM8994_DAC1L_ENA | WM8994_DAC1R_ENA); + } + /* Head Phone Initialisation */ + ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, + WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK, + WM8994_HPOUT1L_DLY | WM8994_HPOUT1R_DLY); + + ret |= wm8994_update_bits(WM8994_DC_SERVO_1, + WM8994_DCS_ENA_CHAN_0_MASK | + WM8994_DCS_ENA_CHAN_1_MASK , WM8994_DCS_ENA_CHAN_0 | + WM8994_DCS_ENA_CHAN_1); + + ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, + WM8994_HPOUT1L_DLY_MASK | + WM8994_HPOUT1R_DLY_MASK | WM8994_HPOUT1L_OUTP_MASK | + WM8994_HPOUT1R_OUTP_MASK | + WM8994_HPOUT1L_RMV_SHORT_MASK | + WM8994_HPOUT1R_RMV_SHORT_MASK, WM8994_HPOUT1L_DLY | + WM8994_HPOUT1R_DLY | WM8994_HPOUT1L_OUTP | + WM8994_HPOUT1R_OUTP | WM8994_HPOUT1L_RMV_SHORT | + WM8994_HPOUT1R_RMV_SHORT); + + /* MIXER Config DAC1 to HP */ + ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_1, + WM8994_DAC1L_TO_HPOUT1L_MASK, WM8994_DAC1L_TO_HPOUT1L); + + ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_2, + WM8994_DAC1R_TO_HPOUT1R_MASK, WM8994_DAC1R_TO_HPOUT1R); + + if (aif_id == WM8994_AIF1) { + /* Routing AIF1 to DAC1 */ + ret |= wm8994_i2c_write(WM8994_DAC1_LEFT_MIXER_ROUTING, + WM8994_AIF1DAC1L_TO_DAC1L); + + ret |= wm8994_i2c_write(WM8994_DAC1_RIGHT_MIXER_ROUTING, + WM8994_AIF1DAC1R_TO_DAC1R); + + /* GPIO Settings for AIF1 */ + ret |= wm8994_i2c_write(WM8994_GPIO_1, WM8994_GPIO_DIR_OUTPUT + | WM8994_GPIO_FUNCTION_I2S_CLK + | WM8994_GPIO_INPUT_DEBOUNCE); + + ret |= wm8994_init_volume_aif1_dac1(); + } else if (aif_id == WM8994_AIF2) { + /* Routing AIF2 to DAC1 */ + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING, + WM8994_AIF2DACL_TO_DAC1L_MASK, + WM8994_AIF2DACL_TO_DAC1L); + + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING, + WM8994_AIF2DACR_TO_DAC1R_MASK, + WM8994_AIF2DACR_TO_DAC1R); + + /* GPIO Settings for AIF2 */ + /* B CLK */ + ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK , + WM8994_GPIO_DIR_OUTPUT); + + /* LR CLK */ + ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT); + + /* DATA */ + ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT); + + ret |= wm8994_init_volume_aif2_dac1(); + } + + if (ret < 0) + goto err; + + debug("%s: Codec chip init ok\n", __func__); + return 0; +err: + debug("%s: Codec chip init error\n", __func__); + return -1; +} + +/* + * Gets fdt values for wm8994 config parameters + * + * @param pcodec_info codec information structure + * @param blob FDT blob + * @return int value, 0 for success + */ +static int get_codec_values(struct sound_codec_info *pcodec_info, + const void *blob) +{ + int error = 0; +#ifdef CONFIG_OF_CONTROL + enum fdt_compat_id compat; + int node; + int parent; + + /* Get the node from FDT for codec */ + node = fdtdec_next_compatible(blob, 0, COMPAT_WOLFSON_WM8994_CODEC); + if (node <= 0) { + debug("EXYNOS_SOUND: No node for codec in device tree\n"); + debug("node = %d\n", node); + return -1; + } + + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + compat = fdtdec_lookup(blob, parent); + switch (compat) { + case COMPAT_SAMSUNG_S3C2440_I2C: + pcodec_info->i2c_bus = i2c_get_bus_num_fdt(parent); + error |= pcodec_info->i2c_bus; + debug("i2c bus = %d\n", pcodec_info->i2c_bus); + pcodec_info->i2c_dev_addr = fdtdec_get_int(blob, node, + "reg", 0); + error |= pcodec_info->i2c_dev_addr; + debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr); + break; + default: + debug("%s: Unknown compat id %d\n", __func__, compat); + return -1; + } +#else + pcodec_info->i2c_bus = AUDIO_I2C_BUS; + pcodec_info->i2c_dev_addr = AUDIO_I2C_REG; + debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr); +#endif + + pcodec_info->codec_type = CODEC_WM_8994; + + if (error == -1) { + debug("fail to get wm8994 codec node properties\n"); + return -1; + } + + return 0; +} + +/* WM8994 Device Initialisation */ +int wm8994_init(const void *blob, enum en_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample, unsigned int channels) +{ + int ret = 0; + struct sound_codec_info *pcodec_info = &g_codec_info; + + /* Get the codec Values */ + if (get_codec_values(pcodec_info, blob) < 0) { + debug("FDT Codec values failed\n"); + return -1; + } + + /* shift the device address by 1 for 7 bit addressing */ + g_wm8994_i2c_dev_addr = pcodec_info->i2c_dev_addr; + wm8994_i2c_init(pcodec_info->i2c_bus); + + if (pcodec_info->codec_type == CODEC_WM_8994) { + g_wm8994_info.type = WM8994; + } else { + debug("%s: Codec id [%d] not defined\n", __func__, + pcodec_info->codec_type); + return -1; + } + + ret = wm8994_device_init(&g_wm8994_info, aif_id); + if (ret < 0) { + debug("%s: wm8994 codec chip init failed\n", __func__); + return ret; + } + + ret = wm8994_set_sysclk(&g_wm8994_info, aif_id, WM8994_SYSCLK_MCLK1, + mclk_freq); + if (ret < 0) { + debug("%s: wm8994 codec set sys clock failed\n", __func__); + return ret; + } + + ret = wm8994_hw_params(&g_wm8994_info, aif_id, sampling_rate, + bits_per_sample, channels); + + if (ret == 0) { + ret = wm8994_set_fmt(aif_id, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + } + return ret; +} diff --git a/qemu/roms/u-boot/drivers/sound/wm8994.h b/qemu/roms/u-boot/drivers/sound/wm8994.h new file mode 100644 index 000000000..6d0c7ca27 --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/wm8994.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chadrasekar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __WM8994_H__ +#define __WM8994_H__ + +/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ +#define WM8994_SYSCLK_MCLK1 1 +#define WM8994_SYSCLK_MCLK2 2 +#define WM8994_SYSCLK_FLL1 3 +#define WM8994_SYSCLK_FLL2 4 + +/* Avilable audi interface ports in wm8994 codec */ +enum en_audio_interface { + WM8994_AIF1 = 1, + WM8994_AIF2, + WM8994_AIF3 +}; + +/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */ +#define WM8994_SYSCLK_OPCLK 5 + +#define WM8994_FLL1 1 +#define WM8994_FLL2 2 + +#define WM8994_FLL_SRC_MCLK1 1 +#define WM8994_FLL_SRC_MCLK2 2 +#define WM8994_FLL_SRC_LRCLK 3 +#define WM8994_FLL_SRC_BCLK 4 + +/* maximum available digital interfac in the dac to configure */ +#define WM8994_MAX_AIF 2 + +#define WM8994_MAX_INPUT_CLK_FREQ 13500000 +#define WM8994_ID 0x8994 + +enum wm8994_vmid_mode { + WM8994_VMID_NORMAL, + WM8994_VMID_FORCE, +}; + +/* wm 8994 family devices */ +enum wm8994_type { + WM8994 = 0, + WM8958 = 1, + WM1811 = 2, +}; + +/* + * intialise wm8994 sound codec device for the given configuration + * + * @param blob FDT node for codec values + * @param aif_id enum value of codec interface port in which + * soc i2s is connected + * @param sampling_rate Sampling rate ranges between from 8khz to 96khz + * @param mclk_freq Master clock frequency. + * @param bits_per_sample bits per Sample can be 16 or 24 + * @param channels Number of channnels, maximum 2 + * + * @returns -1 for error and 0 Success. + */ +int wm8994_init(const void *blob, enum en_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample, unsigned int channels); +#endif /*__WM8994_H__ */ diff --git a/qemu/roms/u-boot/drivers/sound/wm8994_registers.h b/qemu/roms/u-boot/drivers/sound/wm8994_registers.h new file mode 100644 index 000000000..0aba2fdfd --- /dev/null +++ b/qemu/roms/u-boot/drivers/sound/wm8994_registers.h @@ -0,0 +1,326 @@ +/* + * (C) Copyright 2012 Samsung Electronics + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __WM8994_REGISTERS_H__ +#define __WM8994_REGISTERS_H__ + +/* + * Register values. + */ +#define WM8994_SOFTWARE_RESET 0x00 +#define WM8994_POWER_MANAGEMENT_1 0x01 +#define WM8994_POWER_MANAGEMENT_2 0x02 +#define WM8994_POWER_MANAGEMENT_4 0x04 +#define WM8994_POWER_MANAGEMENT_5 0x05 +#define WM8994_LEFT_OUTPUT_VOLUME 0x1C +#define WM8994_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8994_OUTPUT_MIXER_1 0x2D +#define WM8994_OUTPUT_MIXER_2 0x2E +#define WM8994_CHARGE_PUMP_1 0x4C +#define WM8994_DC_SERVO_1 0x54 +#define WM8994_ANALOGUE_HP_1 0x60 +#define WM8994_CHIP_REVISION 0x100 +#define WM8994_AIF1_CLOCKING_1 0x200 +#define WM8994_AIF1_CLOCKING_2 0x201 +#define WM8994_AIF2_CLOCKING_1 0x204 +#define WM8994_CLOCKING_1 0x208 +#define WM8994_CLOCKING_2 0x209 +#define WM8994_AIF1_RATE 0x210 +#define WM8994_AIF2_RATE 0x211 +#define WM8994_RATE_STATUS 0x212 +#define WM8994_AIF1_CONTROL_1 0x300 +#define WM8994_AIF1_CONTROL_2 0x301 +#define WM8994_AIF1_MASTER_SLAVE 0x302 +#define WM8994_AIF1_BCLK 0x303 +#define WM8994_AIF2_CONTROL_1 0x310 +#define WM8994_AIF2_CONTROL_2 0x311 +#define WM8994_AIF2_MASTER_SLAVE 0x312 +#define WM8994_AIF2_BCLK 0x313 +#define WM8994_AIF1_DAC_FILTERS_1 0x420 +#define WM8994_AIF2_DAC_LEFT_VOLUME 0x502 +#define WM8994_AIF2_DAC_RIGHT_VOLUME 0x503 +#define WM8994_AIF2_DAC_FILTERS_1 0x520 +#define WM8994_DAC1_LEFT_MIXER_ROUTING 0x601 +#define WM8994_DAC1_RIGHT_MIXER_ROUTING 0x602 +#define WM8994_DAC1_LEFT_VOLUME 0x610 +#define WM8994_DAC1_RIGHT_VOLUME 0x611 +#define WM8994_GPIO_1 0x700 +#define WM8994_GPIO_3 0x702 +#define WM8994_GPIO_4 0x703 +#define WM8994_GPIO_5 0x704 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +/* SW_RESET */ +#define WM8994_SW_RESET 1 +/* + * R1 (0x01) - Power Management (1) + */ +/* HPOUT1L_ENA */ +#define WM8994_HPOUT1L_ENA 0x0200 +/* HPOUT1L_ENA */ +#define WM8994_HPOUT1L_ENA_MASK 0x0200 +/* HPOUT1R_ENA */ +#define WM8994_HPOUT1R_ENA 0x0100 +/* HPOUT1R_ENA */ +#define WM8994_HPOUT1R_ENA_MASK 0x0100 +/* VMID_SEL - [2:1] */ +#define WM8994_VMID_SEL_MASK 0x0006 +/* BIAS_ENA */ +#define WM8994_BIAS_ENA 0x0001 +/* BIAS_ENA */ +#define WM8994_BIAS_ENA_MASK 0x0001 + +/* + * R2 (0x02) - Power Management (2) + */ +/* OPCLK_ENA */ +#define WM8994_OPCLK_ENA 0x0800 + +#define WM8994_TSHUT_ENA 0x4000 +#define WM8994_MIXINL_ENA 0x0200 +#define WM8994_MIXINR_ENA 0x0100 +#define WM8994_IN2L_ENA 0x0080 +#define WM8994_IN2R_ENA 0x0020 + +/* + * R5 (0x04) - Power Management (4) + */ +#define WM8994_ADCL_ENA 0x0001 +#define WM8994_ADCR_ENA 0x0002 +#define WM8994_AIF1ADC1R_ENA 0x0100 +#define WM8994_AIF1ADC1L_ENA 0x0200 + +/* + * R5 (0x05) - Power Management (5) + */ +/* AIF2DACL_ENA */ +#define WM8994_AIF2DACL_ENA 0x2000 +#define WM8994_AIF2DACL_ENA_MASK 0x2000 +/* AIF2DACR_ENA */ +#define WM8994_AIF2DACR_ENA 0x1000 +#define WM8994_AIF2DACR_ENA_MASK 0x1000 +/* AIF1DACL_ENA */ +#define WM8994_AIF1DACL_ENA 0x0200 +#define WM8994_AIF1DACL_ENA_MASK 0x0200 +/* AIF1DACR_ENA */ +#define WM8994_AIF1DACR_ENA 0x0100 +#define WM8994_AIF1DACR_ENA_MASK 0x0100 +/* DAC1L_ENA */ +#define WM8994_DAC1L_ENA 0x0002 +#define WM8994_DAC1L_ENA_MASK 0x0002 +/* DAC1R_ENA */ +#define WM8994_DAC1R_ENA 0x0001 +#define WM8994_DAC1R_ENA_MASK 0x0001 + +/* + * R45 (0x2D) - Output Mixer (1) + */ +/* DAC1L_TO_HPOUT1L */ +#define WM8994_DAC1L_TO_HPOUT1L 0x0100 +#define WM8994_DAC1L_TO_HPOUT1L_MASK 0x0100 + +/* + * R46 (0x2E) - Output Mixer (2) + */ +/* DAC1R_TO_HPOUT1R */ +#define WM8994_DAC1R_TO_HPOUT1R 0x0100 +#define WM8994_DAC1R_TO_HPOUT1R_MASK 0x0100 + +/* + * R76 (0x4C) - Charge Pump (1) + */ +/* CP_ENA */ +#define WM8994_CP_ENA 0x8000 +#define WM8994_CP_ENA_MASK 0x8000 +/* + * R84 (0x54) - DC Servo (1) + */ +/* DCS_ENA_CHAN_1 */ +#define WM8994_DCS_ENA_CHAN_1 0x0002 +#define WM8994_DCS_ENA_CHAN_1_MASK 0x0002 +/* DCS_ENA_CHAN_0 */ +#define WM8994_DCS_ENA_CHAN_0 0x0001 +#define WM8994_DCS_ENA_CHAN_0_MASK 0x0001 + +/* + * R96 (0x60) - Analogue HP (1) + */ +/* HPOUT1L_RMV_SHORT */ +#define WM8994_HPOUT1L_RMV_SHORT 0x0080 +#define WM8994_HPOUT1L_RMV_SHORT_MASK 0x0080 +/* HPOUT1L_OUTP */ +#define WM8994_HPOUT1L_OUTP 0x0040 +#define WM8994_HPOUT1L_OUTP_MASK 0x0040 +/* HPOUT1L_DLY */ +#define WM8994_HPOUT1L_DLY 0x0020 +#define WM8994_HPOUT1L_DLY_MASK 0x0020 +/* HPOUT1R_RMV_SHORT */ +#define WM8994_HPOUT1R_RMV_SHORT 0x0008 +#define WM8994_HPOUT1R_RMV_SHORT_MASK 0x0008 +/* HPOUT1R_OUTP */ +#define WM8994_HPOUT1R_OUTP 0x0004 +#define WM8994_HPOUT1R_OUTP_MASK 0x0004 +/* HPOUT1R_DLY */ +#define WM8994_HPOUT1R_DLY 0x0002 +#define WM8994_HPOUT1R_DLY_MASK 0x0002 + +/* + * R512 (0x200) - AIF1 Clocking (1) + */ +/* AIF1CLK_SRC - [4:3] */ +#define WM8994_AIF1CLK_SRC_MASK 0x0018 +/* AIF1CLK_DIV */ +#define WM8994_AIF1CLK_DIV 0x0002 +/* AIF1CLK_ENA */ +#define WM8994_AIF1CLK_ENA 0x0001 +#define WM8994_AIF1CLK_ENA_MASK 0x0001 + +/* + * R517 (0x205) - AIF2 Clocking (2) + */ +/* AIF2DAC_DIV - [5:3] */ +#define WM8994_AIF2DAC_DIV_MASK 0x0038 + +/* + * R520 (0x208) - Clocking (1) + */ +/* AIF1DSPCLK_ENA */ +#define WM8994_AIF1DSPCLK_ENA 0x0008 +#define WM8994_AIF1DSPCLK_ENA_MASK 0x0008 +/* AIF2DSPCLK_ENA */ +#define WM8994_AIF2DSPCLK_ENA 0x0004 +#define WM8994_AIF2DSPCLK_ENA_MASK 0x0004 +/* SYSDSPCLK_ENA */ +#define WM8994_SYSDSPCLK_ENA 0x0002 +#define WM8994_SYSDSPCLK_ENA_MASK 0x0002 +/* SYSCLK_SRC */ +#define WM8994_SYSCLK_SRC 0x0001 + +/* + * R521 (0x209) - Clocking (2) + */ +/* OPCLK_DIV - [2:0] */ +#define WM8994_OPCLK_DIV_MASK 0x0007 + +/* + * R528 (0x210) - AIF1 Rate + */ +/* AIF1_SR - [7:4] */ +#define WM8994_AIF1_SR_MASK 0x00F0 +#define WM8994_AIF1_SR_SHIFT 4 +/* AIF1CLK_RATE - [3:0] */ +#define WM8994_AIF1CLK_RATE_MASK 0x000F + +/* + * R768 (0x300) - AIF1 Control (1) + */ +/* AIF1_BCLK_INV */ +#define WM8994_AIF1_BCLK_INV 0x0100 +/* AIF1_LRCLK_INV */ +#define WM8994_AIF1_LRCLK_INV 0x0080 +#define WM8994_AIF1_LRCLK_INV_MASK 0x0080 +/* AIF1_WL - [6:5] */ +#define WM8994_AIF1_WL_MASK 0x0060 +/* AIF1_FMT - [4:3] */ +#define WM8994_AIF1_FMT_MASK 0x0018 + +/* + * R769 (0x301) - AIF1 Control (2) + */ +/* AIF1_MONO */ +#define WM8994_AIF1_MONO 0x0100 + +/* + * R770 (0x302) - AIF1 Master/Slave + */ +/* AIF1_MSTR */ +#define WM8994_AIF1_MSTR 0x4000 +#define WM8994_AIF1_MSTR_MASK 0x4000 + +/* + * R771 (0x303) - AIF1 BCLK + */ +/* AIF1_BCLK_DIV - [8:4] */ +#define WM8994_AIF1_BCLK_DIV_MASK 0x01F0 +#define WM8994_AIF1_BCLK_DIV_SHIFT 4 + +/* + * R1282 (0x502) - AIF2 DAC Left Volume + */ +/* AIF2DAC_VU */ +#define WM8994_AIF2DAC_VU 0x0100 +#define WM8994_AIF2DAC_VU_MASK 0x0100 +/* AIF2DACL_VOL - [7:0] */ +#define WM8994_AIF2DACL_VOL_MASK 0x00FF + +/* + * R1283 (0x503) - AIF2 DAC Right Volume + */ +/* AIF2DACR_VOL - [7:0] */ +#define WM8994_AIF2DACR_VOL_MASK 0x00FF + +/* + * R1312 (0x520) - AIF2 DAC Filters (1) + */ +/* AIF2DAC_MUTE */ +#define WM8994_AIF2DAC_MUTE_MASK 0x0200 + +/* + * R1537 (0x601) - DAC1 Left Mixer Routing + */ +/* AIF2DACL_TO_DAC1L */ +#define WM8994_AIF2DACL_TO_DAC1L 0x0004 +#define WM8994_AIF2DACL_TO_DAC1L_MASK 0x0004 +/* AIF1DAC1L_TO_DAC1L */ +#define WM8994_AIF1DAC1L_TO_DAC1L 0x0001 + +/* + * R1538 (0x602) - DAC1 Right Mixer Routing + */ +/* AIF2DACR_TO_DAC1R */ +#define WM8994_AIF2DACR_TO_DAC1R 0x0004 +#define WM8994_AIF2DACR_TO_DAC1R_MASK 0x0004 +/* AIF1DAC1R_TO_DAC1R */ +#define WM8994_AIF1DAC1R_TO_DAC1R 0x0001 + +/* + * R1552 (0x610) - DAC1 Left Volume + */ +/* DAC1L_MUTE */ +#define WM8994_DAC1L_MUTE_MASK 0x0200 +/* DAC1_VU */ +#define WM8994_DAC1_VU 0x0100 +#define WM8994_DAC1_VU_MASK 0x0100 +/* DAC1L_VOL - [7:0] */ +#define WM8994_DAC1L_VOL_MASK 0x00FF + +/* + * R1553 (0x611) - DAC1 Right Volume + */ +/* DAC1R_MUTE */ +#define WM8994_DAC1R_MUTE_MASK 0x0200 +/* DAC1R_VOL - [7:0] */ +#define WM8994_DAC1R_VOL_MASK 0x00FF + +/* + * GPIO + */ +/* OUTPUT PIN */ +#define WM8994_GPIO_DIR_OUTPUT 0x8000 +/* GPIO PIN MASK */ +#define WM8994_GPIO_DIR_MASK 0xFFE0 +/* I2S CLK */ +#define WM8994_GPIO_FUNCTION_I2S_CLK 0x0001 +#define WM8994_GPIO_INPUT_DEBOUNCE 0x0100 +/* GPn FN */ +#define WM8994_GPIO_FUNCTION_MASK 0x001F +#endif diff --git a/qemu/roms/u-boot/drivers/spi/Makefile b/qemu/roms/u-boot/drivers/spi/Makefile new file mode 100644 index 000000000..81b6af669 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/Makefile @@ -0,0 +1,42 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +# There are many options which enable SPI, so make this library available +obj-y += spi.o + +obj-$(CONFIG_ALTERA_SPI) += altera_spi.o +obj-$(CONFIG_ANDES_SPI) += andes_spi.o +obj-$(CONFIG_ARMADA100_SPI) += armada100_spi.o +obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o +obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BFIN_SPI) += bfin_spi.o +obj-$(CONFIG_BFIN_SPI6XX) += bfin_spi6xx.o +obj-$(CONFIG_CF_SPI) += cf_spi.o +obj-$(CONFIG_CF_QSPI) += cf_qspi.o +obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o +obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o +obj-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o +obj-$(CONFIG_ICH_SPI) += ich.o +obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o +obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o +obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o +obj-$(CONFIG_MXC_SPI) += mxc_spi.o +obj-$(CONFIG_MXS_SPI) += mxs_spi.o +obj-$(CONFIG_OC_TINY_SPI) += oc_tiny_spi.o +obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o +obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o +obj-$(CONFIG_SOFT_SPI) += soft_spi.o +obj-$(CONFIG_SH_SPI) += sh_spi.o +obj-$(CONFIG_SH_QSPI) += sh_qspi.o +obj-$(CONFIG_FSL_ESPI) += fsl_espi.o +obj-$(CONFIG_FDT_SPI) += fdt_spi.o +obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o +obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o +obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o +obj-$(CONFIG_TI_QSPI) += ti_qspi.o +obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o +obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o diff --git a/qemu/roms/u-boot/drivers/spi/altera_spi.c b/qemu/roms/u-boot/drivers/spi/altera_spi.c new file mode 100644 index 000000000..5accbb5c2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/altera_spi.c @@ -0,0 +1,168 @@ +/* + * Altera SPI driver + * + * based on bfin_spi.c + * Copyright (c) 2005-2008 Analog Devices Inc. + * Copyright (C) 2010 Thomas Chou + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include + +#define ALTERA_SPI_RXDATA 0 +#define ALTERA_SPI_TXDATA 4 +#define ALTERA_SPI_STATUS 8 +#define ALTERA_SPI_CONTROL 12 +#define ALTERA_SPI_SLAVE_SEL 20 + +#define ALTERA_SPI_STATUS_ROE_MSK (0x8) +#define ALTERA_SPI_STATUS_TOE_MSK (0x10) +#define ALTERA_SPI_STATUS_TMT_MSK (0x20) +#define ALTERA_SPI_STATUS_TRDY_MSK (0x40) +#define ALTERA_SPI_STATUS_RRDY_MSK (0x80) +#define ALTERA_SPI_STATUS_E_MSK (0x100) + +#define ALTERA_SPI_CONTROL_IROE_MSK (0x8) +#define ALTERA_SPI_CONTROL_ITOE_MSK (0x10) +#define ALTERA_SPI_CONTROL_ITRDY_MSK (0x40) +#define ALTERA_SPI_CONTROL_IRRDY_MSK (0x80) +#define ALTERA_SPI_CONTROL_IE_MSK (0x100) +#define ALTERA_SPI_CONTROL_SSO_MSK (0x400) + +#ifndef CONFIG_SYS_ALTERA_SPI_LIST +#define CONFIG_SYS_ALTERA_SPI_LIST { CONFIG_SYS_SPI_BASE } +#endif + +static ulong altera_spi_base_list[] = CONFIG_SYS_ALTERA_SPI_LIST; + +struct altera_spi_slave { + struct spi_slave slave; + ulong base; +}; +#define to_altera_spi_slave(s) container_of(s, struct altera_spi_slave, slave) + +__attribute__((weak)) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus < ARRAY_SIZE(altera_spi_base_list) && cs < 32; +} + +__attribute__((weak)) +void spi_cs_activate(struct spi_slave *slave) +{ + struct altera_spi_slave *altspi = to_altera_spi_slave(slave); + writel(1 << slave->cs, altspi->base + ALTERA_SPI_SLAVE_SEL); + writel(ALTERA_SPI_CONTROL_SSO_MSK, altspi->base + ALTERA_SPI_CONTROL); +} + +__attribute__((weak)) +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct altera_spi_slave *altspi = to_altera_spi_slave(slave); + writel(0, altspi->base + ALTERA_SPI_CONTROL); + writel(0, altspi->base + ALTERA_SPI_SLAVE_SEL); +} + +void spi_init(void) +{ +} + +void spi_set_speed(struct spi_slave *slave, uint hz) +{ + /* altera spi core does not support programmable speed */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct altera_spi_slave *altspi; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + altspi = spi_alloc_slave(struct altera_spi_slave, bus, cs); + if (!altspi) + return NULL; + + altspi->base = altera_spi_base_list[bus]; + debug("%s: bus:%i cs:%i base:%lx\n", __func__, + bus, cs, altspi->base); + + return &altspi->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct altera_spi_slave *altspi = to_altera_spi_slave(slave); + free(altspi); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct altera_spi_slave *altspi = to_altera_spi_slave(slave); + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + writel(0, altspi->base + ALTERA_SPI_CONTROL); + writel(0, altspi->base + ALTERA_SPI_SLAVE_SEL); + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct altera_spi_slave *altspi = to_altera_spi_slave(slave); + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + writel(0, altspi->base + ALTERA_SPI_SLAVE_SEL); +} + +#ifndef CONFIG_ALTERA_SPI_IDLE_VAL +# define CONFIG_ALTERA_SPI_IDLE_VAL 0xff +#endif + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct altera_spi_slave *altspi = to_altera_spi_slave(slave); + /* assume spi core configured to do 8 bit transfers */ + uint bytes = bitlen / 8; + const uchar *txp = dout; + uchar *rxp = din; + + debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, + slave->bus, slave->cs, bitlen, bytes, flags); + if (bitlen == 0) + goto done; + + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto done; + } + + /* empty read buffer */ + if (readl(altspi->base + ALTERA_SPI_STATUS) & + ALTERA_SPI_STATUS_RRDY_MSK) + readl(altspi->base + ALTERA_SPI_RXDATA); + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + while (bytes--) { + uchar d = txp ? *txp++ : CONFIG_ALTERA_SPI_IDLE_VAL; + debug("%s: tx:%x ", __func__, d); + writel(d, altspi->base + ALTERA_SPI_TXDATA); + while (!(readl(altspi->base + ALTERA_SPI_STATUS) & + ALTERA_SPI_STATUS_RRDY_MSK)) + ; + d = readl(altspi->base + ALTERA_SPI_RXDATA); + if (rxp) + *rxp++ = d; + debug("rx:%x\n", d); + } + done: + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/andes_spi.c b/qemu/roms/u-boot/drivers/spi/andes_spi.c new file mode 100644 index 000000000..82aed75ca --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/andes_spi.c @@ -0,0 +1,284 @@ +/* + * Driver of Andes SPI Controller + * + * (C) Copyright 2011 Andes Technology + * Macpaul Lin + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include "andes_spi.h" + +void spi_init(void) +{ + /* do nothing */ +} + +static void andes_spi_spit_en(struct andes_spi_slave *ds) +{ + unsigned int dcr = readl(&ds->regs->dcr); + + debug("%s: dcr: %x, write value: %x\n", + __func__, dcr, (dcr | ANDES_SPI_DCR_SPIT)); + + writel((dcr | ANDES_SPI_DCR_SPIT), &ds->regs->dcr); +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct andes_spi_slave *ds; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + ds = spi_alloc_slave(struct andes_spi_slave, bus, cs); + if (!ds) + return NULL; + + ds->regs = (struct andes_spi_regs *)CONFIG_SYS_SPI_BASE; + + /* + * The hardware of andes_spi will set its frequency according + * to APB/AHB bus clock. Hence the hardware doesn't allow changing of + * requency and so the user requested speed is always ignored. + */ + ds->freq = max_hz; + + return &ds->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct andes_spi_slave *ds = to_andes_spi(slave); + + free(ds); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct andes_spi_slave *ds = to_andes_spi(slave); + unsigned int apb; + unsigned int baud; + + /* Enable the SPI hardware */ + writel(ANDES_SPI_CR_SPIRST, &ds->regs->cr); + udelay(1000); + + /* setup format */ + baud = ((CONFIG_SYS_CLK_FREQ / CONFIG_SYS_SPI_CLK / 2) - 1) & 0xFF; + + /* + * SPI_CLK = AHB bus clock / ((BAUD + 1)*2) + * BAUD = AHB bus clock / SPI_CLK / 2) - 1 + */ + apb = (readl(&ds->regs->apb) & 0xffffff00) | baud; + writel(apb, &ds->regs->apb); + + /* no interrupts */ + writel(0, &ds->regs->ie); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct andes_spi_slave *ds = to_andes_spi(slave); + + /* Disable the SPI hardware */ + writel(ANDES_SPI_CR_SPIRST, &ds->regs->cr); +} + +static int andes_spi_read(struct spi_slave *slave, unsigned int len, + u8 *rxp, unsigned long flags) +{ + struct andes_spi_slave *ds = to_andes_spi(slave); + unsigned int i, left; + unsigned int data; + + debug("%s: slave: %x, len: %d, rxp: %x, flags: %d\n", + __func__, slave, len, rxp, flags); + + debug("%s: data: ", __func__); + while (len > 0) { + left = min(len, 4); + data = readl(&ds->regs->data); + + debug(" "); + for (i = 0; i < left; i++) { + debug("%02x ", data & 0xff); + *rxp++ = data; + data >>= 8; + len--; + } + } + debug("\n"); + + return 0; +} + +static int andes_spi_write(struct spi_slave *slave, unsigned int wlen, + unsigned int rlen, const u8 *txp, unsigned long flags) +{ + struct andes_spi_slave *ds = to_andes_spi(slave); + unsigned int data; + unsigned int i, left; + unsigned int spit_enabled = 0; + + debug("%s: slave: %x, wlen: %d, rlen: %d, txp: %x, flags: %x\n", + __func__, slave, wlen, rlen, txp, flags); + + /* The value of wlen and rlen wrote to register must minus 1 */ + if (rlen == 0) /* write only */ + writel(ANDES_SPI_DCR_MODE_WO | ANDES_SPI_DCR_WCNT(wlen-1) | + ANDES_SPI_DCR_RCNT(0), &ds->regs->dcr); + else /* write then read */ + writel(ANDES_SPI_DCR_MODE_WR | ANDES_SPI_DCR_WCNT(wlen-1) | + ANDES_SPI_DCR_RCNT(rlen-1), &ds->regs->dcr); + + /* wait till SPIBSY is cleared */ + while (readl(&ds->regs->st) & ANDES_SPI_ST_SPIBSY) + ; + + /* data write process */ + debug("%s: txp: ", __func__); + while (wlen > 0) { + /* clear the data */ + data = 0; + + /* data are usually be read 32bits once a time */ + left = min(wlen, 4); + + for (i = 0; i < left; i++) { + debug("%x ", *txp); + data |= *txp++ << (i * 8); + wlen--; + } + debug("\n"); + + debug("data: %08x\n", data); + debug("streg before write: %08x\n", readl(&ds->regs->st)); + /* wait till TXFULL is deasserted */ + while (readl(&ds->regs->st) & ANDES_SPI_ST_TXFEL) + ; + writel(data, &ds->regs->data); + debug("streg after write: %08x\n", readl(&ds->regs->st)); + + + if (spit_enabled == 0) { + /* enable SPIT bit - trigger the tx and rx progress */ + andes_spi_spit_en(ds); + spit_enabled = 1; + } + + } + debug("\n"); + + return 0; +} + +/* + * spi_xfer: + * Since andes_spi doesn't support independent command transaction, + * that is, write and than read must be operated in continuous + * execution, there is no need to set dcr and trigger spit again in + * RX process. + */ +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + unsigned int len; + static int op_nextime; + static u8 tmp_cmd[5]; + static int tmp_wlen; + unsigned int i; + + if (bitlen == 0) + /* Finish any previously submitted transfers */ + goto out; + + if (bitlen % 8) { + /* Errors always terminate an ongoing transfer */ + flags |= SPI_XFER_END; + goto out; + } + + len = bitlen / 8; + + debug("%s: slave: %08x, bitlen: %d, dout: " + "%08x, din: %08x, flags: %d, len: %d\n", + __func__, slave, bitlen, dout, din, flags, len); + + /* + * Important: + * andes_spi's hardware doesn't support 2 data channel. The read + * and write cmd/data share the same register (data register). + * + * If a command has write and read transaction, you cannot do write + * this time and then do read on next time. + * + * A command writes first with a read response must indicating + * the read length in write operation. Hence the write action must + * be stored temporary and wait until the next read action has been + * arrived. Then we flush the write and read action out together. + */ + if (!dout) { + if (op_nextime == 1) { + /* flags should be SPI_XFER_END, value is 2 */ + op_nextime = 0; + andes_spi_write(slave, tmp_wlen, len, tmp_cmd, flags); + } + return andes_spi_read(slave, len, din, flags); + } else if (!din) { + if (flags == SPI_XFER_BEGIN) { + /* store the write command and do operation next time */ + op_nextime = 1; + memset(tmp_cmd, 0, sizeof(tmp_cmd)); + memcpy(tmp_cmd, dout, len); + + debug("%s: tmp_cmd: ", __func__); + for (i = 0; i < len; i++) + debug("%x ", *(tmp_cmd + i)); + debug("\n"); + + tmp_wlen = len; + } else { + /* + * flags should be (SPI_XFER_BEGIN | SPI_XFER_END), + * the value is 3. + */ + if (op_nextime == 1) { + /* flags should be SPI_XFER_END, value is 2 */ + op_nextime = 0; + /* flags 3 implies write only */ + andes_spi_write(slave, tmp_wlen, 0, tmp_cmd, 3); + } + + debug("flags: %x\n", flags); + return andes_spi_write(slave, len, 0, dout, flags); + } + } + +out: + return 0; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus == 0 && cs == 0; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + /* do nothing */ +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + /* do nothing */ +} diff --git a/qemu/roms/u-boot/drivers/spi/andes_spi.h b/qemu/roms/u-boot/drivers/spi/andes_spi.h new file mode 100644 index 000000000..b7d294599 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/andes_spi.h @@ -0,0 +1,115 @@ +/* + * Register definitions for the Andes SPI Controller + * + * (C) Copyright 2011 Andes Technology + * Macpaul Lin + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ANDES_SPI_H +#define __ANDES_SPI_H + +struct andes_spi_regs { + unsigned int apb; /* 0x00 - APB SPI interface setting */ + unsigned int pio; /* 0x04 - PIO reg */ + unsigned int cr; /* 0x08 - SPI Control reg */ + unsigned int st; /* 0x0c - SPI Status reg */ + unsigned int ie; /* 0x10 - Interrupt Enable reg */ + unsigned int ist; /* 0x14 - Interrupt Status reg */ + unsigned int dcr; /* 0x18 - data control reg */ + unsigned int data; /* 0x1c - data register */ + unsigned int ahb; /* 0x20 - AHB SPI interface setting */ + unsigned int ver; /* 0x3c - SPI version reg */ +}; + +#define BIT(x) (1 << (x)) + +/* 0x00 - APB SPI interface setting register */ +#define ANDES_SPI_APB_BAUD(x) (((x) & 0xff) < 0) +#define ANDES_SPI_APB_CSHT(x) (((x) & 0xf) < 16) +#define ANDES_SPI_APB_SPNTS BIT(20) /* 0: normal, 1: delay */ +#define ANDES_SPI_APB_CPHA BIT(24) /* 0: Sampling at odd edges */ +#define ANDES_SPI_APB_CPOL BIT(25) /* 0: SCK low, 1: SCK high */ +#define ANDES_SPI_APB_MSSL BIT(26) /* 0: SPI Master, 1: slave */ + +/* 0x04 - PIO register */ +#define ANDES_SPI_PIO_MISO BIT(0) /* input value of pin MISO */ +#define ANDES_SPI_PIO_MOSI BIT(1) /* I/O value of pin MOSI */ +#define ANDES_SPI_PIO_SCK BIT(2) /* I/O value of pin SCK */ +#define ANDES_SPI_PIO_CS BIT(3) /* I/O value of pin CS */ +#define ANDES_SPI_PIO_PIOE BIT(4) /* Programming IO Enable */ + +/* 0x08 - SPI Control register */ +#define ANDES_SPI_CR_SPIRST BIT(0) /* SPI mode reset */ +#define ANDES_SPI_CR_RXFRST BIT(1) /* RxFIFO reset */ +#define ANDES_SPI_CR_TXFRST BIT(2) /* TxFIFO reset */ +#define ANDES_SPI_CR_RXFTH(x) (((x) & 0x1f) << 10) /* RxFIFO Threshold */ +#define ANDES_SPI_CR_TXFTH(x) (((x) & 0x1f) << 18) /* TxFIFO Threshold */ + +/* 0x0c - SPI Status register */ +#define ANDES_SPI_ST_SPIBSY BIT(0) /* SPI Transfer is active */ +#define ANDES_SPI_ST_RXFEM BIT(8) /* RxFIFO Empty Flag */ +#define ANDES_SPI_ST_RXFEL BIT(9) /* RxFIFO Full Flag */ +#define ANDES_SPI_ST_RXFVE(x) (((x) >> 10) & 0x1f) +#define ANDES_SPI_ST_TXFEM BIT(16) /* TxFIFO Empty Flag */ +#define ANDES_SPI_ST_TXFEL BIT(7) /* TxFIFO Full Flag */ +#define ANDES_SPI_ST_TXFVE(x) (((x) >> 18) & 0x1f) + +/* 0x10 - Interrupt Enable register */ +#define ANDES_SPI_IE_RXFORIE BIT(0) /* RxFIFO overrun intr */ +#define ANDES_SPI_IE_TXFURIE BIT(1) /* TxFOFO underrun intr */ +#define ANDES_SPI_IE_RXFTHIE BIT(2) /* RxFIFO threshold intr */ +#define ANDES_SPI_IE_TXFTHIE BIT(3) /* TxFIFO threshold intr */ +#define ANDES_SPI_IE_SPIEIE BIT(4) /* SPI transmit END intr */ +#define ANDES_SPI_IE_SPCFIE BIT(5) /* AHB/APB TxReq conflict */ + +/* 0x14 - Interrupt Status Register */ +#define ANDES_SPI_IST_RXFORI BIT(0) /* has RxFIFO overrun */ +#define ANDES_SPI_IST_TXFURI BIT(1) /* has TxFOFO underrun */ +#define ANDES_SPI_IST_RXFTHI BIT(2) /* has RxFIFO threshold */ +#define ANDES_SPI_IST_TXFTHI BIT(3) /* has TxFIFO threshold */ +#define ANDES_SPI_IST_SPIEI BIT(4) /* has SPI transmit END */ +#define ANDES_SPI_IST_SPCFI BIT(5) /* has AHB/APB TxReq conflict */ + +/* 0x18 - Data Control Register */ +#define ANDES_SPI_DCR_RCNT(x) (((x) & 0x3ff) << 0) +#define ANDES_SPI_DCR_DYCNT(x) (((x) & 0x7) << 12) +#define ANDES_SPI_DCR_WCNT(x) (((x) & 0x3ff) << 16) +#define ANDES_SPI_DCR_TRAMODE(x) (((x) & 0x7) << 28) +#define ANDES_SPI_DCR_SPIT BIT(31) /* SPI bus trigger */ + +#define ANDES_SPI_DCR_MODE_WRCON ANDES_SPI_DCR_TRAMODE(0) /* w/r at the same time */ +#define ANDES_SPI_DCR_MODE_WO ANDES_SPI_DCR_TRAMODE(1) /* write only */ +#define ANDES_SPI_DCR_MODE_RO ANDES_SPI_DCR_TRAMODE(2) /* read only */ +#define ANDES_SPI_DCR_MODE_WR ANDES_SPI_DCR_TRAMODE(3) /* write, read */ +#define ANDES_SPI_DCR_MODE_RW ANDES_SPI_DCR_TRAMODE(4) /* read, write */ +#define ANDES_SPI_DCR_MODE_WDR ANDES_SPI_DCR_TRAMODE(5) /* write, dummy, read */ +#define ANDES_SPI_DCR_MODE_RDW ANDES_SPI_DCR_TRAMODE(6) /* read, dummy, write */ +#define ANDES_SPI_DCR_MODE_RECEIVE ANDES_SPI_DCR_TRAMODE(7) /* receive */ + +/* 0x20 - AHB SPI interface setting register */ +#define ANDES_SPI_AHB_BAUD(x) (((x) & 0xff) < 0) +#define ANDES_SPI_AHB_CSHT(x) (((x) & 0xf) < 16) +#define ANDES_SPI_AHB_SPNTS BIT(20) /* 0: normal, 1: delay */ +#define ANDES_SPI_AHB_CPHA BIT(24) /* 0: Sampling at odd edges */ +#define ANDES_SPI_AHB_CPOL BIT(25) /* 0: SCK low, 1: SCK high */ +#define ANDES_SPI_AHB_MSSL BIT(26) /* only Master mode */ + +/* 0x3c - Version Register - (Year V.MAJOR.MINOR) */ +#define ANDES_SPI_VER_MINOR(x) (((x) >> 0) & 0xf) +#define ANDES_SPI_VER_MAJOR(x) (((x) >> 8) & 0xf) +#define ANDES_SPI_VER_YEAR(x) (((x) >> 16) & 0xf) + +struct andes_spi_slave { + struct spi_slave slave; + struct andes_spi_regs *regs; + unsigned int freq; +}; + +static inline struct andes_spi_slave *to_andes_spi(struct spi_slave *slave) +{ + return container_of(slave, struct andes_spi_slave, slave); +} + +#endif /* __ANDES_SPI_H */ diff --git a/qemu/roms/u-boot/drivers/spi/armada100_spi.c b/qemu/roms/u-boot/drivers/spi/armada100_spi.c new file mode 100644 index 000000000..53aaf9534 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/armada100_spi.c @@ -0,0 +1,203 @@ +/* + * (C) Copyright 2011 + * eInfochips Ltd. + * Written-by: Ajay Bhargav + * + * (C) Copyright 2009 + * Marvell Semiconductor + * Based on SSP driver + * Written-by: Lei Wen + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#include +#include +#include + +#include +#include +#include + +#define to_armd_spi_slave(s) container_of(s, struct armd_spi_slave, slave) + +struct armd_spi_slave { + struct spi_slave slave; + struct ssp_reg *spi_reg; + u32 cr0, cr1; + u32 int_cr1; + u32 clear_sr; + const void *tx; + void *rx; + int gpio_cs_inverted; +}; + +static int spi_armd_write(struct armd_spi_slave *pss) +{ + int wait_timeout = SSP_FLUSH_NUM; + while (--wait_timeout && !(readl(&pss->spi_reg->sssr) & SSSR_TNF)) + ; + if (!wait_timeout) { + debug("%s: timeout error\n", __func__); + return -1; + } + + if (pss->tx != NULL) { + writel(*(u8 *)pss->tx, &pss->spi_reg->ssdr); + ++pss->tx; + } else { + writel(0, &pss->spi_reg->ssdr); + } + return 0; +} + +static int spi_armd_read(struct armd_spi_slave *pss) +{ + int wait_timeout = SSP_FLUSH_NUM; + while (--wait_timeout && !(readl(&pss->spi_reg->sssr) & SSSR_RNE)) + ; + if (!wait_timeout) { + debug("%s: timeout error\n", __func__); + return -1; + } + + if (pss->rx != NULL) { + *(u8 *)pss->rx = readl(&pss->spi_reg->ssdr); + ++pss->rx; + } else { + readl(&pss->spi_reg->ssdr); + } + return 0; +} + +static int spi_armd_flush(struct armd_spi_slave *pss) +{ + unsigned long limit = SSP_FLUSH_NUM; + + do { + while (readl(&pss->spi_reg->sssr) & SSSR_RNE) + readl(&pss->spi_reg->ssdr); + } while ((readl(&pss->spi_reg->sssr) & SSSR_BSY) && limit--); + + writel(SSSR_ROR, &pss->spi_reg->sssr); + + return limit; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct armd_spi_slave *pss = to_armd_spi_slave(slave); + + gpio_set_value(slave->cs, pss->gpio_cs_inverted); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct armd_spi_slave *pss = to_armd_spi_slave(slave); + + gpio_set_value(slave->cs, !pss->gpio_cs_inverted); +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct armd_spi_slave *pss; + + pss = spi_alloc_slave(struct armd_spi_slave, bus, cs); + if (!pss) + return NULL; + + pss->spi_reg = (struct ssp_reg *)SSP_REG_BASE(CONFIG_SYS_SSP_PORT); + + pss->cr0 = SSCR0_MOTO | SSCR0_DATASIZE(DEFAULT_WORD_LEN) | SSCR0_SSE; + + pss->cr1 = (SSCR1_RXTRESH(RX_THRESH_DEF) & SSCR1_RFT) | + (SSCR1_TXTRESH(TX_THRESH_DEF) & SSCR1_TFT); + pss->cr1 &= ~(SSCR1_SPO | SSCR1_SPH); + pss->cr1 |= (((mode & SPI_CPHA) != 0) ? SSCR1_SPH : 0) + | (((mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0); + + pss->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; + pss->clear_sr = SSSR_ROR | SSSR_TINT; + + pss->gpio_cs_inverted = mode & SPI_CS_HIGH; + gpio_set_value(cs, !pss->gpio_cs_inverted); + + return &pss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct armd_spi_slave *pss = to_armd_spi_slave(slave); + + free(pss); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct armd_spi_slave *pss = to_armd_spi_slave(slave); + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + if (spi_armd_flush(pss) == 0) + return -1; + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct armd_spi_slave *pss = to_armd_spi_slave(slave); + uint bytes = bitlen / 8; + unsigned long limit; + int ret = 0; + + if (bitlen == 0) + goto done; + + /* we can only do 8 bit transfers */ + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto done; + } + + pss->tx = dout; + pss->rx = din; + + if (flags & SPI_XFER_BEGIN) { + spi_cs_activate(slave); + writel(pss->cr1 | pss->int_cr1, &pss->spi_reg->sscr1); + writel(TIMEOUT_DEF, &pss->spi_reg->ssto); + writel(pss->cr0, &pss->spi_reg->sscr0); + } + + while (bytes--) { + limit = SSP_FLUSH_NUM; + ret = spi_armd_write(pss); + if (ret) + break; + + while ((readl(&pss->spi_reg->sssr) & SSSR_BSY) && limit--) + udelay(1); + + ret = spi_armd_read(pss); + if (ret) + break; + } + + done: + if (flags & SPI_XFER_END) { + /* Stop SSP */ + writel(pss->clear_sr, &pss->spi_reg->sssr); + clrbits_le32(&pss->spi_reg->sscr1, pss->int_cr1); + writel(0, &pss->spi_reg->ssto); + spi_cs_deactivate(slave); + } + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/spi/atmel_dataflash_spi.c b/qemu/roms/u-boot/drivers/spi/atmel_dataflash_spi.c new file mode 100644 index 000000000..a2e9c00ea --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/atmel_dataflash_spi.c @@ -0,0 +1,184 @@ +/* + * Driver for ATMEL DataFlash support + * Author : Hamid Ikdoumi (Atmel) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This driver desperately needs rework: + * + * - use structure SoC access + * - get rid of including asm/arch/at91_spi.h + * - remove asm/arch/at91_spi.h + * - get rid of all CONFIG_ATMEL_LEGACY defines and uses + * + * 02-Aug-2010 Reinhard Meyer + */ + +#include +#ifndef CONFIG_ATMEL_LEGACY +# define CONFIG_ATMEL_LEGACY +#endif +#include +#include + +#include + +#include +#include + +#include "atmel_spi.h" + +#include +#include +#include + +#include + +#define AT91_SPI_PCS0_DATAFLASH_CARD 0xE /* Chip Select 0: NPCS0%1110 */ +#define AT91_SPI_PCS1_DATAFLASH_CARD 0xD /* Chip Select 1: NPCS1%1101 */ +#define AT91_SPI_PCS2_DATAFLASH_CARD 0xB /* Chip Select 2: NPCS2%1011 */ +#define AT91_SPI_PCS3_DATAFLASH_CARD 0x7 /* Chip Select 3: NPCS3%0111 */ + +void AT91F_SpiInit(void) +{ + /* Reset the SPI */ + writel(AT91_SPI_SWRST, ATMEL_BASE_SPI0 + AT91_SPI_CR); + + /* Configure SPI in Master Mode with No CS selected !!! */ + writel(AT91_SPI_MSTR | AT91_SPI_MODFDIS | AT91_SPI_PCS, + ATMEL_BASE_SPI0 + AT91_SPI_MR); + + /* Configure CS0 */ + writel(AT91_SPI_NCPHA | + (AT91_SPI_DLYBS & DATAFLASH_TCSS) | + (AT91_SPI_DLYBCT & DATAFLASH_TCHS) | + ((get_mck_clk_rate() / AT91_SPI_CLK) << 8), + ATMEL_BASE_SPI0 + AT91_SPI_CSR(0)); + +#ifdef CONFIG_SYS_DATAFLASH_LOGIC_ADDR_CS1 + /* Configure CS1 */ + writel(AT91_SPI_NCPHA | + (AT91_SPI_DLYBS & DATAFLASH_TCSS) | + (AT91_SPI_DLYBCT & DATAFLASH_TCHS) | + ((get_mck_clk_rate() / AT91_SPI_CLK) << 8), + ATMEL_BASE_SPI0 + AT91_SPI_CSR(1)); +#endif +#ifdef CONFIG_SYS_DATAFLASH_LOGIC_ADDR_CS2 + /* Configure CS2 */ + writel(AT91_SPI_NCPHA | + (AT91_SPI_DLYBS & DATAFLASH_TCSS) | + (AT91_SPI_DLYBCT & DATAFLASH_TCHS) | + ((get_mck_clk_rate() / AT91_SPI_CLK) << 8), + ATMEL_BASE_SPI0 + AT91_SPI_CSR(2)); +#endif +#ifdef CONFIG_SYS_DATAFLASH_LOGIC_ADDR_CS3 + /* Configure CS3 */ + writel(AT91_SPI_NCPHA | + (AT91_SPI_DLYBS & DATAFLASH_TCSS) | + (AT91_SPI_DLYBCT & DATAFLASH_TCHS) | + ((get_mck_clk_rate() / AT91_SPI_CLK) << 8), + ATMEL_BASE_SPI0 + AT91_SPI_CSR(3)); +#endif + + /* SPI_Enable */ + writel(AT91_SPI_SPIEN, ATMEL_BASE_SPI0 + AT91_SPI_CR); + + while (!(readl(ATMEL_BASE_SPI0 + AT91_SPI_SR) & AT91_SPI_SPIENS)) + ; + + /* + * Add tempo to get SPI in a safe state. + * Should not be needed for new silicon (Rev B) + */ + udelay(500000); + readl(ATMEL_BASE_SPI0 + AT91_SPI_SR); + readl(ATMEL_BASE_SPI0 + AT91_SPI_RDR); + +} + +void AT91F_SpiEnable(int cs) +{ + unsigned long mode; + + mode = readl(ATMEL_BASE_SPI0 + AT91_SPI_MR); + mode &= ~AT91_SPI_PCS; + + switch (cs) { + case 0: + mode |= AT91_SPI_PCS0_DATAFLASH_CARD << 16; + break; + case 1: + mode |= AT91_SPI_PCS1_DATAFLASH_CARD << 16; + break; + case 2: + mode |= AT91_SPI_PCS2_DATAFLASH_CARD << 16; + break; + case 3: + mode |= AT91_SPI_PCS3_DATAFLASH_CARD << 16; + break; + } + + writel(mode, ATMEL_BASE_SPI0 + AT91_SPI_MR); + + /* SPI_Enable */ + writel(AT91_SPI_SPIEN, ATMEL_BASE_SPI0 + AT91_SPI_CR); +} + +unsigned int AT91F_SpiWrite1(AT91PS_DataflashDesc pDesc); + +unsigned int AT91F_SpiWrite(AT91PS_DataflashDesc pDesc) +{ + unsigned int timeout; + unsigned int timebase; + + pDesc->state = BUSY; + + writel(AT91_SPI_TXTDIS + AT91_SPI_RXTDIS, + ATMEL_BASE_SPI0 + AT91_SPI_PTCR); + + /* Initialize the Transmit and Receive Pointer */ + writel((unsigned int)pDesc->rx_cmd_pt, + ATMEL_BASE_SPI0 + AT91_SPI_RPR); + writel((unsigned int)pDesc->tx_cmd_pt, + ATMEL_BASE_SPI0 + AT91_SPI_TPR); + + /* Intialize the Transmit and Receive Counters */ + writel(pDesc->rx_cmd_size, ATMEL_BASE_SPI0 + AT91_SPI_RCR); + writel(pDesc->tx_cmd_size, ATMEL_BASE_SPI0 + AT91_SPI_TCR); + + if (pDesc->tx_data_size != 0) { + /* Initialize the Next Transmit and Next Receive Pointer */ + writel((unsigned int)pDesc->rx_data_pt, + ATMEL_BASE_SPI0 + AT91_SPI_RNPR); + writel((unsigned int)pDesc->tx_data_pt, + ATMEL_BASE_SPI0 + AT91_SPI_TNPR); + + /* Intialize the Next Transmit and Next Receive Counters */ + writel(pDesc->rx_data_size, + ATMEL_BASE_SPI0 + AT91_SPI_RNCR); + writel(pDesc->tx_data_size, + ATMEL_BASE_SPI0 + AT91_SPI_TNCR); + } + + /* arm simple, non interrupt dependent timer */ + timebase = get_timer(0); + timeout = 0; + + writel(AT91_SPI_TXTEN + AT91_SPI_RXTEN, + ATMEL_BASE_SPI0 + AT91_SPI_PTCR); + while (!(readl(ATMEL_BASE_SPI0 + AT91_SPI_SR) & AT91_SPI_RXBUFF) && + ((timeout = get_timer(timebase)) < CONFIG_SYS_SPI_WRITE_TOUT)) + ; + writel(AT91_SPI_TXTDIS + AT91_SPI_RXTDIS, + ATMEL_BASE_SPI0 + AT91_SPI_PTCR); + pDesc->state = IDLE; + + if (timeout >= CONFIG_SYS_SPI_WRITE_TOUT) { + printf("Error Timeout\n\r"); + return DATAFLASH_ERROR; + } + + return DATAFLASH_OK; +} diff --git a/qemu/roms/u-boot/drivers/spi/atmel_spi.c b/qemu/roms/u-boot/drivers/spi/atmel_spi.c new file mode 100644 index 000000000..ed6278b86 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/atmel_spi.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include + +#include + +#include +#include + +#include "atmel_spi.h" + +static int spi_has_wdrbt(struct atmel_spi_slave *slave) +{ + unsigned int ver; + + ver = spi_readl(slave, VERSION); + + return (ATMEL_SPI_VERSION_REV(ver) >= 0x210); +} + +void spi_init() +{ + +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct atmel_spi_slave *as; + unsigned int scbr; + u32 csrx; + void *regs; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + switch (bus) { + case 0: + regs = (void *)ATMEL_BASE_SPI0; + break; +#ifdef ATMEL_BASE_SPI1 + case 1: + regs = (void *)ATMEL_BASE_SPI1; + break; +#endif +#ifdef ATMEL_BASE_SPI2 + case 2: + regs = (void *)ATMEL_BASE_SPI2; + break; +#endif +#ifdef ATMEL_BASE_SPI3 + case 3: + regs = (void *)ATMEL_BASE_SPI3; + break; +#endif + default: + return NULL; + } + + + scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz; + if (scbr > ATMEL_SPI_CSRx_SCBR_MAX) + /* Too low max SCK rate */ + return NULL; + if (scbr < 1) + scbr = 1; + + csrx = ATMEL_SPI_CSRx_SCBR(scbr); + csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8); + if (!(mode & SPI_CPHA)) + csrx |= ATMEL_SPI_CSRx_NCPHA; + if (mode & SPI_CPOL) + csrx |= ATMEL_SPI_CSRx_CPOL; + + as = spi_alloc_slave(struct atmel_spi_slave, bus, cs); + if (!as) + return NULL; + + as->regs = regs; + as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS + | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf); + if (spi_has_wdrbt(as)) + as->mr |= ATMEL_SPI_MR_WDRBT; + + spi_writel(as, CSR(cs), csrx); + + return &as->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + + free(as); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + + /* Enable the SPI hardware */ + spi_writel(as, CR, ATMEL_SPI_CR_SPIEN); + + /* + * Select the slave. This should set SCK to the correct + * initial state, etc. + */ + spi_writel(as, MR, as->mr); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + + /* Disable the SPI hardware */ + spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + unsigned int len_tx; + unsigned int len_rx; + unsigned int len; + u32 status; + const u8 *txp = dout; + u8 *rxp = din; + u8 value; + + if (bitlen == 0) + /* Finish any previously submitted transfers */ + goto out; + + /* + * TODO: The controller can do non-multiple-of-8 bit + * transfers, but this driver currently doesn't support it. + * + * It's also not clear how such transfers are supposed to be + * represented as a stream of bytes...this is a limitation of + * the current SPI interface. + */ + if (bitlen % 8) { + /* Errors always terminate an ongoing transfer */ + flags |= SPI_XFER_END; + goto out; + } + + len = bitlen / 8; + + /* + * The controller can do automatic CS control, but it is + * somewhat quirky, and it doesn't really buy us much anyway + * in the context of U-Boot. + */ + if (flags & SPI_XFER_BEGIN) { + spi_cs_activate(slave); + /* + * sometimes the RDR is not empty when we get here, + * in theory that should not happen, but it DOES happen. + * Read it here to be on the safe side. + * That also clears the OVRES flag. Required if the + * following loop exits due to OVRES! + */ + spi_readl(as, RDR); + } + + for (len_tx = 0, len_rx = 0; len_rx < len; ) { + status = spi_readl(as, SR); + + if (status & ATMEL_SPI_SR_OVRES) + return -1; + + if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) { + if (txp) + value = *txp++; + else + value = 0; + spi_writel(as, TDR, value); + len_tx++; + } + if (status & ATMEL_SPI_SR_RDRF) { + value = spi_readl(as, RDR); + if (rxp) + *rxp++ = value; + len_rx++; + } + } + +out: + if (flags & SPI_XFER_END) { + /* + * Wait until the transfer is completely done before + * we deactivate CS. + */ + do { + status = spi_readl(as, SR); + } while (!(status & ATMEL_SPI_SR_TXEMPTY)); + + spi_cs_deactivate(slave); + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/atmel_spi.h b/qemu/roms/u-boot/drivers/spi/atmel_spi.h new file mode 100644 index 000000000..d2409454f --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/atmel_spi.h @@ -0,0 +1,96 @@ +/* + * Register definitions for the Atmel AT32/AT91 SPI Controller + */ + +/* Register offsets */ +#define ATMEL_SPI_CR 0x0000 +#define ATMEL_SPI_MR 0x0004 +#define ATMEL_SPI_RDR 0x0008 +#define ATMEL_SPI_TDR 0x000c +#define ATMEL_SPI_SR 0x0010 +#define ATMEL_SPI_IER 0x0014 +#define ATMEL_SPI_IDR 0x0018 +#define ATMEL_SPI_IMR 0x001c +#define ATMEL_SPI_CSR(x) (0x0030 + 4 * (x)) +#define ATMEL_SPI_VERSION 0x00fc + +/* Bits in CR */ +#define ATMEL_SPI_CR_SPIEN (1 << 0) +#define ATMEL_SPI_CR_SPIDIS (1 << 1) +#define ATMEL_SPI_CR_SWRST (1 << 7) +#define ATMEL_SPI_CR_LASTXFER (1 << 24) + +/* Bits in MR */ +#define ATMEL_SPI_MR_MSTR (1 << 0) +#define ATMEL_SPI_MR_PS (1 << 1) +#define ATMEL_SPI_MR_PCSDEC (1 << 2) +#define ATMEL_SPI_MR_FDIV (1 << 3) +#define ATMEL_SPI_MR_MODFDIS (1 << 4) +#define ATMEL_SPI_MR_WDRBT (1 << 5) +#define ATMEL_SPI_MR_LLB (1 << 7) +#define ATMEL_SPI_MR_PCS(x) (((x) & 15) << 16) +#define ATMEL_SPI_MR_DLYBCS(x) ((x) << 24) + +/* Bits in RDR */ +#define ATMEL_SPI_RDR_RD(x) (x) +#define ATMEL_SPI_RDR_PCS(x) ((x) << 16) + +/* Bits in TDR */ +#define ATMEL_SPI_TDR_TD(x) (x) +#define ATMEL_SPI_TDR_PCS(x) ((x) << 16) +#define ATMEL_SPI_TDR_LASTXFER (1 << 24) + +/* Bits in SR/IER/IDR/IMR */ +#define ATMEL_SPI_SR_RDRF (1 << 0) +#define ATMEL_SPI_SR_TDRE (1 << 1) +#define ATMEL_SPI_SR_MODF (1 << 2) +#define ATMEL_SPI_SR_OVRES (1 << 3) +#define ATMEL_SPI_SR_ENDRX (1 << 4) +#define ATMEL_SPI_SR_ENDTX (1 << 5) +#define ATMEL_SPI_SR_RXBUFF (1 << 6) +#define ATMEL_SPI_SR_TXBUFE (1 << 7) +#define ATMEL_SPI_SR_NSSR (1 << 8) +#define ATMEL_SPI_SR_TXEMPTY (1 << 9) +#define ATMEL_SPI_SR_SPIENS (1 << 16) + +/* Bits in CSRx */ +#define ATMEL_SPI_CSRx_CPOL (1 << 0) +#define ATMEL_SPI_CSRx_NCPHA (1 << 1) +#define ATMEL_SPI_CSRx_CSAAT (1 << 3) +#define ATMEL_SPI_CSRx_BITS(x) ((x) << 4) +#define ATMEL_SPI_CSRx_SCBR(x) ((x) << 8) +#define ATMEL_SPI_CSRx_SCBR_MAX 0xff +#define ATMEL_SPI_CSRx_DLYBS(x) ((x) << 16) +#define ATMEL_SPI_CSRx_DLYBCT(x) ((x) << 24) + +/* Bits in VERSION */ +#define ATMEL_SPI_VERSION_REV(x) ((x) & 0xfff) +#define ATMEL_SPI_VERSION_MFN(x) ((x) << 16) + +/* Constants for CSRx:BITS */ +#define ATMEL_SPI_BITS_8 0 +#define ATMEL_SPI_BITS_9 1 +#define ATMEL_SPI_BITS_10 2 +#define ATMEL_SPI_BITS_11 3 +#define ATMEL_SPI_BITS_12 4 +#define ATMEL_SPI_BITS_13 5 +#define ATMEL_SPI_BITS_14 6 +#define ATMEL_SPI_BITS_15 7 +#define ATMEL_SPI_BITS_16 8 + +struct atmel_spi_slave { + struct spi_slave slave; + void *regs; + u32 mr; +}; + +static inline struct atmel_spi_slave *to_atmel_spi(struct spi_slave *slave) +{ + return container_of(slave, struct atmel_spi_slave, slave); +} + +/* Register access macros */ +#define spi_readl(as, reg) \ + readl(as->regs + ATMEL_SPI_##reg) +#define spi_writel(as, reg, value) \ + writel(value, as->regs + ATMEL_SPI_##reg) diff --git a/qemu/roms/u-boot/drivers/spi/bfin_spi.c b/qemu/roms/u-boot/drivers/spi/bfin_spi.c new file mode 100644 index 000000000..71a31d012 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/bfin_spi.c @@ -0,0 +1,308 @@ +/* + * Driver for Blackfin On-Chip SPI device + * + * Copyright (c) 2005-2010 Analog Devices Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/*#define DEBUG*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +struct bfin_spi_slave { + struct spi_slave slave; + void *mmr_base; + u16 ctl, baud, flg; +}; + +#define MAKE_SPI_FUNC(mmr, off) \ +static inline void write_##mmr(struct bfin_spi_slave *bss, u16 val) { bfin_write16(bss->mmr_base + off, val); } \ +static inline u16 read_##mmr(struct bfin_spi_slave *bss) { return bfin_read16(bss->mmr_base + off); } +MAKE_SPI_FUNC(SPI_CTL, 0x00) +MAKE_SPI_FUNC(SPI_FLG, 0x04) +MAKE_SPI_FUNC(SPI_STAT, 0x08) +MAKE_SPI_FUNC(SPI_TDBR, 0x0c) +MAKE_SPI_FUNC(SPI_RDBR, 0x10) +MAKE_SPI_FUNC(SPI_BAUD, 0x14) + +#define to_bfin_spi_slave(s) container_of(s, struct bfin_spi_slave, slave) + +#define gpio_cs(cs) ((cs) - MAX_CTRL_CS) +#ifdef CONFIG_BFIN_SPI_GPIO_CS +# define is_gpio_cs(cs) ((cs) > MAX_CTRL_CS) +#else +# define is_gpio_cs(cs) 0 +#endif + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + if (is_gpio_cs(cs)) + return gpio_is_valid(gpio_cs(cs)); + else + return (cs >= 1 && cs <= MAX_CTRL_CS); +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + + if (is_gpio_cs(slave->cs)) { + unsigned int cs = gpio_cs(slave->cs); + gpio_set_value(cs, bss->flg); + debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); + } else { + write_SPI_FLG(bss, + (read_SPI_FLG(bss) & + ~((!bss->flg << 8) << slave->cs)) | + (1 << slave->cs)); + debug("%s: SPI_FLG:%x\n", __func__, read_SPI_FLG(bss)); + } + + SSYNC(); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + + if (is_gpio_cs(slave->cs)) { + unsigned int cs = gpio_cs(slave->cs); + gpio_set_value(cs, !bss->flg); + debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); + } else { + u16 flg; + + /* make sure we force the cs to deassert rather than let the + * pin float back up. otherwise, exact timings may not be + * met some of the time leading to random behavior (ugh). + */ + flg = read_SPI_FLG(bss) | ((!bss->flg << 8) << slave->cs); + write_SPI_FLG(bss, flg); + SSYNC(); + debug("%s: SPI_FLG:%x\n", __func__, read_SPI_FLG(bss)); + + flg &= ~(1 << slave->cs); + write_SPI_FLG(bss, flg); + debug("%s: SPI_FLG:%x\n", __func__, read_SPI_FLG(bss)); + } + + SSYNC(); +} + +void spi_init() +{ +} + +#ifdef SPI_CTL +# define SPI0_CTL SPI_CTL +#endif + +#define SPI_PINS(n) \ + [n] = { 0, P_SPI##n##_SCK, P_SPI##n##_MISO, P_SPI##n##_MOSI, 0 } +static unsigned short pins[][5] = { +#ifdef SPI0_CTL + SPI_PINS(0), +#endif +#ifdef SPI1_CTL + SPI_PINS(1), +#endif +#ifdef SPI2_CTL + SPI_PINS(2), +#endif +}; + +#define SPI_CS_PINS(n) \ + [n] = { \ + P_SPI##n##_SSEL1, P_SPI##n##_SSEL2, P_SPI##n##_SSEL3, \ + P_SPI##n##_SSEL4, P_SPI##n##_SSEL5, P_SPI##n##_SSEL6, \ + P_SPI##n##_SSEL7, \ + } +static const unsigned short cs_pins[][7] = { +#ifdef SPI0_CTL + SPI_CS_PINS(0), +#endif +#ifdef SPI1_CTL + SPI_CS_PINS(1), +#endif +#ifdef SPI2_CTL + SPI_CS_PINS(2), +#endif +}; + +void spi_set_speed(struct spi_slave *slave, uint hz) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + ulong clk; + u32 baud; + + clk = get_spi_clk(); + /* baud should be rounded up */ + baud = DIV_ROUND_UP(clk, 2 * hz); + if (baud < 2) + baud = 2; + else if (baud > (u16)-1) + baud = -1; + bss->baud = baud; +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct bfin_spi_slave *bss; + u32 mmr_base; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + switch (bus) { +#ifdef SPI0_CTL + case 0: + mmr_base = SPI0_CTL; break; +#endif +#ifdef SPI1_CTL + case 1: + mmr_base = SPI1_CTL; break; +#endif +#ifdef SPI2_CTL + case 2: + mmr_base = SPI2_CTL; break; +#endif + default: + debug("%s: invalid bus %u\n", __func__, bus); + return NULL; + } + + bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs); + if (!bss) + return NULL; + + bss->mmr_base = (void *)mmr_base; + bss->ctl = SPE | MSTR | TDBR_CORE; + if (mode & SPI_CPHA) bss->ctl |= CPHA; + if (mode & SPI_CPOL) bss->ctl |= CPOL; + if (mode & SPI_LSB_FIRST) bss->ctl |= LSBF; + bss->flg = mode & SPI_CS_HIGH ? 1 : 0; + spi_set_speed(&bss->slave, max_hz); + + debug("%s: bus:%i cs:%i mmr:%x ctl:%x baud:%i flg:%i\n", __func__, + bus, cs, mmr_base, bss->ctl, bss->baud, bss->flg); + + return &bss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + free(bss); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + + if (is_gpio_cs(slave->cs)) { + unsigned int cs = gpio_cs(slave->cs); + gpio_request(cs, "bfin-spi"); + gpio_direction_output(cs, !bss->flg); + pins[slave->bus][0] = P_DONTCARE; + } else + pins[slave->bus][0] = cs_pins[slave->bus][slave->cs - 1]; + peripheral_request_list(pins[slave->bus], "bfin-spi"); + + write_SPI_CTL(bss, bss->ctl); + write_SPI_BAUD(bss, bss->baud); + SSYNC(); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + + peripheral_free_list(pins[slave->bus]); + if (is_gpio_cs(slave->cs)) + gpio_free(gpio_cs(slave->cs)); + + write_SPI_CTL(bss, 0); + SSYNC(); +} + +#ifndef CONFIG_BFIN_SPI_IDLE_VAL +# define CONFIG_BFIN_SPI_IDLE_VAL 0xff +#endif + +static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx, + uint bytes) +{ + /* discard invalid data and clear RXS */ + read_SPI_RDBR(bss); + /* todo: take advantage of hardware fifos */ + while (bytes--) { + u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL); + debug("%s: tx:%x ", __func__, value); + write_SPI_TDBR(bss, value); + SSYNC(); + while ((read_SPI_STAT(bss) & TXS)) + if (ctrlc()) + return -1; + while (!(read_SPI_STAT(bss) & SPIF)) + if (ctrlc()) + return -1; + while (!(read_SPI_STAT(bss) & RXS)) + if (ctrlc()) + return -1; + value = read_SPI_RDBR(bss); + if (rx) + *rx++ = value; + debug("rx:%x\n", value); + } + + return 0; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + const u8 *tx = dout; + u8 *rx = din; + uint bytes = bitlen / 8; + int ret = 0; + + debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, + slave->bus, slave->cs, bitlen, bytes, flags); + + if (bitlen == 0) + goto done; + + /* we can only do 8 bit transfers */ + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto done; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + ret = spi_pio_xfer(bss, tx, rx, bytes); + + done: + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/spi/bfin_spi6xx.c b/qemu/roms/u-boot/drivers/spi/bfin_spi6xx.c new file mode 100644 index 000000000..eba01d16f --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/bfin_spi6xx.c @@ -0,0 +1,304 @@ +/* + * Analog Devices SPI3 controller driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +struct bfin_spi_slave { + struct spi_slave slave; + u32 control, clock; + struct bfin_spi_regs *regs; + int cs_pol; +}; + +#define to_bfin_spi_slave(s) container_of(s, struct bfin_spi_slave, slave) + +#define gpio_cs(cs) ((cs) - MAX_CTRL_CS) +#ifdef CONFIG_BFIN_SPI_GPIO_CS +# define is_gpio_cs(cs) ((cs) > MAX_CTRL_CS) +#else +# define is_gpio_cs(cs) 0 +#endif + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + if (is_gpio_cs(cs)) + return gpio_is_valid(gpio_cs(cs)); + else + return (cs >= 1 && cs <= MAX_CTRL_CS); +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + + if (is_gpio_cs(slave->cs)) { + unsigned int cs = gpio_cs(slave->cs); + gpio_set_value(cs, bss->cs_pol); + } else { + u32 ssel; + ssel = bfin_read32(&bss->regs->ssel); + ssel |= 1 << slave->cs; + if (bss->cs_pol) + ssel |= (1 << 8) << slave->cs; + else + ssel &= ~((1 << 8) << slave->cs); + bfin_write32(&bss->regs->ssel, ssel); + } + + SSYNC(); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + + if (is_gpio_cs(slave->cs)) { + unsigned int cs = gpio_cs(slave->cs); + gpio_set_value(cs, !bss->cs_pol); + } else { + u32 ssel; + ssel = bfin_read32(&bss->regs->ssel); + if (bss->cs_pol) + ssel &= ~((1 << 8) << slave->cs); + else + ssel |= (1 << 8) << slave->cs; + /* deassert cs */ + bfin_write32(&bss->regs->ssel, ssel); + SSYNC(); + /* disable cs */ + ssel &= ~(1 << slave->cs); + bfin_write32(&bss->regs->ssel, ssel); + } + + SSYNC(); +} + +void spi_init() +{ +} + +#define SPI_PINS(n) \ + { 0, P_SPI##n##_SCK, P_SPI##n##_MISO, P_SPI##n##_MOSI, 0 } +static unsigned short pins[][5] = { +#ifdef SPI0_REGBASE + [0] = SPI_PINS(0), +#endif +#ifdef SPI1_REGBASE + [1] = SPI_PINS(1), +#endif +#ifdef SPI2_REGBASE + [2] = SPI_PINS(2), +#endif +}; + +#define SPI_CS_PINS(n) \ + { \ + P_SPI##n##_SSEL1, P_SPI##n##_SSEL2, P_SPI##n##_SSEL3, \ + P_SPI##n##_SSEL4, P_SPI##n##_SSEL5, P_SPI##n##_SSEL6, \ + P_SPI##n##_SSEL7, \ + } +static const unsigned short cs_pins[][7] = { +#ifdef SPI0_REGBASE + [0] = SPI_CS_PINS(0), +#endif +#ifdef SPI1_REGBASE + [1] = SPI_CS_PINS(1), +#endif +#ifdef SPI2_REGBASE + [2] = SPI_CS_PINS(2), +#endif +}; + +void spi_set_speed(struct spi_slave *slave, uint hz) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + ulong clk; + u32 clock; + + clk = get_spi_clk(); + clock = clk / hz; + if (clock) + clock--; + bss->clock = clock; +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct bfin_spi_slave *bss; + u32 reg_base; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + switch (bus) { +#ifdef SPI0_REGBASE + case 0: + reg_base = SPI0_REGBASE; + break; +#endif +#ifdef SPI1_REGBASE + case 1: + reg_base = SPI1_REGBASE; + break; +#endif +#ifdef SPI2_REGBASE + case 2: + reg_base = SPI2_REGBASE; + break; +#endif + default: + debug("%s: invalid bus %u\n", __func__, bus); + return NULL; + } + + bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs); + if (!bss) + return NULL; + + bss->regs = (struct bfin_spi_regs *)reg_base; + bss->control = SPI_CTL_EN | SPI_CTL_MSTR; + if (mode & SPI_CPHA) + bss->control |= SPI_CTL_CPHA; + if (mode & SPI_CPOL) + bss->control |= SPI_CTL_CPOL; + if (mode & SPI_LSB_FIRST) + bss->control |= SPI_CTL_LSBF; + bss->control &= ~SPI_CTL_ASSEL; + bss->cs_pol = mode & SPI_CS_HIGH ? 1 : 0; + spi_set_speed(&bss->slave, max_hz); + + return &bss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + free(bss); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + + if (is_gpio_cs(slave->cs)) { + unsigned int cs = gpio_cs(slave->cs); + gpio_request(cs, "bfin-spi"); + gpio_direction_output(cs, !bss->cs_pol); + pins[slave->bus][0] = P_DONTCARE; + } else + pins[slave->bus][0] = cs_pins[slave->bus][slave->cs - 1]; + peripheral_request_list(pins[slave->bus], "bfin-spi"); + + bfin_write32(&bss->regs->control, bss->control); + bfin_write32(&bss->regs->clock, bss->clock); + bfin_write32(&bss->regs->delay, 0x0); + bfin_write32(&bss->regs->rx_control, SPI_RXCTL_REN); + bfin_write32(&bss->regs->tx_control, SPI_TXCTL_TEN | SPI_TXCTL_TTI); + SSYNC(); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + + peripheral_free_list(pins[slave->bus]); + if (is_gpio_cs(slave->cs)) + gpio_free(gpio_cs(slave->cs)); + + bfin_write32(&bss->regs->rx_control, 0x0); + bfin_write32(&bss->regs->tx_control, 0x0); + bfin_write32(&bss->regs->control, 0x0); + SSYNC(); +} + +#ifndef CONFIG_BFIN_SPI_IDLE_VAL +# define CONFIG_BFIN_SPI_IDLE_VAL 0xff +#endif + +static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx, + uint bytes) +{ + /* discard invalid rx data and empty rfifo */ + while (!(bfin_read32(&bss->regs->status) & SPI_STAT_RFE)) + bfin_read32(&bss->regs->rfifo); + + while (bytes--) { + u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL); + debug("%s: tx:%x ", __func__, value); + bfin_write32(&bss->regs->tfifo, value); + SSYNC(); + while (bfin_read32(&bss->regs->status) & SPI_STAT_RFE) + if (ctrlc()) + return -1; + value = bfin_read32(&bss->regs->rfifo); + if (rx) + *rx++ = value; + debug("rx:%x\n", value); + } + + return 0; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + const u8 *tx = dout; + u8 *rx = din; + uint bytes = bitlen / 8; + int ret = 0; + + debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, + slave->bus, slave->cs, bitlen, bytes, flags); + + if (bitlen == 0) + goto done; + + /* we can only do 8 bit transfers */ + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto done; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + ret = spi_pio_xfer(bss, tx, rx, bytes); + + done: + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/spi/cf_qspi.c b/qemu/roms/u-boot/drivers/spi/cf_qspi.c new file mode 100644 index 000000000..6b8563366 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/cf_qspi.c @@ -0,0 +1,355 @@ +/* + * Freescale Coldfire Queued SPI driver + * + * NOTE: + * This driver is written to transfer 8 bit at-a-time and uses the dedicated + * SPI slave select pins as bit-banged GPIO to work with spi_flash subsystem. + * + * Copyright (C) 2011 Ruggedcom, Inc. + * Richard Retanubun (richardretanubun@freescale.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define clamp(x, low, high) (min(max(low, x), high)) +#define to_cf_qspi_slave(s) container_of(s, struct cf_qspi_slave, s) + +struct cf_qspi_slave { + struct spi_slave slave; /* Specific bus:cs ID for each device */ + qspi_t *regs; /* Pointer to SPI controller registers */ + u16 qmr; /* QMR: Queued Mode Register */ + u16 qwr; /* QWR: Queued Wrap Register */ + u16 qcr; /* QCR: Queued Command Ram */ +}; + +/* Register write wrapper functions */ +static void write_qmr(volatile qspi_t *qspi, u16 val) { qspi->mr = val; } +static void write_qdlyr(volatile qspi_t *qspi, u16 val) { qspi->dlyr = val; } +static void write_qwr(volatile qspi_t *qspi, u16 val) { qspi->wr = val; } +static void write_qir(volatile qspi_t *qspi, u16 val) { qspi->ir = val; } +static void write_qar(volatile qspi_t *qspi, u16 val) { qspi->ar = val; } +static void write_qdr(volatile qspi_t *qspi, u16 val) { qspi->dr = val; } +/* Register read wrapper functions */ +static u16 read_qdlyr(volatile qspi_t *qspi) { return qspi->dlyr; } +static u16 read_qwr(volatile qspi_t *qspi) { return qspi->wr; } +static u16 read_qir(volatile qspi_t *qspi) { return qspi->ir; } +static u16 read_qdr(volatile qspi_t *qspi) { return qspi->dr; } + +/* These call points may be different for each ColdFire CPU */ +extern void cfspi_port_conf(void); +static void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high); +static void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high); + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} +void spi_release_bus(struct spi_slave *slave) +{ +} + +__attribute__((weak)) +void spi_init(void) +{ + cfspi_port_conf(); +} + +__attribute__((weak)) +void spi_cs_activate(struct spi_slave *slave) +{ + struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + + cfspi_cs_activate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV)); +} + +__attribute__((weak)) +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + + cfspi_cs_deactivate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV)); +} + +__attribute__((weak)) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + /* Only 1 bus and 4 chipselect per controller */ + if (bus == 0 && (cs >= 0 && cs < 4)) + return 1; + else + return 0; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + + free(dev); +} + +/* Translate information given by spi_setup_slave to members of cf_qspi_slave */ +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct cf_qspi_slave *dev = NULL; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + dev = spi_alloc_slave(struct cf_qspi_slave, bus, cs); + if (!dev) + return NULL; + + /* Initialize to known value */ + dev->regs = (qspi_t *)MMAP_QSPI; + dev->qmr = 0; + dev->qwr = 0; + dev->qcr = 0; + + + /* Map max_hz to QMR[BAUD] */ + if (max_hz == 0) /* Go as fast as possible */ + dev->qmr = 2u; + else /* Get the closest baud rate */ + dev->qmr = clamp(((gd->bus_clk >> 2) + max_hz - 1)/max_hz, + 2u, 255u); + + /* Map mode to QMR[CPOL] and QMR[CPHA] */ + if (mode & SPI_CPOL) + dev->qmr |= QSPI_QMR_CPOL; + + if (mode & SPI_CPHA) + dev->qmr |= QSPI_QMR_CPHA; + + /* Hardcode bit length to 8 bit per transter */ + dev->qmr |= QSPI_QMR_BITS_8; + + /* Set QMR[MSTR] to enable QSPI as master */ + dev->qmr |= QSPI_QMR_MSTR; + + /* + * Set QCR and QWR to default values for spi flash operation. + * If more custom QCR and QRW are needed, overload mode variable + */ + dev->qcr = (QSPI_QDR_CONT | QSPI_QDR_BITSE); + + if (!(mode & SPI_CS_HIGH)) + dev->qwr |= QSPI_QWR_CSIV; + + return &dev->slave; +} + +/* Transfer 8 bit at a time */ +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + volatile qspi_t *qspi = dev->regs; + u8 *txbuf = (u8 *)dout; + u8 *rxbuf = (u8 *)din; + u32 count = DIV_ROUND_UP(bitlen, 8); + u32 n, i = 0; + + /* Sanitize arguments */ + if (slave == NULL) { + printf("%s: NULL slave ptr\n", __func__); + return -1; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* There is something to send, lets process it. spi_xfer is also called + * just to toggle chip select, so bitlen of 0 is valid */ + if (count > 0) { + /* + * NOTE: Since chip select is driven as a bit-bang-ed GPIO + * using spi_cs_activate() and spi_cs_deactivate(), + * the chip select settings inside the controller + * (i.e. QCR[CONT] and QWR[CSIV]) are moot. The bits are set to + * keep the controller settings consistent with the actual + * operation of the bus. + */ + + /* Write the slave device's settings for the controller.*/ + write_qmr(qspi, dev->qmr); + write_qwr(qspi, dev->qwr); + + /* Limit transfer to 16 at a time */ + n = min(count, 16u); + do { + /* Setup queue end point */ + write_qwr(qspi, ((read_qwr(qspi) & QSPI_QWR_ENDQP_MASK) + | QSPI_QWR_ENDQP((n-1)))); + + /* Write Command RAM */ + write_qar(qspi, QSPI_QAR_CMD); + for (i = 0; i < n; ++i) + write_qdr(qspi, dev->qcr); + + /* Write TxBuf, if none given, fill with ZEROes */ + write_qar(qspi, QSPI_QAR_TRANS); + if (txbuf) { + for (i = 0; i < n; ++i) + write_qdr(qspi, *txbuf++); + } else { + for (i = 0; i < n; ++i) + write_qdr(qspi, 0); + } + + /* Clear QIR[SPIF] by writing a 1 to it */ + write_qir(qspi, read_qir(qspi) | QSPI_QIR_SPIF); + /* Set QDLYR[SPE] to start sending */ + write_qdlyr(qspi, read_qdlyr(qspi) | QSPI_QDLYR_SPE); + + /* Poll QIR[SPIF] for transfer completion */ + while ((read_qir(qspi) & QSPI_QIR_SPIF) != 1) + udelay(1); + + /* If given read RxBuf, load data to it */ + if (rxbuf) { + write_qar(qspi, QSPI_QAR_RECV); + for (i = 0; i < n; ++i) + *rxbuf++ = read_qdr(qspi); + } + + /* Decrement count */ + count -= n; + } while (count); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} + +/* Each MCF CPU may have different pin assignments for chip selects. */ +#if defined(CONFIG_M5271) +/* Assert chip select, val = [1|0] , dir = out, mode = GPIO */ +void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high) +{ + debug("%s: bus %d cs %d cs_active_high %d\n", + __func__, bus, cs, cs_active_high); + + switch (cs) { + case 0: /* QSPI_CS[0] = PQSPI[3] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08); + else + mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7); + + mbar_writeByte(MCF_GPIO_PDDR_QSPI, + mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x08); + + mbar_writeByte(MCF_GPIO_PAR_QSPI, + mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF); + break; + case 1: /* QSPI_CS[1] = PQSPI[4] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10); + else + mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF); + + mbar_writeByte(MCF_GPIO_PDDR_QSPI, + mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x10); + + mbar_writeByte(MCF_GPIO_PAR_QSPI, + mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F); + break; + case 2: /* QSPI_CS[2] = PTIMER[7] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80); + else + mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F); + + mbar_writeByte(MCF_GPIO_PDDR_TIMER, + mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x80); + + mbar_writeShort(MCF_GPIO_PAR_TIMER, + mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF); + break; + case 3: /* QSPI_CS[3] = PTIMER[3] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08); + else + mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7); + + mbar_writeByte(MCF_GPIO_PDDR_TIMER, + mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x08); + + mbar_writeShort(MCF_GPIO_PAR_TIMER, + mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F); + break; + } +} + +/* Deassert chip select, val = [1|0], dir = in, mode = GPIO + * direction set as IN to undrive the pin, external pullup/pulldown will bring + * bus to deassert state. + */ +void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high) +{ + debug("%s: bus %d cs %d cs_active_high %d\n", + __func__, bus, cs, cs_active_high); + + switch (cs) { + case 0: /* QSPI_CS[0] = PQSPI[3] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7); + else + mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08); + + mbar_writeByte(MCF_GPIO_PDDR_QSPI, + mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xF7); + + mbar_writeByte(MCF_GPIO_PAR_QSPI, + mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF); + break; + case 1: /* QSPI_CS[1] = PQSPI[4] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF); + else + mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10); + + mbar_writeByte(MCF_GPIO_PDDR_QSPI, + mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xEF); + + mbar_writeByte(MCF_GPIO_PAR_QSPI, + mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F); + break; + case 2: /* QSPI_CS[2] = PTIMER[7] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F); + else + mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80); + + mbar_writeByte(MCF_GPIO_PDDR_TIMER, + mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0x7F); + + mbar_writeShort(MCF_GPIO_PAR_TIMER, + mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF); + break; + case 3: /* QSPI_CS[3] = PTIMER[3] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7); + else + mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08); + + mbar_writeByte(MCF_GPIO_PDDR_TIMER, + mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0xF7); + + mbar_writeShort(MCF_GPIO_PAR_TIMER, + mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F); + break; + } +} +#endif /* CONFIG_M5271 */ diff --git a/qemu/roms/u-boot/drivers/spi/cf_spi.c b/qemu/roms/u-boot/drivers/spi/cf_spi.c new file mode 100644 index 000000000..879a809cb --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/cf_spi.c @@ -0,0 +1,347 @@ +/* + * + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 2004-2009 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +struct cf_spi_slave { + struct spi_slave slave; + uint baudrate; + int charbit; +}; + +int cfspi_xfer(struct spi_slave *slave, uint bitlen, const void *dout, + void *din, ulong flags); +struct spi_slave *cfspi_setup_slave(struct cf_spi_slave *cfslave, uint mode); +void cfspi_init(void); +void cfspi_tx(u32 ctrl, u16 data); +u16 cfspi_rx(void); + +extern void cfspi_port_conf(void); +extern int cfspi_claim_bus(uint bus, uint cs); +extern void cfspi_release_bus(uint bus, uint cs); + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SPI_IDLE_VAL +#if defined(CONFIG_SPI_MMC) +#define CONFIG_SPI_IDLE_VAL 0xFFFF +#else +#define CONFIG_SPI_IDLE_VAL 0x0 +#endif +#endif + +#if defined(CONFIG_CF_DSPI) +/* DSPI specific mode */ +#define SPI_MODE_MOD 0x00200000 +#define SPI_DBLRATE 0x00100000 + +void cfspi_init(void) +{ + volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI; + + cfspi_port_conf(); /* port configuration */ + + dspi->mcr = DSPI_MCR_MSTR | DSPI_MCR_CSIS7 | DSPI_MCR_CSIS6 | + DSPI_MCR_CSIS5 | DSPI_MCR_CSIS4 | DSPI_MCR_CSIS3 | + DSPI_MCR_CSIS2 | DSPI_MCR_CSIS1 | DSPI_MCR_CSIS0 | + DSPI_MCR_CRXF | DSPI_MCR_CTXF; + + /* Default setting in platform configuration */ +#ifdef CONFIG_SYS_DSPI_CTAR0 + dspi->ctar[0] = CONFIG_SYS_DSPI_CTAR0; +#endif +#ifdef CONFIG_SYS_DSPI_CTAR1 + dspi->ctar[1] = CONFIG_SYS_DSPI_CTAR1; +#endif +#ifdef CONFIG_SYS_DSPI_CTAR2 + dspi->ctar[2] = CONFIG_SYS_DSPI_CTAR2; +#endif +#ifdef CONFIG_SYS_DSPI_CTAR3 + dspi->ctar[3] = CONFIG_SYS_DSPI_CTAR3; +#endif +#ifdef CONFIG_SYS_DSPI_CTAR4 + dspi->ctar[4] = CONFIG_SYS_DSPI_CTAR4; +#endif +#ifdef CONFIG_SYS_DSPI_CTAR5 + dspi->ctar[5] = CONFIG_SYS_DSPI_CTAR5; +#endif +#ifdef CONFIG_SYS_DSPI_CTAR6 + dspi->ctar[6] = CONFIG_SYS_DSPI_CTAR6; +#endif +#ifdef CONFIG_SYS_DSPI_CTAR7 + dspi->ctar[7] = CONFIG_SYS_DSPI_CTAR7; +#endif +} + +void cfspi_tx(u32 ctrl, u16 data) +{ + volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI; + + while ((dspi->sr & 0x0000F000) >= 4) ; + + dspi->tfr = (ctrl | data); +} + +u16 cfspi_rx(void) +{ + volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI; + + while ((dspi->sr & 0x000000F0) == 0) ; + + return (dspi->rfr & 0xFFFF); +} + +int cfspi_xfer(struct spi_slave *slave, uint bitlen, const void *dout, + void *din, ulong flags) +{ + struct cf_spi_slave *cfslave = (struct cf_spi_slave *)slave; + u16 *spi_rd16 = NULL, *spi_wr16 = NULL; + u8 *spi_rd = NULL, *spi_wr = NULL; + static u32 ctrl = 0; + uint len = bitlen >> 3; + + if (cfslave->charbit == 16) { + bitlen >>= 1; + spi_wr16 = (u16 *) dout; + spi_rd16 = (u16 *) din; + } else { + spi_wr = (u8 *) dout; + spi_rd = (u8 *) din; + } + + if ((flags & SPI_XFER_BEGIN) == SPI_XFER_BEGIN) + ctrl |= DSPI_TFR_CONT; + + ctrl = (ctrl & 0xFF000000) | ((1 << slave->cs) << 16); + + if (len > 1) { + int tmp_len = len - 1; + while (tmp_len--) { + if (dout != NULL) { + if (cfslave->charbit == 16) + cfspi_tx(ctrl, *spi_wr16++); + else + cfspi_tx(ctrl, *spi_wr++); + cfspi_rx(); + } + + if (din != NULL) { + cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL); + if (cfslave->charbit == 16) + *spi_rd16++ = cfspi_rx(); + else + *spi_rd++ = cfspi_rx(); + } + } + + len = 1; /* remaining byte */ + } + + if ((flags & SPI_XFER_END) == SPI_XFER_END) + ctrl &= ~DSPI_TFR_CONT; + + if (len) { + if (dout != NULL) { + if (cfslave->charbit == 16) + cfspi_tx(ctrl, *spi_wr16); + else + cfspi_tx(ctrl, *spi_wr); + cfspi_rx(); + } + + if (din != NULL) { + cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL); + if (cfslave->charbit == 16) + *spi_rd16 = cfspi_rx(); + else + *spi_rd = cfspi_rx(); + } + } else { + /* dummy read */ + cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL); + cfspi_rx(); + } + + return 0; +} + +struct spi_slave *cfspi_setup_slave(struct cf_spi_slave *cfslave, uint mode) +{ + /* + * bit definition for mode: + * bit 31 - 28: Transfer size 3 to 16 bits + * 27 - 26: PCS to SCK delay prescaler + * 25 - 24: After SCK delay prescaler + * 23 - 22: Delay after transfer prescaler + * 21 : Allow overwrite for bit 31-22 and bit 20-8 + * 20 : Double baud rate + * 19 - 16: PCS to SCK delay scaler + * 15 - 12: After SCK delay scaler + * 11 - 8: Delay after transfer scaler + * 7 - 0: SPI_CPHA, SPI_CPOL, SPI_LSB_FIRST + */ + volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI; + int prescaler[] = { 2, 3, 5, 7 }; + int scaler[] = { + 2, 4, 6, 8, + 16, 32, 64, 128, + 256, 512, 1024, 2048, + 4096, 8192, 16384, 32768 + }; + int i, j, pbrcnt, brcnt, diff, tmp, dbr = 0; + int best_i, best_j, bestmatch = 0x7FFFFFFF, baud_speed; + u32 bus_setup = 0; + + tmp = (prescaler[3] * scaler[15]); + /* Maximum and minimum baudrate it can handle */ + if ((cfslave->baudrate > (gd->bus_clk >> 1)) || + (cfslave->baudrate < (gd->bus_clk / tmp))) { + printf("Exceed baudrate limitation: Max %d - Min %d\n", + (int)(gd->bus_clk >> 1), (int)(gd->bus_clk / tmp)); + return NULL; + } + + /* Activate Double Baud when it exceed 1/4 the bus clk */ + if ((CONFIG_SYS_DSPI_CTAR0 & DSPI_CTAR_DBR) || + (cfslave->baudrate > (gd->bus_clk / (prescaler[0] * scaler[0])))) { + bus_setup |= DSPI_CTAR_DBR; + dbr = 1; + } + + if (mode & SPI_CPOL) + bus_setup |= DSPI_CTAR_CPOL; + if (mode & SPI_CPHA) + bus_setup |= DSPI_CTAR_CPHA; + if (mode & SPI_LSB_FIRST) + bus_setup |= DSPI_CTAR_LSBFE; + + /* Overwrite default value set in platform configuration file */ + if (mode & SPI_MODE_MOD) { + + if ((mode & 0xF0000000) == 0) + bus_setup |= + dspi->ctar[cfslave->slave.bus] & 0x78000000; + else + bus_setup |= ((mode & 0xF0000000) >> 1); + + /* + * Check to see if it is enabled by default in platform + * config, or manual setting passed by mode parameter + */ + if (mode & SPI_DBLRATE) { + bus_setup |= DSPI_CTAR_DBR; + dbr = 1; + } + bus_setup |= (mode & 0x0FC00000) >> 4; /* PSCSCK, PASC, PDT */ + bus_setup |= (mode & 0x000FFF00) >> 4; /* CSSCK, ASC, DT */ + } else + bus_setup |= (dspi->ctar[cfslave->slave.bus] & 0x78FCFFF0); + + cfslave->charbit = + ((dspi->ctar[cfslave->slave.bus] & 0x78000000) == + 0x78000000) ? 16 : 8; + + pbrcnt = sizeof(prescaler) / sizeof(int); + brcnt = sizeof(scaler) / sizeof(int); + + /* baudrate calculation - to closer value, may not be exact match */ + for (best_i = 0, best_j = 0, i = 0; i < pbrcnt; i++) { + baud_speed = gd->bus_clk / prescaler[i]; + for (j = 0; j < brcnt; j++) { + tmp = (baud_speed / scaler[j]) * (1 + dbr); + + if (tmp > cfslave->baudrate) + diff = tmp - cfslave->baudrate; + else + diff = cfslave->baudrate - tmp; + + if (diff < bestmatch) { + bestmatch = diff; + best_i = i; + best_j = j; + } + } + } + bus_setup |= (DSPI_CTAR_PBR(best_i) | DSPI_CTAR_BR(best_j)); + dspi->ctar[cfslave->slave.bus] = bus_setup; + + return &cfslave->slave; +} +#endif /* CONFIG_CF_DSPI */ + +#ifdef CONFIG_CF_QSPI +/* 52xx, 53xx */ +#endif /* CONFIG_CF_QSPI */ + +#ifdef CONFIG_CMD_SPI +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + if (((cs >= 0) && (cs < 8)) && ((bus >= 0) && (bus < 8))) + return 1; + else + return 0; +} + +void spi_init_f(void) +{ +} + +void spi_init_r(void) +{ +} + +void spi_init(void) +{ + cfspi_init(); +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct cf_spi_slave *cfslave; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + cfslave = spi_alloc_slave(struct cf_spi_slave, bus, cs); + if (!cfslave) + return NULL; + + cfslave->baudrate = max_hz; + + /* specific setup */ + return cfspi_setup_slave(cfslave, mode); +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return cfspi_claim_bus(slave->bus, slave->cs); +} + +void spi_release_bus(struct spi_slave *slave) +{ + cfspi_release_bus(slave->bus, slave->cs); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + return cfspi_xfer(slave, bitlen, dout, din, flags); +} +#endif /* CONFIG_CMD_SPI */ diff --git a/qemu/roms/u-boot/drivers/spi/davinci_spi.c b/qemu/roms/u-boot/drivers/spi/davinci_spi.c new file mode 100644 index 000000000..28fb3a2e9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/davinci_spi.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * Driver for SPI controller on DaVinci. Based on atmel_spi.c + * by Atmel Corporation + * + * Copyright (C) 2007 Atmel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include "davinci_spi.h" + +void spi_init() +{ + /* do nothing */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct davinci_spi_slave *ds; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + ds = spi_alloc_slave(struct davinci_spi_slave, bus, cs); + if (!ds) + return NULL; + + ds->slave.bus = bus; + ds->slave.cs = cs; + + switch (bus) { + case SPI0_BUS: + ds->regs = (struct davinci_spi_regs *)SPI0_BASE; + break; +#ifdef CONFIG_SYS_SPI1 + case SPI1_BUS: + ds->regs = (struct davinci_spi_regs *)SPI0_BASE; + break; +#endif +#ifdef CONFIG_SYS_SPI2 + case SPI2_BUS: + ds->regs = (struct davinci_spi_regs *)SPI2_BASE; + break; +#endif + default: /* Invalid bus number */ + return NULL; + } + + ds->freq = max_hz; + + return &ds->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + + free(ds); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + unsigned int scalar; + + /* Enable the SPI hardware */ + writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); + udelay(1000); + writel(SPIGCR0_SPIENA_MASK, &ds->regs->gcr0); + + /* Set master mode, powered up and not activated */ + writel(SPIGCR1_MASTER_MASK | SPIGCR1_CLKMOD_MASK, &ds->regs->gcr1); + + /* CS, CLK, SIMO and SOMI are functional pins */ + writel(((1 << slave->cs) | SPIPC0_CLKFUN_MASK | + SPIPC0_DOFUN_MASK | SPIPC0_DIFUN_MASK), &ds->regs->pc0); + + /* setup format */ + scalar = ((CONFIG_SYS_SPI_CLK / ds->freq) - 1) & 0xFF; + + /* + * Use following format: + * character length = 8, + * clock signal delayed by half clk cycle, + * clock low in idle state - Mode 0, + * MSB shifted out first + */ + writel(8 | (scalar << SPIFMT_PRESCALE_SHIFT) | + (1 << SPIFMT_PHASE_SHIFT), &ds->regs->fmt0); + + /* + * Including a minor delay. No science here. Should be good even with + * no delay + */ + writel((50 << SPI_C2TDELAY_SHIFT) | + (50 << SPI_T2CDELAY_SHIFT), &ds->regs->delay); + + /* default chip select register */ + writel(SPIDEF_CSDEF0_MASK, &ds->regs->def); + + /* no interrupts */ + writel(0, &ds->regs->int0); + writel(0, &ds->regs->lvl); + + /* enable SPI */ + writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK), &ds->regs->gcr1); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + + /* Disable the SPI hardware */ + writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); +} + +/* + * This functions needs to act like a macro to avoid pipeline reloads in the + * loops below. Use always_inline. This gains us about 160KiB/s and the bloat + * appears to be zero bytes (da830). + */ +__attribute__((always_inline)) +static inline u32 davinci_spi_xfer_data(struct davinci_spi_slave *ds, u32 data) +{ + u32 buf_reg_val; + + /* send out data */ + writel(data, &ds->regs->dat1); + + /* wait for the data to clock in/out */ + while ((buf_reg_val = readl(&ds->regs->buf)) & SPIBUF_RXEMPTY_MASK) + ; + + return buf_reg_val; +} + +static int davinci_spi_read(struct spi_slave *slave, unsigned int len, + u8 *rxp, unsigned long flags) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + unsigned int data1_reg_val; + + /* enable CS hold, CS[n] and clear the data bits */ + data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) | + (slave->cs << SPIDAT1_CSNR_SHIFT)); + + /* wait till TXFULL is deasserted */ + while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK) + ; + + /* preload the TX buffer to avoid clock starvation */ + writel(data1_reg_val, &ds->regs->dat1); + + /* keep reading 1 byte until only 1 byte left */ + while ((len--) > 1) + *rxp++ = davinci_spi_xfer_data(ds, data1_reg_val); + + /* clear CS hold when we reach the end */ + if (flags & SPI_XFER_END) + data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT); + + /* read the last byte */ + *rxp = davinci_spi_xfer_data(ds, data1_reg_val); + + return 0; +} + +static int davinci_spi_write(struct spi_slave *slave, unsigned int len, + const u8 *txp, unsigned long flags) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + unsigned int data1_reg_val; + + /* enable CS hold and clear the data bits */ + data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) | + (slave->cs << SPIDAT1_CSNR_SHIFT)); + + /* wait till TXFULL is deasserted */ + while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK) + ; + + /* preload the TX buffer to avoid clock starvation */ + if (len > 2) { + writel(data1_reg_val | *txp++, &ds->regs->dat1); + len--; + } + + /* keep writing 1 byte until only 1 byte left */ + while ((len--) > 1) + davinci_spi_xfer_data(ds, data1_reg_val | *txp++); + + /* clear CS hold when we reach the end */ + if (flags & SPI_XFER_END) + data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT); + + /* write the last byte */ + davinci_spi_xfer_data(ds, data1_reg_val | *txp); + + return 0; +} + +#ifndef CONFIG_SPI_HALF_DUPLEX +static int davinci_spi_read_write(struct spi_slave *slave, unsigned int len, + u8 *rxp, const u8 *txp, unsigned long flags) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + unsigned int data1_reg_val; + + /* enable CS hold and clear the data bits */ + data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) | + (slave->cs << SPIDAT1_CSNR_SHIFT)); + + /* wait till TXFULL is deasserted */ + while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK) + ; + + /* keep reading and writing 1 byte until only 1 byte left */ + while ((len--) > 1) + *rxp++ = davinci_spi_xfer_data(ds, data1_reg_val | *txp++); + + /* clear CS hold when we reach the end */ + if (flags & SPI_XFER_END) + data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT); + + /* read and write the last byte */ + *rxp = davinci_spi_xfer_data(ds, data1_reg_val | *txp); + + return 0; +} +#endif + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + unsigned int len; + + if (bitlen == 0) + /* Finish any previously submitted transfers */ + goto out; + + /* + * It's not clear how non-8-bit-aligned transfers are supposed to be + * represented as a stream of bytes...this is a limitation of + * the current SPI interface - here we terminate on receiving such a + * transfer request. + */ + if (bitlen % 8) { + /* Errors always terminate an ongoing transfer */ + flags |= SPI_XFER_END; + goto out; + } + + len = bitlen / 8; + + if (!dout) + return davinci_spi_read(slave, len, din, flags); + else if (!din) + return davinci_spi_write(slave, len, dout, flags); +#ifndef CONFIG_SPI_HALF_DUPLEX + else + return davinci_spi_read_write(slave, len, din, dout, flags); +#else + printf("SPI full duplex transaction requested with " + "CONFIG_SPI_HALF_DUPLEX defined.\n"); + flags |= SPI_XFER_END; +#endif + +out: + if (flags & SPI_XFER_END) { + u8 dummy = 0; + davinci_spi_write(slave, 1, &dummy, flags); + } + return 0; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + int ret = 0; + + switch (bus) { + case SPI0_BUS: + if (cs < SPI0_NUM_CS) + ret = 1; + break; +#ifdef CONFIG_SYS_SPI1 + case SPI1_BUS: + if (cs < SPI1_NUM_CS) + ret = 1; + break; +#endif +#ifdef CONFIG_SYS_SPI2 + case SPI2_BUS: + if (cs < SPI2_NUM_CS) + ret = 1; + break; +#endif + default: + /* Invalid bus number. Do nothing */ + break; + } + return ret; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + /* do nothing */ +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + /* do nothing */ +} diff --git a/qemu/roms/u-boot/drivers/spi/davinci_spi.h b/qemu/roms/u-boot/drivers/spi/davinci_spi.h new file mode 100644 index 000000000..d4612d352 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/davinci_spi.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * Register definitions for the DaVinci SPI Controller + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DAVINCI_SPI_H_ +#define _DAVINCI_SPI_H_ + +struct davinci_spi_regs { + dv_reg gcr0; /* 0x00 */ + dv_reg gcr1; /* 0x04 */ + dv_reg int0; /* 0x08 */ + dv_reg lvl; /* 0x0c */ + dv_reg flg; /* 0x10 */ + dv_reg pc0; /* 0x14 */ + dv_reg pc1; /* 0x18 */ + dv_reg pc2; /* 0x1c */ + dv_reg pc3; /* 0x20 */ + dv_reg pc4; /* 0x24 */ + dv_reg pc5; /* 0x28 */ + dv_reg rsvd[3]; + dv_reg dat0; /* 0x38 */ + dv_reg dat1; /* 0x3c */ + dv_reg buf; /* 0x40 */ + dv_reg emu; /* 0x44 */ + dv_reg delay; /* 0x48 */ + dv_reg def; /* 0x4c */ + dv_reg fmt0; /* 0x50 */ + dv_reg fmt1; /* 0x54 */ + dv_reg fmt2; /* 0x58 */ + dv_reg fmt3; /* 0x5c */ + dv_reg intvec0; /* 0x60 */ + dv_reg intvec1; /* 0x64 */ +}; + +#define BIT(x) (1 << (x)) + +/* SPIGCR0 */ +#define SPIGCR0_SPIENA_MASK 0x1 +#define SPIGCR0_SPIRST_MASK 0x0 + +/* SPIGCR0 */ +#define SPIGCR1_CLKMOD_MASK BIT(1) +#define SPIGCR1_MASTER_MASK BIT(0) +#define SPIGCR1_SPIENA_MASK BIT(24) + +/* SPIPC0 */ +#define SPIPC0_DIFUN_MASK BIT(11) /* SIMO */ +#define SPIPC0_DOFUN_MASK BIT(10) /* SOMI */ +#define SPIPC0_CLKFUN_MASK BIT(9) /* CLK */ +#define SPIPC0_EN0FUN_MASK BIT(0) + +/* SPIFMT0 */ +#define SPIFMT_SHIFTDIR_SHIFT 20 +#define SPIFMT_POLARITY_SHIFT 17 +#define SPIFMT_PHASE_SHIFT 16 +#define SPIFMT_PRESCALE_SHIFT 8 + +/* SPIDAT1 */ +#define SPIDAT1_CSHOLD_SHIFT 28 +#define SPIDAT1_CSNR_SHIFT 16 + +/* SPIDELAY */ +#define SPI_C2TDELAY_SHIFT 24 +#define SPI_T2CDELAY_SHIFT 16 + +/* SPIBUF */ +#define SPIBUF_RXEMPTY_MASK BIT(31) +#define SPIBUF_TXFULL_MASK BIT(29) + +/* SPIDEF */ +#define SPIDEF_CSDEF0_MASK BIT(0) + +#define SPI0_BUS 0 +#define SPI0_BASE CONFIG_SYS_SPI_BASE +/* + * Define default SPI0_NUM_CS as 1 for existing platforms that uses this + * driver. Platform can configure number of CS using CONFIG_SYS_SPI0_NUM_CS + * if more than one CS is supported and by defining CONFIG_SYS_SPI0. + */ +#ifndef CONFIG_SYS_SPI0 +#define SPI0_NUM_CS 1 +#else +#define SPI0_NUM_CS CONFIG_SYS_SPI0_NUM_CS +#endif + +/* + * define CONFIG_SYS_SPI1 when platform has spi-1 device (bus #1) and + * CONFIG_SYS_SPI1_NUM_CS defines number of CS on this bus + */ +#ifdef CONFIG_SYS_SPI1 +#define SPI1_BUS 1 +#define SPI1_NUM_CS CONFIG_SYS_SPI1_NUM_CS +#define SPI1_BASE CONFIG_SYS_SPI1_BASE +#endif + +/* + * define CONFIG_SYS_SPI2 when platform has spi-2 device (bus #2) and + * CONFIG_SYS_SPI2_NUM_CS defines number of CS on this bus + */ +#ifdef CONFIG_SYS_SPI2 +#define SPI2_BUS 2 +#define SPI2_NUM_CS CONFIG_SYS_SPI2_NUM_CS +#define SPI2_BASE CONFIG_SYS_SPI2_BASE +#endif + +struct davinci_spi_slave { + struct spi_slave slave; + struct davinci_spi_regs *regs; + unsigned int freq; +}; + +static inline struct davinci_spi_slave *to_davinci_spi(struct spi_slave *slave) +{ + return container_of(slave, struct davinci_spi_slave, slave); +} + +#endif /* _DAVINCI_SPI_H_ */ diff --git a/qemu/roms/u-boot/drivers/spi/exynos_spi.c b/qemu/roms/u-boot/drivers/spi/exynos_spi.c new file mode 100644 index 000000000..4d5def2d3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/exynos_spi.c @@ -0,0 +1,579 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Padmavathi Venna + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Information about each SPI controller */ +struct spi_bus { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + int inited; /* 1 if this bus is ready for use */ + int node; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + +/* A list of spi buses that we know about */ +static struct spi_bus spi_bus[EXYNOS5_SPI_NUM_CONTROLLERS]; +static unsigned int bus_count; + +struct exynos_spi_slave { + struct spi_slave slave; + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + struct spi_bus *bus; /* Pointer to our SPI bus info */ + ulong last_transaction_us; /* Time of last transaction end */ +}; + +static struct spi_bus *spi_get_bus(unsigned dev_index) +{ + if (dev_index < bus_count) + return &spi_bus[dev_index]; + debug("%s: invalid bus %d", __func__, dev_index); + + return NULL; +} + +static inline struct exynos_spi_slave *to_exynos_spi(struct spi_slave *slave) +{ + return container_of(slave, struct exynos_spi_slave, slave); +} + +/** + * Setup the driver private data + * + * @param bus ID of the bus that the slave is attached to + * @param cs ID of the chip select connected to the slave + * @param max_hz Required spi frequency + * @param mode Required spi mode (clk polarity, clk phase and + * master or slave) + * @return new device or NULL + */ +struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct exynos_spi_slave *spi_slave; + struct spi_bus *bus; + + if (!spi_cs_is_valid(busnum, cs)) { + debug("%s: Invalid bus/chip select %d, %d\n", __func__, + busnum, cs); + return NULL; + } + + spi_slave = spi_alloc_slave(struct exynos_spi_slave, busnum, cs); + if (!spi_slave) { + debug("%s: Could not allocate spi_slave\n", __func__); + return NULL; + } + + bus = &spi_bus[busnum]; + spi_slave->bus = bus; + spi_slave->regs = bus->regs; + spi_slave->mode = mode; + spi_slave->periph_id = bus->periph_id; + if (bus->periph_id == PERIPH_ID_SPI1 || + bus->periph_id == PERIPH_ID_SPI2) + spi_slave->fifo_size = 64; + else + spi_slave->fifo_size = 256; + + spi_slave->skip_preamble = 0; + spi_slave->last_transaction_us = timer_get_us(); + + spi_slave->freq = bus->frequency; + if (max_hz) + spi_slave->freq = min(max_hz, spi_slave->freq); + + return &spi_slave->slave; +} + +/** + * Free spi controller + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_free_slave(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + free(spi_slave); +} + +/** + * Flush spi tx, rx fifos and reset the SPI controller + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +static void spi_flush_fifo(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); +} + +/** + * Initialize the spi base registers, set the required clock frequency and + * initialize the gpios + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + * @return zero on success else a negative value + */ +int spi_claim_bus(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + u32 reg = 0; + int ret; + + ret = set_spi_clk(spi_slave->periph_id, + spi_slave->freq); + if (ret < 0) { + debug("%s: Failed to setup spi clock\n", __func__); + return ret; + } + + exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); + + spi_flush_fifo(slave); + + reg = readl(®s->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (spi_slave->mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (spi_slave->mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, ®s->ch_cfg); + writel(SPI_FB_DELAY_180, ®s->fb_clk); + + return 0; +} + +/** + * Reset the spi H/W and flush the tx and rx fifos + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_release_bus(struct spi_slave *slave) +{ + spi_flush_fifo(slave); +} + +static void spi_get_fifo_levels(struct exynos_spi *regs, + int *rx_lvl, int *tx_lvl) +{ + uint32_t spi_sts = readl(®s->spi_sts); + + *rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK; + *tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK; +} + +/** + * If there's something to transfer, do a software reset and set a + * transaction size. + * + * @param regs SPI peripheral registers + * @param count Number of bytes to transfer + * @param step Number of bytes to transfer in each packet (1 or 4) + */ +static void spi_request_bytes(struct exynos_spi *regs, int count, int step) +{ + /* For word address we need to swap bytes */ + if (step == 4) { + setbits_le32(®s->mode_cfg, + SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); + count /= 4; + setbits_le32(®s->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN | + SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP | + SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP); + } else { + /* Select byte access and clear the swap configuration */ + clrbits_le32(®s->mode_cfg, + SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); + writel(0, ®s->swap_cfg); + } + + assert(count && count < (1 << 16)); + setbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + + writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt); +} + +static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, + void **dinp, void const **doutp, unsigned long flags) +{ + struct exynos_spi *regs = spi_slave->regs; + uchar *rxp = *dinp; + const uchar *txp = *doutp; + int rx_lvl, tx_lvl; + uint out_bytes, in_bytes; + int toread; + unsigned start = get_timer(0); + int stopping; + int step; + + out_bytes = in_bytes = todo; + + stopping = spi_slave->skip_preamble && (flags & SPI_XFER_END) && + !(spi_slave->mode & SPI_SLAVE); + + /* + * Try to transfer words if we can. This helps read performance at + * SPI clock speeds above about 20MHz. + */ + step = 1; + if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) && + !spi_slave->skip_preamble) + step = 4; + + /* + * If there's something to send, do a software reset and set a + * transaction size. + */ + spi_request_bytes(regs, todo, step); + + /* + * Bytes are transmitted/received in pairs. Wait to receive all the + * data because then transmission will be done as well. + */ + toread = in_bytes; + + while (in_bytes) { + int temp; + + /* Keep the fifos full/empty. */ + spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl); + + /* + * Don't completely fill the txfifo, since we don't want our + * rxfifo to overflow, and it may already contain data. + */ + while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) { + if (!txp) + temp = -1; + else if (step == 4) + temp = *(uint32_t *)txp; + else + temp = *txp; + writel(temp, ®s->tx_data); + out_bytes -= step; + if (txp) + txp += step; + tx_lvl += step; + } + if (rx_lvl >= step) { + while (rx_lvl >= step) { + temp = readl(®s->rx_data); + if (spi_slave->skip_preamble) { + if (temp == SPI_PREAMBLE_END_BYTE) { + spi_slave->skip_preamble = 0; + stopping = 0; + } + } else { + if (rxp || stopping) { + *rxp = temp; + rxp += step; + } + in_bytes -= step; + } + toread -= step; + rx_lvl -= step; + } + } else if (!toread) { + /* + * We have run out of input data, but haven't read + * enough bytes after the preamble yet. Read some more, + * and make sure that we transmit dummy bytes too, to + * keep things going. + */ + assert(!out_bytes); + out_bytes = in_bytes; + toread = in_bytes; + txp = NULL; + spi_request_bytes(regs, toread, step); + } + if (spi_slave->skip_preamble && get_timer(start) > 100) { + printf("SPI timeout: in_bytes=%d, out_bytes=%d, ", + in_bytes, out_bytes); + return -1; + } + } + + *dinp = rxp; + *doutp = txp; + + return 0; +} + +/** + * Transfer and receive data + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + * @param bitlen No of bits to tranfer or receive + * @param dout Pointer to transfer buffer + * @param din Pointer to receive buffer + * @param flags Flags for transfer begin and end + * @return zero on success else a negative value + */ +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + int upto, todo; + int bytelen; + int ret = 0; + + /* spi core configured to do 8 bit transfers */ + if (bitlen % 8) { + debug("Non byte aligned SPI transfer.\n"); + return -1; + } + + /* Start the transaction, if necessary. */ + if ((flags & SPI_XFER_BEGIN)) + spi_cs_activate(slave); + + /* + * Exynos SPI limits each transfer to 65535 transfers. To keep + * things simple, allow a maximum of 65532 bytes. We could allow + * more in word mode, but the performance difference is small. + */ + bytelen = bitlen / 8; + for (upto = 0; !ret && upto < bytelen; upto += todo) { + todo = min(bytelen - upto, (1 << 16) - 4); + ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags); + if (ret) + break; + } + + /* Stop the transaction, if necessary. */ + if ((flags & SPI_XFER_END) && !(spi_slave->mode & SPI_SLAVE)) { + spi_cs_deactivate(slave); + if (spi_slave->skip_preamble) { + assert(!spi_slave->skip_preamble); + debug("Failed to complete premable transaction\n"); + ret = -1; + } + } + + return ret; +} + +/** + * Validates the bus and chip select numbers + * + * @param bus ID of the bus that the slave is attached to + * @param cs ID of the chip select connected to the slave + * @return one on success else zero + */ +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return spi_get_bus(bus) && cs == 0; +} + +/** + * Activate the CS by driving it LOW + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_cs_activate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + /* If it's too soon to do another transaction, wait */ + if (spi_slave->bus->deactivate_delay_us && + spi_slave->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - spi_slave->last_transaction_us; + if (delay_us < spi_slave->bus->deactivate_delay_us) + udelay(spi_slave->bus->deactivate_delay_us - delay_us); + } + + clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus %d\n", spi_slave->slave.bus); + spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; + + /* Remember time of this transaction so we can honour the bus delay */ + if (spi_slave->bus->deactivate_delay_us) + spi_slave->last_transaction_us = timer_get_us(); +} + +/** + * Deactivate the CS by driving it HIGH + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + setbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Deactivate CS, bus %d\n", spi_slave->slave.bus); +} + +static inline struct exynos_spi *get_spi_base(int dev_index) +{ + if (dev_index < 3) + return (struct exynos_spi *)samsung_get_base_spi() + dev_index; + else + return (struct exynos_spi *)samsung_get_base_spi_isp() + + (dev_index - 3); +} + +/* + * Read the SPI config from the device tree node. + * + * @param blob FDT blob to read from + * @param node Node offset to read from + * @param bus SPI bus structure to fill with information + * @return 0 if ok, or -FDT_ERR_NOTFOUND if something was missing + */ +#ifdef CONFIG_OF_CONTROL +static int spi_get_config(const void *blob, int node, struct spi_bus *bus) +{ + bus->node = node; + bus->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); + bus->periph_id = pinmux_decode_periph_id(blob, node); + + if (bus->periph_id == PERIPH_ID_NONE) { + debug("%s: Invalid peripheral ID %d\n", __func__, + bus->periph_id); + return -FDT_ERR_NOTFOUND; + } + + /* Use 500KHz as a suitable default */ + bus->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + bus->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + + return 0; +} + +/* + * Process a list of nodes, adding them to our list of SPI ports. + * + * @param blob fdt blob + * @param node_list list of nodes to process (any <=0 are ignored) + * @param count number of nodes to process + * @param is_dvc 1 if these are DVC ports, 0 if standard I2C + * @return 0 if ok, -1 on error + */ +static int process_nodes(const void *blob, int node_list[], int count) +{ + int i; + + /* build the i2c_controllers[] for each controller */ + for (i = 0; i < count; i++) { + int node = node_list[i]; + struct spi_bus *bus; + + if (node <= 0) + continue; + + bus = &spi_bus[i]; + if (spi_get_config(blob, node, bus)) { + printf("exynos spi_init: failed to decode bus %d\n", + i); + return -1; + } + + debug("spi: controller bus %d at %p, periph_id %d\n", + i, bus->regs, bus->periph_id); + bus->inited = 1; + bus_count++; + } + + return 0; +} +#endif + +/** + * Set up a new SPI slave for an fdt node + * + * @param blob Device tree blob + * @param node SPI peripheral node to use + * @return 0 if ok, -1 on error + */ +struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, + int spi_node) +{ + struct spi_bus *bus; + unsigned int i; + + for (i = 0, bus = spi_bus; i < bus_count; i++, bus++) { + if (bus->node == spi_node) + return spi_base_setup_slave_fdt(blob, i, slave_node); + } + + debug("%s: Failed to find bus node %d\n", __func__, spi_node); + return NULL; +} + +/* Sadly there is no error return from this function */ +void spi_init(void) +{ + int count; + +#ifdef CONFIG_OF_CONTROL + int node_list[EXYNOS5_SPI_NUM_CONTROLLERS]; + const void *blob = gd->fdt_blob; + + count = fdtdec_find_aliases_for_id(blob, "spi", + COMPAT_SAMSUNG_EXYNOS_SPI, node_list, + EXYNOS5_SPI_NUM_CONTROLLERS); + if (process_nodes(blob, node_list, count)) + return; + +#else + struct spi_bus *bus; + + for (count = 0; count < EXYNOS5_SPI_NUM_CONTROLLERS; count++) { + bus = &spi_bus[count]; + bus->regs = get_spi_base(count); + bus->periph_id = PERIPH_ID_SPI0 + count; + + /* Although Exynos5 supports upto 50Mhz speed, + * we are setting it to 10Mhz for safe side + */ + bus->frequency = 10000000; + bus->inited = 1; + bus->node = 0; + bus_count = EXYNOS5_SPI_NUM_CONTROLLERS; + } +#endif +} diff --git a/qemu/roms/u-boot/drivers/spi/fdt_spi.c b/qemu/roms/u-boot/drivers/spi/fdt_spi.c new file mode 100644 index 000000000..58f139a54 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/fdt_spi.c @@ -0,0 +1,186 @@ +/* + * Common fdt based SPI driver front end + * + * Copyright (c) 2013 NVIDIA Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct fdt_spi_driver { + int compat; + int max_ctrls; + int (*init)(int *node_list, int count); + int (*claim_bus)(struct spi_slave *slave); + int (*release_bus)(struct spi_slave *slave); + int (*cs_is_valid)(unsigned int bus, unsigned int cs); + struct spi_slave *(*setup_slave)(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode); + void (*free_slave)(struct spi_slave *slave); + void (*cs_activate)(struct spi_slave *slave); + void (*cs_deactivate)(struct spi_slave *slave); + int (*xfer)(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags); +}; + +static struct fdt_spi_driver fdt_spi_drivers[] = { +#ifdef CONFIG_TEGRA20_SFLASH + { + .compat = COMPAT_NVIDIA_TEGRA20_SFLASH, + .max_ctrls = 1, + .init = tegra20_spi_init, + .claim_bus = tegra20_spi_claim_bus, + .cs_is_valid = tegra20_spi_cs_is_valid, + .setup_slave = tegra20_spi_setup_slave, + .free_slave = tegra20_spi_free_slave, + .cs_activate = tegra20_spi_cs_activate, + .cs_deactivate = tegra20_spi_cs_deactivate, + .xfer = tegra20_spi_xfer, + }, +#endif +#ifdef CONFIG_TEGRA20_SLINK + { + .compat = COMPAT_NVIDIA_TEGRA20_SLINK, + .max_ctrls = CONFIG_TEGRA_SLINK_CTRLS, + .init = tegra30_spi_init, + .claim_bus = tegra30_spi_claim_bus, + .cs_is_valid = tegra30_spi_cs_is_valid, + .setup_slave = tegra30_spi_setup_slave, + .free_slave = tegra30_spi_free_slave, + .cs_activate = tegra30_spi_cs_activate, + .cs_deactivate = tegra30_spi_cs_deactivate, + .xfer = tegra30_spi_xfer, + }, +#endif +#ifdef CONFIG_TEGRA114_SPI + { + .compat = COMPAT_NVIDIA_TEGRA114_SPI, + .max_ctrls = CONFIG_TEGRA114_SPI_CTRLS, + .init = tegra114_spi_init, + .claim_bus = tegra114_spi_claim_bus, + .cs_is_valid = tegra114_spi_cs_is_valid, + .setup_slave = tegra114_spi_setup_slave, + .free_slave = tegra114_spi_free_slave, + .cs_activate = tegra114_spi_cs_activate, + .cs_deactivate = tegra114_spi_cs_deactivate, + .xfer = tegra114_spi_xfer, + }, +#endif +}; + +static struct fdt_spi_driver *driver; + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + if (!driver) + return 0; + else if (!driver->cs_is_valid) + return 1; + else + return driver->cs_is_valid(bus, cs); +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + if (!driver || !driver->setup_slave) + return NULL; + + return driver->setup_slave(bus, cs, max_hz, mode); +} + +void spi_free_slave(struct spi_slave *slave) +{ + if (driver && driver->free_slave) + return driver->free_slave(slave); +} + +static int spi_init_driver(struct fdt_spi_driver *driver) +{ + int count; + int node_list[driver->max_ctrls]; + + count = fdtdec_find_aliases_for_id(gd->fdt_blob, "spi", + driver->compat, + node_list, + driver->max_ctrls); + return driver->init(node_list, count); +} + +void spi_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fdt_spi_drivers); i++) { + driver = &fdt_spi_drivers[i]; + if (!spi_init_driver(driver)) + break; + } + if (i == ARRAY_SIZE(fdt_spi_drivers)) + driver = NULL; +} + +int spi_claim_bus(struct spi_slave *slave) +{ + if (!driver) + return 1; + if (!driver->claim_bus) + return 0; + + return driver->claim_bus(slave); +} + +void spi_release_bus(struct spi_slave *slave) +{ + if (driver && driver->release_bus) + driver->release_bus(slave); +} + +void spi_cs_activate(struct spi_slave *slave) +{ + if (driver && driver->cs_activate) + driver->cs_activate(slave); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + if (driver && driver->cs_deactivate) + driver->cs_deactivate(slave); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags) +{ + if (!driver || !driver->xfer) + return -1; + + return driver->xfer(slave, bitlen, data_out, data_in, flags); +} diff --git a/qemu/roms/u-boot/drivers/spi/fsl_espi.c b/qemu/roms/u-boot/drivers/spi/fsl_espi.c new file mode 100644 index 000000000..7c8458276 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/fsl_espi.c @@ -0,0 +1,315 @@ +/* + * eSPI controller driver. + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * Author: Mingkai Hu (Mingkai.hu@freescale.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include +#include +#include + +struct fsl_spi_slave { + struct spi_slave slave; + unsigned int div16; + unsigned int pm; + unsigned int mode; + size_t cmd_len; + u8 cmd_buf[16]; + size_t data_len; + unsigned int max_transfer_length; +}; + +#define to_fsl_spi_slave(s) container_of(s, struct fsl_spi_slave, slave) + +#define ESPI_MAX_CS_NUM 4 + +#define ESPI_EV_RNE (1 << 9) +#define ESPI_EV_TNF (1 << 8) + +#define ESPI_MODE_EN (1 << 31) /* Enable interface */ +#define ESPI_MODE_TXTHR(x) ((x) << 8) /* Tx FIFO threshold */ +#define ESPI_MODE_RXTHR(x) ((x) << 0) /* Rx FIFO threshold */ + +#define ESPI_COM_CS(x) ((x) << 30) +#define ESPI_COM_TRANLEN(x) ((x) << 0) + +#define ESPI_CSMODE_CI_INACTIVEHIGH (1 << 31) +#define ESPI_CSMODE_CP_BEGIN_EDGCLK (1 << 30) +#define ESPI_CSMODE_REV_MSB_FIRST (1 << 29) +#define ESPI_CSMODE_DIV16 (1 << 28) +#define ESPI_CSMODE_PM(x) ((x) << 24) +#define ESPI_CSMODE_POL_ASSERTED_LOW (1 << 20) +#define ESPI_CSMODE_LEN(x) ((x) << 16) +#define ESPI_CSMODE_CSBEF(x) ((x) << 12) +#define ESPI_CSMODE_CSAFT(x) ((x) << 8) +#define ESPI_CSMODE_CSCG(x) ((x) << 3) + +#define ESPI_CSMODE_INIT_VAL (ESPI_CSMODE_POL_ASSERTED_LOW | \ + ESPI_CSMODE_CSBEF(0) | ESPI_CSMODE_CSAFT(0) | \ + ESPI_CSMODE_CSCG(1)) + +#define ESPI_MAX_DATA_TRANSFER_LEN 0xFFF0 + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct fsl_spi_slave *fsl; + sys_info_t sysinfo; + unsigned long spibrg = 0; + unsigned char pm = 0; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + fsl = spi_alloc_slave(struct fsl_spi_slave, bus, cs); + if (!fsl) + return NULL; + + fsl->mode = mode; + fsl->max_transfer_length = ESPI_MAX_DATA_TRANSFER_LEN; + + /* Set eSPI BRG clock source */ + get_sys_info(&sysinfo); + spibrg = sysinfo.freq_systembus / 2; + fsl->div16 = 0; + if ((spibrg / max_hz) > 32) { + fsl->div16 = ESPI_CSMODE_DIV16; + pm = spibrg / (max_hz * 16 * 2); + if (pm > 16) { + pm = 16; + debug("Requested speed is too low: %d Hz, %ld Hz " + "is used.\n", max_hz, spibrg / (32 * 16)); + } + } else + pm = spibrg / (max_hz * 2); + if (pm) + pm--; + fsl->pm = pm; + + return &fsl->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave); + free(fsl); +} + +void spi_init(void) +{ + +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave); + ccsr_espi_t *espi = (void *)(CONFIG_SYS_MPC85xx_ESPI_ADDR); + unsigned char pm = fsl->pm; + unsigned int cs = slave->cs; + unsigned int mode = fsl->mode; + unsigned int div16 = fsl->div16; + int i; + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, cs); + + /* Enable eSPI interface */ + out_be32(&espi->mode, ESPI_MODE_RXTHR(3) + | ESPI_MODE_TXTHR(4) | ESPI_MODE_EN); + + out_be32(&espi->event, 0xffffffff); /* Clear all eSPI events */ + out_be32(&espi->mask, 0x00000000); /* Mask all eSPI interrupts */ + + /* Init CS mode interface */ + for (i = 0; i < ESPI_MAX_CS_NUM; i++) + out_be32(&espi->csmode[i], ESPI_CSMODE_INIT_VAL); + + out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs]) & + ~(ESPI_CSMODE_PM(0xF) | ESPI_CSMODE_DIV16 + | ESPI_CSMODE_CI_INACTIVEHIGH | ESPI_CSMODE_CP_BEGIN_EDGCLK + | ESPI_CSMODE_REV_MSB_FIRST | ESPI_CSMODE_LEN(0xF))); + + /* Set eSPI BRG clock source */ + out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs]) + | ESPI_CSMODE_PM(pm) | div16); + + /* Set eSPI mode */ + if (mode & SPI_CPHA) + out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs]) + | ESPI_CSMODE_CP_BEGIN_EDGCLK); + if (mode & SPI_CPOL) + out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs]) + | ESPI_CSMODE_CI_INACTIVEHIGH); + + /* Character bit order: msb first */ + out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs]) + | ESPI_CSMODE_REV_MSB_FIRST); + + /* Character length in bits, between 0x3~0xf, i.e. 4bits~16bits */ + out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs]) + | ESPI_CSMODE_LEN(7)); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out, + void *data_in, unsigned long flags) +{ + struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave); + ccsr_espi_t *espi = (void *)(CONFIG_SYS_MPC85xx_ESPI_ADDR); + unsigned int tmpdout, tmpdin, event; + const void *dout = NULL; + void *din = NULL; + int len = 0; + int num_blks, num_chunks, max_tran_len, tran_len; + int num_bytes; + unsigned char *ch; + unsigned char *buffer = NULL; + size_t buf_len; + u8 *cmd_buf = fsl->cmd_buf; + size_t cmd_len = fsl->cmd_len; + size_t data_len = bitlen / 8; + size_t rx_offset = 0; + + max_tran_len = fsl->max_transfer_length; + switch (flags) { + case SPI_XFER_BEGIN: + cmd_len = fsl->cmd_len = data_len; + memcpy(cmd_buf, data_out, cmd_len); + return 0; + case 0: + case SPI_XFER_END: + if (bitlen == 0) { + spi_cs_deactivate(slave); + return 0; + } + buf_len = 2 * cmd_len + min(data_len, max_tran_len); + len = cmd_len + data_len; + rx_offset = cmd_len; + buffer = (unsigned char *)malloc(buf_len); + if (!buffer) { + debug("SF: Failed to malloc memory.\n"); + return 1; + } + memcpy(buffer, cmd_buf, cmd_len); + if (data_in == NULL) + memcpy(buffer + cmd_len, data_out, data_len); + break; + case SPI_XFER_BEGIN | SPI_XFER_END: + len = data_len; + buffer = (unsigned char *)malloc(len * 2); + if (!buffer) { + debug("SF: Failed to malloc memory.\n"); + return 1; + } + memcpy(buffer, data_out, len); + rx_offset = len; + cmd_len = 0; + break; + } + + debug("spi_xfer: slave %u:%u dout %08X(%p) din %08X(%p) len %u\n", + slave->bus, slave->cs, *(uint *) dout, + dout, *(uint *) din, din, len); + + num_chunks = DIV_ROUND_UP(data_len, max_tran_len); + while (num_chunks--) { + if (data_in) + din = buffer + rx_offset; + dout = buffer; + tran_len = min(data_len , max_tran_len); + num_blks = DIV_ROUND_UP(tran_len + cmd_len, 4); + num_bytes = (tran_len + cmd_len) % 4; + fsl->data_len = tran_len + cmd_len; + spi_cs_activate(slave); + + /* Clear all eSPI events */ + out_be32(&espi->event , 0xffffffff); + /* handle data in 32-bit chunks */ + while (num_blks--) { + + event = in_be32(&espi->event); + if (event & ESPI_EV_TNF) { + tmpdout = *(u32 *)dout; + + /* Set up the next iteration */ + if (len > 4) { + len -= 4; + dout += 4; + } + + out_be32(&espi->tx, tmpdout); + out_be32(&espi->event, ESPI_EV_TNF); + debug("***spi_xfer:...%08x written\n", tmpdout); + } + + /* Wait for eSPI transmit to get out */ + udelay(80); + + event = in_be32(&espi->event); + if (event & ESPI_EV_RNE) { + tmpdin = in_be32(&espi->rx); + if (num_blks == 0 && num_bytes != 0) { + ch = (unsigned char *)&tmpdin; + while (num_bytes--) + *(unsigned char *)din++ = *ch++; + } else { + *(u32 *) din = tmpdin; + din += 4; + } + + out_be32(&espi->event, in_be32(&espi->event) + | ESPI_EV_RNE); + debug("***spi_xfer:...%08x readed\n", tmpdin); + } + } + if (data_in) { + memcpy(data_in, buffer + 2 * cmd_len, tran_len); + if (*buffer == 0x0b) { + data_in += tran_len; + data_len -= tran_len; + *(int *)buffer += tran_len; + } + } + spi_cs_deactivate(slave); + } + + free(buffer); + return 0; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus == 0 && cs < ESPI_MAX_CS_NUM; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave); + ccsr_espi_t *espi = (void *)(CONFIG_SYS_MPC85xx_ESPI_ADDR); + unsigned int com = 0; + size_t data_len = fsl->data_len; + + com &= ~(ESPI_COM_CS(0x3) | ESPI_COM_TRANLEN(0xFFFF)); + com |= ESPI_COM_CS(slave->cs); + com |= ESPI_COM_TRANLEN(data_len - 1); + out_be32(&espi->com, com); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + ccsr_espi_t *espi = (void *)(CONFIG_SYS_MPC85xx_ESPI_ADDR); + + /* clear the RXCNT and TXCNT */ + out_be32(&espi->mode, in_be32(&espi->mode) & (~ESPI_MODE_EN)); + out_be32(&espi->mode, in_be32(&espi->mode) | ESPI_MODE_EN); +} diff --git a/qemu/roms/u-boot/drivers/spi/ftssp010_spi.c b/qemu/roms/u-boot/drivers/spi/ftssp010_spi.c new file mode 100644 index 000000000..aa3b5a01c --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/ftssp010_spi.c @@ -0,0 +1,508 @@ +/* + * (C) Copyright 2013 + * Faraday Technology Corporation. + * Kuo-Jung Su + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#ifndef CONFIG_FTSSP010_BASE_LIST +#define CONFIG_FTSSP010_BASE_LIST { CONFIG_FTSSP010_BASE } +#endif + +#ifndef CONFIG_FTSSP010_GPIO_BASE +#define CONFIG_FTSSP010_GPIO_BASE 0 +#endif + +#ifndef CONFIG_FTSSP010_GPIO_LIST +#define CONFIG_FTSSP010_GPIO_LIST { CONFIG_FTSSP010_GPIO_BASE } +#endif + +#ifndef CONFIG_FTSSP010_CLOCK +#define CONFIG_FTSSP010_CLOCK clk_get_rate("SSP"); +#endif + +#ifndef CONFIG_FTSSP010_TIMEOUT +#define CONFIG_FTSSP010_TIMEOUT 100 +#endif + +/* FTSSP010 chip registers */ +struct ftssp010_regs { + uint32_t cr[3];/* control register */ + uint32_t sr; /* status register */ + uint32_t icr; /* interrupt control register */ + uint32_t isr; /* interrupt status register */ + uint32_t dr; /* data register */ + uint32_t rsvd[17]; + uint32_t revr; /* revision register */ + uint32_t fear; /* feature register */ +}; + +/* Control Register 0 */ +#define CR0_FFMT_MASK (7 << 12) +#define CR0_FFMT_SSP (0 << 12) +#define CR0_FFMT_SPI (1 << 12) +#define CR0_FFMT_MICROWIRE (2 << 12) +#define CR0_FFMT_I2S (3 << 12) +#define CR0_FFMT_AC97 (4 << 12) +#define CR0_FLASH (1 << 11) +#define CR0_FSDIST(x) (((x) & 0x03) << 8) +#define CR0_LOOP (1 << 7) /* loopback mode */ +#define CR0_LSB (1 << 6) /* LSB */ +#define CR0_FSPO (1 << 5) /* fs atcive low (I2S only) */ +#define CR0_FSJUSTIFY (1 << 4) +#define CR0_OPM_SLAVE (0 << 2) +#define CR0_OPM_MASTER (3 << 2) +#define CR0_OPM_I2S_MSST (3 << 2) /* master stereo mode */ +#define CR0_OPM_I2S_MSMO (2 << 2) /* master mono mode */ +#define CR0_OPM_I2S_SLST (1 << 2) /* slave stereo mode */ +#define CR0_OPM_I2S_SLMO (0 << 2) /* slave mono mode */ +#define CR0_SCLKPO (1 << 1) /* clock polarity */ +#define CR0_SCLKPH (1 << 0) /* clock phase */ + +/* Control Register 1 */ +#define CR1_PDL(x) (((x) & 0xff) << 24) /* padding length */ +#define CR1_SDL(x) ((((x) - 1) & 0x1f) << 16) /* data length */ +#define CR1_DIV(x) (((x) - 1) & 0xffff) /* clock divider */ + +/* Control Register 2 */ +#define CR2_CS(x) (((x) & 3) << 10) /* CS/FS select */ +#define CR2_FS (1 << 9) /* CS/FS signal level */ +#define CR2_TXEN (1 << 8) /* tx enable */ +#define CR2_RXEN (1 << 7) /* rx enable */ +#define CR2_RESET (1 << 6) /* chip reset */ +#define CR2_TXFC (1 << 3) /* tx fifo Clear */ +#define CR2_RXFC (1 << 2) /* rx fifo Clear */ +#define CR2_TXDOE (1 << 1) /* tx data output enable */ +#define CR2_EN (1 << 0) /* chip enable */ + +/* Status Register */ +#define SR_RFF (1 << 0) /* rx fifo full */ +#define SR_TFNF (1 << 1) /* tx fifo not full */ +#define SR_BUSY (1 << 2) /* chip busy */ +#define SR_RFVE(reg) (((reg) >> 4) & 0x1f) /* rx fifo valid entries */ +#define SR_TFVE(reg) (((reg) >> 12) & 0x1f) /* tx fifo valid entries */ + +/* Feature Register */ +#define FEAR_BITS(reg) ((((reg) >> 0) & 0xff) + 1) /* data width */ +#define FEAR_RFSZ(reg) ((((reg) >> 8) & 0xff) + 1) /* rx fifo size */ +#define FEAR_TFSZ(reg) ((((reg) >> 16) & 0xff) + 1) /* tx fifo size */ +#define FEAR_AC97 (1 << 24) +#define FEAR_I2S (1 << 25) +#define FEAR_SPI_MWR (1 << 26) +#define FEAR_SSP (1 << 27) +#define FEAR_SPDIF (1 << 28) + +/* FTGPIO010 chip registers */ +struct ftgpio010_regs { + uint32_t out; /* 0x00: Data Output */ + uint32_t in; /* 0x04: Data Input */ + uint32_t dir; /* 0x08: Direction */ + uint32_t bypass; /* 0x0c: Bypass */ + uint32_t set; /* 0x10: Data Set */ + uint32_t clr; /* 0x14: Data Clear */ + uint32_t pull_up; /* 0x18: Pull-Up Enabled */ + uint32_t pull_st; /* 0x1c: Pull State (0=pull-down, 1=pull-up) */ +}; + +struct ftssp010_gpio { + struct ftgpio010_regs *regs; + uint32_t pin; +}; + +struct ftssp010_spi { + struct spi_slave slave; + struct ftssp010_gpio gpio; + struct ftssp010_regs *regs; + uint32_t fifo; + uint32_t mode; + uint32_t div; + uint32_t clk; + uint32_t speed; + uint32_t revision; +}; + +static inline struct ftssp010_spi *to_ftssp010_spi(struct spi_slave *slave) +{ + return container_of(slave, struct ftssp010_spi, slave); +} + +static int get_spi_chip(int bus, struct ftssp010_spi *chip) +{ + uint32_t fear, base[] = CONFIG_FTSSP010_BASE_LIST; + + if (bus >= ARRAY_SIZE(base) || !base[bus]) + return -1; + + chip->regs = (struct ftssp010_regs *)base[bus]; + + chip->revision = readl(&chip->regs->revr); + + fear = readl(&chip->regs->fear); + chip->fifo = min_t(uint32_t, FEAR_TFSZ(fear), FEAR_RFSZ(fear)); + + return 0; +} + +static int get_spi_gpio(int bus, struct ftssp010_gpio *chip) +{ + uint32_t base[] = CONFIG_FTSSP010_GPIO_LIST; + + if (bus >= ARRAY_SIZE(base) || !base[bus]) + return -1; + + chip->regs = (struct ftgpio010_regs *)(base[bus] & 0xfff00000); + chip->pin = base[bus] & 0x1f; + + /* make it an output pin */ + setbits_le32(&chip->regs->dir, 1 << chip->pin); + + return 0; +} + +static int ftssp010_wait(struct ftssp010_spi *chip) +{ + struct ftssp010_regs *regs = chip->regs; + int ret = -1; + ulong t; + + /* wait until device idle */ + for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { + if (readl(®s->sr) & SR_BUSY) + continue; + ret = 0; + break; + } + + if (ret) + puts("ftspi010: busy timeout\n"); + + return ret; +} + +static int ftssp010_wait_tx(struct ftssp010_spi *chip) +{ + struct ftssp010_regs *regs = chip->regs; + int ret = -1; + ulong t; + + /* wait until tx fifo not full */ + for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { + if (!(readl(®s->sr) & SR_TFNF)) + continue; + ret = 0; + break; + } + + if (ret) + puts("ftssp010: tx timeout\n"); + + return ret; +} + +static int ftssp010_wait_rx(struct ftssp010_spi *chip) +{ + struct ftssp010_regs *regs = chip->regs; + int ret = -1; + ulong t; + + /* wait until rx fifo not empty */ + for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { + if (!SR_RFVE(readl(®s->sr))) + continue; + ret = 0; + break; + } + + if (ret) + puts("ftssp010: rx timeout\n"); + + return ret; +} + +static int ftssp010_spi_work_transfer_v2(struct ftssp010_spi *chip, + const void *tx_buf, void *rx_buf, int len, uint flags) +{ + struct ftssp010_regs *regs = chip->regs; + const uint8_t *txb = tx_buf; + uint8_t *rxb = rx_buf; + + while (len > 0) { + int i, depth = min(chip->fifo >> 2, len); + uint32_t xmsk = 0; + + if (tx_buf) { + for (i = 0; i < depth; ++i) { + ftssp010_wait_tx(chip); + writel(*txb++, ®s->dr); + } + xmsk |= CR2_TXEN | CR2_TXDOE; + if ((readl(®s->cr[2]) & xmsk) != xmsk) + setbits_le32(®s->cr[2], xmsk); + } + if (rx_buf) { + xmsk |= CR2_RXEN; + if ((readl(®s->cr[2]) & xmsk) != xmsk) + setbits_le32(®s->cr[2], xmsk); + for (i = 0; i < depth; ++i) { + ftssp010_wait_rx(chip); + *rxb++ = (uint8_t)readl(®s->dr); + } + } + + len -= depth; + } + + return 0; +} + +static int ftssp010_spi_work_transfer_v1(struct ftssp010_spi *chip, + const void *tx_buf, void *rx_buf, int len, uint flags) +{ + struct ftssp010_regs *regs = chip->regs; + const uint8_t *txb = tx_buf; + uint8_t *rxb = rx_buf; + + while (len > 0) { + int i, depth = min(chip->fifo >> 2, len); + uint32_t tmp; + + for (i = 0; i < depth; ++i) { + ftssp010_wait_tx(chip); + writel(txb ? (*txb++) : 0, ®s->dr); + } + for (i = 0; i < depth; ++i) { + ftssp010_wait_rx(chip); + tmp = readl(®s->dr); + if (rxb) + *rxb++ = (uint8_t)tmp; + } + + len -= depth; + } + + return 0; +} + +static void ftssp010_cs_set(struct ftssp010_spi *chip, int high) +{ + struct ftssp010_regs *regs = chip->regs; + struct ftssp010_gpio *gpio = &chip->gpio; + uint32_t mask; + + /* cs pull high/low */ + if (chip->revision >= 0x11900) { + mask = CR2_CS(chip->slave.cs) | (high ? CR2_FS : 0); + writel(mask, ®s->cr[2]); + } else if (gpio->regs) { + mask = 1 << gpio->pin; + if (high) + writel(mask, &gpio->regs->set); + else + writel(mask, &gpio->regs->clr); + } + + /* extra delay for signal propagation */ + udelay_masked(1); +} + +/* + * Determine if a SPI chipselect is valid. + * This function is provided by the board if the low-level SPI driver + * needs it to determine if a given chipselect is actually valid. + * + * Returns: 1 if bus:cs identifies a valid chip on this board, 0 + * otherwise. + */ +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + struct ftssp010_spi chip; + + if (get_spi_chip(bus, &chip)) + return 0; + + if (!cs) + return 1; + else if ((cs < 4) && (chip.revision >= 0x11900)) + return 1; + + return 0; +} + +/* + * Activate a SPI chipselect. + * This function is provided by the board code when using a driver + * that can't control its chipselects automatically (e.g. + * common/soft_spi.c). When called, it should activate the chip select + * to the device identified by "slave". + */ +void spi_cs_activate(struct spi_slave *slave) +{ + struct ftssp010_spi *chip = to_ftssp010_spi(slave); + struct ftssp010_regs *regs = chip->regs; + + /* cs pull */ + if (chip->mode & SPI_CS_HIGH) + ftssp010_cs_set(chip, 1); + else + ftssp010_cs_set(chip, 0); + + /* chip enable + fifo clear */ + setbits_le32(®s->cr[2], CR2_EN | CR2_TXFC | CR2_RXFC); +} + +/* + * Deactivate a SPI chipselect. + * This function is provided by the board code when using a driver + * that can't control its chipselects automatically (e.g. + * common/soft_spi.c). When called, it should deactivate the chip + * select to the device identified by "slave". + */ +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct ftssp010_spi *chip = to_ftssp010_spi(slave); + + /* wait until chip idle */ + ftssp010_wait(chip); + + /* cs pull */ + if (chip->mode & SPI_CS_HIGH) + ftssp010_cs_set(chip, 0); + else + ftssp010_cs_set(chip, 1); +} + +void spi_init(void) +{ + /* nothing to do */ +} + +struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode) +{ + struct ftssp010_spi *chip; + + if (mode & SPI_3WIRE) { + puts("ftssp010: can't do 3-wire\n"); + return NULL; + } + + if (mode & SPI_SLAVE) { + puts("ftssp010: can't do slave mode\n"); + return NULL; + } + + if (mode & SPI_PREAMBLE) { + puts("ftssp010: can't skip preamble bytes\n"); + return NULL; + } + + if (!spi_cs_is_valid(bus, cs)) { + puts("ftssp010: invalid (bus, cs)\n"); + return NULL; + } + + chip = spi_alloc_slave(struct ftssp010_spi, bus, cs); + if (!chip) + return NULL; + + if (get_spi_chip(bus, chip)) + goto free_out; + + if (chip->revision < 0x11900 && get_spi_gpio(bus, &chip->gpio)) { + puts("ftssp010: Before revision 1.19.0, its clock & cs are\n" + "controlled by tx engine which is not synced with rx engine,\n" + "so the clock & cs might be shutdown before rx engine\n" + "finishs its jobs.\n" + "If possible, please add a dedicated gpio for it.\n"); + } + + chip->mode = mode; + chip->clk = CONFIG_FTSSP010_CLOCK; + chip->div = 2; + if (max_hz) { + while (chip->div < 0xffff) { + if ((chip->clk / (2 * chip->div)) <= max_hz) + break; + chip->div += 1; + } + } + chip->speed = chip->clk / (2 * chip->div); + + return &chip->slave; + +free_out: + free(chip); + return NULL; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct ftssp010_spi *chip = to_ftssp010_spi(slave); + struct ftssp010_regs *regs = chip->regs; + + writel(CR1_SDL(8) | CR1_DIV(chip->div), ®s->cr[1]); + + if (chip->revision >= 0x11900) { + writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO | CR0_FLASH, + ®s->cr[0]); + writel(CR2_TXFC | CR2_RXFC, + ®s->cr[2]); + } else { + writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO, + ®s->cr[0]); + writel(CR2_TXFC | CR2_RXFC | CR2_EN | CR2_TXDOE, + ®s->cr[2]); + } + + if (chip->mode & SPI_LOOP) + setbits_le32(®s->cr[0], CR0_LOOP); + + if (chip->mode & SPI_CPOL) + setbits_le32(®s->cr[0], CR0_SCLKPO); + + if (chip->mode & SPI_CPHA) + setbits_le32(®s->cr[0], CR0_SCLKPH); + + spi_cs_deactivate(slave); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct ftssp010_spi *chip = to_ftssp010_spi(slave); + struct ftssp010_regs *regs = chip->regs; + + writel(0, ®s->cr[2]); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct ftssp010_spi *chip = to_ftssp010_spi(slave); + uint32_t len = bitlen >> 3; + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + if (chip->revision >= 0x11900) + ftssp010_spi_work_transfer_v2(chip, dout, din, len, flags); + else + ftssp010_spi_work_transfer_v1(chip, dout, din, len, flags); + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/ich.c b/qemu/roms/u-boot/drivers/spi/ich.c new file mode 100644 index 000000000..f5c6f3e7d --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/ich.c @@ -0,0 +1,741 @@ +/* + * Copyright (c) 2011-12 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This file is derived from the flashrom project. + */ + +#include +#include +#include +#include +#include +#include + +#include "ich.h" + +#define SPI_OPCODE_WREN 0x06 +#define SPI_OPCODE_FAST_READ 0x0b + +struct ich_ctlr { + pci_dev_t dev; /* PCI device number */ + int ich_version; /* Controller version, 7 or 9 */ + int ichspi_lock; + int locked; + uint8_t *opmenu; + int menubytes; + void *base; /* Base of register set */ + uint16_t *preop; + uint16_t *optype; + uint32_t *addr; + uint8_t *data; + unsigned databytes; + uint8_t *status; + uint16_t *control; + uint32_t *bbar; + uint32_t *pr; /* only for ich9 */ + uint8_t *speed; /* pointer to speed control */ + ulong max_speed; /* Maximum bus speed in MHz */ +}; + +struct ich_ctlr ctlr; + +static inline struct ich_spi_slave *to_ich_spi(struct spi_slave *slave) +{ + return container_of(slave, struct ich_spi_slave, slave); +} + +static unsigned int ich_reg(const void *addr) +{ + return (unsigned)(addr - ctlr.base) & 0xffff; +} + +static u8 ich_readb(const void *addr) +{ + u8 value = readb(addr); + + debug("read %2.2x from %4.4x\n", value, ich_reg(addr)); + + return value; +} + +static u16 ich_readw(const void *addr) +{ + u16 value = readw(addr); + + debug("read %4.4x from %4.4x\n", value, ich_reg(addr)); + + return value; +} + +static u32 ich_readl(const void *addr) +{ + u32 value = readl(addr); + + debug("read %8.8x from %4.4x\n", value, ich_reg(addr)); + + return value; +} + +static void ich_writeb(u8 value, void *addr) +{ + writeb(value, addr); + debug("wrote %2.2x to %4.4x\n", value, ich_reg(addr)); +} + +static void ich_writew(u16 value, void *addr) +{ + writew(value, addr); + debug("wrote %4.4x to %4.4x\n", value, ich_reg(addr)); +} + +static void ich_writel(u32 value, void *addr) +{ + writel(value, addr); + debug("wrote %8.8x to %4.4x\n", value, ich_reg(addr)); +} + +static void write_reg(const void *value, void *dest, uint32_t size) +{ + memcpy_toio(dest, value, size); +} + +static void read_reg(const void *src, void *value, uint32_t size) +{ + memcpy_fromio(value, src, size); +} + +static void ich_set_bbar(struct ich_ctlr *ctlr, uint32_t minaddr) +{ + const uint32_t bbar_mask = 0x00ffff00; + uint32_t ichspi_bbar; + + minaddr &= bbar_mask; + ichspi_bbar = ich_readl(ctlr->bbar) & ~bbar_mask; + ichspi_bbar |= minaddr; + ich_writel(ichspi_bbar, ctlr->bbar); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + puts("spi_cs_is_valid used but not implemented\n"); + return 0; +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct ich_spi_slave *ich; + + ich = spi_alloc_slave(struct ich_spi_slave, bus, cs); + if (!ich) { + puts("ICH SPI: Out of memory\n"); + return NULL; + } + + /* + * Yes this controller can only write a small number of bytes at + * once! The limit is typically 64 bytes. + */ + ich->slave.max_write_size = ctlr.databytes; + ich->speed = max_hz; + + return &ich->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct ich_spi_slave *ich = to_ich_spi(slave); + + free(ich); +} + +/* + * Check if this device ID matches one of supported Intel PCH devices. + * + * Return the ICH version if there is a match, or zero otherwise. + */ +static int get_ich_version(uint16_t device_id) +{ + if (device_id == PCI_DEVICE_ID_INTEL_TGP_LPC) + return 7; + + if ((device_id >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN && + device_id <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX) || + (device_id >= PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MIN && + device_id <= PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MAX)) + return 9; + + return 0; +} + +/* @return 1 if the SPI flash supports the 33MHz speed */ +static int ich9_can_do_33mhz(pci_dev_t dev) +{ + u32 fdod, speed; + + /* Observe SPI Descriptor Component Section 0 */ + pci_write_config_dword(dev, 0xb0, 0x1000); + + /* Extract the Write/Erase SPI Frequency from descriptor */ + pci_read_config_dword(dev, 0xb4, &fdod); + + /* Bits 23:21 have the fast read clock frequency, 0=20MHz, 1=33MHz */ + speed = (fdod >> 21) & 7; + + return speed == 1; +} + +static int ich_find_spi_controller(pci_dev_t *devp, int *ich_versionp) +{ + int last_bus = pci_last_busno(); + int bus; + + if (last_bus == -1) { + debug("No PCI busses?\n"); + return -1; + } + + for (bus = 0; bus <= last_bus; bus++) { + uint16_t vendor_id, device_id; + uint32_t ids; + pci_dev_t dev; + + dev = PCI_BDF(bus, 31, 0); + pci_read_config_dword(dev, 0, &ids); + vendor_id = ids; + device_id = ids >> 16; + + if (vendor_id == PCI_VENDOR_ID_INTEL) { + *devp = dev; + *ich_versionp = get_ich_version(device_id); + return 0; + } + } + + debug("ICH SPI: No ICH found.\n"); + return -1; +} + +static int ich_init_controller(struct ich_ctlr *ctlr) +{ + uint8_t *rcrb; /* Root Complex Register Block */ + uint32_t rcba; /* Root Complex Base Address */ + + pci_read_config_dword(ctlr->dev, 0xf0, &rcba); + /* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable. */ + rcrb = (uint8_t *)(rcba & 0xffffc000); + if (ctlr->ich_version == 7) { + struct ich7_spi_regs *ich7_spi; + + ich7_spi = (struct ich7_spi_regs *)(rcrb + 0x3020); + ctlr->ichspi_lock = ich_readw(&ich7_spi->spis) & SPIS_LOCK; + ctlr->opmenu = ich7_spi->opmenu; + ctlr->menubytes = sizeof(ich7_spi->opmenu); + ctlr->optype = &ich7_spi->optype; + ctlr->addr = &ich7_spi->spia; + ctlr->data = (uint8_t *)ich7_spi->spid; + ctlr->databytes = sizeof(ich7_spi->spid); + ctlr->status = (uint8_t *)&ich7_spi->spis; + ctlr->control = &ich7_spi->spic; + ctlr->bbar = &ich7_spi->bbar; + ctlr->preop = &ich7_spi->preop; + ctlr->base = ich7_spi; + } else if (ctlr->ich_version == 9) { + struct ich9_spi_regs *ich9_spi; + + ich9_spi = (struct ich9_spi_regs *)(rcrb + 0x3800); + ctlr->ichspi_lock = ich_readw(&ich9_spi->hsfs) & HSFS_FLOCKDN; + ctlr->opmenu = ich9_spi->opmenu; + ctlr->menubytes = sizeof(ich9_spi->opmenu); + ctlr->optype = &ich9_spi->optype; + ctlr->addr = &ich9_spi->faddr; + ctlr->data = (uint8_t *)ich9_spi->fdata; + ctlr->databytes = sizeof(ich9_spi->fdata); + ctlr->status = &ich9_spi->ssfs; + ctlr->control = (uint16_t *)ich9_spi->ssfc; + ctlr->speed = ich9_spi->ssfc + 2; + ctlr->bbar = &ich9_spi->bbar; + ctlr->preop = &ich9_spi->preop; + ctlr->pr = &ich9_spi->pr[0]; + ctlr->base = ich9_spi; + } else { + debug("ICH SPI: Unrecognized ICH version %d.\n", + ctlr->ich_version); + return -1; + } + debug("ICH SPI: Version %d detected\n", ctlr->ich_version); + + /* Work out the maximum speed we can support */ + ctlr->max_speed = 20000000; + if (ctlr->ich_version == 9 && ich9_can_do_33mhz(ctlr->dev)) + ctlr->max_speed = 33000000; + + ich_set_bbar(ctlr, 0); + + return 0; +} + +void spi_init(void) +{ + uint8_t bios_cntl; + + if (ich_find_spi_controller(&ctlr.dev, &ctlr.ich_version)) { + printf("ICH SPI: Cannot find device\n"); + return; + } + + if (ich_init_controller(&ctlr)) { + printf("ICH SPI: Cannot setup controller\n"); + return; + } + + /* + * Disable the BIOS write protect so write commands are allowed. On + * v9, deassert SMM BIOS Write Protect Disable. + */ + pci_read_config_byte(ctlr.dev, 0xdc, &bios_cntl); + if (ctlr.ich_version == 9) + bios_cntl &= ~(1 << 5); + pci_write_config_byte(ctlr.dev, 0xdc, bios_cntl | 0x1); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ +} + +void spi_cs_activate(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ +} + +static inline void spi_use_out(struct spi_trans *trans, unsigned bytes) +{ + trans->out += bytes; + trans->bytesout -= bytes; +} + +static inline void spi_use_in(struct spi_trans *trans, unsigned bytes) +{ + trans->in += bytes; + trans->bytesin -= bytes; +} + +static void spi_setup_type(struct spi_trans *trans, int data_bytes) +{ + trans->type = 0xFF; + + /* Try to guess spi type from read/write sizes. */ + if (trans->bytesin == 0) { + if (trans->bytesout + data_bytes > 4) + /* + * If bytesin = 0 and bytesout > 4, we presume this is + * a write data operation, which is accompanied by an + * address. + */ + trans->type = SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS; + else + trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS; + return; + } + + if (trans->bytesout == 1) { /* and bytesin is > 0 */ + trans->type = SPI_OPCODE_TYPE_READ_NO_ADDRESS; + return; + } + + if (trans->bytesout == 4) /* and bytesin is > 0 */ + trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS; + + /* Fast read command is called with 5 bytes instead of 4 */ + if (trans->out[0] == SPI_OPCODE_FAST_READ && trans->bytesout == 5) { + trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS; + --trans->bytesout; + } +} + +static int spi_setup_opcode(struct spi_trans *trans) +{ + uint16_t optypes; + uint8_t opmenu[ctlr.menubytes]; + + trans->opcode = trans->out[0]; + spi_use_out(trans, 1); + if (!ctlr.ichspi_lock) { + /* The lock is off, so just use index 0. */ + ich_writeb(trans->opcode, ctlr.opmenu); + optypes = ich_readw(ctlr.optype); + optypes = (optypes & 0xfffc) | (trans->type & 0x3); + ich_writew(optypes, ctlr.optype); + return 0; + } else { + /* The lock is on. See if what we need is on the menu. */ + uint8_t optype; + uint16_t opcode_index; + + /* Write Enable is handled as atomic prefix */ + if (trans->opcode == SPI_OPCODE_WREN) + return 0; + + read_reg(ctlr.opmenu, opmenu, sizeof(opmenu)); + for (opcode_index = 0; opcode_index < ctlr.menubytes; + opcode_index++) { + if (opmenu[opcode_index] == trans->opcode) + break; + } + + if (opcode_index == ctlr.menubytes) { + printf("ICH SPI: Opcode %x not found\n", + trans->opcode); + return -1; + } + + optypes = ich_readw(ctlr.optype); + optype = (optypes >> (opcode_index * 2)) & 0x3; + if (trans->type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS && + optype == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS && + trans->bytesout >= 3) { + /* We guessed wrong earlier. Fix it up. */ + trans->type = optype; + } + if (optype != trans->type) { + printf("ICH SPI: Transaction doesn't fit type %d\n", + optype); + return -1; + } + return opcode_index; + } +} + +static int spi_setup_offset(struct spi_trans *trans) +{ + /* Separate the SPI address and data. */ + switch (trans->type) { + case SPI_OPCODE_TYPE_READ_NO_ADDRESS: + case SPI_OPCODE_TYPE_WRITE_NO_ADDRESS: + return 0; + case SPI_OPCODE_TYPE_READ_WITH_ADDRESS: + case SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS: + trans->offset = ((uint32_t)trans->out[0] << 16) | + ((uint32_t)trans->out[1] << 8) | + ((uint32_t)trans->out[2] << 0); + spi_use_out(trans, 3); + return 1; + default: + printf("Unrecognized SPI transaction type %#x\n", trans->type); + return -1; + } +} + +/* + * Wait for up to 6s til status register bit(s) turn 1 (in case wait_til_set + * below is true) or 0. In case the wait was for the bit(s) to set - write + * those bits back, which would cause resetting them. + * + * Return the last read status value on success or -1 on failure. + */ +static int ich_status_poll(u16 bitmask, int wait_til_set) +{ + int timeout = 600000; /* This will result in 6s */ + u16 status = 0; + + while (timeout--) { + status = ich_readw(ctlr.status); + if (wait_til_set ^ ((status & bitmask) == 0)) { + if (wait_til_set) + ich_writew((status & bitmask), ctlr.status); + return status; + } + udelay(10); + } + + printf("ICH SPI: SCIP timeout, read %x, expected %x\n", + status, bitmask); + return -1; +} + +/* +int spi_xfer(struct spi_slave *slave, const void *dout, + unsigned int bitsout, void *din, unsigned int bitsin) +*/ +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct ich_spi_slave *ich = to_ich_spi(slave); + uint16_t control; + int16_t opcode_index; + int with_address; + int status; + int bytes = bitlen / 8; + struct spi_trans *trans = &ich->trans; + unsigned type = flags & (SPI_XFER_BEGIN | SPI_XFER_END); + int using_cmd = 0; + /* Align read transactions to 64-byte boundaries */ + char buff[ctlr.databytes]; + + /* Ee don't support writing partial bytes. */ + if (bitlen % 8) { + debug("ICH SPI: Accessing partial bytes not supported\n"); + return -1; + } + + /* An empty end transaction can be ignored */ + if (type == SPI_XFER_END && !dout && !din) + return 0; + + if (type & SPI_XFER_BEGIN) + memset(trans, '\0', sizeof(*trans)); + + /* Dp we need to come back later to finish it? */ + if (dout && type == SPI_XFER_BEGIN) { + if (bytes > ICH_MAX_CMD_LEN) { + debug("ICH SPI: Command length limit exceeded\n"); + return -1; + } + memcpy(trans->cmd, dout, bytes); + trans->cmd_len = bytes; + debug("ICH SPI: Saved %d bytes\n", bytes); + return 0; + } + + /* + * We process a 'middle' spi_xfer() call, which has no + * SPI_XFER_BEGIN/END, as an independent transaction as if it had + * an end. We therefore repeat the command. This is because ICH + * seems to have no support for this, or because interest (in digging + * out the details and creating a special case in the code) is low. + */ + if (trans->cmd_len) { + trans->out = trans->cmd; + trans->bytesout = trans->cmd_len; + using_cmd = 1; + debug("ICH SPI: Using %d bytes\n", trans->cmd_len); + } else { + trans->out = dout; + trans->bytesout = dout ? bytes : 0; + } + + trans->in = din; + trans->bytesin = din ? bytes : 0; + + /* There has to always at least be an opcode. */ + if (!trans->bytesout) { + debug("ICH SPI: No opcode for transfer\n"); + return -1; + } + + if (ich_status_poll(SPIS_SCIP, 0) == -1) + return -1; + + ich_writew(SPIS_CDS | SPIS_FCERR, ctlr.status); + + spi_setup_type(trans, using_cmd ? bytes : 0); + opcode_index = spi_setup_opcode(trans); + if (opcode_index < 0) + return -1; + with_address = spi_setup_offset(trans); + if (with_address < 0) + return -1; + + if (trans->opcode == SPI_OPCODE_WREN) { + /* + * Treat Write Enable as Atomic Pre-Op if possible + * in order to prevent the Management Engine from + * issuing a transaction between WREN and DATA. + */ + if (!ctlr.ichspi_lock) + ich_writew(trans->opcode, ctlr.preop); + return 0; + } + + if (ctlr.speed && ctlr.max_speed >= 33000000) { + int byte; + + byte = ich_readb(ctlr.speed); + if (ich->speed >= 33000000) + byte |= SSFC_SCF_33MHZ; + else + byte &= ~SSFC_SCF_33MHZ; + ich_writeb(byte, ctlr.speed); + } + + /* See if we have used up the command data */ + if (using_cmd && dout && bytes) { + trans->out = dout; + trans->bytesout = bytes; + debug("ICH SPI: Moving to data, %d bytes\n", bytes); + } + + /* Preset control fields */ + control = ich_readw(ctlr.control); + control &= ~SSFC_RESERVED; + control = SPIC_SCGO | ((opcode_index & 0x07) << 4); + + /* Issue atomic preop cycle if needed */ + if (ich_readw(ctlr.preop)) + control |= SPIC_ACS; + + if (!trans->bytesout && !trans->bytesin) { + /* SPI addresses are 24 bit only */ + if (with_address) + ich_writel(trans->offset & 0x00FFFFFF, ctlr.addr); + + /* + * This is a 'no data' command (like Write Enable), its + * bitesout size was 1, decremented to zero while executing + * spi_setup_opcode() above. Tell the chip to send the + * command. + */ + ich_writew(control, ctlr.control); + + /* wait for the result */ + status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1); + if (status == -1) + return -1; + + if (status & SPIS_FCERR) { + debug("ICH SPI: Command transaction error\n"); + return -1; + } + + return 0; + } + + /* + * Check if this is a write command atempting to transfer more bytes + * than the controller can handle. Iterations for writes are not + * supported here because each SPI write command needs to be preceded + * and followed by other SPI commands, and this sequence is controlled + * by the SPI chip driver. + */ + if (trans->bytesout > ctlr.databytes) { + debug("ICH SPI: Too much to write. This should be prevented by the driver's max_write_size?\n"); + return -1; + } + + /* + * Read or write up to databytes bytes at a time until everything has + * been sent. + */ + while (trans->bytesout || trans->bytesin) { + uint32_t data_length; + uint32_t aligned_offset; + uint32_t diff; + + aligned_offset = trans->offset & ~(ctlr.databytes - 1); + diff = trans->offset - aligned_offset; + + /* SPI addresses are 24 bit only */ + ich_writel(aligned_offset & 0x00FFFFFF, ctlr.addr); + + if (trans->bytesout) + data_length = min(trans->bytesout, ctlr.databytes); + else + data_length = min(trans->bytesin, ctlr.databytes); + + /* Program data into FDATA0 to N */ + if (trans->bytesout) { + write_reg(trans->out, ctlr.data, data_length); + spi_use_out(trans, data_length); + if (with_address) + trans->offset += data_length; + } + + /* Add proper control fields' values */ + control &= ~((ctlr.databytes - 1) << 8); + control |= SPIC_DS; + control |= (data_length - 1) << 8; + + /* write it */ + ich_writew(control, ctlr.control); + + /* Wait for Cycle Done Status or Flash Cycle Error. */ + status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1); + if (status == -1) + return -1; + + if (status & SPIS_FCERR) { + debug("ICH SPI: Data transaction error\n"); + return -1; + } + + if (trans->bytesin) { + if (diff) { + data_length -= diff; + read_reg(ctlr.data, buff, ctlr.databytes); + memcpy(trans->in, buff + diff, data_length); + } else { + read_reg(ctlr.data, trans->in, data_length); + } + spi_use_in(trans, data_length); + if (with_address) + trans->offset += data_length; + } + } + + /* Clear atomic preop now that xfer is done */ + ich_writew(0, ctlr.preop); + + return 0; +} + + +/* + * This uses the SPI controller from the Intel Cougar Point and Panther Point + * PCH to write-protect portions of the SPI flash until reboot. The changes + * don't actually take effect until the HSFS[FLOCKDN] bit is set, but that's + * done elsewhere. + */ +int spi_write_protect_region(uint32_t lower_limit, uint32_t length, int hint) +{ + uint32_t tmplong; + uint32_t upper_limit; + + if (!ctlr.pr) { + printf("%s: operation not supported on this chipset\n", + __func__); + return -1; + } + + if (length == 0 || + lower_limit > (0xFFFFFFFFUL - length) + 1 || + hint < 0 || hint > 4) { + printf("%s(0x%x, 0x%x, %d): invalid args\n", __func__, + lower_limit, length, hint); + return -1; + } + + upper_limit = lower_limit + length - 1; + + /* + * Determine bits to write, as follows: + * 31 Write-protection enable (includes erase operation) + * 30:29 reserved + * 28:16 Upper Limit (FLA address bits 24:12, with 11:0 == 0xfff) + * 15 Read-protection enable + * 14:13 reserved + * 12:0 Lower Limit (FLA address bits 24:12, with 11:0 == 0x000) + */ + tmplong = 0x80000000 | + ((upper_limit & 0x01fff000) << 4) | + ((lower_limit & 0x01fff000) >> 12); + + printf("%s: writing 0x%08x to %p\n", __func__, tmplong, + &ctlr.pr[hint]); + ctlr.pr[hint] = tmplong; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/ich.h b/qemu/roms/u-boot/drivers/spi/ich.h new file mode 100644 index 000000000..d2e4b8523 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/ich.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This file is derived from the flashrom project. + */ + +struct ich7_spi_regs { + uint16_t spis; + uint16_t spic; + uint32_t spia; + uint64_t spid[8]; + uint64_t _pad; + uint32_t bbar; + uint16_t preop; + uint16_t optype; + uint8_t opmenu[8]; +} __packed; + +struct ich9_spi_regs { + uint32_t bfpr; /* 0x00 */ + uint16_t hsfs; + uint16_t hsfc; + uint32_t faddr; + uint32_t _reserved0; + uint32_t fdata[16]; /* 0x10 */ + uint32_t frap; /* 0x50 */ + uint32_t freg[5]; + uint32_t _reserved1[3]; + uint32_t pr[5]; /* 0x74 */ + uint32_t _reserved2[2]; + uint8_t ssfs; /* 0x90 */ + uint8_t ssfc[3]; + uint16_t preop; /* 0x94 */ + uint16_t optype; + uint8_t opmenu[8]; /* 0x98 */ + uint32_t bbar; + uint8_t _reserved3[12]; + uint32_t fdoc; + uint32_t fdod; + uint8_t _reserved4[8]; + uint32_t afc; + uint32_t lvscc; + uint32_t uvscc; + uint8_t _reserved5[4]; + uint32_t fpb; + uint8_t _reserved6[28]; + uint32_t srdl; + uint32_t srdc; + uint32_t srd; +} __packed; + +enum { + SPIS_SCIP = 0x0001, + SPIS_GRANT = 0x0002, + SPIS_CDS = 0x0004, + SPIS_FCERR = 0x0008, + SSFS_AEL = 0x0010, + SPIS_LOCK = 0x8000, + SPIS_RESERVED_MASK = 0x7ff0, + SSFS_RESERVED_MASK = 0x7fe2 +}; + +enum { + SPIC_SCGO = 0x000002, + SPIC_ACS = 0x000004, + SPIC_SPOP = 0x000008, + SPIC_DBC = 0x003f00, + SPIC_DS = 0x004000, + SPIC_SME = 0x008000, + SSFC_SCF_MASK = 0x070000, + SSFC_RESERVED = 0xf80000, + + /* Mask for speed byte, biuts 23:16 of SSFC */ + SSFC_SCF_33MHZ = 0x01, +}; + +enum { + HSFS_FDONE = 0x0001, + HSFS_FCERR = 0x0002, + HSFS_AEL = 0x0004, + HSFS_BERASE_MASK = 0x0018, + HSFS_BERASE_SHIFT = 3, + HSFS_SCIP = 0x0020, + HSFS_FDOPSS = 0x2000, + HSFS_FDV = 0x4000, + HSFS_FLOCKDN = 0x8000 +}; + +enum { + HSFC_FGO = 0x0001, + HSFC_FCYCLE_MASK = 0x0006, + HSFC_FCYCLE_SHIFT = 1, + HSFC_FDBC_MASK = 0x3f00, + HSFC_FDBC_SHIFT = 8, + HSFC_FSMIE = 0x8000 +}; + +enum { + SPI_OPCODE_TYPE_READ_NO_ADDRESS = 0, + SPI_OPCODE_TYPE_WRITE_NO_ADDRESS = 1, + SPI_OPCODE_TYPE_READ_WITH_ADDRESS = 2, + SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS = 3 +}; + +enum { + ICH_MAX_CMD_LEN = 5, +}; + +struct spi_trans { + uint8_t cmd[ICH_MAX_CMD_LEN]; + int cmd_len; + const uint8_t *out; + uint32_t bytesout; + uint8_t *in; + uint32_t bytesin; + uint8_t type; + uint8_t opcode; + uint32_t offset; +}; + +struct ich_spi_slave { + struct spi_slave slave; + struct spi_trans trans; /* current transaction in progress */ + int speed; /* SPI speed in Hz */ +}; diff --git a/qemu/roms/u-boot/drivers/spi/kirkwood_spi.c b/qemu/roms/u-boot/drivers/spi/kirkwood_spi.c new file mode 100644 index 000000000..942a208c2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/kirkwood_spi.c @@ -0,0 +1,214 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * Derived from drivers/spi/mpc8xxx_spi.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct kwspi_registers *spireg = (struct kwspi_registers *)KW_SPI_BASE; + +u32 cs_spi_mpp_back[2]; + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct spi_slave *slave; + u32 data; + static const u32 kwspi_mpp_config[2][2] = { + { MPP0_SPI_SCn, 0 }, /* if cs == 0 */ + { MPP7_SPI_SCn, 0 } /* if cs != 0 */ + }; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + slave = spi_alloc_slave_base(bus, cs); + if (!slave) + return NULL; + + writel(~KWSPI_CSN_ACT | KWSPI_SMEMRDY, &spireg->ctrl); + + /* calculate spi clock prescaller using max_hz */ + data = ((CONFIG_SYS_TCLK / 2) / max_hz) + 0x10; + data = data < KWSPI_CLKPRESCL_MIN ? KWSPI_CLKPRESCL_MIN : data; + data = data > KWSPI_CLKPRESCL_MASK ? KWSPI_CLKPRESCL_MASK : data; + + /* program spi clock prescaller using max_hz */ + writel(KWSPI_ADRLEN_3BYTE | data, &spireg->cfg); + debug("data = 0x%08x \n", data); + + writel(KWSPI_SMEMRDIRQ, &spireg->irq_cause); + writel(KWSPI_IRQMASK, &spireg->irq_mask); + + /* program mpp registers to select SPI_CSn */ + kirkwood_mpp_conf(kwspi_mpp_config[cs ? 1 : 0], cs_spi_mpp_back); + + return slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + kirkwood_mpp_conf(cs_spi_mpp_back, NULL); + free(slave); +} + +#if defined(CONFIG_SYS_KW_SPI_MPP) +u32 spi_mpp_backup[4]; +#endif + +__attribute__((weak)) int board_spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +int spi_claim_bus(struct spi_slave *slave) +{ +#if defined(CONFIG_SYS_KW_SPI_MPP) + u32 config; + u32 spi_mpp_config[4]; + + config = CONFIG_SYS_KW_SPI_MPP; + + if (config & MOSI_MPP6) + spi_mpp_config[0] = MPP6_SPI_MOSI; + else + spi_mpp_config[0] = MPP1_SPI_MOSI; + + if (config & SCK_MPP10) + spi_mpp_config[1] = MPP10_SPI_SCK; + else + spi_mpp_config[1] = MPP2_SPI_SCK; + + if (config & MISO_MPP11) + spi_mpp_config[2] = MPP11_SPI_MISO; + else + spi_mpp_config[2] = MPP3_SPI_MISO; + + spi_mpp_config[3] = 0; + spi_mpp_backup[3] = 0; + + /* set new spi mpp and save current mpp config */ + kirkwood_mpp_conf(spi_mpp_config, spi_mpp_backup); + +#endif + + return board_spi_claim_bus(slave); +} + +__attribute__((weak)) void board_spi_release_bus(struct spi_slave *slave) +{ +} + +void spi_release_bus(struct spi_slave *slave) +{ +#if defined(CONFIG_SYS_KW_SPI_MPP) + kirkwood_mpp_conf(spi_mpp_backup, NULL); +#endif + + board_spi_release_bus(slave); +} + +#ifndef CONFIG_SPI_CS_IS_VALID +/* + * you can define this function board specific + * define above CONFIG in board specific config file and + * provide the function in board specific src file + */ +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return (bus == 0 && (cs == 0 || cs == 1)); +} +#endif + +void spi_init(void) +{ +} + +void spi_cs_activate(struct spi_slave *slave) +{ + writel(readl(&spireg->ctrl) | KWSPI_IRQUNMASK, &spireg->ctrl); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + writel(readl(&spireg->ctrl) & KWSPI_IRQMASK, &spireg->ctrl); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + unsigned int tmpdout, tmpdin; + int tm, isread = 0; + + debug("spi_xfer: slave %u:%u dout %p din %p bitlen %u\n", + slave->bus, slave->cs, dout, din, bitlen); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* + * handle data in 8-bit chunks + * TBD: 2byte xfer mode to be enabled + */ + writel(((readl(&spireg->cfg) & ~KWSPI_XFERLEN_MASK) | + KWSPI_XFERLEN_1BYTE), &spireg->cfg); + + while (bitlen > 4) { + debug("loopstart bitlen %d\n", bitlen); + tmpdout = 0; + + /* Shift data so it's msb-justified */ + if (dout) + tmpdout = *(u32 *) dout & 0x0ff; + + writel(~KWSPI_SMEMRDIRQ, &spireg->irq_cause); + writel(tmpdout, &spireg->dout); /* Write the data out */ + debug("*** spi_xfer: ... %08x written, bitlen %d\n", + tmpdout, bitlen); + + /* + * Wait for SPI transmit to get out + * or time out (1 second = 1000 ms) + * The NE event must be read and cleared first + */ + for (tm = 0, isread = 0; tm < KWSPI_TIMEOUT; ++tm) { + if (readl(&spireg->irq_cause) & KWSPI_SMEMRDIRQ) { + isread = 1; + tmpdin = readl(&spireg->din); + debug + ("spi_xfer: din %p..%08x read\n", + din, tmpdin); + + if (din) { + *((u8 *) din) = (u8) tmpdin; + din += 1; + } + if (dout) + dout += 1; + bitlen -= 8; + } + if (isread) + break; + } + if (tm >= KWSPI_TIMEOUT) + printf("*** spi_xfer: Time out during SPI transfer\n"); + + debug("loopend bitlen %d\n", bitlen); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/mpc52xx_spi.c b/qemu/roms/u-boot/drivers/spi/mpc52xx_spi.c new file mode 100644 index 000000000..4613bec2a --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/mpc52xx_spi.c @@ -0,0 +1,90 @@ +/* + * (C) Copyright 2009 + * Frank Bodammer + * (C) Copyright 2009 Semihalf, Grzegorz Bernacki + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +void spi_init(void) +{ + struct mpc5xxx_spi *spi = (struct mpc5xxx_spi *)MPC5XXX_SPI; + /* + * Its important to use the correct order when initializing the + * registers + */ + out_8(&spi->ddr, 0x0F); /* set all SPI pins as output */ + out_8(&spi->pdr, 0x00); /* set SS low */ + /* SPI is master, SS is general purpose output */ + out_8(&spi->cr1, SPI_CR_MSTR | SPI_CR_SPE); + out_8(&spi->cr2, 0x00); /* normal operation */ + out_8(&spi->brr, 0x77); /* baud rate: IPB clock / 2048 */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct spi_slave *slave; + + slave = spi_alloc_slave_base(bus, cs); + if (!slave) + return NULL; + + return slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + return; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct mpc5xxx_spi *spi = (struct mpc5xxx_spi *)MPC5XXX_SPI; + int i, iter = bitlen >> 3; + const uchar *txp = dout; + uchar *rxp = din; + + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(uint *) dout, *(uint *) din, bitlen); + + if (flags & SPI_XFER_BEGIN) + setbits_8(&spi->pdr, SPI_PDR_SS); + + for (i = 0; i < iter; i++) { + udelay(1000); + debug("spi_xfer: sending %x\n", txp[i]); + out_8(&spi->dr, txp[i]); + while (!(in_8(&spi->sr) & SPI_SR_SPIF)) { + udelay(1000); + if (in_8(&spi->sr) & SPI_SR_WCOL) { + rxp[i] = in_8(&spi->dr); + puts("spi_xfer: write collision\n"); + return -1; + } + } + rxp[i] = in_8(&spi->dr); + debug("spi_xfer: received %x\n", rxp[i]); + } + if (flags & SPI_XFER_END) + clrbits_8(&spi->pdr, SPI_PDR_SS); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c b/qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c new file mode 100644 index 000000000..0d59c3615 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/mpc8xxx_spi.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2006 Ben Warren, Qstreams Networks Inc. + * With help from the common/soft_spi and arch/powerpc/cpu/mpc8260 drivers + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include +#include +#include + +#define SPI_EV_NE (0x80000000 >> 22) /* Receiver Not Empty */ +#define SPI_EV_NF (0x80000000 >> 23) /* Transmitter Not Full */ + +#define SPI_MODE_LOOP (0x80000000 >> 1) /* Loopback mode */ +#define SPI_MODE_REV (0x80000000 >> 5) /* Reverse mode - MSB first */ +#define SPI_MODE_MS (0x80000000 >> 6) /* Always master */ +#define SPI_MODE_EN (0x80000000 >> 7) /* Enable interface */ + +#define SPI_TIMEOUT 1000 + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct spi_slave *slave; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + slave = spi_alloc_slave_base(bus, cs); + if (!slave) + return NULL; + + /* + * TODO: Some of the code in spi_init() should probably move + * here, or into spi_claim_bus() below. + */ + + return slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + +void spi_init(void) +{ + volatile spi8xxx_t *spi = &((immap_t *) (CONFIG_SYS_IMMR))->spi; + + /* + * SPI pins on the MPC83xx are not muxed, so all we do is initialize + * some registers + */ + spi->mode = SPI_MODE_REV | SPI_MODE_MS | SPI_MODE_EN; + spi->mode = (spi->mode & 0xfff0ffff) | (1 << 16); /* Use SYSCLK / 8 + (16.67MHz typ.) */ + spi->event = 0xffffffff; /* Clear all SPI events */ + spi->mask = 0x00000000; /* Mask all SPI interrupts */ + spi->com = 0; /* LST bit doesn't do anything, so disregard */ +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + volatile spi8xxx_t *spi = &((immap_t *) (CONFIG_SYS_IMMR))->spi; + unsigned int tmpdout, tmpdin, event; + int numBlks = DIV_ROUND_UP(bitlen, 32); + int tm, isRead = 0; + unsigned char charSize = 32; + + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(uint *) dout, *(uint *) din, bitlen); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + spi->event = 0xffffffff; /* Clear all SPI events */ + + /* handle data in 32-bit chunks */ + while (numBlks--) { + tmpdout = 0; + charSize = (bitlen >= 32 ? 32 : bitlen); + + /* Shift data so it's msb-justified */ + tmpdout = *(u32 *) dout >> (32 - charSize); + + /* The LEN field of the SPMODE register is set as follows: + * + * Bit length setting + * len <= 4 3 + * 4 < len <= 16 len - 1 + * len > 16 0 + */ + + spi->mode &= ~SPI_MODE_EN; + + if (bitlen <= 16) { + if (bitlen <= 4) + spi->mode = (spi->mode & 0xff0fffff) | + (3 << 20); + else + spi->mode = (spi->mode & 0xff0fffff) | + ((bitlen - 1) << 20); + } else { + spi->mode = (spi->mode & 0xff0fffff); + /* Set up the next iteration if sending > 32 bits */ + bitlen -= 32; + dout += 4; + } + + spi->mode |= SPI_MODE_EN; + + spi->tx = tmpdout; /* Write the data out */ + debug("*** spi_xfer: ... %08x written\n", tmpdout); + + /* + * Wait for SPI transmit to get out + * or time out (1 second = 1000 ms) + * The NE event must be read and cleared first + */ + for (tm = 0, isRead = 0; tm < SPI_TIMEOUT; ++tm) { + event = spi->event; + if (event & SPI_EV_NE) { + tmpdin = spi->rx; + spi->event |= SPI_EV_NE; + isRead = 1; + + *(u32 *) din = (tmpdin << (32 - charSize)); + if (charSize == 32) { + /* Advance output buffer by 32 bits */ + din += 4; + } + } + /* + * Only bail when we've had both NE and NF events. + * This will cause timeouts on RO devices, so maybe + * in the future put an arbitrary delay after writing + * the device. Arbitrary delays suck, though... + */ + if (isRead && (event & SPI_EV_NF)) + break; + } + if (tm >= SPI_TIMEOUT) + puts("*** spi_xfer: Time out during SPI transfer"); + + debug("*** spi_xfer: transfer ended. Value=%08x\n", tmpdin); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/mxc_spi.c b/qemu/roms/u-boot/drivers/spi/mxc_spi.c new file mode 100644 index 000000000..f3f029d63 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/mxc_spi.c @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2008, Guennadi Liakhovetski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MX27 +/* i.MX27 has a completely wrong register layout and register definitions in the + * datasheet, the correct one is in the Freescale's Linux driver */ + +#error "i.MX27 CSPI not supported due to drastic differences in register definitions" \ +"See linux mxc_spi driver from Freescale for details." +#endif + +static unsigned long spi_bases[] = { + MXC_SPI_BASE_ADDRESSES +}; + +#define OUT MXC_GPIO_DIRECTION_OUT + +#define reg_read readl +#define reg_write(a, v) writel(v, a) + +struct mxc_spi_slave { + struct spi_slave slave; + unsigned long base; + u32 ctrl_reg; +#if defined(MXC_ECSPI) + u32 cfg_reg; +#endif + int gpio; + int ss_pol; +}; + +static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave) +{ + return container_of(slave, struct mxc_spi_slave, slave); +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); + if (mxcs->gpio > 0) + gpio_set_value(mxcs->gpio, mxcs->ss_pol); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); + if (mxcs->gpio > 0) + gpio_set_value(mxcs->gpio, + !(mxcs->ss_pol)); +} + +u32 get_cspi_div(u32 div) +{ + int i; + + for (i = 0; i < 8; i++) { + if (div <= (4 << i)) + return i; + } + return i; +} + +#ifdef MXC_CSPI +static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + unsigned int ctrl_reg; + u32 clk_src; + u32 div; + + clk_src = mxc_get_clock(MXC_CSPI_CLK); + + div = DIV_ROUND_UP(clk_src, max_hz); + div = get_cspi_div(div); + + debug("clk %d Hz, div %d, real clk %d Hz\n", + max_hz, div, clk_src / (4 << div)); + + ctrl_reg = MXC_CSPICTRL_CHIPSELECT(cs) | + MXC_CSPICTRL_BITCOUNT(MXC_CSPICTRL_MAXBITS) | + MXC_CSPICTRL_DATARATE(div) | + MXC_CSPICTRL_EN | +#ifdef CONFIG_MX35 + MXC_CSPICTRL_SSCTL | +#endif + MXC_CSPICTRL_MODE; + + if (mode & SPI_CPHA) + ctrl_reg |= MXC_CSPICTRL_PHA; + if (mode & SPI_CPOL) + ctrl_reg |= MXC_CSPICTRL_POL; + if (mode & SPI_CS_HIGH) + ctrl_reg |= MXC_CSPICTRL_SSPOL; + mxcs->ctrl_reg = ctrl_reg; + + return 0; +} +#endif + +#ifdef MXC_ECSPI +static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + u32 clk_src = mxc_get_clock(MXC_CSPI_CLK); + s32 reg_ctrl, reg_config; + u32 ss_pol = 0, sclkpol = 0, sclkpha = 0, sclkctl = 0; + u32 pre_div = 0, post_div = 0; + struct cspi_regs *regs = (struct cspi_regs *)mxcs->base; + + if (max_hz == 0) { + printf("Error: desired clock is 0\n"); + return -1; + } + + /* + * Reset SPI and set all CSs to master mode, if toggling + * between slave and master mode we might see a glitch + * on the clock line + */ + reg_ctrl = MXC_CSPICTRL_MODE_MASK; + reg_write(®s->ctrl, reg_ctrl); + reg_ctrl |= MXC_CSPICTRL_EN; + reg_write(®s->ctrl, reg_ctrl); + + if (clk_src > max_hz) { + pre_div = (clk_src - 1) / max_hz; + /* fls(1) = 1, fls(0x80000000) = 32, fls(16) = 5 */ + post_div = fls(pre_div); + if (post_div > 4) { + post_div -= 4; + if (post_div >= 16) { + printf("Error: no divider for the freq: %d\n", + max_hz); + return -1; + } + pre_div >>= post_div; + } else { + post_div = 0; + } + } + + debug("pre_div = %d, post_div=%d\n", pre_div, post_div); + reg_ctrl = (reg_ctrl & ~MXC_CSPICTRL_SELCHAN(3)) | + MXC_CSPICTRL_SELCHAN(cs); + reg_ctrl = (reg_ctrl & ~MXC_CSPICTRL_PREDIV(0x0F)) | + MXC_CSPICTRL_PREDIV(pre_div); + reg_ctrl = (reg_ctrl & ~MXC_CSPICTRL_POSTDIV(0x0F)) | + MXC_CSPICTRL_POSTDIV(post_div); + + /* We need to disable SPI before changing registers */ + reg_ctrl &= ~MXC_CSPICTRL_EN; + + if (mode & SPI_CS_HIGH) + ss_pol = 1; + + if (mode & SPI_CPOL) { + sclkpol = 1; + sclkctl = 1; + } + + if (mode & SPI_CPHA) + sclkpha = 1; + + reg_config = reg_read(®s->cfg); + + /* + * Configuration register setup + * The MX51 supports different setup for each SS + */ + reg_config = (reg_config & ~(1 << (cs + MXC_CSPICON_SSPOL))) | + (ss_pol << (cs + MXC_CSPICON_SSPOL)); + reg_config = (reg_config & ~(1 << (cs + MXC_CSPICON_POL))) | + (sclkpol << (cs + MXC_CSPICON_POL)); + reg_config = (reg_config & ~(1 << (cs + MXC_CSPICON_CTL))) | + (sclkctl << (cs + MXC_CSPICON_CTL)); + reg_config = (reg_config & ~(1 << (cs + MXC_CSPICON_PHA))) | + (sclkpha << (cs + MXC_CSPICON_PHA)); + + debug("reg_ctrl = 0x%x\n", reg_ctrl); + reg_write(®s->ctrl, reg_ctrl); + debug("reg_config = 0x%x\n", reg_config); + reg_write(®s->cfg, reg_config); + + /* save config register and control register */ + mxcs->ctrl_reg = reg_ctrl; + mxcs->cfg_reg = reg_config; + + /* clear interrupt reg */ + reg_write(®s->intr, 0); + reg_write(®s->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF); + + return 0; +} +#endif + +int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen, + const u8 *dout, u8 *din, unsigned long flags) +{ + struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); + int nbytes = DIV_ROUND_UP(bitlen, 8); + u32 data, cnt, i; + struct cspi_regs *regs = (struct cspi_regs *)mxcs->base; + + debug("%s: bitlen %d dout 0x%x din 0x%x\n", + __func__, bitlen, (u32)dout, (u32)din); + + mxcs->ctrl_reg = (mxcs->ctrl_reg & + ~MXC_CSPICTRL_BITCOUNT(MXC_CSPICTRL_MAXBITS)) | + MXC_CSPICTRL_BITCOUNT(bitlen - 1); + + reg_write(®s->ctrl, mxcs->ctrl_reg | MXC_CSPICTRL_EN); +#ifdef MXC_ECSPI + reg_write(®s->cfg, mxcs->cfg_reg); +#endif + + /* Clear interrupt register */ + reg_write(®s->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF); + + /* + * The SPI controller works only with words, + * check if less than a word is sent. + * Access to the FIFO is only 32 bit + */ + if (bitlen % 32) { + data = 0; + cnt = (bitlen % 32) / 8; + if (dout) { + for (i = 0; i < cnt; i++) { + data = (data << 8) | (*dout++ & 0xFF); + } + } + debug("Sending SPI 0x%x\n", data); + + reg_write(®s->txdata, data); + nbytes -= cnt; + } + + data = 0; + + while (nbytes > 0) { + data = 0; + if (dout) { + /* Buffer is not 32-bit aligned */ + if ((unsigned long)dout & 0x03) { + data = 0; + for (i = 0; i < 4; i++) + data = (data << 8) | (*dout++ & 0xFF); + } else { + data = *(u32 *)dout; + data = cpu_to_be32(data); + dout += 4; + } + } + debug("Sending SPI 0x%x\n", data); + reg_write(®s->txdata, data); + nbytes -= 4; + } + + /* FIFO is written, now starts the transfer setting the XCH bit */ + reg_write(®s->ctrl, mxcs->ctrl_reg | + MXC_CSPICTRL_EN | MXC_CSPICTRL_XCH); + + /* Wait until the TC (Transfer completed) bit is set */ + while ((reg_read(®s->stat) & MXC_CSPICTRL_TC) == 0) + ; + + /* Transfer completed, clear any pending request */ + reg_write(®s->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF); + + nbytes = DIV_ROUND_UP(bitlen, 8); + + cnt = nbytes % 32; + + if (bitlen % 32) { + data = reg_read(®s->rxdata); + cnt = (bitlen % 32) / 8; + data = cpu_to_be32(data) >> ((sizeof(data) - cnt) * 8); + debug("SPI Rx unaligned: 0x%x\n", data); + if (din) { + memcpy(din, &data, cnt); + din += cnt; + } + nbytes -= cnt; + } + + while (nbytes > 0) { + u32 tmp; + tmp = reg_read(®s->rxdata); + data = cpu_to_be32(tmp); + debug("SPI Rx: 0x%x 0x%x\n", tmp, data); + cnt = min(nbytes, sizeof(data)); + if (din) { + memcpy(din, &data, cnt); + din += cnt; + } + nbytes -= cnt; + } + + return 0; + +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + int n_bytes = DIV_ROUND_UP(bitlen, 8); + int n_bits; + int ret; + u32 blk_size; + u8 *p_outbuf = (u8 *)dout; + u8 *p_inbuf = (u8 *)din; + + if (!slave) + return -1; + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + while (n_bytes > 0) { + if (n_bytes < MAX_SPI_BYTES) + blk_size = n_bytes; + else + blk_size = MAX_SPI_BYTES; + + n_bits = blk_size * 8; + + ret = spi_xchg_single(slave, n_bits, p_outbuf, p_inbuf, 0); + + if (ret) + return ret; + if (dout) + p_outbuf += blk_size; + if (din) + p_inbuf += blk_size; + n_bytes -= blk_size; + } + + if (flags & SPI_XFER_END) { + spi_cs_deactivate(slave); + } + + return 0; +} + +void spi_init(void) +{ +} + +static int decode_cs(struct mxc_spi_slave *mxcs, unsigned int cs) +{ + int ret; + + /* + * Some SPI devices require active chip-select over multiple + * transactions, we achieve this using a GPIO. Still, the SPI + * controller has to be configured to use one of its own chipselects. + * To use this feature you have to call spi_setup_slave() with + * cs = internal_cs | (gpio << 8), and you have to use some unused + * on this SPI controller cs between 0 and 3. + */ + if (cs > 3) { + mxcs->gpio = cs >> 8; + cs &= 3; + ret = gpio_direction_output(mxcs->gpio, !(mxcs->ss_pol)); + if (ret) { + printf("mxc_spi: cannot setup gpio %d\n", mxcs->gpio); + return -EINVAL; + } + } else { + mxcs->gpio = -1; + } + + return cs; +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct mxc_spi_slave *mxcs; + int ret; + + if (bus >= ARRAY_SIZE(spi_bases)) + return NULL; + + mxcs = spi_alloc_slave(struct mxc_spi_slave, bus, cs); + if (!mxcs) { + puts("mxc_spi: SPI Slave not allocated !\n"); + return NULL; + } + + mxcs->ss_pol = (mode & SPI_CS_HIGH) ? 1 : 0; + + ret = decode_cs(mxcs, cs); + if (ret < 0) { + free(mxcs); + return NULL; + } + + cs = ret; + + mxcs->base = spi_bases[bus]; + + ret = spi_cfg_mxc(mxcs, cs, max_hz, mode); + if (ret) { + printf("mxc_spi: cannot setup SPI controller\n"); + free(mxcs); + return NULL; + } + return &mxcs->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); + + free(mxcs); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); + struct cspi_regs *regs = (struct cspi_regs *)mxcs->base; + + reg_write(®s->rxdata, 1); + udelay(1); + reg_write(®s->ctrl, mxcs->ctrl_reg); + reg_write(®s->period, MXC_CSPIPERIOD_32KHZ); + reg_write(®s->intr, 0); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + /* TODO: Shut the controller down */ +} diff --git a/qemu/roms/u-boot/drivers/spi/mxs_spi.c b/qemu/roms/u-boot/drivers/spi/mxs_spi.c new file mode 100644 index 000000000..2b9f395a9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/mxs_spi.c @@ -0,0 +1,362 @@ +/* + * Freescale i.MX28 SPI driver + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + * + * NOTE: This driver only supports the SPI-controller chipselects, + * GPIO driven chipselects are not supported. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MXS_SPI_MAX_TIMEOUT 1000000 +#define MXS_SPI_PORT_OFFSET 0x2000 +#define MXS_SSP_CHIPSELECT_MASK 0x00300000 +#define MXS_SSP_CHIPSELECT_SHIFT 20 + +#define MXSSSP_SMALL_TRANSFER 512 + +struct mxs_spi_slave { + struct spi_slave slave; + uint32_t max_khz; + uint32_t mode; + struct mxs_ssp_regs *regs; +}; + +static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave) +{ + return container_of(slave, struct mxs_spi_slave, slave); +} + +void spi_init(void) +{ +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + /* MXS SPI: 4 ports and 3 chip selects maximum */ + if (!mxs_ssp_bus_id_valid(bus) || cs > 2) + return 0; + else + return 1; +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct mxs_spi_slave *mxs_slave; + + if (!spi_cs_is_valid(bus, cs)) { + printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs); + return NULL; + } + + mxs_slave = spi_alloc_slave(struct mxs_spi_slave, bus, cs); + if (!mxs_slave) + return NULL; + + if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus)) + goto err_init; + + mxs_slave->max_khz = max_hz / 1000; + mxs_slave->mode = mode; + mxs_slave->regs = mxs_ssp_regs_by_bus(bus); + + return &mxs_slave->slave; + +err_init: + free(mxs_slave); + return NULL; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); + free(mxs_slave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); + struct mxs_ssp_regs *ssp_regs = mxs_slave->regs; + uint32_t reg = 0; + + mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); + + writel((slave->cs << MXS_SSP_CHIPSELECT_SHIFT) | + SSP_CTRL0_BUS_WIDTH_ONE_BIT, + &ssp_regs->hw_ssp_ctrl0); + + reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS; + reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0; + reg |= (mxs_slave->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0; + writel(reg, &ssp_regs->hw_ssp_ctrl1); + + writel(0, &ssp_regs->hw_ssp_cmd0); + + mxs_set_ssp_busclock(slave->bus, mxs_slave->max_khz); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +} + +static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs) +{ + writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set); + writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr); +} + +static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs) +{ + writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr); + writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set); +} + +static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave, + char *data, int length, int write, unsigned long flags) +{ + struct mxs_ssp_regs *ssp_regs = slave->regs; + + if (flags & SPI_XFER_BEGIN) + mxs_spi_start_xfer(ssp_regs); + + while (length--) { + /* We transfer 1 byte */ +#if defined(CONFIG_MX23) + writel(SSP_CTRL0_XFER_COUNT_MASK, &ssp_regs->hw_ssp_ctrl0_clr); + writel(1, &ssp_regs->hw_ssp_ctrl0_set); +#elif defined(CONFIG_MX28) + writel(1, &ssp_regs->hw_ssp_xfer_size); +#endif + + if ((flags & SPI_XFER_END) && !length) + mxs_spi_end_xfer(ssp_regs); + + if (write) + writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr); + else + writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set); + + writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set); + + if (mxs_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg, + SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) { + printf("MXS SPI: Timeout waiting for start\n"); + return -ETIMEDOUT; + } + + if (write) + writel(*data++, &ssp_regs->hw_ssp_data); + + writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set); + + if (!write) { + if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_status_reg, + SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) { + printf("MXS SPI: Timeout waiting for data\n"); + return -ETIMEDOUT; + } + + *data = readl(&ssp_regs->hw_ssp_data); + data++; + } + + if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg, + SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) { + printf("MXS SPI: Timeout waiting for finish\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave, + char *data, int length, int write, unsigned long flags) +{ + const int xfer_max_sz = 0xff00; + const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1; + struct mxs_ssp_regs *ssp_regs = slave->regs; + struct mxs_dma_desc *dp; + uint32_t ctrl0; + uint32_t cache_data_count; + const uint32_t dstart = (uint32_t)data; + int dmach; + int tl; + int ret = 0; + +#if defined(CONFIG_MX23) + const int mxs_spi_pio_words = 1; +#elif defined(CONFIG_MX28) + const int mxs_spi_pio_words = 4; +#endif + + ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count); + + memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count); + + ctrl0 = readl(&ssp_regs->hw_ssp_ctrl0); + ctrl0 |= SSP_CTRL0_DATA_XFER; + + if (flags & SPI_XFER_BEGIN) + ctrl0 |= SSP_CTRL0_LOCK_CS; + if (!write) + ctrl0 |= SSP_CTRL0_READ; + + if (length % ARCH_DMA_MINALIGN) + cache_data_count = roundup(length, ARCH_DMA_MINALIGN); + else + cache_data_count = length; + + /* Flush data to DRAM so DMA can pick them up */ + if (write) + flush_dcache_range(dstart, dstart + cache_data_count); + + /* Invalidate the area, so no writeback into the RAM races with DMA */ + invalidate_dcache_range(dstart, dstart + cache_data_count); + + dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus; + + dp = desc; + while (length) { + dp->address = (dma_addr_t)dp; + dp->cmd.address = (dma_addr_t)data; + + /* + * This is correct, even though it does indeed look insane. + * I hereby have to, wholeheartedly, thank Freescale Inc., + * for always inventing insane hardware and keeping me busy + * and employed ;-) + */ + if (write) + dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; + else + dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; + + /* + * The DMA controller can transfer large chunks (64kB) at + * time by setting the transfer length to 0. Setting tl to + * 0x10000 will overflow below and make .data contain 0. + * Otherwise, 0xff00 is the transfer maximum. + */ + if (length >= 0x10000) + tl = 0x10000; + else + tl = min(length, xfer_max_sz); + + dp->cmd.data |= + ((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) | + (mxs_spi_pio_words << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + MXS_DMA_DESC_HALT_ON_TERMINATE | + MXS_DMA_DESC_TERMINATE_FLUSH; + + data += tl; + length -= tl; + + if (!length) { + dp->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; + + if (flags & SPI_XFER_END) { + ctrl0 &= ~SSP_CTRL0_LOCK_CS; + ctrl0 |= SSP_CTRL0_IGNORE_CRC; + } + } + + /* + * Write CTRL0, CMD0, CMD1 and XFER_SIZE registers in + * case of MX28, write only CTRL0 in case of MX23 due + * to the difference in register layout. It is utterly + * essential that the XFER_SIZE register is written on + * a per-descriptor basis with the same size as is the + * descriptor! + */ + dp->cmd.pio_words[0] = ctrl0; +#ifdef CONFIG_MX28 + dp->cmd.pio_words[1] = 0; + dp->cmd.pio_words[2] = 0; + dp->cmd.pio_words[3] = tl; +#endif + + mxs_dma_desc_append(dmach, dp); + + dp++; + } + + if (mxs_dma_go(dmach)) + ret = -EINVAL; + + /* The data arrived into DRAM, invalidate cache over them */ + if (!write) + invalidate_dcache_range(dstart, dstart + cache_data_count); + + return ret; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); + struct mxs_ssp_regs *ssp_regs = mxs_slave->regs; + int len = bitlen / 8; + char dummy; + int write = 0; + char *data = NULL; + int dma = 1; + + if (bitlen == 0) { + if (flags & SPI_XFER_END) { + din = (void *)&dummy; + len = 1; + } else + return 0; + } + + /* Half-duplex only */ + if (din && dout) + return -EINVAL; + /* No data */ + if (!din && !dout) + return 0; + + if (dout) { + data = (char *)dout; + write = 1; + } else if (din) { + data = (char *)din; + write = 0; + } + + /* + * Check for alignment, if the buffer is aligned, do DMA transfer, + * PIO otherwise. This is a temporary workaround until proper bounce + * buffer is in place. + */ + if (dma) { + if (((uint32_t)data) & (ARCH_DMA_MINALIGN - 1)) + dma = 0; + if (((uint32_t)len) & (ARCH_DMA_MINALIGN - 1)) + dma = 0; + } + + if (!dma || (len < MXSSSP_SMALL_TRANSFER)) { + writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr); + return mxs_spi_xfer_pio(mxs_slave, data, len, write, flags); + } else { + writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set); + return mxs_spi_xfer_dma(mxs_slave, data, len, write, flags); + } +} diff --git a/qemu/roms/u-boot/drivers/spi/oc_tiny_spi.c b/qemu/roms/u-boot/drivers/spi/oc_tiny_spi.c new file mode 100644 index 000000000..4de5d008a --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/oc_tiny_spi.c @@ -0,0 +1,245 @@ +/* + * Opencore tiny_spi driver + * + * http://opencores.org/project,tiny_spi + * + * based on bfin_spi.c + * Copyright (c) 2005-2008 Analog Devices Inc. + * Copyright (C) 2010 Thomas Chou + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define TINY_SPI_STATUS_TXE 0x1 +#define TINY_SPI_STATUS_TXR 0x2 + +struct tiny_spi_regs { + unsigned rxdata; /* Rx data reg */ + unsigned txdata; /* Tx data reg */ + unsigned status; /* Status reg */ + unsigned control; /* Control reg */ + unsigned baud; /* Baud reg */ +}; + +struct tiny_spi_host { + uint base; + uint freq; + uint baudwidth; +}; +static const struct tiny_spi_host tiny_spi_host_list[] = + CONFIG_SYS_TINY_SPI_LIST; + +struct tiny_spi_slave { + struct spi_slave slave; + const struct tiny_spi_host *host; + uint mode; + uint baud; + uint flg; +}; +#define to_tiny_spi_slave(s) container_of(s, struct tiny_spi_slave, slave) + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus < ARRAY_SIZE(tiny_spi_host_list) && gpio_is_valid(cs); +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + unsigned int cs = slave->cs; + + gpio_set_value(cs, tiny_spi->flg); + debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + unsigned int cs = slave->cs; + + gpio_set_value(cs, !tiny_spi->flg); + debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); +} + +void spi_set_speed(struct spi_slave *slave, uint hz) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + const struct tiny_spi_host *host = tiny_spi->host; + + tiny_spi->baud = min(DIV_ROUND_UP(host->freq, hz * 2), + (1 << host->baudwidth)) - 1; + debug("%s: speed %u actual %u\n", __func__, hz, + host->freq / ((tiny_spi->baud + 1) * 2)); +} + +void spi_init(void) +{ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int hz, unsigned int mode) +{ + struct tiny_spi_slave *tiny_spi; + + if (!spi_cs_is_valid(bus, cs) || gpio_request(cs, "tiny_spi")) + return NULL; + + tiny_spi = spi_alloc_slave(struct tiny_spi_slave, bus, cs); + if (!tiny_spi) + return NULL; + + tiny_spi->host = &tiny_spi_host_list[bus]; + tiny_spi->mode = mode & (SPI_CPOL | SPI_CPHA); + tiny_spi->flg = mode & SPI_CS_HIGH ? 1 : 0; + spi_set_speed(&tiny_spi->slave, hz); + + debug("%s: bus:%i cs:%i base:%lx\n", __func__, + bus, cs, tiny_spi->host->base); + return &tiny_spi->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + + gpio_free(slave->cs); + free(tiny_spi); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + struct tiny_spi_regs *regs = (void *)tiny_spi->host->base; + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + gpio_direction_output(slave->cs, !tiny_spi->flg); + writel(tiny_spi->mode, ®s->control); + writel(tiny_spi->baud, ®s->baud); + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); +} + +#ifndef CONFIG_TINY_SPI_IDLE_VAL +# define CONFIG_TINY_SPI_IDLE_VAL 0xff +#endif + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + struct tiny_spi_regs *regs = (void *)tiny_spi->host->base; + const u8 *txp = dout; + u8 *rxp = din; + uint bytes = bitlen / 8; + uint i; + + debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, + slave->bus, slave->cs, bitlen, bytes, flags); + if (bitlen == 0) + goto done; + + /* assume to do 8 bits transfers */ + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto done; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* we need to tighten the transfer loop */ + if (txp && rxp) { + writeb(*txp++, ®s->txdata); + if (bytes > 1) { + writeb(*txp++, ®s->txdata); + for (i = 2; i < bytes; i++) { + u8 rx, tx = *txp++; + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + rx = readb(®s->txdata); + writeb(tx, ®s->txdata); + *rxp++ = rx; + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + *rxp++ = readb(®s->txdata); + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXE)) + ; + *rxp++ = readb(®s->rxdata); + } else if (rxp) { + writeb(CONFIG_TINY_SPI_IDLE_VAL, ®s->txdata); + if (bytes > 1) { + writeb(CONFIG_TINY_SPI_IDLE_VAL, + ®s->txdata); + for (i = 2; i < bytes; i++) { + u8 rx; + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + rx = readb(®s->txdata); + writeb(CONFIG_TINY_SPI_IDLE_VAL, + ®s->txdata); + *rxp++ = rx; + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + *rxp++ = readb(®s->txdata); + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXE)) + ; + *rxp++ = readb(®s->rxdata); + } else if (txp) { + writeb(*txp++, ®s->txdata); + if (bytes > 1) { + writeb(*txp++, ®s->txdata); + for (i = 2; i < bytes; i++) { + u8 tx = *txp++; + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + writeb(tx, ®s->txdata); + } + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXE)) + ; + } else { + writeb(CONFIG_TINY_SPI_IDLE_VAL, ®s->txdata); + if (bytes > 1) { + writeb(CONFIG_TINY_SPI_IDLE_VAL, + ®s->txdata); + for (i = 2; i < bytes; i++) { + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + writeb(CONFIG_TINY_SPI_IDLE_VAL, + ®s->txdata); + } + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXE)) + ; + } + + done: + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/omap3_spi.c b/qemu/roms/u-boot/drivers/spi/omap3_spi.c new file mode 100644 index 000000000..651e46e4b --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/omap3_spi.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2010 Dirk Behme + * + * Driver for McSPI controller on OMAP3. Based on davinci_spi.c + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * Copyright (C) 2007 Atmel Corporation + * + * Parts taken from linux/drivers/spi/omap2_mcspi.c + * Copyright (C) 2005, 2006 Nokia Corporation + * + * Modified by Ruslan Araslanov + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "omap3_spi.h" + +#define SPI_WAIT_TIMEOUT 3000000 + +static void spi_reset(struct omap3_spi_slave *ds) +{ + unsigned int tmp; + + writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &ds->regs->sysconfig); + do { + tmp = readl(&ds->regs->sysstatus); + } while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE)); + + writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE | + OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP | + OMAP3_MCSPI_SYSCONFIG_SMARTIDLE, + &ds->regs->sysconfig); + + writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable); +} + +static void omap3_spi_write_chconf(struct omap3_spi_slave *ds, int val) +{ + writel(val, &ds->regs->channel[ds->slave.cs].chconf); + /* Flash post writes to make immediate effect */ + readl(&ds->regs->channel[ds->slave.cs].chconf); +} + +static void omap3_spi_set_enable(struct omap3_spi_slave *ds, int enable) +{ + writel(enable, &ds->regs->channel[ds->slave.cs].chctrl); + /* Flash post writes to make immediate effect */ + readl(&ds->regs->channel[ds->slave.cs].chctrl); +} + +void spi_init() +{ + /* do nothing */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct omap3_spi_slave *ds; + struct mcspi *regs; + + /* + * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules) + * with different number of chip selects (CS, channels): + * McSPI1 has 4 CS (bus 0, cs 0 - 3) + * McSPI2 has 2 CS (bus 1, cs 0 - 1) + * McSPI3 has 2 CS (bus 2, cs 0 - 1) + * McSPI4 has 1 CS (bus 3, cs 0) + */ + + switch (bus) { + case 0: + regs = (struct mcspi *)OMAP3_MCSPI1_BASE; + break; +#ifdef OMAP3_MCSPI2_BASE + case 1: + regs = (struct mcspi *)OMAP3_MCSPI2_BASE; + break; +#endif +#ifdef OMAP3_MCSPI3_BASE + case 2: + regs = (struct mcspi *)OMAP3_MCSPI3_BASE; + break; +#endif +#ifdef OMAP3_MCSPI4_BASE + case 3: + regs = (struct mcspi *)OMAP3_MCSPI4_BASE; + break; +#endif + default: + printf("SPI error: unsupported bus %i. \ + Supported busses 0 - 3\n", bus); + return NULL; + } + + if (((bus == 0) && (cs > 3)) || + ((bus == 1) && (cs > 1)) || + ((bus == 2) && (cs > 1)) || + ((bus == 3) && (cs > 0))) { + printf("SPI error: unsupported chip select %i \ + on bus %i\n", cs, bus); + return NULL; + } + + if (max_hz > OMAP3_MCSPI_MAX_FREQ) { + printf("SPI error: unsupported frequency %i Hz. \ + Max frequency is 48 Mhz\n", max_hz); + return NULL; + } + + if (mode > SPI_MODE_3) { + printf("SPI error: unsupported SPI mode %i\n", mode); + return NULL; + } + + ds = spi_alloc_slave(struct omap3_spi_slave, bus, cs); + if (!ds) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + + ds->regs = regs; + ds->freq = max_hz; + ds->mode = mode; + + return &ds->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + + free(ds); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + unsigned int conf, div = 0; + + /* McSPI global module configuration */ + + /* + * setup when switching from (reset default) slave mode + * to single-channel master mode + */ + spi_reset(ds); + conf = readl(&ds->regs->modulctrl); + conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS); + conf |= OMAP3_MCSPI_MODULCTRL_SINGLE; + writel(conf, &ds->regs->modulctrl); + + /* McSPI individual channel configuration */ + + /* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */ + if (ds->freq) { + while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div)) + > ds->freq) + div++; + } else + div = 0xC; + + conf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS + * REVISIT: this controller could support SPI_3WIRE mode. + */ +#ifdef CONFIG_OMAP3_SPI_D0_D1_SWAPPED + /* + * Some boards have D0 wired as MOSI / D1 as MISO instead of + * The normal D0 as MISO / D1 as MOSI. + */ + conf &= ~OMAP3_MCSPI_CHCONF_DPE0; + conf |= OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1; +#else + conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); + conf |= OMAP3_MCSPI_CHCONF_DPE0; +#endif + + /* wordlength */ + conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK; + conf |= (ds->slave.wordlen - 1) << 7; + + /* set chipselect polarity; manage with FORCE */ + if (!(ds->mode & SPI_CS_HIGH)) + conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */ + else + conf &= ~OMAP3_MCSPI_CHCONF_EPOL; + + /* set clock divisor */ + conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK; + conf |= div << 2; + + /* set SPI mode 0..3 */ + if (ds->mode & SPI_CPOL) + conf |= OMAP3_MCSPI_CHCONF_POL; + else + conf &= ~OMAP3_MCSPI_CHCONF_POL; + if (ds->mode & SPI_CPHA) + conf |= OMAP3_MCSPI_CHCONF_PHA; + else + conf &= ~OMAP3_MCSPI_CHCONF_PHA; + + /* Transmit & receive mode */ + conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; + + omap3_spi_write_chconf(ds,conf); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + + /* Reset the SPI hardware */ + spi_reset(ds); +} + +int omap3_spi_write(struct spi_slave *slave, unsigned int len, const void *txp, + unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + int i; + int timeout = SPI_WAIT_TIMEOUT; + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + /* Enable the channel */ + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); + + chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); + chconf |= (ds->slave.wordlen - 1) << 7; + chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY; + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + omap3_spi_write_chconf(ds,chconf); + + for (i = 0; i < len; i++) { + /* wait till TX register is empty (TXS == 1) */ + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_TXS)) { + if (--timeout <= 0) { + printf("SPI TXS timed out, status=0x%08x\n", + readl(&ds->regs->channel[ds->slave.cs].chstat)); + return -1; + } + } + /* Write the data */ + unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx; + if (ds->slave.wordlen > 16) + writel(((u32 *)txp)[i], tx); + else if (ds->slave.wordlen > 8) + writel(((u16 *)txp)[i], tx); + else + writel(((u8 *)txp)[i], tx); + } + + /* wait to finish of transfer */ + while ((readl(&ds->regs->channel[ds->slave.cs].chstat) & + (OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)) != + (OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)); + + /* Disable the channel otherwise the next immediate RX will get affected */ + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); + + if (flags & SPI_XFER_END) { + + chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; + omap3_spi_write_chconf(ds,chconf); + } + return 0; +} + +int omap3_spi_read(struct spi_slave *slave, unsigned int len, void *rxp, + unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + int i; + int timeout = SPI_WAIT_TIMEOUT; + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + /* Enable the channel */ + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); + + chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); + chconf |= (ds->slave.wordlen - 1) << 7; + chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY; + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + omap3_spi_write_chconf(ds,chconf); + + writel(0, &ds->regs->channel[ds->slave.cs].tx); + + for (i = 0; i < len; i++) { + /* Wait till RX register contains data (RXS == 1) */ + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_RXS)) { + if (--timeout <= 0) { + printf("SPI RXS timed out, status=0x%08x\n", + readl(&ds->regs->channel[ds->slave.cs].chstat)); + return -1; + } + } + + /* Disable the channel to prevent furher receiving */ + if(i == (len - 1)) + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); + + /* Read the data */ + unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx; + if (ds->slave.wordlen > 16) + ((u32 *)rxp)[i] = readl(rx); + else if (ds->slave.wordlen > 8) + ((u16 *)rxp)[i] = (u16)readl(rx); + else + ((u8 *)rxp)[i] = (u8)readl(rx); + } + + if (flags & SPI_XFER_END) { + chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; + omap3_spi_write_chconf(ds,chconf); + } + + return 0; +} + +/*McSPI Transmit Receive Mode*/ +int omap3_spi_txrx(struct spi_slave *slave, unsigned int len, + const void *txp, void *rxp, unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + int timeout = SPI_WAIT_TIMEOUT; + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + int irqstatus = readl(&ds->regs->irqstatus); + int i=0; + + /*Enable SPI channel*/ + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); + + /*set TRANSMIT-RECEIVE Mode*/ + chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); + chconf |= (ds->slave.wordlen - 1) << 7; + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + omap3_spi_write_chconf(ds,chconf); + + /*Shift in and out 1 byte at time*/ + for (i=0; i < len; i++){ + /* Write: wait for TX empty (TXS == 1)*/ + irqstatus |= (1<< (4*(ds->slave.bus))); + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_TXS)) { + if (--timeout <= 0) { + printf("SPI TXS timed out, status=0x%08x\n", + readl(&ds->regs->channel[ds->slave.cs].chstat)); + return -1; + } + } + /* Write the data */ + unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx; + if (ds->slave.wordlen > 16) + writel(((u32 *)txp)[i], tx); + else if (ds->slave.wordlen > 8) + writel(((u16 *)txp)[i], tx); + else + writel(((u8 *)txp)[i], tx); + + /*Read: wait for RX containing data (RXS == 1)*/ + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_RXS)) { + if (--timeout <= 0) { + printf("SPI RXS timed out, status=0x%08x\n", + readl(&ds->regs->channel[ds->slave.cs].chstat)); + return -1; + } + } + /* Read the data */ + unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx; + if (ds->slave.wordlen > 16) + ((u32 *)rxp)[i] = readl(rx); + else if (ds->slave.wordlen > 8) + ((u16 *)rxp)[i] = (u16)readl(rx); + else + ((u8 *)rxp)[i] = (u8)readl(rx); + } + /* Disable the channel */ + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); + + /*if transfer must be terminated disable the channel*/ + if (flags & SPI_XFER_END) { + chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; + omap3_spi_write_chconf(ds,chconf); + } + + return 0; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + unsigned int len; + int ret = -1; + + if (ds->slave.wordlen < 4 || ds->slave.wordlen > 32) { + printf("omap3_spi: invalid wordlen %d\n", ds->slave.wordlen); + return -1; + } + + if (bitlen % ds->slave.wordlen) + return -1; + + len = bitlen / ds->slave.wordlen; + + if (bitlen == 0) { /* only change CS */ + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + if (flags & SPI_XFER_BEGIN) { + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + omap3_spi_write_chconf(ds,chconf); + } + if (flags & SPI_XFER_END) { + chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; + omap3_spi_write_chconf(ds,chconf); + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); + } + ret = 0; + } else { + if (dout != NULL && din != NULL) + ret = omap3_spi_txrx(slave, len, dout, din, flags); + else if (dout != NULL) + ret = omap3_spi_write(slave, len, dout, flags); + else if (din != NULL) + ret = omap3_spi_read(slave, len, din, flags); + } + return ret; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ +} diff --git a/qemu/roms/u-boot/drivers/spi/omap3_spi.h b/qemu/roms/u-boot/drivers/spi/omap3_spi.h new file mode 100644 index 000000000..ab7cd8444 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/omap3_spi.h @@ -0,0 +1,109 @@ +/* + * Register definitions for the OMAP3 McSPI Controller + * + * Copyright (C) 2010 Dirk Behme + * + * Parts taken from linux/drivers/spi/omap2_mcspi.c + * Copyright (C) 2005, 2006 Nokia Corporation + * + * Modified by Ruslan Araslanov + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _OMAP3_SPI_H_ +#define _OMAP3_SPI_H_ + +#ifdef CONFIG_AM33XX +#define OMAP3_MCSPI1_BASE 0x48030100 +#define OMAP3_MCSPI2_BASE 0x481A0100 +#else +#define OMAP3_MCSPI1_BASE 0x48098000 +#define OMAP3_MCSPI2_BASE 0x4809A000 +#define OMAP3_MCSPI3_BASE 0x480B8000 +#define OMAP3_MCSPI4_BASE 0x480BA000 +#endif + +#define OMAP3_MCSPI_MAX_FREQ 48000000 + +/* OMAP3 McSPI registers */ +struct mcspi_channel { + unsigned int chconf; /* 0x2C, 0x40, 0x54, 0x68 */ + unsigned int chstat; /* 0x30, 0x44, 0x58, 0x6C */ + unsigned int chctrl; /* 0x34, 0x48, 0x5C, 0x70 */ + unsigned int tx; /* 0x38, 0x4C, 0x60, 0x74 */ + unsigned int rx; /* 0x3C, 0x50, 0x64, 0x78 */ +}; + +struct mcspi { + unsigned char res1[0x10]; + unsigned int sysconfig; /* 0x10 */ + unsigned int sysstatus; /* 0x14 */ + unsigned int irqstatus; /* 0x18 */ + unsigned int irqenable; /* 0x1C */ + unsigned int wakeupenable; /* 0x20 */ + unsigned int syst; /* 0x24 */ + unsigned int modulctrl; /* 0x28 */ + struct mcspi_channel channel[4]; /* channel0: 0x2C - 0x3C, bus 0 & 1 & 2 & 3 */ + /* channel1: 0x40 - 0x50, bus 0 & 1 */ + /* channel2: 0x54 - 0x64, bus 0 & 1 */ + /* channel3: 0x68 - 0x78, bus 0 */ +}; + +/* per-register bitmasks */ +#define OMAP3_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3) +#define OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP3_MCSPI_SYSCONFIG_AUTOIDLE (1 << 0) +#define OMAP3_MCSPI_SYSCONFIG_SOFTRESET (1 << 1) + +#define OMAP3_MCSPI_SYSSTATUS_RESETDONE (1 << 0) + +#define OMAP3_MCSPI_MODULCTRL_SINGLE (1 << 0) +#define OMAP3_MCSPI_MODULCTRL_MS (1 << 2) +#define OMAP3_MCSPI_MODULCTRL_STEST (1 << 3) + +#define OMAP3_MCSPI_CHCONF_PHA (1 << 0) +#define OMAP3_MCSPI_CHCONF_POL (1 << 1) +#define OMAP3_MCSPI_CHCONF_CLKD_MASK (0x0f << 2) +#define OMAP3_MCSPI_CHCONF_EPOL (1 << 6) +#define OMAP3_MCSPI_CHCONF_WL_MASK (0x1f << 7) +#define OMAP3_MCSPI_CHCONF_TRM_RX_ONLY (0x01 << 12) +#define OMAP3_MCSPI_CHCONF_TRM_TX_ONLY (0x02 << 12) +#define OMAP3_MCSPI_CHCONF_TRM_MASK (0x03 << 12) +#define OMAP3_MCSPI_CHCONF_DMAW (1 << 14) +#define OMAP3_MCSPI_CHCONF_DMAR (1 << 15) +#define OMAP3_MCSPI_CHCONF_DPE0 (1 << 16) +#define OMAP3_MCSPI_CHCONF_DPE1 (1 << 17) +#define OMAP3_MCSPI_CHCONF_IS (1 << 18) +#define OMAP3_MCSPI_CHCONF_TURBO (1 << 19) +#define OMAP3_MCSPI_CHCONF_FORCE (1 << 20) + +#define OMAP3_MCSPI_CHSTAT_RXS (1 << 0) +#define OMAP3_MCSPI_CHSTAT_TXS (1 << 1) +#define OMAP3_MCSPI_CHSTAT_EOT (1 << 2) + +#define OMAP3_MCSPI_CHCTRL_EN (1 << 0) +#define OMAP3_MCSPI_CHCTRL_DIS (0 << 0) + +#define OMAP3_MCSPI_WAKEUPENABLE_WKEN (1 << 0) + +struct omap3_spi_slave { + struct spi_slave slave; + struct mcspi *regs; + unsigned int freq; + unsigned int mode; +}; + +static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave) +{ + return container_of(slave, struct omap3_spi_slave, slave); +} + +int omap3_spi_txrx(struct spi_slave *slave, unsigned int len, const void *txp, + void *rxp, unsigned long flags); +int omap3_spi_write(struct spi_slave *slave, unsigned int len, const void *txp, + unsigned long flags); +int omap3_spi_read(struct spi_slave *slave, unsigned int len, void *rxp, + unsigned long flags); + +#endif /* _OMAP3_SPI_H_ */ diff --git a/qemu/roms/u-boot/drivers/spi/sandbox_spi.c b/qemu/roms/u-boot/drivers/spi/sandbox_spi.c new file mode 100644 index 000000000..12e9bdad3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/sandbox_spi.c @@ -0,0 +1,217 @@ +/* + * Simulate a SPI port + * + * Copyright (c) 2011-2013 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#ifndef CONFIG_SPI_IDLE_VAL +# define CONFIG_SPI_IDLE_VAL 0xFF +#endif + +struct sandbox_spi_slave { + struct spi_slave slave; + const struct sandbox_spi_emu_ops *ops; + void *priv; +}; + +#define to_sandbox_spi_slave(s) container_of(s, struct sandbox_spi_slave, slave) + +const char *sandbox_spi_parse_spec(const char *arg, unsigned long *bus, + unsigned long *cs) +{ + char *endp; + + *bus = simple_strtoul(arg, &endp, 0); + if (*endp != ':' || *bus >= CONFIG_SANDBOX_SPI_MAX_BUS) + return NULL; + + *cs = simple_strtoul(endp + 1, &endp, 0); + if (*endp != ':' || *cs >= CONFIG_SANDBOX_SPI_MAX_CS) + return NULL; + + return endp + 1; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus < CONFIG_SANDBOX_SPI_MAX_BUS && + cs < CONFIG_SANDBOX_SPI_MAX_CS; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave); + + debug("sandbox_spi: activating CS\n"); + if (sss->ops->cs_activate) + sss->ops->cs_activate(sss->priv); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave); + + debug("sandbox_spi: deactivating CS\n"); + if (sss->ops->cs_deactivate) + sss->ops->cs_deactivate(sss->priv); +} + +void spi_init(void) +{ +} + +void spi_set_speed(struct spi_slave *slave, uint hz) +{ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct sandbox_spi_slave *sss; + struct sandbox_state *state = state_get_current(); + const char *spec; + + if (!spi_cs_is_valid(bus, cs)) { + debug("sandbox_spi: Invalid SPI bus/cs\n"); + return NULL; + } + + sss = spi_alloc_slave(struct sandbox_spi_slave, bus, cs); + if (!sss) { + debug("sandbox_spi: Out of memory\n"); + return NULL; + } + + spec = state->spi[bus][cs].spec; + sss->ops = state->spi[bus][cs].ops; + if (!spec || !sss->ops || sss->ops->setup(&sss->priv, spec)) { + free(sss); + printf("sandbox_spi: unable to locate a slave client\n"); + return NULL; + } + + return &sss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave); + + debug("sandbox_spi: releasing slave\n"); + + if (sss->ops->free) + sss->ops->free(sss->priv); + + free(sss); +} + +static int spi_bus_claim_cnt[CONFIG_SANDBOX_SPI_MAX_BUS]; + +int spi_claim_bus(struct spi_slave *slave) +{ + if (spi_bus_claim_cnt[slave->bus]++) { + printf("sandbox_spi: error: bus already claimed: %d!\n", + spi_bus_claim_cnt[slave->bus]); + } + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + if (--spi_bus_claim_cnt[slave->bus]) { + printf("sandbox_spi: error: bus freed too often: %d!\n", + spi_bus_claim_cnt[slave->bus]); + } +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave); + uint bytes = bitlen / 8, i; + int ret = 0; + u8 *tx = (void *)dout, *rx = din; + + if (bitlen == 0) + goto done; + + /* we can only do 8 bit transfers */ + if (bitlen % 8) { + printf("sandbox_spi: xfer: invalid bitlen size %u; needs to be 8bit\n", + bitlen); + flags |= SPI_XFER_END; + goto done; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* make sure rx/tx buffers are full so clients can assume */ + if (!tx) { + debug("sandbox_spi: xfer: auto-allocating tx scratch buffer\n"); + tx = malloc(bytes); + if (!tx) { + debug("sandbox_spi: Out of memory\n"); + return -ENOMEM; + } + } + if (!rx) { + debug("sandbox_spi: xfer: auto-allocating rx scratch buffer\n"); + rx = malloc(bytes); + if (!rx) { + debug("sandbox_spi: Out of memory\n"); + return -ENOMEM; + } + } + + debug("sandbox_spi: xfer: bytes = %u\n tx:", bytes); + for (i = 0; i < bytes; ++i) + debug(" %u:%02x", i, tx[i]); + debug("\n"); + + ret = sss->ops->xfer(sss->priv, tx, rx, bytes); + + debug("sandbox_spi: xfer: got back %i (that's %s)\n rx:", + ret, ret ? "bad" : "good"); + for (i = 0; i < bytes; ++i) + debug(" %u:%02x", i, rx[i]); + debug("\n"); + + if (tx != dout) + free(tx); + if (rx != din) + free(rx); + + done: + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return ret; +} + +/** + * Set up a new SPI slave for an fdt node + * + * @param blob Device tree blob + * @param node SPI peripheral node to use + * @return 0 if ok, -1 on error + */ +struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, + int spi_node) +{ + return NULL; +} diff --git a/qemu/roms/u-boot/drivers/spi/sh_qspi.c b/qemu/roms/u-boot/drivers/spi/sh_qspi.c new file mode 100644 index 000000000..77ede6bba --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/sh_qspi.c @@ -0,0 +1,278 @@ +/* + * SH QSPI (Quad SPI) driver + * + * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2013 Nobuhiro Iwamatsu + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include + +/* SH QSPI register bit masks _ */ +#define SPCR_MSTR 0x08 +#define SPCR_SPE 0x40 +#define SPSR_SPRFF 0x80 +#define SPSR_SPTEF 0x20 +#define SPPCR_IO3FV 0x04 +#define SPPCR_IO2FV 0x02 +#define SPPCR_IO1FV 0x01 +#define SPBDCR_RXBC0 (1 << 0) +#define SPCMD_SCKDEN (1 << 15) +#define SPCMD_SLNDEN (1 << 14) +#define SPCMD_SPNDEN (1 << 13) +#define SPCMD_SSLKP (1 << 7) +#define SPCMD_BRDV0 (1 << 2) +#define SPCMD_INIT1 SPCMD_SCKDEN | SPCMD_SLNDEN | \ + SPCMD_SPNDEN | SPCMD_SSLKP | \ + SPCMD_BRDV0 +#define SPCMD_INIT2 SPCMD_SPNDEN | SPCMD_SSLKP | \ + SPCMD_BRDV0 +#define SPBFCR_TXRST (1 << 7) +#define SPBFCR_RXRST (1 << 6) + +/* SH QSPI register set */ +struct sh_qspi_regs { + unsigned char spcr; + unsigned char sslp; + unsigned char sppcr; + unsigned char spsr; + unsigned long spdr; + unsigned char spscr; + unsigned char spssr; + unsigned char spbr; + unsigned char spdcr; + unsigned char spckd; + unsigned char sslnd; + unsigned char spnd; + unsigned char dummy0; + unsigned short spcmd0; + unsigned short spcmd1; + unsigned short spcmd2; + unsigned short spcmd3; + unsigned char spbfcr; + unsigned char dummy1; + unsigned short spbdcr; + unsigned long spbmul0; + unsigned long spbmul1; + unsigned long spbmul2; + unsigned long spbmul3; +}; + +struct sh_qspi_slave { + struct spi_slave slave; + struct sh_qspi_regs *regs; +}; + +static inline struct sh_qspi_slave *to_sh_qspi(struct spi_slave *slave) +{ + return container_of(slave, struct sh_qspi_slave, slave); +} + +static void sh_qspi_init(struct sh_qspi_slave *ss) +{ + /* QSPI initialize */ + /* Set master mode only */ + writeb(SPCR_MSTR, &ss->regs->spcr); + + /* Set SSL signal level */ + writeb(0x00, &ss->regs->sslp); + + /* Set MOSI signal value when transfer is in idle state */ + writeb(SPPCR_IO3FV|SPPCR_IO2FV, &ss->regs->sppcr); + + /* Set bit rate. See 58.3.8 Quad Serial Peripheral Interface */ + writeb(0x01, &ss->regs->spbr); + + /* Disable Dummy Data Transmission */ + writeb(0x00, &ss->regs->spdcr); + + /* Set clock delay value */ + writeb(0x00, &ss->regs->spckd); + + /* Set SSL negation delay value */ + writeb(0x00, &ss->regs->sslnd); + + /* Set next-access delay value */ + writeb(0x00, &ss->regs->spnd); + + /* Set equence command */ + writew(SPCMD_INIT2, &ss->regs->spcmd0); + + /* Reset transfer and receive Buffer */ + setbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST); + + /* Clear transfer and receive Buffer control bit */ + clrbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST); + + /* Set equence control method. Use equence0 only */ + writeb(0x00, &ss->regs->spscr); + + /* Enable SPI function */ + setbits_8(&ss->regs->spcr, SPCR_SPE); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct sh_qspi_slave *ss = to_sh_qspi(slave); + + /* Set master mode only */ + writeb(SPCR_MSTR, &ss->regs->spcr); + + /* Set command */ + writew(SPCMD_INIT1, &ss->regs->spcmd0); + + /* Reset transfer and receive Buffer */ + setbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST); + + /* Clear transfer and receive Buffer control bit */ + clrbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST); + + /* Set equence control method. Use equence0 only */ + writeb(0x00, &ss->regs->spscr); + + /* Enable SPI function */ + setbits_8(&ss->regs->spcr, SPCR_SPE); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct sh_qspi_slave *ss = to_sh_qspi(slave); + + /* Disable SPI Function */ + clrbits_8(&ss->regs->spcr, SPCR_SPE); +} + +void spi_init(void) +{ + /* nothing to do */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct sh_qspi_slave *ss; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + ss = spi_alloc_slave(struct sh_qspi_slave, bus, cs); + if (!ss) { + printf("SPI_error: Fail to allocate sh_qspi_slave\n"); + return NULL; + } + + ss->regs = (struct sh_qspi_regs *)SH_QSPI_BASE; + + /* Init SH QSPI */ + sh_qspi_init(ss); + + return &ss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct sh_qspi_slave *spi = to_sh_qspi(slave); + + free(spi); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct sh_qspi_slave *ss = to_sh_qspi(slave); + unsigned long nbyte; + int ret = 0; + unsigned char dtdata = 0, drdata; + unsigned char *tdata = &dtdata, *rdata = &drdata; + unsigned long *spbmul0 = &ss->regs->spbmul0; + + if (dout == NULL && din == NULL) { + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + return 0; + } + + if (bitlen % 8) { + printf("%s: bitlen is not 8bit alined %d", __func__, bitlen); + return 1; + } + + nbyte = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + spi_cs_activate(slave); + + /* Set 1048576 byte */ + writel(0x100000, spbmul0); + } + + if (flags & SPI_XFER_END) + writel(nbyte, spbmul0); + + if (dout != NULL) + tdata = (unsigned char *)dout; + + if (din != NULL) + rdata = din; + + while (nbyte > 0) { + while (!(readb(&ss->regs->spsr) & SPSR_SPTEF)) { + if (ctrlc()) { + puts("abort\n"); + return 1; + } + udelay(10); + } + + writeb(*tdata, (unsigned char *)(&ss->regs->spdr)); + + while ((readw(&ss->regs->spbdcr) != SPBDCR_RXBC0)) { + if (ctrlc()) { + puts("abort\n"); + return 1; + } + udelay(1); + } + + while (!(readb(&ss->regs->spsr) & SPSR_SPRFF)) { + if (ctrlc()) { + puts("abort\n"); + return 1; + } + udelay(10); + } + + *rdata = readb((unsigned char *)(&ss->regs->spdr)); + + if (dout != NULL) + tdata++; + if (din != NULL) + rdata++; + + nbyte--; + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/spi/sh_spi.c b/qemu/roms/u-boot/drivers/spi/sh_spi.c new file mode 100644 index 000000000..7ca5e363d --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/sh_spi.c @@ -0,0 +1,265 @@ +/* + * SH SPI driver + * + * Copyright (C) 2011-2012 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include "sh_spi.h" + +static void sh_spi_write(unsigned long data, unsigned long *reg) +{ + writel(data, reg); +} + +static unsigned long sh_spi_read(unsigned long *reg) +{ + return readl(reg); +} + +static void sh_spi_set_bit(unsigned long val, unsigned long *reg) +{ + unsigned long tmp; + + tmp = sh_spi_read(reg); + tmp |= val; + sh_spi_write(tmp, reg); +} + +static void sh_spi_clear_bit(unsigned long val, unsigned long *reg) +{ + unsigned long tmp; + + tmp = sh_spi_read(reg); + tmp &= ~val; + sh_spi_write(tmp, reg); +} + +static void clear_fifo(struct sh_spi *ss) +{ + sh_spi_set_bit(SH_SPI_RSTF, &ss->regs->cr2); + sh_spi_clear_bit(SH_SPI_RSTF, &ss->regs->cr2); +} + +static int recvbuf_wait(struct sh_spi *ss) +{ + while (sh_spi_read(&ss->regs->cr1) & SH_SPI_RBE) { + if (ctrlc()) + return 1; + udelay(10); + } + return 0; +} + +static int write_fifo_empty_wait(struct sh_spi *ss) +{ + while (!(sh_spi_read(&ss->regs->cr1) & SH_SPI_TBE)) { + if (ctrlc()) + return 1; + udelay(10); + } + return 0; +} + +void spi_init(void) +{ +} + +static void sh_spi_set_cs(struct sh_spi *ss, unsigned int cs) +{ + unsigned long val = 0; + + if (cs & 0x01) + val |= SH_SPI_SSS0; + if (cs & 0x02) + val |= SH_SPI_SSS1; + + sh_spi_clear_bit(SH_SPI_SSS0 | SH_SPI_SSS1, &ss->regs->cr4); + sh_spi_set_bit(val, &ss->regs->cr4); +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct sh_spi *ss; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + ss = spi_alloc_slave(struct sh_spi, bus, cs); + if (!ss) + return NULL; + + ss->regs = (struct sh_spi_regs *)CONFIG_SH_SPI_BASE; + + /* SPI sycle stop */ + sh_spi_write(0xfe, &ss->regs->cr1); + /* CR1 init */ + sh_spi_write(0x00, &ss->regs->cr1); + /* CR3 init */ + sh_spi_write(0x00, &ss->regs->cr3); + sh_spi_set_cs(ss, cs); + + clear_fifo(ss); + + /* 1/8 clock */ + sh_spi_write(sh_spi_read(&ss->regs->cr2) | 0x07, &ss->regs->cr2); + udelay(10); + + return &ss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct sh_spi *spi = to_sh_spi(slave); + + free(spi); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct sh_spi *ss = to_sh_spi(slave); + + sh_spi_write(sh_spi_read(&ss->regs->cr1) & + ~(SH_SPI_SSA | SH_SPI_SSDB | SH_SPI_SSD), &ss->regs->cr1); +} + +static int sh_spi_send(struct sh_spi *ss, const unsigned char *tx_data, + unsigned int len, unsigned long flags) +{ + int i, cur_len, ret = 0; + int remain = (int)len; + + if (len >= SH_SPI_FIFO_SIZE) + sh_spi_set_bit(SH_SPI_SSA, &ss->regs->cr1); + + while (remain > 0) { + cur_len = (remain < SH_SPI_FIFO_SIZE) ? + remain : SH_SPI_FIFO_SIZE; + for (i = 0; i < cur_len && + !(sh_spi_read(&ss->regs->cr4) & SH_SPI_WPABRT) && + !(sh_spi_read(&ss->regs->cr1) & SH_SPI_TBF); + i++) + sh_spi_write(tx_data[i], &ss->regs->tbr_rbr); + + cur_len = i; + + if (sh_spi_read(&ss->regs->cr4) & SH_SPI_WPABRT) { + /* Abort the transaction */ + flags |= SPI_XFER_END; + sh_spi_set_bit(SH_SPI_WPABRT, &ss->regs->cr4); + ret = 1; + break; + } + + remain -= cur_len; + tx_data += cur_len; + + if (remain > 0) + write_fifo_empty_wait(ss); + } + + if (flags & SPI_XFER_END) { + sh_spi_clear_bit(SH_SPI_SSD | SH_SPI_SSDB, &ss->regs->cr1); + sh_spi_set_bit(SH_SPI_SSA, &ss->regs->cr1); + udelay(100); + write_fifo_empty_wait(ss); + } + + return ret; +} + +static int sh_spi_receive(struct sh_spi *ss, unsigned char *rx_data, + unsigned int len, unsigned long flags) +{ + int i; + + if (len > SH_SPI_MAX_BYTE) + sh_spi_write(SH_SPI_MAX_BYTE, &ss->regs->cr3); + else + sh_spi_write(len, &ss->regs->cr3); + + sh_spi_clear_bit(SH_SPI_SSD | SH_SPI_SSDB, &ss->regs->cr1); + sh_spi_set_bit(SH_SPI_SSA, &ss->regs->cr1); + + for (i = 0; i < len; i++) { + if (recvbuf_wait(ss)) + return 0; + + rx_data[i] = (unsigned char)sh_spi_read(&ss->regs->tbr_rbr); + } + sh_spi_write(0, &ss->regs->cr3); + + return 0; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct sh_spi *ss = to_sh_spi(slave); + const unsigned char *tx_data = dout; + unsigned char *rx_data = din; + unsigned int len = bitlen / 8; + int ret = 0; + + if (flags & SPI_XFER_BEGIN) + sh_spi_write(sh_spi_read(&ss->regs->cr1) & ~SH_SPI_SSA, + &ss->regs->cr1); + + if (tx_data) + ret = sh_spi_send(ss, tx_data, len, flags); + + if (ret == 0 && rx_data) + ret = sh_spi_receive(ss, rx_data, len, flags); + + if (flags & SPI_XFER_END) { + sh_spi_set_bit(SH_SPI_SSD, &ss->regs->cr1); + udelay(100); + + sh_spi_clear_bit(SH_SPI_SSA | SH_SPI_SSDB | SH_SPI_SSD, + &ss->regs->cr1); + clear_fifo(ss); + } + + return ret; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + if (!bus && cs < SH_SPI_NUM_CS) + return 1; + else + return 0; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + +} diff --git a/qemu/roms/u-boot/drivers/spi/sh_spi.h b/qemu/roms/u-boot/drivers/spi/sh_spi.h new file mode 100644 index 000000000..96b4b6850 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/sh_spi.h @@ -0,0 +1,80 @@ +/* + * SH SPI driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __SH_SPI_H__ +#define __SH_SPI_H__ + +#include + +struct sh_spi_regs { + unsigned long tbr_rbr; + unsigned long resv1; + unsigned long cr1; + unsigned long resv2; + unsigned long cr2; + unsigned long resv3; + unsigned long cr3; + unsigned long resv4; + unsigned long cr4; +}; + +/* CR1 */ +#define SH_SPI_TBE 0x80 +#define SH_SPI_TBF 0x40 +#define SH_SPI_RBE 0x20 +#define SH_SPI_RBF 0x10 +#define SH_SPI_PFONRD 0x08 +#define SH_SPI_SSDB 0x04 +#define SH_SPI_SSD 0x02 +#define SH_SPI_SSA 0x01 + +/* CR2 */ +#define SH_SPI_RSTF 0x80 +#define SH_SPI_LOOPBK 0x40 +#define SH_SPI_CPOL 0x20 +#define SH_SPI_CPHA 0x10 +#define SH_SPI_L1M0 0x08 + +/* CR3 */ +#define SH_SPI_MAX_BYTE 0xFF + +/* CR4 */ +#define SH_SPI_TBEI 0x80 +#define SH_SPI_TBFI 0x40 +#define SH_SPI_RBEI 0x20 +#define SH_SPI_RBFI 0x10 +#define SH_SPI_SSS1 0x08 +#define SH_SPI_WPABRT 0x04 +#define SH_SPI_SSS0 0x01 + +#define SH_SPI_FIFO_SIZE 32 +#define SH_SPI_NUM_CS 4 + +struct sh_spi { + struct spi_slave slave; + struct sh_spi_regs *regs; +}; + +static inline struct sh_spi *to_sh_spi(struct spi_slave *slave) +{ + return container_of(slave, struct sh_spi, slave); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/spi/soft_spi.c b/qemu/roms/u-boot/drivers/spi/soft_spi.c new file mode 100644 index 000000000..5d2235129 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/soft_spi.c @@ -0,0 +1,175 @@ +/* + * (C) Copyright 2002 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + * + * Influenced by code from: + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include + +/*----------------------------------------------------------------------- + * Definitions + */ + +#ifdef DEBUG_SPI +#define PRINTD(fmt,args...) printf (fmt ,##args) +#else +#define PRINTD(fmt,args...) +#endif + +struct soft_spi_slave { + struct spi_slave slave; + unsigned int mode; +}; + +static inline struct soft_spi_slave *to_soft_spi(struct spi_slave *slave) +{ + return container_of(slave, struct soft_spi_slave, slave); +} + +/*=====================================================================*/ +/* Public Functions */ +/*=====================================================================*/ + +/*----------------------------------------------------------------------- + * Initialization + */ +void spi_init (void) +{ +#ifdef SPI_INIT + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + + SPI_INIT; +#endif +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct soft_spi_slave *ss; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + ss = spi_alloc_slave(struct soft_spi_slave, bus, cs); + if (!ss) + return NULL; + + ss->mode = mode; + + /* TODO: Use max_hz to limit the SCK rate */ + + return &ss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct soft_spi_slave *ss = to_soft_spi(slave); + + free(ss); +} + +int spi_claim_bus(struct spi_slave *slave) +{ +#ifdef CONFIG_SYS_IMMR + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; +#endif + struct soft_spi_slave *ss = to_soft_spi(slave); + + /* + * Make sure the SPI clock is in idle state as defined for + * this slave. + */ + if (ss->mode & SPI_CPOL) + SPI_SCL(1); + else + SPI_SCL(0); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + /* Nothing to do */ +} + +/*----------------------------------------------------------------------- + * SPI transfer + * + * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks + * "bitlen" bits in the SPI MISO port. That's just the way SPI works. + * + * The source of the outgoing bits is the "dout" parameter and the + * destination of the input bits is the "din" parameter. Note that "dout" + * and "din" can point to the same memory location, in which case the + * input data overwrites the output data (since both are buffered by + * temporary variables, this is OK). + */ +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ +#ifdef CONFIG_SYS_IMMR + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; +#endif + struct soft_spi_slave *ss = to_soft_spi(slave); + uchar tmpdin = 0; + uchar tmpdout = 0; + const u8 *txd = dout; + u8 *rxd = din; + int cpol = ss->mode & SPI_CPOL; + int cpha = ss->mode & SPI_CPHA; + unsigned int j; + + PRINTD("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(uint *)txd, *(uint *)rxd, bitlen); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + for(j = 0; j < bitlen; j++) { + /* + * Check if it is time to work on a new byte. + */ + if((j % 8) == 0) { + tmpdout = *txd++; + if(j != 0) { + *rxd++ = tmpdin; + } + tmpdin = 0; + } + + if (!cpha) + SPI_SCL(!cpol); + SPI_SDA(tmpdout & 0x80); + SPI_DELAY; + if (cpha) + SPI_SCL(!cpol); + else + SPI_SCL(cpol); + tmpdin <<= 1; + tmpdin |= SPI_READ; + tmpdout <<= 1; + SPI_DELAY; + if (cpha) + SPI_SCL(cpol); + } + /* + * If the number of bits isn't a multiple of 8, shift the last + * bits over to left-justify them. Then store the last byte + * read in. + */ + if((bitlen % 8) != 0) + tmpdin <<= 8 - (bitlen % 8); + *rxd++ = tmpdin; + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return(0); +} diff --git a/qemu/roms/u-boot/drivers/spi/spi.c b/qemu/roms/u-boot/drivers/spi/spi.c new file mode 100644 index 000000000..7ddea9b02 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/spi.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen) +{ + if (wordlen == 0 || wordlen > 32) { + printf("spi: invalid wordlen %d\n", wordlen); + return -1; + } + + slave->wordlen = wordlen; + + return 0; +} + +void *spi_do_alloc_slave(int offset, int size, unsigned int bus, + unsigned int cs) +{ + struct spi_slave *slave; + void *ptr; + + ptr = malloc(size); + if (ptr) { + memset(ptr, '\0', size); + slave = (struct spi_slave *)(ptr + offset); + slave->bus = bus; + slave->cs = cs; + slave->wordlen = SPI_DEFAULT_WORDLEN; + } + + return ptr; +} + +#ifdef CONFIG_OF_SPI +struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum, + int node) +{ + int cs, max_hz, mode = 0; + + cs = fdtdec_get_int(blob, node, "reg", -1); + max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 100000); + if (fdtdec_get_bool(blob, node, "spi-cpol")) + mode |= SPI_CPOL; + if (fdtdec_get_bool(blob, node, "spi-cpha")) + mode |= SPI_CPHA; + if (fdtdec_get_bool(blob, node, "spi-cs-high")) + mode |= SPI_CS_HIGH; + return spi_setup_slave(busnum, cs, max_hz, mode); +} +#endif diff --git a/qemu/roms/u-boot/drivers/spi/tegra114_spi.c b/qemu/roms/u-boot/drivers/spi/tegra114_spi.c new file mode 100644 index 000000000..810fa4718 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/tegra114_spi.c @@ -0,0 +1,398 @@ +/* + * NVIDIA Tegra SPI controller (T114 and later) + * + * Copyright (c) 2010-2013 NVIDIA Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* COMMAND1 */ +#define SPI_CMD1_GO (1 << 31) +#define SPI_CMD1_M_S (1 << 30) +#define SPI_CMD1_MODE_MASK 0x3 +#define SPI_CMD1_MODE_SHIFT 28 +#define SPI_CMD1_CS_SEL_MASK 0x3 +#define SPI_CMD1_CS_SEL_SHIFT 26 +#define SPI_CMD1_CS_POL_INACTIVE3 (1 << 25) +#define SPI_CMD1_CS_POL_INACTIVE2 (1 << 24) +#define SPI_CMD1_CS_POL_INACTIVE1 (1 << 23) +#define SPI_CMD1_CS_POL_INACTIVE0 (1 << 22) +#define SPI_CMD1_CS_SW_HW (1 << 21) +#define SPI_CMD1_CS_SW_VAL (1 << 20) +#define SPI_CMD1_IDLE_SDA_MASK 0x3 +#define SPI_CMD1_IDLE_SDA_SHIFT 18 +#define SPI_CMD1_BIDIR (1 << 17) +#define SPI_CMD1_LSBI_FE (1 << 16) +#define SPI_CMD1_LSBY_FE (1 << 15) +#define SPI_CMD1_BOTH_EN_BIT (1 << 14) +#define SPI_CMD1_BOTH_EN_BYTE (1 << 13) +#define SPI_CMD1_RX_EN (1 << 12) +#define SPI_CMD1_TX_EN (1 << 11) +#define SPI_CMD1_PACKED (1 << 5) +#define SPI_CMD1_BIT_LEN_MASK 0x1F +#define SPI_CMD1_BIT_LEN_SHIFT 0 + +/* COMMAND2 */ +#define SPI_CMD2_TX_CLK_TAP_DELAY (1 << 6) +#define SPI_CMD2_TX_CLK_TAP_DELAY_MASK (0x3F << 6) +#define SPI_CMD2_RX_CLK_TAP_DELAY (1 << 0) +#define SPI_CMD2_RX_CLK_TAP_DELAY_MASK (0x3F << 0) + +/* TRANSFER STATUS */ +#define SPI_XFER_STS_RDY (1 << 30) + +/* FIFO STATUS */ +#define SPI_FIFO_STS_CS_INACTIVE (1 << 31) +#define SPI_FIFO_STS_FRAME_END (1 << 30) +#define SPI_FIFO_STS_RX_FIFO_FLUSH (1 << 15) +#define SPI_FIFO_STS_TX_FIFO_FLUSH (1 << 14) +#define SPI_FIFO_STS_ERR (1 << 8) +#define SPI_FIFO_STS_TX_FIFO_OVF (1 << 7) +#define SPI_FIFO_STS_TX_FIFO_UNR (1 << 6) +#define SPI_FIFO_STS_RX_FIFO_OVF (1 << 5) +#define SPI_FIFO_STS_RX_FIFO_UNR (1 << 4) +#define SPI_FIFO_STS_TX_FIFO_FULL (1 << 3) +#define SPI_FIFO_STS_TX_FIFO_EMPTY (1 << 2) +#define SPI_FIFO_STS_RX_FIFO_FULL (1 << 1) +#define SPI_FIFO_STS_RX_FIFO_EMPTY (1 << 0) + +#define SPI_TIMEOUT 1000 +#define TEGRA_SPI_MAX_FREQ 52000000 + +struct spi_regs { + u32 command1; /* 000:SPI_COMMAND1 register */ + u32 command2; /* 004:SPI_COMMAND2 register */ + u32 timing1; /* 008:SPI_CS_TIM1 register */ + u32 timing2; /* 00c:SPI_CS_TIM2 register */ + u32 xfer_status;/* 010:SPI_TRANS_STATUS register */ + u32 fifo_status;/* 014:SPI_FIFO_STATUS register */ + u32 tx_data; /* 018:SPI_TX_DATA register */ + u32 rx_data; /* 01c:SPI_RX_DATA register */ + u32 dma_ctl; /* 020:SPI_DMA_CTL register */ + u32 dma_blk; /* 024:SPI_DMA_BLK register */ + u32 rsvd[56]; /* 028-107 reserved */ + u32 tx_fifo; /* 108:SPI_FIFO1 register */ + u32 rsvd2[31]; /* 10c-187 reserved */ + u32 rx_fifo; /* 188:SPI_FIFO2 register */ + u32 spare_ctl; /* 18c:SPI_SPARE_CTRL register */ +}; + +struct tegra_spi_ctrl { + struct spi_regs *regs; + unsigned int freq; + unsigned int mode; + int periph_id; + int valid; +}; + +struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; +}; + +static struct tegra_spi_ctrl spi_ctrls[CONFIG_TEGRA114_SPI_CTRLS]; + +static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) +{ + return container_of(slave, struct tegra_spi_slave, slave); +} + +int tegra114_spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + if (bus >= CONFIG_TEGRA114_SPI_CTRLS || cs > 3 || !spi_ctrls[bus].valid) + return 0; + else + return 1; +} + +struct spi_slave *tegra114_spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct tegra_spi_slave *spi; + + debug("%s: bus: %u, cs: %u, max_hz: %u, mode: %u\n", __func__, + bus, cs, max_hz, mode); + + if (!spi_cs_is_valid(bus, cs)) { + printf("SPI error: unsupported bus %d / chip select %d\n", + bus, cs); + return NULL; + } + + if (max_hz > TEGRA_SPI_MAX_FREQ) { + printf("SPI error: unsupported frequency %d Hz. Max frequency" + " is %d Hz\n", max_hz, TEGRA_SPI_MAX_FREQ); + return NULL; + } + + spi = spi_alloc_slave(struct tegra_spi_slave, bus, cs); + if (!spi) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + spi->ctrl = &spi_ctrls[bus]; + if (!spi->ctrl) { + printf("SPI error: could not find controller for bus %d\n", + bus); + return NULL; + } + + if (max_hz < spi->ctrl->freq) { + debug("%s: limiting frequency from %u to %u\n", __func__, + spi->ctrl->freq, max_hz); + spi->ctrl->freq = max_hz; + } + spi->ctrl->mode = mode; + + return &spi->slave; +} + +void tegra114_spi_free_slave(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + free(spi); +} + +int tegra114_spi_init(int *node_list, int count) +{ + struct tegra_spi_ctrl *ctrl; + int i; + int node = 0; + int found = 0; + + for (i = 0; i < count; i++) { + ctrl = &spi_ctrls[i]; + node = node_list[i]; + + ctrl->regs = (struct spi_regs *)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); + if ((fdt_addr_t)ctrl->regs == FDT_ADDR_T_NONE) { + debug("%s: no spi register found\n", __func__); + continue; + } + ctrl->freq = fdtdec_get_int(gd->fdt_blob, node, + "spi-max-frequency", 0); + if (!ctrl->freq) { + debug("%s: no spi max frequency found\n", __func__); + continue; + } + + ctrl->periph_id = clock_decode_periph_id(gd->fdt_blob, node); + if (ctrl->periph_id == PERIPH_ID_NONE) { + debug("%s: could not decode periph id\n", __func__); + continue; + } + ctrl->valid = 1; + found = 1; + + debug("%s: found controller at %p, freq = %u, periph_id = %d\n", + __func__, ctrl->regs, ctrl->freq, ctrl->periph_id); + } + + return !found; +} + +int tegra114_spi_claim_bus(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + /* Change SPI clock to correct frequency, PLLP_OUT0 source */ + clock_start_periph_pll(spi->ctrl->periph_id, CLOCK_ID_PERIPH, + spi->ctrl->freq); + + /* Clear stale status here */ + setbits_le32(®s->fifo_status, + SPI_FIFO_STS_ERR | + SPI_FIFO_STS_TX_FIFO_OVF | + SPI_FIFO_STS_TX_FIFO_UNR | + SPI_FIFO_STS_RX_FIFO_OVF | + SPI_FIFO_STS_RX_FIFO_UNR | + SPI_FIFO_STS_TX_FIFO_FULL | + SPI_FIFO_STS_TX_FIFO_EMPTY | + SPI_FIFO_STS_RX_FIFO_FULL | + SPI_FIFO_STS_RX_FIFO_EMPTY); + debug("%s: FIFO STATUS = %08x\n", __func__, readl(®s->fifo_status)); + + /* Set master mode and sw controlled CS */ + setbits_le32(®s->command1, SPI_CMD1_M_S | SPI_CMD1_CS_SW_HW | + (spi->ctrl->mode << SPI_CMD1_MODE_SHIFT)); + debug("%s: COMMAND1 = %08x\n", __func__, readl(®s->command1)); + + return 0; +} + +void tegra114_spi_cs_activate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + clrbits_le32(®s->command1, SPI_CMD1_CS_SW_VAL); +} + +void tegra114_spi_cs_deactivate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + setbits_le32(®s->command1, SPI_CMD1_CS_SW_VAL); +} + +int tegra114_spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + u32 reg, tmpdout, tmpdin = 0; + const u8 *dout = data_out; + u8 *din = data_in; + int num_bytes; + int ret; + + debug("%s: slave %u:%u dout %p din %p bitlen %u\n", + __func__, slave->bus, slave->cs, dout, din, bitlen); + if (bitlen % 8) + return -1; + num_bytes = bitlen / 8; + + ret = 0; + + /* clear all error status bits */ + reg = readl(®s->fifo_status); + writel(reg, ®s->fifo_status); + + clrsetbits_le32(®s->command1, SPI_CMD1_CS_SW_VAL, + SPI_CMD1_RX_EN | SPI_CMD1_TX_EN | SPI_CMD1_LSBY_FE | + (slave->cs << SPI_CMD1_CS_SEL_SHIFT)); + + /* set xfer size to 1 block (32 bits) */ + writel(0, ®s->dma_blk); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* handle data in 32-bit chunks */ + while (num_bytes > 0) { + int bytes; + int tm, i; + + tmpdout = 0; + bytes = (num_bytes > 4) ? 4 : num_bytes; + + if (dout != NULL) { + for (i = 0; i < bytes; ++i) + tmpdout = (tmpdout << 8) | dout[i]; + dout += bytes; + } + + num_bytes -= bytes; + + /* clear ready bit */ + setbits_le32(®s->xfer_status, SPI_XFER_STS_RDY); + + clrsetbits_le32(®s->command1, + SPI_CMD1_BIT_LEN_MASK << SPI_CMD1_BIT_LEN_SHIFT, + (bytes * 8 - 1) << SPI_CMD1_BIT_LEN_SHIFT); + writel(tmpdout, ®s->tx_fifo); + setbits_le32(®s->command1, SPI_CMD1_GO); + + /* + * Wait for SPI transmit FIFO to empty, or to time out. + * The RX FIFO status will be read and cleared last + */ + for (tm = 0; tm < SPI_TIMEOUT; ++tm) { + u32 fifo_status, xfer_status; + + xfer_status = readl(®s->xfer_status); + if (!(xfer_status & SPI_XFER_STS_RDY)) + continue; + + fifo_status = readl(®s->fifo_status); + if (fifo_status & SPI_FIFO_STS_ERR) { + debug("%s: got a fifo error: ", __func__); + if (fifo_status & SPI_FIFO_STS_TX_FIFO_OVF) + debug("tx FIFO overflow "); + if (fifo_status & SPI_FIFO_STS_TX_FIFO_UNR) + debug("tx FIFO underrun "); + if (fifo_status & SPI_FIFO_STS_RX_FIFO_OVF) + debug("rx FIFO overflow "); + if (fifo_status & SPI_FIFO_STS_RX_FIFO_UNR) + debug("rx FIFO underrun "); + if (fifo_status & SPI_FIFO_STS_TX_FIFO_FULL) + debug("tx FIFO full "); + if (fifo_status & SPI_FIFO_STS_TX_FIFO_EMPTY) + debug("tx FIFO empty "); + if (fifo_status & SPI_FIFO_STS_RX_FIFO_FULL) + debug("rx FIFO full "); + if (fifo_status & SPI_FIFO_STS_RX_FIFO_EMPTY) + debug("rx FIFO empty "); + debug("\n"); + break; + } + + if (!(fifo_status & SPI_FIFO_STS_RX_FIFO_EMPTY)) { + tmpdin = readl(®s->rx_fifo); + + /* swap bytes read in */ + if (din != NULL) { + for (i = bytes - 1; i >= 0; --i) { + din[i] = tmpdin & 0xff; + tmpdin >>= 8; + } + din += bytes; + } + + /* We can exit when we've had both RX and TX */ + break; + } + } + + if (tm >= SPI_TIMEOUT) + ret = tm; + + /* clear ACK RDY, etc. bits */ + writel(readl(®s->fifo_status), ®s->fifo_status); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + debug("%s: transfer ended. Value=%08x, fifo_status = %08x\n", + __func__, tmpdin, readl(®s->fifo_status)); + + if (ret) { + printf("%s: timeout during SPI transfer, tm %d\n", + __func__, ret); + return -1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/tegra20_sflash.c b/qemu/roms/u-boot/drivers/spi/tegra20_sflash.c new file mode 100644 index 000000000..b5d561be3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/tegra20_sflash.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2010-2013 NVIDIA Corporation + * With help from the mpc8xxx SPI driver + * With more help from omap3_spi SPI driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define SPI_CMD_GO (1 << 30) +#define SPI_CMD_ACTIVE_SCLK_SHIFT 26 +#define SPI_CMD_ACTIVE_SCLK_MASK (3 << SPI_CMD_ACTIVE_SCLK_SHIFT) +#define SPI_CMD_CK_SDA (1 << 21) +#define SPI_CMD_ACTIVE_SDA_SHIFT 18 +#define SPI_CMD_ACTIVE_SDA_MASK (3 << SPI_CMD_ACTIVE_SDA_SHIFT) +#define SPI_CMD_CS_POL (1 << 16) +#define SPI_CMD_TXEN (1 << 15) +#define SPI_CMD_RXEN (1 << 14) +#define SPI_CMD_CS_VAL (1 << 13) +#define SPI_CMD_CS_SOFT (1 << 12) +#define SPI_CMD_CS_DELAY (1 << 9) +#define SPI_CMD_CS3_EN (1 << 8) +#define SPI_CMD_CS2_EN (1 << 7) +#define SPI_CMD_CS1_EN (1 << 6) +#define SPI_CMD_CS0_EN (1 << 5) +#define SPI_CMD_BIT_LENGTH (1 << 4) +#define SPI_CMD_BIT_LENGTH_MASK 0x0000001F + +#define SPI_STAT_BSY (1 << 31) +#define SPI_STAT_RDY (1 << 30) +#define SPI_STAT_RXF_FLUSH (1 << 29) +#define SPI_STAT_TXF_FLUSH (1 << 28) +#define SPI_STAT_RXF_UNR (1 << 27) +#define SPI_STAT_TXF_OVF (1 << 26) +#define SPI_STAT_RXF_EMPTY (1 << 25) +#define SPI_STAT_RXF_FULL (1 << 24) +#define SPI_STAT_TXF_EMPTY (1 << 23) +#define SPI_STAT_TXF_FULL (1 << 22) +#define SPI_STAT_SEL_TXRX_N (1 << 16) +#define SPI_STAT_CUR_BLKCNT (1 << 15) + +#define SPI_TIMEOUT 1000 +#define TEGRA_SPI_MAX_FREQ 52000000 + +struct spi_regs { + u32 command; /* SPI_COMMAND_0 register */ + u32 status; /* SPI_STATUS_0 register */ + u32 rx_cmp; /* SPI_RX_CMP_0 register */ + u32 dma_ctl; /* SPI_DMA_CTL_0 register */ + u32 tx_fifo; /* SPI_TX_FIFO_0 register */ + u32 rsvd[3]; /* offsets 0x14 to 0x1F reserved */ + u32 rx_fifo; /* SPI_RX_FIFO_0 register */ +}; + +struct tegra_spi_ctrl { + struct spi_regs *regs; + unsigned int freq; + unsigned int mode; + int periph_id; + int valid; +}; + +struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; +}; + +/* tegra20 only supports one SFLASH controller */ +static struct tegra_spi_ctrl spi_ctrls[1]; + +static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) +{ + return container_of(slave, struct tegra_spi_slave, slave); +} + +int tegra20_spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + /* Tegra20 SPI-Flash - only 1 device ('bus/cs') */ + if (bus != 0 || cs != 0) + return 0; + else + return 1; +} + +struct spi_slave *tegra20_spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct tegra_spi_slave *spi; + + if (!spi_cs_is_valid(bus, cs)) { + printf("SPI error: unsupported bus %d / chip select %d\n", + bus, cs); + return NULL; + } + + if (max_hz > TEGRA_SPI_MAX_FREQ) { + printf("SPI error: unsupported frequency %d Hz. Max frequency" + " is %d Hz\n", max_hz, TEGRA_SPI_MAX_FREQ); + return NULL; + } + + spi = spi_alloc_slave(struct tegra_spi_slave, bus, cs); + if (!spi) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + spi->ctrl = &spi_ctrls[bus]; + if (!spi->ctrl) { + printf("SPI error: could not find controller for bus %d\n", + bus); + return NULL; + } + + if (max_hz < spi->ctrl->freq) { + debug("%s: limiting frequency from %u to %u\n", __func__, + spi->ctrl->freq, max_hz); + spi->ctrl->freq = max_hz; + } + spi->ctrl->mode = mode; + + return &spi->slave; +} + +void tegra20_spi_free_slave(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + free(spi); +} + +int tegra20_spi_init(int *node_list, int count) +{ + struct tegra_spi_ctrl *ctrl; + int i; + int node = 0; + int found = 0; + + for (i = 0; i < count; i++) { + ctrl = &spi_ctrls[i]; + node = node_list[i]; + + ctrl->regs = (struct spi_regs *)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); + if ((fdt_addr_t)ctrl->regs == FDT_ADDR_T_NONE) { + debug("%s: no slink register found\n", __func__); + continue; + } + ctrl->freq = fdtdec_get_int(gd->fdt_blob, node, + "spi-max-frequency", 0); + if (!ctrl->freq) { + debug("%s: no slink max frequency found\n", __func__); + continue; + } + + ctrl->periph_id = clock_decode_periph_id(gd->fdt_blob, node); + if (ctrl->periph_id == PERIPH_ID_NONE) { + debug("%s: could not decode periph id\n", __func__); + continue; + } + ctrl->valid = 1; + found = 1; + + debug("%s: found controller at %p, freq = %u, periph_id = %d\n", + __func__, ctrl->regs, ctrl->freq, ctrl->periph_id); + } + return !found; +} + +int tegra20_spi_claim_bus(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + u32 reg; + + /* Change SPI clock to correct frequency, PLLP_OUT0 source */ + clock_start_periph_pll(spi->ctrl->periph_id, CLOCK_ID_PERIPH, + spi->ctrl->freq); + + /* Clear stale status here */ + reg = SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | \ + SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF; + writel(reg, ®s->status); + debug("%s: STATUS = %08x\n", __func__, readl(®s->status)); + + /* + * Use sw-controlled CS, so we can clock in data after ReadID, etc. + */ + reg = (spi->ctrl->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT; + if (spi->ctrl->mode & 2) + reg |= 1 << SPI_CMD_ACTIVE_SCLK_SHIFT; + clrsetbits_le32(®s->command, SPI_CMD_ACTIVE_SCLK_MASK | + SPI_CMD_ACTIVE_SDA_MASK, SPI_CMD_CS_SOFT | reg); + debug("%s: COMMAND = %08x\n", __func__, readl(®s->command)); + + /* + * SPI pins on Tegra20 are muxed - change pinmux later due to UART + * issue. + */ + pinmux_set_func(PMUX_PINGRP_GMD, PMUX_FUNC_SFLASH); + pinmux_tristate_disable(PMUX_PINGRP_LSPI); + pinmux_set_func(PMUX_PINGRP_GMC, PMUX_FUNC_SFLASH); + + return 0; +} + +void tegra20_spi_cs_activate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + /* CS is negated on Tegra, so drive a 1 to get a 0 */ + setbits_le32(®s->command, SPI_CMD_CS_VAL); +} + +void tegra20_spi_cs_deactivate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + /* CS is negated on Tegra, so drive a 0 to get a 1 */ + clrbits_le32(®s->command, SPI_CMD_CS_VAL); +} + +int tegra20_spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + u32 reg, tmpdout, tmpdin = 0; + const u8 *dout = data_out; + u8 *din = data_in; + int num_bytes; + int ret; + + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(u8 *)dout, *(u8 *)din, bitlen); + if (bitlen % 8) + return -1; + num_bytes = bitlen / 8; + + ret = 0; + + reg = readl(®s->status); + writel(reg, ®s->status); /* Clear all SPI events via R/W */ + debug("spi_xfer entry: STATUS = %08x\n", reg); + + reg = readl(®s->command); + reg |= SPI_CMD_TXEN | SPI_CMD_RXEN; + writel(reg, ®s->command); + debug("spi_xfer: COMMAND = %08x\n", readl(®s->command)); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* handle data in 32-bit chunks */ + while (num_bytes > 0) { + int bytes; + int is_read = 0; + int tm, i; + + tmpdout = 0; + bytes = (num_bytes > 4) ? 4 : num_bytes; + + if (dout != NULL) { + for (i = 0; i < bytes; ++i) + tmpdout = (tmpdout << 8) | dout[i]; + } + + num_bytes -= bytes; + if (dout) + dout += bytes; + + clrsetbits_le32(®s->command, SPI_CMD_BIT_LENGTH_MASK, + bytes * 8 - 1); + writel(tmpdout, ®s->tx_fifo); + setbits_le32(®s->command, SPI_CMD_GO); + + /* + * Wait for SPI transmit FIFO to empty, or to time out. + * The RX FIFO status will be read and cleared last + */ + for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) { + u32 status; + + status = readl(®s->status); + + /* We can exit when we've had both RX and TX activity */ + if (is_read && (status & SPI_STAT_TXF_EMPTY)) + break; + + if ((status & (SPI_STAT_BSY | SPI_STAT_RDY)) != + SPI_STAT_RDY) + tm++; + + else if (!(status & SPI_STAT_RXF_EMPTY)) { + tmpdin = readl(®s->rx_fifo); + is_read = 1; + + /* swap bytes read in */ + if (din != NULL) { + for (i = bytes - 1; i >= 0; --i) { + din[i] = tmpdin & 0xff; + tmpdin >>= 8; + } + din += bytes; + } + } + } + + if (tm >= SPI_TIMEOUT) + ret = tm; + + /* clear ACK RDY, etc. bits */ + writel(readl(®s->status), ®s->status); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + debug("spi_xfer: transfer ended. Value=%08x, status = %08x\n", + tmpdin, readl(®s->status)); + + if (ret) { + printf("spi_xfer: timeout during SPI transfer, tm %d\n", ret); + return -1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/tegra20_slink.c b/qemu/roms/u-boot/drivers/spi/tegra20_slink.c new file mode 100644 index 000000000..664de6e91 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/tegra20_slink.c @@ -0,0 +1,359 @@ +/* + * NVIDIA Tegra SPI-SLINK controller + * + * Copyright (c) 2010-2013 NVIDIA Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* COMMAND */ +#define SLINK_CMD_ENB (1 << 31) +#define SLINK_CMD_GO (1 << 30) +#define SLINK_CMD_M_S (1 << 28) +#define SLINK_CMD_CK_SDA (1 << 21) +#define SLINK_CMD_CS_POL (1 << 13) +#define SLINK_CMD_CS_VAL (1 << 12) +#define SLINK_CMD_CS_SOFT (1 << 11) +#define SLINK_CMD_BIT_LENGTH (1 << 4) +#define SLINK_CMD_BIT_LENGTH_MASK 0x0000001F +/* COMMAND2 */ +#define SLINK_CMD2_TXEN (1 << 30) +#define SLINK_CMD2_RXEN (1 << 31) +#define SLINK_CMD2_SS_EN (1 << 18) +#define SLINK_CMD2_SS_EN_SHIFT 18 +#define SLINK_CMD2_SS_EN_MASK 0x000C0000 +#define SLINK_CMD2_CS_ACTIVE_BETWEEN (1 << 17) +/* STATUS */ +#define SLINK_STAT_BSY (1 << 31) +#define SLINK_STAT_RDY (1 << 30) +#define SLINK_STAT_ERR (1 << 29) +#define SLINK_STAT_RXF_FLUSH (1 << 27) +#define SLINK_STAT_TXF_FLUSH (1 << 26) +#define SLINK_STAT_RXF_OVF (1 << 25) +#define SLINK_STAT_TXF_UNR (1 << 24) +#define SLINK_STAT_RXF_EMPTY (1 << 23) +#define SLINK_STAT_RXF_FULL (1 << 22) +#define SLINK_STAT_TXF_EMPTY (1 << 21) +#define SLINK_STAT_TXF_FULL (1 << 20) +#define SLINK_STAT_TXF_OVF (1 << 19) +#define SLINK_STAT_RXF_UNR (1 << 18) +#define SLINK_STAT_CUR_BLKCNT (1 << 15) +/* STATUS2 */ +#define SLINK_STAT2_RXF_FULL_CNT (1 << 16) +#define SLINK_STAT2_TXF_FULL_CNT (1 << 0) + +#define SPI_TIMEOUT 1000 +#define TEGRA_SPI_MAX_FREQ 52000000 + +struct spi_regs { + u32 command; /* SLINK_COMMAND_0 register */ + u32 command2; /* SLINK_COMMAND2_0 reg */ + u32 status; /* SLINK_STATUS_0 register */ + u32 reserved; /* Reserved offset 0C */ + u32 mas_data; /* SLINK_MAS_DATA_0 reg */ + u32 slav_data; /* SLINK_SLAVE_DATA_0 reg */ + u32 dma_ctl; /* SLINK_DMA_CTL_0 register */ + u32 status2; /* SLINK_STATUS2_0 reg */ + u32 rsvd[56]; /* 0x20 to 0xFF reserved */ + u32 tx_fifo; /* SLINK_TX_FIFO_0 reg off 100h */ + u32 rsvd2[31]; /* 0x104 to 0x17F reserved */ + u32 rx_fifo; /* SLINK_RX_FIFO_0 reg off 180h */ +}; + +struct tegra_spi_ctrl { + struct spi_regs *regs; + unsigned int freq; + unsigned int mode; + int periph_id; + int valid; +}; + +struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; +}; + +static struct tegra_spi_ctrl spi_ctrls[CONFIG_TEGRA_SLINK_CTRLS]; + +static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) +{ + return container_of(slave, struct tegra_spi_slave, slave); +} + +int tegra30_spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + if (bus >= CONFIG_TEGRA_SLINK_CTRLS || cs > 3 || !spi_ctrls[bus].valid) + return 0; + else + return 1; +} + +struct spi_slave *tegra30_spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct tegra_spi_slave *spi; + + debug("%s: bus: %u, cs: %u, max_hz: %u, mode: %u\n", __func__, + bus, cs, max_hz, mode); + + if (!spi_cs_is_valid(bus, cs)) { + printf("SPI error: unsupported bus %d / chip select %d\n", + bus, cs); + return NULL; + } + + if (max_hz > TEGRA_SPI_MAX_FREQ) { + printf("SPI error: unsupported frequency %d Hz. Max frequency" + " is %d Hz\n", max_hz, TEGRA_SPI_MAX_FREQ); + return NULL; + } + + spi = spi_alloc_slave(struct tegra_spi_slave, bus, cs); + if (!spi) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + spi->ctrl = &spi_ctrls[bus]; + if (!spi->ctrl) { + printf("SPI error: could not find controller for bus %d\n", + bus); + return NULL; + } + + if (max_hz < spi->ctrl->freq) { + debug("%s: limiting frequency from %u to %u\n", __func__, + spi->ctrl->freq, max_hz); + spi->ctrl->freq = max_hz; + } + spi->ctrl->mode = mode; + + return &spi->slave; +} + +void tegra30_spi_free_slave(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + free(spi); +} + +int tegra30_spi_init(int *node_list, int count) +{ + struct tegra_spi_ctrl *ctrl; + int i; + int node = 0; + int found = 0; + + for (i = 0; i < count; i++) { + ctrl = &spi_ctrls[i]; + node = node_list[i]; + + ctrl->regs = (struct spi_regs *)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); + if ((fdt_addr_t)ctrl->regs == FDT_ADDR_T_NONE) { + debug("%s: no slink register found\n", __func__); + continue; + } + ctrl->freq = fdtdec_get_int(gd->fdt_blob, node, + "spi-max-frequency", 0); + if (!ctrl->freq) { + debug("%s: no slink max frequency found\n", __func__); + continue; + } + + ctrl->periph_id = clock_decode_periph_id(gd->fdt_blob, node); + if (ctrl->periph_id == PERIPH_ID_NONE) { + debug("%s: could not decode periph id\n", __func__); + continue; + } + ctrl->valid = 1; + found = 1; + + debug("%s: found controller at %p, freq = %u, periph_id = %d\n", + __func__, ctrl->regs, ctrl->freq, ctrl->periph_id); + } + return !found; +} + +int tegra30_spi_claim_bus(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + u32 reg; + + /* Change SPI clock to correct frequency, PLLP_OUT0 source */ + clock_start_periph_pll(spi->ctrl->periph_id, CLOCK_ID_PERIPH, + spi->ctrl->freq); + + /* Clear stale status here */ + reg = SLINK_STAT_RDY | SLINK_STAT_RXF_FLUSH | SLINK_STAT_TXF_FLUSH | \ + SLINK_STAT_RXF_UNR | SLINK_STAT_TXF_OVF; + writel(reg, ®s->status); + debug("%s: STATUS = %08x\n", __func__, readl(®s->status)); + + /* Set master mode and sw controlled CS */ + reg = readl(®s->command); + reg |= SLINK_CMD_M_S | SLINK_CMD_CS_SOFT; + writel(reg, ®s->command); + debug("%s: COMMAND = %08x\n", __func__, readl(®s->command)); + + return 0; +} + +void tegra30_spi_cs_activate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + /* CS is negated on Tegra, so drive a 1 to get a 0 */ + setbits_le32(®s->command, SLINK_CMD_CS_VAL); +} + +void tegra30_spi_cs_deactivate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + /* CS is negated on Tegra, so drive a 0 to get a 1 */ + clrbits_le32(®s->command, SLINK_CMD_CS_VAL); +} + +int tegra30_spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + u32 reg, tmpdout, tmpdin = 0; + const u8 *dout = data_out; + u8 *din = data_in; + int num_bytes; + int ret; + + debug("%s: slave %u:%u dout %p din %p bitlen %u\n", + __func__, slave->bus, slave->cs, dout, din, bitlen); + if (bitlen % 8) + return -1; + num_bytes = bitlen / 8; + + ret = 0; + + reg = readl(®s->status); + writel(reg, ®s->status); /* Clear all SPI events via R/W */ + debug("%s entry: STATUS = %08x\n", __func__, reg); + + reg = readl(®s->status2); + writel(reg, ®s->status2); /* Clear all STATUS2 events via R/W */ + debug("%s entry: STATUS2 = %08x\n", __func__, reg); + + debug("%s entry: COMMAND = %08x\n", __func__, readl(®s->command)); + + clrsetbits_le32(®s->command2, SLINK_CMD2_SS_EN_MASK, + SLINK_CMD2_TXEN | SLINK_CMD2_RXEN | + (slave->cs << SLINK_CMD2_SS_EN_SHIFT)); + debug("%s entry: COMMAND2 = %08x\n", __func__, readl(®s->command2)); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* handle data in 32-bit chunks */ + while (num_bytes > 0) { + int bytes; + int is_read = 0; + int tm, i; + + tmpdout = 0; + bytes = (num_bytes > 4) ? 4 : num_bytes; + + if (dout != NULL) { + for (i = 0; i < bytes; ++i) + tmpdout = (tmpdout << 8) | dout[i]; + dout += bytes; + } + + num_bytes -= bytes; + + clrsetbits_le32(®s->command, SLINK_CMD_BIT_LENGTH_MASK, + bytes * 8 - 1); + writel(tmpdout, ®s->tx_fifo); + setbits_le32(®s->command, SLINK_CMD_GO); + + /* + * Wait for SPI transmit FIFO to empty, or to time out. + * The RX FIFO status will be read and cleared last + */ + for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) { + u32 status; + + status = readl(®s->status); + + /* We can exit when we've had both RX and TX activity */ + if (is_read && (status & SLINK_STAT_TXF_EMPTY)) + break; + + if ((status & (SLINK_STAT_BSY | SLINK_STAT_RDY)) != + SLINK_STAT_RDY) + tm++; + + else if (!(status & SLINK_STAT_RXF_EMPTY)) { + tmpdin = readl(®s->rx_fifo); + is_read = 1; + + /* swap bytes read in */ + if (din != NULL) { + for (i = bytes - 1; i >= 0; --i) { + din[i] = tmpdin & 0xff; + tmpdin >>= 8; + } + din += bytes; + } + } + } + + if (tm >= SPI_TIMEOUT) + ret = tm; + + /* clear ACK RDY, etc. bits */ + writel(readl(®s->status), ®s->status); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + debug("%s: transfer ended. Value=%08x, status = %08x\n", + __func__, tmpdin, readl(®s->status)); + + if (ret) { + printf("%s: timeout during SPI transfer, tm %d\n", + __func__, ret); + return -1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/ti_qspi.c b/qemu/roms/u-boot/drivers/spi/ti_qspi.c new file mode 100644 index 000000000..c5d2245e4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/ti_qspi.c @@ -0,0 +1,341 @@ +/* + * TI QSPI driver + * + * Copyright (C) 2013, Texas Instruments, Incorporated + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +/* ti qpsi register bit masks */ +#define QSPI_TIMEOUT 2000000 +#define QSPI_FCLK 192000000 +/* clock control */ +#define QSPI_CLK_EN (1 << 31) +#define QSPI_CLK_DIV_MAX 0xffff +/* command */ +#define QSPI_EN_CS(n) (n << 28) +#define QSPI_WLEN(n) ((n-1) << 19) +#define QSPI_3_PIN (1 << 18) +#define QSPI_RD_SNGL (1 << 16) +#define QSPI_WR_SNGL (2 << 16) +#define QSPI_INVAL (4 << 16) +#define QSPI_RD_QUAD (7 << 16) +/* device control */ +#define QSPI_DD(m, n) (m << (3 + n*8)) +#define QSPI_CKPHA(n) (1 << (2 + n*8)) +#define QSPI_CSPOL(n) (1 << (1 + n*8)) +#define QSPI_CKPOL(n) (1 << (n*8)) +/* status */ +#define QSPI_WC (1 << 1) +#define QSPI_BUSY (1 << 0) +#define QSPI_WC_BUSY (QSPI_WC | QSPI_BUSY) +#define QSPI_XFER_DONE QSPI_WC +#define MM_SWITCH 0x01 +#define MEM_CS 0x100 +#define MEM_CS_UNSELECT 0xfffff0ff +#define MMAP_START_ADDR_DRA 0x5c000000 +#define MMAP_START_ADDR_AM43x 0x30000000 +#define CORE_CTRL_IO 0x4a002558 + +#define QSPI_CMD_READ (0x3 << 0) +#define QSPI_CMD_READ_QUAD (0x6b << 0) +#define QSPI_CMD_READ_FAST (0x0b << 0) +#define QSPI_SETUP0_NUM_A_BYTES (0x2 << 8) +#define QSPI_SETUP0_NUM_D_BYTES_NO_BITS (0x0 << 10) +#define QSPI_SETUP0_NUM_D_BYTES_8_BITS (0x1 << 10) +#define QSPI_SETUP0_READ_NORMAL (0x0 << 12) +#define QSPI_SETUP0_READ_QUAD (0x3 << 12) +#define QSPI_CMD_WRITE (0x2 << 16) +#define QSPI_NUM_DUMMY_BITS (0x0 << 24) + +/* ti qspi register set */ +struct ti_qspi_regs { + u32 pid; + u32 pad0[3]; + u32 sysconfig; + u32 pad1[3]; + u32 int_stat_raw; + u32 int_stat_en; + u32 int_en_set; + u32 int_en_ctlr; + u32 intc_eoi; + u32 pad2[3]; + u32 clk_ctrl; + u32 dc; + u32 cmd; + u32 status; + u32 data; + u32 setup0; + u32 setup1; + u32 setup2; + u32 setup3; + u32 memswitch; + u32 data1; + u32 data2; + u32 data3; +}; + +/* ti qspi slave */ +struct ti_qspi_slave { + struct spi_slave slave; + struct ti_qspi_regs *base; + unsigned int mode; + u32 cmd; + u32 dc; +}; + +static inline struct ti_qspi_slave *to_ti_qspi_slave(struct spi_slave *slave) +{ + return container_of(slave, struct ti_qspi_slave, slave); +} + +static void ti_spi_setup_spi_register(struct ti_qspi_slave *qslave) +{ + struct spi_slave *slave = &qslave->slave; + u32 memval = 0; + +#ifdef CONFIG_DRA7XX + slave->memory_map = (void *)MMAP_START_ADDR_DRA; +#else + slave->memory_map = (void *)MMAP_START_ADDR_AM43x; +#endif + + memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES | + QSPI_SETUP0_NUM_D_BYTES_NO_BITS | + QSPI_SETUP0_READ_NORMAL | QSPI_CMD_WRITE | + QSPI_NUM_DUMMY_BITS; + + writel(memval, &qslave->base->setup0); +} + +static void ti_spi_set_speed(struct spi_slave *slave, uint hz) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + uint clk_div; + + debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div); + + if (!hz) + clk_div = 0; + else + clk_div = (QSPI_FCLK / hz) - 1; + + /* disable SCLK */ + writel(readl(&qslave->base->clk_ctrl) & ~QSPI_CLK_EN, + &qslave->base->clk_ctrl); + + /* assign clk_div values */ + if (clk_div < 0) + clk_div = 0; + else if (clk_div > QSPI_CLK_DIV_MAX) + clk_div = QSPI_CLK_DIV_MAX; + + /* enable SCLK */ + writel(QSPI_CLK_EN | clk_div, &qslave->base->clk_ctrl); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + /* CS handled in xfer */ + return; +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + + debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); + + writel(qslave->cmd | QSPI_INVAL, &qslave->base->cmd); +} + +void spi_init(void) +{ + /* nothing to do */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct ti_qspi_slave *qslave; + +#ifdef CONFIG_AM43XX + gpio_request(CONFIG_QSPI_SEL_GPIO, "qspi_gpio"); + gpio_direction_output(CONFIG_QSPI_SEL_GPIO, 1); +#endif + + qslave = spi_alloc_slave(struct ti_qspi_slave, bus, cs); + if (!qslave) { + printf("SPI_error: Fail to allocate ti_qspi_slave\n"); + return NULL; + } + + qslave->base = (struct ti_qspi_regs *)QSPI_BASE; + qslave->mode = mode; + + ti_spi_set_speed(&qslave->slave, max_hz); + +#ifdef CONFIG_TI_SPI_MMAP + ti_spi_setup_spi_register(qslave); +#endif + + return &qslave->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + free(qslave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + + debug("spi_claim_bus: bus:%i cs:%i\n", slave->bus, slave->cs); + + qslave->dc = 0; + if (qslave->mode & SPI_CPHA) + qslave->dc |= QSPI_CKPHA(slave->cs); + if (qslave->mode & SPI_CPOL) + qslave->dc |= QSPI_CKPOL(slave->cs); + if (qslave->mode & SPI_CS_HIGH) + qslave->dc |= QSPI_CSPOL(slave->cs); + + writel(qslave->dc, &qslave->base->dc); + writel(0, &qslave->base->cmd); + writel(0, &qslave->base->data); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + + debug("spi_release_bus: bus:%i cs:%i\n", slave->bus, slave->cs); + + writel(0, &qslave->base->dc); + writel(0, &qslave->base->cmd); + writel(0, &qslave->base->data); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + uint words = bitlen >> 3; /* fixed 8-bit word length */ + const uchar *txp = dout; + uchar *rxp = din; + uint status; + int timeout; + +#ifdef CONFIG_DRA7XX + int val; +#endif + + debug("spi_xfer: bus:%i cs:%i bitlen:%i words:%i flags:%lx\n", + slave->bus, slave->cs, bitlen, words, flags); + + /* Setup mmap flags */ + if (flags & SPI_XFER_MMAP) { + writel(MM_SWITCH, &qslave->base->memswitch); +#ifdef CONFIG_DRA7XX + val = readl(CORE_CTRL_IO); + val |= MEM_CS; + writel(val, CORE_CTRL_IO); +#endif + return 0; + } else if (flags & SPI_XFER_MMAP_END) { + writel(~MM_SWITCH, &qslave->base->memswitch); +#ifdef CONFIG_DRA7XX + val = readl(CORE_CTRL_IO); + val &= MEM_CS_UNSELECT; + writel(val, CORE_CTRL_IO); +#endif + return 0; + } + + if (bitlen == 0) + return -1; + + if (bitlen % 8) { + debug("spi_xfer: Non byte aligned SPI transfer\n"); + return -1; + } + + /* Setup command reg */ + qslave->cmd = 0; + qslave->cmd |= QSPI_WLEN(8); + qslave->cmd |= QSPI_EN_CS(slave->cs); + if (flags & SPI_3WIRE) + qslave->cmd |= QSPI_3_PIN; + qslave->cmd |= 0xfff; + +/* FIXME: This delay is required for successfull + * completion of read/write/erase. Once its root + * caused, it will be remove from the driver. + */ +#ifdef CONFIG_AM43XX + udelay(100); +#endif + while (words--) { + if (txp) { + debug("tx cmd %08x dc %08x data %02x\n", + qslave->cmd | QSPI_WR_SNGL, qslave->dc, *txp); + writel(*txp++, &qslave->base->data); + writel(qslave->cmd | QSPI_WR_SNGL, + &qslave->base->cmd); + status = readl(&qslave->base->status); + timeout = QSPI_TIMEOUT; + while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { + if (--timeout < 0) { + printf("spi_xfer: TX timeout!\n"); + return -1; + } + status = readl(&qslave->base->status); + } + debug("tx done, status %08x\n", status); + } + if (rxp) { + qslave->cmd |= QSPI_RD_SNGL; + debug("rx cmd %08x dc %08x\n", + qslave->cmd, qslave->dc); + #ifdef CONFIG_DRA7XX + udelay(500); + #endif + writel(qslave->cmd, &qslave->base->cmd); + status = readl(&qslave->base->status); + timeout = QSPI_TIMEOUT; + while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { + if (--timeout < 0) { + printf("spi_xfer: RX timeout!\n"); + return -1; + } + status = readl(&qslave->base->status); + } + *rxp++ = readl(&qslave->base->data); + debug("rx done, status %08x, read %02x\n", + status, *(rxp-1)); + } + } + + /* Terminate frame */ + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/xilinx_spi.c b/qemu/roms/u-boot/drivers/spi/xilinx_spi.c new file mode 100644 index 000000000..56d99d17c --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/xilinx_spi.c @@ -0,0 +1,215 @@ +/* + * Xilinx SPI driver + * + * supports 8 bit SPI transfers only, with or w/o FIFO + * + * based on bfin_spi.c, by way of altera_spi.c + * Copyright (c) 2005-2008 Analog Devices Inc. + * Copyright (c) 2010 Thomas Chou + * Copyright (c) 2010 Graeme Smecher + * Copyright (c) 2012 Stephan Linz + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_spi.pdf + * [0]/ip_documentation/axi_spi_ds742.pdf + */ +#include +#include +#include +#include + +#include "xilinx_spi.h" + +#ifndef CONFIG_SYS_XILINX_SPI_LIST +#define CONFIG_SYS_XILINX_SPI_LIST { CONFIG_SYS_SPI_BASE } +#endif + +#ifndef CONFIG_XILINX_SPI_IDLE_VAL +#define CONFIG_XILINX_SPI_IDLE_VAL 0xff +#endif + +#define XILSPI_SPICR_DFLT_ON (SPICR_MANUAL_SS | \ + SPICR_MASTER_MODE | \ + SPICR_SPE) + +#define XILSPI_SPICR_DFLT_OFF (SPICR_MASTER_INHIBIT | \ + SPICR_MANUAL_SS) + +#define XILSPI_MAX_XFER_BITS 8 + +static unsigned long xilinx_spi_base_list[] = CONFIG_SYS_XILINX_SPI_LIST; + +__attribute__((weak)) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus < ARRAY_SIZE(xilinx_spi_base_list) && cs < 32; +} + +__attribute__((weak)) +void spi_cs_activate(struct spi_slave *slave) +{ + struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); + + writel(SPISSR_ACT(slave->cs), &xilspi->regs->spissr); +} + +__attribute__((weak)) +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); + + writel(SPISSR_OFF, &xilspi->regs->spissr); +} + +void spi_init(void) +{ + /* do nothing */ +} + +void spi_set_speed(struct spi_slave *slave, uint hz) +{ + /* xilinx spi core does not support programmable speed */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct xilinx_spi_slave *xilspi; + + if (!spi_cs_is_valid(bus, cs)) { + printf("XILSPI error: %s: unsupported bus %d / cs %d\n", + __func__, bus, cs); + return NULL; + } + + xilspi = spi_alloc_slave(struct xilinx_spi_slave, bus, cs); + if (!xilspi) { + printf("XILSPI error: %s: malloc of SPI structure failed\n", + __func__); + return NULL; + } + xilspi->regs = (struct xilinx_spi_reg *)xilinx_spi_base_list[bus]; + xilspi->freq = max_hz; + xilspi->mode = mode; + debug("%s: bus:%i cs:%i base:%p mode:%x max_hz:%d\n", __func__, + bus, cs, xilspi->regs, xilspi->mode, xilspi->freq); + + writel(SPISSR_RESET_VALUE, &xilspi->regs->srr); + + return &xilspi->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); + + free(xilspi); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); + u32 spicr; + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + writel(SPISSR_OFF, &xilspi->regs->spissr); + + spicr = XILSPI_SPICR_DFLT_ON; + if (xilspi->mode & SPI_LSB_FIRST) + spicr |= SPICR_LSB_FIRST; + if (xilspi->mode & SPI_CPHA) + spicr |= SPICR_CPHA; + if (xilspi->mode & SPI_CPOL) + spicr |= SPICR_CPOL; + if (xilspi->mode & SPI_LOOP) + spicr |= SPICR_LOOP; + + writel(spicr, &xilspi->regs->spicr); + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + writel(SPISSR_OFF, &xilspi->regs->spissr); + writel(XILSPI_SPICR_DFLT_OFF, &xilspi->regs->spicr); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct xilinx_spi_slave *xilspi = to_xilinx_spi_slave(slave); + /* assume spi core configured to do 8 bit transfers */ + unsigned int bytes = bitlen / XILSPI_MAX_XFER_BITS; + const unsigned char *txp = dout; + unsigned char *rxp = din; + unsigned rxecount = 17; /* max. 16 elements in FIFO, leftover 1 */ + unsigned global_timeout; + + debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, + slave->bus, slave->cs, bitlen, bytes, flags); + if (bitlen == 0) + goto done; + + if (bitlen % XILSPI_MAX_XFER_BITS) { + printf("XILSPI warning: %s: Not a multiple of %d bits\n", + __func__, XILSPI_MAX_XFER_BITS); + flags |= SPI_XFER_END; + goto done; + } + + /* empty read buffer */ + while (rxecount && !(readl(&xilspi->regs->spisr) & SPISR_RX_EMPTY)) { + readl(&xilspi->regs->spidrr); + rxecount--; + } + + if (!rxecount) { + printf("XILSPI error: %s: Rx buffer not empty\n", __func__); + return -1; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* at least 1usec or greater, leftover 1 */ + global_timeout = xilspi->freq > XILSPI_MAX_XFER_BITS * 1000000 ? 2 : + (XILSPI_MAX_XFER_BITS * 1000000 / xilspi->freq) + 1; + + while (bytes--) { + unsigned timeout = global_timeout; + /* get Tx element from data out buffer and count up */ + unsigned char d = txp ? *txp++ : CONFIG_XILINX_SPI_IDLE_VAL; + debug("%s: tx:%x ", __func__, d); + + /* write out and wait for processing (receive data) */ + writel(d & SPIDTR_8BIT_MASK, &xilspi->regs->spidtr); + while (timeout && readl(&xilspi->regs->spisr) + & SPISR_RX_EMPTY) { + timeout--; + udelay(1); + } + + if (!timeout) { + printf("XILSPI error: %s: Xfer timeout\n", __func__); + return -1; + } + + /* read Rx element and push into data in buffer */ + d = readl(&xilspi->regs->spidrr) & SPIDRR_8BIT_MASK; + if (rxp) + *rxp++ = d; + debug("rx:%x\n", d); + } + + done: + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/spi/xilinx_spi.h b/qemu/roms/u-boot/drivers/spi/xilinx_spi.h new file mode 100644 index 000000000..ce7d82c39 --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/xilinx_spi.h @@ -0,0 +1,138 @@ +/* + * Xilinx SPI driver + * + * XPS/AXI bus interface + * + * based on bfin_spi.c, by way of altera_spi.c + * Copyright (c) 2005-2008 Analog Devices Inc. + * Copyright (c) 2010 Thomas Chou + * Copyright (c) 2010 Graeme Smecher + * Copyright (c) 2012 Stephan Linz + * + * SPDX-License-Identifier: GPL-2.0+ + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_spi.pdf + * [0]/ip_documentation/axi_spi_ds742.pdf + */ +#ifndef _XILINX_SPI_ +#define _XILINX_SPI_ + +#include +#include + +/* + * Xilinx SPI Register Definition + * + * [1]: [0]/ip_documentation/xps_spi.pdf + * page 8, Register Descriptions + * [2]: [0]/ip_documentation/axi_spi_ds742.pdf + * page 7, Register Overview Table + */ +struct xilinx_spi_reg { + u32 __space0__[7]; + u32 dgier; /* Device Global Interrupt Enable Register (DGIER) */ + u32 ipisr; /* IP Interrupt Status Register (IPISR) */ + u32 __space1__; + u32 ipier; /* IP Interrupt Enable Register (IPIER) */ + u32 __space2__[5]; + u32 srr; /* Softare Reset Register (SRR) */ + u32 __space3__[7]; + u32 spicr; /* SPI Control Register (SPICR) */ + u32 spisr; /* SPI Status Register (SPISR) */ + u32 spidtr; /* SPI Data Transmit Register (SPIDTR) */ + u32 spidrr; /* SPI Data Receive Register (SPIDRR) */ + u32 spissr; /* SPI Slave Select Register (SPISSR) */ + u32 spitfor; /* SPI Transmit FIFO Occupancy Register (SPITFOR) */ + u32 spirfor; /* SPI Receive FIFO Occupancy Register (SPIRFOR) */ +}; + +/* Device Global Interrupt Enable Register (dgier), [1] p15, [2] p15 */ +#define DGIER_GIE (1 << 31) + +/* IP Interrupt Status Register (ipisr), [1] p15, [2] p15 */ +#define IPISR_DRR_NOT_EMPTY (1 << 8) +#define IPISR_SLAVE_SELECT (1 << 7) +#define IPISR_TXF_HALF_EMPTY (1 << 6) +#define IPISR_DRR_OVERRUN (1 << 5) +#define IPISR_DRR_FULL (1 << 4) +#define IPISR_DTR_UNDERRUN (1 << 3) +#define IPISR_DTR_EMPTY (1 << 2) +#define IPISR_SLAVE_MODF (1 << 1) +#define IPISR_MODF (1 << 0) + +/* IP Interrupt Enable Register (ipier), [1] p17, [2] p18 */ +#define IPIER_DRR_NOT_EMPTY (1 << 8) +#define IPIER_SLAVE_SELECT (1 << 7) +#define IPIER_TXF_HALF_EMPTY (1 << 6) +#define IPIER_DRR_OVERRUN (1 << 5) +#define IPIER_DRR_FULL (1 << 4) +#define IPIER_DTR_UNDERRUN (1 << 3) +#define IPIER_DTR_EMPTY (1 << 2) +#define IPIER_SLAVE_MODF (1 << 1) +#define IPIER_MODF (1 << 0) + +/* Softare Reset Register (srr), [1] p9, [2] p8 */ +#define SRR_RESET_CODE 0x0000000A + +/* SPI Control Register (spicr), [1] p9, [2] p8 */ +#define SPICR_LSB_FIRST (1 << 9) +#define SPICR_MASTER_INHIBIT (1 << 8) +#define SPICR_MANUAL_SS (1 << 7) +#define SPICR_RXFIFO_RESEST (1 << 6) +#define SPICR_TXFIFO_RESEST (1 << 5) +#define SPICR_CPHA (1 << 4) +#define SPICR_CPOL (1 << 3) +#define SPICR_MASTER_MODE (1 << 2) +#define SPICR_SPE (1 << 1) +#define SPICR_LOOP (1 << 0) + +/* SPI Status Register (spisr), [1] p11, [2] p10 */ +#define SPISR_SLAVE_MODE_SELECT (1 << 5) +#define SPISR_MODF (1 << 4) +#define SPISR_TX_FULL (1 << 3) +#define SPISR_TX_EMPTY (1 << 2) +#define SPISR_RX_FULL (1 << 1) +#define SPISR_RX_EMPTY (1 << 0) + +/* SPI Data Transmit Register (spidtr), [1] p12, [2] p12 */ +#define SPIDTR_8BIT_MASK (0xff << 0) +#define SPIDTR_16BIT_MASK (0xffff << 0) +#define SPIDTR_32BIT_MASK (0xffffffff << 0) + +/* SPI Data Receive Register (spidrr), [1] p12, [2] p12 */ +#define SPIDRR_8BIT_MASK (0xff << 0) +#define SPIDRR_16BIT_MASK (0xffff << 0) +#define SPIDRR_32BIT_MASK (0xffffffff << 0) + +/* SPI Slave Select Register (spissr), [1] p13, [2] p13 */ +#define SPISSR_MASK(cs) (1 << (cs)) +#define SPISSR_ACT(cs) ~SPISSR_MASK(cs) +#define SPISSR_OFF ~0UL + +/* SPI Transmit FIFO Occupancy Register (spitfor), [1] p13, [2] p14 */ +#define SPITFOR_OCYVAL_POS 0 +#define SPITFOR_OCYVAL_MASK (0xf << SPITFOR_OCYVAL_POS) + +/* SPI Receive FIFO Occupancy Register (spirfor), [1] p14, [2] p14 */ +#define SPIRFOR_OCYVAL_POS 0 +#define SPIRFOR_OCYVAL_MASK (0xf << SPIRFOR_OCYVAL_POS) + +/* SPI Software Reset Register (ssr) */ +#define SPISSR_RESET_VALUE 0x0a + +struct xilinx_spi_slave { + struct spi_slave slave; + struct xilinx_spi_reg *regs; + unsigned int freq; + unsigned int mode; +}; + +static inline struct xilinx_spi_slave *to_xilinx_spi_slave( + struct spi_slave *slave) +{ + return container_of(slave, struct xilinx_spi_slave, slave); +} + +#endif /* _XILINX_SPI_ */ diff --git a/qemu/roms/u-boot/drivers/spi/zynq_spi.c b/qemu/roms/u-boot/drivers/spi/zynq_spi.c new file mode 100644 index 000000000..5da87591c --- /dev/null +++ b/qemu/roms/u-boot/drivers/spi/zynq_spi.c @@ -0,0 +1,280 @@ +/* + * (C) Copyright 2013 Inc. + * + * Xilinx Zynq PS SPI controller driver (master mode only) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +/* zynq spi register bit masks ZYNQ_SPI___MASK */ +#define ZYNQ_SPI_CR_MSA_MASK (1 << 15) /* Manual start enb */ +#define ZYNQ_SPI_CR_MCS_MASK (1 << 14) /* Manual chip select */ +#define ZYNQ_SPI_CR_CS_MASK (0xF << 10) /* Chip select */ +#define ZYNQ_SPI_CR_BRD_MASK (0x7 << 3) /* Baud rate div */ +#define ZYNQ_SPI_CR_CPHA_MASK (1 << 2) /* Clock phase */ +#define ZYNQ_SPI_CR_CPOL_MASK (1 << 1) /* Clock polarity */ +#define ZYNQ_SPI_CR_MSTREN_MASK (1 << 0) /* Mode select */ +#define ZYNQ_SPI_IXR_RXNEMPTY_MASK (1 << 4) /* RX_FIFO_not_empty */ +#define ZYNQ_SPI_IXR_TXOW_MASK (1 << 2) /* TX_FIFO_not_full */ +#define ZYNQ_SPI_IXR_ALL_MASK 0x7F /* All IXR bits */ +#define ZYNQ_SPI_ENR_SPI_EN_MASK (1 << 0) /* SPI Enable */ + +#define ZYNQ_SPI_FIFO_DEPTH 128 +#ifndef CONFIG_SYS_ZYNQ_SPI_WAIT +#define CONFIG_SYS_ZYNQ_SPI_WAIT (CONFIG_SYS_HZ/100) /* 10 ms */ +#endif + +/* zynq spi register set */ +struct zynq_spi_regs { + u32 cr; /* 0x00 */ + u32 isr; /* 0x04 */ + u32 ier; /* 0x08 */ + u32 idr; /* 0x0C */ + u32 imr; /* 0x10 */ + u32 enr; /* 0x14 */ + u32 dr; /* 0x18 */ + u32 txdr; /* 0x1C */ + u32 rxdr; /* 0x20 */ +}; + +/* zynq spi slave */ +struct zynq_spi_slave { + struct spi_slave slave; + struct zynq_spi_regs *base; + u8 mode; + u8 fifo_depth; + u32 speed_hz; + u32 input_hz; + u32 req_hz; +}; + +static inline struct zynq_spi_slave *to_zynq_spi_slave(struct spi_slave *slave) +{ + return container_of(slave, struct zynq_spi_slave, slave); +} + +static inline struct zynq_spi_regs *get_zynq_spi_base(int dev) +{ + if (dev) + return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR1; + else + return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR0; +} + +static void zynq_spi_init_hw(struct zynq_spi_slave *zslave) +{ + u32 confr; + + /* Disable SPI */ + writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + + /* Disable Interrupts */ + writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->idr); + + /* Clear RX FIFO */ + while (readl(&zslave->base->isr) & + ZYNQ_SPI_IXR_RXNEMPTY_MASK) + readl(&zslave->base->rxdr); + + /* Clear Interrupts */ + writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->isr); + + /* Manual slave select and Auto start */ + confr = ZYNQ_SPI_CR_MCS_MASK | ZYNQ_SPI_CR_CS_MASK | + ZYNQ_SPI_CR_MSTREN_MASK; + confr &= ~ZYNQ_SPI_CR_MSA_MASK; + writel(confr, &zslave->base->cr); + + /* Enable SPI */ + writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + /* 2 bus with 3 chipselect */ + return bus < 2 && cs < 3; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + u32 cr; + + debug("spi_cs_activate: 0x%08x\n", (u32)slave); + + clrbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); + cr = readl(&zslave->base->cr); + /* + * CS cal logic: CS[13:10] + * xxx0 - cs0 + * xx01 - cs1 + * x011 - cs2 + */ + cr |= (~(0x1 << slave->cs) << 10) & ZYNQ_SPI_CR_CS_MASK; + writel(cr, &zslave->base->cr); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + + debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); + + setbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); +} + +void spi_init() +{ + /* nothing to do */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct zynq_spi_slave *zslave; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + zslave = spi_alloc_slave(struct zynq_spi_slave, bus, cs); + if (!zslave) { + printf("SPI_error: Fail to allocate zynq_spi_slave\n"); + return NULL; + } + + zslave->base = get_zynq_spi_base(bus); + zslave->mode = mode; + zslave->fifo_depth = ZYNQ_SPI_FIFO_DEPTH; + zslave->input_hz = 166666700; + zslave->speed_hz = zslave->input_hz / 2; + zslave->req_hz = max_hz; + + /* init the zynq spi hw */ + zynq_spi_init_hw(zslave); + + return &zslave->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + + debug("spi_free_slave: 0x%08x\n", (u32)slave); + free(zslave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + u32 confr = 0; + u8 baud_rate_val = 0; + + writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + + /* Set the SPI Clock phase and polarities */ + confr = readl(&zslave->base->cr); + confr &= ~(ZYNQ_SPI_CR_CPHA_MASK | ZYNQ_SPI_CR_CPOL_MASK); + if (zslave->mode & SPI_CPHA) + confr |= ZYNQ_SPI_CR_CPHA_MASK; + if (zslave->mode & SPI_CPOL) + confr |= ZYNQ_SPI_CR_CPOL_MASK; + + /* Set the clock frequency */ + if (zslave->req_hz == 0) { + /* Set baudrate x8, if the req_hz is 0 */ + baud_rate_val = 0x2; + } else if (zslave->speed_hz != zslave->req_hz) { + while ((baud_rate_val < 8) && + ((zslave->input_hz / + (2 << baud_rate_val)) > zslave->req_hz)) + baud_rate_val++; + zslave->speed_hz = zslave->req_hz / (2 << baud_rate_val); + } + confr &= ~ZYNQ_SPI_CR_BRD_MASK; + confr |= (baud_rate_val << 3); + writel(confr, &zslave->base->cr); + + writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + + debug("spi_release_bus: 0x%08x\n", (u32)slave); + writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + u32 len = bitlen / 8; + u32 tx_len = len, rx_len = len, tx_tvl; + const u8 *tx_buf = dout; + u8 *rx_buf = din, buf; + u32 ts, status; + + debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n", + slave->bus, slave->cs, bitlen, len, flags); + + if (bitlen == 0) + return -1; + + if (bitlen % 8) { + debug("spi_xfer: Non byte aligned SPI transfer\n"); + return -1; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + while (rx_len > 0) { + /* Write the data into TX FIFO - tx threshold is fifo_depth */ + tx_tvl = 0; + while ((tx_tvl < zslave->fifo_depth) && tx_len) { + if (tx_buf) + buf = *tx_buf++; + else + buf = 0; + writel(buf, &zslave->base->txdr); + tx_len--; + tx_tvl++; + } + + /* Check TX FIFO completion */ + ts = get_timer(0); + status = readl(&zslave->base->isr); + while (!(status & ZYNQ_SPI_IXR_TXOW_MASK)) { + if (get_timer(ts) > CONFIG_SYS_ZYNQ_SPI_WAIT) { + printf("spi_xfer: Timeout! TX FIFO not full\n"); + return -1; + } + status = readl(&zslave->base->isr); + } + + /* Read the data from RX FIFO */ + status = readl(&zslave->base->isr); + while (status & ZYNQ_SPI_IXR_RXNEMPTY_MASK) { + buf = readl(&zslave->base->rxdr); + if (rx_buf) + *rx_buf++ = buf; + status = readl(&zslave->base->isr); + rx_len--; + } + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/tpm/Makefile b/qemu/roms/u-boot/drivers/tpm/Makefile new file mode 100644 index 000000000..150570ee7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/tpm/Makefile @@ -0,0 +1,11 @@ +# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +# TODO: Merge tpm_tis_lpc.c with tpm.c +obj-$(CONFIG_TPM_ATMEL_TWI) += tpm_atmel_twi.o +obj-$(CONFIG_TPM_TIS_I2C) += tpm.o +obj-$(CONFIG_TPM_TIS_I2C) += tpm_tis_i2c.o +obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o +obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o diff --git a/qemu/roms/u-boot/drivers/tpm/tpm.c b/qemu/roms/u-boot/drivers/tpm/tpm.c new file mode 100644 index 000000000..b65733419 --- /dev/null +++ b/qemu/roms/u-boot/drivers/tpm/tpm.c @@ -0,0 +1,629 @@ +/* + * Copyright (C) 2011 Infineon Technologies + * + * Authors: + * Peter Huewe + * + * Description: + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * It is based on the Linux kernel driver tpm.c from Leendert van + * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall. + * + * Version: 2.1.1 + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpm_private.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* TPM configuration */ +struct tpm { + int i2c_bus; + int slave_addr; + char inited; + int old_bus; +} tpm; + +/* Global structure for tpm chip data */ +static struct tpm_chip g_chip; + +enum tpm_duration { + TPM_SHORT = 0, + TPM_MEDIUM = 1, + TPM_LONG = 2, + TPM_UNDEFINED, +}; + +/* Extended error numbers from linux (see errno.h) */ +#define ECANCELED 125 /* Operation Canceled */ + +/* Timer frequency. Corresponds to msec timer resolution*/ +#define HZ 1000 + +#define TPM_MAX_ORDINAL 243 +#define TPM_MAX_PROTECTED_ORDINAL 12 +#define TPM_PROTECTED_ORDINAL_MASK 0xFF + +#define TPM_CMD_COUNT_BYTE 2 +#define TPM_CMD_ORDINAL_BYTE 6 + +/* + * Array with one entry per ordinal defining the maximum amount + * of time the chip could take to return the result. The ordinal + * designation of short, medium or long is defined in a table in + * TCG Specification TPM Main Part 2 TPM Structures Section 17. The + * values of the SHORT, MEDIUM, and LONG durations are retrieved + * from the chip during initialization with a call to tpm_get_timeouts. + */ +static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = { + TPM_UNDEFINED, /* 0 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 5 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 10 */ + TPM_SHORT, +}; + +static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = { + TPM_UNDEFINED, /* 0 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 5 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 10 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_LONG, + TPM_LONG, + TPM_MEDIUM, /* 15 */ + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, + TPM_LONG, + TPM_SHORT, /* 20 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, /* 25 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 30 */ + TPM_LONG, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 35 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 40 */ + TPM_LONG, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 45 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_LONG, + TPM_MEDIUM, /* 50 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 55 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 60 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 65 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 70 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 75 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 80 */ + TPM_UNDEFINED, + TPM_MEDIUM, + TPM_LONG, + TPM_SHORT, + TPM_UNDEFINED, /* 85 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 90 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 95 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 100 */ + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 105 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 110 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 115 */ + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 120 */ + TPM_LONG, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 125 */ + TPM_SHORT, + TPM_LONG, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 130 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_SHORT, + TPM_MEDIUM, + TPM_UNDEFINED, /* 135 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 140 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 145 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 150 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 155 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 160 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 165 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 170 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 175 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 180 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, /* 185 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 190 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 195 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 200 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 205 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 210 */ + TPM_UNDEFINED, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_UNDEFINED, /* 215 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 220 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 225 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 230 */ + TPM_LONG, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 235 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 240 */ + TPM_UNDEFINED, + TPM_MEDIUM, +}; + +/* Returns max number of milliseconds to wait */ +static unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, + u32 ordinal) +{ + int duration_idx = TPM_UNDEFINED; + int duration = 0; + + if (ordinal < TPM_MAX_ORDINAL) { + duration_idx = tpm_ordinal_duration[ordinal]; + } else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < + TPM_MAX_PROTECTED_ORDINAL) { + duration_idx = tpm_protected_ordinal_duration[ + ordinal & TPM_PROTECTED_ORDINAL_MASK]; + } + + if (duration_idx != TPM_UNDEFINED) + duration = chip->vendor.duration[duration_idx]; + + if (duration <= 0) + return 2 * 60 * HZ; /* Two minutes timeout */ + else + return duration; +} + +static ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz) +{ + ssize_t rc; + u32 count, ordinal; + unsigned long start, stop; + + struct tpm_chip *chip = &g_chip; + + /* switch endianess: big->little */ + count = get_unaligned_be32(buf + TPM_CMD_COUNT_BYTE); + ordinal = get_unaligned_be32(buf + TPM_CMD_ORDINAL_BYTE); + + if (count == 0) { + error("no data\n"); + return -ENODATA; + } + if (count > bufsiz) { + error("invalid count value %x %zx\n", count, bufsiz); + return -E2BIG; + } + + rc = chip->vendor.send(chip, (u8 *)buf, count); + if (rc < 0) { + error("tpm_transmit: tpm_send: error %zd\n", rc); + goto out; + } + + if (chip->vendor.irq) + goto out_recv; + + start = get_timer(0); + stop = tpm_calc_ordinal_duration(chip, ordinal); + do { + debug("waiting for status...\n"); + u8 status = chip->vendor.status(chip); + if ((status & chip->vendor.req_complete_mask) == + chip->vendor.req_complete_val) { + debug("...got it;\n"); + goto out_recv; + } + + if ((status == chip->vendor.req_canceled)) { + error("Operation Canceled\n"); + rc = -ECANCELED; + goto out; + } + udelay(TPM_TIMEOUT * 1000); + } while (get_timer(start) < stop); + + chip->vendor.cancel(chip); + error("Operation Timed out\n"); + rc = -ETIME; + goto out; + +out_recv: + debug("out_recv: reading response...\n"); + rc = chip->vendor.recv(chip, (u8 *)buf, TPM_BUFSIZE); + if (rc < 0) + error("tpm_transmit: tpm_recv: error %zd\n", rc); + +out: + return rc; +} + +static int tpm_open(uint32_t dev_addr) +{ + int rc; + if (g_chip.is_open) + return -EBUSY; + rc = tpm_vendor_init(dev_addr); + if (rc < 0) + g_chip.is_open = 0; + return rc; +} + +static void tpm_close(void) +{ + if (g_chip.is_open) { + tpm_vendor_cleanup(&g_chip); + g_chip.is_open = 0; + } +} + +static int tpm_select(void) +{ + int ret; + + tpm.old_bus = i2c_get_bus_num(); + if (tpm.old_bus != tpm.i2c_bus) { + ret = i2c_set_bus_num(tpm.i2c_bus); + if (ret) { + debug("%s: Fail to set i2c bus %d\n", __func__, + tpm.i2c_bus); + return -1; + } + } + return 0; +} + +static int tpm_deselect(void) +{ + int ret; + + if (tpm.old_bus != i2c_get_bus_num()) { + ret = i2c_set_bus_num(tpm.old_bus); + if (ret) { + debug("%s: Fail to restore i2c bus %d\n", + __func__, tpm.old_bus); + return -1; + } + } + tpm.old_bus = -1; + return 0; +} + +/** + * Decode TPM configuration. + * + * @param dev Returns a configuration of TPM device + * @return 0 if ok, -1 on error + */ +static int tpm_decode_config(struct tpm *dev) +{ +#ifdef CONFIG_OF_CONTROL + const void *blob = gd->fdt_blob; + int node, parent; + int i2c_bus; + + node = fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM); + if (node < 0) { + node = fdtdec_next_compatible(blob, 0, + COMPAT_INFINEON_SLB9645_TPM); + } + if (node < 0) { + debug("%s: Node not found\n", __func__); + return -1; + } + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + i2c_bus = i2c_get_bus_num_fdt(parent); + if (i2c_bus < 0) + return -1; + dev->i2c_bus = i2c_bus; + dev->slave_addr = fdtdec_get_addr(blob, node, "reg"); +#else + dev->i2c_bus = CONFIG_TPM_TIS_I2C_BUS_NUMBER; + dev->slave_addr = CONFIG_TPM_TIS_I2C_SLAVE_ADDRESS; +#endif + return 0; +} + +struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *entry) +{ + struct tpm_chip *chip; + + /* Driver specific per-device data */ + chip = &g_chip; + memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific)); + chip->is_open = 1; + + return chip; +} + +int tis_init(void) +{ + if (tpm.inited) + return 0; + + if (tpm_decode_config(&tpm)) + return -1; + + if (tpm_select()) + return -1; + + /* + * Probe TPM twice; the first probing might fail because TPM is asleep, + * and the probing can wake up TPM. + */ + if (i2c_probe(tpm.slave_addr) && i2c_probe(tpm.slave_addr)) { + debug("%s: fail to probe i2c addr 0x%x\n", __func__, + tpm.slave_addr); + return -1; + } + + tpm_deselect(); + + tpm.inited = 1; + + return 0; +} + +int tis_open(void) +{ + int rc; + + if (!tpm.inited) + return -1; + + if (tpm_select()) + return -1; + + rc = tpm_open(tpm.slave_addr); + + tpm_deselect(); + + return rc; +} + +int tis_close(void) +{ + if (!tpm.inited) + return -1; + + if (tpm_select()) + return -1; + + tpm_close(); + + tpm_deselect(); + + return 0; +} + +int tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size, + uint8_t *recvbuf, size_t *rbuf_len) +{ + int len; + uint8_t buf[4096]; + + if (!tpm.inited) + return -1; + + if (sizeof(buf) < sbuf_size) + return -1; + + memcpy(buf, sendbuf, sbuf_size); + + if (tpm_select()) + return -1; + + len = tpm_transmit(buf, sbuf_size); + + tpm_deselect(); + + if (len < 10) { + *rbuf_len = 0; + return -1; + } + + memcpy(recvbuf, buf, len); + *rbuf_len = len; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/tpm/tpm_atmel_twi.c b/qemu/roms/u-boot/drivers/tpm/tpm_atmel_twi.c new file mode 100644 index 000000000..361a7720f --- /dev/null +++ b/qemu/roms/u-boot/drivers/tpm/tpm_atmel_twi.c @@ -0,0 +1,121 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#define ATMEL_TPM_TIMEOUT_MS 5000 /* sufficient for anything but + generating/exporting keys */ + +/* + * tis_init() + * + * Initialize the TPM device. Returns 0 on success or -1 on + * failure (in case device probing did not succeed). + */ +int tis_init(void) +{ + return 0; +} + +/* + * tis_open() + * + * Requests access to locality 0 for the caller. After all commands have been + * completed the caller is supposed to call tis_close(). + * + * Returns 0 on success, -1 on failure. + */ +int tis_open(void) +{ + return 0; +} + +/* + * tis_close() + * + * terminate the currect session with the TPM by releasing the locked + * locality. Returns 0 on success of -1 on failure (in case lock + * removal did not succeed). + */ +int tis_close(void) +{ + return 0; +} + +/* + * tis_sendrecv() + * + * Send the requested data to the TPM and then try to get its response + * + * @sendbuf - buffer of the data to send + * @send_size size of the data to send + * @recvbuf - memory to save the response to + * @recv_len - pointer to the size of the response buffer + * + * Returns 0 on success (and places the number of response bytes at recv_len) + * or -1 on failure. + */ +int tis_sendrecv(const uint8_t *sendbuf, size_t send_size, uint8_t *recvbuf, + size_t *recv_len) +{ + int res; + unsigned long start; + +#ifdef DEBUG + memset(recvbuf, 0xcc, *recv_len); + printf("send to TPM (%d bytes, recv_len=%d):\n", send_size, *recv_len); + print_buffer(0, (void *)sendbuf, 1, send_size, 0); +#endif + + res = i2c_write(0x29, 0, 0, (uchar *)sendbuf, send_size); + if (res) { + printf("i2c_write returned %d\n", res); + return -1; + } + + start = get_timer(0); + while ((res = i2c_read(0x29, 0, 0, recvbuf, 10))) { + if (get_timer(start) > ATMEL_TPM_TIMEOUT_MS) { + puts("tpm timed out\n"); + return -1; + } + udelay(100); + } + if (!res) { + *recv_len = get_unaligned_be32(recvbuf + 2); + if (*recv_len > 10) + res = i2c_read(0x29, 0, 0, recvbuf, *recv_len); + } + if (res) { + printf("i2c_read returned %d (rlen=%d)\n", res, *recv_len); +#ifdef DEBUG + print_buffer(0, recvbuf, 1, *recv_len, 0); +#endif + } + +#ifdef DEBUG + if (!res) { + printf("read from TPM (%d bytes):\n", *recv_len); + print_buffer(0, recvbuf, 1, *recv_len, 0); + } +#endif + + return res; +} diff --git a/qemu/roms/u-boot/drivers/tpm/tpm_private.h b/qemu/roms/u-boot/drivers/tpm/tpm_private.h new file mode 100644 index 000000000..888a074d3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/tpm/tpm_private.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2011 Infineon Technologies + * + * Authors: + * Peter Huewe + * + * Version: 2.1.1 + * + * Description: + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * It is based on the Linux kernel driver tpm.c from Leendert van + * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall. + * + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _TPM_PRIVATE_H_ +#define _TPM_PRIVATE_H_ + +#include +#include + +enum tpm_timeout { + TPM_TIMEOUT = 5, /* msecs */ +}; + +/* Size of external transmit buffer (used in tpm_transmit)*/ +#define TPM_BUFSIZE 4096 + +/* Index of Count field in TPM response buffer */ +#define TPM_RSP_SIZE_BYTE 2 +#define TPM_RSP_RC_BYTE 6 + +struct tpm_chip; + +struct tpm_vendor_specific { + const u8 req_complete_mask; + const u8 req_complete_val; + const u8 req_canceled; + int irq; + int (*recv) (struct tpm_chip *, u8 *, size_t); + int (*send) (struct tpm_chip *, u8 *, size_t); + void (*cancel) (struct tpm_chip *); + u8(*status) (struct tpm_chip *); + int locality; + unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */ + unsigned long duration[3]; /* msec */ +}; + +struct tpm_chip { + int is_open; + struct tpm_vendor_specific vendor; +}; + +struct tpm_input_header { + __be16 tag; + __be32 length; + __be32 ordinal; +} __packed; + +struct tpm_output_header { + __be16 tag; + __be32 length; + __be32 return_code; +} __packed; + +struct timeout_t { + __be32 a; + __be32 b; + __be32 c; + __be32 d; +} __packed; + +struct duration_t { + __be32 tpm_short; + __be32 tpm_medium; + __be32 tpm_long; +} __packed; + +union cap_t { + struct timeout_t timeout; + struct duration_t duration; +}; + +struct tpm_getcap_params_in { + __be32 cap; + __be32 subcap_size; + __be32 subcap; +} __packed; + +struct tpm_getcap_params_out { + __be32 cap_size; + union cap_t cap; +} __packed; + +union tpm_cmd_header { + struct tpm_input_header in; + struct tpm_output_header out; +}; + +union tpm_cmd_params { + struct tpm_getcap_params_out getcap_out; + struct tpm_getcap_params_in getcap_in; +}; + +struct tpm_cmd_t { + union tpm_cmd_header header; + union tpm_cmd_params params; +} __packed; + +struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *); + +int tpm_vendor_init(uint32_t dev_addr); + +void tpm_vendor_cleanup(struct tpm_chip *chip); + + +#endif diff --git a/qemu/roms/u-boot/drivers/tpm/tpm_tis_i2c.c b/qemu/roms/u-boot/drivers/tpm/tpm_tis_i2c.c new file mode 100644 index 000000000..2dd8501f9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/tpm/tpm_tis_i2c.c @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2011 Infineon Technologies + * + * Authors: + * Peter Huewe + * + * Description: + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This device driver implements the TPM interface as defined in + * the TCG TPM Interface Spec version 1.2, revision 1.0 and the + * Infineon I2C Protocol Stack Specification v0.20. + * + * It is based on the Linux kernel driver tpm.c from Leendert van + * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall. + * + * Version: 2.1.1 + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpm_private.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* Address of the TPM on the I2C bus */ +#define TPM_I2C_ADDR 0x20 + +/* Max buffer size supported by our tpm */ +#define TPM_DEV_BUFSIZE 1260 + +/* Max number of iterations after i2c NAK */ +#define MAX_COUNT 3 + +/* + * Max number of iterations after i2c NAK for 'long' commands + * + * We need this especially for sending TPM_READY, since the cleanup after the + * transtion to the ready state may take some time, but it is unpredictable + * how long it will take. + */ +#define MAX_COUNT_LONG 50 + +#define SLEEP_DURATION 60 /* in usec */ +#define SLEEP_DURATION_LONG 210 /* in usec */ + +#define TPM_HEADER_SIZE 10 + +/* + * Expected value for DIDVID register + * + * The only device the system knows about at this moment is Infineon slb9635. + */ +#define TPM_TIS_I2C_DID_VID 0x000b15d1L + +enum tis_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum tis_status { + TPM_STS_VALID = 0x80, + TPM_STS_COMMAND_READY = 0x40, + TPM_STS_GO = 0x20, + TPM_STS_DATA_AVAIL = 0x10, + TPM_STS_DATA_EXPECT = 0x08, +}; + +enum tis_defaults { + TIS_SHORT_TIMEOUT = 750, /* ms */ + TIS_LONG_TIMEOUT = 2000, /* ms */ +}; + +/* expected value for DIDVID register */ +#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L +#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L + +enum i2c_chip_type { + SLB9635, + SLB9645, + UNKNOWN, +}; + +static const char * const chip_name[] = { + [SLB9635] = "slb9635tt", + [SLB9645] = "slb9645tt", + [UNKNOWN] = "unknown/fallback to slb9635", +}; + +#define TPM_ACCESS(l) (0x0000 | ((l) << 4)) +#define TPM_STS(l) (0x0001 | ((l) << 4)) +#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) +#define TPM_DID_VID(l) (0x0006 | ((l) << 4)) + +/* Structure to store I2C TPM specific stuff */ +struct tpm_dev { + uint addr; + u8 buf[TPM_DEV_BUFSIZE + sizeof(u8)]; /* Max buffer size + addr */ + enum i2c_chip_type chip_type; +}; + +static struct tpm_dev tpm_dev = { + .addr = TPM_I2C_ADDR +}; + +static struct tpm_dev tpm_dev; + +/* + * iic_tpm_read() - read from TPM register + * @addr: register address to read from + * @buffer: provided by caller + * @len: number of bytes to read + * + * Read len bytes from TPM register and put them into + * buffer (little-endian format, i.e. first byte is put into buffer[0]). + * + * NOTE: TPM is big-endian for multi-byte values. Multi-byte + * values have to be swapped. + * + * Return -EIO on error, 0 on success. + */ +static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) +{ + int rc; + int count; + uint32_t addrbuf = addr; + + if ((tpm_dev.chip_type == SLB9635) || (tpm_dev.chip_type == UNKNOWN)) { + /* slb9635 protocol should work in both cases */ + for (count = 0; count < MAX_COUNT; count++) { + rc = i2c_write(tpm_dev.addr, 0, 0, + (uchar *)&addrbuf, 1); + if (rc == 0) + break; /* Success, break to skip sleep */ + udelay(SLEEP_DURATION); + } + if (rc) + return -rc; + + /* After the TPM has successfully received the register address + * it needs some time, thus we're sleeping here again, before + * retrieving the data + */ + for (count = 0; count < MAX_COUNT; count++) { + udelay(SLEEP_DURATION); + rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len); + if (rc == 0) + break; /* success, break to skip sleep */ + } + } else { + /* + * Use a combined read for newer chips. + * Unfortunately the smbus functions are not suitable due to + * the 32 byte limit of the smbus. + * Retries should usually not be needed, but are kept just to + * be safe on the safe side. + */ + for (count = 0; count < MAX_COUNT; count++) { + rc = i2c_read(tpm_dev.addr, addr, 1, buffer, len); + if (rc == 0) + break; /* break here to skip sleep */ + udelay(SLEEP_DURATION); + } + } + + /* Take care of 'guard time' */ + udelay(SLEEP_DURATION); + if (rc) + return -rc; + + return 0; +} + +static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, + unsigned int sleep_time, u8 max_count) +{ + int rc = 0; + int count; + + /* Prepare send buffer */ + tpm_dev.buf[0] = addr; + memcpy(&(tpm_dev.buf[1]), buffer, len); + + for (count = 0; count < max_count; count++) { + rc = i2c_write(tpm_dev.addr, 0, 0, tpm_dev.buf, len + 1); + if (rc == 0) + break; /* Success, break to skip sleep */ + udelay(sleep_time); + } + + /* take care of 'guard time' */ + udelay(SLEEP_DURATION); + if (rc) + return -rc; + + return 0; +} + +/* + * iic_tpm_write() - write to TPM register + * @addr: register address to write to + * @buffer: containing data to be written + * @len: number of bytes to write + * + * Write len bytes from provided buffer to TPM register (little + * endian format, i.e. buffer[0] is written as first byte). + * + * NOTE: TPM is big-endian for multi-byte values. Multi-byte + * values have to be swapped. + * + * NOTE: use this function instead of the iic_tpm_write_generic function. + * + * Return -EIO on error, 0 on success + */ +static int iic_tpm_write(u8 addr, u8 *buffer, size_t len) +{ + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION, + MAX_COUNT); +} + +/* + * This function is needed especially for the cleanup situation after + * sending TPM_READY + */ +static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len) +{ + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG, + MAX_COUNT_LONG); +} + +static int check_locality(struct tpm_chip *chip, int loc) +{ + const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID; + u8 buf; + int rc; + + rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1); + if (rc < 0) + return rc; + + if ((buf & mask) == mask) { + chip->vendor.locality = loc; + return loc; + } + + return -1; +} + +static void release_locality(struct tpm_chip *chip, int loc, int force) +{ + const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID; + u8 buf; + + if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0) + return; + + if (force || (buf & mask) == mask) { + buf = TPM_ACCESS_ACTIVE_LOCALITY; + iic_tpm_write(TPM_ACCESS(loc), &buf, 1); + } +} + +static int request_locality(struct tpm_chip *chip, int loc) +{ + unsigned long start, stop; + u8 buf = TPM_ACCESS_REQUEST_USE; + + if (check_locality(chip, loc) >= 0) + return loc; /* We already have the locality */ + + iic_tpm_write(TPM_ACCESS(loc), &buf, 1); + + /* Wait for burstcount */ + start = get_timer(0); + stop = chip->vendor.timeout_a; + do { + if (check_locality(chip, loc) >= 0) + return loc; + udelay(TPM_TIMEOUT * 1000); + } while (get_timer(start) < stop); + + return -1; +} + +static u8 tpm_tis_i2c_status(struct tpm_chip *chip) +{ + /* NOTE: Since i2c read may fail, return 0 in this case --> time-out */ + u8 buf; + + if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0) + return 0; + else + return buf; +} + +static void tpm_tis_i2c_ready(struct tpm_chip *chip) +{ + /* This causes the current command to be aborted */ + u8 buf = TPM_STS_COMMAND_READY; + + iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1); +} + +static ssize_t get_burstcount(struct tpm_chip *chip) +{ + unsigned long start, stop; + ssize_t burstcnt; + u8 addr, buf[3]; + + /* Wait for burstcount */ + /* XXX: Which timeout value? Spec has 2 answers (c & d) */ + start = get_timer(0); + stop = chip->vendor.timeout_d; + do { + /* Note: STS is little endian */ + addr = TPM_STS(chip->vendor.locality) + 1; + if (iic_tpm_read(addr, buf, 3) < 0) + burstcnt = 0; + else + burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0]; + + if (burstcnt) + return burstcnt; + udelay(TPM_TIMEOUT * 1000); + } while (get_timer(start) < stop); + + return -EBUSY; +} + +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, + int *status) +{ + unsigned long start, stop; + + /* Check current status */ + *status = tpm_tis_i2c_status(chip); + if ((*status & mask) == mask) + return 0; + + start = get_timer(0); + stop = timeout; + do { + udelay(TPM_TIMEOUT * 1000); + *status = tpm_tis_i2c_status(chip); + if ((*status & mask) == mask) + return 0; + } while (get_timer(start) < stop); + + return -ETIME; +} + +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +{ + size_t size = 0; + ssize_t burstcnt; + int rc; + + while (size < count) { + burstcnt = get_burstcount(chip); + + /* burstcount < 0 -> tpm is busy */ + if (burstcnt < 0) + return burstcnt; + + /* Limit received data to max left */ + if (burstcnt > (count - size)) + burstcnt = count - size; + + rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[size]), burstcnt); + if (rc == 0) + size += burstcnt; + } + + return size; +} + +static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + int size = 0; + int expected, status; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + /* Read first 10 bytes, including tag, paramsize, and result */ + size = recv_data(chip, buf, TPM_HEADER_SIZE); + if (size < TPM_HEADER_SIZE) { + error("Unable to read header\n"); + goto out; + } + + expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE); + if ((size_t)expected > count) { + size = -EIO; + goto out; + } + + size += recv_data(chip, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE); + if (size < expected) { + error("Unable to read remainder of result\n"); + size = -ETIME; + goto out; + } + + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + if (status & TPM_STS_DATA_AVAIL) { /* Retry? */ + error("Error left over data\n"); + size = -EIO; + goto out; + } + +out: + tpm_tis_i2c_ready(chip); + /* + * The TPM needs some time to clean up here, + * so we sleep rather than keeping the bus busy + */ + udelay(2000); + release_locality(chip, chip->vendor.locality, 0); + + return size; +} + +static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + int rc, status; + ssize_t burstcnt; + size_t count = 0; + int retry = 0; + u8 sts = TPM_STS_GO; + + if (len > TPM_DEV_BUFSIZE) + return -E2BIG; /* Command is too long for our tpm, sorry */ + + if (request_locality(chip, 0) < 0) + return -EBUSY; + + status = tpm_tis_i2c_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + tpm_tis_i2c_ready(chip); + if (wait_for_stat(chip, TPM_STS_COMMAND_READY, + chip->vendor.timeout_b, &status) < 0) { + rc = -ETIME; + goto out_err; + } + } + + burstcnt = get_burstcount(chip); + + /* burstcount < 0 -> tpm is busy */ + if (burstcnt < 0) + return burstcnt; + + while (count < len - 1) { + if (burstcnt > len - 1 - count) + burstcnt = len - 1 - count; + +#ifdef CONFIG_TPM_TIS_I2C_BURST_LIMITATION + if (retry && burstcnt > CONFIG_TPM_TIS_I2C_BURST_LIMITATION) + burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION; +#endif /* CONFIG_TPM_TIS_I2C_BURST_LIMITATION */ + + rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[count]), burstcnt); + if (rc == 0) + count += burstcnt; + else { + retry++; + wait_for_stat(chip, TPM_STS_VALID, + chip->vendor.timeout_c, &status); + + if ((status & TPM_STS_DATA_EXPECT) == 0) { + rc = -EIO; + goto out_err; + } + } + } + + /* Write last byte */ + iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1); + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + rc = -EIO; + goto out_err; + } + + /* Go and do it */ + iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1); + + return len; + +out_err: + tpm_tis_i2c_ready(chip); + /* + * The TPM needs some time to clean up here, + * so we sleep rather than keeping the bus busy + */ + udelay(2000); + release_locality(chip, chip->vendor.locality, 0); + + return rc; +} + +static struct tpm_vendor_specific tpm_tis_i2c = { + .status = tpm_tis_i2c_status, + .recv = tpm_tis_i2c_recv, + .send = tpm_tis_i2c_send, + .cancel = tpm_tis_i2c_ready, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = TPM_STS_COMMAND_READY, +}; + + +static enum i2c_chip_type tpm_vendor_chip_type(void) +{ +#ifdef CONFIG_OF_CONTROL + const void *blob = gd->fdt_blob; + + if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9645_TPM) >= 0) + return SLB9645; + + if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM) >= 0) + return SLB9635; +#endif + return UNKNOWN; +} + +/* Initialisation of i2c tpm */ +int tpm_vendor_init(uint32_t dev_addr) +{ + u32 vendor; + u32 expected_did_vid; + uint old_addr; + int rc = 0; + struct tpm_chip *chip; + + old_addr = tpm_dev.addr; + if (dev_addr != 0) + tpm_dev.addr = dev_addr; + + tpm_dev.chip_type = tpm_vendor_chip_type(); + + chip = tpm_register_hardware(&tpm_tis_i2c); + if (chip < 0) { + rc = -ENODEV; + goto out_err; + } + + /* Disable interrupts (not supported) */ + chip->vendor.irq = 0; + + /* Default timeouts */ + chip->vendor.timeout_a = TIS_SHORT_TIMEOUT; + chip->vendor.timeout_b = TIS_LONG_TIMEOUT; + chip->vendor.timeout_c = TIS_SHORT_TIMEOUT; + chip->vendor.timeout_d = TIS_SHORT_TIMEOUT; + + if (request_locality(chip, 0) < 0) { + rc = -ENODEV; + goto out_err; + } + + /* Read four bytes from DID_VID register */ + if (iic_tpm_read(TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) { + rc = -EIO; + goto out_release; + } + + if (tpm_dev.chip_type == SLB9635) { + vendor = be32_to_cpu(vendor); + expected_did_vid = TPM_TIS_I2C_DID_VID_9635; + } else { + /* device id and byte order has changed for newer i2c tpms */ + expected_did_vid = TPM_TIS_I2C_DID_VID_9645; + } + + if (tpm_dev.chip_type != UNKNOWN && vendor != expected_did_vid) { + error("Vendor id did not match! ID was %08x\n", vendor); + rc = -ENODEV; + goto out_release; + } + + debug("1.2 TPM (chip type %s device-id 0x%X)\n", + chip_name[tpm_dev.chip_type], vendor >> 16); + + /* + * A timeout query to TPM can be placed here. + * Standard timeout values are used so far + */ + + return 0; + +out_release: + release_locality(chip, 0, 1); + +out_err: + tpm_dev.addr = old_addr; + return rc; +} + +void tpm_vendor_cleanup(struct tpm_chip *chip) +{ + release_locality(chip, chip->vendor.locality, 1); +} diff --git a/qemu/roms/u-boot/drivers/tpm/tpm_tis_lpc.c b/qemu/roms/u-boot/drivers/tpm/tpm_tis_lpc.c new file mode 100644 index 000000000..eecf18cbf --- /dev/null +++ b/qemu/roms/u-boot/drivers/tpm/tpm_tis_lpc.c @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * The code in this file is based on the article "Writing a TPM Device Driver" + * published on http://ptgmedia.pearsoncmg.com. + * + * One principal difference is that in the simplest config the other than 0 + * TPM localities do not get mapped by some devices (for instance, by Infineon + * slb9635), so this driver provides access to locality 0 only. + */ + +#include +#include +#include + +#define PREFIX "lpc_tpm: " + +struct tpm_locality { + u32 access; + u8 padding0[4]; + u32 int_enable; + u8 vector; + u8 padding1[3]; + u32 int_status; + u32 int_capability; + u32 tpm_status; + u8 padding2[8]; + u8 data; + u8 padding3[3803]; + u32 did_vid; + u8 rid; + u8 padding4[251]; +}; + +/* + * This pointer refers to the TPM chip, 5 of its localities are mapped as an + * array. + */ +#define TPM_TOTAL_LOCALITIES 5 +static struct tpm_locality *lpc_tpm_dev = + (struct tpm_locality *)CONFIG_TPM_TIS_BASE_ADDRESS; + +/* Some registers' bit field definitions */ +#define TIS_STS_VALID (1 << 7) /* 0x80 */ +#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */ +#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */ +#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */ +#define TIS_STS_EXPECT (1 << 3) /* 0x08 */ +#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */ + +#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */ +#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */ +#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */ +#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */ +#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */ +#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */ +#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */ + +#define TIS_STS_BURST_COUNT_MASK (0xffff) +#define TIS_STS_BURST_COUNT_SHIFT (8) + +/* + * Error value returned if a tpm register does not enter the expected state + * after continuous polling. No actual TPM register reading ever returns -1, + * so this value is a safe error indication to be mixed with possible status + * register values. + */ +#define TPM_TIMEOUT_ERR (-1) + +/* Error value returned on various TPM driver errors. */ +#define TPM_DRIVER_ERR (1) + + /* 1 second is plenty for anything TPM does. */ +#define MAX_DELAY_US (1000 * 1000) + +/* Retrieve burst count value out of the status register contents. */ +static u16 burst_count(u32 status) +{ + return (status >> TIS_STS_BURST_COUNT_SHIFT) & TIS_STS_BURST_COUNT_MASK; +} + +/* + * Structures defined below allow creating descriptions of TPM vendor/device + * ID information for run time discovery. The only device the system knows + * about at this time is Infineon slb9635. + */ +struct device_name { + u16 dev_id; + const char * const dev_name; +}; + +struct vendor_name { + u16 vendor_id; + const char *vendor_name; + const struct device_name *dev_names; +}; + +static const struct device_name infineon_devices[] = { + {0xb, "SLB9635 TT 1.2"}, + {0} +}; + +static const struct vendor_name vendor_names[] = { + {0x15d1, "Infineon", infineon_devices}, +}; + +/* + * Cached vendor/device ID pair to indicate that the device has been already + * discovered. + */ +static u32 vendor_dev_id; + +/* TPM access wrappers to support tracing */ +static u8 tpm_read_byte(const u8 *ptr) +{ + u8 ret = readb(ptr); + debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n", + (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret); + return ret; +} + +static u32 tpm_read_word(const u32 *ptr) +{ + u32 ret = readl(ptr); + debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n", + (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret); + return ret; +} + +static void tpm_write_byte(u8 value, u8 *ptr) +{ + debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n", + (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value); + writeb(value, ptr); +} + +static void tpm_write_word(u32 value, u32 *ptr) +{ + debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n", + (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value); + writel(value, ptr); +} + +/* + * tis_wait_reg() + * + * Wait for at least a second for a register to change its state to match the + * expected state. Normally the transition happens within microseconds. + * + * @reg - pointer to the TPM register + * @mask - bitmask for the bitfield(s) to watch + * @expected - value the field(s) are supposed to be set to + * + * Returns the register contents in case the expected value was found in the + * appropriate register bits, or TPM_TIMEOUT_ERR on timeout. + */ +static u32 tis_wait_reg(u32 *reg, u8 mask, u8 expected) +{ + u32 time_us = MAX_DELAY_US; + + while (time_us > 0) { + u32 value = tpm_read_word(reg); + if ((value & mask) == expected) + return value; + udelay(1); /* 1 us */ + time_us--; + } + return TPM_TIMEOUT_ERR; +} + +/* + * Probe the TPM device and try determining its manufacturer/device name. + * + * Returns 0 on success (the device is found or was found during an earlier + * invocation) or TPM_DRIVER_ERR if the device is not found. + */ +int tis_init(void) +{ + u32 didvid = tpm_read_word(&lpc_tpm_dev[0].did_vid); + int i; + const char *device_name = "unknown"; + const char *vendor_name = device_name; + u16 vid, did; + + if (vendor_dev_id) + return 0; /* Already probed. */ + + if (!didvid || (didvid == 0xffffffff)) { + printf("%s: No TPM device found\n", __func__); + return TPM_DRIVER_ERR; + } + + vendor_dev_id = didvid; + + vid = didvid & 0xffff; + did = (didvid >> 16) & 0xffff; + for (i = 0; i < ARRAY_SIZE(vendor_names); i++) { + int j = 0; + u16 known_did; + + if (vid == vendor_names[i].vendor_id) + vendor_name = vendor_names[i].vendor_name; + + while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) { + if (known_did == did) { + device_name = + vendor_names[i].dev_names[j].dev_name; + break; + } + j++; + } + break; + } + + printf("Found TPM %s by %s\n", device_name, vendor_name); + return 0; +} + +/* + * tis_senddata() + * + * send the passed in data to the TPM device. + * + * @data - address of the data to send, byte by byte + * @len - length of the data to send + * + * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does + * not accept the entire command). + */ +static u32 tis_senddata(const u8 * const data, u32 len) +{ + u32 offset = 0; + u16 burst = 0; + u32 max_cycles = 0; + u8 locality = 0; + u32 value; + + value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY); + if (value == TPM_TIMEOUT_ERR) { + printf("%s:%d - failed to get 'command_ready' status\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + burst = burst_count(value); + + while (1) { + unsigned count; + + /* Wait till the device is ready to accept more data. */ + while (!burst) { + if (max_cycles++ == MAX_DELAY_US) { + printf("%s:%d failed to feed %d bytes of %d\n", + __FILE__, __LINE__, len - offset, len); + return TPM_DRIVER_ERR; + } + udelay(1); + burst = burst_count(tpm_read_word(&lpc_tpm_dev + [locality].tpm_status)); + } + + max_cycles = 0; + + /* + * Calculate number of bytes the TPM is ready to accept in one + * shot. + * + * We want to send the last byte outside of the loop (hence + * the -1 below) to make sure that the 'expected' status bit + * changes to zero exactly after the last byte is fed into the + * FIFO. + */ + count = min(burst, len - offset - 1); + while (count--) + tpm_write_byte(data[offset++], + &lpc_tpm_dev[locality].data); + + value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + TIS_STS_VALID, TIS_STS_VALID); + + if ((value == TPM_TIMEOUT_ERR) || !(value & TIS_STS_EXPECT)) { + printf("%s:%d TPM command feed overflow\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + burst = burst_count(value); + if ((offset == (len - 1)) && burst) { + /* + * We need to be able to send the last byte to the + * device, so burst size must be nonzero before we + * break out. + */ + break; + } + } + + /* Send the last byte. */ + tpm_write_byte(data[offset++], &lpc_tpm_dev[locality].data); + /* + * Verify that TPM does not expect any more data as part of this + * command. + */ + value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + TIS_STS_VALID, TIS_STS_VALID); + if ((value == TPM_TIMEOUT_ERR) || (value & TIS_STS_EXPECT)) { + printf("%s:%d unexpected TPM status 0x%x\n", + __FILE__, __LINE__, value); + return TPM_DRIVER_ERR; + } + + /* OK, sitting pretty, let's start the command execution. */ + tpm_write_word(TIS_STS_TPM_GO, &lpc_tpm_dev[locality].tpm_status); + return 0; +} + +/* + * tis_readresponse() + * + * read the TPM device response after a command was issued. + * + * @buffer - address where to read the response, byte by byte. + * @len - pointer to the size of buffer + * + * On success stores the number of received bytes to len and returns 0. On + * errors (misformatted TPM data or synchronization problems) returns + * TPM_DRIVER_ERR. + */ +static u32 tis_readresponse(u8 *buffer, u32 *len) +{ + u16 burst; + u32 value; + u32 offset = 0; + u8 locality = 0; + const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID; + u32 expected_count = *len; + int max_cycles = 0; + + /* Wait for the TPM to process the command. */ + value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + has_data, has_data); + if (value == TPM_TIMEOUT_ERR) { + printf("%s:%d failed processing command\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + do { + while ((burst = burst_count(value)) == 0) { + if (max_cycles++ == MAX_DELAY_US) { + printf("%s:%d TPM stuck on read\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + udelay(1); + value = tpm_read_word(&lpc_tpm_dev + [locality].tpm_status); + } + + max_cycles = 0; + + while (burst-- && (offset < expected_count)) { + buffer[offset++] = tpm_read_byte(&lpc_tpm_dev + [locality].data); + + if (offset == 6) { + /* + * We got the first six bytes of the reply, + * let's figure out how many bytes to expect + * total - it is stored as a 4 byte number in + * network order, starting with offset 2 into + * the body of the reply. + */ + u32 real_length; + memcpy(&real_length, + buffer + 2, + sizeof(real_length)); + expected_count = be32_to_cpu(real_length); + + if ((expected_count < offset) || + (expected_count > *len)) { + printf("%s:%d bad response size %d\n", + __FILE__, __LINE__, + expected_count); + return TPM_DRIVER_ERR; + } + } + } + + /* Wait for the next portion. */ + value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status, + TIS_STS_VALID, TIS_STS_VALID); + if (value == TPM_TIMEOUT_ERR) { + printf("%s:%d failed to read response\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + if (offset == expected_count) + break; /* We got all we needed. */ + + } while ((value & has_data) == has_data); + + /* + * Make sure we indeed read all there was. The TIS_STS_VALID bit is + * known to be set. + */ + if (value & TIS_STS_DATA_AVAILABLE) { + printf("%s:%d wrong receive status %x\n", + __FILE__, __LINE__, value); + return TPM_DRIVER_ERR; + } + + /* Tell the TPM that we are done. */ + tpm_write_word(TIS_STS_COMMAND_READY, &lpc_tpm_dev + [locality].tpm_status); + *len = offset; + return 0; +} + +int tis_open(void) +{ + u8 locality = 0; /* we use locality zero for everything. */ + + if (tis_close()) + return TPM_DRIVER_ERR; + + /* now request access to locality. */ + tpm_write_word(TIS_ACCESS_REQUEST_USE, &lpc_tpm_dev[locality].access); + + /* did we get a lock? */ + if (tis_wait_reg(&lpc_tpm_dev[locality].access, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY) == TPM_TIMEOUT_ERR) { + printf("%s:%d - failed to lock locality %d\n", + __FILE__, __LINE__, locality); + return TPM_DRIVER_ERR; + } + + tpm_write_word(TIS_STS_COMMAND_READY, + &lpc_tpm_dev[locality].tpm_status); + return 0; +} + +int tis_close(void) +{ + u8 locality = 0; + + if (tpm_read_word(&lpc_tpm_dev[locality].access) & + TIS_ACCESS_ACTIVE_LOCALITY) { + tpm_write_word(TIS_ACCESS_ACTIVE_LOCALITY, + &lpc_tpm_dev[locality].access); + + if (tis_wait_reg(&lpc_tpm_dev[locality].access, + TIS_ACCESS_ACTIVE_LOCALITY, 0) == + TPM_TIMEOUT_ERR) { + printf("%s:%d - failed to release locality %d\n", + __FILE__, __LINE__, locality); + return TPM_DRIVER_ERR; + } + } + return 0; +} + +int tis_sendrecv(const u8 *sendbuf, size_t send_size, + u8 *recvbuf, size_t *recv_len) +{ + if (tis_senddata(sendbuf, send_size)) { + printf("%s:%d failed sending data to TPM\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + return tis_readresponse(recvbuf, (u32 *)recv_len); +} diff --git a/qemu/roms/u-boot/drivers/tpm/tpm_tis_sandbox.c b/qemu/roms/u-boot/drivers/tpm/tpm_tis_sandbox.c new file mode 100644 index 000000000..ed4b03912 --- /dev/null +++ b/qemu/roms/u-boot/drivers/tpm/tpm_tis_sandbox.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* TPM NVRAM location indices. */ +#define FIRMWARE_NV_INDEX 0x1007 +#define KERNEL_NV_INDEX 0x1008 + +#define NV_DATA_PUBLIC_PERMISSIONS_OFFSET 60 + +/* Kernel TPM space - KERNEL_NV_INDEX, locked with physical presence */ +#define ROLLBACK_SPACE_KERNEL_VERSION 2 +#define ROLLBACK_SPACE_KERNEL_UID 0x4752574C /* 'GRWL' */ + +struct rollback_space_kernel { + /* Struct version, for backwards compatibility */ + uint8_t struct_version; + /* Unique ID to detect space redefinition */ + uint32_t uid; + /* Kernel versions */ + uint32_t kernel_versions; + /* Reserved for future expansion */ + uint8_t reserved[3]; + /* Checksum (v2 and later only) */ + uint8_t crc8; +} __packed rollback_space_kernel; + +/* + * These numbers derive from adding the sizes of command fields as shown in + * the TPM commands manual. + */ +#define TPM_REQUEST_HEADER_LENGTH 10 +#define TPM_RESPONSE_HEADER_LENGTH 10 + +/* These are the different non-volatile spaces that we emulate */ +enum { + NV_GLOBAL_LOCK, + NV_SEQ_FIRMWARE, + NV_SEQ_KERNEL, + NV_SEQ_COUNT, +}; + +/* Size of each non-volatile space */ +#define NV_DATA_SIZE 0x20 + +/* + * Information about our TPM emulation. This is preserved in the sandbox + * state file if enabled. + */ +static struct tpm_state { + uint8_t nvdata[NV_SEQ_COUNT][NV_DATA_SIZE]; +} state; + +/** + * sandbox_tpm_read_state() - read the sandbox EC state from the state file + * + * If data is available, then blob and node will provide access to it. If + * not this function sets up an empty TPM. + * + * @blob: Pointer to device tree blob, or NULL if no data to read + * @node: Node offset to read from + */ +static int sandbox_tpm_read_state(const void *blob, int node) +{ + const char *prop; + int len; + int i; + + if (!blob) + return 0; + + for (i = 0; i < NV_SEQ_COUNT; i++) { + char prop_name[20]; + + sprintf(prop_name, "nvdata%d", i); + prop = fdt_getprop(blob, node, prop_name, &len); + if (prop && len == NV_DATA_SIZE) + memcpy(state.nvdata[i], prop, NV_DATA_SIZE); + } + + return 0; +} + +/** + * cros_ec_write_state() - Write out our state to the state file + * + * The caller will ensure that there is a node ready for the state. The node + * may already contain the old state, in which case it is overridden. + * + * @blob: Device tree blob holding state + * @node: Node to write our state into + */ +static int sandbox_tpm_write_state(void *blob, int node) +{ + int i; + + /* + * We are guaranteed enough space to write basic properties. + * We could use fdt_add_subnode() to put each set of data in its + * own node - perhaps useful if we add access informaiton to each. + */ + for (i = 0; i < NV_SEQ_COUNT; i++) { + char prop_name[20]; + + sprintf(prop_name, "nvdata%d", i); + fdt_setprop(blob, node, prop_name, state.nvdata[i], + NV_DATA_SIZE); + } + + return 0; +} + +SANDBOX_STATE_IO(sandbox_tpm, "google,sandbox-tpm", sandbox_tpm_read_state, + sandbox_tpm_write_state); + +static int index_to_seq(uint32_t index) +{ + switch (index) { + case FIRMWARE_NV_INDEX: + return NV_SEQ_FIRMWARE; + case KERNEL_NV_INDEX: + return NV_SEQ_KERNEL; + case 0: + return NV_GLOBAL_LOCK; + } + + printf("Invalid nv index %#x\n", index); + return -1; +} + +int tis_sendrecv(const u8 *sendbuf, size_t send_size, + u8 *recvbuf, size_t *recv_len) +{ + struct tpm_state *tpm = &state; + uint32_t code, index, length, type; + uint8_t *data; + int seq; + + code = get_unaligned_be32(sendbuf + sizeof(uint16_t) + + sizeof(uint32_t)); + printf("tpm: %zd bytes, recv_len %zd, cmd = %x\n", send_size, + *recv_len, code); + print_buffer(0, sendbuf, 1, send_size, 0); + switch (code) { + case 0x65: /* get flags */ + type = get_unaligned_be32(sendbuf + 14); + switch (type) { + case 4: + index = get_unaligned_be32(sendbuf + 18); + printf("Get flags index %#02x\n", index); + *recv_len = 22; + memset(recvbuf, '\0', *recv_len); + put_unaligned_be32(22, recvbuf + + TPM_RESPONSE_HEADER_LENGTH); + data = recvbuf + TPM_RESPONSE_HEADER_LENGTH + + sizeof(uint32_t); + switch (index) { + case FIRMWARE_NV_INDEX: + break; + case KERNEL_NV_INDEX: + /* TPM_NV_PER_PPWRITE */ + put_unaligned_be32(1, data + + NV_DATA_PUBLIC_PERMISSIONS_OFFSET); + break; + } + break; + case 0x11: /* TPM_CAP_NV_INDEX */ + index = get_unaligned_be32(sendbuf + 18); + printf("Get cap nv index %#02x\n", index); + put_unaligned_be32(22, recvbuf + + TPM_RESPONSE_HEADER_LENGTH); + break; + default: + printf(" ** Unknown 0x65 command type %#02x\n", + type); + return -1; + } + break; + case 0xcd: /* nvwrite */ + index = get_unaligned_be32(sendbuf + 10); + length = get_unaligned_be32(sendbuf + 18); + seq = index_to_seq(index); + if (seq < 0) + return -1; + printf("tpm: nvwrite index=%#02x, len=%#02x\n", index, length); + memcpy(&tpm->nvdata[seq], sendbuf + 22, length); + *recv_len = 12; + memset(recvbuf, '\0', *recv_len); + break; + case 0xcf: /* nvread */ + index = get_unaligned_be32(sendbuf + 10); + length = get_unaligned_be32(sendbuf + 18); + seq = index_to_seq(index); + if (seq < 0) + return -1; + printf("tpm: nvread index=%#02x, len=%#02x\n", index, length); + *recv_len = TPM_RESPONSE_HEADER_LENGTH + sizeof(uint32_t) + + length; + memset(recvbuf, '\0', *recv_len); + put_unaligned_be32(length, recvbuf + + TPM_RESPONSE_HEADER_LENGTH); + if (seq == NV_SEQ_KERNEL) { + struct rollback_space_kernel rsk; + + data = recvbuf + TPM_RESPONSE_HEADER_LENGTH + + sizeof(uint32_t); + rsk.struct_version = 2; + rsk.uid = ROLLBACK_SPACE_KERNEL_UID; + rsk.kernel_versions = 0; + rsk.crc8 = crc8((unsigned char *)&rsk, + offsetof(struct rollback_space_kernel, + crc8)); + memcpy(data, &rsk, sizeof(rsk)); + } else { + memcpy(recvbuf + TPM_RESPONSE_HEADER_LENGTH + + sizeof(uint32_t), &tpm->nvdata[seq], length); + } + break; + case 0x14: /* tpm extend */ + case 0x15: /* pcr read */ + case 0x5d: /* force clear */ + case 0x6f: /* physical enable */ + case 0x72: /* physical set deactivated */ + case 0x99: /* startup */ + case 0x4000000a: /* assert physical presence */ + *recv_len = 12; + memset(recvbuf, '\0', *recv_len); + break; + default: + printf("Unknown tpm command %02x\n", code); + return -1; + } + + return 0; +} + +int tis_open(void) +{ + printf("%s\n", __func__); + return 0; +} + +int tis_close(void) +{ + printf("%s\n", __func__); + return 0; +} + +int tis_init(void) +{ + printf("%s\n", __func__); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/twserial/Makefile b/qemu/roms/u-boot/drivers/twserial/Makefile new file mode 100644 index 000000000..7cc7c4de8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/twserial/Makefile @@ -0,0 +1,8 @@ +# +# (C) Copyright 2009 +# Detlev Zundel, DENX Software Engineering, dzu@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_SOFT_TWS) += soft_tws.o diff --git a/qemu/roms/u-boot/drivers/twserial/soft_tws.c b/qemu/roms/u-boot/drivers/twserial/soft_tws.c new file mode 100644 index 000000000..d0bf93d90 --- /dev/null +++ b/qemu/roms/u-boot/drivers/twserial/soft_tws.c @@ -0,0 +1,94 @@ +/* + * (C) Copyright 2009 + * Detlev Zundel, DENX Software Engineering, dzu@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define TWS_IMPLEMENTATION +#include + +/*=====================================================================*/ +/* Public Functions */ +/*=====================================================================*/ + +/*----------------------------------------------------------------------- + * Read bits + */ +int tws_read(uchar *buffer, int len) +{ + int rem = len; + uchar accu, shift; + + debug("tws_read: buffer %p len %d\n", buffer, len); + + /* Configure the data pin for input */ + tws_data_config_output(0); + + /* Disable WR, i.e. setup a read */ + tws_wr(0); + udelay(1); + + /* Rise CE */ + tws_ce(1); + udelay(1); + + for (; rem > 0; ) { + for (shift = 0, accu = 0; + (rem > 0) && (shift < 8); + rem--, shift++) { + tws_clk(1); + udelay(10); + accu |= (tws_data_read() << shift); /* LSB first */ + tws_clk(0); + udelay(10); + } + *buffer++ = accu; + } + + /* Lower CE */ + tws_ce(0); + + return len - rem; +} + + +/*----------------------------------------------------------------------- + * Write bits + */ +int tws_write(uchar *buffer, int len) +{ + int rem = len; + uchar accu, shift; + + debug("tws_write: buffer %p len %d\n", buffer, len); + + /* Configure the data pin for output */ + tws_data_config_output(1); + + /* Enable WR, i.e. setup a write */ + tws_wr(1); + udelay(1); + + /* Rise CE */ + tws_ce(1); + udelay(1); + + for (; rem > 0; ) { + for (shift = 0, accu = *buffer++; + (rem > 0) && (shift < 8); + rem--, shift++) { + tws_data(accu & 0x01); /* LSB first */ + tws_clk(1); + udelay(10); + tws_clk(0); + udelay(10); + accu >>= 1; + } + } + + /* Lower CE */ + tws_ce(0); + + return len - rem; +} diff --git a/qemu/roms/u-boot/drivers/usb/eth/Makefile b/qemu/roms/u-boot/drivers/usb/eth/Makefile new file mode 100644 index 000000000..94551c4c0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/eth/Makefile @@ -0,0 +1,12 @@ +# +# Copyright (c) 2011 The Chromium OS Authors. +# SPDX-License-Identifier: GPL-2.0+ +# + +# new USB host ethernet layer dependencies +obj-$(CONFIG_USB_HOST_ETHER) += usb_ether.o +ifdef CONFIG_USB_ETHER_ASIX +obj-y += asix.o +endif +obj-$(CONFIG_USB_ETHER_MCS7830) += mcs7830.o +obj-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o diff --git a/qemu/roms/u-boot/drivers/usb/eth/asix.c b/qemu/roms/u-boot/drivers/usb/eth/asix.c new file mode 100644 index 000000000..ce133f006 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/eth/asix.c @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "usb_ether.h" +#include + + +/* ASIX AX8817X based USB 2.0 Ethernet Devices */ + +#define AX_CMD_SET_SW_MII 0x06 +#define AX_CMD_READ_MII_REG 0x07 +#define AX_CMD_WRITE_MII_REG 0x08 +#define AX_CMD_SET_HW_MII 0x0a +#define AX_CMD_READ_EEPROM 0x0b +#define AX_CMD_READ_RX_CTL 0x0f +#define AX_CMD_WRITE_RX_CTL 0x10 +#define AX_CMD_WRITE_IPG0 0x12 +#define AX_CMD_READ_NODE_ID 0x13 +#define AX_CMD_WRITE_NODE_ID 0x14 +#define AX_CMD_READ_PHY_ID 0x19 +#define AX_CMD_WRITE_MEDIUM_MODE 0x1b +#define AX_CMD_WRITE_GPIOS 0x1f +#define AX_CMD_SW_RESET 0x20 +#define AX_CMD_SW_PHY_SELECT 0x22 + +#define AX_SWRESET_CLEAR 0x00 +#define AX_SWRESET_PRTE 0x04 +#define AX_SWRESET_PRL 0x08 +#define AX_SWRESET_IPRL 0x20 +#define AX_SWRESET_IPPD 0x40 + +#define AX88772_IPG0_DEFAULT 0x15 +#define AX88772_IPG1_DEFAULT 0x0c +#define AX88772_IPG2_DEFAULT 0x12 + +/* AX88772 & AX88178 Medium Mode Register */ +#define AX_MEDIUM_PF 0x0080 +#define AX_MEDIUM_JFE 0x0040 +#define AX_MEDIUM_TFC 0x0020 +#define AX_MEDIUM_RFC 0x0010 +#define AX_MEDIUM_ENCK 0x0008 +#define AX_MEDIUM_AC 0x0004 +#define AX_MEDIUM_FD 0x0002 +#define AX_MEDIUM_GM 0x0001 +#define AX_MEDIUM_SM 0x1000 +#define AX_MEDIUM_SBP 0x0800 +#define AX_MEDIUM_PS 0x0200 +#define AX_MEDIUM_RE 0x0100 + +#define AX88178_MEDIUM_DEFAULT \ + (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \ + AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \ + AX_MEDIUM_RE) + +#define AX88772_MEDIUM_DEFAULT \ + (AX_MEDIUM_FD | AX_MEDIUM_RFC | \ + AX_MEDIUM_TFC | AX_MEDIUM_PS | \ + AX_MEDIUM_AC | AX_MEDIUM_RE) + +/* AX88772 & AX88178 RX_CTL values */ +#define AX_RX_CTL_SO 0x0080 +#define AX_RX_CTL_AB 0x0008 + +#define AX_DEFAULT_RX_CTL \ + (AX_RX_CTL_SO | AX_RX_CTL_AB) + +/* GPIO 2 toggles */ +#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */ +#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */ +#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */ + +/* local defines */ +#define ASIX_BASE_NAME "asx" +#define USB_CTRL_SET_TIMEOUT 5000 +#define USB_CTRL_GET_TIMEOUT 5000 +#define USB_BULK_SEND_TIMEOUT 5000 +#define USB_BULK_RECV_TIMEOUT 5000 + +#define AX_RX_URB_SIZE 2048 +#define PHY_CONNECT_TIMEOUT 5000 + +/* asix_flags defines */ +#define FLAG_NONE 0 +#define FLAG_TYPE_AX88172 (1U << 0) +#define FLAG_TYPE_AX88772 (1U << 1) +#define FLAG_TYPE_AX88772B (1U << 2) +#define FLAG_EEPROM_MAC (1U << 3) /* initial mac address in eeprom */ + +/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */ + +/* driver private */ +struct asix_private { + int flags; +}; + +/* + * Asix infrastructure commands + */ +static int asix_write_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + + debug("asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x " + "size=%d\n", cmd, value, index, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_sndctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + data, + size, + USB_CTRL_SET_TIMEOUT); + + return len == size ? 0 : -1; +} + +static int asix_read_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + + debug("asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", + cmd, value, index, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_rcvctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + data, + size, + USB_CTRL_GET_TIMEOUT); + return len == size ? 0 : -1; +} + +static inline int asix_set_sw_mii(struct ueth_data *dev) +{ + int ret; + + ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); + if (ret < 0) + debug("Failed to enable software MII access\n"); + return ret; +} + +static inline int asix_set_hw_mii(struct ueth_data *dev) +{ + int ret; + + ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); + if (ret < 0) + debug("Failed to enable hardware MII access\n"); + return ret; +} + +static int asix_mdio_read(struct ueth_data *dev, int phy_id, int loc) +{ + ALLOC_CACHE_ALIGN_BUFFER(__le16, res, 1); + + asix_set_sw_mii(dev); + asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, res); + asix_set_hw_mii(dev); + + debug("asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", + phy_id, loc, le16_to_cpu(*res)); + + return le16_to_cpu(*res); +} + +static void +asix_mdio_write(struct ueth_data *dev, int phy_id, int loc, int val) +{ + ALLOC_CACHE_ALIGN_BUFFER(__le16, res, 1); + *res = cpu_to_le16(val); + + debug("asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", + phy_id, loc, val); + asix_set_sw_mii(dev); + asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, res); + asix_set_hw_mii(dev); +} + +/* + * Asix "high level" commands + */ +static int asix_sw_reset(struct ueth_data *dev, u8 flags) +{ + int ret; + + ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL); + if (ret < 0) + debug("Failed to send software reset: %02x\n", ret); + else + udelay(150 * 1000); + + return ret; +} + +static inline int asix_get_phy_addr(struct ueth_data *dev) +{ + ALLOC_CACHE_ALIGN_BUFFER(u8, buf, 2); + + int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); + + debug("asix_get_phy_addr()\n"); + + if (ret < 0) { + debug("Error reading PHYID register: %02x\n", ret); + goto out; + } + debug("asix_get_phy_addr() returning 0x%02x%02x\n", buf[0], buf[1]); + ret = buf[1]; + +out: + return ret; +} + +static int asix_write_medium_mode(struct ueth_data *dev, u16 mode) +{ + int ret; + + debug("asix_write_medium_mode() - mode = 0x%04x\n", mode); + ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, + 0, 0, NULL); + if (ret < 0) { + debug("Failed to write Medium Mode mode to 0x%04x: %02x\n", + mode, ret); + } + return ret; +} + +static u16 asix_read_rx_ctl(struct ueth_data *dev) +{ + ALLOC_CACHE_ALIGN_BUFFER(__le16, v, 1); + + int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, v); + + if (ret < 0) + debug("Error reading RX_CTL register: %02x\n", ret); + else + ret = le16_to_cpu(*v); + return ret; +} + +static int asix_write_rx_ctl(struct ueth_data *dev, u16 mode) +{ + int ret; + + debug("asix_write_rx_ctl() - mode = 0x%04x\n", mode); + ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); + if (ret < 0) { + debug("Failed to write RX_CTL mode to 0x%04x: %02x\n", + mode, ret); + } + return ret; +} + +static int asix_write_gpio(struct ueth_data *dev, u16 value, int sleep) +{ + int ret; + + debug("asix_write_gpio() - value = 0x%04x\n", value); + ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL); + if (ret < 0) { + debug("Failed to write GPIO value 0x%04x: %02x\n", + value, ret); + } + if (sleep) + udelay(sleep * 1000); + + return ret; +} + +static int asix_write_hwaddr(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + int ret; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN); + + memcpy(buf, eth->enetaddr, ETH_ALEN); + + ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, buf); + if (ret < 0) + debug("Failed to set MAC address: %02x\n", ret); + + return ret; +} + +/* + * mii commands + */ + +/* + * mii_nway_restart - restart NWay (autonegotiation) for this interface + * + * Returns 0 on success, negative on error. + */ +static int mii_nway_restart(struct ueth_data *dev) +{ + int bmcr; + int r = -1; + + /* if autoneg is off, it's an error */ + bmcr = asix_mdio_read(dev, dev->phy_id, MII_BMCR); + + if (bmcr & BMCR_ANENABLE) { + bmcr |= BMCR_ANRESTART; + asix_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr); + r = 0; + } + + return r; +} + +static int asix_read_mac(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *priv = (struct asix_private *)dev->dev_priv; + int i; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN); + + if (priv->flags & FLAG_EEPROM_MAC) { + for (i = 0; i < (ETH_ALEN >> 1); i++) { + if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, + 0x04 + i, 0, 2, buf) < 0) { + debug("Failed to read SROM address 04h.\n"); + return -1; + } + memcpy((eth->enetaddr + i * 2), buf, 2); + } + } else { + if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf) + < 0) { + debug("Failed to read MAC address.\n"); + return -1; + } + memcpy(eth->enetaddr, buf, ETH_ALEN); + } + + return 0; +} + +static int asix_basic_reset(struct ueth_data *dev) +{ + int embd_phy; + u16 rx_ctl; + + if (asix_write_gpio(dev, + AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5) < 0) + return -1; + + /* 0x10 is the phy id of the embedded 10/100 ethernet phy */ + embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); + if (asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, + embd_phy, 0, 0, NULL) < 0) { + debug("Select PHY #1 failed\n"); + return -1; + } + + if (asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL) < 0) + return -1; + + if (asix_sw_reset(dev, AX_SWRESET_CLEAR) < 0) + return -1; + + if (embd_phy) { + if (asix_sw_reset(dev, AX_SWRESET_IPRL) < 0) + return -1; + } else { + if (asix_sw_reset(dev, AX_SWRESET_PRTE) < 0) + return -1; + } + + rx_ctl = asix_read_rx_ctl(dev); + debug("RX_CTL is 0x%04x after software reset\n", rx_ctl); + if (asix_write_rx_ctl(dev, 0x0000) < 0) + return -1; + + rx_ctl = asix_read_rx_ctl(dev); + debug("RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); + + dev->phy_id = asix_get_phy_addr(dev); + if (dev->phy_id < 0) + debug("Failed to read phy id\n"); + + asix_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET); + asix_mdio_write(dev, dev->phy_id, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_CSMA); + mii_nway_restart(dev); + + if (asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT) < 0) + return -1; + + if (asix_write_cmd(dev, AX_CMD_WRITE_IPG0, + AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, + AX88772_IPG2_DEFAULT, 0, NULL) < 0) { + debug("Write IPG,IPG1,IPG2 failed\n"); + return -1; + } + + return 0; +} + +/* + * Asix callbacks + */ +static int asix_init(struct eth_device *eth, bd_t *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + int timeout = 0; +#define TIMEOUT_RESOLUTION 50 /* ms */ + int link_detected; + + debug("** %s()\n", __func__); + + if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0) + goto out_err; + + do { + link_detected = asix_mdio_read(dev, dev->phy_id, MII_BMSR) & + BMSR_LSTATUS; + if (!link_detected) { + if (timeout == 0) + printf("Waiting for Ethernet connection... "); + udelay(TIMEOUT_RESOLUTION * 1000); + timeout += TIMEOUT_RESOLUTION; + } + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); + if (link_detected) { + if (timeout != 0) + printf("done.\n"); + } else { + printf("unable to connect.\n"); + goto out_err; + } + + return 0; +out_err: + return -1; +} + +static int asix_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + int err; + u32 packet_len; + int actual_len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg, + PKTSIZE + sizeof(packet_len)); + + debug("** %s(), len %d\n", __func__, length); + + packet_len = (((length) ^ 0x0000ffff) << 16) + (length); + cpu_to_le32s(&packet_len); + + memcpy(msg, &packet_len, sizeof(packet_len)); + memcpy(msg + sizeof(packet_len), (void *)packet, length); + + err = usb_bulk_msg(dev->pusb_dev, + usb_sndbulkpipe(dev->pusb_dev, dev->ep_out), + (void *)msg, + length + sizeof(packet_len), + &actual_len, + USB_BULK_SEND_TIMEOUT); + debug("Tx: len = %u, actual = %u, err = %d\n", + length + sizeof(packet_len), actual_len, err); + + return err; +} + +static int asix_recv(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, AX_RX_URB_SIZE); + unsigned char *buf_ptr; + int err; + int actual_len; + u32 packet_len; + + debug("** %s()\n", __func__); + + err = usb_bulk_msg(dev->pusb_dev, + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), + (void *)recv_buf, + AX_RX_URB_SIZE, + &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE, + actual_len, err); + if (err != 0) { + debug("Rx: failed to receive\n"); + return -1; + } + if (actual_len > AX_RX_URB_SIZE) { + debug("Rx: received too many bytes %d\n", actual_len); + return -1; + } + + buf_ptr = recv_buf; + while (actual_len > 0) { + /* + * 1st 4 bytes contain the length of the actual data as two + * complementary 16-bit words. Extract the length of the data. + */ + if (actual_len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + return -1; + } + memcpy(&packet_len, buf_ptr, sizeof(packet_len)); + le32_to_cpus(&packet_len); + if (((~packet_len >> 16) & 0x7ff) != (packet_len & 0x7ff)) { + debug("Rx: malformed packet length: %#x (%#x:%#x)\n", + packet_len, (~packet_len >> 16) & 0x7ff, + packet_len & 0x7ff); + return -1; + } + packet_len = packet_len & 0x7ff; + if (packet_len > actual_len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + return -1; + } + + /* Notify net stack */ + NetReceive(buf_ptr + sizeof(packet_len), packet_len); + + /* Adjust for next iteration. Packets are padded to 16-bits */ + if (packet_len & 1) + packet_len++; + actual_len -= sizeof(packet_len) + packet_len; + buf_ptr += sizeof(packet_len) + packet_len; + } + + return err; +} + +static void asix_halt(struct eth_device *eth) +{ + debug("** %s()\n", __func__); +} + +/* + * Asix probing functions + */ +void asix_eth_before_probe(void) +{ + curr_eth_dev = 0; +} + +struct asix_dongle { + unsigned short vendor; + unsigned short product; + int flags; +}; + +static const struct asix_dongle const asix_dongles[] = { + { 0x05ac, 0x1402, FLAG_TYPE_AX88772 }, /* Apple USB Ethernet Adapter */ + { 0x07d1, 0x3c05, FLAG_TYPE_AX88772 }, /* D-Link DUB-E100 H/W Ver B1 */ + { 0x2001, 0x1a02, FLAG_TYPE_AX88772 }, /* D-Link DUB-E100 H/W Ver C1 */ + /* Cables-to-Go USB Ethernet Adapter */ + { 0x0b95, 0x772a, FLAG_TYPE_AX88772 }, + { 0x0b95, 0x7720, FLAG_TYPE_AX88772 }, /* Trendnet TU2-ET100 V3.0R */ + { 0x0b95, 0x1720, FLAG_TYPE_AX88172 }, /* SMC */ + { 0x0db0, 0xa877, FLAG_TYPE_AX88772 }, /* MSI - ASIX 88772a */ + { 0x13b1, 0x0018, FLAG_TYPE_AX88172 }, /* Linksys 200M v2.1 */ + { 0x1557, 0x7720, FLAG_TYPE_AX88772 }, /* 0Q0 cable ethernet */ + /* DLink DUB-E100 H/W Ver B1 Alternate */ + { 0x2001, 0x3c05, FLAG_TYPE_AX88772 }, + /* ASIX 88772B */ + { 0x0b95, 0x772b, FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC }, + { 0x0000, 0x0000, FLAG_NONE } /* END - Do not remove */ +}; + +/* Probe to see if a new device is actually an asix device */ +int asix_eth_probe(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *iface_desc; + int ep_in_found = 0, ep_out_found = 0; + int i; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &dev->config.if_desc[ifnum].desc; + + for (i = 0; asix_dongles[i].vendor != 0; i++) { + if (dev->descriptor.idVendor == asix_dongles[i].vendor && + dev->descriptor.idProduct == asix_dongles[i].product) + /* Found a supported dongle */ + break; + } + + if (asix_dongles[i].vendor == 0) + return 0; + + memset(ss, 0, sizeof(struct ueth_data)); + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + + /* Initialize the ueth_data structure with some useful info */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->subclass = iface_desc->bInterfaceSubClass; + ss->protocol = iface_desc->bInterfaceProtocol; + + /* alloc driver private */ + ss->dev_priv = calloc(1, sizeof(struct asix_private)); + if (!ss->dev_priv) + return 0; + + ((struct asix_private *)ss->dev_priv)->flags = asix_dongles[i].flags; + + /* + * We are expecting a minimum of 3 endpoints - in, out (bulk), and + * int. We will ignore any others. + */ + for (i = 0; i < iface_desc->bNumEndpoints; i++) { + /* is it an BULK endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + u8 ep_addr = iface->ep_desc[i].bEndpointAddress; + if (ep_addr & USB_DIR_IN) { + if (!ep_in_found) { + ss->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = 1; + } + } else { + if (!ep_out_found) { + ss->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_out_found = 1; + } + } + } + + /* is it an interrupt endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ss->ep_int = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->irqinterval = iface->ep_desc[i].bInterval; + } + } + debug("Endpoints In %d Out %d Int %d\n", + ss->ep_in, ss->ep_out, ss->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || !ss->ep_int) { + debug("Problems with device\n"); + return 0; + } + dev->privptr = (void *)ss; + return 1; +} + +int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + struct asix_private *priv = (struct asix_private *)ss->dev_priv; + + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + sprintf(eth->name, "%s%d", ASIX_BASE_NAME, curr_eth_dev++); + eth->init = asix_init; + eth->send = asix_send; + eth->recv = asix_recv; + eth->halt = asix_halt; + if (!(priv->flags & FLAG_TYPE_AX88172)) + eth->write_hwaddr = asix_write_hwaddr; + eth->priv = ss; + + if (asix_basic_reset(ss)) + return 0; + + /* Get the MAC address */ + if (asix_read_mac(eth)) + return 0; + debug("MAC %pM\n", eth->enetaddr); + + return 1; +} diff --git a/qemu/roms/u-boot/drivers/usb/eth/mcs7830.c b/qemu/roms/u-boot/drivers/usb/eth/mcs7830.c new file mode 100644 index 000000000..c353286b6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/eth/mcs7830.c @@ -0,0 +1,812 @@ +/* + * Copyright (c) 2013 Gerhard Sittig + * based on the U-Boot Asix driver as well as information + * from the Linux Moschip driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices + */ + +#include +#include +#include +#include +#include + +#include "usb_ether.h" + +#define MCS7830_BASE_NAME "mcs" + +#define USBCALL_TIMEOUT 1000 +#define LINKSTATUS_TIMEOUT 5000 /* link status, connect timeout */ +#define LINKSTATUS_TIMEOUT_RES 50 /* link status, resolution in msec */ + +#define MCS7830_RX_URB_SIZE 2048 + +/* command opcodes */ +#define MCS7830_WR_BREQ 0x0d +#define MCS7830_RD_BREQ 0x0e + +/* register layout, numerical offset specs for USB API calls */ +struct mcs7830_regs { + uint8_t multicast_hashes[8]; + uint8_t packet_gap[2]; + uint8_t phy_data[2]; + uint8_t phy_command[2]; + uint8_t configuration; + uint8_t ether_address[6]; + uint8_t frame_drop_count; + uint8_t pause_threshold; +}; +#define REG_MULTICAST_HASH offsetof(struct mcs7830_regs, multicast_hashes) +#define REG_PHY_DATA offsetof(struct mcs7830_regs, phy_data) +#define REG_PHY_CMD offsetof(struct mcs7830_regs, phy_command) +#define REG_CONFIG offsetof(struct mcs7830_regs, configuration) +#define REG_ETHER_ADDR offsetof(struct mcs7830_regs, ether_address) +#define REG_FRAME_DROP_COUNTER offsetof(struct mcs7830_regs, frame_drop_count) +#define REG_PAUSE_THRESHOLD offsetof(struct mcs7830_regs, pause_threshold) + +/* bit masks and default values for the above registers */ +#define PHY_CMD1_READ 0x40 +#define PHY_CMD1_WRITE 0x20 +#define PHY_CMD1_PHYADDR 0x01 + +#define PHY_CMD2_PEND 0x80 +#define PHY_CMD2_READY 0x40 + +#define CONF_CFG 0x80 +#define CONF_SPEED100 0x40 +#define CONF_FDX_ENABLE 0x20 +#define CONF_RXENABLE 0x10 +#define CONF_TXENABLE 0x08 +#define CONF_SLEEPMODE 0x04 +#define CONF_ALLMULTICAST 0x02 +#define CONF_PROMISCUOUS 0x01 + +#define PAUSE_THRESHOLD_DEFAULT 0 + +/* bit masks for the status byte which follows received ethernet frames */ +#define STAT_RX_FRAME_CORRECT 0x20 +#define STAT_RX_LARGE_FRAME 0x10 +#define STAT_RX_CRC_ERROR 0x08 +#define STAT_RX_ALIGNMENT_ERROR 0x04 +#define STAT_RX_LENGTH_ERROR 0x02 +#define STAT_RX_SHORT_FRAME 0x01 + +/* + * struct mcs7830_private - private driver data for an individual adapter + * @config: shadow for the network adapter's configuration register + * @mchash: shadow for the network adapter's multicast hash registers + */ +struct mcs7830_private { + uint8_t config; + uint8_t mchash[8]; +}; + +/* + * mcs7830_read_reg() - read a register of the network adapter + * @dev: network device to read from + * @idx: index of the register to start reading from + * @size: number of bytes to read + * @data: buffer to read into + * Return: zero upon success, negative upon error + */ +static int mcs7830_read_reg(struct ueth_data *dev, uint8_t idx, + uint16_t size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, size); + + debug("%s() idx=0x%04X sz=%d\n", __func__, idx, size); + + len = usb_control_msg(dev->pusb_dev, + usb_rcvctrlpipe(dev->pusb_dev, 0), + MCS7830_RD_BREQ, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, idx, buf, size, + USBCALL_TIMEOUT); + if (len != size) { + debug("%s() len=%d != sz=%d\n", __func__, len, size); + return -EIO; + } + memcpy(data, buf, size); + return 0; +} + +/* + * mcs7830_write_reg() - write a register of the network adapter + * @dev: network device to write to + * @idx: index of the register to start writing to + * @size: number of bytes to write + * @data: buffer holding the data to write + * Return: zero upon success, negative upon error + */ +static int mcs7830_write_reg(struct ueth_data *dev, uint8_t idx, + uint16_t size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, size); + + debug("%s() idx=0x%04X sz=%d\n", __func__, idx, size); + + memcpy(buf, data, size); + len = usb_control_msg(dev->pusb_dev, + usb_sndctrlpipe(dev->pusb_dev, 0), + MCS7830_WR_BREQ, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, idx, buf, size, + USBCALL_TIMEOUT); + if (len != size) { + debug("%s() len=%d != sz=%d\n", __func__, len, size); + return -EIO; + } + return 0; +} + +/* + * mcs7830_phy_emit_wait() - emit PHY read/write access, wait for its execution + * @dev: network device to talk to + * @rwflag: PHY_CMD1_READ or PHY_CMD1_WRITE opcode + * @index: number of the PHY register to read or write + * Return: zero upon success, negative upon error + */ +static int mcs7830_phy_emit_wait(struct ueth_data *dev, + uint8_t rwflag, uint8_t index) +{ + int rc; + int retry; + uint8_t cmd[2]; + + /* send the PHY read/write request */ + cmd[0] = rwflag | PHY_CMD1_PHYADDR; + cmd[1] = PHY_CMD2_PEND | (index & 0x1f); + rc = mcs7830_write_reg(dev, REG_PHY_CMD, sizeof(cmd), cmd); + if (rc < 0) + return rc; + + /* wait for the response to become available (usually < 1ms) */ + retry = 10; + do { + rc = mcs7830_read_reg(dev, REG_PHY_CMD, sizeof(cmd), cmd); + if (rc < 0) + return rc; + if (cmd[1] & PHY_CMD2_READY) + return 0; + if (!retry--) + return -ETIMEDOUT; + mdelay(1); + } while (1); + /* UNREACH */ +} + +/* + * mcs7830_read_phy() - read a PHY register of the network adapter + * @dev: network device to read from + * @index: index of the PHY register to read from + * Return: non-negative 16bit register content, negative upon error + */ +static int mcs7830_read_phy(struct ueth_data *dev, uint8_t index) +{ + int rc; + uint16_t val; + + /* issue the PHY read request and wait for its execution */ + rc = mcs7830_phy_emit_wait(dev, PHY_CMD1_READ, index); + if (rc < 0) + return rc; + + /* fetch the PHY data which was read */ + rc = mcs7830_read_reg(dev, REG_PHY_DATA, sizeof(val), &val); + if (rc < 0) + return rc; + rc = le16_to_cpu(val); + debug("%s(%s, %d) => 0x%04X\n", __func__, dev->eth_dev.name, index, rc); + return rc; +} + +/* + * mcs7830_write_phy() - write a PHY register of the network adapter + * @dev: network device to write to + * @index: index of the PHY register to write to + * @val: value to write to the PHY register + * Return: zero upon success, negative upon error + */ +static int mcs7830_write_phy(struct ueth_data *dev, uint8_t index, uint16_t val) +{ + int rc; + + debug("%s(%s, %d, 0x%04X)\n", __func__, dev->eth_dev.name, index, val); + + /* setup the PHY data which is to get written */ + val = cpu_to_le16(val); + rc = mcs7830_write_reg(dev, REG_PHY_DATA, sizeof(val), &val); + if (rc < 0) + return rc; + + /* issue the PHY write request and wait for its execution */ + rc = mcs7830_phy_emit_wait(dev, PHY_CMD1_WRITE, index); + if (rc < 0) + return rc; + + return 0; +} + +/* + * mcs7830_write_config() - write to the network adapter's config register + * @eth: network device to write to + * Return: zero upon success, negative upon error + * + * the data which gets written is taken from the shadow config register + * within the device driver's private data + */ +static int mcs7830_write_config(struct ueth_data *dev) +{ + struct mcs7830_private *priv; + int rc; + + debug("%s()\n", __func__); + priv = dev->dev_priv; + + rc = mcs7830_write_reg(dev, REG_CONFIG, + sizeof(priv->config), &priv->config); + if (rc < 0) { + debug("writing config to adapter failed\n"); + return rc; + } + + return 0; +} + +/* + * mcs7830_write_mchash() - write the network adapter's multicast filter + * @eth: network device to write to + * Return: zero upon success, negative upon error + * + * the data which gets written is taken from the shadow multicast hashes + * within the device driver's private data + */ +static int mcs7830_write_mchash(struct ueth_data *dev) +{ + struct mcs7830_private *priv; + int rc; + + debug("%s()\n", __func__); + priv = dev->dev_priv; + + rc = mcs7830_write_reg(dev, REG_MULTICAST_HASH, + sizeof(priv->mchash), &priv->mchash); + if (rc < 0) { + debug("writing multicast hash to adapter failed\n"); + return rc; + } + + return 0; +} + +/* + * mcs7830_set_autoneg() - setup and trigger ethernet link autonegotiation + * @eth: network device to run link negotiation on + * Return: zero upon success, negative upon error + * + * the routine advertises available media and starts autonegotiation + */ +static int mcs7830_set_autoneg(struct ueth_data *dev) +{ + int adv, flg; + int rc; + + debug("%s()\n", __func__); + + /* + * algorithm taken from the Linux driver, which took it from + * "the original mcs7830 version 1.4 driver": + * + * enable all media, reset BMCR, enable auto neg, restart + * auto neg while keeping the enable auto neg flag set + */ + + adv = ADVERTISE_PAUSE_CAP | ADVERTISE_ALL | ADVERTISE_CSMA; + rc = mcs7830_write_phy(dev, MII_ADVERTISE, adv); + + flg = 0; + if (!rc) + rc = mcs7830_write_phy(dev, MII_BMCR, flg); + + flg |= BMCR_ANENABLE; + if (!rc) + rc = mcs7830_write_phy(dev, MII_BMCR, flg); + + flg |= BMCR_ANRESTART; + if (!rc) + rc = mcs7830_write_phy(dev, MII_BMCR, flg); + + return rc; +} + +/* + * mcs7830_get_rev() - identify a network adapter's chip revision + * @eth: network device to identify + * Return: non-negative number, reflecting the revision number + * + * currently, only "rev C and higher" and "below rev C" are needed, so + * the return value is #1 for "below rev C", and #2 for "rev C and above" + */ +static int mcs7830_get_rev(struct ueth_data *dev) +{ + uint8_t buf[2]; + int rc; + int rev; + + /* register 22 is readable in rev C and higher */ + rc = mcs7830_read_reg(dev, REG_FRAME_DROP_COUNTER, sizeof(buf), buf); + if (rc < 0) + rev = 1; + else + rev = 2; + debug("%s() rc=%d, rev=%d\n", __func__, rc, rev); + return rev; +} + +/* + * mcs7830_apply_fixup() - identify an adapter and potentially apply fixups + * @eth: network device to identify and apply fixups to + * Return: zero upon success (no errors emitted from here) + * + * this routine identifies the network adapter's chip revision, and applies + * fixups for known issues + */ +static int mcs7830_apply_fixup(struct ueth_data *dev) +{ + int rev; + int i; + uint8_t thr; + + rev = mcs7830_get_rev(dev); + debug("%s() rev=%d\n", __func__, rev); + + /* + * rev C requires setting the pause threshold (the Linux driver + * is inconsistent, the implementation does it for "rev C + * exactly", the introductory comment says "rev C and above") + */ + if (rev == 2) { + debug("%s: applying rev C fixup\n", dev->eth_dev.name); + thr = PAUSE_THRESHOLD_DEFAULT; + for (i = 0; i < 2; i++) { + (void)mcs7830_write_reg(dev, REG_PAUSE_THRESHOLD, + sizeof(thr), &thr); + mdelay(1); + } + } + + return 0; +} + +/* + * mcs7830_basic_reset() - bring the network adapter into a known first state + * @eth: network device to act upon + * Return: zero upon success, negative upon error + * + * this routine initializes the network adapter such that subsequent invocations + * of the interface callbacks can exchange ethernet frames; link negotiation is + * triggered from here already and continues in background + */ +static int mcs7830_basic_reset(struct ueth_data *dev) +{ + struct mcs7830_private *priv; + int rc; + + debug("%s()\n", __func__); + priv = dev->dev_priv; + + /* + * comment from the respective Linux driver, which + * unconditionally sets the ALLMULTICAST flag as well: + * should not be needed, but does not work otherwise + */ + priv->config = CONF_TXENABLE; + priv->config |= CONF_ALLMULTICAST; + + rc = mcs7830_set_autoneg(dev); + if (rc < 0) { + error("setting autoneg failed\n"); + return rc; + } + + rc = mcs7830_write_mchash(dev); + if (rc < 0) { + error("failed to set multicast hash\n"); + return rc; + } + + rc = mcs7830_write_config(dev); + if (rc < 0) { + error("failed to set configuration\n"); + return rc; + } + + rc = mcs7830_apply_fixup(dev); + if (rc < 0) { + error("fixup application failed\n"); + return rc; + } + + return 0; +} + +/* + * mcs7830_read_mac() - read an ethernet adapter's MAC address + * @eth: network device to read from + * Return: zero upon success, negative upon error + * + * this routine fetches the MAC address stored within the ethernet adapter, + * and stores it in the ethernet interface's data structure + */ +static int mcs7830_read_mac(struct eth_device *eth) +{ + struct ueth_data *dev; + int rc; + uint8_t buf[ETH_ALEN]; + + debug("%s()\n", __func__); + dev = eth->priv; + + rc = mcs7830_read_reg(dev, REG_ETHER_ADDR, ETH_ALEN, buf); + if (rc < 0) { + debug("reading MAC from adapter failed\n"); + return rc; + } + + memcpy(ð->enetaddr[0], buf, ETH_ALEN); + return 0; +} + +/* + * mcs7830_write_mac() - write an ethernet adapter's MAC address + * @eth: network device to write to + * Return: zero upon success, negative upon error + * + * this routine takes the MAC address from the ethernet interface's data + * structure, and writes it into the ethernet adapter such that subsequent + * exchange of ethernet frames uses this address + */ +static int mcs7830_write_mac(struct eth_device *eth) +{ + struct ueth_data *dev; + int rc; + + debug("%s()\n", __func__); + dev = eth->priv; + + if (sizeof(eth->enetaddr) != ETH_ALEN) + return -EINVAL; + rc = mcs7830_write_reg(dev, REG_ETHER_ADDR, ETH_ALEN, eth->enetaddr); + if (rc < 0) { + debug("writing MAC to adapter failed\n"); + return rc; + } + return 0; +} + +/* + * mcs7830_init() - network interface's init callback + * @eth: network device to initialize + * @bd: board information + * Return: zero upon success, negative upon error + * + * after initial setup during probe() and get_info(), this init() callback + * ensures that the link is up and subsequent send() and recv() calls can + * exchange ethernet frames + */ +static int mcs7830_init(struct eth_device *eth, bd_t *bd) +{ + struct ueth_data *dev; + int timeout; + int have_link; + + debug("%s()\n", __func__); + dev = eth->priv; + + timeout = 0; + do { + have_link = mcs7830_read_phy(dev, MII_BMSR) & BMSR_LSTATUS; + if (have_link) + break; + udelay(LINKSTATUS_TIMEOUT_RES * 1000); + timeout += LINKSTATUS_TIMEOUT_RES; + } while (timeout < LINKSTATUS_TIMEOUT); + if (!have_link) { + debug("ethernet link is down\n"); + return -ETIMEDOUT; + } + return 0; +} + +/* + * mcs7830_send() - network interface's send callback + * @eth: network device to send the frame from + * @packet: ethernet frame content + * @length: ethernet frame length + * Return: zero upon success, negative upon error + * + * this routine send an ethernet frame out of the network interface + */ +static int mcs7830_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev; + int rc; + int gotlen; + /* there is a status byte after the ethernet frame */ + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, PKTSIZE + sizeof(uint8_t)); + + dev = eth->priv; + + memcpy(buf, packet, length); + rc = usb_bulk_msg(dev->pusb_dev, + usb_sndbulkpipe(dev->pusb_dev, dev->ep_out), + &buf[0], length, &gotlen, + USBCALL_TIMEOUT); + debug("%s() TX want len %d, got len %d, rc %d\n", + __func__, length, gotlen, rc); + return rc; +} + +/* + * mcs7830_recv() - network interface's recv callback + * @eth: network device to receive frames from + * Return: zero upon success, negative upon error + * + * this routine checks for available ethernet frames that the network + * interface might have received, and notifies the network stack + */ +static int mcs7830_recv(struct eth_device *eth) +{ + struct ueth_data *dev; + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, MCS7830_RX_URB_SIZE); + int rc, wantlen, gotlen; + uint8_t sts; + + debug("%s()\n", __func__); + dev = eth->priv; + + /* fetch input data from the adapter */ + wantlen = MCS7830_RX_URB_SIZE; + rc = usb_bulk_msg(dev->pusb_dev, + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), + &buf[0], wantlen, &gotlen, + USBCALL_TIMEOUT); + debug("%s() RX want len %d, got len %d, rc %d\n", + __func__, wantlen, gotlen, rc); + if (rc != 0) { + error("RX: failed to receive\n"); + return rc; + } + if (gotlen > wantlen) { + error("RX: got too many bytes (%d)\n", gotlen); + return -EIO; + } + + /* + * the bulk message that we received from USB contains exactly + * one ethernet frame and a trailing status byte + */ + if (gotlen < sizeof(sts)) + return -EIO; + gotlen -= sizeof(sts); + sts = buf[gotlen]; + + if (sts == STAT_RX_FRAME_CORRECT) { + debug("%s() got a frame, len=%d\n", __func__, gotlen); + NetReceive(buf, gotlen); + return 0; + } + + debug("RX: frame error (sts 0x%02X, %s %s %s %s %s)\n", + sts, + (sts & STAT_RX_LARGE_FRAME) ? "large" : "-", + (sts & STAT_RX_LENGTH_ERROR) ? "length" : "-", + (sts & STAT_RX_SHORT_FRAME) ? "short" : "-", + (sts & STAT_RX_CRC_ERROR) ? "crc" : "-", + (sts & STAT_RX_ALIGNMENT_ERROR) ? "align" : "-"); + return -EIO; +} + +/* + * mcs7830_halt() - network interface's halt callback + * @eth: network device to cease operation of + * Return: none + * + * this routine is supposed to undo the effect of previous initialization and + * ethernet frames exchange; in this implementation it's a NOP + */ +static void mcs7830_halt(struct eth_device *eth) +{ + debug("%s()\n", __func__); +} + +/* + * mcs7830_iface_idx - index of detected network interfaces + * + * this counter keeps track of identified supported interfaces, + * to assign unique names as more interfaces are found + */ +static int mcs7830_iface_idx; + +/* + * mcs7830_eth_before_probe() - network driver's before_probe callback + * Return: none + * + * this routine initializes driver's internal data in preparation of + * subsequent probe callbacks + */ +void mcs7830_eth_before_probe(void) +{ + mcs7830_iface_idx = 0; +} + +/* + * struct mcs7830_dongle - description of a supported Moschip ethernet dongle + * @vendor: 16bit USB vendor identification + * @product: 16bit USB product identification + * + * this structure describes a supported USB ethernet dongle by means of the + * vendor and product codes found during USB enumeration; no flags are held + * here since all supported dongles have identical behaviour, and required + * fixups get determined at runtime, such that no manual configuration is + * needed + */ +struct mcs7830_dongle { + uint16_t vendor; + uint16_t product; +}; + +/* + * mcs7830_dongles - the list of supported Moschip based USB ethernet dongles + */ +static const struct mcs7830_dongle const mcs7830_dongles[] = { + { 0x9710, 0x7832, }, /* Moschip 7832 */ + { 0x9710, 0x7830, }, /* Moschip 7830 */ + { 0x9710, 0x7730, }, /* Moschip 7730 */ + { 0x0df6, 0x0021, }, /* Sitecom LN 30 */ +}; + +/* + * mcs7830_eth_probe() - network driver's probe callback + * @dev: detected USB device to check + * @ifnum: detected USB interface to check + * @ss: USB ethernet data structure to fill in upon match + * Return: #1 upon match, #0 upon mismatch or error + * + * this routine checks whether the found USB device is supported by + * this ethernet driver, and upon match fills in the USB ethernet + * data structure which later is passed to the get_info callback + */ +int mcs7830_eth_probe(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *iface_desc; + int i; + struct mcs7830_private *priv; + int ep_in_found, ep_out_found, ep_intr_found; + + debug("%s()\n", __func__); + + /* iterate the list of supported dongles */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &iface->desc; + for (i = 0; i < ARRAY_SIZE(mcs7830_dongles); i++) { + if (dev->descriptor.idVendor == mcs7830_dongles[i].vendor && + dev->descriptor.idProduct == mcs7830_dongles[i].product) + break; + } + if (i == ARRAY_SIZE(mcs7830_dongles)) + return 0; + debug("detected USB ethernet device: %04X:%04X\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + + /* fill in driver private data */ + priv = calloc(1, sizeof(*priv)); + if (!priv) + return 0; + + /* fill in the ueth_data structure, attach private data */ + memset(ss, 0, sizeof(*ss)); + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->subclass = iface_desc->bInterfaceSubClass; + ss->protocol = iface_desc->bInterfaceProtocol; + ss->dev_priv = priv; + + /* + * a minimum of three endpoints is expected: in (bulk), + * out (bulk), and interrupt; ignore all others + */ + ep_in_found = ep_out_found = ep_intr_found = 0; + for (i = 0; i < iface_desc->bNumEndpoints; i++) { + uint8_t eptype, epaddr; + bool is_input; + + eptype = iface->ep_desc[i].bmAttributes; + eptype &= USB_ENDPOINT_XFERTYPE_MASK; + + epaddr = iface->ep_desc[i].bEndpointAddress; + is_input = epaddr & USB_DIR_IN; + epaddr &= USB_ENDPOINT_NUMBER_MASK; + + if (eptype == USB_ENDPOINT_XFER_BULK) { + if (is_input && !ep_in_found) { + ss->ep_in = epaddr; + ep_in_found++; + } + if (!is_input && !ep_out_found) { + ss->ep_out = epaddr; + ep_out_found++; + } + } + + if (eptype == USB_ENDPOINT_XFER_INT) { + if (is_input && !ep_intr_found) { + ss->ep_int = epaddr; + ss->irqinterval = iface->ep_desc[i].bInterval; + ep_intr_found++; + } + } + } + debug("endpoints: in %d, out %d, intr %d\n", + ss->ep_in, ss->ep_out, ss->ep_int); + + /* apply basic sanity checks */ + if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || !ss->ep_int) { + debug("device probe incomplete\n"); + return 0; + } + + dev->privptr = ss; + return 1; +} + +/* + * mcs7830_eth_get_info() - network driver's get_info callback + * @dev: detected USB device + * @ss: USB ethernet data structure filled in at probe() + * @eth: ethernet interface data structure to fill in + * Return: #1 upon success, #0 upon error + * + * this routine registers the mandatory init(), send(), recv(), and + * halt() callbacks with the ethernet interface, can register the + * optional write_hwaddr() callback with the ethernet interface, + * and initiates configuration of the interface such that subsequent + * calls to those callbacks results in network communication + */ +int mcs7830_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + debug("%s()\n", __func__); + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + + snprintf(eth->name, sizeof(eth->name), "%s%d", + MCS7830_BASE_NAME, mcs7830_iface_idx++); + eth->init = mcs7830_init; + eth->send = mcs7830_send; + eth->recv = mcs7830_recv; + eth->halt = mcs7830_halt; + eth->write_hwaddr = mcs7830_write_mac; + eth->priv = ss; + + if (mcs7830_basic_reset(ss)) + return 0; + + if (mcs7830_read_mac(eth)) + return 0; + debug("MAC %pM\n", eth->enetaddr); + + return 1; +} diff --git a/qemu/roms/u-boot/drivers/usb/eth/smsc95xx.c b/qemu/roms/u-boot/drivers/usb/eth/smsc95xx.c new file mode 100644 index 000000000..7bf0a3407 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/eth/smsc95xx.c @@ -0,0 +1,897 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (C) 2009 NVIDIA, Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "usb_ether.h" +#include + +/* SMSC LAN95xx based USB 2.0 Ethernet Devices */ + +/* LED defines */ +#define LED_GPIO_CFG (0x24) +#define LED_GPIO_CFG_SPD_LED (0x01000000) +#define LED_GPIO_CFG_LNK_LED (0x00100000) +#define LED_GPIO_CFG_FDX_LED (0x00010000) + +/* Tx command words */ +#define TX_CMD_A_FIRST_SEG_ 0x00002000 +#define TX_CMD_A_LAST_SEG_ 0x00001000 + +/* Rx status word */ +#define RX_STS_FL_ 0x3FFF0000 /* Frame Length */ +#define RX_STS_ES_ 0x00008000 /* Error Summary */ + +/* SCSRs */ +#define ID_REV 0x00 + +#define INT_STS 0x08 + +#define TX_CFG 0x10 +#define TX_CFG_ON_ 0x00000004 + +#define HW_CFG 0x14 +#define HW_CFG_BIR_ 0x00001000 +#define HW_CFG_RXDOFF_ 0x00000600 +#define HW_CFG_MEF_ 0x00000020 +#define HW_CFG_BCE_ 0x00000002 +#define HW_CFG_LRST_ 0x00000008 + +#define PM_CTRL 0x20 +#define PM_CTL_PHY_RST_ 0x00000010 + +#define AFC_CFG 0x2C + +/* + * Hi watermark = 15.5Kb (~10 mtu pkts) + * low watermark = 3k (~2 mtu pkts) + * backpressure duration = ~ 350us + * Apply FC on any frame. + */ +#define AFC_CFG_DEFAULT 0x00F830A1 + +#define E2P_CMD 0x30 +#define E2P_CMD_BUSY_ 0x80000000 +#define E2P_CMD_READ_ 0x00000000 +#define E2P_CMD_TIMEOUT_ 0x00000400 +#define E2P_CMD_LOADED_ 0x00000200 +#define E2P_CMD_ADDR_ 0x000001FF + +#define E2P_DATA 0x34 + +#define BURST_CAP 0x38 + +#define INT_EP_CTL 0x68 +#define INT_EP_CTL_PHY_INT_ 0x00008000 + +#define BULK_IN_DLY 0x6C + +/* MAC CSRs */ +#define MAC_CR 0x100 +#define MAC_CR_MCPAS_ 0x00080000 +#define MAC_CR_PRMS_ 0x00040000 +#define MAC_CR_HPFILT_ 0x00002000 +#define MAC_CR_TXEN_ 0x00000008 +#define MAC_CR_RXEN_ 0x00000004 + +#define ADDRH 0x104 + +#define ADDRL 0x108 + +#define MII_ADDR 0x114 +#define MII_WRITE_ 0x02 +#define MII_BUSY_ 0x01 +#define MII_READ_ 0x00 /* ~of MII Write bit */ + +#define MII_DATA 0x118 + +#define FLOW 0x11C + +#define VLAN1 0x120 + +#define COE_CR 0x130 +#define Tx_COE_EN_ 0x00010000 +#define Rx_COE_EN_ 0x00000001 + +/* Vendor-specific PHY Definitions */ +#define PHY_INT_SRC 29 + +#define PHY_INT_MASK 30 +#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040) +#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010) +#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \ + PHY_INT_MASK_LINK_DOWN_) + +/* USB Vendor Requests */ +#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 +#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 + +/* Some extra defines */ +#define HS_USB_PKT_SIZE 512 +#define FS_USB_PKT_SIZE 64 +#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) +#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE) +#define DEFAULT_BULK_IN_DELAY 0x00002000 +#define MAX_SINGLE_PACKET_SIZE 2048 +#define EEPROM_MAC_OFFSET 0x01 +#define SMSC95XX_INTERNAL_PHY_ID 1 +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ + +/* local defines */ +#define SMSC95XX_BASE_NAME "sms" +#define USB_CTRL_SET_TIMEOUT 5000 +#define USB_CTRL_GET_TIMEOUT 5000 +#define USB_BULK_SEND_TIMEOUT 5000 +#define USB_BULK_RECV_TIMEOUT 5000 + +#define AX_RX_URB_SIZE 2048 +#define PHY_CONNECT_TIMEOUT 5000 + +#define TURBO_MODE + +/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */ + +/* driver private */ +struct smsc95xx_private { + size_t rx_urb_size; /* maximum USB URB size */ + u32 mac_cr; /* MAC control register value */ + int have_hwaddr; /* 1 if we have a hardware MAC address */ +}; + +/* + * Smsc95xx infrastructure commands + */ +static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1); + + cpu_to_le32s(&data); + tmpbuf[0] = data; + + len = usb_control_msg(dev->pusb_dev, usb_sndctrlpipe(dev->pusb_dev, 0), + USB_VENDOR_REQUEST_WRITE_REGISTER, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 00, index, tmpbuf, sizeof(data), USB_CTRL_SET_TIMEOUT); + if (len != sizeof(data)) { + debug("smsc95xx_write_reg failed: index=%d, data=%d, len=%d", + index, data, len); + return -1; + } + return 0; +} + +static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1); + + len = usb_control_msg(dev->pusb_dev, usb_rcvctrlpipe(dev->pusb_dev, 0), + USB_VENDOR_REQUEST_READ_REGISTER, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 00, index, tmpbuf, sizeof(data), USB_CTRL_GET_TIMEOUT); + *data = tmpbuf[0]; + if (len != sizeof(data)) { + debug("smsc95xx_read_reg failed: index=%d, len=%d", + index, len); + return -1; + } + + le32_to_cpus(data); + return 0; +} + +/* Loop until the read is completed with timeout */ +static int smsc95xx_phy_wait_not_busy(struct ueth_data *dev) +{ + unsigned long start_time = get_timer(0); + u32 val; + + do { + smsc95xx_read_reg(dev, MII_ADDR, &val); + if (!(val & MII_BUSY_)) + return 0; + } while (get_timer(start_time) < 1 * 1000 * 1000); + + return -1; +} + +static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx) +{ + u32 val, addr; + + /* confirm MII not busy */ + if (smsc95xx_phy_wait_not_busy(dev)) { + debug("MII is busy in smsc95xx_mdio_read\n"); + return -1; + } + + /* set the address, index & direction (read from PHY) */ + addr = (phy_id << 11) | (idx << 6) | MII_READ_; + smsc95xx_write_reg(dev, MII_ADDR, addr); + + if (smsc95xx_phy_wait_not_busy(dev)) { + debug("Timed out reading MII reg %02X\n", idx); + return -1; + } + + smsc95xx_read_reg(dev, MII_DATA, &val); + + return (u16)(val & 0xFFFF); +} + +static void smsc95xx_mdio_write(struct ueth_data *dev, int phy_id, int idx, + int regval) +{ + u32 val, addr; + + /* confirm MII not busy */ + if (smsc95xx_phy_wait_not_busy(dev)) { + debug("MII is busy in smsc95xx_mdio_write\n"); + return; + } + + val = regval; + smsc95xx_write_reg(dev, MII_DATA, val); + + /* set the address, index & direction (write to PHY) */ + addr = (phy_id << 11) | (idx << 6) | MII_WRITE_; + smsc95xx_write_reg(dev, MII_ADDR, addr); + + if (smsc95xx_phy_wait_not_busy(dev)) + debug("Timed out writing MII reg %02X\n", idx); +} + +static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev) +{ + unsigned long start_time = get_timer(0); + u32 val; + + do { + smsc95xx_read_reg(dev, E2P_CMD, &val); + if (!(val & E2P_CMD_BUSY_)) + return 0; + udelay(40); + } while (get_timer(start_time) < 1 * 1000 * 1000); + + debug("EEPROM is busy\n"); + return -1; +} + +static int smsc95xx_wait_eeprom(struct ueth_data *dev) +{ + unsigned long start_time = get_timer(0); + u32 val; + + do { + smsc95xx_read_reg(dev, E2P_CMD, &val); + if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_)) + break; + udelay(40); + } while (get_timer(start_time) < 1 * 1000 * 1000); + + if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) { + debug("EEPROM read operation timeout\n"); + return -1; + } + return 0; +} + +static int smsc95xx_read_eeprom(struct ueth_data *dev, u32 offset, u32 length, + u8 *data) +{ + u32 val; + int i, ret; + + ret = smsc95xx_eeprom_confirm_not_busy(dev); + if (ret) + return ret; + + for (i = 0; i < length; i++) { + val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_); + smsc95xx_write_reg(dev, E2P_CMD, val); + + ret = smsc95xx_wait_eeprom(dev); + if (ret < 0) + return ret; + + smsc95xx_read_reg(dev, E2P_DATA, &val); + data[i] = val & 0xFF; + offset++; + } + return 0; +} + +/* + * mii_nway_restart - restart NWay (autonegotiation) for this interface + * + * Returns 0 on success, negative on error. + */ +static int mii_nway_restart(struct ueth_data *dev) +{ + int bmcr; + int r = -1; + + /* if autoneg is off, it's an error */ + bmcr = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMCR); + + if (bmcr & BMCR_ANENABLE) { + bmcr |= BMCR_ANRESTART; + smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr); + r = 0; + } + return r; +} + +static int smsc95xx_phy_initialize(struct ueth_data *dev) +{ + smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET); + smsc95xx_mdio_write(dev, dev->phy_id, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + + /* read to clear */ + smsc95xx_mdio_read(dev, dev->phy_id, PHY_INT_SRC); + + smsc95xx_mdio_write(dev, dev->phy_id, PHY_INT_MASK, + PHY_INT_MASK_DEFAULT_); + mii_nway_restart(dev); + + debug("phy initialised succesfully\n"); + return 0; +} + +static int smsc95xx_init_mac_address(struct eth_device *eth, + struct ueth_data *dev) +{ + /* try reading mac address from EEPROM */ + if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, + eth->enetaddr) == 0) { + if (is_valid_ether_addr(eth->enetaddr)) { + /* eeprom values are valid so use them */ + debug("MAC address read from EEPROM\n"); + return 0; + } + } + + /* + * No eeprom, or eeprom values are invalid. Generating a random MAC + * address is not safe. Just return an error. + */ + return -1; +} + +static int smsc95xx_write_hwaddr(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct smsc95xx_private *priv = dev->dev_priv; + u32 addr_lo = __get_unaligned_le32(ð->enetaddr[0]); + u32 addr_hi = __get_unaligned_le16(ð->enetaddr[4]); + int ret; + + /* set hardware address */ + debug("** %s()\n", __func__); + ret = smsc95xx_write_reg(dev, ADDRL, addr_lo); + if (ret < 0) + return ret; + + ret = smsc95xx_write_reg(dev, ADDRH, addr_hi); + if (ret < 0) + return ret; + + debug("MAC %pM\n", eth->enetaddr); + priv->have_hwaddr = 1; + return 0; +} + +/* Enable or disable Tx & Rx checksum offload engines */ +static int smsc95xx_set_csums(struct ueth_data *dev, + int use_tx_csum, int use_rx_csum) +{ + u32 read_buf; + int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); + if (ret < 0) + return ret; + + if (use_tx_csum) + read_buf |= Tx_COE_EN_; + else + read_buf &= ~Tx_COE_EN_; + + if (use_rx_csum) + read_buf |= Rx_COE_EN_; + else + read_buf &= ~Rx_COE_EN_; + + ret = smsc95xx_write_reg(dev, COE_CR, read_buf); + if (ret < 0) + return ret; + + debug("COE_CR = 0x%08x\n", read_buf); + return 0; +} + +static void smsc95xx_set_multicast(struct ueth_data *dev) +{ + struct smsc95xx_private *priv = dev->dev_priv; + + /* No multicast in u-boot */ + priv->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_); +} + +/* starts the TX path */ +static void smsc95xx_start_tx_path(struct ueth_data *dev) +{ + struct smsc95xx_private *priv = dev->dev_priv; + u32 reg_val; + + /* Enable Tx at MAC */ + priv->mac_cr |= MAC_CR_TXEN_; + + smsc95xx_write_reg(dev, MAC_CR, priv->mac_cr); + + /* Enable Tx at SCSRs */ + reg_val = TX_CFG_ON_; + smsc95xx_write_reg(dev, TX_CFG, reg_val); +} + +/* Starts the Receive path */ +static void smsc95xx_start_rx_path(struct ueth_data *dev) +{ + struct smsc95xx_private *priv = dev->dev_priv; + + priv->mac_cr |= MAC_CR_RXEN_; + smsc95xx_write_reg(dev, MAC_CR, priv->mac_cr); +} + +/* + * Smsc95xx callbacks + */ +static int smsc95xx_init(struct eth_device *eth, bd_t *bd) +{ + int ret; + u32 write_buf; + u32 read_buf; + u32 burst_cap; + int timeout; + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct smsc95xx_private *priv = + (struct smsc95xx_private *)dev->dev_priv; +#define TIMEOUT_RESOLUTION 50 /* ms */ + int link_detected; + + debug("** %s()\n", __func__); + dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */ + + write_buf = HW_CFG_LRST_; + ret = smsc95xx_write_reg(dev, HW_CFG, write_buf); + if (ret < 0) + return ret; + + timeout = 0; + do { + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) + return ret; + udelay(10 * 1000); + timeout++; + } while ((read_buf & HW_CFG_LRST_) && (timeout < 100)); + + if (timeout >= 100) { + debug("timeout waiting for completion of Lite Reset\n"); + return -1; + } + + write_buf = PM_CTL_PHY_RST_; + ret = smsc95xx_write_reg(dev, PM_CTRL, write_buf); + if (ret < 0) + return ret; + + timeout = 0; + do { + ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf); + if (ret < 0) + return ret; + udelay(10 * 1000); + timeout++; + } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100)); + if (timeout >= 100) { + debug("timeout waiting for PHY Reset\n"); + return -1; + } + if (!priv->have_hwaddr && smsc95xx_init_mac_address(eth, dev) == 0) + priv->have_hwaddr = 1; + if (!priv->have_hwaddr) { + puts("Error: SMSC95xx: No MAC address set - set usbethaddr\n"); + return -1; + } + if (smsc95xx_write_hwaddr(eth) < 0) + return -1; + + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from HW_CFG : 0x%08x\n", read_buf); + + read_buf |= HW_CFG_BIR_; + ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from HW_CFG after writing " + "HW_CFG_BIR_: 0x%08x\n", read_buf); + +#ifdef TURBO_MODE + if (dev->pusb_dev->speed == USB_SPEED_HIGH) { + burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; + priv->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; + } else { + burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; + priv->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; + } +#else + burst_cap = 0; + priv->rx_urb_size = MAX_SINGLE_PACKET_SIZE; +#endif + debug("rx_urb_size=%ld\n", (ulong)priv->rx_urb_size); + + ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf); + + read_buf = DEFAULT_BULK_IN_DELAY; + ret = smsc95xx_write_reg(dev, BULK_IN_DLY, read_buf); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from BULK_IN_DLY after writing: " + "0x%08x\n", read_buf); + + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from HW_CFG: 0x%08x\n", read_buf); + +#ifdef TURBO_MODE + read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_); +#endif + read_buf &= ~HW_CFG_RXDOFF_; + +#define NET_IP_ALIGN 0 + read_buf |= NET_IP_ALIGN << 9; + + ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf); + + write_buf = 0xFFFFFFFF; + ret = smsc95xx_write_reg(dev, INT_STS, write_buf); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(dev, ID_REV, &read_buf); + if (ret < 0) + return ret; + debug("ID_REV = 0x%08x\n", read_buf); + + /* Configure GPIO pins as LED outputs */ + write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED | + LED_GPIO_CFG_FDX_LED; + ret = smsc95xx_write_reg(dev, LED_GPIO_CFG, write_buf); + if (ret < 0) + return ret; + debug("LED_GPIO_CFG set\n"); + + /* Init Tx */ + write_buf = 0; + ret = smsc95xx_write_reg(dev, FLOW, write_buf); + if (ret < 0) + return ret; + + read_buf = AFC_CFG_DEFAULT; + ret = smsc95xx_write_reg(dev, AFC_CFG, read_buf); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(dev, MAC_CR, &priv->mac_cr); + if (ret < 0) + return ret; + + /* Init Rx. Set Vlan */ + write_buf = (u32)ETH_P_8021Q; + ret = smsc95xx_write_reg(dev, VLAN1, write_buf); + if (ret < 0) + return ret; + + /* Disable checksum offload engines */ + ret = smsc95xx_set_csums(dev, 0, 0); + if (ret < 0) { + debug("Failed to set csum offload: %d\n", ret); + return ret; + } + smsc95xx_set_multicast(dev); + + if (smsc95xx_phy_initialize(dev) < 0) + return -1; + ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); + if (ret < 0) + return ret; + + /* enable PHY interrupts */ + read_buf |= INT_EP_CTL_PHY_INT_; + + ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf); + if (ret < 0) + return ret; + + smsc95xx_start_tx_path(dev); + smsc95xx_start_rx_path(dev); + + timeout = 0; + do { + link_detected = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMSR) + & BMSR_LSTATUS; + if (!link_detected) { + if (timeout == 0) + printf("Waiting for Ethernet connection... "); + udelay(TIMEOUT_RESOLUTION * 1000); + timeout += TIMEOUT_RESOLUTION; + } + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); + if (link_detected) { + if (timeout != 0) + printf("done.\n"); + } else { + printf("unable to connect.\n"); + return -1; + } + return 0; +} + +static int smsc95xx_send(struct eth_device *eth, void* packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + int err; + int actual_len; + u32 tx_cmd_a; + u32 tx_cmd_b; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg, + PKTSIZE + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)); + + debug("** %s(), len %d, buf %#x\n", __func__, length, (int)msg); + if (length > PKTSIZE) + return -1; + + tx_cmd_a = (u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_; + tx_cmd_b = (u32)length; + cpu_to_le32s(&tx_cmd_a); + cpu_to_le32s(&tx_cmd_b); + + /* prepend cmd_a and cmd_b */ + memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a)); + memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b)); + memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet, + length); + err = usb_bulk_msg(dev->pusb_dev, + usb_sndbulkpipe(dev->pusb_dev, dev->ep_out), + (void *)msg, + length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), + &actual_len, + USB_BULK_SEND_TIMEOUT); + debug("Tx: len = %u, actual = %u, err = %d\n", + length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), + actual_len, err); + return err; +} + +static int smsc95xx_recv(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + DEFINE_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, AX_RX_URB_SIZE); + unsigned char *buf_ptr; + int err; + int actual_len; + u32 packet_len; + int cur_buf_align; + + debug("** %s()\n", __func__); + err = usb_bulk_msg(dev->pusb_dev, + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), + (void *)recv_buf, + AX_RX_URB_SIZE, + &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE, + actual_len, err); + if (err != 0) { + debug("Rx: failed to receive\n"); + return -1; + } + if (actual_len > AX_RX_URB_SIZE) { + debug("Rx: received too many bytes %d\n", actual_len); + return -1; + } + + buf_ptr = recv_buf; + while (actual_len > 0) { + /* + * 1st 4 bytes contain the length of the actual data plus error + * info. Extract data length. + */ + if (actual_len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + return -1; + } + memcpy(&packet_len, buf_ptr, sizeof(packet_len)); + le32_to_cpus(&packet_len); + if (packet_len & RX_STS_ES_) { + debug("Rx: Error header=%#x", packet_len); + return -1; + } + packet_len = ((packet_len & RX_STS_FL_) >> 16); + + if (packet_len > actual_len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + return -1; + } + + /* Notify net stack */ + NetReceive(buf_ptr + sizeof(packet_len), packet_len - 4); + + /* Adjust for next iteration */ + actual_len -= sizeof(packet_len) + packet_len; + buf_ptr += sizeof(packet_len) + packet_len; + cur_buf_align = (int)buf_ptr - (int)recv_buf; + + if (cur_buf_align & 0x03) { + int align = 4 - (cur_buf_align & 0x03); + + actual_len -= align; + buf_ptr += align; + } + } + return err; +} + +static void smsc95xx_halt(struct eth_device *eth) +{ + debug("** %s()\n", __func__); +} + +/* + * SMSC probing functions + */ +void smsc95xx_eth_before_probe(void) +{ + curr_eth_dev = 0; +} + +struct smsc95xx_dongle { + unsigned short vendor; + unsigned short product; +}; + +static const struct smsc95xx_dongle smsc95xx_dongles[] = { + { 0x0424, 0xec00 }, /* LAN9512/LAN9514 Ethernet */ + { 0x0424, 0x9500 }, /* LAN9500 Ethernet */ + { 0x0424, 0x9730 }, /* LAN9730 Ethernet (HSIC) */ + { 0x0424, 0x9900 }, /* SMSC9500 USB Ethernet Device (SAL10) */ + { 0x0000, 0x0000 } /* END - Do not remove */ +}; + +/* Probe to see if a new device is actually an SMSC device */ +int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *iface_desc; + int i; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &dev->config.if_desc[ifnum].desc; + + for (i = 0; smsc95xx_dongles[i].vendor != 0; i++) { + if (dev->descriptor.idVendor == smsc95xx_dongles[i].vendor && + dev->descriptor.idProduct == smsc95xx_dongles[i].product) + /* Found a supported dongle */ + break; + } + if (smsc95xx_dongles[i].vendor == 0) + return 0; + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Ethernet device detected\n"); + memset(ss, '\0', sizeof(struct ueth_data)); + + /* Initialize the ueth_data structure with some useful info */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->subclass = iface_desc->bInterfaceSubClass; + ss->protocol = iface_desc->bInterfaceProtocol; + + /* + * We are expecting a minimum of 3 endpoints - in, out (bulk), and int. + * We will ignore any others. + */ + for (i = 0; i < iface_desc->bNumEndpoints; i++) { + /* is it an BULK endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN) + ss->ep_in = + iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + else + ss->ep_out = + iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + + /* is it an interrupt endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ss->ep_int = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->irqinterval = iface->ep_desc[i].bInterval; + } + } + debug("Endpoints In %d Out %d Int %d\n", + ss->ep_in, ss->ep_out, ss->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || !ss->ep_int) { + debug("Problems with device\n"); + return 0; + } + dev->privptr = (void *)ss; + + /* alloc driver private */ + ss->dev_priv = calloc(1, sizeof(struct smsc95xx_private)); + if (!ss->dev_priv) + return 0; + + return 1; +} + +int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + debug("** %s()\n", __func__); + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + sprintf(eth->name, "%s%d", SMSC95XX_BASE_NAME, curr_eth_dev++); + eth->init = smsc95xx_init; + eth->send = smsc95xx_send; + eth->recv = smsc95xx_recv; + eth->halt = smsc95xx_halt; + eth->write_hwaddr = smsc95xx_write_hwaddr; + eth->priv = ss; + return 1; +} diff --git a/qemu/roms/u-boot/drivers/usb/eth/usb_ether.c b/qemu/roms/u-boot/drivers/usb/eth/usb_ether.c new file mode 100644 index 000000000..1dda54c2f --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/eth/usb_ether.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include "usb_ether.h" + +typedef void (*usb_eth_before_probe)(void); +typedef int (*usb_eth_probe)(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss); +typedef int (*usb_eth_get_info)(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *dev_desc); + +struct usb_eth_prob_dev { + usb_eth_before_probe before_probe; /* optional */ + usb_eth_probe probe; + usb_eth_get_info get_info; +}; + +/* driver functions go here, each bracketed by #ifdef CONFIG_USB_ETHER_xxx */ +static const struct usb_eth_prob_dev prob_dev[] = { +#ifdef CONFIG_USB_ETHER_ASIX + { + .before_probe = asix_eth_before_probe, + .probe = asix_eth_probe, + .get_info = asix_eth_get_info, + }, +#endif +#ifdef CONFIG_USB_ETHER_MCS7830 + { + .before_probe = mcs7830_eth_before_probe, + .probe = mcs7830_eth_probe, + .get_info = mcs7830_eth_get_info, + }, +#endif +#ifdef CONFIG_USB_ETHER_SMSC95XX + { + .before_probe = smsc95xx_eth_before_probe, + .probe = smsc95xx_eth_probe, + .get_info = smsc95xx_eth_get_info, + }, +#endif + { }, /* END */ +}; + +static int usb_max_eth_dev; /* number of highest available usb eth device */ +static struct ueth_data usb_eth[USB_MAX_ETH_DEV]; + +/******************************************************************************* + * tell if current ethernet device is a usb dongle + */ +int is_eth_dev_on_usb_host(void) +{ + int i; + struct eth_device *dev = eth_get_dev(); + + if (dev) { + for (i = 0; i < usb_max_eth_dev; i++) + if (&usb_eth[i].eth_dev == dev) + return 1; + } + return 0; +} + +/* + * Given a USB device, ask each driver if it can support it, and attach it + * to the first driver that says 'yes' + */ +static void probe_valid_drivers(struct usb_device *dev) +{ + struct eth_device *eth; + int j; + + for (j = 0; prob_dev[j].probe && prob_dev[j].get_info; j++) { + if (!prob_dev[j].probe(dev, 0, &usb_eth[usb_max_eth_dev])) + continue; + /* + * ok, it is a supported eth device. Get info and fill it in + */ + eth = &usb_eth[usb_max_eth_dev].eth_dev; + if (prob_dev[j].get_info(dev, + &usb_eth[usb_max_eth_dev], + eth)) { + /* found proper driver */ + /* register with networking stack */ + usb_max_eth_dev++; + + /* + * usb_max_eth_dev must be incremented prior to this + * call since eth_current_changed (internally called) + * relies on it + */ + eth_register(eth); + if (eth_write_hwaddr(eth, "usbeth", + usb_max_eth_dev - 1)) + puts("Warning: failed to set MAC address\n"); + break; + } + } + } + +/******************************************************************************* + * scan the usb and reports device info + * to the user if mode = 1 + * returns current device or -1 if no + */ +int usb_host_eth_scan(int mode) +{ + int i, old_async; + struct usb_device *dev; + + + if (mode == 1) + printf(" scanning usb for ethernet devices... "); + + old_async = usb_disable_asynch(1); /* asynch transfer not allowed */ + + /* unregister a previously detected device */ + for (i = 0; i < usb_max_eth_dev; i++) + eth_unregister(&usb_eth[i].eth_dev); + + memset(usb_eth, 0, sizeof(usb_eth)); + + for (i = 0; prob_dev[i].probe; i++) { + if (prob_dev[i].before_probe) + prob_dev[i].before_probe(); + } + + usb_max_eth_dev = 0; + for (i = 0; i < USB_MAX_DEVICE; i++) { + dev = usb_get_dev_index(i); /* get device */ + debug("i=%d\n", i); + if (dev == NULL) + break; /* no more devices available */ + + /* find valid usb_ether driver for this device, if any */ + probe_valid_drivers(dev); + + /* check limit */ + if (usb_max_eth_dev == USB_MAX_ETH_DEV) { + printf("max USB Ethernet Device reached: %d stopping\n", + usb_max_eth_dev); + break; + } + } /* for */ + + usb_disable_asynch(old_async); /* restore asynch value */ + printf("%d Ethernet Device(s) found\n", usb_max_eth_dev); + if (usb_max_eth_dev > 0) + return 0; + return -1; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/Makefile b/qemu/roms/u-boot/drivers/usb/gadget/Makefile new file mode 100644 index 000000000..896c8d407 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/Makefile @@ -0,0 +1,38 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_USB_GADGET) += epautoconf.o config.o usbstring.o +obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o + +# new USB gadget layer dependencies +ifdef CONFIG_USB_GADGET +obj-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o +obj-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +obj-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o +obj-$(CONFIG_CI_UDC) += ci_udc.o +obj-$(CONFIG_THOR_FUNCTION) += f_thor.o +obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o +obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o +obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o +endif +ifdef CONFIG_USB_ETHER +obj-y += ether.o +obj-$(CONFIG_USB_ETH_RNDIS) += rndis.o +obj-$(CONFIG_CI_UDC) += ci_udc.o +obj-$(CONFIG_CPU_PXA25X) += pxa25x_udc.o +else +# Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE +ifdef CONFIG_USB_DEVICE +obj-y += core.o +obj-y += ep0.o +obj-$(CONFIG_DW_UDC) += designware_udc.o +obj-$(CONFIG_OMAP1510) += omap1510_udc.o +obj-$(CONFIG_OMAP1610) += omap1510_udc.o +obj-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o +obj-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o +endif +endif diff --git a/qemu/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c b/qemu/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c new file mode 100644 index 000000000..c99208d10 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.c @@ -0,0 +1,1306 @@ +/* + * Driver for the Atmel USBA high speed USB device controller + * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.c] + * + * Copyright (C) 2005-2013 Atmel Corporation + * Bo Shen + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atmel_usba_udc.h" + +static int vbus_is_present(struct usba_udc *udc) +{ + /* No Vbus detection: Assume always present */ + return 1; +} + +static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req) +{ + unsigned int transaction_len; + + transaction_len = req->req.length - req->req.actual; + req->last_transaction = 1; + if (transaction_len > ep->ep.maxpacket) { + transaction_len = ep->ep.maxpacket; + req->last_transaction = 0; + } else if (transaction_len == ep->ep.maxpacket && req->req.zero) { + req->last_transaction = 0; + } + + DBG(DBG_QUEUE, "%s: submit_transaction, req %p (length %d)%s\n", + ep->ep.name, req, transaction_len, + req->last_transaction ? ", done" : ""); + + memcpy(ep->fifo, req->req.buf + req->req.actual, transaction_len); + usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); + req->req.actual += transaction_len; +} + +static void submit_request(struct usba_ep *ep, struct usba_request *req) +{ + DBG(DBG_QUEUE, "%s: submit_request: req %p (length %d), dma: %d\n", + ep->ep.name, req, req->req.length, req->using_dma); + + req->req.actual = 0; + req->submitted = 1; + + next_fifo_transaction(ep, req); + if (req->last_transaction) { + usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); + usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); + } else { + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); + } +} + +static void submit_next_request(struct usba_ep *ep) +{ + struct usba_request *req; + + if (list_empty(&ep->queue)) { + usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY); + return; + } + + req = list_entry(ep->queue.next, struct usba_request, queue); + if (!req->submitted) + submit_request(ep, req); +} + +static void send_status(struct usba_udc *udc, struct usba_ep *ep) +{ + ep->state = STATUS_STAGE_IN; + usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); + usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); +} + +static void receive_data(struct usba_ep *ep) +{ + struct usba_udc *udc = ep->udc; + struct usba_request *req; + unsigned long status; + unsigned int bytecount, nr_busy; + int is_complete = 0; + + status = usba_ep_readl(ep, STA); + nr_busy = USBA_BFEXT(BUSY_BANKS, status); + + DBG(DBG_QUEUE, "receive data: nr_busy=%u\n", nr_busy); + + while (nr_busy > 0) { + if (list_empty(&ep->queue)) { + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + break; + } + req = list_entry(ep->queue.next, + struct usba_request, queue); + + bytecount = USBA_BFEXT(BYTE_COUNT, status); + + if (status & USBA_SHORT_PACKET) + is_complete = 1; + if (req->req.actual + bytecount >= req->req.length) { + is_complete = 1; + bytecount = req->req.length - req->req.actual; + } + + memcpy(req->req.buf + req->req.actual, ep->fifo, bytecount); + req->req.actual += bytecount; + + usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + + if (is_complete) { + DBG(DBG_QUEUE, "%s: request done\n", ep->ep.name); + req->req.status = 0; + list_del_init(&req->queue); + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + spin_lock(&udc->lock); + req->req.complete(&ep->ep, &req->req); + spin_unlock(&udc->lock); + } + + status = usba_ep_readl(ep, STA); + nr_busy = USBA_BFEXT(BUSY_BANKS, status); + + if (is_complete && ep_is_control(ep)) { + send_status(udc, ep); + break; + } + } +} + +static void +request_complete(struct usba_ep *ep, struct usba_request *req, int status) +{ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + + DBG(DBG_GADGET | DBG_REQ, "%s: req %p complete: status %d, actual %u\n", + ep->ep.name, req, req->req.status, req->req.actual); + + req->req.complete(&ep->ep, &req->req); +} + +static void +request_complete_list(struct usba_ep *ep, struct list_head *list, int status) +{ + struct usba_request *req, *tmp_req; + + list_for_each_entry_safe(req, tmp_req, list, queue) { + list_del_init(&req->queue); + request_complete(ep, req, status); + } +} + +static int +usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_udc *udc = ep->udc; + unsigned long flags, ept_cfg, maxpacket; + unsigned int nr_trans; + + DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc); + + maxpacket = usb_endpoint_maxp(desc) & 0x7ff; + + if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) + != ep->index) || + ep->index == 0 || + desc->bDescriptorType != USB_DT_ENDPOINT || + maxpacket == 0 || + maxpacket > ep->fifo_size) { + DBG(DBG_ERR, "ep_enable: Invalid argument"); + return -EINVAL; + } + + ep->is_isoc = 0; + ep->is_in = 0; + + if (maxpacket <= 8) + ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8); + else + /* LSB is bit 1, not 0 */ + ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3); + + DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n", + ep->ep.name, ept_cfg, maxpacket); + + if (usb_endpoint_dir_in(desc)) { + ep->is_in = 1; + ept_cfg |= USBA_EPT_DIR_IN; + } + + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_CONTROL: + ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL); + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); + break; + case USB_ENDPOINT_XFER_ISOC: + if (!ep->can_isoc) { + DBG(DBG_ERR, "ep_enable: %s is not isoc capable\n", + ep->ep.name); + return -EINVAL; + } + + /* + * Bits 11:12 specify number of _additional_ + * transactions per microframe. + */ + nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1; + if (nr_trans > 3) + return -EINVAL; + + ep->is_isoc = 1; + ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO); + + /* + * Do triple-buffering on high-bandwidth iso endpoints. + */ + if (nr_trans > 1 && ep->nr_banks == 3) + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE); + else + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE); + ept_cfg |= USBA_BF(NB_TRANS, nr_trans); + break; + case USB_ENDPOINT_XFER_BULK: + ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK); + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); + break; + case USB_ENDPOINT_XFER_INT: + ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT); + ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); + break; + } + + spin_lock_irqsave(&ep->udc->lock, flags); + + ep->desc = desc; + ep->ep.maxpacket = maxpacket; + + usba_ep_writel(ep, CFG, ept_cfg); + usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); + + usba_writel(udc, INT_ENB, + (usba_readl(udc, INT_ENB) + | USBA_BF(EPT_INT, 1 << ep->index))); + + spin_unlock_irqrestore(&udc->lock, flags); + + DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index, + (unsigned long)usba_ep_readl(ep, CFG)); + DBG(DBG_HW, "INT_ENB after init: %#08lx\n", + (unsigned long)usba_readl(udc, INT_ENB)); + + return 0; +} + +static int usba_ep_disable(struct usb_ep *_ep) +{ + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_udc *udc = ep->udc; + LIST_HEAD(req_list); + unsigned long flags; + + DBG(DBG_GADGET, "ep_disable: %s\n", ep->ep.name); + + spin_lock_irqsave(&udc->lock, flags); + + if (!ep->desc) { + spin_unlock_irqrestore(&udc->lock, flags); + /* REVISIT because this driver disables endpoints in + * reset_all_endpoints() before calling disconnect(), + * most gadget drivers would trigger this non-error ... + */ + if (udc->gadget.speed != USB_SPEED_UNKNOWN) + DBG(DBG_ERR, "ep_disable: %s not enabled\n", + ep->ep.name); + return -EINVAL; + } + ep->desc = NULL; + + list_splice_init(&ep->queue, &req_list); + usba_ep_writel(ep, CFG, 0); + usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE); + usba_writel(udc, INT_ENB, + usba_readl(udc, INT_ENB) & + ~USBA_BF(EPT_INT, 1 << ep->index)); + + request_complete_list(ep, &req_list, -ESHUTDOWN); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static struct usb_request * +usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct usba_request *req; + + DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags); + + req = malloc(sizeof(struct usba_request)); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void +usba_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct usba_request *req = to_usba_req(_req); + + DBG(DBG_GADGET, "ep_free_request: %p, %p\n", _ep, _req); + + free(req); +} + +static int +usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct usba_request *req = to_usba_req(_req); + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_udc *udc = ep->udc; + unsigned long flags; + int ret; + + DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, "%s: queue req %p, len %u\n", + ep->ep.name, req, _req->length); + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || + !ep->desc) + return -ESHUTDOWN; + + req->submitted = 0; + req->using_dma = 0; + req->last_transaction = 0; + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* May have received a reset since last time we checked */ + ret = -ESHUTDOWN; + spin_lock_irqsave(&udc->lock, flags); + if (ep->desc) { + list_add_tail(&req->queue, &ep->queue); + + if ((!ep_is_control(ep) && ep->is_in) || + (ep_is_control(ep) && (ep->state == DATA_STAGE_IN || + ep->state == STATUS_STAGE_IN))) + usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); + else + usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY); + + ret = 0; + } + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_request *req = to_usba_req(_req); + + DBG(DBG_GADGET | DBG_QUEUE, "ep_dequeue: %s, req %p\n", + ep->ep.name, req); + + /* + * Errors should stop the queue from advancing until the + * completion function returns. + */ + list_del_init(&req->queue); + + request_complete(ep, req, -ECONNRESET); + + /* Process the next request if any */ + submit_next_request(ep); + + return 0; +} + +static int usba_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct usba_ep *ep = to_usba_ep(_ep); + unsigned long flags; + int ret = 0; + + DBG(DBG_GADGET, "endpoint %s: %s HALT\n", ep->ep.name, + value ? "set" : "clear"); + + if (!ep->desc) { + DBG(DBG_ERR, "Attempted to halt uninitialized ep %s\n", + ep->ep.name); + return -ENODEV; + } + + if (ep->is_isoc) { + DBG(DBG_ERR, "Attempted to halt isochronous ep %s\n", + ep->ep.name); + return -ENOTTY; + } + + spin_lock_irqsave(&udc->lock, flags); + + /* + * We can't halt IN endpoints while there are still data to be + * transferred + */ + if (!list_empty(&ep->queue) || + ((value && ep->is_in && (usba_ep_readl(ep, STA) & + USBA_BF(BUSY_BANKS, -1L))))) { + ret = -EAGAIN; + } else { + if (value) + usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL); + else + usba_ep_writel(ep, CLR_STA, + USBA_FORCE_STALL | USBA_TOGGLE_CLR); + usba_ep_readl(ep, STA); + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +static int usba_ep_fifo_status(struct usb_ep *_ep) +{ + struct usba_ep *ep = to_usba_ep(_ep); + + return USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA)); +} + +static void usba_ep_fifo_flush(struct usb_ep *_ep) +{ + struct usba_ep *ep = to_usba_ep(_ep); + struct usba_udc *udc = ep->udc; + + usba_writel(udc, EPT_RST, 1 << ep->index); +} + +static const struct usb_ep_ops usba_ep_ops = { + .enable = usba_ep_enable, + .disable = usba_ep_disable, + .alloc_request = usba_ep_alloc_request, + .free_request = usba_ep_free_request, + .queue = usba_ep_queue, + .dequeue = usba_ep_dequeue, + .set_halt = usba_ep_set_halt, + .fifo_status = usba_ep_fifo_status, + .fifo_flush = usba_ep_fifo_flush, +}; + +static int usba_udc_get_frame(struct usb_gadget *gadget) +{ + struct usba_udc *udc = to_usba_udc(gadget); + + return USBA_BFEXT(FRAME_NUMBER, usba_readl(udc, FNUM)); +} + +static int usba_udc_wakeup(struct usb_gadget *gadget) +{ + struct usba_udc *udc = to_usba_udc(gadget); + unsigned long flags; + u32 ctrl; + int ret = -EINVAL; + + spin_lock_irqsave(&udc->lock, flags); + if (udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { + ctrl = usba_readl(udc, CTRL); + usba_writel(udc, CTRL, ctrl | USBA_REMOTE_WAKE_UP); + ret = 0; + } + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +static int +usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ + struct usba_udc *udc = to_usba_udc(gadget); + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + if (is_selfpowered) + udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; + else + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops usba_udc_ops = { + .get_frame = usba_udc_get_frame, + .wakeup = usba_udc_wakeup, + .set_selfpowered = usba_udc_set_selfpowered, +}; + +static struct usb_endpoint_descriptor usba_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(64), + /* FIXME: I have no idea what to put here */ + .bInterval = 1, +}; + +/* + * Called with interrupts disabled and udc->lock held. + */ +static void reset_all_endpoints(struct usba_udc *udc) +{ + struct usba_ep *ep; + struct usba_request *req, *tmp_req; + + usba_writel(udc, EPT_RST, ~0UL); + + ep = to_usba_ep(udc->gadget.ep0); + list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) { + list_del_init(&req->queue); + request_complete(ep, req, -ECONNRESET); + } + + /* NOTE: normally, the next call to the gadget driver is in + * charge of disabling endpoints... usually disconnect(). + * The exception would be entering a high speed test mode. + * + * FIXME remove this code ... and retest thoroughly. + */ + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { + if (ep->desc) { + spin_unlock(&udc->lock); + usba_ep_disable(&ep->ep); + spin_lock(&udc->lock); + } + } +} + +static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex) +{ + struct usba_ep *ep; + + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) + return to_usba_ep(udc->gadget.ep0); + + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { + u8 bEndpointAddress; + + if (!ep->desc) + continue; + bEndpointAddress = ep->desc->bEndpointAddress; + if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) + continue; + if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) + == (wIndex & USB_ENDPOINT_NUMBER_MASK)) + return ep; + } + + return NULL; +} + +/* Called with interrupts disabled and udc->lock held */ +static inline void set_protocol_stall(struct usba_udc *udc, struct usba_ep *ep) +{ + usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL); + ep->state = WAIT_FOR_SETUP; +} + +static inline int is_stalled(struct usba_udc *udc, struct usba_ep *ep) +{ + if (usba_ep_readl(ep, STA) & USBA_FORCE_STALL) + return 1; + return 0; +} + +static inline void set_address(struct usba_udc *udc, unsigned int addr) +{ + u32 regval; + + DBG(DBG_BUS, "setting address %u...\n", addr); + regval = usba_readl(udc, CTRL); + regval = USBA_BFINS(DEV_ADDR, addr, regval); + usba_writel(udc, CTRL, regval); +} + +static int do_test_mode(struct usba_udc *udc) +{ + static const char test_packet_buffer[] = { + /* JKJKJKJK * 9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* JJKKJJKK * 8 */ + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + /* JJKKJJKK * 8 */ + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + /* JJJJJJJKKKKKKK * 8 */ + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /* JJJJJJJK * 8 */ + 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, + /* {JKKKKKKK * 10}, JK */ + 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E + }; + struct usba_ep *ep; + int test_mode; + + test_mode = udc->test_mode; + + /* Start from a clean slate */ + reset_all_endpoints(udc); + + switch (test_mode) { + case 0x0100: + /* Test_J */ + usba_writel(udc, TST, USBA_TST_J_MODE); + DBG(DBG_ALL, "Entering Test_J mode...\n"); + break; + case 0x0200: + /* Test_K */ + usba_writel(udc, TST, USBA_TST_K_MODE); + DBG(DBG_ALL, "Entering Test_K mode...\n"); + break; + case 0x0300: + /* + * Test_SE0_NAK: Force high-speed mode and set up ep0 + * for Bulk IN transfers + */ + ep = &udc->usba_ep[0]; + usba_writel(udc, TST, + USBA_BF(SPEED_CFG, USBA_SPEED_CFG_FORCE_HIGH)); + usba_ep_writel(ep, CFG, + USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64) + | USBA_EPT_DIR_IN + | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK) + | USBA_BF(BK_NUMBER, 1)); + if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) { + set_protocol_stall(udc, ep); + DBG(DBG_ALL, "Test_SE0_NAK: ep0 not mapped\n"); + } else { + usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); + DBG(DBG_ALL, "Entering Test_SE0_NAK mode...\n"); + } + break; + case 0x0400: + /* Test_Packet */ + ep = &udc->usba_ep[0]; + usba_ep_writel(ep, CFG, + USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64) + | USBA_EPT_DIR_IN + | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK) + | USBA_BF(BK_NUMBER, 1)); + if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) { + set_protocol_stall(udc, ep); + DBG(DBG_ALL, "Test_Packet: ep0 not mapped\n"); + } else { + usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); + usba_writel(udc, TST, USBA_TST_PKT_MODE); + memcpy(ep->fifo, test_packet_buffer, + sizeof(test_packet_buffer)); + usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); + DBG(DBG_ALL, "Entering Test_Packet mode...\n"); + } + break; + default: + DBG(DBG_ERR, "Invalid test mode: 0x%04x\n", test_mode); + return -EINVAL; + } + + return 0; +} + +/* Avoid overly long expressions */ +static inline bool feature_is_dev_remote_wakeup(struct usb_ctrlrequest *crq) +{ + if (crq->wValue == cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP)) + return true; + return false; +} + +static inline bool feature_is_dev_test_mode(struct usb_ctrlrequest *crq) +{ + if (crq->wValue == cpu_to_le16(USB_DEVICE_TEST_MODE)) + return true; + return false; +} + +static inline bool feature_is_ep_halt(struct usb_ctrlrequest *crq) +{ + if (crq->wValue == cpu_to_le16(USB_ENDPOINT_HALT)) + return true; + return false; +} + +static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep, + struct usb_ctrlrequest *crq) +{ + int retval = 0; + + switch (crq->bRequest) { + case USB_REQ_GET_STATUS: { + u16 status; + + if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) { + status = cpu_to_le16(udc->devstatus); + } else if (crq->bRequestType + == (USB_DIR_IN | USB_RECIP_INTERFACE)) { + status = cpu_to_le16(0); + } else if (crq->bRequestType + == (USB_DIR_IN | USB_RECIP_ENDPOINT)) { + struct usba_ep *target; + + target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); + if (!target) + goto stall; + + status = 0; + if (is_stalled(udc, target)) + status |= cpu_to_le16(1); + } else { + goto delegate; + } + + /* Write directly to the FIFO. No queueing is done. */ + if (crq->wLength != cpu_to_le16(sizeof(status))) + goto stall; + ep->state = DATA_STAGE_IN; + __raw_writew(status, ep->fifo); + usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); + break; + } + + case USB_REQ_CLEAR_FEATURE: { + if (crq->bRequestType == USB_RECIP_DEVICE) { + if (feature_is_dev_remote_wakeup(crq)) + udc->devstatus + &= ~(1 << USB_DEVICE_REMOTE_WAKEUP); + else + /* Can't CLEAR_FEATURE TEST_MODE */ + goto stall; + } else if (crq->bRequestType == USB_RECIP_ENDPOINT) { + struct usba_ep *target; + + if (crq->wLength != cpu_to_le16(0) || + !feature_is_ep_halt(crq)) + goto stall; + target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); + if (!target) + goto stall; + + usba_ep_writel(target, CLR_STA, USBA_FORCE_STALL); + if (target->index != 0) + usba_ep_writel(target, CLR_STA, + USBA_TOGGLE_CLR); + } else { + goto delegate; + } + + send_status(udc, ep); + break; + } + + case USB_REQ_SET_FEATURE: { + if (crq->bRequestType == USB_RECIP_DEVICE) { + if (feature_is_dev_test_mode(crq)) { + send_status(udc, ep); + ep->state = STATUS_STAGE_TEST; + udc->test_mode = le16_to_cpu(crq->wIndex); + return 0; + } else if (feature_is_dev_remote_wakeup(crq)) { + udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP; + } else { + goto stall; + } + } else if (crq->bRequestType == USB_RECIP_ENDPOINT) { + struct usba_ep *target; + + if (crq->wLength != cpu_to_le16(0) || + !feature_is_ep_halt(crq)) + goto stall; + + target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); + if (!target) + goto stall; + + usba_ep_writel(target, SET_STA, USBA_FORCE_STALL); + } else { + goto delegate; + } + + send_status(udc, ep); + break; + } + + case USB_REQ_SET_ADDRESS: + if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) + goto delegate; + + set_address(udc, le16_to_cpu(crq->wValue)); + send_status(udc, ep); + ep->state = STATUS_STAGE_ADDR; + break; + + default: +delegate: + spin_unlock(&udc->lock); + retval = udc->driver->setup(&udc->gadget, crq); + spin_lock(&udc->lock); + } + + return retval; + +stall: + DBG(DBG_ALL, "%s: Invalid setup request: %02x.%02x v%04x i%04x l%d\n", + ep->ep.name, crq->bRequestType, crq->bRequest, + le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex), + le16_to_cpu(crq->wLength)); + set_protocol_stall(udc, ep); + + return -1; +} + +static void usba_control_irq(struct usba_udc *udc, struct usba_ep *ep) +{ + struct usba_request *req; + u32 epstatus; + u32 epctrl; + +restart: + epstatus = usba_ep_readl(ep, STA); + epctrl = usba_ep_readl(ep, CTL); + + DBG(DBG_INT, "%s [%d]: s/%08x c/%08x\n", + ep->ep.name, ep->state, epstatus, epctrl); + + req = NULL; + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct usba_request, queue); + + if ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) { + if (req->submitted) + next_fifo_transaction(ep, req); + else + submit_request(ep, req); + + if (req->last_transaction) { + usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); + usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); + } + goto restart; + } + if ((epstatus & epctrl) & USBA_TX_COMPLETE) { + usba_ep_writel(ep, CLR_STA, USBA_TX_COMPLETE); + + switch (ep->state) { + case DATA_STAGE_IN: + usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY); + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + ep->state = STATUS_STAGE_OUT; + break; + case STATUS_STAGE_ADDR: + /* Activate our new address */ + usba_writel(udc, CTRL, (usba_readl(udc, CTRL) + | USBA_FADDR_EN)); + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + ep->state = WAIT_FOR_SETUP; + break; + case STATUS_STAGE_IN: + if (req) { + list_del_init(&req->queue); + request_complete(ep, req, 0); + submit_next_request(ep); + } + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + ep->state = WAIT_FOR_SETUP; + break; + case STATUS_STAGE_TEST: + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + ep->state = WAIT_FOR_SETUP; + if (do_test_mode(udc)) + set_protocol_stall(udc, ep); + break; + default: + DBG(DBG_ALL, "%s: TXCOMP: Invalid endpoint state %d\n", + ep->ep.name, ep->state); + set_protocol_stall(udc, ep); + break; + } + + goto restart; + } + if ((epstatus & epctrl) & USBA_RX_BK_RDY) { + switch (ep->state) { + case STATUS_STAGE_OUT: + usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + + if (req) { + list_del_init(&req->queue); + request_complete(ep, req, 0); + } + ep->state = WAIT_FOR_SETUP; + break; + + case DATA_STAGE_OUT: + receive_data(ep); + break; + + default: + usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + DBG(DBG_ALL, "%s: RXRDY: Invalid endpoint state %d\n", + ep->ep.name, ep->state); + set_protocol_stall(udc, ep); + break; + } + + goto restart; + } + if (epstatus & USBA_RX_SETUP) { + union { + struct usb_ctrlrequest crq; + unsigned long data[2]; + } crq; + unsigned int pkt_len; + int ret; + + if (ep->state != WAIT_FOR_SETUP) { + /* + * Didn't expect a SETUP packet at this + * point. Clean up any pending requests (which + * may be successful). + */ + int status = -EPROTO; + + /* + * RXRDY and TXCOMP are dropped when SETUP + * packets arrive. Just pretend we received + * the status packet. + */ + if (ep->state == STATUS_STAGE_OUT || + ep->state == STATUS_STAGE_IN) { + usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + status = 0; + } + + if (req) { + list_del_init(&req->queue); + request_complete(ep, req, status); + } + } + + pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA)); + DBG(DBG_HW, "Packet length: %u\n", pkt_len); + if (pkt_len != sizeof(crq)) { + DBG(DBG_ALL, "udc: Invalid length %u (expected %zu)\n", + pkt_len, sizeof(crq)); + set_protocol_stall(udc, ep); + return; + } + + DBG(DBG_FIFO, "Copying ctrl request from 0x%p:\n", ep->fifo); + memcpy(crq.data, ep->fifo, sizeof(crq)); + + /* Free up one bank in the FIFO so that we can + * generate or receive a reply right away. */ + usba_ep_writel(ep, CLR_STA, USBA_RX_SETUP); + + if (crq.crq.bRequestType & USB_DIR_IN) { + /* + * The USB 2.0 spec states that "if wLength is + * zero, there is no data transfer phase." + * However, testusb #14 seems to actually + * expect a data phase even if wLength = 0... + */ + ep->state = DATA_STAGE_IN; + } else { + if (crq.crq.wLength != cpu_to_le16(0)) + ep->state = DATA_STAGE_OUT; + else + ep->state = STATUS_STAGE_IN; + } + + ret = -1; + if (ep->index == 0) { + ret = handle_ep0_setup(udc, ep, &crq.crq); + } else { + spin_unlock(&udc->lock); + ret = udc->driver->setup(&udc->gadget, &crq.crq); + spin_lock(&udc->lock); + } + + DBG(DBG_BUS, "req %02x.%02x, length %d, state %d, ret %d\n", + crq.crq.bRequestType, crq.crq.bRequest, + le16_to_cpu(crq.crq.wLength), ep->state, ret); + + if (ret < 0) { + /* Let the host know that we failed */ + set_protocol_stall(udc, ep); + } + } +} + +static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep) +{ + struct usba_request *req; + u32 epstatus; + u32 epctrl; + + epstatus = usba_ep_readl(ep, STA); + epctrl = usba_ep_readl(ep, CTL); + + DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", ep->ep.name, epstatus); + + while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) { + DBG(DBG_BUS, "%s: TX PK ready\n", ep->ep.name); + + if (list_empty(&ep->queue)) { + DBG(DBG_INT, "ep_irq: queue empty\n"); + usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); + return; + } + + req = list_entry(ep->queue.next, struct usba_request, queue); + + if (req->submitted) + next_fifo_transaction(ep, req); + else + submit_request(ep, req); + + if (req->last_transaction) { + list_del_init(&req->queue); + submit_next_request(ep); + request_complete(ep, req, 0); + } + + epstatus = usba_ep_readl(ep, STA); + epctrl = usba_ep_readl(ep, CTL); + } + + if ((epstatus & epctrl) & USBA_RX_BK_RDY) { + DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name); + receive_data(ep); + usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + } +} + +static int usba_udc_irq(struct usba_udc *udc) +{ + u32 status, ep_status; + + spin_lock(&udc->lock); + + status = usba_readl(udc, INT_STA); + DBG(DBG_INT, "irq, status=%#08x\n", status); + + if (status & USBA_DET_SUSPEND) { + usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); + DBG(DBG_BUS, "Suspend detected\n"); + if (udc->gadget.speed != USB_SPEED_UNKNOWN && + udc->driver && udc->driver->suspend) { + spin_unlock(&udc->lock); + udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } + } + + if (status & USBA_WAKE_UP) { + usba_writel(udc, INT_CLR, USBA_WAKE_UP); + DBG(DBG_BUS, "Wake Up CPU detected\n"); + } + + if (status & USBA_END_OF_RESUME) { + usba_writel(udc, INT_CLR, USBA_END_OF_RESUME); + DBG(DBG_BUS, "Resume detected\n"); + if (udc->gadget.speed != USB_SPEED_UNKNOWN && + udc->driver && udc->driver->resume) { + spin_unlock(&udc->lock); + udc->driver->resume(&udc->gadget); + spin_lock(&udc->lock); + } + } + + ep_status = USBA_BFEXT(EPT_INT, status); + if (ep_status) { + int i; + + for (i = 0; i < USBA_NR_ENDPOINTS; i++) + if (ep_status & (1 << i)) { + if (ep_is_control(&udc->usba_ep[i])) + usba_control_irq(udc, &udc->usba_ep[i]); + else + usba_ep_irq(udc, &udc->usba_ep[i]); + } + } + + if (status & USBA_END_OF_RESET) { + struct usba_ep *ep0; + + usba_writel(udc, INT_CLR, USBA_END_OF_RESET); + reset_all_endpoints(udc); + + if (udc->gadget.speed != USB_SPEED_UNKNOWN && + udc->driver->disconnect) { + udc->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock(&udc->lock); + udc->driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + } + + if (status & USBA_HIGH_SPEED) + udc->gadget.speed = USB_SPEED_HIGH; + else + udc->gadget.speed = USB_SPEED_FULL; + + ep0 = &udc->usba_ep[0]; + ep0->desc = &usba_ep0_desc; + ep0->state = WAIT_FOR_SETUP; + usba_ep_writel(ep0, CFG, + (USBA_BF(EPT_SIZE, EP0_EPT_SIZE) + | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL) + | USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE))); + usba_ep_writel(ep0, CTL_ENB, + USBA_EPT_ENABLE | USBA_RX_SETUP); + usba_writel(udc, INT_ENB, + (usba_readl(udc, INT_ENB) + | USBA_BF(EPT_INT, 1) + | USBA_DET_SUSPEND + | USBA_END_OF_RESUME)); + + /* + * Unclear why we hit this irregularly, e.g. in usbtest, + * but it's clearly harmless... + */ + if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED)) + DBG(DBG_ALL, "ODD: EP0 configuration is invalid!\n"); + } + + spin_unlock(&udc->lock); + + return 0; +} + +static int atmel_usba_start(struct usba_udc *udc) +{ + udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; + + udc->vbus_prev = 0; + + /* If Vbus is present, enable the controller and wait for reset */ + if (vbus_is_present(udc) && udc->vbus_prev == 0) { + usba_writel(udc, CTRL, USBA_ENABLE_MASK); + usba_writel(udc, INT_ENB, USBA_END_OF_RESET); + } + + return 0; +} + +static int atmel_usba_stop(struct usba_udc *udc) +{ + udc->gadget.speed = USB_SPEED_UNKNOWN; + reset_all_endpoints(udc); + + /* This will also disable the DP pullup */ + usba_writel(udc, CTRL, USBA_DISABLE_MASK); + + return 0; +} + +static struct usba_udc controller = { + .regs = (unsigned *)ATMEL_BASE_UDPHS, + .fifo = (unsigned *)ATMEL_BASE_UDPHS_FIFO, + .gadget = { + .ops = &usba_udc_ops, + .ep_list = LIST_HEAD_INIT(controller.gadget.ep_list), + .speed = USB_SPEED_HIGH, + .is_dualspeed = 1, + .name = "atmel_usba_udc", + }, +}; + +int usb_gadget_handle_interrupts(void) +{ + struct usba_udc *udc = &controller; + + return usba_udc_irq(udc); +} + + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct usba_udc *udc = &controller; + int ret; + + if (!driver || !driver->bind || !driver->setup) { + printf("bad paramter\n"); + return -EINVAL; + } + + if (udc->driver) { + printf("UDC already has a gadget driver\n"); + return -EBUSY; + } + + atmel_usba_start(udc); + + udc->driver = driver; + + ret = driver->bind(&udc->gadget); + if (ret) { + error("driver->bind() returned %d\n", ret); + udc->driver = NULL; + } + + return ret; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usba_udc *udc = &controller; + + if (!driver || !driver->unbind || !driver->disconnect) { + error("bad paramter\n"); + return -EINVAL; + } + + driver->disconnect(&udc->gadget); + driver->unbind(&udc->gadget); + udc->driver = NULL; + + atmel_usba_stop(udc); + + return 0; +} + +static struct usba_ep *usba_udc_pdata(struct usba_platform_data *pdata, + struct usba_udc *udc) +{ + struct usba_ep *eps; + int i; + + eps = malloc(sizeof(struct usba_ep) * pdata->num_ep); + if (!eps) { + error("failed to alloc eps\n"); + return NULL; + } + + udc->gadget.ep0 = &eps[0].ep; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&eps[0].ep.ep_list); + + for (i = 0; i < pdata->num_ep; i++) { + struct usba_ep *ep = &eps[i]; + + ep->ep_regs = udc->regs + USBA_EPT_BASE(i); + ep->dma_regs = udc->regs + USBA_DMA_BASE(i); + ep->fifo = udc->fifo + USBA_FIFO_BASE(i); + ep->ep.ops = &usba_ep_ops; + ep->ep.name = pdata->ep[i].name; + ep->ep.maxpacket = pdata->ep[i].fifo_size; + ep->fifo_size = ep->ep.maxpacket; + ep->udc = udc; + INIT_LIST_HEAD(&ep->queue); + ep->nr_banks = pdata->ep[i].nr_banks; + ep->index = pdata->ep[i].index; + ep->can_dma = pdata->ep[i].can_dma; + ep->can_isoc = pdata->ep[i].can_isoc; + if (i) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + }; + + return eps; +} + +int usba_udc_probe(struct usba_platform_data *pdata) +{ + struct usba_udc *udc; + + udc = &controller; + + udc->usba_ep = usba_udc_pdata(pdata, udc); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h b/qemu/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h new file mode 100644 index 000000000..92e462db6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/atmel_usba_udc.h @@ -0,0 +1,326 @@ +/* + * Register definition for Atmel USBA high speed USB device controller + * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.h] + * + * Copyright (C) 2005-2013 Atmel Corporation + * Bo Shen + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LINUX_USB_GADGET_USBA_UDC_H__ +#define __LINUX_USB_GADGET_USBA_UDC_H__ + +/* USB register offsets */ +#define USBA_CTRL 0x0000 +#define USBA_FNUM 0x0004 +#define USBA_INT_ENB 0x0010 +#define USBA_INT_STA 0x0014 +#define USBA_INT_CLR 0x0018 +#define USBA_EPT_RST 0x001c +#define USBA_TST 0x00e0 + +/* USB endpoint register offsets */ +#define USBA_EPT_CFG 0x0000 +#define USBA_EPT_CTL_ENB 0x0004 +#define USBA_EPT_CTL_DIS 0x0008 +#define USBA_EPT_CTL 0x000c +#define USBA_EPT_SET_STA 0x0014 +#define USBA_EPT_CLR_STA 0x0018 +#define USBA_EPT_STA 0x001c + +/* USB DMA register offsets */ +#define USBA_DMA_NXT_DSC 0x0000 +#define USBA_DMA_ADDRESS 0x0004 +#define USBA_DMA_CONTROL 0x0008 +#define USBA_DMA_STATUS 0x000c + +/* Bitfields in CTRL */ +#define USBA_DEV_ADDR_OFFSET 0 +#define USBA_DEV_ADDR_SIZE 7 +#define USBA_FADDR_EN (1 << 7) +#define USBA_EN_USBA (1 << 8) +#define USBA_DETACH (1 << 9) +#define USBA_REMOTE_WAKE_UP (1 << 10) +#define USBA_PULLD_DIS (1 << 11) + +#if defined(CONFIG_AVR32) +#define USBA_ENABLE_MASK USBA_EN_USBA +#define USBA_DISABLE_MASK 0 +#elif defined(CONFIG_AT91FAMILY) +#define USBA_ENABLE_MASK (USBA_EN_USBA | USBA_PULLD_DIS) +#define USBA_DISABLE_MASK USBA_DETACH +#endif /* CONFIG_ARCH_AT91 */ + +/* Bitfields in FNUM */ +#define USBA_MICRO_FRAME_NUM_OFFSET 0 +#define USBA_MICRO_FRAME_NUM_SIZE 3 +#define USBA_FRAME_NUMBER_OFFSET 3 +#define USBA_FRAME_NUMBER_SIZE 11 +#define USBA_FRAME_NUM_ERROR (1 << 31) + +/* Bitfields in INT_ENB/INT_STA/INT_CLR */ +#define USBA_HIGH_SPEED (1 << 0) +#define USBA_DET_SUSPEND (1 << 1) +#define USBA_MICRO_SOF (1 << 2) +#define USBA_SOF (1 << 3) +#define USBA_END_OF_RESET (1 << 4) +#define USBA_WAKE_UP (1 << 5) +#define USBA_END_OF_RESUME (1 << 6) +#define USBA_UPSTREAM_RESUME (1 << 7) +#define USBA_EPT_INT_OFFSET 8 +#define USBA_EPT_INT_SIZE 16 +#define USBA_DMA_INT_OFFSET 24 +#define USBA_DMA_INT_SIZE 8 + +/* Bitfields in EPT_RST */ +#define USBA_RST_OFFSET 0 +#define USBA_RST_SIZE 16 + +/* Bitfields in USBA_TST */ +#define USBA_SPEED_CFG_OFFSET 0 +#define USBA_SPEED_CFG_SIZE 2 +#define USBA_TST_J_MODE (1 << 2) +#define USBA_TST_K_MODE (1 << 3) +#define USBA_TST_PKT_MODE (1 << 4) +#define USBA_OPMODE2 (1 << 5) + +/* Bitfields in EPT_CFG */ +#define USBA_EPT_SIZE_OFFSET 0 +#define USBA_EPT_SIZE_SIZE 3 +#define USBA_EPT_DIR_IN (1 << 3) +#define USBA_EPT_TYPE_OFFSET 4 +#define USBA_EPT_TYPE_SIZE 2 +#define USBA_BK_NUMBER_OFFSET 6 +#define USBA_BK_NUMBER_SIZE 2 +#define USBA_NB_TRANS_OFFSET 8 +#define USBA_NB_TRANS_SIZE 2 +#define USBA_EPT_MAPPED (1 << 31) + +/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */ +#define USBA_EPT_ENABLE (1 << 0) +#define USBA_AUTO_VALID (1 << 1) +#define USBA_INTDIS_DMA (1 << 3) +#define USBA_NYET_DIS (1 << 4) +#define USBA_DATAX_RX (1 << 6) +#define USBA_MDATA_RX (1 << 7) +/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */ +#define USBA_BUSY_BANK_IE (1 << 18) + +/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */ +#define USBA_FORCE_STALL (1 << 5) +#define USBA_TOGGLE_CLR (1 << 6) +#define USBA_TOGGLE_SEQ_OFFSET 6 +#define USBA_TOGGLE_SEQ_SIZE 2 +#define USBA_ERR_OVFLW (1 << 8) +#define USBA_RX_BK_RDY (1 << 9) +#define USBA_KILL_BANK (1 << 9) +#define USBA_TX_COMPLETE (1 << 10) +#define USBA_TX_PK_RDY (1 << 11) +#define USBA_ISO_ERR_TRANS (1 << 11) +#define USBA_RX_SETUP (1 << 12) +#define USBA_ISO_ERR_FLOW (1 << 12) +#define USBA_STALL_SENT (1 << 13) +#define USBA_ISO_ERR_CRC (1 << 13) +#define USBA_ISO_ERR_NBTRANS (1 << 13) +#define USBA_NAK_IN (1 << 14) +#define USBA_ISO_ERR_FLUSH (1 << 14) +#define USBA_NAK_OUT (1 << 15) +#define USBA_CURRENT_BANK_OFFSET 16 +#define USBA_CURRENT_BANK_SIZE 2 +#define USBA_BUSY_BANKS_OFFSET 18 +#define USBA_BUSY_BANKS_SIZE 2 +#define USBA_BYTE_COUNT_OFFSET 20 +#define USBA_BYTE_COUNT_SIZE 11 +#define USBA_SHORT_PACKET (1 << 31) + +/* Bitfields in DMA_CONTROL */ +#define USBA_DMA_CH_EN (1 << 0) +#define USBA_DMA_LINK (1 << 1) +#define USBA_DMA_END_TR_EN (1 << 2) +#define USBA_DMA_END_BUF_EN (1 << 3) +#define USBA_DMA_END_TR_IE (1 << 4) +#define USBA_DMA_END_BUF_IE (1 << 5) +#define USBA_DMA_DESC_LOAD_IE (1 << 6) +#define USBA_DMA_BURST_LOCK (1 << 7) +#define USBA_DMA_BUF_LEN_OFFSET 16 +#define USBA_DMA_BUF_LEN_SIZE 16 + +/* Bitfields in DMA_STATUS */ +#define USBA_DMA_CH_ACTIVE (1 << 1) +#define USBA_DMA_END_TR_ST (1 << 4) +#define USBA_DMA_END_BUF_ST (1 << 5) +#define USBA_DMA_DESC_LOAD_ST (1 << 6) + +/* Constants for SPEED_CFG */ +#define USBA_SPEED_CFG_NORMAL 0 +#define USBA_SPEED_CFG_FORCE_HIGH 2 +#define USBA_SPEED_CFG_FORCE_FULL 3 + +/* Constants for EPT_SIZE */ +#define USBA_EPT_SIZE_8 0 +#define USBA_EPT_SIZE_16 1 +#define USBA_EPT_SIZE_32 2 +#define USBA_EPT_SIZE_64 3 +#define USBA_EPT_SIZE_128 4 +#define USBA_EPT_SIZE_256 5 +#define USBA_EPT_SIZE_512 6 +#define USBA_EPT_SIZE_1024 7 + +/* Constants for EPT_TYPE */ +#define USBA_EPT_TYPE_CONTROL 0 +#define USBA_EPT_TYPE_ISO 1 +#define USBA_EPT_TYPE_BULK 2 +#define USBA_EPT_TYPE_INT 3 + +/* Constants for BK_NUMBER */ +#define USBA_BK_NUMBER_ZERO 0 +#define USBA_BK_NUMBER_ONE 1 +#define USBA_BK_NUMBER_DOUBLE 2 +#define USBA_BK_NUMBER_TRIPLE 3 + +/* Bit manipulation macros */ +#define USBA_BF(name, value) \ + (((value) & ((1 << USBA_##name##_SIZE) - 1)) \ + << USBA_##name##_OFFSET) +#define USBA_BFEXT(name, value) \ + (((value) >> USBA_##name##_OFFSET) \ + & ((1 << USBA_##name##_SIZE) - 1)) +#define USBA_BFINS(name, value, old) \ + (((old) & ~(((1 << USBA_##name##_SIZE) - 1) \ + << USBA_##name##_OFFSET)) \ + | USBA_BF(name, value)) + +/* Register access macros */ +#define usba_readl(udc, reg) \ + __raw_readl((udc)->regs + USBA_##reg) +#define usba_writel(udc, reg, value) \ + __raw_writel((value), (udc)->regs + USBA_##reg) +#define usba_ep_readl(ep, reg) \ + __raw_readl((ep)->ep_regs + USBA_EPT_##reg) +#define usba_ep_writel(ep, reg, value) \ + __raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg) +#define usba_dma_readl(ep, reg) \ + __raw_readl((ep)->dma_regs + USBA_DMA_##reg) +#define usba_dma_writel(ep, reg, value) \ + __raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg) + +/* Calculate base address for a given endpoint or DMA controller */ +#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20) +#define USBA_DMA_BASE(x) (0x300 + (x) * 0x10) +#define USBA_FIFO_BASE(x) ((x) << 16) + +/* Synth parameters */ +#define USBA_NR_ENDPOINTS 7 + +#define EP0_FIFO_SIZE 64 +#define EP0_EPT_SIZE USBA_EPT_SIZE_64 +#define EP0_NR_BANKS 1 + +#define DBG_ERR 0x0001 /* report all error returns */ +#define DBG_HW 0x0002 /* debug hardware initialization */ +#define DBG_GADGET 0x0004 /* calls to/from gadget driver */ +#define DBG_INT 0x0008 /* interrupts */ +#define DBG_BUS 0x0010 /* report changes in bus state */ +#define DBG_QUEUE 0x0020 /* debug request queue processing */ +#define DBG_FIFO 0x0040 /* debug FIFO contents */ +#define DBG_DMA 0x0080 /* debug DMA handling */ +#define DBG_REQ 0x0100 /* print out queued request length */ +#define DBG_ALL 0xffff +#define DBG_NONE 0x0000 + +#define DEBUG_LEVEL (DBG_ERR) + +#define DBG(level, fmt, ...) \ + do { \ + if ((level) & DEBUG_LEVEL) \ + debug("udc: " fmt, ## __VA_ARGS__); \ + } while (0) + +enum usba_ctrl_state { + WAIT_FOR_SETUP, + DATA_STAGE_IN, + DATA_STAGE_OUT, + STATUS_STAGE_IN, + STATUS_STAGE_OUT, + STATUS_STAGE_ADDR, + STATUS_STAGE_TEST, +}; + +struct usba_dma_desc { + dma_addr_t next; + dma_addr_t addr; + u32 ctrl; +}; + +struct usba_ep { + int state; + void *ep_regs; + void *dma_regs; + void *fifo; + struct usb_ep ep; + struct usba_udc *udc; + + struct list_head queue; + + u16 fifo_size; + u8 nr_banks; + u8 index; + unsigned int can_dma:1; + unsigned int can_isoc:1; + unsigned int is_isoc:1; + unsigned int is_in:1; + + const struct usb_endpoint_descriptor *desc; +}; + +struct usba_request { + struct usb_request req; + struct list_head queue; + + u32 ctrl; + + unsigned int submitted:1; + unsigned int last_transaction:1; + unsigned int using_dma:1; + unsigned int mapped:1; +}; + +struct usba_udc { + void *regs; + void *fifo; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct platform_device *pdev; + int irq; + int vbus_pin; + int vbus_pin_inverted; + int num_ep; + struct usba_ep *usba_ep; + + u16 devstatus; + + u16 test_mode; + int vbus_prev; +}; + +static inline struct usba_ep *to_usba_ep(struct usb_ep *ep) +{ + return container_of(ep, struct usba_ep, ep); +} + +static inline struct usba_request *to_usba_req(struct usb_request *req) +{ + return container_of(req, struct usba_request, req); +} + +static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct usba_udc, gadget); +} + +#define ep_is_control(ep) ((ep)->index == 0) +#define ep_is_idle(ep) ((ep)->state == EP_STATE_IDLE) + +#endif /* __LINUX_USB_GADGET_USBA_UDC_H */ diff --git a/qemu/roms/u-boot/drivers/usb/gadget/ci_udc.c b/qemu/roms/u-boot/drivers/usb/gadget/ci_udc.c new file mode 100644 index 000000000..02d3fdade --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/ci_udc.c @@ -0,0 +1,760 @@ +/* + * Copyright 2011, Marvell Semiconductor Inc. + * Lei Wen + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Back ported to the 8xx platform (from the 8260 platform) by + * Murray.Jensen@cmst.csiro.au, 27-Jan-01. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../host/ehci.h" +#include "ci_udc.h" + +/* + * Check if the system has too long cachelines. If the cachelines are + * longer then 128b, the driver will not be able flush/invalidate data + * cache over separate QH entries. We use 128b because one QH entry is + * 64b long and there are always two QH list entries for each endpoint. + */ +#if ARCH_DMA_MINALIGN > 128 +#error This driver can not work on systems with caches longer than 128b +#endif + +#ifndef DEBUG +#define DBG(x...) do {} while (0) +#else +#define DBG(x...) printf(x) +static const char *reqname(unsigned r) +{ + switch (r) { + case USB_REQ_GET_STATUS: return "GET_STATUS"; + case USB_REQ_CLEAR_FEATURE: return "CLEAR_FEATURE"; + case USB_REQ_SET_FEATURE: return "SET_FEATURE"; + case USB_REQ_SET_ADDRESS: return "SET_ADDRESS"; + case USB_REQ_GET_DESCRIPTOR: return "GET_DESCRIPTOR"; + case USB_REQ_SET_DESCRIPTOR: return "SET_DESCRIPTOR"; + case USB_REQ_GET_CONFIGURATION: return "GET_CONFIGURATION"; + case USB_REQ_SET_CONFIGURATION: return "SET_CONFIGURATION"; + case USB_REQ_GET_INTERFACE: return "GET_INTERFACE"; + case USB_REQ_SET_INTERFACE: return "SET_INTERFACE"; + default: return "*UNKNOWN*"; + } +} +#endif + +static struct usb_endpoint_descriptor ep0_out_desc = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static struct usb_endpoint_descriptor ep0_in_desc = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static int ci_pullup(struct usb_gadget *gadget, int is_on); +static int ci_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc); +static int ci_ep_disable(struct usb_ep *ep); +static int ci_ep_queue(struct usb_ep *ep, + struct usb_request *req, gfp_t gfp_flags); +static struct usb_request * +ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags); +static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req); + +static struct usb_gadget_ops ci_udc_ops = { + .pullup = ci_pullup, +}; + +static struct usb_ep_ops ci_ep_ops = { + .enable = ci_ep_enable, + .disable = ci_ep_disable, + .queue = ci_ep_queue, + .alloc_request = ci_ep_alloc_request, + .free_request = ci_ep_free_request, +}; + +/* Init values for USB endpoints. */ +static const struct usb_ep ci_ep_init[2] = { + [0] = { /* EP 0 */ + .maxpacket = 64, + .name = "ep0", + .ops = &ci_ep_ops, + }, + [1] = { /* EP 1..n */ + .maxpacket = 512, + .name = "ep-", + .ops = &ci_ep_ops, + }, +}; + +static struct ci_drv controller = { + .gadget = { + .name = "ci_udc", + .ops = &ci_udc_ops, + .is_dualspeed = 1, + }, +}; + +/** + * ci_get_qh() - return queue head for endpoint + * @ep_num: Endpoint number + * @dir_in: Direction of the endpoint (IN = 1, OUT = 0) + * + * This function returns the QH associated with particular endpoint + * and it's direction. + */ +static struct ept_queue_head *ci_get_qh(int ep_num, int dir_in) +{ + return &controller.epts[(ep_num * 2) + dir_in]; +} + +/** + * ci_get_qtd() - return queue item for endpoint + * @ep_num: Endpoint number + * @dir_in: Direction of the endpoint (IN = 1, OUT = 0) + * + * This function returns the QH associated with particular endpoint + * and it's direction. + */ +static struct ept_queue_item *ci_get_qtd(int ep_num, int dir_in) +{ + return controller.items[(ep_num * 2) + dir_in]; +} + +/** + * ci_flush_qh - flush cache over queue head + * @ep_num: Endpoint number + * + * This function flushes cache over QH for particular endpoint. + */ +static void ci_flush_qh(int ep_num) +{ + struct ept_queue_head *head = ci_get_qh(ep_num, 0); + const uint32_t start = (uint32_t)head; + const uint32_t end = start + 2 * sizeof(*head); + + flush_dcache_range(start, end); +} + +/** + * ci_invalidate_qh - invalidate cache over queue head + * @ep_num: Endpoint number + * + * This function invalidates cache over QH for particular endpoint. + */ +static void ci_invalidate_qh(int ep_num) +{ + struct ept_queue_head *head = ci_get_qh(ep_num, 0); + uint32_t start = (uint32_t)head; + uint32_t end = start + 2 * sizeof(*head); + + invalidate_dcache_range(start, end); +} + +/** + * ci_flush_qtd - flush cache over queue item + * @ep_num: Endpoint number + * + * This function flushes cache over qTD pair for particular endpoint. + */ +static void ci_flush_qtd(int ep_num) +{ + struct ept_queue_item *item = ci_get_qtd(ep_num, 0); + const uint32_t start = (uint32_t)item; + const uint32_t end_raw = start + 2 * sizeof(*item); + const uint32_t end = roundup(end_raw, ARCH_DMA_MINALIGN); + + flush_dcache_range(start, end); +} + +/** + * ci_invalidate_qtd - invalidate cache over queue item + * @ep_num: Endpoint number + * + * This function invalidates cache over qTD pair for particular endpoint. + */ +static void ci_invalidate_qtd(int ep_num) +{ + struct ept_queue_item *item = ci_get_qtd(ep_num, 0); + const uint32_t start = (uint32_t)item; + const uint32_t end_raw = start + 2 * sizeof(*item); + const uint32_t end = roundup(end_raw, ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +} + +static struct usb_request * +ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags) +{ + struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); + return &ci_ep->req; +} + +static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + return; +} + +static void ep_enable(int num, int in, int maxpacket) +{ + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + unsigned n; + + n = readl(&udc->epctrl[num]); + if (in) + n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK); + else + n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK); + + if (num != 0) { + struct ept_queue_head *head = ci_get_qh(num, in); + + head->config = CONFIG_MAX_PKT(maxpacket) | CONFIG_ZLT; + ci_flush_qh(num); + } + writel(n, &udc->epctrl[num]); +} + +static int ci_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); + int num, in; + num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + in = (desc->bEndpointAddress & USB_DIR_IN) != 0; + ci_ep->desc = desc; + + if (num) { + int max = get_unaligned_le16(&desc->wMaxPacketSize); + + if ((max > 64) && (controller.gadget.speed == USB_SPEED_FULL)) + max = 64; + if (ep->maxpacket != max) { + DBG("%s: from %d to %d\n", __func__, + ep->maxpacket, max); + ep->maxpacket = max; + } + } + ep_enable(num, in, ep->maxpacket); + DBG("%s: num=%d maxpacket=%d\n", __func__, num, ep->maxpacket); + return 0; +} + +static int ci_ep_disable(struct usb_ep *ep) +{ + struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); + + ci_ep->desc = NULL; + return 0; +} + +static int ci_bounce(struct ci_ep *ep, int in) +{ + uint32_t addr = (uint32_t)ep->req.buf; + uint32_t ba; + + /* Input buffer address is not aligned. */ + if (addr & (ARCH_DMA_MINALIGN - 1)) + goto align; + + /* Input buffer length is not aligned. */ + if (ep->req.length & (ARCH_DMA_MINALIGN - 1)) + goto align; + + /* The buffer is well aligned, only flush cache. */ + ep->b_len = ep->req.length; + ep->b_buf = ep->req.buf; + goto flush; + +align: + /* Use internal buffer for small payloads. */ + if (ep->req.length <= 64) { + ep->b_len = 64; + ep->b_buf = ep->b_fast; + } else { + ep->b_len = roundup(ep->req.length, ARCH_DMA_MINALIGN); + ep->b_buf = memalign(ARCH_DMA_MINALIGN, ep->b_len); + if (!ep->b_buf) + return -ENOMEM; + } + if (in) + memcpy(ep->b_buf, ep->req.buf, ep->req.length); + +flush: + ba = (uint32_t)ep->b_buf; + flush_dcache_range(ba, ba + ep->b_len); + + return 0; +} + +static void ci_debounce(struct ci_ep *ep, int in) +{ + uint32_t addr = (uint32_t)ep->req.buf; + uint32_t ba = (uint32_t)ep->b_buf; + + if (in) { + if (addr == ba) + return; /* not a bounce */ + goto free; + } + invalidate_dcache_range(ba, ba + ep->b_len); + + if (addr == ba) + return; /* not a bounce */ + + memcpy(ep->req.buf, ep->b_buf, ep->req.actual); +free: + /* Large payloads use allocated buffer, free it. */ + if (ep->b_buf != ep->b_fast) + free(ep->b_buf); +} + +static int ci_ep_queue(struct usb_ep *ep, + struct usb_request *req, gfp_t gfp_flags) +{ + struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + struct ept_queue_item *item; + struct ept_queue_head *head; + int bit, num, len, in, ret; + num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; + item = ci_get_qtd(num, in); + head = ci_get_qh(num, in); + len = req->length; + + ret = ci_bounce(ci_ep, in); + if (ret) + return ret; + + item->next = TERMINATE; + item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE; + item->page0 = (uint32_t)ci_ep->b_buf; + item->page1 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x1000; + item->page2 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x2000; + item->page3 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x3000; + item->page4 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x4000; + ci_flush_qtd(num); + + head->next = (unsigned) item; + head->info = 0; + + DBG("ept%d %s queue len %x, buffer %p\n", + num, in ? "in" : "out", len, ci_ep->b_buf); + ci_flush_qh(num); + + if (in) + bit = EPT_TX(num); + else + bit = EPT_RX(num); + + writel(bit, &udc->epprime); + + return 0; +} + +static void handle_ep_complete(struct ci_ep *ep) +{ + struct ept_queue_item *item; + int num, in, len; + num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0; + if (num == 0) + ep->desc = &ep0_out_desc; + item = ci_get_qtd(num, in); + ci_invalidate_qtd(num); + + if (item->info & 0xff) + printf("EP%d/%s FAIL info=%x pg0=%x\n", + num, in ? "in" : "out", item->info, item->page0); + + len = (item->info >> 16) & 0x7fff; + ep->req.actual = ep->req.length - len; + ci_debounce(ep, in); + + DBG("ept%d %s complete %x\n", + num, in ? "in" : "out", len); + ep->req.complete(&ep->ep, &ep->req); + if (num == 0) { + ep->req.length = 0; + usb_ep_queue(&ep->ep, &ep->req, 0); + ep->desc = &ep0_in_desc; + } +} + +#define SETUP(type, request) (((type) << 8) | (request)) + +static void handle_setup(void) +{ + struct usb_request *req = &controller.ep[0].req; + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + struct ept_queue_head *head; + struct usb_ctrlrequest r; + int status = 0; + int num, in, _num, _in, i; + char *buf; + head = ci_get_qh(0, 0); /* EP0 OUT */ + + ci_invalidate_qh(0); + memcpy(&r, head->setup_data, sizeof(struct usb_ctrlrequest)); +#ifdef CONFIG_CI_UDC_HAS_HOSTPC + writel(EPT_RX(0), &udc->epsetupstat); +#else + writel(EPT_RX(0), &udc->epstat); +#endif + DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest), + r.bRequestType, r.bRequest, r.wIndex, r.wValue); + + switch (SETUP(r.bRequestType, r.bRequest)) { + case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE): + _num = r.wIndex & 15; + _in = !!(r.wIndex & 0x80); + + if ((r.wValue == 0) && (r.wLength == 0)) { + req->length = 0; + for (i = 0; i < NUM_ENDPOINTS; i++) { + struct ci_ep *ep = &controller.ep[i]; + + if (!ep->desc) + continue; + num = ep->desc->bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK; + in = (ep->desc->bEndpointAddress + & USB_DIR_IN) != 0; + if ((num == _num) && (in == _in)) { + ep_enable(num, in, ep->ep.maxpacket); + usb_ep_queue(controller.gadget.ep0, + req, 0); + break; + } + } + } + return; + + case SETUP(USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS): + /* + * write address delayed (will take effect + * after the next IN txn) + */ + writel((r.wValue << 25) | (1 << 24), &udc->devaddr); + req->length = 0; + usb_ep_queue(controller.gadget.ep0, req, 0); + return; + + case SETUP(USB_DIR_IN | USB_RECIP_DEVICE, USB_REQ_GET_STATUS): + req->length = 2; + buf = (char *)req->buf; + buf[0] = 1 << USB_DEVICE_SELF_POWERED; + buf[1] = 0; + usb_ep_queue(controller.gadget.ep0, req, 0); + return; + } + /* pass request up to the gadget driver */ + if (controller.driver) + status = controller.driver->setup(&controller.gadget, &r); + else + status = -ENODEV; + + if (!status) + return; + DBG("STALL reqname %s type %x value %x, index %x\n", + reqname(r.bRequest), r.bRequestType, r.wValue, r.wIndex); + writel((1<<16) | (1 << 0), &udc->epctrl[0]); +} + +static void stop_activity(void) +{ + int i, num, in; + struct ept_queue_head *head; + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + writel(readl(&udc->epcomp), &udc->epcomp); +#ifdef CONFIG_CI_UDC_HAS_HOSTPC + writel(readl(&udc->epsetupstat), &udc->epsetupstat); +#endif + writel(readl(&udc->epstat), &udc->epstat); + writel(0xffffffff, &udc->epflush); + + /* error out any pending reqs */ + for (i = 0; i < NUM_ENDPOINTS; i++) { + if (i != 0) + writel(0, &udc->epctrl[i]); + if (controller.ep[i].desc) { + num = controller.ep[i].desc->bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK; + in = (controller.ep[i].desc->bEndpointAddress + & USB_DIR_IN) != 0; + head = ci_get_qh(num, in); + head->info = INFO_ACTIVE; + ci_flush_qh(num); + } + } +} + +void udc_irq(void) +{ + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + unsigned n = readl(&udc->usbsts); + writel(n, &udc->usbsts); + int bit, i, num, in; + + n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI); + if (n == 0) + return; + + if (n & STS_URI) { + DBG("-- reset --\n"); + stop_activity(); + } + if (n & STS_SLI) + DBG("-- suspend --\n"); + + if (n & STS_PCI) { + int max = 64; + int speed = USB_SPEED_FULL; + +#ifdef CONFIG_CI_UDC_HAS_HOSTPC + bit = (readl(&udc->hostpc1_devlc) >> 25) & 3; +#else + bit = (readl(&udc->portsc) >> 26) & 3; +#endif + DBG("-- portchange %x %s\n", bit, (bit == 2) ? "High" : "Full"); + if (bit == 2) { + speed = USB_SPEED_HIGH; + max = 512; + } + controller.gadget.speed = speed; + for (i = 1; i < NUM_ENDPOINTS; i++) { + if (controller.ep[i].ep.maxpacket > max) + controller.ep[i].ep.maxpacket = max; + } + } + + if (n & STS_UEI) + printf("\n", readl(&udc->epcomp)); + + if ((n & STS_UI) || (n & STS_UEI)) { +#ifdef CONFIG_CI_UDC_HAS_HOSTPC + n = readl(&udc->epsetupstat); +#else + n = readl(&udc->epstat); +#endif + if (n & EPT_RX(0)) + handle_setup(); + + n = readl(&udc->epcomp); + if (n != 0) + writel(n, &udc->epcomp); + + for (i = 0; i < NUM_ENDPOINTS && n; i++) { + if (controller.ep[i].desc) { + num = controller.ep[i].desc->bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK; + in = (controller.ep[i].desc->bEndpointAddress + & USB_DIR_IN) != 0; + bit = (in) ? EPT_TX(num) : EPT_RX(num); + if (n & bit) + handle_ep_complete(&controller.ep[i]); + } + } + } +} + +int usb_gadget_handle_interrupts(void) +{ + u32 value; + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + + value = readl(&udc->usbsts); + if (value) + udc_irq(); + + return value; +} + +static int ci_pullup(struct usb_gadget *gadget, int is_on) +{ + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + if (is_on) { + /* RESET */ + writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd); + udelay(200); + + writel((unsigned)controller.epts, &udc->epinitaddr); + + /* select DEVICE mode */ + writel(USBMODE_DEVICE, &udc->usbmode); + + writel(0xffffffff, &udc->epflush); + + /* Turn on the USB connection by enabling the pullup resistor */ + writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RUN, &udc->usbcmd); + } else { + stop_activity(); + writel(USBCMD_FS2, &udc->usbcmd); + udelay(800); + if (controller.driver) + controller.driver->disconnect(gadget); + } + + return 0; +} + +void udc_disconnect(void) +{ + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + /* disable pullup */ + stop_activity(); + writel(USBCMD_FS2, &udc->usbcmd); + udelay(800); + if (controller.driver) + controller.driver->disconnect(&controller.gadget); +} + +static int ci_udc_probe(void) +{ + struct ept_queue_head *head; + uint8_t *imem; + int i; + + const int num = 2 * NUM_ENDPOINTS; + + const int eplist_min_align = 4096; + const int eplist_align = roundup(eplist_min_align, ARCH_DMA_MINALIGN); + const int eplist_raw_sz = num * sizeof(struct ept_queue_head); + const int eplist_sz = roundup(eplist_raw_sz, ARCH_DMA_MINALIGN); + + const int ilist_align = roundup(ARCH_DMA_MINALIGN, 32); + const int ilist_ent_raw_sz = 2 * sizeof(struct ept_queue_item); + const int ilist_ent_sz = roundup(ilist_ent_raw_sz, ARCH_DMA_MINALIGN); + const int ilist_sz = NUM_ENDPOINTS * ilist_ent_sz; + + /* The QH list must be aligned to 4096 bytes. */ + controller.epts = memalign(eplist_align, eplist_sz); + if (!controller.epts) + return -ENOMEM; + memset(controller.epts, 0, eplist_sz); + + /* + * Each qTD item must be 32-byte aligned, each qTD touple must be + * cacheline aligned. There are two qTD items for each endpoint and + * only one of them is used for the endpoint at time, so we can group + * them together. + */ + controller.items_mem = memalign(ilist_align, ilist_sz); + if (!controller.items_mem) { + free(controller.epts); + return -ENOMEM; + } + memset(controller.items_mem, 0, ilist_sz); + + for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { + /* + * Configure QH for each endpoint. The structure of the QH list + * is such that each two subsequent fields, N and N+1 where N is + * even, in the QH list represent QH for one endpoint. The Nth + * entry represents OUT configuration and the N+1th entry does + * represent IN configuration of the endpoint. + */ + head = controller.epts + i; + if (i < 2) + head->config = CONFIG_MAX_PKT(EP0_MAX_PACKET_SIZE) + | CONFIG_ZLT | CONFIG_IOS; + else + head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE) + | CONFIG_ZLT; + head->next = TERMINATE; + head->info = 0; + + imem = controller.items_mem + ((i >> 1) * ilist_ent_sz); + if (i & 1) + imem += sizeof(struct ept_queue_item); + + controller.items[i] = (struct ept_queue_item *)imem; + + if (i & 1) { + ci_flush_qh(i - 1); + ci_flush_qtd(i - 1); + } + } + + INIT_LIST_HEAD(&controller.gadget.ep_list); + + /* Init EP 0 */ + memcpy(&controller.ep[0].ep, &ci_ep_init[0], sizeof(*ci_ep_init)); + controller.ep[0].desc = &ep0_in_desc; + controller.gadget.ep0 = &controller.ep[0].ep; + INIT_LIST_HEAD(&controller.gadget.ep0->ep_list); + + /* Init EP 1..n */ + for (i = 1; i < NUM_ENDPOINTS; i++) { + memcpy(&controller.ep[i].ep, &ci_ep_init[1], + sizeof(*ci_ep_init)); + list_add_tail(&controller.ep[i].ep.ep_list, + &controller.gadget.ep_list); + } + + return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int ret; + + if (!driver) + return -EINVAL; + if (!driver->bind || !driver->setup || !driver->disconnect) + return -EINVAL; + if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH) + return -EINVAL; + + ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl); + if (ret) + return ret; + + ret = ci_udc_probe(); +#if defined(CONFIG_USB_EHCI_MX6) || defined(CONFIG_USB_EHCI_MXS) + /* + * FIXME: usb_lowlevel_init()->ehci_hcd_init() should be doing all + * HW-specific initialization, e.g. ULPI-vs-UTMI PHY selection + */ + if (!ret) { + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + + /* select ULPI phy */ + writel(PTS(PTS_ENABLE) | PFSC, &udc->portsc); + } +#endif + + ret = driver->bind(&controller.gadget); + if (ret) { + DBG("driver->bind() returned %d\n", ret); + return ret; + } + controller.driver = driver; + + return 0; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/ci_udc.h b/qemu/roms/u-boot/drivers/usb/gadget/ci_udc.h new file mode 100644 index 000000000..4425fd934 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/ci_udc.h @@ -0,0 +1,144 @@ +/* + * Copyright 2011, Marvell Semiconductor Inc. + * + * Licensed under the GPL-2 or later. + */ +#ifndef __GADGET__CI_UDC_H__ +#define __GADGET__CI_UDC_H__ + +#define NUM_ENDPOINTS 6 + +#ifdef CONFIG_CI_UDC_HAS_HOSTPC +struct ci_udc { + u32 usbcmd; /* 0x130 */ + u32 usbsts; /* 0x134 */ + u32 pad1[3]; + u32 devaddr; /* 0x144 */ + u32 epinitaddr; /* 0x148 */ + u32 pad2[10]; + u32 portsc; /* 0x174 */ + u32 pad178[(0x1b4 - (0x174 + 4)) / 4]; + u32 hostpc1_devlc; /* 0x1b4 */ + u32 pad1b8[(0x1f8 - (0x1b4 + 4)) / 4]; + u32 usbmode; /* 0x1f8 */ + u32 pad1fc[(0x208 - (0x1f8 + 4)) / 4]; + u32 epsetupstat; /* 0x208 */ + u32 epprime; /* 0x20c */ + u32 epflush; /* 0x210 */ + u32 epstat; /* 0x214 */ + u32 epcomp; /* 0x218 */ + u32 epctrl[16]; /* 0x21c */ +}; +#else +struct ci_udc { + u32 usbcmd; /* 0x140 */ + u32 usbsts; /* 0x144 */ + u32 pad1[3]; + u32 devaddr; /* 0x154 */ + u32 epinitaddr; /* 0x158 */ + u32 pad2[10]; + u32 portsc; /* 0x184 */ + u32 pad3[8]; + u32 usbmode; /* 0x1a8 */ + u32 epstat; /* 0x1ac */ + u32 epprime; /* 0x1b0 */ + u32 epflush; /* 0x1b4 */ + u32 pad4; + u32 epcomp; /* 0x1bc */ + u32 epctrl[16]; /* 0x1c0 */ +}; + +#define PTS_ENABLE 2 +#define PTS(x) (((x) & 0x3) << 30) +#define PFSC (1 << 24) +#endif + +#define MICRO_8FRAME 0x8 +#define USBCMD_ITC(x) ((((x) > 0xff) ? 0xff : x) << 16) +#define USBCMD_FS2 (1 << 15) +#define USBCMD_RST (1 << 1) +#define USBCMD_RUN (1) + +#define STS_SLI (1 << 8) +#define STS_URI (1 << 6) +#define STS_PCI (1 << 2) +#define STS_UEI (1 << 1) +#define STS_UI (1 << 0) + +#define USBMODE_DEVICE 2 + +#define EPT_TX(x) (1 << (((x) & 0xffff) + 16)) +#define EPT_RX(x) (1 << ((x) & 0xffff)) + +#define CTRL_TXE (1 << 23) +#define CTRL_TXR (1 << 22) +#define CTRL_RXE (1 << 7) +#define CTRL_RXR (1 << 6) +#define CTRL_TXT_BULK (2 << 18) +#define CTRL_RXT_BULK (2 << 2) + +struct ci_ep { + struct usb_ep ep; + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + + struct usb_request req; + uint8_t *b_buf; + uint32_t b_len; + uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN); +}; + +struct ci_drv { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct ehci_ctrl *ctrl; + struct ept_queue_head *epts; + struct ept_queue_item *items[2 * NUM_ENDPOINTS]; + uint8_t *items_mem; + struct ci_ep ep[NUM_ENDPOINTS]; +}; + +struct ept_queue_head { + unsigned config; + unsigned current; /* read-only */ + + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved_0; + + unsigned char setup_data[8]; + + unsigned reserved_1; + unsigned reserved_2; + unsigned reserved_3; + unsigned reserved_4; +}; + +#define CONFIG_MAX_PKT(n) ((n) << 16) +#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */ +#define CONFIG_IOS (1 << 15) /* IRQ on setup */ + +struct ept_queue_item { + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved; +}; + +#define TERMINATE 1 +#define INFO_BYTES(n) ((n) << 16) +#define INFO_IOC (1 << 15) +#define INFO_ACTIVE (1 << 7) +#define INFO_HALTED (1 << 6) +#define INFO_BUFFER_ERROR (1 << 5) +#define INFO_TX_ERROR (1 << 3) +#endif diff --git a/qemu/roms/u-boot/drivers/usb/gadget/composite.c b/qemu/roms/u-boot/drivers/usb/gadget/composite.c new file mode 100644 index 000000000..7bd25629c --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/composite.c @@ -0,0 +1,1091 @@ +/* + * composite.c - infrastructure for Composite USB Gadgets + * + * Copyright (C) 2006-2008 David Brownell + * U-boot porting: Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#undef DEBUG + +#include +#include + +#define USB_BUFSIZ 4096 + +static struct usb_composite_driver *composite; + +/** + * usb_add_function() - add a function to a configuration + * @config: the configuration + * @function: the function being added + * Context: single threaded during gadget setup + * + * After initialization, each configuration must have one or more + * functions added to it. Adding a function involves calling its @bind() + * method to allocate resources such as interface and string identifiers + * and endpoints. + * + * This function returns the value of the function's bind(), which is + * zero for success else a negative errno value. + */ +int usb_add_function(struct usb_configuration *config, + struct usb_function *function) +{ + int value = -EINVAL; + + debug("adding '%s'/%p to config '%s'/%p\n", + function->name, function, + config->label, config); + + if (!function->set_alt || !function->disable) + goto done; + + function->config = config; + list_add_tail(&function->list, &config->functions); + + if (function->bind) { + value = function->bind(config, function); + if (value < 0) { + list_del(&function->list); + function->config = NULL; + } + } else + value = 0; + + if (!config->fullspeed && function->descriptors) + config->fullspeed = 1; + if (!config->highspeed && function->hs_descriptors) + config->highspeed = 1; + +done: + if (value) + debug("adding '%s'/%p --> %d\n", + function->name, function, value); + return value; +} + +/** + * usb_function_deactivate - prevent function and gadget enumeration + * @function: the function that isn't yet ready to respond + * + * Blocks response of the gadget driver to host enumeration by + * preventing the data line pullup from being activated. This is + * normally called during @bind() processing to change from the + * initial "ready to respond" state, or when a required resource + * becomes available. + * + * For example, drivers that serve as a passthrough to a userspace + * daemon can block enumeration unless that daemon (such as an OBEX, + * MTP, or print server) is ready to handle host requests. + * + * Not all systems support software control of their USB peripheral + * data pullups. + * + * Returns zero on success, else negative errno. + */ +int usb_function_deactivate(struct usb_function *function) +{ + struct usb_composite_dev *cdev = function->config->cdev; + int status = 0; + + if (cdev->deactivations == 0) + status = usb_gadget_disconnect(cdev->gadget); + if (status == 0) + cdev->deactivations++; + + return status; +} + +/** + * usb_function_activate - allow function and gadget enumeration + * @function: function on which usb_function_activate() was called + * + * Reverses effect of usb_function_deactivate(). If no more functions + * are delaying their activation, the gadget driver will respond to + * host enumeration procedures. + * + * Returns zero on success, else negative errno. + */ +int usb_function_activate(struct usb_function *function) +{ + struct usb_composite_dev *cdev = function->config->cdev; + int status = 0; + + if (cdev->deactivations == 0) + status = -EINVAL; + else { + cdev->deactivations--; + if (cdev->deactivations == 0) + status = usb_gadget_connect(cdev->gadget); + } + + return status; +} + +/** + * usb_interface_id() - allocate an unused interface ID + * @config: configuration associated with the interface + * @function: function handling the interface + * Context: single threaded during gadget setup + * + * usb_interface_id() is called from usb_function.bind() callbacks to + * allocate new interface IDs. The function driver will then store that + * ID in interface, association, CDC union, and other descriptors. It + * will also handle any control requests targetted at that interface, + * particularly changing its altsetting via set_alt(). There may + * also be class-specific or vendor-specific requests to handle. + * + * All interface identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. Note that since interface + * identifers are configuration-specific, functions used in more than + * one configuration (or more than once in a given configuration) need + * multiple versions of the relevant descriptors. + * + * Returns the interface ID which was allocated; or -ENODEV if no + * more interface IDs can be allocated. + */ +int usb_interface_id(struct usb_configuration *config, + struct usb_function *function) +{ + unsigned char id = config->next_interface_id; + + if (id < MAX_CONFIG_INTERFACES) { + config->interface[id] = function; + config->next_interface_id = id + 1; + return id; + } + return -ENODEV; +} + +static int config_buf(struct usb_configuration *config, + enum usb_device_speed speed, void *buf, u8 type) +{ + int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + void *next = buf + USB_DT_CONFIG_SIZE; + struct usb_descriptor_header **descriptors; + struct usb_config_descriptor *c = buf; + int status; + struct usb_function *f; + + /* write the config descriptor */ + c = buf; + c->bLength = USB_DT_CONFIG_SIZE; + c->bDescriptorType = type; + + c->bNumInterfaces = config->next_interface_id; + c->bConfigurationValue = config->bConfigurationValue; + c->iConfiguration = config->iConfiguration; + c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; + c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2); + + /* There may be e.g. OTG descriptors */ + if (config->descriptors) { + status = usb_descriptor_fillbuf(next, len, + config->descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + /* add each function's descriptors */ + list_for_each_entry(f, &config->functions, list) { + if (speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + if (!descriptors) + continue; + status = usb_descriptor_fillbuf(next, len, + (const struct usb_descriptor_header **) descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + len = next - buf; + c->wTotalLength = cpu_to_le16(len); + return len; +} + +static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) +{ + enum usb_device_speed speed = USB_SPEED_UNKNOWN; + struct usb_gadget *gadget = cdev->gadget; + u8 type = w_value >> 8; + int hs = 0; + struct usb_configuration *c; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) + speed = USB_SPEED_HIGH; + } + + w_value &= 0xff; + list_for_each_entry(c, &cdev->configs, list) { + if (speed == USB_SPEED_HIGH) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + if (w_value == 0) + return config_buf(c, speed, cdev->req->buf, type); + w_value--; + } + return -EINVAL; +} + +static int count_configs(struct usb_composite_dev *cdev, unsigned type) +{ + struct usb_gadget *gadget = cdev->gadget; + unsigned count = 0; + int hs = 0; + struct usb_configuration *c; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_DEVICE_QUALIFIER) + hs = !hs; + } + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (hs) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + count++; + } + return count; +} + +static void device_qual(struct usb_composite_dev *cdev) +{ + struct usb_qualifier_descriptor *qual = cdev->req->buf; + + qual->bLength = sizeof(*qual); + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; + /* POLICY: same bcdUSB and device type info at both speeds */ + qual->bcdUSB = cdev->desc.bcdUSB; + qual->bDeviceClass = cdev->desc.bDeviceClass; + qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; + qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; + /* ASSUME same EP0 fifo size at both speeds */ + qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); + qual->bRESERVED = 0; +} + +static void reset_config(struct usb_composite_dev *cdev) +{ + struct usb_function *f; + + debug("%s:\n", __func__); + + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->disable) + f->disable(f); + + bitmap_zero(f->endpoints, 32); + } + cdev->config = NULL; +} + +static int set_config(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl, unsigned number) +{ + struct usb_gadget *gadget = cdev->gadget; + unsigned power = gadget_is_otg(gadget) ? 8 : 100; + struct usb_descriptor_header **descriptors; + int result = -EINVAL; + struct usb_endpoint_descriptor *ep; + struct usb_configuration *c = NULL; + int addr; + int tmp; + struct usb_function *f; + + if (cdev->config) + reset_config(cdev); + + if (number) { + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == number) { + result = 0; + break; + } + } + if (result < 0) + goto done; + } else + result = 0; + + debug("%s: %s speed config #%d: %s\n", __func__, + ({ char *speed; + switch (gadget->speed) { + case USB_SPEED_LOW: + speed = "low"; + break; + case USB_SPEED_FULL: + speed = "full"; + break; + case USB_SPEED_HIGH: + speed = "high"; + break; + default: + speed = "?"; + break; + }; + speed; + }), number, c ? c->label : "unconfigured"); + + if (!c) + goto done; + + cdev->config = c; + + /* Initialize all interfaces by setting them to altsetting zero. */ + for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { + f = c->interface[tmp]; + if (!f) + break; + + /* + * Record which endpoints are used by the function. This is used + * to dispatch control requests targeted at that endpoint to the + * function's setup callback instead of the current + * configuration's setup callback. + */ + if (gadget->speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + + for (; *descriptors; ++descriptors) { + if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT) + continue; + + ep = (struct usb_endpoint_descriptor *)*descriptors; + addr = ((ep->bEndpointAddress & 0x80) >> 3) + | (ep->bEndpointAddress & 0x0f); + __set_bit(addr, f->endpoints); + } + + result = f->set_alt(f, tmp, 0); + if (result < 0) { + debug("interface %d (%s/%p) alt 0 --> %d\n", + tmp, f->name, f, result); + + reset_config(cdev); + goto done; + } + } + + /* when we return, be sure our power usage is valid */ + power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; +done: + usb_gadget_vbus_draw(gadget, power); + return result; +} + +/** + * usb_add_config() - add a configuration to a device. + * @cdev: wraps the USB gadget + * @config: the configuration, with bConfigurationValue assigned + * Context: single threaded during gadget setup + * + * One of the main tasks of a composite driver's bind() routine is to + * add each of the configurations it supports, using this routine. + * + * This function returns the value of the configuration's bind(), which + * is zero for success else a negative errno value. Binding configurations + * assigns global resources including string IDs, and per-configuration + * resources such as interface IDs and endpoints. + */ +int usb_add_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + int status = -EINVAL; + struct usb_configuration *c; + struct usb_function *f; + unsigned int i; + + debug("%s: adding config #%u '%s'/%p\n", __func__, + config->bConfigurationValue, + config->label, config); + + if (!config->bConfigurationValue || !config->bind) + goto done; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) { + status = -EBUSY; + goto done; + } + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + + status = config->bind(config); + if (status < 0) { + list_del(&config->list); + config->cdev = NULL; + } else { + debug("cfg %d/%p speeds:%s%s\n", + config->bConfigurationValue, config, + config->highspeed ? " high" : "", + config->fullspeed + ? (gadget_is_dualspeed(cdev->gadget) + ? " full" + : " full/low") + : ""); + + for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { + f = config->interface[i]; + if (!f) + continue; + debug("%s: interface %d = %s/%p\n", + __func__, i, f->name, f); + } + } + + usb_ep_autoconfig_reset(cdev->gadget); + +done: + if (status) + debug("added config '%s'/%u --> %d\n", config->label, + config->bConfigurationValue, status); + return status; +} + +/* + * We support strings in multiple languages ... string descriptor zero + * says which languages are supported. The typical case will be that + * only one language (probably English) is used, with I18N handled on + * the host side. + */ + +static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) +{ + const struct usb_gadget_strings *s; + u16 language; + __le16 *tmp; + + while (*sp) { + s = *sp; + language = cpu_to_le16(s->language); + for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + if (*tmp == language) + goto repeat; + } + *tmp++ = language; +repeat: + sp++; + } +} + +static int lookup_string( + struct usb_gadget_strings **sp, + void *buf, + u16 language, + int id +) +{ + int value; + struct usb_gadget_strings *s; + + while (*sp) { + s = *sp++; + if (s->language != language) + continue; + value = usb_gadget_get_string(s, id, buf); + if (value > 0) + return value; + } + return -EINVAL; +} + +static int get_string(struct usb_composite_dev *cdev, + void *buf, u16 language, int id) +{ + struct usb_string_descriptor *s = buf; + struct usb_gadget_strings **sp; + int len; + struct usb_configuration *c; + struct usb_function *f; + + /* + * Yes, not only is USB's I18N support probably more than most + * folk will ever care about ... also, it's all supported here. + * (Except for UTF8 support for Unicode's "Astral Planes".) + */ + + /* 0 == report all available language codes */ + if (id == 0) { + memset(s, 0, 256); + s->bDescriptorType = USB_DT_STRING; + + sp = composite->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(c, &cdev->configs, list) { + sp = c->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(f, &c->functions, list) { + sp = f->strings; + if (sp) + collect_langs(sp, s->wData); + } + } + + for (len = 0; len <= 126 && s->wData[len]; len++) + continue; + if (!len) + return -EINVAL; + + s->bLength = 2 * (len + 1); + return s->bLength; + } + + /* + * Otherwise, look up and return a specified string. String IDs + * are device-scoped, so we look up each string table we're told + * about. These lookups are infrequent; simpler-is-better here. + */ + if (composite->strings) { + len = lookup_string(composite->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(c, &cdev->configs, list) { + if (c->strings) { + len = lookup_string(c->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(f, &c->functions, list) { + if (!f->strings) + continue; + len = lookup_string(f->strings, buf, language, id); + if (len > 0) + return len; + } + } + return -EINVAL; +} + +/** + * usb_string_id() - allocate an unused string ID + * @cdev: the device whose string descriptor IDs are being allocated + * Context: single threaded during gadget setup + * + * @usb_string_id() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this, + * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure + * that for example different functions don't wrongly assign different + * meanings to the same identifier. + */ +int usb_string_id(struct usb_composite_dev *cdev) +{ + if (cdev->next_string_id < 254) { + /* + * string id 0 is reserved by USB spec for list of + * supported languages + * 255 reserved as well? -- mina86 + */ + cdev->next_string_id++; + return cdev->next_string_id; + } + return -ENODEV; +} + +/** + * usb_string_ids() - allocate unused string IDs in batch + * @cdev: the device whose string descriptor IDs are being allocated + * @str: an array of usb_string objects to assign numbers to + * Context: single threaded during gadget setup + * + * @usb_string_ids() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then copy IDs from the string table to the appropriate descriptors + * and string table for other languages. + * + * All string identifier should be allocated using this, + * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for + * example different functions don't wrongly assign different meanings + * to the same identifier. + */ +int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str) +{ + u8 next = cdev->next_string_id; + + for (; str->s; ++str) { + if (next >= 254) + return -ENODEV; + str->id = ++next; + } + + cdev->next_string_id = next; + + return 0; +} + +/** + * usb_string_ids_n() - allocate unused string IDs in batch + * @c: the device whose string descriptor IDs are being allocated + * @n: number of string IDs to allocate + * Context: single threaded during gadget setup + * + * Returns the first requested ID. This ID and next @n-1 IDs are now + * valid IDs. At least provided that @n is non-zero because if it + * is, returns last requested ID which is now very useful information. + * + * @usb_string_ids_n() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this, + * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for + * example different functions don't wrongly assign different meanings + * to the same identifier. + */ +int usb_string_ids_n(struct usb_composite_dev *c, unsigned n) +{ + u8 next = c->next_string_id; + + if (n > 254 || next + n > 254) + return -ENODEV; + + c->next_string_id += n; + return next + 1; +} + +static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + debug("%s: setup complete --> %d, %d/%d\n", __func__, + req->status, req->actual, req->length); +} + +/* + * The setup() callback implements all the ep0 functionality that's + * not handled lower down, in hardware or the hardware driver(like + * device and endpoint feature flags, and their status). It's all + * housekeeping for the gadget function we're implementing. Most of + * the work is in config and function specific setup. + */ +static int +composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + u16 w_length = le16_to_cpu(ctrl->wLength); + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + struct usb_composite_dev *cdev = get_gadget_data(gadget); + u8 intf = w_index & 0xFF; + int value = -EOPNOTSUPP; + struct usb_request *req = cdev->req; + struct usb_function *f = NULL; + int standard; + u8 endp; + struct usb_configuration *c; + + /* + * partial re-init of the response message; the function or the + * gadget might need to intercept e.g. a control-OUT completion + * when we delegate to it. + */ + req->zero = 0; + req->complete = composite_setup_complete; + req->length = USB_BUFSIZ; + gadget->ep0->driver_data = cdev; + standard = (ctrl->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + if (!standard) + goto unknown; + + switch (ctrl->bRequest) { + + /* we handle all standard USB descriptors */ + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + switch (w_value >> 8) { + + case USB_DT_DEVICE: + cdev->desc.bNumConfigurations = + count_configs(cdev, USB_DT_DEVICE); + value = min(w_length, (u16) sizeof cdev->desc); + memcpy(req->buf, &cdev->desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + if (!gadget_is_dualspeed(gadget)) + break; + device_qual(cdev); + value = min(w_length, + sizeof(struct usb_qualifier_descriptor)); + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget_is_dualspeed(gadget)) + break; + + case USB_DT_CONFIG: + value = config_desc(cdev, w_value); + if (value >= 0) + value = min(w_length, (u16) value); + break; + case USB_DT_STRING: + value = get_string(cdev, req->buf, + w_index, w_value & 0xff); + if (value >= 0) + value = min(w_length, (u16) value); + break; + default: + goto unknown; + } + break; + + /* any number of configs can work */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + goto unknown; + if (gadget_is_otg(gadget)) { + if (gadget->a_hnp_support) + debug("HNP available\n"); + else if (gadget->a_alt_hnp_support) + debug("HNP on another port\n"); + else + debug("HNP inactive\n"); + } + + value = set_config(cdev, ctrl, w_value); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + if (cdev->config) + *(u8 *)req->buf = cdev->config->bConfigurationValue; + else + *(u8 *)req->buf = 0; + value = min(w_length, (u16) 1); + break; + + /* + * function drivers must handle get/set altsetting; if there's + * no get() method, we know only altsetting zero works. + */ + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[intf]; + if (!f) + break; + if (w_value && !f->set_alt) + break; + value = f->set_alt(f, w_index, w_value); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[intf]; + if (!f) + break; + /* lots of interfaces only need altsetting zero... */ + value = f->get_alt ? f->get_alt(f, w_index) : 0; + if (value < 0) + break; + *((u8 *)req->buf) = value; + value = min(w_length, (u16) 1); + break; + default: +unknown: + debug("non-core control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* + * functions always handle their interfaces and endpoints... + * punt other recipients (other, WUSB, ...) to the current + * configuration code. + */ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + f = cdev->config->interface[intf]; + break; + + case USB_RECIP_ENDPOINT: + endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f); + list_for_each_entry(f, &cdev->config->functions, list) { + if (test_bit(endp, f->endpoints)) + break; + } + if (&f->list == &cdev->config->functions) + f = NULL; + break; + /* + * dfu-util (version 0.5) sets bmRequestType.Receipent = Device + * for non-standard request (w_value = 0x21, + * bRequest = GET_DESCRIPTOR in this case). + * When only one interface is registered (as it is done now), + * then this request shall be handled as it was requested for + * interface. + * + * In the below code it is checked if only one interface is + * present and proper function for it is extracted. Due to that + * function's setup (f->setup) is called to handle this + * special non-standard request. + */ + case USB_RECIP_DEVICE: + debug("cdev->config->next_interface_id: %d intf: %d\n", + cdev->config->next_interface_id, intf); + if (cdev->config->next_interface_id == 1) + f = cdev->config->interface[intf]; + break; + } + + if (f && f->setup) + value = f->setup(f, ctrl); + else { + c = cdev->config; + if (c && c->setup) + value = c->setup(c, ctrl); + } + + goto done; + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_KERNEL); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + } + +done: + /* device either stalls (value < 0) or reports success */ + return value; +} + +static void composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + + if (cdev->config) + reset_config(cdev); + if (composite->disconnect) + composite->disconnect(cdev); +} + +static void composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_configuration *c; + struct usb_function *f; + + /* + * composite_disconnect() must already have been called + * by the underlying peripheral controller driver! + * so there's no i/o concurrency that could affect the + * state protected by cdev->lock. + */ + BUG_ON(cdev->config); + + while (!list_empty(&cdev->configs)) { + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); + while (!list_empty(&c->functions)) { + f = list_first_entry(&c->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + debug("unbind function '%s'/%p\n", + f->name, f); + f->unbind(c, f); + } + } + list_del(&c->list); + if (c->unbind) { + debug("unbind config '%s'/%p\n", c->label, c); + c->unbind(c); + } + } + if (composite->unbind) + composite->unbind(cdev); + + if (cdev->req) { + kfree(cdev->req->buf); + usb_ep_free_request(gadget->ep0, cdev->req); + } + kfree(cdev); + set_gadget_data(gadget, NULL); + + composite = NULL; +} + +static int composite_bind(struct usb_gadget *gadget) +{ + int status = -ENOMEM; + struct usb_composite_dev *cdev; + + cdev = calloc(sizeof *cdev, 1); + if (!cdev) + return status; + + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + INIT_LIST_HEAD(&cdev->configs); + + /* preallocate control response and buffer */ + cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!cdev->req) + goto fail; + cdev->req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, USB_BUFSIZ); + if (!cdev->req->buf) + goto fail; + cdev->req->complete = composite_setup_complete; + gadget->ep0->driver_data = cdev; + + cdev->bufsiz = USB_BUFSIZ; + cdev->driver = composite; + + usb_gadget_set_selfpowered(gadget); + usb_ep_autoconfig_reset(cdev->gadget); + + status = composite->bind(cdev); + if (status < 0) + goto fail; + + memcpy(&cdev->desc, composite->dev, + sizeof(struct usb_device_descriptor)); + cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + debug("%s: ready\n", composite->name); + return 0; + +fail: + composite_unbind(gadget); + return status; +} + +static void +composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + debug("%s: suspend\n", __func__); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->suspend) + f->suspend(f); + } + } + if (composite->suspend) + composite->suspend(cdev); + + cdev->suspended = 1; +} + +static void +composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + debug("%s: resume\n", __func__); + if (composite->resume) + composite->resume(cdev); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->resume) + f->resume(f); + } + } + + cdev->suspended = 0; +} + +static struct usb_gadget_driver composite_driver = { + .speed = USB_SPEED_HIGH, + + .bind = composite_bind, + .unbind = composite_unbind, + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .suspend = composite_suspend, + .resume = composite_resume, +}; + +/** + * usb_composite_register() - register a composite driver + * @driver: the driver to register + * Context: single threaded during gadget setup + * + * This function is used to register drivers using the composite driver + * framework. The return value is zero, or a negative errno value. + * Those values normally come from the driver's @bind method, which does + * all the work of setting up the driver to match the hardware. + * + * On successful return, the gadget is ready to respond to requests from + * the host, unless one of its components invokes usb_gadget_disconnect() + * while it was binding. That would usually be done in order to wait for + * some userspace participation. + */ +int usb_composite_register(struct usb_composite_driver *driver) +{ + if (!driver || !driver->dev || !driver->bind || composite) + return -EINVAL; + + if (!driver->name) + driver->name = "composite"; + composite = driver; + + return usb_gadget_register_driver(&composite_driver); +} + +/** + * usb_composite_unregister() - unregister a composite driver + * @driver: the driver to unregister + * + * This function is used to unregister drivers using the composite + * driver framework. + */ +void usb_composite_unregister(struct usb_composite_driver *driver) +{ + if (composite != driver) + return; + usb_gadget_unregister_driver(&composite_driver); + composite = NULL; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/config.c b/qemu/roms/u-boot/drivers/usb/gadget/config.c new file mode 100644 index 000000000..014a6791c --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/config.c @@ -0,0 +1,108 @@ +/* + * usb/gadget/config.c -- simplify building config descriptors + * + * Copyright (C) 2003 David Brownell + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Ported to U-boot by: Thomas Smits and + * Remy Bohmer + */ + +#include +#include +#include +#include +#include + +#include +#include + + +/** + * usb_descriptor_fillbuf - fill buffer with descriptors + * @buf: Buffer to be filled + * @buflen: Size of buf + * @src: Array of descriptor pointers, terminated by null pointer. + * + * Copies descriptors into the buffer, returning the length or a + * negative error code if they can't all be copied. Useful when + * assembling descriptors for an associated set of interfaces used + * as part of configuring a composite device; or in other cases where + * sets of descriptors need to be marshaled. + */ +int +usb_descriptor_fillbuf(void *buf, unsigned buflen, + const struct usb_descriptor_header **src) +{ + u8 *dest = buf; + + if (!src) + return -EINVAL; + + /* fill buffer from src[] until null descriptor ptr */ + for (; NULL != *src; src++) { + unsigned len = (*src)->bLength; + + if (len > buflen) + return -EINVAL; + memcpy(dest, *src, len); + buflen -= len; + dest += len; + } + return dest - (u8 *)buf; +} + + +/** + * usb_gadget_config_buf - builts a complete configuration descriptor + * @config: Header for the descriptor, including characteristics such + * as power requirements and number of interfaces. + * @desc: Null-terminated vector of pointers to the descriptors (interface, + * endpoint, etc) defining all functions in this device configuration. + * @buf: Buffer for the resulting configuration descriptor. + * @length: Length of buffer. If this is not big enough to hold the + * entire configuration descriptor, an error code will be returned. + * + * This copies descriptors into the response buffer, building a descriptor + * for that configuration. It returns the buffer length or a negative + * status code. The config.wTotalLength field is set to match the length + * of the result, but other descriptor fields (including power usage and + * interface count) must be set by the caller. + * + * Gadget drivers could use this when constructing a config descriptor + * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the + * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed. + */ +int usb_gadget_config_buf( + const struct usb_config_descriptor *config, + void *buf, + unsigned length, + const struct usb_descriptor_header **desc +) +{ + struct usb_config_descriptor *cp = buf; + int len; + + /* config descriptor first */ + if (length < USB_DT_CONFIG_SIZE || !desc) + return -EINVAL; + /* config need not be aligned */ + memcpy(cp, config, sizeof(*cp)); + + /* then interface/endpoint/class/vendor/... */ + len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf, + length - USB_DT_CONFIG_SIZE, desc); + if (len < 0) + return len; + len += USB_DT_CONFIG_SIZE; + if (len > 0xffff) + return -EINVAL; + + /* patch up the config descriptor */ + cp->bLength = USB_DT_CONFIG_SIZE; + cp->bDescriptorType = USB_DT_CONFIG; + put_unaligned_le16(len, &cp->wTotalLength); + cp->bmAttributes |= USB_CONFIG_ATT_ONE; + return len; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/core.c b/qemu/roms/u-boot/drivers/usb/gadget/core.c new file mode 100644 index 000000000..30d55a49a --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/core.c @@ -0,0 +1,669 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * Based on + * linux/drivers/usbd/usbd.c.c - USB Device Core Layer + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#define MAX_INTERFACES 2 + + +int maxstrings = 20; + +/* Global variables ************************************************************************** */ + +struct usb_string_descriptor **usb_strings; + +int usb_devices; + +extern struct usb_function_driver ep0_driver; + +int registered_functions; +int registered_devices; + +char *usbd_device_events[] = { + "DEVICE_UNKNOWN", + "DEVICE_INIT", + "DEVICE_CREATE", + "DEVICE_HUB_CONFIGURED", + "DEVICE_RESET", + "DEVICE_ADDRESS_ASSIGNED", + "DEVICE_CONFIGURED", + "DEVICE_SET_INTERFACE", + "DEVICE_SET_FEATURE", + "DEVICE_CLEAR_FEATURE", + "DEVICE_DE_CONFIGURED", + "DEVICE_BUS_INACTIVE", + "DEVICE_BUS_ACTIVITY", + "DEVICE_POWER_INTERRUPTION", + "DEVICE_HUB_RESET", + "DEVICE_DESTROY", + "DEVICE_FUNCTION_PRIVATE", +}; + +char *usbd_device_states[] = { + "STATE_INIT", + "STATE_CREATED", + "STATE_ATTACHED", + "STATE_POWERED", + "STATE_DEFAULT", + "STATE_ADDRESSED", + "STATE_CONFIGURED", + "STATE_UNKNOWN", +}; + +char *usbd_device_requests[] = { + "GET STATUS", /* 0 */ + "CLEAR FEATURE", /* 1 */ + "RESERVED", /* 2 */ + "SET FEATURE", /* 3 */ + "RESERVED", /* 4 */ + "SET ADDRESS", /* 5 */ + "GET DESCRIPTOR", /* 6 */ + "SET DESCRIPTOR", /* 7 */ + "GET CONFIGURATION", /* 8 */ + "SET CONFIGURATION", /* 9 */ + "GET INTERFACE", /* 10 */ + "SET INTERFACE", /* 11 */ + "SYNC FRAME", /* 12 */ +}; + +char *usbd_device_descriptors[] = { + "UNKNOWN", /* 0 */ + "DEVICE", /* 1 */ + "CONFIG", /* 2 */ + "STRING", /* 3 */ + "INTERFACE", /* 4 */ + "ENDPOINT", /* 5 */ + "DEVICE QUALIFIER", /* 6 */ + "OTHER SPEED", /* 7 */ + "INTERFACE POWER", /* 8 */ +}; + +char *usbd_device_status[] = { + "USBD_OPENING", + "USBD_OK", + "USBD_SUSPENDED", + "USBD_CLOSING", +}; + + +/* Descriptor support functions ************************************************************** */ + + +/** + * usbd_get_string - find and return a string descriptor + * @index: string index to return + * + * Find an indexed string and return a pointer to a it. + */ +struct usb_string_descriptor *usbd_get_string (__u8 index) +{ + if (index >= maxstrings) { + return NULL; + } + return usb_strings[index]; +} + + +/* Access to device descriptor functions ***************************************************** */ + + +/* * + * usbd_device_configuration_instance - find a configuration instance for this device + * @device: + * @configuration: index to configuration, 0 - N-1 + * + * Get specifed device configuration. Index should be bConfigurationValue-1. + */ +static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device, + unsigned int port, unsigned int configuration) +{ + if (configuration >= device->configurations) + return NULL; + + return device->configuration_instance_array + configuration; +} + + +/* * + * usbd_device_interface_instance + * @device: + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * + * Return the specified interface descriptor for the specified device. + */ +struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface) +{ + struct usb_configuration_instance *configuration_instance; + + if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) { + return NULL; + } + if (interface >= configuration_instance->interfaces) { + return NULL; + } + return configuration_instance->interface_instance_array + interface; +} + +/* * + * usbd_device_alternate_descriptor_list + * @device: + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: alternate setting + * + * Return the specified alternate descriptor for the specified device. + */ +struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate) +{ + struct usb_interface_instance *interface_instance; + + if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) { + return NULL; + } + + if (alternate >= interface_instance->alternates) { + return NULL; + } + + return interface_instance->alternates_instance_array + alternate; +} + + +/* * + * usbd_device_device_descriptor + * @device: which device + * @configuration: index to configuration, 0 - N-1 + * @port: which port + * + * Return the specified configuration descriptor for the specified device. + */ +struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port) +{ + return (device->device_descriptor); +} + +/** + * usbd_device_configuration_descriptor + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * + * Return the specified configuration descriptor for the specified device. + */ +struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct + usb_device_instance + *device, int port, int configuration) +{ + struct usb_configuration_instance *configuration_instance; + if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) { + return NULL; + } + return (configuration_instance->configuration_descriptor); +} + + +/** + * usbd_device_interface_descriptor + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: alternate setting + * + * Return the specified interface descriptor for the specified device. + */ +struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance + *device, int port, int configuration, int interface, int alternate) +{ + struct usb_interface_instance *interface_instance; + if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) { + return NULL; + } + if ((alternate < 0) || (alternate >= interface_instance->alternates)) { + return NULL; + } + return (interface_instance->alternates_instance_array[alternate].interface_descriptor); +} + +/** + * usbd_device_endpoint_descriptor_index + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: index setting + * @index: which index + * + * Return the specified endpoint descriptor for the specified device. + */ +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance + *device, int port, int configuration, int interface, int alternate, int index) +{ + struct usb_alternate_instance *alternate_instance; + + if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { + return NULL; + } + if (index >= alternate_instance->endpoints) { + return NULL; + } + return *(alternate_instance->endpoints_descriptor_array + index); +} + + +/** + * usbd_device_endpoint_transfersize + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @index: which index + * + * Return the specified endpoint transfer size; + */ +int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) +{ + struct usb_alternate_instance *alternate_instance; + + if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { + return 0; + } + if (index >= alternate_instance->endpoints) { + return 0; + } + return *(alternate_instance->endpoint_transfersize_array + index); +} + + +/** + * usbd_device_endpoint_descriptor + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: alternate setting + * @endpoint: which endpoint + * + * Return the specified endpoint descriptor for the specified device. + */ +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint) +{ + struct usb_endpoint_descriptor *endpoint_descriptor; + int i; + + for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) { + if (endpoint_descriptor->bEndpointAddress == endpoint) { + return endpoint_descriptor; + } + } + return NULL; +} + +/** + * usbd_endpoint_halted + * @device: point to struct usb_device_instance + * @endpoint: endpoint to check + * + * Return non-zero if endpoint is halted. + */ +int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint) +{ + return (device->status == USB_STATUS_HALT); +} + + +/** + * usbd_rcv_complete - complete a receive + * @endpoint: + * @len: + * @urb_bad: + * + * Called from rcv interrupt to complete. + */ +void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad) +{ + if (endpoint) { + struct urb *rcv_urb; + + /*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */ + + /* if we had an urb then update actual_length, dispatch if neccessary */ + if ((rcv_urb = endpoint->rcv_urb)) { + + /*usbdbg("actual: %d buffer: %d\n", */ + /*rcv_urb->actual_length, rcv_urb->buffer_length); */ + + /* check the urb is ok, are we adding data less than the packetsize */ + if (!urb_bad && (len <= endpoint->rcv_packetSize)) { + /*usbdbg("updating actual_length by %d\n",len); */ + + /* increment the received data size */ + rcv_urb->actual_length += len; + + } else { + usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n", + rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad); + + rcv_urb->actual_length = 0; + rcv_urb->status = RECV_ERROR; + } + } else { + usberr("no rcv_urb!"); + } + } else { + usberr("no endpoint!"); + } + +} + +/** + * usbd_tx_complete - complete a transmit + * @endpoint: + * @resetart: + * + * Called from tx interrupt to complete. + */ +void usbd_tx_complete (struct usb_endpoint_instance *endpoint) +{ + if (endpoint) { + struct urb *tx_urb; + + /* if we have a tx_urb advance or reset, finish if complete */ + if ((tx_urb = endpoint->tx_urb)) { + int sent = endpoint->last; + endpoint->sent += sent; + endpoint->last -= sent; + + if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) { + tx_urb->actual_length = 0; + endpoint->sent = 0; + endpoint->last = 0; + + /* Remove from active, save for re-use */ + urb_detach(tx_urb); + urb_append(&endpoint->done, tx_urb); + /*usbdbg("done->next %p, tx_urb %p, done %p", */ + /* endpoint->done.next, tx_urb, &endpoint->done); */ + + endpoint->tx_urb = first_urb_detached(&endpoint->tx); + if( endpoint->tx_urb ) { + endpoint->tx_queue--; + usbdbg("got urb from tx list"); + } + if( !endpoint->tx_urb ) { + /*usbdbg("taking urb from done list"); */ + endpoint->tx_urb = first_urb_detached(&endpoint->done); + } + if( !endpoint->tx_urb ) { + usbdbg("allocating new urb for tx_urb"); + endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint); + } + } + } + } +} + +/* URB linked list functions ***************************************************** */ + +/* + * Initialize an urb_link to be a single element list. + * If the urb_link is being used as a distinguished list head + * the list is empty when the head is the only link in the list. + */ +void urb_link_init (urb_link * ul) +{ + if (ul) { + ul->prev = ul->next = ul; + } +} + +/* + * Detach an urb_link from a list, and set it + * up as a single element list, so no dangling + * pointers can be followed, and so it can be + * joined to another list if so desired. + */ +void urb_detach (struct urb *urb) +{ + if (urb) { + urb_link *ul = &urb->link; + ul->next->prev = ul->prev; + ul->prev->next = ul->next; + urb_link_init (ul); + } +} + +/* + * Return the first urb_link in a list with a distinguished + * head "hd", or NULL if the list is empty. This will also + * work as a predicate, returning NULL if empty, and non-NULL + * otherwise. + */ +urb_link *first_urb_link (urb_link * hd) +{ + urb_link *nx; + if (NULL != hd && NULL != (nx = hd->next) && nx != hd) { + /* There is at least one element in the list */ + /* (besides the distinguished head). */ + return (nx); + } + /* The list is empty */ + return (NULL); +} + +/* + * Return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + */ +struct urb *first_urb (urb_link * hd) +{ + urb_link *nx; + if (NULL == (nx = first_urb_link (hd))) { + /* The list is empty */ + return (NULL); + } + return (p2surround (struct urb, link, nx)); +} + +/* + * Detach and return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + * + */ +struct urb *first_urb_detached (urb_link * hd) +{ + struct urb *urb; + if ((urb = first_urb (hd))) { + urb_detach (urb); + } + return urb; +} + + +/* + * Append an urb_link (or a whole list of + * urb_links) to the tail of another list + * of urb_links. + */ +void urb_append (urb_link * hd, struct urb *urb) +{ + if (hd && urb) { + urb_link *new = &urb->link; + + /* This allows the new urb to be a list of urbs, */ + /* with new pointing at the first, but the link */ + /* must be initialized. */ + /* Order is important here... */ + urb_link *pul = hd->prev; + new->prev->next = hd; + hd->prev = new->prev; + new->prev = pul; + pul->next = new; + } +} + +/* URB create/destroy functions ***************************************************** */ + +/** + * usbd_alloc_urb - allocate an URB appropriate for specified endpoint + * @device: device instance + * @endpoint: endpoint + * + * Allocate an urb structure. The usb device urb structure is used to + * contain all data associated with a transfer, including a setup packet for + * control transfers. + * + * NOTE: endpoint_address MUST contain a direction flag. + */ +struct urb *usbd_alloc_urb (struct usb_device_instance *device, + struct usb_endpoint_instance *endpoint) +{ + struct urb *urb; + + if (!(urb = (struct urb *) malloc (sizeof (struct urb)))) { + usberr (" F A T A L: malloc(%zu) FAILED!!!!", + sizeof (struct urb)); + return NULL; + } + + /* Fill in known fields */ + memset (urb, 0, sizeof (struct urb)); + urb->endpoint = endpoint; + urb->device = device; + urb->buffer = (u8 *) urb->buffer_data; + urb->buffer_length = sizeof (urb->buffer_data); + + urb_link_init (&urb->link); + + return urb; +} + +/** + * usbd_dealloc_urb - deallocate an URB and associated buffer + * @urb: pointer to an urb structure + * + * Deallocate an urb structure and associated data. + */ +void usbd_dealloc_urb (struct urb *urb) +{ + if (urb) { + free (urb); + } +} + +/* Event signaling functions ***************************************************** */ + +/** + * usbd_device_event - called to respond to various usb events + * @device: pointer to struct device + * @event: event to respond to + * + * Used by a Bus driver to indicate an event. + */ +void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data) +{ + usb_device_state_t state; + + if (!device || !device->bus) { + usberr("(%p,%d) NULL device or device->bus", device, event); + return; + } + + state = device->device_state; + + usbinfo("%s", usbd_device_events[event]); + + switch (event) { + case DEVICE_UNKNOWN: + break; + case DEVICE_INIT: + device->device_state = STATE_INIT; + break; + + case DEVICE_CREATE: + device->device_state = STATE_ATTACHED; + break; + + case DEVICE_HUB_CONFIGURED: + device->device_state = STATE_POWERED; + break; + + case DEVICE_RESET: + device->device_state = STATE_DEFAULT; + device->address = 0; + break; + + case DEVICE_ADDRESS_ASSIGNED: + device->device_state = STATE_ADDRESSED; + break; + + case DEVICE_CONFIGURED: + device->device_state = STATE_CONFIGURED; + break; + + case DEVICE_DE_CONFIGURED: + device->device_state = STATE_ADDRESSED; + break; + + case DEVICE_BUS_INACTIVE: + if (device->status != USBD_CLOSING) { + device->status = USBD_SUSPENDED; + } + break; + case DEVICE_BUS_ACTIVITY: + if (device->status != USBD_CLOSING) { + device->status = USBD_OK; + } + break; + + case DEVICE_SET_INTERFACE: + break; + case DEVICE_SET_FEATURE: + break; + case DEVICE_CLEAR_FEATURE: + break; + + case DEVICE_POWER_INTERRUPTION: + device->device_state = STATE_POWERED; + break; + case DEVICE_HUB_RESET: + device->device_state = STATE_ATTACHED; + break; + case DEVICE_DESTROY: + device->device_state = STATE_UNKNOWN; + break; + + case DEVICE_FUNCTION_PRIVATE: + break; + + default: + usbdbg("event %d - not handled",event); + break; + } + debug("%s event: %d oldstate: %d newstate: %d status: %d address: %d", + device->name, event, state, + device->device_state, device->status, device->address); + + /* tell the bus interface driver */ + if( device->event ) { + /* usbdbg("calling device->event"); */ + device->event(device, event, data); + } +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/designware_udc.c b/qemu/roms/u-boot/drivers/usb/gadget/designware_udc.c new file mode 100644 index 000000000..b7c10384a --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/designware_udc.c @@ -0,0 +1,1019 @@ +/* + * Based on drivers/usb/gadget/omap1510_udc.c + * TI OMAP1510 USB bus interface driver + * + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include +#include "ep0.h" +#include +#include +#include + +#define UDC_INIT_MDELAY 80 /* Device settle delay */ + +/* Some kind of debugging output... */ +#ifndef DEBUG_DWUSBTTY +#define UDCDBG(str) +#define UDCDBGA(fmt, args...) +#else +#define UDCDBG(str) serial_printf(str "\n") +#define UDCDBGA(fmt, args...) serial_printf(fmt "\n", ##args) +#endif + +static struct urb *ep0_urb; +static struct usb_device_instance *udc_device; + +static struct plug_regs *const plug_regs_p = + (struct plug_regs * const)CONFIG_SYS_PLUG_BASE; +static struct udc_regs *const udc_regs_p = + (struct udc_regs * const)CONFIG_SYS_USBD_BASE; +static struct udc_endp_regs *const outep_regs_p = + &((struct udc_regs * const)CONFIG_SYS_USBD_BASE)->out_regs[0]; +static struct udc_endp_regs *const inep_regs_p = + &((struct udc_regs * const)CONFIG_SYS_USBD_BASE)->in_regs[0]; + +/* + * udc_state_transition - Write the next packet to TxFIFO. + * @initial: Initial state. + * @final: Final state. + * + * Helper function to implement device state changes. The device states and + * the events that transition between them are: + * + * STATE_ATTACHED + * || /\ + * \/ || + * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET + * || /\ + * \/ || + * STATE_POWERED + * || /\ + * \/ || + * DEVICE_RESET DEVICE_POWER_INTERRUPTION + * || /\ + * \/ || + * STATE_DEFAULT + * || /\ + * \/ || + * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET + * || /\ + * \/ || + * STATE_ADDRESSED + * || /\ + * \/ || + * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED + * || /\ + * \/ || + * STATE_CONFIGURED + * + * udc_state_transition transitions up (in the direction from STATE_ATTACHED + * to STATE_CONFIGURED) from the specified initial state to the specified final + * state, passing through each intermediate state on the way. If the initial + * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then + * no state transitions will take place. + * + * udc_state_transition also transitions down (in the direction from + * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the + * specified final state, passing through each intermediate state on the way. + * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final + * state, then no state transitions will take place. + * + * This function must only be called with interrupts disabled. + */ +static void udc_state_transition(usb_device_state_t initial, + usb_device_state_t final) +{ + if (initial < final) { + switch (initial) { + case STATE_ATTACHED: + usbd_device_event_irq(udc_device, + DEVICE_HUB_CONFIGURED, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq(udc_device, + DEVICE_ADDRESS_ASSIGNED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); + case STATE_CONFIGURED: + break; + default: + break; + } + } else if (initial > final) { + switch (initial) { + case STATE_CONFIGURED: + usbd_device_event_irq(udc_device, + DEVICE_DE_CONFIGURED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq(udc_device, + DEVICE_POWER_INTERRUPTION, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq(udc_device, DEVICE_HUB_RESET, 0); + case STATE_ATTACHED: + break; + default: + break; + } + } +} + +/* Stall endpoint */ +static void udc_stall_ep(u32 ep_num) +{ + writel(readl(&inep_regs_p[ep_num].endp_cntl) | ENDP_CNTL_STALL, + &inep_regs_p[ep_num].endp_cntl); + + writel(readl(&outep_regs_p[ep_num].endp_cntl) | ENDP_CNTL_STALL, + &outep_regs_p[ep_num].endp_cntl); +} + +static void *get_fifo(int ep_num, int in) +{ + u32 *fifo_ptr = (u32 *)CONFIG_SYS_FIFO_BASE; + + switch (ep_num) { + case UDC_EP3: + fifo_ptr += readl(&inep_regs_p[1].endp_bsorfn); + /* break intentionally left out */ + + case UDC_EP1: + fifo_ptr += readl(&inep_regs_p[0].endp_bsorfn); + /* break intentionally left out */ + + case UDC_EP0: + default: + if (in) { + fifo_ptr += + readl(&outep_regs_p[2].endp_maxpacksize) >> 16; + /* break intentionally left out */ + } else { + break; + } + + case UDC_EP2: + fifo_ptr += readl(&outep_regs_p[0].endp_maxpacksize) >> 16; + /* break intentionally left out */ + } + + return (void *)fifo_ptr; +} + +static int usbgetpckfromfifo(int epNum, u8 *bufp, u32 len) +{ + u8 *fifo_ptr = (u8 *)get_fifo(epNum, 0); + u32 i, nw, nb; + u32 *wrdp; + u8 *bytp; + u32 tmp[128]; + + if (readl(&udc_regs_p->dev_stat) & DEV_STAT_RXFIFO_EMPTY) + return -1; + + nw = len / sizeof(u32); + nb = len % sizeof(u32); + + /* use tmp buf if bufp is not word aligned */ + if ((int)bufp & 0x3) + wrdp = (u32 *)&tmp[0]; + else + wrdp = (u32 *)bufp; + + for (i = 0; i < nw; i++) { + writel(readl(fifo_ptr), wrdp); + wrdp++; + } + + bytp = (u8 *)wrdp; + for (i = 0; i < nb; i++) { + writeb(readb(fifo_ptr), bytp); + fifo_ptr++; + bytp++; + } + readl(&outep_regs_p[epNum].write_done); + + /* copy back tmp buffer to bufp if bufp is not word aligned */ + if ((int)bufp & 0x3) + memcpy(bufp, tmp, len); + + return 0; +} + +static void usbputpcktofifo(int epNum, u8 *bufp, u32 len) +{ + u32 i, nw, nb; + u32 *wrdp; + u8 *bytp; + u8 *fifo_ptr = get_fifo(epNum, 1); + + nw = len / sizeof(int); + nb = len % sizeof(int); + wrdp = (u32 *)bufp; + for (i = 0; i < nw; i++) { + writel(*wrdp, fifo_ptr); + wrdp++; + } + + bytp = (u8 *)wrdp; + for (i = 0; i < nb; i++) { + writeb(*bytp, fifo_ptr); + fifo_ptr++; + bytp++; + } +} + +/* + * dw_write_noniso_tx_fifo - Write the next packet to TxFIFO. + * @endpoint: Endpoint pointer. + * + * If the endpoint has an active tx_urb, then the next packet of data from the + * URB is written to the tx FIFO. The total amount of data in the urb is given + * by urb->actual_length. The maximum amount of data that can be sent in any + * one packet is given by endpoint->tx_packetSize. The number of data bytes + * from this URB that have already been transmitted is given by endpoint->sent. + * endpoint->last is updated by this routine with the number of data bytes + * transmitted in this packet. + * + */ +static void dw_write_noniso_tx_fifo(struct usb_endpoint_instance + *endpoint) +{ + struct urb *urb = endpoint->tx_urb; + int align; + + if (urb) { + u32 last; + + UDCDBGA("urb->buffer %p, buffer_length %d, actual_length %d", + urb->buffer, urb->buffer_length, urb->actual_length); + + last = MIN(urb->actual_length - endpoint->sent, + endpoint->tx_packetSize); + + if (last) { + u8 *cp = urb->buffer + endpoint->sent; + + /* + * This ensures that USBD packet fifo is accessed + * - through word aligned pointer or + * - through non word aligned pointer but only + * with a max length to make the next packet + * word aligned + */ + + align = ((ulong)cp % sizeof(int)); + if (align) + last = MIN(last, sizeof(int) - align); + + UDCDBGA("endpoint->sent %d, tx_packetSize %d, last %d", + endpoint->sent, endpoint->tx_packetSize, last); + + usbputpcktofifo(endpoint->endpoint_address & + USB_ENDPOINT_NUMBER_MASK, cp, last); + } + endpoint->last = last; + } +} + +/* + * Handle SETUP USB interrupt. + * This function implements TRM Figure 14-14. + */ +static void dw_udc_setup(struct usb_endpoint_instance *endpoint) +{ + u8 *datap = (u8 *)&ep0_urb->device_request; + int ep_addr = endpoint->endpoint_address; + + UDCDBG("-> Entering device setup"); + usbgetpckfromfifo(ep_addr, datap, 8); + + /* Try to process setup packet */ + if (ep0_recv_setup(ep0_urb)) { + /* Not a setup packet, stall next EP0 transaction */ + udc_stall_ep(0); + UDCDBG("can't parse setup packet, still waiting for setup"); + return; + } + + /* Check direction */ + if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) + == USB_REQ_HOST2DEVICE) { + UDCDBG("control write on EP0"); + if (le16_to_cpu(ep0_urb->device_request.wLength)) { + /* Stall this request */ + UDCDBG("Stalling unsupported EP0 control write data " + "stage."); + udc_stall_ep(0); + } + } else { + + UDCDBG("control read on EP0"); + /* + * The ep0_recv_setup function has already placed our response + * packet data in ep0_urb->buffer and the packet length in + * ep0_urb->actual_length. + */ + endpoint->tx_urb = ep0_urb; + endpoint->sent = 0; + /* + * Write packet data to the FIFO. dw_write_noniso_tx_fifo + * will update endpoint->last with the number of bytes written + * to the FIFO. + */ + dw_write_noniso_tx_fifo(endpoint); + + writel(0x0, &inep_regs_p[ep_addr].write_done); + } + + udc_unset_nak(endpoint->endpoint_address); + + UDCDBG("<- Leaving device setup"); +} + +/* + * Handle endpoint 0 RX interrupt + */ +static void dw_udc_ep0_rx(struct usb_endpoint_instance *endpoint) +{ + u8 dummy[64]; + + UDCDBG("RX on EP0"); + + /* Check direction */ + if ((ep0_urb->device_request.bmRequestType + & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + /* + * This rx interrupt must be for a control write data + * stage packet. + * + * We don't support control write data stages. + * We should never end up here. + */ + + UDCDBG("Stalling unexpected EP0 control write " + "data stage packet"); + udc_stall_ep(0); + } else { + /* + * This rx interrupt must be for a control read status + * stage packet. + */ + UDCDBG("ACK on EP0 control read status stage packet"); + u32 len = (readl(&outep_regs_p[0].endp_status) >> 11) & 0xfff; + usbgetpckfromfifo(0, dummy, len); + } +} + +/* + * Handle endpoint 0 TX interrupt + */ +static void dw_udc_ep0_tx(struct usb_endpoint_instance *endpoint) +{ + struct usb_device_request *request = &ep0_urb->device_request; + int ep_addr; + + UDCDBG("TX on EP0"); + + /* Check direction */ + if ((request->bmRequestType & USB_REQ_DIRECTION_MASK) == + USB_REQ_HOST2DEVICE) { + /* + * This tx interrupt must be for a control write status + * stage packet. + */ + UDCDBG("ACK on EP0 control write status stage packet"); + } else { + /* + * This tx interrupt must be for a control read data + * stage packet. + */ + int wLength = le16_to_cpu(request->wLength); + + /* + * Update our count of bytes sent so far in this + * transfer. + */ + endpoint->sent += endpoint->last; + + /* + * We are finished with this transfer if we have sent + * all of the bytes in our tx urb (urb->actual_length) + * unless we need a zero-length terminating packet. We + * need a zero-length terminating packet if we returned + * fewer bytes than were requested (wLength) by the host, + * and the number of bytes we returned is an exact + * multiple of the packet size endpoint->tx_packetSize. + */ + if ((endpoint->sent == ep0_urb->actual_length) && + ((ep0_urb->actual_length == wLength) || + (endpoint->last != endpoint->tx_packetSize))) { + /* Done with control read data stage. */ + UDCDBG("control read data stage complete"); + } else { + /* + * We still have another packet of data to send + * in this control read data stage or else we + * need a zero-length terminating packet. + */ + UDCDBG("ACK control read data stage packet"); + dw_write_noniso_tx_fifo(endpoint); + + ep_addr = endpoint->endpoint_address; + writel(0x0, &inep_regs_p[ep_addr].write_done); + } + } +} + +static struct usb_endpoint_instance *dw_find_ep(int ep) +{ + int i; + + for (i = 0; i < udc_device->bus->max_endpoints; i++) { + if ((udc_device->bus->endpoint_array[i].endpoint_address & + USB_ENDPOINT_NUMBER_MASK) == ep) + return &udc_device->bus->endpoint_array[i]; + } + return NULL; +} + +/* + * Handle RX transaction on non-ISO endpoint. + * The ep argument is a physical endpoint number for a non-ISO IN endpoint + * in the range 1 to 15. + */ +static void dw_udc_epn_rx(int ep) +{ + int nbytes = 0; + struct urb *urb; + struct usb_endpoint_instance *endpoint = dw_find_ep(ep); + + if (endpoint) { + urb = endpoint->rcv_urb; + + if (urb) { + u8 *cp = urb->buffer + urb->actual_length; + + nbytes = (readl(&outep_regs_p[ep].endp_status) >> 11) & + 0xfff; + usbgetpckfromfifo(ep, cp, nbytes); + usbd_rcv_complete(endpoint, nbytes, 0); + } + } +} + +/* + * Handle TX transaction on non-ISO endpoint. + * The ep argument is a physical endpoint number for a non-ISO IN endpoint + * in the range 16 to 30. + */ +static void dw_udc_epn_tx(int ep) +{ + struct usb_endpoint_instance *endpoint = dw_find_ep(ep); + + if (!endpoint) + return; + + /* + * We need to transmit a terminating zero-length packet now if + * we have sent all of the data in this URB and the transfer + * size was an exact multiple of the packet size. + */ + if (endpoint->tx_urb && + (endpoint->last == endpoint->tx_packetSize) && + (endpoint->tx_urb->actual_length - endpoint->sent - + endpoint->last == 0)) { + /* handle zero length packet here */ + writel(0x0, &inep_regs_p[ep].write_done); + + } + + if (endpoint->tx_urb && endpoint->tx_urb->actual_length) { + /* retire the data that was just sent */ + usbd_tx_complete(endpoint); + /* + * Check to see if we have more data ready to transmit + * now. + */ + if (endpoint->tx_urb && endpoint->tx_urb->actual_length) { + /* write data to FIFO */ + dw_write_noniso_tx_fifo(endpoint); + writel(0x0, &inep_regs_p[ep].write_done); + + } else if (endpoint->tx_urb + && (endpoint->tx_urb->actual_length == 0)) { + /* udc_set_nak(ep); */ + } + } +} + +/* + * Start of public functions. + */ + +/* Called to start packet transmission. */ +int udc_endpoint_write(struct usb_endpoint_instance *endpoint) +{ + udc_unset_nak(endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK); + return 0; +} + +/* Start to initialize h/w stuff */ +int udc_init(void) +{ + int i; + u32 plug_st; + + udc_device = NULL; + + UDCDBG("starting"); + + readl(&plug_regs_p->plug_pending); + + for (i = 0; i < UDC_INIT_MDELAY; i++) + udelay(1000); + + plug_st = readl(&plug_regs_p->plug_state); + writel(plug_st | PLUG_STATUS_EN, &plug_regs_p->plug_state); + + writel(~0x0, &udc_regs_p->endp_int); + writel(~0x0, &udc_regs_p->dev_int_mask); + writel(~0x0, &udc_regs_p->endp_int_mask); + +#ifndef CONFIG_USBD_HS + writel(DEV_CONF_FS_SPEED | DEV_CONF_REMWAKEUP | DEV_CONF_SELFPOW | + DEV_CONF_PHYINT_16, &udc_regs_p->dev_conf); +#else + writel(DEV_CONF_HS_SPEED | DEV_CONF_REMWAKEUP | DEV_CONF_SELFPOW | + DEV_CONF_PHYINT_16, &udc_regs_p->dev_conf); +#endif + + writel(DEV_CNTL_SOFTDISCONNECT, &udc_regs_p->dev_cntl); + + /* Clear all interrupts pending */ + writel(DEV_INT_MSK, &udc_regs_p->dev_int); + + return 0; +} + +int is_usbd_high_speed(void) +{ + return (readl(&udc_regs_p->dev_stat) & DEV_STAT_ENUM) ? 0 : 1; +} + +/* + * udc_setup_ep - setup endpoint + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep(struct usb_device_instance *device, + u32 ep, struct usb_endpoint_instance *endpoint) +{ + UDCDBGA("setting up endpoint addr %x", endpoint->endpoint_address); + int ep_addr; + int ep_num, ep_type; + int packet_size; + int buffer_size; + int attributes; + char *tt; + u32 endp_intmask; + + if ((ep != 0) && (udc_device->device_state < STATE_ADDRESSED)) + return; + + tt = getenv("usbtty"); + if (!tt) + tt = "generic"; + + ep_addr = endpoint->endpoint_address; + ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + + if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* IN endpoint */ + packet_size = endpoint->tx_packetSize; + buffer_size = packet_size * 2; + attributes = endpoint->tx_attributes; + } else { + /* OUT endpoint */ + packet_size = endpoint->rcv_packetSize; + buffer_size = packet_size * 2; + attributes = endpoint->rcv_attributes; + } + + switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + ep_type = ENDP_EPTYPE_CNTL; + break; + case USB_ENDPOINT_XFER_BULK: + default: + ep_type = ENDP_EPTYPE_BULK; + break; + case USB_ENDPOINT_XFER_INT: + ep_type = ENDP_EPTYPE_INT; + break; + case USB_ENDPOINT_XFER_ISOC: + ep_type = ENDP_EPTYPE_ISO; + break; + } + + struct udc_endp_regs *out_p = &outep_regs_p[ep_num]; + struct udc_endp_regs *in_p = &inep_regs_p[ep_num]; + + if (!ep_addr) { + /* Setup endpoint 0 */ + buffer_size = packet_size; + + writel(readl(&in_p->endp_cntl) | ENDP_CNTL_CNAK, + &in_p->endp_cntl); + + writel(readl(&out_p->endp_cntl) | ENDP_CNTL_CNAK, + &out_p->endp_cntl); + + writel(ENDP_CNTL_CONTROL | ENDP_CNTL_FLUSH, &in_p->endp_cntl); + + writel(buffer_size / sizeof(int), &in_p->endp_bsorfn); + + writel(packet_size, &in_p->endp_maxpacksize); + + writel(ENDP_CNTL_CONTROL | ENDP_CNTL_RRDY, &out_p->endp_cntl); + + writel(packet_size | ((buffer_size / sizeof(int)) << 16), + &out_p->endp_maxpacksize); + + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* Setup the IN endpoint */ + writel(0x0, &in_p->endp_status); + writel((ep_type << 4) | ENDP_CNTL_RRDY, &in_p->endp_cntl); + writel(buffer_size / sizeof(int), &in_p->endp_bsorfn); + writel(packet_size, &in_p->endp_maxpacksize); + + if (!strcmp(tt, "cdc_acm")) { + if (ep_type == ENDP_EPTYPE_INT) { + /* Conf no. 1 Interface no. 0 */ + writel((packet_size << 19) | + ENDP_EPDIR_IN | (1 << 7) | + (0 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } else { + /* Conf no. 1 Interface no. 1 */ + writel((packet_size << 19) | + ENDP_EPDIR_IN | (1 << 7) | + (1 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } + } else { + /* Conf no. 1 Interface no. 0 */ + writel((packet_size << 19) | + ENDP_EPDIR_IN | (1 << 7) | + (0 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } + + } else { + /* Setup the OUT endpoint */ + writel(0x0, &out_p->endp_status); + writel((ep_type << 4) | ENDP_CNTL_RRDY, &out_p->endp_cntl); + writel(packet_size | ((buffer_size / sizeof(int)) << 16), + &out_p->endp_maxpacksize); + + if (!strcmp(tt, "cdc_acm")) { + writel((packet_size << 19) | + ENDP_EPDIR_OUT | (1 << 7) | + (1 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } else { + writel((packet_size << 19) | + ENDP_EPDIR_OUT | (1 << 7) | + (0 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } + + } + + endp_intmask = readl(&udc_regs_p->endp_int_mask); + endp_intmask &= ~((1 << ep_num) | 0x10000 << ep_num); + writel(endp_intmask, &udc_regs_p->endp_int_mask); +} + +/* Turn on the USB connection by enabling the pullup resistor */ +void udc_connect(void) +{ + u32 plug_st, dev_cntl; + + dev_cntl = readl(&udc_regs_p->dev_cntl); + dev_cntl |= DEV_CNTL_SOFTDISCONNECT; + writel(dev_cntl, &udc_regs_p->dev_cntl); + + udelay(1000); + + dev_cntl = readl(&udc_regs_p->dev_cntl); + dev_cntl &= ~DEV_CNTL_SOFTDISCONNECT; + writel(dev_cntl, &udc_regs_p->dev_cntl); + + plug_st = readl(&plug_regs_p->plug_state); + plug_st &= ~(PLUG_STATUS_PHY_RESET | PLUG_STATUS_PHY_MODE); + writel(plug_st, &plug_regs_p->plug_state); +} + +/* Turn off the USB connection by disabling the pullup resistor */ +void udc_disconnect(void) +{ + u32 plug_st; + + writel(DEV_CNTL_SOFTDISCONNECT, &udc_regs_p->dev_cntl); + + plug_st = readl(&plug_regs_p->plug_state); + plug_st |= (PLUG_STATUS_PHY_RESET | PLUG_STATUS_PHY_MODE); + writel(plug_st, &plug_regs_p->plug_state); +} + +/* Switch on the UDC */ +void udc_enable(struct usb_device_instance *device) +{ + UDCDBGA("enable device %p, status %d", device, device->status); + + /* Save the device structure pointer */ + udc_device = device; + + /* Setup ep0 urb */ + if (!ep0_urb) { + ep0_urb = + usbd_alloc_urb(udc_device, udc_device->bus->endpoint_array); + } else { + serial_printf("udc_enable: ep0_urb already allocated %p\n", + ep0_urb); + } + + writel(DEV_INT_SOF, &udc_regs_p->dev_int_mask); +} + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup_events(struct usb_device_instance *device) +{ + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ + usbd_device_event_irq(device, DEVICE_INIT, 0); + + /* + * The DEVICE_CREATE event puts the USB device in the state + * STATE_ATTACHED. + */ + usbd_device_event_irq(device, DEVICE_CREATE, 0); + + /* + * Some USB controller driver implementations signal + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. + * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, + * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. + * The DW USB client controller has the capability to detect when the + * USB cable is connected to a powered USB bus, so we will defer the + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events until later. + */ + + udc_enable(device); +} + +/* + * Plug detection interrupt handling + */ +static void dw_udc_plug_irq(void) +{ + if (readl(&plug_regs_p->plug_state) & PLUG_STATUS_ATTACHED) { + /* + * USB cable attached + * Turn off PHY reset bit (PLUG detect). + * Switch PHY opmode to normal operation (PLUG detect). + */ + udc_connect(); + writel(DEV_INT_SOF, &udc_regs_p->dev_int_mask); + + UDCDBG("device attached and powered"); + udc_state_transition(udc_device->device_state, STATE_POWERED); + } else { + writel(~0x0, &udc_regs_p->dev_int_mask); + + UDCDBG("device detached or unpowered"); + udc_state_transition(udc_device->device_state, STATE_ATTACHED); + } +} + +/* + * Device interrupt handling + */ +static void dw_udc_dev_irq(void) +{ + if (readl(&udc_regs_p->dev_int) & DEV_INT_USBRESET) { + writel(~0x0, &udc_regs_p->endp_int_mask); + + writel(readl(&inep_regs_p[0].endp_cntl) | ENDP_CNTL_FLUSH, + &inep_regs_p[0].endp_cntl); + + writel(DEV_INT_USBRESET, &udc_regs_p->dev_int); + + /* + * This endpoint0 specific register can be programmed only + * after the phy clock is initialized + */ + writel((EP0_MAX_PACKET_SIZE << 19) | ENDP_EPTYPE_CNTL, + &udc_regs_p->udc_endp_reg[0]); + + UDCDBG("device reset in progess"); + udc_state_transition(udc_device->device_state, STATE_DEFAULT); + } + + /* Device Enumeration completed */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_ENUM) { + writel(DEV_INT_ENUM, &udc_regs_p->dev_int); + + /* Endpoint interrupt enabled for Ctrl IN & Ctrl OUT */ + writel(readl(&udc_regs_p->endp_int_mask) & ~0x10001, + &udc_regs_p->endp_int_mask); + + UDCDBG("default -> addressed"); + udc_state_transition(udc_device->device_state, STATE_ADDRESSED); + } + + /* The USB will be in SUSPEND in 3 ms */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_INACTIVE) { + writel(DEV_INT_INACTIVE, &udc_regs_p->dev_int); + + UDCDBG("entering inactive state"); + /* usbd_device_event_irq(udc_device, DEVICE_BUS_INACTIVE, 0); */ + } + + /* SetConfiguration command received */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_SETCFG) { + writel(DEV_INT_SETCFG, &udc_regs_p->dev_int); + + UDCDBG("entering configured state"); + udc_state_transition(udc_device->device_state, + STATE_CONFIGURED); + } + + /* SetInterface command received */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_SETINTF) + writel(DEV_INT_SETINTF, &udc_regs_p->dev_int); + + /* USB Suspend detected on cable */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_SUSPUSB) { + writel(DEV_INT_SUSPUSB, &udc_regs_p->dev_int); + + UDCDBG("entering suspended state"); + usbd_device_event_irq(udc_device, DEVICE_BUS_INACTIVE, 0); + } + + /* USB Start-Of-Frame detected on cable */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_SOF) + writel(DEV_INT_SOF, &udc_regs_p->dev_int); +} + +/* + * Endpoint interrupt handling + */ +static void dw_udc_endpoint_irq(void) +{ + while (readl(&udc_regs_p->endp_int) & ENDP0_INT_CTRLOUT) { + + writel(ENDP0_INT_CTRLOUT, &udc_regs_p->endp_int); + + if ((readl(&outep_regs_p[0].endp_status) & ENDP_STATUS_OUTMSK) + == ENDP_STATUS_OUT_SETUP) { + dw_udc_setup(udc_device->bus->endpoint_array + 0); + writel(ENDP_STATUS_OUT_SETUP, + &outep_regs_p[0].endp_status); + + } else if ((readl(&outep_regs_p[0].endp_status) & + ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_DATA) { + dw_udc_ep0_rx(udc_device->bus->endpoint_array + 0); + writel(ENDP_STATUS_OUT_DATA, + &outep_regs_p[0].endp_status); + + } else if ((readl(&outep_regs_p[0].endp_status) & + ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_NONE) { + /* NONE received */ + } + + writel(0x0, &outep_regs_p[0].endp_status); + } + + if (readl(&udc_regs_p->endp_int) & ENDP0_INT_CTRLIN) { + dw_udc_ep0_tx(udc_device->bus->endpoint_array + 0); + + writel(ENDP_STATUS_IN, &inep_regs_p[0].endp_status); + writel(ENDP0_INT_CTRLIN, &udc_regs_p->endp_int); + } + + if (readl(&udc_regs_p->endp_int) & ENDP_INT_NONISOOUT_MSK) { + u32 epnum = 0; + u32 ep_int = readl(&udc_regs_p->endp_int) & + ENDP_INT_NONISOOUT_MSK; + + ep_int >>= 16; + while (0x0 == (ep_int & 0x1)) { + ep_int >>= 1; + epnum++; + } + + writel((1 << 16) << epnum, &udc_regs_p->endp_int); + + if ((readl(&outep_regs_p[epnum].endp_status) & + ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_DATA) { + + dw_udc_epn_rx(epnum); + writel(ENDP_STATUS_OUT_DATA, + &outep_regs_p[epnum].endp_status); + } else if ((readl(&outep_regs_p[epnum].endp_status) & + ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_NONE) { + writel(0x0, &outep_regs_p[epnum].endp_status); + } + } + + if (readl(&udc_regs_p->endp_int) & ENDP_INT_NONISOIN_MSK) { + u32 epnum = 0; + u32 ep_int = readl(&udc_regs_p->endp_int) & + ENDP_INT_NONISOIN_MSK; + + while (0x0 == (ep_int & 0x1)) { + ep_int >>= 1; + epnum++; + } + + if (readl(&inep_regs_p[epnum].endp_status) & ENDP_STATUS_IN) { + writel(ENDP_STATUS_IN, + &outep_regs_p[epnum].endp_status); + dw_udc_epn_tx(epnum); + + writel(ENDP_STATUS_IN, + &outep_regs_p[epnum].endp_status); + } + + writel((1 << epnum), &udc_regs_p->endp_int); + } +} + +/* + * UDC interrupts + */ +void udc_irq(void) +{ + /* + * Loop while we have interrupts. + * If we don't do this, the input chain + * polling delay is likely to miss + * host requests. + */ + while (readl(&plug_regs_p->plug_pending)) + dw_udc_plug_irq(); + + while (readl(&udc_regs_p->dev_int)) + dw_udc_dev_irq(); + + if (readl(&udc_regs_p->endp_int)) + dw_udc_endpoint_irq(); +} + +/* Flow control */ +void udc_set_nak(int epid) +{ + writel(readl(&inep_regs_p[epid].endp_cntl) | ENDP_CNTL_SNAK, + &inep_regs_p[epid].endp_cntl); + + writel(readl(&outep_regs_p[epid].endp_cntl) | ENDP_CNTL_SNAK, + &outep_regs_p[epid].endp_cntl); +} + +void udc_unset_nak(int epid) +{ + u32 val; + + val = readl(&inep_regs_p[epid].endp_cntl); + val &= ~ENDP_CNTL_SNAK; + val |= ENDP_CNTL_CNAK; + writel(val, &inep_regs_p[epid].endp_cntl); + + val = readl(&outep_regs_p[epid].endp_cntl); + val &= ~ENDP_CNTL_SNAK; + val |= ENDP_CNTL_CNAK; + writel(val, &outep_regs_p[epid].endp_cntl); +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/ep0.c b/qemu/roms/u-boot/drivers/usb/gadget/ep0.c new file mode 100644 index 000000000..b3214882f --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/ep0.c @@ -0,0 +1,597 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * (C) Copyright 2006 + * Bryan O'Donoghue, deckard@CodeHermit.ie + * + * Based on + * linux/drivers/usbd/ep0.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This is the builtin ep0 control function. It implements all required functionality + * for responding to control requests (SETUP packets). + * + * XXX + * + * Currently we do not pass any SETUP packets (or other) to the configured + * function driver. This may need to change. + * + * XXX + * + * As alluded to above, a simple callback cdc_recv_setup has been implemented + * in the usb_device data structure to facilicate passing + * Common Device Class packets to a function driver. + * + * XXX + */ + +#include +#include + +#if 0 +#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args) +#else +#define dbg_ep0(lvl,fmt,args...) +#endif + +/* EP0 Configuration Set ********************************************************************* */ + + +/** + * ep0_get_status - fill in URB data with appropriate status + * @device: + * @urb: + * @index: + * @requesttype: + * + */ +static int ep0_get_status (struct usb_device_instance *device, + struct urb *urb, int index, int requesttype) +{ + char *cp; + + urb->actual_length = 2; + cp = (char*)urb->buffer; + cp[0] = cp[1] = 0; + + switch (requesttype) { + case USB_REQ_RECIPIENT_DEVICE: + cp[0] = USB_STATUS_SELFPOWERED; + break; + case USB_REQ_RECIPIENT_INTERFACE: + break; + case USB_REQ_RECIPIENT_ENDPOINT: + cp[0] = usbd_endpoint_halted (device, index); + break; + case USB_REQ_RECIPIENT_OTHER: + urb->actual_length = 0; + default: + break; + } + dbg_ep0 (2, "%02x %02x", cp[0], cp[1]); + return 0; +} + +/** + * ep0_get_one + * @device: + * @urb: + * @result: + * + * Set a single byte value in the urb send buffer. Return non-zero to signal + * a request error. + */ +static int ep0_get_one (struct usb_device_instance *device, struct urb *urb, + __u8 result) +{ + urb->actual_length = 1; /* XXX 2? */ + ((char *) urb->buffer)[0] = result; + return 0; +} + +/** + * copy_config + * @urb: pointer to urb + * @data: pointer to configuration data + * @length: length of data + * + * Copy configuration data to urb transfer buffer if there is room for it. + */ +void copy_config (struct urb *urb, void *data, int max_length, + int max_buf) +{ + int available; + int length; + + /*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */ + /* urb->actual_length, urb->buffer_length, max_buf, max_length, data); */ + + if (!data) { + dbg_ep0 (1, "data is NULL"); + return; + } + length = max_length; + + if (length > max_length) { + dbg_ep0 (1, "length: %d >= max_length: %d", length, + max_length); + return; + } + /*dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", */ + /* urb->actual_length, urb->buffer_length, max_buf, max_length, length); */ + + if ((available = + /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) { + return; + } + /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ + /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ + + if (length > available) { + length = available; + } + /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ + /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ + + memcpy (urb->buffer + urb->actual_length, data, length); + urb->actual_length += length; + + dbg_ep0 (3, + "copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d", + urb->actual_length, urb->buffer_length, max_buf, max_length, + available); +} + +/** + * ep0_get_descriptor + * @device: + * @urb: + * @max: + * @descriptor_type: + * @index: + * + * Called by ep0_rx_process for a get descriptor device command. Determine what + * descriptor is being requested, copy to send buffer. Return zero if ok to send, + * return non-zero to signal a request error. + */ +static int ep0_get_descriptor (struct usb_device_instance *device, + struct urb *urb, int max, int descriptor_type, + int index) +{ + int port = 0; /* XXX compound device */ + + /*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */ + + if (!urb || !urb->buffer || !urb->buffer_length + || (urb->buffer_length < 255)) { + dbg_ep0 (2, "invalid urb %p", urb); + return -1L; + } + + /* setup tx urb */ + urb->actual_length = 0; + + dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type)); + + switch (descriptor_type) { + case USB_DESCRIPTOR_TYPE_DEVICE: + { + struct usb_device_descriptor *device_descriptor; + if (! + (device_descriptor = + usbd_device_device_descriptor (device, port))) { + return -1; + } + /* copy descriptor for this device */ + copy_config (urb, device_descriptor, + sizeof (struct usb_device_descriptor), + max); + + /* correct the correct control endpoint 0 max packet size into the descriptor */ + device_descriptor = + (struct usb_device_descriptor *) urb->buffer; + + } + dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length); + break; + + case USB_DESCRIPTOR_TYPE_CONFIGURATION: + { + struct usb_configuration_descriptor + *configuration_descriptor; + struct usb_device_descriptor *device_descriptor; + if (! + (device_descriptor = + usbd_device_device_descriptor (device, port))) { + return -1; + } + /*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */ + if (index >= device_descriptor->bNumConfigurations) { + dbg_ep0 (0, "index too large: %d >= %d", index, + device_descriptor-> + bNumConfigurations); + return -1; + } + + if (! + (configuration_descriptor = + usbd_device_configuration_descriptor (device, + port, + index))) { + dbg_ep0 (0, + "usbd_device_configuration_descriptor failed: %d", + index); + return -1; + } + dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength)); + copy_config (urb, configuration_descriptor, + + cpu_to_le16(configuration_descriptor->wTotalLength), + max); + } + + break; + + case USB_DESCRIPTOR_TYPE_STRING: + { + struct usb_string_descriptor *string_descriptor; + if (!(string_descriptor = usbd_get_string (index))) { + serial_printf("Invalid string index %d\n", index); + return -1; + } + dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength); + copy_config (urb, string_descriptor, string_descriptor->bLength, max); + } + break; + case USB_DESCRIPTOR_TYPE_INTERFACE: + serial_printf("USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n"); + return -1; + case USB_DESCRIPTOR_TYPE_ENDPOINT: + serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n"); + return -1; + case USB_DESCRIPTOR_TYPE_HID: + { + serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n"); + return -1; /* unsupported at this time */ +#if 0 + int bNumInterface = + le16_to_cpu (urb->device_request.wIndex); + int bAlternateSetting = 0; + int class = 0; + struct usb_class_descriptor *class_descriptor; + + if (!(class_descriptor = + usbd_device_class_descriptor_index (device, + port, 0, + bNumInterface, + bAlternateSetting, + class)) + || class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) { + dbg_ep0 (3, "[%d] interface is not HID", + bNumInterface); + return -1; + } + /* copy descriptor for this class */ + copy_config (urb, class_descriptor, + class_descriptor->descriptor.hid.bLength, + max); +#endif + } + break; + case USB_DESCRIPTOR_TYPE_REPORT: + { + serial_printf("USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n"); + return -1; /* unsupported at this time */ +#if 0 + int bNumInterface = + le16_to_cpu (urb->device_request.wIndex); + int bAlternateSetting = 0; + int class = 0; + struct usb_class_report_descriptor *report_descriptor; + + if (!(report_descriptor = + usbd_device_class_report_descriptor_index + (device, port, 0, bNumInterface, + bAlternateSetting, class)) + || report_descriptor->bDescriptorType != + USB_DT_REPORT) { + dbg_ep0 (3, "[%d] descriptor is not REPORT", + bNumInterface); + return -1; + } + /* copy report descriptor for this class */ + /*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */ + if (max - urb->actual_length > 0) { + int length = + MIN (report_descriptor->wLength, + max - urb->actual_length); + memcpy (urb->buffer + urb->actual_length, + &report_descriptor->bData[0], length); + urb->actual_length += length; + } +#endif + } + break; + case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER: +#if defined(CONFIG_USBD_HS) + { + struct usb_qualifier_descriptor *qualifier_descriptor = + device->qualifier_descriptor; + + if (!qualifier_descriptor) + return -1; + + /* copy descriptor for this device */ + copy_config(urb, qualifier_descriptor, + sizeof(struct usb_qualifier_descriptor), + max); + + } + dbg_ep0(3, "copied qualifier descriptor, actual_length: 0x%x", + urb->actual_length); +#else + return -1; +#endif + break; + + default: + return -1; + } + + + dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d", + urb->buffer, urb->buffer_length, urb->actual_length, + device->bus->endpoint_array[0].tx_packetSize); +/* + if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) { + dbg_ep0(0, "adding null byte"); + urb->buffer[urb->actual_length++] = 0; + dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d", + urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize); + } +*/ + return 0; + +} + +/** + * ep0_recv_setup - called to indicate URB has been received + * @urb: pointer to struct urb + * + * Check if this is a setup packet, process the device request, put results + * back into the urb and return zero or non-zero to indicate success (DATA) + * or failure (STALL). + * + */ +int ep0_recv_setup (struct urb *urb) +{ + /*struct usb_device_request *request = urb->buffer; */ + /*struct usb_device_instance *device = urb->device; */ + + struct usb_device_request *request; + struct usb_device_instance *device; + int address; + + dbg_ep0 (0, "entering ep0_recv_setup()"); + if (!urb || !urb->device) { + dbg_ep0 (3, "invalid URB %p", urb); + return -1; + } + + request = &urb->device_request; + device = urb->device; + + dbg_ep0 (3, "urb: %p device: %p", urb, urb->device); + + + /*dbg_ep0(2, "- - - - - - - - - -"); */ + + dbg_ep0 (2, + "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s", + request->bmRequestType, request->bRequest, + le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex), + le16_to_cpu (request->wLength), + USBD_DEVICE_REQUESTS (request->bRequest)); + + /* handle USB Standard Request (c.f. USB Spec table 9-2) */ + if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) { + if(device->device_state <= STATE_CONFIGURED){ + /* Attempt to handle a CDC specific request if we are + * in the configured state. + */ + return device->cdc_recv_setup(request,urb); + } + dbg_ep0 (1, "non standard request: %x", + request->bmRequestType & USB_REQ_TYPE_MASK); + return -1; /* Stall here */ + } + + switch (device->device_state) { + case STATE_CREATED: + case STATE_ATTACHED: + case STATE_POWERED: + /* It actually is important to allow requests in these states, + * Windows will request descriptors before assigning an + * address to the client. + */ + + /*dbg_ep0 (1, "request %s not allowed in this state: %s", */ + /* USBD_DEVICE_REQUESTS(request->bRequest), */ + /* usbd_device_states[device->device_state]); */ + /*return -1; */ + break; + + case STATE_INIT: + case STATE_DEFAULT: + switch (request->bRequest) { + case USB_REQ_GET_STATUS: + case USB_REQ_GET_INTERFACE: + case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_DESCRIPTOR: + /* case USB_REQ_SET_CONFIGURATION: */ + case USB_REQ_SET_INTERFACE: + dbg_ep0 (1, + "request %s not allowed in DEFAULT state: %s", + USBD_DEVICE_REQUESTS (request->bRequest), + usbd_device_states[device->device_state]); + return -1; + + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_ADDRESS: + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + break; + } + case STATE_ADDRESSED: + case STATE_CONFIGURED: + break; + case STATE_UNKNOWN: + dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s", + USBD_DEVICE_REQUESTS (request->bRequest), + usbd_device_states[device->device_state]); + return -1; + } + + /* handle all requests that return data (direction bit set on bm RequestType) */ + if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) { + + dbg_ep0 (3, "Device-to-Host"); + + switch (request->bRequest) { + + case USB_REQ_GET_STATUS: + return ep0_get_status (device, urb, request->wIndex, + request->bmRequestType & + USB_REQ_RECIPIENT_MASK); + + case USB_REQ_GET_DESCRIPTOR: + return ep0_get_descriptor (device, urb, + le16_to_cpu (request->wLength), + le16_to_cpu (request->wValue) >> 8, + le16_to_cpu (request->wValue) & 0xff); + + case USB_REQ_GET_CONFIGURATION: + serial_printf("get config %d\n", device->configuration); + return ep0_get_one (device, urb, + device->configuration); + + case USB_REQ_GET_INTERFACE: + return ep0_get_one (device, urb, device->alternate); + + case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ + return -1; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_DESCRIPTOR: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_INTERFACE: + return -1; + } + } + /* handle the requests that do not return data */ + else { + + + /*dbg_ep0(3, "Host-to-Device"); */ + switch (request->bRequest) { + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + dbg_ep0 (0, "Host-to-Device"); + switch (request-> + bmRequestType & USB_REQ_RECIPIENT_MASK) { + case USB_REQ_RECIPIENT_DEVICE: + /* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */ + /* XXX fall through for now as we do not support either */ + case USB_REQ_RECIPIENT_INTERFACE: + case USB_REQ_RECIPIENT_OTHER: + dbg_ep0 (0, "request %s not", + USBD_DEVICE_REQUESTS (request->bRequest)); + default: + return -1; + + case USB_REQ_RECIPIENT_ENDPOINT: + dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue)); + if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) { + /*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */ + /* request->bRequest == USB_REQ_SET_FEATURE); */ + /* NEED TO IMPLEMENT THIS!!! */ + return -1; + } else { + dbg_ep0 (1, "request %s bad wValue: %04x", + USBD_DEVICE_REQUESTS + (request->bRequest), + le16_to_cpu (request->wValue)); + return -1; + } + } + + case USB_REQ_SET_ADDRESS: + /* check if this is a re-address, reset first if it is (this shouldn't be possible) */ + if (device->device_state != STATE_DEFAULT) { + dbg_ep0 (1, "set_address: %02x state: %s", + le16_to_cpu (request->wValue), + usbd_device_states[device->device_state]); + return -1; + } + address = le16_to_cpu (request->wValue); + if ((address & 0x7f) != address) { + dbg_ep0 (1, "invalid address %04x %04x", + address, address & 0x7f); + return -1; + } + device->address = address; + + /*dbg_ep0(2, "address: %d %d %d", */ + /* request->wValue, le16_to_cpu(request->wValue), device->address); */ + + return 0; + + case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */ + dbg_ep0 (0, "set descriptor: NOT SUPPORTED"); + return -1; + + case USB_REQ_SET_CONFIGURATION: + /* c.f. 9.4.7 - the top half of wValue is reserved */ + device->configuration = le16_to_cpu(request->wValue) & 0xff; + + /* reset interface and alternate settings */ + device->interface = device->alternate = 0; + + /*dbg_ep0(2, "set configuration: %d", device->configuration); */ + /*serial_printf("DEVICE_CONFIGURED.. event?\n"); */ + return 0; + + case USB_REQ_SET_INTERFACE: + device->interface = le16_to_cpu (request->wIndex); + device->alternate = le16_to_cpu (request->wValue); + /*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */ + serial_printf ("DEVICE_SET_INTERFACE.. event?\n"); + return 0; + + case USB_REQ_GET_STATUS: + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_GET_INTERFACE: + case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ + return -1; + } + } + return -1; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/ep0.h b/qemu/roms/u-boot/drivers/usb/gadget/ep0.h new file mode 100644 index 000000000..6042e7562 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/ep0.h @@ -0,0 +1,26 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * Based on + * linux/drivers/usbd/ep0.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __USBDCORE_EP0_H__ +#define __USBDCORE_EP0_H__ + + +int ep0_recv_setup (struct urb *urb); + + +#endif diff --git a/qemu/roms/u-boot/drivers/usb/gadget/epautoconf.c b/qemu/roms/u-boot/drivers/usb/gadget/epautoconf.c new file mode 100644 index 000000000..0df4b2a10 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/epautoconf.c @@ -0,0 +1,296 @@ +/* + * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers + * + * Copyright (C) 2004 David Brownell + * + * SPDX-License-Identifier: GPL-2.0+ + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Ported to U-boot by: Thomas Smits and + * Remy Bohmer + */ + +#include +#include +#include +#include +#include +#include "gadget_chips.h" + +#define isdigit(c) ('0' <= (c) && (c) <= '9') + +/* we must assign addresses for configurable endpoints (like net2280) */ +static unsigned epnum; + +/* #define MANY_ENDPOINTS */ +#ifdef MANY_ENDPOINTS +/* more than 15 configurable endpoints */ +static unsigned in_epnum; +#endif + + +/* + * This should work with endpoints from controller drivers sharing the + * same endpoint naming convention. By example: + * + * - ep1, ep2, ... address is fixed, not direction or type + * - ep1in, ep2out, ... address and direction are fixed, not type + * - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction + * - ep1in-bulk, ep2out-iso, ... all three are fixed + * - ep-* ... no functionality restrictions + * + * Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal. + * Less common restrictions are implied by gadget_is_*(). + * + * NOTE: each endpoint is unidirectional, as specified by its USB + * descriptor; and isn't specific to a configuration or altsetting. + */ +static int ep_matches( + struct usb_gadget *gadget, + struct usb_ep *ep, + struct usb_endpoint_descriptor *desc +) +{ + u8 type; + const char *tmp; + u16 max; + + /* endpoint already claimed? */ + if (NULL != ep->driver_data) + return 0; + + /* only support ep0 for portable CONTROL traffic */ + type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (USB_ENDPOINT_XFER_CONTROL == type) + return 0; + + /* some other naming convention */ + if ('e' != ep->name[0]) + return 0; + + /* type-restriction: "-iso", "-bulk", or "-int". + * direction-restriction: "in", "out". + */ + if ('-' != ep->name[2]) { + tmp = strrchr(ep->name, '-'); + if (tmp) { + switch (type) { + case USB_ENDPOINT_XFER_INT: + /* bulk endpoints handle interrupt transfers, + * except the toggle-quirky iso-synch kind + */ + if ('s' == tmp[2]) /* == "-iso" */ + return 0; + /* for now, avoid PXA "interrupt-in"; + * it's documented as never using DATA1. + */ + if (gadget_is_pxa(gadget) + && 'i' == tmp[1]) + return 0; + break; + case USB_ENDPOINT_XFER_BULK: + if ('b' != tmp[1]) /* != "-bulk" */ + return 0; + break; + case USB_ENDPOINT_XFER_ISOC: + if ('s' != tmp[2]) /* != "-iso" */ + return 0; + } + } else { + tmp = ep->name + strlen(ep->name); + } + + /* direction-restriction: "..in-..", "out-.." */ + tmp--; + if (!isdigit(*tmp)) { + if (desc->bEndpointAddress & USB_DIR_IN) { + if ('n' != *tmp) + return 0; + } else { + if ('t' != *tmp) + return 0; + } + } + } + + /* endpoint maxpacket size is an input parameter, except for bulk + * where it's an output parameter representing the full speed limit. + * the usb spec fixes high speed bulk maxpacket at 512 bytes. + */ + max = 0x7ff & le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); + switch (type) { + case USB_ENDPOINT_XFER_INT: + /* INT: limit 64 bytes full speed, 1024 high speed */ + if (!gadget->is_dualspeed && max > 64) + return 0; + /* FALLTHROUGH */ + + case USB_ENDPOINT_XFER_ISOC: + /* ISO: limit 1023 bytes full speed, 1024 high speed */ + if (ep->maxpacket < max) + return 0; + if (!gadget->is_dualspeed && max > 1023) + return 0; + + /* BOTH: "high bandwidth" works only at high speed */ + if ((get_unaligned(&desc->wMaxPacketSize) & + __constant_cpu_to_le16(3<<11))) { + if (!gadget->is_dualspeed) + return 0; + /* configure your hardware with enough buffering!! */ + } + break; + } + + /* MATCH!! */ + + /* report address */ + if (isdigit(ep->name[2])) { + u8 num = simple_strtoul(&ep->name[2], NULL, 10); + desc->bEndpointAddress |= num; +#ifdef MANY_ENDPOINTS + } else if (desc->bEndpointAddress & USB_DIR_IN) { + if (++in_epnum > 15) + return 0; + desc->bEndpointAddress = USB_DIR_IN | in_epnum; +#endif + } else { + if (++epnum > 15) + return 0; + desc->bEndpointAddress |= epnum; + } + + /* report (variable) full speed bulk maxpacket */ + if (USB_ENDPOINT_XFER_BULK == type) { + int size = ep->maxpacket; + + /* min() doesn't work on bitfields with gcc-3.5 */ + if (size > 64) + size = 64; + put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize); + } + return 1; +} + +static struct usb_ep * +find_ep(struct usb_gadget *gadget, const char *name) +{ + struct usb_ep *ep; + + list_for_each_entry(ep, &gadget->ep_list, ep_list) { + if (0 == strcmp(ep->name, name)) + return ep; + } + return NULL; +} + +/** + * usb_ep_autoconfig - choose an endpoint matching the descriptor + * @gadget: The device to which the endpoint must belong. + * @desc: Endpoint descriptor, with endpoint direction and transfer mode + * initialized. For periodic transfers, the maximum packet + * size must also be initialized. This is modified on success. + * + * By choosing an endpoint to use with the specified descriptor, this + * routine simplifies writing gadget drivers that work with multiple + * USB device controllers. The endpoint would be passed later to + * usb_ep_enable(), along with some descriptor. + * + * That second descriptor won't always be the same as the first one. + * For example, isochronous endpoints can be autoconfigured for high + * bandwidth, and then used in several lower bandwidth altsettings. + * Also, high and full speed descriptors will be different. + * + * Be sure to examine and test the results of autoconfiguration on your + * hardware. This code may not make the best choices about how to use the + * USB controller, and it can't know all the restrictions that may apply. + * Some combinations of driver and hardware won't be able to autoconfigure. + * + * On success, this returns an un-claimed usb_ep, and modifies the endpoint + * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value + * is initialized as if the endpoint were used at full speed. To prevent + * the endpoint from being returned by a later autoconfig call, claim it + * by assigning ep->driver_data to some non-null value. + * + * On failure, this returns a null endpoint descriptor. + */ +struct usb_ep *usb_ep_autoconfig( + struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc +) +{ + struct usb_ep *ep; + u8 type; + + type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + /* First, apply chip-specific "best usage" knowledge. + * This might make a good usb_gadget_ops hook ... + */ + if (gadget_is_net2280(gadget) && type == USB_ENDPOINT_XFER_INT) { + /* ep-e, ep-f are PIO with only 64 byte fifos */ + ep = find_ep(gadget, "ep-e"); + if (ep && ep_matches(gadget, ep, desc)) + return ep; + ep = find_ep(gadget, "ep-f"); + if (ep && ep_matches(gadget, ep, desc)) + return ep; + + } else if (gadget_is_goku(gadget)) { + if (USB_ENDPOINT_XFER_INT == type) { + /* single buffering is enough */ + ep = find_ep(gadget, "ep3-bulk"); + if (ep && ep_matches(gadget, ep, desc)) + return ep; + } else if (USB_ENDPOINT_XFER_BULK == type + && (USB_DIR_IN & desc->bEndpointAddress)) { + /* DMA may be available */ + ep = find_ep(gadget, "ep2-bulk"); + if (ep && ep_matches(gadget, ep, desc)) + return ep; + } + + } else if (gadget_is_sh(gadget) && USB_ENDPOINT_XFER_INT == type) { + /* single buffering is enough; maybe 8 byte fifo is too */ + ep = find_ep(gadget, "ep3in-bulk"); + if (ep && ep_matches(gadget, ep, desc)) + return ep; + + } else if (gadget_is_mq11xx(gadget) && USB_ENDPOINT_XFER_INT == type) { + ep = find_ep(gadget, "ep1-bulk"); + if (ep && ep_matches(gadget, ep, desc)) + return ep; + } + + /* Second, look at endpoints until an unclaimed one looks usable */ + list_for_each_entry(ep, &gadget->ep_list, ep_list) { + if (ep_matches(gadget, ep, desc)) + return ep; + } + + /* Fail */ + return NULL; +} + +/** + * usb_ep_autoconfig_reset - reset endpoint autoconfig state + * @gadget: device for which autoconfig state will be reset + * + * Use this for devices where one configuration may need to assign + * endpoint resources very differently from the next one. It clears + * state such as ep->driver_data and the record of assigned endpoints + * used by usb_ep_autoconfig(). + */ +void usb_ep_autoconfig_reset(struct usb_gadget *gadget) +{ + struct usb_ep *ep; + + list_for_each_entry(ep, &gadget->ep_list, ep_list) { + ep->driver_data = NULL; + } +#ifdef MANY_ENDPOINTS + in_epnum = 0; +#endif + epnum = 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/ether.c b/qemu/roms/u-boot/drivers/usb/gadget/ether.c new file mode 100644 index 000000000..cc6cc1f32 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/ether.c @@ -0,0 +1,2555 @@ +/* + * ether.c -- Ethernet gadget driver, with CDC and non-CDC options + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gadget_chips.h" +#include "rndis.h" + +#define USB_NET_NAME "usb_ether" + +#define atomic_read +extern struct platform_data brd; +#define spin_lock(x) +#define spin_unlock(x) + + +unsigned packet_received, packet_sent; + +#define GFP_ATOMIC ((gfp_t) 0) +#define GFP_KERNEL ((gfp_t) 0) + +/* + * Ethernet gadget driver -- with CDC and non-CDC options + * Builds on hardware support for a full duplex link. + * + * CDC Ethernet is the standard USB solution for sending Ethernet frames + * using USB. Real hardware tends to use the same framing protocol but look + * different for control features. This driver strongly prefers to use + * this USB-IF standard as its open-systems interoperability solution; + * most host side USB stacks (except from Microsoft) support it. + * + * This is sometimes called "CDC ECM" (Ethernet Control Model) to support + * TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new + * "CDC EEM" (Ethernet Emulation Model) is starting to spread. + * + * There's some hardware that can't talk CDC ECM. We make that hardware + * implement a "minimalist" vendor-agnostic CDC core: same framing, but + * link-level setup only requires activating the configuration. Only the + * endpoint descriptors, and product/vendor IDs, are relevant; no control + * operations are available. Linux supports it, but other host operating + * systems may not. (This is a subset of CDC Ethernet.) + * + * It turns out that if you add a few descriptors to that "CDC Subset", + * (Windows) host side drivers from MCCI can treat it as one submode of + * a proprietary scheme called "SAFE" ... without needing to know about + * specific product/vendor IDs. So we do that, making it easier to use + * those MS-Windows drivers. Those added descriptors make it resemble a + * CDC MDLM device, but they don't change device behavior at all. (See + * MCCI Engineering report 950198 "SAFE Networking Functions".) + * + * A third option is also in use. Rather than CDC Ethernet, or something + * simpler, Microsoft pushes their own approach: RNDIS. The published + * RNDIS specs are ambiguous and appear to be incomplete, and are also + * needlessly complex. They borrow more from CDC ACM than CDC ECM. + */ +#define ETH_ALEN 6 /* Octets in one ethernet addr */ +#define ETH_HLEN 14 /* Total octets in header. */ +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ +#define ETH_DATA_LEN 1500 /* Max. octets in payload */ +#define ETH_FRAME_LEN PKTSIZE_ALIGN /* Max. octets in frame sans FCS */ +#define ETH_FCS_LEN 4 /* Octets in the FCS */ + +#define DRIVER_DESC "Ethernet Gadget" +/* Based on linux 2.6.27 version */ +#define DRIVER_VERSION "May Day 2005" + +static const char shortname[] = "ether"; +static const char driver_desc[] = DRIVER_DESC; + +#define RX_EXTRA 20 /* guard against rx overflows */ + +#ifndef CONFIG_USB_ETH_RNDIS +#define rndis_uninit(x) do {} while (0) +#define rndis_deregister(c) do {} while (0) +#define rndis_exit() do {} while (0) +#endif + +/* CDC and RNDIS support the same host-chosen outgoing packet filters. */ +#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ + |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ + |USB_CDC_PACKET_TYPE_PROMISCUOUS \ + |USB_CDC_PACKET_TYPE_DIRECTED) + +#define USB_CONNECT_TIMEOUT (3 * CONFIG_SYS_HZ) + +/*-------------------------------------------------------------------------*/ + +struct eth_dev { + struct usb_gadget *gadget; + struct usb_request *req; /* for control responses */ + struct usb_request *stat_req; /* for cdc & rndis status */ + + u8 config; + struct usb_ep *in_ep, *out_ep, *status_ep; + const struct usb_endpoint_descriptor + *in, *out, *status; + + struct usb_request *tx_req, *rx_req; + + struct eth_device *net; + struct net_device_stats stats; + unsigned int tx_qlen; + + unsigned zlp:1; + unsigned cdc:1; + unsigned rndis:1; + unsigned suspended:1; + unsigned network_started:1; + u16 cdc_filter; + unsigned long todo; + int mtu; +#define WORK_RX_MEMORY 0 + int rndis_config; + u8 host_mac[ETH_ALEN]; +}; + +/* + * This version autoconfigures as much as possible at run-time. + * + * It also ASSUMES a self-powered device, without remote wakeup, + * although remote wakeup support would make sense. + */ + +/*-------------------------------------------------------------------------*/ +static struct eth_dev l_ethdev; +static struct eth_device l_netdev; +static struct usb_gadget_driver eth_driver; + +/*-------------------------------------------------------------------------*/ + +/* "main" config is either CDC, or its simple subset */ +static inline int is_cdc(struct eth_dev *dev) +{ +#if !defined(CONFIG_USB_ETH_SUBSET) + return 1; /* only cdc possible */ +#elif !defined(CONFIG_USB_ETH_CDC) + return 0; /* only subset possible */ +#else + return dev->cdc; /* depends on what hardware we found */ +#endif +} + +/* "secondary" RNDIS config may sometimes be activated */ +static inline int rndis_active(struct eth_dev *dev) +{ +#ifdef CONFIG_USB_ETH_RNDIS + return dev->rndis; +#else + return 0; +#endif +} + +#define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev)) +#define cdc_active(dev) (is_cdc(dev) && !rndis_active(dev)) + +#define DEFAULT_QLEN 2 /* double buffering by default */ + +/* peak bulk transfer bits-per-second */ +#define HS_BPS (13 * 512 * 8 * 1000 * 8) +#define FS_BPS (19 * 64 * 1 * 1000 * 8) + +#ifdef CONFIG_USB_GADGET_DUALSPEED +#define DEVSPEED USB_SPEED_HIGH + +#ifdef CONFIG_USB_ETH_QMULT +#define qmult CONFIG_USB_ETH_QMULT +#else +#define qmult 5 +#endif + +/* for dual-speed hardware, use deeper queues at highspeed */ +#define qlen(gadget) \ + (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1)) + +static inline int BITRATE(struct usb_gadget *g) +{ + return (g->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS; +} + +#else /* full speed (low speed doesn't do bulk) */ + +#define qmult 1 + +#define DEVSPEED USB_SPEED_FULL + +#define qlen(gadget) DEFAULT_QLEN + +static inline int BITRATE(struct usb_gadget *g) +{ + return FS_BPS; +} +#endif + +/*-------------------------------------------------------------------------*/ + +/* + * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ + +/* + * Thanks to NetChip Technologies for donating this product ID. + * It's for devices with only CDC Ethernet configurations. + */ +#define CDC_VENDOR_NUM 0x0525 /* NetChip */ +#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ + +/* + * For hardware that can't talk CDC, we use the same vendor ID that + * ARM Linux has used for ethernet-over-usb, both with sa1100 and + * with pxa250. We're protocol-compatible, if the host-side drivers + * use the endpoint descriptors. bcdDevice (version) is nonzero, so + * drivers that need to hard-wire endpoint numbers have a hook. + * + * The protocol is a minimal subset of CDC Ether, which works on any bulk + * hardware that's not deeply broken ... even on hardware that can't talk + * RNDIS (like SA-1100, with no interrupt endpoint, or anything that + * doesn't handle control-OUT). + */ +#define SIMPLE_VENDOR_NUM 0x049f /* Compaq Computer Corp. */ +#define SIMPLE_PRODUCT_NUM 0x505a /* Linux-USB "CDC Subset" Device */ + +/* + * For hardware that can talk RNDIS and either of the above protocols, + * use this ID ... the windows INF files will know it. Unless it's + * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose + * the non-RNDIS configuration. + */ +#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ +#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ + +/* + * Some systems will want different product identifers published in the + * device descriptor, either numbers or strings or both. These string + * parameters are in UTF-8 (superset of ASCII's 7 bit characters). + */ + +/* + * Emulating them in eth_bind: + * static ushort idVendor; + * static ushort idProduct; + */ + +#if defined(CONFIG_USBNET_MANUFACTURER) +static char *iManufacturer = CONFIG_USBNET_MANUFACTURER; +#else +static char *iManufacturer = "U-boot"; +#endif + +/* These probably need to be configurable. */ +static ushort bcdDevice; +static char *iProduct; +static char *iSerialNumber; + +static char dev_addr[18]; + +static char host_addr[18]; + + +/*-------------------------------------------------------------------------*/ + +/* + * USB DRIVER HOOKUP (to the hardware driver, below us), mostly + * ep0 implementation: descriptors, config management, setup(). + * also optional class-specific notification interrupt transfer. + */ + +/* + * DESCRIPTORS ... most are static, but strings and (full) configuration + * descriptors are built on demand. For now we do either full CDC, or + * our simple subset, with RNDIS as an optional second configuration. + * + * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet. But + * the class descriptors match a modem (they're ignored; it's really just + * Ethernet functionality), they don't need the NOP altsetting, and the + * status transfer endpoint isn't optional. + */ + +#define STRING_MANUFACTURER 1 +#define STRING_PRODUCT 2 +#define STRING_ETHADDR 3 +#define STRING_DATA 4 +#define STRING_CONTROL 5 +#define STRING_RNDIS_CONTROL 6 +#define STRING_CDC 7 +#define STRING_SUBSET 8 +#define STRING_RNDIS 9 +#define STRING_SERIALNUMBER 10 + +/* holds our biggest descriptor (or RNDIS response) */ +#define USB_BUFSIZ 256 + +/* + * This device advertises one configuration, eth_config, unless RNDIS + * is enabled (rndis_config) on hardware supporting at least two configs. + * + * NOTE: Controllers like superh_udc should probably be able to use + * an RNDIS-only configuration. + * + * FIXME define some higher-powered configurations to make it easier + * to recharge batteries ... + */ + +#define DEV_CONFIG_VALUE 1 /* cdc or subset */ +#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ + +static struct usb_device_descriptor +device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + + .idVendor = __constant_cpu_to_le16(CDC_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(CDC_PRODUCT_NUM), + .iManufacturer = STRING_MANUFACTURER, + .iProduct = STRING_PRODUCT, + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor +otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + .bmAttributes = USB_OTG_SRP, +}; + +static struct usb_config_descriptor +eth_config = { + .bLength = sizeof eth_config, + .bDescriptorType = USB_DT_CONFIG, + + /* compute wTotalLength on the fly */ + .bNumInterfaces = 2, + .bConfigurationValue = DEV_CONFIG_VALUE, + .iConfiguration = STRING_CDC, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, +}; + +#ifdef CONFIG_USB_ETH_RNDIS +static struct usb_config_descriptor +rndis_config = { + .bLength = sizeof rndis_config, + .bDescriptorType = USB_DT_CONFIG, + + /* compute wTotalLength on the fly */ + .bNumInterfaces = 2, + .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, + .iConfiguration = STRING_RNDIS, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, +}; +#endif + +/* + * Compared to the simple CDC subset, the full CDC Ethernet model adds + * three class descriptors, two interface descriptors, optional status + * endpoint. Both have a "data" interface and two bulk endpoints. + * There are also differences in how control requests are handled. + * + * RNDIS shares a lot with CDC-Ethernet, since it's a variant of the + * CDC-ACM (modem) spec. Unfortunately MSFT's RNDIS driver is buggy; it + * may hang or oops. Since bugfixes (or accurate specs, letting Linux + * work around those bugs) are unlikely to ever come from MSFT, you may + * wish to avoid using RNDIS. + * + * MCCI offers an alternative to RNDIS if you need to connect to Windows + * but have hardware that can't support CDC Ethernet. We add descriptors + * to present the CDC Subset as a (nonconformant) CDC MDLM variant called + * "SAFE". That borrows from both CDC Ethernet and CDC MDLM. You can + * get those drivers from MCCI, or bundled with various products. + */ + +#ifdef CONFIG_USB_ETH_CDC +static struct usb_interface_descriptor +control_intf = { + .bLength = sizeof control_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + /* status endpoint is optional; this may be patched later */ + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bInterfaceProtocol = USB_CDC_PROTO_NONE, + .iInterface = STRING_CONTROL, +}; +#endif + +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_interface_descriptor +rndis_control_intf = { + .bLength = sizeof rndis_control_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, + .iInterface = STRING_RNDIS_CONTROL, +}; +#endif + +static const struct usb_cdc_header_desc header_desc = { + .bLength = sizeof header_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + + .bcdCDC = __constant_cpu_to_le16(0x0110), +}; + +#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) + +static const struct usb_cdc_union_desc union_desc = { + .bLength = sizeof union_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_UNION_TYPE, + + .bMasterInterface0 = 0, /* index of control interface */ + .bSlaveInterface0 = 1, /* index of DATA interface */ +}; + +#endif /* CDC || RNDIS */ + +#ifdef CONFIG_USB_ETH_RNDIS + +static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { + .bLength = sizeof call_mgmt_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, + + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + +static const struct usb_cdc_acm_descriptor acm_descriptor = { + .bLength = sizeof acm_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ACM_TYPE, + + .bmCapabilities = 0x00, +}; + +#endif + +#ifndef CONFIG_USB_ETH_CDC + +/* + * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various + * ways: data endpoints live in the control interface, there's no data + * interface, and it's not used to talk to a cell phone radio. + */ + +static const struct usb_cdc_mdlm_desc mdlm_desc = { + .bLength = sizeof mdlm_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_MDLM_TYPE, + + .bcdVersion = __constant_cpu_to_le16(0x0100), + .bGUID = { + 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, + 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, + }, +}; + +/* + * since "usb_cdc_mdlm_detail_desc" is a variable length structure, we + * can't really use its struct. All we do here is say that we're using + * the submode of "SAFE" which directly matches the CDC Subset. + */ +static const u8 mdlm_detail_desc[] = { + 6, + USB_DT_CS_INTERFACE, + USB_CDC_MDLM_DETAIL_TYPE, + + 0, /* "SAFE" */ + 0, /* network control capabilities (none) */ + 0, /* network data capabilities ("raw" encapsulation) */ +}; + +#endif + +static const struct usb_cdc_ether_desc ether_desc = { + .bLength = sizeof(ether_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, + + /* this descriptor actually adds value, surprise! */ + .iMACAddress = STRING_ETHADDR, + .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */ + .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN), + .wNumberMCFilters = __constant_cpu_to_le16(0), + .bNumberPowerFilters = 0, +}; + +#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) + +/* + * include the status endpoint if we can, even where it's optional. + * use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one + * packet, to simplify cancellation; and a big transfer interval, to + * waste less bandwidth. + * + * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even + * if they ignore the connect/disconnect notifications that real aether + * can provide. more advanced cdc configurations might want to support + * encapsulated commands (vendor-specific, using control-OUT). + * + * RNDIS requires the status endpoint, since it uses that encapsulation + * mechanism for its funky RPC scheme. + */ + +#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ + +static struct usb_endpoint_descriptor +fs_status_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, +}; +#endif + +#ifdef CONFIG_USB_ETH_CDC + +/* the default data interface has no endpoints ... */ + +static const struct usb_interface_descriptor +data_nop_intf = { + .bLength = sizeof data_nop_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, +}; + +/* ... but the "real" data interface has two bulk endpoints */ + +static const struct usb_interface_descriptor +data_intf = { + .bLength = sizeof data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = STRING_DATA, +}; + +#endif + +#ifdef CONFIG_USB_ETH_RNDIS + +/* RNDIS doesn't activate by changing to the "real" altsetting */ + +static const struct usb_interface_descriptor +rndis_data_intf = { + .bLength = sizeof rndis_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = STRING_DATA, +}; + +#endif + +#ifdef CONFIG_USB_ETH_SUBSET + +/* + * "Simple" CDC-subset option is a simple vendor-neutral model that most + * full speed controllers can handle: one interface, two bulk endpoints. + * + * To assist host side drivers, we fancy it up a bit, and add descriptors + * so some host side drivers will understand it as a "SAFE" variant. + */ + +static const struct usb_interface_descriptor +subset_data_intf = { + .bLength = sizeof subset_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, + .bInterfaceProtocol = 0, + .iInterface = STRING_DATA, +}; + +#endif /* SUBSET */ + +static struct usb_endpoint_descriptor +fs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(64), +}; + +static struct usb_endpoint_descriptor +fs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(64), +}; + +static const struct usb_descriptor_header *fs_eth_function[11] = { + (struct usb_descriptor_header *) &otg_descriptor, +#ifdef CONFIG_USB_ETH_CDC + /* "cdc" mode descriptors */ + (struct usb_descriptor_header *) &control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) ðer_desc, + /* NOTE: status endpoint may need to be removed */ + (struct usb_descriptor_header *) &fs_status_desc, + /* data interface, with altsetting */ + (struct usb_descriptor_header *) &data_nop_intf, + (struct usb_descriptor_header *) &data_intf, + (struct usb_descriptor_header *) &fs_source_desc, + (struct usb_descriptor_header *) &fs_sink_desc, + NULL, +#endif /* CONFIG_USB_ETH_CDC */ +}; + +static inline void fs_subset_descriptors(void) +{ +#ifdef CONFIG_USB_ETH_SUBSET + /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ + fs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; + fs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; + fs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; + fs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; + fs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; + fs_eth_function[6] = (struct usb_descriptor_header *) &fs_source_desc; + fs_eth_function[7] = (struct usb_descriptor_header *) &fs_sink_desc; + fs_eth_function[8] = NULL; +#else + fs_eth_function[1] = NULL; +#endif +} + +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_descriptor_header *fs_rndis_function[] = { + (struct usb_descriptor_header *) &otg_descriptor, + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) &fs_status_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &fs_source_desc, + (struct usb_descriptor_header *) &fs_sink_desc, + NULL, +}; +#endif + +/* + * usb 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + */ + +#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) +static struct usb_endpoint_descriptor +hs_status_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; +#endif /* CONFIG_USB_ETH_CDC */ + +static struct usb_endpoint_descriptor +hs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor +hs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_qualifier_descriptor +dev_qualifier = { + .bLength = sizeof dev_qualifier, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_COMM, + + .bNumConfigurations = 1, +}; + +static const struct usb_descriptor_header *hs_eth_function[11] = { + (struct usb_descriptor_header *) &otg_descriptor, +#ifdef CONFIG_USB_ETH_CDC + /* "cdc" mode descriptors */ + (struct usb_descriptor_header *) &control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) ðer_desc, + /* NOTE: status endpoint may need to be removed */ + (struct usb_descriptor_header *) &hs_status_desc, + /* data interface, with altsetting */ + (struct usb_descriptor_header *) &data_nop_intf, + (struct usb_descriptor_header *) &data_intf, + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + NULL, +#endif /* CONFIG_USB_ETH_CDC */ +}; + +static inline void hs_subset_descriptors(void) +{ +#ifdef CONFIG_USB_ETH_SUBSET + /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ + hs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; + hs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; + hs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; + hs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; + hs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; + hs_eth_function[6] = (struct usb_descriptor_header *) &hs_source_desc; + hs_eth_function[7] = (struct usb_descriptor_header *) &hs_sink_desc; + hs_eth_function[8] = NULL; +#else + hs_eth_function[1] = NULL; +#endif +} + +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_descriptor_header *hs_rndis_function[] = { + (struct usb_descriptor_header *) &otg_descriptor, + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) &hs_status_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + NULL, +}; +#endif + + +/* maxpacket and other transfer characteristics vary by speed. */ +static inline struct usb_endpoint_descriptor * +ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +/*-------------------------------------------------------------------------*/ + +/* descriptors that are built on-demand */ + +static char manufacturer[50]; +static char product_desc[40] = DRIVER_DESC; +static char serial_number[20]; + +/* address that the host will use ... usually assigned at random */ +static char ethaddr[2 * ETH_ALEN + 1]; + +/* static strings, in UTF-8 */ +static struct usb_string strings[] = { + { STRING_MANUFACTURER, manufacturer, }, + { STRING_PRODUCT, product_desc, }, + { STRING_SERIALNUMBER, serial_number, }, + { STRING_DATA, "Ethernet Data", }, + { STRING_ETHADDR, ethaddr, }, +#ifdef CONFIG_USB_ETH_CDC + { STRING_CDC, "CDC Ethernet", }, + { STRING_CONTROL, "CDC Communications Control", }, +#endif +#ifdef CONFIG_USB_ETH_SUBSET + { STRING_SUBSET, "CDC Ethernet Subset", }, +#endif +#ifdef CONFIG_USB_ETH_RNDIS + { STRING_RNDIS, "RNDIS", }, + { STRING_RNDIS_CONTROL, "RNDIS Communications Control", }, +#endif + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab = { + .language = 0x0409, /* en-us */ + .strings = strings, +}; + +/*============================================================================*/ +DEFINE_CACHE_ALIGN_BUFFER(u8, control_req, USB_BUFSIZ); + +#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) +DEFINE_CACHE_ALIGN_BUFFER(u8, status_req, STATUS_BYTECOUNT); +#endif + + +/** + * strlcpy - Copy a %NUL terminated string into a sized buffer + * @dest: Where to copy the string to + * @src: Where to copy the string from + * @size: size of destination buffer + * + * Compatible with *BSD: the result is always a valid + * NUL-terminated string that fits in the buffer (unless, + * of course, the buffer size is zero). It does not pad + * out the result like strncpy() does. + */ +size_t strlcpy(char *dest, const char *src, size_t size) +{ + size_t ret = strlen(src); + + if (size) { + size_t len = (ret >= size) ? size - 1 : ret; + memcpy(dest, src, len); + dest[len] = '\0'; + } + return ret; +} + +/*============================================================================*/ + +/* + * one config, two interfaces: control, data. + * complications: class descriptors, and an altsetting. + */ +static int +config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index, int is_otg) +{ + int len; + const struct usb_config_descriptor *config; + const struct usb_descriptor_header **function; + int hs = 0; + + if (gadget_is_dualspeed(g)) { + hs = (g->speed == USB_SPEED_HIGH); + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + } +#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function) + + if (index >= device_desc.bNumConfigurations) + return -EINVAL; + +#ifdef CONFIG_USB_ETH_RNDIS + /* + * list the RNDIS config first, to make Microsoft's drivers + * happy. DOCSIS 1.0 needs this too. + */ + if (device_desc.bNumConfigurations == 2 && index == 0) { + config = &rndis_config; + function = which_fn(rndis); + } else +#endif + { + config = ð_config; + function = which_fn(eth); + } + + /* for now, don't advertise srp-only devices */ + if (!is_otg) + function++; + + len = usb_gadget_config_buf(config, buf, USB_BUFSIZ, function); + if (len < 0) + return len; + ((struct usb_config_descriptor *) buf)->bDescriptorType = type; + return len; +} + +/*-------------------------------------------------------------------------*/ + +static void eth_start(struct eth_dev *dev, gfp_t gfp_flags); +static int alloc_requests(struct eth_dev *dev, unsigned n, gfp_t gfp_flags); + +static int +set_ether_config(struct eth_dev *dev, gfp_t gfp_flags) +{ + int result = 0; + struct usb_gadget *gadget = dev->gadget; + +#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) + /* status endpoint used for RNDIS and (optionally) CDC */ + if (!subset_active(dev) && dev->status_ep) { + dev->status = ep_desc(gadget, &hs_status_desc, + &fs_status_desc); + dev->status_ep->driver_data = dev; + + result = usb_ep_enable(dev->status_ep, dev->status); + if (result != 0) { + debug("enable %s --> %d\n", + dev->status_ep->name, result); + goto done; + } + } +#endif + + dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc); + dev->in_ep->driver_data = dev; + + dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); + dev->out_ep->driver_data = dev; + + /* + * With CDC, the host isn't allowed to use these two data + * endpoints in the default altsetting for the interface. + * so we don't activate them yet. Reset from SET_INTERFACE. + * + * Strictly speaking RNDIS should work the same: activation is + * a side effect of setting a packet filter. Deactivation is + * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG. + */ + if (!cdc_active(dev)) { + result = usb_ep_enable(dev->in_ep, dev->in); + if (result != 0) { + debug("enable %s --> %d\n", + dev->in_ep->name, result); + goto done; + } + + result = usb_ep_enable(dev->out_ep, dev->out); + if (result != 0) { + debug("enable %s --> %d\n", + dev->out_ep->name, result); + goto done; + } + } + +done: + if (result == 0) + result = alloc_requests(dev, qlen(gadget), gfp_flags); + + /* on error, disable any endpoints */ + if (result < 0) { + if (!subset_active(dev) && dev->status_ep) + (void) usb_ep_disable(dev->status_ep); + dev->status = NULL; + (void) usb_ep_disable(dev->in_ep); + (void) usb_ep_disable(dev->out_ep); + dev->in = NULL; + dev->out = NULL; + } else if (!cdc_active(dev)) { + /* + * activate non-CDC configs right away + * this isn't strictly according to the RNDIS spec + */ + eth_start(dev, GFP_ATOMIC); + } + + /* caller is responsible for cleanup on error */ + return result; +} + +static void eth_reset_config(struct eth_dev *dev) +{ + if (dev->config == 0) + return; + + debug("%s\n", __func__); + + rndis_uninit(dev->rndis_config); + + /* + * disable endpoints, forcing (synchronous) completion of + * pending i/o. then free the requests. + */ + + if (dev->in) { + usb_ep_disable(dev->in_ep); + if (dev->tx_req) { + usb_ep_free_request(dev->in_ep, dev->tx_req); + dev->tx_req = NULL; + } + } + if (dev->out) { + usb_ep_disable(dev->out_ep); + if (dev->rx_req) { + usb_ep_free_request(dev->out_ep, dev->rx_req); + dev->rx_req = NULL; + } + } + if (dev->status) + usb_ep_disable(dev->status_ep); + + dev->rndis = 0; + dev->cdc_filter = 0; + dev->config = 0; +} + +/* + * change our operational config. must agree with the code + * that returns config descriptors, and altsetting code. + */ +static int eth_set_config(struct eth_dev *dev, unsigned number, + gfp_t gfp_flags) +{ + int result = 0; + struct usb_gadget *gadget = dev->gadget; + + if (gadget_is_sa1100(gadget) + && dev->config + && dev->tx_qlen != 0) { + /* tx fifo is full, but we can't clear it...*/ + error("can't change configurations"); + return -ESPIPE; + } + eth_reset_config(dev); + + switch (number) { + case DEV_CONFIG_VALUE: + result = set_ether_config(dev, gfp_flags); + break; +#ifdef CONFIG_USB_ETH_RNDIS + case DEV_RNDIS_CONFIG_VALUE: + dev->rndis = 1; + result = set_ether_config(dev, gfp_flags); + break; +#endif + default: + result = -EINVAL; + /* FALL THROUGH */ + case 0: + break; + } + + if (result) { + if (number) + eth_reset_config(dev); + usb_gadget_vbus_draw(dev->gadget, + gadget_is_otg(dev->gadget) ? 8 : 100); + } else { + char *speed; + unsigned power; + + power = 2 * eth_config.bMaxPower; + usb_gadget_vbus_draw(dev->gadget, power); + + switch (gadget->speed) { + case USB_SPEED_FULL: + speed = "full"; break; +#ifdef CONFIG_USB_GADGET_DUALSPEED + case USB_SPEED_HIGH: + speed = "high"; break; +#endif + default: + speed = "?"; break; + } + + dev->config = number; + printf("%s speed config #%d: %d mA, %s, using %s\n", + speed, number, power, driver_desc, + rndis_active(dev) + ? "RNDIS" + : (cdc_active(dev) + ? "CDC Ethernet" + : "CDC Ethernet Subset")); + } + return result; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_ETH_CDC + +/* + * The interrupt endpoint is used in CDC networking models (Ethernet, ATM) + * only to notify the host about link status changes (which we support) or + * report completion of some encapsulated command (as used in RNDIS). Since + * we want this CDC Ethernet code to be vendor-neutral, we don't use that + * command mechanism; and only one status request is ever queued. + */ +static void eth_status_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct usb_cdc_notification *event = req->buf; + int value = req->status; + struct eth_dev *dev = ep->driver_data; + + /* issue the second notification if host reads the first */ + if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION + && value == 0) { + __le32 *data = req->buf + sizeof *event; + + event->bmRequestType = 0xA1; + event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; + event->wValue = __constant_cpu_to_le16(0); + event->wIndex = __constant_cpu_to_le16(1); + event->wLength = __constant_cpu_to_le16(8); + + /* SPEED_CHANGE data is up/down speeds in bits/sec */ + data[0] = data[1] = cpu_to_le32(BITRATE(dev->gadget)); + + req->length = STATUS_BYTECOUNT; + value = usb_ep_queue(ep, req, GFP_ATOMIC); + debug("send SPEED_CHANGE --> %d\n", value); + if (value == 0) + return; + } else if (value != -ECONNRESET) { + debug("event %02x --> %d\n", + event->bNotificationType, value); + if (event->bNotificationType == + USB_CDC_NOTIFY_SPEED_CHANGE) { + l_ethdev.network_started = 1; + printf("USB network up!\n"); + } + } + req->context = NULL; +} + +static void issue_start_status(struct eth_dev *dev) +{ + struct usb_request *req = dev->stat_req; + struct usb_cdc_notification *event; + int value; + + /* + * flush old status + * + * FIXME ugly idiom, maybe we'd be better with just + * a "cancel the whole queue" primitive since any + * unlink-one primitive has way too many error modes. + * here, we "know" toggle is already clear... + * + * FIXME iff req->context != null just dequeue it + */ + usb_ep_disable(dev->status_ep); + usb_ep_enable(dev->status_ep, dev->status); + + /* + * 3.8.1 says to issue first NETWORK_CONNECTION, then + * a SPEED_CHANGE. could be useful in some configs. + */ + event = req->buf; + event->bmRequestType = 0xA1; + event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; + event->wValue = __constant_cpu_to_le16(1); /* connected */ + event->wIndex = __constant_cpu_to_le16(1); + event->wLength = 0; + + req->length = sizeof *event; + req->complete = eth_status_complete; + req->context = dev; + + value = usb_ep_queue(dev->status_ep, req, GFP_ATOMIC); + if (value < 0) + debug("status buf queue --> %d\n", value); +} + +#endif + +/*-------------------------------------------------------------------------*/ + +static void eth_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + debug("setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +#ifdef CONFIG_USB_ETH_RNDIS + +static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + debug("rndis response complete --> %d, %d/%d\n", + req->status, req->actual, req->length); + + /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */ +} + +static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct eth_dev *dev = ep->driver_data; + int status; + + /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ + status = rndis_msg_parser(dev->rndis_config, (u8 *) req->buf); + if (status < 0) + error("%s: rndis parse error %d", __func__, status); +} + +#endif /* RNDIS */ + +/* + * The setup() callback implements all the ep0 functionality that's not + * handled lower down. CDC has a number of less-common features: + * + * - two interfaces: control, and ethernet data + * - Ethernet data interface has two altsettings: default, and active + * - class-specific descriptors for the control interface + * - class-specific control requests + */ +static int +eth_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct eth_dev *dev = get_gadget_data(gadget); + struct usb_request *req = dev->req; + int value = -EOPNOTSUPP; + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); + + /* + * descriptors just go into the pre-allocated ep0 buffer, + * while config change events may enable network traffic. + */ + + debug("%s\n", __func__); + + req->complete = eth_setup_complete; + switch (ctrl->bRequest) { + + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + break; + switch (wValue >> 8) { + + case USB_DT_DEVICE: + value = min(wLength, (u16) sizeof device_desc); + memcpy(req->buf, &device_desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + if (!gadget_is_dualspeed(gadget)) + break; + value = min(wLength, (u16) sizeof dev_qualifier); + memcpy(req->buf, &dev_qualifier, value); + break; + + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget_is_dualspeed(gadget)) + break; + /* FALLTHROUGH */ + case USB_DT_CONFIG: + value = config_buf(gadget, req->buf, + wValue >> 8, + wValue & 0xff, + gadget_is_otg(gadget)); + if (value >= 0) + value = min(wLength, (u16) value); + break; + + case USB_DT_STRING: + value = usb_gadget_get_string(&stringtab, + wValue & 0xff, req->buf); + + if (value >= 0) + value = min(wLength, (u16) value); + + break; + } + break; + + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + break; + if (gadget->a_hnp_support) + debug("HNP available\n"); + else if (gadget->a_alt_hnp_support) + debug("HNP needs a different root port\n"); + value = eth_set_config(dev, wValue, GFP_ATOMIC); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + break; + *(u8 *)req->buf = dev->config; + value = min(wLength, (u16) 1); + break; + + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE + || !dev->config + || wIndex > 1) + break; + if (!cdc_active(dev) && wIndex != 0) + break; + + /* + * PXA hardware partially handles SET_INTERFACE; + * we need to kluge around that interference. + */ + if (gadget_is_pxa(gadget)) { + value = eth_set_config(dev, DEV_CONFIG_VALUE, + GFP_ATOMIC); + /* + * PXA25x driver use non-CDC ethernet gadget. + * But only _CDC and _RNDIS code can signalize + * that network is working. So we signalize it + * here. + */ + l_ethdev.network_started = 1; + debug("USB network up!\n"); + goto done_set_intf; + } + +#ifdef CONFIG_USB_ETH_CDC + switch (wIndex) { + case 0: /* control/master intf */ + if (wValue != 0) + break; + if (dev->status) { + usb_ep_disable(dev->status_ep); + usb_ep_enable(dev->status_ep, dev->status); + } + + value = 0; + break; + case 1: /* data intf */ + if (wValue > 1) + break; + usb_ep_disable(dev->in_ep); + usb_ep_disable(dev->out_ep); + + /* + * CDC requires the data transfers not be done from + * the default interface setting ... also, setting + * the non-default interface resets filters etc. + */ + if (wValue == 1) { + if (!cdc_active(dev)) + break; + usb_ep_enable(dev->in_ep, dev->in); + usb_ep_enable(dev->out_ep, dev->out); + dev->cdc_filter = DEFAULT_FILTER; + if (dev->status) + issue_start_status(dev); + eth_start(dev, GFP_ATOMIC); + } + value = 0; + break; + } +#else + /* + * FIXME this is wrong, as is the assumption that + * all non-PXA hardware talks real CDC ... + */ + debug("set_interface ignored!\n"); +#endif /* CONFIG_USB_ETH_CDC */ + +done_set_intf: + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) + || !dev->config + || wIndex > 1) + break; + if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0) + break; + + /* for CDC, iff carrier is on, data interface is active. */ + if (rndis_active(dev) || wIndex != 1) + *(u8 *)req->buf = 0; + else { + /* *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; */ + /* carrier always ok ...*/ + *(u8 *)req->buf = 1 ; + } + value = min(wLength, (u16) 1); + break; + +#ifdef CONFIG_USB_ETH_CDC + case USB_CDC_SET_ETHERNET_PACKET_FILTER: + /* + * see 6.2.30: no data, wIndex = interface, + * wValue = packet filter bitmap + */ + if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) + || !cdc_active(dev) + || wLength != 0 + || wIndex > 1) + break; + debug("packet filter %02x\n", wValue); + dev->cdc_filter = wValue; + value = 0; + break; + + /* + * and potentially: + * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: + * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: + * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: + * case USB_CDC_GET_ETHERNET_STATISTIC: + */ + +#endif /* CONFIG_USB_ETH_CDC */ + +#ifdef CONFIG_USB_ETH_RNDIS + /* + * RNDIS uses the CDC command encapsulation mechanism to implement + * an RPC scheme, with much getting/setting of attributes by OID. + */ + case USB_CDC_SEND_ENCAPSULATED_COMMAND: + if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) + || !rndis_active(dev) + || wLength > USB_BUFSIZ + || wValue + || rndis_control_intf.bInterfaceNumber + != wIndex) + break; + /* read the request, then process it */ + value = wLength; + req->complete = rndis_command_complete; + /* later, rndis_control_ack () sends a notification */ + break; + + case USB_CDC_GET_ENCAPSULATED_RESPONSE: + if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) + == ctrl->bRequestType + && rndis_active(dev) + /* && wLength >= 0x0400 */ + && !wValue + && rndis_control_intf.bInterfaceNumber + == wIndex) { + u8 *buf; + u32 n; + + /* return the result */ + buf = rndis_get_next_response(dev->rndis_config, &n); + if (buf) { + memcpy(req->buf, buf, n); + req->complete = rndis_response_complete; + rndis_free_response(dev->rndis_config, buf); + value = n; + } + /* else stalls ... spec says to avoid that */ + } + break; +#endif /* RNDIS */ + + default: + debug("unknown control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + debug("respond with data transfer before status phase\n"); + req->length = value; + req->zero = value < wLength + && (value % gadget->ep0->maxpacket) == 0; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + eth_setup_complete(gadget->ep0, req); + } + } + + /* host either stalls (value < 0) or reports success */ + return value; +} + +/*-------------------------------------------------------------------------*/ + +static void rx_complete(struct usb_ep *ep, struct usb_request *req); + +static int rx_submit(struct eth_dev *dev, struct usb_request *req, + gfp_t gfp_flags) +{ + int retval = -ENOMEM; + size_t size; + + /* + * Padding up to RX_EXTRA handles minor disagreements with host. + * Normally we use the USB "terminate on short read" convention; + * so allow up to (N*maxpacket), since that memory is normally + * already allocated. Some hardware doesn't deal well with short + * reads (e.g. DMA must be N*maxpacket), so for now don't trim a + * byte off the end (to force hardware errors on overflow). + * + * RNDIS uses internal framing, and explicitly allows senders to + * pad to end-of-packet. That's potentially nice for speed, + * but means receivers can't recover synch on their own. + */ + + debug("%s\n", __func__); + if (!req) + return -EINVAL; + + size = (ETHER_HDR_SIZE + dev->mtu + RX_EXTRA); + size += dev->out_ep->maxpacket - 1; + if (rndis_active(dev)) + size += sizeof(struct rndis_packet_msg_type); + size -= size % dev->out_ep->maxpacket; + + /* + * Some platforms perform better when IP packets are aligned, + * but on at least one, checksumming fails otherwise. Note: + * RNDIS headers involve variable numbers of LE32 values. + */ + + req->buf = (u8 *) NetRxPackets[0]; + req->length = size; + req->complete = rx_complete; + + retval = usb_ep_queue(dev->out_ep, req, gfp_flags); + + if (retval) + error("rx submit --> %d", retval); + + return retval; +} + +static void rx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct eth_dev *dev = ep->driver_data; + + debug("%s: status %d\n", __func__, req->status); + switch (req->status) { + /* normal completion */ + case 0: + if (rndis_active(dev)) { + /* we know MaxPacketsPerTransfer == 1 here */ + int length = rndis_rm_hdr(req->buf, req->actual); + if (length < 0) + goto length_err; + req->length -= length; + req->actual -= length; + } + if (req->actual < ETH_HLEN || ETH_FRAME_LEN < req->actual) { +length_err: + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; + debug("rx length %d\n", req->length); + break; + } + + dev->stats.rx_packets++; + dev->stats.rx_bytes += req->length; + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + break; + + /* data overrun */ + case -EOVERFLOW: + dev->stats.rx_over_errors++; + /* FALLTHROUGH */ + default: + dev->stats.rx_errors++; + break; + } + + packet_received = 1; +} + +static int alloc_requests(struct eth_dev *dev, unsigned n, gfp_t gfp_flags) +{ + + dev->tx_req = usb_ep_alloc_request(dev->in_ep, 0); + + if (!dev->tx_req) + goto fail1; + + dev->rx_req = usb_ep_alloc_request(dev->out_ep, 0); + + if (!dev->rx_req) + goto fail2; + + return 0; + +fail2: + usb_ep_free_request(dev->in_ep, dev->tx_req); +fail1: + error("can't alloc requests"); + return -1; +} + +static void tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct eth_dev *dev = ep->driver_data; + + debug("%s: status %s\n", __func__, (req->status) ? "failed" : "ok"); + switch (req->status) { + default: + dev->stats.tx_errors++; + debug("tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + dev->stats.tx_bytes += req->length; + } + dev->stats.tx_packets++; + + packet_sent = 1; +} + +static inline int eth_is_promisc(struct eth_dev *dev) +{ + /* no filters for the CDC subset; always promisc */ + if (subset_active(dev)) + return 1; + return dev->cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; +} + +#if 0 +static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + int length = skb->len; + int retval; + struct usb_request *req = NULL; + unsigned long flags; + + /* apply outgoing CDC or RNDIS filters */ + if (!eth_is_promisc (dev)) { + u8 *dest = skb->data; + + if (is_multicast_ether_addr(dest)) { + u16 type; + + /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host + * SET_ETHERNET_MULTICAST_FILTERS requests + */ + if (is_broadcast_ether_addr(dest)) + type = USB_CDC_PACKET_TYPE_BROADCAST; + else + type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; + if (!(dev->cdc_filter & type)) { + dev_kfree_skb_any (skb); + return 0; + } + } + /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ + } + + spin_lock_irqsave(&dev->req_lock, flags); + /* + * this freelist can be empty if an interrupt triggered disconnect() + * and reconfigured the gadget (shutting down this queue) after the + * network stack decided to xmit but before we got the spinlock. + */ + if (list_empty(&dev->tx_reqs)) { + spin_unlock_irqrestore(&dev->req_lock, flags); + return 1; + } + + req = container_of (dev->tx_reqs.next, struct usb_request, list); + list_del (&req->list); + + /* temporarily stop TX queue when the freelist empties */ + if (list_empty (&dev->tx_reqs)) + netif_stop_queue (net); + spin_unlock_irqrestore(&dev->req_lock, flags); + + /* no buffer copies needed, unless the network stack did it + * or the hardware can't use skb buffers. + * or there's not enough space for any RNDIS headers we need + */ + if (rndis_active(dev)) { + struct sk_buff *skb_rndis; + + skb_rndis = skb_realloc_headroom (skb, + sizeof (struct rndis_packet_msg_type)); + if (!skb_rndis) + goto drop; + + dev_kfree_skb_any (skb); + skb = skb_rndis; + rndis_add_hdr (skb); + length = skb->len; + } + req->buf = skb->data; + req->context = skb; + req->complete = tx_complete; + + /* use zlp framing on tx for strict CDC-Ether conformance, + * though any robust network rx path ignores extra padding. + * and some hardware doesn't like to write zlps. + */ + req->zero = 1; + if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0) + length++; + + req->length = length; + + /* throttle highspeed IRQ rate back slightly */ + if (gadget_is_dualspeed(dev->gadget)) + req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) + ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) + : 0; + + retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC); + switch (retval) { + default: + DEBUG (dev, "tx queue err %d\n", retval); + break; + case 0: + net->trans_start = jiffies; + atomic_inc (&dev->tx_qlen); + } + + if (retval) { +drop: + dev->stats.tx_dropped++; + dev_kfree_skb_any (skb); + spin_lock_irqsave(&dev->req_lock, flags); + if (list_empty (&dev->tx_reqs)) + netif_start_queue (net); + list_add (&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ +#endif + +static void eth_unbind(struct usb_gadget *gadget) +{ + struct eth_dev *dev = get_gadget_data(gadget); + + debug("%s...\n", __func__); + rndis_deregister(dev->rndis_config); + rndis_exit(); + + /* we've already been disconnected ... no i/o is active */ + if (dev->req) { + usb_ep_free_request(gadget->ep0, dev->req); + dev->req = NULL; + } + if (dev->stat_req) { + usb_ep_free_request(dev->status_ep, dev->stat_req); + dev->stat_req = NULL; + } + + if (dev->tx_req) { + usb_ep_free_request(dev->in_ep, dev->tx_req); + dev->tx_req = NULL; + } + + if (dev->rx_req) { + usb_ep_free_request(dev->out_ep, dev->rx_req); + dev->rx_req = NULL; + } + +/* unregister_netdev (dev->net);*/ +/* free_netdev(dev->net);*/ + + dev->gadget = NULL; + set_gadget_data(gadget, NULL); +} + +static void eth_disconnect(struct usb_gadget *gadget) +{ + eth_reset_config(get_gadget_data(gadget)); + /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */ +} + +static void eth_suspend(struct usb_gadget *gadget) +{ + /* Not used */ +} + +static void eth_resume(struct usb_gadget *gadget) +{ + /* Not used */ +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_ETH_RNDIS + +/* + * The interrupt endpoint is used in RNDIS to notify the host when messages + * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT + * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even + * REMOTE_NDIS_KEEPALIVE_MSG. + * + * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and + * normally just one notification will be queued. + */ + +static void rndis_control_ack_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct eth_dev *dev = ep->driver_data; + + debug("%s...\n", __func__); + if (req->status || req->actual != req->length) + debug("rndis control ack complete --> %d, %d/%d\n", + req->status, req->actual, req->length); + + if (!l_ethdev.network_started) { + if (rndis_get_state(dev->rndis_config) + == RNDIS_DATA_INITIALIZED) { + l_ethdev.network_started = 1; + printf("USB RNDIS network up!\n"); + } + } + + req->context = NULL; + + if (req != dev->stat_req) + usb_ep_free_request(ep, req); +} + +static char rndis_resp_buf[8] __attribute__((aligned(sizeof(__le32)))); + +static int rndis_control_ack(struct eth_device *net) +{ + struct eth_dev *dev = &l_ethdev; + int length; + struct usb_request *resp = dev->stat_req; + + /* in case RNDIS calls this after disconnect */ + if (!dev->status) { + debug("status ENODEV\n"); + return -ENODEV; + } + + /* in case queue length > 1 */ + if (resp->context) { + resp = usb_ep_alloc_request(dev->status_ep, GFP_ATOMIC); + if (!resp) + return -ENOMEM; + resp->buf = rndis_resp_buf; + } + + /* + * Send RNDIS RESPONSE_AVAILABLE notification; + * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too + */ + resp->length = 8; + resp->complete = rndis_control_ack_complete; + resp->context = dev; + + *((__le32 *) resp->buf) = __constant_cpu_to_le32(1); + *((__le32 *) (resp->buf + 4)) = __constant_cpu_to_le32(0); + + length = usb_ep_queue(dev->status_ep, resp, GFP_ATOMIC); + if (length < 0) { + resp->status = 0; + rndis_control_ack_complete(dev->status_ep, resp); + } + + return 0; +} + +#else + +#define rndis_control_ack NULL + +#endif /* RNDIS */ + +static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) +{ + if (rndis_active(dev)) { + rndis_set_param_medium(dev->rndis_config, + NDIS_MEDIUM_802_3, + BITRATE(dev->gadget)/100); + rndis_signal_connect(dev->rndis_config); + } +} + +static int eth_stop(struct eth_dev *dev) +{ +#ifdef RNDIS_COMPLETE_SIGNAL_DISCONNECT + unsigned long ts; + unsigned long timeout = CONFIG_SYS_HZ; /* 1 sec to stop RNDIS */ +#endif + + if (rndis_active(dev)) { + rndis_set_param_medium(dev->rndis_config, NDIS_MEDIUM_802_3, 0); + rndis_signal_disconnect(dev->rndis_config); + +#ifdef RNDIS_COMPLETE_SIGNAL_DISCONNECT + /* Wait until host receives OID_GEN_MEDIA_CONNECT_STATUS */ + ts = get_timer(0); + while (get_timer(ts) < timeout) + usb_gadget_handle_interrupts(); +#endif + + rndis_uninit(dev->rndis_config); + dev->rndis = 0; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int is_eth_addr_valid(char *str) +{ + if (strlen(str) == 17) { + int i; + char *p, *q; + uchar ea[6]; + + /* see if it looks like an ethernet address */ + + p = str; + + for (i = 0; i < 6; i++) { + char term = (i == 5 ? '\0' : ':'); + + ea[i] = simple_strtol(p, &q, 16); + + if ((q - p) != 2 || *q++ != term) + break; + + p = q; + } + + /* Now check the contents. */ + return is_valid_ether_addr(ea); + } + return 0; +} + +static u8 nibble(unsigned char c) +{ + if (likely(isdigit(c))) + return c - '0'; + c = toupper(c); + if (likely(isxdigit(c))) + return 10 + c - 'A'; + return 0; +} + +static int get_ether_addr(const char *str, u8 *dev_addr) +{ + if (str) { + unsigned i; + + for (i = 0; i < 6; i++) { + unsigned char num; + + if ((*str == '.') || (*str == ':')) + str++; + num = nibble(*str++) << 4; + num |= (nibble(*str++)); + dev_addr[i] = num; + } + if (is_valid_ether_addr(dev_addr)) + return 0; + } + return 1; +} + +static int eth_bind(struct usb_gadget *gadget) +{ + struct eth_dev *dev = &l_ethdev; + u8 cdc = 1, zlp = 1, rndis = 1; + struct usb_ep *in_ep, *out_ep, *status_ep = NULL; + int status = -ENOMEM; + int gcnum; + u8 tmp[7]; + + /* these flags are only ever cleared; compiler take note */ +#ifndef CONFIG_USB_ETH_CDC + cdc = 0; +#endif +#ifndef CONFIG_USB_ETH_RNDIS + rndis = 0; +#endif + /* + * Because most host side USB stacks handle CDC Ethernet, that + * standard protocol is _strongly_ preferred for interop purposes. + * (By everyone except Microsoft.) + */ + if (gadget_is_pxa(gadget)) { + /* pxa doesn't support altsettings */ + cdc = 0; + } else if (gadget_is_musbhdrc(gadget)) { + /* reduce tx dma overhead by avoiding special cases */ + zlp = 0; + } else if (gadget_is_sh(gadget)) { + /* sh doesn't support multiple interfaces or configs */ + cdc = 0; + rndis = 0; + } else if (gadget_is_sa1100(gadget)) { + /* hardware can't write zlps */ + zlp = 0; + /* + * sa1100 CAN do CDC, without status endpoint ... we use + * non-CDC to be compatible with ARM Linux-2.4 "usb-eth". + */ + cdc = 0; + } + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0300 + gcnum); + else { + /* + * can't assume CDC works. don't want to default to + * anything less functional on CDC-capable hardware, + * so we fail in this case. + */ + error("controller '%s' not recognized", + gadget->name); + return -ENODEV; + } + + /* + * If there's an RNDIS configuration, that's what Windows wants to + * be using ... so use these product IDs here and in the "linux.inf" + * needed to install MSFT drivers. Current Linux kernels will use + * the second configuration if it's CDC Ethernet, and need some help + * to choose the right configuration otherwise. + */ + if (rndis) { +#if defined(CONFIG_USB_RNDIS_VENDOR_ID) && defined(CONFIG_USB_RNDIS_PRODUCT_ID) + device_desc.idVendor = + __constant_cpu_to_le16(CONFIG_USB_RNDIS_VENDOR_ID); + device_desc.idProduct = + __constant_cpu_to_le16(CONFIG_USB_RNDIS_PRODUCT_ID); +#else + device_desc.idVendor = + __constant_cpu_to_le16(RNDIS_VENDOR_NUM); + device_desc.idProduct = + __constant_cpu_to_le16(RNDIS_PRODUCT_NUM); +#endif + sprintf(product_desc, "RNDIS/%s", driver_desc); + + /* + * CDC subset ... recognized by Linux since 2.4.10, but Windows + * drivers aren't widely available. (That may be improved by + * supporting one submode of the "SAFE" variant of MDLM.) + */ + } else { +#if defined(CONFIG_USB_CDC_VENDOR_ID) && defined(CONFIG_USB_CDC_PRODUCT_ID) + device_desc.idVendor = cpu_to_le16(CONFIG_USB_CDC_VENDOR_ID); + device_desc.idProduct = cpu_to_le16(CONFIG_USB_CDC_PRODUCT_ID); +#else + if (!cdc) { + device_desc.idVendor = + __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); + device_desc.idProduct = + __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); + } +#endif + } + /* support optional vendor/distro customization */ + if (bcdDevice) + device_desc.bcdDevice = cpu_to_le16(bcdDevice); + if (iManufacturer) + strlcpy(manufacturer, iManufacturer, sizeof manufacturer); + if (iProduct) + strlcpy(product_desc, iProduct, sizeof product_desc); + if (iSerialNumber) { + device_desc.iSerialNumber = STRING_SERIALNUMBER, + strlcpy(serial_number, iSerialNumber, sizeof serial_number); + } + + /* all we really need is bulk IN/OUT */ + usb_ep_autoconfig_reset(gadget); + in_ep = usb_ep_autoconfig(gadget, &fs_source_desc); + if (!in_ep) { +autoconf_fail: + error("can't autoconfigure on %s\n", + gadget->name); + return -ENODEV; + } + in_ep->driver_data = in_ep; /* claim */ + + out_ep = usb_ep_autoconfig(gadget, &fs_sink_desc); + if (!out_ep) + goto autoconf_fail; + out_ep->driver_data = out_ep; /* claim */ + +#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) + /* + * CDC Ethernet control interface doesn't require a status endpoint. + * Since some hosts expect one, try to allocate one anyway. + */ + if (cdc || rndis) { + status_ep = usb_ep_autoconfig(gadget, &fs_status_desc); + if (status_ep) { + status_ep->driver_data = status_ep; /* claim */ + } else if (rndis) { + error("can't run RNDIS on %s", gadget->name); + return -ENODEV; +#ifdef CONFIG_USB_ETH_CDC + } else if (cdc) { + control_intf.bNumEndpoints = 0; + /* FIXME remove endpoint from descriptor list */ +#endif + } + } +#endif + + /* one config: cdc, else minimal subset */ + if (!cdc) { + eth_config.bNumInterfaces = 1; + eth_config.iConfiguration = STRING_SUBSET; + + /* + * use functions to set these up, in case we're built to work + * with multiple controllers and must override CDC Ethernet. + */ + fs_subset_descriptors(); + hs_subset_descriptors(); + } + + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + usb_gadget_set_selfpowered(gadget); + + /* For now RNDIS is always a second config */ + if (rndis) + device_desc.bNumConfigurations = 2; + + if (gadget_is_dualspeed(gadget)) { + if (rndis) + dev_qualifier.bNumConfigurations = 2; + else if (!cdc) + dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC; + + /* assumes ep0 uses the same value for both speeds ... */ + dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; + + /* and that all endpoints are dual-speed */ + hs_source_desc.bEndpointAddress = + fs_source_desc.bEndpointAddress; + hs_sink_desc.bEndpointAddress = + fs_sink_desc.bEndpointAddress; +#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) + if (status_ep) + hs_status_desc.bEndpointAddress = + fs_status_desc.bEndpointAddress; +#endif + } + + if (gadget_is_otg(gadget)) { + otg_descriptor.bmAttributes |= USB_OTG_HNP, + eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + eth_config.bMaxPower = 4; +#ifdef CONFIG_USB_ETH_RNDIS + rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + rndis_config.bMaxPower = 4; +#endif + } + + + /* network device setup */ + dev->net = &l_netdev; + + dev->cdc = cdc; + dev->zlp = zlp; + + dev->in_ep = in_ep; + dev->out_ep = out_ep; + dev->status_ep = status_ep; + + /* + * Module params for these addresses should come from ID proms. + * The host side address is used with CDC and RNDIS, and commonly + * ends up in a persistent config database. It's not clear if + * host side code for the SAFE thing cares -- its original BLAN + * thing didn't, Sharp never assigned those addresses on Zaurii. + */ + get_ether_addr(dev_addr, dev->net->enetaddr); + + memset(tmp, 0, sizeof(tmp)); + memcpy(tmp, dev->net->enetaddr, sizeof(dev->net->enetaddr)); + + get_ether_addr(host_addr, dev->host_mac); + + sprintf(ethaddr, "%02X%02X%02X%02X%02X%02X", + dev->host_mac[0], dev->host_mac[1], + dev->host_mac[2], dev->host_mac[3], + dev->host_mac[4], dev->host_mac[5]); + + if (rndis) { + status = rndis_init(); + if (status < 0) { + error("can't init RNDIS, %d", status); + goto fail; + } + } + + /* + * use PKTSIZE (or aligned... from u-boot) and set + * wMaxSegmentSize accordingly + */ + dev->mtu = PKTSIZE_ALIGN; /* RNDIS does not like this, only 1514, TODO*/ + + /* preallocate control message data and buffer */ + dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!dev->req) + goto fail; + dev->req->buf = control_req; + dev->req->complete = eth_setup_complete; + + /* ... and maybe likewise for status transfer */ +#if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) + if (dev->status_ep) { + dev->stat_req = usb_ep_alloc_request(dev->status_ep, + GFP_KERNEL); + if (!dev->stat_req) { + usb_ep_free_request(dev->status_ep, dev->req); + + goto fail; + } + dev->stat_req->buf = status_req; + dev->stat_req->context = NULL; + } +#endif + + /* finish hookup to lower layer ... */ + dev->gadget = gadget; + set_gadget_data(gadget, dev); + gadget->ep0->driver_data = dev; + + /* + * two kinds of host-initiated state changes: + * - iff DATA transfer is active, carrier is "on" + * - tx queueing enabled if open *and* carrier is "on" + */ + + printf("using %s, OUT %s IN %s%s%s\n", gadget->name, + out_ep->name, in_ep->name, + status_ep ? " STATUS " : "", + status_ep ? status_ep->name : "" + ); + printf("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->net->enetaddr[0], dev->net->enetaddr[1], + dev->net->enetaddr[2], dev->net->enetaddr[3], + dev->net->enetaddr[4], dev->net->enetaddr[5]); + + if (cdc || rndis) + printf("HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->host_mac[0], dev->host_mac[1], + dev->host_mac[2], dev->host_mac[3], + dev->host_mac[4], dev->host_mac[5]); + + if (rndis) { + u32 vendorID = 0; + + /* FIXME RNDIS vendor id == "vendor NIC code" == ? */ + + dev->rndis_config = rndis_register(rndis_control_ack); + if (dev->rndis_config < 0) { +fail0: + eth_unbind(gadget); + debug("RNDIS setup failed\n"); + status = -ENODEV; + goto fail; + } + + /* these set up a lot of the OIDs that RNDIS needs */ + rndis_set_host_mac(dev->rndis_config, dev->host_mac); + if (rndis_set_param_dev(dev->rndis_config, dev->net, dev->mtu, + &dev->stats, &dev->cdc_filter)) + goto fail0; + if (rndis_set_param_vendor(dev->rndis_config, vendorID, + manufacturer)) + goto fail0; + if (rndis_set_param_medium(dev->rndis_config, + NDIS_MEDIUM_802_3, 0)) + goto fail0; + printf("RNDIS ready\n"); + } + return 0; + +fail: + error("%s failed, status = %d", __func__, status); + eth_unbind(gadget); + return status; +} + +/*-------------------------------------------------------------------------*/ + +static int usb_eth_init(struct eth_device *netdev, bd_t *bd) +{ + struct eth_dev *dev = &l_ethdev; + struct usb_gadget *gadget; + unsigned long ts; + unsigned long timeout = USB_CONNECT_TIMEOUT; + + if (!netdev) { + error("received NULL ptr"); + goto fail; + } + + /* Configure default mac-addresses for the USB ethernet device */ +#ifdef CONFIG_USBNET_DEV_ADDR + strlcpy(dev_addr, CONFIG_USBNET_DEV_ADDR, sizeof(dev_addr)); +#endif +#ifdef CONFIG_USBNET_HOST_ADDR + strlcpy(host_addr, CONFIG_USBNET_HOST_ADDR, sizeof(host_addr)); +#endif + /* Check if the user overruled the MAC addresses */ + if (getenv("usbnet_devaddr")) + strlcpy(dev_addr, getenv("usbnet_devaddr"), + sizeof(dev_addr)); + + if (getenv("usbnet_hostaddr")) + strlcpy(host_addr, getenv("usbnet_hostaddr"), + sizeof(host_addr)); + + if (!is_eth_addr_valid(dev_addr)) { + error("Need valid 'usbnet_devaddr' to be set"); + goto fail; + } + if (!is_eth_addr_valid(host_addr)) { + error("Need valid 'usbnet_hostaddr' to be set"); + goto fail; + } + + if (usb_gadget_register_driver(ð_driver) < 0) + goto fail; + + dev->network_started = 0; + + packet_received = 0; + packet_sent = 0; + + gadget = dev->gadget; + usb_gadget_connect(gadget); + + if (getenv("cdc_connect_timeout")) + timeout = simple_strtoul(getenv("cdc_connect_timeout"), + NULL, 10) * CONFIG_SYS_HZ; + ts = get_timer(0); + while (!l_ethdev.network_started) { + /* Handle control-c and timeouts */ + if (ctrlc() || (get_timer(ts) > timeout)) { + error("The remote end did not respond in time."); + goto fail; + } + usb_gadget_handle_interrupts(); + } + + packet_received = 0; + rx_submit(dev, dev->rx_req, 0); + return 0; +fail: + return -1; +} + +static int usb_eth_send(struct eth_device *netdev, void *packet, int length) +{ + int retval; + void *rndis_pkt = NULL; + struct eth_dev *dev = &l_ethdev; + struct usb_request *req = dev->tx_req; + unsigned long ts; + unsigned long timeout = USB_CONNECT_TIMEOUT; + + debug("%s:...\n", __func__); + + /* new buffer is needed to include RNDIS header */ + if (rndis_active(dev)) { + rndis_pkt = malloc(length + + sizeof(struct rndis_packet_msg_type)); + if (!rndis_pkt) { + error("No memory to alloc RNDIS packet"); + goto drop; + } + rndis_add_hdr(rndis_pkt, length); + memcpy(rndis_pkt + sizeof(struct rndis_packet_msg_type), + packet, length); + packet = rndis_pkt; + length += sizeof(struct rndis_packet_msg_type); + } + req->buf = packet; + req->context = NULL; + req->complete = tx_complete; + + /* + * use zlp framing on tx for strict CDC-Ether conformance, + * though any robust network rx path ignores extra padding. + * and some hardware doesn't like to write zlps. + */ + req->zero = 1; + if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0) + length++; + + req->length = length; +#if 0 + /* throttle highspeed IRQ rate back slightly */ + if (gadget_is_dualspeed(dev->gadget)) + req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) + ? ((dev->tx_qlen % qmult) != 0) : 0; +#endif + dev->tx_qlen = 1; + ts = get_timer(0); + packet_sent = 0; + + retval = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC); + + if (!retval) + debug("%s: packet queued\n", __func__); + while (!packet_sent) { + if (get_timer(ts) > timeout) { + printf("timeout sending packets to usb ethernet\n"); + return -1; + } + usb_gadget_handle_interrupts(); + } + if (rndis_pkt) + free(rndis_pkt); + + return 0; +drop: + dev->stats.tx_dropped++; + return -ENOMEM; +} + +static int usb_eth_recv(struct eth_device *netdev) +{ + struct eth_dev *dev = &l_ethdev; + + usb_gadget_handle_interrupts(); + + if (packet_received) { + debug("%s: packet received\n", __func__); + if (dev->rx_req) { + NetReceive(NetRxPackets[0], dev->rx_req->length); + packet_received = 0; + + rx_submit(dev, dev->rx_req, 0); + } else + error("dev->rx_req invalid"); + } + return 0; +} + +void usb_eth_halt(struct eth_device *netdev) +{ + struct eth_dev *dev = &l_ethdev; + + if (!netdev) { + error("received NULL ptr"); + return; + } + + /* If the gadget not registered, simple return */ + if (!dev->gadget) + return; + + /* + * Some USB controllers may need additional deinitialization here + * before dropping pull-up (also due to hardware issues). + * For example: unhandled interrupt with status stage started may + * bring the controller to fully broken state (until board reset). + * There are some variants to debug and fix such cases: + * 1) In the case of RNDIS connection eth_stop can perform additional + * interrupt handling. See RNDIS_COMPLETE_SIGNAL_DISCONNECT definition. + * 2) 'pullup' callback in your UDC driver can be improved to perform + * this deinitialization. + */ + eth_stop(dev); + + usb_gadget_disconnect(dev->gadget); + + /* Clear pending interrupt */ + if (dev->network_started) { + usb_gadget_handle_interrupts(); + dev->network_started = 0; + } + + usb_gadget_unregister_driver(ð_driver); +} + +static struct usb_gadget_driver eth_driver = { + .speed = DEVSPEED, + + .bind = eth_bind, + .unbind = eth_unbind, + + .setup = eth_setup, + .disconnect = eth_disconnect, + + .suspend = eth_suspend, + .resume = eth_resume, +}; + +int usb_eth_initialize(bd_t *bi) +{ + struct eth_device *netdev = &l_netdev; + + strlcpy(netdev->name, USB_NET_NAME, sizeof(netdev->name)); + + netdev->init = usb_eth_init; + netdev->send = usb_eth_send; + netdev->recv = usb_eth_recv; + netdev->halt = usb_eth_halt; + +#ifdef CONFIG_MCAST_TFTP + #error not supported +#endif + eth_register(netdev); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/f_dfu.c b/qemu/roms/u-boot/drivers/usb/gadget/f_dfu.c new file mode 100644 index 000000000..1b1e1793d --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/f_dfu.c @@ -0,0 +1,822 @@ +/* + * f_dfu.c -- Device Firmware Update USB function + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz + * Lukasz Majewski + * + * Based on OpenMoko u-boot: drivers/usb/usbdfu.c + * (C) 2007 by OpenMoko, Inc. + * Author: Harald Welte + * + * based on existing SAM7DFU code from OpenPCD: + * (C) Copyright 2006 by Harald Welte + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "f_dfu.h" + +struct f_dfu { + struct usb_function usb_function; + + struct usb_descriptor_header **function; + struct usb_string *strings; + + /* when configured, we have one config */ + u8 config; + u8 altsetting; + enum dfu_state dfu_state; + unsigned int dfu_status; + + /* Send/received block number is handy for data integrity check */ + int blk_seq_num; + unsigned int poll_timeout; +}; + +typedef int (*dfu_state_fn) (struct f_dfu *, + const struct usb_ctrlrequest *, + struct usb_gadget *, + struct usb_request *); + +static inline struct f_dfu *func_to_dfu(struct usb_function *f) +{ + return container_of(f, struct f_dfu, usb_function); +} + +static const struct dfu_function_descriptor dfu_func = { + .bLength = sizeof dfu_func, + .bDescriptorType = DFU_DT_FUNC, + .bmAttributes = DFU_BIT_WILL_DETACH | + DFU_BIT_MANIFESTATION_TOLERANT | + DFU_BIT_CAN_UPLOAD | + DFU_BIT_CAN_DNLOAD, + .wDetachTimeOut = 0, + .wTransferSize = DFU_USB_BUFSIZ, + .bcdDFUVersion = __constant_cpu_to_le16(0x0110), +}; + +static struct usb_interface_descriptor dfu_intf_runtime = { + .bLength = sizeof dfu_intf_runtime, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_APP_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_descriptor_header *dfu_runtime_descs[] = { + (struct usb_descriptor_header *) &dfu_intf_runtime, + NULL, +}; + +static const struct usb_qualifier_descriptor dev_qualifier = { + .bLength = sizeof dev_qualifier, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + .bNumConfigurations = 1, +}; + +static const char dfu_name[] = "Device Firmware Upgrade"; + +/* + * static strings, in UTF-8 + * + * dfu_generic configuration + */ +static struct usb_string strings_dfu_generic[] = { + [0].s = dfu_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dfu_generic = { + .language = 0x0409, /* en-us */ + .strings = strings_dfu_generic, +}; + +static struct usb_gadget_strings *dfu_generic_strings[] = { + &stringtab_dfu_generic, + NULL, +}; + +/* + * usb_function specific + */ +static struct usb_gadget_strings stringtab_dfu = { + .language = 0x0409, /* en-us */ + /* + * .strings + * + * assigned during initialization, + * depends on number of flash entities + * + */ +}; + +static struct usb_gadget_strings *dfu_strings[] = { + &stringtab_dfu, + NULL, +}; + +static void dfu_set_poll_timeout(struct dfu_status *dstat, unsigned int ms) +{ + /* + * The bwPollTimeout DFU_GETSTATUS request payload provides information + * about minimum time, in milliseconds, that the host should wait before + * sending a subsequent DFU_GETSTATUS request + * + * This permits the device to vary the delay depending on its need to + * erase or program the memory + * + */ + + unsigned char *p = (unsigned char *)&ms; + + if (!ms || (ms & ~DFU_POLL_TIMEOUT_MASK)) { + dstat->bwPollTimeout[0] = 0; + dstat->bwPollTimeout[1] = 0; + dstat->bwPollTimeout[2] = 0; + + return; + } + + dstat->bwPollTimeout[0] = *p++; + dstat->bwPollTimeout[1] = *p++; + dstat->bwPollTimeout[2] = *p; +} + +/*-------------------------------------------------------------------------*/ + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); +} + +static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + dfu_flush(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); +} + +static void handle_getstatus(struct usb_request *req) +{ + struct dfu_status *dstat = (struct dfu_status *)req->buf; + struct f_dfu *f_dfu = req->context; + + dfu_set_poll_timeout(dstat, 0); + + switch (f_dfu->dfu_state) { + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + break; + case DFU_STATE_dfuMANIFEST_SYNC: + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST; + break; + case DFU_STATE_dfuMANIFEST: + dfu_set_poll_timeout(dstat, DFU_MANIFEST_POLL_TIMEOUT); + default: + break; + } + + if (f_dfu->poll_timeout) + if (!(f_dfu->blk_seq_num % + (dfu_get_buf_size() / DFU_USB_BUFSIZ))) + dfu_set_poll_timeout(dstat, f_dfu->poll_timeout); + + /* send status response */ + dstat->bStatus = f_dfu->dfu_status; + dstat->bState = f_dfu->dfu_state; + dstat->iString = 0; +} + +static void handle_getstate(struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + ((u8 *)req->buf)[0] = f_dfu->dfu_state; + req->actual = sizeof(u8); +} + +static inline void to_dfu_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = dfu_strings; + f_dfu->usb_function.hs_descriptors = f_dfu->function; + f_dfu->dfu_state = DFU_STATE_dfuIDLE; +} + +static inline void to_runtime_mode(struct f_dfu *f_dfu) +{ + f_dfu->usb_function.strings = NULL; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; +} + +static int handle_upload(struct usb_request *req, u16 len) +{ + struct f_dfu *f_dfu = req->context; + + return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); +} + +static int handle_dnload(struct usb_gadget *gadget, u16 len) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + struct f_dfu *f_dfu = req->context; + + if (len == 0) + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + + req->complete = dnload_request_complete; + + return len; +} + +/*-------------------------------------------------------------------------*/ +/* DFU state machine */ +static int state_app_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_DETACH: + f_dfu->dfu_state = DFU_STATE_appDETACH; + to_dfu_mode(f_dfu); + value = RET_ZLP; + break; + default: + value = RET_STALL; + break; + } + + return value; +} + +static int state_app_detach(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_appIDLE; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + if (len == 0) { + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_UPLOAD: + f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + f_dfu->blk_seq_num = 0; + value = handle_upload(req, len); + break; + case USB_REQ_DFU_ABORT: + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_DETACH: + /* + * Proprietary extension: 'detach' from idle mode and + * get back to runtime mode in case of USB Reset. As + * much as I dislike this, we just can't use every USB + * bus reset to switch back to runtime mode, since at + * least the Linux USB stack likes to send a number of + * resets in a row :( + */ + f_dfu->dfu_state = + DFU_STATE_dfuMANIFEST_WAIT_RST; + to_runtime_mode(f_dfu); + f_dfu->dfu_state = DFU_STATE_appIDLE; + + dfu_trigger_reset(); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnbusy(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_dnload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: + f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + f_dfu->blk_seq_num = w_value; + value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_manifest_sync(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* We're MainfestationTolerant */ + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST; + handle_getstatus(req); + f_dfu->blk_seq_num = 0; + value = RET_STAT_LEN; + req->complete = dnload_request_flush; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_manifest(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* We're MainfestationTolerant */ + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + handle_getstatus(req); + f_dfu->blk_seq_num = 0; + value = RET_STAT_LEN; + puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n"); + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + return value; +} + +static int state_dfu_upload_idle(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_UPLOAD: + /* state transition if less data then requested */ + f_dfu->blk_seq_num = w_value; + value = handle_upload(req, len); + if (value >= 0 && value < len) + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + value = RET_ZLP; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static int state_dfu_error(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(req); + value = RET_STAT_LEN; + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + case USB_REQ_DFU_CLRSTATUS: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + /* no zlp? */ + value = RET_ZLP; + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + + return value; +} + +static dfu_state_fn dfu_state[] = { + state_app_idle, /* DFU_STATE_appIDLE */ + state_app_detach, /* DFU_STATE_appDETACH */ + state_dfu_idle, /* DFU_STATE_dfuIDLE */ + state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */ + state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */ + state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */ + state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */ + state_dfu_manifest, /* DFU_STATE_dfuMANIFEST */ + NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */ + state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */ + state_dfu_error /* DFU_STATE_dfuERROR */ +}; + +static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_gadget *gadget = f->config->cdev->gadget; + struct usb_request *req = f->config->cdev->req; + struct f_dfu *f_dfu = f->config->cdev->req->context; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + int value = 0; + u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; + + debug("w_value: 0x%x len: 0x%x\n", w_value, len); + debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n", + req_type, ctrl->bRequest, f_dfu->dfu_state); + + if (req_type == USB_TYPE_STANDARD) { + if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && + (w_value >> 8) == DFU_DT_FUNC) { + value = min(len, (u16) sizeof(dfu_func)); + memcpy(req->buf, &dfu_func, value); + } + } else /* DFU specific request */ + value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req); + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + } + } + + return value; +} + +/*-------------------------------------------------------------------------*/ + +static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{ + struct dfu_entity *de = NULL; + int i = 0; + + f_dfu->strings = calloc(sizeof(struct usb_string), n + 1); + if (!f_dfu->strings) + goto enomem; + + for (i = 0; i < n; ++i) { + de = dfu_get_entity(i); + f_dfu->strings[i].s = de->name; + } + + f_dfu->strings[i].id = 0; + f_dfu->strings[i].s = NULL; + + return 0; + +enomem: + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + + return -ENOMEM; +} + +static int dfu_prepare_function(struct f_dfu *f_dfu, int n) +{ + struct usb_interface_descriptor *d; + int i = 0; + + f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n + 1); + if (!f_dfu->function) + goto enomem; + + for (i = 0; i < n; ++i) { + d = calloc(sizeof(*d), 1); + if (!d) + goto enomem; + + d->bLength = sizeof(*d); + d->bDescriptorType = USB_DT_INTERFACE; + d->bAlternateSetting = i; + d->bNumEndpoints = 0; + d->bInterfaceClass = USB_CLASS_APP_SPEC; + d->bInterfaceSubClass = 1; + d->bInterfaceProtocol = 2; + + f_dfu->function[i] = (struct usb_descriptor_header *)d; + } + f_dfu->function[i] = NULL; + + return 0; + +enomem: + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + + return -ENOMEM; +} + +static int dfu_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int rv, id, i; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + dfu_intf_runtime.bInterfaceNumber = id; + + f_dfu->dfu_state = DFU_STATE_appIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; + + rv = dfu_prepare_function(f_dfu, alt_num); + if (rv) + goto error; + + rv = dfu_prepare_strings(f_dfu, alt_num); + if (rv) + goto error; + for (i = 0; i < alt_num; i++) { + id = usb_string_id(cdev); + if (id < 0) + return id; + f_dfu->strings[i].id = id; + ((struct usb_interface_descriptor *)f_dfu->function[i]) + ->iInterface = id; + } + + to_dfu_mode(f_dfu); + + stringtab_dfu.strings = f_dfu->strings; + + cdev->req->context = f_dfu; + +error: + return rv; +} + +static void dfu_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + int alt_num = dfu_get_alt_number(); + int i; + + if (f_dfu->strings) { + i = alt_num; + while (i) + f_dfu->strings[--i].s = NULL; + + free(f_dfu->strings); + } + + if (f_dfu->function) { + i = alt_num; + while (i) { + free(f_dfu->function[--i]); + f_dfu->function[i] = NULL; + } + free(f_dfu->function); + } + + free(f_dfu); +} + +static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + + debug("%s: intf:%d alt:%d\n", __func__, intf, alt); + + f_dfu->altsetting = alt; + + return 0; +} + +/* TODO: is this really what we need here? */ +static void dfu_disable(struct usb_function *f) +{ + struct f_dfu *f_dfu = func_to_dfu(f); + if (f_dfu->config == 0) + return; + + debug("%s: reset config\n", __func__); + + f_dfu->config = 0; +} + +static int dfu_bind_config(struct usb_configuration *c) +{ + struct f_dfu *f_dfu; + int status; + + f_dfu = calloc(sizeof(*f_dfu), 1); + if (!f_dfu) + return -ENOMEM; + f_dfu->usb_function.name = "dfu"; + f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; + f_dfu->usb_function.bind = dfu_bind; + f_dfu->usb_function.unbind = dfu_unbind; + f_dfu->usb_function.set_alt = dfu_set_alt; + f_dfu->usb_function.disable = dfu_disable; + f_dfu->usb_function.strings = dfu_generic_strings; + f_dfu->usb_function.setup = dfu_handle; + f_dfu->poll_timeout = DFU_DEFAULT_POLL_TIMEOUT; + + status = usb_add_function(c, &f_dfu->usb_function); + if (status) + free(f_dfu); + + return status; +} + +int dfu_add(struct usb_configuration *c) +{ + int id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + strings_dfu_generic[0].id = id; + dfu_intf_runtime.iInterface = id; + + debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__, + c->cdev, c->cdev->gadget, c->cdev->gadget->ep0); + + return dfu_bind_config(c); +} + +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_dfu, dfu_add); diff --git a/qemu/roms/u-boot/drivers/usb/gadget/f_dfu.h b/qemu/roms/u-boot/drivers/usb/gadget/f_dfu.h new file mode 100644 index 000000000..0c29954ad --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/f_dfu.h @@ -0,0 +1,87 @@ +/* + * f_dfu.h -- Device Firmware Update gadget + * + * Copyright (C) 2011-2012 Samsung Electronics + * author: Andrzej Pietrasiewicz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __F_DFU_H_ +#define __F_DFU_H_ + +#include +#include + +#define DFU_CONFIG_VAL 1 +#define DFU_DT_FUNC 0x21 + +#define DFU_BIT_WILL_DETACH (0x1 << 3) +#define DFU_BIT_MANIFESTATION_TOLERANT (0x1 << 2) +#define DFU_BIT_CAN_UPLOAD (0x1 << 1) +#define DFU_BIT_CAN_DNLOAD 0x1 + +/* big enough to hold our biggest descriptor */ +#define DFU_USB_BUFSIZ 4096 + +#define USB_REQ_DFU_DETACH 0x00 +#define USB_REQ_DFU_DNLOAD 0x01 +#define USB_REQ_DFU_UPLOAD 0x02 +#define USB_REQ_DFU_GETSTATUS 0x03 +#define USB_REQ_DFU_CLRSTATUS 0x04 +#define USB_REQ_DFU_GETSTATE 0x05 +#define USB_REQ_DFU_ABORT 0x06 + +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_errTARGET 0x01 +#define DFU_STATUS_errFILE 0x02 +#define DFU_STATUS_errWRITE 0x03 +#define DFU_STATUS_errERASE 0x04 +#define DFU_STATUS_errCHECK_ERASED 0x05 +#define DFU_STATUS_errPROG 0x06 +#define DFU_STATUS_errVERIFY 0x07 +#define DFU_STATUS_errADDRESS 0x08 +#define DFU_STATUS_errNOTDONE 0x09 +#define DFU_STATUS_errFIRMWARE 0x0a +#define DFU_STATUS_errVENDOR 0x0b +#define DFU_STATUS_errUSBR 0x0c +#define DFU_STATUS_errPOR 0x0d +#define DFU_STATUS_errUNKNOWN 0x0e +#define DFU_STATUS_errSTALLEDPKT 0x0f + +#define RET_STALL -1 +#define RET_ZLP 0 +#define RET_STAT_LEN 6 + +enum dfu_state { + DFU_STATE_appIDLE = 0, + DFU_STATE_appDETACH = 1, + DFU_STATE_dfuIDLE = 2, + DFU_STATE_dfuDNLOAD_SYNC = 3, + DFU_STATE_dfuDNBUSY = 4, + DFU_STATE_dfuDNLOAD_IDLE = 5, + DFU_STATE_dfuMANIFEST_SYNC = 6, + DFU_STATE_dfuMANIFEST = 7, + DFU_STATE_dfuMANIFEST_WAIT_RST = 8, + DFU_STATE_dfuUPLOAD_IDLE = 9, + DFU_STATE_dfuERROR = 10, +}; + +struct dfu_status { + __u8 bStatus; + __u8 bwPollTimeout[3]; + __u8 bState; + __u8 iString; +} __packed; + +struct dfu_function_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bmAttributes; + __le16 wDetachTimeOut; + __le16 wTransferSize; + __le16 bcdDFUVersion; +} __packed; + +#define DFU_POLL_TIMEOUT_MASK (0xFFFFFFUL) +#endif /* __F_DFU_H_ */ diff --git a/qemu/roms/u-boot/drivers/usb/gadget/f_mass_storage.c b/qemu/roms/u-boot/drivers/usb/gadget/f_mass_storage.c new file mode 100644 index 000000000..6374bb953 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/f_mass_storage.c @@ -0,0 +1,2783 @@ +/* + * f_mass_storage.c -- Mass Storage USB Composite Function + * + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause + */ + +/* + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. + * + * Function supports multiple logical units (LUNs). Backing storage + * for each LUN is provided by a regular file or a block device. + * Access for each LUN can be limited to read-only. Moreover, the + * function can indicate that LUN is removable and/or CD-ROM. (The + * later implies read-only access.) + * + * MSF is configured by specifying a fsg_config structure. It has the + * following fields: + * + * nluns Number of LUNs function have (anywhere from 1 + * to FSG_MAX_LUNS which is 8). + * luns An array of LUN configuration values. This + * should be filled for each LUN that + * function will include (ie. for "nluns" + * LUNs). Each element of the array has + * the following fields: + * ->filename The path to the backing file for the LUN. + * Required if LUN is not marked as + * removable. + * ->ro Flag specifying access to the LUN shall be + * read-only. This is implied if CD-ROM + * emulation is enabled as well as when + * it was impossible to open "filename" + * in R/W mode. + * ->removable Flag specifying that LUN shall be indicated as + * being removable. + * ->cdrom Flag specifying that LUN shall be reported as + * being a CD-ROM. + * + * lun_name_format A printf-like format for names of the LUN + * devices. This determines how the + * directory in sysfs will be named. + * Unless you are using several MSFs in + * a single gadget (as opposed to single + * MSF in many configurations) you may + * leave it as NULL (in which case + * "lun%d" will be used). In the format + * you can use "%d" to index LUNs for + * MSF's with more than one LUN. (Beware + * that there is only one integer given + * as an argument for the format and + * specifying invalid format may cause + * unspecified behaviour.) + * thread_name Name of the kernel thread process used by the + * MSF. You can safely set it to NULL + * (in which case default "file-storage" + * will be used). + * + * vendor_name + * product_name + * release Information used as a reply to INQUIRY + * request. To use default set to NULL, + * NULL, 0xffff respectively. The first + * field should be 8 and the second 16 + * characters or less. + * + * can_stall Set to permit function to halt bulk endpoints. + * Disabled on some USB devices known not + * to work correctly. You should set it + * to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified. If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error). The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN. Note also that the CD-ROM block length is + * set to 512 rather than the more common value 2048. + * + * + * MSF includes support for module parameters. If gadget using it + * decides to use it, the following module parameters will be + * available: + * + * file=filename[,filename...] + * Names of the files or block devices used for + * backing storage. + * ro=b[,b...] Default false, boolean for read-only access. + * removable=b[,b...] + * Default true, boolean for removable media. + * cdrom=b[,b...] Default false, boolean for whether to emulate + * a CD-ROM drive. + * luns=N Default N = number of filenames, number of + * LUNs to support. + * stall Default determined according to the type of + * USB device controller (usually true), + * boolean to permit the driver to halt + * bulk endpoints. + * + * The module parameters may be prefixed with some string. You need + * to consult gadget's documentation or source to verify whether it is + * using those module parameters and if it does what are the prefixes + * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is + * the prefix). + * + * + * Requirements are modest; only a bulk-in and a bulk-out endpoint are + * needed. The memory requirement amounts to two 16K buffers, size + * configurable by a parameter. Support is included for both + * full-speed and high-speed operation. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and + * interrupt-in endpoints. With most device controllers this isn't an + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * + * + * The pathnames of the backing files and the ro settings are + * available in the attribute files "file" and "ro" in the lun (or + * to be more precise in a directory which name comes from + * "lun_name_format" option!) subdirectory of the gadget's sysfs + * directory. If the "removable" option is set, writing to these + * files will simulate ejecting/loading the medium (writing an empty + * line means eject) and adjusting a write-enable tab. Changes to the + * ro setting are not allowed when the medium is loaded or if CD-ROM + * emulation is being used. + * + * When a LUN receive an "eject" SCSI request (Start/Stop Unit), + * if the LUN is removable, the backing file is released to simulate + * ejection. + * + * + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell. The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at . + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at + * . + */ + +/* + * Driver Design + * + * The MSF is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls. Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction. It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file. This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example. To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind(). But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead. At of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd). In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering). But it helps to think of the + * pipeline as being a long one. Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol. There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete. Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows. This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint. However, under certain circumstances the + * Bulk-only specification requires a stall. In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset. Hopefully this + * will permit everything to work correctly. Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests. Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time. During that delay the host might give up on + * the original ep0 request and issue a new one. When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it. So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change). When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag. Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long. It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/*------------------------------------------------------------------------*/ + +#define FSG_DRIVER_DESC "Mass Storage Function" +#define FSG_DRIVER_VERSION "2012/06/5" + +static const char fsg_string_interface[] = "Mass Storage"; + +#define FSG_NO_INTR_EP 1 +#define FSG_NO_DEVICE_STRINGS 1 +#define FSG_NO_OTG 1 +#define FSG_NO_INTR_EP 1 + +#include "storage_common.c" + +/*-------------------------------------------------------------------------*/ + +#define GFP_ATOMIC ((gfp_t) 0) +#define PAGE_CACHE_SHIFT 12 +#define PAGE_CACHE_SIZE (1 << PAGE_CACHE_SHIFT) +#define kthread_create(...) __builtin_return_address(0) +#define wait_for_completion(...) do {} while (0) + +struct kref {int x; }; +struct completion {int x; }; + +inline void set_bit(int nr, volatile void *addr) +{ + int mask; + unsigned int *a = (unsigned int *) addr; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + *a |= mask; +} + +inline void clear_bit(int nr, volatile void *addr) +{ + int mask; + unsigned int *a = (unsigned int *) addr; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + *a &= ~mask; +} + +struct fsg_dev; +struct fsg_common; + +/* Data shared by all the FSG instances. */ +struct fsg_common { + struct usb_gadget *gadget; + struct fsg_dev *fsg, *new_fsg; + + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ + unsigned int ep0_req_tag; + + struct fsg_buffhd *next_buffhd_to_fill; + struct fsg_buffhd *next_buffhd_to_drain; + struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + + unsigned int nluns; + unsigned int lun; + struct fsg_lun luns[FSG_MAX_LUNS]; + + unsigned int bulk_out_maxpacket; + enum fsg_state state; /* For exception handling */ + unsigned int exception_req_tag; + + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + u32 residue; + u32 usb_amount_left; + + unsigned int can_stall:1; + unsigned int free_storage_on_release:1; + unsigned int phase_error:1; + unsigned int short_packet_received:1; + unsigned int bad_lun_okay:1; + unsigned int running:1; + + int thread_wakeup_needed; + struct completion thread_notifier; + struct task_struct *thread_task; + + /* Callback functions. */ + const struct fsg_operations *ops; + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + u16 release; + + /* Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte */ + char inquiry_string[8 + 16 + 4 + 1]; + + struct kref ref; +}; + +struct fsg_config { + unsigned nluns; + struct fsg_lun_config { + const char *filename; + char ro; + char removable; + char cdrom; + char nofua; + } luns[FSG_MAX_LUNS]; + + /* Callback functions. */ + const struct fsg_operations *ops; + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + + char can_stall; +}; + +struct fsg_dev { + struct usb_function function; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + + u16 interface_number; + + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; + + unsigned long atomic_bitflags; +#define IGNORE_BULK_OUT 0 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; +}; + + +static inline int __fsg_is_set(struct fsg_common *common, + const char *func, unsigned line) +{ + if (common->fsg) + return 1; + ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); + WARN_ON(1); + return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ + return container_of(f, struct fsg_dev, function); +} + + +typedef void (*fsg_routine_t)(struct fsg_dev *); + +static int exception_in_progress(struct fsg_common *common) +{ + return common->state > FSG_STATE_IDLE; +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_common *common, + struct fsg_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % common->bulk_out_maxpacket; + if (rem > 0) + length += common->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + +/*-------------------------------------------------------------------------*/ + +struct ums *ums; +struct fsg_common *the_fsg_common; + +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +{ + const char *name; + + if (ep == fsg->bulk_in) + name = "bulk-in"; + else if (ep == fsg->bulk_out) + name = "bulk-out"; + else + name = ep->name; + DBG(fsg, "%s set halt\n", name); + return usb_ep_set_halt(ep); +} + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +/* Caller must hold fsg->lock */ +static void wakeup_thread(struct fsg_common *common) +{ + common->thread_wakeup_needed = 1; +} + +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +{ + /* Do nothing if a higher-priority exception is already in progress. + * If a lower-or-equal priority exception is in progress, preempt it + * and notify the main thread by sending it a signal. */ + if (common->state <= new_state) { + common->exception_req_tag = common->ep0_req_tag; + common->state = new_state; + common->thread_wakeup_needed = 1; + } +} + +/*-------------------------------------------------------------------------*/ + +static int ep0_queue(struct fsg_common *common) +{ + int rc; + + rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC); + common->ep0->driver_data = common; + if (rc != 0 && rc != -ESHUTDOWN) { + /* We can't do much more than wait for a reset */ + WARNING(common, "error in submission: %s --> %d\n", + common->ep0->name, rc); + } + return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* Bulk and interrupt endpoint completion handlers. + * These always run in_irq. */ + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + bh->inreq_busy = 0; + bh->state = BUF_STATE_EMPTY; + wakeup_thread(common); +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + dump_msg(common, "bulk-out", req->buf, req->actual); + if (req->status || req->actual != bh->bulk_out_intended_length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, + bh->bulk_out_intended_length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + bh->outreq_busy = 0; + bh->state = BUF_STATE_FULL; + wakeup_thread(common); +} + +/*-------------------------------------------------------------------------*/ + +/* Ep0 class-specific handlers. These always run in_irq. */ + +static int fsg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_request *req = fsg->common->ep0req; + u16 w_index = get_unaligned_le16(&ctrl->wIndex); + u16 w_value = get_unaligned_le16(&ctrl->wValue); + u16 w_length = get_unaligned_le16(&ctrl->wLength); + + if (!fsg_is_set(fsg->common)) + return -EOPNOTSUPP; + + switch (ctrl->bRequest) { + + case USB_BULK_RESET_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DBG(fsg, "bulk reset request\n"); + raise_exception(fsg->common, FSG_STATE_RESET); + return DELAYED_STATUS; + + case USB_BULK_GET_MAX_LUN_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + VDBG(fsg, "get max LUN\n"); + *(u8 *) req->buf = fsg->common->nluns - 1; + + /* Respond with data/status */ + req->length = min((u16)1, w_length); + return ep0_queue(fsg->common); + } + + VDBG(fsg, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + get_unaligned_le16(&ctrl->wValue), w_index, w_length); + return -EOPNOTSUPP; +} + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + +/* Use this for bulk or interrupt transfers, not ep0 */ +static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request *req, int *pbusy, + enum fsg_buffer_state *state) +{ + int rc; + + if (ep == fsg->bulk_in) + dump_msg(fsg, "bulk-in", req->buf, req->length); + + *pbusy = 1; + *state = BUF_STATE_BUSY; + rc = usb_ep_queue(ep, req, GFP_KERNEL); + if (rc != 0) { + *pbusy = 0; + *state = BUF_STATE_EMPTY; + + /* We can't do much more than wait for a reset */ + + /* Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. */ + if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && + req->length == 0)) + WARNING(fsg, "error in submission: %s --> %d\n", + ep->name, rc); + } +} + +#define START_TRANSFER_OR(common, ep_name, req, pbusy, state) \ + if (fsg_is_set(common)) \ + start_transfer((common)->fsg, (common)->fsg->ep_name, \ + req, pbusy, state); \ + else + +#define START_TRANSFER(common, ep_name, req, pbusy, state) \ + START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0 + +static void busy_indicator(void) +{ + static int state; + + switch (state) { + case 0: + puts("\r|"); break; + case 1: + puts("\r/"); break; + case 2: + puts("\r-"); break; + case 3: + puts("\r\\"); break; + case 4: + puts("\r|"); break; + case 5: + puts("\r/"); break; + case 6: + puts("\r-"); break; + case 7: + puts("\r\\"); break; + default: + state = 0; + } + if (state++ == 8) + state = 0; +} + +static int sleep_thread(struct fsg_common *common) +{ + int rc = 0; + int i = 0, k = 0; + + /* Wait until a signal arrives or we are woken up */ + for (;;) { + if (common->thread_wakeup_needed) + break; + + if (++i == 50000) { + busy_indicator(); + i = 0; + k++; + } + + if (k == 10) { + /* Handle CTRL+C */ + if (ctrlc()) + return -EPIPE; + + /* Check cable connection */ + if (!g_dnl_board_usb_cable_connected()) + return -EIO; + + k = 0; + } + + usb_gadget_handle_interrupts(); + } + common->thread_wakeup_needed = 0; + return rc; +} + +/*-------------------------------------------------------------------------*/ + +static int do_read(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba; + struct fsg_buffhd *bh; + int rc; + u32 amount_left; + loff_t file_offset; + unsigned int amount; + unsigned int partial_page; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (common->cmnd[0] == SC_READ_6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = don't read from the + * cache), but we don't implement them. */ + if ((common->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + file_offset = ((loff_t) lba) << 9; + + /* Carry out the file reads */ + amount_left = common->data_size_from_cmnd; + if (unlikely(amount_left == 0)) + return -EIO; /* No default reply */ + + for (;;) { + + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 then we were asked to read past + * the end of file. */ + amount = min(amount_left, FSG_BUFLEN); + partial_page = file_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - + partial_page); + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + /* If we were asked to read past the end of file, + * end with an empty buffer. */ + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->info_valid = 1; + bh->inreq->length = 0; + bh->state = BUF_STATE_FULL; + break; + } + + /* Perform the read */ + rc = ums->read_sector(ums, + file_offset / SECTOR_SIZE, + amount / SECTOR_SIZE, + (char __user *)bh->buf); + if (!rc) + return -EIO; + + nread = rc * SECTOR_SIZE; + + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); + + if (nread < 0) { + LDBG(curlun, "error in file read: %d\n", + (int) nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file read: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a block */ + } + file_offset += nread; + amount_left -= nread; + common->residue -= nread; + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + + /* If an error occurred, report it and its position */ + if (nread < amount) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->info_valid = 1; + break; + } + + if (amount_left == 0) + break; /* No more left to read */ + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + } + + return -EIO; /* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_write(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba; + struct fsg_buffhd *bh; + int get_some_more; + u32 amount_left_to_req, amount_left_to_write; + loff_t usb_offset, file_offset; + unsigned int amount; + unsigned int partial_page; + ssize_t nwritten; + int rc; + + if (curlun->ro) { + curlun->sense_data = SS_WRITE_PROTECTED; + return -EINVAL; + } + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (common->cmnd[0] == SC_WRITE_6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = write directly to the + * medium). We don't implement DPO; we implement FUA by + * performing synchronous output. */ + if (common->cmnd[1] & ~0x18) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* Carry out the file writes */ + get_some_more = 1; + file_offset = usb_offset = ((loff_t) lba) << 9; + amount_left_to_req = common->data_size_from_cmnd; + amount_left_to_write = common->data_size_from_cmnd; + + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + /* Figure out how much we want to get: + * Try to get the remaining amount. + * But don't get more than the buffer size. + * And don't try to go past the end of the file. + * If we're not at a page boundary, + * don't go past the next page. + * If this means getting 0, then we were asked + * to write past the end of file. + * Finally, round down to a block boundary. */ + amount = min(amount_left_to_req, FSG_BUFLEN); + partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, + (unsigned int) PAGE_CACHE_SIZE - partial_page); + + if (amount == 0) { + get_some_more = 0; + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->info_valid = 1; + continue; + } + amount -= (amount & 511); + if (amount == 0) { + + /* Why were we were asked to transfer a + * partial block? */ + get_some_more = 0; + continue; + } + + /* Get the next buffer */ + usb_offset += amount; + common->usb_amount_left -= amount; + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ + bh = common->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; /* We stopped early */ + if (bh->state == BUF_STATE_FULL) { + common->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ + if (bh->outreq->status != 0) { + curlun->sense_data = SS_COMMUNICATION_FAILURE; + curlun->info_valid = 1; + break; + } + + amount = bh->outreq->actual; + + /* Perform the write */ + rc = ums->write_sector(ums, + file_offset / SECTOR_SIZE, + amount / SECTOR_SIZE, + (char __user *)bh->buf); + if (!rc) + return -EIO; + nwritten = rc * SECTOR_SIZE; + + VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nwritten); + + if (nwritten < 0) { + LDBG(curlun, "error in file write: %d\n", + (int) nwritten); + nwritten = 0; + } else if (nwritten < amount) { + LDBG(curlun, "partial file write: %d/%u\n", + (int) nwritten, amount); + nwritten -= (nwritten & 511); + /* Round down to a block */ + } + file_offset += nwritten; + amount_left_to_write -= nwritten; + common->residue -= nwritten; + + /* If an error occurred, report it and its position */ + if (nwritten < amount) { + printf("nwritten:%d amount:%d\n", nwritten, + amount); + curlun->sense_data = SS_WRITE_ERROR; + curlun->info_valid = 1; + break; + } + + /* Did the host decide to stop early? */ + if (bh->outreq->actual != bh->outreq->length) { + common->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ + rc = sleep_thread(common); + if (rc) + return rc; + } + + return -EIO; /* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_synchronize_cache(struct fsg_common *common) +{ + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_verify(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba; + u32 verification_length; + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + loff_t file_offset; + u32 amount_left; + unsigned int amount; + ssize_t nread; + int rc; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + lba = get_unaligned_be32(&common->cmnd[2]); + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) but we don't implement it. */ + if (common->cmnd[1] & ~0x10) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + verification_length = get_unaligned_be16(&common->cmnd[7]); + if (unlikely(verification_length == 0)) + return -EIO; /* No default reply */ + + /* Prepare to carry out the file verify */ + amount_left = verification_length << 9; + file_offset = ((loff_t) lba) << 9; + + /* Write out all the dirty buffers before invalidating them */ + + /* Just try to read the requested blocks */ + while (amount_left > 0) { + + /* Figure out how much we need to read: + * Try to read the remaining amount, but not more than + * the buffer size. + * And don't try to read past the end of the file. + * If this means reading 0 then we were asked to read + * past the end of file. */ + amount = min(amount_left, FSG_BUFLEN); + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->info_valid = 1; + break; + } + + /* Perform the read */ + rc = ums->read_sector(ums, + file_offset / SECTOR_SIZE, + amount / SECTOR_SIZE, + (char __user *)bh->buf); + if (!rc) + return -EIO; + nread = rc * SECTOR_SIZE; + + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); + if (nread < 0) { + LDBG(curlun, "error in file verify: %d\n", + (int) nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file verify: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a sector */ + } + if (nread == 0) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->info_valid = 1; + break; + } + file_offset += nread; + amount_left -= nread; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + static const char vendor_id[] = "Linux "; + u8 *buf = (u8 *) bh->buf; + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[4] = 31; /* Additional length */ + return 36; + } + + memset(buf, 0, 8); + buf[0] = TYPE_DISK; + buf[2] = 2; /* ANSI SCSI level 2 */ + buf[3] = 2; /* SCSI-2 INQUIRY data format */ + buf[4] = 31; /* Additional length */ + /* No special options */ + sprintf((char *) (buf + 8), "%-8s%-16s%04x", (char*) vendor_id , + ums->name, (u16) 0xffff); + + return 36; +} + + +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u8 *buf = (u8 *) bh->buf; + u32 sd, sdinfo; + int valid; + + /* + * From the SCSI-2 spec., section 7.9 (Unit attention condition): + * + * If a REQUEST SENSE command is received from an initiator + * with a pending unit attention condition (before the target + * generates the contingent allegiance condition), then the + * target shall either: + * a) report any pending sense data and preserve the unit + * attention condition on the logical unit, or, + * b) report the unit attention condition, may discard any + * pending sense data, and clear the unit attention + * condition on the logical unit for that initiator. + * + * FSG normally uses option a); enable this code to use option b). + */ +#if 0 + if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + } +#endif + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + sdinfo = 0; + valid = 0; + } else { + sd = curlun->sense_data; + valid = curlun->info_valid << 7; + curlun->sense_data = SS_NO_SENSE; + curlun->info_valid = 0; + } + + memset(buf, 0, 18); + buf[0] = valid | 0x70; /* Valid, current error */ + buf[2] = SK(sd); + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ + buf[7] = 18 - 8; /* Additional sense length */ + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return 18; +} + +static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + int pmi = common->cmnd[8]; + u8 *buf = (u8 *) bh->buf; + + /* Check the PMI and LBA fields */ + if (pmi > 1 || (pmi == 0 && lba != 0)) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); + /* Max logical block */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + return 8; +} + +static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int msf = common->cmnd[1] & 0x02; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + u8 *buf = (u8 *) bh->buf; + + if (common->cmnd[1] & ~0x02) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + memset(buf, 0, 8); + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ + store_cdrom_address(&buf[4], msf, lba); + return 8; +} + + +static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int msf = common->cmnd[1] & 0x02; + int start_track = common->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + + if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + + return 20; +} + +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int mscmnd = common->cmnd[0]; + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; + int changeable_values, all_pages; + int valid_page = 0; + int len, limit; + + if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + pc = common->cmnd[2] >> 6; + page_code = common->cmnd[2] & 0x3f; + if (pc == 3) { + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. */ + memset(buf, 0, 8); + if (mscmnd == SC_MODE_SENSE_6) { + buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 4; + limit = 255; + } else { /* SC_MODE_SENSE_10 */ + buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 8; + limit = 65535; /* Should really be FSG_BUFLEN */ + } + + /* No block descriptors */ + + /* The mode pages, in numerical order. The only page we support + * is the Caching page. */ + if (page_code == 0x08 || all_pages) { + valid_page = 1; + buf[0] = 0x08; /* Page code */ + buf[1] = 10; /* Page length */ + memset(buf+2, 0, 10); /* None of the fields are changeable */ + + if (!changeable_values) { + buf[2] = 0x04; /* Write cache enable, */ + /* Read cache not disabled */ + /* No cache retention priorities */ + put_unaligned_be16(0xffff, &buf[4]); + /* Don't disable prefetch */ + /* Minimum prefetch = 0 */ + put_unaligned_be16(0xffff, &buf[8]); + /* Maximum prefetch */ + put_unaligned_be16(0xffff, &buf[10]); + /* Maximum prefetch ceiling */ + } + buf += 12; + } + + /* Check that a valid page was requested and the mode data length + * isn't too long. */ + len = buf - buf0; + if (!valid_page || len > limit) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + /* Store the mode data length */ + if (mscmnd == SC_MODE_SENSE_6) + buf0[0] = len - 1; + else + put_unaligned_be16(len - 2, buf0); + return len; +} + + +static int do_start_stop(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + + if (!curlun) { + return -EINVAL; + } else if (!curlun->removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + return 0; +} + +static int do_prevent_allow(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int prevent; + + if (!curlun->removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + prevent = common->cmnd[4] & 0x01; + if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (curlun->prevent_medium_removal && !prevent) + fsg_lun_fsync_sub(curlun); + curlun->prevent_medium_removal = prevent; + return 0; +} + + +static int do_read_format_capacities(struct fsg_common *common, + struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u8 *buf = (u8 *) bh->buf; + + buf[0] = buf[1] = buf[2] = 0; + buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */ + buf += 4; + + put_unaligned_be32(curlun->num_sectors, &buf[0]); + /* Number of blocks */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + buf[4] = 0x02; /* Current capacity */ + return 12; +} + + +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + + /* We don't support MODE SELECT */ + if (curlun) + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + rc = fsg_set_halt(fsg, fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } + + rc = usb_ep_set_halt(fsg->bulk_in); + } + return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + DBG(fsg, "bulk-in set wedge\n"); + rc = 0; /* usb_ep_set_wedge(fsg->bulk_in); */ + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } + } + return rc; +} + +static int pad_with_zeros(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; + u32 nkeep = bh->inreq->length; + u32 nsend; + int rc; + + bh->state = BUF_STATE_EMPTY; /* For the first iteration */ + fsg->common->usb_amount_left = nkeep + fsg->common->residue; + while (fsg->common->usb_amount_left > 0) { + + /* Wait for the next buffer to be free */ + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg->common); + if (rc) + return rc; + } + + nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); + memset(bh->buf + nkeep, 0, nsend - nkeep); + bh->inreq->length = nsend; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + bh = fsg->common->next_buffhd_to_fill = bh->next; + fsg->common->usb_amount_left -= nsend; + nkeep = 0; + } + return 0; +} + +static int throw_away_data(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + u32 amount; + int rc; + + for (bh = common->next_buffhd_to_drain; + bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; + bh = common->next_buffhd_to_drain) { + + /* Throw away the data in a filled buffer */ + if (bh->state == BUF_STATE_FULL) { + bh->state = BUF_STATE_EMPTY; + common->next_buffhd_to_drain = bh->next; + + /* A short packet or an error ends everything */ + if (bh->outreq->actual != bh->outreq->length || + bh->outreq->status != 0) { + raise_exception(common, + FSG_STATE_ABORT_BULK_OUT); + return -EINTR; + } + continue; + } + + /* Try to submit another request if we need one */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY + && common->usb_amount_left > 0) { + amount = min(common->usb_amount_left, FSG_BUFLEN); + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + common->usb_amount_left -= amount; + continue; + } + + /* Otherwise wait for something to happen */ + rc = sleep_thread(common); + if (rc) + return rc; + } + return 0; +} + + +static int finish_reply(struct fsg_common *common) +{ + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + int rc = 0; + + switch (common->data_dir) { + case DATA_DIR_NONE: + break; /* Nothing to send */ + + /* If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. */ + case DATA_DIR_UNKNOWN: + if (!common->can_stall) { + /* Nothing */ + } else if (fsg_is_set(common)) { + fsg_set_halt(common->fsg, common->fsg->bulk_out); + rc = halt_bulk_in_endpoint(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* All but the last buffer of data must have already been sent */ + case DATA_DIR_TO_HOST: + if (common->data_size == 0) { + /* Nothing to send */ + + /* If there's no residue, simply send the last buffer */ + } else if (common->residue == 0) { + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + return -EIO; + common->next_buffhd_to_fill = bh->next; + + /* For Bulk-only, if we're allowed to stall then send the + * short packet and halt the bulk-in endpoint. If we can't + * stall, pad out the remaining data with 0's. */ + } else if (common->can_stall) { + bh->inreq->zero = 1; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + rc = -EIO; + common->next_buffhd_to_fill = bh->next; + if (common->fsg) + rc = halt_bulk_in_endpoint(common->fsg); + } else if (fsg_is_set(common)) { + rc = pad_with_zeros(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. */ + case DATA_DIR_FROM_HOST: + if (common->residue == 0) { + /* Nothing to receive */ + + /* Did the host stop sending unexpectedly early? */ + } else if (common->short_packet_received) { + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; + + /* We haven't processed all the incoming data. Even though + * we may be allowed to stall, doing so would cause a race. + * The controller may already have ACK'ed all the remaining + * bulk-out packets, in which case the host wouldn't see a + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. */ +#if 0 + } else if (common->can_stall) { + if (fsg_is_set(common)) + fsg_set_halt(common->fsg, + common->fsg->bulk_out); + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; +#endif + + /* We can't stall. Read in the excess data and throw it + * all away. */ + } else { + rc = throw_away_data(common); + } + break; + } + return rc; +} + + +static int send_status(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + struct fsg_buffhd *bh; + struct bulk_cs_wrap *csw; + int rc; + u8 status = USB_STATUS_PASS; + u32 sd, sdinfo = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + if (curlun) + sd = curlun->sense_data; + else if (common->bad_lun_okay) + sd = SS_NO_SENSE; + else + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + + if (common->phase_error) { + DBG(common, "sending phase-error status\n"); + status = USB_STATUS_PHASE_ERROR; + sd = SS_INVALID_COMMAND; + } else if (sd != SS_NO_SENSE) { + DBG(common, "sending command-failure status\n"); + status = USB_STATUS_FAIL; + VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + " info x%x\n", + SK(sd), ASC(sd), ASCQ(sd), sdinfo); + } + + /* Store and send the Bulk-only CSW */ + csw = (void *)bh->buf; + + csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); + csw->Tag = common->tag; + csw->Residue = cpu_to_le32(common->residue); + csw->Status = status; + + bh->inreq->length = USB_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + common->next_buffhd_to_fill = bh->next; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +static int check_command(struct fsg_common *common, int cmnd_size, + enum data_direction data_dir, unsigned int mask, + int needs_medium, const char *name) +{ + int i; + int lun = common->cmnd[1] >> 5; + static const char dirletter[4] = {'u', 'o', 'i', 'n'}; + char hdlen[20]; + struct fsg_lun *curlun; + + hdlen[0] = 0; + if (common->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], + common->data_size); + VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], + common->data_size_from_cmnd, common->cmnd_size, hdlen); + + /* We can't reply at all until we know the correct data direction + * and size. */ + if (common->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; + if (common->data_size < common->data_size_from_cmnd) { + /* Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much as + * we are allowed. */ + common->data_size_from_cmnd = common->data_size; + common->phase_error = 1; + } + common->residue = common->data_size; + common->usb_amount_left = common->data_size; + + /* Conflicting data directions is a phase error */ + if (common->data_dir != data_dir + && common->data_size_from_cmnd > 0) { + common->phase_error = 1; + return -EINVAL; + } + + /* Verify the length of the command itself */ + if (cmnd_size != common->cmnd_size) { + + /* Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length + * field passing a wrong command size. For those cases we + * always try to work around the problem by using the length + * sent by the host side provided it is at least as large + * as the correct command length. + * Examples of such cases would be MS-Windows, which issues + * REQUEST SENSE with cbw->Length == 12 where it should + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ + if (cmnd_size <= common->cmnd_size) { + DBG(common, "%s is buggy! Expected length %d " + "but we got %d\n", name, + cmnd_size, common->cmnd_size); + cmnd_size = common->cmnd_size; + } else { + common->phase_error = 1; + return -EINVAL; + } + } + + /* Check that the LUN values are consistent */ + if (common->lun != lun) + DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", + common->lun, lun); + + /* Check the LUN */ + if (common->lun >= 0 && common->lun < common->nluns) { + curlun = &common->luns[common->lun]; + if (common->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->info_valid = 0; + } + } else { + curlun = NULL; + common->bad_lun_okay = 0; + + /* INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. */ + if (common->cmnd[0] != SC_INQUIRY && + common->cmnd[0] != SC_REQUEST_SENSE) { + DBG(common, "unsupported LUN %d\n", common->lun); + return -EINVAL; + } + } +#if 0 + /* If a unit attention condition exists, only INQUIRY and + * REQUEST SENSE commands are allowed; anything else must fail. */ + if (curlun && curlun->unit_attention_data != SS_NO_SENSE && + common->cmnd[0] != SC_INQUIRY && + common->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + return -EINVAL; + } +#endif + /* Check that only command bytes listed in the mask are non-zero */ + common->cmnd[1] &= 0x1f; /* Mask away the LUN */ + for (i = 1; i < cmnd_size; ++i) { + if (common->cmnd[i] && !(mask & (1 << i))) { + if (curlun) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + + return 0; +} + + +static int do_scsi_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc; + int reply = -EINVAL; + int i; + static char unknown[16]; + struct fsg_lun *curlun = &common->luns[common->lun]; + + dump_cdb(common); + + /* Wait for the next buffer to become available for data or status */ + bh = common->next_buffhd_to_fill; + common->next_buffhd_to_drain = bh; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + common->phase_error = 0; + common->short_packet_received = 0; + + down_read(&common->filesem); /* We're using the backing file */ + switch (common->cmnd[0]) { + + case SC_INQUIRY: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) + reply = do_inquiry(common, bh); + break; + + case SC_MODE_SELECT_6: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case SC_MODE_SELECT_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case SC_MODE_SENSE_6: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case SC_MODE_SENSE_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) + reply = do_prevent_allow(common); + break; + + case SC_READ_6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)"); + if (reply == 0) + reply = do_read(common); + break; + + case SC_READ_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)"); + if (reply == 0) + reply = do_read(common); + break; + + case SC_READ_12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)"); + if (reply == 0) + reply = do_read(common); + break; + + case SC_READ_CAPACITY: + common->data_size_from_cmnd = 8; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY"); + if (reply == 0) + reply = do_read_capacity(common, bh); + break; + + case SC_READ_HEADER: + if (!common->luns[common->lun].cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER"); + if (reply == 0) + reply = do_read_header(common, bh); + break; + + case SC_READ_TOC: + if (!common->luns[common->lun].cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC"); + if (reply == 0) + reply = do_read_toc(common, bh); + break; + + case SC_READ_FORMAT_CAPACITIES: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES"); + if (reply == 0) + reply = do_read_format_capacities(common, bh); + break; + + case SC_REQUEST_SENSE: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) + reply = do_request_sense(common, bh); + break; + + case SC_START_STOP_UNIT: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) + reply = do_start_stop(common); + break; + + case SC_SYNCHRONIZE_CACHE: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0) + reply = do_synchronize_cache(common); + break; + + case SC_TEST_UNIT_READY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); + break; + + /* Although optional, this command is used by MS-Windows. We + * support a minimal version: BytChk must be 0. */ + case SC_VERIFY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY"); + if (reply == 0) + reply = do_verify(common); + break; + + case SC_WRITE_6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)"); + if (reply == 0) + reply = do_write(common); + break; + + case SC_WRITE_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)"); + if (reply == 0) + reply = do_write(common); + break; + + case SC_WRITE_12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)"); + if (reply == 0) + reply = do_write(common); + break; + + /* Some mandatory commands that we recognize but don't implement. + * They don't mean much in this setting. It's left as an exercise + * for anyone interested to implement RESERVE and RELEASE in terms + * of Posix locks. */ + case SC_FORMAT_UNIT: + case SC_RELEASE: + case SC_RESERVE: + case SC_SEND_DIAGNOSTIC: + /* Fall through */ + + default: +unknown_cmnd: + common->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", common->cmnd[0]); + reply = check_command(common, common->cmnd_size, + DATA_DIR_UNKNOWN, 0xff, 0, unknown); + if (reply == 0) { + curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } + break; + } + up_read(&common->filesem); + + if (reply == -EINTR) + return -EINTR; + + /* Set up the single reply buffer for finish_reply() */ + if (reply == -EINVAL) + reply = 0; /* Error reply length */ + if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32) reply, common->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; + common->residue -= reply; + } /* Otherwise it's already set */ + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = bh->outreq; + struct fsg_bulk_cb_wrap *cbw = req->buf; + struct fsg_common *common = fsg->common; + + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) + return -EINVAL; + + /* Is the CBW valid? */ + if (req->actual != USB_BULK_CB_WRAP_LEN || + cbw->Signature != cpu_to_le32( + USB_BULK_CB_SIG)) { + DBG(fsg, "invalid CBW: len %u sig 0x%x\n", + req->actual, + le32_to_cpu(cbw->Signature)); + + /* The Bulk-only spec says we MUST stall the IN endpoint + * (6.6.1), so it's unavoidable. It also says we must + * retain this state until the next reset, but there's + * no way to tell the controller driver it should ignore + * Clear-Feature(HALT) requests. + * + * We aren't required to halt the OUT endpoint; instead + * we can simply accept and discard any data received + * until the next reset. */ + wedge_bulk_in_endpoint(fsg); + set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + return -EINVAL; + } + + /* Is the CBW meaningful? */ + if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || + cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { + DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " + "cmdlen %u\n", + cbw->Lun, cbw->Flags, cbw->Length); + + /* We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. */ + if (common->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } + return -EINVAL; + } + + /* Save the command for later */ + common->cmnd_size = cbw->Length; + memcpy(common->cmnd, cbw->CDB, common->cmnd_size); + if (cbw->Flags & USB_BULK_IN_FLAG) + common->data_dir = DATA_DIR_TO_HOST; + else + common->data_dir = DATA_DIR_FROM_HOST; + common->data_size = le32_to_cpu(cbw->DataTransferLength); + if (common->data_size == 0) + common->data_dir = DATA_DIR_NONE; + common->lun = cbw->Lun; + common->tag = cbw->Tag; + return 0; +} + + +static int get_next_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + /* Queue a request to read a Bulk-only CBW */ + set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + /* We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance + * next_buffhd_to_fill. */ + + /* Wait for the CBW to arrive */ + while (bh->state != BUF_STATE_FULL) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; + bh->state = BUF_STATE_EMPTY; + + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep, + const struct usb_endpoint_descriptor *d) +{ + int rc; + + ep->driver_data = common; + rc = usb_ep_enable(ep, d); + if (rc) + ERROR(common, "can't enable %s, result %d\n", ep->name, rc); + return rc; +} + +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, + struct usb_request **preq) +{ + *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (*preq) + return 0; + ERROR(common, "can't allocate request for %s\n", ep->name); + return -ENOMEM; +} + +/* Reset interface setting and re-init endpoint state (toggle etc). */ +static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg) +{ + const struct usb_endpoint_descriptor *d; + struct fsg_dev *fsg; + int i, rc = 0; + + if (common->running) + DBG(common, "reset interface\n"); + +reset: + /* Deallocate the requests */ + if (common->fsg) { + fsg = common->fsg; + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } + } + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; + } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; + } + + common->fsg = NULL; + /* wake_up(&common->fsg_wait); */ + } + + common->running = 0; + if (!new_fsg || rc) + return rc; + + common->fsg = new_fsg; + fsg = common->fsg; + + /* Enable the endpoints */ + d = fsg_ep_desc(common->gadget, + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); + rc = enable_endpoint(common, fsg->bulk_in, d); + if (rc) + goto reset; + fsg->bulk_in_enabled = 1; + + d = fsg_ep_desc(common->gadget, + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); + rc = enable_endpoint(common, fsg->bulk_out, d); + if (rc) + goto reset; + fsg->bulk_out_enabled = 1; + common->bulk_out_maxpacket = + le16_to_cpu(get_unaligned(&d->wMaxPacketSize)); + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + + /* Allocate the requests */ + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + rc = alloc_request(common, fsg->bulk_in, &bh->inreq); + if (rc) + goto reset; + rc = alloc_request(common, fsg->bulk_out, &bh->outreq); + if (rc) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = bulk_in_complete; + bh->outreq->complete = bulk_out_complete; + } + + common->running = 1; + + return rc; +} + + +/****************************** ALT CONFIGS ******************************/ + + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = fsg; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + return 0; +} + +static void fsg_disable(struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +} + +/*-------------------------------------------------------------------------*/ + +static void handle_exception(struct fsg_common *common) +{ + int i; + struct fsg_buffhd *bh; + enum fsg_state old_state; + struct fsg_lun *curlun; + unsigned int exception_req_tag; + + /* Cancel all the pending transfers */ + if (common->fsg) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + if (bh->inreq_busy) + usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); + if (bh->outreq_busy) + usb_ep_dequeue(common->fsg->bulk_out, + bh->outreq); + } + + /* Wait until everything is idle */ + for (;;) { + int num_active = 0; + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + num_active += bh->inreq_busy + bh->outreq_busy; + } + if (num_active == 0) + break; + if (sleep_thread(common)) + return; + } + + /* Clear out the controller's fifos */ + if (common->fsg->bulk_in_enabled) + usb_ep_fifo_flush(common->fsg->bulk_in); + if (common->fsg->bulk_out_enabled) + usb_ep_fifo_flush(common->fsg->bulk_out); + } + + /* Reset the I/O buffer states and pointers, the SCSI + * state, and the exception. Then invoke the handler. */ + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + bh->state = BUF_STATE_EMPTY; + } + common->next_buffhd_to_fill = &common->buffhds[0]; + common->next_buffhd_to_drain = &common->buffhds[0]; + exception_req_tag = common->exception_req_tag; + old_state = common->state; + + if (old_state == FSG_STATE_ABORT_BULK_OUT) + common->state = FSG_STATE_STATUS_PHASE; + else { + for (i = 0; i < common->nluns; ++i) { + curlun = &common->luns[i]; + curlun->sense_data = SS_NO_SENSE; + curlun->info_valid = 0; + } + common->state = FSG_STATE_IDLE; + } + + /* Carry out any extra actions required for the exception */ + switch (old_state) { + case FSG_STATE_ABORT_BULK_OUT: + send_status(common); + + if (common->state == FSG_STATE_STATUS_PHASE) + common->state = FSG_STATE_IDLE; + break; + + case FSG_STATE_RESET: + /* In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) */ + if (!fsg_is_set(common)) + break; + if (test_and_clear_bit(IGNORE_BULK_OUT, + &common->fsg->atomic_bitflags)) + usb_ep_clear_halt(common->fsg->bulk_in); + + if (common->ep0_req_tag == exception_req_tag) + ep0_queue(common); /* Complete the status stage */ + + break; + + case FSG_STATE_CONFIG_CHANGE: + do_set_interface(common, common->new_fsg); + break; + + case FSG_STATE_EXIT: + case FSG_STATE_TERMINATED: + do_set_interface(common, NULL); /* Free resources */ + common->state = FSG_STATE_TERMINATED; /* Stop the thread */ + break; + + case FSG_STATE_INTERFACE_CHANGE: + case FSG_STATE_DISCONNECT: + case FSG_STATE_COMMAND_PHASE: + case FSG_STATE_DATA_PHASE: + case FSG_STATE_STATUS_PHASE: + case FSG_STATE_IDLE: + break; + } +} + +/*-------------------------------------------------------------------------*/ + +int fsg_main_thread(void *common_) +{ + int ret; + struct fsg_common *common = the_fsg_common; + /* The main loop */ + do { + if (exception_in_progress(common)) { + handle_exception(common); + continue; + } + + if (!common->running) { + ret = sleep_thread(common); + if (ret) + return ret; + + continue; + } + + ret = get_next_command(common); + if (ret) + return ret; + + if (!exception_in_progress(common)) + common->state = FSG_STATE_DATA_PHASE; + + if (do_scsi_command(common) || finish_reply(common)) + continue; + + if (!exception_in_progress(common)) + common->state = FSG_STATE_STATUS_PHASE; + + if (send_status(common)) + continue; + + if (!exception_in_progress(common)) + common->state = FSG_STATE_IDLE; + } while (0); + + common->thread_task = NULL; + + return 0; +} + +static void fsg_common_release(struct kref *ref); + +static struct fsg_common *fsg_common_init(struct fsg_common *common, + struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + struct fsg_buffhd *bh; + struct fsg_lun *curlun; + int nluns, i, rc; + + /* Find out how many LUNs there should be */ + nluns = 1; + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + printf("invalid number of LUNs: %u\n", nluns); + return ERR_PTR(-EINVAL); + } + + /* Allocate? */ + if (!common) { + common = calloc(sizeof *common, 1); + if (!common) + return ERR_PTR(-ENOMEM); + common->free_storage_on_release = 1; + } else { + memset(common, 0, sizeof common); + common->free_storage_on_release = 0; + } + + common->ops = NULL; + common->private_data = NULL; + + common->gadget = gadget; + common->ep0 = gadget->ep0; + common->ep0req = cdev->req; + + /* Maybe allocate device-global string IDs, and patch descriptors */ + if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { + rc = usb_string_id(cdev); + if (unlikely(rc < 0)) + goto error_release; + fsg_strings[FSG_STRING_INTERFACE].id = rc; + fsg_intf_desc.iInterface = rc; + } + + /* Create the LUNs, open their backing files, and register the + * LUN devices in sysfs. */ + curlun = calloc(nluns, sizeof *curlun); + if (!curlun) { + rc = -ENOMEM; + goto error_release; + } + common->nluns = nluns; + + for (i = 0; i < nluns; i++) { + common->luns[i].removable = 1; + + rc = fsg_lun_open(&common->luns[i], ""); + if (rc) + goto error_luns; + } + common->lun = 0; + + /* Data buffers cyclic list */ + bh = common->buffhds; + + i = FSG_NUM_BUFFERS; + goto buffhds_first_it; + do { + bh->next = bh + 1; + ++bh; +buffhds_first_it: + bh->inreq_busy = 0; + bh->outreq_busy = 0; + bh->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, FSG_BUFLEN); + if (unlikely(!bh->buf)) { + rc = -ENOMEM; + goto error_release; + } + } while (--i); + bh->next = common->buffhds; + + snprintf(common->inquiry_string, sizeof common->inquiry_string, + "%-8s%-16s%04x", + "Linux ", + "File-Store Gadget", + 0xffff); + + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + + /* Tell the thread to start working */ + common->thread_task = + kthread_create(fsg_main_thread, common, + OR(cfg->thread_name, "file-storage")); + if (IS_ERR(common->thread_task)) { + rc = PTR_ERR(common->thread_task); + goto error_release; + } + +#undef OR + /* Information */ + INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + INFO(common, "Number of LUNs=%d\n", common->nluns); + + return common; + +error_luns: + common->nluns = i + 1; +error_release: + common->state = FSG_STATE_TERMINATED; /* The thread is dead */ + /* Call fsg_common_release() directly, ref might be not + * initialised */ + fsg_common_release(&common->ref); + return ERR_PTR(rc); +} + +static void fsg_common_release(struct kref *ref) +{ + struct fsg_common *common = container_of(ref, struct fsg_common, ref); + + /* If the thread isn't already dead, tell it to exit now */ + if (common->state != FSG_STATE_TERMINATED) { + raise_exception(common, FSG_STATE_EXIT); + wait_for_completion(&common->thread_notifier); + } + + if (likely(common->luns)) { + struct fsg_lun *lun = common->luns; + unsigned i = common->nluns; + + /* In error recovery common->nluns may be zero. */ + for (; i; --i, ++lun) + fsg_lun_close(lun); + + kfree(common->luns); + } + + { + struct fsg_buffhd *bh = common->buffhds; + unsigned i = FSG_NUM_BUFFERS; + do { + kfree(bh->buf); + } while (++bh, --i); + } + + if (common->free_storage_on_release) + kfree(common); +} + + +/*-------------------------------------------------------------------------*/ + +/** + * usb_copy_descriptors - copy a vector of USB descriptors + * @src: null-terminated vector to copy + * Context: initialization code, which may sleep + * + * This makes a copy of a vector of USB descriptors. Its primary use + * is to support usb_function objects which can have multiple copies, + * each needing different descriptors. Functions may have static + * tables of descriptors, which are used as templates and customized + * with identifiers (for interfaces, strings, endpoints, and more) + * as needed by a given function instance. + */ +struct usb_descriptor_header ** +usb_copy_descriptors(struct usb_descriptor_header **src) +{ + struct usb_descriptor_header **tmp; + unsigned bytes; + unsigned n_desc; + void *mem; + struct usb_descriptor_header **ret; + + /* count descriptors and their sizes; then add vector size */ + for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) + bytes += (*tmp)->bLength; + bytes += (n_desc + 1) * sizeof(*tmp); + + mem = memalign(CONFIG_SYS_CACHELINE_SIZE, bytes); + if (!mem) + return NULL; + + /* fill in pointers starting at "tmp", + * to descriptors copied starting at "mem"; + * and return "ret" + */ + tmp = mem; + ret = mem; + mem += (n_desc + 1) * sizeof(*tmp); + while (*src) { + memcpy(mem, *src, (*src)->bLength); + *tmp = mem; + tmp++; + mem += (*src)->bLength; + src++; + } + *tmp = NULL; + + return ret; +} + +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + + DBG(fsg, "unbind\n"); + if (fsg->common->fsg == fsg) { + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + } + + free(fsg->function.descriptors); + free(fsg->function.hs_descriptors); + kfree(fsg); +} + +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_gadget *gadget = c->cdev->gadget; + int i; + struct usb_ep *ep; + fsg->gadget = gadget; + + /* New interface */ + i = usb_interface_id(c, f); + if (i < 0) + return i; + fsg_intf_desc.bInterfaceNumber = i; + fsg->interface_number = i; + + /* Find all the endpoints we will use */ + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_out = ep; + + /* Copy descriptors */ + f->descriptors = usb_copy_descriptors(fsg_fs_function); + if (unlikely(!f->descriptors)) + return -ENOMEM; + + if (gadget_is_dualspeed(gadget)) { + /* Assume endpoint addresses are the same for both speeds */ + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + f->hs_descriptors = usb_copy_descriptors(fsg_hs_function); + if (unlikely(!f->hs_descriptors)) { + free(f->descriptors); + return -ENOMEM; + } + } + return 0; + +autoconf_fail: + ERROR(fsg, "unable to autoconfigure all endpoints\n"); + return -ENOTSUPP; +} + + +/****************************** ADD FUNCTION ******************************/ + +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, +}; + +static int fsg_bind_config(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct fsg_common *common) +{ + struct fsg_dev *fsg; + int rc; + + fsg = calloc(1, sizeof *fsg); + if (!fsg) + return -ENOMEM; + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.strings = fsg_strings_array; + fsg->function.bind = fsg_bind; + fsg->function.unbind = fsg_unbind; + fsg->function.setup = fsg_setup; + fsg->function.set_alt = fsg_set_alt; + fsg->function.disable = fsg_disable; + + fsg->common = common; + common->fsg = fsg; + /* Our caller holds a reference to common structure so we + * don't have to be worry about it being freed until we return + * from this function. So instead of incrementing counter now + * and decrement in error recovery we increment it only when + * call to usb_add_function() was successful. */ + + rc = usb_add_function(c, &fsg->function); + + if (rc) + kfree(fsg); + + return rc; +} + +int fsg_add(struct usb_configuration *c) +{ + struct fsg_common *fsg_common; + + fsg_common = fsg_common_init(NULL, c->cdev); + + fsg_common->vendor_name = 0; + fsg_common->product_name = 0; + fsg_common->release = 0xffff; + + fsg_common->ops = NULL; + fsg_common->private_data = NULL; + + the_fsg_common = fsg_common; + + return fsg_bind_config(c->cdev, c, fsg_common); +} + +int fsg_init(struct ums *ums_dev) +{ + ums = ums_dev; + + return 0; +} + +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_ums, fsg_add); diff --git a/qemu/roms/u-boot/drivers/usb/gadget/f_thor.c b/qemu/roms/u-boot/drivers/usb/gadget/f_thor.c new file mode 100644 index 000000000..feef9e461 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/f_thor.c @@ -0,0 +1,1008 @@ +/* + * f_thor.c -- USB TIZEN THOR Downloader gadget function + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski + * + * Based on code from: + * git://review.tizen.org/kernel/u-boot + * + * Developed by: + * Copyright (C) 2009 Samsung Electronics + * Minkyu Kang + * Sanghee Kim + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f_thor.h" + +static void thor_tx_data(unsigned char *data, int len); +static void thor_set_dma(void *addr, int len); +static int thor_rx_data(void); + +static struct f_thor *thor_func; +static inline struct f_thor *func_to_thor(struct usb_function *f) +{ + return container_of(f, struct f_thor, usb_function); +} + +DEFINE_CACHE_ALIGN_BUFFER(unsigned char, thor_tx_data_buf, + sizeof(struct rsp_box)); +DEFINE_CACHE_ALIGN_BUFFER(unsigned char, thor_rx_data_buf, + sizeof(struct rqt_box)); + +/* ********************************************************** */ +/* THOR protocol - transmission handling */ +/* ********************************************************** */ +DEFINE_CACHE_ALIGN_BUFFER(char, f_name, F_NAME_BUF_SIZE); +static unsigned long long int thor_file_size; +static int alt_setting_num; + +static void send_rsp(const struct rsp_box *rsp) +{ + memcpy(thor_tx_data_buf, rsp, sizeof(struct rsp_box)); + thor_tx_data(thor_tx_data_buf, sizeof(struct rsp_box)); + + debug("-RSP: %d, %d\n", rsp->rsp, rsp->rsp_data); +} + +static void send_data_rsp(s32 ack, s32 count) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct data_rsp_box, rsp, + sizeof(struct data_rsp_box)); + + rsp->ack = ack; + rsp->count = count; + + memcpy(thor_tx_data_buf, rsp, sizeof(struct data_rsp_box)); + thor_tx_data(thor_tx_data_buf, sizeof(struct data_rsp_box)); + + debug("-DATA RSP: %d, %d\n", ack, count); +} + +static int process_rqt_info(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + memset(rsp, 0, sizeof(struct rsp_box)); + + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_INFO_VER_PROTOCOL: + rsp->int_data[0] = VER_PROTOCOL_MAJOR; + rsp->int_data[1] = VER_PROTOCOL_MINOR; + break; + case RQT_INIT_VER_HW: + snprintf(rsp->str_data[0], sizeof(rsp->str_data[0]), + "%x", checkboard()); + break; + case RQT_INIT_VER_BOOT: + sprintf(rsp->str_data[0], "%s", U_BOOT_VERSION); + break; + case RQT_INIT_VER_KERNEL: + sprintf(rsp->str_data[0], "%s", "k unknown"); + break; + case RQT_INIT_VER_PLATFORM: + sprintf(rsp->str_data[0], "%s", "p unknown"); + break; + case RQT_INIT_VER_CSC: + sprintf(rsp->str_data[0], "%s", "c unknown"); + break; + default: + return -EINVAL; + } + + send_rsp(rsp); + return true; +} + +static int process_rqt_cmd(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + memset(rsp, 0, sizeof(struct rsp_box)); + + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_CMD_REBOOT: + debug("TARGET RESET\n"); + send_rsp(rsp); + g_dnl_unregister(); + dfu_free_entities(); + run_command("reset", 0); + break; + case RQT_CMD_POWEROFF: + case RQT_CMD_EFSCLEAR: + send_rsp(rsp); + default: + printf("Command not supported -> cmd: %d\n", rqt->rqt_data); + return -EINVAL; + } + + return true; +} + +static long long int download_head(unsigned long long total, + unsigned int packet_size, + long long int *left, + int *cnt) +{ + long long int rcv_cnt = 0, left_to_rcv, ret_rcv; + void *transfer_buffer = dfu_get_buf(); + void *buf = transfer_buffer; + int usb_pkt_cnt = 0, ret; + + /* + * Files smaller than THOR_STORE_UNIT_SIZE (now 32 MiB) are stored on + * the medium. + * The packet response is sent on the purpose after successful data + * chunk write. There is a room for improvement when asynchronous write + * is performed. + */ + while (total - rcv_cnt >= packet_size) { + thor_set_dma(buf, packet_size); + buf += packet_size; + ret_rcv = thor_rx_data(); + if (ret_rcv < 0) + return ret_rcv; + rcv_cnt += ret_rcv; + debug("%d: RCV data count: %llu cnt: %d\n", usb_pkt_cnt, + rcv_cnt, *cnt); + + if ((rcv_cnt % THOR_STORE_UNIT_SIZE) == 0) { + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, THOR_STORE_UNIT_SIZE, + (*cnt)++); + if (ret) { + error("DFU write failed [%d] cnt: %d", + ret, *cnt); + return ret; + } + buf = transfer_buffer; + } + send_data_rsp(0, ++usb_pkt_cnt); + } + + /* Calculate the amount of data to arrive from PC (in bytes) */ + left_to_rcv = total - rcv_cnt; + + /* + * Calculate number of data already received. but not yet stored + * on the medium (they are smaller than THOR_STORE_UNIT_SIZE) + */ + *left = left_to_rcv + buf - transfer_buffer; + debug("%s: left: %llu left_to_rcv: %llu buf: 0x%p\n", __func__, + *left, left_to_rcv, buf); + + if (left_to_rcv) { + thor_set_dma(buf, packet_size); + ret_rcv = thor_rx_data(); + if (ret_rcv < 0) + return ret_rcv; + rcv_cnt += ret_rcv; + send_data_rsp(0, ++usb_pkt_cnt); + } + + debug("%s: %llu total: %llu cnt: %d\n", __func__, rcv_cnt, total, *cnt); + + return rcv_cnt; +} + +static int download_tail(long long int left, int cnt) +{ + struct dfu_entity *dfu_entity = dfu_get_entity(alt_setting_num); + void *transfer_buffer = dfu_get_buf(); + int ret; + + debug("%s: left: %llu cnt: %d\n", __func__, left, cnt); + + if (left) { + ret = dfu_write(dfu_entity, transfer_buffer, left, cnt++); + if (ret) { + error("DFU write failed [%d]: left: %llu", ret, left); + return ret; + } + } + + /* + * To store last "packet" DFU storage backend requires dfu_write with + * size parameter equal to 0 + * + * This also frees memory malloc'ed by dfu_get_buf(), so no explicit + * need fo call dfu_free_buf() is needed. + */ + ret = dfu_write(dfu_entity, transfer_buffer, 0, cnt); + if (ret) + error("DFU write failed [%d] cnt: %d", ret, cnt); + + ret = dfu_flush(dfu_entity, transfer_buffer, 0, cnt); + if (ret) { + error("DFU flush failed!"); + return ret; + } + + return ret; +} + +static long long int process_rqt_download(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + static long long int left, ret_head; + int file_type, ret = 0; + static int cnt; + + memset(rsp, 0, sizeof(struct rsp_box)); + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_DL_INIT: + thor_file_size = rqt->int_data[0]; + debug("INIT: total %d bytes\n", rqt->int_data[0]); + break; + case RQT_DL_FILE_INFO: + file_type = rqt->int_data[0]; + if (file_type == FILE_TYPE_PIT) { + puts("PIT table file - not supported\n"); + rsp->ack = -ENOTSUPP; + ret = rsp->ack; + break; + } + + thor_file_size = rqt->int_data[1]; + memcpy(f_name, rqt->str_data[0], F_NAME_BUF_SIZE); + + debug("INFO: name(%s, %d), size(%llu), type(%d)\n", + f_name, 0, thor_file_size, file_type); + + rsp->int_data[0] = THOR_PACKET_SIZE; + + alt_setting_num = dfu_get_alt(f_name); + if (alt_setting_num < 0) { + error("Alt setting [%d] to write not found!", + alt_setting_num); + rsp->ack = -ENODEV; + ret = rsp->ack; + } + break; + case RQT_DL_FILE_START: + send_rsp(rsp); + ret_head = download_head(thor_file_size, THOR_PACKET_SIZE, + &left, &cnt); + if (ret_head < 0) { + left = 0; + cnt = 0; + } + return ret_head; + case RQT_DL_FILE_END: + debug("DL FILE_END\n"); + rsp->ack = download_tail(left, cnt); + ret = rsp->ack; + left = 0; + cnt = 0; + break; + case RQT_DL_EXIT: + debug("DL EXIT\n"); + break; + default: + error("Operation not supported: %d", rqt->rqt_data); + ret = -ENOTSUPP; + } + + send_rsp(rsp); + return ret; +} + +static int process_data(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rqt_box, rqt, sizeof(struct rqt_box)); + int ret = -EINVAL; + + memset(rqt, 0, sizeof(rqt)); + memcpy(rqt, thor_rx_data_buf, sizeof(struct rqt_box)); + + debug("+RQT: %d, %d\n", rqt->rqt, rqt->rqt_data); + + switch (rqt->rqt) { + case RQT_INFO: + ret = process_rqt_info(rqt); + break; + case RQT_CMD: + ret = process_rqt_cmd(rqt); + break; + case RQT_DL: + ret = (int) process_rqt_download(rqt); + break; + case RQT_UL: + puts("RQT: UPLOAD not supported!\n"); + break; + default: + error("unknown request (%d)", rqt->rqt); + } + + return ret; +} + +/* ********************************************************** */ +/* THOR USB Function */ +/* ********************************************************** */ + +static inline struct usb_endpoint_descriptor * +ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +static struct usb_interface_descriptor thor_downloader_intf_data = { + .bLength = sizeof(thor_downloader_intf_data), + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, +}; + +static struct usb_endpoint_descriptor fs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* CDC configuration */ +static struct usb_interface_descriptor thor_downloader_intf_int = { + .bLength = sizeof(thor_downloader_intf_int), + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + /* 0x02 Abstract Line Control Model */ + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + /* 0x01 Common AT commands */ + .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, +}; + +static struct usb_cdc_header_desc thor_downloader_cdc_header = { + .bLength = sizeof(thor_downloader_cdc_header), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x00, + .bcdCDC = 0x0110, +}; + +static struct usb_cdc_call_mgmt_descriptor thor_downloader_cdc_call = { + .bLength = sizeof(thor_downloader_cdc_call), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x01, + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + +static struct usb_cdc_acm_descriptor thor_downloader_cdc_abstract = { + .bLength = sizeof(thor_downloader_cdc_abstract), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x02, + .bmCapabilities = 0x00, +}; + +static struct usb_cdc_union_desc thor_downloader_cdc_union = { + .bLength = sizeof(thor_downloader_cdc_union), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = USB_CDC_UNION_TYPE, +}; + +static struct usb_endpoint_descriptor fs_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = 3 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(16), + + .bInterval = 0x9, +}; + +static struct usb_interface_assoc_descriptor +thor_iad_descriptor = { + .bLength = sizeof(thor_iad_descriptor), + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, + .bInterfaceCount = 2, /* control + data */ + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_PROTO_NONE, +}; + +static struct usb_endpoint_descriptor hs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(16), + + .bInterval = 0x9, +}; + +static struct usb_qualifier_descriptor dev_qualifier = { + .bLength = sizeof(dev_qualifier), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + + .bNumConfigurations = 2, +}; + +/* + * This attribute vendor descriptor is necessary for correct operation with + * Windows version of THOR download program + * + * It prevents windows driver from sending zero lenght packet (ZLP) after + * each THOR_PACKET_SIZE. This assures consistent behaviour with libusb + */ +static struct usb_cdc_attribute_vendor_descriptor thor_downloader_cdc_av = { + .bLength = sizeof(thor_downloader_cdc_av), + .bDescriptorType = 0x24, + .bDescriptorSubType = 0x80, + .DAUType = 0x0002, + .DAULength = 0x0001, + .DAUValue = 0x00, +}; + +static const struct usb_descriptor_header *hs_thor_downloader_function[] = { + (struct usb_descriptor_header *)&thor_iad_descriptor, + + (struct usb_descriptor_header *)&thor_downloader_intf_int, + (struct usb_descriptor_header *)&thor_downloader_cdc_header, + (struct usb_descriptor_header *)&thor_downloader_cdc_call, + (struct usb_descriptor_header *)&thor_downloader_cdc_abstract, + (struct usb_descriptor_header *)&thor_downloader_cdc_union, + (struct usb_descriptor_header *)&hs_int_desc, + + (struct usb_descriptor_header *)&thor_downloader_intf_data, + (struct usb_descriptor_header *)&thor_downloader_cdc_av, + (struct usb_descriptor_header *)&hs_in_desc, + (struct usb_descriptor_header *)&hs_out_desc, + NULL, +}; + +/*-------------------------------------------------------------------------*/ +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, 0); + if (!req) + return req; + + req->length = length; + req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + + return req; +} + +static int thor_rx_data(void) +{ + struct thor_dev *dev = thor_func->dev; + int data_to_rx, tmp, status; + + data_to_rx = dev->out_req->length; + tmp = data_to_rx; + do { + dev->out_req->length = data_to_rx; + debug("dev->out_req->length:%d dev->rxdata:%d\n", + dev->out_req->length, dev->rxdata); + + status = usb_ep_queue(dev->out_ep, dev->out_req, 0); + if (status) { + error("kill %s: resubmit %d bytes --> %d", + dev->out_ep->name, dev->out_req->length, status); + usb_ep_set_halt(dev->out_ep); + return -EAGAIN; + } + + while (!dev->rxdata) { + usb_gadget_handle_interrupts(); + if (ctrlc()) + return -1; + } + dev->rxdata = 0; + data_to_rx -= dev->out_req->actual; + } while (data_to_rx); + + return tmp; +} + +static void thor_tx_data(unsigned char *data, int len) +{ + struct thor_dev *dev = thor_func->dev; + unsigned char *ptr = dev->in_req->buf; + int status; + + memset(ptr, 0, len); + memcpy(ptr, data, len); + + dev->in_req->length = len; + + debug("%s: dev->in_req->length:%d to_cpy:%d\n", __func__, + dev->in_req->length, sizeof(data)); + + status = usb_ep_queue(dev->in_ep, dev->in_req, 0); + if (status) { + error("kill %s: resubmit %d bytes --> %d", + dev->in_ep->name, dev->in_req->length, status); + usb_ep_set_halt(dev->in_ep); + } + + /* Wait until tx interrupt received */ + while (!dev->txdata) + usb_gadget_handle_interrupts(); + + dev->txdata = 0; +} + +static void thor_rx_tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct thor_dev *dev = thor_func->dev; + int status = req->status; + + debug("%s: ep_ptr:%p, req_ptr:%p\n", __func__, ep, req); + switch (status) { + case 0: + if (ep == dev->out_ep) + dev->rxdata = 1; + else + dev->txdata = 1; + + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + case -EREMOTEIO: /* short read */ + case -EOVERFLOW: + error("ERROR:%d", status); + break; + } + + debug("%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); +} + +static struct usb_request *thor_start_ep(struct usb_ep *ep) +{ + struct usb_request *req; + + req = alloc_ep_req(ep, THOR_PACKET_SIZE); + debug("%s: ep:%p req:%p\n", __func__, ep, req); + + if (!req) + return NULL; + + memset(req->buf, 0, req->length); + req->complete = thor_rx_tx_complete; + + return req; +} + +static void thor_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + debug("setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +static int +thor_func_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct thor_dev *dev = thor_func->dev; + struct usb_request *req = dev->req; + struct usb_gadget *gadget = dev->gadget; + int value = 0; + + u16 len = le16_to_cpu(ctrl->wLength); + + debug("Req_Type: 0x%x Req: 0x%x wValue: 0x%x wIndex: 0x%x wLen: 0x%x\n", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, + ctrl->wLength); + + switch (ctrl->bRequest) { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + value = 0; + break; + case USB_CDC_REQ_SET_LINE_CODING: + value = len; + /* Line Coding set done = configuration done */ + thor_func->dev->configuration_done = 1; + break; + + default: + error("thor_setup: unknown request: %d", ctrl->bRequest); + } + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("%s: ep_queue: %d\n", __func__, value); + req->status = 0; + } + } + + return value; +} + +/* Specific to the THOR protocol */ +static void thor_set_dma(void *addr, int len) +{ + struct thor_dev *dev = thor_func->dev; + + debug("in_req:%p, out_req:%p\n", dev->in_req, dev->out_req); + debug("addr:%p, len:%d\n", addr, len); + + dev->out_req->buf = addr; + dev->out_req->length = len; +} + +int thor_init(void) +{ + struct thor_dev *dev = thor_func->dev; + + /* Wait for a device enumeration and configuration settings */ + debug("THOR enumeration/configuration setting....\n"); + while (!dev->configuration_done) + usb_gadget_handle_interrupts(); + + thor_set_dma(thor_rx_data_buf, strlen("THOR")); + /* detect the download request from Host PC */ + if (thor_rx_data() < 0) { + printf("%s: Data not received!\n", __func__); + return -1; + } + + if (!strncmp((char *)thor_rx_data_buf, "THOR", strlen("THOR"))) { + puts("Download request from the Host PC\n"); + udelay(30 * 1000); /* 30 ms */ + + strcpy((char *)thor_tx_data_buf, "ROHT"); + thor_tx_data(thor_tx_data_buf, strlen("ROHT")); + } else { + puts("Wrong reply information\n"); + return -1; + } + + return 0; +} + +int thor_handle(void) +{ + int ret; + + /* receive the data from Host PC */ + while (1) { + thor_set_dma(thor_rx_data_buf, sizeof(struct rqt_box)); + ret = thor_rx_data(); + + if (ret > 0) { + ret = process_data(); + if (ret < 0) + return ret; + } else { + printf("%s: No data received!\n", __func__); + break; + } + } + + return 0; +} + +static int thor_func_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_gadget *gadget = c->cdev->gadget; + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev; + struct usb_ep *ep; + int status; + + thor_func = f_thor; + dev = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*dev)); + if (!dev) + return -ENOMEM; + + memset(dev, 0, sizeof(*dev)); + dev->gadget = gadget; + f_thor->dev = dev; + + debug("%s: usb_configuration: 0x%p usb_function: 0x%p\n", + __func__, c, f); + debug("f_thor: 0x%p thor: 0x%p\n", f_thor, dev); + + /* EP0 */ + /* preallocate control response and buffer */ + dev->req = usb_ep_alloc_request(gadget->ep0, 0); + if (!dev->req) { + status = -ENOMEM; + goto fail; + } + dev->req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, + gadget->ep0->maxpacket); + if (!dev->req->buf) { + status = -ENOMEM; + goto fail; + } + + dev->req->complete = thor_setup_complete; + + /* DYNAMIC interface numbers assignments */ + status = usb_interface_id(c, f); + + if (status < 0) + goto fail; + + thor_downloader_intf_int.bInterfaceNumber = status; + thor_downloader_cdc_union.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + + if (status < 0) + goto fail; + + thor_downloader_intf_data.bInterfaceNumber = status; + thor_downloader_cdc_union.bSlaveInterface0 = status; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(gadget, &fs_in_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + if (gadget_is_dualspeed(gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + } + + dev->in_ep = ep; /* Store IN EP for enabling @ setup */ + + ep = usb_ep_autoconfig(gadget, &fs_out_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + if (gadget_is_dualspeed(gadget)) + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + + dev->out_ep = ep; /* Store OUT EP for enabling @ setup */ + + ep = usb_ep_autoconfig(gadget, &fs_int_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + dev->int_ep = ep; + + if (gadget_is_dualspeed(gadget)) { + hs_int_desc.bEndpointAddress = + fs_int_desc.bEndpointAddress; + + f->hs_descriptors = (struct usb_descriptor_header **) + &hs_thor_downloader_function; + + if (!f->hs_descriptors) + goto fail; + } + + debug("%s: out_ep:%p out_req:%p\n", __func__, + dev->out_ep, dev->out_req); + + return 0; + + fail: + free(dev); + return status; +} + +static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + free(req->buf); + usb_ep_free_request(ep, req); +} + +static void thor_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev = f_thor->dev; + + free(dev); + memset(thor_func, 0, sizeof(*thor_func)); + thor_func = NULL; +} + +static void thor_func_disable(struct usb_function *f) +{ + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev = f_thor->dev; + + debug("%s:\n", __func__); + + /* Avoid freeing memory when ep is still claimed */ + if (dev->in_ep->driver_data) { + free_ep_req(dev->in_ep, dev->in_req); + usb_ep_disable(dev->in_ep); + dev->in_ep->driver_data = NULL; + } + + if (dev->out_ep->driver_data) { + dev->out_req->buf = NULL; + usb_ep_free_request(dev->out_ep, dev->out_req); + usb_ep_disable(dev->out_ep); + dev->out_ep->driver_data = NULL; + } + + if (dev->int_ep->driver_data) { + usb_ep_disable(dev->int_ep); + dev->int_ep->driver_data = NULL; + } +} + +static int thor_eps_setup(struct usb_function *f) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct thor_dev *dev = thor_func->dev; + struct usb_endpoint_descriptor *d; + struct usb_request *req; + struct usb_ep *ep; + int result; + + ep = dev->in_ep; + d = ep_desc(gadget, &hs_in_desc, &fs_in_desc); + debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress); + + result = usb_ep_enable(ep, d); + if (result) + goto exit; + + ep->driver_data = cdev; /* claim */ + req = thor_start_ep(ep); + if (!req) { + usb_ep_disable(ep); + result = -EIO; + goto exit; + } + + dev->in_req = req; + ep = dev->out_ep; + d = ep_desc(gadget, &hs_out_desc, &fs_out_desc); + debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress); + + result = usb_ep_enable(ep, d); + if (result) + goto exit; + + ep->driver_data = cdev; /* claim */ + req = thor_start_ep(ep); + if (!req) { + usb_ep_disable(ep); + result = -EIO; + goto exit; + } + + dev->out_req = req; + /* ACM control EP */ + ep = dev->int_ep; + ep->driver_data = cdev; /* claim */ + + exit: + return result; +} + +static int thor_func_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct thor_dev *dev = thor_func->dev; + int result; + + debug("%s: func: %s intf: %d alt: %d\n", + __func__, f->name, intf, alt); + + switch (intf) { + case 0: + debug("ACM INTR interface\n"); + break; + case 1: + debug("Communication Data interface\n"); + result = thor_eps_setup(f); + if (result) + error("%s: EPs setup failed!", __func__); + dev->configuration_done = 1; + break; + } + + return 0; +} + +static int thor_func_init(struct usb_configuration *c) +{ + struct f_thor *f_thor; + int status; + + debug("%s: cdev: 0x%p\n", __func__, c->cdev); + + f_thor = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_thor)); + if (!f_thor) + return -ENOMEM; + + memset(f_thor, 0, sizeof(*f_thor)); + + f_thor->usb_function.name = "f_thor"; + f_thor->usb_function.bind = thor_func_bind; + f_thor->usb_function.unbind = thor_unbind; + f_thor->usb_function.setup = thor_func_setup; + f_thor->usb_function.set_alt = thor_func_set_alt; + f_thor->usb_function.disable = thor_func_disable; + + status = usb_add_function(c, &f_thor->usb_function); + if (status) + free(f_thor); + + return status; +} + +int thor_add(struct usb_configuration *c) +{ + debug("%s:\n", __func__); + return thor_func_init(c); +} + +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_thor, thor_add); diff --git a/qemu/roms/u-boot/drivers/usb/gadget/f_thor.h b/qemu/roms/u-boot/drivers/usb/gadget/f_thor.h new file mode 100644 index 000000000..833a9d24a --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/f_thor.h @@ -0,0 +1,124 @@ +/* + * f_thor.h - USB TIZEN THOR - internal gadget definitions + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _USB_THOR_H_ +#define _USB_THOR_H_ + +#include +#include + +/* THOR Composite Gadget */ +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 + +/* ********************************************************** */ +/* THOR protocol definitions */ +/* ********************************************************** */ + +/* + * Attribute Vendor descriptor - necessary to prevent ZLP transmission + * from Windows XP HOST PC + */ +struct usb_cdc_attribute_vendor_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u16 DAUType; + __u16 DAULength; + __u8 DAUValue; +} __packed; + +#define VER_PROTOCOL_MAJOR 4 +#define VER_PROTOCOL_MINOR 0 + +enum rqt { + RQT_INFO = 200, + RQT_CMD, + RQT_DL, + RQT_UL, +}; + +enum rqt_data { + /* RQT_INFO */ + RQT_INFO_VER_PROTOCOL = 1, + RQT_INIT_VER_HW, + RQT_INIT_VER_BOOT, + RQT_INIT_VER_KERNEL, + RQT_INIT_VER_PLATFORM, + RQT_INIT_VER_CSC, + + /* RQT_CMD */ + RQT_CMD_REBOOT = 1, + RQT_CMD_POWEROFF, + RQT_CMD_EFSCLEAR, + + /* RQT_DL */ + RQT_DL_INIT = 1, + RQT_DL_FILE_INFO, + RQT_DL_FILE_START, + RQT_DL_FILE_END, + RQT_DL_EXIT, + + /* RQT_UL */ + RQT_UL_INIT = 1, + RQT_UL_START, + RQT_UL_END, + RQT_UL_EXIT, +}; + +struct rqt_box { /* total: 256B */ + s32 rqt; /* request id */ + s32 rqt_data; /* request data id */ + s32 int_data[14]; /* int data */ + char str_data[5][32]; /* string data */ + char md5[32]; /* md5 checksum */ +} __packed; + +struct rsp_box { /* total: 128B */ + s32 rsp; /* response id (= request id) */ + s32 rsp_data; /* response data id */ + s32 ack; /* ack */ + s32 int_data[5]; /* int data */ + char str_data[3][32]; /* string data */ +} __packed; + +struct data_rsp_box { /* total: 8B */ + s32 ack; /* response id (= request id) */ + s32 count; /* response data id */ +} __packed; + +enum { + FILE_TYPE_NORMAL, + FILE_TYPE_PIT, +}; + +struct thor_dev { + struct usb_gadget *gadget; + struct usb_request *req; /* EP0 -> control responses */ + + /* IN/OUT EP's and correspoinding requests */ + struct usb_ep *in_ep, *out_ep, *int_ep; + struct usb_request *in_req, *out_req; + + /* Control flow variables */ + unsigned char configuration_done; + unsigned char rxdata; + unsigned char txdata; +}; + +struct f_thor { + struct usb_function usb_function; + struct thor_dev *dev; +}; + +#define F_NAME_BUF_SIZE 32 +#define THOR_PACKET_SIZE SZ_1M /* 1 MiB */ +#define THOR_STORE_UNIT_SIZE SZ_32M /* 32 MiB */ +#endif /* _USB_THOR_H_ */ diff --git a/qemu/roms/u-boot/drivers/usb/gadget/fotg210.c b/qemu/roms/u-boot/drivers/usb/gadget/fotg210.c new file mode 100644 index 000000000..3acf6a1f4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/fotg210.c @@ -0,0 +1,962 @@ +/* + * Faraday USB 2.0 OTG Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CFG_NUM_ENDPOINTS 4 +#define CFG_EP0_MAX_PACKET_SIZE 64 +#define CFG_EPX_MAX_PACKET_SIZE 512 + +#define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 2) /* 250 ms */ + +struct fotg210_chip; + +struct fotg210_ep { + struct usb_ep ep; + + uint maxpacket; + uint id; + uint stopped; + + struct list_head queue; + struct fotg210_chip *chip; + const struct usb_endpoint_descriptor *desc; +}; + +struct fotg210_request { + struct usb_request req; + struct list_head queue; + struct fotg210_ep *ep; +}; + +struct fotg210_chip { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct fotg210_regs *regs; + uint8_t irq; + uint16_t addr; + int pullup; + enum usb_device_state state; + struct fotg210_ep ep[1 + CFG_NUM_ENDPOINTS]; +}; + +static struct usb_endpoint_descriptor ep0_desc = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static inline int fifo_to_ep(struct fotg210_chip *chip, int id, int in) +{ + return (id < 0) ? 0 : ((id & 0x03) + 1); +} + +static inline int ep_to_fifo(struct fotg210_chip *chip, int id) +{ + return (id <= 0) ? -1 : ((id - 1) & 0x03); +} + +static inline int ep_reset(struct fotg210_chip *chip, uint8_t ep_addr) +{ + int ep = ep_addr & USB_ENDPOINT_NUMBER_MASK; + struct fotg210_regs *regs = chip->regs; + + if (ep_addr & USB_DIR_IN) { + /* reset endpoint */ + setbits_le32(®s->iep[ep - 1], IEP_RESET); + mdelay(1); + clrbits_le32(®s->iep[ep - 1], IEP_RESET); + /* clear endpoint stall */ + clrbits_le32(®s->iep[ep - 1], IEP_STALL); + } else { + /* reset endpoint */ + setbits_le32(®s->oep[ep - 1], OEP_RESET); + mdelay(1); + clrbits_le32(®s->oep[ep - 1], OEP_RESET); + /* clear endpoint stall */ + clrbits_le32(®s->oep[ep - 1], OEP_STALL); + } + + return 0; +} + +static int fotg210_reset(struct fotg210_chip *chip) +{ + struct fotg210_regs *regs = chip->regs; + uint32_t i; + + chip->state = USB_STATE_POWERED; + + /* chip enable */ + writel(DEVCTRL_EN, ®s->dev_ctrl); + + /* device address reset */ + chip->addr = 0; + writel(0, ®s->dev_addr); + + /* set idle counter to 7ms */ + writel(7, ®s->idle); + + /* disable all interrupts */ + writel(IMR_MASK, ®s->imr); + writel(GIMR_MASK, ®s->gimr); + writel(GIMR0_MASK, ®s->gimr0); + writel(GIMR1_MASK, ®s->gimr1); + writel(GIMR2_MASK, ®s->gimr2); + + /* clear interrupts */ + writel(ISR_MASK, ®s->isr); + writel(0, ®s->gisr); + writel(0, ®s->gisr0); + writel(0, ®s->gisr1); + writel(0, ®s->gisr2); + + /* chip reset */ + setbits_le32(®s->dev_ctrl, DEVCTRL_RESET); + mdelay(10); + if (readl(®s->dev_ctrl) & DEVCTRL_RESET) { + printf("fotg210: chip reset failed\n"); + return -1; + } + + /* CX FIFO reset */ + setbits_le32(®s->cxfifo, CXFIFO_CXFIFOCLR); + mdelay(10); + if (readl(®s->cxfifo) & CXFIFO_CXFIFOCLR) { + printf("fotg210: ep0 fifo reset failed\n"); + return -1; + } + + /* create static ep-fifo map (EP1 <-> FIFO0, EP2 <-> FIFO1 ...) */ + writel(EPMAP14_DEFAULT, ®s->epmap14); + writel(EPMAP58_DEFAULT, ®s->epmap58); + writel(FIFOMAP_DEFAULT, ®s->fifomap); + writel(0, ®s->fifocfg); + for (i = 0; i < 8; ++i) { + writel(CFG_EPX_MAX_PACKET_SIZE, ®s->iep[i]); + writel(CFG_EPX_MAX_PACKET_SIZE, ®s->oep[i]); + } + + /* FIFO reset */ + for (i = 0; i < 4; ++i) { + writel(FIFOCSR_RESET, ®s->fifocsr[i]); + mdelay(10); + if (readl(®s->fifocsr[i]) & FIFOCSR_RESET) { + printf("fotg210: fifo%d reset failed\n", i); + return -1; + } + } + + /* enable only device interrupt and triggered at level-high */ + writel(IMR_IRQLH | IMR_HOST | IMR_OTG, ®s->imr); + writel(ISR_MASK, ®s->isr); + /* disable EP0 IN/OUT interrupt */ + writel(GIMR0_CXOUT | GIMR0_CXIN, ®s->gimr0); + /* disable EPX IN+SPK+OUT interrupts */ + writel(GIMR1_MASK, ®s->gimr1); + /* disable wakeup+idle+dma+zlp interrupts */ + writel(GIMR2_WAKEUP | GIMR2_IDLE | GIMR2_DMAERR | GIMR2_DMAFIN + | GIMR2_ZLPRX | GIMR2_ZLPTX, ®s->gimr2); + /* enable all group interrupt */ + writel(0, ®s->gimr); + + /* suspend delay = 3 ms */ + writel(3, ®s->idle); + + /* turn-on device interrupts */ + setbits_le32(®s->dev_ctrl, DEVCTRL_GIRQ_EN); + + return 0; +} + +static inline int fotg210_cxwait(struct fotg210_chip *chip, uint32_t mask) +{ + struct fotg210_regs *regs = chip->regs; + int ret = -1; + ulong ts; + + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + if ((readl(®s->cxfifo) & mask) != mask) + continue; + ret = 0; + break; + } + + if (ret) + printf("fotg210: cx/ep0 timeout\n"); + + return ret; +} + +static int fotg210_dma(struct fotg210_ep *ep, struct fotg210_request *req) +{ + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + uint32_t tmp, ts; + uint8_t *buf = req->req.buf + req->req.actual; + uint32_t len = req->req.length - req->req.actual; + int fifo = ep_to_fifo(chip, ep->id); + int ret = -EBUSY; + + /* 1. init dma buffer */ + if (len > ep->maxpacket) + len = ep->maxpacket; + + /* 2. wait for dma ready (hardware) */ + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + if (!(readl(®s->dma_ctrl) & DMACTRL_START)) { + ret = 0; + break; + } + } + if (ret) { + printf("fotg210: dma busy\n"); + req->req.status = ret; + return ret; + } + + /* 3. DMA target setup */ + if (ep->desc->bEndpointAddress & USB_DIR_IN) + flush_dcache_range((ulong)buf, (ulong)buf + len); + else + invalidate_dcache_range((ulong)buf, (ulong)buf + len); + + writel(virt_to_phys(buf), ®s->dma_addr); + + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + if (ep->id == 0) { + /* Wait until cx/ep0 fifo empty */ + fotg210_cxwait(chip, CXFIFO_CXFIFOE); + udelay(1); + writel(DMAFIFO_CX, ®s->dma_fifo); + } else { + /* Wait until epx fifo empty */ + fotg210_cxwait(chip, CXFIFO_FIFOE(fifo)); + writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo); + } + writel(DMACTRL_LEN(len) | DMACTRL_MEM2FIFO, ®s->dma_ctrl); + } else { + uint32_t blen; + + if (ep->id == 0) { + writel(DMAFIFO_CX, ®s->dma_fifo); + do { + blen = CXFIFO_BYTES(readl(®s->cxfifo)); + } while (blen < len); + } else { + writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo); + blen = FIFOCSR_BYTES(readl(®s->fifocsr[fifo])); + } + len = (len < blen) ? len : blen; + writel(DMACTRL_LEN(len) | DMACTRL_FIFO2MEM, ®s->dma_ctrl); + } + + /* 4. DMA start */ + setbits_le32(®s->dma_ctrl, DMACTRL_START); + + /* 5. DMA wait */ + ret = -EBUSY; + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + tmp = readl(®s->gisr2); + /* DMA complete */ + if (tmp & GISR2_DMAFIN) { + ret = 0; + break; + } + /* DMA error */ + if (tmp & GISR2_DMAERR) { + printf("fotg210: dma error\n"); + break; + } + /* resume, suspend, reset */ + if (tmp & (GISR2_RESUME | GISR2_SUSPEND | GISR2_RESET)) { + printf("fotg210: dma reset by host\n"); + break; + } + } + + /* 7. DMA target reset */ + if (ret) + writel(DMACTRL_ABORT | DMACTRL_CLRFF, ®s->dma_ctrl); + + writel(0, ®s->gisr2); + writel(0, ®s->dma_fifo); + + req->req.status = ret; + if (!ret) + req->req.actual += len; + else + printf("fotg210: ep%d dma error(code=%d)\n", ep->id, ret); + + return len; +} + +/* + * result of setup packet + */ +#define CX_IDLE 0 +#define CX_FINISH 1 +#define CX_STALL 2 + +static void fotg210_setup(struct fotg210_chip *chip) +{ + int id, ret = CX_IDLE; + uint32_t tmp[2]; + struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)tmp; + struct fotg210_regs *regs = chip->regs; + + /* + * If this is the first Cx 8 byte command, + * we can now query USB mode (high/full speed; USB 2.0/USB 1.0) + */ + if (chip->state == USB_STATE_POWERED) { + chip->state = USB_STATE_DEFAULT; + if (readl(®s->otgcsr) & OTGCSR_DEV_B) { + /* Mini-B */ + if (readl(®s->dev_ctrl) & DEVCTRL_HS) { + puts("fotg210: HS\n"); + chip->gadget.speed = USB_SPEED_HIGH; + /* SOF mask timer = 1100 ticks */ + writel(SOFMTR_TMR(1100), ®s->sof_mtr); + } else { + puts("fotg210: FS\n"); + chip->gadget.speed = USB_SPEED_FULL; + /* SOF mask timer = 10000 ticks */ + writel(SOFMTR_TMR(10000), ®s->sof_mtr); + } + } else { + printf("fotg210: mini-A?\n"); + } + } + + /* switch data port to ep0 */ + writel(DMAFIFO_CX, ®s->dma_fifo); + /* fetch 8 bytes setup packet */ + tmp[0] = readl(®s->ep0_data); + tmp[1] = readl(®s->ep0_data); + /* release data port */ + writel(0, ®s->dma_fifo); + + if (req->bRequestType & USB_DIR_IN) + ep0_desc.bEndpointAddress = USB_DIR_IN; + else + ep0_desc.bEndpointAddress = USB_DIR_OUT; + + ret = CX_IDLE; + + if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (req->bRequest) { + case USB_REQ_SET_CONFIGURATION: + debug("fotg210: set_cfg(%d)\n", req->wValue & 0x00FF); + if (!(req->wValue & 0x00FF)) { + chip->state = USB_STATE_ADDRESS; + writel(chip->addr, ®s->dev_addr); + } else { + chip->state = USB_STATE_CONFIGURED; + writel(chip->addr | DEVADDR_CONF, + ®s->dev_addr); + } + ret = CX_IDLE; + break; + + case USB_REQ_SET_ADDRESS: + debug("fotg210: set_addr(0x%04X)\n", req->wValue); + chip->state = USB_STATE_ADDRESS; + chip->addr = req->wValue & DEVADDR_ADDR_MASK; + ret = CX_FINISH; + writel(chip->addr, ®s->dev_addr); + break; + + case USB_REQ_CLEAR_FEATURE: + debug("fotg210: clr_feature(%d, %d)\n", + req->bRequestType & 0x03, req->wValue); + switch (req->wValue) { + case 0: /* [Endpoint] halt */ + ep_reset(chip, req->wIndex); + ret = CX_FINISH; + break; + case 1: /* [Device] remote wake-up */ + case 2: /* [Device] test mode */ + default: + ret = CX_STALL; + break; + } + break; + + case USB_REQ_SET_FEATURE: + debug("fotg210: set_feature(%d, %d)\n", + req->wValue, req->wIndex & 0xf); + switch (req->wValue) { + case 0: /* Endpoint Halt */ + id = req->wIndex & 0xf; + setbits_le32(®s->iep[id - 1], IEP_STALL); + setbits_le32(®s->oep[id - 1], OEP_STALL); + ret = CX_FINISH; + break; + case 1: /* Remote Wakeup */ + case 2: /* Test Mode */ + default: + ret = CX_STALL; + break; + } + break; + + case USB_REQ_GET_STATUS: + debug("fotg210: get_status\n"); + ret = CX_STALL; + break; + + case USB_REQ_SET_DESCRIPTOR: + debug("fotg210: set_descriptor\n"); + ret = CX_STALL; + break; + + case USB_REQ_SYNCH_FRAME: + debug("fotg210: sync frame\n"); + ret = CX_STALL; + break; + } + } /* if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) */ + + if (ret == CX_IDLE && chip->driver->setup) { + if (chip->driver->setup(&chip->gadget, req) < 0) + ret = CX_STALL; + else + ret = CX_FINISH; + } + + switch (ret) { + case CX_FINISH: + setbits_le32(®s->cxfifo, CXFIFO_CXFIN); + break; + + case CX_STALL: + setbits_le32(®s->cxfifo, CXFIFO_CXSTALL | CXFIFO_CXFIN); + printf("fotg210: cx_stall!\n"); + break; + + case CX_IDLE: + debug("fotg210: cx_idle?\n"); + default: + break; + } +} + +/* + * fifo - FIFO id + * zlp - zero length packet + */ +static void fotg210_recv(struct fotg210_chip *chip, int ep_id) +{ + struct fotg210_regs *regs = chip->regs; + struct fotg210_ep *ep = chip->ep + ep_id; + struct fotg210_request *req; + int len; + + if (ep->stopped || (ep->desc->bEndpointAddress & USB_DIR_IN)) { + printf("fotg210: ep%d recv, invalid!\n", ep->id); + return; + } + + if (list_empty(&ep->queue)) { + printf("fotg210: ep%d recv, drop!\n", ep->id); + return; + } + + req = list_first_entry(&ep->queue, struct fotg210_request, queue); + len = fotg210_dma(ep, req); + if (len < ep->ep.maxpacket || req->req.length <= req->req.actual) { + list_del_init(&req->queue); + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + } + + if (ep->id > 0 && list_empty(&ep->queue)) { + setbits_le32(®s->gimr1, + GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id))); + } +} + +/* + * USB Gadget Layer + */ +static int fotg210_ep_enable( + struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + int id = ep_to_fifo(chip, ep->id); + int in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; + + if (!_ep || !desc + || desc->bDescriptorType != USB_DT_ENDPOINT + || le16_to_cpu(desc->wMaxPacketSize) == 0) { + printf("fotg210: bad ep or descriptor\n"); + return -EINVAL; + } + + ep->desc = desc; + ep->stopped = 0; + + if (in) + setbits_le32(®s->fifomap, FIFOMAP(id, FIFOMAP_IN)); + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + return -EINVAL; + + case USB_ENDPOINT_XFER_ISOC: + setbits_le32(®s->fifocfg, + FIFOCFG(id, FIFOCFG_EN | FIFOCFG_ISOC)); + break; + + case USB_ENDPOINT_XFER_BULK: + setbits_le32(®s->fifocfg, + FIFOCFG(id, FIFOCFG_EN | FIFOCFG_BULK)); + break; + + case USB_ENDPOINT_XFER_INT: + setbits_le32(®s->fifocfg, + FIFOCFG(id, FIFOCFG_EN | FIFOCFG_INTR)); + break; + } + + return 0; +} + +static int fotg210_ep_disable(struct usb_ep *_ep) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + int id = ep_to_fifo(chip, ep->id); + + ep->desc = NULL; + ep->stopped = 1; + + clrbits_le32(®s->fifocfg, FIFOCFG(id, FIFOCFG_CFG_MASK)); + clrbits_le32(®s->fifomap, FIFOMAP(id, FIFOMAP_DIR_MASK)); + + return 0; +} + +static struct usb_request *fotg210_ep_alloc_request( + struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct fotg210_request *req = malloc(sizeof(*req)); + + if (req) { + memset(req, 0, sizeof(*req)); + INIT_LIST_HEAD(&req->queue); + } + return &req->req; +} + +static void fotg210_ep_free_request( + struct usb_ep *_ep, struct usb_request *_req) +{ + struct fotg210_request *req; + + req = container_of(_req, struct fotg210_request, req); + free(req); +} + +static int fotg210_ep_queue( + struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + struct fotg210_request *req; + + req = container_of(_req, struct fotg210_request, req); + if (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue)) { + printf("fotg210: invalid request to ep%d\n", ep->id); + return -EINVAL; + } + + if (!chip || chip->state == USB_STATE_SUSPENDED) { + printf("fotg210: request while chip suspended\n"); + return -EINVAL; + } + + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (req->req.length == 0) { + req->req.status = 0; + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + return 0; + } + + if (ep->id == 0) { + do { + int len = fotg210_dma(ep, req); + if (len < ep->ep.maxpacket) + break; + if (ep->desc->bEndpointAddress & USB_DIR_IN) + udelay(100); + } while (req->req.length > req->req.actual); + } else { + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + do { + int len = fotg210_dma(ep, req); + if (len < ep->ep.maxpacket) + break; + } while (req->req.length > req->req.actual); + } else { + list_add_tail(&req->queue, &ep->queue); + clrbits_le32(®s->gimr1, + GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id))); + } + } + + if (ep->id == 0 || (ep->desc->bEndpointAddress & USB_DIR_IN)) { + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + } + + return 0; +} + +static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_request *req; + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) + return -EINVAL; + + /* remove the request */ + list_del_init(&req->queue); + + /* update status & invoke complete callback */ + if (req->req.status == -EINPROGRESS) { + req->req.status = -ECONNRESET; + if (req->req.complete) + req->req.complete(_ep, &req->req); + } + + return 0; +} + +static int fotg210_ep_halt(struct usb_ep *_ep, int halt) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + int ret = -1; + + debug("fotg210: ep%d halt=%d\n", ep->id, halt); + + /* Endpoint STALL */ + if (ep->id > 0 && ep->id <= CFG_NUM_ENDPOINTS) { + if (halt) { + /* wait until all ep fifo empty */ + fotg210_cxwait(chip, 0xf00); + /* stall */ + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + setbits_le32(®s->iep[ep->id - 1], + IEP_STALL); + } else { + setbits_le32(®s->oep[ep->id - 1], + OEP_STALL); + } + } else { + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + clrbits_le32(®s->iep[ep->id - 1], + IEP_STALL); + } else { + clrbits_le32(®s->oep[ep->id - 1], + OEP_STALL); + } + } + ret = 0; + } + + return ret; +} + +/* + * activate/deactivate link with host. + */ +static void pullup(struct fotg210_chip *chip, int is_on) +{ + struct fotg210_regs *regs = chip->regs; + + if (is_on) { + if (!chip->pullup) { + chip->state = USB_STATE_POWERED; + chip->pullup = 1; + /* enable the chip */ + setbits_le32(®s->dev_ctrl, DEVCTRL_EN); + /* clear unplug bit (BIT0) */ + clrbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG); + } + } else { + chip->state = USB_STATE_NOTATTACHED; + chip->pullup = 0; + chip->addr = 0; + writel(chip->addr, ®s->dev_addr); + /* set unplug bit (BIT0) */ + setbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG); + /* disable the chip */ + clrbits_le32(®s->dev_ctrl, DEVCTRL_EN); + } +} + +static int fotg210_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct fotg210_chip *chip; + + chip = container_of(_gadget, struct fotg210_chip, gadget); + + debug("fotg210: pullup=%d\n", is_on); + + pullup(chip, is_on); + + return 0; +} + +static int fotg210_get_frame(struct usb_gadget *_gadget) +{ + struct fotg210_chip *chip; + struct fotg210_regs *regs; + + chip = container_of(_gadget, struct fotg210_chip, gadget); + regs = chip->regs; + + return SOFFNR_FNR(readl(®s->sof_fnr)); +} + +static struct usb_gadget_ops fotg210_gadget_ops = { + .get_frame = fotg210_get_frame, + .pullup = fotg210_pullup, +}; + +static struct usb_ep_ops fotg210_ep_ops = { + .enable = fotg210_ep_enable, + .disable = fotg210_ep_disable, + .queue = fotg210_ep_queue, + .dequeue = fotg210_ep_dequeue, + .set_halt = fotg210_ep_halt, + .alloc_request = fotg210_ep_alloc_request, + .free_request = fotg210_ep_free_request, +}; + +static struct fotg210_chip controller = { + .regs = (void __iomem *)CONFIG_FOTG210_BASE, + .gadget = { + .name = "fotg210_udc", + .ops = &fotg210_gadget_ops, + .ep0 = &controller.ep[0].ep, + .speed = USB_SPEED_UNKNOWN, + .is_dualspeed = 1, + .is_otg = 0, + .is_a_peripheral = 0, + .b_hnp_enable = 0, + .a_hnp_support = 0, + .a_alt_hnp_support = 0, + }, + .ep[0] = { + .id = 0, + .ep = { + .name = "ep0", + .ops = &fotg210_ep_ops, + }, + .desc = &ep0_desc, + .chip = &controller, + .maxpacket = CFG_EP0_MAX_PACKET_SIZE, + }, + .ep[1] = { + .id = 1, + .ep = { + .name = "ep1", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, + .ep[2] = { + .id = 2, + .ep = { + .name = "ep2", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, + .ep[3] = { + .id = 3, + .ep = { + .name = "ep3", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, + .ep[4] = { + .id = 4, + .ep = { + .name = "ep4", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, +}; + +int usb_gadget_handle_interrupts(void) +{ + struct fotg210_chip *chip = &controller; + struct fotg210_regs *regs = chip->regs; + uint32_t id, st, isr, gisr; + + isr = readl(®s->isr) & (~readl(®s->imr)); + gisr = readl(®s->gisr) & (~readl(®s->gimr)); + if (!(isr & ISR_DEV) || !gisr) + return 0; + + writel(ISR_DEV, ®s->isr); + + /* CX interrupts */ + if (gisr & GISR_GRP0) { + st = readl(®s->gisr0); + /* + * Write 1 and then 0 works for both W1C & RW. + * + * HW v1.11.0+: It's a W1C register (write 1 clear) + * HW v1.10.0-: It's a R/W register (write 0 clear) + */ + writel(st & GISR0_CXABORT, ®s->gisr0); + writel(0, ®s->gisr0); + + if (st & GISR0_CXERR) + printf("fotg210: cmd error\n"); + + if (st & GISR0_CXABORT) + printf("fotg210: cmd abort\n"); + + if (st & GISR0_CXSETUP) /* setup */ + fotg210_setup(chip); + else if (st & GISR0_CXEND) /* command finish */ + setbits_le32(®s->cxfifo, CXFIFO_CXFIN); + } + + /* FIFO interrupts */ + if (gisr & GISR_GRP1) { + st = readl(®s->gisr1); + for (id = 0; id < 4; ++id) { + if (st & GISR1_RX_FIFO(id)) + fotg210_recv(chip, fifo_to_ep(chip, id, 0)); + } + } + + /* Device Status Interrupts */ + if (gisr & GISR_GRP2) { + st = readl(®s->gisr2); + /* + * Write 1 and then 0 works for both W1C & RW. + * + * HW v1.11.0+: It's a W1C register (write 1 clear) + * HW v1.10.0-: It's a R/W register (write 0 clear) + */ + writel(st, ®s->gisr2); + writel(0, ®s->gisr2); + + if (st & GISR2_RESET) + printf("fotg210: reset by host\n"); + else if (st & GISR2_SUSPEND) + printf("fotg210: suspend/removed\n"); + else if (st & GISR2_RESUME) + printf("fotg210: resume\n"); + + /* Errors */ + if (st & GISR2_ISOCERR) + printf("fotg210: iso error\n"); + if (st & GISR2_ISOCABT) + printf("fotg210: iso abort\n"); + if (st & GISR2_DMAERR) + printf("fotg210: dma error\n"); + } + + return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int i, ret = 0; + struct fotg210_chip *chip = &controller; + + if (!driver || !driver->bind || !driver->setup) { + puts("fotg210: bad parameter.\n"); + return -EINVAL; + } + + INIT_LIST_HEAD(&chip->gadget.ep_list); + for (i = 0; i < CFG_NUM_ENDPOINTS + 1; ++i) { + struct fotg210_ep *ep = chip->ep + i; + + ep->ep.maxpacket = ep->maxpacket; + INIT_LIST_HEAD(&ep->queue); + + if (ep->id == 0) { + ep->stopped = 0; + } else { + ep->stopped = 1; + list_add_tail(&ep->ep.ep_list, &chip->gadget.ep_list); + } + } + + if (fotg210_reset(chip)) { + puts("fotg210: reset failed.\n"); + return -EINVAL; + } + + ret = driver->bind(&chip->gadget); + if (ret) { + debug("fotg210: driver->bind() returned %d\n", ret); + return ret; + } + chip->driver = driver; + + return ret; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct fotg210_chip *chip = &controller; + + driver->unbind(&chip->gadget); + chip->driver = NULL; + + pullup(chip, 0); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/g_dnl.c b/qemu/roms/u-boot/drivers/usb/gadget/g_dnl.c new file mode 100644 index 000000000..25611acd6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/g_dnl.c @@ -0,0 +1,264 @@ +/* + * g_dnl.c -- USB Downloader Gadget + * + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "gadget_chips.h" +#include "composite.c" + +/* + * One needs to define the following: + * CONFIG_G_DNL_VENDOR_NUM + * CONFIG_G_DNL_PRODUCT_NUM + * CONFIG_G_DNL_MANUFACTURER + * at e.g. ./include/configs/.h + */ + +#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 2 +/* Index of String Descriptor describing this configuration */ +#define STRING_USBDOWN 2 +/* Index of String serial */ +#define STRING_SERIAL 3 +#define MAX_STRING_SERIAL 32 +/* Number of supported configurations */ +#define CONFIGURATION_NUMBER 1 + +#define DRIVER_VERSION "usb_dnl 2.0" + +static const char product[] = "USB download gadget"; +static char g_dnl_serial[MAX_STRING_SERIAL]; +static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; + +void g_dnl_set_serialnumber(char *s) +{ + memset(g_dnl_serial, 0, MAX_STRING_SERIAL); + if (strlen(s) < MAX_STRING_SERIAL) + strncpy(g_dnl_serial, s, strlen(s)); +} + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ + + .idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), + .iProduct = STRING_PRODUCT, + .iSerialNumber = STRING_SERIAL, + .bNumConfigurations = 1, +}; + +/* + * static strings, in UTF-8 + * IDs for those strings are assigned dynamically at g_dnl_bind() + */ +static struct usb_string g_dnl_string_defs[] = { + {.s = manufacturer}, + {.s = product}, + {.s = g_dnl_serial}, + { } /* end of list */ +}; + +static struct usb_gadget_strings g_dnl_string_tab = { + .language = 0x0409, /* en-us */ + .strings = g_dnl_string_defs, +}; + +static struct usb_gadget_strings *g_dnl_composite_strings[] = { + &g_dnl_string_tab, + NULL, +}; + +static int g_dnl_unbind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + + free(cdev->config); + cdev->config = NULL; + debug("%s: calling usb_gadget_disconnect for " + "controller '%s'\n", __func__, gadget->name); + usb_gadget_disconnect(gadget); + + return 0; +} + +static inline struct g_dnl_bind_callback *g_dnl_bind_callback_first(void) +{ + return ll_entry_start(struct g_dnl_bind_callback, + g_dnl_bind_callbacks); +} + +static inline struct g_dnl_bind_callback *g_dnl_bind_callback_end(void) +{ + return ll_entry_end(struct g_dnl_bind_callback, + g_dnl_bind_callbacks); +} + +static int g_dnl_do_config(struct usb_configuration *c) +{ + const char *s = c->cdev->driver->name; + struct g_dnl_bind_callback *callback = g_dnl_bind_callback_first(); + + debug("%s: configuration: 0x%p composite dev: 0x%p\n", + __func__, c, c->cdev); + + for (; callback != g_dnl_bind_callback_end(); callback++) + if (!strcmp(s, callback->usb_function_name)) + return callback->fptr(c); + return -ENODEV; +} + +static int g_dnl_config_register(struct usb_composite_dev *cdev) +{ + struct usb_configuration *config; + const char *name = "usb_dnload"; + + config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config)); + if (!config) + return -ENOMEM; + + memset(config, 0, sizeof(*config)); + + config->label = name; + config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; + config->bConfigurationValue = CONFIGURATION_NUMBER; + config->iConfiguration = STRING_USBDOWN; + config->bind = g_dnl_do_config; + + return usb_add_config(cdev, config); +} + +__weak +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{ + return 0; +} + +__weak int g_dnl_get_board_bcd_device_number(int gcnum) +{ + return gcnum; +} + +__weak int g_dnl_board_usb_cable_connected(void) +{ + return -EOPNOTSUPP; +} + +static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + int gcnum; + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum > 0) + gcnum += 0x200; + + return g_dnl_get_board_bcd_device_number(gcnum); +} + +static int g_dnl_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + int id, ret; + int gcnum; + + debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); + + id = usb_string_id(cdev); + + if (id < 0) + return id; + g_dnl_string_defs[0].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + + g_dnl_string_defs[1].id = id; + device_desc.iProduct = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + + g_dnl_string_defs[2].id = id; + device_desc.iSerialNumber = id; + + g_dnl_bind_fixup(&device_desc, cdev->driver->name); + ret = g_dnl_config_register(cdev); + if (ret) + goto error; + + gcnum = g_dnl_get_bcd_device_number(cdev); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(gcnum); + else { + debug("%s: controller '%s' not recognized\n", + __func__, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + debug("%s: calling usb_gadget_connect for " + "controller '%s'\n", __func__, gadget->name); + usb_gadget_connect(gadget); + + return 0; + + error: + g_dnl_unbind(cdev); + return -ENOMEM; +} + +static struct usb_composite_driver g_dnl_driver = { + .name = NULL, + .dev = &device_desc, + .strings = g_dnl_composite_strings, + + .bind = g_dnl_bind, + .unbind = g_dnl_unbind, +}; + +/* + * NOTICE: + * Registering via USB function name won't be necessary after rewriting + * g_dnl to support multiple USB functions. + */ +int g_dnl_register(const char *name) +{ + int ret; + + debug("%s: g_dnl_driver.name = %s\n", __func__, name); + g_dnl_driver.name = name; + + ret = usb_composite_register(&g_dnl_driver); + if (ret) { + printf("%s: failed!, error: %d\n", __func__, ret); + return ret; + } + return 0; +} + +void g_dnl_unregister(void) +{ + usb_composite_unregister(&g_dnl_driver); +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/gadget_chips.h b/qemu/roms/u-boot/drivers/usb/gadget/gadget_chips.h new file mode 100644 index 000000000..cc94771e3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/gadget_chips.h @@ -0,0 +1,227 @@ +/* + * USB device controllers have lots of quirks. Use these macros in + * gadget drivers or other code that needs to deal with them, and which + * autoconfigures instead of using early binding to the hardware. + * + * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by + * some config file that gets updated as new hardware is supported. + * (And avoiding all runtime comparisons in typical one-choice configs!) + * + * NOTE: some of these controller drivers may not be available yet. + * Some are available on 2.4 kernels; several are available, but not + * yet pushed in the 2.6 mainline tree. + * + * Ported to U-boot by: Thomas Smits and + * Remy Bohmer + */ +#ifdef CONFIG_USB_GADGET_NET2280 +#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name)) +#else +#define gadget_is_net2280(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_AMD5536UDC +#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name)) +#else +#define gadget_is_amd5536udc(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_DUMMY_HCD +#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name)) +#else +#define gadget_is_dummy(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_PXA2XX +#define gadget_is_pxa(g) (!strcmp("pxa2xx_udc", (g)->name)) +#else +#define gadget_is_pxa(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_GOKU +#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name)) +#else +#define gadget_is_goku(g) 0 +#endif + +/* SH3 UDC -- not yet ported 2.4 --> 2.6 */ +#ifdef CONFIG_USB_GADGET_SUPERH +#define gadget_is_sh(g) (!strcmp("sh_udc", (g)->name)) +#else +#define gadget_is_sh(g) 0 +#endif + +/* not yet stable on 2.6 (would help "original Zaurus") */ +#ifdef CONFIG_USB_GADGET_SA1100 +#define gadget_is_sa1100(g) (!strcmp("sa1100_udc", (g)->name)) +#else +#define gadget_is_sa1100(g) 0 +#endif + +/* handhelds.org tree (?) */ +#ifdef CONFIG_USB_GADGET_MQ11XX +#define gadget_is_mq11xx(g) (!strcmp("mq11xx_udc", (g)->name)) +#else +#define gadget_is_mq11xx(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_OMAP +#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name)) +#else +#define gadget_is_omap(g) 0 +#endif + +/* not yet ported 2.4 --> 2.6 */ +#ifdef CONFIG_USB_GADGET_N9604 +#define gadget_is_n9604(g) (!strcmp("n9604_udc", (g)->name)) +#else +#define gadget_is_n9604(g) 0 +#endif + +/* various unstable versions available */ +#ifdef CONFIG_USB_GADGET_PXA27X +#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name)) +#else +#define gadget_is_pxa27x(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_ATMEL_USBA +#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name)) +#else +#define gadget_is_atmel_usba(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_S3C2410 +#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name)) +#else +#define gadget_is_s3c2410(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_AT91 +#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name)) +#else +#define gadget_is_at91(g) 0 +#endif + +/* status unclear */ +#ifdef CONFIG_USB_GADGET_IMX +#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name)) +#else +#define gadget_is_imx(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_FSL_USB2 +#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name)) +#else +#define gadget_is_fsl_usb2(g) 0 +#endif + +/* Mentor high speed function controller */ +/* from Montavista kernel (?) */ +#ifdef CONFIG_USB_GADGET_MUSBHSFC +#define gadget_is_musbhsfc(g) (!strcmp("musbhsfc_udc", (g)->name)) +#else +#define gadget_is_musbhsfc(g) 0 +#endif + +/* Mentor high speed "dual role" controller, in peripheral role */ +#ifdef CONFIG_MUSB_GADGET +#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name)) +#else +#define gadget_is_musbhdrc(g) 0 +#endif + +/* from Montavista kernel (?) */ +#ifdef CONFIG_USB_GADGET_MPC8272 +#define gadget_is_mpc8272(g) (!strcmp("mpc8272_udc", (g)->name)) +#else +#define gadget_is_mpc8272(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_M66592 +#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name)) +#else +#define gadget_is_m66592(g) 0 +#endif + +#ifdef CONFIG_CI_UDC +#define gadget_is_ci(g) (!strcmp("ci_udc", (g)->name)) +#else +#define gadget_is_ci(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_FOTG210 +#define gadget_is_fotg210(g) (!strcmp("fotg210_udc", (g)->name)) +#else +#define gadget_is_fotg210(g) 0 +#endif + +/* + * CONFIG_USB_GADGET_SX2 + * CONFIG_USB_GADGET_AU1X00 + * ... + */ + +/** + * usb_gadget_controller_number - support bcdDevice id convention + * @gadget: the controller being driven + * + * Return a 2-digit BCD value associated with the peripheral controller, + * suitable for use as part of a bcdDevice value, or a negative error code. + * + * NOTE: this convention is purely optional, and has no meaning in terms of + * any USB specification. If you want to use a different convention in your + * gadget driver firmware -- maybe a more formal revision ID -- feel free. + * + * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!) + * to change their behavior accordingly. For example it might help avoiding + * some chip bug. + */ +static inline int usb_gadget_controller_number(struct usb_gadget *gadget) +{ + if (gadget_is_net2280(gadget)) + return 0x01; + else if (gadget_is_dummy(gadget)) + return 0x02; + else if (gadget_is_pxa(gadget)) + return 0x03; + else if (gadget_is_sh(gadget)) + return 0x04; + else if (gadget_is_sa1100(gadget)) + return 0x05; + else if (gadget_is_goku(gadget)) + return 0x06; + else if (gadget_is_mq11xx(gadget)) + return 0x07; + else if (gadget_is_omap(gadget)) + return 0x08; + else if (gadget_is_n9604(gadget)) + return 0x09; + else if (gadget_is_pxa27x(gadget)) + return 0x10; + else if (gadget_is_s3c2410(gadget)) + return 0x11; + else if (gadget_is_at91(gadget)) + return 0x12; + else if (gadget_is_imx(gadget)) + return 0x13; + else if (gadget_is_musbhsfc(gadget)) + return 0x14; + else if (gadget_is_musbhdrc(gadget)) + return 0x15; + else if (gadget_is_mpc8272(gadget)) + return 0x16; + else if (gadget_is_atmel_usba(gadget)) + return 0x17; + else if (gadget_is_fsl_usb2(gadget)) + return 0x18; + else if (gadget_is_amd5536udc(gadget)) + return 0x19; + else if (gadget_is_m66592(gadget)) + return 0x20; + else if (gadget_is_ci(gadget)) + return 0x21; + else if (gadget_is_fotg210(gadget)) + return 0x22; + return -ENOENT; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/mpc8xx_udc.c b/qemu/roms/u-boot/drivers/usb/gadget/mpc8xx_udc.c new file mode 100644 index 000000000..7f72972dc --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/mpc8xx_udc.c @@ -0,0 +1,1386 @@ +/* + * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit + * bodonoghue@CodeHermit.ie + * + * References + * DasUBoot/drivers/usb/gadget/omap1510_udc.c, for design and implementation + * ideas. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Notes : + * 1. #define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX + * packet to force the USB re-transmit protocol. + * + * 2. #define __DEBUG_UDC__ to switch on debug tracing to serial console + * be careful that tracing doesn't create Hiesen-bugs with respect to + * response timeouts to control requests. + * + * 3. This driver should be able to support any higher level driver that + * that wants to do either of the two standard UDC implementations + * Control-Bulk-Interrupt or Bulk-IN/Bulk-Out standards. Hence + * gserial and cdc_acm should work with this code. + * + * 4. NAK events never actually get raised at all, the documentation + * is just wrong ! + * + * 5. For some reason, cbd_datlen is *always* +2 the value it should be. + * this means that having an RX cbd of 16 bytes is not possible, since + * the same size is reported for 14 bytes received as 16 bytes received + * until we can find out why this happens, RX cbds must be limited to 8 + * bytes. TODO: check errata for this behaviour. + * + * 6. Right now this code doesn't support properly powering up with the USB + * cable attached to the USB host my development board the Adder87x doesn't + * have a pull-up fitted to allow this, so it is necessary to power the + * board and *then* attached the USB cable to the host. However somebody + * with a different design in their board may be able to keep the cable + * constantly connected and simply enable/disable a pull-up re + * figure 31.1 in MPC885RM.pdf instead of having to power up the board and + * then attach the cable ! + * + */ +#include +#include +#include +#include +#include +#include + +#include "ep0.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define ERR(fmt, args...)\ + serial_printf("ERROR : [%s] %s:%d: "fmt,\ + __FILE__,__FUNCTION__,__LINE__, ##args) +#ifdef __DEBUG_UDC__ +#define DBG(fmt,args...)\ + serial_printf("[%s] %s:%d: "fmt,\ + __FILE__,__FUNCTION__,__LINE__, ##args) +#else +#define DBG(fmt,args...) +#endif + +/* Static Data */ +#ifdef __SIMULATE_ERROR__ +static char err_poison_test = 0; +#endif +static struct mpc8xx_ep ep_ref[MAX_ENDPOINTS]; +static u32 address_base = STATE_NOT_READY; +static mpc8xx_udc_state_t udc_state = 0; +static struct usb_device_instance *udc_device = 0; +static volatile usb_epb_t *endpoints[MAX_ENDPOINTS]; +static volatile cbd_t *tx_cbd[TX_RING_SIZE]; +static volatile cbd_t *rx_cbd[RX_RING_SIZE]; +static volatile immap_t *immr = 0; +static volatile cpm8xx_t *cp = 0; +static volatile usb_pram_t *usb_paramp = 0; +static volatile usb_t *usbp = 0; +static int rx_ct = 0; +static int tx_ct = 0; + +/* Static Function Declarations */ +static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, + usb_device_state_t final); +static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, + usb_device_state_t final); +static void mpc8xx_udc_stall (unsigned int ep); +static void mpc8xx_udc_flush_tx_fifo (int epid); +static void mpc8xx_udc_flush_rx_fifo (void); +static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp); +static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi, + struct urb *tx_urb); +static void mpc8xx_udc_dump_request (struct usb_device_request *request); +static void mpc8xx_udc_clock_init (volatile immap_t * immr, + volatile cpm8xx_t * cp); +static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi); +static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp); +static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp); +static void mpc8xx_udc_cbd_init (void); +static void mpc8xx_udc_endpoint_init (void); +static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size); +static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment); +static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp); +static void mpc8xx_udc_set_nak (unsigned int ep); +static short mpc8xx_udc_handle_txerr (void); +static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid); + +/****************************************************************************** + Global Linkage + *****************************************************************************/ + +/* udc_init + * + * Do initial bus gluing + */ +int udc_init (void) +{ + /* Init various pointers */ + immr = (immap_t *) CONFIG_SYS_IMMR; + cp = (cpm8xx_t *) & (immr->im_cpm); + usb_paramp = (usb_pram_t *) & (cp->cp_dparam[PROFF_USB]); + usbp = (usb_t *) & (cp->cp_scc[0]); + + memset (ep_ref, 0x00, (sizeof (struct mpc8xx_ep) * MAX_ENDPOINTS)); + + udc_device = 0; + udc_state = STATE_NOT_READY; + + usbp->usmod = 0x00; + usbp->uscom = 0; + + /* Set USB Frame #0, Respond at Address & Get a clock source */ + usbp->usaddr = 0x00; + mpc8xx_udc_clock_init (immr, cp); + + /* PA15, PA14 as perhiperal USBRXD and USBOE */ + immr->im_ioport.iop_padir &= ~0x0003; + immr->im_ioport.iop_papar |= 0x0003; + + /* PC11/PC10 as peripheral USBRXP USBRXN */ + immr->im_ioport.iop_pcso |= 0x0030; + + /* PC7/PC6 as perhiperal USBTXP and USBTXN */ + immr->im_ioport.iop_pcdir |= 0x0300; + immr->im_ioport.iop_pcpar |= 0x0300; + + /* Set the base address */ + address_base = (u32) (cp->cp_dpmem + CPM_USB_BASE); + + /* Initialise endpoints and circular buffers */ + mpc8xx_udc_endpoint_init (); + mpc8xx_udc_cbd_init (); + + /* Assign allocated Dual Port Endpoint descriptors */ + usb_paramp->ep0ptr = (u32) endpoints[0]; + usb_paramp->ep1ptr = (u32) endpoints[1]; + usb_paramp->ep2ptr = (u32) endpoints[2]; + usb_paramp->ep3ptr = (u32) endpoints[3]; + usb_paramp->frame_n = 0; + + DBG ("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n", + usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr, + usb_paramp->ep3ptr); + + return 0; +} + +/* udc_irq + * + * Poll for whatever events may have occured + */ +void udc_irq (void) +{ + int epid = 0; + volatile cbd_t *rx_cbdp = 0; + volatile cbd_t *rx_cbdp_base = 0; + + if (udc_state != STATE_READY) { + return; + } + + if (usbp->usber & USB_E_BSY) { + /* This shouldn't happen. If it does then it's a bug ! */ + usbp->usber |= USB_E_BSY; + mpc8xx_udc_flush_rx_fifo (); + } + + /* Scan all RX/Bidirectional Endpoints for RX data. */ + for (epid = 0; epid < MAX_ENDPOINTS; epid++) { + if (!ep_ref[epid].prx) { + continue; + } + rx_cbdp = rx_cbdp_base = ep_ref[epid].prx; + + do { + if (!(rx_cbdp->cbd_sc & RX_BD_E)) { + + if (rx_cbdp->cbd_sc & 0x1F) { + /* Corrupt data discard it. + * Controller has NAK'd this packet. + */ + mpc8xx_udc_clear_rxbd (rx_cbdp); + + } else { + if (!epid) { + mpc8xx_udc_ep0_rx (rx_cbdp); + + } else { + /* Process data */ + mpc8xx_udc_set_nak (epid); + mpc8xx_udc_epn_rx (epid, rx_cbdp); + mpc8xx_udc_clear_rxbd (rx_cbdp); + } + } + + /* Advance RX CBD pointer */ + mpc8xx_udc_advance_rx (&rx_cbdp, epid); + ep_ref[epid].prx = rx_cbdp; + } else { + /* Advance RX CBD pointer */ + mpc8xx_udc_advance_rx (&rx_cbdp, epid); + } + + } while (rx_cbdp != rx_cbdp_base); + } + + /* Handle TX events as appropiate, the correct place to do this is + * in a tx routine. Perhaps TX on epn was pre-empted by ep0 + */ + + if (usbp->usber & USB_E_TXB) { + usbp->usber |= USB_E_TXB; + } + + if (usbp->usber & (USB_TX_ERRMASK)) { + mpc8xx_udc_handle_txerr (); + } + + /* Switch to the default state, respond at the default address */ + if (usbp->usber & USB_E_RESET) { + usbp->usber |= USB_E_RESET; + usbp->usaddr = 0x00; + udc_device->device_state = STATE_DEFAULT; + } + + /* if(usbp->usber&USB_E_IDLE){ + We could suspend here ! + usbp->usber|=USB_E_IDLE; + DBG("idle state change\n"); + } + if(usbp->usbs){ + We could resume here when IDLE is deasserted ! + Not worth doing, so long as we are self powered though. + } + */ + + return; +} + +/* udc_endpoint_write + * + * Write some data to an endpoint + */ +int udc_endpoint_write (struct usb_endpoint_instance *epi) +{ + int ep = 0; + short epid = 1, unnak = 0, ret = 0; + + if (udc_state != STATE_READY) { + ERR ("invalid udc_state != STATE_READY!\n"); + return -1; + } + + if (!udc_device || !epi) { + return -1; + } + + if (udc_device->device_state != STATE_CONFIGURED) { + return -1; + } + + ep = epi->endpoint_address & 0x03; + if (ep >= MAX_ENDPOINTS) { + return -1; + } + + /* Set NAK for all RX endpoints during TX */ + for (epid = 1; epid < MAX_ENDPOINTS; epid++) { + + /* Don't set NAK on DATA IN/CONTROL endpoints */ + if (ep_ref[epid].sc & USB_DIR_IN) { + continue; + } + + if (!(usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK))) { + unnak |= 1 << epid; + } + + mpc8xx_udc_set_nak (epid); + } + + mpc8xx_udc_init_tx (&udc_device->bus->endpoint_array[ep], + epi->tx_urb); + ret = mpc8xx_udc_ep_tx (&udc_device->bus->endpoint_array[ep]); + + /* Remove temporary NAK */ + for (epid = 1; epid < MAX_ENDPOINTS; epid++) { + if (unnak & (1 << epid)) { + udc_unset_nak (epid); + } + } + + return ret; +} + +/* mpc8xx_udc_assign_urb + * + * Associate a given urb to an endpoint TX or RX transmit/receive buffers + */ +static int mpc8xx_udc_assign_urb (int ep, char direction) +{ + struct usb_endpoint_instance *epi = 0; + + if (ep >= MAX_ENDPOINTS) { + goto err; + } + epi = &udc_device->bus->endpoint_array[ep]; + if (!epi) { + goto err; + } + + if (!ep_ref[ep].urb) { + ep_ref[ep].urb = usbd_alloc_urb (udc_device, udc_device->bus->endpoint_array); + if (!ep_ref[ep].urb) { + goto err; + } + } else { + ep_ref[ep].urb->actual_length = 0; + } + + switch (direction) { + case USB_DIR_IN: + epi->tx_urb = ep_ref[ep].urb; + break; + case USB_DIR_OUT: + epi->rcv_urb = ep_ref[ep].urb; + break; + default: + goto err; + } + return 0; + + err: + udc_state = STATE_ERROR; + return -1; +} + +/* udc_setup_ep + * + * Associate U-Boot software endpoints to mpc8xx endpoint parameter ram + * Isochronous endpoints aren't yet supported! + */ +void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *epi) +{ + uchar direction = 0; + int ep_attrib = 0; + + if (epi && (ep < MAX_ENDPOINTS)) { + + if (ep == 0) { + if (epi->rcv_attributes != USB_ENDPOINT_XFER_CONTROL + || epi->tx_attributes != + USB_ENDPOINT_XFER_CONTROL) { + + /* ep0 must be a control endpoint */ + udc_state = STATE_ERROR; + return; + + } + if (!(ep_ref[ep].sc & EP_ATTACHED)) { + mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize, + epi->rcv_packetSize); + } + usbp->usep[ep] = 0x0000; + return; + } + + if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) { + + direction = 1; + ep_attrib = epi->tx_attributes; + epi->rcv_packetSize = 0; + ep_ref[ep].sc |= USB_DIR_IN; + } else { + + direction = 0; + ep_attrib = epi->rcv_attributes; + epi->tx_packetSize = 0; + ep_ref[ep].sc &= ~USB_DIR_IN; + } + + if (mpc8xx_udc_assign_urb (ep, epi->endpoint_address + & USB_ENDPOINT_DIR_MASK)) { + return; + } + + switch (ep_attrib) { + case USB_ENDPOINT_XFER_CONTROL: + if (!(ep_ref[ep].sc & EP_ATTACHED)) { + mpc8xx_udc_cbd_attach (ep, + epi->tx_packetSize, + epi->rcv_packetSize); + } + usbp->usep[ep] = ep << 12; + epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb; + + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + if (!(ep_ref[ep].sc & EP_ATTACHED)) { + if (direction) { + mpc8xx_udc_cbd_attach (ep, + epi->tx_packetSize, + 0); + } else { + mpc8xx_udc_cbd_attach (ep, + 0, + epi->rcv_packetSize); + } + } + usbp->usep[ep] = (ep << 12) | ((ep_attrib) << 8); + + break; + case USB_ENDPOINT_XFER_ISOC: + default: + serial_printf ("Error endpoint attrib %d>3\n", ep_attrib); + udc_state = STATE_ERROR; + break; + } + } + +} + +/* udc_connect + * + * Move state, switch on the USB + */ +void udc_connect (void) +{ + /* Enable pull-up resistor on D+ + * TODO: fit a pull-up resistor to drive SE0 for > 2.5us + */ + + if (udc_state != STATE_ERROR) { + udc_state = STATE_READY; + usbp->usmod |= USMOD_EN; + } +} + +/* udc_disconnect + * + * Disconnect is not used but, is included for completeness + */ +void udc_disconnect (void) +{ + /* Disable pull-up resistor on D- + * TODO: fix a pullup resistor to control this + */ + + if (udc_state != STATE_ERROR) { + udc_state = STATE_NOT_READY; + } + usbp->usmod &= ~USMOD_EN; +} + +/* udc_enable + * + * Grab an EP0 URB, register interest in a subset of USB events + */ +void udc_enable (struct usb_device_instance *device) +{ + if (udc_state == STATE_ERROR) { + return; + } + + udc_device = device; + + if (!ep_ref[0].urb) { + ep_ref[0].urb = usbd_alloc_urb (device, device->bus->endpoint_array); + } + + /* Register interest in all events except SOF, enable transceiver */ + usbp->usber = 0x03FF; + usbp->usbmr = 0x02F7; + + return; +} + +/* udc_disable + * + * disable the currently hooked device + */ +void udc_disable (void) +{ + int i = 0; + + if (udc_state == STATE_ERROR) { + DBG ("Won't disable UDC. udc_state==STATE_ERROR !\n"); + return; + } + + udc_device = 0; + + for (; i < MAX_ENDPOINTS; i++) { + if (ep_ref[i].urb) { + usbd_dealloc_urb (ep_ref[i].urb); + ep_ref[i].urb = 0; + } + } + + usbp->usbmr = 0x00; + usbp->usmod = ~USMOD_EN; + udc_state = STATE_NOT_READY; +} + +/* udc_startup_events + * + * Enable the specified device + */ +void udc_startup_events (struct usb_device_instance *device) +{ + udc_enable (device); + if (udc_state == STATE_READY) { + usbd_device_event_irq (device, DEVICE_CREATE, 0); + } +} + +/* udc_set_nak + * + * Allow upper layers to signal lower layers should not accept more RX data + * + */ +void udc_set_nak (int epid) +{ + if (epid) { + mpc8xx_udc_set_nak (epid); + } +} + +/* udc_unset_nak + * + * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint. + * Switch off NAKing on this endpoint to accept more data output from host. + * + */ +void udc_unset_nak (int epid) +{ + if (epid > MAX_ENDPOINTS) { + return; + } + + if (usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK)) { + usbp->usep[epid] &= ~(USEP_THS_NAK | USEP_RHS_NAK); + __asm__ ("eieio"); + } +} + +/****************************************************************************** + Static Linkage +******************************************************************************/ + +/* udc_state_transition_up + * udc_state_transition_down + * + * Helper functions to implement device state changes. The device states and + * the events that transition between them are: + * + * STATE_ATTACHED + * || /\ + * \/ || + * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET + * || /\ + * \/ || + * STATE_POWERED + * || /\ + * \/ || + * DEVICE_RESET DEVICE_POWER_INTERRUPTION + * || /\ + * \/ || + * STATE_DEFAULT + * || /\ + * \/ || + * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET + * || /\ + * \/ || + * STATE_ADDRESSED + * || /\ + * \/ || + * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED + * || /\ + * \/ || + * STATE_CONFIGURED + * + * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED + * to STATE_CONFIGURED) from the specified initial state to the specified final + * state, passing through each intermediate state on the way. If the initial + * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then + * no state transitions will take place. + * + * udc_state_transition_down transitions down (in the direction from + * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the + * specified final state, passing through each intermediate state on the way. + * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final + * state, then no state transitions will take place. + * + */ + +static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, + usb_device_state_t final) +{ + if (initial < final) { + switch (initial) { + case STATE_ATTACHED: + usbd_device_event_irq (udc_device, + DEVICE_HUB_CONFIGURED, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq (udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq (udc_device, + DEVICE_ADDRESS_ASSIGNED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq (udc_device, DEVICE_CONFIGURED, + 0); + case STATE_CONFIGURED: + break; + default: + break; + } + } +} + +static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, + usb_device_state_t final) +{ + if (initial > final) { + switch (initial) { + case STATE_CONFIGURED: + usbd_device_event_irq (udc_device, + DEVICE_DE_CONFIGURED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq (udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq (udc_device, + DEVICE_POWER_INTERRUPTION, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq (udc_device, DEVICE_HUB_RESET, + 0); + case STATE_ATTACHED: + break; + default: + break; + } + } +} + +/* mpc8xx_udc_stall + * + * Force returning of STALL tokens on the given endpoint. Protocol or function + * STALL conditions are permissable here + */ +static void mpc8xx_udc_stall (unsigned int ep) +{ + usbp->usep[ep] |= STALL_BITMASK; +} + +/* mpc8xx_udc_set_nak + * + * Force returning of NAK responses for the given endpoint as a kind of very + * simple flow control + */ +static void mpc8xx_udc_set_nak (unsigned int ep) +{ + usbp->usep[ep] |= NAK_BITMASK; + __asm__ ("eieio"); +} + +/* mpc8xx_udc_handle_txerr + * + * Handle errors relevant to TX. Return a status code to allow calling + * indicative of what if anything happened + */ +static short mpc8xx_udc_handle_txerr () +{ + short ep = 0, ret = 0; + + for (; ep < TX_RING_SIZE; ep++) { + if (usbp->usber & (0x10 << ep)) { + + /* Timeout or underrun */ + if (tx_cbd[ep]->cbd_sc & 0x06) { + ret = 1; + mpc8xx_udc_flush_tx_fifo (ep); + + } else { + if (usbp->usep[ep] & STALL_BITMASK) { + if (!ep) { + usbp->usep[ep] &= ~STALL_BITMASK; + } + } /* else NAK */ + } + usbp->usber |= (0x10 << ep); + } + } + return ret; +} + +/* mpc8xx_udc_advance_rx + * + * Advance cbd rx + */ +static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid) +{ + if ((*rx_cbdp)->cbd_sc & RX_BD_W) { + *rx_cbdp = (volatile cbd_t *) (endpoints[epid]->rbase + CONFIG_SYS_IMMR); + + } else { + (*rx_cbdp)++; + } +} + + +/* mpc8xx_udc_flush_tx_fifo + * + * Flush a given TX fifo. Assumes one tx cbd per endpoint + */ +static void mpc8xx_udc_flush_tx_fifo (int epid) +{ + volatile cbd_t *tx_cbdp = 0; + + if (epid > MAX_ENDPOINTS) { + return; + } + + /* TX stop */ + immr->im_cpm.cp_cpcr = ((epid << 2) | 0x1D01); + __asm__ ("eieio"); + while (immr->im_cpm.cp_cpcr & 0x01); + + usbp->uscom = 0x40 | 0; + + /* reset ring */ + tx_cbdp = (cbd_t *) (endpoints[epid]->tbptr + CONFIG_SYS_IMMR); + tx_cbdp->cbd_sc = (TX_BD_I | TX_BD_W); + + + endpoints[epid]->tptr = endpoints[epid]->tbase; + endpoints[epid]->tstate = 0x00; + endpoints[epid]->tbcnt = 0x00; + + /* TX start */ + immr->im_cpm.cp_cpcr = ((epid << 2) | 0x2D01); + __asm__ ("eieio"); + while (immr->im_cpm.cp_cpcr & 0x01); + + return; +} + +/* mpc8xx_udc_flush_rx_fifo + * + * For the sake of completeness of the namespace, it seems like + * a good-design-decision (tm) to include mpc8xx_udc_flush_rx_fifo(); + * If RX_BD_E is true => a driver bug either here or in an upper layer + * not polling frequently enough. If RX_BD_E is true we have told the host + * we have accepted data but, the CPM found it had no-where to put that data + * which needless to say would be a bad thing. + */ +static void mpc8xx_udc_flush_rx_fifo () +{ + int i = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) { + ERR ("buf %p used rx data len = 0x%x sc=0x%x!\n", + rx_cbd[i], rx_cbd[i]->cbd_datlen, + rx_cbd[i]->cbd_sc); + + } + } + ERR ("BUG : Input over-run\n"); +} + +/* mpc8xx_udc_clear_rxbd + * + * Release control of RX CBD to CP. + */ +static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp) +{ + rx_cbdp->cbd_datlen = 0x0000; + rx_cbdp->cbd_sc = ((rx_cbdp->cbd_sc & RX_BD_W) | (RX_BD_E | RX_BD_I)); + __asm__ ("eieio"); +} + +/* mpc8xx_udc_tx_irq + * + * Parse for tx timeout, control RX or USB reset/busy conditions + * Return -1 on timeout, -2 on fatal error, else return zero + */ +static int mpc8xx_udc_tx_irq (int ep) +{ + int i = 0; + + if (usbp->usber & (USB_TX_ERRMASK)) { + if (mpc8xx_udc_handle_txerr ()) { + /* Timeout, controlling function must retry send */ + return -1; + } + } + + if (usbp->usber & (USB_E_RESET | USB_E_BSY)) { + /* Fatal, abandon TX transaction */ + return -2; + } + + if (usbp->usber & USB_E_RXB) { + for (i = 0; i < RX_RING_SIZE; i++) { + if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) { + if ((rx_cbd[i] == ep_ref[0].prx) || ep) { + return -2; + } + } + } + } + + return 0; +} + +/* mpc8xx_udc_ep_tx + * + * Transmit in a re-entrant fashion outbound USB packets. + * Implement retry/timeout mechanism described in USB specification + * Toggle DATA0/DATA1 pids as necessary + * Introduces non-standard tx_retry. The USB standard has no scope for slave + * devices to give up TX, however tx_retry stops us getting stuck in an endless + * TX loop. + */ +static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi) +{ + struct urb *urb = epi->tx_urb; + volatile cbd_t *tx_cbdp = 0; + unsigned int ep = 0, pkt_len = 0, x = 0, tx_retry = 0; + int ret = 0; + + if (!epi || (epi->endpoint_address & 0x03) >= MAX_ENDPOINTS || !urb) { + return -1; + } + + ep = epi->endpoint_address & 0x03; + tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CONFIG_SYS_IMMR); + + if (tx_cbdp->cbd_sc & TX_BD_R || usbp->usber & USB_E_TXB) { + mpc8xx_udc_flush_tx_fifo (ep); + usbp->usber |= USB_E_TXB; + }; + + while (tx_retry++ < 100) { + ret = mpc8xx_udc_tx_irq (ep); + if (ret == -1) { + /* ignore timeout here */ + } else if (ret == -2) { + /* Abandon TX */ + mpc8xx_udc_flush_tx_fifo (ep); + return -1; + } + + tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CONFIG_SYS_IMMR); + while (tx_cbdp->cbd_sc & TX_BD_R) { + }; + tx_cbdp->cbd_sc = (tx_cbdp->cbd_sc & TX_BD_W); + + pkt_len = urb->actual_length - epi->sent; + + if (pkt_len > epi->tx_packetSize || pkt_len > EP_MAX_PKT) { + pkt_len = MIN (epi->tx_packetSize, EP_MAX_PKT); + } + + for (x = 0; x < pkt_len; x++) { + *((unsigned char *) (tx_cbdp->cbd_bufaddr + x)) = + urb->buffer[epi->sent + x]; + } + tx_cbdp->cbd_datlen = pkt_len; + tx_cbdp->cbd_sc |= (CBD_TX_BITMASK | ep_ref[ep].pid); + __asm__ ("eieio"); + +#ifdef __SIMULATE_ERROR__ + if (++err_poison_test == 2) { + err_poison_test = 0; + tx_cbdp->cbd_sc &= ~TX_BD_TC; + } +#endif + + usbp->uscom = (USCOM_STR | ep); + + while (!(usbp->usber & USB_E_TXB)) { + ret = mpc8xx_udc_tx_irq (ep); + if (ret == -1) { + /* TX timeout */ + break; + } else if (ret == -2) { + if (usbp->usber & USB_E_TXB) { + usbp->usber |= USB_E_TXB; + } + mpc8xx_udc_flush_tx_fifo (ep); + return -1; + } + }; + + if (usbp->usber & USB_E_TXB) { + usbp->usber |= USB_E_TXB; + } + + /* ACK must be present <= 18bit times from TX */ + if (ret == -1) { + continue; + } + + /* TX ACK : USB 2.0 8.7.2, Toggle PID, Advance TX */ + epi->sent += pkt_len; + epi->last = MIN (urb->actual_length - epi->sent, epi->tx_packetSize); + TOGGLE_TX_PID (ep_ref[ep].pid); + + if (epi->sent >= epi->tx_urb->actual_length) { + + epi->tx_urb->actual_length = 0; + epi->sent = 0; + + if (ep_ref[ep].sc & EP_SEND_ZLP) { + ep_ref[ep].sc &= ~EP_SEND_ZLP; + } else { + return 0; + } + } + } + + ERR ("TX fail, endpoint 0x%x tx bytes 0x%x/0x%x\n", ep, epi->sent, + epi->tx_urb->actual_length); + + return -1; +} + +/* mpc8xx_udc_dump_request + * + * Dump a control request to console + */ +static void mpc8xx_udc_dump_request (struct usb_device_request *request) +{ + DBG ("bmRequestType:%02x bRequest:%02x wValue:%04x " + "wIndex:%04x wLength:%04x ?\n", + request->bmRequestType, + request->bRequest, + request->wValue, request->wIndex, request->wLength); + + return; +} + +/* mpc8xx_udc_ep0_rx_setup + * + * Decode received ep0 SETUP packet. return non-zero on error + */ +static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp) +{ + unsigned int x = 0; + struct urb *purb = ep_ref[0].urb; + struct usb_endpoint_instance *epi = + &udc_device->bus->endpoint_array[0]; + + for (; x < rx_cbdp->cbd_datlen; x++) { + *(((unsigned char *) &ep_ref[0].urb->device_request) + x) = + *((unsigned char *) (rx_cbdp->cbd_bufaddr + x)); + } + + mpc8xx_udc_clear_rxbd (rx_cbdp); + + if (ep0_recv_setup (purb)) { + mpc8xx_udc_dump_request (&purb->device_request); + return -1; + } + + if ((purb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) + == USB_REQ_HOST2DEVICE) { + + switch (purb->device_request.bRequest) { + case USB_REQ_SET_ADDRESS: + /* Send the Status OUT ZLP */ + ep_ref[0].pid = TX_BD_PID_DATA1; + purb->actual_length = 0; + mpc8xx_udc_init_tx (epi, purb); + mpc8xx_udc_ep_tx (epi); + + /* Move to the addressed state */ + usbp->usaddr = udc_device->address; + mpc8xx_udc_state_transition_up (udc_device->device_state, + STATE_ADDRESSED); + return 0; + + case USB_REQ_SET_CONFIGURATION: + if (!purb->device_request.wValue) { + /* Respond at default address */ + usbp->usaddr = 0x00; + mpc8xx_udc_state_transition_down (udc_device->device_state, + STATE_ADDRESSED); + } else { + /* TODO: Support multiple configurations */ + mpc8xx_udc_state_transition_up (udc_device->device_state, + STATE_CONFIGURED); + for (x = 1; x < MAX_ENDPOINTS; x++) { + if ((udc_device->bus->endpoint_array[x].endpoint_address & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) { + ep_ref[x].pid = TX_BD_PID_DATA0; + } else { + ep_ref[x].pid = RX_BD_PID_DATA0; + } + /* Set configuration must unstall endpoints */ + usbp->usep[x] &= ~STALL_BITMASK; + } + } + break; + default: + /* CDC/Vendor specific */ + break; + } + + /* Send ZLP as ACK in Status OUT phase */ + ep_ref[0].pid = TX_BD_PID_DATA1; + purb->actual_length = 0; + mpc8xx_udc_init_tx (epi, purb); + mpc8xx_udc_ep_tx (epi); + + } else { + + if (purb->actual_length) { + ep_ref[0].pid = TX_BD_PID_DATA1; + mpc8xx_udc_init_tx (epi, purb); + + if (!(purb->actual_length % EP0_MAX_PACKET_SIZE)) { + ep_ref[0].sc |= EP_SEND_ZLP; + } + + if (purb->device_request.wValue == + USB_DESCRIPTOR_TYPE_DEVICE) { + if (le16_to_cpu (purb->device_request.wLength) + > purb->actual_length) { + /* Send EP0_MAX_PACKET_SIZE bytes + * unless correct size requested. + */ + if (purb->actual_length > epi->tx_packetSize) { + purb->actual_length = epi->tx_packetSize; + } + } + } + mpc8xx_udc_ep_tx (epi); + + } else { + /* Corrupt SETUP packet? */ + ERR ("Zero length data or SETUP with DATA-IN phase ?\n"); + return 1; + } + } + return 0; +} + +/* mpc8xx_udc_init_tx + * + * Setup some basic parameters for a TX transaction + */ +static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi, + struct urb *tx_urb) +{ + epi->sent = 0; + epi->last = 0; + epi->tx_urb = tx_urb; +} + +/* mpc8xx_udc_ep0_rx + * + * Receive ep0/control USB data. Parse and possibly send a response. + */ +static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp) +{ + if (rx_cbdp->cbd_sc & RX_BD_PID_SETUP) { + + /* Unconditionally accept SETUP packets */ + if (mpc8xx_udc_ep0_rx_setup (rx_cbdp)) { + mpc8xx_udc_stall (0); + } + + } else { + + mpc8xx_udc_clear_rxbd (rx_cbdp); + + if ((rx_cbdp->cbd_datlen - 2)) { + /* SETUP with a DATA phase + * outside of SETUP packet. + * Reply with STALL. + */ + mpc8xx_udc_stall (0); + } + } +} + +/* mpc8xx_udc_epn_rx + * + * Receive some data from cbd into USB system urb data abstraction + * Upper layers should NAK if there is insufficient RX data space + */ +static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp) +{ + struct usb_endpoint_instance *epi = 0; + struct urb *urb = 0; + unsigned int x = 0; + + if (epid >= MAX_ENDPOINTS || !rx_cbdp->cbd_datlen) { + return 0; + } + + /* USB 2.0 PDF section 8.6.4 + * Discard data with invalid PID it is a resend. + */ + if (ep_ref[epid].pid != (rx_cbdp->cbd_sc & 0xC0)) { + return 1; + } + TOGGLE_RX_PID (ep_ref[epid].pid); + + epi = &udc_device->bus->endpoint_array[epid]; + urb = epi->rcv_urb; + + for (; x < (rx_cbdp->cbd_datlen - 2); x++) { + *((unsigned char *) (urb->buffer + urb->actual_length + x)) = + *((unsigned char *) (rx_cbdp->cbd_bufaddr + x)); + } + + if (x) { + usbd_rcv_complete (epi, x, 0); + if (ep_ref[epid].urb->status == RECV_ERROR) { + DBG ("RX error unset NAK\n"); + udc_unset_nak (epid); + } + } + return x; +} + +/* mpc8xx_udc_clock_init + * + * Obtain a clock reference for Full Speed Signaling + */ +static void mpc8xx_udc_clock_init (volatile immap_t * immr, + volatile cpm8xx_t * cp) +{ + +#if defined(CONFIG_SYS_USB_EXTC_CLK) + + /* This has been tested with a 48MHz crystal on CLK6 */ + switch (CONFIG_SYS_USB_EXTC_CLK) { + case 1: + immr->im_ioport.iop_papar |= 0x0100; + immr->im_ioport.iop_padir &= ~0x0100; + cp->cp_sicr |= 0x24; + break; + case 2: + immr->im_ioport.iop_papar |= 0x0200; + immr->im_ioport.iop_padir &= ~0x0200; + cp->cp_sicr |= 0x2D; + break; + case 3: + immr->im_ioport.iop_papar |= 0x0400; + immr->im_ioport.iop_padir &= ~0x0400; + cp->cp_sicr |= 0x36; + break; + case 4: + immr->im_ioport.iop_papar |= 0x0800; + immr->im_ioport.iop_padir &= ~0x0800; + cp->cp_sicr |= 0x3F; + break; + default: + udc_state = STATE_ERROR; + break; + } + +#elif defined(CONFIG_SYS_USB_BRGCLK) + + /* This has been tested with brgclk == 50MHz */ + int divisor = 0; + + if (gd->cpu_clk < 48000000L) { + ERR ("brgclk is too slow for full-speed USB!\n"); + udc_state = STATE_ERROR; + return; + } + + /* Assume the brgclk is 'good enough', we want !(gd->cpu_clk%48MHz) + * but, can /probably/ live with close-ish alternative rates. + */ + divisor = (gd->cpu_clk / 48000000L) - 1; + cp->cp_sicr &= ~0x0000003F; + + switch (CONFIG_SYS_USB_BRGCLK) { + case 1: + cp->cp_brgc1 |= (divisor | CPM_BRG_EN); + cp->cp_sicr &= ~0x2F; + break; + case 2: + cp->cp_brgc2 |= (divisor | CPM_BRG_EN); + cp->cp_sicr |= 0x00000009; + break; + case 3: + cp->cp_brgc3 |= (divisor | CPM_BRG_EN); + cp->cp_sicr |= 0x00000012; + break; + case 4: + cp->cp_brgc4 = (divisor | CPM_BRG_EN); + cp->cp_sicr |= 0x0000001B; + break; + default: + udc_state = STATE_ERROR; + break; + } + +#else +#error "CONFIG_SYS_USB_EXTC_CLK or CONFIG_SYS_USB_BRGCLK must be defined" +#endif + +} + +/* mpc8xx_udc_cbd_attach + * + * attach a cbd to and endpoint + */ +static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size) +{ + + if (!tx_cbd[ep] || !rx_cbd[ep] || ep >= MAX_ENDPOINTS) { + udc_state = STATE_ERROR; + return; + } + + if (tx_size > USB_MAX_PKT || rx_size > USB_MAX_PKT || + (!tx_size && !rx_size)) { + udc_state = STATE_ERROR; + return; + } + + /* Attach CBD to appropiate Parameter RAM Endpoint data structure */ + if (rx_size) { + endpoints[ep]->rbase = (u32) rx_cbd[rx_ct]; + endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct]; + rx_ct++; + + if (!ep) { + + endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct]; + rx_cbd[rx_ct]->cbd_sc |= RX_BD_W; + rx_ct++; + + } else { + rx_ct += 2; + endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct]; + rx_cbd[rx_ct]->cbd_sc |= RX_BD_W; + rx_ct++; + } + + /* Where we expect to RX data on this endpoint */ + ep_ref[ep].prx = rx_cbd[rx_ct - 1]; + } else { + + ep_ref[ep].prx = 0; + endpoints[ep]->rbase = 0; + endpoints[ep]->rbptr = 0; + } + + if (tx_size) { + endpoints[ep]->tbase = (u32) tx_cbd[tx_ct]; + endpoints[ep]->tbptr = (u32) tx_cbd[tx_ct]; + tx_ct++; + } else { + endpoints[ep]->tbase = 0; + endpoints[ep]->tbptr = 0; + } + + endpoints[ep]->tstate = 0; + endpoints[ep]->tbcnt = 0; + endpoints[ep]->mrblr = EP_MAX_PKT; + endpoints[ep]->rfcr = 0x18; + endpoints[ep]->tfcr = 0x18; + ep_ref[ep].sc |= EP_ATTACHED; + + DBG ("ep %d rbase 0x%08x rbptr 0x%08x tbase 0x%08x tbptr 0x%08x prx = %p\n", + ep, endpoints[ep]->rbase, endpoints[ep]->rbptr, + endpoints[ep]->tbase, endpoints[ep]->tbptr, + ep_ref[ep].prx); + + return; +} + +/* mpc8xx_udc_cbd_init + * + * Allocate space for a cbd and allocate TX/RX data space + */ +static void mpc8xx_udc_cbd_init (void) +{ + int i = 0; + + for (; i < TX_RING_SIZE; i++) { + tx_cbd[i] = (cbd_t *) + mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int)); + } + + for (i = 0; i < RX_RING_SIZE; i++) { + rx_cbd[i] = (cbd_t *) + mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int)); + } + + for (i = 0; i < TX_RING_SIZE; i++) { + tx_cbd[i]->cbd_bufaddr = + mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int)); + + tx_cbd[i]->cbd_sc = (TX_BD_I | TX_BD_W); + tx_cbd[i]->cbd_datlen = 0x0000; + } + + + for (i = 0; i < RX_RING_SIZE; i++) { + rx_cbd[i]->cbd_bufaddr = + mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int)); + rx_cbd[i]->cbd_sc = (RX_BD_I | RX_BD_E); + rx_cbd[i]->cbd_datlen = 0x0000; + + } + + return; +} + +/* mpc8xx_udc_endpoint_init + * + * Attach an endpoint to some dpram + */ +static void mpc8xx_udc_endpoint_init (void) +{ + int i = 0; + + for (; i < MAX_ENDPOINTS; i++) { + endpoints[i] = (usb_epb_t *) + mpc8xx_udc_alloc (sizeof (usb_epb_t), 32); + } +} + +/* mpc8xx_udc_alloc + * + * Grab the address of some dpram + */ +static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment) +{ + u32 retaddr = address_base; + + while (retaddr % alignment) { + retaddr++; + } + address_base += data_size; + + return retaddr; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/ndis.h b/qemu/roms/u-boot/drivers/usb/gadget/ndis.h new file mode 100644 index 000000000..753838f79 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/ndis.h @@ -0,0 +1,217 @@ +/* + * ndis.h + * + * ntddndis.h modified by Benedikt Spranger + * + * Thanks to the cygwin development team, + * espacially to Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _USBGADGET_NDIS_H +#define _USBGADGET_NDIS_H + + +#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 +#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A +#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B + +enum NDIS_DEVICE_POWER_STATE { + NdisDeviceStateUnspecified = 0, + NdisDeviceStateD0, + NdisDeviceStateD1, + NdisDeviceStateD2, + NdisDeviceStateD3, + NdisDeviceStateMaximum +}; + +struct NDIS_PM_WAKE_UP_CAPABILITIES { + enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp; + enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp; + enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp; +}; + +/* NDIS_PNP_CAPABILITIES.Flags constants */ +#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001 +#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002 +#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 + +struct NDIS_PNP_CAPABILITIES { + __le32 Flags; + struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities; +}; + +struct NDIS_PM_PACKET_PATTERN { + __le32 Priority; + __le32 Reserved; + __le32 MaskSize; + __le32 PatternOffset; + __le32 PatternSize; + __le32 PatternFlags; +}; + + +/* Required Object IDs (OIDs) */ +#define OID_GEN_SUPPORTED_LIST 0x00010101 +#define OID_GEN_HARDWARE_STATUS 0x00010102 +#define OID_GEN_MEDIA_SUPPORTED 0x00010103 +#define OID_GEN_MEDIA_IN_USE 0x00010104 +#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +#define OID_GEN_LINK_SPEED 0x00010107 +#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +#define OID_GEN_VENDOR_ID 0x0001010C +#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D +#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +#define OID_GEN_DRIVER_VERSION 0x00010110 +#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 +#define OID_GEN_MAC_OPTIONS 0x00010113 +#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +#define OID_GEN_SUPPORTED_GUIDS 0x00010117 +#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +#define OID_GEN_MACHINE_NAME 0x0001021A +#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B +#define OID_GEN_VLAN_ID 0x0001021C + +/* Optional OIDs */ +#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 +#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 + +/* Required statistics OIDs */ +#define OID_GEN_XMIT_OK 0x00020101 +#define OID_GEN_RCV_OK 0x00020102 +#define OID_GEN_XMIT_ERROR 0x00020103 +#define OID_GEN_RCV_ERROR 0x00020104 +#define OID_GEN_RCV_NO_BUFFER 0x00020105 + +/* Optional statistics OIDs */ +#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C +#define OID_GEN_RCV_CRC_ERROR 0x0002020D +#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E +#define OID_GEN_GET_TIME_CAPS 0x0002020F +#define OID_GEN_GET_NETCARD_TIME 0x00020210 +#define OID_GEN_NETCARD_LOAD 0x00020211 +#define OID_GEN_DEVICE_PROFILE 0x00020212 +#define OID_GEN_INIT_TIME_MS 0x00020213 +#define OID_GEN_RESET_COUNTS 0x00020214 +#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 +#define OID_GEN_FRIENDLY_NAME 0x00020216 +#define OID_GEN_MINIPORT_INFO 0x00020217 +#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 + +/* IEEE 802.3 (Ethernet) OIDs */ +#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 + +#define OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define OID_802_3_CURRENT_ADDRESS 0x01010102 +#define OID_802_3_MULTICAST_LIST 0x01010103 +#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +#define OID_802_3_MAC_OPTIONS 0x01010105 +#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 +#define OID_802_3_XMIT_DEFERRED 0x01020201 +#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define OID_802_3_RCV_OVERRUN 0x01020203 +#define OID_802_3_XMIT_UNDERRUN 0x01020204 +#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + +/* OID_GEN_MINIPORT_INFO constants */ +#define NDIS_MINIPORT_BUS_MASTER 0x00000001 +#define NDIS_MINIPORT_WDM_DRIVER 0x00000002 +#define NDIS_MINIPORT_SG_LIST 0x00000004 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008 +#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010 +#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020 +#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040 +#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080 +#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100 +#define NDIS_MINIPORT_IS_NDIS_5 0x00000200 +#define NDIS_MINIPORT_IS_CO 0x00000400 +#define NDIS_MINIPORT_DESERIALIZE 0x00000800 +#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000 +#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000 +#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000 +#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000 +#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000 +#define NDIS_MINIPORT_HIDDEN 0x00040000 +#define NDIS_MINIPORT_SWENUM 0x00080000 +#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000 +#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000 +#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000 +#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000 +#define NDIS_MINIPORT_64BITS_DMA 0x01000000 + +#define NDIS_MEDIUM_802_3 0x00000000 +#define NDIS_MEDIUM_802_5 0x00000001 +#define NDIS_MEDIUM_FDDI 0x00000002 +#define NDIS_MEDIUM_WAN 0x00000003 +#define NDIS_MEDIUM_LOCAL_TALK 0x00000004 +#define NDIS_MEDIUM_DIX 0x00000005 +#define NDIS_MEDIUM_ARCENT_RAW 0x00000006 +#define NDIS_MEDIUM_ARCENT_878_2 0x00000007 +#define NDIS_MEDIUM_ATM 0x00000008 +#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009 +#define NDIS_MEDIUM_IRDA 0x0000000A +#define NDIS_MEDIUM_BPC 0x0000000B +#define NDIS_MEDIUM_CO_WAN 0x0000000C +#define NDIS_MEDIUM_1394 0x0000000D + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + +#define NDIS_MEDIA_STATE_CONNECTED 0x00000000 +#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001 + +#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 +#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 +#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 +#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 +#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 +#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 +#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 +#define NDIS_MAC_OPTION_RESERVED 0x80000000 + +#endif /* _USBGADGET_NDIS_H */ diff --git a/qemu/roms/u-boot/drivers/usb/gadget/omap1510_udc.c b/qemu/roms/u-boot/drivers/usb/gadget/omap1510_udc.c new file mode 100644 index 000000000..bdc1b886f --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/omap1510_udc.c @@ -0,0 +1,1555 @@ +/* + * (C) Copyright 2003 + * Gerry Hamel, geh@ti.com, Texas Instruments + * + * Based on + * linux/drivers/usb/device/bi/omap.c + * TI OMAP1510 USB bus interface driver + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * (C) Copyright 2002 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#ifdef CONFIG_OMAP_SX1 +#include +#endif +#include +#include +#include + +#include "ep0.h" + + +#define UDC_INIT_MDELAY 80 /* Device settle delay */ +#define UDC_MAX_ENDPOINTS 31 /* Number of endpoints on this UDC */ + +/* Some kind of debugging output... */ +#if 1 +#define UDCDBG(str) +#define UDCDBGA(fmt,args...) +#else /* The bugs still exists... */ +#define UDCDBG(str) serial_printf("[%s] %s:%d: " str "\n", __FILE__,__FUNCTION__,__LINE__) +#define UDCDBGA(fmt,args...) serial_printf("[%s] %s:%d: " fmt "\n", __FILE__,__FUNCTION__,__LINE__, ##args) +#endif + +#if 1 +#define UDCREG(name) +#define UDCREGL(name) +#else /* The bugs still exists... */ +#define UDCREG(name) serial_printf("%s():%d: %s[%08x]=%.4x\n",__FUNCTION__,__LINE__, (#name), name, inw(name)) /* For 16-bit regs */ +#define UDCREGL(name) serial_printf("%s():%d: %s[%08x]=%.8x\n",__FUNCTION__,__LINE__, (#name), name, inl(name)) /* For 32-bit regs */ +#endif + + +static struct urb *ep0_urb = NULL; + +static struct usb_device_instance *udc_device; /* Used in interrupt handler */ +static u16 udc_devstat = 0; /* UDC status (DEVSTAT) */ +static u32 udc_interrupts = 0; + +static void udc_stall_ep (unsigned int ep_addr); + + +static struct usb_endpoint_instance *omap1510_find_ep (int ep) +{ + int i; + + for (i = 0; i < udc_device->bus->max_endpoints; i++) { + if (udc_device->bus->endpoint_array[i].endpoint_address == ep) + return &udc_device->bus->endpoint_array[i]; + } + return NULL; +} + +/* ************************************************************************** */ +/* IO + */ + +/* + * omap1510_prepare_endpoint_for_rx + * + * This function implements TRM Figure 14-11. + * + * The endpoint to prepare for transfer is specified as a physical endpoint + * number. For OUT (rx) endpoints 1 through 15, the corresponding endpoint + * configuration register is checked to see if the endpoint is ISO or not. + * If the OUT endpoint is valid and is non-ISO then its FIFO is enabled. + * No action is taken for endpoint 0 or for IN (tx) endpoints 16 through 30. + */ +static void omap1510_prepare_endpoint_for_rx (int ep_addr) +{ + int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + + UDCDBGA ("omap1510_prepare_endpoint %x", ep_addr); + if (((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)) { + if ((inw (UDC_EP_RX (ep_num)) & + (UDC_EPn_RX_Valid | UDC_EPn_RX_Iso)) == + UDC_EPn_RX_Valid) { + /* rx endpoint is valid, non-ISO, so enable its FIFO */ + outw (UDC_EP_Sel | ep_num, UDC_EP_NUM); + outw (UDC_Set_FIFO_En, UDC_CTRL); + outw (0, UDC_EP_NUM); + } + } +} + +/* omap1510_configure_endpoints + * + * This function implements TRM Figure 14-10. + */ +static void omap1510_configure_endpoints (struct usb_device_instance *device) +{ + int ep; + struct usb_bus_instance *bus; + struct usb_endpoint_instance *endpoint; + unsigned short ep_ptr; + unsigned short ep_size; + unsigned short ep_isoc; + unsigned short ep_doublebuffer; + int ep_addr; + int packet_size; + int buffer_size; + int attributes; + + bus = device->bus; + + /* There is a dedicated 2048 byte buffer for USB packets that may be + * arbitrarily partitioned among the endpoints on 8-byte boundaries. + * The first 8 bytes are reserved for receiving setup packets on + * endpoint 0. + */ + ep_ptr = 8; /* reserve the first 8 bytes for the setup fifo */ + + for (ep = 0; ep < bus->max_endpoints; ep++) { + endpoint = bus->endpoint_array + ep; + ep_addr = endpoint->endpoint_address; + if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* IN endpoint */ + packet_size = endpoint->tx_packetSize; + attributes = endpoint->tx_attributes; + } else { + /* OUT endpoint */ + packet_size = endpoint->rcv_packetSize; + attributes = endpoint->rcv_attributes; + } + + switch (packet_size) { + case 0: + ep_size = 0; + break; + case 8: + ep_size = 0; + break; + case 16: + ep_size = 1; + break; + case 32: + ep_size = 2; + break; + case 64: + ep_size = 3; + break; + case 128: + ep_size = 4; + break; + case 256: + ep_size = 5; + break; + case 512: + ep_size = 6; + break; + default: + UDCDBGA ("ep 0x%02x has bad packet size %d", + ep_addr, packet_size); + packet_size = 0; + ep_size = 0; + break; + } + + switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + default: + /* A non-isochronous endpoint may optionally be + * double-buffered. For now we disable + * double-buffering. + */ + ep_doublebuffer = 0; + ep_isoc = 0; + if (packet_size > 64) + packet_size = 0; + if (!ep || !ep_doublebuffer) + buffer_size = packet_size; + else + buffer_size = packet_size * 2; + break; + case USB_ENDPOINT_XFER_ISOC: + /* Isochronous endpoints are always double- + * buffered, but the double-buffering bit + * in the endpoint configuration register + * becomes the msb of the endpoint size so we + * set the double-buffering flag to zero. + */ + ep_doublebuffer = 0; + ep_isoc = 1; + buffer_size = packet_size * 2; + break; + } + + /* check to see if our packet buffer RAM is exhausted */ + if ((ep_ptr + buffer_size) > 2048) { + UDCDBGA ("out of packet RAM for ep 0x%02x buf size %d", ep_addr, buffer_size); + buffer_size = packet_size = 0; + } + + /* force a default configuration for endpoint 0 since it is + * always enabled + */ + if (!ep && ((packet_size < 8) || (packet_size > 64))) { + buffer_size = packet_size = 64; + ep_size = 3; + } + + if (!ep) { + /* configure endpoint 0 */ + outw ((ep_size << 12) | (ep_ptr >> 3), UDC_EP0); + /*UDCDBGA("ep 0 buffer offset 0x%03x packet size 0x%03x", */ + /* ep_ptr, packet_size); */ + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* IN endpoint */ + if (packet_size) { + outw ((1 << 15) | (ep_doublebuffer << 14) | + (ep_size << 12) | (ep_isoc << 11) | + (ep_ptr >> 3), + UDC_EP_TX (ep_addr & + USB_ENDPOINT_NUMBER_MASK)); + UDCDBGA ("IN ep %d buffer offset 0x%03x" + " packet size 0x%03x", + ep_addr & USB_ENDPOINT_NUMBER_MASK, + ep_ptr, packet_size); + } else { + outw (0, + UDC_EP_TX (ep_addr & + USB_ENDPOINT_NUMBER_MASK)); + } + } else { + /* OUT endpoint */ + if (packet_size) { + outw ((1 << 15) | (ep_doublebuffer << 14) | + (ep_size << 12) | (ep_isoc << 11) | + (ep_ptr >> 3), + UDC_EP_RX (ep_addr & + USB_ENDPOINT_NUMBER_MASK)); + UDCDBGA ("OUT ep %d buffer offset 0x%03x" + " packet size 0x%03x", + ep_addr & USB_ENDPOINT_NUMBER_MASK, + ep_ptr, packet_size); + } else { + outw (0, + UDC_EP_RX (ep_addr & + USB_ENDPOINT_NUMBER_MASK)); + } + } + ep_ptr += buffer_size; + } +} + +/* omap1510_deconfigure_device + * + * This function balances omap1510_configure_device. + */ +static void omap1510_deconfigure_device (void) +{ + int epnum; + + UDCDBG ("clear Cfg_Lock"); + outw (inw (UDC_SYSCON1) & ~UDC_Cfg_Lock, UDC_SYSCON1); + UDCREG (UDC_SYSCON1); + + /* deconfigure all endpoints */ + for (epnum = 1; epnum <= 15; epnum++) { + outw (0, UDC_EP_RX (epnum)); + outw (0, UDC_EP_TX (epnum)); + } +} + +/* omap1510_configure_device + * + * This function implements TRM Figure 14-9. + */ +static void omap1510_configure_device (struct usb_device_instance *device) +{ + omap1510_configure_endpoints (device); + + + /* Figure 14-9 indicates we should enable interrupts here, but we have + * other routines (udc_all_interrupts, udc_suspended_interrupts) to + * do that. + */ + + UDCDBG ("set Cfg_Lock"); + outw (inw (UDC_SYSCON1) | UDC_Cfg_Lock, UDC_SYSCON1); + UDCREG (UDC_SYSCON1); +} + +/* omap1510_write_noniso_tx_fifo + * + * This function implements TRM Figure 14-30. + * + * If the endpoint has an active tx_urb, then the next packet of data from the + * URB is written to the tx FIFO. The total amount of data in the urb is given + * by urb->actual_length. The maximum amount of data that can be sent in any + * one packet is given by endpoint->tx_packetSize. The number of data bytes + * from this URB that have already been transmitted is given by endpoint->sent. + * endpoint->last is updated by this routine with the number of data bytes + * transmitted in this packet. + * + * In accordance with Figure 14-30, the EP_NUM register must already have been + * written with the value to select the appropriate tx FIFO before this routine + * is called. + */ +static void omap1510_write_noniso_tx_fifo (struct usb_endpoint_instance + *endpoint) +{ + struct urb *urb = endpoint->tx_urb; + + if (urb) { + unsigned int last, i; + + UDCDBGA ("urb->buffer %p, buffer_length %d, actual_length %d", + urb->buffer, urb->buffer_length, urb->actual_length); + if ((last = + MIN (urb->actual_length - endpoint->sent, + endpoint->tx_packetSize))) { + u8 *cp = urb->buffer + endpoint->sent; + + UDCDBGA ("endpoint->sent %d, tx_packetSize %d, last %d", endpoint->sent, endpoint->tx_packetSize, last); + + if (((u32) cp & 1) == 0) { /* word aligned? */ + outsw (UDC_DATA, cp, last >> 1); + } else { /* byte aligned. */ + for (i = 0; i < (last >> 1); i++) { + u16 w = ((u16) cp[2 * i + 1] << 8) | + (u16) cp[2 * i]; + outw (w, UDC_DATA); + } + } + if (last & 1) { + outb (*(cp + last - 1), UDC_DATA); + } + } + endpoint->last = last; + } +} + +/* omap1510_read_noniso_rx_fifo + * + * This function implements TRM Figure 14-28. + * + * If the endpoint has an active rcv_urb, then the next packet of data is read + * from the rcv FIFO and written to rcv_urb->buffer at offset + * rcv_urb->actual_length to append the packet data to the data from any + * previous packets for this transfer. We assume that there is sufficient room + * left in the buffer to hold an entire packet of data. + * + * The return value is the number of bytes read from the FIFO for this packet. + * + * In accordance with Figure 14-28, the EP_NUM register must already have been + * written with the value to select the appropriate rcv FIFO before this routine + * is called. + */ +static int omap1510_read_noniso_rx_fifo (struct usb_endpoint_instance + *endpoint) +{ + struct urb *urb = endpoint->rcv_urb; + int len = 0; + + if (urb) { + len = inw (UDC_RXFSTAT); + + if (len) { + unsigned char *cp = urb->buffer + urb->actual_length; + + insw (UDC_DATA, cp, len >> 1); + if (len & 1) + *(cp + len - 1) = inb (UDC_DATA); + } + } + return len; +} + +/* omap1510_prepare_for_control_write_status + * + * This function implements TRM Figure 14-17. + * + * We have to deal here with non-autodecoded control writes that haven't already + * been dealt with by ep0_recv_setup. The non-autodecoded standard control + * write requests are: set/clear endpoint feature, set configuration, set + * interface, and set descriptor. ep0_recv_setup handles set/clear requests for + * ENDPOINT_HALT by halting the endpoint for a set request and resetting the + * endpoint for a clear request. ep0_recv_setup returns an error for + * SET_DESCRIPTOR requests which causes them to be terminated with a stall by + * the setup handler. A SET_INTERFACE request is handled by ep0_recv_setup by + * generating a DEVICE_SET_INTERFACE event. This leaves only the + * SET_CONFIGURATION event for us to deal with here. + * + */ +static void omap1510_prepare_for_control_write_status (struct urb *urb) +{ + struct usb_device_request *request = &urb->device_request;; + + /* check for a SET_CONFIGURATION request */ + if (request->bRequest == USB_REQ_SET_CONFIGURATION) { + int configuration = le16_to_cpu (request->wValue) & 0xff; + unsigned short devstat = inw (UDC_DEVSTAT); + + if ((devstat & (UDC_ADD | UDC_CFG)) == UDC_ADD) { + /* device is currently in ADDRESSED state */ + if (configuration) { + /* Assume the specified non-zero configuration + * value is valid and switch to the CONFIGURED + * state. + */ + outw (UDC_Dev_Cfg, UDC_SYSCON2); + } + } else if ((devstat & UDC_CFG) == UDC_CFG) { + /* device is currently in CONFIGURED state */ + if (!configuration) { + /* Switch to ADDRESSED state. */ + outw (UDC_Clr_Cfg, UDC_SYSCON2); + } + } + } + + /* select EP0 tx FIFO */ + outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); + /* clear endpoint (no data bytes in status stage) */ + outw (UDC_Clr_EP, UDC_CTRL); + /* enable the EP0 tx FIFO */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect the endpoint */ + outw (UDC_EP_Dir, UDC_EP_NUM); +} + +/* udc_state_transition_up + * udc_state_transition_down + * + * Helper functions to implement device state changes. The device states and + * the events that transition between them are: + * + * STATE_ATTACHED + * || /\ + * \/ || + * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET + * || /\ + * \/ || + * STATE_POWERED + * || /\ + * \/ || + * DEVICE_RESET DEVICE_POWER_INTERRUPTION + * || /\ + * \/ || + * STATE_DEFAULT + * || /\ + * \/ || + * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET + * || /\ + * \/ || + * STATE_ADDRESSED + * || /\ + * \/ || + * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED + * || /\ + * \/ || + * STATE_CONFIGURED + * + * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED + * to STATE_CONFIGURED) from the specified initial state to the specified final + * state, passing through each intermediate state on the way. If the initial + * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then + * no state transitions will take place. + * + * udc_state_transition_down transitions down (in the direction from + * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the + * specified final state, passing through each intermediate state on the way. + * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final + * state, then no state transitions will take place. + * + * These functions must only be called with interrupts disabled. + */ +static void udc_state_transition_up (usb_device_state_t initial, + usb_device_state_t final) +{ + if (initial < final) { + switch (initial) { + case STATE_ATTACHED: + usbd_device_event_irq (udc_device, + DEVICE_HUB_CONFIGURED, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq (udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq (udc_device, + DEVICE_ADDRESS_ASSIGNED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq (udc_device, DEVICE_CONFIGURED, + 0); + case STATE_CONFIGURED: + break; + default: + break; + } + } +} + +static void udc_state_transition_down (usb_device_state_t initial, + usb_device_state_t final) +{ + if (initial > final) { + switch (initial) { + case STATE_CONFIGURED: + usbd_device_event_irq (udc_device, DEVICE_DE_CONFIGURED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq (udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq (udc_device, DEVICE_POWER_INTERRUPTION, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq (udc_device, DEVICE_HUB_RESET, 0); + case STATE_ATTACHED: + break; + default: + break; + } + } +} + +/* Handle all device state changes. + * This function implements TRM Figure 14-21. + */ +static void omap1510_udc_state_changed (void) +{ + u16 bits; + u16 devstat = inw (UDC_DEVSTAT); + + UDCDBGA ("state changed, devstat %x, old %x", devstat, udc_devstat); + + bits = devstat ^ udc_devstat; + if (bits) { + if (bits & UDC_ATT) { + if (devstat & UDC_ATT) { + UDCDBG ("device attached and powered"); + udc_state_transition_up (udc_device->device_state, STATE_POWERED); + } else { + UDCDBG ("device detached or unpowered"); + udc_state_transition_down (udc_device->device_state, STATE_ATTACHED); + } + } + if (bits & UDC_USB_Reset) { + if (devstat & UDC_USB_Reset) { + UDCDBG ("device reset in progess"); + udc_state_transition_down (udc_device->device_state, STATE_POWERED); + } else { + UDCDBG ("device reset completed"); + } + } + if (bits & UDC_DEF) { + if (devstat & UDC_DEF) { + UDCDBG ("device entering default state"); + udc_state_transition_up (udc_device->device_state, STATE_DEFAULT); + } else { + UDCDBG ("device leaving default state"); + udc_state_transition_down (udc_device->device_state, STATE_POWERED); + } + } + if (bits & UDC_SUS) { + if (devstat & UDC_SUS) { + UDCDBG ("entering suspended state"); + usbd_device_event_irq (udc_device, DEVICE_BUS_INACTIVE, 0); + } else { + UDCDBG ("leaving suspended state"); + usbd_device_event_irq (udc_device, DEVICE_BUS_ACTIVITY, 0); + } + } + if (bits & UDC_R_WK_OK) { + UDCDBGA ("remote wakeup %s", (devstat & UDC_R_WK_OK) + ? "enabled" : "disabled"); + } + if (bits & UDC_ADD) { + if (devstat & UDC_ADD) { + UDCDBG ("default -> addressed"); + udc_state_transition_up (udc_device->device_state, STATE_ADDRESSED); + } else { + UDCDBG ("addressed -> default"); + udc_state_transition_down (udc_device->device_state, STATE_DEFAULT); + } + } + if (bits & UDC_CFG) { + if (devstat & UDC_CFG) { + UDCDBG ("device configured"); + /* The ep0_recv_setup function generates the + * DEVICE_CONFIGURED event when a + * USB_REQ_SET_CONFIGURATION setup packet is + * received, so we should already be in the + * state STATE_CONFIGURED. + */ + udc_state_transition_up (udc_device->device_state, STATE_CONFIGURED); + } else { + UDCDBG ("device deconfigured"); + udc_state_transition_down (udc_device->device_state, STATE_ADDRESSED); + } + } + } + + /* Clear interrupt source */ + outw (UDC_DS_Chg, UDC_IRQ_SRC); + + /* Save current DEVSTAT */ + udc_devstat = devstat; +} + +/* Handle SETUP USB interrupt. + * This function implements TRM Figure 14-14. + */ +static void omap1510_udc_setup (struct usb_endpoint_instance *endpoint) +{ + UDCDBG ("-> Entering device setup"); + + do { + const int setup_pktsize = 8; + unsigned char *datap = + (unsigned char *) &ep0_urb->device_request; + + /* Gain access to EP 0 setup FIFO */ + outw (UDC_Setup_Sel, UDC_EP_NUM); + + /* Read control request data */ + insb (UDC_DATA, datap, setup_pktsize); + + UDCDBGA ("EP0 setup read [%x %x %x %x %x %x %x %x]", + *(datap + 0), *(datap + 1), *(datap + 2), + *(datap + 3), *(datap + 4), *(datap + 5), + *(datap + 6), *(datap + 7)); + + /* Reset EP0 setup FIFO */ + outw (0, UDC_EP_NUM); + } while (inw (UDC_IRQ_SRC) & UDC_Setup); + + /* Try to process setup packet */ + if (ep0_recv_setup (ep0_urb)) { + /* Not a setup packet, stall next EP0 transaction */ + udc_stall_ep (0); + UDCDBG ("can't parse setup packet, still waiting for setup"); + return; + } + + /* Check direction */ + if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) + == USB_REQ_HOST2DEVICE) { + UDCDBG ("control write on EP0"); + if (le16_to_cpu (ep0_urb->device_request.wLength)) { + /* We don't support control write data stages. + * The only standard control write request with a data + * stage is SET_DESCRIPTOR, and ep0_recv_setup doesn't + * support that so we just stall those requests. A + * function driver might support a non-standard + * write request with a data stage, but it isn't + * obvious what we would do with the data if we read it + * so we'll just stall it. It seems like the API isn't + * quite right here. + */ +#if 0 + /* Here is what we would do if we did support control + * write data stages. + */ + ep0_urb->actual_length = 0; + outw (0, UDC_EP_NUM); + /* enable the EP0 rx FIFO */ + outw (UDC_Set_FIFO_En, UDC_CTRL); +#else + /* Stall this request */ + UDCDBG ("Stalling unsupported EP0 control write data " + "stage."); + udc_stall_ep (0); +#endif + } else { + omap1510_prepare_for_control_write_status (ep0_urb); + } + } else { + UDCDBG ("control read on EP0"); + /* The ep0_recv_setup function has already placed our response + * packet data in ep0_urb->buffer and the packet length in + * ep0_urb->actual_length. + */ + endpoint->tx_urb = ep0_urb; + endpoint->sent = 0; + /* select the EP0 tx FIFO */ + outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); + /* Write packet data to the FIFO. omap1510_write_noniso_tx_fifo + * will update endpoint->last with the number of bytes written + * to the FIFO. + */ + omap1510_write_noniso_tx_fifo (endpoint); + /* enable the FIFO to start the packet transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect the EP0 tx FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } + + UDCDBG ("<- Leaving device setup"); +} + +/* Handle endpoint 0 RX interrupt + * This routine implements TRM Figure 14-16. + */ +static void omap1510_udc_ep0_rx (struct usb_endpoint_instance *endpoint) +{ + unsigned short status; + + UDCDBG ("RX on EP0"); + /* select EP0 rx FIFO */ + outw (UDC_EP_Sel, UDC_EP_NUM); + + status = inw (UDC_STAT_FLG); + + if (status & UDC_ACK) { + /* Check direction */ + if ((ep0_urb->device_request.bmRequestType + & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + /* This rx interrupt must be for a control write data + * stage packet. + * + * We don't support control write data stages. + * We should never end up here. + */ + + /* clear the EP0 rx FIFO */ + outw (UDC_Clr_EP, UDC_CTRL); + + /* deselect the EP0 rx FIFO */ + outw (0, UDC_EP_NUM); + + UDCDBG ("Stalling unexpected EP0 control write " + "data stage packet"); + udc_stall_ep (0); + } else { + /* This rx interrupt must be for a control read status + * stage packet. + */ + UDCDBG ("ACK on EP0 control read status stage packet"); + /* deselect EP0 rx FIFO */ + outw (0, UDC_EP_NUM); + } + } else if (status & UDC_STALL) { + UDCDBG ("EP0 stall during RX"); + /* deselect EP0 rx FIFO */ + outw (0, UDC_EP_NUM); + } else { + /* deselect EP0 rx FIFO */ + outw (0, UDC_EP_NUM); + } +} + +/* Handle endpoint 0 TX interrupt + * This routine implements TRM Figure 14-18. + */ +static void omap1510_udc_ep0_tx (struct usb_endpoint_instance *endpoint) +{ + unsigned short status; + struct usb_device_request *request = &ep0_urb->device_request; + + UDCDBG ("TX on EP0"); + /* select EP0 TX FIFO */ + outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); + + status = inw (UDC_STAT_FLG); + if (status & UDC_ACK) { + /* Check direction */ + if ((request->bmRequestType & USB_REQ_DIRECTION_MASK) == + USB_REQ_HOST2DEVICE) { + /* This tx interrupt must be for a control write status + * stage packet. + */ + UDCDBG ("ACK on EP0 control write status stage packet"); + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } else { + /* This tx interrupt must be for a control read data + * stage packet. + */ + int wLength = le16_to_cpu (request->wLength); + + /* Update our count of bytes sent so far in this + * transfer. + */ + endpoint->sent += endpoint->last; + + /* We are finished with this transfer if we have sent + * all of the bytes in our tx urb (urb->actual_length) + * unless we need a zero-length terminating packet. We + * need a zero-length terminating packet if we returned + * fewer bytes than were requested (wLength) by the host, + * and the number of bytes we returned is an exact + * multiple of the packet size endpoint->tx_packetSize. + */ + if ((endpoint->sent == ep0_urb->actual_length) + && ((ep0_urb->actual_length == wLength) + || (endpoint->last != + endpoint->tx_packetSize))) { + /* Done with control read data stage. */ + UDCDBG ("control read data stage complete"); + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + /* select EP0 RX FIFO to prepare for control + * read status stage. + */ + outw (UDC_EP_Sel, UDC_EP_NUM); + /* clear the EP0 RX FIFO */ + outw (UDC_Clr_EP, UDC_CTRL); + /* enable the EP0 RX FIFO */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect the EP0 RX FIFO */ + outw (0, UDC_EP_NUM); + } else { + /* We still have another packet of data to send + * in this control read data stage or else we + * need a zero-length terminating packet. + */ + UDCDBG ("ACK control read data stage packet"); + omap1510_write_noniso_tx_fifo (endpoint); + /* enable the EP0 tx FIFO to start transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } + } + } else if (status & UDC_STALL) { + UDCDBG ("EP0 stall during TX"); + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } else { + /* deselect EP0 TX FIFO */ + outw (UDC_EP_Dir, UDC_EP_NUM); + } +} + +/* Handle RX transaction on non-ISO endpoint. + * This function implements TRM Figure 14-27. + * The ep argument is a physical endpoint number for a non-ISO OUT endpoint + * in the range 1 to 15. + */ +static void omap1510_udc_epn_rx (int ep) +{ + unsigned short status; + + /* Check endpoint status */ + status = inw (UDC_STAT_FLG); + + if (status & UDC_ACK) { + int nbytes; + struct usb_endpoint_instance *endpoint = + omap1510_find_ep (ep); + + nbytes = omap1510_read_noniso_rx_fifo (endpoint); + usbd_rcv_complete (endpoint, nbytes, 0); + + /* enable rx FIFO to prepare for next packet */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + } else if (status & UDC_STALL) { + UDCDBGA ("STALL on RX endpoint %d", ep); + } else if (status & UDC_NAK) { + UDCDBGA ("NAK on RX ep %d", ep); + } else { + serial_printf ("omap-bi: RX on ep %d with status %x", ep, + status); + } +} + +/* Handle TX transaction on non-ISO endpoint. + * This function implements TRM Figure 14-29. + * The ep argument is a physical endpoint number for a non-ISO IN endpoint + * in the range 16 to 30. + */ +static void omap1510_udc_epn_tx (int ep) +{ + unsigned short status; + + /*serial_printf("omap1510_udc_epn_tx( %x )\n",ep); */ + + /* Check endpoint status */ + status = inw (UDC_STAT_FLG); + + if (status & UDC_ACK) { + struct usb_endpoint_instance *endpoint = + omap1510_find_ep (ep); + + /* We need to transmit a terminating zero-length packet now if + * we have sent all of the data in this URB and the transfer + * size was an exact multiple of the packet size. + */ + if (endpoint->tx_urb + && (endpoint->last == endpoint->tx_packetSize) + && (endpoint->tx_urb->actual_length - endpoint->sent - + endpoint->last == 0)) { + /* Prepare to transmit a zero-length packet. */ + endpoint->sent += endpoint->last; + /* write 0 bytes of data to FIFO */ + omap1510_write_noniso_tx_fifo (endpoint); + /* enable tx FIFO to start transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + } else if (endpoint->tx_urb + && endpoint->tx_urb->actual_length) { + /* retire the data that was just sent */ + usbd_tx_complete (endpoint); + /* Check to see if we have more data ready to transmit + * now. + */ + if (endpoint->tx_urb + && endpoint->tx_urb->actual_length) { + /* write data to FIFO */ + omap1510_write_noniso_tx_fifo (endpoint); + /* enable tx FIFO to start transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + } + } + } else if (status & UDC_STALL) { + UDCDBGA ("STALL on TX endpoint %d", ep); + } else if (status & UDC_NAK) { + UDCDBGA ("NAK on TX endpoint %d", ep); + } else { + /*serial_printf("omap-bi: TX on ep %d with status %x\n", ep, status); */ + } +} + + +/* +------------------------------------------------------------------------------- +*/ + +/* Handle general USB interrupts and dispatch according to type. + * This function implements TRM Figure 14-13. + */ +void omap1510_udc_irq (void) +{ + u16 irq_src = inw (UDC_IRQ_SRC); + int valid_irq = 0; + + if (!(irq_src & ~UDC_SOF_Flg)) /* ignore SOF interrupts ) */ + return; + + UDCDBGA ("< IRQ #%d start >- %x", udc_interrupts, irq_src); + /*serial_printf("< IRQ #%d start >- %x\n", udc_interrupts, irq_src); */ + + if (irq_src & UDC_DS_Chg) { + /* Device status changed */ + omap1510_udc_state_changed (); + valid_irq++; + } + if (irq_src & UDC_EP0_RX) { + /* Endpoint 0 receive */ + outw (UDC_EP0_RX, UDC_IRQ_SRC); /* ack interrupt */ + omap1510_udc_ep0_rx (udc_device->bus->endpoint_array + 0); + valid_irq++; + } + if (irq_src & UDC_EP0_TX) { + /* Endpoint 0 transmit */ + outw (UDC_EP0_TX, UDC_IRQ_SRC); /* ack interrupt */ + omap1510_udc_ep0_tx (udc_device->bus->endpoint_array + 0); + valid_irq++; + } + if (irq_src & UDC_Setup) { + /* Device setup */ + omap1510_udc_setup (udc_device->bus->endpoint_array + 0); + valid_irq++; + } + /*if (!valid_irq) */ + /* serial_printf("unknown interrupt, IRQ_SRC %.4x\n", irq_src); */ + UDCDBGA ("< IRQ #%d end >", udc_interrupts); + udc_interrupts++; +} + +/* This function implements TRM Figure 14-26. */ +void omap1510_udc_noniso_irq (void) +{ + unsigned short epnum; + unsigned short irq_src = inw (UDC_IRQ_SRC); + int valid_irq = 0; + + if (!(irq_src & (UDC_EPn_RX | UDC_EPn_TX))) + return; + + UDCDBGA ("non-ISO IRQ, IRQ_SRC %x", inw (UDC_IRQ_SRC)); + + if (irq_src & UDC_EPn_RX) { /* Endpoint N OUT transaction */ + /* Determine the endpoint number for this interrupt */ + epnum = (inw (UDC_EPN_STAT) & 0x0f00) >> 8; + UDCDBGA ("RX on ep %x", epnum); + + /* acknowledge interrupt */ + outw (UDC_EPn_RX, UDC_IRQ_SRC); + + if (epnum) { + /* select the endpoint FIFO */ + outw (UDC_EP_Sel | epnum, UDC_EP_NUM); + + omap1510_udc_epn_rx (epnum); + + /* deselect the endpoint FIFO */ + outw (epnum, UDC_EP_NUM); + } + valid_irq++; + } + if (irq_src & UDC_EPn_TX) { /* Endpoint N IN transaction */ + /* Determine the endpoint number for this interrupt */ + epnum = (inw (UDC_EPN_STAT) & 0x000f) | USB_DIR_IN; + UDCDBGA ("TX on ep %x", epnum); + + /* acknowledge interrupt */ + outw (UDC_EPn_TX, UDC_IRQ_SRC); + + if (epnum) { + /* select the endpoint FIFO */ + outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM); + + omap1510_udc_epn_tx (epnum); + + /* deselect the endpoint FIFO */ + outw (UDC_EP_Dir | epnum, UDC_EP_NUM); + } + valid_irq++; + } + if (!valid_irq) + serial_printf (": unknown non-ISO interrupt, IRQ_SRC %.4x\n", + irq_src); +} + +/* +------------------------------------------------------------------------------- +*/ + + +/* + * Start of public functions. + */ + +/* Called to start packet transmission. */ +int udc_endpoint_write (struct usb_endpoint_instance *endpoint) +{ + unsigned short epnum = + endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; + + UDCDBGA ("Starting transmit on ep %x", epnum); + + if (endpoint->tx_urb) { + /* select the endpoint FIFO */ + outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM); + /* write data to FIFO */ + omap1510_write_noniso_tx_fifo (endpoint); + /* enable tx FIFO to start transmission */ + outw (UDC_Set_FIFO_En, UDC_CTRL); + /* deselect the endpoint FIFO */ + outw (UDC_EP_Dir | epnum, UDC_EP_NUM); + } + + return 0; +} + +/* Start to initialize h/w stuff */ +int udc_init (void) +{ + u16 udc_rev; + uchar value; + ulong gpio; + int i; + + /* Let the device settle down before we start */ + for (i = 0; i < UDC_INIT_MDELAY; i++) udelay(1000); + + udc_device = NULL; + + UDCDBG ("starting"); + + /* Check peripheral reset. Must be 1 to make sure + MPU TIPB peripheral reset is inactive */ + UDCREG (ARM_RSTCT2); + + /* Set and check clock control. + * We might ought to be using the clock control API to do + * this instead of fiddling with the clock registers directly + * here. + */ + outw ((1 << 4) | (1 << 5), CLOCK_CTRL); + UDCREG (CLOCK_CTRL); + +#ifdef CONFIG_OMAP1510 + /* This code was originally implemented for OMAP1510 and + * therefore is only applicable for OMAP1510 boards. For + * OMAP5912 or OMAP16xx the register APLL_CTRL does not + * exist and DPLL_CTRL is already configured. + */ + + /* Set and check APLL */ + outw (0x0008, APLL_CTRL); + UDCREG (APLL_CTRL); + /* Set and check DPLL */ + outw (0x2210, DPLL_CTRL); + UDCREG (DPLL_CTRL); +#endif + /* Set and check SOFT + * The below line of code has been changed to perform a + * read-modify-write instead of a simple write for + * configuring the SOFT_REQ register. This allows the code + * to be compatible with OMAP5912 and OMAP16xx devices + */ + outw ((1 << 4) | (1 << 3) | 1 | (inw(SOFT_REQ)), SOFT_REQ); + + /* Short delay to wait for DPLL */ + udelay (1000); + + /* Print banner with device revision */ + udc_rev = inw (UDC_REV) & 0xff; +#ifdef CONFIG_OMAP1510 + printf ("USB: TI OMAP1510 USB function module rev %d.%d\n", + udc_rev >> 4, udc_rev & 0xf); +#endif + +#ifdef CONFIG_OMAP1610 + printf ("USB: TI OMAP5912 USB function module rev %d.%d\n", + udc_rev >> 4, udc_rev & 0xf); +#endif + +#ifdef CONFIG_OMAP_SX1 + i2c_read (0x32, 0x04, 1, &value, 1); + value |= 0x04; + i2c_write (0x32, 0x04, 1, &value, 1); + + i2c_read (0x32, 0x03, 1, &value, 1); + value |= 0x01; + i2c_write (0x32, 0x03, 1, &value, 1); + + gpio = inl(GPIO_PIN_CONTROL_REG); + gpio |= 0x0002; /* A_IRDA_OFF */ + gpio |= 0x0800; /* A_SWITCH */ + gpio |= 0x8000; /* A_USB_ON */ + outl (gpio, GPIO_PIN_CONTROL_REG); + + gpio = inl(GPIO_DIR_CONTROL_REG); + gpio &= ~0x0002; /* A_IRDA_OFF */ + gpio &= ~0x0800; /* A_SWITCH */ + gpio &= ~0x8000; /* A_USB_ON */ + outl (gpio, GPIO_DIR_CONTROL_REG); + + gpio = inl(GPIO_DATA_OUTPUT_REG); + gpio |= 0x0002; /* A_IRDA_OFF */ + gpio &= ~0x0800; /* A_SWITCH */ + gpio &= ~0x8000; /* A_USB_ON */ + outl (gpio, GPIO_DATA_OUTPUT_REG); +#endif + + /* The VBUS_MODE bit selects whether VBUS detection is done via + * software (1) or hardware (0). When software detection is + * selected, VBUS_CTRL selects whether USB is not connected (0) + * or connected (1). + */ + outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0); + outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0); + UDCREGL (FUNC_MUX_CTRL_0); + + /* + * At this point, device is ready for configuration... + */ + + UDCDBG ("disable USB interrupts"); + outw (0, UDC_IRQ_EN); + UDCREG (UDC_IRQ_EN); + + UDCDBG ("disable USB DMA"); + outw (0, UDC_DMA_IRQ_EN); + UDCREG (UDC_DMA_IRQ_EN); + + UDCDBG ("initialize SYSCON1"); + outw (UDC_Self_Pwr | UDC_Pullup_En, UDC_SYSCON1); + UDCREG (UDC_SYSCON1); + + return 0; +} + +/* Stall endpoint */ +static void udc_stall_ep (unsigned int ep_addr) +{ + /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ + int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + + UDCDBGA ("stall ep_addr %d", ep_addr); + + /* REVISIT? + * The OMAP TRM section 14.2.4.2 says we must check that the FIFO + * is empty before halting the endpoint. The current implementation + * doesn't check that the FIFO is empty. + */ + + if (!ep_num) { + outw (UDC_Stall_Cmd, UDC_SYSCON2); + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { + if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) { + /* we have a valid rx endpoint, so halt it */ + outw (UDC_EP_Sel | ep_num, UDC_EP_NUM); + outw (UDC_Set_Halt, UDC_CTRL); + outw (ep_num, UDC_EP_NUM); + } + } else { + if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) { + /* we have a valid tx endpoint, so halt it */ + outw (UDC_EP_Sel | UDC_EP_Dir | ep_num, UDC_EP_NUM); + outw (UDC_Set_Halt, UDC_CTRL); + outw (ep_num, UDC_EP_NUM); + } + } +} + +/* Reset endpoint */ +#if 0 +static void udc_reset_ep (unsigned int ep_addr) +{ + /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ + int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + + UDCDBGA ("reset ep_addr %d", ep_addr); + + if (!ep_num) { + /* control endpoint 0 can't be reset */ + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { + UDCDBGA ("UDC_EP_RX(%d) = 0x%04x", ep_num, + inw (UDC_EP_RX (ep_num))); + if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) { + /* we have a valid rx endpoint, so reset it */ + outw (ep_num | UDC_EP_Sel, UDC_EP_NUM); + outw (UDC_Reset_EP, UDC_CTRL); + outw (ep_num, UDC_EP_NUM); + UDCDBGA ("OUT endpoint %d reset", ep_num); + } + } else { + UDCDBGA ("UDC_EP_TX(%d) = 0x%04x", ep_num, + inw (UDC_EP_TX (ep_num))); + /* Resetting of tx endpoints seems to be causing the USB function + * module to fail, which causes problems when the driver is + * uninstalled. We'll skip resetting tx endpoints for now until + * we figure out what the problem is. + */ +#if 0 + if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) { + /* we have a valid tx endpoint, so reset it */ + outw (ep_num | UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); + outw (UDC_Reset_EP, UDC_CTRL); + outw (ep_num | UDC_EP_Dir, UDC_EP_NUM); + UDCDBGA ("IN endpoint %d reset", ep_num); + } +#endif + } +} +#endif + +/* ************************************************************************** */ + +/** + * udc_check_ep - check logical endpoint + * + * Return physical endpoint number to use for this logical endpoint or zero if not valid. + */ +#if 0 +int udc_check_ep (int logical_endpoint, int packetsize) +{ + if ((logical_endpoint == 0x80) || + ((logical_endpoint & 0x8f) != logical_endpoint)) { + return 0; + } + + switch (packetsize) { + case 8: + case 16: + case 32: + case 64: + case 128: + case 256: + case 512: + break; + default: + return 0; + } + + return EP_ADDR_TO_PHYS_EP (logical_endpoint); +} +#endif + +/* + * udc_setup_ep - setup endpoint + * + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep (struct usb_device_instance *device, + unsigned int ep, struct usb_endpoint_instance *endpoint) +{ + UDCDBGA ("setting up endpoint addr %x", endpoint->endpoint_address); + + /* This routine gets called by bi_modinit for endpoint 0 and from + * bi_config for all of the other endpoints. bi_config gets called + * during the DEVICE_CREATE, DEVICE_CONFIGURED, and + * DEVICE_SET_INTERFACE events. We need to reconfigure the OMAP packet + * RAM after bi_config scans the selected device configuration and + * initializes the endpoint structures, but before this routine enables + * the OUT endpoint FIFOs. Since bi_config calls this routine in a + * loop for endpoints 1 through UDC_MAX_ENDPOINTS, we reconfigure our + * packet RAM here when ep==1. + * I really hate to do this here, but it seems like the API exported + * by the USB bus interface controller driver to the usbd-bi module + * isn't quite right so there is no good place to do this. + */ + if (ep == 1) { + omap1510_deconfigure_device (); + omap1510_configure_device (device); + } + + if (endpoint && (ep < UDC_MAX_ENDPOINTS)) { + int ep_addr = endpoint->endpoint_address; + + if (!ep_addr) { + /* nothing to do for endpoint 0 */ + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* nothing to do for IN (tx) endpoints */ + } else { /* OUT (rx) endpoint */ + if (endpoint->rcv_packetSize) { + /*struct urb* urb = &(urb_out_array[ep&0xFF]); */ + /*urb->endpoint = endpoint; */ + /*urb->device = device; */ + /*urb->buffer_length = sizeof(urb->buffer); */ + + /*endpoint->rcv_urb = urb; */ + omap1510_prepare_endpoint_for_rx (ep_addr); + } + } + } +} + +/** + * udc_disable_ep - disable endpoint + * @ep: + * + * Disable specified endpoint + */ +#if 0 +void udc_disable_ep (unsigned int ep_addr) +{ + /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ + int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + struct usb_endpoint_instance *endpoint = omap1510_find_ep (ep_addr); /*udc_device->bus->endpoint_array + ep; */ + + UDCDBGA ("disable ep_addr %d", ep_addr); + + if (!ep_num) { + /* nothing to do for endpoint 0 */ ; + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + if (endpoint->tx_packetSize) { + /* we have a valid tx endpoint */ + /*usbd_flush_tx(endpoint); */ + endpoint->tx_urb = NULL; + } + } else { + if (endpoint->rcv_packetSize) { + /* we have a valid rx endpoint */ + /*usbd_flush_rcv(endpoint); */ + endpoint->rcv_urb = NULL; + } + } +} +#endif + +/* ************************************************************************** */ + +/** + * udc_connected - is the USB cable connected + * + * Return non-zero if cable is connected. + */ +#if 0 +int udc_connected (void) +{ + return ((inw (UDC_DEVSTAT) & UDC_ATT) == UDC_ATT); +} +#endif + +/* Turn on the USB connection by enabling the pullup resistor */ +void udc_connect (void) +{ + UDCDBG ("connect, enable Pullup"); + outl (0x00000018, FUNC_MUX_CTRL_D); +} + +/* Turn off the USB connection by disabling the pullup resistor */ +void udc_disconnect (void) +{ + UDCDBG ("disconnect, disable Pullup"); + outl (0x00000000, FUNC_MUX_CTRL_D); +} + +/* ************************************************************************** */ + + +/* + * udc_disable_interrupts - disable interrupts + * switch off interrupts + */ +#if 0 +void udc_disable_interrupts (struct usb_device_instance *device) +{ + UDCDBG ("disabling all interrupts"); + outw (0, UDC_IRQ_EN); +} +#endif + +/* ************************************************************************** */ + +/** + * udc_ep0_packetsize - return ep0 packetsize + */ +#if 0 +int udc_ep0_packetsize (void) +{ + return EP0_PACKETSIZE; +} +#endif + +/* Switch on the UDC */ +void udc_enable (struct usb_device_instance *device) +{ + UDCDBGA ("enable device %p, status %d", device, device->status); + + /* initialize driver state variables */ + udc_devstat = 0; + + /* Save the device structure pointer */ + udc_device = device; + + /* Setup ep0 urb */ + if (!ep0_urb) { + ep0_urb = + usbd_alloc_urb (udc_device, + udc_device->bus->endpoint_array); + } else { + serial_printf ("udc_enable: ep0_urb already allocated %p\n", + ep0_urb); + } + + UDCDBG ("Check clock status"); + UDCREG (STATUS_REQ); + + /* The VBUS_MODE bit selects whether VBUS detection is done via + * software (1) or hardware (0). When software detection is + * selected, VBUS_CTRL selects whether USB is not connected (0) + * or connected (1). + */ + outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_CTRL | UDC_VBUS_MODE, + FUNC_MUX_CTRL_0); + UDCREGL (FUNC_MUX_CTRL_0); + + omap1510_configure_device (device); +} + +/* Switch off the UDC */ +void udc_disable (void) +{ + UDCDBG ("disable UDC"); + + omap1510_deconfigure_device (); + + /* The VBUS_MODE bit selects whether VBUS detection is done via + * software (1) or hardware (0). When software detection is + * selected, VBUS_CTRL selects whether USB is not connected (0) + * or connected (1). + */ + outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0); + outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0); + UDCREGL (FUNC_MUX_CTRL_0); + + /* Free ep0 URB */ + if (ep0_urb) { + /*usbd_dealloc_urb(ep0_urb); */ + ep0_urb = NULL; + } + + /* Reset device pointer. + * We ought to do this here to balance the initialization of udc_device + * in udc_enable, but some of our other exported functions get called + * by the bus interface driver after udc_disable, so we have to hang on + * to the device pointer to avoid a null pointer dereference. */ + /* udc_device = NULL; */ +} + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup_events (struct usb_device_instance *device) +{ + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ + usbd_device_event_irq (device, DEVICE_INIT, 0); + + /* The DEVICE_CREATE event puts the USB device in the state + * STATE_ATTACHED. + */ + usbd_device_event_irq (device, DEVICE_CREATE, 0); + + /* Some USB controller driver implementations signal + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. + * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, + * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. + * The OMAP USB client controller has the capability to detect when the + * USB cable is connected to a powered USB bus via the ATT bit in the + * DEVSTAT register, so we will defer the DEVICE_HUB_CONFIGURED and + * DEVICE_RESET events until later. + */ + + udc_enable (device); +} + +/** + * udc_irq - do pseudo interrupts + */ +void udc_irq(void) +{ + /* Loop while we have interrupts. + * If we don't do this, the input chain + * polling delay is likely to miss + * host requests. + */ + while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) { + /* Handle any new IRQs */ + omap1510_udc_irq (); + omap1510_udc_noniso_irq (); + } +} + +/* Flow control */ +void udc_set_nak(int epid) +{ + /* TODO: implement this functionality in omap1510 */ +} + +void udc_unset_nak (int epid) +{ + /* TODO: implement this functionality in omap1510 */ +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c b/qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c new file mode 100644 index 000000000..8945c5b66 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.c @@ -0,0 +1,2047 @@ +/* + * Intel PXA25x and IXP4xx on-chip full speed USB device controllers + * + * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) + * Copyright (C) 2003 Robert Schwebel, Pengutronix + * Copyright (C) 2003 Benedikt Spranger, Pengutronix + * Copyright (C) 2003 David Brownell + * Copyright (C) 2003 Joshua Wise + * Copyright (C) 2012 Lukasz Dalek + * + * SPDX-License-Identifier: GPL-2.0+ + * + * MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); + */ + +#define CONFIG_USB_PXA25X_SMALL +#define DRIVER_NAME "pxa25x_udc_linux" +#define ARCH_HAS_PREFETCH + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pxa25x_udc.h" + +/* + * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x + * series processors. The UDC for the IXP 4xx series is very similar. + * There are fifteen endpoints, in addition to ep0. + * + * Such controller drivers work with a gadget driver. The gadget driver + * returns descriptors, implements configuration and data protocols used + * by the host to interact with this device, and allocates endpoints to + * the different protocol interfaces. The controller driver virtualizes + * usb hardware so that the gadget drivers will be more portable. + * + * This UDC hardware wants to implement a bit too much USB protocol, so + * it constrains the sorts of USB configuration change events that work. + * The errata for these chips are misleading; some "fixed" bugs from + * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. + * + * Note that the UDC hardware supports DMA (except on IXP) but that's + * not used here. IN-DMA (to host) is simple enough, when the data is + * suitably aligned (16 bytes) ... the network stack doesn't do that, + * other software can. OUT-DMA is buggy in most chip versions, as well + * as poorly designed (data toggle not automatic). So this driver won't + * bother using DMA. (Mostly-working IN-DMA support was available in + * kernels before 2.6.23, but was never enabled or well tested.) + */ + +#define DRIVER_VERSION "18-August-2012" +#define DRIVER_DESC "PXA 25x USB Device Controller driver" + +static const char driver_name[] = "pxa25x_udc"; +static const char ep0name[] = "ep0"; + +/* Watchdog */ +static inline void start_watchdog(struct pxa25x_udc *udc) +{ + debug("Started watchdog\n"); + udc->watchdog.base = get_timer(0); + udc->watchdog.running = 1; +} + +static inline void stop_watchdog(struct pxa25x_udc *udc) +{ + udc->watchdog.running = 0; + debug("Stopped watchdog\n"); +} + +static inline void test_watchdog(struct pxa25x_udc *udc) +{ + if (!udc->watchdog.running) + return; + + debug("watchdog %ld %ld\n", get_timer(udc->watchdog.base), + udc->watchdog.period); + + if (get_timer(udc->watchdog.base) >= udc->watchdog.period) { + stop_watchdog(udc); + udc->watchdog.function(udc); + } +} + +static void udc_watchdog(struct pxa25x_udc *dev) +{ + uint32_t udccs0 = readl(&dev->regs->udccs[0]); + + debug("Fired up udc_watchdog\n"); + + local_irq_disable(); + if (dev->ep0state == EP0_STALL + && (udccs0 & UDCCS0_FST) == 0 + && (udccs0 & UDCCS0_SST) == 0) { + writel(UDCCS0_FST|UDCCS0_FTF, &dev->regs->udccs[0]); + debug("ep0 re-stall\n"); + start_watchdog(dev); + } + local_irq_enable(); +} + +#ifdef DEBUG + +static const char * const state_name[] = { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", + "EP0_END_XFER", "EP0_STALL" +}; + +static void +dump_udccr(const char *label) +{ + u32 udccr = readl(&UDC_REGS->udccr); + debug("%s %02X =%s%s%s%s%s%s%s%s\n", + label, udccr, + (udccr & UDCCR_REM) ? " rem" : "", + (udccr & UDCCR_RSTIR) ? " rstir" : "", + (udccr & UDCCR_SRM) ? " srm" : "", + (udccr & UDCCR_SUSIR) ? " susir" : "", + (udccr & UDCCR_RESIR) ? " resir" : "", + (udccr & UDCCR_RSM) ? " rsm" : "", + (udccr & UDCCR_UDA) ? " uda" : "", + (udccr & UDCCR_UDE) ? " ude" : ""); +} + +static void +dump_udccs0(const char *label) +{ + u32 udccs0 = readl(&UDC_REGS->udccs[0]); + + debug("%s %s %02X =%s%s%s%s%s%s%s%s\n", + label, state_name[the_controller->ep0state], udccs0, + (udccs0 & UDCCS0_SA) ? " sa" : "", + (udccs0 & UDCCS0_RNE) ? " rne" : "", + (udccs0 & UDCCS0_FST) ? " fst" : "", + (udccs0 & UDCCS0_SST) ? " sst" : "", + (udccs0 & UDCCS0_DRWF) ? " dwrf" : "", + (udccs0 & UDCCS0_FTF) ? " ftf" : "", + (udccs0 & UDCCS0_IPR) ? " ipr" : "", + (udccs0 & UDCCS0_OPR) ? " opr" : ""); +} + +static void +dump_state(struct pxa25x_udc *dev) +{ + u32 tmp; + unsigned i; + + debug("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", + state_name[dev->ep0state], + readl(&UDC_REGS->uicr1), readl(&UDC_REGS->uicr0), + readl(&UDC_REGS->usir1), readl(&UDC_REGS->usir0), + readl(&UDC_REGS->ufnrh), readl(&UDC_REGS->ufnrl)); + dump_udccr("udccr"); + if (dev->has_cfr) { + tmp = readl(&UDC_REGS->udccfr); + debug("udccfr %02X =%s%s\n", tmp, + (tmp & UDCCFR_AREN) ? " aren" : "", + (tmp & UDCCFR_ACM) ? " acm" : ""); + } + + if (!dev->driver) { + debug("no gadget driver bound\n"); + return; + } else + debug("ep0 driver '%s'\n", "ether"); + + dump_udccs0("udccs0"); + debug("ep0 IN %lu/%lu, OUT %lu/%lu\n", + dev->stats.write.bytes, dev->stats.write.ops, + dev->stats.read.bytes, dev->stats.read.ops); + + for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) { + if (dev->ep[i].desc == NULL) + continue; + debug("udccs%d = %02x\n", i, *dev->ep->reg_udccs); + } +} + +#else /* DEBUG */ + +static inline void dump_udccr(const char *label) { } +static inline void dump_udccs0(const char *label) { } +static inline void dump_state(struct pxa25x_udc *dev) { } + +#endif /* DEBUG */ + +/* + * --------------------------------------------------------------------------- + * endpoint related parts of the api to the usb controller hardware, + * used by gadget driver; and the inner talker-to-hardware core. + * --------------------------------------------------------------------------- + */ + +static void pxa25x_ep_fifo_flush(struct usb_ep *ep); +static void nuke(struct pxa25x_ep *, int status); + +/* one GPIO should control a D+ pullup, so host sees this device (or not) */ +static void pullup_off(void) +{ + struct pxa2xx_udc_mach_info *mach = the_controller->mach; + + if (mach->udc_command) + mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); +} + +static void pullup_on(void) +{ + struct pxa2xx_udc_mach_info *mach = the_controller->mach; + + if (mach->udc_command) + mach->udc_command(PXA2XX_UDC_CMD_CONNECT); +} + +static void pio_irq_enable(int bEndpointAddress) +{ + bEndpointAddress &= 0xf; + if (bEndpointAddress < 8) { + clrbits_le32(&the_controller->regs->uicr0, + 1 << bEndpointAddress); + } else { + bEndpointAddress -= 8; + clrbits_le32(&the_controller->regs->uicr1, + 1 << bEndpointAddress); + } +} + +static void pio_irq_disable(int bEndpointAddress) +{ + bEndpointAddress &= 0xf; + if (bEndpointAddress < 8) { + setbits_le32(&the_controller->regs->uicr0, + 1 << bEndpointAddress); + } else { + bEndpointAddress -= 8; + setbits_le32(&the_controller->regs->uicr1, + 1 << bEndpointAddress); + } +} + +static inline void udc_set_mask_UDCCR(int mask) +{ + /* + * The UDCCR reg contains mask and interrupt status bits, + * so using '|=' isn't safe as it may ack an interrupt. + */ + const uint32_t mask_bits = UDCCR_REM | UDCCR_SRM | UDCCR_UDE; + + mask &= mask_bits; + clrsetbits_le32(&the_controller->regs->udccr, ~mask_bits, mask); +} + +static inline void udc_clear_mask_UDCCR(int mask) +{ + const uint32_t mask_bits = UDCCR_REM | UDCCR_SRM | UDCCR_UDE; + + mask = ~mask & mask_bits; + clrbits_le32(&the_controller->regs->udccr, ~mask); +} + +static inline void udc_ack_int_UDCCR(int mask) +{ + const uint32_t mask_bits = UDCCR_REM | UDCCR_SRM | UDCCR_UDE; + + mask &= ~mask_bits; + clrsetbits_le32(&the_controller->regs->udccr, ~mask_bits, mask); +} + +/* + * endpoint enable/disable + * + * we need to verify the descriptors used to enable endpoints. since pxa25x + * endpoint configurations are fixed, and are pretty much always enabled, + * there's not a lot to manage here. + * + * because pxa25x can't selectively initialize bulk (or interrupt) endpoints, + * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except + * for a single interface (with only the default altsetting) and for gadget + * drivers that don't halt endpoints (not reset by set_interface). that also + * means that if you use ISO, you must violate the USB spec rule that all + * iso endpoints must be in non-default altsettings. + */ +static int pxa25x_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct pxa25x_ep *ep; + struct pxa25x_udc *dev; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep->fifo_size < + le16_to_cpu(get_unaligned(&desc->wMaxPacketSize))) { + printf("%s, bad ep or descriptor\n", __func__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + printf("%s, %s type mismatch\n", __func__, _ep->name); + return -EINVAL; + } + + /* hardware _could_ do smaller, but driver doesn't */ + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)) + != BULK_FIFO_SIZE) + || !get_unaligned(&desc->wMaxPacketSize)) { + printf("%s, bad %s maxpacket\n", __func__, _ep->name); + return -ERANGE; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + printf("%s, bogus device state\n", __func__); + return -ESHUTDOWN; + } + + ep->desc = desc; + ep->stopped = 0; + ep->pio_irqs = 0; + ep->ep.maxpacket = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); + + /* flush fifo (mostly for OUT buffers) */ + pxa25x_ep_fifo_flush(_ep); + + /* ... reset halt state too, if we could ... */ + + debug("enabled %s\n", _ep->name); + return 0; +} + +static int pxa25x_ep_disable(struct usb_ep *_ep) +{ + struct pxa25x_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (!_ep || !ep->desc) { + printf("%s, %s not enabled\n", __func__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + local_irq_save(flags); + + nuke(ep, -ESHUTDOWN); + + /* flush fifo (mostly for IN buffers) */ + pxa25x_ep_fifo_flush(_ep); + + ep->desc = NULL; + ep->stopped = 1; + + local_irq_restore(flags); + debug("%s disabled\n", _ep->name); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* + * for the pxa25x, these can just wrap kmalloc/kfree. gadget drivers + * must still pass correctly initialized endpoints, since other controller + * drivers may care about how it's currently set up (dma issues etc). + */ + +/* + * pxa25x_ep_alloc_request - allocate a request data structure + */ +static struct usb_request * +pxa25x_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct pxa25x_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + return &req->req; +} + + +/* + * pxa25x_ep_free_request - deallocate a request data structure + */ +static void +pxa25x_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct pxa25x_request *req; + + req = container_of(_req, struct pxa25x_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +/*-------------------------------------------------------------------------*/ + +/* + * done - retire a request; caller blocked irqs + */ +static void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status) +{ + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + debug("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + req->req.complete(&ep->ep, &req->req); + ep->stopped = stopped; +} + + +static inline void ep0_idle(struct pxa25x_udc *dev) +{ + dev->ep0state = EP0_IDLE; +} + +static int +write_packet(u32 *uddr, struct pxa25x_request *req, unsigned max) +{ + u8 *buf; + unsigned length, count; + + debug("%s(): uddr %p\n", __func__, uddr); + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + /* how big will this packet be? */ + length = min(req->req.length - req->req.actual, max); + req->req.actual += length; + + count = length; + while (likely(count--)) + writeb(*buf++, uddr); + + return length; +} + +/* + * write to an IN endpoint fifo, as many packets as possible. + * irqs will use this to write the rest later. + * caller guarantees at least one packet buffer is ready (or a zlp). + */ +static int +write_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req) +{ + unsigned max; + + max = le16_to_cpu(get_unaligned(&ep->desc->wMaxPacketSize)); + do { + unsigned count; + int is_last, is_short; + + count = write_packet(ep->reg_uddr, req, max); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = is_short = 1; + else { + if (likely(req->req.length != req->req.actual) + || req->req.zero) + is_last = 0; + else + is_last = 1; + /* interrupt/iso maxpacket may not fill the fifo */ + is_short = unlikely(max < ep->fifo_size); + } + + debug_cond(NOISY, "wrote %s %d bytes%s%s %d left %p\n", + ep->ep.name, count, + is_last ? "/L" : "", is_short ? "/S" : "", + req->req.length - req->req.actual, req); + + /* + * let loose that packet. maybe try writing another one, + * double buffering might work. TSP, TPC, and TFS + * bit values are the same for all normal IN endpoints. + */ + writel(UDCCS_BI_TPC, ep->reg_udccs); + if (is_short) + writel(UDCCS_BI_TSP, ep->reg_udccs); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + if (list_empty(&ep->queue)) + pio_irq_disable(ep->bEndpointAddress); + return 1; + } + + /* + * TODO experiment: how robust can fifo mode tweaking be? + * double buffering is off in the default fifo mode, which + * prevents TFS from being set here. + */ + + } while (readl(ep->reg_udccs) & UDCCS_BI_TFS); + return 0; +} + +/* + * caller asserts req->pending (ep0 irq status nyet cleared); starts + * ep0 data stage. these chips want very simple state transitions. + */ +static inline +void ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag) +{ + writel(flags|UDCCS0_SA|UDCCS0_OPR, &dev->regs->udccs[0]); + writel(USIR0_IR0, &dev->regs->usir0); + dev->req_pending = 0; + debug_cond(NOISY, "%s() %s, udccs0: %02x/%02x usir: %X.%X\n", + __func__, tag, readl(&dev->regs->udccs[0]), flags, + readl(&dev->regs->usir1), readl(&dev->regs->usir0)); +} + +static int +write_ep0_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req) +{ + unsigned count; + int is_short; + + count = write_packet(&ep->dev->regs->uddr0, req, EP0_FIFO_SIZE); + ep->dev->stats.write.bytes += count; + + /* last packet "must be" short (or a zlp) */ + is_short = (count != EP0_FIFO_SIZE); + + debug_cond(NOISY, "ep0in %d bytes %d left %p\n", count, + req->req.length - req->req.actual, req); + + if (unlikely(is_short)) { + if (ep->dev->req_pending) + ep0start(ep->dev, UDCCS0_IPR, "short IN"); + else + writel(UDCCS0_IPR, &ep->dev->regs->udccs[0]); + + count = req->req.length; + done(ep, req, 0); + ep0_idle(ep->dev); + + /* + * This seems to get rid of lost status irqs in some cases: + * host responds quickly, or next request involves config + * change automagic, or should have been hidden, or ... + * + * FIXME get rid of all udelays possible... + */ + if (count >= EP0_FIFO_SIZE) { + count = 100; + do { + if ((readl(&ep->dev->regs->udccs[0]) & + UDCCS0_OPR) != 0) { + /* clear OPR, generate ack */ + writel(UDCCS0_OPR, + &ep->dev->regs->udccs[0]); + break; + } + count--; + udelay(1); + } while (count); + } + } else if (ep->dev->req_pending) + ep0start(ep->dev, 0, "IN"); + + return is_short; +} + + +/* + * read_fifo - unload packet(s) from the fifo we use for usb OUT + * transfers and put them into the request. caller should have made + * sure there's at least one packet ready. + * + * returns true if the request completed because of short packet or the + * request buffer having filled (and maybe overran till end-of-packet). + */ +static int +read_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req) +{ + u32 udccs; + u8 *buf; + unsigned bufferspace, count, is_short; + + for (;;) { + /* + * make sure there's a packet in the FIFO. + * UDCCS_{BO,IO}_RPC are all the same bit value. + * UDCCS_{BO,IO}_RNE are all the same bit value. + */ + udccs = readl(ep->reg_udccs); + if (unlikely((udccs & UDCCS_BO_RPC) == 0)) + break; + buf = req->req.buf + req->req.actual; + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + if (likely(udccs & UDCCS_BO_RNE)) { + count = 1 + (0x0ff & readl(ep->reg_ubcr)); + req->req.actual += min(count, bufferspace); + } else /* zlp */ + count = 0; + is_short = (count < ep->ep.maxpacket); + debug_cond(NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, udccs, count, + is_short ? "/S" : "", + req, req->req.actual, req->req.length); + while (likely(count-- != 0)) { + u8 byte = readb(ep->reg_uddr); + + if (unlikely(bufferspace == 0)) { + /* + * this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + printf("%s overflow %d\n", + ep->ep.name, count); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + bufferspace--; + } + } + writel(UDCCS_BO_RPC, ep->reg_udccs); + /* RPC/RSP/RNE could now reflect the other packet buffer */ + + /* iso is one request per packet */ + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (udccs & UDCCS_IO_ROF) + req->req.status = -EHOSTUNREACH; + /* more like "is_done" */ + is_short = 1; + } + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + if (list_empty(&ep->queue)) + pio_irq_disable(ep->bEndpointAddress); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + } + return 0; +} + +/* + * special ep0 version of the above. no UBCR0 or double buffering; status + * handshaking is magic. most device protocols don't need control-OUT. + * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other + * protocols do use them. + */ +static int +read_ep0_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req) +{ + u8 *buf, byte; + unsigned bufferspace; + + buf = req->req.buf + req->req.actual; + bufferspace = req->req.length - req->req.actual; + + while (readl(&ep->dev->regs->udccs[0]) & UDCCS0_RNE) { + byte = (u8)readb(&ep->dev->regs->uddr0); + + if (unlikely(bufferspace == 0)) { + /* + * this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + printf("%s overflow\n", ep->ep.name); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + req->req.actual++; + bufferspace--; + } + } + + writel(UDCCS0_OPR | UDCCS0_IPR, &ep->dev->regs->udccs[0]); + + /* completion */ + if (req->req.actual >= req->req.length) + return 1; + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int +pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct pxa25x_request *req; + struct pxa25x_ep *ep; + struct pxa25x_udc *dev; + unsigned long flags; + + req = container_of(_req, struct pxa25x_request, req); + if (unlikely(!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue))) { + printf("%s, bad params\n", __func__); + return -EINVAL; + } + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + printf("%s, bad ep\n", __func__); + return -EINVAL; + } + + dev = ep->dev; + if (unlikely(!dev->driver + || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + printf("%s, bogus device state\n", __func__); + return -ESHUTDOWN; + } + + /* + * iso is always one packet per request, that's the only way + * we can report per-packet status. that also helps with dma. + */ + if (unlikely(ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + && req->req.length > + le16_to_cpu(get_unaligned(&ep->desc->wMaxPacketSize)))) + return -EMSGSIZE; + + debug_cond(NOISY, "%s queue req %p, len %d buf %p\n", + _ep->name, _req, _req->length, _req->buf); + + local_irq_save(flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) { + if (ep->desc == NULL/* ep0 */) { + unsigned length = _req->length; + + switch (dev->ep0state) { + case EP0_IN_DATA_PHASE: + dev->stats.write.ops++; + if (write_ep0_fifo(ep, req)) + req = NULL; + break; + + case EP0_OUT_DATA_PHASE: + dev->stats.read.ops++; + /* messy ... */ + if (dev->req_config) { + debug("ep0 config ack%s\n", + dev->has_cfr ? "" : " raced"); + if (dev->has_cfr) + writel(UDCCFR_AREN|UDCCFR_ACM + |UDCCFR_MB1, + &ep->dev->regs->udccfr); + done(ep, req, 0); + dev->ep0state = EP0_END_XFER; + local_irq_restore(flags); + return 0; + } + if (dev->req_pending) + ep0start(dev, UDCCS0_IPR, "OUT"); + if (length == 0 || + ((readl( + &ep->dev->regs->udccs[0]) + & UDCCS0_RNE) != 0 + && read_ep0_fifo(ep, req))) { + ep0_idle(dev); + done(ep, req, 0); + req = NULL; + } + break; + + default: + printf("ep0 i/o, odd state %d\n", + dev->ep0state); + local_irq_restore(flags); + return -EL2HLT; + } + /* can the FIFO can satisfy the request immediately? */ + } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { + if ((readl(ep->reg_udccs) & UDCCS_BI_TFS) != 0 + && write_fifo(ep, req)) + req = NULL; + } else if ((readl(ep->reg_udccs) & UDCCS_BO_RFS) != 0 + && read_fifo(ep, req)) { + req = NULL; + } + + if (likely(req && ep->desc)) + pio_irq_enable(ep->bEndpointAddress); + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != NULL)) + list_add_tail(&req->queue, &ep->queue); + local_irq_restore(flags); + + return 0; +} + + +/* + * nuke - dequeue ALL requests + */ +static void nuke(struct pxa25x_ep *ep, int status) +{ + struct pxa25x_request *req; + + /* called with irqs blocked */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct pxa25x_request, + queue); + done(ep, req, status); + } + if (ep->desc) + pio_irq_disable(ep->bEndpointAddress); +} + + +/* dequeue JUST ONE request */ +static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct pxa25x_ep *ep; + struct pxa25x_request *req; + unsigned long flags; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + local_irq_save(flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + local_irq_restore(flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + local_irq_restore(flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct pxa25x_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (unlikely(!_ep + || (!ep->desc && ep->ep.name != ep0name)) + || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + printf("%s, bad ep\n", __func__); + return -EINVAL; + } + if (value == 0) { + /* + * this path (reset toggle+halt) is needed to implement + * SET_INTERFACE on normal hardware. but it can't be + * done from software on the PXA UDC, and the hardware + * forgets to do it as part of SET_INTERFACE automagic. + */ + printf("only host can clear %s halt\n", _ep->name); + return -EROFS; + } + + local_irq_save(flags); + + if ((ep->bEndpointAddress & USB_DIR_IN) != 0 + && ((readl(ep->reg_udccs) & UDCCS_BI_TFS) == 0 + || !list_empty(&ep->queue))) { + local_irq_restore(flags); + return -EAGAIN; + } + + /* FST bit is the same for control, bulk in, bulk out, interrupt in */ + writel(UDCCS_BI_FST|UDCCS_BI_FTF, ep->reg_udccs); + + /* ep0 needs special care */ + if (!ep->desc) { + start_watchdog(ep->dev); + ep->dev->req_pending = 0; + ep->dev->ep0state = EP0_STALL; + + /* and bulk/intr endpoints like dropping stalls too */ + } else { + unsigned i; + for (i = 0; i < 1000; i += 20) { + if (readl(ep->reg_udccs) & UDCCS_BI_SST) + break; + udelay(20); + } + } + local_irq_restore(flags); + + debug("%s halt\n", _ep->name); + return 0; +} + +static int pxa25x_ep_fifo_status(struct usb_ep *_ep) +{ + struct pxa25x_ep *ep; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (!_ep) { + printf("%s, bad ep\n", __func__); + return -ENODEV; + } + /* pxa can't report unclaimed bytes from IN fifos */ + if ((ep->bEndpointAddress & USB_DIR_IN) != 0) + return -EOPNOTSUPP; + if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN + || (readl(ep->reg_udccs) & UDCCS_BO_RFS) == 0) + return 0; + else + return (readl(ep->reg_ubcr) & 0xfff) + 1; +} + +static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) +{ + struct pxa25x_ep *ep; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { + printf("%s, bad ep\n", __func__); + return; + } + + /* toggle and halt bits stay unchanged */ + + /* for OUT, just read and discard the FIFO contents. */ + if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { + while (((readl(ep->reg_udccs)) & UDCCS_BO_RNE) != 0) + (void)readb(ep->reg_uddr); + return; + } + + /* most IN status is the same, but ISO can't stall */ + writel(UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR + | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + ? 0 : UDCCS_BI_SST), ep->reg_udccs); +} + + +static struct usb_ep_ops pxa25x_ep_ops = { + .enable = pxa25x_ep_enable, + .disable = pxa25x_ep_disable, + + .alloc_request = pxa25x_ep_alloc_request, + .free_request = pxa25x_ep_free_request, + + .queue = pxa25x_ep_queue, + .dequeue = pxa25x_ep_dequeue, + + .set_halt = pxa25x_ep_set_halt, + .fifo_status = pxa25x_ep_fifo_status, + .fifo_flush = pxa25x_ep_fifo_flush, +}; + + +/* --------------------------------------------------------------------------- + * device-scoped parts of the api to the usb controller hardware + * --------------------------------------------------------------------------- + */ + +static int pxa25x_udc_get_frame(struct usb_gadget *_gadget) +{ + return ((readl(&the_controller->regs->ufnrh) & 0x07) << 8) | + (readl(&the_controller->regs->ufnrl) & 0xff); +} + +static int pxa25x_udc_wakeup(struct usb_gadget *_gadget) +{ + /* host may not have enabled remote wakeup */ + if ((readl(&the_controller->regs->udccs[0]) & UDCCS0_DRWF) == 0) + return -EHOSTUNREACH; + udc_set_mask_UDCCR(UDCCR_RSM); + return 0; +} + +static void stop_activity(struct pxa25x_udc *, struct usb_gadget_driver *); +static void udc_enable(struct pxa25x_udc *); +static void udc_disable(struct pxa25x_udc *); + +/* + * We disable the UDC -- and its 48 MHz clock -- whenever it's not + * in active use. + */ +static int pullup(struct pxa25x_udc *udc) +{ + if (udc->pullup) + pullup_on(); + else + pullup_off(); + + + int is_active = udc->pullup; + if (is_active) { + if (!udc->active) { + udc->active = 1; + udc_enable(udc); + } + } else { + if (udc->active) { + if (udc->gadget.speed != USB_SPEED_UNKNOWN) + stop_activity(udc, udc->driver); + udc_disable(udc); + udc->active = 0; + } + + } + return 0; +} + +/* VBUS reporting logically comes from a transceiver */ +static int pxa25x_udc_vbus_session(struct usb_gadget *_gadget, int is_active) +{ + struct pxa25x_udc *udc; + + udc = container_of(_gadget, struct pxa25x_udc, gadget); + printf("vbus %s\n", is_active ? "supplied" : "inactive"); + pullup(udc); + return 0; +} + +/* drivers may have software control over D+ pullup */ +static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) +{ + struct pxa25x_udc *udc; + + udc = container_of(_gadget, struct pxa25x_udc, gadget); + + /* not all boards support pullup control */ + if (!udc->mach->udc_command) + return -EOPNOTSUPP; + + udc->pullup = (is_active != 0); + pullup(udc); + return 0; +} + +/* + * boards may consume current from VBUS, up to 100-500mA based on config. + * the 500uA suspend ceiling means that exclusively vbus-powered PXA designs + * violate USB specs. + */ +static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) +{ + return -EOPNOTSUPP; +} + +static const struct usb_gadget_ops pxa25x_udc_ops = { + .get_frame = pxa25x_udc_get_frame, + .wakeup = pxa25x_udc_wakeup, + .vbus_session = pxa25x_udc_vbus_session, + .pullup = pxa25x_udc_pullup, + .vbus_draw = pxa25x_udc_vbus_draw, +}; + +/*-------------------------------------------------------------------------*/ + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct pxa25x_udc *dev) +{ + /* block all irqs */ + udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); + writel(0xff, &dev->regs->uicr0); + writel(0xff, &dev->regs->uicr1); + writel(UFNRH_SIM, &dev->regs->ufnrh); + + /* if hardware supports it, disconnect from usb */ + pullup_off(); + + udc_clear_mask_UDCCR(UDCCR_UDE); + + ep0_idle(dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; +} + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct pxa25x_udc *dev) +{ + u32 i; + + /* device/ep0 records init */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + dev->ep0state = EP0_IDLE; + + /* basic endpoint records init */ + for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { + struct pxa25x_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->desc = NULL; + ep->stopped = 0; + INIT_LIST_HEAD(&ep->queue); + ep->pio_irqs = 0; + } + + /* the rest was statically initialized, and is read-only */ +} + +/* + * until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static void udc_enable(struct pxa25x_udc *dev) +{ + debug("udc: enabling udc\n"); + + udc_clear_mask_UDCCR(UDCCR_UDE); + + /* + * Try to clear these bits before we enable the udc. + * Do not touch reset ack bit, we would take care of it in + * interrupt handle routine + */ + udc_ack_int_UDCCR(UDCCR_SUSIR|UDCCR_RESIR); + + ep0_idle(dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->stats.irqs = 0; + + /* + * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: + * - enable UDC + * - if RESET is already in progress, ack interrupt + * - unmask reset interrupt + */ + udc_set_mask_UDCCR(UDCCR_UDE); + if (!(readl(&dev->regs->udccr) & UDCCR_UDA)) + udc_ack_int_UDCCR(UDCCR_RSTIR); + + if (dev->has_cfr /* UDC_RES2 is defined */) { + /* + * pxa255 (a0+) can avoid a set_config race that could + * prevent gadget drivers from configuring correctly + */ + writel(UDCCFR_ACM | UDCCFR_MB1, &dev->regs->udccfr); + } + + /* enable suspend/resume and reset irqs */ + udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); + + /* enable ep0 irqs */ + clrbits_le32(&dev->regs->uicr0, UICR0_IM0); + + /* if hardware supports it, pullup D+ and wait for reset */ + pullup_on(); +} + +static inline void clear_ep_state(struct pxa25x_udc *dev) +{ + unsigned i; + + /* + * hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint + * fifos, and pending transactions mustn't be continued in any case. + */ + for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) + nuke(&dev->ep[i], -ECONNABORTED); +} + +static void handle_ep0(struct pxa25x_udc *dev) +{ + u32 udccs0 = readl(&dev->regs->udccs[0]); + struct pxa25x_ep *ep = &dev->ep[0]; + struct pxa25x_request *req; + union { + struct usb_ctrlrequest r; + u8 raw[8]; + u32 word[2]; + } u; + + if (list_empty(&ep->queue)) + req = NULL; + else + req = list_entry(ep->queue.next, struct pxa25x_request, queue); + + /* clear stall status */ + if (udccs0 & UDCCS0_SST) { + nuke(ep, -EPIPE); + writel(UDCCS0_SST, &dev->regs->udccs[0]); + stop_watchdog(dev); + ep0_idle(dev); + } + + /* previous request unfinished? non-error iff back-to-back ... */ + if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { + nuke(ep, 0); + stop_watchdog(dev); + ep0_idle(dev); + } + + switch (dev->ep0state) { + case EP0_IDLE: + /* late-breaking status? */ + udccs0 = readl(&dev->regs->udccs[0]); + + /* start control request? */ + if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) + == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) { + int i; + + nuke(ep, -EPROTO); + + /* read SETUP packet */ + for (i = 0; i < 8; i++) { + if (unlikely(!(readl(&dev->regs->udccs[0]) & + UDCCS0_RNE))) { +bad_setup: + debug("SETUP %d!\n", i); + goto stall; + } + u.raw[i] = (u8)readb(&dev->regs->uddr0); + } + if (unlikely((readl(&dev->regs->udccs[0]) & + UDCCS0_RNE) != 0)) + goto bad_setup; + +got_setup: + debug("SETUP %02x.%02x v%04x i%04x l%04x\n", + u.r.bRequestType, u.r.bRequest, + le16_to_cpu(u.r.wValue), + le16_to_cpu(u.r.wIndex), + le16_to_cpu(u.r.wLength)); + + /* cope with automagic for some standard requests. */ + dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + dev->req_config = 0; + dev->req_pending = 1; + switch (u.r.bRequest) { + /* hardware restricts gadget drivers here! */ + case USB_REQ_SET_CONFIGURATION: + debug("GOT SET_CONFIGURATION\n"); + if (u.r.bRequestType == USB_RECIP_DEVICE) { + /* + * reflect hardware's automagic + * up to the gadget driver. + */ +config_change: + dev->req_config = 1; + clear_ep_state(dev); + /* + * if !has_cfr, there's no synch + * else use AREN (later) not SA|OPR + * USIR0_IR0 acts edge sensitive + */ + } + break; + /* ... and here, even more ... */ + case USB_REQ_SET_INTERFACE: + if (u.r.bRequestType == USB_RECIP_INTERFACE) { + /* + * udc hardware is broken by design: + * - altsetting may only be zero; + * - hw resets all interfaces' eps; + * - ep reset doesn't include halt(?). + */ + printf("broken set_interface (%d/%d)\n", + le16_to_cpu(u.r.wIndex), + le16_to_cpu(u.r.wValue)); + goto config_change; + } + break; + /* hardware was supposed to hide this */ + case USB_REQ_SET_ADDRESS: + debug("GOT SET ADDRESS\n"); + if (u.r.bRequestType == USB_RECIP_DEVICE) { + ep0start(dev, 0, "address"); + return; + } + break; + } + + if (u.r.bRequestType & USB_DIR_IN) + dev->ep0state = EP0_IN_DATA_PHASE; + else + dev->ep0state = EP0_OUT_DATA_PHASE; + + i = dev->driver->setup(&dev->gadget, &u.r); + if (i < 0) { + /* hardware automagic preventing STALL... */ + if (dev->req_config) { + /* + * hardware sometimes neglects to tell + * tell us about config change events, + * so later ones may fail... + */ + printf("config change %02x fail %d?\n", + u.r.bRequest, i); + return; + /* + * TODO experiment: if has_cfr, + * hardware didn't ACK; maybe we + * could actually STALL! + */ + } + if (0) { +stall: + /* uninitialized when goto stall */ + i = 0; + } + debug("protocol STALL, " + "%02x err %d\n", + readl(&dev->regs->udccs[0]), i); + + /* + * the watchdog timer helps deal with cases + * where udc seems to clear FST wrongly, and + * then NAKs instead of STALLing. + */ + ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); + start_watchdog(dev); + dev->ep0state = EP0_STALL; + + /* deferred i/o == no response yet */ + } else if (dev->req_pending) { + if (likely(dev->ep0state == EP0_IN_DATA_PHASE + || dev->req_std || u.r.wLength)) + ep0start(dev, 0, "defer"); + else + ep0start(dev, UDCCS0_IPR, "defer/IPR"); + } + + /* expect at least one data or status stage irq */ + return; + + } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA)) + == (UDCCS0_OPR|UDCCS0_SA))) { + unsigned i; + + /* + * pxa210/250 erratum 131 for B0/B1 says RNE lies. + * still observed on a pxa255 a0. + */ + debug("e131\n"); + nuke(ep, -EPROTO); + + /* read SETUP data, but don't trust it too much */ + for (i = 0; i < 8; i++) + u.raw[i] = (u8)readb(&dev->regs->uddr0); + if ((u.r.bRequestType & USB_RECIP_MASK) + > USB_RECIP_OTHER) + goto stall; + if (u.word[0] == 0 && u.word[1] == 0) + goto stall; + goto got_setup; + } else { + /* + * some random early IRQ: + * - we acked FST + * - IPR cleared + * - OPR got set, without SA (likely status stage) + */ + debug("random IRQ %X %X\n", udccs0, + readl(&dev->regs->udccs[0])); + writel(udccs0 & (UDCCS0_SA|UDCCS0_OPR), + &dev->regs->udccs[0]); + } + break; + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ + if (udccs0 & UDCCS0_OPR) { + debug("ep0in premature status\n"); + if (req) + done(ep, req, 0); + ep0_idle(dev); + } else /* irq was IPR clearing */ { + if (req) { + debug("next ep0 in packet\n"); + /* this IN packet might finish the request */ + (void) write_ep0_fifo(ep, req); + } /* else IN token before response was written */ + } + break; + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ + if (udccs0 & UDCCS0_OPR) { + if (req) { + /* this OUT packet might finish the request */ + if (read_ep0_fifo(ep, req)) + done(ep, req, 0); + /* else more OUT packets expected */ + } /* else OUT token before read was issued */ + } else /* irq was IPR clearing */ { + debug("ep0out premature status\n"); + if (req) + done(ep, req, 0); + ep0_idle(dev); + } + break; + case EP0_END_XFER: + if (req) + done(ep, req, 0); + /* + * ack control-IN status (maybe in-zlp was skipped) + * also appears after some config change events. + */ + if (udccs0 & UDCCS0_OPR) + writel(UDCCS0_OPR, &dev->regs->udccs[0]); + ep0_idle(dev); + break; + case EP0_STALL: + writel(UDCCS0_FST, &dev->regs->udccs[0]); + break; + } + + writel(USIR0_IR0, &dev->regs->usir0); +} + +static void handle_ep(struct pxa25x_ep *ep) +{ + struct pxa25x_request *req; + int is_in = ep->bEndpointAddress & USB_DIR_IN; + int completed; + u32 udccs, tmp; + + do { + completed = 0; + if (likely(!list_empty(&ep->queue))) + req = list_entry(ep->queue.next, + struct pxa25x_request, queue); + else + req = NULL; + + /* TODO check FST handling */ + + udccs = readl(ep->reg_udccs); + if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */ + tmp = UDCCS_BI_TUR; + if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) + tmp |= UDCCS_BI_SST; + tmp &= udccs; + if (likely(tmp)) + writel(tmp, ep->reg_udccs); + if (req && likely((udccs & UDCCS_BI_TFS) != 0)) + completed = write_fifo(ep, req); + + } else { /* irq from RPC (or for ISO, ROF) */ + if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) + tmp = UDCCS_BO_SST | UDCCS_BO_DME; + else + tmp = UDCCS_IO_ROF | UDCCS_IO_DME; + tmp &= udccs; + if (likely(tmp)) + writel(tmp, ep->reg_udccs); + + /* fifos can hold packets, ready for reading... */ + if (likely(req)) + completed = read_fifo(ep, req); + else + pio_irq_disable(ep->bEndpointAddress); + } + ep->pio_irqs++; + } while (completed); +} + +/* + * pxa25x_udc_irq - interrupt handler + * + * avoid delays in ep0 processing. the control handshaking isn't always + * under software control (pxa250c0 and the pxa255 are better), and delays + * could cause usb protocol errors. + */ +static struct pxa25x_udc memory; +static int +pxa25x_udc_irq(void) +{ + struct pxa25x_udc *dev = &memory; + int handled; + + test_watchdog(dev); + + dev->stats.irqs++; + do { + u32 udccr = readl(&dev->regs->udccr); + + handled = 0; + + /* SUSpend Interrupt Request */ + if (unlikely(udccr & UDCCR_SUSIR)) { + udc_ack_int_UDCCR(UDCCR_SUSIR); + handled = 1; + debug("USB suspend\n"); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + ep0_idle(dev); + } + + /* RESume Interrupt Request */ + if (unlikely(udccr & UDCCR_RESIR)) { + udc_ack_int_UDCCR(UDCCR_RESIR); + handled = 1; + debug("USB resume\n"); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume) + dev->driver->resume(&dev->gadget); + } + + /* ReSeT Interrupt Request - USB reset */ + if (unlikely(udccr & UDCCR_RSTIR)) { + udc_ack_int_UDCCR(UDCCR_RSTIR); + handled = 1; + + if ((readl(&dev->regs->udccr) & UDCCR_UDA) == 0) { + debug("USB reset start\n"); + + /* + * reset driver and endpoints, + * in case that's not yet done + */ + stop_activity(dev, dev->driver); + + } else { + debug("USB reset end\n"); + dev->gadget.speed = USB_SPEED_FULL; + memset(&dev->stats, 0, sizeof dev->stats); + /* driver and endpoints are still reset */ + } + + } else { + u32 uicr0 = readl(&dev->regs->uicr0); + u32 uicr1 = readl(&dev->regs->uicr1); + u32 usir0 = readl(&dev->regs->usir0); + u32 usir1 = readl(&dev->regs->usir1); + + usir0 = usir0 & ~uicr0; + usir1 = usir1 & ~uicr1; + int i; + + if (unlikely(!usir0 && !usir1)) + continue; + + debug_cond(NOISY, "irq %02x.%02x\n", usir1, usir0); + + /* control traffic */ + if (usir0 & USIR0_IR0) { + dev->ep[0].pio_irqs++; + handle_ep0(dev); + handled = 1; + } + + /* endpoint data transfers */ + for (i = 0; i < 8; i++) { + u32 tmp = 1 << i; + + if (i && (usir0 & tmp)) { + handle_ep(&dev->ep[i]); + setbits_le32(&dev->regs->usir0, tmp); + handled = 1; + } +#ifndef CONFIG_USB_PXA25X_SMALL + if (usir1 & tmp) { + handle_ep(&dev->ep[i+8]); + setbits_le32(&dev->regs->usir1, tmp); + handled = 1; + } +#endif + } + } + + /* we could also ask for 1 msec SOF (SIR) interrupts */ + + } while (handled); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +/* + * this uses load-time allocation and initialization (instead of + * doing it at run-time) to save code, eliminate fault paths, and + * be more obviously correct. + */ +static struct pxa25x_udc memory = { + .regs = UDC_REGS, + + .gadget = { + .ops = &pxa25x_udc_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &pxa25x_ep_ops, + .maxpacket = EP0_FIFO_SIZE, + }, + .dev = &memory, + .reg_udccs = &UDC_REGS->udccs[0], + .reg_uddr = &UDC_REGS->uddr0, + }, + + /* first group of endpoints */ + .ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDC_REGS->udccs[1], + .reg_uddr = &UDC_REGS->uddr1, + }, + .ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDC_REGS->udccs[2], + .reg_ubcr = &UDC_REGS->ubcr2, + .reg_uddr = &UDC_REGS->uddr2, + }, +#ifndef CONFIG_USB_PXA25X_SMALL + .ep[3] = { + .ep = { + .name = "ep3in-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 3, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDC_REGS->udccs[3], + .reg_uddr = &UDC_REGS->uddr3, + }, + .ep[4] = { + .ep = { + .name = "ep4out-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = 4, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDC_REGS->udccs[4], + .reg_ubcr = &UDC_REGS->ubcr4, + .reg_uddr = &UDC_REGS->uddr4, + }, + .ep[5] = { + .ep = { + .name = "ep5in-int", + .ops = &pxa25x_ep_ops, + .maxpacket = INT_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = INT_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 5, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .reg_udccs = &UDC_REGS->udccs[5], + .reg_uddr = &UDC_REGS->uddr5, + }, + + /* second group of endpoints */ + .ep[6] = { + .ep = { + .name = "ep6in-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 6, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDC_REGS->udccs[6], + .reg_uddr = &UDC_REGS->uddr6, + }, + .ep[7] = { + .ep = { + .name = "ep7out-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = 7, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDC_REGS->udccs[7], + .reg_ubcr = &UDC_REGS->ubcr7, + .reg_uddr = &UDC_REGS->uddr7, + }, + .ep[8] = { + .ep = { + .name = "ep8in-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 8, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDC_REGS->udccs[8], + .reg_uddr = &UDC_REGS->uddr8, + }, + .ep[9] = { + .ep = { + .name = "ep9out-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = 9, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDC_REGS->udccs[9], + .reg_ubcr = &UDC_REGS->ubcr9, + .reg_uddr = &UDC_REGS->uddr9, + }, + .ep[10] = { + .ep = { + .name = "ep10in-int", + .ops = &pxa25x_ep_ops, + .maxpacket = INT_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = INT_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 10, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .reg_udccs = &UDC_REGS->udccs[10], + .reg_uddr = &UDC_REGS->uddr10, + }, + + /* third group of endpoints */ + .ep[11] = { + .ep = { + .name = "ep11in-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 11, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDC_REGS->udccs[11], + .reg_uddr = &UDC_REGS->uddr11, + }, + .ep[12] = { + .ep = { + .name = "ep12out-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = 12, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDC_REGS->udccs[12], + .reg_ubcr = &UDC_REGS->ubcr12, + .reg_uddr = &UDC_REGS->uddr12, + }, + .ep[13] = { + .ep = { + .name = "ep13in-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 13, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDC_REGS->udccs[13], + .reg_uddr = &UDC_REGS->uddr13, + }, + .ep[14] = { + .ep = { + .name = "ep14out-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = 14, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDC_REGS->udccs[14], + .reg_ubcr = &UDC_REGS->ubcr14, + .reg_uddr = &UDC_REGS->uddr14, + }, + .ep[15] = { + .ep = { + .name = "ep15in-int", + .ops = &pxa25x_ep_ops, + .maxpacket = INT_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = INT_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 15, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .reg_udccs = &UDC_REGS->udccs[15], + .reg_uddr = &UDC_REGS->uddr15, + }, +#endif /* !CONFIG_USB_PXA25X_SMALL */ +}; + +static void udc_command(int cmd) +{ + switch (cmd) { + case PXA2XX_UDC_CMD_CONNECT: + setbits_le32(GPDR(CONFIG_USB_DEV_PULLUP_GPIO), + GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO)); + + /* enable pullup */ + writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), + GPCR(CONFIG_USB_DEV_PULLUP_GPIO)); + + debug("Connected to USB\n"); + break; + + case PXA2XX_UDC_CMD_DISCONNECT: + /* disable pullup resistor */ + writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), + GPSR(CONFIG_USB_DEV_PULLUP_GPIO)); + + /* setup pin as input, line will float */ + clrbits_le32(GPDR(CONFIG_USB_DEV_PULLUP_GPIO), + GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO)); + + debug("Disconnected from USB\n"); + break; + } +} + +static struct pxa2xx_udc_mach_info mach_info = { + .udc_command = udc_command, +}; + +/* + * when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct pxa25x_udc *dev = &memory; + int retval; + uint32_t chiprev; + + if (!driver + || driver->speed < USB_SPEED_FULL + || !driver->disconnect + || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + /* Enable clock for usb controller */ + setbits_le32(CKEN, CKEN11_USB); + + /* first hook up the driver ... */ + dev->driver = driver; + dev->pullup = 1; + + /* trigger chiprev-specific logic */ + switch ((chiprev = pxa_get_cpu_revision())) { + case PXA255_A0: + dev->has_cfr = 1; + break; + case PXA250_A0: + case PXA250_A1: + /* A0/A1 "not released"; ep 13, 15 unusable */ + /* fall through */ + case PXA250_B2: case PXA210_B2: + case PXA250_B1: case PXA210_B1: + case PXA250_B0: case PXA210_B0: + /* OUT-DMA is broken ... */ + /* fall through */ + case PXA250_C0: case PXA210_C0: + break; + default: + printf("%s: unrecognized processor: %08x\n", + DRIVER_NAME, chiprev); + return -ENODEV; + } + + the_controller = dev; + + /* prepare watchdog timer */ + dev->watchdog.running = 0; + dev->watchdog.period = 5000 * CONFIG_SYS_HZ / 1000000; /* 5 ms */ + dev->watchdog.function = udc_watchdog; + + udc_disable(dev); + udc_reinit(dev); + + dev->mach = &mach_info; + + dev->gadget.name = "pxa2xx_udc"; + retval = driver->bind(&dev->gadget); + if (retval) { + printf("bind to driver %s --> error %d\n", + DRIVER_NAME, retval); + dev->driver = NULL; + return retval; + } + + /* + * ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + printf("registered gadget driver '%s'\n", DRIVER_NAME); + + pullup(dev); + dump_state(dev); + return 0; +} + +static void +stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { + struct pxa25x_ep *ep = &dev->ep[i]; + + ep->stopped = 1; + nuke(ep, -ESHUTDOWN); + } + stop_watchdog(dev); + + /* report disconnect; the driver is already quiesced */ + if (driver) + driver->disconnect(&dev->gadget); + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct pxa25x_udc *dev = the_controller; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver || !driver->unbind) + return -EINVAL; + + local_irq_disable(); + dev->pullup = 0; + pullup(dev); + stop_activity(dev, driver); + local_irq_enable(); + + driver->unbind(&dev->gadget); + dev->driver = NULL; + + printf("unregistered gadget driver '%s'\n", DRIVER_NAME); + dump_state(dev); + + the_controller = NULL; + + clrbits_le32(CKEN, CKEN11_USB); + + return 0; +} + +extern void udc_disconnect(void) +{ + setbits_le32(CKEN, CKEN11_USB); + udc_clear_mask_UDCCR(UDCCR_UDE); + udc_command(PXA2XX_UDC_CMD_DISCONNECT); + clrbits_le32(CKEN, CKEN11_USB); +} + +/*-------------------------------------------------------------------------*/ + +extern int +usb_gadget_handle_interrupts(void) +{ + return pxa25x_udc_irq(); +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h b/qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h new file mode 100644 index 000000000..f543b2d58 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/pxa25x_udc.h @@ -0,0 +1,150 @@ +/* + * Intel PXA25x on-chip full speed USB device controller + * + * Copyright (C) 2003 Robert Schwebel , Pengutronix + * Copyright (C) 2003 David Brownell + * Copyright (C) 2012 Lukasz Dalek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LINUX_USB_GADGET_PXA25X_H +#define __LINUX_USB_GADGET_PXA25X_H + +#include +#include + +/* + * Prefetching support - only ARMv5. + */ + +#ifdef ARCH_HAS_PREFETCH +static inline void prefetch(const void *ptr) +{ + __asm__ __volatile__( + "pld\t%a0" + : + : "p" (ptr) + : "cc"); +} + +#define prefetchw(ptr) prefetch(ptr) +#endif /* ARCH_HAS_PREFETCH */ + +/*-------------------------------------------------------------------------*/ + +#define UDC_REGS ((struct pxa25x_udc_regs *)PXA25X_UDC_BASE) + +/*-------------------------------------------------------------------------*/ + +struct pxa2xx_udc_mach_info { + int (*udc_is_connected)(void); /* do we see host? */ + void (*udc_command)(int cmd); +#define PXA2XX_UDC_CMD_CONNECT 0 /* let host see us */ +#define PXA2XX_UDC_CMD_DISCONNECT 1 /* so host won't see us */ +}; + +struct pxa25x_udc; + +struct pxa25x_ep { + struct usb_ep ep; + struct pxa25x_udc *dev; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + unsigned long pio_irqs; + + unsigned short fifo_size; + u8 bEndpointAddress; + u8 bmAttributes; + + unsigned stopped:1; + + /* UDCCS = UDC Control/Status for this EP + * UBCR = UDC Byte Count Remaining (contents of OUT fifo) + * UDDR = UDC Endpoint Data Register (the fifo) + * DRCM = DMA Request Channel Map + */ + u32 *reg_udccs; + u32 *reg_ubcr; + u32 *reg_uddr; +}; + +struct pxa25x_request { + struct usb_request req; + struct list_head queue; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_END_XFER, + EP0_STALL, +}; + +#define EP0_FIFO_SIZE 16U +#define BULK_FIFO_SIZE 64U +#define ISO_FIFO_SIZE 256U +#define INT_FIFO_SIZE 8U + +struct udc_stats { + struct ep0stats { + unsigned long ops; + unsigned long bytes; + } read, write; + unsigned long irqs; +}; + +#ifdef CONFIG_USB_PXA25X_SMALL +/* when memory's tight, SMALL config saves code+data. */ +#define PXA_UDC_NUM_ENDPOINTS 3 +#endif + +#ifndef PXA_UDC_NUM_ENDPOINTS +#define PXA_UDC_NUM_ENDPOINTS 16 +#endif + +struct pxa25x_watchdog { + unsigned running:1; + ulong period; + ulong base; + struct pxa25x_udc *udc; + + void (*function)(struct pxa25x_udc *udc); +}; + +struct pxa25x_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct pxa25x_udc_regs *regs; + + enum ep0_state ep0state; + struct udc_stats stats; + unsigned got_irq:1, + pullup:1, + has_cfr:1, + req_pending:1, + req_std:1, + req_config:1, + active:1; + + struct clk *clk; + struct pxa2xx_udc_mach_info *mach; + u64 dma_mask; + struct pxa25x_ep ep[PXA_UDC_NUM_ENDPOINTS]; + + struct pxa25x_watchdog watchdog; +}; + +/*-------------------------------------------------------------------------*/ + +static struct pxa25x_udc *the_controller; + +/*-------------------------------------------------------------------------*/ + +#ifndef DEBUG +# define NOISY 0 +#endif + +#endif /* __LINUX_USB_GADGET_PXA25X_H */ diff --git a/qemu/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c b/qemu/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c new file mode 100644 index 000000000..733558def --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/pxa27x_udc.c @@ -0,0 +1,703 @@ +/* + * PXA27x USB device driver for u-boot. + * + * Copyright (C) 2007 Rodolfo Giometti + * Copyright (C) 2007 Eurotech S.p.A. + * Copyright (C) 2008 Vivek Kutal + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ep0.h" + +/* number of endpoints on this UDC */ +#define UDC_MAX_ENDPOINTS 24 + +static struct urb *ep0_urb; +static struct usb_device_instance *udc_device; +static int ep0state = EP0_IDLE; + +#ifdef USBDDBG +static void udc_dump_buffer(char *name, u8 *buf, int len) +{ + usbdbg("%s - buf %p, len %d", name, buf, len); + print_buffer(0, buf, 1, len, 0); +} +#else +#define udc_dump_buffer(name, buf, len) /* void */ +#endif + +static inline void udc_ack_int_UDCCR(int mask) +{ + writel(readl(USIR1) | mask, USIR1); +} + +/* + * If the endpoint has an active tx_urb, then the next packet of data from the + * URB is written to the tx FIFO. + * The total amount of data in the urb is given by urb->actual_length. + * The maximum amount of data that can be sent in any one packet is given by + * endpoint->tx_packetSize. + * The number of data bytes from this URB that have already been transmitted + * is given by endpoint->sent. + * endpoint->last is updated by this routine with the number of data bytes + * transmitted in this packet. + */ +static int udc_write_urb(struct usb_endpoint_instance *endpoint) +{ + struct urb *urb = endpoint->tx_urb; + int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; + u32 *data32 = (u32 *) urb->buffer; + u8 *data8 = (u8 *) urb->buffer; + unsigned int i, n, w, b, is_short; + int timeout = 2000; /* 2ms */ + + if (!urb || !urb->actual_length) + return -1; + + n = MIN(urb->actual_length - endpoint->sent, endpoint->tx_packetSize); + if (n <= 0) + return -1; + + usbdbg("write urb on ep %d", ep_num); +#if defined(USBDDBG) && defined(USBDPARANOIA) + usbdbg("urb: buf %p, buf_len %d, actual_len %d", + urb->buffer, urb->buffer_length, urb->actual_length); + usbdbg("endpoint: sent %d, tx_packetSize %d, last %d", + endpoint->sent, endpoint->tx_packetSize, endpoint->last); +#endif + + is_short = n != endpoint->tx_packetSize; + w = n / 4; + b = n % 4; + usbdbg("n %d%s w %d b %d", n, is_short ? "-s" : "", w, b); + udc_dump_buffer("urb write", data8 + endpoint->sent, n); + + /* Prepare for data send */ + if (ep_num) + writel(UDCCSR_PC ,UDCCSN(ep_num)); + + for (i = 0; i < w; i++) + writel(data32[endpoint->sent / 4 + i], UDCDN(ep_num)); + + for (i = 0; i < b; i++) + writeb(data8[endpoint->sent + w * 4 + i], UDCDN(ep_num)); + + /* Set "Packet Complete" if less data then tx_packetSize */ + if (is_short) + writel(ep_num ? UDCCSR_SP : UDCCSR0_IPR, UDCCSN(ep_num)); + + /* Wait for data sent */ + if (ep_num) { + while (!(readl(UDCCSN(ep_num)) & UDCCSR_PC)) { + if (timeout-- == 0) + return -1; + else + udelay(1); + } + } + + endpoint->last = n; + + if (ep_num) { + usbd_tx_complete(endpoint); + } else { + endpoint->sent += n; + endpoint->last -= n; + } + + if (endpoint->sent >= urb->actual_length) { + urb->actual_length = 0; + endpoint->sent = 0; + endpoint->last = 0; + } + + if ((endpoint->sent >= urb->actual_length) && (!ep_num)) { + usbdbg("ep0 IN stage done"); + if (is_short) + ep0state = EP0_IDLE; + else + ep0state = EP0_XFER_COMPLETE; + } + + return 0; +} + +static int udc_read_urb(struct usb_endpoint_instance *endpoint) +{ + struct urb *urb = endpoint->rcv_urb; + int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; + u32 *data32 = (u32 *) urb->buffer; + unsigned int i, n; + + usbdbg("read urb on ep %d", ep_num); +#if defined(USBDDBG) && defined(USBDPARANOIA) + usbdbg("urb: buf %p, buf_len %d, actual_len %d", + urb->buffer, urb->buffer_length, urb->actual_length); + usbdbg("endpoint: rcv_packetSize %d", + endpoint->rcv_packetSize); +#endif + + if (readl(UDCCSN(ep_num)) & UDCCSR_BNE) + n = readl(UDCBCN(ep_num)) & 0x3ff; + else /* zlp */ + n = 0; + + usbdbg("n %d%s", n, n != endpoint->rcv_packetSize ? "-s" : ""); + for (i = 0; i < n; i += 4) + data32[urb->actual_length / 4 + i / 4] = readl(UDCDN(ep_num)); + + udc_dump_buffer("urb read", (u8 *) data32, urb->actual_length + n); + usbd_rcv_complete(endpoint, n, 0); + + return 0; +} + +static int udc_read_urb_ep0(void) +{ + u32 *data32 = (u32 *) ep0_urb->buffer; + u8 *data8 = (u8 *) ep0_urb->buffer; + unsigned int i, n, w, b; + + usbdbg("read urb on ep 0"); +#if defined(USBDDBG) && defined(USBDPARANOIA) + usbdbg("urb: buf %p, buf_len %d, actual_len %d", + ep0_urb->buffer, ep0_urb->buffer_length, ep0_urb->actual_length); +#endif + + n = readl(UDCBCR0); + w = n / 4; + b = n % 4; + + for (i = 0; i < w; i++) { + data32[ep0_urb->actual_length / 4 + i] = readl(UDCDN(0)); + /* ep0_urb->actual_length += 4; */ + } + + for (i = 0; i < b; i++) { + data8[ep0_urb->actual_length + w * 4 + i] = readb(UDCDN(0)); + /* ep0_urb->actual_length++; */ + } + + ep0_urb->actual_length += n; + + udc_dump_buffer("urb read", (u8 *) data32, ep0_urb->actual_length); + + writel(UDCCSR0_OPC | UDCCSR0_IPR, UDCCSR0); + if (ep0_urb->actual_length == ep0_urb->device_request.wLength) + return 1; + + return 0; +} + +static void udc_handle_ep0(struct usb_endpoint_instance *endpoint) +{ + u32 udccsr0 = readl(UDCCSR0); + u32 *data = (u32 *) &ep0_urb->device_request; + int i; + + usbdbg("udccsr0 %x", udccsr0); + + /* Clear stall status */ + if (udccsr0 & UDCCSR0_SST) { + usberr("clear stall status"); + writel(UDCCSR0_SST, UDCCSR0); + ep0state = EP0_IDLE; + } + + /* previous request unfinished? non-error iff back-to-back ... */ + if ((udccsr0 & UDCCSR0_SA) != 0 && ep0state != EP0_IDLE) + ep0state = EP0_IDLE; + + switch (ep0state) { + + case EP0_IDLE: + udccsr0 = readl(UDCCSR0); + /* Start control request? */ + if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)) + == (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)) { + + /* Read SETUP packet. + * SETUP packet size is 8 bytes (aka 2 words) + */ + usbdbg("try reading SETUP packet"); + for (i = 0; i < 2; i++) { + if ((readl(UDCCSR0) & UDCCSR0_RNE) == 0) { + usberr("setup packet too short:%d", i); + goto stall; + } + data[i] = readl(UDCDR0); + } + + writel(readl(UDCCSR0) | UDCCSR0_OPC | UDCCSR0_SA, UDCCSR0); + if ((readl(UDCCSR0) & UDCCSR0_RNE) != 0) { + usberr("setup packet too long"); + goto stall; + } + + udc_dump_buffer("ep0 setup read", (u8 *) data, 8); + + if (ep0_urb->device_request.wLength == 0) { + usbdbg("Zero Data control Packet\n"); + if (ep0_recv_setup(ep0_urb)) { + usberr("Invalid Setup Packet\n"); + udc_dump_buffer("ep0 setup read", + (u8 *)data, 8); + goto stall; + } + writel(UDCCSR0_IPR, UDCCSR0); + ep0state = EP0_IDLE; + } else { + /* Check direction */ + if ((ep0_urb->device_request.bmRequestType & + USB_REQ_DIRECTION_MASK) + == USB_REQ_HOST2DEVICE) { + ep0state = EP0_OUT_DATA; + ep0_urb->buffer = + (u8 *)ep0_urb->buffer_data; + ep0_urb->buffer_length = + sizeof(ep0_urb->buffer_data); + ep0_urb->actual_length = 0; + writel(UDCCSR0_IPR, UDCCSR0); + } else { + /* The ep0_recv_setup function has + * already placed our response packet + * data in ep0_urb->buffer and the + * packet length in + * ep0_urb->actual_length. + */ + if (ep0_recv_setup(ep0_urb)) { +stall: + usberr("Invalid setup packet"); + udc_dump_buffer("ep0 setup read" + , (u8 *) data, 8); + ep0state = EP0_IDLE; + + writel(UDCCSR0_SA | + UDCCSR0_OPC | UDCCSR0_FST | + UDCCS0_FTF, UDCCSR0); + + return; + } + + endpoint->tx_urb = ep0_urb; + endpoint->sent = 0; + usbdbg("EP0_IN_DATA"); + ep0state = EP0_IN_DATA; + if (udc_write_urb(endpoint) < 0) + goto stall; + + } + } + return; + } else if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA)) + == (UDCCSR0_OPC|UDCCSR0_SA)) { + usberr("Setup Active but no data. Stalling ....\n"); + goto stall; + } else { + usbdbg("random early IRQs"); + /* Some random early IRQs: + * - we acked FST + * - IPR cleared + * - OPC got set, without SA (likely status stage) + */ + writel(udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC), UDCCSR0); + } + break; + + case EP0_OUT_DATA: + + if ((udccsr0 & UDCCSR0_OPC) && !(udccsr0 & UDCCSR0_SA)) { + if (udc_read_urb_ep0()) { +read_complete: + ep0state = EP0_IDLE; + if (ep0_recv_setup(ep0_urb)) { + /* Not a setup packet, stall next + * EP0 transaction + */ + udc_dump_buffer("ep0 setup read", + (u8 *) data, 8); + usberr("can't parse setup packet\n"); + goto stall; + } + } + } else if (!(udccsr0 & UDCCSR0_OPC) && + !(udccsr0 & UDCCSR0_IPR)) { + if (ep0_urb->device_request.wLength == + ep0_urb->actual_length) + goto read_complete; + + usberr("Premature Status\n"); + ep0state = EP0_IDLE; + } + break; + + case EP0_IN_DATA: + /* GET_DESCRIPTOR etc */ + if (udccsr0 & UDCCSR0_OPC) { + writel(UDCCSR0_OPC | UDCCSR0_FTF, UDCCSR0); + usberr("ep0in premature status"); + ep0state = EP0_IDLE; + } else { + /* irq was IPR clearing */ + if (udc_write_urb(endpoint) < 0) { + usberr("ep0_write_error\n"); + goto stall; + } + } + break; + + case EP0_XFER_COMPLETE: + writel(UDCCSR0_IPR, UDCCSR0); + ep0state = EP0_IDLE; + break; + + default: + usbdbg("Default\n"); + } + writel(USIR0_IR0, USIR0); +} + +static void udc_handle_ep(struct usb_endpoint_instance *endpoint) +{ + int ep_addr = endpoint->endpoint_address; + int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + int ep_isout = (ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT; + + u32 flags = readl(UDCCSN(ep_num)) & (UDCCSR_SST | UDCCSR_TRN); + if (flags) + writel(flags, UDCCSN(ep_num)); + + if (ep_isout) + udc_read_urb(endpoint); + else + udc_write_urb(endpoint); + + writel(UDCCSR_PC, UDCCSN(ep_num)); +} + +static void udc_state_changed(void) +{ + + writel(readl(UDCCR) | UDCCR_SMAC, UDCCR); + + usbdbg("New UDC settings are: conf %d - inter %d - alter %d", + (readl(UDCCR) & UDCCR_ACN) >> UDCCR_ACN_S, + (readl(UDCCR) & UDCCR_AIN) >> UDCCR_AIN_S, + (readl(UDCCR) & UDCCR_AAISN) >> UDCCR_AAISN_S); + + usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); + writel(UDCISR1_IRCC, UDCISR1); +} + +void udc_irq(void) +{ + int handled; + struct usb_endpoint_instance *endpoint; + int ep_num, i; + u32 udcisr0; + + do { + handled = 0; + /* Suspend Interrupt Request */ + if (readl(USIR1) & UDCCR_SUSIR) { + usbdbg("Suspend\n"); + udc_ack_int_UDCCR(UDCCR_SUSIR); + handled = 1; + ep0state = EP0_IDLE; + } + + /* Resume Interrupt Request */ + if (readl(USIR1) & UDCCR_RESIR) { + udc_ack_int_UDCCR(UDCCR_RESIR); + handled = 1; + usbdbg("USB resume\n"); + } + + if (readl(USIR1) & (1<<31)) { + handled = 1; + udc_state_changed(); + } + + /* Reset Interrupt Request */ + if (readl(USIR1) & UDCCR_RSTIR) { + udc_ack_int_UDCCR(UDCCR_RSTIR); + handled = 1; + usbdbg("Reset\n"); + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + } else { + if (readl(USIR0)) + usbdbg("UISR0: %x \n", readl(USIR0)); + + if (readl(USIR0) & 0x2) + writel(0x2, USIR0); + + /* Control traffic */ + if (readl(USIR0) & USIR0_IR0) { + handled = 1; + writel(USIR0_IR0, USIR0); + udc_handle_ep0(udc_device->bus->endpoint_array); + } + + endpoint = udc_device->bus->endpoint_array; + for (i = 0; i < udc_device->bus->max_endpoints; i++) { + ep_num = (endpoint[i].endpoint_address) & + USB_ENDPOINT_NUMBER_MASK; + if (!ep_num) + continue; + udcisr0 = readl(UDCISR0); + if (udcisr0 & + UDCISR_INT(ep_num, UDC_INT_PACKETCMP)) { + writel(UDCISR_INT(ep_num, UDC_INT_PACKETCMP), + UDCISR0); + udc_handle_ep(&endpoint[i]); + } + } + } + + } while (handled); +} + +/* The UDCCR reg contains mask and interrupt status bits, + * so using '|=' isn't safe as it may ack an interrupt. + */ +#define UDCCR_OEN (1 << 31) /* On-the-Go Enable */ +#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_UDE) + +static inline void udc_set_mask_UDCCR(int mask) +{ + writel((readl(UDCCR) & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS), UDCCR); +} + +static inline void udc_clear_mask_UDCCR(int mask) +{ + writel((readl(UDCCR) & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS), UDCCR); +} + +static void pio_irq_enable(int ep_num) +{ + if (ep_num < 16) + writel(readl(UDCICR0) | 3 << (ep_num * 2), UDCICR0); + else { + ep_num -= 16; + writel(readl(UDCICR1) | 3 << (ep_num * 2), UDCICR1); + } +} + +/* + * udc_set_nak + * + * Allow upper layers to signal lower layers should not accept more RX data + */ +void udc_set_nak(int ep_num) +{ + /* TODO */ +} + +/* + * udc_unset_nak + * + * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint. + * Switch off NAKing on this endpoint to accept more data output from host. + */ +void udc_unset_nak(int ep_num) +{ + /* TODO */ +} + +int udc_endpoint_write(struct usb_endpoint_instance *endpoint) +{ + return udc_write_urb(endpoint); +} + +/* Associate a physical endpoint with endpoint instance */ +void udc_setup_ep(struct usb_device_instance *device, unsigned int id, + struct usb_endpoint_instance *endpoint) +{ + int ep_num, ep_addr, ep_isout, ep_type, ep_size; + int config, interface, alternate; + u32 tmp; + + usbdbg("setting up endpoint id %d", id); + + if (!endpoint) { + usberr("endpoint void!"); + return; + } + + ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; + if (ep_num >= UDC_MAX_ENDPOINTS) { + usberr("unable to setup ep %d!", ep_num); + return; + } + + pio_irq_enable(ep_num); + if (ep_num == 0) { + /* Done for ep0 */ + return; + } + + config = 1; + interface = 0; + alternate = 0; + + usbdbg("config %d - interface %d - alternate %d", + config, interface, alternate); + + ep_addr = endpoint->endpoint_address; + ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + ep_isout = (ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT; + ep_type = ep_isout ? endpoint->rcv_attributes : endpoint->tx_attributes; + ep_size = ep_isout ? endpoint->rcv_packetSize : endpoint->tx_packetSize; + + usbdbg("addr %x, num %d, dir %s, type %s, packet size %d", + ep_addr, ep_num, + ep_isout ? "out" : "in", + ep_type == USB_ENDPOINT_XFER_ISOC ? "isoc" : + ep_type == USB_ENDPOINT_XFER_BULK ? "bulk" : + ep_type == USB_ENDPOINT_XFER_INT ? "int" : "???", + ep_size + ); + + /* Configure UDCCRx */ + tmp = 0; + tmp |= (config << UDCCONR_CN_S) & UDCCONR_CN; + tmp |= (interface << UDCCONR_IN_S) & UDCCONR_IN; + tmp |= (alternate << UDCCONR_AISN_S) & UDCCONR_AISN; + tmp |= (ep_num << UDCCONR_EN_S) & UDCCONR_EN; + tmp |= (ep_type << UDCCONR_ET_S) & UDCCONR_ET; + tmp |= ep_isout ? 0 : UDCCONR_ED; + tmp |= (ep_size << UDCCONR_MPS_S) & UDCCONR_MPS; + tmp |= UDCCONR_EE; + + writel(tmp, UDCCN(ep_num)); + + usbdbg("UDCCR%c = %x", 'A' + ep_num-1, readl(UDCCN(ep_num))); + usbdbg("UDCCSR%c = %x", 'A' + ep_num-1, readl(UDCCSN(ep_num))); +} + +/* Connect the USB device to the bus */ +void udc_connect(void) +{ + usbdbg("UDC connect"); + +#ifdef CONFIG_USB_DEV_PULLUP_GPIO + /* Turn on the USB connection by enabling the pullup resistor */ + writel(readl(GPDR(CONFIG_USB_DEV_PULLUP_GPIO)) + | GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), + GPDR(CONFIG_USB_DEV_PULLUP_GPIO)); + writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPSR(CONFIG_USB_DEV_PULLUP_GPIO)); +#else + /* Host port 2 transceiver D+ pull up enable */ + writel(readl(UP2OCR) | UP2OCR_DPPUE, UP2OCR); +#endif +} + +/* Disconnect the USB device to the bus */ +void udc_disconnect(void) +{ + usbdbg("UDC disconnect"); + +#ifdef CONFIG_USB_DEV_PULLUP_GPIO + /* Turn off the USB connection by disabling the pullup resistor */ + writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPCR(CONFIG_USB_DEV_PULLUP_GPIO)); +#else + /* Host port 2 transceiver D+ pull up disable */ + writel(readl(UP2OCR) & ~UP2OCR_DPPUE, UP2OCR); +#endif +} + +/* Switch on the UDC */ +void udc_enable(struct usb_device_instance *device) +{ + + ep0state = EP0_IDLE; + + /* enable endpoint 0, A, B's Packet Complete Interrupt. */ + writel(0xffffffff, UDCICR0); + writel(0xa8000000, UDCICR1); + + /* clear the interrupt status/control registers */ + writel(0xffffffff, UDCISR0); + writel(0xffffffff, UDCISR1); + + /* set UDC-enable */ + udc_set_mask_UDCCR(UDCCR_UDE); + + udc_device = device; + if (!ep0_urb) + ep0_urb = usbd_alloc_urb(udc_device, + udc_device->bus->endpoint_array); + else + usbinfo("ep0_urb %p already allocated", ep0_urb); + + usbdbg("UDC Enabled\n"); +} + +/* Need to check this again */ +void udc_disable(void) +{ + usbdbg("disable UDC"); + + udc_clear_mask_UDCCR(UDCCR_UDE); + + /* Disable clock for USB device */ + writel(readl(CKEN) & ~CKEN11_USB, CKEN); + + /* Free ep0 URB */ + if (ep0_urb) { + usbd_dealloc_urb(ep0_urb); + ep0_urb = NULL; + } + + /* Reset device pointer */ + udc_device = NULL; +} + +/* Allow udc code to do any additional startup */ +void udc_startup_events(struct usb_device_instance *device) +{ + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT */ + usbd_device_event_irq(device, DEVICE_INIT, 0); + + /* The DEVICE_CREATE event puts the USB device in the state + * STATE_ATTACHED */ + usbd_device_event_irq(device, DEVICE_CREATE, 0); + + /* Some USB controller driver implementations signal + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. + * DEVICE_HUB_CONFIGURED causes a transition to the state + * STATE_POWERED, and DEVICE_RESET causes a transition to + * the state STATE_DEFAULT. + */ + udc_enable(device); +} + +/* Initialize h/w stuff */ +int udc_init(void) +{ + udc_device = NULL; + usbdbg("PXA27x usbd start"); + + /* Enable clock for USB device */ + writel(readl(CKEN) | CKEN11_USB, CKEN); + + /* Disable the UDC */ + udc_clear_mask_UDCCR(UDCCR_UDE); + + /* Disable IRQs: we don't use them */ + writel(0, UDCICR0); + writel(0, UDCICR1); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/regs-otg.h b/qemu/roms/u-boot/drivers/usb/gadget/regs-otg.h new file mode 100644 index 000000000..ac5d11213 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/regs-otg.h @@ -0,0 +1,273 @@ +/* linux/arch/arm/plat-s3c/include/plat/regs-otg.h + * + * Copyright (C) 2004 Herbert Poetzl + * + * Registers remapping: + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_REGS_USB_OTG_HS_H +#define __ASM_ARCH_REGS_USB_OTG_HS_H + +/* USB2.0 OTG Controller register */ +struct s3c_usbotg_phy { + u32 phypwr; + u32 phyclk; + u32 rstcon; +}; + +/* Device Logical IN Endpoint-Specific Registers */ +struct s3c_dev_in_endp { + u32 diepctl; + u8 res1[4]; + u32 diepint; + u8 res2[4]; + u32 dieptsiz; + u32 diepdma; + u8 res3[4]; + u32 diepdmab; +}; + +/* Device Logical OUT Endpoint-Specific Registers */ +struct s3c_dev_out_endp { + u32 doepctl; + u8 res1[4]; + u32 doepint; + u8 res2[4]; + u32 doeptsiz; + u32 doepdma; + u8 res3[4]; + u32 doepdmab; +}; + +struct ep_fifo { + u32 fifo; + u8 res[4092]; +}; + +/* USB2.0 OTG Controller register */ +struct s3c_usbotg_reg { + /* Core Global Registers */ + u32 gotgctl; /* OTG Control & Status */ + u32 gotgint; /* OTG Interrupt */ + u32 gahbcfg; /* Core AHB Configuration */ + u32 gusbcfg; /* Core USB Configuration */ + u32 grstctl; /* Core Reset */ + u32 gintsts; /* Core Interrupt */ + u32 gintmsk; /* Core Interrupt Mask */ + u32 grxstsr; /* Receive Status Debug Read/Status Read */ + u32 grxstsp; /* Receive Status Debug Pop/Status Pop */ + u32 grxfsiz; /* Receive FIFO Size */ + u32 gnptxfsiz; /* Non-Periodic Transmit FIFO Size */ + u8 res1[216]; + u32 dieptxf[15]; /* Device Periodic Transmit FIFO size register */ + u8 res2[1728]; + /* Device Configuration */ + u32 dcfg; /* Device Configuration Register */ + u32 dctl; /* Device Control */ + u32 dsts; /* Device Status */ + u8 res3[4]; + u32 diepmsk; /* Device IN Endpoint Common Interrupt Mask */ + u32 doepmsk; /* Device OUT Endpoint Common Interrupt Mask */ + u32 daint; /* Device All Endpoints Interrupt */ + u32 daintmsk; /* Device All Endpoints Interrupt Mask */ + u8 res4[224]; + struct s3c_dev_in_endp in_endp[16]; + struct s3c_dev_out_endp out_endp[16]; + u8 res5[768]; + struct ep_fifo ep[16]; +}; + +/*===================================================================== */ +/*definitions related to CSR setting */ + +/* S3C_UDC_OTG_GOTGCTL */ +#define B_SESSION_VALID (0x1<<19) +#define A_SESSION_VALID (0x1<<18) + +/* S3C_UDC_OTG_GAHBCFG */ +#define PTXFE_HALF (0<<8) +#define PTXFE_ZERO (1<<8) +#define NPTXFE_HALF (0<<7) +#define NPTXFE_ZERO (1<<7) +#define MODE_SLAVE (0<<5) +#define MODE_DMA (1<<5) +#define BURST_SINGLE (0<<1) +#define BURST_INCR (1<<1) +#define BURST_INCR4 (3<<1) +#define BURST_INCR8 (5<<1) +#define BURST_INCR16 (7<<1) +#define GBL_INT_UNMASK (1<<0) +#define GBL_INT_MASK (0<<0) + +/* S3C_UDC_OTG_GRSTCTL */ +#define AHB_MASTER_IDLE (1u<<31) +#define CORE_SOFT_RESET (0x1<<0) + +/* S3C_UDC_OTG_GINTSTS/S3C_UDC_OTG_GINTMSK core interrupt register */ +#define INT_RESUME (1u<<31) +#define INT_DISCONN (0x1<<29) +#define INT_CONN_ID_STS_CNG (0x1<<28) +#define INT_OUT_EP (0x1<<19) +#define INT_IN_EP (0x1<<18) +#define INT_ENUMDONE (0x1<<13) +#define INT_RESET (0x1<<12) +#define INT_SUSPEND (0x1<<11) +#define INT_EARLY_SUSPEND (0x1<<10) +#define INT_NP_TX_FIFO_EMPTY (0x1<<5) +#define INT_RX_FIFO_NOT_EMPTY (0x1<<4) +#define INT_SOF (0x1<<3) +#define INT_DEV_MODE (0x0<<0) +#define INT_HOST_MODE (0x1<<1) +#define INT_GOUTNakEff (0x01<<7) +#define INT_GINNakEff (0x01<<6) + +#define FULL_SPEED_CONTROL_PKT_SIZE 8 +#define FULL_SPEED_BULK_PKT_SIZE 64 + +#define HIGH_SPEED_CONTROL_PKT_SIZE 64 +#define HIGH_SPEED_BULK_PKT_SIZE 512 + +#define RX_FIFO_SIZE (1024*4) +#define NPTX_FIFO_SIZE (1024*4) +#define PTX_FIFO_SIZE (1536*1) + +#define DEPCTL_TXFNUM_0 (0x0<<22) +#define DEPCTL_TXFNUM_1 (0x1<<22) +#define DEPCTL_TXFNUM_2 (0x2<<22) +#define DEPCTL_TXFNUM_3 (0x3<<22) +#define DEPCTL_TXFNUM_4 (0x4<<22) + +/* Enumeration speed */ +#define USB_HIGH_30_60MHZ (0x0<<1) +#define USB_FULL_30_60MHZ (0x1<<1) +#define USB_LOW_6MHZ (0x2<<1) +#define USB_FULL_48MHZ (0x3<<1) + +/* S3C_UDC_OTG_GRXSTSP STATUS */ +#define OUT_PKT_RECEIVED (0x2<<17) +#define OUT_TRANSFER_COMPLELTED (0x3<<17) +#define SETUP_TRANSACTION_COMPLETED (0x4<<17) +#define SETUP_PKT_RECEIVED (0x6<<17) +#define GLOBAL_OUT_NAK (0x1<<17) + +/* S3C_UDC_OTG_DCTL device control register */ +#define NORMAL_OPERATION (0x1<<0) +#define SOFT_DISCONNECT (0x1<<1) + +/* S3C_UDC_OTG_DAINT device all endpoint interrupt register */ +#define DAINT_OUT_BIT (16) +#define DAINT_MASK (0xFFFF) + +/* S3C_UDC_OTG_DIEPCTL0/DOEPCTL0 device + control IN/OUT endpoint 0 control register */ +#define DEPCTL_EPENA (0x1<<31) +#define DEPCTL_EPDIS (0x1<<30) +#define DEPCTL_SETD1PID (0x1<<29) +#define DEPCTL_SETD0PID (0x1<<28) +#define DEPCTL_SNAK (0x1<<27) +#define DEPCTL_CNAK (0x1<<26) +#define DEPCTL_STALL (0x1<<21) +#define DEPCTL_TYPE_BIT (18) +#define DEPCTL_TYPE_MASK (0x3<<18) +#define DEPCTL_CTRL_TYPE (0x0<<18) +#define DEPCTL_ISO_TYPE (0x1<<18) +#define DEPCTL_BULK_TYPE (0x2<<18) +#define DEPCTL_INTR_TYPE (0x3<<18) +#define DEPCTL_USBACTEP (0x1<<15) +#define DEPCTL_NEXT_EP_BIT (11) +#define DEPCTL_MPS_BIT (0) +#define DEPCTL_MPS_MASK (0x7FF) + +#define DEPCTL0_MPS_64 (0x0<<0) +#define DEPCTL0_MPS_32 (0x1<<0) +#define DEPCTL0_MPS_16 (0x2<<0) +#define DEPCTL0_MPS_8 (0x3<<0) +#define DEPCTL_MPS_BULK_512 (512<<0) +#define DEPCTL_MPS_INT_MPS_16 (16<<0) + +#define DIEPCTL0_NEXT_EP_BIT (11) + + +/* S3C_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint + common interrupt mask register */ +/* S3C_UDC_OTG_DIEPINTn/DOEPINTn device IN/OUT endpoint interrupt register */ +#define BACK2BACK_SETUP_RECEIVED (0x1<<6) +#define INTKNEPMIS (0x1<<5) +#define INTKN_TXFEMP (0x1<<4) +#define NON_ISO_IN_EP_TIMEOUT (0x1<<3) +#define CTRL_OUT_EP_SETUP_PHASE_DONE (0x1<<3) +#define AHB_ERROR (0x1<<2) +#define EPDISBLD (0x1<<1) +#define TRANSFER_DONE (0x1<<0) + +#define USB_PHY_CTRL_EN0 (0x1 << 0) + +/* OPHYPWR */ +#define PHY_0_SLEEP (0x1 << 5) +#define OTG_DISABLE_0 (0x1 << 4) +#define ANALOG_PWRDOWN (0x1 << 3) +#define FORCE_SUSPEND_0 (0x1 << 0) + +/* URSTCON */ +#define HOST_SW_RST (0x1 << 4) +#define PHY_SW_RST1 (0x1 << 3) +#define PHYLNK_SW_RST (0x1 << 2) +#define LINK_SW_RST (0x1 << 1) +#define PHY_SW_RST0 (0x1 << 0) + +/* OPHYCLK */ +#define COMMON_ON_N1 (0x1 << 7) +#define COMMON_ON_N0 (0x1 << 4) +#define ID_PULLUP0 (0x1 << 2) +#define CLK_SEL_24MHZ (0x3 << 0) +#define CLK_SEL_12MHZ (0x2 << 0) +#define CLK_SEL_48MHZ (0x0 << 0) + +#define EXYNOS4X12_ID_PULLUP0 (0x01 << 3) +#define EXYNOS4X12_COMMON_ON_N0 (0x01 << 4) +#define EXYNOS4X12_CLK_SEL_12MHZ (0x02 << 0) +#define EXYNOS4X12_CLK_SEL_24MHZ (0x05 << 0) + +/* Device Configuration Register DCFG */ +#define DEV_SPEED_HIGH_SPEED_20 (0x0 << 0) +#define DEV_SPEED_FULL_SPEED_20 (0x1 << 0) +#define DEV_SPEED_LOW_SPEED_11 (0x2 << 0) +#define DEV_SPEED_FULL_SPEED_11 (0x3 << 0) +#define EP_MISS_CNT(x) (x << 18) +#define DEVICE_ADDRESS(x) (x << 4) + +/* Core Reset Register (GRSTCTL) */ +#define TX_FIFO_FLUSH (0x1 << 5) +#define RX_FIFO_FLUSH (0x1 << 4) +#define TX_FIFO_NUMBER(x) (x << 6) +#define TX_FIFO_FLUSH_ALL TX_FIFO_NUMBER(0x10) + +/* Masks definitions */ +#define GINTMSK_INIT (INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE\ + | INT_RESET | INT_SUSPEND) +#define DOEPMSK_INIT (CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR|TRANSFER_DONE) +#define DIEPMSK_INIT (NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE) +#define GAHBCFG_INIT (PTXFE_HALF | NPTXFE_HALF | MODE_DMA | BURST_INCR4\ + | GBL_INT_UNMASK) + +/* Device Endpoint X Transfer Size Register (DIEPTSIZX) */ +#define DIEPT_SIZ_PKT_CNT(x) (x << 19) +#define DIEPT_SIZ_XFER_SIZE(x) (x << 0) + +/* Device OUT Endpoint X Transfer Size Register (DOEPTSIZX) */ +#define DOEPT_SIZ_PKT_CNT(x) (x << 19) +#define DOEPT_SIZ_XFER_SIZE(x) (x << 0) +#define DOEPT_SIZ_XFER_SIZE_MAX_EP0 (0x7F << 0) +#define DOEPT_SIZ_XFER_SIZE_MAX_EP (0x7FFF << 0) + +/* Device Endpoint-N Control Register (DIEPCTLn/DOEPCTLn) */ +#define DIEPCTL_TX_FIFO_NUM(x) (x << 22) +#define DIEPCTL_TX_FIFO_NUM_MASK (~DIEPCTL_TX_FIFO_NUM(0xF)) + +/* Device ALL Endpoints Interrupt Register (DAINT) */ +#define DAINT_IN_EP_INT(x) (x << 0) +#define DAINT_OUT_EP_INT(x) (x << 16) +#endif diff --git a/qemu/roms/u-boot/drivers/usb/gadget/rndis.c b/qemu/roms/u-boot/drivers/usb/gadget/rndis.c new file mode 100644 index 000000000..404a7b96f --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/rndis.c @@ -0,0 +1,1316 @@ +/* + * RNDIS MSG parser + * + * Authors: Benedikt Spranger, Pengutronix + * Robert Schwebel, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This software was originally developed in conformance with + * Microsoft's Remote NDIS Specification License Agreement. + * + * 03/12/2004 Kai-Uwe Bloem + * Fixed message length bug in init_response + * + * 03/25/2004 Kai-Uwe Bloem + * Fixed rndis_rm_hdr length bug. + * + * Copyright (C) 2004 by David Brownell + * updates to merge with Linux 2.6, better match RNDIS spec + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#undef RNDIS_PM +#undef RNDIS_WAKEUP +#undef VERBOSE + +#include "rndis.h" + +#define ETH_ALEN 6 /* Octets in one ethernet addr */ +#define ETH_HLEN 14 /* Total octets in header. */ +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ +#define ETH_DATA_LEN 1500 /* Max. octets in payload */ +#define ETH_FRAME_LEN PKTSIZE_ALIGN /* Max. octets in frame sans FCS */ +#define ETH_FCS_LEN 4 /* Octets in the FCS */ +#define ENOTSUPP 524 /* Operation is not supported */ + + +/* + * The driver for your USB chip needs to support ep0 OUT to work with + * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional). + * + * Windows hosts need an INF file like Documentation/usb/linux.inf + * and will be happier if you provide the host_addr module parameter. + */ + +#define RNDIS_MAX_CONFIGS 1 + +static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS]; + +/* Driver Version */ +static const __le32 rndis_driver_version = __constant_cpu_to_le32(1); + +/* Function Prototypes */ +static rndis_resp_t *rndis_add_response(int configNr, u32 length); + + +/* supported OIDs */ +static const u32 oid_supported_list[] = { + /* the general stuff */ + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_PHYSICAL_MEDIUM, +#if 0 + OID_GEN_RNDIS_CONFIG_PARAMETER, +#endif + + /* the statistical stuff */ + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, +#ifdef RNDIS_OPTIONAL_STATS + OID_GEN_DIRECTED_BYTES_XMIT, + OID_GEN_DIRECTED_FRAMES_XMIT, + OID_GEN_MULTICAST_BYTES_XMIT, + OID_GEN_MULTICAST_FRAMES_XMIT, + OID_GEN_BROADCAST_BYTES_XMIT, + OID_GEN_BROADCAST_FRAMES_XMIT, + OID_GEN_DIRECTED_BYTES_RCV, + OID_GEN_DIRECTED_FRAMES_RCV, + OID_GEN_MULTICAST_BYTES_RCV, + OID_GEN_MULTICAST_FRAMES_RCV, + OID_GEN_BROADCAST_BYTES_RCV, + OID_GEN_BROADCAST_FRAMES_RCV, + OID_GEN_RCV_CRC_ERROR, + OID_GEN_TRANSMIT_QUEUE_LENGTH, +#endif /* RNDIS_OPTIONAL_STATS */ + + /* mandatory 802.3 */ + /* the general stuff */ + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAC_OPTIONS, + OID_802_3_MAXIMUM_LIST_SIZE, + + /* the statistical stuff */ + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, +#ifdef RNDIS_OPTIONAL_STATS + OID_802_3_XMIT_DEFERRED, + OID_802_3_XMIT_MAX_COLLISIONS, + OID_802_3_RCV_OVERRUN, + OID_802_3_XMIT_UNDERRUN, + OID_802_3_XMIT_HEARTBEAT_FAILURE, + OID_802_3_XMIT_TIMES_CRS_LOST, + OID_802_3_XMIT_LATE_COLLISIONS, +#endif /* RNDIS_OPTIONAL_STATS */ + +#ifdef RNDIS_PM + /* PM and wakeup are mandatory for USB: */ + + /* power management */ + OID_PNP_CAPABILITIES, + OID_PNP_QUERY_POWER, + OID_PNP_SET_POWER, + +#ifdef RNDIS_WAKEUP + /* wake up host */ + OID_PNP_ENABLE_WAKE_UP, + OID_PNP_ADD_WAKE_UP_PATTERN, + OID_PNP_REMOVE_WAKE_UP_PATTERN, +#endif /* RNDIS_WAKEUP */ +#endif /* RNDIS_PM */ +}; + + +/* NDIS Functions */ +static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, + unsigned buf_len, rndis_resp_t *r) +{ + int retval = -ENOTSUPP; + u32 length = 4; /* usually */ + __le32 *outbuf; + int i, count; + rndis_query_cmplt_type *resp; + rndis_params *params; + + if (!r) + return -ENOMEM; + resp = (rndis_query_cmplt_type *) r->buf; + + if (!resp) + return -ENOMEM; + +#if defined(DEBUG) && defined(DEBUG_VERBOSE) + if (buf_len) { + debug("query OID %08x value, len %d:\n", OID, buf_len); + for (i = 0; i < buf_len; i += 16) { + debug("%03d: %08x %08x %08x %08x\n", i, + get_unaligned_le32(&buf[i]), + get_unaligned_le32(&buf[i + 4]), + get_unaligned_le32(&buf[i + 8]), + get_unaligned_le32(&buf[i + 12])); + } + } +#endif + + /* response goes here, right after the header */ + outbuf = (__le32 *) &resp[1]; + resp->InformationBufferOffset = __constant_cpu_to_le32(16); + + params = &rndis_per_dev_params[configNr]; + switch (OID) { + + /* general oids (table 4-1) */ + + /* mandatory */ + case OID_GEN_SUPPORTED_LIST: + debug("%s: OID_GEN_SUPPORTED_LIST\n", __func__); + length = sizeof(oid_supported_list); + count = length / sizeof(u32); + for (i = 0; i < count; i++) + outbuf[i] = cpu_to_le32(oid_supported_list[i]); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_HARDWARE_STATUS: + debug("%s: OID_GEN_HARDWARE_STATUS\n", __func__); + /* + * Bogus question! + * Hardware must be ready to receive high level protocols. + * BTW: + * reddite ergo quae sunt Caesaris Caesari + * et quae sunt Dei Deo! + */ + *outbuf = __constant_cpu_to_le32(0); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_SUPPORTED: + debug("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__); + *outbuf = cpu_to_le32(params->medium); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_IN_USE: + debug("%s: OID_GEN_MEDIA_IN_USE\n", __func__); + /* one medium, one transport... (maybe you do it better) */ + *outbuf = cpu_to_le32(params->medium); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MAXIMUM_FRAME_SIZE: + debug("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__); + if (params->dev) { + *outbuf = cpu_to_le32(params->mtu); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_LINK_SPEED: +#if defined(DEBUG) && defined(DEBUG_VERBOSE) + debug("%s: OID_GEN_LINK_SPEED\n", __func__); +#endif + if (params->media_state == NDIS_MEDIA_STATE_DISCONNECTED) + *outbuf = __constant_cpu_to_le32(0); + else + *outbuf = cpu_to_le32(params->speed); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_TRANSMIT_BLOCK_SIZE: + debug("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__); + if (params->dev) { + *outbuf = cpu_to_le32(params->mtu); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RECEIVE_BLOCK_SIZE: + debug("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__); + if (params->dev) { + *outbuf = cpu_to_le32(params->mtu); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_VENDOR_ID: + debug("%s: OID_GEN_VENDOR_ID\n", __func__); + *outbuf = cpu_to_le32(params->vendorID); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_VENDOR_DESCRIPTION: + debug("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__); + length = strlen(params->vendorDescr); + memcpy(outbuf, params->vendorDescr, length); + retval = 0; + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + debug("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __func__); + /* Created as LE */ + *outbuf = rndis_driver_version; + retval = 0; + break; + + /* mandatory */ + case OID_GEN_CURRENT_PACKET_FILTER: + debug("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __func__); + *outbuf = cpu_to_le32(*params->filter); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MAXIMUM_TOTAL_SIZE: + debug("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__); + *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_CONNECT_STATUS: +#if defined(DEBUG) && defined(DEBUG_VERBOSE) + debug("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __func__); +#endif + *outbuf = cpu_to_le32(params->media_state); + retval = 0; + break; + + case OID_GEN_PHYSICAL_MEDIUM: + debug("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__); + *outbuf = __constant_cpu_to_le32(0); + retval = 0; + break; + + /* + * The RNDIS specification is incomplete/wrong. Some versions + * of MS-Windows expect OIDs that aren't specified there. Other + * versions emit undefined RNDIS messages. DOCUMENT ALL THESE! + */ + case OID_GEN_MAC_OPTIONS: /* from WinME */ + debug("%s: OID_GEN_MAC_OPTIONS\n", __func__); + *outbuf = __constant_cpu_to_le32( + NDIS_MAC_OPTION_RECEIVE_SERIALIZED + | NDIS_MAC_OPTION_FULL_DUPLEX); + retval = 0; + break; + + /* statistics OIDs (table 4-2) */ + + /* mandatory */ + case OID_GEN_XMIT_OK: +#if defined(DEBUG) && defined(DEBUG_VERBOSE) + debug("%s: OID_GEN_XMIT_OK\n", __func__); +#endif + if (params->stats) { + *outbuf = cpu_to_le32( + params->stats->tx_packets - + params->stats->tx_errors - + params->stats->tx_dropped); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RCV_OK: +#if defined(DEBUG) && defined(DEBUG_VERBOSE) + debug("%s: OID_GEN_RCV_OK\n", __func__); +#endif + if (params->stats) { + *outbuf = cpu_to_le32( + params->stats->rx_packets - + params->stats->rx_errors - + params->stats->rx_dropped); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_XMIT_ERROR: +#if defined(DEBUG) && defined(DEBUG_VERBOSE) + debug("%s: OID_GEN_XMIT_ERROR\n", __func__); +#endif + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->tx_errors); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RCV_ERROR: +#if defined(DEBUG) && defined(DEBUG_VERBOSE) + debug("%s: OID_GEN_RCV_ERROR\n", __func__); +#endif + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->rx_errors); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RCV_NO_BUFFER: + debug("%s: OID_GEN_RCV_NO_BUFFER\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->rx_dropped); + retval = 0; + } + break; + +#ifdef RNDIS_OPTIONAL_STATS + case OID_GEN_DIRECTED_BYTES_XMIT: + debug("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__); + /* + * Aunt Tilly's size of shoes + * minus antarctica count of penguins + * divided by weight of Alpha Centauri + */ + if (params->stats) { + *outbuf = cpu_to_le32( + (params->stats->tx_packets - + params->stats->tx_errors - + params->stats->tx_dropped) + * 123); + retval = 0; + } + break; + + case OID_GEN_DIRECTED_FRAMES_XMIT: + debug("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__); + /* dito */ + if (params->stats) { + *outbuf = cpu_to_le32( + (params->stats->tx_packets - + params->stats->tx_errors - + params->stats->tx_dropped) + / 123); + retval = 0; + } + break; + + case OID_GEN_MULTICAST_BYTES_XMIT: + debug("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->multicast * 1234); + retval = 0; + } + break; + + case OID_GEN_MULTICAST_FRAMES_XMIT: + debug("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->multicast); + retval = 0; + } + break; + + case OID_GEN_BROADCAST_BYTES_XMIT: + debug("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->tx_packets/42*255); + retval = 0; + } + break; + + case OID_GEN_BROADCAST_FRAMES_XMIT: + debug("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->tx_packets / 42); + retval = 0; + } + break; + + case OID_GEN_DIRECTED_BYTES_RCV: + debug("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__); + *outbuf = __constant_cpu_to_le32(0); + retval = 0; + break; + + case OID_GEN_DIRECTED_FRAMES_RCV: + debug("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__); + *outbuf = __constant_cpu_to_le32(0); + retval = 0; + break; + + case OID_GEN_MULTICAST_BYTES_RCV: + debug("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->multicast * 1111); + retval = 0; + } + break; + + case OID_GEN_MULTICAST_FRAMES_RCV: + debug("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->multicast); + retval = 0; + } + break; + + case OID_GEN_BROADCAST_BYTES_RCV: + debug("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->rx_packets/42*255); + retval = 0; + } + break; + + case OID_GEN_BROADCAST_FRAMES_RCV: + debug("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->rx_packets / 42); + retval = 0; + } + break; + + case OID_GEN_RCV_CRC_ERROR: + debug("%s: OID_GEN_RCV_CRC_ERROR\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->rx_crc_errors); + retval = 0; + } + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + debug("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__); + *outbuf = __constant_cpu_to_le32(0); + retval = 0; + break; +#endif /* RNDIS_OPTIONAL_STATS */ + + /* ieee802.3 OIDs (table 4-3) */ + + /* mandatory */ + case OID_802_3_PERMANENT_ADDRESS: + debug("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__); + if (params->dev) { + length = ETH_ALEN; + memcpy(outbuf, params->host_mac, length); + retval = 0; + } + break; + + /* mandatory */ + case OID_802_3_CURRENT_ADDRESS: + debug("%s: OID_802_3_CURRENT_ADDRESS\n", __func__); + if (params->dev) { + length = ETH_ALEN; + memcpy(outbuf, params->host_mac, length); + retval = 0; + } + break; + + /* mandatory */ + case OID_802_3_MULTICAST_LIST: + debug("%s: OID_802_3_MULTICAST_LIST\n", __func__); + /* Multicast base address only */ + *outbuf = __constant_cpu_to_le32(0xE0000000); + retval = 0; + break; + + /* mandatory */ + case OID_802_3_MAXIMUM_LIST_SIZE: + debug("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__); + /* Multicast base address only */ + *outbuf = __constant_cpu_to_le32(1); + retval = 0; + break; + + case OID_802_3_MAC_OPTIONS: + debug("%s: OID_802_3_MAC_OPTIONS\n", __func__); + break; + + /* ieee802.3 statistics OIDs (table 4-4) */ + + /* mandatory */ + case OID_802_3_RCV_ERROR_ALIGNMENT: + debug("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__); + if (params->stats) { + *outbuf = cpu_to_le32(params->stats->rx_frame_errors); + retval = 0; + } + break; + + /* mandatory */ + case OID_802_3_XMIT_ONE_COLLISION: + debug("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__); + *outbuf = __constant_cpu_to_le32(0); + retval = 0; + break; + + /* mandatory */ + case OID_802_3_XMIT_MORE_COLLISIONS: + debug("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__); + *outbuf = __constant_cpu_to_le32(0); + retval = 0; + break; + +#ifdef RNDIS_OPTIONAL_STATS + case OID_802_3_XMIT_DEFERRED: + debug("%s: OID_802_3_XMIT_DEFERRED\n", __func__); + /* TODO */ + break; + + case OID_802_3_XMIT_MAX_COLLISIONS: + debug("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__); + /* TODO */ + break; + + case OID_802_3_RCV_OVERRUN: + debug("%s: OID_802_3_RCV_OVERRUN\n", __func__); + /* TODO */ + break; + + case OID_802_3_XMIT_UNDERRUN: + debug("%s: OID_802_3_XMIT_UNDERRUN\n", __func__); + /* TODO */ + break; + + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + debug("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__); + /* TODO */ + break; + + case OID_802_3_XMIT_TIMES_CRS_LOST: + debug("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__); + /* TODO */ + break; + + case OID_802_3_XMIT_LATE_COLLISIONS: + debug("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__); + /* TODO */ + break; +#endif /* RNDIS_OPTIONAL_STATS */ + +#ifdef RNDIS_PM + /* power management OIDs (table 4-5) */ + case OID_PNP_CAPABILITIES: + debug("%s: OID_PNP_CAPABILITIES\n", __func__); + + /* for now, no wakeup capabilities */ + length = sizeof(struct NDIS_PNP_CAPABILITIES); + memset(outbuf, 0, length); + retval = 0; + break; + case OID_PNP_QUERY_POWER: + debug("%s: OID_PNP_QUERY_POWER D%d\n", __func__, + get_unaligned_le32(buf) - 1); + /* + * only suspend is a real power state, and + * it can't be entered by OID_PNP_SET_POWER... + */ + length = 0; + retval = 0; + break; +#endif + + default: + debug("%s: query unknown OID 0x%08X\n", __func__, OID); + } + if (retval < 0) + length = 0; + + resp->InformationBufferLength = cpu_to_le32(length); + r->length = length + sizeof *resp; + resp->MessageLength = cpu_to_le32(r->length); + return retval; +} + +static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, + rndis_resp_t *r) +{ + rndis_set_cmplt_type *resp; + int retval = -ENOTSUPP; + struct rndis_params *params; +#if (defined(DEBUG) && defined(DEBUG_VERBOSE)) || defined(RNDIS_PM) + int i; +#endif + + if (!r) + return -ENOMEM; + resp = (rndis_set_cmplt_type *) r->buf; + if (!resp) + return -ENOMEM; + +#if defined(DEBUG) && defined(DEBUG_VERBOSE) + if (buf_len) { + debug("set OID %08x value, len %d:\n", OID, buf_len); + for (i = 0; i < buf_len; i += 16) { + debug("%03d: %08x %08x %08x %08x\n", i, + get_unaligned_le32(&buf[i]), + get_unaligned_le32(&buf[i + 4]), + get_unaligned_le32(&buf[i + 8]), + get_unaligned_le32(&buf[i + 12])); + } + } +#endif + + params = &rndis_per_dev_params[configNr]; + switch (OID) { + case OID_GEN_CURRENT_PACKET_FILTER: + + /* + * these NDIS_PACKET_TYPE_* bitflags are shared with + * cdc_filter; it's not RNDIS-specific + * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in: + * PROMISCUOUS, DIRECTED, + * MULTICAST, ALL_MULTICAST, BROADCAST + */ + *params->filter = (u16) get_unaligned_le32(buf); + debug("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", + __func__, *params->filter); + + /* + * this call has a significant side effect: it's + * what makes the packet flow start and stop, like + * activating the CDC Ethernet altsetting. + */ +#ifdef RNDIS_PM +update_linkstate: +#endif + retval = 0; + if (*params->filter) + params->state = RNDIS_DATA_INITIALIZED; + else + params->state = RNDIS_INITIALIZED; + break; + + case OID_802_3_MULTICAST_LIST: + /* I think we can ignore this */ + debug("%s: OID_802_3_MULTICAST_LIST\n", __func__); + retval = 0; + break; +#if 0 + case OID_GEN_RNDIS_CONFIG_PARAMETER: + { + struct rndis_config_parameter *param; + param = (struct rndis_config_parameter *) buf; + debug("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", + __func__, + min(cpu_to_le32(param->ParameterNameLength), 80), + buf + param->ParameterNameOffset); + retval = 0; + } + break; +#endif + +#ifdef RNDIS_PM + case OID_PNP_SET_POWER: + /* + * The only real power state is USB suspend, and RNDIS requests + * can't enter it; this one isn't really about power. After + * resuming, Windows forces a reset, and then SET_POWER D0. + * FIXME ... then things go batty; Windows wedges itself. + */ + i = get_unaligned_le32(buf); + debug("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1); + switch (i) { + case NdisDeviceStateD0: + *params->filter = params->saved_filter; + goto update_linkstate; + case NdisDeviceStateD3: + case NdisDeviceStateD2: + case NdisDeviceStateD1: + params->saved_filter = *params->filter; + retval = 0; + break; + } + break; + +#ifdef RNDIS_WAKEUP + /* + * no wakeup support advertised, so wakeup OIDs always fail: + * - OID_PNP_ENABLE_WAKE_UP + * - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN + */ +#endif + +#endif /* RNDIS_PM */ + + default: + debug("%s: set unknown OID 0x%08X, size %d\n", + __func__, OID, buf_len); + } + + return retval; +} + +/* + * Response Functions + */ + +static int rndis_init_response(int configNr, rndis_init_msg_type *buf) +{ + rndis_init_cmplt_type *resp; + rndis_resp_t *r; + + if (!rndis_per_dev_params[configNr].dev) + return -ENOTSUPP; + + r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type)); + if (!r) + return -ENOMEM; + resp = (rndis_init_cmplt_type *) r->buf; + + resp->MessageType = __constant_cpu_to_le32( + REMOTE_NDIS_INITIALIZE_CMPLT); + resp->MessageLength = __constant_cpu_to_le32(52); + resp->RequestID = get_unaligned(&buf->RequestID); /* Still LE in msg buffer */ + resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS); + resp->MajorVersion = __constant_cpu_to_le32(RNDIS_MAJOR_VERSION); + resp->MinorVersion = __constant_cpu_to_le32(RNDIS_MINOR_VERSION); + resp->DeviceFlags = __constant_cpu_to_le32(RNDIS_DF_CONNECTIONLESS); + resp->Medium = __constant_cpu_to_le32(RNDIS_MEDIUM_802_3); + resp->MaxPacketsPerTransfer = __constant_cpu_to_le32(1); + resp->MaxTransferSize = cpu_to_le32( + rndis_per_dev_params[configNr].mtu + + ETHER_HDR_SIZE + + sizeof(struct rndis_packet_msg_type) + + 22); + resp->PacketAlignmentFactor = __constant_cpu_to_le32(0); + resp->AFListOffset = __constant_cpu_to_le32(0); + resp->AFListSize = __constant_cpu_to_le32(0); + + if (rndis_per_dev_params[configNr].ack) + rndis_per_dev_params[configNr].ack( + rndis_per_dev_params[configNr].dev); + + return 0; +} + +static int rndis_query_response(int configNr, rndis_query_msg_type *buf) +{ + rndis_query_cmplt_type *resp; + rndis_resp_t *r; + + debug("%s: OID = %08X\n", __func__, get_unaligned_le32(&buf->OID)); + if (!rndis_per_dev_params[configNr].dev) + return -ENOTSUPP; + + /* + * we need more memory: + * gen_ndis_query_resp expects enough space for + * rndis_query_cmplt_type followed by data. + * oid_supported_list is the largest data reply + */ + r = rndis_add_response(configNr, + sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type)); + if (!r) + return -ENOMEM; + resp = (rndis_query_cmplt_type *) r->buf; + + resp->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_QUERY_CMPLT); + resp->RequestID = get_unaligned(&buf->RequestID); /* Still LE in msg buffer */ + + if (gen_ndis_query_resp(configNr, get_unaligned_le32(&buf->OID), + get_unaligned_le32(&buf->InformationBufferOffset) + + 8 + (u8 *) buf, + get_unaligned_le32(&buf->InformationBufferLength), + r)) { + /* OID not supported */ + resp->Status = __constant_cpu_to_le32( + RNDIS_STATUS_NOT_SUPPORTED); + resp->MessageLength = __constant_cpu_to_le32(sizeof *resp); + resp->InformationBufferLength = __constant_cpu_to_le32(0); + resp->InformationBufferOffset = __constant_cpu_to_le32(0); + } else + resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS); + + if (rndis_per_dev_params[configNr].ack) + rndis_per_dev_params[configNr].ack( + rndis_per_dev_params[configNr].dev); + return 0; +} + +static int rndis_set_response(int configNr, rndis_set_msg_type *buf) +{ + u32 BufLength, BufOffset; + rndis_set_cmplt_type *resp; + rndis_resp_t *r; + + r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type)); + if (!r) + return -ENOMEM; + resp = (rndis_set_cmplt_type *) r->buf; + + BufLength = get_unaligned_le32(&buf->InformationBufferLength); + BufOffset = get_unaligned_le32(&buf->InformationBufferOffset); + +#ifdef VERBOSE + debug("%s: Length: %d\n", __func__, BufLength); + debug("%s: Offset: %d\n", __func__, BufOffset); + debug("%s: InfoBuffer: ", __func__); + + for (i = 0; i < BufLength; i++) + debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); + + debug("\n"); +#endif + + resp->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_SET_CMPLT); + resp->MessageLength = __constant_cpu_to_le32(16); + resp->RequestID = get_unaligned(&buf->RequestID); /* Still LE in msg buffer */ + if (gen_ndis_set_resp(configNr, get_unaligned_le32(&buf->OID), + ((u8 *) buf) + 8 + BufOffset, BufLength, r)) + resp->Status = __constant_cpu_to_le32( + RNDIS_STATUS_NOT_SUPPORTED); + else + resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS); + + if (rndis_per_dev_params[configNr].ack) + rndis_per_dev_params[configNr].ack( + rndis_per_dev_params[configNr].dev); + + return 0; +} + +static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) +{ + rndis_reset_cmplt_type *resp; + rndis_resp_t *r; + + r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type)); + if (!r) + return -ENOMEM; + resp = (rndis_reset_cmplt_type *) r->buf; + + resp->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_RESET_CMPLT); + resp->MessageLength = __constant_cpu_to_le32(16); + resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS); + /* resent information */ + resp->AddressingReset = __constant_cpu_to_le32(1); + + if (rndis_per_dev_params[configNr].ack) + rndis_per_dev_params[configNr].ack( + rndis_per_dev_params[configNr].dev); + + return 0; +} + +static int rndis_keepalive_response(int configNr, + rndis_keepalive_msg_type *buf) +{ + rndis_keepalive_cmplt_type *resp; + rndis_resp_t *r; + + /* host "should" check only in RNDIS_DATA_INITIALIZED state */ + + r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type)); + if (!r) + return -ENOMEM; + resp = (rndis_keepalive_cmplt_type *) r->buf; + + resp->MessageType = __constant_cpu_to_le32( + REMOTE_NDIS_KEEPALIVE_CMPLT); + resp->MessageLength = __constant_cpu_to_le32(16); + resp->RequestID = get_unaligned(&buf->RequestID); /* Still LE in msg buffer */ + resp->Status = __constant_cpu_to_le32(RNDIS_STATUS_SUCCESS); + + if (rndis_per_dev_params[configNr].ack) + rndis_per_dev_params[configNr].ack( + rndis_per_dev_params[configNr].dev); + + return 0; +} + + +/* + * Device to Host Comunication + */ +static int rndis_indicate_status_msg(int configNr, u32 status) +{ + rndis_indicate_status_msg_type *resp; + rndis_resp_t *r; + + if (rndis_per_dev_params[configNr].state == RNDIS_UNINITIALIZED) + return -ENOTSUPP; + + r = rndis_add_response(configNr, + sizeof(rndis_indicate_status_msg_type)); + if (!r) + return -ENOMEM; + resp = (rndis_indicate_status_msg_type *) r->buf; + + resp->MessageType = __constant_cpu_to_le32( + REMOTE_NDIS_INDICATE_STATUS_MSG); + resp->MessageLength = __constant_cpu_to_le32(20); + resp->Status = cpu_to_le32(status); + resp->StatusBufferLength = __constant_cpu_to_le32(0); + resp->StatusBufferOffset = __constant_cpu_to_le32(0); + + if (rndis_per_dev_params[configNr].ack) + rndis_per_dev_params[configNr].ack( + rndis_per_dev_params[configNr].dev); + return 0; +} + +int rndis_signal_connect(int configNr) +{ + rndis_per_dev_params[configNr].media_state + = NDIS_MEDIA_STATE_CONNECTED; + return rndis_indicate_status_msg(configNr, + RNDIS_STATUS_MEDIA_CONNECT); +} + +int rndis_signal_disconnect(int configNr) +{ + rndis_per_dev_params[configNr].media_state + = NDIS_MEDIA_STATE_DISCONNECTED; + +#ifdef RNDIS_COMPLETE_SIGNAL_DISCONNECT + return rndis_indicate_status_msg(configNr, + RNDIS_STATUS_MEDIA_DISCONNECT); +#else + return 0; +#endif +} + +void rndis_uninit(int configNr) +{ + u8 *buf; + u32 length; + + if (configNr >= RNDIS_MAX_CONFIGS) + return; + rndis_per_dev_params[configNr].used = 0; + rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED; + + /* drain the response queue */ + while ((buf = rndis_get_next_response(configNr, &length))) + rndis_free_response(configNr, buf); +} + +void rndis_set_host_mac(int configNr, const u8 *addr) +{ + rndis_per_dev_params[configNr].host_mac = addr; +} + +enum rndis_state rndis_get_state(int configNr) +{ + if (configNr >= RNDIS_MAX_CONFIGS || configNr < 0) + return -ENOTSUPP; + return rndis_per_dev_params[configNr].state; +} + +/* + * Message Parser + */ +int rndis_msg_parser(u8 configNr, u8 *buf) +{ + u32 MsgType, MsgLength; + __le32 *tmp; + struct rndis_params *params; + + debug("%s: configNr = %d, %p\n", __func__, configNr, buf); + + if (!buf) + return -ENOMEM; + + tmp = (__le32 *) buf; + MsgType = get_unaligned_le32(tmp++); + MsgLength = get_unaligned_le32(tmp++); + + if (configNr >= RNDIS_MAX_CONFIGS) + return -ENOTSUPP; + params = &rndis_per_dev_params[configNr]; + + /* + * NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for + * rx/tx statistics and link status, in addition to KEEPALIVE traffic + * and normal HC level polling to see if there's any IN traffic. + */ + + /* For USB: responses may take up to 10 seconds */ + switch (MsgType) { + case REMOTE_NDIS_INITIALIZE_MSG: + debug("%s: REMOTE_NDIS_INITIALIZE_MSG\n", __func__); + params->state = RNDIS_INITIALIZED; + return rndis_init_response(configNr, + (rndis_init_msg_type *) buf); + + case REMOTE_NDIS_HALT_MSG: + debug("%s: REMOTE_NDIS_HALT_MSG\n", __func__); + params->state = RNDIS_UNINITIALIZED; + return 0; + + case REMOTE_NDIS_QUERY_MSG: + return rndis_query_response(configNr, + (rndis_query_msg_type *) buf); + + case REMOTE_NDIS_SET_MSG: + return rndis_set_response(configNr, + (rndis_set_msg_type *) buf); + + case REMOTE_NDIS_RESET_MSG: + debug("%s: REMOTE_NDIS_RESET_MSG\n", __func__); + return rndis_reset_response(configNr, + (rndis_reset_msg_type *) buf); + + case REMOTE_NDIS_KEEPALIVE_MSG: + /* For USB: host does this every 5 seconds */ +#if defined(DEBUG) && defined(DEBUG_VERBOSE) + debug("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", __func__); +#endif + return rndis_keepalive_response(configNr, + (rndis_keepalive_msg_type *) buf); + + default: + /* + * At least Windows XP emits some undefined RNDIS messages. + * In one case those messages seemed to relate to the host + * suspending itself. + */ + debug("%s: unknown RNDIS message 0x%08X len %d\n", + __func__ , MsgType, MsgLength); + { + unsigned i; + for (i = 0; i < MsgLength; i += 16) { + debug("%03d: " + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + "\n", + i, + buf[i], buf[i+1], + buf[i+2], buf[i+3], + buf[i+4], buf[i+5], + buf[i+6], buf[i+7], + buf[i+8], buf[i+9], + buf[i+10], buf[i+11], + buf[i+12], buf[i+13], + buf[i+14], buf[i+15]); + } + } + break; + } + + return -ENOTSUPP; +} + +int rndis_register(int (*rndis_control_ack)(struct eth_device *)) +{ + u8 i; + + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + if (!rndis_per_dev_params[i].used) { + rndis_per_dev_params[i].used = 1; + rndis_per_dev_params[i].ack = rndis_control_ack; + debug("%s: configNr = %d\n", __func__, i); + return i; + } + } + debug("%s failed\n", __func__); + + return -1; +} + +void rndis_deregister(int configNr) +{ + debug("%s: configNr = %d\n", __func__, configNr); + + if (configNr >= RNDIS_MAX_CONFIGS) + return; + rndis_per_dev_params[configNr].used = 0; + + return; +} + +int rndis_set_param_dev(u8 configNr, struct eth_device *dev, int mtu, + struct net_device_stats *stats, u16 *cdc_filter) +{ + debug("%s: configNr = %d\n", __func__, configNr); + if (!dev || !stats) + return -1; + if (configNr >= RNDIS_MAX_CONFIGS) + return -1; + + rndis_per_dev_params[configNr].dev = dev; + rndis_per_dev_params[configNr].stats = stats; + rndis_per_dev_params[configNr].mtu = mtu; + rndis_per_dev_params[configNr].filter = cdc_filter; + + return 0; +} + +int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr) +{ + debug("%s: configNr = %d\n", __func__, configNr); + if (!vendorDescr) + return -1; + if (configNr >= RNDIS_MAX_CONFIGS) + return -1; + + rndis_per_dev_params[configNr].vendorID = vendorID; + rndis_per_dev_params[configNr].vendorDescr = vendorDescr; + + return 0; +} + +int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed) +{ + debug("%s: configNr = %d, %u %u\n", __func__, configNr, medium, speed); + if (configNr >= RNDIS_MAX_CONFIGS) + return -1; + + rndis_per_dev_params[configNr].medium = medium; + rndis_per_dev_params[configNr].speed = speed; + + return 0; +} + +void rndis_add_hdr(void *buf, int length) +{ + struct rndis_packet_msg_type *header; + + header = buf; + memset(header, 0, sizeof *header); + header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG); + header->MessageLength = cpu_to_le32(length + sizeof *header); + header->DataOffset = __constant_cpu_to_le32(36); + header->DataLength = cpu_to_le32(length); +} + +void rndis_free_response(int configNr, u8 *buf) +{ + rndis_resp_t *r; + struct list_head *act, *tmp; + + list_for_each_safe(act, tmp, + &(rndis_per_dev_params[configNr].resp_queue)) + { + r = list_entry(act, rndis_resp_t, list); + if (r && r->buf == buf) { + list_del(&r->list); + free(r); + } + } +} + +u8 *rndis_get_next_response(int configNr, u32 *length) +{ + rndis_resp_t *r; + struct list_head *act, *tmp; + + if (!length) + return NULL; + + list_for_each_safe(act, tmp, + &(rndis_per_dev_params[configNr].resp_queue)) + { + r = list_entry(act, rndis_resp_t, list); + if (!r->send) { + r->send = 1; + *length = r->length; + return r->buf; + } + } + + return NULL; +} + +static rndis_resp_t *rndis_add_response(int configNr, u32 length) +{ + rndis_resp_t *r; + + /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */ + r = malloc(sizeof(rndis_resp_t) + length); + if (!r) + return NULL; + + r->buf = (u8 *) (r + 1); + r->length = length; + r->send = 0; + + list_add_tail(&r->list, + &(rndis_per_dev_params[configNr].resp_queue)); + return r; +} + +int rndis_rm_hdr(void *buf, int length) +{ + /* tmp points to a struct rndis_packet_msg_type */ + __le32 *tmp = buf; + int offs, len; + + /* MessageType, MessageLength */ + if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG) + != get_unaligned(tmp++)) + return -EINVAL; + tmp++; + + /* DataOffset, DataLength */ + offs = get_unaligned_le32(tmp++) + 8 /* offset of DataOffset */; + if (offs != sizeof(struct rndis_packet_msg_type)) + debug("%s: unexpected DataOffset: %d\n", __func__, offs); + if (offs >= length) + return -EOVERFLOW; + + len = get_unaligned_le32(tmp++); + if (len + sizeof(struct rndis_packet_msg_type) != length) + debug("%s: unexpected DataLength: %d, packet length=%d\n", + __func__, len, length); + + memmove(buf, buf + offs, len); + + return offs; +} + +int rndis_init(void) +{ + u8 i; + + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + rndis_per_dev_params[i].confignr = i; + rndis_per_dev_params[i].used = 0; + rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED; + rndis_per_dev_params[i].media_state + = NDIS_MEDIA_STATE_DISCONNECTED; + INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); + } + + return 0; +} + +void rndis_exit(void) +{ + /* Nothing to do */ +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/rndis.h b/qemu/roms/u-boot/drivers/usb/gadget/rndis.h new file mode 100644 index 000000000..d9e3a7528 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/rndis.h @@ -0,0 +1,260 @@ +/* + * RNDIS Definitions for Remote NDIS + * + * Authors: Benedikt Spranger, Pengutronix + * Robert Schwebel, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This software was originally developed in conformance with + * Microsoft's Remote NDIS Specification License Agreement. + */ + +#ifndef _USBGADGET_RNDIS_H +#define _USBGADGET_RNDIS_H + +#include "ndis.h" + +/* + * By default rndis_signal_disconnect does not send status message about + * RNDIS disconnection to USB host (indicated as cable disconnected). + * Define RNDIS_COMPLETE_SIGNAL_DISCONNECT to send it. + * However, this will cause 1 sec delay on Ethernet device halt. + * Usually you do not need to define it. Mostly usable for debugging. + */ + +#define RNDIS_MAXIMUM_FRAME_SIZE 1518 +#define RNDIS_MAX_TOTAL_SIZE 1558 + +/* Remote NDIS Versions */ +#define RNDIS_MAJOR_VERSION 1 +#define RNDIS_MINOR_VERSION 0 + +/* Status Values */ +#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ +#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */ +#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */ +#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */ +#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */ +#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */ +/* + * For all not specified status messages: + * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx + */ + +/* Message Set for Connectionless (802.3) Devices */ +#define REMOTE_NDIS_PACKET_MSG 0x00000001U +#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */ +#define REMOTE_NDIS_HALT_MSG 0x00000003U +#define REMOTE_NDIS_QUERY_MSG 0x00000004U +#define REMOTE_NDIS_SET_MSG 0x00000005U +#define REMOTE_NDIS_RESET_MSG 0x00000006U +#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U +#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U + +/* Message completion */ +#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U +#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U +#define REMOTE_NDIS_SET_CMPLT 0x80000005U +#define REMOTE_NDIS_RESET_CMPLT 0x80000006U +#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U + +/* Device Flags */ +#define RNDIS_DF_CONNECTIONLESS 0x00000001U +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U + +#define RNDIS_MEDIUM_802_3 0x00000000U + +/* from drivers/net/sk98lin/h/skgepnmi.h */ +#define OID_PNP_CAPABILITIES 0xFD010100 +#define OID_PNP_SET_POWER 0xFD010101 +#define OID_PNP_QUERY_POWER 0xFD010102 +#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 +#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 +#define OID_PNP_ENABLE_WAKE_UP 0xFD010106 + + +typedef struct rndis_init_msg_type { + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 MajorVersion; + __le32 MinorVersion; + __le32 MaxTransferSize; +} rndis_init_msg_type; + +typedef struct rndis_init_cmplt_type { + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 Status; + __le32 MajorVersion; + __le32 MinorVersion; + __le32 DeviceFlags; + __le32 Medium; + __le32 MaxPacketsPerTransfer; + __le32 MaxTransferSize; + __le32 PacketAlignmentFactor; + __le32 AFListOffset; + __le32 AFListSize; +} rndis_init_cmplt_type; + +typedef struct rndis_halt_msg_type { + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; +} rndis_halt_msg_type; + +typedef struct rndis_query_msg_type { + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 OID; + __le32 InformationBufferLength; + __le32 InformationBufferOffset; + __le32 DeviceVcHandle; +} rndis_query_msg_type; + +typedef struct rndis_query_cmplt_type { + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 Status; + __le32 InformationBufferLength; + __le32 InformationBufferOffset; +} rndis_query_cmplt_type; + +typedef struct rndis_set_msg_type { + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 OID; + __le32 InformationBufferLength; + __le32 InformationBufferOffset; + __le32 DeviceVcHandle; +} rndis_set_msg_type; + +typedef struct rndis_set_cmplt_type { + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 Status; +} rndis_set_cmplt_type; + +typedef struct rndis_reset_msg_type { + __le32 MessageType; + __le32 MessageLength; + __le32 Reserved; +} rndis_reset_msg_type; + +typedef struct rndis_reset_cmplt_type { + __le32 MessageType; + __le32 MessageLength; + __le32 Status; + __le32 AddressingReset; +} rndis_reset_cmplt_type; + +typedef struct rndis_indicate_status_msg_type { + __le32 MessageType; + __le32 MessageLength; + __le32 Status; + __le32 StatusBufferLength; + __le32 StatusBufferOffset; +} rndis_indicate_status_msg_type; + +typedef struct rndis_keepalive_msg_type { + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; +} rndis_keepalive_msg_type; + +typedef struct rndis_keepalive_cmplt_type { + __le32 MessageType; + __le32 MessageLength; + __le32 RequestID; + __le32 Status; +} rndis_keepalive_cmplt_type; + +struct rndis_packet_msg_type { + __le32 MessageType; + __le32 MessageLength; + __le32 DataOffset; + __le32 DataLength; + __le32 OOBDataOffset; + __le32 OOBDataLength; + __le32 NumOOBDataElements; + __le32 PerPacketInfoOffset; + __le32 PerPacketInfoLength; + __le32 VcHandle; + __le32 Reserved; +} __attribute__ ((packed)); + +struct rndis_config_parameter { + __le32 ParameterNameOffset; + __le32 ParameterNameLength; + __le32 ParameterType; + __le32 ParameterValueOffset; + __le32 ParameterValueLength; +}; + +/* implementation specific */ +enum rndis_state { + RNDIS_UNINITIALIZED, + RNDIS_INITIALIZED, + RNDIS_DATA_INITIALIZED, +}; + +typedef struct rndis_resp_t { + struct list_head list; + u8 *buf; + u32 length; + int send; +} rndis_resp_t; + +typedef struct rndis_params { + u8 confignr; + u8 used; + u16 saved_filter; + enum rndis_state state; + u32 medium; + u32 speed; + u32 media_state; + + const u8 *host_mac; + u16 *filter; + struct eth_device *dev; + struct net_device_stats *stats; + int mtu; + + u32 vendorID; + const char *vendorDescr; + int (*ack)(struct eth_device *); + struct list_head resp_queue; +} rndis_params; + +/* RNDIS Message parser and other useless functions */ +int rndis_msg_parser(u8 configNr, u8 *buf); +enum rndis_state rndis_get_state(int configNr); +int rndis_register(int (*rndis_control_ack)(struct eth_device *)); +void rndis_deregister(int configNr); +int rndis_set_param_dev(u8 configNr, struct eth_device *dev, int mtu, + struct net_device_stats *stats, u16 *cdc_filter); +int rndis_set_param_vendor(u8 configNr, u32 vendorID, + const char *vendorDescr); +int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed); +void rndis_add_hdr(void *bug, int length); +int rndis_rm_hdr(void *bug, int length); +u8 *rndis_get_next_response(int configNr, u32 *length); +void rndis_free_response(int configNr, u8 *buf); + +void rndis_uninit(int configNr); +int rndis_signal_connect(int configNr); +int rndis_signal_disconnect(int configNr); +extern void rndis_set_host_mac(int configNr, const u8 *addr); + +int rndis_init(void); +void rndis_exit(void); + +#endif /* _USBGADGET_RNDIS_H */ diff --git a/qemu/roms/u-boot/drivers/usb/gadget/s3c_udc_otg.c b/qemu/roms/u-boot/drivers/usb/gadget/s3c_udc_otg.c new file mode 100644 index 000000000..63d4487a9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/s3c_udc_otg.c @@ -0,0 +1,890 @@ +/* + * drivers/usb/gadget/s3c_udc_otg.c + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * Copyright (C) 2008 for Samsung Electronics + * + * BSP Support for Samsung's UDC driver + * available at: + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git + * + * State machine bugfixes: + * Marek Szyprowski + * + * Ported to u-boot: + * Marek Szyprowski + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#undef DEBUG +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include "regs-otg.h" +#include + +/***********************************************************/ + +#define OTG_DMA_MODE 1 + +#define DEBUG_SETUP 0 +#define DEBUG_EP0 0 +#define DEBUG_ISR 0 +#define DEBUG_OUT_EP 0 +#define DEBUG_IN_EP 0 + +#include + +#define EP0_CON 0 +#define EP_MASK 0xF + +static char *state_names[] = { + "WAIT_FOR_SETUP", + "DATA_STATE_XMIT", + "DATA_STATE_NEED_ZLP", + "WAIT_FOR_OUT_STATUS", + "DATA_STATE_RECV", + "WAIT_FOR_COMPLETE", + "WAIT_FOR_OUT_COMPLETE", + "WAIT_FOR_IN_COMPLETE", + "WAIT_FOR_NULL_COMPLETE", +}; + +#define DRIVER_DESC "S3C HS USB OTG Device Driver, (c) Samsung Electronics" +#define DRIVER_VERSION "15 March 2009" + +struct s3c_udc *the_controller; + +static const char driver_name[] = "s3c-udc"; +static const char driver_desc[] = DRIVER_DESC; +static const char ep0name[] = "ep0-control"; + +/* Max packet size*/ +static unsigned int ep0_fifo_size = 64; +static unsigned int ep_fifo_size = 512; +static unsigned int ep_fifo_size2 = 1024; +static int reset_available = 1; + +static struct usb_ctrlrequest *usb_ctrl; +static dma_addr_t usb_ctrl_dma_addr; + +/* + Local declarations. +*/ +static int s3c_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *); +static int s3c_ep_disable(struct usb_ep *ep); +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags); +static void s3c_free_request(struct usb_ep *ep, struct usb_request *); + +static int s3c_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags); +static int s3c_dequeue(struct usb_ep *ep, struct usb_request *); +static int s3c_fifo_status(struct usb_ep *ep); +static void s3c_fifo_flush(struct usb_ep *ep); +static void s3c_ep0_read(struct s3c_udc *dev); +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep); +static void s3c_handle_ep0(struct s3c_udc *dev); +static int s3c_ep0_write(struct s3c_udc *dev); +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req); +static void done(struct s3c_ep *ep, struct s3c_request *req, int status); +static void stop_activity(struct s3c_udc *dev, + struct usb_gadget_driver *driver); +static int udc_enable(struct s3c_udc *dev); +static void udc_set_address(struct s3c_udc *dev, unsigned char address); +static void reconfig_usbd(void); +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed); +static void nuke(struct s3c_ep *ep, int status); +static int s3c_udc_set_halt(struct usb_ep *_ep, int value); +static void s3c_udc_set_nak(struct s3c_ep *ep); + +void set_udc_gadget_private_data(void *p) +{ + debug_cond(DEBUG_SETUP != 0, + "%s: the_controller: 0x%p, p: 0x%p\n", __func__, + the_controller, p); + the_controller->gadget.dev.device_data = p; +} + +void *get_udc_gadget_private_data(struct usb_gadget *gadget) +{ + return gadget->dev.device_data; +} + +static struct usb_ep_ops s3c_ep_ops = { + .enable = s3c_ep_enable, + .disable = s3c_ep_disable, + + .alloc_request = s3c_alloc_request, + .free_request = s3c_free_request, + + .queue = s3c_queue, + .dequeue = s3c_dequeue, + + .set_halt = s3c_udc_set_halt, + .fifo_status = s3c_fifo_status, + .fifo_flush = s3c_fifo_flush, +}; + +#define create_proc_files() do {} while (0) +#define remove_proc_files() do {} while (0) + +/***********************************************************/ + +void __iomem *regs_otg; +struct s3c_usbotg_reg *reg; +struct s3c_usbotg_phy *phy; +static unsigned int usb_phy_ctrl; + +void otg_phy_init(struct s3c_udc *dev) +{ + dev->pdata->phy_control(1); + + /*USB PHY0 Enable */ + printf("USB PHY0 Enable\n"); + + /* Enable PHY */ + writel(readl(usb_phy_ctrl) | USB_PHY_CTRL_EN0, usb_phy_ctrl); + + if (dev->pdata->usb_flags == PHY0_SLEEP) /* C210 Universal */ + writel((readl(&phy->phypwr) + &~(PHY_0_SLEEP | OTG_DISABLE_0 | ANALOG_PWRDOWN) + &~FORCE_SUSPEND_0), &phy->phypwr); + else /* C110 GONI */ + writel((readl(&phy->phypwr) &~(OTG_DISABLE_0 | ANALOG_PWRDOWN) + &~FORCE_SUSPEND_0), &phy->phypwr); + + if (s5p_cpu_id == 0x4412) + writel((readl(&phy->phyclk) & ~(EXYNOS4X12_ID_PULLUP0 | + EXYNOS4X12_COMMON_ON_N0)) | EXYNOS4X12_CLK_SEL_24MHZ, + &phy->phyclk); /* PLL 24Mhz */ + else + writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)) | + CLK_SEL_24MHZ, &phy->phyclk); /* PLL 24Mhz */ + + writel((readl(&phy->rstcon) &~(LINK_SW_RST | PHYLNK_SW_RST)) + | PHY_SW_RST0, &phy->rstcon); + udelay(10); + writel(readl(&phy->rstcon) + &~(PHY_SW_RST0 | LINK_SW_RST | PHYLNK_SW_RST), &phy->rstcon); + udelay(10); +} + +void otg_phy_off(struct s3c_udc *dev) +{ + /* reset controller just in case */ + writel(PHY_SW_RST0, &phy->rstcon); + udelay(20); + writel(readl(&phy->phypwr) &~PHY_SW_RST0, &phy->rstcon); + udelay(20); + + writel(readl(&phy->phypwr) | OTG_DISABLE_0 | ANALOG_PWRDOWN + | FORCE_SUSPEND_0, &phy->phypwr); + + writel(readl(usb_phy_ctrl) &~USB_PHY_CTRL_EN0, usb_phy_ctrl); + + writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)), + &phy->phyclk); + + udelay(10000); + + dev->pdata->phy_control(0); +} + +/***********************************************************/ + +#include "s3c_udc_otg_xfer_dma.c" + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct s3c_udc *dev) +{ + debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev); + + udc_set_address(dev, 0); + + dev->ep0state = WAIT_FOR_SETUP; + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->usb_address = 0; + + otg_phy_off(dev); +} + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct s3c_udc *dev) +{ + unsigned int i; + + debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev); + + /* device/ep0 records init */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + dev->ep0state = WAIT_FOR_SETUP; + + /* basic endpoint records init */ + for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { + struct s3c_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->desc = 0; + ep->stopped = 0; + INIT_LIST_HEAD(&ep->queue); + ep->pio_irqs = 0; + } + + /* the rest was statically initialized, and is read-only */ +} + +#define BYTES2MAXP(x) (x / 8) +#define MAXP2BYTES(x) (x * 8) + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static int udc_enable(struct s3c_udc *dev) +{ + debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev); + + otg_phy_init(dev); + reconfig_usbd(); + + debug_cond(DEBUG_SETUP != 0, + "S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n", + readl(®->gintmsk)); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + + return 0; +} + +/* + Register entry point for the peripheral controller driver. +*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct s3c_udc *dev = the_controller; + int retval = 0; + unsigned long flags; + + debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name"); + + if (!driver + || (driver->speed != USB_SPEED_FULL + && driver->speed != USB_SPEED_HIGH) + || !driver->bind || !driver->disconnect || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + spin_lock_irqsave(&dev->lock, flags); + /* first hook up the driver ... */ + dev->driver = driver; + spin_unlock_irqrestore(&dev->lock, flags); + + if (retval) { /* TODO */ + printf("target device_add failed, error %d\n", retval); + return retval; + } + + retval = driver->bind(&dev->gadget); + if (retval) { + debug_cond(DEBUG_SETUP != 0, + "%s: bind to driver --> error %d\n", + dev->gadget.name, retval); + dev->driver = 0; + return retval; + } + + enable_irq(IRQ_OTG); + + debug_cond(DEBUG_SETUP != 0, + "Registered gadget driver %s\n", dev->gadget.name); + udc_enable(dev); + + return 0; +} + +/* + * Unregister entry point for the peripheral controller driver. + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct s3c_udc *dev = the_controller; + unsigned long flags; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + + disable_irq(IRQ_OTG); + + udc_disable(dev); + return 0; +} + +/* + * done - retire a request; caller blocked irqs + */ +static void done(struct s3c_ep *ep, struct s3c_request *req, int status) +{ + unsigned int stopped = ep->stopped; + + debug("%s: %s %p, req = %p, stopped = %d\n", + __func__, ep->ep.name, ep, &req->req, stopped); + + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) { + debug("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + } + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + +#ifdef DEBUG + printf("calling complete callback\n"); + { + int i, len = req->req.length; + + printf("pkt[%d] = ", req->req.length); + if (len > 64) + len = 64; + for (i = 0; i < len; i++) { + printf("%02x", ((u8 *)req->req.buf)[i]); + if ((i & 7) == 7) + printf(" "); + } + printf("\n"); + } +#endif + spin_unlock(&ep->dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->dev->lock); + + debug("callback completed\n"); + + ep->stopped = stopped; +} + +/* + * nuke - dequeue ALL requests + */ +static void nuke(struct s3c_ep *ep, int status) +{ + struct s3c_request *req; + + debug("%s: %s %p\n", __func__, ep->ep.name, ep); + + /* called with irqs blocked */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + done(ep, req, status); + } +} + +static void stop_activity(struct s3c_udc *dev, + struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { + struct s3c_ep *ep = &dev->ep[i]; + ep->stopped = 1; + nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +static void reconfig_usbd(void) +{ + /* 2. Soft-reset OTG Core and then unreset again. */ + int i; + unsigned int uTemp = writel(CORE_SOFT_RESET, ®->grstctl); + + debug("Reseting OTG controller\n"); + + writel(0<<15 /* PHY Low Power Clock sel*/ + |1<<14 /* Non-Periodic TxFIFO Rewind Enable*/ + |0x5<<10 /* Turnaround time*/ + |0<<9 | 0<<8 /* [0:HNP disable,1:HNP enable][ 0:SRP disable*/ + /* 1:SRP enable] H1= 1,1*/ + |0<<7 /* Ulpi DDR sel*/ + |0<<6 /* 0: high speed utmi+, 1: full speed serial*/ + |0<<4 /* 0: utmi+, 1:ulpi*/ + |1<<3 /* phy i/f 0:8bit, 1:16bit*/ + |0x7<<0, /* HS/FS Timeout**/ + ®->gusbcfg); + + /* 3. Put the OTG device core in the disconnected state.*/ + uTemp = readl(®->dctl); + uTemp |= SOFT_DISCONNECT; + writel(uTemp, ®->dctl); + + udelay(20); + + /* 4. Make the OTG device core exit from the disconnected state.*/ + uTemp = readl(®->dctl); + uTemp = uTemp & ~SOFT_DISCONNECT; + writel(uTemp, ®->dctl); + + /* 5. Configure OTG Core to initial settings of device mode.*/ + /* [][1: full speed(30Mhz) 0:high speed]*/ + writel(EP_MISS_CNT(1) | DEV_SPEED_HIGH_SPEED_20, ®->dcfg); + + mdelay(1); + + /* 6. Unmask the core interrupts*/ + writel(GINTMSK_INIT, ®->gintmsk); + + /* 7. Set NAK bit of EP0, EP1, EP2*/ + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[EP0_CON].doepctl); + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[EP0_CON].diepctl); + + for (i = 1; i < S3C_MAX_ENDPOINTS; i++) { + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[i].doepctl); + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[i].diepctl); + } + + /* 8. Unmask EPO interrupts*/ + writel(((1 << EP0_CON) << DAINT_OUT_BIT) + | (1 << EP0_CON), ®->daintmsk); + + /* 9. Unmask device OUT EP common interrupts*/ + writel(DOEPMSK_INIT, ®->doepmsk); + + /* 10. Unmask device IN EP common interrupts*/ + writel(DIEPMSK_INIT, ®->diepmsk); + + /* 11. Set Rx FIFO Size (in 32-bit words) */ + writel(RX_FIFO_SIZE >> 2, ®->grxfsiz); + + /* 12. Set Non Periodic Tx FIFO Size */ + writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0, + ®->gnptxfsiz); + + for (i = 1; i < S3C_MAX_HW_ENDPOINTS; i++) + writel((PTX_FIFO_SIZE >> 2) << 16 | + ((RX_FIFO_SIZE + NPTX_FIFO_SIZE + + PTX_FIFO_SIZE*(i-1)) >> 2) << 0, + ®->dieptxf[i-1]); + + /* Flush the RX FIFO */ + writel(RX_FIFO_FLUSH, ®->grstctl); + while (readl(®->grstctl) & RX_FIFO_FLUSH) + debug("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); + + /* Flush all the Tx FIFO's */ + writel(TX_FIFO_FLUSH_ALL, ®->grstctl); + writel(TX_FIFO_FLUSH_ALL | TX_FIFO_FLUSH, ®->grstctl); + while (readl(®->grstctl) & TX_FIFO_FLUSH) + debug("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); + + /* 13. Clear NAK bit of EP0, EP1, EP2*/ + /* For Slave mode*/ + /* EP0: Control OUT */ + writel(DEPCTL_EPDIS | DEPCTL_CNAK, + ®->out_endp[EP0_CON].doepctl); + + /* 14. Initialize OTG Link Core.*/ + writel(GAHBCFG_INIT, ®->gahbcfg); +} + +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed) +{ + unsigned int ep_ctrl; + int i; + + if (speed == USB_SPEED_HIGH) { + ep0_fifo_size = 64; + ep_fifo_size = 512; + ep_fifo_size2 = 1024; + dev->gadget.speed = USB_SPEED_HIGH; + } else { + ep0_fifo_size = 64; + ep_fifo_size = 64; + ep_fifo_size2 = 64; + dev->gadget.speed = USB_SPEED_FULL; + } + + dev->ep[0].ep.maxpacket = ep0_fifo_size; + for (i = 1; i < S3C_MAX_ENDPOINTS; i++) + dev->ep[i].ep.maxpacket = ep_fifo_size; + + /* EP0 - Control IN (64 bytes)*/ + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + writel(ep_ctrl|(0<<0), ®->in_endp[EP0_CON].diepctl); + + /* EP0 - Control OUT (64 bytes)*/ + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); + writel(ep_ctrl|(0<<0), ®->out_endp[EP0_CON].doepctl); +} + +static int s3c_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct s3c_ep *ep; + struct s3c_udc *dev; + unsigned long flags; + + debug("%s: %p\n", __func__, _ep); + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep_maxpacket(ep) < + le16_to_cpu(get_unaligned(&desc->wMaxPacketSize))) { + + debug("%s: bad ep or descriptor\n", __func__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + + debug("%s: %s type mismatch\n", __func__, _ep->name); + return -EINVAL; + } + + /* hardware _could_ do smaller, but driver doesn't */ + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)) != + ep_maxpacket(ep)) || !get_unaligned(&desc->wMaxPacketSize)) { + + debug("%s: bad %s maxpacket\n", __func__, _ep->name); + return -ERANGE; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + + debug("%s: bogus device state\n", __func__); + return -ESHUTDOWN; + } + + ep->stopped = 0; + ep->desc = desc; + ep->pio_irqs = 0; + ep->ep.maxpacket = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); + + /* Reset halt state */ + s3c_udc_set_nak(ep); + s3c_udc_set_halt(_ep, 0); + + spin_lock_irqsave(&ep->dev->lock, flags); + s3c_udc_ep_activate(ep); + spin_unlock_irqrestore(&ep->dev->lock, flags); + + debug("%s: enabled %s, stopped = %d, maxpacket = %d\n", + __func__, _ep->name, ep->stopped, ep->ep.maxpacket); + return 0; +} + +/* + * Disable EP + */ +static int s3c_ep_disable(struct usb_ep *_ep) +{ + struct s3c_ep *ep; + unsigned long flags; + + debug("%s: %p\n", __func__, _ep); + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep || !ep->desc) { + debug("%s: %s not enabled\n", __func__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* Nuke all pending requests */ + nuke(ep, -ESHUTDOWN); + + ep->desc = 0; + ep->stopped = 1; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + debug("%s: disabled %s\n", __func__, _ep->name); + return 0; +} + +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct s3c_request *req; + + debug("%s: %s %p\n", __func__, ep->name, ep); + + req = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*req)); + if (!req) + return 0; + + memset(req, 0, sizeof *req); + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void s3c_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct s3c_request *req; + + debug("%s: %p\n", __func__, ep); + + req = container_of(_req, struct s3c_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +/* dequeue JUST ONE request */ +static int s3c_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c_ep *ep; + struct s3c_request *req; + unsigned long flags; + + debug("%s: %p\n", __func__, _ep); + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/* + * Return bytes in EP FIFO + */ +static int s3c_fifo_status(struct usb_ep *_ep) +{ + int count = 0; + struct s3c_ep *ep; + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep) { + debug("%s: bad ep\n", __func__); + return -ENODEV; + } + + debug("%s: %d\n", __func__, ep_index(ep)); + + /* LPD can't report unclaimed bytes from IN fifos */ + if (ep_is_in(ep)) + return -EOPNOTSUPP; + + return count; +} + +/* + * Flush EP FIFO + */ +static void s3c_fifo_flush(struct usb_ep *_ep) +{ + struct s3c_ep *ep; + + ep = container_of(_ep, struct s3c_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + debug("%s: bad ep\n", __func__); + return; + } + + debug("%s: %d\n", __func__, ep_index(ep)); +} + +static const struct usb_gadget_ops s3c_udc_ops = { + /* current versions must always be self-powered */ +}; + +static struct s3c_udc memory = { + .usb_address = 0, + .gadget = { + .ops = &s3c_udc_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &s3c_ep_ops, + .maxpacket = EP0_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = 0, + .bmAttributes = 0, + + .ep_type = ep_control, + }, + + /* first group of endpoints */ + .ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_out, + .fifo_num = 1, + }, + + .ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_OUT | 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_in, + .fifo_num = 2, + }, + + .ep[3] = { + .ep = { + .name = "ep3in-int", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 3, + .bmAttributes = USB_ENDPOINT_XFER_INT, + + .ep_type = ep_interrupt, + .fifo_num = 3, + }, +}; + +/* + * probe - binds to the platform device + */ + +int s3c_udc_probe(struct s3c_plat_otg_data *pdata) +{ + struct s3c_udc *dev = &memory; + int retval = 0; + + debug("%s: %p\n", __func__, pdata); + + dev->pdata = pdata; + + phy = (struct s3c_usbotg_phy *)pdata->regs_phy; + reg = (struct s3c_usbotg_reg *)pdata->regs_otg; + usb_phy_ctrl = pdata->usb_phy_ctrl; + + /* regs_otg = (void *)pdata->regs_otg; */ + + dev->gadget.is_dualspeed = 1; /* Hack only*/ + dev->gadget.is_otg = 0; + dev->gadget.is_a_peripheral = 0; + dev->gadget.b_hnp_enable = 0; + dev->gadget.a_hnp_support = 0; + dev->gadget.a_alt_hnp_support = 0; + + the_controller = dev; + + usb_ctrl = memalign(CONFIG_SYS_CACHELINE_SIZE, + ROUND(sizeof(struct usb_ctrlrequest), + CONFIG_SYS_CACHELINE_SIZE)); + if (!usb_ctrl) { + error("No memory available for UDC!\n"); + return -ENOMEM; + } + + usb_ctrl_dma_addr = (dma_addr_t) usb_ctrl; + + udc_reinit(dev); + + return retval; +} + +int usb_gadget_handle_interrupts() +{ + u32 intr_status = readl(®->gintsts); + u32 gintmsk = readl(®->gintmsk); + + if (intr_status & gintmsk) + return s3c_udc_irq(1, (void *)the_controller); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/qemu/roms/u-boot/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c new file mode 100644 index 000000000..06dfeed90 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -0,0 +1,1480 @@ +/* + * drivers/usb/gadget/s3c_udc_otg_xfer_dma.c + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * Copyright (C) 2009 for Samsung Electronics + * + * BSP Support for Samsung's UDC driver + * available at: + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git + * + * State machine bugfixes: + * Marek Szyprowski + * + * Ported to u-boot: + * Marek Szyprowski + * Lukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +static u8 clear_feature_num; +int clear_feature_flag; + +/* Bulk-Only Mass Storage Reset (class-specific request) */ +#define GET_MAX_LUN_REQUEST 0xFE +#define BOT_RESET_REQUEST 0xFF + +static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev) +{ + u32 ep_ctrl; + + writel(usb_ctrl_dma_addr, ®->in_endp[EP0_CON].diepdma); + writel(DIEPT_SIZ_PKT_CNT(1), ®->in_endp[EP0_CON].dieptsiz); + + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + ®->in_endp[EP0_CON].diepctl); + + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + dev->ep0state = WAIT_FOR_IN_COMPLETE; +} + +void s3c_udc_pre_setup(void) +{ + u32 ep_ctrl; + + debug_cond(DEBUG_IN_EP, + "%s : Prepare Setup packets.\n", __func__); + + writel(DOEPT_SIZ_PKT_CNT(1) | sizeof(struct usb_ctrlrequest), + ®->out_endp[EP0_CON].doeptsiz); + writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); + + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); + writel(ep_ctrl|DEPCTL_EPENA, ®->out_endp[EP0_CON].doepctl); + + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + __func__, readl(®->out_endp[EP0_CON].doepctl)); + +} + +static inline void s3c_ep0_complete_out(void) +{ + u32 ep_ctrl; + + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + __func__, readl(®->out_endp[EP0_CON].doepctl)); + + debug_cond(DEBUG_IN_EP, + "%s : Prepare Complete Out packet.\n", __func__); + + writel(DOEPT_SIZ_PKT_CNT(1) | sizeof(struct usb_ctrlrequest), + ®->out_endp[EP0_CON].doeptsiz); + writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); + + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + ®->out_endp[EP0_CON].doepctl); + + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + __func__, readl(®->out_endp[EP0_CON].doepctl)); + +} + + +static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 *buf, ctrl; + u32 length, pktcnt; + u32 ep_num = ep_index(ep); + + buf = req->req.buf + req->req.actual; + length = min(req->req.length - req->req.actual, + ep_num ? DMA_BUFFER_SIZE : ep->ep.maxpacket); + + ep->len = length; + ep->dma_buf = buf; + + if (ep_num == EP0_CON || length == 0) + pktcnt = 1; + else + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + + ctrl = readl(®->out_endp[ep_num].doepctl); + + writel((unsigned int) ep->dma_buf, ®->out_endp[ep_num].doepdma); + writel(DOEPT_SIZ_PKT_CNT(pktcnt) | DOEPT_SIZ_XFER_SIZE(length), + ®->out_endp[ep_num].doeptsiz); + writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->out_endp[ep_num].doepctl); + + debug_cond(DEBUG_OUT_EP != 0, + "%s: EP%d RX DMA start : DOEPDMA = 0x%x," + "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n" + "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", + __func__, ep_num, + readl(®->out_endp[ep_num].doepdma), + readl(®->out_endp[ep_num].doeptsiz), + readl(®->out_endp[ep_num].doepctl), + buf, pktcnt, length); + return 0; + +} + +int setdma_tx(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 *buf, ctrl = 0; + u32 length, pktcnt; + u32 ep_num = ep_index(ep); + + buf = req->req.buf + req->req.actual; + length = req->req.length - req->req.actual; + + if (ep_num == EP0_CON) + length = min(length, (u32)ep_maxpacket(ep)); + + ep->len = length; + ep->dma_buf = buf; + + flush_dcache_range((unsigned long) ep->dma_buf, + (unsigned long) ep->dma_buf + + ROUND(ep->len, CONFIG_SYS_CACHELINE_SIZE)); + + if (length == 0) + pktcnt = 1; + else + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + + /* Flush the endpoint's Tx FIFO */ + writel(TX_FIFO_NUMBER(ep->fifo_num), ®->grstctl); + writel(TX_FIFO_NUMBER(ep->fifo_num) | TX_FIFO_FLUSH, ®->grstctl); + while (readl(®->grstctl) & TX_FIFO_FLUSH) + ; + + writel((unsigned long) ep->dma_buf, ®->in_endp[ep_num].diepdma); + writel(DIEPT_SIZ_PKT_CNT(pktcnt) | DIEPT_SIZ_XFER_SIZE(length), + ®->in_endp[ep_num].dieptsiz); + + ctrl = readl(®->in_endp[ep_num].diepctl); + + /* Write the FIFO number to be used for this endpoint */ + ctrl &= DIEPCTL_TX_FIFO_NUM_MASK; + ctrl |= DIEPCTL_TX_FIFO_NUM(ep->fifo_num); + + /* Clear reserved (Next EP) bits */ + ctrl = (ctrl&~(EP_MASK<in_endp[ep_num].diepctl); + + debug_cond(DEBUG_IN_EP, + "%s:EP%d TX DMA start : DIEPDMA0 = 0x%x," + "DIEPTSIZ0 = 0x%x, DIEPCTL0 = 0x%x\n" + "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", + __func__, ep_num, + readl(®->in_endp[ep_num].diepdma), + readl(®->in_endp[ep_num].dieptsiz), + readl(®->in_endp[ep_num].diepctl), + buf, pktcnt, length); + + return length; +} + +static void complete_rx(struct s3c_udc *dev, u8 ep_num) +{ + struct s3c_ep *ep = &dev->ep[ep_num]; + struct s3c_request *req = NULL; + u32 ep_tsr = 0, xfer_size = 0, is_short = 0; + + if (list_empty(&ep->queue)) { + debug_cond(DEBUG_OUT_EP != 0, + "%s: RX DMA done : NULL REQ on OUT EP-%d\n", + __func__, ep_num); + return; + + } + + req = list_entry(ep->queue.next, struct s3c_request, queue); + ep_tsr = readl(®->out_endp[ep_num].doeptsiz); + + if (ep_num == EP0_CON) + xfer_size = (ep_tsr & DOEPT_SIZ_XFER_SIZE_MAX_EP0); + else + xfer_size = (ep_tsr & DOEPT_SIZ_XFER_SIZE_MAX_EP); + + xfer_size = ep->len - xfer_size; + + /* + * NOTE: + * + * Please be careful with proper buffer allocation for USB request, + * which needs to be aligned to CONFIG_SYS_CACHELINE_SIZE, not only + * with starting address, but also its size shall be a cache line + * multiplication. + * + * This will prevent from corruption of data allocated immediatelly + * before or after the buffer. + * + * For armv7, the cache_v7.c provides proper code to emit "ERROR" + * message to warn users. + */ + invalidate_dcache_range((unsigned long) ep->dma_buf, + (unsigned long) ep->dma_buf + + ROUND(xfer_size, CONFIG_SYS_CACHELINE_SIZE)); + + req->req.actual += min(xfer_size, req->req.length - req->req.actual); + is_short = (xfer_size < ep->ep.maxpacket); + + debug_cond(DEBUG_OUT_EP != 0, + "%s: RX DMA done : ep = %d, rx bytes = %d/%d, " + "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n", + __func__, ep_num, req->req.actual, req->req.length, + is_short, ep_tsr, xfer_size); + + if (is_short || req->req.actual == req->req.length) { + if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) { + debug_cond(DEBUG_OUT_EP != 0, " => Send ZLP\n"); + s3c_udc_ep0_zlp(dev); + /* packet will be completed in complete_tx() */ + dev->ep0state = WAIT_FOR_IN_COMPLETE; + } else { + done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct s3c_request, queue); + debug_cond(DEBUG_OUT_EP != 0, + "%s: Next Rx request start...\n", + __func__); + setdma_rx(ep, req); + } + } + } else + setdma_rx(ep, req); +} + +static void complete_tx(struct s3c_udc *dev, u8 ep_num) +{ + struct s3c_ep *ep = &dev->ep[ep_num]; + struct s3c_request *req; + u32 ep_tsr = 0, xfer_size = 0, is_short = 0; + u32 last; + + if (dev->ep0state == WAIT_FOR_NULL_COMPLETE) { + dev->ep0state = WAIT_FOR_OUT_COMPLETE; + s3c_ep0_complete_out(); + return; + } + + if (list_empty(&ep->queue)) { + debug_cond(DEBUG_IN_EP, + "%s: TX DMA done : NULL REQ on IN EP-%d\n", + __func__, ep_num); + return; + + } + + req = list_entry(ep->queue.next, struct s3c_request, queue); + + ep_tsr = readl(®->in_endp[ep_num].dieptsiz); + + xfer_size = ep->len; + is_short = (xfer_size < ep->ep.maxpacket); + req->req.actual += min(xfer_size, req->req.length - req->req.actual); + + debug_cond(DEBUG_IN_EP, + "%s: TX DMA done : ep = %d, tx bytes = %d/%d, " + "is_short = %d, DIEPTSIZ = 0x%x, remained bytes = %d\n", + __func__, ep_num, req->req.actual, req->req.length, + is_short, ep_tsr, xfer_size); + + if (ep_num == 0) { + if (dev->ep0state == DATA_STATE_XMIT) { + debug_cond(DEBUG_IN_EP, + "%s: ep_num = %d, ep0stat ==" + "DATA_STATE_XMIT\n", + __func__, ep_num); + last = write_fifo_ep0(ep, req); + if (last) + dev->ep0state = WAIT_FOR_COMPLETE; + } else if (dev->ep0state == WAIT_FOR_IN_COMPLETE) { + debug_cond(DEBUG_IN_EP, + "%s: ep_num = %d, completing request\n", + __func__, ep_num); + done(ep, req, 0); + dev->ep0state = WAIT_FOR_SETUP; + } else if (dev->ep0state == WAIT_FOR_COMPLETE) { + debug_cond(DEBUG_IN_EP, + "%s: ep_num = %d, completing request\n", + __func__, ep_num); + done(ep, req, 0); + dev->ep0state = WAIT_FOR_OUT_COMPLETE; + s3c_ep0_complete_out(); + } else { + debug_cond(DEBUG_IN_EP, + "%s: ep_num = %d, invalid ep state\n", + __func__, ep_num); + } + return; + } + + if (req->req.actual == req->req.length) + done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + debug_cond(DEBUG_IN_EP, + "%s: Next Tx request start...\n", __func__); + setdma_tx(ep, req); + } +} + +static inline void s3c_udc_check_tx_queue(struct s3c_udc *dev, u8 ep_num) +{ + struct s3c_ep *ep = &dev->ep[ep_num]; + struct s3c_request *req; + + debug_cond(DEBUG_IN_EP, + "%s: Check queue, ep_num = %d\n", __func__, ep_num); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + debug_cond(DEBUG_IN_EP, + "%s: Next Tx request(0x%p) start...\n", + __func__, req); + + if (ep_is_in(ep)) + setdma_tx(ep, req); + else + setdma_rx(ep, req); + } else { + debug_cond(DEBUG_IN_EP, + "%s: NULL REQ on IN EP-%d\n", __func__, ep_num); + + return; + } + +} + +static void process_ep_in_intr(struct s3c_udc *dev) +{ + u32 ep_intr, ep_intr_status; + u8 ep_num = 0; + + ep_intr = readl(®->daint); + debug_cond(DEBUG_IN_EP, + "*** %s: EP In interrupt : DAINT = 0x%x\n", __func__, ep_intr); + + ep_intr &= DAINT_MASK; + + while (ep_intr) { + if (ep_intr & DAINT_IN_EP_INT(1)) { + ep_intr_status = readl(®->in_endp[ep_num].diepint); + debug_cond(DEBUG_IN_EP, + "\tEP%d-IN : DIEPINT = 0x%x\n", + ep_num, ep_intr_status); + + /* Interrupt Clear */ + writel(ep_intr_status, ®->in_endp[ep_num].diepint); + + if (ep_intr_status & TRANSFER_DONE) { + complete_tx(dev, ep_num); + + if (ep_num == 0) { + if (dev->ep0state == + WAIT_FOR_IN_COMPLETE) + dev->ep0state = WAIT_FOR_SETUP; + + if (dev->ep0state == WAIT_FOR_SETUP) + s3c_udc_pre_setup(); + + /* continue transfer after + set_clear_halt for DMA mode */ + if (clear_feature_flag == 1) { + s3c_udc_check_tx_queue(dev, + clear_feature_num); + clear_feature_flag = 0; + } + } + } + } + ep_num++; + ep_intr >>= 1; + } +} + +static void process_ep_out_intr(struct s3c_udc *dev) +{ + u32 ep_intr, ep_intr_status; + u8 ep_num = 0; + + ep_intr = readl(®->daint); + debug_cond(DEBUG_OUT_EP != 0, + "*** %s: EP OUT interrupt : DAINT = 0x%x\n", + __func__, ep_intr); + + ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK; + + while (ep_intr) { + if (ep_intr & 0x1) { + ep_intr_status = readl(®->out_endp[ep_num].doepint); + debug_cond(DEBUG_OUT_EP != 0, + "\tEP%d-OUT : DOEPINT = 0x%x\n", + ep_num, ep_intr_status); + + /* Interrupt Clear */ + writel(ep_intr_status, ®->out_endp[ep_num].doepint); + + if (ep_num == 0) { + if (ep_intr_status & TRANSFER_DONE) { + if (dev->ep0state != + WAIT_FOR_OUT_COMPLETE) + complete_rx(dev, ep_num); + else { + dev->ep0state = WAIT_FOR_SETUP; + s3c_udc_pre_setup(); + } + } + + if (ep_intr_status & + CTRL_OUT_EP_SETUP_PHASE_DONE) { + debug_cond(DEBUG_OUT_EP != 0, + "SETUP packet arrived\n"); + s3c_handle_ep0(dev); + } + } else { + if (ep_intr_status & TRANSFER_DONE) + complete_rx(dev, ep_num); + } + } + ep_num++; + ep_intr >>= 1; + } +} + +/* + * usb client interrupt handler. + */ +static int s3c_udc_irq(int irq, void *_dev) +{ + struct s3c_udc *dev = _dev; + u32 intr_status; + u32 usb_status, gintmsk; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + intr_status = readl(®->gintsts); + gintmsk = readl(®->gintmsk); + + debug_cond(DEBUG_ISR, + "\n*** %s : GINTSTS=0x%x(on state %s), GINTMSK : 0x%x," + "DAINT : 0x%x, DAINTMSK : 0x%x\n", + __func__, intr_status, state_names[dev->ep0state], gintmsk, + readl(®->daint), readl(®->daintmsk)); + + if (!intr_status) { + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_HANDLED; + } + + if (intr_status & INT_ENUMDONE) { + debug_cond(DEBUG_ISR, "\tSpeed Detection interrupt\n"); + + writel(INT_ENUMDONE, ®->gintsts); + usb_status = (readl(®->dsts) & 0x6); + + if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) { + debug_cond(DEBUG_ISR, + "\t\tFull Speed Detection\n"); + set_max_pktsize(dev, USB_SPEED_FULL); + + } else { + debug_cond(DEBUG_ISR, + "\t\tHigh Speed Detection : 0x%x\n", + usb_status); + set_max_pktsize(dev, USB_SPEED_HIGH); + } + } + + if (intr_status & INT_EARLY_SUSPEND) { + debug_cond(DEBUG_ISR, "\tEarly suspend interrupt\n"); + writel(INT_EARLY_SUSPEND, ®->gintsts); + } + + if (intr_status & INT_SUSPEND) { + usb_status = readl(®->dsts); + debug_cond(DEBUG_ISR, + "\tSuspend interrupt :(DSTS):0x%x\n", usb_status); + writel(INT_SUSPEND, ®->gintsts); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver) { + if (dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + + /* HACK to let gadget detect disconnected state */ + if (dev->driver->disconnect) { + spin_unlock_irqrestore(&dev->lock, flags); + dev->driver->disconnect(&dev->gadget); + spin_lock_irqsave(&dev->lock, flags); + } + } + } + + if (intr_status & INT_RESUME) { + debug_cond(DEBUG_ISR, "\tResume interrupt\n"); + writel(INT_RESUME, ®->gintsts); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume) { + + dev->driver->resume(&dev->gadget); + } + } + + if (intr_status & INT_RESET) { + usb_status = readl(®->gotgctl); + debug_cond(DEBUG_ISR, + "\tReset interrupt - (GOTGCTL):0x%x\n", usb_status); + writel(INT_RESET, ®->gintsts); + + if ((usb_status & 0xc0000) == (0x3 << 18)) { + if (reset_available) { + debug_cond(DEBUG_ISR, + "\t\tOTG core got reset (%d)!!\n", + reset_available); + reconfig_usbd(); + dev->ep0state = WAIT_FOR_SETUP; + reset_available = 0; + s3c_udc_pre_setup(); + } else + reset_available = 1; + + } else { + reset_available = 1; + debug_cond(DEBUG_ISR, + "\t\tRESET handling skipped\n"); + } + } + + if (intr_status & INT_IN_EP) + process_ep_in_intr(dev); + + if (intr_status & INT_OUT_EP) + process_ep_out_intr(dev); + + spin_unlock_irqrestore(&dev->lock, flags); + + return IRQ_HANDLED; +} + +/** Queue one request + * Kickstart transfer if needed + */ +static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct s3c_request *req; + struct s3c_ep *ep; + struct s3c_udc *dev; + unsigned long flags; + u32 ep_num, gintsts; + + req = container_of(_req, struct s3c_request, req); + if (unlikely(!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue))) { + + debug("%s: bad params\n", __func__); + return -EINVAL; + } + + ep = container_of(_ep, struct s3c_ep, ep); + + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + + debug("%s: bad ep: %s, %d, %p\n", __func__, + ep->ep.name, !ep->desc, _ep); + return -EINVAL; + } + + ep_num = ep_index(ep); + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + + debug("%s: bogus device state %p\n", __func__, dev->driver); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + debug("\n*** %s: %s-%s req = %p, len = %d, buf = %p" + "Q empty = %d, stopped = %d\n", + __func__, _ep->name, ep_is_in(ep) ? "in" : "out", + _req, _req->length, _req->buf, + list_empty(&ep->queue), ep->stopped); + +#ifdef DEBUG + { + int i, len = _req->length; + + printf("pkt = "); + if (len > 64) + len = 64; + for (i = 0; i < len; i++) { + printf("%02x", ((u8 *)_req->buf)[i]); + if ((i & 7) == 7) + printf(" "); + } + printf("\n"); + } +#endif + + if (list_empty(&ep->queue) && !ep->stopped) { + + if (ep_num == 0) { + /* EP0 */ + list_add_tail(&req->queue, &ep->queue); + s3c_ep0_kick(dev, ep); + req = 0; + + } else if (ep_is_in(ep)) { + gintsts = readl(®->gintsts); + debug_cond(DEBUG_IN_EP, + "%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n", + __func__, gintsts); + + setdma_tx(ep, req); + } else { + gintsts = readl(®->gintsts); + debug_cond(DEBUG_OUT_EP != 0, + "%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n", + __func__, gintsts); + + setdma_rx(ep, req); + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != 0)) + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/****************************************************************/ +/* End Point 0 related functions */ +/****************************************************************/ + +/* return: 0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 max; + unsigned count; + int is_last; + + max = ep_maxpacket(ep); + + debug_cond(DEBUG_EP0 != 0, "%s: max = %d\n", __func__, max); + + count = setdma_tx(ep, req); + + /* last packet is usually short (or a zlp) */ + if (likely(count != max)) + is_last = 1; + else { + if (likely(req->req.length != req->req.actual + count) + || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + debug_cond(DEBUG_EP0 != 0, + "%s: wrote %s %d bytes%s %d left %p\n", __func__, + ep->ep.name, count, + is_last ? "/L" : "", + req->req.length - req->req.actual - count, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + ep->dev->ep0state = WAIT_FOR_SETUP; + return 1; + } + + return 0; +} + +int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max) +{ + invalidate_dcache_range((unsigned long)cp, (unsigned long)cp + + ROUND(max, CONFIG_SYS_CACHELINE_SIZE)); + + debug_cond(DEBUG_EP0 != 0, + "%s: bytes=%d, ep_index=%d 0x%p\n", __func__, + max, ep_index(ep), cp); + + return max; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function + * after it decodes a set address setup packet. + */ +static void udc_set_address(struct s3c_udc *dev, unsigned char address) +{ + u32 ctrl = readl(®->dcfg); + writel(DEVICE_ADDRESS(address) | ctrl, ®->dcfg); + + s3c_udc_ep0_zlp(dev); + + debug_cond(DEBUG_EP0 != 0, + "%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n", + __func__, address, readl(®->dcfg)); + + dev->usb_address = address; +} + +static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep) +{ + struct s3c_udc *dev; + u32 ep_ctrl = 0; + + dev = ep->dev; + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + + /* set the disable and stall bits */ + if (ep_ctrl & DEPCTL_EPENA) + ep_ctrl |= DEPCTL_EPDIS; + + ep_ctrl |= DEPCTL_STALL; + + writel(ep_ctrl, ®->in_endp[EP0_CON].diepctl); + + debug_cond(DEBUG_EP0 != 0, + "%s: set ep%d stall, DIEPCTL0 = 0x%p\n", + __func__, ep_index(ep), ®->in_endp[EP0_CON].diepctl); + /* + * The application can only set this bit, and the core clears it, + * when a SETUP token is received for this endpoint + */ + dev->ep0state = WAIT_FOR_SETUP; + + s3c_udc_pre_setup(); +} + +static void s3c_ep0_read(struct s3c_udc *dev) +{ + struct s3c_request *req; + struct s3c_ep *ep = &dev->ep[0]; + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + + } else { + debug("%s: ---> BUG\n", __func__); + BUG(); + return; + } + + debug_cond(DEBUG_EP0 != 0, + "%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", + __func__, req, req->req.length, req->req.actual); + + if (req->req.length == 0) { + /* zlp for Set_configuration, Set_interface, + * or Bulk-Only mass storge reset */ + + ep->len = 0; + s3c_udc_ep0_zlp(dev); + + debug_cond(DEBUG_EP0 != 0, + "%s: req.length = 0, bRequest = %d\n", + __func__, usb_ctrl->bRequest); + return; + } + + setdma_rx(ep, req); +} + +/* + * DATA_STATE_XMIT + */ +static int s3c_ep0_write(struct s3c_udc *dev) +{ + struct s3c_request *req; + struct s3c_ep *ep = &dev->ep[0]; + int ret, need_zlp = 0; + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct s3c_request, queue); + + if (!req) { + debug_cond(DEBUG_EP0 != 0, "%s: NULL REQ\n", __func__); + return 0; + } + + debug_cond(DEBUG_EP0 != 0, + "%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", + __func__, req, req->req.length, req->req.actual); + + if (req->req.length - req->req.actual == ep0_fifo_size) { + /* Next write will end with the packet size, */ + /* so we need Zero-length-packet */ + need_zlp = 1; + } + + ret = write_fifo_ep0(ep, req); + + if ((ret == 1) && !need_zlp) { + /* Last packet */ + dev->ep0state = WAIT_FOR_COMPLETE; + debug_cond(DEBUG_EP0 != 0, + "%s: finished, waiting for status\n", __func__); + + } else { + dev->ep0state = DATA_STATE_XMIT; + debug_cond(DEBUG_EP0 != 0, + "%s: not finished\n", __func__); + } + + return 1; +} + +int s3c_udc_get_status(struct s3c_udc *dev, + struct usb_ctrlrequest *crq) +{ + u8 ep_num = crq->wIndex & 0x7F; + u16 g_status = 0; + u32 ep_ctrl; + + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_GET_STATUS\n", __func__); + printf("crq->brequest:0x%x\n", crq->bRequestType & USB_RECIP_MASK); + switch (crq->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + g_status = 0; + debug_cond(DEBUG_SETUP != 0, + "\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n", + g_status); + break; + + case USB_RECIP_DEVICE: + g_status = 0x1; /* Self powered */ + debug_cond(DEBUG_SETUP != 0, + "\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n", + g_status); + break; + + case USB_RECIP_ENDPOINT: + if (crq->wLength > 2) { + debug_cond(DEBUG_SETUP != 0, + "\tGET_STATUS:Not support EP or wLength\n"); + return 1; + } + + g_status = dev->ep[ep_num].stopped; + debug_cond(DEBUG_SETUP != 0, + "\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n", + g_status); + + break; + + default: + return 1; + } + + memcpy(usb_ctrl, &g_status, sizeof(g_status)); + + flush_dcache_range((unsigned long) usb_ctrl, + (unsigned long) usb_ctrl + + ROUND(sizeof(g_status), CONFIG_SYS_CACHELINE_SIZE)); + + writel(usb_ctrl_dma_addr, ®->in_endp[EP0_CON].diepdma); + writel(DIEPT_SIZ_PKT_CNT(1) | DIEPT_SIZ_XFER_SIZE(2), + ®->in_endp[EP0_CON].dieptsiz); + + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + ®->in_endp[EP0_CON].diepctl); + dev->ep0state = WAIT_FOR_NULL_COMPLETE; + + return 0; +} + +static void s3c_udc_set_nak(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0; + + ep_num = ep_index(ep); + debug("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + ep_ctrl |= DEPCTL_SNAK; + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + debug("%s: set NAK, DIEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + ep_ctrl |= DEPCTL_SNAK; + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + debug("%s: set NAK, DOEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); + } + + return; +} + + +void s3c_udc_ep_set_stall(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0; + + ep_num = ep_index(ep); + debug("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + + /* set the disable and stall bits */ + if (ep_ctrl & DEPCTL_EPENA) + ep_ctrl |= DEPCTL_EPDIS; + + ep_ctrl |= DEPCTL_STALL; + + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + debug("%s: set stall, DIEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + + /* set the stall bit */ + ep_ctrl |= DEPCTL_STALL; + + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + debug("%s: set stall, DOEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); + } + + return; +} + +void s3c_udc_ep_clear_stall(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0; + + ep_num = ep_index(ep); + debug("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + + /* clear stall bit */ + ep_ctrl &= ~DEPCTL_STALL; + + /* + * USB Spec 9.4.5: For endpoints using data toggle, regardless + * of whether an endpoint has the Halt feature set, a + * ClearFeature(ENDPOINT_HALT) request always results in the + * data toggle being reinitialized to DATA0. + */ + if (ep->bmAttributes == USB_ENDPOINT_XFER_INT + || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ + } + + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + debug("%s: cleared stall, DIEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + + /* clear stall bit */ + ep_ctrl &= ~DEPCTL_STALL; + + if (ep->bmAttributes == USB_ENDPOINT_XFER_INT + || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ + } + + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + debug("%s: cleared stall, DOEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); + } + + return; +} + +static int s3c_udc_set_halt(struct usb_ep *_ep, int value) +{ + struct s3c_ep *ep; + struct s3c_udc *dev; + unsigned long flags; + u8 ep_num; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + + if (unlikely(!_ep || !ep->desc || ep_num == EP0_CON || + ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)) { + debug("%s: %s bad ep or descriptor\n", __func__, ep->ep.name); + return -EINVAL; + } + + /* Attempt to halt IN ep will fail if any transfer requests + * are still queue */ + if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { + debug("%s: %s queue not empty, req = %p\n", + __func__, ep->ep.name, + list_entry(ep->queue.next, struct s3c_request, queue)); + + return -EAGAIN; + } + + dev = ep->dev; + debug("%s: ep_num = %d, value = %d\n", __func__, ep_num, value); + + spin_lock_irqsave(&dev->lock, flags); + + if (value == 0) { + ep->stopped = 0; + s3c_udc_ep_clear_stall(ep); + } else { + if (ep_num == 0) + dev->ep0state = WAIT_FOR_SETUP; + + ep->stopped = 1; + s3c_udc_ep_set_stall(ep); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +void s3c_udc_ep_activate(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0, daintmsk = 0; + + ep_num = ep_index(ep); + + /* Read DEPCTLn register */ + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + daintmsk = 1 << ep_num; + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + daintmsk = (1 << ep_num) << DAINT_OUT_BIT; + } + + debug("%s: EPCTRL%d = 0x%x, ep_is_in = %d\n", + __func__, ep_num, ep_ctrl, ep_is_in(ep)); + + /* If the EP is already active don't change the EP Control + * register. */ + if (!(ep_ctrl & DEPCTL_USBACTEP)) { + ep_ctrl = (ep_ctrl & ~DEPCTL_TYPE_MASK) | + (ep->bmAttributes << DEPCTL_TYPE_BIT); + ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) | + (ep->ep.maxpacket << DEPCTL_MPS_BIT); + ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP | DEPCTL_SNAK); + + if (ep_is_in(ep)) { + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + debug("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n", + __func__, ep_num, ep_num, + readl(®->in_endp[ep_num].diepctl)); + } else { + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + debug("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n", + __func__, ep_num, ep_num, + readl(®->out_endp[ep_num].doepctl)); + } + } + + /* Unmask EP Interrtupt */ + writel(readl(®->daintmsk)|daintmsk, ®->daintmsk); + debug("%s: DAINTMSK = 0x%x\n", __func__, readl(®->daintmsk)); + +} + +static int s3c_udc_clear_feature(struct usb_ep *_ep) +{ + struct s3c_udc *dev; + struct s3c_ep *ep; + u8 ep_num; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + + dev = ep->dev; + debug_cond(DEBUG_SETUP != 0, + "%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n", + __func__, ep_num, ep_is_in(ep), clear_feature_flag); + + if (usb_ctrl->wLength != 0) { + debug_cond(DEBUG_SETUP != 0, + "\tCLEAR_FEATURE: wLength is not zero.....\n"); + return 1; + } + + switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (usb_ctrl->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + debug_cond(DEBUG_SETUP != 0, + "\tOFF:USB_DEVICE_REMOTE_WAKEUP\n"); + break; + + case USB_DEVICE_TEST_MODE: + debug_cond(DEBUG_SETUP != 0, + "\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n"); + /** @todo Add CLEAR_FEATURE for TEST modes. */ + break; + } + + s3c_udc_ep0_zlp(dev); + break; + + case USB_RECIP_ENDPOINT: + debug_cond(DEBUG_SETUP != 0, + "\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n", + usb_ctrl->wValue); + + if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { + if (ep_num == 0) { + s3c_udc_ep0_set_stall(ep); + return 0; + } + + s3c_udc_ep0_zlp(dev); + + s3c_udc_ep_clear_stall(ep); + s3c_udc_ep_activate(ep); + ep->stopped = 0; + + clear_feature_num = ep_num; + clear_feature_flag = 1; + } + break; + } + + return 0; +} + +static int s3c_udc_set_feature(struct usb_ep *_ep) +{ + struct s3c_udc *dev; + struct s3c_ep *ep; + u8 ep_num; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + dev = ep->dev; + + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n", + __func__, ep_num); + + if (usb_ctrl->wLength != 0) { + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE: wLength is not zero.....\n"); + return 1; + } + + switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (usb_ctrl->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n"); + break; + case USB_DEVICE_B_HNP_ENABLE: + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); + break; + + case USB_DEVICE_A_HNP_SUPPORT: + /* RH port supports HNP */ + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n"); + break; + + case USB_DEVICE_A_ALT_HNP_SUPPORT: + /* other RH port does */ + debug_cond(DEBUG_SETUP != 0, + "\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); + break; + } + + s3c_udc_ep0_zlp(dev); + return 0; + + case USB_RECIP_INTERFACE: + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE: USB_RECIP_INTERFACE\n"); + break; + + case USB_RECIP_ENDPOINT: + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE: USB_RECIP_ENDPOINT\n"); + if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { + if (ep_num == 0) { + s3c_udc_ep0_set_stall(ep); + return 0; + } + ep->stopped = 1; + s3c_udc_ep_set_stall(ep); + } + + s3c_udc_ep0_zlp(dev); + return 0; + } + + return 1; +} + +/* + * WAIT_FOR_SETUP (OUT_PKT_RDY) + */ +void s3c_ep0_setup(struct s3c_udc *dev) +{ + struct s3c_ep *ep = &dev->ep[0]; + int i; + u8 ep_num; + + /* Nuke all previous transfers */ + nuke(ep, -EPROTO); + + /* read control req from fifo (8 bytes) */ + s3c_fifo_read(ep, (u32 *)usb_ctrl, 8); + + debug_cond(DEBUG_SETUP != 0, + "%s: bRequestType = 0x%x(%s), bRequest = 0x%x" + "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n", + __func__, usb_ctrl->bRequestType, + (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", + usb_ctrl->bRequest, + usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex); + +#ifdef DEBUG + { + int i, len = sizeof(*usb_ctrl); + char *p = (char *)usb_ctrl; + + printf("pkt = "); + for (i = 0; i < len; i++) { + printf("%02x", ((u8 *)p)[i]); + if ((i & 7) == 7) + printf(" "); + } + printf("\n"); + } +#endif + + if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST && + usb_ctrl->wLength != 1) { + debug_cond(DEBUG_SETUP != 0, + "\t%s:GET_MAX_LUN_REQUEST:invalid", + __func__); + debug_cond(DEBUG_SETUP != 0, + "wLength = %d, setup returned\n", + usb_ctrl->wLength); + + s3c_udc_ep0_set_stall(ep); + dev->ep0state = WAIT_FOR_SETUP; + + return; + } else if (usb_ctrl->bRequest == BOT_RESET_REQUEST && + usb_ctrl->wLength != 0) { + /* Bulk-Only *mass storge reset of class-specific request */ + debug_cond(DEBUG_SETUP != 0, + "%s:BOT Rest:invalid wLength =%d, setup returned\n", + __func__, usb_ctrl->wLength); + + s3c_udc_ep0_set_stall(ep); + dev->ep0state = WAIT_FOR_SETUP; + + return; + } + + /* Set direction of EP0 */ + if (likely(usb_ctrl->bRequestType & USB_DIR_IN)) { + ep->bEndpointAddress |= USB_DIR_IN; + } else { + ep->bEndpointAddress &= ~USB_DIR_IN; + } + /* cope with automagic for some standard requests. */ + dev->req_std = (usb_ctrl->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + + dev->req_pending = 1; + + /* Handle some SETUP packets ourselves */ + if (dev->req_std) { + switch (usb_ctrl->bRequest) { + case USB_REQ_SET_ADDRESS: + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_SET_ADDRESS (%d)\n", + __func__, usb_ctrl->wValue); + if (usb_ctrl->bRequestType + != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + udc_set_address(dev, usb_ctrl->wValue); + return; + + case USB_REQ_SET_CONFIGURATION: + debug_cond(DEBUG_SETUP != 0, + "=====================================\n"); + debug_cond(DEBUG_SETUP != 0, + "%s: USB_REQ_SET_CONFIGURATION (%d)\n", + __func__, usb_ctrl->wValue); + + if (usb_ctrl->bRequestType == USB_RECIP_DEVICE) + reset_available = 1; + + break; + + case USB_REQ_GET_DESCRIPTOR: + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_GET_DESCRIPTOR\n", + __func__); + break; + + case USB_REQ_SET_INTERFACE: + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_SET_INTERFACE (%d)\n", + __func__, usb_ctrl->wValue); + + if (usb_ctrl->bRequestType == USB_RECIP_INTERFACE) + reset_available = 1; + + break; + + case USB_REQ_GET_CONFIGURATION: + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_GET_CONFIGURATION\n", + __func__); + break; + + case USB_REQ_GET_STATUS: + if (!s3c_udc_get_status(dev, usb_ctrl)) + return; + + break; + + case USB_REQ_CLEAR_FEATURE: + ep_num = usb_ctrl->wIndex & 0x7f; + + if (!s3c_udc_clear_feature(&dev->ep[ep_num].ep)) + return; + + break; + + case USB_REQ_SET_FEATURE: + ep_num = usb_ctrl->wIndex & 0x7f; + + if (!s3c_udc_set_feature(&dev->ep[ep_num].ep)) + return; + + break; + + default: + debug_cond(DEBUG_SETUP != 0, + "%s: *** Default of usb_ctrl->bRequest=0x%x" + "happened.\n", __func__, usb_ctrl->bRequest); + break; + } + } + + + if (likely(dev->driver)) { + /* device-2-host (IN) or no data setup command, + * process immediately */ + debug_cond(DEBUG_SETUP != 0, + "%s:usb_ctrlreq will be passed to fsg_setup()\n", + __func__); + + spin_unlock(&dev->lock); + i = dev->driver->setup(&dev->gadget, usb_ctrl); + spin_lock(&dev->lock); + + if (i < 0) { + /* setup processing failed, force stall */ + s3c_udc_ep0_set_stall(ep); + dev->ep0state = WAIT_FOR_SETUP; + + debug_cond(DEBUG_SETUP != 0, + "\tdev->driver->setup failed (%d)," + " bRequest = %d\n", + i, usb_ctrl->bRequest); + + + } else if (dev->req_pending) { + dev->req_pending = 0; + debug_cond(DEBUG_SETUP != 0, + "\tdev->req_pending...\n"); + } + + debug_cond(DEBUG_SETUP != 0, + "\tep0state = %s\n", state_names[dev->ep0state]); + + } +} + +/* + * handle ep0 interrupt + */ +static void s3c_handle_ep0(struct s3c_udc *dev) +{ + if (dev->ep0state == WAIT_FOR_SETUP) { + debug_cond(DEBUG_OUT_EP != 0, + "%s: WAIT_FOR_SETUP\n", __func__); + s3c_ep0_setup(dev); + + } else { + debug_cond(DEBUG_OUT_EP != 0, + "%s: strange state!!(state = %s)\n", + __func__, state_names[dev->ep0state]); + } +} + +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep) +{ + debug_cond(DEBUG_EP0 != 0, + "%s: ep_is_in = %d\n", __func__, ep_is_in(ep)); + if (ep_is_in(ep)) { + dev->ep0state = DATA_STATE_XMIT; + s3c_ep0_write(dev); + + } else { + dev->ep0state = DATA_STATE_RECV; + s3c_ep0_read(dev); + } +} diff --git a/qemu/roms/u-boot/drivers/usb/gadget/storage_common.c b/qemu/roms/u-boot/drivers/usb/gadget/storage_common.c new file mode 100644 index 000000000..74300746b --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/storage_common.c @@ -0,0 +1,624 @@ +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * Ported to u-boot: + * Andrzej Pietrasiewicz + * + * Code refactoring & cleanup: + * Łukasz Majewski + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +/* + * This file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + * - fsg_string_manufacturer -- name of the manufacturer + * - fsg_string_product -- name of the product + * - fsg_string_serial -- product's serial + * - fsg_string_config -- name of the configuration + * - fsg_string_interface -- name of the interface + * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS + * macro is defined prior to including this file. + */ + +/* + * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and + * fsg_hs_intr_in_desc objects as well as + * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES + * macros are not defined. + * + * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER, + * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not + * defined (as well as corresponding entries in string tables are + * missing) and FSG_STRING_INTERFACE has value of zero. + * + * When FSG_NO_OTG is defined fsg_otg_desc won't be defined. + */ + +/* + * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included + * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN + * characters rather then a pointer to void. + */ + + +/* #include */ + + +/* + * Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ +#define FSG_VENDOR_ID 0x0525 /* NetChip */ +#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ + +/*-------------------------------------------------------------------------*/ + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VLDBG LDBG +#else +#define VLDBG(lun, fmt, args...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +/* +#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args) +#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) +#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) +#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) +*/ + +#define LDBG(lun, fmt, args...) do { } while (0) +#define LERROR(lun, fmt, args...) do { } while (0) +#define LWARN(lun, fmt, args...) do { } while (0) +#define LINFO(lun, fmt, args...) do { } while (0) + +/* + * Keep those macros in sync with those in + * include/linux/usb/composite.h or else GCC will complain. If they + * are identical (the same names of arguments, white spaces in the + * same places) GCC will allow redefinition otherwise (even if some + * white space is removed or added) warning will be issued. + * + * Those macros are needed here because File Storage Gadget does not + * include the composite.h header. For composite gadgets those macros + * are redundant since composite.h is included any way. + * + * One could check whether those macros are already defined (which + * would indicate composite.h had been included) or not (which would + * indicate we were in FSG) but this is not done because a warning is + * desired if definitions here differ from the ones in composite.h. + * + * We want the definitions to match and be the same in File Storage + * Gadget as well as Mass Storage Function (and so composite gadgets + * using MSF). If someone changes them in composite.h it will produce + * a warning in this file when building MSF. + */ + +#define DBG(d, fmt, args...) debug(fmt , ## args) +#define VDBG(d, fmt, args...) debug(fmt , ## args) +/* #define ERROR(d, fmt, args...) printf(fmt , ## args) */ +/* #define WARNING(d, fmt, args...) printf(fmt , ## args) */ +/* #define INFO(d, fmt, args...) printf(fmt , ## args) */ + +/* #define DBG(d, fmt, args...) do { } while (0) */ +/* #define VDBG(d, fmt, args...) do { } while (0) */ +#define ERROR(d, fmt, args...) do { } while (0) +#define WARNING(d, fmt, args...) do { } while (0) +#define INFO(d, fmt, args...) do { } while (0) + +#ifdef DUMP_MSGS + +/* dump_msg(fsg, const char * label, const u8 * buf, unsigned length); */ +# define dump_msg(fsg, label, buf, length) do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ + 16, 1, buf, length, 0); \ + } \ +} while (0) + +# define dump_cdb(fsg) do { } while (0) + +#else + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +# define dump_cdb(fsg) \ + print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +# else + +# define dump_cdb(fsg) do { } while (0) + +# endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + +/*-------------------------------------------------------------------------*/ + +/* SCSI device types */ +#define TYPE_DISK 0x00 +#define TYPE_CDROM 0x05 + +/* USB protocol value = the transport method */ +#define USB_PR_CBI 0x00 /* Control/Bulk/Interrupt */ +#define USB_PR_CB 0x01 /* Control/Bulk w/o interrupt */ +#define USB_PR_BULK 0x50 /* Bulk-only */ + +/* USB subclass value = the protocol encapsulation */ +#define USB_SC_RBC 0x01 /* Reduced Block Commands (flash) */ +#define USB_SC_8020 0x02 /* SFF-8020i, MMC-2, ATAPI (CD-ROM) */ +#define USB_SC_QIC 0x03 /* QIC-157 (tape) */ +#define USB_SC_UFI 0x04 /* UFI (floppy) */ +#define USB_SC_8070 0x05 /* SFF-8070i (removable) */ +#define USB_SC_SCSI 0x06 /* Transparent SCSI */ + +/* Bulk-only data structures */ + +/* Command Block Wrapper */ +struct fsg_bulk_cb_wrap { + __le32 Signature; /* Contains 'USBC' */ + u32 Tag; /* Unique per command id */ + __le32 DataTransferLength; /* Size of the data */ + u8 Flags; /* Direction in bit 7 */ + u8 Lun; /* LUN (normally 0) */ + u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */ + u8 CDB[16]; /* Command Data Block */ +}; + +#define USB_BULK_CB_WRAP_LEN 31 +#define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */ +#define USB_BULK_IN_FLAG 0x80 + +/* Command Status Wrapper */ +struct bulk_cs_wrap { + __le32 Signature; /* Should = 'USBS' */ + u32 Tag; /* Same as original command */ + __le32 Residue; /* Amount not transferred */ + u8 Status; /* See below */ +}; + +#define USB_BULK_CS_WRAP_LEN 13 +#define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */ +#define USB_STATUS_PASS 0 +#define USB_STATUS_FAIL 1 +#define USB_STATUS_PHASE_ERROR 2 + +/* Bulk-only class specific requests */ +#define USB_BULK_RESET_REQUEST 0xff +#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe + +/* CBI Interrupt data structure */ +struct interrupt_data { + u8 bType; + u8 bValue; +}; + +#define CBI_INTERRUPT_DATA_LEN 2 + +/* CBI Accept Device-Specific Command request */ +#define USB_CBI_ADSC_REQUEST 0x00 + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 + +/* SCSI commands that we recognize */ +#define SC_FORMAT_UNIT 0x04 +#define SC_INQUIRY 0x12 +#define SC_MODE_SELECT_6 0x15 +#define SC_MODE_SELECT_10 0x55 +#define SC_MODE_SENSE_6 0x1a +#define SC_MODE_SENSE_10 0x5a +#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SC_READ_6 0x08 +#define SC_READ_10 0x28 +#define SC_READ_12 0xa8 +#define SC_READ_CAPACITY 0x25 +#define SC_READ_FORMAT_CAPACITIES 0x23 +#define SC_READ_HEADER 0x44 +#define SC_READ_TOC 0x43 +#define SC_RELEASE 0x17 +#define SC_REQUEST_SENSE 0x03 +#define SC_RESERVE 0x16 +#define SC_SEND_DIAGNOSTIC 0x1d +#define SC_START_STOP_UNIT 0x1b +#define SC_SYNCHRONIZE_CACHE 0x35 +#define SC_TEST_UNIT_READY 0x00 +#define SC_VERIFY 0x2f +#define SC_WRITE_6 0x0a +#define SC_WRITE_10 0x2a +#define SC_WRITE_12 0xaa + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + +struct device_attribute { int i; }; +struct rw_semaphore { int i; }; +#define down_write(...) do { } while (0) +#define up_write(...) do { } while (0) +#define down_read(...) do { } while (0) +#define up_read(...) do { } while (0) +#define ETOOSMALL 525 + +#include + +/*-------------------------------------------------------------------------*/ + +struct fsg_lun { + loff_t file_length; + loff_t num_sectors; + + unsigned int initially_ro:1; + unsigned int ro:1; + unsigned int removable:1; + unsigned int cdrom:1; + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; + unsigned int nofua:1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + struct device dev; +}; + +#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL) +#if 0 +static struct fsg_lun *fsg_lun_from_dev(struct device *dev) +{ + return container_of(dev, struct fsg_lun, dev); +} +#endif + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE 256 +#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ + +/* Number of buffers we will use. 2 is enough for double-buffering */ +#ifndef CONFIG_CI_UDC +#define FSG_NUM_BUFFERS 2 +#else +#define FSG_NUM_BUFFERS 1 /* ci_udc only allows 1 req per ep at present */ +#endif + +/* Default size of buffer length. */ +#define FSG_BUFLEN ((u32)16384) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS 8 + +enum fsg_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +struct fsg_buffhd { +#ifdef FSG_BUFFHD_STATIC_BUFFER + char buf[FSG_BUFLEN]; +#else + void *buf; +#endif + enum fsg_buffer_state state; + struct fsg_buffhd *next; + + /* + * The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. + */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + int inreq_busy; + struct usb_request *outreq; + int outreq_busy; +}; + +enum fsg_state { + /* This one isn't used anywhere */ + FSG_STATE_COMMAND_PHASE = -10, + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + + FSG_STATE_IDLE = 0, + FSG_STATE_ABORT_BULK_OUT, + FSG_STATE_RESET, + FSG_STATE_INTERFACE_CHANGE, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, + FSG_STATE_EXIT, + FSG_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + +/*-------------------------------------------------------------------------*/ + +static inline u32 get_unaligned_be24(u8 *buf) +{ + return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + +/*-------------------------------------------------------------------------*/ + +enum { +#ifndef FSG_NO_DEVICE_STRINGS + FSG_STRING_MANUFACTURER = 1, + FSG_STRING_PRODUCT, + FSG_STRING_SERIAL, + FSG_STRING_CONFIG, +#endif + FSG_STRING_INTERFACE +}; + +#ifndef FSG_NO_OTG +static struct usb_otg_descriptor +fsg_otg_desc = { + .bLength = sizeof fsg_otg_desc, + .bDescriptorType = USB_DT_OTG, + + .bmAttributes = USB_OTG_SRP, +}; +#endif + +/* There is only one interface. */ + +static struct usb_interface_descriptor +fsg_intf_desc = { + .bLength = sizeof fsg_intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ + .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ + .iInterface = FSG_STRING_INTERFACE, +}; + +/* + * Three full-speed endpoint descriptors: bulk-in, bulk-out, and + * interrupt-in. + */ + +static struct usb_endpoint_descriptor +fsg_fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +static struct usb_endpoint_descriptor +fsg_fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_fs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 32, /* frames -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_fs_function[] = { +#ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, +#endif + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP + (struct usb_descriptor_header *) &fsg_fs_intr_in_desc, +#endif + NULL, +}; + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the configuration descriptor. + */ +static struct usb_endpoint_descriptor +fsg_hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor +fsg_hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 1, /* NAK every 1 uframe */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_hs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_hs_function[] = { +#ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, +#endif + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP + (struct usb_descriptor_header *) &fsg_hs_intr_in_desc, +#endif + NULL, +}; + +/* Maxpacket and other transfer characteristics vary by speed. */ +static struct usb_endpoint_descriptor * +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string fsg_strings[] = { +#ifndef FSG_NO_DEVICE_STRINGS + {FSG_STRING_MANUFACTURER, fsg_string_manufacturer}, + {FSG_STRING_PRODUCT, fsg_string_product}, + {FSG_STRING_SERIAL, fsg_string_serial}, + {FSG_STRING_CONFIG, fsg_string_config}, +#endif + {FSG_STRING_INTERFACE, fsg_string_interface}, + {} +}; + +static struct usb_gadget_strings fsg_stringtab = { + .language = 0x0409, /* en-us */ + .strings = fsg_strings, +}; + +/*-------------------------------------------------------------------------*/ + +/* + * If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. + */ + +static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) +{ + int ro; + + /* R/W if we can, R/O if we must */ + ro = curlun->initially_ro; + + curlun->ro = ro; + curlun->file_length = ums->num_sectors << 9; + curlun->num_sectors = ums->num_sectors; + debug("open backing file: %s\n", filename); + + return 0; +} + +static void fsg_lun_close(struct fsg_lun *curlun) +{ +} + +/*-------------------------------------------------------------------------*/ + +/* + * Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). + */ +static int fsg_lun_fsync_sub(struct fsg_lun *curlun) +{ + return 0; +} + +static void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_unaligned_be32(addr, dest); + } +} + +/*-------------------------------------------------------------------------*/ diff --git a/qemu/roms/u-boot/drivers/usb/gadget/usbstring.c b/qemu/roms/u-boot/drivers/usb/gadget/usbstring.c new file mode 100644 index 000000000..8c3ff64fe --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/gadget/usbstring.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2003 David Brownell + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Ported to U-boot by: Thomas Smits and + * Remy Bohmer + */ + +#include +#include +#include +#include + +#include + + +static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len) +{ + int count = 0; + u8 c; + u16 uchar; + + /* + * this insists on correct encodings, though not minimal ones. + * BUT it currently rejects legit 4-byte UTF-8 code points, + * which need surrogate pairs. (Unicode 3.1 can use them.) + */ + while (len != 0 && (c = (u8) *s++) != 0) { + if ((c & 0x80)) { + /* + * 2-byte sequence: + * 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx + */ + if ((c & 0xe0) == 0xc0) { + uchar = (c & 0x1f) << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0x80) + goto fail; + c &= 0x3f; + uchar |= c; + + /* + * 3-byte sequence (most CJKV characters): + * zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx + */ + } else if ((c & 0xf0) == 0xe0) { + uchar = (c & 0x0f) << 12; + + c = (u8) *s++; + if ((c & 0xc0) != 0x80) + goto fail; + c &= 0x3f; + uchar |= c << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0x80) + goto fail; + c &= 0x3f; + uchar |= c; + + /* no bogus surrogates */ + if (0xd800 <= uchar && uchar <= 0xdfff) + goto fail; + + /* + * 4-byte sequence (surrogate pairs, currently rare): + * 11101110wwwwzzzzyy + 110111yyyyxxxxxx + * = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx + * (uuuuu = wwww + 1) + * FIXME accept the surrogate code points (only) + */ + } else + goto fail; + } else + uchar = c; + put_unaligned_le16(uchar, cp++); + count++; + len--; + } + return count; +fail: + return -1; +} + + +/** + * usb_gadget_get_string - fill out a string descriptor + * @table: of c strings encoded using UTF-8 + * @id: string id, from low byte of wValue in get string descriptor + * @buf: at least 256 bytes + * + * Finds the UTF-8 string matching the ID, and converts it into a + * string descriptor in utf16-le. + * Returns length of descriptor (always even) or negative errno + * + * If your driver needs stings in multiple languages, you'll probably + * "switch (wIndex) { ... }" in your ep0 string descriptor logic, + * using this routine after choosing which set of UTF-8 strings to use. + * Note that US-ASCII is a strict subset of UTF-8; any string bytes with + * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 + * characters (which are also widely used in C strings). + */ +int +usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf) +{ + struct usb_string *s; + int len; + + if (!table) + return -EINVAL; + + /* descriptor 0 has the language id */ + if (id == 0) { + buf[0] = 4; + buf[1] = USB_DT_STRING; + buf[2] = (u8) table->language; + buf[3] = (u8) (table->language >> 8); + return 4; + } + for (s = table->strings; s && s->s; s++) + if (s->id == id) + break; + + /* unrecognized: stall. */ + if (!s || !s->s) + return -EINVAL; + + /* string descriptors have length, tag, then UTF16-LE text */ + len = min((size_t) 126, strlen(s->s)); + memset(buf + 2, 0, 2 * len); /* zero all the bytes */ + len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len); + if (len < 0) + return -EINVAL; + buf[0] = (len + 1) * 2; + buf[1] = USB_DT_STRING; + return buf[0]; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/Makefile b/qemu/roms/u-boot/drivers/usb/host/Makefile new file mode 100644 index 000000000..b301e2825 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/Makefile @@ -0,0 +1,44 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +# ohci +obj-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o +obj-$(CONFIG_USB_ATMEL) += ohci-at91.o +obj-$(CONFIG_USB_OHCI_DA8XX) += ohci-da8xx.o +obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o +obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o +obj-$(CONFIG_USB_SL811HS) += sl811-hcd.o +obj-$(CONFIG_USB_OHCI_S3C24XX) += ohci-s3c24xx.o + +# echi +obj-$(CONFIG_USB_EHCI) += ehci-hcd.o +obj-$(CONFIG_USB_EHCI_ARMADA100) += ehci-armada100.o utmi-armada100.o +obj-$(CONFIG_USB_EHCI_ATMEL) += ehci-atmel.o +ifdef CONFIG_MPC512X +obj-$(CONFIG_USB_EHCI_FSL) += ehci-mpc512x.o +else +obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o +endif +obj-$(CONFIG_USB_EHCI_FARADAY) += ehci-faraday.o +obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o +obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o +obj-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o +obj-$(CONFIG_USB_EHCI_MX5) += ehci-mx5.o +obj-$(CONFIG_USB_EHCI_MX6) += ehci-mx6.o +obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o +obj-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o +obj-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o +obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o +obj-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o +obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o +obj-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o +obj-$(CONFIG_USB_EHCI_RMOBILE) += ehci-rmobile.o + +# xhci +obj-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o +obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o +obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-armada100.c b/qemu/roms/u-boot/drivers/usb/host/ehci-armada100.c new file mode 100644 index 000000000..012eb3a1a --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-armada100.c @@ -0,0 +1,48 @@ +/* + * (C) Copyright 2012 + * eInfochips Ltd. + * Written-by: Ajay Bhargav + * + * This driver is based on Kirkwood echi driver + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "ehci.h" +#include +#include +#include + +/* + * EHCI host controller init + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + if (utmi_init() < 0) + return -1; + + *hccr = (struct ehci_hccr *)(ARMD1_USB_HOST_BASE + 0x100); + *hcor = (struct ehci_hcor *)((uint32_t) *hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + debug("armada100-ehci: init hccr %x and hcor %x hc_length %d\n", + (uint32_t)*hccr, (uint32_t)*hcor, + (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + return 0; +} + +/* + * EHCI host controller stop + */ +int ehci_hcd_stop(int index) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-atmel.c b/qemu/roms/u-boot/drivers/usb/host/ehci-atmel.c new file mode 100644 index 000000000..9ffe5010b --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-atmel.c @@ -0,0 +1,73 @@ +/* + * (C) Copyright 2012 + * Atmel Semiconductor + * Written-by: Bo Shen + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +/* Enable UTMI PLL time out 500us + * 10 times as datasheet specified + */ +#define EN_UPLL_TIMEOUT 500UL + +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC; + ulong start_time, tmp_time; + + start_time = get_timer(0); + /* Enable UTMI PLL */ + writel(AT91_PMC_UPLLEN | AT91_PMC_BIASEN, &pmc->uckr); + while ((readl(&pmc->sr) & AT91_PMC_LOCKU) != AT91_PMC_LOCKU) { + WATCHDOG_RESET(); + tmp_time = get_timer(0); + if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) { + printf("ERROR: failed to enable UPLL\n"); + return -1; + } + } + + /* Enable USB Host clock */ + writel(1 << ATMEL_ID_UHPHS, &pmc->pcer); + + *hccr = (struct ehci_hccr *)ATMEL_BASE_EHCI; + *hcor = (struct ehci_hcor *)((uint32_t)*hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + return 0; +} + +int ehci_hcd_stop(int index) +{ + at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC; + ulong start_time, tmp_time; + + /* Disable USB Host Clock */ + writel(1 << ATMEL_ID_UHPHS, &pmc->pcdr); + + start_time = get_timer(0); + /* Disable UTMI PLL */ + writel(readl(&pmc->uckr) & ~AT91_PMC_UPLLEN, &pmc->uckr); + while ((readl(&pmc->sr) & AT91_PMC_LOCKU) == AT91_PMC_LOCKU) { + WATCHDOG_RESET(); + tmp_time = get_timer(0); + if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) { + printf("ERROR: failed to stop UPLL\n"); + return -1; + } + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-exynos.c b/qemu/roms/u-boot/drivers/usb/host/ehci-exynos.c new file mode 100644 index 000000000..edd91a84a --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-exynos.c @@ -0,0 +1,231 @@ +/* + * SAMSUNG EXYNOS USB HOST EHCI Controller + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Vivek Gautam + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ehci.h" + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +/** + * Contains pointers to register base addresses + * for the usb controller. + */ +struct exynos_ehci { + struct exynos_usb_phy *usb; + struct ehci_hccr *hcd; + struct fdt_gpio_state vbus_gpio; +}; + +static struct exynos_ehci exynos; + +#ifdef CONFIG_OF_CONTROL +static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos) +{ + fdt_addr_t addr; + unsigned int node; + int depth; + + node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_EHCI); + if (node <= 0) { + debug("EHCI: Can't get device node for ehci\n"); + return -ENODEV; + } + + /* + * Get the base address for EHCI controller from the device node + */ + addr = fdtdec_get_addr(blob, node, "reg"); + if (addr == FDT_ADDR_T_NONE) { + debug("Can't get the EHCI register address\n"); + return -ENXIO; + } + + exynos->hcd = (struct ehci_hccr *)addr; + + /* Vbus gpio */ + fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio); + + depth = 0; + node = fdtdec_next_compatible_subnode(blob, node, + COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth); + if (node <= 0) { + debug("EHCI: Can't get device node for usb-phy controller\n"); + return -ENODEV; + } + + /* + * Get the base address for usbphy from the device node + */ + exynos->usb = (struct exynos_usb_phy *)fdtdec_get_addr(blob, node, + "reg"); + if (exynos->usb == NULL) { + debug("Can't get the usbphy register address\n"); + return -ENXIO; + } + + return 0; +} +#endif + +/* Setup the EHCI host controller. */ +static void setup_usb_phy(struct exynos_usb_phy *usb) +{ + u32 hsic_ctrl; + + set_usbhost_mode(USB20_PHY_CFG_HOST_LINK_EN); + + set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_EN); + + clrbits_le32(&usb->usbphyctrl0, + HOST_CTRL0_FSEL_MASK | + HOST_CTRL0_COMMONON_N | + /* HOST Phy setting */ + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + + setbits_le32(&usb->usbphyctrl0, + /* Setting up the ref freq */ + (CLK_24MHZ << 16) | + /* HOST Phy setting */ + HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + udelay(10); + clrbits_le32(&usb->usbphyctrl0, + HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + + /* HSIC Phy Setting */ + hsic_ctrl = (HSIC_CTRL_FORCESUSPEND | + HSIC_CTRL_FORCESLEEP | + HSIC_CTRL_SIDDQ); + + clrbits_le32(&usb->hsicphyctrl1, hsic_ctrl); + clrbits_le32(&usb->hsicphyctrl2, hsic_ctrl); + + hsic_ctrl = (((HSIC_CTRL_REFCLKDIV_12 & HSIC_CTRL_REFCLKDIV_MASK) + << HSIC_CTRL_REFCLKDIV_SHIFT) + | ((HSIC_CTRL_REFCLKSEL & HSIC_CTRL_REFCLKSEL_MASK) + << HSIC_CTRL_REFCLKSEL_SHIFT) + | HSIC_CTRL_UTMISWRST); + + setbits_le32(&usb->hsicphyctrl1, hsic_ctrl); + setbits_le32(&usb->hsicphyctrl2, hsic_ctrl); + + udelay(10); + + clrbits_le32(&usb->hsicphyctrl1, HSIC_CTRL_PHYSWRST | + HSIC_CTRL_UTMISWRST); + + clrbits_le32(&usb->hsicphyctrl2, HSIC_CTRL_PHYSWRST | + HSIC_CTRL_UTMISWRST); + + udelay(20); + + /* EHCI Ctrl setting */ + setbits_le32(&usb->ehcictrl, + EHCICTRL_ENAINCRXALIGN | + EHCICTRL_ENAINCR4 | + EHCICTRL_ENAINCR8 | + EHCICTRL_ENAINCR16); +} + +/* Reset the EHCI host controller. */ +static void reset_usb_phy(struct exynos_usb_phy *usb) +{ + u32 hsic_ctrl; + + /* HOST_PHY reset */ + setbits_le32(&usb->usbphyctrl0, + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + + /* HSIC Phy reset */ + hsic_ctrl = (HSIC_CTRL_FORCESUSPEND | + HSIC_CTRL_FORCESLEEP | + HSIC_CTRL_SIDDQ | + HSIC_CTRL_PHYSWRST); + + setbits_le32(&usb->hsicphyctrl1, hsic_ctrl); + setbits_le32(&usb->hsicphyctrl2, hsic_ctrl); + + set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE); +} + +/* + * EHCI-initialization + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + struct exynos_ehci *ctx = &exynos; + +#ifdef CONFIG_OF_CONTROL + if (exynos_usb_parse_dt(gd->fdt_blob, ctx)) { + debug("Unable to parse device tree for ehci-exynos\n"); + return -ENODEV; + } +#else + ctx->usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy(); + ctx->hcd = (struct ehci_hccr *)samsung_get_base_usb_ehci(); +#endif + +#ifdef CONFIG_OF_CONTROL + /* setup the Vbus gpio here */ + if (fdt_gpio_isvalid(&ctx->vbus_gpio) && + !fdtdec_setup_gpio(&ctx->vbus_gpio)) + gpio_direction_output(ctx->vbus_gpio.gpio, 1); +#endif + + setup_usb_phy(ctx->usb); + + board_usb_init(index, init); + + *hccr = ctx->hcd; + *hcor = (struct ehci_hcor *)((uint32_t) *hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + debug("Exynos5-ehci: init hccr %x and hcor %x hc_length %d\n", + (uint32_t)*hccr, (uint32_t)*hcor, + (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + struct exynos_ehci *ctx = &exynos; + + reset_usb_phy(ctx->usb); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-faraday.c b/qemu/roms/u-boot/drivers/usb/host/ehci-faraday.c new file mode 100644 index 000000000..3b761bc32 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-faraday.c @@ -0,0 +1,147 @@ +/* + * Faraday USB 2.0 EHCI Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include "ehci.h" + +#ifndef CONFIG_USB_EHCI_BASE_LIST +#define CONFIG_USB_EHCI_BASE_LIST { CONFIG_USB_EHCI_BASE } +#endif + +union ehci_faraday_regs { + struct fusbh200_regs usb; + struct fotg210_regs otg; +}; + +static inline int ehci_is_fotg2xx(union ehci_faraday_regs *regs) +{ + return !readl(®s->usb.easstr); +} + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **ret_hccr, struct ehci_hcor **ret_hcor) +{ + struct ehci_hccr *hccr; + struct ehci_hcor *hcor; + union ehci_faraday_regs *regs; + uint32_t base_list[] = CONFIG_USB_EHCI_BASE_LIST; + + if (index < 0 || index >= ARRAY_SIZE(base_list)) + return -1; + regs = (void __iomem *)base_list[index]; + hccr = (struct ehci_hccr *)®s->usb.hccr; + hcor = (struct ehci_hcor *)®s->usb.hcor; + + if (ehci_is_fotg2xx(regs)) { + /* A-device bus reset */ + /* ... Power off A-device */ + setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP); + /* ... Drop vbus and bus traffic */ + clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ); + mdelay(1); + /* ... Power on A-device */ + clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP); + /* ... Drive vbus and bus traffic */ + setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ); + mdelay(1); + /* Disable OTG & DEV interrupts, triggered at level-high */ + writel(IMR_IRQLH | IMR_OTG | IMR_DEV, ®s->otg.imr); + /* Clear all interrupt status */ + writel(ISR_HOST | ISR_OTG | ISR_DEV, ®s->otg.isr); + } else { + /* Interrupt=level-high */ + setbits_le32(®s->usb.bmcsr, BMCSR_IRQLH); + /* VBUS on */ + clrbits_le32(®s->usb.bmcsr, BMCSR_VBUS_OFF); + /* Disable all interrupts */ + writel(0x00, ®s->usb.bmier); + writel(0x1f, ®s->usb.bmisr); + } + + *ret_hccr = hccr; + *ret_hcor = hcor; + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +} + +/* + * This ehci_set_usbmode() overrides the weak function + * in "ehci-hcd.c". + */ +void ehci_set_usbmode(int index) +{ + /* nothing needs to be done */ +} + +/* + * This ehci_get_port_speed() overrides the weak function + * in "ehci-hcd.c". + */ +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ + int spd, ret = PORTSC_PSPD_HS; + union ehci_faraday_regs *regs = (void __iomem *)((ulong)hcor - 0x10); + + if (ehci_is_fotg2xx(regs)) + spd = OTGCSR_SPD(readl(®s->otg.otgcsr)); + else + spd = BMCSR_SPD(readl(®s->usb.bmcsr)); + + switch (spd) { + case 0: /* full speed */ + ret = PORTSC_PSPD_FS; + break; + case 1: /* low speed */ + ret = PORTSC_PSPD_LS; + break; + case 2: /* high speed */ + ret = PORTSC_PSPD_HS; + break; + default: + printf("ehci-faraday: invalid device speed\n"); + break; + } + + return ret; +} + +/* + * This ehci_get_portsc_register() overrides the weak function + * in "ehci-hcd.c". + */ +uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +{ + /* Faraday EHCI has one and only one portsc register */ + if (port) { + /* Printing the message would cause a scan failure! */ + debug("The request port(%d) is not configured\n", port); + return NULL; + } + + /* Faraday EHCI PORTSC register offset is 0x20 from hcor */ + return (uint32_t *)((uint8_t *)hcor + 0x20); +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-fsl.c b/qemu/roms/u-boot/drivers/usb/host/ehci-fsl.c new file mode 100644 index 000000000..6cb4d9866 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-fsl.c @@ -0,0 +1,156 @@ +/* + * (C) Copyright 2009, 2011 Freescale Semiconductor, Inc. + * + * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB + * + * Author: Tor Krill tor@excito.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +static void set_txfifothresh(struct usb_ehci *, u32); + +/* Check USB PHY clock valid */ +static int usb_phy_clk_valid(struct usb_ehci *ehci) +{ + if (!((in_be32(&ehci->control) & PHY_CLK_VALID) || + in_be32(&ehci->prictrl))) { + printf("USB PHY clock invalid!\n"); + return 0; + } else { + return 1; + } +} + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + * + * Excerpts from linux ehci fsl driver. + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + struct usb_ehci *ehci = NULL; + const char *phy_type = NULL; + size_t len; + char current_usb_controller[5]; +#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY + char usb_phy[5]; + + usb_phy[0] = '\0'; +#endif + if (has_erratum_a007075()) { + /* + * A 5ms delay is needed after applying soft-reset to the + * controller to let external ULPI phy come out of reset. + * This delay needs to be added before re-initializing + * the controller after soft-resetting completes + */ + mdelay(5); + } + memset(current_usb_controller, '\0', 5); + snprintf(current_usb_controller, 4, "usb%d", index+1); + + switch (index) { + case 0: + ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR; + break; + case 1: + ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB2_ADDR; + break; + default: + printf("ERROR: wrong controller index!!\n"); + break; + }; + + *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); + *hcor = (struct ehci_hcor *)((uint32_t) *hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + /* Set to Host mode */ + setbits_le32(&ehci->usbmode, CM_HOST); + + out_be32(&ehci->snoop1, SNOOP_SIZE_2GB); + out_be32(&ehci->snoop2, 0x80000000 | SNOOP_SIZE_2GB); + + /* Init phy */ + if (hwconfig_sub(current_usb_controller, "phy_type")) + phy_type = hwconfig_subarg(current_usb_controller, + "phy_type", &len); + else + phy_type = getenv("usb_phy_type"); + + if (!phy_type) { +#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY + /* if none specified assume internal UTMI */ + strcpy(usb_phy, "utmi"); + phy_type = usb_phy; +#else + printf("WARNING: USB phy type not defined !!\n"); + return -1; +#endif + } + + if (!strncmp(phy_type, "utmi", 4)) { +#if defined(CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY) + setbits_be32(&ehci->control, PHY_CLK_SEL_UTMI); + setbits_be32(&ehci->control, UTMI_PHY_EN); + udelay(1000); /* delay required for PHY Clk to appear */ +#endif + out_le32(&(*hcor)->or_portsc[0], PORT_PTS_UTMI); + setbits_be32(&ehci->control, USB_EN); + } else { + setbits_be32(&ehci->control, PHY_CLK_SEL_ULPI); + clrsetbits_be32(&ehci->control, UTMI_PHY_EN, USB_EN); + udelay(1000); /* delay required for PHY Clk to appear */ + if (!usb_phy_clk_valid(ehci)) + return -EINVAL; + out_le32(&(*hcor)->or_portsc[0], PORT_PTS_ULPI); + } + + out_be32(&ehci->prictrl, 0x0000000c); + out_be32(&ehci->age_cnt_limit, 0x00000040); + out_be32(&ehci->sictrl, 0x00000001); + + in_le32(&ehci->usbmode); + + if (SVR_SOC_VER(get_svr()) == SVR_T4240 && + IS_SVR_REV(get_svr(), 2, 0)) + set_txfifothresh(ehci, TXFIFOTHRESH); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +} + +/* + * Setting the value of TXFIFO_THRESH field in TXFILLTUNING register + * to counter DDR latencies in writing data into Tx buffer. + * This prevents Tx buffer from getting underrun + */ +static void set_txfifothresh(struct usb_ehci *ehci, u32 txfifo_thresh) +{ + u32 cmd; + cmd = ehci_readl(&ehci->txfilltuning); + cmd &= ~TXFIFO_THRESH_MASK; + cmd |= TXFIFO_THRESH(txfifo_thresh); + ehci_writel(&ehci->txfilltuning, cmd); +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-hcd.c b/qemu/roms/u-boot/drivers/usb/host/ehci-hcd.c new file mode 100644 index 000000000..eaf59134c --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-hcd.c @@ -0,0 +1,1404 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Excito Elektronik i Skåne AB + * Copyright (c) 2008, Michael Trimarchi + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + +/* + * EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt. + * Let's time out after 8 to have a little safety margin on top of that. + */ +#define HCHALT_TIMEOUT (8 * 1000) + +static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; + +#define ALIGN_END_ADDR(type, ptr, size) \ + ((uint32_t)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN)) + +static struct descriptor { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_linux_config_descriptor config; + struct usb_linux_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; +} __attribute__ ((packed)) descriptor = { + { + 0x8, /* bDescLength */ + 0x29, /* bDescriptorType: hub descriptor */ + 2, /* bNrPorts -- runtime modified */ + 0, /* wHubCharacteristics */ + 10, /* bPwrOn2PwrGood */ + 0, /* bHubCntrCurrent */ + {}, /* Device removable */ + {} /* at most 7 ports! XXX */ + }, + { + 0x12, /* bLength */ + 1, /* bDescriptorType: UDESC_DEVICE */ + cpu_to_le16(0x0200), /* bcdUSB: v2.0 */ + 9, /* bDeviceClass: UDCLASS_HUB */ + 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ + 1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */ + 64, /* bMaxPacketSize: 64 bytes */ + 0x0000, /* idVendor */ + 0x0000, /* idProduct */ + cpu_to_le16(0x0100), /* bcdDevice */ + 1, /* iManufacturer */ + 2, /* iProduct */ + 0, /* iSerialNumber */ + 1 /* bNumConfigurations: 1 */ + }, + { + 0x9, + 2, /* bDescriptorType: UDESC_CONFIG */ + cpu_to_le16(0x19), + 1, /* bNumInterface */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x40, /* bmAttributes: UC_SELF_POWER */ + 0 /* bMaxPower */ + }, + { + 0x9, /* bLength */ + 4, /* bDescriptorType: UDESC_INTERFACE */ + 0, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ + 1, /* bNumEndpoints */ + 9, /* bInterfaceClass: UICLASS_HUB */ + 0, /* bInterfaceSubClass: UISUBCLASS_HUB */ + 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ + 0 /* iInterface */ + }, + { + 0x7, /* bLength */ + 5, /* bDescriptorType: UDESC_ENDPOINT */ + 0x81, /* bEndpointAddress: + * UE_DIR_IN | EHCI_INTR_ENDPT + */ + 3, /* bmAttributes: UE_INTERRUPT */ + 8, /* wMaxPacketSize */ + 255 /* bInterval */ + }, +}; + +#if defined(CONFIG_EHCI_IS_TDI) +#define ehci_is_TDI() (1) +#else +#define ehci_is_TDI() (0) +#endif + +int __ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ + return PORTSC_PSPD(reg); +} + +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) + __attribute__((weak, alias("__ehci_get_port_speed"))); + +void __ehci_set_usbmode(int index) +{ + uint32_t tmp; + uint32_t *reg_ptr; + + reg_ptr = (uint32_t *)((u8 *)&ehcic[index].hcor->or_usbcmd + USBMODE); + tmp = ehci_readl(reg_ptr); + tmp |= USBMODE_CM_HC; +#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) + tmp |= USBMODE_BE; +#endif + ehci_writel(reg_ptr, tmp); +} + +void ehci_set_usbmode(int index) + __attribute__((weak, alias("__ehci_set_usbmode"))); + +void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +{ + mdelay(50); +} + +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) + __attribute__((weak, alias("__ehci_powerup_fixup"))); + +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) +{ + uint32_t result; + do { + result = ehci_readl(ptr); + udelay(5); + if (result == ~(uint32_t)0) + return -1; + result &= mask; + if (result == done) + return 0; + usec--; + } while (usec > 0); + return -1; +} + +static int ehci_reset(int index) +{ + uint32_t cmd; + int ret = 0; + + cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); + cmd = (cmd & ~CMD_RUN) | CMD_RESET; + ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd); + ret = handshake((uint32_t *)&ehcic[index].hcor->or_usbcmd, + CMD_RESET, 0, 250 * 1000); + if (ret < 0) { + printf("EHCI fail to reset\n"); + goto out; + } + + if (ehci_is_TDI()) + ehci_set_usbmode(index); + +#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH + cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning); + cmd &= ~TXFIFO_THRESH_MASK; + cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH); + ehci_writel(&ehcic[index].hcor->or_txfilltuning, cmd); +#endif +out: + return ret; +} + +static int ehci_shutdown(struct ehci_ctrl *ctrl) +{ + int i, ret = 0; + uint32_t cmd, reg; + + if (!ctrl || !ctrl->hcor) + return -EINVAL; + + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); + cmd &= ~(CMD_PSE | CMD_ASE); + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + ret = handshake(&ctrl->hcor->or_usbsts, STS_ASS | STS_PSS, 0, + 100 * 1000); + + if (!ret) { + for (i = 0; i < CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS; i++) { + reg = ehci_readl(&ctrl->hcor->or_portsc[i]); + reg |= EHCI_PS_SUSP; + ehci_writel(&ctrl->hcor->or_portsc[i], reg); + } + + cmd &= ~CMD_RUN; + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + ret = handshake(&ctrl->hcor->or_usbsts, STS_HALT, STS_HALT, + HCHALT_TIMEOUT); + } + + if (ret) + puts("EHCI failed to shut down host controller.\n"); + + return ret; +} + +static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) +{ + uint32_t delta, next; + uint32_t addr = (uint32_t)buf; + int idx; + + if (addr != ALIGN(addr, ARCH_DMA_MINALIGN)) + debug("EHCI-HCD: Misaligned buffer address (%p)\n", buf); + + flush_dcache_range(addr, ALIGN(addr + sz, ARCH_DMA_MINALIGN)); + + idx = 0; + while (idx < QT_BUFFER_CNT) { + td->qt_buffer[idx] = cpu_to_hc32(addr); + td->qt_buffer_hi[idx] = 0; + next = (addr + EHCI_PAGE_SIZE) & ~(EHCI_PAGE_SIZE - 1); + delta = next - addr; + if (delta >= sz) + break; + sz -= delta; + addr = next; + idx++; + } + + if (idx == QT_BUFFER_CNT) { + printf("out of buffer pointers (%u bytes left)\n", sz); + return -1; + } + + return 0; +} + +static inline u8 ehci_encode_speed(enum usb_device_speed speed) +{ + #define QH_HIGH_SPEED 2 + #define QH_FULL_SPEED 0 + #define QH_LOW_SPEED 1 + if (speed == USB_SPEED_HIGH) + return QH_HIGH_SPEED; + if (speed == USB_SPEED_LOW) + return QH_LOW_SPEED; + return QH_FULL_SPEED; +} + +static int +ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *req) +{ + ALLOC_ALIGN_BUFFER(struct QH, qh, 1, USB_DMA_MINALIGN); + struct qTD *qtd; + int qtd_count = 0; + int qtd_counter = 0; + volatile struct qTD *vtd; + unsigned long ts; + uint32_t *tdp; + uint32_t endpt, maxpacket, token, usbsts; + uint32_t c, toggle; + uint32_t cmd; + int timeout; + int ret = 0; + struct ehci_ctrl *ctrl = dev->controller; + + debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, + buffer, length, req); + if (req != NULL) + debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->value), + le16_to_cpu(req->index)); + +#define PKT_ALIGN 512 + /* + * The USB transfer is split into qTD transfers. Eeach qTD transfer is + * described by a transfer descriptor (the qTD). The qTDs form a linked + * list with a queue head (QH). + * + * Each qTD transfer starts with a new USB packet, i.e. a packet cannot + * have its beginning in a qTD transfer and its end in the following + * one, so the qTD transfer lengths have to be chosen accordingly. + * + * Each qTD transfer uses up to QT_BUFFER_CNT data buffers, mapped to + * single pages. The first data buffer can start at any offset within a + * page (not considering the cache-line alignment issues), while the + * following buffers must be page-aligned. There is no alignment + * constraint on the size of a qTD transfer. + */ + if (req != NULL) + /* 1 qTD will be needed for SETUP, and 1 for ACK. */ + qtd_count += 1 + 1; + if (length > 0 || req == NULL) { + /* + * Determine the qTD transfer size that will be used for the + * data payload (not considering the first qTD transfer, which + * may be longer or shorter, and the final one, which may be + * shorter). + * + * In order to keep each packet within a qTD transfer, the qTD + * transfer size is aligned to PKT_ALIGN, which is a multiple of + * wMaxPacketSize (except in some cases for interrupt transfers, + * see comment in submit_int_msg()). + * + * By default, i.e. if the input buffer is aligned to PKT_ALIGN, + * QT_BUFFER_CNT full pages will be used. + */ + int xfr_sz = QT_BUFFER_CNT; + /* + * However, if the input buffer is not aligned to PKT_ALIGN, the + * qTD transfer size will be one page shorter, and the first qTD + * data buffer of each transfer will be page-unaligned. + */ + if ((uint32_t)buffer & (PKT_ALIGN - 1)) + xfr_sz--; + /* Convert the qTD transfer size to bytes. */ + xfr_sz *= EHCI_PAGE_SIZE; + /* + * Approximate by excess the number of qTDs that will be + * required for the data payload. The exact formula is way more + * complicated and saves at most 2 qTDs, i.e. a total of 128 + * bytes. + */ + qtd_count += 2 + length / xfr_sz; + } +/* + * Threshold value based on the worst-case total size of the allocated qTDs for + * a mass-storage transfer of 65535 blocks of 512 bytes. + */ +#if CONFIG_SYS_MALLOC_LEN <= 64 + 128 * 1024 +#warning CONFIG_SYS_MALLOC_LEN may be too small for EHCI +#endif + qtd = memalign(USB_DMA_MINALIGN, qtd_count * sizeof(struct qTD)); + if (qtd == NULL) { + printf("unable to allocate TDs\n"); + return -1; + } + + memset(qh, 0, sizeof(struct QH)); + memset(qtd, 0, qtd_count * sizeof(*qtd)); + + toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + + /* + * Setup QH (3.6 in ehci-r10.pdf) + * + * qh_link ................. 03-00 H + * qh_endpt1 ............... 07-04 H + * qh_endpt2 ............... 0B-08 H + * - qh_curtd + * qh_overlay.qt_next ...... 13-10 H + * - qh_overlay.qt_altnext + */ + qh->qh_link = cpu_to_hc32((uint32_t)&ctrl->qh_list | QH_LINK_TYPE_QH); + c = (dev->speed != USB_SPEED_HIGH) && !usb_pipeendpoint(pipe); + maxpacket = usb_maxpacket(dev, pipe); + endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) | + QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) | + QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) | + QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) | + QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) | + QH_ENDPT1_DEVADDR(usb_pipedevice(pipe)); + qh->qh_endpt1 = cpu_to_hc32(endpt); + endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) | + QH_ENDPT2_HUBADDR(dev->parent->devnum) | + QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0); + qh->qh_endpt2 = cpu_to_hc32(endpt); + qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + + tdp = &qh->qh_overlay.qt_next; + + if (req != NULL) { + /* + * Setup request qTD (3.5 in ehci-r10.pdf) + * + * qt_next ................ 03-00 H + * qt_altnext ............. 07-04 H + * qt_token ............... 0B-08 H + * + * [ buffer, buffer_hi ] loaded with "req". + */ + qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = QT_TOKEN_DT(0) | QT_TOKEN_TOTALBYTES(sizeof(*req)) | + QT_TOKEN_IOC(0) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) | + QT_TOKEN_PID(QT_TOKEN_PID_SETUP) | + QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); + qtd[qtd_counter].qt_token = cpu_to_hc32(token); + if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req))) { + printf("unable to construct SETUP TD\n"); + goto fail; + } + /* Update previous qTD! */ + *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); + tdp = &qtd[qtd_counter++].qt_next; + toggle = 1; + } + + if (length > 0 || req == NULL) { + uint8_t *buf_ptr = buffer; + int left_length = length; + + do { + /* + * Determine the size of this qTD transfer. By default, + * QT_BUFFER_CNT full pages can be used. + */ + int xfr_bytes = QT_BUFFER_CNT * EHCI_PAGE_SIZE; + /* + * However, if the input buffer is not page-aligned, the + * portion of the first page before the buffer start + * offset within that page is unusable. + */ + xfr_bytes -= (uint32_t)buf_ptr & (EHCI_PAGE_SIZE - 1); + /* + * In order to keep each packet within a qTD transfer, + * align the qTD transfer size to PKT_ALIGN. + */ + xfr_bytes &= ~(PKT_ALIGN - 1); + /* + * This transfer may be shorter than the available qTD + * transfer size that has just been computed. + */ + xfr_bytes = min(xfr_bytes, left_length); + + /* + * Setup request qTD (3.5 in ehci-r10.pdf) + * + * qt_next ................ 03-00 H + * qt_altnext ............. 07-04 H + * qt_token ............... 0B-08 H + * + * [ buffer, buffer_hi ] loaded with "buffer". + */ + qtd[qtd_counter].qt_next = + cpu_to_hc32(QT_NEXT_TERMINATE); + qtd[qtd_counter].qt_altnext = + cpu_to_hc32(QT_NEXT_TERMINATE); + token = QT_TOKEN_DT(toggle) | + QT_TOKEN_TOTALBYTES(xfr_bytes) | + QT_TOKEN_IOC(req == NULL) | QT_TOKEN_CPAGE(0) | + QT_TOKEN_CERR(3) | + QT_TOKEN_PID(usb_pipein(pipe) ? + QT_TOKEN_PID_IN : QT_TOKEN_PID_OUT) | + QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); + qtd[qtd_counter].qt_token = cpu_to_hc32(token); + if (ehci_td_buffer(&qtd[qtd_counter], buf_ptr, + xfr_bytes)) { + printf("unable to construct DATA TD\n"); + goto fail; + } + /* Update previous qTD! */ + *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); + tdp = &qtd[qtd_counter++].qt_next; + /* + * Data toggle has to be adjusted since the qTD transfer + * size is not always an even multiple of + * wMaxPacketSize. + */ + if ((xfr_bytes / maxpacket) & 1) + toggle ^= 1; + buf_ptr += xfr_bytes; + left_length -= xfr_bytes; + } while (left_length > 0); + } + + if (req != NULL) { + /* + * Setup request qTD (3.5 in ehci-r10.pdf) + * + * qt_next ................ 03-00 H + * qt_altnext ............. 07-04 H + * qt_token ............... 0B-08 H + */ + qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = QT_TOKEN_DT(1) | QT_TOKEN_TOTALBYTES(0) | + QT_TOKEN_IOC(1) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) | + QT_TOKEN_PID(usb_pipein(pipe) ? + QT_TOKEN_PID_OUT : QT_TOKEN_PID_IN) | + QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); + qtd[qtd_counter].qt_token = cpu_to_hc32(token); + /* Update previous qTD! */ + *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); + tdp = &qtd[qtd_counter++].qt_next; + } + + ctrl->qh_list.qh_link = cpu_to_hc32((uint32_t)qh | QH_LINK_TYPE_QH); + + /* Flush dcache */ + flush_dcache_range((uint32_t)&ctrl->qh_list, + ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1)); + flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1)); + flush_dcache_range((uint32_t)qtd, + ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); + + /* Set async. queue head pointer. */ + ehci_writel(&ctrl->hcor->or_asynclistaddr, (uint32_t)&ctrl->qh_list); + + usbsts = ehci_readl(&ctrl->hcor->or_usbsts); + ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f)); + + /* Enable async. schedule. */ + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); + cmd |= CMD_ASE; + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + + ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS, + 100 * 1000); + if (ret < 0) { + printf("EHCI fail timeout STS_ASS set\n"); + goto fail; + } + + /* Wait for TDs to be processed. */ + ts = get_timer(0); + vtd = &qtd[qtd_counter - 1]; + timeout = USB_TIMEOUT_MS(pipe); + do { + /* Invalidate dcache */ + invalidate_dcache_range((uint32_t)&ctrl->qh_list, + ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1)); + invalidate_dcache_range((uint32_t)qh, + ALIGN_END_ADDR(struct QH, qh, 1)); + invalidate_dcache_range((uint32_t)qtd, + ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); + + token = hc32_to_cpu(vtd->qt_token); + if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) + break; + WATCHDOG_RESET(); + } while (get_timer(ts) < timeout); + + /* + * Invalidate the memory area occupied by buffer + * Don't try to fix the buffer alignment, if it isn't properly + * aligned it's upper layer's fault so let invalidate_dcache_range() + * vow about it. But we have to fix the length as it's actual + * transfer length and can be unaligned. This is potentially + * dangerous operation, it's responsibility of the calling + * code to make sure enough space is reserved. + */ + invalidate_dcache_range((uint32_t)buffer, + ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN)); + + /* Check that the TD processing happened */ + if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) + printf("EHCI timed out on TD - token=%#x\n", token); + + /* Disable async schedule. */ + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); + cmd &= ~CMD_ASE; + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + + ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, 0, + 100 * 1000); + if (ret < 0) { + printf("EHCI fail timeout STS_ASS reset\n"); + goto fail; + } + + token = hc32_to_cpu(qh->qh_overlay.qt_token); + if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) { + debug("TOKEN=%#x\n", token); + switch (QT_TOKEN_GET_STATUS(token) & + ~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) { + case 0: + toggle = QT_TOKEN_GET_DT(token); + usb_settoggle(dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), toggle); + dev->status = 0; + break; + case QT_TOKEN_STATUS_HALTED: + dev->status = USB_ST_STALLED; + break; + case QT_TOKEN_STATUS_ACTIVE | QT_TOKEN_STATUS_DATBUFERR: + case QT_TOKEN_STATUS_DATBUFERR: + dev->status = USB_ST_BUF_ERR; + break; + case QT_TOKEN_STATUS_HALTED | QT_TOKEN_STATUS_BABBLEDET: + case QT_TOKEN_STATUS_BABBLEDET: + dev->status = USB_ST_BABBLE_DET; + break; + default: + dev->status = USB_ST_CRC_ERR; + if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED) + dev->status |= USB_ST_STALLED; + break; + } + dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token); + } else { + dev->act_len = 0; +#ifndef CONFIG_USB_EHCI_FARADAY + debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", + dev->devnum, ehci_readl(&ctrl->hcor->or_usbsts), + ehci_readl(&ctrl->hcor->or_portsc[0]), + ehci_readl(&ctrl->hcor->or_portsc[1])); +#endif + } + + free(qtd); + return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; + +fail: + free(qtd); + return -1; +} + +__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +{ + if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { + /* Printing the message would cause a scan failure! */ + debug("The request port(%u) is not configured\n", port); + return NULL; + } + + return (uint32_t *)&hcor->or_portsc[port]; +} + +int +ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *req) +{ + uint8_t tmpbuf[4]; + u16 typeReq; + void *srcptr = NULL; + int len, srclen; + uint32_t reg; + uint32_t *status_reg; + int port = le16_to_cpu(req->index) & 0xff; + struct ehci_ctrl *ctrl = dev->controller; + + srclen = 0; + + debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->index)); + + typeReq = req->request | req->requesttype << 8; + + switch (typeReq) { + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + status_reg = ehci_get_portsc_register(ctrl->hcor, port - 1); + if (!status_reg) + return -1; + break; + default: + status_reg = NULL; + break; + } + + switch (typeReq) { + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_DEVICE: + debug("USB_DT_DEVICE request\n"); + srcptr = &descriptor.device; + srclen = descriptor.device.bLength; + break; + case USB_DT_CONFIG: + debug("USB_DT_CONFIG config\n"); + srcptr = &descriptor.config; + srclen = descriptor.config.bLength + + descriptor.interface.bLength + + descriptor.endpoint.bLength; + break; + case USB_DT_STRING: + debug("USB_DT_STRING config\n"); + switch (le16_to_cpu(req->value) & 0xff) { + case 0: /* Language */ + srcptr = "\4\3\1\0"; + srclen = 4; + break; + case 1: /* Vendor */ + srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; + srclen = 14; + break; + case 2: /* Product */ + srcptr = "\52\3E\0H\0C\0I\0 " + "\0H\0o\0s\0t\0 " + "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; + srclen = 42; + break; + default: + debug("unknown value DT_STRING %x\n", + le16_to_cpu(req->value)); + goto unknown; + } + break; + default: + debug("unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_HUB: + debug("USB_DT_HUB config\n"); + srcptr = &descriptor.hub; + srclen = descriptor.hub.bLength; + break; + default: + debug("unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): + debug("USB_REQ_SET_ADDRESS\n"); + ctrl->rootdev = le16_to_cpu(req->value); + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + debug("USB_REQ_SET_CONFIGURATION\n"); + /* Nothing to do */ + break; + case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): + tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */ + tmpbuf[1] = 0; + srcptr = tmpbuf; + srclen = 2; + break; + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): + memset(tmpbuf, 0, 4); + reg = ehci_readl(status_reg); + if (reg & EHCI_PS_CS) + tmpbuf[0] |= USB_PORT_STAT_CONNECTION; + if (reg & EHCI_PS_PE) + tmpbuf[0] |= USB_PORT_STAT_ENABLE; + if (reg & EHCI_PS_SUSP) + tmpbuf[0] |= USB_PORT_STAT_SUSPEND; + if (reg & EHCI_PS_OCA) + tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; + if (reg & EHCI_PS_PR) + tmpbuf[0] |= USB_PORT_STAT_RESET; + if (reg & EHCI_PS_PP) + tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + + if (ehci_is_TDI()) { + switch (ehci_get_port_speed(ctrl->hcor, reg)) { + case PORTSC_PSPD_FS: + break; + case PORTSC_PSPD_LS: + tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; + break; + case PORTSC_PSPD_HS: + default: + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + break; + } + } else { + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + } + + if (reg & EHCI_PS_CSC) + tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; + if (reg & EHCI_PS_PEC) + tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; + if (reg & EHCI_PS_OCC) + tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; + if (ctrl->portreset & (1 << port)) + tmpbuf[2] |= USB_PORT_STAT_C_RESET; + + srcptr = tmpbuf; + srclen = 4; + break; + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(status_reg); + reg &= ~EHCI_PS_CLEAR; + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg |= EHCI_PS_PE; + ehci_writel(status_reg, reg); + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) { + reg |= EHCI_PS_PP; + ehci_writel(status_reg, reg); + } + break; + case USB_PORT_FEAT_RESET: + if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && + !ehci_is_TDI() && + EHCI_PS_IS_LOWSPEED(reg)) { + /* Low speed device, give up ownership. */ + debug("port %d low speed --> companion\n", + port - 1); + reg |= EHCI_PS_PO; + ehci_writel(status_reg, reg); + break; + } else { + int ret; + + reg |= EHCI_PS_PR; + reg &= ~EHCI_PS_PE; + ehci_writel(status_reg, reg); + /* + * caller must wait, then call GetPortStatus + * usb 2.0 specification say 50 ms resets on + * root + */ + ehci_powerup_fixup(status_reg, ®); + + ehci_writel(status_reg, reg & ~EHCI_PS_PR); + /* + * A host controller must terminate the reset + * and stabilize the state of the port within + * 2 milliseconds + */ + ret = handshake(status_reg, EHCI_PS_PR, 0, + 2 * 1000); + if (!ret) + ctrl->portreset |= 1 << port; + else + printf("port(%d) reset error\n", + port - 1); + } + break; + case USB_PORT_FEAT_TEST: + ehci_shutdown(ctrl); + reg &= ~(0xf << 16); + reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16; + ehci_writel(status_reg, reg); + break; + default: + debug("unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + /* unblock posted writes */ + (void) ehci_readl(&ctrl->hcor->or_usbcmd); + break; + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(status_reg); + reg &= ~EHCI_PS_CLEAR; + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg &= ~EHCI_PS_PE; + break; + case USB_PORT_FEAT_C_ENABLE: + reg |= EHCI_PS_PE; + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) + reg &= ~EHCI_PS_PP; + break; + case USB_PORT_FEAT_C_CONNECTION: + reg |= EHCI_PS_CSC; + break; + case USB_PORT_FEAT_OVER_CURRENT: + reg |= EHCI_PS_OCC; + break; + case USB_PORT_FEAT_C_RESET: + ctrl->portreset &= ~(1 << port); + break; + default: + debug("unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + ehci_writel(status_reg, reg); + /* unblock posted write */ + (void) ehci_readl(&ctrl->hcor->or_usbcmd); + break; + default: + debug("Unknown request\n"); + goto unknown; + } + + mdelay(1); + len = min3(srclen, le16_to_cpu(req->length), length); + if (srcptr != NULL && len > 0) + memcpy(buffer, srcptr, len); + else + debug("Len is 0\n"); + + dev->act_len = len; + dev->status = 0; + return 0; + +unknown: + debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n", + req->requesttype, req->request, le16_to_cpu(req->value), + le16_to_cpu(req->index), le16_to_cpu(req->length)); + + dev->act_len = 0; + dev->status = USB_ST_STALLED; + return -1; +} + +int usb_lowlevel_stop(int index) +{ + ehci_shutdown(&ehcic[index]); + return ehci_hcd_stop(index); +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + uint32_t reg; + uint32_t cmd; + struct QH *qh_list; + struct QH *periodic; + int i; + int rc; + + rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor); + if (rc) + return rc; + if (init == USB_INIT_DEVICE) + goto done; + + /* EHCI spec section 4.1 */ + if (ehci_reset(index)) + return -1; + +#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) + rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor); + if (rc) + return rc; +#endif + /* Set the high address word (aka segment) for 64-bit controller */ + if (ehci_readl(&ehcic[index].hccr->cr_hccparams) & 1) + ehci_writel(&ehcic[index].hcor->or_ctrldssegment, 0); + + qh_list = &ehcic[index].qh_list; + + /* Set head of reclaim list */ + memset(qh_list, 0, sizeof(*qh_list)); + qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH); + qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) | + QH_ENDPT1_EPS(USB_SPEED_HIGH)); + qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + qh_list->qh_overlay.qt_token = + cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED)); + + flush_dcache_range((uint32_t)qh_list, + ALIGN_END_ADDR(struct QH, qh_list, 1)); + + /* Set async. queue head pointer. */ + ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list); + + /* + * Set up periodic list + * Step 1: Parent QH for all periodic transfers. + */ + periodic = &ehcic[index].periodic_queue; + memset(periodic, 0, sizeof(*periodic)); + periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); + periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + + flush_dcache_range((uint32_t)periodic, + ALIGN_END_ADDR(struct QH, periodic, 1)); + + /* + * Step 2: Setup frame-list: Every microframe, USB tries the same list. + * In particular, device specifications on polling frequency + * are disregarded. Keyboards seem to send NAK/NYet reliably + * when polled with an empty buffer. + * + * Split Transactions will be spread across microframes using + * S-mask and C-mask. + */ + if (ehcic[index].periodic_list == NULL) + ehcic[index].periodic_list = memalign(4096, 1024 * 4); + + if (!ehcic[index].periodic_list) + return -ENOMEM; + for (i = 0; i < 1024; i++) { + ehcic[index].periodic_list[i] = cpu_to_hc32((uint32_t)periodic + | QH_LINK_TYPE_QH); + } + + flush_dcache_range((uint32_t)ehcic[index].periodic_list, + ALIGN_END_ADDR(uint32_t, ehcic[index].periodic_list, + 1024)); + + /* Set periodic list base address */ + ehci_writel(&ehcic[index].hcor->or_periodiclistbase, + (uint32_t)ehcic[index].periodic_list); + + reg = ehci_readl(&ehcic[index].hccr->cr_hcsparams); + descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); + debug("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); + /* Port Indicators */ + if (HCS_INDICATOR(reg)) + put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) + | 0x80, &descriptor.hub.wHubCharacteristics); + /* Port Power Control */ + if (HCS_PPC(reg)) + put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) + | 0x01, &descriptor.hub.wHubCharacteristics); + + /* Start the host controller. */ + cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); + /* + * Philips, Intel, and maybe others need CMD_RUN before the + * root hub will detect new devices (why?); NEC doesn't + */ + cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + cmd |= CMD_RUN; + ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd); + +#ifndef CONFIG_USB_EHCI_FARADAY + /* take control over the ports */ + cmd = ehci_readl(&ehcic[index].hcor->or_configflag); + cmd |= FLAG_CF; + ehci_writel(&ehcic[index].hcor->or_configflag, cmd); +#endif + + /* unblock posted write */ + cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); + mdelay(5); + reg = HC_VERSION(ehci_readl(&ehcic[index].hccr->cr_capbase)); + printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff); + + ehcic[index].rootdev = 0; +done: + *controller = &ehcic[index]; + return 0; +} + +int +submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length) +{ + + if (usb_pipetype(pipe) != PIPE_BULK) { + debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); + return -1; + } + return ehci_submit_async(dev, pipe, buffer, length, NULL); +} + +int +submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *setup) +{ + struct ehci_ctrl *ctrl = dev->controller; + + if (usb_pipetype(pipe) != PIPE_CONTROL) { + debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); + return -1; + } + + if (usb_pipedevice(pipe) == ctrl->rootdev) { + if (!ctrl->rootdev) + dev->speed = USB_SPEED_HIGH; + return ehci_submit_root(dev, pipe, buffer, length, setup); + } + return ehci_submit_async(dev, pipe, buffer, length, setup); +} + +struct int_queue { + struct QH *first; + struct QH *current; + struct QH *last; + struct qTD *tds; +}; + +#define NEXT_QH(qh) (struct QH *)(hc32_to_cpu((qh)->qh_link) & ~0x1f) + +static int +enable_periodic(struct ehci_ctrl *ctrl) +{ + uint32_t cmd; + struct ehci_hcor *hcor = ctrl->hcor; + int ret; + + cmd = ehci_readl(&hcor->or_usbcmd); + cmd |= CMD_PSE; + ehci_writel(&hcor->or_usbcmd, cmd); + + ret = handshake((uint32_t *)&hcor->or_usbsts, + STS_PSS, STS_PSS, 100 * 1000); + if (ret < 0) { + printf("EHCI failed: timeout when enabling periodic list\n"); + return -ETIMEDOUT; + } + udelay(1000); + return 0; +} + +static int +disable_periodic(struct ehci_ctrl *ctrl) +{ + uint32_t cmd; + struct ehci_hcor *hcor = ctrl->hcor; + int ret; + + cmd = ehci_readl(&hcor->or_usbcmd); + cmd &= ~CMD_PSE; + ehci_writel(&hcor->or_usbcmd, cmd); + + ret = handshake((uint32_t *)&hcor->or_usbsts, + STS_PSS, 0, 100 * 1000); + if (ret < 0) { + printf("EHCI failed: timeout when disabling periodic list\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int periodic_schedules; + +struct int_queue * +create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, + int elementsize, void *buffer) +{ + struct ehci_ctrl *ctrl = dev->controller; + struct int_queue *result = NULL; + int i; + + debug("Enter create_int_queue\n"); + if (usb_pipetype(pipe) != PIPE_INTERRUPT) { + debug("non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); + return NULL; + } + + /* limit to 4 full pages worth of data - + * we can safely fit them in a single TD, + * no matter the alignment + */ + if (elementsize >= 16384) { + debug("too large elements for interrupt transfers\n"); + return NULL; + } + + result = malloc(sizeof(*result)); + if (!result) { + debug("ehci intr queue: out of memory\n"); + goto fail1; + } + result->first = memalign(USB_DMA_MINALIGN, + sizeof(struct QH) * queuesize); + if (!result->first) { + debug("ehci intr queue: out of memory\n"); + goto fail2; + } + result->current = result->first; + result->last = result->first + queuesize - 1; + result->tds = memalign(USB_DMA_MINALIGN, + sizeof(struct qTD) * queuesize); + if (!result->tds) { + debug("ehci intr queue: out of memory\n"); + goto fail3; + } + memset(result->first, 0, sizeof(struct QH) * queuesize); + memset(result->tds, 0, sizeof(struct qTD) * queuesize); + + for (i = 0; i < queuesize; i++) { + struct QH *qh = result->first + i; + struct qTD *td = result->tds + i; + void **buf = &qh->buffer; + + qh->qh_link = cpu_to_hc32((uint32_t)(qh+1) | QH_LINK_TYPE_QH); + if (i == queuesize - 1) + qh->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); + + qh->qh_overlay.qt_next = cpu_to_hc32((uint32_t)td); + qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + qh->qh_endpt1 = + cpu_to_hc32((0 << 28) | /* No NAK reload (ehci 4.9) */ + (usb_maxpacket(dev, pipe) << 16) | /* MPS */ + (1 << 14) | + QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) | + (usb_pipeendpoint(pipe) << 8) | /* Endpoint Number */ + (usb_pipedevice(pipe) << 0)); + qh->qh_endpt2 = cpu_to_hc32((1 << 30) | /* 1 Tx per mframe */ + (1 << 0)); /* S-mask: microframe 0 */ + if (dev->speed == USB_SPEED_LOW || + dev->speed == USB_SPEED_FULL) { + debug("TT: port: %d, hub address: %d\n", + dev->portnr, dev->parent->devnum); + qh->qh_endpt2 |= cpu_to_hc32((dev->portnr << 23) | + (dev->parent->devnum << 16) | + (0x1c << 8)); /* C-mask: microframes 2-4 */ + } + + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + debug("communication direction is '%s'\n", + usb_pipein(pipe) ? "in" : "out"); + td->qt_token = cpu_to_hc32((elementsize << 16) | + ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ + 0x80); /* active */ + td->qt_buffer[0] = + cpu_to_hc32((uint32_t)buffer + i * elementsize); + td->qt_buffer[1] = + cpu_to_hc32((td->qt_buffer[0] + 0x1000) & ~0xfff); + td->qt_buffer[2] = + cpu_to_hc32((td->qt_buffer[0] + 0x2000) & ~0xfff); + td->qt_buffer[3] = + cpu_to_hc32((td->qt_buffer[0] + 0x3000) & ~0xfff); + td->qt_buffer[4] = + cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff); + + *buf = buffer + i * elementsize; + } + + flush_dcache_range((uint32_t)buffer, + ALIGN_END_ADDR(char, buffer, + queuesize * elementsize)); + flush_dcache_range((uint32_t)result->first, + ALIGN_END_ADDR(struct QH, result->first, + queuesize)); + flush_dcache_range((uint32_t)result->tds, + ALIGN_END_ADDR(struct qTD, result->tds, + queuesize)); + + if (disable_periodic(ctrl) < 0) { + debug("FATAL: periodic should never fail, but did"); + goto fail3; + } + + /* hook up to periodic list */ + struct QH *list = &ctrl->periodic_queue; + result->last->qh_link = list->qh_link; + list->qh_link = cpu_to_hc32((uint32_t)result->first | QH_LINK_TYPE_QH); + + flush_dcache_range((uint32_t)result->last, + ALIGN_END_ADDR(struct QH, result->last, 1)); + flush_dcache_range((uint32_t)list, + ALIGN_END_ADDR(struct QH, list, 1)); + + if (enable_periodic(ctrl) < 0) { + debug("FATAL: periodic should never fail, but did"); + goto fail3; + } + periodic_schedules++; + + debug("Exit create_int_queue\n"); + return result; +fail3: + if (result->tds) + free(result->tds); +fail2: + if (result->first) + free(result->first); + if (result) + free(result); +fail1: + return NULL; +} + +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + struct QH *cur = queue->current; + + /* depleted queue */ + if (cur == NULL) { + debug("Exit poll_int_queue with completed queue\n"); + return NULL; + } + /* still active */ + invalidate_dcache_range((uint32_t)cur, + ALIGN_END_ADDR(struct QH, cur, 1)); + if (cur->qh_overlay.qt_token & cpu_to_hc32(0x80)) { + debug("Exit poll_int_queue with no completed intr transfer. " + "token is %x\n", cur->qh_overlay.qt_token); + return NULL; + } + if (!(cur->qh_link & QH_LINK_TERMINATE)) + queue->current++; + else + queue->current = NULL; + debug("Exit poll_int_queue with completed intr transfer. " + "token is %x at %p (first at %p)\n", cur->qh_overlay.qt_token, + &cur->qh_overlay.qt_token, queue->first); + return cur->buffer; +} + +/* Do not free buffers associated with QHs, they're owned by someone else */ +int +destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + struct ehci_ctrl *ctrl = dev->controller; + int result = -1; + unsigned long timeout; + + if (disable_periodic(ctrl) < 0) { + debug("FATAL: periodic should never fail, but did"); + goto out; + } + periodic_schedules--; + + struct QH *cur = &ctrl->periodic_queue; + timeout = get_timer(0) + 500; /* abort after 500ms */ + while (!(cur->qh_link & cpu_to_hc32(QH_LINK_TERMINATE))) { + debug("considering %p, with qh_link %x\n", cur, cur->qh_link); + if (NEXT_QH(cur) == queue->first) { + debug("found candidate. removing from chain\n"); + cur->qh_link = queue->last->qh_link; + result = 0; + break; + } + cur = NEXT_QH(cur); + if (get_timer(0) > timeout) { + printf("Timeout destroying interrupt endpoint queue\n"); + result = -1; + goto out; + } + } + + if (periodic_schedules > 0) { + result = enable_periodic(ctrl); + if (result < 0) + debug("FATAL: periodic should never fail, but did"); + } + +out: + free(queue->tds); + free(queue->first); + free(queue); + + return result; +} + +int +submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, int interval) +{ + void *backbuffer; + struct int_queue *queue; + unsigned long timeout; + int result = 0, ret; + + debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", + dev, pipe, buffer, length, interval); + + /* + * Interrupt transfers requiring several transactions are not supported + * because bInterval is ignored. + * + * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2 + * <= PKT_ALIGN if several qTDs are required, while the USB + * specification does not constrain this for interrupt transfers. That + * means that ehci_submit_async() would support interrupt transfers + * requiring several transactions only as long as the transfer size does + * not require more than a single qTD. + */ + if (length > usb_maxpacket(dev, pipe)) { + printf("%s: Interrupt transfers requiring several " + "transactions are not supported.\n", __func__); + return -1; + } + + queue = create_int_queue(dev, pipe, 1, length, buffer); + + timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); + while ((backbuffer = poll_int_queue(dev, queue)) == NULL) + if (get_timer(0) > timeout) { + printf("Timeout poll on interrupt endpoint\n"); + result = -ETIMEDOUT; + break; + } + + if (backbuffer != buffer) { + debug("got wrong buffer back (%x instead of %x)\n", + (uint32_t)backbuffer, (uint32_t)buffer); + return -EINVAL; + } + + invalidate_dcache_range((uint32_t)buffer, + ALIGN_END_ADDR(char, buffer, length)); + + ret = destroy_int_queue(dev, queue); + if (ret < 0) + return ret; + + /* everything worked out fine */ + return result; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-marvell.c b/qemu/roms/u-boot/drivers/usb/host/ehci-marvell.c new file mode 100644 index 000000000..52c43fdc5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-marvell.c @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "ehci.h" +#include + +#if defined(CONFIG_KIRKWOOD) +#include +#elif defined(CONFIG_ORION5X) +#include +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#define rdl(off) readl(MVUSB0_BASE + (off)) +#define wrl(off, val) writel((val), MVUSB0_BASE + (off)) + +#define USB_WINDOW_CTRL(i) (0x320 + ((i) << 4)) +#define USB_WINDOW_BASE(i) (0x324 + ((i) << 4)) +#define USB_TARGET_DRAM 0x0 + +/* + * USB 2.0 Bridge Address Decoding registers setup + */ +static void usb_brg_adrdec_setup(void) +{ + int i; + u32 size, base, attrib; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + + /* Enable DRAM bank */ + switch (i) { + case 0: + attrib = MVUSB0_CPU_ATTR_DRAM_CS0; + break; + case 1: + attrib = MVUSB0_CPU_ATTR_DRAM_CS1; + break; + case 2: + attrib = MVUSB0_CPU_ATTR_DRAM_CS2; + break; + case 3: + attrib = MVUSB0_CPU_ATTR_DRAM_CS3; + break; + default: + /* invalide bank, disable access */ + attrib = 0; + break; + } + + size = gd->bd->bi_dram[i].size; + base = gd->bd->bi_dram[i].start; + if ((size) && (attrib)) + wrl(USB_WINDOW_CTRL(i), + MVCPU_WIN_CTRL_DATA(size, USB_TARGET_DRAM, + attrib, MVCPU_WIN_ENABLE)); + else + wrl(USB_WINDOW_CTRL(i), MVCPU_WIN_DISABLE); + + wrl(USB_WINDOW_BASE(i), base); + } +} + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + usb_brg_adrdec_setup(); + + *hccr = (struct ehci_hccr *)(MVUSB0_BASE + 0x100); + *hcor = (struct ehci_hcor *)((uint32_t) *hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + debug("ehci-marvell: init hccr %x and hcor %x hc_length %d\n", + (uint32_t)*hccr, (uint32_t)*hcor, + (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-mpc512x.c b/qemu/roms/u-boot/drivers/usb/host/ehci-mpc512x.c new file mode 100644 index 000000000..b320c4a4e --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-mpc512x.c @@ -0,0 +1,140 @@ +/* + * (C) Copyright 2010, Damien Dusha, + * + * (C) Copyright 2009, Value Team S.p.A. + * Francesco Rendine, + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + * + * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB + * + * Author: Tor Krill tor@excito.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include "ehci.h" + +static void fsl_setup_phy(volatile struct ehci_hcor *); +static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci); +static int reset_usb_controller(volatile struct usb_ehci *ehci); +static void usb_platform_dr_init(volatile struct usb_ehci *ehci); + +/* + * Initialize SOC FSL EHCI Controller + * + * This code is derived from EHCI FSL USB Linux driver for MPC5121 + * + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + volatile struct usb_ehci *ehci; + + /* Hook the memory mapped registers for EHCI-Controller */ + ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR; + *hccr = (struct ehci_hccr *)((uint32_t)&(ehci->caplength)); + *hcor = (struct ehci_hcor *)((uint32_t) *hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + /* configure interface for UTMI_WIDE */ + usb_platform_dr_init(ehci); + + /* Init Phy USB0 to UTMI+ */ + fsl_setup_phy(*hcor); + + /* Set to host mode */ + fsl_platform_set_host_mode(ehci); + + /* + * Setting the burst size seems to be required to prevent the + * USB from hanging when communicating with certain USB Mass + * storage devices. This was determined by analysing the + * EHCI registers under Linux vs U-Boot and burstsize was the + * major non-interrupt related difference between the two + * implementations. + * + * Some USB sticks behave better than others. In particular, + * the following USB stick is especially problematic: + * 0930:6545 Toshiba Corp + * + * The burstsize is set here to match the Linux implementation. + */ + out_be32(&ehci->burstsize, FSL_EHCI_TXPBURST(8) | + FSL_EHCI_RXPBURST(8)); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + volatile struct usb_ehci *ehci; + int exit_status = 0; + + /* Reset the USB controller */ + ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR; + exit_status = reset_usb_controller(ehci); + + return exit_status; +} + +static int reset_usb_controller(volatile struct usb_ehci *ehci) +{ + unsigned int i; + + /* Command a reset of the USB Controller */ + out_be32(&(ehci->usbcmd), EHCI_FSL_USBCMD_RST); + + /* Wait for the reset process to finish */ + for (i = 65535 ; i > 0 ; i--) { + /* + * The host will set this bit to zero once the + * reset process is complete + */ + if ((in_be32(&(ehci->usbcmd)) & EHCI_FSL_USBCMD_RST) == 0) + return 0; + } + + /* Hub did not reset in time */ + return -1; +} + +static void fsl_setup_phy(volatile struct ehci_hcor *hcor) +{ + uint32_t portsc; + + portsc = ehci_readl(&hcor->or_portsc[0]); + portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW); + + /* Enable the phy mode to UTMI Wide */ + portsc |= PORT_PTS_PTW; + portsc |= PORT_PTS_UTMI; + + ehci_writel(&hcor->or_portsc[0], portsc); +} + +static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci) +{ + uint32_t temp; + + temp = in_le32(&ehci->usbmode); + temp |= CM_HOST | ES_BE; + out_le32(&ehci->usbmode, temp); +} + +static void usb_platform_dr_init(volatile struct usb_ehci *ehci) +{ + /* Configure interface for UTMI_WIDE */ + out_be32(&ehci->isiphyctrl, PHYCTRL_PHYE | PHYCTRL_PXE); + out_be32(&ehci->usbgenctrl, GC_PPP | GC_PFP ); +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-mx5.c b/qemu/roms/u-boot/drivers/usb/host/ehci-mx5.c new file mode 100644 index 000000000..7566c6128 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-mx5.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2009 Daniel Mack + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +#define MX5_USBOTHER_REGS_OFFSET 0x800 + + +#define MXC_OTG_OFFSET 0 +#define MXC_H1_OFFSET 0x200 +#define MXC_H2_OFFSET 0x400 +#define MXC_H3_OFFSET 0x600 + +#define MXC_USBCTRL_OFFSET 0 +#define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8 +#define MXC_USB_PHY_CTR_FUNC2_OFFSET 0xc +#define MXC_USB_CTRL_1_OFFSET 0x10 +#define MXC_USBH2CTRL_OFFSET 0x14 +#define MXC_USBH3CTRL_OFFSET 0x18 + +/* USB_CTRL */ +/* OTG wakeup intr enable */ +#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) +/* OTG power mask */ +#define MXC_OTG_UCTRL_OPM_BIT (1 << 24) +/* OTG power pin polarity */ +#define MXC_OTG_UCTRL_O_PWR_POL_BIT (1 << 24) +/* Host1 ULPI interrupt enable */ +#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) +/* HOST1 wakeup intr enable */ +#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) +/* HOST1 power mask */ +#define MXC_H1_UCTRL_H1PM_BIT (1 << 8) +/* HOST1 power pin polarity */ +#define MXC_H1_UCTRL_H1_PWR_POL_BIT (1 << 8) + +/* USB_PHY_CTRL_FUNC */ +/* OTG Polarity of Overcurrent */ +#define MXC_OTG_PHYCTRL_OC_POL_BIT (1 << 9) +/* OTG Disable Overcurrent Event */ +#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) +/* UH1 Polarity of Overcurrent */ +#define MXC_H1_OC_POL_BIT (1 << 6) +/* UH1 Disable Overcurrent Event */ +#define MXC_H1_OC_DIS_BIT (1 << 5) +/* OTG Power Pin Polarity */ +#define MXC_OTG_PHYCTRL_PWR_POL_BIT (1 << 3) + +/* USBH2CTRL */ +#define MXC_H2_UCTRL_H2_OC_POL_BIT (1 << 31) +#define MXC_H2_UCTRL_H2_OC_DIS_BIT (1 << 30) +#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8) +#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7) +#define MXC_H2_UCTRL_H2PM_BIT (1 << 4) +#define MXC_H2_UCTRL_H2_PWR_POL_BIT (1 << 4) + +/* USBH3CTRL */ +#define MXC_H3_UCTRL_H3_OC_POL_BIT (1 << 31) +#define MXC_H3_UCTRL_H3_OC_DIS_BIT (1 << 30) +#define MXC_H3_UCTRL_H3UIE_BIT (1 << 8) +#define MXC_H3_UCTRL_H3WIE_BIT (1 << 7) +#define MXC_H3_UCTRL_H3_PWR_POL_BIT (1 << 4) + +/* USB_CTRL_1 */ +#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) + +int mxc_set_usbcontrol(int port, unsigned int flags) +{ + unsigned int v; + void __iomem *usb_base = (void __iomem *)OTG_BASE_ADDR; + void __iomem *usbother_base; + int ret = 0; + + usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + + switch (port) { + case 0: /* OTG port */ + if (flags & MXC_EHCI_INTERNAL_PHY) { + v = __raw_readl(usbother_base + + MXC_USB_PHY_CTR_FUNC_OFFSET); + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + v |= MXC_OTG_PHYCTRL_OC_POL_BIT; + else + v &= ~MXC_OTG_PHYCTRL_OC_POL_BIT; + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + /* OC/USBPWR is used */ + v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT; + else + /* OC/USBPWR is not used */ + v |= MXC_OTG_PHYCTRL_OC_DIS_BIT; +#ifdef CONFIG_MX51 + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_OTG_PHYCTRL_PWR_POL_BIT; + else + v &= ~MXC_OTG_PHYCTRL_PWR_POL_BIT; +#endif + __raw_writel(v, usbother_base + + MXC_USB_PHY_CTR_FUNC_OFFSET); + + v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); +#ifdef CONFIG_MX51 + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_OTG_UCTRL_OPM_BIT; + else + v |= MXC_OTG_UCTRL_OPM_BIT; +#endif +#ifdef CONFIG_MX53 + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_OTG_UCTRL_O_PWR_POL_BIT; + else + v &= ~MXC_OTG_UCTRL_O_PWR_POL_BIT; +#endif + __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); + } + break; + case 1: /* Host 1 ULPI */ +#ifdef CONFIG_MX51 + /* The clock for the USBH1 ULPI port will come externally + from the PHY. */ + v = __raw_readl(usbother_base + MXC_USB_CTRL_1_OFFSET); + __raw_writel(v | MXC_USB_CTRL_UH1_EXT_CLK_EN, usbother_base + + MXC_USB_CTRL_1_OFFSET); +#endif + + v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); +#ifdef CONFIG_MX51 + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask unused */ + else + v |= MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask used */ +#endif +#ifdef CONFIG_MX53 + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_H1_UCTRL_H1_PWR_POL_BIT; + else + v &= ~MXC_H1_UCTRL_H1_PWR_POL_BIT; +#endif + __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); + + v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + v |= MXC_H1_OC_POL_BIT; + else + v &= ~MXC_H1_OC_POL_BIT; + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */ + else + v |= MXC_H1_OC_DIS_BIT; /* OC is not used */ + __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + + break; + case 2: /* Host 2 ULPI */ + v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET); +#ifdef CONFIG_MX51 + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask unused */ + else + v |= MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask used */ +#endif +#ifdef CONFIG_MX53 + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + v |= MXC_H2_UCTRL_H2_OC_POL_BIT; + else + v &= ~MXC_H2_UCTRL_H2_OC_POL_BIT; + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is used */ + else + v |= MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is not used */ + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_H2_UCTRL_H2_PWR_POL_BIT; + else + v &= ~MXC_H2_UCTRL_H2_PWR_POL_BIT; +#endif + __raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); + break; +#ifdef CONFIG_MX53 + case 3: /* Host 3 ULPI */ + v = __raw_readl(usbother_base + MXC_USBH3CTRL_OFFSET); + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + v |= MXC_H3_UCTRL_H3_OC_POL_BIT; + else + v &= ~MXC_H3_UCTRL_H3_OC_POL_BIT; + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is used */ + else + v |= MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is not used */ + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_H3_UCTRL_H3_PWR_POL_BIT; + else + v &= ~MXC_H3_UCTRL_H3_PWR_POL_BIT; + __raw_writel(v, usbother_base + MXC_USBH3CTRL_OFFSET); + break; +#endif + } + + return ret; +} + +int __weak board_ehci_hcd_init(int port) +{ + return 0; +} + +void __weak board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) +{ +} + +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + struct usb_ehci *ehci; + + set_usboh3_clk(); + enable_usboh3_clk(true); + set_usb_phy_clk(); + enable_usb_phy1_clk(true); + enable_usb_phy2_clk(true); + mdelay(1); + + /* Do board specific initialization */ + board_ehci_hcd_init(CONFIG_MXC_USB_PORT); + + ehci = (struct usb_ehci *)(OTG_BASE_ADDR + + (0x200 * CONFIG_MXC_USB_PORT)); + *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); + *hcor = (struct ehci_hcor *)((uint32_t)*hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + setbits_le32(&ehci->usbmode, CM_HOST); + + __raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); + setbits_le32(&ehci->portsc, USB_EN); + + mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS); + mdelay(10); + + /* Do board specific post-initialization */ + board_ehci_hcd_postinit(ehci, CONFIG_MXC_USB_PORT); + + return 0; +} + +int ehci_hcd_stop(int index) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-mx6.c b/qemu/roms/u-boot/drivers/usb/host/ehci-mx6.c new file mode 100644 index 000000000..c0a557b2a --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-mx6.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2009 Daniel Mack + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +#define USB_OTGREGS_OFFSET 0x000 +#define USB_H1REGS_OFFSET 0x200 +#define USB_H2REGS_OFFSET 0x400 +#define USB_H3REGS_OFFSET 0x600 +#define USB_OTHERREGS_OFFSET 0x800 + +#define USB_H1_CTRL_OFFSET 0x04 + +#define USBPHY_CTRL 0x00000030 +#define USBPHY_CTRL_SET 0x00000034 +#define USBPHY_CTRL_CLR 0x00000038 +#define USBPHY_CTRL_TOG 0x0000003c + +#define USBPHY_PWD 0x00000000 +#define USBPHY_CTRL_SFTRST 0x80000000 +#define USBPHY_CTRL_CLKGATE 0x40000000 +#define USBPHY_CTRL_ENUTMILEVEL3 0x00008000 +#define USBPHY_CTRL_ENUTMILEVEL2 0x00004000 +#define USBPHY_CTRL_OTG_ID 0x08000000 + +#define ANADIG_USB2_CHRG_DETECT_EN_B 0x00100000 +#define ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B 0x00080000 + +#define ANADIG_USB2_PLL_480_CTRL_BYPASS 0x00010000 +#define ANADIG_USB2_PLL_480_CTRL_ENABLE 0x00002000 +#define ANADIG_USB2_PLL_480_CTRL_POWER 0x00001000 +#define ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS 0x00000040 + + +#define UCTRL_OVER_CUR_POL (1 << 8) /* OTG Polarity of Overcurrent */ +#define UCTRL_OVER_CUR_DIS (1 << 7) /* Disable OTG Overcurrent Detection */ + +/* USBCMD */ +#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */ +#define UCMD_RESET (1 << 1) /* controller reset */ + +static const unsigned phy_bases[] = { + USB_PHY0_BASE_ADDR, + USB_PHY1_BASE_ADDR, +}; + +static void usb_internal_phy_clock_gate(int index, int on) +{ + void __iomem *phy_reg; + + if (index >= ARRAY_SIZE(phy_bases)) + return; + + phy_reg = (void __iomem *)phy_bases[index]; + phy_reg += on ? USBPHY_CTRL_CLR : USBPHY_CTRL_SET; + __raw_writel(USBPHY_CTRL_CLKGATE, phy_reg); +} + +static void usb_power_config(int index) +{ + struct anatop_regs __iomem *anatop = + (struct anatop_regs __iomem *)ANATOP_BASE_ADDR; + void __iomem *chrg_detect; + void __iomem *pll_480_ctrl_clr; + void __iomem *pll_480_ctrl_set; + + switch (index) { + case 0: + chrg_detect = &anatop->usb1_chrg_detect; + pll_480_ctrl_clr = &anatop->usb1_pll_480_ctrl_clr; + pll_480_ctrl_set = &anatop->usb1_pll_480_ctrl_set; + break; + case 1: + chrg_detect = &anatop->usb2_chrg_detect; + pll_480_ctrl_clr = &anatop->usb2_pll_480_ctrl_clr; + pll_480_ctrl_set = &anatop->usb2_pll_480_ctrl_set; + break; + default: + return; + } + /* + * Some phy and power's special controls + * 1. The external charger detector needs to be disabled + * or the signal at DP will be poor + * 2. The PLL's power and output to usb + * is totally controlled by IC, so the Software only needs + * to enable them at initializtion. + */ + __raw_writel(ANADIG_USB2_CHRG_DETECT_EN_B | + ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B, + chrg_detect); + + __raw_writel(ANADIG_USB2_PLL_480_CTRL_BYPASS, + pll_480_ctrl_clr); + + __raw_writel(ANADIG_USB2_PLL_480_CTRL_ENABLE | + ANADIG_USB2_PLL_480_CTRL_POWER | + ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS, + pll_480_ctrl_set); +} + +/* Return 0 : host node, <>0 : device mode */ +static int usb_phy_enable(int index, struct usb_ehci *ehci) +{ + void __iomem *phy_reg; + void __iomem *phy_ctrl; + void __iomem *usb_cmd; + u32 val; + + if (index >= ARRAY_SIZE(phy_bases)) + return 0; + + phy_reg = (void __iomem *)phy_bases[index]; + phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL); + usb_cmd = (void __iomem *)&ehci->usbcmd; + + /* Stop then Reset */ + val = __raw_readl(usb_cmd); + val &= ~UCMD_RUN_STOP; + __raw_writel(val, usb_cmd); + while (__raw_readl(usb_cmd) & UCMD_RUN_STOP) + ; + + val = __raw_readl(usb_cmd); + val |= UCMD_RESET; + __raw_writel(val, usb_cmd); + while (__raw_readl(usb_cmd) & UCMD_RESET) + ; + + /* Reset USBPHY module */ + val = __raw_readl(phy_ctrl); + val |= USBPHY_CTRL_SFTRST; + __raw_writel(val, phy_ctrl); + udelay(10); + + /* Remove CLKGATE and SFTRST */ + val = __raw_readl(phy_ctrl); + val &= ~(USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST); + __raw_writel(val, phy_ctrl); + udelay(10); + + /* Power up the PHY */ + __raw_writel(0, phy_reg + USBPHY_PWD); + /* enable FS/LS device */ + val = __raw_readl(phy_ctrl); + val |= (USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3); + __raw_writel(val, phy_ctrl); + + return val & USBPHY_CTRL_OTG_ID; +} + +/* Base address for this IP block is 0x02184800 */ +struct usbnc_regs { + u32 ctrl[4]; /* otg/host1-3 */ + u32 uh2_hsic_ctrl; + u32 uh3_hsic_ctrl; + u32 otg_phy_ctrl_0; + u32 uh1_phy_ctrl_0; +}; + +static void usb_oc_config(int index) +{ + struct usbnc_regs *usbnc = (struct usbnc_regs *)(USBOH3_USB_BASE_ADDR + + USB_OTHERREGS_OFFSET); + void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl[index]); + u32 val; + + val = __raw_readl(ctrl); +#if CONFIG_MACH_TYPE == MACH_TYPE_MX6Q_ARM2 + /* mx6qarm2 seems to required a different setting*/ + val &= ~UCTRL_OVER_CUR_POL; +#else + val |= UCTRL_OVER_CUR_POL; +#endif + __raw_writel(val, ctrl); + + val = __raw_readl(ctrl); + val |= UCTRL_OVER_CUR_DIS; + __raw_writel(val, ctrl); +} + +int __weak board_ehci_hcd_init(int port) +{ + return 0; +} + +int __weak board_ehci_power(int port, int on) +{ + return 0; +} + +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + enum usb_init_type type; + struct usb_ehci *ehci = (struct usb_ehci *)(USBOH3_USB_BASE_ADDR + + (0x200 * index)); + + if (index > 3) + return -EINVAL; + enable_usboh3_clk(1); + mdelay(1); + + /* Do board specific initialization */ + board_ehci_hcd_init(index); + + usb_power_config(index); + usb_oc_config(index); + usb_internal_phy_clock_gate(index, 1); + type = usb_phy_enable(index, ehci) ? USB_INIT_DEVICE : USB_INIT_HOST; + + *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); + *hcor = (struct ehci_hcor *)((uint32_t)*hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + if ((type == init) || (type == USB_INIT_DEVICE)) + board_ehci_power(index, (type == USB_INIT_DEVICE) ? 0 : 1); + if (type != init) + return -ENODEV; + if (type == USB_INIT_DEVICE) + return 0; + setbits_le32(&ehci->usbmode, CM_HOST); + __raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); + setbits_le32(&ehci->portsc, USB_EN); + + mdelay(10); + + return 0; +} + +int ehci_hcd_stop(int index) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-mxc.c b/qemu/roms/u-boot/drivers/usb/host/ehci-mxc.c new file mode 100644 index 000000000..f09c75a9b --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-mxc.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2009 Daniel Mack + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +#define USBCTRL_OTGBASE_OFFSET 0x600 + +#define MX25_OTG_SIC_SHIFT 29 +#define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT) +#define MX25_OTG_PM_BIT (1 << 24) +#define MX25_OTG_PP_BIT (1 << 11) +#define MX25_OTG_OCPOL_BIT (1 << 3) + +#define MX25_H1_SIC_SHIFT 21 +#define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT) +#define MX25_H1_PP_BIT (1 << 18) +#define MX25_H1_PM_BIT (1 << 16) +#define MX25_H1_IPPUE_UP_BIT (1 << 7) +#define MX25_H1_IPPUE_DOWN_BIT (1 << 6) +#define MX25_H1_TLL_BIT (1 << 5) +#define MX25_H1_USBTE_BIT (1 << 4) +#define MX25_H1_OCPOL_BIT (1 << 2) + +#define MX31_OTG_SIC_SHIFT 29 +#define MX31_OTG_SIC_MASK (0x3 << MX31_OTG_SIC_SHIFT) +#define MX31_OTG_PM_BIT (1 << 24) + +#define MX31_H2_SIC_SHIFT 21 +#define MX31_H2_SIC_MASK (0x3 << MX31_H2_SIC_SHIFT) +#define MX31_H2_PM_BIT (1 << 16) +#define MX31_H2_DT_BIT (1 << 5) + +#define MX31_H1_SIC_SHIFT 13 +#define MX31_H1_SIC_MASK (0x3 << MX31_H1_SIC_SHIFT) +#define MX31_H1_PM_BIT (1 << 8) +#define MX31_H1_DT_BIT (1 << 4) + +#define MX35_OTG_SIC_SHIFT 29 +#define MX35_OTG_SIC_MASK (0x3 << MX35_OTG_SIC_SHIFT) +#define MX35_OTG_PM_BIT (1 << 24) +#define MX35_OTG_PP_BIT (1 << 11) +#define MX35_OTG_OCPOL_BIT (1 << 3) + +#define MX35_H1_SIC_SHIFT 21 +#define MX35_H1_SIC_MASK (0x3 << MX35_H1_SIC_SHIFT) +#define MX35_H1_PP_BIT (1 << 18) +#define MX35_H1_PM_BIT (1 << 16) +#define MX35_H1_IPPUE_UP_BIT (1 << 7) +#define MX35_H1_IPPUE_DOWN_BIT (1 << 6) +#define MX35_H1_TLL_BIT (1 << 5) +#define MX35_H1_USBTE_BIT (1 << 4) +#define MX35_H1_OCPOL_BIT (1 << 2) + +static int mxc_set_usbcontrol(int port, unsigned int flags) +{ + unsigned int v; + + v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET); +#if defined(CONFIG_MX25) + switch (port) { + case 0: /* OTG port */ + v &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PM_BIT | MX25_OTG_PP_BIT | + MX25_OTG_OCPOL_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX25_OTG_PM_BIT; + + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MX25_OTG_PP_BIT; + + if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) + v |= MX25_OTG_OCPOL_BIT; + + break; + case 1: /* H1 port */ + v &= ~(MX25_H1_SIC_MASK | MX25_H1_PM_BIT | MX25_H1_PP_BIT | + MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT | + MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT | + MX25_H1_IPPUE_UP_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX25_H1_PM_BIT; + + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MX25_H1_PP_BIT; + + if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) + v |= MX25_H1_OCPOL_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX25_H1_TLL_BIT; + + if (flags & MXC_EHCI_INTERNAL_PHY) + v |= MX25_H1_USBTE_BIT; + + if (flags & MXC_EHCI_IPPUE_DOWN) + v |= MX25_H1_IPPUE_DOWN_BIT; + + if (flags & MXC_EHCI_IPPUE_UP) + v |= MX25_H1_IPPUE_UP_BIT; + + break; + default: + return -EINVAL; + } +#elif defined(CONFIG_MX31) + switch (port) { + case 0: /* OTG port */ + v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_OTG_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX31_OTG_PM_BIT; + + break; + case 1: /* H1 port */ + v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT | MX31_H1_DT_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H1_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX31_H1_PM_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX31_H1_DT_BIT; + + break; + case 2: /* H2 port */ + v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT | MX31_H2_DT_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H2_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX31_H2_PM_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX31_H2_DT_BIT; + + break; + default: + return -EINVAL; + } +#elif defined(CONFIG_MX35) + switch (port) { + case 0: /* OTG port */ + v &= ~(MX35_OTG_SIC_MASK | MX35_OTG_PM_BIT | MX35_OTG_PP_BIT | + MX35_OTG_OCPOL_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_OTG_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX35_OTG_PM_BIT; + + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MX35_OTG_PP_BIT; + + if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) + v |= MX35_OTG_OCPOL_BIT; + + break; + case 1: /* H1 port */ + v &= ~(MX35_H1_SIC_MASK | MX35_H1_PM_BIT | MX35_H1_PP_BIT | + MX35_H1_OCPOL_BIT | MX35_H1_TLL_BIT | + MX35_H1_USBTE_BIT | MX35_H1_IPPUE_DOWN_BIT | + MX35_H1_IPPUE_UP_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_H1_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX35_H1_PM_BIT; + + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MX35_H1_PP_BIT; + + if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) + v |= MX35_H1_OCPOL_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX35_H1_TLL_BIT; + + if (flags & MXC_EHCI_INTERNAL_PHY) + v |= MX35_H1_USBTE_BIT; + + if (flags & MXC_EHCI_IPPUE_DOWN) + v |= MX35_H1_IPPUE_DOWN_BIT; + + if (flags & MXC_EHCI_IPPUE_UP) + v |= MX35_H1_IPPUE_UP_BIT; + + break; + default: + return -EINVAL; + } +#else +#error MXC EHCI USB driver not supported on this platform +#endif + writel(v, IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET); + + return 0; +} + +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + struct usb_ehci *ehci; +#ifdef CONFIG_MX31 + struct clock_control_regs *sc_regs = + (struct clock_control_regs *)CCM_BASE; + + __raw_readl(&sc_regs->ccmr); + __raw_writel(__raw_readl(&sc_regs->ccmr) | (1 << 9), &sc_regs->ccmr) ; +#endif + + udelay(80); + + ehci = (struct usb_ehci *)(IMX_USB_BASE + + IMX_USB_PORT_OFFSET * CONFIG_MXC_USB_PORT); + *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); + *hcor = (struct ehci_hcor *)((uint32_t) *hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + setbits_le32(&ehci->usbmode, CM_HOST); + __raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); + mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS); +#ifdef CONFIG_MX35 + /* Workaround for ENGcm11601 */ + __raw_writel(0, &ehci->sbuscfg); +#endif + + udelay(10000); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-mxs.c b/qemu/roms/u-boot/drivers/usb/host/ehci-mxs.c new file mode 100644 index 000000000..4d652b32d --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-mxs.c @@ -0,0 +1,158 @@ +/* + * Freescale i.MX28 USB Host driver + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#include "ehci.h" + +/* This DIGCTL register ungates clock to USB */ +#define HW_DIGCTL_CTRL 0x8001c000 +#define HW_DIGCTL_CTRL_USB0_CLKGATE (1 << 2) +#define HW_DIGCTL_CTRL_USB1_CLKGATE (1 << 16) + +struct ehci_mxs_port { + uint32_t usb_regs; + struct mxs_usbphy_regs *phy_regs; + + struct mxs_register_32 *pll; + uint32_t pll_en_bits; + uint32_t pll_dis_bits; + uint32_t gate_bits; +}; + +static const struct ehci_mxs_port mxs_port[] = { +#ifdef CONFIG_EHCI_MXS_PORT0 + { + MXS_USBCTRL0_BASE, + (struct mxs_usbphy_regs *)MXS_USBPHY0_BASE, + (struct mxs_register_32 *)(MXS_CLKCTRL_BASE + + offsetof(struct mxs_clkctrl_regs, + hw_clkctrl_pll0ctrl0_reg)), + CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER, + CLKCTRL_PLL0CTRL0_EN_USB_CLKS, + HW_DIGCTL_CTRL_USB0_CLKGATE, + }, +#endif +#ifdef CONFIG_EHCI_MXS_PORT1 + { + MXS_USBCTRL1_BASE, + (struct mxs_usbphy_regs *)MXS_USBPHY1_BASE, + (struct mxs_register_32 *)(MXS_CLKCTRL_BASE + + offsetof(struct mxs_clkctrl_regs, + hw_clkctrl_pll1ctrl0_reg)), + CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER, + CLKCTRL_PLL1CTRL0_EN_USB_CLKS, + HW_DIGCTL_CTRL_USB1_CLKGATE, + }, +#endif +}; + +static int ehci_mxs_toggle_clock(const struct ehci_mxs_port *port, int enable) +{ + struct mxs_register_32 *digctl_ctrl = + (struct mxs_register_32 *)HW_DIGCTL_CTRL; + int pll_offset, dig_offset; + + if (enable) { + pll_offset = offsetof(struct mxs_register_32, reg_set); + dig_offset = offsetof(struct mxs_register_32, reg_clr); + writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset); + writel(port->pll_en_bits, (u32)port->pll + pll_offset); + } else { + pll_offset = offsetof(struct mxs_register_32, reg_clr); + dig_offset = offsetof(struct mxs_register_32, reg_set); + writel(port->pll_dis_bits, (u32)port->pll + pll_offset); + writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset); + } + + return 0; +} + +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + + int ret; + uint32_t usb_base, cap_base; + const struct ehci_mxs_port *port; + + if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) { + printf("Invalid port index (index = %d)!\n", index); + return -EINVAL; + } + + port = &mxs_port[index]; + + /* Reset the PHY block */ + writel(USBPHY_CTRL_SFTRST, &port->phy_regs->hw_usbphy_ctrl_set); + udelay(10); + writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, + &port->phy_regs->hw_usbphy_ctrl_clr); + + /* Enable USB clock */ + ret = ehci_mxs_toggle_clock(port, 1); + if (ret) + return ret; + + /* Start USB PHY */ + writel(0, &port->phy_regs->hw_usbphy_pwd); + + /* Enable UTMI+ Level 2 and Level 3 compatibility */ + writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1, + &port->phy_regs->hw_usbphy_ctrl_set); + + usb_base = port->usb_regs + 0x100; + *hccr = (struct ehci_hccr *)usb_base; + + cap_base = ehci_readl(&(*hccr)->cr_capbase); + *hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base)); + + return 0; +} + +int ehci_hcd_stop(int index) +{ + int ret; + uint32_t usb_base, cap_base, tmp; + struct ehci_hccr *hccr; + struct ehci_hcor *hcor; + const struct ehci_mxs_port *port; + + if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) { + printf("Invalid port index (index = %d)!\n", index); + return -EINVAL; + } + + port = &mxs_port[index]; + + /* Stop the USB port */ + usb_base = port->usb_regs + 0x100; + hccr = (struct ehci_hccr *)usb_base; + cap_base = ehci_readl(&hccr->cr_capbase); + hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base)); + + tmp = ehci_readl(&hcor->or_usbcmd); + tmp &= ~CMD_RUN; + ehci_writel(tmp, &hcor->or_usbcmd); + + /* Disable the PHY */ + tmp = USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF | + USBPHY_PWD_RXPWD1PT1 | USBPHY_PWD_RXPWDENV | + USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS | + USBPHY_PWD_TXPWDFS; + writel(tmp, &port->phy_regs->hw_usbphy_pwd); + + /* Disable USB clock */ + ret = ehci_mxs_toggle_clock(port, 0); + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-omap.c b/qemu/roms/u-boot/drivers/usb/host/ehci-omap.c new file mode 100644 index 000000000..1b215c25f --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-omap.c @@ -0,0 +1,295 @@ +/* + * (C) Copyright 2011 Ilya Yanok, Emcraft Systems + * (C) Copyright 2004-2008 + * Texas Instruments, + * + * Derived from Beagle Board code by + * Sunil Kumar + * Shashi Ranjan + * + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +static struct omap_uhh *const uhh = (struct omap_uhh *)OMAP_UHH_BASE; +static struct omap_usbtll *const usbtll = (struct omap_usbtll *)OMAP_USBTLL_BASE; +static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE; + +static int omap_uhh_reset(void) +{ + int timeout = 0; + u32 rev; + + rev = readl(&uhh->rev); + + /* Soft RESET */ + writel(OMAP_UHH_SYSCONFIG_SOFTRESET, &uhh->sysc); + + switch (rev) { + case OMAP_USBHS_REV1: + /* Wait for soft RESET to complete */ + while (!(readl(&uhh->syss) & 0x1)) { + if (timeout > 100) { + printf("%s: RESET timeout\n", __func__); + return -1; + } + udelay(10); + timeout++; + } + + /* Set No-Idle, No-Standby */ + writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); + break; + + default: /* Rev. 2 onwards */ + + udelay(2); /* Need to wait before accessing SYSCONFIG back */ + + /* Wait for soft RESET to complete */ + while ((readl(&uhh->sysc) & 0x1)) { + if (timeout > 100) { + printf("%s: RESET timeout\n", __func__); + return -1; + } + udelay(10); + timeout++; + } + + writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); + break; + } + + return 0; +} + +static int omap_ehci_tll_reset(void) +{ + unsigned long init = get_timer(0); + + /* perform TLL soft reset, and wait until reset is complete */ + writel(OMAP_USBTLL_SYSCONFIG_SOFTRESET, &usbtll->sysc); + + /* Wait for TLL reset to complete */ + while (!(readl(&usbtll->syss) & OMAP_USBTLL_SYSSTATUS_RESETDONE)) + if (get_timer(init) > CONFIG_SYS_HZ) { + debug("OMAP EHCI error: timeout resetting TLL\n"); + return -EL3RST; + } + + return 0; +} + +static void omap_usbhs_hsic_init(int port) +{ + unsigned int reg; + + /* Enable channels now */ + reg = readl(&usbtll->channel_conf + port); + + setbits_le32(®, (OMAP_TLL_CHANNEL_CONF_CHANMODE_TRANSPARENT_UTMI + | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF + | OMAP_TLL_CHANNEL_CONF_DRVVBUS + | OMAP_TLL_CHANNEL_CONF_CHRGVBUS + | OMAP_TLL_CHANNEL_CONF_CHANEN)); + + writel(reg, &usbtll->channel_conf + port); +} + +#ifdef CONFIG_USB_ULPI +static void omap_ehci_soft_phy_reset(int port) +{ + struct ulpi_viewport ulpi_vp; + + ulpi_vp.viewport_addr = (u32)&ehci->insreg05_utmi_ulpi; + ulpi_vp.port_num = port; + + ulpi_reset(&ulpi_vp); +} +#else +static void omap_ehci_soft_phy_reset(int port) +{ + return; +} +#endif + +#if defined(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO) || \ + defined(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO) || \ + defined(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO) +/* controls PHY(s) reset signal(s) */ +static inline void omap_ehci_phy_reset(int on, int delay) +{ + /* + * Refer ISSUE1: + * Hold the PHY in RESET for enough time till + * PHY is settled and ready + */ + if (delay && !on) + udelay(delay); +#ifdef CONFIG_OMAP_EHCI_PHY1_RESET_GPIO + gpio_request(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, "USB PHY1 reset"); + gpio_direction_output(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, !on); +#endif +#ifdef CONFIG_OMAP_EHCI_PHY2_RESET_GPIO + gpio_request(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, "USB PHY2 reset"); + gpio_direction_output(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, !on); +#endif +#ifdef CONFIG_OMAP_EHCI_PHY3_RESET_GPIO + gpio_request(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO, "USB PHY3 reset"); + gpio_direction_output(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO, !on); +#endif + + /* Hold the PHY in RESET for enough time till DIR is high */ + /* Refer: ISSUE1 */ + if (delay && on) + udelay(delay); +} +#else +#define omap_ehci_phy_reset(on, delay) do {} while (0) +#endif + +/* Reset is needed otherwise the kernel-driver will throw an error. */ +int omap_ehci_hcd_stop(void) +{ + debug("Resetting OMAP EHCI\n"); + omap_ehci_phy_reset(1, 0); + + if (omap_uhh_reset() < 0) + return -1; + + if (omap_ehci_tll_reset() < 0) + return -1; + + return 0; +} + +/* + * Initialize the OMAP EHCI controller and PHY. + * Based on "drivers/usb/host/ehci-omap.c" from Linux 3.1 + * See there for additional Copyrights. + */ +int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + int ret; + unsigned int i, reg = 0, rev = 0; + + debug("Initializing OMAP EHCI\n"); + + ret = board_usb_init(index, USB_INIT_HOST); + if (ret < 0) + return ret; + + /* Put the PHY in RESET */ + omap_ehci_phy_reset(1, 10); + + ret = omap_uhh_reset(); + if (ret < 0) + return ret; + + ret = omap_ehci_tll_reset(); + if (ret) + return ret; + + writel(OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | + OMAP_USBTLL_SYSCONFIG_SIDLEMODE | + OMAP_USBTLL_SYSCONFIG_CACTIVITY, &usbtll->sysc); + + /* Put UHH in NoIdle/NoStandby mode */ + writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); + + /* setup ULPI bypass and burst configurations */ + clrsetbits_le32(®, OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN, + (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN | + OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN | + OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN)); + + rev = readl(&uhh->rev); + if (rev == OMAP_USBHS_REV1) { + if (is_ehci_phy_mode(usbhs_pdata->port_mode[0])) + clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); + else + setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); + + if (is_ehci_phy_mode(usbhs_pdata->port_mode[1])) + clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS); + else + setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS); + + if (is_ehci_phy_mode(usbhs_pdata->port_mode[2])) + clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS); + else + setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS); + } else if (rev == OMAP_USBHS_REV2) { + + clrsetbits_le32(®, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR), + OMAP4_UHH_HOSTCONFIG_APP_START_CLK); + + /* Clear port mode fields for PHY mode */ + + if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0])) + setbits_le32(®, OMAP_P1_MODE_HSIC); + + if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1])) + setbits_le32(®, OMAP_P2_MODE_HSIC); + + } else if (rev == OMAP_USBHS_REV2_1) { + + clrsetbits_le32(®, + (OMAP_P1_MODE_CLEAR | + OMAP_P2_MODE_CLEAR | + OMAP_P3_MODE_CLEAR), + OMAP4_UHH_HOSTCONFIG_APP_START_CLK); + + /* Clear port mode fields for PHY mode */ + + if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0])) + setbits_le32(®, OMAP_P1_MODE_HSIC); + + if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1])) + setbits_le32(®, OMAP_P2_MODE_HSIC); + + if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2])) + setbits_le32(®, OMAP_P3_MODE_HSIC); + } + + debug("OMAP UHH_REVISION 0x%x\n", rev); + writel(reg, &uhh->hostconfig); + + for (i = 0; i < OMAP_HS_USB_PORTS; i++) + if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i])) + omap_usbhs_hsic_init(i); + + omap_ehci_phy_reset(0, 10); + + /* + * 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 ehci_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. + */ + writel(EHCI_INSNREG04_DISABLE_UNSUSPEND, &ehci->insreg04); + + for (i = 0; i < OMAP_HS_USB_PORTS; i++) + if (is_ehci_phy_mode(usbhs_pdata->port_mode[i])) + omap_ehci_soft_phy_reset(i); + + *hccr = (struct ehci_hccr *)(OMAP_EHCI_BASE); + *hcor = (struct ehci_hcor *)(OMAP_EHCI_BASE + 0x10); + + debug("OMAP EHCI init done\n"); + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-pci.c b/qemu/roms/u-boot/drivers/usb/host/ehci-pci.c new file mode 100644 index 000000000..991b19998 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-pci.c @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "ehci.h" + +#ifdef CONFIG_PCI_EHCI_DEVICE +static struct pci_device_id ehci_pci_ids[] = { + /* Please add supported PCI EHCI controller ids here */ + {0x1033, 0x00E0}, /* NEC */ + {0x10B9, 0x5239}, /* ULI1575 PCI EHCI module ids */ + {0x12D8, 0x400F}, /* Pericom */ + {0, 0} +}; +#else +static pci_dev_t ehci_find_class(int index) +{ + int bus; + int devnum; + pci_dev_t bdf; + uint32_t class; + + for (bus = 0; bus <= pci_last_busno(); bus++) { + for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++) { + pci_read_config_dword(PCI_BDF(bus, devnum, 0), + PCI_CLASS_REVISION, &class); + if (class >> 16 == 0xffff) + continue; + + for (bdf = PCI_BDF(bus, devnum, 0); + bdf <= PCI_BDF(bus, devnum, + PCI_MAX_PCI_FUNCTIONS - 1); + bdf += PCI_BDF(0, 0, 1)) { + pci_read_config_dword(bdf, PCI_CLASS_REVISION, + &class); + class >>= 8; + /* + * Here be dragons! In case we have multiple + * PCI EHCI controllers, this function will + * be called multiple times as well. This + * function will scan the PCI busses, always + * starting from bus 0, device 0, function 0, + * until it finds an USB controller. The USB + * stack gives us an 'index' of a controller + * that is currently being registered, which + * is a number, starting from 0 and growing + * in ascending order as controllers are added. + * To avoid probing the same controller in tne + * subsequent runs of this function, we will + * skip 'index - 1' detected controllers and + * report the index'th controller. + */ + if (class != PCI_CLASS_SERIAL_USB_EHCI) + continue; + if (index) { + index--; + continue; + } + /* Return index'th controller. */ + return bdf; + } + } + } + + return -ENODEV; +} +#endif + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **ret_hccr, struct ehci_hcor **ret_hcor) +{ + pci_dev_t pdev; + uint32_t cmd; + struct ehci_hccr *hccr; + struct ehci_hcor *hcor; + +#ifdef CONFIG_PCI_EHCI_DEVICE + pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE); +#else + pdev = ehci_find_class(index); +#endif + if (pdev < 0) { + printf("EHCI host controller not found\n"); + return -1; + } + + hccr = (struct ehci_hccr *)pci_map_bar(pdev, + PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n", + (uint32_t)hccr, (uint32_t)hcor, + (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + *ret_hccr = hccr; + *ret_hcor = hcor; + + /* enable busmaster */ + pci_read_config_dword(pdev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER; + pci_write_config_dword(pdev, PCI_COMMAND, cmd); + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-ppc4xx.c b/qemu/roms/u-boot/drivers/usb/host/ehci-ppc4xx.c new file mode 100644 index 000000000..9aee3ff78 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-ppc4xx.c @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2010, Chris Zhang + * + * Author: Chris Zhang + * This code is based on ehci freescale driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include + +#include "ehci.h" + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + *hccr = (struct ehci_hccr *)(CONFIG_SYS_PPC4XX_USB_ADDR); + *hcor = (struct ehci_hcor *)((uint32_t) *hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-rmobile.c b/qemu/roms/u-boot/drivers/usb/host/ehci-rmobile.c new file mode 100644 index 000000000..049e4c4e6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-rmobile.c @@ -0,0 +1,130 @@ +/* + * EHCI HCD (Host Controller Driver) for USB. + * + * Copyright (C) 2013,2014 Renesas Electronics Corporation + * Copyright (C) 2014 Nobuhiro Iwamatsu + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include "ehci.h" + +#if defined(CONFIG_R8A7740) +static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { + 0xC6700000 +}; +#elif defined(CONFIG_R8A7790) +static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { + 0xEE080000, /* USB0 (EHCI) */ + 0xEE0A0000, /* USB1 */ + 0xEE0C0000, /* USB2 */ + 0xEE000000 /* USB3 (USB3.0 Host)*/ +}; +#elif defined(CONFIG_R8A7791) +static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { + 0xEE080000, /* USB0 (EHCI) */ + 0xEE0C0000, /* USB1 */ + 0xEE000000 /* USB3 (USB3.0 Host)*/ +}; +#else +#error rmobile EHCI USB driver not supported on this platform +#endif + +int ehci_hcd_stop(int index) +{ + int i; + u32 base; + struct ahbcom_pci_bridge *ahbcom_pci; + + base = usb_base_address[index]; + ahbcom_pci = (struct ahbcom_pci_bridge *)(base + AHBPCI_OFFSET); + writel(0, &ahbcom_pci->ahb_bus_ctr); + + /* reset ehci */ + setbits_le32(base + EHCI_USBCMD, CMD_RESET); + for (i = 100; i > 0; i--) { + if (!(readl(base + EHCI_USBCMD) & CMD_RESET)) + break; + udelay(100); + } + + if (!i) + printf("error : ehci(%d) reset failed.\n", index); + + if (index == (CONFIG_USB_MAX_CONTROLLER_COUNT - 1)) + setbits_le32(SMSTPCR7, SMSTPCR703); + + return 0; +} + +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + u32 base; + u32 phys_base; + struct rmobile_ehci_reg *rehci; + struct ahbcom_pci_bridge *ahbcom_pci; + struct ahbconf_pci_bridge *ahbconf_pci; + struct ahb_pciconf *ahb_pciconf_ohci; + struct ahb_pciconf *ahb_pciconf_ehci; + uint32_t cap_base; + + base = usb_base_address[index]; + phys_base = base; + if (index == 0) + clrbits_le32(SMSTPCR7, SMSTPCR703); + + rehci = (struct rmobile_ehci_reg *)(base + EHCI_OFFSET); + ahbcom_pci = (struct ahbcom_pci_bridge *)(base + AHBPCI_OFFSET); + ahbconf_pci = + (struct ahbconf_pci_bridge *)(base + PCI_CONF_AHBPCI_OFFSET); + ahb_pciconf_ohci = (struct ahb_pciconf *)(base + PCI_CONF_OHCI_OFFSET); + ahb_pciconf_ehci = (struct ahb_pciconf *)(base + PCI_CONF_EHCI_OFFSET); + + /* Clock & Reset & Direct Power Down */ + clrsetbits_le32(&ahbcom_pci->usbctr, + (DIRPD | PCICLK_MASK | USBH_RST), USBCTR_WIN_SIZE_1GB); + clrbits_le32(&ahbcom_pci->usbctr, PLL_RST); + + /* AHB-PCI Bridge Communication Registers */ + writel(AHB_BUS_CTR_INIT, &ahbcom_pci->ahb_bus_ctr); + writel((CONFIG_SYS_SDRAM_BASE & 0xf0000000) | PCIAHB_WIN_PREFETCH, + &ahbcom_pci->pciahb_win1_ctr); + writel(0xf0000000 | PCIAHB_WIN_PREFETCH, + &ahbcom_pci->pciahb_win2_ctr); + writel(phys_base | PCIWIN2_PCICMD, &ahbcom_pci->ahbpci_win2_ctr); + + setbits_le32(&ahbcom_pci->pci_arbiter_ctr, + PCIBP_MODE | PCIREQ1 | PCIREQ0); + + /* PCI Configuration Registers for AHBPCI */ + writel(PCIWIN1_PCICMD | AHB_CFG_AHBPCI, + &ahbcom_pci->ahbpci_win1_ctr); + writel(phys_base + AHBPCI_OFFSET, &ahbconf_pci->basead); + writel(CONFIG_SYS_SDRAM_BASE & 0xf0000000, &ahbconf_pci->win1_basead); + writel(0xf0000000, &ahbconf_pci->win2_basead); + writel(SERREN | PERREN | MASTEREN | MEMEN, + &ahbconf_pci->cmnd_sts); + + /* PCI Configuration Registers for EHCI */ + writel(PCIWIN1_PCICMD | AHB_CFG_HOST, &ahbcom_pci->ahbpci_win1_ctr); + writel(phys_base + OHCI_OFFSET, &ahb_pciconf_ohci->basead); + writel(phys_base + EHCI_OFFSET, &ahb_pciconf_ehci->basead); + writel(SERREN | PERREN | MASTEREN | MEMEN, + &ahb_pciconf_ohci->cmnd_sts); + writel(SERREN | PERREN | MASTEREN | MEMEN, + &ahb_pciconf_ehci->cmnd_sts); + + /* Enable PCI interrupt */ + setbits_le32(&ahbcom_pci->pci_int_enable, + USBH_PMEEN | USBH_INTBEN | USBH_INTAEN); + + *hccr = (struct ehci_hccr *)((uint32_t)&rehci->hciversion); + cap_base = ehci_readl(&(*hccr)->cr_capbase); + *hcor = (struct ehci_hcor *)((uint32_t)*hccr + HC_LENGTH(cap_base)); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-spear.c b/qemu/roms/u-boot/drivers/usb/host/ehci-spear.c new file mode 100644 index 000000000..210ee9e88 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-spear.c @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2010 + * Armando Visconti, ST Micoelectronics, . + * + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "ehci.h" +#include + + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + *hccr = (struct ehci_hccr *)(CONFIG_SYS_UHC0_EHCI_BASE + 0x100); + *hcor = (struct ehci_hcor *)((uint32_t)*hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + debug("SPEAr-ehci: init hccr %x and hcor %x hc_length %d\n", + (uint32_t)*hccr, (uint32_t)*hcor, + (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-tegra.c b/qemu/roms/u-boot/drivers/usb/host/ehci-tegra.c new file mode 100644 index 000000000..38db18e2c --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-tegra.c @@ -0,0 +1,827 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (c) 2009-2013 NVIDIA Corporation + * Copyright (c) 2013 Lucas Stach + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +#define USB1_ADDR_MASK 0xFFFF0000 + +#define HOSTPC1_DEVLC 0x84 +#define HOSTPC1_PSPD(x) (((x) >> 25) & 0x3) + +#ifdef CONFIG_USB_ULPI + #ifndef CONFIG_USB_ULPI_VIEWPORT + #error "To use CONFIG_USB_ULPI on Tegra Boards you have to also \ + define CONFIG_USB_ULPI_VIEWPORT" + #endif +#endif + +enum { + USB_PORTS_MAX = 3, /* Maximum ports we allow */ +}; + +/* Parameters we need for USB */ +enum { + PARAM_DIVN, /* PLL FEEDBACK DIVIDer */ + PARAM_DIVM, /* PLL INPUT DIVIDER */ + PARAM_DIVP, /* POST DIVIDER (2^N) */ + PARAM_CPCON, /* BASE PLLC CHARGE Pump setup ctrl */ + PARAM_LFCON, /* BASE PLLC LOOP FILter setup ctrl */ + PARAM_ENABLE_DELAY_COUNT, /* PLL-U Enable Delay Count */ + PARAM_STABLE_COUNT, /* PLL-U STABLE count */ + PARAM_ACTIVE_DELAY_COUNT, /* PLL-U Active delay count */ + PARAM_XTAL_FREQ_COUNT, /* PLL-U XTAL frequency count */ + PARAM_DEBOUNCE_A_TIME, /* 10MS DELAY for BIAS_DEBOUNCE_A */ + PARAM_BIAS_TIME, /* 20US DELAY AFter bias cell op */ + + PARAM_COUNT +}; + +/* Possible port types (dual role mode) */ +enum dr_mode { + DR_MODE_NONE = 0, + DR_MODE_HOST, /* supports host operation */ + DR_MODE_DEVICE, /* supports device operation */ + DR_MODE_OTG, /* supports both */ +}; + +/* Information about a USB port */ +struct fdt_usb { + struct usb_ctlr *reg; /* address of registers in physical memory */ + unsigned utmi:1; /* 1 if port has external tranceiver, else 0 */ + unsigned ulpi:1; /* 1 if port has external ULPI transceiver */ + unsigned enabled:1; /* 1 to enable, 0 to disable */ + unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */ + unsigned initialized:1; /* has this port already been initialized? */ + enum dr_mode dr_mode; /* dual role mode */ + enum periph_id periph_id;/* peripheral id */ + struct fdt_gpio_state vbus_gpio; /* GPIO for vbus enable */ + struct fdt_gpio_state phy_reset_gpio; /* GPIO to reset ULPI phy */ +}; + +static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */ +static unsigned port_count; /* Number of available ports */ +/* Port that needs to clear CSC after Port Reset */ +static u32 port_addr_clear_csc; + +/* + * This table has USB timing parameters for each Oscillator frequency we + * support. There are four sets of values: + * + * 1. PLLU configuration information (reference clock is osc/clk_m and + * PLLU-FOs are fixed at 12MHz/60MHz/480MHz). + * + * Reference frequency 13.0MHz 19.2MHz 12.0MHz 26.0MHz + * ---------------------------------------------------------------------- + * DIVN 960 (0x3c0) 200 (0c8) 960 (3c0h) 960 (3c0) + * DIVM 13 (0d) 4 (04) 12 (0c) 26 (1a) + * Filter frequency (MHz) 1 4.8 6 2 + * CPCON 1100b 0011b 1100b 1100b + * LFCON0 0 0 0 0 + * + * 2. PLL CONFIGURATION & PARAMETERS for different clock generators: + * + * Reference frequency 13.0MHz 19.2MHz 12.0MHz 26.0MHz + * --------------------------------------------------------------------------- + * PLLU_ENABLE_DLY_COUNT 02 (0x02) 03 (03) 02 (02) 04 (04) + * PLLU_STABLE_COUNT 51 (33) 75 (4B) 47 (2F) 102 (66) + * PLL_ACTIVE_DLY_COUNT 05 (05) 06 (06) 04 (04) 09 (09) + * XTAL_FREQ_COUNT 127 (7F) 187 (BB) 118 (76) 254 (FE) + * + * 3. Debounce values IdDig, Avalid, Bvalid, VbusValid, VbusWakeUp, and + * SessEnd. Each of these signals have their own debouncer and for each of + * those one out of two debouncing times can be chosen (BIAS_DEBOUNCE_A or + * BIAS_DEBOUNCE_B). + * + * The values of DEBOUNCE_A and DEBOUNCE_B are calculated as follows: + * 0xffff -> No debouncing at all + * ms = *1000 / (1/19.2MHz) / 4 + * + * So to program a 1 ms debounce for BIAS_DEBOUNCE_A, we have: + * BIAS_DEBOUNCE_A[15:0] = 1000 * 19.2 / 4 = 4800 = 0x12c0 + * + * We need to use only DebounceA for BOOTROM. We don't need the DebounceB + * values, so we can keep those to default. + * + * 4. The 20 microsecond delay after bias cell operation. + */ +static const unsigned T20_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { + /* DivN, DivM, DivP, CPCON, LFCON, Delays Debounce, Bias */ + { 0x3C0, 0x0D, 0x00, 0xC, 0, 0x02, 0x33, 0x05, 0x7F, 0x7EF4, 5 }, + { 0x0C8, 0x04, 0x00, 0x3, 0, 0x03, 0x4B, 0x06, 0xBB, 0xBB80, 7 }, + { 0x3C0, 0x0C, 0x00, 0xC, 0, 0x02, 0x2F, 0x04, 0x76, 0x7530, 5 }, + { 0x3C0, 0x1A, 0x00, 0xC, 0, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 } +}; + +static const unsigned T30_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { + /* DivN, DivM, DivP, CPCON, LFCON, Delays Debounce, Bias */ + { 0x3C0, 0x0D, 0x00, 0xC, 1, 0x02, 0x33, 0x09, 0x7F, 0x7EF4, 5 }, + { 0x0C8, 0x04, 0x00, 0x3, 0, 0x03, 0x4B, 0x0C, 0xBB, 0xBB80, 7 }, + { 0x3C0, 0x0C, 0x00, 0xC, 1, 0x02, 0x2F, 0x08, 0x76, 0x7530, 5 }, + { 0x3C0, 0x1A, 0x00, 0xC, 1, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 } +}; + +static const unsigned T114_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { + /* DivN, DivM, DivP, CPCON, LFCON, Delays Debounce, Bias */ + { 0x3C0, 0x0D, 0x00, 0xC, 2, 0x02, 0x33, 0x09, 0x7F, 0x7EF4, 6 }, + { 0x0C8, 0x04, 0x00, 0x3, 2, 0x03, 0x4B, 0x0C, 0xBB, 0xBB80, 8 }, + { 0x3C0, 0x0C, 0x00, 0xC, 2, 0x02, 0x2F, 0x08, 0x76, 0x7530, 5 }, + { 0x3C0, 0x1A, 0x00, 0xC, 2, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 0xB } +}; + +/* UTMIP Idle Wait Delay */ +static const u8 utmip_idle_wait_delay = 17; + +/* UTMIP Elastic limit */ +static const u8 utmip_elastic_limit = 16; + +/* UTMIP High Speed Sync Start Delay */ +static const u8 utmip_hs_sync_start_delay = 9; + +struct fdt_usb_controller { + int compat; + /* flag to determine whether controller supports hostpc register */ + u32 has_hostpc:1; + const unsigned *pll_parameter; +}; + +static struct fdt_usb_controller fdt_usb_controllers[] = { + { + .compat = COMPAT_NVIDIA_TEGRA20_USB, + .has_hostpc = 0, + .pll_parameter = (const unsigned *)T20_usb_pll, + }, + { + .compat = COMPAT_NVIDIA_TEGRA30_USB, + .has_hostpc = 1, + .pll_parameter = (const unsigned *)T30_usb_pll, + }, + { + .compat = COMPAT_NVIDIA_TEGRA114_USB, + .has_hostpc = 1, + .pll_parameter = (const unsigned *)T114_usb_pll, + }, +}; + +static struct fdt_usb_controller *controller; + +/* + * A known hardware issue where Connect Status Change bit of PORTSC register + * of USB1 controller will be set after Port Reset. + * We have to clear it in order for later device enumeration to proceed. + * This ehci_powerup_fixup overrides the weak function ehci_powerup_fixup + * in "ehci-hcd.c". + */ +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +{ + mdelay(50); + /* This is to avoid PORT_ENABLE bit to be cleared in "ehci-hcd.c". */ + if (controller->has_hostpc) + *reg |= EHCI_PS_PE; + + if (((u32)status_reg & TEGRA_USB_ADDR_MASK) != port_addr_clear_csc) + return; + /* For EHCI_PS_CSC to be cleared in ehci_hcd.c */ + if (ehci_readl(status_reg) & EHCI_PS_CSC) + *reg |= EHCI_PS_CSC; +} + +/* + * This ehci_set_usbmode overrides the weak function ehci_set_usbmode + * in "ehci-hcd.c". + */ +void ehci_set_usbmode(int index) +{ + struct fdt_usb *config; + struct usb_ctlr *usbctlr; + uint32_t tmp; + + config = &port[index]; + usbctlr = config->reg; + + tmp = ehci_readl(&usbctlr->usb_mode); + tmp |= USBMODE_CM_HC; + ehci_writel(&usbctlr->usb_mode, tmp); +} + +/* + * This ehci_get_port_speed overrides the weak function ehci_get_port_speed + * in "ehci-hcd.c". + */ +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ + uint32_t tmp; + uint32_t *reg_ptr; + + if (controller->has_hostpc) { + reg_ptr = (uint32_t *)((u8 *)&hcor->or_usbcmd + HOSTPC1_DEVLC); + tmp = ehci_readl(reg_ptr); + return HOSTPC1_PSPD(tmp); + } else + return PORTSC_PSPD(reg); +} + +/* Put the port into host mode */ +static void set_host_mode(struct fdt_usb *config) +{ + /* + * If we are an OTG port, check if remote host is driving VBus and + * bail out in this case. + */ + if (config->dr_mode == DR_MODE_OTG && + (readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)) + return; + + /* + * If not driving, we set the GPIO to enable VBUS. We assume + * that the pinmux is set up correctly for this. + */ + if (fdt_gpio_isvalid(&config->vbus_gpio)) { + fdtdec_setup_gpio(&config->vbus_gpio); + gpio_direction_output(config->vbus_gpio.gpio, + (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ? + 0 : 1); + debug("set_host_mode: GPIO %d %s\n", config->vbus_gpio.gpio, + (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ? + "low" : "high"); + } +} + +void usbf_reset_controller(struct fdt_usb *config, struct usb_ctlr *usbctlr) +{ + /* Reset the USB controller with 2us delay */ + reset_periph(config->periph_id, 2); + + /* + * Set USB1_NO_LEGACY_MODE to 1, Registers are accessible under + * base address + */ + if (config->has_legacy_mode) + setbits_le32(&usbctlr->usb1_legacy_ctrl, USB1_NO_LEGACY_MODE); + + /* Put UTMIP1/3 in reset */ + setbits_le32(&usbctlr->susp_ctrl, UTMIP_RESET); + + /* Enable the UTMIP PHY */ + if (config->utmi) + setbits_le32(&usbctlr->susp_ctrl, UTMIP_PHY_ENB); +} + +static const unsigned *get_pll_timing(void) +{ + const unsigned *timing; + + timing = controller->pll_parameter + + clock_get_osc_freq() * PARAM_COUNT; + + return timing; +} + +/* set up the UTMI USB controller with the parameters provided */ +static int init_utmi_usb_controller(struct fdt_usb *config) +{ + u32 val; + int loop_count; + const unsigned *timing; + struct usb_ctlr *usbctlr = config->reg; + struct clk_rst_ctlr *clkrst; + struct usb_ctlr *usb1ctlr; + + clock_enable(config->periph_id); + + /* Reset the usb controller */ + usbf_reset_controller(config, usbctlr); + + /* Stop crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN low */ + clrbits_le32(&usbctlr->utmip_misc_cfg1, UTMIP_PHY_XTAL_CLOCKEN); + + /* Follow the crystal clock disable by >100ns delay */ + udelay(1); + + /* + * To Use the A Session Valid for cable detection logic, VBUS_WAKEUP + * mux must be switched to actually use a_sess_vld threshold. + */ + if (config->dr_mode == DR_MODE_OTG && + fdt_gpio_isvalid(&config->vbus_gpio)) + clrsetbits_le32(&usbctlr->usb1_legacy_ctrl, + VBUS_SENSE_CTL_MASK, + VBUS_SENSE_CTL_A_SESS_VLD << VBUS_SENSE_CTL_SHIFT); + + /* + * PLL Delay CONFIGURATION settings. The following parameters control + * the bring up of the plls. + */ + timing = get_pll_timing(); + + if (!controller->has_hostpc) { + val = readl(&usbctlr->utmip_misc_cfg1); + clrsetbits_le32(&val, UTMIP_PLLU_STABLE_COUNT_MASK, + timing[PARAM_STABLE_COUNT] << + UTMIP_PLLU_STABLE_COUNT_SHIFT); + clrsetbits_le32(&val, UTMIP_PLL_ACTIVE_DLY_COUNT_MASK, + timing[PARAM_ACTIVE_DELAY_COUNT] << + UTMIP_PLL_ACTIVE_DLY_COUNT_SHIFT); + writel(val, &usbctlr->utmip_misc_cfg1); + + /* Set PLL enable delay count and crystal frequency count */ + val = readl(&usbctlr->utmip_pll_cfg1); + clrsetbits_le32(&val, UTMIP_PLLU_ENABLE_DLY_COUNT_MASK, + timing[PARAM_ENABLE_DELAY_COUNT] << + UTMIP_PLLU_ENABLE_DLY_COUNT_SHIFT); + clrsetbits_le32(&val, UTMIP_XTAL_FREQ_COUNT_MASK, + timing[PARAM_XTAL_FREQ_COUNT] << + UTMIP_XTAL_FREQ_COUNT_SHIFT); + writel(val, &usbctlr->utmip_pll_cfg1); + } else { + clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + + val = readl(&clkrst->crc_utmip_pll_cfg2); + clrsetbits_le32(&val, UTMIP_PLLU_STABLE_COUNT_MASK, + timing[PARAM_STABLE_COUNT] << + UTMIP_PLLU_STABLE_COUNT_SHIFT); + clrsetbits_le32(&val, UTMIP_PLL_ACTIVE_DLY_COUNT_MASK, + timing[PARAM_ACTIVE_DELAY_COUNT] << + UTMIP_PLL_ACTIVE_DLY_COUNT_SHIFT); + writel(val, &clkrst->crc_utmip_pll_cfg2); + + /* Set PLL enable delay count and crystal frequency count */ + val = readl(&clkrst->crc_utmip_pll_cfg1); + clrsetbits_le32(&val, UTMIP_PLLU_ENABLE_DLY_COUNT_MASK, + timing[PARAM_ENABLE_DELAY_COUNT] << + UTMIP_PLLU_ENABLE_DLY_COUNT_SHIFT); + clrsetbits_le32(&val, UTMIP_XTAL_FREQ_COUNT_MASK, + timing[PARAM_XTAL_FREQ_COUNT] << + UTMIP_XTAL_FREQ_COUNT_SHIFT); + writel(val, &clkrst->crc_utmip_pll_cfg1); + + /* Disable Power Down state for PLL */ + clrbits_le32(&clkrst->crc_utmip_pll_cfg1, + PLLU_POWERDOWN | PLL_ENABLE_POWERDOWN | + PLL_ACTIVE_POWERDOWN); + + /* Recommended PHY settings for EYE diagram */ + val = readl(&usbctlr->utmip_xcvr_cfg0); + clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK, + 0x4 << UTMIP_XCVR_SETUP_SHIFT); + clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK, + 0x3 << UTMIP_XCVR_SETUP_MSB_SHIFT); + clrsetbits_le32(&val, UTMIP_XCVR_HSSLEW_MSB_MASK, + 0x8 << UTMIP_XCVR_HSSLEW_MSB_SHIFT); + writel(val, &usbctlr->utmip_xcvr_cfg0); + clrsetbits_le32(&usbctlr->utmip_xcvr_cfg1, + UTMIP_XCVR_TERM_RANGE_ADJ_MASK, + 0x7 << UTMIP_XCVR_TERM_RANGE_ADJ_SHIFT); + + /* Some registers can be controlled from USB1 only. */ + if (config->periph_id != PERIPH_ID_USBD) { + clock_enable(PERIPH_ID_USBD); + /* Disable Reset if in Reset state */ + reset_set_enable(PERIPH_ID_USBD, 0); + } + usb1ctlr = (struct usb_ctlr *) + ((u32)config->reg & USB1_ADDR_MASK); + val = readl(&usb1ctlr->utmip_bias_cfg0); + setbits_le32(&val, UTMIP_HSDISCON_LEVEL_MSB); + clrsetbits_le32(&val, UTMIP_HSDISCON_LEVEL_MASK, + 0x1 << UTMIP_HSDISCON_LEVEL_SHIFT); + clrsetbits_le32(&val, UTMIP_HSSQUELCH_LEVEL_MASK, + 0x2 << UTMIP_HSSQUELCH_LEVEL_SHIFT); + writel(val, &usb1ctlr->utmip_bias_cfg0); + + /* Miscellaneous setting mentioned in Programming Guide */ + clrbits_le32(&usbctlr->utmip_misc_cfg0, + UTMIP_SUSPEND_EXIT_ON_EDGE); + } + + /* Setting the tracking length time */ + clrsetbits_le32(&usbctlr->utmip_bias_cfg1, + UTMIP_BIAS_PDTRK_COUNT_MASK, + timing[PARAM_BIAS_TIME] << UTMIP_BIAS_PDTRK_COUNT_SHIFT); + + /* Program debounce time for VBUS to become valid */ + clrsetbits_le32(&usbctlr->utmip_debounce_cfg0, + UTMIP_DEBOUNCE_CFG0_MASK, + timing[PARAM_DEBOUNCE_A_TIME] << UTMIP_DEBOUNCE_CFG0_SHIFT); + + setbits_le32(&usbctlr->utmip_tx_cfg0, UTMIP_FS_PREAMBLE_J); + + /* Disable battery charge enabling bit */ + setbits_le32(&usbctlr->utmip_bat_chrg_cfg0, UTMIP_PD_CHRG); + + clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_XCVR_LSBIAS_SE); + setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL); + + /* + * Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT + * Setting these fields, together with default values of the + * other fields, results in programming the registers below as + * follows: + * UTMIP_HSRX_CFG0 = 0x9168c000 + * UTMIP_HSRX_CFG1 = 0x13 + */ + + /* Set PLL enable delay count and Crystal frequency count */ + val = readl(&usbctlr->utmip_hsrx_cfg0); + clrsetbits_le32(&val, UTMIP_IDLE_WAIT_MASK, + utmip_idle_wait_delay << UTMIP_IDLE_WAIT_SHIFT); + clrsetbits_le32(&val, UTMIP_ELASTIC_LIMIT_MASK, + utmip_elastic_limit << UTMIP_ELASTIC_LIMIT_SHIFT); + writel(val, &usbctlr->utmip_hsrx_cfg0); + + /* Configure the UTMIP_HS_SYNC_START_DLY */ + clrsetbits_le32(&usbctlr->utmip_hsrx_cfg1, + UTMIP_HS_SYNC_START_DLY_MASK, + utmip_hs_sync_start_delay << UTMIP_HS_SYNC_START_DLY_SHIFT); + + /* Preceed the crystal clock disable by >100ns delay. */ + udelay(1); + + /* Resuscitate crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN */ + setbits_le32(&usbctlr->utmip_misc_cfg1, UTMIP_PHY_XTAL_CLOCKEN); + + if (controller->has_hostpc) { + if (config->periph_id == PERIPH_ID_USBD) + clrbits_le32(&clkrst->crc_utmip_pll_cfg2, + UTMIP_FORCE_PD_SAMP_A_POWERDOWN); + if (config->periph_id == PERIPH_ID_USB2) + clrbits_le32(&clkrst->crc_utmip_pll_cfg2, + UTMIP_FORCE_PD_SAMP_B_POWERDOWN); + if (config->periph_id == PERIPH_ID_USB3) + clrbits_le32(&clkrst->crc_utmip_pll_cfg2, + UTMIP_FORCE_PD_SAMP_C_POWERDOWN); + } + /* Finished the per-controller init. */ + + /* De-assert UTMIP_RESET to bring out of reset. */ + clrbits_le32(&usbctlr->susp_ctrl, UTMIP_RESET); + + /* Wait for the phy clock to become valid in 100 ms */ + for (loop_count = 100000; loop_count != 0; loop_count--) { + if (readl(&usbctlr->susp_ctrl) & USB_PHY_CLK_VALID) + break; + udelay(1); + } + if (!loop_count) + return -1; + + /* Disable ICUSB FS/LS transceiver */ + clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1); + + /* Select UTMI parallel interface */ +#if defined(CONFIG_TEGRA20) + if (config->periph_id == PERIPH_ID_USBD) { + clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK, + PTS_UTMI << PTS1_SHIFT); + clrbits_le32(&usbctlr->port_sc1, STS1); + } else { + clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, + PTS_UTMI << PTS_SHIFT); + clrbits_le32(&usbctlr->port_sc1, STS); + } +#else + clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, + PTS_UTMI << PTS_SHIFT); + clrbits_le32(&usbctlr->hostpc1_devlc, STS); +#endif + + /* Deassert power down state */ + clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN | + UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN); + clrbits_le32(&usbctlr->utmip_xcvr_cfg1, UTMIP_FORCE_PDDISC_POWERDOWN | + UTMIP_FORCE_PDCHRP_POWERDOWN | UTMIP_FORCE_PDDR_POWERDOWN); + + if (controller->has_hostpc) { + /* + * BIAS Pad Power Down is common among all 3 USB + * controllers and can be controlled from USB1 only. + */ + usb1ctlr = (struct usb_ctlr *) + ((u32)config->reg & USB1_ADDR_MASK); + clrbits_le32(&usb1ctlr->utmip_bias_cfg0, UTMIP_BIASPD); + udelay(25); + clrbits_le32(&usb1ctlr->utmip_bias_cfg1, + UTMIP_FORCE_PDTRK_POWERDOWN); + } + return 0; +} + +#ifdef CONFIG_USB_ULPI +/* if board file does not set a ULPI reference frequency we default to 24MHz */ +#ifndef CONFIG_ULPI_REF_CLK +#define CONFIG_ULPI_REF_CLK 24000000 +#endif + +/* set up the ULPI USB controller with the parameters provided */ +static int init_ulpi_usb_controller(struct fdt_usb *config) +{ + u32 val; + int loop_count; + struct ulpi_viewport ulpi_vp; + struct usb_ctlr *usbctlr = config->reg; + + /* set up ULPI reference clock on pllp_out4 */ + clock_enable(PERIPH_ID_DEV2_OUT); + clock_set_pllout(CLOCK_ID_PERIPH, PLL_OUT4, CONFIG_ULPI_REF_CLK); + + /* reset ULPI phy */ + if (fdt_gpio_isvalid(&config->phy_reset_gpio)) { + fdtdec_setup_gpio(&config->phy_reset_gpio); + gpio_direction_output(config->phy_reset_gpio.gpio, 0); + mdelay(5); + gpio_set_value(config->phy_reset_gpio.gpio, 1); + } + + /* Reset the usb controller */ + clock_enable(config->periph_id); + usbf_reset_controller(config, usbctlr); + + /* enable pinmux bypass */ + setbits_le32(&usbctlr->ulpi_timing_ctrl_0, + ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP); + + /* Select ULPI parallel interface */ +#if defined(CONFIG_TEGRA20) + clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, + PTS_ULPI << PTS_SHIFT); +#else + clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, + PTS_ULPI << PTS_SHIFT); +#endif + + /* enable ULPI transceiver */ + setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB); + + /* configure ULPI transceiver timings */ + val = 0; + writel(val, &usbctlr->ulpi_timing_ctrl_1); + + val |= ULPI_DATA_TRIMMER_SEL(4); + val |= ULPI_STPDIRNXT_TRIMMER_SEL(4); + val |= ULPI_DIR_TRIMMER_SEL(4); + writel(val, &usbctlr->ulpi_timing_ctrl_1); + udelay(10); + + val |= ULPI_DATA_TRIMMER_LOAD; + val |= ULPI_STPDIRNXT_TRIMMER_LOAD; + val |= ULPI_DIR_TRIMMER_LOAD; + writel(val, &usbctlr->ulpi_timing_ctrl_1); + + /* set up phy for host operation with external vbus supply */ + ulpi_vp.port_num = 0; + ulpi_vp.viewport_addr = (u32)&usbctlr->ulpi_viewport; + + if (ulpi_init(&ulpi_vp)) { + printf("Tegra ULPI viewport init failed\n"); + return -1; + } + + ulpi_set_vbus(&ulpi_vp, 1, 1); + ulpi_set_vbus_indicator(&ulpi_vp, 1, 1, 0); + + /* enable wakeup events */ + setbits_le32(&usbctlr->port_sc1, WKCN | WKDS | WKOC); + + /* Enable and wait for the phy clock to become valid in 100 ms */ + setbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR); + for (loop_count = 100000; loop_count != 0; loop_count--) { + if (readl(&usbctlr->susp_ctrl) & USB_PHY_CLK_VALID) + break; + udelay(1); + } + if (!loop_count) + return -1; + clrbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR); + + return 0; +} +#else +static int init_ulpi_usb_controller(struct fdt_usb *config) +{ + printf("No code to set up ULPI controller, please enable" + "CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT"); + return -1; +} +#endif + +static void config_clock(const u32 timing[]) +{ + clock_start_pll(CLOCK_ID_USB, + timing[PARAM_DIVM], timing[PARAM_DIVN], timing[PARAM_DIVP], + timing[PARAM_CPCON], timing[PARAM_LFCON]); +} + +static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) +{ + const char *phy, *mode; + + config->reg = (struct usb_ctlr *)fdtdec_get_addr(blob, node, "reg"); + mode = fdt_getprop(blob, node, "dr_mode", NULL); + if (mode) { + if (0 == strcmp(mode, "host")) + config->dr_mode = DR_MODE_HOST; + else if (0 == strcmp(mode, "peripheral")) + config->dr_mode = DR_MODE_DEVICE; + else if (0 == strcmp(mode, "otg")) + config->dr_mode = DR_MODE_OTG; + else { + debug("%s: Cannot decode dr_mode '%s'\n", __func__, + mode); + return -FDT_ERR_NOTFOUND; + } + } else { + config->dr_mode = DR_MODE_HOST; + } + + phy = fdt_getprop(blob, node, "phy_type", NULL); + config->utmi = phy && 0 == strcmp("utmi", phy); + config->ulpi = phy && 0 == strcmp("ulpi", phy); + config->enabled = fdtdec_get_is_enabled(blob, node); + config->has_legacy_mode = fdtdec_get_bool(blob, node, + "nvidia,has-legacy-mode"); + if (config->has_legacy_mode) + port_addr_clear_csc = (u32) config->reg; + config->periph_id = clock_decode_periph_id(blob, node); + if (config->periph_id == PERIPH_ID_NONE) { + debug("%s: Missing/invalid peripheral ID\n", __func__); + return -FDT_ERR_NOTFOUND; + } + fdtdec_decode_gpio(blob, node, "nvidia,vbus-gpio", &config->vbus_gpio); + fdtdec_decode_gpio(blob, node, "nvidia,phy-reset-gpio", + &config->phy_reset_gpio); + debug("enabled=%d, legacy_mode=%d, utmi=%d, ulpi=%d, periph_id=%d, " + "vbus=%d, phy_reset=%d, dr_mode=%d\n", + config->enabled, config->has_legacy_mode, config->utmi, + config->ulpi, config->periph_id, config->vbus_gpio.gpio, + config->phy_reset_gpio.gpio, config->dr_mode); + + return 0; +} + +/* + * process_usb_nodes() - Process a list of USB nodes, adding them to our list + * of USB ports. + * @blob: fdt blob + * @node_list: list of nodes to process (any <=0 are ignored) + * @count: number of nodes to process + * + * Return: 0 - ok, -1 - error + */ +static int process_usb_nodes(const void *blob, int node_list[], int count) +{ + struct fdt_usb config; + int node, i; + int clk_done = 0; + + port_count = 0; + for (i = 0; i < count; i++) { + if (port_count == USB_PORTS_MAX) { + printf("tegrausb: Cannot register more than %d ports\n", + USB_PORTS_MAX); + return -1; + } + + debug("USB %d: ", i); + node = node_list[i]; + if (!node) + continue; + if (fdt_decode_usb(blob, node, &config)) { + debug("Cannot decode USB node %s\n", + fdt_get_name(blob, node, NULL)); + return -1; + } + if (!clk_done) { + config_clock(get_pll_timing()); + clk_done = 1; + } + config.initialized = 0; + + /* add new USB port to the list of available ports */ + port[port_count++] = config; + } + + return 0; +} + +int usb_process_devicetree(const void *blob) +{ + int node_list[USB_PORTS_MAX]; + int count, err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(fdt_usb_controllers); i++) { + controller = &fdt_usb_controllers[i]; + + count = fdtdec_find_aliases_for_id(blob, "usb", + controller->compat, node_list, USB_PORTS_MAX); + if (count) { + err = process_usb_nodes(blob, node_list, count); + if (err) + printf("%s: Error processing USB node!\n", + __func__); + return err; + } + } + if (i == ARRAY_SIZE(fdt_usb_controllers)) + controller = NULL; + + return err; +} + +/** + * Start up the given port number (ports are numbered from 0 on each board). + * This returns values for the appropriate hccr and hcor addresses to use for + * USB EHCI operations. + * + * @param index port number to start + * @param hccr returns start address of EHCI HCCR registers + * @param hcor returns start address of EHCI HCOR registers + * @return 0 if ok, -1 on error (generally invalid port number) + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + struct fdt_usb *config; + struct usb_ctlr *usbctlr; + + if (index >= port_count) + return -1; + + config = &port[index]; + + /* skip init, if the port is already initialized */ + if (config->initialized) + goto success; + + if (config->utmi && init_utmi_usb_controller(config)) { + printf("tegrausb: Cannot init port %d\n", index); + return -1; + } + + if (config->ulpi && init_ulpi_usb_controller(config)) { + printf("tegrausb: Cannot init port %d\n", index); + return -1; + } + + set_host_mode(config); + + config->initialized = 1; + +success: + usbctlr = config->reg; + *hccr = (struct ehci_hccr *)&usbctlr->cap_length; + *hcor = (struct ehci_hcor *)&usbctlr->usb_cmd; + + if (controller->has_hostpc) { + /* Set to Host mode after Controller Reset was done */ + clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, + USBMODE_CM_HC); + /* Select UTMI parallel interface after setting host mode */ + if (config->utmi) { + clrsetbits_le32((char *)&usbctlr->usb_cmd + + HOSTPC1_DEVLC, PTS_MASK, + PTS_UTMI << PTS_SHIFT); + clrbits_le32((char *)&usbctlr->usb_cmd + + HOSTPC1_DEVLC, STS); + } + } + return 0; +} + +/* + * Bring down the specified USB controller + */ +int ehci_hcd_stop(int index) +{ + struct usb_ctlr *usbctlr; + + usbctlr = port[index].reg; + + /* Stop controller */ + writel(0, &usbctlr->usb_cmd); + udelay(1000); + + /* Initiate controller reset */ + writel(2, &usbctlr->usb_cmd); + udelay(1000); + + port[index].initialized = 0; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci-vct.c b/qemu/roms/u-boot/drivers/usb/host/ehci-vct.c new file mode 100644 index 000000000..512ad3fb7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci-vct.c @@ -0,0 +1,45 @@ +/* + * (C) Copyright 2009 Stefan Roese , DENX Software Engineering + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include "ehci.h" + +int vct_ehci_hcd_init(u32 *hccr, u32 *hcor); + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + int ret; + u32 vct_hccr; + u32 vct_hcor; + + /* + * Init VCT specific stuff + */ + ret = vct_ehci_hcd_init(&vct_hccr, &vct_hcor); + if (ret) + return ret; + + *hccr = (struct ehci_hccr *)vct_hccr; + *hcor = (struct ehci_hcor *)vct_hcor; + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ehci.h b/qemu/roms/u-boot/drivers/usb/host/ehci.h new file mode 100644 index 000000000..093eb4b83 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ehci.h @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Michael Trimarchi + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef USB_EHCI_H +#define USB_EHCI_H + +#include + +#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 +#endif + +/* + * Register Space. + */ +struct ehci_hccr { + uint32_t cr_capbase; +#define HC_LENGTH(p) (((p) >> 0) & 0x00ff) +#define HC_VERSION(p) (((p) >> 16) & 0xffff) + uint32_t cr_hcsparams; +#define HCS_PPC(p) ((p) & (1 << 4)) +#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */ +#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) + uint32_t cr_hccparams; + uint8_t cr_hcsp_portrt[8]; +} __attribute__ ((packed, aligned(4))); + +struct ehci_hcor { + uint32_t or_usbcmd; +#define CMD_PARK (1 << 11) /* enable "park" */ +#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */ +#define CMD_ASE (1 << 5) /* async schedule enable */ +#define CMD_LRESET (1 << 7) /* partial reset */ +#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */ +#define CMD_PSE (1 << 4) /* periodic schedule enable */ +#define CMD_RESET (1 << 1) /* reset HC not bus */ +#define CMD_RUN (1 << 0) /* start/stop HC */ + uint32_t or_usbsts; +#define STS_ASS (1 << 15) +#define STS_PSS (1 << 14) +#define STS_HALT (1 << 12) + uint32_t or_usbintr; +#define INTR_UE (1 << 0) /* USB interrupt enable */ +#define INTR_UEE (1 << 1) /* USB error interrupt enable */ +#define INTR_PCE (1 << 2) /* Port change detect enable */ +#define INTR_SEE (1 << 4) /* system error enable */ +#define INTR_AAE (1 << 5) /* Interrupt on async adavance enable */ + uint32_t or_frindex; + uint32_t or_ctrldssegment; + uint32_t or_periodiclistbase; + uint32_t or_asynclistaddr; + uint32_t _reserved_0_; + uint32_t or_burstsize; + uint32_t or_txfilltuning; +#define TXFIFO_THRESH_MASK (0x3f << 16) +#define TXFIFO_THRESH(p) ((p & 0x3f) << 16) + uint32_t _reserved_1_[6]; + uint32_t or_configflag; +#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ + uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; +#define PORTSC_PSPD(x) (((x) >> 26) & 0x3) +#define PORTSC_PSPD_FS 0x0 +#define PORTSC_PSPD_LS 0x1 +#define PORTSC_PSPD_HS 0x2 + uint32_t or_systune; +} __attribute__ ((packed, aligned(4))); + +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1 << 3) /* Stream disable */ +#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */ +#define USBMODE_CM_HC (3 << 0) /* host controller mode */ +#define USBMODE_CM_IDLE (0 << 0) /* idle state */ + +/* Interface descriptor */ +struct usb_linux_interface_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; +} __attribute__ ((packed)); + +/* Configuration descriptor information.. */ +struct usb_linux_config_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char MaxPower; +} __attribute__ ((packed)); + +#if defined CONFIG_EHCI_DESC_BIG_ENDIAN +#define ehci_readl(x) (*((volatile u32 *)(x))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b)) +#else +#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x)))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \ + cpu_to_le32(((volatile u32)b))) +#endif + +#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN +#define hc32_to_cpu(x) be32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_be32((x)) +#else +#define hc32_to_cpu(x) le32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_le32((x)) +#endif + +#define EHCI_PS_WKOC_E (1 << 22) /* RW wake on over current */ +#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */ +#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */ +#define EHCI_PS_PO (1 << 13) /* RW port owner */ +#define EHCI_PS_PP (1 << 12) /* RW,RO port power */ +#define EHCI_PS_LS (3 << 10) /* RO line status */ +#define EHCI_PS_PR (1 << 8) /* RW port reset */ +#define EHCI_PS_SUSP (1 << 7) /* RW suspend */ +#define EHCI_PS_FPR (1 << 6) /* RW force port resume */ +#define EHCI_PS_OCC (1 << 5) /* RWC over current change */ +#define EHCI_PS_OCA (1 << 4) /* RO over current active */ +#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */ +#define EHCI_PS_PE (1 << 2) /* RW port enable */ +#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */ +#define EHCI_PS_CS (1 << 0) /* RO connect status */ +#define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) + +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10)) + +/* + * Schedule Interface Space. + * + * IMPORTANT: Software must ensure that no interface data structure + * reachable by the EHCI host controller spans a 4K page boundary! + * + * Periodic transfers (i.e. isochronous and interrupt transfers) are + * not supported. + */ + +/* Queue Element Transfer Descriptor (qTD). */ +struct qTD { + /* this part defined by EHCI spec */ + uint32_t qt_next; /* see EHCI 3.5.1 */ +#define QT_NEXT_TERMINATE 1 + uint32_t qt_altnext; /* see EHCI 3.5.2 */ + uint32_t qt_token; /* see EHCI 3.5.3 */ +#define QT_TOKEN_DT(x) (((x) & 0x1) << 31) /* Data Toggle */ +#define QT_TOKEN_GET_DT(x) (((x) >> 31) & 0x1) +#define QT_TOKEN_TOTALBYTES(x) (((x) & 0x7fff) << 16) /* Total Bytes to Transfer */ +#define QT_TOKEN_GET_TOTALBYTES(x) (((x) >> 16) & 0x7fff) +#define QT_TOKEN_IOC(x) (((x) & 0x1) << 15) /* Interrupt On Complete */ +#define QT_TOKEN_CPAGE(x) (((x) & 0x7) << 12) /* Current Page */ +#define QT_TOKEN_CERR(x) (((x) & 0x3) << 10) /* Error Counter */ +#define QT_TOKEN_PID(x) (((x) & 0x3) << 8) /* PID Code */ +#define QT_TOKEN_PID_OUT 0x0 +#define QT_TOKEN_PID_IN 0x1 +#define QT_TOKEN_PID_SETUP 0x2 +#define QT_TOKEN_STATUS(x) (((x) & 0xff) << 0) /* Status */ +#define QT_TOKEN_GET_STATUS(x) (((x) >> 0) & 0xff) +#define QT_TOKEN_STATUS_ACTIVE 0x80 +#define QT_TOKEN_STATUS_HALTED 0x40 +#define QT_TOKEN_STATUS_DATBUFERR 0x20 +#define QT_TOKEN_STATUS_BABBLEDET 0x10 +#define QT_TOKEN_STATUS_XACTERR 0x08 +#define QT_TOKEN_STATUS_MISSEDUFRAME 0x04 +#define QT_TOKEN_STATUS_SPLITXSTATE 0x02 +#define QT_TOKEN_STATUS_PERR 0x01 +#define QT_BUFFER_CNT 5 + uint32_t qt_buffer[QT_BUFFER_CNT]; /* see EHCI 3.5.4 */ + uint32_t qt_buffer_hi[QT_BUFFER_CNT]; /* Appendix B */ + /* pad struct for 32 byte alignment */ + uint32_t unused[3]; +}; + +#define EHCI_PAGE_SIZE 4096 + +/* Queue Head (QH). */ +struct QH { + uint32_t qh_link; +#define QH_LINK_TERMINATE 1 +#define QH_LINK_TYPE_ITD 0 +#define QH_LINK_TYPE_QH 2 +#define QH_LINK_TYPE_SITD 4 +#define QH_LINK_TYPE_FSTN 6 + uint32_t qh_endpt1; +#define QH_ENDPT1_RL(x) (((x) & 0xf) << 28) /* NAK Count Reload */ +#define QH_ENDPT1_C(x) (((x) & 0x1) << 27) /* Control Endpoint Flag */ +#define QH_ENDPT1_MAXPKTLEN(x) (((x) & 0x7ff) << 16) /* Maximum Packet Length */ +#define QH_ENDPT1_H(x) (((x) & 0x1) << 15) /* Head of Reclamation List Flag */ +#define QH_ENDPT1_DTC(x) (((x) & 0x1) << 14) /* Data Toggle Control */ +#define QH_ENDPT1_DTC_IGNORE_QTD_TD 0x0 +#define QH_ENDPT1_DTC_DT_FROM_QTD 0x1 +#define QH_ENDPT1_EPS(x) (((x) & 0x3) << 12) /* Endpoint Speed */ +#define QH_ENDPT1_EPS_FS 0x0 +#define QH_ENDPT1_EPS_LS 0x1 +#define QH_ENDPT1_EPS_HS 0x2 +#define QH_ENDPT1_ENDPT(x) (((x) & 0xf) << 8) /* Endpoint Number */ +#define QH_ENDPT1_I(x) (((x) & 0x1) << 7) /* Inactivate on Next Transaction */ +#define QH_ENDPT1_DEVADDR(x) (((x) & 0x7f) << 0) /* Device Address */ + uint32_t qh_endpt2; +#define QH_ENDPT2_MULT(x) (((x) & 0x3) << 30) /* High-Bandwidth Pipe Multiplier */ +#define QH_ENDPT2_PORTNUM(x) (((x) & 0x7f) << 23) /* Port Number */ +#define QH_ENDPT2_HUBADDR(x) (((x) & 0x7f) << 16) /* Hub Address */ +#define QH_ENDPT2_UFCMASK(x) (((x) & 0xff) << 8) /* Split Completion Mask */ +#define QH_ENDPT2_UFSMASK(x) (((x) & 0xff) << 0) /* Interrupt Schedule Mask */ + uint32_t qh_curtd; + struct qTD qh_overlay; + /* + * Add dummy fill value to make the size of this struct + * aligned to 32 bytes + */ + union { + uint32_t fill[4]; + void *buffer; + }; +}; + +struct ehci_ctrl { + struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ + struct ehci_hcor *hcor; + int rootdev; + uint16_t portreset; + struct QH qh_list __aligned(USB_DMA_MINALIGN); + struct QH periodic_queue __aligned(USB_DMA_MINALIGN); + uint32_t *periodic_list; + int ntds; +}; + +/* Low level init functions */ +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor); +int ehci_hcd_stop(int index); + +#endif /* USB_EHCI_H */ diff --git a/qemu/roms/u-boot/drivers/usb/host/isp116x-hcd.c b/qemu/roms/u-boot/drivers/usb/host/isp116x-hcd.c new file mode 100644 index 000000000..46e4cee1d --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/isp116x-hcd.c @@ -0,0 +1,1329 @@ +/* + * ISP116x HCD (Host Controller Driver) for u-boot. + * + * Copyright (C) 2006-2007 Rodolfo Giometti + * Copyright (C) 2006-2007 Eurotech S.p.A. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Derived in part from the SL811 HCD driver "u-boot/drivers/usb/sl811_usb.c" + * (original copyright message follows): + * + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This code is based on linux driver for sl811hs chip, source at + * drivers/usb/host/sl811.c: + * + * SL811 Host Controller Interface driver for USB. + * + * Copyright (c) 2003/06, Courage Co., Ltd. + * + * Based on: + * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, + * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, + * Adam Richter, Gregory P. Smith; + * 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 3.Rewrited as sl811.o by Yin Aihua + * + * [[GNU/GPL disclaimer]] + * + * and in part from AU1x00 OHCI HCD driver "u-boot/arch/mips/cpu/au1x00_usb_ohci.c" + * (original copyright message follows): + * + * URB OHCI HCD (Host Controller Driver) for USB on the AU1x00. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * [[GNU/GPL disclaimer]] + * + * Note: Part of this code has been derived from linux + */ + +#include +#include +#include +#include +#include + +/* + * ISP116x chips require certain delays between accesses to its + * registers. The following timing options exist. + * + * 1. Configure your memory controller (the best) + * 2. Use ndelay (easiest, poorest). For that, enable the following macro. + * + * Value is in microseconds. + */ +#ifdef ISP116X_HCD_USE_UDELAY +#define UDELAY 1 +#endif + +/* + * On some (slowly?) machines an extra delay after data packing into + * controller's FIFOs is required, * otherwise you may get the following + * error: + * + * uboot> usb start + * (Re)start USB... + * USB: scanning bus for devices... isp116x: isp116x_submit_job: CTL:TIMEOUT + * isp116x: isp116x_submit_job: ****** FIFO not ready! ****** + * + * USB device not responding, giving up (status=4) + * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + * 3 USB Device(s) found + * scanning bus for storage devices... 0 Storage Device(s) found + * + * Value is in milliseconds. + */ +#ifdef ISP116X_HCD_USE_EXTRA_DELAY +#define EXTRA_DELAY 2 +#endif + +/* + * Enable the following defines if you wish enable debugging messages. + */ +#undef DEBUG /* enable debugging messages */ +#undef TRACE /* enable tracing code */ +#undef VERBOSE /* verbose debugging messages */ + +#include "isp116x.h" + +#define DRIVER_VERSION "08 Jan 2007" +static const char hcd_name[] = "isp116x-hcd"; + +struct isp116x isp116x_dev; +struct isp116x_platform_data isp116x_board; +static int got_rhsc; /* root hub status change */ +struct usb_device *devgone; /* device which was disconnected */ +static int rh_devnum; /* address of Root Hub endpoint */ + +/* ------------------------------------------------------------------------- */ + +#define ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL)) +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) + +/* ------------------------------------------------------------------------- */ + +static int isp116x_reset(struct isp116x *isp116x); + +/* --- Debugging functions ------------------------------------------------- */ + +#define isp116x_show_reg(d, r) { \ + if ((r) < 0x20) { \ + DBG("%-12s[%02x]: %08x", #r, \ + r, isp116x_read_reg32(d, r)); \ + } else { \ + DBG("%-12s[%02x]: %04x", #r, \ + r, isp116x_read_reg16(d, r)); \ + } \ +} + +#define isp116x_show_regs(d) { \ + isp116x_show_reg(d, HCREVISION); \ + isp116x_show_reg(d, HCCONTROL); \ + isp116x_show_reg(d, HCCMDSTAT); \ + isp116x_show_reg(d, HCINTSTAT); \ + isp116x_show_reg(d, HCINTENB); \ + isp116x_show_reg(d, HCFMINTVL); \ + isp116x_show_reg(d, HCFMREM); \ + isp116x_show_reg(d, HCFMNUM); \ + isp116x_show_reg(d, HCLSTHRESH); \ + isp116x_show_reg(d, HCRHDESCA); \ + isp116x_show_reg(d, HCRHDESCB); \ + isp116x_show_reg(d, HCRHSTATUS); \ + isp116x_show_reg(d, HCRHPORT1); \ + isp116x_show_reg(d, HCRHPORT2); \ + isp116x_show_reg(d, HCHWCFG); \ + isp116x_show_reg(d, HCDMACFG); \ + isp116x_show_reg(d, HCXFERCTR); \ + isp116x_show_reg(d, HCuPINT); \ + isp116x_show_reg(d, HCuPINTENB); \ + isp116x_show_reg(d, HCCHIPID); \ + isp116x_show_reg(d, HCSCRATCH); \ + isp116x_show_reg(d, HCITLBUFLEN); \ + isp116x_show_reg(d, HCATLBUFLEN); \ + isp116x_show_reg(d, HCBUFSTAT); \ + isp116x_show_reg(d, HCRDITL0LEN); \ + isp116x_show_reg(d, HCRDITL1LEN); \ +} + +#if defined(TRACE) + +static int isp116x_get_current_frame_number(struct usb_device *usb_dev) +{ + struct isp116x *isp116x = &isp116x_dev; + + return isp116x_read_reg32(isp116x, HCFMNUM); +} + +static void dump_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, char *str) +{ +#if defined(VERBOSE) + int i; +#endif + + DBG("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d stat:%#lx", + str, + isp116x_get_current_frame_number(dev), + usb_pipedevice(pipe), + usb_pipeendpoint(pipe), + usb_pipeout(pipe) ? 'O' : 'I', + usb_pipetype(pipe) < 2 ? + (usb_pipeint(pipe) ? + "INTR" : "ISOC") : + (usb_pipecontrol(pipe) ? "CTRL" : "BULK"), len, dev->status); +#if defined(VERBOSE) + if (len > 0 && buffer) { + printf(__FILE__ ": data(%d):", len); + for (i = 0; i < 16 && i < len; i++) + printf(" %02x", ((__u8 *) buffer)[i]); + printf("%s\n", i < len ? "..." : ""); + } +#endif +} + +#define PTD_DIR_STR(ptd) ({char __c; \ + switch(PTD_GET_DIR(ptd)){ \ + case 0: __c = 's'; break; \ + case 1: __c = 'o'; break; \ + default: __c = 'i'; break; \ + }; __c;}) + +/* + Dump PTD info. The code documents the format + perfectly, right :) +*/ +static inline void dump_ptd(struct ptd *ptd) +{ +#if defined(VERBOSE) + int k; +#endif + + DBG("PTD(ext) : cc:%x %d%c%d %d,%d,%d t:%x %x%x%x", + PTD_GET_CC(ptd), + PTD_GET_FA(ptd), PTD_DIR_STR(ptd), PTD_GET_EP(ptd), + PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), + PTD_GET_TOGGLE(ptd), + PTD_GET_ACTIVE(ptd), PTD_GET_SPD(ptd), PTD_GET_LAST(ptd)); +#if defined(VERBOSE) + printf("isp116x: %s: PTD(byte): ", __FUNCTION__); + for (k = 0; k < sizeof(struct ptd); ++k) + printf("%02x ", ((u8 *) ptd)[k]); + printf("\n"); +#endif +} + +static inline void dump_ptd_data(struct ptd *ptd, u8 * buf, int type) +{ +#if defined(VERBOSE) + int k; + + if (type == 0 /* 0ut data */ ) { + printf("isp116x: %s: out data: ", __FUNCTION__); + for (k = 0; k < PTD_GET_LEN(ptd); ++k) + printf("%02x ", ((u8 *) buf)[k]); + printf("\n"); + } + if (type == 1 /* 1n data */ ) { + printf("isp116x: %s: in data: ", __FUNCTION__); + for (k = 0; k < PTD_GET_COUNT(ptd); ++k) + printf("%02x ", ((u8 *) buf)[k]); + printf("\n"); + } + + if (PTD_GET_LAST(ptd)) + DBG("--- last PTD ---"); +#endif +} + +#else + +#define dump_msg(dev, pipe, buffer, len, str) do { } while (0) +#define dump_pkt(dev, pipe, buffer, len, setup, str, small) do {} while (0) + +#define dump_ptd(ptd) do {} while (0) +#define dump_ptd_data(ptd, buf, type) do {} while (0) + +#endif + +/* --- Virtual Root Hub ---------------------------------------------------- */ + +#include + +/* + * Hub class-specific descriptor is constructed dynamically + */ + +/* --- Virtual root hub management functions ------------------------------- */ + +static int rh_check_port_status(struct isp116x *isp116x) +{ + u32 temp, ndp, i; + int res; + + res = -1; + temp = isp116x_read_reg32(isp116x, HCRHSTATUS); + ndp = (temp & RH_A_NDP); + for (i = 0; i < ndp; i++) { + temp = isp116x_read_reg32(isp116x, HCRHPORT1 + i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +/* --- HC management functions --------------------------------------------- */ + +/* Write len bytes to fifo, pad till 32-bit boundary + */ +static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len) +{ + u8 *dp = (u8 *) buf; + u16 *dp2 = (u16 *) buf; + u16 w; + int quot = len % 4; + + if ((unsigned long)dp2 & 1) { + /* not aligned */ + for (; len > 1; len -= 2) { + w = *dp++; + w |= *dp++ << 8; + isp116x_raw_write_data16(isp116x, w); + } + if (len) + isp116x_write_data16(isp116x, (u16) * dp); + } else { + /* aligned */ + for (; len > 1; len -= 2) + isp116x_raw_write_data16(isp116x, *dp2++); + if (len) + isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2)); + } + if (quot == 1 || quot == 2) + isp116x_raw_write_data16(isp116x, 0); +} + +/* Read len bytes from fifo and then read till 32-bit boundary + */ +static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len) +{ + u8 *dp = (u8 *) buf; + u16 *dp2 = (u16 *) buf; + u16 w; + int quot = len % 4; + + if ((unsigned long)dp2 & 1) { + /* not aligned */ + for (; len > 1; len -= 2) { + w = isp116x_raw_read_data16(isp116x); + *dp++ = w & 0xff; + *dp++ = (w >> 8) & 0xff; + } + if (len) + *dp = 0xff & isp116x_read_data16(isp116x); + } else { + /* aligned */ + for (; len > 1; len -= 2) + *dp2++ = isp116x_raw_read_data16(isp116x); + if (len) + *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x); + } + if (quot == 1 || quot == 2) + isp116x_raw_read_data16(isp116x); +} + +/* Write PTD's and data for scheduled transfers into the fifo ram. + * Fifo must be empty and ready */ +static void pack_fifo(struct isp116x *isp116x, struct usb_device *dev, + unsigned long pipe, struct ptd *ptd, int n, void *data, + int len) +{ + int buflen = n * sizeof(struct ptd) + len; + int i, done; + + DBG("--- pack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); + + isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); + isp116x_write_reg16(isp116x, HCXFERCTR, buflen); + isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET); + + done = 0; + for (i = 0; i < n; i++) { + DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); + + dump_ptd(&ptd[i]); + isp116x_write_data16(isp116x, ptd[i].count); + isp116x_write_data16(isp116x, ptd[i].mps); + isp116x_write_data16(isp116x, ptd[i].len); + isp116x_write_data16(isp116x, ptd[i].faddr); + + dump_ptd_data(&ptd[i], (__u8 *) data + done, 0); + write_ptddata_to_fifo(isp116x, + (__u8 *) data + done, + PTD_GET_LEN(&ptd[i])); + + done += PTD_GET_LEN(&ptd[i]); + } +} + +/* Read the processed PTD's and data from fifo ram back to URBs' buffers. + * Fifo must be full and done */ +static int unpack_fifo(struct isp116x *isp116x, struct usb_device *dev, + unsigned long pipe, struct ptd *ptd, int n, void *data, + int len) +{ + int buflen = n * sizeof(struct ptd) + len; + int i, done, cc, ret; + + isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); + isp116x_write_reg16(isp116x, HCXFERCTR, buflen); + isp116x_write_addr(isp116x, HCATLPORT); + + ret = TD_CC_NOERROR; + done = 0; + for (i = 0; i < n; i++) { + DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); + + ptd[i].count = isp116x_read_data16(isp116x); + ptd[i].mps = isp116x_read_data16(isp116x); + ptd[i].len = isp116x_read_data16(isp116x); + ptd[i].faddr = isp116x_read_data16(isp116x); + dump_ptd(&ptd[i]); + + read_ptddata_from_fifo(isp116x, + (__u8 *) data + done, + PTD_GET_LEN(&ptd[i])); + dump_ptd_data(&ptd[i], (__u8 *) data + done, 1); + + done += PTD_GET_LEN(&ptd[i]); + + cc = PTD_GET_CC(&ptd[i]); + + /* Data underrun means basically that we had more buffer space than + * the function had data. It is perfectly normal but upper levels have + * to know how much we actually transferred. + */ + if (cc == TD_NOTACCESSED || + (cc != TD_CC_NOERROR && (ret == TD_CC_NOERROR || ret == TD_DATAUNDERRUN))) + ret = cc; + } + + DBG("--- unpack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); + + return ret; +} + +/* Interrupt handling + */ +static int isp116x_interrupt(struct isp116x *isp116x) +{ + u16 irqstat; + u32 intstat; + int ret = 0; + + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + irqstat = isp116x_read_reg16(isp116x, HCuPINT); + isp116x_write_reg16(isp116x, HCuPINT, irqstat); + DBG(">>>>>> irqstat %x <<<<<<", irqstat); + + if (irqstat & HCuPINT_ATL) { + DBG(">>>>>> HCuPINT_ATL <<<<<<"); + udelay(500); + ret = 1; + } + + if (irqstat & HCuPINT_OPR) { + intstat = isp116x_read_reg32(isp116x, HCINTSTAT); + isp116x_write_reg32(isp116x, HCINTSTAT, intstat); + DBG(">>>>>> HCuPINT_OPR %x <<<<<<", intstat); + + if (intstat & HCINT_UE) { + ERR("unrecoverable error, controller disabled"); + + /* FIXME: be optimistic, hope that bug won't repeat + * often. Make some non-interrupt context restart the + * controller. Count and limit the retries though; + * either hardware or software errors can go forever... + */ + isp116x_reset(isp116x); + ret = -1; + return -1; + } + + if (intstat & HCINT_RHSC) { + got_rhsc = 1; + ret = 1; + /* When root hub or any of its ports is going + to come out of suspend, it may take more + than 10ms for status bits to stabilize. */ + mdelay(20); + } + + if (intstat & HCINT_SO) { + ERR("schedule overrun"); + ret = -1; + } + + irqstat &= ~HCuPINT_OPR; + } + + return ret; +} + +/* With one PTD we can transfer almost 1K in one go; + * HC does the splitting into endpoint digestible transactions + */ +struct ptd ptd[1]; + +static inline int max_transfer_len(struct usb_device *dev, unsigned long pipe) +{ + unsigned mpck = usb_maxpacket(dev, pipe); + + /* One PTD can transfer 1023 bytes but try to always + * transfer multiples of endpoint buffer size + */ + return 1023 / mpck * mpck; +} + +/* Do an USB transfer + */ +static int isp116x_submit_job(struct usb_device *dev, unsigned long pipe, + int dir, void *buffer, int len) +{ + struct isp116x *isp116x = &isp116x_dev; + int type = usb_pipetype(pipe); + int epnum = usb_pipeendpoint(pipe); + int max = usb_maxpacket(dev, pipe); + int dir_out = usb_pipeout(pipe); + int speed_low = (dev->speed == USB_SPEED_LOW); + int i, done = 0, stat, timeout, cc; + + /* 500 frames or 0.5s timeout when function is busy and NAKs transactions for a while */ + int retries = 500; + + DBG("------------------------------------------------"); + dump_msg(dev, pipe, buffer, len, "SUBMIT"); + DBG("------------------------------------------------"); + + if (len >= 1024) { + ERR("Too big job"); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + if (isp116x->disabled) { + ERR("EPIPE"); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + ERR("ENODEV"); + dev->status = USB_ST_CRC_ERR; + return USB_ST_CRC_ERR; + } + + if (!max) { + ERR("pipesize for pipe %lx is zero", pipe); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + if (type == PIPE_ISOCHRONOUS) { + ERR("isochronous transfers not supported"); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + /* FIFO not empty? */ + if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) { + ERR("****** FIFO not empty! ******"); + dev->status = USB_ST_BUF_ERR; + return -1; + } + + retry: + isp116x_write_reg32(isp116x, HCINTSTAT, 0xff); + + /* Prepare the PTD data */ + ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | + PTD_TOGGLE(usb_gettoggle(dev, epnum, dir_out)); + ptd->mps = PTD_MPS(max) | PTD_SPD(speed_low) | PTD_EP(epnum) | PTD_LAST_MSK; + ptd->len = PTD_LEN(len) | PTD_DIR(dir); + ptd->faddr = PTD_FA(usb_pipedevice(pipe)); + +retry_same: + /* Pack data into FIFO ram */ + pack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len); +#ifdef EXTRA_DELAY + mdelay(EXTRA_DELAY); +#endif + + /* Start the data transfer */ + + /* Allow more time for a BULK device to react - some are slow */ + if (usb_pipebulk(pipe)) + timeout = 5000; + else + timeout = 100; + + /* Wait for it to complete */ + for (;;) { + /* Check whether the controller is done */ + stat = isp116x_interrupt(isp116x); + + if (stat < 0) { + dev->status = USB_ST_CRC_ERR; + break; + } + if (stat > 0) + break; + + /* Check the timeout */ + if (--timeout) + udelay(1); + else { + ERR("CTL:TIMEOUT "); + stat = USB_ST_CRC_ERR; + break; + } + } + + /* We got an Root Hub Status Change interrupt */ + if (got_rhsc) { + isp116x_show_regs(isp116x); + + got_rhsc = 0; + + /* Abuse timeout */ + timeout = rh_check_port_status(isp116x); + if (timeout >= 0) { + /* + * FIXME! NOTE! AAAARGH! + * This is potentially dangerous because it assumes + * that only one device is ever plugged in! + */ + devgone = dev; + } + } + + /* Ok, now we can read transfer status */ + + /* FIFO not ready? */ + if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) { + ERR("****** FIFO not ready! ******"); + dev->status = USB_ST_BUF_ERR; + return -1; + } + + /* Unpack data from FIFO ram */ + cc = unpack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len); + + i = PTD_GET_COUNT(ptd); + done += i; + buffer += i; + len -= i; + + /* There was some kind of real problem; Prepare the PTD again + * and retry from the failed transaction on + */ + if (cc && cc != TD_NOTACCESSED && cc != TD_DATAUNDERRUN) { + if (retries >= 100) { + retries -= 100; + /* The chip will have toggled the toggle bit for the failed + * transaction too. We have to toggle it back. + */ + usb_settoggle(dev, epnum, dir_out, !PTD_GET_TOGGLE(ptd)); + goto retry; + } + } + /* "Normal" errors; TD_NOTACCESSED would mean in effect that the function have NAKed + * the transactions from the first on for the whole frame. It may be busy and we retry + * with the same PTD. PTD_ACTIVE (and not TD_NOTACCESSED) would mean that some of the + * PTD didn't make it because the function was busy or the frame ended before the PTD + * finished. We prepare the rest of the data and try again. + */ + else if (cc == TD_NOTACCESSED || PTD_GET_ACTIVE(ptd) || (cc != TD_DATAUNDERRUN && PTD_GET_COUNT(ptd) < PTD_GET_LEN(ptd))) { + if (retries) { + --retries; + if (cc == TD_NOTACCESSED && PTD_GET_ACTIVE(ptd) && !PTD_GET_COUNT(ptd)) goto retry_same; + usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); + goto retry; + } + } + + if (cc != TD_CC_NOERROR && cc != TD_DATAUNDERRUN) { + DBG("****** completition code error %x ******", cc); + switch (cc) { + case TD_CC_BITSTUFFING: + dev->status = USB_ST_BIT_ERR; + break; + case TD_CC_STALL: + dev->status = USB_ST_STALLED; + break; + case TD_BUFFEROVERRUN: + case TD_BUFFERUNDERRUN: + dev->status = USB_ST_BUF_ERR; + break; + default: + dev->status = USB_ST_CRC_ERR; + } + return -cc; + } + else usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); + + dump_msg(dev, pipe, buffer, len, "SUBMIT(ret)"); + + dev->status = 0; + return done; +} + +/* Adapted from au1x00_usb_ohci.c + */ +static int isp116x_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *cmd) +{ + struct isp116x *isp116x = &isp116x_dev; + u32 tmp = 0; + + int leni = transfer_len; + int len = 0; + int stat = 0; + u32 datab[4]; + u8 *data_buf = (u8 *) datab; + u16 bmRType_bReq; + u16 wValue; + u16 wIndex; + u16 wLength; + + if (usb_pipeint(pipe)) { + INFO("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = swap_16(cmd->value); + wIndex = swap_16(cmd->index); + wLength = swap_16(cmd->length); + + DBG("--- HUB ----------------------------------------"); + DBG("submit rh urb, req=%x val=%#x index=%#x len=%d", + bmRType_bReq, wValue, wIndex, wLength); + dump_msg(dev, pipe, buffer, transfer_len, "RH"); + DBG("------------------------------------------------"); + + switch (bmRType_bReq) { + case RH_GET_STATUS: + DBG("RH_GET_STATUS"); + + *(__u16 *) data_buf = swap_16(1); + len = 2; + break; + + case RH_GET_STATUS | RH_INTERFACE: + DBG("RH_GET_STATUS | RH_INTERFACE"); + + *(__u16 *) data_buf = swap_16(0); + len = 2; + break; + + case RH_GET_STATUS | RH_ENDPOINT: + DBG("RH_GET_STATUS | RH_ENDPOINT"); + + *(__u16 *) data_buf = swap_16(0); + len = 2; + break; + + case RH_GET_STATUS | RH_CLASS: + DBG("RH_GET_STATUS | RH_CLASS"); + + tmp = isp116x_read_reg32(isp116x, HCRHSTATUS); + + *(__u32 *) data_buf = swap_32(tmp & ~(RH_HS_CRWE | RH_HS_DRWE)); + len = 4; + break; + + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + DBG("RH_GET_STATUS | RH_OTHER | RH_CLASS"); + + tmp = isp116x_read_reg32(isp116x, HCRHPORT1 + wIndex - 1); + *(__u32 *) data_buf = swap_32(tmp); + isp116x_show_regs(isp116x); + len = 4; + break; + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + DBG("RH_CLEAR_FEATURE | RH_ENDPOINT"); + + switch (wValue) { + case RH_ENDPOINT_STALL: + DBG("C_HUB_ENDPOINT_STALL"); + len = 0; + break; + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + DBG("RH_CLEAR_FEATURE | RH_CLASS"); + + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + DBG("C_HUB_LOCAL_POWER"); + len = 0; + break; + + case RH_C_HUB_OVER_CURRENT: + DBG("C_HUB_OVER_CURRENT"); + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC); + len = 0; + break; + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + DBG("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS"); + + switch (wValue) { + case RH_PORT_ENABLE: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_CCS); + len = 0; + break; + + case RH_PORT_SUSPEND: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_POCI); + len = 0; + break; + + case RH_PORT_POWER: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_LSDA); + len = 0; + break; + + case RH_C_PORT_CONNECTION: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_CSC); + len = 0; + break; + + case RH_C_PORT_ENABLE: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PESC); + len = 0; + break; + + case RH_C_PORT_SUSPEND: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PSSC); + len = 0; + break; + + case RH_C_PORT_OVER_CURRENT: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_POCI); + len = 0; + break; + + case RH_C_PORT_RESET: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PRSC); + len = 0; + break; + + default: + ERR("invalid wValue"); + stat = USB_ST_STALLED; + } + + isp116x_show_regs(isp116x); + + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + DBG("RH_SET_FEATURE | RH_OTHER | RH_CLASS"); + + switch (wValue) { + case RH_PORT_SUSPEND: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PSS); + len = 0; + break; + + case RH_PORT_RESET: + /* Spin until any current reset finishes */ + while (1) { + tmp = + isp116x_read_reg32(isp116x, + HCRHPORT1 + wIndex - 1); + if (!(tmp & RH_PS_PRS)) + break; + mdelay(1); + } + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PRS); + mdelay(10); + + len = 0; + break; + + case RH_PORT_POWER: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PPS); + len = 0; + break; + + case RH_PORT_ENABLE: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PES); + len = 0; + break; + + default: + ERR("invalid wValue"); + stat = USB_ST_STALLED; + } + + isp116x_show_regs(isp116x); + + break; + + case RH_SET_ADDRESS: + DBG("RH_SET_ADDRESS"); + + rh_devnum = wValue; + len = 0; + break; + + case RH_GET_DESCRIPTOR: + DBG("RH_GET_DESCRIPTOR: %x, %d", wValue, wLength); + + switch (wValue) { + case (USB_DT_DEVICE << 8): /* device descriptor */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; + break; + + case (USB_DT_CONFIG << 8): /* configuration descriptor */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; + break; + + case ((USB_DT_STRING << 8) | 0x00): /* string 0 descriptors */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + break; + + case ((USB_DT_STRING << 8) | 0x01): /* string 1 descriptors */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + break; + + default: + ERR("invalid wValue"); + stat = USB_ST_STALLED; + } + + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + DBG("RH_GET_DESCRIPTOR | RH_CLASS"); + + tmp = isp116x_read_reg32(isp116x, HCRHDESCA); + + data_buf[0] = 0x09; /* min length; */ + data_buf[1] = 0x29; + data_buf[2] = tmp & RH_A_NDP; + data_buf[3] = 0; + if (tmp & RH_A_PSM) /* per-port power switching? */ + data_buf[3] |= 0x01; + if (tmp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf[3] |= 0x10; + else if (tmp & RH_A_OCPM) /* per-port overcurrent rep? */ + data_buf[3] |= 0x08; + + /* Corresponds to data_buf[4-7] */ + datab[1] = 0; + data_buf[5] = (tmp & RH_A_POTPGT) >> 24; + + tmp = isp116x_read_reg32(isp116x, HCRHDESCB); + + data_buf[7] = tmp & RH_B_DR; + if (data_buf[2] < 7) + data_buf[8] = 0xff; + else { + data_buf[0] += 2; + data_buf[8] = (tmp & RH_B_DR) >> 8; + data_buf[10] = data_buf[9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf[0], wLength)); + break; + + case RH_GET_CONFIGURATION: + DBG("RH_GET_CONFIGURATION"); + + *(__u8 *) data_buf = 0x01; + len = 1; + break; + + case RH_SET_CONFIGURATION: + DBG("RH_SET_CONFIGURATION"); + + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPSC); + len = 0; + break; + + default: + ERR("*** *** *** unsupported root hub command *** *** ***"); + stat = USB_ST_STALLED; + } + + len = min_t(int, len, leni); + if (buffer != data_buf) + memcpy(buffer, data_buf, len); + + dev->act_len = len; + dev->status = stat; + DBG("dev act_len %d, status %d", dev->act_len, dev->status); + + dump_msg(dev, pipe, buffer, transfer_len, "RH(ret)"); + + return stat; +} + +/* --- Transfer functions -------------------------------------------------- */ + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, int interval) +{ + DBG("dev=%p pipe=%#lx buf=%p size=%d int=%d", + dev, pipe, buffer, len, interval); + + return -1; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + int devnum = usb_pipedevice(pipe); + int epnum = usb_pipeendpoint(pipe); + int max = max_transfer_len(dev, pipe); + int dir_in = usb_pipein(pipe); + int done, ret; + + /* Control message is for the HUB? */ + if (devnum == rh_devnum) + return isp116x_submit_rh_msg(dev, pipe, buffer, len, setup); + + /* Ok, no HUB message so send the message to the device */ + + /* Setup phase */ + DBG("--- SETUP PHASE --------------------------------"); + usb_settoggle(dev, epnum, 1, 0); + ret = isp116x_submit_job(dev, pipe, + PTD_DIR_SETUP, + setup, sizeof(struct devrequest)); + if (ret < 0) { + DBG("control setup phase error (ret = %d", ret); + return -1; + } + + /* Data phase */ + DBG("--- DATA PHASE ---------------------------------"); + done = 0; + usb_settoggle(dev, epnum, !dir_in, 1); + while (done < len) { + ret = isp116x_submit_job(dev, pipe, + dir_in ? PTD_DIR_IN : PTD_DIR_OUT, + (__u8 *) buffer + done, + max > len - done ? len - done : max); + if (ret < 0) { + DBG("control data phase error (ret = %d)", ret); + return -1; + } + done += ret; + + if (dir_in && ret < max) /* short packet */ + break; + } + + /* Status phase */ + DBG("--- STATUS PHASE -------------------------------"); + usb_settoggle(dev, epnum, !dir_in, 1); + ret = isp116x_submit_job(dev, pipe, + !dir_in ? PTD_DIR_IN : PTD_DIR_OUT, NULL, 0); + if (ret < 0) { + DBG("control status phase error (ret = %d", ret); + return -1; + } + + dev->act_len = done; + + dump_msg(dev, pipe, buffer, len, "DEV(ret)"); + + return done; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len) +{ + int dir_out = usb_pipeout(pipe); + int max = max_transfer_len(dev, pipe); + int done, ret; + + DBG("--- BULK ---------------------------------------"); + DBG("dev=%ld pipe=%ld buf=%p size=%d dir_out=%d", + usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); + + done = 0; + while (done < len) { + ret = isp116x_submit_job(dev, pipe, + !dir_out ? PTD_DIR_IN : PTD_DIR_OUT, + (__u8 *) buffer + done, + max > len - done ? len - done : max); + if (ret < 0) { + DBG("error on bulk message (ret = %d)", ret); + return -1; + } + + done += ret; + + if (!dir_out && ret < max) /* short packet */ + break; + } + + dev->act_len = done; + + return 0; +} + +/* --- Basic functions ----------------------------------------------------- */ + +static int isp116x_sw_reset(struct isp116x *isp116x) +{ + int retries = 15; + int ret = 0; + + DBG(""); + + isp116x->disabled = 1; + + isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC); + isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR); + while (--retries) { + /* It usually resets within 1 ms */ + mdelay(1); + if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR)) + break; + } + if (!retries) { + ERR("software reset timeout"); + ret = -1; + } + return ret; +} + +static int isp116x_reset(struct isp116x *isp116x) +{ + unsigned long t; + u16 clkrdy = 0; + int ret, timeout = 15 /* ms */ ; + + DBG(""); + + ret = isp116x_sw_reset(isp116x); + if (ret) + return ret; + + for (t = 0; t < timeout; t++) { + clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY; + if (clkrdy) + break; + mdelay(1); + } + if (!clkrdy) { + ERR("clock not ready after %dms", timeout); + /* After sw_reset the clock won't report to be ready, if + H_WAKEUP pin is high. */ + ERR("please make sure that the H_WAKEUP pin is pulled low!"); + ret = -1; + } + return ret; +} + +static void isp116x_stop(struct isp116x *isp116x) +{ + u32 val; + + DBG(""); + + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + + /* Switch off ports' power, some devices don't come up + after next 'start' without this */ + val = isp116x_read_reg32(isp116x, HCRHDESCA); + val &= ~(RH_A_NPS | RH_A_PSM); + isp116x_write_reg32(isp116x, HCRHDESCA, val); + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS); + + isp116x_sw_reset(isp116x); +} + +/* + * Configure the chip. The chip must be successfully reset by now. + */ +static int isp116x_start(struct isp116x *isp116x) +{ + struct isp116x_platform_data *board = isp116x->board; + u32 val; + + DBG(""); + + /* Clear interrupt status and disable all interrupt sources */ + isp116x_write_reg16(isp116x, HCuPINT, 0xff); + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + + isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE); + isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE); + + /* Hardware configuration */ + val = HCHWCFG_DBWIDTH(1); + if (board->sel15Kres) + val |= HCHWCFG_15KRSEL; + /* Remote wakeup won't work without working clock */ + if (board->remote_wakeup_enable) + val |= HCHWCFG_CLKNOTSTOP; + if (board->oc_enable) + val |= HCHWCFG_ANALOG_OC; + isp116x_write_reg16(isp116x, HCHWCFG, val); + + /* --- Root hub configuration */ + val = (25 << 24) & RH_A_POTPGT; + /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to + be always set. Yet, instead, we request individual port + power switching. */ + val |= RH_A_PSM; + /* Report overcurrent per port */ + val |= RH_A_OCPM; + isp116x_write_reg32(isp116x, HCRHDESCA, val); + isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA); + + val = RH_B_PPCM; + isp116x_write_reg32(isp116x, HCRHDESCB, val); + isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB); + + val = 0; + if (board->remote_wakeup_enable) + val |= RH_HS_DRWE; + isp116x_write_reg32(isp116x, HCRHSTATUS, val); + isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS); + + isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf); + + /* Go operational */ + val = HCCONTROL_USB_OPER; + if (board->remote_wakeup_enable) + val |= HCCONTROL_RWE; + isp116x_write_reg32(isp116x, HCCONTROL, val); + + /* Disable ports to avoid race in device enumeration */ + isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS); + isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS); + + isp116x_show_regs(isp116x); + + isp116x->disabled = 0; + + return 0; +} + +/* --- Init functions ------------------------------------------------------ */ + +int isp116x_check_id(struct isp116x *isp116x) +{ + int val; + + val = isp116x_read_reg16(isp116x, HCCHIPID); + if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) { + ERR("invalid chip ID %04x", val); + return -1; + } + + return 0; +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)) +{ + struct isp116x *isp116x = &isp116x_dev; + + DBG(""); + + got_rhsc = rh_devnum = 0; + + /* Init device registers addr */ + isp116x->addr_reg = (u16 *) ISP116X_HCD_ADDR; + isp116x->data_reg = (u16 *) ISP116X_HCD_DATA; + + /* Setup specific board settings */ +#ifdef ISP116X_HCD_SEL15kRES + isp116x_board.sel15Kres = 1; +#endif +#ifdef ISP116X_HCD_OC_ENABLE + isp116x_board.oc_enable = 1; +#endif +#ifdef ISP116X_HCD_REMOTE_WAKEUP_ENABLE + isp116x_board.remote_wakeup_enable = 1; +#endif + isp116x->board = &isp116x_board; + + /* Try to get ISP116x silicon chip ID */ + if (isp116x_check_id(isp116x) < 0) + return -1; + + isp116x->disabled = 1; + isp116x->sleeping = 0; + + isp116x_reset(isp116x); + isp116x_start(isp116x); + + return 0; +} + +int usb_lowlevel_stop(int index) +{ + struct isp116x *isp116x = &isp116x_dev; + + DBG(""); + + if (!isp116x->disabled) + isp116x_stop(isp116x); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/isp116x.h b/qemu/roms/u-boot/drivers/usb/host/isp116x.h new file mode 100644 index 000000000..5b7afaf42 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/isp116x.h @@ -0,0 +1,476 @@ +/* + * ISP116x register declarations and HCD data structures + * + * Copyright (C) 2007 Rodolfo Giometti + * Copyright (C) 2007 Eurotech S.p.A. + * Copyright (C) 2005 Olav Kongas + * Portions: + * Copyright (C) 2004 Lothar Wassmann + * Copyright (C) 2004 Psion Teklogix + * Copyright (C) 2004 David Brownell + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifdef DEBUG +#define DBG(fmt, args...) \ + printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#else +#define DBG(fmt, args...) do {} while (0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(fmt, args...) do {} while (0) +#endif + +#define ERR(fmt, args...) \ + printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#define WARN(fmt, args...) \ + printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#define INFO(fmt, args...) \ + printf("isp116x: " fmt "\n" , ## args) + +/* ------------------------------------------------------------------------- */ + +/* us of 1ms frame */ +#define MAX_LOAD_LIMIT 850 + +/* Full speed: max # of bytes to transfer for a single urb + at a time must be < 1024 && must be multiple of 64. + 832 allows transfering 4kiB within 5 frames. */ +#define MAX_TRANSFER_SIZE_FULLSPEED 832 + +/* Low speed: there is no reason to schedule in very big + chunks; often the requested long transfers are for + string descriptors containing short strings. */ +#define MAX_TRANSFER_SIZE_LOWSPEED 64 + +/* Bytetime (us), a rough indication of how much time it + would take to transfer a byte of useful data over USB */ +#define BYTE_TIME_FULLSPEED 1 +#define BYTE_TIME_LOWSPEED 20 + +/* Buffer sizes */ +#define ISP116x_BUF_SIZE 4096 +#define ISP116x_ITL_BUFSIZE 0 +#define ISP116x_ATL_BUFSIZE ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE)) + +#define ISP116x_WRITE_OFFSET 0x80 + +/* --- ISP116x registers/bits ---------------------------------------------- */ + +#define HCREVISION 0x00 +#define HCCONTROL 0x01 +#define HCCONTROL_HCFS (3 << 6) /* host controller + functional state */ +#define HCCONTROL_USB_RESET (0 << 6) +#define HCCONTROL_USB_RESUME (1 << 6) +#define HCCONTROL_USB_OPER (2 << 6) +#define HCCONTROL_USB_SUSPEND (3 << 6) +#define HCCONTROL_RWC (1 << 9) /* remote wakeup connected */ +#define HCCONTROL_RWE (1 << 10) /* remote wakeup enable */ +#define HCCMDSTAT 0x02 +#define HCCMDSTAT_HCR (1 << 0) /* host controller reset */ +#define HCCMDSTAT_SOC (3 << 16) /* scheduling overrun count */ +#define HCINTSTAT 0x03 +#define HCINT_SO (1 << 0) /* scheduling overrun */ +#define HCINT_WDH (1 << 1) /* writeback of done_head */ +#define HCINT_SF (1 << 2) /* start frame */ +#define HCINT_RD (1 << 3) /* resume detect */ +#define HCINT_UE (1 << 4) /* unrecoverable error */ +#define HCINT_FNO (1 << 5) /* frame number overflow */ +#define HCINT_RHSC (1 << 6) /* root hub status change */ +#define HCINT_OC (1 << 30) /* ownership change */ +#define HCINT_MIE (1 << 31) /* master interrupt enable */ +#define HCINTENB 0x04 +#define HCINTDIS 0x05 +#define HCFMINTVL 0x0d +#define HCFMREM 0x0e +#define HCFMNUM 0x0f +#define HCLSTHRESH 0x11 +#define HCRHDESCA 0x12 +#define RH_A_NDP (0x3 << 0) /* # downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* overcurrent protection + mode */ +#define RH_A_NOCP (1 << 12) /* no overcurrent protection */ +#define RH_A_POTPGT (0xff << 24) /* power on -> power good + time */ +#define HCRHDESCB 0x13 +#define RH_B_DR (0xffff << 0) /* device removable flags */ +#define RH_B_PPCM (0xffff << 16) /* port power control mask */ +#define HCRHSTATUS 0x14 +#define RH_HS_LPS (1 << 0) /* local power status */ +#define RH_HS_OCI (1 << 1) /* over current indicator */ +#define RH_HS_DRWE (1 << 15) /* device remote wakeup + enable */ +#define RH_HS_LPSC (1 << 16) /* local power status change */ +#define RH_HS_OCIC (1 << 17) /* over current indicator + change */ +#define RH_HS_CRWE (1 << 31) /* clear remote wakeup + enable */ +#define HCRHPORT1 0x15 +#define RH_PS_CCS (1 << 0) /* current connect status */ +#define RH_PS_PES (1 << 1) /* port enable status */ +#define RH_PS_PSS (1 << 2) /* port suspend status */ +#define RH_PS_POCI (1 << 3) /* port over current + indicator */ +#define RH_PS_PRS (1 << 4) /* port reset status */ +#define RH_PS_PPS (1 << 8) /* port power status */ +#define RH_PS_LSDA (1 << 9) /* low speed device attached */ +#define RH_PS_CSC (1 << 16) /* connect status change */ +#define RH_PS_PESC (1 << 17) /* port enable status change */ +#define RH_PS_PSSC (1 << 18) /* port suspend status + change */ +#define RH_PS_OCIC (1 << 19) /* over current indicator + change */ +#define RH_PS_PRSC (1 << 20) /* port reset status change */ +#define HCRHPORT_CLRMASK (0x1f << 16) +#define HCRHPORT2 0x16 +#define HCHWCFG 0x20 +#define HCHWCFG_15KRSEL (1 << 12) +#define HCHWCFG_CLKNOTSTOP (1 << 11) +#define HCHWCFG_ANALOG_OC (1 << 10) +#define HCHWCFG_DACK_MODE (1 << 8) +#define HCHWCFG_EOT_POL (1 << 7) +#define HCHWCFG_DACK_POL (1 << 6) +#define HCHWCFG_DREQ_POL (1 << 5) +#define HCHWCFG_DBWIDTH_MASK (0x03 << 3) +#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK) +#define HCHWCFG_INT_POL (1 << 2) +#define HCHWCFG_INT_TRIGGER (1 << 1) +#define HCHWCFG_INT_ENABLE (1 << 0) +#define HCDMACFG 0x21 +#define HCDMACFG_BURST_LEN_MASK (0x03 << 5) +#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK) +#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0) +#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1) +#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2) +#define HCDMACFG_DMA_ENABLE (1 << 4) +#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1) +#define HCDMACFG_CTR_SEL (1 << 2) +#define HCDMACFG_ITLATL_SEL (1 << 1) +#define HCDMACFG_DMA_RW_SELECT (1 << 0) +#define HCXFERCTR 0x22 +#define HCuPINT 0x24 +#define HCuPINT_SOF (1 << 0) +#define HCuPINT_ATL (1 << 1) +#define HCuPINT_AIIEOT (1 << 2) +#define HCuPINT_OPR (1 << 4) +#define HCuPINT_SUSP (1 << 5) +#define HCuPINT_CLKRDY (1 << 6) +#define HCuPINTENB 0x25 +#define HCCHIPID 0x27 +#define HCCHIPID_MASK 0xff00 +#define HCCHIPID_MAGIC 0x6100 +#define HCSCRATCH 0x28 +#define HCSWRES 0x29 +#define HCSWRES_MAGIC 0x00f6 +#define HCITLBUFLEN 0x2a +#define HCATLBUFLEN 0x2b +#define HCBUFSTAT 0x2c +#define HCBUFSTAT_ITL0_FULL (1 << 0) +#define HCBUFSTAT_ITL1_FULL (1 << 1) +#define HCBUFSTAT_ATL_FULL (1 << 2) +#define HCBUFSTAT_ITL0_DONE (1 << 3) +#define HCBUFSTAT_ITL1_DONE (1 << 4) +#define HCBUFSTAT_ATL_DONE (1 << 5) +#define HCRDITL0LEN 0x2d +#define HCRDITL1LEN 0x2e +#define HCITLPORT 0x40 +#define HCATLPORT 0x41 + +/* PTD accessor macros. */ +#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0) +#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK) +#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10) +#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK) +#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11) +#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK) +#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12) +#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK) +#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0) +#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK) +#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10) +#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK) +#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11) +#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK) +#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12) +#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK) +#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0) +#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK) +#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10) +#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK) +#define PTD_GET_B5_5(p) (((p)->len & PTD_B5_5_MSK) >> 13) +#define PTD_B5_5(v) (((v) << 13) & PTD_B5_5_MSK) +#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0) +#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK) +#define PTD_GET_FMT(p) (((p)->faddr & PTD_FMT_MSK) >> 7) +#define PTD_FMT(v) (((v) << 7) & PTD_FMT_MSK) + +/* Hardware transfer status codes -- CC from ptd->count */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 + /* 0x0A, 0x0B reserved for hardware */ +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D + /* 0x0E, 0x0F reserved for HCD */ +#define TD_NOTACCESSED 0x0F + +/* ------------------------------------------------------------------------- */ + +#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + +/* Philips transfer descriptor */ +struct ptd { + u16 count; +#define PTD_COUNT_MSK (0x3ff << 0) +#define PTD_TOGGLE_MSK (1 << 10) +#define PTD_ACTIVE_MSK (1 << 11) +#define PTD_CC_MSK (0xf << 12) + u16 mps; +#define PTD_MPS_MSK (0x3ff << 0) +#define PTD_SPD_MSK (1 << 10) +#define PTD_LAST_MSK (1 << 11) +#define PTD_EP_MSK (0xf << 12) + u16 len; +#define PTD_LEN_MSK (0x3ff << 0) +#define PTD_DIR_MSK (3 << 10) +#define PTD_DIR_SETUP (0) +#define PTD_DIR_OUT (1) +#define PTD_DIR_IN (2) +#define PTD_B5_5_MSK (1 << 13) + u16 faddr; +#define PTD_FA_MSK (0x7f << 0) +#define PTD_FMT_MSK (1 << 7) +} __attribute__ ((packed, aligned(2))); + +struct isp116x_ep { + struct usb_device *udev; + struct ptd ptd; + + u8 maxpacket; + u8 epnum; + u8 nextpid; + + u16 length; /* of current packet */ + unsigned char *data; /* to databuf */ + + u16 error_count; +}; + +/* URB struct */ +#define N_URB_TD 48 +#define URB_DEL 1 +typedef struct { + struct isp116x_ep *ed; + void *transfer_buffer; /* (in) associated data buffer */ + int actual_length; /* (return) actual transfer length */ + unsigned long pipe; /* (in) pipe information */ +#if 0 + int state; +#endif +} urb_priv_t; + +struct isp116x_platform_data { + /* Enable internal resistors on downstream ports */ + unsigned sel15Kres:1; + /* On-chip overcurrent detection */ + unsigned oc_enable:1; + /* Enable wakeup by devices on usb bus (e.g. wakeup + by attachment/detachment or by device activity + such as moving a mouse). When chosen, this option + prevents stopping internal clock, increasing + thereby power consumption in suspended state. */ + unsigned remote_wakeup_enable:1; +}; + +struct isp116x { + u16 *addr_reg; + u16 *data_reg; + + struct isp116x_platform_data *board; + + struct dentry *dentry; + unsigned long stat1, stat2, stat4, stat8, stat16; + + /* Status flags */ + unsigned disabled:1; + unsigned sleeping:1; + + /* Root hub registers */ + u32 rhdesca; + u32 rhdescb; + u32 rhstatus; + u32 rhport[2]; + + /* Schedule for the current frame */ + struct isp116x_ep *atl_active; + int atl_buflen; + int atl_bufshrt; + int atl_last_dir; + int atl_finishing; +}; + +/* ------------------------------------------------- */ + +/* Inter-io delay (ns). The chip is picky about access timings; it + * expects at least: + * 150ns delay between consecutive accesses to DATA_REG, + * 300ns delay between access to ADDR_REG and DATA_REG + * OE, WE MUST NOT be changed during these intervals + */ +#if defined(UDELAY) +#define isp116x_delay(h,d) udelay(d) +#else +#define isp116x_delay(h,d) do {} while (0) +#endif + +static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg) +{ + writew(reg & 0xff, isp116x->addr_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val) +{ + writew(val, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val) +{ + __raw_writew(val, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline u16 isp116x_read_data16(struct isp116x *isp116x) +{ + u16 val; + + val = readw(isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + return val; +} + +static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x) +{ + u16 val; + + val = __raw_readw(isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + return val; +} + +static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val) +{ + writew(val & 0xffff, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + writew(val >> 16, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline u32 isp116x_read_data32(struct isp116x *isp116x) +{ + u32 val; + + val = (u32) readw(isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + val |= ((u32) readw(isp116x->data_reg)) << 16; + isp116x_delay(isp116x, UDELAY); + return val; +} + +/* Let's keep register access functions out of line. Hint: + we wait at least 150 ns at every access. +*/ +static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg) +{ + isp116x_write_addr(isp116x, reg); + return isp116x_read_data16(isp116x); +} + +static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg) +{ + isp116x_write_addr(isp116x, reg); + return isp116x_read_data32(isp116x); +} + +static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg, + unsigned val) +{ + isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); + isp116x_write_data16(isp116x, (u16) (val & 0xffff)); +} + +static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg, + unsigned val) +{ + isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); + isp116x_write_data32(isp116x, (u32) val); +} + +/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 diff --git a/qemu/roms/u-boot/drivers/usb/host/ohci-at91.c b/qemu/roms/u-boot/drivers/usb/host/ohci-at91.c new file mode 100644 index 000000000..c24505e78 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ohci-at91.c @@ -0,0 +1,96 @@ +/* + * (C) Copyright 2006 + * DENX Software Engineering + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) + +#include +#include +#include +#include + +int usb_cpu_init(void) +{ + at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC; + +#ifdef CONFIG_USB_ATMEL_CLK_SEL_PLLB + /* Enable PLLB */ + writel(get_pllb_init(), &pmc->pllbr); + while ((readl(&pmc->sr) & AT91_PMC_LOCKB) != AT91_PMC_LOCKB) + ; +#ifdef CONFIG_AT91SAM9N12 + writel(AT91_PMC_USBS_USB_PLLB | AT91_PMC_USB_DIV_2, &pmc->usb); +#endif +#elif defined(CONFIG_USB_ATMEL_CLK_SEL_UPLL) + /* Enable UPLL */ + writel(readl(&pmc->uckr) | AT91_PMC_UPLLEN | AT91_PMC_BIASEN, + &pmc->uckr); + while ((readl(&pmc->sr) & AT91_PMC_LOCKU) != AT91_PMC_LOCKU) + ; + + /* Select PLLA as input clock of OHCI */ + writel(AT91_PMC_USBS_USB_UPLL | AT91_PMC_USBDIV_10, &pmc->usb); +#endif + + /* Enable USB host clock. */ +#ifdef CONFIG_SAMA5D3 + writel(1 << (ATMEL_ID_UHP - 32), &pmc->pcer1); +#else + writel(1 << ATMEL_ID_UHP, &pmc->pcer); +#endif + +#if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10) + writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scer); +#else + writel(ATMEL_PMC_UHP, &pmc->scer); +#endif + + return 0; +} + +int usb_cpu_stop(void) +{ + at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC; + + /* Disable USB host clock. */ +#ifdef CONFIG_SAMA5D3 + writel(1 << (ATMEL_ID_UHP - 32), &pmc->pcdr1); +#else + writel(1 << ATMEL_ID_UHP, &pmc->pcdr); +#endif + +#if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10) + writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scdr); +#else + writel(ATMEL_PMC_UHP, &pmc->scdr); +#endif + +#ifdef CONFIG_USB_ATMEL_CLK_SEL_PLLB +#ifdef CONFIG_AT91SAM9N12 + writel(0, &pmc->usb); +#endif + /* Disable PLLB */ + writel(0, &pmc->pllbr); + while ((readl(&pmc->sr) & AT91_PMC_LOCKB) != 0) + ; +#elif defined(CONFIG_USB_ATMEL_CLK_SEL_UPLL) + /* Disable UPLL */ + writel(readl(&pmc->uckr) & (~AT91_PMC_UPLLEN), &pmc->uckr); + while ((readl(&pmc->sr) & AT91_PMC_LOCKU) == AT91_PMC_LOCKU) + ; +#endif + + return 0; +} + +int usb_cpu_init_fail(void) +{ + return usb_cpu_stop(); +} + +#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */ diff --git a/qemu/roms/u-boot/drivers/usb/host/ohci-da8xx.c b/qemu/roms/u-boot/drivers/usb/host/ohci-da8xx.c new file mode 100644 index 000000000..981662806 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ohci-da8xx.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Sughosh Ganu + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include + +int usb_cpu_init(void) +{ + /* enable psc for usb2.0 */ + lpsc_on(DAVINCI_LPSC_USB20); + + /* enable psc for usb1.0 */ + lpsc_on(DAVINCI_LPSC_USB11); + + /* start the on-chip usb phy and its pll */ + if (usb_phy_on()) + return 0; + + return 1; +} + +int usb_cpu_stop(void) +{ + usb_phy_off(); + + /* turn off the usb clock and assert the module reset */ + lpsc_disable(DAVINCI_LPSC_USB11); + lpsc_disable(DAVINCI_LPSC_USB20); + + return 0; +} + +int usb_cpu_init_fail(void) +{ + return usb_cpu_stop(); +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ohci-hcd.c b/qemu/roms/u-boot/drivers/usb/host/ohci-hcd.c new file mode 100644 index 000000000..dc0a4e317 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ohci-hcd.c @@ -0,0 +1,1885 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus. + * + * Interrupt support is added. Now, it has been tested + * on ULI1575 chip and works well with USB keyboard. + * + * (C) Copyright 2007 + * Zhang Wei, Freescale Semiconductor, Inc. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * Modified for the MP2USB by (C) Copyright 2005 Eric Benard + * ebenard@eukrea.com - based on s3c24x0's driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* + * IMPORTANT NOTES + * 1 - Read doc/README.generic_usb_ohci + * 2 - this driver is intended for use with USB Mass Storage Devices + * (BBB) and USB keyboard. There is NO support for Isochronous pipes! + * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG + * to activate workaround for bug #41 or this driver will NOT work! + */ + +#include +#include + +#if defined(CONFIG_PCI_OHCI) +# include +#if !defined(CONFIG_PCI_OHCI_DEVNO) +#define CONFIG_PCI_OHCI_DEVNO 0 +#endif +#endif + +#include +#include + +#include "ohci.h" + +#ifdef CONFIG_AT91RM9200 +#include /* needed for AT91_USB_HOST_BASE */ +#endif + +#if defined(CONFIG_ARM920T) || \ + defined(CONFIG_S3C24X0) || \ + defined(CONFIG_440EP) || \ + defined(CONFIG_PCI_OHCI) || \ + defined(CONFIG_MPC5200) || \ + defined(CONFIG_SYS_OHCI_USE_NPS) +# define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#endif + +#undef OHCI_VERBOSE_DEBUG /* not always helpful */ +#undef DEBUG +#undef SHOW_INFO +#undef OHCI_FILL_TRACE + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define min_t(type, x, y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#ifdef CONFIG_PCI_OHCI +static struct pci_device_id ohci_pci_ids[] = { + {0x10b9, 0x5237}, /* ULI1575 PCI OHCI module ids */ + {0x1033, 0x0035}, /* NEC PCI OHCI module ids */ + {0x1131, 0x1561}, /* Philips 1561 PCI OHCI module ids */ + /* Please add supported PCI OHCI controller ids here */ + {0, 0} +}; +#endif + +#ifdef CONFIG_PCI_EHCI_DEVNO +static struct pci_device_id ehci_pci_ids[] = { + {0x1131, 0x1562}, /* Philips 1562 PCI EHCI module ids */ + /* Please add supported PCI EHCI controller ids here */ + {0, 0} +}; +#endif + +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while (0) +#endif + +#ifdef CONFIG_SYS_OHCI_BE_CONTROLLER +# define m16_swap(x) cpu_to_be16(x) +# define m32_swap(x) cpu_to_be32(x) +#else +# define m16_swap(x) cpu_to_le16(x) +# define m32_swap(x) cpu_to_le32(x) +#endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */ + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* device which was disconnected */ +struct usb_device *devgone; + +static inline u32 roothub_a(struct ohci *hc) + { return ohci_readl(&hc->regs->roothub.a); } +static inline u32 roothub_b(struct ohci *hc) + { return ohci_readl(&hc->regs->roothub.b); } +static inline u32 roothub_status(struct ohci *hc) + { return ohci_readl(&hc->regs->roothub.status); } +static inline u32 roothub_portstatus(struct ohci *hc, int i) + { return ohci_readl(&hc->regs->roothub.portstatus[i]); } + +/* forward declaration */ +static int hc_interrupt(void); +static void td_submit_job(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *setup, urb_priv_t *urb, + int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv(urb_priv_t *urb) +{ + int i; + int last; + struct td *td; + + last = urb->length - 1; + if (last >= 0) { + for (i = 0; i <= last; i++) { + td = urb->td[i]; + if (td) { + td->usb_dev = NULL; + urb->td[i] = NULL; + } + } + } + free(urb); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number(struct usb_device *dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print(urb_priv_t *purb, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, + struct devrequest *setup, char *str, int small) +{ + dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number(dev), + usb_pipedevice(pipe), + usb_pipeendpoint(pipe), + usb_pipeout(pipe)? 'O': 'I', + usb_pipetype(pipe) < 2 ? \ + (usb_pipeint(pipe)? "INTR": "ISOC"): \ + (usb_pipecontrol(pipe)? "CTRL": "BULK"), + (purb ? purb->actual_length : 0), + transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol(pipe)) { + printf(__FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printf(" %02x", ((__u8 *) setup) [i]); + printf("\n"); + } + if (transfer_len > 0 && buffer) { + printf(__FILE__ ": data(%d/%d):", + (purb ? purb->actual_length : 0), + transfer_len); + len = usb_pipeout(pipe)? transfer_len: + (purb ? purb->actual_length : 0); + for (i = 0; i < 16 && i < len; i++) + printf(" %02x", ((__u8 *) buffer) [i]); + printf("%s\n", i < len? "...": ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree + * inclusive iso eds */ +void ep_print_int_eds(ohci_t *ohci, char *str) +{ + int i, j; + __u32 *ed_p; + for (i = 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + ed_t *ed = (ed_t *)m32_swap(ed_p); + printf(" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printf("\n"); + } +} + +static void ohci_dump_intr_mask(char *label, __u32 mask) +{ + dbg("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} + +static void maybe_print_eds(char *label, __u32 value) +{ + ed_t *edp = (ed_t *)value; + + if (value) { + dbg("%s %08x", label, value); + dbg("%08x", edp->hwINFO); + dbg("%08x", edp->hwTailP); + dbg("%08x", edp->hwHeadP); + dbg("%08x", edp->hwNextED); + } +} + +static char *hcfs2string(int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status(ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = ohci_readl(®s->revision) & 0xff; + if (temp != 0x10) + dbg("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = ohci_readl(®s->control); + dbg("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string(temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = ohci_readl(®s->cmdstatus); + dbg("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask("intrstatus", ohci_readl(®s->intrstatus)); + ohci_dump_intr_mask("intrenable", ohci_readl(®s->intrenable)); + + maybe_print_eds("ed_periodcurrent", + ohci_readl(®s->ed_periodcurrent)); + + maybe_print_eds("ed_controlhead", ohci_readl(®s->ed_controlhead)); + maybe_print_eds("ed_controlcurrent", + ohci_readl(®s->ed_controlcurrent)); + + maybe_print_eds("ed_bulkhead", ohci_readl(®s->ed_bulkhead)); + maybe_print_eds("ed_bulkcurrent", ohci_readl(®s->ed_bulkcurrent)); + + maybe_print_eds("donehead", ohci_readl(®s->donehead)); +} + +static void ohci_dump_roothub(ohci_t *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a(controller); + ndp = (temp & RH_A_NDP); +#ifdef CONFIG_AT91C_PQFP_UHPBUG + ndp = (ndp == 2) ? 1:0; +#endif + if (verbose) { + dbg("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b(controller); + dbg("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status(controller); + dbg("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus(controller, i); + dbg("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump(ohci_t *controller, int verbose) +{ + dbg("OHCI controller usb-%s state", controller->slot_name); + + /* dumps some of the state we know about */ + ohci_dump_status(controller); + if (verbose) + ep_print_int_eds(controller, "hcca"); + dbg("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub(controller, 1); +} +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) +{ + ohci_t *ohci; + ed_t *ed; + urb_priv_t *purb_priv = urb; + int i, size = 0; + struct usb_device *dev = urb->dev; + unsigned long pipe = urb->pipe; + void *buffer = urb->transfer_buffer; + int transfer_len = urb->transfer_buffer_length; + int interval = urb->interval; + + ohci = &gohci; + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + err("sohci_submit_job: EPIPE"); + return -1; + } + + /* we're about to begin a new transaction here so mark the + * URB unfinished */ + urb->finished = 0; + + /* every endpoint has a ed, locate and fill it */ + ed = ep_add_ed(dev, pipe, interval, 1); + if (!ed) { + err("sohci_submit_job: ENOMEM"); + return -1; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype(pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL:/* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0)? 2: + (transfer_len - 1) / 4096 + 3; + break; + case PIPE_INTERRUPT: /* 1 TD */ + size = 1; + break; + } + + ed->purb = urb; + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv->pipe = pipe; + + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for (i = 0; i < size; i++) { + purb_priv->td[i] = td_alloc(dev); + if (!purb_priv->td[i]) { + purb_priv->length = i; + urb_free_priv(purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv(purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link(ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_job(dev, pipe, buffer, transfer_len, + setup, purb_priv, interval); + + return 0; +} + +static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb) +{ + struct ohci_regs *regs = hc->regs; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_INTERRUPT: + /* implicitly requeued */ + if (urb->dev->irq_handle && + (urb->dev->irq_act_len = urb->actual_length)) { + ohci_writel(OHCI_INTR_WDH, ®s->intrenable); + ohci_readl(®s->intrenable); /* PCI posting flush */ + urb->dev->irq_handle(urb->dev); + ohci_writel(OHCI_INTR_WDH, ®s->intrdisable); + ohci_readl(®s->intrdisable); /* PCI posting flush */ + } + urb->actual_length = 0; + td_submit_job( + urb->dev, + urb->pipe, + urb->transfer_buffer, + urb->transfer_buffer_length, + NULL, + urb, + urb->interval); + break; + case PIPE_CONTROL: + case PIPE_BULK: + break; + default: + return 0; + } + return 1; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number(struct usb_device *usb_dev) +{ + ohci_t *ohci = &gohci; + + return m16_swap(ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* search for the right branch to insert an interrupt ed into the int tree + * do some load ballancing; + * returns the branch and + * sets the interval to interval = 2^integer (ld (interval)) */ + +static int ep_int_ballance(ohci_t *ohci, int interval, int load) +{ + int i, branch = 0; + + /* search for the least loaded interrupt endpoint + * branch of all 32 branches + */ + for (i = 0; i < 32; i++) + if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) + branch = i; + + branch = branch % interval; + for (i = branch; i < 32; i += interval) + ohci->ohci_int_load [i] += load; + + return branch; +} + +/*-------------------------------------------------------------------------*/ + +/* 2^int( ld (inter)) */ + +static int ep_2_n_interval(int inter) +{ + int i; + for (i = 0; ((inter >> i) > 1) && (i < 5); i++); + return 1 << i; +} + +/*-------------------------------------------------------------------------*/ + +/* the int tree is a binary tree + * in order to process it sequentially the indexes of the branches have to + * be mapped the mapping reverses the bits of a word of num_bits length */ +static int ep_rev(int num_bits, int word) +{ + int i, wout = 0; + + for (i = 0; i < num_bits; i++) + wout |= (((word >> i) & 1) << (num_bits - i - 1)); + return wout; +} + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link(ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + int int_branch; + int i; + int inter; + int interval; + int load; + __u32 *ed_p; + + ed->state = ED_OPER; + ed->int_interval = 0; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) + ohci_writel(ed, &ohci->regs->ed_controlhead); + else + ohci->ed_controltail->hwNextED = + m32_swap((unsigned long)ed); + + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_CLE; + ohci_writel(ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) + ohci_writel(ed, &ohci->regs->ed_bulkhead); + else + ohci->ed_bulktail->hwNextED = + m32_swap((unsigned long)ed); + + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_BLE; + ohci_writel(ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + + case PIPE_INTERRUPT: + load = ed->int_load; + interval = ep_2_n_interval(ed->int_period); + ed->int_interval = interval; + int_branch = ep_int_ballance(ohci, interval, load); + ed->int_branch = int_branch; + + for (i = 0; i < ep_rev(6, interval); i += inter) { + inter = 1; + for (ed_p = &(ohci->hcca->int_table[\ + ep_rev(5, i) + int_branch]); + (*ed_p != 0) && + (((ed_t *)ed_p)->int_interval >= interval); + ed_p = &(((ed_t *)ed_p)->hwNextED)) + inter = ep_rev(6, + ((ed_t *)ed_p)->int_interval); + ed->hwNextED = *ed_p; + *ed_p = m32_swap((unsigned long)ed); + } + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* scan the periodic table to find and unlink this ED */ +static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, + unsigned index, unsigned period) +{ + for (; index < NUM_INTS; index += period) { + __u32 *ed_p = &ohci->hcca->int_table [index]; + + /* ED might have been unlinked through another path */ + while (*ed_p != 0) { + if (((struct ed *) + m32_swap((unsigned long)ed_p)) == ed) { + *ed_p = ed->hwNextED; + break; + } + ed_p = &(((struct ed *) + m32_swap((unsigned long)ed_p))->hwNextED); + } + } +} + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink(ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + int i; + + ed->hwINFO |= m32_swap(OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + ohci_writel(ohci->hc_control, + &ohci->regs->control); + } + ohci_writel(m32_swap(*((__u32 *)&ed->hwNextED)), + &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *)m32_swap( + *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + ohci_writel(ohci->hc_control, + &ohci->regs->control); + } + ohci_writel(m32_swap(*((__u32 *)&ed->hwNextED)), + &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *)m32_swap( + *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_INTERRUPT: + periodic_unlink(ohci, ed, 0, 1); + for (i = ed->int_branch; i < 32; i += ed->int_interval) + ohci->ohci_int_load[i] -= ed->int_load; + break; + } + ed->state = ED_UNLINK; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the + * usb_set_configuration command, but the USB stack is a little bit + * stateless so we do it at every transaction if the state of the ed + * is ED_NEW then a dummy td is added and the state is changed to + * ED_UNLINK in all other cases the state is left unchanged the ed + * info fields are setted anyway even though most of them should not + * change + */ +static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe, + int interval, int load) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + + ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) | + (usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))]; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + + if (ed->state == ED_NEW) { + /* dummy td; end of td list for ed */ + td = td_alloc(usb_dev); + ed->hwTailP = m32_swap((unsigned long)td); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype(pipe); + ohci_dev.ed_cnt++; + } + + ed->hwINFO = m32_swap(usb_pipedevice(pipe) + | usb_pipeendpoint(pipe) << 7 + | (usb_pipeisoc(pipe)? 0x8000: 0) + | (usb_pipecontrol(pipe)? 0: \ + (usb_pipeout(pipe)? 0x800: 0x1000)) + | (usb_dev->speed == USB_SPEED_LOW) << 13 + | usb_maxpacket(usb_dev, pipe) << 16); + + if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { + ed->int_period = interval; + ed->int_load = load; + } + + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill(ohci_t *ohci, unsigned int info, + void *data, int len, + struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ + volatile td_t *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + + if (index > urb_priv->length) { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = + (td_t *)(m32_swap(urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x ", i, ((unsigned char *)td->data)[i]); + printf("\n"); + } +#endif + if (!len) + data = 0; + + td->hwINFO = m32_swap(info); + td->hwCBP = m32_swap((unsigned long)data); + if (data) + td->hwBE = m32_swap((unsigned long)(data + len - 1)); + else + td->hwBE = 0; + + td->hwNextTD = m32_swap((unsigned long)td_pt); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_job(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *setup, urb_priv_t *urb, + int interval) +{ + ohci_t *ohci = &gohci; + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle + * bits for reseting */ + if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), 1); + } + urb->td_cnt = 0; + if (data_len) + data = buffer; + else + data = 0; + + switch (usb_pipetype(pipe)) { + case PIPE_BULK: + info = usb_pipeout(pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while (data_len > 4096) { + td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), + data, 4096, dev, cnt, urb); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout(pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), data, + data_len, dev, cnt, urb); + cnt++; + + if (!ohci->sleeping) { + /* start bulk list */ + ohci_writel(OHCI_BLF, &ohci->regs->cmdstatus); + } + break; + + case PIPE_CONTROL: + /* Setup phase */ + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill(ohci, info, setup, 8, dev, cnt++, urb); + + /* Optional Data phase */ + if (data_len > 0) { + info = usb_pipeout(pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : + TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill(ohci, info, data, data_len, dev, cnt++, urb); + } + + /* Status phase */ + info = usb_pipeout(pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: + TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill(ohci, info, data, 0, dev, cnt++, urb); + + if (!ohci->sleeping) { + /* start Control list */ + ohci_writel(OHCI_CLF, &ohci->regs->cmdstatus); + } + break; + + case PIPE_INTERRUPT: + info = usb_pipeout(urb->pipe)? + TD_CC | TD_DP_OUT | toggle: + TD_CC | TD_R | TD_DP_IN | toggle; + td_fill(ohci, info, data, data_len, dev, cnt++, urb); + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t *td) +{ + __u32 tdBE, tdCBP; + urb_priv_t *lurb_priv = td->ed->purb; + + tdBE = m32_swap(td->hwBE); + tdCBP = m32_swap(td->hwCBP); + + if (!(usb_pipecontrol(lurb_priv->pipe) && + ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + lurb_priv->actual_length += tdBE - td->data + 1; + else + lurb_priv->actual_length += tdCBP - td->data; + } + } +} + +/*-------------------------------------------------------------------------*/ +static void check_status(td_t *td_list) +{ + urb_priv_t *lurb_priv = td_list->ed->purb; + int urb_len = lurb_priv->length; + __u32 *phwHeadP = &td_list->ed->hwHeadP; + int cc; + + cc = TD_CC_GET(m32_swap(td_list->hwINFO)); + if (cc) { + err(" USB-error: %s (%x)", cc_to_string[cc], cc); + + if (*phwHeadP & m32_swap(0x1)) { + if (lurb_priv && + ((td_list->index + 1) < urb_len)) { + *phwHeadP = + (lurb_priv->td[urb_len - 1]->hwNextTD &\ + m32_swap(0xfffffff0)) | + (*phwHeadP & m32_swap(0x2)); + + lurb_priv->td_cnt += urb_len - + td_list->index - 1; + } else + *phwHeadP &= m32_swap(0xfffffff2); + } +#ifdef CONFIG_MPC5200 + td_list->hwNextTD = 0; +#endif + } +} + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ +static td_t *dl_reverse_done_list(ohci_t *ohci) +{ + __u32 td_list_hc; + td_t *td_rev = NULL; + td_t *td_list = NULL; + + td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = (td_t *)td_list_hc; + check_status(td_list); + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = m32_swap(td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ + +static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status) +{ + if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL)) + urb->finished = sohci_return_job(ohci, urb); + else + dbg("finish_urb: strange.., ED state %x, \n", status); +} + +/* + * Used to take back a TD from the host controller. This would normally be + * called from within dl_done_list, however it may be called directly if the + * HC no longer sees the TD and it has not appeared on the donelist (after + * two frames). This bug has been observed on ZF Micro systems. + */ +static int takeback_td(ohci_t *ohci, td_t *td_list) +{ + ed_t *ed; + int cc; + int stat = 0; + /* urb_t *urb; */ + urb_priv_t *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + + tdINFO = m32_swap(td_list->hwINFO); + + ed = td_list->ed; + lurb_priv = ed->purb; + + dl_transfer_length(td_list); + + lurb_priv->td_cnt++; + + /* error code of transfer */ + cc = TD_CC_GET(tdINFO); + if (cc) { + err("USB-error: %s (%x)", cc_to_string[cc], cc); + stat = cc_to_error[cc]; + } + + /* see if this done list makes for all TD's of current URB, + * and mark the URB finished if so */ + if (lurb_priv->td_cnt == lurb_priv->length) + finish_urb(ohci, lurb_priv, ed->state); + + dbg("dl_done_list: processing TD %x, len %x\n", + lurb_priv->td_cnt, lurb_priv->length); + + if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) { + edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0; + edTailP = m32_swap(ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink(ohci, ed); + } + return stat; +} + +static int dl_done_list(ohci_t *ohci) +{ + int stat = 0; + td_t *td_list = dl_reverse_done_list(ohci); + + while (td_list) { + td_t *td_next = td_list->next_dl_td; + stat = takeback_td(ohci, td_list); + td_list = td_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +#include + +/* Hub class-specific descriptor is constructed dynamically */ + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) {info("WR:status %#8x", (x)); ohci_writel((x), \ + &gohci.regs->roothub.status); } +#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, \ + (x)); ohci_writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); } +#else +#define WR_RH_STAT(x) ohci_writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x) ohci_writel((x), \ + &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci, wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + __u32 temp, ndp, i; + int res; + + res = -1; + temp = roothub_a(controller); + ndp = (temp & RH_A_NDP); +#ifdef CONFIG_AT91C_PQFP_UHPBUG + ndp = (ndp == 2) ? 1:0; +#endif + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus(controller, i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && + ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + void *data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32)); + +#ifdef DEBUG +pkt_print(NULL, dev, pipe, buffer, transfer_len, + cmd, "SUB(rh)", usb_pipein(pipe)); +#else + mdelay(1); +#endif + if (usb_pipeint(pipe)) { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = le16_to_cpu(cmd->value); + wIndex = le16_to_cpu(cmd->index); + wLength = le16_to_cpu(cmd->length); + + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", + dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(u16 *)databuf = cpu_to_le16(1); + OK(2); + case RH_GET_STATUS | RH_INTERFACE: + *(u16 *)databuf = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_ENDPOINT: + *(u16 *)databuf = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_CLASS: + *(u32 *)databuf = cpu_to_le32( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK(4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(u32 *)databuf = cpu_to_le32(RD_RH_PORTSTAT); + OK(4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + OK(0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); + OK(0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_CCS); OK(0); + case (RH_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_POCI); OK(0); + case (RH_PORT_POWER): WR_RH_PORTSTAT(RH_PS_LSDA); OK(0); + case (RH_C_PORT_CONNECTION): WR_RH_PORTSTAT(RH_PS_CSC); OK(0); + case (RH_C_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_PESC); OK(0); + case (RH_C_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_PSSC); OK(0); + case (RH_C_PORT_OVER_CURRENT):WR_RH_PORTSTAT(RH_PS_OCIC); OK(0); + case (RH_C_PORT_RESET): WR_RH_PORTSTAT(RH_PS_PRSC); OK(0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT(RH_PS_PSS); OK(0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT(RH_PS_PRS); + OK(0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT(RH_PS_PPS); + mdelay(100); + OK(0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT(RH_PS_PES); + OK(0); + } + break; + + case RH_SET_ADDRESS: + gohci.rh.devnum = wValue; + OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_dev_des), + wLength)); + databuf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + databuf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + if (wValue == 0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + databuf = root_hub_str_index0; + OK(len); + } + if (wValue == 0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + databuf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a(&gohci); + + databuf[0] = 9; /* min length; */ + databuf[1] = 0x29; + databuf[2] = temp & RH_A_NDP; +#ifdef CONFIG_AT91C_PQFP_UHPBUG + databuf[2] = (databuf[2] == 2) ? 1 : 0; +#endif + databuf[3] = 0; + if (temp & RH_A_PSM) /* per-port power switching? */ + databuf[3] |= 0x1; + if (temp & RH_A_NOCP) /* no overcurrent reporting? */ + databuf[3] |= 0x10; + else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ + databuf[3] |= 0x8; + + databuf[4] = 0; + databuf[5] = (temp & RH_A_POTPGT) >> 24; + databuf[6] = 0; + temp = roothub_b(&gohci); + databuf[7] = temp & RH_B_DR; + if (databuf[2] < 7) { + databuf[8] = 0xff; + } else { + databuf[0] += 2; + databuf[8] = (temp & RH_B_DR) >> 8; + databuf[10] = databuf[9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, databuf[0], wLength)); + OK(len); + } + + case RH_GET_CONFIGURATION: + databuf[0] = 0x01; + OK(1); + + case RH_SET_CONFIGURATION: + WR_RH_STAT(0x10000); + OK(0); + + default: + dbg("unsupported root hub command"); + stat = USB_ST_STALLED; + } + +#ifdef DEBUG + ohci_dump_roothub(&gohci, 1); +#else + mdelay(1); +#endif + + len = min_t(int, len, leni); + if (data != databuf) + memcpy(data, databuf, len); + dev->act_len = len; + dev->status = stat; + +#ifdef DEBUG + pkt_print(NULL, dev, pipe, buffer, + transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#else + mdelay(1); +#endif + + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + urb_priv_t *urb; + + urb = malloc(sizeof(urb_priv_t)); + memset(urb, 0, sizeof(urb_priv_t)); + + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = buffer; + urb->transfer_buffer_length = transfer_len; + urb->interval = interval; + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + +#ifdef DEBUG + urb->actual_length = 0; + pkt_print(urb, dev, pipe, buffer, transfer_len, + setup, "SUB", usb_pipein(pipe)); +#else + mdelay(1); +#endif + if (!maxsize) { + err("submit_common_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + + if (sohci_submit_job(urb, setup) < 0) { + err("sohci_submit_job failed"); + return -1; + } + +#if 0 + mdelay(10); + /* ohci_dump_status(&gohci); */ +#endif + + timeout = USB_TIMEOUT_MS(pipe); + + /* wait for it to complete */ + for (;;) { + /* check whether the controller is done */ + stat = hc_interrupt(); + if (stat < 0) { + stat = USB_ST_CRC_ERR; + break; + } + + /* NOTE: since we are not interrupt driven in U-Boot and always + * handle only one URB at a time, we cannot assume the + * transaction finished on the first successful return from + * hc_interrupt().. unless the flag for current URB is set, + * meaning that all TD's to/from device got actually + * transferred and processed. If the current URB is not + * finished we need to re-iterate this loop so as + * hc_interrupt() gets called again as there needs to be some + * more TD's to process still */ + if ((stat >= 0) && (stat != 0xff) && (urb->finished)) { + /* 0xff is returned for an SF-interrupt */ + break; + } + + if (--timeout) { + mdelay(1); + if (!urb->finished) + dbg("*"); + + } else { + err("CTL:TIMEOUT "); + dbg("submit_common_msg: TO status %x\n", stat); + urb->finished = 1; + stat = USB_ST_CRC_ERR; + break; + } + } + + dev->status = stat; + dev->act_len = urb->actual_length; + +#ifdef DEBUG + pkt_print(urb, dev, pipe, buffer, transfer_len, + setup, "RET(ctlr)", usb_pipein(pipe)); +#else + mdelay(1); +#endif + + /* free TDs in urb_priv */ + if (!usb_pipeint(pipe)) + urb_free_priv(urb); + return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + info("submit_bulk_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup) +{ + int maxsize = usb_maxpacket(dev, pipe); + + info("submit_control_msg"); +#ifdef DEBUG + pkt_print(NULL, dev, pipe, buffer, transfer_len, + setup, "SUB", usb_pipein(pipe)); +#else + mdelay(1); +#endif + if (!maxsize) { + err("submit_control_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { + gohci.rh.dev = dev; + /* root hub - redirect */ + return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + } + + return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, + interval); +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset(ohci_t *ohci) +{ +#ifdef CONFIG_PCI_EHCI_DEVNO + pci_dev_t pdev; +#endif + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + dbg("%s\n", __FUNCTION__); + +#ifdef CONFIG_PCI_EHCI_DEVNO + /* + * Some multi-function controllers (e.g. ISP1562) allow root hub + * resetting via EHCI registers only. + */ + pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVNO); + if (pdev != -1) { + u32 base; + int timeout = 1000; + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); + base += EHCI_USBCMD_OFF; + ohci_writel(ohci_readl(base) | EHCI_USBCMD_HCRESET, base); + + while (ohci_readl(base) & EHCI_USBCMD_HCRESET) { + if (timeout-- <= 0) { + printf("USB RootHub reset timed out!"); + break; + } + udelay(1); + } + } else + printf("No EHCI func at %d index!\n", CONFIG_PCI_EHCI_DEVNO); +#endif + if (ohci_readl(&ohci->regs->control) & OHCI_CTRL_IR) { + /* SMM owns the HC, request ownership */ + ohci_writel(OHCI_OCR, &ohci->regs->cmdstatus); + info("USB HC TakeOver from SMM"); + while (ohci_readl(&ohci->regs->control) & OHCI_CTRL_IR) { + mdelay(10); + if (--smm_timeout == 0) { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + ohci_writel(OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n", + ohci->slot_name, + ohci_readl(&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + ohci->hc_control = 0; + ohci_writel(ohci->hc_control, &ohci->regs->control); + + /* HC Reset requires max 10 us delay */ + ohci_writel(OHCI_HCR, &ohci->regs->cmdstatus); + while ((ohci_readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err("USB HC reset timed out!"); + return -1; + } + udelay(1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start(ohci_t *ohci) +{ + __u32 mask; + unsigned int fminterval; + + ohci->disabled = 1; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + ohci_writel(0, &ohci->regs->ed_controlhead); + ohci_writel(0, &ohci->regs->ed_bulkhead); + + ohci_writel((__u32)ohci->hcca, + &ohci->regs->hcca); /* reset clears this */ + + fminterval = 0x2edf; + ohci_writel((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + ohci_writel(fminterval, &ohci->regs->fminterval); + ohci_writel(0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + ohci_writel(ohci->hc_control, &ohci->regs->control); + + /* disable all interrupts */ + mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | + OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | + OHCI_INTR_OC | OHCI_INTR_MIE); + ohci_writel(mask, &ohci->regs->intrdisable); + /* clear all interrupts */ + mask &= ~OHCI_INTR_MIE; + ohci_writel(mask, &ohci->regs->intrstatus); + /* Choose the interrupts we care about now - but w/o MIE */ + mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + ohci_writel(mask, &ohci->regs->intrenable); + +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + ohci_writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + ohci_writel(RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + + /* POTPGT delay is bits 24-31, in 2 ms units. */ + mdelay((roothub_a(ohci) >> 23) & 0x1fe); + + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int hc_interrupt(void) +{ + ohci_t *ohci = &gohci; + struct ohci_regs *regs = ohci->regs; + int ints; + int stat = -1; + + if ((ohci->hcca->done_head != 0) && + !(m32_swap(ohci->hcca->done_head) & 0x01)) { + ints = OHCI_INTR_WDH; + } else { + ints = ohci_readl(®s->intrstatus); + if (ints == ~(u32)0) { + ohci->disabled++; + err("%s device removed!", ohci->slot_name); + return -1; + } else { + ints &= ohci_readl(®s->intrenable); + if (ints == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; + } + } + } + + /* dbg("Interrupt: %x frame: %x", ints, + le16_to_cpu(ohci->hcca->frame_no)); */ + + if (ints & OHCI_INTR_RHSC) + stat = 0xff; + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err("OHCI Unrecoverable Error, controller usb-%s disabled", + ohci->slot_name); + /* e.g. due to PCI Master/Target Abort */ + +#ifdef DEBUG + ohci_dump(ohci, 1); +#else + mdelay(1); +#endif + /* FIXME: be optimistic, hope that bug won't repeat often. */ + /* Make some non-interrupt context restart the controller. */ + /* Count and limit the retries though; either hardware or */ + /* software errors can go forever... */ + hc_reset(ohci); + return -1; + } + + if (ints & OHCI_INTR_WDH) { + mdelay(1); + ohci_writel(OHCI_INTR_WDH, ®s->intrdisable); + (void)ohci_readl(®s->intrdisable); /* flush */ + stat = dl_done_list(&gohci); + ohci_writel(OHCI_INTR_WDH, ®s->intrenable); + (void)ohci_readl(®s->intrdisable); /* flush */ + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun\n"); + ohci_writel(OHCI_INTR_SO, ®s->intrenable); + stat = -1; + } + + /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ + if (ints & OHCI_INTR_SF) { + unsigned int frame = m16_swap(ohci->hcca->frame_no) & 1; + mdelay(1); + ohci_writel(OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[frame] != NULL) + ohci_writel(OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + + ohci_writel(ints, ®s->intrstatus); + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci(ohci_t *ohci) +{ + dbg("USB HC release ohci usb-%s", ohci->slot_name); + + if (!ohci->disabled) + hc_reset(ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ +#ifdef CONFIG_PCI_OHCI + pci_dev_t pdev; +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT + /* cpu dependant init */ + if (usb_cpu_init()) + return -1; +#endif + +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT + /* board dependant init */ + if (board_usb_init(index, USB_INIT_HOST)) + return -1; +#endif + memset(&gohci, 0, sizeof(ohci_t)); + + /* align the storage */ + if ((__u32)&ghcca[0] & 0xff) { + err("HCCA not aligned!!"); + return -1; + } + phcca = &ghcca[0]; + info("aligned ghcca %p", phcca); + memset(&ohci_dev, 0, sizeof(struct ohci_device)); + if ((__u32)&ohci_dev.ed[0] & 0x7) { + err("EDs not aligned!!"); + return -1; + } + memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); + if ((__u32)gtd & 0x7) { + err("TDs not aligned!!"); + return -1; + } + ptd = gtd; + gohci.hcca = phcca; + memset(phcca, 0, sizeof(struct ohci_hcca)); + + gohci.disabled = 1; + gohci.sleeping = 0; + gohci.irq = -1; +#ifdef CONFIG_PCI_OHCI + pdev = pci_find_devices(ohci_pci_ids, CONFIG_PCI_OHCI_DEVNO); + + if (pdev != -1) { + u16 vid, did; + u32 base; + pci_read_config_word(pdev, PCI_VENDOR_ID, &vid); + pci_read_config_word(pdev, PCI_DEVICE_ID, &did); + printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n", + vid, did, (pdev >> 16) & 0xff, + (pdev >> 11) & 0x1f, (pdev >> 8) & 0x7); + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); + printf("OHCI regs address 0x%08x\n", base); + gohci.regs = (struct ohci_regs *)base; + } else + return -1; +#else + gohci.regs = (struct ohci_regs *)CONFIG_SYS_USB_OHCI_REGS_BASE; +#endif + + gohci.flags = 0; + gohci.slot_name = CONFIG_SYS_USB_OHCI_SLOT_NAME; + + if (hc_reset (&gohci) < 0) { + hc_release_ohci (&gohci); + err ("can't reset usb-%s", gohci.slot_name); +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT + /* board dependant cleanup */ + board_usb_cleanup(index, USB_INIT_HOST); +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT + /* cpu dependant cleanup */ + usb_cpu_init_fail(); +#endif + return -1; + } + + if (hc_start(&gohci) < 0) { + err("can't start usb-%s", gohci.slot_name); + hc_release_ohci(&gohci); + /* Initialization failed */ +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT + /* board dependant cleanup */ + usb_board_stop(); +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT + /* cpu dependant cleanup */ + usb_cpu_stop(); +#endif + return -1; + } + +#ifdef DEBUG + ohci_dump(&gohci, 1); +#else + mdelay(1); +#endif + ohci_inited = 1; + return 0; +} + +int usb_lowlevel_stop(int index) +{ + /* this gets called really early - before the controller has */ + /* even been initialized! */ + if (!ohci_inited) + return 0; + /* TODO release any interrupts, etc. */ + /* call hc_release_ohci() here ? */ + hc_reset(&gohci); + +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT + /* board dependant cleanup */ + if (usb_board_stop()) + return -1; +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT + /* cpu dependant cleanup */ + if (usb_cpu_stop()) + return -1; +#endif + /* This driver is no longer initialised. It needs a new low-level + * init (board/cpu) before it can be used again. */ + ohci_inited = 0; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.c b/qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.c new file mode 100644 index 000000000..3c659c60c --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.c @@ -0,0 +1,1691 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the S3C2400. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* + * IMPORTANT NOTES + * 1 - this driver is intended for use with USB Mass Storage Devices + * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include +/* #include no PCI on the S3C24X0 */ + +#if defined(CONFIG_USB_OHCI) && defined(CONFIG_S3C24X0) + +#include +#include +#include +#include +#include "ohci-s3c24xx.h" + +#define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#undef OHCI_VERBOSE_DEBUG /* not always helpful */ + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define min_t(type, x, y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) + +#undef DEBUG +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#undef SHOW_INFO +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) swap_16(x) +#define m32_swap(x) swap_32(x) + +/* global struct ohci */ +static struct ohci gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +struct urb_priv urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; +/* flag guarding URB transation */ +int urb_finished = 0; + +/*-------------------------------------------------------------------------*/ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define OHCI_QUIRK_AMD756 0xabcd +#define read_roothub(hc, register, mask) ({ \ + u32 temp = readl (&hc->regs->roothub.register); \ + if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = readl (&hc->regs->roothub.register); \ + temp; }) + +static u32 roothub_a(struct ohci *hc) +{ + return read_roothub(hc, a, 0xfc0fe000); +} +static inline u32 roothub_b(struct ohci *hc) +{ + return readl(&hc->regs->roothub.b); +} +static inline u32 roothub_status(struct ohci *hc) +{ + return readl(&hc->regs->roothub.status); +} +static u32 roothub_portstatus(struct ohci *hc, int i) +{ + return read_roothub(hc, portstatus[i], 0xffe0fce0); +} + +/* forward declaration */ +static int hc_interrupt(void); +static void td_submit_job(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *setup, struct urb_priv *urb, + int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv(struct urb_priv *urb) +{ + int i; + int last; + struct td *td; + + last = urb->length - 1; + if (last >= 0) { + for (i = 0; i <= last; i++) { + td = urb->td[i]; + if (td) { + td->usb_dev = NULL; + urb->td[i] = NULL; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number(struct usb_device *dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, char *str, + int small) +{ + struct urb_priv *purb = &urb_priv; + + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number(dev), + usb_pipedevice(pipe), + usb_pipeendpoint(pipe), + usb_pipeout(pipe) ? 'O' : 'I', + usb_pipetype(pipe) < 2 ? + (usb_pipeint(pipe) ? "INTR" : "ISOC") : + (usb_pipecontrol(pipe) ? "CTRL" : "BULK"), + purb->actual_length, transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol(pipe)) { + printf(__FILE__ ": cmd(8):"); + for (i = 0; i < 8; i++) + printf(" %02x", ((__u8 *) setup)[i]); + printf("\n"); + } + if (transfer_len > 0 && buffer) { + printf(__FILE__ ": data(%d/%d):", + purb->actual_length, transfer_len); + len = usb_pipeout(pipe) ? + transfer_len : purb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf(" %02x", ((__u8 *) buffer)[i]); + printf("%s\n", i < len ? "..." : ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the + int ed tree inclusive iso eds*/ +void ep_print_int_eds(struct ohci *ohci, char *str) +{ + int i, j; + __u32 *ed_p; + for (i = 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table[i]); + if (*ed_p == 0) + continue; + printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + struct ed *ed = (struct ed *) m32_swap(ed_p); + printf(" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printf("\n"); + } +} + +static void ohci_dump_intr_mask(char *label, __u32 mask) +{ + dbg("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : ""); +} + +static void maybe_print_eds(char *label, __u32 value) +{ + struct ed *edp = (struct ed *) value; + + if (value) { + dbg("%s %08x", label, value); + dbg("%08x", edp->hwINFO); + dbg("%08x", edp->hwTailP); + dbg("%08x", edp->hwHeadP); + dbg("%08x", edp->hwNextED); + } +} + +static char *hcfs2string(int state) +{ + switch (state) { + case OHCI_USB_RESET: + return "reset"; + case OHCI_USB_RESUME: + return "resume"; + case OHCI_USB_OPER: + return "operational"; + case OHCI_USB_SUSPEND: + return "suspend"; + } + return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status(struct ohci *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = readl(®s->revision) & 0xff; + if (temp != 0x10) + dbg("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl(®s->control); + dbg("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string(temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", temp & OHCI_CTRL_CBSR); + + temp = readl(®s->cmdstatus); + dbg("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", (temp & OHCI_HCR) ? " HCR" : ""); + + ohci_dump_intr_mask("intrstatus", readl(®s->intrstatus)); + ohci_dump_intr_mask("intrenable", readl(®s->intrenable)); + + maybe_print_eds("ed_periodcurrent", readl(®s->ed_periodcurrent)); + + maybe_print_eds("ed_controlhead", readl(®s->ed_controlhead)); + maybe_print_eds("ed_controlcurrent", readl(®s->ed_controlcurrent)); + + maybe_print_eds("ed_bulkhead", readl(®s->ed_bulkhead)); + maybe_print_eds("ed_bulkcurrent", readl(®s->ed_bulkcurrent)); + + maybe_print_eds("donehead", readl(®s->donehead)); +} + +static void ohci_dump_roothub(struct ohci *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a(controller); + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", ndp); + temp = roothub_b(controller); + dbg("roothub.b: %08x PPCM=%04x DR=%04x", + temp, (temp & RH_B_PPCM) >> 16, (temp & RH_B_DR) + ); + temp = roothub_status(controller); + dbg("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : ""); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus(controller, i); + dbg("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : ""); + } +} + +static void ohci_dump(struct ohci *controller, int verbose) +{ + dbg("OHCI controller usb-%s state", controller->slot_name); + + /* dumps some of the state we know about */ + ohci_dump_status(controller); + if (verbose) + ep_print_int_eds(controller, "hcca"); + dbg("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub(controller, 1); +} + +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + struct ohci *ohci; + struct ed *ed; + struct urb_priv *purb_priv; + int i, size = 0; + + ohci = &gohci; + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + err("sohci_submit_job: EPIPE"); + return -1; + } + + /* if we have an unfinished URB from previous transaction let's + * fail and scream as quickly as possible so as not to corrupt + * further communication */ + if (!urb_finished) { + err("sohci_submit_job: URB NOT FINISHED"); + return -1; + } + /* we're about to begin a new transaction here + so mark the URB unfinished */ + urb_finished = 0; + + /* every endpoint has a ed, locate and fill it */ + ed = ep_add_ed(dev, pipe); + if (!ed) { + err("sohci_submit_job: ENOMEM"); + return -1; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype(pipe)) { + case PIPE_BULK: + /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL: + /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0) ? 2 : (transfer_len - 1) / 4096 + 3; + break; + } + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv = &urb_priv; + purb_priv->pipe = pipe; + + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for (i = 0; i < size; i++) { + purb_priv->td[i] = td_alloc(dev); + if (!purb_priv->td[i]) { + purb_priv->length = i; + urb_free_priv(purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv(purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link(ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, + interval); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number(struct usb_device *usb_dev) +{ + struct ohci *ohci = &gohci; + + return m16_swap(ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link(struct ohci *ohci, struct ed *edi) +{ + struct ed *ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel((u32)ed, &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = (__u32) m32_swap(ed); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_CLE; + writel(ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + writel((u32)ed, &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = (__u32) m32_swap(ed); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_BLE; + writel(ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink(struct ohci *ohci, struct ed *ed) +{ + struct ed *next; + ed->hwINFO |= m32_swap(OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel(ohci->hc_control, &ohci->regs->control); + } + writel(m32_swap(*((__u32 *) &ed->hwNextED)), + &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + next = (struct ed *)m32_swap(*((__u32 *)&ed->hwNextED)); + next->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel(ohci->hc_control, &ohci->regs->control); + } + writel(m32_swap(*((__u32 *) &ed->hwNextED)), + &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + next = (struct ed *)m32_swap(*((__u32 *)&ed->hwNextED)); + next->ed_prev = ed->ed_prev; + } + break; + } + ed->state = ED_UNLINK; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration + * command, but the USB stack is a little bit stateless so we do it at every + * transaction. If the state of the ed is ED_NEW then a dummy td is added and + * the state is changed to ED_UNLINK. In all other cases the state is left + * unchanged. The ed info fields are setted anyway even though most of them + * should not change */ + +static struct ed *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe) +{ + struct td *td; + struct ed *ed_ret; + struct ed *ed; + + ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) | + (usb_pipecontrol(pipe) ? 0 : + usb_pipeout(pipe))]; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + + if (ed->state == ED_NEW) { + ed->hwINFO = m32_swap(OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc(usb_dev); + ed->hwTailP = (__u32) m32_swap(td); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype(pipe); + ohci_dev.ed_cnt++; + } + + ed->hwINFO = m32_swap(usb_pipedevice(pipe) + | usb_pipeendpoint(pipe) << 7 + | (usb_pipeisoc(pipe) ? 0x8000 : 0) + | (usb_pipecontrol(pipe) ? 0 : + (usb_pipeout(pipe) ? 0x800 : 0x1000)) + | (usb_dev->speed == USB_SPEED_LOW) << 13 | + usb_maxpacket(usb_dev, pipe) << 16); + + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill(struct ohci *ohci, unsigned int info, void *data, int len, + struct usb_device *dev, int index, + struct urb_priv *urb_priv) +{ + struct td *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + + if (index > urb_priv->length) { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td[index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td[index] = + (struct td *) (m32_swap(urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32) data; +#ifdef OHCI_FILL_TRACE + if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x ", i, + ((unsigned char *)td->data)[i]); + printf("\n"); + } +#endif + if (!len) + data = 0; + + td->hwINFO = (__u32) m32_swap(info); + td->hwCBP = (__u32) m32_swap(data); + if (data) + td->hwBE = (__u32) m32_swap(data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = (__u32) m32_swap(td_pt); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_job(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *setup, struct urb_priv *urb, + int interval) +{ + struct ohci *ohci = &gohci; + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just + use the USB-toggle bits for reseting */ + if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), + 1); + } + urb->td_cnt = 0; + if (data_len) + data = buffer; + else + data = 0; + + switch (usb_pipetype(pipe)) { + case PIPE_BULK: + info = usb_pipeout(pipe) ? TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN; + while (data_len > 4096) { + td_fill(ohci, info | (cnt ? TD_T_TOGGLE : toggle), data, + 4096, dev, cnt, urb); + data += 4096; + data_len -= 4096; + cnt++; + } + info = usb_pipeout(pipe) ? + TD_CC | TD_DP_OUT : + TD_CC | TD_R | TD_DP_IN; + td_fill(ohci, info | (cnt ? TD_T_TOGGLE : toggle), data, + data_len, dev, cnt, urb); + cnt++; + + if (!ohci->sleeping) + /* start bulk list */ + writel(OHCI_BLF, &ohci->regs->cmdstatus); + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill(ohci, info, setup, 8, dev, cnt++, urb); + if (data_len > 0) { + info = usb_pipeout(pipe) ? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : + TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill(ohci, info, data, data_len, dev, cnt++, urb); + } + info = usb_pipeout(pipe) ? + TD_CC | TD_DP_IN | TD_T_DATA1 : + TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill(ohci, info, data, 0, dev, cnt++, urb); + if (!ohci->sleeping) + /* start Control list */ + writel(OHCI_CLF, &ohci->regs->cmdstatus); + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(struct td *td) +{ + __u32 tdBE, tdCBP; + struct urb_priv *lurb_priv = &urb_priv; + + tdBE = m32_swap(td->hwBE); + tdCBP = m32_swap(td->hwCBP); + + if (!(usb_pipecontrol(lurb_priv->pipe) && + ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + lurb_priv->actual_length += tdBE - td->data + 1; + else + lurb_priv->actual_length += tdCBP - td->data; + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static struct td *dl_reverse_done_list(struct ohci *ohci) +{ + __u32 td_list_hc; + __u32 tmp; + struct td *td_rev = NULL; + struct td *td_list = NULL; + struct urb_priv *lurb_priv = NULL; + + td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = (struct td *) td_list_hc; + + if (TD_CC_GET(m32_swap(td_list->hwINFO))) { + lurb_priv = &urb_priv; + dbg(" USB-error/status: %x : %p", + TD_CC_GET(m32_swap(td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & m32_swap(0x1)) { + if (lurb_priv && + ((td_list->index+1) < lurb_priv->length)) { + tmp = lurb_priv->length - 1; + td_list->ed->hwHeadP = + (lurb_priv->td[tmp]->hwNextTD & + m32_swap(0xfffffff0)) | + (td_list->ed->hwHeadP & + m32_swap(0x2)); + lurb_priv->td_cnt += lurb_priv->length - + td_list->index - 1; + } else + td_list->ed->hwHeadP &= + m32_swap(0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = m32_swap(td_list->hwNextTD) & 0xfffffff0; + } + + return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* td done list */ +static int dl_done_list(struct ohci *ohci, struct td *td_list) +{ + struct td *td_list_next = NULL; + struct ed *ed; + int cc = 0; + int stat = 0; + /* urb_t *urb; */ + struct urb_priv *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + lurb_priv = &urb_priv; + tdINFO = m32_swap(td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); + + /* error code of transfer */ + cc = TD_CC_GET(tdINFO); + if (cc != 0) { + dbg("ConditionCode %#x", cc); + stat = cc_to_error[cc]; + } + + /* see if this done list makes for all TD's of current URB, + * and mark the URB finished if so */ + if (++(lurb_priv->td_cnt) == lurb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK))) + urb_finished = 1; + else + dbg("dl_done_list: strange.., ED state %x, " + "ed->state\n"); + } else + dbg("dl_done_list: processing TD %x, len %x\n", + lurb_priv->td_cnt, lurb_priv->length); + + if (ed->state != ED_NEW) { + edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0; + edTailP = m32_swap(ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink(ohci, ed); + } + + td_list = td_list_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +#include + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) \ +{ \ + info("WR:status %#8x", (x)); \ + writel((x), &gohci.regs->roothub.status); \ +} +#define WR_RH_PORTSTAT(x) \ +{ \ + info("WR:portstatus[%d] %#8x", wIndex-1, (x)); \ + writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); \ +} +#else +#define WR_RH_STAT(x) \ + writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x)\ + writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci, wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(struct ohci *controller) +{ + __u32 temp, ndp, i; + int res; + + res = -1; + temp = roothub_a(controller); + ndp = (temp & RH_A_NDP); + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus(controller, i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *cmd) +{ + void *data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + union { + __u32 word[4]; + __u16 hword[8]; + __u8 byte[16]; + } datab; + __u8 *data_buf = datab.byte; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", + usb_pipein(pipe)); +#else + mdelay(1); +#endif + if (usb_pipeint(pipe)) { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = m16_swap(cmd->value); + wIndex = m16_swap(cmd->index); + wLength = m16_swap(cmd->length); + + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", + dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + datab.hword[0] = m16_swap(1); + OK(2); + case RH_GET_STATUS | RH_INTERFACE: + datab.hword[0] = m16_swap(0); + OK(2); + case RH_GET_STATUS | RH_ENDPOINT: + datab.hword[0] = m16_swap(0); + OK(2); + case RH_GET_STATUS | RH_CLASS: + datab.word[0] = + m32_swap(RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK(4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + datab.word[0] = m32_swap(RD_RH_PORTSTAT); + OK(4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + OK(0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); + OK(0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + WR_RH_PORTSTAT(RH_PS_CCS); + OK(0); + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT(RH_PS_POCI); + OK(0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT(RH_PS_LSDA); + OK(0); + case (RH_C_PORT_CONNECTION): + WR_RH_PORTSTAT(RH_PS_CSC); + OK(0); + case (RH_C_PORT_ENABLE): + WR_RH_PORTSTAT(RH_PS_PESC); + OK(0); + case (RH_C_PORT_SUSPEND): + WR_RH_PORTSTAT(RH_PS_PSSC); + OK(0); + case (RH_C_PORT_OVER_CURRENT): + WR_RH_PORTSTAT(RH_PS_OCIC); + OK(0); + case (RH_C_PORT_RESET): + WR_RH_PORTSTAT(RH_PS_PRSC); + OK(0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT(RH_PS_PSS); + OK(0); + case (RH_PORT_RESET): /* BUG IN HUP CODE ******** */ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT(RH_PS_PRS); + OK(0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT(RH_PS_PPS); + OK(0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE ******** */ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT(RH_PS_PES); + OK(0); + } + break; + + case RH_SET_ADDRESS: + gohci.rh.devnum = wValue; + OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_dev_des), wLength)); + data_buf = root_hub_dev_des; + OK(len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; + OK(len); + case (0x03): /* string descriptors */ + if (wValue == 0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + OK(len); + } + if (wValue == 0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a(&gohci); + + data_buf[0] = 9; /* min length; */ + data_buf[1] = 0x29; + data_buf[2] = temp & RH_A_NDP; + data_buf[3] = 0; + if (temp & RH_A_PSM) + /* per-port power switching? */ + data_buf[3] |= 0x1; + if (temp & RH_A_NOCP) + /* no overcurrent reporting? */ + data_buf[3] |= 0x10; + else if (temp & RH_A_OCPM) + /* per-port overcurrent reporting? */ + data_buf[3] |= 0x8; + + /* corresponds to data_buf[4-7] */ + datab.word[1] = 0; + data_buf[5] = (temp & RH_A_POTPGT) >> 24; + temp = roothub_b(&gohci); + data_buf[7] = temp & RH_B_DR; + if (data_buf[2] < 7) { + data_buf[8] = 0xff; + } else { + data_buf[0] += 2; + data_buf[8] = (temp & RH_B_DR) >> 8; + data_buf[10] = data_buf[9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf[0], wLength)); + OK(len); + } + + case RH_GET_CONFIGURATION: + *(__u8 *) data_buf = 0x01; + OK(1); + + case RH_SET_CONFIGURATION: + WR_RH_STAT(0x10000); + OK(0); + + default: + dbg("unsupported root hub command"); + stat = USB_ST_STALLED; + } + +#ifdef DEBUG + ohci_dump_roothub(&gohci, 1); +#else + mdelay(1); +#endif + + len = min_t(int, len, leni); + if (data != data_buf) + memcpy(data, data_buf, len); + dev->act_len = len; + dev->status = stat; + +#ifdef DEBUG + if (transfer_len) + urb_priv.actual_length = transfer_len; + pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", + 0 /*usb_pipein(pipe) */); +#else + mdelay(1); +#endif + + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + dev->status = USB_ST_CRC_ERR; + return 0; + } +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", + usb_pipein(pipe)); +#else + mdelay(1); +#endif + if (!maxsize) { + err("submit_common_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + + if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < + 0) { + err("sohci_submit_job failed"); + return -1; + } + + mdelay(10); + /* ohci_dump_status(&gohci); */ + + /* allow more time for a BULK device to react - some are slow */ +#define BULK_TO 5000 /* timeout in milliseconds */ + if (usb_pipebulk(pipe)) + timeout = BULK_TO; + else + timeout = 100; + + /* wait for it to complete */ + for (;;) { + /* check whether the controller is done */ + stat = hc_interrupt(); + + if (stat < 0) { + stat = USB_ST_CRC_ERR; + break; + } + + /* NOTE: since we are not interrupt driven in U-Boot and always + * handle only one URB at a time, we cannot assume the + * transaction finished on the first successful return from + * hc_interrupt().. unless the flag for current URB is set, + * meaning that all TD's to/from device got actually + * transferred and processed. If the current URB is not + * finished we need to re-iterate this loop so as + * hc_interrupt() gets called again as there needs to be some + * more TD's to process still */ + if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { + /* 0xff is returned for an SF-interrupt */ + break; + } + + if (--timeout) { + mdelay(1); + if (!urb_finished) + dbg("\%"); + + } else { + err("CTL:TIMEOUT "); + dbg("submit_common_msg: TO status %x\n", stat); + stat = USB_ST_CRC_ERR; + urb_finished = 1; + break; + } + } + +#if 0 + /* we got an Root Hub Status Change interrupt */ + if (got_rhsc) { +#ifdef DEBUG + ohci_dump_roothub(&gohci, 1); +#endif + got_rhsc = 0; + /* abuse timeout */ + timeout = rh_check_port_status(&gohci); + if (timeout >= 0) { +#if 0 /* this does nothing useful, but leave it here + in case that changes */ + /* the called routine adds 1 to the passed value */ + usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); +#endif + /* + * XXX + * This is potentially dangerous because it assumes + * that only one device is ever plugged in! + */ + devgone = dev; + } + } +#endif + + dev->status = stat; + dev->act_len = transfer_len; + +#ifdef DEBUG + pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", + usb_pipein(pipe)); +#else + mdelay(1); +#endif + + /* free TDs in urb_priv */ + urb_free_priv(&urb_priv); + return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + info("submit_bulk_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup) +{ + int maxsize = usb_maxpacket(dev, pipe); + + info("submit_control_msg"); +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", + usb_pipein(pipe)); +#else + mdelay(1); +#endif + if (!maxsize) { + err("submit_control_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { + gohci.rh.dev = dev; + /* root hub - redirect */ + return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + } + + return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return -1; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset(struct ohci *ohci) +{ + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + if (readl(&ohci->regs->control) & OHCI_CTRL_IR) { + /* SMM owns the HC - request ownership */ + writel(OHCI_OCR, &ohci->regs->cmdstatus); + info("USB HC TakeOver from SMM"); + while (readl(&ohci->regs->control) & OHCI_CTRL_IR) { + mdelay(10); + if (--smm_timeout == 0) { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + writel(OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;", + ohci->slot_name, readl(&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + writel(0, &ohci->regs->control); + + /* HC Reset requires max 10 us delay */ + writel(OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err("USB HC reset timed out!"); + return -1; + } + udelay(1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start(struct ohci *ohci) +{ + __u32 mask; + unsigned int fminterval; + + ohci->disabled = 1; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel(0, &ohci->regs->ed_controlhead); + writel(0, &ohci->regs->ed_bulkhead); + + /* a reset clears this */ + writel((__u32) ohci->hcca, &ohci->regs->hcca); + + fminterval = 0x2edf; + writel((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel(fminterval, &ohci->regs->fminterval); + writel(0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + writel(ohci->hc_control, &ohci->regs->control); + + /* disable all interrupts */ + mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | + OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | + OHCI_INTR_OC | OHCI_INTR_MIE); + writel(mask, &ohci->regs->intrdisable); + /* clear all interrupts */ + mask &= ~OHCI_INTR_MIE; + writel(mask, &ohci->regs->intrstatus); + /* Choose the interrupts we care about now - but w/o MIE */ + mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel(mask, &ohci->regs->intrenable); + +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + writel(RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + + /* POTPGT delay is bits 24-31, in 2 ms units. */ + mdelay((roothub_a(ohci) >> 23) & 0x1fe); + + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int hc_interrupt(void) +{ + struct ohci *ohci = &gohci; + struct ohci_regs *regs = ohci->regs; + int ints; + int stat = -1; + + if ((ohci->hcca->done_head != 0) && + !(m32_swap(ohci->hcca->done_head) & 0x01)) { + + ints = OHCI_INTR_WDH; + + } else { + ints = readl(®s->intrstatus); + if (ints == ~(u32) 0) { + ohci->disabled++; + err("%s device removed!", ohci->slot_name); + return -1; + } + ints &= readl(®s->intrenable); + if (ints == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; + } + } + + /* dbg("Interrupt: %x frame: %x", ints, + le16_to_cpu(ohci->hcca->frame_no)); */ + + if (ints & OHCI_INTR_RHSC) { + got_rhsc = 1; + stat = 0xff; + } + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err("OHCI Unrecoverable Error, controller usb-%s disabled", + ohci->slot_name); + /* e.g. due to PCI Master/Target Abort */ + +#ifdef DEBUG + ohci_dump(ohci, 1); +#else + mdelay(1); +#endif + /* FIXME: be optimistic, hope that bug won't repeat often. */ + /* Make some non-interrupt context restart the controller. */ + /* Count and limit the retries though; either hardware or */ + /* software errors can go forever... */ + hc_reset(ohci); + return -1; + } + + if (ints & OHCI_INTR_WDH) { + mdelay(1); + + writel(OHCI_INTR_WDH, ®s->intrdisable); + stat = dl_done_list(&gohci, dl_reverse_done_list(&gohci)); + writel(OHCI_INTR_WDH, ®s->intrenable); + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun\n"); + writel(OHCI_INTR_SO, ®s->intrenable); + stat = -1; + } + + /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ + if (ints & OHCI_INTR_SF) { + unsigned int frame = m16_swap(ohci->hcca->frame_no) & 1; + mdelay(1); + writel(OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[frame] != NULL) + writel(OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + + writel(ints, ®s->intrstatus); + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci(struct ohci *ohci) +{ + dbg("USB HC release ohci usb-%s", ohci->slot_name); + + if (!ohci->disabled) + hc_reset(ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + + /* + * Set the 48 MHz UPLL clocking. Values are taken from + * "PLL value selection guide", 6-23, s3c2400_UM.pdf. + */ + clk_power->upllcon = ((40 << 12) + (1 << 4) + 2); + gpio->misccr |= 0x8; /* 1 = use pads related USB for USB host */ + + /* + * Enable USB host clock. + */ + clk_power->clkcon |= (1 << 4); + + memset(&gohci, 0, sizeof(struct ohci)); + memset(&urb_priv, 0, sizeof(struct urb_priv)); + + /* align the storage */ + if ((__u32) &ghcca[0] & 0xff) { + err("HCCA not aligned!!"); + return -1; + } + phcca = &ghcca[0]; + info("aligned ghcca %p", phcca); + memset(&ohci_dev, 0, sizeof(struct ohci_device)); + if ((__u32) &ohci_dev.ed[0] & 0x7) { + err("EDs not aligned!!"); + return -1; + } + memset(gtd, 0, sizeof(struct td) * (NUM_TD + 1)); + if ((__u32) gtd & 0x7) { + err("TDs not aligned!!"); + return -1; + } + ptd = gtd; + gohci.hcca = phcca; + memset(phcca, 0, sizeof(struct ohci_hcca)); + + gohci.disabled = 1; + gohci.sleeping = 0; + gohci.irq = -1; + gohci.regs = (struct ohci_regs *)S3C24X0_USB_HOST_BASE; + + gohci.flags = 0; + gohci.slot_name = "s3c2400"; + + if (hc_reset(&gohci) < 0) { + hc_release_ohci(&gohci); + /* Initialization failed */ + clk_power->clkcon &= ~(1 << 4); + return -1; + } + + /* FIXME this is a second HC reset; why?? */ + gohci.hc_control = OHCI_USB_RESET; + writel(gohci.hc_control, &gohci.regs->control); + mdelay(10); + + if (hc_start(&gohci) < 0) { + err("can't start usb-%s", gohci.slot_name); + hc_release_ohci(&gohci); + /* Initialization failed */ + clk_power->clkcon &= ~(1 << 4); + return -1; + } +#ifdef DEBUG + ohci_dump(&gohci, 1); +#else + mdelay(1); +#endif + ohci_inited = 1; + urb_finished = 1; + + return 0; +} + +int usb_lowlevel_stop(int index) +{ + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); + + /* this gets called really early - before the controller has */ + /* even been initialized! */ + if (!ohci_inited) + return 0; + /* TODO release any interrupts, etc. */ + /* call hc_release_ohci() here ? */ + hc_reset(&gohci); + /* may not want to do this */ + clk_power->clkcon &= ~(1 << 4); + return 0; +} + +#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_S3C24X0) */ + +#if defined(CONFIG_USB_OHCI_NEW) && \ + defined(CONFIG_SYS_USB_OHCI_CPU_INIT) && \ + defined(CONFIG_S3C24X0) + +int usb_cpu_init(void) +{ + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + + /* + * Set the 48 MHz UPLL clocking. Values are taken from + * "PLL value selection guide", 6-23, s3c2400_UM.pdf. + */ + writel((40 << 12) + (1 << 4) + 2, &clk_power->upllcon); + /* 1 = use pads related USB for USB host */ + writel(readl(&gpio->misccr) | 0x8, &gpio->misccr); + + /* + * Enable USB host clock. + */ + writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon); + + return 0; +} + +int usb_cpu_stop(void) +{ + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); + /* may not want to do this */ + writel(readl(&clk_power->clkcon) & ~(1 << 4), &clk_power->clkcon); + return 0; +} + +int usb_cpu_init_fail(void) +{ + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); + writel(readl(&clk_power->clkcon) & ~(1 << 4), &clk_power->clkcon); + return 0; +} + +#endif /* defined(CONFIG_USB_OHCI_NEW) && \ + defined(CONFIG_SYS_USB_OHCI_CPU_INIT) && \ + defined(CONFIG_S3C24X0) */ diff --git a/qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.h b/qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.h new file mode 100644 index 000000000..f272d7885 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ohci-s3c24xx.h @@ -0,0 +1,409 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * usb-ohci.h + */ + + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +/* ED States */ +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed *ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed *ed_rm_list; + + struct usb_device *usb_dev; + __u32 unused[3]; +} __attribute__ ((aligned(16))); + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) (((td_p) >> 28) & 0x0f) +#define TD_CC_SET(td_p, cc) \ + {(td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)} +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute__ ((aligned(32))); + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute__ ((aligned(256))); + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute__ ((aligned(32))); + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status */ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +struct urb_priv { + struct ed *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + int state; + unsigned long pipe; + int actual_length; + struct td *td[N_URB_TD]; /* list pointer to all corresponding TDs + associated with this request */ +}; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +struct ohci { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma; */ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + struct ed *ed_rm_list[2]; /* lists of all endpoints to be removed */ + struct ed *ed_bulktail; /* last endpoint of bulk list */ + struct ed *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +}; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + struct ed ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(struct ohci *ohci, struct ed *ed); +static int ep_unlink(struct ohci *ohci, struct ed *ed); +static struct ed *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +struct td gtd[NUM_TD + 1]; + +/* pointers to aligned storage */ +struct td *ptd; + +/* TDs ... */ +static inline struct td *td_alloc(struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) { + if (ptd[i].usb_dev == NULL) { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void ed_free(struct ed *ed) +{ + ed->usb_dev = NULL; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/ohci.h b/qemu/roms/u-boot/drivers/usb/host/ohci.h new file mode 100644 index 000000000..9a4a2c247 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/ohci.h @@ -0,0 +1,491 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * usb-ohci.h + */ + +/* + * e.g. PCI controllers need this + */ +#ifdef CONFIG_SYS_OHCI_SWAP_REG_ACCESS +# define ohci_readl(a) __swap_32(*((volatile u32 *)(a))) +# define ohci_writel(a, b) (*((volatile u32 *)(b)) = __swap_32((volatile u32)a)) +#else +# define ohci_readl(a) (*((volatile u32 *)(a))) +# define ohci_writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) +#endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */ + +/* functions for doing board or CPU specific setup/cleanup */ +int usb_board_stop(void); + +int usb_cpu_init(void); +int usb_cpu_stop(void); +int usb_cpu_init_fail(void); + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +static const char *cc_to_string[16] = { + "No Error", + "CRC: Last data packet from endpoint contained a CRC error.", + "BITSTUFFING: Last data packet from endpoint contained a bit " \ + "stuffing violation", + "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \ + "that did not match the expected value.", + "STALL: TD was moved to the Done Queue because the endpoint returned" \ + " a STALL PID", + "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \ + "not provide a handshake (OUT)", + "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\ + "(IN) or handshake (OUT)", + "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \ + "value is not defined.", + "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \ + "either the size of the maximum data packet allowed\n" \ + "from the endpoint (found in MaximumPacketSize field\n" \ + "of ED) or the remaining buffer size.", + "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \ + "and that amount was not sufficient to fill the\n" \ + "specified buffer", + "reserved1", + "reserved2", + "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \ + "than it could be written to system memory", + "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \ + "system memory fast enough to keep up with data USB " \ + "data rate.", + "NOT ACCESSED: This code is set by software before the TD is placed" \ + "on a list to be processed by the HC.(1)", + "NOT ACCESSED: This code is set by software before the TD is placed" \ + "on a list to be processed by the HC.(2)", +}; + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed *ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed *ed_rm_list; + + struct usb_device *usb_dev; + void *purb; + __u32 unused[2]; +} __attribute__((aligned(16))); +typedef struct ed ed_t; + + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + +/* #ifndef CONFIG_MPC5200 /\* this seems wrong *\/ */ + __u16 hwPSW[MAXPSW]; +/* #endif */ + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute__((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ +#if defined(CONFIG_MPC5200) + __u16 pad1; /* set to 0 on each frame_no change */ + __u16 frame_no; /* current frame number */ +#else + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ +#endif + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute__((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#ifndef CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS +# error "CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS undefined!" +#endif + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the ohci_readl() and + * ohci_writel() macros defined in this file to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS]; + } roothub; +} __attribute__((aligned(32))); + +/* Some EHCI controls */ +#define EHCI_USBCMD_OFF 0x20 +#define EHCI_USBCMD_HCRESET (1 << 1) + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct +{ + ed_t *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + struct usb_device *dev; + int state; + unsigned long pipe; + void *transfer_buffer; + int transfer_buffer_length; + int interval; + int actual_length; + int finished; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma;*/ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ + ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t *ed_bulktail; /* last endpoint of bulk list */ + ed_t *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +} ohci_t; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe, + int interval, int load); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD+1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td * +td_alloc (struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) + { + if (ptd[i].usb_dev == NULL) + { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void +ed_free (struct ed *ed) +{ + ed->usb_dev = NULL; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/r8a66597-hcd.c b/qemu/roms/u-boot/drivers/usb/host/r8a66597-hcd.c new file mode 100644 index 000000000..dfe5423b8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/r8a66597-hcd.c @@ -0,0 +1,832 @@ +/* + * R8A66597 HCD (Host Controller Driver) for u-boot + * + * Copyright (C) 2008 Yoshihiro Shimoda + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include + +#include "r8a66597.h" + +#ifdef R8A66597_DEBUG +#define R8A66597_DPRINT printf +#else +#define R8A66597_DPRINT(...) +#endif + +static const char hcd_name[] = "r8a66597_hcd"; +static struct r8a66597 gr8a66597; + +static void get_hub_data(struct usb_device *dev, u16 *hub_devnum, u16 *hubport) +{ + int i; + + *hub_devnum = 0; + *hubport = 0; + + /* check a device connected to root_hub */ + if ((dev->parent && dev->parent->devnum == 1) || + (dev->devnum == 1)) + return; + + for (i = 0; i < USB_MAXCHILDREN; i++) { + if (dev->parent->children[i] == dev) { + *hub_devnum = (u8)dev->parent->devnum; + *hubport = i; + return; + } + } + + printf("get_hub_data error.\n"); +} + +static void set_devadd(struct r8a66597 *r8a66597, u8 r8a66597_address, + struct usb_device *dev, int port) +{ + u16 val, usbspd, upphub, hubport; + unsigned long devadd_reg = get_devadd_addr(r8a66597_address); + + get_hub_data(dev, &upphub, &hubport); + usbspd = r8a66597->speed; + val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001); + r8a66597_write(r8a66597, val, devadd_reg); +} + +static int r8a66597_clock_enable(struct r8a66597 *r8a66597) +{ + u16 tmp; + int i = 0; + +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + do { + r8a66597_write(r8a66597, SCKE, SYSCFG0); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 1000) { + printf("register access fail.\n"); + return -1; + } + } while ((tmp & SCKE) != SCKE); + r8a66597_write(r8a66597, 0x04, 0x02); +#else + do { + r8a66597_write(r8a66597, USBE, SYSCFG0); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 1000) { + printf("register access fail.\n"); + return -1; + } + } while ((tmp & USBE) != USBE); + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_mdfy(r8a66597, CONFIG_R8A66597_XTAL, XTAL, SYSCFG0); + + i = 0; + r8a66597_bset(r8a66597, XCKE, SYSCFG0); + do { + udelay(1000); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 500) { + printf("register access fail.\n"); + return -1; + } + } while ((tmp & SCKE) != SCKE); +#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */ + + return 0; +} + +static void r8a66597_clock_disable(struct r8a66597 *r8a66597) +{ + r8a66597_bclr(r8a66597, SCKE, SYSCFG0); + udelay(1); +#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + r8a66597_bclr(r8a66597, PLLC, SYSCFG0); + r8a66597_bclr(r8a66597, XCKE, SYSCFG0); + r8a66597_bclr(r8a66597, USBE, SYSCFG0); +#endif +} + +static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port) +{ + u16 val; + + val = port ? DRPD : DCFM | DRPD; + r8a66597_bset(r8a66597, val, get_syscfg_reg(port)); + r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port)); + + r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port)); +} + +static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port) +{ + u16 val, tmp; + + r8a66597_write(r8a66597, 0, get_intenb_reg(port)); + r8a66597_write(r8a66597, 0, get_intsts_reg(port)); + + r8a66597_port_power(r8a66597, port, 0); + + do { + tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS; + udelay(640); + } while (tmp == EDGESTS); + + val = port ? DRPD : DCFM | DRPD; + r8a66597_bclr(r8a66597, val, get_syscfg_reg(port)); + r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port)); +} + +static int enable_controller(struct r8a66597 *r8a66597) +{ + int ret, port; + + ret = r8a66597_clock_enable(r8a66597); + if (ret < 0) + return ret; + + r8a66597_bset(r8a66597, CONFIG_R8A66597_LDRV & LDRV, PINCFG); + r8a66597_bset(r8a66597, USBE, SYSCFG0); + + r8a66597_bset(r8a66597, INTL, SOFCFG); + r8a66597_write(r8a66597, 0, INTENB0); + r8a66597_write(r8a66597, 0, INTENB1); + r8a66597_write(r8a66597, 0, INTENB2); + + r8a66597_bset(r8a66597, CONFIG_R8A66597_ENDIAN & BIGEND, CFIFOSEL); + r8a66597_bset(r8a66597, CONFIG_R8A66597_ENDIAN & BIGEND, D0FIFOSEL); + r8a66597_bset(r8a66597, CONFIG_R8A66597_ENDIAN & BIGEND, D1FIFOSEL); + r8a66597_bset(r8a66597, TRNENSEL, SOFCFG); + + for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) + r8a66597_enable_port(r8a66597, port); + + return 0; +} + +static void disable_controller(struct r8a66597 *r8a66597) +{ + int i; + + if (!(r8a66597_read(r8a66597, SYSCFG0) & USBE)) + return; + + r8a66597_write(r8a66597, 0, INTENB0); + r8a66597_write(r8a66597, 0, INTSTS0); + + r8a66597_write(r8a66597, 0, D0FIFOSEL); + r8a66597_write(r8a66597, 0, D1FIFOSEL); + r8a66597_write(r8a66597, 0, DCPCFG); + r8a66597_write(r8a66597, 0x40, DCPMAXP); + r8a66597_write(r8a66597, 0, DCPCTR); + + for (i = 0; i <= 10; i++) + r8a66597_write(r8a66597, 0, get_devadd_addr(i)); + for (i = 1; i <= 5; i++) { + r8a66597_write(r8a66597, 0, get_pipetre_addr(i)); + r8a66597_write(r8a66597, 0, get_pipetrn_addr(i)); + } + for (i = 1; i < R8A66597_MAX_NUM_PIPE; i++) { + r8a66597_write(r8a66597, 0, get_pipectr_addr(i)); + r8a66597_write(r8a66597, i, PIPESEL); + r8a66597_write(r8a66597, 0, PIPECFG); + r8a66597_write(r8a66597, 0, PIPEBUF); + r8a66597_write(r8a66597, 0, PIPEMAXP); + r8a66597_write(r8a66597, 0, PIPEPERI); + } + + for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++) + r8a66597_disable_port(r8a66597, i); + + r8a66597_clock_disable(r8a66597); +} + +static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg, + u16 mask, u16 loop) +{ + u16 tmp; + int i = 0; + + do { + tmp = r8a66597_read(r8a66597, reg); + if (i++ > 1000000) { + printf("register%lx, loop %x is timeout\n", reg, loop); + break; + } + } while ((tmp & mask) != loop); +} + +static void pipe_buffer_setting(struct r8a66597 *r8a66597, + struct usb_device *dev, unsigned long pipe) +{ + u16 val = 0; + u16 pipenum, bufnum, maxpacket; + + if (usb_pipein(pipe)) { + pipenum = BULK_IN_PIPENUM; + bufnum = BULK_IN_BUFNUM; + maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; + } else { + pipenum = BULK_OUT_PIPENUM; + bufnum = BULK_OUT_BUFNUM; + maxpacket = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; + } + + if (r8a66597->pipe_config & (1 << pipenum)) + return; + r8a66597->pipe_config |= (1 << pipenum); + + r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(pipenum)); + r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(pipenum)); + r8a66597_write(r8a66597, pipenum, PIPESEL); + + /* FIXME: This driver support bulk transfer only. */ + if (!usb_pipein(pipe)) + val |= R8A66597_DIR; + else + val |= R8A66597_SHTNAK; + val |= R8A66597_BULK | R8A66597_DBLB | usb_pipeendpoint(pipe); + r8a66597_write(r8a66597, val, PIPECFG); + + r8a66597_write(r8a66597, (8 << 10) | bufnum, PIPEBUF); + r8a66597_write(r8a66597, make_devsel(usb_pipedevice(pipe)) | + maxpacket, PIPEMAXP); + r8a66597_write(r8a66597, 0, PIPEPERI); + r8a66597_write(r8a66597, SQCLR, get_pipectr_addr(pipenum)); +} + +static int send_setup_packet(struct r8a66597 *r8a66597, struct usb_device *dev, + struct devrequest *setup) +{ + int i; + unsigned short *p = (unsigned short *)setup; + unsigned long setup_addr = USBREQ; + u16 intsts1; + int timeout = 3000; + u16 devsel = setup->request == USB_REQ_SET_ADDRESS ? 0 : dev->devnum; + + r8a66597_write(r8a66597, make_devsel(devsel) | + (8 << dev->maxpacketsize), DCPMAXP); + r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1); + + for (i = 0; i < 4; i++) { + r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr); + setup_addr += 2; + } + r8a66597_write(r8a66597, ~0x0001, BRDYSTS); + r8a66597_write(r8a66597, SUREQ, DCPCTR); + + while (1) { + intsts1 = r8a66597_read(r8a66597, INTSTS1); + if (intsts1 & SACK) + break; + if (intsts1 & SIGN) { + printf("setup packet send error\n"); + return -1; + } + if (timeout-- < 0) { + printf("setup packet timeout\n"); + return -1; + } + udelay(500); + } + + return 0; +} + +static int send_bulk_packet(struct r8a66597 *r8a66597, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len) +{ + u16 tmp, bufsize; + u16 *buf; + size_t size; + + R8A66597_DPRINT("%s\n", __func__); + + r8a66597_mdfy(r8a66597, MBW | BULK_OUT_PIPENUM, + MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, BULK_OUT_PIPENUM); + tmp = r8a66597_read(r8a66597, CFIFOCTR); + if ((tmp & FRDY) == 0) { + printf("%s FRDY is not set (%x)\n", __func__, tmp); + return -1; + } + + /* prepare parameters */ + bufsize = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; + buf = (u16 *)(buffer + dev->act_len); + size = min((int)bufsize, transfer_len - dev->act_len); + + /* write fifo */ + r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); + if (buffer) { + r8a66597_write_fifo(r8a66597, CFIFO, buf, size); + r8a66597_write(r8a66597, BVAL, CFIFOCTR); + } + + /* update parameters */ + dev->act_len += size; + + r8a66597_mdfy(r8a66597, PID_BUF, PID, + get_pipectr_addr(BULK_OUT_PIPENUM)); + + while (!(r8a66597_read(r8a66597, BEMPSTS) & (1 << BULK_OUT_PIPENUM))) + if (ctrlc()) + return -1; + r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); + + if (dev->act_len >= transfer_len) + r8a66597_mdfy(r8a66597, PID_NAK, PID, + get_pipectr_addr(BULK_OUT_PIPENUM)); + + return 0; +} + +static int receive_bulk_packet(struct r8a66597 *r8a66597, + struct usb_device *dev, + unsigned long pipe, + void *buffer, int transfer_len) +{ + u16 tmp; + u16 *buf; + const u16 pipenum = BULK_IN_PIPENUM; + int rcv_len; + int maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; + + R8A66597_DPRINT("%s\n", __func__); + + /* prepare */ + if (dev->act_len == 0) { + r8a66597_mdfy(r8a66597, PID_NAK, PID, + get_pipectr_addr(pipenum)); + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + + r8a66597_write(r8a66597, TRCLR, get_pipetre_addr(pipenum)); + r8a66597_write(r8a66597, + (transfer_len + maxpacket - 1) / maxpacket, + get_pipetrn_addr(pipenum)); + r8a66597_bset(r8a66597, TRENB, get_pipetre_addr(pipenum)); + + r8a66597_mdfy(r8a66597, PID_BUF, PID, + get_pipectr_addr(pipenum)); + } + + r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum); + + while (!(r8a66597_read(r8a66597, BRDYSTS) & (1 << pipenum))) + if (ctrlc()) + return -1; + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + + tmp = r8a66597_read(r8a66597, CFIFOCTR); + if ((tmp & FRDY) == 0) { + printf("%s FRDY is not set. (%x)\n", __func__, tmp); + return -1; + } + + buf = (u16 *)(buffer + dev->act_len); + rcv_len = tmp & DTLN; + dev->act_len += rcv_len; + + if (buffer) { + if (rcv_len == 0) + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + else + r8a66597_read_fifo(r8a66597, CFIFO, buf, rcv_len); + } + + return 0; +} + +static int receive_control_packet(struct r8a66597 *r8a66597, + struct usb_device *dev, + void *buffer, int transfer_len) +{ + u16 tmp; + int rcv_len; + + /* FIXME: limit transfer size : 64byte or less */ + + r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_bset(r8a66597, SQSET, DCPCTR); + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); + + while (!(r8a66597_read(r8a66597, BRDYSTS) & 0x0001)) + if (ctrlc()) + return -1; + r8a66597_write(r8a66597, ~0x0001, BRDYSTS); + + r8a66597_mdfy(r8a66597, MBW, MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + + tmp = r8a66597_read(r8a66597, CFIFOCTR); + if ((tmp & FRDY) == 0) { + printf("%s FRDY is not set. (%x)\n", __func__, tmp); + return -1; + } + + rcv_len = tmp & DTLN; + dev->act_len += rcv_len; + + r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); + + if (buffer) { + if (rcv_len == 0) + r8a66597_write(r8a66597, BCLR, DCPCTR); + else + r8a66597_read_fifo(r8a66597, CFIFO, buffer, rcv_len); + } + + return 0; +} + +static int send_status_packet(struct r8a66597 *r8a66597, + unsigned long pipe) +{ + r8a66597_bset(r8a66597, SQSET, DCPCTR); + r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); + + if (usb_pipein(pipe)) { + r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_write(r8a66597, ~BEMP0, BEMPSTS); + r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR); + } else { + r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + } + r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); + + while (!(r8a66597_read(r8a66597, BEMPSTS) & 0x0001)) + if (ctrlc()) + return -1; + + return 0; +} + +static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port) +{ + int count = R8A66597_MAX_SAMPLING; + unsigned short syssts, old_syssts; + + R8A66597_DPRINT("%s\n", __func__); + + old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); + while (count > 0) { + mdelay(R8A66597_RH_POLL_TIME); + + syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); + if (syssts == old_syssts) { + count--; + } else { + count = R8A66597_MAX_SAMPLING; + old_syssts = syssts; + } + } +} + +static void r8a66597_bus_reset(struct r8a66597 *r8a66597, int port) +{ + mdelay(10); + r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT, get_dvstctr_reg(port)); + mdelay(50); + r8a66597_mdfy(r8a66597, UACT, USBRST | UACT, get_dvstctr_reg(port)); + mdelay(50); +} + +static int check_usb_device_connecting(struct r8a66597 *r8a66597) +{ + int timeout = 10000; /* 100usec * 10000 = 1sec */ + int i; + + for (i = 0; i < 5; i++) { + /* check a usb cable connect */ + while (!(r8a66597_read(r8a66597, INTSTS1) & ATTCH)) { + if (timeout-- < 0) { + printf("%s timeout.\n", __func__); + return -1; + } + udelay(100); + } + + /* check a data line */ + r8a66597_check_syssts(r8a66597, 0); + + r8a66597_bus_reset(r8a66597, 0); + r8a66597->speed = get_rh_usb_speed(r8a66597, 0); + + if (!(r8a66597_read(r8a66597, INTSTS1) & DTCH)) { + r8a66597->port_change = USB_PORT_STAT_C_CONNECTION; + r8a66597->port_status = USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE; + return 0; /* success */ + } + + R8A66597_DPRINT("USB device has detached. retry = %d\n", i); + r8a66597_write(r8a66597, ~DTCH, INTSTS1); + } + + return -1; /* fail */ +} + +/* based on usb_ohci.c */ +#define min_t(type, x, y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +#include + +static int r8a66597_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wLength; + unsigned char data[32]; + + R8A66597_DPRINT("%s\n", __func__); + + if (usb_pipeint(pipe)) { + printf("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = cpu_to_le16 (cmd->value); + wLength = cpu_to_le16 (cmd->length); + + switch (bmRType_bReq) { + case RH_GET_STATUS: + *(__u16 *)buffer = cpu_to_le16(1); + len = 2; + break; + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *)buffer = cpu_to_le16(0); + len = 2; + break; + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *)buffer = cpu_to_le16(0); + len = 2; + break; + case RH_GET_STATUS | RH_CLASS: + *(__u32 *)buffer = cpu_to_le32(0); + len = 4; + break; + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *)buffer = cpu_to_le32(r8a66597->port_status | + (r8a66597->port_change << 16)); + len = 4; + break; + case RH_CLEAR_FEATURE | RH_ENDPOINT: + case RH_CLEAR_FEATURE | RH_CLASS: + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_C_PORT_CONNECTION: + r8a66597->port_change &= ~USB_PORT_STAT_C_CONNECTION; + break; + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + break; + case (RH_PORT_RESET): + r8a66597_bus_reset(r8a66597, 0); + break; + case (RH_PORT_POWER): + break; + case (RH_PORT_ENABLE): + break; + } + break; + case RH_SET_ADDRESS: + gr8a66597.rh_devnum = wValue; + break; + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_dev_des), + wLength)); + memcpy(buffer, root_hub_dev_des, len); + break; + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + memcpy(buffer, root_hub_config_des, len); + break; + case (0x03): /* string descriptors */ + if (wValue == 0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + memcpy(buffer, root_hub_str_index0, len); + } + if (wValue == 0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + memcpy(buffer, root_hub_str_index1, len); + } + break; + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = 0x00000001; + + data[0] = 9; /* min length; */ + data[1] = 0x29; + data[2] = temp & RH_A_NDP; + data[3] = 0; + if (temp & RH_A_PSM) + data[3] |= 0x1; + if (temp & RH_A_NOCP) + data[3] |= 0x10; + else if (temp & RH_A_OCPM) + data[3] |= 0x8; + + /* corresponds to data[4-7] */ + data[5] = (temp & RH_A_POTPGT) >> 24; + data[7] = temp & RH_B_DR; + if (data[2] < 7) { + data[8] = 0xff; + } else { + data[0] += 2; + data[8] = (temp & RH_B_DR) >> 8; + data[10] = data[9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data[0], wLength)); + memcpy(buffer, data, len); + break; + } + + case RH_GET_CONFIGURATION: + *(__u8 *) buffer = 0x01; + len = 1; + break; + case RH_SET_CONFIGURATION: + break; + default: + R8A66597_DPRINT("unsupported root hub command"); + stat = USB_ST_STALLED; + } + + mdelay(1); + + len = min_t(int, len, leni); + + dev->act_len = len; + dev->status = stat; + + return stat; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + int ret = 0; + + R8A66597_DPRINT("%s\n", __func__); + R8A66597_DPRINT("pipe = %08x, buffer = %p, len = %d, devnum = %d\n", + pipe, buffer, transfer_len, dev->devnum); + + set_devadd(r8a66597, dev->devnum, dev, 0); + + pipe_buffer_setting(r8a66597, dev, pipe); + + dev->act_len = 0; + while (dev->act_len < transfer_len && ret == 0) { + if (ctrlc()) + return -1; + + if (usb_pipein(pipe)) + ret = receive_bulk_packet(r8a66597, dev, pipe, buffer, + transfer_len); + else + ret = send_bulk_packet(r8a66597, dev, pipe, buffer, + transfer_len); + } + + if (ret == 0) + dev->status = 0; + + return ret; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *setup) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + u16 r8a66597_address = setup->request == USB_REQ_SET_ADDRESS ? + 0 : dev->devnum; + + R8A66597_DPRINT("%s\n", __func__); + if (usb_pipedevice(pipe) == r8a66597->rh_devnum) + return r8a66597_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + + R8A66597_DPRINT("%s: setup\n", __func__); + set_devadd(r8a66597, r8a66597_address, dev, 0); + + if (send_setup_packet(r8a66597, dev, setup) < 0) { + printf("setup packet send error\n"); + return -1; + } + + dev->act_len = 0; + if (usb_pipein(pipe)) + if (receive_control_packet(r8a66597, dev, buffer, + transfer_len) < 0) + return -1; + + if (send_status_packet(r8a66597, pipe) < 0) + return -1; + + dev->status = 0; + + return 0; +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + /* no implement */ + R8A66597_DPRINT("%s\n", __func__); + return 0; +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + struct r8a66597 *r8a66597 = &gr8a66597; + + R8A66597_DPRINT("%s\n", __func__); + + memset(r8a66597, 0, sizeof(r8a66597)); + r8a66597->reg = CONFIG_R8A66597_BASE_ADDR; + + disable_controller(r8a66597); + mdelay(100); + + enable_controller(r8a66597); + r8a66597_port_power(r8a66597, 0 , 1); + + /* check usb device */ + check_usb_device_connecting(r8a66597); + + mdelay(50); + + return 0; +} + +int usb_lowlevel_stop(int index) +{ + disable_controller(&gr8a66597); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/r8a66597.h b/qemu/roms/u-boot/drivers/usb/host/r8a66597.h new file mode 100644 index 000000000..ca1b67155 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/r8a66597.h @@ -0,0 +1,659 @@ +/* + * R8A66597 HCD (Host Controller Driver) for u-boot + * + * Copyright (C) 2008 Yoshihiro Shimoda + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __R8A66597_H__ +#define __R8A66597_H__ + +#define SYSCFG0 0x00 +#define SYSCFG1 0x02 +#define SYSSTS0 0x04 +#define SYSSTS1 0x06 +#define DVSTCTR0 0x08 +#define DVSTCTR1 0x0A +#define TESTMODE 0x0C +#define PINCFG 0x0E +#define DMA0CFG 0x10 +#define DMA1CFG 0x12 +#define CFIFO 0x14 +#define D0FIFO 0x18 +#define D1FIFO 0x1C +#define CFIFOSEL 0x20 +#define CFIFOCTR 0x22 +#define CFIFOSIE 0x24 +#define D0FIFOSEL 0x28 +#define D0FIFOCTR 0x2A +#define D1FIFOSEL 0x2C +#define D1FIFOCTR 0x2E +#define INTENB0 0x30 +#define INTENB1 0x32 +#define INTENB2 0x34 +#define BRDYENB 0x36 +#define NRDYENB 0x38 +#define BEMPENB 0x3A +#define SOFCFG 0x3C +#define INTSTS0 0x40 +#define INTSTS1 0x42 +#define INTSTS2 0x44 +#define BRDYSTS 0x46 +#define NRDYSTS 0x48 +#define BEMPSTS 0x4A +#define FRMNUM 0x4C +#define UFRMNUM 0x4E +#define USBADDR 0x50 +#define USBREQ 0x54 +#define USBVAL 0x56 +#define USBINDX 0x58 +#define USBLENG 0x5A +#define DCPCFG 0x5C +#define DCPMAXP 0x5E +#define DCPCTR 0x60 +#define PIPESEL 0x64 +#define PIPECFG 0x68 +#define PIPEBUF 0x6A +#define PIPEMAXP 0x6C +#define PIPEPERI 0x6E +#define PIPE1CTR 0x70 +#define PIPE2CTR 0x72 +#define PIPE3CTR 0x74 +#define PIPE4CTR 0x76 +#define PIPE5CTR 0x78 +#define PIPE6CTR 0x7A +#define PIPE7CTR 0x7C +#define PIPE8CTR 0x7E +#define PIPE9CTR 0x80 +#define PIPE1TRE 0x90 +#define PIPE1TRN 0x92 +#define PIPE2TRE 0x94 +#define PIPE2TRN 0x96 +#define PIPE3TRE 0x98 +#define PIPE3TRN 0x9A +#define PIPE4TRE 0x9C +#define PIPE4TRN 0x9E +#define PIPE5TRE 0xA0 +#define PIPE5TRN 0xA2 +#define DEVADD0 0xD0 +#define DEVADD1 0xD2 +#define DEVADD2 0xD4 +#define DEVADD3 0xD6 +#define DEVADD4 0xD8 +#define DEVADD5 0xDA +#define DEVADD6 0xDC +#define DEVADD7 0xDE +#define DEVADD8 0xE0 +#define DEVADD9 0xE2 +#define DEVADDA 0xE4 + +/* System Configuration Control Register */ +#define XTAL 0xC000 /* b15-14: Crystal selection */ +#define XTAL48 0x8000 /* 48MHz */ +#define XTAL24 0x4000 /* 24MHz */ +#define XTAL12 0x0000 /* 12MHz */ +#define XCKE 0x2000 /* b13: External clock enable */ +#define PLLC 0x0800 /* b11: PLL control */ +#define SCKE 0x0400 /* b10: USB clock enable */ +#define PCSDIS 0x0200 /* b9: not CS wakeup */ +#define LPSME 0x0100 /* b8: Low power sleep mode */ +#define HSE 0x0080 /* b7: Hi-speed enable */ +#define DCFM 0x0040 /* b6: Controller function select */ +#define DRPD 0x0020 /* b5: D+/- pull down control */ +#define DPRPU 0x0010 /* b4: D+ pull up control */ +#define USBE 0x0001 /* b0: USB module operation enable */ + +/* System Configuration Status Register */ +#define OVCBIT 0x8000 /* b15-14: Over-current bit */ +#define OVCMON 0xC000 /* b15-14: Over-current monitor */ +#define SOFEA 0x0020 /* b5: SOF monitor */ +#define IDMON 0x0004 /* b3: ID-pin monitor */ +#define LNST 0x0003 /* b1-0: D+, D- line status */ +#define SE1 0x0003 /* SE1 */ +#define FS_KSTS 0x0002 /* Full-Speed K State */ +#define FS_JSTS 0x0001 /* Full-Speed J State */ +#define LS_JSTS 0x0002 /* Low-Speed J State */ +#define LS_KSTS 0x0001 /* Low-Speed K State */ +#define SE0 0x0000 /* SE0 */ + +/* Device State Control Register */ +#define EXTLP0 0x0400 /* b10: External port */ +#define VBOUT 0x0200 /* b9: VBUS output */ +#define WKUP 0x0100 /* b8: Remote wakeup */ +#define RWUPE 0x0080 /* b7: Remote wakeup sense */ +#define USBRST 0x0040 /* b6: USB reset enable */ +#define RESUME 0x0020 /* b5: Resume enable */ +#define UACT 0x0010 /* b4: USB bus enable */ +#define RHST 0x0007 /* b1-0: Reset handshake status */ +#define HSPROC 0x0004 /* HS handshake is processing */ +#define HSMODE 0x0003 /* Hi-Speed mode */ +#define FSMODE 0x0002 /* Full-Speed mode */ +#define LSMODE 0x0001 /* Low-Speed mode */ +#define UNDECID 0x0000 /* Undecided */ + +/* Test Mode Register */ +#define UTST 0x000F /* b3-0: Test select */ +#define H_TST_PACKET 0x000C /* HOST TEST Packet */ +#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */ +#define H_TST_K 0x000A /* HOST TEST K */ +#define H_TST_J 0x0009 /* HOST TEST J */ +#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */ +#define P_TST_PACKET 0x0004 /* PERI TEST Packet */ +#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */ +#define P_TST_K 0x0002 /* PERI TEST K */ +#define P_TST_J 0x0001 /* PERI TEST J */ +#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */ + +/* Data Pin Configuration Register */ +#define LDRV 0x8000 /* b15: Drive Current Adjust */ +#define VIF1 0x0000 /* VIF = 1.8V */ +#define VIF3 0x8000 /* VIF = 3.3V */ +#define INTA 0x0001 /* b1: USB INT-pin active */ + +/* DMAx Pin Configuration Register */ +#define DREQA 0x4000 /* b14: Dreq active select */ +#define BURST 0x2000 /* b13: Burst mode */ +#define DACKA 0x0400 /* b10: Dack active select */ +#define DFORM 0x0380 /* b9-7: DMA mode select */ +#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */ +#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */ +#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */ +#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */ +#define DENDA 0x0040 /* b6: Dend active select */ +#define PKTM 0x0020 /* b5: Packet mode */ +#define DENDE 0x0010 /* b4: Dend enable */ +#define OBUS 0x0004 /* b2: OUTbus mode */ + +/* CFIFO/DxFIFO Port Select Register */ +#define RCNT 0x8000 /* b15: Read count mode */ +#define REW 0x4000 /* b14: Buffer rewind */ +#define DCLRM 0x2000 /* b13: DMA buffer clear mode */ +#define DREQE 0x1000 /* b12: DREQ output enable */ +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#define MBW 0x0800 +#else +#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */ +#endif +#define MBW_8 0x0000 /* 8bit */ +#define MBW_16 0x0400 /* 16bit */ +#define BIGEND 0x0100 /* b8: Big endian mode */ +#define BYTE_LITTLE 0x0000 /* little dendian */ +#define BYTE_BIG 0x0100 /* big endifan */ +#define ISEL 0x0020 /* b5: DCP FIFO port direction select */ +#define CURPIPE 0x000F /* b2-0: PIPE select */ + +/* CFIFO/DxFIFO Port Control Register */ +#define BVAL 0x8000 /* b15: Buffer valid flag */ +#define BCLR 0x4000 /* b14: Buffer clear */ +#define FRDY 0x2000 /* b13: FIFO ready */ +#define DTLN 0x0FFF /* b11-0: FIFO received data length */ + +/* Interrupt Enable Register 0 */ +#define VBSE 0x8000 /* b15: VBUS interrupt */ +#define RSME 0x4000 /* b14: Resume interrupt */ +#define SOFE 0x2000 /* b13: Frame update interrupt */ +#define DVSE 0x1000 /* b12: Device state transition interrupt */ +#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMPE 0x0400 /* b10: Buffer empty interrupt */ +#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDYE 0x0100 /* b8: Buffer ready interrupt */ + +/* Interrupt Enable Register 1 */ +#define OVRCRE 0x8000 /* b15: Over-current interrupt */ +#define BCHGE 0x4000 /* b14: USB us chenge interrupt */ +#define DTCHE 0x1000 /* b12: Detach sense interrupt */ +#define ATTCHE 0x0800 /* b11: Attach sense interrupt */ +#define EOFERRE 0x0040 /* b6: EOF error interrupt */ +#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */ +#define SACKE 0x0010 /* b4: SETUP ACK interrupt */ + +/* BRDY Interrupt Enable/Status Register */ +#define BRDY9 0x0200 /* b9: PIPE9 */ +#define BRDY8 0x0100 /* b8: PIPE8 */ +#define BRDY7 0x0080 /* b7: PIPE7 */ +#define BRDY6 0x0040 /* b6: PIPE6 */ +#define BRDY5 0x0020 /* b5: PIPE5 */ +#define BRDY4 0x0010 /* b4: PIPE4 */ +#define BRDY3 0x0008 /* b3: PIPE3 */ +#define BRDY2 0x0004 /* b2: PIPE2 */ +#define BRDY1 0x0002 /* b1: PIPE1 */ +#define BRDY0 0x0001 /* b1: PIPE0 */ + +/* NRDY Interrupt Enable/Status Register */ +#define NRDY9 0x0200 /* b9: PIPE9 */ +#define NRDY8 0x0100 /* b8: PIPE8 */ +#define NRDY7 0x0080 /* b7: PIPE7 */ +#define NRDY6 0x0040 /* b6: PIPE6 */ +#define NRDY5 0x0020 /* b5: PIPE5 */ +#define NRDY4 0x0010 /* b4: PIPE4 */ +#define NRDY3 0x0008 /* b3: PIPE3 */ +#define NRDY2 0x0004 /* b2: PIPE2 */ +#define NRDY1 0x0002 /* b1: PIPE1 */ +#define NRDY0 0x0001 /* b1: PIPE0 */ + +/* BEMP Interrupt Enable/Status Register */ +#define BEMP9 0x0200 /* b9: PIPE9 */ +#define BEMP8 0x0100 /* b8: PIPE8 */ +#define BEMP7 0x0080 /* b7: PIPE7 */ +#define BEMP6 0x0040 /* b6: PIPE6 */ +#define BEMP5 0x0020 /* b5: PIPE5 */ +#define BEMP4 0x0010 /* b4: PIPE4 */ +#define BEMP3 0x0008 /* b3: PIPE3 */ +#define BEMP2 0x0004 /* b2: PIPE2 */ +#define BEMP1 0x0002 /* b1: PIPE1 */ +#define BEMP0 0x0001 /* b0: PIPE0 */ + +/* SOF Pin Configuration Register */ +#define TRNENSEL 0x0100 /* b8: Select transaction enable period */ +#define BRDYM 0x0040 /* b6: BRDY clear timing */ +#define INTL 0x0020 /* b5: Interrupt sense select */ +#define EDGESTS 0x0010 /* b4: */ +#define SOFMODE 0x000C /* b3-2: SOF pin select */ +#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */ +#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */ +#define SOF_DISABLE 0x0000 /* SOF OUT Disable */ + +/* Interrupt Status Register 0 */ +#define VBINT 0x8000 /* b15: VBUS interrupt */ +#define RESM 0x4000 /* b14: Resume interrupt */ +#define SOFR 0x2000 /* b13: SOF frame update interrupt */ +#define DVST 0x1000 /* b12: Device state transition interrupt */ +#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMP 0x0400 /* b10: Buffer empty interrupt */ +#define NRDY 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDY 0x0100 /* b8: Buffer ready interrupt */ +#define VBSTS 0x0080 /* b7: VBUS input port */ +#define DVSQ 0x0070 /* b6-4: Device state */ +#define DS_SPD_CNFG 0x0070 /* Suspend Configured */ +#define DS_SPD_ADDR 0x0060 /* Suspend Address */ +#define DS_SPD_DFLT 0x0050 /* Suspend Default */ +#define DS_SPD_POWR 0x0040 /* Suspend Powered */ +#define DS_SUSP 0x0040 /* Suspend */ +#define DS_CNFG 0x0030 /* Configured */ +#define DS_ADDS 0x0020 /* Address */ +#define DS_DFLT 0x0010 /* Default */ +#define DS_POWR 0x0000 /* Powered */ +#define DVSQS 0x0030 /* b5-4: Device state */ +#define VALID 0x0008 /* b3: Setup packet detected flag */ +#define CTSQ 0x0007 /* b2-0: Control transfer stage */ +#define CS_SQER 0x0006 /* Sequence error */ +#define CS_WRND 0x0005 /* Control write nodata status stage */ +#define CS_WRSS 0x0004 /* Control write status stage */ +#define CS_WRDS 0x0003 /* Control write data stage */ +#define CS_RDSS 0x0002 /* Control read status stage */ +#define CS_RDDS 0x0001 /* Control read data stage */ +#define CS_IDST 0x0000 /* Idle or setup stage */ + +/* Interrupt Status Register 1 */ +#define OVRCR 0x8000 /* b15: Over-current interrupt */ +#define BCHG 0x4000 /* b14: USB bus chenge interrupt */ +#define DTCH 0x1000 /* b12: Detach sense interrupt */ +#define ATTCH 0x0800 /* b11: Attach sense interrupt */ +#define EOFERR 0x0040 /* b6: EOF-error interrupt */ +#define SIGN 0x0020 /* b5: Setup ignore interrupt */ +#define SACK 0x0010 /* b4: Setup acknowledge interrupt */ + +/* Frame Number Register */ +#define OVRN 0x8000 /* b15: Overrun error */ +#define CRCE 0x4000 /* b14: Received data error */ +#define FRNM 0x07FF /* b10-0: Frame number */ + +/* Micro Frame Number Register */ +#define UFRNM 0x0007 /* b2-0: Micro frame number */ + +/* Default Control Pipe Maxpacket Size Register */ +/* Pipe Maxpacket Size Register */ +#define DEVSEL 0xF000 /* b15-14: Device address select */ +#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */ + +/* Default Control Pipe Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define SUREQ 0x4000 /* b14: Send USB request */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define SUREQCLR 0x0800 /* b11: stop setup request */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PINGE 0x0010 /* b4: ping enable */ +#define CCPL 0x0004 /* b2: Enable control transfer complete */ +#define PID 0x0003 /* b1-0: Response PID */ +#define PID_STALL11 0x0003 /* STALL */ +#define PID_STALL 0x0002 /* STALL */ +#define PID_BUF 0x0001 /* BUF */ +#define PID_NAK 0x0000 /* NAK */ + +/* Pipe Window Select Register */ +#define PIPENM 0x0007 /* b2-0: Pipe select */ + +/* Pipe Configuration Register */ +#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */ +#define R8A66597_ISO 0xC000 /* Isochronous */ +#define R8A66597_INT 0x8000 /* Interrupt */ +#define R8A66597_BULK 0x4000 /* Bulk */ +#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */ +#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */ +#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */ +#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */ +#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */ +#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */ + +/* Pipe Buffer Configuration Register */ +#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */ +#define BUFNMB 0x007F /* b6-0: Pipe buffer number */ +#define PIPE0BUF 256 +#define PIPExBUF 64 + +/* Pipe Maxpacket Size Register */ +#define MXPS 0x07FF /* b10-0: Maxpacket size */ + +/* Pipe Cycle Configuration Register */ +#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */ +#define IITV 0x0007 /* b2-0: Isochronous interval */ + +/* Pipex Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define ATREPM 0x0400 /* b10: Auto repeat mode */ +#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PID 0x0003 /* b1-0: Response PID */ + +/* PIPExTRE */ +#define TRENB 0x0200 /* b9: Transaction counter enable */ +#define TRCLR 0x0100 /* b8: Transaction counter clear */ + +/* PIPExTRN */ +#define TRNCNT 0xFFFF /* b15-0: Transaction counter */ + +/* DEVADDx */ +#define UPPHUB 0x7800 +#define HUBPORT 0x0700 +#define USBSPD 0x00C0 +#define RTPORT 0x0001 + +#define R8A66597_MAX_NUM_PIPE 10 +#define R8A66597_BUF_BSIZE 8 +#define R8A66597_MAX_DEVICE 10 +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#define R8A66597_MAX_ROOT_HUB 1 +#else +#define R8A66597_MAX_ROOT_HUB 2 +#endif +#define R8A66597_MAX_SAMPLING 5 +#define R8A66597_RH_POLL_TIME 10 + +#define BULK_IN_PIPENUM 3 +#define BULK_IN_BUFNUM 8 + +#define BULK_OUT_PIPENUM 4 +#define BULK_OUT_BUFNUM 40 + +#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5)) +#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9)) +#define make_devsel(addr) (addr << 12) + +struct r8a66597 { + unsigned long reg; + unsigned short pipe_config; /* bit field */ + unsigned short port_status; + unsigned short port_change; + u16 speed; /* HSMODE or FSMODE or LSMODE */ + unsigned char rh_devnum; +}; + +static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) +{ + return inw(r8a66597->reg + offset); +} + +static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597, + unsigned long offset, void *buf, + int len) +{ + int i; +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + unsigned long fifoaddr = r8a66597->reg + offset; + unsigned long count; + unsigned long *p = buf; + + count = len / 4; + for (i = 0; i < count; i++) + p[i] = inl(r8a66597->reg + offset); + + if (len & 0x00000003) { + unsigned long tmp = inl(fifoaddr); + memcpy((unsigned char *)buf + count * 4, &tmp, len & 0x03); + } +#else + unsigned short *p = buf; + + len = (len + 1) / 2; + for (i = 0; i < len; i++) + p[i] = inw(r8a66597->reg + offset); +#endif +} + +static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, + unsigned long offset) +{ + outw(val, r8a66597->reg + offset); +} + +static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, + unsigned long offset, void *buf, + int len) +{ + int i; + unsigned long fifoaddr = r8a66597->reg + offset; +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) + unsigned long count; + unsigned char *pb; + unsigned long *p = buf; + + count = len / 4; + for (i = 0; i < count; i++) + outl(p[i], fifoaddr); + + if (len & 0x00000003) { + pb = (unsigned char *)buf + count * 4; + for (i = 0; i < (len & 0x00000003); i++) { + if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND) + outb(pb[i], fifoaddr + i); + else + outb(pb[i], fifoaddr + 3 - i); + } + } +#else + int odd = len & 0x0001; + unsigned short *p = buf; + + len = len / 2; + for (i = 0; i < len; i++) + outw(p[i], fifoaddr); + + if (odd) { + unsigned char *pb = (unsigned char *)(buf + len); + outb(*pb, fifoaddr); + } +#endif +} + +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + +static inline unsigned long get_syscfg_reg(int port) +{ + return port == 0 ? SYSCFG0 : SYSCFG1; +} + +static inline unsigned long get_syssts_reg(int port) +{ + return port == 0 ? SYSSTS0 : SYSSTS1; +} + +static inline unsigned long get_dvstctr_reg(int port) +{ + return port == 0 ? DVSTCTR0 : DVSTCTR1; +} + +static inline unsigned long get_dmacfg_reg(int port) +{ + return port == 0 ? DMA0CFG : DMA1CFG; +} + +static inline unsigned long get_intenb_reg(int port) +{ + return port == 0 ? INTENB1 : INTENB2; +} + +static inline unsigned long get_intsts_reg(int port) +{ + return port == 0 ? INTSTS1 : INTSTS2; +} + +static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port) +{ + unsigned long dvstctr_reg = get_dvstctr_reg(port); + + return r8a66597_read(r8a66597, dvstctr_reg) & RHST; +} + +static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, + int power) +{ + unsigned long dvstctr_reg = get_dvstctr_reg(port); + + if (power) + r8a66597_bset(r8a66597, VBOUT, dvstctr_reg); + else + r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg); +} + +#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4) +#define get_devadd_addr(address) (DEVADD0 + address * 2) + + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h, based on usb_ohci.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +#endif /* __R8A66597_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/host/sl811-hcd.c b/qemu/roms/u-boot/drivers/usb/host/sl811-hcd.c new file mode 100644 index 000000000..b29c67e18 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/sl811-hcd.c @@ -0,0 +1,714 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This code is based on linux driver for sl811hs chip, source at + * drivers/usb/host/sl811.c: + * + * SL811 Host Controller Interface driver for USB. + * + * Copyright (c) 2003/06, Courage Co., Ltd. + * + * Based on: + * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, + * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, + * Adam Richter, Gregory P. Smith; + * 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 3.Rewrited as sl811.o by Yin Aihua + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "sl811.h" + +#include "../../../board/kup/common/kup.h" + +#ifdef __PPC__ +# define EIEIO __asm__ volatile ("eieio") +#else +# define EIEIO /* nothing */ +#endif + +#define SL811_ADR (0x50000000) +#define SL811_DAT (0x50000001) + +#ifdef SL811_DEBUG +static int debug = 9; +#endif + +static int root_hub_devnum = 0; +static struct usb_port_status rh_status = { 0 };/* root hub port status */ + +static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, + void *data, int buf_len, struct devrequest *cmd); + +static void sl811_write (__u8 index, __u8 data) +{ + *(volatile unsigned char *) (SL811_ADR) = index; + EIEIO; + *(volatile unsigned char *) (SL811_DAT) = data; + EIEIO; +} + +static __u8 sl811_read (__u8 index) +{ + __u8 data; + + *(volatile unsigned char *) (SL811_ADR) = index; + EIEIO; + data = *(volatile unsigned char *) (SL811_DAT); + EIEIO; + return (data); +} + +/* + * Read consecutive bytes of data from the SL811H/SL11H buffer + */ +static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size) +{ + *(volatile unsigned char *) (SL811_ADR) = offset; + EIEIO; + while (size--) { + *buf++ = *(volatile unsigned char *) (SL811_DAT); + EIEIO; + } +} + +/* + * Write consecutive bytes of data to the SL811H/SL11H buffer + */ +static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size) +{ + *(volatile unsigned char *) (SL811_ADR) = offset; + EIEIO; + while (size--) { + *(volatile unsigned char *) (SL811_DAT) = *buf++; + EIEIO; + } +} + +int usb_init_kup4x (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + int i; + unsigned char tmp; + + memctl = &immap->im_memctl; + memctl->memc_or7 = 0xFFFF8726; + memctl->memc_br7 = 0x50000401; /* start at 0x50000000 */ + /* BP 14 low = USB ON */ + immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC); + /* PB 14 nomal port */ + immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC); + /* output */ + immap->im_cpm.cp_pbdir |= (BP_USB_VCC); + + puts ("USB: "); + + for (i = 0x10; i < 0xff; i++) { + sl811_write(i, i); + tmp = (sl811_read(i)); + if (tmp != i) { + printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp); + return (-1); + } + } + printf ("SL811 ready\n"); + return (0); +} + +/* + * This function resets SL811HS controller and detects the speed of + * the connecting device + * + * Return: 0 = no device attached; 1 = USB device attached + */ +static int sl811_hc_reset(void) +{ + int status ; + + sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); + sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + + mdelay(20); + + /* Disable hardware SOF generation, clear all irq status. */ + sl811_write(SL811_CTRL1, 0); + mdelay(2); + sl811_write(SL811_INTRSTS, 0xff); + status = sl811_read(SL811_INTRSTS); + + if (status & SL811_INTR_NOTPRESENT) { + /* Device is not present */ + PDEBUG(0, "Device not present\n"); + rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); + rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; + sl811_write(SL811_INTR, SL811_INTR_INSRMV); + return 0; + } + + /* Send SOF to address 0, endpoint 0. */ + sl811_write(SL811_LEN_B, 0); + sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); + sl811_write(SL811_DEV_B, 0x00); + sl811_write(SL811_SOFLOW, SL811_12M_LOW); + + if (status & SL811_INTR_SPEED_FULL) { + /* full speed device connect directly to root hub */ + PDEBUG (0, "Full speed Device attached\n"); + + sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + mdelay(20); + sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); + sl811_write(SL811_CTRL1, SL811_CTRL1_SOF); + + /* start the SOF or EOP */ + sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); + rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; + rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; + mdelay(2); + sl811_write(SL811_INTRSTS, 0xff); + } else { + /* slow speed device connect directly to root-hub */ + PDEBUG(0, "Low speed Device attached\n"); + + sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + mdelay(20); + sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); + sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); + + /* start the SOF or EOP */ + sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); + rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; + mdelay(2); + sl811_write(SL811_INTRSTS, 0xff); + } + + rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; + sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A); + + return 1; +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + root_hub_devnum = 0; + sl811_hc_reset(); + return 0; +} + +int usb_lowlevel_stop(int index) +{ + sl811_hc_reset(); + return 0; +} + +static int calc_needed_buswidth(int bytes, int need_preamble) +{ + return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048; +} + +static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len) +{ + __u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; + __u16 status = 0; + int err = 0, time_start = get_timer(0); + int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && + (dev->speed == USB_SPEED_LOW); + + if (len > 239) + return -1; + + if (usb_pipeout(pipe)) + ctrl |= SL811_USB_CTRL_DIR_OUT; + if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) + ctrl |= SL811_USB_CTRL_TOGGLE_1; + if (need_preamble) + ctrl |= SL811_USB_CTRL_PREAMBLE; + + sl811_write(SL811_INTRSTS, 0xff); + + while (err < 3) { + sl811_write(SL811_ADDR_A, 0x10); + sl811_write(SL811_LEN_A, len); + if (usb_pipeout(pipe) && len) + sl811_write_buf(0x10, buffer, len); + + if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && + sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble)) + ctrl |= SL811_USB_CTRL_SOF; + else + ctrl &= ~SL811_USB_CTRL_SOF; + + sl811_write(SL811_CTRL_A, ctrl); + while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) { + if (5*CONFIG_SYS_HZ < get_timer(time_start)) { + printf("USB transmit timed out\n"); + return -USB_ST_CRC_ERR; + } + } + + sl811_write(SL811_INTRSTS, 0xff); + status = sl811_read(SL811_STS_A); + + if (status & SL811_USB_STS_ACK) { + int remainder = sl811_read(SL811_CNT_A); + if (remainder) { + PDEBUG(0, "usb transfer remainder = %d\n", remainder); + len -= remainder; + } + if (usb_pipein(pipe) && len) + sl811_read_buf(0x10, buffer, len); + return len; + } + + if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK) + continue; + + PDEBUG(0, "usb transfer error %#x\n", (int)status); + err++; + } + + err = 0; + + if (status & SL811_USB_STS_ERROR) + err |= USB_ST_BUF_ERR; + if (status & SL811_USB_STS_TIMEOUT) + err |= USB_ST_CRC_ERR; + if (status & SL811_USB_STS_STALL) + err |= USB_ST_STALLED; + + return -err; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int max = usb_maxpacket(dev, pipe); + int done = 0; + + PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n", + usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); + + dev->status = 0; + + sl811_write(SL811_DEV_A, usb_pipedevice(pipe)); + sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep)); + while (done < len) { + int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, + max > len - done ? len - done : max); + if (res < 0) { + dev->status = -res; + return res; + } + + if (!dir_out && res < max) /* short packet */ + break; + + done += res; + usb_dotoggle(dev, ep, dir_out); + } + + dev->act_len = done; + + return 0; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len,struct devrequest *setup) +{ + int done = 0; + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + + dev->status = 0; + + if (devnum == root_hub_devnum) + return sl811_rh_submit_urb(dev, pipe, buffer, len, setup); + + PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n", + devnum, ep, buffer, len, (int)setup->requesttype, + (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64); + + sl811_write(SL811_DEV_A, devnum); + sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep)); + /* setup phase */ + usb_settoggle(dev, ep, 1, 0); + if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep), + (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) { + int dir_in = usb_pipein(pipe); + int max = usb_maxpacket(dev, pipe); + + /* data phase */ + sl811_write(SL811_PIDEP_A, + PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep)); + usb_settoggle(dev, ep, usb_pipeout(pipe), 1); + while (done < len) { + int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, + max > len - done ? len - done : max); + if (res < 0) { + PDEBUG(0, "status data failed!\n"); + dev->status = -res; + return 0; + } + done += res; + usb_dotoggle(dev, ep, usb_pipeout(pipe)); + if (dir_in && res < max) /* short packet */ + break; + } + + /* status phase */ + sl811_write(SL811_PIDEP_A, + PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep)); + usb_settoggle(dev, ep, !usb_pipeout(pipe), 1); + if (sl811_send_packet(dev, + !dir_in ? usb_rcvctrlpipe(dev, ep) : + usb_sndctrlpipe(dev, ep), + 0, 0) < 0) { + PDEBUG(0, "status phase failed!\n"); + dev->status = -1; + } + } else { + PDEBUG(0, "setup phase failed!\n"); + dev->status = -1; + } + + dev->act_len = done; + + return done; +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, int interval) +{ + PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe, + buffer, len, interval); + return -1; +} + +/* + * SL811 Virtual Root Hub + */ + +/* Device descriptor */ +static __u8 sl811_rh_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 sl811_rh_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +/* root hub class descriptor*/ +static __u8 sl811_rh_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x01, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x50, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0xfc, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + +/* + * helper routine for returning string descriptors in UTF-16LE + * input can actually be ISO-8859-1; ASCII is its 7-bit subset + */ +static int ascii2utf (char *s, u8 *utf, int utfmax) +{ + int retval; + + for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { + *utf++ = *s++; + *utf++ = 0; + } + return retval; +} + +/* + * root_hub_string is used by each host controller's root hub code, + * so that they're identified consistently throughout the system. + */ +static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len) +{ + char buf [30]; + + /* assert (len > (2 * (sizeof (buf) + 1))); + assert (strlen (type) <= 8);*/ + + /* language ids */ + if (id == 0) { + *data++ = 4; *data++ = 3; /* 4 bytes data */ + *data++ = 0; *data++ = 0; /* some language id */ + return 4; + + /* serial number */ + } else if (id == 1) { + sprintf (buf, "%#x", serial); + + /* product description */ + } else if (id == 2) { + sprintf (buf, "USB %s Root Hub", type); + + /* id 3 == vendor description */ + + /* unsupported IDs --> "stall" */ + } else + return 0; + + ascii2utf (buf, data + 2, len - 2); + data [0] = 2 + strlen(buf) * 2; + data [1] = 3; + return data [0]; +} + +/* helper macro */ +#define OK(x) len = (x); break + +/* + * This function handles all USB request to the the virtual root hub + */ +static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, + void *data, int buf_len, struct devrequest *cmd) +{ + __u8 data_buf[16]; + __u8 *bufp = data_buf; + int len = 0; + int status = 0; + __u16 bmRType_bReq; + __u16 wValue = le16_to_cpu (cmd->value); + __u16 wLength = le16_to_cpu (cmd->length); +#ifdef SL811_DEBUG + __u16 wIndex = le16_to_cpu (cmd->index); +#endif + + if (usb_pipeint(pipe)) { + PDEBUG(0, "interrupt transfer unimplemented!\n"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + + PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n", + bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength); + + /* Request Destination: + without flags: Device, + USB_RECIP_INTERFACE: interface, + USB_RECIP_ENDPOINT: endpoint, + USB_TYPE_CLASS means HUB here, + USB_RECIP_OTHER | USB_TYPE_CLASS almost ever means HUB_PORT here + */ + switch (bmRType_bReq) { + case RH_GET_STATUS: + *(__u16 *)bufp = cpu_to_le16(1); + OK(2); + + case RH_GET_STATUS | USB_RECIP_INTERFACE: + *(__u16 *)bufp = cpu_to_le16(0); + OK(2); + + case RH_GET_STATUS | USB_RECIP_ENDPOINT: + *(__u16 *)bufp = cpu_to_le16(0); + OK(2); + + case RH_GET_STATUS | USB_TYPE_CLASS: + *(__u32 *)bufp = cpu_to_le32(0); + OK(4); + + case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS: + *(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus); + OK(4); + + case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT: + switch (wValue) { + case 1: + OK(0); + } + break; + + case RH_CLEAR_FEATURE | USB_TYPE_CLASS: + switch (wValue) { + case C_HUB_LOCAL_POWER: + OK(0); + + case C_HUB_OVER_CURRENT: + OK(0); + } + break; + + case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; + OK(0); + + case USB_PORT_FEAT_SUSPEND: + rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND; + OK(0); + + case USB_PORT_FEAT_POWER: + rh_status.wPortStatus &= ~USB_PORT_STAT_POWER; + OK(0); + + case USB_PORT_FEAT_C_CONNECTION: + rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION; + OK(0); + + case USB_PORT_FEAT_C_ENABLE: + rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE; + OK(0); + + case USB_PORT_FEAT_C_SUSPEND: + rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND; + OK(0); + + case USB_PORT_FEAT_C_OVER_CURRENT: + rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT; + OK(0); + + case USB_PORT_FEAT_C_RESET: + rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET; + OK(0); + } + break; + + case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND; + OK(0); + + case USB_PORT_FEAT_RESET: + rh_status.wPortStatus |= USB_PORT_STAT_RESET; + rh_status.wPortChange = 0; + rh_status.wPortChange |= USB_PORT_STAT_C_RESET; + rh_status.wPortStatus &= ~USB_PORT_STAT_RESET; + rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; + OK(0); + + case USB_PORT_FEAT_POWER: + rh_status.wPortStatus |= USB_PORT_STAT_POWER; + OK(0); + + case USB_PORT_FEAT_ENABLE: + rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; + OK(0); + } + break; + + case RH_SET_ADDRESS: + root_hub_devnum = wValue; + OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case USB_DT_DEVICE: + len = sizeof(sl811_rh_dev_des); + bufp = sl811_rh_dev_des; + OK(len); + + case USB_DT_CONFIG: + len = sizeof(sl811_rh_config_des); + bufp = sl811_rh_config_des; + OK(len); + + case USB_DT_STRING: + len = usb_root_hub_string(wValue & 0xff, (int)(long)0, "SL811HS", data, wLength); + if (len > 0) { + bufp = data; + OK(len); + } + + default: + status = -32; + } + break; + + case RH_GET_DESCRIPTOR | USB_TYPE_CLASS: + len = sizeof(sl811_rh_hub_des); + bufp = sl811_rh_hub_des; + OK(len); + + case RH_GET_CONFIGURATION: + bufp[0] = 0x01; + OK(1); + + case RH_SET_CONFIGURATION: + OK(0); + + default: + PDEBUG(1, "unsupported root hub command\n"); + status = -32; + } + + len = min(len, buf_len); + if (data != bufp) + memcpy(data, bufp, len); + + PDEBUG(5, "len = %d, status = %d\n", len, status); + + usb_dev->status = status; + usb_dev->act_len = len; + + return status == 0 ? len : status; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/sl811.h b/qemu/roms/u-boot/drivers/usb/host/sl811.h new file mode 100644 index 000000000..c1f9f013b --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/sl811.h @@ -0,0 +1,104 @@ +#ifndef __UBOOT_SL811_H +#define __UBOOT_SL811_H + +#undef SL811_DEBUG + +#ifdef SL811_DEBUG + #define PDEBUG(level, fmt, args...) \ + if (debug >= (level)) printf("[%s:%d] " fmt, \ + __PRETTY_FUNCTION__, __LINE__ , ## args) +#else + #define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +/* Sl811 host control register */ +#define SL811_CTRL_A 0x00 +#define SL811_ADDR_A 0x01 +#define SL811_LEN_A 0x02 +#define SL811_STS_A 0x03 /* read */ +#define SL811_PIDEP_A 0x03 /* write */ +#define SL811_CNT_A 0x04 /* read */ +#define SL811_DEV_A 0x04 /* write */ +#define SL811_CTRL1 0x05 +#define SL811_INTR 0x06 +#define SL811_CTRL_B 0x08 +#define SL811_ADDR_B 0x09 +#define SL811_LEN_B 0x0A +#define SL811_STS_B 0x0B /* read */ +#define SL811_PIDEP_B 0x0B /* write */ +#define SL811_CNT_B 0x0C /* read */ +#define SL811_DEV_B 0x0C /* write */ +#define SL811_INTRSTS 0x0D /* write clears bitwise */ +#define SL811_HWREV 0x0E /* read */ +#define SL811_SOFLOW 0x0E /* write */ +#define SL811_SOFCNTDIV 0x0F /* read */ +#define SL811_CTRL2 0x0F /* write */ + +/* USB control register bits (addr 0x00 and addr 0x08) */ +#define SL811_USB_CTRL_ARM 0x01 +#define SL811_USB_CTRL_ENABLE 0x02 +#define SL811_USB_CTRL_DIR_OUT 0x04 +#define SL811_USB_CTRL_ISO 0x10 +#define SL811_USB_CTRL_SOF 0x20 +#define SL811_USB_CTRL_TOGGLE_1 0x40 +#define SL811_USB_CTRL_PREAMBLE 0x80 + +/* USB status register bits (addr 0x03 and addr 0x0B) */ +#define SL811_USB_STS_ACK 0x01 +#define SL811_USB_STS_ERROR 0x02 +#define SL811_USB_STS_TIMEOUT 0x04 +#define SL811_USB_STS_TOGGLE_1 0x08 +#define SL811_USB_STS_SETUP 0x10 +#define SL811_USB_STS_OVERFLOW 0x20 +#define SL811_USB_STS_NAK 0x40 +#define SL811_USB_STS_STALL 0x80 + +/* Control register 1 bits (addr 0x05) */ +#define SL811_CTRL1_SOF 0x01 +#define SL811_CTRL1_RESET 0x08 +#define SL811_CTRL1_JKSTATE 0x10 +#define SL811_CTRL1_SPEED_LOW 0x20 +#define SL811_CTRL1_SUSPEND 0x40 + +/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */ +#define SL811_INTR_DONE_A 0x01 +#define SL811_INTR_DONE_B 0x02 +#define SL811_INTR_SOF 0x10 +#define SL811_INTR_INSRMV 0x20 +#define SL811_INTR_DETECT 0x40 +#define SL811_INTR_NOTPRESENT 0x40 +#define SL811_INTR_SPEED_FULL 0x80 /* only in status reg */ + +/* HW rev and SOF lo register bits (addr 0x0E) */ +#define SL811_HWR_HWREV 0xF0 + +/* SOF counter and control reg 2 (addr 0x0F) */ +#define SL811_CTL2_SOFHI 0x3F +#define SL811_CTL2_DSWAP 0x40 +#define SL811_CTL2_HOST 0x80 + +/* Set up for 1-ms SOF time. */ +#define SL811_12M_LOW 0xE0 +#define SL811_12M_HI 0x2E + +#define SL811_DATA_START 0x10 +#define SL811_DATA_LIMIT 240 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 + + +#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep)) + +#endif /* __UBOOT_SL811_H */ diff --git a/qemu/roms/u-boot/drivers/usb/host/utmi-armada100.c b/qemu/roms/u-boot/drivers/usb/host/utmi-armada100.c new file mode 100644 index 000000000..1e878280f --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/utmi-armada100.c @@ -0,0 +1,80 @@ +/* + * (C) Copyright 2012 + * eInfochips Ltd. + * Written-by: Ajay Bhargav + * + * (C) Copyright 2009 + * Marvell Semiconductor + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +static int utmi_phy_init(void) +{ + struct armd1usb_phy_reg *phy_regs = + (struct armd1usb_phy_reg *)UTMI_PHY_BASE; + int timeout; + + setbits_le32(&phy_regs->utmi_ctrl, INPKT_DELAY_SOF | PLL_PWR_UP); + udelay(1000); + setbits_le32(&phy_regs->utmi_ctrl, PHY_PWR_UP); + + clrbits_le32(&phy_regs->utmi_pll, PLL_FBDIV_MASK | PLL_REFDIV_MASK); + setbits_le32(&phy_regs->utmi_pll, N_DIVIDER << PLL_FBDIV | M_DIVIDER); + + setbits_le32(&phy_regs->utmi_tx, PHSEL_VAL << CK60_PHSEL); + + /* Calibrate pll */ + timeout = 10000; + while (--timeout && ((readl(&phy_regs->utmi_pll) & PLL_READY) == 0)) + ; + if (!timeout) + return -1; + + udelay(200); + setbits_le32(&phy_regs->utmi_pll, VCOCAL_START); + udelay(400); + clrbits_le32(&phy_regs->utmi_pll, VCOCAL_START); + + udelay(200); + setbits_le32(&phy_regs->utmi_tx, RCAL_START); + udelay(400); + clrbits_le32(&phy_regs->utmi_tx, RCAL_START); + + timeout = 10000; + while (--timeout && ((readl(&phy_regs->utmi_pll) & PLL_READY) == 0)) + ; + if (!timeout) + return -1; + + return 0; +} + +/* + * Initialize USB host controller's UTMI Physical interface + */ +int utmi_init(void) +{ + struct armd1mpmu_registers *mpmu_regs = + (struct armd1mpmu_registers *)ARMD1_MPMU_BASE; + + struct armd1apmu_registers *apmu_regs = + (struct armd1apmu_registers *)ARMD1_APMU_BASE; + + /* Turn on 26Mhz ref clock for UTMI PLL */ + setbits_le32(&mpmu_regs->acgr, APB2_26M_EN | AP_26M); + + /* USB Clock reset */ + writel(USB_SPH_AXICLK_EN, &apmu_regs->usbcrc); + writel(USB_SPH_AXICLK_EN | USB_SPH_AXI_RST, &apmu_regs->usbcrc); + + /* Initialize UTMI transceiver */ + return utmi_phy_init(); +} diff --git a/qemu/roms/u-boot/drivers/usb/host/xhci-exynos5.c b/qemu/roms/u-boot/drivers/usb/host/xhci-exynos5.c new file mode 100644 index 000000000..b4946a3f1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/xhci-exynos5.c @@ -0,0 +1,328 @@ +/* + * SAMSUNG EXYNOS5 USB HOST XHCI Controller + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Vivek Gautam + * Vikas Sajjan + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * This file is a conglomeration for DWC3-init sequence and further + * exynos5 specific PHY-init sequence. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xhci.h" + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +/** + * Contains pointers to register base addresses + * for the usb controller. + */ +struct exynos_xhci { + struct exynos_usb3_phy *usb3_phy; + struct xhci_hccr *hcd; + struct dwc3 *dwc3_reg; + struct fdt_gpio_state vbus_gpio; +}; + +static struct exynos_xhci exynos; + +#ifdef CONFIG_OF_CONTROL +static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos) +{ + fdt_addr_t addr; + unsigned int node; + int depth; + + node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_XHCI); + if (node <= 0) { + debug("XHCI: Can't get device node for xhci\n"); + return -ENODEV; + } + + /* + * Get the base address for XHCI controller from the device node + */ + addr = fdtdec_get_addr(blob, node, "reg"); + if (addr == FDT_ADDR_T_NONE) { + debug("Can't get the XHCI register base address\n"); + return -ENXIO; + } + exynos->hcd = (struct xhci_hccr *)addr; + + /* Vbus gpio */ + fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio); + + depth = 0; + node = fdtdec_next_compatible_subnode(blob, node, + COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth); + if (node <= 0) { + debug("XHCI: Can't get device node for usb3-phy controller\n"); + return -ENODEV; + } + + /* + * Get the base address for usbphy from the device node + */ + exynos->usb3_phy = (struct exynos_usb3_phy *)fdtdec_get_addr(blob, node, + "reg"); + if (exynos->usb3_phy == NULL) { + debug("Can't get the usbphy register address\n"); + return -ENXIO; + } + + return 0; +} +#endif + +static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy) +{ + u32 reg; + + /* enabling usb_drd phy */ + set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN); + + /* Reset USB 3.0 PHY */ + writel(0x0, &phy->phy_reg0); + + clrbits_le32(&phy->phy_param0, + /* Select PHY CLK source */ + PHYPARAM0_REF_USE_PAD | + /* Set Loss-of-Signal Detector sensitivity */ + PHYPARAM0_REF_LOSLEVEL_MASK); + setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL); + + writel(0x0, &phy->phy_resume); + + /* + * Setting the Frame length Adj value[6:1] to default 0x20 + * See xHCI 1.0 spec, 5.2.4 + */ + setbits_le32(&phy->link_system, + LINKSYSTEM_XHCI_VERSION_CONTROL | + LINKSYSTEM_FLADJ(0x20)); + + /* Set Tx De-Emphasis level */ + clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK); + setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH); + + setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL); + + /* PHYTEST POWERDOWN Control */ + clrbits_le32(&phy->phy_test, + PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP); + + /* UTMI Power Control */ + writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi); + + /* Use core clock from main PLL */ + reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | + /* Default 24Mhz crystal clock */ + PHYCLKRST_FSEL(FSEL_CLKSEL_24M) | + PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88) | + /* Force PortReset of PHY */ + PHYCLKRST_PORTRESET | + /* Digital power supply in normal operating mode */ + PHYCLKRST_RETENABLEN | + /* Enable ref clock for SS function */ + PHYCLKRST_REF_SSP_EN | + /* Enable spread spectrum */ + PHYCLKRST_SSC_EN | + /* Power down HS Bias and PLL blocks in suspend mode */ + PHYCLKRST_COMMONONN; + + writel(reg, &phy->phy_clk_rst); + + /* giving time to Phy clock to settle before resetting */ + udelay(10); + + reg &= ~PHYCLKRST_PORTRESET; + writel(reg, &phy->phy_clk_rst); +} + +static void exynos5_usb3_phy_exit(struct exynos_usb3_phy *phy) +{ + setbits_le32(&phy->phy_utmi, + PHYUTMI_OTGDISABLE | + PHYUTMI_FORCESUSPEND | + PHYUTMI_FORCESLEEP); + + clrbits_le32(&phy->phy_clk_rst, + PHYCLKRST_REF_SSP_EN | + PHYCLKRST_SSC_EN | + PHYCLKRST_COMMONONN); + + /* PHYTEST POWERDOWN Control to remove leakage current */ + setbits_le32(&phy->phy_test, + PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP); + + /* disabling usb_drd phy */ + set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_DISABLE); +} + +void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) +{ + clrsetbits_le32(&dwc3_reg->g_ctl, + DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG), + DWC3_GCTL_PRTCAPDIR(mode)); +} + +static void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) +{ + /* Before Resetting PHY, put Core in Reset */ + setbits_le32(&dwc3_reg->g_ctl, + DWC3_GCTL_CORESOFTRESET); + + /* Assert USB3 PHY reset */ + setbits_le32(&dwc3_reg->g_usb3pipectl[0], + DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Assert USB2 PHY reset */ + setbits_le32(&dwc3_reg->g_usb2phycfg, + DWC3_GUSB2PHYCFG_PHYSOFTRST); + + mdelay(100); + + /* Clear USB3 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb3pipectl[0], + DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Clear USB2 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb2phycfg, + DWC3_GUSB2PHYCFG_PHYSOFTRST); + + /* After PHYs are stable we can take Core out of reset state */ + clrbits_le32(&dwc3_reg->g_ctl, + DWC3_GCTL_CORESOFTRESET); +} + +static int dwc3_core_init(struct dwc3 *dwc3_reg) +{ + u32 reg; + u32 revision; + unsigned int dwc3_hwparams1; + + revision = readl(&dwc3_reg->g_snpsid); + /* This should read as U3 followed by revision number */ + if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) { + puts("this is not a DesignWare USB3 DRD Core\n"); + return -EINVAL; + } + + dwc3_core_soft_reset(dwc3_reg); + + dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1); + + reg = readl(&dwc3_reg->g_ctl); + reg &= ~DWC3_GCTL_SCALEDOWN_MASK; + reg &= ~DWC3_GCTL_DISSCRAMBLE; + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + default: + debug("No power optimization available\n"); + } + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * where the device can fail to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter a Connect/Disconnect loop + */ + if ((revision & DWC3_REVISION_MASK) < 0x190a) + reg |= DWC3_GCTL_U2RSTECN; + + writel(reg, &dwc3_reg->g_ctl); + + return 0; +} + +static int exynos_xhci_core_init(struct exynos_xhci *exynos) +{ + int ret; + + exynos5_usb3_phy_init(exynos->usb3_phy); + + ret = dwc3_core_init(exynos->dwc3_reg); + if (ret) { + debug("failed to initialize core\n"); + return -EINVAL; + } + + /* We are hard-coding DWC3 core to Host Mode */ + dwc3_set_mode(exynos->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); + + return 0; +} + +static void exynos_xhci_core_exit(struct exynos_xhci *exynos) +{ + exynos5_usb3_phy_exit(exynos->usb3_phy); +} + +int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) +{ + struct exynos_xhci *ctx = &exynos; + int ret; + +#ifdef CONFIG_OF_CONTROL + exynos_usb3_parse_dt(gd->fdt_blob, ctx); +#else + ctx->usb3_phy = (struct exynos_usb3_phy *)samsung_get_base_usb3_phy(); + ctx->hcd = (struct xhci_hccr *)samsung_get_base_usb_xhci(); +#endif + + ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); + +#ifdef CONFIG_OF_CONTROL + /* setup the Vbus gpio here */ + if (fdt_gpio_isvalid(&ctx->vbus_gpio) && + !fdtdec_setup_gpio(&ctx->vbus_gpio)) + gpio_direction_output(ctx->vbus_gpio.gpio, 1); +#endif + + ret = exynos_xhci_core_init(ctx); + if (ret) { + puts("XHCI: failed to initialize controller\n"); + return -EINVAL; + } + + *hccr = (ctx->hcd); + *hcor = (struct xhci_hcor *)((uint32_t) *hccr + + HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); + + debug("Exynos5-xhci: init hccr %x and hcor %x hc_length %d\n", + (uint32_t)*hccr, (uint32_t)*hcor, + (uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); + + return 0; +} + +void xhci_hcd_stop(int index) +{ + struct exynos_xhci *ctx = &exynos; + + exynos_xhci_core_exit(ctx); +} diff --git a/qemu/roms/u-boot/drivers/usb/host/xhci-mem.c b/qemu/roms/u-boot/drivers/usb/host/xhci-mem.c new file mode 100644 index 000000000..89908e8a8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/xhci-mem.c @@ -0,0 +1,720 @@ +/* + * USB HOST XHCI Controller stack + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam + * Vikas Sajjan + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include "xhci.h" + +#define CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE +/** + * flushes the address passed till the length + * + * @param addr pointer to memory region to be flushed + * @param len the length of the cache line to be flushed + * @return none + */ +void xhci_flush_cache(uint32_t addr, u32 len) +{ + BUG_ON((void *)addr == NULL || len == 0); + + flush_dcache_range(addr & ~(CACHELINE_SIZE - 1), + ALIGN(addr + len, CACHELINE_SIZE)); +} + +/** + * invalidates the address passed till the length + * + * @param addr pointer to memory region to be invalidates + * @param len the length of the cache line to be invalidated + * @return none + */ +void xhci_inval_cache(uint32_t addr, u32 len) +{ + BUG_ON((void *)addr == NULL || len == 0); + + invalidate_dcache_range(addr & ~(CACHELINE_SIZE - 1), + ALIGN(addr + len, CACHELINE_SIZE)); +} + + +/** + * frees the "segment" pointer passed + * + * @param ptr pointer to "segement" to be freed + * @return none + */ +static void xhci_segment_free(struct xhci_segment *seg) +{ + free(seg->trbs); + seg->trbs = NULL; + + free(seg); +} + +/** + * frees the "ring" pointer passed + * + * @param ptr pointer to "ring" to be freed + * @return none + */ +static void xhci_ring_free(struct xhci_ring *ring) +{ + struct xhci_segment *seg; + struct xhci_segment *first_seg; + + BUG_ON(!ring); + + first_seg = ring->first_seg; + seg = first_seg->next; + while (seg != first_seg) { + struct xhci_segment *next = seg->next; + xhci_segment_free(seg); + seg = next; + } + xhci_segment_free(first_seg); + + free(ring); +} + +/** + * frees the "xhci_container_ctx" pointer passed + * + * @param ptr pointer to "xhci_container_ctx" to be freed + * @return none + */ +static void xhci_free_container_ctx(struct xhci_container_ctx *ctx) +{ + free(ctx->bytes); + free(ctx); +} + +/** + * frees the virtual devices for "xhci_ctrl" pointer passed + * + * @param ptr pointer to "xhci_ctrl" whose virtual devices are to be freed + * @return none + */ +static void xhci_free_virt_devices(struct xhci_ctrl *ctrl) +{ + int i; + int slot_id; + struct xhci_virt_device *virt_dev; + + /* + * refactored here to loop through all virt_dev + * Slot ID 0 is reserved + */ + for (slot_id = 0; slot_id < MAX_HC_SLOTS; slot_id++) { + virt_dev = ctrl->devs[slot_id]; + if (!virt_dev) + continue; + + ctrl->dcbaa->dev_context_ptrs[slot_id] = 0; + + for (i = 0; i < 31; ++i) + if (virt_dev->eps[i].ring) + xhci_ring_free(virt_dev->eps[i].ring); + + if (virt_dev->in_ctx) + xhci_free_container_ctx(virt_dev->in_ctx); + if (virt_dev->out_ctx) + xhci_free_container_ctx(virt_dev->out_ctx); + + free(virt_dev); + /* make sure we are pointing to NULL */ + ctrl->devs[slot_id] = NULL; + } +} + +/** + * frees all the memory allocated + * + * @param ptr pointer to "xhci_ctrl" to be cleaned up + * @return none + */ +void xhci_cleanup(struct xhci_ctrl *ctrl) +{ + xhci_ring_free(ctrl->event_ring); + xhci_ring_free(ctrl->cmd_ring); + xhci_free_virt_devices(ctrl); + free(ctrl->erst.entries); + free(ctrl->dcbaa); + memset(ctrl, '\0', sizeof(struct xhci_ctrl)); +} + +/** + * Malloc the aligned memory + * + * @param size size of memory to be allocated + * @return allocates the memory and returns the aligned pointer + */ +static void *xhci_malloc(unsigned int size) +{ + void *ptr; + size_t cacheline_size = max(XHCI_ALIGNMENT, CACHELINE_SIZE); + + ptr = memalign(cacheline_size, ALIGN(size, cacheline_size)); + BUG_ON(!ptr); + memset(ptr, '\0', size); + + xhci_flush_cache((uint32_t)ptr, size); + + return ptr; +} + +/** + * Make the prev segment point to the next segment. + * Change the last TRB in the prev segment to be a Link TRB which points to the + * address of the next segment. The caller needs to set any Link TRB + * related flags, such as End TRB, Toggle Cycle, and no snoop. + * + * @param prev pointer to the previous segment + * @param next pointer to the next segment + * @param link_trbs flag to indicate whether to link the trbs or NOT + * @return none + */ +static void xhci_link_segments(struct xhci_segment *prev, + struct xhci_segment *next, bool link_trbs) +{ + u32 val; + u64 val_64 = 0; + + if (!prev || !next) + return; + prev->next = next; + if (link_trbs) { + val_64 = (uintptr_t)next->trbs; + prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = val_64; + + /* + * Set the last TRB in the segment to + * have a TRB type ID of Link TRB + */ + val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); + val &= ~TRB_TYPE_BITMASK; + val |= (TRB_LINK << TRB_TYPE_SHIFT); + + prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); + } +} + +/** + * Initialises the Ring's enqueue,dequeue,enq_seg pointers + * + * @param ring pointer to the RING to be intialised + * @return none + */ +static void xhci_initialize_ring_info(struct xhci_ring *ring) +{ + /* + * The ring is empty, so the enqueue pointer == dequeue pointer + */ + ring->enqueue = ring->first_seg->trbs; + ring->enq_seg = ring->first_seg; + ring->dequeue = ring->enqueue; + ring->deq_seg = ring->first_seg; + + /* + * The ring is initialized to 0. The producer must write 1 to the + * cycle bit to handover ownership of the TRB, so PCS = 1. + * The consumer must compare CCS to the cycle bit to + * check ownership, so CCS = 1. + */ + ring->cycle_state = 1; +} + +/** + * Allocates a generic ring segment from the ring pool, sets the dma address, + * initializes the segment to zero, and sets the private next pointer to NULL. + * Section 4.11.1.1: + * "All components of all Command and Transfer TRBs shall be initialized to '0'" + * + * @param none + * @return pointer to the newly allocated SEGMENT + */ +static struct xhci_segment *xhci_segment_alloc(void) +{ + struct xhci_segment *seg; + + seg = (struct xhci_segment *)malloc(sizeof(struct xhci_segment)); + BUG_ON(!seg); + + seg->trbs = (union xhci_trb *)xhci_malloc(SEGMENT_SIZE); + + seg->next = NULL; + + return seg; +} + +/** + * Create a new ring with zero or more segments. + * TODO: current code only uses one-time-allocated single-segment rings + * of 1KB anyway, so we might as well get rid of all the segment and + * linking code (and maybe increase the size a bit, e.g. 4KB). + * + * + * Link each segment together into a ring. + * Set the end flag and the cycle toggle bit on the last segment. + * See section 4.9.2 and figures 15 and 16 of XHCI spec rev1.0. + * + * @param num_segs number of segments in the ring + * @param link_trbs flag to indicate whether to link the trbs or NOT + * @return pointer to the newly created RING + */ +struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs) +{ + struct xhci_ring *ring; + struct xhci_segment *prev; + + ring = (struct xhci_ring *)malloc(sizeof(struct xhci_ring)); + BUG_ON(!ring); + + if (num_segs == 0) + return ring; + + ring->first_seg = xhci_segment_alloc(); + BUG_ON(!ring->first_seg); + + num_segs--; + + prev = ring->first_seg; + while (num_segs > 0) { + struct xhci_segment *next; + + next = xhci_segment_alloc(); + BUG_ON(!next); + + xhci_link_segments(prev, next, link_trbs); + + prev = next; + num_segs--; + } + xhci_link_segments(prev, ring->first_seg, link_trbs); + if (link_trbs) { + /* See section 4.9.2.1 and 6.4.4.1 */ + prev->trbs[TRBS_PER_SEGMENT-1].link.control |= + cpu_to_le32(LINK_TOGGLE); + } + xhci_initialize_ring_info(ring); + + return ring; +} + +/** + * Allocates the Container context + * + * @param ctrl Host controller data structure + * @param type type of XHCI Container Context + * @return NULL if failed else pointer to the context on success + */ +static struct xhci_container_ctx + *xhci_alloc_container_ctx(struct xhci_ctrl *ctrl, int type) +{ + struct xhci_container_ctx *ctx; + + ctx = (struct xhci_container_ctx *) + malloc(sizeof(struct xhci_container_ctx)); + BUG_ON(!ctx); + + BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT)); + ctx->type = type; + ctx->size = (MAX_EP_CTX_NUM + 1) * + CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)); + if (type == XHCI_CTX_TYPE_INPUT) + ctx->size += CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)); + + ctx->bytes = (u8 *)xhci_malloc(ctx->size); + + return ctx; +} + +/** + * Allocating virtual device + * + * @param udev pointer to USB deivce structure + * @return 0 on success else -1 on failure + */ +int xhci_alloc_virt_device(struct usb_device *udev) +{ + u64 byte_64 = 0; + unsigned int slot_id = udev->slot_id; + struct xhci_virt_device *virt_dev; + struct xhci_ctrl *ctrl = udev->controller; + + /* Slot ID 0 is reserved */ + if (ctrl->devs[slot_id]) { + printf("Virt dev for slot[%d] already allocated\n", slot_id); + return -EEXIST; + } + + ctrl->devs[slot_id] = (struct xhci_virt_device *) + malloc(sizeof(struct xhci_virt_device)); + + if (!ctrl->devs[slot_id]) { + puts("Failed to allocate virtual device\n"); + return -ENOMEM; + } + + memset(ctrl->devs[slot_id], 0, sizeof(struct xhci_virt_device)); + virt_dev = ctrl->devs[slot_id]; + + /* Allocate the (output) device context that will be used in the HC. */ + virt_dev->out_ctx = xhci_alloc_container_ctx(ctrl, + XHCI_CTX_TYPE_DEVICE); + if (!virt_dev->out_ctx) { + puts("Failed to allocate out context for virt dev\n"); + return -ENOMEM; + } + + /* Allocate the (input) device context for address device command */ + virt_dev->in_ctx = xhci_alloc_container_ctx(ctrl, + XHCI_CTX_TYPE_INPUT); + if (!virt_dev->in_ctx) { + puts("Failed to allocate in context for virt dev\n"); + return -ENOMEM; + } + + /* Allocate endpoint 0 ring */ + virt_dev->eps[0].ring = xhci_ring_alloc(1, true); + + byte_64 = (uintptr_t)(virt_dev->out_ctx->bytes); + + /* Point to output device context in dcbaa. */ + ctrl->dcbaa->dev_context_ptrs[slot_id] = byte_64; + + xhci_flush_cache((uint32_t)&ctrl->dcbaa->dev_context_ptrs[slot_id], + sizeof(__le64)); + return 0; +} + +/** + * Allocates the necessary data structures + * for XHCI host controller + * + * @param ctrl Host controller data structure + * @param hccr pointer to HOST Controller Control Registers + * @param hcor pointer to HOST Controller Operational Registers + * @return 0 if successful else -1 on failure + */ +int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, + struct xhci_hcor *hcor) +{ + uint64_t val_64; + uint64_t trb_64; + uint32_t val; + unsigned long deq; + int i; + struct xhci_segment *seg; + + /* DCBAA initialization */ + ctrl->dcbaa = (struct xhci_device_context_array *) + xhci_malloc(sizeof(struct xhci_device_context_array)); + if (ctrl->dcbaa == NULL) { + puts("unable to allocate DCBA\n"); + return -ENOMEM; + } + + val_64 = (uintptr_t)ctrl->dcbaa; + /* Set the pointer in DCBAA register */ + xhci_writeq(&hcor->or_dcbaap, val_64); + + /* Command ring control pointer register initialization */ + ctrl->cmd_ring = xhci_ring_alloc(1, true); + + /* Set the address in the Command Ring Control register */ + trb_64 = (uintptr_t)ctrl->cmd_ring->first_seg->trbs; + val_64 = xhci_readq(&hcor->or_crcr); + val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) | + (trb_64 & (u64) ~CMD_RING_RSVD_BITS) | + ctrl->cmd_ring->cycle_state; + xhci_writeq(&hcor->or_crcr, val_64); + + /* write the address of db register */ + val = xhci_readl(&hccr->cr_dboff); + val &= DBOFF_MASK; + ctrl->dba = (struct xhci_doorbell_array *)((char *)hccr + val); + + /* write the address of runtime register */ + val = xhci_readl(&hccr->cr_rtsoff); + val &= RTSOFF_MASK; + ctrl->run_regs = (struct xhci_run_regs *)((char *)hccr + val); + + /* writting the address of ir_set structure */ + ctrl->ir_set = &ctrl->run_regs->ir_set[0]; + + /* Event ring does not maintain link TRB */ + ctrl->event_ring = xhci_ring_alloc(ERST_NUM_SEGS, false); + ctrl->erst.entries = (struct xhci_erst_entry *) + xhci_malloc(sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS); + + ctrl->erst.num_entries = ERST_NUM_SEGS; + + for (val = 0, seg = ctrl->event_ring->first_seg; + val < ERST_NUM_SEGS; + val++) { + trb_64 = 0; + trb_64 = (uintptr_t)seg->trbs; + struct xhci_erst_entry *entry = &ctrl->erst.entries[val]; + xhci_writeq(&entry->seg_addr, trb_64); + entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); + entry->rsvd = 0; + seg = seg->next; + } + xhci_flush_cache((uint32_t)ctrl->erst.entries, + ERST_NUM_SEGS * sizeof(struct xhci_erst_entry)); + + deq = (unsigned long)ctrl->event_ring->dequeue; + + /* Update HC event ring dequeue pointer */ + xhci_writeq(&ctrl->ir_set->erst_dequeue, + (u64)deq & (u64)~ERST_PTR_MASK); + + /* set ERST count with the number of entries in the segment table */ + val = xhci_readl(&ctrl->ir_set->erst_size); + val &= ERST_SIZE_MASK; + val |= ERST_NUM_SEGS; + xhci_writel(&ctrl->ir_set->erst_size, val); + + /* this is the event ring segment table pointer */ + val_64 = xhci_readq(&ctrl->ir_set->erst_base); + val_64 &= ERST_PTR_MASK; + val_64 |= ((u32)(ctrl->erst.entries) & ~ERST_PTR_MASK); + + xhci_writeq(&ctrl->ir_set->erst_base, val_64); + + /* initializing the virtual devices to NULL */ + for (i = 0; i < MAX_HC_SLOTS; ++i) + ctrl->devs[i] = NULL; + + /* + * Just Zero'ing this register completely, + * or some spurious Device Notification Events + * might screw things here. + */ + xhci_writel(&hcor->or_dnctrl, 0x0); + + return 0; +} + +/** + * Give the input control context for the passed container context + * + * @param ctx pointer to the context + * @return pointer to the Input control context data + */ +struct xhci_input_control_ctx + *xhci_get_input_control_ctx(struct xhci_container_ctx *ctx) +{ + BUG_ON(ctx->type != XHCI_CTX_TYPE_INPUT); + return (struct xhci_input_control_ctx *)ctx->bytes; +} + +/** + * Give the slot context for the passed container context + * + * @param ctrl Host controller data structure + * @param ctx pointer to the context + * @return pointer to the slot control context data + */ +struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *ctx) +{ + if (ctx->type == XHCI_CTX_TYPE_DEVICE) + return (struct xhci_slot_ctx *)ctx->bytes; + + return (struct xhci_slot_ctx *) + (ctx->bytes + CTX_SIZE(readl(&ctrl->hccr->cr_hccparams))); +} + +/** + * Gets the EP context from based on the ep_index + * + * @param ctrl Host controller data structure + * @param ctx context container + * @param ep_index index of the endpoint + * @return pointer to the End point context + */ +struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *ctx, + unsigned int ep_index) +{ + /* increment ep index by offset of start of ep ctx array */ + ep_index++; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + ep_index++; + + return (struct xhci_ep_ctx *) + (ctx->bytes + + (ep_index * CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)))); +} + +/** + * Copy output xhci_ep_ctx to the input xhci_ep_ctx copy. + * Useful when you want to change one particular aspect of the endpoint + * and then issue a configure endpoint command. + * + * @param ctrl Host controller data structure + * @param in_ctx contains the input context + * @param out_ctx contains the input context + * @param ep_index index of the end point + * @return none + */ +void xhci_endpoint_copy(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx, + unsigned int ep_index) +{ + struct xhci_ep_ctx *out_ep_ctx; + struct xhci_ep_ctx *in_ep_ctx; + + out_ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); + in_ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); + + in_ep_ctx->ep_info = out_ep_ctx->ep_info; + in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2; + in_ep_ctx->deq = out_ep_ctx->deq; + in_ep_ctx->tx_info = out_ep_ctx->tx_info; +} + +/** + * Copy output xhci_slot_ctx to the input xhci_slot_ctx. + * Useful when you want to change one particular aspect of the endpoint + * and then issue a configure endpoint command. + * Only the context entries field matters, but + * we'll copy the whole thing anyway. + * + * @param ctrl Host controller data structure + * @param in_ctx contains the inpout context + * @param out_ctx contains the inpout context + * @return none + */ +void xhci_slot_copy(struct xhci_ctrl *ctrl, struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx) +{ + struct xhci_slot_ctx *in_slot_ctx; + struct xhci_slot_ctx *out_slot_ctx; + + in_slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx); + out_slot_ctx = xhci_get_slot_ctx(ctrl, out_ctx); + + in_slot_ctx->dev_info = out_slot_ctx->dev_info; + in_slot_ctx->dev_info2 = out_slot_ctx->dev_info2; + in_slot_ctx->tt_info = out_slot_ctx->tt_info; + in_slot_ctx->dev_state = out_slot_ctx->dev_state; +} + +/** + * Setup an xHCI virtual device for a Set Address command + * + * @param udev pointer to the Device Data Structure + * @return returns negative value on failure else 0 on success + */ +void xhci_setup_addressable_virt_dev(struct usb_device *udev) +{ + struct usb_device *hop = udev; + struct xhci_virt_device *virt_dev; + struct xhci_ep_ctx *ep0_ctx; + struct xhci_slot_ctx *slot_ctx; + u32 port_num = 0; + u64 trb_64 = 0; + struct xhci_ctrl *ctrl = udev->controller; + + virt_dev = ctrl->devs[udev->slot_id]; + + BUG_ON(!virt_dev); + + /* Extract the EP0 and Slot Ctrl */ + ep0_ctx = xhci_get_ep_ctx(ctrl, virt_dev->in_ctx, 0); + slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->in_ctx); + + /* Only the control endpoint is valid - one endpoint context */ + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | 0); + + switch (udev->speed) { + case USB_SPEED_SUPER: + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS); + break; + case USB_SPEED_HIGH: + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS); + break; + case USB_SPEED_FULL: + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS); + break; + case USB_SPEED_LOW: + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS); + break; + default: + /* Speed was set earlier, this shouldn't happen. */ + BUG(); + } + + /* Extract the root hub port number */ + if (hop->parent) + while (hop->parent->parent) + hop = hop->parent; + port_num = hop->portnr; + debug("port_num = %d\n", port_num); + + slot_ctx->dev_info2 |= + cpu_to_le32(((port_num & ROOT_HUB_PORT_MASK) << + ROOT_HUB_PORT_SHIFT)); + + /* Step 4 - ring already allocated */ + /* Step 5 */ + ep0_ctx->ep_info2 = cpu_to_le32(CTRL_EP << EP_TYPE_SHIFT); + debug("SPEED = %d\n", udev->speed); + + switch (udev->speed) { + case USB_SPEED_SUPER: + ep0_ctx->ep_info2 |= cpu_to_le32(((512 & MAX_PACKET_MASK) << + MAX_PACKET_SHIFT)); + debug("Setting Packet size = 512bytes\n"); + break; + case USB_SPEED_HIGH: + /* USB core guesses at a 64-byte max packet first for FS devices */ + case USB_SPEED_FULL: + ep0_ctx->ep_info2 |= cpu_to_le32(((64 & MAX_PACKET_MASK) << + MAX_PACKET_SHIFT)); + debug("Setting Packet size = 64bytes\n"); + break; + case USB_SPEED_LOW: + ep0_ctx->ep_info2 |= cpu_to_le32(((8 & MAX_PACKET_MASK) << + MAX_PACKET_SHIFT)); + debug("Setting Packet size = 8bytes\n"); + break; + default: + /* New speed? */ + BUG(); + } + + /* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */ + ep0_ctx->ep_info2 |= + cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) | + ((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT)); + + trb_64 = (uintptr_t)virt_dev->eps[0].ring->first_seg->trbs; + ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state); + + /* Steps 7 and 8 were done in xhci_alloc_virt_device() */ + + xhci_flush_cache((uint32_t)ep0_ctx, sizeof(struct xhci_ep_ctx)); + xhci_flush_cache((uint32_t)slot_ctx, sizeof(struct xhci_slot_ctx)); +} diff --git a/qemu/roms/u-boot/drivers/usb/host/xhci-omap.c b/qemu/roms/u-boot/drivers/usb/host/xhci-omap.c new file mode 100644 index 000000000..e667810bb --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/xhci-omap.c @@ -0,0 +1,158 @@ +/* + * OMAP USB HOST xHCI Controller + * + * (C) Copyright 2013 + * Texas Instruments, + * + * Author: Dan Murphy + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "xhci.h" + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +static struct omap_xhci omap; + +inline int __board_usb_init(int index, enum usb_init_type init) +{ + return 0; +} +int board_usb_init(int index, enum usb_init_type init) + __attribute__((weak, alias("__board_usb_init"))); + +static void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) +{ + clrsetbits_le32(&dwc3_reg->g_ctl, + DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG), + DWC3_GCTL_PRTCAPDIR(mode)); +} + +static void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) +{ + /* Before Resetting PHY, put Core in Reset */ + setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); + + omap_reset_usb_phy(dwc3_reg); + + /* After PHYs are stable we can take Core out of reset state */ + clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); +} + +static int dwc3_core_init(struct dwc3 *dwc3_reg) +{ + u32 reg; + u32 revision; + unsigned int dwc3_hwparams1; + + revision = readl(&dwc3_reg->g_snpsid); + /* This should read as U3 followed by revision number */ + if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) { + puts("this is not a DesignWare USB3 DRD Core\n"); + return -1; + } + + dwc3_core_soft_reset(dwc3_reg); + + dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1); + + reg = readl(&dwc3_reg->g_ctl); + reg &= ~DWC3_GCTL_SCALEDOWN_MASK; + reg &= ~DWC3_GCTL_DISSCRAMBLE; + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + default: + debug("No power optimization available\n"); + } + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * where the device can fail to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter a Connect/Disconnect loop + */ + if ((revision & DWC3_REVISION_MASK) < 0x190a) + reg |= DWC3_GCTL_U2RSTECN; + + writel(reg, &dwc3_reg->g_ctl); + + return 0; +} + +static int omap_xhci_core_init(struct omap_xhci *omap) +{ + int ret = 0; + + omap_enable_phy(omap); + + ret = dwc3_core_init(omap->dwc3_reg); + if (ret) { + debug("%s:failed to initialize core\n", __func__); + return ret; + } + + /* We are hard-coding DWC3 core to Host Mode */ + dwc3_set_mode(omap->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); + + return ret; +} + +static void omap_xhci_core_exit(struct omap_xhci *omap) +{ + usb_phy_power(0); +} + +int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) +{ + struct omap_xhci *ctx = &omap; + int ret = 0; + + ctx->hcd = (struct xhci_hccr *)OMAP_XHCI_BASE; + ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); + ctx->usb3_phy = (struct omap_usb3_phy *)OMAP_OCP1_SCP_BASE; + ctx->otg_wrapper = (struct omap_dwc_wrapper *)OMAP_OTG_WRAPPER_BASE; + + ret = board_usb_init(index, USB_INIT_HOST); + if (ret != 0) { + puts("Failed to initialize board for USB\n"); + return ret; + } + + ret = omap_xhci_core_init(ctx); + if (ret < 0) { + puts("Failed to initialize xhci\n"); + return ret; + } + + *hccr = (struct xhci_hccr *)(OMAP_XHCI_BASE); + *hcor = (struct xhci_hcor *)((uint32_t) *hccr + + HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); + + debug("omap-xhci: init hccr %x and hcor %x hc_length %d\n", + (uint32_t)*hccr, (uint32_t)*hcor, + (uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); + + return ret; +} + +void xhci_hcd_stop(int index) +{ + struct omap_xhci *ctx = &omap; + + omap_xhci_core_exit(ctx); +} diff --git a/qemu/roms/u-boot/drivers/usb/host/xhci-ring.c b/qemu/roms/u-boot/drivers/usb/host/xhci-ring.c new file mode 100644 index 000000000..19c3ec621 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/xhci-ring.c @@ -0,0 +1,939 @@ +/* + * USB HOST XHCI Controller stack + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam + * Vikas Sajjan + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include "xhci.h" + +/** + * Is this TRB a link TRB or was the last TRB the last TRB in this event ring + * segment? I.e. would the updated event TRB pointer step off the end of the + * event seg ? + * + * @param ctrl Host controller data structure + * @param ring pointer to the ring + * @param seg poniter to the segment to which TRB belongs + * @param trb poniter to the ring trb + * @return 1 if this TRB a link TRB else 0 + */ +static int last_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring, + struct xhci_segment *seg, union xhci_trb *trb) +{ + if (ring == ctrl->event_ring) + return trb == &seg->trbs[TRBS_PER_SEGMENT]; + else + return TRB_TYPE_LINK_LE32(trb->link.control); +} + +/** + * Does this link TRB point to the first segment in a ring, + * or was the previous TRB the last TRB on the last segment in the ERST? + * + * @param ctrl Host controller data structure + * @param ring pointer to the ring + * @param seg poniter to the segment to which TRB belongs + * @param trb poniter to the ring trb + * @return 1 if this TRB is the last TRB on the last segment else 0 + */ +static bool last_trb_on_last_seg(struct xhci_ctrl *ctrl, + struct xhci_ring *ring, + struct xhci_segment *seg, + union xhci_trb *trb) +{ + if (ring == ctrl->event_ring) + return ((trb == &seg->trbs[TRBS_PER_SEGMENT]) && + (seg->next == ring->first_seg)); + else + return le32_to_cpu(trb->link.control) & LINK_TOGGLE; +} + +/** + * See Cycle bit rules. SW is the consumer for the event ring only. + * Don't make a ring full of link TRBs. That would be dumb and this would loop. + * + * If we've just enqueued a TRB that is in the middle of a TD (meaning the + * chain bit is set), then set the chain bit in all the following link TRBs. + * If we've enqueued the last TRB in a TD, make sure the following link TRBs + * have their chain bit cleared (so that each Link TRB is a separate TD). + * + * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit + * set, but other sections talk about dealing with the chain bit set. This was + * fixed in the 0.96 specification errata, but we have to assume that all 0.95 + * xHCI hardware can't handle the chain bit being cleared on a link TRB. + * + * @param ctrl Host controller data structure + * @param ring pointer to the ring + * @param more_trbs_coming flag to indicate whether more trbs + * are expected or NOT. + * Will you enqueue more TRBs before calling + * prepare_ring()? + * @return none + */ +static void inc_enq(struct xhci_ctrl *ctrl, struct xhci_ring *ring, + bool more_trbs_coming) +{ + u32 chain; + union xhci_trb *next; + + chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; + next = ++(ring->enqueue); + + /* + * Update the dequeue pointer further if that was a link TRB or we're at + * the end of an event ring segment (which doesn't have link TRBS) + */ + while (last_trb(ctrl, ring, ring->enq_seg, next)) { + if (ring != ctrl->event_ring) { + /* + * If the caller doesn't plan on enqueueing more + * TDs before ringing the doorbell, then we + * don't want to give the link TRB to the + * hardware just yet. We'll give the link TRB + * back in prepare_ring() just before we enqueue + * the TD at the top of the ring. + */ + if (!chain && !more_trbs_coming) + break; + + /* + * If we're not dealing with 0.95 hardware or + * isoc rings on AMD 0.96 host, + * carry over the chain bit of the previous TRB + * (which may mean the chain bit is cleared). + */ + next->link.control &= cpu_to_le32(~TRB_CHAIN); + next->link.control |= cpu_to_le32(chain); + + next->link.control ^= cpu_to_le32(TRB_CYCLE); + xhci_flush_cache((uint32_t)next, + sizeof(union xhci_trb)); + } + /* Toggle the cycle bit after the last ring segment. */ + if (last_trb_on_last_seg(ctrl, ring, + ring->enq_seg, next)) + ring->cycle_state = (ring->cycle_state ? 0 : 1); + + ring->enq_seg = ring->enq_seg->next; + ring->enqueue = ring->enq_seg->trbs; + next = ring->enqueue; + } +} + +/** + * See Cycle bit rules. SW is the consumer for the event ring only. + * Don't make a ring full of link TRBs. That would be dumb and this would loop. + * + * @param ctrl Host controller data structure + * @param ring Ring whose Dequeue TRB pointer needs to be incremented. + * return none + */ +static void inc_deq(struct xhci_ctrl *ctrl, struct xhci_ring *ring) +{ + do { + /* + * Update the dequeue pointer further if that was a link TRB or + * we're at the end of an event ring segment (which doesn't have + * link TRBS) + */ + if (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue)) { + if (ring == ctrl->event_ring && + last_trb_on_last_seg(ctrl, ring, + ring->deq_seg, ring->dequeue)) { + ring->cycle_state = (ring->cycle_state ? 0 : 1); + } + ring->deq_seg = ring->deq_seg->next; + ring->dequeue = ring->deq_seg->trbs; + } else { + ring->dequeue++; + } + } while (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue)); +} + +/** + * Generic function for queueing a TRB on a ring. + * The caller must have checked to make sure there's room on the ring. + * + * @param more_trbs_coming: Will you enqueue more TRBs before calling + * prepare_ring()? + * @param ctrl Host controller data structure + * @param ring pointer to the ring + * @param more_trbs_coming flag to indicate whether more trbs + * @param trb_fields pointer to trb field array containing TRB contents + * @return pointer to the enqueued trb + */ +static struct xhci_generic_trb *queue_trb(struct xhci_ctrl *ctrl, + struct xhci_ring *ring, + bool more_trbs_coming, + unsigned int *trb_fields) +{ + struct xhci_generic_trb *trb; + int i; + + trb = &ring->enqueue->generic; + + for (i = 0; i < 4; i++) + trb->field[i] = cpu_to_le32(trb_fields[i]); + + xhci_flush_cache((uint32_t)trb, sizeof(struct xhci_generic_trb)); + + inc_enq(ctrl, ring, more_trbs_coming); + + return trb; +} + +/** + * Does various checks on the endpoint ring, and makes it ready + * to queue num_trbs. + * + * @param ctrl Host controller data structure + * @param ep_ring pointer to the EP Transfer Ring + * @param ep_state State of the End Point + * @return error code in case of invalid ep_state, 0 on success + */ +static int prepare_ring(struct xhci_ctrl *ctrl, struct xhci_ring *ep_ring, + u32 ep_state) +{ + union xhci_trb *next = ep_ring->enqueue; + + /* Make sure the endpoint has been added to xHC schedule */ + switch (ep_state) { + case EP_STATE_DISABLED: + /* + * USB core changed config/interfaces without notifying us, + * or hardware is reporting the wrong state. + */ + puts("WARN urb submitted to disabled ep\n"); + return -ENOENT; + case EP_STATE_ERROR: + puts("WARN waiting for error on ep to be cleared\n"); + return -EINVAL; + case EP_STATE_HALTED: + puts("WARN halted endpoint, queueing URB anyway.\n"); + case EP_STATE_STOPPED: + case EP_STATE_RUNNING: + debug("EP STATE RUNNING.\n"); + break; + default: + puts("ERROR unknown endpoint state for ep\n"); + return -EINVAL; + } + + while (last_trb(ctrl, ep_ring, ep_ring->enq_seg, next)) { + /* + * If we're not dealing with 0.95 hardware or isoc rings + * on AMD 0.96 host, clear the chain bit. + */ + next->link.control &= cpu_to_le32(~TRB_CHAIN); + + next->link.control ^= cpu_to_le32(TRB_CYCLE); + + xhci_flush_cache((uint32_t)next, sizeof(union xhci_trb)); + + /* Toggle the cycle bit after the last ring segment. */ + if (last_trb_on_last_seg(ctrl, ep_ring, + ep_ring->enq_seg, next)) + ep_ring->cycle_state = (ep_ring->cycle_state ? 0 : 1); + ep_ring->enq_seg = ep_ring->enq_seg->next; + ep_ring->enqueue = ep_ring->enq_seg->trbs; + next = ep_ring->enqueue; + } + + return 0; +} + +/** + * Generic function for queueing a command TRB on the command ring. + * Check to make sure there's room on the command ring for one command TRB. + * + * @param ctrl Host controller data structure + * @param ptr Pointer address to write in the first two fields (opt.) + * @param slot_id Slot ID to encode in the flags field (opt.) + * @param ep_index Endpoint index to encode in the flags field (opt.) + * @param cmd Command type to enqueue + * @return none + */ +void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, u32 slot_id, + u32 ep_index, trb_type cmd) +{ + u32 fields[4]; + u64 val_64 = (uintptr_t)ptr; + + BUG_ON(prepare_ring(ctrl, ctrl->cmd_ring, EP_STATE_RUNNING)); + + fields[0] = lower_32_bits(val_64); + fields[1] = upper_32_bits(val_64); + fields[2] = 0; + fields[3] = TRB_TYPE(cmd) | EP_ID_FOR_TRB(ep_index) | + SLOT_ID_FOR_TRB(slot_id) | ctrl->cmd_ring->cycle_state; + + queue_trb(ctrl, ctrl->cmd_ring, false, fields); + + /* Ring the command ring doorbell */ + xhci_writel(&ctrl->dba->doorbell[0], DB_VALUE_HOST); +} + +/** + * The TD size is the number of bytes remaining in the TD (including this TRB), + * right shifted by 10. + * It must fit in bits 21:17, so it can't be bigger than 31. + * + * @param remainder remaining packets to be sent + * @return remainder if remainder is less than max else max + */ +static u32 xhci_td_remainder(unsigned int remainder) +{ + u32 max = (1 << (21 - 17 + 1)) - 1; + + if ((remainder >> 10) >= max) + return max << 17; + else + return (remainder >> 10) << 17; +} + +/** + * Finds out the remanining packets to be sent + * + * @param running_total total size sent so far + * @param trb_buff_len length of the TRB Buffer + * @param total_packet_count total packet count + * @param maxpacketsize max packet size of current pipe + * @param num_trbs_left number of TRBs left to be processed + * @return 0 if running_total or trb_buff_len is 0, else remainder + */ +static u32 xhci_v1_0_td_remainder(int running_total, + int trb_buff_len, + unsigned int total_packet_count, + int maxpacketsize, + unsigned int num_trbs_left) +{ + int packets_transferred; + + /* One TRB with a zero-length data packet. */ + if (num_trbs_left == 0 || (running_total == 0 && trb_buff_len == 0)) + return 0; + + /* + * All the TRB queueing functions don't count the current TRB in + * running_total. + */ + packets_transferred = (running_total + trb_buff_len) / maxpacketsize; + + if ((total_packet_count - packets_transferred) > 31) + return 31 << 17; + return (total_packet_count - packets_transferred) << 17; +} + +/** + * Ring the doorbell of the End Point + * + * @param udev pointer to the USB device structure + * @param ep_index index of the endpoint + * @param start_cycle cycle flag of the first TRB + * @param start_trb pionter to the first TRB + * @return none + */ +static void giveback_first_trb(struct usb_device *udev, int ep_index, + int start_cycle, + struct xhci_generic_trb *start_trb) +{ + struct xhci_ctrl *ctrl = udev->controller; + + /* + * Pass all the TRBs to the hardware at once and make sure this write + * isn't reordered. + */ + if (start_cycle) + start_trb->field[3] |= cpu_to_le32(start_cycle); + else + start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE); + + xhci_flush_cache((uint32_t)start_trb, sizeof(struct xhci_generic_trb)); + + /* Ringing EP doorbell here */ + xhci_writel(&ctrl->dba->doorbell[udev->slot_id], + DB_VALUE(ep_index, 0)); + + return; +} + +/**** POLLING mechanism for XHCI ****/ + +/** + * Finalizes a handled event TRB by advancing our dequeue pointer and giving + * the TRB back to the hardware for recycling. Must call this exactly once at + * the end of each event handler, and not touch the TRB again afterwards. + * + * @param ctrl Host controller data structure + * @return none + */ +void xhci_acknowledge_event(struct xhci_ctrl *ctrl) +{ + /* Advance our dequeue pointer to the next event */ + inc_deq(ctrl, ctrl->event_ring); + + /* Inform the hardware */ + xhci_writeq(&ctrl->ir_set->erst_dequeue, + (uintptr_t)ctrl->event_ring->dequeue | ERST_EHB); +} + +/** + * Checks if there is a new event to handle on the event ring. + * + * @param ctrl Host controller data structure + * @return 0 if failure else 1 on success + */ +static int event_ready(struct xhci_ctrl *ctrl) +{ + union xhci_trb *event; + + xhci_inval_cache((uint32_t)ctrl->event_ring->dequeue, + sizeof(union xhci_trb)); + + event = ctrl->event_ring->dequeue; + + /* Does the HC or OS own the TRB? */ + if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != + ctrl->event_ring->cycle_state) + return 0; + + return 1; +} + +/** + * Waits for a specific type of event and returns it. Discards unexpected + * events. Caller *must* call xhci_acknowledge_event() after it is finished + * processing the event, and must not access the returned pointer afterwards. + * + * @param ctrl Host controller data structure + * @param expected TRB type expected from Event TRB + * @return pointer to event trb + */ +union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) +{ + trb_type type; + unsigned long ts = get_timer(0); + + do { + union xhci_trb *event = ctrl->event_ring->dequeue; + + if (!event_ready(ctrl)) + continue; + + type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); + if (type == expected) + return event; + + if (type == TRB_PORT_STATUS) + /* TODO: remove this once enumeration has been reworked */ + /* + * Port status change events always have a + * successful completion code + */ + BUG_ON(GET_COMP_CODE( + le32_to_cpu(event->generic.field[2])) != + COMP_SUCCESS); + else + printf("Unexpected XHCI event TRB, skipping... " + "(%08x %08x %08x %08x)\n", + le32_to_cpu(event->generic.field[0]), + le32_to_cpu(event->generic.field[1]), + le32_to_cpu(event->generic.field[2]), + le32_to_cpu(event->generic.field[3])); + + xhci_acknowledge_event(ctrl); + } while (get_timer(ts) < XHCI_TIMEOUT); + + if (expected == TRB_TRANSFER) + return NULL; + + printf("XHCI timeout on event type %d... cannot recover.\n", expected); + BUG(); +} + +/* + * Stops transfer processing for an endpoint and throws away all unprocessed + * TRBs by setting the xHC's dequeue pointer to our enqueue pointer. The next + * xhci_bulk_tx/xhci_ctrl_tx on this enpoint will add new transfers there and + * ring the doorbell, causing this endpoint to start working again. + * (Careful: This will BUG() when there was no transfer in progress. Shouldn't + * happen in practice for current uses and is too complicated to fix right now.) + */ +static void abort_td(struct usb_device *udev, int ep_index) +{ + struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring; + union xhci_trb *event; + u32 field; + + xhci_queue_command(ctrl, NULL, udev->slot_id, ep_index, TRB_STOP_RING); + + event = xhci_wait_for_event(ctrl, TRB_TRANSFER); + field = le32_to_cpu(event->trans_event.flags); + BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); + BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); + BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len + != COMP_STOP))); + xhci_acknowledge_event(ctrl); + + event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id || GET_COMP_CODE(le32_to_cpu( + event->event_cmd.status)) != COMP_SUCCESS); + xhci_acknowledge_event(ctrl); + + xhci_queue_command(ctrl, (void *)((uintptr_t)ring->enqueue | + ring->cycle_state), udev->slot_id, ep_index, TRB_SET_DEQ); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id || GET_COMP_CODE(le32_to_cpu( + event->event_cmd.status)) != COMP_SUCCESS); + xhci_acknowledge_event(ctrl); +} + +static void record_transfer_result(struct usb_device *udev, + union xhci_trb *event, int length) +{ + udev->act_len = min(length, length - + EVENT_TRB_LEN(le32_to_cpu(event->trans_event.transfer_len))); + + switch (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len))) { + case COMP_SUCCESS: + BUG_ON(udev->act_len != length); + /* fallthrough */ + case COMP_SHORT_TX: + udev->status = 0; + break; + case COMP_STALL: + udev->status = USB_ST_STALLED; + break; + case COMP_DB_ERR: + case COMP_TRB_ERR: + udev->status = USB_ST_BUF_ERR; + break; + case COMP_BABBLE: + udev->status = USB_ST_BABBLE_DET; + break; + default: + udev->status = 0x80; /* USB_ST_TOO_LAZY_TO_MAKE_A_NEW_MACRO */ + } +} + +/**** Bulk and Control transfer methods ****/ +/** + * Queues up the BULK Request + * + * @param udev pointer to the USB device structure + * @param pipe contains the DIR_IN or OUT , devnum + * @param length length of the buffer + * @param buffer buffer to be read/written based on the request + * @return returns 0 if successful else -1 on failure + */ +int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, + int length, void *buffer) +{ + int num_trbs = 0; + struct xhci_generic_trb *start_trb; + bool first_trb = 0; + int start_cycle; + u32 field = 0; + u32 length_field = 0; + struct xhci_ctrl *ctrl = udev->controller; + int slot_id = udev->slot_id; + int ep_index; + struct xhci_virt_device *virt_dev; + struct xhci_ep_ctx *ep_ctx; + struct xhci_ring *ring; /* EP transfer ring */ + union xhci_trb *event; + + int running_total, trb_buff_len; + unsigned int total_packet_count; + int maxpacketsize; + u64 addr; + int ret; + u32 trb_fields[4]; + u64 val_64 = (uintptr_t)buffer; + + debug("dev=%p, pipe=%lx, buffer=%p, length=%d\n", + udev, pipe, buffer, length); + + ep_index = usb_pipe_ep_index(pipe); + virt_dev = ctrl->devs[slot_id]; + + xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes, + virt_dev->out_ctx->size); + + ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); + + ring = virt_dev->eps[ep_index].ring; + /* + * How much data is (potentially) left before the 64KB boundary? + * XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec) + * that the buffer should not span 64KB boundary. if so + * we send request in more than 1 TRB by chaining them. + */ + running_total = TRB_MAX_BUFF_SIZE - + (lower_32_bits(val_64) & (TRB_MAX_BUFF_SIZE - 1)); + trb_buff_len = running_total; + running_total &= TRB_MAX_BUFF_SIZE - 1; + + /* + * If there's some data on this 64KB chunk, or we have to send a + * zero-length transfer, we need at least one TRB + */ + if (running_total != 0 || length == 0) + num_trbs++; + + /* How many more 64KB chunks to transfer, how many more TRBs? */ + while (running_total < length) { + num_trbs++; + running_total += TRB_MAX_BUFF_SIZE; + } + + /* + * XXX: Calling routine prepare_ring() called in place of + * prepare_trasfer() as there in 'Linux' since we are not + * maintaining multiple TDs/transfer at the same time. + */ + ret = prepare_ring(ctrl, ring, + le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK); + if (ret < 0) + return ret; + + /* + * Don't give the first TRB to the hardware (by toggling the cycle bit) + * until we've finished creating all the other TRBs. The ring's cycle + * state may change as we enqueue the other TRBs, so save it too. + */ + start_trb = &ring->enqueue->generic; + start_cycle = ring->cycle_state; + + running_total = 0; + maxpacketsize = usb_maxpacket(udev, pipe); + + total_packet_count = DIV_ROUND_UP(length, maxpacketsize); + + /* How much data is in the first TRB? */ + /* + * How much data is (potentially) left before the 64KB boundary? + * XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec) + * that the buffer should not span 64KB boundary. if so + * we send request in more than 1 TRB by chaining them. + */ + addr = val_64; + + if (trb_buff_len > length) + trb_buff_len = length; + + first_trb = true; + + /* flush the buffer before use */ + xhci_flush_cache((uint32_t)buffer, length); + + /* Queue the first TRB, even if it's zero-length */ + do { + u32 remainder = 0; + field = 0; + /* Don't change the cycle bit of the first TRB until later */ + if (first_trb) { + first_trb = false; + if (start_cycle == 0) + field |= TRB_CYCLE; + } else { + field |= ring->cycle_state; + } + + /* + * Chain all the TRBs together; clear the chain bit in the last + * TRB to indicate it's the last TRB in the chain. + */ + if (num_trbs > 1) + field |= TRB_CHAIN; + else + field |= TRB_IOC; + + /* Only set interrupt on short packet for IN endpoints */ + if (usb_pipein(pipe)) + field |= TRB_ISP; + + /* Set the TRB length, TD size, and interrupter fields. */ + if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) < 0x100) + remainder = xhci_td_remainder(length - running_total); + else + remainder = xhci_v1_0_td_remainder(running_total, + trb_buff_len, + total_packet_count, + maxpacketsize, + num_trbs - 1); + + length_field = ((trb_buff_len & TRB_LEN_MASK) | + remainder | + ((0 & TRB_INTR_TARGET_MASK) << + TRB_INTR_TARGET_SHIFT)); + + trb_fields[0] = lower_32_bits(addr); + trb_fields[1] = upper_32_bits(addr); + trb_fields[2] = length_field; + trb_fields[3] = field | (TRB_NORMAL << TRB_TYPE_SHIFT); + + queue_trb(ctrl, ring, (num_trbs > 1), trb_fields); + + --num_trbs; + + running_total += trb_buff_len; + + /* Calculate length for next transfer */ + addr += trb_buff_len; + trb_buff_len = min((length - running_total), TRB_MAX_BUFF_SIZE); + } while (running_total < length); + + giveback_first_trb(udev, ep_index, start_cycle, start_trb); + + event = xhci_wait_for_event(ctrl, TRB_TRANSFER); + if (!event) { + debug("XHCI bulk transfer timed out, aborting...\n"); + abort_td(udev, ep_index); + udev->status = USB_ST_NAK_REC; /* closest thing to a timeout */ + udev->act_len = 0; + return -ETIMEDOUT; + } + field = le32_to_cpu(event->trans_event.flags); + + BUG_ON(TRB_TO_SLOT_ID(field) != slot_id); + BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); + BUG_ON(*(void **)(uintptr_t)le64_to_cpu(event->trans_event.buffer) - + buffer > (size_t)length); + + record_transfer_result(udev, event, length); + xhci_acknowledge_event(ctrl); + xhci_inval_cache((uint32_t)buffer, length); + + return (udev->status != USB_ST_NOT_PROC) ? 0 : -1; +} + +/** + * Queues up the Control Transfer Request + * + * @param udev pointer to the USB device structure + * @param pipe contains the DIR_IN or OUT , devnum + * @param req request type + * @param length length of the buffer + * @param buffer buffer to be read/written based on the request + * @return returns 0 if successful else error code on failure + */ +int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, + struct devrequest *req, int length, + void *buffer) +{ + int ret; + int start_cycle; + int num_trbs; + u32 field; + u32 length_field; + u64 buf_64 = 0; + struct xhci_generic_trb *start_trb; + struct xhci_ctrl *ctrl = udev->controller; + int slot_id = udev->slot_id; + int ep_index; + u32 trb_fields[4]; + struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; + struct xhci_ring *ep_ring; + union xhci_trb *event; + + debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", + req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->value), + le16_to_cpu(req->index)); + + ep_index = usb_pipe_ep_index(pipe); + + ep_ring = virt_dev->eps[ep_index].ring; + + /* + * Check to see if the max packet size for the default control + * endpoint changed during FS device enumeration + */ + if (udev->speed == USB_SPEED_FULL) { + ret = xhci_check_maxpacket(udev); + if (ret < 0) + return ret; + } + + xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes, + virt_dev->out_ctx->size); + + struct xhci_ep_ctx *ep_ctx = NULL; + ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); + + /* 1 TRB for setup, 1 for status */ + num_trbs = 2; + /* + * Don't need to check if we need additional event data and normal TRBs, + * since data in control transfers will never get bigger than 16MB + * XXX: can we get a buffer that crosses 64KB boundaries? + */ + + if (length > 0) + num_trbs++; + /* + * XXX: Calling routine prepare_ring() called in place of + * prepare_trasfer() as there in 'Linux' since we are not + * maintaining multiple TDs/transfer at the same time. + */ + ret = prepare_ring(ctrl, ep_ring, + le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK); + + if (ret < 0) + return ret; + + /* + * Don't give the first TRB to the hardware (by toggling the cycle bit) + * until we've finished creating all the other TRBs. The ring's cycle + * state may change as we enqueue the other TRBs, so save it too. + */ + start_trb = &ep_ring->enqueue->generic; + start_cycle = ep_ring->cycle_state; + + debug("start_trb %p, start_cycle %d\n", start_trb, start_cycle); + + /* Queue setup TRB - see section 6.4.1.2.1 */ + /* FIXME better way to translate setup_packet into two u32 fields? */ + field = 0; + field |= TRB_IDT | (TRB_SETUP << TRB_TYPE_SHIFT); + if (start_cycle == 0) + field |= 0x1; + + /* xHCI 1.0 6.4.1.2.1: Transfer Type field */ + if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) == 0x100) { + if (length > 0) { + if (req->requesttype & USB_DIR_IN) + field |= (TRB_DATA_IN << TRB_TX_TYPE_SHIFT); + else + field |= (TRB_DATA_OUT << TRB_TX_TYPE_SHIFT); + } + } + + debug("req->requesttype = %d, req->request = %d," + "le16_to_cpu(req->value) = %d," + "le16_to_cpu(req->index) = %d," + "le16_to_cpu(req->length) = %d\n", + req->requesttype, req->request, le16_to_cpu(req->value), + le16_to_cpu(req->index), le16_to_cpu(req->length)); + + trb_fields[0] = req->requesttype | req->request << 8 | + le16_to_cpu(req->value) << 16; + trb_fields[1] = le16_to_cpu(req->index) | + le16_to_cpu(req->length) << 16; + /* TRB_LEN | (TRB_INTR_TARGET) */ + trb_fields[2] = (8 | ((0 & TRB_INTR_TARGET_MASK) << + TRB_INTR_TARGET_SHIFT)); + /* Immediate data in pointer */ + trb_fields[3] = field; + queue_trb(ctrl, ep_ring, true, trb_fields); + + /* Re-initializing field to zero */ + field = 0; + /* If there's data, queue data TRBs */ + /* Only set interrupt on short packet for IN endpoints */ + if (usb_pipein(pipe)) + field = TRB_ISP | (TRB_DATA << TRB_TYPE_SHIFT); + else + field = (TRB_DATA << TRB_TYPE_SHIFT); + + length_field = (length & TRB_LEN_MASK) | xhci_td_remainder(length) | + ((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT); + debug("length_field = %d, length = %d," + "xhci_td_remainder(length) = %d , TRB_INTR_TARGET(0) = %d\n", + length_field, (length & TRB_LEN_MASK), + xhci_td_remainder(length), 0); + + if (length > 0) { + if (req->requesttype & USB_DIR_IN) + field |= TRB_DIR_IN; + buf_64 = (uintptr_t)buffer; + + trb_fields[0] = lower_32_bits(buf_64); + trb_fields[1] = upper_32_bits(buf_64); + trb_fields[2] = length_field; + trb_fields[3] = field | ep_ring->cycle_state; + + xhci_flush_cache((uint32_t)buffer, length); + queue_trb(ctrl, ep_ring, true, trb_fields); + } + + /* + * Queue status TRB - + * see Table 7 and sections 4.11.2.2 and 6.4.1.2.3 + */ + + /* If the device sent data, the status stage is an OUT transfer */ + field = 0; + if (length > 0 && req->requesttype & USB_DIR_IN) + field = 0; + else + field = TRB_DIR_IN; + + trb_fields[0] = 0; + trb_fields[1] = 0; + trb_fields[2] = ((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT); + /* Event on completion */ + trb_fields[3] = field | TRB_IOC | + (TRB_STATUS << TRB_TYPE_SHIFT) | + ep_ring->cycle_state; + + queue_trb(ctrl, ep_ring, false, trb_fields); + + giveback_first_trb(udev, ep_index, start_cycle, start_trb); + + event = xhci_wait_for_event(ctrl, TRB_TRANSFER); + if (!event) + goto abort; + field = le32_to_cpu(event->trans_event.flags); + + BUG_ON(TRB_TO_SLOT_ID(field) != slot_id); + BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); + + record_transfer_result(udev, event, length); + xhci_acknowledge_event(ctrl); + + /* Invalidate buffer to make it available to usb-core */ + if (length > 0) + xhci_inval_cache((uint32_t)buffer, length); + + if (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len)) + == COMP_SHORT_TX) { + /* Short data stage, clear up additional status stage event */ + event = xhci_wait_for_event(ctrl, TRB_TRANSFER); + if (!event) + goto abort; + BUG_ON(TRB_TO_SLOT_ID(field) != slot_id); + BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); + xhci_acknowledge_event(ctrl); + } + + return (udev->status != USB_ST_NOT_PROC) ? 0 : -1; + +abort: + debug("XHCI control transfer timed out, aborting...\n"); + abort_td(udev, ep_index); + udev->status = USB_ST_NAK_REC; + udev->act_len = 0; + return -ETIMEDOUT; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/xhci.c b/qemu/roms/u-boot/drivers/usb/host/xhci.c new file mode 100644 index 000000000..d1c2e5c45 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/xhci.c @@ -0,0 +1,1030 @@ +/* + * USB HOST XHCI Controller stack + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam + * Vikas Sajjan + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/** + * This file gives the xhci stack for usb3.0 looking into + * xhci specification Rev1.0 (5/21/10). + * The quirk devices support hasn't been given yet. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "xhci.h" + +#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + +static struct descriptor { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_config_descriptor config; + struct usb_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; + struct usb_ss_ep_comp_descriptor ep_companion; +} __attribute__ ((packed)) descriptor = { + { + 0xc, /* bDescLength */ + 0x2a, /* bDescriptorType: hub descriptor */ + 2, /* bNrPorts -- runtime modified */ + cpu_to_le16(0x8), /* wHubCharacteristics */ + 10, /* bPwrOn2PwrGood */ + 0, /* bHubCntrCurrent */ + {}, /* Device removable */ + {} /* at most 7 ports! XXX */ + }, + { + 0x12, /* bLength */ + 1, /* bDescriptorType: UDESC_DEVICE */ + cpu_to_le16(0x0300), /* bcdUSB: v3.0 */ + 9, /* bDeviceClass: UDCLASS_HUB */ + 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ + 3, /* bDeviceProtocol: UDPROTO_SSHUBSTT */ + 9, /* bMaxPacketSize: 512 bytes 2^9 */ + 0x0000, /* idVendor */ + 0x0000, /* idProduct */ + cpu_to_le16(0x0100), /* bcdDevice */ + 1, /* iManufacturer */ + 2, /* iProduct */ + 0, /* iSerialNumber */ + 1 /* bNumConfigurations: 1 */ + }, + { + 0x9, + 2, /* bDescriptorType: UDESC_CONFIG */ + cpu_to_le16(0x1f), /* includes SS endpoint descriptor */ + 1, /* bNumInterface */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x40, /* bmAttributes: UC_SELF_POWER */ + 0 /* bMaxPower */ + }, + { + 0x9, /* bLength */ + 4, /* bDescriptorType: UDESC_INTERFACE */ + 0, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ + 1, /* bNumEndpoints */ + 9, /* bInterfaceClass: UICLASS_HUB */ + 0, /* bInterfaceSubClass: UISUBCLASS_HUB */ + 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ + 0 /* iInterface */ + }, + { + 0x7, /* bLength */ + 5, /* bDescriptorType: UDESC_ENDPOINT */ + 0x81, /* bEndpointAddress: IN endpoint 1 */ + 3, /* bmAttributes: UE_INTERRUPT */ + 8, /* wMaxPacketSize */ + 255 /* bInterval */ + }, + { + 0x06, /* ss_bLength */ + 0x30, /* ss_bDescriptorType: SS EP Companion */ + 0x00, /* ss_bMaxBurst: allows 1 TX between ACKs */ + /* ss_bmAttributes: 1 packet per service interval */ + 0x00, + /* ss_wBytesPerInterval: 15 bits for max 15 ports */ + cpu_to_le16(0x02), + }, +}; + +static struct xhci_ctrl xhcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; + +/** + * Waits for as per specified amount of time + * for the "result" to match with "done" + * + * @param ptr pointer to the register to be read + * @param mask mask for the value read + * @param done value to be campared with result + * @param usec time to wait till + * @return 0 if handshake is success else < 0 on failure + */ +static int handshake(uint32_t volatile *ptr, uint32_t mask, + uint32_t done, int usec) +{ + uint32_t result; + + do { + result = xhci_readl(ptr); + if (result == ~(uint32_t)0) + return -ENODEV; + result &= mask; + if (result == done) + return 0; + usec--; + udelay(1); + } while (usec > 0); + + return -ETIMEDOUT; +} + +/** + * Set the run bit and wait for the host to be running. + * + * @param hcor pointer to host controller operation registers + * @return status of the Handshake + */ +static int xhci_start(struct xhci_hcor *hcor) +{ + u32 temp; + int ret; + + puts("Starting the controller\n"); + temp = xhci_readl(&hcor->or_usbcmd); + temp |= (CMD_RUN); + xhci_writel(&hcor->or_usbcmd, temp); + + /* + * Wait for the HCHalted Status bit to be 0 to indicate the host is + * running. + */ + ret = handshake(&hcor->or_usbsts, STS_HALT, 0, XHCI_MAX_HALT_USEC); + if (ret) + debug("Host took too long to start, " + "waited %u microseconds.\n", + XHCI_MAX_HALT_USEC); + return ret; +} + +/** + * Resets the XHCI Controller + * + * @param hcor pointer to host controller operation registers + * @return -EBUSY if XHCI Controller is not halted else status of handshake + */ +int xhci_reset(struct xhci_hcor *hcor) +{ + u32 cmd; + u32 state; + int ret; + + /* Halting the Host first */ + debug("// Halt the HC\n"); + state = xhci_readl(&hcor->or_usbsts) & STS_HALT; + if (!state) { + cmd = xhci_readl(&hcor->or_usbcmd); + cmd &= ~CMD_RUN; + xhci_writel(&hcor->or_usbcmd, cmd); + } + + ret = handshake(&hcor->or_usbsts, + STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); + if (ret) { + printf("Host not halted after %u microseconds.\n", + XHCI_MAX_HALT_USEC); + return -EBUSY; + } + + debug("// Reset the HC\n"); + cmd = xhci_readl(&hcor->or_usbcmd); + cmd |= CMD_RESET; + xhci_writel(&hcor->or_usbcmd, cmd); + + ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, XHCI_MAX_RESET_USEC); + if (ret) + return ret; + + /* + * xHCI cannot write to any doorbells or operational registers other + * than status until the "Controller Not Ready" flag is cleared. + */ + return handshake(&hcor->or_usbsts, STS_CNR, 0, XHCI_MAX_RESET_USEC); +} + +/** + * Used for passing endpoint bitmasks between the core and HCDs. + * Find the index for an endpoint given its descriptor. + * Use the return value to right shift 1 for the bitmask. + * + * Index = (epnum * 2) + direction - 1, + * where direction = 0 for OUT, 1 for IN. + * For control endpoints, the IN index is used (OUT index is unused), so + * index = (epnum * 2) + direction - 1 = (epnum * 2) + 1 - 1 = (epnum * 2) + * + * @param desc USB enpdoint Descriptor + * @return index of the Endpoint + */ +static unsigned int xhci_get_ep_index(struct usb_endpoint_descriptor *desc) +{ + unsigned int index; + + if (usb_endpoint_xfer_control(desc)) + index = (unsigned int)(usb_endpoint_num(desc) * 2); + else + index = (unsigned int)((usb_endpoint_num(desc) * 2) - + (usb_endpoint_dir_in(desc) ? 0 : 1)); + + return index; +} + +/** + * Issue a configure endpoint command or evaluate context command + * and wait for it to finish. + * + * @param udev pointer to the Device Data Structure + * @param ctx_change flag to indicate the Context has changed or NOT + * @return 0 on success, -1 on failure + */ +static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change) +{ + struct xhci_container_ctx *in_ctx; + struct xhci_virt_device *virt_dev; + struct xhci_ctrl *ctrl = udev->controller; + union xhci_trb *event; + + virt_dev = ctrl->devs[udev->slot_id]; + in_ctx = virt_dev->in_ctx; + + xhci_flush_cache((uint32_t)in_ctx->bytes, in_ctx->size); + xhci_queue_command(ctrl, in_ctx->bytes, udev->slot_id, 0, + ctx_change ? TRB_EVAL_CONTEXT : TRB_CONFIG_EP); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id); + + switch (GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))) { + case COMP_SUCCESS: + debug("Successful %s command\n", + ctx_change ? "Evaluate Context" : "Configure Endpoint"); + break; + default: + printf("ERROR: %s command returned completion code %d.\n", + ctx_change ? "Evaluate Context" : "Configure Endpoint", + GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))); + return -EINVAL; + } + + xhci_acknowledge_event(ctrl); + + return 0; +} + +/** + * Configure the endpoint, programming the device contexts. + * + * @param udev pointer to the USB device structure + * @return returns the status of the xhci_configure_endpoints + */ +static int xhci_set_configuration(struct usb_device *udev) +{ + struct xhci_container_ctx *in_ctx; + struct xhci_container_ctx *out_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_slot_ctx *slot_ctx; + struct xhci_ep_ctx *ep_ctx[MAX_EP_CTX_NUM]; + int cur_ep; + int max_ep_flag = 0; + int ep_index; + unsigned int dir; + unsigned int ep_type; + struct xhci_ctrl *ctrl = udev->controller; + int num_of_ep; + int ep_flag = 0; + u64 trb_64 = 0; + int slot_id = udev->slot_id; + struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; + struct usb_interface *ifdesc; + + out_ctx = virt_dev->out_ctx; + in_ctx = virt_dev->in_ctx; + + num_of_ep = udev->config.if_desc[0].no_of_ep; + ifdesc = &udev->config.if_desc[0]; + + ctrl_ctx = xhci_get_input_control_ctx(in_ctx); + /* Zero the input context control */ + ctrl_ctx->add_flags = 0; + ctrl_ctx->drop_flags = 0; + + /* EP_FLAG gives values 1 & 4 for EP1OUT and EP2IN */ + for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { + ep_flag = xhci_get_ep_index(&ifdesc->ep_desc[cur_ep]); + ctrl_ctx->add_flags |= cpu_to_le32(1 << (ep_flag + 1)); + if (max_ep_flag < ep_flag) + max_ep_flag = ep_flag; + } + + xhci_inval_cache((uint32_t)out_ctx->bytes, out_ctx->size); + + /* slot context */ + xhci_slot_copy(ctrl, in_ctx, out_ctx); + slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx); + slot_ctx->dev_info &= ~(LAST_CTX_MASK); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(max_ep_flag + 1) | 0); + + xhci_endpoint_copy(ctrl, in_ctx, out_ctx, 0); + + /* filling up ep contexts */ + for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { + struct usb_endpoint_descriptor *endpt_desc = NULL; + + endpt_desc = &ifdesc->ep_desc[cur_ep]; + trb_64 = 0; + + ep_index = xhci_get_ep_index(endpt_desc); + ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); + + /* Allocate the ep rings */ + virt_dev->eps[ep_index].ring = xhci_ring_alloc(1, true); + if (!virt_dev->eps[ep_index].ring) + return -ENOMEM; + + /*NOTE: ep_desc[0] actually represents EP1 and so on */ + dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7); + ep_type = (((endpt_desc->bmAttributes) & (0x3)) | (dir << 2)); + ep_ctx[ep_index]->ep_info2 = + cpu_to_le32(ep_type << EP_TYPE_SHIFT); + ep_ctx[ep_index]->ep_info2 |= + cpu_to_le32(MAX_PACKET + (get_unaligned(&endpt_desc->wMaxPacketSize))); + + ep_ctx[ep_index]->ep_info2 |= + cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) | + ((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT)); + + trb_64 = (uintptr_t) + virt_dev->eps[ep_index].ring->enqueue; + ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 | + virt_dev->eps[ep_index].ring->cycle_state); + } + + return xhci_configure_endpoints(udev, false); +} + +/** + * Issue an Address Device command (which will issue a SetAddress request to + * the device). + * + * @param udev pointer to the Device Data Structure + * @return 0 if successful else error code on failure + */ +static int xhci_address_device(struct usb_device *udev) +{ + int ret = 0; + struct xhci_ctrl *ctrl = udev->controller; + struct xhci_slot_ctx *slot_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_virt_device *virt_dev; + int slot_id = udev->slot_id; + union xhci_trb *event; + + virt_dev = ctrl->devs[slot_id]; + + /* + * This is the first Set Address since device plug-in + * so setting up the slot context. + */ + debug("Setting up addressable devices\n"); + xhci_setup_addressable_virt_dev(udev); + + ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); + ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); + ctrl_ctx->drop_flags = 0; + + xhci_queue_command(ctrl, (void *)ctrl_ctx, slot_id, 0, TRB_ADDR_DEV); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != slot_id); + + switch (GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))) { + case COMP_CTX_STATE: + case COMP_EBADSLT: + printf("Setup ERROR: address device command for slot %d.\n", + slot_id); + ret = -EINVAL; + break; + case COMP_TX_ERR: + puts("Device not responding to set address.\n"); + ret = -EPROTO; + break; + case COMP_DEV_ERR: + puts("ERROR: Incompatible device" + "for address device command.\n"); + ret = -ENODEV; + break; + case COMP_SUCCESS: + debug("Successful Address Device command\n"); + udev->status = 0; + break; + default: + printf("ERROR: unexpected command completion code 0x%x.\n", + GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))); + ret = -EINVAL; + break; + } + + xhci_acknowledge_event(ctrl); + + if (ret < 0) + /* + * TODO: Unsuccessful Address Device command shall leave the + * slot in default state. So, issue Disable Slot command now. + */ + return ret; + + xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes, + virt_dev->out_ctx->size); + slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->out_ctx); + + debug("xHC internal address is: %d\n", + le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK); + + return 0; +} + +/** + * Issue Enable slot command to the controller to allocate + * device slot and assign the slot id. It fails if the xHC + * ran out of device slots, the Enable Slot command timed out, + * or allocating memory failed. + * + * @param udev pointer to the Device Data Structure + * @return Returns 0 on succes else return error code on failure + */ +int usb_alloc_device(struct usb_device *udev) +{ + union xhci_trb *event; + struct xhci_ctrl *ctrl = udev->controller; + int ret; + + /* + * Root hub will be first device to be initailized. + * If this device is root-hub, don't do any xHC related + * stuff. + */ + if (ctrl->rootdev == 0) { + udev->speed = USB_SPEED_SUPER; + return 0; + } + + xhci_queue_command(ctrl, NULL, 0, 0, TRB_ENABLE_SLOT); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + BUG_ON(GET_COMP_CODE(le32_to_cpu(event->event_cmd.status)) + != COMP_SUCCESS); + + udev->slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)); + + xhci_acknowledge_event(ctrl); + + ret = xhci_alloc_virt_device(udev); + if (ret < 0) { + /* + * TODO: Unsuccessful Address Device command shall leave + * the slot in default. So, issue Disable Slot command now. + */ + puts("Could not allocate xHCI USB device data structures\n"); + return ret; + } + + return 0; +} + +/* + * Full speed devices may have a max packet size greater than 8 bytes, but the + * USB core doesn't know that until it reads the first 8 bytes of the + * descriptor. If the usb_device's max packet size changes after that point, + * we need to issue an evaluate context command and wait on it. + * + * @param udev pointer to the Device Data Structure + * @return returns the status of the xhci_configure_endpoints + */ +int xhci_check_maxpacket(struct usb_device *udev) +{ + struct xhci_ctrl *ctrl = udev->controller; + unsigned int slot_id = udev->slot_id; + int ep_index = 0; /* control endpoint */ + struct xhci_container_ctx *in_ctx; + struct xhci_container_ctx *out_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_ep_ctx *ep_ctx; + int max_packet_size; + int hw_max_packet_size; + int ret = 0; + struct usb_interface *ifdesc; + + ifdesc = &udev->config.if_desc[0]; + + out_ctx = ctrl->devs[slot_id]->out_ctx; + xhci_inval_cache((uint32_t)out_ctx->bytes, out_ctx->size); + + ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); + hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); + max_packet_size = usb_endpoint_maxp(&ifdesc->ep_desc[0]); + if (hw_max_packet_size != max_packet_size) { + debug("Max Packet Size for ep 0 changed.\n"); + debug("Max packet size in usb_device = %d\n", max_packet_size); + debug("Max packet size in xHCI HW = %d\n", hw_max_packet_size); + debug("Issuing evaluate context command.\n"); + + /* Set up the modified control endpoint 0 */ + xhci_endpoint_copy(ctrl, ctrl->devs[slot_id]->in_ctx, + ctrl->devs[slot_id]->out_ctx, ep_index); + in_ctx = ctrl->devs[slot_id]->in_ctx; + ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); + ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); + + /* + * Set up the input context flags for the command + * FIXME: This won't work if a non-default control endpoint + * changes max packet sizes. + */ + ctrl_ctx = xhci_get_input_control_ctx(in_ctx); + ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG); + ctrl_ctx->drop_flags = 0; + + ret = xhci_configure_endpoints(udev, true); + } + return ret; +} + +/** + * Clears the Change bits of the Port Status Register + * + * @param wValue request value + * @param wIndex request index + * @param addr address of posrt status register + * @param port_status state of port status register + * @return none + */ +static void xhci_clear_port_change_bit(u16 wValue, + u16 wIndex, volatile uint32_t *addr, u32 port_status) +{ + char *port_change_bit; + u32 status; + + switch (wValue) { + case USB_PORT_FEAT_C_RESET: + status = PORT_RC; + port_change_bit = "reset"; + break; + case USB_PORT_FEAT_C_CONNECTION: + status = PORT_CSC; + port_change_bit = "connect"; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + status = PORT_OCC; + port_change_bit = "over-current"; + break; + case USB_PORT_FEAT_C_ENABLE: + status = PORT_PEC; + port_change_bit = "enable/disable"; + break; + case USB_PORT_FEAT_C_SUSPEND: + status = PORT_PLC; + port_change_bit = "suspend/resume"; + break; + default: + /* Should never happen */ + return; + } + + /* Change bits are all write 1 to clear */ + xhci_writel(addr, port_status | status); + + port_status = xhci_readl(addr); + debug("clear port %s change, actual port %d status = 0x%x\n", + port_change_bit, wIndex, port_status); +} + +/** + * Save Read Only (RO) bits and save read/write bits where + * writing a 0 clears the bit and writing a 1 sets the bit (RWS). + * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect. + * + * @param state state of the Port Status and Control Regsiter + * @return a value that would result in the port being in the + * same state, if the value was written to the port + * status control register. + */ +static u32 xhci_port_state_to_neutral(u32 state) +{ + /* Save read-only status and port state */ + return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); +} + +/** + * Submits the Requests to the XHCI Host Controller + * + * @param udev pointer to the USB device structure + * @param pipe contains the DIR_IN or OUT , devnum + * @param buffer buffer to be read/written based on the request + * @return returns 0 if successful else -1 on failure + */ +static int xhci_submit_root(struct usb_device *udev, unsigned long pipe, + void *buffer, struct devrequest *req) +{ + uint8_t tmpbuf[4]; + u16 typeReq; + void *srcptr = NULL; + int len, srclen; + uint32_t reg; + volatile uint32_t *status_reg; + struct xhci_ctrl *ctrl = udev->controller; + struct xhci_hcor *hcor = ctrl->hcor; + + if (((req->requesttype & USB_RT_PORT) && + le16_to_cpu(req->index)) > CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS) { + printf("The request port(%d) is not configured\n", + le16_to_cpu(req->index) - 1); + return -EINVAL; + } + + status_reg = (volatile uint32_t *) + (&hcor->portregs[le16_to_cpu(req->index) - 1].or_portsc); + srclen = 0; + + typeReq = req->request | req->requesttype << 8; + + switch (typeReq) { + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_DEVICE: + debug("USB_DT_DEVICE request\n"); + srcptr = &descriptor.device; + srclen = 0x12; + break; + case USB_DT_CONFIG: + debug("USB_DT_CONFIG config\n"); + srcptr = &descriptor.config; + srclen = 0x19; + break; + case USB_DT_STRING: + debug("USB_DT_STRING config\n"); + switch (le16_to_cpu(req->value) & 0xff) { + case 0: /* Language */ + srcptr = "\4\3\11\4"; + srclen = 4; + break; + case 1: /* Vendor String */ + srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; + srclen = 14; + break; + case 2: /* Product Name */ + srcptr = "\52\3X\0H\0C\0I\0 " + "\0H\0o\0s\0t\0 " + "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; + srclen = 42; + break; + default: + printf("unknown value DT_STRING %x\n", + le16_to_cpu(req->value)); + goto unknown; + } + break; + default: + printf("unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_HUB: + debug("USB_DT_HUB config\n"); + srcptr = &descriptor.hub; + srclen = 0x8; + break; + default: + printf("unknown value %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): + debug("USB_REQ_SET_ADDRESS\n"); + ctrl->rootdev = le16_to_cpu(req->value); + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + /* Do nothing */ + break; + case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): + tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */ + tmpbuf[1] = 0; + srcptr = tmpbuf; + srclen = 2; + break; + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): + memset(tmpbuf, 0, 4); + reg = xhci_readl(status_reg); + if (reg & PORT_CONNECT) { + tmpbuf[0] |= USB_PORT_STAT_CONNECTION; + switch (reg & DEV_SPEED_MASK) { + case XDEV_FS: + debug("SPEED = FULLSPEED\n"); + break; + case XDEV_LS: + debug("SPEED = LOWSPEED\n"); + tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; + break; + case XDEV_HS: + debug("SPEED = HIGHSPEED\n"); + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + break; + case XDEV_SS: + debug("SPEED = SUPERSPEED\n"); + tmpbuf[1] |= USB_PORT_STAT_SUPER_SPEED >> 8; + break; + } + } + if (reg & PORT_PE) + tmpbuf[0] |= USB_PORT_STAT_ENABLE; + if ((reg & PORT_PLS_MASK) == XDEV_U3) + tmpbuf[0] |= USB_PORT_STAT_SUSPEND; + if (reg & PORT_OC) + tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; + if (reg & PORT_RESET) + tmpbuf[0] |= USB_PORT_STAT_RESET; + if (reg & PORT_POWER) + /* + * XXX: This Port power bit (for USB 3.0 hub) + * we are faking in USB 2.0 hub port status; + * since there's a change in bit positions in + * two: + * USB 2.0 port status PP is at position[8] + * USB 3.0 port status PP is at position[9] + * So, we are still keeping it at position [8] + */ + tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + if (reg & PORT_CSC) + tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; + if (reg & PORT_PEC) + tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; + if (reg & PORT_OCC) + tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; + if (reg & PORT_RC) + tmpbuf[2] |= USB_PORT_STAT_C_RESET; + + srcptr = tmpbuf; + srclen = 4; + break; + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = xhci_readl(status_reg); + reg = xhci_port_state_to_neutral(reg); + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg |= PORT_PE; + xhci_writel(status_reg, reg); + break; + case USB_PORT_FEAT_POWER: + reg |= PORT_POWER; + xhci_writel(status_reg, reg); + break; + case USB_PORT_FEAT_RESET: + reg |= PORT_RESET; + xhci_writel(status_reg, reg); + break; + default: + printf("unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = xhci_readl(status_reg); + reg = xhci_port_state_to_neutral(reg); + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg &= ~PORT_PE; + break; + case USB_PORT_FEAT_POWER: + reg &= ~PORT_POWER; + break; + case USB_PORT_FEAT_C_RESET: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_ENABLE: + xhci_clear_port_change_bit((le16_to_cpu(req->value)), + le16_to_cpu(req->index), + status_reg, reg); + break; + default: + printf("unknown feature %x\n", le16_to_cpu(req->value)); + goto unknown; + } + xhci_writel(status_reg, reg); + break; + default: + puts("Unknown request\n"); + goto unknown; + } + + debug("scrlen = %d\n req->length = %d\n", + srclen, le16_to_cpu(req->length)); + + len = min(srclen, le16_to_cpu(req->length)); + + if (srcptr != NULL && len > 0) + memcpy(buffer, srcptr, len); + else + debug("Len is 0\n"); + + udev->act_len = len; + udev->status = 0; + + return 0; + +unknown: + udev->act_len = 0; + udev->status = USB_ST_STALLED; + + return -ENODEV; +} + +/** + * Submits the INT request to XHCI Host cotroller + * + * @param udev pointer to the USB device + * @param pipe contains the DIR_IN or OUT , devnum + * @param buffer buffer to be read/written based on the request + * @param length length of the buffer + * @param interval interval of the interrupt + * @return 0 + */ +int +submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, + int length, int interval) +{ + /* + * TODO: Not addressing any interrupt type transfer requests + * Add support for it later. + */ + return -EINVAL; +} + +/** + * submit the BULK type of request to the USB Device + * + * @param udev pointer to the USB device + * @param pipe contains the DIR_IN or OUT , devnum + * @param buffer buffer to be read/written based on the request + * @param length length of the buffer + * @return returns 0 if successful else -1 on failure + */ +int +submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, + int length) +{ + if (usb_pipetype(pipe) != PIPE_BULK) { + printf("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); + return -EINVAL; + } + + return xhci_bulk_tx(udev, pipe, length, buffer); +} + +/** + * submit the control type of request to the Root hub/Device based on the devnum + * + * @param udev pointer to the USB device + * @param pipe contains the DIR_IN or OUT , devnum + * @param buffer buffer to be read/written based on the request + * @param length length of the buffer + * @param setup Request type + * @return returns 0 if successful else -1 on failure + */ +int +submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer, + int length, struct devrequest *setup) +{ + struct xhci_ctrl *ctrl = udev->controller; + int ret = 0; + + if (usb_pipetype(pipe) != PIPE_CONTROL) { + printf("non-control pipe (type=%lu)", usb_pipetype(pipe)); + return -EINVAL; + } + + if (usb_pipedevice(pipe) == ctrl->rootdev) + return xhci_submit_root(udev, pipe, buffer, setup); + + if (setup->request == USB_REQ_SET_ADDRESS) + return xhci_address_device(udev); + + if (setup->request == USB_REQ_SET_CONFIGURATION) { + ret = xhci_set_configuration(udev); + if (ret) { + puts("Failed to configure xHCI endpoint\n"); + return ret; + } + } + + return xhci_ctrl_tx(udev, pipe, setup, length, buffer); +} + +/** + * Intialises the XHCI host controller + * and allocates the necessary data structures + * + * @param index index to the host controller data structure + * @return pointer to the intialised controller + */ +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + uint32_t val; + uint32_t val2; + uint32_t reg; + struct xhci_hccr *hccr; + struct xhci_hcor *hcor; + struct xhci_ctrl *ctrl; + + if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0) + return -ENODEV; + + if (xhci_reset(hcor) != 0) + return -ENODEV; + + ctrl = &xhcic[index]; + + ctrl->hccr = hccr; + ctrl->hcor = hcor; + + /* + * Program the Number of Device Slots Enabled field in the CONFIG + * register with the max value of slots the HC can handle. + */ + val = (xhci_readl(&hccr->cr_hcsparams1) & HCS_SLOTS_MASK); + val2 = xhci_readl(&hcor->or_config); + val |= (val2 & ~HCS_SLOTS_MASK); + xhci_writel(&hcor->or_config, val); + + /* initializing xhci data structures */ + if (xhci_mem_init(ctrl, hccr, hcor) < 0) + return -ENOMEM; + + reg = xhci_readl(&hccr->cr_hcsparams1); + descriptor.hub.bNbrPorts = ((reg & HCS_MAX_PORTS_MASK) >> + HCS_MAX_PORTS_SHIFT); + printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); + + /* Port Indicators */ + reg = xhci_readl(&hccr->cr_hccparams); + if (HCS_INDICATOR(reg)) + put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) + | 0x80, &descriptor.hub.wHubCharacteristics); + + /* Port Power Control */ + if (HCC_PPC(reg)) + put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) + | 0x01, &descriptor.hub.wHubCharacteristics); + + if (xhci_start(hcor)) { + xhci_reset(hcor); + return -ENODEV; + } + + /* Zero'ing IRQ control register and IRQ pending register */ + xhci_writel(&ctrl->ir_set->irq_control, 0x0); + xhci_writel(&ctrl->ir_set->irq_pending, 0x0); + + reg = HC_VERSION(xhci_readl(&hccr->cr_capbase)); + printf("USB XHCI %x.%02x\n", reg >> 8, reg & 0xff); + + *controller = &xhcic[index]; + + return 0; +} + +/** + * Stops the XHCI host controller + * and cleans up all the related data structures + * + * @param index index to the host controller data structure + * @return none + */ +int usb_lowlevel_stop(int index) +{ + struct xhci_ctrl *ctrl = (xhcic + index); + u32 temp; + + xhci_reset(ctrl->hcor); + + debug("// Disabling event ring interrupts\n"); + temp = xhci_readl(&ctrl->hcor->or_usbsts); + xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT); + temp = xhci_readl(&ctrl->ir_set->irq_pending); + xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp)); + + xhci_hcd_stop(index); + + xhci_cleanup(ctrl); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/host/xhci.h b/qemu/roms/u-boot/drivers/usb/host/xhci.h new file mode 100644 index 000000000..ceb1573d8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/host/xhci.h @@ -0,0 +1,1255 @@ +/* + * USB HOST XHCI Controller + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam + * Vikas Sajjan + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef HOST_XHCI_H_ +#define HOST_XHCI_H_ + +#include +#include +#include + +#define upper_32_bits(n) (u32)((n) >> 32) +#define lower_32_bits(n) (u32)(n) + +#define MAX_EP_CTX_NUM 31 +#define XHCI_ALIGNMENT 64 +/* Generic timeout for XHCI events */ +#define XHCI_TIMEOUT 5000 +/* Max number of USB devices for any host controller - limit in section 6.1 */ +#define MAX_HC_SLOTS 256 +/* Section 5.3.3 - MaxPorts */ +#define MAX_HC_PORTS 127 + +/* Up to 16 ms to halt an HC */ +#define XHCI_MAX_HALT_USEC (16*1000) + +#define XHCI_MAX_RESET_USEC (250*1000) + +/* + * These bits are Read Only (RO) and should be saved and written to the + * registers: 0, 3, 10:13, 30 + * connect status, over-current status, port speed, and device removable. + * connect status and port speed are also sticky - meaning they're in + * the AUX well and they aren't changed by a hot, warm, or cold reset. + */ +#define XHCI_PORT_RO ((1 << 0) | (1 << 3) | (0xf << 10) | (1 << 30)) +/* + * These bits are RW; writing a 0 clears the bit, writing a 1 sets the bit: + * bits 5:8, 9, 14:15, 25:27 + * link state, port power, port indicator state, "wake on" enable state + */ +#define XHCI_PORT_RWS ((0xf << 5) | (1 << 9) | (0x3 << 14) | (0x7 << 25)) +/* + * These bits are RW; writing a 1 sets the bit, writing a 0 has no effect: + * bit 4 (port reset) + */ +#define XHCI_PORT_RW1S ((1 << 4)) +/* + * These bits are RW; writing a 1 clears the bit, writing a 0 has no effect: + * bits 1, 17, 18, 19, 20, 21, 22, 23 + * port enable/disable, and + * change bits: connect, PED, + * warm port reset changed (reserved zero for USB 2.0 ports), + * over-current, reset, link state, and L1 change + */ +#define XHCI_PORT_RW1CS ((1 << 1) | (0x7f << 17)) +/* + * Bit 16 is RW, and writing a '1' to it causes the link state control to be + * latched in + */ +#define XHCI_PORT_RW ((1 << 16)) +/* + * These bits are Reserved Zero (RsvdZ) and zero should be written to them: + * bits 2, 24, 28:31 + */ +#define XHCI_PORT_RZ ((1 << 2) | (1 << 24) | (0xf << 28)) + +/* + * XHCI Register Space. + */ +struct xhci_hccr { + uint32_t cr_capbase; + uint32_t cr_hcsparams1; + uint32_t cr_hcsparams2; + uint32_t cr_hcsparams3; + uint32_t cr_hccparams; + uint32_t cr_dboff; + uint32_t cr_rtsoff; + +/* hc_capbase bitmasks */ +/* bits 7:0 - how long is the Capabilities register */ +#define HC_LENGTH(p) XHCI_HC_LENGTH(p) +/* bits 31:16 */ +#define HC_VERSION(p) (((p) >> 16) & 0xffff) + +/* HCSPARAMS1 - hcs_params1 - bitmasks */ +/* bits 0:7, Max Device Slots */ +#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) +#define HCS_SLOTS_MASK 0xff +/* bits 8:18, Max Interrupters */ +#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) +/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */ +#define HCS_MAX_PORTS_SHIFT 24 +#define HCS_MAX_PORTS_MASK (0x7f << HCS_MAX_PORTS_SHIFT) +#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f) + +/* HCSPARAMS2 - hcs_params2 - bitmasks */ +/* bits 0:3, frames or uframes that SW needs to queue transactions + * ahead of the HW to meet periodic deadlines */ +#define HCS_IST(p) (((p) >> 0) & 0xf) +/* bits 4:7, max number of Event Ring segments */ +#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) +/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ +/* bits 27:31 number of Scratchpad buffers SW must allocate for the HW */ +#define HCS_MAX_SCRATCHPAD(p) (((p) >> 27) & 0x1f) + +/* HCSPARAMS3 - hcs_params3 - bitmasks */ +/* bits 0:7, Max U1 to U0 latency for the roothub ports */ +#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff) +/* bits 16:31, Max U2 to U0 latency for the roothub ports */ +#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff) + +/* HCCPARAMS - hcc_params - bitmasks */ +/* true: HC can use 64-bit address pointers */ +#define HCC_64BIT_ADDR(p) ((p) & (1 << 0)) +/* true: HC can do bandwidth negotiation */ +#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1)) +/* true: HC uses 64-byte Device Context structures + * FIXME 64-byte context structures aren't supported yet. + */ +#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2)) +/* true: HC has port power switches */ +#define HCC_PPC(p) ((p) & (1 << 3)) +/* true: HC has port indicators */ +#define HCS_INDICATOR(p) ((p) & (1 << 4)) +/* true: HC has Light HC Reset Capability */ +#define HCC_LIGHT_RESET(p) ((p) & (1 << 5)) +/* true: HC supports latency tolerance messaging */ +#define HCC_LTC(p) ((p) & (1 << 6)) +/* true: no secondary Stream ID Support */ +#define HCC_NSS(p) ((p) & (1 << 7)) +/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */ +#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) +/* Extended Capabilities pointer from PCI base - section 5.3.6 */ +#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p) + +/* db_off bitmask - bits 0:1 reserved */ +#define DBOFF_MASK (~0x3) + +/* run_regs_off bitmask - bits 0:4 reserved */ +#define RTSOFF_MASK (~0x1f) + +}; + +struct xhci_hcor_port_regs { + volatile uint32_t or_portsc; + volatile uint32_t or_portpmsc; + volatile uint32_t or_portli; + volatile uint32_t reserved_3; +}; + +struct xhci_hcor { + volatile uint32_t or_usbcmd; + volatile uint32_t or_usbsts; + volatile uint32_t or_pagesize; + volatile uint32_t reserved_0[2]; + volatile uint32_t or_dnctrl; + volatile uint64_t or_crcr; + volatile uint32_t reserved_1[4]; + volatile uint64_t or_dcbaap; + volatile uint32_t or_config; + volatile uint32_t reserved_2[241]; + struct xhci_hcor_port_regs portregs[CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS]; + + uint32_t reserved_4[CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS * 254]; +}; + +/* USBCMD - USB command - command bitmasks */ +/* start/stop HC execution - do not write unless HC is halted*/ +#define CMD_RUN XHCI_CMD_RUN +/* Reset HC - resets internal HC state machine and all registers (except + * PCI config regs). HC does NOT drive a USB reset on the downstream ports. + * The xHCI driver must reinitialize the xHC after setting this bit. + */ +#define CMD_RESET (1 << 1) +/* Event Interrupt Enable - a '1' allows interrupts from the host controller */ +#define CMD_EIE XHCI_CMD_EIE +/* Host System Error Interrupt Enable - get out-of-band signal for HC errors */ +#define CMD_HSEIE XHCI_CMD_HSEIE +/* bits 4:6 are reserved (and should be preserved on writes). */ +/* light reset (port status stays unchanged) - reset completed when this is 0 */ +#define CMD_LRESET (1 << 7) +/* host controller save/restore state. */ +#define CMD_CSS (1 << 8) +#define CMD_CRS (1 << 9) +/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */ +#define CMD_EWE XHCI_CMD_EWE +/* MFINDEX power management - '1' means xHC can stop MFINDEX counter if all root + * hubs are in U3 (selective suspend), disconnect, disabled, or powered-off. + * '0' means the xHC can power it off if all ports are in the disconnect, + * disabled, or powered-off state. + */ +#define CMD_PM_INDEX (1 << 11) +/* bits 12:31 are reserved (and should be preserved on writes). */ + +/* USBSTS - USB status - status bitmasks */ +/* HC not running - set to 1 when run/stop bit is cleared. */ +#define STS_HALT XHCI_STS_HALT +/* serious error, e.g. PCI parity error. The HC will clear the run/stop bit. */ +#define STS_FATAL (1 << 2) +/* event interrupt - clear this prior to clearing any IP flags in IR set*/ +#define STS_EINT (1 << 3) +/* port change detect */ +#define STS_PORT (1 << 4) +/* bits 5:7 reserved and zeroed */ +/* save state status - '1' means xHC is saving state */ +#define STS_SAVE (1 << 8) +/* restore state status - '1' means xHC is restoring state */ +#define STS_RESTORE (1 << 9) +/* true: save or restore error */ +#define STS_SRE (1 << 10) +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */ +#define STS_CNR XHCI_STS_CNR +/* true: internal Host Controller Error - SW needs to reset and reinitialize */ +#define STS_HCE (1 << 12) +/* bits 13:31 reserved and should be preserved */ + +/* + * DNCTRL - Device Notification Control Register - dev_notification bitmasks + * Generate a device notification event when the HC sees a transaction with a + * notification type that matches a bit set in this bit field. + */ +#define DEV_NOTE_MASK (0xffff) +#define ENABLE_DEV_NOTE(x) (1 << (x)) +/* Most of the device notification types should only be used for debug. + * SW does need to pay attention to function wake notifications. + */ +#define DEV_NOTE_FWAKE ENABLE_DEV_NOTE(1) + +/* CRCR - Command Ring Control Register - cmd_ring bitmasks */ +/* bit 0 is the command ring cycle state */ +/* stop ring operation after completion of the currently executing command */ +#define CMD_RING_PAUSE (1 << 1) +/* stop ring immediately - abort the currently executing command */ +#define CMD_RING_ABORT (1 << 2) +/* true: command ring is running */ +#define CMD_RING_RUNNING (1 << 3) +/* bits 4:5 reserved and should be preserved */ +/* Command Ring pointer - bit mask for the lower 32 bits. */ +#define CMD_RING_RSVD_BITS (0x3f) + +/* CONFIG - Configure Register - config_reg bitmasks */ +/* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */ +#define MAX_DEVS(p) ((p) & 0xff) +/* bits 8:31 - reserved and should be preserved */ + +/* PORTSC - Port Status and Control Register - port_status_base bitmasks */ +/* true: device connected */ +#define PORT_CONNECT (1 << 0) +/* true: port enabled */ +#define PORT_PE (1 << 1) +/* bit 2 reserved and zeroed */ +/* true: port has an over-current condition */ +#define PORT_OC (1 << 3) +/* true: port reset signaling asserted */ +#define PORT_RESET (1 << 4) +/* Port Link State - bits 5:8 + * A read gives the current link PM state of the port, + * a write with Link State Write Strobe set sets the link state. + */ +#define PORT_PLS_MASK (0xf << 5) +#define XDEV_U0 (0x0 << 5) +#define XDEV_U2 (0x2 << 5) +#define XDEV_U3 (0x3 << 5) +#define XDEV_RESUME (0xf << 5) +/* true: port has power (see HCC_PPC) */ +#define PORT_POWER (1 << 9) +/* bits 10:13 indicate device speed: + * 0 - undefined speed - port hasn't be initialized by a reset yet + * 1 - full speed + * 2 - low speed + * 3 - high speed + * 4 - super speed + * 5-15 reserved + */ +#define DEV_SPEED_MASK (0xf << 10) +#define XDEV_FS (0x1 << 10) +#define XDEV_LS (0x2 << 10) +#define XDEV_HS (0x3 << 10) +#define XDEV_SS (0x4 << 10) +#define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0<<10)) +#define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS) +#define DEV_LOWSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_LS) +#define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS) +#define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS) +/* Bits 20:23 in the Slot Context are the speed for the device */ +#define SLOT_SPEED_FS (XDEV_FS << 10) +#define SLOT_SPEED_LS (XDEV_LS << 10) +#define SLOT_SPEED_HS (XDEV_HS << 10) +#define SLOT_SPEED_SS (XDEV_SS << 10) +/* Port Indicator Control */ +#define PORT_LED_OFF (0 << 14) +#define PORT_LED_AMBER (1 << 14) +#define PORT_LED_GREEN (2 << 14) +#define PORT_LED_MASK (3 << 14) +/* Port Link State Write Strobe - set this when changing link state */ +#define PORT_LINK_STROBE (1 << 16) +/* true: connect status change */ +#define PORT_CSC (1 << 17) +/* true: port enable change */ +#define PORT_PEC (1 << 18) +/* true: warm reset for a USB 3.0 device is done. A "hot" reset puts the port + * into an enabled state, and the device into the default state. A "warm" reset + * also resets the link, forcing the device through the link training sequence. + * SW can also look at the Port Reset register to see when warm reset is done. + */ +#define PORT_WRC (1 << 19) +/* true: over-current change */ +#define PORT_OCC (1 << 20) +/* true: reset change - 1 to 0 transition of PORT_RESET */ +#define PORT_RC (1 << 21) +/* port link status change - set on some port link state transitions: + * Transition Reason + * -------------------------------------------------------------------------- + * - U3 to Resume Wakeup signaling from a device + * - Resume to Recovery to U0 USB 3.0 device resume + * - Resume to U0 USB 2.0 device resume + * - U3 to Recovery to U0 Software resume of USB 3.0 device complete + * - U3 to U0 Software resume of USB 2.0 device complete + * - U2 to U0 L1 resume of USB 2.1 device complete + * - U0 to U0 (???) L1 entry rejection by USB 2.1 device + * - U0 to disabled L1 entry error with USB 2.1 device + * - Any state to inactive Error on USB 3.0 port + */ +#define PORT_PLC (1 << 22) +/* port configure error change - port failed to configure its link partner */ +#define PORT_CEC (1 << 23) +/* bit 24 reserved */ +/* wake on connect (enable) */ +#define PORT_WKCONN_E (1 << 25) +/* wake on disconnect (enable) */ +#define PORT_WKDISC_E (1 << 26) +/* wake on over-current (enable) */ +#define PORT_WKOC_E (1 << 27) +/* bits 28:29 reserved */ +/* true: device is removable - for USB 3.0 roothub emulation */ +#define PORT_DEV_REMOVE (1 << 30) +/* Initiate a warm port reset - complete when PORT_WRC is '1' */ +#define PORT_WR (1 << 31) + +/* We mark duplicate entries with -1 */ +#define DUPLICATE_ENTRY ((u8)(-1)) + +/* Port Power Management Status and Control - port_power_base bitmasks */ +/* Inactivity timer value for transitions into U1, in microseconds. + * Timeout can be up to 127us. 0xFF means an infinite timeout. + */ +#define PORT_U1_TIMEOUT(p) ((p) & 0xff) +/* Inactivity timer value for transitions into U2 */ +#define PORT_U2_TIMEOUT(p) (((p) & 0xff) << 8) +/* Bits 24:31 for port testing */ + +/* USB2 Protocol PORTSPMSC */ +#define PORT_L1S_MASK 7 +#define PORT_L1S_SUCCESS 1 +#define PORT_RWE (1 << 3) +#define PORT_HIRD(p) (((p) & 0xf) << 4) +#define PORT_HIRD_MASK (0xf << 4) +#define PORT_L1DS(p) (((p) & 0xff) << 8) +#define PORT_HLE (1 << 16) + +/** +* struct xhci_intr_reg - Interrupt Register Set +* @irq_pending: IMAN - Interrupt Management Register. Used to enable +* interrupts and check for pending interrupts. +* @irq_control: IMOD - Interrupt Moderation Register. +* Used to throttle interrupts. +* @erst_size: Number of segments in the + Event Ring Segment Table (ERST). +* @erst_base: ERST base address. +* @erst_dequeue: Event ring dequeue pointer. +* +* Each interrupter (defined by a MSI-X vector) has an event ring and an Event +* Ring Segment Table (ERST) associated with it. +* The event ring is comprised of multiple segments of the same size. +* The HC places events on the ring and "updates the Cycle bit in the TRBs to +* indicate to software the current position of the Enqueue Pointer." +* The HCD (Linux) processes those events and updates the dequeue pointer. +*/ +struct xhci_intr_reg { + volatile __le32 irq_pending; + volatile __le32 irq_control; + volatile __le32 erst_size; + volatile __le32 rsvd; + volatile __le64 erst_base; + volatile __le64 erst_dequeue; +}; + +/* irq_pending bitmasks */ +#define ER_IRQ_PENDING(p) ((p) & 0x1) +/* bits 2:31 need to be preserved */ +/* THIS IS BUGGY - FIXME - IP IS WRITE 1 TO CLEAR */ +#define ER_IRQ_CLEAR(p) ((p) & 0xfffffffe) +#define ER_IRQ_ENABLE(p) ((ER_IRQ_CLEAR(p)) | 0x2) +#define ER_IRQ_DISABLE(p) ((ER_IRQ_CLEAR(p)) & ~(0x2)) + +/* irq_control bitmasks */ +/* Minimum interval between interrupts (in 250ns intervals). The interval + * between interrupts will be longer if there are no events on the event ring. + * Default is 4000 (1 ms). + */ +#define ER_IRQ_INTERVAL_MASK (0xffff) +/* Counter used to count down the time to the next interrupt - HW use only */ +#define ER_IRQ_COUNTER_MASK (0xffff << 16) + +/* erst_size bitmasks */ +/* Preserve bits 16:31 of erst_size */ +#define ERST_SIZE_MASK (0xffff << 16) + +/* erst_dequeue bitmasks */ +/* Dequeue ERST Segment Index (DESI) - Segment number (or alias) + * where the current dequeue pointer lies. This is an optional HW hint. + */ +#define ERST_DESI_MASK (0x7) +/* Event Handler Busy (EHB) - is the event ring scheduled to be serviced by + * a work queue (or delayed service routine)? + */ +#define ERST_EHB (1 << 3) +#define ERST_PTR_MASK (0xf) + +/** + * struct xhci_run_regs + * @microframe_index: MFINDEX - current microframe number + * + * Section 5.5 Host Controller Runtime Registers: + * "Software should read and write these registers using only Dword (32 bit) + * or larger accesses" + */ +struct xhci_run_regs { + __le32 microframe_index; + __le32 rsvd[7]; + struct xhci_intr_reg ir_set[128]; +}; + +/** + * struct doorbell_array + * + * Bits 0 - 7: Endpoint target + * Bits 8 - 15: RsvdZ + * Bits 16 - 31: Stream ID + * + * Section 5.6 + */ +struct xhci_doorbell_array { + volatile __le32 doorbell[256]; +}; + +#define DB_VALUE(ep, stream) ((((ep) + 1) & 0xff) | ((stream) << 16)) +#define DB_VALUE_HOST 0x00000000 + +/** + * struct xhci_protocol_caps + * @revision: major revision, minor revision, capability ID, + * and next capability pointer. + * @name_string: Four ASCII characters to say which spec this xHC + * follows, typically "USB ". + * @port_info: Port offset, count, and protocol-defined information. + */ +struct xhci_protocol_caps { + u32 revision; + u32 name_string; + u32 port_info; +}; + +#define XHCI_EXT_PORT_MAJOR(x) (((x) >> 24) & 0xff) +#define XHCI_EXT_PORT_OFF(x) ((x) & 0xff) +#define XHCI_EXT_PORT_COUNT(x) (((x) >> 8) & 0xff) + +/** + * struct xhci_container_ctx + * @type: Type of context. Used to calculated offsets to contained contexts. + * @size: Size of the context data + * @bytes: The raw context data given to HW + * @dma: dma address of the bytes + * + * Represents either a Device or Input context. Holds a pointer to the raw + * memory used for the context (bytes) and dma address of it (dma). + */ +struct xhci_container_ctx { + unsigned type; +#define XHCI_CTX_TYPE_DEVICE 0x1 +#define XHCI_CTX_TYPE_INPUT 0x2 + + int size; + u8 *bytes; +}; + +/** + * struct xhci_slot_ctx + * @dev_info: Route string, device speed, hub info, and last valid endpoint + * @dev_info2: Max exit latency for device number, root hub port number + * @tt_info: tt_info is used to construct split transaction tokens + * @dev_state: slot state and device address + * + * Slot Context - section 6.2.1.1. This assumes the HC uses 32-byte context + * structures. If the HC uses 64-byte contexts, there is an additional 32 bytes + * reserved at the end of the slot context for HC internal use. + */ +struct xhci_slot_ctx { + __le32 dev_info; + __le32 dev_info2; + __le32 tt_info; + __le32 dev_state; + /* offset 0x10 to 0x1f reserved for HC internal use */ + __le32 reserved[4]; +}; + +/* dev_info bitmasks */ +/* Route String - 0:19 */ +#define ROUTE_STRING_MASK (0xfffff) +/* Device speed - values defined by PORTSC Device Speed field - 20:23 */ +#define DEV_SPEED (0xf << 20) +/* bit 24 reserved */ +/* Is this LS/FS device connected through a HS hub? - bit 25 */ +#define DEV_MTT (0x1 << 25) +/* Set if the device is a hub - bit 26 */ +#define DEV_HUB (0x1 << 26) +/* Index of the last valid endpoint context in this device context - 27:31 */ +#define LAST_CTX_MASK (0x1f << 27) +#define LAST_CTX(p) ((p) << 27) +#define LAST_CTX_TO_EP_NUM(p) (((p) >> 27) - 1) +#define SLOT_FLAG (1 << 0) +#define EP0_FLAG (1 << 1) + +/* dev_info2 bitmasks */ +/* Max Exit Latency (ms) - worst case time to wake up all links in dev path */ +#define MAX_EXIT (0xffff) +/* Root hub port number that is needed to access the USB device */ +#define ROOT_HUB_PORT(p) (((p) & 0xff) << 16) +#define ROOT_HUB_PORT_MASK (0xff) +#define ROOT_HUB_PORT_SHIFT (16) +#define DEVINFO_TO_ROOT_HUB_PORT(p) (((p) >> 16) & 0xff) +/* Maximum number of ports under a hub device */ +#define XHCI_MAX_PORTS(p) (((p) & 0xff) << 24) + +/* tt_info bitmasks */ +/* + * TT Hub Slot ID - for low or full speed devices attached to a high-speed hub + * The Slot ID of the hub that isolates the high speed signaling from + * this low or full-speed device. '0' if attached to root hub port. + */ +#define TT_SLOT (0xff) +/* + * The number of the downstream facing port of the high-speed hub + * '0' if the device is not low or full speed. + */ +#define TT_PORT (0xff << 8) +#define TT_THINK_TIME(p) (((p) & 0x3) << 16) + +/* dev_state bitmasks */ +/* USB device address - assigned by the HC */ +#define DEV_ADDR_MASK (0xff) +/* bits 8:26 reserved */ +/* Slot state */ +#define SLOT_STATE (0x1f << 27) +#define GET_SLOT_STATE(p) (((p) & (0x1f << 27)) >> 27) + +#define SLOT_STATE_DISABLED 0 +#define SLOT_STATE_ENABLED SLOT_STATE_DISABLED +#define SLOT_STATE_DEFAULT 1 +#define SLOT_STATE_ADDRESSED 2 +#define SLOT_STATE_CONFIGURED 3 + +/** + * struct xhci_ep_ctx + * @ep_info: endpoint state, streams, mult, and interval information. + * @ep_info2: information on endpoint type, max packet size, max burst size, + * error count, and whether the HC will force an event for all + * transactions. + * @deq: 64-bit ring dequeue pointer address. If the endpoint only + * defines one stream, this points to the endpoint transfer ring. + * Otherwise, it points to a stream context array, which has a + * ring pointer for each flow. + * @tx_info: + * Average TRB lengths for the endpoint ring and + * max payload within an Endpoint Service Interval Time (ESIT). + * + * Endpoint Context - section 6.2.1.2.This assumes the HC uses 32-byte context + * structures.If the HC uses 64-byte contexts, there is an additional 32 bytes + * reserved at the end of the endpoint context for HC internal use. + */ +struct xhci_ep_ctx { + __le32 ep_info; + __le32 ep_info2; + __le64 deq; + __le32 tx_info; + /* offset 0x14 - 0x1f reserved for HC internal use */ + __le32 reserved[3]; +}; + +/* ep_info bitmasks */ +/* + * Endpoint State - bits 0:2 + * 0 - disabled + * 1 - running + * 2 - halted due to halt condition - ok to manipulate endpoint ring + * 3 - stopped + * 4 - TRB error + * 5-7 - reserved + */ +#define EP_STATE_MASK (0xf) +#define EP_STATE_DISABLED 0 +#define EP_STATE_RUNNING 1 +#define EP_STATE_HALTED 2 +#define EP_STATE_STOPPED 3 +#define EP_STATE_ERROR 4 +/* Mult - Max number of burtst within an interval, in EP companion desc. */ +#define EP_MULT(p) (((p) & 0x3) << 8) +#define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3) +/* bits 10:14 are Max Primary Streams */ +/* bit 15 is Linear Stream Array */ +/* Interval - period between requests to an endpoint - 125u increments. */ +#define EP_INTERVAL(p) (((p) & 0xff) << 16) +#define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) +#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff) +#define EP_MAXPSTREAMS_MASK (0x1f << 10) +#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) +/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ +#define EP_HAS_LSA (1 << 15) + +/* ep_info2 bitmasks */ +/* + * Force Event - generate transfer events for all TRBs for this endpoint + * This will tell the HC to ignore the IOC and ISP flags (for debugging only). + */ +#define FORCE_EVENT (0x1) +#define ERROR_COUNT(p) (((p) & 0x3) << 1) +#define ERROR_COUNT_SHIFT (1) +#define ERROR_COUNT_MASK (0x3) +#define CTX_TO_EP_TYPE(p) (((p) >> 3) & 0x7) +#define EP_TYPE(p) ((p) << 3) +#define EP_TYPE_SHIFT (3) +#define ISOC_OUT_EP 1 +#define BULK_OUT_EP 2 +#define INT_OUT_EP 3 +#define CTRL_EP 4 +#define ISOC_IN_EP 5 +#define BULK_IN_EP 6 +#define INT_IN_EP 7 +/* bit 6 reserved */ +/* bit 7 is Host Initiate Disable - for disabling stream selection */ +#define MAX_BURST(p) (((p)&0xff) << 8) +#define MAX_BURST_MASK (0xff) +#define MAX_BURST_SHIFT (8) +#define CTX_TO_MAX_BURST(p) (((p) >> 8) & 0xff) +#define MAX_PACKET(p) (((p)&0xffff) << 16) +#define MAX_PACKET_MASK (0xffff) +#define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff) +#define MAX_PACKET_SHIFT (16) + +/* Get max packet size from ep desc. Bit 10..0 specify the max packet size. + * USB2.0 spec 9.6.6. + */ +#define GET_MAX_PACKET(p) ((p) & 0x7ff) + +/* tx_info bitmasks */ +#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) +#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) + +/* deq bitmasks */ +#define EP_CTX_CYCLE_MASK (1 << 0) + + +/** + * struct xhci_input_control_context + * Input control context; see section 6.2.5. + * + * @drop_context: set the bit of the endpoint context you want to disable + * @add_context: set the bit of the endpoint context you want to enable + */ +struct xhci_input_control_ctx { + volatile __le32 drop_flags; + volatile __le32 add_flags; + __le32 rsvd2[6]; +}; + + +/** + * struct xhci_device_context_array + * @dev_context_ptr array of 64-bit DMA addresses for device contexts + */ +struct xhci_device_context_array { + /* 64-bit device addresses; we only write 32-bit addresses */ + __le64 dev_context_ptrs[MAX_HC_SLOTS]; +}; +/* TODO: write function to set the 64-bit device DMA address */ +/* + * TODO: change this to be dynamically sized at HC mem init time since the HC + * might not be able to handle the maximum number of devices possible. + */ + + +struct xhci_transfer_event { + /* 64-bit buffer address, or immediate data */ + __le64 buffer; + __le32 transfer_len; + /* This field is interpreted differently based on the type of TRB */ + volatile __le32 flags; +}; + +/* Transfer event TRB length bit mask */ +/* bits 0:23 */ +#define EVENT_TRB_LEN(p) ((p) & 0xffffff) + +/** Transfer Event bit fields **/ +#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f) + +/* Completion Code - only applicable for some types of TRBs */ +#define COMP_CODE_MASK (0xff << 24) +#define COMP_CODE_SHIFT (24) +#define GET_COMP_CODE(p) (((p) & COMP_CODE_MASK) >> 24) + +typedef enum { + COMP_SUCCESS = 1, + /* Data Buffer Error */ + COMP_DB_ERR, /* 2 */ + /* Babble Detected Error */ + COMP_BABBLE, /* 3 */ + /* USB Transaction Error */ + COMP_TX_ERR, /* 4 */ + /* TRB Error - some TRB field is invalid */ + COMP_TRB_ERR, /* 5 */ + /* Stall Error - USB device is stalled */ + COMP_STALL, /* 6 */ + /* Resource Error - HC doesn't have memory for that device configuration */ + COMP_ENOMEM, /* 7 */ + /* Bandwidth Error - not enough room in schedule for this dev config */ + COMP_BW_ERR, /* 8 */ + /* No Slots Available Error - HC ran out of device slots */ + COMP_ENOSLOTS, /* 9 */ + /* Invalid Stream Type Error */ + COMP_STREAM_ERR, /* 10 */ + /* Slot Not Enabled Error - doorbell rung for disabled device slot */ + COMP_EBADSLT, /* 11 */ + /* Endpoint Not Enabled Error */ + COMP_EBADEP,/* 12 */ + /* Short Packet */ + COMP_SHORT_TX, /* 13 */ + /* Ring Underrun - doorbell rung for an empty isoc OUT ep ring */ + COMP_UNDERRUN, /* 14 */ + /* Ring Overrun - isoc IN ep ring is empty when ep is scheduled to RX */ + COMP_OVERRUN, /* 15 */ + /* Virtual Function Event Ring Full Error */ + COMP_VF_FULL, /* 16 */ + /* Parameter Error - Context parameter is invalid */ + COMP_EINVAL, /* 17 */ + /* Bandwidth Overrun Error - isoc ep exceeded its allocated bandwidth */ + COMP_BW_OVER,/* 18 */ + /* Context State Error - illegal context state transition requested */ + COMP_CTX_STATE,/* 19 */ + /* No Ping Response Error - HC didn't get PING_RESPONSE in time to TX */ + COMP_PING_ERR,/* 20 */ + /* Event Ring is full */ + COMP_ER_FULL,/* 21 */ + /* Incompatible Device Error */ + COMP_DEV_ERR,/* 22 */ + /* Missed Service Error - HC couldn't service an isoc ep within interval */ + COMP_MISSED_INT,/* 23 */ + /* Successfully stopped command ring */ + COMP_CMD_STOP, /* 24 */ + /* Successfully aborted current command and stopped command ring */ + COMP_CMD_ABORT, /* 25 */ + /* Stopped - transfer was terminated by a stop endpoint command */ + COMP_STOP,/* 26 */ + /* Same as COMP_EP_STOPPED, but the transferred length in the event + * is invalid */ + COMP_STOP_INVAL, /* 27*/ + /* Control Abort Error - Debug Capability - control pipe aborted */ + COMP_DBG_ABORT, /* 28 */ + /* Max Exit Latency Too Large Error */ + COMP_MEL_ERR,/* 29 */ + /* TRB type 30 reserved */ + /* Isoc Buffer Overrun - an isoc IN ep sent more data than could fit in TD */ + COMP_BUFF_OVER = 31, + /* Event Lost Error - xHC has an "internal event overrun condition" */ + COMP_ISSUES, /* 32 */ + /* Undefined Error - reported when other error codes don't apply */ + COMP_UNKNOWN, /* 33 */ + /* Invalid Stream ID Error */ + COMP_STRID_ERR, /* 34 */ + /* Secondary Bandwidth Error - may be returned by a Configure Endpoint cmd */ + COMP_2ND_BW_ERR, /* 35 */ + /* Split Transaction Error */ + COMP_SPLIT_ERR /* 36 */ + +} xhci_comp_code; + +struct xhci_link_trb { + /* 64-bit segment pointer*/ + volatile __le64 segment_ptr; + volatile __le32 intr_target; + volatile __le32 control; +}; + +/* control bitfields */ +#define LINK_TOGGLE (0x1 << 1) + +/* Command completion event TRB */ +struct xhci_event_cmd { + /* Pointer to command TRB, or the value passed by the event data trb */ + volatile __le64 cmd_trb; + volatile __le32 status; + volatile __le32 flags; +}; + +/* flags bitmasks */ +/* bits 16:23 are the virtual function ID */ +/* bits 24:31 are the slot ID */ +#define TRB_TO_SLOT_ID(p) (((p) & (0xff << 24)) >> 24) +#define TRB_TO_SLOT_ID_SHIFT (24) +#define TRB_TO_SLOT_ID_MASK (0xff << TRB_TO_SLOT_ID_SHIFT) +#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24) +#define SLOT_ID_FOR_TRB_MASK (0xff) +#define SLOT_ID_FOR_TRB_SHIFT (24) + +/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */ +#define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1) +#define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16) + +#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23) +#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23) +#define LAST_EP_INDEX 30 + +/* Set TR Dequeue Pointer command TRB fields */ +#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16)) +#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16) + + +/* Port Status Change Event TRB fields */ +/* Port ID - bits 31:24 */ +#define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24) +#define PORT_ID_SHIFT (24) +#define PORT_ID_MASK (0xff << PORT_ID_SHIFT) + +/* Normal TRB fields */ +/* transfer_len bitmasks - bits 0:16 */ +#define TRB_LEN(p) ((p) & 0x1ffff) +#define TRB_LEN_MASK (0x1ffff) +/* Interrupter Target - which MSI-X vector to target the completion event at */ +#define TRB_INTR_TARGET_SHIFT (22) +#define TRB_INTR_TARGET_MASK (0x3ff) +#define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) +#define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) +#define TRB_TBC(p) (((p) & 0x3) << 7) +#define TRB_TLBPC(p) (((p) & 0xf) << 16) + +/* Cycle bit - indicates TRB ownership by HC or HCD */ +#define TRB_CYCLE (1<<0) +/* + * Force next event data TRB to be evaluated before task switch. + * Used to pass OS data back after a TD completes. + */ +#define TRB_ENT (1<<1) +/* Interrupt on short packet */ +#define TRB_ISP (1<<2) +/* Set PCIe no snoop attribute */ +#define TRB_NO_SNOOP (1<<3) +/* Chain multiple TRBs into a TD */ +#define TRB_CHAIN (1<<4) +/* Interrupt on completion */ +#define TRB_IOC (1<<5) +/* The buffer pointer contains immediate data */ +#define TRB_IDT (1<<6) + +/* Block Event Interrupt */ +#define TRB_BEI (1<<9) + +/* Control transfer TRB specific fields */ +#define TRB_DIR_IN (1<<16) +#define TRB_TX_TYPE(p) ((p) << 16) +#define TRB_TX_TYPE_SHIFT (16) +#define TRB_DATA_OUT 2 +#define TRB_DATA_IN 3 + +/* Isochronous TRB specific fields */ +#define TRB_SIA (1 << 31) + +struct xhci_generic_trb { + volatile __le32 field[4]; +}; + +union xhci_trb { + struct xhci_link_trb link; + struct xhci_transfer_event trans_event; + struct xhci_event_cmd event_cmd; + struct xhci_generic_trb generic; +}; + +/* TRB bit mask */ +#define TRB_TYPE_BITMASK (0xfc00) +#define TRB_TYPE(p) ((p) << 10) +#define TRB_TYPE_SHIFT (10) +#define TRB_FIELD_TO_TYPE(p) (((p) & TRB_TYPE_BITMASK) >> 10) + +/* TRB type IDs */ +typedef enum { + /* bulk, interrupt, isoc scatter/gather, and control data stage */ + TRB_NORMAL = 1, + /* setup stage for control transfers */ + TRB_SETUP, /* 2 */ + /* data stage for control transfers */ + TRB_DATA, /* 3 */ + /* status stage for control transfers */ + TRB_STATUS, /* 4 */ + /* isoc transfers */ + TRB_ISOC, /* 5 */ + /* TRB for linking ring segments */ + TRB_LINK, /* 6 */ + /* TRB for EVENT DATA */ + TRB_EVENT_DATA, /* 7 */ + /* Transfer Ring No-op (not for the command ring) */ + TRB_TR_NOOP, /* 8 */ + /* Command TRBs */ + /* Enable Slot Command */ + TRB_ENABLE_SLOT, /* 9 */ + /* Disable Slot Command */ + TRB_DISABLE_SLOT, /* 10 */ + /* Address Device Command */ + TRB_ADDR_DEV, /* 11 */ + /* Configure Endpoint Command */ + TRB_CONFIG_EP, /* 12 */ + /* Evaluate Context Command */ + TRB_EVAL_CONTEXT, /* 13 */ + /* Reset Endpoint Command */ + TRB_RESET_EP, /* 14 */ + /* Stop Transfer Ring Command */ + TRB_STOP_RING, /* 15 */ + /* Set Transfer Ring Dequeue Pointer Command */ + TRB_SET_DEQ, /* 16 */ + /* Reset Device Command */ + TRB_RESET_DEV, /* 17 */ + /* Force Event Command (opt) */ + TRB_FORCE_EVENT, /* 18 */ + /* Negotiate Bandwidth Command (opt) */ + TRB_NEG_BANDWIDTH, /* 19 */ + /* Set Latency Tolerance Value Command (opt) */ + TRB_SET_LT, /* 20 */ + /* Get port bandwidth Command */ + TRB_GET_BW, /* 21 */ + /* Force Header Command - generate a transaction or link management packet */ + TRB_FORCE_HEADER, /* 22 */ + /* No-op Command - not for transfer rings */ + TRB_CMD_NOOP, /* 23 */ + /* TRB IDs 24-31 reserved */ + /* Event TRBS */ + /* Transfer Event */ + TRB_TRANSFER = 32, + /* Command Completion Event */ + TRB_COMPLETION, /* 33 */ + /* Port Status Change Event */ + TRB_PORT_STATUS, /* 34 */ + /* Bandwidth Request Event (opt) */ + TRB_BANDWIDTH_EVENT, /* 35 */ + /* Doorbell Event (opt) */ + TRB_DOORBELL, /* 36 */ + /* Host Controller Event */ + TRB_HC_EVENT, /* 37 */ + /* Device Notification Event - device sent function wake notification */ + TRB_DEV_NOTE, /* 38 */ + /* MFINDEX Wrap Event - microframe counter wrapped */ + TRB_MFINDEX_WRAP, /* 39 */ + /* TRB IDs 40-47 reserved, 48-63 is vendor-defined */ + /* Nec vendor-specific command completion event. */ + TRB_NEC_CMD_COMP = 48, /* 48 */ + /* Get NEC firmware revision. */ + TRB_NEC_GET_FW, /* 49 */ +} trb_type; + +#define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) +/* Above, but for __le32 types -- can avoid work by swapping constants: */ +#define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \ + cpu_to_le32(TRB_TYPE(TRB_LINK))) +#define TRB_TYPE_NOOP_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \ + cpu_to_le32(TRB_TYPE(TRB_TR_NOOP))) + +/* + * TRBS_PER_SEGMENT must be a multiple of 4, + * since the command ring is 64-byte aligned. + * It must also be greater than 16. + */ +#define TRBS_PER_SEGMENT 64 +/* Allow two commands + a link TRB, along with any reserved command TRBs */ +#define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3) +#define SEGMENT_SIZE (TRBS_PER_SEGMENT*16) +/* SEGMENT_SHIFT should be log2(SEGMENT_SIZE). + * Change this if you change TRBS_PER_SEGMENT! + */ +#define SEGMENT_SHIFT 10 +/* TRB buffer pointers can't cross 64KB boundaries */ +#define TRB_MAX_BUFF_SHIFT 16 +#define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT) + +struct xhci_segment { + union xhci_trb *trbs; + /* private to HCD */ + struct xhci_segment *next; +}; + +struct xhci_ring { + struct xhci_segment *first_seg; + union xhci_trb *enqueue; + struct xhci_segment *enq_seg; + union xhci_trb *dequeue; + struct xhci_segment *deq_seg; + /* + * Write the cycle state into the TRB cycle field to give ownership of + * the TRB to the host controller (if we are the producer), or to check + * if we own the TRB (if we are the consumer). See section 4.9.1. + */ + volatile u32 cycle_state; + unsigned int num_segs; +}; + +struct xhci_erst_entry { + /* 64-bit event ring segment address */ + __le64 seg_addr; + __le32 seg_size; + /* Set to zero */ + __le32 rsvd; +}; + +struct xhci_erst { + struct xhci_erst_entry *entries; + unsigned int num_entries; + /* Num entries the ERST can contain */ + unsigned int erst_size; +}; + +/* + * Each segment table entry is 4*32bits long. 1K seems like an ok size: + * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, + * meaning 64 ring segments. + * Initial allocated size of the ERST, in number of entries */ +#define ERST_NUM_SEGS 3 +/* Initial number of event segment rings allocated */ +#define ERST_ENTRIES 3 +/* Initial allocated size of the ERST, in number of entries */ +#define ERST_SIZE 64 +/* Poll every 60 seconds */ +#define POLL_TIMEOUT 60 +/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */ +#define XHCI_STOP_EP_CMD_TIMEOUT 5 +/* XXX: Make these module parameters */ + +struct xhci_virt_ep { + struct xhci_ring *ring; + unsigned int ep_state; +#define SET_DEQ_PENDING (1 << 0) +#define EP_HALTED (1 << 1) /* For stall handling */ +#define EP_HALT_PENDING (1 << 2) /* For URB cancellation */ +/* Transitioning the endpoint to using streams, don't enqueue URBs */ +#define EP_GETTING_STREAMS (1 << 3) +#define EP_HAS_STREAMS (1 << 4) +/* Transitioning the endpoint to not using streams, don't enqueue URBs */ +#define EP_GETTING_NO_STREAMS (1 << 5) +}; + +#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) + +struct xhci_virt_device { + struct usb_device *udev; + /* + * Commands to the hardware are passed an "input context" that + * tells the hardware what to change in its data structures. + * The hardware will return changes in an "output context" that + * software must allocate for the hardware. We need to keep + * track of input and output contexts separately because + * these commands might fail and we don't trust the hardware. + */ + struct xhci_container_ctx *out_ctx; + /* Used for addressing devices and configuration changes */ + struct xhci_container_ctx *in_ctx; + /* Rings saved to ensure old alt settings can be re-instated */ +#define XHCI_MAX_RINGS_CACHED 31 + struct xhci_virt_ep eps[31]; +}; + +/* TODO: copied from ehci.h - can be refactored? */ +/* xHCI spec says all registers are little endian */ +static inline unsigned int xhci_readl(uint32_t volatile *regs) +{ + return readl(regs); +} + +static inline void xhci_writel(uint32_t volatile *regs, const unsigned int val) +{ + writel(val, regs); +} + +/* + * Registers should always be accessed with double word or quad word accesses. + * Some xHCI implementations may support 64-bit address pointers. Registers + * with 64-bit address pointers should be written to with dword accesses by + * writing the low dword first (ptr[0]), then the high dword (ptr[1]) second. + * xHCI implementations that do not support 64-bit address pointers will ignore + * the high dword, and write order is irrelevant. + */ +static inline u64 xhci_readq(__le64 volatile *regs) +{ + __u32 *ptr = (__u32 *)regs; + u64 val_lo = readl(ptr); + u64 val_hi = readl(ptr + 1); + return val_lo + (val_hi << 32); +} + +static inline void xhci_writeq(__le64 volatile *regs, const u64 val) +{ + __u32 *ptr = (__u32 *)regs; + u32 val_lo = lower_32_bits(val); + /* FIXME */ + u32 val_hi = 0; + writel(val_lo, ptr); + writel(val_hi, ptr + 1); +} + +int xhci_hcd_init(int index, struct xhci_hccr **ret_hccr, + struct xhci_hcor **ret_hcor); +void xhci_hcd_stop(int index); + + +/************************************************************* + EXTENDED CAPABILITY DEFINITIONS +*************************************************************/ +/* Up to 16 ms to halt an HC */ +#define XHCI_MAX_HALT_USEC (16*1000) +/* HC not running - set to 1 when run/stop bit is cleared. */ +#define XHCI_STS_HALT (1 << 0) + +/* HCCPARAMS offset from PCI base address */ +#define XHCI_HCC_PARAMS_OFFSET 0x10 +/* HCCPARAMS contains the first extended capability pointer */ +#define XHCI_HCC_EXT_CAPS(p) (((p)>>16)&0xffff) + +/* Command and Status registers offset from the Operational Registers address */ +#define XHCI_CMD_OFFSET 0x00 +#define XHCI_STS_OFFSET 0x04 + +#define XHCI_MAX_EXT_CAPS 50 + +/* Capability Register */ +/* bits 7:0 - how long is the Capabilities register */ +#define XHCI_HC_LENGTH(p) (((p) >> 00) & 0x00ff) + +/* Extended capability register fields */ +#define XHCI_EXT_CAPS_ID(p) (((p) >> 0) & 0xff) +#define XHCI_EXT_CAPS_NEXT(p) (((p) >> 8) & 0xff) +#define XHCI_EXT_CAPS_VAL(p) ((p) >> 16) +/* Extended capability IDs - ID 0 reserved */ +#define XHCI_EXT_CAPS_LEGACY 1 +#define XHCI_EXT_CAPS_PROTOCOL 2 +#define XHCI_EXT_CAPS_PM 3 +#define XHCI_EXT_CAPS_VIRT 4 +#define XHCI_EXT_CAPS_ROUTE 5 +/* IDs 6-9 reserved */ +#define XHCI_EXT_CAPS_DEBUG 10 +/* USB Legacy Support Capability - section 7.1.1 */ +#define XHCI_HC_BIOS_OWNED (1 << 16) +#define XHCI_HC_OS_OWNED (1 << 24) + +/* USB Legacy Support Capability - section 7.1.1 */ +/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ +#define XHCI_LEGACY_SUPPORT_OFFSET (0x00) + +/* USB Legacy Support Control and Status Register - section 7.1.2 */ +/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ +#define XHCI_LEGACY_CONTROL_OFFSET (0x04) +/* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ +#define XHCI_LEGACY_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17)) + +/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */ +#define XHCI_L1C (1 << 16) + +/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ +#define XHCI_HLC (1 << 19) + +/* command register values to disable interrupts and halt the HC */ +/* start/stop HC execution - do not write unless HC is halted*/ +#define XHCI_CMD_RUN (1 << 0) +/* Event Interrupt Enable - get irq when EINT bit is set in USBSTS register */ +#define XHCI_CMD_EIE (1 << 2) +/* Host System Error Interrupt Enable - get irq when HSEIE bit set in USBSTS */ +#define XHCI_CMD_HSEIE (1 << 3) +/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */ +#define XHCI_CMD_EWE (1 << 10) + +#define XHCI_IRQS (XHCI_CMD_EIE | XHCI_CMD_HSEIE | XHCI_CMD_EWE) + +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */ +#define XHCI_STS_CNR (1 << 11) + +struct xhci_ctrl { + struct xhci_hccr *hccr; /* R/O registers, not need for volatile */ + struct xhci_hcor *hcor; + struct xhci_doorbell_array *dba; + struct xhci_run_regs *run_regs; + struct xhci_device_context_array *dcbaa \ + __attribute__ ((aligned(ARCH_DMA_MINALIGN))); + struct xhci_ring *event_ring; + struct xhci_ring *cmd_ring; + struct xhci_ring *transfer_ring; + struct xhci_segment *seg; + struct xhci_intr_reg *ir_set; + struct xhci_erst erst; + struct xhci_erst_entry entry[ERST_NUM_SEGS]; + struct xhci_virt_device *devs[MAX_HC_SLOTS]; + int rootdev; +}; + +unsigned long trb_addr(struct xhci_segment *seg, union xhci_trb *trb); +struct xhci_input_control_ctx + *xhci_get_input_control_ctx(struct xhci_container_ctx *ctx); +struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *ctx); +struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *ctx, + unsigned int ep_index); +void xhci_endpoint_copy(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx, + unsigned int ep_index); +void xhci_slot_copy(struct xhci_ctrl *ctrl, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx); +void xhci_setup_addressable_virt_dev(struct usb_device *udev); +void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, + u32 slot_id, u32 ep_index, trb_type cmd); +void xhci_acknowledge_event(struct xhci_ctrl *ctrl); +union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected); +int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, + int length, void *buffer); +int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, + struct devrequest *req, int length, void *buffer); +int xhci_check_maxpacket(struct usb_device *udev); +void xhci_flush_cache(uint32_t addr, u32 type_len); +void xhci_inval_cache(uint32_t addr, u32 type_len); +void xhci_cleanup(struct xhci_ctrl *ctrl); +struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs); +int xhci_alloc_virt_device(struct usb_device *udev); +int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, + struct xhci_hcor *hcor); + +#endif /* HOST_XHCI_H_ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/Makefile b/qemu/roms/u-boot/drivers/usb/musb-new/Makefile new file mode 100644 index 000000000..3facf0fc1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/Makefile @@ -0,0 +1,14 @@ +# +# for USB OTG silicon based on Mentor Graphics INVENTRA designs +# + +obj-$(CONFIG_MUSB_GADGET) += musb_gadget.o musb_gadget_ep0.o musb_core.o +obj-$(CONFIG_MUSB_GADGET) += musb_uboot.o +obj-$(CONFIG_MUSB_HOST) += musb_host.o musb_core.o musb_uboot.o +obj-$(CONFIG_USB_MUSB_DSPS) += musb_dsps.o +obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o +obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o + +ccflags-y := $(call cc-option,-Wno-unused-variable) \ + $(call cc-option,-Wno-unused-but-set-variable) \ + $(call cc-option,-Wno-unused-label) diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/am35x.c b/qemu/roms/u-boot/drivers/usb/musb-new/am35x.c new file mode 100644 index 000000000..57c9bd393 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/am35x.c @@ -0,0 +1,709 @@ +/* + * Texas Instruments AM35x "glue layer" + * + * Copyright (c) 2010, by Texas Instruments + * + * Based on the DA8xx "glue layer" code. + * Copyright (c) 2008-2009, MontaVista Software, Inc. + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + * The Inventra Controller Driver for Linux is distributed in + * the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __UBOOT__ +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include + +#include +#else +#include +#include +#include "linux-compat.h" +#endif + +#include "musb_core.h" + +/* + * AM35x specific definitions + */ +/* USB 2.0 OTG module registers */ +#define USB_REVISION_REG 0x00 +#define USB_CTRL_REG 0x04 +#define USB_STAT_REG 0x08 +#define USB_EMULATION_REG 0x0c +/* 0x10 Reserved */ +#define USB_AUTOREQ_REG 0x14 +#define USB_SRP_FIX_TIME_REG 0x18 +#define USB_TEARDOWN_REG 0x1c +#define EP_INTR_SRC_REG 0x20 +#define EP_INTR_SRC_SET_REG 0x24 +#define EP_INTR_SRC_CLEAR_REG 0x28 +#define EP_INTR_MASK_REG 0x2c +#define EP_INTR_MASK_SET_REG 0x30 +#define EP_INTR_MASK_CLEAR_REG 0x34 +#define EP_INTR_SRC_MASKED_REG 0x38 +#define CORE_INTR_SRC_REG 0x40 +#define CORE_INTR_SRC_SET_REG 0x44 +#define CORE_INTR_SRC_CLEAR_REG 0x48 +#define CORE_INTR_MASK_REG 0x4c +#define CORE_INTR_MASK_SET_REG 0x50 +#define CORE_INTR_MASK_CLEAR_REG 0x54 +#define CORE_INTR_SRC_MASKED_REG 0x58 +/* 0x5c Reserved */ +#define USB_END_OF_INTR_REG 0x60 + +/* Control register bits */ +#define AM35X_SOFT_RESET_MASK 1 + +/* USB interrupt register bits */ +#define AM35X_INTR_USB_SHIFT 16 +#define AM35X_INTR_USB_MASK (0x1ff << AM35X_INTR_USB_SHIFT) +#define AM35X_INTR_DRVVBUS 0x100 +#define AM35X_INTR_RX_SHIFT 16 +#define AM35X_INTR_TX_SHIFT 0 +#define AM35X_TX_EP_MASK 0xffff /* EP0 + 15 Tx EPs */ +#define AM35X_RX_EP_MASK 0xfffe /* 15 Rx EPs */ +#define AM35X_TX_INTR_MASK (AM35X_TX_EP_MASK << AM35X_INTR_TX_SHIFT) +#define AM35X_RX_INTR_MASK (AM35X_RX_EP_MASK << AM35X_INTR_RX_SHIFT) + +#define USB_MENTOR_CORE_OFFSET 0x400 + +struct am35x_glue { + struct device *dev; + struct platform_device *musb; + struct clk *phy_clk; + struct clk *clk; +}; +#define glue_to_musb(g) platform_get_drvdata(g->musb) + +/* + * am35x_musb_enable - enable interrupts + */ +static void am35x_musb_enable(struct musb *musb) +{ + void __iomem *reg_base = musb->ctrl_base; + u32 epmask; + + /* Workaround: setup IRQs through both register sets. */ + epmask = ((musb->epmask & AM35X_TX_EP_MASK) << AM35X_INTR_TX_SHIFT) | + ((musb->epmask & AM35X_RX_EP_MASK) << AM35X_INTR_RX_SHIFT); + + musb_writel(reg_base, EP_INTR_MASK_SET_REG, epmask); + musb_writel(reg_base, CORE_INTR_MASK_SET_REG, AM35X_INTR_USB_MASK); + + /* Force the DRVVBUS IRQ so we can start polling for ID change. */ + if (is_otg_enabled(musb)) + musb_writel(reg_base, CORE_INTR_SRC_SET_REG, + AM35X_INTR_DRVVBUS << AM35X_INTR_USB_SHIFT); +} + +/* + * am35x_musb_disable - disable HDRC and flush interrupts + */ +static void am35x_musb_disable(struct musb *musb) +{ + void __iomem *reg_base = musb->ctrl_base; + + musb_writel(reg_base, CORE_INTR_MASK_CLEAR_REG, AM35X_INTR_USB_MASK); + musb_writel(reg_base, EP_INTR_MASK_CLEAR_REG, + AM35X_TX_INTR_MASK | AM35X_RX_INTR_MASK); + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + musb_writel(reg_base, USB_END_OF_INTR_REG, 0); +} + +#ifndef __UBOOT__ +#define portstate(stmt) stmt + +static void am35x_musb_set_vbus(struct musb *musb, int is_on) +{ + WARN_ON(is_on && is_peripheral_active(musb)); +} + +#define POLL_SECONDS 2 + +static struct timer_list otg_workaround; + +static void otg_timer(unsigned long _musb) +{ + struct musb *musb = (void *)_musb; + void __iomem *mregs = musb->mregs; + u8 devctl; + unsigned long flags; + + /* + * We poll because AM35x's won't expose several OTG-critical + * status change events (from the transceiver) otherwise. + */ + devctl = musb_readb(mregs, MUSB_DEVCTL); + dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, + otg_state_string(musb->xceiv->state)); + + spin_lock_irqsave(&musb->lock, flags); + switch (musb->xceiv->state) { + case OTG_STATE_A_WAIT_BCON: + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) { + musb->xceiv->state = OTG_STATE_B_IDLE; + MUSB_DEV_MODE(musb); + } else { + musb->xceiv->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + } + break; + case OTG_STATE_A_WAIT_VFALL: + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; + musb_writel(musb->ctrl_base, CORE_INTR_SRC_SET_REG, + MUSB_INTR_VBUSERROR << AM35X_INTR_USB_SHIFT); + break; + case OTG_STATE_B_IDLE: + if (!is_peripheral_enabled(musb)) + break; + + devctl = musb_readb(mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + else + musb->xceiv->state = OTG_STATE_A_IDLE; + break; + default: + break; + } + spin_unlock_irqrestore(&musb->lock, flags); +} + +static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) +{ + static unsigned long last_timer; + + if (!is_otg_enabled(musb)) + return; + + if (timeout == 0) + timeout = jiffies + msecs_to_jiffies(3); + + /* Never idle if active, or when VBUS timeout is not set as host */ + if (musb->is_active || (musb->a_wait_bcon == 0 && + musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { + dev_dbg(musb->controller, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); + del_timer(&otg_workaround); + last_timer = jiffies; + return; + } + + if (time_after(last_timer, timeout) && timer_pending(&otg_workaround)) { + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); + return; + } + last_timer = timeout; + + dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", + otg_state_string(musb->xceiv->state), + jiffies_to_msecs(timeout - jiffies)); + mod_timer(&otg_workaround, timeout); +} +#endif + +static irqreturn_t am35x_musb_interrupt(int irq, void *hci) +{ + struct musb *musb = hci; + void __iomem *reg_base = musb->ctrl_base; +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + struct usb_otg *otg = musb->xceiv->otg; +#else + struct omap_musb_board_data *data = + (struct omap_musb_board_data *)musb->controller; +#endif + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + u32 epintr, usbintr; + +#ifdef __UBOOT__ + /* + * It seems that on AM35X interrupt registers can be updated + * before core registers. This confuses the code. + * As a workaround add a small delay here. + */ + udelay(10); +#endif + spin_lock_irqsave(&musb->lock, flags); + + /* Get endpoint interrupts */ + epintr = musb_readl(reg_base, EP_INTR_SRC_MASKED_REG); + + if (epintr) { + musb_writel(reg_base, EP_INTR_SRC_CLEAR_REG, epintr); + + musb->int_rx = + (epintr & AM35X_RX_INTR_MASK) >> AM35X_INTR_RX_SHIFT; + musb->int_tx = + (epintr & AM35X_TX_INTR_MASK) >> AM35X_INTR_TX_SHIFT; + } + + /* Get usb core interrupts */ + usbintr = musb_readl(reg_base, CORE_INTR_SRC_MASKED_REG); + if (!usbintr && !epintr) + goto eoi; + + if (usbintr) { + musb_writel(reg_base, CORE_INTR_SRC_CLEAR_REG, usbintr); + + musb->int_usb = + (usbintr & AM35X_INTR_USB_MASK) >> AM35X_INTR_USB_SHIFT; + } +#ifndef __UBOOT__ + /* + * DRVVBUS IRQs are the only proxy we have (a very poor one!) for + * AM35x's missing ID change IRQ. We need an ID change IRQ to + * switch appropriately between halves of the OTG state machine. + * Managing DEVCTL.SESSION per Mentor docs requires that we know its + * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set. + * Also, DRVVBUS pulses for SRP (but not at 5V) ... + */ + if (usbintr & (AM35X_INTR_DRVVBUS << AM35X_INTR_USB_SHIFT)) { + int drvvbus = musb_readl(reg_base, USB_STAT_REG); + void __iomem *mregs = musb->mregs; + u8 devctl = musb_readb(mregs, MUSB_DEVCTL); + int err; + + err = is_host_enabled(musb) && (musb->int_usb & + MUSB_INTR_VBUSERROR); + if (err) { + /* + * The Mentor core doesn't debounce VBUS as needed + * to cope with device connect current spikes. This + * means it's not uncommon for bus-powered devices + * to get VBUS errors during enumeration. + * + * This is a workaround, but newer RTL from Mentor + * seems to allow a better one: "re"-starting sessions + * without waiting for VBUS to stop registering in + * devctl. + */ + musb->int_usb &= ~MUSB_INTR_VBUSERROR; + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); + WARNING("VBUS error workaround (delay coming)\n"); + } else if (is_host_enabled(musb) && drvvbus) { + MUSB_HST_MODE(musb); + otg->default_a = 1; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; + portstate(musb->port1_status |= USB_PORT_STAT_POWER); + del_timer(&otg_workaround); + } else { + musb->is_active = 0; + MUSB_DEV_MODE(musb); + otg->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; + portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); + } + + /* NOTE: this must complete power-on within 100 ms. */ + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", + drvvbus ? "on" : "off", + otg_state_string(musb->xceiv->state), + err ? " ERROR" : "", + devctl); + ret = IRQ_HANDLED; + } +#endif + + if (musb->int_tx || musb->int_rx || musb->int_usb) + ret |= musb_interrupt(musb); + +eoi: + /* EOI needs to be written for the IRQ to be re-asserted. */ + if (ret == IRQ_HANDLED || epintr || usbintr) { + /* clear level interrupt */ + if (data->clear_irq) + data->clear_irq(); + /* write EOI */ + musb_writel(reg_base, USB_END_OF_INTR_REG, 0); + } + +#ifndef __UBOOT__ + /* Poll for ID change */ + if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE) + mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); +#endif + + spin_unlock_irqrestore(&musb->lock, flags); + + return ret; +} + +#ifndef __UBOOT__ +static int am35x_musb_set_mode(struct musb *musb, u8 musb_mode) +{ + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + int retval = 0; + + if (data->set_mode) + data->set_mode(musb_mode); + else + retval = -EIO; + + return retval; +} +#endif + +static int am35x_musb_init(struct musb *musb) +{ +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; +#else + struct omap_musb_board_data *data = + (struct omap_musb_board_data *)musb->controller; +#endif + void __iomem *reg_base = musb->ctrl_base; + u32 rev; + + musb->mregs += USB_MENTOR_CORE_OFFSET; + + /* Returns zero if e.g. not clocked */ + rev = musb_readl(reg_base, USB_REVISION_REG); + if (!rev) + return -ENODEV; + +#ifndef __UBOOT__ + usb_nop_xceiv_register(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(musb->xceiv)) + return -ENODEV; + + if (is_host_enabled(musb)) + setup_timer(&otg_workaround, otg_timer, (unsigned long) musb); +#endif + + /* Reset the musb */ + if (data->reset) + data->reset(); + + /* Reset the controller */ + musb_writel(reg_base, USB_CTRL_REG, AM35X_SOFT_RESET_MASK); + + /* Start the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(1); + + msleep(5); + + musb->isr = am35x_musb_interrupt; + + /* clear level interrupt */ + if (data->clear_irq) + data->clear_irq(); + + return 0; +} + +static int am35x_musb_exit(struct musb *musb) +{ +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; +#else + struct omap_musb_board_data *data = + (struct omap_musb_board_data *)musb->controller; +#endif + +#ifndef __UBOOT__ + if (is_host_enabled(musb)) + del_timer_sync(&otg_workaround); +#endif + + /* Shutdown the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(0); + +#ifndef __UBOOT__ + usb_put_phy(musb->xceiv); + usb_nop_xceiv_unregister(); +#endif + + return 0; +} + +/* AM35x supports only 32bit read operation */ +void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) +{ + void __iomem *fifo = hw_ep->fifo; + u32 val; + int i; + + /* Read for 32bit-aligned destination address */ + if (likely((0x03 & (unsigned long) dst) == 0) && len >= 4) { + readsl(fifo, dst, len >> 2); + dst += len & ~0x03; + len &= 0x03; + } + /* + * Now read the remaining 1 to 3 byte or complete length if + * unaligned address. + */ + if (len > 4) { + for (i = 0; i < (len >> 2); i++) { + *(u32 *) dst = musb_readl(fifo, 0); + dst += 4; + } + len &= 0x03; + } + if (len > 0) { + val = musb_readl(fifo, 0); + memcpy(dst, &val, len); + } +} + +#ifndef __UBOOT__ +static const struct musb_platform_ops am35x_ops = { +#else +const struct musb_platform_ops am35x_ops = { +#endif + .init = am35x_musb_init, + .exit = am35x_musb_exit, + + .enable = am35x_musb_enable, + .disable = am35x_musb_disable, + +#ifndef __UBOOT__ + .set_mode = am35x_musb_set_mode, + .try_idle = am35x_musb_try_idle, + + .set_vbus = am35x_musb_set_vbus, +#endif +}; + +#ifndef __UBOOT__ +static u64 am35x_dmamask = DMA_BIT_MASK(32); + +static int __devinit am35x_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct platform_device *musb; + struct am35x_glue *glue; + + struct clk *phy_clk; + struct clk *clk; + + int ret = -ENOMEM; + + glue = kzalloc(sizeof(*glue), GFP_KERNEL); + if (!glue) { + dev_err(&pdev->dev, "failed to allocate glue context\n"); + goto err0; + } + + musb = platform_device_alloc("musb-hdrc", -1); + if (!musb) { + dev_err(&pdev->dev, "failed to allocate musb device\n"); + goto err1; + } + + phy_clk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(phy_clk)) { + dev_err(&pdev->dev, "failed to get PHY clock\n"); + ret = PTR_ERR(phy_clk); + goto err2; + } + + clk = clk_get(&pdev->dev, "ick"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(clk); + goto err3; + } + + ret = clk_enable(phy_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable PHY clock\n"); + goto err4; + } + + ret = clk_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + goto err5; + } + + musb->dev.parent = &pdev->dev; + musb->dev.dma_mask = &am35x_dmamask; + musb->dev.coherent_dma_mask = am35x_dmamask; + + glue->dev = &pdev->dev; + glue->musb = musb; + glue->phy_clk = phy_clk; + glue->clk = clk; + + pdata->platform_ops = &am35x_ops; + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err6; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err6; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err6; + } + + return 0; + +err6: + clk_disable(clk); + +err5: + clk_disable(phy_clk); + +err4: + clk_put(clk); + +err3: + clk_put(phy_clk); + +err2: + platform_device_put(musb); + +err1: + kfree(glue); + +err0: + return ret; +} + +static int __devexit am35x_remove(struct platform_device *pdev) +{ + struct am35x_glue *glue = platform_get_drvdata(pdev); + + platform_device_del(glue->musb); + platform_device_put(glue->musb); + clk_disable(glue->clk); + clk_disable(glue->phy_clk); + clk_put(glue->clk); + clk_put(glue->phy_clk); + kfree(glue); + + return 0; +} + +#ifdef CONFIG_PM +static int am35x_suspend(struct device *dev) +{ + struct am35x_glue *glue = dev_get_drvdata(dev); + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + + /* Shutdown the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(0); + + clk_disable(glue->phy_clk); + clk_disable(glue->clk); + + return 0; +} + +static int am35x_resume(struct device *dev) +{ + struct am35x_glue *glue = dev_get_drvdata(dev); + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + int ret; + + /* Start the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(1); + + ret = clk_enable(glue->phy_clk); + if (ret) { + dev_err(dev, "failed to enable PHY clock\n"); + return ret; + } + + ret = clk_enable(glue->clk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + + return 0; +} + +static struct dev_pm_ops am35x_pm_ops = { + .suspend = am35x_suspend, + .resume = am35x_resume, +}; + +#define DEV_PM_OPS &am35x_pm_ops +#else +#define DEV_PM_OPS NULL +#endif + +static struct platform_driver am35x_driver = { + .probe = am35x_probe, + .remove = __devexit_p(am35x_remove), + .driver = { + .name = "musb-am35x", + .pm = DEV_PM_OPS, + }, +}; + +MODULE_DESCRIPTION("AM35x MUSB Glue Layer"); +MODULE_AUTHOR("Ajay Kumar Gupta "); +MODULE_LICENSE("GPL v2"); + +static int __init am35x_init(void) +{ + return platform_driver_register(&am35x_driver); +} +module_init(am35x_init); + +static void __exit am35x_exit(void) +{ + platform_driver_unregister(&am35x_driver); +} +module_exit(am35x_exit); +#endif diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/linux-compat.h b/qemu/roms/u-boot/drivers/usb/musb-new/linux-compat.h new file mode 100644 index 000000000..d7a5663de --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/linux-compat.h @@ -0,0 +1,98 @@ +#ifndef __LINUX_COMPAT_H__ +#define __LINUX_COMPAT_H__ + +#include +#include +#include + +#define __init +#define __devinit +#define __devinitdata +#define __devinitconst +#define __iomem +#define __deprecated + +struct unused {}; +typedef struct unused unused_t; + +typedef int irqreturn_t; +typedef unused_t spinlock_t; + +struct work_struct {}; + +struct timer_list {}; +struct notifier_block {}; + +typedef unsigned long dmaaddr_t; + +#define spin_lock_init(lock) do {} while (0) +#define spin_lock(lock) do {} while (0) +#define spin_unlock(lock) do {} while (0) +#define spin_lock_irqsave(lock, flags) do {} while (0) +#define spin_unlock_irqrestore(lock, flags) do {} while (0) + +#define setup_timer(timer, func, data) do {} while (0) +#define del_timer_sync(timer) do {} while (0) +#define schedule_work(work) do {} while (0) +#define INIT_WORK(work, fun) do {} while (0) + +#define cpu_relax() do {} while (0) + +#define pr_debug(fmt, args...) debug(fmt, ##args) + +#define WARN(condition, fmt, args...) ({ \ + int ret_warn = !!condition; \ + if (ret_warn) \ + printf(fmt, ##args); \ + ret_warn; }) + +#define pm_runtime_get_sync(dev) do {} while (0) +#define pm_runtime_put(dev) do {} while (0) +#define pm_runtime_put_sync(dev) do {} while (0) +#define pm_runtime_use_autosuspend(dev) do {} while (0) +#define pm_runtime_set_autosuspend_delay(dev, delay) do {} while (0) +#define pm_runtime_enable(dev) do {} while (0) + +#define MODULE_DESCRIPTION(desc) +#define MODULE_AUTHOR(author) +#define MODULE_LICENSE(license) +#define MODULE_ALIAS(alias) +#define module_param(name, type, perm) +#define MODULE_PARM_DESC(name, desc) +#define EXPORT_SYMBOL_GPL(name) + +#define writesl(a, d, s) __raw_writesl((unsigned long)a, d, s) +#define readsl(a, d, s) __raw_readsl((unsigned long)a, d, s) +#define writesw(a, d, s) __raw_writesw((unsigned long)a, d, s) +#define readsw(a, d, s) __raw_readsw((unsigned long)a, d, s) +#define writesb(a, d, s) __raw_writesb((unsigned long)a, d, s) +#define readsb(a, d, s) __raw_readsb((unsigned long)a, d, s) + +#define IRQ_NONE 0 +#define IRQ_HANDLED 0 + +#define dev_set_drvdata(dev, data) do {} while (0) + +#define disable_irq_wake(irq) do {} while (0) +#define enable_irq_wake(irq) -EINVAL +#define free_irq(irq, data) do {} while (0) +#define request_irq(nr, f, flags, nm, data) 0 + +#define device_init_wakeup(dev, a) do {} while (0) + +#define platform_data device_data + +#ifndef wmb +#define wmb() asm volatile ("" : : : "memory") +#endif + +#define msleep(a) udelay(a * 1000) + +/* + * Map U-Boot config options to Linux ones + */ +#ifdef CONFIG_OMAP34XX +#define CONFIG_SOC_OMAP3430 +#endif + +#endif /* __LINUX_COMPAT_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_core.c b/qemu/roms/u-boot/drivers/usb/musb-new/musb_core.c new file mode 100644 index 000000000..36681b6fc --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_core.c @@ -0,0 +1,2500 @@ +/* + * MUSB OTG driver core code + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +/* + * Inventra (Multipoint) Dual-Role Controller Driver for Linux. + * + * This consists of a Host Controller Driver (HCD) and a peripheral + * controller driver implementing the "Gadget" API; OTG support is + * in the works. These are normal Linux-USB controller drivers which + * use IRQs and have no dedicated thread. + * + * This version of the driver has only been used with products from + * Texas Instruments. Those products integrate the Inventra logic + * with other DMA, IRQ, and bus modules, as well as other logic that + * needs to be reflected in this driver. + * + * + * NOTE: the original Mentor code here was pretty much a collection + * of mechanisms that don't seem to have been fully integrated/working + * for *any* Linux kernel version. This version aims at Linux 2.6.now, + * Key open issues include: + * + * - Lack of host-side transaction scheduling, for all transfer types. + * The hardware doesn't do it; instead, software must. + * + * This is not an issue for OTG devices that don't support external + * hubs, but for more "normal" USB hosts it's a user issue that the + * "multipoint" support doesn't scale in the expected ways. That + * includes DaVinci EVM in a common non-OTG mode. + * + * * Control and bulk use dedicated endpoints, and there's as + * yet no mechanism to either (a) reclaim the hardware when + * peripherals are NAKing, which gets complicated with bulk + * endpoints, or (b) use more than a single bulk endpoint in + * each direction. + * + * RESULT: one device may be perceived as blocking another one. + * + * * Interrupt and isochronous will dynamically allocate endpoint + * hardware, but (a) there's no record keeping for bandwidth; + * (b) in the common case that few endpoints are available, there + * is no mechanism to reuse endpoints to talk to multiple devices. + * + * RESULT: At one extreme, bandwidth can be overcommitted in + * some hardware configurations, no faults will be reported. + * At the other extreme, the bandwidth capabilities which do + * exist tend to be severely undercommitted. You can't yet hook + * up both a keyboard and a mouse to an external USB hub. + */ + +/* + * This gets many kinds of configuration information: + * - Kconfig for everything user-configurable + * - platform_device for addressing, irq, and platform_data + * - platform_data is mostly for board-specific informarion + * (plus recentrly, SOC or family details) + * + * Most of the conditional compilation will (someday) vanish. + */ + +#define __UBOOT__ +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include "linux-compat.h" +#include "usb-compat.h" +#endif + +#include "musb_core.h" + +#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON) + + +#define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia" +#define DRIVER_DESC "Inventra Dual-Role USB Controller Driver" + +#define MUSB_VERSION "6.0" + +#define DRIVER_INFO DRIVER_DESC ", v" MUSB_VERSION + +#define MUSB_DRIVER_NAME "musb-hdrc" +const char musb_driver_name[] = MUSB_DRIVER_NAME; + +MODULE_DESCRIPTION(DRIVER_INFO); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" MUSB_DRIVER_NAME); + + +#ifndef __UBOOT__ +/*-------------------------------------------------------------------------*/ + +static inline struct musb *dev_to_musb(struct device *dev) +{ + return dev_get_drvdata(dev); +} +#endif + +/*-------------------------------------------------------------------------*/ + +#ifndef __UBOOT__ +#ifndef CONFIG_BLACKFIN +static int musb_ulpi_read(struct usb_phy *phy, u32 offset) +{ + void __iomem *addr = phy->io_priv; + int i = 0; + u8 r; + u8 power; + int ret; + + pm_runtime_get_sync(phy->io_dev); + + /* Make sure the transceiver is not in low power mode */ + power = musb_readb(addr, MUSB_POWER); + power &= ~MUSB_POWER_SUSPENDM; + musb_writeb(addr, MUSB_POWER, power); + + /* REVISIT: musbhdrc_ulpi_an.pdf recommends setting the + * ULPICarKitControlDisableUTMI after clearing POWER_SUSPENDM. + */ + + musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, + MUSB_ULPI_REG_REQ | MUSB_ULPI_RDN_WR); + + while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) + & MUSB_ULPI_REG_CMPLT)) { + i++; + if (i == 10000) { + ret = -ETIMEDOUT; + goto out; + } + + } + r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); + r &= ~MUSB_ULPI_REG_CMPLT; + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); + + ret = musb_readb(addr, MUSB_ULPI_REG_DATA); + +out: + pm_runtime_put(phy->io_dev); + + return ret; +} + +static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data) +{ + void __iomem *addr = phy->io_priv; + int i = 0; + u8 r = 0; + u8 power; + int ret = 0; + + pm_runtime_get_sync(phy->io_dev); + + /* Make sure the transceiver is not in low power mode */ + power = musb_readb(addr, MUSB_POWER); + power &= ~MUSB_POWER_SUSPENDM; + musb_writeb(addr, MUSB_POWER, power); + + musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); + musb_writeb(addr, MUSB_ULPI_REG_DATA, (u8)data); + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, MUSB_ULPI_REG_REQ); + + while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) + & MUSB_ULPI_REG_CMPLT)) { + i++; + if (i == 10000) { + ret = -ETIMEDOUT; + goto out; + } + } + + r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); + r &= ~MUSB_ULPI_REG_CMPLT; + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); + +out: + pm_runtime_put(phy->io_dev); + + return ret; +} +#else +#define musb_ulpi_read NULL +#define musb_ulpi_write NULL +#endif + +static struct usb_phy_io_ops musb_ulpi_access = { + .read = musb_ulpi_read, + .write = musb_ulpi_write, +}; +#endif + +/*-------------------------------------------------------------------------*/ + +#if !defined(CONFIG_USB_MUSB_TUSB6010) && !defined(CONFIG_USB_MUSB_BLACKFIN) + +/* + * Load an endpoint's FIFO + */ +void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) +{ + struct musb *musb = hw_ep->musb; + void __iomem *fifo = hw_ep->fifo; + + prefetch((u8 *)src); + + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", + 'T', hw_ep->epnum, fifo, len, src); + + /* we can't assume unaligned reads work */ + if (likely((0x01 & (unsigned long) src) == 0)) { + u16 index = 0; + + /* best case is 32bit-aligned source address */ + if ((0x02 & (unsigned long) src) == 0) { + if (len >= 4) { + writesl(fifo, src + index, len >> 2); + index += len & ~0x03; + } + if (len & 0x02) { + musb_writew(fifo, 0, *(u16 *)&src[index]); + index += 2; + } + } else { + if (len >= 2) { + writesw(fifo, src + index, len >> 1); + index += len & ~0x01; + } + } + if (len & 0x01) + musb_writeb(fifo, 0, src[index]); + } else { + /* byte aligned */ + writesb(fifo, src, len); + } +} + +#if !defined(CONFIG_USB_MUSB_AM35X) +/* + * Unload an endpoint's FIFO + */ +void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) +{ + struct musb *musb = hw_ep->musb; + void __iomem *fifo = hw_ep->fifo; + + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", + 'R', hw_ep->epnum, fifo, len, dst); + + /* we can't assume unaligned writes work */ + if (likely((0x01 & (unsigned long) dst) == 0)) { + u16 index = 0; + + /* best case is 32bit-aligned destination address */ + if ((0x02 & (unsigned long) dst) == 0) { + if (len >= 4) { + readsl(fifo, dst, len >> 2); + index = len & ~0x03; + } + if (len & 0x02) { + *(u16 *)&dst[index] = musb_readw(fifo, 0); + index += 2; + } + } else { + if (len >= 2) { + readsw(fifo, dst, len >> 1); + index = len & ~0x01; + } + } + if (len & 0x01) + dst[index] = musb_readb(fifo, 0); + } else { + /* byte aligned */ + readsb(fifo, dst, len); + } +} +#endif + +#endif /* normal PIO */ + + +/*-------------------------------------------------------------------------*/ + +/* for high speed test mode; see USB 2.0 spec 7.1.20 */ +static const u8 musb_test_packet[53] = { + /* implicit SYNC then DATA0 to start */ + + /* JKJKJKJK x9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* JJKKJJKK x8 */ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + /* JJJJKKKK x8 */ + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + /* JJJJJJJKKKKKKK x8 */ + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* JJJJJJJK x8 */ + 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, + /* JKKKKKKK x10, JK */ + 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e + + /* implicit CRC16 then EOP to end */ +}; + +void musb_load_testpacket(struct musb *musb) +{ + void __iomem *regs = musb->endpoints[0].regs; + + musb_ep_select(musb->mregs, 0); + musb_write_fifo(musb->control_ep, + sizeof(musb_test_packet), musb_test_packet); + musb_writew(regs, MUSB_CSR0, MUSB_CSR0_TXPKTRDY); +} + +#ifndef __UBOOT__ +/*-------------------------------------------------------------------------*/ + +/* + * Handles OTG hnp timeouts, such as b_ase0_brst + */ +void musb_otg_timer_func(unsigned long data) +{ + struct musb *musb = (struct musb *)data; + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + switch (musb->xceiv->state) { + case OTG_STATE_B_WAIT_ACON: + dev_dbg(musb->controller, "HNP: b_wait_acon timeout; back to b_peripheral\n"); + musb_g_disconnect(musb); + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + musb->is_active = 0; + break; + case OTG_STATE_A_SUSPEND: + case OTG_STATE_A_WAIT_BCON: + dev_dbg(musb->controller, "HNP: %s timeout\n", + otg_state_string(musb->xceiv->state)); + musb_platform_set_vbus(musb, 0); + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; + break; + default: + dev_dbg(musb->controller, "HNP: Unhandled mode %s\n", + otg_state_string(musb->xceiv->state)); + } + musb->ignore_disconnect = 0; + spin_unlock_irqrestore(&musb->lock, flags); +} + +/* + * Stops the HNP transition. Caller must take care of locking. + */ +void musb_hnp_stop(struct musb *musb) +{ + struct usb_hcd *hcd = musb_to_hcd(musb); + void __iomem *mbase = musb->mregs; + u8 reg; + + dev_dbg(musb->controller, "HNP: stop from %s\n", otg_state_string(musb->xceiv->state)); + + switch (musb->xceiv->state) { + case OTG_STATE_A_PERIPHERAL: + musb_g_disconnect(musb); + dev_dbg(musb->controller, "HNP: back to %s\n", + otg_state_string(musb->xceiv->state)); + break; + case OTG_STATE_B_HOST: + dev_dbg(musb->controller, "HNP: Disabling HR\n"); + hcd->self.is_b_host = 0; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + MUSB_DEV_MODE(musb); + reg = musb_readb(mbase, MUSB_POWER); + reg |= MUSB_POWER_SUSPENDM; + musb_writeb(mbase, MUSB_POWER, reg); + /* REVISIT: Start SESSION_REQUEST here? */ + break; + default: + dev_dbg(musb->controller, "HNP: Stopping in unknown state %s\n", + otg_state_string(musb->xceiv->state)); + } + + /* + * When returning to A state after HNP, avoid hub_port_rebounce(), + * which cause occasional OPT A "Did not receive reset after connect" + * errors. + */ + musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16); +} +#endif + +/* + * Interrupt Service Routine to record USB "global" interrupts. + * Since these do not happen often and signify things of + * paramount importance, it seems OK to check them individually; + * the order of the tests is specified in the manual + * + * @param musb instance pointer + * @param int_usb register contents + * @param devctl + * @param power + */ + +static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, + u8 devctl, u8 power) +{ +#ifndef __UBOOT__ + struct usb_otg *otg = musb->xceiv->otg; +#endif + irqreturn_t handled = IRQ_NONE; + + dev_dbg(musb->controller, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl, + int_usb); + +#ifndef __UBOOT__ + /* in host mode, the peripheral may issue remote wakeup. + * in peripheral mode, the host may resume the link. + * spurious RESUME irqs happen too, paired with SUSPEND. + */ + if (int_usb & MUSB_INTR_RESUME) { + handled = IRQ_HANDLED; + dev_dbg(musb->controller, "RESUME (%s)\n", otg_state_string(musb->xceiv->state)); + + if (devctl & MUSB_DEVCTL_HM) { + void __iomem *mbase = musb->mregs; + + switch (musb->xceiv->state) { + case OTG_STATE_A_SUSPEND: + /* remote wakeup? later, GetPortStatus + * will stop RESUME signaling + */ + + if (power & MUSB_POWER_SUSPENDM) { + /* spurious */ + musb->int_usb &= ~MUSB_INTR_SUSPEND; + dev_dbg(musb->controller, "Spurious SUSPENDM\n"); + break; + } + + power &= ~MUSB_POWER_SUSPENDM; + musb_writeb(mbase, MUSB_POWER, + power | MUSB_POWER_RESUME); + + musb->port1_status |= + (USB_PORT_STAT_C_SUSPEND << 16) + | MUSB_PORT_STAT_RESUME; + musb->rh_timer = jiffies + + msecs_to_jiffies(20); + + musb->xceiv->state = OTG_STATE_A_HOST; + musb->is_active = 1; + usb_hcd_resume_root_hub(musb_to_hcd(musb)); + break; + case OTG_STATE_B_WAIT_ACON: + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + musb->is_active = 1; + MUSB_DEV_MODE(musb); + break; + default: + WARNING("bogus %s RESUME (%s)\n", + "host", + otg_state_string(musb->xceiv->state)); + } + } else { + switch (musb->xceiv->state) { + case OTG_STATE_A_SUSPEND: + /* possibly DISCONNECT is upcoming */ + musb->xceiv->state = OTG_STATE_A_HOST; + usb_hcd_resume_root_hub(musb_to_hcd(musb)); + break; + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_PERIPHERAL: + /* disconnect while suspended? we may + * not get a disconnect irq... + */ + if ((devctl & MUSB_DEVCTL_VBUS) + != (3 << MUSB_DEVCTL_VBUS_SHIFT) + ) { + musb->int_usb |= MUSB_INTR_DISCONNECT; + musb->int_usb &= ~MUSB_INTR_SUSPEND; + break; + } + musb_g_resume(musb); + break; + case OTG_STATE_B_IDLE: + musb->int_usb &= ~MUSB_INTR_SUSPEND; + break; + default: + WARNING("bogus %s RESUME (%s)\n", + "peripheral", + otg_state_string(musb->xceiv->state)); + } + } + } + + /* see manual for the order of the tests */ + if (int_usb & MUSB_INTR_SESSREQ) { + void __iomem *mbase = musb->mregs; + + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS + && (devctl & MUSB_DEVCTL_BDEVICE)) { + dev_dbg(musb->controller, "SessReq while on B state\n"); + return IRQ_HANDLED; + } + + dev_dbg(musb->controller, "SESSION_REQUEST (%s)\n", + otg_state_string(musb->xceiv->state)); + + /* IRQ arrives from ID pin sense or (later, if VBUS power + * is removed) SRP. responses are time critical: + * - turn on VBUS (with silicon-specific mechanism) + * - go through A_WAIT_VRISE + * - ... to A_WAIT_BCON. + * a_wait_vrise_tmout triggers VBUS_ERROR transitions + */ + musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); + musb->ep0_stage = MUSB_EP0_START; + musb->xceiv->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + musb_platform_set_vbus(musb, 1); + + handled = IRQ_HANDLED; + } + + if (int_usb & MUSB_INTR_VBUSERROR) { + int ignore = 0; + + /* During connection as an A-Device, we may see a short + * current spikes causing voltage drop, because of cable + * and peripheral capacitance combined with vbus draw. + * (So: less common with truly self-powered devices, where + * vbus doesn't act like a power supply.) + * + * Such spikes are short; usually less than ~500 usec, max + * of ~2 msec. That is, they're not sustained overcurrent + * errors, though they're reported using VBUSERROR irqs. + * + * Workarounds: (a) hardware: use self powered devices. + * (b) software: ignore non-repeated VBUS errors. + * + * REVISIT: do delays from lots of DEBUG_KERNEL checks + * make trouble here, keeping VBUS < 4.4V ? + */ + switch (musb->xceiv->state) { + case OTG_STATE_A_HOST: + /* recovery is dicey once we've gotten past the + * initial stages of enumeration, but if VBUS + * stayed ok at the other end of the link, and + * another reset is due (at least for high speed, + * to redo the chirp etc), it might work OK... + */ + case OTG_STATE_A_WAIT_BCON: + case OTG_STATE_A_WAIT_VRISE: + if (musb->vbuserr_retry) { + void __iomem *mbase = musb->mregs; + + musb->vbuserr_retry--; + ignore = 1; + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(mbase, MUSB_DEVCTL, devctl); + } else { + musb->port1_status |= + USB_PORT_STAT_OVERCURRENT + | (USB_PORT_STAT_C_OVERCURRENT << 16); + } + break; + default: + break; + } + + dev_dbg(musb->controller, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", + otg_state_string(musb->xceiv->state), + devctl, + ({ char *s; + switch (devctl & MUSB_DEVCTL_VBUS) { + case 0 << MUSB_DEVCTL_VBUS_SHIFT: + s = "vbuserr_retry, + musb->port1_status); + + /* go through A_WAIT_VFALL then start a new session */ + if (!ignore) + musb_platform_set_vbus(musb, 0); + handled = IRQ_HANDLED; + } + + if (int_usb & MUSB_INTR_SUSPEND) { + dev_dbg(musb->controller, "SUSPEND (%s) devctl %02x power %02x\n", + otg_state_string(musb->xceiv->state), devctl, power); + handled = IRQ_HANDLED; + + switch (musb->xceiv->state) { + case OTG_STATE_A_PERIPHERAL: + /* We also come here if the cable is removed, since + * this silicon doesn't report ID-no-longer-grounded. + * + * We depend on T(a_wait_bcon) to shut us down, and + * hope users don't do anything dicey during this + * undesired detour through A_WAIT_BCON. + */ + musb_hnp_stop(musb); + usb_hcd_resume_root_hub(musb_to_hcd(musb)); + musb_root_disconnect(musb); + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon + ? : OTG_TIME_A_WAIT_BCON)); + + break; + case OTG_STATE_B_IDLE: + if (!musb->is_active) + break; + case OTG_STATE_B_PERIPHERAL: + musb_g_suspend(musb); + musb->is_active = is_otg_enabled(musb) + && otg->gadget->b_hnp_enable; + if (musb->is_active) { + musb->xceiv->state = OTG_STATE_B_WAIT_ACON; + dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n"); + mod_timer(&musb->otg_timer, jiffies + + msecs_to_jiffies( + OTG_TIME_B_ASE0_BRST)); + } + break; + case OTG_STATE_A_WAIT_BCON: + if (musb->a_wait_bcon != 0) + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon)); + break; + case OTG_STATE_A_HOST: + musb->xceiv->state = OTG_STATE_A_SUSPEND; + musb->is_active = is_otg_enabled(musb) + && otg->host->b_hnp_enable; + break; + case OTG_STATE_B_HOST: + /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ + dev_dbg(musb->controller, "REVISIT: SUSPEND as B_HOST\n"); + break; + default: + /* "should not happen" */ + musb->is_active = 0; + break; + } + } +#endif + + if (int_usb & MUSB_INTR_CONNECT) { + struct usb_hcd *hcd = musb_to_hcd(musb); + + handled = IRQ_HANDLED; + musb->is_active = 1; + + musb->ep0_stage = MUSB_EP0_START; + + /* flush endpoints when transitioning from Device Mode */ + if (is_peripheral_active(musb)) { + /* REVISIT HNP; just force disconnect */ + } + musb_writew(musb->mregs, MUSB_INTRTXE, musb->epmask); + musb_writew(musb->mregs, MUSB_INTRRXE, musb->epmask & 0xfffe); + musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7); +#ifndef __UBOOT__ + musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED + |USB_PORT_STAT_HIGH_SPEED + |USB_PORT_STAT_ENABLE + ); + musb->port1_status |= USB_PORT_STAT_CONNECTION + |(USB_PORT_STAT_C_CONNECTION << 16); + + /* high vs full speed is just a guess until after reset */ + if (devctl & MUSB_DEVCTL_LSDEV) + musb->port1_status |= USB_PORT_STAT_LOW_SPEED; + + /* indicate new connection to OTG machine */ + switch (musb->xceiv->state) { + case OTG_STATE_B_PERIPHERAL: + if (int_usb & MUSB_INTR_SUSPEND) { + dev_dbg(musb->controller, "HNP: SUSPEND+CONNECT, now b_host\n"); + int_usb &= ~MUSB_INTR_SUSPEND; + goto b_host; + } else + dev_dbg(musb->controller, "CONNECT as b_peripheral???\n"); + break; + case OTG_STATE_B_WAIT_ACON: + dev_dbg(musb->controller, "HNP: CONNECT, now b_host\n"); +b_host: + musb->xceiv->state = OTG_STATE_B_HOST; + hcd->self.is_b_host = 1; + musb->ignore_disconnect = 0; + del_timer(&musb->otg_timer); + break; + default: + if ((devctl & MUSB_DEVCTL_VBUS) + == (3 << MUSB_DEVCTL_VBUS_SHIFT)) { + musb->xceiv->state = OTG_STATE_A_HOST; + hcd->self.is_b_host = 0; + } + break; + } + + /* poke the root hub */ + MUSB_HST_MODE(musb); + if (hcd->status_urb) + usb_hcd_poll_rh_status(hcd); + else + usb_hcd_resume_root_hub(hcd); + + dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n", + otg_state_string(musb->xceiv->state), devctl); +#endif + } + +#ifndef __UBOOT__ + if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { + dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n", + otg_state_string(musb->xceiv->state), + MUSB_MODE(musb), devctl); + handled = IRQ_HANDLED; + + switch (musb->xceiv->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_A_SUSPEND: + usb_hcd_resume_root_hub(musb_to_hcd(musb)); + musb_root_disconnect(musb); + if (musb->a_wait_bcon != 0 && is_otg_enabled(musb)) + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon)); + break; + case OTG_STATE_B_HOST: + /* REVISIT this behaves for "real disconnect" + * cases; make sure the other transitions from + * from B_HOST act right too. The B_HOST code + * in hnp_stop() is currently not used... + */ + musb_root_disconnect(musb); + musb_to_hcd(musb)->self.is_b_host = 0; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + MUSB_DEV_MODE(musb); + musb_g_disconnect(musb); + break; + case OTG_STATE_A_PERIPHERAL: + musb_hnp_stop(musb); + musb_root_disconnect(musb); + /* FALLTHROUGH */ + case OTG_STATE_B_WAIT_ACON: + /* FALLTHROUGH */ + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_IDLE: + musb_g_disconnect(musb); + break; + default: + WARNING("unhandled DISCONNECT transition (%s)\n", + otg_state_string(musb->xceiv->state)); + break; + } + } + + /* mentor saves a bit: bus reset and babble share the same irq. + * only host sees babble; only peripheral sees bus reset. + */ + if (int_usb & MUSB_INTR_RESET) { + handled = IRQ_HANDLED; + if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) { + /* + * Looks like non-HS BABBLE can be ignored, but + * HS BABBLE is an error condition. For HS the solution + * is to avoid babble in the first place and fix what + * caused BABBLE. When HS BABBLE happens we can only + * stop the session. + */ + if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) + dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl); + else { + ERR("Stopping host session -- babble\n"); + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + } + } else if (is_peripheral_capable()) { + dev_dbg(musb->controller, "BUS RESET as %s\n", + otg_state_string(musb->xceiv->state)); + switch (musb->xceiv->state) { + case OTG_STATE_A_SUSPEND: + /* We need to ignore disconnect on suspend + * otherwise tusb 2.0 won't reconnect after a + * power cycle, which breaks otg compliance. + */ + musb->ignore_disconnect = 1; + musb_g_reset(musb); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ + /* never use invalid T(a_wait_bcon) */ + dev_dbg(musb->controller, "HNP: in %s, %d msec timeout\n", + otg_state_string(musb->xceiv->state), + TA_WAIT_BCON(musb)); + mod_timer(&musb->otg_timer, jiffies + + msecs_to_jiffies(TA_WAIT_BCON(musb))); + break; + case OTG_STATE_A_PERIPHERAL: + musb->ignore_disconnect = 0; + del_timer(&musb->otg_timer); + musb_g_reset(musb); + break; + case OTG_STATE_B_WAIT_ACON: + dev_dbg(musb->controller, "HNP: RESET (%s), to b_peripheral\n", + otg_state_string(musb->xceiv->state)); + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + musb_g_reset(musb); + break; + case OTG_STATE_B_IDLE: + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + /* FALLTHROUGH */ + case OTG_STATE_B_PERIPHERAL: + musb_g_reset(musb); + break; + default: + dev_dbg(musb->controller, "Unhandled BUS RESET as %s\n", + otg_state_string(musb->xceiv->state)); + } + } + } +#endif + +#if 0 +/* REVISIT ... this would be for multiplexing periodic endpoints, or + * supporting transfer phasing to prevent exceeding ISO bandwidth + * limits of a given frame or microframe. + * + * It's not needed for peripheral side, which dedicates endpoints; + * though it _might_ use SOF irqs for other purposes. + * + * And it's not currently needed for host side, which also dedicates + * endpoints, relies on TX/RX interval registers, and isn't claimed + * to support ISO transfers yet. + */ + if (int_usb & MUSB_INTR_SOF) { + void __iomem *mbase = musb->mregs; + struct musb_hw_ep *ep; + u8 epnum; + u16 frame; + + dev_dbg(musb->controller, "START_OF_FRAME\n"); + handled = IRQ_HANDLED; + + /* start any periodic Tx transfers waiting for current frame */ + frame = musb_readw(mbase, MUSB_FRAME); + ep = musb->endpoints; + for (epnum = 1; (epnum < musb->nr_endpoints) + && (musb->epmask >= (1 << epnum)); + epnum++, ep++) { + /* + * FIXME handle framecounter wraps (12 bits) + * eliminate duplicated StartUrb logic + */ + if (ep->dwWaitFrame >= frame) { + ep->dwWaitFrame = 0; + pr_debug("SOF --> periodic TX%s on %d\n", + ep->tx_channel ? " DMA" : "", + epnum); + if (!ep->tx_channel) + musb_h_tx_start(musb, epnum); + else + cppi_hostdma_start(musb, epnum); + } + } /* end of for loop */ + } +#endif + + schedule_work(&musb->irq_work); + + return handled; +} + +/*-------------------------------------------------------------------------*/ + +/* +* Program the HDRC to start (enable interrupts, dma, etc.). +*/ +void musb_start(struct musb *musb) +{ + void __iomem *regs = musb->mregs; + u8 devctl = musb_readb(regs, MUSB_DEVCTL); + + dev_dbg(musb->controller, "<== devctl %02x\n", devctl); + + /* Set INT enable registers, enable interrupts */ + musb_writew(regs, MUSB_INTRTXE, musb->epmask); + musb_writew(regs, MUSB_INTRRXE, musb->epmask & 0xfffe); + musb_writeb(regs, MUSB_INTRUSBE, 0xf7); + + musb_writeb(regs, MUSB_TESTMODE, 0); + + /* put into basic highspeed mode and start session */ + musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE +#ifdef CONFIG_USB_GADGET_DUALSPEED + | MUSB_POWER_HSENAB +#endif + /* ENSUSPEND wedges tusb */ + /* | MUSB_POWER_ENSUSPEND */ + ); + + musb->is_active = 0; + devctl = musb_readb(regs, MUSB_DEVCTL); + devctl &= ~MUSB_DEVCTL_SESSION; + + if (is_otg_enabled(musb)) { +#ifndef __UBOOT__ + /* session started after: + * (a) ID-grounded irq, host mode; + * (b) vbus present/connect IRQ, peripheral mode; + * (c) peripheral initiates, using SRP + */ + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) + musb->is_active = 1; + else + devctl |= MUSB_DEVCTL_SESSION; +#endif + + } else if (is_host_enabled(musb)) { + /* assume ID pin is hard-wired to ground */ + devctl |= MUSB_DEVCTL_SESSION; + + } else /* peripheral is enabled */ { + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) + musb->is_active = 1; + } + musb_platform_enable(musb); + musb_writeb(regs, MUSB_DEVCTL, devctl); +} + + +static void musb_generic_disable(struct musb *musb) +{ + void __iomem *mbase = musb->mregs; + u16 temp; + + /* disable interrupts */ + musb_writeb(mbase, MUSB_INTRUSBE, 0); + musb_writew(mbase, MUSB_INTRTXE, 0); + musb_writew(mbase, MUSB_INTRRXE, 0); + + /* off */ + musb_writeb(mbase, MUSB_DEVCTL, 0); + + /* flush pending interrupts */ + temp = musb_readb(mbase, MUSB_INTRUSB); + temp = musb_readw(mbase, MUSB_INTRTX); + temp = musb_readw(mbase, MUSB_INTRRX); + +} + +/* + * Make the HDRC stop (disable interrupts, etc.); + * reversible by musb_start + * called on gadget driver unregister + * with controller locked, irqs blocked + * acts as a NOP unless some role activated the hardware + */ +void musb_stop(struct musb *musb) +{ + /* stop IRQs, timers, ... */ + musb_platform_disable(musb); + musb_generic_disable(musb); + dev_dbg(musb->controller, "HDRC disabled\n"); + + /* FIXME + * - mark host and/or peripheral drivers unusable/inactive + * - disable DMA (and enable it in HdrcStart) + * - make sure we can musb_start() after musb_stop(); with + * OTG mode, gadget driver module rmmod/modprobe cycles that + * - ... + */ + musb_platform_try_idle(musb, 0); +} + +#ifndef __UBOOT__ +static void musb_shutdown(struct platform_device *pdev) +{ + struct musb *musb = dev_to_musb(&pdev->dev); + unsigned long flags; + + pm_runtime_get_sync(musb->controller); + + musb_gadget_cleanup(musb); + + spin_lock_irqsave(&musb->lock, flags); + musb_platform_disable(musb); + musb_generic_disable(musb); + spin_unlock_irqrestore(&musb->lock, flags); + + if (!is_otg_enabled(musb) && is_host_enabled(musb)) + usb_remove_hcd(musb_to_hcd(musb)); + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + musb_platform_exit(musb); + + pm_runtime_put(musb->controller); + /* FIXME power down */ +} +#endif + + +/*-------------------------------------------------------------------------*/ + +/* + * The silicon either has hard-wired endpoint configurations, or else + * "dynamic fifo" sizing. The driver has support for both, though at this + * writing only the dynamic sizing is very well tested. Since we switched + * away from compile-time hardware parameters, we can no longer rely on + * dead code elimination to leave only the relevant one in the object file. + * + * We don't currently use dynamic fifo setup capability to do anything + * more than selecting one of a bunch of predefined configurations. + */ +#if defined(CONFIG_USB_MUSB_TUSB6010) \ + || defined(CONFIG_USB_MUSB_TUSB6010_MODULE) \ + || defined(CONFIG_USB_MUSB_OMAP2PLUS) \ + || defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE) \ + || defined(CONFIG_USB_MUSB_AM35X) \ + || defined(CONFIG_USB_MUSB_AM35X_MODULE) \ + || defined(CONFIG_USB_MUSB_DSPS) \ + || defined(CONFIG_USB_MUSB_DSPS_MODULE) +static ushort __devinitdata fifo_mode = 4; +#elif defined(CONFIG_USB_MUSB_UX500) \ + || defined(CONFIG_USB_MUSB_UX500_MODULE) +static ushort __devinitdata fifo_mode = 5; +#else +static ushort __devinitdata fifo_mode = 2; +#endif + +/* "modprobe ... fifo_mode=1" etc */ +module_param(fifo_mode, ushort, 0); +MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration"); + +/* + * tables defining fifo_mode values. define more if you like. + * for host side, make sure both halves of ep1 are set up. + */ + +/* mode 0 - fits in 2KB */ +static struct musb_fifo_cfg __devinitdata mode_0_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, }, +{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, }, +}; + +/* mode 1 - fits in 4KB */ +static struct musb_fifo_cfg __devinitdata mode_1_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, }, +{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, }, +}; + +/* mode 2 - fits in 4KB */ +static struct musb_fifo_cfg __devinitdata mode_2_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, }, +{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, }, +}; + +/* mode 3 - fits in 4KB */ +static struct musb_fifo_cfg __devinitdata mode_3_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, }, +{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, }, +}; + +/* mode 4 - fits in 16KB */ +static struct musb_fifo_cfg __devinitdata mode_4_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 4096, }, +{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, }, +{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, }, +}; + +/* mode 5 - fits in 8KB */ +static struct musb_fifo_cfg __devinitdata mode_5_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 512, }, +{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, }, +{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, }, +}; + +/* + * configure a fifo; for non-shared endpoints, this may be called + * once for a tx fifo and once for an rx fifo. + * + * returns negative errno or offset for next fifo. + */ +static int __devinit +fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, + const struct musb_fifo_cfg *cfg, u16 offset) +{ + void __iomem *mbase = musb->mregs; + int size = 0; + u16 maxpacket = cfg->maxpacket; + u16 c_off = offset >> 3; + u8 c_size; + + /* expect hw_ep has already been zero-initialized */ + + size = ffs(max(maxpacket, (u16) 8)) - 1; + maxpacket = 1 << size; + + c_size = size - 3; + if (cfg->mode == BUF_DOUBLE) { + if ((offset + (maxpacket << 1)) > + (1 << (musb->config->ram_bits + 2))) + return -EMSGSIZE; + c_size |= MUSB_FIFOSZ_DPB; + } else { + if ((offset + maxpacket) > (1 << (musb->config->ram_bits + 2))) + return -EMSGSIZE; + } + + /* configure the FIFO */ + musb_writeb(mbase, MUSB_INDEX, hw_ep->epnum); + + /* EP0 reserved endpoint for control, bidirectional; + * EP1 reserved for bulk, two unidirection halves. + */ + if (hw_ep->epnum == 1) + musb->bulk_ep = hw_ep; + /* REVISIT error check: be sure ep0 can both rx and tx ... */ + switch (cfg->style) { + case FIFO_TX: + musb_write_txfifosz(mbase, c_size); + musb_write_txfifoadd(mbase, c_off); + hw_ep->tx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB); + hw_ep->max_packet_sz_tx = maxpacket; + break; + case FIFO_RX: + musb_write_rxfifosz(mbase, c_size); + musb_write_rxfifoadd(mbase, c_off); + hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB); + hw_ep->max_packet_sz_rx = maxpacket; + break; + case FIFO_RXTX: + musb_write_txfifosz(mbase, c_size); + musb_write_txfifoadd(mbase, c_off); + hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB); + hw_ep->max_packet_sz_rx = maxpacket; + + musb_write_rxfifosz(mbase, c_size); + musb_write_rxfifoadd(mbase, c_off); + hw_ep->tx_double_buffered = hw_ep->rx_double_buffered; + hw_ep->max_packet_sz_tx = maxpacket; + + hw_ep->is_shared_fifo = true; + break; + } + + /* NOTE rx and tx endpoint irqs aren't managed separately, + * which happens to be ok + */ + musb->epmask |= (1 << hw_ep->epnum); + + return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0)); +} + +static struct musb_fifo_cfg __devinitdata ep0_cfg = { + .style = FIFO_RXTX, .maxpacket = 64, +}; + +static int __devinit ep_config_from_table(struct musb *musb) +{ + const struct musb_fifo_cfg *cfg; + unsigned i, n; + int offset; + struct musb_hw_ep *hw_ep = musb->endpoints; + + if (musb->config->fifo_cfg) { + cfg = musb->config->fifo_cfg; + n = musb->config->fifo_cfg_size; + goto done; + } + + switch (fifo_mode) { + default: + fifo_mode = 0; + /* FALLTHROUGH */ + case 0: + cfg = mode_0_cfg; + n = ARRAY_SIZE(mode_0_cfg); + break; + case 1: + cfg = mode_1_cfg; + n = ARRAY_SIZE(mode_1_cfg); + break; + case 2: + cfg = mode_2_cfg; + n = ARRAY_SIZE(mode_2_cfg); + break; + case 3: + cfg = mode_3_cfg; + n = ARRAY_SIZE(mode_3_cfg); + break; + case 4: + cfg = mode_4_cfg; + n = ARRAY_SIZE(mode_4_cfg); + break; + case 5: + cfg = mode_5_cfg; + n = ARRAY_SIZE(mode_5_cfg); + break; + } + + pr_debug("%s: setup fifo_mode %d\n", musb_driver_name, fifo_mode); + +done: + offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0); + /* assert(offset > 0) */ + + /* NOTE: for RTL versions >= 1.400 EPINFO and RAMINFO would + * be better than static musb->config->num_eps and DYN_FIFO_SIZE... + */ + + for (i = 0; i < n; i++) { + u8 epn = cfg->hw_ep_num; + + if (epn >= musb->config->num_eps) { + pr_debug("%s: invalid ep %d\n", + musb_driver_name, epn); + return -EINVAL; + } + offset = fifo_setup(musb, hw_ep + epn, cfg++, offset); + if (offset < 0) { + pr_debug("%s: mem overrun, ep %d\n", + musb_driver_name, epn); + return -EINVAL; + } + epn++; + musb->nr_endpoints = max(epn, musb->nr_endpoints); + } + + pr_debug("%s: %d/%d max ep, %d/%d memory\n", musb_driver_name, n + 1, + musb->config->num_eps * 2 - 1, offset, + (1 << (musb->config->ram_bits + 2))); + + if (!musb->bulk_ep) { + pr_debug("%s: missing bulk\n", musb_driver_name); + return -EINVAL; + } + + return 0; +} + + +/* + * ep_config_from_hw - when MUSB_C_DYNFIFO_DEF is false + * @param musb the controller + */ +static int __devinit ep_config_from_hw(struct musb *musb) +{ + u8 epnum = 0; + struct musb_hw_ep *hw_ep; + void *mbase = musb->mregs; + int ret = 0; + + dev_dbg(musb->controller, "<== static silicon ep config\n"); + + /* FIXME pick up ep0 maxpacket size */ + + for (epnum = 1; epnum < musb->config->num_eps; epnum++) { + musb_ep_select(mbase, epnum); + hw_ep = musb->endpoints + epnum; + + ret = musb_read_fifosize(musb, hw_ep, epnum); + if (ret < 0) + break; + + /* FIXME set up hw_ep->{rx,tx}_double_buffered */ + + /* pick an RX/TX endpoint for bulk */ + if (hw_ep->max_packet_sz_tx < 512 + || hw_ep->max_packet_sz_rx < 512) + continue; + + /* REVISIT: this algorithm is lazy, we should at least + * try to pick a double buffered endpoint. + */ + if (musb->bulk_ep) + continue; + musb->bulk_ep = hw_ep; + } + + if (!musb->bulk_ep) { + pr_debug("%s: missing bulk\n", musb_driver_name); + return -EINVAL; + } + + return 0; +} + +enum { MUSB_CONTROLLER_MHDRC, MUSB_CONTROLLER_HDRC, }; + +/* Initialize MUSB (M)HDRC part of the USB hardware subsystem; + * configure endpoints, or take their config from silicon + */ +static int __devinit musb_core_init(u16 musb_type, struct musb *musb) +{ + u8 reg; + char *type; + char aInfo[90], aRevision[32], aDate[12]; + void __iomem *mbase = musb->mregs; + int status = 0; + int i; + + /* log core options (read using indexed model) */ + reg = musb_read_configdata(mbase); + + strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); + if (reg & MUSB_CONFIGDATA_DYNFIFO) { + strcat(aInfo, ", dyn FIFOs"); + musb->dyn_fifo = true; + } +#ifndef CONFIG_MUSB_DISABLE_BULK_COMBINE_SPLIT + if (reg & MUSB_CONFIGDATA_MPRXE) { + strcat(aInfo, ", bulk combine"); + musb->bulk_combine = true; + } + if (reg & MUSB_CONFIGDATA_MPTXE) { + strcat(aInfo, ", bulk split"); + musb->bulk_split = true; + } +#else + musb->bulk_combine = false; + musb->bulk_split = false; +#endif + if (reg & MUSB_CONFIGDATA_HBRXE) { + strcat(aInfo, ", HB-ISO Rx"); + musb->hb_iso_rx = true; + } + if (reg & MUSB_CONFIGDATA_HBTXE) { + strcat(aInfo, ", HB-ISO Tx"); + musb->hb_iso_tx = true; + } + if (reg & MUSB_CONFIGDATA_SOFTCONE) + strcat(aInfo, ", SoftConn"); + + pr_debug("%s:ConfigData=0x%02x (%s)\n", musb_driver_name, reg, aInfo); + + aDate[0] = 0; + if (MUSB_CONTROLLER_MHDRC == musb_type) { + musb->is_multipoint = 1; + type = "M"; + } else { + musb->is_multipoint = 0; + type = ""; +#ifndef CONFIG_USB_OTG_BLACKLIST_HUB + printk(KERN_ERR + "%s: kernel must blacklist external hubs\n", + musb_driver_name); +#endif + } + + /* log release info */ + musb->hwvers = musb_read_hwvers(mbase); + snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers), + MUSB_HWVERS_MINOR(musb->hwvers), + (musb->hwvers & MUSB_HWVERS_RC) ? "RC" : ""); + pr_debug("%s: %sHDRC RTL version %s %s\n", musb_driver_name, type, + aRevision, aDate); + + /* configure ep0 */ + musb_configure_ep0(musb); + + /* discover endpoint configuration */ + musb->nr_endpoints = 1; + musb->epmask = 1; + + if (musb->dyn_fifo) + status = ep_config_from_table(musb); + else + status = ep_config_from_hw(musb); + + if (status < 0) + return status; + + /* finish init, and print endpoint config */ + for (i = 0; i < musb->nr_endpoints; i++) { + struct musb_hw_ep *hw_ep = musb->endpoints + i; + + hw_ep->fifo = MUSB_FIFO_OFFSET(i) + mbase; +#if defined(CONFIG_USB_MUSB_TUSB6010) || defined (CONFIG_USB_MUSB_TUSB6010_MODULE) + hw_ep->fifo_async = musb->async + 0x400 + MUSB_FIFO_OFFSET(i); + hw_ep->fifo_sync = musb->sync + 0x400 + MUSB_FIFO_OFFSET(i); + hw_ep->fifo_sync_va = + musb->sync_va + 0x400 + MUSB_FIFO_OFFSET(i); + + if (i == 0) + hw_ep->conf = mbase - 0x400 + TUSB_EP0_CONF; + else + hw_ep->conf = mbase + 0x400 + (((i - 1) & 0xf) << 2); +#endif + + hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase; + hw_ep->target_regs = musb_read_target_reg_base(i, mbase); + hw_ep->rx_reinit = 1; + hw_ep->tx_reinit = 1; + + if (hw_ep->max_packet_sz_tx) { + dev_dbg(musb->controller, + "%s: hw_ep %d%s, %smax %d\n", + musb_driver_name, i, + hw_ep->is_shared_fifo ? "shared" : "tx", + hw_ep->tx_double_buffered + ? "doublebuffer, " : "", + hw_ep->max_packet_sz_tx); + } + if (hw_ep->max_packet_sz_rx && !hw_ep->is_shared_fifo) { + dev_dbg(musb->controller, + "%s: hw_ep %d%s, %smax %d\n", + musb_driver_name, i, + "rx", + hw_ep->rx_double_buffered + ? "doublebuffer, " : "", + hw_ep->max_packet_sz_rx); + } + if (!(hw_ep->max_packet_sz_tx || hw_ep->max_packet_sz_rx)) + dev_dbg(musb->controller, "hw_ep %d not configured\n", i); + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_SOC_OMAP3430) || \ + defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500) + +static irqreturn_t generic_interrupt(int irq, void *__hci) +{ + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + struct musb *musb = __hci; + + spin_lock_irqsave(&musb->lock, flags); + + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + + if (musb->int_usb || musb->int_tx || musb->int_rx) + retval = musb_interrupt(musb); + + spin_unlock_irqrestore(&musb->lock, flags); + + return retval; +} + +#else +#define generic_interrupt NULL +#endif + +/* + * handle all the irqs defined by the HDRC core. for now we expect: other + * irq sources (phy, dma, etc) will be handled first, musb->int_* values + * will be assigned, and the irq will already have been acked. + * + * called in irq context with spinlock held, irqs blocked + */ +irqreturn_t musb_interrupt(struct musb *musb) +{ + irqreturn_t retval = IRQ_NONE; + u8 devctl, power; + int ep_num; + u32 reg; + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + power = musb_readb(musb->mregs, MUSB_POWER); + + dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n", + (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral", + musb->int_usb, musb->int_tx, musb->int_rx); + + /* the core can interrupt us for multiple reasons; docs have + * a generic interrupt flowchart to follow + */ + if (musb->int_usb) + retval |= musb_stage0_irq(musb, musb->int_usb, + devctl, power); + + /* "stage 1" is handling endpoint irqs */ + + /* handle endpoint 0 first */ + if (musb->int_tx & 1) { + if (devctl & MUSB_DEVCTL_HM) { + if (is_host_capable()) + retval |= musb_h_ep0_irq(musb); + } else { + if (is_peripheral_capable()) + retval |= musb_g_ep0_irq(musb); + } + } + + /* RX on endpoints 1-15 */ + reg = musb->int_rx >> 1; + ep_num = 1; + while (reg) { + if (reg & 1) { + /* musb_ep_select(musb->mregs, ep_num); */ + /* REVISIT just retval = ep->rx_irq(...) */ + retval = IRQ_HANDLED; + if (devctl & MUSB_DEVCTL_HM) { + if (is_host_capable()) + musb_host_rx(musb, ep_num); + } else { + if (is_peripheral_capable()) + musb_g_rx(musb, ep_num); + } + } + + reg >>= 1; + ep_num++; + } + + /* TX on endpoints 1-15 */ + reg = musb->int_tx >> 1; + ep_num = 1; + while (reg) { + if (reg & 1) { + /* musb_ep_select(musb->mregs, ep_num); */ + /* REVISIT just retval |= ep->tx_irq(...) */ + retval = IRQ_HANDLED; + if (devctl & MUSB_DEVCTL_HM) { + if (is_host_capable()) + musb_host_tx(musb, ep_num); + } else { + if (is_peripheral_capable()) + musb_g_tx(musb, ep_num); + } + } + reg >>= 1; + ep_num++; + } + + return retval; +} +EXPORT_SYMBOL_GPL(musb_interrupt); + +#ifndef CONFIG_MUSB_PIO_ONLY +static bool __devinitdata use_dma = 1; + +/* "modprobe ... use_dma=0" etc */ +module_param(use_dma, bool, 0); +MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); + +void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) +{ + u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + /* called with controller lock already held */ + + if (!epnum) { +#ifndef CONFIG_USB_TUSB_OMAP_DMA + if (!is_cppi_enabled()) { + /* endpoint 0 */ + if (devctl & MUSB_DEVCTL_HM) + musb_h_ep0_irq(musb); + else + musb_g_ep0_irq(musb); + } +#endif + } else { + /* endpoints 1..15 */ + if (transmit) { + if (devctl & MUSB_DEVCTL_HM) { + if (is_host_capable()) + musb_host_tx(musb, epnum); + } else { + if (is_peripheral_capable()) + musb_g_tx(musb, epnum); + } + } else { + /* receive */ + if (devctl & MUSB_DEVCTL_HM) { + if (is_host_capable()) + musb_host_rx(musb, epnum); + } else { + if (is_peripheral_capable()) + musb_g_rx(musb, epnum); + } + } + } +} +EXPORT_SYMBOL_GPL(musb_dma_completion); + +#else +#define use_dma 0 +#endif + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_SYSFS + +static ssize_t +musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&musb->lock, flags); + ret = sprintf(buf, "%s\n", otg_state_string(musb->xceiv->state)); + spin_unlock_irqrestore(&musb->lock, flags); + + return ret; +} + +static ssize_t +musb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + int status; + + spin_lock_irqsave(&musb->lock, flags); + if (sysfs_streq(buf, "host")) + status = musb_platform_set_mode(musb, MUSB_HOST); + else if (sysfs_streq(buf, "peripheral")) + status = musb_platform_set_mode(musb, MUSB_PERIPHERAL); + else if (sysfs_streq(buf, "otg")) + status = musb_platform_set_mode(musb, MUSB_OTG); + else + status = -EINVAL; + spin_unlock_irqrestore(&musb->lock, flags); + + return (status == 0) ? n : status; +} +static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store); + +static ssize_t +musb_vbus_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + unsigned long val; + + if (sscanf(buf, "%lu", &val) < 1) { + dev_err(dev, "Invalid VBUS timeout ms value\n"); + return -EINVAL; + } + + spin_lock_irqsave(&musb->lock, flags); + /* force T(a_wait_bcon) to be zero/unlimited *OR* valid */ + musb->a_wait_bcon = val ? max_t(int, val, OTG_TIME_A_WAIT_BCON) : 0 ; + if (musb->xceiv->state == OTG_STATE_A_WAIT_BCON) + musb->is_active = 0; + musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val)); + spin_unlock_irqrestore(&musb->lock, flags); + + return n; +} + +static ssize_t +musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + unsigned long val; + int vbus; + + spin_lock_irqsave(&musb->lock, flags); + val = musb->a_wait_bcon; + /* FIXME get_vbus_status() is normally #defined as false... + * and is effectively TUSB-specific. + */ + vbus = musb_platform_get_vbus_status(musb); + spin_unlock_irqrestore(&musb->lock, flags); + + return sprintf(buf, "Vbus %s, timeout %lu msec\n", + vbus ? "on" : "off", val); +} +static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store); + +/* Gadget drivers can't know that a host is connected so they might want + * to start SRP, but users can. This allows userspace to trigger SRP. + */ +static ssize_t +musb_srp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct musb *musb = dev_to_musb(dev); + unsigned short srp; + + if (sscanf(buf, "%hu", &srp) != 1 + || (srp != 1)) { + dev_err(dev, "SRP: Value must be 1\n"); + return -EINVAL; + } + + if (srp == 1) + musb_g_wakeup(musb); + + return n; +} +static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store); + +static struct attribute *musb_attributes[] = { + &dev_attr_mode.attr, + &dev_attr_vbus.attr, + &dev_attr_srp.attr, + NULL +}; + +static const struct attribute_group musb_attr_group = { + .attrs = musb_attributes, +}; + +#endif /* sysfs */ + +#ifndef __UBOOT__ +/* Only used to provide driver mode change events */ +static void musb_irq_work(struct work_struct *data) +{ + struct musb *musb = container_of(data, struct musb, irq_work); + static int old_state; + + if (musb->xceiv->state != old_state) { + old_state = musb->xceiv->state; + sysfs_notify(&musb->controller->kobj, NULL, "mode"); + } +} +#endif + +/* -------------------------------------------------------------------------- + * Init support + */ + +static struct musb *__devinit +allocate_instance(struct device *dev, + struct musb_hdrc_config *config, void __iomem *mbase) +{ + struct musb *musb; + struct musb_hw_ep *ep; + int epnum; +#ifndef __UBOOT__ + struct usb_hcd *hcd; + + hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev)); + if (!hcd) + return NULL; + /* usbcore sets dev->driver_data to hcd, and sometimes uses that... */ + + musb = hcd_to_musb(hcd); +#else + musb = calloc(1, sizeof(*musb)); + if (!musb) + return NULL; +#endif + INIT_LIST_HEAD(&musb->control); + INIT_LIST_HEAD(&musb->in_bulk); + INIT_LIST_HEAD(&musb->out_bulk); + +#ifndef __UBOOT__ + hcd->uses_new_polling = 1; + hcd->has_tt = 1; +#endif + + musb->vbuserr_retry = VBUSERR_RETRY_COUNT; + musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON; + dev_set_drvdata(dev, musb); + musb->mregs = mbase; + musb->ctrl_base = mbase; + musb->nIrq = -ENODEV; + musb->config = config; + BUG_ON(musb->config->num_eps > MUSB_C_NUM_EPS); + for (epnum = 0, ep = musb->endpoints; + epnum < musb->config->num_eps; + epnum++, ep++) { + ep->musb = musb; + ep->epnum = epnum; + } + + musb->controller = dev; + + return musb; +} + +static void musb_free(struct musb *musb) +{ + /* this has multiple entry modes. it handles fault cleanup after + * probe(), where things may be partially set up, as well as rmmod + * cleanup after everything's been de-activated. + */ + +#ifdef CONFIG_SYSFS + sysfs_remove_group(&musb->controller->kobj, &musb_attr_group); +#endif + + if (musb->nIrq >= 0) { + if (musb->irq_wake) + disable_irq_wake(musb->nIrq); + free_irq(musb->nIrq, musb); + } + if (is_dma_capable() && musb->dma_controller) { + struct dma_controller *c = musb->dma_controller; + + (void) c->stop(c); + dma_controller_destroy(c); + } + + kfree(musb); +} + +/* + * Perform generic per-controller initialization. + * + * @pDevice: the controller (already clocked, etc) + * @nIrq: irq + * @mregs: virtual address of controller registers, + * not yet corrected for platform-specific offsets + */ +#ifndef __UBOOT__ +static int __devinit +musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) +#else +struct musb * +musb_init_controller(struct musb_hdrc_platform_data *plat, struct device *dev, + void *ctrl) +#endif +{ + int status; + struct musb *musb; +#ifndef __UBOOT__ + struct musb_hdrc_platform_data *plat = dev->platform_data; +#else + int nIrq = 0; +#endif + + /* The driver might handle more features than the board; OK. + * Fail when the board needs a feature that's not enabled. + */ + if (!plat) { + dev_dbg(dev, "no platform_data?\n"); + status = -ENODEV; + goto fail0; + } + + /* allocate */ + musb = allocate_instance(dev, plat->config, ctrl); + if (!musb) { + status = -ENOMEM; + goto fail0; + } + + pm_runtime_use_autosuspend(musb->controller); + pm_runtime_set_autosuspend_delay(musb->controller, 200); + pm_runtime_enable(musb->controller); + + spin_lock_init(&musb->lock); + musb->board_mode = plat->mode; + musb->board_set_power = plat->set_power; + musb->min_power = plat->min_power; + musb->ops = plat->platform_ops; + + /* The musb_platform_init() call: + * - adjusts musb->mregs and musb->isr if needed, + * - may initialize an integrated tranceiver + * - initializes musb->xceiv, usually by otg_get_phy() + * - stops powering VBUS + * + * There are various transceiver configurations. Blackfin, + * DaVinci, TUSB60x0, and others integrate them. OMAP3 uses + * external/discrete ones in various flavors (twl4030 family, + * isp1504, non-OTG, etc) mostly hooking up through ULPI. + */ + musb->isr = generic_interrupt; + status = musb_platform_init(musb); + if (status < 0) + goto fail1; + + if (!musb->isr) { + status = -ENODEV; + goto fail2; + } + +#ifndef __UBOOT__ + if (!musb->xceiv->io_ops) { + musb->xceiv->io_dev = musb->controller; + musb->xceiv->io_priv = musb->mregs; + musb->xceiv->io_ops = &musb_ulpi_access; + } +#endif + + pm_runtime_get_sync(musb->controller); + +#ifndef CONFIG_MUSB_PIO_ONLY + if (use_dma && dev->dma_mask) { + struct dma_controller *c; + + c = dma_controller_create(musb, musb->mregs); + musb->dma_controller = c; + if (c) + (void) c->start(c); + } +#endif +#ifndef __UBOOT__ + /* ideally this would be abstracted in platform setup */ + if (!is_dma_capable() || !musb->dma_controller) + dev->dma_mask = NULL; +#endif + + /* be sure interrupts are disabled before connecting ISR */ + musb_platform_disable(musb); + musb_generic_disable(musb); + + /* setup musb parts of the core (especially endpoints) */ + status = musb_core_init(plat->config->multipoint + ? MUSB_CONTROLLER_MHDRC + : MUSB_CONTROLLER_HDRC, musb); + if (status < 0) + goto fail3; + + setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb); + + /* Init IRQ workqueue before request_irq */ + INIT_WORK(&musb->irq_work, musb_irq_work); + + /* attach to the IRQ */ + if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) { + dev_err(dev, "request_irq %d failed!\n", nIrq); + status = -ENODEV; + goto fail3; + } + musb->nIrq = nIrq; +/* FIXME this handles wakeup irqs wrong */ + if (enable_irq_wake(nIrq) == 0) { + musb->irq_wake = 1; + device_init_wakeup(dev, 1); + } else { + musb->irq_wake = 0; + } + +#ifndef __UBOOT__ + /* host side needs more setup */ + if (is_host_enabled(musb)) { + struct usb_hcd *hcd = musb_to_hcd(musb); + + otg_set_host(musb->xceiv->otg, &hcd->self); + + if (is_otg_enabled(musb)) + hcd->self.otg_port = 1; + musb->xceiv->otg->host = &hcd->self; + hcd->power_budget = 2 * (plat->power ? : 250); + + /* program PHY to use external vBus if required */ + if (plat->extvbus) { + u8 busctl = musb_read_ulpi_buscontrol(musb->mregs); + busctl |= MUSB_ULPI_USE_EXTVBUS; + musb_write_ulpi_buscontrol(musb->mregs, busctl); + } + } +#endif + + /* For the host-only role, we can activate right away. + * (We expect the ID pin to be forcibly grounded!!) + * Otherwise, wait till the gadget driver hooks up. + */ + if (!is_otg_enabled(musb) && is_host_enabled(musb)) { + struct usb_hcd *hcd = musb_to_hcd(musb); + + MUSB_HST_MODE(musb); +#ifndef __UBOOT__ + musb->xceiv->otg->default_a = 1; + musb->xceiv->state = OTG_STATE_A_IDLE; + + status = usb_add_hcd(musb_to_hcd(musb), 0, 0); + + hcd->self.uses_pio_for_control = 1; + dev_dbg(musb->controller, "%s mode, status %d, devctl %02x %c\n", + "HOST", status, + musb_readb(musb->mregs, MUSB_DEVCTL), + (musb_readb(musb->mregs, MUSB_DEVCTL) + & MUSB_DEVCTL_BDEVICE + ? 'B' : 'A')); +#endif + + } else /* peripheral is enabled */ { + MUSB_DEV_MODE(musb); +#ifndef __UBOOT__ + musb->xceiv->otg->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; +#endif + + if (is_peripheral_capable()) + status = musb_gadget_setup(musb); + +#ifndef __UBOOT__ + dev_dbg(musb->controller, "%s mode, status %d, dev%02x\n", + is_otg_enabled(musb) ? "OTG" : "PERIPHERAL", + status, + musb_readb(musb->mregs, MUSB_DEVCTL)); +#endif + + } + if (status < 0) + goto fail3; + + status = musb_init_debugfs(musb); + if (status < 0) + goto fail4; + +#ifdef CONFIG_SYSFS + status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group); + if (status) + goto fail5; +#endif + + pm_runtime_put(musb->controller); + + pr_debug("USB %s mode controller at %p using %s, IRQ %d\n", + ({char *s; + switch (musb->board_mode) { + case MUSB_HOST: s = "Host"; break; + case MUSB_PERIPHERAL: s = "Peripheral"; break; + default: s = "OTG"; break; + }; s; }), + ctrl, + (is_dma_capable() && musb->dma_controller) + ? "DMA" : "PIO", + musb->nIrq); + +#ifndef __UBOOT__ + return 0; +#else + return status == 0 ? musb : NULL; +#endif + +fail5: + musb_exit_debugfs(musb); + +fail4: +#ifndef __UBOOT__ + if (!is_otg_enabled(musb) && is_host_enabled(musb)) + usb_remove_hcd(musb_to_hcd(musb)); + else +#endif + musb_gadget_cleanup(musb); + +fail3: + pm_runtime_put_sync(musb->controller); + +fail2: + if (musb->irq_wake) + device_init_wakeup(dev, 0); + musb_platform_exit(musb); + +fail1: + dev_err(musb->controller, + "musb_init_controller failed with status %d\n", status); + + musb_free(musb); + +fail0: + +#ifndef __UBOOT__ + return status; +#else + return status == 0 ? musb : NULL; +#endif + +} + +/*-------------------------------------------------------------------------*/ + +/* all implementations (PCI bridge to FPGA, VLYNQ, etc) should just + * bridge to a platform device; this driver then suffices. + */ + +#ifndef CONFIG_MUSB_PIO_ONLY +static u64 *orig_dma_mask; +#endif + +#ifndef __UBOOT__ +static int __devinit musb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq = platform_get_irq_byname(pdev, "mc"); + int status; + struct resource *iomem; + void __iomem *base; + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem || irq <= 0) + return -ENODEV; + + base = ioremap(iomem->start, resource_size(iomem)); + if (!base) { + dev_err(dev, "ioremap failed\n"); + return -ENOMEM; + } + +#ifndef CONFIG_MUSB_PIO_ONLY + /* clobbered by use_dma=n */ + orig_dma_mask = dev->dma_mask; +#endif + status = musb_init_controller(dev, irq, base); + if (status < 0) + iounmap(base); + + return status; +} + +static int __devexit musb_remove(struct platform_device *pdev) +{ + struct musb *musb = dev_to_musb(&pdev->dev); + void __iomem *ctrl_base = musb->ctrl_base; + + /* this gets called on rmmod. + * - Host mode: host may still be active + * - Peripheral mode: peripheral is deactivated (or never-activated) + * - OTG mode: both roles are deactivated (or never-activated) + */ + musb_exit_debugfs(musb); + musb_shutdown(pdev); + + musb_free(musb); + iounmap(ctrl_base); + device_init_wakeup(&pdev->dev, 0); +#ifndef CONFIG_MUSB_PIO_ONLY + pdev->dev.dma_mask = orig_dma_mask; +#endif + return 0; +} + +#ifdef CONFIG_PM + +static void musb_save_context(struct musb *musb) +{ + int i; + void __iomem *musb_base = musb->mregs; + void __iomem *epio; + + if (is_host_enabled(musb)) { + musb->context.frame = musb_readw(musb_base, MUSB_FRAME); + musb->context.testmode = musb_readb(musb_base, MUSB_TESTMODE); + musb->context.busctl = musb_read_ulpi_buscontrol(musb->mregs); + } + musb->context.power = musb_readb(musb_base, MUSB_POWER); + musb->context.intrtxe = musb_readw(musb_base, MUSB_INTRTXE); + musb->context.intrrxe = musb_readw(musb_base, MUSB_INTRRXE); + musb->context.intrusbe = musb_readb(musb_base, MUSB_INTRUSBE); + musb->context.index = musb_readb(musb_base, MUSB_INDEX); + musb->context.devctl = musb_readb(musb_base, MUSB_DEVCTL); + + for (i = 0; i < musb->config->num_eps; ++i) { + struct musb_hw_ep *hw_ep; + + hw_ep = &musb->endpoints[i]; + if (!hw_ep) + continue; + + epio = hw_ep->regs; + if (!epio) + continue; + + musb_writeb(musb_base, MUSB_INDEX, i); + musb->context.index_regs[i].txmaxp = + musb_readw(epio, MUSB_TXMAXP); + musb->context.index_regs[i].txcsr = + musb_readw(epio, MUSB_TXCSR); + musb->context.index_regs[i].rxmaxp = + musb_readw(epio, MUSB_RXMAXP); + musb->context.index_regs[i].rxcsr = + musb_readw(epio, MUSB_RXCSR); + + if (musb->dyn_fifo) { + musb->context.index_regs[i].txfifoadd = + musb_read_txfifoadd(musb_base); + musb->context.index_regs[i].rxfifoadd = + musb_read_rxfifoadd(musb_base); + musb->context.index_regs[i].txfifosz = + musb_read_txfifosz(musb_base); + musb->context.index_regs[i].rxfifosz = + musb_read_rxfifosz(musb_base); + } + if (is_host_enabled(musb)) { + musb->context.index_regs[i].txtype = + musb_readb(epio, MUSB_TXTYPE); + musb->context.index_regs[i].txinterval = + musb_readb(epio, MUSB_TXINTERVAL); + musb->context.index_regs[i].rxtype = + musb_readb(epio, MUSB_RXTYPE); + musb->context.index_regs[i].rxinterval = + musb_readb(epio, MUSB_RXINTERVAL); + + musb->context.index_regs[i].txfunaddr = + musb_read_txfunaddr(musb_base, i); + musb->context.index_regs[i].txhubaddr = + musb_read_txhubaddr(musb_base, i); + musb->context.index_regs[i].txhubport = + musb_read_txhubport(musb_base, i); + + musb->context.index_regs[i].rxfunaddr = + musb_read_rxfunaddr(musb_base, i); + musb->context.index_regs[i].rxhubaddr = + musb_read_rxhubaddr(musb_base, i); + musb->context.index_regs[i].rxhubport = + musb_read_rxhubport(musb_base, i); + } + } +} + +static void musb_restore_context(struct musb *musb) +{ + int i; + void __iomem *musb_base = musb->mregs; + void __iomem *ep_target_regs; + void __iomem *epio; + + if (is_host_enabled(musb)) { + musb_writew(musb_base, MUSB_FRAME, musb->context.frame); + musb_writeb(musb_base, MUSB_TESTMODE, musb->context.testmode); + musb_write_ulpi_buscontrol(musb->mregs, musb->context.busctl); + } + musb_writeb(musb_base, MUSB_POWER, musb->context.power); + musb_writew(musb_base, MUSB_INTRTXE, musb->context.intrtxe); + musb_writew(musb_base, MUSB_INTRRXE, musb->context.intrrxe); + musb_writeb(musb_base, MUSB_INTRUSBE, musb->context.intrusbe); + musb_writeb(musb_base, MUSB_DEVCTL, musb->context.devctl); + + for (i = 0; i < musb->config->num_eps; ++i) { + struct musb_hw_ep *hw_ep; + + hw_ep = &musb->endpoints[i]; + if (!hw_ep) + continue; + + epio = hw_ep->regs; + if (!epio) + continue; + + musb_writeb(musb_base, MUSB_INDEX, i); + musb_writew(epio, MUSB_TXMAXP, + musb->context.index_regs[i].txmaxp); + musb_writew(epio, MUSB_TXCSR, + musb->context.index_regs[i].txcsr); + musb_writew(epio, MUSB_RXMAXP, + musb->context.index_regs[i].rxmaxp); + musb_writew(epio, MUSB_RXCSR, + musb->context.index_regs[i].rxcsr); + + if (musb->dyn_fifo) { + musb_write_txfifosz(musb_base, + musb->context.index_regs[i].txfifosz); + musb_write_rxfifosz(musb_base, + musb->context.index_regs[i].rxfifosz); + musb_write_txfifoadd(musb_base, + musb->context.index_regs[i].txfifoadd); + musb_write_rxfifoadd(musb_base, + musb->context.index_regs[i].rxfifoadd); + } + + if (is_host_enabled(musb)) { + musb_writeb(epio, MUSB_TXTYPE, + musb->context.index_regs[i].txtype); + musb_writeb(epio, MUSB_TXINTERVAL, + musb->context.index_regs[i].txinterval); + musb_writeb(epio, MUSB_RXTYPE, + musb->context.index_regs[i].rxtype); + musb_writeb(epio, MUSB_RXINTERVAL, + + musb->context.index_regs[i].rxinterval); + musb_write_txfunaddr(musb_base, i, + musb->context.index_regs[i].txfunaddr); + musb_write_txhubaddr(musb_base, i, + musb->context.index_regs[i].txhubaddr); + musb_write_txhubport(musb_base, i, + musb->context.index_regs[i].txhubport); + + ep_target_regs = + musb_read_target_reg_base(i, musb_base); + + musb_write_rxfunaddr(ep_target_regs, + musb->context.index_regs[i].rxfunaddr); + musb_write_rxhubaddr(ep_target_regs, + musb->context.index_regs[i].rxhubaddr); + musb_write_rxhubport(ep_target_regs, + musb->context.index_regs[i].rxhubport); + } + } + musb_writeb(musb_base, MUSB_INDEX, musb->context.index); +} + +static int musb_suspend(struct device *dev) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + + if (is_peripheral_active(musb)) { + /* FIXME force disconnect unless we know USB will wake + * the system up quickly enough to respond ... + */ + } else if (is_host_active(musb)) { + /* we know all the children are suspended; sometimes + * they will even be wakeup-enabled. + */ + } + + spin_unlock_irqrestore(&musb->lock, flags); + return 0; +} + +static int musb_resume_noirq(struct device *dev) +{ + /* for static cmos like DaVinci, register values were preserved + * unless for some reason the whole soc powered down or the USB + * module got reset through the PSC (vs just being disabled). + */ + return 0; +} + +static int musb_runtime_suspend(struct device *dev) +{ + struct musb *musb = dev_to_musb(dev); + + musb_save_context(musb); + + return 0; +} + +static int musb_runtime_resume(struct device *dev) +{ + struct musb *musb = dev_to_musb(dev); + static int first = 1; + + /* + * When pm_runtime_get_sync called for the first time in driver + * init, some of the structure is still not initialized which is + * used in restore function. But clock needs to be + * enabled before any register access, so + * pm_runtime_get_sync has to be called. + * Also context restore without save does not make + * any sense + */ + if (!first) + musb_restore_context(musb); + first = 0; + + return 0; +} + +static const struct dev_pm_ops musb_dev_pm_ops = { + .suspend = musb_suspend, + .resume_noirq = musb_resume_noirq, + .runtime_suspend = musb_runtime_suspend, + .runtime_resume = musb_runtime_resume, +}; + +#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops) +#else +#define MUSB_DEV_PM_OPS NULL +#endif + +static struct platform_driver musb_driver = { + .driver = { + .name = (char *)musb_driver_name, + .bus = &platform_bus_type, + .owner = THIS_MODULE, + .pm = MUSB_DEV_PM_OPS, + }, + .probe = musb_probe, + .remove = __devexit_p(musb_remove), + .shutdown = musb_shutdown, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init musb_init(void) +{ + if (usb_disabled()) + return 0; + + pr_info("%s: version " MUSB_VERSION ", " + "?dma?" + ", " + "otg (peripheral+host)", + musb_driver_name); + return platform_driver_register(&musb_driver); +} +module_init(musb_init); + +static void __exit musb_cleanup(void) +{ + platform_driver_unregister(&musb_driver); +} +module_exit(musb_cleanup); +#endif diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_core.h b/qemu/roms/u-boot/drivers/usb/musb-new/musb_core.h new file mode 100644 index 000000000..269574209 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_core.h @@ -0,0 +1,623 @@ +/* + * MUSB OTG driver defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#ifndef __MUSB_CORE_H__ +#define __MUSB_CORE_H__ + +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif +#include +#include +#include + +struct musb; +struct musb_hw_ep; +struct musb_ep; + +/* Helper defines for struct musb->hwvers */ +#define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f) +#define MUSB_HWVERS_MINOR(x) (x & 0x3ff) +#define MUSB_HWVERS_RC 0x8000 +#define MUSB_HWVERS_1300 0x52C +#define MUSB_HWVERS_1400 0x590 +#define MUSB_HWVERS_1800 0x720 +#define MUSB_HWVERS_1900 0x784 +#define MUSB_HWVERS_2000 0x800 + +#include "musb_debug.h" +#include "musb_dma.h" + +#include "musb_io.h" +#include "musb_regs.h" + +#include "musb_gadget.h" +#ifndef __UBOOT__ +#include +#endif +#include "musb_host.h" + +#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST) +#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL) +#define is_otg_enabled(musb) ((musb)->board_mode == MUSB_OTG) + +/* NOTE: otg and peripheral-only state machines start at B_IDLE. + * OTG or host-only go to A_IDLE when ID is sensed. + */ +#define is_peripheral_active(m) (!(m)->is_host) +#define is_host_active(m) ((m)->is_host) + +#ifdef CONFIG_PROC_FS +#include +#define MUSB_CONFIG_PROC_FS +#endif + +/****************************** PERIPHERAL ROLE *****************************/ + +#ifndef __UBOOT__ +#define is_peripheral_capable() (1) +#else +#ifdef CONFIG_MUSB_GADGET +#define is_peripheral_capable() (1) +#else +#define is_peripheral_capable() (0) +#endif +#endif + +extern irqreturn_t musb_g_ep0_irq(struct musb *); +extern void musb_g_tx(struct musb *, u8); +extern void musb_g_rx(struct musb *, u8); +extern void musb_g_reset(struct musb *); +extern void musb_g_suspend(struct musb *); +extern void musb_g_resume(struct musb *); +extern void musb_g_wakeup(struct musb *); +extern void musb_g_disconnect(struct musb *); + +/****************************** HOST ROLE ***********************************/ + +#ifndef __UBOOT__ +#define is_host_capable() (1) +#else +#ifdef CONFIG_MUSB_HOST +#define is_host_capable() (1) +#else +#define is_host_capable() (0) +#endif +#endif + +extern irqreturn_t musb_h_ep0_irq(struct musb *); +extern void musb_host_tx(struct musb *, u8); +extern void musb_host_rx(struct musb *, u8); + +/****************************** CONSTANTS ********************************/ + +#ifndef MUSB_C_NUM_EPS +#define MUSB_C_NUM_EPS ((u8)16) +#endif + +#ifndef MUSB_MAX_END0_PACKET +#define MUSB_MAX_END0_PACKET ((u16)MUSB_EP0_FIFOSIZE) +#endif + +/* host side ep0 states */ +enum musb_h_ep0_state { + MUSB_EP0_IDLE, + MUSB_EP0_START, /* expect ack of setup */ + MUSB_EP0_IN, /* expect IN DATA */ + MUSB_EP0_OUT, /* expect ack of OUT DATA */ + MUSB_EP0_STATUS, /* expect ack of STATUS */ +} __attribute__ ((packed)); + +/* peripheral side ep0 states */ +enum musb_g_ep0_state { + MUSB_EP0_STAGE_IDLE, /* idle, waiting for SETUP */ + MUSB_EP0_STAGE_SETUP, /* received SETUP */ + MUSB_EP0_STAGE_TX, /* IN data */ + MUSB_EP0_STAGE_RX, /* OUT data */ + MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */ + MUSB_EP0_STAGE_STATUSOUT, /* (after IN data) */ + MUSB_EP0_STAGE_ACKWAIT, /* after zlp, before statusin */ +} __attribute__ ((packed)); + +/* + * OTG protocol constants. See USB OTG 1.3 spec, + * sections 5.5 "Device Timings" and 6.6.5 "Timers". + */ +#define OTG_TIME_A_WAIT_VRISE 100 /* msec (max) */ +#define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */ +#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */ +#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */ + + +/*************************** REGISTER ACCESS ********************************/ + +/* Endpoint registers (other than dynfifo setup) can be accessed either + * directly with the "flat" model, or after setting up an index register. + */ + +#if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_SOC_OMAP2430) \ + || defined(CONFIG_SOC_OMAP3430) || defined(CONFIG_BLACKFIN) \ + || defined(CONFIG_ARCH_OMAP4) +/* REVISIT indexed access seemed to + * misbehave (on DaVinci) for at least peripheral IN ... + */ +#define MUSB_FLAT_REG +#endif + +/* TUSB mapping: "flat" plus ep0 special cases */ +#if defined(CONFIG_USB_MUSB_TUSB6010) || \ + defined(CONFIG_USB_MUSB_TUSB6010_MODULE) +#define musb_ep_select(_mbase, _epnum) \ + musb_writeb((_mbase), MUSB_INDEX, (_epnum)) +#define MUSB_EP_OFFSET MUSB_TUSB_OFFSET + +/* "flat" mapping: each endpoint has its own i/o address */ +#elif defined(MUSB_FLAT_REG) +#define musb_ep_select(_mbase, _epnum) (((void)(_mbase)), ((void)(_epnum))) +#define MUSB_EP_OFFSET MUSB_FLAT_OFFSET + +/* "indexed" mapping: INDEX register controls register bank select */ +#else +#define musb_ep_select(_mbase, _epnum) \ + musb_writeb((_mbase), MUSB_INDEX, (_epnum)) +#define MUSB_EP_OFFSET MUSB_INDEXED_OFFSET +#endif + +/****************************** FUNCTIONS ********************************/ + +#define MUSB_HST_MODE(_musb)\ + { (_musb)->is_host = true; } +#define MUSB_DEV_MODE(_musb) \ + { (_musb)->is_host = false; } + +#define test_devctl_hst_mode(_x) \ + (musb_readb((_x)->mregs, MUSB_DEVCTL)&MUSB_DEVCTL_HM) + +#define MUSB_MODE(musb) ((musb)->is_host ? "Host" : "Peripheral") + +/******************************** TYPES *************************************/ + +/** + * struct musb_platform_ops - Operations passed to musb_core by HW glue layer + * @init: turns on clocks, sets up platform-specific registers, etc + * @exit: undoes @init + * @set_mode: forcefully changes operating mode + * @try_ilde: tries to idle the IP + * @vbus_status: returns vbus status if possible + * @set_vbus: forces vbus status + * @adjust_channel_params: pre check for standard dma channel_program func + */ +struct musb_platform_ops { + int (*init)(struct musb *musb); + int (*exit)(struct musb *musb); + + void (*enable)(struct musb *musb); + void (*disable)(struct musb *musb); + + int (*set_mode)(struct musb *musb, u8 mode); + void (*try_idle)(struct musb *musb, unsigned long timeout); + + int (*vbus_status)(struct musb *musb); + void (*set_vbus)(struct musb *musb, int on); + + int (*adjust_channel_params)(struct dma_channel *channel, + u16 packet_sz, u8 *mode, + dma_addr_t *dma_addr, u32 *len); +}; + +/* + * struct musb_hw_ep - endpoint hardware (bidirectional) + * + * Ordered slightly for better cacheline locality. + */ +struct musb_hw_ep { + struct musb *musb; + void __iomem *fifo; + void __iomem *regs; + +#if defined(CONFIG_USB_MUSB_TUSB6010) || \ + defined(CONFIG_USB_MUSB_TUSB6010_MODULE) + void __iomem *conf; +#endif + + /* index in musb->endpoints[] */ + u8 epnum; + + /* hardware configuration, possibly dynamic */ + bool is_shared_fifo; + bool tx_double_buffered; + bool rx_double_buffered; + u16 max_packet_sz_tx; + u16 max_packet_sz_rx; + + struct dma_channel *tx_channel; + struct dma_channel *rx_channel; + +#if defined(CONFIG_USB_MUSB_TUSB6010) || \ + defined(CONFIG_USB_MUSB_TUSB6010_MODULE) + /* TUSB has "asynchronous" and "synchronous" dma modes */ + dma_addr_t fifo_async; + dma_addr_t fifo_sync; + void __iomem *fifo_sync_va; +#endif + + void __iomem *target_regs; + + /* currently scheduled peripheral endpoint */ + struct musb_qh *in_qh; + struct musb_qh *out_qh; + + u8 rx_reinit; + u8 tx_reinit; + + /* peripheral side */ + struct musb_ep ep_in; /* TX */ + struct musb_ep ep_out; /* RX */ +}; + +static inline struct musb_request *next_in_request(struct musb_hw_ep *hw_ep) +{ + return next_request(&hw_ep->ep_in); +} + +static inline struct musb_request *next_out_request(struct musb_hw_ep *hw_ep) +{ + return next_request(&hw_ep->ep_out); +} + +struct musb_csr_regs { + /* FIFO registers */ + u16 txmaxp, txcsr, rxmaxp, rxcsr; + u16 rxfifoadd, txfifoadd; + u8 txtype, txinterval, rxtype, rxinterval; + u8 rxfifosz, txfifosz; + u8 txfunaddr, txhubaddr, txhubport; + u8 rxfunaddr, rxhubaddr, rxhubport; +}; + +struct musb_context_registers { + + u8 power; + u16 intrtxe, intrrxe; + u8 intrusbe; + u16 frame; + u8 index, testmode; + + u8 devctl, busctl, misc; + u32 otg_interfsel; + + struct musb_csr_regs index_regs[MUSB_C_NUM_EPS]; +}; + +/* + * struct musb - Driver instance data. + */ +struct musb { + /* device lock */ + spinlock_t lock; + + const struct musb_platform_ops *ops; + struct musb_context_registers context; + + irqreturn_t (*isr)(int, void *); + struct work_struct irq_work; + u16 hwvers; + +/* this hub status bit is reserved by USB 2.0 and not seen by usbcore */ +#define MUSB_PORT_STAT_RESUME (1 << 31) + + u32 port1_status; + + unsigned long rh_timer; + + enum musb_h_ep0_state ep0_stage; + + /* bulk traffic normally dedicates endpoint hardware, and each + * direction has its own ring of host side endpoints. + * we try to progress the transfer at the head of each endpoint's + * queue until it completes or NAKs too much; then we try the next + * endpoint. + */ + struct musb_hw_ep *bulk_ep; + + struct list_head control; /* of musb_qh */ + struct list_head in_bulk; /* of musb_qh */ + struct list_head out_bulk; /* of musb_qh */ + + struct timer_list otg_timer; + struct notifier_block nb; + + struct dma_controller *dma_controller; + + struct device *controller; + void __iomem *ctrl_base; + void __iomem *mregs; + +#if defined(CONFIG_USB_MUSB_TUSB6010) || \ + defined(CONFIG_USB_MUSB_TUSB6010_MODULE) + dma_addr_t async; + dma_addr_t sync; + void __iomem *sync_va; +#endif + + /* passed down from chip/board specific irq handlers */ + u8 int_usb; + u16 int_rx; + u16 int_tx; + + struct usb_phy *xceiv; + + int nIrq; + unsigned irq_wake:1; + + struct musb_hw_ep endpoints[MUSB_C_NUM_EPS]; +#define control_ep endpoints + +#define VBUSERR_RETRY_COUNT 3 + u16 vbuserr_retry; + u16 epmask; + u8 nr_endpoints; + + u8 board_mode; /* enum musb_mode */ + int (*board_set_power)(int state); + + u8 min_power; /* vbus for periph, in mA/2 */ + + bool is_host; + + int a_wait_bcon; /* VBUS timeout in msecs */ + unsigned long idle_timeout; /* Next timeout in jiffies */ + + /* active means connected and not suspended */ + unsigned is_active:1; + + unsigned is_multipoint:1; + unsigned ignore_disconnect:1; /* during bus resets */ + + unsigned hb_iso_rx:1; /* high bandwidth iso rx? */ + unsigned hb_iso_tx:1; /* high bandwidth iso tx? */ + unsigned dyn_fifo:1; /* dynamic FIFO supported? */ + + unsigned bulk_split:1; +#define can_bulk_split(musb,type) \ + (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_split) + + unsigned bulk_combine:1; +#define can_bulk_combine(musb,type) \ + (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_combine) + + /* is_suspended means USB B_PERIPHERAL suspend */ + unsigned is_suspended:1; + + /* may_wakeup means remote wakeup is enabled */ + unsigned may_wakeup:1; + + /* is_self_powered is reported in device status and the + * config descriptor. is_bus_powered means B_PERIPHERAL + * draws some VBUS current; both can be true. + */ + unsigned is_self_powered:1; + unsigned is_bus_powered:1; + + unsigned set_address:1; + unsigned test_mode:1; + unsigned softconnect:1; + + u8 address; + u8 test_mode_nr; + u16 ackpend; /* ep0 */ + enum musb_g_ep0_state ep0_state; + struct usb_gadget g; /* the gadget */ + struct usb_gadget_driver *gadget_driver; /* its driver */ + + /* + * FIXME: Remove this flag. + * + * This is only added to allow Blackfin to work + * with current driver. For some unknown reason + * Blackfin doesn't work with double buffering + * and that's enabled by default. + * + * We added this flag to forcefully disable double + * buffering until we get it working. + */ + unsigned double_buffer_not_ok:1; + + struct musb_hdrc_config *config; + +#ifdef MUSB_CONFIG_PROC_FS + struct proc_dir_entry *proc_entry; +#endif +}; + +static inline struct musb *gadget_to_musb(struct usb_gadget *g) +{ + return container_of(g, struct musb, g); +} + +#ifdef CONFIG_BLACKFIN +static inline int musb_read_fifosize(struct musb *musb, + struct musb_hw_ep *hw_ep, u8 epnum) +{ + musb->nr_endpoints++; + musb->epmask |= (1 << epnum); + + if (epnum < 5) { + hw_ep->max_packet_sz_tx = 128; + hw_ep->max_packet_sz_rx = 128; + } else { + hw_ep->max_packet_sz_tx = 1024; + hw_ep->max_packet_sz_rx = 1024; + } + hw_ep->is_shared_fifo = false; + + return 0; +} + +static inline void musb_configure_ep0(struct musb *musb) +{ + musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE; + musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE; + musb->endpoints[0].is_shared_fifo = true; +} + +#else + +static inline int musb_read_fifosize(struct musb *musb, + struct musb_hw_ep *hw_ep, u8 epnum) +{ + void *mbase = musb->mregs; + u8 reg = 0; + + /* read from core using indexed model */ + reg = musb_readb(mbase, MUSB_EP_OFFSET(epnum, MUSB_FIFOSIZE)); + /* 0's returned when no more endpoints */ + if (!reg) + return -ENODEV; + + musb->nr_endpoints++; + musb->epmask |= (1 << epnum); + + hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f); + + /* shared TX/RX FIFO? */ + if ((reg & 0xf0) == 0xf0) { + hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx; + hw_ep->is_shared_fifo = true; + return 0; + } else { + hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4); + hw_ep->is_shared_fifo = false; + } + + return 0; +} + +static inline void musb_configure_ep0(struct musb *musb) +{ + musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE; + musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE; + musb->endpoints[0].is_shared_fifo = true; +} +#endif /* CONFIG_BLACKFIN */ + + +/***************************** Glue it together *****************************/ + +extern const char musb_driver_name[]; + +extern void musb_start(struct musb *musb); +extern void musb_stop(struct musb *musb); + +extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src); +extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst); + +extern void musb_load_testpacket(struct musb *); + +extern irqreturn_t musb_interrupt(struct musb *); + +extern void musb_hnp_stop(struct musb *musb); + +static inline void musb_platform_set_vbus(struct musb *musb, int is_on) +{ + if (musb->ops->set_vbus) + musb->ops->set_vbus(musb, is_on); +} + +static inline void musb_platform_enable(struct musb *musb) +{ + if (musb->ops->enable) + musb->ops->enable(musb); +} + +static inline void musb_platform_disable(struct musb *musb) +{ + if (musb->ops->disable) + musb->ops->disable(musb); +} + +static inline int musb_platform_set_mode(struct musb *musb, u8 mode) +{ + if (!musb->ops->set_mode) + return 0; + + return musb->ops->set_mode(musb, mode); +} + +static inline void musb_platform_try_idle(struct musb *musb, + unsigned long timeout) +{ + if (musb->ops->try_idle) + musb->ops->try_idle(musb, timeout); +} + +static inline int musb_platform_get_vbus_status(struct musb *musb) +{ + if (!musb->ops->vbus_status) + return 0; + + return musb->ops->vbus_status(musb); +} + +static inline int musb_platform_init(struct musb *musb) +{ + if (!musb->ops->init) + return -EINVAL; + + return musb->ops->init(musb); +} + +static inline int musb_platform_exit(struct musb *musb) +{ + if (!musb->ops->exit) + return -EINVAL; + + return musb->ops->exit(musb); +} + +#ifdef __UBOOT__ +struct musb * +musb_init_controller(struct musb_hdrc_platform_data *plat, struct device *dev, + void *ctrl); +#endif +#endif /* __MUSB_CORE_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_debug.h b/qemu/roms/u-boot/drivers/usb/musb-new/musb_debug.h new file mode 100644 index 000000000..27ba8f799 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_debug.h @@ -0,0 +1,58 @@ +/* + * MUSB OTG driver debug defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#ifndef __MUSB_LINUX_DEBUG_H__ +#define __MUSB_LINUX_DEBUG_H__ + +#define yprintk(facility, format, args...) \ + do { printk(facility "%s %d: " format , \ + __func__, __LINE__ , ## args); } while (0) +#define WARNING(fmt, args...) yprintk(KERN_WARNING, fmt, ## args) +#define INFO(fmt, args...) yprintk(KERN_INFO, fmt, ## args) +#define ERR(fmt, args...) yprintk(KERN_ERR, fmt, ## args) + +#ifdef CONFIG_DEBUG_FS +int musb_init_debugfs(struct musb *musb); +void musb_exit_debugfs(struct musb *musb); +#else +static inline int musb_init_debugfs(struct musb *musb) +{ + return 0; +} +static inline void musb_exit_debugfs(struct musb *musb) +{ +} +#endif + +#endif /* __MUSB_LINUX_DEBUG_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_dma.h b/qemu/roms/u-boot/drivers/usb/musb-new/musb_dma.h new file mode 100644 index 000000000..3a97c4e2d --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_dma.h @@ -0,0 +1,186 @@ +/* + * MUSB OTG driver DMA controller abstraction + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#ifndef __MUSB_DMA_H__ +#define __MUSB_DMA_H__ + +struct musb_hw_ep; + +/* + * DMA Controller Abstraction + * + * DMA Controllers are abstracted to allow use of a variety of different + * implementations of DMA, as allowed by the Inventra USB cores. On the + * host side, usbcore sets up the DMA mappings and flushes caches; on the + * peripheral side, the gadget controller driver does. Responsibilities + * of a DMA controller driver include: + * + * - Handling the details of moving multiple USB packets + * in cooperation with the Inventra USB core, including especially + * the correct RX side treatment of short packets and buffer-full + * states (both of which terminate transfers). + * + * - Knowing the correlation between dma channels and the + * Inventra core's local endpoint resources and data direction. + * + * - Maintaining a list of allocated/available channels. + * + * - Updating channel status on interrupts, + * whether shared with the Inventra core or separate. + */ + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +#ifndef CONFIG_MUSB_PIO_ONLY +#define is_dma_capable() (1) +#else +#define is_dma_capable() (0) +#endif + +#ifdef CONFIG_USB_TI_CPPI_DMA +#define is_cppi_enabled() 1 +#else +#define is_cppi_enabled() 0 +#endif + +#ifdef CONFIG_USB_TUSB_OMAP_DMA +#define tusb_dma_omap() 1 +#else +#define tusb_dma_omap() 0 +#endif + +/* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1 + * Only allow DMA mode 1 to be used when the USB will actually generate the + * interrupts we expect. + */ +#ifdef CONFIG_BLACKFIN +# undef USE_MODE1 +# if !ANOMALY_05000456 +# define USE_MODE1 +# endif +#endif + +/* + * DMA channel status ... updated by the dma controller driver whenever that + * status changes, and protected by the overall controller spinlock. + */ +enum dma_channel_status { + /* unallocated */ + MUSB_DMA_STATUS_UNKNOWN, + /* allocated ... but not busy, no errors */ + MUSB_DMA_STATUS_FREE, + /* busy ... transactions are active */ + MUSB_DMA_STATUS_BUSY, + /* transaction(s) aborted due to ... dma or memory bus error */ + MUSB_DMA_STATUS_BUS_ABORT, + /* transaction(s) aborted due to ... core error or USB fault */ + MUSB_DMA_STATUS_CORE_ABORT +}; + +struct dma_controller; + +/** + * struct dma_channel - A DMA channel. + * @private_data: channel-private data + * @max_len: the maximum number of bytes the channel can move in one + * transaction (typically representing many USB maximum-sized packets) + * @actual_len: how many bytes have been transferred + * @status: current channel status (updated e.g. on interrupt) + * @desired_mode: true if mode 1 is desired; false if mode 0 is desired + * + * channels are associated with an endpoint for the duration of at least + * one usb transfer. + */ +struct dma_channel { + void *private_data; + /* FIXME not void* private_data, but a dma_controller * */ + size_t max_len; + size_t actual_len; + enum dma_channel_status status; + bool desired_mode; +}; + +/* + * dma_channel_status - return status of dma channel + * @c: the channel + * + * Returns the software's view of the channel status. If that status is BUSY + * then it's possible that the hardware has completed (or aborted) a transfer, + * so the driver needs to update that status. + */ +static inline enum dma_channel_status +dma_channel_status(struct dma_channel *c) +{ + return (is_dma_capable() && c) ? c->status : MUSB_DMA_STATUS_UNKNOWN; +} + +/** + * struct dma_controller - A DMA Controller. + * @start: call this to start a DMA controller; + * return 0 on success, else negative errno + * @stop: call this to stop a DMA controller + * return 0 on success, else negative errno + * @channel_alloc: call this to allocate a DMA channel + * @channel_release: call this to release a DMA channel + * @channel_abort: call this to abort a pending DMA transaction, + * returning it to FREE (but allocated) state + * + * Controllers manage dma channels. + */ +struct dma_controller { + int (*start)(struct dma_controller *); + int (*stop)(struct dma_controller *); + struct dma_channel *(*channel_alloc)(struct dma_controller *, + struct musb_hw_ep *, u8 is_tx); + void (*channel_release)(struct dma_channel *); + int (*channel_program)(struct dma_channel *channel, + u16 maxpacket, u8 mode, + dma_addr_t dma_addr, + u32 length); + int (*channel_abort)(struct dma_channel *); + int (*is_compatible)(struct dma_channel *channel, + u16 maxpacket, + void *buf, u32 length); +}; + +/* called after channel_program(), may indicate a fault */ +extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit); + + +extern struct dma_controller *__init +dma_controller_create(struct musb *, void __iomem *); + +extern void dma_controller_destroy(struct dma_controller *); + +#endif /* __MUSB_DMA_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_dsps.c b/qemu/roms/u-boot/drivers/usb/musb-new/musb_dsps.c new file mode 100644 index 000000000..9a03917e8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_dsps.c @@ -0,0 +1,771 @@ +/* + * Texas Instruments DSPS platforms "glue layer" + * + * Copyright (C) 2012, by Texas Instruments + * + * Based on the am35x "glue layer" code. + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + * The Inventra Controller Driver for Linux is distributed in + * the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + * musb_dsps.c will be a common file for all the TI DSPS platforms + * such as dm64x, dm36x, dm35x, da8x, am35x and ti81x. + * For now only ti81x is using this and in future davinci.c, am35x.c + * da8xx.c would be merged to this file after testing. + */ + +#define __UBOOT__ +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#else +#include +#include +#include "linux-compat.h" +#endif + +#include "musb_core.h" + +/** + * avoid using musb_readx()/musb_writex() as glue layer should not be + * dependent on musb core layer symbols. + */ +static inline u8 dsps_readb(const void __iomem *addr, unsigned offset) + { return __raw_readb(addr + offset); } + +static inline u32 dsps_readl(const void __iomem *addr, unsigned offset) + { return __raw_readl(addr + offset); } + +static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data) + { __raw_writeb(data, addr + offset); } + +static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data) + { __raw_writel(data, addr + offset); } + +/** + * DSPS musb wrapper register offset. + * FIXME: This should be expanded to have all the wrapper registers from TI DSPS + * musb ips. + */ +struct dsps_musb_wrapper { + u16 revision; + u16 control; + u16 status; + u16 eoi; + u16 epintr_set; + u16 epintr_clear; + u16 epintr_status; + u16 coreintr_set; + u16 coreintr_clear; + u16 coreintr_status; + u16 phy_utmi; + u16 mode; + + /* bit positions for control */ + unsigned reset:5; + + /* bit positions for interrupt */ + unsigned usb_shift:5; + u32 usb_mask; + u32 usb_bitmap; + unsigned drvvbus:5; + + unsigned txep_shift:5; + u32 txep_mask; + u32 txep_bitmap; + + unsigned rxep_shift:5; + u32 rxep_mask; + u32 rxep_bitmap; + + /* bit positions for phy_utmi */ + unsigned otg_disable:5; + + /* bit positions for mode */ + unsigned iddig:5; + /* miscellaneous stuff */ + u32 musb_core_offset; + u8 poll_seconds; +}; + +static const struct dsps_musb_wrapper ti81xx_driver_data __devinitconst = { + .revision = 0x00, + .control = 0x14, + .status = 0x18, + .eoi = 0x24, + .epintr_set = 0x38, + .epintr_clear = 0x40, + .epintr_status = 0x30, + .coreintr_set = 0x3c, + .coreintr_clear = 0x44, + .coreintr_status = 0x34, + .phy_utmi = 0xe0, + .mode = 0xe8, + .reset = 0, + .otg_disable = 21, + .iddig = 8, + .usb_shift = 0, + .usb_mask = 0x1ff, + .usb_bitmap = (0x1ff << 0), + .drvvbus = 8, + .txep_shift = 0, + .txep_mask = 0xffff, + .txep_bitmap = (0xffff << 0), + .rxep_shift = 16, + .rxep_mask = 0xfffe, + .rxep_bitmap = (0xfffe << 16), + .musb_core_offset = 0x400, + .poll_seconds = 2, +}; + +/** + * DSPS glue structure. + */ +struct dsps_glue { + struct device *dev; + struct platform_device *musb; /* child musb pdev */ + const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ + struct timer_list timer; /* otg_workaround timer */ +}; + +/** + * dsps_musb_enable - enable interrupts + */ +static void dsps_musb_enable(struct musb *musb) +{ +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev->parent); + struct dsps_glue *glue = platform_get_drvdata(pdev); + const struct dsps_musb_wrapper *wrp = glue->wrp; +#else + const struct dsps_musb_wrapper *wrp = &ti81xx_driver_data; +#endif + void __iomem *reg_base = musb->ctrl_base; + u32 epmask, coremask; + + /* Workaround: setup IRQs through both register sets. */ + epmask = ((musb->epmask & wrp->txep_mask) << wrp->txep_shift) | + ((musb->epmask & wrp->rxep_mask) << wrp->rxep_shift); + coremask = (wrp->usb_bitmap & ~MUSB_INTR_SOF); + + dsps_writel(reg_base, wrp->epintr_set, epmask); + dsps_writel(reg_base, wrp->coreintr_set, coremask); + /* Force the DRVVBUS IRQ so we can start polling for ID change. */ +#ifndef __UBOOT__ + if (is_otg_enabled(musb)) + dsps_writel(reg_base, wrp->coreintr_set, + (1 << wrp->drvvbus) << wrp->usb_shift); +#endif +} + +/** + * dsps_musb_disable - disable HDRC and flush interrupts + */ +static void dsps_musb_disable(struct musb *musb) +{ +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev->parent); + struct dsps_glue *glue = platform_get_drvdata(pdev); + const struct dsps_musb_wrapper *wrp = glue->wrp; + void __iomem *reg_base = musb->ctrl_base; + + dsps_writel(reg_base, wrp->coreintr_clear, wrp->usb_bitmap); + dsps_writel(reg_base, wrp->epintr_clear, + wrp->txep_bitmap | wrp->rxep_bitmap); + dsps_writeb(musb->mregs, MUSB_DEVCTL, 0); + dsps_writel(reg_base, wrp->eoi, 0); +#endif +} + +#ifndef __UBOOT__ +static void otg_timer(unsigned long _musb) +{ + struct musb *musb = (void *)_musb; + void __iomem *mregs = musb->mregs; + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev->parent); + struct dsps_glue *glue = platform_get_drvdata(pdev); + const struct dsps_musb_wrapper *wrp = glue->wrp; + u8 devctl; + unsigned long flags; + + /* + * We poll because DSPS IP's won't expose several OTG-critical + * status change events (from the transceiver) otherwise. + */ + devctl = dsps_readb(mregs, MUSB_DEVCTL); + dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, + otg_state_string(musb->xceiv->state)); + + spin_lock_irqsave(&musb->lock, flags); + switch (musb->xceiv->state) { + case OTG_STATE_A_WAIT_BCON: + devctl &= ~MUSB_DEVCTL_SESSION; + dsps_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + devctl = dsps_readb(musb->mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) { + musb->xceiv->state = OTG_STATE_B_IDLE; + MUSB_DEV_MODE(musb); + } else { + musb->xceiv->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + } + break; + case OTG_STATE_A_WAIT_VFALL: + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; + dsps_writel(musb->ctrl_base, wrp->coreintr_set, + MUSB_INTR_VBUSERROR << wrp->usb_shift); + break; + case OTG_STATE_B_IDLE: + if (!is_peripheral_enabled(musb)) + break; + + devctl = dsps_readb(mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) + mod_timer(&glue->timer, + jiffies + wrp->poll_seconds * HZ); + else + musb->xceiv->state = OTG_STATE_A_IDLE; + break; + default: + break; + } + spin_unlock_irqrestore(&musb->lock, flags); +} + +static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) +{ + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev->parent); + struct dsps_glue *glue = platform_get_drvdata(pdev); + static unsigned long last_timer; + + if (!is_otg_enabled(musb)) + return; + + if (timeout == 0) + timeout = jiffies + msecs_to_jiffies(3); + + /* Never idle if active, or when VBUS timeout is not set as host */ + if (musb->is_active || (musb->a_wait_bcon == 0 && + musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { + dev_dbg(musb->controller, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); + del_timer(&glue->timer); + last_timer = jiffies; + return; + } + + if (time_after(last_timer, timeout) && timer_pending(&glue->timer)) { + dev_dbg(musb->controller, + "Longer idle timer already pending, ignoring...\n"); + return; + } + last_timer = timeout; + + dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", + otg_state_string(musb->xceiv->state), + jiffies_to_msecs(timeout - jiffies)); + mod_timer(&glue->timer, timeout); +} +#endif + +static irqreturn_t dsps_interrupt(int irq, void *hci) +{ + struct musb *musb = hci; + void __iomem *reg_base = musb->ctrl_base; +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev->parent); + struct dsps_glue *glue = platform_get_drvdata(pdev); + const struct dsps_musb_wrapper *wrp = glue->wrp; +#else + const struct dsps_musb_wrapper *wrp = &ti81xx_driver_data; +#endif + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + u32 epintr, usbintr; + + spin_lock_irqsave(&musb->lock, flags); + + /* Get endpoint interrupts */ + epintr = dsps_readl(reg_base, wrp->epintr_status); + musb->int_rx = (epintr & wrp->rxep_bitmap) >> wrp->rxep_shift; + musb->int_tx = (epintr & wrp->txep_bitmap) >> wrp->txep_shift; + + if (epintr) + dsps_writel(reg_base, wrp->epintr_status, epintr); + + /* Get usb core interrupts */ + usbintr = dsps_readl(reg_base, wrp->coreintr_status); + if (!usbintr && !epintr) + goto eoi; + + musb->int_usb = (usbintr & wrp->usb_bitmap) >> wrp->usb_shift; + if (usbintr) + dsps_writel(reg_base, wrp->coreintr_status, usbintr); + + dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n", + usbintr, epintr); +#ifndef __UBOOT__ + /* + * DRVVBUS IRQs are the only proxy we have (a very poor one!) for + * DSPS IP's missing ID change IRQ. We need an ID change IRQ to + * switch appropriately between halves of the OTG state machine. + * Managing DEVCTL.SESSION per Mentor docs requires that we know its + * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set. + * Also, DRVVBUS pulses for SRP (but not at 5V) ... + */ + if ((usbintr & MUSB_INTR_BABBLE) && is_host_enabled(musb)) + pr_info("CAUTION: musb: Babble Interrupt Occured\n"); + + if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) { + int drvvbus = dsps_readl(reg_base, wrp->status); + void __iomem *mregs = musb->mregs; + u8 devctl = dsps_readb(mregs, MUSB_DEVCTL); + int err; + + err = is_host_enabled(musb) && (musb->int_usb & + MUSB_INTR_VBUSERROR); + if (err) { + /* + * The Mentor core doesn't debounce VBUS as needed + * to cope with device connect current spikes. This + * means it's not uncommon for bus-powered devices + * to get VBUS errors during enumeration. + * + * This is a workaround, but newer RTL from Mentor + * seems to allow a better one: "re"-starting sessions + * without waiting for VBUS to stop registering in + * devctl. + */ + musb->int_usb &= ~MUSB_INTR_VBUSERROR; + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; + mod_timer(&glue->timer, + jiffies + wrp->poll_seconds * HZ); + WARNING("VBUS error workaround (delay coming)\n"); + } else if (is_host_enabled(musb) && drvvbus) { + musb->is_active = 1; + MUSB_HST_MODE(musb); + musb->xceiv->otg->default_a = 1; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; + del_timer(&glue->timer); + } else { + musb->is_active = 0; + MUSB_DEV_MODE(musb); + musb->xceiv->otg->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; + } + + /* NOTE: this must complete power-on within 100 ms. */ + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", + drvvbus ? "on" : "off", + otg_state_string(musb->xceiv->state), + err ? " ERROR" : "", + devctl); + ret = IRQ_HANDLED; + } +#endif + + if (musb->int_tx || musb->int_rx || musb->int_usb) + ret |= musb_interrupt(musb); + + eoi: + /* EOI needs to be written for the IRQ to be re-asserted. */ + if (ret == IRQ_HANDLED || epintr || usbintr) + dsps_writel(reg_base, wrp->eoi, 1); + +#ifndef __UBOOT__ + /* Poll for ID change */ + if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE) + mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); +#endif + + spin_unlock_irqrestore(&musb->lock, flags); + + return ret; +} + +static int dsps_musb_init(struct musb *musb) +{ +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct platform_device *pdev = to_platform_device(dev->parent); + struct dsps_glue *glue = platform_get_drvdata(pdev); + const struct dsps_musb_wrapper *wrp = glue->wrp; + struct omap_musb_board_data *data = plat->board_data; +#else + struct omap_musb_board_data *data = + (struct omap_musb_board_data *)musb->controller; + const struct dsps_musb_wrapper *wrp = &ti81xx_driver_data; +#endif + void __iomem *reg_base = musb->ctrl_base; + u32 rev, val; + int status; + + /* mentor core register starts at offset of 0x400 from musb base */ + musb->mregs += wrp->musb_core_offset; + +#ifndef __UBOOT__ + /* NOP driver needs change if supporting dual instance */ + usb_nop_xceiv_register(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(musb->xceiv)) + return -ENODEV; +#endif + + /* Returns zero if e.g. not clocked */ + rev = dsps_readl(reg_base, wrp->revision); + if (!rev) { + status = -ENODEV; + goto err0; + } + +#ifndef __UBOOT__ + if (is_host_enabled(musb)) + setup_timer(&glue->timer, otg_timer, (unsigned long) musb); +#endif + + /* Reset the musb */ + dsps_writel(reg_base, wrp->control, (1 << wrp->reset)); + + /* Start the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(1); + + musb->isr = dsps_interrupt; + + /* reset the otgdisable bit, needed for host mode to work */ + val = dsps_readl(reg_base, wrp->phy_utmi); + val &= ~(1 << wrp->otg_disable); + dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); + + /* clear level interrupt */ + dsps_writel(reg_base, wrp->eoi, 0); + + return 0; +err0: +#ifndef __UBOOT__ + usb_put_phy(musb->xceiv); + usb_nop_xceiv_unregister(); +#endif + return status; +} + +static int dsps_musb_exit(struct musb *musb) +{ +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + struct platform_device *pdev = to_platform_device(dev->parent); + struct dsps_glue *glue = platform_get_drvdata(pdev); +#else + struct omap_musb_board_data *data = + (struct omap_musb_board_data *)musb->controller; +#endif + +#ifndef __UBOOT__ + if (is_host_enabled(musb)) + del_timer_sync(&glue->timer); +#endif + + /* Shutdown the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(0); + +#ifndef __UBOOT__ + /* NOP driver needs change if supporting dual instance */ + usb_put_phy(musb->xceiv); + usb_nop_xceiv_unregister(); +#endif + + return 0; +} + +#ifndef __UBOOT__ +static struct musb_platform_ops dsps_ops = { +#else +struct musb_platform_ops musb_dsps_ops = { +#endif + .init = dsps_musb_init, + .exit = dsps_musb_exit, + + .enable = dsps_musb_enable, + .disable = dsps_musb_disable, + +#ifndef __UBOOT__ + .try_idle = dsps_musb_try_idle, +#endif +}; + +#ifndef __UBOOT__ +static u64 musb_dmamask = DMA_BIT_MASK(32); +#endif + +#ifndef __UBOOT__ +static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id) +{ + struct device *dev = glue->dev; + struct platform_device *pdev = to_platform_device(dev); + struct musb_hdrc_platform_data *pdata = dev->platform_data; + struct platform_device *musb; + struct resource *res; + struct resource resources[2]; + char res_name[10]; + int ret; + + /* get memory resource */ + sprintf(res_name, "musb%d", id); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); + if (!res) { + dev_err(dev, "%s get mem resource failed\n", res_name); + ret = -ENODEV; + goto err0; + } + res->parent = NULL; + resources[0] = *res; + + /* get irq resource */ + sprintf(res_name, "musb%d-irq", id); + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name); + if (!res) { + dev_err(dev, "%s get irq resource failed\n", res_name); + ret = -ENODEV; + goto err0; + } + res->parent = NULL; + resources[1] = *res; + resources[1].name = "mc"; + + /* allocate the child platform device */ + musb = platform_device_alloc("musb-hdrc", -1); + if (!musb) { + dev_err(dev, "failed to allocate musb device\n"); + ret = -ENOMEM; + goto err0; + } + + musb->dev.parent = dev; + musb->dev.dma_mask = &musb_dmamask; + musb->dev.coherent_dma_mask = musb_dmamask; + + glue->musb = musb; + + pdata->platform_ops = &dsps_ops; + + ret = platform_device_add_resources(musb, resources, 2); + if (ret) { + dev_err(dev, "failed to add resources\n"); + goto err1; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(dev, "failed to add platform_data\n"); + goto err1; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(dev, "failed to register musb device\n"); + goto err1; + } + + return 0; + +err1: + platform_device_put(musb); +err0: + return ret; +} + +static void __devexit dsps_delete_musb_pdev(struct dsps_glue *glue) +{ + platform_device_del(glue->musb); + platform_device_put(glue->musb); +} + +static int __devinit dsps_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + const struct dsps_musb_wrapper *wrp = + (struct dsps_musb_wrapper *)id->driver_data; + struct dsps_glue *glue; + struct resource *iomem; + int ret; + + /* allocate glue */ + glue = kzalloc(sizeof(*glue), GFP_KERNEL); + if (!glue) { + dev_err(&pdev->dev, "unable to allocate glue memory\n"); + ret = -ENOMEM; + goto err0; + } + + /* get memory resource */ + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) { + dev_err(&pdev->dev, "failed to get usbss mem resourse\n"); + ret = -ENODEV; + goto err1; + } + + glue->dev = &pdev->dev; + + glue->wrp = kmemdup(wrp, sizeof(*wrp), GFP_KERNEL); + if (!glue->wrp) { + dev_err(&pdev->dev, "failed to duplicate wrapper struct memory\n"); + ret = -ENOMEM; + goto err1; + } + platform_set_drvdata(pdev, glue); + + /* enable the usbss clocks */ + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "pm_runtime_get_sync FAILED"); + goto err2; + } + + /* create the child platform device for first instances of musb */ + ret = dsps_create_musb_pdev(glue, 0); + if (ret != 0) { + dev_err(&pdev->dev, "failed to create child pdev\n"); + goto err3; + } + + return 0; + +err3: + pm_runtime_put(&pdev->dev); +err2: + pm_runtime_disable(&pdev->dev); + kfree(glue->wrp); +err1: + kfree(glue); +err0: + return ret; +} +static int __devexit dsps_remove(struct platform_device *pdev) +{ + struct dsps_glue *glue = platform_get_drvdata(pdev); + + /* delete the child platform device */ + dsps_delete_musb_pdev(glue); + + /* disable usbss clocks */ + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + kfree(glue->wrp); + kfree(glue); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dsps_suspend(struct device *dev) +{ + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + + /* Shutdown the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(0); + + return 0; +} + +static int dsps_resume(struct device *dev) +{ + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + + /* Start the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(1); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume); +#endif + +#ifndef __UBOOT__ +static const struct platform_device_id musb_dsps_id_table[] __devinitconst = { + { + .name = "musb-ti81xx", + .driver_data = (kernel_ulong_t) &ti81xx_driver_data, + }, + { }, /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(platform, musb_dsps_id_table); + +static const struct of_device_id musb_dsps_of_match[] __devinitconst = { + { .compatible = "musb-ti81xx", }, + { .compatible = "ti,ti81xx-musb", }, + { .compatible = "ti,am335x-musb", }, + { }, +}; +MODULE_DEVICE_TABLE(of, musb_dsps_of_match); + +static struct platform_driver dsps_usbss_driver = { + .probe = dsps_probe, + .remove = __devexit_p(dsps_remove), + .driver = { + .name = "musb-dsps", + .pm = &dsps_pm_ops, + .of_match_table = musb_dsps_of_match, + }, + .id_table = musb_dsps_id_table, +}; + +MODULE_DESCRIPTION("TI DSPS MUSB Glue Layer"); +MODULE_AUTHOR("Ravi B "); +MODULE_AUTHOR("Ajay Kumar Gupta "); +MODULE_LICENSE("GPL v2"); + +static int __init dsps_init(void) +{ + return platform_driver_register(&dsps_usbss_driver); +} +subsys_initcall(dsps_init); + +static void __exit dsps_exit(void) +{ + platform_driver_unregister(&dsps_usbss_driver); +} +module_exit(dsps_exit); +#endif diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget.c b/qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget.c new file mode 100644 index 000000000..d2cb91a89 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget.c @@ -0,0 +1,2333 @@ +/* + * MUSB OTG driver peripheral support + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * Copyright (C) 2009 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#define __UBOOT__ +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include "linux-compat.h" +#endif + +#include "musb_core.h" + + +/* MUSB PERIPHERAL status 3-mar-2006: + * + * - EP0 seems solid. It passes both USBCV and usbtest control cases. + * Minor glitches: + * + * + remote wakeup to Linux hosts work, but saw USBCV failures; + * in one test run (operator error?) + * + endpoint halt tests -- in both usbtest and usbcv -- seem + * to break when dma is enabled ... is something wrongly + * clearing SENDSTALL? + * + * - Mass storage behaved ok when last tested. Network traffic patterns + * (with lots of short transfers etc) need retesting; they turn up the + * worst cases of the DMA, since short packets are typical but are not + * required. + * + * - TX/IN + * + both pio and dma behave in with network and g_zero tests + * + no cppi throughput issues other than no-hw-queueing + * + failed with FLAT_REG (DaVinci) + * + seems to behave with double buffering, PIO -and- CPPI + * + with gadgetfs + AIO, requests got lost? + * + * - RX/OUT + * + both pio and dma behave in with network and g_zero tests + * + dma is slow in typical case (short_not_ok is clear) + * + double buffering ok with PIO + * + double buffering *FAILS* with CPPI, wrong data bytes sometimes + * + request lossage observed with gadgetfs + * + * - ISO not tested ... might work, but only weakly isochronous + * + * - Gadget driver disabling of softconnect during bind() is ignored; so + * drivers can't hold off host requests until userspace is ready. + * (Workaround: they can turn it off later.) + * + * - PORTABILITY (assumes PIO works): + * + DaVinci, basically works with cppi dma + * + OMAP 2430, ditto with mentor dma + * + TUSB 6010, platform-specific dma in the works + */ + +/* ----------------------------------------------------------------------- */ + +#define is_buffer_mapped(req) (is_dma_capable() && \ + (req->map_state != UN_MAPPED)) + +#ifndef CONFIG_MUSB_PIO_ONLY +/* Maps the buffer to dma */ + +static inline void map_dma_buffer(struct musb_request *request, + struct musb *musb, struct musb_ep *musb_ep) +{ + int compatible = true; + struct dma_controller *dma = musb->dma_controller; + + request->map_state = UN_MAPPED; + + if (!is_dma_capable() || !musb_ep->dma) + return; + + /* Check if DMA engine can handle this request. + * DMA code must reject the USB request explicitly. + * Default behaviour is to map the request. + */ + if (dma->is_compatible) + compatible = dma->is_compatible(musb_ep->dma, + musb_ep->packet_sz, request->request.buf, + request->request.length); + if (!compatible) + return; + + if (request->request.dma == DMA_ADDR_INVALID) { + request->request.dma = dma_map_single( + musb->controller, + request->request.buf, + request->request.length, + request->tx + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + request->map_state = MUSB_MAPPED; + } else { + dma_sync_single_for_device(musb->controller, + request->request.dma, + request->request.length, + request->tx + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + request->map_state = PRE_MAPPED; + } +} + +/* Unmap the buffer from dma and maps it back to cpu */ +static inline void unmap_dma_buffer(struct musb_request *request, + struct musb *musb) +{ + if (!is_buffer_mapped(request)) + return; + + if (request->request.dma == DMA_ADDR_INVALID) { + dev_vdbg(musb->controller, + "not unmapping a never mapped buffer\n"); + return; + } + if (request->map_state == MUSB_MAPPED) { + dma_unmap_single(musb->controller, + request->request.dma, + request->request.length, + request->tx + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + request->request.dma = DMA_ADDR_INVALID; + } else { /* PRE_MAPPED */ + dma_sync_single_for_cpu(musb->controller, + request->request.dma, + request->request.length, + request->tx + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + } + request->map_state = UN_MAPPED; +} +#else +static inline void map_dma_buffer(struct musb_request *request, + struct musb *musb, struct musb_ep *musb_ep) +{ +} + +static inline void unmap_dma_buffer(struct musb_request *request, + struct musb *musb) +{ +} +#endif + +/* + * Immediately complete a request. + * + * @param request the request to complete + * @param status the status to complete the request with + * Context: controller locked, IRQs blocked. + */ +void musb_g_giveback( + struct musb_ep *ep, + struct usb_request *request, + int status) +__releases(ep->musb->lock) +__acquires(ep->musb->lock) +{ + struct musb_request *req; + struct musb *musb; + int busy = ep->busy; + + req = to_musb_request(request); + + list_del(&req->list); + if (req->request.status == -EINPROGRESS) + req->request.status = status; + musb = req->musb; + + ep->busy = 1; + spin_unlock(&musb->lock); + unmap_dma_buffer(req, musb); + if (request->status == 0) + dev_dbg(musb->controller, "%s done request %p, %d/%d\n", + ep->end_point.name, request, + req->request.actual, req->request.length); + else + dev_dbg(musb->controller, "%s request %p, %d/%d fault %d\n", + ep->end_point.name, request, + req->request.actual, req->request.length, + request->status); + req->request.complete(&req->ep->end_point, &req->request); + spin_lock(&musb->lock); + ep->busy = busy; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Abort requests queued to an endpoint using the status. Synchronous. + * caller locked controller and blocked irqs, and selected this ep. + */ +static void nuke(struct musb_ep *ep, const int status) +{ + struct musb *musb = ep->musb; + struct musb_request *req = NULL; + void __iomem *epio = ep->musb->endpoints[ep->current_epnum].regs; + + ep->busy = 1; + + if (is_dma_capable() && ep->dma) { + struct dma_controller *c = ep->musb->dma_controller; + int value; + + if (ep->is_in) { + /* + * The programming guide says that we must not clear + * the DMAMODE bit before DMAENAB, so we only + * clear it in the second write... + */ + musb_writew(epio, MUSB_TXCSR, + MUSB_TXCSR_DMAMODE | MUSB_TXCSR_FLUSHFIFO); + musb_writew(epio, MUSB_TXCSR, + 0 | MUSB_TXCSR_FLUSHFIFO); + } else { + musb_writew(epio, MUSB_RXCSR, + 0 | MUSB_RXCSR_FLUSHFIFO); + musb_writew(epio, MUSB_RXCSR, + 0 | MUSB_RXCSR_FLUSHFIFO); + } + + value = c->channel_abort(ep->dma); + dev_dbg(musb->controller, "%s: abort DMA --> %d\n", + ep->name, value); + c->channel_release(ep->dma); + ep->dma = NULL; + } + + while (!list_empty(&ep->req_list)) { + req = list_first_entry(&ep->req_list, struct musb_request, list); + musb_g_giveback(ep, &req->request, status); + } +} + +/* ----------------------------------------------------------------------- */ + +/* Data transfers - pure PIO, pure DMA, or mixed mode */ + +/* + * This assumes the separate CPPI engine is responding to DMA requests + * from the usb core ... sequenced a bit differently from mentor dma. + */ + +static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep) +{ + if (can_bulk_split(musb, ep->type)) + return ep->hw_ep->max_packet_sz_tx; + else + return ep->packet_sz; +} + + +#ifdef CONFIG_USB_INVENTRA_DMA + +/* Peripheral tx (IN) using Mentor DMA works as follows: + Only mode 0 is used for transfers <= wPktSize, + mode 1 is used for larger transfers, + + One of the following happens: + - Host sends IN token which causes an endpoint interrupt + -> TxAvail + -> if DMA is currently busy, exit. + -> if queue is non-empty, txstate(). + + - Request is queued by the gadget driver. + -> if queue was previously empty, txstate() + + txstate() + -> start + /\ -> setup DMA + | (data is transferred to the FIFO, then sent out when + | IN token(s) are recd from Host. + | -> DMA interrupt on completion + | calls TxAvail. + | -> stop DMA, ~DMAENAB, + | -> set TxPktRdy for last short pkt or zlp + | -> Complete Request + | -> Continue next request (call txstate) + |___________________________________| + + * Non-Mentor DMA engines can of course work differently, such as by + * upleveling from irq-per-packet to irq-per-buffer. + */ + +#endif + +/* + * An endpoint is transmitting data. This can be called either from + * the IRQ routine or from ep.queue() to kickstart a request on an + * endpoint. + * + * Context: controller locked, IRQs blocked, endpoint selected + */ +static void txstate(struct musb *musb, struct musb_request *req) +{ + u8 epnum = req->epnum; + struct musb_ep *musb_ep; + void __iomem *epio = musb->endpoints[epnum].regs; + struct usb_request *request; + u16 fifo_count = 0, csr; + int use_dma = 0; + + musb_ep = req->ep; + + /* Check if EP is disabled */ + if (!musb_ep->desc) { + dev_dbg(musb->controller, "ep:%s disabled - ignore request\n", + musb_ep->end_point.name); + return; + } + + /* we shouldn't get here while DMA is active ... but we do ... */ + if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { + dev_dbg(musb->controller, "dma pending...\n"); + return; + } + + /* read TXCSR before */ + csr = musb_readw(epio, MUSB_TXCSR); + + request = &req->request; + fifo_count = min(max_ep_writesize(musb, musb_ep), + (int)(request->length - request->actual)); + + if (csr & MUSB_TXCSR_TXPKTRDY) { + dev_dbg(musb->controller, "%s old packet still ready , txcsr %03x\n", + musb_ep->end_point.name, csr); + return; + } + + if (csr & MUSB_TXCSR_P_SENDSTALL) { + dev_dbg(musb->controller, "%s stalling, txcsr %03x\n", + musb_ep->end_point.name, csr); + return; + } + + dev_dbg(musb->controller, "hw_ep%d, maxpacket %d, fifo count %d, txcsr %03x\n", + epnum, musb_ep->packet_sz, fifo_count, + csr); + +#ifndef CONFIG_MUSB_PIO_ONLY + if (is_buffer_mapped(req)) { + struct dma_controller *c = musb->dma_controller; + size_t request_size; + + /* setup DMA, then program endpoint CSR */ + request_size = min_t(size_t, request->length - request->actual, + musb_ep->dma->max_len); + + use_dma = (request->dma != DMA_ADDR_INVALID); + + /* MUSB_TXCSR_P_ISO is still set correctly */ + +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) + { + if (request_size < musb_ep->packet_sz) + musb_ep->dma->desired_mode = 0; + else + musb_ep->dma->desired_mode = 1; + + use_dma = use_dma && c->channel_program( + musb_ep->dma, musb_ep->packet_sz, + musb_ep->dma->desired_mode, + request->dma + request->actual, request_size); + if (use_dma) { + if (musb_ep->dma->desired_mode == 0) { + /* + * We must not clear the DMAMODE bit + * before the DMAENAB bit -- and the + * latter doesn't always get cleared + * before we get here... + */ + csr &= ~(MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAENAB); + musb_writew(epio, MUSB_TXCSR, csr + | MUSB_TXCSR_P_WZC_BITS); + csr &= ~MUSB_TXCSR_DMAMODE; + csr |= (MUSB_TXCSR_DMAENAB | + MUSB_TXCSR_MODE); + /* against programming guide */ + } else { + csr |= (MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_DMAMODE + | MUSB_TXCSR_MODE); + if (!musb_ep->hb_mult) + csr |= MUSB_TXCSR_AUTOSET; + } + csr &= ~MUSB_TXCSR_P_UNDERRUN; + + musb_writew(epio, MUSB_TXCSR, csr); + } + } + +#elif defined(CONFIG_USB_TI_CPPI_DMA) + /* program endpoint CSR first, then setup DMA */ + csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); + csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE | + MUSB_TXCSR_MODE; + musb_writew(epio, MUSB_TXCSR, + (MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN) + | csr); + + /* ensure writebuffer is empty */ + csr = musb_readw(epio, MUSB_TXCSR); + + /* NOTE host side sets DMAENAB later than this; both are + * OK since the transfer dma glue (between CPPI and Mentor + * fifos) just tells CPPI it could start. Data only moves + * to the USB TX fifo when both fifos are ready. + */ + + /* "mode" is irrelevant here; handle terminating ZLPs like + * PIO does, since the hardware RNDIS mode seems unreliable + * except for the last-packet-is-already-short case. + */ + use_dma = use_dma && c->channel_program( + musb_ep->dma, musb_ep->packet_sz, + 0, + request->dma + request->actual, + request_size); + if (!use_dma) { + c->channel_release(musb_ep->dma); + musb_ep->dma = NULL; + csr &= ~MUSB_TXCSR_DMAENAB; + musb_writew(epio, MUSB_TXCSR, csr); + /* invariant: prequest->buf is non-null */ + } +#elif defined(CONFIG_USB_TUSB_OMAP_DMA) + use_dma = use_dma && c->channel_program( + musb_ep->dma, musb_ep->packet_sz, + request->zero, + request->dma + request->actual, + request_size); +#endif + } +#endif + + if (!use_dma) { + /* + * Unmap the dma buffer back to cpu if dma channel + * programming fails + */ + unmap_dma_buffer(req, musb); + + musb_write_fifo(musb_ep->hw_ep, fifo_count, + (u8 *) (request->buf + request->actual)); + request->actual += fifo_count; + csr |= MUSB_TXCSR_TXPKTRDY; + csr &= ~MUSB_TXCSR_P_UNDERRUN; + musb_writew(epio, MUSB_TXCSR, csr); + } + + /* host may already have the data when this message shows... */ + dev_dbg(musb->controller, "%s TX/IN %s len %d/%d, txcsr %04x, fifo %d/%d\n", + musb_ep->end_point.name, use_dma ? "dma" : "pio", + request->actual, request->length, + musb_readw(epio, MUSB_TXCSR), + fifo_count, + musb_readw(epio, MUSB_TXMAXP)); +} + +/* + * FIFO state update (e.g. data ready). + * Called from IRQ, with controller locked. + */ +void musb_g_tx(struct musb *musb, u8 epnum) +{ + u16 csr; + struct musb_request *req; + struct usb_request *request; + u8 __iomem *mbase = musb->mregs; + struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_in; + void __iomem *epio = musb->endpoints[epnum].regs; + struct dma_channel *dma; + + musb_ep_select(mbase, epnum); + req = next_request(musb_ep); + request = &req->request; + + csr = musb_readw(epio, MUSB_TXCSR); + dev_dbg(musb->controller, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); + + dma = is_dma_capable() ? musb_ep->dma : NULL; + + /* + * REVISIT: for high bandwidth, MUSB_TXCSR_P_INCOMPTX + * probably rates reporting as a host error. + */ + if (csr & MUSB_TXCSR_P_SENTSTALL) { + csr |= MUSB_TXCSR_P_WZC_BITS; + csr &= ~MUSB_TXCSR_P_SENTSTALL; + musb_writew(epio, MUSB_TXCSR, csr); + return; + } + + if (csr & MUSB_TXCSR_P_UNDERRUN) { + /* We NAKed, no big deal... little reason to care. */ + csr |= MUSB_TXCSR_P_WZC_BITS; + csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, csr); + dev_vdbg(musb->controller, "underrun on ep%d, req %p\n", + epnum, request); + } + + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + /* + * SHOULD NOT HAPPEN... has with CPPI though, after + * changing SENDSTALL (and other cases); harmless? + */ + dev_dbg(musb->controller, "%s dma still busy?\n", musb_ep->end_point.name); + return; + } + + if (request) { + u8 is_dma = 0; + + if (dma && (csr & MUSB_TXCSR_DMAENAB)) { + is_dma = 1; + csr |= MUSB_TXCSR_P_WZC_BITS; + csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN | + MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_AUTOSET); + musb_writew(epio, MUSB_TXCSR, csr); + /* Ensure writebuffer is empty. */ + csr = musb_readw(epio, MUSB_TXCSR); + request->actual += musb_ep->dma->actual_len; + dev_dbg(musb->controller, "TXCSR%d %04x, DMA off, len %zu, req %p\n", + epnum, csr, musb_ep->dma->actual_len, request); + } + + /* + * First, maybe a terminating short packet. Some DMA + * engines might handle this by themselves. + */ + if ((request->zero && request->length + && (request->length % musb_ep->packet_sz == 0) + && (request->actual == request->length)) +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) + || (is_dma && (!dma->desired_mode || + (request->actual & + (musb_ep->packet_sz - 1)))) +#endif + ) { + /* + * On DMA completion, FIFO may not be + * available yet... + */ + if (csr & MUSB_TXCSR_TXPKTRDY) + return; + + dev_dbg(musb->controller, "sending zero pkt\n"); + musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE + | MUSB_TXCSR_TXPKTRDY); + request->zero = 0; + } + + if (request->actual == request->length) { + musb_g_giveback(musb_ep, request, 0); + /* + * In the giveback function the MUSB lock is + * released and acquired after sometime. During + * this time period the INDEX register could get + * changed by the gadget_queue function especially + * on SMP systems. Reselect the INDEX to be sure + * we are reading/modifying the right registers + */ + musb_ep_select(mbase, epnum); + req = musb_ep->desc ? next_request(musb_ep) : NULL; + if (!req) { + dev_dbg(musb->controller, "%s idle now\n", + musb_ep->end_point.name); + return; + } + } + + txstate(musb, req); + } +} + +/* ------------------------------------------------------------ */ + +#ifdef CONFIG_USB_INVENTRA_DMA + +/* Peripheral rx (OUT) using Mentor DMA works as follows: + - Only mode 0 is used. + + - Request is queued by the gadget class driver. + -> if queue was previously empty, rxstate() + + - Host sends OUT token which causes an endpoint interrupt + /\ -> RxReady + | -> if request queued, call rxstate + | /\ -> setup DMA + | | -> DMA interrupt on completion + | | -> RxReady + | | -> stop DMA + | | -> ack the read + | | -> if data recd = max expected + | | by the request, or host + | | sent a short packet, + | | complete the request, + | | and start the next one. + | |_____________________________________| + | else just wait for the host + | to send the next OUT token. + |__________________________________________________| + + * Non-Mentor DMA engines can of course work differently. + */ + +#endif + +/* + * Context: controller locked, IRQs blocked, endpoint selected + */ +static void rxstate(struct musb *musb, struct musb_request *req) +{ + const u8 epnum = req->epnum; + struct usb_request *request = &req->request; + struct musb_ep *musb_ep; + void __iomem *epio = musb->endpoints[epnum].regs; + unsigned fifo_count = 0; + u16 len; + u16 csr = musb_readw(epio, MUSB_RXCSR); + struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + u8 use_mode_1; + + if (hw_ep->is_shared_fifo) + musb_ep = &hw_ep->ep_in; + else + musb_ep = &hw_ep->ep_out; + + len = musb_ep->packet_sz; + + /* Check if EP is disabled */ + if (!musb_ep->desc) { + dev_dbg(musb->controller, "ep:%s disabled - ignore request\n", + musb_ep->end_point.name); + return; + } + + /* We shouldn't get here while DMA is active, but we do... */ + if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { + dev_dbg(musb->controller, "DMA pending...\n"); + return; + } + + if (csr & MUSB_RXCSR_P_SENDSTALL) { + dev_dbg(musb->controller, "%s stalling, RXCSR %04x\n", + musb_ep->end_point.name, csr); + return; + } + + if (is_cppi_enabled() && is_buffer_mapped(req)) { + struct dma_controller *c = musb->dma_controller; + struct dma_channel *channel = musb_ep->dma; + + /* NOTE: CPPI won't actually stop advancing the DMA + * queue after short packet transfers, so this is almost + * always going to run as IRQ-per-packet DMA so that + * faults will be handled correctly. + */ + if (c->channel_program(channel, + musb_ep->packet_sz, + !request->short_not_ok, + request->dma + request->actual, + request->length - request->actual)) { + + /* make sure that if an rxpkt arrived after the irq, + * the cppi engine will be ready to take it as soon + * as DMA is enabled + */ + csr &= ~(MUSB_RXCSR_AUTOCLEAR + | MUSB_RXCSR_DMAMODE); + csr |= MUSB_RXCSR_DMAENAB | MUSB_RXCSR_P_WZC_BITS; + musb_writew(epio, MUSB_RXCSR, csr); + return; + } + } + + if (csr & MUSB_RXCSR_RXPKTRDY) { + len = musb_readw(epio, MUSB_RXCOUNT); + + /* + * Enable Mode 1 on RX transfers only when short_not_ok flag + * is set. Currently short_not_ok flag is set only from + * file_storage and f_mass_storage drivers + */ + + if (request->short_not_ok && len == musb_ep->packet_sz) + use_mode_1 = 1; + else + use_mode_1 = 0; + + if (request->actual < request->length) { +#ifdef CONFIG_USB_INVENTRA_DMA + if (is_buffer_mapped(req)) { + struct dma_controller *c; + struct dma_channel *channel; + int use_dma = 0; + + c = musb->dma_controller; + channel = musb_ep->dma; + + /* We use DMA Req mode 0 in rx_csr, and DMA controller operates in + * mode 0 only. So we do not get endpoint interrupts due to DMA + * completion. We only get interrupts from DMA controller. + * + * We could operate in DMA mode 1 if we knew the size of the tranfer + * in advance. For mass storage class, request->length = what the host + * sends, so that'd work. But for pretty much everything else, + * request->length is routinely more than what the host sends. For + * most these gadgets, end of is signified either by a short packet, + * or filling the last byte of the buffer. (Sending extra data in + * that last pckate should trigger an overflow fault.) But in mode 1, + * we don't get DMA completion interrupt for short packets. + * + * Theoretically, we could enable DMAReq irq (MUSB_RXCSR_DMAMODE = 1), + * to get endpoint interrupt on every DMA req, but that didn't seem + * to work reliably. + * + * REVISIT an updated g_file_storage can set req->short_not_ok, which + * then becomes usable as a runtime "use mode 1" hint... + */ + + /* Experimental: Mode1 works with mass storage use cases */ + if (use_mode_1) { + csr |= MUSB_RXCSR_AUTOCLEAR; + musb_writew(epio, MUSB_RXCSR, csr); + csr |= MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + + /* + * this special sequence (enabling and then + * disabling MUSB_RXCSR_DMAMODE) is required + * to get DMAReq to activate + */ + musb_writew(epio, MUSB_RXCSR, + csr | MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, csr); + + } else { + if (!musb_ep->hb_mult && + musb_ep->hw_ep->rx_double_buffered) + csr |= MUSB_RXCSR_AUTOCLEAR; + csr |= MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + } + + if (request->actual < request->length) { + int transfer_size = 0; + if (use_mode_1) { + transfer_size = min(request->length - request->actual, + channel->max_len); + musb_ep->dma->desired_mode = 1; + } else { + transfer_size = min(request->length - request->actual, + (unsigned)len); + musb_ep->dma->desired_mode = 0; + } + + use_dma = c->channel_program( + channel, + musb_ep->packet_sz, + channel->desired_mode, + request->dma + + request->actual, + transfer_size); + } + + if (use_dma) + return; + } +#elif defined(CONFIG_USB_UX500_DMA) + if ((is_buffer_mapped(req)) && + (request->actual < request->length)) { + + struct dma_controller *c; + struct dma_channel *channel; + int transfer_size = 0; + + c = musb->dma_controller; + channel = musb_ep->dma; + + /* In case first packet is short */ + if (len < musb_ep->packet_sz) + transfer_size = len; + else if (request->short_not_ok) + transfer_size = min(request->length - + request->actual, + channel->max_len); + else + transfer_size = min(request->length - + request->actual, + (unsigned)len); + + csr &= ~MUSB_RXCSR_DMAMODE; + csr |= (MUSB_RXCSR_DMAENAB | + MUSB_RXCSR_AUTOCLEAR); + + musb_writew(epio, MUSB_RXCSR, csr); + + if (transfer_size <= musb_ep->packet_sz) { + musb_ep->dma->desired_mode = 0; + } else { + musb_ep->dma->desired_mode = 1; + /* Mode must be set after DMAENAB */ + csr |= MUSB_RXCSR_DMAMODE; + musb_writew(epio, MUSB_RXCSR, csr); + } + + if (c->channel_program(channel, + musb_ep->packet_sz, + channel->desired_mode, + request->dma + + request->actual, + transfer_size)) + + return; + } +#endif /* Mentor's DMA */ + + fifo_count = request->length - request->actual; + dev_dbg(musb->controller, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n", + musb_ep->end_point.name, + len, fifo_count, + musb_ep->packet_sz); + + fifo_count = min_t(unsigned, len, fifo_count); + +#ifdef CONFIG_USB_TUSB_OMAP_DMA + if (tusb_dma_omap() && is_buffer_mapped(req)) { + struct dma_controller *c = musb->dma_controller; + struct dma_channel *channel = musb_ep->dma; + u32 dma_addr = request->dma + request->actual; + int ret; + + ret = c->channel_program(channel, + musb_ep->packet_sz, + channel->desired_mode, + dma_addr, + fifo_count); + if (ret) + return; + } +#endif + /* + * Unmap the dma buffer back to cpu if dma channel + * programming fails. This buffer is mapped if the + * channel allocation is successful + */ + if (is_buffer_mapped(req)) { + unmap_dma_buffer(req, musb); + + /* + * Clear DMAENAB and AUTOCLEAR for the + * PIO mode transfer + */ + csr &= ~(MUSB_RXCSR_DMAENAB | MUSB_RXCSR_AUTOCLEAR); + musb_writew(epio, MUSB_RXCSR, csr); + } + + musb_read_fifo(musb_ep->hw_ep, fifo_count, (u8 *) + (request->buf + request->actual)); + request->actual += fifo_count; + + /* REVISIT if we left anything in the fifo, flush + * it and report -EOVERFLOW + */ + + /* ack the read! */ + csr |= MUSB_RXCSR_P_WZC_BITS; + csr &= ~MUSB_RXCSR_RXPKTRDY; + musb_writew(epio, MUSB_RXCSR, csr); + } + } + + /* reach the end or short packet detected */ + if (request->actual == request->length || len < musb_ep->packet_sz) + musb_g_giveback(musb_ep, request, 0); +} + +/* + * Data ready for a request; called from IRQ + */ +void musb_g_rx(struct musb *musb, u8 epnum) +{ + u16 csr; + struct musb_request *req; + struct usb_request *request; + void __iomem *mbase = musb->mregs; + struct musb_ep *musb_ep; + void __iomem *epio = musb->endpoints[epnum].regs; + struct dma_channel *dma; + struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + + if (hw_ep->is_shared_fifo) + musb_ep = &hw_ep->ep_in; + else + musb_ep = &hw_ep->ep_out; + + musb_ep_select(mbase, epnum); + + req = next_request(musb_ep); + if (!req) + return; + + request = &req->request; + + csr = musb_readw(epio, MUSB_RXCSR); + dma = is_dma_capable() ? musb_ep->dma : NULL; + + dev_dbg(musb->controller, "<== %s, rxcsr %04x%s %p\n", musb_ep->end_point.name, + csr, dma ? " (dma)" : "", request); + + if (csr & MUSB_RXCSR_P_SENTSTALL) { + csr |= MUSB_RXCSR_P_WZC_BITS; + csr &= ~MUSB_RXCSR_P_SENTSTALL; + musb_writew(epio, MUSB_RXCSR, csr); + return; + } + + if (csr & MUSB_RXCSR_P_OVERRUN) { + /* csr |= MUSB_RXCSR_P_WZC_BITS; */ + csr &= ~MUSB_RXCSR_P_OVERRUN; + musb_writew(epio, MUSB_RXCSR, csr); + + dev_dbg(musb->controller, "%s iso overrun on %p\n", musb_ep->name, request); + if (request->status == -EINPROGRESS) + request->status = -EOVERFLOW; + } + if (csr & MUSB_RXCSR_INCOMPRX) { + /* REVISIT not necessarily an error */ + dev_dbg(musb->controller, "%s, incomprx\n", musb_ep->end_point.name); + } + + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + /* "should not happen"; likely RXPKTRDY pending for DMA */ + dev_dbg(musb->controller, "%s busy, csr %04x\n", + musb_ep->end_point.name, csr); + return; + } + + if (dma && (csr & MUSB_RXCSR_DMAENAB)) { + csr &= ~(MUSB_RXCSR_AUTOCLEAR + | MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_P_WZC_BITS | csr); + + request->actual += musb_ep->dma->actual_len; + + dev_dbg(musb->controller, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n", + epnum, csr, + musb_readw(epio, MUSB_RXCSR), + musb_ep->dma->actual_len, request); + +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) || \ + defined(CONFIG_USB_UX500_DMA) + /* Autoclear doesn't clear RxPktRdy for short packets */ + if ((dma->desired_mode == 0 && !hw_ep->rx_double_buffered) + || (dma->actual_len + & (musb_ep->packet_sz - 1))) { + /* ack the read! */ + csr &= ~MUSB_RXCSR_RXPKTRDY; + musb_writew(epio, MUSB_RXCSR, csr); + } + + /* incomplete, and not short? wait for next IN packet */ + if ((request->actual < request->length) + && (musb_ep->dma->actual_len + == musb_ep->packet_sz)) { + /* In double buffer case, continue to unload fifo if + * there is Rx packet in FIFO. + **/ + csr = musb_readw(epio, MUSB_RXCSR); + if ((csr & MUSB_RXCSR_RXPKTRDY) && + hw_ep->rx_double_buffered) + goto exit; + return; + } +#endif + musb_g_giveback(musb_ep, request, 0); + /* + * In the giveback function the MUSB lock is + * released and acquired after sometime. During + * this time period the INDEX register could get + * changed by the gadget_queue function especially + * on SMP systems. Reselect the INDEX to be sure + * we are reading/modifying the right registers + */ + musb_ep_select(mbase, epnum); + + req = next_request(musb_ep); + if (!req) + return; + } +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) || \ + defined(CONFIG_USB_UX500_DMA) +exit: +#endif + /* Analyze request */ + rxstate(musb, req); +} + +/* ------------------------------------------------------------ */ + +static int musb_gadget_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + unsigned long flags; + struct musb_ep *musb_ep; + struct musb_hw_ep *hw_ep; + void __iomem *regs; + struct musb *musb; + void __iomem *mbase; + u8 epnum; + u16 csr; + unsigned tmp; + int status = -EINVAL; + + if (!ep || !desc) + return -EINVAL; + + musb_ep = to_musb_ep(ep); + hw_ep = musb_ep->hw_ep; + regs = hw_ep->regs; + musb = musb_ep->musb; + mbase = musb->mregs; + epnum = musb_ep->current_epnum; + + spin_lock_irqsave(&musb->lock, flags); + + if (musb_ep->desc) { + status = -EBUSY; + goto fail; + } + musb_ep->type = usb_endpoint_type(desc); + + /* check direction and (later) maxpacket size against endpoint */ + if (usb_endpoint_num(desc) != epnum) + goto fail; + + /* REVISIT this rules out high bandwidth periodic transfers */ + tmp = usb_endpoint_maxp(desc); + if (tmp & ~0x07ff) { + int ok; + + if (usb_endpoint_dir_in(desc)) + ok = musb->hb_iso_tx; + else + ok = musb->hb_iso_rx; + + if (!ok) { + dev_dbg(musb->controller, "no support for high bandwidth ISO\n"); + goto fail; + } + musb_ep->hb_mult = (tmp >> 11) & 3; + } else { + musb_ep->hb_mult = 0; + } + + musb_ep->packet_sz = tmp & 0x7ff; + tmp = musb_ep->packet_sz * (musb_ep->hb_mult + 1); + + /* enable the interrupts for the endpoint, set the endpoint + * packet size (or fail), set the mode, clear the fifo + */ + musb_ep_select(mbase, epnum); + if (usb_endpoint_dir_in(desc)) { + u16 int_txe = musb_readw(mbase, MUSB_INTRTXE); + + if (hw_ep->is_shared_fifo) + musb_ep->is_in = 1; + if (!musb_ep->is_in) + goto fail; + + if (tmp > hw_ep->max_packet_sz_tx) { + dev_dbg(musb->controller, "packet size beyond hardware FIFO size\n"); + goto fail; + } + + int_txe |= (1 << epnum); + musb_writew(mbase, MUSB_INTRTXE, int_txe); + + /* REVISIT if can_bulk_split(), use by updating "tmp"; + * likewise high bandwidth periodic tx + */ + /* Set TXMAXP with the FIFO size of the endpoint + * to disable double buffering mode. + */ + if (musb->double_buffer_not_ok) + musb_writew(regs, MUSB_TXMAXP, hw_ep->max_packet_sz_tx); + else + musb_writew(regs, MUSB_TXMAXP, musb_ep->packet_sz + | (musb_ep->hb_mult << 11)); + + csr = MUSB_TXCSR_MODE | MUSB_TXCSR_CLRDATATOG; + if (musb_readw(regs, MUSB_TXCSR) + & MUSB_TXCSR_FIFONOTEMPTY) + csr |= MUSB_TXCSR_FLUSHFIFO; + if (musb_ep->type == USB_ENDPOINT_XFER_ISOC) + csr |= MUSB_TXCSR_P_ISO; + + /* set twice in case of double buffering */ + musb_writew(regs, MUSB_TXCSR, csr); + /* REVISIT may be inappropriate w/o FIFONOTEMPTY ... */ + musb_writew(regs, MUSB_TXCSR, csr); + + } else { + u16 int_rxe = musb_readw(mbase, MUSB_INTRRXE); + + if (hw_ep->is_shared_fifo) + musb_ep->is_in = 0; + if (musb_ep->is_in) + goto fail; + + if (tmp > hw_ep->max_packet_sz_rx) { + dev_dbg(musb->controller, "packet size beyond hardware FIFO size\n"); + goto fail; + } + + int_rxe |= (1 << epnum); + musb_writew(mbase, MUSB_INTRRXE, int_rxe); + + /* REVISIT if can_bulk_combine() use by updating "tmp" + * likewise high bandwidth periodic rx + */ + /* Set RXMAXP with the FIFO size of the endpoint + * to disable double buffering mode. + */ + if (musb->double_buffer_not_ok) + musb_writew(regs, MUSB_RXMAXP, hw_ep->max_packet_sz_tx); + else + musb_writew(regs, MUSB_RXMAXP, musb_ep->packet_sz + | (musb_ep->hb_mult << 11)); + + /* force shared fifo to OUT-only mode */ + if (hw_ep->is_shared_fifo) { + csr = musb_readw(regs, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY); + musb_writew(regs, MUSB_TXCSR, csr); + } + + csr = MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_CLRDATATOG; + if (musb_ep->type == USB_ENDPOINT_XFER_ISOC) + csr |= MUSB_RXCSR_P_ISO; + else if (musb_ep->type == USB_ENDPOINT_XFER_INT) + csr |= MUSB_RXCSR_DISNYET; + + /* set twice in case of double buffering */ + musb_writew(regs, MUSB_RXCSR, csr); + musb_writew(regs, MUSB_RXCSR, csr); + } + + /* NOTE: all the I/O code _should_ work fine without DMA, in case + * for some reason you run out of channels here. + */ + if (is_dma_capable() && musb->dma_controller) { + struct dma_controller *c = musb->dma_controller; + + musb_ep->dma = c->channel_alloc(c, hw_ep, + (desc->bEndpointAddress & USB_DIR_IN)); + } else + musb_ep->dma = NULL; + + musb_ep->desc = desc; + musb_ep->busy = 0; + musb_ep->wedged = 0; + status = 0; + + pr_debug("%s periph: enabled %s for %s %s, %smaxpacket %d\n", + musb_driver_name, musb_ep->end_point.name, + ({ char *s; switch (musb_ep->type) { + case USB_ENDPOINT_XFER_BULK: s = "bulk"; break; + case USB_ENDPOINT_XFER_INT: s = "int"; break; + default: s = "iso"; break; + }; s; }), + musb_ep->is_in ? "IN" : "OUT", + musb_ep->dma ? "dma, " : "", + musb_ep->packet_sz); + + schedule_work(&musb->irq_work); + +fail: + spin_unlock_irqrestore(&musb->lock, flags); + return status; +} + +/* + * Disable an endpoint flushing all requests queued. + */ +static int musb_gadget_disable(struct usb_ep *ep) +{ + unsigned long flags; + struct musb *musb; + u8 epnum; + struct musb_ep *musb_ep; + void __iomem *epio; + int status = 0; + + musb_ep = to_musb_ep(ep); + musb = musb_ep->musb; + epnum = musb_ep->current_epnum; + epio = musb->endpoints[epnum].regs; + + spin_lock_irqsave(&musb->lock, flags); + musb_ep_select(musb->mregs, epnum); + + /* zero the endpoint sizes */ + if (musb_ep->is_in) { + u16 int_txe = musb_readw(musb->mregs, MUSB_INTRTXE); + int_txe &= ~(1 << epnum); + musb_writew(musb->mregs, MUSB_INTRTXE, int_txe); + musb_writew(epio, MUSB_TXMAXP, 0); + } else { + u16 int_rxe = musb_readw(musb->mregs, MUSB_INTRRXE); + int_rxe &= ~(1 << epnum); + musb_writew(musb->mregs, MUSB_INTRRXE, int_rxe); + musb_writew(epio, MUSB_RXMAXP, 0); + } + + musb_ep->desc = NULL; +#ifndef __UBOOT__ + musb_ep->end_point.desc = NULL; +#endif + + /* abort all pending DMA and requests */ + nuke(musb_ep, -ESHUTDOWN); + + schedule_work(&musb->irq_work); + + spin_unlock_irqrestore(&(musb->lock), flags); + + dev_dbg(musb->controller, "%s\n", musb_ep->end_point.name); + + return status; +} + +/* + * Allocate a request for an endpoint. + * Reused by ep0 code. + */ +struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + struct musb *musb = musb_ep->musb; + struct musb_request *request = NULL; + + request = kzalloc(sizeof *request, gfp_flags); + if (!request) { + dev_dbg(musb->controller, "not enough memory\n"); + return NULL; + } + + request->request.dma = DMA_ADDR_INVALID; + request->epnum = musb_ep->current_epnum; + request->ep = musb_ep; + + return &request->request; +} + +/* + * Free a request + * Reused by ep0 code. + */ +void musb_free_request(struct usb_ep *ep, struct usb_request *req) +{ + kfree(to_musb_request(req)); +} + +static LIST_HEAD(buffers); + +struct free_record { + struct list_head list; + struct device *dev; + unsigned bytes; + dma_addr_t dma; +}; + +/* + * Context: controller locked, IRQs blocked. + */ +void musb_ep_restart(struct musb *musb, struct musb_request *req) +{ + dev_dbg(musb->controller, "<== %s request %p len %u on hw_ep%d\n", + req->tx ? "TX/IN" : "RX/OUT", + &req->request, req->request.length, req->epnum); + + musb_ep_select(musb->mregs, req->epnum); + if (req->tx) + txstate(musb, req); + else + rxstate(musb, req); +} + +static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, + gfp_t gfp_flags) +{ + struct musb_ep *musb_ep; + struct musb_request *request; + struct musb *musb; + int status = 0; + unsigned long lockflags; + + if (!ep || !req) + return -EINVAL; + if (!req->buf) + return -ENODATA; + + musb_ep = to_musb_ep(ep); + musb = musb_ep->musb; + + request = to_musb_request(req); + request->musb = musb; + + if (request->ep != musb_ep) + return -EINVAL; + + dev_dbg(musb->controller, "<== to %s request=%p\n", ep->name, req); + + /* request is mine now... */ + request->request.actual = 0; + request->request.status = -EINPROGRESS; + request->epnum = musb_ep->current_epnum; + request->tx = musb_ep->is_in; + + map_dma_buffer(request, musb, musb_ep); + + spin_lock_irqsave(&musb->lock, lockflags); + + /* don't queue if the ep is down */ + if (!musb_ep->desc) { + dev_dbg(musb->controller, "req %p queued to %s while ep %s\n", + req, ep->name, "disabled"); + status = -ESHUTDOWN; + goto cleanup; + } + + /* add request to the list */ + list_add_tail(&request->list, &musb_ep->req_list); + + /* it this is the head of the queue, start i/o ... */ + if (!musb_ep->busy && &request->list == musb_ep->req_list.next) + musb_ep_restart(musb, request); + +cleanup: + spin_unlock_irqrestore(&musb->lock, lockflags); + return status; +} + +static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + struct musb_request *req = to_musb_request(request); + struct musb_request *r; + unsigned long flags; + int status = 0; + struct musb *musb = musb_ep->musb; + + if (!ep || !request || to_musb_request(request)->ep != musb_ep) + return -EINVAL; + + spin_lock_irqsave(&musb->lock, flags); + + list_for_each_entry(r, &musb_ep->req_list, list) { + if (r == req) + break; + } + if (r != req) { + dev_dbg(musb->controller, "request %p not queued to %s\n", request, ep->name); + status = -EINVAL; + goto done; + } + + /* if the hardware doesn't have the request, easy ... */ + if (musb_ep->req_list.next != &req->list || musb_ep->busy) + musb_g_giveback(musb_ep, request, -ECONNRESET); + + /* ... else abort the dma transfer ... */ + else if (is_dma_capable() && musb_ep->dma) { + struct dma_controller *c = musb->dma_controller; + + musb_ep_select(musb->mregs, musb_ep->current_epnum); + if (c->channel_abort) + status = c->channel_abort(musb_ep->dma); + else + status = -EBUSY; + if (status == 0) + musb_g_giveback(musb_ep, request, -ECONNRESET); + } else { + /* NOTE: by sticking to easily tested hardware/driver states, + * we leave counting of in-flight packets imprecise. + */ + musb_g_giveback(musb_ep, request, -ECONNRESET); + } + +done: + spin_unlock_irqrestore(&musb->lock, flags); + return status; +} + +/* + * Set or clear the halt bit of an endpoint. A halted enpoint won't tx/rx any + * data but will queue requests. + * + * exported to ep0 code + */ +static int musb_gadget_set_halt(struct usb_ep *ep, int value) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + u8 epnum = musb_ep->current_epnum; + struct musb *musb = musb_ep->musb; + void __iomem *epio = musb->endpoints[epnum].regs; + void __iomem *mbase; + unsigned long flags; + u16 csr; + struct musb_request *request; + int status = 0; + + if (!ep) + return -EINVAL; + mbase = musb->mregs; + + spin_lock_irqsave(&musb->lock, flags); + + if ((USB_ENDPOINT_XFER_ISOC == musb_ep->type)) { + status = -EINVAL; + goto done; + } + + musb_ep_select(mbase, epnum); + + request = next_request(musb_ep); + if (value) { + if (request) { + dev_dbg(musb->controller, "request in progress, cannot halt %s\n", + ep->name); + status = -EAGAIN; + goto done; + } + /* Cannot portably stall with non-empty FIFO */ + if (musb_ep->is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) { + dev_dbg(musb->controller, "FIFO busy, cannot halt %s\n", ep->name); + status = -EAGAIN; + goto done; + } + } + } else + musb_ep->wedged = 0; + + /* set/clear the stall and toggle bits */ + dev_dbg(musb->controller, "%s: %s stall\n", ep->name, value ? "set" : "clear"); + if (musb_ep->is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + csr |= MUSB_TXCSR_P_WZC_BITS + | MUSB_TXCSR_CLRDATATOG; + if (value) + csr |= MUSB_TXCSR_P_SENDSTALL; + else + csr &= ~(MUSB_TXCSR_P_SENDSTALL + | MUSB_TXCSR_P_SENTSTALL); + csr &= ~MUSB_TXCSR_TXPKTRDY; + musb_writew(epio, MUSB_TXCSR, csr); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_P_WZC_BITS + | MUSB_RXCSR_FLUSHFIFO + | MUSB_RXCSR_CLRDATATOG; + if (value) + csr |= MUSB_RXCSR_P_SENDSTALL; + else + csr &= ~(MUSB_RXCSR_P_SENDSTALL + | MUSB_RXCSR_P_SENTSTALL); + musb_writew(epio, MUSB_RXCSR, csr); + } + + /* maybe start the first request in the queue */ + if (!musb_ep->busy && !value && request) { + dev_dbg(musb->controller, "restarting the request\n"); + musb_ep_restart(musb, request); + } + +done: + spin_unlock_irqrestore(&musb->lock, flags); + return status; +} + +#ifndef __UBOOT__ +/* + * Sets the halt feature with the clear requests ignored + */ +static int musb_gadget_set_wedge(struct usb_ep *ep) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + + if (!ep) + return -EINVAL; + + musb_ep->wedged = 1; + + return usb_ep_set_halt(ep); +} +#endif + +static int musb_gadget_fifo_status(struct usb_ep *ep) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + void __iomem *epio = musb_ep->hw_ep->regs; + int retval = -EINVAL; + + if (musb_ep->desc && !musb_ep->is_in) { + struct musb *musb = musb_ep->musb; + int epnum = musb_ep->current_epnum; + void __iomem *mbase = musb->mregs; + unsigned long flags; + + spin_lock_irqsave(&musb->lock, flags); + + musb_ep_select(mbase, epnum); + /* FIXME return zero unless RXPKTRDY is set */ + retval = musb_readw(epio, MUSB_RXCOUNT); + + spin_unlock_irqrestore(&musb->lock, flags); + } + return retval; +} + +static void musb_gadget_fifo_flush(struct usb_ep *ep) +{ + struct musb_ep *musb_ep = to_musb_ep(ep); + struct musb *musb = musb_ep->musb; + u8 epnum = musb_ep->current_epnum; + void __iomem *epio = musb->endpoints[epnum].regs; + void __iomem *mbase; + unsigned long flags; + u16 csr, int_txe; + + mbase = musb->mregs; + + spin_lock_irqsave(&musb->lock, flags); + musb_ep_select(mbase, (u8) epnum); + + /* disable interrupts */ + int_txe = musb_readw(mbase, MUSB_INTRTXE); + musb_writew(mbase, MUSB_INTRTXE, int_txe & ~(1 << epnum)); + + if (musb_ep->is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) { + csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_P_WZC_BITS; + /* + * Setting both TXPKTRDY and FLUSHFIFO makes controller + * to interrupt current FIFO loading, but not flushing + * the already loaded ones. + */ + csr &= ~MUSB_TXCSR_TXPKTRDY; + musb_writew(epio, MUSB_TXCSR, csr); + /* REVISIT may be inappropriate w/o FIFONOTEMPTY ... */ + musb_writew(epio, MUSB_TXCSR, csr); + } + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_P_WZC_BITS; + musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + } + + /* re-enable interrupt */ + musb_writew(mbase, MUSB_INTRTXE, int_txe); + spin_unlock_irqrestore(&musb->lock, flags); +} + +static const struct usb_ep_ops musb_ep_ops = { + .enable = musb_gadget_enable, + .disable = musb_gadget_disable, + .alloc_request = musb_alloc_request, + .free_request = musb_free_request, + .queue = musb_gadget_queue, + .dequeue = musb_gadget_dequeue, + .set_halt = musb_gadget_set_halt, +#ifndef __UBOOT__ + .set_wedge = musb_gadget_set_wedge, +#endif + .fifo_status = musb_gadget_fifo_status, + .fifo_flush = musb_gadget_fifo_flush +}; + +/* ----------------------------------------------------------------------- */ + +static int musb_gadget_get_frame(struct usb_gadget *gadget) +{ + struct musb *musb = gadget_to_musb(gadget); + + return (int)musb_readw(musb->mregs, MUSB_FRAME); +} + +static int musb_gadget_wakeup(struct usb_gadget *gadget) +{ +#ifndef __UBOOT__ + struct musb *musb = gadget_to_musb(gadget); + void __iomem *mregs = musb->mregs; + unsigned long flags; + int status = -EINVAL; + u8 power, devctl; + int retries; + + spin_lock_irqsave(&musb->lock, flags); + + switch (musb->xceiv->state) { + case OTG_STATE_B_PERIPHERAL: + /* NOTE: OTG state machine doesn't include B_SUSPENDED; + * that's part of the standard usb 1.1 state machine, and + * doesn't affect OTG transitions. + */ + if (musb->may_wakeup && musb->is_suspended) + break; + goto done; + case OTG_STATE_B_IDLE: + /* Start SRP ... OTG not required. */ + devctl = musb_readb(mregs, MUSB_DEVCTL); + dev_dbg(musb->controller, "Sending SRP: devctl: %02x\n", devctl); + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(mregs, MUSB_DEVCTL, devctl); + devctl = musb_readb(mregs, MUSB_DEVCTL); + retries = 100; + while (!(devctl & MUSB_DEVCTL_SESSION)) { + devctl = musb_readb(mregs, MUSB_DEVCTL); + if (retries-- < 1) + break; + } + retries = 10000; + while (devctl & MUSB_DEVCTL_SESSION) { + devctl = musb_readb(mregs, MUSB_DEVCTL); + if (retries-- < 1) + break; + } + + spin_unlock_irqrestore(&musb->lock, flags); + otg_start_srp(musb->xceiv->otg); + spin_lock_irqsave(&musb->lock, flags); + + /* Block idling for at least 1s */ + musb_platform_try_idle(musb, + jiffies + msecs_to_jiffies(1 * HZ)); + + status = 0; + goto done; + default: + dev_dbg(musb->controller, "Unhandled wake: %s\n", + otg_state_string(musb->xceiv->state)); + goto done; + } + + status = 0; + + power = musb_readb(mregs, MUSB_POWER); + power |= MUSB_POWER_RESUME; + musb_writeb(mregs, MUSB_POWER, power); + dev_dbg(musb->controller, "issue wakeup\n"); + + /* FIXME do this next chunk in a timer callback, no udelay */ + mdelay(2); + + power = musb_readb(mregs, MUSB_POWER); + power &= ~MUSB_POWER_RESUME; + musb_writeb(mregs, MUSB_POWER, power); +done: + spin_unlock_irqrestore(&musb->lock, flags); + return status; +#else + return 0; +#endif +} + +static int +musb_gadget_set_self_powered(struct usb_gadget *gadget, int is_selfpowered) +{ + struct musb *musb = gadget_to_musb(gadget); + + musb->is_self_powered = !!is_selfpowered; + return 0; +} + +static void musb_pullup(struct musb *musb, int is_on) +{ + u8 power; + + power = musb_readb(musb->mregs, MUSB_POWER); + if (is_on) + power |= MUSB_POWER_SOFTCONN; + else + power &= ~MUSB_POWER_SOFTCONN; + + /* FIXME if on, HdrcStart; if off, HdrcStop */ + + dev_dbg(musb->controller, "gadget D+ pullup %s\n", + is_on ? "on" : "off"); + musb_writeb(musb->mregs, MUSB_POWER, power); +} + +#if 0 +static int musb_gadget_vbus_session(struct usb_gadget *gadget, int is_active) +{ + dev_dbg(musb->controller, "<= %s =>\n", __func__); + + /* + * FIXME iff driver's softconnect flag is set (as it is during probe, + * though that can clear it), just musb_pullup(). + */ + + return -EINVAL; +} +#endif + +static int musb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ +#ifndef __UBOOT__ + struct musb *musb = gadget_to_musb(gadget); + + if (!musb->xceiv->set_power) + return -EOPNOTSUPP; + return usb_phy_set_power(musb->xceiv, mA); +#else + return 0; +#endif +} + +static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct musb *musb = gadget_to_musb(gadget); + unsigned long flags; + + is_on = !!is_on; + + pm_runtime_get_sync(musb->controller); + + /* NOTE: this assumes we are sensing vbus; we'd rather + * not pullup unless the B-session is active. + */ + spin_lock_irqsave(&musb->lock, flags); + if (is_on != musb->softconnect) { + musb->softconnect = is_on; + musb_pullup(musb, is_on); + } + spin_unlock_irqrestore(&musb->lock, flags); + + pm_runtime_put(musb->controller); + + return 0; +} + +#ifndef __UBOOT__ +static int musb_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int musb_gadget_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); +#endif + +static const struct usb_gadget_ops musb_gadget_operations = { + .get_frame = musb_gadget_get_frame, + .wakeup = musb_gadget_wakeup, + .set_selfpowered = musb_gadget_set_self_powered, + /* .vbus_session = musb_gadget_vbus_session, */ + .vbus_draw = musb_gadget_vbus_draw, + .pullup = musb_gadget_pullup, +#ifndef __UBOOT__ + .udc_start = musb_gadget_start, + .udc_stop = musb_gadget_stop, +#endif +}; + +/* ----------------------------------------------------------------------- */ + +/* Registration */ + +/* Only this registration code "knows" the rule (from USB standards) + * about there being only one external upstream port. It assumes + * all peripheral ports are external... + */ + +#ifndef __UBOOT__ +static void musb_gadget_release(struct device *dev) +{ + /* kref_put(WHAT) */ + dev_dbg(dev, "%s\n", __func__); +} +#endif + + +static void __devinit +init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 epnum, int is_in) +{ + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + + memset(ep, 0, sizeof *ep); + + ep->current_epnum = epnum; + ep->musb = musb; + ep->hw_ep = hw_ep; + ep->is_in = is_in; + + INIT_LIST_HEAD(&ep->req_list); + + sprintf(ep->name, "ep%d%s", epnum, + (!epnum || hw_ep->is_shared_fifo) ? "" : ( + is_in ? "in" : "out")); + ep->end_point.name = ep->name; + INIT_LIST_HEAD(&ep->end_point.ep_list); + if (!epnum) { + ep->end_point.maxpacket = 64; + ep->end_point.ops = &musb_g_ep0_ops; + musb->g.ep0 = &ep->end_point; + } else { + if (is_in) + ep->end_point.maxpacket = hw_ep->max_packet_sz_tx; + else + ep->end_point.maxpacket = hw_ep->max_packet_sz_rx; + ep->end_point.ops = &musb_ep_ops; + list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list); + } +} + +/* + * Initialize the endpoints exposed to peripheral drivers, with backlinks + * to the rest of the driver state. + */ +static inline void __devinit musb_g_init_endpoints(struct musb *musb) +{ + u8 epnum; + struct musb_hw_ep *hw_ep; + unsigned count = 0; + + /* initialize endpoint list just once */ + INIT_LIST_HEAD(&(musb->g.ep_list)); + + for (epnum = 0, hw_ep = musb->endpoints; + epnum < musb->nr_endpoints; + epnum++, hw_ep++) { + if (hw_ep->is_shared_fifo /* || !epnum */) { + init_peripheral_ep(musb, &hw_ep->ep_in, epnum, 0); + count++; + } else { + if (hw_ep->max_packet_sz_tx) { + init_peripheral_ep(musb, &hw_ep->ep_in, + epnum, 1); + count++; + } + if (hw_ep->max_packet_sz_rx) { + init_peripheral_ep(musb, &hw_ep->ep_out, + epnum, 0); + count++; + } + } + } +} + +/* called once during driver setup to initialize and link into + * the driver model; memory is zeroed. + */ +int __devinit musb_gadget_setup(struct musb *musb) +{ + int status; + + /* REVISIT minor race: if (erroneously) setting up two + * musb peripherals at the same time, only the bus lock + * is probably held. + */ + + musb->g.ops = &musb_gadget_operations; +#ifndef __UBOOT__ + musb->g.max_speed = USB_SPEED_HIGH; +#endif + musb->g.speed = USB_SPEED_UNKNOWN; + +#ifndef __UBOOT__ + /* this "gadget" abstracts/virtualizes the controller */ + dev_set_name(&musb->g.dev, "gadget"); + musb->g.dev.parent = musb->controller; + musb->g.dev.dma_mask = musb->controller->dma_mask; + musb->g.dev.release = musb_gadget_release; +#endif + musb->g.name = musb_driver_name; + +#ifndef __UBOOT__ + if (is_otg_enabled(musb)) + musb->g.is_otg = 1; +#endif + + musb_g_init_endpoints(musb); + + musb->is_active = 0; + musb_platform_try_idle(musb, 0); + +#ifndef __UBOOT__ + status = device_register(&musb->g.dev); + if (status != 0) { + put_device(&musb->g.dev); + return status; + } + status = usb_add_gadget_udc(musb->controller, &musb->g); + if (status) + goto err; +#endif + + return 0; +#ifndef __UBOOT__ +err: + musb->g.dev.parent = NULL; + device_unregister(&musb->g.dev); + return status; +#endif +} + +void musb_gadget_cleanup(struct musb *musb) +{ +#ifndef __UBOOT__ + usb_del_gadget_udc(&musb->g); + if (musb->g.dev.parent) + device_unregister(&musb->g.dev); +#endif +} + +/* + * Register the gadget driver. Used by gadget drivers when + * registering themselves with the controller. + * + * -EINVAL something went wrong (not driver) + * -EBUSY another gadget is already using the controller + * -ENOMEM no memory to perform the operation + * + * @param driver the gadget driver + * @return <0 if error, 0 if everything is fine + */ +#ifndef __UBOOT__ +static int musb_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +#else +int musb_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +#endif +{ + struct musb *musb = gadget_to_musb(g); +#ifndef __UBOOT__ + struct usb_otg *otg = musb->xceiv->otg; +#endif + unsigned long flags; + int retval = -EINVAL; + +#ifndef __UBOOT__ + if (driver->max_speed < USB_SPEED_HIGH) + goto err0; +#endif + + pm_runtime_get_sync(musb->controller); + +#ifndef __UBOOT__ + dev_dbg(musb->controller, "registering driver %s\n", driver->function); +#endif + + musb->softconnect = 0; + musb->gadget_driver = driver; + + spin_lock_irqsave(&musb->lock, flags); + musb->is_active = 1; + +#ifndef __UBOOT__ + otg_set_peripheral(otg, &musb->g); + musb->xceiv->state = OTG_STATE_B_IDLE; + + /* + * FIXME this ignores the softconnect flag. Drivers are + * allowed hold the peripheral inactive until for example + * userspace hooks up printer hardware or DSP codecs, so + * hosts only see fully functional devices. + */ + + if (!is_otg_enabled(musb)) +#endif + musb_start(musb); + + spin_unlock_irqrestore(&musb->lock, flags); + +#ifndef __UBOOT__ + if (is_otg_enabled(musb)) { + struct usb_hcd *hcd = musb_to_hcd(musb); + + dev_dbg(musb->controller, "OTG startup...\n"); + + /* REVISIT: funcall to other code, which also + * handles power budgeting ... this way also + * ensures HdrcStart is indirectly called. + */ + retval = usb_add_hcd(musb_to_hcd(musb), 0, 0); + if (retval < 0) { + dev_dbg(musb->controller, "add_hcd failed, %d\n", retval); + goto err2; + } + + if ((musb->xceiv->last_event == USB_EVENT_ID) + && otg->set_vbus) + otg_set_vbus(otg, 1); + + hcd->self.uses_pio_for_control = 1; + } + if (musb->xceiv->last_event == USB_EVENT_NONE) + pm_runtime_put(musb->controller); +#endif + + return 0; + +#ifndef __UBOOT__ +err2: + if (!is_otg_enabled(musb)) + musb_stop(musb); +err0: + return retval; +#endif +} + +#ifndef __UBOOT__ +static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) +{ + int i; + struct musb_hw_ep *hw_ep; + + /* don't disconnect if it's not connected */ + if (musb->g.speed == USB_SPEED_UNKNOWN) + driver = NULL; + else + musb->g.speed = USB_SPEED_UNKNOWN; + + /* deactivate the hardware */ + if (musb->softconnect) { + musb->softconnect = 0; + musb_pullup(musb, 0); + } + musb_stop(musb); + + /* killing any outstanding requests will quiesce the driver; + * then report disconnect + */ + if (driver) { + for (i = 0, hw_ep = musb->endpoints; + i < musb->nr_endpoints; + i++, hw_ep++) { + musb_ep_select(musb->mregs, i); + if (hw_ep->is_shared_fifo /* || !epnum */) { + nuke(&hw_ep->ep_in, -ESHUTDOWN); + } else { + if (hw_ep->max_packet_sz_tx) + nuke(&hw_ep->ep_in, -ESHUTDOWN); + if (hw_ep->max_packet_sz_rx) + nuke(&hw_ep->ep_out, -ESHUTDOWN); + } + } + } +} + +/* + * Unregister the gadget driver. Used by gadget drivers when + * unregistering themselves from the controller. + * + * @param driver the gadget driver to unregister + */ +static int musb_gadget_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct musb *musb = gadget_to_musb(g); + unsigned long flags; + + if (musb->xceiv->last_event == USB_EVENT_NONE) + pm_runtime_get_sync(musb->controller); + + /* + * REVISIT always use otg_set_peripheral() here too; + * this needs to shut down the OTG engine. + */ + + spin_lock_irqsave(&musb->lock, flags); + + musb_hnp_stop(musb); + + (void) musb_gadget_vbus_draw(&musb->g, 0); + + musb->xceiv->state = OTG_STATE_UNDEFINED; + stop_activity(musb, driver); + otg_set_peripheral(musb->xceiv->otg, NULL); + + dev_dbg(musb->controller, "unregistering driver %s\n", driver->function); + + musb->is_active = 0; + musb_platform_try_idle(musb, 0); + spin_unlock_irqrestore(&musb->lock, flags); + + if (is_otg_enabled(musb)) { + usb_remove_hcd(musb_to_hcd(musb)); + /* FIXME we need to be able to register another + * gadget driver here and have everything work; + * that currently misbehaves. + */ + } + + if (!is_otg_enabled(musb)) + musb_stop(musb); + + pm_runtime_put(musb->controller); + + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +/* lifecycle operations called through plat_uds.c */ + +void musb_g_resume(struct musb *musb) +{ +#ifndef __UBOOT__ + musb->is_suspended = 0; + switch (musb->xceiv->state) { + case OTG_STATE_B_IDLE: + break; + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_PERIPHERAL: + musb->is_active = 1; + if (musb->gadget_driver && musb->gadget_driver->resume) { + spin_unlock(&musb->lock); + musb->gadget_driver->resume(&musb->g); + spin_lock(&musb->lock); + } + break; + default: + WARNING("unhandled RESUME transition (%s)\n", + otg_state_string(musb->xceiv->state)); + } +#endif +} + +/* called when SOF packets stop for 3+ msec */ +void musb_g_suspend(struct musb *musb) +{ +#ifndef __UBOOT__ + u8 devctl; + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + dev_dbg(musb->controller, "devctl %02x\n", devctl); + + switch (musb->xceiv->state) { + case OTG_STATE_B_IDLE: + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + break; + case OTG_STATE_B_PERIPHERAL: + musb->is_suspended = 1; + if (musb->gadget_driver && musb->gadget_driver->suspend) { + spin_unlock(&musb->lock); + musb->gadget_driver->suspend(&musb->g); + spin_lock(&musb->lock); + } + break; + default: + /* REVISIT if B_HOST, clear DEVCTL.HOSTREQ; + * A_PERIPHERAL may need care too + */ + WARNING("unhandled SUSPEND transition (%s)\n", + otg_state_string(musb->xceiv->state)); + } +#endif +} + +/* Called during SRP */ +void musb_g_wakeup(struct musb *musb) +{ + musb_gadget_wakeup(&musb->g); +} + +/* called when VBUS drops below session threshold, and in other cases */ +void musb_g_disconnect(struct musb *musb) +{ + void __iomem *mregs = musb->mregs; + u8 devctl = musb_readb(mregs, MUSB_DEVCTL); + + dev_dbg(musb->controller, "devctl %02x\n", devctl); + + /* clear HR */ + musb_writeb(mregs, MUSB_DEVCTL, devctl & MUSB_DEVCTL_SESSION); + + /* don't draw vbus until new b-default session */ + (void) musb_gadget_vbus_draw(&musb->g, 0); + + musb->g.speed = USB_SPEED_UNKNOWN; + if (musb->gadget_driver && musb->gadget_driver->disconnect) { + spin_unlock(&musb->lock); + musb->gadget_driver->disconnect(&musb->g); + spin_lock(&musb->lock); + } + +#ifndef __UBOOT__ + switch (musb->xceiv->state) { + default: + dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n", + otg_state_string(musb->xceiv->state)); + musb->xceiv->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + break; + case OTG_STATE_A_PERIPHERAL: + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; + MUSB_HST_MODE(musb); + break; + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_HOST: + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_IDLE: + musb->xceiv->state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_B_SRP_INIT: + break; + } +#endif + + musb->is_active = 0; +} + +void musb_g_reset(struct musb *musb) +__releases(musb->lock) +__acquires(musb->lock) +{ + void __iomem *mbase = musb->mregs; + u8 devctl = musb_readb(mbase, MUSB_DEVCTL); + u8 power; + +#ifndef __UBOOT__ + dev_dbg(musb->controller, "<== %s addr=%x driver '%s'\n", + (devctl & MUSB_DEVCTL_BDEVICE) + ? "B-Device" : "A-Device", + musb_readb(mbase, MUSB_FADDR), + musb->gadget_driver + ? musb->gadget_driver->driver.name + : NULL + ); +#endif + + /* report disconnect, if we didn't already (flushing EP state) */ + if (musb->g.speed != USB_SPEED_UNKNOWN) + musb_g_disconnect(musb); + + /* clear HR */ + else if (devctl & MUSB_DEVCTL_HR) + musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); + + + /* what speed did we negotiate? */ + power = musb_readb(mbase, MUSB_POWER); + musb->g.speed = (power & MUSB_POWER_HSMODE) + ? USB_SPEED_HIGH : USB_SPEED_FULL; + + /* start in USB_STATE_DEFAULT */ + musb->is_active = 1; + musb->is_suspended = 0; + MUSB_DEV_MODE(musb); + musb->address = 0; + musb->ep0_state = MUSB_EP0_STAGE_SETUP; + + musb->may_wakeup = 0; + musb->g.b_hnp_enable = 0; + musb->g.a_alt_hnp_support = 0; + musb->g.a_hnp_support = 0; + +#ifndef __UBOOT__ + /* Normal reset, as B-Device; + * or else after HNP, as A-Device + */ + if (devctl & MUSB_DEVCTL_BDEVICE) { + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + musb->g.is_a_peripheral = 0; + } else if (is_otg_enabled(musb)) { + musb->xceiv->state = OTG_STATE_A_PERIPHERAL; + musb->g.is_a_peripheral = 1; + } else + WARN_ON(1); + + /* start with default limits on VBUS power draw */ + (void) musb_gadget_vbus_draw(&musb->g, + is_otg_enabled(musb) ? 8 : 100); +#endif +} diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget.h b/qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget.h new file mode 100644 index 000000000..392f701a8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget.h @@ -0,0 +1,130 @@ +/* + * MUSB OTG driver peripheral defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#ifndef __MUSB_GADGET_H +#define __MUSB_GADGET_H + +#include +#ifdef __UBOOT__ +#include +#include +#include +#include +#endif + +enum buffer_map_state { + UN_MAPPED = 0, + PRE_MAPPED, + MUSB_MAPPED +}; + +struct musb_request { + struct usb_request request; + struct list_head list; + struct musb_ep *ep; + struct musb *musb; + u8 tx; /* endpoint direction */ + u8 epnum; + enum buffer_map_state map_state; +}; + +static inline struct musb_request *to_musb_request(struct usb_request *req) +{ + return req ? container_of(req, struct musb_request, request) : NULL; +} + +extern struct usb_request * +musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags); +extern void musb_free_request(struct usb_ep *ep, struct usb_request *req); + + +/* + * struct musb_ep - peripheral side view of endpoint rx or tx side + */ +struct musb_ep { + /* stuff towards the head is basically write-once. */ + struct usb_ep end_point; + char name[12]; + struct musb_hw_ep *hw_ep; + struct musb *musb; + u8 current_epnum; + + /* ... when enabled/disabled ... */ + u8 type; + u8 is_in; + u16 packet_sz; + const struct usb_endpoint_descriptor *desc; + struct dma_channel *dma; + + /* later things are modified based on usage */ + struct list_head req_list; + + u8 wedged; + + /* true if lock must be dropped but req_list may not be advanced */ + u8 busy; + + u8 hb_mult; +}; + +static inline struct musb_ep *to_musb_ep(struct usb_ep *ep) +{ + return ep ? container_of(ep, struct musb_ep, end_point) : NULL; +} + +static inline struct musb_request *next_request(struct musb_ep *ep) +{ + struct list_head *queue = &ep->req_list; + + if (list_empty(queue)) + return NULL; + return container_of(queue->next, struct musb_request, list); +} + +extern void musb_g_tx(struct musb *musb, u8 epnum); +extern void musb_g_rx(struct musb *musb, u8 epnum); + +extern const struct usb_ep_ops musb_g_ep0_ops; + +extern int musb_gadget_setup(struct musb *); +extern void musb_gadget_cleanup(struct musb *); + +extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int); + +extern void musb_ep_restart(struct musb *, struct musb_request *); + +#ifdef __UBOOT__ +int musb_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver); +#endif +#endif /* __MUSB_GADGET_H */ diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget_ep0.c b/qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget_ep0.c new file mode 100644 index 000000000..6599d386d --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_gadget_ep0.c @@ -0,0 +1,1089 @@ +/* + * MUSB OTG peripheral driver ep0 handling + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * Copyright (C) 2008-2009 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#define __UBOOT__ +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#else +#include +#include "linux-compat.h" +#endif + +#include "musb_core.h" + +/* ep0 is always musb->endpoints[0].ep_in */ +#define next_ep0_request(musb) next_in_request(&(musb)->endpoints[0]) + +/* + * locking note: we use only the controller lock, for simpler correctness. + * It's always held with IRQs blocked. + * + * It protects the ep0 request queue as well as ep0_state, not just the + * controller and indexed registers. And that lock stays held unless it + * needs to be dropped to allow reentering this driver ... like upcalls to + * the gadget driver, or adjusting endpoint halt status. + */ + +static char *decode_ep0stage(u8 stage) +{ + switch (stage) { + case MUSB_EP0_STAGE_IDLE: return "idle"; + case MUSB_EP0_STAGE_SETUP: return "setup"; + case MUSB_EP0_STAGE_TX: return "in"; + case MUSB_EP0_STAGE_RX: return "out"; + case MUSB_EP0_STAGE_ACKWAIT: return "wait"; + case MUSB_EP0_STAGE_STATUSIN: return "in/status"; + case MUSB_EP0_STAGE_STATUSOUT: return "out/status"; + default: return "?"; + } +} + +/* handle a standard GET_STATUS request + * Context: caller holds controller lock + */ +static int service_tx_status_request( + struct musb *musb, + const struct usb_ctrlrequest *ctrlrequest) +{ + void __iomem *mbase = musb->mregs; + int handled = 1; + u8 result[2], epnum = 0; + const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK; + + result[1] = 0; + + switch (recip) { + case USB_RECIP_DEVICE: + result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED; + result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; + if (musb->g.is_otg) { + result[0] |= musb->g.b_hnp_enable + << USB_DEVICE_B_HNP_ENABLE; + result[0] |= musb->g.a_alt_hnp_support + << USB_DEVICE_A_ALT_HNP_SUPPORT; + result[0] |= musb->g.a_hnp_support + << USB_DEVICE_A_HNP_SUPPORT; + } + break; + + case USB_RECIP_INTERFACE: + result[0] = 0; + break; + + case USB_RECIP_ENDPOINT: { + int is_in; + struct musb_ep *ep; + u16 tmp; + void __iomem *regs; + + epnum = (u8) ctrlrequest->wIndex; + if (!epnum) { + result[0] = 0; + break; + } + + is_in = epnum & USB_DIR_IN; + if (is_in) { + epnum &= 0x0f; + ep = &musb->endpoints[epnum].ep_in; + } else { + ep = &musb->endpoints[epnum].ep_out; + } + regs = musb->endpoints[epnum].regs; + + if (epnum >= MUSB_C_NUM_EPS || !ep->desc) { + handled = -EINVAL; + break; + } + + musb_ep_select(mbase, epnum); + if (is_in) + tmp = musb_readw(regs, MUSB_TXCSR) + & MUSB_TXCSR_P_SENDSTALL; + else + tmp = musb_readw(regs, MUSB_RXCSR) + & MUSB_RXCSR_P_SENDSTALL; + musb_ep_select(mbase, 0); + + result[0] = tmp ? 1 : 0; + } break; + + default: + /* class, vendor, etc ... delegate */ + handled = 0; + break; + } + + /* fill up the fifo; caller updates csr0 */ + if (handled > 0) { + u16 len = le16_to_cpu(ctrlrequest->wLength); + + if (len > 2) + len = 2; + musb_write_fifo(&musb->endpoints[0], len, result); + } + + return handled; +} + +/* + * handle a control-IN request, the end0 buffer contains the current request + * that is supposed to be a standard control request. Assumes the fifo to + * be at least 2 bytes long. + * + * @return 0 if the request was NOT HANDLED, + * < 0 when error + * > 0 when the request is processed + * + * Context: caller holds controller lock + */ +static int +service_in_request(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest) +{ + int handled = 0; /* not handled */ + + if ((ctrlrequest->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD) { + switch (ctrlrequest->bRequest) { + case USB_REQ_GET_STATUS: + handled = service_tx_status_request(musb, + ctrlrequest); + break; + + /* case USB_REQ_SYNC_FRAME: */ + + default: + break; + } + } + return handled; +} + +/* + * Context: caller holds controller lock + */ +static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req) +{ + musb_g_giveback(&musb->endpoints[0].ep_in, req, 0); +} + +/* + * Tries to start B-device HNP negotiation if enabled via sysfs + */ +static inline void musb_try_b_hnp_enable(struct musb *musb) +{ + void __iomem *mbase = musb->mregs; + u8 devctl; + + dev_dbg(musb->controller, "HNP: Setting HR\n"); + devctl = musb_readb(mbase, MUSB_DEVCTL); + musb_writeb(mbase, MUSB_DEVCTL, devctl | MUSB_DEVCTL_HR); +} + +/* + * Handle all control requests with no DATA stage, including standard + * requests such as: + * USB_REQ_SET_CONFIGURATION, USB_REQ_SET_INTERFACE, unrecognized + * always delegated to the gadget driver + * USB_REQ_SET_ADDRESS, USB_REQ_CLEAR_FEATURE, USB_REQ_SET_FEATURE + * always handled here, except for class/vendor/... features + * + * Context: caller holds controller lock + */ +static int +service_zero_data_request(struct musb *musb, + struct usb_ctrlrequest *ctrlrequest) +__releases(musb->lock) +__acquires(musb->lock) +{ + int handled = -EINVAL; + void __iomem *mbase = musb->mregs; + const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK; + + /* the gadget driver handles everything except what we MUST handle */ + if ((ctrlrequest->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD) { + switch (ctrlrequest->bRequest) { + case USB_REQ_SET_ADDRESS: + /* change it after the status stage */ + musb->set_address = true; + musb->address = (u8) (ctrlrequest->wValue & 0x7f); + handled = 1; + break; + + case USB_REQ_CLEAR_FEATURE: + switch (recip) { + case USB_RECIP_DEVICE: + if (ctrlrequest->wValue + != USB_DEVICE_REMOTE_WAKEUP) + break; + musb->may_wakeup = 0; + handled = 1; + break; + case USB_RECIP_INTERFACE: + break; + case USB_RECIP_ENDPOINT:{ + const u8 epnum = + ctrlrequest->wIndex & 0x0f; + struct musb_ep *musb_ep; + struct musb_hw_ep *ep; + struct musb_request *request; + void __iomem *regs; + int is_in; + u16 csr; + + if (epnum == 0 || epnum >= MUSB_C_NUM_EPS || + ctrlrequest->wValue != USB_ENDPOINT_HALT) + break; + + ep = musb->endpoints + epnum; + regs = ep->regs; + is_in = ctrlrequest->wIndex & USB_DIR_IN; + if (is_in) + musb_ep = &ep->ep_in; + else + musb_ep = &ep->ep_out; + if (!musb_ep->desc) + break; + + handled = 1; + /* Ignore request if endpoint is wedged */ + if (musb_ep->wedged) + break; + + musb_ep_select(mbase, epnum); + if (is_in) { + csr = musb_readw(regs, MUSB_TXCSR); + csr |= MUSB_TXCSR_CLRDATATOG | + MUSB_TXCSR_P_WZC_BITS; + csr &= ~(MUSB_TXCSR_P_SENDSTALL | + MUSB_TXCSR_P_SENTSTALL | + MUSB_TXCSR_TXPKTRDY); + musb_writew(regs, MUSB_TXCSR, csr); + } else { + csr = musb_readw(regs, MUSB_RXCSR); + csr |= MUSB_RXCSR_CLRDATATOG | + MUSB_RXCSR_P_WZC_BITS; + csr &= ~(MUSB_RXCSR_P_SENDSTALL | + MUSB_RXCSR_P_SENTSTALL); + musb_writew(regs, MUSB_RXCSR, csr); + } + + /* Maybe start the first request in the queue */ + request = next_request(musb_ep); + if (!musb_ep->busy && request) { + dev_dbg(musb->controller, "restarting the request\n"); + musb_ep_restart(musb, request); + } + + /* select ep0 again */ + musb_ep_select(mbase, 0); + } break; + default: + /* class, vendor, etc ... delegate */ + handled = 0; + break; + } + break; + + case USB_REQ_SET_FEATURE: + switch (recip) { + case USB_RECIP_DEVICE: + handled = 1; + switch (ctrlrequest->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + musb->may_wakeup = 1; + break; + case USB_DEVICE_TEST_MODE: + if (musb->g.speed != USB_SPEED_HIGH) + goto stall; + if (ctrlrequest->wIndex & 0xff) + goto stall; + + switch (ctrlrequest->wIndex >> 8) { + case 1: + pr_debug("TEST_J\n"); + /* TEST_J */ + musb->test_mode_nr = + MUSB_TEST_J; + break; + case 2: + /* TEST_K */ + pr_debug("TEST_K\n"); + musb->test_mode_nr = + MUSB_TEST_K; + break; + case 3: + /* TEST_SE0_NAK */ + pr_debug("TEST_SE0_NAK\n"); + musb->test_mode_nr = + MUSB_TEST_SE0_NAK; + break; + case 4: + /* TEST_PACKET */ + pr_debug("TEST_PACKET\n"); + musb->test_mode_nr = + MUSB_TEST_PACKET; + break; + + case 0xc0: + /* TEST_FORCE_HS */ + pr_debug("TEST_FORCE_HS\n"); + musb->test_mode_nr = + MUSB_TEST_FORCE_HS; + break; + case 0xc1: + /* TEST_FORCE_FS */ + pr_debug("TEST_FORCE_FS\n"); + musb->test_mode_nr = + MUSB_TEST_FORCE_FS; + break; + case 0xc2: + /* TEST_FIFO_ACCESS */ + pr_debug("TEST_FIFO_ACCESS\n"); + musb->test_mode_nr = + MUSB_TEST_FIFO_ACCESS; + break; + case 0xc3: + /* TEST_FORCE_HOST */ + pr_debug("TEST_FORCE_HOST\n"); + musb->test_mode_nr = + MUSB_TEST_FORCE_HOST; + break; + default: + goto stall; + } + + /* enter test mode after irq */ + if (handled > 0) + musb->test_mode = true; + break; + case USB_DEVICE_B_HNP_ENABLE: + if (!musb->g.is_otg) + goto stall; + musb->g.b_hnp_enable = 1; + musb_try_b_hnp_enable(musb); + break; + case USB_DEVICE_A_HNP_SUPPORT: + if (!musb->g.is_otg) + goto stall; + musb->g.a_hnp_support = 1; + break; + case USB_DEVICE_A_ALT_HNP_SUPPORT: + if (!musb->g.is_otg) + goto stall; + musb->g.a_alt_hnp_support = 1; + break; + case USB_DEVICE_DEBUG_MODE: + handled = 0; + break; +stall: + default: + handled = -EINVAL; + break; + } + break; + + case USB_RECIP_INTERFACE: + break; + + case USB_RECIP_ENDPOINT:{ + const u8 epnum = + ctrlrequest->wIndex & 0x0f; + struct musb_ep *musb_ep; + struct musb_hw_ep *ep; + void __iomem *regs; + int is_in; + u16 csr; + + if (epnum == 0 || epnum >= MUSB_C_NUM_EPS || + ctrlrequest->wValue != USB_ENDPOINT_HALT) + break; + + ep = musb->endpoints + epnum; + regs = ep->regs; + is_in = ctrlrequest->wIndex & USB_DIR_IN; + if (is_in) + musb_ep = &ep->ep_in; + else + musb_ep = &ep->ep_out; + if (!musb_ep->desc) + break; + + musb_ep_select(mbase, epnum); + if (is_in) { + csr = musb_readw(regs, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) + csr |= MUSB_TXCSR_FLUSHFIFO; + csr |= MUSB_TXCSR_P_SENDSTALL + | MUSB_TXCSR_CLRDATATOG + | MUSB_TXCSR_P_WZC_BITS; + musb_writew(regs, MUSB_TXCSR, csr); + } else { + csr = musb_readw(regs, MUSB_RXCSR); + csr |= MUSB_RXCSR_P_SENDSTALL + | MUSB_RXCSR_FLUSHFIFO + | MUSB_RXCSR_CLRDATATOG + | MUSB_RXCSR_P_WZC_BITS; + musb_writew(regs, MUSB_RXCSR, csr); + } + + /* select ep0 again */ + musb_ep_select(mbase, 0); + handled = 1; + } break; + + default: + /* class, vendor, etc ... delegate */ + handled = 0; + break; + } + break; + default: + /* delegate SET_CONFIGURATION, etc */ + handled = 0; + } + } else + handled = 0; + return handled; +} + +/* we have an ep0out data packet + * Context: caller holds controller lock + */ +static void ep0_rxstate(struct musb *musb) +{ + void __iomem *regs = musb->control_ep->regs; + struct musb_request *request; + struct usb_request *req; + u16 count, csr; + + request = next_ep0_request(musb); + req = &request->request; + + /* read packet and ack; or stall because of gadget driver bug: + * should have provided the rx buffer before setup() returned. + */ + if (req) { + void *buf = req->buf + req->actual; + unsigned len = req->length - req->actual; + + /* read the buffer */ + count = musb_readb(regs, MUSB_COUNT0); + if (count > len) { + req->status = -EOVERFLOW; + count = len; + } + musb_read_fifo(&musb->endpoints[0], count, buf); + req->actual += count; + csr = MUSB_CSR0_P_SVDRXPKTRDY; + if (count < 64 || req->actual == req->length) { + musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; + csr |= MUSB_CSR0_P_DATAEND; + } else + req = NULL; + } else + csr = MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_SENDSTALL; + + + /* Completion handler may choose to stall, e.g. because the + * message just received holds invalid data. + */ + if (req) { + musb->ackpend = csr; + musb_g_ep0_giveback(musb, req); + if (!musb->ackpend) + return; + musb->ackpend = 0; + } + musb_ep_select(musb->mregs, 0); + musb_writew(regs, MUSB_CSR0, csr); +} + +/* + * transmitting to the host (IN), this code might be called from IRQ + * and from kernel thread. + * + * Context: caller holds controller lock + */ +static void ep0_txstate(struct musb *musb) +{ + void __iomem *regs = musb->control_ep->regs; + struct musb_request *req = next_ep0_request(musb); + struct usb_request *request; + u16 csr = MUSB_CSR0_TXPKTRDY; + u8 *fifo_src; + u8 fifo_count; + + if (!req) { + /* WARN_ON(1); */ + dev_dbg(musb->controller, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0)); + return; + } + + request = &req->request; + + /* load the data */ + fifo_src = (u8 *) request->buf + request->actual; + fifo_count = min((unsigned) MUSB_EP0_FIFOSIZE, + request->length - request->actual); + musb_write_fifo(&musb->endpoints[0], fifo_count, fifo_src); + request->actual += fifo_count; + + /* update the flags */ + if (fifo_count < MUSB_MAX_END0_PACKET + || (request->actual == request->length + && !request->zero)) { + musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT; + csr |= MUSB_CSR0_P_DATAEND; + } else + request = NULL; + + /* report completions as soon as the fifo's loaded; there's no + * win in waiting till this last packet gets acked. (other than + * very precise fault reporting, needed by USB TMC; possible with + * this hardware, but not usable from portable gadget drivers.) + */ + if (request) { + musb->ackpend = csr; + musb_g_ep0_giveback(musb, request); + if (!musb->ackpend) + return; + musb->ackpend = 0; + } + + /* send it out, triggering a "txpktrdy cleared" irq */ + musb_ep_select(musb->mregs, 0); + musb_writew(regs, MUSB_CSR0, csr); +} + +/* + * Read a SETUP packet (struct usb_ctrlrequest) from the hardware. + * Fields are left in USB byte-order. + * + * Context: caller holds controller lock. + */ +static void +musb_read_setup(struct musb *musb, struct usb_ctrlrequest *req) +{ + struct musb_request *r; + void __iomem *regs = musb->control_ep->regs; + + musb_read_fifo(&musb->endpoints[0], sizeof *req, (u8 *)req); + + /* NOTE: earlier 2.6 versions changed setup packets to host + * order, but now USB packets always stay in USB byte order. + */ + dev_dbg(musb->controller, "SETUP req%02x.%02x v%04x i%04x l%d\n", + req->bRequestType, + req->bRequest, + le16_to_cpu(req->wValue), + le16_to_cpu(req->wIndex), + le16_to_cpu(req->wLength)); + + /* clean up any leftover transfers */ + r = next_ep0_request(musb); + if (r) + musb_g_ep0_giveback(musb, &r->request); + + /* For zero-data requests we want to delay the STATUS stage to + * avoid SETUPEND errors. If we read data (OUT), delay accepting + * packets until there's a buffer to store them in. + * + * If we write data, the controller acts happier if we enable + * the TX FIFO right away, and give the controller a moment + * to switch modes... + */ + musb->set_address = false; + musb->ackpend = MUSB_CSR0_P_SVDRXPKTRDY; + if (req->wLength == 0) { + if (req->bRequestType & USB_DIR_IN) + musb->ackpend |= MUSB_CSR0_TXPKTRDY; + musb->ep0_state = MUSB_EP0_STAGE_ACKWAIT; + } else if (req->bRequestType & USB_DIR_IN) { + musb->ep0_state = MUSB_EP0_STAGE_TX; + musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDRXPKTRDY); + while ((musb_readw(regs, MUSB_CSR0) + & MUSB_CSR0_RXPKTRDY) != 0) + cpu_relax(); + musb->ackpend = 0; + } else + musb->ep0_state = MUSB_EP0_STAGE_RX; +} + +static int +forward_to_driver(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest) +__releases(musb->lock) +__acquires(musb->lock) +{ + int retval; + if (!musb->gadget_driver) + return -EOPNOTSUPP; + spin_unlock(&musb->lock); + retval = musb->gadget_driver->setup(&musb->g, ctrlrequest); + spin_lock(&musb->lock); + return retval; +} + +/* + * Handle peripheral ep0 interrupt + * + * Context: irq handler; we won't re-enter the driver that way. + */ +irqreturn_t musb_g_ep0_irq(struct musb *musb) +{ + u16 csr; + u16 len; + void __iomem *mbase = musb->mregs; + void __iomem *regs = musb->endpoints[0].regs; + irqreturn_t retval = IRQ_NONE; + + musb_ep_select(mbase, 0); /* select ep0 */ + csr = musb_readw(regs, MUSB_CSR0); + len = musb_readb(regs, MUSB_COUNT0); + + dev_dbg(musb->controller, "csr %04x, count %d, myaddr %d, ep0stage %s\n", + csr, len, + musb_readb(mbase, MUSB_FADDR), + decode_ep0stage(musb->ep0_state)); + + if (csr & MUSB_CSR0_P_DATAEND) { + /* + * If DATAEND is set we should not call the callback, + * hence the status stage is not complete. + */ + return IRQ_HANDLED; + } + + /* I sent a stall.. need to acknowledge it now.. */ + if (csr & MUSB_CSR0_P_SENTSTALL) { + musb_writew(regs, MUSB_CSR0, + csr & ~MUSB_CSR0_P_SENTSTALL); + retval = IRQ_HANDLED; + musb->ep0_state = MUSB_EP0_STAGE_IDLE; + csr = musb_readw(regs, MUSB_CSR0); + } + + /* request ended "early" */ + if (csr & MUSB_CSR0_P_SETUPEND) { + musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND); + retval = IRQ_HANDLED; + /* Transition into the early status phase */ + switch (musb->ep0_state) { + case MUSB_EP0_STAGE_TX: + musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT; + break; + case MUSB_EP0_STAGE_RX: + musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; + break; + default: + ERR("SetupEnd came in a wrong ep0stage %s\n", + decode_ep0stage(musb->ep0_state)); + } + csr = musb_readw(regs, MUSB_CSR0); + /* NOTE: request may need completion */ + } + + /* docs from Mentor only describe tx, rx, and idle/setup states. + * we need to handle nuances around status stages, and also the + * case where status and setup stages come back-to-back ... + */ + switch (musb->ep0_state) { + + case MUSB_EP0_STAGE_TX: + /* irq on clearing txpktrdy */ + if ((csr & MUSB_CSR0_TXPKTRDY) == 0) { + ep0_txstate(musb); + retval = IRQ_HANDLED; + } + break; + + case MUSB_EP0_STAGE_RX: + /* irq on set rxpktrdy */ + if (csr & MUSB_CSR0_RXPKTRDY) { + ep0_rxstate(musb); + retval = IRQ_HANDLED; + } + break; + + case MUSB_EP0_STAGE_STATUSIN: + /* end of sequence #2 (OUT/RX state) or #3 (no data) */ + + /* update address (if needed) only @ the end of the + * status phase per usb spec, which also guarantees + * we get 10 msec to receive this irq... until this + * is done we won't see the next packet. + */ + if (musb->set_address) { + musb->set_address = false; + musb_writeb(mbase, MUSB_FADDR, musb->address); + } + + /* enter test mode if needed (exit by reset) */ + else if (musb->test_mode) { + dev_dbg(musb->controller, "entering TESTMODE\n"); + + if (MUSB_TEST_PACKET == musb->test_mode_nr) + musb_load_testpacket(musb); + + musb_writeb(mbase, MUSB_TESTMODE, + musb->test_mode_nr); + } + /* FALLTHROUGH */ + + case MUSB_EP0_STAGE_STATUSOUT: + /* end of sequence #1: write to host (TX state) */ + { + struct musb_request *req; + + req = next_ep0_request(musb); + if (req) + musb_g_ep0_giveback(musb, &req->request); + } + + /* + * In case when several interrupts can get coalesced, + * check to see if we've already received a SETUP packet... + */ + if (csr & MUSB_CSR0_RXPKTRDY) + goto setup; + + retval = IRQ_HANDLED; + musb->ep0_state = MUSB_EP0_STAGE_IDLE; + break; + + case MUSB_EP0_STAGE_IDLE: + /* + * This state is typically (but not always) indiscernible + * from the status states since the corresponding interrupts + * tend to happen within too little period of time (with only + * a zero-length packet in between) and so get coalesced... + */ + retval = IRQ_HANDLED; + musb->ep0_state = MUSB_EP0_STAGE_SETUP; + /* FALLTHROUGH */ + + case MUSB_EP0_STAGE_SETUP: +setup: + if (csr & MUSB_CSR0_RXPKTRDY) { + struct usb_ctrlrequest setup; + int handled = 0; + + if (len != 8) { + ERR("SETUP packet len %d != 8 ?\n", len); + break; + } + musb_read_setup(musb, &setup); + retval = IRQ_HANDLED; + + /* sometimes the RESET won't be reported */ + if (unlikely(musb->g.speed == USB_SPEED_UNKNOWN)) { + u8 power; + + printk(KERN_NOTICE "%s: peripheral reset " + "irq lost!\n", + musb_driver_name); + power = musb_readb(mbase, MUSB_POWER); + musb->g.speed = (power & MUSB_POWER_HSMODE) + ? USB_SPEED_HIGH : USB_SPEED_FULL; + + } + + switch (musb->ep0_state) { + + /* sequence #3 (no data stage), includes requests + * we can't forward (notably SET_ADDRESS and the + * device/endpoint feature set/clear operations) + * plus SET_CONFIGURATION and others we must + */ + case MUSB_EP0_STAGE_ACKWAIT: + handled = service_zero_data_request( + musb, &setup); + + /* + * We're expecting no data in any case, so + * always set the DATAEND bit -- doing this + * here helps avoid SetupEnd interrupt coming + * in the idle stage when we're stalling... + */ + musb->ackpend |= MUSB_CSR0_P_DATAEND; + + /* status stage might be immediate */ + if (handled > 0) + musb->ep0_state = + MUSB_EP0_STAGE_STATUSIN; + break; + + /* sequence #1 (IN to host), includes GET_STATUS + * requests that we can't forward, GET_DESCRIPTOR + * and others that we must + */ + case MUSB_EP0_STAGE_TX: + handled = service_in_request(musb, &setup); + if (handled > 0) { + musb->ackpend = MUSB_CSR0_TXPKTRDY + | MUSB_CSR0_P_DATAEND; + musb->ep0_state = + MUSB_EP0_STAGE_STATUSOUT; + } + break; + + /* sequence #2 (OUT from host), always forward */ + default: /* MUSB_EP0_STAGE_RX */ + break; + } + + dev_dbg(musb->controller, "handled %d, csr %04x, ep0stage %s\n", + handled, csr, + decode_ep0stage(musb->ep0_state)); + + /* unless we need to delegate this to the gadget + * driver, we know how to wrap this up: csr0 has + * not yet been written. + */ + if (handled < 0) + goto stall; + else if (handled > 0) + goto finish; + + handled = forward_to_driver(musb, &setup); + if (handled < 0) { + musb_ep_select(mbase, 0); +stall: + dev_dbg(musb->controller, "stall (%d)\n", handled); + musb->ackpend |= MUSB_CSR0_P_SENDSTALL; + musb->ep0_state = MUSB_EP0_STAGE_IDLE; +finish: + musb_writew(regs, MUSB_CSR0, + musb->ackpend); + musb->ackpend = 0; + } + } + break; + + case MUSB_EP0_STAGE_ACKWAIT: + /* This should not happen. But happens with tusb6010 with + * g_file_storage and high speed. Do nothing. + */ + retval = IRQ_HANDLED; + break; + + default: + /* "can't happen" */ + WARN_ON(1); + musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL); + musb->ep0_state = MUSB_EP0_STAGE_IDLE; + break; + } + + return retval; +} + + +static int +musb_g_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) +{ + /* always enabled */ + return -EINVAL; +} + +static int musb_g_ep0_disable(struct usb_ep *e) +{ + /* always enabled */ + return -EINVAL; +} + +static int +musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags) +{ + struct musb_ep *ep; + struct musb_request *req; + struct musb *musb; + int status; + unsigned long lockflags; + void __iomem *regs; + + if (!e || !r) + return -EINVAL; + + ep = to_musb_ep(e); + musb = ep->musb; + regs = musb->control_ep->regs; + + req = to_musb_request(r); + req->musb = musb; + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->tx = ep->is_in; + + spin_lock_irqsave(&musb->lock, lockflags); + + if (!list_empty(&ep->req_list)) { + status = -EBUSY; + goto cleanup; + } + + switch (musb->ep0_state) { + case MUSB_EP0_STAGE_RX: /* control-OUT data */ + case MUSB_EP0_STAGE_TX: /* control-IN data */ + case MUSB_EP0_STAGE_ACKWAIT: /* zero-length data */ + status = 0; + break; + default: + dev_dbg(musb->controller, "ep0 request queued in state %d\n", + musb->ep0_state); + status = -EINVAL; + goto cleanup; + } + + /* add request to the list */ + list_add_tail(&req->list, &ep->req_list); + + dev_dbg(musb->controller, "queue to %s (%s), length=%d\n", + ep->name, ep->is_in ? "IN/TX" : "OUT/RX", + req->request.length); + + musb_ep_select(musb->mregs, 0); + + /* sequence #1, IN ... start writing the data */ + if (musb->ep0_state == MUSB_EP0_STAGE_TX) + ep0_txstate(musb); + + /* sequence #3, no-data ... issue IN status */ + else if (musb->ep0_state == MUSB_EP0_STAGE_ACKWAIT) { + if (req->request.length) + status = -EINVAL; + else { + musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; + musb_writew(regs, MUSB_CSR0, + musb->ackpend | MUSB_CSR0_P_DATAEND); + musb->ackpend = 0; + musb_g_ep0_giveback(ep->musb, r); + } + + /* else for sequence #2 (OUT), caller provides a buffer + * before the next packet arrives. deferred responses + * (after SETUP is acked) are racey. + */ + } else if (musb->ackpend) { + musb_writew(regs, MUSB_CSR0, musb->ackpend); + musb->ackpend = 0; + } + +cleanup: + spin_unlock_irqrestore(&musb->lock, lockflags); + return status; +} + +static int musb_g_ep0_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + /* we just won't support this */ + return -EINVAL; +} + +static int musb_g_ep0_halt(struct usb_ep *e, int value) +{ + struct musb_ep *ep; + struct musb *musb; + void __iomem *base, *regs; + unsigned long flags; + int status; + u16 csr; + + if (!e || !value) + return -EINVAL; + + ep = to_musb_ep(e); + musb = ep->musb; + base = musb->mregs; + regs = musb->control_ep->regs; + status = 0; + + spin_lock_irqsave(&musb->lock, flags); + + if (!list_empty(&ep->req_list)) { + status = -EBUSY; + goto cleanup; + } + + musb_ep_select(base, 0); + csr = musb->ackpend; + + switch (musb->ep0_state) { + + /* Stalls are usually issued after parsing SETUP packet, either + * directly in irq context from setup() or else later. + */ + case MUSB_EP0_STAGE_TX: /* control-IN data */ + case MUSB_EP0_STAGE_ACKWAIT: /* STALL for zero-length data */ + case MUSB_EP0_STAGE_RX: /* control-OUT data */ + csr = musb_readw(regs, MUSB_CSR0); + /* FALLTHROUGH */ + + /* It's also OK to issue stalls during callbacks when a non-empty + * DATA stage buffer has been read (or even written). + */ + case MUSB_EP0_STAGE_STATUSIN: /* control-OUT status */ + case MUSB_EP0_STAGE_STATUSOUT: /* control-IN status */ + + csr |= MUSB_CSR0_P_SENDSTALL; + musb_writew(regs, MUSB_CSR0, csr); + musb->ep0_state = MUSB_EP0_STAGE_IDLE; + musb->ackpend = 0; + break; + default: + dev_dbg(musb->controller, "ep0 can't halt in state %d\n", musb->ep0_state); + status = -EINVAL; + } + +cleanup: + spin_unlock_irqrestore(&musb->lock, flags); + return status; +} + +const struct usb_ep_ops musb_g_ep0_ops = { + .enable = musb_g_ep0_enable, + .disable = musb_g_ep0_disable, + .alloc_request = musb_alloc_request, + .free_request = musb_free_request, + .queue = musb_g_ep0_queue, + .dequeue = musb_g_ep0_dequeue, + .set_halt = musb_g_ep0_halt, +}; diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_host.c b/qemu/roms/u-boot/drivers/usb/musb-new/musb_host.c new file mode 100644 index 000000000..9a2cf59d9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_host.c @@ -0,0 +1,2400 @@ +/* + * MUSB OTG driver host support + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * Copyright (C) 2008-2009 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#define __UBOOT__ +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include "linux-compat.h" +#include "usb-compat.h" +#endif + +#include "musb_core.h" +#include "musb_host.h" + + +/* MUSB HOST status 22-mar-2006 + * + * - There's still lots of partial code duplication for fault paths, so + * they aren't handled as consistently as they need to be. + * + * - PIO mostly behaved when last tested. + * + including ep0, with all usbtest cases 9, 10 + * + usbtest 14 (ep0out) doesn't seem to run at all + * + double buffered OUT/TX endpoints saw stalls(!) with certain usbtest + * configurations, but otherwise double buffering passes basic tests. + * + for 2.6.N, for N > ~10, needs API changes for hcd framework. + * + * - DMA (CPPI) ... partially behaves, not currently recommended + * + about 1/15 the speed of typical EHCI implementations (PCI) + * + RX, all too often reqpkt seems to misbehave after tx + * + TX, no known issues (other than evident silicon issue) + * + * - DMA (Mentor/OMAP) ...has at least toggle update problems + * + * - [23-feb-2009] minimal traffic scheduling to avoid bulk RX packet + * starvation ... nothing yet for TX, interrupt, or bulk. + * + * - Not tested with HNP, but some SRP paths seem to behave. + * + * NOTE 24-August-2006: + * + * - Bulk traffic finally uses both sides of hardware ep1, freeing up an + * extra endpoint for periodic use enabling hub + keybd + mouse. That + * mostly works, except that with "usbnet" it's easy to trigger cases + * with "ping" where RX loses. (a) ping to davinci, even "ping -f", + * fine; but (b) ping _from_ davinci, even "ping -c 1", ICMP RX loses + * although ARP RX wins. (That test was done with a full speed link.) + */ + + +/* + * NOTE on endpoint usage: + * + * CONTROL transfers all go through ep0. BULK ones go through dedicated IN + * and OUT endpoints ... hardware is dedicated for those "async" queue(s). + * (Yes, bulk _could_ use more of the endpoints than that, and would even + * benefit from it.) + * + * INTERUPPT and ISOCHRONOUS transfers are scheduled to the other endpoints. + * So far that scheduling is both dumb and optimistic: the endpoint will be + * "claimed" until its software queue is no longer refilled. No multiplexing + * of transfers between endpoints, or anything clever. + */ + + +static void musb_ep_program(struct musb *musb, u8 epnum, + struct urb *urb, int is_out, + u8 *buf, u32 offset, u32 len); + +/* + * Clear TX fifo. Needed to avoid BABBLE errors. + */ +static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) +{ + struct musb *musb = ep->musb; + void __iomem *epio = ep->regs; + u16 csr; + u16 lastcsr = 0; + int retries = 1000; + + csr = musb_readw(epio, MUSB_TXCSR); + while (csr & MUSB_TXCSR_FIFONOTEMPTY) { + if (csr != lastcsr) + dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr); + lastcsr = csr; + csr |= MUSB_TXCSR_FLUSHFIFO; + musb_writew(epio, MUSB_TXCSR, csr); + csr = musb_readw(epio, MUSB_TXCSR); + if (WARN(retries-- < 1, + "Could not flush host TX%d fifo: csr: %04x\n", + ep->epnum, csr)) + return; + mdelay(1); + } +} + +static void musb_h_ep0_flush_fifo(struct musb_hw_ep *ep) +{ + void __iomem *epio = ep->regs; + u16 csr; + int retries = 5; + + /* scrub any data left in the fifo */ + do { + csr = musb_readw(epio, MUSB_TXCSR); + if (!(csr & (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_RXPKTRDY))) + break; + musb_writew(epio, MUSB_TXCSR, MUSB_CSR0_FLUSHFIFO); + csr = musb_readw(epio, MUSB_TXCSR); + udelay(10); + } while (--retries); + + WARN(!retries, "Could not flush host TX%d fifo: csr: %04x\n", + ep->epnum, csr); + + /* and reset for the next transfer */ + musb_writew(epio, MUSB_TXCSR, 0); +} + +/* + * Start transmit. Caller is responsible for locking shared resources. + * musb must be locked. + */ +static inline void musb_h_tx_start(struct musb_hw_ep *ep) +{ + u16 txcsr; + + /* NOTE: no locks here; caller should lock and select EP */ + if (ep->epnum) { + txcsr = musb_readw(ep->regs, MUSB_TXCSR); + txcsr |= MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_H_WZC_BITS; + musb_writew(ep->regs, MUSB_TXCSR, txcsr); + } else { + txcsr = MUSB_CSR0_H_SETUPPKT | MUSB_CSR0_TXPKTRDY; + musb_writew(ep->regs, MUSB_CSR0, txcsr); + } + +} + +static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep) +{ + u16 txcsr; + + /* NOTE: no locks here; caller should lock and select EP */ + txcsr = musb_readw(ep->regs, MUSB_TXCSR); + txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS; + if (is_cppi_enabled()) + txcsr |= MUSB_TXCSR_DMAMODE; + musb_writew(ep->regs, MUSB_TXCSR, txcsr); +} + +static void musb_ep_set_qh(struct musb_hw_ep *ep, int is_in, struct musb_qh *qh) +{ + if (is_in != 0 || ep->is_shared_fifo) + ep->in_qh = qh; + if (is_in == 0 || ep->is_shared_fifo) + ep->out_qh = qh; +} + +static struct musb_qh *musb_ep_get_qh(struct musb_hw_ep *ep, int is_in) +{ + return is_in ? ep->in_qh : ep->out_qh; +} + +/* + * Start the URB at the front of an endpoint's queue + * end must be claimed from the caller. + * + * Context: controller locked, irqs blocked + */ +static void +musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) +{ + u16 frame; + u32 len; + void __iomem *mbase = musb->mregs; + struct urb *urb = next_urb(qh); + void *buf = urb->transfer_buffer; + u32 offset = 0; + struct musb_hw_ep *hw_ep = qh->hw_ep; + unsigned pipe = urb->pipe; + u8 address = usb_pipedevice(pipe); + int epnum = hw_ep->epnum; + + /* initialize software qh state */ + qh->offset = 0; + qh->segsize = 0; + + /* gather right source of data */ + switch (qh->type) { + case USB_ENDPOINT_XFER_CONTROL: + /* control transfers always start with SETUP */ + is_in = 0; + musb->ep0_stage = MUSB_EP0_START; + buf = urb->setup_packet; + len = 8; + break; +#ifndef __UBOOT__ + case USB_ENDPOINT_XFER_ISOC: + qh->iso_idx = 0; + qh->frame = 0; + offset = urb->iso_frame_desc[0].offset; + len = urb->iso_frame_desc[0].length; + break; +#endif + default: /* bulk, interrupt */ + /* actual_length may be nonzero on retry paths */ + buf = urb->transfer_buffer + urb->actual_length; + len = urb->transfer_buffer_length - urb->actual_length; + } + + dev_dbg(musb->controller, "qh %p urb %p dev%d ep%d%s%s, hw_ep %d, %p/%d\n", + qh, urb, address, qh->epnum, + is_in ? "in" : "out", + ({char *s; switch (qh->type) { + case USB_ENDPOINT_XFER_CONTROL: s = ""; break; + case USB_ENDPOINT_XFER_BULK: s = "-bulk"; break; +#ifndef __UBOOT__ + case USB_ENDPOINT_XFER_ISOC: s = "-iso"; break; +#endif + default: s = "-intr"; break; + }; s; }), + epnum, buf + offset, len); + + /* Configure endpoint */ + musb_ep_set_qh(hw_ep, is_in, qh); + musb_ep_program(musb, epnum, urb, !is_in, buf, offset, len); + + /* transmit may have more work: start it when it is time */ + if (is_in) + return; + + /* determine if the time is right for a periodic transfer */ + switch (qh->type) { +#ifndef __UBOOT__ + case USB_ENDPOINT_XFER_ISOC: +#endif + case USB_ENDPOINT_XFER_INT: + dev_dbg(musb->controller, "check whether there's still time for periodic Tx\n"); + frame = musb_readw(mbase, MUSB_FRAME); + /* FIXME this doesn't implement that scheduling policy ... + * or handle framecounter wrapping + */ +#ifndef __UBOOT__ + if ((urb->transfer_flags & URB_ISO_ASAP) + || (frame >= urb->start_frame)) { + /* REVISIT the SOF irq handler shouldn't duplicate + * this code; and we don't init urb->start_frame... + */ + qh->frame = 0; + goto start; + } else { +#endif + qh->frame = urb->start_frame; + /* enable SOF interrupt so we can count down */ + dev_dbg(musb->controller, "SOF for %d\n", epnum); +#if 1 /* ifndef CONFIG_ARCH_DAVINCI */ + musb_writeb(mbase, MUSB_INTRUSBE, 0xff); +#endif +#ifndef __UBOOT__ + } +#endif + break; + default: +start: + dev_dbg(musb->controller, "Start TX%d %s\n", epnum, + hw_ep->tx_channel ? "dma" : "pio"); + + if (!hw_ep->tx_channel) + musb_h_tx_start(hw_ep); + else if (is_cppi_enabled() || tusb_dma_omap()) + musb_h_tx_dma_start(hw_ep); + } +} + +/* Context: caller owns controller lock, IRQs are blocked */ +static void musb_giveback(struct musb *musb, struct urb *urb, int status) +__releases(musb->lock) +__acquires(musb->lock) +{ + dev_dbg(musb->controller, + "complete %p %pF (%d), dev%d ep%d%s, %d/%d\n", + urb, urb->complete, status, + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->actual_length, urb->transfer_buffer_length + ); + + usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb); + spin_unlock(&musb->lock); + usb_hcd_giveback_urb(musb_to_hcd(musb), urb, status); + spin_lock(&musb->lock); +} + +/* For bulk/interrupt endpoints only */ +static inline void musb_save_toggle(struct musb_qh *qh, int is_in, + struct urb *urb) +{ + void __iomem *epio = qh->hw_ep->regs; + u16 csr; + + /* + * FIXME: the current Mentor DMA code seems to have + * problems getting toggle correct. + */ + + if (is_in) + csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; + else + csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; + + usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0); +} + +/* + * Advance this hardware endpoint's queue, completing the specified URB and + * advancing to either the next URB queued to that qh, or else invalidating + * that qh and advancing to the next qh scheduled after the current one. + * + * Context: caller owns controller lock, IRQs are blocked + */ +static void musb_advance_schedule(struct musb *musb, struct urb *urb, + struct musb_hw_ep *hw_ep, int is_in) +{ + struct musb_qh *qh = musb_ep_get_qh(hw_ep, is_in); + struct musb_hw_ep *ep = qh->hw_ep; + int ready = qh->is_ready; + int status; + + status = (urb->status == -EINPROGRESS) ? 0 : urb->status; + + /* save toggle eagerly, for paranoia */ + switch (qh->type) { + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + musb_save_toggle(qh, is_in, urb); + break; +#ifndef __UBOOT__ + case USB_ENDPOINT_XFER_ISOC: + if (status == 0 && urb->error_count) + status = -EXDEV; + break; +#endif + } + + qh->is_ready = 0; + musb_giveback(musb, urb, status); + qh->is_ready = ready; + + /* reclaim resources (and bandwidth) ASAP; deschedule it, and + * invalidate qh as soon as list_empty(&hep->urb_list) + */ + if (list_empty(&qh->hep->urb_list)) { + struct list_head *head; + struct dma_controller *dma = musb->dma_controller; + + if (is_in) { + ep->rx_reinit = 1; + if (ep->rx_channel) { + dma->channel_release(ep->rx_channel); + ep->rx_channel = NULL; + } + } else { + ep->tx_reinit = 1; + if (ep->tx_channel) { + dma->channel_release(ep->tx_channel); + ep->tx_channel = NULL; + } + } + + /* Clobber old pointers to this qh */ + musb_ep_set_qh(ep, is_in, NULL); + qh->hep->hcpriv = NULL; + + switch (qh->type) { + + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + /* fifo policy for these lists, except that NAKing + * should rotate a qh to the end (for fairness). + */ + if (qh->mux == 1) { + head = qh->ring.prev; + list_del(&qh->ring); + kfree(qh); + qh = first_qh(head); + break; + } + + case USB_ENDPOINT_XFER_ISOC: + case USB_ENDPOINT_XFER_INT: + /* this is where periodic bandwidth should be + * de-allocated if it's tracked and allocated; + * and where we'd update the schedule tree... + */ + kfree(qh); + qh = NULL; + break; + } + } + + if (qh != NULL && qh->is_ready) { + dev_dbg(musb->controller, "... next ep%d %cX urb %p\n", + hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh)); + musb_start_urb(musb, is_in, qh); + } +} + +static u16 musb_h_flush_rxfifo(struct musb_hw_ep *hw_ep, u16 csr) +{ + /* we don't want fifo to fill itself again; + * ignore dma (various models), + * leave toggle alone (may not have been saved yet) + */ + csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_RXPKTRDY; + csr &= ~(MUSB_RXCSR_H_REQPKT + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR); + + /* write 2x to allow double buffering */ + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + + /* flush writebuffer */ + return musb_readw(hw_ep->regs, MUSB_RXCSR); +} + +/* + * PIO RX for a packet (or part of it). + */ +static bool +musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) +{ + u16 rx_count; + u8 *buf; + u16 csr; + bool done = false; + u32 length; + int do_flush = 0; + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh = hw_ep->in_qh; + int pipe = urb->pipe; + void *buffer = urb->transfer_buffer; + + /* musb_ep_select(mbase, epnum); */ + rx_count = musb_readw(epio, MUSB_RXCOUNT); + dev_dbg(musb->controller, "RX%d count %d, buffer %p len %d/%d\n", epnum, rx_count, + urb->transfer_buffer, qh->offset, + urb->transfer_buffer_length); + + /* unload FIFO */ +#ifndef __UBOOT__ + if (usb_pipeisoc(pipe)) { + int status = 0; + struct usb_iso_packet_descriptor *d; + + if (iso_err) { + status = -EILSEQ; + urb->error_count++; + } + + d = urb->iso_frame_desc + qh->iso_idx; + buf = buffer + d->offset; + length = d->length; + if (rx_count > length) { + if (status == 0) { + status = -EOVERFLOW; + urb->error_count++; + } + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", rx_count, length); + do_flush = 1; + } else + length = rx_count; + urb->actual_length += length; + d->actual_length = length; + + d->status = status; + + /* see if we are done */ + done = (++qh->iso_idx >= urb->number_of_packets); + } else { +#endif + /* non-isoch */ + buf = buffer + qh->offset; + length = urb->transfer_buffer_length - qh->offset; + if (rx_count > length) { + if (urb->status == -EINPROGRESS) + urb->status = -EOVERFLOW; + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", rx_count, length); + do_flush = 1; + } else + length = rx_count; + urb->actual_length += length; + qh->offset += length; + + /* see if we are done */ + done = (urb->actual_length == urb->transfer_buffer_length) + || (rx_count < qh->maxpacket) + || (urb->status != -EINPROGRESS); + if (done + && (urb->status == -EINPROGRESS) + && (urb->transfer_flags & URB_SHORT_NOT_OK) + && (urb->actual_length + < urb->transfer_buffer_length)) + urb->status = -EREMOTEIO; +#ifndef __UBOOT__ + } +#endif + + musb_read_fifo(hw_ep, length, buf); + + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_H_WZC_BITS; + if (unlikely(do_flush)) + musb_h_flush_rxfifo(hw_ep, csr); + else { + /* REVISIT this assumes AUTOCLEAR is never set */ + csr &= ~(MUSB_RXCSR_RXPKTRDY | MUSB_RXCSR_H_REQPKT); + if (!done) + csr |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, csr); + } + + return done; +} + +/* we don't always need to reinit a given side of an endpoint... + * when we do, use tx/rx reinit routine and then construct a new CSR + * to address data toggle, NYET, and DMA or PIO. + * + * it's possible that driver bugs (especially for DMA) or aborting a + * transfer might have left the endpoint busier than it should be. + * the busy/not-empty tests are basically paranoia. + */ +static void +musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) +{ + u16 csr; + + /* NOTE: we know the "rx" fifo reinit never triggers for ep0. + * That always uses tx_reinit since ep0 repurposes TX register + * offsets; the initial SETUP packet is also a kind of OUT. + */ + + /* if programmed for Tx, put it in RX mode */ + if (ep->is_shared_fifo) { + csr = musb_readw(ep->regs, MUSB_TXCSR); + if (csr & MUSB_TXCSR_MODE) { + musb_h_tx_flush_fifo(ep); + csr = musb_readw(ep->regs, MUSB_TXCSR); + musb_writew(ep->regs, MUSB_TXCSR, + csr | MUSB_TXCSR_FRCDATATOG); + } + + /* + * Clear the MODE bit (and everything else) to enable Rx. + * NOTE: we mustn't clear the DMAMODE bit before DMAENAB. + */ + if (csr & MUSB_TXCSR_DMAMODE) + musb_writew(ep->regs, MUSB_TXCSR, MUSB_TXCSR_DMAMODE); + musb_writew(ep->regs, MUSB_TXCSR, 0); + + /* scrub all previous state, clearing toggle */ + } else { + csr = musb_readw(ep->regs, MUSB_RXCSR); + if (csr & MUSB_RXCSR_RXPKTRDY) + WARNING("rx%d, packet/%d ready?\n", ep->epnum, + musb_readw(ep->regs, MUSB_RXCOUNT)); + + musb_h_flush_rxfifo(ep, MUSB_RXCSR_CLRDATATOG); + } + + /* target addr and (for multipoint) hub addr/port */ + if (musb->is_multipoint) { + musb_write_rxfunaddr(ep->target_regs, qh->addr_reg); + musb_write_rxhubaddr(ep->target_regs, qh->h_addr_reg); + musb_write_rxhubport(ep->target_regs, qh->h_port_reg); + + } else + musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg); + + /* protocol/endpoint, interval/NAKlimit, i/o size */ + musb_writeb(ep->regs, MUSB_RXTYPE, qh->type_reg); + musb_writeb(ep->regs, MUSB_RXINTERVAL, qh->intv_reg); + /* NOTE: bulk combining rewrites high bits of maxpacket */ + /* Set RXMAXP with the FIFO size of the endpoint + * to disable double buffer mode. + */ + if (musb->double_buffer_not_ok) + musb_writew(ep->regs, MUSB_RXMAXP, ep->max_packet_sz_rx); + else + musb_writew(ep->regs, MUSB_RXMAXP, + qh->maxpacket | ((qh->hb_mult - 1) << 11)); + + ep->rx_reinit = 0; +} + +static bool musb_tx_dma_program(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, struct musb_qh *qh, + struct urb *urb, u32 offset, u32 length) +{ + struct dma_channel *channel = hw_ep->tx_channel; + void __iomem *epio = hw_ep->regs; + u16 pkt_size = qh->maxpacket; + u16 csr; + u8 mode; + +#ifdef CONFIG_USB_INVENTRA_DMA + if (length > channel->max_len) + length = channel->max_len; + + csr = musb_readw(epio, MUSB_TXCSR); + if (length > pkt_size) { + mode = 1; + csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB; + /* autoset shouldn't be set in high bandwidth */ + if (qh->hb_mult == 1) + csr |= MUSB_TXCSR_AUTOSET; + } else { + mode = 0; + csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE); + csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */ + } + channel->desired_mode = mode; + musb_writew(epio, MUSB_TXCSR, csr); +#else + if (!is_cppi_enabled() && !tusb_dma_omap()) + return false; + + channel->actual_len = 0; + + /* + * TX uses "RNDIS" mode automatically but needs help + * to identify the zero-length-final-packet case. + */ + mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0; +#endif + + qh->segsize = length; + + /* + * Ensure the data reaches to main memory before starting + * DMA transfer + */ + wmb(); + + if (!dma->channel_program(channel, pkt_size, mode, + urb->transfer_dma + offset, length)) { + dma->channel_release(channel); + hw_ep->tx_channel = NULL; + + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB); + musb_writew(epio, MUSB_TXCSR, csr | MUSB_TXCSR_H_WZC_BITS); + return false; + } + return true; +} + +/* + * Program an HDRC endpoint as per the given URB + * Context: irqs blocked, controller lock held + */ +static void musb_ep_program(struct musb *musb, u8 epnum, + struct urb *urb, int is_out, + u8 *buf, u32 offset, u32 len) +{ + struct dma_controller *dma_controller; + struct dma_channel *dma_channel; + u8 dma_ok; + void __iomem *mbase = musb->mregs; + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh = musb_ep_get_qh(hw_ep, !is_out); + u16 packet_sz = qh->maxpacket; + + dev_dbg(musb->controller, "%s hw%d urb %p spd%d dev%d ep%d%s " + "h_addr%02x h_port%02x bytes %d\n", + is_out ? "-->" : "<--", + epnum, urb, urb->dev->speed, + qh->addr_reg, qh->epnum, is_out ? "out" : "in", + qh->h_addr_reg, qh->h_port_reg, + len); + + musb_ep_select(mbase, epnum); + + /* candidate for DMA? */ + dma_controller = musb->dma_controller; + if (is_dma_capable() && epnum && dma_controller) { + dma_channel = is_out ? hw_ep->tx_channel : hw_ep->rx_channel; + if (!dma_channel) { + dma_channel = dma_controller->channel_alloc( + dma_controller, hw_ep, is_out); + if (is_out) + hw_ep->tx_channel = dma_channel; + else + hw_ep->rx_channel = dma_channel; + } + } else + dma_channel = NULL; + + /* make sure we clear DMAEnab, autoSet bits from previous run */ + + /* OUT/transmit/EP0 or IN/receive? */ + if (is_out) { + u16 csr; + u16 int_txe; + u16 load_count; + + csr = musb_readw(epio, MUSB_TXCSR); + + /* disable interrupt in case we flush */ + int_txe = musb_readw(mbase, MUSB_INTRTXE); + musb_writew(mbase, MUSB_INTRTXE, int_txe & ~(1 << epnum)); + + /* general endpoint setup */ + if (epnum) { + /* flush all old state, set default */ + musb_h_tx_flush_fifo(hw_ep); + + /* + * We must not clear the DMAMODE bit before or in + * the same cycle with the DMAENAB bit, so we clear + * the latter first... + */ + csr &= ~(MUSB_TXCSR_H_NAKTIMEOUT + | MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_FRCDATATOG + | MUSB_TXCSR_H_RXSTALL + | MUSB_TXCSR_H_ERROR + | MUSB_TXCSR_TXPKTRDY + ); + csr |= MUSB_TXCSR_MODE; + + if (usb_gettoggle(urb->dev, qh->epnum, 1)) + csr |= MUSB_TXCSR_H_WR_DATATOGGLE + | MUSB_TXCSR_H_DATATOGGLE; + else + csr |= MUSB_TXCSR_CLRDATATOG; + + musb_writew(epio, MUSB_TXCSR, csr); + /* REVISIT may need to clear FLUSHFIFO ... */ + csr &= ~MUSB_TXCSR_DMAMODE; + musb_writew(epio, MUSB_TXCSR, csr); + csr = musb_readw(epio, MUSB_TXCSR); + } else { + /* endpoint 0: just flush */ + musb_h_ep0_flush_fifo(hw_ep); + } + + /* target addr and (for multipoint) hub addr/port */ + if (musb->is_multipoint) { + musb_write_txfunaddr(mbase, epnum, qh->addr_reg); + musb_write_txhubaddr(mbase, epnum, qh->h_addr_reg); + musb_write_txhubport(mbase, epnum, qh->h_port_reg); +/* FIXME if !epnum, do the same for RX ... */ + } else + musb_writeb(mbase, MUSB_FADDR, qh->addr_reg); + + /* protocol/endpoint/interval/NAKlimit */ + if (epnum) { + musb_writeb(epio, MUSB_TXTYPE, qh->type_reg); + if (musb->double_buffer_not_ok) + musb_writew(epio, MUSB_TXMAXP, + hw_ep->max_packet_sz_tx); + else if (can_bulk_split(musb, qh->type)) + musb_writew(epio, MUSB_TXMAXP, packet_sz + | ((hw_ep->max_packet_sz_tx / + packet_sz) - 1) << 11); + else + musb_writew(epio, MUSB_TXMAXP, + qh->maxpacket | + ((qh->hb_mult - 1) << 11)); + musb_writeb(epio, MUSB_TXINTERVAL, qh->intv_reg); + } else { + musb_writeb(epio, MUSB_NAKLIMIT0, qh->intv_reg); + if (musb->is_multipoint) + musb_writeb(epio, MUSB_TYPE0, + qh->type_reg); + } + + if (can_bulk_split(musb, qh->type)) + load_count = min((u32) hw_ep->max_packet_sz_tx, + len); + else + load_count = min((u32) packet_sz, len); + + if (dma_channel && musb_tx_dma_program(dma_controller, + hw_ep, qh, urb, offset, len)) + load_count = 0; + + if (load_count) { + /* PIO to load FIFO */ + qh->segsize = load_count; + musb_write_fifo(hw_ep, load_count, buf); + } + + /* re-enable interrupt */ + musb_writew(mbase, MUSB_INTRTXE, int_txe); + + /* IN/receive */ + } else { + u16 csr; + + if (hw_ep->rx_reinit) { + musb_rx_reinit(musb, qh, hw_ep); + + /* init new state: toggle and NYET, maybe DMA later */ + if (usb_gettoggle(urb->dev, qh->epnum, 0)) + csr = MUSB_RXCSR_H_WR_DATATOGGLE + | MUSB_RXCSR_H_DATATOGGLE; + else + csr = 0; + if (qh->type == USB_ENDPOINT_XFER_INT) + csr |= MUSB_RXCSR_DISNYET; + + } else { + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); + + if (csr & (MUSB_RXCSR_RXPKTRDY + | MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_REQPKT)) + ERR("broken !rx_reinit, ep%d csr %04x\n", + hw_ep->epnum, csr); + + /* scrub any stale state, leaving toggle alone */ + csr &= MUSB_RXCSR_DISNYET; + } + + /* kick things off */ + + if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) { + /* Candidate for DMA */ + dma_channel->actual_len = 0L; + qh->segsize = len; + + /* AUTOREQ is in a DMA register */ + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); + + /* + * Unless caller treats short RX transfers as + * errors, we dare not queue multiple transfers. + */ + dma_ok = dma_controller->channel_program(dma_channel, + packet_sz, !(urb->transfer_flags & + URB_SHORT_NOT_OK), + urb->transfer_dma + offset, + qh->segsize); + if (!dma_ok) { + dma_controller->channel_release(dma_channel); + hw_ep->rx_channel = dma_channel = NULL; + } else + csr |= MUSB_RXCSR_DMAENAB; + } + + csr |= MUSB_RXCSR_H_REQPKT; + dev_dbg(musb->controller, "RXCSR%d := %04x\n", epnum, csr); + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); + } +} + + +/* + * Service the default endpoint (ep0) as host. + * Return true until it's time to start the status stage. + */ +static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) +{ + bool more = false; + u8 *fifo_dest = NULL; + u16 fifo_count = 0; + struct musb_hw_ep *hw_ep = musb->control_ep; + struct musb_qh *qh = hw_ep->in_qh; + struct usb_ctrlrequest *request; + + switch (musb->ep0_stage) { + case MUSB_EP0_IN: + fifo_dest = urb->transfer_buffer + urb->actual_length; + fifo_count = min_t(size_t, len, urb->transfer_buffer_length - + urb->actual_length); + if (fifo_count < len) + urb->status = -EOVERFLOW; + + musb_read_fifo(hw_ep, fifo_count, fifo_dest); + + urb->actual_length += fifo_count; + if (len < qh->maxpacket) { + /* always terminate on short read; it's + * rarely reported as an error. + */ + } else if (urb->actual_length < + urb->transfer_buffer_length) + more = true; + break; + case MUSB_EP0_START: + request = (struct usb_ctrlrequest *) urb->setup_packet; + + if (!request->wLength) { + dev_dbg(musb->controller, "start no-DATA\n"); + break; + } else if (request->bRequestType & USB_DIR_IN) { + dev_dbg(musb->controller, "start IN-DATA\n"); + musb->ep0_stage = MUSB_EP0_IN; + more = true; + break; + } else { + dev_dbg(musb->controller, "start OUT-DATA\n"); + musb->ep0_stage = MUSB_EP0_OUT; + more = true; + } + /* FALLTHROUGH */ + case MUSB_EP0_OUT: + fifo_count = min_t(size_t, qh->maxpacket, + urb->transfer_buffer_length - + urb->actual_length); + if (fifo_count) { + fifo_dest = (u8 *) (urb->transfer_buffer + + urb->actual_length); + dev_dbg(musb->controller, "Sending %d byte%s to ep0 fifo %p\n", + fifo_count, + (fifo_count == 1) ? "" : "s", + fifo_dest); + musb_write_fifo(hw_ep, fifo_count, fifo_dest); + + urb->actual_length += fifo_count; + more = true; + } + break; + default: + ERR("bogus ep0 stage %d\n", musb->ep0_stage); + break; + } + + return more; +} + +/* + * Handle default endpoint interrupt as host. Only called in IRQ time + * from musb_interrupt(). + * + * called with controller irqlocked + */ +irqreturn_t musb_h_ep0_irq(struct musb *musb) +{ + struct urb *urb; + u16 csr, len; + int status = 0; + void __iomem *mbase = musb->mregs; + struct musb_hw_ep *hw_ep = musb->control_ep; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh = hw_ep->in_qh; + bool complete = false; + irqreturn_t retval = IRQ_NONE; + + /* ep0 only has one queue, "in" */ + urb = next_urb(qh); + + musb_ep_select(mbase, 0); + csr = musb_readw(epio, MUSB_CSR0); + len = (csr & MUSB_CSR0_RXPKTRDY) + ? musb_readb(epio, MUSB_COUNT0) + : 0; + + dev_dbg(musb->controller, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d\n", + csr, qh, len, urb, musb->ep0_stage); + + /* if we just did status stage, we are done */ + if (MUSB_EP0_STATUS == musb->ep0_stage) { + retval = IRQ_HANDLED; + complete = true; + } + + /* prepare status */ + if (csr & MUSB_CSR0_H_RXSTALL) { + dev_dbg(musb->controller, "STALLING ENDPOINT\n"); + status = -EPIPE; + + } else if (csr & MUSB_CSR0_H_ERROR) { + dev_dbg(musb->controller, "no response, csr0 %04x\n", csr); + status = -EPROTO; + + } else if (csr & MUSB_CSR0_H_NAKTIMEOUT) { + dev_dbg(musb->controller, "control NAK timeout\n"); + + /* NOTE: this code path would be a good place to PAUSE a + * control transfer, if another one is queued, so that + * ep0 is more likely to stay busy. That's already done + * for bulk RX transfers. + * + * if (qh->ring.next != &musb->control), then + * we have a candidate... NAKing is *NOT* an error + */ + musb_writew(epio, MUSB_CSR0, 0); + retval = IRQ_HANDLED; + } + + if (status) { + dev_dbg(musb->controller, "aborting\n"); + retval = IRQ_HANDLED; + if (urb) + urb->status = status; + complete = true; + + /* use the proper sequence to abort the transfer */ + if (csr & MUSB_CSR0_H_REQPKT) { + csr &= ~MUSB_CSR0_H_REQPKT; + musb_writew(epio, MUSB_CSR0, csr); + csr &= ~MUSB_CSR0_H_NAKTIMEOUT; + musb_writew(epio, MUSB_CSR0, csr); + } else { + musb_h_ep0_flush_fifo(hw_ep); + } + + musb_writeb(epio, MUSB_NAKLIMIT0, 0); + + /* clear it */ + musb_writew(epio, MUSB_CSR0, 0); + } + + if (unlikely(!urb)) { + /* stop endpoint since we have no place for its data, this + * SHOULD NEVER HAPPEN! */ + ERR("no URB for end 0\n"); + + musb_h_ep0_flush_fifo(hw_ep); + goto done; + } + + if (!complete) { + /* call common logic and prepare response */ + if (musb_h_ep0_continue(musb, len, urb)) { + /* more packets required */ + csr = (MUSB_EP0_IN == musb->ep0_stage) + ? MUSB_CSR0_H_REQPKT : MUSB_CSR0_TXPKTRDY; + } else { + /* data transfer complete; perform status phase */ + if (usb_pipeout(urb->pipe) + || !urb->transfer_buffer_length) + csr = MUSB_CSR0_H_STATUSPKT + | MUSB_CSR0_H_REQPKT; + else + csr = MUSB_CSR0_H_STATUSPKT + | MUSB_CSR0_TXPKTRDY; + + /* flag status stage */ + musb->ep0_stage = MUSB_EP0_STATUS; + + dev_dbg(musb->controller, "ep0 STATUS, csr %04x\n", csr); + + } + musb_writew(epio, MUSB_CSR0, csr); + retval = IRQ_HANDLED; + } else + musb->ep0_stage = MUSB_EP0_IDLE; + + /* call completion handler if done */ + if (complete) + musb_advance_schedule(musb, urb, hw_ep, 1); +done: + return retval; +} + + +#ifdef CONFIG_USB_INVENTRA_DMA + +/* Host side TX (OUT) using Mentor DMA works as follows: + submit_urb -> + - if queue was empty, Program Endpoint + - ... which starts DMA to fifo in mode 1 or 0 + + DMA Isr (transfer complete) -> TxAvail() + - Stop DMA (~DmaEnab) (<--- Alert ... currently happens + only in musb_cleanup_urb) + - TxPktRdy has to be set in mode 0 or for + short packets in mode 1. +*/ + +#endif + +/* Service a Tx-Available or dma completion irq for the endpoint */ +void musb_host_tx(struct musb *musb, u8 epnum) +{ + int pipe; + bool done = false; + u16 tx_csr; + size_t length = 0; + size_t offset = 0; + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh = hw_ep->out_qh; + struct urb *urb = next_urb(qh); + u32 status = 0; + void __iomem *mbase = musb->mregs; + struct dma_channel *dma; + bool transfer_pending = false; + + musb_ep_select(mbase, epnum); + tx_csr = musb_readw(epio, MUSB_TXCSR); + + /* with CPPI, DMA sometimes triggers "extra" irqs */ + if (!urb) { + dev_dbg(musb->controller, "extra TX%d ready, csr %04x\n", epnum, tx_csr); + return; + } + + pipe = urb->pipe; + dma = is_dma_capable() ? hw_ep->tx_channel : NULL; + dev_dbg(musb->controller, "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr, + dma ? ", dma" : ""); + + /* check for errors */ + if (tx_csr & MUSB_TXCSR_H_RXSTALL) { + /* dma was disabled, fifo flushed */ + dev_dbg(musb->controller, "TX end %d stall\n", epnum); + + /* stall; record URB status */ + status = -EPIPE; + + } else if (tx_csr & MUSB_TXCSR_H_ERROR) { + /* (NON-ISO) dma was disabled, fifo flushed */ + dev_dbg(musb->controller, "TX 3strikes on ep=%d\n", epnum); + + status = -ETIMEDOUT; + + } else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) { + dev_dbg(musb->controller, "TX end=%d device not responding\n", epnum); + + /* NOTE: this code path would be a good place to PAUSE a + * transfer, if there's some other (nonperiodic) tx urb + * that could use this fifo. (dma complicates it...) + * That's already done for bulk RX transfers. + * + * if (bulk && qh->ring.next != &musb->out_bulk), then + * we have a candidate... NAKing is *NOT* an error + */ + musb_ep_select(mbase, epnum); + musb_writew(epio, MUSB_TXCSR, + MUSB_TXCSR_H_WZC_BITS + | MUSB_TXCSR_TXPKTRDY); + return; + } + + if (status) { + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + (void) musb->dma_controller->channel_abort(dma); + } + + /* do the proper sequence to abort the transfer in the + * usb core; the dma engine should already be stopped. + */ + musb_h_tx_flush_fifo(hw_ep); + tx_csr &= ~(MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_H_ERROR + | MUSB_TXCSR_H_RXSTALL + | MUSB_TXCSR_H_NAKTIMEOUT + ); + + musb_ep_select(mbase, epnum); + musb_writew(epio, MUSB_TXCSR, tx_csr); + /* REVISIT may need to clear FLUSHFIFO ... */ + musb_writew(epio, MUSB_TXCSR, tx_csr); + musb_writeb(epio, MUSB_TXINTERVAL, 0); + + done = true; + } + + /* second cppi case */ + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dev_dbg(musb->controller, "extra TX%d ready, csr %04x\n", epnum, tx_csr); + return; + } + + if (is_dma_capable() && dma && !status) { + /* + * DMA has completed. But if we're using DMA mode 1 (multi + * packet DMA), we need a terminal TXPKTRDY interrupt before + * we can consider this transfer completed, lest we trash + * its last packet when writing the next URB's data. So we + * switch back to mode 0 to get that interrupt; we'll come + * back here once it happens. + */ + if (tx_csr & MUSB_TXCSR_DMAMODE) { + /* + * We shouldn't clear DMAMODE with DMAENAB set; so + * clear them in a safe order. That should be OK + * once TXPKTRDY has been set (and I've never seen + * it being 0 at this moment -- DMA interrupt latency + * is significant) but if it hasn't been then we have + * no choice but to stop being polite and ignore the + * programmer's guide... :-) + * + * Note that we must write TXCSR with TXPKTRDY cleared + * in order not to re-trigger the packet send (this bit + * can't be cleared by CPU), and there's another caveat: + * TXPKTRDY may be set shortly and then cleared in the + * double-buffered FIFO mode, so we do an extra TXCSR + * read for debouncing... + */ + tx_csr &= musb_readw(epio, MUSB_TXCSR); + if (tx_csr & MUSB_TXCSR_TXPKTRDY) { + tx_csr &= ~(MUSB_TXCSR_DMAENAB | + MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, + tx_csr | MUSB_TXCSR_H_WZC_BITS); + } + tx_csr &= ~(MUSB_TXCSR_DMAMODE | + MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, + tx_csr | MUSB_TXCSR_H_WZC_BITS); + + /* + * There is no guarantee that we'll get an interrupt + * after clearing DMAMODE as we might have done this + * too late (after TXPKTRDY was cleared by controller). + * Re-read TXCSR as we have spoiled its previous value. + */ + tx_csr = musb_readw(epio, MUSB_TXCSR); + } + + /* + * We may get here from a DMA completion or TXPKTRDY interrupt. + * In any case, we must check the FIFO status here and bail out + * only if the FIFO still has data -- that should prevent the + * "missed" TXPKTRDY interrupts and deal with double-buffered + * FIFO mode too... + */ + if (tx_csr & (MUSB_TXCSR_FIFONOTEMPTY | MUSB_TXCSR_TXPKTRDY)) { + dev_dbg(musb->controller, "DMA complete but packet still in FIFO, " + "CSR %04x\n", tx_csr); + return; + } + } + + if (!status || dma || usb_pipeisoc(pipe)) { + if (dma) + length = dma->actual_len; + else + length = qh->segsize; + qh->offset += length; + + if (usb_pipeisoc(pipe)) { +#ifndef __UBOOT__ + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length = length; + d->status = status; + if (++qh->iso_idx >= urb->number_of_packets) { + done = true; + } else { + d++; + offset = d->offset; + length = d->length; + } +#endif + } else if (dma && urb->transfer_buffer_length == qh->offset) { + done = true; + } else { + /* see if we need to send more data, or ZLP */ + if (qh->segsize < qh->maxpacket) + done = true; + else if (qh->offset == urb->transfer_buffer_length + && !(urb->transfer_flags + & URB_ZERO_PACKET)) + done = true; + if (!done) { + offset = qh->offset; + length = urb->transfer_buffer_length - offset; + transfer_pending = true; + } + } + } + + /* urb->status != -EINPROGRESS means request has been faulted, + * so we must abort this transfer after cleanup + */ + if (urb->status != -EINPROGRESS) { + done = true; + if (status == 0) + status = urb->status; + } + + if (done) { + /* set status */ + urb->status = status; + urb->actual_length = qh->offset; + musb_advance_schedule(musb, urb, hw_ep, USB_DIR_OUT); + return; + } else if ((usb_pipeisoc(pipe) || transfer_pending) && dma) { + if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb, + offset, length)) { + if (is_cppi_enabled() || tusb_dma_omap()) + musb_h_tx_dma_start(hw_ep); + return; + } + } else if (tx_csr & MUSB_TXCSR_DMAENAB) { + dev_dbg(musb->controller, "not complete, but DMA enabled?\n"); + return; + } + + /* + * PIO: start next packet in this URB. + * + * REVISIT: some docs say that when hw_ep->tx_double_buffered, + * (and presumably, FIFO is not half-full) we should write *two* + * packets before updating TXCSR; other docs disagree... + */ + if (length > qh->maxpacket) + length = qh->maxpacket; + /* Unmap the buffer so that CPU can use it */ + usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb); + musb_write_fifo(hw_ep, length, urb->transfer_buffer + offset); + qh->segsize = length; + + musb_ep_select(mbase, epnum); + musb_writew(epio, MUSB_TXCSR, + MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); +} + + +#ifdef CONFIG_USB_INVENTRA_DMA + +/* Host side RX (IN) using Mentor DMA works as follows: + submit_urb -> + - if queue was empty, ProgramEndpoint + - first IN token is sent out (by setting ReqPkt) + LinuxIsr -> RxReady() + /\ => first packet is received + | - Set in mode 0 (DmaEnab, ~ReqPkt) + | -> DMA Isr (transfer complete) -> RxReady() + | - Ack receive (~RxPktRdy), turn off DMA (~DmaEnab) + | - if urb not complete, send next IN token (ReqPkt) + | | else complete urb. + | | + --------------------------- + * + * Nuances of mode 1: + * For short packets, no ack (+RxPktRdy) is sent automatically + * (even if AutoClear is ON) + * For full packets, ack (~RxPktRdy) and next IN token (+ReqPkt) is sent + * automatically => major problem, as collecting the next packet becomes + * difficult. Hence mode 1 is not used. + * + * REVISIT + * All we care about at this driver level is that + * (a) all URBs terminate with REQPKT cleared and fifo(s) empty; + * (b) termination conditions are: short RX, or buffer full; + * (c) fault modes include + * - iff URB_SHORT_NOT_OK, short RX status is -EREMOTEIO. + * (and that endpoint's dma queue stops immediately) + * - overflow (full, PLUS more bytes in the terminal packet) + * + * So for example, usb-storage sets URB_SHORT_NOT_OK, and would + * thus be a great candidate for using mode 1 ... for all but the + * last packet of one URB's transfer. + */ + +#endif + +/* Schedule next QH from musb->in_bulk and move the current qh to + * the end; avoids starvation for other endpoints. + */ +static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep) +{ + struct dma_channel *dma; + struct urb *urb; + void __iomem *mbase = musb->mregs; + void __iomem *epio = ep->regs; + struct musb_qh *cur_qh, *next_qh; + u16 rx_csr; + + musb_ep_select(mbase, ep->epnum); + dma = is_dma_capable() ? ep->rx_channel : NULL; + + /* clear nak timeout bit */ + rx_csr = musb_readw(epio, MUSB_RXCSR); + rx_csr |= MUSB_RXCSR_H_WZC_BITS; + rx_csr &= ~MUSB_RXCSR_DATAERROR; + musb_writew(epio, MUSB_RXCSR, rx_csr); + + cur_qh = first_qh(&musb->in_bulk); + if (cur_qh) { + urb = next_urb(cur_qh); + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + musb->dma_controller->channel_abort(dma); + urb->actual_length += dma->actual_len; + dma->actual_len = 0L; + } + musb_save_toggle(cur_qh, 1, urb); + + /* move cur_qh to end of queue */ + list_move_tail(&cur_qh->ring, &musb->in_bulk); + + /* get the next qh from musb->in_bulk */ + next_qh = first_qh(&musb->in_bulk); + + /* set rx_reinit and schedule the next qh */ + ep->rx_reinit = 1; + musb_start_urb(musb, 1, next_qh); + } +} + +/* + * Service an RX interrupt for the given IN endpoint; docs cover bulk, iso, + * and high-bandwidth IN transfer cases. + */ +void musb_host_rx(struct musb *musb, u8 epnum) +{ + struct urb *urb; + struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + void __iomem *epio = hw_ep->regs; + struct musb_qh *qh = hw_ep->in_qh; + size_t xfer_len; + void __iomem *mbase = musb->mregs; + int pipe; + u16 rx_csr, val; + bool iso_err = false; + bool done = false; + u32 status; + struct dma_channel *dma; + + musb_ep_select(mbase, epnum); + + urb = next_urb(qh); + dma = is_dma_capable() ? hw_ep->rx_channel : NULL; + status = 0; + xfer_len = 0; + + rx_csr = musb_readw(epio, MUSB_RXCSR); + val = rx_csr; + + if (unlikely(!urb)) { + /* REVISIT -- THIS SHOULD NEVER HAPPEN ... but, at least + * usbtest #11 (unlinks) triggers it regularly, sometimes + * with fifo full. (Only with DMA??) + */ + dev_dbg(musb->controller, "BOGUS RX%d ready, csr %04x, count %d\n", epnum, val, + musb_readw(epio, MUSB_RXCOUNT)); + musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); + return; + } + + pipe = urb->pipe; + + dev_dbg(musb->controller, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", + epnum, rx_csr, urb->actual_length, + dma ? dma->actual_len : 0); + + /* check for errors, concurrent stall & unlink is not really + * handled yet! */ + if (rx_csr & MUSB_RXCSR_H_RXSTALL) { + dev_dbg(musb->controller, "RX end %d STALL\n", epnum); + + /* stall; record URB status */ + status = -EPIPE; + + } else if (rx_csr & MUSB_RXCSR_H_ERROR) { + dev_dbg(musb->controller, "end %d RX proto error\n", epnum); + + status = -EPROTO; + musb_writeb(epio, MUSB_RXINTERVAL, 0); + + } else if (rx_csr & MUSB_RXCSR_DATAERROR) { + + if (USB_ENDPOINT_XFER_ISOC != qh->type) { + dev_dbg(musb->controller, "RX end %d NAK timeout\n", epnum); + + /* NOTE: NAKing is *NOT* an error, so we want to + * continue. Except ... if there's a request for + * another QH, use that instead of starving it. + * + * Devices like Ethernet and serial adapters keep + * reads posted at all times, which will starve + * other devices without this logic. + */ + if (usb_pipebulk(urb->pipe) + && qh->mux == 1 + && !list_is_singular(&musb->in_bulk)) { + musb_bulk_rx_nak_timeout(musb, hw_ep); + return; + } + musb_ep_select(mbase, epnum); + rx_csr |= MUSB_RXCSR_H_WZC_BITS; + rx_csr &= ~MUSB_RXCSR_DATAERROR; + musb_writew(epio, MUSB_RXCSR, rx_csr); + + goto finish; + } else { + dev_dbg(musb->controller, "RX end %d ISO data error\n", epnum); + /* packet error reported later */ + iso_err = true; + } + } else if (rx_csr & MUSB_RXCSR_INCOMPRX) { + dev_dbg(musb->controller, "end %d high bandwidth incomplete ISO packet RX\n", + epnum); + status = -EPROTO; + } + + /* faults abort the transfer */ + if (status) { + /* clean up dma and collect transfer count */ + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + (void) musb->dma_controller->channel_abort(dma); + xfer_len = dma->actual_len; + } + musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); + musb_writeb(epio, MUSB_RXINTERVAL, 0); + done = true; + goto finish; + } + + if (unlikely(dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY)) { + /* SHOULD NEVER HAPPEN ... but at least DaVinci has done it */ + ERR("RX%d dma busy, csr %04x\n", epnum, rx_csr); + goto finish; + } + + /* thorough shutdown for now ... given more precise fault handling + * and better queueing support, we might keep a DMA pipeline going + * while processing this irq for earlier completions. + */ + + /* FIXME this is _way_ too much in-line logic for Mentor DMA */ + +#ifndef CONFIG_USB_INVENTRA_DMA + if (rx_csr & MUSB_RXCSR_H_REQPKT) { + /* REVISIT this happened for a while on some short reads... + * the cleanup still needs investigation... looks bad... + * and also duplicates dma cleanup code above ... plus, + * shouldn't this be the "half full" double buffer case? + */ + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + (void) musb->dma_controller->channel_abort(dma); + xfer_len = dma->actual_len; + done = true; + } + + dev_dbg(musb->controller, "RXCSR%d %04x, reqpkt, len %zu%s\n", epnum, rx_csr, + xfer_len, dma ? ", dma" : ""); + rx_csr &= ~MUSB_RXCSR_H_REQPKT; + + musb_ep_select(mbase, epnum); + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS | rx_csr); + } +#endif + if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) { + xfer_len = dma->actual_len; + + val &= ~(MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR + | MUSB_RXCSR_RXPKTRDY); + musb_writew(hw_ep->regs, MUSB_RXCSR, val); + +#ifdef CONFIG_USB_INVENTRA_DMA + if (usb_pipeisoc(pipe)) { + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length = xfer_len; + + /* even if there was an error, we did the dma + * for iso_frame_desc->length + */ + if (d->status != -EILSEQ && d->status != -EOVERFLOW) + d->status = 0; + + if (++qh->iso_idx >= urb->number_of_packets) + done = true; + else + done = false; + + } else { + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + xfer_len >= + urb->transfer_buffer_length + || dma->actual_len < qh->maxpacket); + } + + /* send IN token for next packet, without AUTOREQ */ + if (!done) { + val |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS | val); + } + + dev_dbg(musb->controller, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, + done ? "off" : "reset", + musb_readw(epio, MUSB_RXCSR), + musb_readw(epio, MUSB_RXCOUNT)); +#else + done = true; +#endif + } else if (urb->status == -EINPROGRESS) { + /* if no errors, be sure a packet is ready for unloading */ + if (unlikely(!(rx_csr & MUSB_RXCSR_RXPKTRDY))) { + status = -EPROTO; + ERR("Rx interrupt with no errors or packet!\n"); + + /* FIXME this is another "SHOULD NEVER HAPPEN" */ + +/* SCRUB (RX) */ + /* do the proper sequence to abort the transfer */ + musb_ep_select(mbase, epnum); + val &= ~MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, val); + goto finish; + } + + /* we are expecting IN packets */ +#ifdef CONFIG_USB_INVENTRA_DMA + if (dma) { + struct dma_controller *c; + u16 rx_count; + int ret, length; + dma_addr_t buf; + + rx_count = musb_readw(epio, MUSB_RXCOUNT); + + dev_dbg(musb->controller, "RX%d count %d, buffer 0x%x len %d/%d\n", + epnum, rx_count, + urb->transfer_dma + + urb->actual_length, + qh->offset, + urb->transfer_buffer_length); + + c = musb->dma_controller; + + if (usb_pipeisoc(pipe)) { + int d_status = 0; + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + + if (iso_err) { + d_status = -EILSEQ; + urb->error_count++; + } + if (rx_count > d->length) { + if (d_status == 0) { + d_status = -EOVERFLOW; + urb->error_count++; + } + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",\ + rx_count, d->length); + + length = d->length; + } else + length = rx_count; + d->status = d_status; + buf = urb->transfer_dma + d->offset; + } else { + length = rx_count; + buf = urb->transfer_dma + + urb->actual_length; + } + + dma->desired_mode = 0; +#ifdef USE_MODE1 + /* because of the issue below, mode 1 will + * only rarely behave with correct semantics. + */ + if ((urb->transfer_flags & + URB_SHORT_NOT_OK) + && (urb->transfer_buffer_length - + urb->actual_length) + > qh->maxpacket) + dma->desired_mode = 1; + if (rx_count < hw_ep->max_packet_sz_rx) { + length = rx_count; + dma->desired_mode = 0; + } else { + length = urb->transfer_buffer_length; + } +#endif + +/* Disadvantage of using mode 1: + * It's basically usable only for mass storage class; essentially all + * other protocols also terminate transfers on short packets. + * + * Details: + * An extra IN token is sent at the end of the transfer (due to AUTOREQ) + * If you try to use mode 1 for (transfer_buffer_length - 512), and try + * to use the extra IN token to grab the last packet using mode 0, then + * the problem is that you cannot be sure when the device will send the + * last packet and RxPktRdy set. Sometimes the packet is recd too soon + * such that it gets lost when RxCSR is re-set at the end of the mode 1 + * transfer, while sometimes it is recd just a little late so that if you + * try to configure for mode 0 soon after the mode 1 transfer is + * completed, you will find rxcount 0. Okay, so you might think why not + * wait for an interrupt when the pkt is recd. Well, you won't get any! + */ + + val = musb_readw(epio, MUSB_RXCSR); + val &= ~MUSB_RXCSR_H_REQPKT; + + if (dma->desired_mode == 0) + val &= ~MUSB_RXCSR_H_AUTOREQ; + else + val |= MUSB_RXCSR_H_AUTOREQ; + val |= MUSB_RXCSR_DMAENAB; + + /* autoclear shouldn't be set in high bandwidth */ + if (qh->hb_mult == 1) + val |= MUSB_RXCSR_AUTOCLEAR; + + musb_writew(epio, MUSB_RXCSR, + MUSB_RXCSR_H_WZC_BITS | val); + + /* REVISIT if when actual_length != 0, + * transfer_buffer_length needs to be + * adjusted first... + */ + ret = c->channel_program( + dma, qh->maxpacket, + dma->desired_mode, buf, length); + + if (!ret) { + c->channel_release(dma); + hw_ep->rx_channel = NULL; + dma = NULL; + val = musb_readw(epio, MUSB_RXCSR); + val &= ~(MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR); + musb_writew(epio, MUSB_RXCSR, val); + } + } +#endif /* Mentor DMA */ + + if (!dma) { + /* Unmap the buffer so that CPU can use it */ + usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb); + done = musb_host_packet_rx(musb, urb, + epnum, iso_err); + dev_dbg(musb->controller, "read %spacket\n", done ? "last " : ""); + } + } + +finish: + urb->actual_length += xfer_len; + qh->offset += xfer_len; + if (done) { + if (urb->status == -EINPROGRESS) + urb->status = status; + musb_advance_schedule(musb, urb, hw_ep, USB_DIR_IN); + } +} + +/* schedule nodes correspond to peripheral endpoints, like an OHCI QH. + * the software schedule associates multiple such nodes with a given + * host side hardware endpoint + direction; scheduling may activate + * that hardware endpoint. + */ +static int musb_schedule( + struct musb *musb, + struct musb_qh *qh, + int is_in) +{ + int idle; + int best_diff; + int best_end, epnum; + struct musb_hw_ep *hw_ep = NULL; + struct list_head *head = NULL; + u8 toggle; + u8 txtype; + struct urb *urb = next_urb(qh); + + /* use fixed hardware for control and bulk */ + if (qh->type == USB_ENDPOINT_XFER_CONTROL) { + head = &musb->control; + hw_ep = musb->control_ep; + goto success; + } + + /* else, periodic transfers get muxed to other endpoints */ + + /* + * We know this qh hasn't been scheduled, so all we need to do + * is choose which hardware endpoint to put it on ... + * + * REVISIT what we really want here is a regular schedule tree + * like e.g. OHCI uses. + */ + best_diff = 4096; + best_end = -1; + + for (epnum = 1, hw_ep = musb->endpoints + 1; + epnum < musb->nr_endpoints; + epnum++, hw_ep++) { + int diff; + + if (musb_ep_get_qh(hw_ep, is_in) != NULL) + continue; + + if (hw_ep == musb->bulk_ep) + continue; + + if (is_in) + diff = hw_ep->max_packet_sz_rx; + else + diff = hw_ep->max_packet_sz_tx; + diff -= (qh->maxpacket * qh->hb_mult); + + if (diff >= 0 && best_diff > diff) { + + /* + * Mentor controller has a bug in that if we schedule + * a BULK Tx transfer on an endpoint that had earlier + * handled ISOC then the BULK transfer has to start on + * a zero toggle. If the BULK transfer starts on a 1 + * toggle then this transfer will fail as the mentor + * controller starts the Bulk transfer on a 0 toggle + * irrespective of the programming of the toggle bits + * in the TXCSR register. Check for this condition + * while allocating the EP for a Tx Bulk transfer. If + * so skip this EP. + */ + hw_ep = musb->endpoints + epnum; + toggle = usb_gettoggle(urb->dev, qh->epnum, !is_in); + txtype = (musb_readb(hw_ep->regs, MUSB_TXTYPE) + >> 4) & 0x3; + if (!is_in && (qh->type == USB_ENDPOINT_XFER_BULK) && + toggle && (txtype == USB_ENDPOINT_XFER_ISOC)) + continue; + + best_diff = diff; + best_end = epnum; + } + } + /* use bulk reserved ep1 if no other ep is free */ + if (best_end < 0 && qh->type == USB_ENDPOINT_XFER_BULK) { + hw_ep = musb->bulk_ep; + if (is_in) + head = &musb->in_bulk; + else + head = &musb->out_bulk; + + /* Enable bulk RX NAK timeout scheme when bulk requests are + * multiplexed. This scheme doen't work in high speed to full + * speed scenario as NAK interrupts are not coming from a + * full speed device connected to a high speed device. + * NAK timeout interval is 8 (128 uframe or 16ms) for HS and + * 4 (8 frame or 8ms) for FS device. + */ + if (is_in && qh->dev) + qh->intv_reg = + (USB_SPEED_HIGH == qh->dev->speed) ? 8 : 4; + goto success; + } else if (best_end < 0) { + return -ENOSPC; + } + + idle = 1; + qh->mux = 0; + hw_ep = musb->endpoints + best_end; + dev_dbg(musb->controller, "qh %p periodic slot %d\n", qh, best_end); +success: + if (head) { + idle = list_empty(head); + list_add_tail(&qh->ring, head); + qh->mux = 1; + } + qh->hw_ep = hw_ep; + qh->hep->hcpriv = qh; + if (idle) + musb_start_urb(musb, is_in, qh); + return 0; +} + +#ifdef __UBOOT__ +/* check if transaction translator is needed for device */ +static int tt_needed(struct musb *musb, struct usb_device *dev) +{ + if ((musb_readb(musb->mregs, MUSB_POWER) & MUSB_POWER_HSMODE) && + (dev->speed < USB_SPEED_HIGH)) + return 1; + return 0; +} +#endif + +#ifndef __UBOOT__ +static int musb_urb_enqueue( +#else +int musb_urb_enqueue( +#endif + struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + unsigned long flags; + struct musb *musb = hcd_to_musb(hcd); + struct usb_host_endpoint *hep = urb->ep; + struct musb_qh *qh; + struct usb_endpoint_descriptor *epd = &hep->desc; + int ret; + unsigned type_reg; + unsigned interval; + + /* host role must be active */ + if (!is_host_active(musb) || !musb->is_active) + return -ENODEV; + + spin_lock_irqsave(&musb->lock, flags); + ret = usb_hcd_link_urb_to_ep(hcd, urb); + qh = ret ? NULL : hep->hcpriv; + if (qh) + urb->hcpriv = qh; + spin_unlock_irqrestore(&musb->lock, flags); + + /* DMA mapping was already done, if needed, and this urb is on + * hep->urb_list now ... so we're done, unless hep wasn't yet + * scheduled onto a live qh. + * + * REVISIT best to keep hep->hcpriv valid until the endpoint gets + * disabled, testing for empty qh->ring and avoiding qh setup costs + * except for the first urb queued after a config change. + */ + if (qh || ret) + return ret; + + /* Allocate and initialize qh, minimizing the work done each time + * hw_ep gets reprogrammed, or with irqs blocked. Then schedule it. + * + * REVISIT consider a dedicated qh kmem_cache, so it's harder + * for bugs in other kernel code to break this driver... + */ + qh = kzalloc(sizeof *qh, mem_flags); + if (!qh) { + spin_lock_irqsave(&musb->lock, flags); + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&musb->lock, flags); + return -ENOMEM; + } + + qh->hep = hep; + qh->dev = urb->dev; + INIT_LIST_HEAD(&qh->ring); + qh->is_ready = 1; + + qh->maxpacket = usb_endpoint_maxp(epd); + qh->type = usb_endpoint_type(epd); + + /* Bits 11 & 12 of wMaxPacketSize encode high bandwidth multiplier. + * Some musb cores don't support high bandwidth ISO transfers; and + * we don't (yet!) support high bandwidth interrupt transfers. + */ + qh->hb_mult = 1 + ((qh->maxpacket >> 11) & 0x03); + if (qh->hb_mult > 1) { + int ok = (qh->type == USB_ENDPOINT_XFER_ISOC); + + if (ok) + ok = (usb_pipein(urb->pipe) && musb->hb_iso_rx) + || (usb_pipeout(urb->pipe) && musb->hb_iso_tx); + if (!ok) { + ret = -EMSGSIZE; + goto done; + } + qh->maxpacket &= 0x7ff; + } + + qh->epnum = usb_endpoint_num(epd); + + /* NOTE: urb->dev->devnum is wrong during SET_ADDRESS */ + qh->addr_reg = (u8) usb_pipedevice(urb->pipe); + + /* precompute rxtype/txtype/type0 register */ + type_reg = (qh->type << 4) | qh->epnum; + switch (urb->dev->speed) { + case USB_SPEED_LOW: + type_reg |= 0xc0; + break; + case USB_SPEED_FULL: + type_reg |= 0x80; + break; + default: + type_reg |= 0x40; + } + qh->type_reg = type_reg; + + /* Precompute RXINTERVAL/TXINTERVAL register */ + switch (qh->type) { + case USB_ENDPOINT_XFER_INT: + /* + * Full/low speeds use the linear encoding, + * high speed uses the logarithmic encoding. + */ + if (urb->dev->speed <= USB_SPEED_FULL) { + interval = max_t(u8, epd->bInterval, 1); + break; + } + /* FALLTHROUGH */ + case USB_ENDPOINT_XFER_ISOC: + /* ISO always uses logarithmic encoding */ + interval = min_t(u8, epd->bInterval, 16); + break; + default: + /* REVISIT we actually want to use NAK limits, hinting to the + * transfer scheduling logic to try some other qh, e.g. try + * for 2 msec first: + * + * interval = (USB_SPEED_HIGH == urb->dev->speed) ? 16 : 2; + * + * The downside of disabling this is that transfer scheduling + * gets VERY unfair for nonperiodic transfers; a misbehaving + * peripheral could make that hurt. That's perfectly normal + * for reads from network or serial adapters ... so we have + * partial NAKlimit support for bulk RX. + * + * The upside of disabling it is simpler transfer scheduling. + */ + interval = 0; + } + qh->intv_reg = interval; + + /* precompute addressing for external hub/tt ports */ + if (musb->is_multipoint) { + struct usb_device *parent = urb->dev->parent; + +#ifndef __UBOOT__ + if (parent != hcd->self.root_hub) { +#else + if (parent) { +#endif + qh->h_addr_reg = (u8) parent->devnum; + +#ifndef __UBOOT__ + /* set up tt info if needed */ + if (urb->dev->tt) { + qh->h_port_reg = (u8) urb->dev->ttport; + if (urb->dev->tt->hub) + qh->h_addr_reg = + (u8) urb->dev->tt->hub->devnum; + if (urb->dev->tt->multi) + qh->h_addr_reg |= 0x80; + } +#else + if (tt_needed(musb, urb->dev)) { + u16 hub_port = find_tt(urb->dev); + qh->h_addr_reg = (u8) (hub_port >> 8); + qh->h_port_reg = (u8) (hub_port & 0xff); + } +#endif + } + } + + /* invariant: hep->hcpriv is null OR the qh that's already scheduled. + * until we get real dma queues (with an entry for each urb/buffer), + * we only have work to do in the former case. + */ + spin_lock_irqsave(&musb->lock, flags); + if (hep->hcpriv) { + /* some concurrent activity submitted another urb to hep... + * odd, rare, error prone, but legal. + */ + kfree(qh); + qh = NULL; + ret = 0; + } else + ret = musb_schedule(musb, qh, + epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK); + + if (ret == 0) { + urb->hcpriv = qh; + /* FIXME set urb->start_frame for iso/intr, it's tested in + * musb_start_urb(), but otherwise only konicawc cares ... + */ + } + spin_unlock_irqrestore(&musb->lock, flags); + +done: + if (ret != 0) { + spin_lock_irqsave(&musb->lock, flags); + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&musb->lock, flags); + kfree(qh); + } + return ret; +} + + +#ifndef __UBOOT__ +/* + * abort a transfer that's at the head of a hardware queue. + * called with controller locked, irqs blocked + * that hardware queue advances to the next transfer, unless prevented + */ +static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) +{ + struct musb_hw_ep *ep = qh->hw_ep; + struct musb *musb = ep->musb; + void __iomem *epio = ep->regs; + unsigned hw_end = ep->epnum; + void __iomem *regs = ep->musb->mregs; + int is_in = usb_pipein(urb->pipe); + int status = 0; + u16 csr; + + musb_ep_select(regs, hw_end); + + if (is_dma_capable()) { + struct dma_channel *dma; + + dma = is_in ? ep->rx_channel : ep->tx_channel; + if (dma) { + status = ep->musb->dma_controller->channel_abort(dma); + dev_dbg(musb->controller, + "abort %cX%d DMA for urb %p --> %d\n", + is_in ? 'R' : 'T', ep->epnum, + urb, status); + urb->actual_length += dma->actual_len; + } + } + + /* turn off DMA requests, discard state, stop polling ... */ + if (ep->epnum && is_in) { + /* giveback saves bulk toggle */ + csr = musb_h_flush_rxfifo(ep, 0); + + /* REVISIT we still get an irq; should likely clear the + * endpoint's irq status here to avoid bogus irqs. + * clearing that status is platform-specific... + */ + } else if (ep->epnum) { + musb_h_tx_flush_fifo(ep); + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_AUTOSET + | MUSB_TXCSR_DMAENAB + | MUSB_TXCSR_H_RXSTALL + | MUSB_TXCSR_H_NAKTIMEOUT + | MUSB_TXCSR_H_ERROR + | MUSB_TXCSR_TXPKTRDY); + musb_writew(epio, MUSB_TXCSR, csr); + /* REVISIT may need to clear FLUSHFIFO ... */ + musb_writew(epio, MUSB_TXCSR, csr); + /* flush cpu writebuffer */ + csr = musb_readw(epio, MUSB_TXCSR); + } else { + musb_h_ep0_flush_fifo(ep); + } + if (status == 0) + musb_advance_schedule(ep->musb, urb, ep, is_in); + return status; +} + +static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct musb *musb = hcd_to_musb(hcd); + struct musb_qh *qh; + unsigned long flags; + int is_in = usb_pipein(urb->pipe); + int ret; + + dev_dbg(musb->controller, "urb=%p, dev%d ep%d%s\n", urb, + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + is_in ? "in" : "out"); + + spin_lock_irqsave(&musb->lock, flags); + ret = usb_hcd_check_unlink_urb(hcd, urb, status); + if (ret) + goto done; + + qh = urb->hcpriv; + if (!qh) + goto done; + + /* + * Any URB not actively programmed into endpoint hardware can be + * immediately given back; that's any URB not at the head of an + * endpoint queue, unless someday we get real DMA queues. And even + * if it's at the head, it might not be known to the hardware... + * + * Otherwise abort current transfer, pending DMA, etc.; urb->status + * has already been updated. This is a synchronous abort; it'd be + * OK to hold off until after some IRQ, though. + * + * NOTE: qh is invalid unless !list_empty(&hep->urb_list) + */ + if (!qh->is_ready + || urb->urb_list.prev != &qh->hep->urb_list + || musb_ep_get_qh(qh->hw_ep, is_in) != qh) { + int ready = qh->is_ready; + + qh->is_ready = 0; + musb_giveback(musb, urb, 0); + qh->is_ready = ready; + + /* If nothing else (usually musb_giveback) is using it + * and its URB list has emptied, recycle this qh. + */ + if (ready && list_empty(&qh->hep->urb_list)) { + qh->hep->hcpriv = NULL; + list_del(&qh->ring); + kfree(qh); + } + } else + ret = musb_cleanup_urb(urb, qh); +done: + spin_unlock_irqrestore(&musb->lock, flags); + return ret; +} + +/* disable an endpoint */ +static void +musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) +{ + u8 is_in = hep->desc.bEndpointAddress & USB_DIR_IN; + unsigned long flags; + struct musb *musb = hcd_to_musb(hcd); + struct musb_qh *qh; + struct urb *urb; + + spin_lock_irqsave(&musb->lock, flags); + + qh = hep->hcpriv; + if (qh == NULL) + goto exit; + + /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */ + + /* Kick the first URB off the hardware, if needed */ + qh->is_ready = 0; + if (musb_ep_get_qh(qh->hw_ep, is_in) == qh) { + urb = next_urb(qh); + + /* make software (then hardware) stop ASAP */ + if (!urb->unlinked) + urb->status = -ESHUTDOWN; + + /* cleanup */ + musb_cleanup_urb(urb, qh); + + /* Then nuke all the others ... and advance the + * queue on hw_ep (e.g. bulk ring) when we're done. + */ + while (!list_empty(&hep->urb_list)) { + urb = next_urb(qh); + urb->status = -ESHUTDOWN; + musb_advance_schedule(musb, urb, qh->hw_ep, is_in); + } + } else { + /* Just empty the queue; the hardware is busy with + * other transfers, and since !qh->is_ready nothing + * will activate any of these as it advances. + */ + while (!list_empty(&hep->urb_list)) + musb_giveback(musb, next_urb(qh), -ESHUTDOWN); + + hep->hcpriv = NULL; + list_del(&qh->ring); + kfree(qh); + } +exit: + spin_unlock_irqrestore(&musb->lock, flags); +} + +static int musb_h_get_frame_number(struct usb_hcd *hcd) +{ + struct musb *musb = hcd_to_musb(hcd); + + return musb_readw(musb->mregs, MUSB_FRAME); +} + +static int musb_h_start(struct usb_hcd *hcd) +{ + struct musb *musb = hcd_to_musb(hcd); + + /* NOTE: musb_start() is called when the hub driver turns + * on port power, or when (OTG) peripheral starts. + */ + hcd->state = HC_STATE_RUNNING; + musb->port1_status = 0; + return 0; +} + +static void musb_h_stop(struct usb_hcd *hcd) +{ + musb_stop(hcd_to_musb(hcd)); + hcd->state = HC_STATE_HALT; +} + +static int musb_bus_suspend(struct usb_hcd *hcd) +{ + struct musb *musb = hcd_to_musb(hcd); + u8 devctl; + + if (!is_host_active(musb)) + return 0; + + switch (musb->xceiv->state) { + case OTG_STATE_A_SUSPEND: + return 0; + case OTG_STATE_A_WAIT_VRISE: + /* ID could be grounded even if there's no device + * on the other end of the cable. NOTE that the + * A_WAIT_VRISE timers are messy with MUSB... + */ + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; + break; + default: + break; + } + + if (musb->is_active) { + WARNING("trying to suspend as %s while active\n", + otg_state_string(musb->xceiv->state)); + return -EBUSY; + } else + return 0; +} + +static int musb_bus_resume(struct usb_hcd *hcd) +{ + /* resuming child port does the work */ + return 0; +} + +const struct hc_driver musb_hc_driver = { + .description = "musb-hcd", + .product_desc = "MUSB HDRC host driver", + .hcd_priv_size = sizeof(struct musb), + .flags = HCD_USB2 | HCD_MEMORY, + + /* not using irq handler or reset hooks from usbcore, since + * those must be shared with peripheral code for OTG configs + */ + + .start = musb_h_start, + .stop = musb_h_stop, + + .get_frame_number = musb_h_get_frame_number, + + .urb_enqueue = musb_urb_enqueue, + .urb_dequeue = musb_urb_dequeue, + .endpoint_disable = musb_h_disable, + + .hub_status_data = musb_hub_status_data, + .hub_control = musb_hub_control, + .bus_suspend = musb_bus_suspend, + .bus_resume = musb_bus_resume, + /* .start_port_reset = NULL, */ + /* .hub_irq_enable = NULL, */ +}; +#endif diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_host.h b/qemu/roms/u-boot/drivers/usb/musb-new/musb_host.h new file mode 100644 index 000000000..ebebe0c02 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_host.h @@ -0,0 +1,114 @@ +/* + * MUSB OTG driver host defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#ifndef _MUSB_HOST_H +#define _MUSB_HOST_H +#ifdef __UBOOT__ +#include "usb-compat.h" +#endif + +static inline struct usb_hcd *musb_to_hcd(struct musb *musb) +{ + return container_of((void *) musb, struct usb_hcd, hcd_priv); +} + +static inline struct musb *hcd_to_musb(struct usb_hcd *hcd) +{ + return (struct musb *) (hcd->hcd_priv); +} + +/* stored in "usb_host_endpoint.hcpriv" for scheduled endpoints */ +struct musb_qh { + struct usb_host_endpoint *hep; /* usbcore info */ + struct usb_device *dev; + struct musb_hw_ep *hw_ep; /* current binding */ + + struct list_head ring; /* of musb_qh */ + /* struct musb_qh *next; */ /* for periodic tree */ + u8 mux; /* qh multiplexed to hw_ep */ + + unsigned offset; /* in urb->transfer_buffer */ + unsigned segsize; /* current xfer fragment */ + + u8 type_reg; /* {rx,tx} type register */ + u8 intv_reg; /* {rx,tx} interval register */ + u8 addr_reg; /* device address register */ + u8 h_addr_reg; /* hub address register */ + u8 h_port_reg; /* hub port register */ + + u8 is_ready; /* safe to modify hw_ep */ + u8 type; /* XFERTYPE_* */ + u8 epnum; + u8 hb_mult; /* high bandwidth pkts per uf */ + u16 maxpacket; + u16 frame; /* for periodic schedule */ + unsigned iso_idx; /* in urb->iso_frame_desc[] */ +}; + +/* map from control or bulk queue head to the first qh on that ring */ +static inline struct musb_qh *first_qh(struct list_head *q) +{ + if (list_empty(q)) + return NULL; + return list_entry(q->next, struct musb_qh, ring); +} + + +extern void musb_root_disconnect(struct musb *musb); + +struct usb_hcd; + +extern int musb_hub_status_data(struct usb_hcd *hcd, char *buf); +extern int musb_hub_control(struct usb_hcd *hcd, + u16 typeReq, u16 wValue, u16 wIndex, + char *buf, u16 wLength); + +extern const struct hc_driver musb_hc_driver; + +static inline struct urb *next_urb(struct musb_qh *qh) +{ + struct list_head *queue; + + if (!qh) + return NULL; + queue = &qh->hep->urb_list; + if (list_empty(queue)) + return NULL; + return list_entry(queue->next, struct urb, urb_list); +} + +#ifdef __UBOOT__ +int musb_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); +#endif +#endif /* _MUSB_HOST_H */ diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_io.h b/qemu/roms/u-boot/drivers/usb/musb-new/musb_io.h new file mode 100644 index 000000000..51730aee5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_io.h @@ -0,0 +1,146 @@ +/* + * MUSB OTG driver register I/O + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#ifndef __MUSB_LINUX_PLATFORM_ARCH_H__ +#define __MUSB_LINUX_PLATFORM_ARCH_H__ + +#ifndef __UBOOT__ +#include +#else +#include +#endif + +#if !defined(CONFIG_ARM) && !defined(CONFIG_SUPERH) \ + && !defined(CONFIG_AVR32) && !defined(CONFIG_PPC32) \ + && !defined(CONFIG_PPC64) && !defined(CONFIG_BLACKFIN) \ + && !defined(CONFIG_MIPS) && !defined(CONFIG_M68K) +static inline void readsl(const void __iomem *addr, void *buf, int len) + { insl((unsigned long)addr, buf, len); } +static inline void readsw(const void __iomem *addr, void *buf, int len) + { insw((unsigned long)addr, buf, len); } +static inline void readsb(const void __iomem *addr, void *buf, int len) + { insb((unsigned long)addr, buf, len); } + +static inline void writesl(const void __iomem *addr, const void *buf, int len) + { outsl((unsigned long)addr, buf, len); } +static inline void writesw(const void __iomem *addr, const void *buf, int len) + { outsw((unsigned long)addr, buf, len); } +static inline void writesb(const void __iomem *addr, const void *buf, int len) + { outsb((unsigned long)addr, buf, len); } + +#endif + +#ifndef CONFIG_BLACKFIN + +/* NOTE: these offsets are all in bytes */ + +static inline u16 musb_readw(const void __iomem *addr, unsigned offset) + { return __raw_readw(addr + offset); } + +static inline u32 musb_readl(const void __iomem *addr, unsigned offset) + { return __raw_readl(addr + offset); } + + +static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data) + { __raw_writew(data, addr + offset); } + +static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data) + { __raw_writel(data, addr + offset); } + + +#if defined(CONFIG_USB_MUSB_TUSB6010) || defined (CONFIG_USB_MUSB_TUSB6010_MODULE) + +/* + * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum. + */ +static inline u8 musb_readb(const void __iomem *addr, unsigned offset) +{ + u16 tmp; + u8 val; + + tmp = __raw_readw(addr + (offset & ~1)); + if (offset & 1) + val = (tmp >> 8); + else + val = tmp & 0xff; + + return val; +} + +static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) +{ + u16 tmp; + + tmp = __raw_readw(addr + (offset & ~1)); + if (offset & 1) + tmp = (data << 8) | (tmp & 0xff); + else + tmp = (tmp & 0xff00) | data; + + __raw_writew(tmp, addr + (offset & ~1)); +} + +#else + +static inline u8 musb_readb(const void __iomem *addr, unsigned offset) + { return __raw_readb(addr + offset); } + +static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) + { __raw_writeb(data, addr + offset); } + +#endif /* CONFIG_USB_MUSB_TUSB6010 */ + +#else + +static inline u8 musb_readb(const void __iomem *addr, unsigned offset) + { return (u8) (bfin_read16(addr + offset)); } + +static inline u16 musb_readw(const void __iomem *addr, unsigned offset) + { return bfin_read16(addr + offset); } + +static inline u32 musb_readl(const void __iomem *addr, unsigned offset) + { return (u32) (bfin_read16(addr + offset)); } + +static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) + { bfin_write16(addr + offset, (u16) data); } + +static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data) + { bfin_write16(addr + offset, data); } + +static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data) + { bfin_write16(addr + offset, (u16) data); } + +#endif /* CONFIG_BLACKFIN */ + +#endif diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_regs.h b/qemu/roms/u-boot/drivers/usb/musb-new/musb_regs.h new file mode 100644 index 000000000..03f2655af --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_regs.h @@ -0,0 +1,645 @@ +/* + * MUSB OTG driver register defines + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * Copyright (C) 2006-2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * 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. + * + */ + +#ifndef __MUSB_REGS_H__ +#define __MUSB_REGS_H__ + +#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ + +/* + * MUSB Register bits + */ + +/* POWER */ +#define MUSB_POWER_ISOUPDATE 0x80 +#define MUSB_POWER_SOFTCONN 0x40 +#define MUSB_POWER_HSENAB 0x20 +#define MUSB_POWER_HSMODE 0x10 +#define MUSB_POWER_RESET 0x08 +#define MUSB_POWER_RESUME 0x04 +#define MUSB_POWER_SUSPENDM 0x02 +#define MUSB_POWER_ENSUSPEND 0x01 + +/* INTRUSB */ +#define MUSB_INTR_SUSPEND 0x01 +#define MUSB_INTR_RESUME 0x02 +#define MUSB_INTR_RESET 0x04 +#define MUSB_INTR_BABBLE 0x04 +#define MUSB_INTR_SOF 0x08 +#define MUSB_INTR_CONNECT 0x10 +#define MUSB_INTR_DISCONNECT 0x20 +#define MUSB_INTR_SESSREQ 0x40 +#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */ + +/* DEVCTL */ +#define MUSB_DEVCTL_BDEVICE 0x80 +#define MUSB_DEVCTL_FSDEV 0x40 +#define MUSB_DEVCTL_LSDEV 0x20 +#define MUSB_DEVCTL_VBUS 0x18 +#define MUSB_DEVCTL_VBUS_SHIFT 3 +#define MUSB_DEVCTL_HM 0x04 +#define MUSB_DEVCTL_HR 0x02 +#define MUSB_DEVCTL_SESSION 0x01 + +/* MUSB ULPI VBUSCONTROL */ +#define MUSB_ULPI_USE_EXTVBUS 0x01 +#define MUSB_ULPI_USE_EXTVBUSIND 0x02 +/* ULPI_REG_CONTROL */ +#define MUSB_ULPI_REG_REQ (1 << 0) +#define MUSB_ULPI_REG_CMPLT (1 << 1) +#define MUSB_ULPI_RDN_WR (1 << 2) + +/* TESTMODE */ +#define MUSB_TEST_FORCE_HOST 0x80 +#define MUSB_TEST_FIFO_ACCESS 0x40 +#define MUSB_TEST_FORCE_FS 0x20 +#define MUSB_TEST_FORCE_HS 0x10 +#define MUSB_TEST_PACKET 0x08 +#define MUSB_TEST_K 0x04 +#define MUSB_TEST_J 0x02 +#define MUSB_TEST_SE0_NAK 0x01 + +/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */ +#define MUSB_FIFOSZ_DPB 0x10 +/* Allocation size (8, 16, 32, ... 4096) */ +#define MUSB_FIFOSZ_SIZE 0x0f + +/* CSR0 */ +#define MUSB_CSR0_FLUSHFIFO 0x0100 +#define MUSB_CSR0_TXPKTRDY 0x0002 +#define MUSB_CSR0_RXPKTRDY 0x0001 + +/* CSR0 in Peripheral mode */ +#define MUSB_CSR0_P_SVDSETUPEND 0x0080 +#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040 +#define MUSB_CSR0_P_SENDSTALL 0x0020 +#define MUSB_CSR0_P_SETUPEND 0x0010 +#define MUSB_CSR0_P_DATAEND 0x0008 +#define MUSB_CSR0_P_SENTSTALL 0x0004 + +/* CSR0 in Host mode */ +#define MUSB_CSR0_H_DIS_PING 0x0800 +#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */ +#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */ +#define MUSB_CSR0_H_NAKTIMEOUT 0x0080 +#define MUSB_CSR0_H_STATUSPKT 0x0040 +#define MUSB_CSR0_H_REQPKT 0x0020 +#define MUSB_CSR0_H_ERROR 0x0010 +#define MUSB_CSR0_H_SETUPPKT 0x0008 +#define MUSB_CSR0_H_RXSTALL 0x0004 + +/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_CSR0_P_WZC_BITS \ + (MUSB_CSR0_P_SENTSTALL) +#define MUSB_CSR0_H_WZC_BITS \ + (MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \ + | MUSB_CSR0_RXPKTRDY) + +/* TxType/RxType */ +#define MUSB_TYPE_SPEED 0xc0 +#define MUSB_TYPE_SPEED_SHIFT 6 +#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_SHIFT 4 +#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */ + +/* CONFIGDATA */ +#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */ +#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */ +#define MUSB_CONFIGDATA_BIGENDIAN 0x20 +#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ +#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ +#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */ +#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ +#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */ + +/* TXCSR in Peripheral and Host mode */ +#define MUSB_TXCSR_AUTOSET 0x8000 +#define MUSB_TXCSR_DMAENAB 0x1000 +#define MUSB_TXCSR_FRCDATATOG 0x0800 +#define MUSB_TXCSR_DMAMODE 0x0400 +#define MUSB_TXCSR_CLRDATATOG 0x0040 +#define MUSB_TXCSR_FLUSHFIFO 0x0008 +#define MUSB_TXCSR_FIFONOTEMPTY 0x0002 +#define MUSB_TXCSR_TXPKTRDY 0x0001 + +/* TXCSR in Peripheral mode */ +#define MUSB_TXCSR_P_ISO 0x4000 +#define MUSB_TXCSR_P_INCOMPTX 0x0080 +#define MUSB_TXCSR_P_SENTSTALL 0x0020 +#define MUSB_TXCSR_P_SENDSTALL 0x0010 +#define MUSB_TXCSR_P_UNDERRUN 0x0004 + +/* TXCSR in Host mode */ +#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200 +#define MUSB_TXCSR_H_DATATOGGLE 0x0100 +#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080 +#define MUSB_TXCSR_H_RXSTALL 0x0020 +#define MUSB_TXCSR_H_ERROR 0x0004 + +/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_TXCSR_P_WZC_BITS \ + (MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \ + | MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY) +#define MUSB_TXCSR_H_WZC_BITS \ + (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ + | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) + +/* RXCSR in Peripheral and Host mode */ +#define MUSB_RXCSR_AUTOCLEAR 0x8000 +#define MUSB_RXCSR_DMAENAB 0x2000 +#define MUSB_RXCSR_DISNYET 0x1000 +#define MUSB_RXCSR_PID_ERR 0x1000 +#define MUSB_RXCSR_DMAMODE 0x0800 +#define MUSB_RXCSR_INCOMPRX 0x0100 +#define MUSB_RXCSR_CLRDATATOG 0x0080 +#define MUSB_RXCSR_FLUSHFIFO 0x0010 +#define MUSB_RXCSR_DATAERROR 0x0008 +#define MUSB_RXCSR_FIFOFULL 0x0002 +#define MUSB_RXCSR_RXPKTRDY 0x0001 + +/* RXCSR in Peripheral mode */ +#define MUSB_RXCSR_P_ISO 0x4000 +#define MUSB_RXCSR_P_SENTSTALL 0x0040 +#define MUSB_RXCSR_P_SENDSTALL 0x0020 +#define MUSB_RXCSR_P_OVERRUN 0x0004 + +/* RXCSR in Host mode */ +#define MUSB_RXCSR_H_AUTOREQ 0x4000 +#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400 +#define MUSB_RXCSR_H_DATATOGGLE 0x0200 +#define MUSB_RXCSR_H_RXSTALL 0x0040 +#define MUSB_RXCSR_H_REQPKT 0x0020 +#define MUSB_RXCSR_H_ERROR 0x0004 + +/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_RXCSR_P_WZC_BITS \ + (MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \ + | MUSB_RXCSR_RXPKTRDY) +#define MUSB_RXCSR_H_WZC_BITS \ + (MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \ + | MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY) + +/* HUBADDR */ +#define MUSB_HUBADDR_MULTI_TT 0x80 + + +#ifndef CONFIG_BLACKFIN + +/* + * Common USB registers + */ + +#define MUSB_FADDR 0x00 /* 8-bit */ +#define MUSB_POWER 0x01 /* 8-bit */ + +#define MUSB_INTRTX 0x02 /* 16-bit */ +#define MUSB_INTRRX 0x04 +#define MUSB_INTRTXE 0x06 +#define MUSB_INTRRXE 0x08 +#define MUSB_INTRUSB 0x0A /* 8 bit */ +#define MUSB_INTRUSBE 0x0B /* 8 bit */ +#define MUSB_FRAME 0x0C +#define MUSB_INDEX 0x0E /* 8 bit */ +#define MUSB_TESTMODE 0x0F /* 8 bit */ + +/* Get offset for a given FIFO from musb->mregs */ +#if defined(CONFIG_USB_MUSB_TUSB6010) || \ + defined(CONFIG_USB_MUSB_TUSB6010_MODULE) +#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20)) +#else +#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4)) +#endif + +/* + * Additional Control Registers + */ + +#define MUSB_DEVCTL 0x60 /* 8 bit */ + +/* These are always controlled through the INDEX register */ +#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */ +#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */ +#define MUSB_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */ +#define MUSB_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */ + +/* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */ +#define MUSB_HWVERS 0x6C /* 8 bit */ +#define MUSB_ULPI_BUSCONTROL 0x70 /* 8 bit */ +#define MUSB_ULPI_INT_MASK 0x72 /* 8 bit */ +#define MUSB_ULPI_INT_SRC 0x73 /* 8 bit */ +#define MUSB_ULPI_REG_DATA 0x74 /* 8 bit */ +#define MUSB_ULPI_REG_ADDR 0x75 /* 8 bit */ +#define MUSB_ULPI_REG_CONTROL 0x76 /* 8 bit */ +#define MUSB_ULPI_RAW_DATA 0x77 /* 8 bit */ + +#define MUSB_EPINFO 0x78 /* 8 bit */ +#define MUSB_RAMINFO 0x79 /* 8 bit */ +#define MUSB_LINKINFO 0x7a /* 8 bit */ +#define MUSB_VPLEN 0x7b /* 8 bit */ +#define MUSB_HS_EOF1 0x7c /* 8 bit */ +#define MUSB_FS_EOF1 0x7d /* 8 bit */ +#define MUSB_LS_EOF1 0x7e /* 8 bit */ + +/* Offsets to endpoint registers */ +#define MUSB_TXMAXP 0x00 +#define MUSB_TXCSR 0x02 +#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */ +#define MUSB_RXMAXP 0x04 +#define MUSB_RXCSR 0x06 +#define MUSB_RXCOUNT 0x08 +#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */ +#define MUSB_TXTYPE 0x0A +#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */ +#define MUSB_TXINTERVAL 0x0B +#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */ +#define MUSB_RXTYPE 0x0C +#define MUSB_RXINTERVAL 0x0D +#define MUSB_FIFOSIZE 0x0F +#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */ + +/* Offsets to endpoint registers in indexed model (using INDEX register) */ +#define MUSB_INDEXED_OFFSET(_epnum, _offset) \ + (0x10 + (_offset)) + +/* Offsets to endpoint registers in flat models */ +#define MUSB_FLAT_OFFSET(_epnum, _offset) \ + (0x100 + (0x10*(_epnum)) + (_offset)) + +#if defined(CONFIG_USB_MUSB_TUSB6010) || \ + defined(CONFIG_USB_MUSB_TUSB6010_MODULE) +/* TUSB6010 EP0 configuration register is special */ +#define MUSB_TUSB_OFFSET(_epnum, _offset) \ + (0x10 + _offset) +#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */ +#endif + +#define MUSB_TXCSR_MODE 0x2000 + +/* "bus control"/target registers, for host side multipoint (external hubs) */ +#define MUSB_TXFUNCADDR 0x00 +#define MUSB_TXHUBADDR 0x02 +#define MUSB_TXHUBPORT 0x03 + +#define MUSB_RXFUNCADDR 0x04 +#define MUSB_RXHUBADDR 0x06 +#define MUSB_RXHUBPORT 0x07 + +#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ + (0x80 + (8*(_epnum)) + (_offset)) + +static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) +{ + musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); +} + +static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off) +{ + musb_writew(mbase, MUSB_TXFIFOADD, c_off); +} + +static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size) +{ + musb_writeb(mbase, MUSB_RXFIFOSZ, c_size); +} + +static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off) +{ + musb_writew(mbase, MUSB_RXFIFOADD, c_off); +} + +static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val) +{ + musb_writeb(mbase, MUSB_ULPI_BUSCONTROL, val); +} + +static inline u8 musb_read_txfifosz(void __iomem *mbase) +{ + return musb_readb(mbase, MUSB_TXFIFOSZ); +} + +static inline u16 musb_read_txfifoadd(void __iomem *mbase) +{ + return musb_readw(mbase, MUSB_TXFIFOADD); +} + +static inline u8 musb_read_rxfifosz(void __iomem *mbase) +{ + return musb_readb(mbase, MUSB_RXFIFOSZ); +} + +static inline u16 musb_read_rxfifoadd(void __iomem *mbase) +{ + return musb_readw(mbase, MUSB_RXFIFOADD); +} + +static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase) +{ + return musb_readb(mbase, MUSB_ULPI_BUSCONTROL); +} + +static inline u8 musb_read_configdata(void __iomem *mbase) +{ + musb_writeb(mbase, MUSB_INDEX, 0); + return musb_readb(mbase, 0x10 + MUSB_CONFIGDATA); +} + +static inline u16 musb_read_hwvers(void __iomem *mbase) +{ + return musb_readw(mbase, MUSB_HWVERS); +} + +static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) +{ + return (MUSB_BUSCTL_OFFSET(i, 0) + mbase); +} + +static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, + u8 qh_addr_reg) +{ + musb_writeb(ep_target_regs, MUSB_RXFUNCADDR, qh_addr_reg); +} + +static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, + u8 qh_h_addr_reg) +{ + musb_writeb(ep_target_regs, MUSB_RXHUBADDR, qh_h_addr_reg); +} + +static inline void musb_write_rxhubport(void __iomem *ep_target_regs, + u8 qh_h_port_reg) +{ + musb_writeb(ep_target_regs, MUSB_RXHUBPORT, qh_h_port_reg); +} + +static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, + u8 qh_addr_reg) +{ + musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR), + qh_addr_reg); +} + +static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum, + u8 qh_addr_reg) +{ + musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR), + qh_addr_reg); +} + +static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, + u8 qh_h_port_reg) +{ + musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT), + qh_h_port_reg); +} + +static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum) +{ + return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR)); +} + +static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum) +{ + return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR)); +} + +static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum) +{ + return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT)); +} + +static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum) +{ + return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR)); +} + +static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum) +{ + return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR)); +} + +static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum) +{ + return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT)); +} + +#else /* CONFIG_BLACKFIN */ + +#define USB_BASE USB_FADDR +#define USB_OFFSET(reg) (reg - USB_BASE) + +/* + * Common USB registers + */ +#define MUSB_FADDR USB_OFFSET(USB_FADDR) /* 8-bit */ +#define MUSB_POWER USB_OFFSET(USB_POWER) /* 8-bit */ +#define MUSB_INTRTX USB_OFFSET(USB_INTRTX) /* 16-bit */ +#define MUSB_INTRRX USB_OFFSET(USB_INTRRX) +#define MUSB_INTRTXE USB_OFFSET(USB_INTRTXE) +#define MUSB_INTRRXE USB_OFFSET(USB_INTRRXE) +#define MUSB_INTRUSB USB_OFFSET(USB_INTRUSB) /* 8 bit */ +#define MUSB_INTRUSBE USB_OFFSET(USB_INTRUSBE)/* 8 bit */ +#define MUSB_FRAME USB_OFFSET(USB_FRAME) +#define MUSB_INDEX USB_OFFSET(USB_INDEX) /* 8 bit */ +#define MUSB_TESTMODE USB_OFFSET(USB_TESTMODE)/* 8 bit */ + +/* Get offset for a given FIFO from musb->mregs */ +#define MUSB_FIFO_OFFSET(epnum) \ + (USB_OFFSET(USB_EP0_FIFO) + ((epnum) * 8)) + +/* + * Additional Control Registers + */ + +#define MUSB_DEVCTL USB_OFFSET(USB_OTG_DEV_CTL) /* 8 bit */ + +#define MUSB_LINKINFO USB_OFFSET(USB_LINKINFO)/* 8 bit */ +#define MUSB_VPLEN USB_OFFSET(USB_VPLEN) /* 8 bit */ +#define MUSB_HS_EOF1 USB_OFFSET(USB_HS_EOF1) /* 8 bit */ +#define MUSB_FS_EOF1 USB_OFFSET(USB_FS_EOF1) /* 8 bit */ +#define MUSB_LS_EOF1 USB_OFFSET(USB_LS_EOF1) /* 8 bit */ + +/* Offsets to endpoint registers */ +#define MUSB_TXMAXP 0x00 +#define MUSB_TXCSR 0x04 +#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */ +#define MUSB_RXMAXP 0x08 +#define MUSB_RXCSR 0x0C +#define MUSB_RXCOUNT 0x10 +#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */ +#define MUSB_TXTYPE 0x14 +#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */ +#define MUSB_TXINTERVAL 0x18 +#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */ +#define MUSB_RXTYPE 0x1C +#define MUSB_RXINTERVAL 0x20 +#define MUSB_TXCOUNT 0x28 + +/* Offsets to endpoint registers in indexed model (using INDEX register) */ +#define MUSB_INDEXED_OFFSET(_epnum, _offset) \ + (0x40 + (_offset)) + +/* Offsets to endpoint registers in flat models */ +#define MUSB_FLAT_OFFSET(_epnum, _offset) \ + (USB_OFFSET(USB_EP_NI0_TXMAXP) + (0x40 * (_epnum)) + (_offset)) + +/* Not implemented - HW has separate Tx/Rx FIFO */ +#define MUSB_TXCSR_MODE 0x0000 + +static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) +{ +} + +static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off) +{ +} + +static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size) +{ +} + +static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off) +{ +} + +static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val) +{ +} + +static inline u8 musb_read_txfifosz(void __iomem *mbase) +{ + return 0; +} + +static inline u16 musb_read_txfifoadd(void __iomem *mbase) +{ + return 0; +} + +static inline u8 musb_read_rxfifosz(void __iomem *mbase) +{ + return 0; +} + +static inline u16 musb_read_rxfifoadd(void __iomem *mbase) +{ + return 0; +} + +static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase) +{ + return 0; +} + +static inline u8 musb_read_configdata(void __iomem *mbase) +{ + return 0; +} + +static inline u16 musb_read_hwvers(void __iomem *mbase) +{ + /* + * This register is invisible on Blackfin, actually the MUSB + * RTL version of Blackfin is 1.9, so just harcode its value. + */ + return MUSB_HWVERS_1900; +} + +static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) +{ + return NULL; +} + +static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, + u8 qh_addr_req) +{ +} + +static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, + u8 qh_h_addr_reg) +{ +} + +static inline void musb_write_rxhubport(void __iomem *ep_target_regs, + u8 qh_h_port_reg) +{ +} + +static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, + u8 qh_addr_reg) +{ +} + +static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum, + u8 qh_addr_reg) +{ +} + +static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, + u8 qh_h_port_reg) +{ +} + +static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum) +{ + return 0; +} + +static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum) +{ + return 0; +} + +static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum) +{ + return 0; +} + +static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum) +{ + return 0; +} + +static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum) +{ + return 0; +} + +static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum) +{ + return 0; +} + +#endif /* CONFIG_BLACKFIN */ + +#endif /* __MUSB_REGS_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/musb_uboot.c b/qemu/roms/u-boot/drivers/usb/musb-new/musb_uboot.c new file mode 100644 index 000000000..0d7b89fcf --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/musb_uboot.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include +#include + +#define __UBOOT__ +#include +#include "linux-compat.h" +#include "usb-compat.h" +#include "musb_core.h" +#include "musb_host.h" +#include "musb_gadget.h" + +#ifdef CONFIG_MUSB_HOST +static struct musb *host; +static struct usb_hcd hcd; +static enum usb_device_speed host_speed; + +static void musb_host_complete_urb(struct urb *urb) +{ + urb->dev->status &= ~USB_ST_NOT_PROC; + urb->dev->act_len = urb->actual_length; +} + +static struct usb_host_endpoint hep; +static struct urb urb; + +static struct urb *construct_urb(struct usb_device *dev, int endpoint_type, + unsigned long pipe, void *buffer, int len, + struct devrequest *setup, int interval) +{ + int epnum = usb_pipeendpoint(pipe); + int is_in = usb_pipein(pipe); + + memset(&urb, 0, sizeof(struct urb)); + memset(&hep, 0, sizeof(struct usb_host_endpoint)); + INIT_LIST_HEAD(&hep.urb_list); + INIT_LIST_HEAD(&urb.urb_list); + urb.ep = &hep; + urb.complete = musb_host_complete_urb; + urb.status = -EINPROGRESS; + urb.dev = dev; + urb.pipe = pipe; + urb.transfer_buffer = buffer; + urb.transfer_dma = (unsigned long)buffer; + urb.transfer_buffer_length = len; + urb.setup_packet = (unsigned char *)setup; + + urb.ep->desc.wMaxPacketSize = + __cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] : + dev->epmaxpacketout[epnum]); + urb.ep->desc.bmAttributes = endpoint_type; + urb.ep->desc.bEndpointAddress = + (is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum; + urb.ep->desc.bInterval = interval; + + return &urb; +} + +#define MUSB_HOST_TIMEOUT 0x3ffffff + +static int submit_urb(struct usb_hcd *hcd, struct urb *urb) +{ + struct musb *host = hcd->hcd_priv; + int ret; + int timeout; + + ret = musb_urb_enqueue(hcd, urb, 0); + if (ret < 0) { + printf("Failed to enqueue URB to controller\n"); + return ret; + } + + timeout = MUSB_HOST_TIMEOUT; + do { + if (ctrlc()) + return -EIO; + host->isr(0, host); + } while ((urb->dev->status & USB_ST_NOT_PROC) && --timeout); + + return urb->status; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len, struct devrequest *setup) +{ + struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_CONTROL, pipe, + buffer, len, setup, 0); + + /* Fix speed for non hub-attached devices */ + if (!dev->parent) + dev->speed = host_speed; + + return submit_urb(&hcd, urb); +} + + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len) +{ + struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_BULK, pipe, + buffer, len, NULL, 0); + return submit_urb(&hcd, urb); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len, int interval) +{ + struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_INT, pipe, + buffer, len, NULL, interval); + return submit_urb(&hcd, urb); +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + u8 power; + void *mbase; + int timeout = MUSB_HOST_TIMEOUT; + + if (!host) { + printf("MUSB host is not registered\n"); + return -ENODEV; + } + + musb_start(host); + mbase = host->mregs; + do { + if (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_HM) + break; + } while (--timeout); + if (!timeout) + return -ENODEV; + + power = musb_readb(mbase, MUSB_POWER); + musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); + udelay(30000); + power = musb_readb(mbase, MUSB_POWER); + musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); + host->isr(0, host); + host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? + USB_SPEED_HIGH : + (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? + USB_SPEED_FULL : USB_SPEED_LOW; + host->is_active = 1; + hcd.hcd_priv = host; + + return 0; +} + +int usb_lowlevel_stop(int index) +{ + if (!host) { + printf("MUSB host is not registered\n"); + return -ENODEV; + } + + musb_stop(host); + return 0; +} +#endif /* CONFIG_MUSB_HOST */ + +#ifdef CONFIG_MUSB_GADGET +static struct musb *gadget; + +int usb_gadget_handle_interrupts(void) +{ + WATCHDOG_RESET(); + if (!gadget || !gadget->isr) + return -EINVAL; + + return gadget->isr(0, gadget); +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int ret; + + if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind || + !driver->setup) { + printf("bad parameter.\n"); + return -EINVAL; + } + + if (!gadget) { + printf("Controller uninitialized\n"); + return -ENXIO; + } + + ret = musb_gadget_start(&gadget->g, driver); + if (ret < 0) { + printf("gadget_start failed with %d\n", ret); + return ret; + } + + ret = driver->bind(&gadget->g); + if (ret < 0) { + printf("bind failed with %d\n", ret); + return ret; + } + + return 0; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + if (driver->disconnect) + driver->disconnect(&gadget->g); + if (driver->unbind) + driver->unbind(&gadget->g); + return 0; +} +#endif /* CONFIG_MUSB_GADGET */ + +int musb_register(struct musb_hdrc_platform_data *plat, void *bdata, + void *ctl_regs) +{ + struct musb **musbp; + + switch (plat->mode) { +#ifdef CONFIG_MUSB_HOST + case MUSB_HOST: + musbp = &host; + break; +#endif +#ifdef CONFIG_MUSB_GADGET + case MUSB_PERIPHERAL: + musbp = &gadget; + break; +#endif + default: + return -EINVAL; + } + + *musbp = musb_init_controller(plat, (struct device *)bdata, ctl_regs); + if (!musbp) { + printf("Failed to init the controller\n"); + return -EIO; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/omap2430.c b/qemu/roms/u-boot/drivers/usb/musb-new/omap2430.c new file mode 100644 index 000000000..b1c4dc782 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/omap2430.c @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2005-2007 by Texas Instruments + * Some code has been taken from tusb6010.c + * Copyrights for that are attributable to: + * Copyright (C) 2006 Nokia Corporation + * Tony Lindgren + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + * The Inventra Controller Driver for Linux is distributed in + * the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + */ +#define __UBOOT__ +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include "linux-compat.h" +#endif + +#include "musb_core.h" +#include "omap2430.h" + +#ifndef __UBOOT__ +struct omap2430_glue { + struct device *dev; + struct platform_device *musb; + enum omap_musb_vbus_id_status status; + struct work_struct omap_musb_mailbox_work; +}; +#define glue_to_musb(g) platform_get_drvdata(g->musb) + +struct omap2430_glue *_glue; + +static struct timer_list musb_idle_timer; + +static void musb_do_idle(unsigned long _musb) +{ + struct musb *musb = (void *)_musb; + unsigned long flags; + u8 power; + u8 devctl; + + spin_lock_irqsave(&musb->lock, flags); + + switch (musb->xceiv->state) { + case OTG_STATE_A_WAIT_BCON: + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) { + musb->xceiv->state = OTG_STATE_B_IDLE; + MUSB_DEV_MODE(musb); + } else { + musb->xceiv->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + } + break; + case OTG_STATE_A_SUSPEND: + /* finish RESUME signaling? */ + if (musb->port1_status & MUSB_PORT_STAT_RESUME) { + power = musb_readb(musb->mregs, MUSB_POWER); + power &= ~MUSB_POWER_RESUME; + dev_dbg(musb->controller, "root port resume stopped, power %02x\n", power); + musb_writeb(musb->mregs, MUSB_POWER, power); + musb->is_active = 1; + musb->port1_status &= ~(USB_PORT_STAT_SUSPEND + | MUSB_PORT_STAT_RESUME); + musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; + usb_hcd_poll_rh_status(musb_to_hcd(musb)); + /* NOTE: it might really be A_WAIT_BCON ... */ + musb->xceiv->state = OTG_STATE_A_HOST; + } + break; + case OTG_STATE_A_HOST: + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) + musb->xceiv->state = OTG_STATE_B_IDLE; + else + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; + default: + break; + } + spin_unlock_irqrestore(&musb->lock, flags); +} + + +static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) +{ + unsigned long default_timeout = jiffies + msecs_to_jiffies(3); + static unsigned long last_timer; + + if (timeout == 0) + timeout = default_timeout; + + /* Never idle if active, or when VBUS timeout is not set as host */ + if (musb->is_active || ((musb->a_wait_bcon == 0) + && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { + dev_dbg(musb->controller, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); + del_timer(&musb_idle_timer); + last_timer = jiffies; + return; + } + + if (time_after(last_timer, timeout)) { + if (!timer_pending(&musb_idle_timer)) + last_timer = timeout; + else { + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n"); + return; + } + } + last_timer = timeout; + + dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", + otg_state_string(musb->xceiv->state), + (unsigned long)jiffies_to_msecs(timeout - jiffies)); + mod_timer(&musb_idle_timer, timeout); +} + +static void omap2430_musb_set_vbus(struct musb *musb, int is_on) +{ + struct usb_otg *otg = musb->xceiv->otg; + u8 devctl; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + int ret = 1; + /* HDRC controls CPEN, but beware current surges during device + * connect. They can trigger transient overcurrent conditions + * that must be ignored. + */ + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + if (is_on) { + if (musb->xceiv->state == OTG_STATE_A_IDLE) { + /* start the session */ + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + /* + * Wait for the musb to set as A device to enable the + * VBUS + */ + while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) { + + cpu_relax(); + + if (time_after(jiffies, timeout)) { + dev_err(musb->controller, + "configured as A device timeout"); + ret = -EINVAL; + break; + } + } + + if (ret && otg->set_vbus) + otg_set_vbus(otg, 1); + } else { + musb->is_active = 1; + otg->default_a = 1; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; + devctl |= MUSB_DEVCTL_SESSION; + MUSB_HST_MODE(musb); + } + } else { + musb->is_active = 0; + + /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and + * jumping right to B_IDLE... + */ + + otg->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; + devctl &= ~MUSB_DEVCTL_SESSION; + + MUSB_DEV_MODE(musb); + } + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + dev_dbg(musb->controller, "VBUS %s, devctl %02x " + /* otg %3x conf %08x prcm %08x */ "\n", + otg_state_string(musb->xceiv->state), + musb_readb(musb->mregs, MUSB_DEVCTL)); +} + +static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode) +{ + u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + return 0; +} +#endif + +static inline void omap2430_low_level_exit(struct musb *musb) +{ + u32 l; + + /* in any role */ + l = musb_readl(musb->mregs, OTG_FORCESTDBY); + l |= ENABLEFORCE; /* enable MSTANDBY */ + musb_writel(musb->mregs, OTG_FORCESTDBY, l); +} + +static inline void omap2430_low_level_init(struct musb *musb) +{ + u32 l; + + l = musb_readl(musb->mregs, OTG_FORCESTDBY); + l &= ~ENABLEFORCE; /* disable MSTANDBY */ + musb_writel(musb->mregs, OTG_FORCESTDBY, l); +} + +#ifndef __UBOOT__ +void omap_musb_mailbox(enum omap_musb_vbus_id_status status) +{ + struct omap2430_glue *glue = _glue; + struct musb *musb = glue_to_musb(glue); + + glue->status = status; + if (!musb) { + dev_err(glue->dev, "musb core is not yet ready\n"); + return; + } + + schedule_work(&glue->omap_musb_mailbox_work); +} +EXPORT_SYMBOL_GPL(omap_musb_mailbox); + +static void omap_musb_set_mailbox(struct omap2430_glue *glue) +{ + struct musb *musb = glue_to_musb(glue); + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *pdata = dev->platform_data; + struct omap_musb_board_data *data = pdata->board_data; + struct usb_otg *otg = musb->xceiv->otg; + + switch (glue->status) { + case OMAP_MUSB_ID_GROUND: + dev_dbg(dev, "ID GND\n"); + + otg->default_a = true; + musb->xceiv->state = OTG_STATE_A_IDLE; + musb->xceiv->last_event = USB_EVENT_ID; + if (!is_otg_enabled(musb) || musb->gadget_driver) { + pm_runtime_get_sync(dev); + usb_phy_init(musb->xceiv); + omap2430_musb_set_vbus(musb, 1); + } + break; + + case OMAP_MUSB_VBUS_VALID: + dev_dbg(dev, "VBUS Connect\n"); + + otg->default_a = false; + musb->xceiv->state = OTG_STATE_B_IDLE; + musb->xceiv->last_event = USB_EVENT_VBUS; + if (musb->gadget_driver) + pm_runtime_get_sync(dev); + usb_phy_init(musb->xceiv); + break; + + case OMAP_MUSB_ID_FLOAT: + case OMAP_MUSB_VBUS_OFF: + dev_dbg(dev, "VBUS Disconnect\n"); + + musb->xceiv->last_event = USB_EVENT_NONE; + if (is_otg_enabled(musb) || is_peripheral_enabled(musb)) + if (musb->gadget_driver) { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } + + if (data->interface_type == MUSB_INTERFACE_UTMI) { + if (musb->xceiv->otg->set_vbus) + otg_set_vbus(musb->xceiv->otg, 0); + } + usb_phy_shutdown(musb->xceiv); + break; + default: + dev_dbg(dev, "ID float\n"); + } +} + + +static void omap_musb_mailbox_work(struct work_struct *mailbox_work) +{ + struct omap2430_glue *glue = container_of(mailbox_work, + struct omap2430_glue, omap_musb_mailbox_work); + omap_musb_set_mailbox(glue); +} +#endif + +static int omap2430_musb_init(struct musb *musb) +{ + u32 l; + int status = 0; +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; +#else + struct omap_musb_board_data *data = + (struct omap_musb_board_data *)musb->controller; +#endif + + +#ifndef __UBOOT__ + /* We require some kind of external transceiver, hooked + * up through ULPI. TWL4030-family PMICs include one, + * which needs a driver, drivers aren't always needed. + */ + musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(musb->xceiv)) { + pr_err("HS USB OTG: no transceiver configured\n"); + return -ENODEV; + } + + status = pm_runtime_get_sync(dev); + if (status < 0) { + dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status); + goto err1; + } +#endif + + l = musb_readl(musb->mregs, OTG_INTERFSEL); + + if (data->interface_type == MUSB_INTERFACE_UTMI) { + /* OMAP4 uses Internal PHY GS70 which uses UTMI interface */ + l &= ~ULPI_12PIN; /* Disable ULPI */ + l |= UTMI_8BIT; /* Enable UTMI */ + } else { + l |= ULPI_12PIN; + } + + musb_writel(musb->mregs, OTG_INTERFSEL, l); + + pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, " + "sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n", + musb_readl(musb->mregs, OTG_REVISION), + musb_readl(musb->mregs, OTG_SYSCONFIG), + musb_readl(musb->mregs, OTG_SYSSTATUS), + musb_readl(musb->mregs, OTG_INTERFSEL), + musb_readl(musb->mregs, OTG_SIMENABLE)); + +#ifndef __UBOOT__ + setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); + + if (glue->status != OMAP_MUSB_UNKNOWN) + omap_musb_set_mailbox(glue); + + pm_runtime_put_noidle(musb->controller); +#endif + return 0; + +err1: + return status; +} + +static void omap2430_musb_enable(struct musb *musb) +{ +#ifndef __UBOOT__ + u8 devctl; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + struct musb_hdrc_platform_data *pdata = dev->platform_data; + struct omap_musb_board_data *data = pdata->board_data; + + switch (glue->status) { + + case OMAP_MUSB_ID_GROUND: + usb_phy_init(musb->xceiv); + if (data->interface_type != MUSB_INTERFACE_UTMI) + break; + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + /* start the session */ + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + while (musb_readb(musb->mregs, MUSB_DEVCTL) & + MUSB_DEVCTL_BDEVICE) { + cpu_relax(); + + if (time_after(jiffies, timeout)) { + dev_err(dev, "configured as A device timeout"); + break; + } + } + break; + + case OMAP_MUSB_VBUS_VALID: + usb_phy_init(musb->xceiv); + break; + + default: + break; + } +#else +#ifdef CONFIG_TWL4030_USB + if (twl4030_usb_ulpi_init()) { + serial_printf("ERROR: %s Could not initialize PHY\n", + __PRETTY_FUNCTION__); + } +#endif +#endif +} + +static void omap2430_musb_disable(struct musb *musb) +{ +#ifndef __UBOOT__ + struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + + if (glue->status != OMAP_MUSB_UNKNOWN) + usb_phy_shutdown(musb->xceiv); +#endif +} + +static int omap2430_musb_exit(struct musb *musb) +{ + del_timer_sync(&musb_idle_timer); + + omap2430_low_level_exit(musb); + + return 0; +} + +#ifndef __UBOOT__ +static const struct musb_platform_ops omap2430_ops = { +#else +const struct musb_platform_ops omap2430_ops = { +#endif + .init = omap2430_musb_init, + .exit = omap2430_musb_exit, + +#ifndef __UBOOT__ + .set_mode = omap2430_musb_set_mode, + .try_idle = omap2430_musb_try_idle, + + .set_vbus = omap2430_musb_set_vbus, +#endif + + .enable = omap2430_musb_enable, + .disable = omap2430_musb_disable, +}; + +#ifndef __UBOOT__ +static u64 omap2430_dmamask = DMA_BIT_MASK(32); + +static int __devinit omap2430_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct platform_device *musb; + struct omap2430_glue *glue; + int ret = -ENOMEM; + + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); + if (!glue) { + dev_err(&pdev->dev, "failed to allocate glue context\n"); + goto err0; + } + + musb = platform_device_alloc("musb-hdrc", -1); + if (!musb) { + dev_err(&pdev->dev, "failed to allocate musb device\n"); + goto err0; + } + + musb->dev.parent = &pdev->dev; + musb->dev.dma_mask = &omap2430_dmamask; + musb->dev.coherent_dma_mask = omap2430_dmamask; + + glue->dev = &pdev->dev; + glue->musb = musb; + glue->status = OMAP_MUSB_UNKNOWN; + + pdata->platform_ops = &omap2430_ops; + + platform_set_drvdata(pdev, glue); + + /* + * REVISIT if we ever have two instances of the wrapper, we will be + * in big trouble + */ + _glue = glue; + + INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err1; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err1; + } + + pm_runtime_enable(&pdev->dev); + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err1; + } + + return 0; + +err1: + platform_device_put(musb); + +err0: + return ret; +} + +static int __devexit omap2430_remove(struct platform_device *pdev) +{ + struct omap2430_glue *glue = platform_get_drvdata(pdev); + + cancel_work_sync(&glue->omap_musb_mailbox_work); + platform_device_del(glue->musb); + platform_device_put(glue->musb); + + return 0; +} + +#ifdef CONFIG_PM + +static int omap2430_runtime_suspend(struct device *dev) +{ + struct omap2430_glue *glue = dev_get_drvdata(dev); + struct musb *musb = glue_to_musb(glue); + + if (musb) { + musb->context.otg_interfsel = musb_readl(musb->mregs, + OTG_INTERFSEL); + + omap2430_low_level_exit(musb); + usb_phy_set_suspend(musb->xceiv, 1); + } + + return 0; +} + +static int omap2430_runtime_resume(struct device *dev) +{ + struct omap2430_glue *glue = dev_get_drvdata(dev); + struct musb *musb = glue_to_musb(glue); + + if (musb) { + omap2430_low_level_init(musb); + musb_writel(musb->mregs, OTG_INTERFSEL, + musb->context.otg_interfsel); + + usb_phy_set_suspend(musb->xceiv, 0); + } + + return 0; +} + +static struct dev_pm_ops omap2430_pm_ops = { + .runtime_suspend = omap2430_runtime_suspend, + .runtime_resume = omap2430_runtime_resume, +}; + +#define DEV_PM_OPS (&omap2430_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +static struct platform_driver omap2430_driver = { + .probe = omap2430_probe, + .remove = __devexit_p(omap2430_remove), + .driver = { + .name = "musb-omap2430", + .pm = DEV_PM_OPS, + }, +}; + +MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer"); +MODULE_AUTHOR("Felipe Balbi "); +MODULE_LICENSE("GPL v2"); + +static int __init omap2430_init(void) +{ + return platform_driver_register(&omap2430_driver); +} +subsys_initcall(omap2430_init); + +static void __exit omap2430_exit(void) +{ + platform_driver_unregister(&omap2430_driver); +} +module_exit(omap2430_exit); +#endif diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/omap2430.h b/qemu/roms/u-boot/drivers/usb/musb-new/omap2430.h new file mode 100644 index 000000000..3b795c248 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/omap2430.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2006 by Texas Instruments + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + */ + +#ifndef __MUSB_OMAP243X_H__ +#define __MUSB_OMAP243X_H__ + +#ifndef __UBOOT__ +#include +#else +#undef RESETDONE +#endif + +/* + * OMAP2430-specific definitions + */ + +#define OTG_REVISION 0x400 + +#define OTG_SYSCONFIG 0x404 +# define MIDLEMODE 12 /* bit position */ +# define FORCESTDBY (0 << MIDLEMODE) +# define NOSTDBY (1 << MIDLEMODE) +# define SMARTSTDBY (2 << MIDLEMODE) + +# define SIDLEMODE 3 /* bit position */ +# define FORCEIDLE (0 << SIDLEMODE) +# define NOIDLE (1 << SIDLEMODE) +# define SMARTIDLE (2 << SIDLEMODE) + +# define ENABLEWAKEUP (1 << 2) +# define SOFTRST (1 << 1) +# define AUTOIDLE (1 << 0) + +#define OTG_SYSSTATUS 0x408 +# define RESETDONE (1 << 0) + +#define OTG_INTERFSEL 0x40c +# define EXTCP (1 << 2) +# define PHYSEL 0 /* bit position */ +# define UTMI_8BIT (0 << PHYSEL) +# define ULPI_12PIN (1 << PHYSEL) +# define ULPI_8PIN (2 << PHYSEL) + +#define OTG_SIMENABLE 0x410 +# define TM1 (1 << 0) + +#define OTG_FORCESTDBY 0x414 +# define ENABLEFORCE (1 << 0) + +#endif /* __MUSB_OMAP243X_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb-new/usb-compat.h b/qemu/roms/u-boot/drivers/usb/musb-new/usb-compat.h new file mode 100644 index 000000000..27f656f0c --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb-new/usb-compat.h @@ -0,0 +1,88 @@ +#ifndef __USB_COMPAT_H__ +#define __USB_COMPAT_H__ + +#include "usb.h" + +struct usb_hcd { + void *hcd_priv; +}; + +struct usb_host_endpoint { + struct usb_endpoint_descriptor desc; + struct list_head urb_list; + void *hcpriv; +}; + +/* + * urb->transfer_flags: + * + * Note: URB_DIR_IN/OUT is automatically set in usb_submit_urb(). + */ +#define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */ +#define URB_ZERO_PACKET 0x0040 /* Finish bulk OUT with short packet */ + +struct urb; + +typedef void (*usb_complete_t)(struct urb *); + +struct urb { + void *hcpriv; /* private data for host controller */ + struct list_head urb_list; /* list head for use by the urb's + * current owner */ + struct usb_device *dev; /* (in) pointer to associated device */ + struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */ + unsigned int pipe; /* (in) pipe information */ + int status; /* (return) non-ISO status */ + unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ + void *transfer_buffer; /* (in) associated data buffer */ + dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ + u32 transfer_buffer_length; /* (in) data buffer length */ + u32 actual_length; /* (return) actual transfer length */ + unsigned char *setup_packet; /* (in) setup packet (control only) */ + int start_frame; /* (modify) start frame (ISO) */ + usb_complete_t complete; /* (in) completion routine */ +}; + +#define usb_hcd_link_urb_to_ep(hcd, urb) ({ \ + int ret = 0; \ + list_add_tail(&urb->urb_list, &urb->ep->urb_list); \ + ret; }) +#define usb_hcd_unlink_urb_from_ep(hcd, urb) list_del_init(&urb->urb_list) + +static inline void usb_hcd_giveback_urb(struct usb_hcd *hcd, + struct urb *urb, + int status) +{ + urb->status = status; + if (urb->complete) + urb->complete(urb); +} + +static inline int usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, + struct urb *urb) +{ + /* TODO: add cache invalidation here */ + return 0; +} + +static inline u16 find_tt(struct usb_device *dev) +{ + u8 chid; + u8 hub; + + /* Find out the nearest parent which is high speed */ + while (dev->parent->parent != NULL) + if (dev->parent->speed != USB_SPEED_HIGH) + dev = dev->parent; + else + break; + + /* determine the port address at that hub */ + hub = dev->parent->devnum; + for (chid = 0; chid < USB_MAXCHILDREN; chid++) + if (dev->parent->children[chid] == dev) + break; + + return (hub << 8) | chid; +} +#endif /* __USB_COMPAT_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb/Makefile b/qemu/roms/u-boot/drivers/usb/musb/Makefile new file mode 100644 index 000000000..3c9ed98bc --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/Makefile @@ -0,0 +1,14 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o +obj-$(CONFIG_MUSB_UDC) += musb_udc.o musb_core.o +obj-$(CONFIG_USB_BLACKFIN) += blackfin_usb.o +obj-$(CONFIG_USB_DAVINCI) += davinci.o +obj-$(CONFIG_USB_OMAP3) += omap3.o +obj-$(CONFIG_USB_DA8XX) += da8xx.o +obj-$(CONFIG_USB_AM35X) += am35x.o diff --git a/qemu/roms/u-boot/drivers/usb/musb/am35x.c b/qemu/roms/u-boot/drivers/usb/musb/am35x.c new file mode 100644 index 000000000..62c3a6f60 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/am35x.c @@ -0,0 +1,139 @@ +/* + * am35x.c - TI's AM35x platform specific usb wrapper functions. + * + * Author: Ajay Kumar Gupta + * + * Based on drivers/usb/musb/da8xx.c + * + * Copyright (c) 2010 Texas Instruments Incorporated + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#include "am35x.h" + +/* MUSB platform configuration */ +struct musb_config musb_cfg = { + .regs = (struct musb_regs *)AM35X_USB_OTG_CORE_BASE, + .timeout = AM35X_USB_OTG_TIMEOUT, + .musb_speed = 0, +}; + +/* + * Enable the USB phy + */ +static u8 phy_on(void) +{ + u32 devconf2; + u32 timeout; + + devconf2 = readl(&am35x_scm_general_regs->devconf2); + + devconf2 &= ~(DEVCONF2_RESET | DEVCONF2_PHYPWRDN | DEVCONF2_OTGPWRDN | + DEVCONF2_OTGMODE | DEVCONF2_REFFREQ | + DEVCONF2_PHY_GPIOMODE); + devconf2 |= DEVCONF2_SESENDEN | DEVCONF2_VBDTCTEN | DEVCONF2_PHY_PLLON | + DEVCONF2_REFFREQ_13MHZ | DEVCONF2_DATPOL; + + writel(devconf2, &am35x_scm_general_regs->devconf2); + + /* wait until the USB phy is turned on */ + timeout = musb_cfg.timeout; + while (timeout--) + if (readl(&am35x_scm_general_regs->devconf2) & DEVCONF2_PHYCKGD) + return 1; + + /* USB phy was not turned on */ + return 0; +} + +/* + * Disable the USB phy + */ +static void phy_off(void) +{ + u32 devconf2; + + /* + * Power down the on-chip PHY. + */ + devconf2 = readl(&am35x_scm_general_regs->devconf2); + + devconf2 &= ~DEVCONF2_PHY_PLLON; + devconf2 |= DEVCONF2_PHYPWRDN | DEVCONF2_OTGPWRDN; + writel(devconf2, &am35x_scm_general_regs->devconf2); +} + +/* + * This function performs platform specific initialization for usb0. + */ +int musb_platform_init(void) +{ + u32 revision; + u32 sw_reset; + + /* global usb reset */ + sw_reset = readl(&am35x_scm_general_regs->ip_sw_reset); + sw_reset |= (1 << 0); + writel(sw_reset, &am35x_scm_general_regs->ip_sw_reset); + sw_reset &= ~(1 << 0); + writel(sw_reset, &am35x_scm_general_regs->ip_sw_reset); + + /* reset the controller */ + writel(0x1, &am35x_usb_regs->control); + udelay(5000); + + /* start the on-chip usb phy and its pll */ + if (phy_on() == 0) + return -1; + + /* Returns zero if e.g. not clocked */ + revision = readl(&am35x_usb_regs->revision); + if (revision == 0) + return -1; + + return 0; +} + +/* + * This function performs platform specific deinitialization for usb0. + */ +void musb_platform_deinit(void) +{ + /* Turn off the phy */ + phy_off(); +} + +/* + * This function reads data from endpoint fifo for AM35x + * which supports only 32bit read operation. + * + * ep - endpoint number + * length - number of bytes to read from FIFO + * fifo_data - pointer to data buffer into which data is read + */ +__attribute__((weak)) +void read_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + u32 val; + int i; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + if (length > 4) { + for (i = 0; i < (length >> 2); i++) { + val = readl(&musbr->fifox[ep]); + memcpy(data, &val, 4); + data += 4; + } + length %= 4; + } + if (length > 0) { + val = readl(&musbr->fifox[ep]); + memcpy(data, &val, length); + } +} diff --git a/qemu/roms/u-boot/drivers/usb/musb/am35x.h b/qemu/roms/u-boot/drivers/usb/musb/am35x.h new file mode 100644 index 000000000..bebe38d23 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/am35x.h @@ -0,0 +1,82 @@ +/* + * am35x.h - TI's AM35x platform specific usb wrapper definitions. + * + * Author: Ajay Kumar Gupta + * + * Based on drivers/usb/musb/da8xx.h + * + * Copyright (c) 2010 Texas Instruments Incorporated + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __AM35X_USB_H__ +#define __AM35X_USB_H__ + +#include +#include "musb_core.h" + +/* Base address of musb wrapper */ +#define AM35X_USB_OTG_BASE 0x5C040000 + +/* Base address of musb core */ +#define AM35X_USB_OTG_CORE_BASE (AM35X_USB_OTG_BASE + 0x400) + +/* Timeout for AM35x usb module */ +#define AM35X_USB_OTG_TIMEOUT 0x3FFFFFF + +/* + * AM35x platform USB wrapper register overlay. + */ +struct am35x_usb_regs { + u32 revision; + u32 control; + u32 status; + u32 emulation; + u32 reserved0[1]; + u32 autoreq; + u32 srpfixtime; + u32 ep_intsrc; + u32 ep_intsrcset; + u32 ep_intsrcclr; + u32 ep_intmsk; + u32 ep_intmskset; + u32 ep_intmskclr; + u32 ep_intsrcmsked; + u32 reserved1[1]; + u32 core_intsrc; + u32 core_intsrcset; + u32 core_intsrcclr; + u32 core_intmsk; + u32 core_intmskset; + u32 core_intmskclr; + u32 core_intsrcmsked; + u32 reserved2[1]; + u32 eoi; + u32 mop_sop_en; + u32 reserved3[2]; + u32 txmode; + u32 rxmode; + u32 epcount_mode; +}; + +#define am35x_usb_regs ((struct am35x_usb_regs *)AM35X_USB_OTG_BASE) + +/* USB 2.0 PHY Control */ +#define DEVCONF2_PHY_GPIOMODE (1 << 23) +#define DEVCONF2_OTGMODE (3 << 14) +#define DEVCONF2_SESENDEN (1 << 13) /* Vsess_end comparator */ +#define DEVCONF2_VBDTCTEN (1 << 12) /* Vbus comparator */ +#define DEVCONF2_REFFREQ_24MHZ (2 << 8) +#define DEVCONF2_REFFREQ_26MHZ (7 << 8) +#define DEVCONF2_REFFREQ_13MHZ (6 << 8) +#define DEVCONF2_REFFREQ (0xf << 8) +#define DEVCONF2_PHYCKGD (1 << 7) +#define DEVCONF2_VBUSSENSE (1 << 6) +#define DEVCONF2_PHY_PLLON (1 << 5) /* override PLL suspend */ +#define DEVCONF2_RESET (1 << 4) +#define DEVCONF2_PHYPWRDN (1 << 3) +#define DEVCONF2_OTGPWRDN (1 << 2) +#define DEVCONF2_DATPOL (1 << 1) + +#endif /* __AM35X_USB_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.c b/qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.c new file mode 100644 index 000000000..65fff887d --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.c @@ -0,0 +1,172 @@ +/* + * Blackfin MUSB HCD (Host Controller Driver) for u-boot + * + * Copyright (c) 2008-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include + +#include + +#include +#include +#include + +#include "musb_core.h" + +#ifndef CONFIG_USB_BLACKFIN_CLKIN +#define CONFIG_USB_BLACKFIN_CLKIN 24 +#endif + +/* MUSB platform configuration */ +struct musb_config musb_cfg = { + .regs = (struct musb_regs *)USB_FADDR, + .timeout = 0x3FFFFFF, + .musb_speed = 0, +}; + +/* + * This function read or write data to endpoint fifo + * Blackfin use DMA polling method to avoid buffer alignment issues + * + * ep - Endpoint number + * length - Number of bytes to write to FIFO + * fifo_data - Pointer to data buffer to be read/write + * is_write - Flag for read or write + */ +void rw_fifo(u8 ep, u32 length, void *fifo_data, int is_write) +{ + struct bfin_musb_dma_regs *regs; + u32 val = (u32)fifo_data; + + blackfin_dcache_flush_invalidate_range(fifo_data, fifo_data + length); + + regs = (void *)USB_DMA_INTERRUPT; + regs += ep; + + /* Setup DMA address register */ + bfin_write16(®s->addr_low, val); + SSYNC(); + + bfin_write16(®s->addr_high, val >> 16); + SSYNC(); + + /* Setup DMA count register */ + bfin_write16(®s->count_low, length); + bfin_write16(®s->count_high, 0); + SSYNC(); + + /* Enable the DMA */ + val = (ep << 4) | DMA_ENA | INT_ENA; + if (is_write) + val |= DIRECTION; + bfin_write16(®s->control, val); + SSYNC(); + + /* Wait for compelete */ + while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << ep))) + continue; + + /* acknowledge dma interrupt */ + bfin_write_USB_DMA_INTERRUPT(1 << ep); + SSYNC(); + + /* Reset DMA */ + bfin_write16(®s->control, 0); + SSYNC(); +} + +void write_fifo(u8 ep, u32 length, void *fifo_data) +{ + rw_fifo(ep, length, fifo_data, 1); +} + +void read_fifo(u8 ep, u32 length, void *fifo_data) +{ + rw_fifo(ep, length, fifo_data, 0); +} + + +/* + * CPU and board-specific MUSB initializations. Aliased function + * signals caller to move on. + */ +static void __def_musb_init(void) +{ +} +void board_musb_init(void) __attribute__((weak, alias("__def_musb_init"))); + +static void bfin_anomaly_init(void) +{ + u32 revid; + + if (!ANOMALY_05000346 && !ANOMALY_05000347) + return; + + revid = bfin_revid(); + +#ifdef __ADSPBF54x__ + if (revid > 0) + return; +#endif +#ifdef __ADSPBF52x__ + if (ANOMALY_BF526 && revid > 0) + return; + if (ANOMALY_BF527 && revid > 1) + return; +#endif + + if (ANOMALY_05000346) { + bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value); + SSYNC(); + } + + if (ANOMALY_05000347) { + bfin_write_USB_APHY_CNTRL(0x0); + SSYNC(); + } +} + +int musb_platform_init(void) +{ + /* board specific initialization */ + board_musb_init(); + + bfin_anomaly_init(); + + /* Configure PLL oscillator register */ + bfin_write_USB_PLLOSC_CTRL(0x3080 | + ((480 / CONFIG_USB_BLACKFIN_CLKIN) << 1)); + SSYNC(); + + bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1); + SSYNC(); + + bfin_write_USB_EP_NI0_RXMAXP(64); + SSYNC(); + + bfin_write_USB_EP_NI0_TXMAXP(64); + SSYNC(); + + /* Route INTRUSB/INTR_RX/INTR_TX to USB_INT0*/ + bfin_write_USB_GLOBINTR(0x7); + SSYNC(); + + bfin_write_USB_GLOBAL_CTL(GLOBAL_ENA | EP1_TX_ENA | EP2_TX_ENA | + EP3_TX_ENA | EP4_TX_ENA | EP5_TX_ENA | + EP6_TX_ENA | EP7_TX_ENA | EP1_RX_ENA | + EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA | + EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA); + SSYNC(); + + return 0; +} + +/* + * This function performs Blackfin platform specific deinitialization for usb. +*/ +void musb_platform_deinit(void) +{ +} diff --git a/qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.h b/qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.h new file mode 100644 index 000000000..de994bf33 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/blackfin_usb.h @@ -0,0 +1,99 @@ +/* + * Blackfin MUSB HCD (Host Controller Driver) for u-boot + * + * Copyright (c) 2008-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __BLACKFIN_USB_H__ +#define __BLACKFIN_USB_H__ + +#include + +/* Every register is 32bit aligned, but only 16bits in size */ +#define ureg(name) u16 name; u16 __pad_##name; + +#define musb_regs musb_regs +struct musb_regs { + /* common registers */ + ureg(faddr) + ureg(power) + ureg(intrtx) + ureg(intrrx) + ureg(intrtxe) + ureg(intrrxe) + ureg(intrusb) + ureg(intrusbe) + ureg(frame) + ureg(index) + ureg(testmode) + ureg(globintr) + ureg(global_ctl) + u32 reserved0[3]; + /* indexed registers */ + ureg(txmaxp) + ureg(txcsr) + ureg(rxmaxp) + ureg(rxcsr) + ureg(rxcount) + ureg(txtype) + ureg(txinterval) + ureg(rxtype) + ureg(rxinterval) + u32 reserved1; + ureg(txcount) + u32 reserved2[5]; + /* fifo */ + u16 fifox[32]; + /* OTG, dynamic FIFO, version & vendor registers */ + u32 reserved3[16]; + ureg(devctl) + ureg(vbus_irq) + ureg(vbus_mask) + u32 reserved4[15]; + ureg(linkinfo) + ureg(vplen) + ureg(hseof1) + ureg(fseof1) + ureg(lseof1) + u32 reserved5[41]; + /* target address registers */ + struct musb_tar_regs { + ureg(txmaxp) + ureg(txcsr) + ureg(rxmaxp) + ureg(rxcsr) + ureg(rxcount) + ureg(txtype) + ureg(txinternal) + ureg(rxtype) + ureg(rxinternal) + u32 reserved6; + ureg(txcount) + u32 reserved7[5]; + } tar[8]; +} __attribute__((packed)); + +struct bfin_musb_dma_regs { + ureg(interrupt); + ureg(control); + ureg(addr_low); + ureg(addr_high); + ureg(count_low); + ureg(count_high); + u32 reserved0[2]; +}; + +#undef ureg + +/* EP5-EP7 are the only ones with 1024 byte FIFOs which BULK really needs */ +#define MUSB_BULK_EP 5 + +/* Blackfin FIFO's are static */ +#define MUSB_NO_DYNAMIC_FIFO + +/* No HUB support :( */ +#define MUSB_NO_MULTIPOINT + +#endif diff --git a/qemu/roms/u-boot/drivers/usb/musb/da8xx.c b/qemu/roms/u-boot/drivers/usb/musb/da8xx.c new file mode 100644 index 000000000..97d4ddb57 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/da8xx.c @@ -0,0 +1,128 @@ +/* + * da8xx.c - TI's DA8xx platform specific usb wrapper functions. + * + * Author: Ajay Kumar Gupta + * + * Based on drivers/usb/musb/davinci.c + * + * Copyright (C) 2009 Texas Instruments Incorporated + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include + +#include "musb_core.h" +#include + +/* MUSB platform configuration */ +struct musb_config musb_cfg = { + .regs = (struct musb_regs *)DA8XX_USB_OTG_CORE_BASE, + .timeout = DA8XX_USB_OTG_TIMEOUT, + .musb_speed = 0, +}; + +/* + * This function enables VBUS by driving the GPIO Bank4 Pin 15 high. + */ +static void enable_vbus(void) +{ + u32 value; + + /* configure GPIO bank4 pin 15 in output direction */ + value = readl(&davinci_gpio_bank45->dir); + writel((value & (~DA8XX_USB_VBUS_GPIO)), &davinci_gpio_bank45->dir); + + /* set GPIO bank4 pin 15 high to drive VBUS */ + value = readl(&davinci_gpio_bank45->set_data); + writel((value | DA8XX_USB_VBUS_GPIO), &davinci_gpio_bank45->set_data); +} + +/* + * Enable the usb0 phy. This initialization procedure is explained in + * the DA8xx USB user guide document. + */ +static u8 phy_on(void) +{ + u32 timeout; + u32 cfgchip2; + + cfgchip2 = readl(&davinci_syscfg_regs->cfgchip2); + + cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN | + CFGCHIP2_OTGMODE | CFGCHIP2_REFFREQ); + cfgchip2 |= CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN | CFGCHIP2_PHY_PLLON | + CFGCHIP2_REFFREQ_24MHZ; + + writel(cfgchip2, &davinci_syscfg_regs->cfgchip2); + + /* wait until the usb phy pll locks */ + timeout = musb_cfg.timeout; + while (timeout--) + if (readl(&davinci_syscfg_regs->cfgchip2) & CFGCHIP2_PHYCLKGD) + return 1; + + /* USB phy was not turned on */ + return 0; +} + +/* + * Disable the usb phy + */ +static void phy_off(void) +{ + u32 cfgchip2; + + /* + * Power down the on-chip PHY. + */ + cfgchip2 = readl(&davinci_syscfg_regs->cfgchip2); + cfgchip2 &= ~CFGCHIP2_PHY_PLLON; + cfgchip2 |= CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN; + writel(cfgchip2, &davinci_syscfg_regs->cfgchip2); +} + +/* + * This function performs DA8xx platform specific initialization for usb0. + */ +int musb_platform_init(void) +{ + u32 revision; + + /* enable psc for usb2.0 */ + lpsc_on(33); + + /* enable usb vbus */ + enable_vbus(); + + /* reset the controller */ + writel(0x1, &da8xx_usb_regs->control); + udelay(5000); + + /* start the on-chip usb phy and its pll */ + if (phy_on() == 0) + return -1; + + /* Returns zero if e.g. not clocked */ + revision = readl(&da8xx_usb_regs->revision); + if (revision == 0) + return -1; + + /* Disable all interrupts */ + writel((DA8XX_USB_USBINT_MASK | DA8XX_USB_TXINT_MASK | + DA8XX_USB_RXINT_MASK), &da8xx_usb_regs->intmsk_set); + return 0; +} + +/* + * This function performs DA8xx platform specific deinitialization for usb0. + */ +void musb_platform_deinit(void) +{ + /* Turn of the phy */ + phy_off(); + + /* flush any interrupts */ + writel((DA8XX_USB_USBINT_MASK | DA8XX_USB_TXINT_MASK | + DA8XX_USB_RXINT_MASK), &da8xx_usb_regs->intmsk_clr); + writel(0, &da8xx_usb_regs->eoi); +} diff --git a/qemu/roms/u-boot/drivers/usb/musb/davinci.c b/qemu/roms/u-boot/drivers/usb/musb/davinci.c new file mode 100644 index 000000000..a9707a898 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/davinci.c @@ -0,0 +1,124 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include +#include +#include "davinci.h" +#include + +#if !defined(CONFIG_DV_USBPHY_CTL) +#define CONFIG_DV_USBPHY_CTL (USBPHY_SESNDEN | USBPHY_VBDTCTEN) +#endif + +/* MUSB platform configuration */ +struct musb_config musb_cfg = { + .regs = (struct musb_regs *)MENTOR_USB0_BASE, + .timeout = DAVINCI_USB_TIMEOUT, + .musb_speed = 0, +}; + +/* MUSB module register overlay */ +struct davinci_usb_regs *dregs; + +/* + * Enable the USB phy + */ +static u8 phy_on(void) +{ + u32 timeout; +#ifdef DAVINCI_DM365EVM + u32 val; +#endif + /* Wait until the USB phy is turned on */ +#ifdef DAVINCI_DM365EVM + writel(USBPHY_PHY24MHZ | USBPHY_SESNDEN | + USBPHY_VBDTCTEN, USBPHY_CTL_PADDR); +#else + writel(CONFIG_DV_USBPHY_CTL, USBPHY_CTL_PADDR); +#endif + timeout = musb_cfg.timeout; + +#ifdef DAVINCI_DM365EVM + /* Set the ownership of GIO33 to USB */ + val = readl(PINMUX4); + val &= ~(PINMUX4_USBDRVBUS_BITCLEAR); + val |= PINMUX4_USBDRVBUS_BITSET; + writel(val, PINMUX4); +#endif + while (timeout--) + if (readl(USBPHY_CTL_PADDR) & USBPHY_PHYCLKGD) + return 1; + + /* USB phy was not turned on */ + return 0; +} + +/* + * Disable the USB phy + */ +static void phy_off(void) +{ + /* powerdown the on-chip PHY and its oscillator */ + writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, USBPHY_CTL_PADDR); +} + +void __enable_vbus(void) +{ + /* + * nothing to do, vbus is handled through the cpu. + * Define this function in board code, if it is + * different on your board. + */ +} +void enable_vbus(void) + __attribute__((weak, alias("__enable_vbus"))); + +/* + * This function performs Davinci platform specific initialization for usb0. + */ +int musb_platform_init(void) +{ + u32 revision; + + /* enable USB VBUS */ + enable_vbus(); + + /* start the on-chip USB phy and its pll */ + if (!phy_on()) + return -1; + + /* reset the controller */ + dregs = (struct davinci_usb_regs *)DAVINCI_USB0_BASE; + writel(1, &dregs->ctrlr); + udelay(5000); + + /* Returns zero if e.g. not clocked */ + revision = readl(&dregs->version); + if (!revision) + return -1; + + /* Disable all interrupts */ + writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_RXINT_MASK | + DAVINCI_USB_TXINT_MASK , &dregs->intmsksetr); + return 0; +} + +/* + * This function performs Davinci platform specific deinitialization for usb0. + */ +void musb_platform_deinit(void) +{ + /* Turn of the phy */ + phy_off(); + + /* flush any interrupts */ + writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_TXINT_MASK | + DAVINCI_USB_RXINT_MASK , &dregs->intclrr); +} diff --git a/qemu/roms/u-boot/drivers/usb/musb/davinci.h b/qemu/roms/u-boot/drivers/usb/musb/davinci.h new file mode 100644 index 000000000..9efefe81d --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/davinci.h @@ -0,0 +1,74 @@ +/* + * TI's Davinci platform specific USB wrapper functions. + * + * Copyright (c) 2008 Texas Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __DAVINCI_USB_H__ +#define __DAVINCI_USB_H__ + +#include +#include "musb_core.h" + +/* Base address of DAVINCI usb0 wrapper */ +#define DAVINCI_USB0_BASE 0x01C64000 + +/* Base address of DAVINCI musb core */ +#define MENTOR_USB0_BASE (DAVINCI_USB0_BASE+0x400) + +/* + * Davinci platform USB wrapper register overlay. Note: Only the required + * registers are included in this structure. It can be expanded as required. + */ +struct davinci_usb_regs { + u32 version; + u32 ctrlr; + u32 reserved[0x20]; + u32 intclrr; + u32 intmskr; + u32 intmsksetr; +}; + +#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */ +#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */ +#define DAVINCI_USB_USBINT_SHIFT 16 +#define DAVINCI_USB_TXINT_SHIFT 0 +#define DAVINCI_USB_RXINT_SHIFT 8 +#define DAVINCI_INTR_DRVVBUS 0x0100 + +#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */ +#define DAVINCI_USB_TXINT_MASK \ + (DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT) +#define DAVINCI_USB_RXINT_MASK \ + (DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT) +#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \ + (0x80 + (8*(_bEnd)) + (_bOffset)) + +/* Integrated highspeed/otg PHY */ +#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) +#define USBPHY_PHY24MHZ (1 << 13) +#define USBPHY_PHYCLKGD (1 << 8) +#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */ +#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */ +#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */ +#define USBPHY_CLKO1SEL (1 << 3) +#define USBPHY_OSCPDWN (1 << 2) +#define USBPHY_PHYPDWN (1 << 0) + +/* Timeout for Davinci USB module */ +#define DAVINCI_USB_TIMEOUT 0x3FFFFFF + +/* IO Expander I2C address and VBUS enable mask */ +#define IOEXP_I2C_ADDR 0x3A +#define IOEXP_VBUSEN_MASK 1 + +/* extern functions */ +extern void lpsc_on(unsigned int id); +extern int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len); +extern void enable_vbus(void); +#endif /* __DAVINCI_USB_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb/musb_core.c b/qemu/roms/u-boot/drivers/usb/musb/musb_core.c new file mode 100644 index 000000000..786909fb6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/musb_core.c @@ -0,0 +1,155 @@ +/* + * Mentor USB OTG Core functionality common for both Host and Device + * functionality. + * + * Copyright (c) 2008 Texas Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include + +#include "musb_core.h" +struct musb_regs *musbr; + +/* + * program the mentor core to start (enable interrupts, dma, etc.) + */ +void musb_start(void) +{ +#if defined(CONFIG_MUSB_HCD) + u8 devctl; + u8 busctl; +#endif + + /* disable all interrupts */ + writew(0, &musbr->intrtxe); + writew(0, &musbr->intrrxe); + writeb(0, &musbr->intrusbe); + writeb(0, &musbr->testmode); + + /* put into basic highspeed mode and start session */ + writeb(MUSB_POWER_HSENAB, &musbr->power); +#if defined(CONFIG_MUSB_HCD) + /* Program PHY to use EXT VBUS if required */ + if (musb_cfg.extvbus == 1) { + busctl = musb_read_ulpi_buscontrol(musbr); + musb_write_ulpi_buscontrol(musbr, busctl | ULPI_USE_EXTVBUS); + } + + devctl = readb(&musbr->devctl); + writeb(devctl | MUSB_DEVCTL_SESSION, &musbr->devctl); +#endif +} + +#ifdef MUSB_NO_DYNAMIC_FIFO +# define config_fifo(dir, idx, addr) +#else +# define config_fifo(dir, idx, addr) \ + do { \ + writeb(idx, &musbr->dir##fifosz); \ + writew(fifoaddr >> 3, &musbr->dir##fifoadd); \ + } while (0) +#endif + +/* + * This function configures the endpoint configuration. The musb hcd or musb + * device implementation can use this function to configure the endpoints + * and set the FIFO sizes. Note: The summation of FIFO sizes of all endpoints + * should not be more than the available FIFO size. + * + * epinfo - Pointer to EP configuration table + * cnt - Number of entries in the EP conf table. + */ +void musb_configure_ep(const struct musb_epinfo *epinfo, u8 cnt) +{ + u16 csr; + u16 fifoaddr = 64; /* First 64 bytes of FIFO reserved for EP0 */ + u32 fifosize; + u8 idx; + + while (cnt--) { + /* prepare fifosize to write to register */ + fifosize = epinfo->epsize >> 3; + idx = ffs(fifosize) - 1; + + writeb(epinfo->epnum, &musbr->index); + if (epinfo->epdir) { + /* Configure fifo size and fifo base address */ + config_fifo(tx, idx, fifoaddr); + + csr = readw(&musbr->txcsr); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + writew(csr | MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_TXCSR_TXPKTRDY) + writew(csr | MUSB_TXCSR_FLUSHFIFO, + &musbr->txcsr); + } else { + /* Configure fifo size and fifo base address */ + config_fifo(rx, idx, fifoaddr); + + csr = readw(&musbr->rxcsr); +#if defined(CONFIG_MUSB_HCD) + /* clear the data toggle bit */ + writew(csr | MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); +#endif + /* Flush fifo if required */ + if (csr & MUSB_RXCSR_RXPKTRDY) + writew(csr | MUSB_RXCSR_FLUSHFIFO, + &musbr->rxcsr); + } + fifoaddr += epinfo->epsize; + epinfo++; + } +} + +/* + * This function writes data to endpoint fifo + * + * ep - endpoint number + * length - number of bytes to write to FIFO + * fifo_data - Pointer to data buffer that contains the data to write + */ +__attribute__((weak)) +void write_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* write the data to the fifo */ + while (length--) + writeb(*data++, &musbr->fifox[ep]); +} + +/* + * AM35x supports only 32bit read operations so + * use seperate read_fifo() function for it. + */ +#ifndef CONFIG_USB_AM35X +/* + * This function reads data from endpoint fifo + * + * ep - endpoint number + * length - number of bytes to read from FIFO + * fifo_data - pointer to data buffer into which data is read + */ +__attribute__((weak)) +void read_fifo(u8 ep, u32 length, void *fifo_data) +{ + u8 *data = (u8 *)fifo_data; + + /* select the endpoint index */ + writeb(ep, &musbr->index); + + /* read the data to the fifo */ + while (length--) + *data++ = readb(&musbr->fifox[ep]); +} +#endif /* CONFIG_USB_AM35X */ diff --git a/qemu/roms/u-boot/drivers/usb/musb/musb_core.h b/qemu/roms/u-boot/drivers/usb/musb/musb_core.h new file mode 100644 index 000000000..ec8a038c7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/musb_core.h @@ -0,0 +1,395 @@ +/****************************************************************** + * Copyright 2008 Mentor Graphics Corporation + * Copyright (C) 2008 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + * The Inventra Controller Driver for Linux is distributed in + * the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION + * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE + * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS + * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER. + * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT + * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR + * GRAPHICS SUPPORT CUSTOMER. + ******************************************************************/ + +#ifndef __MUSB_HDRC_DEFS_H__ +#define __MUSB_HDRC_DEFS_H__ + +#include +#include + +#ifdef CONFIG_USB_BLACKFIN +# include "blackfin_usb.h" +#endif + +#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ + +/* EP0 */ +struct musb_ep0_regs { + u16 reserved4; + u16 csr0; + u16 reserved5; + u16 reserved6; + u16 count0; + u8 host_type0; + u8 host_naklimit0; + u8 reserved7; + u8 reserved8; + u8 reserved9; + u8 configdata; +}; + +/* EP 1-15 */ +struct musb_epN_regs { + u16 txmaxp; + u16 txcsr; + u16 rxmaxp; + u16 rxcsr; + u16 rxcount; + u8 txtype; + u8 txinterval; + u8 rxtype; + u8 rxinterval; + u8 reserved0; + u8 fifosize; +}; + +/* Mentor USB core register overlay structure */ +#ifndef musb_regs +struct musb_regs { + /* common registers */ + u8 faddr; + u8 power; + u16 intrtx; + u16 intrrx; + u16 intrtxe; + u16 intrrxe; + u8 intrusb; + u8 intrusbe; + u16 frame; + u8 index; + u8 testmode; + /* indexed registers */ + u16 txmaxp; + u16 txcsr; + u16 rxmaxp; + u16 rxcsr; + u16 rxcount; + u8 txtype; + u8 txinterval; + u8 rxtype; + u8 rxinterval; + u8 reserved0; + u8 fifosize; + /* fifo */ + u32 fifox[16]; + /* OTG, dynamic FIFO, version & vendor registers */ + u8 devctl; + u8 reserved1; + u8 txfifosz; + u8 rxfifosz; + u16 txfifoadd; + u16 rxfifoadd; + u32 vcontrol; + u16 hwvers; + u16 reserved2a[1]; + u8 ulpi_busctl; + u8 reserved2b[1]; + u16 reserved2[3]; + u8 epinfo; + u8 raminfo; + u8 linkinfo; + u8 vplen; + u8 hseof1; + u8 fseof1; + u8 lseof1; + u8 reserved3; + /* target address registers */ + struct musb_tar_regs { + u8 txfuncaddr; + u8 reserved0; + u8 txhubaddr; + u8 txhubport; + u8 rxfuncaddr; + u8 reserved1; + u8 rxhubaddr; + u8 rxhubport; + } tar[16]; + /* + * endpoint registers + * ep0 elements are valid when array index is 0 + * otherwise epN is valid + */ + union musb_ep_regs { + struct musb_ep0_regs ep0; + struct musb_epN_regs epN; + } ep[16]; + +} __attribute__((packed)); +#endif + +/* + * MUSB Register bits + */ + +/* POWER */ +#define MUSB_POWER_ISOUPDATE 0x80 +#define MUSB_POWER_SOFTCONN 0x40 +#define MUSB_POWER_HSENAB 0x20 +#define MUSB_POWER_HSMODE 0x10 +#define MUSB_POWER_RESET 0x08 +#define MUSB_POWER_RESUME 0x04 +#define MUSB_POWER_SUSPENDM 0x02 +#define MUSB_POWER_ENSUSPEND 0x01 +#define MUSB_POWER_HSMODE_SHIFT 4 + +/* INTRUSB */ +#define MUSB_INTR_SUSPEND 0x01 +#define MUSB_INTR_RESUME 0x02 +#define MUSB_INTR_RESET 0x04 +#define MUSB_INTR_BABBLE 0x04 +#define MUSB_INTR_SOF 0x08 +#define MUSB_INTR_CONNECT 0x10 +#define MUSB_INTR_DISCONNECT 0x20 +#define MUSB_INTR_SESSREQ 0x40 +#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */ + +/* DEVCTL */ +#define MUSB_DEVCTL_BDEVICE 0x80 +#define MUSB_DEVCTL_FSDEV 0x40 +#define MUSB_DEVCTL_LSDEV 0x20 +#define MUSB_DEVCTL_VBUS 0x18 +#define MUSB_DEVCTL_VBUS_SHIFT 3 +#define MUSB_DEVCTL_HM 0x04 +#define MUSB_DEVCTL_HR 0x02 +#define MUSB_DEVCTL_SESSION 0x01 + +/* ULPI VBUSCONTROL */ +#define ULPI_USE_EXTVBUS 0x01 +#define ULPI_USE_EXTVBUSIND 0x02 + +/* TESTMODE */ +#define MUSB_TEST_FORCE_HOST 0x80 +#define MUSB_TEST_FIFO_ACCESS 0x40 +#define MUSB_TEST_FORCE_FS 0x20 +#define MUSB_TEST_FORCE_HS 0x10 +#define MUSB_TEST_PACKET 0x08 +#define MUSB_TEST_K 0x04 +#define MUSB_TEST_J 0x02 +#define MUSB_TEST_SE0_NAK 0x01 + +/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */ +#define MUSB_FIFOSZ_DPB 0x10 +/* Allocation size (8, 16, 32, ... 4096) */ +#define MUSB_FIFOSZ_SIZE 0x0f + +/* CSR0 */ +#define MUSB_CSR0_FLUSHFIFO 0x0100 +#define MUSB_CSR0_TXPKTRDY 0x0002 +#define MUSB_CSR0_RXPKTRDY 0x0001 + +/* CSR0 in Peripheral mode */ +#define MUSB_CSR0_P_SVDSETUPEND 0x0080 +#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040 +#define MUSB_CSR0_P_SENDSTALL 0x0020 +#define MUSB_CSR0_P_SETUPEND 0x0010 +#define MUSB_CSR0_P_DATAEND 0x0008 +#define MUSB_CSR0_P_SENTSTALL 0x0004 + +/* CSR0 in Host mode */ +#define MUSB_CSR0_H_DIS_PING 0x0800 +#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */ +#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */ +#define MUSB_CSR0_H_NAKTIMEOUT 0x0080 +#define MUSB_CSR0_H_STATUSPKT 0x0040 +#define MUSB_CSR0_H_REQPKT 0x0020 +#define MUSB_CSR0_H_ERROR 0x0010 +#define MUSB_CSR0_H_SETUPPKT 0x0008 +#define MUSB_CSR0_H_RXSTALL 0x0004 + +/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_CSR0_P_WZC_BITS \ + (MUSB_CSR0_P_SENTSTALL) +#define MUSB_CSR0_H_WZC_BITS \ + (MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \ + | MUSB_CSR0_RXPKTRDY) + +/* TxType/RxType */ +#define MUSB_TYPE_SPEED 0xc0 +#define MUSB_TYPE_SPEED_SHIFT 6 +#define MUSB_TYPE_SPEED_HIGH 1 +#define MUSB_TYPE_SPEED_FULL 2 +#define MUSB_TYPE_SPEED_LOW 3 +#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_SHIFT 4 +#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */ +#define MUSB_TYPE_PROTO_BULK 2 +#define MUSB_TYPE_PROTO_INTR 3 + +/* CONFIGDATA */ +#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */ +#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */ +#define MUSB_CONFIGDATA_BIGENDIAN 0x20 +#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ +#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ +#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */ +#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ +#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */ + +/* TXCSR in Peripheral and Host mode */ +#define MUSB_TXCSR_AUTOSET 0x8000 +#define MUSB_TXCSR_MODE 0x2000 +#define MUSB_TXCSR_DMAENAB 0x1000 +#define MUSB_TXCSR_FRCDATATOG 0x0800 +#define MUSB_TXCSR_DMAMODE 0x0400 +#define MUSB_TXCSR_CLRDATATOG 0x0040 +#define MUSB_TXCSR_FLUSHFIFO 0x0008 +#define MUSB_TXCSR_FIFONOTEMPTY 0x0002 +#define MUSB_TXCSR_TXPKTRDY 0x0001 + +/* TXCSR in Peripheral mode */ +#define MUSB_TXCSR_P_ISO 0x4000 +#define MUSB_TXCSR_P_INCOMPTX 0x0080 +#define MUSB_TXCSR_P_SENTSTALL 0x0020 +#define MUSB_TXCSR_P_SENDSTALL 0x0010 +#define MUSB_TXCSR_P_UNDERRUN 0x0004 + +/* TXCSR in Host mode */ +#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200 +#define MUSB_TXCSR_H_DATATOGGLE 0x0100 +#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080 +#define MUSB_TXCSR_H_RXSTALL 0x0020 +#define MUSB_TXCSR_H_ERROR 0x0004 +#define MUSB_TXCSR_H_DATATOGGLE_SHIFT 8 + +/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_TXCSR_P_WZC_BITS \ + (MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \ + | MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY) +#define MUSB_TXCSR_H_WZC_BITS \ + (MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \ + | MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY) + +/* RXCSR in Peripheral and Host mode */ +#define MUSB_RXCSR_AUTOCLEAR 0x8000 +#define MUSB_RXCSR_DMAENAB 0x2000 +#define MUSB_RXCSR_DISNYET 0x1000 +#define MUSB_RXCSR_PID_ERR 0x1000 +#define MUSB_RXCSR_DMAMODE 0x0800 +#define MUSB_RXCSR_INCOMPRX 0x0100 +#define MUSB_RXCSR_CLRDATATOG 0x0080 +#define MUSB_RXCSR_FLUSHFIFO 0x0010 +#define MUSB_RXCSR_DATAERROR 0x0008 +#define MUSB_RXCSR_FIFOFULL 0x0002 +#define MUSB_RXCSR_RXPKTRDY 0x0001 + +/* RXCSR in Peripheral mode */ +#define MUSB_RXCSR_P_ISO 0x4000 +#define MUSB_RXCSR_P_SENTSTALL 0x0040 +#define MUSB_RXCSR_P_SENDSTALL 0x0020 +#define MUSB_RXCSR_P_OVERRUN 0x0004 + +/* RXCSR in Host mode */ +#define MUSB_RXCSR_H_AUTOREQ 0x4000 +#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400 +#define MUSB_RXCSR_H_DATATOGGLE 0x0200 +#define MUSB_RXCSR_H_RXSTALL 0x0040 +#define MUSB_RXCSR_H_REQPKT 0x0020 +#define MUSB_RXCSR_H_ERROR 0x0004 +#define MUSB_S_RXCSR_H_DATATOGGLE 9 + +/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MUSB_RXCSR_P_WZC_BITS \ + (MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \ + | MUSB_RXCSR_RXPKTRDY) +#define MUSB_RXCSR_H_WZC_BITS \ + (MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \ + | MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY) + +/* HUBADDR */ +#define MUSB_HUBADDR_MULTI_TT 0x80 + +/* Endpoint configuration information. Note: The value of endpoint fifo size + * element should be either 8,16,32,64,128,256,512,1024,2048 or 4096. Other + * values are not supported + */ +struct musb_epinfo { + u8 epnum; /* endpoint number */ + u8 epdir; /* endpoint direction */ + u16 epsize; /* endpoint FIFO size */ +}; + +/* + * Platform specific MUSB configuration. Any platform using the musb + * functionality should create one instance of this structure in the + * platform specific file. + */ +struct musb_config { + struct musb_regs *regs; + u32 timeout; + u8 musb_speed; + u8 extvbus; +}; + +/* externally defined data */ +extern struct musb_config musb_cfg; +extern struct musb_regs *musbr; + +/* exported functions */ +extern void musb_start(void); +extern void musb_configure_ep(const struct musb_epinfo *epinfo, u8 cnt); +extern void write_fifo(u8 ep, u32 length, void *fifo_data); +extern void read_fifo(u8 ep, u32 length, void *fifo_data); + +#if defined(CONFIG_USB_BLACKFIN) +/* Every USB register is accessed as a 16-bit even if the value itself + * is only 8-bits in size. Fun stuff. + */ +# undef readb +# define readb(addr) (u8)bfin_read16(addr) +# undef writeb +# define writeb(b, addr) bfin_write16(addr, b) +# undef MUSB_TXCSR_MODE /* not supported */ +# define MUSB_TXCSR_MODE 0 +/* + * The USB PHY on current Blackfin processors is a UTMI+ level 2 PHY. + * However, it has no ULPI support - so there are no registers at all. + * That means accesses to ULPI_BUSCONTROL have to be abstracted away. + */ +static inline u8 musb_read_ulpi_buscontrol(struct musb_regs *musbr) +{ + return 0; +} +static inline void musb_write_ulpi_buscontrol(struct musb_regs *musbr, u8 val) +{} +#else +static inline u8 musb_read_ulpi_buscontrol(struct musb_regs *musbr) +{ + return readb(&musbr->ulpi_busctl); +} +static inline void musb_write_ulpi_buscontrol(struct musb_regs *musbr, u8 val) +{ + writeb(val, &musbr->ulpi_busctl); +} +#endif + +#endif /* __MUSB_HDRC_DEFS_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb/musb_debug.h b/qemu/roms/u-boot/drivers/usb/musb/musb_debug.h new file mode 100644 index 000000000..b387fc36e --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/musb_debug.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Define MUSB_DEBUG before including this file to get debug macros */ +#ifdef MUSB_DEBUG + +#define MUSB_FLAGS_PRINT(v, x, y) \ + if (((v) & MUSB_##x##_##y)) \ + serial_printf("\t\t"#y"\n") + +static inline void musb_print_pwr(u8 b) +{ + serial_printf("\tpower 0x%2.2x\n", b); + MUSB_FLAGS_PRINT(b, POWER, ISOUPDATE); + MUSB_FLAGS_PRINT(b, POWER, SOFTCONN); + MUSB_FLAGS_PRINT(b, POWER, HSENAB); + MUSB_FLAGS_PRINT(b, POWER, HSMODE); + MUSB_FLAGS_PRINT(b, POWER, RESET); + MUSB_FLAGS_PRINT(b, POWER, RESUME); + MUSB_FLAGS_PRINT(b, POWER, SUSPENDM); + MUSB_FLAGS_PRINT(b, POWER, ENSUSPEND); +} + +static inline void musb_print_csr0(u16 w) +{ + serial_printf("\tcsr0 0x%4.4x\n", w); + MUSB_FLAGS_PRINT(w, CSR0, FLUSHFIFO); + MUSB_FLAGS_PRINT(w, CSR0_P, SVDSETUPEND); + MUSB_FLAGS_PRINT(w, CSR0_P, SVDRXPKTRDY); + MUSB_FLAGS_PRINT(w, CSR0_P, SENDSTALL); + MUSB_FLAGS_PRINT(w, CSR0_P, SETUPEND); + MUSB_FLAGS_PRINT(w, CSR0_P, DATAEND); + MUSB_FLAGS_PRINT(w, CSR0_P, SENTSTALL); + MUSB_FLAGS_PRINT(w, CSR0, TXPKTRDY); + MUSB_FLAGS_PRINT(w, CSR0, RXPKTRDY); +} + +static inline void musb_print_intrusb(u8 b) +{ + serial_printf("\tintrusb 0x%2.2x\n", b); + MUSB_FLAGS_PRINT(b, INTR, VBUSERROR); + MUSB_FLAGS_PRINT(b, INTR, SESSREQ); + MUSB_FLAGS_PRINT(b, INTR, DISCONNECT); + MUSB_FLAGS_PRINT(b, INTR, CONNECT); + MUSB_FLAGS_PRINT(b, INTR, SOF); + MUSB_FLAGS_PRINT(b, INTR, RESUME); + MUSB_FLAGS_PRINT(b, INTR, SUSPEND); + + if (b & MUSB_INTR_BABBLE) + serial_printf("\t\tMUSB_INTR_RESET or MUSB_INTR_BABBLE\n"); + +} + +static inline void musb_print_intrtx(u16 w) +{ + serial_printf("\tintrtx 0x%4.4x\n", w); +} + +static inline void musb_print_intrrx(u16 w) +{ + serial_printf("\tintrx 0x%4.4x\n", w); +} + +static inline void musb_print_devctl(u8 b) +{ + serial_printf("\tdevctl 0x%2.2x\n", b); + if (b & MUSB_DEVCTL_BDEVICE) + serial_printf("\t\tB device\n"); + else + serial_printf("\t\tA device\n"); + if (b & MUSB_DEVCTL_FSDEV) + serial_printf("\t\tFast Device -(host mode)\n"); + if (b & MUSB_DEVCTL_LSDEV) + serial_printf("\t\tSlow Device -(host mode)\n"); + if (b & MUSB_DEVCTL_HM) + serial_printf("\t\tHost mode\n"); + else + serial_printf("\t\tPeripherial mode\n"); + if (b & MUSB_DEVCTL_HR) + serial_printf("\t\tHost request started(B device)\n"); + else + serial_printf("\t\tHost request finished(B device)\n"); + if (b & MUSB_DEVCTL_BDEVICE) { + if (b & MUSB_DEVCTL_SESSION) + serial_printf("\t\tStart of session(B device)\n"); + else + serial_printf("\t\tEnd of session(B device)\n"); + } else { + if (b & MUSB_DEVCTL_SESSION) + serial_printf("\t\tStart of session(A device)\n"); + else + serial_printf("\t\tEnd of session(A device)\n"); + } +} + +static inline void musb_print_config(u8 b) +{ + serial_printf("\tconfig 0x%2.2x\n", b); + if (b & MUSB_CONFIGDATA_MPRXE) + serial_printf("\t\tAuto combine rx bulk packets\n"); + if (b & MUSB_CONFIGDATA_MPTXE) + serial_printf("\t\tAuto split tx bulk packets\n"); + if (b & MUSB_CONFIGDATA_BIGENDIAN) + serial_printf("\t\tBig Endian ordering\n"); + else + serial_printf("\t\tLittle Endian ordering\n"); + if (b & MUSB_CONFIGDATA_HBRXE) + serial_printf("\t\tHigh speed rx iso endpoint\n"); + if (b & MUSB_CONFIGDATA_HBTXE) + serial_printf("\t\tHigh speed tx iso endpoint\n"); + if (b & MUSB_CONFIGDATA_DYNFIFO) + serial_printf("\t\tDynamic fifo sizing\n"); + if (b & MUSB_CONFIGDATA_SOFTCONE) + serial_printf("\t\tSoft Connect\n"); + if (b & MUSB_CONFIGDATA_UTMIDW) + serial_printf("\t\t16 bit data width\n"); + else + serial_printf("\t\t8 bit data width\n"); +} + +static inline void musb_print_rxmaxp(u16 w) +{ + serial_printf("\trxmaxp 0x%4.4x\n", w); +} + +static inline void musb_print_rxcsr(u16 w) +{ + serial_printf("\trxcsr 0x%4.4x\n", w); + MUSB_FLAGS_PRINT(w, RXCSR, AUTOCLEAR); + MUSB_FLAGS_PRINT(w, RXCSR, DMAENAB); + MUSB_FLAGS_PRINT(w, RXCSR, DISNYET); + MUSB_FLAGS_PRINT(w, RXCSR, PID_ERR); + MUSB_FLAGS_PRINT(w, RXCSR, DMAMODE); + MUSB_FLAGS_PRINT(w, RXCSR, CLRDATATOG); + MUSB_FLAGS_PRINT(w, RXCSR, FLUSHFIFO); + MUSB_FLAGS_PRINT(w, RXCSR, DATAERROR); + MUSB_FLAGS_PRINT(w, RXCSR, FIFOFULL); + MUSB_FLAGS_PRINT(w, RXCSR, RXPKTRDY); + MUSB_FLAGS_PRINT(w, RXCSR_P, SENTSTALL); + MUSB_FLAGS_PRINT(w, RXCSR_P, SENDSTALL); + MUSB_FLAGS_PRINT(w, RXCSR_P, OVERRUN); + + if (w & MUSB_RXCSR_P_ISO) + serial_printf("\t\tiso mode\n"); + else + serial_printf("\t\tbulk mode\n"); + +} + +static inline void musb_print_txmaxp(u16 w) +{ + serial_printf("\ttxmaxp 0x%4.4x\n", w); +} + +static inline void musb_print_txcsr(u16 w) +{ + serial_printf("\ttxcsr 0x%4.4x\n", w); + MUSB_FLAGS_PRINT(w, TXCSR, TXPKTRDY); + MUSB_FLAGS_PRINT(w, TXCSR, FIFONOTEMPTY); + MUSB_FLAGS_PRINT(w, TXCSR, FLUSHFIFO); + MUSB_FLAGS_PRINT(w, TXCSR, CLRDATATOG); + MUSB_FLAGS_PRINT(w, TXCSR_P, UNDERRUN); + MUSB_FLAGS_PRINT(w, TXCSR_P, SENTSTALL); + MUSB_FLAGS_PRINT(w, TXCSR_P, SENDSTALL); + + if (w & MUSB_TXCSR_MODE) + serial_printf("\t\tTX mode\n"); + else + serial_printf("\t\tRX mode\n"); +} + +#else + +/* stubs */ + +#define musb_print_pwr(b) +#define musb_print_csr0(w) +#define musb_print_intrusb(b) +#define musb_print_intrtx(w) +#define musb_print_intrrx(w) +#define musb_print_devctl(b) +#define musb_print_config(b) +#define musb_print_rxmaxp(w) +#define musb_print_rxcsr(w) +#define musb_print_txmaxp(w) +#define musb_print_txcsr(w) + +#endif /* MUSB_DEBUG */ diff --git a/qemu/roms/u-boot/drivers/usb/musb/musb_hcd.c b/qemu/roms/u-boot/drivers/usb/musb/musb_hcd.c new file mode 100644 index 000000000..f0ba8aaaa --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/musb_hcd.c @@ -0,0 +1,1172 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include +#include +#include "musb_hcd.h" + +/* MSC control transfers */ +#define USB_MSC_BBB_RESET 0xFF +#define USB_MSC_BBB_GET_MAX_LUN 0xFE + +/* Endpoint configuration information */ +static const struct musb_epinfo epinfo[3] = { + {MUSB_BULK_EP, 1, 512}, /* EP1 - Bluk Out - 512 Bytes */ + {MUSB_BULK_EP, 0, 512}, /* EP1 - Bluk In - 512 Bytes */ + {MUSB_INTR_EP, 0, 64} /* EP2 - Interrupt IN - 64 Bytes */ +}; + +/* --- Virtual Root Hub ---------------------------------------------------- */ +#ifdef MUSB_NO_MULTIPOINT +static int rh_devnum; +static u32 port_status; + +#include + +#endif + +/* + * This function writes the data toggle value. + */ +static void write_toggle(struct usb_device *dev, u8 ep, u8 dir_out) +{ + u16 toggle = usb_gettoggle(dev, ep, dir_out); + u16 csr; + + if (dir_out) { + csr = readw(&musbr->txcsr); + if (!toggle) { + if (csr & MUSB_TXCSR_MODE) + csr = MUSB_TXCSR_CLRDATATOG; + else + csr = 0; + writew(csr, &musbr->txcsr); + } else { + csr |= MUSB_TXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->txcsr); + csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT); + writew(csr, &musbr->txcsr); + } + } else { + if (!toggle) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_MODE) + csr = MUSB_RXCSR_CLRDATATOG; + else + csr = 0; + writew(csr, &musbr->rxcsr); + } else { + csr = readw(&musbr->rxcsr); + csr |= MUSB_RXCSR_H_WR_DATATOGGLE; + writew(csr, &musbr->rxcsr); + csr |= (toggle << MUSB_S_RXCSR_H_DATATOGGLE); + writew(csr, &musbr->rxcsr); + } + } +} + +/* + * This function checks if RxStall has occured on the endpoint. If a RxStall + * has occured, the RxStall is cleared and 1 is returned. If RxStall has + * not occured, 0 is returned. + */ +static u8 check_stall(u8 ep, u8 dir_out) +{ + u16 csr; + + /* For endpoint 0 */ + if (!ep) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_RXSTALL) { + csr &= ~MUSB_CSR0_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* For non-ep0 */ + if (dir_out) { /* is it tx ep */ + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_RXSTALL) { + csr &= ~MUSB_TXCSR_H_RXSTALL; + writew(csr, &musbr->txcsr); + return 1; + } + } else { /* is it rx ep */ + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_RXSTALL) { + csr &= ~MUSB_RXCSR_H_RXSTALL; + writew(csr, &musbr->rxcsr); + return 1; + } + } + } + return 0; +} + +/* + * waits until ep0 is ready. Returns 0 if ep is ready, -1 for timeout + * error and -2 for stall. + */ +static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask) +{ + u16 csr; + int result = 1; + int timeout = CONFIG_MUSB_TIMEOUT; + + while (result > 0) { + csr = readw(&musbr->txcsr); + if (csr & MUSB_CSR0_H_ERROR) { + csr &= ~MUSB_CSR0_H_ERROR; + writew(csr, &musbr->txcsr); + dev->status = USB_ST_CRC_ERR; + result = -1; + break; + } + + switch (bit_mask) { + case MUSB_CSR0_TXPKTRDY: + if (!(csr & MUSB_CSR0_TXPKTRDY)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + + case MUSB_CSR0_RXPKTRDY: + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + if (csr & MUSB_CSR0_RXPKTRDY) + result = 0; + break; + + case MUSB_CSR0_H_REQPKT: + if (!(csr & MUSB_CSR0_H_REQPKT)) { + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + result = -2; + } else + result = 0; + } + break; + } + + /* Check the timeout */ + if (--timeout) + udelay(1); + else { + dev->status = USB_ST_CRC_ERR; + result = -1; + break; + } + } + + return result; +} + +/* + * waits until tx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static int wait_until_txep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + int timeout = CONFIG_MUSB_TIMEOUT; + + do { + if (check_stall(ep, 1)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->txcsr); + if (csr & MUSB_TXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + + /* Check the timeout */ + if (--timeout) + udelay(1); + else { + dev->status = USB_ST_CRC_ERR; + return -1; + } + + } while (csr & MUSB_TXCSR_TXPKTRDY); + return 1; +} + +/* + * waits until rx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static int wait_until_rxep_ready(struct usb_device *dev, u8 ep) +{ + u16 csr; + int timeout = CONFIG_MUSB_TIMEOUT; + + do { + if (check_stall(ep, 0)) { + dev->status = USB_ST_STALLED; + return 0; + } + + csr = readw(&musbr->rxcsr); + if (csr & MUSB_RXCSR_H_ERROR) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + + /* Check the timeout */ + if (--timeout) + udelay(1); + else { + dev->status = USB_ST_CRC_ERR; + return -1; + } + + } while (!(csr & MUSB_RXCSR_RXPKTRDY)); + return 1; +} + +/* + * This function performs the setup phase of the control transfer + */ +static int ctrlreq_setup_phase(struct usb_device *dev, struct devrequest *setup) +{ + int result; + u16 csr; + + /* write the control request to ep0 fifo */ + write_fifo(MUSB_CONTROL_EP, sizeof(struct devrequest), (void *)setup); + + /* enable transfer of setup packet */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_TXPKTRDY|MUSB_CSR0_H_SETUPPKT); + writew(csr, &musbr->txcsr); + + /* wait until the setup packet is transmitted */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + dev->act_len = 0; + return result; +} + +/* + * This function handles the control transfer in data phase + */ +static int ctrlreq_in_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 rxlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *rxbuff = (u8 *)buffer; + u8 rxedlength; + int result; + + while (rxlen < len) { + /* Determine the next read length */ + nextlen = ((len-rxlen) > maxpktsize) ? maxpktsize : (len-rxlen); + + /* Set the ReqPkt bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_CSR0_H_REQPKT, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_RXPKTRDY); + if (result < 0) + return result; + + /* Actual number of bytes received by usb */ + rxedlength = readb(&musbr->rxcount); + + /* Read the data from the RxFIFO */ + read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]); + + /* Clear the RxPktRdy Bit */ + csr = readw(&musbr->txcsr); + csr &= ~MUSB_CSR0_RXPKTRDY; + writew(csr, &musbr->txcsr); + + /* short packet? */ + if (rxedlength != nextlen) { + dev->act_len += rxedlength; + break; + } + rxlen += nextlen; + dev->act_len = rxlen; + } + return 0; +} + +/* + * This function handles the control transfer out data phase + */ +static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 maxpktsize = (1 << dev->maxpacketsize) * 8; + u8 *txbuff = (u8 *)buffer; + int result = 0; + + while (txlen < len) { + /* Determine the next write length */ + nextlen = ((len-txlen) > maxpktsize) ? maxpktsize : (len-txlen); + + /* Load the data to send in FIFO */ + write_fifo(MUSB_CONTROL_EP, txlen, &txbuff[txlen]); + + /* Set TXPKTRDY bit */ + csr = readw(&musbr->txcsr); + + csr |= MUSB_CSR0_TXPKTRDY; +#if !defined(CONFIG_SOC_DM365) + csr |= MUSB_CSR0_H_DIS_PING; +#endif + writew(csr, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + if (result < 0) + break; + + txlen += nextlen; + dev->act_len = txlen; + } + return result; +} + +/* + * This function handles the control transfer out status phase + */ +static int ctrlreq_out_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit */ + csr = readw(&musbr->txcsr); + csr |= (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_H_STATUSPKT); +#if !defined(CONFIG_SOC_DM365) + csr |= MUSB_CSR0_H_DIS_PING; +#endif + writew(csr, &musbr->txcsr); + + /* Wait until TXPKTRDY bit is cleared */ + result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); + return result; +} + +/* + * This function handles the control transfer in status phase + */ +static int ctrlreq_in_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit and ReqPkt bit */ + csr = MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; +#if !defined(CONFIG_SOC_DM365) + csr |= MUSB_CSR0_H_DIS_PING; +#endif + writew(csr, &musbr->txcsr); + result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT); + + /* clear StatusPkt bit and RxPktRdy bit */ + csr = readw(&musbr->txcsr); + csr &= ~(MUSB_CSR0_RXPKTRDY | MUSB_CSR0_H_STATUSPKT); + writew(csr, &musbr->txcsr); + return result; +} + +/* + * determines the speed of the device (High/Full/Slow) + */ +static u8 get_dev_speed(struct usb_device *dev) +{ + return (dev->speed == USB_SPEED_HIGH) ? MUSB_TYPE_SPEED_HIGH : + ((dev->speed == USB_SPEED_LOW) ? MUSB_TYPE_SPEED_LOW : + MUSB_TYPE_SPEED_FULL); +} + +/* + * configure the hub address and the port address. + */ +static void config_hub_port(struct usb_device *dev, u8 ep) +{ + u8 chid; + u8 hub; + + /* Find out the nearest parent which is high speed */ + while (dev->parent->parent != NULL) + if (get_dev_speed(dev->parent) != MUSB_TYPE_SPEED_HIGH) + dev = dev->parent; + else + break; + + /* determine the port address at that hub */ + hub = dev->parent->devnum; + for (chid = 0; chid < USB_MAXCHILDREN; chid++) + if (dev->parent->children[chid] == dev) + break; + +#ifndef MUSB_NO_MULTIPOINT + /* configure the hub address and the port address */ + writeb(hub, &musbr->tar[ep].txhubaddr); + writeb((chid + 1), &musbr->tar[ep].txhubport); + writeb(hub, &musbr->tar[ep].rxhubaddr); + writeb((chid + 1), &musbr->tar[ep].rxhubport); +#endif +} + +#ifdef MUSB_NO_MULTIPOINT + +static void musb_port_reset(int do_reset) +{ + u8 power = readb(&musbr->power); + + if (do_reset) { + power &= 0xf0; + writeb(power | MUSB_POWER_RESET, &musbr->power); + port_status |= USB_PORT_STAT_RESET; + port_status &= ~USB_PORT_STAT_ENABLE; + udelay(30000); + } else { + writeb(power & ~MUSB_POWER_RESET, &musbr->power); + + power = readb(&musbr->power); + if (power & MUSB_POWER_HSMODE) + port_status |= USB_PORT_STAT_HIGH_SPEED; + + port_status &= ~(USB_PORT_STAT_RESET | (USB_PORT_STAT_C_CONNECTION << 16)); + port_status |= USB_PORT_STAT_ENABLE + | (USB_PORT_STAT_C_RESET << 16) + | (USB_PORT_STAT_C_ENABLE << 16); + } +} + +/* + * root hub control + */ +static int musb_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *cmd) +{ + int leni = transfer_len; + int len = 0; + int stat = 0; + u32 datab[4]; + const u8 *data_buf = (u8 *) datab; + u16 bmRType_bReq; + u16 wValue; + u16 wIndex; + u16 wLength; + u16 int_usb; + + if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { + debug("Root-Hub submit IRQ: NOT implemented\n"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = swap_16(cmd->value); + wIndex = swap_16(cmd->index); + wLength = swap_16(cmd->length); + + debug("--- HUB ----------------------------------------\n"); + debug("submit rh urb, req=%x val=%#x index=%#x len=%d\n", + bmRType_bReq, wValue, wIndex, wLength); + debug("------------------------------------------------\n"); + + switch (bmRType_bReq) { + case RH_GET_STATUS: + debug("RH_GET_STATUS\n"); + + *(__u16 *) data_buf = swap_16(1); + len = 2; + break; + + case RH_GET_STATUS | RH_INTERFACE: + debug("RH_GET_STATUS | RH_INTERFACE\n"); + + *(__u16 *) data_buf = swap_16(0); + len = 2; + break; + + case RH_GET_STATUS | RH_ENDPOINT: + debug("RH_GET_STATUS | RH_ENDPOINT\n"); + + *(__u16 *) data_buf = swap_16(0); + len = 2; + break; + + case RH_GET_STATUS | RH_CLASS: + debug("RH_GET_STATUS | RH_CLASS\n"); + + *(__u32 *) data_buf = swap_32(0); + len = 4; + break; + + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + debug("RH_GET_STATUS | RH_OTHER | RH_CLASS\n"); + + int_usb = readw(&musbr->intrusb); + if (int_usb & MUSB_INTR_CONNECT) { + port_status |= USB_PORT_STAT_CONNECTION + | (USB_PORT_STAT_C_CONNECTION << 16); + port_status |= USB_PORT_STAT_HIGH_SPEED + | USB_PORT_STAT_ENABLE; + } + + if (port_status & USB_PORT_STAT_RESET) + musb_port_reset(0); + + *(__u32 *) data_buf = swap_32(port_status); + len = 4; + break; + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + debug("RH_CLEAR_FEATURE | RH_ENDPOINT\n"); + + switch (wValue) { + case RH_ENDPOINT_STALL: + debug("C_HUB_ENDPOINT_STALL\n"); + len = 0; + break; + } + port_status &= ~(1 << wValue); + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + debug("RH_CLEAR_FEATURE | RH_CLASS\n"); + + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + debug("C_HUB_LOCAL_POWER\n"); + len = 0; + break; + + case RH_C_HUB_OVER_CURRENT: + debug("C_HUB_OVER_CURRENT\n"); + len = 0; + break; + } + port_status &= ~(1 << wValue); + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + debug("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS\n"); + + switch (wValue) { + case RH_PORT_ENABLE: + len = 0; + break; + + case RH_PORT_SUSPEND: + len = 0; + break; + + case RH_PORT_POWER: + len = 0; + break; + + case RH_C_PORT_CONNECTION: + len = 0; + break; + + case RH_C_PORT_ENABLE: + len = 0; + break; + + case RH_C_PORT_SUSPEND: + len = 0; + break; + + case RH_C_PORT_OVER_CURRENT: + len = 0; + break; + + case RH_C_PORT_RESET: + len = 0; + break; + + default: + debug("invalid wValue\n"); + stat = USB_ST_STALLED; + } + + port_status &= ~(1 << wValue); + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + debug("RH_SET_FEATURE | RH_OTHER | RH_CLASS\n"); + + switch (wValue) { + case RH_PORT_SUSPEND: + len = 0; + break; + + case RH_PORT_RESET: + musb_port_reset(1); + len = 0; + break; + + case RH_PORT_POWER: + len = 0; + break; + + case RH_PORT_ENABLE: + len = 0; + break; + + default: + debug("invalid wValue\n"); + stat = USB_ST_STALLED; + } + + port_status |= 1 << wValue; + break; + + case RH_SET_ADDRESS: + debug("RH_SET_ADDRESS\n"); + + rh_devnum = wValue; + len = 0; + break; + + case RH_GET_DESCRIPTOR: + debug("RH_GET_DESCRIPTOR: %x, %d\n", wValue, wLength); + + switch (wValue) { + case (USB_DT_DEVICE << 8): /* device descriptor */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; + break; + + case (USB_DT_CONFIG << 8): /* configuration descriptor */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; + break; + + case ((USB_DT_STRING << 8) | 0x00): /* string 0 descriptors */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + break; + + case ((USB_DT_STRING << 8) | 0x01): /* string 1 descriptors */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + break; + + default: + debug("invalid wValue\n"); + stat = USB_ST_STALLED; + } + + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: { + u8 *_data_buf = (u8 *) datab; + debug("RH_GET_DESCRIPTOR | RH_CLASS\n"); + + _data_buf[0] = 0x09; /* min length; */ + _data_buf[1] = 0x29; + _data_buf[2] = 0x1; /* 1 port */ + _data_buf[3] = 0x01; /* per-port power switching */ + _data_buf[3] |= 0x10; /* no overcurrent reporting */ + + /* Corresponds to data_buf[4-7] */ + _data_buf[4] = 0; + _data_buf[5] = 5; + _data_buf[6] = 0; + _data_buf[7] = 0x02; + _data_buf[8] = 0xff; + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf[0], wLength)); + break; + } + + case RH_GET_CONFIGURATION: + debug("RH_GET_CONFIGURATION\n"); + + *(__u8 *) data_buf = 0x01; + len = 1; + break; + + case RH_SET_CONFIGURATION: + debug("RH_SET_CONFIGURATION\n"); + + len = 0; + break; + + default: + debug("*** *** *** unsupported root hub command *** *** ***\n"); + stat = USB_ST_STALLED; + } + + len = min_t(int, len, leni); + if (buffer != data_buf) + memcpy(buffer, data_buf, len); + + dev->act_len = len; + dev->status = stat; + debug("dev act_len %d, status %lu\n", dev->act_len, dev->status); + + return stat; +} + +static void musb_rh_init(void) +{ + rh_devnum = 0; + port_status = 0; +} + +#else + +static void musb_rh_init(void) {} + +#endif + +/* + * do a control transfer + */ +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + int devnum = usb_pipedevice(pipe); + u8 devspeed; + +#ifdef MUSB_NO_MULTIPOINT + /* Control message is for the HUB? */ + if (devnum == rh_devnum) { + int stat = musb_submit_rh_msg(dev, pipe, buffer, len, setup); + if (stat) + return stat; + } +#endif + + /* select control endpoint */ + writeb(MUSB_CONTROL_EP, &musbr->index); + readw(&musbr->txcsr); + +#ifndef MUSB_NO_MULTIPOINT + /* target addr and (for multipoint) hub addr/port */ + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr); + writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr); +#endif + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + config_hub_port(dev, MUSB_CONTROL_EP); + writeb(devspeed << 6, &musbr->txtype); + } else { + writeb(musb_cfg.musb_speed << 6, &musbr->txtype); +#ifndef MUSB_NO_MULTIPOINT + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubport); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubport); +#endif + } + + /* Control transfer setup phase */ + if (ctrlreq_setup_phase(dev, setup) < 0) + return 0; + + switch (setup->request) { + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_GET_INTERFACE: + case USB_REQ_GET_STATUS: + case USB_MSC_BBB_GET_MAX_LUN: + /* control transfer in-data-phase */ + if (ctrlreq_in_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer out-status-phase */ + if (ctrlreq_out_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_INTERFACE: + case USB_REQ_CLEAR_FEATURE: + case USB_MSC_BBB_RESET: + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + case USB_REQ_SET_DESCRIPTOR: + /* control transfer out data phase */ + if (ctrlreq_out_data_phase(dev, len, buffer) < 0) + return 0; + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return 0; + break; + + default: + /* unhandled control transfer */ + return -1; + } + + dev->status = 0; + dev->act_len = len; + +#ifdef MUSB_NO_MULTIPOINT + /* Set device address to USB_FADDR register */ + if (setup->request == USB_REQ_SET_ADDRESS) + writeb(dev->devnum, &musbr->faddr); +#endif + + return len; +} + +/* + * do a bulk transfer + */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); +#ifndef MUSB_NO_MULTIPOINT + int devnum = usb_pipedevice(pipe); +#endif + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select bulk endpoint */ + writeb(MUSB_BULK_EP, &musbr->index); + +#ifndef MUSB_NO_MULTIPOINT + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_BULK_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_BULK_EP].rxfuncaddr); +#endif + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_BULK_EP); + } else { +#ifndef MUSB_NO_MULTIPOINT + if (dir_out) { + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubport); + } +#endif + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (dir_out) { /* bulk-out transfer */ + /* Program the TxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->txtype); + + /* Write maximum packet size to the TxMaxp register */ + writew(dev->epmaxpacketout[ep], &musbr->txmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketout[ep]) ? + (len-txlen) : dev->epmaxpacketout[ep]; + +#ifdef CONFIG_USB_BLACKFIN + /* Set the transfer data size */ + writew(nextlen, &musbr->txcount); +#endif + + /* Write the data to the FIFO */ + write_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Set the TxPktRdy bit */ + csr = readw(&musbr->txcsr); + writew(csr | MUSB_TXCSR_TXPKTRDY, &musbr->txcsr); + + /* Wait until the TxPktRdy bit is cleared */ + if (wait_until_txep_ready(dev, MUSB_BULK_EP) != 1) { + readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + dev->act_len = txlen; + return 0; + } + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->txcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); + } else { /* bulk-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + csr = readw(&musbr->rxcsr); + writew(csr | MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (wait_until_rxep_ready(dev, MUSB_BULK_EP) != 1) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_BULK_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* bulk transfer is complete */ + dev->status = 0; + dev->act_len = len; + return 0; +} + +/* + * This function initializes the usb controller module. + */ +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + u8 power; + u32 timeout; + + musb_rh_init(); + + if (musb_platform_init() == -1) + return -1; + + /* Configure all the endpoint FIFO's and start usb controller */ + musbr = musb_cfg.regs; + musb_configure_ep(&epinfo[0], ARRAY_SIZE(epinfo)); + musb_start(); + + /* + * Wait until musb is enabled in host mode with a timeout. There + * should be a usb device connected. + */ + timeout = musb_cfg.timeout; + while (--timeout) + if (readb(&musbr->devctl) & MUSB_DEVCTL_HM) + break; + + /* if musb core is not in host mode, then return */ + if (!timeout) + return -1; + + /* start usb bus reset */ + power = readb(&musbr->power); + writeb(power | MUSB_POWER_RESET, &musbr->power); + + /* After initiating a usb reset, wait for about 20ms to 30ms */ + udelay(30000); + + /* stop usb bus reset */ + power = readb(&musbr->power); + power &= ~MUSB_POWER_RESET; + writeb(power, &musbr->power); + + /* Determine if the connected device is a high/full/low speed device */ + musb_cfg.musb_speed = (readb(&musbr->power) & MUSB_POWER_HSMODE) ? + MUSB_TYPE_SPEED_HIGH : + ((readb(&musbr->devctl) & MUSB_DEVCTL_FSDEV) ? + MUSB_TYPE_SPEED_FULL : MUSB_TYPE_SPEED_LOW); + return 0; +} + +/* + * This function stops the operation of the davinci usb module. + */ +int usb_lowlevel_stop(int index) +{ + /* Reset the USB module */ + musb_platform_deinit(); + writeb(0, &musbr->devctl); + return 0; +} + +/* + * This function supports usb interrupt transfers. Currently, usb interrupt + * transfers are not supported. + */ +int submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int len, int interval) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); +#ifndef MUSB_NO_MULTIPOINT + int devnum = usb_pipedevice(pipe); +#endif + u8 type; + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 devspeed; + + /* select interrupt endpoint */ + writeb(MUSB_INTR_EP, &musbr->index); + +#ifndef MUSB_NO_MULTIPOINT + /* write the address of the device */ + if (dir_out) + writeb(devnum, &musbr->tar[MUSB_INTR_EP].txfuncaddr); + else + writeb(devnum, &musbr->tar[MUSB_INTR_EP].rxfuncaddr); +#endif + + /* configure the hub address and the port number as required */ + devspeed = get_dev_speed(dev); + if ((musb_ishighspeed()) && (dev->parent != NULL) && + (devspeed != MUSB_TYPE_SPEED_HIGH)) { + /* + * MUSB is in high speed and the destination device is full + * speed device. So configure the hub address and port + * address registers. + */ + config_hub_port(dev, MUSB_INTR_EP); + } else { +#ifndef MUSB_NO_MULTIPOINT + if (dir_out) { + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].txhubport); + } else { + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubaddr); + writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubport); + } +#endif + devspeed = musb_cfg.musb_speed; + } + + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + + if (!dir_out) { /* intrrupt-in transfer */ + /* Write the saved toggle bit value */ + write_toggle(dev, ep, dir_out); + writeb(interval, &musbr->rxinterval); + + /* Program the RxType register */ + type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | + (MUSB_TYPE_PROTO_INTR << MUSB_TYPE_PROTO_SHIFT) | + (ep & MUSB_TYPE_REMOTE_END); + writeb(type, &musbr->rxtype); + + /* Write the maximum packet size to the RxMaxp register */ + writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + + while (txlen < len) { + nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? + (len-txlen) : dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + csr = readw(&musbr->rxcsr); + writew(csr | MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + + /* Wait until the RxPktRdy bit is set */ + if (wait_until_rxep_ready(dev, MUSB_INTR_EP) != 1) { + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + dev->act_len = txlen; + return 0; + } + + /* Read the data from the FIFO */ + read_fifo(MUSB_INTR_EP, nextlen, + (void *)(((u8 *)buffer) + txlen)); + + /* Clear the RxPktRdy bit */ + csr = readw(&musbr->rxcsr); + csr &= ~MUSB_RXCSR_RXPKTRDY; + writew(csr, &musbr->rxcsr); + txlen += nextlen; + } + + /* Keep a copy of the data toggle bit */ + csr = readw(&musbr->rxcsr); + usb_settoggle(dev, ep, dir_out, + (csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); + } + + /* interrupt transfer is complete */ + dev->irq_status = 0; + dev->irq_act_len = len; + dev->irq_handle(dev); + dev->status = 0; + dev->act_len = len; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/musb/musb_hcd.h b/qemu/roms/u-boot/drivers/usb/musb/musb_hcd.h new file mode 100644 index 000000000..02b9adcbe --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/musb_hcd.h @@ -0,0 +1,99 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#ifndef __MUSB_HCD_H__ +#define __MUSB_HCD_H__ + +#include "musb_core.h" +#ifdef CONFIG_USB_KEYBOARD +#include +extern unsigned char new[]; +#endif + +#ifndef CONFIG_MUSB_TIMEOUT +# define CONFIG_MUSB_TIMEOUT 100000 +#endif + +/* This defines the endpoint number used for control transfers */ +#define MUSB_CONTROL_EP 0 + +/* This defines the endpoint number used for bulk transfer */ +#ifndef MUSB_BULK_EP +# define MUSB_BULK_EP 1 +#endif + +/* This defines the endpoint number used for interrupt transfer */ +#define MUSB_INTR_EP 2 + +/* Determine the operating speed of MUSB core */ +#define musb_ishighspeed() \ + ((readb(&musbr->power) & MUSB_POWER_HSMODE) \ + >> MUSB_POWER_HSMODE_SHIFT) + +#define min_t(type, x, y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +/* extern functions */ +extern int musb_platform_init(void); +extern void musb_platform_deinit(void); + +#endif /* __MUSB_HCD_H__ */ diff --git a/qemu/roms/u-boot/drivers/usb/musb/musb_udc.c b/qemu/roms/u-boot/drivers/usb/musb/musb_udc.c new file mode 100644 index 000000000..87640f4e3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/musb_udc.c @@ -0,0 +1,959 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix + * + * This file is a rewrite of the usb device part of + * repository git.omapzoom.org/repo/u-boot.git, branch master, + * file cpu/omap3/fastboot.c + * + * This is the unique part of its copyright : + * + * ------------------------------------------------------------------------- + * + * (C) Copyright 2008 - 2009 + * Windriver, + * Tom Rix + * + * ------------------------------------------------------------------------- + * + * The details of connecting the device to the uboot usb device subsystem + * came from the old omap3 repository www.sakoman.net/u-boot-omap3.git, + * branch omap3-dev-usb, file drivers/usb/usbdcore_musb.c + * + * This is the unique part of its copyright : + * + * ------------------------------------------------------------------------- + * + * (C) Copyright 2008 Texas Instruments Incorporated. + * + * Based on + * u-boot OMAP1510 USB drivers (drivers/usbdcore_omap1510.c) + * twl4030 init based on linux (drivers/i2c/chips/twl4030_usb.c) + * + * Author: Diego Dompe (diego.dompe@ridgerun.com) + * Atin Malaviya (atin.malaviya@gmail.com) + * + * ------------------------------------------------------------------------- + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "../gadget/ep0.h" +#include "musb_core.h" +#if defined(CONFIG_USB_OMAP3) +#include "omap3.h" +#elif defined(CONFIG_USB_AM35X) +#include "am35x.h" +#elif defined(CONFIG_USB_DAVINCI) +#include "davinci.h" +#endif + +/* Define MUSB_DEBUG for debugging */ +/* #define MUSB_DEBUG */ +#include "musb_debug.h" + +#define MAX_ENDPOINT 15 + +#define GET_ENDPOINT(dev,ep) \ +(((struct usb_device_instance *)(dev))->bus->endpoint_array + ep) + +#define SET_EP0_STATE(s) \ +do { \ + if ((0 <= (s)) && (SET_ADDRESS >= (s))) { \ + if ((s) != ep0_state) { \ + if ((debug_setup) && (debug_level > 1)) \ + serial_printf("INFO : Changing state " \ + "from %s to %s in %s at " \ + "line %d\n", \ + ep0_state_strings[ep0_state],\ + ep0_state_strings[s], \ + __PRETTY_FUNCTION__, \ + __LINE__); \ + ep0_state = s; \ + } \ + } else { \ + if (debug_level > 0) \ + serial_printf("Error at %s %d with setting " \ + "state %d is invalid\n", \ + __PRETTY_FUNCTION__, __LINE__, s); \ + } \ +} while (0) + +/* static implies these initialized to 0 or NULL */ +static int debug_setup; +static int debug_level; +static struct musb_epinfo epinfo[MAX_ENDPOINT * 2]; +static enum ep0_state_enum { + IDLE = 0, + TX, + RX, + SET_ADDRESS +} ep0_state = IDLE; +static char *ep0_state_strings[4] = { + "IDLE", + "TX", + "RX", + "SET_ADDRESS", +}; + +static struct urb *ep0_urb; +struct usb_endpoint_instance *ep0_endpoint; +static struct usb_device_instance *udc_device; +static int enabled; + +#ifdef MUSB_DEBUG +static void musb_db_regs(void) +{ + u8 b; + u16 w; + + b = readb(&musbr->faddr); + serial_printf("\tfaddr 0x%2.2x\n", b); + + b = readb(&musbr->power); + musb_print_pwr(b); + + w = readw(&musbr->ep[0].ep0.csr0); + musb_print_csr0(w); + + b = readb(&musbr->devctl); + musb_print_devctl(b); + + b = readb(&musbr->ep[0].ep0.configdata); + musb_print_config(b); + + w = readw(&musbr->frame); + serial_printf("\tframe 0x%4.4x\n", w); + + b = readb(&musbr->index); + serial_printf("\tindex 0x%2.2x\n", b); + + w = readw(&musbr->ep[1].epN.rxmaxp); + musb_print_rxmaxp(w); + + w = readw(&musbr->ep[1].epN.rxcsr); + musb_print_rxcsr(w); + + w = readw(&musbr->ep[1].epN.txmaxp); + musb_print_txmaxp(w); + + w = readw(&musbr->ep[1].epN.txcsr); + musb_print_txcsr(w); +} +#else +#define musb_db_regs() +#endif /* DEBUG_MUSB */ + +static void musb_peri_softconnect(void) +{ + u8 power, devctl; + + /* Power off MUSB */ + power = readb(&musbr->power); + power &= ~MUSB_POWER_SOFTCONN; + writeb(power, &musbr->power); + + /* Read intr to clear */ + readb(&musbr->intrusb); + readw(&musbr->intrrx); + readw(&musbr->intrtx); + + udelay(1000 * 1000); /* 1 sec */ + + /* Power on MUSB */ + power = readb(&musbr->power); + power |= MUSB_POWER_SOFTCONN; + /* + * The usb device interface is usb 1.1 + * Disable 2.0 high speed by clearring the hsenable bit. + */ + power &= ~MUSB_POWER_HSENAB; + writeb(power, &musbr->power); + + /* Check if device is in b-peripheral mode */ + devctl = readb(&musbr->devctl); + if (!(devctl & MUSB_DEVCTL_BDEVICE) || + (devctl & MUSB_DEVCTL_HM)) { + serial_printf("ERROR : Unsupport USB mode\n"); + serial_printf("Check that mini-B USB cable is attached " + "to the device\n"); + } + + if (debug_setup && (debug_level > 1)) + musb_db_regs(); +} + +static void musb_peri_reset(void) +{ + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s reset\n", __PRETTY_FUNCTION__); + + if (ep0_endpoint) + ep0_endpoint->endpoint_address = 0xff; + + /* Sync sw and hw addresses */ + writeb(udc_device->address, &musbr->faddr); + + SET_EP0_STATE(IDLE); +} + +static void musb_peri_resume(void) +{ + /* noop */ +} + +static void musb_peri_ep0_stall(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= MUSB_CSR0_P_SENDSTALL; + writew(csr0, &musbr->ep[0].ep0.csr0); + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s stall\n", __PRETTY_FUNCTION__); +} + +static void musb_peri_ep0_ack_req(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= MUSB_CSR0_P_SVDRXPKTRDY; + writew(csr0, &musbr->ep[0].ep0.csr0); +} + +static void musb_ep0_tx_ready(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= MUSB_CSR0_TXPKTRDY; + writew(csr0, &musbr->ep[0].ep0.csr0); +} + +static void musb_ep0_tx_ready_and_last(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_P_DATAEND); + writew(csr0, &musbr->ep[0].ep0.csr0); +} + +static void musb_peri_ep0_last(void) +{ + u16 csr0; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + csr0 |= MUSB_CSR0_P_DATAEND; + writew(csr0, &musbr->ep[0].ep0.csr0); +} + +static void musb_peri_ep0_set_address(void) +{ + u8 faddr; + writeb(udc_device->address, &musbr->faddr); + + /* Verify */ + faddr = readb(&musbr->faddr); + if (udc_device->address == faddr) { + SET_EP0_STATE(IDLE); + usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0); + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s Address set to %d\n", + __PRETTY_FUNCTION__, udc_device->address); + } else { + if (debug_level > 0) + serial_printf("ERROR : %s Address missmatch " + "sw %d vs hw %d\n", + __PRETTY_FUNCTION__, + udc_device->address, faddr); + } +} + +static void musb_peri_rx_ack(unsigned int ep) +{ + u16 peri_rxcsr; + + peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr); + peri_rxcsr &= ~MUSB_RXCSR_RXPKTRDY; + writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr); +} + +static void musb_peri_tx_ready(unsigned int ep) +{ + u16 peri_txcsr; + + peri_txcsr = readw(&musbr->ep[ep].epN.txcsr); + peri_txcsr |= MUSB_TXCSR_TXPKTRDY; + writew(peri_txcsr, &musbr->ep[ep].epN.txcsr); +} + +static void musb_peri_ep0_zero_data_request(int err) +{ + musb_peri_ep0_ack_req(); + + if (err) { + musb_peri_ep0_stall(); + SET_EP0_STATE(IDLE); + } else { + + musb_peri_ep0_last(); + + /* USBD state */ + switch (ep0_urb->device_request.bRequest) { + case USB_REQ_SET_ADDRESS: + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s received set " + "address\n", __PRETTY_FUNCTION__); + break; + + case USB_REQ_SET_CONFIGURATION: + if ((debug_setup) && (debug_level > 1)) + serial_printf("INFO : %s Configured\n", + __PRETTY_FUNCTION__); + usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); + break; + } + + /* EP0 state */ + if (USB_REQ_SET_ADDRESS == ep0_urb->device_request.bRequest) { + SET_EP0_STATE(SET_ADDRESS); + } else { + SET_EP0_STATE(IDLE); + } + } +} + +static void musb_peri_ep0_rx_data_request(void) +{ + /* + * This is the completion of the data OUT / RX + * + * Host is sending data to ep0 that is not + * part of setup. This comes from the cdc_recv_setup + * op that is device specific. + * + */ + musb_peri_ep0_ack_req(); + + ep0_endpoint->rcv_urb = ep0_urb; + ep0_urb->actual_length = 0; + SET_EP0_STATE(RX); +} + +static void musb_peri_ep0_tx_data_request(int err) +{ + if (err) { + musb_peri_ep0_stall(); + SET_EP0_STATE(IDLE); + } else { + musb_peri_ep0_ack_req(); + + ep0_endpoint->tx_urb = ep0_urb; + ep0_endpoint->sent = 0; + SET_EP0_STATE(TX); + } +} + +static void musb_peri_ep0_idle(void) +{ + u16 count0; + int err; + u16 csr0; + + /* + * Verify addresses + * A lot of confusion can be caused if the address + * in software, udc layer, does not agree with the + * hardware. Since the setting of the hardware address + * must be set after the set address request, the + * usb state machine is out of sync for a few frame. + * It is a good idea to run this check when changes + * are made to the state machine. + */ + if ((debug_level > 0) && + (ep0_state != SET_ADDRESS)) { + u8 faddr; + + faddr = readb(&musbr->faddr); + if (udc_device->address != faddr) { + serial_printf("ERROR : %s addresses do not" + "match sw %d vs hw %d\n", + __PRETTY_FUNCTION__, + udc_device->address, faddr); + udelay(1000 * 1000); + hang(); + } + } + + csr0 = readw(&musbr->ep[0].ep0.csr0); + + if (!(MUSB_CSR0_RXPKTRDY & csr0)) + goto end; + + count0 = readw(&musbr->ep[0].ep0.count0); + if (count0 == 0) + goto end; + + if (count0 != 8) { + if ((debug_setup) && (debug_level > 1)) + serial_printf("WARN : %s SETUP incorrect size %d\n", + __PRETTY_FUNCTION__, count0); + musb_peri_ep0_stall(); + goto end; + } + + read_fifo(0, count0, &ep0_urb->device_request); + + if (debug_level > 2) + print_usb_device_request(&ep0_urb->device_request); + + if (ep0_urb->device_request.wLength == 0) { + err = ep0_recv_setup(ep0_urb); + + /* Zero data request */ + musb_peri_ep0_zero_data_request(err); + } else { + /* Is data coming or going ? */ + u8 reqType = ep0_urb->device_request.bmRequestType; + + if (USB_REQ_DEVICE2HOST == (reqType & USB_REQ_DIRECTION_MASK)) { + err = ep0_recv_setup(ep0_urb); + /* Device to host */ + musb_peri_ep0_tx_data_request(err); + } else { + /* + * Host to device + * + * The RX routine will call ep0_recv_setup + * when the data packet has arrived. + */ + musb_peri_ep0_rx_data_request(); + } + } + +end: + return; +} + +static void musb_peri_ep0_rx(void) +{ + /* + * This is the completion of the data OUT / RX + * + * Host is sending data to ep0 that is not + * part of setup. This comes from the cdc_recv_setup + * op that is device specific. + * + * Pass the data back to driver ep0_recv_setup which + * should give the cdc_recv_setup the chance to handle + * the rx + */ + u16 csr0; + u16 count0; + + if (debug_level > 3) { + if (0 != ep0_urb->actual_length) { + serial_printf("%s finished ? %d of %d\n", + __PRETTY_FUNCTION__, + ep0_urb->actual_length, + ep0_urb->device_request.wLength); + } + } + + if (ep0_urb->device_request.wLength == ep0_urb->actual_length) { + musb_peri_ep0_last(); + SET_EP0_STATE(IDLE); + ep0_recv_setup(ep0_urb); + return; + } + + csr0 = readw(&musbr->ep[0].ep0.csr0); + if (!(MUSB_CSR0_RXPKTRDY & csr0)) + return; + + count0 = readw(&musbr->ep[0].ep0.count0); + + if (count0) { + struct usb_endpoint_instance *endpoint; + u32 length; + u8 *data; + + endpoint = ep0_endpoint; + if (endpoint && endpoint->rcv_urb) { + struct urb *urb = endpoint->rcv_urb; + unsigned int remaining_space = urb->buffer_length - + urb->actual_length; + + if (remaining_space) { + int urb_bad = 0; /* urb is good */ + + if (count0 > remaining_space) + length = remaining_space; + else + length = count0; + + data = (u8 *) urb->buffer_data; + data += urb->actual_length; + + /* The common musb fifo reader */ + read_fifo(0, length, data); + + musb_peri_ep0_ack_req(); + + /* + * urb's actual_length is updated in + * usbd_rcv_complete + */ + usbd_rcv_complete(endpoint, length, urb_bad); + + } else { + if (debug_level > 0) + serial_printf("ERROR : %s no space in " + "rcv buffer\n", + __PRETTY_FUNCTION__); + } + } else { + if (debug_level > 0) + serial_printf("ERROR : %s problem with " + "endpoint\n", + __PRETTY_FUNCTION__); + } + } else { + if (debug_level > 0) + serial_printf("ERROR : %s with nothing to do\n", + __PRETTY_FUNCTION__); + } +} + +static void musb_peri_ep0_tx(void) +{ + u16 csr0; + int transfer_size = 0; + unsigned int p, pm; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + + /* Check for pending tx */ + if (csr0 & MUSB_CSR0_TXPKTRDY) + goto end; + + /* Check if this is the last packet sent */ + if (ep0_endpoint->sent >= ep0_urb->actual_length) { + SET_EP0_STATE(IDLE); + goto end; + } + + transfer_size = ep0_urb->actual_length - ep0_endpoint->sent; + /* Is the transfer size negative ? */ + if (transfer_size <= 0) { + if (debug_level > 0) + serial_printf("ERROR : %s problem with the" + " transfer size %d\n", + __PRETTY_FUNCTION__, + transfer_size); + SET_EP0_STATE(IDLE); + goto end; + } + + /* Truncate large transfers to the fifo size */ + if (transfer_size > ep0_endpoint->tx_packetSize) + transfer_size = ep0_endpoint->tx_packetSize; + + write_fifo(0, transfer_size, &ep0_urb->buffer[ep0_endpoint->sent]); + ep0_endpoint->sent += transfer_size; + + /* Done or more to send ? */ + if (ep0_endpoint->sent >= ep0_urb->actual_length) + musb_ep0_tx_ready_and_last(); + else + musb_ep0_tx_ready(); + + /* Wait a bit */ + pm = 10; + for (p = 0; p < pm; p++) { + csr0 = readw(&musbr->ep[0].ep0.csr0); + if (!(csr0 & MUSB_CSR0_TXPKTRDY)) + break; + + /* Double the delay. */ + udelay(1 << pm); + } + + if ((ep0_endpoint->sent >= ep0_urb->actual_length) && (p < pm)) + SET_EP0_STATE(IDLE); + +end: + return; +} + +static void musb_peri_ep0(void) +{ + u16 csr0; + + if (SET_ADDRESS == ep0_state) + return; + + csr0 = readw(&musbr->ep[0].ep0.csr0); + + /* Error conditions */ + if (MUSB_CSR0_P_SENTSTALL & csr0) { + csr0 &= ~MUSB_CSR0_P_SENTSTALL; + writew(csr0, &musbr->ep[0].ep0.csr0); + SET_EP0_STATE(IDLE); + } + if (MUSB_CSR0_P_SETUPEND & csr0) { + csr0 |= MUSB_CSR0_P_SVDSETUPEND; + writew(csr0, &musbr->ep[0].ep0.csr0); + SET_EP0_STATE(IDLE); + if ((debug_setup) && (debug_level > 1)) + serial_printf("WARN: %s SETUPEND\n", + __PRETTY_FUNCTION__); + } + + /* Normal states */ + if (IDLE == ep0_state) + musb_peri_ep0_idle(); + + if (TX == ep0_state) + musb_peri_ep0_tx(); + + if (RX == ep0_state) + musb_peri_ep0_rx(); +} + +static void musb_peri_rx_ep(unsigned int ep) +{ + u16 peri_rxcount; + u8 peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr); + + if (!(peri_rxcsr & MUSB_RXCSR_RXPKTRDY)) { + if (debug_level > 0) + serial_printf("ERROR : %s %d without MUSB_RXCSR_RXPKTRDY set\n", + __PRETTY_FUNCTION__, ep); + return; + } + + peri_rxcount = readw(&musbr->ep[ep].epN.rxcount); + if (peri_rxcount) { + struct usb_endpoint_instance *endpoint; + u32 length; + u8 *data; + + endpoint = GET_ENDPOINT(udc_device, ep); + if (endpoint && endpoint->rcv_urb) { + struct urb *urb = endpoint->rcv_urb; + unsigned int remaining_space = urb->buffer_length - + urb->actual_length; + + if (remaining_space) { + int urb_bad = 0; /* urb is good */ + + if (peri_rxcount > remaining_space) + length = remaining_space; + else + length = peri_rxcount; + + data = (u8 *) urb->buffer_data; + data += urb->actual_length; + + /* The common musb fifo reader */ + read_fifo(ep, length, data); + + musb_peri_rx_ack(ep); + + /* + * urb's actual_length is updated in + * usbd_rcv_complete + */ + usbd_rcv_complete(endpoint, length, urb_bad); + + } else { + if (debug_level > 0) + serial_printf("ERROR : %s %d no space " + "in rcv buffer\n", + __PRETTY_FUNCTION__, ep); + } + } else { + if (debug_level > 0) + serial_printf("ERROR : %s %d problem with " + "endpoint\n", + __PRETTY_FUNCTION__, ep); + } + + } else { + if (debug_level > 0) + serial_printf("ERROR : %s %d with nothing to do\n", + __PRETTY_FUNCTION__, ep); + } +} + +static void musb_peri_rx(u16 intr) +{ + unsigned int ep; + + /* Check for EP0 */ + if (0x01 & intr) + musb_peri_ep0(); + + for (ep = 1; ep < 16; ep++) { + if ((1 << ep) & intr) + musb_peri_rx_ep(ep); + } +} + +static void musb_peri_tx(u16 intr) +{ + /* Check for EP0 */ + if (0x01 & intr) + musb_peri_ep0_tx(); + + /* + * Use this in the future when handling epN tx + * + * u8 ep; + * + * for (ep = 1; ep < 16; ep++) { + * if ((1 << ep) & intr) { + * / * handle tx for this endpoint * / + * } + * } + */ +} + +void udc_irq(void) +{ + /* This is a high freq called function */ + if (enabled) { + u8 intrusb; + + intrusb = readb(&musbr->intrusb); + + /* + * See drivers/usb/gadget/mpc8xx_udc.c for + * state diagram going from detached through + * configuration. + */ + if (MUSB_INTR_RESUME & intrusb) { + usbd_device_event_irq(udc_device, + DEVICE_BUS_ACTIVITY, 0); + musb_peri_resume(); + } + + musb_peri_ep0(); + + if (MUSB_INTR_RESET & intrusb) { + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + musb_peri_reset(); + } + + if (MUSB_INTR_DISCONNECT & intrusb) { + /* cable unplugged from hub/host */ + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + musb_peri_reset(); + usbd_device_event_irq(udc_device, DEVICE_HUB_RESET, 0); + } + + if (MUSB_INTR_SOF & intrusb) { + usbd_device_event_irq(udc_device, + DEVICE_BUS_ACTIVITY, 0); + musb_peri_resume(); + } + + if (MUSB_INTR_SUSPEND & intrusb) { + usbd_device_event_irq(udc_device, + DEVICE_BUS_INACTIVE, 0); + } + + if (ep0_state != SET_ADDRESS) { + u16 intrrx, intrtx; + + intrrx = readw(&musbr->intrrx); + intrtx = readw(&musbr->intrtx); + + if (intrrx) + musb_peri_rx(intrrx); + + if (intrtx) + musb_peri_tx(intrtx); + } else { + if (MUSB_INTR_SOF & intrusb) { + u8 faddr; + faddr = readb(&musbr->faddr); + /* + * Setting of the address can fail. + * Normally it succeeds the second time. + */ + if (udc_device->address != faddr) + musb_peri_ep0_set_address(); + } + } + } +} + +void udc_set_nak(int ep_num) +{ + /* noop */ +} + +void udc_unset_nak(int ep_num) +{ + /* noop */ +} + +int udc_endpoint_write(struct usb_endpoint_instance *endpoint) +{ + int ret = 0; + + /* Transmit only if the hardware is available */ + if (endpoint->tx_urb && endpoint->state == 0) { + unsigned int ep = endpoint->endpoint_address & + USB_ENDPOINT_NUMBER_MASK; + + u16 peri_txcsr = readw(&musbr->ep[ep].epN.txcsr); + + /* Error conditions */ + if (peri_txcsr & MUSB_TXCSR_P_UNDERRUN) { + peri_txcsr &= ~MUSB_TXCSR_P_UNDERRUN; + writew(peri_txcsr, &musbr->ep[ep].epN.txcsr); + } + + if (debug_level > 1) + musb_print_txcsr(peri_txcsr); + + /* Check if a packet is waiting to be sent */ + if (!(peri_txcsr & MUSB_TXCSR_TXPKTRDY)) { + u32 length; + u8 *data; + struct urb *urb = endpoint->tx_urb; + unsigned int remaining_packet = urb->actual_length - + endpoint->sent; + + if (endpoint->tx_packetSize < remaining_packet) + length = endpoint->tx_packetSize; + else + length = remaining_packet; + + data = (u8 *) urb->buffer; + data += endpoint->sent; + + /* common musb fifo function */ + write_fifo(ep, length, data); + + musb_peri_tx_ready(ep); + + endpoint->last = length; + /* usbd_tx_complete will take care of updating 'sent' */ + usbd_tx_complete(endpoint); + } + } else { + if (debug_level > 0) + serial_printf("ERROR : %s Problem with urb %p " + "or ep state %d\n", + __PRETTY_FUNCTION__, + endpoint->tx_urb, endpoint->state); + } + + return ret; +} + +void udc_setup_ep(struct usb_device_instance *device, unsigned int id, + struct usb_endpoint_instance *endpoint) +{ + if (0 == id) { + /* EP0 */ + ep0_endpoint = endpoint; + ep0_endpoint->endpoint_address = 0xff; + ep0_urb = usbd_alloc_urb(device, endpoint); + } else if (MAX_ENDPOINT >= id) { + int ep_addr; + + /* Check the direction */ + ep_addr = endpoint->endpoint_address; + if (USB_DIR_IN == (ep_addr & USB_ENDPOINT_DIR_MASK)) { + /* IN */ + epinfo[(id * 2) + 1].epsize = endpoint->tx_packetSize; + } else { + /* OUT */ + epinfo[id * 2].epsize = endpoint->rcv_packetSize; + } + + musb_configure_ep(&epinfo[0], ARRAY_SIZE(epinfo)); + } else { + if (debug_level > 0) + serial_printf("ERROR : %s endpoint request %d " + "exceeds maximum %d\n", + __PRETTY_FUNCTION__, id, MAX_ENDPOINT); + } +} + +void udc_connect(void) +{ + /* noop */ +} + +void udc_disconnect(void) +{ + /* noop */ +} + +void udc_enable(struct usb_device_instance *device) +{ + /* Save the device structure pointer */ + udc_device = device; + + enabled = 1; +} + +void udc_disable(void) +{ + enabled = 0; +} + +void udc_startup_events(struct usb_device_instance *device) +{ + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ + usbd_device_event_irq(device, DEVICE_INIT, 0); + + /* + * The DEVICE_CREATE event puts the USB device in the state + * STATE_ATTACHED. + */ + usbd_device_event_irq(device, DEVICE_CREATE, 0); + + /* Resets the address to 0 */ + usbd_device_event_irq(device, DEVICE_RESET, 0); + + udc_enable(device); +} + +int udc_init(void) +{ + int ret; + int ep_loop; + + ret = musb_platform_init(); + if (ret < 0) + goto end; + + /* Configure all the endpoint FIFO's and start usb controller */ + musbr = musb_cfg.regs; + + /* Initialize the endpoints */ + for (ep_loop = 0; ep_loop < MAX_ENDPOINT * 2; ep_loop++) { + epinfo[ep_loop].epnum = (ep_loop / 2) + 1; + epinfo[ep_loop].epdir = ep_loop % 2; /* OUT, IN */ + epinfo[ep_loop].epsize = 0; + } + + musb_peri_softconnect(); + + ret = 0; +end: + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/usb/musb/omap3.c b/qemu/roms/u-boot/drivers/usb/musb/omap3.c new file mode 100644 index 000000000..97da529b4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/omap3.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix + * + * This is file is based on + * repository git.gitorious.org/u-boot-omap3/mainline.git, + * branch omap3-dev-usb, file drivers/usb/host/omap3530_usb.c + * + * This is the unique part of its copyright : + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2009 Texas Instruments + * + * ------------------------------------------------------------------------ + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "omap3.h" + +static int platform_needs_initialization = 1; + +struct musb_config musb_cfg = { + .regs = (struct musb_regs *)MENTOR_USB0_BASE, + .timeout = OMAP3_USB_TIMEOUT, + .musb_speed = 0, +}; + +/* + * OMAP3 USB OTG registers. + */ +struct omap3_otg_regs { + u32 revision; + u32 sysconfig; + u32 sysstatus; + u32 interfsel; + u32 simenable; + u32 forcestdby; +}; + +static struct omap3_otg_regs *otg; + +#define OMAP3_OTG_SYSCONFIG_SMART_STANDBY_MODE 0x2000 +#define OMAP3_OTG_SYSCONFIG_NO_STANDBY_MODE 0x1000 +#define OMAP3_OTG_SYSCONFIG_SMART_IDLE_MODE 0x0010 +#define OMAP3_OTG_SYSCONFIG_NO_IDLE_MODE 0x0008 +#define OMAP3_OTG_SYSCONFIG_ENABLEWAKEUP 0x0004 +#define OMAP3_OTG_SYSCONFIG_SOFTRESET 0x0002 +#define OMAP3_OTG_SYSCONFIG_AUTOIDLE 0x0001 + +#define OMAP3_OTG_SYSSTATUS_RESETDONE 0x0001 + +/* OMAP4430 has an internal PHY, use it */ +#ifdef CONFIG_OMAP4430 +#define OMAP3_OTG_INTERFSEL_OMAP 0x0000 +#else +#define OMAP3_OTG_INTERFSEL_OMAP 0x0001 +#endif + +#define OMAP3_OTG_FORCESTDBY_STANDBY 0x0001 + + +#ifdef DEBUG_MUSB_OMAP3 +static void musb_db_otg_regs(void) +{ + u32 l; + l = readl(&otg->revision); + serial_printf("OTG_REVISION 0x%x\n", l); + l = readl(&otg->sysconfig); + serial_printf("OTG_SYSCONFIG 0x%x\n", l); + l = readl(&otg->sysstatus); + serial_printf("OTG_SYSSTATUS 0x%x\n", l); + l = readl(&otg->interfsel); + serial_printf("OTG_INTERFSEL 0x%x\n", l); + l = readl(&otg->forcestdby); + serial_printf("OTG_FORCESTDBY 0x%x\n", l); +} +#endif + +int musb_platform_init(void) +{ + int ret = -1; + + if (platform_needs_initialization) { + u32 stdby; + + /* + * OMAP3EVM uses ISP1504 phy and so + * twl4030 related init is not required. + */ +#ifdef CONFIG_TWL4030_USB + if (twl4030_usb_ulpi_init()) { + serial_printf("ERROR: %s Could not initialize PHY\n", + __PRETTY_FUNCTION__); + goto end; + } +#endif + +#ifdef CONFIG_TWL6030_POWER + twl6030_usb_device_settings(); +#endif + + otg = (struct omap3_otg_regs *)OMAP3_OTG_BASE; + + /* Set OTG to always be on */ + writel(OMAP3_OTG_SYSCONFIG_NO_STANDBY_MODE | + OMAP3_OTG_SYSCONFIG_NO_IDLE_MODE, &otg->sysconfig); + + /* Set the interface */ + writel(OMAP3_OTG_INTERFSEL_OMAP, &otg->interfsel); + + /* Clear force standby */ + stdby = readl(&otg->forcestdby); + stdby &= ~OMAP3_OTG_FORCESTDBY_STANDBY; + writel(stdby, &otg->forcestdby); + +#ifdef CONFIG_OMAP3_EVM + musb_cfg.extvbus = omap3_evm_need_extvbus(); +#endif + +#ifdef CONFIG_OMAP4430 + u32 *usbotghs_control = + (u32 *)((*ctrl)->control_usbotghs_ctrl); + *usbotghs_control = 0x15; +#endif + platform_needs_initialization = 0; + } + + ret = platform_needs_initialization; + +#ifdef CONFIG_TWL4030_USB +end: +#endif + return ret; + +} + +void musb_platform_deinit(void) +{ + /* noop */ +} diff --git a/qemu/roms/u-boot/drivers/usb/musb/omap3.h b/qemu/roms/u-boot/drivers/usb/musb/omap3.h new file mode 100644 index 000000000..ae645c72d --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/musb/omap3.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix + * + * This file is based on the file drivers/usb/musb/davinci.h + * + * This is the unique part of its copyright: + * + * -------------------------------------------------------------------- + * + * Copyright (c) 2008 Texas Instruments + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + * + * -------------------------------------------------------------------- + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _MUSB_OMAP3_H_ +#define _MUSB_OMAP3_H_ + +#include +#include "musb_core.h" + +/* Base address of MUSB registers */ +#define MENTOR_USB0_BASE MUSB_BASE + +/* Base address of OTG registers */ +#define OMAP3_OTG_BASE (MENTOR_USB0_BASE + 0x400) + +/* Timeout for USB module */ +#define OMAP3_USB_TIMEOUT 0x3FFFFFF + +int musb_platform_init(void); + +#ifdef CONFIG_OMAP3_EVM +extern u8 omap3_evm_need_extvbus(void); +#endif + +#endif /* _MUSB_OMAP3_H */ diff --git a/qemu/roms/u-boot/drivers/usb/phy/Makefile b/qemu/roms/u-boot/drivers/usb/phy/Makefile new file mode 100644 index 000000000..93d147e26 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/phy/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (c) 2009 Wind River Systems, Inc. +# Tom Rix +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_TWL4030_USB) += twl4030.o +obj-$(CONFIG_OMAP_USB_PHY) += omap_usb_phy.o diff --git a/qemu/roms/u-boot/drivers/usb/phy/omap_usb_phy.c b/qemu/roms/u-boot/drivers/usb/phy/omap_usb_phy.c new file mode 100644 index 000000000..af46db2ed --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/phy/omap_usb_phy.c @@ -0,0 +1,261 @@ +/* + * OMAP USB PHY Support + * + * (C) Copyright 2013 + * Texas Instruments, + * + * Author: Dan Murphy + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../host/xhci.h" + +#ifdef CONFIG_OMAP_USB3PHY1_HOST +struct usb_dpll_params { + u16 m; + u8 n; + u8 freq:3; + u8 sd; + u32 mf; +}; + +#define NUM_USB_CLKS 6 + +static struct usb_dpll_params omap_usb3_dpll_params[NUM_USB_CLKS] = { + {1250, 5, 4, 20, 0}, /* 12 MHz */ + {3125, 20, 4, 20, 0}, /* 16.8 MHz */ + {1172, 8, 4, 20, 65537}, /* 19.2 MHz */ + {1250, 12, 4, 20, 0}, /* 26 MHz */ + {3125, 47, 4, 20, 92843}, /* 38.4 MHz */ + {1000, 7, 4, 10, 0}, /* 20 MHz */ +}; + +static void omap_usb_dpll_relock(struct omap_usb3_phy *phy_regs) +{ + u32 val; + + writel(SET_PLL_GO, &phy_regs->pll_go); + do { + val = readl(&phy_regs->pll_status); + if (val & PLL_LOCK) + break; + } while (1); +} + +static void omap_usb_dpll_lock(struct omap_usb3_phy *phy_regs) +{ + u32 clk_index = get_sys_clk_index(); + u32 val; + + val = readl(&phy_regs->pll_config_1); + val &= ~PLL_REGN_MASK; + val |= omap_usb3_dpll_params[clk_index].n << PLL_REGN_SHIFT; + writel(val, &phy_regs->pll_config_1); + + val = readl(&phy_regs->pll_config_2); + val &= ~PLL_SELFREQDCO_MASK; + val |= omap_usb3_dpll_params[clk_index].freq << PLL_SELFREQDCO_SHIFT; + writel(val, &phy_regs->pll_config_2); + + val = readl(&phy_regs->pll_config_1); + val &= ~PLL_REGM_MASK; + val |= omap_usb3_dpll_params[clk_index].m << PLL_REGM_SHIFT; + writel(val, &phy_regs->pll_config_1); + + val = readl(&phy_regs->pll_config_4); + val &= ~PLL_REGM_F_MASK; + val |= omap_usb3_dpll_params[clk_index].mf << PLL_REGM_F_SHIFT; + writel(val, &phy_regs->pll_config_4); + + val = readl(&phy_regs->pll_config_3); + val &= ~PLL_SD_MASK; + val |= omap_usb3_dpll_params[clk_index].sd << PLL_SD_SHIFT; + writel(val, &phy_regs->pll_config_3); + + omap_usb_dpll_relock(phy_regs); +} + +static void usb3_phy_partial_powerup(struct omap_usb3_phy *phy_regs) +{ + u32 rate = get_sys_clk_freq()/1000000; + u32 val; + + val = readl((*ctrl)->control_phy_power_usb); + val &= ~(USB3_PWRCTL_CLK_CMD_MASK | USB3_PWRCTL_CLK_FREQ_MASK); + val |= (USB3_PHY_PARTIAL_RX_POWERON | USB3_PHY_TX_RX_POWERON); + val |= rate << USB3_PWRCTL_CLK_FREQ_SHIFT; + + writel(val, (*ctrl)->control_phy_power_usb); +} + +void usb_phy_power(int on) +{ + u32 val; + + val = readl((*ctrl)->control_phy_power_usb); + if (on) { + val &= ~USB3_PWRCTL_CLK_CMD_MASK; + val |= USB3_PHY_TX_RX_POWERON; + } else { + val &= (~USB3_PWRCTL_CLK_CMD_MASK & ~USB3_PHY_TX_RX_POWERON); + } + + writel(val, (*ctrl)->control_phy_power_usb); +} + +void omap_usb3_phy_init(struct omap_usb3_phy *phy_regs) +{ + omap_usb_dpll_lock(phy_regs); + + usb3_phy_partial_powerup(phy_regs); + /* + * Give enough time for the PHY to partially power-up before + * powering it up completely. delay value suggested by the HW + * team. + */ + mdelay(100); + usb3_phy_power(1); +} + +static void omap_enable_usb3_phy(struct omap_xhci *omap) +{ + u32 val; + + /* Setting OCP2SCP1 register */ + setbits_le32((*prcm)->cm_l3init_ocp2scp1_clkctrl, + OCP2SCP1_CLKCTRL_MODULEMODE_HW); + + /* Turn on 32K AON clk */ + setbits_le32((*prcm)->cm_coreaon_usb_phy_core_clkctrl, + USBPHY_CORE_CLKCTRL_OPTFCLKEN_CLK32K); + + /* Setting CM_L3INIT_CLKSTCTRL to 0x0 i.e NO sleep */ + writel(0x0, (*prcm)->cm_l3init_clkstctrl); + + val = (USBOTGSS_DMADISABLE | + USBOTGSS_STANDBYMODE_SMRT_WKUP | + USBOTGSS_IDLEMODE_NOIDLE); + writel(val, &omap->otg_wrapper->sysconfig); + + /* Clear the utmi OTG status */ + val = readl(&omap->otg_wrapper->utmi_otg_status); + writel(val, &omap->otg_wrapper->utmi_otg_status); + + /* Enable interrupts */ + writel(USBOTGSS_COREIRQ_EN, &omap->otg_wrapper->irqenable_set_0); + val = (USBOTGSS_IRQ_SET_1_IDPULLUP_FALL_EN | + USBOTGSS_IRQ_SET_1_DISCHRGVBUS_FALL_EN | + USBOTGSS_IRQ_SET_1_CHRGVBUS_FALL_EN | + USBOTGSS_IRQ_SET_1_DRVVBUS_FALL_EN | + USBOTGSS_IRQ_SET_1_IDPULLUP_RISE_EN | + USBOTGSS_IRQ_SET_1_DISCHRGVBUS_RISE_EN | + USBOTGSS_IRQ_SET_1_CHRGVBUS_RISE_EN | + USBOTGSS_IRQ_SET_1_DRVVBUS_RISE_EN | + USBOTGSS_IRQ_SET_1_OEVT_EN); + writel(val, &omap->otg_wrapper->irqenable_set_1); + + /* Clear the IRQ status */ + val = readl(&omap->otg_wrapper->irqstatus_1); + writel(val, &omap->otg_wrapper->irqstatus_1); + val = readl(&omap->otg_wrapper->irqstatus_0); + writel(val, &omap->otg_wrapper->irqstatus_0); + + /* Enable the USB OTG Super speed clocks */ + val = (OPTFCLKEN_REFCLK960M | OTG_SS_CLKCTRL_MODULEMODE_HW); + setbits_le32((*prcm)->cm_l3init_usb_otg_ss_clkctrl, val); + +}; +#endif /* CONFIG_OMAP_USB3PHY1_HOST */ + +#ifdef CONFIG_OMAP_USB2PHY2_HOST +static void omap_enable_usb2_phy2(struct omap_xhci *omap) +{ + u32 reg, val; + + val = (~USB2PHY_AUTORESUME_EN & USB2PHY_DISCHGDET); + writel(val, (*ctrl)->control_srcomp_north_side); + + setbits_le32((*prcm)->cm_coreaon_usb_phy2_core_clkctrl, + USBPHY_CORE_CLKCTRL_OPTFCLKEN_CLK32K); + + setbits_le32((*prcm)->cm_l3init_hsusbhost_clkctrl, + (USBPHY_CORE_CLKCTRL_OPTFCLKEN_CLK32K | + OTG_SS_CLKCTRL_MODULEMODE_HW)); + + /* This is an undocumented Reserved register */ + reg = 0x4a0086c0; + val = readl(reg); + val |= 0x100; + setbits_le32(reg, val); +} + +void usb_phy_power(int on) +{ + return; +} +#endif /* CONFIG_OMAP_USB2PHY2_HOST */ + +#ifdef CONFIG_AM437X_USB2PHY2_HOST +static void am437x_enable_usb2_phy2(struct omap_xhci *omap) +{ + const u32 usb_otg_ss_clk_val = (USBOTGSSX_CLKCTRL_MODULE_EN | + USBOTGSSX_CLKCTRL_OPTFCLKEN_REFCLK960); + + writel(usb_otg_ss_clk_val, PRM_PER_USB_OTG_SS0_CLKCTRL); + writel(usb_otg_ss_clk_val, PRM_PER_USB_OTG_SS1_CLKCTRL); + + writel(USBPHYOCPSCP_MODULE_EN, PRM_PER_USBPHYOCP2SCP0_CLKCTRL); + writel(USBPHYOCPSCP_MODULE_EN, PRM_PER_USBPHYOCP2SCP1_CLKCTRL); +} + +void usb_phy_power(int on) +{ + return; +} +#endif /* CONFIG_AM437X_USB2PHY2_HOST */ + +void omap_reset_usb_phy(struct dwc3 *dwc3_reg) +{ + /* Assert USB3 PHY reset */ + setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Assert USB2 PHY reset */ + setbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); + + mdelay(100); + + /* Clear USB3 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Clear USB2 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST); + +} + +void omap_enable_phy(struct omap_xhci *omap) +{ +#ifdef CONFIG_OMAP_USB2PHY2_HOST + omap_enable_usb2_phy2(omap); +#endif + +#ifdef CONFIG_AM437X_USB2PHY2_HOST + am437x_enable_usb2_phy2(omap); +#endif + +#ifdef CONFIG_OMAP_USB3PHY1_HOST + omap_enable_usb3_phy(omap); + omap_usb3_phy_init(omap->usb3_phy); +#endif +} diff --git a/qemu/roms/u-boot/drivers/usb/phy/twl4030.c b/qemu/roms/u-boot/drivers/usb/phy/twl4030.c new file mode 100644 index 000000000..6dcb336bb --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/phy/twl4030.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix + * + * This is file is based on + * repository git.gitorious.org/u-boot-omap3/mainline.git, + * branch omap3-dev-usb, file drivers/usb/gadget/twl4030_usb.c + * + * This is the unique part of its copyright : + * + * ------------------------------------------------------------------------ + * + * * (C) Copyright 2009 Atin Malaviya (atin.malaviya@gmail.com) + * + * Based on: twl4030_usb.c in linux 2.6 (drivers/i2c/chips/twl4030_usb.c) + * Copyright (C) 2004-2007 Texas Instruments + * Copyright (C) 2008 Nokia Corporation + * Contact: Felipe Balbi + * + * Author: Atin Malaviya (atin.malaviya@gmail.com) + * + * ------------------------------------------------------------------------ + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +/* Defines for bits in registers */ +#define OPMODE_MASK (3 << 3) +#define XCVRSELECT_MASK (3 << 0) +#define CARKITMODE (1 << 2) +#define OTG_ENAB (1 << 5) +#define PHYPWD (1 << 0) +#define CLOCKGATING_EN (1 << 2) +#define CLK32K_EN (1 << 1) +#define REQ_PHY_DPLL_CLK (1 << 0) +#define PHY_DPLL_CLK (1 << 0) + +static int twl4030_usb_write(u8 address, u8 data) +{ + int ret; + + ret = twl4030_i2c_write_u8(TWL4030_CHIP_USB, address, data); + if (ret != 0) + printf("TWL4030:USB:Write[0x%x] Error %d\n", address, ret); + + return ret; +} + +static int twl4030_usb_read(u8 address) +{ + u8 data; + int ret; + + ret = twl4030_i2c_read_u8(TWL4030_CHIP_USB, address, &data); + if (ret == 0) + ret = data; + else + printf("TWL4030:USB:Read[0x%x] Error %d\n", address, ret); + + return ret; +} + +static void twl4030_usb_ldo_init(void) +{ + /* Enable writing to power configuration registers */ + twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_PROTECT_KEY, 0xC0); + twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_PROTECT_KEY, 0x0C); + + /* put VUSB3V1 LDO in active state */ + twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, + TWL4030_PM_RECEIVER_VUSB_DEDICATED2, 0x00); + + /* input to VUSB3V1 LDO is from VBAT, not VBUS */ + twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, + TWL4030_PM_RECEIVER_VUSB_DEDICATED1, 0x14); + + /* turn on 3.1V regulator */ + twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, + TWL4030_PM_RECEIVER_VUSB3V1_DEV_GRP, 0x20); + twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, + TWL4030_PM_RECEIVER_VUSB3V1_TYPE, 0x00); + + /* turn on 1.5V regulator */ + twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, + TWL4030_PM_RECEIVER_VUSB1V5_DEV_GRP, 0x20); + twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, + TWL4030_PM_RECEIVER_VUSB1V5_TYPE, 0x00); + + /* turn on 1.8V regulator */ + twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, + TWL4030_PM_RECEIVER_VUSB1V8_DEV_GRP, 0x20); + twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, + TWL4030_PM_RECEIVER_VUSB1V8_TYPE, 0x00); + + /* disable access to power configuration registers */ + twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_PROTECT_KEY, 0x00); +} + +static void twl4030_phy_power(void) +{ + u8 pwr, clk; + + /* Power the PHY */ + pwr = twl4030_usb_read(TWL4030_USB_PHY_PWR_CTRL); + pwr &= ~PHYPWD; + twl4030_usb_write(TWL4030_USB_PHY_PWR_CTRL, pwr); + /* Enable clocks */ + clk = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL); + clk |= CLOCKGATING_EN | CLK32K_EN; + twl4030_usb_write(TWL4030_USB_PHY_CLK_CTRL, clk); +} + +/* + * Initiaze the ULPI interface + * ULPI : Universal Transceiver Macrocell Low Pin Interface + * An interface between the USB link controller like musb and the + * the PHY or transceiver that drives the actual bus. + */ +int twl4030_usb_ulpi_init(void) +{ + long timeout = 1000 * 1000; /* 1 sec */; + u8 clk, sts, pwr; + + /* twl4030 ldo init */ + twl4030_usb_ldo_init(); + + /* Enable the twl4030 phy */ + twl4030_phy_power(); + + /* Enable DPLL to access PHY registers over I2C */ + clk = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL); + clk |= REQ_PHY_DPLL_CLK; + twl4030_usb_write(TWL4030_USB_PHY_CLK_CTRL, clk); + + /* Check if the PHY DPLL is locked */ + sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS); + while (!(sts & PHY_DPLL_CLK) && 0 < timeout) { + udelay(10); + sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS); + timeout -= 10; + } + + /* Final check */ + sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS); + if (!(sts & PHY_DPLL_CLK)) { + printf("Error:TWL4030:USB Timeout setting PHY DPLL clock\n"); + return -1; + } + + /* + * There are two circuit blocks attached to the PHY, + * Carkit and USB OTG. Disable Carkit and enable USB OTG + */ + twl4030_usb_write(TWL4030_USB_IFC_CTRL_CLR, CARKITMODE); + pwr = twl4030_usb_read(TWL4030_USB_POWER_CTRL); + pwr |= OTG_ENAB; + twl4030_usb_write(TWL4030_USB_POWER_CTRL_SET, pwr); + + /* Clear the opmode bits to ensure normal encode */ + twl4030_usb_write(TWL4030_USB_FUNC_CTRL_CLR, OPMODE_MASK); + + /* Clear the xcvrselect bits to enable the high speed transeiver */ + twl4030_usb_write(TWL4030_USB_FUNC_CTRL_CLR, XCVRSELECT_MASK); + + /* Let ULPI control the DPLL clock */ + clk = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL); + clk &= ~REQ_PHY_DPLL_CLK; + twl4030_usb_write(TWL4030_USB_PHY_CLK_CTRL, clk); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/usb/ulpi/Makefile b/qemu/roms/u-boot/drivers/usb/ulpi/Makefile new file mode 100644 index 000000000..a21fe2c93 --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/ulpi/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2011 Jana Rapava +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_USB_ULPI) += ulpi.o +obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi-viewport.o +obj-$(CONFIG_USB_ULPI_VIEWPORT_OMAP) += omap-ulpi-viewport.o diff --git a/qemu/roms/u-boot/drivers/usb/ulpi/omap-ulpi-viewport.c b/qemu/roms/u-boot/drivers/usb/ulpi/omap-ulpi-viewport.c new file mode 100644 index 000000000..4db7fa43c --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/ulpi/omap-ulpi-viewport.c @@ -0,0 +1,81 @@ +/* + * OMAP ulpi viewport support + * Based on drivers/usb/ulpi/ulpi-viewport.c + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com + * Author: Govindraj R + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#define OMAP_ULPI_WR_OPSEL (2 << 22) +#define OMAP_ULPI_RD_OPSEL (3 << 22) +#define OMAP_ULPI_START (1 << 31) + +/* + * Wait for having ulpi in done state + */ +static int ulpi_wait(struct ulpi_viewport *ulpi_vp, u32 mask) +{ + int timeout = CONFIG_USB_ULPI_TIMEOUT; + + while (--timeout) { + if (!(readl(ulpi_vp->viewport_addr) & mask)) + return 0; + + udelay(1); + } + + return ULPI_ERROR; +} + +/* + * Issue a ULPI read/write request + */ +static int ulpi_request(struct ulpi_viewport *ulpi_vp, u32 value) +{ + int err; + + writel(value, ulpi_vp->viewport_addr); + + err = ulpi_wait(ulpi_vp, OMAP_ULPI_START); + if (err) + debug("ULPI request timed out\n"); + + return err; +} + +int ulpi_write(struct ulpi_viewport *ulpi_vp, u8 *reg, u32 value) +{ + u32 val = OMAP_ULPI_START | (((ulpi_vp->port_num + 1) & 0xf) << 24) | + OMAP_ULPI_WR_OPSEL | ((u32)reg << 16) | (value & 0xff); + + return ulpi_request(ulpi_vp, val); +} + +u32 ulpi_read(struct ulpi_viewport *ulpi_vp, u8 *reg) +{ + int err; + u32 val = OMAP_ULPI_START | (((ulpi_vp->port_num + 1) & 0xf) << 24) | + OMAP_ULPI_RD_OPSEL | ((u32)reg << 16); + + err = ulpi_request(ulpi_vp, val); + if (err) + return err; + + return readl(ulpi_vp->viewport_addr) & 0xff; +} diff --git a/qemu/roms/u-boot/drivers/usb/ulpi/ulpi-viewport.c b/qemu/roms/u-boot/drivers/usb/ulpi/ulpi-viewport.c new file mode 100644 index 000000000..b4974ed2a --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/ulpi/ulpi-viewport.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 Jana Rapava + * Copyright (C) 2011 CompuLab, Ltd. + * + * Authors: Jana Rapava + * Igor Grinberg + * + * Based on: + * linux/drivers/usb/otg/ulpi_viewport.c + * + * Original Copyright follow: + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +/* ULPI viewport control bits */ +#define ULPI_SS (1 << 27) +#define ULPI_RWCTRL (1 << 29) +#define ULPI_RWRUN (1 << 30) +#define ULPI_WU (1 << 31) + +/* + * Wait for the ULPI request to complete + * + * @ulpi_viewport - the address of the viewport + * @mask - expected value to wait for + * + * returns 0 on mask match, ULPI_ERROR on time out. + */ +static int ulpi_wait(struct ulpi_viewport *ulpi_vp, u32 mask) +{ + int timeout = CONFIG_USB_ULPI_TIMEOUT; + + /* Wait for the bits in mask to become zero. */ + while (--timeout) { + if ((readl(ulpi_vp->viewport_addr) & mask) == 0) + return 0; + + udelay(1); + } + + return ULPI_ERROR; +} + +/* + * Wake the ULPI PHY up for communication + * + * returns 0 on success. + */ +static int ulpi_wakeup(struct ulpi_viewport *ulpi_vp) +{ + int err; + + if (readl(ulpi_vp->viewport_addr) & ULPI_SS) + return 0; /* already awake */ + + writel(ULPI_WU, ulpi_vp->viewport_addr); + + err = ulpi_wait(ulpi_vp, ULPI_WU); + if (err) + printf("ULPI wakeup timed out\n"); + + return err; +} + +/* + * Issue a ULPI read/write request + * + * @value - the ULPI request + */ +static int ulpi_request(struct ulpi_viewport *ulpi_vp, u32 value) +{ + int err; + + err = ulpi_wakeup(ulpi_vp); + if (err) + return err; + + writel(value, ulpi_vp->viewport_addr); + + err = ulpi_wait(ulpi_vp, ULPI_RWRUN); + if (err) + printf("ULPI request timed out\n"); + + return err; +} + +int ulpi_write(struct ulpi_viewport *ulpi_vp, u8 *reg, u32 value) +{ + u32 val = ULPI_RWRUN | ULPI_RWCTRL | ((u32)reg << 16) | (value & 0xff); + + val |= (ulpi_vp->port_num & 0x7) << 24; + return ulpi_request(ulpi_vp, val); +} + +u32 ulpi_read(struct ulpi_viewport *ulpi_vp, u8 *reg) +{ + int err; + u32 val = ULPI_RWRUN | ((u32)reg << 16); + + val |= (ulpi_vp->port_num & 0x7) << 24; + err = ulpi_request(ulpi_vp, val); + if (err) + return err; + + return (readl(ulpi_vp->viewport_addr) >> 8) & 0xff; +} diff --git a/qemu/roms/u-boot/drivers/usb/ulpi/ulpi.c b/qemu/roms/u-boot/drivers/usb/ulpi/ulpi.c new file mode 100644 index 000000000..f3d18564b --- /dev/null +++ b/qemu/roms/u-boot/drivers/usb/ulpi/ulpi.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2011 Jana Rapava + * Copyright (C) 2011 CompuLab, Ltd. + * + * Authors: Jana Rapava + * Igor Grinberg + * + * Based on: + * linux/drivers/usb/otg/ulpi.c + * Generic ULPI USB transceiver support + * + * Original Copyright follow: + * Copyright (C) 2009 Daniel Mack + * + * Based on sources from + * + * Sascha Hauer + * Freescale Semiconductors + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define ULPI_ID_REGS_COUNT 4 +#define ULPI_TEST_VALUE 0x55 /* 0x55 == 0b01010101 */ + +static struct ulpi_regs *ulpi = (struct ulpi_regs *)0; + +static int ulpi_integrity_check(struct ulpi_viewport *ulpi_vp) +{ + u32 val, tval = ULPI_TEST_VALUE; + int err, i; + + /* Use the 'special' test value to check all bits */ + for (i = 0; i < 2; i++, tval <<= 1) { + err = ulpi_write(ulpi_vp, &ulpi->scratch, tval); + if (err) + return err; + + val = ulpi_read(ulpi_vp, &ulpi->scratch); + if (val != tval) { + printf("ULPI integrity check failed\n"); + return val; + } + } + + return 0; +} + +int ulpi_init(struct ulpi_viewport *ulpi_vp) +{ + u32 val, id = 0; + u8 *reg = &ulpi->product_id_high; + int i; + + /* Assemble ID from four ULPI ID registers (8 bits each). */ + for (i = 0; i < ULPI_ID_REGS_COUNT; i++) { + val = ulpi_read(ulpi_vp, reg - i); + if (val == ULPI_ERROR) + return val; + + id = (id << 8) | val; + } + + /* Split ID into vendor and product ID. */ + debug("ULPI transceiver ID 0x%04x:0x%04x\n", id >> 16, id & 0xffff); + + return ulpi_integrity_check(ulpi_vp); +} + +int ulpi_select_transceiver(struct ulpi_viewport *ulpi_vp, unsigned speed) +{ + u32 tspeed = ULPI_FC_FULL_SPEED; + u32 val; + + switch (speed) { + case ULPI_FC_HIGH_SPEED: + case ULPI_FC_FULL_SPEED: + case ULPI_FC_LOW_SPEED: + case ULPI_FC_FS4LS: + tspeed = speed; + break; + default: + printf("ULPI: %s: wrong transceiver speed specified: %u, " + "falling back to full speed\n", __func__, speed); + } + + val = ulpi_read(ulpi_vp, &ulpi->function_ctrl); + if (val == ULPI_ERROR) + return val; + + /* clear the previous speed setting */ + val = (val & ~ULPI_FC_XCVRSEL_MASK) | tspeed; + + return ulpi_write(ulpi_vp, &ulpi->function_ctrl, val); +} + +int ulpi_set_vbus(struct ulpi_viewport *ulpi_vp, int on, int ext_power) +{ + u32 flags = ULPI_OTG_DRVVBUS; + u8 *reg = on ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; + + if (ext_power) + flags |= ULPI_OTG_DRVVBUS_EXT; + + return ulpi_write(ulpi_vp, reg, flags); +} + +int ulpi_set_vbus_indicator(struct ulpi_viewport *ulpi_vp, int external, + int passthu, int complement) +{ + u32 flags, val; + u8 *reg; + + reg = external ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; + val = ulpi_write(ulpi_vp, reg, ULPI_OTG_EXTVBUSIND); + if (val) + return val; + + flags = passthu ? ULPI_IFACE_PASSTHRU : 0; + flags |= complement ? ULPI_IFACE_EXTVBUS_COMPLEMENT : 0; + + val = ulpi_read(ulpi_vp, &ulpi->iface_ctrl); + if (val == ULPI_ERROR) + return val; + + val = val & ~(ULPI_IFACE_PASSTHRU & ULPI_IFACE_EXTVBUS_COMPLEMENT); + val |= flags; + val = ulpi_write(ulpi_vp, &ulpi->iface_ctrl, val); + if (val) + return val; + + return 0; +} + +int ulpi_set_pd(struct ulpi_viewport *ulpi_vp, int enable) +{ + u32 val = ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN; + u8 *reg = enable ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; + + return ulpi_write(ulpi_vp, reg, val); +} + +int ulpi_opmode_sel(struct ulpi_viewport *ulpi_vp, unsigned opmode) +{ + u32 topmode = ULPI_FC_OPMODE_NORMAL; + u32 val; + + switch (opmode) { + case ULPI_FC_OPMODE_NORMAL: + case ULPI_FC_OPMODE_NONDRIVING: + case ULPI_FC_OPMODE_DISABLE_NRZI: + case ULPI_FC_OPMODE_NOSYNC_NOEOP: + topmode = opmode; + break; + default: + printf("ULPI: %s: wrong OpMode specified: %u, " + "falling back to OpMode Normal\n", __func__, opmode); + } + + val = ulpi_read(ulpi_vp, &ulpi->function_ctrl); + if (val == ULPI_ERROR) + return val; + + /* clear the previous opmode setting */ + val = (val & ~ULPI_FC_OPMODE_MASK) | topmode; + + return ulpi_write(ulpi_vp, &ulpi->function_ctrl, val); +} + +int ulpi_serial_mode_enable(struct ulpi_viewport *ulpi_vp, unsigned smode) +{ + switch (smode) { + case ULPI_IFACE_6_PIN_SERIAL_MODE: + case ULPI_IFACE_3_PIN_SERIAL_MODE: + break; + default: + printf("ULPI: %s: unrecognized Serial Mode specified: %u\n", + __func__, smode); + return ULPI_ERROR; + } + + return ulpi_write(ulpi_vp, &ulpi->iface_ctrl_set, smode); +} + +int ulpi_suspend(struct ulpi_viewport *ulpi_vp) +{ + int err; + + err = ulpi_write(ulpi_vp, &ulpi->function_ctrl_clear, + ULPI_FC_SUSPENDM); + if (err) + printf("ULPI: %s: failed writing the suspend bit\n", __func__); + + return err; +} + +/* + * Wait for ULPI PHY reset to complete. + * Actual wait for reset must be done in a view port specific way, + * because it involves checking the DIR line. + */ +static int __ulpi_reset_wait(struct ulpi_viewport *ulpi_vp) +{ + u32 val; + int timeout = CONFIG_USB_ULPI_TIMEOUT; + + /* Wait for the RESET bit to become zero */ + while (--timeout) { + /* + * This function is generic and suppose to work + * with any viewport, so we cheat here and don't check + * for the error of ulpi_read(), if there is one, then + * there will be a timeout. + */ + val = ulpi_read(ulpi_vp, &ulpi->function_ctrl); + if (!(val & ULPI_FC_RESET)) + return 0; + + udelay(1); + } + + printf("ULPI: %s: reset timed out\n", __func__); + + return ULPI_ERROR; +} +int ulpi_reset_wait(struct ulpi_viewport *ulpi_vp) + __attribute__((weak, alias("__ulpi_reset_wait"))); + +int ulpi_reset(struct ulpi_viewport *ulpi_vp) +{ + int err; + + err = ulpi_write(ulpi_vp, + &ulpi->function_ctrl_set, ULPI_FC_RESET); + if (err) { + printf("ULPI: %s: failed writing reset bit\n", __func__); + return err; + } + + return ulpi_reset_wait(ulpi_vp); +} diff --git a/qemu/roms/u-boot/drivers/video/Makefile b/qemu/roms/u-boot/drivers/video/Makefile new file mode 100644 index 000000000..c52702924 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/Makefile @@ -0,0 +1,42 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o +obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o +obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o +obj-$(CONFIG_CFB_CONSOLE) += cfb_console.o +obj-$(CONFIG_EXYNOS_DP) += exynos_dp.o exynos_dp_lowlevel.o +obj-$(CONFIG_EXYNOS_FB) += exynos_fb.o exynos_fimd.o +obj-$(CONFIG_EXYNOS_MIPI_DSIM) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \ + exynos_mipi_dsi_lowlevel.o +obj-$(CONFIG_EXYNOS_PWM_BL) += exynos_pwm_bl.o +obj-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o +obj-$(CONFIG_L5F31188) += l5f31188.o +obj-$(CONFIG_MPC8XX_LCD) += mpc8xx_lcd.o +obj-$(CONFIG_PXA_LCD) += pxa_lcd.o +obj-$(CONFIG_SCF0403_LCD) += scf0403_lcd.o +obj-$(CONFIG_S6E8AX0) += s6e8ax0.o +obj-$(CONFIG_S6E63D6) += s6e63d6.o +obj-$(CONFIG_LD9040) += ld9040.o +obj-$(CONFIG_SED156X) += sed156x.o +obj-$(CONFIG_VIDEO_BCM2835) += bcm2835.o +obj-$(CONFIG_VIDEO_COREBOOT) += coreboot_fb.o +obj-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o +obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o +obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o +obj-$(CONFIG_VIDEO_MB86R0xGDC) += mb86r0xgdc.o videomodes.o +obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o +obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o +obj-$(CONFIG_VIDEO_MXS) += mxsfb.o videomodes.o +obj-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o +obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o +obj-$(CONFIG_VIDEO_SED13806) += sed13806.o +obj-$(CONFIG_VIDEO_SM501) += sm501.o +obj-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o +obj-$(CONFIG_VIDEO_TEGRA) += tegra.o +obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o +obj-$(CONFIG_FORMIKE) += formike.o diff --git a/qemu/roms/u-boot/drivers/video/ati_ids.h b/qemu/roms/u-boot/drivers/video/ati_ids.h new file mode 100644 index 000000000..3e72a7dd4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/ati_ids.h @@ -0,0 +1,211 @@ +/* + * ATI PCI IDs from XFree86, kept here to make sync'ing with + * XFree much simpler. Currently, this list is only used by + * radeonfb + */ + +#define PCI_CHIP_RV380_3150 0x3150 +#define PCI_CHIP_RV380_3151 0x3151 +#define PCI_CHIP_RV380_3152 0x3152 +#define PCI_CHIP_RV380_3153 0x3153 +#define PCI_CHIP_RV380_3154 0x3154 +#define PCI_CHIP_RV380_3156 0x3156 +#define PCI_CHIP_RV380_3E50 0x3E50 +#define PCI_CHIP_RV380_3E51 0x3E51 +#define PCI_CHIP_RV380_3E52 0x3E52 +#define PCI_CHIP_RV380_3E53 0x3E53 +#define PCI_CHIP_RV380_3E54 0x3E54 +#define PCI_CHIP_RV380_3E56 0x3E56 +#define PCI_CHIP_RS100_4136 0x4136 +#define PCI_CHIP_RS200_4137 0x4137 +#define PCI_CHIP_R300_AD 0x4144 +#define PCI_CHIP_R300_AE 0x4145 +#define PCI_CHIP_R300_AF 0x4146 +#define PCI_CHIP_R300_AG 0x4147 +#define PCI_CHIP_R350_AH 0x4148 +#define PCI_CHIP_R350_AI 0x4149 +#define PCI_CHIP_R350_AJ 0x414A +#define PCI_CHIP_R350_AK 0x414B +#define PCI_CHIP_RV350_AP 0x4150 +#define PCI_CHIP_RV350_AQ 0x4151 +#define PCI_CHIP_RV360_AR 0x4152 +#define PCI_CHIP_RV350_AS 0x4153 +#define PCI_CHIP_RV350_AT 0x4154 +#define PCI_CHIP_RV350_AV 0x4156 +#define PCI_CHIP_MACH32 0x4158 +#define PCI_CHIP_RS250_4237 0x4237 +#define PCI_CHIP_R200_BB 0x4242 +#define PCI_CHIP_R200_BC 0x4243 +#define PCI_CHIP_RS100_4336 0x4336 +#define PCI_CHIP_RS200_4337 0x4337 +#define PCI_CHIP_MACH64CT 0x4354 +#define PCI_CHIP_MACH64CX 0x4358 +#define PCI_CHIP_RS250_4437 0x4437 +#define PCI_CHIP_MACH64ET 0x4554 +#define PCI_CHIP_MACH64GB 0x4742 +#define PCI_CHIP_MACH64GD 0x4744 +#define PCI_CHIP_MACH64GI 0x4749 +#define PCI_CHIP_MACH64GL 0x474C +#define PCI_CHIP_MACH64GM 0x474D +#define PCI_CHIP_MACH64GN 0x474E +#define PCI_CHIP_MACH64GO 0x474F +#define PCI_CHIP_MACH64GP 0x4750 +#define PCI_CHIP_MACH64GQ 0x4751 +#define PCI_CHIP_MACH64GR 0x4752 +#define PCI_CHIP_MACH64GS 0x4753 +#define PCI_CHIP_MACH64GT 0x4754 +#define PCI_CHIP_MACH64GU 0x4755 +#define PCI_CHIP_MACH64GV 0x4756 +#define PCI_CHIP_MACH64GW 0x4757 +#define PCI_CHIP_MACH64GX 0x4758 +#define PCI_CHIP_MACH64GY 0x4759 +#define PCI_CHIP_MACH64GZ 0x475A +#define PCI_CHIP_RV250_Id 0x4964 +#define PCI_CHIP_RV250_Ie 0x4965 +#define PCI_CHIP_RV250_If 0x4966 +#define PCI_CHIP_RV250_Ig 0x4967 +#define PCI_CHIP_R420_JH 0x4A48 +#define PCI_CHIP_R420_JI 0x4A49 +#define PCI_CHIP_R420_JJ 0x4A4A +#define PCI_CHIP_R420_JK 0x4A4B +#define PCI_CHIP_R420_JL 0x4A4C +#define PCI_CHIP_R420_JM 0x4A4D +#define PCI_CHIP_R420_JN 0x4A4E +#define PCI_CHIP_R420_JP 0x4A50 +#define PCI_CHIP_MACH64LB 0x4C42 +#define PCI_CHIP_MACH64LD 0x4C44 +#define PCI_CHIP_RAGE128LE 0x4C45 +#define PCI_CHIP_RAGE128LF 0x4C46 +#define PCI_CHIP_MACH64LG 0x4C47 +#define PCI_CHIP_MACH64LI 0x4C49 +#define PCI_CHIP_MACH64LM 0x4C4D +#define PCI_CHIP_MACH64LN 0x4C4E +#define PCI_CHIP_MACH64LP 0x4C50 +#define PCI_CHIP_MACH64LQ 0x4C51 +#define PCI_CHIP_MACH64LR 0x4C52 +#define PCI_CHIP_MACH64LS 0x4C53 +#define PCI_CHIP_MACH64LT 0x4C54 +#define PCI_CHIP_RADEON_LW 0x4C57 +#define PCI_CHIP_RADEON_LX 0x4C58 +#define PCI_CHIP_RADEON_LY 0x4C59 +#define PCI_CHIP_RADEON_LZ 0x4C5A +#define PCI_CHIP_RV250_Ld 0x4C64 +#define PCI_CHIP_RV250_Le 0x4C65 +#define PCI_CHIP_RV250_Lf 0x4C66 +#define PCI_CHIP_RV250_Lg 0x4C67 +#define PCI_CHIP_RV250_Ln 0x4C6E +#define PCI_CHIP_RAGE128MF 0x4D46 +#define PCI_CHIP_RAGE128ML 0x4D4C +#define PCI_CHIP_R300_ND 0x4E44 +#define PCI_CHIP_R300_NE 0x4E45 +#define PCI_CHIP_R300_NF 0x4E46 +#define PCI_CHIP_R300_NG 0x4E47 +#define PCI_CHIP_R350_NH 0x4E48 +#define PCI_CHIP_R350_NI 0x4E49 +#define PCI_CHIP_R360_NJ 0x4E4A +#define PCI_CHIP_R350_NK 0x4E4B +#define PCI_CHIP_RV350_NP 0x4E50 +#define PCI_CHIP_RV350_NQ 0x4E51 +#define PCI_CHIP_RV350_NR 0x4E52 +#define PCI_CHIP_RV350_NS 0x4E53 +#define PCI_CHIP_RV350_NT 0x4E54 +#define PCI_CHIP_RV350_NV 0x4E56 +#define PCI_CHIP_RAGE128PA 0x5041 +#define PCI_CHIP_RAGE128PB 0x5042 +#define PCI_CHIP_RAGE128PC 0x5043 +#define PCI_CHIP_RAGE128PD 0x5044 +#define PCI_CHIP_RAGE128PE 0x5045 +#define PCI_CHIP_RAGE128PF 0x5046 +#define PCI_CHIP_RAGE128PG 0x5047 +#define PCI_CHIP_RAGE128PH 0x5048 +#define PCI_CHIP_RAGE128PI 0x5049 +#define PCI_CHIP_RAGE128PJ 0x504A +#define PCI_CHIP_RAGE128PK 0x504B +#define PCI_CHIP_RAGE128PL 0x504C +#define PCI_CHIP_RAGE128PM 0x504D +#define PCI_CHIP_RAGE128PN 0x504E +#define PCI_CHIP_RAGE128PO 0x504F +#define PCI_CHIP_RAGE128PP 0x5050 +#define PCI_CHIP_RAGE128PQ 0x5051 +#define PCI_CHIP_RAGE128PR 0x5052 +#define PCI_CHIP_RAGE128PS 0x5053 +#define PCI_CHIP_RAGE128PT 0x5054 +#define PCI_CHIP_RAGE128PU 0x5055 +#define PCI_CHIP_RAGE128PV 0x5056 +#define PCI_CHIP_RAGE128PW 0x5057 +#define PCI_CHIP_RAGE128PX 0x5058 +#define PCI_CHIP_RADEON_QD 0x5144 +#define PCI_CHIP_RADEON_QE 0x5145 +#define PCI_CHIP_RADEON_QF 0x5146 +#define PCI_CHIP_RADEON_QG 0x5147 +#define PCI_CHIP_R200_QH 0x5148 +#define PCI_CHIP_R200_QI 0x5149 +#define PCI_CHIP_R200_QJ 0x514A +#define PCI_CHIP_R200_QK 0x514B +#define PCI_CHIP_R200_QL 0x514C +#define PCI_CHIP_R200_QM 0x514D +#define PCI_CHIP_R200_QN 0x514E +#define PCI_CHIP_R200_QO 0x514F +#define PCI_CHIP_RV200_QW 0x5157 +#define PCI_CHIP_RV200_QX 0x5158 +#define PCI_CHIP_RV100_QY 0x5159 +#define PCI_CHIP_RV100_QZ 0x515A +#define PCI_CHIP_RN50 0x515E +#define PCI_CHIP_RAGE128RE 0x5245 +#define PCI_CHIP_RAGE128RF 0x5246 +#define PCI_CHIP_RAGE128RG 0x5247 +#define PCI_CHIP_RAGE128RK 0x524B +#define PCI_CHIP_RAGE128RL 0x524C +#define PCI_CHIP_RAGE128SE 0x5345 +#define PCI_CHIP_RAGE128SF 0x5346 +#define PCI_CHIP_RAGE128SG 0x5347 +#define PCI_CHIP_RAGE128SH 0x5348 +#define PCI_CHIP_RAGE128SK 0x534B +#define PCI_CHIP_RAGE128SL 0x534C +#define PCI_CHIP_RAGE128SM 0x534D +#define PCI_CHIP_RAGE128SN 0x534E +#define PCI_CHIP_RAGE128TF 0x5446 +#define PCI_CHIP_RAGE128TL 0x544C +#define PCI_CHIP_RAGE128TR 0x5452 +#define PCI_CHIP_RAGE128TS 0x5453 +#define PCI_CHIP_RAGE128TT 0x5454 +#define PCI_CHIP_RAGE128TU 0x5455 +#define PCI_CHIP_RV370_5460 0x5460 +#define PCI_CHIP_RV370_5461 0x5461 +#define PCI_CHIP_RV370_5462 0x5462 +#define PCI_CHIP_RV370_5463 0x5463 +#define PCI_CHIP_RV370_5464 0x5464 +#define PCI_CHIP_RV370_5465 0x5465 +#define PCI_CHIP_RV370_5466 0x5466 +#define PCI_CHIP_RV370_5467 0x5467 +#define PCI_CHIP_R423_UH 0x5548 +#define PCI_CHIP_R423_UI 0x5549 +#define PCI_CHIP_R423_UJ 0x554A +#define PCI_CHIP_R423_UK 0x554B +#define PCI_CHIP_R423_UQ 0x5551 +#define PCI_CHIP_R423_UR 0x5552 +#define PCI_CHIP_R423_UT 0x5554 +#define PCI_CHIP_MACH64VT 0x5654 +#define PCI_CHIP_MACH64VU 0x5655 +#define PCI_CHIP_MACH64VV 0x5656 +#define PCI_CHIP_RS300_5834 0x5834 +#define PCI_CHIP_RS300_5835 0x5835 +#define PCI_CHIP_RS300_5836 0x5836 +#define PCI_CHIP_RS300_5837 0x5837 +#define PCI_CHIP_RV370_5B60 0x5B60 +#define PCI_CHIP_RV370_5B61 0x5B61 +#define PCI_CHIP_RV370_5B62 0x5B62 +#define PCI_CHIP_RV370_5B63 0x5B63 +#define PCI_CHIP_RV370_5B64 0x5B64 +#define PCI_CHIP_RV370_5B65 0x5B65 +#define PCI_CHIP_RV370_5B66 0x5B66 +#define PCI_CHIP_RV370_5B67 0x5B67 +#define PCI_CHIP_RV280_5960 0x5960 +#define PCI_CHIP_RV280_5961 0x5961 +#define PCI_CHIP_RV280_5962 0x5962 +#define PCI_CHIP_RV280_5964 0x5964 +#define PCI_CHIP_RV280_5C61 0x5C61 +#define PCI_CHIP_RV280_5C63 0x5C63 +#define PCI_CHIP_R423_5D57 0x5D57 +#define PCI_CHIP_RS350_7834 0x7834 +#define PCI_CHIP_RS350_7835 0x7835 diff --git a/qemu/roms/u-boot/drivers/video/ati_radeon_fb.c b/qemu/roms/u-boot/drivers/video/ati_radeon_fb.c new file mode 100644 index 000000000..38d2eb107 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/ati_radeon_fb.c @@ -0,0 +1,764 @@ +/* + * ATI Radeon Video card Framebuffer driver. + * + * Copyright 2007 Freescale Semiconductor, Inc. + * Zhang Wei + * Jason Jin + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Some codes of this file is partly ported from Linux kernel + * ATI video framebuffer driver. + * + * Now the driver is tested on below ATI chips: + * 9200 + * X300 + * X700 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "videomodes.h" + +#include +#include "ati_ids.h" +#include "ati_radeon_fb.h" + +#undef DEBUG + +#ifdef DEBUG +#define DPRINT(x...) printf(x) +#else +#define DPRINT(x...) do{}while(0) +#endif + +#ifndef min_t +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +#endif + +#define MAX_MAPPED_VRAM (2048*2048*4) +#define MIN_MAPPED_VRAM (1024*768*1) + +#define RADEON_BUFFER_ALIGN 0x00000fff +#define SURF_UPPER_BOUND(x,y,bpp) (((((x) * (((y) + 15) & ~15) * (bpp)/8) + RADEON_BUFFER_ALIGN) \ + & ~RADEON_BUFFER_ALIGN) - 1) +#define RADEON_CRT_PITCH(width, bpp) ((((width) * (bpp) + ((bpp) * 8 - 1)) / ((bpp) * 8)) | \ + ((((width) * (bpp) + ((bpp) * 8 - 1)) / ((bpp) * 8)) << 16)) + +#define CRTC_H_TOTAL_DISP_VAL(htotal, hdisp) \ + (((((htotal) / 8) - 1) & 0x3ff) | (((((hdisp) / 8) - 1) & 0x1ff) << 16)) +#define CRTC_HSYNC_STRT_WID_VAL(hsync_srtr, hsync_wid) \ + (((hsync_srtr) & 0x1fff) | (((hsync_wid) & 0x3f) << 16)) +#define CRTC_V_TOTAL_DISP_VAL(vtotal, vdisp) \ + ((((vtotal) - 1) & 0xffff) | (((vdisp) - 1) << 16)) +#define CRTC_VSYNC_STRT_WID_VAL(vsync_srtr, vsync_wid) \ + ((((vsync_srtr) - 1) & 0xfff) | (((vsync_wid) & 0x1f) << 16)) + +/*#define PCI_VENDOR_ID_ATI*/ +#define PCI_CHIP_RV280_5960 0x5960 +#define PCI_CHIP_RV280_5961 0x5961 +#define PCI_CHIP_RV280_5962 0x5962 +#define PCI_CHIP_RV280_5964 0x5964 +#define PCI_CHIP_RV280_5C63 0x5C63 +#define PCI_CHIP_RV370_5B60 0x5B60 +#define PCI_CHIP_RV380_5657 0x5657 +#define PCI_CHIP_R420_554d 0x554d + +static struct pci_device_id ati_radeon_pci_ids[] = { + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5960}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5961}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5962}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5964}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5C63}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV370_5B60}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV380_5657}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_R420_554d}, + {0, 0} +}; + +static u16 ati_radeon_id_family_table[][2] = { + {PCI_CHIP_RV280_5960, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV280_5961, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV280_5962, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV280_5964, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV280_5C63, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV370_5B60, CHIP_FAMILY_RV380}, + {PCI_CHIP_RV380_5657, CHIP_FAMILY_RV380}, + {PCI_CHIP_R420_554d, CHIP_FAMILY_R420}, + {0, 0} +}; + +u16 get_radeon_id_family(u16 device) +{ + int i; + for (i=0; ati_radeon_id_family_table[0][i]; i+=2) + if (ati_radeon_id_family_table[0][i] == device) + return ati_radeon_id_family_table[0][i + 1]; + return 0; +} + +struct radeonfb_info *rinfo; + +static void radeon_identify_vram(struct radeonfb_info *rinfo) +{ + u32 tmp; + + /* framebuffer size */ + if ((rinfo->family == CHIP_FAMILY_RS100) || + (rinfo->family == CHIP_FAMILY_RS200) || + (rinfo->family == CHIP_FAMILY_RS300)) { + u32 tom = INREG(NB_TOM); + tmp = ((((tom >> 16) - (tom & 0xffff) + 1) << 6) * 1024); + + radeon_fifo_wait(6); + OUTREG(MC_FB_LOCATION, tom); + OUTREG(DISPLAY_BASE_ADDR, (tom & 0xffff) << 16); + OUTREG(CRTC2_DISPLAY_BASE_ADDR, (tom & 0xffff) << 16); + OUTREG(OV0_BASE_ADDR, (tom & 0xffff) << 16); + + /* This is supposed to fix the crtc2 noise problem. */ + OUTREG(GRPH2_BUFFER_CNTL, INREG(GRPH2_BUFFER_CNTL) & ~0x7f0000); + + if ((rinfo->family == CHIP_FAMILY_RS100) || + (rinfo->family == CHIP_FAMILY_RS200)) { + /* This is to workaround the asic bug for RMX, some versions + of BIOS dosen't have this register initialized correctly. + */ + OUTREGP(CRTC_MORE_CNTL, CRTC_H_CUTOFF_ACTIVE_EN, + ~CRTC_H_CUTOFF_ACTIVE_EN); + } + } else { + tmp = INREG(CONFIG_MEMSIZE); + } + + /* mem size is bits [28:0], mask off the rest */ + rinfo->video_ram = tmp & CONFIG_MEMSIZE_MASK; + + /* + * Hack to get around some busted production M6's + * reporting no ram + */ + if (rinfo->video_ram == 0) { + switch (rinfo->pdev.device) { + case PCI_CHIP_RADEON_LY: + case PCI_CHIP_RADEON_LZ: + rinfo->video_ram = 8192 * 1024; + break; + default: + break; + } + } + + /* + * Now try to identify VRAM type + */ + if ((rinfo->family >= CHIP_FAMILY_R300) || + (INREG(MEM_SDRAM_MODE_REG) & (1<<30))) + rinfo->vram_ddr = 1; + else + rinfo->vram_ddr = 0; + + tmp = INREG(MEM_CNTL); + if (IS_R300_VARIANT(rinfo)) { + tmp &= R300_MEM_NUM_CHANNELS_MASK; + switch (tmp) { + case 0: rinfo->vram_width = 64; break; + case 1: rinfo->vram_width = 128; break; + case 2: rinfo->vram_width = 256; break; + default: rinfo->vram_width = 128; break; + } + } else if ((rinfo->family == CHIP_FAMILY_RV100) || + (rinfo->family == CHIP_FAMILY_RS100) || + (rinfo->family == CHIP_FAMILY_RS200)){ + if (tmp & RV100_MEM_HALF_MODE) + rinfo->vram_width = 32; + else + rinfo->vram_width = 64; + } else { + if (tmp & MEM_NUM_CHANNELS_MASK) + rinfo->vram_width = 128; + else + rinfo->vram_width = 64; + } + + /* This may not be correct, as some cards can have half of channel disabled + * ToDo: identify these cases + */ + + DPRINT("radeonfb: Found %dk of %s %d bits wide videoram\n", + rinfo->video_ram / 1024, + rinfo->vram_ddr ? "DDR" : "SDRAM", + rinfo->vram_width); + +} + +static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *mode) +{ + int i; + + radeon_fifo_wait(20); + +#if 0 + /* Workaround from XFree */ + if (rinfo->is_mobility) { + /* A temporal workaround for the occational blanking on certain laptop + * panels. This appears to related to the PLL divider registers + * (fail to lock?). It occurs even when all dividers are the same + * with their old settings. In this case we really don't need to + * fiddle with PLL registers. By doing this we can avoid the blanking + * problem with some panels. + */ + if ((mode->ppll_ref_div == (INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK)) && + (mode->ppll_div_3 == (INPLL(PPLL_DIV_3) & + (PPLL_POST3_DIV_MASK | PPLL_FB3_DIV_MASK)))) { + /* We still have to force a switch to selected PPLL div thanks to + * an XFree86 driver bug which will switch it away in some cases + * even when using UseFDev */ + OUTREGP(CLOCK_CNTL_INDEX, + mode->clk_cntl_index & PPLL_DIV_SEL_MASK, + ~PPLL_DIV_SEL_MASK); + radeon_pll_errata_after_index(rinfo); + radeon_pll_errata_after_data(rinfo); + return; + } + } +#endif + if(rinfo->pdev.device == PCI_CHIP_RV370_5B60) return; + + /* Swich VCKL clock input to CPUCLK so it stays fed while PPLL updates*/ + OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_CPUCLK, ~VCLK_SRC_SEL_MASK); + + /* Reset PPLL & enable atomic update */ + OUTPLLP(PPLL_CNTL, + PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN, + ~(PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN)); + + /* Switch to selected PPLL divider */ + OUTREGP(CLOCK_CNTL_INDEX, + mode->clk_cntl_index & PPLL_DIV_SEL_MASK, + ~PPLL_DIV_SEL_MASK); + + /* Set PPLL ref. div */ + if (rinfo->family == CHIP_FAMILY_R300 || + rinfo->family == CHIP_FAMILY_RS300 || + rinfo->family == CHIP_FAMILY_R350 || + rinfo->family == CHIP_FAMILY_RV350) { + if (mode->ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { + /* When restoring console mode, use saved PPLL_REF_DIV + * setting. + */ + OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, 0); + } else { + /* R300 uses ref_div_acc field as real ref divider */ + OUTPLLP(PPLL_REF_DIV, + (mode->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), + ~R300_PPLL_REF_DIV_ACC_MASK); + } + } else + OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, ~PPLL_REF_DIV_MASK); + + /* Set PPLL divider 3 & post divider*/ + OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_FB3_DIV_MASK); + OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_POST3_DIV_MASK); + + /* Write update */ + while (INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R) + ; + OUTPLLP(PPLL_REF_DIV, PPLL_ATOMIC_UPDATE_W, ~PPLL_ATOMIC_UPDATE_W); + + /* Wait read update complete */ + /* FIXME: Certain revisions of R300 can't recover here. Not sure of + the cause yet, but this workaround will mask the problem for now. + Other chips usually will pass at the very first test, so the + workaround shouldn't have any effect on them. */ + for (i = 0; (i < 10000 && INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); i++) + ; + + OUTPLL(HTOTAL_CNTL, 0); + + /* Clear reset & atomic update */ + OUTPLLP(PPLL_CNTL, 0, + ~(PPLL_RESET | PPLL_SLEEP | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN)); + + /* We may want some locking ... oh well */ + udelay(5000); + + /* Switch back VCLK source to PPLL */ + OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_PPLLCLK, ~VCLK_SRC_SEL_MASK); +} + +typedef struct { + u16 reg; + u32 val; +} reg_val; + +#if 0 /* unused ? -> scheduled for removal */ +/* these common regs are cleared before mode setting so they do not + * interfere with anything + */ +static reg_val common_regs[] = { + { OVR_CLR, 0 }, + { OVR_WID_LEFT_RIGHT, 0 }, + { OVR_WID_TOP_BOTTOM, 0 }, + { OV0_SCALE_CNTL, 0 }, + { SUBPIC_CNTL, 0 }, + { VIPH_CONTROL, 0 }, + { I2C_CNTL_1, 0 }, + { GEN_INT_CNTL, 0 }, + { CAP0_TRIG_CNTL, 0 }, + { CAP1_TRIG_CNTL, 0 }, +}; +#endif /* 0 */ + +void radeon_setmode(void) +{ + struct radeon_regs *mode = malloc(sizeof(struct radeon_regs)); + + mode->crtc_gen_cntl = 0x03000200; + mode->crtc_ext_cntl = 0x00008048; + mode->dac_cntl = 0xff002100; + mode->crtc_h_total_disp = 0x4f0063; + mode->crtc_h_sync_strt_wid = 0x8c02a2; + mode->crtc_v_total_disp = 0x01df020c; + mode->crtc_v_sync_strt_wid = 0x8201ea; + mode->crtc_pitch = 0x00500050; + + OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl); + OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl, + ~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS)); + OUTREGP(DAC_CNTL, mode->dac_cntl, DAC_RANGE_CNTL | DAC_BLANKING); + OUTREG(CRTC_H_TOTAL_DISP, mode->crtc_h_total_disp); + OUTREG(CRTC_H_SYNC_STRT_WID, mode->crtc_h_sync_strt_wid); + OUTREG(CRTC_V_TOTAL_DISP, mode->crtc_v_total_disp); + OUTREG(CRTC_V_SYNC_STRT_WID, mode->crtc_v_sync_strt_wid); + OUTREG(CRTC_OFFSET, 0); + OUTREG(CRTC_OFFSET_CNTL, 0); + OUTREG(CRTC_PITCH, mode->crtc_pitch); + + mode->clk_cntl_index = 0x300; + mode->ppll_ref_div = 0xc; + mode->ppll_div_3 = 0x00030059; + + radeon_write_pll_regs(rinfo, mode); +} + +static void set_pal(void) +{ + int idx, val = 0; + + for (idx = 0; idx < 256; idx++) { + OUTREG8(PALETTE_INDEX, idx); + OUTREG(PALETTE_DATA, val); + val += 0x00010101; + } +} + +void radeon_setmode_9200(int vesa_idx, int bpp) +{ + struct radeon_regs *mode = malloc(sizeof(struct radeon_regs)); + + mode->crtc_gen_cntl = CRTC_EN | CRTC_EXT_DISP_EN; + mode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN | CRTC_CRT_ON; + mode->dac_cntl = DAC_MASK_ALL | DAC_VGA_ADR_EN | DAC_8BIT_EN; + mode->crtc_offset_cntl = CRTC_OFFSET_CNTL__CRTC_TILE_EN; + + switch (bpp) { + case 24: + mode->crtc_gen_cntl |= 0x6 << 8; /* x888 */ +#if defined(__BIG_ENDIAN) + mode->surface_cntl = NONSURF_AP0_SWP_32BPP | NONSURF_AP1_SWP_32BPP; + mode->surf_info[0] = NONSURF_AP0_SWP_32BPP | NONSURF_AP1_SWP_32BPP; +#endif + break; + case 16: + mode->crtc_gen_cntl |= 0x4 << 8; /* 565 */ +#if defined(__BIG_ENDIAN) + mode->surface_cntl = NONSURF_AP0_SWP_16BPP | NONSURF_AP1_SWP_16BPP; + mode->surf_info[0] = NONSURF_AP0_SWP_16BPP | NONSURF_AP1_SWP_16BPP; +#endif + break; + default: + mode->crtc_gen_cntl |= 0x2 << 8; /* palette */ + mode->surface_cntl = 0x00000000; + break; + } + + switch (vesa_idx) { + case RES_MODE_1280x1024: + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1688,1280); + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(1066,1024); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(1025,3); +#if defined(CONFIG_RADEON_VREFRESH_75HZ) + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1288,18); + mode->ppll_div_3 = 0x00010078; +#else /* default @ 60 Hz */ + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1320,14); + mode->ppll_div_3 = 0x00010060; +#endif + /* + * for this mode pitch expands to the same value for 32, 16 and 8 bpp, + * so we set it here once only. + */ + mode->crtc_pitch = RADEON_CRT_PITCH(1280,32); + switch (bpp) { + case 24: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1280 * 4 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1280,1024,32); + break; + case 16: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1280 * 2 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1280,1024,16); + break; + default: /* 8 bpp */ + mode->surf_info[0] = R200_SURF_TILE_COLOR_MACRO | (1280 * 1 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1280,1024,8); + break; + } + break; + case RES_MODE_1024x768: +#if defined(CONFIG_RADEON_VREFRESH_75HZ) + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1312,1024); + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1032,12); + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(800,768); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(769,3); + mode->ppll_div_3 = 0x0002008c; +#else /* @ 60 Hz */ + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1344,1024); + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1040,17) | CRTC_H_SYNC_POL; + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(806,768); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(771,6) | CRTC_V_SYNC_POL; + mode->ppll_div_3 = 0x00020074; +#endif + /* also same pitch value for 32, 16 and 8 bpp */ + mode->crtc_pitch = RADEON_CRT_PITCH(1024,32); + switch (bpp) { + case 24: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1024 * 4 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,768,32); + break; + case 16: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1024 * 2 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,768,16); + break; + default: /* 8 bpp */ + mode->surf_info[0] = R200_SURF_TILE_COLOR_MACRO | (1024 * 1 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,768,8); + break; + } + break; + case RES_MODE_800x600: + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1056,800); +#if defined(CONFIG_RADEON_VREFRESH_75HZ) + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(808,10); + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(625,600); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(601,3); + mode->ppll_div_3 = 0x000300b0; +#else /* @ 60 Hz */ + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(832,16); + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(628,600); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(601,4); + mode->ppll_div_3 = 0x0003008e; +#endif + switch (bpp) { + case 24: + mode->crtc_pitch = RADEON_CRT_PITCH(832,32); + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (832 * 4 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(832,600,32); + break; + case 16: + mode->crtc_pitch = RADEON_CRT_PITCH(896,16); + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (896 * 2 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(896,600,16); + break; + default: /* 8 bpp */ + mode->crtc_pitch = RADEON_CRT_PITCH(1024,8); + mode->surf_info[0] = R200_SURF_TILE_COLOR_MACRO | (1024 * 1 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,600,8); + break; + } + break; + default: /* RES_MODE_640x480 */ +#if defined(CONFIG_RADEON_VREFRESH_75HZ) + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(840,640); + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(648,8) | CRTC_H_SYNC_POL; + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(500,480); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(481,3) | CRTC_V_SYNC_POL; + mode->ppll_div_3 = 0x00030070; +#else /* @ 60 Hz */ + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(800,640); + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(674,12) | CRTC_H_SYNC_POL; + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(525,480); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(491,2) | CRTC_V_SYNC_POL; + mode->ppll_div_3 = 0x00030059; +#endif + /* also same pitch value for 32, 16 and 8 bpp */ + mode->crtc_pitch = RADEON_CRT_PITCH(640,32); + switch (bpp) { + case 24: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (640 * 4 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(640,480,32); + break; + case 16: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (640 * 2 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(640,480,16); + break; + default: /* 8 bpp */ + mode->crtc_offset_cntl = 0x00000000; + break; + } + break; + } + + OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl | CRTC_DISP_REQ_EN_B); + OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl, + (CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS)); + OUTREGP(DAC_CNTL, mode->dac_cntl, DAC_RANGE_CNTL | DAC_BLANKING); + OUTREG(CRTC_H_TOTAL_DISP, mode->crtc_h_total_disp); + OUTREG(CRTC_H_SYNC_STRT_WID, mode->crtc_h_sync_strt_wid); + OUTREG(CRTC_V_TOTAL_DISP, mode->crtc_v_total_disp); + OUTREG(CRTC_V_SYNC_STRT_WID, mode->crtc_v_sync_strt_wid); + OUTREG(CRTC_OFFSET, 0); + OUTREG(CRTC_OFFSET_CNTL, mode->crtc_offset_cntl); + OUTREG(CRTC_PITCH, mode->crtc_pitch); + OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl); + + mode->clk_cntl_index = 0x300; + mode->ppll_ref_div = 0xc; + + radeon_write_pll_regs(rinfo, mode); + + OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl, + ~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS)); + OUTREG(SURFACE0_INFO, mode->surf_info[0]); + OUTREG(SURFACE0_LOWER_BOUND, 0); + OUTREG(SURFACE0_UPPER_BOUND, mode->surf_upper_bound[0]); + OUTREG(SURFACE_CNTL, mode->surface_cntl); + + if (bpp > 8) + set_pal(); + + free(mode); +} + +#include "../bios_emulator/include/biosemu.h" +extern int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo ** pVGAInfo, int cleanUp); + +int radeon_probe(struct radeonfb_info *rinfo) +{ + pci_dev_t pdev; + u16 did; + + pdev = pci_find_devices(ati_radeon_pci_ids, 0); + + if (pdev != -1) { + pci_read_config_word(pdev, PCI_DEVICE_ID, &did); + printf("ATI Radeon video card (%04x, %04x) found @(%d:%d:%d)\n", + PCI_VENDOR_ID_ATI, did, (pdev >> 16) & 0xff, + (pdev >> 11) & 0x1f, (pdev >> 8) & 0x7); + + strcpy(rinfo->name, "ATI Radeon"); + rinfo->pdev.vendor = PCI_VENDOR_ID_ATI; + rinfo->pdev.device = did; + rinfo->family = get_radeon_id_family(rinfo->pdev.device); + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, + &rinfo->fb_base_bus); + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, + &rinfo->mmio_base_bus); + rinfo->fb_base_bus &= 0xfffff000; + rinfo->mmio_base_bus &= ~0x04; + + rinfo->mmio_base = pci_bus_to_virt(pdev, rinfo->mmio_base_bus, + PCI_REGION_MEM, 0, MAP_NOCACHE); + DPRINT("rinfo->mmio_base = 0x%p bus=0x%x\n", + rinfo->mmio_base, rinfo->mmio_base_bus); + rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16; + DPRINT("rinfo->fb_local_base = 0x%x\n",rinfo->fb_local_base); + /* PostBIOS with x86 emulater */ + if (!BootVideoCardBIOS(pdev, NULL, 0)) + return -1; + + /* + * Check for errata + * (These will be added in the future for the chipfamily + * R300, RV200, RS200, RV100, RS100.) + */ + + /* Get VRAM size and type */ + radeon_identify_vram(rinfo); + + rinfo->mapped_vram = min_t(unsigned long, MAX_MAPPED_VRAM, + rinfo->video_ram); + rinfo->fb_base = pci_bus_to_virt(pdev, rinfo->fb_base_bus, + PCI_REGION_MEM, 0, MAP_NOCACHE); + DPRINT("Radeon: framebuffer base address 0x%08x, " + "bus address 0x%08x\n" + "MMIO base address 0x%08x, bus address 0x%08x, " + "framebuffer local base 0x%08x.\n ", + (u32)rinfo->fb_base, rinfo->fb_base_bus, + (u32)rinfo->mmio_base, rinfo->mmio_base_bus, + rinfo->fb_local_base); + return 0; + } + return -1; +} + +/* + * The Graphic Device + */ +GraphicDevice ctfb; + +#define CURSOR_SIZE 0x1000 /* in KByte for HW Cursor */ +#define PATTERN_ADR (pGD->dprBase + CURSOR_SIZE) /* pattern Memory after Cursor Memory */ +#define PATTERN_SIZE 8*8*4 /* 4 Bytes per Pixel 8 x 8 Pixel */ +#define ACCELMEMORY (CURSOR_SIZE + PATTERN_SIZE) /* reserved Memory for BITBlt and hw cursor */ + +void *video_hw_init(void) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + u32 *vm; + char *penv; + unsigned long t1, hsynch, vsynch; + int bits_per_pixel, i, tmp, vesa_idx = 0, videomode; + struct ctfb_res_modes *res_mode; + struct ctfb_res_modes var_mode; + + rinfo = malloc(sizeof(struct radeonfb_info)); + + printf("Video: "); + if(radeon_probe(rinfo)) { + printf("No radeon video card found!\n"); + return NULL; + } + + tmp = 0; + + videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE; + /* get video mode via environment */ + if ((penv = getenv ("videomode")) != NULL) { + /* deceide if it is a string */ + if (penv[0] <= '9') { + videomode = (int) simple_strtoul (penv, NULL, 16); + tmp = 1; + } + } else { + tmp = 1; + } + if (tmp) { + /* parameter are vesa modes */ + /* search params */ + for (i = 0; i < VESA_MODES_COUNT; i++) { + if (vesa_modes[i].vesanr == videomode) + break; + } + if (i == VESA_MODES_COUNT) { + printf ("no VESA Mode found, switching to mode 0x%x ", CONFIG_SYS_DEFAULT_VIDEO_MODE); + i = 0; + } + res_mode = (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i].resindex]; + bits_per_pixel = vesa_modes[i].bits_per_pixel; + vesa_idx = vesa_modes[i].resindex; + } else { + res_mode = (struct ctfb_res_modes *) &var_mode; + bits_per_pixel = video_get_params (res_mode, penv); + } + + /* calculate hsynch and vsynch freq (info only) */ + t1 = (res_mode->left_margin + res_mode->xres + + res_mode->right_margin + res_mode->hsync_len) / 8; + t1 *= 8; + t1 *= res_mode->pixclock; + t1 /= 1000; + hsynch = 1000000000L / t1; + t1 *= (res_mode->upper_margin + res_mode->yres + + res_mode->lower_margin + res_mode->vsync_len); + t1 /= 1000; + vsynch = 1000000000L / t1; + + /* fill in Graphic device struct */ + sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres, + res_mode->yres, bits_per_pixel, (hsynch / 1000), + (vsynch / 1000)); + printf ("%s\n", pGD->modeIdent); + pGD->winSizeX = res_mode->xres; + pGD->winSizeY = res_mode->yres; + pGD->plnSizeX = res_mode->xres; + pGD->plnSizeY = res_mode->yres; + + switch (bits_per_pixel) { + case 24: + pGD->gdfBytesPP = 4; + pGD->gdfIndex = GDF_32BIT_X888RGB; + if (res_mode->xres == 800) { + pGD->winSizeX = 832; + pGD->plnSizeX = 832; + } + break; + case 16: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_16BIT_565RGB; + if (res_mode->xres == 800) { + pGD->winSizeX = 896; + pGD->plnSizeX = 896; + } + break; + default: + if (res_mode->xres == 800) { + pGD->winSizeX = 1024; + pGD->plnSizeX = 1024; + } + pGD->gdfBytesPP = 1; + pGD->gdfIndex = GDF__8BIT_INDEX; + break; + } + + pGD->isaBase = CONFIG_SYS_ISA_IO_BASE_ADDRESS; + pGD->pciBase = (unsigned int)rinfo->fb_base; + pGD->frameAdrs = (unsigned int)rinfo->fb_base; + pGD->memSize = 64 * 1024 * 1024; + + /* Cursor Start Address */ + pGD->dprBase = (pGD->winSizeX * pGD->winSizeY * pGD->gdfBytesPP) + + (unsigned int)rinfo->fb_base; + if ((pGD->dprBase & 0x0fff) != 0) { + /* allign it */ + pGD->dprBase &= 0xfffff000; + pGD->dprBase += 0x00001000; + } + DPRINT ("Cursor Start %x Pattern Start %x\n", pGD->dprBase, + PATTERN_ADR); + pGD->vprBase = (unsigned int)rinfo->fb_base; /* Dummy */ + pGD->cprBase = (unsigned int)rinfo->fb_base; /* Dummy */ + /* set up Hardware */ + + /* Clear video memory (only visible screen area) */ + i = pGD->winSizeX * pGD->winSizeY * pGD->gdfBytesPP / 4; + vm = (unsigned int *) pGD->pciBase; + while (i--) + *vm++ = 0; + /*SetDrawingEngine (bits_per_pixel);*/ + + if (rinfo->family == CHIP_FAMILY_RV280) + radeon_setmode_9200(vesa_idx, bits_per_pixel); + else + radeon_setmode(); + + return ((void *) pGD); +} + +void video_set_lut (unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + OUTREG(PALETTE_INDEX, index); + OUTREG(PALETTE_DATA, (r << 16) | (g << 8) | b); +} diff --git a/qemu/roms/u-boot/drivers/video/ati_radeon_fb.h b/qemu/roms/u-boot/drivers/video/ati_radeon_fb.h new file mode 100644 index 000000000..9dd638bb9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/ati_radeon_fb.h @@ -0,0 +1,282 @@ +#ifndef __ATI_RADEON_FB_H +#define __ATI_RADEON_FB_H + +/*************************************************************** + * Most of the definitions here are adapted right from XFree86 * + ***************************************************************/ + +/* + * Chip families. Must fit in the low 16 bits of a long word + */ +enum radeon_family { + CHIP_FAMILY_UNKNOW, + CHIP_FAMILY_LEGACY, + CHIP_FAMILY_RADEON, + CHIP_FAMILY_RV100, + CHIP_FAMILY_RS100, /* U1 (IGP320M) or A3 (IGP320)*/ + CHIP_FAMILY_RV200, + CHIP_FAMILY_RS200, /* U2 (IGP330M/340M/350M) or A4 (IGP330/340/345/350), + RS250 (IGP 7000) */ + CHIP_FAMILY_R200, + CHIP_FAMILY_RV250, + CHIP_FAMILY_RS300, /* Radeon 9000 IGP */ + CHIP_FAMILY_RV280, + CHIP_FAMILY_R300, + CHIP_FAMILY_R350, + CHIP_FAMILY_RV350, + CHIP_FAMILY_RV380, /* RV370/RV380/M22/M24 */ + CHIP_FAMILY_R420, /* R420/R423/M18 */ + CHIP_FAMILY_LAST, +}; + +#define IS_RV100_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_RV100) || \ + ((rinfo)->family == CHIP_FAMILY_RV200) || \ + ((rinfo)->family == CHIP_FAMILY_RS100) || \ + ((rinfo)->family == CHIP_FAMILY_RS200) || \ + ((rinfo)->family == CHIP_FAMILY_RV250) || \ + ((rinfo)->family == CHIP_FAMILY_RV280) || \ + ((rinfo)->family == CHIP_FAMILY_RS300)) + +#define IS_R300_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_R300) || \ + ((rinfo)->family == CHIP_FAMILY_RV350) || \ + ((rinfo)->family == CHIP_FAMILY_R350) || \ + ((rinfo)->family == CHIP_FAMILY_RV380) || \ + ((rinfo)->family == CHIP_FAMILY_R420)) + +struct radeonfb_info { + char name[20]; + + struct pci_device_id pdev; + u16 family; + + u32 fb_base_bus; + u32 mmio_base_bus; + + void *mmio_base; + void *fb_base; + + u32 video_ram; + u32 mapped_vram; + int vram_width; + int vram_ddr; + + u32 fb_local_base; +}; + +#define INREG8(addr) readb((rinfo->mmio_base)+addr) +#define OUTREG8(addr,val) writeb(val, (rinfo->mmio_base)+addr) +#define INREG16(addr) readw((rinfo->mmio_base)+addr) +#define OUTREG16(addr,val) writew(val, (rinfo->mmio_base)+addr) +#define INREG(addr) readl((rinfo->mmio_base)+addr) +#define OUTREG(addr,val) writel(val, (rinfo->mmio_base)+addr) + +static inline void _OUTREGP(struct radeonfb_info *rinfo, u32 addr, + u32 val, u32 mask) +{ + unsigned int tmp; + + tmp = INREG(addr); + tmp &= (mask); + tmp |= (val); + OUTREG(addr, tmp); +} + +#define OUTREGP(addr,val,mask) _OUTREGP(rinfo, addr, val,mask) + +/* + * 2D Engine helper routines + */ +static inline void radeon_engine_flush (struct radeonfb_info *rinfo) +{ + int i; + + /* initiate flush */ + OUTREGP(RB2D_DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL, + ~RB2D_DC_FLUSH_ALL); + + for (i=0; i < 2000000; i++) { + if (!(INREG(RB2D_DSTCACHE_CTLSTAT) & RB2D_DC_BUSY)) + return; + udelay(1); + } + printf("radeonfb: Flush Timeout !\n"); +} + +static inline void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries) +{ + int i; + + for (i=0; i<2000000; i++) { + if ((INREG(RBBM_STATUS) & 0x7f) >= entries) + return; + udelay(1); + } + printf("radeonfb: FIFO Timeout !\n"); +} + +static inline void _radeon_engine_idle(struct radeonfb_info *rinfo) +{ + int i; + + /* ensure FIFO is empty before waiting for idle */ + _radeon_fifo_wait (rinfo, 64); + + for (i=0; i<2000000; i++) { + if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) { + radeon_engine_flush (rinfo); + return; + } + udelay(1); + } + printf("radeonfb: Idle Timeout !\n"); +} + +#define radeon_engine_idle() _radeon_engine_idle(rinfo) +#define radeon_fifo_wait(entries) _radeon_fifo_wait(rinfo,entries) +#define radeon_msleep(ms) _radeon_msleep(rinfo,ms) + +/* + * This structure contains the various registers manipulated by this + * driver for setting or restoring a mode. It's mostly copied from + * XFree's RADEONSaveRec structure. A few chip settings might still be + * tweaked without beeing reflected or saved in these registers though + */ +struct radeon_regs { + /* Common registers */ + u32 ovr_clr; + u32 ovr_wid_left_right; + u32 ovr_wid_top_bottom; + u32 ov0_scale_cntl; + u32 mpp_tb_config; + u32 mpp_gp_config; + u32 subpic_cntl; + u32 viph_control; + u32 i2c_cntl_1; + u32 gen_int_cntl; + u32 cap0_trig_cntl; + u32 cap1_trig_cntl; + u32 bus_cntl; + u32 surface_cntl; + u32 bios_5_scratch; + + /* Other registers to save for VT switches or driver load/unload */ + u32 dp_datatype; + u32 rbbm_soft_reset; + u32 clock_cntl_index; + u32 amcgpio_en_reg; + u32 amcgpio_mask; + + /* Surface/tiling registers */ + u32 surf_lower_bound[8]; + u32 surf_upper_bound[8]; + u32 surf_info[8]; + + /* CRTC registers */ + u32 crtc_gen_cntl; + u32 crtc_ext_cntl; + u32 dac_cntl; + u32 crtc_h_total_disp; + u32 crtc_h_sync_strt_wid; + u32 crtc_v_total_disp; + u32 crtc_v_sync_strt_wid; + u32 crtc_offset; + u32 crtc_offset_cntl; + u32 crtc_pitch; + u32 disp_merge_cntl; + u32 grph_buffer_cntl; + u32 crtc_more_cntl; + + /* CRTC2 registers */ + u32 crtc2_gen_cntl; + u32 dac2_cntl; + u32 disp_output_cntl; + u32 disp_hw_debug; + u32 disp2_merge_cntl; + u32 grph2_buffer_cntl; + u32 crtc2_h_total_disp; + u32 crtc2_h_sync_strt_wid; + u32 crtc2_v_total_disp; + u32 crtc2_v_sync_strt_wid; + u32 crtc2_offset; + u32 crtc2_offset_cntl; + u32 crtc2_pitch; + + /* Flat panel regs */ + u32 fp_crtc_h_total_disp; + u32 fp_crtc_v_total_disp; + u32 fp_gen_cntl; + u32 fp2_gen_cntl; + u32 fp_h_sync_strt_wid; + u32 fp2_h_sync_strt_wid; + u32 fp_horz_stretch; + u32 fp_panel_cntl; + u32 fp_v_sync_strt_wid; + u32 fp2_v_sync_strt_wid; + u32 fp_vert_stretch; + u32 lvds_gen_cntl; + u32 lvds_pll_cntl; + u32 tmds_crc; + u32 tmds_transmitter_cntl; + + /* Computed values for PLL */ + u32 dot_clock_freq; + int feedback_div; + int post_div; + + /* PLL registers */ + u32 ppll_div_3; + u32 ppll_ref_div; + u32 vclk_ecp_cntl; + u32 clk_cntl_index; + + /* Computed values for PLL2 */ + u32 dot_clock_freq_2; + int feedback_div_2; + int post_div_2; + + /* PLL2 registers */ + u32 p2pll_ref_div; + u32 p2pll_div_0; + u32 htotal_cntl2; + + /* Palette */ + int palette_valid; +}; + +static inline u32 __INPLL(struct radeonfb_info *rinfo, u32 addr) +{ + u32 data; + + OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000003f); + /* radeon_pll_errata_after_index(rinfo); */ + data = INREG(CLOCK_CNTL_DATA); + /* radeon_pll_errata_after_data(rinfo); */ + return data; +} + +static inline void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index, + u32 val) +{ + + OUTREG8(CLOCK_CNTL_INDEX, (index & 0x0000003f) | 0x00000080); + /* radeon_pll_errata_after_index(rinfo); */ + OUTREG(CLOCK_CNTL_DATA, val); + /* radeon_pll_errata_after_data(rinfo); */ +} + +static inline void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index, + u32 val, u32 mask) +{ + unsigned int tmp; + + tmp = __INPLL(rinfo, index); + tmp &= (mask); + tmp |= (val); + __OUTPLL(rinfo, index, tmp); +} + +#define INPLL(addr) __INPLL(rinfo, addr) +#define OUTPLL(index, val) __OUTPLL(rinfo, index, val) +#define OUTPLLP(index, val, mask) __OUTPLLP(rinfo, index, val, mask) + +#endif diff --git a/qemu/roms/u-boot/drivers/video/atmel_hlcdfb.c b/qemu/roms/u-boot/drivers/video/atmel_hlcdfb.c new file mode 100644 index 000000000..bb4d7d8c1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/atmel_hlcdfb.c @@ -0,0 +1,197 @@ +/* + * Driver for AT91/AT32 MULTI LAYER LCD Controller + * + * Copyright (C) 2012 Atmel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +/* configurable parameters */ +#define ATMEL_LCDC_CVAL_DEFAULT 0xc8 +#define ATMEL_LCDC_DMA_BURST_LEN 8 +#ifndef ATMEL_LCDC_GUARD_TIME +#define ATMEL_LCDC_GUARD_TIME 1 +#endif + +#define ATMEL_LCDC_FIFO_SIZE 512 + +#define lcdc_readl(reg) __raw_readl((reg)) +#define lcdc_writel(reg, val) __raw_writel((val), (reg)) + +/* + * the CLUT register map as following + * RCLUT(24 ~ 16), GCLUT(15 ~ 8), BCLUT(7 ~ 0) + */ +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ + lcdc_writel(((red << LCDC_BASECLUT_RCLUT_Pos) & LCDC_BASECLUT_RCLUT_Msk) + | ((green << LCDC_BASECLUT_GCLUT_Pos) & LCDC_BASECLUT_GCLUT_Msk) + | ((blue << LCDC_BASECLUT_BCLUT_Pos) & LCDC_BASECLUT_BCLUT_Msk), + panel_info.mmio + ATMEL_LCDC_LUT(regno)); +} + +void lcd_ctrl_init(void *lcdbase) +{ + unsigned long value; + struct lcd_dma_desc *desc; + struct atmel_hlcd_regs *regs; + + if (!has_lcdc()) + return; /* No lcdc */ + + regs = (struct atmel_hlcd_regs *)panel_info.mmio; + + /* Disable DISP signal */ + lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_DISPDIS); + while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_DISPSTS)) + udelay(1); + /* Disable synchronization */ + lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_SYNCDIS); + while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_LCDSTS)) + udelay(1); + /* Disable pixel clock */ + lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_CLKDIS); + while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_CLKSTS)) + udelay(1); + /* Disable PWM */ + lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_PWMDIS); + while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_PWMSTS)) + udelay(1); + + /* Set pixel clock */ + value = get_lcdc_clk_rate(0) / panel_info.vl_clk; + if (get_lcdc_clk_rate(0) % panel_info.vl_clk) + value++; + + if (value < 1) { + /* Using system clock as pixel clock */ + lcdc_writel(®s->lcdc_lcdcfg0, + LCDC_LCDCFG0_CLKDIV(0) + | LCDC_LCDCFG0_CGDISHCR + | LCDC_LCDCFG0_CGDISHEO + | LCDC_LCDCFG0_CGDISOVR1 + | LCDC_LCDCFG0_CGDISBASE + | panel_info.vl_clk_pol + | LCDC_LCDCFG0_CLKSEL); + + } else { + lcdc_writel(®s->lcdc_lcdcfg0, + LCDC_LCDCFG0_CLKDIV(value - 2) + | LCDC_LCDCFG0_CGDISHCR + | LCDC_LCDCFG0_CGDISHEO + | LCDC_LCDCFG0_CGDISOVR1 + | LCDC_LCDCFG0_CGDISBASE + | panel_info.vl_clk_pol); + } + + /* Initialize control register 5 */ + value = 0; + + value |= panel_info.vl_sync; + +#ifndef LCD_OUTPUT_BPP + /* Output is 24bpp */ + value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; +#else + switch (LCD_OUTPUT_BPP) { + case 12: + value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP; + break; + case 16: + value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP; + break; + case 18: + value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP; + break; + case 24: + value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; + break; + default: + BUG(); + break; + } +#endif + + value |= LCDC_LCDCFG5_GUARDTIME(ATMEL_LCDC_GUARD_TIME); + value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS); + lcdc_writel(®s->lcdc_lcdcfg5, value); + + /* Vertical & Horizontal Timing */ + value = LCDC_LCDCFG1_VSPW(panel_info.vl_vsync_len - 1); + value |= LCDC_LCDCFG1_HSPW(panel_info.vl_hsync_len - 1); + lcdc_writel(®s->lcdc_lcdcfg1, value); + + value = LCDC_LCDCFG2_VBPW(panel_info.vl_upper_margin); + value |= LCDC_LCDCFG2_VFPW(panel_info.vl_lower_margin - 1); + lcdc_writel(®s->lcdc_lcdcfg2, value); + + value = LCDC_LCDCFG3_HBPW(panel_info.vl_left_margin - 1); + value |= LCDC_LCDCFG3_HFPW(panel_info.vl_right_margin - 1); + lcdc_writel(®s->lcdc_lcdcfg3, value); + + /* Display size */ + value = LCDC_LCDCFG4_RPF(panel_info.vl_row - 1); + value |= LCDC_LCDCFG4_PPL(panel_info.vl_col - 1); + lcdc_writel(®s->lcdc_lcdcfg4, value); + + lcdc_writel(®s->lcdc_basecfg0, + LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO); + + switch (NBITS(panel_info.vl_bpix)) { + case 16: + lcdc_writel(®s->lcdc_basecfg1, + LCDC_BASECFG1_RGBMODE_16BPP_RGB_565); + break; + default: + BUG(); + break; + } + + lcdc_writel(®s->lcdc_basecfg2, LCDC_BASECFG2_XSTRIDE(0)); + lcdc_writel(®s->lcdc_basecfg3, 0); + lcdc_writel(®s->lcdc_basecfg4, LCDC_BASECFG4_DMA); + + /* Disable all interrupts */ + lcdc_writel(®s->lcdc_lcdidr, ~0UL); + lcdc_writel(®s->lcdc_baseidr, ~0UL); + + /* Setup the DMA descriptor, this descriptor will loop to itself */ + desc = (struct lcd_dma_desc *)(lcdbase - 16); + + desc->address = (u32)lcdbase; + /* Disable DMA transfer interrupt & descriptor loaded interrupt. */ + desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN + | LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH; + desc->next = (u32)desc; + + lcdc_writel(®s->lcdc_baseaddr, desc->address); + lcdc_writel(®s->lcdc_basectrl, desc->control); + lcdc_writel(®s->lcdc_basenext, desc->next); + lcdc_writel(®s->lcdc_basecher, LCDC_BASECHER_CHEN | + LCDC_BASECHER_UPDATEEN); + + /* Enable LCD */ + value = lcdc_readl(®s->lcdc_lcden); + lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_CLKEN); + while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_CLKSTS)) + udelay(1); + value = lcdc_readl(®s->lcdc_lcden); + lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_SYNCEN); + while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_LCDSTS)) + udelay(1); + value = lcdc_readl(®s->lcdc_lcden); + lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_DISPEN); + while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_DISPSTS)) + udelay(1); + value = lcdc_readl(®s->lcdc_lcden); + lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_PWMEN); + while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_PWMSTS)) + udelay(1); +} diff --git a/qemu/roms/u-boot/drivers/video/atmel_lcdfb.c b/qemu/roms/u-boot/drivers/video/atmel_lcdfb.c new file mode 100644 index 000000000..3cf008ce6 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/atmel_lcdfb.c @@ -0,0 +1,138 @@ +/* + * Driver for AT91/AT32 LCD Controller + * + * Copyright (C) 2007 Atmel Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +/* configurable parameters */ +#define ATMEL_LCDC_CVAL_DEFAULT 0xc8 +#define ATMEL_LCDC_DMA_BURST_LEN 8 +#ifndef ATMEL_LCDC_GUARD_TIME +#define ATMEL_LCDC_GUARD_TIME 1 +#endif + +#if defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91CAP9) +#define ATMEL_LCDC_FIFO_SIZE 2048 +#else +#define ATMEL_LCDC_FIFO_SIZE 512 +#endif + +#define lcdc_readl(mmio, reg) __raw_readl((mmio)+(reg)) +#define lcdc_writel(mmio, reg, val) __raw_writel((val), (mmio)+(reg)) + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +#if defined(CONFIG_ATMEL_LCD_BGR555) + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno), + (red >> 3) | ((green & 0xf8) << 2) | ((blue & 0xf8) << 7)); +#else + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno), + (blue >> 3) | ((green & 0xfc) << 3) | ((red & 0xf8) << 8)); +#endif +} + +void lcd_ctrl_init(void *lcdbase) +{ + unsigned long value; + + /* Turn off the LCD controller and the DMA controller */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_PWRCON, + ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET); + + /* Wait for the LCDC core to become idle */ + while (lcdc_readl(panel_info.mmio, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY) + udelay(10); + + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMACON, 0); + + /* Reset LCDC DMA */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST); + + /* ...set frame size and burst length = 8 words (?) */ + value = (panel_info.vl_col * panel_info.vl_row * + NBITS(panel_info.vl_bpix)) / 32; + value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMAFRMCFG, value); + + /* Set pixel clock */ + value = get_lcdc_clk_rate(0) / panel_info.vl_clk; + if (get_lcdc_clk_rate(0) % panel_info.vl_clk) + value++; + value = (value / 2) - 1; + + if (!value) { + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); + } else + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDCON1, + value << ATMEL_LCDC_CLKVAL_OFFSET); + + /* Initialize control register 2 */ +#ifdef CONFIG_AVR32 + value = ATMEL_LCDC_MEMOR_BIG | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE; +#else + value = ATMEL_LCDC_MEMOR_LITTLE | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE; +#endif + if (panel_info.vl_tft) + value |= ATMEL_LCDC_DISTYPE_TFT; + + value |= panel_info.vl_sync; + value |= (panel_info.vl_bpix << 5); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDCON2, value); + + /* Vertical timing */ + value = (panel_info.vl_vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET; + value |= panel_info.vl_upper_margin << ATMEL_LCDC_VBP_OFFSET; + value |= panel_info.vl_lower_margin; + lcdc_writel(panel_info.mmio, ATMEL_LCDC_TIM1, value); + + /* Horizontal timing */ + value = (panel_info.vl_right_margin - 1) << ATMEL_LCDC_HFP_OFFSET; + value |= (panel_info.vl_hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET; + value |= (panel_info.vl_left_margin - 1); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_TIM2, value); + + /* Display size */ + value = (panel_info.vl_col - 1) << ATMEL_LCDC_HOZVAL_OFFSET; + value |= panel_info.vl_row - 1; + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDFRMCFG, value); + + /* FIFO Threshold: Use formula from data sheet */ + value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_FIFO, value); + + /* Toggle LCD_MODE every frame */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_MVAL, 0); + + /* Disable all interrupts */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_IDR, ~0UL); + + /* Set contrast */ + value = ATMEL_LCDC_PS_DIV8 | + ATMEL_LCDC_ENA_PWMENABLE; + if (!panel_info.vl_cont_pol_low) + value |= ATMEL_LCDC_POL_POSITIVE; + lcdc_writel(panel_info.mmio, ATMEL_LCDC_CONTRAST_CTR, value); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT); + + /* Set framebuffer DMA base address and pixel offset */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMABADDR1, (u_long)lcdbase); + + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMAEN); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_PWRCON, + (ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR); +} + +ulong calc_fbsize(void) +{ + return ((panel_info.vl_col * panel_info.vl_row * + NBITS(panel_info.vl_bpix)) / 8) + PAGE_SIZE; +} diff --git a/qemu/roms/u-boot/drivers/video/bcm2835.c b/qemu/roms/u-boot/drivers/video/bcm2835.c new file mode 100644 index 000000000..1f18231ac --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/bcm2835.c @@ -0,0 +1,116 @@ +/* + * (C) Copyright 2012 Stephen Warren + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Global variables that lcd.c expects to exist */ +vidinfo_t panel_info; + +static u32 bcm2835_pitch; + +struct msg_query { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_physical_w_h physical_w_h; + u32 end_tag; +}; + +struct msg_setup { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_physical_w_h physical_w_h; + struct bcm2835_mbox_tag_virtual_w_h virtual_w_h; + struct bcm2835_mbox_tag_depth depth; + struct bcm2835_mbox_tag_pixel_order pixel_order; + struct bcm2835_mbox_tag_alpha_mode alpha_mode; + struct bcm2835_mbox_tag_virtual_offset virtual_offset; + struct bcm2835_mbox_tag_overscan overscan; + struct bcm2835_mbox_tag_allocate_buffer allocate_buffer; + struct bcm2835_mbox_tag_pitch pitch; + u32 end_tag; +}; + +void lcd_ctrl_init(void *lcdbase) +{ + ALLOC_ALIGN_BUFFER(struct msg_query, msg_query, 1, 16); + ALLOC_ALIGN_BUFFER(struct msg_setup, msg_setup, 1, 16); + int ret; + u32 w, h; + + debug("bcm2835: Query resolution...\n"); + + BCM2835_MBOX_INIT_HDR(msg_query); + BCM2835_MBOX_INIT_TAG_NO_REQ(&msg_query->physical_w_h, + GET_PHYSICAL_W_H); + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_query->hdr); + if (ret) { + printf("bcm2835: Could not query display resolution\n"); + /* FIXME: How to disable the LCD to prevent errors? hang()? */ + return; + } + + w = msg_query->physical_w_h.body.resp.width; + h = msg_query->physical_w_h.body.resp.height; + + debug("bcm2835: Setting up display for %d x %d\n", w, h); + + BCM2835_MBOX_INIT_HDR(msg_setup); + BCM2835_MBOX_INIT_TAG(&msg_setup->physical_w_h, SET_PHYSICAL_W_H); + msg_setup->physical_w_h.body.req.width = w; + msg_setup->physical_w_h.body.req.height = h; + BCM2835_MBOX_INIT_TAG(&msg_setup->virtual_w_h, SET_VIRTUAL_W_H); + msg_setup->virtual_w_h.body.req.width = w; + msg_setup->virtual_w_h.body.req.height = h; + BCM2835_MBOX_INIT_TAG(&msg_setup->depth, SET_DEPTH); + msg_setup->depth.body.req.bpp = 16; + BCM2835_MBOX_INIT_TAG(&msg_setup->pixel_order, SET_PIXEL_ORDER); + msg_setup->pixel_order.body.req.order = BCM2835_MBOX_PIXEL_ORDER_BGR; + BCM2835_MBOX_INIT_TAG(&msg_setup->alpha_mode, SET_ALPHA_MODE); + msg_setup->alpha_mode.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED; + BCM2835_MBOX_INIT_TAG(&msg_setup->virtual_offset, SET_VIRTUAL_OFFSET); + msg_setup->virtual_offset.body.req.x = 0; + msg_setup->virtual_offset.body.req.y = 0; + BCM2835_MBOX_INIT_TAG(&msg_setup->overscan, SET_OVERSCAN); + msg_setup->overscan.body.req.top = 0; + msg_setup->overscan.body.req.bottom = 0; + msg_setup->overscan.body.req.left = 0; + msg_setup->overscan.body.req.right = 0; + BCM2835_MBOX_INIT_TAG(&msg_setup->allocate_buffer, ALLOCATE_BUFFER); + msg_setup->allocate_buffer.body.req.alignment = 0x100; + BCM2835_MBOX_INIT_TAG_NO_REQ(&msg_setup->pitch, GET_PITCH); + + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_setup->hdr); + if (ret) { + printf("bcm2835: Could not configure display\n"); + /* FIXME: How to disable the LCD to prevent errors? hang()? */ + return; + } + + w = msg_setup->physical_w_h.body.resp.width; + h = msg_setup->physical_w_h.body.resp.height; + bcm2835_pitch = msg_setup->pitch.body.resp.pitch; + + debug("bcm2835: Final resolution is %d x %d\n", w, h); + + panel_info.vl_col = w; + panel_info.vl_row = h; + panel_info.vl_bpix = LCD_COLOR16; + + gd->fb_base = msg_setup->allocate_buffer.body.resp.fb_address; +} + +void lcd_enable(void) +{ +} + +int lcd_get_size(int *line_length) +{ + *line_length = bcm2835_pitch; + return *line_length * panel_info.vl_row; +} diff --git a/qemu/roms/u-boot/drivers/video/bus_vcxk.c b/qemu/roms/u-boot/drivers/video/bus_vcxk.c new file mode 100644 index 000000000..60a5cc5b7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/bus_vcxk.c @@ -0,0 +1,426 @@ +/* + * (C) Copyright 2005-2009 + * Jens Scharsig @ BuS Elektronik GmbH & Co. KG, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +vu_char *vcxk_bws = ((vu_char *) (CONFIG_SYS_VCXK_BASE)); +vu_short *vcxk_bws_word = ((vu_short *)(CONFIG_SYS_VCXK_BASE)); +vu_long *vcxk_bws_long = ((vu_long *) (CONFIG_SYS_VCXK_BASE)); + +#ifdef CONFIG_AT91RM9200 + #include + #include + + #ifndef VCBITMASK + #define VCBITMASK(bitno) (0x0001 << (bitno % 16)) + #endif +at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE; +#define VCXK_INIT_PIN(PORT, PIN, DDR, I0O1) \ + do { \ + writel(PIN, &pio->PORT.per); \ + writel(PIN, &pio->PORT.DDR); \ + writel(PIN, &pio->PORT.mddr); \ + if (!I0O1) \ + writel(PIN, &pio->PORT.puer); \ + } while (0); + +#define VCXK_SET_PIN(PORT, PIN) writel(PIN, &pio->PORT.sodr); +#define VCXK_CLR_PIN(PORT, PIN) writel(PIN, &pio->PORT.codr); + +#define VCXK_ACKNOWLEDGE \ + (!(readl(&pio->CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT.pdsr) & \ + CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN)) +#elif defined(CONFIG_MCF52x2) + #include + #ifndef VCBITMASK + #define VCBITMASK(bitno) (0x8000 >> (bitno % 16)) + #endif + + #define VCXK_INIT_PIN(PORT, PIN, DDR, I0O1) \ + if (I0O1) DDR |= PIN; else DDR &= ~PIN; + + #define VCXK_SET_PIN(PORT, PIN) PORT |= PIN; + #define VCXK_CLR_PIN(PORT, PIN) PORT &= ~PIN; + + #define VCXK_ACKNOWLEDGE \ + (!(CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT & \ + CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN)) + +#else + #error no vcxk support for selected ARCH +#endif + +#define VCXK_DISABLE\ + VCXK_SET_PIN(CONFIG_SYS_VCXK_ENABLE_PORT, CONFIG_SYS_VCXK_ENABLE_PIN) +#define VCXK_ENABLE\ + VCXK_CLR_PIN(CONFIG_SYS_VCXK_ENABLE_PORT, CONFIG_SYS_VCXK_ENABLE_PIN) + +#ifndef CONFIG_SYS_VCXK_DOUBLEBUFFERED + #define VCXK_BWS(x, data) vcxk_bws[x] = data; + #define VCXK_BWS_WORD_SET(x, mask) vcxk_bws_word[x] |= mask; + #define VCXK_BWS_WORD_CLEAR(x, mask) vcxk_bws_word[x] &= ~mask; + #define VCXK_BWS_LONG(x, data) vcxk_bws_long[x] = data; +#else + u_char double_bws[16384]; + u_short *double_bws_word; + u_long *double_bws_long; + #define VCXK_BWS(x,data) \ + double_bws[x] = data; vcxk_bws[x] = data; + #define VCXK_BWS_WORD_SET(x,mask) \ + double_bws_word[x] |= mask; \ + vcxk_bws_word[x] = double_bws_word[x]; + #define VCXK_BWS_WORD_CLEAR(x,mask) \ + double_bws_word[x] &= ~mask; \ + vcxk_bws_word[x] = double_bws_word[x]; + #define VCXK_BWS_LONG(x,data) \ + double_bws_long[x] = data; vcxk_bws_long[x] = data; +#endif + +#define VC4K16_Bright1 vcxk_bws_word[0x20004 / 2] +#define VC4K16_Bright2 vcxk_bws_word[0x20006 / 2] +#define VC2K_Bright vcxk_bws[0x8000] +#define VC8K_BrightH vcxk_bws[0xC000] +#define VC8K_BrightL vcxk_bws[0xC001] + +vu_char VC4K16; + +u_long display_width; +u_long display_height; +u_long display_bwidth; + +ulong search_vcxk_driver(void); +void vcxk_cls(void); +void vcxk_setbrightness(unsigned int side, short brightness); +int vcxk_request(void); +int vcxk_acknowledge_wait(void); +void vcxk_clear(void); + +/* + ****f* bus_vcxk/vcxk_init + * FUNCTION + * initialalize Video Controller + * PARAMETERS + * width visible display width in pixel + * height visible display height in pixel + *** + */ + +int vcxk_init(unsigned long width, unsigned long height) +{ +#ifdef CONFIG_SYS_VCXK_RESET_PORT + VCXK_INIT_PIN(CONFIG_SYS_VCXK_RESET_PORT, + CONFIG_SYS_VCXK_RESET_PIN, CONFIG_SYS_VCXK_RESET_DDR, 1) + VCXK_SET_PIN(CONFIG_SYS_VCXK_RESET_PORT, CONFIG_SYS_VCXK_RESET_PIN); +#endif + +#ifdef CONFIG_SYS_VCXK_DOUBLEBUFFERED + double_bws_word = (u_short *)double_bws; + double_bws_long = (u_long *)double_bws; + debug("%px %px %px\n", double_bws, double_bws_word, double_bws_long); +#endif + display_width = width; + display_height = height; +#if (CONFIG_SYS_VCXK_DEFAULT_LINEALIGN == 4) + display_bwidth = ((width + 31) / 8) & ~0x3; +#elif (CONFIG_SYS_VCXK_DEFAULT_LINEALIGN == 2) + display_bwidth = ((width + 15) / 8) & ~0x1; +#else + #error CONFIG_SYS_VCXK_DEFAULT_LINEALIGN is invalid +#endif + debug("linesize ((%ld + 15) / 8 & ~0x1) = %ld\n", + display_width, display_bwidth); + +#ifdef CONFIG_SYS_VCXK_AUTODETECT + VC4K16 = 0; + vcxk_bws_long[1] = 0x0; + vcxk_bws_long[1] = 0x55AAAA55; + vcxk_bws_long[5] = 0x0; + if (vcxk_bws_long[1] == 0x55AAAA55) + VC4K16 = 1; +#else + VC4K16 = 1; + debug("No autodetect: use vc4k\n"); +#endif + + VCXK_INIT_PIN(CONFIG_SYS_VCXK_INVERT_PORT, + CONFIG_SYS_VCXK_INVERT_PIN, CONFIG_SYS_VCXK_INVERT_DDR, 1) + VCXK_SET_PIN(CONFIG_SYS_VCXK_INVERT_PORT, CONFIG_SYS_VCXK_INVERT_PIN) + + VCXK_SET_PIN(CONFIG_SYS_VCXK_REQUEST_PORT, CONFIG_SYS_VCXK_REQUEST_PIN); + VCXK_INIT_PIN(CONFIG_SYS_VCXK_REQUEST_PORT, + CONFIG_SYS_VCXK_REQUEST_PIN, CONFIG_SYS_VCXK_REQUEST_DDR, 1) + + VCXK_INIT_PIN(CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT, + CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN, + CONFIG_SYS_VCXK_ACKNOWLEDGE_DDR, 0) + + VCXK_DISABLE; + VCXK_INIT_PIN(CONFIG_SYS_VCXK_ENABLE_PORT, + CONFIG_SYS_VCXK_ENABLE_PIN, CONFIG_SYS_VCXK_ENABLE_DDR, 1) + + vcxk_cls(); + vcxk_cls(); /* clear second/hidden page */ + + vcxk_setbrightness(3, 1000); + VCXK_ENABLE; + return 1; +} + +/* + ****f* bus_vcxk/vcxk_setpixel + * FUNCTION + * set the pixel[x,y] with the given color + * PARAMETER + * x pixel colum + * y pixel row + * color <0x40 off/black + * >0x40 on + *** + */ + +void vcxk_setpixel(int x, int y, unsigned long color) +{ + vu_short dataptr; + + if ((x < display_width) && (y < display_height)) { + dataptr = ((x / 16)) + (y * (display_bwidth >> 1)); + + color = ((color >> 16) & 0xFF) | + ((color >> 8) & 0xFF) | (color & 0xFF); + + if (color > 0x40) { + VCXK_BWS_WORD_SET(dataptr, VCBITMASK(x)); + } else { + VCXK_BWS_WORD_CLEAR(dataptr, VCBITMASK(x)); + } + } +} + +/* + ****f* bus_vcxk/vcxk_loadimage + * FUNCTION + * copies a binary image to display memory + *** + */ + +void vcxk_loadimage(ulong source) +{ + int cnt; + vcxk_acknowledge_wait(); + if (VC4K16) { + for (cnt = 0; cnt < (16384 / 4); cnt++) { + VCXK_BWS_LONG(cnt, (*(ulong *) source)); + source = source + 4; + } + } else { + for (cnt = 0; cnt < 16384; cnt++) { + VCXK_BWS_LONG(cnt*2, (*(vu_char *) source)); + source++; + } + } + vcxk_request(); +} + +/* + ****f* bus_vcxk/vcxk_cls + * FUNCTION + * clear the display + *** + */ + +void vcxk_cls(void) +{ + vcxk_acknowledge_wait(); + vcxk_clear(); + vcxk_request(); +} + +/* + ****f* bus_vcxk/vcxk_clear(void) + * FUNCTION + * clear the display memory + *** + */ + +void vcxk_clear(void) +{ + int cnt; + + for (cnt = 0; cnt < (16384 / 4); cnt++) { + VCXK_BWS_LONG(cnt, 0) + } +} + +/* + ****f* bus_vcxk/vcxk_setbrightness + * FUNCTION + * set the display brightness + * PARAMETER + * side 1 set front side brightness + * 2 set back side brightness + * 3 set brightness for both sides + * brightness 0..1000 + *** + */ + +void vcxk_setbrightness(unsigned int side, short brightness) +{ + if (VC4K16) { + if ((side == 0) || (side & 0x1)) + VC4K16_Bright1 = brightness + 23; + if ((side == 0) || (side & 0x2)) + VC4K16_Bright2 = brightness + 23; + } else { + VC2K_Bright = (brightness >> 4) + 2; + VC8K_BrightH = (brightness + 23) >> 8; + VC8K_BrightL = (brightness + 23) & 0xFF; + } +} + +/* + ****f* bus_vcxk/vcxk_request + * FUNCTION + * requests viewing of display memory + *** + */ + +int vcxk_request(void) +{ + VCXK_CLR_PIN(CONFIG_SYS_VCXK_REQUEST_PORT, + CONFIG_SYS_VCXK_REQUEST_PIN) + VCXK_SET_PIN(CONFIG_SYS_VCXK_REQUEST_PORT, + CONFIG_SYS_VCXK_REQUEST_PIN); + return 1; +} + +/* + ****f* bus_vcxk/vcxk_acknowledge_wait + * FUNCTION + * wait for acknowledge viewing requests + *** + */ + +int vcxk_acknowledge_wait(void) +{ + while (VCXK_ACKNOWLEDGE) + ; + return 1; +} + +/* + ****f* bus_vcxk/vcxk_draw_mono + * FUNCTION + * copies a monochrom bitmap (BMP-Format) from given memory + * PARAMETER + * dataptr pointer to bitmap + * x output bitmap @ columne + * y output bitmap @ row + *** + */ + +void vcxk_draw_mono(unsigned char *dataptr, unsigned long linewidth, + unsigned long cp_width, unsigned long cp_height) +{ + unsigned char *lineptr; + unsigned long xcnt, ycnt; + + for (ycnt = cp_height; ycnt > 0; ycnt--) { + lineptr = dataptr; + for (xcnt = 0; xcnt < cp_width; xcnt++) { + if ((*lineptr << (xcnt % 8)) & 0x80) + vcxk_setpixel(xcnt, ycnt - 1, 0xFFFFFF); + else + vcxk_setpixel(xcnt, ycnt-1, 0); + + if ((xcnt % 8) == 7) + lineptr++; + } /* endfor xcnt */ + dataptr = dataptr + linewidth; + } /* endfor ycnt */ +} + +/* + ****f* bus_vcxk/vcxk_display_bitmap + * FUNCTION + * copies a bitmap (BMP-Format) to the given position + * PARAMETER + * addr pointer to bitmap + * x output bitmap @ columne + * y output bitmap @ row + *** + */ + +int vcxk_display_bitmap(ulong addr, int x, int y) +{ + bmp_image_t *bmp; + unsigned long width; + unsigned long height; + unsigned long bpp; + + unsigned long lw; + + unsigned long c_width; + unsigned long c_height; + unsigned char *dataptr; + + bmp = (bmp_image_t *) addr; + if ((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M')) { + width = le32_to_cpu(bmp->header.width); + height = le32_to_cpu(bmp->header.height); + bpp = le16_to_cpu(bmp->header.bit_count); + + dataptr = (unsigned char *) bmp + + le32_to_cpu(bmp->header.data_offset); + + if (display_width < (width + x)) + c_width = display_width - x; + else + c_width = width; + if (display_height < (height + y)) + c_height = display_height - y; + else + c_height = height; + + lw = (((width + 7) / 8) + 3) & ~0x3; + + if (c_height < height) + dataptr = dataptr + lw * (height - c_height); + switch (bpp) { + case 1: + vcxk_draw_mono(dataptr, lw, c_width, c_height); + break; + default: + printf("Error: %ld bit per pixel " + "not supported by VCxK\n", bpp); + return 0; + } + } else { + printf("Error: no valid bmp at %lx\n", (ulong) bmp); + return 0; + } + return 1; +} + +/* + ****f* bus_vcxk/video_display_bitmap + *** + */ + +int video_display_bitmap(ulong addr, int x, int y) +{ + vcxk_acknowledge_wait(); + if (vcxk_display_bitmap(addr, x, y)) { + vcxk_request(); + return 0; + } + return 1; +} + +/* EOF */ diff --git a/qemu/roms/u-boot/drivers/video/cfb_console.c b/qemu/roms/u-boot/drivers/video/cfb_console.c new file mode 100644 index 000000000..b52e9edd2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/cfb_console.c @@ -0,0 +1,2323 @@ +/* + * (C) Copyright 2002 ELTEC Elektronik AG + * Frank Gottschling + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * cfb_console.c + * + * Color Framebuffer Console driver for 8/15/16/24/32 bits per pixel. + * + * At the moment only the 8x16 font is tested and the font fore- and + * background color is limited to black/white/gray colors. The Linux + * logo can be placed in the upper left corner and additional board + * information strings (that normally goes to serial port) can be drawn. + * + * The console driver can use the standard PC keyboard interface (i8042) + * for character input. Character output goes to a memory mapped video + * framebuffer with little or big-endian organisation. + * With environment setting 'console=serial' the console i/o can be + * forced to serial port. + * + * The driver uses graphic specific defines/parameters/functions: + * + * (for SMI LynxE graphic chip) + * + * CONFIG_VIDEO_SMI_LYNXEM - use graphic driver for SMI 710,712,810 + * VIDEO_FB_LITTLE_ENDIAN - framebuffer organisation default: big endian + * VIDEO_HW_RECTFILL - graphic driver supports hardware rectangle fill + * VIDEO_HW_BITBLT - graphic driver supports hardware bit blt + * + * Console Parameters are set by graphic drivers global struct: + * + * VIDEO_VISIBLE_COLS - x resolution + * VIDEO_VISIBLE_ROWS - y resolution + * VIDEO_PIXEL_SIZE - storage size in byte per pixel + * VIDEO_DATA_FORMAT - graphical data format GDF + * VIDEO_FB_ADRS - start of video memory + * + * CONFIG_I8042_KBD - AT Keyboard driver for i8042 + * VIDEO_KBD_INIT_FCT - init function for keyboard + * VIDEO_TSTC_FCT - keyboard_tstc function + * VIDEO_GETC_FCT - keyboard_getc function + * + * CONFIG_CONSOLE_CURSOR - on/off drawing cursor is done with + * delay loop in VIDEO_TSTC_FCT (i8042) + * + * CONFIG_SYS_CONSOLE_BLINK_COUNT - value for delay loop - blink rate + * CONFIG_CONSOLE_TIME - display time/date in upper right + * corner, needs CONFIG_CMD_DATE and + * CONFIG_CONSOLE_CURSOR + * CONFIG_VIDEO_LOGO - display Linux Logo in upper left corner. + * Use CONFIG_SPLASH_SCREEN_ALIGN with + * environment variable "splashpos" to place + * the logo on other position. In this case + * no CONSOLE_EXTRA_INFO is possible. + * CONFIG_VIDEO_BMP_LOGO - use bmp_logo instead of linux_logo + * CONFIG_CONSOLE_EXTRA_INFO - display additional board information + * strings that normaly goes to serial + * port. This define requires a board + * specific function: + * video_drawstring (VIDEO_INFO_X, + * VIDEO_INFO_Y + i*VIDEO_FONT_HEIGHT, + * info); + * that fills a info buffer at i=row. + * s.a: board/eltec/bab7xx. + * CONFIG_VGA_AS_SINGLE_DEVICE - If set the framebuffer device will be + * initialized as an output only device. + * The Keyboard driver will not be + * set-up. This may be used, if you have + * no or more than one Keyboard devices + * (USB Keyboard, AT Keyboard). + * + * CONFIG_VIDEO_SW_CURSOR: - Draws a cursor after the last + * character. No blinking is provided. + * Uses the macros CURSOR_SET and + * CURSOR_OFF. + * + * CONFIG_VIDEO_HW_CURSOR: - Uses the hardware cursor capability + * of the graphic chip. Uses the macro + * CURSOR_SET. ATTENTION: If booting an + * OS, the display driver must disable + * the hardware register of the graphic + * chip. Otherwise a blinking field is + * displayed. + */ + +#include +#include +#include +#include + +/* + * Console device defines with SMI graphic + * Any other graphic must change this section + */ + +#ifdef CONFIG_VIDEO_SMI_LYNXEM + +#define VIDEO_FB_LITTLE_ENDIAN +#define VIDEO_HW_RECTFILL +#define VIDEO_HW_BITBLT +#endif + +/* + * Defines for the CT69000 driver + */ +#ifdef CONFIG_VIDEO_CT69000 + +#define VIDEO_FB_LITTLE_ENDIAN +#define VIDEO_HW_RECTFILL +#define VIDEO_HW_BITBLT +#endif + +/* + * Defines for the SED13806 driver + */ +#ifdef CONFIG_VIDEO_SED13806 + +#ifndef CONFIG_TOTAL5200 +#define VIDEO_FB_LITTLE_ENDIAN +#endif +#define VIDEO_HW_RECTFILL +#define VIDEO_HW_BITBLT +#endif + +/* + * Defines for the SED13806 driver + */ +#ifdef CONFIG_VIDEO_SM501 + +#ifdef CONFIG_HH405 +#define VIDEO_FB_LITTLE_ENDIAN +#endif +#endif + +#ifdef CONFIG_VIDEO_MXS +#define VIDEO_FB_16BPP_WORD_SWAP +#endif + +/* + * Defines for the MB862xx driver + */ +#ifdef CONFIG_VIDEO_MB862xx + +#ifdef CONFIG_VIDEO_CORALP +#define VIDEO_FB_LITTLE_ENDIAN +#endif +#ifdef CONFIG_VIDEO_MB862xx_ACCEL +#define VIDEO_HW_RECTFILL +#define VIDEO_HW_BITBLT +#endif +#endif + +/* + * Defines for the i.MX31 driver (mx3fb.c) + */ +#if defined(CONFIG_VIDEO_MX3) || defined(CONFIG_VIDEO_IPUV3) +#define VIDEO_FB_16BPP_WORD_SWAP +#endif + +/* + * Include video_fb.h after definitions of VIDEO_HW_RECTFILL etc. + */ +#include + +#include + +/* + * some Macros + */ +#define VIDEO_VISIBLE_COLS (pGD->winSizeX) +#define VIDEO_VISIBLE_ROWS (pGD->winSizeY) +#define VIDEO_PIXEL_SIZE (pGD->gdfBytesPP) +#define VIDEO_DATA_FORMAT (pGD->gdfIndex) +#define VIDEO_FB_ADRS (pGD->frameAdrs) + +/* + * Console device defines with i8042 keyboard controller + * Any other keyboard controller must change this section + */ + +#ifdef CONFIG_I8042_KBD +#include + +#define VIDEO_KBD_INIT_FCT i8042_kbd_init() +#define VIDEO_TSTC_FCT i8042_tstc +#define VIDEO_GETC_FCT i8042_getc +#endif + +/* + * Console device + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CMD_DATE) +#include +#endif + +#if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN) +#include +#include +#include +#endif + +/* + * Cursor definition: + * CONFIG_CONSOLE_CURSOR: Uses a timer function (see drivers/input/i8042.c) + * to let the cursor blink. Uses the macros + * CURSOR_OFF and CURSOR_ON. + * CONFIG_VIDEO_SW_CURSOR: Draws a cursor after the last character. No + * blinking is provided. Uses the macros CURSOR_SET + * and CURSOR_OFF. + * CONFIG_VIDEO_HW_CURSOR: Uses the hardware cursor capability of the + * graphic chip. Uses the macro CURSOR_SET. + * ATTENTION: If booting an OS, the display driver + * must disable the hardware register of the graphic + * chip. Otherwise a blinking field is displayed + */ +#if !defined(CONFIG_CONSOLE_CURSOR) && \ + !defined(CONFIG_VIDEO_SW_CURSOR) && \ + !defined(CONFIG_VIDEO_HW_CURSOR) +/* no Cursor defined */ +#define CURSOR_ON +#define CURSOR_OFF +#define CURSOR_SET +#endif + +#if defined(CONFIG_CONSOLE_CURSOR) || defined(CONFIG_VIDEO_SW_CURSOR) +#if defined(CURSOR_ON) || \ + (defined(CONFIG_CONSOLE_CURSOR) && defined(CONFIG_VIDEO_SW_CURSOR)) +#error only one of CONFIG_CONSOLE_CURSOR, CONFIG_VIDEO_SW_CURSOR, \ + or CONFIG_VIDEO_HW_CURSOR can be defined +#endif +void console_cursor(int state); + +#define CURSOR_ON console_cursor(1) +#define CURSOR_OFF console_cursor(0) +#define CURSOR_SET video_set_cursor() +#endif /* CONFIG_CONSOLE_CURSOR || CONFIG_VIDEO_SW_CURSOR */ + +#ifdef CONFIG_CONSOLE_CURSOR +#ifndef CONFIG_CONSOLE_TIME +#error CONFIG_CONSOLE_CURSOR must be defined for CONFIG_CONSOLE_TIME +#endif +#ifndef CONFIG_I8042_KBD +#warning Cursor drawing on/off needs timer function s.a. drivers/input/i8042.c +#endif +#endif /* CONFIG_CONSOLE_CURSOR */ + + +#ifdef CONFIG_VIDEO_HW_CURSOR +#ifdef CURSOR_ON +#error only one of CONFIG_CONSOLE_CURSOR, CONFIG_VIDEO_SW_CURSOR, \ + or CONFIG_VIDEO_HW_CURSOR can be defined +#endif +#define CURSOR_ON +#define CURSOR_OFF +#define CURSOR_SET video_set_hw_cursor(console_col * VIDEO_FONT_WIDTH, \ + (console_row * VIDEO_FONT_HEIGHT) + video_logo_height) +#endif /* CONFIG_VIDEO_HW_CURSOR */ + +#ifdef CONFIG_VIDEO_LOGO +#ifdef CONFIG_VIDEO_BMP_LOGO +#include +#include +#define VIDEO_LOGO_WIDTH BMP_LOGO_WIDTH +#define VIDEO_LOGO_HEIGHT BMP_LOGO_HEIGHT +#define VIDEO_LOGO_LUT_OFFSET BMP_LOGO_OFFSET +#define VIDEO_LOGO_COLORS BMP_LOGO_COLORS + +#else /* CONFIG_VIDEO_BMP_LOGO */ +#define LINUX_LOGO_WIDTH 80 +#define LINUX_LOGO_HEIGHT 80 +#define LINUX_LOGO_COLORS 214 +#define LINUX_LOGO_LUT_OFFSET 0x20 +#define __initdata +#include +#define VIDEO_LOGO_WIDTH LINUX_LOGO_WIDTH +#define VIDEO_LOGO_HEIGHT LINUX_LOGO_HEIGHT +#define VIDEO_LOGO_LUT_OFFSET LINUX_LOGO_LUT_OFFSET +#define VIDEO_LOGO_COLORS LINUX_LOGO_COLORS +#endif /* CONFIG_VIDEO_BMP_LOGO */ +#define VIDEO_INFO_X (VIDEO_LOGO_WIDTH) +#define VIDEO_INFO_Y (VIDEO_FONT_HEIGHT/2) +#else /* CONFIG_VIDEO_LOGO */ +#define VIDEO_LOGO_WIDTH 0 +#define VIDEO_LOGO_HEIGHT 0 +#endif /* CONFIG_VIDEO_LOGO */ + +#define VIDEO_COLS VIDEO_VISIBLE_COLS +#define VIDEO_ROWS VIDEO_VISIBLE_ROWS +#define VIDEO_SIZE (VIDEO_ROWS*VIDEO_COLS*VIDEO_PIXEL_SIZE) +#define VIDEO_PIX_BLOCKS (VIDEO_SIZE >> 2) +#define VIDEO_LINE_LEN (VIDEO_COLS*VIDEO_PIXEL_SIZE) +#define VIDEO_BURST_LEN (VIDEO_COLS/8) + +#ifdef CONFIG_VIDEO_LOGO +#define CONSOLE_ROWS ((VIDEO_ROWS - video_logo_height) / VIDEO_FONT_HEIGHT) +#else +#define CONSOLE_ROWS (VIDEO_ROWS / VIDEO_FONT_HEIGHT) +#endif + +#define CONSOLE_COLS (VIDEO_COLS / VIDEO_FONT_WIDTH) +#define CONSOLE_ROW_SIZE (VIDEO_FONT_HEIGHT * VIDEO_LINE_LEN) +#define CONSOLE_ROW_FIRST (video_console_address) +#define CONSOLE_ROW_SECOND (video_console_address + CONSOLE_ROW_SIZE) +#define CONSOLE_ROW_LAST (video_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE) +#define CONSOLE_SIZE (CONSOLE_ROW_SIZE * CONSOLE_ROWS) +#define CONSOLE_SCROLL_SIZE (CONSOLE_SIZE - CONSOLE_ROW_SIZE) + +/* Macros */ +#ifdef VIDEO_FB_LITTLE_ENDIAN +#define SWAP16(x) ((((x) & 0x00ff) << 8) | \ + ((x) >> 8) \ + ) +#define SWAP32(x) ((((x) & 0x000000ff) << 24) | \ + (((x) & 0x0000ff00) << 8) | \ + (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0xff000000) >> 24) \ + ) +#define SHORTSWAP32(x) ((((x) & 0x000000ff) << 8) | \ + (((x) & 0x0000ff00) >> 8) | \ + (((x) & 0x00ff0000) << 8) | \ + (((x) & 0xff000000) >> 8) \ + ) +#else +#define SWAP16(x) (x) +#define SWAP32(x) (x) +#if defined(VIDEO_FB_16BPP_WORD_SWAP) +#define SHORTSWAP32(x) (((x) >> 16) | ((x) << 16)) +#else +#define SHORTSWAP32(x) (x) +#endif +#endif + +#ifdef CONFIG_CONSOLE_EXTRA_INFO +/* + * setup a board string: type, speed, etc. + * + * line_number: location to place info string beside logo + * info: buffer for info string + */ +extern void video_get_info_str(int line_number, char *info); +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* Locals */ +static GraphicDevice *pGD; /* Pointer to Graphic array */ + +static void *video_fb_address; /* frame buffer address */ +static void *video_console_address; /* console buffer start address */ + +static int video_logo_height = VIDEO_LOGO_HEIGHT; + +static int __maybe_unused cursor_state; +static int __maybe_unused old_col; +static int __maybe_unused old_row; + +static int console_col; /* cursor col */ +static int console_row; /* cursor row */ + +static u32 eorx, fgx, bgx; /* color pats */ + +static int cfb_do_flush_cache; + +#ifdef CONFIG_CFB_CONSOLE_ANSI +static char ansi_buf[10]; +static int ansi_buf_size; +static int ansi_colors_need_revert; +static int ansi_cursor_hidden; +#endif + +static const int video_font_draw_table8[] = { + 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, + 0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff, + 0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, + 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff +}; + +static const int video_font_draw_table15[] = { + 0x00000000, 0x00007fff, 0x7fff0000, 0x7fff7fff +}; + +static const int video_font_draw_table16[] = { + 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff +}; + +static const int video_font_draw_table24[16][3] = { + {0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00ffffff}, + {0x00000000, 0x0000ffff, 0xff000000}, + {0x00000000, 0x0000ffff, 0xffffffff}, + {0x000000ff, 0xffff0000, 0x00000000}, + {0x000000ff, 0xffff0000, 0x00ffffff}, + {0x000000ff, 0xffffffff, 0xff000000}, + {0x000000ff, 0xffffffff, 0xffffffff}, + {0xffffff00, 0x00000000, 0x00000000}, + {0xffffff00, 0x00000000, 0x00ffffff}, + {0xffffff00, 0x0000ffff, 0xff000000}, + {0xffffff00, 0x0000ffff, 0xffffffff}, + {0xffffffff, 0xffff0000, 0x00000000}, + {0xffffffff, 0xffff0000, 0x00ffffff}, + {0xffffffff, 0xffffffff, 0xff000000}, + {0xffffffff, 0xffffffff, 0xffffffff} +}; + +static const int video_font_draw_table32[16][4] = { + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00ffffff}, + {0x00000000, 0x00000000, 0x00ffffff, 0x00000000}, + {0x00000000, 0x00000000, 0x00ffffff, 0x00ffffff}, + {0x00000000, 0x00ffffff, 0x00000000, 0x00000000}, + {0x00000000, 0x00ffffff, 0x00000000, 0x00ffffff}, + {0x00000000, 0x00ffffff, 0x00ffffff, 0x00000000}, + {0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff}, + {0x00ffffff, 0x00000000, 0x00000000, 0x00000000}, + {0x00ffffff, 0x00000000, 0x00000000, 0x00ffffff}, + {0x00ffffff, 0x00000000, 0x00ffffff, 0x00000000}, + {0x00ffffff, 0x00000000, 0x00ffffff, 0x00ffffff}, + {0x00ffffff, 0x00ffffff, 0x00000000, 0x00000000}, + {0x00ffffff, 0x00ffffff, 0x00000000, 0x00ffffff}, + {0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00000000}, + {0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff} +}; + +/* + * Implement a weak default function for boards that optionally + * need to skip the cfb initialization. + */ +__weak int board_cfb_skip(void) +{ + /* As default, don't skip cfb init */ + return 0; +} + +static void video_drawchars(int xx, int yy, unsigned char *s, int count) +{ + u8 *cdat, *dest, *dest0; + int rows, offset, c; + + offset = yy * VIDEO_LINE_LEN + xx * VIDEO_PIXEL_SIZE; + dest0 = video_fb_address + offset; + + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + case GDF__8BIT_332RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = + (video_font_draw_table8[bits >> 4] & + eorx) ^ bgx; + + if (VIDEO_FONT_WIDTH == 4) + continue; + + ((u32 *) dest)[1] = + (video_font_draw_table8[bits & 15] & + eorx) ^ bgx; + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + + case GDF_15BIT_555RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = + SHORTSWAP32((video_font_draw_table15 + [bits >> 6] & eorx) ^ + bgx); + ((u32 *) dest)[1] = + SHORTSWAP32((video_font_draw_table15 + [bits >> 4 & 3] & eorx) ^ + bgx); + + if (VIDEO_FONT_WIDTH == 4) + continue; + + ((u32 *) dest)[2] = + SHORTSWAP32((video_font_draw_table15 + [bits >> 2 & 3] & eorx) ^ + bgx); + ((u32 *) dest)[3] = + SHORTSWAP32((video_font_draw_table15 + [bits & 3] & eorx) ^ + bgx); + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + + case GDF_16BIT_565RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = + SHORTSWAP32((video_font_draw_table16 + [bits >> 6] & eorx) ^ + bgx); + ((u32 *) dest)[1] = + SHORTSWAP32((video_font_draw_table16 + [bits >> 4 & 3] & eorx) ^ + bgx); + + if (VIDEO_FONT_WIDTH == 4) + continue; + + ((u32 *) dest)[2] = + SHORTSWAP32((video_font_draw_table16 + [bits >> 2 & 3] & eorx) ^ + bgx); + ((u32 *) dest)[3] = + SHORTSWAP32((video_font_draw_table16 + [bits & 3] & eorx) ^ + bgx); + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + + case GDF_32BIT_X888RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = + SWAP32((video_font_draw_table32 + [bits >> 4][0] & eorx) ^ bgx); + ((u32 *) dest)[1] = + SWAP32((video_font_draw_table32 + [bits >> 4][1] & eorx) ^ bgx); + ((u32 *) dest)[2] = + SWAP32((video_font_draw_table32 + [bits >> 4][2] & eorx) ^ bgx); + ((u32 *) dest)[3] = + SWAP32((video_font_draw_table32 + [bits >> 4][3] & eorx) ^ bgx); + + + if (VIDEO_FONT_WIDTH == 4) + continue; + + ((u32 *) dest)[4] = + SWAP32((video_font_draw_table32 + [bits & 15][0] & eorx) ^ bgx); + ((u32 *) dest)[5] = + SWAP32((video_font_draw_table32 + [bits & 15][1] & eorx) ^ bgx); + ((u32 *) dest)[6] = + SWAP32((video_font_draw_table32 + [bits & 15][2] & eorx) ^ bgx); + ((u32 *) dest)[7] = + SWAP32((video_font_draw_table32 + [bits & 15][3] & eorx) ^ bgx); + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + + case GDF_24BIT_888RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = + (video_font_draw_table24[bits >> 4][0] + & eorx) ^ bgx; + ((u32 *) dest)[1] = + (video_font_draw_table24[bits >> 4][1] + & eorx) ^ bgx; + ((u32 *) dest)[2] = + (video_font_draw_table24[bits >> 4][2] + & eorx) ^ bgx; + + if (VIDEO_FONT_WIDTH == 4) + continue; + + ((u32 *) dest)[3] = + (video_font_draw_table24[bits & 15][0] + & eorx) ^ bgx; + ((u32 *) dest)[4] = + (video_font_draw_table24[bits & 15][1] + & eorx) ^ bgx; + ((u32 *) dest)[5] = + (video_font_draw_table24[bits & 15][2] + & eorx) ^ bgx; + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + } +} + +static inline void video_drawstring(int xx, int yy, unsigned char *s) +{ + video_drawchars(xx, yy, s, strlen((char *) s)); +} + +static void video_putchar(int xx, int yy, unsigned char c) +{ + video_drawchars(xx, yy + video_logo_height, &c, 1); +} + +#if defined(CONFIG_CONSOLE_CURSOR) || defined(CONFIG_VIDEO_SW_CURSOR) +static void video_set_cursor(void) +{ + if (cursor_state) + console_cursor(0); + console_cursor(1); +} + +static void video_invertchar(int xx, int yy) +{ + int firstx = xx * VIDEO_PIXEL_SIZE; + int lastx = (xx + VIDEO_FONT_WIDTH) * VIDEO_PIXEL_SIZE; + int firsty = yy * VIDEO_LINE_LEN; + int lasty = (yy + VIDEO_FONT_HEIGHT) * VIDEO_LINE_LEN; + int x, y; + for (y = firsty; y < lasty; y += VIDEO_LINE_LEN) { + for (x = firstx; x < lastx; x++) { + u8 *dest = (u8 *)(video_fb_address) + x + y; + *dest = ~*dest; + } + } +} + +void console_cursor(int state) +{ +#ifdef CONFIG_CONSOLE_TIME + struct rtc_time tm; + char info[16]; + + /* time update only if cursor is on (faster scroll) */ + if (state) { + rtc_get(&tm); + + sprintf(info, " %02d:%02d:%02d ", tm.tm_hour, tm.tm_min, + tm.tm_sec); + video_drawstring(VIDEO_VISIBLE_COLS - 10 * VIDEO_FONT_WIDTH, + VIDEO_INFO_Y, (uchar *) info); + + sprintf(info, "%02d.%02d.%04d", tm.tm_mday, tm.tm_mon, + tm.tm_year); + video_drawstring(VIDEO_VISIBLE_COLS - 10 * VIDEO_FONT_WIDTH, + VIDEO_INFO_Y + 1 * VIDEO_FONT_HEIGHT, + (uchar *) info); + } +#endif + + if (cursor_state != state) { + if (cursor_state) { + /* turn off the cursor */ + video_invertchar(old_col * VIDEO_FONT_WIDTH, + old_row * VIDEO_FONT_HEIGHT + + video_logo_height); + } else { + /* turn off the cursor and record where it is */ + video_invertchar(console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT + + video_logo_height); + old_col = console_col; + old_row = console_row; + } + cursor_state = state; + } + if (cfb_do_flush_cache) + flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE); +} +#endif + +#ifndef VIDEO_HW_RECTFILL +static void memsetl(int *p, int c, int v) +{ + while (c--) + *(p++) = v; +} +#endif + +#ifndef VIDEO_HW_BITBLT +static void memcpyl(int *d, int *s, int c) +{ + while (c--) + *(d++) = *(s++); +} +#endif + +static void console_clear_line(int line, int begin, int end) +{ +#ifdef VIDEO_HW_RECTFILL + video_hw_rectfill(VIDEO_PIXEL_SIZE, /* bytes per pixel */ + VIDEO_FONT_WIDTH * begin, /* dest pos x */ + video_logo_height + + VIDEO_FONT_HEIGHT * line, /* dest pos y */ + VIDEO_FONT_WIDTH * (end - begin + 1), /* fr. width */ + VIDEO_FONT_HEIGHT, /* frame height */ + bgx /* fill color */ + ); +#else + if (begin == 0 && (end + 1) == CONSOLE_COLS) { + memsetl(CONSOLE_ROW_FIRST + + CONSOLE_ROW_SIZE * line, /* offset of row */ + CONSOLE_ROW_SIZE >> 2, /* length of row */ + bgx /* fill color */ + ); + } else { + void *offset; + int i, size; + + offset = CONSOLE_ROW_FIRST + + CONSOLE_ROW_SIZE * line + /* offset of row */ + VIDEO_FONT_WIDTH * + VIDEO_PIXEL_SIZE * begin; /* offset of col */ + size = VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE * (end - begin + 1); + size >>= 2; /* length to end for memsetl() */ + /* fill at col offset of i'th line using bgx as fill color */ + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + memsetl(offset + i * VIDEO_LINE_LEN, size, bgx); + } +#endif +} + +static void console_scrollup(void) +{ + /* copy up rows ignoring the first one */ + +#ifdef VIDEO_HW_BITBLT + video_hw_bitblt(VIDEO_PIXEL_SIZE, /* bytes per pixel */ + 0, /* source pos x */ + video_logo_height + + VIDEO_FONT_HEIGHT, /* source pos y */ + 0, /* dest pos x */ + video_logo_height, /* dest pos y */ + VIDEO_VISIBLE_COLS, /* frame width */ + VIDEO_VISIBLE_ROWS + - video_logo_height + - VIDEO_FONT_HEIGHT /* frame height */ + ); +#else + memcpyl(CONSOLE_ROW_FIRST, CONSOLE_ROW_SECOND, + CONSOLE_SCROLL_SIZE >> 2); +#endif + /* clear the last one */ + console_clear_line(CONSOLE_ROWS - 1, 0, CONSOLE_COLS - 1); +} + +static void console_back(void) +{ + console_col--; + + if (console_col < 0) { + console_col = CONSOLE_COLS - 1; + console_row--; + if (console_row < 0) + console_row = 0; + } +} + +#ifdef CONFIG_CFB_CONSOLE_ANSI + +static void console_clear(void) +{ +#ifdef VIDEO_HW_RECTFILL + video_hw_rectfill(VIDEO_PIXEL_SIZE, /* bytes per pixel */ + 0, /* dest pos x */ + video_logo_height, /* dest pos y */ + VIDEO_VISIBLE_COLS, /* frame width */ + VIDEO_VISIBLE_ROWS, /* frame height */ + bgx /* fill color */ + ); +#else + memsetl(CONSOLE_ROW_FIRST, CONSOLE_SIZE, bgx); +#endif +} + +static void console_cursor_fix(void) +{ + if (console_row < 0) + console_row = 0; + if (console_row >= CONSOLE_ROWS) + console_row = CONSOLE_ROWS - 1; + if (console_col < 0) + console_col = 0; + if (console_col >= CONSOLE_COLS) + console_col = CONSOLE_COLS - 1; +} + +static void console_cursor_up(int n) +{ + console_row -= n; + console_cursor_fix(); +} + +static void console_cursor_down(int n) +{ + console_row += n; + console_cursor_fix(); +} + +static void console_cursor_left(int n) +{ + console_col -= n; + console_cursor_fix(); +} + +static void console_cursor_right(int n) +{ + console_col += n; + console_cursor_fix(); +} + +static void console_cursor_set_position(int row, int col) +{ + if (console_row != -1) + console_row = row; + if (console_col != -1) + console_col = col; + console_cursor_fix(); +} + +static void console_previousline(int n) +{ + /* FIXME: also scroll terminal ? */ + console_row -= n; + console_cursor_fix(); +} + +static void console_swap_colors(void) +{ + eorx = fgx; + fgx = bgx; + bgx = eorx; + eorx = fgx ^ bgx; +} + +static inline int console_cursor_is_visible(void) +{ + return !ansi_cursor_hidden; +} +#else +static inline int console_cursor_is_visible(void) +{ + return 1; +} +#endif + +static void console_newline(int n) +{ + console_row += n; + console_col = 0; + + /* Check if we need to scroll the terminal */ + if (console_row >= CONSOLE_ROWS) { + /* Scroll everything up */ + console_scrollup(); + + /* Decrement row number */ + console_row = CONSOLE_ROWS - 1; + } +} + +static void console_cr(void) +{ + console_col = 0; +} + +static void parse_putc(const char c) +{ + static int nl = 1; + + if (console_cursor_is_visible()) + CURSOR_OFF; + + switch (c) { + case 13: /* back to first column */ + console_cr(); + break; + + case '\n': /* next line */ + if (console_col || (!console_col && nl)) + console_newline(1); + nl = 1; + break; + + case 9: /* tab 8 */ + console_col |= 0x0008; + console_col &= ~0x0007; + + if (console_col >= CONSOLE_COLS) + console_newline(1); + break; + + case 8: /* backspace */ + console_back(); + break; + + case 7: /* bell */ + break; /* ignored */ + + default: /* draw the char */ + video_putchar(console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, c); + console_col++; + + /* check for newline */ + if (console_col >= CONSOLE_COLS) { + console_newline(1); + nl = 0; + } + } + + if (console_cursor_is_visible()) + CURSOR_SET; +} + +void video_putc(const char c) +{ +#ifdef CONFIG_CFB_CONSOLE_ANSI + int i; + + if (c == 27) { + for (i = 0; i < ansi_buf_size; ++i) + parse_putc(ansi_buf[i]); + ansi_buf[0] = 27; + ansi_buf_size = 1; + return; + } + + if (ansi_buf_size > 0) { + /* + * 0 - ESC + * 1 - [ + * 2 - num1 + * 3 - .. + * 4 - ; + * 5 - num2 + * 6 - .. + * - cchar + */ + int next = 0; + + int flush = 0; + int fail = 0; + + int num1 = 0; + int num2 = 0; + int cchar = 0; + + ansi_buf[ansi_buf_size++] = c; + + if (ansi_buf_size >= sizeof(ansi_buf)) + fail = 1; + + for (i = 0; i < ansi_buf_size; ++i) { + if (fail) + break; + + switch (next) { + case 0: + if (ansi_buf[i] == 27) + next = 1; + else + fail = 1; + break; + + case 1: + if (ansi_buf[i] == '[') + next = 2; + else + fail = 1; + break; + + case 2: + if (ansi_buf[i] >= '0' && ansi_buf[i] <= '9') { + num1 = ansi_buf[i]-'0'; + next = 3; + } else if (ansi_buf[i] != '?') { + --i; + num1 = 1; + next = 4; + } + break; + + case 3: + if (ansi_buf[i] >= '0' && ansi_buf[i] <= '9') { + num1 *= 10; + num1 += ansi_buf[i]-'0'; + } else { + --i; + next = 4; + } + break; + + case 4: + if (ansi_buf[i] != ';') { + --i; + next = 7; + } else + next = 5; + break; + + case 5: + if (ansi_buf[i] >= '0' && ansi_buf[i] <= '9') { + num2 = ansi_buf[i]-'0'; + next = 6; + } else + fail = 1; + break; + + case 6: + if (ansi_buf[i] >= '0' && ansi_buf[i] <= '9') { + num2 *= 10; + num2 += ansi_buf[i]-'0'; + } else { + --i; + next = 7; + } + break; + + case 7: + if ((ansi_buf[i] >= 'A' && ansi_buf[i] <= 'H') + || ansi_buf[i] == 'J' + || ansi_buf[i] == 'K' + || ansi_buf[i] == 'h' + || ansi_buf[i] == 'l' + || ansi_buf[i] == 'm') { + cchar = ansi_buf[i]; + flush = 1; + } else + fail = 1; + break; + } + } + + if (fail) { + for (i = 0; i < ansi_buf_size; ++i) + parse_putc(ansi_buf[i]); + ansi_buf_size = 0; + return; + } + + if (flush) { + if (!ansi_cursor_hidden) + CURSOR_OFF; + ansi_buf_size = 0; + switch (cchar) { + case 'A': + /* move cursor num1 rows up */ + console_cursor_up(num1); + break; + case 'B': + /* move cursor num1 rows down */ + console_cursor_down(num1); + break; + case 'C': + /* move cursor num1 columns forward */ + console_cursor_right(num1); + break; + case 'D': + /* move cursor num1 columns back */ + console_cursor_left(num1); + break; + case 'E': + /* move cursor num1 rows up at begin of row */ + console_previousline(num1); + break; + case 'F': + /* move cursor num1 rows down at begin of row */ + console_newline(num1); + break; + case 'G': + /* move cursor to column num1 */ + console_cursor_set_position(-1, num1-1); + break; + case 'H': + /* move cursor to row num1, column num2 */ + console_cursor_set_position(num1-1, num2-1); + break; + case 'J': + /* clear console and move cursor to 0, 0 */ + console_clear(); + console_cursor_set_position(0, 0); + break; + case 'K': + /* clear line */ + if (num1 == 0) + console_clear_line(console_row, + console_col, + CONSOLE_COLS-1); + else if (num1 == 1) + console_clear_line(console_row, + 0, console_col); + else + console_clear_line(console_row, + 0, CONSOLE_COLS-1); + break; + case 'h': + ansi_cursor_hidden = 0; + break; + case 'l': + ansi_cursor_hidden = 1; + break; + case 'm': + if (num1 == 0) { /* reset swapped colors */ + if (ansi_colors_need_revert) { + console_swap_colors(); + ansi_colors_need_revert = 0; + } + } else if (num1 == 7) { /* once swap colors */ + if (!ansi_colors_need_revert) { + console_swap_colors(); + ansi_colors_need_revert = 1; + } + } + break; + } + if (!ansi_cursor_hidden) + CURSOR_SET; + } + } else { + parse_putc(c); + } +#else + parse_putc(c); +#endif + if (cfb_do_flush_cache) + flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE); +} + +void video_puts(const char *s) +{ + int count = strlen(s); + + while (count--) + video_putc(*s++); +} + +/* + * Do not enforce drivers (or board code) to provide empty + * video_set_lut() if they do not support 8 bpp format. + * Implement weak default function instead. + */ +void __video_set_lut(unsigned int index, unsigned char r, + unsigned char g, unsigned char b) +{ +} + +void video_set_lut(unsigned int, unsigned char, unsigned char, unsigned char) + __attribute__ ((weak, alias("__video_set_lut"))); + +#if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN) + +#define FILL_8BIT_332RGB(r,g,b) { \ + *fb = ((r>>5)<<5) | ((g>>5)<<2) | (b>>6); \ + fb ++; \ +} + +#define FILL_15BIT_555RGB(r,g,b) { \ + *(unsigned short *)fb = \ + SWAP16((unsigned short)(((r>>3)<<10) | \ + ((g>>3)<<5) | \ + (b>>3))); \ + fb += 2; \ +} + +#define FILL_16BIT_565RGB(r,g,b) { \ + *(unsigned short *)fb = \ + SWAP16((unsigned short)((((r)>>3)<<11)| \ + (((g)>>2)<<5) | \ + ((b)>>3))); \ + fb += 2; \ +} + +#define FILL_32BIT_X888RGB(r,g,b) { \ + *(unsigned long *)fb = \ + SWAP32((unsigned long)(((r<<16) | \ + (g<<8) | \ + b))); \ + fb += 4; \ +} + +#ifdef VIDEO_FB_LITTLE_ENDIAN +#define FILL_24BIT_888RGB(r,g,b) { \ + fb[0] = b; \ + fb[1] = g; \ + fb[2] = r; \ + fb += 3; \ +} +#else +#define FILL_24BIT_888RGB(r,g,b) { \ + fb[0] = r; \ + fb[1] = g; \ + fb[2] = b; \ + fb += 3; \ +} +#endif + +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) +static inline void fill_555rgb_pswap(uchar *fb, int x, u8 r, u8 g, u8 b) +{ + ushort *dst = (ushort *) fb; + ushort color = (ushort) (((r >> 3) << 10) | + ((g >> 3) << 5) | + (b >> 3)); + if (x & 1) + *(--dst) = color; + else + *(++dst) = color; +} +#endif + +/* + * RLE8 bitmap support + */ + +#ifdef CONFIG_VIDEO_BMP_RLE8 +/* Pre-calculated color table entry */ +struct palette { + union { + unsigned short w; /* word */ + unsigned int dw; /* double word */ + } ce; /* color entry */ +}; + +/* + * Helper to draw encoded/unencoded run. + */ +static void draw_bitmap(uchar **fb, uchar *bm, struct palette *p, + int cnt, int enc) +{ + ulong addr = (ulong) *fb; + int *off; + int enc_off = 1; + int i; + + /* + * Setup offset of the color index in the bitmap. + * Color index of encoded run is at offset 1. + */ + off = enc ? &enc_off : &i; + + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + for (i = 0; i < cnt; i++) + *(unsigned char *) addr++ = bm[*off]; + break; + case GDF_15BIT_555RGB: + case GDF_16BIT_565RGB: + /* differences handled while pre-calculating palette */ + for (i = 0; i < cnt; i++) { + *(unsigned short *) addr = p[bm[*off]].ce.w; + addr += 2; + } + break; + case GDF_32BIT_X888RGB: + for (i = 0; i < cnt; i++) { + *(unsigned long *) addr = p[bm[*off]].ce.dw; + addr += 4; + } + break; + } + *fb = (uchar *) addr; /* return modified address */ +} + +static int display_rle8_bitmap(bmp_image_t *img, int xoff, int yoff, + int width, int height) +{ + unsigned char *bm; + unsigned char *fbp; + unsigned int cnt, runlen; + int decode = 1; + int x, y, bpp, i, ncolors; + struct palette p[256]; + bmp_color_table_entry_t cte; + int green_shift, red_off; + int limit = VIDEO_COLS * VIDEO_ROWS; + int pixels = 0; + + x = 0; + y = __le32_to_cpu(img->header.height) - 1; + ncolors = __le32_to_cpu(img->header.colors_used); + bpp = VIDEO_PIXEL_SIZE; + fbp = (unsigned char *) ((unsigned int) video_fb_address + + (((y + yoff) * VIDEO_COLS) + xoff) * bpp); + + bm = (uchar *) img + __le32_to_cpu(img->header.data_offset); + + /* pre-calculate and setup palette */ + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + for (i = 0; i < ncolors; i++) { + cte = img->color_table[i]; + video_set_lut(i, cte.red, cte.green, cte.blue); + } + break; + case GDF_15BIT_555RGB: + case GDF_16BIT_565RGB: + if (VIDEO_DATA_FORMAT == GDF_15BIT_555RGB) { + green_shift = 3; + red_off = 10; + } else { + green_shift = 2; + red_off = 11; + } + for (i = 0; i < ncolors; i++) { + cte = img->color_table[i]; + p[i].ce.w = SWAP16((unsigned short) + (((cte.red >> 3) << red_off) | + ((cte.green >> green_shift) << 5) | + cte.blue >> 3)); + } + break; + case GDF_32BIT_X888RGB: + for (i = 0; i < ncolors; i++) { + cte = img->color_table[i]; + p[i].ce.dw = SWAP32((cte.red << 16) | + (cte.green << 8) | + cte.blue); + } + break; + default: + printf("RLE Bitmap unsupported in video mode 0x%x\n", + VIDEO_DATA_FORMAT); + return -1; + } + + while (decode) { + switch (bm[0]) { + case 0: + switch (bm[1]) { + case 0: + /* scan line end marker */ + bm += 2; + x = 0; + y--; + fbp = (unsigned char *) + ((unsigned int) video_fb_address + + (((y + yoff) * VIDEO_COLS) + + xoff) * bpp); + continue; + case 1: + /* end of bitmap data marker */ + decode = 0; + break; + case 2: + /* run offset marker */ + x += bm[2]; + y -= bm[3]; + fbp = (unsigned char *) + ((unsigned int) video_fb_address + + (((y + yoff) * VIDEO_COLS) + + x + xoff) * bpp); + bm += 4; + break; + default: + /* unencoded run */ + cnt = bm[1]; + runlen = cnt; + pixels += cnt; + if (pixels > limit) + goto error; + + bm += 2; + if (y < height) { + if (x >= width) { + x += runlen; + goto next_run; + } + if (x + runlen > width) + cnt = width - x; + draw_bitmap(&fbp, bm, p, cnt, 0); + x += runlen; + } +next_run: + bm += runlen; + if (runlen & 1) + bm++; /* 0 padding if length is odd */ + } + break; + default: + /* encoded run */ + cnt = bm[0]; + runlen = cnt; + pixels += cnt; + if (pixels > limit) + goto error; + + if (y < height) { /* only draw into visible area */ + if (x >= width) { + x += runlen; + bm += 2; + continue; + } + if (x + runlen > width) + cnt = width - x; + draw_bitmap(&fbp, bm, p, cnt, 1); + x += runlen; + } + bm += 2; + break; + } + } + return 0; +error: + printf("Error: Too much encoded pixel data, validate your bitmap\n"); + return -1; +} +#endif + +/* + * Display the BMP file located at address bmp_image. + */ +int video_display_bitmap(ulong bmp_image, int x, int y) +{ + ushort xcount, ycount; + uchar *fb; + bmp_image_t *bmp = (bmp_image_t *) bmp_image; + uchar *bmap; + ushort padded_line; + unsigned long width, height, bpp; + unsigned colors; + unsigned long compression; + bmp_color_table_entry_t cte; + +#ifdef CONFIG_VIDEO_BMP_GZIP + unsigned char *dst = NULL; + ulong len; +#endif + + WATCHDOG_RESET(); + + if (!((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M'))) { + +#ifdef CONFIG_VIDEO_BMP_GZIP + /* + * Could be a gzipped bmp image, try to decrompress... + */ + len = CONFIG_SYS_VIDEO_LOGO_MAX_SIZE; + dst = malloc(CONFIG_SYS_VIDEO_LOGO_MAX_SIZE); + if (dst == NULL) { + printf("Error: malloc in gunzip failed!\n"); + return 1; + } + /* + * NB: we need to force offset of +2 + * See doc/README.displaying-bmps + */ + if (gunzip(dst+2, CONFIG_SYS_VIDEO_LOGO_MAX_SIZE-2, + (uchar *) bmp_image, + &len) != 0) { + printf("Error: no valid bmp or bmp.gz image at %lx\n", + bmp_image); + free(dst); + return 1; + } + if (len == CONFIG_SYS_VIDEO_LOGO_MAX_SIZE) { + printf("Image could be truncated " + "(increase CONFIG_SYS_VIDEO_LOGO_MAX_SIZE)!\n"); + } + + /* + * Set addr to decompressed image + */ + bmp = (bmp_image_t *)(dst+2); + + if (!((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M'))) { + printf("Error: no valid bmp.gz image at %lx\n", + bmp_image); + free(dst); + return 1; + } +#else + printf("Error: no valid bmp image at %lx\n", bmp_image); + return 1; +#endif /* CONFIG_VIDEO_BMP_GZIP */ + } + + width = le32_to_cpu(bmp->header.width); + height = le32_to_cpu(bmp->header.height); + bpp = le16_to_cpu(bmp->header.bit_count); + colors = le32_to_cpu(bmp->header.colors_used); + compression = le32_to_cpu(bmp->header.compression); + + debug("Display-bmp: %ld x %ld with %d colors\n", + width, height, colors); + + if (compression != BMP_BI_RGB +#ifdef CONFIG_VIDEO_BMP_RLE8 + && compression != BMP_BI_RLE8 +#endif + ) { + printf("Error: compression type %ld not supported\n", + compression); +#ifdef CONFIG_VIDEO_BMP_GZIP + if (dst) + free(dst); +#endif + return 1; + } + + padded_line = (((width * bpp + 7) / 8) + 3) & ~0x3; + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN + if (x == BMP_ALIGN_CENTER) + x = max(0, (VIDEO_VISIBLE_COLS - width) / 2); + else if (x < 0) + x = max(0, VIDEO_VISIBLE_COLS - width + x + 1); + + if (y == BMP_ALIGN_CENTER) + y = max(0, (VIDEO_VISIBLE_ROWS - height) / 2); + else if (y < 0) + y = max(0, VIDEO_VISIBLE_ROWS - height + y + 1); +#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ + + /* + * Just ignore elements which are completely beyond screen + * dimensions. + */ + if ((x >= VIDEO_VISIBLE_COLS) || (y >= VIDEO_VISIBLE_ROWS)) + return 0; + + if ((x + width) > VIDEO_VISIBLE_COLS) + width = VIDEO_VISIBLE_COLS - x; + if ((y + height) > VIDEO_VISIBLE_ROWS) + height = VIDEO_VISIBLE_ROWS - y; + + bmap = (uchar *) bmp + le32_to_cpu(bmp->header.data_offset); + fb = (uchar *) (video_fb_address + + ((y + height - 1) * VIDEO_COLS * VIDEO_PIXEL_SIZE) + + x * VIDEO_PIXEL_SIZE); + +#ifdef CONFIG_VIDEO_BMP_RLE8 + if (compression == BMP_BI_RLE8) { + return display_rle8_bitmap(bmp, x, y, width, height); + } +#endif + + /* We handle only 4, 8, or 24 bpp bitmaps */ + switch (le16_to_cpu(bmp->header.bit_count)) { + case 4: + padded_line -= width / 2; + ycount = height; + + switch (VIDEO_DATA_FORMAT) { + case GDF_32BIT_X888RGB: + while (ycount--) { + WATCHDOG_RESET(); + /* + * Don't assume that 'width' is an + * even number + */ + for (xcount = 0; xcount < width; xcount++) { + uchar idx; + + if (xcount & 1) { + idx = *bmap & 0xF; + bmap++; + } else + idx = *bmap >> 4; + cte = bmp->color_table[idx]; + FILL_32BIT_X888RGB(cte.red, cte.green, + cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + default: + puts("4bpp bitmap unsupported with current " + "video mode\n"); + break; + } + break; + + case 8: + padded_line -= width; + if (VIDEO_DATA_FORMAT == GDF__8BIT_INDEX) { + /* Copy colormap */ + for (xcount = 0; xcount < colors; ++xcount) { + cte = bmp->color_table[xcount]; + video_set_lut(xcount, cte.red, cte.green, + cte.blue); + } + } + ycount = height; + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + while (ycount--) { + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + *fb++ = *bmap++; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + case GDF__8BIT_332RGB: + while (ycount--) { + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; + FILL_8BIT_332RGB(cte.red, cte.green, + cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + case GDF_15BIT_555RGB: + while (ycount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + int xpos = x; +#endif + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + fill_555rgb_pswap(fb, xpos++, cte.red, + cte.green, + cte.blue); + fb += 2; +#else + FILL_15BIT_555RGB(cte.red, cte.green, + cte.blue); +#endif + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + case GDF_16BIT_565RGB: + while (ycount--) { + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; + FILL_16BIT_565RGB(cte.red, cte.green, + cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + case GDF_32BIT_X888RGB: + while (ycount--) { + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; + FILL_32BIT_X888RGB(cte.red, cte.green, + cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + case GDF_24BIT_888RGB: + while (ycount--) { + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; + FILL_24BIT_888RGB(cte.red, cte.green, + cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + } + break; + case 24: + padded_line -= 3 * width; + ycount = height; + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_332RGB: + while (ycount--) { + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + FILL_8BIT_332RGB(bmap[2], bmap[1], + bmap[0]); + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + case GDF_15BIT_555RGB: + while (ycount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + int xpos = x; +#endif + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + fill_555rgb_pswap(fb, xpos++, bmap[2], + bmap[1], bmap[0]); + fb += 2; +#else + FILL_15BIT_555RGB(bmap[2], bmap[1], + bmap[0]); +#endif + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + case GDF_16BIT_565RGB: + while (ycount--) { + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + FILL_16BIT_565RGB(bmap[2], bmap[1], + bmap[0]); + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + case GDF_32BIT_X888RGB: + while (ycount--) { + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + FILL_32BIT_X888RGB(bmap[2], bmap[1], + bmap[0]); + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + case GDF_24BIT_888RGB: + while (ycount--) { + WATCHDOG_RESET(); + xcount = width; + while (xcount--) { + FILL_24BIT_888RGB(bmap[2], bmap[1], + bmap[0]); + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + default: + printf("Error: 24 bits/pixel bitmap incompatible " + "with current video mode\n"); + break; + } + break; + default: + printf("Error: %d bit/pixel bitmaps not supported by U-Boot\n", + le16_to_cpu(bmp->header.bit_count)); + break; + } + +#ifdef CONFIG_VIDEO_BMP_GZIP + if (dst) { + free(dst); + } +#endif + + if (cfb_do_flush_cache) + flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE); + return (0); +} +#endif + + +#ifdef CONFIG_VIDEO_LOGO +static int video_logo_xpos; +static int video_logo_ypos; + +static void plot_logo_or_black(void *screen, int width, int x, int y, \ + int black); + +static void logo_plot(void *screen, int width, int x, int y) +{ + plot_logo_or_black(screen, width, x, y, 0); +} + +static void logo_black(void) +{ + plot_logo_or_black(video_fb_address, \ + VIDEO_COLS, \ + video_logo_xpos, \ + video_logo_ypos, \ + 1); +} + +static int do_clrlogo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc != 1) + return cmd_usage(cmdtp); + + logo_black(); + return 0; +} + +U_BOOT_CMD( + clrlogo, 1, 0, do_clrlogo, + "fill the boot logo area with black", + " " + ); + +static void plot_logo_or_black(void *screen, int width, int x, int y, int black) +{ + + int xcount, i; + int skip = (width - VIDEO_LOGO_WIDTH) * VIDEO_PIXEL_SIZE; + int ycount = video_logo_height; + unsigned char r, g, b, *logo_red, *logo_blue, *logo_green; + unsigned char *source; + unsigned char *dest; + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN + if (x == BMP_ALIGN_CENTER) + x = max(0, (VIDEO_VISIBLE_COLS - VIDEO_LOGO_WIDTH) / 2); + else if (x < 0) + x = max(0, VIDEO_VISIBLE_COLS - VIDEO_LOGO_WIDTH + x + 1); + + if (y == BMP_ALIGN_CENTER) + y = max(0, (VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT) / 2); + else if (y < 0) + y = max(0, VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT + y + 1); +#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ + + dest = (unsigned char *)screen + (y * width + x) * VIDEO_PIXEL_SIZE; + +#ifdef CONFIG_VIDEO_BMP_LOGO + source = bmp_logo_bitmap; + + /* Allocate temporary space for computing colormap */ + logo_red = malloc(BMP_LOGO_COLORS); + logo_green = malloc(BMP_LOGO_COLORS); + logo_blue = malloc(BMP_LOGO_COLORS); + /* Compute color map */ + for (i = 0; i < VIDEO_LOGO_COLORS; i++) { + logo_red[i] = (bmp_logo_palette[i] & 0x0f00) >> 4; + logo_green[i] = (bmp_logo_palette[i] & 0x00f0); + logo_blue[i] = (bmp_logo_palette[i] & 0x000f) << 4; + } +#else + source = linux_logo; + logo_red = linux_logo_red; + logo_green = linux_logo_green; + logo_blue = linux_logo_blue; +#endif + + if (VIDEO_DATA_FORMAT == GDF__8BIT_INDEX) { + for (i = 0; i < VIDEO_LOGO_COLORS; i++) { + video_set_lut(i + VIDEO_LOGO_LUT_OFFSET, + logo_red[i], logo_green[i], + logo_blue[i]); + } + } + + while (ycount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + int xpos = x; +#endif + xcount = VIDEO_LOGO_WIDTH; + while (xcount--) { + if (black) { + r = 0x00; + g = 0x00; + b = 0x00; + } else { + r = logo_red[*source - VIDEO_LOGO_LUT_OFFSET]; + g = logo_green[*source - VIDEO_LOGO_LUT_OFFSET]; + b = logo_blue[*source - VIDEO_LOGO_LUT_OFFSET]; + } + + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + *dest = *source; + break; + case GDF__8BIT_332RGB: + *dest = ((r >> 5) << 5) | + ((g >> 5) << 2) | + (b >> 6); + break; + case GDF_15BIT_555RGB: +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + fill_555rgb_pswap(dest, xpos++, r, g, b); +#else + *(unsigned short *) dest = + SWAP16((unsigned short) ( + ((r >> 3) << 10) | + ((g >> 3) << 5) | + (b >> 3))); +#endif + break; + case GDF_16BIT_565RGB: + *(unsigned short *) dest = + SWAP16((unsigned short) ( + ((r >> 3) << 11) | + ((g >> 2) << 5) | + (b >> 3))); + break; + case GDF_32BIT_X888RGB: + *(unsigned long *) dest = + SWAP32((unsigned long) ( + (r << 16) | + (g << 8) | + b)); + break; + case GDF_24BIT_888RGB: +#ifdef VIDEO_FB_LITTLE_ENDIAN + dest[0] = b; + dest[1] = g; + dest[2] = r; +#else + dest[0] = r; + dest[1] = g; + dest[2] = b; +#endif + break; + } + source++; + dest += VIDEO_PIXEL_SIZE; + } + dest += skip; + } +#ifdef CONFIG_VIDEO_BMP_LOGO + free(logo_red); + free(logo_green); + free(logo_blue); +#endif +} + +static void *video_logo(void) +{ + char info[128]; + int space, len; + __maybe_unused int y_off = 0; + __maybe_unused ulong addr; + __maybe_unused char *s; + + splash_get_pos(&video_logo_xpos, &video_logo_ypos); + +#ifdef CONFIG_SPLASH_SCREEN + s = getenv("splashimage"); + if (s != NULL) { + splash_screen_prepare(); + addr = simple_strtoul(s, NULL, 16); + + if (video_display_bitmap(addr, + video_logo_xpos, + video_logo_ypos) == 0) { + video_logo_height = 0; + return ((void *) (video_fb_address)); + } + } +#endif /* CONFIG_SPLASH_SCREEN */ + + logo_plot(video_fb_address, VIDEO_COLS, + video_logo_xpos, video_logo_ypos); + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN + /* + * when using splashpos for video_logo, skip any info + * output on video console if the logo is not at 0,0 + */ + if (video_logo_xpos || video_logo_ypos) { + /* + * video_logo_height is used in text and cursor offset + * calculations. Since the console is below the logo, + * we need to adjust the logo height + */ + if (video_logo_ypos == BMP_ALIGN_CENTER) + video_logo_height += max(0, (VIDEO_VISIBLE_ROWS - \ + VIDEO_LOGO_HEIGHT) / 2); + else if (video_logo_ypos > 0) + video_logo_height += video_logo_ypos; + + return video_fb_address + video_logo_height * VIDEO_LINE_LEN; + } +#endif + if (board_cfb_skip()) + return 0; + + sprintf(info, " %s", version_string); + + space = (VIDEO_LINE_LEN / 2 - VIDEO_INFO_X) / VIDEO_FONT_WIDTH; + len = strlen(info); + + if (len > space) { + video_drawchars(VIDEO_INFO_X, VIDEO_INFO_Y, + (uchar *) info, space); + video_drawchars(VIDEO_INFO_X + VIDEO_FONT_WIDTH, + VIDEO_INFO_Y + VIDEO_FONT_HEIGHT, + (uchar *) info + space, len - space); + y_off = 1; + } else + video_drawstring(VIDEO_INFO_X, VIDEO_INFO_Y, (uchar *) info); + +#ifdef CONFIG_CONSOLE_EXTRA_INFO + { + int i, n = + ((video_logo_height - + VIDEO_FONT_HEIGHT) / VIDEO_FONT_HEIGHT); + + for (i = 1; i < n; i++) { + video_get_info_str(i, info); + if (!*info) + continue; + + len = strlen(info); + if (len > space) { + video_drawchars(VIDEO_INFO_X, + VIDEO_INFO_Y + + (i + y_off) * + VIDEO_FONT_HEIGHT, + (uchar *) info, space); + y_off++; + video_drawchars(VIDEO_INFO_X + + VIDEO_FONT_WIDTH, + VIDEO_INFO_Y + + (i + y_off) * + VIDEO_FONT_HEIGHT, + (uchar *) info + space, + len - space); + } else { + video_drawstring(VIDEO_INFO_X, + VIDEO_INFO_Y + + (i + y_off) * + VIDEO_FONT_HEIGHT, + (uchar *) info); + } + } + } +#endif + + return (video_fb_address + video_logo_height * VIDEO_LINE_LEN); +} +#endif + +static int cfb_fb_is_in_dram(void) +{ + bd_t *bd = gd->bd; +#if defined(CONFIG_ARM) || defined(CONFIG_AVR32) || defined(COFNIG_NDS32) || \ +defined(CONFIG_SANDBOX) || defined(CONFIG_X86) + ulong start, end; + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { + start = bd->bi_dram[i].start; + end = bd->bi_dram[i].start + bd->bi_dram[i].size - 1; + if ((ulong)video_fb_address >= start && + (ulong)video_fb_address < end) + return 1; + } +#else + if ((ulong)video_fb_address >= bd->bi_memstart && + (ulong)video_fb_address < bd->bi_memstart + bd->bi_memsize) + return 1; +#endif + return 0; +} + +void video_clear(void) +{ + if (!video_fb_address) + return; +#ifdef VIDEO_HW_RECTFILL + video_hw_rectfill(VIDEO_PIXEL_SIZE, /* bytes per pixel */ + 0, /* dest pos x */ + 0, /* dest pos y */ + VIDEO_VISIBLE_COLS, /* frame width */ + VIDEO_VISIBLE_ROWS, /* frame height */ + bgx /* fill color */ + ); +#else + memsetl(video_fb_address, + (VIDEO_VISIBLE_ROWS * VIDEO_LINE_LEN) / sizeof(int), bgx); +#endif +} + +static int video_init(void) +{ + unsigned char color8; + + pGD = video_hw_init(); + if (pGD == NULL) + return -1; + + video_fb_address = (void *) VIDEO_FB_ADRS; +#ifdef CONFIG_VIDEO_HW_CURSOR + video_init_hw_cursor(VIDEO_FONT_WIDTH, VIDEO_FONT_HEIGHT); +#endif + + cfb_do_flush_cache = cfb_fb_is_in_dram() && dcache_status(); + + /* Init drawing pats */ + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + video_set_lut(0x01, CONSOLE_FG_COL, CONSOLE_FG_COL, + CONSOLE_FG_COL); + video_set_lut(0x00, CONSOLE_BG_COL, CONSOLE_BG_COL, + CONSOLE_BG_COL); + fgx = 0x01010101; + bgx = 0x00000000; + break; + case GDF__8BIT_332RGB: + color8 = ((CONSOLE_FG_COL & 0xe0) | + ((CONSOLE_FG_COL >> 3) & 0x1c) | + CONSOLE_FG_COL >> 6); + fgx = (color8 << 24) | (color8 << 16) | (color8 << 8) | + color8; + color8 = ((CONSOLE_BG_COL & 0xe0) | + ((CONSOLE_BG_COL >> 3) & 0x1c) | + CONSOLE_BG_COL >> 6); + bgx = (color8 << 24) | (color8 << 16) | (color8 << 8) | + color8; + break; + case GDF_15BIT_555RGB: + fgx = (((CONSOLE_FG_COL >> 3) << 26) | + ((CONSOLE_FG_COL >> 3) << 21) | + ((CONSOLE_FG_COL >> 3) << 16) | + ((CONSOLE_FG_COL >> 3) << 10) | + ((CONSOLE_FG_COL >> 3) << 5) | + (CONSOLE_FG_COL >> 3)); + bgx = (((CONSOLE_BG_COL >> 3) << 26) | + ((CONSOLE_BG_COL >> 3) << 21) | + ((CONSOLE_BG_COL >> 3) << 16) | + ((CONSOLE_BG_COL >> 3) << 10) | + ((CONSOLE_BG_COL >> 3) << 5) | + (CONSOLE_BG_COL >> 3)); + break; + case GDF_16BIT_565RGB: + fgx = (((CONSOLE_FG_COL >> 3) << 27) | + ((CONSOLE_FG_COL >> 2) << 21) | + ((CONSOLE_FG_COL >> 3) << 16) | + ((CONSOLE_FG_COL >> 3) << 11) | + ((CONSOLE_FG_COL >> 2) << 5) | + (CONSOLE_FG_COL >> 3)); + bgx = (((CONSOLE_BG_COL >> 3) << 27) | + ((CONSOLE_BG_COL >> 2) << 21) | + ((CONSOLE_BG_COL >> 3) << 16) | + ((CONSOLE_BG_COL >> 3) << 11) | + ((CONSOLE_BG_COL >> 2) << 5) | + (CONSOLE_BG_COL >> 3)); + break; + case GDF_32BIT_X888RGB: + fgx = (CONSOLE_FG_COL << 16) | + (CONSOLE_FG_COL << 8) | + CONSOLE_FG_COL; + bgx = (CONSOLE_BG_COL << 16) | + (CONSOLE_BG_COL << 8) | + CONSOLE_BG_COL; + break; + case GDF_24BIT_888RGB: + fgx = (CONSOLE_FG_COL << 24) | + (CONSOLE_FG_COL << 16) | + (CONSOLE_FG_COL << 8) | + CONSOLE_FG_COL; + bgx = (CONSOLE_BG_COL << 24) | + (CONSOLE_BG_COL << 16) | + (CONSOLE_BG_COL << 8) | + CONSOLE_BG_COL; + break; + } + eorx = fgx ^ bgx; + + video_clear(); + +#ifdef CONFIG_VIDEO_LOGO + /* Plot the logo and get start point of console */ + debug("Video: Drawing the logo ...\n"); + video_console_address = video_logo(); +#else + video_console_address = video_fb_address; +#endif + + /* Initialize the console */ + console_col = 0; + console_row = 0; + + if (cfb_do_flush_cache) + flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE); + + return 0; +} + +/* + * Implement a weak default function for boards that optionally + * need to skip the video initialization. + */ +int __board_video_skip(void) +{ + /* As default, don't skip test */ + return 0; +} + +int board_video_skip(void) + __attribute__ ((weak, alias("__board_video_skip"))); + +int drv_video_init(void) +{ + int skip_dev_init; + struct stdio_dev console_dev; + + /* Check if video initialization should be skipped */ + if (board_video_skip()) + return 0; + + /* Init video chip - returns with framebuffer cleared */ + skip_dev_init = (video_init() == -1); + + if (board_cfb_skip()) + return 0; + +#if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) + debug("KBD: Keyboard init ...\n"); + skip_dev_init |= (VIDEO_KBD_INIT_FCT == -1); +#endif + + if (skip_dev_init) + return 0; + + /* Init vga device */ + memset(&console_dev, 0, sizeof(console_dev)); + strcpy(console_dev.name, "vga"); + console_dev.ext = DEV_EXT_VIDEO; /* Video extensions */ + console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM; + console_dev.putc = video_putc; /* 'putc' function */ + console_dev.puts = video_puts; /* 'puts' function */ + console_dev.tstc = NULL; /* 'tstc' function */ + console_dev.getc = NULL; /* 'getc' function */ + +#if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) + /* Also init console device */ + console_dev.flags |= DEV_FLAGS_INPUT; + console_dev.tstc = VIDEO_TSTC_FCT; /* 'tstc' function */ + console_dev.getc = VIDEO_GETC_FCT; /* 'getc' function */ +#endif /* CONFIG_VGA_AS_SINGLE_DEVICE */ + + if (stdio_register(&console_dev) != 0) + return 0; + + /* Return success */ + return 1; +} + +void video_position_cursor(unsigned col, unsigned row) +{ + console_col = min(col, CONSOLE_COLS - 1); + console_row = min(row, CONSOLE_ROWS - 1); +} + +int video_get_pixel_width(void) +{ + return VIDEO_VISIBLE_COLS; +} + +int video_get_pixel_height(void) +{ + return VIDEO_VISIBLE_ROWS; +} + +int video_get_screen_rows(void) +{ + return CONSOLE_ROWS; +} + +int video_get_screen_columns(void) +{ + return CONSOLE_COLS; +} diff --git a/qemu/roms/u-boot/drivers/video/coreboot_fb.c b/qemu/roms/u-boot/drivers/video/coreboot_fb.c new file mode 100644 index 000000000..56c35c18f --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/coreboot_fb.c @@ -0,0 +1,85 @@ +/* + * coreboot Framebuffer driver. + * + * Copyright (C) 2011 The Chromium OS authors + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "videomodes.h" + +/* + * The Graphic Device + */ +GraphicDevice ctfb; + +static int parse_coreboot_table_fb(GraphicDevice *gdev) +{ + struct cb_framebuffer *fb = lib_sysinfo.framebuffer; + + /* If there is no framebuffer structure, bail out and keep + * running on the serial console. + */ + if (!fb) + return 0; + + gdev->winSizeX = fb->x_resolution; + gdev->winSizeY = fb->y_resolution; + + gdev->plnSizeX = fb->x_resolution; + gdev->plnSizeY = fb->y_resolution; + + gdev->gdfBytesPP = fb->bits_per_pixel / 8; + + switch (fb->bits_per_pixel) { + case 24: + gdev->gdfIndex = GDF_32BIT_X888RGB; + break; + case 16: + gdev->gdfIndex = GDF_16BIT_565RGB; + break; + default: + gdev->gdfIndex = GDF__8BIT_INDEX; + break; + } + + gdev->isaBase = CONFIG_SYS_ISA_IO_BASE_ADDRESS; + gdev->pciBase = (unsigned int)fb->physical_address; + + gdev->frameAdrs = (unsigned int)fb->physical_address; + gdev->memSize = fb->bytes_per_line * fb->y_resolution; + + gdev->vprBase = (unsigned int)fb->physical_address; + gdev->cprBase = (unsigned int)fb->physical_address; + + return 1; +} + +void *video_hw_init(void) +{ + GraphicDevice *gdev = &ctfb; + int bits_per_pixel; + + printf("Video: "); + + if (!parse_coreboot_table_fb(gdev)) { + printf("No video mode configured in coreboot!\n"); + return NULL; + } + + bits_per_pixel = gdev->gdfBytesPP * 8; + + /* fill in Graphic device struct */ + sprintf(gdev->modeIdent, "%dx%dx%d", gdev->winSizeX, gdev->winSizeY, + bits_per_pixel); + printf("%s\n", gdev->modeIdent); + + memset((void *)gdev->pciBase, 0, + gdev->winSizeX * gdev->winSizeY * gdev->gdfBytesPP); + + return (void *)gdev; +} diff --git a/qemu/roms/u-boot/drivers/video/ct69000.c b/qemu/roms/u-boot/drivers/video/ct69000.c new file mode 100644 index 000000000..168b9bad9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/ct69000.c @@ -0,0 +1,1267 @@ +/* ported from ctfb.c (linux kernel): + * Created in Jan - July 2000 by Thomas Höhenleitner + * + * Ported to U-Boot: + * (C) Copyright 2002 Denis Peter, MPL AG Switzerland + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#ifdef CONFIG_VIDEO + +#include +#include +#include "videomodes.h" + +/* debug */ +#undef VGA_DEBUG +#undef VGA_DUMP_REG +#ifdef VGA_DEBUG +#undef _DEBUG +#define _DEBUG 1 +#else +#undef _DEBUG +#define _DEBUG 0 +#endif + +/* Macros */ +#ifndef min +#define min( a, b ) ( ( a ) < ( b ) ) ? ( a ) : ( b ) +#endif +#ifndef max +#define max( a, b ) ( ( a ) > ( b ) ) ? ( a ) : ( b ) +#endif +#ifdef minmax +#error "term minmax already used." +#endif +#define minmax( a, x, b ) max( ( a ), min( ( x ), ( b ) ) ) +#define N_ELTS( x ) ( sizeof( x ) / sizeof( x[ 0 ] ) ) + +/* CT Register Offsets */ +#define CT_AR_O 0x3c0 /* Index and Data write port of the attribute Registers */ +#define CT_GR_O 0x3ce /* Index port of the Graphic Controller Registers */ +#define CT_SR_O 0x3c4 /* Index port of the Sequencer Controller */ +#define CT_CR_O 0x3d4 /* Index port of the CRT Controller */ +#define CT_XR_O 0x3d6 /* Extended Register index */ +#define CT_MSR_W_O 0x3c2 /* Misc. Output Register (write only) */ +#define CT_LUT_MASK_O 0x3c6 /* Color Palette Mask */ +#define CT_LUT_START_O 0x3c8 /* Color Palette Write Mode Index */ +#define CT_LUT_RGB_O 0x3c9 /* Color Palette Data Port */ +#define CT_STATUS_REG0_O 0x3c2 /* Status Register 0 (read only) */ +#define CT_STATUS_REG1_O 0x3da /* Input Status Register 1 (read only) */ + +#define CT_FP_O 0x3d0 /* Index port of the Flat panel Registers */ +#define CT_MR_O 0x3d2 /* Index Port of the Multimedia Extension */ + +/* defines for the memory mapped registers */ +#define BR00_o 0x400000 /* Source and Destination Span Register */ +#define BR01_o 0x400004 /* Pattern/Source Expansion Background Color & Transparency Key Register */ +#define BR02_o 0x400008 /* Pattern/Source Expansion Foreground Color Register */ +#define BR03_o 0x40000C /* Monochrome Source Control Register */ +#define BR04_o 0x400010 /* BitBLT Control Register */ +#define BR05_o 0x400014 /* Pattern Address Registe */ +#define BR06_o 0x400018 /* Source Address Register */ +#define BR07_o 0x40001C /* Destination Address Register */ +#define BR08_o 0x400020 /* Destination Width & Height Register */ +#define BR09_o 0x400024 /* Source Expansion Background Color & Transparency Key Register */ +#define BR0A_o 0x400028 /* Source Expansion Foreground Color Register */ + +#define CURSOR_SIZE 0x1000 /* in KByte for HW Cursor */ +#define PATTERN_ADR (pGD->dprBase + CURSOR_SIZE) /* pattern Memory after Cursor Memory */ +#define PATTERN_SIZE 8*8*4 /* 4 Bytes per Pixel 8 x 8 Pixel */ +#define ACCELMEMORY (CURSOR_SIZE + PATTERN_SIZE) /* reserved Memory for BITBlt and hw cursor */ + +/* Some Mode definitions */ +#define FB_SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */ +#define FB_SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */ +#define FB_SYNC_EXT 4 /* external sync */ +#define FB_SYNC_COMP_HIGH_ACT 8 /* composite sync high active */ +#define FB_SYNC_BROADCAST 16 /* broadcast video timings */ + /* vtotal = 144d/288n/576i => PAL */ + /* vtotal = 121d/242n/484i => NTSC */ +#define FB_SYNC_ON_GREEN 32 /* sync on green */ + +#define FB_VMODE_NONINTERLACED 0 /* non interlaced */ +#define FB_VMODE_INTERLACED 1 /* interlaced */ +#define FB_VMODE_DOUBLE 2 /* double scan */ +#define FB_VMODE_MASK 255 + +#define FB_VMODE_YWRAP 256 /* ywrap instead of panning */ +#define FB_VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */ +#define FB_VMODE_CONUPDATE 512 /* don't update x/yoffset */ + +#define text 0 +#define fntwidth 8 + +/* table for VGA Initialization */ +typedef struct { + const unsigned char reg; + const unsigned char val; +} CT_CFG_TABLE; + +/* this table provides some basic initialisations such as Memory Clock etc */ +static CT_CFG_TABLE xreg[] = { + {0x09, 0x01}, /* CRT Controller Extensions Enable */ + {0x0A, 0x02}, /* Frame Buffer Mapping */ + {0x0B, 0x01}, /* PCI Write Burst support */ + {0x20, 0x00}, /* BitBLT Configuration */ + {0x40, 0x03}, /* Memory Access Control */ + {0x60, 0x00}, /* Video Pin Control */ + {0x61, 0x00}, /* DPMS Synch control */ + {0x62, 0x00}, /* GPIO Pin Control */ + {0x63, 0xBD}, /* GPIO Pin Data */ + {0x67, 0x00}, /* Pin Tri-State */ + {0x80, 0x80}, /* Pixel Pipeline Config 0 register */ + {0xA0, 0x00}, /* Cursor 1 Control Reg */ + {0xA1, 0x00}, /* Cursor 1 Vertical Extension Reg */ + {0xA2, 0x00}, /* Cursor 1 Base Address Low */ + {0xA3, 0x00}, /* Cursor 1 Base Address High */ + {0xA4, 0x00}, /* Cursor 1 X-Position Low */ + {0xA5, 0x00}, /* Cursor 1 X-Position High */ + {0xA6, 0x00}, /* Cursor 1 Y-Position Low */ + {0xA7, 0x00}, /* Cursor 1 Y-Position High */ + {0xA8, 0x00}, /* Cursor 2 Control Reg */ + {0xA9, 0x00}, /* Cursor 2 Vertical Extension Reg */ + {0xAA, 0x00}, /* Cursor 2 Base Address Low */ + {0xAB, 0x00}, /* Cursor 2 Base Address High */ + {0xAC, 0x00}, /* Cursor 2 X-Position Low */ + {0xAD, 0x00}, /* Cursor 2 X-Position High */ + {0xAE, 0x00}, /* Cursor 2 Y-Position Low */ + {0xAF, 0x00}, /* Cursor 2 Y-Position High */ + {0xC0, 0x7D}, /* Dot Clock 0 VCO M-Divisor */ + {0xC1, 0x07}, /* Dot Clock 0 VCO N-Divisor */ + {0xC3, 0x34}, /* Dot Clock 0 Divisor select */ + {0xC4, 0x55}, /* Dot Clock 1 VCO M-Divisor */ + {0xC5, 0x09}, /* Dot Clock 1 VCO N-Divisor */ + {0xC7, 0x24}, /* Dot Clock 1 Divisor select */ + {0xC8, 0x7D}, /* Dot Clock 2 VCO M-Divisor */ + {0xC9, 0x07}, /* Dot Clock 2 VCO N-Divisor */ + {0xCB, 0x34}, /* Dot Clock 2 Divisor select */ + {0xCC, 0x38}, /* Memory Clock 0 VCO M-Divisor */ + {0xCD, 0x03}, /* Memory Clock 0 VCO N-Divisor */ + {0xCE, 0x90}, /* Memory Clock 0 Divisor select */ + {0xCF, 0x06}, /* Clock Config */ + {0xD0, 0x0F}, /* Power Down */ + {0xD1, 0x01}, /* Power Down BitBLT */ + {0xFF, 0xFF} /* end of table */ +}; +/* Clock Config: + * ============= + * + * PD Registers: + * ------------- + * Bit2 and Bit4..6 are used for the Loop Divisor and Post Divisor. + * They are encoded as follows: + * + * +---+--------------+ + * | 2 | Loop Divisor | + * +---+--------------+ + * | 1 | 1 | + * +---+--------------+ + * | 0 | 4 | + * +---+--------------+ + * Note: The Memory Clock does not have a Loop Divisor. + * +---+---+---+--------------+ + * | 6 | 5 | 4 | Post Divisor | + * +---+---+---+--------------+ + * | 0 | 0 | 0 | 1 | + * +---+---+---+--------------+ + * | 0 | 0 | 1 | 2 | + * +---+---+---+--------------+ + * | 0 | 1 | 0 | 4 | + * +---+---+---+--------------+ + * | 0 | 1 | 1 | 8 | + * +---+---+---+--------------+ + * | 1 | 0 | 0 | 16 | + * +---+---+---+--------------+ + * | 1 | 0 | 1 | 32 | + * +---+---+---+--------------+ + * | 1 | 1 | X | reserved | + * +---+---+---+--------------+ + * + * All other bits are reserved in these registers. + * + * Clock VCO M Registers: + * ---------------------- + * These Registers contain the M Value -2. + * + * Clock VCO N Registers: + * ---------------------- + * These Registers contain the N Value -2. + * + * Formulas: + * --------- + * Fvco = (Fref * Loop Divisor * M/N), whereas 100MHz < Fvco < 220MHz + * Fout = Fvco / Post Divisor + * + * Dot Clk0 (default 25MHz): + * ------------------------- + * Fvco = 14.318 * 127 / 9 = 202.045MHz + * Fout = 202.045MHz / 8 = 25.25MHz + * Post Divisor = 8 + * Loop Divisor = 1 + * XRC0 = (M - 2) = 125 = 0x7D + * XRC1 = (N - 2) = 7 = 0x07 + * XRC3 = 0x34 + * + * Dot Clk1 (default 28MHz): + * ------------------------- + * Fvco = 14.318 * 87 / 11 = 113.24MHz + * Fout = 113.24MHz / 4 = 28.31MHz + * Post Divisor = 4 + * Loop Divisor = 1 + * XRC4 = (M - 2) = 85 = 0x55 + * XRC5 = (N - 2) = 9 = 0x09 + * XRC7 = 0x24 + * + * Dot Clk2 (variable for extended modes set to 25MHz): + * ---------------------------------------------------- + * Fvco = 14.318 * 127 / 9 = 202.045MHz + * Fout = 202.045MHz / 8 = 25.25MHz + * Post Divisor = 8 + * Loop Divisor = 1 + * XRC8 = (M - 2) = 125 = 0x7D + * XRC9 = (N - 2) = 7 = 0x07 + * XRCB = 0x34 + * + * Memory Clk for most modes >50MHz: + * ---------------------------------- + * Fvco = 14.318 * 58 / 5 = 166MHz + * Fout = 166MHz / 2 = 83MHz + * Post Divisor = 2 + * XRCC = (M - 2) = 57 = 0x38 + * XRCD = (N - 2) = 3 = 0x03 + * XRCE = 0x90 + * + * Note Bit7 enables the clock source from the VCO + * + */ + +/******************************************************************* + * Chips struct + *******************************************************************/ +struct ctfb_chips_properties { + int device_id; /* PCI Device ID */ + unsigned long max_mem; /* memory for frame buffer */ + int vld_set; /* value of VLD if bit2 in clock control is set */ + int vld_not_set; /* value of VLD if bit2 in clock control is set */ + int mn_diff; /* difference between M/N Value + mn_diff = M/N Register */ + int mn_min; /* min value of M/N Value */ + int mn_max; /* max value of M/N Value */ + int vco_min; /* VCO Min in MHz */ + int vco_max; /* VCO Max in MHz */ +}; + +static const struct ctfb_chips_properties chips[] = { + {PCI_DEVICE_ID_CT_69000, 0x200000, 1, 4, -2, 3, 257, 100, 220}, +#ifdef CONFIG_USE_CPCIDVI + {PCI_DEVICE_ID_CT_69030, 0x400000, 1, 4, -2, 3, 257, 100, 220}, +#endif + {PCI_DEVICE_ID_CT_65555, 0x100000, 16, 4, 0, 1, 255, 48, 220}, /* NOT TESTED */ + {0, 0, 0, 0, 0, 0, 0, 0, 0} /* Terminator */ +}; + +/* + * The Graphic Device + */ +GraphicDevice ctfb; + +/******************************************************************************* +* +* Low Level Routines +*/ + +/******************************************************************************* +* +* Read CT ISA register +*/ +#ifdef VGA_DEBUG +static unsigned char +ctRead (unsigned short index) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + if (index == CT_AR_O) + /* synch the Flip Flop */ + in8 (pGD->isaBase + CT_STATUS_REG1_O); + + return (in8 (pGD->isaBase + index)); +} +#endif +/******************************************************************************* +* +* Write CT ISA register +*/ +static void +ctWrite (unsigned short index, unsigned char val) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + + out8 ((pGD->isaBase + index), val); +} + +/******************************************************************************* +* +* Read CT ISA register indexed +*/ +static unsigned char +ctRead_i (unsigned short index, char reg) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + if (index == CT_AR_O) + /* synch the Flip Flop */ + in8 (pGD->isaBase + CT_STATUS_REG1_O); + out8 ((pGD->isaBase + index), reg); + return (in8 (pGD->isaBase + index + 1)); +} + +/******************************************************************************* +* +* Write CT ISA register indexed +*/ +static void +ctWrite_i (unsigned short index, char reg, char val) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + if (index == CT_AR_O) { + /* synch the Flip Flop */ + in8 (pGD->isaBase + CT_STATUS_REG1_O); + out8 ((pGD->isaBase + index), reg); + out8 ((pGD->isaBase + index), val); + } else { + out8 ((pGD->isaBase + index), reg); + out8 ((pGD->isaBase + index + 1), val); + } +} + +/******************************************************************************* +* +* Write a table of CT ISA register +*/ +static void +ctLoadRegs (unsigned short index, CT_CFG_TABLE * regTab) +{ + while (regTab->reg != 0xFF) { + ctWrite_i (index, regTab->reg, regTab->val); + regTab++; + } +} + +/*****************************************************************************/ +static void +SetArRegs (void) +{ + int i, tmp; + + for (i = 0; i < 0x10; i++) + ctWrite_i (CT_AR_O, i, i); + if (text) + tmp = 0x04; + else + tmp = 0x41; + + ctWrite_i (CT_AR_O, 0x10, tmp); /* Mode Control Register */ + ctWrite_i (CT_AR_O, 0x11, 0x00); /* Overscan Color Register */ + ctWrite_i (CT_AR_O, 0x12, 0x0f); /* Memory Plane Enable Register */ + if (fntwidth == 9) + tmp = 0x08; + else + tmp = 0x00; + ctWrite_i (CT_AR_O, 0x13, tmp); /* Horizontal Pixel Panning */ + ctWrite_i (CT_AR_O, 0x14, 0x00); /* Color Select Register */ + ctWrite (CT_AR_O, 0x20); /* enable video */ +} + +/*****************************************************************************/ +static void +SetGrRegs (void) +{ /* Set Graphics Mode */ + int i; + + for (i = 0; i < 0x05; i++) + ctWrite_i (CT_GR_O, i, 0); + if (text) { + ctWrite_i (CT_GR_O, 0x05, 0x10); + ctWrite_i (CT_GR_O, 0x06, 0x02); + } else { + ctWrite_i (CT_GR_O, 0x05, 0x40); + ctWrite_i (CT_GR_O, 0x06, 0x05); + } + ctWrite_i (CT_GR_O, 0x07, 0x0f); + ctWrite_i (CT_GR_O, 0x08, 0xff); +} + +/*****************************************************************************/ +static void +SetSrRegs (void) +{ + int tmp = 0; + + ctWrite_i (CT_SR_O, 0x00, 0x00); /* reset */ + /*rr( sr, 0x01, tmp ); + if( fntwidth == 8 ) tmp |= 0x01; else tmp &= ~0x01; + wr( sr, 0x01, tmp ); */ + if (fntwidth == 8) + ctWrite_i (CT_SR_O, 0x01, 0x01); /* Clocking Mode Register */ + else + ctWrite_i (CT_SR_O, 0x01, 0x00); /* Clocking Mode Register */ + ctWrite_i (CT_SR_O, 0x02, 0x0f); /* Enable CPU wr access to given memory plane */ + ctWrite_i (CT_SR_O, 0x03, 0x00); /* Character Map Select Register */ + if (text) + tmp = 0x02; + else + tmp = 0x0e; + ctWrite_i (CT_SR_O, 0x04, tmp); /* Enable CPU accesses to the rest of the 256KB + total VGA memory beyond the first 64KB and set + fb mapping mode. */ + ctWrite_i (CT_SR_O, 0x00, 0x03); /* enable */ +} + +/*****************************************************************************/ +static void +SetBitsPerPixelIntoXrRegs (int bpp) +{ + unsigned int n = (bpp >> 3), tmp; /* only for 15, 8, 16, 24 bpp */ + static char md[4] = { 0x04, 0x02, 0x05, 0x06 }; /* DisplayColorMode */ + static char off[4] = { ~0x20, ~0x30, ~0x20, ~0x10 }; /* mask */ + static char on[4] = { 0x10, 0x00, 0x10, 0x20 }; /* mask */ + if (bpp == 15) + n = 0; + tmp = ctRead_i (CT_XR_O, 0x20); + tmp &= off[n]; + tmp |= on[n]; + ctWrite_i (CT_XR_O, 0x20, tmp); /* BitBLT Configuration */ + ctWrite_i (CT_XR_O, 0x81, md[n]); +} + +/*****************************************************************************/ +static void +SetCrRegs (struct ctfb_res_modes *var, int bits_per_pixel) +{ /* he -le- ht|0 hd -ri- hs -h- he */ + unsigned char cr[0x7a]; + int i, tmp; + unsigned int hd, hs, he, ht, hbe; /* Horizontal. */ + unsigned int vd, vs, ve, vt; /* vertical */ + unsigned int bpp, wd, dblscan, interlaced, bcast, CrtHalfLine; + unsigned int CompSyncCharClkDelay, CompSyncPixelClkDelay; + unsigned int NTSC_PAL_HorizontalPulseWidth, BlDelayCtrl; + unsigned int HorizontalEqualizationPulses; + unsigned int HorizontalSerration1Start, HorizontalSerration2Start; + + const int LineCompare = 0x3ff; + unsigned int TextScanLines = 1; /* this is in fact a vertical zoom factor */ + unsigned int RAMDAC_BlankPedestalEnable = 0; /* 1=en-, 0=disable, see XR82 */ + + hd = (var->xres) / 8; /* HDisp. */ + hs = (var->xres + var->right_margin) / 8; /* HsStrt */ + he = (var->xres + var->right_margin + var->hsync_len) / 8; /* HsEnd */ + ht = (var->left_margin + var->xres + var->right_margin + var->hsync_len) / 8; /* HTotal */ + hbe = ht - 1; /* HBlankEnable todo docu wants ht here, but it does not work */ + /* ve -up- vt|0 vd -lo- vs -v- ve */ + vd = var->yres; /* VDisplay */ + vs = var->yres + var->lower_margin; /* VSyncStart */ + ve = var->yres + var->lower_margin + var->vsync_len; /* VSyncEnd */ + vt = var->upper_margin + var->yres + var->lower_margin + var->vsync_len; /* VTotal */ + bpp = bits_per_pixel; + dblscan = (var->vmode & FB_VMODE_DOUBLE) ? 1 : 0; + interlaced = var->vmode & FB_VMODE_INTERLACED; + bcast = var->sync & FB_SYNC_BROADCAST; + CrtHalfLine = bcast ? (hd >> 1) : 0; + BlDelayCtrl = bcast ? 1 : 0; + CompSyncCharClkDelay = 0; /* 2 bit */ + CompSyncPixelClkDelay = 0; /* 3 bit */ + if (bcast) { + NTSC_PAL_HorizontalPulseWidth = 7; /*( var->hsync_len >> 1 ) + 1 */ + HorizontalEqualizationPulses = 0; /* inverse value */ + HorizontalSerration1Start = 31; /* ( ht >> 1 ) */ + HorizontalSerration2Start = 89; /* ( ht >> 1 ) */ + } else { + NTSC_PAL_HorizontalPulseWidth = 0; + /* 4 bit: hsync pulse width = ( ( CR74[4:0] - CR74[5] ) + * / 2 ) + 1 --> CR74[4:0] = 2*(hs-1) + CR74[5] */ + HorizontalEqualizationPulses = 1; /* inverse value */ + HorizontalSerration1Start = 0; /* ( ht >> 1 ) */ + HorizontalSerration2Start = 0; /* ( ht >> 1 ) */ + } + + if (bpp == 15) + bpp = 16; + wd = var->xres * bpp / 64; /* double words per line */ + if (interlaced) { /* we divide all vertical timings, exept vd */ + vs >>= 1; + ve >>= 1; + vt >>= 1; + } + memset (cr, 0, sizeof (cr)); + cr[0x00] = 0xff & (ht - 5); + cr[0x01] = hd - 1; /* soll:4f ist 59 */ + cr[0x02] = hd; + cr[0x03] = (hbe & 0x1F) | 0x80; /* hd + ht - hd */ + cr[0x04] = hs; + cr[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f); + cr[0x06] = (vt - 2) & 0xFF; + cr[0x30] = (vt - 2) >> 8; + cr[0x07] = ((vt & 0x100) >> 8) + | ((vd & 0x100) >> 7) + | ((vs & 0x100) >> 6) + | ((vs & 0x100) >> 5) + | ((LineCompare & 0x100) >> 4) + | ((vt & 0x200) >> 4) + | ((vd & 0x200) >> 3) + | ((vs & 0x200) >> 2); + cr[0x08] = 0x00; + cr[0x09] = (dblscan << 7) + | ((LineCompare & 0x200) >> 3) + | ((vs & 0x200) >> 4) + | (TextScanLines - 1); + cr[0x10] = vs & 0xff; /* VSyncPulseStart */ + cr[0x32] = (vs & 0xf00) >> 8; /* VSyncPulseStart */ + cr[0x11] = (ve & 0x0f); /* | 0x20; */ + cr[0x12] = (vd - 1) & 0xff; /* LineCount */ + cr[0x31] = ((vd - 1) & 0xf00) >> 8; /* LineCount */ + cr[0x13] = wd & 0xff; + cr[0x41] = (wd & 0xf00) >> 8; + cr[0x15] = vs & 0xff; + cr[0x33] = (vs & 0xf00) >> 8; + cr[0x38] = (0x100 & (ht - 5)) >> 8; + cr[0x3C] = 0xc0 & hbe; + cr[0x16] = (vt - 1) & 0xff; /* vbe - docu wants vt here, */ + cr[0x17] = 0xe3; /* but it does not work */ + cr[0x18] = 0xff & LineCompare; + cr[0x22] = 0xff; /* todo? */ + cr[0x70] = interlaced ? (0x80 | CrtHalfLine) : 0x00; /* check:0xa6 */ + cr[0x71] = 0x80 | (RAMDAC_BlankPedestalEnable << 6) + | (BlDelayCtrl << 5) + | ((0x03 & CompSyncCharClkDelay) << 3) + | (0x07 & CompSyncPixelClkDelay); /* todo: see XR82 */ + cr[0x72] = HorizontalSerration1Start; + cr[0x73] = HorizontalSerration2Start; + cr[0x74] = (HorizontalEqualizationPulses << 5) + | NTSC_PAL_HorizontalPulseWidth; + /* todo: ct69000 has also 0x75-79 */ + /* now set the registers */ + for (i = 0; i <= 0x0d; i++) { /*CR00 .. CR0D */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + for (i = 0x10; i <= 0x18; i++) { /*CR10 .. CR18 */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + i = 0x22; /*CR22 */ + ctWrite_i (CT_CR_O, i, cr[i]); + for (i = 0x30; i <= 0x33; i++) { /*CR30 .. CR33 */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + i = 0x38; /*CR38 */ + ctWrite_i (CT_CR_O, i, cr[i]); + i = 0x3C; /*CR3C */ + ctWrite_i (CT_CR_O, i, cr[i]); + for (i = 0x40; i <= 0x41; i++) { /*CR40 .. CR41 */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + for (i = 0x70; i <= 0x74; i++) { /*CR70 .. CR74 */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + tmp = ctRead_i (CT_CR_O, 0x40); + tmp &= 0x0f; + tmp |= 0x80; + ctWrite_i (CT_CR_O, 0x40, tmp); /* StartAddressEnable */ +} + +/* pixelclock control */ + +/***************************************************************************** + We have a rational number p/q and need an m/n which is very close to p/q + but has m and n within mnmin and mnmax. We have no floating point in the + kernel. We can use long long without divide. And we have time to compute... +******************************************************************************/ +static unsigned int +FindBestPQFittingMN (unsigned int p, unsigned int q, unsigned int mnmin, + unsigned int mnmax, unsigned int *pm, unsigned int *pn) +{ + /* this code is not for general purpose usable but good for our number ranges */ + unsigned int n = mnmin, m = 0; + long long int L = 0, P = p, Q = q, H = P >> 1; + long long int D = 0x7ffffffffffffffLL; + for (n = mnmin; n <= mnmax; n++) { + m = mnmin; /* p/q ~ m/n -> p*n ~ m*q -> p*n-x*q ~ 0 */ + L = P * n - m * Q; /* n * vco - m * fref should be near 0 */ + while (L > 0 && m < mnmax) { + L -= q; /* difference is greater as 0 subtract fref */ + m++; /* and increment m */ + } + /* difference is less or equal than 0 or m > maximum */ + if (m > mnmax) + break; /* no solution: if we increase n we get the same situation */ + /* L is <= 0 now */ + if (-L > H && m > mnmin) { /* if difference > the half fref */ + L += q; /* we take the situation before */ + m--; /* because its closer to 0 */ + } + L = (L < 0) ? -L : +L; /* absolute value */ + if (D < L) /* if last difference was better take next n */ + continue; + D = L; + *pm = m; + *pn = n; /* keep improved data */ + if (D == 0) + break; /* best result we can get */ + } + return (unsigned int) (0xffffffff & D); +} + +/* that is the hardware < 69000 we have to manage + +---------+ +-------------------+ +----------------------+ +--+ + | REFCLK |__|NTSC Divisor Select|__|FVCO Reference Divisor|__|÷N|__ + | 14.3MHz | |(NTSCDS) (÷1, ÷5) | |Select (RDS) (÷1, ÷4) | | | | + +---------+ +-------------------+ +----------------------+ +--+ | + ___________________________________________________________________| + | + | fvco fout + | +--------+ +------------+ +-----+ +-------------------+ +----+ + +-| Phase |__|Charge Pump |__| VCO |_____|Post Divisor (PD) |___|CLK |---> + +-| Detect | |& Filter VCO| | | | |÷1, 2, 4, 8, 16, 32| | | + | +--------+ +------------+ +-----+ | +-------------------+ +----+ + | | + | +--+ +---------------+ | + |____|÷M|___|VCO Loop Divide|__________| + | | |(VLD)(÷4, ÷16) | + +--+ +---------------+ +**************************************************************************** + that is the hardware >= 69000 we have to manage + +---------+ +--+ + | REFCLK |__|÷N|__ + | 14.3MHz | | | | + +---------+ +--+ | + __________________| + | + | fvco fout + | +--------+ +------------+ +-----+ +-------------------+ +----+ + +-| Phase |__|Charge Pump |__| VCO |_____|Post Divisor (PD) |___|CLK |---> + +-| Detect | |& Filter VCO| | | | |÷1, 2, 4, 8, 16, 32| | | + | +--------+ +------------+ +-----+ | +-------------------+ +----+ + | | + | +--+ +---------------+ | + |____|÷M|___|VCO Loop Divide|__________| + | | |(VLD)(÷1, ÷4) | + +--+ +---------------+ + + +*/ + +#define VIDEO_FREF 14318180; /* Hz */ +/*****************************************************************************/ +static int +ReadPixClckFromXrRegsBack (struct ctfb_chips_properties *param) +{ + unsigned int m, n, vld, pd, PD, fref, xr_cb, i, pixclock; + i = 0; + pixclock = -1; + fref = VIDEO_FREF; + m = ctRead_i (CT_XR_O, 0xc8); + n = ctRead_i (CT_XR_O, 0xc9); + m -= param->mn_diff; + n -= param->mn_diff; + xr_cb = ctRead_i (CT_XR_O, 0xcb); + PD = (0x70 & xr_cb) >> 4; + pd = 1; + for (i = 0; i < PD; i++) { + pd *= 2; + } + vld = (0x04 & xr_cb) ? param->vld_set : param->vld_not_set; + if (n * vld * m) { + unsigned long long p = 1000000000000LL * pd * n; + unsigned long long q = (long long) fref * vld * m; + while ((p > 0xffffffffLL) || (q > 0xffffffffLL)) { + p >>= 1; /* can't divide with long long so we scale down */ + q >>= 1; + } + pixclock = (unsigned) p / (unsigned) q; + } else + printf ("Invalid data in xr regs.\n"); + return pixclock; +} + +/*****************************************************************************/ +static void +FindAndSetPllParamIntoXrRegs (unsigned int pixelclock, + struct ctfb_chips_properties *param) +{ + unsigned int m, n, vld, pd, PD, fref, xr_cb; + unsigned int fvcomin, fvcomax, pclckmin, pclckmax, pclk; + unsigned int pfreq, fvco, new_pixclock; + unsigned int D,nback,mback; + + fref = VIDEO_FREF; + pd = 1; + PD = 0; + fvcomin = param->vco_min; + fvcomax = param->vco_max; /* MHz */ + pclckmin = 1000000 / fvcomax + 1; /* 4546 */ + pclckmax = 32000000 / fvcomin - 1; /* 666665 */ + pclk = minmax (pclckmin, pixelclock, pclckmax); /* ps pp */ + pfreq = 250 * (4000000000U / pclk); + fvco = pfreq; /* Hz */ + new_pixclock = 0; + while (fvco < fvcomin * 1000000) { + /* double VCO starting with the pixelclock frequency + * as long as it is lower than the minimal VCO frequency */ + fvco *= 2; + pd *= 2; + PD++; + } + /* fvco is exactly pd * pixelclock and higher than the ninmal VCO frequency */ + /* first try */ + vld = param->vld_set; + D=FindBestPQFittingMN (fvco / vld, fref, param->mn_min, param->mn_max, &m, &n); /* rds = 1 */ + mback=m; + nback=n; + /* second try */ + vld = param->vld_not_set; + if(Dmn_min, param->mn_max, &m, &n)) { /* rds = 1 */ + /* first try was better */ + m=mback; + n=nback; + vld = param->vld_set; + } + m += param->mn_diff; + n += param->mn_diff; + debug("VCO %d, pd %d, m %d n %d vld %d\n", fvco, pd, m, n, vld); + xr_cb = ((0x7 & PD) << 4) | (vld == param->vld_set ? 0x04 : 0); + /* All four of the registers used for dot clock 2 (XRC8 - XRCB) must be + * written, and in order from XRC8 to XRCB, before the hardware will + * update the synthesizer s settings. + */ + ctWrite_i (CT_XR_O, 0xc8, m); + ctWrite_i (CT_XR_O, 0xc9, n); /* xrca does not exist in CT69000 and CT69030 */ + ctWrite_i (CT_XR_O, 0xca, 0); /* because of a hw bug I guess, but we write */ + ctWrite_i (CT_XR_O, 0xcb, xr_cb); /* 0 to it for savety */ + new_pixclock = ReadPixClckFromXrRegsBack (param); + debug("pixelclock.set = %d, pixelclock.real = %d\n", + pixelclock, new_pixclock); +} + +/*****************************************************************************/ +static void +SetMsrRegs (struct ctfb_res_modes *mode) +{ + unsigned char h_synch_high, v_synch_high; + + h_synch_high = (mode->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x40; /* horizontal Synch High active */ + v_synch_high = (mode->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x80; /* vertical Synch High active */ + ctWrite (CT_MSR_W_O, (h_synch_high | v_synch_high | 0x29)); + /* upper64K==0x20, CLC2select==0x08, RAMenable==0x02!(todo), CGA==0x01 + * Selects the upper 64KB page.Bit5=1 + * CLK2 (left reserved in standard VGA) Bit3|2=1|0 + * Disables CPU access to frame buffer. Bit1=0 + * Sets the I/O address decode for ST01, FCR, and all CR registers + * to the 3Dx I/O address range (CGA emulation). Bit0=1 + */ +} + +/************************************************************************************/ +#ifdef VGA_DUMP_REG + +static void +ctDispRegs (unsigned short index, int from, int to) +{ + unsigned char status; + int i; + + for (i = from; i < to; i++) { + status = ctRead_i (index, i); + printf ("%02X: is %02X\n", i, status); + } +} + +void +video_dump_reg (void) +{ + int i; + + printf ("Extended Regs:\n"); + ctDispRegs (CT_XR_O, 0, 0xC); + ctDispRegs (CT_XR_O, 0xe, 0xf); + ctDispRegs (CT_XR_O, 0x20, 0x21); + ctDispRegs (CT_XR_O, 0x40, 0x50); + ctDispRegs (CT_XR_O, 0x60, 0x64); + ctDispRegs (CT_XR_O, 0x67, 0x68); + ctDispRegs (CT_XR_O, 0x70, 0x72); + ctDispRegs (CT_XR_O, 0x80, 0x83); + ctDispRegs (CT_XR_O, 0xA0, 0xB0); + ctDispRegs (CT_XR_O, 0xC0, 0xD3); + printf ("Sequencer Regs:\n"); + ctDispRegs (CT_SR_O, 0, 0x8); + printf ("Graphic Regs:\n"); + ctDispRegs (CT_GR_O, 0, 0x9); + printf ("CRT Regs:\n"); + ctDispRegs (CT_CR_O, 0, 0x19); + ctDispRegs (CT_CR_O, 0x22, 0x23); + ctDispRegs (CT_CR_O, 0x30, 0x34); + ctDispRegs (CT_CR_O, 0x38, 0x39); + ctDispRegs (CT_CR_O, 0x3C, 0x3D); + ctDispRegs (CT_CR_O, 0x40, 0x42); + ctDispRegs (CT_CR_O, 0x70, 0x80); + /* don't display the attributes */ +} + +#endif + +#ifdef CONFIG_VIDEO_HW_CURSOR +/*************************************************************** + * Set Hardware Cursor in Pixel + */ +void +video_set_hw_cursor (int x, int y) +{ + int sig_x = 0, sig_y = 0; + if (x < 0) { + x *= -1; + sig_x = 1; + } + if (y < 0) { + y *= -1; + sig_y = 1; + } + ctWrite_i (CT_XR_O, 0xa4, x & 0xff); + ctWrite_i (CT_XR_O, 0xa5, (x >> 8) & 0x7); + ctWrite_i (CT_XR_O, 0xa6, y & 0xff); + ctWrite_i (CT_XR_O, 0xa7, (y >> 8) & 0x7); +} + +/*************************************************************** + * Init Hardware Cursor. To know the size of the Cursor, + * we have to know the Font size. + */ +void +video_init_hw_cursor (int font_width, int font_height) +{ + unsigned char xr_80; + unsigned long *curs, pattern; + int i; + int cursor_start; + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + + cursor_start = pGD->dprBase; + xr_80 = ctRead_i (CT_XR_O, 0x80); + /* set start address */ + ctWrite_i (CT_XR_O, 0xa2, (cursor_start >> 8) & 0xf0); + ctWrite_i (CT_XR_O, 0xa3, (cursor_start >> 16) & 0x3f); + /* set cursor shape */ + curs = (unsigned long *) cursor_start; + i = 0; + while (i < 0x400) { + curs[i++] = 0xffffffff; /* AND mask */ + curs[i++] = 0xffffffff; /* AND mask */ + curs[i++] = 0; /* XOR mask */ + curs[i++] = 0; /* XOR mask */ + /* Transparent */ + } + pattern = 0xffffffff >> font_width; + i = 0; + while (i < (font_height * 2)) { + curs[i++] = pattern; /* AND mask */ + curs[i++] = pattern; /* AND mask */ + curs[i++] = 0; /* XOR mask */ + curs[i++] = 0; /* XOR mask */ + /* Cursor Color 0 */ + } + /* set blink rate */ + ctWrite_i (CT_FP_O, 0x19, 0xf); + + /* set cursors colors */ + xr_80 = ctRead_i (CT_XR_O, 0x80); + xr_80 |= 0x1; /* alternate palette select */ + ctWrite_i (CT_XR_O, 0x80, xr_80); + video_set_lut (4, CONSOLE_FG_COL, CONSOLE_FG_COL, CONSOLE_FG_COL); + /* position 4 is color 0 cursor 0 */ + xr_80 &= 0xfe; /* normal palette select */ + ctWrite_i (CT_XR_O, 0x80, xr_80); + /* cursor enable */ + ctWrite_i (CT_XR_O, 0xa0, 0x91); + xr_80 |= 0x10; /* enable hwcursor */ + ctWrite_i (CT_XR_O, 0x80, xr_80); + video_set_hw_cursor (0, 0); +} +#endif /* CONFIG_VIDEO_HW_CURSOR */ + +/*************************************************************** + * Wait for BitBlt ready + */ +static int +video_wait_bitblt (unsigned long addr) +{ + unsigned long br04; + int i = 0; + br04 = in32r (addr); + while (br04 & 0x80000000) { + udelay (1); + br04 = in32r (addr); + if (i++ > 1000000) { + printf ("ERROR Timeout %lx\n", br04); + return 1; + } + } + return 0; +} + +/*************************************************************** + * Set up BitBlt Registrs + */ +static void +SetDrawingEngine (int bits_per_pixel) +{ + unsigned long br04, br00; + unsigned char tmp; + + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + + tmp = ctRead_i (CT_XR_O, 0x20); /* BitBLT Configuration */ + tmp |= 0x02; /* reset BitBLT */ + ctWrite_i (CT_XR_O, 0x20, tmp); /* BitBLT Configuration */ + udelay (10); + tmp &= 0xfd; /* release reset BitBLT */ + ctWrite_i (CT_XR_O, 0x20, tmp); /* BitBLT Configuration */ + video_wait_bitblt (pGD->pciBase + BR04_o); + + /* set pattern Address */ + out32r (pGD->pciBase + BR05_o, PATTERN_ADR & 0x003ffff8); + br04 = 0; + if (bits_per_pixel == 1) { + br04 |= 0x00040000; /* monochome Pattern */ + br04 |= 0x00001000; /* monochome source */ + } + br00 = ((pGD->winSizeX * pGD->gdfBytesPP) << 16) + (pGD->winSizeX * pGD->gdfBytesPP); /* bytes per scanline */ + out32r (pGD->pciBase + BR00_o, br00); /* */ + out32r (pGD->pciBase + BR08_o, (10 << 16) + 10); /* dummy */ + out32r (pGD->pciBase + BR04_o, br04); /* write all 0 */ + out32r (pGD->pciBase + BR07_o, 0); /* destination */ + video_wait_bitblt (pGD->pciBase + BR04_o); +} + +/**************************************************************************** +* supported Video Chips +*/ +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_69000}, +#ifdef CONFIG_USE_CPCIDVI + {PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_69030}, +#endif + {} +}; + +/******************************************************************************* +* +* Init video chip +*/ +void * +video_hw_init (void) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + unsigned short device_id; + pci_dev_t devbusfn; + int videomode; + unsigned long t1, hsynch, vsynch; + unsigned int pci_mem_base, *vm; + int tmp, i, bits_per_pixel; + char *penv; + struct ctfb_res_modes *res_mode; + struct ctfb_res_modes var_mode; + struct ctfb_chips_properties *chips_param; + /* Search for video chip */ + + if ((devbusfn = pci_find_devices (supported, 0)) < 0) { +#ifdef CONFIG_VIDEO_ONBOARD + printf ("Video: Controller not found !\n"); +#endif + return (NULL); + } + + /* PCI setup */ + pci_write_config_dword (devbusfn, PCI_COMMAND, + (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)); + pci_read_config_word (devbusfn, PCI_DEVICE_ID, &device_id); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base); + pci_mem_base = pci_mem_to_phys (devbusfn, pci_mem_base); + + /* get chips params */ + for (chips_param = (struct ctfb_chips_properties *) &chips[0]; + chips_param->device_id != 0; chips_param++) { + if (chips_param->device_id == device_id) + break; + } + if (chips_param->device_id == 0) { +#ifdef CONFIG_VIDEO_ONBOARD + printf ("Video: controller 0x%X not supported\n", device_id); +#endif + return NULL; + } + /* supported Video controller found */ + printf ("Video: "); + + tmp = 0; + videomode = 0x301; + /* get video mode via environment */ + if ((penv = getenv ("videomode")) != NULL) { + /* deceide if it is a string */ + if (penv[0] <= '9') { + videomode = (int) simple_strtoul (penv, NULL, 16); + tmp = 1; + } + } else { + tmp = 1; + } + if (tmp) { + /* parameter are vesa modes */ + /* search params */ + for (i = 0; i < VESA_MODES_COUNT; i++) { + if (vesa_modes[i].vesanr == videomode) + break; + } + if (i == VESA_MODES_COUNT) { + printf ("no VESA Mode found, switching to mode 0x301 "); + i = 0; + } + res_mode = + (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i]. + resindex]; + bits_per_pixel = vesa_modes[i].bits_per_pixel; + } else { + + res_mode = (struct ctfb_res_modes *) &var_mode; + bits_per_pixel = video_get_params (res_mode, penv); + } + + /* calculate available color depth for controller memory */ + if (bits_per_pixel == 15) + tmp = 2; + else + tmp = bits_per_pixel >> 3; /* /8 */ + if (((chips_param->max_mem - + ACCELMEMORY) / (res_mode->xres * res_mode->yres)) < tmp) { + tmp = + ((chips_param->max_mem - + ACCELMEMORY) / (res_mode->xres * res_mode->yres)); + if (tmp == 0) { + printf + ("No matching videomode found .-> reduce resolution\n"); + return NULL; + } else { + printf ("Switching back to %d Bits per Pixel ", + tmp << 3); + bits_per_pixel = tmp << 3; + } + } + + /* calculate hsynch and vsynch freq (info only) */ + t1 = (res_mode->left_margin + res_mode->xres + + res_mode->right_margin + res_mode->hsync_len) / 8; + t1 *= 8; + t1 *= res_mode->pixclock; + t1 /= 1000; + hsynch = 1000000000L / t1; + t1 *= + (res_mode->upper_margin + res_mode->yres + + res_mode->lower_margin + res_mode->vsync_len); + t1 /= 1000; + vsynch = 1000000000L / t1; + + /* fill in Graphic device struct */ + sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres, + res_mode->yres, bits_per_pixel, (hsynch / 1000), + (vsynch / 1000)); + printf ("%s\n", pGD->modeIdent); + pGD->winSizeX = res_mode->xres; + pGD->winSizeY = res_mode->yres; + pGD->plnSizeX = res_mode->xres; + pGD->plnSizeY = res_mode->yres; + switch (bits_per_pixel) { + case 8: + pGD->gdfBytesPP = 1; + pGD->gdfIndex = GDF__8BIT_INDEX; + break; + case 15: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_15BIT_555RGB; + break; + case 16: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_16BIT_565RGB; + break; + case 24: + pGD->gdfBytesPP = 3; + pGD->gdfIndex = GDF_24BIT_888RGB; + break; + } + pGD->isaBase = CONFIG_SYS_ISA_IO_BASE_ADDRESS; + pGD->pciBase = pci_mem_base; + pGD->frameAdrs = pci_mem_base; + pGD->memSize = chips_param->max_mem; + /* Cursor Start Address */ + pGD->dprBase = + (pGD->winSizeX * pGD->winSizeY * pGD->gdfBytesPP) + pci_mem_base; + if ((pGD->dprBase & 0x0fff) != 0) { + /* allign it */ + pGD->dprBase &= 0xfffff000; + pGD->dprBase += 0x00001000; + } + debug("Cursor Start %x Pattern Start %x\n", pGD->dprBase, + PATTERN_ADR); + pGD->vprBase = pci_mem_base; /* Dummy */ + pGD->cprBase = pci_mem_base; /* Dummy */ + /* set up Hardware */ + +#ifdef CONFIG_USE_CPCIDVI + if (device_id == PCI_DEVICE_ID_CT_69030) { + ctWrite (CT_MSR_W_O, 0x0b); + ctWrite (0x3cd, 0x13); + ctWrite_i (CT_FP_O, 0x02, 0x00); + ctWrite_i (CT_FP_O, 0x05, 0x00); + ctWrite_i (CT_FP_O, 0x06, 0x00); + ctWrite (0x3c2, 0x0b); + ctWrite_i (CT_FP_O, 0x02, 0x10); + ctWrite_i (CT_FP_O, 0x01, 0x09); + } else { + ctWrite (CT_MSR_W_O, 0x01); + } +#else + ctWrite (CT_MSR_W_O, 0x01); +#endif + + /* set the extended Registers */ + ctLoadRegs (CT_XR_O, xreg); + /* set atribute registers */ + SetArRegs (); + /* set Graphics register */ + SetGrRegs (); + /* set sequencer */ + SetSrRegs (); + + /* set msr */ + SetMsrRegs (res_mode); + + /* set CRT Registers */ + SetCrRegs (res_mode, bits_per_pixel); + /* set color mode */ + SetBitsPerPixelIntoXrRegs (bits_per_pixel); + + /* set PLL */ + FindAndSetPllParamIntoXrRegs (res_mode->pixclock, chips_param); + + ctWrite_i (CT_SR_O, 0, 0x03); /* clear synchronous reset */ + /* Clear video memory */ + i = pGD->memSize / 4; + vm = (unsigned int *) pGD->pciBase; + while (i--) + *vm++ = 0; + SetDrawingEngine (bits_per_pixel); +#ifdef VGA_DUMP_REG + video_dump_reg (); +#endif + + return ((void *) &ctfb); +} + + /******************************************************************************* +* +* Set a RGB color in the LUT (8 bit index) +*/ +void +video_set_lut (unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + + ctWrite (CT_LUT_MASK_O, 0xff); + + ctWrite (CT_LUT_START_O, (char) index); + + ctWrite (CT_LUT_RGB_O, r); /* red */ + ctWrite (CT_LUT_RGB_O, g); /* green */ + ctWrite (CT_LUT_RGB_O, b); /* blue */ + udelay (1); + ctWrite (CT_LUT_MASK_O, 0xff); +} + +/******************************************************************************* +* +* Drawing engine fill on screen region +*/ +void +video_hw_rectfill (unsigned int bpp, /* bytes per pixel */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y, /* frame height */ + unsigned int color /* fill color */ + ) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + unsigned long *p, br04; + + video_wait_bitblt (pGD->pciBase + BR04_o); + + p = (unsigned long *) PATTERN_ADR; + dim_x *= bpp; + if (bpp == 3) + bpp++; /* 24Bit needs a 32bit pattern */ + memset (p, color, (bpp * sizeof (unsigned char) * 8 * 8)); /* 8 x 8 pattern data */ + out32r (pGD->pciBase + BR07_o, ((pGD->winSizeX * dst_y) + dst_x) * pGD->gdfBytesPP); /* destination */ + br04 = in32r (pGD->pciBase + BR04_o) & 0xffffff00; + br04 |= 0xF0; /* write Pattern P -> D */ + out32r (pGD->pciBase + BR04_o, br04); /* */ + out32r (pGD->pciBase + BR08_o, (dim_y << 16) + dim_x); /* starts the BITBlt */ + video_wait_bitblt (pGD->pciBase + BR04_o); +} + +/******************************************************************************* +* +* Drawing engine bitblt with screen region +*/ +void +video_hw_bitblt (unsigned int bpp, /* bytes per pixel */ + unsigned int src_x, /* source pos x */ + unsigned int src_y, /* source pos y */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y /* frame height */ + ) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + unsigned long br04; + + br04 = in32r (pGD->pciBase + BR04_o); + + /* to prevent data corruption due to overlap, we have to + * find out if, and how the frames overlaps */ + if (src_x < dst_x) { + /* src is more left than dest + * the frame may overlap -> start from right to left */ + br04 |= 0x00000100; /* set bit 8 */ + src_x += dim_x; + dst_x += dim_x; + } else { + br04 &= 0xfffffeff; /* clear bit 8 left to right */ + } + if (src_y < dst_y) { + /* src is higher than dst + * the frame may overlap => start from bottom */ + br04 |= 0x00000200; /* set bit 9 */ + src_y += dim_y; + dst_y += dim_y; + } else { + br04 &= 0xfffffdff; /* clear bit 9 top to bottom */ + } + dim_x *= bpp; + out32r (pGD->pciBase + BR06_o, ((pGD->winSizeX * src_y) + src_x) * pGD->gdfBytesPP); /* source */ + out32r (pGD->pciBase + BR07_o, ((pGD->winSizeX * dst_y) + dst_x) * pGD->gdfBytesPP); /* destination */ + br04 &= 0xffffff00; + br04 |= 0x000000CC; /* S -> D */ + out32r (pGD->pciBase + BR04_o, br04); /* */ + out32r (pGD->pciBase + BR08_o, (dim_y << 16) + dim_x); /* start the BITBlt */ + video_wait_bitblt (pGD->pciBase + BR04_o); +} +#endif /* CONFIG_VIDEO */ diff --git a/qemu/roms/u-boot/drivers/video/da8xx-fb.c b/qemu/roms/u-boot/drivers/video/da8xx-fb.c new file mode 100644 index 000000000..3a5f325cd --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/da8xx-fb.c @@ -0,0 +1,1054 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2011 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * Copyright (C) 2008-2009 MontaVista Software Inc. + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Based on the LCD driver for TI Avalanche processors written by + * Ajay Singh and Shalom Hai. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "videomodes.h" +#include "da8xx-fb.h" + +#if !defined(DA8XX_LCD_CNTL_BASE) +#define DA8XX_LCD_CNTL_BASE DAVINCI_LCD_CNTL_BASE +#endif + +#define DRIVER_NAME "da8xx_lcdc" + +#define LCD_VERSION_1 1 +#define LCD_VERSION_2 2 + +/* LCD Status Register */ +#define LCD_END_OF_FRAME1 (1 << 9) +#define LCD_END_OF_FRAME0 (1 << 8) +#define LCD_PL_LOAD_DONE (1 << 6) +#define LCD_FIFO_UNDERFLOW (1 << 5) +#define LCD_SYNC_LOST (1 << 2) + +/* LCD DMA Control Register */ +#define LCD_DMA_BURST_SIZE(x) ((x) << 4) +#define LCD_DMA_BURST_1 0x0 +#define LCD_DMA_BURST_2 0x1 +#define LCD_DMA_BURST_4 0x2 +#define LCD_DMA_BURST_8 0x3 +#define LCD_DMA_BURST_16 0x4 +#define LCD_V1_END_OF_FRAME_INT_ENA (1 << 2) +#define LCD_V2_END_OF_FRAME0_INT_ENA (1 << 8) +#define LCD_V2_END_OF_FRAME1_INT_ENA (1 << 9) +#define LCD_DUAL_FRAME_BUFFER_ENABLE (1 << 0) + +#define LCD_V2_TFT_24BPP_MODE (1 << 25) +#define LCD_V2_TFT_24BPP_UNPACK (1 << 26) + +/* LCD Control Register */ +#define LCD_CLK_DIVISOR(x) ((x) << 8) +#define LCD_RASTER_MODE 0x01 + +/* LCD Raster Control Register */ +#define LCD_PALETTE_LOAD_MODE(x) ((x) << 20) +#define PALETTE_AND_DATA 0x00 +#define PALETTE_ONLY 0x01 +#define DATA_ONLY 0x02 + +#define LCD_MONO_8BIT_MODE (1 << 9) +#define LCD_RASTER_ORDER (1 << 8) +#define LCD_TFT_MODE (1 << 7) +#define LCD_V1_UNDERFLOW_INT_ENA (1 << 6) +#define LCD_V2_UNDERFLOW_INT_ENA (1 << 5) +#define LCD_V1_PL_INT_ENA (1 << 4) +#define LCD_V2_PL_INT_ENA (1 << 6) +#define LCD_MONOCHROME_MODE (1 << 1) +#define LCD_RASTER_ENABLE (1 << 0) +#define LCD_TFT_ALT_ENABLE (1 << 23) +#define LCD_STN_565_ENABLE (1 << 24) +#define LCD_V2_DMA_CLK_EN (1 << 2) +#define LCD_V2_LIDD_CLK_EN (1 << 1) +#define LCD_V2_CORE_CLK_EN (1 << 0) +#define LCD_V2_LPP_B10 26 +#define LCD_V2_TFT_24BPP_MODE (1 << 25) +#define LCD_V2_TFT_24BPP_UNPACK (1 << 26) + +/* LCD Raster Timing 2 Register */ +#define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) +#define LCD_AC_BIAS_FREQUENCY(x) ((x) << 8) +#define LCD_SYNC_CTRL (1 << 25) +#define LCD_SYNC_EDGE (1 << 24) +#define LCD_INVERT_PIXEL_CLOCK (1 << 22) +#define LCD_INVERT_LINE_CLOCK (1 << 21) +#define LCD_INVERT_FRAME_CLOCK (1 << 20) + +/* Clock registers available only on Version 2 */ +#define LCD_CLK_MAIN_RESET (1 << 3) +/* LCD Block */ +struct da8xx_lcd_regs { + u32 revid; + u32 ctrl; + u32 stat; + u32 lidd_ctrl; + u32 lidd_cs0_conf; + u32 lidd_cs0_addr; + u32 lidd_cs0_data; + u32 lidd_cs1_conf; + u32 lidd_cs1_addr; + u32 lidd_cs1_data; + u32 raster_ctrl; + u32 raster_timing_0; + u32 raster_timing_1; + u32 raster_timing_2; + u32 raster_subpanel; + u32 reserved; + u32 dma_ctrl; + u32 dma_frm_buf_base_addr_0; + u32 dma_frm_buf_ceiling_addr_0; + u32 dma_frm_buf_base_addr_1; + u32 dma_frm_buf_ceiling_addr_1; + u32 resv1; + u32 raw_stat; + u32 masked_stat; + u32 int_ena_set; + u32 int_ena_clr; + u32 end_of_int_ind; + /* Clock registers available only on Version 2 */ + u32 clk_ena; + u32 clk_reset; +}; + +#define LCD_NUM_BUFFERS 1 + +#define WSI_TIMEOUT 50 +#define PALETTE_SIZE 256 +#define LEFT_MARGIN 64 +#define RIGHT_MARGIN 64 +#define UPPER_MARGIN 32 +#define LOWER_MARGIN 32 +#define WAIT_FOR_FRAME_DONE true +#define NO_WAIT_FOR_FRAME_DONE false + +#define calc_fbsize() (panel.plnSizeX * panel.plnSizeY * panel.gdfBytesPP) + +static struct da8xx_lcd_regs *da8xx_fb_reg_base; + +DECLARE_GLOBAL_DATA_PTR; + +/* graphics setup */ +static GraphicDevice gpanel; +static const struct da8xx_panel *lcd_panel; +static struct fb_info *da8xx_fb_info; +static int bits_x_pixel; +static unsigned int lcd_revision; +const struct lcd_ctrl_config *da8xx_lcd_cfg; + +static inline unsigned int lcdc_read(u32 *addr) +{ + return (unsigned int)readl(addr); +} + +static inline void lcdc_write(unsigned int val, u32 *addr) +{ + writel(val, addr); +} + +struct da8xx_fb_par { + u32 p_palette_base; + unsigned char *v_palette_base; + dma_addr_t vram_phys; + unsigned long vram_size; + void *vram_virt; + unsigned int dma_start; + unsigned int dma_end; + struct clk *lcdc_clk; + int irq; + unsigned short pseudo_palette[16]; + unsigned int palette_sz; + unsigned int pxl_clk; + int blank; + int vsync_flag; + int vsync_timeout; +}; + + +/* Variable Screen Information */ +static struct fb_var_screeninfo da8xx_fb_var = { + .xoffset = 0, + .yoffset = 0, + .transp = {0, 0, 0}, + .nonstd = 0, + .activate = 0, + .height = -1, + .width = -1, + .pixclock = 46666, /* 46us - AUO display */ + .accel_flags = 0, + .left_margin = LEFT_MARGIN, + .right_margin = RIGHT_MARGIN, + .upper_margin = UPPER_MARGIN, + .lower_margin = LOWER_MARGIN, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_fix_screeninfo da8xx_fb_fix = { + .id = "DA8xx FB Drv", + .type = FB_TYPE_PACKED_PIXELS, + .type_aux = 0, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 0, + .ypanstep = 1, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE +}; + +/* Enable the Raster Engine of the LCD Controller */ +static inline void lcd_enable_raster(void) +{ + u32 reg; + + /* Put LCDC in reset for several cycles */ + if (lcd_revision == LCD_VERSION_2) + lcdc_write(LCD_CLK_MAIN_RESET, + &da8xx_fb_reg_base->clk_reset); + + udelay(1000); + /* Bring LCDC out of reset */ + if (lcd_revision == LCD_VERSION_2) + lcdc_write(0, + &da8xx_fb_reg_base->clk_reset); + + udelay(1000); + + reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl); + if (!(reg & LCD_RASTER_ENABLE)) + lcdc_write(reg | LCD_RASTER_ENABLE, + &da8xx_fb_reg_base->raster_ctrl); +} + +/* Disable the Raster Engine of the LCD Controller */ +static inline void lcd_disable_raster(bool wait_for_frame_done) +{ + u32 reg; + u32 loop_cnt = 0; + u32 stat; + u32 i = 0; + + if (wait_for_frame_done) + loop_cnt = 5000; + + reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl); + if (reg & LCD_RASTER_ENABLE) + lcdc_write(reg & ~LCD_RASTER_ENABLE, + &da8xx_fb_reg_base->raster_ctrl); + + /* Wait for the current frame to complete */ + do { + if (lcd_revision == LCD_VERSION_1) + stat = lcdc_read(&da8xx_fb_reg_base->stat); + else + stat = lcdc_read(&da8xx_fb_reg_base->raw_stat); + + mdelay(1); + } while (!(stat & 0x01) && (i++ < loop_cnt)); + + if (lcd_revision == LCD_VERSION_1) + lcdc_write(stat, &da8xx_fb_reg_base->stat); + else + lcdc_write(stat, &da8xx_fb_reg_base->raw_stat); + + if ((loop_cnt != 0) && (i >= loop_cnt)) { + printf("LCD Controller timed out\n"); + return; + } +} + +static void lcd_blit(int load_mode, struct da8xx_fb_par *par) +{ + u32 start; + u32 end; + u32 reg_ras; + u32 reg_dma; + u32 reg_int; + + /* init reg to clear PLM (loading mode) fields */ + reg_ras = lcdc_read(&da8xx_fb_reg_base->raster_ctrl); + reg_ras &= ~(3 << 20); + + reg_dma = lcdc_read(&da8xx_fb_reg_base->dma_ctrl); + + if (load_mode == LOAD_DATA) { + start = par->dma_start; + end = par->dma_end; + + reg_ras |= LCD_PALETTE_LOAD_MODE(DATA_ONLY); + if (lcd_revision == LCD_VERSION_1) { + reg_dma |= LCD_V1_END_OF_FRAME_INT_ENA; + } else { + reg_int = lcdc_read(&da8xx_fb_reg_base->int_ena_set) | + LCD_V2_END_OF_FRAME0_INT_ENA | + LCD_V2_END_OF_FRAME1_INT_ENA | + LCD_V2_UNDERFLOW_INT_ENA | LCD_SYNC_LOST; + lcdc_write(reg_int, &da8xx_fb_reg_base->int_ena_set); + } + +#if (LCD_NUM_BUFFERS == 2) + reg_dma |= LCD_DUAL_FRAME_BUFFER_ENABLE; + lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_1); + lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_1); +#else + reg_dma &= ~LCD_DUAL_FRAME_BUFFER_ENABLE; + lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + lcdc_write(0, &da8xx_fb_reg_base->dma_frm_buf_base_addr_1); + lcdc_write(0, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_1); +#endif + + } else if (load_mode == LOAD_PALETTE) { + start = par->p_palette_base; + end = start + par->palette_sz - 1; + + reg_ras |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); + if (lcd_revision == LCD_VERSION_1) { + reg_ras |= LCD_V1_PL_INT_ENA; + } else { + reg_int = lcdc_read(&da8xx_fb_reg_base->int_ena_set) | + LCD_V2_PL_INT_ENA; + lcdc_write(reg_int, &da8xx_fb_reg_base->int_ena_set); + } + + lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + } + + lcdc_write(reg_dma, &da8xx_fb_reg_base->dma_ctrl); + lcdc_write(reg_ras, &da8xx_fb_reg_base->raster_ctrl); + + /* + * The Raster enable bit must be set after all other control fields are + * set. + */ + lcd_enable_raster(); +} + +/* Configure the Burst Size of DMA */ +static int lcd_cfg_dma(int burst_size) +{ + u32 reg; + + reg = lcdc_read(&da8xx_fb_reg_base->dma_ctrl) & 0x00000001; + switch (burst_size) { + case 1: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_1); + break; + case 2: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_2); + break; + case 4: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_4); + break; + case 8: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_8); + break; + case 16: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16); + break; + default: + return -EINVAL; + } + lcdc_write(reg, &da8xx_fb_reg_base->dma_ctrl); + + return 0; +} + +static void lcd_cfg_ac_bias(int period, int transitions_per_int) +{ + u32 reg; + + /* Set the AC Bias Period and Number of Transisitons per Interrupt */ + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_2) & 0xFFF00000; + reg |= LCD_AC_BIAS_FREQUENCY(period) | + LCD_AC_BIAS_TRANSITIONS_PER_INT(transitions_per_int); + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_2); +} + +static void lcd_cfg_horizontal_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_0) & 0xf; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_0); +} + +static void lcd_cfg_vertical_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_1) & 0x3ff; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_1); +} + +static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) +{ + u32 reg; + u32 reg_int; + + reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl) & ~(LCD_TFT_MODE | + LCD_MONO_8BIT_MODE | + LCD_MONOCHROME_MODE); + + switch (cfg->p_disp_panel->panel_shade) { + case MONOCHROME: + reg |= LCD_MONOCHROME_MODE; + if (cfg->mono_8bit_mode) + reg |= LCD_MONO_8BIT_MODE; + break; + case COLOR_ACTIVE: + reg |= LCD_TFT_MODE; + if (cfg->tft_alt_mode) + reg |= LCD_TFT_ALT_ENABLE; + break; + + case COLOR_PASSIVE: + if (cfg->stn_565_mode) + reg |= LCD_STN_565_ENABLE; + break; + + default: + return -EINVAL; + } + + /* enable additional interrupts here */ + if (lcd_revision == LCD_VERSION_1) { + reg |= LCD_V1_UNDERFLOW_INT_ENA; + } else { + reg_int = lcdc_read(&da8xx_fb_reg_base->int_ena_set) | + LCD_V2_UNDERFLOW_INT_ENA; + lcdc_write(reg_int, &da8xx_fb_reg_base->int_ena_set); + } + + lcdc_write(reg, &da8xx_fb_reg_base->raster_ctrl); + + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_2); + + if (cfg->sync_ctrl) + reg |= LCD_SYNC_CTRL; + else + reg &= ~LCD_SYNC_CTRL; + + if (cfg->sync_edge) + reg |= LCD_SYNC_EDGE; + else + reg &= ~LCD_SYNC_EDGE; + + if (cfg->invert_line_clock) + reg |= LCD_INVERT_LINE_CLOCK; + else + reg &= ~LCD_INVERT_LINE_CLOCK; + + if (cfg->invert_frm_clock) + reg |= LCD_INVERT_FRAME_CLOCK; + else + reg &= ~LCD_INVERT_FRAME_CLOCK; + + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_2); + + return 0; +} + +static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, + u32 bpp, u32 raster_order) +{ + u32 reg; + + /* Set the Panel Width */ + /* Pixels per line = (PPL + 1)*16 */ + if (lcd_revision == LCD_VERSION_1) { + /* + * 0x3F in bits 4..9 gives max horisontal resolution = 1024 + * pixels + */ + width &= 0x3f0; + } else { + /* + * 0x7F in bits 4..10 gives max horizontal resolution = 2048 + * pixels. + */ + width &= 0x7f0; + } + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_0); + reg &= 0xfffffc00; + if (lcd_revision == LCD_VERSION_1) { + reg |= ((width >> 4) - 1) << 4; + } else { + width = (width >> 4) - 1; + reg |= ((width & 0x3f) << 4) | ((width & 0x40) >> 3); + } + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_0); + + /* Set the Panel Height */ + /* Set bits 9:0 of Lines Per Pixel */ + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_1); + reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_1); + + /* Set bit 10 of Lines Per Pixel */ + if (lcd_revision == LCD_VERSION_2) { + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_2); + reg |= ((height - 1) & 0x400) << 16; + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_2); + } + + /* Set the Raster Order of the Frame Buffer */ + reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl) & ~(1 << 8); + if (raster_order) + reg |= LCD_RASTER_ORDER; + + if (bpp == 24) + reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE); + else if (bpp == 32) + reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE + | LCD_V2_TFT_24BPP_UNPACK); + + lcdc_write(reg, &da8xx_fb_reg_base->raster_ctrl); + + switch (bpp) { + case 1: + case 2: + case 4: + case 16: + case 24: + case 32: + par->palette_sz = 16 * 2; + break; + + case 8: + par->palette_sz = 256 * 2; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct da8xx_fb_par *par = info->par; + unsigned short *palette = (unsigned short *) par->v_palette_base; + u_short pal; + int update_hw = 0; + + if (regno > 255) + return 1; + + if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) + return 1; + + if (info->var.bits_per_pixel == 8) { + red >>= 4; + green >>= 8; + blue >>= 12; + + pal = (red & 0x0f00); + pal |= (green & 0x00f0); + pal |= (blue & 0x000f); + + if (palette[regno] != pal) { + update_hw = 1; + palette[regno] = pal; + } + } else if ((info->var.bits_per_pixel == 16) && regno < 16) { + red >>= (16 - info->var.red.length); + red <<= info->var.red.offset; + + green >>= (16 - info->var.green.length); + green <<= info->var.green.offset; + + blue >>= (16 - info->var.blue.length); + blue <<= info->var.blue.offset; + + par->pseudo_palette[regno] = red | green | blue; + + if (palette[0] != 0x4000) { + update_hw = 1; + palette[0] = 0x4000; + } + } else if (((info->var.bits_per_pixel == 32) && regno < 32) || + ((info->var.bits_per_pixel == 24) && regno < 24)) { + red >>= (24 - info->var.red.length); + red <<= info->var.red.offset; + + green >>= (24 - info->var.green.length); + green <<= info->var.green.offset; + + blue >>= (24 - info->var.blue.length); + blue <<= info->var.blue.offset; + + par->pseudo_palette[regno] = red | green | blue; + + if (palette[0] != 0x4000) { + update_hw = 1; + palette[0] = 0x4000; + } + } + + /* Update the palette in the h/w as needed. */ + if (update_hw) + lcd_blit(LOAD_PALETTE, par); + + return 0; +} + +static void lcd_reset(struct da8xx_fb_par *par) +{ + /* Disable the Raster if previously Enabled */ + lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE); + + /* DMA has to be disabled */ + lcdc_write(0, &da8xx_fb_reg_base->dma_ctrl); + lcdc_write(0, &da8xx_fb_reg_base->raster_ctrl); + + if (lcd_revision == LCD_VERSION_2) { + lcdc_write(0, &da8xx_fb_reg_base->int_ena_set); + /* Write 1 to reset */ + lcdc_write(LCD_CLK_MAIN_RESET, &da8xx_fb_reg_base->clk_reset); + lcdc_write(0, &da8xx_fb_reg_base->clk_reset); + } +} + +static void lcd_calc_clk_divider(struct da8xx_fb_par *par) +{ + unsigned int lcd_clk, div; + + /* Get clock from sysclk2 */ + lcd_clk = clk_get(2); + + div = lcd_clk / par->pxl_clk; + debug("LCD Clock: %d Divider: %d PixClk: %d\n", + lcd_clk, div, par->pxl_clk); + + /* Configure the LCD clock divisor. */ + lcdc_write(LCD_CLK_DIVISOR(div) | + (LCD_RASTER_MODE & 0x1), &da8xx_fb_reg_base->ctrl); + + if (lcd_revision == LCD_VERSION_2) + lcdc_write(LCD_V2_DMA_CLK_EN | LCD_V2_LIDD_CLK_EN | + LCD_V2_CORE_CLK_EN, + &da8xx_fb_reg_base->clk_ena); +} + +static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, + const struct da8xx_panel *panel) +{ + u32 bpp; + int ret = 0; + + lcd_reset(par); + + /* Calculate the divider */ + lcd_calc_clk_divider(par); + + if (panel->invert_pxl_clk) + lcdc_write((lcdc_read(&da8xx_fb_reg_base->raster_timing_2) | + LCD_INVERT_PIXEL_CLOCK), + &da8xx_fb_reg_base->raster_timing_2); + else + lcdc_write((lcdc_read(&da8xx_fb_reg_base->raster_timing_2) & + ~LCD_INVERT_PIXEL_CLOCK), + &da8xx_fb_reg_base->raster_timing_2); + + /* Configure the DMA burst size. */ + ret = lcd_cfg_dma(cfg->dma_burst_sz); + if (ret < 0) + return ret; + + /* Configure the AC bias properties. */ + lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt); + + /* Configure the vertical and horizontal sync properties. */ + lcd_cfg_vertical_sync(panel->vbp, panel->vsw, panel->vfp); + lcd_cfg_horizontal_sync(panel->hbp, panel->hsw, panel->hfp); + + /* Configure for disply */ + ret = lcd_cfg_display(cfg); + if (ret < 0) + return ret; + + if ((QVGA != cfg->p_disp_panel->panel_type) && + (WVGA != cfg->p_disp_panel->panel_type)) + return -EINVAL; + + if (cfg->bpp <= cfg->p_disp_panel->max_bpp && + cfg->bpp >= cfg->p_disp_panel->min_bpp) + bpp = cfg->bpp; + else + bpp = cfg->p_disp_panel->max_bpp; + if (bpp == 12) + bpp = 16; + ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->width, + (unsigned int)panel->height, bpp, + cfg->raster_order); + if (ret < 0) + return ret; + + /* Configure FDD */ + lcdc_write((lcdc_read(&da8xx_fb_reg_base->raster_ctrl) & 0xfff00fff) | + (cfg->fdd << 12), &da8xx_fb_reg_base->raster_ctrl); + + return 0; +} + +static void lcdc_dma_start(void) +{ + struct da8xx_fb_par *par = da8xx_fb_info->par; + lcdc_write(par->dma_start, + &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(par->dma_end, + &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + lcdc_write(0, + &da8xx_fb_reg_base->dma_frm_buf_base_addr_1); + lcdc_write(0, + &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_1); +} + +static u32 lcdc_irq_handler_rev01(void) +{ + struct da8xx_fb_par *par = da8xx_fb_info->par; + u32 stat = lcdc_read(&da8xx_fb_reg_base->stat); + u32 reg_ras; + + if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { + debug("LCD_SYNC_LOST\n"); + lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE); + lcdc_write(stat, &da8xx_fb_reg_base->stat); + lcd_enable_raster(); + return LCD_SYNC_LOST; + } else if (stat & LCD_PL_LOAD_DONE) { + debug("LCD_PL_LOAD_DONE\n"); + /* + * Must disable raster before changing state of any control bit. + * And also must be disabled before clearing the PL loading + * interrupt via the following write to the status register. If + * this is done after then one gets multiple PL done interrupts. + */ + lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE); + + lcdc_write(stat, &da8xx_fb_reg_base->stat); + + /* Disable PL completion inerrupt */ + reg_ras = lcdc_read(&da8xx_fb_reg_base->raster_ctrl); + reg_ras &= ~LCD_V1_PL_INT_ENA; + lcdc_write(reg_ras, &da8xx_fb_reg_base->raster_ctrl); + + /* Setup and start data loading mode */ + lcd_blit(LOAD_DATA, par); + return LCD_PL_LOAD_DONE; + } else { + lcdc_write(stat, &da8xx_fb_reg_base->stat); + + if (stat & LCD_END_OF_FRAME0) + debug("LCD_END_OF_FRAME0\n"); + + lcdc_write(par->dma_start, + &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(par->dma_end, + &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + par->vsync_flag = 1; + return LCD_END_OF_FRAME0; + } + return stat; +} + +static u32 lcdc_irq_handler_rev02(void) +{ + struct da8xx_fb_par *par = da8xx_fb_info->par; + u32 stat = lcdc_read(&da8xx_fb_reg_base->masked_stat); + u32 reg_int; + + if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { + debug("LCD_SYNC_LOST\n"); + lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE); + lcdc_write(stat, &da8xx_fb_reg_base->masked_stat); + lcd_enable_raster(); + lcdc_write(0, &da8xx_fb_reg_base->end_of_int_ind); + return LCD_SYNC_LOST; + } else if (stat & LCD_PL_LOAD_DONE) { + debug("LCD_PL_LOAD_DONE\n"); + /* + * Must disable raster before changing state of any control bit. + * And also must be disabled before clearing the PL loading + * interrupt via the following write to the status register. If + * this is done after then one gets multiple PL done interrupts. + */ + lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE); + + lcdc_write(stat, &da8xx_fb_reg_base->masked_stat); + + /* Disable PL completion inerrupt */ + reg_int = lcdc_read(&da8xx_fb_reg_base->int_ena_clr) | + (LCD_V2_PL_INT_ENA); + lcdc_write(reg_int, &da8xx_fb_reg_base->int_ena_clr); + + /* Setup and start data loading mode */ + lcd_blit(LOAD_DATA, par); + lcdc_write(0, &da8xx_fb_reg_base->end_of_int_ind); + return LCD_PL_LOAD_DONE; + } else { + lcdc_write(stat, &da8xx_fb_reg_base->masked_stat); + + if (stat & LCD_END_OF_FRAME0) + debug("LCD_END_OF_FRAME0\n"); + + lcdc_write(par->dma_start, + &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(par->dma_end, + &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + par->vsync_flag = 1; + lcdc_write(0, &da8xx_fb_reg_base->end_of_int_ind); + return LCD_END_OF_FRAME0; + } + lcdc_write(0, &da8xx_fb_reg_base->end_of_int_ind); + return stat; +} + +static u32 lcdc_irq_handler(void) +{ + if (lcd_revision == LCD_VERSION_1) + return lcdc_irq_handler_rev01(); + else + return lcdc_irq_handler_rev02(); +} + +static u32 wait_for_event(u32 event) +{ + u32 timeout = 50000; + u32 ret; + + do { + ret = lcdc_irq_handler(); + udelay(1000); + } while (!(ret & event)); + + if (timeout <= 0) { + printf("%s: event %d not hit\n", __func__, event); + return -1; + } + + return 0; + +} + +void *video_hw_init(void) +{ + struct da8xx_fb_par *par; + u32 size; + u32 rev; + char *p; + + if (!lcd_panel) { + printf("Display not initialized\n"); + return NULL; + } + gpanel.winSizeX = lcd_panel->width; + gpanel.winSizeY = lcd_panel->height; + gpanel.plnSizeX = lcd_panel->width; + gpanel.plnSizeY = lcd_panel->height; + + switch (bits_x_pixel) { + case 32: + gpanel.gdfBytesPP = 4; + gpanel.gdfIndex = GDF_32BIT_X888RGB; + break; + case 24: + gpanel.gdfBytesPP = 4; + gpanel.gdfIndex = GDF_32BIT_X888RGB; + break; + case 16: + gpanel.gdfBytesPP = 2; + gpanel.gdfIndex = GDF_16BIT_565RGB; + break; + default: + gpanel.gdfBytesPP = 1; + gpanel.gdfIndex = GDF__8BIT_INDEX; + break; + } + + da8xx_fb_reg_base = (struct da8xx_lcd_regs *)DA8XX_LCD_CNTL_BASE; + + /* Determine LCD IP Version */ + rev = lcdc_read(&da8xx_fb_reg_base->revid); + switch (rev) { + case 0x4C100102: + lcd_revision = LCD_VERSION_1; + break; + case 0x4F200800: + case 0x4F201000: + lcd_revision = LCD_VERSION_2; + break; + default: + printf("Unknown PID Reg value 0x%x, defaulting to LCD revision 1\n", + rev); + lcd_revision = LCD_VERSION_1; + break; + } + + debug("rev: 0x%x Resolution: %dx%d %d\n", rev, + gpanel.winSizeX, + gpanel.winSizeY, + da8xx_lcd_cfg->bpp); + + size = sizeof(struct fb_info) + sizeof(struct da8xx_fb_par); + da8xx_fb_info = malloc(size); + debug("da8xx_fb_info at %x\n", (unsigned int)da8xx_fb_info); + + if (!da8xx_fb_info) { + printf("Memory allocation failed for fb_info\n"); + return NULL; + } + memset(da8xx_fb_info, 0, size); + p = (char *)da8xx_fb_info; + da8xx_fb_info->par = p + sizeof(struct fb_info); + debug("da8xx_par at %x\n", (unsigned int)da8xx_fb_info->par); + + par = da8xx_fb_info->par; + par->pxl_clk = lcd_panel->pxl_clk; + + if (lcd_init(par, da8xx_lcd_cfg, lcd_panel) < 0) { + printf("lcd_init failed\n"); + goto err_release_fb; + } + + /* allocate frame buffer */ + par->vram_size = lcd_panel->width * lcd_panel->height * + da8xx_lcd_cfg->bpp; + par->vram_size = par->vram_size * LCD_NUM_BUFFERS / 8; + + par->vram_virt = malloc(par->vram_size); + + par->vram_phys = (dma_addr_t) par->vram_virt; + debug("Requesting 0x%x bytes for framebuffer at 0x%x\n", + (unsigned int)par->vram_size, + (unsigned int)par->vram_virt); + if (!par->vram_virt) { + printf("GLCD: malloc for frame buffer failed\n"); + goto err_release_fb; + } + gd->fb_base = (int)par->vram_virt; + + gpanel.frameAdrs = (unsigned int)par->vram_virt; + da8xx_fb_info->screen_base = (char *) par->vram_virt; + da8xx_fb_fix.smem_start = gpanel.frameAdrs; + da8xx_fb_fix.smem_len = par->vram_size; + da8xx_fb_fix.line_length = (lcd_panel->width * da8xx_lcd_cfg->bpp) / 8; + + par->dma_start = par->vram_phys; + par->dma_end = par->dma_start + lcd_panel->height * + da8xx_fb_fix.line_length - 1; + + /* allocate palette buffer */ + par->v_palette_base = malloc(PALETTE_SIZE); + if (!par->v_palette_base) { + printf("GLCD: malloc for palette buffer failed\n"); + goto err_release_fb_mem; + } + memset(par->v_palette_base, 0, PALETTE_SIZE); + par->p_palette_base = (unsigned int)par->v_palette_base; + + /* Initialize par */ + da8xx_fb_info->var.bits_per_pixel = da8xx_lcd_cfg->bpp; + + da8xx_fb_var.xres = lcd_panel->width; + da8xx_fb_var.xres_virtual = lcd_panel->width; + + da8xx_fb_var.yres = lcd_panel->height; + da8xx_fb_var.yres_virtual = lcd_panel->height * LCD_NUM_BUFFERS; + + da8xx_fb_var.grayscale = + da8xx_lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0; + da8xx_fb_var.bits_per_pixel = da8xx_lcd_cfg->bpp; + + da8xx_fb_var.hsync_len = lcd_panel->hsw; + da8xx_fb_var.vsync_len = lcd_panel->vsw; + + /* Initialize fbinfo */ + da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT; + da8xx_fb_info->fix = da8xx_fb_fix; + da8xx_fb_info->var = da8xx_fb_var; + da8xx_fb_info->pseudo_palette = par->pseudo_palette; + da8xx_fb_info->fix.visual = (da8xx_fb_info->var.bits_per_pixel <= 8) ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + + /* Clear interrupt */ + memset((void *)par->vram_virt, 0, par->vram_size); + lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE); + if (lcd_revision == LCD_VERSION_1) + lcdc_write(0xFFFF, &da8xx_fb_reg_base->stat); + else + lcdc_write(0xFFFF, &da8xx_fb_reg_base->masked_stat); + debug("Palette at 0x%x size %d\n", par->p_palette_base, + par->palette_sz); + lcdc_dma_start(); + + /* Load a default palette */ + fb_setcolreg(0, 0, 0, 0, 0xffff, da8xx_fb_info); + + /* Check that the palette is loaded */ + wait_for_event(LCD_PL_LOAD_DONE); + + /* Wait until DMA is working */ + wait_for_event(LCD_END_OF_FRAME0); + + return (void *)&gpanel; + +err_release_fb_mem: + free(par->vram_virt); + +err_release_fb: + free(da8xx_fb_info); + + return NULL; +} + +void video_set_lut(unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + + return; +} + +void da8xx_video_init(const struct da8xx_panel *panel, + const struct lcd_ctrl_config *lcd_cfg, int bits_pixel) +{ + lcd_panel = panel; + da8xx_lcd_cfg = lcd_cfg; + bits_x_pixel = bits_pixel; +} diff --git a/qemu/roms/u-boot/drivers/video/da8xx-fb.h b/qemu/roms/u-boot/drivers/video/da8xx-fb.h new file mode 100644 index 000000000..6447a4047 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/da8xx-fb.h @@ -0,0 +1,116 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2011 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * Copyright (C) 2008-2009 MontaVista Software Inc. + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Based on the LCD driver for TI Avalanche processors written by + * Ajay Singh and Shalom Hai. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef DA8XX_FB_H +#define DA8XX_FB_H + +enum panel_type { + QVGA = 0, + WVGA +}; + +enum panel_shade { + MONOCHROME = 0, + COLOR_ACTIVE, + COLOR_PASSIVE, +}; + +enum raster_load_mode { + LOAD_DATA = 1, + LOAD_PALETTE, +}; + +struct display_panel { + enum panel_type panel_type; /* QVGA */ + int max_bpp; + int min_bpp; + enum panel_shade panel_shade; +}; + +struct da8xx_panel { + const char name[25]; /* Full name _ */ + unsigned short width; + unsigned short height; + int hfp; /* Horizontal front porch */ + int hbp; /* Horizontal back porch */ + int hsw; /* Horizontal Sync Pulse Width */ + int vfp; /* Vertical front porch */ + int vbp; /* Vertical back porch */ + int vsw; /* Vertical Sync Pulse Width */ + unsigned int pxl_clk; /* Pixel clock */ + unsigned char invert_pxl_clk; /* Invert Pixel clock */ +}; + +struct da8xx_lcdc_platform_data { + const char manu_name[10]; + void *controller_data; + const char type[25]; + void (*panel_power_ctrl)(int); +}; + +struct lcd_ctrl_config { + const struct display_panel *p_disp_panel; + + /* AC Bias Pin Frequency */ + int ac_bias; + + /* AC Bias Pin Transitions per Interrupt */ + int ac_bias_intrpt; + + /* DMA burst size */ + int dma_burst_sz; + + /* Bits per pixel */ + int bpp; + + /* FIFO DMA Request Delay */ + int fdd; + + /* TFT Alternative Signal Mapping (Only for active) */ + unsigned char tft_alt_mode; + + /* 12 Bit Per Pixel (5-6-5) Mode (Only for passive) */ + unsigned char stn_565_mode; + + /* Mono 8-bit Mode: 1=D0-D7 or 0=D0-D3 */ + unsigned char mono_8bit_mode; + + /* Invert line clock */ + unsigned char invert_line_clock; + + /* Invert frame clock */ + unsigned char invert_frm_clock; + + /* Horizontal and Vertical Sync Edge: 0=rising 1=falling */ + unsigned char sync_edge; + + /* Horizontal and Vertical Sync: Control: 0=ignore */ + unsigned char sync_ctrl; + + /* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */ + unsigned char raster_order; +}; + +struct lcd_sync_arg { + int back_porch; + int front_porch; + int pulse_width; +}; + +void da8xx_video_init(const struct da8xx_panel *panel, + const struct lcd_ctrl_config *lcd_cfg, + int bits_pixel); + +#endif /* ifndef DA8XX_FB_H */ diff --git a/qemu/roms/u-boot/drivers/video/exynos_dp.c b/qemu/roms/u-boot/drivers/video/exynos_dp.c new file mode 100644 index 000000000..682483fc3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_dp.c @@ -0,0 +1,982 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exynos_dp_lowlevel.h" + +DECLARE_GLOBAL_DATA_PTR; + +static struct exynos_dp_platform_data *dp_pd; + +void __exynos_set_dp_phy(unsigned int onoff) +{ +} +void exynos_set_dp_phy(unsigned int onoff) + __attribute__((weak, alias("__exynos_set_dp_phy"))); + +static void exynos_dp_disp_info(struct edp_disp_info *disp_info) +{ + disp_info->h_total = disp_info->h_res + disp_info->h_sync_width + + disp_info->h_back_porch + disp_info->h_front_porch; + disp_info->v_total = disp_info->v_res + disp_info->v_sync_width + + disp_info->v_back_porch + disp_info->v_front_porch; + + return; +} + +static int exynos_dp_init_dp(void) +{ + int ret; + exynos_dp_reset(); + + /* SW defined function Normal operation */ + exynos_dp_enable_sw_func(DP_ENABLE); + + ret = exynos_dp_init_analog_func(); + if (ret != EXYNOS_DP_SUCCESS) + return ret; + + exynos_dp_init_hpd(); + exynos_dp_init_aux(); + + return ret; +} + +static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data) +{ + int i; + unsigned char sum = 0; + + for (i = 0; i < EDID_BLOCK_LENGTH; i++) + sum = sum + edid_data[i]; + + return sum; +} + +static unsigned int exynos_dp_read_edid(void) +{ + unsigned char edid[EDID_BLOCK_LENGTH * 2]; + unsigned int extend_block = 0; + unsigned char sum; + unsigned char test_vector; + int retval; + + /* + * EDID device address is 0x50. + * However, if necessary, you must have set upper address + * into E-EDID in I2C device, 0x30. + */ + + /* Read Extension Flag, Number of 128-byte EDID extension blocks */ + exynos_dp_read_byte_from_i2c(I2C_EDID_DEVICE_ADDR, EDID_EXTENSION_FLAG, + &extend_block); + + if (extend_block > 0) { + printf("DP EDID data includes a single extension!\n"); + + /* Read EDID data */ + retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR, + EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, + &edid[EDID_HEADER_PATTERN]); + if (retval != 0) { + printf("DP EDID Read failed!\n"); + return -1; + } + sum = exynos_dp_calc_edid_check_sum(edid); + if (sum != 0) { + printf("DP EDID bad checksum!\n"); + return -1; + } + + /* Read additional EDID data */ + retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR, + EDID_BLOCK_LENGTH, + EDID_BLOCK_LENGTH, + &edid[EDID_BLOCK_LENGTH]); + if (retval != 0) { + printf("DP EDID Read failed!\n"); + return -1; + } + sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); + if (sum != 0) { + printf("DP EDID bad checksum!\n"); + return -1; + } + + exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST, + &test_vector); + if (test_vector & DPCD_TEST_EDID_READ) { + exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM, + edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); + exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE, + DPCD_TEST_EDID_CHECKSUM_WRITE); + } + } else { + debug("DP EDID data does not include any extensions.\n"); + + /* Read EDID data */ + retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR, + EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, + &edid[EDID_HEADER_PATTERN]); + + if (retval != 0) { + printf("DP EDID Read failed!\n"); + return -1; + } + sum = exynos_dp_calc_edid_check_sum(edid); + if (sum != 0) { + printf("DP EDID bad checksum!\n"); + return -1; + } + + exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST, + &test_vector); + if (test_vector & DPCD_TEST_EDID_READ) { + exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM, + edid[EDID_CHECKSUM]); + exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE, + DPCD_TEST_EDID_CHECKSUM_WRITE); + } + } + + debug("DP EDID Read success!\n"); + + return 0; +} + +static unsigned int exynos_dp_handle_edid(struct edp_device_info *edp_info) +{ + unsigned char buf[12]; + unsigned int ret; + unsigned char temp; + unsigned char retry_cnt; + unsigned char dpcd_rev[16]; + unsigned char lane_bw[16]; + unsigned char lane_cnt[16]; + + memset(dpcd_rev, 0, 16); + memset(lane_bw, 0, 16); + memset(lane_cnt, 0, 16); + memset(buf, 0, 12); + + retry_cnt = 5; + while (retry_cnt) { + /* Read DPCD 0x0000-0x000b */ + ret = exynos_dp_read_bytes_from_dpcd(DPCD_DPCD_REV, 12, + buf); + if (ret != EXYNOS_DP_SUCCESS) { + if (retry_cnt == 0) { + printf("DP read_byte_from_dpcd() failed\n"); + return ret; + } + retry_cnt--; + } else + break; + } + + /* */ + temp = buf[DPCD_DPCD_REV]; + if (temp == DP_DPCD_REV_10 || temp == DP_DPCD_REV_11) + edp_info->dpcd_rev = temp; + else { + printf("DP Wrong DPCD Rev : %x\n", temp); + return -ENODEV; + } + + temp = buf[DPCD_MAX_LINK_RATE]; + if (temp == DP_LANE_BW_1_62 || temp == DP_LANE_BW_2_70) + edp_info->lane_bw = temp; + else { + printf("DP Wrong MAX LINK RATE : %x\n", temp); + return -EINVAL; + } + + /*Refer VESA Display Port Stnadard Ver1.1a Page 120 */ + if (edp_info->dpcd_rev == DP_DPCD_REV_11) { + temp = buf[DPCD_MAX_LANE_COUNT] & 0x1f; + if (buf[DPCD_MAX_LANE_COUNT] & 0x80) + edp_info->dpcd_efc = 1; + else + edp_info->dpcd_efc = 0; + } else { + temp = buf[DPCD_MAX_LANE_COUNT]; + edp_info->dpcd_efc = 0; + } + + if (temp == DP_LANE_CNT_1 || temp == DP_LANE_CNT_2 || + temp == DP_LANE_CNT_4) { + edp_info->lane_cnt = temp; + } else { + printf("DP Wrong MAX LANE COUNT : %x\n", temp); + return -EINVAL; + } + + ret = exynos_dp_read_edid(); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP exynos_dp_read_edid() failed\n"); + return -EINVAL; + } + + return ret; +} + +static void exynos_dp_init_training(void) +{ + /* + * MACRO_RST must be applied after the PLL_LOCK to avoid + * the DP inter pair skew issue for at least 10 us + */ + exynos_dp_reset_macro(); + + /* All DP analog module power up */ + exynos_dp_set_analog_power_down(POWER_ALL, 0); +} + +static unsigned int exynos_dp_link_start(struct edp_device_info *edp_info) +{ + unsigned char buf[5]; + unsigned int ret = 0; + + debug("DP: %s was called\n", __func__); + + edp_info->lt_info.lt_status = DP_LT_CR; + edp_info->lt_info.ep_loop = 0; + edp_info->lt_info.cr_loop[0] = 0; + edp_info->lt_info.cr_loop[1] = 0; + edp_info->lt_info.cr_loop[2] = 0; + edp_info->lt_info.cr_loop[3] = 0; + + /* Set sink to D0 (Sink Not Ready) mode. */ + ret = exynos_dp_write_byte_to_dpcd(DPCD_SINK_POWER_STATE, + DPCD_SET_POWER_STATE_D0); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP write_dpcd_byte failed\n"); + return ret; + } + + /* Set link rate and count as you want to establish*/ + exynos_dp_set_link_bandwidth(edp_info->lane_bw); + exynos_dp_set_lane_count(edp_info->lane_cnt); + + /* Setup RX configuration */ + buf[0] = edp_info->lane_bw; + buf[1] = edp_info->lane_cnt; + + ret = exynos_dp_write_bytes_to_dpcd(DPCD_LINK_BW_SET, 2, + buf); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP write_dpcd_byte failed\n"); + return ret; + } + + exynos_dp_set_lane_pre_emphasis(PRE_EMPHASIS_LEVEL_0, + edp_info->lane_cnt); + + /* Set training pattern 1 */ + exynos_dp_set_training_pattern(TRAINING_PTN1); + + /* Set RX training pattern */ + buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1; + + buf[1] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | + DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; + buf[2] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | + DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; + buf[3] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | + DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; + buf[4] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | + DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; + + ret = exynos_dp_write_bytes_to_dpcd(DPCD_TRAINING_PATTERN_SET, + 5, buf); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP write_dpcd_byte failed\n"); + return ret; + } + + return ret; +} + +static unsigned int exynos_dp_training_pattern_dis(void) +{ + unsigned int ret = EXYNOS_DP_SUCCESS; + + exynos_dp_set_training_pattern(DP_NONE); + + ret = exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET, + DPCD_TRAINING_PATTERN_DISABLED); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP requst_link_traninig_req failed\n"); + return -EAGAIN; + } + + return ret; +} + +static unsigned int exynos_dp_enable_rx_to_enhanced_mode(unsigned char enable) +{ + unsigned char data; + unsigned int ret = EXYNOS_DP_SUCCESS; + + ret = exynos_dp_read_byte_from_dpcd(DPCD_LANE_COUNT_SET, + &data); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP read_from_dpcd failed\n"); + return -EAGAIN; + } + + if (enable) + data = DPCD_ENHANCED_FRAME_EN | DPCD_LN_COUNT_SET(data); + else + data = DPCD_LN_COUNT_SET(data); + + ret = exynos_dp_write_byte_to_dpcd(DPCD_LANE_COUNT_SET, + data); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP write_to_dpcd failed\n"); + return -EAGAIN; + + } + + return ret; +} + +static unsigned int exynos_dp_set_enhanced_mode(unsigned char enhance_mode) +{ + unsigned int ret = EXYNOS_DP_SUCCESS; + + ret = exynos_dp_enable_rx_to_enhanced_mode(enhance_mode); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP rx_enhance_mode failed\n"); + return -EAGAIN; + } + + exynos_dp_enable_enhanced_mode(enhance_mode); + + return ret; +} + +static int exynos_dp_read_dpcd_lane_stat(struct edp_device_info *edp_info, + unsigned char *status) +{ + unsigned int ret, i; + unsigned char buf[2]; + unsigned char lane_stat[DP_LANE_CNT_4] = {0,}; + unsigned char shift_val[DP_LANE_CNT_4] = {0,}; + + shift_val[0] = 0; + shift_val[1] = 4; + shift_val[2] = 0; + shift_val[3] = 4; + + ret = exynos_dp_read_bytes_from_dpcd(DPCD_LANE0_1_STATUS, 2, buf); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP read lane status failed\n"); + return ret; + } + + for (i = 0; i < edp_info->lane_cnt; i++) { + lane_stat[i] = (buf[(i / 2)] >> shift_val[i]) & 0x0f; + if (lane_stat[0] != lane_stat[i]) { + printf("Wrong lane status\n"); + return -EINVAL; + } + } + + *status = lane_stat[0]; + + return ret; +} + +static unsigned int exynos_dp_read_dpcd_adj_req(unsigned char lane_num, + unsigned char *sw, unsigned char *em) +{ + unsigned int ret = EXYNOS_DP_SUCCESS; + unsigned char buf; + unsigned int dpcd_addr; + unsigned char shift_val[DP_LANE_CNT_4] = {0, 4, 0, 4}; + + /*lane_num value is used as arry index, so this range 0 ~ 3 */ + dpcd_addr = DPCD_ADJUST_REQUEST_LANE0_1 + (lane_num / 2); + + ret = exynos_dp_read_byte_from_dpcd(dpcd_addr, &buf); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP read adjust request failed\n"); + return -EAGAIN; + } + + *sw = ((buf >> shift_val[lane_num]) & 0x03); + *em = ((buf >> shift_val[lane_num]) & 0x0c) >> 2; + + return ret; +} + +static int exynos_dp_equalizer_err_link(struct edp_device_info *edp_info) +{ + int ret; + + ret = exynos_dp_training_pattern_dis(); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP training_patter_disable() failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + } + + ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP set_enhanced_mode() failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + } + + return ret; +} + +static int exynos_dp_reduce_link_rate(struct edp_device_info *edp_info) +{ + int ret; + + if (edp_info->lane_bw == DP_LANE_BW_2_70) { + edp_info->lane_bw = DP_LANE_BW_1_62; + printf("DP Change lane bw to 1.62Gbps\n"); + edp_info->lt_info.lt_status = DP_LT_START; + ret = EXYNOS_DP_SUCCESS; + } else { + ret = exynos_dp_training_pattern_dis(); + if (ret != EXYNOS_DP_SUCCESS) + printf("DP training_patter_disable() failed\n"); + + ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc); + if (ret != EXYNOS_DP_SUCCESS) + printf("DP set_enhanced_mode() failed\n"); + + edp_info->lt_info.lt_status = DP_LT_FAIL; + } + + return ret; +} + +static unsigned int exynos_dp_process_clock_recovery(struct edp_device_info + *edp_info) +{ + unsigned int ret = EXYNOS_DP_SUCCESS; + unsigned char lane_stat; + unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, }; + unsigned int i; + unsigned char adj_req_sw; + unsigned char adj_req_em; + unsigned char buf[5]; + + debug("DP: %s was called\n", __func__); + mdelay(1); + + ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP read lane status failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + + if (lane_stat & DP_LANE_STAT_CR_DONE) { + debug("DP clock Recovery training succeed\n"); + exynos_dp_set_training_pattern(TRAINING_PTN2); + + for (i = 0; i < edp_info->lane_cnt; i++) { + ret = exynos_dp_read_dpcd_adj_req(i, &adj_req_sw, + &adj_req_em); + if (ret != EXYNOS_DP_SUCCESS) { + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + + lt_ctl_val[i] = 0; + lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; + + if ((adj_req_sw == VOLTAGE_LEVEL_3) + || (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { + lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | + MAX_PRE_EMPHASIS_REACH_3; + } + exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i); + } + + buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_2; + buf[1] = lt_ctl_val[0]; + buf[2] = lt_ctl_val[1]; + buf[3] = lt_ctl_val[2]; + buf[4] = lt_ctl_val[3]; + + ret = exynos_dp_write_bytes_to_dpcd( + DPCD_TRAINING_PATTERN_SET, 5, buf); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP write traning pattern1 failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } else + edp_info->lt_info.lt_status = DP_LT_ET; + } else { + for (i = 0; i < edp_info->lane_cnt; i++) { + lt_ctl_val[i] = exynos_dp_get_lanex_pre_emphasis(i); + ret = exynos_dp_read_dpcd_adj_req(i, + &adj_req_sw, &adj_req_em); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP read adj req failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + + if ((adj_req_sw == VOLTAGE_LEVEL_3) || + (adj_req_em == PRE_EMPHASIS_LEVEL_3)) + ret = exynos_dp_reduce_link_rate(edp_info); + + if ((DRIVE_CURRENT_SET_0_GET(lt_ctl_val[i]) == + adj_req_sw) && + (PRE_EMPHASIS_SET_0_GET(lt_ctl_val[i]) == + adj_req_em)) { + edp_info->lt_info.cr_loop[i]++; + if (edp_info->lt_info.cr_loop[i] == MAX_CR_LOOP) + ret = exynos_dp_reduce_link_rate( + edp_info); + } + + lt_ctl_val[i] = 0; + lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; + + if ((adj_req_sw == VOLTAGE_LEVEL_3) || + (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { + lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | + MAX_PRE_EMPHASIS_REACH_3; + } + exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i); + } + + ret = exynos_dp_write_bytes_to_dpcd( + DPCD_TRAINING_LANE0_SET, 4, lt_ctl_val); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP write traning pattern2 failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + } + + return ret; +} + +static unsigned int exynos_dp_process_equalizer_training(struct edp_device_info + *edp_info) +{ + unsigned int ret = EXYNOS_DP_SUCCESS; + unsigned char lane_stat, adj_req_sw, adj_req_em, i; + unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,}; + unsigned char interlane_aligned = 0; + unsigned char f_bw; + unsigned char f_lane_cnt; + unsigned char sink_stat; + + mdelay(1); + + ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP read lane status failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + return ret; + } + + debug("DP lane stat : %x\n", lane_stat); + + if (lane_stat & DP_LANE_STAT_CR_DONE) { + ret = exynos_dp_read_byte_from_dpcd(DPCD_LN_ALIGN_UPDATED, + &sink_stat); + if (ret != EXYNOS_DP_SUCCESS) { + edp_info->lt_info.lt_status = DP_LT_FAIL; + + return ret; + } + + interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE); + + for (i = 0; i < edp_info->lane_cnt; i++) { + ret = exynos_dp_read_dpcd_adj_req(i, + &adj_req_sw, &adj_req_em); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP read adj req 1 failed\n"); + edp_info->lt_info.lt_status = DP_LT_FAIL; + + return ret; + } + + lt_ctl_val[i] = 0; + lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; + + if ((adj_req_sw == VOLTAGE_LEVEL_3) || + (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { + lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3; + lt_ctl_val[i] |= MAX_PRE_EMPHASIS_REACH_3; + } + } + + if (((lane_stat&DP_LANE_STAT_CE_DONE) && + (lane_stat&DP_LANE_STAT_SYM_LOCK)) + && (interlane_aligned == DPCD_INTERLANE_ALIGN_DONE)) { + debug("DP Equalizer training succeed\n"); + + f_bw = exynos_dp_get_link_bandwidth(); + f_lane_cnt = exynos_dp_get_lane_count(); + + debug("DP final BandWidth : %x\n", f_bw); + debug("DP final Lane Count : %x\n", f_lane_cnt); + + edp_info->lt_info.lt_status = DP_LT_FINISHED; + + exynos_dp_equalizer_err_link(edp_info); + + } else { + edp_info->lt_info.ep_loop++; + + if (edp_info->lt_info.ep_loop > MAX_EQ_LOOP) { + if (edp_info->lane_bw == DP_LANE_BW_2_70) { + ret = exynos_dp_reduce_link_rate( + edp_info); + } else { + edp_info->lt_info.lt_status = + DP_LT_FAIL; + exynos_dp_equalizer_err_link(edp_info); + } + } else { + for (i = 0; i < edp_info->lane_cnt; i++) + exynos_dp_set_lanex_pre_emphasis( + lt_ctl_val[i], i); + + ret = exynos_dp_write_bytes_to_dpcd( + DPCD_TRAINING_LANE0_SET, + 4, lt_ctl_val); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP set lt pattern failed\n"); + edp_info->lt_info.lt_status = + DP_LT_FAIL; + exynos_dp_equalizer_err_link(edp_info); + } + } + } + } else if (edp_info->lane_bw == DP_LANE_BW_2_70) { + ret = exynos_dp_reduce_link_rate(edp_info); + } else { + edp_info->lt_info.lt_status = DP_LT_FAIL; + exynos_dp_equalizer_err_link(edp_info); + } + + return ret; +} + +static unsigned int exynos_dp_sw_link_training(struct edp_device_info *edp_info) +{ + unsigned int ret = 0; + int training_finished; + + /* Turn off unnecessary lane */ + if (edp_info->lane_cnt == 1) + exynos_dp_set_analog_power_down(CH1_BLOCK, 1); + + training_finished = 0; + + edp_info->lt_info.lt_status = DP_LT_START; + + /* Process here */ + while (!training_finished) { + switch (edp_info->lt_info.lt_status) { + case DP_LT_START: + ret = exynos_dp_link_start(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP LT:link start failed\n"); + return ret; + } + break; + case DP_LT_CR: + ret = exynos_dp_process_clock_recovery(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP LT:clock recovery failed\n"); + return ret; + } + break; + case DP_LT_ET: + ret = exynos_dp_process_equalizer_training(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP LT:equalizer training failed\n"); + return ret; + } + break; + case DP_LT_FINISHED: + training_finished = 1; + break; + case DP_LT_FAIL: + return -1; + } + } + + return ret; +} + +static unsigned int exynos_dp_set_link_train(struct edp_device_info *edp_info) +{ + unsigned int ret; + + exynos_dp_init_training(); + + ret = exynos_dp_sw_link_training(edp_info); + if (ret != EXYNOS_DP_SUCCESS) + printf("DP dp_sw_link_traning() failed\n"); + + return ret; +} + +static void exynos_dp_enable_scramble(unsigned int enable) +{ + unsigned char data; + + if (enable) { + exynos_dp_enable_scrambling(DP_ENABLE); + + exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET, + &data); + exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET, + (u8)(data & ~DPCD_SCRAMBLING_DISABLED)); + } else { + exynos_dp_enable_scrambling(DP_DISABLE); + exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET, + &data); + exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET, + (u8)(data | DPCD_SCRAMBLING_DISABLED)); + } +} + +static unsigned int exynos_dp_config_video(struct edp_device_info *edp_info) +{ + unsigned int ret = 0; + unsigned int retry_cnt; + + mdelay(1); + + if (edp_info->video_info.master_mode) { + printf("DP does not support master mode\n"); + return -ENODEV; + } else { + /* debug slave */ + exynos_dp_config_video_slave_mode(&edp_info->video_info); + } + + exynos_dp_set_video_color_format(&edp_info->video_info); + + if (edp_info->video_info.bist_mode) { + if (exynos_dp_config_video_bist(edp_info) != 0) + return -1; + } + + ret = exynos_dp_get_pll_lock_status(); + if (ret != PLL_LOCKED) { + printf("DP PLL is not locked yet\n"); + return -EIO; + } + + if (edp_info->video_info.master_mode == 0) { + retry_cnt = 10; + while (retry_cnt) { + ret = exynos_dp_is_slave_video_stream_clock_on(); + if (ret != EXYNOS_DP_SUCCESS) { + if (retry_cnt == 0) { + printf("DP stream_clock_on failed\n"); + return ret; + } + retry_cnt--; + mdelay(1); + } else + break; + } + } + + /* Set to use the register calculated M/N video */ + exynos_dp_set_video_cr_mn(CALCULATED_M, 0, 0); + + /* For video bist, Video timing must be generated by register */ + exynos_dp_set_video_timing_mode(VIDEO_TIMING_FROM_CAPTURE); + + /* Enable video bist */ + if (edp_info->video_info.bist_pattern != COLOR_RAMP && + edp_info->video_info.bist_pattern != BALCK_WHITE_V_LINES && + edp_info->video_info.bist_pattern != COLOR_SQUARE) + exynos_dp_enable_video_bist(edp_info->video_info.bist_mode); + else + exynos_dp_enable_video_bist(DP_DISABLE); + + /* Disable video mute */ + exynos_dp_enable_video_mute(DP_DISABLE); + + /* Configure video Master or Slave mode */ + exynos_dp_enable_video_master(edp_info->video_info.master_mode); + + /* Enable video */ + exynos_dp_start_video(); + + if (edp_info->video_info.master_mode == 0) { + retry_cnt = 100; + while (retry_cnt) { + ret = exynos_dp_is_video_stream_on(); + if (ret != EXYNOS_DP_SUCCESS) { + if (retry_cnt == 0) { + printf("DP Timeout of video stream\n"); + return ret; + } + retry_cnt--; + mdelay(5); + } else + break; + } + } + + return ret; +} + +#ifdef CONFIG_OF_CONTROL +int exynos_dp_parse_dt(const void *blob, struct edp_device_info *edp_info) +{ + unsigned int node = fdtdec_next_compatible(blob, 0, + COMPAT_SAMSUNG_EXYNOS5_DP); + if (node <= 0) { + debug("exynos_dp: Can't get device node for dp\n"); + return -ENODEV; + } + + edp_info->disp_info.h_res = fdtdec_get_int(blob, node, + "samsung,h-res", 0); + edp_info->disp_info.h_sync_width = fdtdec_get_int(blob, node, + "samsung,h-sync-width", 0); + edp_info->disp_info.h_back_porch = fdtdec_get_int(blob, node, + "samsung,h-back-porch", 0); + edp_info->disp_info.h_front_porch = fdtdec_get_int(blob, node, + "samsung,h-front-porch", 0); + edp_info->disp_info.v_res = fdtdec_get_int(blob, node, + "samsung,v-res", 0); + edp_info->disp_info.v_sync_width = fdtdec_get_int(blob, node, + "samsung,v-sync-width", 0); + edp_info->disp_info.v_back_porch = fdtdec_get_int(blob, node, + "samsung,v-back-porch", 0); + edp_info->disp_info.v_front_porch = fdtdec_get_int(blob, node, + "samsung,v-front-porch", 0); + edp_info->disp_info.v_sync_rate = fdtdec_get_int(blob, node, + "samsung,v-sync-rate", 0); + + edp_info->lt_info.lt_status = fdtdec_get_int(blob, node, + "samsung,lt-status", 0); + + edp_info->video_info.master_mode = fdtdec_get_int(blob, node, + "samsung,master-mode", 0); + edp_info->video_info.bist_mode = fdtdec_get_int(blob, node, + "samsung,bist-mode", 0); + edp_info->video_info.bist_pattern = fdtdec_get_int(blob, node, + "samsung,bist-pattern", 0); + edp_info->video_info.h_sync_polarity = fdtdec_get_int(blob, node, + "samsung,h-sync-polarity", 0); + edp_info->video_info.v_sync_polarity = fdtdec_get_int(blob, node, + "samsung,v-sync-polarity", 0); + edp_info->video_info.interlaced = fdtdec_get_int(blob, node, + "samsung,interlaced", 0); + edp_info->video_info.color_space = fdtdec_get_int(blob, node, + "samsung,color-space", 0); + edp_info->video_info.dynamic_range = fdtdec_get_int(blob, node, + "samsung,dynamic-range", 0); + edp_info->video_info.ycbcr_coeff = fdtdec_get_int(blob, node, + "samsung,ycbcr-coeff", 0); + edp_info->video_info.color_depth = fdtdec_get_int(blob, node, + "samsung,color-depth", 0); + return 0; +} +#endif + +unsigned int exynos_init_dp(void) +{ + unsigned int ret; + struct edp_device_info *edp_info; + + edp_info = kzalloc(sizeof(struct edp_device_info), GFP_KERNEL); + if (!edp_info) { + debug("failed to allocate edp device object.\n"); + return -EFAULT; + } + +#ifdef CONFIG_OF_CONTROL + if (exynos_dp_parse_dt(gd->fdt_blob, edp_info)) + debug("unable to parse DP DT node\n"); +#else + edp_info = dp_pd->edp_dev_info; + if (edp_info == NULL) { + debug("failed to get edp_info data.\n"); + return -EFAULT; + } +#endif + + exynos_dp_set_base_addr(); + + exynos_dp_disp_info(&edp_info->disp_info); + + exynos_set_dp_phy(1); + + ret = exynos_dp_init_dp(); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP exynos_dp_init_dp() failed\n"); + return ret; + } + + ret = exynos_dp_handle_edid(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printf("EDP handle_edid fail\n"); + return ret; + } + + ret = exynos_dp_set_link_train(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP link training fail\n"); + return ret; + } + + exynos_dp_enable_scramble(DP_ENABLE); + exynos_dp_enable_rx_to_enhanced_mode(DP_ENABLE); + exynos_dp_enable_enhanced_mode(DP_ENABLE); + + exynos_dp_set_link_bandwidth(edp_info->lane_bw); + exynos_dp_set_lane_count(edp_info->lane_cnt); + + exynos_dp_init_video(); + ret = exynos_dp_config_video(edp_info); + if (ret != EXYNOS_DP_SUCCESS) { + printf("Exynos DP init failed\n"); + return ret; + } + + printf("Exynos DP init done\n"); + + return ret; +} + +void exynos_set_dp_platform_data(struct exynos_dp_platform_data *pd) +{ + if (pd == NULL) { + debug("pd is NULL\n"); + return; + } + + dp_pd = pd; +} diff --git a/qemu/roms/u-boot/drivers/video/exynos_dp_lowlevel.c b/qemu/roms/u-boot/drivers/video/exynos_dp_lowlevel.c new file mode 100644 index 000000000..bf0ea108e --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_dp_lowlevel.c @@ -0,0 +1,1257 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +struct exynos_dp *dp_regs; + +void exynos_dp_set_base_addr(void) +{ +#ifdef CONFIG_OF_CONTROL + unsigned int node = fdtdec_next_compatible(gd->fdt_blob, + 0, COMPAT_SAMSUNG_EXYNOS5_DP); + if (node <= 0) + debug("exynos_dp: Can't get device node for dp\n"); + + dp_regs = (struct exynos_dp *)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); + if (dp_regs == NULL) + debug("Can't get the DP base address\n"); +#else + dp_regs = (struct exynos_dp *)samsung_get_base_dp(); +#endif +} + +static void exynos_dp_enable_video_input(unsigned int enable) +{ + unsigned int reg; + + reg = readl(&dp_regs->video_ctl1); + reg &= ~VIDEO_EN_MASK; + + /* enable video input*/ + if (enable) + reg |= VIDEO_EN_MASK; + + writel(reg, &dp_regs->video_ctl1); + + return; +} + +void exynos_dp_enable_video_bist(unsigned int enable) +{ + /*enable video bist*/ + unsigned int reg; + + reg = readl(&dp_regs->video_ctl4); + reg &= ~VIDEO_BIST_MASK; + + /*enable video bist*/ + if (enable) + reg |= VIDEO_BIST_MASK; + + writel(reg, &dp_regs->video_ctl4); + + return; +} + +void exynos_dp_enable_video_mute(unsigned int enable) +{ + unsigned int reg; + + reg = readl(&dp_regs->video_ctl1); + reg &= ~(VIDEO_MUTE_MASK); + if (enable) + reg |= VIDEO_MUTE_MASK; + + writel(reg, &dp_regs->video_ctl1); + + return; +} + + +static void exynos_dp_init_analog_param(void) +{ + unsigned int reg; + + /* + * Set termination + * Normal bandgap, Normal swing, Tx terminal registor 61 ohm + * 24M Phy clock, TX digital logic power is 100:1.0625V + */ + reg = SEL_BG_NEW_BANDGAP | TX_TERMINAL_CTRL_61_OHM | + SWING_A_30PER_G_NORMAL; + writel(reg, &dp_regs->analog_ctl1); + + reg = SEL_24M | TX_DVDD_BIT_1_0625V; + writel(reg, &dp_regs->analog_ctl2); + + /* + * Set power source for internal clk driver to 1.0625v. + * Select current reference of TX driver current to 00:Ipp/2+Ic/2. + * Set VCO range of PLL +- 0uA + */ + reg = DRIVE_DVDD_BIT_1_0625V | SEL_CURRENT_DEFAULT | VCO_BIT_000_MICRO; + writel(reg, &dp_regs->analog_ctl3); + + /* + * Set AUX TX terminal resistor to 102 ohm + * Set AUX channel amplitude control + */ + reg = PD_RING_OSC | AUX_TERMINAL_CTRL_52_OHM | TX_CUR1_2X | TX_CUR_4_MA; + writel(reg, &dp_regs->pll_filter_ctl1); + + /* + * PLL loop filter bandwidth + * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz + * PLL digital power select: 1.2500V + */ + reg = CH3_AMP_0_MV | CH2_AMP_0_MV | CH1_AMP_0_MV | CH0_AMP_0_MV; + + writel(reg, &dp_regs->amp_tuning_ctl); + + /* + * PLL loop filter bandwidth + * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz + * PLL digital power select: 1.1250V + */ + reg = DP_PLL_LOOP_BIT_DEFAULT | DP_PLL_REF_BIT_1_1250V; + writel(reg, &dp_regs->pll_ctl); +} + +static void exynos_dp_init_interrupt(void) +{ + /* Set interrupt registers to initial states */ + + /* + * Disable interrupt + * INT pin assertion polarity. It must be configured + * correctly according to ICU setting. + * 1 = assert high, 0 = assert low + */ + writel(INT_POL, &dp_regs->int_ctl); + + /* Clear pending regisers */ + writel(0xff, &dp_regs->common_int_sta1); + writel(0xff, &dp_regs->common_int_sta2); + writel(0xff, &dp_regs->common_int_sta3); + writel(0xff, &dp_regs->common_int_sta4); + writel(0xff, &dp_regs->int_sta); + + /* 0:mask,1: unmask */ + writel(0x00, &dp_regs->int_sta_mask1); + writel(0x00, &dp_regs->int_sta_mask2); + writel(0x00, &dp_regs->int_sta_mask3); + writel(0x00, &dp_regs->int_sta_mask4); + writel(0x00, &dp_regs->int_sta_mask); +} + +void exynos_dp_reset(void) +{ + unsigned int reg_func_1; + + /*dp tx sw reset*/ + writel(RESET_DP_TX, &dp_regs->tx_sw_reset); + + exynos_dp_enable_video_input(DP_DISABLE); + exynos_dp_enable_video_bist(DP_DISABLE); + exynos_dp_enable_video_mute(DP_DISABLE); + + /* software reset */ + reg_func_1 = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N | + AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N | + HDCP_FUNC_EN_N | SW_FUNC_EN_N; + + writel(reg_func_1, &dp_regs->func_en1); + writel(reg_func_1, &dp_regs->func_en2); + + mdelay(1); + + exynos_dp_init_analog_param(); + exynos_dp_init_interrupt(); + + return; +} + +void exynos_dp_enable_sw_func(unsigned int enable) +{ + unsigned int reg; + + reg = readl(&dp_regs->func_en1); + reg &= ~(SW_FUNC_EN_N); + + if (!enable) + reg |= SW_FUNC_EN_N; + + writel(reg, &dp_regs->func_en1); + + return; +} + +unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable) +{ + unsigned int reg; + + reg = readl(&dp_regs->phy_pd); + switch (block) { + case AUX_BLOCK: + reg &= ~(AUX_PD); + if (enable) + reg |= AUX_PD; + break; + case CH0_BLOCK: + reg &= ~(CH0_PD); + if (enable) + reg |= CH0_PD; + break; + case CH1_BLOCK: + reg &= ~(CH1_PD); + if (enable) + reg |= CH1_PD; + break; + case CH2_BLOCK: + reg &= ~(CH2_PD); + if (enable) + reg |= CH2_PD; + break; + case CH3_BLOCK: + reg &= ~(CH3_PD); + if (enable) + reg |= CH3_PD; + break; + case ANALOG_TOTAL: + reg &= ~PHY_PD; + if (enable) + reg |= PHY_PD; + break; + case POWER_ALL: + reg &= ~(PHY_PD | AUX_PD | CH0_PD | CH1_PD | CH2_PD | + CH3_PD); + if (enable) + reg |= (PHY_PD | AUX_PD | CH0_PD | CH1_PD | + CH2_PD | CH3_PD); + break; + default: + printf("DP undefined block number : %d\n", block); + return -1; + } + + writel(reg, &dp_regs->phy_pd); + + return 0; +} + +unsigned int exynos_dp_get_pll_lock_status(void) +{ + unsigned int reg; + + reg = readl(&dp_regs->debug_ctl); + + if (reg & PLL_LOCK) + return PLL_LOCKED; + else + return PLL_UNLOCKED; +} + +static void exynos_dp_set_pll_power(unsigned int enable) +{ + unsigned int reg; + + reg = readl(&dp_regs->pll_ctl); + reg &= ~(DP_PLL_PD); + + if (!enable) + reg |= DP_PLL_PD; + + writel(reg, &dp_regs->pll_ctl); +} + +int exynos_dp_init_analog_func(void) +{ + int ret = EXYNOS_DP_SUCCESS; + unsigned int retry_cnt = 10; + unsigned int reg; + + /*Power On All Analog block */ + exynos_dp_set_analog_power_down(POWER_ALL, DP_DISABLE); + + reg = PLL_LOCK_CHG; + writel(reg, &dp_regs->common_int_sta1); + + reg = readl(&dp_regs->debug_ctl); + reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL); + writel(reg, &dp_regs->debug_ctl); + + /*Assert DP PLL Reset*/ + reg = readl(&dp_regs->pll_ctl); + reg |= DP_PLL_RESET; + writel(reg, &dp_regs->pll_ctl); + + mdelay(1); + + /*Deassert DP PLL Reset*/ + reg = readl(&dp_regs->pll_ctl); + reg &= ~(DP_PLL_RESET); + writel(reg, &dp_regs->pll_ctl); + + exynos_dp_set_pll_power(DP_ENABLE); + + while (exynos_dp_get_pll_lock_status() == PLL_UNLOCKED) { + mdelay(1); + retry_cnt--; + if (retry_cnt == 0) { + printf("DP dp's pll lock failed : retry : %d\n", + retry_cnt); + return -EINVAL; + } + } + + debug("dp's pll lock success(%d)\n", retry_cnt); + + /* Enable Serdes FIFO function and Link symbol clock domain module */ + reg = readl(&dp_regs->func_en2); + reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N + | AUX_FUNC_EN_N); + writel(reg, &dp_regs->func_en2); + + return ret; +} + +void exynos_dp_init_hpd(void) +{ + unsigned int reg; + + /* Clear interrupts releated to Hot Plug Dectect */ + reg = HOTPLUG_CHG | HPD_LOST | PLUG; + writel(reg, &dp_regs->common_int_sta4); + + reg = INT_HPD; + writel(reg, &dp_regs->int_sta); + + reg = readl(&dp_regs->sys_ctl3); + reg &= ~(F_HPD | HPD_CTRL); + writel(reg, &dp_regs->sys_ctl3); + + return; +} + +static inline void exynos_dp_reset_aux(void) +{ + unsigned int reg; + + /* Disable AUX channel module */ + reg = readl(&dp_regs->func_en2); + reg |= AUX_FUNC_EN_N; + writel(reg, &dp_regs->func_en2); + + return; +} + +void exynos_dp_init_aux(void) +{ + unsigned int reg; + + /* Clear inerrupts related to AUX channel */ + reg = RPLY_RECEIV | AUX_ERR; + writel(reg, &dp_regs->int_sta); + + exynos_dp_reset_aux(); + + /* Disable AUX transaction H/W retry */ + reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(3)| + AUX_HW_RETRY_INTERVAL_600_MICROSECONDS; + writel(reg, &dp_regs->aux_hw_retry_ctl); + + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ + reg = DEFER_CTRL_EN | DEFER_COUNT(1); + writel(reg, &dp_regs->aux_ch_defer_ctl); + + /* Enable AUX channel module */ + reg = readl(&dp_regs->func_en2); + reg &= ~AUX_FUNC_EN_N; + writel(reg, &dp_regs->func_en2); + + return; +} + +void exynos_dp_config_interrupt(void) +{ + unsigned int reg; + + /* 0: mask, 1: unmask */ + reg = COMMON_INT_MASK_1; + writel(reg, &dp_regs->common_int_mask1); + + reg = COMMON_INT_MASK_2; + writel(reg, &dp_regs->common_int_mask2); + + reg = COMMON_INT_MASK_3; + writel(reg, &dp_regs->common_int_mask3); + + reg = COMMON_INT_MASK_4; + writel(reg, &dp_regs->common_int_mask4); + + reg = INT_STA_MASK; + writel(reg, &dp_regs->int_sta_mask); + + return; +} + +unsigned int exynos_dp_get_plug_in_status(void) +{ + unsigned int reg; + + reg = readl(&dp_regs->sys_ctl3); + if (reg & HPD_STATUS) + return 0; + + return -1; +} + +unsigned int exynos_dp_detect_hpd(void) +{ + int timeout_loop = DP_TIMEOUT_LOOP_COUNT; + + mdelay(2); + + while (exynos_dp_get_plug_in_status() != 0) { + if (timeout_loop == 0) + return -EINVAL; + mdelay(10); + timeout_loop--; + } + + return EXYNOS_DP_SUCCESS; +} + +unsigned int exynos_dp_start_aux_transaction(void) +{ + unsigned int reg; + unsigned int ret = 0; + unsigned int retry_cnt; + + /* Enable AUX CH operation */ + reg = readl(&dp_regs->aux_ch_ctl2); + reg |= AUX_EN; + writel(reg, &dp_regs->aux_ch_ctl2); + + retry_cnt = 10; + while (retry_cnt) { + reg = readl(&dp_regs->int_sta); + if (!(reg & RPLY_RECEIV)) { + if (retry_cnt == 0) { + printf("DP Reply Timeout!!\n"); + ret = -EAGAIN; + return ret; + } + mdelay(1); + retry_cnt--; + } else + break; + } + + /* Clear interrupt source for AUX CH command reply */ + writel(reg, &dp_regs->int_sta); + + /* Clear interrupt source for AUX CH access error */ + reg = readl(&dp_regs->int_sta); + if (reg & AUX_ERR) { + printf("DP Aux Access Error\n"); + writel(AUX_ERR, &dp_regs->int_sta); + ret = -EAGAIN; + return ret; + } + + /* Check AUX CH error access status */ + reg = readl(&dp_regs->aux_ch_sta); + if ((reg & AUX_STATUS_MASK) != 0) { + debug("DP AUX CH error happens: %x\n", reg & AUX_STATUS_MASK); + ret = -EAGAIN; + return ret; + } + + return EXYNOS_DP_SUCCESS; +} + +unsigned int exynos_dp_write_byte_to_dpcd(unsigned int reg_addr, + unsigned char data) +{ + unsigned int reg, ret; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, &dp_regs->buffer_data_ctl); + + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr); + writel(reg, &dp_regs->aux_addr_7_0); + reg = AUX_ADDR_15_8(reg_addr); + writel(reg, &dp_regs->aux_addr_15_8); + reg = AUX_ADDR_19_16(reg_addr); + writel(reg, &dp_regs->aux_addr_19_16); + + /* Write data buffer */ + reg = (unsigned int)data; + writel(reg, &dp_regs->buf_data0); + + /* + * Set DisplayPort transaction and write 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + writel(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + ret = exynos_dp_start_aux_transaction(); + if (ret != EXYNOS_DP_SUCCESS) { + printf("DP Aux transaction failed\n"); + return ret; + } + + return ret; +} + +unsigned int exynos_dp_read_byte_from_dpcd(unsigned int reg_addr, + unsigned char *data) +{ + unsigned int reg; + int retval; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, &dp_regs->buffer_data_ctl); + + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr); + writel(reg, &dp_regs->aux_addr_7_0); + reg = AUX_ADDR_15_8(reg_addr); + writel(reg, &dp_regs->aux_addr_15_8); + reg = AUX_ADDR_19_16(reg_addr); + writel(reg, &dp_regs->aux_addr_19_16); + + /* + * Set DisplayPort transaction and read 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + writel(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(); + if (!retval) + debug("DP Aux Transaction fail!\n"); + + /* Read data buffer */ + reg = readl(&dp_regs->buf_data0); + *data = (unsigned char)(reg & 0xff); + + return retval; +} + +unsigned int exynos_dp_write_bytes_to_dpcd(unsigned int reg_addr, + unsigned int count, + unsigned char data[]) +{ + unsigned int reg; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + unsigned int retry_cnt; + unsigned int ret = 0; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, &dp_regs->buffer_data_ctl); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + retry_cnt = 5; + while (retry_cnt) { + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr + start_offset); + writel(reg, &dp_regs->aux_addr_7_0); + reg = AUX_ADDR_15_8(reg_addr + start_offset); + writel(reg, &dp_regs->aux_addr_15_8); + reg = AUX_ADDR_19_16(reg_addr + start_offset); + writel(reg, &dp_regs->aux_addr_19_16); + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + reg = data[start_offset + cur_data_idx]; + writel(reg, (unsigned int)&dp_regs->buf_data0 + + (4 * cur_data_idx)); + } + /* + * Set DisplayPort transaction and write + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + writel(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + ret = exynos_dp_start_aux_transaction(); + if (ret != EXYNOS_DP_SUCCESS) { + if (retry_cnt == 0) { + printf("DP Aux Transaction failed\n"); + return ret; + } + retry_cnt--; + } else + break; + } + start_offset += cur_data_count; + } + + return ret; +} + +unsigned int exynos_dp_read_bytes_from_dpcd(unsigned int reg_addr, + unsigned int count, + unsigned char data[]) +{ + unsigned int reg; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + unsigned int retry_cnt; + unsigned int ret = 0; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, &dp_regs->buffer_data_ctl); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + retry_cnt = 5; + while (retry_cnt) { + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr + start_offset); + writel(reg, &dp_regs->aux_addr_7_0); + reg = AUX_ADDR_15_8(reg_addr + start_offset); + writel(reg, &dp_regs->aux_addr_15_8); + reg = AUX_ADDR_19_16(reg_addr + start_offset); + writel(reg, &dp_regs->aux_addr_19_16); + /* + * Set DisplayPort transaction and read + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + writel(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + ret = exynos_dp_start_aux_transaction(); + if (ret != EXYNOS_DP_SUCCESS) { + if (retry_cnt == 0) { + printf("DP Aux Transaction failed\n"); + return ret; + } + retry_cnt--; + } else + break; + } + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + reg = readl((unsigned int)&dp_regs->buf_data0 + + 4 * cur_data_idx); + data[start_offset + cur_data_idx] = (unsigned char)reg; + } + + start_offset += cur_data_count; + } + + return ret; +} + +int exynos_dp_select_i2c_device(unsigned int device_addr, + unsigned int reg_addr) +{ + unsigned int reg; + int retval; + + /* Set EDID device address */ + reg = device_addr; + writel(reg, &dp_regs->aux_addr_7_0); + writel(0x0, &dp_regs->aux_addr_15_8); + writel(0x0, &dp_regs->aux_addr_19_16); + + /* Set offset from base address of EDID device */ + writel(reg_addr, &dp_regs->buf_data0); + + /* + * Set I2C transaction and write address + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | + AUX_TX_COMM_WRITE; + writel(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(); + if (retval != 0) + printf("%s: DP Aux Transaction fail!\n", __func__); + + return retval; +} + +int exynos_dp_read_byte_from_i2c(unsigned int device_addr, + unsigned int reg_addr, + unsigned int *data) +{ + unsigned int reg; + int i; + int retval; + + for (i = 0; i < 10; i++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, &dp_regs->buffer_data_ctl); + + /* Select EDID device */ + retval = exynos_dp_select_i2c_device(device_addr, reg_addr); + if (retval != 0) { + printf("DP Select EDID device fail. retry !\n"); + continue; + } + + /* + * Set I2C transaction and read data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + writel(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(); + if (retval != EXYNOS_DP_SUCCESS) + printf("%s: DP Aux Transaction fail!\n", __func__); + } + + /* Read data */ + if (retval == 0) + *data = readl(&dp_regs->buf_data0); + + return retval; +} + +int exynos_dp_read_bytes_from_i2c(unsigned int device_addr, + unsigned int reg_addr, unsigned int count, unsigned char edid[]) +{ + unsigned int reg; + unsigned int i, j; + unsigned int cur_data_idx; + unsigned int defer = 0; + int retval = 0; + + for (i = 0; i < count; i += 16) { /* use 16 burst */ + for (j = 0; j < 100; j++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, &dp_regs->buffer_data_ctl); + + /* Set normal AUX CH command */ + reg = readl(&dp_regs->aux_ch_ctl2); + reg &= ~ADDR_ONLY; + writel(reg, &dp_regs->aux_ch_ctl2); + + /* + * If Rx sends defer, Tx sends only reads + * request without sending addres + */ + if (!defer) + retval = + exynos_dp_select_i2c_device(device_addr, + reg_addr + i); + else + defer = 0; + + if (retval == EXYNOS_DP_SUCCESS) { + /* + * Set I2C transaction and write data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(16) | + AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + writel(reg, &dp_regs->aux_ch_ctl1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(); + if (retval == 0) + break; + else + printf("DP Aux Transaction fail!\n"); + } + /* Check if Rx sends defer */ + reg = readl(&dp_regs->aux_rx_comm); + if (reg == AUX_RX_COMM_AUX_DEFER || + reg == AUX_RX_COMM_I2C_DEFER) { + printf("DP Defer: %d\n\n", reg); + defer = 1; + } + } + + for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { + reg = readl((unsigned int)&dp_regs->buf_data0 + + 4 * cur_data_idx); + edid[i + cur_data_idx] = (unsigned char)reg; + } + } + + return retval; +} + +void exynos_dp_reset_macro(void) +{ + unsigned int reg; + + reg = readl(&dp_regs->phy_test); + reg |= MACRO_RST; + writel(reg, &dp_regs->phy_test); + + /* 10 us is the minimum Macro reset time. */ + mdelay(1); + + reg &= ~MACRO_RST; + writel(reg, &dp_regs->phy_test); +} + +void exynos_dp_set_link_bandwidth(unsigned char bwtype) +{ + unsigned int reg; + + reg = (unsigned int)bwtype; + + /* Set bandwidth to 2.7G or 1.62G */ + if ((bwtype == DP_LANE_BW_1_62) || (bwtype == DP_LANE_BW_2_70)) + writel(reg, &dp_regs->link_bw_set); +} + +unsigned char exynos_dp_get_link_bandwidth(void) +{ + unsigned char ret; + unsigned int reg; + + reg = readl(&dp_regs->link_bw_set); + ret = (unsigned char)reg; + + return ret; +} + +void exynos_dp_set_lane_count(unsigned char count) +{ + unsigned int reg; + + reg = (unsigned int)count; + + if ((count == DP_LANE_CNT_1) || (count == DP_LANE_CNT_2) || + (count == DP_LANE_CNT_4)) + writel(reg, &dp_regs->lane_count_set); +} + +unsigned int exynos_dp_get_lane_count(void) +{ + unsigned int reg; + + reg = readl(&dp_regs->lane_count_set); + + return reg; +} + +unsigned char exynos_dp_get_lanex_pre_emphasis(unsigned char lanecnt) +{ + unsigned int reg_list[DP_LANE_CNT_4] = { + (unsigned int)&dp_regs->ln0_link_training_ctl, + (unsigned int)&dp_regs->ln1_link_training_ctl, + (unsigned int)&dp_regs->ln2_link_training_ctl, + (unsigned int)&dp_regs->ln3_link_training_ctl, + }; + + return readl(reg_list[lanecnt]); +} + +void exynos_dp_set_lanex_pre_emphasis(unsigned char request_val, + unsigned char lanecnt) +{ + unsigned int reg_list[DP_LANE_CNT_4] = { + (unsigned int)&dp_regs->ln0_link_training_ctl, + (unsigned int)&dp_regs->ln1_link_training_ctl, + (unsigned int)&dp_regs->ln2_link_training_ctl, + (unsigned int)&dp_regs->ln3_link_training_ctl, + }; + + writel(request_val, reg_list[lanecnt]); +} + +void exynos_dp_set_lane_pre_emphasis(unsigned int level, unsigned char lanecnt) +{ + unsigned char i; + unsigned int reg; + unsigned int reg_list[DP_LANE_CNT_4] = { + (unsigned int)&dp_regs->ln0_link_training_ctl, + (unsigned int)&dp_regs->ln1_link_training_ctl, + (unsigned int)&dp_regs->ln2_link_training_ctl, + (unsigned int)&dp_regs->ln3_link_training_ctl, + }; + unsigned int reg_shift[DP_LANE_CNT_4] = { + PRE_EMPHASIS_SET_0_SHIFT, + PRE_EMPHASIS_SET_1_SHIFT, + PRE_EMPHASIS_SET_2_SHIFT, + PRE_EMPHASIS_SET_3_SHIFT + }; + + for (i = 0; i < lanecnt; i++) { + reg = level << reg_shift[i]; + writel(reg, reg_list[i]); + } +} + +void exynos_dp_set_training_pattern(unsigned int pattern) +{ + unsigned int reg = 0; + + switch (pattern) { + case PRBS7: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7; + break; + case D10_2: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2; + break; + case TRAINING_PTN1: + reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1; + break; + case TRAINING_PTN2: + reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2; + break; + case DP_NONE: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_DISABLE | + SW_TRAINING_PATTERN_SET_NORMAL; + break; + default: + break; + } + + writel(reg, &dp_regs->training_ptn_set); +} + +void exynos_dp_enable_enhanced_mode(unsigned char enable) +{ + unsigned int reg; + + reg = readl(&dp_regs->sys_ctl4); + reg &= ~ENHANCED; + + if (enable) + reg |= ENHANCED; + + writel(reg, &dp_regs->sys_ctl4); +} + +void exynos_dp_enable_scrambling(unsigned int enable) +{ + unsigned int reg; + + reg = readl(&dp_regs->training_ptn_set); + reg &= ~(SCRAMBLING_DISABLE); + + if (!enable) + reg |= SCRAMBLING_DISABLE; + + writel(reg, &dp_regs->training_ptn_set); +} + +int exynos_dp_init_video(void) +{ + unsigned int reg; + + /* Clear VID_CLK_CHG[1] and VID_FORMAT_CHG[3] and VSYNC_DET[7] */ + reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; + writel(reg, &dp_regs->common_int_sta1); + + /* I_STRM__CLK detect : DE_CTL : Auto detect */ + reg &= ~DET_CTRL; + writel(reg, &dp_regs->sys_ctl1); + + return 0; +} + +void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info) +{ + unsigned int reg; + + /* Video Slave mode setting */ + reg = readl(&dp_regs->func_en1); + reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N); + reg |= MASTER_VID_FUNC_EN_N; + writel(reg, &dp_regs->func_en1); + + /* Configure Interlaced for slave mode video */ + reg = readl(&dp_regs->video_ctl10); + reg &= ~INTERACE_SCAN_CFG; + reg |= (video_info->interlaced << INTERACE_SCAN_CFG_SHIFT); + writel(reg, &dp_regs->video_ctl10); + + /* Configure V sync polarity for slave mode video */ + reg = readl(&dp_regs->video_ctl10); + reg &= ~VSYNC_POLARITY_CFG; + reg |= (video_info->v_sync_polarity << V_S_POLARITY_CFG_SHIFT); + writel(reg, &dp_regs->video_ctl10); + + /* Configure H sync polarity for slave mode video */ + reg = readl(&dp_regs->video_ctl10); + reg &= ~HSYNC_POLARITY_CFG; + reg |= (video_info->h_sync_polarity << H_S_POLARITY_CFG_SHIFT); + writel(reg, &dp_regs->video_ctl10); + + /*Set video mode to slave mode */ + reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE; + writel(reg, &dp_regs->soc_general_ctl); +} + +void exynos_dp_set_video_color_format(struct edp_video_info *video_info) +{ + unsigned int reg; + + /* Configure the input color depth, color space, dynamic range */ + reg = (video_info->dynamic_range << IN_D_RANGE_SHIFT) | + (video_info->color_depth << IN_BPC_SHIFT) | + (video_info->color_space << IN_COLOR_F_SHIFT); + writel(reg, &dp_regs->video_ctl2); + + /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ + reg = readl(&dp_regs->video_ctl3); + reg &= ~IN_YC_COEFFI_MASK; + if (video_info->ycbcr_coeff) + reg |= IN_YC_COEFFI_ITU709; + else + reg |= IN_YC_COEFFI_ITU601; + writel(reg, &dp_regs->video_ctl3); +} + +int exynos_dp_config_video_bist(struct edp_device_info *edp_info) +{ + unsigned int reg; + unsigned int bist_type = 0; + struct edp_video_info video_info = edp_info->video_info; + + /* For master mode, you don't need to set the video format */ + if (video_info.master_mode == 0) { + writel(TOTAL_LINE_CFG_L(edp_info->disp_info.v_total), + &dp_regs->total_ln_cfg_l); + writel(TOTAL_LINE_CFG_H(edp_info->disp_info.v_total), + &dp_regs->total_ln_cfg_h); + writel(ACTIVE_LINE_CFG_L(edp_info->disp_info.v_res), + &dp_regs->active_ln_cfg_l); + writel(ACTIVE_LINE_CFG_H(edp_info->disp_info.v_res), + &dp_regs->active_ln_cfg_h); + writel(edp_info->disp_info.v_sync_width, + &dp_regs->vsw_cfg); + writel(edp_info->disp_info.v_back_porch, + &dp_regs->vbp_cfg); + writel(edp_info->disp_info.v_front_porch, + &dp_regs->vfp_cfg); + + writel(TOTAL_PIXEL_CFG_L(edp_info->disp_info.h_total), + &dp_regs->total_pix_cfg_l); + writel(TOTAL_PIXEL_CFG_H(edp_info->disp_info.h_total), + &dp_regs->total_pix_cfg_h); + writel(ACTIVE_PIXEL_CFG_L(edp_info->disp_info.h_res), + &dp_regs->active_pix_cfg_l); + writel(ACTIVE_PIXEL_CFG_H(edp_info->disp_info.h_res), + &dp_regs->active_pix_cfg_h); + writel(H_F_PORCH_CFG_L(edp_info->disp_info.h_front_porch), + &dp_regs->hfp_cfg_l); + writel(H_F_PORCH_CFG_H(edp_info->disp_info.h_front_porch), + &dp_regs->hfp_cfg_h); + writel(H_SYNC_PORCH_CFG_L(edp_info->disp_info.h_sync_width), + &dp_regs->hsw_cfg_l); + writel(H_SYNC_PORCH_CFG_H(edp_info->disp_info.h_sync_width), + &dp_regs->hsw_cfg_h); + writel(H_B_PORCH_CFG_L(edp_info->disp_info.h_back_porch), + &dp_regs->hbp_cfg_l); + writel(H_B_PORCH_CFG_H(edp_info->disp_info.h_back_porch), + &dp_regs->hbp_cfg_h); + + /* + * Set SLAVE_I_SCAN_CFG[2], VSYNC_P_CFG[1], + * HSYNC_P_CFG[0] properly + */ + reg = (video_info.interlaced << INTERACE_SCAN_CFG_SHIFT | + video_info.v_sync_polarity << V_S_POLARITY_CFG_SHIFT | + video_info.h_sync_polarity << H_S_POLARITY_CFG_SHIFT); + writel(reg, &dp_regs->video_ctl10); + } + + /* BIST color bar width set--set to each bar is 32 pixel width */ + switch (video_info.bist_pattern) { + case COLORBAR_32: + bist_type = BIST_WIDTH_BAR_32_PIXEL | + BIST_TYPE_COLOR_BAR; + break; + case COLORBAR_64: + bist_type = BIST_WIDTH_BAR_64_PIXEL | + BIST_TYPE_COLOR_BAR; + break; + case WHITE_GRAY_BALCKBAR_32: + bist_type = BIST_WIDTH_BAR_32_PIXEL | + BIST_TYPE_WHITE_GRAY_BLACK_BAR; + break; + case WHITE_GRAY_BALCKBAR_64: + bist_type = BIST_WIDTH_BAR_64_PIXEL | + BIST_TYPE_WHITE_GRAY_BLACK_BAR; + break; + case MOBILE_WHITEBAR_32: + bist_type = BIST_WIDTH_BAR_32_PIXEL | + BIST_TYPE_MOBILE_WHITE_BAR; + break; + case MOBILE_WHITEBAR_64: + bist_type = BIST_WIDTH_BAR_64_PIXEL | + BIST_TYPE_MOBILE_WHITE_BAR; + break; + default: + return -1; + } + + reg = bist_type; + writel(reg, &dp_regs->video_ctl4); + + return 0; +} + +unsigned int exynos_dp_is_slave_video_stream_clock_on(void) +{ + unsigned int reg; + + /* Update Video stream clk detect status */ + reg = readl(&dp_regs->sys_ctl1); + writel(reg, &dp_regs->sys_ctl1); + + reg = readl(&dp_regs->sys_ctl1); + + if (!(reg & DET_STA)) { + debug("DP Input stream clock not detected.\n"); + return -EIO; + } + + return EXYNOS_DP_SUCCESS; +} + +void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value, + unsigned int n_value) +{ + unsigned int reg; + + if (type == REGISTER_M) { + reg = readl(&dp_regs->sys_ctl4); + reg |= FIX_M_VID; + writel(reg, &dp_regs->sys_ctl4); + reg = M_VID0_CFG(m_value); + writel(reg, &dp_regs->m_vid0); + reg = M_VID1_CFG(m_value); + writel(reg, &dp_regs->m_vid1); + reg = M_VID2_CFG(m_value); + writel(reg, &dp_regs->m_vid2); + + reg = N_VID0_CFG(n_value); + writel(reg, &dp_regs->n_vid0); + reg = N_VID1_CFG(n_value); + writel(reg, &dp_regs->n_vid1); + reg = N_VID2_CFG(n_value); + writel(reg, &dp_regs->n_vid2); + } else { + reg = readl(&dp_regs->sys_ctl4); + reg &= ~FIX_M_VID; + writel(reg, &dp_regs->sys_ctl4); + } +} + +void exynos_dp_set_video_timing_mode(unsigned int type) +{ + unsigned int reg; + + reg = readl(&dp_regs->video_ctl10); + reg &= ~FORMAT_SEL; + + if (type != VIDEO_TIMING_FROM_CAPTURE) + reg |= FORMAT_SEL; + + writel(reg, &dp_regs->video_ctl10); +} + +void exynos_dp_enable_video_master(unsigned int enable) +{ + unsigned int reg; + + reg = readl(&dp_regs->soc_general_ctl); + if (enable) { + reg &= ~VIDEO_MODE_MASK; + reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE; + } else { + reg &= ~VIDEO_MODE_MASK; + reg |= VIDEO_MODE_SLAVE_MODE; + } + + writel(reg, &dp_regs->soc_general_ctl); +} + +void exynos_dp_start_video(void) +{ + unsigned int reg; + + /* Enable Video input and disable Mute */ + reg = readl(&dp_regs->video_ctl1); + reg |= VIDEO_EN; + writel(reg, &dp_regs->video_ctl1); +} + +unsigned int exynos_dp_is_video_stream_on(void) +{ + unsigned int reg; + + /* Update STRM_VALID */ + reg = readl(&dp_regs->sys_ctl3); + writel(reg, &dp_regs->sys_ctl3); + + reg = readl(&dp_regs->sys_ctl3); + if (!(reg & STRM_VALID)) + return -EIO; + + return EXYNOS_DP_SUCCESS; +} diff --git a/qemu/roms/u-boot/drivers/video/exynos_dp_lowlevel.h b/qemu/roms/u-boot/drivers/video/exynos_dp_lowlevel.h new file mode 100644 index 000000000..865168152 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_dp_lowlevel.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _EXYNOS_EDP_LOWLEVEL_H +#define _EXYNOS_EDP_LOWLEVEL_H + +void exynos_dp_enable_video_bist(unsigned int enable); +void exynos_dp_enable_video_mute(unsigned int enable); +void exynos_dp_reset(void); +void exynos_dp_enable_sw_func(unsigned int enable); +unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable); +unsigned int exynos_dp_get_pll_lock_status(void); +int exynos_dp_init_analog_func(void); +void exynos_dp_init_hpd(void); +void exynos_dp_init_aux(void); +void exynos_dp_config_interrupt(void); +unsigned int exynos_dp_get_plug_in_status(void); +unsigned int exynos_dp_detect_hpd(void); +unsigned int exynos_dp_start_aux_transaction(void); +unsigned int exynos_dp_write_byte_to_dpcd(unsigned int reg_addr, + unsigned char data); +unsigned int exynos_dp_read_byte_from_dpcd(unsigned int reg_addr, + unsigned char *data); +unsigned int exynos_dp_write_bytes_to_dpcd(unsigned int reg_addr, + unsigned int count, + unsigned char data[]); +unsigned int exynos_dp_read_bytes_from_dpcd( unsigned int reg_addr, + unsigned int count, + unsigned char data[]); +int exynos_dp_select_i2c_device( unsigned int device_addr, + unsigned int reg_addr); +int exynos_dp_read_byte_from_i2c(unsigned int device_addr, + unsigned int reg_addr, unsigned int *data); +int exynos_dp_read_bytes_from_i2c(unsigned int device_addr, + unsigned int reg_addr, unsigned int count, + unsigned char edid[]); +void exynos_dp_reset_macro(void); +void exynos_dp_set_link_bandwidth(unsigned char bwtype); +unsigned char exynos_dp_get_link_bandwidth(void); +void exynos_dp_set_lane_count(unsigned char count); +unsigned int exynos_dp_get_lane_count(void); +unsigned char exynos_dp_get_lanex_pre_emphasis(unsigned char lanecnt); +void exynos_dp_set_lane_pre_emphasis(unsigned int level, + unsigned char lanecnt); +void exynos_dp_set_lanex_pre_emphasis(unsigned char request_val, + unsigned char lanecnt); +void exynos_dp_set_training_pattern(unsigned int pattern); +void exynos_dp_enable_enhanced_mode(unsigned char enable); +void exynos_dp_enable_scrambling(unsigned int enable); +int exynos_dp_init_video(void); +void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info); +void exynos_dp_set_video_color_format(struct edp_video_info *video_info); +int exynos_dp_config_video_bist(struct edp_device_info *edp_info); +unsigned int exynos_dp_is_slave_video_stream_clock_on(void); +void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value, + unsigned int n_value); +void exynos_dp_set_video_timing_mode(unsigned int type); +void exynos_dp_enable_video_master(unsigned int enable); +void exynos_dp_start_video(void); +unsigned int exynos_dp_is_video_stream_on(void); +void exynos_dp_set_base_addr(void); + +#endif /* _EXYNOS_DP_LOWLEVEL_H */ diff --git a/qemu/roms/u-boot/drivers/video/exynos_fb.c b/qemu/roms/u-boot/drivers/video/exynos_fb.c new file mode 100644 index 000000000..e1e0d802f --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_fb.c @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exynos_fb.h" + +DECLARE_GLOBAL_DATA_PTR; + +static unsigned int panel_width, panel_height; + +/* + * board_init_f(arch/arm/lib/board.c) calls lcd_setmem() which needs + * panel_info.vl_col, panel_info.vl_row and panel_info.vl_bpix to reserve + * FB memory at a very early stage, i.e even before exynos_fimd_parse_dt() + * is called. So, we are forced to statically assign it. + */ +#ifdef CONFIG_OF_CONTROL +vidinfo_t panel_info = { + .vl_col = LCD_XRES, + .vl_row = LCD_YRES, + .vl_bpix = LCD_COLOR16, +}; +#endif + +static void exynos_lcd_init_mem(void *lcdbase, vidinfo_t *vid) +{ + unsigned long palette_size; + unsigned int fb_size; + + fb_size = vid->vl_row * vid->vl_col * (NBITS(vid->vl_bpix) >> 3); + + palette_size = NBITS(vid->vl_bpix) == 8 ? 256 : 16; + + exynos_fimd_lcd_init_mem((unsigned long)lcdbase, + (unsigned long)fb_size, palette_size); +} + +static void exynos_lcd_init(vidinfo_t *vid) +{ + exynos_fimd_lcd_init(vid); + + /* Enable flushing after LCD writes if requested */ + lcd_set_flush_dcache(1); +} + +void __exynos_cfg_lcd_gpio(void) +{ +} +void exynos_cfg_lcd_gpio(void) + __attribute__((weak, alias("__exynos_cfg_lcd_gpio"))); + +void __exynos_backlight_on(unsigned int onoff) +{ +} +void exynos_backlight_on(unsigned int onoff) + __attribute__((weak, alias("__exynos_cfg_lcd_gpio"))); + +void __exynos_reset_lcd(void) +{ +} +void exynos_reset_lcd(void) + __attribute__((weak, alias("__exynos_reset_lcd"))); + +void __exynos_lcd_power_on(void) +{ +} +void exynos_lcd_power_on(void) + __attribute__((weak, alias("__exynos_lcd_power_on"))); + +void __exynos_cfg_ldo(void) +{ +} +void exynos_cfg_ldo(void) + __attribute__((weak, alias("__exynos_cfg_ldo"))); + +void __exynos_enable_ldo(unsigned int onoff) +{ +} +void exynos_enable_ldo(unsigned int onoff) + __attribute__((weak, alias("__exynos_enable_ldo"))); + +void __exynos_backlight_reset(void) +{ +} +void exynos_backlight_reset(void) + __attribute__((weak, alias("__exynos_backlight_reset"))); + +int __exynos_lcd_misc_init(vidinfo_t *vid) +{ + return 0; +} +int exynos_lcd_misc_init(vidinfo_t *vid) + __attribute__((weak, alias("__exynos_lcd_misc_init"))); + +static void lcd_panel_on(vidinfo_t *vid) +{ + udelay(vid->init_delay); + + exynos_backlight_reset(); + + exynos_cfg_lcd_gpio(); + + exynos_lcd_power_on(); + + udelay(vid->power_on_delay); + + if (vid->dp_enabled) + exynos_init_dp(); + + exynos_reset_lcd(); + + udelay(vid->reset_delay); + + exynos_backlight_on(1); + + exynos_cfg_ldo(); + + exynos_enable_ldo(1); + + if (vid->mipi_enabled) + exynos_mipi_dsi_init(); +} + +#ifdef CONFIG_OF_CONTROL +int exynos_fimd_parse_dt(const void *blob) +{ + unsigned int node; + node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_FIMD); + if (node <= 0) { + debug("exynos_fb: Can't get device node for fimd\n"); + return -ENODEV; + } + + panel_info.vl_col = fdtdec_get_int(blob, node, "samsung,vl-col", 0); + if (panel_info.vl_col == 0) { + debug("Can't get XRES\n"); + return -ENXIO; + } + + panel_info.vl_row = fdtdec_get_int(blob, node, "samsung,vl-row", 0); + if (panel_info.vl_row == 0) { + debug("Can't get YRES\n"); + return -ENXIO; + } + + panel_info.vl_width = fdtdec_get_int(blob, node, + "samsung,vl-width", 0); + + panel_info.vl_height = fdtdec_get_int(blob, node, + "samsung,vl-height", 0); + + panel_info.vl_freq = fdtdec_get_int(blob, node, "samsung,vl-freq", 0); + if (panel_info.vl_freq == 0) { + debug("Can't get refresh rate\n"); + return -ENXIO; + } + + if (fdtdec_get_bool(blob, node, "samsung,vl-clkp")) + panel_info.vl_clkp = CONFIG_SYS_LOW; + + if (fdtdec_get_bool(blob, node, "samsung,vl-oep")) + panel_info.vl_oep = CONFIG_SYS_LOW; + + if (fdtdec_get_bool(blob, node, "samsung,vl-hsp")) + panel_info.vl_hsp = CONFIG_SYS_LOW; + + if (fdtdec_get_bool(blob, node, "samsung,vl-vsp")) + panel_info.vl_vsp = CONFIG_SYS_LOW; + + if (fdtdec_get_bool(blob, node, "samsung,vl-dp")) + panel_info.vl_dp = CONFIG_SYS_LOW; + + panel_info.vl_bpix = fdtdec_get_int(blob, node, "samsung,vl-bpix", 0); + if (panel_info.vl_bpix == 0) { + debug("Can't get bits per pixel\n"); + return -ENXIO; + } + + panel_info.vl_hspw = fdtdec_get_int(blob, node, "samsung,vl-hspw", 0); + if (panel_info.vl_hspw == 0) { + debug("Can't get hsync width\n"); + return -ENXIO; + } + + panel_info.vl_hfpd = fdtdec_get_int(blob, node, "samsung,vl-hfpd", 0); + if (panel_info.vl_hfpd == 0) { + debug("Can't get right margin\n"); + return -ENXIO; + } + + panel_info.vl_hbpd = (u_char)fdtdec_get_int(blob, node, + "samsung,vl-hbpd", 0); + if (panel_info.vl_hbpd == 0) { + debug("Can't get left margin\n"); + return -ENXIO; + } + + panel_info.vl_vspw = (u_char)fdtdec_get_int(blob, node, + "samsung,vl-vspw", 0); + if (panel_info.vl_vspw == 0) { + debug("Can't get vsync width\n"); + return -ENXIO; + } + + panel_info.vl_vfpd = fdtdec_get_int(blob, node, + "samsung,vl-vfpd", 0); + if (panel_info.vl_vfpd == 0) { + debug("Can't get lower margin\n"); + return -ENXIO; + } + + panel_info.vl_vbpd = fdtdec_get_int(blob, node, "samsung,vl-vbpd", 0); + if (panel_info.vl_vbpd == 0) { + debug("Can't get upper margin\n"); + return -ENXIO; + } + + panel_info.vl_cmd_allow_len = fdtdec_get_int(blob, node, + "samsung,vl-cmd-allow-len", 0); + + panel_info.win_id = fdtdec_get_int(blob, node, "samsung,winid", 0); + panel_info.init_delay = fdtdec_get_int(blob, node, + "samsung,init-delay", 0); + panel_info.power_on_delay = fdtdec_get_int(blob, node, + "samsung,power-on-delay", 0); + panel_info.reset_delay = fdtdec_get_int(blob, node, + "samsung,reset-delay", 0); + panel_info.interface_mode = fdtdec_get_int(blob, node, + "samsung,interface-mode", 0); + panel_info.mipi_enabled = fdtdec_get_int(blob, node, + "samsung,mipi-enabled", 0); + panel_info.dp_enabled = fdtdec_get_int(blob, node, + "samsung,dp-enabled", 0); + panel_info.cs_setup = fdtdec_get_int(blob, node, + "samsung,cs-setup", 0); + panel_info.wr_setup = fdtdec_get_int(blob, node, + "samsung,wr-setup", 0); + panel_info.wr_act = fdtdec_get_int(blob, node, "samsung,wr-act", 0); + panel_info.wr_hold = fdtdec_get_int(blob, node, "samsung,wr-hold", 0); + + panel_info.logo_on = fdtdec_get_int(blob, node, "samsung,logo-on", 0); + if (panel_info.logo_on) { + panel_info.logo_width = fdtdec_get_int(blob, node, + "samsung,logo-width", 0); + panel_info.logo_height = fdtdec_get_int(blob, node, + "samsung,logo-height", 0); + panel_info.logo_addr = fdtdec_get_int(blob, node, + "samsung,logo-addr", 0); + } + + panel_info.rgb_mode = fdtdec_get_int(blob, node, + "samsung,rgb-mode", 0); + panel_info.pclk_name = fdtdec_get_int(blob, node, + "samsung,pclk-name", 0); + panel_info.sclk_div = fdtdec_get_int(blob, node, + "samsung,sclk-div", 0); + panel_info.dual_lcd_enabled = fdtdec_get_int(blob, node, + "samsung,dual-lcd-enabled", 0); + + return 0; +} +#endif + +void lcd_ctrl_init(void *lcdbase) +{ + set_system_display_ctrl(); + set_lcd_clk(); + +#ifdef CONFIG_OF_CONTROL + if (exynos_fimd_parse_dt(gd->fdt_blob)) + debug("Can't get proper panel info\n"); +#ifdef CONFIG_EXYNOS_MIPI_DSIM + exynos_init_dsim_platform_data(&panel_info); +#endif + exynos_lcd_misc_init(&panel_info); +#else + /* initialize parameters which is specific to panel. */ + init_panel_info(&panel_info); +#endif + + panel_width = panel_info.vl_width; + panel_height = panel_info.vl_height; + + exynos_lcd_init_mem(lcdbase, &panel_info); + + exynos_lcd_init(&panel_info); +} + +void lcd_enable(void) +{ + if (panel_info.logo_on) { + memset((void *) gd->fb_base, 0, panel_width * panel_height * + (NBITS(panel_info.vl_bpix) >> 3)); + } + + lcd_panel_on(&panel_info); +} + +/* dummy function */ +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ + return; +} diff --git a/qemu/roms/u-boot/drivers/video/exynos_fb.h b/qemu/roms/u-boot/drivers/video/exynos_fb.h new file mode 100644 index 000000000..2c2f94bd0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_fb.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _EXYNOS_FB_H_ +#define _EXYNOS_FB_H_ + +#include + +#define MAX_CLOCK (86 * 1000000) + +enum exynos_cpu_auto_cmd_rate { + DISABLE_AUTO_FRM, + PER_TWO_FRM, + PER_FOUR_FRM, + PER_SIX_FRM, + PER_EIGHT_FRM, + PER_TEN_FRM, + PER_TWELVE_FRM, + PER_FOURTEEN_FRM, + PER_SIXTEEN_FRM, + PER_EIGHTEEN_FRM, + PER_TWENTY_FRM, + PER_TWENTY_TWO_FRM, + PER_TWENTY_FOUR_FRM, + PER_TWENTY_SIX_FRM, + PER_TWENTY_EIGHT_FRM, + PER_THIRTY_FRM, +}; + +void exynos_fimd_lcd_init_mem(unsigned long screen_base, unsigned long fb_size, + unsigned long palette_size); +void exynos_fimd_lcd_init(vidinfo_t *vid); +unsigned long exynos_fimd_calc_fbsize(void); + +#endif diff --git a/qemu/roms/u-boot/drivers/video/exynos_fimd.c b/qemu/roms/u-boot/drivers/video/exynos_fimd.c new file mode 100644 index 000000000..cebbba758 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_fimd.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "exynos_fb.h" + +DECLARE_GLOBAL_DATA_PTR; + +static unsigned long *lcd_base_addr; +static vidinfo_t *pvid; +static struct exynos_fb *fimd_ctrl; + +void exynos_fimd_lcd_init_mem(u_long screen_base, u_long fb_size, + u_long palette_size) +{ + lcd_base_addr = (unsigned long *)screen_base; +} + +static void exynos_fimd_set_dualrgb(unsigned int enabled) +{ + unsigned int cfg = 0; + + if (enabled) { + cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT | + EXYNOS_DUALRGB_VDEN_EN_ENABLE; + + /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */ + cfg |= EXYNOS_DUALRGB_SUB_CNT(pvid->vl_col / 2) | + EXYNOS_DUALRGB_MAIN_CNT(0); + } + + writel(cfg, &fimd_ctrl->dualrgb); +} + +static void exynos_fimd_set_dp_clkcon(unsigned int enabled) +{ + unsigned int cfg = 0; + + if (enabled) + cfg = EXYNOS_DP_CLK_ENABLE; + + writel(cfg, &fimd_ctrl->dp_mie_clkcon); +} + +static void exynos_fimd_set_par(unsigned int win_id) +{ + unsigned int cfg = 0; + + /* set window control */ + cfg = readl((unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + + cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE | + EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE | + EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK | + EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK); + + /* DATAPATH is DMA */ + cfg |= EXYNOS_WINCON_DATAPATH_DMA; + + cfg |= EXYNOS_WINCON_HAWSWP_ENABLE; + + /* dma burst is 16 */ + cfg |= EXYNOS_WINCON_BURSTLEN_16WORD; + + switch (pvid->vl_bpix) { + case 4: + cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565; + break; + default: + cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888; + break; + } + + writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + + /* set window position to x=0, y=0*/ + cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0); + writel(cfg, (unsigned int)&fimd_ctrl->vidosd0a + + EXYNOS_VIDOSD(win_id)); + + cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) | + EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1) | + EXYNOS_VIDOSD_RIGHT_X_E(1) | + EXYNOS_VIDOSD_BOTTOM_Y_E(0); + + writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b + + EXYNOS_VIDOSD(win_id)); + + /* set window size for window0*/ + cfg = EXYNOS_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row); + writel(cfg, (unsigned int)&fimd_ctrl->vidosd0c + + EXYNOS_VIDOSD(win_id)); +} + +static void exynos_fimd_set_buffer_address(unsigned int win_id) +{ + unsigned long start_addr, end_addr; + + start_addr = (unsigned long)lcd_base_addr; + end_addr = start_addr + ((pvid->vl_col * (NBITS(pvid->vl_bpix) / 8)) * + pvid->vl_row); + + writel(start_addr, (unsigned int)&fimd_ctrl->vidw00add0b0 + + EXYNOS_BUFFER_OFFSET(win_id)); + writel(end_addr, (unsigned int)&fimd_ctrl->vidw00add1b0 + + EXYNOS_BUFFER_OFFSET(win_id)); +} + +static void exynos_fimd_set_clock(vidinfo_t *pvid) +{ + unsigned int cfg = 0, div = 0, remainder, remainder_div; + unsigned long pixel_clock; + unsigned long long src_clock; + + if (pvid->dual_lcd_enabled) { + pixel_clock = pvid->vl_freq * + (pvid->vl_hspw + pvid->vl_hfpd + + pvid->vl_hbpd + pvid->vl_col / 2) * + (pvid->vl_vspw + pvid->vl_vfpd + + pvid->vl_vbpd + pvid->vl_row); + } else if (pvid->interface_mode == FIMD_CPU_INTERFACE) { + pixel_clock = pvid->vl_freq * + pvid->vl_width * pvid->vl_height * + (pvid->cs_setup + pvid->wr_setup + + pvid->wr_act + pvid->wr_hold + 1); + } else { + pixel_clock = pvid->vl_freq * + (pvid->vl_hspw + pvid->vl_hfpd + + pvid->vl_hbpd + pvid->vl_col) * + (pvid->vl_vspw + pvid->vl_vfpd + + pvid->vl_vbpd + pvid->vl_row); + } + + cfg = readl(&fimd_ctrl->vidcon0); + cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK | + EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK | + EXYNOS_VIDCON0_CLKDIR_MASK); + cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS | + EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED); + + src_clock = (unsigned long long) get_lcd_clk(); + + /* get quotient and remainder. */ + remainder = do_div(src_clock, pixel_clock); + div = src_clock; + + remainder *= 10; + remainder_div = remainder / pixel_clock; + + /* round about one places of decimals. */ + if (remainder_div >= 5) + div++; + + /* in case of dual lcd mode. */ + if (pvid->dual_lcd_enabled) + div--; + + cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1); + writel(cfg, &fimd_ctrl->vidcon0); +} + +void exynos_set_trigger(void) +{ + unsigned int cfg = 0; + + cfg = readl(&fimd_ctrl->trigcon); + + cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG); + + writel(cfg, &fimd_ctrl->trigcon); +} + +int exynos_is_i80_frame_done(void) +{ + unsigned int cfg = 0; + int status; + + cfg = readl(&fimd_ctrl->trigcon); + + /* frame done func is valid only when TRIMODE[0] is set to 1. */ + status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) == + EXYNOS_I80STATUS_TRIG_DONE; + + return status; +} + +static void exynos_fimd_lcd_on(void) +{ + unsigned int cfg = 0; + + /* display on */ + cfg = readl(&fimd_ctrl->vidcon0); + cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE); + writel(cfg, &fimd_ctrl->vidcon0); +} + +static void exynos_fimd_window_on(unsigned int win_id) +{ + unsigned int cfg = 0; + + /* enable window */ + cfg = readl((unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + cfg |= EXYNOS_WINCON_ENWIN_ENABLE; + writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + + cfg = readl(&fimd_ctrl->winshmap); + cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id); + writel(cfg, &fimd_ctrl->winshmap); +} + +void exynos_fimd_lcd_off(void) +{ + unsigned int cfg = 0; + + cfg = readl(&fimd_ctrl->vidcon0); + cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE); + writel(cfg, &fimd_ctrl->vidcon0); +} + +void exynos_fimd_window_off(unsigned int win_id) +{ + unsigned int cfg = 0; + + cfg = readl((unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + cfg &= EXYNOS_WINCON_ENWIN_DISABLE; + writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + + cfg = readl(&fimd_ctrl->winshmap); + cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id); + writel(cfg, &fimd_ctrl->winshmap); +} + + +void exynos_fimd_lcd_init(vidinfo_t *vid) +{ + unsigned int cfg = 0, rgb_mode; + unsigned int offset; +#ifdef CONFIG_OF_CONTROL + unsigned int node; + + node = fdtdec_next_compatible(gd->fdt_blob, + 0, COMPAT_SAMSUNG_EXYNOS_FIMD); + if (node <= 0) + debug("exynos_fb: Can't get device node for fimd\n"); + + fimd_ctrl = (struct exynos_fb *)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); + if (fimd_ctrl == NULL) + debug("Can't get the FIMD base address\n"); +#else + fimd_ctrl = (struct exynos_fb *)samsung_get_base_fimd(); +#endif + + offset = exynos_fimd_get_base_offset(); + + /* store panel info to global variable */ + pvid = vid; + + rgb_mode = vid->rgb_mode; + + if (vid->interface_mode == FIMD_RGB_INTERFACE) { + cfg |= EXYNOS_VIDCON0_VIDOUT_RGB; + writel(cfg, &fimd_ctrl->vidcon0); + + cfg = readl(&fimd_ctrl->vidcon2); + cfg &= ~(EXYNOS_VIDCON2_WB_MASK | + EXYNOS_VIDCON2_TVFORMATSEL_MASK | + EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK); + cfg |= EXYNOS_VIDCON2_WB_DISABLE; + writel(cfg, &fimd_ctrl->vidcon2); + + /* set polarity */ + cfg = 0; + if (!pvid->vl_clkp) + cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE; + if (!pvid->vl_hsp) + cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT; + if (!pvid->vl_vsp) + cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT; + if (!pvid->vl_dp) + cfg |= EXYNOS_VIDCON1_IVDEN_INVERT; + + writel(cfg, (unsigned int)&fimd_ctrl->vidcon1 + offset); + + /* set timing */ + cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1); + cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1); + cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1); + writel(cfg, (unsigned int)&fimd_ctrl->vidtcon0 + offset); + + cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1); + cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1); + cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1); + + writel(cfg, (unsigned int)&fimd_ctrl->vidtcon1 + offset); + + /* set lcd size */ + cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1) | + EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1) | + EXYNOS_VIDTCON2_HOZVAL_E(pvid->vl_col - 1) | + EXYNOS_VIDTCON2_LINEVAL_E(pvid->vl_row - 1); + + writel(cfg, (unsigned int)&fimd_ctrl->vidtcon2 + offset); + } + + /* set display mode */ + cfg = readl(&fimd_ctrl->vidcon0); + cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK; + cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT); + writel(cfg, &fimd_ctrl->vidcon0); + + /* set par */ + exynos_fimd_set_par(pvid->win_id); + + /* set memory address */ + exynos_fimd_set_buffer_address(pvid->win_id); + + /* set buffer size */ + cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) | + EXYNOS_VIDADDR_PAGEWIDTH_E(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) | + EXYNOS_VIDADDR_OFFSIZE(0) | + EXYNOS_VIDADDR_OFFSIZE_E(0); + + writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 + + EXYNOS_BUFFER_SIZE(pvid->win_id)); + + /* set clock */ + exynos_fimd_set_clock(pvid); + + /* set rgb mode to dual lcd. */ + exynos_fimd_set_dualrgb(pvid->dual_lcd_enabled); + + /* display on */ + exynos_fimd_lcd_on(); + + /* window on */ + exynos_fimd_window_on(pvid->win_id); + + exynos_fimd_set_dp_clkcon(pvid->dp_enabled); +} + +unsigned long exynos_fimd_calc_fbsize(void) +{ + return pvid->vl_col * pvid->vl_row * (NBITS(pvid->vl_bpix) / 8); +} diff --git a/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi.c b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi.c new file mode 100644 index 000000000..7dd465293 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exynos_mipi_dsi_lowlevel.h" +#include "exynos_mipi_dsi_common.h" + +#define master_to_driver(a) (a->dsim_lcd_drv) +#define master_to_device(a) (a->dsim_lcd_dev) + +DECLARE_GLOBAL_DATA_PTR; + +static struct exynos_platform_mipi_dsim *dsim_pd; +#ifdef CONFIG_OF_CONTROL +static struct mipi_dsim_config dsim_config_dt; +static struct exynos_platform_mipi_dsim dsim_platform_data_dt; +static struct mipi_dsim_lcd_device mipi_lcd_device_dt; +#endif + +struct mipi_dsim_ddi { + int bus_id; + struct list_head list; + struct mipi_dsim_lcd_device *dsim_lcd_dev; + struct mipi_dsim_lcd_driver *dsim_lcd_drv; +}; + +static LIST_HEAD(dsim_ddi_list); +static LIST_HEAD(dsim_lcd_dev_list); + +int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev) +{ + struct mipi_dsim_ddi *dsim_ddi; + + if (!lcd_dev) { + debug("mipi_dsim_lcd_device is NULL.\n"); + return -EFAULT; + } + + if (!lcd_dev->name) { + debug("dsim_lcd_device name is NULL.\n"); + return -EFAULT; + } + + dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL); + if (!dsim_ddi) { + debug("failed to allocate dsim_ddi object.\n"); + return -EFAULT; + } + + dsim_ddi->dsim_lcd_dev = lcd_dev; + + list_add_tail(&dsim_ddi->list, &dsim_ddi_list); + + return 0; +} + +struct mipi_dsim_ddi + *exynos_mipi_dsi_find_lcd_device(struct mipi_dsim_lcd_driver *lcd_drv) +{ + struct mipi_dsim_ddi *dsim_ddi; + struct mipi_dsim_lcd_device *lcd_dev; + + list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) { + lcd_dev = dsim_ddi->dsim_lcd_dev; + if (!lcd_dev) + continue; + + if (lcd_drv->id >= 0) { + if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0 && + lcd_drv->id == lcd_dev->id) { + /** + * bus_id would be used to identify + * connected bus. + */ + dsim_ddi->bus_id = lcd_dev->bus_id; + + return dsim_ddi; + } + } else { + if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) { + /** + * bus_id would be used to identify + * connected bus. + */ + dsim_ddi->bus_id = lcd_dev->bus_id; + + return dsim_ddi; + } + } + + kfree(dsim_ddi); + list_del(&dsim_ddi_list); + } + + return NULL; +} + +int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv) +{ + struct mipi_dsim_ddi *dsim_ddi; + + if (!lcd_drv) { + debug("mipi_dsim_lcd_driver is NULL.\n"); + return -EFAULT; + } + + if (!lcd_drv->name) { + debug("dsim_lcd_driver name is NULL.\n"); + return -EFAULT; + } + + dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv); + if (!dsim_ddi) { + debug("mipi_dsim_ddi object not found.\n"); + return -EFAULT; + } + + dsim_ddi->dsim_lcd_drv = lcd_drv; + + debug("registered panel driver(%s) to mipi-dsi driver.\n", + lcd_drv->name); + + return 0; + +} + +struct mipi_dsim_ddi + *exynos_mipi_dsi_bind_lcd_ddi(struct mipi_dsim_device *dsim, + const char *name) +{ + struct mipi_dsim_ddi *dsim_ddi; + struct mipi_dsim_lcd_driver *lcd_drv; + struct mipi_dsim_lcd_device *lcd_dev; + + list_for_each_entry(dsim_ddi, &dsim_ddi_list, list) { + lcd_drv = dsim_ddi->dsim_lcd_drv; + lcd_dev = dsim_ddi->dsim_lcd_dev; + if (!lcd_drv || !lcd_dev) + continue; + + debug("lcd_drv->id = %d, lcd_dev->id = %d\n", + lcd_drv->id, lcd_dev->id); + + if ((strcmp(lcd_drv->name, name) == 0)) { + lcd_dev->master = dsim; + + dsim->dsim_lcd_dev = lcd_dev; + dsim->dsim_lcd_drv = lcd_drv; + + return dsim_ddi; + } + } + + return NULL; +} + +/* define MIPI-DSI Master operations. */ +static struct mipi_dsim_master_ops master_ops = { + .cmd_write = exynos_mipi_dsi_wr_data, + .get_dsim_frame_done = exynos_mipi_dsi_get_frame_done_status, + .clear_dsim_frame_done = exynos_mipi_dsi_clear_frame_done, +}; + +int exynos_mipi_dsi_init(void) +{ + struct mipi_dsim_device *dsim; + struct mipi_dsim_config *dsim_config; + struct mipi_dsim_ddi *dsim_ddi; + + dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL); + if (!dsim) { + debug("failed to allocate dsim object.\n"); + return -EFAULT; + } + + /* get mipi_dsim_config. */ + dsim_config = dsim_pd->dsim_config; + if (dsim_config == NULL) { + debug("failed to get dsim config data.\n"); + return -EFAULT; + } + + dsim->pd = dsim_pd; + dsim->dsim_config = dsim_config; + dsim->master_ops = &master_ops; + + /* bind lcd ddi matched with panel name. */ + dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name); + if (!dsim_ddi) { + debug("mipi_dsim_ddi object not found.\n"); + return -ENOSYS; + } + if (dsim_pd->lcd_power) + dsim_pd->lcd_power(); + + if (dsim_pd->mipi_power) + dsim_pd->mipi_power(); + + /* phy_enable(unsigned int dev_index, unsigned int enable) */ + if (dsim_pd->phy_enable) + dsim_pd->phy_enable(0, 1); + + set_mipi_clk(); + + exynos_mipi_dsi_init_dsim(dsim); + exynos_mipi_dsi_init_link(dsim); + exynos_mipi_dsi_set_hs_enable(dsim); + + /* set display timing. */ + exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config); + + /* initialize mipi-dsi client(lcd panel). */ + if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->mipi_panel_init) { + dsim_ddi->dsim_lcd_drv->mipi_panel_init(dsim); + dsim_ddi->dsim_lcd_drv->mipi_display_on(dsim); + } + + debug("mipi-dsi driver(%s mode) has been probed.\n", + (dsim_config->e_interface == DSIM_COMMAND) ? + "CPU" : "RGB"); + + return 0; +} + +void exynos_set_dsim_platform_data(struct exynos_platform_mipi_dsim *pd) +{ + if (pd == NULL) { + debug("pd is NULL\n"); + return; + } + + dsim_pd = pd; +} + +#ifdef CONFIG_OF_CONTROL +int exynos_dsim_config_parse_dt(const void *blob) +{ + int node; + + node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_MIPI_DSI); + if (node <= 0) { + printf("exynos_mipi_dsi: Can't get device node for mipi dsi\n"); + return -ENODEV; + } + + dsim_config_dt.e_interface = fdtdec_get_int(blob, node, + "samsung,dsim-config-e-interface", 0); + + dsim_config_dt.e_virtual_ch = fdtdec_get_int(blob, node, + "samsung,dsim-config-e-virtual-ch", 0); + + dsim_config_dt.e_pixel_format = fdtdec_get_int(blob, node, + "samsung,dsim-config-e-pixel-format", 0); + + dsim_config_dt.e_burst_mode = fdtdec_get_int(blob, node, + "samsung,dsim-config-e-burst-mode", 0); + + dsim_config_dt.e_no_data_lane = fdtdec_get_int(blob, node, + "samsung,dsim-config-e-no-data-lane", 0); + + dsim_config_dt.e_byte_clk = fdtdec_get_int(blob, node, + "samsung,dsim-config-e-byte-clk", 0); + + dsim_config_dt.hfp = fdtdec_get_int(blob, node, + "samsung,dsim-config-hfp", 0); + + dsim_config_dt.p = fdtdec_get_int(blob, node, + "samsung,dsim-config-p", 0); + dsim_config_dt.m = fdtdec_get_int(blob, node, + "samsung,dsim-config-m", 0); + dsim_config_dt.s = fdtdec_get_int(blob, node, + "samsung,dsim-config-s", 0); + + dsim_config_dt.pll_stable_time = fdtdec_get_int(blob, node, + "samsung,dsim-config-pll-stable-time", 0); + + dsim_config_dt.esc_clk = fdtdec_get_int(blob, node, + "samsung,dsim-config-esc-clk", 0); + + dsim_config_dt.stop_holding_cnt = fdtdec_get_int(blob, node, + "samsung,dsim-config-stop-holding-cnt", 0); + + dsim_config_dt.bta_timeout = fdtdec_get_int(blob, node, + "samsung,dsim-config-bta-timeout", 0); + + dsim_config_dt.rx_timeout = fdtdec_get_int(blob, node, + "samsung,dsim-config-rx-timeout", 0); + + mipi_lcd_device_dt.name = fdtdec_get_config_string(blob, + "samsung,dsim-device-name"); + + mipi_lcd_device_dt.id = fdtdec_get_int(blob, node, + "samsung,dsim-device-id", 0); + + mipi_lcd_device_dt.bus_id = fdtdec_get_int(blob, node, + "samsung,dsim-device-bus_id", 0); + + mipi_lcd_device_dt.reverse_panel = fdtdec_get_int(blob, node, + "samsung,dsim-device-reverse-panel", 0); + + return 0; +} + +void exynos_init_dsim_platform_data(vidinfo_t *vid) +{ + if (exynos_dsim_config_parse_dt(gd->fdt_blob)) + debug("Can't get proper dsim config.\n"); + + strcpy(dsim_platform_data_dt.lcd_panel_name, mipi_lcd_device_dt.name); + dsim_platform_data_dt.dsim_config = &dsim_config_dt; + dsim_platform_data_dt.mipi_power = mipi_power; + dsim_platform_data_dt.phy_enable = set_mipi_phy_ctrl; + dsim_platform_data_dt.lcd_panel_info = (void *)vid; + + mipi_lcd_device_dt.platform_data = (void *)&dsim_platform_data_dt; + exynos_mipi_dsi_register_lcd_device(&mipi_lcd_device_dt); + + dsim_pd = &dsim_platform_data_dt; +} +#endif diff --git a/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_common.c b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_common.c new file mode 100644 index 000000000..925d51500 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_common.c @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include "exynos_mipi_dsi_lowlevel.h" + +#define MHZ (1000 * 1000) +#define FIN_HZ (24 * MHZ) + +#define DFIN_PLL_MIN_HZ (6 * MHZ) +#define DFIN_PLL_MAX_HZ (12 * MHZ) + +#define DFVCO_MIN_HZ (500 * MHZ) +#define DFVCO_MAX_HZ (1000 * MHZ) + +#define TRY_GET_FIFO_TIMEOUT (5000 * 2) + +/* MIPI-DSIM status types. */ +enum { + DSIM_STATE_INIT, /* should be initialized. */ + DSIM_STATE_STOP, /* CPU and LCDC are LP mode. */ + DSIM_STATE_HSCLKEN, /* HS clock was enabled. */ + DSIM_STATE_ULPS +}; + +/* define DSI lane types. */ +enum { + DSIM_LANE_CLOCK = (1 << 0), + DSIM_LANE_DATA0 = (1 << 1), + DSIM_LANE_DATA1 = (1 << 2), + DSIM_LANE_DATA2 = (1 << 3), + DSIM_LANE_DATA3 = (1 << 4) +}; + +static unsigned int dpll_table[15] = { + 100, 120, 170, 220, 270, + 320, 390, 450, 510, 560, + 640, 690, 770, 870, 950 +}; + +static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim, + const unsigned char *data0, unsigned int data1) +{ + unsigned int data_cnt = 0, payload = 0; + + /* in case that data count is more then 4 */ + for (data_cnt = 0; data_cnt < data1; data_cnt += 4) { + /* + * after sending 4bytes per one time, + * send remainder data less then 4. + */ + if ((data1 - data_cnt) < 4) { + if ((data1 - data_cnt) == 3) { + payload = data0[data_cnt] | + data0[data_cnt + 1] << 8 | + data0[data_cnt + 2] << 16; + debug("count = 3 payload = %x, %x %x %x\n", + payload, data0[data_cnt], + data0[data_cnt + 1], + data0[data_cnt + 2]); + } else if ((data1 - data_cnt) == 2) { + payload = data0[data_cnt] | + data0[data_cnt + 1] << 8; + debug("count = 2 payload = %x, %x %x\n", payload, + data0[data_cnt], data0[data_cnt + 1]); + } else if ((data1 - data_cnt) == 1) { + payload = data0[data_cnt]; + } + } else { + /* send 4bytes per one time. */ + payload = data0[data_cnt] | + data0[data_cnt + 1] << 8 | + data0[data_cnt + 2] << 16 | + data0[data_cnt + 3] << 24; + + debug("count = 4 payload = %x, %x %x %x %x\n", + payload, *(u8 *)(data0 + data_cnt), + data0[data_cnt + 1], + data0[data_cnt + 2], + data0[data_cnt + 3]); + } + exynos_mipi_dsi_wr_tx_data(dsim, payload); + } +} + +int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, + const unsigned char *data0, unsigned int data1) +{ + unsigned int timeout = TRY_GET_FIFO_TIMEOUT; + unsigned long delay_val, delay; + unsigned int check_rx_ack = 0; + + if (dsim->state == DSIM_STATE_ULPS) { + debug("state is ULPS.\n"); + + return -EINVAL; + } + + delay_val = MHZ / dsim->dsim_config->esc_clk; + delay = 10 * delay_val; + + mdelay(delay); + + /* only if transfer mode is LPDT, wait SFR becomes empty. */ + if (dsim->state == DSIM_STATE_STOP) { + while (!(exynos_mipi_dsi_get_fifo_state(dsim) & + SFR_HEADER_EMPTY)) { + if ((timeout--) > 0) + mdelay(1); + else { + debug("SRF header fifo is not empty.\n"); + return -EINVAL; + } + } + } + + switch (data_id) { + /* short packet types of packet types for command. */ + case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: + case MIPI_DSI_DCS_SHORT_WRITE: + case MIPI_DSI_DCS_SHORT_WRITE_PARAM: + case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: + debug("data0 = %x data1 = %x\n", + data0[0], data0[1]); + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); + if (check_rx_ack) { + /* process response func should be implemented */ + return 0; + } else { + return -EINVAL; + } + + /* general command */ + case MIPI_DSI_COLOR_MODE_OFF: + case MIPI_DSI_COLOR_MODE_ON: + case MIPI_DSI_SHUTDOWN_PERIPHERAL: + case MIPI_DSI_TURN_ON_PERIPHERAL: + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); + if (check_rx_ack) { + /* process response func should be implemented. */ + return 0; + } else { + return -EINVAL; + } + + /* packet types for video data */ + case MIPI_DSI_V_SYNC_START: + case MIPI_DSI_V_SYNC_END: + case MIPI_DSI_H_SYNC_START: + case MIPI_DSI_H_SYNC_END: + case MIPI_DSI_END_OF_TRANSMISSION: + return 0; + + /* short and response packet types for command */ + case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: + case MIPI_DSI_DCS_READ: + exynos_mipi_dsi_clear_all_interrupt(dsim); + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); + /* process response func should be implemented. */ + return 0; + + /* long packet type and null packet */ + case MIPI_DSI_NULL_PACKET: + case MIPI_DSI_BLANKING_PACKET: + return 0; + case MIPI_DSI_GENERIC_LONG_WRITE: + case MIPI_DSI_DCS_LONG_WRITE: + { + unsigned int payload = 0; + + /* if data count is less then 4, then send 3bytes data. */ + if (data1 < 4) { + payload = data0[0] | + data0[1] << 8 | + data0[2] << 16; + + exynos_mipi_dsi_wr_tx_data(dsim, payload); + + debug("count = %d payload = %x,%x %x %x\n", + data1, payload, data0[0], + data0[1], data0[2]); + } else { + /* in case that data count is more then 4 */ + exynos_mipi_dsi_long_data_wr(dsim, data0, data1); + } + + /* put data into header fifo */ + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data1 & 0xff, + (data1 & 0xff00) >> 8); + + } + if (check_rx_ack) + /* process response func should be implemented. */ + return 0; + else + return -EINVAL; + + /* packet typo for video data */ + case MIPI_DSI_PACKED_PIXEL_STREAM_16: + case MIPI_DSI_PACKED_PIXEL_STREAM_18: + case MIPI_DSI_PIXEL_STREAM_3BYTE_18: + case MIPI_DSI_PACKED_PIXEL_STREAM_24: + if (check_rx_ack) { + /* process response func should be implemented. */ + return 0; + } else { + return -EINVAL; + } + default: + debug("data id %x is not supported current DSI spec.\n", + data_id); + + return -EINVAL; + } + + return 0; +} + +int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable) +{ + int sw_timeout; + + if (enable) { + sw_timeout = 1000; + + exynos_mipi_dsi_clear_interrupt(dsim); + exynos_mipi_dsi_enable_pll(dsim, 1); + while (1) { + sw_timeout--; + if (exynos_mipi_dsi_is_pll_stable(dsim)) + return 0; + if (sw_timeout == 0) + return -EINVAL; + } + } else + exynos_mipi_dsi_enable_pll(dsim, 0); + + return 0; +} + +unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler) +{ + unsigned long dfin_pll, dfvco, dpll_out; + unsigned int i, freq_band = 0xf; + + dfin_pll = (FIN_HZ / pre_divider); + + /****************************************************** + * Serial Clock(=ByteClk X 8) FreqBand[3:0] * + ****************************************************** + * ~ 99.99 MHz 0000 + * 100 ~ 119.99 MHz 0001 + * 120 ~ 159.99 MHz 0010 + * 160 ~ 199.99 MHz 0011 + * 200 ~ 239.99 MHz 0100 + * 140 ~ 319.99 MHz 0101 + * 320 ~ 389.99 MHz 0110 + * 390 ~ 449.99 MHz 0111 + * 450 ~ 509.99 MHz 1000 + * 510 ~ 559.99 MHz 1001 + * 560 ~ 639.99 MHz 1010 + * 640 ~ 689.99 MHz 1011 + * 690 ~ 769.99 MHz 1100 + * 770 ~ 869.99 MHz 1101 + * 870 ~ 949.99 MHz 1110 + * 950 ~ 1000 MHz 1111 + ******************************************************/ + if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) { + debug("fin_pll range should be 6MHz ~ 12MHz\n"); + exynos_mipi_dsi_enable_afc(dsim, 0, 0); + } else { + if (dfin_pll < 7 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x1); + else if (dfin_pll < 8 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x0); + else if (dfin_pll < 9 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x3); + else if (dfin_pll < 10 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x2); + else if (dfin_pll < 11 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x5); + else + exynos_mipi_dsi_enable_afc(dsim, 1, 0x4); + } + + dfvco = dfin_pll * main_divider; + debug("dfvco = %lu, dfin_pll = %lu, main_divider = %d\n", + dfvco, dfin_pll, main_divider); + if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ) + debug("fvco range should be 500MHz ~ 1000MHz\n"); + + dpll_out = dfvco / (1 << scaler); + debug("dpll_out = %lu, dfvco = %lu, scaler = %d\n", + dpll_out, dfvco, scaler); + + for (i = 0; i < ARRAY_SIZE(dpll_table); i++) { + if (dpll_out < dpll_table[i] * MHZ) { + freq_band = i; + break; + } + } + + debug("freq_band = %d\n", freq_band); + + exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler); + + exynos_mipi_dsi_hs_zero_ctrl(dsim, 0); + exynos_mipi_dsi_prep_ctrl(dsim, 0); + + /* Freq Band */ + exynos_mipi_dsi_pll_freq_band(dsim, freq_band); + + /* Stable time */ + exynos_mipi_dsi_pll_stable_time(dsim, + dsim->dsim_config->pll_stable_time); + + /* Enable PLL */ + debug("FOUT of mipi dphy pll is %luMHz\n", + (dpll_out / MHZ)); + + return dpll_out; +} + +int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim, + unsigned int byte_clk_sel, unsigned int enable) +{ + unsigned int esc_div; + unsigned long esc_clk_error_rate; + unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0; + + if (enable) { + dsim->e_clk_src = byte_clk_sel; + + /* Escape mode clock and byte clock source */ + exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel); + + /* DPHY, DSIM Link : D-PHY clock out */ + if (byte_clk_sel == DSIM_PLL_OUT_DIV8) { + hs_clk = exynos_mipi_dsi_change_pll(dsim, + dsim->dsim_config->p, dsim->dsim_config->m, + dsim->dsim_config->s); + if (hs_clk == 0) { + debug("failed to get hs clock.\n"); + return -EINVAL; + } + + byte_clk = hs_clk / 8; + exynos_mipi_dsi_enable_pll_bypass(dsim, 0); + exynos_mipi_dsi_pll_on(dsim, 1); + /* DPHY : D-PHY clock out, DSIM link : external clock out */ + } else if (byte_clk_sel == DSIM_EXT_CLK_DIV8) + debug("not support EXT CLK source for MIPI DSIM\n"); + else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) + debug("not support EXT CLK source for MIPI DSIM\n"); + + /* escape clock divider */ + esc_div = byte_clk / (dsim->dsim_config->esc_clk); + debug("esc_div = %d, byte_clk = %lu, esc_clk = %lu\n", + esc_div, byte_clk, dsim->dsim_config->esc_clk); + if ((byte_clk / esc_div) >= (20 * MHZ) || + (byte_clk / esc_div) > dsim->dsim_config->esc_clk) + esc_div += 1; + + escape_clk = byte_clk / esc_div; + debug("escape_clk = %lu, byte_clk = %lu, esc_div = %d\n", + escape_clk, byte_clk, esc_div); + + /* enable escape clock. */ + exynos_mipi_dsi_enable_byte_clock(dsim, 1); + + /* enable byte clk and escape clock */ + exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div); + /* escape clock on lane */ + exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, + (DSIM_LANE_CLOCK | dsim->data_lane), 1); + + debug("byte clock is %luMHz\n", + (byte_clk / MHZ)); + debug("escape clock that user's need is %lu\n", + (dsim->dsim_config->esc_clk / MHZ)); + debug("escape clock divider is %x\n", esc_div); + debug("escape clock is %luMHz\n", + ((byte_clk / esc_div) / MHZ)); + + if ((byte_clk / esc_div) > escape_clk) { + esc_clk_error_rate = escape_clk / + (byte_clk / esc_div); + debug("error rate is %lu over.\n", + (esc_clk_error_rate / 100)); + } else if ((byte_clk / esc_div) < (escape_clk)) { + esc_clk_error_rate = (byte_clk / esc_div) / + escape_clk; + debug("error rate is %lu under.\n", + (esc_clk_error_rate / 100)); + } + } else { + exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, + (DSIM_LANE_CLOCK | dsim->data_lane), 0); + exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0); + + /* disable escape clock. */ + exynos_mipi_dsi_enable_byte_clock(dsim, 0); + + if (byte_clk_sel == DSIM_PLL_OUT_DIV8) + exynos_mipi_dsi_pll_on(dsim, 0); + } + + return 0; +} + +int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim) +{ + dsim->state = DSIM_STATE_INIT; + + switch (dsim->dsim_config->e_no_data_lane) { + case DSIM_DATA_LANE_1: + dsim->data_lane = DSIM_LANE_DATA0; + break; + case DSIM_DATA_LANE_2: + dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1; + break; + case DSIM_DATA_LANE_3: + dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | + DSIM_LANE_DATA2; + break; + case DSIM_DATA_LANE_4: + dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | + DSIM_LANE_DATA2 | DSIM_LANE_DATA3; + break; + default: + debug("data lane is invalid.\n"); + return -EINVAL; + }; + + exynos_mipi_dsi_sw_reset(dsim); + exynos_mipi_dsi_dp_dn_swap(dsim, 0); + + return 0; +} + +int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + /* enable only frame done interrupt */ + exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable); + + return 0; +} + +static void convert_to_fb_videomode(struct fb_videomode *mode1, + vidinfo_t *mode2) +{ + mode1->xres = mode2->vl_width; + mode1->yres = mode2->vl_height; + mode1->upper_margin = mode2->vl_vfpd; + mode1->lower_margin = mode2->vl_vbpd; + mode1->left_margin = mode2->vl_hfpd; + mode1->right_margin = mode2->vl_hbpd; + mode1->vsync_len = mode2->vl_vspw; + mode1->hsync_len = mode2->vl_hspw; +} + +int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_config) +{ + struct exynos_platform_mipi_dsim *dsim_pd; + struct fb_videomode lcd_video; + vidinfo_t *vid; + + dsim_pd = (struct exynos_platform_mipi_dsim *)dsim->pd; + vid = (vidinfo_t *)dsim_pd->lcd_panel_info; + + convert_to_fb_videomode(&lcd_video, vid); + + /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */ + if (dsim->dsim_config->e_interface == (u32) DSIM_VIDEO) { + if (dsim->dsim_config->auto_vertical_cnt == 0) { + exynos_mipi_dsi_set_main_disp_vporch(dsim, + vid->vl_cmd_allow_len, + lcd_video.upper_margin, + lcd_video.lower_margin); + exynos_mipi_dsi_set_main_disp_hporch(dsim, + lcd_video.left_margin, + lcd_video.right_margin); + exynos_mipi_dsi_set_main_disp_sync_area(dsim, + lcd_video.vsync_len, + lcd_video.hsync_len); + } + } + + exynos_mipi_dsi_set_main_disp_resol(dsim, lcd_video.xres, + lcd_video.yres); + + exynos_mipi_dsi_display_config(dsim, dsim->dsim_config); + + debug("lcd panel ==> width = %d, height = %d\n", + lcd_video.xres, lcd_video.yres); + + return 0; +} + +int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim) +{ + unsigned int time_out = 100; + + switch (dsim->state) { + case DSIM_STATE_INIT: + exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f); + + /* dsi configuration */ + exynos_mipi_dsi_init_config(dsim); + exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1); + exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1); + + /* set clock configuration */ + exynos_mipi_dsi_set_clock(dsim, + dsim->dsim_config->e_byte_clk, 1); + + /* check clock and data lane state are stop state */ + while (!(exynos_mipi_dsi_is_lane_state(dsim))) { + time_out--; + if (time_out == 0) { + debug("DSI Master is not stop state.\n"); + debug("Check initialization process\n"); + + return -EINVAL; + } + } + + dsim->state = DSIM_STATE_STOP; + + /* BTA sequence counters */ + exynos_mipi_dsi_set_stop_state_counter(dsim, + dsim->dsim_config->stop_holding_cnt); + exynos_mipi_dsi_set_bta_timeout(dsim, + dsim->dsim_config->bta_timeout); + exynos_mipi_dsi_set_lpdr_timeout(dsim, + dsim->dsim_config->rx_timeout); + + return 0; + default: + debug("DSI Master is already init.\n"); + return 0; + } + + return 0; +} + +int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim) +{ + if (dsim->state == DSIM_STATE_STOP) { + if (dsim->e_clk_src != DSIM_EXT_CLK_BYPASS) { + dsim->state = DSIM_STATE_HSCLKEN; + + /* set LCDC and CPU transfer mode to HS. */ + exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); + exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); + + exynos_mipi_dsi_enable_hs_clock(dsim, 1); + + return 0; + } else + debug("clock source is external bypass.\n"); + } else + debug("DSIM is not stop state.\n"); + + return 0; +} + +int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int mode) +{ + if (mode) { + if (dsim->state != DSIM_STATE_HSCLKEN) { + debug("HS Clock lane is not enabled.\n"); + return -EINVAL; + } + + exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); + } else { + if (dsim->state == DSIM_STATE_INIT || dsim->state == + DSIM_STATE_ULPS) { + debug("DSI Master is not STOP or HSDT state.\n"); + return -EINVAL; + } + + exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); + } + + return 0; +} + +int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) +{ + return _exynos_mipi_dsi_get_frame_done_status(dsim); +} + +int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) +{ + _exynos_mipi_dsi_clear_frame_done(dsim); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_common.h b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_common.h new file mode 100644 index 000000000..98eb78e5f --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_common.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +#ifndef _EXYNOS_MIPI_DSI_COMMON_H +#define _EXYNOS_MIPI_DSI_COMMON_H + +int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, + const unsigned char *data0, unsigned int data1); +int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, unsigned int enable); +unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler); +int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim, + unsigned int byte_clk_sel, unsigned int enable); +int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_info); +int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int mode); +int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, + unsigned int enable); +int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); + +#endif /* _EXYNOS_MIPI_DSI_COMMON_H */ diff --git a/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_lowlevel.c b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_lowlevel.c new file mode 100644 index 000000000..fcfdc8d12 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_lowlevel.c @@ -0,0 +1,639 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include "exynos_mipi_dsi_lowlevel.h" +#include "exynos_mipi_dsi_common.h" + +void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + reg = readl(&mipi_dsim->swrst); + + reg |= DSIM_FUNCRST; + + writel(reg, &mipi_dsim->swrst); +} + +void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim) +{ + unsigned int reg = 0; + + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + reg = readl(&mipi_dsim->swrst); + + reg |= DSIM_SWRST; + reg |= DSIM_FUNCRST; + + writel(reg, &mipi_dsim->swrst); +} + +void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->intsrc); + + reg |= INTSRC_SWRST_RELEASE; + + writel(reg, &mipi_dsim->intsrc); +} + +void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, + unsigned int mode, unsigned int mask) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->intmsk); + + if (mask) + reg |= mode; + else + reg &= ~mode; + + writel(reg, &mipi_dsim->intmsk); +} + +void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, + unsigned int cfg) +{ + unsigned int reg; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + reg = readl(&mipi_dsim->fifoctrl); + + writel(reg & ~(cfg), &mipi_dsim->fifoctrl); + udelay(10 * 1000); + reg |= cfg; + + writel(reg, &mipi_dsim->fifoctrl); +} + +/* + * this function set PLL P, M and S value in D-PHY + */ +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, + unsigned int value) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + writel(DSIM_AFC_CTL(value), &mipi_dsim->phyacchr); +} + +void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, + unsigned int width_resol, unsigned int height_resol) +{ + unsigned int reg; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + /* standby should be set after configuration so set to not ready*/ + reg = (readl(&mipi_dsim->mdresol)) & ~(DSIM_MAIN_STAND_BY); + writel(reg, &mipi_dsim->mdresol); + + /* reset resolution */ + reg &= ~(DSIM_MAIN_VRESOL(0x7ff) | DSIM_MAIN_HRESOL(0x7ff)); + reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol); + + reg |= DSIM_MAIN_STAND_BY; + writel(reg, &mipi_dsim->mdresol); +} + +void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, + unsigned int cmd_allow, unsigned int vfront, unsigned int vback) +{ + unsigned int reg; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + reg = (readl(&mipi_dsim->mvporch)) & + ~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) | + (DSIM_MAIN_VBP_MASK)); + + reg |= ((cmd_allow & 0xf) << DSIM_CMD_ALLOW_SHIFT) | + ((vfront & 0x7ff) << DSIM_STABLE_VFP_SHIFT) | + ((vback & 0x7ff) << DSIM_MAIN_VBP_SHIFT); + + writel(reg, &mipi_dsim->mvporch); +} + +void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, + unsigned int front, unsigned int back) +{ + unsigned int reg; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + reg = (readl(&mipi_dsim->mhporch)) & + ~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK)); + + reg |= (front << DSIM_MAIN_HFP_SHIFT) | (back << DSIM_MAIN_HBP_SHIFT); + + writel(reg, &mipi_dsim->mhporch); +} + +void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori) +{ + unsigned int reg; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + reg = (readl(&mipi_dsim->msync)) & + ~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK)); + + reg |= ((vert & 0x3ff) << DSIM_MAIN_VSA_SHIFT) | + (hori << DSIM_MAIN_HSA_SHIFT); + + writel(reg, &mipi_dsim->msync); +} + +void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori) +{ + unsigned int reg; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + reg = (readl(&mipi_dsim->sdresol)) & + ~(DSIM_SUB_STANDY_MASK); + + writel(reg, &mipi_dsim->sdresol); + + reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK); + reg |= ((vert & 0x7ff) << DSIM_SUB_VRESOL_SHIFT) | + ((hori & 0x7ff) << DSIM_SUB_HRESOL_SHIFT); + writel(reg, &mipi_dsim->sdresol); + + /* DSIM STANDBY */ + reg |= (1 << DSIM_SUB_STANDY_SHIFT); + writel(reg, &mipi_dsim->sdresol); +} + +void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim) +{ + struct mipi_dsim_config *dsim_config = dsim->dsim_config; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int cfg = (readl(&mipi_dsim->config)) & + ~((1 << DSIM_EOT_PACKET_SHIFT) | + (0x1f << DSIM_HSA_MODE_SHIFT) | + (0x3 << DSIM_NUM_OF_DATALANE_SHIFT)); + + cfg |= (dsim_config->auto_flush << DSIM_AUTO_FLUSH_SHIFT) | + (dsim_config->eot_disable << DSIM_EOT_PACKET_SHIFT) | + (dsim_config->auto_vertical_cnt << DSIM_AUTO_MODE_SHIFT) | + (dsim_config->hse << DSIM_HSE_MODE_SHIFT) | + (dsim_config->hfp << DSIM_HFP_MODE_SHIFT) | + (dsim_config->hbp << DSIM_HBP_MODE_SHIFT) | + (dsim_config->hsa << DSIM_HSA_MODE_SHIFT) | + (dsim_config->e_no_data_lane << DSIM_NUM_OF_DATALANE_SHIFT); + + writel(cfg, &mipi_dsim->config); +} + +void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_config) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + u32 reg = (readl(&mipi_dsim->config)) & + ~((0x3 << DSIM_BURST_MODE_SHIFT) | (1 << DSIM_VIDEO_MODE_SHIFT) + | (0x3 << DSIM_MAINVC_SHIFT) | (0x7 << DSIM_MAINPIX_SHIFT) + | (0x3 << DSIM_SUBVC_SHIFT) | (0x7 << DSIM_SUBPIX_SHIFT)); + + if (dsim_config->e_interface == DSIM_VIDEO) + reg |= (1 << DSIM_VIDEO_MODE_SHIFT); + else if (dsim_config->e_interface == DSIM_COMMAND) + reg &= ~(1 << DSIM_VIDEO_MODE_SHIFT); + else { + printf("unknown lcd type.\n"); + return; + } + + /* main lcd */ + reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << DSIM_BURST_MODE_SHIFT + | ((u8) (dsim_config->e_virtual_ch) & 0x3) << DSIM_MAINVC_SHIFT + | ((u8) (dsim_config->e_pixel_format) & 0x7) << DSIM_MAINPIX_SHIFT; + + writel(reg, &mipi_dsim->config); +} + +void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, + unsigned int lane, unsigned int enable) +{ + unsigned int reg; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + reg = readl(&mipi_dsim->config); + + if (enable) + reg |= DSIM_LANE_ENx(lane); + else + reg &= ~DSIM_LANE_ENx(lane); + + writel(reg, &mipi_dsim->config); +} + +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, + unsigned int count) +{ + unsigned int cfg; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + /* get the data lane number. */ + cfg = DSIM_NUM_OF_DATA_LANE(count); + + writel(cfg, &mipi_dsim->config); +} + +void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, + unsigned int enable, unsigned int afc_code) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->phyacchr); + + reg = 0; + + if (enable) { + reg |= DSIM_AFC_EN; + reg &= ~(0x7 << DSIM_AFC_CTL_SHIFT); + reg |= DSIM_AFC_CTL(afc_code); + } else + reg &= ~DSIM_AFC_EN; + + writel(reg, &mipi_dsim->phyacchr); +} + +void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->clkctrl)) & + ~(DSIM_PLL_BYPASS_EXTERNAL); + + reg |= enable << DSIM_PLL_BYPASS_SHIFT; + + writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, + unsigned int freq_band) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->pllctrl)) & + ~(0x1f << DSIM_FREQ_BAND_SHIFT); + + reg |= ((freq_band & 0x1f) << DSIM_FREQ_BAND_SHIFT); + + writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->pllctrl)) & + ~(0x7ffff << 1); + + reg |= ((pre_divider & 0x3f) << DSIM_PREDIV_SHIFT) | + ((main_divider & 0x1ff) << DSIM_MAIN_SHIFT) | + ((scaler & 0x7) << DSIM_SCALER_SHIFT); + + writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, + unsigned int lock_time) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + writel(lock_time, &mipi_dsim->plltmr); +} + +void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->pllctrl)) & + ~(0x1 << DSIM_PLL_EN_SHIFT); + + reg |= ((enable & 0x1) << DSIM_PLL_EN_SHIFT); + + writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, + unsigned int src) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->clkctrl)) & + ~(0x3 << DSIM_BYTE_CLK_SRC_SHIFT); + + reg |= ((unsigned int) src) << DSIM_BYTE_CLK_SRC_SHIFT; + + writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->clkctrl)) & + ~(1 << DSIM_BYTE_CLKEN_SHIFT); + + reg |= enable << DSIM_BYTE_CLKEN_SHIFT; + + writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, + unsigned int enable, unsigned int prs_val) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->clkctrl)) & + ~((1 << DSIM_ESC_CLKEN_SHIFT) | (0xffff)); + + reg |= enable << DSIM_ESC_CLKEN_SHIFT; + if (enable) + reg |= prs_val; + + writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, + unsigned int lane_sel, unsigned int enable) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->clkctrl); + + if (enable) + reg |= DSIM_LANE_ESC_CLKEN(lane_sel); + else + reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel); + + writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->escmode)) & + ~(0x1 << DSIM_FORCE_STOP_STATE_SHIFT); + + reg |= ((enable & 0x1) << DSIM_FORCE_STOP_STATE_SHIFT); + + writel(reg, &mipi_dsim->escmode); +} + +unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->status); + + /** + * check clock and data lane states. + * if MIPI-DSI controller was enabled at bootloader then + * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK. + * so it should be checked for two case. + */ + if ((reg & DSIM_STOP_STATE_DAT(0xf)) && + ((reg & DSIM_STOP_STATE_CLK) || + (reg & DSIM_TX_READY_HS_CLK))) + return 1; + else + return 0; +} + +void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, + unsigned int cnt_val) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->escmode)) & + ~(0x7ff << DSIM_STOP_STATE_CNT_SHIFT); + + reg |= ((cnt_val & 0x7ff) << DSIM_STOP_STATE_CNT_SHIFT); + + writel(reg, &mipi_dsim->escmode); +} + +void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->timeout)) & + ~(0xff << DSIM_BTA_TOUT_SHIFT); + + reg |= (timeout << DSIM_BTA_TOUT_SHIFT); + + writel(reg, &mipi_dsim->timeout); +} + +void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->timeout)) & + ~(0xffff << DSIM_LPDR_TOUT_SHIFT); + + reg |= (timeout << DSIM_LPDR_TOUT_SHIFT); + + writel(reg, &mipi_dsim->timeout); +} + +void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->escmode); + + reg &= ~DSIM_CMD_LPDT_LP; + + if (lp) + reg |= DSIM_CMD_LPDT_LP; + + writel(reg, &mipi_dsim->escmode); +} + +void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->escmode); + + reg &= ~DSIM_TX_LPDT_LP; + + if (lp) + reg |= DSIM_TX_LPDT_LP; + + writel(reg, &mipi_dsim->escmode); +} + +void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->clkctrl)) & + ~(1 << DSIM_TX_REQUEST_HSCLK_SHIFT); + + reg |= enable << DSIM_TX_REQUEST_HSCLK_SHIFT; + + writel(reg, &mipi_dsim->clkctrl); +} + +void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, + unsigned int swap_en) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->phyacchr1); + + reg &= ~(0x3 << DSIM_DPDN_SWAP_DATA_SHIFT); + reg |= (swap_en & 0x3) << DSIM_DPDN_SWAP_DATA_SHIFT; + + writel(reg, &mipi_dsim->phyacchr1); +} + +void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, + unsigned int hs_zero) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->pllctrl)) & + ~(0xf << DSIM_ZEROCTRL_SHIFT); + + reg |= ((hs_zero & 0xf) << DSIM_ZEROCTRL_SHIFT); + + writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (readl(&mipi_dsim->pllctrl)) & + ~(0x7 << DSIM_PRECTRL_SHIFT); + + reg |= ((prep & 0x7) << DSIM_PRECTRL_SHIFT); + + writel(reg, &mipi_dsim->pllctrl); +} + +void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->intsrc); + + reg |= INTSRC_PLL_STABLE; + + writel(reg, &mipi_dsim->intsrc); +} + +void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + writel(0xffffffff, &mipi_dsim->intsrc); +} + +unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + reg = readl(&mipi_dsim->status); + + return reg & DSIM_PLL_STABLE ? 1 : 0; +} + +unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + return readl(&mipi_dsim->fifoctrl) & ~(0x1f); +} + +void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, + unsigned int di, const unsigned char data0, const unsigned char data1) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = (DSIM_PKTHDR_DAT1(data1) | DSIM_PKTHDR_DAT0(data0) | + DSIM_PKTHDR_DI(di)); + + writel(reg, &mipi_dsim->pkthdr); +} + +unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device + *dsim) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->intsrc); + + return (reg & INTSRC_FRAME_DONE) ? 1 : 0; +} + +void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + unsigned int reg = readl(&mipi_dsim->intsrc); + + writel(reg | INTSRC_FRAME_DONE, &mipi_dsim->intsrc); +} + +void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, + unsigned int tx_data) +{ + struct exynos_mipi_dsim *mipi_dsim = + (struct exynos_mipi_dsim *)samsung_get_base_mipi_dsim(); + + writel(tx_data, &mipi_dsim->payload); +} diff --git a/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_lowlevel.h b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_lowlevel.h new file mode 100644 index 000000000..0bede25e4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_mipi_dsi_lowlevel.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H +#define _EXYNOS_MIPI_DSI_LOWLEVEL_H + +void exynos_mipi_dsi_register(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_sw_release(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, + unsigned int mode, unsigned int mask); +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, + unsigned int count); +void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, + unsigned int cfg); +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, + unsigned int value); +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, + unsigned int value); +void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, + unsigned int width_resol, unsigned int height_resol); +void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, + unsigned int cmd_allow, unsigned int vfront, unsigned int vback); +void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, + unsigned int front, unsigned int back); +void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori); +void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori); +void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_config); +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, + unsigned int count); +void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, + unsigned int lane, unsigned int enable); +void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, + unsigned int enable, unsigned int afc_code); +void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, + unsigned int freq_band); +void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler); +void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, + unsigned int lock_time); +void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, + unsigned int src); +void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, + unsigned int enable, unsigned int prs_val); +void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, + unsigned int lane_sel, unsigned int enable); +void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, + unsigned int enable); +unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, + unsigned int cnt_val); +void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout); +void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout); +void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp); +void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp); +void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, + unsigned int swap_en); +void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, + unsigned int hs_zero); +void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, + unsigned int prep); +void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_clear_all_interrupt(struct mipi_dsim_device *dsim); +unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim); +unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim); +unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device + *dsim); +void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, + unsigned int di, const unsigned char data0, const unsigned char data1); +void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, + unsigned int tx_data); + +#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */ diff --git a/qemu/roms/u-boot/drivers/video/exynos_pwm_bl.c b/qemu/roms/u-boot/drivers/video/exynos_pwm_bl.c new file mode 100644 index 000000000..a6890daf2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/exynos_pwm_bl.c @@ -0,0 +1,45 @@ +/* + * PWM BACKLIGHT driver for Board based on EXYNOS. + * + * Author: Donghwa Lee + * + * Derived from linux/drivers/video/backlight/pwm_backlight.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct pwm_backlight_data *pwm; + +static int exynos_pwm_backlight_update_status(void) +{ + int brightness = pwm->brightness; + int max = pwm->max_brightness; + + if (brightness == 0) { + pwm_config(pwm->pwm_id, 0, pwm->period); + pwm_disable(pwm->pwm_id); + } else { + pwm_config(pwm->pwm_id, + brightness * pwm->period / max, pwm->period); + pwm_enable(pwm->pwm_id); + } + return 0; +} + +int exynos_pwm_backlight_init(struct pwm_backlight_data *pd) +{ + pwm = pd; + + exynos_pwm_backlight_update_status(); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/video/formike.c b/qemu/roms/u-boot/drivers/video/formike.c new file mode 100644 index 000000000..138315843 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/formike.c @@ -0,0 +1,512 @@ +/* + * LCD: Formike, TFT 4.3", 480x800, RGB24, KWH043ST20-F01, DriverIC NT35510-16 + * LCD initialization via SPI + * + * SPDX-License-Identifier: GPL-2.0 + * Based on: + * + */ +#include +#include +#include + +#define TAG_READ 0x80 +#define TAG_WRITE 0x00 + +#define TAG_DATA 0x40 +#define TAG_COMMAND 0x00 + +#define TAG_ADDR_H 0x20 +#define TAG_ADDR_L 0x00 + +static int spi_write_tag_val(struct spi_slave *spi, unsigned char tag, + unsigned char val) +{ + unsigned long flags = SPI_XFER_BEGIN; + u8 buf[2]; + int ret; + + buf[0] = tag; + ret = spi_xfer(spi, 8, buf, NULL, flags); + buf[0] = val; + flags = SPI_XFER_END; + ret = spi_xfer(spi, 8, buf, NULL, flags); + +#ifdef KWH043ST20_F01_SPI_DEBUG + printf("spi_write_tag_val: tag=%02X, val=%02X ret: %d\n", + tag, val, ret); +#endif /* KWH043ST20_F01_SPI_DEBUG */ + if (ret) + debug("%s: Failed to send: %d\n", __func__, ret); + + return ret; +} + +static void spi_write_dat(struct spi_slave *spi, unsigned int val) +{ + spi_write_tag_val(spi, TAG_WRITE|TAG_DATA, val); +} + +static void spi_write_com(struct spi_slave *spi, unsigned int addr) +{ + spi_write_tag_val(spi, TAG_WRITE|TAG_COMMAND|TAG_ADDR_H, + (addr & 0xff00) >> 8); + spi_write_tag_val(spi, TAG_WRITE|TAG_COMMAND|TAG_ADDR_L, + (addr & 0x00ff) >> 0); +} + +int kwh043st20_f01_spi_startup(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *spi; + int ret; + + spi = spi_setup_slave(bus, cs, max_hz, spi_mode); + if (!spi) { + debug("%s: Failed to set up slave\n", __func__); + return -1; + } + + ret = spi_claim_bus(spi); + if (ret) { + debug("%s: Failed to claim SPI bus: %d\n", __func__, ret); + goto err_claim_bus; + } + + + /* LV2 Page 1 enable */ + spi_write_com(spi, 0xF000); spi_write_dat(spi, 0x55); + spi_write_com(spi, 0xF001); spi_write_dat(spi, 0xAA); + spi_write_com(spi, 0xF002); spi_write_dat(spi, 0x52); + spi_write_com(spi, 0xF003); spi_write_dat(spi, 0x08); + spi_write_com(spi, 0xF004); spi_write_dat(spi, 0x01); + + /* AVDD Set AVDD 5.2V */ + spi_write_com(spi, 0xB000); spi_write_dat(spi, 0x0D); + spi_write_com(spi, 0xB001); spi_write_dat(spi, 0x0D); + spi_write_com(spi, 0xB002); spi_write_dat(spi, 0x0D); + + /* AVDD ratio */ + spi_write_com(spi, 0xB600); spi_write_dat(spi, 0x34); + spi_write_com(spi, 0xB601); spi_write_dat(spi, 0x34); + spi_write_com(spi, 0xB602); spi_write_dat(spi, 0x34); + + /* AVEE -5.2V */ + spi_write_com(spi, 0xB100); spi_write_dat(spi, 0x0D); + spi_write_com(spi, 0xB101); spi_write_dat(spi, 0x0D); + spi_write_com(spi, 0xB102); spi_write_dat(spi, 0x0D); + + /* AVEE ratio */ + spi_write_com(spi, 0xB700); spi_write_dat(spi, 0x35); + spi_write_com(spi, 0xB701); spi_write_dat(spi, 0x35); + spi_write_com(spi, 0xB702); spi_write_dat(spi, 0x35); + + /* VCL -2.5V */ + spi_write_com(spi, 0xB200); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xB201); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xB202); spi_write_dat(spi, 0x00); + + /* VCL ratio */ + spi_write_com(spi, 0xB800); spi_write_dat(spi, 0x24); + spi_write_com(spi, 0xB801); spi_write_dat(spi, 0x24); + spi_write_com(spi, 0xB802); spi_write_dat(spi, 0x24); + + /* VGH 15V */ + spi_write_com(spi, 0xBF00); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xB300); spi_write_dat(spi, 0x08); + spi_write_com(spi, 0xB301); spi_write_dat(spi, 0x08); + spi_write_com(spi, 0xB302); spi_write_dat(spi, 0x08); + + /* VGH ratio */ + spi_write_com(spi, 0xB900); spi_write_dat(spi, 0x34); + spi_write_com(spi, 0xB901); spi_write_dat(spi, 0x34); + spi_write_com(spi, 0xB902); spi_write_dat(spi, 0x34); + + /* VGLX ratio */ + spi_write_com(spi, 0xBA00); spi_write_dat(spi, 0x24); + spi_write_com(spi, 0xBA01); spi_write_dat(spi, 0x24); + spi_write_com(spi, 0xBA02); spi_write_dat(spi, 0x24); + + /* VGMP/VGSP 4.7V/0V */ + spi_write_com(spi, 0xBC00); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xBC01); spi_write_dat(spi, 0x88); + spi_write_com(spi, 0xBC02); spi_write_dat(spi, 0x00); + + /* VGMN/VGSN -4.7V/0V */ + spi_write_com(spi, 0xBD00); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xBD01); spi_write_dat(spi, 0x88); + spi_write_com(spi, 0xBD02); spi_write_dat(spi, 0x00); + + /* VCOM 1.525V */ + spi_write_com(spi, 0xBE00); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xBE01); spi_write_dat(spi, 0x7A); + + /* Gamma Setting */ + spi_write_com(spi, 0xD100); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD101); spi_write_dat(spi, 0x05); + spi_write_com(spi, 0xD102); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD103); spi_write_dat(spi, 0x15); + spi_write_com(spi, 0xD104); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD105); spi_write_dat(spi, 0x30); + spi_write_com(spi, 0xD106); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD107); spi_write_dat(spi, 0x47); + spi_write_com(spi, 0xD108); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD109); spi_write_dat(spi, 0x5B); + spi_write_com(spi, 0xD10A); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD10B); spi_write_dat(spi, 0x7D); + spi_write_com(spi, 0xD10C); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD10D); spi_write_dat(spi, 0x9D); + spi_write_com(spi, 0xD10E); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD10F); spi_write_dat(spi, 0xCC); + spi_write_com(spi, 0xD110); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD111); spi_write_dat(spi, 0xF3); + spi_write_com(spi, 0xD112); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD113); spi_write_dat(spi, 0x32); + spi_write_com(spi, 0xD114); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD115); spi_write_dat(spi, 0x63); + spi_write_com(spi, 0xD116); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD117); spi_write_dat(spi, 0xB1); + spi_write_com(spi, 0xD118); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD119); spi_write_dat(spi, 0xF0); + spi_write_com(spi, 0xD11A); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD11B); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD11C); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD11D); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD11E); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD11F); spi_write_dat(spi, 0x67); + spi_write_com(spi, 0xD120); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD121); spi_write_dat(spi, 0x90); + spi_write_com(spi, 0xD122); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD123); spi_write_dat(spi, 0xCB); + spi_write_com(spi, 0xD124); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD125); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD126); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD127); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD128); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD129); spi_write_dat(spi, 0x51); + spi_write_com(spi, 0xD12A); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD12B); spi_write_dat(spi, 0x80); + spi_write_com(spi, 0xD12C); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD12D); spi_write_dat(spi, 0x9F); + spi_write_com(spi, 0xD12E); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD12F); spi_write_dat(spi, 0xBE); + spi_write_com(spi, 0xD130); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD131); spi_write_dat(spi, 0xF9); + spi_write_com(spi, 0xD132); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD133); spi_write_dat(spi, 0xFF); + + spi_write_com(spi, 0xD200); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD201); spi_write_dat(spi, 0x05); + spi_write_com(spi, 0xD202); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD203); spi_write_dat(spi, 0x15); + spi_write_com(spi, 0xD204); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD205); spi_write_dat(spi, 0x30); + spi_write_com(spi, 0xD206); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD207); spi_write_dat(spi, 0x47); + spi_write_com(spi, 0xD208); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD209); spi_write_dat(spi, 0x5B); + spi_write_com(spi, 0xD20A); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD20B); spi_write_dat(spi, 0x7D); + spi_write_com(spi, 0xD20C); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD20D); spi_write_dat(spi, 0x9D); + spi_write_com(spi, 0xD20E); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD20F); spi_write_dat(spi, 0xCC); + spi_write_com(spi, 0xD210); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD211); spi_write_dat(spi, 0xF3); + spi_write_com(spi, 0xD212); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD213); spi_write_dat(spi, 0x32); + spi_write_com(spi, 0xD214); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD215); spi_write_dat(spi, 0x63); + spi_write_com(spi, 0xD216); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD217); spi_write_dat(spi, 0xB1); + spi_write_com(spi, 0xD218); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD219); spi_write_dat(spi, 0xF0); + spi_write_com(spi, 0xD21A); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD21B); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD21C); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD21D); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD21E); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD21F); spi_write_dat(spi, 0x67); + spi_write_com(spi, 0xD220); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD221); spi_write_dat(spi, 0x90); + spi_write_com(spi, 0xD222); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD223); spi_write_dat(spi, 0xCB); + spi_write_com(spi, 0xD224); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD225); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD226); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD227); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD228); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD229); spi_write_dat(spi, 0x51); + spi_write_com(spi, 0xD22A); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD22B); spi_write_dat(spi, 0x80); + spi_write_com(spi, 0xD22C); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD22D); spi_write_dat(spi, 0x9F); + spi_write_com(spi, 0xD22E); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD22F); spi_write_dat(spi, 0xBE); + spi_write_com(spi, 0xD230); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD231); spi_write_dat(spi, 0xF9); + spi_write_com(spi, 0xD232); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD233); spi_write_dat(spi, 0xFF); + + spi_write_com(spi, 0xD300); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD301); spi_write_dat(spi, 0x05); + spi_write_com(spi, 0xD302); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD303); spi_write_dat(spi, 0x15); + spi_write_com(spi, 0xD304); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD305); spi_write_dat(spi, 0x30); + spi_write_com(spi, 0xD306); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD307); spi_write_dat(spi, 0x47); + spi_write_com(spi, 0xD308); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD309); spi_write_dat(spi, 0x5B); + spi_write_com(spi, 0xD30A); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD30B); spi_write_dat(spi, 0x7D); + spi_write_com(spi, 0xD30C); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD30D); spi_write_dat(spi, 0x9D); + spi_write_com(spi, 0xD30E); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD30F); spi_write_dat(spi, 0xCC); + spi_write_com(spi, 0xD310); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD311); spi_write_dat(spi, 0xF3); + spi_write_com(spi, 0xD312); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD313); spi_write_dat(spi, 0x32); + spi_write_com(spi, 0xD314); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD315); spi_write_dat(spi, 0x63); + spi_write_com(spi, 0xD316); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD317); spi_write_dat(spi, 0xB1); + spi_write_com(spi, 0xD318); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD319); spi_write_dat(spi, 0xF0); + spi_write_com(spi, 0xD31A); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD31B); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD31C); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD31D); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD31E); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD31F); spi_write_dat(spi, 0x67); + spi_write_com(spi, 0xD320); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD321); spi_write_dat(spi, 0x90); + spi_write_com(spi, 0xD322); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD323); spi_write_dat(spi, 0xCB); + spi_write_com(spi, 0xD324); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD325); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD326); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD327); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD328); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD329); spi_write_dat(spi, 0x51); + spi_write_com(spi, 0xD32A); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD32B); spi_write_dat(spi, 0x80); + spi_write_com(spi, 0xD32C); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD32D); spi_write_dat(spi, 0x9F); + spi_write_com(spi, 0xD32E); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD32F); spi_write_dat(spi, 0xBE); + spi_write_com(spi, 0xD330); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD331); spi_write_dat(spi, 0xF9); + spi_write_com(spi, 0xD332); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD333); spi_write_dat(spi, 0xFF); + + spi_write_com(spi, 0xD400); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD401); spi_write_dat(spi, 0x05); + spi_write_com(spi, 0xD402); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD403); spi_write_dat(spi, 0x15); + spi_write_com(spi, 0xD404); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD405); spi_write_dat(spi, 0x30); + spi_write_com(spi, 0xD406); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD407); spi_write_dat(spi, 0x47); + spi_write_com(spi, 0xD408); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD409); spi_write_dat(spi, 0x5B); + spi_write_com(spi, 0xD40A); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD40B); spi_write_dat(spi, 0x7D); + spi_write_com(spi, 0xD40C); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD40D); spi_write_dat(spi, 0x9D); + spi_write_com(spi, 0xD40E); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD40F); spi_write_dat(spi, 0xCC); + spi_write_com(spi, 0xD410); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD411); spi_write_dat(spi, 0xF3); + spi_write_com(spi, 0xD412); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD413); spi_write_dat(spi, 0x32); + spi_write_com(spi, 0xD414); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD415); spi_write_dat(spi, 0x63); + spi_write_com(spi, 0xD416); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD417); spi_write_dat(spi, 0xB1); + spi_write_com(spi, 0xD418); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD419); spi_write_dat(spi, 0xF0); + spi_write_com(spi, 0xD41A); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD41B); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD41C); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD41D); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD41E); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD41F); spi_write_dat(spi, 0x67); + spi_write_com(spi, 0xD420); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD421); spi_write_dat(spi, 0x90); + spi_write_com(spi, 0xD422); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD423); spi_write_dat(spi, 0xCB); + spi_write_com(spi, 0xD424); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD425); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD426); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD427); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD428); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD429); spi_write_dat(spi, 0x51); + spi_write_com(spi, 0xD42A); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD42B); spi_write_dat(spi, 0x80); + spi_write_com(spi, 0xD42C); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD42D); spi_write_dat(spi, 0x9F); + spi_write_com(spi, 0xD42E); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD42F); spi_write_dat(spi, 0xBE); + spi_write_com(spi, 0xD430); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD431); spi_write_dat(spi, 0xF9); + spi_write_com(spi, 0xD432); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD433); spi_write_dat(spi, 0xFF); + + spi_write_com(spi, 0xD500); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD501); spi_write_dat(spi, 0x05); + spi_write_com(spi, 0xD502); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD503); spi_write_dat(spi, 0x15); + spi_write_com(spi, 0xD504); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD505); spi_write_dat(spi, 0x30); + spi_write_com(spi, 0xD506); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD507); spi_write_dat(spi, 0x47); + spi_write_com(spi, 0xD508); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD509); spi_write_dat(spi, 0x5B); + spi_write_com(spi, 0xD50A); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD50B); spi_write_dat(spi, 0x7D); + spi_write_com(spi, 0xD50C); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD50D); spi_write_dat(spi, 0x9D); + spi_write_com(spi, 0xD50E); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD50F); spi_write_dat(spi, 0xCC); + spi_write_com(spi, 0xD510); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD511); spi_write_dat(spi, 0xF3); + spi_write_com(spi, 0xD512); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD513); spi_write_dat(spi, 0x32); + spi_write_com(spi, 0xD514); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD515); spi_write_dat(spi, 0x63); + spi_write_com(spi, 0xD516); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD517); spi_write_dat(spi, 0xB1); + spi_write_com(spi, 0xD518); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD519); spi_write_dat(spi, 0xF0); + spi_write_com(spi, 0xD51A); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD51B); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD51C); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD51D); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD51E); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD51F); spi_write_dat(spi, 0x67); + spi_write_com(spi, 0xD520); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD521); spi_write_dat(spi, 0x90); + spi_write_com(spi, 0xD522); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD523); spi_write_dat(spi, 0xCB); + spi_write_com(spi, 0xD524); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD525); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD526); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD527); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD528); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD529); spi_write_dat(spi, 0x51); + spi_write_com(spi, 0xD52A); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD52B); spi_write_dat(spi, 0x80); + spi_write_com(spi, 0xD52C); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD52D); spi_write_dat(spi, 0x9F); + spi_write_com(spi, 0xD52E); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD52F); spi_write_dat(spi, 0xBE); + spi_write_com(spi, 0xD530); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD531); spi_write_dat(spi, 0xF9); + spi_write_com(spi, 0xD532); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD533); spi_write_dat(spi, 0xFF); + + spi_write_com(spi, 0xD600); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD601); spi_write_dat(spi, 0x05); + spi_write_com(spi, 0xD602); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD603); spi_write_dat(spi, 0x15); + spi_write_com(spi, 0xD604); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD605); spi_write_dat(spi, 0x30); + spi_write_com(spi, 0xD606); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD607); spi_write_dat(spi, 0x47); + spi_write_com(spi, 0xD608); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD609); spi_write_dat(spi, 0x5B); + spi_write_com(spi, 0xD60A); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD60B); spi_write_dat(spi, 0x7D); + spi_write_com(spi, 0xD60C); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD60D); spi_write_dat(spi, 0x9D); + spi_write_com(spi, 0xD60E); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD60F); spi_write_dat(spi, 0xCC); + spi_write_com(spi, 0xD610); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xD611); spi_write_dat(spi, 0xF3); + spi_write_com(spi, 0xD612); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD613); spi_write_dat(spi, 0x32); + spi_write_com(spi, 0xD614); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD615); spi_write_dat(spi, 0x63); + spi_write_com(spi, 0xD616); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD617); spi_write_dat(spi, 0xB1); + spi_write_com(spi, 0xD618); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD619); spi_write_dat(spi, 0xF0); + spi_write_com(spi, 0xD61A); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xD61B); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD61C); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD61D); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD61E); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD61F); spi_write_dat(spi, 0x67); + spi_write_com(spi, 0xD620); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD621); spi_write_dat(spi, 0x90); + spi_write_com(spi, 0xD622); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD623); spi_write_dat(spi, 0xCB); + spi_write_com(spi, 0xD624); spi_write_dat(spi, 0x02); + spi_write_com(spi, 0xD625); spi_write_dat(spi, 0xF2); + spi_write_com(spi, 0xD626); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD627); spi_write_dat(spi, 0x2A); + spi_write_com(spi, 0xD628); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD629); spi_write_dat(spi, 0x51); + spi_write_com(spi, 0xD62A); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD62B); spi_write_dat(spi, 0x80); + spi_write_com(spi, 0xD62C); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD62D); spi_write_dat(spi, 0x9F); + spi_write_com(spi, 0xD62E); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD62F); spi_write_dat(spi, 0xBE); + spi_write_com(spi, 0xD630); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD631); spi_write_dat(spi, 0xF9); + spi_write_com(spi, 0xD632); spi_write_dat(spi, 0x03); + spi_write_com(spi, 0xD633); spi_write_dat(spi, 0xFF); + + /* LV2 Page 0 enable */ + spi_write_com(spi, 0xF000); spi_write_dat(spi, 0x55); + spi_write_com(spi, 0xF001); spi_write_dat(spi, 0xAA); + spi_write_com(spi, 0xF002); spi_write_dat(spi, 0x52); + spi_write_com(spi, 0xF003); spi_write_dat(spi, 0x08); + spi_write_com(spi, 0xF004); spi_write_dat(spi, 0x00); + + /* Display control */ + spi_write_com(spi, 0xB100); spi_write_dat(spi, 0xFC); + spi_write_com(spi, 0xB101); spi_write_dat(spi, 0x00); + + /* Source hold time */ + spi_write_com(spi, 0xB600); spi_write_dat(spi, 0x05); + + /* Gate EQ control */ + spi_write_com(spi, 0xB700); spi_write_dat(spi, 0x70); + spi_write_com(spi, 0xB701); spi_write_dat(spi, 0x70); + + /* Source EQ control (Mode 2) */ + spi_write_com(spi, 0xB800); spi_write_dat(spi, 0x01); + spi_write_com(spi, 0xB801); spi_write_dat(spi, 0x05); + spi_write_com(spi, 0xB802); spi_write_dat(spi, 0x05); + spi_write_com(spi, 0xB803); spi_write_dat(spi, 0x05); + + /* Inversion mode (Column) */ + spi_write_com(spi, 0xBC00); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xBC01); spi_write_dat(spi, 0x00); + spi_write_com(spi, 0xBC02); spi_write_dat(spi, 0x00); + + /* Timing control 8phase dual side/4H/4delay/RST_EN */ + spi_write_com(spi, 0xC900); spi_write_dat(spi, 0xD0); + spi_write_com(spi, 0xC901); spi_write_dat(spi, 0x82); + spi_write_com(spi, 0xC902); spi_write_dat(spi, 0x50); + spi_write_com(spi, 0xC903); spi_write_dat(spi, 0x50); + spi_write_com(spi, 0xC904); spi_write_dat(spi, 0x50); + + spi_write_com(spi, 0x3A00); spi_write_dat(spi, 0x55); + mdelay(120); + spi_write_com(spi, 0x1100); + mdelay(120); + spi_write_com(spi, 0x2900); + mdelay(120); + /* spi_write_com(spi, 0x2100); spi_write_dat(spi, 0x00); */ + spi_write_com(spi, 0x2C00); + + return 0; +err_claim_bus: + spi_free_slave(spi); + return -1; +} diff --git a/qemu/roms/u-boot/drivers/video/fsl_diu_fb.c b/qemu/roms/u-boot/drivers/video/fsl_diu_fb.c new file mode 100644 index 000000000..b3d46b86f --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/fsl_diu_fb.c @@ -0,0 +1,417 @@ +/* + * Copyright 2007, 2010-2011 Freescale Semiconductor, Inc. + * Authors: York Sun + * Timur Tabi + * + * FSL DIU Framebuffer driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include "videomodes.h" +#include +#include +#include +#include + +/* This setting is used for the ifm pdm360ng with PRIMEVIEW PM070WL3 */ +static struct fb_videomode fsl_diu_mode_800_480 = { + .name = "800x480-60", + .refresh = 60, + .xres = 800, + .yres = 480, + .pixclock = 31250, + .left_margin = 86, + .right_margin = 42, + .upper_margin = 33, + .lower_margin = 10, + .hsync_len = 128, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED +}; + +/* For the SHARP LQ084S3LG01, used on the P1022DS board */ +static struct fb_videomode fsl_diu_mode_800_600 = { + .name = "800x600-60", + .refresh = 60, + .xres = 800, + .yres = 600, + .pixclock = 25000, + .left_margin = 88, + .right_margin = 40, + .upper_margin = 23, + .lower_margin = 1, + .hsync_len = 128, + .vsync_len = 4, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED +}; + +/* + * These parameters give default parameters + * for video output 1024x768, + * FIXME - change timing to proper amounts + * hsync 31.5kHz, vsync 60Hz + */ +static struct fb_videomode fsl_diu_mode_1024_768 = { + .name = "1024x768-60", + .refresh = 60, + .xres = 1024, + .yres = 768, + .pixclock = 15385, + .left_margin = 160, + .right_margin = 24, + .upper_margin = 29, + .lower_margin = 3, + .hsync_len = 136, + .vsync_len = 6, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_videomode fsl_diu_mode_1280_1024 = { + .name = "1280x1024-60", + .refresh = 60, + .xres = 1280, + .yres = 1024, + .pixclock = 9375, + .left_margin = 38, + .right_margin = 128, + .upper_margin = 2, + .lower_margin = 7, + .hsync_len = 216, + .vsync_len = 37, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_videomode fsl_diu_mode_1280_720 = { + .name = "1280x720-60", + .refresh = 60, + .xres = 1280, + .yres = 720, + .pixclock = 13426, + .left_margin = 192, + .right_margin = 64, + .upper_margin = 22, + .lower_margin = 1, + .hsync_len = 136, + .vsync_len = 3, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_videomode fsl_diu_mode_1920_1080 = { + .name = "1920x1080-60", + .refresh = 60, + .xres = 1920, + .yres = 1080, + .pixclock = 5787, + .left_margin = 328, + .right_margin = 120, + .upper_margin = 34, + .lower_margin = 1, + .hsync_len = 208, + .vsync_len = 3, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED +}; + +/* + * These are the fields of area descriptor(in DDR memory) for every plane + */ +struct diu_ad { + /* Word 0(32-bit) in DDR memory */ + __le32 pix_fmt; /* hard coding pixel format */ + /* Word 1(32-bit) in DDR memory */ + __le32 addr; + /* Word 2(32-bit) in DDR memory */ + __le32 src_size_g_alpha; + /* Word 3(32-bit) in DDR memory */ + __le32 aoi_size; + /* Word 4(32-bit) in DDR memory */ + __le32 offset_xyi; + /* Word 5(32-bit) in DDR memory */ + __le32 offset_xyd; + /* Word 6(32-bit) in DDR memory */ + __le32 ckmax_r:8; + __le32 ckmax_g:8; + __le32 ckmax_b:8; + __le32 res9:8; + /* Word 7(32-bit) in DDR memory */ + __le32 ckmin_r:8; + __le32 ckmin_g:8; + __le32 ckmin_b:8; + __le32 res10:8; + /* Word 8(32-bit) in DDR memory */ + __le32 next_ad; + /* Word 9(32-bit) in DDR memory, just for 64-bit aligned */ + __le32 res[3]; +} __attribute__ ((packed)); + +/* + * DIU register map + */ +struct diu { + __be32 desc[3]; + __be32 gamma; + __be32 pallete; + __be32 cursor; + __be32 curs_pos; + __be32 diu_mode; + __be32 bgnd; + __be32 bgnd_wb; + __be32 disp_size; + __be32 wb_size; + __be32 wb_mem_addr; + __be32 hsyn_para; + __be32 vsyn_para; + __be32 syn_pol; + __be32 thresholds; + __be32 int_status; + __be32 int_mask; + __be32 colorbar[8]; + __be32 filling; + __be32 plut; +} __attribute__ ((packed)); + +struct diu_addr { + void *vaddr; /* Virtual address */ + u32 paddr; /* 32-bit physical address */ + unsigned int offset; /* Alignment offset */ +}; + +static struct fb_info info; + +/* + * Align to 64-bit(8-byte), 32-byte, etc. + */ +static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align) +{ + u32 offset, ssize; + u32 mask; + + ssize = size + bytes_align; + buf->vaddr = malloc(ssize); + if (!buf->vaddr) + return -1; + + memset(buf->vaddr, 0, ssize); + mask = bytes_align - 1; + offset = (u32)buf->vaddr & mask; + if (offset) { + buf->offset = bytes_align - offset; + buf->vaddr += offset; + } else + buf->offset = 0; + + buf->paddr = virt_to_phys(buf->vaddr); + return 0; +} + +/* + * Allocate a framebuffer and an Area Descriptor that points to it. Both + * are created in the same memory block. The Area Descriptor is updated to + * point to the framebuffer memory. Memory is aligned as needed. + */ +static struct diu_ad *allocate_fb(unsigned int xres, unsigned int yres, + unsigned int depth, char **fb) +{ + unsigned long size = xres * yres * depth; + struct diu_addr addr; + struct diu_ad *ad; + size_t ad_size = roundup(sizeof(struct diu_ad), 32); + + /* + * Allocate a memory block that holds the Area Descriptor and the + * frame buffer right behind it. To keep the code simple, everything + * is aligned on a 32-byte address. + */ + if (allocate_buf(&addr, ad_size + size, 32) < 0) + return NULL; + + ad = addr.vaddr; + ad->addr = cpu_to_le32(addr.paddr + ad_size); + ad->aoi_size = cpu_to_le32((yres << 16) | xres); + ad->src_size_g_alpha = cpu_to_le32((yres << 12) | xres); + ad->offset_xyi = 0; + ad->offset_xyd = 0; + + if (fb) + *fb = addr.vaddr + ad_size; + + return ad; +} + +int fsl_diu_init(u16 xres, u16 yres, u32 pixel_format, int gamma_fix) +{ + struct fb_videomode *fsl_diu_mode_db; + struct diu_ad *ad; + struct diu *hw = (struct diu *)CONFIG_SYS_DIU_ADDR; + u8 *gamma_table_base; + unsigned int i, j; + struct diu_addr gamma; + struct diu_addr cursor; + +/* Convert the X,Y resolution pair into a single number */ +#define RESOLUTION(x, y) (((u32)(x) << 16) | (y)) + + switch (RESOLUTION(xres, yres)) { + case RESOLUTION(800, 480): + fsl_diu_mode_db = &fsl_diu_mode_800_480; + break; + case RESOLUTION(800, 600): + fsl_diu_mode_db = &fsl_diu_mode_800_600; + break; + case RESOLUTION(1024, 768): + fsl_diu_mode_db = &fsl_diu_mode_1024_768; + break; + case RESOLUTION(1280, 1024): + fsl_diu_mode_db = &fsl_diu_mode_1280_1024; + break; + case RESOLUTION(1280, 720): + fsl_diu_mode_db = &fsl_diu_mode_1280_720; + break; + case RESOLUTION(1920, 1080): + fsl_diu_mode_db = &fsl_diu_mode_1920_1080; + break; + default: + printf("DIU: Unsupported resolution %ux%u\n", xres, yres); + return -1; + } + + /* read mode info */ + info.var.xres = fsl_diu_mode_db->xres; + info.var.yres = fsl_diu_mode_db->yres; + info.var.bits_per_pixel = 32; + info.var.pixclock = fsl_diu_mode_db->pixclock; + info.var.left_margin = fsl_diu_mode_db->left_margin; + info.var.right_margin = fsl_diu_mode_db->right_margin; + info.var.upper_margin = fsl_diu_mode_db->upper_margin; + info.var.lower_margin = fsl_diu_mode_db->lower_margin; + info.var.hsync_len = fsl_diu_mode_db->hsync_len; + info.var.vsync_len = fsl_diu_mode_db->vsync_len; + info.var.sync = fsl_diu_mode_db->sync; + info.var.vmode = fsl_diu_mode_db->vmode; + info.fix.line_length = info.var.xres * info.var.bits_per_pixel / 8; + + /* Memory allocation for framebuffer */ + info.screen_size = + info.var.xres * info.var.yres * (info.var.bits_per_pixel / 8); + ad = allocate_fb(info.var.xres, info.var.yres, + info.var.bits_per_pixel / 8, &info.screen_base); + if (!ad) { + printf("DIU: Out of memory\n"); + return -1; + } + + ad->pix_fmt = pixel_format; + + /* Disable chroma keying function */ + ad->ckmax_r = 0; + ad->ckmax_g = 0; + ad->ckmax_b = 0; + + ad->ckmin_r = 255; + ad->ckmin_g = 255; + ad->ckmin_b = 255; + + /* Initialize the gamma table */ + if (allocate_buf(&gamma, 256 * 3, 32) < 0) { + printf("DIU: Out of memory\n"); + return -1; + } + gamma_table_base = gamma.vaddr; + for (i = 0; i <= 2; i++) + for (j = 0; j < 256; j++) + *gamma_table_base++ = j; + + if (gamma_fix == 1) { /* fix the gamma */ + gamma_table_base = gamma.vaddr; + for (i = 0; i < 256 * 3; i++) { + gamma_table_base[i] = (gamma_table_base[i] << 2) + | ((gamma_table_base[i] >> 6) & 0x03); + } + } + + /* Initialize the cursor */ + if (allocate_buf(&cursor, 32 * 32 * 2, 32) < 0) { + printf("DIU: Can't alloc cursor data\n"); + return -1; + } + + /* Program DIU registers */ + out_be32(&hw->diu_mode, 0); /* Temporarily disable the DIU */ + + out_be32(&hw->gamma, gamma.paddr); + out_be32(&hw->cursor, cursor.paddr); + out_be32(&hw->bgnd, 0x007F7F7F); + out_be32(&hw->disp_size, info.var.yres << 16 | info.var.xres); + out_be32(&hw->hsyn_para, info.var.left_margin << 22 | + info.var.hsync_len << 11 | + info.var.right_margin); + + out_be32(&hw->vsyn_para, info.var.upper_margin << 22 | + info.var.vsync_len << 11 | + info.var.lower_margin); + + /* Pixel Clock configuration */ + diu_set_pixel_clock(info.var.pixclock); + + /* Set the frame buffers */ + out_be32(&hw->desc[0], virt_to_phys(ad)); + out_be32(&hw->desc[1], 0); + out_be32(&hw->desc[2], 0); + + /* Enable the DIU, set display to all three planes */ + out_be32(&hw->diu_mode, 1); + + return 0; +} + +void *video_hw_init(void) +{ + static GraphicDevice ctfb; + const char *options; + unsigned int depth = 0, freq = 0; + + if (!video_get_video_mode(&ctfb.winSizeX, &ctfb.winSizeY, &depth, &freq, + &options)) + return NULL; + + /* Find the monitor port, which is a required option */ + if (!options) + return NULL; + if (strncmp(options, "monitor=", 8) != 0) + return NULL; + + if (platform_diu_init(ctfb.winSizeX, ctfb.winSizeY, options + 8) < 0) + return NULL; + + /* fill in Graphic device struct */ + sprintf(ctfb.modeIdent, "%ix%ix%i %ikHz %iHz", + ctfb.winSizeX, ctfb.winSizeY, depth, 64, freq); + + ctfb.frameAdrs = (unsigned int)info.screen_base; + ctfb.plnSizeX = ctfb.winSizeX; + ctfb.plnSizeY = ctfb.winSizeY; + + ctfb.gdfBytesPP = 4; + ctfb.gdfIndex = GDF_32BIT_X888RGB; + + ctfb.isaBase = 0; + ctfb.pciBase = 0; + ctfb.memSize = info.screen_size; + + /* Cursor Start Address */ + ctfb.dprBase = 0; + ctfb.vprBase = 0; + ctfb.cprBase = 0; + + return &ctfb; +} diff --git a/qemu/roms/u-boot/drivers/video/ipu.h b/qemu/roms/u-boot/drivers/video/ipu.h new file mode 100644 index 000000000..3d2741f22 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/ipu.h @@ -0,0 +1,261 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_IPU_H__ +#define __ASM_ARCH_IPU_H__ + +#include +#include + +#define IDMA_CHAN_INVALID 0xFF +#define HIGH_RESOLUTION_WIDTH 1024 + +struct clk { + const char *name; + int id; + /* Source clock this clk depends on */ + struct clk *parent; + /* Secondary clock to enable/disable with this clock */ + struct clk *secondary; + /* Current clock rate */ + unsigned long rate; + /* Reference count of clock enable/disable */ + __s8 usecount; + /* Register bit position for clock's enable/disable control. */ + u8 enable_shift; + /* Register address for clock's enable/disable control. */ + void *enable_reg; + u32 flags; + /* + * Function ptr to recalculate the clock's rate based on parent + * clock's rate + */ + void (*recalc) (struct clk *); + /* + * Function ptr to set the clock to a new rate. The rate must match a + * supported rate returned from round_rate. Leave blank if clock is not + * programmable + */ + int (*set_rate) (struct clk *, unsigned long); + /* + * Function ptr to round the requested clock rate to the nearest + * supported rate that is less than or equal to the requested rate. + */ + unsigned long (*round_rate) (struct clk *, unsigned long); + /* + * Function ptr to enable the clock. Leave blank if clock can not + * be gated. + */ + int (*enable) (struct clk *); + /* + * Function ptr to disable the clock. Leave blank if clock can not + * be gated. + */ + void (*disable) (struct clk *); + /* Function ptr to set the parent clock of the clock. */ + int (*set_parent) (struct clk *, struct clk *); +}; + +/* + * Enumeration of Synchronous (Memory-less) panel types + */ +typedef enum { + IPU_PANEL_SHARP_TFT, + IPU_PANEL_TFT, +} ipu_panel_t; + +/* + * IPU Driver channels definitions. + * Note these are different from IDMA channels + */ +#define IPU_MAX_CH 32 +#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \ + ((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out) +#define _MAKE_ALT_CHAN(ch) (ch | (IPU_MAX_CH << 24)) +#define IPU_CHAN_ID(ch) (ch >> 24) +#define IPU_CHAN_ALT(ch) (ch & 0x02000000) +#define IPU_CHAN_ALPHA_IN_DMA(ch) ((uint32_t) (ch >> 6) & 0x3F) +#define IPU_CHAN_GRAPH_IN_DMA(ch) ((uint32_t) (ch >> 12) & 0x3F) +#define IPU_CHAN_VIDEO_IN_DMA(ch) ((uint32_t) (ch >> 18) & 0x3F) +#define IPU_CHAN_OUT_DMA(ch) ((uint32_t) (ch & 0x3F)) +#define NO_DMA 0x3F +#define ALT 1 + +/* + * Enumeration of IPU logical channels. An IPU logical channel is defined as a + * combination of an input (memory to IPU), output (IPU to memory), and/or + * secondary input IDMA channels and in some cases an Image Converter task. + * Some channels consist of only an input or output. + */ +typedef enum { + CHAN_NONE = -1, + + MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA), + MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA), + MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA), + MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA), + + MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA), + MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA), + MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0), + MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0), + + DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA), + DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA), + +} ipu_channel_t; + +/* + * Enumeration of types of buffers for a logical channel. + */ +typedef enum { + IPU_OUTPUT_BUFFER = 0, /*< Buffer for output from IPU */ + IPU_ALPHA_IN_BUFFER = 1, /*< Buffer for input to IPU */ + IPU_GRAPH_IN_BUFFER = 2, /*< Buffer for input to IPU */ + IPU_VIDEO_IN_BUFFER = 3, /*< Buffer for input to IPU */ + IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER, + IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER, +} ipu_buffer_t; + +#define IPU_PANEL_SERIAL 1 +#define IPU_PANEL_PARALLEL 2 + +struct ipu_channel { + u8 video_in_dma; + u8 alpha_in_dma; + u8 graph_in_dma; + u8 out_dma; +}; + +enum ipu_dmfc_type { + DMFC_NORMAL = 0, + DMFC_HIGH_RESOLUTION_DC, + DMFC_HIGH_RESOLUTION_DP, + DMFC_HIGH_RESOLUTION_ONLY_DP, +}; + + +/* + * Union of initialization parameters for a logical channel. + */ +typedef union { + struct { + uint32_t di; + unsigned char interlaced; + } mem_dc_sync; + struct { + uint32_t temp; + } mem_sdc_fg; + struct { + uint32_t di; + unsigned char interlaced; + uint32_t in_pixel_fmt; + uint32_t out_pixel_fmt; + unsigned char alpha_chan_en; + } mem_dp_bg_sync; + struct { + uint32_t temp; + } mem_sdc_bg; + struct { + uint32_t di; + unsigned char interlaced; + uint32_t in_pixel_fmt; + uint32_t out_pixel_fmt; + unsigned char alpha_chan_en; + } mem_dp_fg_sync; +} ipu_channel_params_t; + +/* + * Bitfield of Display Interface signal polarities. + */ +typedef struct { + unsigned datamask_en:1; + unsigned ext_clk:1; + unsigned interlaced:1; + unsigned odd_field_first:1; + unsigned clksel_en:1; + unsigned clkidle_en:1; + unsigned data_pol:1; /* true = inverted */ + unsigned clk_pol:1; /* true = rising edge */ + unsigned enable_pol:1; + unsigned Hsync_pol:1; /* true = active high */ + unsigned Vsync_pol:1; +} ipu_di_signal_cfg_t; + +typedef enum { + RGB, + YCbCr, + YUV +} ipu_color_space_t; + +/* Common IPU API */ +int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params); +void ipu_uninit_channel(ipu_channel_t channel); + +int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, + uint32_t u_offset, uint32_t v_offset); + +int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum, dma_addr_t phyaddr); + +int32_t ipu_is_channel_busy(ipu_channel_t channel); +void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum); +int32_t ipu_enable_channel(ipu_channel_t channel); +int32_t ipu_disable_channel(ipu_channel_t channel); + +int32_t ipu_init_sync_panel(int disp, + uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint32_t pixel_fmt, + uint16_t h_start_width, uint16_t h_sync_width, + uint16_t h_end_width, uint16_t v_start_width, + uint16_t v_sync_width, uint16_t v_end_width, + uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig); + +int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable, + uint8_t alpha); +int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable, + uint32_t colorKey); + +uint32_t bytes_per_pixel(uint32_t fmt); + +void clk_enable(struct clk *clk); +void clk_disable(struct clk *clk); +u32 clk_get_rate(struct clk *clk); +int clk_set_rate(struct clk *clk, unsigned long rate); +long clk_round_rate(struct clk *clk, unsigned long rate); +int clk_set_parent(struct clk *clk, struct clk *parent); +int clk_get_usecount(struct clk *clk); +struct clk *clk_get_parent(struct clk *clk); + +void ipu_dump_registers(void); +int ipu_probe(void); + +void ipu_dmfc_init(int dmfc_type, int first); +void ipu_init_dc_mappings(void); +void ipu_dmfc_set_wait4eot(int dma_chan, int width); +void ipu_dc_init(int dc_chan, int di, unsigned char interlaced); +void ipu_dc_uninit(int dc_chan); +void ipu_dp_dc_enable(ipu_channel_t channel); +int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt, + uint32_t out_pixel_fmt); +void ipu_dp_uninit(ipu_channel_t channel); +void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap); +ipu_color_space_t format_to_colorspace(uint32_t fmt); + +#endif diff --git a/qemu/roms/u-boot/drivers/video/ipu_common.c b/qemu/roms/u-boot/drivers/video/ipu_common.c new file mode 100644 index 000000000..8d4e92547 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/ipu_common.c @@ -0,0 +1,1196 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include "ipu.h" +#include "ipu_regs.h" + +extern struct mxc_ccm_reg *mxc_ccm; +extern u32 *ipu_cpmem_base; + +struct ipu_ch_param_word { + uint32_t data[5]; + uint32_t res[3]; +}; + +struct ipu_ch_param { + struct ipu_ch_param_word word[2]; +}; + +#define ipu_ch_param_addr(ch) (((struct ipu_ch_param *)ipu_cpmem_base) + (ch)) + +#define _param_word(base, w) \ + (((struct ipu_ch_param *)(base))->word[(w)].data) + +#define ipu_ch_param_set_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + _param_word(base, w)[i] |= (v) << off; \ + if (((bit) + (size) - 1) / 32 > i) { \ + _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \ + } \ +} + +#define ipu_ch_param_mod_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + u32 temp = _param_word(base, w)[i]; \ + temp &= ~(mask << off); \ + _param_word(base, w)[i] = temp | (v) << off; \ + if (((bit) + (size) - 1) / 32 > i) { \ + temp = _param_word(base, w)[i + 1]; \ + temp &= ~(mask >> (32 - off)); \ + _param_word(base, w)[i + 1] = \ + temp | ((v) >> (off ? (32 - off) : 0)); \ + } \ +} + +#define ipu_ch_param_read_field(base, w, bit, size) ({ \ + u32 temp2; \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + u32 temp1 = _param_word(base, w)[i]; \ + temp1 = mask & (temp1 >> off); \ + if (((bit)+(size) - 1) / 32 > i) { \ + temp2 = _param_word(base, w)[i + 1]; \ + temp2 &= mask >> (off ? (32 - off) : 0); \ + temp1 |= temp2 << (off ? (32 - off) : 0); \ + } \ + temp1; \ +}) + +#define IPU_SW_RST_TOUT_USEC (10000) + +void clk_enable(struct clk *clk) +{ + if (clk) { + if (clk->usecount++ == 0) { + clk->enable(clk); + } + } +} + +void clk_disable(struct clk *clk) +{ + if (clk) { + if (!(--clk->usecount)) { + if (clk->disable) + clk->disable(clk); + } + } +} + +int clk_get_usecount(struct clk *clk) +{ + if (clk == NULL) + return 0; + + return clk->usecount; +} + +u32 clk_get_rate(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->rate; +} + +struct clk *clk_get_parent(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->parent; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + if (clk && clk->set_rate) + clk->set_rate(clk, rate); + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (clk == NULL || !clk->round_rate) + return 0; + + return clk->round_rate(clk, rate); +} + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + clk->parent = parent; + if (clk->set_parent) + return clk->set_parent(clk, parent); + return 0; +} + +static int clk_ipu_enable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(clk->enable_reg); + reg |= MXC_CCM_CCGR_CG_MASK << clk->enable_shift; + __raw_writel(reg, clk->enable_reg); + +#if defined(CONFIG_MX51) || defined(CONFIG_MX53) + /* Handshake with IPU when certain clock rates are changed. */ + reg = __raw_readl(&mxc_ccm->ccdr); + reg &= ~MXC_CCM_CCDR_IPU_HS_MASK; + __raw_writel(reg, &mxc_ccm->ccdr); + + /* Handshake with IPU when LPM is entered as its enabled. */ + reg = __raw_readl(&mxc_ccm->clpcr); + reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; + __raw_writel(reg, &mxc_ccm->clpcr); +#endif + return 0; +} + +static void clk_ipu_disable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(clk->enable_reg); + reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift); + __raw_writel(reg, clk->enable_reg); + +#if defined(CONFIG_MX51) || defined(CONFIG_MX53) + /* + * No handshake with IPU whe dividers are changed + * as its not enabled. + */ + reg = __raw_readl(&mxc_ccm->ccdr); + reg |= MXC_CCM_CCDR_IPU_HS_MASK; + __raw_writel(reg, &mxc_ccm->ccdr); + + /* No handshake with IPU when LPM is entered as its not enabled. */ + reg = __raw_readl(&mxc_ccm->clpcr); + reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; + __raw_writel(reg, &mxc_ccm->clpcr); +#endif +} + + +static struct clk ipu_clk = { + .name = "ipu_clk", + .rate = CONFIG_IPUV3_CLK, +#if defined(CONFIG_MX51) || defined(CONFIG_MX53) + .enable_reg = (u32 *)(CCM_BASE_ADDR + + offsetof(struct mxc_ccm_reg, CCGR5)), + .enable_shift = MXC_CCM_CCGR5_IPU_OFFSET, +#else + .enable_reg = (u32 *)(CCM_BASE_ADDR + + offsetof(struct mxc_ccm_reg, CCGR3)), + .enable_shift = MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET, +#endif + .enable = clk_ipu_enable, + .disable = clk_ipu_disable, + .usecount = 0, +}; + +static struct clk ldb_clk = { + .name = "ldb_clk", + .rate = 65000000, + .usecount = 0, +}; + +/* Globals */ +struct clk *g_ipu_clk; +struct clk *g_ldb_clk; +unsigned char g_ipu_clk_enabled; +struct clk *g_di_clk[2]; +struct clk *g_pixel_clk[2]; +unsigned char g_dc_di_assignment[10]; +uint32_t g_channel_init_mask; +uint32_t g_channel_enable_mask; + +static int ipu_dc_use_count; +static int ipu_dp_use_count; +static int ipu_dmfc_use_count; +static int ipu_di_use_count[2]; + +u32 *ipu_cpmem_base; +u32 *ipu_dc_tmpl_reg; + +/* Static functions */ + +static inline void ipu_ch_param_set_high_priority(uint32_t ch) +{ + ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 93, 2, 1); +}; + +static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type) +{ + return ((uint32_t) ch >> (6 * type)) & 0x3F; +}; + +/* Either DP BG or DP FG can be graphic window */ +static inline int ipu_is_dp_graphic_chan(uint32_t dma_chan) +{ + return (dma_chan == 23 || dma_chan == 27); +} + +static inline int ipu_is_dmfc_chan(uint32_t dma_chan) +{ + return ((dma_chan >= 23) && (dma_chan <= 29)); +} + + +static inline void ipu_ch_param_set_buffer(uint32_t ch, int bufNum, + dma_addr_t phyaddr) +{ + ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 29 * bufNum, 29, + phyaddr / 8); +}; + +#define idma_is_valid(ch) (ch != NO_DMA) +#define idma_mask(ch) (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0) +#define idma_is_set(reg, dma) (__raw_readl(reg(dma)) & idma_mask(dma)) + +static void ipu_pixel_clk_recalc(struct clk *clk) +{ + u32 div = __raw_readl(DI_BS_CLKGEN0(clk->id)); + if (div == 0) + clk->rate = 0; + else + clk->rate = (clk->parent->rate * 16) / div; +} + +static unsigned long ipu_pixel_clk_round_rate(struct clk *clk, + unsigned long rate) +{ + u32 div, div1; + u32 tmp; + /* + * Calculate divider + * Fractional part is 4 bits, + * so simply multiply by 2^4 to get fractional part. + */ + tmp = (clk->parent->rate * 16); + div = tmp / rate; + + if (div < 0x10) /* Min DI disp clock divider is 1 */ + div = 0x10; + if (div & ~0xFEF) + div &= 0xFF8; + else { + div1 = div & 0xFE0; + if ((tmp/div1 - tmp/div) < rate / 4) + div = div1; + else + div &= 0xFF8; + } + return (clk->parent->rate * 16) / div; +} + +static int ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate) +{ + u32 div = (clk->parent->rate * 16) / rate; + + __raw_writel(div, DI_BS_CLKGEN0(clk->id)); + + /* Setup pixel clock timing */ + __raw_writel((div / 16) << 16, DI_BS_CLKGEN1(clk->id)); + + clk->rate = (clk->parent->rate * 16) / div; + return 0; +} + +static int ipu_pixel_clk_enable(struct clk *clk) +{ + u32 disp_gen = __raw_readl(IPU_DISP_GEN); + disp_gen |= clk->id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE; + __raw_writel(disp_gen, IPU_DISP_GEN); + + return 0; +} + +static void ipu_pixel_clk_disable(struct clk *clk) +{ + u32 disp_gen = __raw_readl(IPU_DISP_GEN); + disp_gen &= clk->id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE; + __raw_writel(disp_gen, IPU_DISP_GEN); + +} + +static int ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent) +{ + u32 di_gen = __raw_readl(DI_GENERAL(clk->id)); + + if (parent == g_ipu_clk) + di_gen &= ~DI_GEN_DI_CLK_EXT; + else if (!IS_ERR(g_di_clk[clk->id]) && parent == g_ldb_clk) + di_gen |= DI_GEN_DI_CLK_EXT; + else + return -EINVAL; + + __raw_writel(di_gen, DI_GENERAL(clk->id)); + ipu_pixel_clk_recalc(clk); + return 0; +} + +static struct clk pixel_clk[] = { + { + .name = "pixel_clk", + .id = 0, + .recalc = ipu_pixel_clk_recalc, + .set_rate = ipu_pixel_clk_set_rate, + .round_rate = ipu_pixel_clk_round_rate, + .set_parent = ipu_pixel_clk_set_parent, + .enable = ipu_pixel_clk_enable, + .disable = ipu_pixel_clk_disable, + .usecount = 0, + }, + { + .name = "pixel_clk", + .id = 1, + .recalc = ipu_pixel_clk_recalc, + .set_rate = ipu_pixel_clk_set_rate, + .round_rate = ipu_pixel_clk_round_rate, + .set_parent = ipu_pixel_clk_set_parent, + .enable = ipu_pixel_clk_enable, + .disable = ipu_pixel_clk_disable, + .usecount = 0, + }, +}; + +/* + * This function resets IPU + */ +void ipu_reset(void) +{ + u32 *reg; + u32 value; + int timeout = IPU_SW_RST_TOUT_USEC; + + reg = (u32 *)SRC_BASE_ADDR; + value = __raw_readl(reg); + value = value | SW_IPU_RST; + __raw_writel(value, reg); + + while (__raw_readl(reg) & SW_IPU_RST) { + udelay(1); + if (!(timeout--)) { + printf("ipu software reset timeout\n"); + break; + } + }; +} + +/* + * This function is called by the driver framework to initialize the IPU + * hardware. + * + * @param dev The device structure for the IPU passed in by the + * driver framework. + * + * @return Returns 0 on success or negative error code on error + */ +int ipu_probe(void) +{ + unsigned long ipu_base; +#if defined CONFIG_MX51 + u32 temp; + + u32 *reg_hsc_mcd = (u32 *)MIPI_HSC_BASE_ADDR; + u32 *reg_hsc_mxt_conf = (u32 *)(MIPI_HSC_BASE_ADDR + 0x800); + + __raw_writel(0xF00, reg_hsc_mcd); + + /* CSI mode reserved*/ + temp = __raw_readl(reg_hsc_mxt_conf); + __raw_writel(temp | 0x0FF, reg_hsc_mxt_conf); + + temp = __raw_readl(reg_hsc_mxt_conf); + __raw_writel(temp | 0x10000, reg_hsc_mxt_conf); +#endif + + ipu_base = IPU_CTRL_BASE_ADDR; + ipu_cpmem_base = (u32 *)(ipu_base + IPU_CPMEM_REG_BASE); + ipu_dc_tmpl_reg = (u32 *)(ipu_base + IPU_DC_TMPL_REG_BASE); + + g_pixel_clk[0] = &pixel_clk[0]; + g_pixel_clk[1] = &pixel_clk[1]; + + g_ipu_clk = &ipu_clk; + debug("ipu_clk = %u\n", clk_get_rate(g_ipu_clk)); + g_ldb_clk = &ldb_clk; + debug("ldb_clk = %u\n", clk_get_rate(g_ldb_clk)); + ipu_reset(); + + clk_set_parent(g_pixel_clk[0], g_ipu_clk); + clk_set_parent(g_pixel_clk[1], g_ipu_clk); + clk_enable(g_ipu_clk); + + g_di_clk[0] = NULL; + g_di_clk[1] = NULL; + + __raw_writel(0x807FFFFF, IPU_MEM_RST); + while (__raw_readl(IPU_MEM_RST) & 0x80000000) + ; + + ipu_init_dc_mappings(); + + __raw_writel(0, IPU_INT_CTRL(5)); + __raw_writel(0, IPU_INT_CTRL(6)); + __raw_writel(0, IPU_INT_CTRL(9)); + __raw_writel(0, IPU_INT_CTRL(10)); + + /* DMFC Init */ + ipu_dmfc_init(DMFC_NORMAL, 1); + + /* Set sync refresh channels as high priority */ + __raw_writel(0x18800000L, IDMAC_CHA_PRI(0)); + + /* Set MCU_T to divide MCU access window into 2 */ + __raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN); + + clk_disable(g_ipu_clk); + + return 0; +} + +void ipu_dump_registers(void) +{ + debug("IPU_CONF = \t0x%08X\n", __raw_readl(IPU_CONF)); + debug("IDMAC_CONF = \t0x%08X\n", __raw_readl(IDMAC_CONF)); + debug("IDMAC_CHA_EN1 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_EN(0))); + debug("IDMAC_CHA_EN2 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_EN(32))); + debug("IDMAC_CHA_PRI1 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_PRI(0))); + debug("IDMAC_CHA_PRI2 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_PRI(32))); + debug("IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", + __raw_readl(IPU_CHA_DB_MODE_SEL(0))); + debug("IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", + __raw_readl(IPU_CHA_DB_MODE_SEL(32))); + debug("DMFC_WR_CHAN = \t0x%08X\n", + __raw_readl(DMFC_WR_CHAN)); + debug("DMFC_WR_CHAN_DEF = \t0x%08X\n", + __raw_readl(DMFC_WR_CHAN_DEF)); + debug("DMFC_DP_CHAN = \t0x%08X\n", + __raw_readl(DMFC_DP_CHAN)); + debug("DMFC_DP_CHAN_DEF = \t0x%08X\n", + __raw_readl(DMFC_DP_CHAN_DEF)); + debug("DMFC_IC_CTRL = \t0x%08X\n", + __raw_readl(DMFC_IC_CTRL)); + debug("IPU_FS_PROC_FLOW1 = \t0x%08X\n", + __raw_readl(IPU_FS_PROC_FLOW1)); + debug("IPU_FS_PROC_FLOW2 = \t0x%08X\n", + __raw_readl(IPU_FS_PROC_FLOW2)); + debug("IPU_FS_PROC_FLOW3 = \t0x%08X\n", + __raw_readl(IPU_FS_PROC_FLOW3)); + debug("IPU_FS_DISP_FLOW1 = \t0x%08X\n", + __raw_readl(IPU_FS_DISP_FLOW1)); +} + +/* + * This function is called to initialize a logical IPU channel. + * + * @param channel Input parameter for the logical channel ID to init. + * + * @param params Input parameter containing union of channel + * initialization parameters. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params) +{ + int ret = 0; + uint32_t ipu_conf; + + debug("init channel = %d\n", IPU_CHAN_ID(channel)); + + if (g_ipu_clk_enabled == 0) { + g_ipu_clk_enabled = 1; + clk_enable(g_ipu_clk); + } + + + if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) { + printf("Warning: channel already initialized %d\n", + IPU_CHAN_ID(channel)); + } + + ipu_conf = __raw_readl(IPU_CONF); + + switch (channel) { + case MEM_DC_SYNC: + if (params->mem_dc_sync.di > 1) { + ret = -EINVAL; + goto err; + } + + g_dc_di_assignment[1] = params->mem_dc_sync.di; + ipu_dc_init(1, params->mem_dc_sync.di, + params->mem_dc_sync.interlaced); + ipu_di_use_count[params->mem_dc_sync.di]++; + ipu_dc_use_count++; + ipu_dmfc_use_count++; + break; + case MEM_BG_SYNC: + if (params->mem_dp_bg_sync.di > 1) { + ret = -EINVAL; + goto err; + } + + g_dc_di_assignment[5] = params->mem_dp_bg_sync.di; + ipu_dp_init(channel, params->mem_dp_bg_sync.in_pixel_fmt, + params->mem_dp_bg_sync.out_pixel_fmt); + ipu_dc_init(5, params->mem_dp_bg_sync.di, + params->mem_dp_bg_sync.interlaced); + ipu_di_use_count[params->mem_dp_bg_sync.di]++; + ipu_dc_use_count++; + ipu_dp_use_count++; + ipu_dmfc_use_count++; + break; + case MEM_FG_SYNC: + ipu_dp_init(channel, params->mem_dp_fg_sync.in_pixel_fmt, + params->mem_dp_fg_sync.out_pixel_fmt); + + ipu_dc_use_count++; + ipu_dp_use_count++; + ipu_dmfc_use_count++; + break; + default: + printf("Missing channel initialization\n"); + break; + } + + /* Enable IPU sub module */ + g_channel_init_mask |= 1L << IPU_CHAN_ID(channel); + if (ipu_dc_use_count == 1) + ipu_conf |= IPU_CONF_DC_EN; + if (ipu_dp_use_count == 1) + ipu_conf |= IPU_CONF_DP_EN; + if (ipu_dmfc_use_count == 1) + ipu_conf |= IPU_CONF_DMFC_EN; + if (ipu_di_use_count[0] == 1) { + ipu_conf |= IPU_CONF_DI0_EN; + } + if (ipu_di_use_count[1] == 1) { + ipu_conf |= IPU_CONF_DI1_EN; + } + + __raw_writel(ipu_conf, IPU_CONF); + +err: + return ret; +} + +/* + * This function is called to uninitialize a logical IPU channel. + * + * @param channel Input parameter for the logical channel ID to uninit. + */ +void ipu_uninit_channel(ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma, out_dma = 0; + uint32_t ipu_conf; + + if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + debug("Channel already uninitialized %d\n", + IPU_CHAN_ID(channel)); + return; + } + + /* + * Make sure channel is disabled + * Get input and output dma channels + */ + in_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + out_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if (idma_is_set(IDMAC_CHA_EN, in_dma) || + idma_is_set(IDMAC_CHA_EN, out_dma)) { + printf( + "Channel %d is not disabled, disable first\n", + IPU_CHAN_ID(channel)); + return; + } + + ipu_conf = __raw_readl(IPU_CONF); + + /* Reset the double buffer */ + reg = __raw_readl(IPU_CHA_DB_MODE_SEL(in_dma)); + __raw_writel(reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma)); + reg = __raw_readl(IPU_CHA_DB_MODE_SEL(out_dma)); + __raw_writel(reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma)); + + switch (channel) { + case MEM_DC_SYNC: + ipu_dc_uninit(1); + ipu_di_use_count[g_dc_di_assignment[1]]--; + ipu_dc_use_count--; + ipu_dmfc_use_count--; + break; + case MEM_BG_SYNC: + ipu_dp_uninit(channel); + ipu_dc_uninit(5); + ipu_di_use_count[g_dc_di_assignment[5]]--; + ipu_dc_use_count--; + ipu_dp_use_count--; + ipu_dmfc_use_count--; + break; + case MEM_FG_SYNC: + ipu_dp_uninit(channel); + ipu_dc_use_count--; + ipu_dp_use_count--; + ipu_dmfc_use_count--; + break; + default: + break; + } + + g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel)); + + if (ipu_dc_use_count == 0) + ipu_conf &= ~IPU_CONF_DC_EN; + if (ipu_dp_use_count == 0) + ipu_conf &= ~IPU_CONF_DP_EN; + if (ipu_dmfc_use_count == 0) + ipu_conf &= ~IPU_CONF_DMFC_EN; + if (ipu_di_use_count[0] == 0) { + ipu_conf &= ~IPU_CONF_DI0_EN; + } + if (ipu_di_use_count[1] == 0) { + ipu_conf &= ~IPU_CONF_DI1_EN; + } + + __raw_writel(ipu_conf, IPU_CONF); + + if (ipu_conf == 0) { + clk_disable(g_ipu_clk); + g_ipu_clk_enabled = 0; + } + +} + +static inline void ipu_ch_param_dump(int ch) +{ +#ifdef DEBUG + struct ipu_ch_param *p = ipu_ch_param_addr(ch); + debug("ch %d word 0 - %08X %08X %08X %08X %08X\n", ch, + p->word[0].data[0], p->word[0].data[1], p->word[0].data[2], + p->word[0].data[3], p->word[0].data[4]); + debug("ch %d word 1 - %08X %08X %08X %08X %08X\n", ch, + p->word[1].data[0], p->word[1].data[1], p->word[1].data[2], + p->word[1].data[3], p->word[1].data[4]); + debug("PFS 0x%x, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 85, 4)); + debug("BPP 0x%x, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 107, 3)); + debug("NPB 0x%x\n", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 78, 7)); + + debug("FW %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 125, 13)); + debug("FH %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 138, 12)); + debug("Stride %d\n", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 102, 14)); + + debug("Width0 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 116, 3)); + debug("Width1 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 119, 3)); + debug("Width2 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 122, 3)); + debug("Width3 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 125, 3)); + debug("Offset0 %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 128, 5)); + debug("Offset1 %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 133, 5)); + debug("Offset2 %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 138, 5)); + debug("Offset3 %d\n", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 143, 5)); +#endif +} + +static inline void ipu_ch_params_set_packing(struct ipu_ch_param *p, + int red_width, int red_offset, + int green_width, int green_offset, + int blue_width, int blue_offset, + int alpha_width, int alpha_offset) +{ + /* Setup red width and offset */ + ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1); + ipu_ch_param_set_field(p, 1, 128, 5, red_offset); + /* Setup green width and offset */ + ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1); + ipu_ch_param_set_field(p, 1, 133, 5, green_offset); + /* Setup blue width and offset */ + ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1); + ipu_ch_param_set_field(p, 1, 138, 5, blue_offset); + /* Setup alpha width and offset */ + ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1); + ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset); +} + +static void ipu_ch_param_init(int ch, + uint32_t pixel_fmt, uint32_t width, + uint32_t height, uint32_t stride, + uint32_t u, uint32_t v, + uint32_t uv_stride, dma_addr_t addr0, + dma_addr_t addr1) +{ + uint32_t u_offset = 0; + uint32_t v_offset = 0; + struct ipu_ch_param params; + + memset(¶ms, 0, sizeof(params)); + + ipu_ch_param_set_field(¶ms, 0, 125, 13, width - 1); + + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 0, 138, 12, (height / 2) - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, (stride * 2) - 1); + } else { + ipu_ch_param_set_field(¶ms, 0, 138, 12, height - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, stride - 1); + } + + ipu_ch_param_set_field(¶ms, 1, 0, 29, addr0 >> 3); + ipu_ch_param_set_field(¶ms, 1, 29, 29, addr1 >> 3); + + switch (pixel_fmt) { + case IPU_PIX_FMT_GENERIC: + /*Represents 8-bit Generic data */ + ipu_ch_param_set_field(¶ms, 0, 107, 3, 5); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 6); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 63); /* burst size */ + + break; + case IPU_PIX_FMT_GENERIC_32: + /*Represents 32-bit Generic data */ + break; + case IPU_PIX_FMT_RGB565: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 5, 0, 6, 5, 5, 11, 8, 16); + break; + case IPU_PIX_FMT_BGR24: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_YUV444: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 16, 8, 8, 8, 0, 8, 24); + break; + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0); + break; + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 24, 8, 16, 8, 8, 8, 0); + break; + case IPU_PIX_FMT_ABGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + + ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_UYVY: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0xA); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + break; + case IPU_PIX_FMT_YUYV: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0x8); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + break; + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = stride * height; + v_offset = u_offset + (uv_stride * height / 2); + /* burst size */ + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); + uv_stride = uv_stride*2; + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); + } + break; + case IPU_PIX_FMT_YVU422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + v_offset = (v == 0) ? stride * height : v; + u_offset = (u == 0) ? v_offset + v_offset / 2 : u; + break; + case IPU_PIX_FMT_YUV422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = (u == 0) ? stride * height : u; + v_offset = (v == 0) ? u_offset + u_offset / 2 : v; + break; + case IPU_PIX_FMT_NV12: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 4); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + uv_stride = stride; + u_offset = (u == 0) ? stride * height : u; + break; + default: + puts("mxc ipu: unimplemented pixel format\n"); + break; + } + + + if (uv_stride) + ipu_ch_param_set_field(¶ms, 1, 128, 14, uv_stride - 1); + + /* Get the uv offset from user when need cropping */ + if (u || v) { + u_offset = u; + v_offset = v; + } + + /* UBO and VBO are 22-bit */ + if (u_offset/8 > 0x3fffff) + puts("The value of U offset exceeds IPU limitation\n"); + if (v_offset/8 > 0x3fffff) + puts("The value of V offset exceeds IPU limitation\n"); + + ipu_ch_param_set_field(¶ms, 0, 46, 22, u_offset / 8); + ipu_ch_param_set_field(¶ms, 0, 68, 22, v_offset / 8); + + debug("initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ch)); + memcpy(ipu_ch_param_addr(ch), ¶ms, sizeof(params)); +}; + +/* + * This function is called to initialize a buffer for logical IPU channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to initialize. + * + * @param pixel_fmt Input parameter for pixel format of buffer. + * Pixel format is a FOURCC ASCII code. + * + * @param width Input parameter for width of buffer in pixels. + * + * @param height Input parameter for height of buffer in pixels. + * + * @param stride Input parameter for stride length of buffer + * in pixels. + * + * @param phyaddr_0 Input parameter buffer 0 physical address. + * + * @param phyaddr_1 Input parameter buffer 1 physical address. + * Setting this to a value other than NULL enables + * double buffering mode. + * + * @param u private u offset for additional cropping, + * zero if not used. + * + * @param v private v offset for additional cropping, + * zero if not used. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, + uint32_t u, uint32_t v) +{ + uint32_t reg; + uint32_t dma_chan; + + dma_chan = channel_2_dma(channel, type); + if (!idma_is_valid(dma_chan)) + return -EINVAL; + + if (stride < width * bytes_per_pixel(pixel_fmt)) + stride = width * bytes_per_pixel(pixel_fmt); + + if (stride % 4) { + printf( + "Stride not 32-bit aligned, stride = %d\n", stride); + return -EINVAL; + } + /* Build parameter memory data for DMA channel */ + ipu_ch_param_init(dma_chan, pixel_fmt, width, height, stride, u, v, 0, + phyaddr_0, phyaddr_1); + + if (ipu_is_dmfc_chan(dma_chan)) { + ipu_dmfc_set_wait4eot(dma_chan, width); + } + + if (idma_is_set(IDMAC_CHA_PRI, dma_chan)) + ipu_ch_param_set_high_priority(dma_chan); + + ipu_ch_param_dump(dma_chan); + + reg = __raw_readl(IPU_CHA_DB_MODE_SEL(dma_chan)); + if (phyaddr_1) + reg |= idma_mask(dma_chan); + else + reg &= ~idma_mask(dma_chan); + __raw_writel(reg, IPU_CHA_DB_MODE_SEL(dma_chan)); + + /* Reset to buffer 0 */ + __raw_writel(idma_mask(dma_chan), IPU_CHA_CUR_BUF(dma_chan)); + + return 0; +} + +/* + * This function enables a logical channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_enable_channel(ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma; + uint32_t out_dma; + + if (g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) { + printf("Warning: channel already enabled %d\n", + IPU_CHAN_ID(channel)); + } + + /* Get input and output dma channels */ + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if (idma_is_valid(in_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(in_dma)); + __raw_writel(reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); + } + if (idma_is_valid(out_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(out_dma)); + __raw_writel(reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); + } + + if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) || + (channel == MEM_FG_SYNC)) + ipu_dp_dc_enable(channel); + + g_channel_enable_mask |= 1L << IPU_CHAN_ID(channel); + + return 0; +} + +/* + * This function clear buffer ready for a logical channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to clear. + * + * @param bufNum Input parameter for which buffer number clear + * ready state. + * + */ +void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum) +{ + uint32_t dma_ch = channel_2_dma(channel, type); + + if (!idma_is_valid(dma_ch)) + return; + + __raw_writel(0xF0000000, IPU_GPR); /* write one to clear */ + if (bufNum == 0) { + if (idma_is_set(IPU_CHA_BUF0_RDY, dma_ch)) { + __raw_writel(idma_mask(dma_ch), + IPU_CHA_BUF0_RDY(dma_ch)); + } + } else { + if (idma_is_set(IPU_CHA_BUF1_RDY, dma_ch)) { + __raw_writel(idma_mask(dma_ch), + IPU_CHA_BUF1_RDY(dma_ch)); + } + } + __raw_writel(0x0, IPU_GPR); /* write one to set */ +} + +/* + * This function disables a logical channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @param wait_for_stop Flag to set whether to wait for channel end + * of frame or return immediately. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_disable_channel(ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma; + uint32_t out_dma; + + if ((g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + debug("Channel already disabled %d\n", + IPU_CHAN_ID(channel)); + return 0; + } + + /* Get input and output dma channels */ + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if ((idma_is_valid(in_dma) && + !idma_is_set(IDMAC_CHA_EN, in_dma)) + && (idma_is_valid(out_dma) && + !idma_is_set(IDMAC_CHA_EN, out_dma))) + return -EINVAL; + + if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) || + (channel == MEM_DC_SYNC)) { + ipu_dp_dc_disable(channel, 0); + } + + /* Disable DMA channel(s) */ + if (idma_is_valid(in_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(in_dma)); + __raw_writel(reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); + __raw_writel(idma_mask(in_dma), IPU_CHA_CUR_BUF(in_dma)); + } + if (idma_is_valid(out_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(out_dma)); + __raw_writel(reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); + __raw_writel(idma_mask(out_dma), IPU_CHA_CUR_BUF(out_dma)); + } + + g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel)); + + /* Set channel buffers NOT to be ready */ + if (idma_is_valid(in_dma)) { + ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 0); + ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 1); + } + if (idma_is_valid(out_dma)) { + ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 0); + ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 1); + } + + return 0; +} + +uint32_t bytes_per_pixel(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: /*generic data */ + case IPU_PIX_FMT_RGB332: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YUV422P: + return 1; + break; + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_UYVY: + return 2; + break; + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + return 3; + break; + case IPU_PIX_FMT_GENERIC_32: /*generic data */ + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_ABGR32: + return 4; + break; + default: + return 1; + break; + } + return 0; +} + +ipu_color_space_t format_to_colorspace(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_RGB666: + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_ABGR32: + case IPU_PIX_FMT_LVDS666: + case IPU_PIX_FMT_LVDS888: + return RGB; + break; + + default: + return YCbCr; + break; + } + return RGB; +} diff --git a/qemu/roms/u-boot/drivers/video/ipu_disp.c b/qemu/roms/u-boot/drivers/video/ipu_disp.c new file mode 100644 index 000000000..cefd2dc14 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/ipu_disp.c @@ -0,0 +1,1335 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include "ipu.h" +#include "ipu_regs.h" + +enum csc_type_t { + RGB2YUV = 0, + YUV2RGB, + RGB2RGB, + YUV2YUV, + CSC_NONE, + CSC_NUM +}; + +struct dp_csc_param_t { + int mode; + void *coeff; +}; + +#define SYNC_WAVE 0 + +/* DC display ID assignments */ +#define DC_DISP_ID_SYNC(di) (di) +#define DC_DISP_ID_SERIAL 2 +#define DC_DISP_ID_ASYNC 3 + +int dmfc_type_setup; +static int dmfc_size_28, dmfc_size_29, dmfc_size_24, dmfc_size_27, dmfc_size_23; +int g_di1_tvout; + +extern struct clk *g_ipu_clk; +extern struct clk *g_ldb_clk; +extern struct clk *g_di_clk[2]; +extern struct clk *g_pixel_clk[2]; + +extern unsigned char g_ipu_clk_enabled; +extern unsigned char g_dc_di_assignment[]; + +void ipu_dmfc_init(int dmfc_type, int first) +{ + u32 dmfc_wr_chan, dmfc_dp_chan; + + if (first) { + if (dmfc_type_setup > dmfc_type) + dmfc_type = dmfc_type_setup; + else + dmfc_type_setup = dmfc_type; + + /* disable DMFC-IC channel*/ + __raw_writel(0x2, DMFC_IC_CTRL); + } else if (dmfc_type_setup >= DMFC_HIGH_RESOLUTION_DC) { + printf("DMFC high resolution has set, will not change\n"); + return; + } else + dmfc_type_setup = dmfc_type; + + if (dmfc_type == DMFC_HIGH_RESOLUTION_DC) { + /* 1 - segment 0~3; + * 5B - segement 4, 5; + * 5F - segement 6, 7; + * 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC DC HIGH RES: 1(0~3), 5B(4,5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000088; + dmfc_dp_chan = 0x00009694; + dmfc_size_28 = 256 * 4; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 128 * 4; + dmfc_size_23 = 128 * 4; + } else if (dmfc_type == DMFC_HIGH_RESOLUTION_DP) { + /* 1 - segment 0, 1; + * 5B - segement 2~5; + * 5F - segement 6,7; + * 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC DP HIGH RES: 1(0,1), 5B(2~5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000090; + dmfc_dp_chan = 0x0000968a; + dmfc_size_28 = 128 * 4; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 128 * 4; + dmfc_size_23 = 256 * 4; + } else if (dmfc_type == DMFC_HIGH_RESOLUTION_ONLY_DP) { + /* 5B - segement 0~3; + * 5F - segement 4~7; + * 1, 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC ONLY-DP HIGH RES: 5B(0~3), 5F(4~7)\n"); + dmfc_wr_chan = 0x00000000; + dmfc_dp_chan = 0x00008c88; + dmfc_size_28 = 0; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 256 * 4; + dmfc_size_23 = 256 * 4; + } else { + /* 1 - segment 0, 1; + * 5B - segement 4, 5; + * 5F - segement 6, 7; + * 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000090; + dmfc_dp_chan = 0x00009694; + dmfc_size_28 = 128 * 4; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 128 * 4; + dmfc_size_23 = 128 * 4; + } + __raw_writel(dmfc_wr_chan, DMFC_WR_CHAN); + __raw_writel(0x202020F6, DMFC_WR_CHAN_DEF); + __raw_writel(dmfc_dp_chan, DMFC_DP_CHAN); + /* Enable chan 5 watermark set at 5 bursts and clear at 7 bursts */ + __raw_writel(0x2020F6F6, DMFC_DP_CHAN_DEF); +} + +void ipu_dmfc_set_wait4eot(int dma_chan, int width) +{ + u32 dmfc_gen1 = __raw_readl(DMFC_GENERAL1); + + if (width >= HIGH_RESOLUTION_WIDTH) { + if (dma_chan == 23) + ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DP, 0); + else if (dma_chan == 28) + ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DC, 0); + } + + if (dma_chan == 23) { /*5B*/ + if (dmfc_size_23 / width > 3) + dmfc_gen1 |= 1UL << 20; + else + dmfc_gen1 &= ~(1UL << 20); + } else if (dma_chan == 24) { /*6B*/ + if (dmfc_size_24 / width > 1) + dmfc_gen1 |= 1UL << 22; + else + dmfc_gen1 &= ~(1UL << 22); + } else if (dma_chan == 27) { /*5F*/ + if (dmfc_size_27 / width > 2) + dmfc_gen1 |= 1UL << 21; + else + dmfc_gen1 &= ~(1UL << 21); + } else if (dma_chan == 28) { /*1*/ + if (dmfc_size_28 / width > 2) + dmfc_gen1 |= 1UL << 16; + else + dmfc_gen1 &= ~(1UL << 16); + } else if (dma_chan == 29) { /*6F*/ + if (dmfc_size_29 / width > 1) + dmfc_gen1 |= 1UL << 23; + else + dmfc_gen1 &= ~(1UL << 23); + } + + __raw_writel(dmfc_gen1, DMFC_GENERAL1); +} + +static void ipu_di_data_wave_config(int di, + int wave_gen, + int access_size, int component_size) +{ + u32 reg; + reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | + (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); + __raw_writel(reg, DI_DW_GEN(di, wave_gen)); +} + +static void ipu_di_data_pin_config(int di, int wave_gen, int di_pin, int set, + int up, int down) +{ + u32 reg; + + reg = __raw_readl(DI_DW_GEN(di, wave_gen)); + reg &= ~(0x3 << (di_pin * 2)); + reg |= set << (di_pin * 2); + __raw_writel(reg, DI_DW_GEN(di, wave_gen)); + + __raw_writel((down << 16) | up, DI_DW_SET(di, wave_gen, set)); +} + +static void ipu_di_sync_config(int di, int wave_gen, + int run_count, int run_src, + int offset_count, int offset_src, + 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) +{ + u32 reg; + + if ((run_count >= 0x1000) || (offset_count >= 0x1000) || + (repeat_count >= 0x1000) || + (cnt_up >= 0x400) || (cnt_down >= 0x400)) { + printf("DI%d counters out of range.\n", di); + return; + } + + reg = (run_count << 19) | (++run_src << 16) | + (offset_count << 3) | ++offset_src; + __raw_writel(reg, DI_SW_GEN0(di, wave_gen)); + reg = (cnt_polarity_gen_en << 29) | (++cnt_clr_src << 25) | + (++cnt_polarity_trigger_src << 12) | (++cnt_polarity_clr_src << 9); + reg |= (cnt_down << 16) | cnt_up; + if (repeat_count == 0) { + /* Enable auto reload */ + reg |= 0x10000000; + } + __raw_writel(reg, DI_SW_GEN1(di, wave_gen)); + reg = __raw_readl(DI_STP_REP(di, wave_gen)); + reg &= ~(0xFFFF << (16 * ((wave_gen - 1) & 0x1))); + reg |= repeat_count << (16 * ((wave_gen - 1) & 0x1)); + __raw_writel(reg, DI_STP_REP(di, wave_gen)); +} + +static void ipu_dc_map_config(int map, int byte_num, int offset, int mask) +{ + int ptr = map * 3 + byte_num; + u32 reg; + + reg = __raw_readl(DC_MAP_CONF_VAL(ptr)); + reg &= ~(0xFFFF << (16 * (ptr & 0x1))); + reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); + __raw_writel(reg, DC_MAP_CONF_VAL(ptr)); + + reg = __raw_readl(DC_MAP_CONF_PTR(map)); + reg &= ~(0x1F << ((16 * (map & 0x1)) + (5 * byte_num))); + reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); + __raw_writel(reg, DC_MAP_CONF_PTR(map)); +} + +static void ipu_dc_map_clear(int map) +{ + u32 reg = __raw_readl(DC_MAP_CONF_PTR(map)); + __raw_writel(reg & ~(0xFFFF << (16 * (map & 0x1))), + DC_MAP_CONF_PTR(map)); +} + +static void ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map, + int wave, int glue, int sync) +{ + u32 reg; + int stop = 1; + + reg = sync; + reg |= (glue << 4); + reg |= (++wave << 11); + reg |= (++map << 15); + reg |= (operand << 20) & 0xFFF00000; + __raw_writel(reg, ipu_dc_tmpl_reg + word * 2); + + reg = (operand >> 12); + reg |= opcode << 4; + reg |= (stop << 9); + __raw_writel(reg, ipu_dc_tmpl_reg + word * 2 + 1); +} + +static void ipu_dc_link_event(int chan, int event, int addr, int priority) +{ + u32 reg; + + reg = __raw_readl(DC_RL_CH(chan, event)); + reg &= ~(0xFFFF << (16 * (event & 0x1))); + reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); + __raw_writel(reg, DC_RL_CH(chan, event)); +} + +/* Y = R * 1.200 + G * 2.343 + B * .453 + 0.250; + * U = R * -.672 + G * -1.328 + B * 2.000 + 512.250.; + * V = R * 2.000 + G * -1.672 + B * -.328 + 512.250.; + */ +static const int rgb2ycbcr_coeff[5][3] = { + {0x4D, 0x96, 0x1D}, + {0x3D5, 0x3AB, 0x80}, + {0x80, 0x395, 0x3EB}, + {0x0000, 0x0200, 0x0200}, /* B0, B1, B2 */ + {0x2, 0x2, 0x2}, /* S0, S1, S2 */ +}; + +/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); + * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); + * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); + */ +static const int ycbcr2rgb_coeff[5][3] = { + {0x095, 0x000, 0x0CC}, + {0x095, 0x3CE, 0x398}, + {0x095, 0x0FF, 0x000}, + {0x3E42, 0x010A, 0x3DD6}, /*B0,B1,B2 */ + {0x1, 0x1, 0x1}, /*S0,S1,S2 */ +}; + +#define mask_a(a) ((u32)(a) & 0x3FF) +#define mask_b(b) ((u32)(b) & 0x3FFF) + +/* Pls keep S0, S1 and S2 as 0x2 by using this convertion */ +static int rgb_to_yuv(int n, int red, int green, int blue) +{ + int c; + c = red * rgb2ycbcr_coeff[n][0]; + c += green * rgb2ycbcr_coeff[n][1]; + c += blue * rgb2ycbcr_coeff[n][2]; + c /= 16; + c += rgb2ycbcr_coeff[3][n] * 4; + c += 8; + c /= 16; + if (c < 0) + c = 0; + if (c > 255) + c = 255; + return c; +} + +/* + * Row is for BG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE + * Column is for FG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE + */ +static struct dp_csc_param_t dp_csc_array[CSC_NUM][CSC_NUM] = { + { + {DP_COM_CONF_CSC_DEF_BOTH, &rgb2ycbcr_coeff}, + {0, 0}, + {0, 0}, + {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff}, + {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff} + }, + { + {0, 0}, + {DP_COM_CONF_CSC_DEF_BOTH, &ycbcr2rgb_coeff}, + {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff}, + {0, 0}, + {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff} + }, + { + {0, 0}, + {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, + {0, 0}, + {0, 0}, + {0, 0} + }, + { + {DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} + }, + { + {DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, + {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, + {0, 0}, + {0, 0}, + {0, 0} + } +}; + +static enum csc_type_t fg_csc_type = CSC_NONE, bg_csc_type = CSC_NONE; +static int color_key_4rgb = 1; + +void ipu_dp_csc_setup(int dp, struct dp_csc_param_t dp_csc_param, + unsigned char srm_mode_update) +{ + u32 reg; + const int (*coeff)[5][3]; + + if (dp_csc_param.mode >= 0) { + reg = __raw_readl(DP_COM_CONF()); + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + reg |= dp_csc_param.mode; + __raw_writel(reg, DP_COM_CONF()); + } + + coeff = dp_csc_param.coeff; + + if (coeff) { + __raw_writel(mask_a((*coeff)[0][0]) | + (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0()); + __raw_writel(mask_a((*coeff)[0][2]) | + (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1()); + __raw_writel(mask_a((*coeff)[1][1]) | + (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2()); + __raw_writel(mask_a((*coeff)[2][0]) | + (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3()); + __raw_writel(mask_a((*coeff)[2][2]) | + (mask_b((*coeff)[3][0]) << 16) | + ((*coeff)[4][0] << 30), DP_CSC_0()); + __raw_writel(mask_b((*coeff)[3][1]) | ((*coeff)[4][1] << 14) | + (mask_b((*coeff)[3][2]) << 16) | + ((*coeff)[4][2] << 30), DP_CSC_1()); + } + + if (srm_mode_update) { + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + } +} + +int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt, + uint32_t out_pixel_fmt) +{ + int in_fmt, out_fmt; + int dp; + int partial = 0; + uint32_t reg; + + if (channel == MEM_FG_SYNC) { + dp = DP_SYNC; + partial = 1; + } else if (channel == MEM_BG_SYNC) { + dp = DP_SYNC; + partial = 0; + } else if (channel == MEM_BG_ASYNC0) { + dp = DP_ASYNC0; + partial = 0; + } else { + return -EINVAL; + } + + in_fmt = format_to_colorspace(in_pixel_fmt); + out_fmt = format_to_colorspace(out_pixel_fmt); + + if (partial) { + if (in_fmt == RGB) { + if (out_fmt == RGB) + fg_csc_type = RGB2RGB; + else + fg_csc_type = RGB2YUV; + } else { + if (out_fmt == RGB) + fg_csc_type = YUV2RGB; + else + fg_csc_type = YUV2YUV; + } + } else { + if (in_fmt == RGB) { + if (out_fmt == RGB) + bg_csc_type = RGB2RGB; + else + bg_csc_type = RGB2YUV; + } else { + if (out_fmt == RGB) + bg_csc_type = YUV2RGB; + else + bg_csc_type = YUV2YUV; + } + } + + /* Transform color key from rgb to yuv if CSC is enabled */ + reg = __raw_readl(DP_COM_CONF()); + if (color_key_4rgb && (reg & DP_COM_CONF_GWCKE) && + (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB)))) { + int red, green, blue; + int y, u, v; + uint32_t color_key = __raw_readl(DP_GRAPH_WIND_CTRL()) & + 0xFFFFFFL; + + debug("_ipu_dp_init color key 0x%x need change to yuv fmt!\n", + color_key); + + red = (color_key >> 16) & 0xFF; + green = (color_key >> 8) & 0xFF; + blue = color_key & 0xFF; + + y = rgb_to_yuv(0, red, green, blue); + u = rgb_to_yuv(1, red, green, blue); + v = rgb_to_yuv(2, red, green, blue); + color_key = (y << 16) | (u << 8) | v; + + reg = __raw_readl(DP_GRAPH_WIND_CTRL()) & 0xFF000000L; + __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL()); + color_key_4rgb = 0; + + debug("_ipu_dp_init color key change to yuv fmt 0x%x!\n", + color_key); + } + + ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], 1); + + return 0; +} + +void ipu_dp_uninit(ipu_channel_t channel) +{ + int dp; + int partial = 0; + + if (channel == MEM_FG_SYNC) { + dp = DP_SYNC; + partial = 1; + } else if (channel == MEM_BG_SYNC) { + dp = DP_SYNC; + partial = 0; + } else if (channel == MEM_BG_ASYNC0) { + dp = DP_ASYNC0; + partial = 0; + } else { + return; + } + + if (partial) + fg_csc_type = CSC_NONE; + else + bg_csc_type = CSC_NONE; + + ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], 0); +} + +void ipu_dc_init(int dc_chan, int di, unsigned char interlaced) +{ + u32 reg = 0; + + if ((dc_chan == 1) || (dc_chan == 5)) { + if (interlaced) { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1); + } else { + if (di) { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, + 4, 1); + } else { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, + 7, 1); + } + } + ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0); + + reg = 0x2; + reg |= DC_DISP_ID_SYNC(di) << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; + reg |= di << 2; + if (interlaced) + reg |= DC_WR_CH_CONF_FIELD_MODE; + } else if ((dc_chan == 8) || (dc_chan == 9)) { + /* async channels */ + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0x64, 1); + + reg = 0x3; + reg |= DC_DISP_ID_SERIAL << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; + } + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + + __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan)); + + __raw_writel(0x00000084, DC_GEN); +} + +void ipu_dc_uninit(int dc_chan) +{ + if ((dc_chan == 1) || (dc_chan == 5)) { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0); + } else if ((dc_chan == 8) || (dc_chan == 9)) { + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_1, 0, 0); + } +} + +int ipu_chan_is_interlaced(ipu_channel_t channel) +{ + if (channel == MEM_DC_SYNC) + return !!(__raw_readl(DC_WR_CH_CONF_1) & + DC_WR_CH_CONF_FIELD_MODE); + else if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC)) + return !!(__raw_readl(DC_WR_CH_CONF_5) & + DC_WR_CH_CONF_FIELD_MODE); + return 0; +} + +void ipu_dp_dc_enable(ipu_channel_t channel) +{ + int di; + uint32_t reg; + uint32_t dc_chan; + + if (channel == MEM_FG_SYNC) + dc_chan = 5; + if (channel == MEM_DC_SYNC) + dc_chan = 1; + else if (channel == MEM_BG_SYNC) + dc_chan = 5; + else + return; + + if (channel == MEM_FG_SYNC) { + /* Enable FG channel */ + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg | DP_COM_CONF_FG_EN, DP_COM_CONF()); + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + return; + } + + di = g_dc_di_assignment[dc_chan]; + + /* Make sure other DC sync channel is not assigned same DI */ + reg = __raw_readl(DC_WR_CH_CONF(6 - dc_chan)); + if ((di << 2) == (reg & DC_WR_CH_CONF_PROG_DI_ID)) { + reg &= ~DC_WR_CH_CONF_PROG_DI_ID; + reg |= di ? 0 : DC_WR_CH_CONF_PROG_DI_ID; + __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan)); + } + + reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); + reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET; + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + + clk_enable(g_pixel_clk[di]); +} + +static unsigned char dc_swap; + +void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) +{ + uint32_t reg; + uint32_t csc; + uint32_t dc_chan = 0; + int timeout = 50; + + dc_swap = swap; + + if (channel == MEM_DC_SYNC) { + dc_chan = 1; + } else if (channel == MEM_BG_SYNC) { + dc_chan = 5; + } else if (channel == MEM_FG_SYNC) { + /* Disable FG channel */ + dc_chan = 5; + + reg = __raw_readl(DP_COM_CONF()); + csc = reg & DP_COM_CONF_CSC_DEF_MASK; + if (csc == DP_COM_CONF_CSC_DEF_FG) + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + + reg &= ~DP_COM_CONF_FG_EN; + __raw_writel(reg, DP_COM_CONF()); + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + + timeout = 50; + + /* + * Wait for DC triple buffer to empty, + * this check is useful for tv overlay. + */ + if (g_dc_di_assignment[dc_chan] == 0) + while ((__raw_readl(DC_STAT) & 0x00000002) + != 0x00000002) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + else if (g_dc_di_assignment[dc_chan] == 1) + while ((__raw_readl(DC_STAT) & 0x00000020) + != 0x00000020) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + return; + } else { + return; + } + + if (dc_swap) { + /* Swap DC channel 1 and 5 settings, and disable old dc chan */ + reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); + __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan)); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + reg ^= DC_WR_CH_CONF_PROG_DI_ID; + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + } else { + timeout = 50; + + /* Wait for DC triple buffer to empty */ + if (g_dc_di_assignment[dc_chan] == 0) + while ((__raw_readl(DC_STAT) & 0x00000002) + != 0x00000002) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + else if (g_dc_di_assignment[dc_chan] == 1) + while ((__raw_readl(DC_STAT) & 0x00000020) + != 0x00000020) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + + reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + + reg = __raw_readl(IPU_DISP_GEN); + if (g_dc_di_assignment[dc_chan]) + reg &= ~DI1_COUNTER_RELEASE; + else + reg &= ~DI0_COUNTER_RELEASE; + __raw_writel(reg, IPU_DISP_GEN); + + /* Clock is already off because it must be done quickly, but + we need to fix the ref count */ + clk_disable(g_pixel_clk[g_dc_di_assignment[dc_chan]]); + } +} + +void ipu_init_dc_mappings(void) +{ + /* IPU_PIX_FMT_RGB24 */ + ipu_dc_map_clear(0); + ipu_dc_map_config(0, 0, 7, 0xFF); + ipu_dc_map_config(0, 1, 15, 0xFF); + ipu_dc_map_config(0, 2, 23, 0xFF); + + /* IPU_PIX_FMT_RGB666 */ + ipu_dc_map_clear(1); + ipu_dc_map_config(1, 0, 5, 0xFC); + ipu_dc_map_config(1, 1, 11, 0xFC); + ipu_dc_map_config(1, 2, 17, 0xFC); + + /* IPU_PIX_FMT_YUV444 */ + ipu_dc_map_clear(2); + ipu_dc_map_config(2, 0, 15, 0xFF); + ipu_dc_map_config(2, 1, 23, 0xFF); + ipu_dc_map_config(2, 2, 7, 0xFF); + + /* IPU_PIX_FMT_RGB565 */ + ipu_dc_map_clear(3); + ipu_dc_map_config(3, 0, 4, 0xF8); + ipu_dc_map_config(3, 1, 10, 0xFC); + ipu_dc_map_config(3, 2, 15, 0xF8); + + /* IPU_PIX_FMT_LVDS666 */ + ipu_dc_map_clear(4); + ipu_dc_map_config(4, 0, 5, 0xFC); + ipu_dc_map_config(4, 1, 13, 0xFC); + ipu_dc_map_config(4, 2, 21, 0xFC); +} + +int ipu_pixfmt_to_map(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: + case IPU_PIX_FMT_RGB24: + return 0; + case IPU_PIX_FMT_RGB666: + return 1; + case IPU_PIX_FMT_YUV444: + return 2; + case IPU_PIX_FMT_RGB565: + return 3; + case IPU_PIX_FMT_LVDS666: + return 4; + } + + return -1; +} + +/* + * This function is called to adapt synchronous LCD panel to IPU restriction. + */ +void adapt_panel_to_ipu_restricitions(uint32_t *pixel_clk, + uint16_t width, uint16_t height, + uint16_t h_start_width, + uint16_t h_end_width, + uint16_t v_start_width, + uint16_t *v_end_width) +{ + if (*v_end_width < 2) { + uint16_t total_width = width + h_start_width + h_end_width; + uint16_t total_height_old = height + v_start_width + + (*v_end_width); + uint16_t total_height_new = height + v_start_width + 2; + *v_end_width = 2; + *pixel_clk = (*pixel_clk) * total_width * total_height_new / + (total_width * total_height_old); + printf("WARNING: adapt panel end blank lines\n"); + } +} + +/* + * This function is called to initialize a synchronous LCD panel. + * + * @param disp The DI the panel is attached to. + * + * @param pixel_clk Desired pixel clock frequency in Hz. + * + * @param pixel_fmt Input parameter for pixel format of buffer. + * Pixel format is a FOURCC ASCII code. + * + * @param width The width of panel in pixels. + * + * @param height The height of panel in pixels. + * + * @param hStartWidth The number of pixel clocks between the HSYNC + * signal pulse and the start of valid data. + * + * @param hSyncWidth The width of the HSYNC signal in units of pixel + * clocks. + * + * @param hEndWidth The number of pixel clocks between the end of + * valid data and the HSYNC signal for next line. + * + * @param vStartWidth The number of lines between the VSYNC + * signal pulse and the start of valid data. + * + * @param vSyncWidth The width of the VSYNC signal in units of lines + * + * @param vEndWidth The number of lines between the end of valid + * data and the VSYNC signal for next frame. + * + * @param sig Bitfield of signal polarities for LCD interface. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ + +int32_t ipu_init_sync_panel(int disp, uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint32_t pixel_fmt, + uint16_t h_start_width, uint16_t h_sync_width, + uint16_t h_end_width, uint16_t v_start_width, + uint16_t v_sync_width, uint16_t v_end_width, + uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig) +{ + uint32_t reg; + uint32_t di_gen, vsync_cnt; + uint32_t div, rounded_pixel_clk; + uint32_t h_total, v_total; + int map; + struct clk *di_parent; + + debug("panel size = %d x %d\n", width, height); + + if ((v_sync_width == 0) || (h_sync_width == 0)) + return -EINVAL; + + adapt_panel_to_ipu_restricitions(&pixel_clk, width, height, + h_start_width, h_end_width, + v_start_width, &v_end_width); + h_total = width + h_sync_width + h_start_width + h_end_width; + v_total = height + v_sync_width + v_start_width + v_end_width; + + /* Init clocking */ + debug("pixel clk = %d\n", pixel_clk); + + if (sig.ext_clk) { + if (!(g_di1_tvout && (disp == 1))) { /*not round div for tvout*/ + /* + * Set the PLL to be an even multiple + * of the pixel clock. + */ + if ((clk_get_usecount(g_pixel_clk[0]) == 0) && + (clk_get_usecount(g_pixel_clk[1]) == 0)) { + di_parent = clk_get_parent(g_di_clk[disp]); + rounded_pixel_clk = + clk_round_rate(g_pixel_clk[disp], + pixel_clk); + div = clk_get_rate(di_parent) / + rounded_pixel_clk; + if (div % 2) + div++; + if (clk_get_rate(di_parent) != div * + rounded_pixel_clk) + clk_set_rate(di_parent, + div * rounded_pixel_clk); + udelay(10000); + clk_set_rate(g_di_clk[disp], + 2 * rounded_pixel_clk); + udelay(10000); + } + } + clk_set_parent(g_pixel_clk[disp], g_ldb_clk); + } else { + if (clk_get_usecount(g_pixel_clk[disp]) != 0) + clk_set_parent(g_pixel_clk[disp], g_ipu_clk); + } + rounded_pixel_clk = clk_round_rate(g_pixel_clk[disp], pixel_clk); + clk_set_rate(g_pixel_clk[disp], rounded_pixel_clk); + udelay(5000); + /* Get integer portion of divider */ + div = clk_get_rate(clk_get_parent(g_pixel_clk[disp])) / + rounded_pixel_clk; + + ipu_di_data_wave_config(disp, SYNC_WAVE, div - 1, div - 1); + ipu_di_data_pin_config(disp, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); + + map = ipu_pixfmt_to_map(pixel_fmt); + if (map < 0) { + debug("IPU_DISP: No MAP\n"); + return -EINVAL; + } + + di_gen = __raw_readl(DI_GENERAL(disp)); + + if (sig.interlaced) { + /* Setup internal HSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 1, /* counter */ + h_total / 2 - 1,/* run count */ + DI_SYNC_CLK, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Field 1 VSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 2, /* counter */ + h_total - 1, /* run count */ + DI_SYNC_CLK, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 4 /* COUNT DOWN */ + ); + + /* Setup internal HSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 3, /* counter */ + v_total * 2 - 1,/* run count */ + DI_SYNC_INT_HSYNC, /* run_resolution */ + 1, /* offset */ + DI_SYNC_INT_HSYNC, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 4 /* COUNT DOWN */ + ); + + /* Active Field ? */ + ipu_di_sync_config( + disp, /* display */ + 4, /* counter */ + v_total / 2 - 1,/* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + v_start_width, /* offset */ + DI_SYNC_HSYNC, /* offset resolution */ + 2, /* repeat count */ + DI_SYNC_VSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Active Line */ + ipu_di_sync_config( + disp, /* display */ + 5, /* counter */ + 0, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + height / 2, /* repeat count */ + 4, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Field 0 VSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 6, /* counter */ + v_total - 1, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* DC VSYNC waveform */ + vsync_cnt = 7; + ipu_di_sync_config( + disp, /* display */ + 7, /* counter */ + v_total / 2 - 1,/* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 9, /* offset */ + DI_SYNC_HSYNC, /* offset resolution */ + 2, /* repeat count */ + DI_SYNC_VSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* active pixel waveform */ + ipu_di_sync_config( + disp, /* display */ + 8, /* counter */ + 0, /* run count */ + DI_SYNC_CLK, /* run_resolution */ + h_start_width, /* offset */ + DI_SYNC_CLK, /* offset resolution */ + width, /* repeat count */ + 5, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + ipu_di_sync_config( + disp, /* display */ + 9, /* counter */ + v_total - 1, /* run count */ + DI_SYNC_INT_HSYNC,/* run_resolution */ + v_total / 2, /* offset */ + DI_SYNC_INT_HSYNC,/* offset resolution */ + 0, /* repeat count */ + DI_SYNC_HSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 4 /* COUNT DOWN */ + ); + + /* set gentime select and tag sel */ + reg = __raw_readl(DI_SW_GEN1(disp, 9)); + reg &= 0x1FFFFFFF; + reg |= (3 - 1)<<29 | 0x00008000; + __raw_writel(reg, DI_SW_GEN1(disp, 9)); + + __raw_writel(v_total / 2 - 1, DI_SCR_CONF(disp)); + + /* set y_sel = 1 */ + di_gen |= 0x10000000; + di_gen |= DI_GEN_POLARITY_5; + di_gen |= DI_GEN_POLARITY_8; + } else { + /* Setup internal HSYNC waveform */ + ipu_di_sync_config(disp, 1, h_total - 1, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, + 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + + /* Setup external (delayed) HSYNC waveform */ + ipu_di_sync_config(disp, DI_SYNC_HSYNC, h_total - 1, + DI_SYNC_CLK, div * v_to_h_sync, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 1, DI_SYNC_NONE, + DI_SYNC_CLK, 0, h_sync_width * 2); + /* Setup VSYNC waveform */ + vsync_cnt = DI_SYNC_VSYNC; + ipu_di_sync_config(disp, DI_SYNC_VSYNC, v_total - 1, + DI_SYNC_INT_HSYNC, 0, DI_SYNC_NONE, 0, + DI_SYNC_NONE, 1, DI_SYNC_NONE, + DI_SYNC_INT_HSYNC, 0, v_sync_width * 2); + __raw_writel(v_total - 1, DI_SCR_CONF(disp)); + + /* Setup active data waveform to sync with DC */ + ipu_di_sync_config(disp, 4, 0, DI_SYNC_HSYNC, + v_sync_width + v_start_width, DI_SYNC_HSYNC, + height, + DI_SYNC_VSYNC, 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + ipu_di_sync_config(disp, 5, 0, DI_SYNC_CLK, + h_sync_width + h_start_width, DI_SYNC_CLK, + width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, + 0); + + /* reset all unused counters */ + __raw_writel(0, DI_SW_GEN0(disp, 6)); + __raw_writel(0, DI_SW_GEN1(disp, 6)); + __raw_writel(0, DI_SW_GEN0(disp, 7)); + __raw_writel(0, DI_SW_GEN1(disp, 7)); + __raw_writel(0, DI_SW_GEN0(disp, 8)); + __raw_writel(0, DI_SW_GEN1(disp, 8)); + __raw_writel(0, DI_SW_GEN0(disp, 9)); + __raw_writel(0, DI_SW_GEN1(disp, 9)); + + reg = __raw_readl(DI_STP_REP(disp, 6)); + reg &= 0x0000FFFF; + __raw_writel(reg, DI_STP_REP(disp, 6)); + __raw_writel(0, DI_STP_REP(disp, 7)); + __raw_writel(0, DI_STP_REP(disp, 9)); + + /* Init template microcode */ + if (disp) { + ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5); + ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5); + ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5); + } else { + ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5); + ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5); + ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5); + } + + if (sig.Hsync_pol) + di_gen |= DI_GEN_POLARITY_2; + if (sig.Vsync_pol) + di_gen |= DI_GEN_POLARITY_3; + + if (!sig.clk_pol) + di_gen |= DI_GEN_POL_CLK; + + } + + __raw_writel(di_gen, DI_GENERAL(disp)); + + __raw_writel((--vsync_cnt << DI_VSYNC_SEL_OFFSET) | + 0x00000002, DI_SYNC_AS_GEN(disp)); + + reg = __raw_readl(DI_POL(disp)); + reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); + if (sig.enable_pol) + reg |= DI_POL_DRDY_POLARITY_15; + if (sig.data_pol) + reg |= DI_POL_DRDY_DATA_POLARITY; + __raw_writel(reg, DI_POL(disp)); + + __raw_writel(width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp))); + + return 0; +} + +/* + * This function sets the foreground and background plane global alpha blending + * modes. This function also sets the DP graphic plane according to the + * parameter of IPUv3 DP channel. + * + * @param channel IPUv3 DP channel + * + * @param enable Boolean to enable or disable global alpha + * blending. If disabled, local blending is used. + * + * @param alpha Global alpha value. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable, + uint8_t alpha) +{ + uint32_t reg; + + unsigned char bg_chan; + + if (!((channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) || + (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) || + (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1))) + return -EINVAL; + + if (channel == MEM_BG_SYNC || channel == MEM_BG_ASYNC0 || + channel == MEM_BG_ASYNC1) + bg_chan = 1; + else + bg_chan = 0; + + if (!g_ipu_clk_enabled) + clk_enable(g_ipu_clk); + + if (bg_chan) { + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg & ~DP_COM_CONF_GWSEL, DP_COM_CONF()); + } else { + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg | DP_COM_CONF_GWSEL, DP_COM_CONF()); + } + + if (enable) { + reg = __raw_readl(DP_GRAPH_WIND_CTRL()) & 0x00FFFFFFL; + __raw_writel(reg | ((uint32_t) alpha << 24), + DP_GRAPH_WIND_CTRL()); + + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg | DP_COM_CONF_GWAM, DP_COM_CONF()); + } else { + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg & ~DP_COM_CONF_GWAM, DP_COM_CONF()); + } + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + + if (!g_ipu_clk_enabled) + clk_disable(g_ipu_clk); + + return 0; +} + +/* + * This function sets the transparent color key for SDC graphic plane. + * + * @param channel Input parameter for the logical channel ID. + * + * @param enable Boolean to enable or disable color key + * + * @param colorKey 24-bit RGB color for transparent color key. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable, + uint32_t color_key) +{ + uint32_t reg; + int y, u, v; + int red, green, blue; + + if (!((channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) || + (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) || + (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1))) + return -EINVAL; + + if (!g_ipu_clk_enabled) + clk_enable(g_ipu_clk); + + color_key_4rgb = 1; + /* Transform color key from rgb to yuv if CSC is enabled */ + if (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB))) { + + debug("color key 0x%x need change to yuv fmt\n", color_key); + + red = (color_key >> 16) & 0xFF; + green = (color_key >> 8) & 0xFF; + blue = color_key & 0xFF; + + y = rgb_to_yuv(0, red, green, blue); + u = rgb_to_yuv(1, red, green, blue); + v = rgb_to_yuv(2, red, green, blue); + color_key = (y << 16) | (u << 8) | v; + + color_key_4rgb = 0; + + debug("color key change to yuv fmt 0x%x\n", color_key); + } + + if (enable) { + reg = __raw_readl(DP_GRAPH_WIND_CTRL()) & 0xFF000000L; + __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL()); + + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg | DP_COM_CONF_GWCKE, DP_COM_CONF()); + } else { + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF()); + } + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + + if (!g_ipu_clk_enabled) + clk_disable(g_ipu_clk); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/video/ipu_regs.h b/qemu/roms/u-boot/drivers/video/ipu_regs.h new file mode 100644 index 000000000..21e9c99e0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/ipu_regs.h @@ -0,0 +1,412 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __IPU_REGS_INCLUDED__ +#define __IPU_REGS_INCLUDED__ + +#define IPU_DISP0_BASE 0x00000000 +#define IPU_MCU_T_DEFAULT 8 +#define IPU_DISP1_BASE (IPU_MCU_T_DEFAULT << 25) +#define IPU_CM_REG_BASE 0x00000000 +#define IPU_STAT_REG_BASE 0x00000200 +#define IPU_IDMAC_REG_BASE 0x00008000 +#define IPU_ISP_REG_BASE 0x00010000 +#define IPU_DP_REG_BASE 0x00018000 +#define IPU_IC_REG_BASE 0x00020000 +#define IPU_IRT_REG_BASE 0x00028000 +#define IPU_CSI0_REG_BASE 0x00030000 +#define IPU_CSI1_REG_BASE 0x00038000 +#define IPU_DI0_REG_BASE 0x00040000 +#define IPU_DI1_REG_BASE 0x00048000 +#define IPU_SMFC_REG_BASE 0x00050000 +#define IPU_DC_REG_BASE 0x00058000 +#define IPU_DMFC_REG_BASE 0x00060000 +#define IPU_VDI_REG_BASE 0x00680000 +#if defined(CONFIG_MX51) || defined(CONFIG_MX53) +#define IPU_CPMEM_REG_BASE 0x01000000 +#define IPU_LUT_REG_BASE 0x01020000 +#define IPU_SRM_REG_BASE 0x01040000 +#define IPU_TPM_REG_BASE 0x01060000 +#define IPU_DC_TMPL_REG_BASE 0x01080000 +#define IPU_ISP_TBPR_REG_BASE 0x010C0000 +#elif defined(CONFIG_MX6) +#define IPU_CPMEM_REG_BASE 0x00100000 +#define IPU_LUT_REG_BASE 0x00120000 +#define IPU_SRM_REG_BASE 0x00140000 +#define IPU_TPM_REG_BASE 0x00160000 +#define IPU_DC_TMPL_REG_BASE 0x00180000 +#define IPU_ISP_TBPR_REG_BASE 0x001C0000 +#endif + +#define IPU_CTRL_BASE_ADDR (IPU_SOC_BASE_ADDR + IPU_SOC_OFFSET) + +extern u32 *ipu_dc_tmpl_reg; + +#define DC_EVT_NF 0 +#define DC_EVT_NL 1 +#define DC_EVT_EOF 2 +#define DC_EVT_NFIELD 3 +#define DC_EVT_EOL 4 +#define DC_EVT_EOFIELD 5 +#define DC_EVT_NEW_ADDR 6 +#define DC_EVT_NEW_CHAN 7 +#define DC_EVT_NEW_DATA 8 + +#define DC_EVT_NEW_ADDR_W_0 0 +#define DC_EVT_NEW_ADDR_W_1 1 +#define DC_EVT_NEW_CHAN_W_0 2 +#define DC_EVT_NEW_CHAN_W_1 3 +#define DC_EVT_NEW_DATA_W_0 4 +#define DC_EVT_NEW_DATA_W_1 5 +#define DC_EVT_NEW_ADDR_R_0 6 +#define DC_EVT_NEW_ADDR_R_1 7 +#define DC_EVT_NEW_CHAN_R_0 8 +#define DC_EVT_NEW_CHAN_R_1 9 +#define DC_EVT_NEW_DATA_R_0 10 +#define DC_EVT_NEW_DATA_R_1 11 + +/* Software reset for ipu */ +#define SW_IPU_RST 8 + +enum { + IPU_CONF_DP_EN = 0x00000020, + IPU_CONF_DI0_EN = 0x00000040, + IPU_CONF_DI1_EN = 0x00000080, + IPU_CONF_DMFC_EN = 0x00000400, + IPU_CONF_DC_EN = 0x00000200, + + DI0_COUNTER_RELEASE = 0x01000000, + DI1_COUNTER_RELEASE = 0x02000000, + + DI_DW_GEN_ACCESS_SIZE_OFFSET = 24, + DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16, + + DI_GEN_DI_CLK_EXT = 0x100000, + DI_GEN_POLARITY_1 = 0x00000001, + DI_GEN_POLARITY_2 = 0x00000002, + DI_GEN_POLARITY_3 = 0x00000004, + DI_GEN_POLARITY_4 = 0x00000008, + DI_GEN_POLARITY_5 = 0x00000010, + DI_GEN_POLARITY_6 = 0x00000020, + DI_GEN_POLARITY_7 = 0x00000040, + DI_GEN_POLARITY_8 = 0x00000080, + DI_GEN_POL_CLK = 0x20000, + + DI_POL_DRDY_DATA_POLARITY = 0x00000080, + DI_POL_DRDY_POLARITY_15 = 0x00000010, + DI_VSYNC_SEL_OFFSET = 13, + + DC_WR_CH_CONF_FIELD_MODE = 0x00000200, + DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5, + DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0, + DC_WR_CH_CONF_PROG_DI_ID = 0x00000004, + DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3, + DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018, + + DP_COM_CONF_FG_EN = 0x00000001, + DP_COM_CONF_GWSEL = 0x00000002, + DP_COM_CONF_GWAM = 0x00000004, + DP_COM_CONF_GWCKE = 0x00000008, + DP_COM_CONF_CSC_DEF_MASK = 0x00000300, + DP_COM_CONF_CSC_DEF_OFFSET = 8, + DP_COM_CONF_CSC_DEF_FG = 0x00000300, + DP_COM_CONF_CSC_DEF_BG = 0x00000200, + DP_COM_CONF_CSC_DEF_BOTH = 0x00000100, + DP_COM_CONF_GAMMA_EN = 0x00001000, + DP_COM_CONF_GAMMA_YUV_EN = 0x00002000, +}; + +enum di_pins { + DI_PIN11 = 0, + DI_PIN12 = 1, + DI_PIN13 = 2, + DI_PIN14 = 3, + DI_PIN15 = 4, + DI_PIN16 = 5, + DI_PIN17 = 6, + DI_PIN_CS = 7, + + DI_PIN_SER_CLK = 0, + DI_PIN_SER_RS = 1, +}; + +enum di_sync_wave { + DI_SYNC_NONE = -1, + DI_SYNC_CLK = 0, + DI_SYNC_INT_HSYNC = 1, + DI_SYNC_HSYNC = 2, + DI_SYNC_VSYNC = 3, + DI_SYNC_DE = 5, +}; + +struct ipu_cm { + u32 conf; + u32 sisg_ctrl0; + u32 sisg_ctrl1; + u32 sisg_set[6]; + u32 sisg_clear[6]; + u32 int_ctrl[15]; + u32 sdma_event[10]; + u32 srm_pri1; + u32 srm_pri2; + u32 fs_proc_flow[3]; + u32 fs_disp_flow[2]; + u32 skip; + u32 disp_alt_conf; + u32 disp_gen; + u32 disp_alt[4]; + u32 snoop; + u32 mem_rst; + u32 pm; + u32 gpr; + u32 reserved0[26]; + u32 ch_db_mode_sel[2]; + u32 reserved1[4]; + u32 alt_ch_db_mode_sel[2]; + u32 reserved2[2]; + u32 ch_trb_mode_sel[2]; +}; + +struct ipu_idmac { + u32 conf; + u32 ch_en[2]; + u32 sep_alpha; + u32 alt_sep_alpha; + u32 ch_pri[2]; + u32 wm_en[2]; + u32 lock_en[2]; + u32 sub_addr[5]; + u32 bndm_en[2]; + u32 sc_cord[2]; + u32 reserved[44]; + u32 ch_busy[2]; +}; + +struct ipu_com_async { + u32 com_conf_async; + u32 graph_wind_ctrl_async; + u32 fg_pos_async; + u32 cur_pos_async; + u32 cur_map_async; + u32 gamma_c_async[8]; + u32 gamma_s_async[4]; + u32 dp_csca_async[4]; + u32 dp_csc_async[2]; +}; + +struct ipu_dp { + u32 com_conf_sync; + u32 graph_wind_ctrl_sync; + u32 fg_pos_sync; + u32 cur_pos_sync; + u32 cur_map_sync; + u32 gamma_c_sync[8]; + u32 gamma_s_sync[4]; + u32 csca_sync[4]; + u32 csc_sync[2]; + u32 cur_pos_alt; + struct ipu_com_async async[2]; +}; + +struct ipu_di { + u32 general; + u32 bs_clkgen0; + u32 bs_clkgen1; + u32 sw_gen0[9]; + u32 sw_gen1[9]; + u32 sync_as; + u32 dw_gen[12]; + u32 dw_set[48]; + u32 stp_rep[4]; + u32 stp_rep9; + u32 ser_conf; + u32 ssc; + u32 pol; + u32 aw0; + u32 aw1; + u32 scr_conf; + u32 stat; +}; + +struct ipu_stat { + u32 int_stat[15]; + u32 cur_buf[2]; + u32 alt_cur_buf_0; + u32 alt_cur_buf_1; + u32 srm_stat; + u32 proc_task_stat; + u32 disp_task_stat; + u32 triple_cur_buf[4]; + u32 ch_buf0_rdy[2]; + u32 ch_buf1_rdy[2]; + u32 alt_ch_buf0_rdy[2]; + u32 alt_ch_buf1_rdy[2]; + u32 ch_buf2_rdy[2]; +}; + +struct ipu_dc_ch { + u32 wr_ch_conf; + u32 wr_ch_addr; + u32 rl[5]; +}; + +struct ipu_dc { + struct ipu_dc_ch dc_ch0_1_2[3]; + u32 cmd_ch_conf_3; + u32 cmd_ch_conf_4; + struct ipu_dc_ch dc_ch5_6[2]; + struct ipu_dc_ch dc_ch8; + u32 rl6_ch_8; + struct ipu_dc_ch dc_ch9; + u32 rl6_ch_9; + u32 gen; + u32 disp_conf1[4]; + u32 disp_conf2[4]; + u32 di0_conf[2]; + u32 di1_conf[2]; + u32 dc_map_ptr[15]; + u32 dc_map_val[12]; + u32 udge[16]; + u32 lla[2]; + u32 r_lla[2]; + u32 wr_ch_addr_5_alt; + u32 stat; +}; + +struct ipu_dmfc { + u32 rd_chan; + u32 wr_chan; + u32 wr_chan_def; + u32 dp_chan; + u32 dp_chan_def; + u32 general[2]; + u32 ic_ctrl; + u32 wr_chan_alt; + u32 wr_chan_def_alt; + u32 general1_alt; + u32 stat; +}; + +#define IPU_CM_REG ((struct ipu_cm *)(IPU_CTRL_BASE_ADDR + \ + IPU_CM_REG_BASE)) +#define IPU_CONF (&IPU_CM_REG->conf) +#define IPU_SRM_PRI1 (&IPU_CM_REG->srm_pri1) +#define IPU_SRM_PRI2 (&IPU_CM_REG->srm_pri2) +#define IPU_FS_PROC_FLOW1 (&IPU_CM_REG->fs_proc_flow[0]) +#define IPU_FS_PROC_FLOW2 (&IPU_CM_REG->fs_proc_flow[1]) +#define IPU_FS_PROC_FLOW3 (&IPU_CM_REG->fs_proc_flow[2]) +#define IPU_FS_DISP_FLOW1 (&IPU_CM_REG->fs_disp_flow[0]) +#define IPU_DISP_GEN (&IPU_CM_REG->disp_gen) +#define IPU_MEM_RST (&IPU_CM_REG->mem_rst) +#define IPU_GPR (&IPU_CM_REG->gpr) +#define IPU_CHA_DB_MODE_SEL(ch) (&IPU_CM_REG->ch_db_mode_sel[ch / 32]) + +#define IPU_STAT ((struct ipu_stat *)(IPU_CTRL_BASE_ADDR + \ + IPU_STAT_REG_BASE)) +#define IPU_CHA_CUR_BUF(ch) (&IPU_STAT->cur_buf[ch / 32]) +#define IPU_CHA_BUF0_RDY(ch) (&IPU_STAT->ch_buf0_rdy[ch / 32]) +#define IPU_CHA_BUF1_RDY(ch) (&IPU_STAT->ch_buf1_rdy[ch / 32]) + +#define IPU_INT_CTRL(n) (&IPU_CM_REG->int_ctrl[(n) - 1]) + +#define IDMAC_REG ((struct ipu_idmac *)(IPU_CTRL_BASE_ADDR + \ + IPU_IDMAC_REG_BASE)) +#define IDMAC_CONF (&IDMAC_REG->conf) +#define IDMAC_CHA_EN(ch) (&IDMAC_REG->ch_en[ch / 32]) +#define IDMAC_CHA_PRI(ch) (&IDMAC_REG->ch_pri[ch / 32]) + +#define DI_REG(di) ((struct ipu_di *)(IPU_CTRL_BASE_ADDR + \ + ((di == 1) ? IPU_DI1_REG_BASE : \ + IPU_DI0_REG_BASE))) +#define DI_GENERAL(di) (&DI_REG(di)->general) +#define DI_BS_CLKGEN0(di) (&DI_REG(di)->bs_clkgen0) +#define DI_BS_CLKGEN1(di) (&DI_REG(di)->bs_clkgen1) + +#define DI_SW_GEN0(di, gen) (&DI_REG(di)->sw_gen0[gen - 1]) +#define DI_SW_GEN1(di, gen) (&DI_REG(di)->sw_gen1[gen - 1]) +#define DI_STP_REP(di, gen) (&DI_REG(di)->stp_rep[(gen - 1) / 2]) +#define DI_SYNC_AS_GEN(di) (&DI_REG(di)->sync_as) +#define DI_DW_GEN(di, gen) (&DI_REG(di)->dw_gen[gen]) +#define DI_DW_SET(di, gen, set) (&DI_REG(di)->dw_set[gen + 12 * set]) +#define DI_POL(di) (&DI_REG(di)->pol) +#define DI_SCR_CONF(di) (&DI_REG(di)->scr_conf) + +#define DMFC_REG ((struct ipu_dmfc *)(IPU_CTRL_BASE_ADDR + \ + IPU_DMFC_REG_BASE)) +#define DMFC_WR_CHAN (&DMFC_REG->wr_chan) +#define DMFC_WR_CHAN_DEF (&DMFC_REG->wr_chan_def) +#define DMFC_DP_CHAN (&DMFC_REG->dp_chan) +#define DMFC_DP_CHAN_DEF (&DMFC_REG->dp_chan_def) +#define DMFC_GENERAL1 (&DMFC_REG->general[0]) +#define DMFC_IC_CTRL (&DMFC_REG->ic_ctrl) + + +#define DC_REG ((struct ipu_dc *)(IPU_CTRL_BASE_ADDR + \ + IPU_DC_REG_BASE)) +#define DC_MAP_CONF_PTR(n) (&DC_REG->dc_map_ptr[n / 2]) +#define DC_MAP_CONF_VAL(n) (&DC_REG->dc_map_val[n / 2]) + + +static inline struct ipu_dc_ch *dc_ch_offset(int ch) +{ + switch (ch) { + case 0: + case 1: + case 2: + return &DC_REG->dc_ch0_1_2[ch]; + case 5: + case 6: + return &DC_REG->dc_ch5_6[ch - 5]; + case 8: + return &DC_REG->dc_ch8; + case 9: + return &DC_REG->dc_ch9; + default: + printf("%s: invalid channel %d\n", __func__, ch); + return NULL; + } + +} + +#define DC_RL_CH(ch, evt) (&dc_ch_offset(ch)->rl[evt / 2]) + +#define DC_WR_CH_CONF(ch) (&dc_ch_offset(ch)->wr_ch_conf) +#define DC_WR_CH_ADDR(ch) (&dc_ch_offset(ch)->wr_ch_addr) + +#define DC_WR_CH_CONF_1 DC_WR_CH_CONF(1) +#define DC_WR_CH_CONF_5 DC_WR_CH_CONF(5) + +#define DC_GEN (&DC_REG->gen) +#define DC_DISP_CONF2(disp) (&DC_REG->disp_conf2[disp]) +#define DC_STAT (&DC_REG->stat) + +#define DP_SYNC 0 +#define DP_ASYNC0 0x60 +#define DP_ASYNC1 0xBC + +#define DP_REG ((struct ipu_dp *)(IPU_CTRL_BASE_ADDR + \ + IPU_DP_REG_BASE)) +#define DP_COM_CONF() (&DP_REG->com_conf_sync) +#define DP_GRAPH_WIND_CTRL() (&DP_REG->graph_wind_ctrl_sync) +#define DP_CSC_A_0() (&DP_REG->csca_sync[0]) +#define DP_CSC_A_1() (&DP_REG->csca_sync[1]) +#define DP_CSC_A_2() (&DP_REG->csca_sync[2]) +#define DP_CSC_A_3() (&DP_REG->csca_sync[3]) + +#define DP_CSC_0() (&DP_REG->csc_sync[0]) +#define DP_CSC_1() (&DP_REG->csc_sync[1]) + +/* DC template opcodes */ +#define WROD(lf) (0x18 | (lf << 1)) + +#endif diff --git a/qemu/roms/u-boot/drivers/video/l5f31188.c b/qemu/roms/u-boot/drivers/video/l5f31188.c new file mode 100644 index 000000000..3312dcfb3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/l5f31188.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. + * Hyungwon Hwang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#define SCAN_FROM_LEFT_TO_RIGHT 0 +#define SCAN_FROM_RIGHT_TO_LEFT 1 +#define SCAN_FROM_TOP_TO_BOTTOM 0 +#define SCAN_FROM_BOTTOM_TO_TOP 1 + +static void l5f31188_sleep_in(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE, 0x10, 0x00); +} + +static void l5f31188_sleep_out(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE, 0x11, 0x00); +} + +static void l5f31188_set_gamma(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE, 0x26, 0x00); +} + +static void l5f31188_display_off(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE, 0x28, 0x00); +} + +static void l5f31188_display_on(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE, 0x29, 0x00); +} + +static void l5f31188_ctl_memory_access(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops, + int h_direction, int v_direction) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x36, + (((h_direction & 0x1) << 1) | (v_direction & 0x1))); +} + +static void l5f31188_set_pixel_format(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x3A, 0x70); +} + +static void l5f31188_write_disbv(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops, unsigned int brightness) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x51, brightness); +} + +static void l5f31188_write_ctrld(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x53, 0x2C); +} + +static void l5f31188_write_cabc(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops, + unsigned int wm_mode) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x55, wm_mode); +} + +static void l5f31188_write_cabcmb(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops, unsigned int min_brightness) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0x5E, + min_brightness); +} + +static void l5f31188_set_extension(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + const unsigned char data_to_send[] = { + 0xB9, 0xFF, 0x83, 0x94 + }; + + ops->cmd_write(dev, MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void l5f31188_set_dgc_lut(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + const unsigned char data_to_send[] = { + 0xC1, 0x01, 0x00, 0x04, 0x0E, 0x18, 0x1E, 0x26, + 0x2F, 0x36, 0x3E, 0x47, 0x4E, 0x56, 0x5D, 0x65, + 0x6D, 0x75, 0x7D, 0x84, 0x8C, 0x94, 0x9C, 0xA4, + 0xAD, 0xB5, 0xBD, 0xC5, 0xCC, 0xD4, 0xDE, 0xE5, + 0xEE, 0xF7, 0xFF, 0x3F, 0x9A, 0xCE, 0xD4, 0x21, + 0xA1, 0x26, 0x54, 0x00, 0x00, 0x04, 0x0E, 0x19, + 0x1F, 0x27, 0x30, 0x37, 0x40, 0x48, 0x50, 0x58, + 0x60, 0x67, 0x6F, 0x77, 0x7F, 0x87, 0x8F, 0x97, + 0x9F, 0xA7, 0xB0, 0xB8, 0xC0, 0xC8, 0xCE, 0xD8, + 0xE0, 0xE7, 0xF0, 0xF7, 0xFF, 0x3C, 0xEB, 0xFD, + 0x2F, 0x66, 0xA8, 0x2C, 0x46, 0x00, 0x00, 0x04, + 0x0E, 0x18, 0x1E, 0x26, 0x30, 0x38, 0x41, 0x4A, + 0x52, 0x5A, 0x62, 0x6B, 0x73, 0x7B, 0x83, 0x8C, + 0x94, 0x9C, 0xA5, 0xAD, 0xB6, 0xBD, 0xC5, 0xCC, + 0xD4, 0xDD, 0xE3, 0xEB, 0xF2, 0xF9, 0xFF, 0x3F, + 0xA4, 0x8A, 0x8F, 0xC7, 0x33, 0xF5, 0xE9, 0x00 + }; + ops->cmd_write(dev, MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void l5f31188_set_tcon(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + const unsigned char data_to_send[] = { + 0xC7, 0x00, 0x20 + }; + ops->cmd_write(dev, MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void l5f31188_set_ptba(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + const unsigned char data_to_send[] = { + 0xBF, 0x06, 0x10 + }; + ops->cmd_write(dev, MIPI_DSI_DCS_LONG_WRITE, + (unsigned int)data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void l5f31188_set_eco(struct mipi_dsim_device *dev, + struct mipi_dsim_master_ops *ops) +{ + ops->cmd_write(dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xC6, 0x0C); +} + +static int l5f31188_panel_init(struct mipi_dsim_device *dev) +{ + struct mipi_dsim_master_ops *ops = dev->master_ops; + + l5f31188_set_extension(dev, ops); + l5f31188_set_dgc_lut(dev, ops); + + l5f31188_set_eco(dev, ops); + l5f31188_set_tcon(dev, ops); + l5f31188_set_ptba(dev, ops); + l5f31188_set_gamma(dev, ops); + l5f31188_ctl_memory_access(dev, ops, + SCAN_FROM_LEFT_TO_RIGHT, SCAN_FROM_TOP_TO_BOTTOM); + l5f31188_set_pixel_format(dev, ops); + l5f31188_write_disbv(dev, ops, 0xFF); + l5f31188_write_ctrld(dev, ops); + l5f31188_write_cabc(dev, ops, 0x0); + l5f31188_write_cabcmb(dev, ops, 0x0); + + l5f31188_sleep_out(dev, ops); + + /* 120 msec */ + udelay(120 * 1000); + + return 0; +} + +static void l5f31188_display_enable(struct mipi_dsim_device *dev) +{ + struct mipi_dsim_master_ops *ops = dev->master_ops; + l5f31188_display_on(dev, ops); +} + +static struct mipi_dsim_lcd_driver l5f31188_dsim_ddi_driver = { + .name = "l5f31188", + .id = -1, + + .mipi_panel_init = l5f31188_panel_init, + .mipi_display_on = l5f31188_display_enable, +}; + +void l5f31188_init(void) +{ + exynos_mipi_dsi_register_lcd_driver(&l5f31188_dsim_ddi_driver); +} diff --git a/qemu/roms/u-boot/drivers/video/ld9040.c b/qemu/roms/u-boot/drivers/video/ld9040.c new file mode 100644 index 000000000..23fe783c8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/ld9040.c @@ -0,0 +1,128 @@ +/* + * ld9040 AMOLED LCD panel driver. + * + * Copyright (C) 2012 Samsung Electronics + * Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +static const unsigned char SEQ_SWRESET[] = { + 0x01, +}; + +static const unsigned char SEQ_USER_SETTING[] = { + 0xF0, 0x5A, 0x5A +}; + +static const unsigned char SEQ_ELVSS_ON[] = { + 0xB1, 0x0D, 0x00, 0x16, +}; + +static const unsigned char SEQ_TEMP_SWIRE[] = { + 0xB2, 0x06, 0x06, 0x06, 0x06, +}; + +static const unsigned char SEQ_GTCON[] = { + 0xF7, 0x09, 0x00, 0x00, +}; + +static const unsigned char SEQ_PANEL_CONDITION[] = { + 0xF8, 0x05, 0x65, 0x96, 0x71, 0x7D, 0x19, 0x3B, + 0x0D, 0x19, 0x7E, 0x0D, 0xE2, 0x00, 0x00, 0x7E, + 0x7D, 0x07, 0x07, 0x20, 0x20, 0x20, 0x02, 0x02, +}; + +static const unsigned char SEQ_GAMMA_SET1[] = { + 0xF9, 0x00, 0xA7, 0xB4, 0xAE, 0xBF, 0x00, 0x91, + 0x00, 0xB2, 0xB4, 0xAA, 0xBB, 0x00, 0xAC, 0x00, + 0xB3, 0xB1, 0xAA, 0xBC, 0x00, 0xB3, +}; + +static const unsigned char SEQ_GAMMA_CTRL[] = { + 0xFB, 0x02, 0x5A, +}; + +static const unsigned char SEQ_APON[] = { + 0xF3, 0x00, 0x00, 0x00, 0x0A, 0x02, +}; + +static const unsigned char SEQ_DISPCTL[] = { + 0xF2, 0x02, 0x08, 0x08, 0x10, 0x10, +}; + +static const unsigned char SEQ_MANPWR[] = { + 0xB0, 0x04, +}; + +static const unsigned char SEQ_PWR_CTRL[] = { + 0xF4, 0x0A, 0x87, 0x25, 0x6A, 0x44, 0x02, 0x88, +}; + +static const unsigned char SEQ_SLPOUT[] = { + 0x11, +}; + +static const unsigned char SEQ_SLPIN[] = { + 0x10, +}; + +static const unsigned char SEQ_DISPON[] = { + 0x29, +}; + +static const unsigned char SEQ_DISPOFF[] = { + 0x28, +}; + +static void ld9040_spi_write(const unsigned char *wbuf, unsigned int size_cmd) +{ + int i = 0; + + /* + * Data are transmitted in 9-bit words: + * the first bit is command/parameter, the other are the value. + * The value's LSB is shifted to MSB position, to be sent as 9th bit + */ + + unsigned int data_out = 0, data_in = 0; + for (i = 0; i < size_cmd; i++) { + data_out = wbuf[i] >> 1; + if (i != 0) + data_out += 0x0080; + if (wbuf[i] & 0x01) + data_out += 0x8000; + spi_xfer(NULL, 9, &data_out, &data_in, SPI_XFER_BEGIN); + } +} + +void ld9040_cfg_ldo(void) +{ + udelay(10); + + ld9040_spi_write(SEQ_USER_SETTING, + ARRAY_SIZE(SEQ_USER_SETTING)); + ld9040_spi_write(SEQ_PANEL_CONDITION, + ARRAY_SIZE(SEQ_PANEL_CONDITION)); + ld9040_spi_write(SEQ_DISPCTL, ARRAY_SIZE(SEQ_DISPCTL)); + ld9040_spi_write(SEQ_MANPWR, ARRAY_SIZE(SEQ_MANPWR)); + ld9040_spi_write(SEQ_PWR_CTRL, ARRAY_SIZE(SEQ_PWR_CTRL)); + ld9040_spi_write(SEQ_ELVSS_ON, ARRAY_SIZE(SEQ_ELVSS_ON)); + ld9040_spi_write(SEQ_GTCON, ARRAY_SIZE(SEQ_GTCON)); + ld9040_spi_write(SEQ_GAMMA_SET1, ARRAY_SIZE(SEQ_GAMMA_SET1)); + ld9040_spi_write(SEQ_GAMMA_CTRL, ARRAY_SIZE(SEQ_GAMMA_CTRL)); + ld9040_spi_write(SEQ_SLPOUT, ARRAY_SIZE(SEQ_SLPOUT)); + + udelay(120); +} + +void ld9040_enable_ldo(unsigned int onoff) +{ + if (onoff) + ld9040_spi_write(SEQ_DISPON, ARRAY_SIZE(SEQ_DISPON)); + else + ld9040_spi_write(SEQ_DISPOFF, ARRAY_SIZE(SEQ_DISPOFF)); +} diff --git a/qemu/roms/u-boot/drivers/video/mb862xx.c b/qemu/roms/u-boot/drivers/video/mb862xx.c new file mode 100644 index 000000000..1c74e97c5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/mb862xx.c @@ -0,0 +1,484 @@ +/* + * (C) Copyright 2007 + * DENX Software Engineering, Anatolij Gustschin, agust@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * mb862xx.c - Graphic interface for Fujitsu CoralP/Lime + * PCI and video mode code was derived from smiLynxEM driver. + */ + +#include + +#include +#include +#include +#include "videomodes.h" +#include + +#if defined(CONFIG_POST) +#include +#endif + +/* + * Graphic Device + */ +GraphicDevice mb862xx; + +/* + * 32MB external RAM - 256K Chip MMIO = 0x1FC0000 ; + */ +#define VIDEO_MEM_SIZE 0x01FC0000 + +#if defined(CONFIG_PCI) +#if defined(CONFIG_VIDEO_CORALP) + +static struct pci_device_id supported[] = { + { PCI_VENDOR_ID_FUJITSU, PCI_DEVICE_ID_CORAL_P }, + { PCI_VENDOR_ID_FUJITSU, PCI_DEVICE_ID_CORAL_PA }, + { } +}; + +/* Internal clock frequency divider table, index is mode number */ +unsigned int fr_div[] = { 0x00000f00, 0x00000900, 0x00000500 }; +#endif +#endif + +#if defined(CONFIG_VIDEO_CORALP) +#define rd_io in32r +#define wr_io out32r +#else +#define rd_io(addr) in_be32((volatile unsigned *)(addr)) +#define wr_io(addr, val) out_be32((volatile unsigned *)(addr), (val)) +#endif + +#define HOST_RD_REG(off) rd_io((dev->frameAdrs + GC_HOST_BASE + (off))) +#define HOST_WR_REG(off, val) wr_io((dev->frameAdrs + GC_HOST_BASE + (off)), \ + (val)) +#define DISP_RD_REG(off) rd_io((dev->frameAdrs + GC_DISP_BASE + (off))) +#define DISP_WR_REG(off, val) wr_io((dev->frameAdrs + GC_DISP_BASE + (off)), \ + (val)) +#define DE_RD_REG(off) rd_io((dev->dprBase + (off))) +#define DE_WR_REG(off, val) wr_io((dev->dprBase + (off)), (val)) + +#if defined(CONFIG_VIDEO_CORALP) +#define DE_WR_FIFO(val) wr_io((dev->dprBase + (GC_GEO_FIFO)), (val)) +#else +#define DE_WR_FIFO(val) wr_io((dev->dprBase + (GC_FIFO)), (val)) +#endif + +#define L0PAL_WR_REG(idx, val) wr_io((dev->frameAdrs + \ + (GC_DISP_BASE | GC_L0PAL0) + \ + ((idx) << 2)), (val)) + +#if defined(CONFIG_VIDEO_MB862xx_ACCEL) +static void gdc_sw_reset (void) +{ + GraphicDevice *dev = &mb862xx; + + HOST_WR_REG (GC_SRST, 0x1); + udelay (500); + video_hw_init (); +} + + +static void de_wait (void) +{ + GraphicDevice *dev = &mb862xx; + int lc = 0x10000; + + /* + * Sync with software writes to framebuffer, + * try to reset if engine locked + */ + while (DE_RD_REG (GC_CTR) & 0x00000131) + if (lc-- < 0) { + gdc_sw_reset (); + puts ("gdc reset done after drawing engine lock.\n"); + break; + } +} + +static void de_wait_slots (int slots) +{ + GraphicDevice *dev = &mb862xx; + int lc = 0x10000; + + /* Wait for free fifo slots */ + while (DE_RD_REG (GC_IFCNT) < slots) + if (lc-- < 0) { + gdc_sw_reset (); + puts ("gdc reset done after drawing engine lock.\n"); + break; + } +} +#endif + +#if !defined(CONFIG_VIDEO_CORALP) +static void board_disp_init (void) +{ + GraphicDevice *dev = &mb862xx; + const gdc_regs *regs = board_get_regs (); + + while (regs->index) { + DISP_WR_REG (regs->index, regs->value); + regs++; + } +} +#endif + +/* + * Init drawing engine if accel enabled. + * Also clears visible framebuffer. + */ +static void de_init (void) +{ + GraphicDevice *dev = &mb862xx; +#if defined(CONFIG_VIDEO_MB862xx_ACCEL) + int cf = (dev->gdfBytesPP == 1) ? 0x0000 : 0x8000; + + dev->dprBase = dev->frameAdrs + GC_DRAW_BASE; + + /* Setup mode and fbbase, xres, fg, bg */ + de_wait_slots (2); + DE_WR_FIFO (0xf1010108); + DE_WR_FIFO (cf | 0x0300); + DE_WR_REG (GC_FBR, 0x0); + DE_WR_REG (GC_XRES, dev->winSizeX); + DE_WR_REG (GC_FC, 0x0); + DE_WR_REG (GC_BC, 0x0); + /* Reset clipping */ + DE_WR_REG (GC_CXMIN, 0x0); + DE_WR_REG (GC_CXMAX, dev->winSizeX); + DE_WR_REG (GC_CYMIN, 0x0); + DE_WR_REG (GC_CYMAX, dev->winSizeY); + + /* Clear framebuffer using drawing engine */ + de_wait_slots (3); + DE_WR_FIFO (0x09410000); + DE_WR_FIFO (0x00000000); + DE_WR_FIFO (dev->winSizeY << 16 | dev->winSizeX); + /* sync with SW access to framebuffer */ + de_wait (); +#else + unsigned int i, *p; + + i = dev->winSizeX * dev->winSizeY; + p = (unsigned int *)dev->frameAdrs; + while (i--) + *p++ = 0; +#endif +} + +#if defined(CONFIG_VIDEO_CORALP) +/* use CCF and MMR parameters for Coral-P Eval. Board as default */ +#ifndef CONFIG_SYS_MB862xx_CCF +#define CONFIG_SYS_MB862xx_CCF 0x00090000 +#endif +#ifndef CONFIG_SYS_MB862xx_MMR +#define CONFIG_SYS_MB862xx_MMR 0x11d7fa13 +#endif + +unsigned int pci_video_init (void) +{ + GraphicDevice *dev = &mb862xx; + pci_dev_t devbusfn; + u16 device; + + if ((devbusfn = pci_find_devices (supported, 0)) < 0) { + puts("controller not present\n"); + return 0; + } + + /* PCI setup */ + pci_write_config_dword (devbusfn, PCI_COMMAND, + (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &dev->frameAdrs); + dev->frameAdrs = pci_mem_to_phys (devbusfn, dev->frameAdrs); + + if (dev->frameAdrs == 0) { + puts ("PCI config: failed to get base address\n"); + return 0; + } + + dev->pciBase = dev->frameAdrs; + + puts("Coral-"); + + pci_read_config_word(devbusfn, PCI_DEVICE_ID, &device); + switch (device) { + case PCI_DEVICE_ID_CORAL_P: + puts("P\n"); + break; + case PCI_DEVICE_ID_CORAL_PA: + puts("PA\n"); + break; + default: + puts("Unknown\n"); + return 0; + } + + /* Setup clocks and memory mode for Coral-P(A) */ + HOST_WR_REG(GC_CCF, CONFIG_SYS_MB862xx_CCF); + udelay (200); + HOST_WR_REG(GC_MMR, CONFIG_SYS_MB862xx_MMR); + udelay (100); + return dev->frameAdrs; +} + +unsigned int card_init (void) +{ + GraphicDevice *dev = &mb862xx; + unsigned int cf, videomode, div = 0; + unsigned long t1, hsync, vsync; + char *penv; + int tmp, i, bpp; + struct ctfb_res_modes *res_mode; + struct ctfb_res_modes var_mode; + + memset (dev, 0, sizeof (GraphicDevice)); + + if (!pci_video_init ()) + return 0; + + tmp = 0; + videomode = 0x310; + /* get video mode via environment */ + if ((penv = getenv ("videomode")) != NULL) { + /* decide if it is a string */ + if (penv[0] <= '9') { + videomode = (int) simple_strtoul (penv, NULL, 16); + tmp = 1; + } + } else { + tmp = 1; + } + + if (tmp) { + /* parameter are vesa modes, search params */ + for (i = 0; i < VESA_MODES_COUNT; i++) { + if (vesa_modes[i].vesanr == videomode) + break; + } + if (i == VESA_MODES_COUNT) { + printf ("\tno VESA Mode found, fallback to mode 0x%x\n", + videomode); + i = 0; + } + res_mode = (struct ctfb_res_modes *) + &res_mode_init[vesa_modes[i].resindex]; + if (vesa_modes[i].resindex > 2) { + puts ("\tUnsupported resolution, using default\n"); + bpp = vesa_modes[1].bits_per_pixel; + div = fr_div[1]; + } + bpp = vesa_modes[i].bits_per_pixel; + div = fr_div[vesa_modes[i].resindex]; + } else { + res_mode = (struct ctfb_res_modes *) &var_mode; + bpp = video_get_params (res_mode, penv); + } + + /* calculate hsync and vsync freq (info only) */ + t1 = (res_mode->left_margin + res_mode->xres + + res_mode->right_margin + res_mode->hsync_len) / 8; + t1 *= 8; + t1 *= res_mode->pixclock; + t1 /= 1000; + hsync = 1000000000L / t1; + t1 *= (res_mode->upper_margin + res_mode->yres + + res_mode->lower_margin + res_mode->vsync_len); + t1 /= 1000; + vsync = 1000000000L / t1; + + /* fill in Graphic device struct */ + sprintf (dev->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres, + res_mode->yres, bpp, (hsync / 1000), (vsync / 1000)); + printf ("\t%s\n", dev->modeIdent); + dev->winSizeX = res_mode->xres; + dev->winSizeY = res_mode->yres; + dev->memSize = VIDEO_MEM_SIZE; + + switch (bpp) { + case 8: + dev->gdfIndex = GDF__8BIT_INDEX; + dev->gdfBytesPP = 1; + break; + case 15: + case 16: + dev->gdfIndex = GDF_15BIT_555RGB; + dev->gdfBytesPP = 2; + break; + default: + printf ("\t%d bpp configured, but only 8,15 and 16 supported\n", + bpp); + puts ("\tfallback to 15bpp\n"); + dev->gdfIndex = GDF_15BIT_555RGB; + dev->gdfBytesPP = 2; + } + + /* Setup dot clock (internal pll, division rate) */ + DISP_WR_REG (GC_DCM1, div); + /* L0 init */ + cf = (dev->gdfBytesPP == 1) ? 0x00000000 : 0x80000000; + DISP_WR_REG (GC_L0M, ((dev->winSizeX * dev->gdfBytesPP) / 64) << 16 | + (dev->winSizeY - 1) | cf); + DISP_WR_REG (GC_L0OA0, 0x0); + DISP_WR_REG (GC_L0DA0, 0x0); + DISP_WR_REG (GC_L0DY_L0DX, 0x0); + DISP_WR_REG (GC_L0EM, 0x0); + DISP_WR_REG (GC_L0WY_L0WX, 0x0); + DISP_WR_REG (GC_L0WH_L0WW, (dev->winSizeY - 1) << 16 | dev->winSizeX); + + /* Display timing init */ + DISP_WR_REG (GC_HTP_A, (dev->winSizeX + + res_mode->left_margin + + res_mode->right_margin + + res_mode->hsync_len - 1) << 16); + DISP_WR_REG (GC_HDB_HDP_A, (dev->winSizeX - 1) << 16 | + (dev->winSizeX - 1)); + DISP_WR_REG (GC_VSW_HSW_HSP_A, (res_mode->vsync_len - 1) << 24 | + (res_mode->hsync_len - 1) << 16 | + (dev->winSizeX + + res_mode->right_margin - 1)); + DISP_WR_REG (GC_VTR_A, (dev->winSizeY + res_mode->lower_margin + + res_mode->upper_margin + + res_mode->vsync_len - 1) << 16); + DISP_WR_REG (GC_VDP_VSP_A, (dev->winSizeY-1) << 16 | + (dev->winSizeY + + res_mode->lower_margin - 1)); + DISP_WR_REG (GC_WY_WX, 0x0); + DISP_WR_REG (GC_WH_WW, dev->winSizeY << 16 | dev->winSizeX); + /* Display enable, L0 layer */ + DISP_WR_REG (GC_DCM1, 0x80010000 | div); + + return dev->frameAdrs; +} +#endif + + +#if !defined(CONFIG_VIDEO_CORALP) +int mb862xx_probe(unsigned int addr) +{ + GraphicDevice *dev = &mb862xx; + unsigned int reg; + + dev->frameAdrs = addr; + dev->dprBase = dev->frameAdrs + GC_DRAW_BASE; + + /* Try to access GDC ID/Revision registers */ + reg = HOST_RD_REG (GC_CID); + reg = HOST_RD_REG (GC_CID); + if (reg == 0x303) { + reg = DE_RD_REG(GC_REV); + reg = DE_RD_REG(GC_REV); + if ((reg & ~0xff) == 0x20050100) + return MB862XX_TYPE_LIME; + } + + return 0; +} +#endif + +void *video_hw_init (void) +{ + GraphicDevice *dev = &mb862xx; + + puts ("Video: Fujitsu "); + + memset (dev, 0, sizeof (GraphicDevice)); + +#if defined(CONFIG_VIDEO_CORALP) + if (card_init () == 0) + return NULL; +#else + /* + * Preliminary init of the onboard graphic controller, + * retrieve base address + */ + if ((dev->frameAdrs = board_video_init ()) == 0) { + puts ("Controller not found!\n"); + return NULL; + } else { + puts ("Lime\n"); + + /* Set Change of Clock Frequency Register */ + HOST_WR_REG (GC_CCF, CONFIG_SYS_MB862xx_CCF); + /* Delay required */ + udelay(300); + /* Set Memory I/F Mode Register) */ + HOST_WR_REG (GC_MMR, CONFIG_SYS_MB862xx_MMR); + } +#endif + + de_init (); + +#if !defined(CONFIG_VIDEO_CORALP) + board_disp_init (); +#endif + +#if (defined(CONFIG_LWMON5) || \ + defined(CONFIG_SOCRATES)) && !(CONFIG_POST & CONFIG_SYS_POST_SYSMON) + /* Lamp on */ + board_backlight_switch (1); +#endif + + return dev; +} + +/* + * Set a RGB color in the LUT + */ +void video_set_lut (unsigned int index, unsigned char r, + unsigned char g, unsigned char b) +{ + GraphicDevice *dev = &mb862xx; + + L0PAL_WR_REG (index, (r << 16) | (g << 8) | (b)); +} + +#if defined(CONFIG_VIDEO_MB862xx_ACCEL) +/* + * Drawing engine Fill and BitBlt screen region + */ +void video_hw_rectfill (unsigned int bpp, unsigned int dst_x, + unsigned int dst_y, unsigned int dim_x, + unsigned int dim_y, unsigned int color) +{ + GraphicDevice *dev = &mb862xx; + + de_wait_slots (3); + DE_WR_REG (GC_FC, color); + DE_WR_FIFO (0x09410000); + DE_WR_FIFO ((dst_y << 16) | dst_x); + DE_WR_FIFO ((dim_y << 16) | dim_x); + de_wait (); +} + +void video_hw_bitblt (unsigned int bpp, unsigned int src_x, + unsigned int src_y, unsigned int dst_x, + unsigned int dst_y, unsigned int width, + unsigned int height) +{ + GraphicDevice *dev = &mb862xx; + unsigned int ctrl = 0x0d000000L; + + if (src_x >= dst_x && src_y >= dst_y) + ctrl |= 0x00440000L; + else if (src_x >= dst_x && src_y <= dst_y) + ctrl |= 0x00460000L; + else if (src_x <= dst_x && src_y >= dst_y) + ctrl |= 0x00450000L; + else + ctrl |= 0x00470000L; + + de_wait_slots (4); + DE_WR_FIFO (ctrl); + DE_WR_FIFO ((src_y << 16) | src_x); + DE_WR_FIFO ((dst_y << 16) | dst_x); + DE_WR_FIFO ((height << 16) | width); + de_wait (); /* sync */ +} +#endif diff --git a/qemu/roms/u-boot/drivers/video/mb86r0xgdc.c b/qemu/roms/u-boot/drivers/video/mb86r0xgdc.c new file mode 100644 index 000000000..bb7a7497c --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/mb86r0xgdc.c @@ -0,0 +1,168 @@ +/* + * (C) Copyright 2010 + * Matthias Weisser + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * mb86r0xgdc.c - Graphic interface for Fujitsu MB86R0x integrated graphic + * controller. + */ + +#include + +#include +#include +#include +#include +#include "videomodes.h" + +/* + * 4MB (at the end of system RAM) + */ +#define VIDEO_MEM_SIZE 0x400000 + +#define FB_SYNC_CLK_INV (1<<16) /* pixel clock inverted */ + +/* + * Graphic Device + */ +static GraphicDevice mb86r0x; + +static void dsp_init(struct mb86r0x_gdc_dsp *dsp, char *modestr, + u32 *videomem) +{ + struct ctfb_res_modes var_mode; + u32 dcm1, dcm2, dcm3; + u16 htp, hdp, hdb, hsp, vtr, vsp, vdp; + u8 hsw, vsw; + u32 l2m, l2em, l2oa0, l2da0, l2oa1, l2da1; + u16 l2dx, l2dy, l2wx, l2wy, l2ww, l2wh; + unsigned long div; + int bpp; + + bpp = video_get_params(&var_mode, modestr); + + if (bpp == 0) { + var_mode.xres = 640; + var_mode.yres = 480; + var_mode.pixclock = 39721; /* 25MHz */ + var_mode.left_margin = 48; + var_mode.right_margin = 16; + var_mode.upper_margin = 33; + var_mode.lower_margin = 10; + var_mode.hsync_len = 96; + var_mode.vsync_len = 2; + var_mode.sync = 0; + var_mode.vmode = 0; + bpp = 15; + } + + /* Fill memory with white */ + memset(videomem, 0xFF, var_mode.xres * var_mode.yres * 2); + + mb86r0x.winSizeX = var_mode.xres; + mb86r0x.winSizeY = var_mode.yres; + + /* LCD base clock is ~ 660MHZ. We do calculations in kHz */ + div = 660000 / (1000000000L / var_mode.pixclock); + if (div > 64) + div = 64; + if (0 == div) + div = 1; + + dcm1 = (div - 1) << 8; + dcm2 = 0x00000000; + if (var_mode.sync & FB_SYNC_CLK_INV) + dcm3 = 0x00000100; + else + dcm3 = 0x00000000; + + htp = var_mode.left_margin + var_mode.xres + + var_mode.hsync_len + var_mode.right_margin; + hdp = var_mode.xres; + hdb = var_mode.xres; + hsp = var_mode.xres + var_mode.right_margin; + hsw = var_mode.hsync_len; + + vsw = var_mode.vsync_len; + vtr = var_mode.upper_margin + var_mode.yres + + var_mode.vsync_len + var_mode.lower_margin; + vsp = var_mode.yres + var_mode.lower_margin; + vdp = var_mode.yres; + + l2m = ((var_mode.yres - 1) << (0)) | + (((var_mode.xres * 2) / 64) << (16)) | + ((1) << (31)); + + l2em = (1 << 0) | (1 << 1); + + l2oa0 = mb86r0x.frameAdrs; + l2da0 = mb86r0x.frameAdrs; + l2oa1 = mb86r0x.frameAdrs; + l2da1 = mb86r0x.frameAdrs; + l2dx = 0; + l2dy = 0; + l2wx = 0; + l2wy = 0; + l2ww = var_mode.xres; + l2wh = var_mode.yres - 1; + + writel(dcm1, &dsp->dcm1); + writel(dcm2, &dsp->dcm2); + writel(dcm3, &dsp->dcm3); + + writew(htp, &dsp->htp); + writew(hdp, &dsp->hdp); + writew(hdb, &dsp->hdb); + writew(hsp, &dsp->hsp); + writeb(hsw, &dsp->hsw); + + writeb(vsw, &dsp->vsw); + writew(vtr, &dsp->vtr); + writew(vsp, &dsp->vsp); + writew(vdp, &dsp->vdp); + + writel(l2m, &dsp->l2m); + writel(l2em, &dsp->l2em); + writel(l2oa0, &dsp->l2oa0); + writel(l2da0, &dsp->l2da0); + writel(l2oa1, &dsp->l2oa1); + writel(l2da1, &dsp->l2da1); + writew(l2dx, &dsp->l2dx); + writew(l2dy, &dsp->l2dy); + writew(l2wx, &dsp->l2wx); + writew(l2wy, &dsp->l2wy); + writew(l2ww, &dsp->l2ww); + writew(l2wh, &dsp->l2wh); + + writel(dcm1 | (1 << 18) | (1 << 31), &dsp->dcm1); +} + +void *video_hw_init(void) +{ + struct mb86r0x_gdc *gdc = (struct mb86r0x_gdc *) MB86R0x_GDC_BASE; + GraphicDevice *pGD = &mb86r0x; + char *s; + u32 *vid; + + memset(pGD, 0, sizeof(GraphicDevice)); + + pGD->gdfIndex = GDF_15BIT_555RGB; + pGD->gdfBytesPP = 2; + pGD->memSize = VIDEO_MEM_SIZE; + pGD->frameAdrs = PHYS_SDRAM + PHYS_SDRAM_SIZE - VIDEO_MEM_SIZE; + + vid = (u32 *)pGD->frameAdrs; + + s = getenv("videomode"); + if (s != NULL) + dsp_init(&gdc->dsp0, s, vid); + + s = getenv("videomode1"); + if (s != NULL) + dsp_init(&gdc->dsp1, s, vid); + + return pGD; +} diff --git a/qemu/roms/u-boot/drivers/video/mpc8xx_lcd.c b/qemu/roms/u-boot/drivers/video/mpc8xx_lcd.c new file mode 100644 index 000000000..fceed871a --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/mpc8xx_lcd.c @@ -0,0 +1,548 @@ +/* + * (C) Copyright 2001-2002 + * Wolfgang Denk, DENX Software Engineering -- wd@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/************************************************************************/ +/* ** HEADER FILES */ +/************************************************************************/ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_POST) +#include +#endif +#include + +#ifdef CONFIG_LCD + +/************************************************************************/ +/* ** CONFIG STUFF -- should be moved to board config file */ +/************************************************************************/ +#ifndef CONFIG_LCD_INFO +#define CONFIG_LCD_INFO /* Display Logo, (C) and system info */ +#endif + +#if defined(CONFIG_V37) || defined(CONFIG_EDT32F10) +#undef CONFIG_LCD_LOGO +#undef CONFIG_LCD_INFO +#endif + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_KYOCERA_KCS057QV1AJ +/* + * Kyocera KCS057QV1AJ-G23. Passive, color, single scan. + */ +#define LCD_BPP LCD_COLOR4 + +vidinfo_t panel_info = { + 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, + LCD_BPP, 1, 0, 1, 0, 5, 0, 0, 0 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_KYOCERA_KCS057QV1AJ */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_HITACHI_SP19X001_Z1A +/* + * Hitachi SP19X001-. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 154, 116, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, + LCD_COLOR8, 1, 0, 1, 0, 0, 0, 0, 0 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_HITACHI_SP19X001_Z1A */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_NEC_NL6448AC33 +/* + * NEC NL6448AC33-18. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 144, 2, 0, 33 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_NEC_NL6448AC33 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_NEC_NL6448BC20 +/* + * NEC NL6448BC20-08. 6.5", 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 144, 2, 0, 33 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_NEC_NL6448BC20 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_NEC_NL6448BC33_54 +/* + * NEC NL6448BC33-54. 10.4", 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 212, 158, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 144, 2, 0, 33 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_NEC_NL6448BC33_54 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_LQ104V7DS01 +/* + * SHARP LQ104V7DS01. 6.5", 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_LOW, + 3, 0, 0, 1, 1, 25, 1, 0, 33 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_SHARP_LQ104V7DS01 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_16x9 +/* + * Sharp 320x240. Active, color, single scan. It isn't 16x9, and I am + * not sure what it is....... + */ +vidinfo_t panel_info = { + 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 15, 4, 0, 3 +}; +#endif /* CONFIG_SHARP_16x9 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_LQ057Q3DC02 +/* + * Sharp LQ057Q3DC02 display. Active, color, single scan. + */ +#undef LCD_DF +#define LCD_DF 12 + +vidinfo_t panel_info = { + 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 15, 4, 0, 3 + /* wbl, vpw, lcdac, wbf */ +}; +#define CONFIG_LCD_INFO_BELOW_LOGO +#endif /* CONFIG_SHARP_LQ057Q3DC02 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_LQ64D341 +/* + * Sharp LQ64D341 display, 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 128, 16, 0, 32 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_SHARP_LQ64D341 */ + +#ifdef CONFIG_SHARP_LQ065T9DR51U +/* + * Sharp LQ065T9DR51U display, 400x240. Active, color, single scan. + */ +vidinfo_t panel_info = { + 400, 240, 143, 79, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 248, 4, 0, 35 + /* wbl, vpw, lcdac, wbf */ +}; +#define CONFIG_LCD_INFO_BELOW_LOGO +#endif /* CONFIG_SHARP_LQ065T9DR51U */ + +#ifdef CONFIG_SHARP_LQ084V1DG21 +/* + * Sharp LQ084V1DG21 display, 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 171, 129, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_LOW, + 3, 0, 0, 1, 1, 160, 3, 0, 48 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_SHARP_LQ084V1DG21 */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_HLD1045 +/* + * HLD1045 display, 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 160, 3, 0, 48 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_HLD1045 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_PRIMEVIEW_V16C6448AC +/* + * Prime View V16C6448AC + */ +vidinfo_t panel_info = { + 640, 480, 130, 98, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 144, 2, 0, 35 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_PRIMEVIEW_V16C6448AC */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_OPTREX_BW +/* + * Optrex CBL50840-2 NF-FW 99 22 M5 + * or + * Hitachi LMG6912RPFC-00T + * or + * Hitachi SP14Q002 + * + * 320x240. Black & white. + */ +#define OPTREX_BPP 0 /* 0 - monochrome, 1 bpp */ + /* 1 - 4 grey levels, 2 bpp */ + /* 2 - 16 grey levels, 4 bpp */ +vidinfo_t panel_info = { + 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, + OPTREX_BPP, 0, 0, 0, 0, 0, 0, 0, 0, 4 +}; +#endif /* CONFIG_OPTREX_BW */ + +/*-----------------------------------------------------------------*/ +#ifdef CONFIG_EDT32F10 +/* + * Emerging Display Technologies 320x240. Passive, monochrome, single scan. + */ +#define LCD_BPP LCD_MONOCHROME +#define LCD_DF 10 + +vidinfo_t panel_info = { + 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, + LCD_BPP, 0, 0, 0, 0, 33, 0, 0, 0 +}; +#endif + +/************************************************************************/ +/* ----------------- chipset specific functions ----------------------- */ +/************************************************************************/ + +/* + * Calculate fb size for VIDEOLFB_ATAG. + */ +ulong calc_fbsize (void) +{ + ulong size; + int line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; + + size = line_length * panel_info.vl_row; + + return size; +} + +void lcd_ctrl_init (void *lcdbase) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile lcd823_t *lcdp = &immr->im_lcd; + + uint lccrtmp; + uint lchcr_hpc_tmp; + + /* Initialize the LCD control register according to the LCD + * parameters defined. We do everything here but enable + * the controller. + */ + +#ifdef CONFIG_RPXLITE + /* This is special for RPXlite_DW Software Development Platform **[Sam]** */ + panel_info.vl_dp = CONFIG_SYS_LOW; +#endif + + lccrtmp = LCDBIT (LCCR_BNUM_BIT, + (((panel_info.vl_row * panel_info.vl_col) * (1 << LCD_BPP)) / 128)); + + lccrtmp |= LCDBIT (LCCR_CLKP_BIT, panel_info.vl_clkp) | + LCDBIT (LCCR_OEP_BIT, panel_info.vl_oep) | + LCDBIT (LCCR_HSP_BIT, panel_info.vl_hsp) | + LCDBIT (LCCR_VSP_BIT, panel_info.vl_vsp) | + LCDBIT (LCCR_DP_BIT, panel_info.vl_dp) | + LCDBIT (LCCR_BPIX_BIT, panel_info.vl_bpix) | + LCDBIT (LCCR_LBW_BIT, panel_info.vl_lbw) | + LCDBIT (LCCR_SPLT_BIT, panel_info.vl_splt) | + LCDBIT (LCCR_CLOR_BIT, panel_info.vl_clor) | + LCDBIT (LCCR_TFT_BIT, panel_info.vl_tft); + +#if 0 + lccrtmp |= ((SIU_LEVEL5 / 2) << 12); + lccrtmp |= LCCR_EIEN; +#endif + + lcdp->lcd_lccr = lccrtmp; + lcdp->lcd_lcsr = 0xFF; /* Clear pending interrupts */ + + /* Initialize LCD controller bus priorities. + */ +#ifdef CONFIG_RBC823 + immr->im_siu_conf.sc_sdcr = (immr->im_siu_conf.sc_sdcr & ~0x0f) | 1; /* RAID = 01, LAID = 00 */ +#else + immr->im_siu_conf.sc_sdcr &= ~0x0f; /* RAID = LAID = 0 */ + + /* set SHFT/CLOCK division factor 4 + * This needs to be set based upon display type and processor + * speed. The TFT displays run about 20 to 30 MHz. + * I was running 64 MHz processor speed. + * The value for this divider must be chosen so the result is + * an integer of the processor speed (i.e., divide by 3 with + * 64 MHz would be bad). + */ + immr->im_clkrst.car_sccr &= ~0x1F; + immr->im_clkrst.car_sccr |= LCD_DF; /* was 8 */ + +#endif /* CONFIG_RBC823 */ + +#if defined(CONFIG_RBC823) + /* Enable LCD on port D. + */ + immr->im_ioport.iop_pddat &= 0x0300; + immr->im_ioport.iop_pdpar |= 0x1CFF; + immr->im_ioport.iop_pddir |= 0x1CFF; + + /* Configure LCD_ON, VEE_ON, CCFL_ON on port B. + */ + immr->im_cpm.cp_pbdat &= ~0x00005001; + immr->im_cpm.cp_pbpar &= ~0x00005001; + immr->im_cpm.cp_pbdir |= 0x00005001; +#elif !defined(CONFIG_EDT32F10) + /* Enable LCD on port D. + */ + immr->im_ioport.iop_pdpar |= 0x1FFF; + immr->im_ioport.iop_pddir |= 0x1FFF; + + /* Enable LCD_A/B/C on port B. + */ + immr->im_cpm.cp_pbpar |= 0x00005001; + immr->im_cpm.cp_pbdir |= 0x00005001; +#else + /* Enable LCD on port D. + */ + immr->im_ioport.iop_pdpar |= 0x1DFF; + immr->im_ioport.iop_pdpar &= ~0x0200; + immr->im_ioport.iop_pddir |= 0x1FFF; + immr->im_ioport.iop_pddat |= 0x0200; +#endif + + /* Load the physical address of the linear frame buffer + * into the LCD controller. + * BIG NOTE: This has to be modified to load A and B depending + * upon the split mode of the LCD. + */ + lcdp->lcd_lcfaa = (ulong)lcdbase; + lcdp->lcd_lcfba = (ulong)lcdbase; + + /* MORE HACKS...This must be updated according to 823 manual + * for different panels. + * Udi Finkelstein - done - see below: + * Note: You better not try unsupported combinations such as + * 4-bit wide passive dual scan LCD at 4/8 Bit color. + */ + lchcr_hpc_tmp = + (panel_info.vl_col * + (panel_info.vl_tft ? 8 : + (((2 - panel_info.vl_lbw) << /* 4 bit=2, 8-bit = 1 */ + /* use << to mult by: single scan = 1, dual scan = 2 */ + panel_info.vl_splt) * + (panel_info.vl_bpix | 1)))) >> 3; /* 2/4 BPP = 1, 8/16 BPP = 3 */ + + lcdp->lcd_lchcr = LCHCR_BO | + LCDBIT (LCHCR_AT_BIT, 4) | + LCDBIT (LCHCR_HPC_BIT, lchcr_hpc_tmp) | + panel_info.vl_wbl; + + lcdp->lcd_lcvcr = LCDBIT (LCVCR_VPW_BIT, panel_info.vl_vpw) | + LCDBIT (LCVCR_LCD_AC_BIT, panel_info.vl_lcdac) | + LCDBIT (LCVCR_VPC_BIT, panel_info.vl_row) | + panel_info.vl_wbf; + +} + +/*----------------------------------------------------------------------*/ + +#if LCD_BPP == LCD_COLOR8 +void +lcd_setcolreg (ushort regno, ushort red, ushort green, ushort blue) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile cpm8xx_t *cp = &(immr->im_cpm); + unsigned short colreg, *cmap_ptr; + + cmap_ptr = (unsigned short *)&cp->lcd_cmap[regno * 2]; + + colreg = ((red & 0x0F) << 8) | + ((green & 0x0F) << 4) | + (blue & 0x0F) ; +#ifdef CONFIG_SYS_INVERT_COLORS + colreg ^= 0x0FFF; +#endif + *cmap_ptr = colreg; + + debug ("setcolreg: reg %2d @ %p: R=%02X G=%02X B=%02X => %02X%02X\n", + regno, &(cp->lcd_cmap[regno * 2]), + red, green, blue, + cp->lcd_cmap[ regno * 2 ], cp->lcd_cmap[(regno * 2) + 1]); +} +#endif /* LCD_COLOR8 */ + +/*----------------------------------------------------------------------*/ + +#if LCD_BPP == LCD_MONOCHROME +static +void lcd_initcolregs (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile cpm8xx_t *cp = &(immr->im_cpm); + ushort regno; + + for (regno = 0; regno < 16; regno++) { + cp->lcd_cmap[regno * 2] = 0; + cp->lcd_cmap[(regno * 2) + 1] = regno & 0x0f; + } +} +#endif + +/*----------------------------------------------------------------------*/ + +void lcd_enable (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile lcd823_t *lcdp = &immr->im_lcd; + + /* Enable the LCD panel */ +#ifndef CONFIG_RBC823 + immr->im_siu_conf.sc_sdcr |= (1 << (31 - 25)); /* LAM = 1 */ +#endif + lcdp->lcd_lccr |= LCCR_PON; + +#ifdef CONFIG_V37 + /* Turn on display backlight */ + immr->im_cpm.cp_pbpar |= 0x00008000; + immr->im_cpm.cp_pbdir |= 0x00008000; +#elif defined(CONFIG_RBC823) + /* Turn on display backlight */ + immr->im_cpm.cp_pbdat |= 0x00004000; +#endif + +#if defined(CONFIG_LWMON) + { uchar c = pic_read (0x60); +#if defined(CONFIG_LCD) && defined(CONFIG_LWMON) && (CONFIG_POST & CONFIG_SYS_POST_SYSMON) + /* Enable LCD later in sysmon test, only if temperature is OK */ +#else + c |= 0x07; /* Power on CCFL, Enable CCFL, Chip Enable LCD */ +#endif + pic_write (0x60, c); + } +#endif /* CONFIG_LWMON */ + +#if defined(CONFIG_R360MPI) + { + extern void r360_i2c_lcd_write (uchar data0, uchar data1); + unsigned long bgi, ctr; + char *p; + + if ((p = getenv("lcdbgi")) != NULL) { + bgi = simple_strtoul (p, 0, 10) & 0xFFF; + } else { + bgi = 0xFFF; + } + + if ((p = getenv("lcdctr")) != NULL) { + ctr = simple_strtoul (p, 0, 10) & 0xFFF; + } else { + ctr=0x7FF; + } + + r360_i2c_lcd_write(0x10, 0x01); + r360_i2c_lcd_write(0x20, 0x01); + r360_i2c_lcd_write(0x30 | ((bgi>>8) & 0xF), bgi & 0xFF); + r360_i2c_lcd_write(0x40 | ((ctr>>8) & 0xF), ctr & 0xFF); + } +#endif /* CONFIG_R360MPI */ +#ifdef CONFIG_RBC823 + udelay(200000); /* wait 200ms */ + /* Turn VEE_ON first */ + immr->im_cpm.cp_pbdat |= 0x00000001; + udelay(200000); /* wait 200ms */ + /* Now turn on LCD_ON */ + immr->im_cpm.cp_pbdat |= 0x00001000; +#endif +#ifdef CONFIG_RRVISION + debug ("PC4->Output(1): enable LVDS\n"); + debug ("PC5->Output(0): disable PAL clock\n"); + immr->im_ioport.iop_pddir |= 0x1000; + immr->im_ioport.iop_pcpar &= ~(0x0C00); + immr->im_ioport.iop_pcdir |= 0x0C00 ; + immr->im_ioport.iop_pcdat |= 0x0800 ; + immr->im_ioport.iop_pcdat &= ~(0x0400); + debug ("PDPAR=0x%04X PDDIR=0x%04X PDDAT=0x%04X\n", + immr->im_ioport.iop_pdpar, + immr->im_ioport.iop_pddir, + immr->im_ioport.iop_pddat); + debug ("PCPAR=0x%04X PCDIR=0x%04X PCDAT=0x%04X\n", + immr->im_ioport.iop_pcpar, + immr->im_ioport.iop_pcdir, + immr->im_ioport.iop_pcdat); +#endif +} + +/*----------------------------------------------------------------------*/ + +#if defined (CONFIG_RBC823) +void lcd_disable (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile lcd823_t *lcdp = &immr->im_lcd; + +#if defined(CONFIG_LWMON) + { uchar c = pic_read (0x60); + c &= ~0x07; /* Power off CCFL, Disable CCFL, Chip Disable LCD */ + pic_write (0x60, c); + } +#elif defined(CONFIG_R360MPI) + { + extern void r360_i2c_lcd_write (uchar data0, uchar data1); + + r360_i2c_lcd_write(0x10, 0x00); + r360_i2c_lcd_write(0x20, 0x00); + r360_i2c_lcd_write(0x30, 0x00); + r360_i2c_lcd_write(0x40, 0x00); + } +#endif /* CONFIG_LWMON */ + /* Disable the LCD panel */ + lcdp->lcd_lccr &= ~LCCR_PON; +#ifdef CONFIG_RBC823 + /* Turn off display backlight, VEE and LCD_ON */ + immr->im_cpm.cp_pbdat &= ~0x00005001; +#else + immr->im_siu_conf.sc_sdcr &= ~(1 << (31 - 25)); /* LAM = 0 */ +#endif /* CONFIG_RBC823 */ +} +#endif /* NOT_USED_SO_FAR || CONFIG_RBC823 */ + + +/************************************************************************/ + +#endif /* CONFIG_LCD */ diff --git a/qemu/roms/u-boot/drivers/video/mx3fb.c b/qemu/roms/u-boot/drivers/video/mx3fb.c new file mode 100644 index 000000000..aa4cc433b --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/mx3fb.c @@ -0,0 +1,915 @@ +/* + * Copyright (C) 2009 + * Guennadi Liakhovetski, DENX Software Engineering, + * Copyright (C) 2011 + * HALE electronic GmbH, + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include + +#include +#include +#include +#include + +#include "videomodes.h" + +/* this might need panel specific set-up as-well */ +#define IF_CONF 0 + +/* -------------- controller specific stuff -------------- */ + +/* IPU DMA Controller channel definitions. */ +enum ipu_channel { + IDMAC_IC_0 = 0, /* IC (encoding task) to memory */ + IDMAC_IC_1 = 1, /* IC (viewfinder task) to memory */ + IDMAC_ADC_0 = 1, + IDMAC_IC_2 = 2, + IDMAC_ADC_1 = 2, + IDMAC_IC_3 = 3, + IDMAC_IC_4 = 4, + IDMAC_IC_5 = 5, + IDMAC_IC_6 = 6, + IDMAC_IC_7 = 7, /* IC (sensor data) to memory */ + IDMAC_IC_8 = 8, + IDMAC_IC_9 = 9, + IDMAC_IC_10 = 10, + IDMAC_IC_11 = 11, + IDMAC_IC_12 = 12, + IDMAC_IC_13 = 13, + IDMAC_SDC_0 = 14, /* Background synchronous display data */ + IDMAC_SDC_1 = 15, /* Foreground data (overlay) */ + IDMAC_SDC_2 = 16, + IDMAC_SDC_3 = 17, + IDMAC_ADC_2 = 18, + IDMAC_ADC_3 = 19, + IDMAC_ADC_4 = 20, + IDMAC_ADC_5 = 21, + IDMAC_ADC_6 = 22, + IDMAC_ADC_7 = 23, + IDMAC_PF_0 = 24, + IDMAC_PF_1 = 25, + IDMAC_PF_2 = 26, + IDMAC_PF_3 = 27, + IDMAC_PF_4 = 28, + IDMAC_PF_5 = 29, + IDMAC_PF_6 = 30, + IDMAC_PF_7 = 31, +}; + +/* More formats can be copied from the Linux driver if needed */ +enum pixel_fmt { + /* 2 bytes */ + IPU_PIX_FMT_RGB565, + IPU_PIX_FMT_RGB666, + IPU_PIX_FMT_BGR666, + /* 3 bytes */ + IPU_PIX_FMT_RGB24, +}; + +struct pixel_fmt_cfg { + u32 b0; + u32 b1; + u32 b2; + u32 acc; +}; + +static struct pixel_fmt_cfg fmt_cfg[] = { + [IPU_PIX_FMT_RGB24] = { + 0x1600AAAA, 0x00E05555, 0x00070000, 3, + }, + [IPU_PIX_FMT_RGB666] = { + 0x0005000F, 0x000B000F, 0x0011000F, 1, + }, + [IPU_PIX_FMT_BGR666] = { + 0x0011000F, 0x000B000F, 0x0005000F, 1, + }, + [IPU_PIX_FMT_RGB565] = { + 0x0004003F, 0x000A000F, 0x000F003F, 1, + } +}; + +enum ipu_panel { + IPU_PANEL_SHARP_TFT, + IPU_PANEL_TFT, +}; + +/* IPU Common registers */ +/* IPU_CONF and its bits already defined in imx-regs.h */ +#define IPU_CHA_BUF0_RDY (0x04 + IPU_BASE) +#define IPU_CHA_BUF1_RDY (0x08 + IPU_BASE) +#define IPU_CHA_DB_MODE_SEL (0x0C + IPU_BASE) +#define IPU_CHA_CUR_BUF (0x10 + IPU_BASE) +#define IPU_FS_PROC_FLOW (0x14 + IPU_BASE) +#define IPU_FS_DISP_FLOW (0x18 + IPU_BASE) +#define IPU_TASKS_STAT (0x1C + IPU_BASE) +#define IPU_IMA_ADDR (0x20 + IPU_BASE) +#define IPU_IMA_DATA (0x24 + IPU_BASE) +#define IPU_INT_CTRL_1 (0x28 + IPU_BASE) +#define IPU_INT_CTRL_2 (0x2C + IPU_BASE) +#define IPU_INT_CTRL_3 (0x30 + IPU_BASE) +#define IPU_INT_CTRL_4 (0x34 + IPU_BASE) +#define IPU_INT_CTRL_5 (0x38 + IPU_BASE) +#define IPU_INT_STAT_1 (0x3C + IPU_BASE) +#define IPU_INT_STAT_2 (0x40 + IPU_BASE) +#define IPU_INT_STAT_3 (0x44 + IPU_BASE) +#define IPU_INT_STAT_4 (0x48 + IPU_BASE) +#define IPU_INT_STAT_5 (0x4C + IPU_BASE) +#define IPU_BRK_CTRL_1 (0x50 + IPU_BASE) +#define IPU_BRK_CTRL_2 (0x54 + IPU_BASE) +#define IPU_BRK_STAT (0x58 + IPU_BASE) +#define IPU_DIAGB_CTRL (0x5C + IPU_BASE) + +/* Image Converter Registers */ +#define IC_CONF (0x88 + IPU_BASE) +#define IC_PRP_ENC_RSC (0x8C + IPU_BASE) +#define IC_PRP_VF_RSC (0x90 + IPU_BASE) +#define IC_PP_RSC (0x94 + IPU_BASE) +#define IC_CMBP_1 (0x98 + IPU_BASE) +#define IC_CMBP_2 (0x9C + IPU_BASE) +#define PF_CONF (0xA0 + IPU_BASE) +#define IDMAC_CONF (0xA4 + IPU_BASE) +#define IDMAC_CHA_EN (0xA8 + IPU_BASE) +#define IDMAC_CHA_PRI (0xAC + IPU_BASE) +#define IDMAC_CHA_BUSY (0xB0 + IPU_BASE) + +/* Image Converter Register bits */ +#define IC_CONF_PRPENC_EN 0x00000001 +#define IC_CONF_PRPENC_CSC1 0x00000002 +#define IC_CONF_PRPENC_ROT_EN 0x00000004 +#define IC_CONF_PRPVF_EN 0x00000100 +#define IC_CONF_PRPVF_CSC1 0x00000200 +#define IC_CONF_PRPVF_CSC2 0x00000400 +#define IC_CONF_PRPVF_CMB 0x00000800 +#define IC_CONF_PRPVF_ROT_EN 0x00001000 +#define IC_CONF_PP_EN 0x00010000 +#define IC_CONF_PP_CSC1 0x00020000 +#define IC_CONF_PP_CSC2 0x00040000 +#define IC_CONF_PP_CMB 0x00080000 +#define IC_CONF_PP_ROT_EN 0x00100000 +#define IC_CONF_IC_GLB_LOC_A 0x10000000 +#define IC_CONF_KEY_COLOR_EN 0x20000000 +#define IC_CONF_RWS_EN 0x40000000 +#define IC_CONF_CSI_MEM_WR_EN 0x80000000 + +/* SDC Registers */ +#define SDC_COM_CONF (0xB4 + IPU_BASE) +#define SDC_GW_CTRL (0xB8 + IPU_BASE) +#define SDC_FG_POS (0xBC + IPU_BASE) +#define SDC_BG_POS (0xC0 + IPU_BASE) +#define SDC_CUR_POS (0xC4 + IPU_BASE) +#define SDC_PWM_CTRL (0xC8 + IPU_BASE) +#define SDC_CUR_MAP (0xCC + IPU_BASE) +#define SDC_HOR_CONF (0xD0 + IPU_BASE) +#define SDC_VER_CONF (0xD4 + IPU_BASE) +#define SDC_SHARP_CONF_1 (0xD8 + IPU_BASE) +#define SDC_SHARP_CONF_2 (0xDC + IPU_BASE) + +/* Register bits */ +#define SDC_COM_TFT_COLOR 0x00000001UL +#define SDC_COM_FG_EN 0x00000010UL +#define SDC_COM_GWSEL 0x00000020UL +#define SDC_COM_GLB_A 0x00000040UL +#define SDC_COM_KEY_COLOR_G 0x00000080UL +#define SDC_COM_BG_EN 0x00000200UL +#define SDC_COM_SHARP 0x00001000UL + +#define SDC_V_SYNC_WIDTH_L 0x00000001UL + +/* Display Interface registers */ +#define DI_DISP_IF_CONF (0x0124 + IPU_BASE) +#define DI_DISP_SIG_POL (0x0128 + IPU_BASE) +#define DI_SER_DISP1_CONF (0x012C + IPU_BASE) +#define DI_SER_DISP2_CONF (0x0130 + IPU_BASE) +#define DI_HSP_CLK_PER (0x0134 + IPU_BASE) +#define DI_DISP0_TIME_CONF_1 (0x0138 + IPU_BASE) +#define DI_DISP0_TIME_CONF_2 (0x013C + IPU_BASE) +#define DI_DISP0_TIME_CONF_3 (0x0140 + IPU_BASE) +#define DI_DISP1_TIME_CONF_1 (0x0144 + IPU_BASE) +#define DI_DISP1_TIME_CONF_2 (0x0148 + IPU_BASE) +#define DI_DISP1_TIME_CONF_3 (0x014C + IPU_BASE) +#define DI_DISP2_TIME_CONF_1 (0x0150 + IPU_BASE) +#define DI_DISP2_TIME_CONF_2 (0x0154 + IPU_BASE) +#define DI_DISP2_TIME_CONF_3 (0x0158 + IPU_BASE) +#define DI_DISP3_TIME_CONF (0x015C + IPU_BASE) +#define DI_DISP0_DB0_MAP (0x0160 + IPU_BASE) +#define DI_DISP0_DB1_MAP (0x0164 + IPU_BASE) +#define DI_DISP0_DB2_MAP (0x0168 + IPU_BASE) +#define DI_DISP0_CB0_MAP (0x016C + IPU_BASE) +#define DI_DISP0_CB1_MAP (0x0170 + IPU_BASE) +#define DI_DISP0_CB2_MAP (0x0174 + IPU_BASE) +#define DI_DISP1_DB0_MAP (0x0178 + IPU_BASE) +#define DI_DISP1_DB1_MAP (0x017C + IPU_BASE) +#define DI_DISP1_DB2_MAP (0x0180 + IPU_BASE) +#define DI_DISP1_CB0_MAP (0x0184 + IPU_BASE) +#define DI_DISP1_CB1_MAP (0x0188 + IPU_BASE) +#define DI_DISP1_CB2_MAP (0x018C + IPU_BASE) +#define DI_DISP2_DB0_MAP (0x0190 + IPU_BASE) +#define DI_DISP2_DB1_MAP (0x0194 + IPU_BASE) +#define DI_DISP2_DB2_MAP (0x0198 + IPU_BASE) +#define DI_DISP2_CB0_MAP (0x019C + IPU_BASE) +#define DI_DISP2_CB1_MAP (0x01A0 + IPU_BASE) +#define DI_DISP2_CB2_MAP (0x01A4 + IPU_BASE) +#define DI_DISP3_B0_MAP (0x01A8 + IPU_BASE) +#define DI_DISP3_B1_MAP (0x01AC + IPU_BASE) +#define DI_DISP3_B2_MAP (0x01B0 + IPU_BASE) +#define DI_DISP_ACC_CC (0x01B4 + IPU_BASE) +#define DI_DISP_LLA_CONF (0x01B8 + IPU_BASE) +#define DI_DISP_LLA_DATA (0x01BC + IPU_BASE) + +/* DI_DISP_SIG_POL bits */ +#define DI_D3_VSYNC_POL (1 << 28) +#define DI_D3_HSYNC_POL (1 << 27) +#define DI_D3_DRDY_SHARP_POL (1 << 26) +#define DI_D3_CLK_POL (1 << 25) +#define DI_D3_DATA_POL (1 << 24) + +/* DI_DISP_IF_CONF bits */ +#define DI_D3_CLK_IDLE (1 << 26) +#define DI_D3_CLK_SEL (1 << 25) +#define DI_D3_DATAMSK (1 << 24) + +#define IOMUX_PADNUM_MASK 0x1ff +#define IOMUX_GPIONUM_SHIFT 9 +#define IOMUX_GPIONUM_MASK (0xff << IOMUX_GPIONUM_SHIFT) + +#define IOMUX_PIN(gpionum, padnum) ((padnum) & IOMUX_PADNUM_MASK) + +#define IOMUX_MODE_L(pin, mode) IOMUX_MODE(((pin) + 0xc) ^ 3, mode) + +struct chan_param_mem_planar { + /* Word 0 */ + u32 xv:10; + u32 yv:10; + u32 xb:12; + + u32 yb:12; + u32 res1:2; + u32 nsb:1; + u32 lnpb:6; + u32 ubo_l:11; + + u32 ubo_h:15; + u32 vbo_l:17; + + u32 vbo_h:9; + u32 res2:3; + u32 fw:12; + u32 fh_l:8; + + u32 fh_h:4; + u32 res3:28; + + /* Word 1 */ + u32 eba0; + + u32 eba1; + + u32 bpp:3; + u32 sl:14; + u32 pfs:3; + u32 bam:3; + u32 res4:2; + u32 npb:6; + u32 res5:1; + + u32 sat:2; + u32 res6:30; +} __attribute__ ((packed)); + +struct chan_param_mem_interleaved { + /* Word 0 */ + u32 xv:10; + u32 yv:10; + u32 xb:12; + + u32 yb:12; + u32 sce:1; + u32 res1:1; + u32 nsb:1; + u32 lnpb:6; + u32 sx:10; + u32 sy_l:1; + + u32 sy_h:9; + u32 ns:10; + u32 sm:10; + u32 sdx_l:3; + + u32 sdx_h:2; + u32 sdy:5; + u32 sdrx:1; + u32 sdry:1; + u32 sdr1:1; + u32 res2:2; + u32 fw:12; + u32 fh_l:8; + + u32 fh_h:4; + u32 res3:28; + + /* Word 1 */ + u32 eba0; + + u32 eba1; + + u32 bpp:3; + u32 sl:14; + u32 pfs:3; + u32 bam:3; + u32 res4:2; + u32 npb:6; + u32 res5:1; + + u32 sat:2; + u32 scc:1; + u32 ofs0:5; + u32 ofs1:5; + u32 ofs2:5; + u32 ofs3:5; + u32 wid0:3; + u32 wid1:3; + u32 wid2:3; + + u32 wid3:3; + u32 dec_sel:1; + u32 res6:28; +} __attribute__ ((packed)); + +union chan_param_mem { + struct chan_param_mem_planar pp; + struct chan_param_mem_interleaved ip; +}; + +DECLARE_GLOBAL_DATA_PTR; + +/* graphics setup */ +static GraphicDevice panel; +static struct ctfb_res_modes *mode; +static struct ctfb_res_modes var_mode; + +/* + * sdc_init_panel() - initialize a synchronous LCD panel. + * @width: width of panel in pixels. + * @height: height of panel in pixels. + * @di_setup: pixel format of the frame buffer + * @di_panel: either SHARP or normal TFT + * @return: 0 on success or negative error code on failure. + */ +static int sdc_init_panel(u16 width, u16 height, + enum pixel_fmt di_setup, enum ipu_panel di_panel) +{ + u32 reg, div; + uint32_t old_conf; + int clock; + + debug("%s(width=%d, height=%d)\n", __func__, width, height); + + /* Init clocking, the IPU receives its clock from the hsp divder */ + clock = mxc_get_clock(MXC_IPU_CLK); + if (clock < 0) + return -EACCES; + + /* Init panel size and blanking periods */ + reg = width + mode->left_margin + mode->right_margin - 1; + if (reg > 1023) { + printf("mx3fb: Display width too large, coerced to 1023!"); + reg = 1023; + } + reg = ((mode->hsync_len - 1) << 26) | (reg << 16); + writel(reg, SDC_HOR_CONF); + + reg = height + mode->upper_margin + mode->lower_margin - 1; + if (reg > 1023) { + printf("mx3fb: Display height too large, coerced to 1023!"); + reg = 1023; + } + reg = ((mode->vsync_len - 1) << 26) | SDC_V_SYNC_WIDTH_L | (reg << 16); + writel(reg, SDC_VER_CONF); + + switch (di_panel) { + case IPU_PANEL_SHARP_TFT: + writel(0x00FD0102L, SDC_SHARP_CONF_1); + writel(0x00F500F4L, SDC_SHARP_CONF_2); + writel(SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF); + /* TODO: probably IF_CONF must be adapted (see below)! */ + break; + case IPU_PANEL_TFT: + writel(SDC_COM_TFT_COLOR, SDC_COM_CONF); + break; + default: + return -EINVAL; + } + + /* + * Calculate divider: The fractional part is 4 bits so simply + * multiple by 2^4 to get it. + * + * Opposed to the kernel driver mode->pixclock is the time of one + * pixel in pico seconds, so: + * pixel_clk = 1e12 / mode->pixclock + * div = ipu_clk * 16 / pixel_clk + * leads to: + * div = ipu_clk * 16 / (1e12 / mode->pixclock) + * or: + * div = ipu_clk * 16 * mode->pixclock / 1e12 + * + * To avoid integer overflows this is split into 2 shifts and + * one divide with sufficient accuracy: + * 16*1024*128*476837 = 0.9999996682e12 + */ + div = ((clock/1024) * (mode->pixclock/128)) / 476837; + debug("hsp_clk is %d, div=%d\n", clock, div); + /* coerce to not less than 4.0, not more than 255.9375 */ + if (div < 0x40) + div = 0x40; + else if (div > 0xFFF) + div = 0xFFF; + /* DISP3_IF_CLK_DOWN_WR is half the divider value and 2 less + * fraction bits. Subtract 1 extra from DISP3_IF_CLK_DOWN_WR + * based on timing debug DISP3_IF_CLK_UP_WR is 0 + */ + writel((((div / 8) - 1) << 22) | div, DI_DISP3_TIME_CONF); + + /* DI settings for display 3: clock idle (bit 26) during vsync */ + old_conf = readl(DI_DISP_IF_CONF) & 0x78FFFFFF; + writel(old_conf | IF_CONF, DI_DISP_IF_CONF); + + /* only set display 3 polarity bits */ + old_conf = readl(DI_DISP_SIG_POL) & 0xE0FFFFFF; + writel(old_conf | mode->sync, DI_DISP_SIG_POL); + + writel(fmt_cfg[di_setup].b0, DI_DISP3_B0_MAP); + writel(fmt_cfg[di_setup].b1, DI_DISP3_B1_MAP); + writel(fmt_cfg[di_setup].b2, DI_DISP3_B2_MAP); + writel(readl(DI_DISP_ACC_CC) | + ((fmt_cfg[di_setup].acc - 1) << 12), DI_DISP_ACC_CC); + + debug("DI_DISP_IF_CONF = 0x%08X\n", readl(DI_DISP_IF_CONF)); + debug("DI_DISP_SIG_POL = 0x%08X\n", readl(DI_DISP_SIG_POL)); + debug("DI_DISP3_TIME_CONF = 0x%08X\n", readl(DI_DISP3_TIME_CONF)); + debug("SDC_HOR_CONF = 0x%08X\n", readl(SDC_HOR_CONF)); + debug("SDC_VER_CONF = 0x%08X\n", readl(SDC_VER_CONF)); + + return 0; +} + +static void ipu_ch_param_set_size(union chan_param_mem *params, + uint pixelfmt, uint16_t width, + uint16_t height, uint16_t stride) +{ + debug("%s(pixelfmt=%d, width=%d, height=%d, stride=%d)\n", + __func__, pixelfmt, width, height, stride); + + params->pp.fw = width - 1; + params->pp.fh_l = height - 1; + params->pp.fh_h = (height - 1) >> 8; + params->pp.sl = stride - 1; + + /* See above, for further formats see the Linux driver */ + switch (pixelfmt) { + case GDF_16BIT_565RGB: + params->ip.bpp = 2; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 0; /* Red bit offset */ + params->ip.ofs1 = 5; /* Green bit offset */ + params->ip.ofs2 = 11; /* Blue bit offset */ + params->ip.ofs3 = 16; /* Alpha bit offset */ + params->ip.wid0 = 4; /* Red bit width - 1 */ + params->ip.wid1 = 5; /* Green bit width - 1 */ + params->ip.wid2 = 4; /* Blue bit width - 1 */ + break; + case GDF_32BIT_X888RGB: + params->ip.bpp = 1; /* 24 BPP & RGB PFS */ + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 16; /* Red bit offset */ + params->ip.ofs1 = 8; /* Green bit offset */ + params->ip.ofs2 = 0; /* Blue bit offset */ + params->ip.ofs3 = 24; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + break; + default: + printf("mx3fb: Pixel format not supported!\n"); + break; + } + + params->pp.nsb = 1; +} + +static void ipu_ch_param_set_buffer(union chan_param_mem *params, + void *buf0, void *buf1) +{ + params->pp.eba0 = (u32)buf0; + params->pp.eba1 = (u32)buf1; +} + +static void ipu_write_param_mem(uint32_t addr, uint32_t *data, + uint32_t num_words) +{ + for (; num_words > 0; num_words--) { + writel(addr, IPU_IMA_ADDR); + writel(*data++, IPU_IMA_DATA); + addr++; + if ((addr & 0x7) == 5) { + addr &= ~0x7; /* set to word 0 */ + addr += 8; /* increment to next row */ + } + } +} + +static uint32_t dma_param_addr(enum ipu_channel channel) +{ + /* Channel Parameter Memory */ + return 0x10000 | (channel << 4); +} + +static void ipu_init_channel_buffer(enum ipu_channel channel, void *fbmem) +{ + union chan_param_mem params = {}; + uint32_t reg; + uint32_t stride_bytes; + + stride_bytes = (panel.plnSizeX * panel.gdfBytesPP + 3) & ~3; + + debug("%s(channel=%d, fbmem=%p)\n", __func__, channel, fbmem); + + /* Build parameter memory data for DMA channel */ + ipu_ch_param_set_size(¶ms, panel.gdfIndex, + panel.plnSizeX, panel.plnSizeY, stride_bytes); + ipu_ch_param_set_buffer(¶ms, fbmem, NULL); + params.pp.bam = 0; + /* Some channels (rotation) have restriction on burst length */ + + switch (channel) { + case IDMAC_SDC_0: + /* In original code only IPU_PIX_FMT_RGB565 was setting burst */ + params.pp.npb = 16 - 1; + break; + default: + break; + } + + ipu_write_param_mem(dma_param_addr(channel), (uint32_t *)¶ms, 10); + + /* Disable double-buffering */ + reg = readl(IPU_CHA_DB_MODE_SEL); + reg &= ~(1UL << channel); + writel(reg, IPU_CHA_DB_MODE_SEL); +} + +static void ipu_channel_set_priority(enum ipu_channel channel, + int prio) +{ + u32 reg = readl(IDMAC_CHA_PRI); + + if (prio) + reg |= 1UL << channel; + else + reg &= ~(1UL << channel); + + writel(reg, IDMAC_CHA_PRI); +} + +/* + * ipu_enable_channel() - enable an IPU channel. + * @channel: channel ID. + * @return: 0 on success or negative error code on failure. + */ +static int ipu_enable_channel(enum ipu_channel channel) +{ + uint32_t reg; + + /* Reset to buffer 0 */ + writel(1UL << channel, IPU_CHA_CUR_BUF); + + switch (channel) { + case IDMAC_SDC_0: + ipu_channel_set_priority(channel, 1); + break; + default: + break; + } + + reg = readl(IDMAC_CHA_EN); + writel(reg | (1UL << channel), IDMAC_CHA_EN); + + return 0; +} + +static int ipu_update_channel_buffer(enum ipu_channel channel, void *buf) +{ + uint32_t reg; + + reg = readl(IPU_CHA_BUF0_RDY); + if (reg & (1UL << channel)) + return -EACCES; + + /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 0) */ + writel(dma_param_addr(channel) + 0x0008UL, IPU_IMA_ADDR); + writel((u32)buf, IPU_IMA_DATA); + + return 0; +} + +static int idmac_tx_submit(enum ipu_channel channel, void *buf) +{ + int ret; + + ipu_init_channel_buffer(channel, buf); + + + /* ipu_idmac.c::ipu_submit_channel_buffers() */ + ret = ipu_update_channel_buffer(channel, buf); + if (ret < 0) + return ret; + + /* ipu_idmac.c::ipu_select_buffer() */ + /* Mark buffer 0 as ready. */ + writel(1UL << channel, IPU_CHA_BUF0_RDY); + + + ret = ipu_enable_channel(channel); + return ret; +} + +static void sdc_enable_channel(void *fbmem) +{ + int ret; + u32 reg; + + ret = idmac_tx_submit(IDMAC_SDC_0, fbmem); + + /* mx3fb.c::sdc_fb_init() */ + if (ret >= 0) { + reg = readl(SDC_COM_CONF); + writel(reg | SDC_COM_BG_EN, SDC_COM_CONF); + } + + /* + * Attention! Without this msleep the channel keeps generating + * interrupts. Next sdc_set_brightness() is going to be called + * from mx3fb_blank(). + */ + udelay(2000); +} + +/* + * mx3fb_set_par() - set framebuffer parameters and change the operating mode. + * @return: 0 on success or negative error code on failure. + * TODO: currently only 666 and TFT as DI setup supported + */ +static int mx3fb_set_par(void) +{ + int ret; + + ret = sdc_init_panel(panel.plnSizeX, panel.plnSizeY, + IPU_PIX_FMT_RGB666, IPU_PANEL_TFT); + if (ret < 0) + return ret; + + writel((mode->left_margin << 16) | mode->upper_margin, SDC_BG_POS); + + return 0; +} + +static void ll_disp3_enable(void *base) +{ + u32 reg; + + debug("%s(base=0x%x)\n", __func__, (u32) base); + /* pcm037.c::mxc_board_init() */ + + /* Display Interface #3 */ + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD0, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD1, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD2, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD3, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD4, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD5, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD6, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD7, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD8, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD9, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD10, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD11, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD12, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD13, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD14, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD15, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD16, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD17, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_VSYNC3, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_HSYNC, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_FPSHIFT, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_DRDY0, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_D3_REV, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_CONTRAST, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_D3_SPL, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_D3_CLS, MUX_CTL_FUNC)); + + + /* ipu_idmac.c::ipu_probe() */ + + /* Start the clock */ + __REG(CCM_CGR1) = __REG(CCM_CGR1) | (3 << 22); + + + /* ipu_idmac.c::ipu_idmac_init() */ + + /* Service request counter to maximum - shouldn't be needed */ + writel(0x00000070, IDMAC_CONF); + + + /* ipu_idmac.c::ipu_init_channel() */ + + /* Enable IPU sub modules */ + reg = readl(IPU_CONF) | IPU_CONF_SDC_EN | IPU_CONF_DI_EN; + writel(reg, IPU_CONF); + + + /* mx3fb.c::init_fb_chan() */ + + /* set Display Interface clock period */ + writel(0x00100010L, DI_HSP_CLK_PER); + /* Might need to trigger HSP clock change - see 44.3.3.8.5 */ + + + /* mx3fb.c::sdc_set_brightness() */ + + /* This might be board-specific */ + writel(0x03000000UL | 255 << 16, SDC_PWM_CTRL); + + + /* mx3fb.c::sdc_set_global_alpha() */ + + /* Use global - not per-pixel - Alpha-blending */ + reg = readl(SDC_GW_CTRL) & 0x00FFFFFFL; + writel(reg | ((uint32_t) 0xff << 24), SDC_GW_CTRL); + + reg = readl(SDC_COM_CONF); + writel(reg | SDC_COM_GLB_A, SDC_COM_CONF); + + + /* mx3fb.c::sdc_set_color_key() */ + + /* Disable colour-keying for background */ + reg = readl(SDC_COM_CONF) & + ~(SDC_COM_GWSEL | SDC_COM_KEY_COLOR_G); + writel(reg, SDC_COM_CONF); + + + mx3fb_set_par(); + + sdc_enable_channel(base); + + /* + * Linux driver calls sdc_set_brightness() here again, + * once is enough for us + */ + debug("%s() done\n", __func__); +} + +/* ------------------------ public part ------------------- */ +ulong calc_fbsize(void) +{ + return panel.plnSizeX * panel.plnSizeY * panel.gdfBytesPP; +} + +/* + * The current implementation is only tested for GDF_16BIT_565RGB! + * It was switched from the original CONFIG_LCD setup to CONFIG_VIDEO, + * because the lcd code seemed loaded with color table stuff, that + * does not relate to most modern TFTs. cfb_console.c looks more + * straight forward. + * This is the environment setting for the original setup + * "unknown=video=ctfb:x:240,y:320,depth:16,mode:0,pclk:185925,le:9,ri:17, + * up:7,lo:10,hs:1,vs:1,sync:100663296,vmode:0" + * "videomode=unknown" + * + * Settings for VBEST VGG322403 display: + * "videomode=video=ctfb:x:320,y:240,depth:16,mode:0,pclk:156000, + * "le:20,ri:68,up:7,lo:29,hs:30,vs:3,sync:100663296,vmode:0" + * + * Settings for COM57H5M10XRC display: + * "videomode=video=ctfb:x:640,y:480,depth:16,mode:0,pclk:40000, + * "le:120,ri:40,up:35,lo:10,hs:30,vs:3,sync:100663296,vmode:0" + */ +void *video_hw_init(void) +{ + char *penv; + u32 memsize; + unsigned long t1, hsynch, vsynch; + int bits_per_pixel, i, tmp, videomode; + + tmp = 0; + + puts("Video: "); + + videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE; + /* get video mode via environment */ + penv = getenv("videomode"); + if (penv) { + /* decide if it is a string */ + if (penv[0] <= '9') { + videomode = (int) simple_strtoul(penv, NULL, 16); + tmp = 1; + } + } else { + tmp = 1; + } + if (tmp) { + /* parameter are vesa modes */ + /* search params */ + for (i = 0; i < VESA_MODES_COUNT; i++) { + if (vesa_modes[i].vesanr == videomode) + break; + } + if (i == VESA_MODES_COUNT) { + printf("No VESA Mode found, switching to mode 0x%x ", + CONFIG_SYS_DEFAULT_VIDEO_MODE); + i = 0; + } + mode = (struct ctfb_res_modes *) + &res_mode_init[vesa_modes[i].resindex]; + bits_per_pixel = vesa_modes[i].bits_per_pixel; + } else { + mode = (struct ctfb_res_modes *) &var_mode; + bits_per_pixel = video_get_params(mode, penv); + } + + /* calculate hsynch and vsynch freq (info only) */ + t1 = (mode->left_margin + mode->xres + + mode->right_margin + mode->hsync_len) / 8; + t1 *= 8; + t1 *= mode->pixclock; + t1 /= 1000; + hsynch = 1000000000L / t1; + t1 *= (mode->upper_margin + mode->yres + + mode->lower_margin + mode->vsync_len); + t1 /= 1000; + vsynch = 1000000000L / t1; + + /* fill in Graphic device struct */ + sprintf(panel.modeIdent, "%dx%dx%d %ldkHz %ldHz", + mode->xres, mode->yres, + bits_per_pixel, (hsynch / 1000), (vsynch / 1000)); + printf("%s\n", panel.modeIdent); + panel.winSizeX = mode->xres; + panel.winSizeY = mode->yres; + panel.plnSizeX = mode->xres; + panel.plnSizeY = mode->yres; + + switch (bits_per_pixel) { + case 24: + panel.gdfBytesPP = 4; + panel.gdfIndex = GDF_32BIT_X888RGB; + break; + case 16: + panel.gdfBytesPP = 2; + panel.gdfIndex = GDF_16BIT_565RGB; + break; + default: + panel.gdfBytesPP = 1; + panel.gdfIndex = GDF__8BIT_INDEX; + break; + } + + /* set up Hardware */ + memsize = calc_fbsize(); + + debug("%s() allocating %d bytes\n", __func__, memsize); + + /* fill in missing Graphic device struct */ + panel.frameAdrs = (u32) malloc(memsize); + if (panel.frameAdrs == 0) { + printf("%s() malloc(%d) failed\n", __func__, memsize); + return 0; + } + panel.memSize = memsize; + + ll_disp3_enable((void *) panel.frameAdrs); + memset((void *) panel.frameAdrs, 0, memsize); + + debug("%s() done, framebuffer at 0x%x, size=%d cleared\n", + __func__, panel.frameAdrs, memsize); + + return (void *) &panel; +} + +void video_set_lut(unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + return; +} diff --git a/qemu/roms/u-boot/drivers/video/mxc_ipuv3_fb.c b/qemu/roms/u-boot/drivers/video/mxc_ipuv3_fb.c new file mode 100644 index 000000000..f75d77064 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/mxc_ipuv3_fb.c @@ -0,0 +1,621 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * MX51 Linux framebuffer: + * + * (C) Copyright 2004-2010 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "videomodes.h" +#include "ipu.h" +#include "mxcfb.h" +#include "ipu_regs.h" + +DECLARE_GLOBAL_DATA_PTR; + +static int mxcfb_map_video_memory(struct fb_info *fbi); +static int mxcfb_unmap_video_memory(struct fb_info *fbi); + +/* graphics setup */ +static GraphicDevice panel; +static struct fb_videomode const *gmode; +static uint8_t gdisp; +static uint32_t gpixfmt; + +void fb_videomode_to_var(struct fb_var_screeninfo *var, + const struct fb_videomode *mode) +{ + var->xres = mode->xres; + var->yres = mode->yres; + var->xres_virtual = mode->xres; + var->yres_virtual = mode->yres; + var->xoffset = 0; + var->yoffset = 0; + var->pixclock = mode->pixclock; + var->left_margin = mode->left_margin; + var->right_margin = mode->right_margin; + var->upper_margin = mode->upper_margin; + var->lower_margin = mode->lower_margin; + var->hsync_len = mode->hsync_len; + var->vsync_len = mode->vsync_len; + var->sync = mode->sync; + var->vmode = mode->vmode & FB_VMODE_MASK; +} + +/* + * Structure containing the MXC specific framebuffer information. + */ +struct mxcfb_info { + int blank; + ipu_channel_t ipu_ch; + int ipu_di; + u32 ipu_di_pix_fmt; + unsigned char overlay; + unsigned char alpha_chan_en; + dma_addr_t alpha_phy_addr0; + dma_addr_t alpha_phy_addr1; + void *alpha_virt_addr0; + void *alpha_virt_addr1; + uint32_t alpha_mem_len; + uint32_t cur_ipu_buf; + uint32_t cur_ipu_alpha_buf; + + u32 pseudo_palette[16]; +}; + +enum { + BOTH_ON, + SRC_ON, + TGT_ON, + BOTH_OFF +}; + +static unsigned long default_bpp = 16; +static unsigned char g_dp_in_use; +static struct fb_info *mxcfb_info[3]; +static int ext_clk_used; + +static uint32_t bpp_to_pixfmt(struct fb_info *fbi) +{ + uint32_t pixfmt = 0; + + debug("bpp_to_pixfmt: %d\n", fbi->var.bits_per_pixel); + + if (fbi->var.nonstd) + return fbi->var.nonstd; + + switch (fbi->var.bits_per_pixel) { + case 24: + pixfmt = IPU_PIX_FMT_BGR24; + break; + case 32: + pixfmt = IPU_PIX_FMT_BGR32; + break; + case 16: + pixfmt = IPU_PIX_FMT_RGB565; + break; + } + return pixfmt; +} + +/* + * Set fixed framebuffer parameters based on variable settings. + * + * @param info framebuffer information pointer + */ +static int mxcfb_set_fix(struct fb_info *info) +{ + struct fb_fix_screeninfo *fix = &info->fix; + struct fb_var_screeninfo *var = &info->var; + + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + + fix->type = FB_TYPE_PACKED_PIXELS; + fix->accel = FB_ACCEL_NONE; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 1; + fix->ypanstep = 1; + + return 0; +} + +static int setup_disp_channel1(struct fb_info *fbi) +{ + ipu_channel_params_t params; + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + + memset(¶ms, 0, sizeof(params)); + params.mem_dp_bg_sync.di = mxc_fbi->ipu_di; + + debug("%s called\n", __func__); + /* + * Assuming interlaced means yuv output, below setting also + * valid for mem_dc_sync. FG should have the same vmode as BG. + */ + if (fbi->var.vmode & FB_VMODE_INTERLACED) { + params.mem_dp_bg_sync.interlaced = 1; + params.mem_dp_bg_sync.out_pixel_fmt = + IPU_PIX_FMT_YUV444; + } else { + if (mxc_fbi->ipu_di_pix_fmt) { + params.mem_dp_bg_sync.out_pixel_fmt = + mxc_fbi->ipu_di_pix_fmt; + } else { + params.mem_dp_bg_sync.out_pixel_fmt = + IPU_PIX_FMT_RGB666; + } + } + params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi); + if (mxc_fbi->alpha_chan_en) + params.mem_dp_bg_sync.alpha_chan_en = 1; + + ipu_init_channel(mxc_fbi->ipu_ch, ¶ms); + + return 0; +} + +static int setup_disp_channel2(struct fb_info *fbi) +{ + int retval = 0; + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + + mxc_fbi->cur_ipu_buf = 1; + if (mxc_fbi->alpha_chan_en) + mxc_fbi->cur_ipu_alpha_buf = 1; + + fbi->var.xoffset = fbi->var.yoffset = 0; + + debug("%s: %x %d %d %d %lx %lx\n", + __func__, + mxc_fbi->ipu_ch, + fbi->var.xres, + fbi->var.yres, + fbi->fix.line_length, + fbi->fix.smem_start, + fbi->fix.smem_start + + (fbi->fix.line_length * fbi->var.yres)); + + retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, + bpp_to_pixfmt(fbi), + fbi->var.xres, fbi->var.yres, + fbi->fix.line_length, + fbi->fix.smem_start + + (fbi->fix.line_length * fbi->var.yres), + fbi->fix.smem_start, + 0, 0); + if (retval) + printf("ipu_init_channel_buffer error %d\n", retval); + + return retval; +} + +/* + * Set framebuffer parameters and change the operating mode. + * + * @param info framebuffer information pointer + */ +static int mxcfb_set_par(struct fb_info *fbi) +{ + int retval = 0; + u32 mem_len; + ipu_di_signal_cfg_t sig_cfg; + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + uint32_t out_pixel_fmt; + + ipu_disable_channel(mxc_fbi->ipu_ch); + ipu_uninit_channel(mxc_fbi->ipu_ch); + mxcfb_set_fix(fbi); + + mem_len = fbi->var.yres_virtual * fbi->fix.line_length; + if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { + if (fbi->fix.smem_start) + mxcfb_unmap_video_memory(fbi); + + if (mxcfb_map_video_memory(fbi) < 0) + return -ENOMEM; + } + + setup_disp_channel1(fbi); + + memset(&sig_cfg, 0, sizeof(sig_cfg)); + if (fbi->var.vmode & FB_VMODE_INTERLACED) { + sig_cfg.interlaced = 1; + out_pixel_fmt = IPU_PIX_FMT_YUV444; + } else { + if (mxc_fbi->ipu_di_pix_fmt) + out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt; + else + out_pixel_fmt = IPU_PIX_FMT_RGB666; + } + if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */ + sig_cfg.odd_field_first = 1; + if ((fbi->var.sync & FB_SYNC_EXT) || ext_clk_used) + sig_cfg.ext_clk = 1; + if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) + sig_cfg.Hsync_pol = 1; + if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) + sig_cfg.Vsync_pol = 1; + if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL)) + sig_cfg.clk_pol = 1; + if (fbi->var.sync & FB_SYNC_DATA_INVERT) + sig_cfg.data_pol = 1; + if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT)) + sig_cfg.enable_pol = 1; + if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) + sig_cfg.clkidle_en = 1; + + debug("pixclock = %ul Hz\n", + (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); + + if (ipu_init_sync_panel(mxc_fbi->ipu_di, + (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, + fbi->var.xres, fbi->var.yres, + out_pixel_fmt, + fbi->var.left_margin, + fbi->var.hsync_len, + fbi->var.right_margin, + fbi->var.upper_margin, + fbi->var.vsync_len, + fbi->var.lower_margin, + 0, sig_cfg) != 0) { + puts("mxcfb: Error initializing panel.\n"); + return -EINVAL; + } + + retval = setup_disp_channel2(fbi); + if (retval) + return retval; + + if (mxc_fbi->blank == FB_BLANK_UNBLANK) + ipu_enable_channel(mxc_fbi->ipu_ch); + + return retval; +} + +/* + * Check framebuffer variable parameters and adjust to valid values. + * + * @param var framebuffer variable parameters + * + * @param info framebuffer information pointer + */ +static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + u32 vtotal; + u32 htotal; + + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + + if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && + (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8)) + var->bits_per_pixel = default_bpp; + + switch (var->bits_per_pixel) { + case 8: + var->red.length = 3; + var->red.offset = 5; + var->red.msb_right = 0; + + var->green.length = 3; + var->green.offset = 2; + var->green.msb_right = 0; + + var->blue.length = 2; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 16: + var->red.length = 5; + var->red.offset = 11; + var->red.msb_right = 0; + + var->green.length = 6; + var->green.offset = 5; + var->green.msb_right = 0; + + var->blue.length = 5; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 24: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 32: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 8; + var->transp.offset = 24; + var->transp.msb_right = 0; + break; + } + + if (var->pixclock < 1000) { + htotal = var->xres + var->right_margin + var->hsync_len + + var->left_margin; + vtotal = var->yres + var->lower_margin + var->vsync_len + + var->upper_margin; + var->pixclock = (vtotal * htotal * 6UL) / 100UL; + var->pixclock = KHZ2PICOS(var->pixclock); + printf("pixclock set for 60Hz refresh = %u ps\n", + var->pixclock); + } + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + return 0; +} + +static int mxcfb_map_video_memory(struct fb_info *fbi) +{ + if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) { + fbi->fix.smem_len = fbi->var.yres_virtual * + fbi->fix.line_length; + } + fbi->fix.smem_len = roundup(fbi->fix.smem_len, ARCH_DMA_MINALIGN); + fbi->screen_base = (char *)memalign(ARCH_DMA_MINALIGN, + fbi->fix.smem_len); + fbi->fix.smem_start = (unsigned long)fbi->screen_base; + if (fbi->screen_base == 0) { + puts("Unable to allocate framebuffer memory\n"); + fbi->fix.smem_len = 0; + fbi->fix.smem_start = 0; + return -EBUSY; + } + + debug("allocated fb @ paddr=0x%08X, size=%d.\n", + (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len); + + fbi->screen_size = fbi->fix.smem_len; + + gd->fb_base = fbi->fix.smem_start; + + /* Clear the screen */ + memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); + + return 0; +} + +static int mxcfb_unmap_video_memory(struct fb_info *fbi) +{ + fbi->screen_base = 0; + fbi->fix.smem_start = 0; + fbi->fix.smem_len = 0; + return 0; +} + +/* + * Initializes the framebuffer information pointer. After allocating + * sufficient memory for the framebuffer structure, the fields are + * filled with custom information passed in from the configurable + * structures. This includes information such as bits per pixel, + * color maps, screen width/height and RGBA offsets. + * + * @return Framebuffer structure initialized with our information + */ +static struct fb_info *mxcfb_init_fbinfo(void) +{ +#define BYTES_PER_LONG 4 +#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) + struct fb_info *fbi; + struct mxcfb_info *mxcfbi; + char *p; + int size = sizeof(struct mxcfb_info) + PADDING + + sizeof(struct fb_info); + + debug("%s: %d %d %d %d\n", + __func__, + PADDING, + size, + sizeof(struct mxcfb_info), + sizeof(struct fb_info)); + /* + * Allocate sufficient memory for the fb structure + */ + + p = malloc(size); + if (!p) + return NULL; + + memset(p, 0, size); + + fbi = (struct fb_info *)p; + fbi->par = p + sizeof(struct fb_info) + PADDING; + + mxcfbi = (struct mxcfb_info *)fbi->par; + debug("Framebuffer structures at: fbi=0x%x mxcfbi=0x%x\n", + (unsigned int)fbi, (unsigned int)mxcfbi); + + fbi->var.activate = FB_ACTIVATE_NOW; + + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = mxcfbi->pseudo_palette; + + return fbi; +} + +/* + * Probe routine for the framebuffer driver. It is called during the + * driver binding process. The following functions are performed in + * this routine: Framebuffer initialization, Memory allocation and + * mapping, Framebuffer registration, IPU initialization. + * + * @return Appropriate error code to the kernel common code + */ +static int mxcfb_probe(u32 interface_pix_fmt, uint8_t disp, + struct fb_videomode const *mode) +{ + struct fb_info *fbi; + struct mxcfb_info *mxcfbi; + int ret = 0; + + /* + * Initialize FB structures + */ + fbi = mxcfb_init_fbinfo(); + if (!fbi) { + ret = -ENOMEM; + goto err0; + } + mxcfbi = (struct mxcfb_info *)fbi->par; + + if (!g_dp_in_use) { + mxcfbi->ipu_ch = MEM_BG_SYNC; + mxcfbi->blank = FB_BLANK_UNBLANK; + } else { + mxcfbi->ipu_ch = MEM_DC_SYNC; + mxcfbi->blank = FB_BLANK_POWERDOWN; + } + + mxcfbi->ipu_di = disp; + + ipu_disp_set_global_alpha(mxcfbi->ipu_ch, 1, 0x80); + ipu_disp_set_color_key(mxcfbi->ipu_ch, 0, 0); + strcpy(fbi->fix.id, "DISP3 BG"); + + g_dp_in_use = 1; + + mxcfb_info[mxcfbi->ipu_di] = fbi; + + /* Need dummy values until real panel is configured */ + + mxcfbi->ipu_di_pix_fmt = interface_pix_fmt; + fb_videomode_to_var(&fbi->var, mode); + fbi->var.bits_per_pixel = 16; + fbi->fix.line_length = fbi->var.xres * (fbi->var.bits_per_pixel / 8); + fbi->fix.smem_len = fbi->var.yres_virtual * fbi->fix.line_length; + + mxcfb_check_var(&fbi->var, fbi); + + /* Default Y virtual size is 2x panel size */ + fbi->var.yres_virtual = fbi->var.yres * 2; + + mxcfb_set_fix(fbi); + + /* alocate fb first */ + if (mxcfb_map_video_memory(fbi) < 0) + return -ENOMEM; + + mxcfb_set_par(fbi); + + panel.winSizeX = mode->xres; + panel.winSizeY = mode->yres; + panel.plnSizeX = mode->xres; + panel.plnSizeY = mode->yres; + + panel.frameAdrs = (u32)fbi->screen_base; + panel.memSize = fbi->screen_size; + + panel.gdfBytesPP = 2; + panel.gdfIndex = GDF_16BIT_565RGB; + + ipu_dump_registers(); + + return 0; + +err0: + return ret; +} + +void ipuv3_fb_shutdown(void) +{ + int i; + struct ipu_stat *stat = (struct ipu_stat *)IPU_STAT; + + for (i = 0; i < ARRAY_SIZE(mxcfb_info); i++) { + struct fb_info *fbi = mxcfb_info[i]; + if (fbi) { + struct mxcfb_info *mxc_fbi = fbi->par; + ipu_disable_channel(mxc_fbi->ipu_ch); + ipu_uninit_channel(mxc_fbi->ipu_ch); + } + } + for (i = 0; i < ARRAY_SIZE(stat->int_stat); i++) { + __raw_writel(__raw_readl(&stat->int_stat[i]), + &stat->int_stat[i]); + } +} + +void *video_hw_init(void) +{ + int ret; + + ret = ipu_probe(); + if (ret) + puts("Error initializing IPU\n"); + + ret = mxcfb_probe(gpixfmt, gdisp, gmode); + debug("Framebuffer at 0x%x\n", (unsigned int)panel.frameAdrs); + + return (void *)&panel; +} + +void video_set_lut(unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + return; +} + +int ipuv3_fb_init(struct fb_videomode const *mode, + uint8_t disp, + uint32_t pixfmt) +{ + gmode = mode; + gdisp = disp; + gpixfmt = pixfmt; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/video/mxcfb.h b/qemu/roms/u-boot/drivers/video/mxcfb.h new file mode 100644 index 000000000..77079e85e --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/mxcfb.h @@ -0,0 +1,52 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2004-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_MXCFB_H__ +#define __ASM_ARCH_MXCFB_H__ + +#define FB_SYNC_OE_LOW_ACT 0x80000000 +#define FB_SYNC_CLK_LAT_FALL 0x40000000 +#define FB_SYNC_DATA_INVERT 0x20000000 +#define FB_SYNC_CLK_IDLE_EN 0x10000000 +#define FB_SYNC_SHARP_MODE 0x08000000 +#define FB_SYNC_SWAP_RGB 0x04000000 + +struct mxcfb_gbl_alpha { + int enable; + int alpha; +}; + +struct mxcfb_loc_alpha { + int enable; + int alpha_in_pixel; + unsigned long alpha_phy_addr0; + unsigned long alpha_phy_addr1; +}; + +struct mxcfb_color_key { + int enable; + __u32 color_key; +}; + +struct mxcfb_pos { + __u16 x; + __u16 y; +}; + +struct mxcfb_gamma { + int enable; + int constk[16]; + int slopek[16]; +}; + +#endif diff --git a/qemu/roms/u-boot/drivers/video/mxsfb.c b/qemu/roms/u-boot/drivers/video/mxsfb.c new file mode 100644 index 000000000..03b0f88ac --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/mxsfb.c @@ -0,0 +1,224 @@ +/* + * Freescale i.MX23/i.MX28 LCDIF driver + * + * Copyright (C) 2011-2013 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "videomodes.h" + +#define PS2KHZ(ps) (1000000000UL / (ps)) + +static GraphicDevice panel; +struct mxs_dma_desc desc; + +/** + * mxsfb_system_setup() - Fine-tune LCDIF configuration + * + * This function is used to adjust the LCDIF configuration. This is usually + * needed when driving the controller in System-Mode to operate an 8080 or + * 6800 connected SmartLCD. + */ +__weak void mxsfb_system_setup(void) +{ +} + +/* + * DENX M28EVK: + * setenv videomode + * video=ctfb:x:800,y:480,depth:18,mode:0,pclk:30066, + * le:0,ri:256,up:0,lo:45,hs:1,vs:1,sync:100663296,vmode:0 + * + * Freescale mx23evk/mx28evk with a Seiko 4.3'' WVGA panel: + * setenv videomode + * video=ctfb:x:800,y:480,depth:24,mode:0,pclk:29851, + * le:89,ri:164,up:23,lo:10,hs:10,vs:10,sync:0,vmode:0 + */ + +static void mxs_lcd_init(GraphicDevice *panel, + struct ctfb_res_modes *mode, int bpp) +{ + struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; + uint32_t word_len = 0, bus_width = 0; + uint8_t valid_data = 0; + + /* Kick in the LCDIF clock */ + mxs_set_lcdclk(PS2KHZ(mode->pixclock)); + + /* Restart the LCDIF block */ + mxs_reset_block(®s->hw_lcdif_ctrl_reg); + + switch (bpp) { + case 24: + word_len = LCDIF_CTRL_WORD_LENGTH_24BIT; + bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_24BIT; + valid_data = 0x7; + break; + case 18: + word_len = LCDIF_CTRL_WORD_LENGTH_24BIT; + bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_18BIT; + valid_data = 0x7; + break; + case 16: + word_len = LCDIF_CTRL_WORD_LENGTH_16BIT; + bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_16BIT; + valid_data = 0xf; + break; + case 8: + word_len = LCDIF_CTRL_WORD_LENGTH_8BIT; + bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_8BIT; + valid_data = 0xf; + break; + } + + writel(bus_width | word_len | LCDIF_CTRL_DOTCLK_MODE | + LCDIF_CTRL_BYPASS_COUNT | LCDIF_CTRL_LCDIF_MASTER, + ®s->hw_lcdif_ctrl); + + writel(valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET, + ®s->hw_lcdif_ctrl1); + + mxsfb_system_setup(); + + writel((mode->yres << LCDIF_TRANSFER_COUNT_V_COUNT_OFFSET) | mode->xres, + ®s->hw_lcdif_transfer_count); + + writel(LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_ENABLE_POL | + LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT | + LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | + mode->vsync_len, ®s->hw_lcdif_vdctrl0); + writel(mode->upper_margin + mode->lower_margin + + mode->vsync_len + mode->yres, + ®s->hw_lcdif_vdctrl1); + writel((mode->hsync_len << LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH_OFFSET) | + (mode->left_margin + mode->right_margin + + mode->hsync_len + mode->xres), + ®s->hw_lcdif_vdctrl2); + writel(((mode->left_margin + mode->hsync_len) << + LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT_OFFSET) | + (mode->upper_margin + mode->vsync_len), + ®s->hw_lcdif_vdctrl3); + writel((0 << LCDIF_VDCTRL4_DOTCLK_DLY_SEL_OFFSET) | mode->xres, + ®s->hw_lcdif_vdctrl4); + + writel(panel->frameAdrs, ®s->hw_lcdif_cur_buf); + writel(panel->frameAdrs, ®s->hw_lcdif_next_buf); + + /* Flush FIFO first */ + writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_set); + +#ifndef CONFIG_VIDEO_MXS_MODE_SYSTEM + /* Sync signals ON */ + setbits_le32(®s->hw_lcdif_vdctrl4, LCDIF_VDCTRL4_SYNC_SIGNALS_ON); +#endif + + /* FIFO cleared */ + writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_clr); + + /* RUN! */ + writel(LCDIF_CTRL_RUN, ®s->hw_lcdif_ctrl_set); +} + +void *video_hw_init(void) +{ + int bpp = -1; + char *penv; + void *fb; + struct ctfb_res_modes mode; + + puts("Video: "); + + /* Suck display configuration from "videomode" variable */ + penv = getenv("videomode"); + if (!penv) { + puts("MXSFB: 'videomode' variable not set!\n"); + return NULL; + } + + bpp = video_get_params(&mode, penv); + + /* fill in Graphic device struct */ + sprintf(panel.modeIdent, "%dx%dx%d", + mode.xres, mode.yres, bpp); + + panel.winSizeX = mode.xres; + panel.winSizeY = mode.yres; + panel.plnSizeX = mode.xres; + panel.plnSizeY = mode.yres; + + switch (bpp) { + case 24: + case 18: + panel.gdfBytesPP = 4; + panel.gdfIndex = GDF_32BIT_X888RGB; + break; + case 16: + panel.gdfBytesPP = 2; + panel.gdfIndex = GDF_16BIT_565RGB; + break; + case 8: + panel.gdfBytesPP = 1; + panel.gdfIndex = GDF__8BIT_INDEX; + break; + default: + printf("MXSFB: Invalid BPP specified! (bpp = %i)\n", bpp); + return NULL; + } + + panel.memSize = mode.xres * mode.yres * panel.gdfBytesPP; + + /* Allocate framebuffer */ + fb = memalign(ARCH_DMA_MINALIGN, + roundup(panel.memSize, ARCH_DMA_MINALIGN)); + if (!fb) { + printf("MXSFB: Error allocating framebuffer!\n"); + return NULL; + } + + /* Wipe framebuffer */ + memset(fb, 0, panel.memSize); + + panel.frameAdrs = (u32)fb; + + printf("%s\n", panel.modeIdent); + + /* Start framebuffer */ + mxs_lcd_init(&panel, &mode, bpp); + +#ifdef CONFIG_VIDEO_MXS_MODE_SYSTEM + /* + * If the LCD runs in system mode, the LCD refresh has to be triggered + * manually by setting the RUN bit in HW_LCDIF_CTRL register. To avoid + * having to set this bit manually after every single change in the + * framebuffer memory, we set up specially crafted circular DMA, which + * sets the RUN bit, then waits until it gets cleared and repeats this + * infinitelly. This way, we get smooth continuous updates of the LCD. + */ + struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; + + memset(&desc, 0, sizeof(struct mxs_dma_desc)); + desc.address = (dma_addr_t)&desc; + desc.cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_WAIT4END | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + desc.cmd.pio_words[0] = readl(®s->hw_lcdif_ctrl) | LCDIF_CTRL_RUN; + desc.cmd.next = (uint32_t)&desc.cmd; + + /* Execute the DMA chain. */ + mxs_dma_circ_start(MXS_DMA_CHANNEL_AHB_APBH_LCDIF, &desc); +#endif + + return (void *)&panel; +} diff --git a/qemu/roms/u-boot/drivers/video/omap3_dss.c b/qemu/roms/u-boot/drivers/video/omap3_dss.c new file mode 100644 index 000000000..6efba122e --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/omap3_dss.c @@ -0,0 +1,167 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, + * Syed Mohammed Khasim + * + * Referred to Linux Kernel DSS driver files for OMAP3 by + * Tomi Valkeinen from drivers/video/omap2/dss/ + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation's version 2 and any + * later version the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include + +/* Configure VENC for a given Mode (NTSC / PAL) */ +void omap3_dss_venc_config(const struct venc_regs *venc_cfg, + u32 height, u32 width) +{ + struct venc_regs *venc = (struct venc_regs *) OMAP3_VENC_BASE; + struct dss_regs *dss = (struct dss_regs *) OMAP3_DSS_BASE; + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + + writel(venc_cfg->status, &venc->status); + writel(venc_cfg->f_control, &venc->f_control); + writel(venc_cfg->vidout_ctrl, &venc->vidout_ctrl); + writel(venc_cfg->sync_ctrl, &venc->sync_ctrl); + writel(venc_cfg->llen, &venc->llen); + writel(venc_cfg->flens, &venc->flens); + writel(venc_cfg->hfltr_ctrl, &venc->hfltr_ctrl); + writel(venc_cfg->cc_carr_wss_carr, &venc->cc_carr_wss_carr); + writel(venc_cfg->c_phase, &venc->c_phase); + writel(venc_cfg->gain_u, &venc->gain_u); + writel(venc_cfg->gain_v, &venc->gain_v); + writel(venc_cfg->gain_y, &venc->gain_y); + writel(venc_cfg->black_level, &venc->black_level); + writel(venc_cfg->blank_level, &venc->blank_level); + writel(venc_cfg->x_color, &venc->x_color); + writel(venc_cfg->m_control, &venc->m_control); + writel(venc_cfg->bstamp_wss_data, &venc->bstamp_wss_data); + writel(venc_cfg->s_carr, &venc->s_carr); + writel(venc_cfg->line21, &venc->line21); + writel(venc_cfg->ln_sel, &venc->ln_sel); + writel(venc_cfg->l21__wc_ctl, &venc->l21__wc_ctl); + writel(venc_cfg->htrigger_vtrigger, &venc->htrigger_vtrigger); + writel(venc_cfg->savid__eavid, &venc->savid__eavid); + writel(venc_cfg->flen__fal, &venc->flen__fal); + writel(venc_cfg->lal__phase_reset, &venc->lal__phase_reset); + writel(venc_cfg->hs_int_start_stop_x, &venc->hs_int_start_stop_x); + writel(venc_cfg->hs_ext_start_stop_x, &venc->hs_ext_start_stop_x); + writel(venc_cfg->vs_int_start_x, &venc->vs_int_start_x); + writel(venc_cfg->vs_int_stop_x__vs_int_start_y, + &venc->vs_int_stop_x__vs_int_start_y); + writel(venc_cfg->vs_int_stop_y__vs_ext_start_x, + &venc->vs_int_stop_y__vs_ext_start_x); + writel(venc_cfg->vs_ext_stop_x__vs_ext_start_y, + &venc->vs_ext_stop_x__vs_ext_start_y); + writel(venc_cfg->vs_ext_stop_y, &venc->vs_ext_stop_y); + writel(venc_cfg->avid_start_stop_x, &venc->avid_start_stop_x); + writel(venc_cfg->avid_start_stop_y, &venc->avid_start_stop_y); + writel(venc_cfg->fid_int_start_x__fid_int_start_y, + &venc->fid_int_start_x__fid_int_start_y); + writel(venc_cfg->fid_int_offset_y__fid_ext_start_x, + &venc->fid_int_offset_y__fid_ext_start_x); + writel(venc_cfg->fid_ext_start_y__fid_ext_offset_y, + &venc->fid_ext_start_y__fid_ext_offset_y); + writel(venc_cfg->tvdetgp_int_start_stop_x, + &venc->tvdetgp_int_start_stop_x); + writel(venc_cfg->tvdetgp_int_start_stop_y, + &venc->tvdetgp_int_start_stop_y); + writel(venc_cfg->gen_ctrl, &venc->gen_ctrl); + writel(venc_cfg->output_control, &venc->output_control); + writel(venc_cfg->dac_b__dac_c, &venc->dac_b__dac_c); + + /* Configure DSS for VENC Settings */ + writel(VENC_CLK_ENABLE | DAC_DEMEN | DAC_POWERDN | VENC_OUT_SEL, + &dss->control); + + /* Configure height and width for Digital out */ + writel(height << DIG_LPP_SHIFT | width, &dispc->size_dig); +} + +/* Configure Panel Specific Parameters */ +void omap3_dss_panel_config(const struct panel_config *panel_cfg) +{ + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + struct dss_regs *dss = (struct dss_regs *) OMAP3_DSS_BASE; + + writel(DSS_SOFTRESET, &dss->sysconfig); + while (!(readl(&dss->sysstatus) & DSS_RESETDONE)) + ; + + writel(panel_cfg->timing_h, &dispc->timing_h); + writel(panel_cfg->timing_v, &dispc->timing_v); + writel(panel_cfg->pol_freq, &dispc->pol_freq); + writel(panel_cfg->divisor, &dispc->divisor); + writel(panel_cfg->lcd_size, &dispc->size_lcd); + writel(panel_cfg->load_mode << LOADMODE_SHIFT, &dispc->config); + writel(panel_cfg->panel_type << TFTSTN_SHIFT | + panel_cfg->data_lines << DATALINES_SHIFT, &dispc->control); + writel(panel_cfg->panel_color, &dispc->default_color0); + writel((u32) panel_cfg->frame_buffer, &dispc->gfx_ba0); + + if (!panel_cfg->frame_buffer) + return; + + writel(panel_cfg->gfx_format | GFX_ENABLE, &dispc->gfx_attributes); + writel(1, &dispc->gfx_row_inc); + writel(1, &dispc->gfx_pixel_inc); + writel(panel_cfg->lcd_size, &dispc->gfx_size); +} + +/* Enable LCD and DIGITAL OUT in DSS */ +void omap3_dss_enable(void) +{ + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + u32 l; + + l = readl(&dispc->control); + l |= LCD_ENABLE | GO_LCD | DIG_ENABLE | GO_DIG | GP_OUT0 | GP_OUT1; + writel(l, &dispc->control); +} + +#ifdef CONFIG_CFB_CONSOLE +int __board_video_init(void) +{ + return -1; +} + +int board_video_init(void) + __attribute__((weak, alias("__board_video_init"))); + +void *video_hw_init(void) +{ + static GraphicDevice dssfb; + GraphicDevice *pGD = &dssfb; + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + + if (board_video_init() || !readl(&dispc->gfx_ba0)) + return NULL; + + pGD->winSizeX = (readl(&dispc->size_lcd) & 0x7FF) + 1; + pGD->winSizeY = ((readl(&dispc->size_lcd) >> 16) & 0x7FF) + 1; + pGD->gdfBytesPP = 4; + pGD->gdfIndex = GDF_32BIT_X888RGB; + pGD->frameAdrs = readl(&dispc->gfx_ba0); + + return pGD; +} +#endif diff --git a/qemu/roms/u-boot/drivers/video/pxa_lcd.c b/qemu/roms/u-boot/drivers/video/pxa_lcd.c new file mode 100644 index 000000000..e19f6ac7d --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/pxa_lcd.c @@ -0,0 +1,625 @@ +/* + * PXA LCD Controller + * + * (C) Copyright 2001-2002 + * Wolfgang Denk, DENX Software Engineering -- wd@denx.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/************************************************************************/ +/* ** HEADER FILES */ +/************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* #define DEBUG */ + +#ifdef CONFIG_LCD + +/*----------------------------------------------------------------------*/ +/* + * Define panel bpp, LCCR0, LCCR3 and panel_info video struct for + * your display. + */ + +#ifdef CONFIG_PXA_VGA +/* LCD outputs connected to a video DAC */ +# define LCD_BPP LCD_COLOR8 + +/* you have to set lccr0 and lccr3 (including pcd) */ +# define REG_LCCR0 0x003008f8 +# define REG_LCCR3 0x0300FF01 + +/* 640x480x16 @ 61 Hz */ +vidinfo_t panel_info = { + .vl_col = 640, + .vl_row = 480, + .vl_width = 640, + .vl_height = 480, + .vl_clkp = CONFIG_SYS_HIGH, + .vl_oep = CONFIG_SYS_HIGH, + .vl_hsp = CONFIG_SYS_HIGH, + .vl_vsp = CONFIG_SYS_HIGH, + .vl_dp = CONFIG_SYS_HIGH, + .vl_bpix = LCD_BPP, + .vl_lbw = 0, + .vl_splt = 0, + .vl_clor = 0, + .vl_tft = 1, + .vl_hpw = 40, + .vl_blw = 56, + .vl_elw = 56, + .vl_vpw = 20, + .vl_bfw = 8, + .vl_efw = 8, +}; +#endif /* CONFIG_PXA_VIDEO */ + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_SHARP_LM8V31 + +# define LCD_BPP LCD_COLOR8 +# define LCD_INVERT_COLORS /* Needed for colors to be correct, but why? */ + +/* you have to set lccr0 and lccr3 (including pcd) */ +# define REG_LCCR0 0x0030087C +# define REG_LCCR3 0x0340FF08 + +vidinfo_t panel_info = { + .vl_col = 640, + .vl_row = 480, + .vl_width = 157, + .vl_height = 118, + .vl_clkp = CONFIG_SYS_HIGH, + .vl_oep = CONFIG_SYS_HIGH, + .vl_hsp = CONFIG_SYS_HIGH, + .vl_vsp = CONFIG_SYS_HIGH, + .vl_dp = CONFIG_SYS_HIGH, + .vl_bpix = LCD_BPP, + .vl_lbw = 0, + .vl_splt = 1, + .vl_clor = 1, + .vl_tft = 0, + .vl_hpw = 1, + .vl_blw = 3, + .vl_elw = 3, + .vl_vpw = 1, + .vl_bfw = 0, + .vl_efw = 0, +}; +#endif /* CONFIG_SHARP_LM8V31 */ +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_VOIPAC_LCD + +# define LCD_BPP LCD_COLOR8 +# define LCD_INVERT_COLORS + +/* you have to set lccr0 and lccr3 (including pcd) */ +# define REG_LCCR0 0x043008f8 +# define REG_LCCR3 0x0340FF08 + +vidinfo_t panel_info = { + .vl_col = 640, + .vl_row = 480, + .vl_width = 157, + .vl_height = 118, + .vl_clkp = CONFIG_SYS_HIGH, + .vl_oep = CONFIG_SYS_HIGH, + .vl_hsp = CONFIG_SYS_HIGH, + .vl_vsp = CONFIG_SYS_HIGH, + .vl_dp = CONFIG_SYS_HIGH, + .vl_bpix = LCD_BPP, + .vl_lbw = 0, + .vl_splt = 1, + .vl_clor = 1, + .vl_tft = 1, + .vl_hpw = 32, + .vl_blw = 144, + .vl_elw = 32, + .vl_vpw = 2, + .vl_bfw = 13, + .vl_efw = 30, +}; +#endif /* CONFIG_VOIPAC_LCD */ + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_HITACHI_SX14 +/* Hitachi SX14Q004-ZZA color STN LCD */ +#define LCD_BPP LCD_COLOR8 + +/* you have to set lccr0 and lccr3 (including pcd) */ +#define REG_LCCR0 0x00301079 +#define REG_LCCR3 0x0340FF20 + +vidinfo_t panel_info = { + .vl_col = 320, + .vl_row = 240, + .vl_width = 167, + .vl_height = 109, + .vl_clkp = CONFIG_SYS_HIGH, + .vl_oep = CONFIG_SYS_HIGH, + .vl_hsp = CONFIG_SYS_HIGH, + .vl_vsp = CONFIG_SYS_HIGH, + .vl_dp = CONFIG_SYS_HIGH, + .vl_bpix = LCD_BPP, + .vl_lbw = 1, + .vl_splt = 0, + .vl_clor = 1, + .vl_tft = 0, + .vl_hpw = 1, + .vl_blw = 1, + .vl_elw = 1, + .vl_vpw = 7, + .vl_bfw = 0, + .vl_efw = 0, +}; +#endif /* CONFIG_HITACHI_SX14 */ + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_LMS283GF05 + +# define LCD_BPP LCD_COLOR8 +/*# define LCD_INVERT_COLORS*/ + +/* you have to set lccr0 and lccr3 (including pcd) */ +# define REG_LCCR0 0x043008f8 +# define REG_LCCR3 0x03b00009 + +vidinfo_t panel_info = { + .vl_col = 240, + .vl_row = 320, + .vl_width = 240, + .vl_height = 320, + .vl_clkp = CONFIG_SYS_HIGH, + .vl_oep = CONFIG_SYS_LOW, + .vl_hsp = CONFIG_SYS_LOW, + .vl_vsp = CONFIG_SYS_LOW, + .vl_dp = CONFIG_SYS_HIGH, + .vl_bpix = LCD_BPP, + .vl_lbw = 0, + .vl_splt = 1, + .vl_clor = 1, + .vl_tft = 1, + .vl_hpw = 4, + .vl_blw = 4, + .vl_elw = 8, + .vl_vpw = 4, + .vl_bfw = 4, + .vl_efw = 8, +}; +#endif /* CONFIG_LMS283GF05 */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_ACX517AKN + +# define LCD_BPP LCD_COLOR8 + +/* you have to set lccr0 and lccr3 (including pcd) */ +# define REG_LCCR0 0x003008f9 +# define REG_LCCR3 0x03700006 + +vidinfo_t panel_info = { + .vl_col = 320, + .vl_row = 320, + .vl_width = 320, + .vl_height = 320, + .vl_clkp = CONFIG_SYS_HIGH, + .vl_oep = CONFIG_SYS_LOW, + .vl_hsp = CONFIG_SYS_LOW, + .vl_vsp = CONFIG_SYS_LOW, + .vl_dp = CONFIG_SYS_HIGH, + .vl_bpix = LCD_BPP, + .vl_lbw = 0, + .vl_splt = 1, + .vl_clor = 1, + .vl_tft = 1, + .vl_hpw = 0x04, + .vl_blw = 0x1c, + .vl_elw = 0x08, + .vl_vpw = 0x01, + .vl_bfw = 0x07, + .vl_efw = 0x08, +}; +#endif /* CONFIG_ACX517AKN */ + +#ifdef CONFIG_ACX544AKN + +# define LCD_BPP LCD_COLOR16 + +/* you have to set lccr0 and lccr3 (including pcd) */ +# define REG_LCCR0 0x003008f9 +# define REG_LCCR3 0x04700007 /* 16bpp */ + +vidinfo_t panel_info = { + .vl_col = 320, + .vl_row = 320, + .vl_width = 320, + .vl_height = 320, + .vl_clkp = CONFIG_SYS_LOW, + .vl_oep = CONFIG_SYS_LOW, + .vl_hsp = CONFIG_SYS_LOW, + .vl_vsp = CONFIG_SYS_LOW, + .vl_dp = CONFIG_SYS_LOW, + .vl_bpix = LCD_BPP, + .vl_lbw = 0, + .vl_splt = 0, + .vl_clor = 1, + .vl_tft = 1, + .vl_hpw = 0x05, + .vl_blw = 0x13, + .vl_elw = 0x08, + .vl_vpw = 0x02, + .vl_bfw = 0x07, + .vl_efw = 0x05, +}; +#endif /* CONFIG_ACX544AKN */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_LQ038J7DH53 + +# define LCD_BPP LCD_COLOR8 + +/* you have to set lccr0 and lccr3 (including pcd) */ +# define REG_LCCR0 0x003008f9 +# define REG_LCCR3 0x03700004 + +vidinfo_t panel_info = { + .vl_col = 320, + .vl_row = 480, + .vl_width = 320, + .vl_height = 480, + .vl_clkp = CONFIG_SYS_HIGH, + .vl_oep = CONFIG_SYS_LOW, + .vl_hsp = CONFIG_SYS_LOW, + .vl_vsp = CONFIG_SYS_LOW, + .vl_dp = CONFIG_SYS_HIGH, + .vl_bpix = LCD_BPP, + .vl_lbw = 0, + .vl_splt = 1, + .vl_clor = 1, + .vl_tft = 1, + .vl_hpw = 0x04, + .vl_blw = 0x20, + .vl_elw = 0x01, + .vl_vpw = 0x01, + .vl_bfw = 0x04, + .vl_efw = 0x01, +}; +#endif /* CONFIG_ACX517AKN */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_LITTLETON_LCD +# define LCD_BPP LCD_COLOR8 + +/* you have to set lccr0 and lccr3 (including pcd) */ +# define REG_LCCR0 0x003008f8 +# define REG_LCCR3 0x0300FF04 + +vidinfo_t panel_info = { + .vl_col = 480, + .vl_row = 640, + .vl_width = 480, + .vl_height = 640, + .vl_clkp = CONFIG_SYS_HIGH, + .vl_oep = CONFIG_SYS_HIGH, + .vl_hsp = CONFIG_SYS_HIGH, + .vl_vsp = CONFIG_SYS_HIGH, + .vl_dp = CONFIG_SYS_HIGH, + .vl_bpix = LCD_BPP, + .vl_lbw = 0, + .vl_splt = 0, + .vl_clor = 0, + .vl_tft = 1, + .vl_hpw = 9, + .vl_blw = 8, + .vl_elw = 24, + .vl_vpw = 2, + .vl_bfw = 2, + .vl_efw = 4, +}; +#endif /* CONFIG_LITTLETON_LCD */ + +/*----------------------------------------------------------------------*/ + +static int pxafb_init_mem (void *lcdbase, vidinfo_t *vid); +static void pxafb_setup_gpio (vidinfo_t *vid); +static void pxafb_enable_controller (vidinfo_t *vid); +static int pxafb_init (vidinfo_t *vid); + +/************************************************************************/ +/* --------------- PXA chipset specific functions ------------------- */ +/************************************************************************/ + +void lcd_ctrl_init (void *lcdbase) +{ + pxafb_init_mem(lcdbase, &panel_info); + pxafb_init(&panel_info); + pxafb_setup_gpio(&panel_info); + pxafb_enable_controller(&panel_info); +} + +/*----------------------------------------------------------------------*/ +#if LCD_BPP == LCD_COLOR8 +void +lcd_setcolreg (ushort regno, ushort red, ushort green, ushort blue) +{ + struct pxafb_info *fbi = &panel_info.pxa; + unsigned short *palette = (unsigned short *)fbi->palette; + u_int val; + + if (regno < fbi->palette_size) { + val = ((red << 8) & 0xf800); + val |= ((green << 4) & 0x07e0); + val |= (blue & 0x001f); + +#ifdef LCD_INVERT_COLORS + palette[regno] = ~val; +#else + palette[regno] = val; +#endif + } + + debug ("setcolreg: reg %2d @ %p: R=%02X G=%02X B=%02X => %04X\n", + regno, &palette[regno], + red, green, blue, + palette[regno]); +} +#endif /* LCD_COLOR8 */ + +/*----------------------------------------------------------------------*/ +#if LCD_BPP == LCD_MONOCHROME +void lcd_initcolregs (void) +{ + struct pxafb_info *fbi = &panel_info.pxa; + cmap = (ushort *)fbi->palette; + ushort regno; + + for (regno = 0; regno < 16; regno++) { + cmap[regno * 2] = 0; + cmap[(regno * 2) + 1] = regno & 0x0f; + } +} +#endif /* LCD_MONOCHROME */ + +/*----------------------------------------------------------------------*/ +__weak void lcd_enable(void) +{ +} + +/************************************************************************/ +/* ** PXA255 specific routines */ +/************************************************************************/ + +/* + * Calculate fb size for VIDEOLFB_ATAG. Size returned contains fb, + * descriptors and palette areas. + */ +ulong calc_fbsize (void) +{ + ulong size; + int line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; + + size = line_length * panel_info.vl_row; + size += PAGE_SIZE; + + return size; +} + +static int pxafb_init_mem (void *lcdbase, vidinfo_t *vid) +{ + u_long palette_mem_size; + struct pxafb_info *fbi = &vid->pxa; + int fb_size = vid->vl_row * (vid->vl_col * NBITS (vid->vl_bpix)) / 8; + + fbi->screen = (u_long)lcdbase; + + fbi->palette_size = NBITS(vid->vl_bpix) == 8 ? 256 : 16; + palette_mem_size = fbi->palette_size * sizeof(u16); + + debug("palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size); + /* locate palette and descs at end of page following fb */ + fbi->palette = (u_long)lcdbase + fb_size + PAGE_SIZE - palette_mem_size; + + return 0; +} +#ifdef CONFIG_CPU_MONAHANS +static inline void pxafb_setup_gpio (vidinfo_t *vid) {} +#else +static void pxafb_setup_gpio (vidinfo_t *vid) +{ + u_long lccr0; + + /* + * setup is based on type of panel supported + */ + + lccr0 = vid->pxa.reg_lccr0; + + /* 4 bit interface */ + if ((lccr0 & LCCR0_CMS) && (lccr0 & LCCR0_SDS) && !(lccr0 & LCCR0_DPD)) + { + debug("Setting GPIO for 4 bit data\n"); + /* bits 58-61 */ + writel(readl(GPDR1) | (0xf << 26), GPDR1); + writel((readl(GAFR1_U) & ~(0xff << 20)) | (0xaa << 20), + GAFR1_U); + + /* bits 74-77 */ + writel(readl(GPDR2) | (0xf << 10), GPDR2); + writel((readl(GAFR2_L) & ~(0xff << 20)) | (0xaa << 20), + GAFR2_L); + } + + /* 8 bit interface */ + else if (((lccr0 & LCCR0_CMS) && ((lccr0 & LCCR0_SDS) || (lccr0 & LCCR0_DPD))) || + (!(lccr0 & LCCR0_CMS) && !(lccr0 & LCCR0_PAS) && !(lccr0 & LCCR0_SDS))) + { + debug("Setting GPIO for 8 bit data\n"); + /* bits 58-65 */ + writel(readl(GPDR1) | (0x3f << 26), GPDR1); + writel(readl(GPDR2) | (0x3), GPDR2); + + writel((readl(GAFR1_U) & ~(0xfff << 20)) | (0xaaa << 20), + GAFR1_U); + writel((readl(GAFR2_L) & ~0xf) | (0xa), GAFR2_L); + + /* bits 74-77 */ + writel(readl(GPDR2) | (0xf << 10), GPDR2); + writel((readl(GAFR2_L) & ~(0xff << 20)) | (0xaa << 20), + GAFR2_L); + } + + /* 16 bit interface */ + else if (!(lccr0 & LCCR0_CMS) && ((lccr0 & LCCR0_SDS) || (lccr0 & LCCR0_PAS))) + { + debug("Setting GPIO for 16 bit data\n"); + /* bits 58-77 */ + writel(readl(GPDR1) | (0x3f << 26), GPDR1); + writel(readl(GPDR2) | 0x00003fff, GPDR2); + + writel((readl(GAFR1_U) & ~(0xfff << 20)) | (0xaaa << 20), + GAFR1_U); + writel((readl(GAFR2_L) & 0xf0000000) | 0x0aaaaaaa, GAFR2_L); + } + else + { + printf("pxafb_setup_gpio: unable to determine bits per pixel\n"); + } +} +#endif + +static void pxafb_enable_controller (vidinfo_t *vid) +{ + debug("Enabling LCD controller\n"); + + /* Sequence from 11.7.10 */ + writel(vid->pxa.reg_lccr3, LCCR3); + writel(vid->pxa.reg_lccr2, LCCR2); + writel(vid->pxa.reg_lccr1, LCCR1); + writel(vid->pxa.reg_lccr0 & ~LCCR0_ENB, LCCR0); + writel(vid->pxa.fdadr0, FDADR0); + writel(vid->pxa.fdadr1, FDADR1); + writel(readl(LCCR0) | LCCR0_ENB, LCCR0); + +#ifdef CONFIG_CPU_MONAHANS + writel(readl(CKENA) | CKENA_1_LCD, CKENA); +#else + writel(readl(CKEN) | CKEN16_LCD, CKEN); +#endif + + debug("FDADR0 = 0x%08x\n", readl(FDADR0)); + debug("FDADR1 = 0x%08x\n", readl(FDADR1)); + debug("LCCR0 = 0x%08x\n", readl(LCCR0)); + debug("LCCR1 = 0x%08x\n", readl(LCCR1)); + debug("LCCR2 = 0x%08x\n", readl(LCCR2)); + debug("LCCR3 = 0x%08x\n", readl(LCCR3)); +} + +static int pxafb_init (vidinfo_t *vid) +{ + struct pxafb_info *fbi = &vid->pxa; + + debug("Configuring PXA LCD\n"); + + fbi->reg_lccr0 = REG_LCCR0; + fbi->reg_lccr3 = REG_LCCR3; + + debug("vid: vl_col=%d hslen=%d lm=%d rm=%d\n", + vid->vl_col, vid->vl_hpw, + vid->vl_blw, vid->vl_elw); + debug("vid: vl_row=%d vslen=%d um=%d bm=%d\n", + vid->vl_row, vid->vl_vpw, + vid->vl_bfw, vid->vl_efw); + + fbi->reg_lccr1 = + LCCR1_DisWdth(vid->vl_col) + + LCCR1_HorSnchWdth(vid->vl_hpw) + + LCCR1_BegLnDel(vid->vl_blw) + + LCCR1_EndLnDel(vid->vl_elw); + + fbi->reg_lccr2 = + LCCR2_DisHght(vid->vl_row) + + LCCR2_VrtSnchWdth(vid->vl_vpw) + + LCCR2_BegFrmDel(vid->vl_bfw) + + LCCR2_EndFrmDel(vid->vl_efw); + + fbi->reg_lccr3 = REG_LCCR3 & ~(LCCR3_HSP | LCCR3_VSP); + fbi->reg_lccr3 |= (vid->vl_hsp ? LCCR3_HorSnchL : LCCR3_HorSnchH) + | (vid->vl_vsp ? LCCR3_VrtSnchL : LCCR3_VrtSnchH); + + + /* setup dma descriptors */ + fbi->dmadesc_fblow = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette - 3*16); + fbi->dmadesc_fbhigh = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette - 2*16); + fbi->dmadesc_palette = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette - 1*16); + + #define BYTES_PER_PANEL ((fbi->reg_lccr0 & LCCR0_SDS) ? \ + (vid->vl_col * vid->vl_row * NBITS(vid->vl_bpix) / 8 / 2) : \ + (vid->vl_col * vid->vl_row * NBITS(vid->vl_bpix) / 8)) + + /* populate descriptors */ + fbi->dmadesc_fblow->fdadr = (u_long)fbi->dmadesc_fblow; + fbi->dmadesc_fblow->fsadr = fbi->screen + BYTES_PER_PANEL; + fbi->dmadesc_fblow->fidr = 0; + fbi->dmadesc_fblow->ldcmd = BYTES_PER_PANEL; + + fbi->fdadr1 = (u_long)fbi->dmadesc_fblow; /* only used in dual-panel mode */ + + fbi->dmadesc_fbhigh->fsadr = fbi->screen; + fbi->dmadesc_fbhigh->fidr = 0; + fbi->dmadesc_fbhigh->ldcmd = BYTES_PER_PANEL; + + fbi->dmadesc_palette->fsadr = fbi->palette; + fbi->dmadesc_palette->fidr = 0; + fbi->dmadesc_palette->ldcmd = (fbi->palette_size * 2) | LDCMD_PAL; + + if( NBITS(vid->vl_bpix) < 12) + { + /* assume any mode with <12 bpp is palette driven */ + fbi->dmadesc_palette->fdadr = (u_long)fbi->dmadesc_fbhigh; + fbi->dmadesc_fbhigh->fdadr = (u_long)fbi->dmadesc_palette; + /* flips back and forth between pal and fbhigh */ + fbi->fdadr0 = (u_long)fbi->dmadesc_palette; + } + else + { + /* palette shouldn't be loaded in true-color mode */ + fbi->dmadesc_fbhigh->fdadr = (u_long)fbi->dmadesc_fbhigh; + fbi->fdadr0 = (u_long)fbi->dmadesc_fbhigh; /* no pal just fbhigh */ + } + + debug("fbi->dmadesc_fblow = 0x%lx\n", (u_long)fbi->dmadesc_fblow); + debug("fbi->dmadesc_fbhigh = 0x%lx\n", (u_long)fbi->dmadesc_fbhigh); + debug("fbi->dmadesc_palette = 0x%lx\n", (u_long)fbi->dmadesc_palette); + + debug("fbi->dmadesc_fblow->fdadr = 0x%lx\n", fbi->dmadesc_fblow->fdadr); + debug("fbi->dmadesc_fbhigh->fdadr = 0x%lx\n", fbi->dmadesc_fbhigh->fdadr); + debug("fbi->dmadesc_palette->fdadr = 0x%lx\n", fbi->dmadesc_palette->fdadr); + + debug("fbi->dmadesc_fblow->fsadr = 0x%lx\n", fbi->dmadesc_fblow->fsadr); + debug("fbi->dmadesc_fbhigh->fsadr = 0x%lx\n", fbi->dmadesc_fbhigh->fsadr); + debug("fbi->dmadesc_palette->fsadr = 0x%lx\n", fbi->dmadesc_palette->fsadr); + + debug("fbi->dmadesc_fblow->ldcmd = 0x%lx\n", fbi->dmadesc_fblow->ldcmd); + debug("fbi->dmadesc_fbhigh->ldcmd = 0x%lx\n", fbi->dmadesc_fbhigh->ldcmd); + debug("fbi->dmadesc_palette->ldcmd = 0x%lx\n", fbi->dmadesc_palette->ldcmd); + + return 0; +} + +/************************************************************************/ +/************************************************************************/ + +#endif /* CONFIG_LCD */ diff --git a/qemu/roms/u-boot/drivers/video/s6e63d6.c b/qemu/roms/u-boot/drivers/video/s6e63d6.c new file mode 100644 index 000000000..f077260d7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/s6e63d6.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009 + * Guennadi Liakhovetski, DENX Software Engineering, + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include + +/* + * Each transfer is performed as: + * 1. chip-select active + * 2. send 8-bit start code + * 3. send 16-bit data + * 4. chip-select inactive + */ +static int send_word(struct s6e63d6 *data, u8 rs, u16 word) +{ + /* + * The start byte looks like (binary): + * 01110 + * RS is 0 for index or 1 for data, and R/W is 0 for write. + */ + u32 buf8 = 0x70 | data->id | (rs & 2); + u32 buf16 = cpu_to_le16(word); + u32 buf_in; + int err; + + err = spi_xfer(data->slave, 8, &buf8, &buf_in, SPI_XFER_BEGIN); + if (err) + return err; + + return spi_xfer(data->slave, 16, &buf16, &buf_in, SPI_XFER_END); +} + +/* Index and param differ in Register Select bit */ +int s6e63d6_index(struct s6e63d6 *data, u8 idx) +{ + return send_word(data, 0, idx); +} + +int s6e63d6_param(struct s6e63d6 *data, u16 param) +{ + return send_word(data, 2, param); +} + +int s6e63d6_init(struct s6e63d6 *data) +{ + if (data->id != 0 && data->id != 4) { + printf("s6e63d6: invalid ID %u\n", data->id); + return 1; + } + + data->slave = spi_setup_slave(data->bus, data->cs, 100000, SPI_MODE_3); + if (!data->slave) + return 1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/video/s6e8ax0.c b/qemu/roms/u-boot/drivers/video/s6e8ax0.c new file mode 100644 index 000000000..84948177e --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/s6e8ax0.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include "exynos_mipi_dsi_lowlevel.h" +#include "exynos_mipi_dsi_common.h" + +static void s6e8ax0_panel_cond(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + int reverse = dsim_dev->dsim_lcd_dev->reverse_panel; + static const unsigned char data_to_send[] = { + 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x4c, + 0x6e, 0x10, 0x27, 0x7d, 0x3f, 0x10, 0x00, 0x00, 0x20, + 0x04, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, + 0x23, 0x23, 0xc0, 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc3, + 0xff, 0xff, 0xc8 + }; + + static const unsigned char data_to_send_reverse[] = { + 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, + 0x7d, 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, + 0x04, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, + 0x23, 0x23, 0xc0, 0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, + 0xf6, 0xf6, 0xc1 + }; + + if (reverse) { + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send_reverse, + ARRAY_SIZE(data_to_send_reverse)); + } else { + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); + } +} + +static void s6e8ax0_display_cond(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xf2, 0x80, 0x03, 0x0d + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_gamma_cond(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + /* 7500K 2.2 Set : 30cd */ + static const unsigned char data_to_send[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, + 0xaf, 0xba, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, + 0xdc, 0xc0, 0x00, 0x61, 0x00, 0x5a, 0x00, 0x74, + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_gamma_update(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xf7, 0x03 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send, + ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_source_control(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xf6, 0x00, 0x02, 0x00 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_pentile_control(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, + 0x00 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_mipi_control1(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_mipi_control2(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_power_control(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_mipi_control3(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xe3, 0x40 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send, + ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_mipi_control4(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_elvss_set(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xb1, 0x04, 0x00 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_display_on(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0x29, 0x00 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_SHORT_WRITE, data_to_send, + ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_sleep_out(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0x11, 0x00 + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_SHORT_WRITE, data_to_send, + ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_apply_level1_key(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xf0, 0x5a, 0x5a + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_apply_mtp_key(struct mipi_dsim_device *dsim_dev) +{ + struct mipi_dsim_master_ops *ops = dsim_dev->master_ops; + static const unsigned char data_to_send[] = { + 0xf1, 0x5a, 0x5a + }; + + ops->cmd_write(dsim_dev, MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_panel_init(struct mipi_dsim_device *dsim_dev) +{ + /* + * in case of setting gamma and panel condition at first, + * it shuold be setting like below. + * set_gamma() -> set_panel_condition() + */ + + s6e8ax0_apply_level1_key(dsim_dev); + s6e8ax0_apply_mtp_key(dsim_dev); + + s6e8ax0_sleep_out(dsim_dev); + mdelay(5); + s6e8ax0_panel_cond(dsim_dev); + s6e8ax0_display_cond(dsim_dev); + s6e8ax0_gamma_cond(dsim_dev); + s6e8ax0_gamma_update(dsim_dev); + + s6e8ax0_etc_source_control(dsim_dev); + s6e8ax0_elvss_set(dsim_dev); + s6e8ax0_etc_pentile_control(dsim_dev); + s6e8ax0_etc_mipi_control1(dsim_dev); + s6e8ax0_etc_mipi_control2(dsim_dev); + s6e8ax0_etc_power_control(dsim_dev); + s6e8ax0_etc_mipi_control3(dsim_dev); + s6e8ax0_etc_mipi_control4(dsim_dev); +} + +static int s6e8ax0_panel_set(struct mipi_dsim_device *dsim_dev) +{ + s6e8ax0_panel_init(dsim_dev); + + return 0; +} + +static void s6e8ax0_display_enable(struct mipi_dsim_device *dsim_dev) +{ + s6e8ax0_display_on(dsim_dev); +} + +static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = { + .name = "s6e8ax0", + .id = -1, + + .mipi_panel_init = s6e8ax0_panel_set, + .mipi_display_on = s6e8ax0_display_enable, +}; + +void s6e8ax0_init(void) +{ + exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver); +} diff --git a/qemu/roms/u-boot/drivers/video/sandbox_sdl.c b/qemu/roms/u-boot/drivers/video/sandbox_sdl.c new file mode 100644 index 000000000..ba4578e9d --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/sandbox_sdl.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +enum { + /* Maximum LCD size we support */ + LCD_MAX_WIDTH = 1366, + LCD_MAX_HEIGHT = 768, + LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */ +}; + +vidinfo_t panel_info; + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +} + +void lcd_ctrl_init(void *lcdbase) +{ + /* + * Allocate memory to keep BMP color conversion map. This is required + * for 8 bit BMPs only (hence 256 colors). If malloc fails - keep + * going, it is not even clear if displyaing the bitmap will be + * required on the way up. + */ + panel_info.cmap = malloc(256 * NBITS(panel_info.vl_bpix) / 8); +} + +void lcd_enable(void) +{ + if (sandbox_sdl_init_display(panel_info.vl_col, panel_info.vl_row, + panel_info.vl_bpix)) + puts("LCD init failed\n"); +} + +int sandbox_lcd_sdl_early_init(void) +{ + const void *blob = gd->fdt_blob; + int xres = LCD_MAX_WIDTH, yres = LCD_MAX_HEIGHT; + int node; + int ret = 0; + + /* + * The code in common/lcd.c does not cope with not being able to + * set up a frame buffer. It will just happily keep writing to + * invalid memory. So here we make sure that at least some buffer + * is available even if it actually won't be displayed. + */ + node = fdtdec_next_compatible(blob, 0, COMPAT_SANDBOX_LCD_SDL); + if (node >= 0) { + xres = fdtdec_get_int(blob, node, "xres", LCD_MAX_WIDTH); + yres = fdtdec_get_int(blob, node, "yres", LCD_MAX_HEIGHT); + if (xres < 0 || xres > LCD_MAX_WIDTH) { + xres = LCD_MAX_WIDTH; + ret = -EINVAL; + } + if (yres < 0 || yres > LCD_MAX_HEIGHT) { + yres = LCD_MAX_HEIGHT; + ret = -EINVAL; + } + } + + panel_info.vl_col = xres; + panel_info.vl_row = yres; + panel_info.vl_bpix = LCD_COLOR16; + + return ret; +} diff --git a/qemu/roms/u-boot/drivers/video/scf0403_lcd.c b/qemu/roms/u-boot/drivers/video/scf0403_lcd.c new file mode 100644 index 000000000..2bc8bcae8 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/scf0403_lcd.c @@ -0,0 +1,296 @@ +/* + * scf0403.c -- support for DataImage SCF0403 LCD + * + * Copyright (c) 2013 Adapted from Linux driver: + * Copyright (c) 2012 Anders Electronics plc. All Rights Reserved. + * Copyright (c) 2012 CompuLab, Ltd + * Dmitry Lifshitz + * Ilya Ledvich + * Inspired by Alberto Panizzo & + * Marek Vasut work in l4f00242t03.c + * + * U-Boot port: Nikita Kiryanov + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +struct scf0403_cmd { + u16 cmd; + u16 *params; + int count; +}; + +struct scf0403_initseq_entry { + struct scf0403_cmd cmd; + int delay_ms; +}; + +struct scf0403_priv { + struct spi_slave *spi; + unsigned int reset_gpio; + u32 rddid; + struct scf0403_initseq_entry *init_seq; + int seq_size; +}; + +struct scf0403_priv priv; + +#define SCF0403852GGU04_ID 0x000080 + +/* SCF0403526GGU20 model commands parameters */ +static u16 extcmd_params_sn20[] = {0xff, 0x98, 0x06}; +static u16 spiinttype_params_sn20[] = {0x60}; +static u16 bc_params_sn20[] = { + 0x01, 0x10, 0x61, 0x74, 0x01, 0x01, 0x1B, + 0x12, 0x71, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x05, 0x00, 0xFF, 0xF2, 0x01, 0x00, 0x40, +}; +static u16 bd_params_sn20[] = {0x01, 0x23, 0x45, 0x67, 0x01, 0x23, 0x45, 0x67}; +static u16 be_params_sn20[] = { + 0x01, 0x22, 0x22, 0xBA, 0xDC, 0x26, 0x28, 0x22, 0x22, +}; +static u16 vcom_params_sn20[] = {0x74}; +static u16 vmesur_params_sn20[] = {0x7F, 0x0F, 0x00}; +static u16 powerctl_params_sn20[] = {0x03, 0x0b, 0x00}; +static u16 lvglvolt_params_sn20[] = {0x08}; +static u16 engsetting_params_sn20[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x20}; +static u16 dispfunc_params_sn20[] = {0xa0}; +static u16 dvddvolt_params_sn20[] = {0x74}; +static u16 dispinv_params_sn20[] = {0x00, 0x00, 0x00}; +static u16 panelres_params_sn20[] = {0x82}; +static u16 framerate_params_sn20[] = {0x00, 0x13, 0x13}; +static u16 timing_params_sn20[] = {0x80, 0x05, 0x40, 0x28}; +static u16 powerctl2_params_sn20[] = {0x17, 0x75, 0x79, 0x20}; +static u16 memaccess_params_sn20[] = {0x00}; +static u16 pixfmt_params_sn20[] = {0x66}; +static u16 pgamma_params_sn20[] = { + 0x00, 0x03, 0x0b, 0x0c, 0x0e, 0x08, 0xc5, 0x04, + 0x08, 0x0c, 0x13, 0x11, 0x11, 0x14, 0x0c, 0x10, +}; +static u16 ngamma_params_sn20[] = { + 0x00, 0x0d, 0x11, 0x0c, 0x0c, 0x04, 0x76, 0x03, + 0x08, 0x0b, 0x16, 0x10, 0x0d, 0x16, 0x0a, 0x00, +}; +static u16 tearing_params_sn20[] = {0x00}; + +/* SCF0403852GGU04 model commands parameters */ +static u16 memaccess_params_sn04[] = {0x08}; +static u16 pixfmt_params_sn04[] = {0x66}; +static u16 modectl_params_sn04[] = {0x01}; +static u16 dispfunc_params_sn04[] = {0x22, 0xe2, 0xFF, 0x04}; +static u16 vcom_params_sn04[] = {0x00, 0x6A}; +static u16 pgamma_params_sn04[] = { + 0x00, 0x07, 0x0d, 0x10, 0x13, 0x19, 0x0f, 0x0c, + 0x05, 0x08, 0x06, 0x13, 0x0f, 0x30, 0x20, 0x1f, +}; +static u16 ngamma_params_sn04[] = { + 0x1F, 0x20, 0x30, 0x0F, 0x13, 0x06, 0x08, 0x05, + 0x0C, 0x0F, 0x19, 0x13, 0x10, 0x0D, 0x07, 0x00, +}; +static u16 dispinv_params_sn04[] = {0x02}; + +/* Common commands */ +static struct scf0403_cmd scf0403_cmd_slpout = {0x11, NULL, 0}; +static struct scf0403_cmd scf0403_cmd_dison = {0x29, NULL, 0}; + +/* SCF0403852GGU04 init sequence */ +static struct scf0403_initseq_entry scf0403_initseq_sn04[] = { + {{0x36, memaccess_params_sn04, ARRAY_SIZE(memaccess_params_sn04)}, 0}, + {{0x3A, pixfmt_params_sn04, ARRAY_SIZE(pixfmt_params_sn04)}, 0}, + {{0xB6, dispfunc_params_sn04, ARRAY_SIZE(dispfunc_params_sn04)}, 0}, + {{0xC5, vcom_params_sn04, ARRAY_SIZE(vcom_params_sn04)}, 0}, + {{0xE0, pgamma_params_sn04, ARRAY_SIZE(pgamma_params_sn04)}, 0}, + {{0xE1, ngamma_params_sn04, ARRAY_SIZE(ngamma_params_sn04)}, 20}, + {{0xB0, modectl_params_sn04, ARRAY_SIZE(modectl_params_sn04)}, 0}, + {{0xB4, dispinv_params_sn04, ARRAY_SIZE(dispinv_params_sn04)}, 100}, +}; + +/* SCF0403526GGU20 init sequence */ +static struct scf0403_initseq_entry scf0403_initseq_sn20[] = { + {{0xff, extcmd_params_sn20, ARRAY_SIZE(extcmd_params_sn20)}, 0}, + {{0xba, spiinttype_params_sn20, ARRAY_SIZE(spiinttype_params_sn20)}, 0}, + {{0xbc, bc_params_sn20, ARRAY_SIZE(bc_params_sn20)}, 0}, + {{0xbd, bd_params_sn20, ARRAY_SIZE(bd_params_sn20)}, 0}, + {{0xbe, be_params_sn20, ARRAY_SIZE(be_params_sn20)}, 0}, + {{0xc7, vcom_params_sn20, ARRAY_SIZE(vcom_params_sn20)}, 0}, + {{0xed, vmesur_params_sn20, ARRAY_SIZE(vmesur_params_sn20)}, 0}, + {{0xc0, powerctl_params_sn20, ARRAY_SIZE(powerctl_params_sn20)}, 0}, + {{0xfc, lvglvolt_params_sn20, ARRAY_SIZE(lvglvolt_params_sn20)}, 0}, + {{0xb6, dispfunc_params_sn20, ARRAY_SIZE(dispfunc_params_sn20)}, 0}, + {{0xdf, engsetting_params_sn20, ARRAY_SIZE(engsetting_params_sn20)}, 0}, + {{0xf3, dvddvolt_params_sn20, ARRAY_SIZE(dvddvolt_params_sn20)}, 0}, + {{0xb4, dispinv_params_sn20, ARRAY_SIZE(dispinv_params_sn20)}, 0}, + {{0xf7, panelres_params_sn20, ARRAY_SIZE(panelres_params_sn20)}, 0}, + {{0xb1, framerate_params_sn20, ARRAY_SIZE(framerate_params_sn20)}, 0}, + {{0xf2, timing_params_sn20, ARRAY_SIZE(timing_params_sn20)}, 0}, + {{0xc1, powerctl2_params_sn20, ARRAY_SIZE(powerctl2_params_sn20)}, 0}, + {{0x36, memaccess_params_sn20, ARRAY_SIZE(memaccess_params_sn20)}, 0}, + {{0x3a, pixfmt_params_sn20, ARRAY_SIZE(pixfmt_params_sn20)}, 0}, + {{0xe0, pgamma_params_sn20, ARRAY_SIZE(pgamma_params_sn20)}, 0}, + {{0xe1, ngamma_params_sn20, ARRAY_SIZE(ngamma_params_sn20)}, 0}, + {{0x35, tearing_params_sn20, ARRAY_SIZE(tearing_params_sn20)}, 0}, +}; + +static void scf0403_gpio_reset(unsigned int gpio) +{ + if (!gpio_is_valid(gpio)) + return; + + gpio_set_value(gpio, 1); + mdelay(100); + gpio_set_value(gpio, 0); + mdelay(40); + gpio_set_value(gpio, 1); + mdelay(100); +} + +static int scf0403_spi_read_rddid(struct spi_slave *spi, u32 *rddid) +{ + int error = 0; + u8 ids_buf = 0x00; + u16 dummy_buf = 0x00; + u16 cmd = 0x04; + + error = spi_set_wordlen(spi, 9); + if (error) + return error; + + /* Here 9 bits required to transmit a command */ + error = spi_xfer(spi, 9, &cmd, NULL, SPI_XFER_ONCE); + if (error) + return error; + + /* + * Here 8 + 1 bits required to arrange extra clock cycle + * before the first data bit. + * According to the datasheet - first parameter is the dummy data. + */ + error = spi_xfer(spi, 9, NULL, &dummy_buf, SPI_XFER_ONCE); + if (error) + return error; + + error = spi_set_wordlen(spi, 8); + if (error) + return error; + + /* Read rest of the data */ + error = spi_xfer(spi, 8, NULL, &ids_buf, SPI_XFER_ONCE); + if (error) + return error; + + *rddid = ids_buf; + + return 0; +} + +static int scf0403_spi_transfer(struct spi_slave *spi, struct scf0403_cmd *cmd) +{ + int i, error; + u32 command = cmd->cmd; + u32 msg; + + error = spi_set_wordlen(spi, 9); + if (error) + return error; + + error = spi_xfer(spi, 9, &command, NULL, SPI_XFER_ONCE); + if (error) + return error; + + for (i = 0; i < cmd->count; i++) { + msg = (cmd->params[i] | 0x100); + error = spi_xfer(spi, 9, &msg, NULL, SPI_XFER_ONCE); + if (error) + return error; + } + + return 0; +} + +static void scf0403_lcd_init(struct scf0403_priv *priv) +{ + int i; + + /* reset LCD */ + scf0403_gpio_reset(priv->reset_gpio); + + for (i = 0; i < priv->seq_size; i++) { + if (scf0403_spi_transfer(priv->spi, &priv->init_seq[i].cmd) < 0) + puts("SPI transfer failed\n"); + + mdelay(priv->init_seq[i].delay_ms); + } +} + +static int scf0403_request_reset_gpio(unsigned gpio) +{ + int err = gpio_request(gpio, "lcd reset"); + + if (err) + return err; + + err = gpio_direction_output(gpio, 0); + if (err) + gpio_free(gpio); + + return err; +} + +int scf0403_init(int reset_gpio) +{ + int error; + + if (gpio_is_valid(reset_gpio)) { + error = scf0403_request_reset_gpio(reset_gpio); + if (error) { + printf("Failed requesting reset GPIO%d: %d\n", + reset_gpio, error); + return error; + } + } + + priv.reset_gpio = reset_gpio; + priv.spi = spi_setup_slave(3, 0, 1000000, SPI_MODE_0); + error = spi_claim_bus(priv.spi); + if (error) + goto bus_claim_fail; + + /* reset LCD */ + scf0403_gpio_reset(reset_gpio); + + error = scf0403_spi_read_rddid(priv.spi, &priv.rddid); + if (error) { + puts("IDs read failed\n"); + goto readid_fail; + } + + if (priv.rddid == SCF0403852GGU04_ID) { + priv.init_seq = scf0403_initseq_sn04; + priv.seq_size = ARRAY_SIZE(scf0403_initseq_sn04); + } else { + priv.init_seq = scf0403_initseq_sn20; + priv.seq_size = ARRAY_SIZE(scf0403_initseq_sn20); + } + + scf0403_lcd_init(&priv); + + /* Start operation */ + scf0403_spi_transfer(priv.spi, &scf0403_cmd_dison); + mdelay(100); + scf0403_spi_transfer(priv.spi, &scf0403_cmd_slpout); + spi_release_bus(priv.spi); + + return 0; + +readid_fail: + spi_release_bus(priv.spi); +bus_claim_fail: + if (gpio_is_valid(priv.reset_gpio)) + gpio_free(priv.reset_gpio); + + return error; +} diff --git a/qemu/roms/u-boot/drivers/video/sed13806.c b/qemu/roms/u-boot/drivers/video/sed13806.c new file mode 100644 index 000000000..da653c0f5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/sed13806.c @@ -0,0 +1,291 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - + * Pierre AUBERT p.aubert@staubli.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* Video support for Epson SED13806 chipset */ + +#include + +#include +#include + +#define readByte(ptrReg) \ + *(volatile unsigned char *)(sed13806.isaBase + ptrReg) + +#define writeByte(ptrReg,value) \ + *(volatile unsigned char *)(sed13806.isaBase + ptrReg) = value + +#ifdef CONFIG_TOTAL5200 +#define writeWord(ptrReg,value) \ + (*(volatile unsigned short *)(sed13806.isaBase + ptrReg) = value) +#else +#define writeWord(ptrReg,value) \ + (*(volatile unsigned short *)(sed13806.isaBase + ptrReg) = ((value >> 8 ) & 0xff) | ((value << 8) & 0xff00)) +#endif + +GraphicDevice sed13806; + +/*----------------------------------------------------------------------------- + * EpsonSetRegs -- + *----------------------------------------------------------------------------- + */ +static void EpsonSetRegs (void) +{ + /* the content of the chipset register depends on the board (clocks, ...)*/ + const S1D_REGS *preg = board_get_regs (); + while (preg -> Index) { + writeByte (preg -> Index, preg -> Value); + preg ++; + } +} + +/*----------------------------------------------------------------------------- + * video_hw_init -- + *----------------------------------------------------------------------------- + */ +void *video_hw_init (void) +{ + unsigned int *vm, i; + + memset (&sed13806, 0, sizeof (GraphicDevice)); + + /* Initialization of the access to the graphic chipset + Retreive base address of the chipset + (see board/RPXClassic/eccx.c) */ + if ((sed13806.isaBase = board_video_init ()) == 0) { + return (NULL); + } + + sed13806.frameAdrs = sed13806.isaBase + FRAME_BUFFER_OFFSET; + sed13806.winSizeX = board_get_width (); + sed13806.winSizeY = board_get_height (); + +#if defined(CONFIG_VIDEO_SED13806_8BPP) + sed13806.gdfIndex = GDF__8BIT_INDEX; + sed13806.gdfBytesPP = 1; + +#elif defined(CONFIG_VIDEO_SED13806_16BPP) + sed13806.gdfIndex = GDF_16BIT_565RGB; + sed13806.gdfBytesPP = 2; + +#else +#error Unsupported SED13806 BPP +#endif + + sed13806.memSize = sed13806.winSizeX * sed13806.winSizeY * sed13806.gdfBytesPP; + + /* Load SED registers */ + EpsonSetRegs (); + + /* (see board/RPXClassic/RPXClassic.c) */ + board_validate_screen (sed13806.isaBase); + + /* Clear video memory */ + i = sed13806.memSize/4; + vm = (unsigned int *)sed13806.frameAdrs; + while(i--) + *vm++ = 0; + + + return (&sed13806); +} +/*----------------------------------------------------------------------------- + * Epson_wait_idle -- Wait for hardware to become idle + *----------------------------------------------------------------------------- + */ +static void Epson_wait_idle (void) +{ + while (readByte (BLT_CTRL0) & 0x80); + + /* Read a word in the BitBLT memory area to shutdown the BitBLT engine */ + *(volatile unsigned short *)(sed13806.isaBase + BLT_REG); +} + +/*----------------------------------------------------------------------------- + * video_hw_bitblt -- + *----------------------------------------------------------------------------- + */ +void video_hw_bitblt ( + unsigned int bpp, /* bytes per pixel */ + unsigned int src_x, /* source pos x */ + unsigned int src_y, /* source pos y */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y /* frame height */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&sed13806; + unsigned long srcAddr, dstAddr; + unsigned int stride = bpp * pGD -> winSizeX; + + srcAddr = (src_y * stride) + (src_x * bpp); + dstAddr = (dst_y * stride) + (dst_x * bpp); + + Epson_wait_idle (); + + writeByte(BLT_ROP,0x0C); /* source */ + writeByte(BLT_OP,0x02);/* move blit in positive direction with ROP */ + writeWord(BLT_MEM_OFF0, stride / 2); + if (pGD -> gdfIndex == GDF__8BIT_INDEX) { + writeByte(BLT_CTRL1,0x00); + } + else { + writeByte(BLT_CTRL1,0x01); + } + + writeWord(BLT_WIDTH0,(dim_x - 1)); + writeWord(BLT_HEIGHT0,(dim_y - 1)); + + /* set up blit registers */ + writeByte(BLT_SRC_ADDR0,srcAddr); + writeByte(BLT_SRC_ADDR1,srcAddr>>8); + writeByte(BLT_SRC_ADDR2,srcAddr>>16); + + writeByte(BLT_DST_ADDR0,dstAddr); + writeByte(BLT_DST_ADDR1,dstAddr>>8); + writeByte(BLT_DST_ADDR2,dstAddr>>16); + + /* Engage the blt engine */ + /* rectangular region for src and dst */ + writeByte(BLT_CTRL0,0x80); + + /* wait untill current blits finished */ + Epson_wait_idle (); +} +/*----------------------------------------------------------------------------- + * video_hw_rectfill -- + *----------------------------------------------------------------------------- + */ +void video_hw_rectfill ( + unsigned int bpp, /* bytes per pixel */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y, /* frame height */ + unsigned int color /* fill color */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&sed13806; + unsigned long dstAddr; + unsigned int stride = bpp * pGD -> winSizeX; + + dstAddr = (dst_y * stride) + (dst_x * bpp); + + Epson_wait_idle (); + + /* set up blit registers */ + writeByte(BLT_DST_ADDR0,dstAddr); + writeByte(BLT_DST_ADDR1,dstAddr>>8); + writeByte(BLT_DST_ADDR2,dstAddr>>16); + + writeWord(BLT_WIDTH0,(dim_x - 1)); + writeWord(BLT_HEIGHT0,(dim_y - 1)); + writeWord(BLT_FGCOLOR0,color); + + writeByte(BLT_OP,0x0C); /* solid fill */ + writeWord(BLT_MEM_OFF0,stride / 2); + + if (pGD -> gdfIndex == GDF__8BIT_INDEX) { + writeByte(BLT_CTRL1,0x00); + } + else { + writeByte(BLT_CTRL1,0x01); + } + + /* Engage the blt engine */ + /* rectangular region for src and dst */ + writeByte(BLT_CTRL0,0x80); + + /* wait untill current blits finished */ + Epson_wait_idle (); +} + +/*----------------------------------------------------------------------------- + * video_set_lut -- + *----------------------------------------------------------------------------- + */ +void video_set_lut ( + unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + writeByte(REG_LUT_ADDR, index ); + writeByte(REG_LUT_DATA, r); + writeByte(REG_LUT_DATA, g); + writeByte(REG_LUT_DATA, b); +} +#ifdef CONFIG_VIDEO_HW_CURSOR +/*----------------------------------------------------------------------------- + * video_set_hw_cursor -- + *----------------------------------------------------------------------------- + */ +void video_set_hw_cursor (int x, int y) +{ + writeByte (LCD_CURSOR_XL, (x & 0xff)); + writeByte (LCD_CURSOR_XM, (x >> 8)); + writeByte (LCD_CURSOR_YL, (y & 0xff)); + writeByte (LCD_CURSOR_YM, (y >> 8)); +} + +/*----------------------------------------------------------------------------- + * video_init_hw_cursor -- + *----------------------------------------------------------------------------- + */ +void video_init_hw_cursor (int font_width, int font_height) +{ + volatile unsigned char *ptr; + unsigned char pattern; + int i; + + + /* Init cursor content + Cursor size is 64x64 pixels + Start of the cursor memory depends on panel type (dual panel ...) */ + if ((i = readByte (LCD_CURSOR_START)) == 0) { + ptr = (unsigned char *)(sed13806.frameAdrs + DEFAULT_VIDEO_MEMORY_SIZE - HWCURSORSIZE); + } + else { + ptr = (unsigned char *)(sed13806.frameAdrs + DEFAULT_VIDEO_MEMORY_SIZE - (i * 8192)); + } + + /* Fill the first line and the first empty line after cursor */ + for (i = 0, pattern = 0; i < 64; i++) { + if (i < font_width) { + /* Invert background */ + pattern |= 0x3; + + } + else { + /* Background */ + pattern |= 0x2; + } + if ((i & 3) == 3) { + *ptr = pattern; + *(ptr + font_height * 16) = 0xaa; + ptr ++; + pattern = 0; + } + pattern <<= 2; + } + + /* Duplicate this line */ + for (i = 1; i < font_height; i++) { + memcpy ((void *)ptr, (void *)(ptr - 16), 16); + ptr += 16; + } + + for (; i < 64; i++) { + memcpy ((void *)(ptr + 16), (void *)ptr, 16); + ptr += 16; + } + + /* Select cursor mode */ + writeByte (LCD_CURSOR_CNTL, 1); +} +#endif diff --git a/qemu/roms/u-boot/drivers/video/sed156x.c b/qemu/roms/u-boot/drivers/video/sed156x.c new file mode 100644 index 000000000..2c906ecf1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/sed156x.c @@ -0,0 +1,546 @@ +/* + * (C) Copyright 2003 + * + * Pantelis Antoniou + * Intracom S.A. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include + +/* configure according to the selected display */ +#if defined(CONFIG_SED156X_PG12864Q) +#define LCD_WIDTH 128 +#define LCD_HEIGHT 64 +#define LCD_LINES 64 +#define LCD_PAGES 9 +#define LCD_COLUMNS 132 +#else +#error Unsupported SED156x configuration +#endif + +/* include the font data */ +#include + +#if VIDEO_FONT_WIDTH != 8 || VIDEO_FONT_HEIGHT != 16 +#error Expecting VIDEO_FONT_WIDTH == 8 && VIDEO_FONT_HEIGHT == 16 +#endif + +#define LCD_BYTE_WIDTH (LCD_WIDTH / 8) +#define VIDEO_FONT_BYTE_WIDTH (VIDEO_FONT_WIDTH / 8) + +#define LCD_TEXT_WIDTH (LCD_WIDTH / VIDEO_FONT_WIDTH) +#define LCD_TEXT_HEIGHT (LCD_HEIGHT / VIDEO_FONT_HEIGHT) + +#define LCD_BYTE_LINESZ (LCD_BYTE_WIDTH * VIDEO_FONT_HEIGHT) + +const int sed156x_text_width = LCD_TEXT_WIDTH; +const int sed156x_text_height = LCD_TEXT_HEIGHT; + +/**************************************************************************************/ + +#define SED156X_SPI_RXD() (SED156X_SPI_RXD_PORT & SED156X_SPI_RXD_MASK) + +#define SED156X_SPI_TXD(x) \ + do { \ + if (x) \ + SED156X_SPI_TXD_PORT |= SED156X_SPI_TXD_MASK; \ + else \ + SED156X_SPI_TXD_PORT &= ~SED156X_SPI_TXD_MASK; \ + } while(0) + +#define SED156X_SPI_CLK(x) \ + do { \ + if (x) \ + SED156X_SPI_CLK_PORT |= SED156X_SPI_CLK_MASK; \ + else \ + SED156X_SPI_CLK_PORT &= ~SED156X_SPI_CLK_MASK; \ + } while(0) + +#define SED156X_SPI_CLK_TOGGLE() (SED156X_SPI_CLK_PORT ^= SED156X_SPI_CLK_MASK) + +#define SED156X_SPI_BIT_DELAY() /* no delay */ + +#define SED156X_CS(x) \ + do { \ + if (x) \ + SED156X_CS_PORT |= SED156X_CS_MASK; \ + else \ + SED156X_CS_PORT &= ~SED156X_CS_MASK; \ + } while(0) + +#define SED156X_A0(x) \ + do { \ + if (x) \ + SED156X_A0_PORT |= SED156X_A0_MASK; \ + else \ + SED156X_A0_PORT &= ~SED156X_A0_MASK; \ + } while(0) + +/**************************************************************************************/ + +/*** LCD Commands ***/ + +#define LCD_ON 0xAF /* Display ON */ +#define LCD_OFF 0xAE /* Display OFF */ +#define LCD_LADDR 0x40 /* Display start line set + (6-bit) address */ +#define LCD_PADDR 0xB0 /* Page address set + (4-bit) page */ +#define LCD_CADRH 0x10 /* Column address set upper + (4-bit) column hi */ +#define LCD_CADRL 0x00 /* Column address set lower + (4-bit) column lo */ +#define LCD_ADC_NRM 0xA0 /* ADC select Normal */ +#define LCD_ADC_REV 0xA1 /* ADC select Reverse */ +#define LCD_DSP_NRM 0xA6 /* LCD display Normal */ +#define LCD_DSP_REV 0xA7 /* LCD display Reverse */ +#define LCD_DPT_NRM 0xA4 /* Display all points Normal */ +#define LCD_DPT_ALL 0xA5 /* Display all points ON */ +#define LCD_BIAS9 0xA2 /* LCD bias set 1/9 */ +#define LCD_BIAS7 0xA3 /* LCD bias set 1/7 */ +#define LCD_CAINC 0xE0 /* Read/modify/write */ +#define LCD_CAEND 0xEE /* End */ +#define LCD_RESET 0xE2 /* Reset */ +#define LCD_C_NRM 0xC0 /* Common output mode select Normal direction */ +#define LCD_C_RVS 0xC8 /* Common output mode select Reverse direction */ +#define LCD_PWRMD 0x28 /* Power control set + (3-bit) mode */ +#define LCD_RESRT 0x20 /* V5 v. reg. int. resistor ratio set + (3-bit) ratio */ +#define LCD_EVSET 0x81 /* Electronic volume mode set + byte = (6-bit) volume */ +#define LCD_SIOFF 0xAC /* Static indicator OFF */ +#define LCD_SION 0xAD /* Static indicator ON + byte = (2-bit) mode */ +#define LCD_NOP 0xE3 /* NOP */ +#define LCD_TEST 0xF0 /* Test/Test mode reset (Note: *DO NOT USE*) */ + +/*------------------------------------------------------------------------------- + Compound commands + ------------------------------------------------------------------------------- + Command Description Commands + ---------- ------------------------ ------------------------------------- + POWS_ON POWER SAVER ON command LCD_OFF, LCD_D_ALL + POWS_OFF POWER SAVER OFF command LCD_D_NRM + SLEEPON SLEEP mode LCD_SIOFF, POWS_ON + SLEEPOFF SLEEP mode cancel LCD_D_NRM, LCD_SION, LCD_SIS_??? + STDBYON STAND BY mode LCD_SION, POWS_ON + STDBYOFF STAND BY mode cancel LCD_D_NRM + -------------------------------------------------------------------------------*/ + +/*** LCD various parameters ***/ +#define LCD_PPB 8 /* Pixels per byte (display is B/W, 1 bit per pixel) */ + +/*** LCD Status byte masks ***/ +#define LCD_S_BUSY 0x80 /* Status Read - BUSY mask */ +#define LCD_S_ADC 0x40 /* Status Read - ADC mask */ +#define LCD_S_ONOFF 0x20 /* Status Read - ON/OFF mask */ +#define LCD_S_RESET 0x10 /* Status Read - RESET mask */ + +/*** LCD commands parameter masks ***/ +#define LCD_M_LADDR 0x3F /* Display start line (6-bit) address mask */ +#define LCD_M_PADDR 0x0F /* Page address (4-bit) page mask */ +#define LCD_M_CADRH 0x0F /* Column address upper (4-bit) column hi mask */ +#define LCD_M_CADRL 0x0F /* Column address lower (4-bit) column lo mask */ +#define LCD_M_PWRMD 0x07 /* Power control (3-bit) mode mask */ +#define LCD_M_RESRT 0x07 /* V5 v. reg. int. resistor ratio (3-bit) ratio mask */ +#define LCD_M_EVSET 0x3F /* Electronic volume mode byte (6-bit) volume mask */ +#define LCD_M_SION 0x03 /* Static indicator ON (2-bit) mode mask */ + +/*** LCD Power control cirquits control masks ***/ +#define LCD_PWRBSTR 0x04 /* Power control mode - Booster cirquit ON */ +#define LCD_PWRVREG 0x02 /* Power control mode - Voltage regulator cirquit ON */ +#define LCD_PWRVFOL 0x01 /* Power control mode - Voltage follower cirquit ON */ + +/*** LCD Static indicator states ***/ +#define LCD_SIS_OFF 0x00 /* Static indicator register set - OFF state */ +#define LCD_SIS_BL 0x01 /* Static indicator register set - 1s blink state */ +#define LCD_SIS_RBL 0x02 /* Static indicator register set - .5s rapid blink state */ +#define LCD_SIS_ON 0x03 /* Static indicator register set - constantly on state */ + +/*** LCD functions special parameters (commands) ***/ +#define LCD_PREVP 0x80 /* Page number for moving to previous */ +#define LCD_NEXTP 0x81 /* or next page */ +#define LCD_ERR_P 0xFF /* Error in page number */ + +/*** LCD initialization settings ***/ +#define LCD_BIAS LCD_BIAS9 /* Bias: 1/9 */ +#define LCD_ADCMODE LCD_ADC_NRM /* ADC mode: normal */ +#define LCD_COMDIR LCD_C_NRM /* Common output mode: normal */ +#define LCD_RRATIO 0 /* Resistor ratio: 0 */ +#define LCD_CNTRST 0x1C /* electronic volume: 1Ch */ +#define LCD_POWERM (LCD_PWRBSTR | LCD_PWRVREG | LCD_PWRVFOL) /* Power mode: All on */ + +/**************************************************************************************/ + +static inline unsigned int sed156x_transfer(unsigned int val) +{ + unsigned int rx; + int b; + + rx = 0; b = 8; + while (--b >= 0) { + SED156X_SPI_TXD(val & 0x80); + val <<= 1; + SED156X_SPI_CLK_TOGGLE(); + SED156X_SPI_BIT_DELAY(); + rx <<= 1; + if (SED156X_SPI_RXD()) + rx |= 1; + SED156X_SPI_CLK_TOGGLE(); + SED156X_SPI_BIT_DELAY(); + } + + return rx; +} + +unsigned int sed156x_data_transfer(unsigned int val) +{ + unsigned int rx; + + SED156X_SPI_CLK(1); + SED156X_CS(0); + SED156X_A0(1); + + rx = sed156x_transfer(val); + + SED156X_CS(1); + + return rx; +} + +void sed156x_data_block_transfer(const u8 *p, int size) +{ + SED156X_SPI_CLK(1); + SED156X_CS(0); + SED156X_A0(1); + + while (--size >= 0) + sed156x_transfer(*p++); + + SED156X_CS(1); +} + +unsigned int sed156x_cmd_transfer(unsigned int val) +{ + unsigned int rx; + + SED156X_SPI_CLK(1); + SED156X_CS(0); + SED156X_A0(0); + + rx = sed156x_transfer(val); + + SED156X_CS(1); + SED156X_A0(1); + + return rx; +} + +/******************************************************************************/ + +static u8 hw_screen[LCD_PAGES][LCD_COLUMNS]; +static u8 last_hw_screen[LCD_PAGES][LCD_COLUMNS]; +static u8 sw_screen[LCD_BYTE_WIDTH * LCD_HEIGHT]; + +void sed156x_sync(void) +{ + int i, j, last_page; + u8 *d; + const u8 *s, *e, *b, *r; + u8 v0, v1, v2, v3, v4, v5, v6, v7; + + /* copy and rotate sw_screen to hw_screen */ + for (i = 0; i < LCD_HEIGHT / 8; i++) { + + d = &hw_screen[i][0]; + s = &sw_screen[LCD_BYTE_WIDTH * 8 * i + LCD_BYTE_WIDTH - 1]; + + for (j = 0; j < LCD_WIDTH / 8; j++) { + + v0 = s[0 * LCD_BYTE_WIDTH]; + v1 = s[1 * LCD_BYTE_WIDTH]; + v2 = s[2 * LCD_BYTE_WIDTH]; + v3 = s[3 * LCD_BYTE_WIDTH]; + v4 = s[4 * LCD_BYTE_WIDTH]; + v5 = s[5 * LCD_BYTE_WIDTH]; + v6 = s[6 * LCD_BYTE_WIDTH]; + v7 = s[7 * LCD_BYTE_WIDTH]; + + d[0] = ((v7 & 0x01) << 7) | + ((v6 & 0x01) << 6) | + ((v5 & 0x01) << 5) | + ((v4 & 0x01) << 4) | + ((v3 & 0x01) << 3) | + ((v2 & 0x01) << 2) | + ((v1 & 0x01) << 1) | + (v0 & 0x01) ; + + d[1] = ((v7 & 0x02) << 6) | + ((v6 & 0x02) << 5) | + ((v5 & 0x02) << 4) | + ((v4 & 0x02) << 3) | + ((v3 & 0x02) << 2) | + ((v2 & 0x02) << 1) | + ((v1 & 0x02) << 0) | + ((v0 & 0x02) >> 1) ; + + d[2] = ((v7 & 0x04) << 5) | + ((v6 & 0x04) << 4) | + ((v5 & 0x04) << 3) | + ((v4 & 0x04) << 2) | + ((v3 & 0x04) << 1) | + (v2 & 0x04) | + ((v1 & 0x04) >> 1) | + ((v0 & 0x04) >> 2) ; + + d[3] = ((v7 & 0x08) << 4) | + ((v6 & 0x08) << 3) | + ((v5 & 0x08) << 2) | + ((v4 & 0x08) << 1) | + (v3 & 0x08) | + ((v2 & 0x08) >> 1) | + ((v1 & 0x08) >> 2) | + ((v0 & 0x08) >> 3) ; + + d[4] = ((v7 & 0x10) << 3) | + ((v6 & 0x10) << 2) | + ((v5 & 0x10) << 1) | + (v4 & 0x10) | + ((v3 & 0x10) >> 1) | + ((v2 & 0x10) >> 2) | + ((v1 & 0x10) >> 3) | + ((v0 & 0x10) >> 4) ; + + d[5] = ((v7 & 0x20) << 2) | + ((v6 & 0x20) << 1) | + (v5 & 0x20) | + ((v4 & 0x20) >> 1) | + ((v3 & 0x20) >> 2) | + ((v2 & 0x20) >> 3) | + ((v1 & 0x20) >> 4) | + ((v0 & 0x20) >> 5) ; + + d[6] = ((v7 & 0x40) << 1) | + (v6 & 0x40) | + ((v5 & 0x40) >> 1) | + ((v4 & 0x40) >> 2) | + ((v3 & 0x40) >> 3) | + ((v2 & 0x40) >> 4) | + ((v1 & 0x40) >> 5) | + ((v0 & 0x40) >> 6) ; + + d[7] = (v7 & 0x80) | + ((v6 & 0x80) >> 1) | + ((v5 & 0x80) >> 2) | + ((v4 & 0x80) >> 3) | + ((v3 & 0x80) >> 4) | + ((v2 & 0x80) >> 5) | + ((v1 & 0x80) >> 6) | + ((v0 & 0x80) >> 7) ; + + d += 8; + s--; + } + } + + /* and now output only the differences */ + for (i = 0; i < LCD_PAGES; i++) { + + b = &hw_screen[i][0]; + e = &hw_screen[i][LCD_COLUMNS]; + + d = &last_hw_screen[i][0]; + s = b; + + last_page = -1; + + /* update only the differences */ + do { + while (s < e && *s == *d) { + s++; + d++; + } + if (s == e) + break; + r = s; + while (s < e && *s != *d) + *d++ = *s++; + + j = r - b; + + if (i != last_page) { + sed156x_cmd_transfer(LCD_PADDR | i); + last_page = i; + } + + sed156x_cmd_transfer(LCD_CADRH | ((j >> 4) & 0x0F)); + sed156x_cmd_transfer(LCD_CADRL | (j & 0x0F)); + sed156x_data_block_transfer(r, s - r); + + } while (s < e); + } + +/******** + for (i = 0; i < LCD_PAGES; i++) { + sed156x_cmd_transfer(LCD_PADDR | i); + sed156x_cmd_transfer(LCD_CADRH | 0); + sed156x_cmd_transfer(LCD_CADRL | 0); + sed156x_data_block_transfer(&hw_screen[i][0], LCD_COLUMNS); + } + memcpy(last_hw_screen, hw_screen, sizeof(last_hw_screen)); +********/ +} + +void sed156x_clear(void) +{ + memset(sw_screen, 0, sizeof(sw_screen)); +} + +void sed156x_output_at(int x, int y, const char *str, int size) +{ + int i, j; + u8 *p; + const u8 *s; + + if ((unsigned int)y >= LCD_TEXT_HEIGHT || (unsigned int)x >= LCD_TEXT_WIDTH) + return; + + p = &sw_screen[y * VIDEO_FONT_HEIGHT * LCD_BYTE_WIDTH + x * VIDEO_FONT_BYTE_WIDTH]; + + while (--size >= 0) { + + s = &video_fontdata[((int)*str++ & 0xff) * VIDEO_FONT_BYTE_WIDTH * VIDEO_FONT_HEIGHT]; + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + for (j = 0; j < VIDEO_FONT_BYTE_WIDTH; j++) + *p++ = *s++; + p += LCD_BYTE_WIDTH - VIDEO_FONT_BYTE_WIDTH; + } + p -= (LCD_BYTE_LINESZ - VIDEO_FONT_BYTE_WIDTH); + + if (x >= LCD_TEXT_WIDTH) + break; + x++; + } +} + +void sed156x_reverse_at(int x, int y, int size) +{ + int i, j; + u8 *p; + + if ((unsigned int)y >= LCD_TEXT_HEIGHT || (unsigned int)x >= LCD_TEXT_WIDTH) + return; + + p = &sw_screen[y * VIDEO_FONT_HEIGHT * LCD_BYTE_WIDTH + x * VIDEO_FONT_BYTE_WIDTH]; + + while (--size >= 0) { + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + for (j = 0; j < VIDEO_FONT_BYTE_WIDTH; j++, p++) + *p = ~*p; + p += LCD_BYTE_WIDTH - VIDEO_FONT_BYTE_WIDTH; + } + p -= (LCD_BYTE_LINESZ - VIDEO_FONT_BYTE_WIDTH); + + if (x >= LCD_TEXT_WIDTH) + break; + x++; + } +} + +void sed156x_scroll_line(void) +{ + memmove(&sw_screen[0], + &sw_screen[LCD_BYTE_LINESZ], + LCD_BYTE_WIDTH * (LCD_HEIGHT - VIDEO_FONT_HEIGHT)); +} + +void sed156x_scroll(int dx, int dy) +{ + u8 *p1 = NULL, *p2 = NULL, *p3 = NULL; /* pacify gcc */ + int adx, ady, i, sz; + + adx = dx > 0 ? dx : -dx; + ady = dy > 0 ? dy : -dy; + + /* overscroll? erase everything */ + if (adx >= LCD_TEXT_WIDTH || ady >= LCD_TEXT_HEIGHT) { + memset(sw_screen, 0, sizeof(sw_screen)); + return; + } + + sz = LCD_BYTE_LINESZ * ady; + if (dy > 0) { + p1 = &sw_screen[0]; + p2 = &sw_screen[sz]; + p3 = &sw_screen[LCD_BYTE_WIDTH * LCD_HEIGHT - sz]; + } else if (dy < 0) { + p1 = &sw_screen[sz]; + p2 = &sw_screen[0]; + p3 = &sw_screen[0]; + } + + if (ady > 0) { + memmove(p1, p2, LCD_BYTE_WIDTH * LCD_HEIGHT - sz); + memset(p3, 0, sz); + } + + sz = VIDEO_FONT_BYTE_WIDTH * adx; + if (dx > 0) { + p1 = &sw_screen[0]; + p2 = &sw_screen[0] + sz; + p3 = &sw_screen[0] + LCD_BYTE_WIDTH - sz; + } else if (dx < 0) { + p1 = &sw_screen[0] + sz; + p2 = &sw_screen[0]; + p3 = &sw_screen[0]; + } + + /* xscroll */ + if (adx > 0) { + for (i = 0; i < LCD_HEIGHT; i++) { + memmove(p1, p2, LCD_BYTE_WIDTH - sz); + memset(p3, 0, sz); + p1 += LCD_BYTE_WIDTH; + p2 += LCD_BYTE_WIDTH; + p3 += LCD_BYTE_WIDTH; + } + } +} + +void sed156x_init(void) +{ + int i; + + SED156X_CS(1); + SED156X_A0(1); + + /* Send initialization commands to the LCD */ + sed156x_cmd_transfer(LCD_OFF); /* Turn display OFF */ + sed156x_cmd_transfer(LCD_BIAS); /* set the LCD Bias, */ + sed156x_cmd_transfer(LCD_ADCMODE); /* ADC mode, */ + sed156x_cmd_transfer(LCD_COMDIR); /* common output mode, */ + sed156x_cmd_transfer(LCD_RESRT | LCD_RRATIO); /* resistor ratio, */ + sed156x_cmd_transfer(LCD_EVSET); /* electronic volume, */ + sed156x_cmd_transfer(LCD_CNTRST); + sed156x_cmd_transfer(LCD_PWRMD | LCD_POWERM); /* and power mode */ + sed156x_cmd_transfer(LCD_PADDR | 0); /* cursor home */ + sed156x_cmd_transfer(LCD_CADRH | 0); + sed156x_cmd_transfer(LCD_CADRL | 0); + sed156x_cmd_transfer(LCD_LADDR | 0); /* and display start line */ + sed156x_cmd_transfer(LCD_DSP_NRM); /* LCD display Normal */ + + /* clear everything */ + memset(sw_screen, 0, sizeof(sw_screen)); + memset(hw_screen, 0, sizeof(hw_screen)); + memset(last_hw_screen, 0, sizeof(last_hw_screen)); + + for (i = 0; i < LCD_PAGES; i++) { + sed156x_cmd_transfer(LCD_PADDR | i); + sed156x_cmd_transfer(LCD_CADRH | 0); + sed156x_cmd_transfer(LCD_CADRL | 0); + sed156x_data_block_transfer(&hw_screen[i][0], LCD_COLUMNS); + } + + sed156x_clear(); + sed156x_sync(); + sed156x_cmd_transfer(LCD_ON); /* Turn display ON */ +} diff --git a/qemu/roms/u-boot/drivers/video/sm501.c b/qemu/roms/u-boot/drivers/video/sm501.c new file mode 100644 index 000000000..cafaae5f3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/sm501.c @@ -0,0 +1,224 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - + * Pierre AUBERT p.aubert@staubli.com + * + * (C) Copyright 2005 + * Martin Krause TQ-Systems GmbH martin.krause@tqs.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Basic video support for SMI SM501 "Voyager" graphic controller + */ + +#include + +#include +#include +#include + +#define read8(ptrReg) \ + *(volatile unsigned char *)(sm501.isaBase + ptrReg) + +#define write8(ptrReg,value) \ + *(volatile unsigned char *)(sm501.isaBase + ptrReg) = value + +#define read16(ptrReg) \ + (*(volatile unsigned short *)(sm501.isaBase + ptrReg)) + +#define write16(ptrReg,value) \ + (*(volatile unsigned short *)(sm501.isaBase + ptrReg) = value) + +#define read32(ptrReg) \ + (*(volatile unsigned int *)(sm501.isaBase + ptrReg)) + +#define write32(ptrReg, value) \ + (*(volatile unsigned int *)(sm501.isaBase + ptrReg) = value) + +GraphicDevice sm501; + +void write_be32(int off, unsigned int val) +{ + out_be32((unsigned __iomem *)(sm501.isaBase + off), val); +} + +void write_le32(int off, unsigned int val) +{ + out_le32((unsigned __iomem *)(sm501.isaBase + off), val); +} + +void (*write_reg32)(int off, unsigned int val) = write_be32; + +/*----------------------------------------------------------------------------- + * SmiSetRegs -- + *----------------------------------------------------------------------------- + */ +static void SmiSetRegs (void) +{ + /* + * The content of the chipset register depends on the board (clocks, + * ...) + */ + const SMI_REGS *preg = board_get_regs (); + while (preg->Index) { + write_reg32 (preg->Index, preg->Value); + /* + * Insert a delay between + */ + udelay (1000); + preg ++; + } +} + +#ifdef CONFIG_VIDEO_SM501_PCI +static struct pci_device_id sm501_pci_tbl[] = { + { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_501 }, + {} +}; +#endif + +/* + * We do not enforce board code to provide empty/unused + * functions for this driver and define weak default + * functions here. + */ +unsigned int __board_video_init (void) +{ + return 0; +} + +unsigned int board_video_init (void) + __attribute__((weak, alias("__board_video_init"))); + +unsigned int __board_video_get_fb (void) +{ + return 0; +} + +unsigned int board_video_get_fb (void) + __attribute__((weak, alias("__board_video_get_fb"))); + +void __board_validate_screen (unsigned int base) +{ +} + +void board_validate_screen (unsigned int base) + __attribute__((weak, alias("__board_validate_screen"))); + +/*----------------------------------------------------------------------------- + * video_hw_init -- + *----------------------------------------------------------------------------- + */ +void *video_hw_init (void) +{ +#ifdef CONFIG_VIDEO_SM501_PCI + unsigned int pci_mem_base, pci_mmio_base; + unsigned int id; + unsigned short device_id; + pci_dev_t devbusfn; + int mem; +#endif + unsigned int *vm, i; + + memset (&sm501, 0, sizeof (GraphicDevice)); + +#ifdef CONFIG_VIDEO_SM501_PCI + printf("Video: "); + + /* Look for SM501/SM502 chips */ + devbusfn = pci_find_devices(sm501_pci_tbl, 0); + if (devbusfn < 0) { + printf ("PCI Controller not found.\n"); + goto not_pci; + } + + /* Setup */ + pci_write_config_dword (devbusfn, PCI_COMMAND, + (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)); + pci_read_config_word (devbusfn, PCI_DEVICE_ID, &device_id); + pci_read_config_dword (devbusfn, PCI_REVISION_ID, &id); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_1, &pci_mmio_base); + sm501.frameAdrs = pci_mem_to_phys (devbusfn, pci_mem_base); + sm501.isaBase = pci_mem_to_phys (devbusfn, pci_mmio_base); + + if (sm501.isaBase) + write_reg32 = write_le32; + + mem = in_le32 ((unsigned __iomem *)(sm501.isaBase + 0x10)); + mem = (mem & 0x0000e000) >> 13; + switch (mem) { + case 1: + mem = 8; + break; + case 2: + mem = 16; + break; + case 3: + mem = 32; + break; + case 4: + mem = 64; + break; + case 5: + mem = 2; + break; + case 0: + default: + mem = 4; + } + printf ("PCI SM50%d %d MB\n", ((id & 0xff) == 0xC0) ? 2 : 1, mem); +not_pci: +#endif + /* + * Initialization of the access to the graphic chipset Retreive base + * address of the chipset (see board/RPXClassic/eccx.c) + */ + if (!sm501.isaBase) { + sm501.isaBase = board_video_init (); + if (!sm501.isaBase) + return NULL; + } + + if (!sm501.frameAdrs) { + sm501.frameAdrs = board_video_get_fb (); + if (!sm501.frameAdrs) + return NULL; + } + + sm501.winSizeX = board_get_width (); + sm501.winSizeY = board_get_height (); + +#if defined(CONFIG_VIDEO_SM501_8BPP) + sm501.gdfIndex = GDF__8BIT_INDEX; + sm501.gdfBytesPP = 1; + +#elif defined(CONFIG_VIDEO_SM501_16BPP) + sm501.gdfIndex = GDF_16BIT_565RGB; + sm501.gdfBytesPP = 2; + +#elif defined(CONFIG_VIDEO_SM501_32BPP) + sm501.gdfIndex = GDF_32BIT_X888RGB; + sm501.gdfBytesPP = 4; +#else +#error Unsupported SM501 BPP +#endif + + sm501.memSize = sm501.winSizeX * sm501.winSizeY * sm501.gdfBytesPP; + + /* Load Smi registers */ + SmiSetRegs (); + + /* (see board/RPXClassic/RPXClassic.c) */ + board_validate_screen (sm501.isaBase); + + /* Clear video memory */ + i = sm501.memSize/4; + vm = (unsigned int *)sm501.frameAdrs; + while(i--) + *vm++ = 0; + + return (&sm501); +} diff --git a/qemu/roms/u-boot/drivers/video/smiLynxEM.c b/qemu/roms/u-boot/drivers/video/smiLynxEM.c new file mode 100644 index 000000000..614bcb3c9 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/smiLynxEM.c @@ -0,0 +1,838 @@ +/* + * (C) Copyright 1997-2002 ELTEC Elektronik AG + * Frank Gottschling + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * smiLynxEM.c + * + * Silicon Motion graphic interface for sm810/sm710/sm712 accelerator + * + * modification history + * -------------------- + * 04-18-2002 Rewritten for U-Boot . + * + * 18-03-2004 - Unify videomodes handling with the ct69000 + * - The video output can be set via the variable "videoout" + * in the environment. + * videoout=1 output on LCD + * videoout=2 output on CRT (default value) + * + */ + +#include + +#include +#include +#include "videomodes.h" +/* + * Export Graphic Device + */ +GraphicDevice smi; + +/* + * SMI 710/712 have 4MB internal RAM; SMI 810 2MB internal + 2MB external + */ +#define VIDEO_MEM_SIZE 0x400000 + + +/* + * ISA mapped regs + */ +#define SMI_INDX_C4 (pGD->isaBase + 0x03c4) /* index reg */ +#define SMI_DATA_C5 (pGD->isaBase + 0x03c5) /* data reg */ +#define SMI_INDX_D4 (pGD->isaBase + 0x03d4) /* index reg */ +#define SMI_DATA_D5 (pGD->isaBase + 0x03d5) /* data reg */ +#define SMI_ISR1 (pGD->isaBase + 0x03ca) +#define SMI_INDX_CE (pGD->isaBase + 0x03ce) /* index reg */ +#define SMI_DATA_CF (pGD->isaBase + 0x03cf) /* data reg */ +#define SMI_LOCK_REG (pGD->isaBase + 0x03c3) /* unlock/lock ext crt reg */ +#define SMI_MISC_REG (pGD->isaBase + 0x03c2) /* misc reg */ +#define SMI_LUT_MASK (pGD->isaBase + 0x03c6) /* lut mask reg */ +#define SMI_LUT_START (pGD->isaBase + 0x03c8) /* lut start index */ +#define SMI_LUT_RGB (pGD->isaBase + 0x03c9) /* lut colors auto incr.*/ +#define SMI_INDX_ATTR (pGD->isaBase + 0x03c0) /* attributes index reg */ + +/* + * Video processor control + */ +typedef struct { + unsigned int control; + unsigned int colorKey; + unsigned int colorKeyMask; + unsigned int start; + unsigned short offset; + unsigned short width; + unsigned int fifoPrio; + unsigned int fifoERL; + unsigned int YUVtoRGB; +} SmiVideoProc; + +/* + * Video window control + */ +typedef struct { + unsigned short top; + unsigned short left; + unsigned short bottom; + unsigned short right; + unsigned int srcStart; + unsigned short width; + unsigned short offset; + unsigned char hStretch; + unsigned char vStretch; +} SmiVideoWin; + +/* + * Capture port control + */ +typedef struct { + unsigned int control; + unsigned short topClip; + unsigned short leftClip; + unsigned short srcHeight; + unsigned short srcWidth; + unsigned int srcBufStart1; + unsigned int srcBufStart2; + unsigned short srcOffset; + unsigned short fifoControl; +} SmiCapturePort; + + +/* + * Register values for common video modes + */ +static char SMI_SCR[] = { + /* all modes */ + 0x10, 0xff, 0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x15, 0x90, + 0x17, 0x20, 0x18, 0xb1, 0x19, 0x00, +}; +static char SMI_EXT_CRT[] = { + 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, + 0x36, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00, +}; +static char SMI_ATTR [] = { + 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x41, 0x11, 0x00, + 0x12, 0x0f, 0x13, 0x00, 0x14, 0x00, +}; +static char SMI_GCR[18] = { + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x40, + 0x06, 0x05, 0x07, 0x0f, 0x08, 0xff, +}; +static char SMI_SEQR[] = { + 0x00, 0x00, 0x01, 0x01, 0x02, 0x0f, 0x03, 0x03, 0x04, 0x0e, 0x00, 0x03, +}; +static char SMI_PCR [] = { + 0x20, 0x04, 0x21, 0x30, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, +}; +static char SMI_MCR[] = { + 0x60, 0x01, 0x61, 0x00, +#ifdef CONFIG_HMI1001 + 0x62, 0x74, /* Memory type is not configured by pins on HMI1001 */ +#endif +}; + +static char SMI_HCR[] = { + 0x80, 0xff, 0x81, 0x07, 0x82, 0x00, 0x83, 0xff, 0x84, 0xff, 0x88, 0x00, + 0x89, 0x02, 0x8a, 0x80, 0x8b, 0x01, 0x8c, 0xff, 0x8d, 0x00, +}; + + +/******************************************************************************* + * + * Write SMI ISA register + */ +static void smiWrite (unsigned short index, char reg, char val) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + out8 ((pGD->isaBase + index), reg); + out8 ((pGD->isaBase + index + 1), val); +} + +/******************************************************************************* + * + * Write a table of SMI ISA register + */ +static void smiLoadRegs ( + unsigned int iReg, + unsigned int dReg, + char *regTab, + unsigned int tabSize + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + register int i; + + for (i=0; icprBase + 0x0004), ((pCP->topClip<<16) | pCP->leftClip)); + out32r ((pGD->cprBase + 0x0008), ((pCP->srcHeight<<16) | pCP->srcWidth)); + out32r ((pGD->cprBase + 0x000c), pCP->srcBufStart1/8); + out32r ((pGD->cprBase + 0x0010), pCP->srcBufStart2/8); + out32r ((pGD->cprBase + 0x0014), pCP->srcOffset/8); + out32r ((pGD->cprBase + 0x0018), pCP->fifoControl); + out32r ((pGD->cprBase + 0x0000), pCP->control); +} + + +/******************************************************************************* + * + * Init video processor registers + */ +static void smiInitVideoProcessor (void) +{ + SmiVideoProc smiVP = { 0x100000, 0, 0, 0, 0, 1600, 0x1200543, 4, 0xededed }; + SmiVideoWin smiVW = { 0, 0, 599, 799, 0, 1600, 0, 0, 0 }; + register GraphicDevice *pGD = (GraphicDevice *)&smi; + register SmiVideoProc *pVP = (SmiVideoProc *)&smiVP; + register SmiVideoWin *pVWin = (SmiVideoWin *)&smiVW; + + pVP->width = pGD->plnSizeX * pGD->gdfBytesPP; + pVP->control |= pGD->gdfIndex << 16; + pVWin->bottom = pGD->winSizeY - 1; + pVWin->right = pGD->winSizeX - 1; + pVWin->width = pVP->width; + + /* color key */ + out32r ((pGD->vprBase + 0x0004), pVP->colorKey); + + /* color key mask */ + out32r ((pGD->vprBase + 0x0008), pVP->colorKeyMask); + + /* data src start adrs */ + out32r ((pGD->vprBase + 0x000c), pVP->start / 8); + + /* data width and offset */ + out32r ((pGD->vprBase + 0x0010), + ((pVP->offset / 8 * pGD->gdfBytesPP) << 16) | + (pGD->plnSizeX / 8 * pGD->gdfBytesPP)); + + /* video window 1 */ + out32r ((pGD->vprBase + 0x0014), + ((pVWin->top << 16) | pVWin->left)); + + out32r ((pGD->vprBase + 0x0018), + ((pVWin->bottom << 16) | pVWin->right)); + + out32r ((pGD->vprBase + 0x001c), pVWin->srcStart / 8); + + out32r ((pGD->vprBase + 0x0020), + (((pVWin->offset / 8) << 16) | (pVWin->width / 8))); + + out32r ((pGD->vprBase + 0x0024), + (((pVWin->hStretch) << 8) | pVWin->vStretch)); + + /* video window 2 */ + out32r ((pGD->vprBase + 0x0028), + ((pVWin->top << 16) | pVWin->left)); + + out32r ((pGD->vprBase + 0x002c), + ((pVWin->bottom << 16) | pVWin->right)); + + out32r ((pGD->vprBase + 0x0030), + pVWin->srcStart / 8); + + out32r ((pGD->vprBase + 0x0034), + (((pVWin->offset / 8) << 16) | (pVWin->width / 8))); + + out32r ((pGD->vprBase + 0x0038), + (((pVWin->hStretch) << 8) | pVWin->vStretch)); + + /* fifo prio control */ + out32r ((pGD->vprBase + 0x0054), pVP->fifoPrio); + + /* fifo empty request levell */ + out32r ((pGD->vprBase + 0x0058), pVP->fifoERL); + + /* conversion constant */ + out32r ((pGD->vprBase + 0x005c), pVP->YUVtoRGB); + + /* vpr control word */ + out32r ((pGD->vprBase + 0x0000), pVP->control); +} + +/****************************************************************************** + * + * Init drawing engine registers + */ +static void smiInitDrawingEngine (void) +{ + GraphicDevice *pGD = (GraphicDevice *)&smi; + unsigned int val; + + /* don't start now */ + out32r ((pGD->dprBase + 0x000c), 0x000f0000); + + /* set rop2 to copypen */ + val = 0xffff3ff0 & in32r ((pGD->dprBase + 0x000c)); + out32r ((pGD->dprBase + 0x000c), (val | 0x8000 | 0x0c)); + + /* set clip rect */ + out32r ((pGD->dprBase + 0x002c), 0); + out32r ((pGD->dprBase + 0x0030), + ((pGD->winSizeY<<16) | pGD->winSizeX * pGD->gdfBytesPP )); + + /* src row pitch */ + val = 0xffff0000 & (in32r ((pGD->dprBase + 0x0010))); + out32r ((pGD->dprBase + 0x0010), + (val | pGD->plnSizeX * pGD->gdfBytesPP)); + + /* dst row pitch */ + val = 0x0000ffff & (in32r ((pGD->dprBase + 0x0010))); + out32r ((pGD->dprBase + 0x0010), + (((pGD->plnSizeX * pGD->gdfBytesPP)<<16) | val)); + + /* window width src/dst */ + out32r ((pGD->dprBase + 0x003c), + (((pGD->plnSizeX * pGD->gdfBytesPP & 0x0fff)<<16) | + (pGD->plnSizeX * pGD->gdfBytesPP & 0x0fff))); + out16r ((pGD->dprBase + 0x001e), 0x0000); + + /* src base adrs */ + out32r ((pGD->dprBase + 0x0040), + (((pGD->frameAdrs/8) & 0x000fffff))); + + /* dst base adrs */ + out32r ((pGD->dprBase + 0x0044), + (((pGD->frameAdrs/8) & 0x000fffff))); + + /* foreground color */ + out32r ((pGD->dprBase + 0x0014), pGD->fg); + + /* background color */ + out32r ((pGD->dprBase + 0x0018), pGD->bg); + + /* xcolor */ + out32r ((pGD->dprBase + 0x0020), 0x00ffffff); + + /* xcolor mask */ + out32r ((pGD->dprBase + 0x0024), 0x00ffffff); + + /* bit mask */ + out32r ((pGD->dprBase + 0x0028), 0x00ffffff); + + /* load mono pattern */ + out32r ((pGD->dprBase + 0x0034), 0); + out32r ((pGD->dprBase + 0x0038), 0); +} + +static struct pci_device_id supported[] = { + { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_710 }, + { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_712 }, + { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_810 }, + { } +}; + +/*****************************************************************************/ +static void smiLoadMsr (struct ctfb_res_modes *mode) +{ + unsigned char h_synch_high, v_synch_high; + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + h_synch_high = (mode->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x40; /* horizontal Synch High active */ + v_synch_high = (mode->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x80; /* vertical Synch High active */ + out8 (SMI_MISC_REG, (h_synch_high | v_synch_high | 0x29)); + /* upper64K==0x20, CLC2select==0x08, RAMenable==0x02!(todo), CGA==0x01 + * Selects the upper 64KB page.Bit5=1 + * CLK2 (left reserved in standard VGA) Bit3|2=1|0 + * Disables CPU access to frame buffer. Bit1=0 + * Sets the I/O address decode for ST01, FCR, and all CR registers + * to the 3Dx I/O address range (CGA emulation). Bit0=1 + */ +} +/*****************************************************************************/ +static void smiLoadCrt (struct ctfb_res_modes *var, int bits_per_pixel) +{ + unsigned char cr[0x7a]; + int i; + unsigned int hd, hs, he, ht, hbs, hbe; /* Horizontal. */ + unsigned int vd, vs, ve, vt, vbs, vbe; /* vertical */ + unsigned int bpp, wd, dblscan, interlaced; + + const int LineCompare = 0x3ff; + unsigned int TextScanLines = 1; /* this is in fact a vertical zoom factor */ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + /* Horizontal */ + hd = (var->xres) / 8; /* HDisp. */ + hs = (var->xres + var->right_margin) / 8; /* HsStrt */ + he = (var->xres + var->right_margin + var->hsync_len) / 8; /* HsEnd */ + ht = (var->left_margin + var->xres + var->right_margin + var->hsync_len) / 8; /* HTotal */ + /* Blank */ + hbs = hd; + hbe = 0; /* Blank end at 0 */ + + /* Vertical */ + vd = var->yres; /* VDisplay */ + vs = var->yres + var->lower_margin; /* VSyncStart */ + ve = var->yres + var->lower_margin + var->vsync_len; /* VSyncEnd */ + vt = var->upper_margin + var->yres + var->lower_margin + var->vsync_len; /* VTotal */ + vbs = vd; + vbe = 0; + + bpp = bits_per_pixel; + dblscan = (var->vmode & FB_VMODE_DOUBLE) ? 1 : 0; + interlaced = var->vmode & FB_VMODE_INTERLACED; + + + if (bpp == 15) + bpp = 16; + wd = var->xres * bpp / 64; /* double words per line */ + if (interlaced) { /* we divide all vertical timings, exept vd */ + vs >>= 1; + vbs >>= 1; + ve >>= 1; + vt >>= 1; + } + + memset (cr, 0, sizeof (cr)); + cr[0x00] = ht - 5; + cr[0x01] = hd - 1; + cr[0x02] = hbs - 1; + cr[0x03] = (hbe & 0x1F); + cr[0x04] = hs; + cr[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f); + + cr[0x06] = (vt - 2) & 0xFF; + cr[0x07] = (((vt - 2) & 0x100) >> 8) + | (((vd - 1) & 0x100) >> 7) + | ((vs & 0x100) >> 6) + | (((vbs - 1) & 0x100) >> 5) + | ((LineCompare & 0x100) >> 4) + | (((vt - 2) & 0x200) >> 4) + | (((vd - 1) & 0x200) >> 3) + | ((vs & 0x200) >> 2); + + cr[0x30] = ((vt - 2) & 0x400) >> 7 + | (((vd - 1) & 0x400) >> 8) + | (((vbs - 1) & 0x400) >> 9) + | ((vs & 0x400) >> 10) + | (interlaced) ? 0x80 : 0; + + + cr[0x08] = 0x00; + cr[0x09] = (dblscan << 7) + | ((LineCompare & 0x200) >> 3) + | (((vbs - 1) & 0x200) >> 4) + | (TextScanLines - 1); + + cr[0x10] = vs & 0xff; /* VSyncPulseStart */ + cr[0x11] = (ve & 0x0f); + cr[0x12] = (vd - 1) & 0xff; /* LineCount */ + cr[0x13] = wd & 0xff; + cr[0x14] = 0x40; + cr[0x15] = (vbs - 1) & 0xff; + cr[0x16] = vbe & 0xff; + cr[0x17] = 0xe3; /* but it does not work */ + cr[0x18] = 0xff & LineCompare; + cr[0x22] = 0x00; /* todo? */ + + + /* now set the registers */ + for (i = 0; i <= 0x18; i++) { /*CR00 .. CR18 */ + smiWrite (SMI_INDX_D4, i, cr[i]); + } + i = 0x22; /*CR22 */ + smiWrite (SMI_INDX_D4, i, cr[i]); + i = 0x30; /*CR30 */ + smiWrite (SMI_INDX_D4, i, cr[i]); +} + +/*****************************************************************************/ +#define REF_FREQ 14318180 +#define PMIN 1 +#define PMAX 255 +#define QMIN 1 +#define QMAX 63 + +static unsigned int FindPQ (unsigned int freq, unsigned int *pp, unsigned int *pq) +{ + unsigned int n = QMIN, m = 0; + long long int L = 0, P = freq, Q = REF_FREQ, H = P >> 1; + long long int D = 0x7ffffffffffffffLL; + + for (n = QMIN; n <= QMAX; n++) { + m = PMIN; /* p/q ~ freq/ref -> p*ref-freq*q ~ 0 */ + L = P * n - m * Q; + while (L > 0 && m < PMAX) { + L -= REF_FREQ; /* difference is greater as 0 subtract fref */ + m++; /* and increment m */ + } + /* difference is less or equal than 0 or m > maximum */ + if (m > PMAX) + break; /* no solution: if we increase n we get the same situation */ + /* L is <= 0 now */ + if (-L > H && m > PMIN) { /* if difference > the half fref */ + L += REF_FREQ; /* we take the situation before */ + m--; /* because its closer to 0 */ + } + L = (L < 0) ? -L : +L; /* absolute value */ + if (D < L) /* if last difference was better take next n */ + continue; + D = L; + *pp = m; + *pq = n; /* keep improved data */ + if (D == 0) + break; /* best result we can get */ + } + return (unsigned int) (0xffffffff & D); +} + +/*****************************************************************************/ +static void smiLoadCcr (struct ctfb_res_modes *var, unsigned short device_id) +{ + unsigned int p = 0; + unsigned int q = 0; + long long freq; + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + smiWrite (SMI_INDX_C4, 0x65, 0); + smiWrite (SMI_INDX_C4, 0x66, 0); + smiWrite (SMI_INDX_C4, 0x68, 0x50); + if (device_id == PCI_DEVICE_ID_SMI_810) { + smiWrite (SMI_INDX_C4, 0x69, 0x3); + } else { + smiWrite (SMI_INDX_C4, 0x69, 0x0); + } + + /* Memory clock */ + switch (device_id) { + case PCI_DEVICE_ID_SMI_710 : + smiWrite (SMI_INDX_C4, 0x6a, 0x75); + break; + case PCI_DEVICE_ID_SMI_712 : + smiWrite (SMI_INDX_C4, 0x6a, 0x80); + break; + default : + smiWrite (SMI_INDX_C4, 0x6a, 0x53); + break; + } + smiWrite (SMI_INDX_C4, 0x6b, 0x15); + + /* VCLK */ + freq = 1000000000000LL / var -> pixclock; + + FindPQ ((unsigned int)freq, &p, &q); + + smiWrite (SMI_INDX_C4, 0x6c, p); + smiWrite (SMI_INDX_C4, 0x6d, q); + +} + +/******************************************************************************* + * + * Init video chip with common Linux graphic modes (lilo) + */ +void *video_hw_init (void) +{ + GraphicDevice *pGD = (GraphicDevice *)&smi; + unsigned short device_id; + pci_dev_t devbusfn; + int videomode; + unsigned long t1, hsynch, vsynch; + unsigned int pci_mem_base, *vm; + char *penv; + int tmp, i, bits_per_pixel; + struct ctfb_res_modes *res_mode; + struct ctfb_res_modes var_mode; + unsigned char videoout; + + /* Search for video chip */ + printf("Video: "); + + if ((devbusfn = pci_find_devices(supported, 0)) < 0) + { + printf ("Controller not found !\n"); + return (NULL); + } + + /* PCI setup */ + pci_write_config_dword (devbusfn, PCI_COMMAND, (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)); + pci_read_config_word (devbusfn, PCI_DEVICE_ID, &device_id); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base); + pci_mem_base = pci_mem_to_phys (devbusfn, pci_mem_base); + + tmp = 0; + + videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE; + /* get video mode via environment */ + if ((penv = getenv ("videomode")) != NULL) { + /* deceide if it is a string */ + if (penv[0] <= '9') { + videomode = (int) simple_strtoul (penv, NULL, 16); + tmp = 1; + } + } else { + tmp = 1; + } + if (tmp) { + /* parameter are vesa modes */ + /* search params */ + for (i = 0; i < VESA_MODES_COUNT; i++) { + if (vesa_modes[i].vesanr == videomode) + break; + } + if (i == VESA_MODES_COUNT) { + printf ("no VESA Mode found, switching to mode 0x%x ", CONFIG_SYS_DEFAULT_VIDEO_MODE); + i = 0; + } + res_mode = + (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i]. + resindex]; + bits_per_pixel = vesa_modes[i].bits_per_pixel; + } else { + + res_mode = (struct ctfb_res_modes *) &var_mode; + bits_per_pixel = video_get_params (res_mode, penv); + } + + /* calculate hsynch and vsynch freq (info only) */ + t1 = (res_mode->left_margin + res_mode->xres + + res_mode->right_margin + res_mode->hsync_len) / 8; + t1 *= 8; + t1 *= res_mode->pixclock; + t1 /= 1000; + hsynch = 1000000000L / t1; + t1 *= + (res_mode->upper_margin + res_mode->yres + + res_mode->lower_margin + res_mode->vsync_len); + t1 /= 1000; + vsynch = 1000000000L / t1; + + /* fill in Graphic device struct */ + sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres, + res_mode->yres, bits_per_pixel, (hsynch / 1000), + (vsynch / 1000)); + printf ("%s\n", pGD->modeIdent); + pGD->winSizeX = res_mode->xres; + pGD->winSizeY = res_mode->yres; + pGD->plnSizeX = res_mode->xres; + pGD->plnSizeY = res_mode->yres; + switch (bits_per_pixel) { + case 8: + pGD->gdfBytesPP = 1; + pGD->gdfIndex = GDF__8BIT_INDEX; + break; + case 15: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_15BIT_555RGB; + break; + case 16: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_16BIT_565RGB; + break; + case 24: + pGD->gdfBytesPP = 3; + pGD->gdfIndex = GDF_24BIT_888RGB; + break; + } + + pGD->isaBase = CONFIG_SYS_ISA_IO; + pGD->pciBase = pci_mem_base; + pGD->dprBase = (pci_mem_base + 0x400000 + 0x8000); + pGD->vprBase = (pci_mem_base + 0x400000 + 0xc000); + pGD->cprBase = (pci_mem_base + 0x400000 + 0xe000); + pGD->frameAdrs = pci_mem_base; + pGD->memSize = VIDEO_MEM_SIZE; + + /* Set up hardware : select color mode, + set Register base to isa 3dx for 3?x regs*/ + out8 (SMI_MISC_REG, 0x01); + + /* Turn off display */ + smiWrite (SMI_INDX_C4, 0x01, 0x20); + + /* Unlock ext. crt regs */ + out8 (SMI_LOCK_REG, 0x40); + + /* Unlock crt regs 0-7 */ + smiWrite (SMI_INDX_D4, 0x11, 0x0e); + + /* Sytem Control Register */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_SCR, sizeof(SMI_SCR)); + + /* extented CRT Register */ + smiLoadRegs (SMI_INDX_D4, SMI_DATA_D5, SMI_EXT_CRT, sizeof(SMI_EXT_CRT)); + + /* Attributes controller registers */ + smiLoadRegs (SMI_INDX_ATTR, SMI_INDX_ATTR, SMI_ATTR, sizeof(SMI_ATTR)); + + /* Graphics Controller Register */ + smiLoadRegs (SMI_INDX_CE, SMI_DATA_CF, SMI_GCR, sizeof(SMI_GCR)); + + /* Sequencer Register */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_SEQR, sizeof(SMI_SEQR)); + + /* Power Control Register */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_PCR, sizeof(SMI_PCR)); + + /* Memory Control Register */ + /* Register MSR62 is a power on configurable register. We don't */ + /* modify it */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_MCR, sizeof(SMI_MCR)); + + /* Set misc output register */ + smiLoadMsr (res_mode); + + /* Set CRT and Clock control registers */ + smiLoadCrt (res_mode, bits_per_pixel); + + smiLoadCcr (res_mode, device_id); + + /* Hardware Cusor Register */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_HCR, sizeof(SMI_HCR)); + + /* Enable Display */ + videoout = 2; /* Default output is CRT */ + if ((penv = getenv ("videoout")) != NULL) { + /* deceide if it is a string */ + videoout = (int) simple_strtoul (penv, NULL, 16); + } + smiWrite (SMI_INDX_C4, 0x31, videoout); + + /* Video processor default setup */ + smiInitVideoProcessor (); + + /* Capture port default setup */ + smiInitCapturePort (); + + /* Drawing engine default setup */ + smiInitDrawingEngine (); + + /* Turn on display */ + smiWrite (0x3c4, 0x01, 0x01); + + /* Clear video memory */ + i = pGD->memSize/4; + vm = (unsigned int *)pGD->pciBase; + while(i--) + *vm++ = 0; + return ((void*)&smi); +} + +/******************************************************************************* + * + * Drawing engine fill on screen region + */ +void video_hw_rectfill ( + unsigned int bpp, /* bytes per pixel */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y, /* frame height */ + unsigned int color /* fill color */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + register unsigned int control; + + dim_x *= bpp; + + out32r ((pGD->dprBase + 0x0014), color); + out32r ((pGD->dprBase + 0x0004), ((dst_x<<16) | dst_y)); + out32r ((pGD->dprBase + 0x0008), ((dim_x<<16) | dim_y)); + + control = 0x0000ffff & in32r ((pGD->dprBase + 0x000c)); + + control |= 0x80010000; + + out32r ((pGD->dprBase + 0x000c), control); + + /* Wait for drawing processor */ + do + { + out8 ((pGD->isaBase + 0x3c4), 0x16); + } while (in8 (pGD->isaBase + 0x3c5) & 0x08); +} + +/******************************************************************************* + * + * Drawing engine bitblt with screen region + */ +void video_hw_bitblt ( + unsigned int bpp, /* bytes per pixel */ + unsigned int src_x, /* source pos x */ + unsigned int src_y, /* source pos y */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y /* frame height */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + register unsigned int control; + + dim_x *= bpp; + + if ((src_ydprBase + 0x0000), (((src_x+dim_x-1)<<16) | (src_y+dim_y-1))); + out32r ((pGD->dprBase + 0x0004), (((dst_x+dim_x-1)<<16) | (dst_y+dim_y-1))); + control = 0x88000000; + } else { + out32r ((pGD->dprBase + 0x0000), ((src_x<<16) | src_y)); + out32r ((pGD->dprBase + 0x0004), ((dst_x<<16) | dst_y)); + control = 0x80000000; + } + + out32r ((pGD->dprBase + 0x0008), ((dim_x<<16) | dim_y)); + control |= (0x0000ffff & in32r ((pGD->dprBase + 0x000c))); + out32r ((pGD->dprBase + 0x000c), control); + + /* Wait for drawing processor */ + do + { + out8 ((pGD->isaBase + 0x3c4), 0x16); + } while (in8 (pGD->isaBase + 0x3c5) & 0x08); +} + +/******************************************************************************* + * + * Set a RGB color in the LUT (8 bit index) + */ +void video_set_lut ( + unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + out8 (SMI_LUT_MASK, 0xff); + + out8 (SMI_LUT_START, (char)index); + + out8 (SMI_LUT_RGB, r>>2); /* red */ + udelay (10); + out8 (SMI_LUT_RGB, g>>2); /* green */ + udelay (10); + out8 (SMI_LUT_RGB, b>>2); /* blue */ + udelay (10); +} diff --git a/qemu/roms/u-boot/drivers/video/tegra.c b/qemu/roms/u-boot/drivers/video/tegra.c new file mode 100644 index 000000000..57cb0074e --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/tegra.c @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* These are the stages we go throuh in enabling the LCD */ +enum stage_t { + STAGE_START, + STAGE_PANEL_VDD, + STAGE_LVDS, + STAGE_BACKLIGHT_VDD, + STAGE_PWM, + STAGE_BACKLIGHT_EN, + STAGE_DONE, +}; + +static enum stage_t stage; /* Current stage we are at */ +static unsigned long timer_next; /* Time we can move onto next stage */ + +/* Our LCD config, set up in handle_stage() */ +static struct fdt_panel_config config; +struct fdt_disp_config *disp_config; /* Display controller config */ + +enum { + /* Maximum LCD size we support */ + LCD_MAX_WIDTH = 1366, + LCD_MAX_HEIGHT = 768, + LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */ +}; + +vidinfo_t panel_info = { + /* Insert a value here so that we don't end up in the BSS */ + .vl_col = -1, +}; + +#ifndef CONFIG_OF_CONTROL +#error "You must enable CONFIG_OF_CONTROL to get Tegra LCD support" +#endif + +static void update_panel_size(struct fdt_disp_config *config) +{ + panel_info.vl_col = config->width; + panel_info.vl_row = config->height; + panel_info.vl_bpix = config->log2_bpp; +} + +/* + * Main init function called by lcd driver. + * Inits and then prints test pattern if required. + */ + +void lcd_ctrl_init(void *lcdbase) +{ + int type = DCACHE_OFF; + int size; + + assert(disp_config); + + /* Make sure that we can acommodate the selected LCD */ + assert(disp_config->width <= LCD_MAX_WIDTH); + assert(disp_config->height <= LCD_MAX_HEIGHT); + assert(disp_config->log2_bpp <= LCD_MAX_LOG2_BPP); + if (disp_config->width <= LCD_MAX_WIDTH + && disp_config->height <= LCD_MAX_HEIGHT + && disp_config->log2_bpp <= LCD_MAX_LOG2_BPP) + update_panel_size(disp_config); + size = lcd_get_size(&lcd_line_length); + + /* Set up the LCD caching as requested */ + if (config.cache_type & FDT_LCD_CACHE_WRITE_THROUGH) + type = DCACHE_WRITETHROUGH; + else if (config.cache_type & FDT_LCD_CACHE_WRITE_BACK) + type = DCACHE_WRITEBACK; + mmu_set_region_dcache_behaviour(disp_config->frame_buffer, size, type); + + /* Enable flushing after LCD writes if requested */ + lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH); + + debug("LCD frame buffer at %08X\n", disp_config->frame_buffer); +} + +ulong calc_fbsize(void) +{ + return (panel_info.vl_col * panel_info.vl_row * + NBITS(panel_info.vl_bpix)) / 8; +} + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +} + +void tegra_lcd_early_init(const void *blob) +{ + /* + * Go with the maximum size for now. We will fix this up after + * relocation. These values are only used for memory alocation. + */ + panel_info.vl_col = LCD_MAX_WIDTH; + panel_info.vl_row = LCD_MAX_HEIGHT; + panel_info.vl_bpix = LCD_MAX_LOG2_BPP; +} + +/** + * Decode the panel information from the fdt. + * + * @param blob fdt blob + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config) +{ + int display_node; + + disp_config = tegra_display_get_config(); + if (!disp_config) { + debug("%s: Display controller is not configured\n", __func__); + return -1; + } + display_node = disp_config->panel_node; + if (display_node < 0) { + debug("%s: No panel configuration available\n", __func__); + return -1; + } + + config->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm"); + if (config->pwm_channel < 0) { + debug("%s: Unable to request PWM channel\n", __func__); + return -1; + } + + config->cache_type = fdtdec_get_int(blob, display_node, + "nvidia,cache-type", + FDT_LCD_CACHE_WRITE_BACK_FLUSH); + + /* These GPIOs are all optional */ + fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-enable-gpios", + &config->backlight_en); + fdtdec_decode_gpio(blob, display_node, "nvidia,lvds-shutdown-gpios", + &config->lvds_shutdown); + fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-vdd-gpios", + &config->backlight_vdd); + fdtdec_decode_gpio(blob, display_node, "nvidia,panel-vdd-gpios", + &config->panel_vdd); + + return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings", + config->panel_timings, FDT_LCD_TIMINGS); +} + +/** + * Handle the next stage of device init + */ +static int handle_stage(const void *blob) +{ + debug("%s: stage %d\n", __func__, stage); + + /* do the things for this stage */ + switch (stage) { + case STAGE_START: + /* Initialize the Tegra display controller */ + if (tegra_display_probe(gd->fdt_blob, (void *)gd->fb_base)) { + printf("%s: Failed to probe display driver\n", + __func__); + return -1; + } + + /* get panel details */ + if (fdt_decode_lcd(blob, &config)) { + printf("No valid LCD information in device tree\n"); + return -1; + } + + /* + * It is possible that the FDT has requested that the LCD be + * disabled. We currently don't support this. It would require + * changes to U-Boot LCD subsystem to have LCD support + * compiled in but not used. An easier option might be to + * still have a frame buffer, but leave the backlight off and + * remove all mention of lcd in the stdout environment + * variable. + */ + + funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); + + fdtdec_setup_gpio(&config.panel_vdd); + fdtdec_setup_gpio(&config.lvds_shutdown); + fdtdec_setup_gpio(&config.backlight_vdd); + fdtdec_setup_gpio(&config.backlight_en); + + /* + * TODO: If fdt includes output flag we can omit this code + * since fdtdec_setup_gpio will do it for us. + */ + if (fdt_gpio_isvalid(&config.panel_vdd)) + gpio_direction_output(config.panel_vdd.gpio, 0); + if (fdt_gpio_isvalid(&config.lvds_shutdown)) + gpio_direction_output(config.lvds_shutdown.gpio, 0); + if (fdt_gpio_isvalid(&config.backlight_vdd)) + gpio_direction_output(config.backlight_vdd.gpio, 0); + if (fdt_gpio_isvalid(&config.backlight_en)) + gpio_direction_output(config.backlight_en.gpio, 0); + break; + case STAGE_PANEL_VDD: + if (fdt_gpio_isvalid(&config.panel_vdd)) + gpio_direction_output(config.panel_vdd.gpio, 1); + break; + case STAGE_LVDS: + if (fdt_gpio_isvalid(&config.lvds_shutdown)) + gpio_set_value(config.lvds_shutdown.gpio, 1); + break; + case STAGE_BACKLIGHT_VDD: + if (fdt_gpio_isvalid(&config.backlight_vdd)) + gpio_set_value(config.backlight_vdd.gpio, 1); + break; + case STAGE_PWM: + /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ + pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); + pinmux_tristate_disable(PMUX_PINGRP_GPU); + + pwm_enable(config.pwm_channel, 32768, 0xdf, 1); + break; + case STAGE_BACKLIGHT_EN: + if (fdt_gpio_isvalid(&config.backlight_en)) + gpio_set_value(config.backlight_en.gpio, 1); + break; + case STAGE_DONE: + break; + } + + /* set up timer for next stage */ + timer_next = timer_get_us(); + if (stage < FDT_LCD_TIMINGS) + timer_next += config.panel_timings[stage] * 1000; + + /* move to next stage */ + stage++; + return 0; +} + +int tegra_lcd_check_next_stage(const void *blob, int wait) +{ + if (stage == STAGE_DONE) + return 0; + + do { + /* wait if we need to */ + debug("%s: stage %d\n", __func__, stage); + if (stage != STAGE_START) { + int delay = timer_next - timer_get_us(); + + if (delay > 0) { + if (wait) + udelay(delay); + else + return 0; + } + } + + if (handle_stage(blob)) + return -1; + } while (wait && stage != STAGE_DONE); + if (stage == STAGE_DONE) + debug("%s: LCD init complete\n", __func__); + + return 0; +} + +void lcd_enable(void) +{ + /* + * Backlight and power init will be done separately in + * tegra_lcd_check_next_stage(), which should be called in + * board_late_init(). + * + * U-Boot code supports only colour depth, selected at compile time. + * The device tree setting should match this. Otherwise the display + * will not look right, and U-Boot may crash. + */ + if (disp_config->log2_bpp != LCD_BPP) { + printf("%s: Error: LCD depth configured in FDT (%d = %dbpp)" + " must match setting of LCD_BPP (%d)\n", __func__, + disp_config->log2_bpp, disp_config->bpp, LCD_BPP); + } +} diff --git a/qemu/roms/u-boot/drivers/video/videomodes.c b/qemu/roms/u-boot/drivers/video/videomodes.c new file mode 100644 index 000000000..18c1f3d8a --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/videomodes.c @@ -0,0 +1,262 @@ +/* + * (C) Copyright 2004 + * Pierre Aubert, Staubli Faverges , + * Copyright 2011 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/************************************************************************ + Get Parameters for the video mode: + The default video mode can be defined in CONFIG_SYS_DEFAULT_VIDEO_MODE. + If undefined, default video mode is set to 0x301 + Parameters can be set via the variable "videomode" in the environment. + 2 diferent ways are possible: + "videomode=301" - 301 is a hexadecimal number describing the VESA + mode. Following modes are implemented: + + Colors 640x480 800x600 1024x768 1152x864 1280x1024 + --------+--------------------------------------------- + 8 bits | 0x301 0x303 0x305 0x161 0x307 + 15 bits | 0x310 0x313 0x316 0x162 0x319 + 16 bits | 0x311 0x314 0x317 0x163 0x31A + 24 bits | 0x312 0x315 0x318 ? 0x31B + --------+--------------------------------------------- + "videomode=bootargs" + - the parameters are parsed from the bootargs. + The format is "NAME:VALUE,NAME:VALUE" etc. + Ex.: + "bootargs=video=ctfb:x:800,y:600,depth:16,pclk:25000" + Parameters not included in the list will be taken from + the default mode, which is one of the following: + mode:0 640x480x24 + mode:1 800x600x16 + mode:2 1024x768x8 + mode:3 960x720x24 + mode:4 1152x864x16 + mode:5 1280x1024x8 + + if "mode" is not provided within the parameter list, + mode:0 is assumed. + Following parameters are supported: + x xres = visible resolution horizontal + y yres = visible resolution vertical + pclk pixelclocks in pico sec + le left_marging time from sync to picture in pixelclocks + ri right_marging time from picture to sync in pixelclocks + up upper_margin time from sync to picture + lo lower_margin + hs hsync_len length of horizontal sync + vs vsync_len length of vertical sync + sync see FB_SYNC_* + vmode see FB_VMODE_* + depth Color depth in bits per pixel + All other parameters in the variable bootargs are ignored. + It is also possible to set the parameters direct in the + variable "videomode", or in another variable i.e. + "myvideo" and setting the variable "videomode=myvideo".. +****************************************************************************/ + +#include +#include + +#include "videomodes.h" + +const struct ctfb_vesa_modes vesa_modes[VESA_MODES_COUNT] = { + {0x301, RES_MODE_640x480, 8}, + {0x310, RES_MODE_640x480, 15}, + {0x311, RES_MODE_640x480, 16}, + {0x312, RES_MODE_640x480, 24}, + {0x303, RES_MODE_800x600, 8}, + {0x313, RES_MODE_800x600, 15}, + {0x314, RES_MODE_800x600, 16}, + {0x315, RES_MODE_800x600, 24}, + {0x305, RES_MODE_1024x768, 8}, + {0x316, RES_MODE_1024x768, 15}, + {0x317, RES_MODE_1024x768, 16}, + {0x318, RES_MODE_1024x768, 24}, + {0x161, RES_MODE_1152x864, 8}, + {0x162, RES_MODE_1152x864, 15}, + {0x163, RES_MODE_1152x864, 16}, + {0x307, RES_MODE_1280x1024, 8}, + {0x319, RES_MODE_1280x1024, 15}, + {0x31A, RES_MODE_1280x1024, 16}, + {0x31B, RES_MODE_1280x1024, 24}, +}; +const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { + /* x y pixclk le ri up lo hs vs s vmode */ + {640, 480, 39721, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED}, + {800, 600, 27778, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED}, + {1024, 768, 15384, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED}, + {960, 720, 13100, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED}, + {1152, 864, 12004, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED}, + {1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED}, +}; + +/************************************************************************ + * Get Parameters for the video mode: + */ +/********************************************************************* + * returns the length to the next seperator + */ +static int +video_get_param_len (char *start, char sep) +{ + int i = 0; + while ((*start != 0) && (*start != sep)) { + start++; + i++; + } + return i; +} + +static int +video_search_param (char *start, char *param) +{ + int len, totallen, i; + char *p = start; + len = strlen (param); + totallen = len + strlen (start); + for (i = 0; i < totallen; i++) { + if (strncmp (p++, param, len) == 0) + return (i); + } + return -1; +} + +/*************************************************************** + * Get parameter via the environment as it is done for the + * linux kernel i.e: + * video=ctfb:x:800,xv:1280,y:600,yv:1024,depth:16,mode:0,pclk:25000, + * le:56,ri:48,up:26,lo:5,hs:152,vs:2,sync:0,vmode:0,accel:0 + * + * penv is a pointer to the environment, containing the string, or the name of + * another environment variable. It could even be the term "bootargs" + */ + +#define GET_OPTION(name,var) \ + if(strncmp(p,name,strlen(name))==0) { \ + val_s=p+strlen(name); \ + var=simple_strtoul(val_s, NULL, 10); \ + } + +int video_get_params (struct ctfb_res_modes *pPar, char *penv) +{ + char *p, *s, *val_s; + int i = 0; + int bpp; + int mode; + + /* first search for the environment containing the real param string */ + s = penv; + + if ((p = getenv (s)) != NULL) + s = p; + + /* + * in case of the bootargs line, we have to start + * after "video=ctfb:" + */ + i = video_search_param (s, "video=ctfb:"); + if (i >= 0) { + s += i; + s += strlen ("video=ctfb:"); + } + /* search for mode as a default value */ + p = s; + mode = 0; /* default */ + + while ((i = video_get_param_len (p, ',')) != 0) { + GET_OPTION ("mode:", mode) + p += i; + if (*p != 0) + p++; /* skip ',' */ + } + + if (mode >= RES_MODES_COUNT) + mode = 0; + + *pPar = res_mode_init[mode]; /* copy default values */ + bpp = 24 - ((mode % 3) * 8); + p = s; /* restart */ + + while ((i = video_get_param_len (p, ',')) != 0) { + GET_OPTION ("x:", pPar->xres) + GET_OPTION ("y:", pPar->yres) + GET_OPTION ("le:", pPar->left_margin) + GET_OPTION ("ri:", pPar->right_margin) + GET_OPTION ("up:", pPar->upper_margin) + GET_OPTION ("lo:", pPar->lower_margin) + GET_OPTION ("hs:", pPar->hsync_len) + GET_OPTION ("vs:", pPar->vsync_len) + GET_OPTION ("sync:", pPar->sync) + GET_OPTION ("vmode:", pPar->vmode) + GET_OPTION ("pclk:", pPar->pixclock) + GET_OPTION ("depth:", bpp) + p += i; + if (*p != 0) + p++; /* skip ',' */ + } + return bpp; +} + +/* + * Parse the 'video-mode' environment variable + * + * Example: "video-mode=fslfb:1280x1024-32@60,monitor=dvi". See + * doc/README.video for more information on how to set the variable. + * + * @xres: returned value of X-resolution + * @yres: returned value of Y-resolution + * @depth: returned value of color depth + * @freq: returned value of monitor frequency + * @options: pointer to any remaining options, or NULL + * + * Returns 1 if valid values were found, 0 otherwise + */ +int video_get_video_mode(unsigned int *xres, unsigned int *yres, + unsigned int *depth, unsigned int *freq, const char **options) +{ + char *p = getenv("video-mode"); + if (!p) + return 0; + + /* Skip over the driver name, which we don't care about. */ + p = strchr(p, ':'); + if (!p) + return 0; + + /* Get the X-resolution*/ + while (*p && !isdigit(*p)) + p++; + *xres = simple_strtoul(p, &p, 10); + if (!*xres) + return 0; + + /* Get the Y-resolution */ + while (*p && !isdigit(*p)) + p++; + *yres = simple_strtoul(p, &p, 10); + if (!*yres) + return 0; + + /* Get the depth */ + while (*p && !isdigit(*p)) + p++; + *depth = simple_strtoul(p, &p, 10); + if (!*depth) + return 0; + + /* Get the frequency */ + while (*p && !isdigit(*p)) + p++; + *freq = simple_strtoul(p, &p, 10); + if (!*freq) + return 0; + + /* Find the extra options, if any */ + p = strchr(p, ','); + *options = p ? p + 1 : NULL; + + return 1; +} diff --git a/qemu/roms/u-boot/drivers/video/videomodes.h b/qemu/roms/u-boot/drivers/video/videomodes.h new file mode 100644 index 000000000..d83993a56 --- /dev/null +++ b/qemu/roms/u-boot/drivers/video/videomodes.h @@ -0,0 +1,75 @@ +/* + * (C) Copyright 2004 + * Pierre Aubert, Staubli Faverges , + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#ifndef CONFIG_SYS_DEFAULT_VIDEO_MODE +#define CONFIG_SYS_DEFAULT_VIDEO_MODE 0x301 +#endif + +/* Some mode definitions */ +#define FB_SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */ +#define FB_SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */ +#define FB_SYNC_EXT 4 /* external sync */ +#define FB_SYNC_COMP_HIGH_ACT 8 /* composite sync high active */ +#define FB_SYNC_BROADCAST 16 /* broadcast video timings */ + /* vtotal = 144d/288n/576i => PAL */ + /* vtotal = 121d/242n/484i => NTSC */ +#define FB_SYNC_ON_GREEN 32 /* sync on green */ +#define FB_VMODE_NONINTERLACED 0 /* non interlaced */ +#define FB_VMODE_INTERLACED 1 /* interlaced */ +#define FB_VMODE_DOUBLE 2 /* double scan */ +#define FB_VMODE_MASK 255 + +#define FB_VMODE_YWRAP 256 /* ywrap instead of panning */ +#define FB_VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */ +#define FB_VMODE_CONUPDATE 512 /* don't update x/yoffset */ + + +/****************************************************************** + * Resolution Struct + ******************************************************************/ +struct ctfb_res_modes { + int xres; /* visible resolution */ + int yres; + /* Timing: All values in pixclocks, except pixclock (of course) */ + int pixclock; /* pixel clock in ps (pico seconds) */ + int left_margin; /* time from sync to picture */ + int right_margin; /* time from picture to sync */ + int upper_margin; /* time from sync to picture */ + int lower_margin; + int hsync_len; /* length of horizontal sync */ + int vsync_len; /* length of vertical sync */ + int sync; /* see FB_SYNC_* */ + int vmode; /* see FB_VMODE_* */ +}; + +/****************************************************************** + * Vesa Mode Struct + ******************************************************************/ +struct ctfb_vesa_modes { + int vesanr; /* Vesa number as in LILO (VESA Nr + 0x200} */ + int resindex; /* index to resolution struct */ + int bits_per_pixel; /* bpp */ +}; + +#define RES_MODE_640x480 0 +#define RES_MODE_800x600 1 +#define RES_MODE_1024x768 2 +#define RES_MODE_960_720 3 +#define RES_MODE_1152x864 4 +#define RES_MODE_1280x1024 5 +#define RES_MODES_COUNT 6 + +#define VESA_MODES_COUNT 19 + +extern const struct ctfb_vesa_modes vesa_modes[]; +extern const struct ctfb_res_modes res_mode_init[]; + +int video_get_params (struct ctfb_res_modes *pPar, char *penv); + +int video_get_video_mode(unsigned int *xres, unsigned int *yres, + unsigned int *depth, unsigned int *freq, const char **options); diff --git a/qemu/roms/u-boot/drivers/watchdog/Makefile b/qemu/roms/u-boot/drivers/watchdog/Makefile new file mode 100644 index 000000000..06ced10c3 --- /dev/null +++ b/qemu/roms/u-boot/drivers/watchdog/Makefile @@ -0,0 +1,17 @@ +# +# (C) Copyright 2008 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_AT91SAM9_WATCHDOG) += at91sam9_wdt.o +obj-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o +ifneq (,$(filter $(SOC), mx31 mx35 mx5 mx6 vf610)) +obj-y += imx_watchdog.o +endif +obj-$(CONFIG_TNETV107X_WATCHDOG) += tnetv107x_wdt.o +obj-$(CONFIG_S5P) += s5p_wdt.o +obj-$(CONFIG_XILINX_TB_WATCHDOG) += xilinx_tb_wdt.o +obj-$(CONFIG_BFIN_WATCHDOG) += bfin_wdt.o +obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o diff --git a/qemu/roms/u-boot/drivers/watchdog/at91sam9_wdt.c b/qemu/roms/u-boot/drivers/watchdog/at91sam9_wdt.c new file mode 100644 index 000000000..ffd49a2be --- /dev/null +++ b/qemu/roms/u-boot/drivers/watchdog/at91sam9_wdt.c @@ -0,0 +1,77 @@ +/* + * [origin: Linux kernel drivers/watchdog/at91sam9_wdt.c] + * + * Watchdog driver for Atmel AT91SAM9x processors. + * + * Copyright (C) 2008 Jean-Christophe PLAGNIOL-VILLARD + * Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * The Watchdog Timer Mode Register can be only written to once. If the + * timeout need to be set from U-Boot, be sure that the bootstrap doesn't + * write to this register. Inform Linux to it too + */ + +#include +#include +#include +#include +#include + +/* + * AT91SAM9 watchdog runs a 12bit counter @ 256Hz, + * use this to convert a watchdog + * value from/to milliseconds. + */ +#define ms_to_ticks(t) (((t << 8) / 1000) - 1) +#define ticks_to_ms(t) (((t + 1) * 1000) >> 8) + +/* Hardware timeout in seconds */ +#define WDT_HW_TIMEOUT 2 + +/* + * Set the watchdog time interval in 1/256Hz (write-once) + * Counter is 12 bit. + */ +static int at91_wdt_settimeout(unsigned int timeout) +{ + unsigned int reg; + at91_wdt_t *wd = (at91_wdt_t *) ATMEL_BASE_WDT; + + /* Check if disabled */ + if (readl(&wd->mr) & AT91_WDT_MR_WDDIS) { + printf("sorry, watchdog is disabled\n"); + return -1; + } + + /* + * All counting occurs at SLOW_CLOCK / 128 = 256 Hz + * + * Since WDV is a 12-bit counter, the maximum period is + * 4096 / 256 = 16 seconds. + */ + + reg = AT91_WDT_MR_WDRSTEN /* causes watchdog reset */ + | AT91_WDT_MR_WDDBGHLT /* disabled in debug mode */ + | AT91_WDT_MR_WDD(0xfff) /* restart at any time */ + | AT91_WDT_MR_WDV(timeout); /* timer value */ + + writel(reg, &wd->mr); + + return 0; +} + +void hw_watchdog_reset(void) +{ + at91_wdt_t *wd = (at91_wdt_t *) ATMEL_BASE_WDT; + writel(AT91_WDT_CR_WDRSTT | AT91_WDT_CR_KEY, &wd->cr); +} + +void hw_watchdog_init(void) +{ + /* 16 seconds timer, resets enabled */ + at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000)); +} diff --git a/qemu/roms/u-boot/drivers/watchdog/bfin_wdt.c b/qemu/roms/u-boot/drivers/watchdog/bfin_wdt.c new file mode 100644 index 000000000..7a6756b2e --- /dev/null +++ b/qemu/roms/u-boot/drivers/watchdog/bfin_wdt.c @@ -0,0 +1,26 @@ +/* + * watchdog.c - driver for Blackfin on-chip watchdog + * + * Copyright (c) 2007-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include + +void hw_watchdog_reset(void) +{ + bfin_write_WDOG_STAT(0); +} + +void hw_watchdog_init(void) +{ + bfin_write_WDOG_CTL(WDDIS); + SSYNC(); + bfin_write_WDOG_CNT(CONFIG_WATCHDOG_TIMEOUT_MSECS / 1000 * get_sclk()); + hw_watchdog_reset(); + bfin_write_WDOG_CTL(WDEN); +} diff --git a/qemu/roms/u-boot/drivers/watchdog/ftwdt010_wdt.c b/qemu/roms/u-boot/drivers/watchdog/ftwdt010_wdt.c new file mode 100644 index 000000000..11f58f529 --- /dev/null +++ b/qemu/roms/u-boot/drivers/watchdog/ftwdt010_wdt.c @@ -0,0 +1,92 @@ +/* + * Watchdog driver for the FTWDT010 Watch Dog Driver + * + * (c) Copyright 2004 Faraday Technology Corp. (www.faraday-tech.com) + * Based on sa1100_wdt.c by Oleg Drokin + * Based on SoftDog driver by Alan Cox + * + * Copyright (C) 2011 Andes Technology Corporation + * Macpaul Lin, Andes Technology Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + * + * 27/11/2004 Initial release, Faraday. + * 12/01/2011 Port to u-boot, Macpaul Lin. + */ + +#include +#include +#include +#include + +/* + * Set the watchdog time interval. + * Counter is 32 bit. + */ +int ftwdt010_wdt_settimeout(unsigned int timeout) +{ + unsigned int reg; + + struct ftwdt010_wdt *wd = (struct ftwdt010_wdt *)CONFIG_FTWDT010_BASE; + + debug("Activating WDT..\n"); + + /* Check if disabled */ + if (readl(&wd->wdcr) & ~FTWDT010_WDCR_ENABLE) { + printf("sorry, watchdog is disabled\n"); + return -1; + } + + /* + * In a 66MHz system, + * if you set WDLOAD as 0x03EF1480 (66000000) + * the reset timer is 1 second. + */ + reg = FTWDT010_WDLOAD(timeout * FTWDT010_TIMEOUT_FACTOR); + + writel(reg, &wd->wdload); + + return 0; +} + +void ftwdt010_wdt_reset(void) +{ + struct ftwdt010_wdt *wd = (struct ftwdt010_wdt *)CONFIG_FTWDT010_BASE; + + /* clear control register */ + writel(0, &wd->wdcr); + + /* Write Magic number */ + writel(FTWDT010_WDRESTART_MAGIC, &wd->wdrestart); + + /* Enable WDT */ + writel((FTWDT010_WDCR_RST | FTWDT010_WDCR_ENABLE), &wd->wdcr); +} + +void ftwdt010_wdt_disable(void) +{ + struct ftwdt010_wdt *wd = (struct ftwdt010_wdt *)CONFIG_FTWDT010_BASE; + + debug("Deactivating WDT..\n"); + + /* + * It was defined with CONFIG_WATCHDOG_NOWAYOUT in Linux + * + * Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT + */ + writel(0, &wd->wdcr); +} + +#if defined(CONFIG_HW_WATCHDOG) +void hw_watchdog_reset(void) +{ + ftwdt010_wdt_reset(); +} + +void hw_watchdog_init(void) +{ + /* set timer in ms */ + ftwdt010_wdt_settimeout(CONFIG_FTWDT010_HW_TIMEOUT * 1000); +} +#endif diff --git a/qemu/roms/u-boot/drivers/watchdog/imx_watchdog.c b/qemu/roms/u-boot/drivers/watchdog/imx_watchdog.c new file mode 100644 index 000000000..d5993b4d2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/watchdog/imx_watchdog.c @@ -0,0 +1,67 @@ +/* + * watchdog.c - driver for i.mx on-chip watchdog + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include + +struct watchdog_regs { + u16 wcr; /* Control */ + u16 wsr; /* Service */ + u16 wrsr; /* Reset Status */ +}; + +#define WCR_WDZST 0x01 +#define WCR_WDBG 0x02 +#define WCR_WDE 0x04 /* WDOG enable */ +#define WCR_WDT 0x08 +#define WCR_SRS 0x10 +#define WCR_WDW 0x80 +#define SET_WCR_WT(x) (x << 8) + +#ifdef CONFIG_IMX_WATCHDOG +void hw_watchdog_reset(void) +{ + struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR; + + writew(0x5555, &wdog->wsr); + writew(0xaaaa, &wdog->wsr); +} + +void hw_watchdog_init(void) +{ + struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR; + u16 timeout; + + /* + * The timer watchdog can be set between + * 0.5 and 128 Seconds. If not defined + * in configuration file, sets 128 Seconds + */ +#ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS +#define CONFIG_WATCHDOG_TIMEOUT_MSECS 128000 +#endif + timeout = (CONFIG_WATCHDOG_TIMEOUT_MSECS / 500) - 1; + writew(WCR_WDZST | WCR_WDBG | WCR_WDE | WCR_WDT | WCR_SRS | + WCR_WDW | SET_WCR_WT(timeout), &wdog->wcr); + hw_watchdog_reset(); +} +#endif + +void reset_cpu(ulong addr) +{ + struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR; + + writew(WCR_WDE, &wdog->wcr); + writew(0x5555, &wdog->wsr); + writew(0xaaaa, &wdog->wsr); /* load minimum 1/2 second timeout */ + while (1) { + /* + * spin for .5 seconds before reset + */ + } +} diff --git a/qemu/roms/u-boot/drivers/watchdog/omap_wdt.c b/qemu/roms/u-boot/drivers/watchdog/omap_wdt.c new file mode 100644 index 000000000..7ea4b604c --- /dev/null +++ b/qemu/roms/u-boot/drivers/watchdog/omap_wdt.c @@ -0,0 +1,121 @@ +/* + * omap_wdt.c + * + * (C) Copyright 2013 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * SPDX-License-Identifier: GPL-2.0 + * + * Based on: + * + * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog + * + * commit 2d991a164a61858012651e13c59521975504e260 + * Author: Bill Pemberton + * Date: Mon Nov 19 13:21:41 2012 -0500 + * + * watchdog: remove use of __devinit + * + * CONFIG_HOTPLUG is going away as an option so __devinit is no longer + * needed. + * + * Author: MontaVista Software, Inc. + * or + * + * History: + * + * 20030527: George G. Davis + * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c + * (c) Copyright 2000 Oleg Drokin + * Based on SoftDog driver by Alan Cox + * + * Copyright (c) 2004 Texas Instruments. + * 1. Modified to support OMAP1610 32-KHz watchdog timer + * 2. Ported to 2.6 kernel + * + * Copyright (c) 2005 David Brownell + * Use the driver model and standard identifiers; handle bigger timeouts. + */ + +#include +#include +#include +#include +#include +#include + +/* Hardware timeout in seconds */ +#define WDT_HW_TIMEOUT 60 + +static unsigned int wdt_trgr_pattern = 0x1234; + +void hw_watchdog_reset(void) +{ + struct wd_timer *wdt = (struct wd_timer *)WDT_BASE; + + /* wait for posted write to complete */ + while ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WTGR) + ; + + wdt_trgr_pattern = ~wdt_trgr_pattern; + writel(wdt_trgr_pattern, &wdt->wdtwtgr); + + /* wait for posted write to complete */ + while ((readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WTGR)) + ; +} + +static int omap_wdt_set_timeout(unsigned int timeout) +{ + struct wd_timer *wdt = (struct wd_timer *)WDT_BASE; + u32 pre_margin = GET_WLDR_VAL(timeout); + + /* just count up at 32 KHz */ + while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WLDR) + ; + + writel(pre_margin, &wdt->wdtwldr); + while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WLDR) + ; + + return 0; +} + +void hw_watchdog_init(void) +{ + struct wd_timer *wdt = (struct wd_timer *)WDT_BASE; + + /* initialize prescaler */ + while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WCLR) + ; + + writel(WDT_WCLR_PRE | (PTV << WDT_WCLR_PTV_OFF), &wdt->wdtwclr); + while (readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WCLR) + ; + + omap_wdt_set_timeout(WDT_HW_TIMEOUT); + + /* Sequence to enable the watchdog */ + writel(0xBBBB, &wdt->wdtwspr); + while ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WSPR) + ; + + writel(0x4444, &wdt->wdtwspr); + while ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WSPR) + ; +} + +void hw_watchdog_disable(void) +{ + struct wd_timer *wdt = (struct wd_timer *)WDT_BASE; + + /* + * Disable watchdog + */ + writel(0xAAAA, &wdt->wdtwspr); + while (readl(&wdt->wdtwwps) != 0x0) + ; + writel(0x5555, &wdt->wdtwspr); + while (readl(&wdt->wdtwwps) != 0x0) + ; +} diff --git a/qemu/roms/u-boot/drivers/watchdog/s5p_wdt.c b/qemu/roms/u-boot/drivers/watchdog/s5p_wdt.c new file mode 100644 index 000000000..a6e54d9f7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/watchdog/s5p_wdt.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Minkyu Kang + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define PRESCALER_VAL 255 + +void wdt_stop(void) +{ + struct s5p_watchdog *wdt = + (struct s5p_watchdog *)samsung_get_base_watchdog(); + unsigned int wtcon; + + wtcon = readl(&wdt->wtcon); + wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET); + + writel(wtcon, &wdt->wtcon); +} + +void wdt_start(unsigned int timeout) +{ + struct s5p_watchdog *wdt = + (struct s5p_watchdog *)samsung_get_base_watchdog(); + unsigned int wtcon; + + wdt_stop(); + + wtcon = readl(&wdt->wtcon); + wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128)); + wtcon &= ~WTCON_INT; + wtcon |= WTCON_RESET; + wtcon |= WTCON_PRESCALER(PRESCALER_VAL); + + writel(timeout, &wdt->wtdat); + writel(timeout, &wdt->wtcnt); + writel(wtcon, &wdt->wtcon); +} diff --git a/qemu/roms/u-boot/drivers/watchdog/tnetv107x_wdt.c b/qemu/roms/u-boot/drivers/watchdog/tnetv107x_wdt.c new file mode 100644 index 000000000..3d3f366c0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/watchdog/tnetv107x_wdt.c @@ -0,0 +1,165 @@ +/* + * TNETV107X: Watchdog timer implementation (for reset) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define MAX_DIV 0xFFFE0001 + +struct wdt_regs { + u32 kick_lock; +#define KICK_LOCK_1 0x5555 +#define KICK_LOCK_2 0xaaaa + u32 kick; + + u32 change_lock; +#define CHANGE_LOCK_1 0x6666 +#define CHANGE_LOCK_2 0xbbbb + u32 change; + + u32 disable_lock; +#define DISABLE_LOCK_1 0x7777 +#define DISABLE_LOCK_2 0xcccc +#define DISABLE_LOCK_3 0xdddd + u32 disable; + + u32 prescale_lock; +#define PRESCALE_LOCK_1 0x5a5a +#define PRESCALE_LOCK_2 0xa5a5 + u32 prescale; +}; + +static struct wdt_regs* regs = (struct wdt_regs *)TNETV107X_WDT0_ARM_BASE; + +#define wdt_reg_read(reg) __raw_readl(®s->reg) +#define wdt_reg_write(reg, val) __raw_writel((val), ®s->reg) + +static int write_prescale_reg(unsigned long prescale_value) +{ + wdt_reg_write(prescale_lock, PRESCALE_LOCK_1); + if ((wdt_reg_read(prescale_lock) & 0x3) != 0x1) + return -1; + + wdt_reg_write(prescale_lock, PRESCALE_LOCK_2); + if ((wdt_reg_read(prescale_lock) & 0x3) != 0x3) + return -1; + + wdt_reg_write(prescale, prescale_value); + + return 0; +} + +static int write_change_reg(unsigned long initial_timer_value) +{ + wdt_reg_write(change_lock, CHANGE_LOCK_1); + if ((wdt_reg_read(change_lock) & 0x3) != 0x1) + return -1; + + wdt_reg_write(change_lock, CHANGE_LOCK_2); + if ((wdt_reg_read(change_lock) & 0x3) != 0x3) + return -1; + + wdt_reg_write(change, initial_timer_value); + + return 0; +} + +static int wdt_control(unsigned long disable_value) +{ + wdt_reg_write(disable_lock, DISABLE_LOCK_1); + if ((wdt_reg_read(disable_lock) & 0x3) != 0x1) + return -1; + + wdt_reg_write(disable_lock, DISABLE_LOCK_2); + if ((wdt_reg_read(disable_lock) & 0x3) != 0x2) + return -1; + + wdt_reg_write(disable_lock, DISABLE_LOCK_3); + if ((wdt_reg_read(disable_lock) & 0x3) != 0x3) + return -1; + + wdt_reg_write(disable, disable_value); + return 0; +} + +static int wdt_set_period(unsigned long msec) +{ + unsigned long change_value, count_value; + unsigned long prescale_value = 1; + unsigned long refclk_khz, maxdiv; + int ret; + + refclk_khz = clk_get_rate(TNETV107X_LPSC_WDT_ARM); + maxdiv = (MAX_DIV / refclk_khz); + + if ((!msec) || (msec > maxdiv)) + return -1; + + count_value = refclk_khz * msec; + if (count_value > 0xffff) { + change_value = count_value / 0xffff + 1; + prescale_value = count_value / change_value; + } else { + change_value = count_value; + } + + ret = write_prescale_reg(prescale_value - 1); + if (ret) + return ret; + + ret = write_change_reg(change_value); + if (ret) + return ret; + + return 0; +} + +unsigned long last_wdt = -1; + +int wdt_start(unsigned long msecs) +{ + int ret; + ret = wdt_control(0); + if (ret) + return ret; + ret = wdt_set_period(msecs); + if (ret) + return ret; + ret = wdt_control(1); + if (ret) + return ret; + ret = wdt_kick(); + last_wdt = msecs; + return ret; +} + +int wdt_stop(void) +{ + last_wdt = -1; + return wdt_control(0); +} + +int wdt_kick(void) +{ + wdt_reg_write(kick_lock, KICK_LOCK_1); + if ((wdt_reg_read(kick_lock) & 0x3) != 0x1) + return -1; + + wdt_reg_write(kick_lock, KICK_LOCK_2); + if ((wdt_reg_read(kick_lock) & 0x3) != 0x3) + return -1; + + wdt_reg_write(kick, 1); + return 0; +} + +void reset_cpu(ulong addr) +{ + clk_enable(TNETV107X_LPSC_WDT_ARM); + wdt_start(1); + wdt_kick(); +} diff --git a/qemu/roms/u-boot/drivers/watchdog/xilinx_tb_wdt.c b/qemu/roms/u-boot/drivers/watchdog/xilinx_tb_wdt.c new file mode 100644 index 000000000..6336c9488 --- /dev/null +++ b/qemu/roms/u-boot/drivers/watchdog/xilinx_tb_wdt.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011-2013 Xilinx Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status Mask */ +#define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state Mask */ +#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 Mask*/ +#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 Mask */ + +struct watchdog_regs { + u32 twcsr0; /* 0x0 */ + u32 twcsr1; /* 0x4 */ + u32 tbr; /* 0x8 */ +}; + +static struct watchdog_regs *watchdog_base = + (struct watchdog_regs *)CONFIG_WATCHDOG_BASEADDR; + +void hw_watchdog_reset(void) +{ + u32 reg; + + /* Read the current contents of TCSR0 */ + reg = readl(&watchdog_base->twcsr0); + + /* Clear the watchdog WDS bit */ + if (reg & (XWT_CSR0_EWDT1_MASK | XWT_CSRX_EWDT2_MASK)) + writel(reg | XWT_CSR0_WDS_MASK, &watchdog_base->twcsr0); +} + +void hw_watchdog_disable(void) +{ + u32 reg; + + /* Read the current contents of TCSR0 */ + reg = readl(&watchdog_base->twcsr0); + + writel(reg & ~XWT_CSR0_EWDT1_MASK, &watchdog_base->twcsr0); + writel(~XWT_CSRX_EWDT2_MASK, &watchdog_base->twcsr1); + + puts("Watchdog disabled!\n"); +} + +static void hw_watchdog_isr(void *arg) +{ + hw_watchdog_reset(); +} + +void hw_watchdog_init(void) +{ + int ret; + + writel((XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK | XWT_CSR0_EWDT1_MASK), + &watchdog_base->twcsr0); + writel(XWT_CSRX_EWDT2_MASK, &watchdog_base->twcsr1); + + ret = install_interrupt_handler(CONFIG_WATCHDOG_IRQ, + hw_watchdog_isr, NULL); + if (ret) + puts("Watchdog IRQ registration failed."); +} -- cgit 1.2.3-korg